diff --git a/.editorconfig b/.editorconfig index b1d2dbdb588..72e85029ef6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,22 +1,22 @@ +# http://editorconfig.org root = true [*] -charset = utf-8 +indent_style = tab end_of_line = lf -insert_final_newline = true +charset = utf-8 trim_trailing_whitespace = true +insert_final_newline = true -[*.{dme,dmf,dmm,dm}] -end_of_line = crlf -indent_style = tab - -[*.md] -trim_trailing_whitespace = false +[*.yml] +indent_style = space +indent_size = 2 [*.py] indent_style = space -indent_size = 4 -[*.yml] +[*.md] +trim_trailing_whitespace = false + +[Dockerfile] indent_style = space -indent_size = 2 \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index 6f1fee81506..77c5aad8c4b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,25 +1,51 @@ +* text=auto + ## Enforce text mode and LF line breaks +*.bat text eol=lf *.cjs text eol=lf *.css text eol=lf +*.dm text eol=lf +*.dme text eol=lf +*.dmf text eol=lf +*.htm text eol=lf +*.html text eol=lf *.js text eol=lf +*.json text eol=lf *.jsx text eol=lf +*.md text eol=lf +*.ps1 text eol=lf +*.py text eol=lf *.scss text eol=lf +*.sh text eol=lf +*.sql text eol=lf +*.svg text eol=lf *.ts text eol=lf *.tsx text eol=lf +*.txt text eol=lf +*.yaml text eol=lf +*.yml text eol=lf ## Enforce binary mode +*.bmp binary *.dll binary +*.dmb binary *.exe binary +*.gif binary +*.jpg binary +*.png binary *.so binary *.zip binary -# dmm map merger hook -# needs additional setup, see tools/mapmerge/install.txt -# *.dmm merge=merge-dmm +## Merger hooks, run tools/hooks/install.bat or install.sh to set up +#*.dmm text eol=lf merge=dmm +#*.dmi binary merge=dmi -# dmi icon merger hook +# old dmi icon merger hook # needs additional setup, see tools/dmitool/merging.txt *.dmi merge=merge-dmi +## Force tab indents on dm files +*.dm whitespace=indent-with-non-tab + # force changelog merging to use union html/changelog.html merge=union diff --git a/.vscode/settings.json b/.vscode/settings.json index 129fa592bc3..a004179bb77 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -27,7 +27,7 @@ ], "tgstationTestExplorer.project.resultsType": "json", "[dm]": { - "files.eol": "\r\n", + "files.eol": "\n", "editor.detectIndentation": false, "editor.insertSpaces": false }, diff --git a/bot/CORE_DATA.py b/bot/CORE_DATA.py index ca235888f15..5fe47961db1 100644 --- a/bot/CORE_DATA.py +++ b/bot/CORE_DATA.py @@ -1,13 +1,13 @@ -Name = "CC_NanoTrasen" #The name he uses to connect -no_absolute_paths = True -debug_on = False -SName = ["cc","nt","trasen","nano","nanotrasen"] #Other names he will respond to, in lowercase -DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS = False -directory = "bot/directory/here/" # Directory the bot is located in, make sure to keep the "/" at the end -version = "TG CC-BY-SA 6" -Network = 'YOUR.SERVER.HERE' #e.g. "irc.rizon.net" -channel = "#YOUR CHANNEL HERE" #what channel you want the bot in -channels = ["#YOUR CHANNEL HERE"] #same as above -greeting = "Welcome!" #what he says when a person he hasn't seen before joins -prefix = "!" #prefix for bot commands -Port = 7000 +Name = "CC_NanoTrasen" #The name he uses to connect +no_absolute_paths = True +debug_on = False +SName = ["cc","nt","trasen","nano","nanotrasen"] #Other names he will respond to, in lowercase +DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS = False +directory = "bot/directory/here/" # Directory the bot is located in, make sure to keep the "/" at the end +version = "TG CC-BY-SA 6" +Network = 'YOUR.SERVER.HERE' #e.g. "irc.rizon.net" +channel = "#YOUR CHANNEL HERE" #what channel you want the bot in +channels = ["#YOUR CHANNEL HERE"] #same as above +greeting = "Welcome!" #what he says when a person he hasn't seen before joins +prefix = "!" #prefix for bot commands +Port = 7000 diff --git a/bot/C_eightball.py b/bot/C_eightball.py index 2ed73dafd57..3841c221720 100644 --- a/bot/C_eightball.py +++ b/bot/C_eightball.py @@ -1,32 +1,32 @@ -from random import choice as fsample #Yay for added speed! -global responses -responses = ['Yes','Too bad','Will you turn me off if I tell you?','Absolutely', - "Not at all", "Nope", "It does", "No", "All the time", - "I don't really know", "Could be","Possibly","You're still here?",# Chaoticag - "No idea", "Of course", "Would you turn me off if I tell you?", - "Sweet!","Nah","Certainly","Yeah","Yup","I am quite confident that the answer is Yes", - "Perhaps", "Yeeeeaah... No.", "Indubitably" ] # Richard -def eightball(data,debug,sender,prefix): - global responses - arg = data.lower().replace(prefix+"eightball ","") - arg = arg.replace(prefix+"8ball ","") - if debug: - print sender+":"+prefix+"eightball", arg - if "answer" in arg and "everything" in arg and "to" in arg: - if debug: - print "Responded with",42 - return "42" - elif arg == "derp": - if debug: - print "Responded with herp" - return("herp") - elif arg == "herp": - if debug: - print "Responded with derp" - return("derp") - else: - #choice = sample(responses,1)[0] - choice = fsample(responses) - if debug: - print "Responded with", choice - return(choice) +from random import choice as fsample #Yay for added speed! +global responses +responses = ['Yes','Too bad','Will you turn me off if I tell you?','Absolutely', + "Not at all", "Nope", "It does", "No", "All the time", + "I don't really know", "Could be","Possibly","You're still here?",# Chaoticag + "No idea", "Of course", "Would you turn me off if I tell you?", + "Sweet!","Nah","Certainly","Yeah","Yup","I am quite confident that the answer is Yes", + "Perhaps", "Yeeeeaah... No.", "Indubitably" ] # Richard +def eightball(data,debug,sender,prefix): + global responses + arg = data.lower().replace(prefix+"eightball ","") + arg = arg.replace(prefix+"8ball ","") + if debug: + print sender+":"+prefix+"eightball", arg + if "answer" in arg and "everything" in arg and "to" in arg: + if debug: + print "Responded with",42 + return "42" + elif arg == "derp": + if debug: + print "Responded with herp" + return("herp") + elif arg == "herp": + if debug: + print "Responded with derp" + return("derp") + else: + #choice = sample(responses,1)[0] + choice = fsample(responses) + if debug: + print "Responded with", choice + return(choice) diff --git a/bot/C_heaortai.py b/bot/C_heaortai.py index 6e8b304e73b..257c3a86346 100644 --- a/bot/C_heaortai.py +++ b/bot/C_heaortai.py @@ -1,5 +1,5 @@ -#Throws a coin, simple. -from random import random -def heaortai(debug,sender): return("Heads" if random() > 0.5 else "Tails") -# Takes 1/6th the time of doing it with random.randint(0,1) -# This file used to be a lot bigger, now it's kind of useless. +#Throws a coin, simple. +from random import random +def heaortai(debug,sender): return("Heads" if random() > 0.5 else "Tails") +# Takes 1/6th the time of doing it with random.randint(0,1) +# This file used to be a lot bigger, now it's kind of useless. diff --git a/bot/C_makequote.py b/bot/C_makequote.py index f79b863449a..9ae556ab7ef 100644 --- a/bot/C_makequote.py +++ b/bot/C_makequote.py @@ -1,21 +1,21 @@ -from save_load import save -from os import listdir -import CORE_DATA -directory = CORE_DATA.directory -def mkquote(prefix,influx,sender,debug): - arg = influx[10+len(prefix):] - if debug: - print sender+":"+prefix+"makequote "+str(len(arg))+" Characters" - if len(arg) == 0: - return("Type something to a quote") - else: - files = listdir(directory+"userquotes") - numb = 0 - while True: - numb += 1 - if sender.lower()+str(numb) in files: - pass - else: - save(directory+"userquotes/"+sender.lower()+str(numb),[arg,sender.lower()]) - return("Saved as:"+sender.lower()+str(numb)) - break +from save_load import save +from os import listdir +import CORE_DATA +directory = CORE_DATA.directory +def mkquote(prefix,influx,sender,debug): + arg = influx[10+len(prefix):] + if debug: + print sender+":"+prefix+"makequote "+str(len(arg))+" Characters" + if len(arg) == 0: + return("Type something to a quote") + else: + files = listdir(directory+"userquotes") + numb = 0 + while True: + numb += 1 + if sender.lower()+str(numb) in files: + pass + else: + save(directory+"userquotes/"+sender.lower()+str(numb),[arg,sender.lower()]) + return("Saved as:"+sender.lower()+str(numb)) + break diff --git a/bot/C_maths.py b/bot/C_maths.py index 86013b74a7b..ea604573314 100644 --- a/bot/C_maths.py +++ b/bot/C_maths.py @@ -1,70 +1,70 @@ -### EXPERIMENTAL PROTOTYPE ### -# e = 2.7182818284590452353602874713526624977572 -# pi = math.pi -from __future__ import division #PYTHON Y U NO TELL ME THIS BEFORE -import math -import random -import re -e = "2.7182818284590452353602874713526624977572" -pi = str(math.pi) -global pre -pre = len("maths ") -def maths(influx,prefix="!",sender="NaN",debug=True,method="n"): - global pre - influx = influx.lower() - influx = influx[len(prefix)+pre:] - influx = influx.replace("pie",pi+"*"+e) - influx = influx.replace("e*",e+"*") - influx = influx.replace("*e","*"+e) - influx = influx.replace("pi",pi) - if debug: - print sender+":"+prefix+"maths" - if influx.count("**") == 0 and influx.count('"') == 0 and influx.count("'") == 0 and influx.count(";") == 0 and influx.count(":") == 0: - influx_low = influx.lower() - influx_hi = influx.upper() - if "0b" in influx_low: - influx_low = re.sub("0b[0-1]*","",influx_low) - influx_hi = re.sub("0B[0-1]*","",influx_hi) - if "0x" in influx_low: - influx_low = re.sub("0x[a-f0-9]*","",influx_low) - influx_hi = re.sub("0X[A-F0-9]*","",influx_hi) - if "rand" in influx_low: - influx_low = re.sub("rand","",influx_low) - influx_hi = re.sub("RAND","",influx_hi) - if influx_low == influx_hi: - influx = re.sub("rand","random.random()",influx) - try: - result = eval(influx.lower()) - except ZeroDivisionError: - return "Divide by zero detected." - except SyntaxError: - return "Syntax Error detected." - except TypeError: - return "Type Error detected." - except: - return "Unknown Error detected." - else: - if method == "n": #Normal - return result - elif method == "i": #Forced Int - return int(result) - elif method == "h": #Hex - try: - if "L" in hex(result)[2:]: - return hex(result)[2:-1] - else: - return hex(result)[2:].upper() - except TypeError: - return "That value (%s) cannot be interpreted properly using !hmaths" %(str(result)) - elif method == "b": #Binary - try: - return bin(result)[2:].upper() - except TypeError: - return "That value (%s) cannot be interpreted properly using !bmaths" %(str(result)) - else: - return result - else: - return "What are you trying to make me do again?" - else: - return "Those are likely to make me hang" - +### EXPERIMENTAL PROTOTYPE ### +# e = 2.7182818284590452353602874713526624977572 +# pi = math.pi +from __future__ import division #PYTHON Y U NO TELL ME THIS BEFORE +import math +import random +import re +e = "2.7182818284590452353602874713526624977572" +pi = str(math.pi) +global pre +pre = len("maths ") +def maths(influx,prefix="!",sender="NaN",debug=True,method="n"): + global pre + influx = influx.lower() + influx = influx[len(prefix)+pre:] + influx = influx.replace("pie",pi+"*"+e) + influx = influx.replace("e*",e+"*") + influx = influx.replace("*e","*"+e) + influx = influx.replace("pi",pi) + if debug: + print sender+":"+prefix+"maths" + if influx.count("**") == 0 and influx.count('"') == 0 and influx.count("'") == 0 and influx.count(";") == 0 and influx.count(":") == 0: + influx_low = influx.lower() + influx_hi = influx.upper() + if "0b" in influx_low: + influx_low = re.sub("0b[0-1]*","",influx_low) + influx_hi = re.sub("0B[0-1]*","",influx_hi) + if "0x" in influx_low: + influx_low = re.sub("0x[a-f0-9]*","",influx_low) + influx_hi = re.sub("0X[A-F0-9]*","",influx_hi) + if "rand" in influx_low: + influx_low = re.sub("rand","",influx_low) + influx_hi = re.sub("RAND","",influx_hi) + if influx_low == influx_hi: + influx = re.sub("rand","random.random()",influx) + try: + result = eval(influx.lower()) + except ZeroDivisionError: + return "Divide by zero detected." + except SyntaxError: + return "Syntax Error detected." + except TypeError: + return "Type Error detected." + except: + return "Unknown Error detected." + else: + if method == "n": #Normal + return result + elif method == "i": #Forced Int + return int(result) + elif method == "h": #Hex + try: + if "L" in hex(result)[2:]: + return hex(result)[2:-1] + else: + return hex(result)[2:].upper() + except TypeError: + return "That value (%s) cannot be interpreted properly using !hmaths" %(str(result)) + elif method == "b": #Binary + try: + return bin(result)[2:].upper() + except TypeError: + return "That value (%s) cannot be interpreted properly using !bmaths" %(str(result)) + else: + return result + else: + return "What are you trying to make me do again?" + else: + return "Those are likely to make me hang" + diff --git a/bot/C_rot13.py b/bot/C_rot13.py index 62b72dc2ad2..36e0796b9be 100644 --- a/bot/C_rot13.py +++ b/bot/C_rot13.py @@ -1,23 +1,23 @@ -global parta,partb -parta = {"A":"N","B":"O","C":"P","D":"Q","E":"R","F":"S","G":"T","H":"U","I":"V","J":"W","K":"X","L":"Y","M":"Z"} -partb = {'O':'B','N':'A','Q':'D','P':'C','S':'F','R':'E','U':'H','T':'G','W':'J','V':'I','Y':'L','X':'K','Z':'M'} -def rot13(text): - global parta,partb - newtext = "" - for letter in text: - try: - if letter.isupper(): - newtext += parta[letter] - else: - newtext += parta[letter.upper()].lower() - except: - try: - if letter.isupper(): - newtext += partb[letter] - pass - else: - newtext += partb[letter.upper()].lower() - pass - except: - newtext += letter - return newtext +global parta,partb +parta = {"A":"N","B":"O","C":"P","D":"Q","E":"R","F":"S","G":"T","H":"U","I":"V","J":"W","K":"X","L":"Y","M":"Z"} +partb = {'O':'B','N':'A','Q':'D','P':'C','S':'F','R':'E','U':'H','T':'G','W':'J','V':'I','Y':'L','X':'K','Z':'M'} +def rot13(text): + global parta,partb + newtext = "" + for letter in text: + try: + if letter.isupper(): + newtext += parta[letter] + else: + newtext += parta[letter.upper()].lower() + except: + try: + if letter.isupper(): + newtext += partb[letter] + pass + else: + newtext += partb[letter.upper()].lower() + pass + except: + newtext += letter + return newtext diff --git a/bot/C_rtd.py b/bot/C_rtd.py index 96736fec21c..e4a032a0de8 100644 --- a/bot/C_rtd.py +++ b/bot/C_rtd.py @@ -1,96 +1,96 @@ -import random -def rtd(data,debug,sender): - backo = data - try: - arg1,arg2 = backo.split("d") - except ValueError, err: - return("Too many or too small amount of arguments") - else: - if debug: - print sender+":!rtd "+arg1+"d"+arg2 #faster than using %s's - die,die2 = [],[] - current_mark = "" - outcome = 0 - realnumberfound = False - checks = [] - count = 0 - arg1 = arg1.replace(" ","") - arg2 = arg2.replace(" ","") - try: - i_arg1 = int(arg1) - a_arg1 = abs(i_arg1) - if "+" in arg2 or "-" in arg2: - plus_spot = arg2.find("+") - minus_spot = arg2.find("-") - if plus_spot == -1 and minus_spot == -1: - nicer_form = "" - elif plus_spot != -1 and minus_spot == -1: - nicer_form = arg2[plus_spot:] - elif plus_spot == -1 and minus_spot != -1: - nicer_form = arg2[minus_spot:] - else: - if plus_spot < minus_spot: - nicer_form = arg2[plus_spot:] - else: - nicer_form = arg2[minus_spot:] - for letter in arg2: - if letter == "+" or letter == "-": - current_mark = letter - checks = [] - count += 1 - continue - checks.append(letter) - try: - next_up = arg2[count+1] - except: - if realnumberfound == False: - i_arg2 = int("".join(checks)) - checks = [] - realnumberfound = True - elif current_mark == "+": - outcome += int("".join(checks)) - else: - outcome -= int("".join(checks)) - else: - if next_up == "+" or next_up == "-": - if realnumberfound == False: - i_arg2 = int("".join(checks)) - checks = [] - realnumberfound = True - else: - if current_mark == "+": - outcome += int("".join(checks)) - else: - outcome -= int("".join(checks)) - checks = [] - count += 1 - else: - i_arg2 = int(arg2) - if a_arg1 == 0 or abs(i_arg2) == 0: - raise RuntimeError - except ValueError: - return("You lied! That's not a number!") - except RuntimeError: - return("Too many zeroes!") - else: - if a_arg1 > 100: - return("Too many rolls, I can only do one hundred at max.") - else: - for i in xrange(0,a_arg1): - if i_arg2 < 0: - dice = random.randint(i_arg2,0) - else: - dice = random.randint(1,i_arg2) - die.append(dice) - die2.append(str(dice)) - if i_arg2 < 0: - flist = "".join(die2) - else: - flist = "+".join(die2) - if len(flist) > 350: - return(str(reduce(lambda x,y: x+y, die)+outcome)) - else: - if current_mark == "": - return(flist+" = "+str(reduce(lambda x,y: x+y, die)+outcome)) - else: - return(flist+" ("+nicer_form+") = "+str(reduce(lambda x,y: x+y, die)+outcome)) +import random +def rtd(data,debug,sender): + backo = data + try: + arg1,arg2 = backo.split("d") + except ValueError, err: + return("Too many or too small amount of arguments") + else: + if debug: + print sender+":!rtd "+arg1+"d"+arg2 #faster than using %s's + die,die2 = [],[] + current_mark = "" + outcome = 0 + realnumberfound = False + checks = [] + count = 0 + arg1 = arg1.replace(" ","") + arg2 = arg2.replace(" ","") + try: + i_arg1 = int(arg1) + a_arg1 = abs(i_arg1) + if "+" in arg2 or "-" in arg2: + plus_spot = arg2.find("+") + minus_spot = arg2.find("-") + if plus_spot == -1 and minus_spot == -1: + nicer_form = "" + elif plus_spot != -1 and minus_spot == -1: + nicer_form = arg2[plus_spot:] + elif plus_spot == -1 and minus_spot != -1: + nicer_form = arg2[minus_spot:] + else: + if plus_spot < minus_spot: + nicer_form = arg2[plus_spot:] + else: + nicer_form = arg2[minus_spot:] + for letter in arg2: + if letter == "+" or letter == "-": + current_mark = letter + checks = [] + count += 1 + continue + checks.append(letter) + try: + next_up = arg2[count+1] + except: + if realnumberfound == False: + i_arg2 = int("".join(checks)) + checks = [] + realnumberfound = True + elif current_mark == "+": + outcome += int("".join(checks)) + else: + outcome -= int("".join(checks)) + else: + if next_up == "+" or next_up == "-": + if realnumberfound == False: + i_arg2 = int("".join(checks)) + checks = [] + realnumberfound = True + else: + if current_mark == "+": + outcome += int("".join(checks)) + else: + outcome -= int("".join(checks)) + checks = [] + count += 1 + else: + i_arg2 = int(arg2) + if a_arg1 == 0 or abs(i_arg2) == 0: + raise RuntimeError + except ValueError: + return("You lied! That's not a number!") + except RuntimeError: + return("Too many zeroes!") + else: + if a_arg1 > 100: + return("Too many rolls, I can only do one hundred at max.") + else: + for i in xrange(0,a_arg1): + if i_arg2 < 0: + dice = random.randint(i_arg2,0) + else: + dice = random.randint(1,i_arg2) + die.append(dice) + die2.append(str(dice)) + if i_arg2 < 0: + flist = "".join(die2) + else: + flist = "+".join(die2) + if len(flist) > 350: + return(str(reduce(lambda x,y: x+y, die)+outcome)) + else: + if current_mark == "": + return(flist+" = "+str(reduce(lambda x,y: x+y, die)+outcome)) + else: + return(flist+" ("+nicer_form+") = "+str(reduce(lambda x,y: x+y, die)+outcome)) diff --git a/bot/C_sarcasticball.py b/bot/C_sarcasticball.py index 1f5ae321816..f5b0e60b1de 100644 --- a/bot/C_sarcasticball.py +++ b/bot/C_sarcasticball.py @@ -1,30 +1,30 @@ -from random import choice as fsample -sarcastic_responses = ["Yeah right","What do I look like to you?","Are you kidding me?",#UsF - "As much as you","You don't believe that yourself","When pigs fly",#UsF - "Like your grandma","You would like to know, wouldn't you?", #UsF - "Like your mom", #Spectre - "Totally","Not at all", #Spectre - "AHAHAHahahaha, No.", #Strumpetplaya - "Not as much as USER","As much as USER", - "Really, you expect me to tell you that?", - "Right, and you've been building NOUNs for those USERs in the LOCATION, haven't you?" ] #Richard -locations = ["woods","baystation","ditch"] -nouns = ["bomb","toilet","robot","cyborg", - "garbage can","gun","cake", - "missile"] -def sarcasticball(data,debug,sender,users,prefix): - arg = data.lower().replace(prefix+"sarcasticball ","") - arg = arg.replace(prefix+"sball ","") - if debug: - print sender+":"+prefix+"sarcasticball", arg - choice = fsample(sarcastic_responses) - if "USER" in choice: - choice = choice.replace("USER",fsample(users),1) - choice = choice.replace("USER",fsample(users),1) - if "NOUN" in choice: - choice = choice.replace("NOUN",fsample(nouns),1) - if "LOCATION" in choice: - choice = choice.replace("LOCATION",fsample(locations),1) - if debug: - print "Responded with", choice - return(choice) +from random import choice as fsample +sarcastic_responses = ["Yeah right","What do I look like to you?","Are you kidding me?",#UsF + "As much as you","You don't believe that yourself","When pigs fly",#UsF + "Like your grandma","You would like to know, wouldn't you?", #UsF + "Like your mom", #Spectre + "Totally","Not at all", #Spectre + "AHAHAHahahaha, No.", #Strumpetplaya + "Not as much as USER","As much as USER", + "Really, you expect me to tell you that?", + "Right, and you've been building NOUNs for those USERs in the LOCATION, haven't you?" ] #Richard +locations = ["woods","baystation","ditch"] +nouns = ["bomb","toilet","robot","cyborg", + "garbage can","gun","cake", + "missile"] +def sarcasticball(data,debug,sender,users,prefix): + arg = data.lower().replace(prefix+"sarcasticball ","") + arg = arg.replace(prefix+"sball ","") + if debug: + print sender+":"+prefix+"sarcasticball", arg + choice = fsample(sarcastic_responses) + if "USER" in choice: + choice = choice.replace("USER",fsample(users),1) + choice = choice.replace("USER",fsample(users),1) + if "NOUN" in choice: + choice = choice.replace("NOUN",fsample(nouns),1) + if "LOCATION" in choice: + choice = choice.replace("LOCATION",fsample(locations),1) + if debug: + print "Responded with", choice + return(choice) diff --git a/bot/C_srtd.py b/bot/C_srtd.py index 761c0628d69..b49b8f8c1c0 100644 --- a/bot/C_srtd.py +++ b/bot/C_srtd.py @@ -1,35 +1,35 @@ -import random -def srtd(data,debug,sender): - try: - arg1,arg2 = data.split("d") - except ValueError, err: - if str(err) == "need more than 1 value to unpack": - return("Too small amount of arguments") - else: - return("Too many arguments") - else: - if debug: - print sender+":!rtd "+arg1+"d"+arg2 - die = [] - arg1 = arg1.replace(" ","") - arg2 = arg2.replace(" ","") - try: - i_arg1 = int(arg1) - i_arg2 = int(arg2) - if abs(i_arg1) == 0 or abs(i_arg2) == 0: - raise RuntimeError - except ValueError: - return("You lied! That's not a number!") - except RuntimeError: - return("Too many zeroes!") - else: - if abs(i_arg1) > 500: - return("Too many rolls, I can only do five hundred at max.") - else: - for i in xrange(0,abs(i_arg1)): - if i_arg2 < 0: - dice = random.randint(i_arg2,0) - else: - dice = random.randint(1,i_arg2) - die.append(dice) - return(str(reduce(lambda x,y: x+y, die))) +import random +def srtd(data,debug,sender): + try: + arg1,arg2 = data.split("d") + except ValueError, err: + if str(err) == "need more than 1 value to unpack": + return("Too small amount of arguments") + else: + return("Too many arguments") + else: + if debug: + print sender+":!rtd "+arg1+"d"+arg2 + die = [] + arg1 = arg1.replace(" ","") + arg2 = arg2.replace(" ","") + try: + i_arg1 = int(arg1) + i_arg2 = int(arg2) + if abs(i_arg1) == 0 or abs(i_arg2) == 0: + raise RuntimeError + except ValueError: + return("You lied! That's not a number!") + except RuntimeError: + return("Too many zeroes!") + else: + if abs(i_arg1) > 500: + return("Too many rolls, I can only do five hundred at max.") + else: + for i in xrange(0,abs(i_arg1)): + if i_arg2 < 0: + dice = random.randint(i_arg2,0) + else: + dice = random.randint(1,i_arg2) + die.append(dice) + return(str(reduce(lambda x,y: x+y, die))) diff --git a/bot/D_help.py b/bot/D_help.py index ee4e2c07cfe..d75ed94f5f5 100644 --- a/bot/D_help.py +++ b/bot/D_help.py @@ -1,60 +1,60 @@ -#As new commands are added, update this. -# Last updated: 8.3.2011 - -# Updated 12.3.2011: -# - Added the missing help data for Version -# - Imported CORE_DATA to get the name. -# - Tidied some commands up a bit. -# - Replaced all "Bot"s with the Skibot's current name. - -from CORE_DATA import Name -everything = {"8ball":"[8ball ] Responds to the argument", - "allcaps":"[allcaps ] Takes an uppercase string and returns a capitalized version", - "bmaths":"[bmaths ] Takes a math equation (Like 5+5) and returns a binary result", - "coin":"[coin] Flips a coin", - "dance":"[dance] Makes %s do a little dance" %(Name), - "delquote":"(OP ONLY) [delquote ] Removes a quote with the filename equal to the argument", - "disable":"(OP ONLY) [disable] Disables all output from %s" %(Name), - "disable dance":"(HALFOP / OP ONLY) [disable dance] or [dd] Toggles dancing", - "disable fml":"(HALFOP / OP ONLY) [disable fml] Disables FML", - "eightball":"[eightball ] Responds to the argument", - "enable":"(OP ONLY) [enable] After being disabled, enable will turn output back on", - "enable fml":"{HALFOP / OP ONLY} [enable fml] After fml has been disabled, enable fml will make it available again", - "fml":"[fml] Returns a random Fuck My Life bit", - "give":"[give ] Gives the Pneumatic Disposal Unit the argument", - "help":"[help []] Returns the list of commands or a detailed description of a command if specified", - "hmaths":"[hmaths ] Takes a math equation (Like 5+5) and returns a hex result", - "makequote":"[makequote ] Creates a quote with arg being the quote itself", - "maths":"[maths ] Takes a math equation (Like 5+5) and returns a default result", - "note":"[note []] Opens a note if only arg1 is specified, Creates a note with the name of arg1 and contents of arg2 if arg2 is specified, if you prefix the note name with [CP], it creates a public note only to that channel. Which can be accessed by !note _", - "notes":"[notes] Displays all your saved notes on %s" %(Name), - "otherball":"[otherball] If Floorbot is on the same channel, %s will ask him a random question when this command is passed" %(Name), - "purgemessages":"[purgemessages] Used to delete all your Tell messages (%s,Tell )" %(Name), - "quote":"[quote []] Picks a random quote, if the author is specified, a random quote by that author", - "redmine":"[redmine] If you have a note called redmine, with a valid whoopshop redmine address, this displays all the bugs labeled as 'New' on that page. It also displays the todo note if it's found.", - "replace":"[replace] Fixes the Pneumatic Smasher if it's been broken", - "rot13":"[rot13 ] Encrypts the arg by using the rot13 method", - "rtd":"[rtd [d]] Rolls a six-sided dice if no arguments are specified, otherwise arg1 is the amount of rolls and arg2 is the amount of sides the dice have", - "sarcasticball":"[sarcasticball ] Responds to the argument sarcastically", - "sball":"[sball ] Responds to the argument sarcastically", - "srtd":"[srtd d] Rolls amount of sided die without showing the dice values separately", - "stop":"(RESTRICTED TO OP AND CREATOR) [stop] Stops %s, plain and simple" %(Name), - "suggest":"[suggest ] Saves a suggestion given to %s, to be later viewed by the creator" %(Name), - "take":"[take ] Takes an item specified in the argument from the Pneumatic Smasher", - "tban":"(OP ONLY) [tban ] When %s is an operator, You can ban an user for specified amount of seconds" %(Name), - "thm":"(RESTRICTED TO OP AND CREATOR) [thm] Normally in 8ball and sarcasticball, Users are not shown, instead replaced by things like demons or plasma researchers, toggling this changes that behaviour.", - "tm":"(OP AND CREATOR ONLY) [tm] Toggles marakov", - "togglequotemakers":"(OP ONLY) [togglequotemakers or tqm] Normally with the quote command, makers are not shown, this toggles that behaviour.", - "tqm":"(OP ONLY) [tqm or togglequotemakers] Normally with the quote command, makers are not shown, this toggles that behaviour.", - "toggleofflinemessages":"(OP ONLY) [toggleofflinemessages or tom] Allows an operator to toggle leaving Tell messages (%s, Tell ] Whenever the user says something in allcaps, it's capitalized.", - "uptime":"[uptime] Displays how long %s has been alive on the channel."%(Name), - "use":"[use] Uses the Pneumatic Smasher.", - "youtube":"[youtube ] Shows the title of a video by checking the URL provided.", - "version":"[version] Shows the current version of %s." %(Name), - "weather":"[weather ] Displays the current weather of the provided location.", - "life":"I cannot help you with that, sorry."} - +#As new commands are added, update this. +# Last updated: 8.3.2011 + +# Updated 12.3.2011: +# - Added the missing help data for Version +# - Imported CORE_DATA to get the name. +# - Tidied some commands up a bit. +# - Replaced all "Bot"s with the Skibot's current name. + +from CORE_DATA import Name +everything = {"8ball":"[8ball ] Responds to the argument", + "allcaps":"[allcaps ] Takes an uppercase string and returns a capitalized version", + "bmaths":"[bmaths ] Takes a math equation (Like 5+5) and returns a binary result", + "coin":"[coin] Flips a coin", + "dance":"[dance] Makes %s do a little dance" %(Name), + "delquote":"(OP ONLY) [delquote ] Removes a quote with the filename equal to the argument", + "disable":"(OP ONLY) [disable] Disables all output from %s" %(Name), + "disable dance":"(HALFOP / OP ONLY) [disable dance] or [dd] Toggles dancing", + "disable fml":"(HALFOP / OP ONLY) [disable fml] Disables FML", + "eightball":"[eightball ] Responds to the argument", + "enable":"(OP ONLY) [enable] After being disabled, enable will turn output back on", + "enable fml":"{HALFOP / OP ONLY} [enable fml] After fml has been disabled, enable fml will make it available again", + "fml":"[fml] Returns a random Fuck My Life bit", + "give":"[give ] Gives the Pneumatic Disposal Unit the argument", + "help":"[help []] Returns the list of commands or a detailed description of a command if specified", + "hmaths":"[hmaths ] Takes a math equation (Like 5+5) and returns a hex result", + "makequote":"[makequote ] Creates a quote with arg being the quote itself", + "maths":"[maths ] Takes a math equation (Like 5+5) and returns a default result", + "note":"[note []] Opens a note if only arg1 is specified, Creates a note with the name of arg1 and contents of arg2 if arg2 is specified, if you prefix the note name with [CP], it creates a public note only to that channel. Which can be accessed by !note _", + "notes":"[notes] Displays all your saved notes on %s" %(Name), + "otherball":"[otherball] If Floorbot is on the same channel, %s will ask him a random question when this command is passed" %(Name), + "purgemessages":"[purgemessages] Used to delete all your Tell messages (%s,Tell )" %(Name), + "quote":"[quote []] Picks a random quote, if the author is specified, a random quote by that author", + "redmine":"[redmine] If you have a note called redmine, with a valid whoopshop redmine address, this displays all the bugs labeled as 'New' on that page. It also displays the todo note if it's found.", + "replace":"[replace] Fixes the Pneumatic Smasher if it's been broken", + "rot13":"[rot13 ] Encrypts the arg by using the rot13 method", + "rtd":"[rtd [d]] Rolls a six-sided dice if no arguments are specified, otherwise arg1 is the amount of rolls and arg2 is the amount of sides the dice have", + "sarcasticball":"[sarcasticball ] Responds to the argument sarcastically", + "sball":"[sball ] Responds to the argument sarcastically", + "srtd":"[srtd d] Rolls amount of sided die without showing the dice values separately", + "stop":"(RESTRICTED TO OP AND CREATOR) [stop] Stops %s, plain and simple" %(Name), + "suggest":"[suggest ] Saves a suggestion given to %s, to be later viewed by the creator" %(Name), + "take":"[take ] Takes an item specified in the argument from the Pneumatic Smasher", + "tban":"(OP ONLY) [tban ] When %s is an operator, You can ban an user for specified amount of seconds" %(Name), + "thm":"(RESTRICTED TO OP AND CREATOR) [thm] Normally in 8ball and sarcasticball, Users are not shown, instead replaced by things like demons or plasma researchers, toggling this changes that behaviour.", + "tm":"(OP AND CREATOR ONLY) [tm] Toggles marakov", + "togglequotemakers":"(OP ONLY) [togglequotemakers or tqm] Normally with the quote command, makers are not shown, this toggles that behaviour.", + "tqm":"(OP ONLY) [tqm or togglequotemakers] Normally with the quote command, makers are not shown, this toggles that behaviour.", + "toggleofflinemessages":"(OP ONLY) [toggleofflinemessages or tom] Allows an operator to toggle leaving Tell messages (%s, Tell ] Whenever the user says something in allcaps, it's capitalized.", + "uptime":"[uptime] Displays how long %s has been alive on the channel."%(Name), + "use":"[use] Uses the Pneumatic Smasher.", + "youtube":"[youtube ] Shows the title of a video by checking the URL provided.", + "version":"[version] Shows the current version of %s." %(Name), + "weather":"[weather ] Displays the current weather of the provided location.", + "life":"I cannot help you with that, sorry."} + diff --git a/bot/FMLformatter.py b/bot/FMLformatter.py index 153af61a41a..78649fe0537 100644 --- a/bot/FMLformatter.py +++ b/bot/FMLformatter.py @@ -1,55 +1,55 @@ -from htmltagremove import htr -def formatter(data): - newdata = [] - data = htr(data) - bad = ["Your nick : Categories : ","\r","Advanced search - last", - "FMyLife","Get the guts to spill the beans","FML: Your random funny stories", - "Woman","Man","Choose","Health","Intimacy","Miscellaneous","Man or woman? ", - "Money","Kids","Work","Love","Email notification?", - "Moderate the FMLs","Submit your FML story", - "- If your story isn't published on the website, don't feel offended, and thank you nevertheless!", - "Pick a country","See all","Your account","Team's blog", - "Meet the FMLHello readers! Did you meet someone new this...The whole blog", - "Amazon","Borders","IndieBound","Personalized book","Terms of use", - "FML t-shirts -","Love - Money - Kids - Work - Health - Intimacy - Miscellaneous - Members", - "Follow the FML Follow the FML blog Follow the FML comments ", - "_qoptions={", - "};","})();","Categories","Sign up - Password? ", " Net Avenir : gestion publicitaire", - "FMyLife, the book","Available NOW on:","Barnes & Noble"] - - for checkable in data: - if checkable in bad: - pass - elif "_gaq.push" in checkable: - pass - elif "ga.src" in checkable: - pass - elif "var _gaq" in checkable: - pass - elif "var s =" in checkable: - pass - elif "var ga" in checkable: - pass - elif "function()" in checkable: - pass - elif "siteanalytics" in checkable: - pass - elif "qacct:" in checkable: - pass - elif "\r" in checkable: - pass - elif "ic_" in checkable: - pass - elif "Please note that spam and nonsensical stories" in checkable: - pass - elif "Refresh this page" in checkable: - pass - elif "You...The whole blo" in checkable: - pass - elif "Net Avenir : gestion publicitair" in checkable: - pass - else: - if "Net Avenir : gestion publicitaireClose the advertisement" in checkable: - checkable = checkable.replace("Net Avenir : gestion publicitaireClose the advertisement","") - newdata.append(checkable) - return newdata +from htmltagremove import htr +def formatter(data): + newdata = [] + data = htr(data) + bad = ["Your nick : Categories : ","\r","Advanced search - last", + "FMyLife","Get the guts to spill the beans","FML: Your random funny stories", + "Woman","Man","Choose","Health","Intimacy","Miscellaneous","Man or woman? ", + "Money","Kids","Work","Love","Email notification?", + "Moderate the FMLs","Submit your FML story", + "- If your story isn't published on the website, don't feel offended, and thank you nevertheless!", + "Pick a country","See all","Your account","Team's blog", + "Meet the FMLHello readers! Did you meet someone new this...The whole blog", + "Amazon","Borders","IndieBound","Personalized book","Terms of use", + "FML t-shirts -","Love - Money - Kids - Work - Health - Intimacy - Miscellaneous - Members", + "Follow the FML Follow the FML blog Follow the FML comments ", + "_qoptions={", + "};","})();","Categories","Sign up - Password? ", " Net Avenir : gestion publicitaire", + "FMyLife, the book","Available NOW on:","Barnes & Noble"] + + for checkable in data: + if checkable in bad: + pass + elif "_gaq.push" in checkable: + pass + elif "ga.src" in checkable: + pass + elif "var _gaq" in checkable: + pass + elif "var s =" in checkable: + pass + elif "var ga" in checkable: + pass + elif "function()" in checkable: + pass + elif "siteanalytics" in checkable: + pass + elif "qacct:" in checkable: + pass + elif "\r" in checkable: + pass + elif "ic_" in checkable: + pass + elif "Please note that spam and nonsensical stories" in checkable: + pass + elif "Refresh this page" in checkable: + pass + elif "You...The whole blo" in checkable: + pass + elif "Net Avenir : gestion publicitair" in checkable: + pass + else: + if "Net Avenir : gestion publicitaireClose the advertisement" in checkable: + checkable = checkable.replace("Net Avenir : gestion publicitaireClose the advertisement","") + newdata.append(checkable) + return newdata diff --git a/bot/Marakov/Marakov.Cache b/bot/Marakov/Marakov.Cache index 500f4384edd..9818ecc7ab0 100644 --- a/bot/Marakov/Marakov.Cache +++ b/bot/Marakov/Marakov.Cache @@ -1,2450 +1,2450 @@ -(dp0 -S'all' -p1 -(lp2 -S':p' -p3 -aS'the' -p4 -asS'code' -p5 -(lp6 -S'in' -p7 -asS'stores' -p8 -(lp9 -S'that' -p10 -asS'just' -p11 -(lp12 -S'like' -p13 -aS'marakov' -p14 -aS'felt' -p15 -aS'gives' -p16 -aS'a' -p17 -aS'add' -p18 -aS'what' -p19 -asS'being' -p20 -(lp21 -S'goon' -p22 -aS'a' -p23 -asS'text' -p24 -(lp25 -S'string' -p26 -asS'dependant' -p27 -(lp28 -S'on' -p29 -asS'speedup' -p30 -(lp31 -S'at' -p32 -asS'felt' -p33 -(lp34 -S'like' -p35 -asS'installed' -p36 -(lp37 -S'tho' -p38 -asS'disabled' -p39 -(lp40 -S'it' -p41 -asS'timing' -p42 -(lp43 -S'when' -p44 -asS'psyco' -p45 -(lp46 -S'installed' -p47 -aS'is' -p48 -asS'stops' -p49 -(lp50 -S'timing' -p51 -asS'file' -p52 -(lp53 -S'too' -p54 -aS'that' -p55 -aS'where' -p56 -asS'go' -p57 -(lp58 -S'fuck' -p59 -aS'into' -p60 -aS'test' -p61 -asS'hell' -p62 -(lp63 -S'recreate' -p64 -asS'configurable' -p65 -(lp66 -S'greeting' -p67 -asS'bs12' -p68 -(lp69 -S'message' -p70 -asS'its' -p71 -(lp72 -S'fine' -p73 -aS'just' -p74 -aS'calculated' -p75 -aS'not' -p76 -aS'really' -p77 -aS'ridiculously' -p78 -aS'now' -p79 -aS'a' -p80 -aS'missing' -p81 -aS'for' -p82 -aS'false' -p83 -aS'on' -p84 -asS'before' -p85 -(lp86 -S'that' -p87 -asS'rp-heavy' -p88 -(lp89 -S'server' -p90 -asS'announcement' -p91 -(lp92 -S'like' -p93 -asS'now' -p94 -(lp95 -S'it' -p96 -aS'running' -p97 -aS'makie' -p98 -aS'i' -p99 -asS'nudge' -p100 -(lp101 -S'python' -p102 -aS'is' -p103 -asS'sourced' -p104 -(lp105 -S'under' -p106 -asS'title' -p107 -(lp108 -S'of' -p109 -asS'situations' -p110 -(lp111 -S'where' -p112 -asS'fine-tune' -p113 -(lp114 -S'it' -p115 -asS'enough' -p116 -(lp117 -S'to' -p118 -asS'send' -p119 -(lp120 -S'it' -p121 -aS'one' -p122 -asS'should' -p123 -(lp124 -S'be' -p125 -aS'learn' -p126 -aS'we' -p127 -asS'values' -p128 -(lp129 -S'dont' -p130 -asS'to' -p131 -(lp132 -S'edit' -p133 -aS'back' -p134 -aS'know' -p135 -aS'care' -p136 -aS'be' -p137 -aS'configure' -p138 -aS'the' -p139 -aS'null' -p140 -aS'phone' -p141 -aS'welcome' -p142 -aS'reverse' -p143 -aS'send' -p144 -aS'reg' -p145 -aS'point' -p146 -aS'automatically' -p147 -aS'work' -p148 -aS'waste' -p149 -aS'marshmallow' -p150 -aS'queries' -p151 -aS'+o' -p152 -aS'disable' -p153 -asS'jit' -p154 -(lp155 -S'compiler' -p156 -asS'going' -p157 -(lp158 -S'to' -p159 -asS'helps' -p160 -(lp161 -S'me' -p162 -asS'messes' -p163 -(lp164 -S'it' -p165 -asS'indeed' -p166 -(lp167 -S'it' -p168 -asS'tg' -p169 -(lp170 -S'and' -p171 -asS'has' -p172 -(lp173 -S'been' -p174 -aS'my' -p175 -asS'into' -p176 -(lp177 -S'#tgstation13' -p178 -asS'ridiculously' -p179 -(lp180 -S'simple' -p181 -asS'annoy' -p182 -(lp183 -S'downstream' -p184 -asS'them' -p185 -(lp186 -S'out' -p187 -asS'someone' -p188 -(lp189 -S'adminhelps' -p190 -asS'sense' -p191 -(lp192 -S'i' -p193 -asS'string' -p194 -(lp195 -S'called' -p196 -asS'get' -p197 -(lp198 -S'ready' -p199 -asS'python' -p200 -(lp201 -S'script' -p202 -aS'so' -p203 -aS'code' -p204 -aS'scripts' -p205 -aS'and' -p206 -aS'but' -p207 -aS'now' -p208 -aS'released' -p209 -aS'is' -p210 -aS'enough' -p211 -asS'goon' -p212 -(lp213 -S'tg' -p214 -asS'showing' -p215 -(lp216 -S'up' -p217 -asS'20ish' -p218 -(lp219 -S'line' -p220 -asS'gonna' -p221 -(lp222 -S'go' -p223 -asS'made' -p224 -(lp225 -S'doctors' -p226 -asS'every' -p227 -(lp228 -S'loop' -p229 -aS'time' -p230 -asS'know' -p231 -(lp232 -S'the' -p233 -aS'why' -p234 -aS'that' -p235 -asS'not' -p236 -(lp237 -S'just' -p238 -aS'necessary' -p239 -aS'to' -p240 -aS'so' -p241 -aS'need' -p242 -aS'sure' -p243 -aS'relaying' -p244 -aS'very' -p245 -aS'even' -p246 -asS'2' -p247 -(lp248 -S'loop' -p249 -asS'password' -p250 -(lp251 -S'var' -p252 -asS'day' -p253 -(lp254 -S'so' -p255 -asS'swapping' -p256 -(lp257 -S'to' -p258 -asS'easily' -p259 -(lp260 -S'editable' -p261 -asS'necessary' -p262 -(lp263 -S'at' -p264 -asS'like' -p265 -(lp266 -S'being' -p267 -aS'linking' -p268 -aS'to' -p269 -aS'how' -p270 -aS'it' -p271 -aS'skbzzzzzibi' -p272 -asS'course' -p273 -(lp274 -S'that' -p275 -asS'edit' -p276 -(lp277 -S'baystation' -p278 -asS'fully' -p279 -(lp280 -S'open' -p281 -asS'greeting' -p282 -(lp283 -S'message' -p284 -asS'server' -p285 -(lp286 -S'basically' -p287 -aS'' -p288 -asS'default' -p289 -(lp290 -S'config' -p291 -asS'bad' -p292 -(lp293 -S'company' -p294 -asS'channel' -p295 -(lp296 -S'or' -p297 -asS'always' -p298 -(lp299 -S'makes' -p300 -asS'went' -p301 -(lp302 -S'past' -p303 -asS'quarxink' -p304 -(lp305 -S'its' -p306 -asS'automatic' -p307 -(lp308 -S'announcement' -p309 -asS'once' -p310 -(lp311 -S'per' -p312 -asS'wrote' -p313 -(lp314 -S'most' -p315 -asS'pain' -p316 -(lp317 -S'on' -p318 -asS'system' -p319 -(lp320 -S'calls' -p321 -asS'right' -p322 -(lp323 -S'brb' -p324 -asS'decides' -p325 -(lp326 -S'not' -p327 -asS'people' -p328 -(lp329 -S'say' -p330 -aS'i' -p331 -aS'he' -p332 -asS'goddamn' -p333 -(lp334 -S'python' -p335 -asS'back' -p336 -(lp337 -S'it' -p338 -asS'used' -p339 -(lp340 -S'to' -p341 -aS'for' -p342 -asS'past' -p343 -(lp344 -S'too' -p345 -asS'cost' -p346 -(lp347 -S'of' -p348 -asS'learn' -p349 -(lp350 -S'python' -p351 -asS'are' -p352 -(lp353 -S'lawyers' -p354 -aS'actually' -p355 -aS'configurable' -p356 -aS'we' -p357 -asS'celestialike' -p358 -(lp359 -S'of' -p360 -asS'lawyers' -p361 -(lp362 -S'for' -p363 -asS'time' -p364 -(lp365 -S'he' -p366 -asS'out' -p367 -(lp368 -S'switch' -p369 -aS'slowdowns' -p370 -aS'that' -p371 -aS'why' -p372 -aS'nudge' -p373 -asS'even' -p374 -(lp375 -S'an' -p376 -asS'what' -p377 -(lp378 -S'the' -p379 -aS'is' -p380 -aS'these' -p381 -aS'was' -p382 -aS'license' -p383 -aS'about' -p384 -aS'i' -p385 -asS'said' -p386 -(lp387 -S'in' -p388 -asS'sayt' -p389 -(lp390 -S'hat' -p391 -asS'for' -p392 -(lp393 -S'that' -p394 -aS'every' -p395 -aS'the' -p396 -aS'quarx' -p397 -aS'cc_nanotrasen' -p398 -aS'homoerotic' -p399 -aS'situations' -p400 -aS'good' -p401 -asS'#tgstation13' -p402 -(lp403 -S'and' -p404 -asS'per' -p405 -(lp406 -S'name' -p407 -asS'whole' -p408 -(lp409 -S'config' -p410 -asS'state' -p411 -(lp412 -S'the' -p413 -asS'does' -p414 -(lp415 -S'the' -p416 -aS'it' -p417 -asS'goes' -p418 -(lp419 -S'on' -p420 -asS'readme' -p421 -(lp422 -S'too' -p423 -asS'new' -p424 -(lp425 -S'bot' -p426 -aS'person' -p427 -asS'learned' -p428 -(lp429 -S'python' -p430 -asS'irc' -p431 -(lp432 -S'bot' -p433 -asS'reg' -p434 -(lp435 -S'it' -p436 -asS'blow' -p437 -(lp438 -S'borgs' -p439 -asS'shut' -p440 -(lp441 -S'down' -p442 -asS'after' -p443 -(lp444 -S'an' -p445 -aS'a' -p446 -asS'ill' -p447 -(lp448 -S'switch' -p449 -asS'says' -p450 -(lp451 -S'someones' -p452 -asS'queries' -p453 -(lp454 -S'again' -p455 -asS'technocracy' -p456 -(lp457 -S'and' -p458 -asS'we' -p459 -(lp460 -S'go' -p461 -aS'do' -p462 -aS'totally' -p463 -aS'going' -p464 -aS'expect' -p465 -aS'just' -p466 -aS'dont' -p467 -aS'have' -p468 -asS'put' -p469 -(lp470 -S'all' -p471 -asS'from' -p472 -(lp473 -S'the' -p474 -asS'data11lower' -p475 -(lp476 -S'==' -p477 -asS'configuration' -p478 -(lp479 -S'for' -p480 -asS'wait' -p481 -(lp482 -S'what' -p483 -asS'on' -p484 -(lp485 -S'my' -p486 -aS'the' -p487 -aS'a' -p488 -aS'connect' -p489 -aS'svn' -p490 -aS'one' -p491 -aS'/' -p492 -asS'about' -p493 -(lp494 -S'system' -p495 -asS'ok' -p496 -(lp497 -S'thats' -p498 -asS'reverse' -p499 -(lp500 -S'engineer' -p501 -asS'license' -p502 -(lp503 -S'is' -p504 -asS'oh' -p505 -(lp506 -S'okay' -p507 -aS'ok' -p508 -aS'i' -p509 -aS'wait' -p510 -asS'starts' -p511 -(lp512 -S'timing' -p513 -asS'could' -p514 -(lp515 -S'learn' -p516 -asS'larger' -p517 -(lp518 -S'ram' -p519 -asS'bot' -p520 -(lp521 -S'is' -p522 -aS'have' -p523 -aS'shut' -p524 -aS'uses' -p525 -asS'running' -p526 -(lp527 -S'it' -p528 -aS'the' -p529 -aS'on' -p530 -asS'times' -p531 -(lp532 -S'on' -p533 -aS'reported' -p534 -aS'went' -p535 -asS'where' -p536 -(lp537 -S'he' -p538 -asS'heck' -p539 -(lp540 -S':d' -p541 -asS'idk' -p542 -(lp543 -S'magic' -p544 -asS'receives' -p545 -(lp546 -S'a' -p547 -asS'bots' -p548 -(lp549 -S'showing' -p550 -asS'slightly' -p551 -(lp552 -S'larger' -p553 -asS'or' -p554 -(lp555 -S'know' -p556 -aS'what' -p557 -aS'work' -p558 -aS'should' -p559 -aS'data11lower' -p560 -asS'automatically' -p561 -(lp562 -S'state' -p563 -asS'thats' -p564 -(lp565 -S'not' -p566 -aS'the' -p567 -aS'kind' -p568 -asS'ugh' -p569 -(lp570 -S'fuck' -p571 -asS'major' -p572 -(lp573 -S'ss13' -p574 -aS'three' -p575 -asS'py' -p576 -(lp577 -S'file' -p578 -asS'soss' -p579 -(lp580 -S'server' -p581 -asS'dont' -p582 -(lp583 -S'need' -p584 -aS'have' -p585 -aS'send' -p586 -aS'want' -p587 -aS'speak' -p588 -asS'hostmask' -p589 -(lp590 -S'combination' -p591 -asS'point' -p592 -(lp593 -S'out' -p594 -aS'has' -p595 -asS'simple' -p596 -(lp597 -S'to' -p598 -asS'miura' -p599 -(lp600 -S'doesnt' -p601 -asS'variables' -p602 -(lp603 -S'in' -p604 -asS'recreate' -p605 -(lp606 -S'it' -p607 -asS'welcome' -p608 -(lp609 -S'to' -p610 -asS'linking' -p611 -(lp612 -S'them' -p613 -asS'down' -p614 -(lp615 -S'when' -p616 -asS'why' -p617 -(lp618 -S'its' -p619 -aS'it' -p620 -aS'is' -p621 -asS'doesnt' -p622 -(lp623 -S'play' -p624 -aS'call' -p625 -asS'marakov' -p626 -(lp627 -S'loops' -p628 -aS'helps' -p629 -asS'laugh' -p630 -(lp631 -S'when' -p632 -asS'pony' -p633 -(lp634 -S'asshole' -p635 -asS'message' -p636 -(lp637 -S'has' -p638 -aS'should' -p639 -asS'open' -p640 -(lp641 -S'sourced' -p642 -aS'source' -p643 -asS'brb' -p644 -(lp645 -S'swapping' -p646 -asS'speak' -p647 -(lp648 -S'python' -p649 -asS'pastebin' -p650 -(lp651 -S'the' -p652 -asS'line' -p653 -(lp654 -S'core' -p655 -aS'on' -p656 -asS'three' -p657 -(lp658 -S'being' -p659 -asS'yay' -p660 -(lp661 -S'it' -p662 -asS'meatbag' -p663 -(lp664 -S'when' -p665 -asS'would' -p666 -(lp667 -S'be' -p668 -aS'expect' -p669 -asS'script' -p670 -(lp671 -S'that' -p672 -asS'illegal' -p673 -(lp674 -S'ban' -p675 -asS'there' -p676 -(lp677 -S'are' -p678 -aS'we' -p679 -asS'add' -p680 -(lp681 -S'that' -p682 -aS'a' -p683 -asS'been' -p684 -(lp685 -S'processed' -p686 -asS'name' -p687 -(lp688 -S'/' -p689 -aS'when' -p690 -asS'ai' -p691 -(lp692 -S'malf' -p693 -asS'marshmallow' -p694 -(lp695 -S'pony' -p696 -asS'of' -p697 -(lp698 -S'the' -p699 -aS'ss13' -p700 -aS'tgstation13' -p701 -aS'a' -p702 -aS'course' -p703 -aS'it' -p704 -aS'soss' -p705 -aS'annoying' -p706 -aS'any' -p707 -aS'me' -p708 -aS'cap' -p709 -aS'technocracy' -p710 -asS'call' -p711 -(lp712 -S'me' -p713 -asS'too' -p714 -(lp715 -S':' -p716 -aS'fast' -p717 -asS'basic' -p718 -(lp719 -S'configuration' -p720 -asS'var' -p721 -(lp722 -S'and' -p723 -asS'calc' -p724 -(lp725 -S'times' -p726 -asS'was' -p727 -(lp728 -S'going' -p729 -aS'it' -p730 -aS'that' -p731 -aS'not' -p732 -aS'intended' -p733 -asS'tell' -p734 -(lp735 -S'people' -p736 -asS'500' -p737 -(lp738 -S'chance' -p739 -asS'gives' -p740 -(lp741 -S'a' -p742 -asS'sort' -p743 -(lp744 -S'of' -p745 -asS'svn' -p746 -(lp747 -S'size' -p748 -asS'only' -p749 -(lp750 -S'does' -p751 -aS'2' -p752 -asS'10-30%' -p753 -(lp754 -S'speedup' -p755 -asS'knows' -p756 -(lp757 -S'about' -p758 -asS'webpage' -p759 -(lp760 -S'title' -p761 -asS'that' -p762 -(lp763 -S'makes' -p764 -aS'would' -p765 -aS'the' -p766 -aS'needs' -p767 -aS'to' -p768 -aS'at' -p769 -aS'for' -p770 -aS'was' -p771 -aS'data' -p772 -asS'company' -p773 -(lp774 -S'2' -p775 -asS'under' -p776 -(lp777 -S'cc-by-sa' -p778 -asS'editable' -p779 -(lp780 -S'config' -p781 -asS'but' -p782 -(lp783 -S'of' -p784 -asS'idea' -p785 -(lp786 -S'what' -p787 -asS'released' -p788 -(lp789 -S'under' -p790 -asS'part' -p791 -(lp792 -S'before' -p793 -asS'link' -p794 -(lp795 -S'said' -p796 -aS'to' -p797 -asS'basically' -p798 -(lp799 -S'it' -p800 -asS'doctors' -p801 -(lp802 -S'useless' -p803 -asS'==' -p804 -(lp805 -S'channel' -p806 -aS'channel1::' -p807 -asS'be' -p808 -(lp809 -S'an' -p810 -aS'called' -p811 -aS'a' -p812 -aS'in' -p813 -aS'running' -p814 -aS'used' -p815 -asS'editing' -p816 -(lp817 -S'goddamn' -p818 -asS'with' -p819 -(lp820 -S'the' -p821 -aS'easily' -p822 -aS'adminhelps' -p823 -aS'my' -p824 -aS'a' -p825 -asS'those' -p826 -(lp827 -S'are' -p828 -asS'he' -p829 -(lp830 -S'put' -p831 -aS'disabled' -p832 -aS'is' -p833 -aS'keeps' -p834 -aS'knows' -p835 -aS'stores' -p836 -aS'notices' -p837 -aS'only' -p838 -aS'doesnt' -p839 -aS'receives' -p840 -aS'says' -p841 -aS'messes' -p842 -asS'me' -p843 -(lp844 -S'figure' -p845 -aS'laugh' -p846 -aS'to' -p847 -aS'meatbag' -p848 -asS'also' -p849 -(lp850 -S'i' -p851 -aS'we' -p852 -aS'now' -p853 -asS'kind' -p854 -(lp855 -S'of' -p856 -asS'main' -p857 -(lp858 -S'bot' -p859 -asS'/' -p860 -(lp861 -S'hostmask' -p862 -aS'off' -p863 -asS'full' -p864 -(lp865 -S'of' -p866 -asS'these' -p867 -(lp868 -S'are' -p869 -asS'makie' -p870 -(lp871 -S'it' -p872 -asS'sleepers' -p873 -(lp874 -S'made' -p875 -asS'up' -p876 -(lp877 -S'elsewhere' -p878 -aS'me' -p879 -aS'a' -p880 -asS'will' -p881 -(lp882 -S'annoy' -p883 -asS'computer' -p884 -(lp885 -S'explodd' -p886 -asS'limit' -p887 -(lp888 -S'on' -p889 -asS'can' -p890 -(lp891 -S'fine-tune' -p892 -aS'add' -p893 -aS'i' -p894 -aS'we' -p895 -asS'how' -p896 -(lp897 -S'its' -p898 -aS'he' -p899 -asS'were' -p900 -(lp901 -S'the' -p902 -asS'malf' -p903 -(lp904 -S'blow' -p905 -asS'baystation' -p906 -(lp907 -S'12' -p908 -asS'other' -p909 -(lp910 -S'loop' -p911 -asS'my' -p912 -(lp913 -S'end' -p914 -aS'computer' -p915 -aS'code' -p916 -asS'called' -p917 -(lp918 -S'as' -p919 -aS'when' -p920 -asS'loop' -p921 -(lp922 -S'times' -p923 -asS'expect' -p924 -(lp925 -S'it' -p926 -asS'and' -p927 -(lp928 -S'bs12' -p929 -aS'stops' -p930 -aS'i' -p931 -aS'do' -p932 -aS'to' -p933 -aS'make' -p934 -aS'he' -p935 -aS'preferably' -p936 -aS'thats' -p937 -aS'sayt' -p938 -aS'fascism' -p939 -asS'dedicated' -p940 -(lp941 -S'solely' -p942 -asS'changed' -p943 -(lp944 -S'it' -p945 -asS'sees' -p946 -(lp947 -S'a' -p948 -asS'relaying' -p949 -(lp950 -S'adminhelps' -p951 -asS'figure' -p952 -(lp953 -S'out' -p954 -asS'do' -p955 -(lp956 -S'not' -p957 -aS'it' -p958 -aS'seem' -p959 -asS'ran' -p960 -(lp961 -S'the' -p962 -asS'ah' -p963 -(lp964 -S'running' -p965 -aS'ok' -p966 -asS'is' -p967 -(lp968 -S'baystation' -p969 -aS'a' -p970 -aS'that' -p971 -aS'it' -p972 -aS'coded' -p973 -aS'python' -p974 -aS'open' -p975 -aS'dependant' -p976 -aS'so' -p977 -aS'apparently' -p978 -asS'ram' -p979 -(lp980 -S'footprint' -p981 -asS'am' -p982 -(lp983 -S'the' -p984 -asS'it' -p985 -(lp986 -S'up' -p987 -aS'starts' -p988 -aS'receives' -p989 -aS'just' -p990 -aS'expires' -p991 -aS'works' -p992 -aS'always' -p993 -aS'decides' -p994 -aS'on' -p995 -aS'here' -p996 -aS'used' -p997 -aS'to' -p998 -aS'those' -p999 -aS'sees' -p1000 -aS'does' -p1001 -aS'myself' -p1002 -aS'a' -p1003 -aS'okay' -p1004 -aS'was' -p1005 -aS'go' -p1006 -aS'if' -p1007 -aS'once' -p1008 -aS'is' -p1009 -aS'for' -p1010 -asS'an' -p1011 -(lp1012 -S'hour' -p1013 -aS'illegal' -p1014 -aS'automatic' -p1015 -aS'error' -p1016 -asS'ready' -p1017 -(lp1018 -S'for' -p1019 -asS'say' -p1020 -(lp1021 -S'sleepers' -p1022 -aS'ai' -p1023 -aS'that*' -p1024 -aS'stop' -p1025 -asS'good' -p1026 -(lp1027 -S':p' -p1028 -asS'im' -p1029 -(lp1030 -S'not' -p1031 -aS'sorry' -p1032 -aS'lazy' -p1033 -asS'at' -p1034 -(lp1035 -S'all' -p1036 -aS'the' -p1037 -aS'no' -p1038 -asS'have' -p1039 -(lp1040 -S'no' -p1041 -aS'psyco' -p1042 -aS'the' -p1043 -aS'a' -p1044 -asS'in' -p1045 -(lp1046 -S'python' -p1047 -aS'a' -p1048 -aS'the' -p1049 -aS'100' -p1050 -asS'need' -p1051 -(lp1052 -S'to' -p1053 -aS'six' -p1054 -asS'politics' -p1055 -(lp1056 -S'of' -p1057 -asS'seem' -p1058 -(lp1059 -S'familiar' -p1060 -asS'work' -p1061 -(lp1062 -S'with' -p1063 -asS'apparently' -p1064 -(lp1065 -S'homophobic' -p1066 -aS'i' -p1067 -asS'any' -p1068 -(lp1069 -S'link' -p1070 -asS'as' -p1071 -(lp1072 -S'well' -p1073 -aS'variables' -p1074 -asS'sci-fi' -p1075 -(lp1076 -S'with' -p1077 -asS'preferably' -p1078 -(lp1079 -S'python' -p1080 -asS'really' -p1081 -(lp1082 -S'simple' -p1083 -aS'now' -p1084 -asS'needs' -p1085 -(lp1086 -S'to' -p1087 -aS'a' -p1088 -asS'null' -p1089 -(lp1090 -S'them' -p1091 -asS'because' -p1092 -(lp1093 -S'we' -p1094 -asS'want' -p1095 -(lp1096 -S'to' -p1097 -asS'no' -p1098 -(lp1099 -S'pain' -p1100 -aS'idea' -p1101 -aS'point' -p1102 -aS'the' -p1103 -asS'solely' -p1104 -(lp1105 -S'to' -p1106 -asS'nah' -p1107 -(lp1108 -S'its' -p1109 -aS'ill' -p1110 -asS'dunno' -p1111 -(lp1112 -S'is' -p1113 -asS'when' -p1114 -(lp1115 -S'it' -p1116 -aS'the' -p1117 -aS'people' -p1118 -aS'i' -p1119 -aS'can' -p1120 -aS'someone' -p1121 -aS'its' -p1122 -asS'same' -p1123 -(lp1124 -S'file' -p1125 -asS'id' -p1126 -(lp1127 -S'like' -p1128 -asS'note' -p1129 -(lp1130 -S'how' -p1131 -asS'figuring' -p1132 -(lp1133 -S'out' -p1134 -asS'bah' -p1135 -(lp1136 -S'apparently' -p1137 -asS'coded' -p1138 -(lp1139 -S'in' -p1140 -asS'take' -p1141 -(lp1142 -S'it' -p1143 -asS'hop' -p1144 -(lp1145 -S'to' -p1146 -asS'familiar' -p1147 -(lp1148 -S'message' -p1149 -asS'test' -p1150 -(lp1151 -S'server' -p1152 -aS'bots' -p1153 -aS'bad' -p1154 -asS'asshole' -p1155 -(lp1156 -g288 -asS'if' -p1157 -(lp1158 -S'it' -p1159 -aS'its' -p1160 -aS'data11lower' -p1161 -aS'he' -p1162 -asS'config' -p1163 -(lp1164 -S'file' -p1165 -aS'values' -p1166 -asS'homophobic' -p1167 -(lp1168 -S'as' -p1169 -asS'dose' -p1170 -(lp1171 -S'of' -p1172 -asS'play' -p1173 -(lp1174 -S'ss13' -p1175 -asS'sure' -p1176 -(lp1177 -S'the' -p1178 -aS'if' -p1179 -asS'okay' -p1180 -(lp1181 -S'desu' -p1182 -aS'cool' -p1183 -aS'cc' -p1184 -asS'intended' -p1185 -(lp1186 -S'to' -p1187 -asS'one' -p1188 -(lp1189 -S'of' -p1190 -aS'line' -p1191 -aS'in' -p1192 -asS'neat' -p1193 -(lp1194 -S'is' -p1195 -asS'adminhelps' -p1196 -(lp1197 -S'from' -p1198 -aS'with' -p1199 -asS'expires' -p1200 -(lp1201 -S'after' -p1202 -asS'chance' -p1203 -(lp1204 -S'every' -p1205 -asS'most' -p1206 -(lp1207 -S'of' -p1208 -asS'fascism' -p1209 -(lp1210 -g288 -asS'disable' -p1211 -(lp1212 -S'it' -p1213 -asS'connected' -p1214 -(lp1215 -S'businessman' -p1216 -asS'never' -p1217 -(lp1218 -S'learned' -p1219 -asS'scripts' -p1220 -(lp1221 -S'will' -p1222 -asS'along' -p1223 -(lp1224 -S'with' -p1225 -asS'waste' -p1226 -(lp1227 -S'space' -p1228 -asS'ss13' -p1229 -(lp1230 -S'servers' -p1231 -asS'cap' -p1232 -(lp1233 -S'troopers' -p1234 -asS'totally' -p1235 -(lp1236 -S'need' -p1237 -asS'six' -p1238 -(lp1239 -S'test' -p1240 -asS'a' -p1241 -(lp1242 -S'businessman' -p1243 -aS'message' -p1244 -aS'jit' -p1245 -aS'10-30%' -p1246 -aS'slightly' -p1247 -aS'day' -p1248 -aS'test' -p1249 -aS'config' -p1250 -aS'20ish' -p1251 -aS'bs12' -p1252 -aS'text' -p1253 -aS'new' -p1254 -aS'password' -p1255 -aS'vhost' -p1256 -aS'configurable' -p1257 -aS'link' -p1258 -aS'limit' -p1259 -aS'file' -p1260 -aS'dose' -p1261 -aS'500' -p1262 -aS'bit' -p1263 -asS'ofc' -p1264 -(lp1265 -S'i' -p1266 -asS'off' -p1267 -(lp1268 -S'of' -p1269 -asS'calls' -p1270 -(lp1271 -S'external' -p1272 -asS'i' -p1273 -(lp1274 -S'need' -p1275 -aS'can' -p1276 -aS'dont' -p1277 -aS'am' -p1278 -aS'guess' -p1279 -aS'see' -p1280 -aS'have' -p1281 -aS'just' -p1282 -aS'dunno' -p1283 -aS'wrote' -p1284 -aS'know' -p1285 -aS'was' -p1286 -aS'changed' -p1287 -aS'take' -p1288 -aS'could' -p1289 -aS'never' -p1290 -aS'should' -p1291 -aS'say' -p1292 -aS'tell' -p1293 -aS'code' -p1294 -aS'would' -p1295 -aS'like' -p1296 -aS'disabled' -p1297 -asS'makes' -p1298 -(lp1299 -S'sense' -p1300 -aS'me' -p1301 -asS'calculated' -p1302 -(lp1303 -S'for' -p1304 -asS'afk' -p1305 -(lp1306 -S'vidya' -p1307 -asS'well' -p1308 -(lp1309 -S'connected' -p1310 -asS'data' -p1311 -(lp1312 -S'in' -p1313 -asS'homoerotic' -p1314 -(lp1315 -S'sci-fi' -p1316 -asS'switch' -p1317 -(lp1318 -S'after' -p1319 -aS'goes' -p1320 -asS'so' -p1321 -(lp1322 -S'i' -p1323 -aS'uh' -p1324 -aS':p' -p1325 -aS'bad' -p1326 -aS'sly' -p1327 -asS'someones' -p1328 -(lp1329 -S'name' -p1330 -asS'keeps' -p1331 -(lp1332 -S'all' -p1333 -asS'very' -p1334 -(lp1335 -S'celestialike' -p1336 -asS'businessman' -p1337 -(lp1338 -S'of' -p1339 -ag288 -asS'the' -p1340 -(lp1341 -S'heck' -p1342 -aS'major' -p1343 -aS'well' -p1344 -aS'politics' -p1345 -aS'rp-heavy' -p1346 -aS'marakov' -p1347 -aS'law' -p1348 -aS'message' -p1349 -aS'cost' -p1350 -aS'new' -p1351 -aS'nudge' -p1352 -aS'bot' -p1353 -aS'python' -p1354 -aS'basic' -p1355 -aS'whole' -p1356 -aS'configuration' -p1357 -aS'irc' -p1358 -aS'readme' -p1359 -aS'default' -p1360 -aS'conspiracy' -p1361 -aS'config' -p1362 -aS'webpage' -p1363 -aS'channel' -p1364 -aS'server' -p1365 -aS'download' -p1366 -aS'main' -p1367 -aS'dmb' -p1368 -aS'part' -p1369 -aS'people' -p1370 -aS'same' -p1371 -aS'hell' -p1372 -aS'other' -p1373 -aS'switch' -p1374 -asS'12' -p1375 -(lp1376 -S'out' -p1377 -aS'anyway' -p1378 -asS'core' -p1379 -(lp1380 -S'py' -p1381 -asS'make' -p1382 -(lp1383 -S'sure' -p1384 -aS'the' -p1385 -asS'turns' -p1386 -(lp1387 -S'out' -p1388 -asS'external' -p1389 -(lp1390 -S'apps' -p1391 +(dp0 +S'all' +p1 +(lp2 +S':p' +p3 +aS'the' +p4 +asS'code' +p5 +(lp6 +S'in' +p7 +asS'stores' +p8 +(lp9 +S'that' +p10 +asS'just' +p11 +(lp12 +S'like' +p13 +aS'marakov' +p14 +aS'felt' +p15 +aS'gives' +p16 +aS'a' +p17 +aS'add' +p18 +aS'what' +p19 +asS'being' +p20 +(lp21 +S'goon' +p22 +aS'a' +p23 +asS'text' +p24 +(lp25 +S'string' +p26 +asS'dependant' +p27 +(lp28 +S'on' +p29 +asS'speedup' +p30 +(lp31 +S'at' +p32 +asS'felt' +p33 +(lp34 +S'like' +p35 +asS'installed' +p36 +(lp37 +S'tho' +p38 +asS'disabled' +p39 +(lp40 +S'it' +p41 +asS'timing' +p42 +(lp43 +S'when' +p44 +asS'psyco' +p45 +(lp46 +S'installed' +p47 +aS'is' +p48 +asS'stops' +p49 +(lp50 +S'timing' +p51 +asS'file' +p52 +(lp53 +S'too' +p54 +aS'that' +p55 +aS'where' +p56 +asS'go' +p57 +(lp58 +S'fuck' +p59 +aS'into' +p60 +aS'test' +p61 +asS'hell' +p62 +(lp63 +S'recreate' +p64 +asS'configurable' +p65 +(lp66 +S'greeting' +p67 +asS'bs12' +p68 +(lp69 +S'message' +p70 +asS'its' +p71 +(lp72 +S'fine' +p73 +aS'just' +p74 +aS'calculated' +p75 +aS'not' +p76 +aS'really' +p77 +aS'ridiculously' +p78 +aS'now' +p79 +aS'a' +p80 +aS'missing' +p81 +aS'for' +p82 +aS'false' +p83 +aS'on' +p84 +asS'before' +p85 +(lp86 +S'that' +p87 +asS'rp-heavy' +p88 +(lp89 +S'server' +p90 +asS'announcement' +p91 +(lp92 +S'like' +p93 +asS'now' +p94 +(lp95 +S'it' +p96 +aS'running' +p97 +aS'makie' +p98 +aS'i' +p99 +asS'nudge' +p100 +(lp101 +S'python' +p102 +aS'is' +p103 +asS'sourced' +p104 +(lp105 +S'under' +p106 +asS'title' +p107 +(lp108 +S'of' +p109 +asS'situations' +p110 +(lp111 +S'where' +p112 +asS'fine-tune' +p113 +(lp114 +S'it' +p115 +asS'enough' +p116 +(lp117 +S'to' +p118 +asS'send' +p119 +(lp120 +S'it' +p121 +aS'one' +p122 +asS'should' +p123 +(lp124 +S'be' +p125 +aS'learn' +p126 +aS'we' +p127 +asS'values' +p128 +(lp129 +S'dont' +p130 +asS'to' +p131 +(lp132 +S'edit' +p133 +aS'back' +p134 +aS'know' +p135 +aS'care' +p136 +aS'be' +p137 +aS'configure' +p138 +aS'the' +p139 +aS'null' +p140 +aS'phone' +p141 +aS'welcome' +p142 +aS'reverse' +p143 +aS'send' +p144 +aS'reg' +p145 +aS'point' +p146 +aS'automatically' +p147 +aS'work' +p148 +aS'waste' +p149 +aS'marshmallow' +p150 +aS'queries' +p151 +aS'+o' +p152 +aS'disable' +p153 +asS'jit' +p154 +(lp155 +S'compiler' +p156 +asS'going' +p157 +(lp158 +S'to' +p159 +asS'helps' +p160 +(lp161 +S'me' +p162 +asS'messes' +p163 +(lp164 +S'it' +p165 +asS'indeed' +p166 +(lp167 +S'it' +p168 +asS'tg' +p169 +(lp170 +S'and' +p171 +asS'has' +p172 +(lp173 +S'been' +p174 +aS'my' +p175 +asS'into' +p176 +(lp177 +S'#tgstation13' +p178 +asS'ridiculously' +p179 +(lp180 +S'simple' +p181 +asS'annoy' +p182 +(lp183 +S'downstream' +p184 +asS'them' +p185 +(lp186 +S'out' +p187 +asS'someone' +p188 +(lp189 +S'adminhelps' +p190 +asS'sense' +p191 +(lp192 +S'i' +p193 +asS'string' +p194 +(lp195 +S'called' +p196 +asS'get' +p197 +(lp198 +S'ready' +p199 +asS'python' +p200 +(lp201 +S'script' +p202 +aS'so' +p203 +aS'code' +p204 +aS'scripts' +p205 +aS'and' +p206 +aS'but' +p207 +aS'now' +p208 +aS'released' +p209 +aS'is' +p210 +aS'enough' +p211 +asS'goon' +p212 +(lp213 +S'tg' +p214 +asS'showing' +p215 +(lp216 +S'up' +p217 +asS'20ish' +p218 +(lp219 +S'line' +p220 +asS'gonna' +p221 +(lp222 +S'go' +p223 +asS'made' +p224 +(lp225 +S'doctors' +p226 +asS'every' +p227 +(lp228 +S'loop' +p229 +aS'time' +p230 +asS'know' +p231 +(lp232 +S'the' +p233 +aS'why' +p234 +aS'that' +p235 +asS'not' +p236 +(lp237 +S'just' +p238 +aS'necessary' +p239 +aS'to' +p240 +aS'so' +p241 +aS'need' +p242 +aS'sure' +p243 +aS'relaying' +p244 +aS'very' +p245 +aS'even' +p246 +asS'2' +p247 +(lp248 +S'loop' +p249 +asS'password' +p250 +(lp251 +S'var' +p252 +asS'day' +p253 +(lp254 +S'so' +p255 +asS'swapping' +p256 +(lp257 +S'to' +p258 +asS'easily' +p259 +(lp260 +S'editable' +p261 +asS'necessary' +p262 +(lp263 +S'at' +p264 +asS'like' +p265 +(lp266 +S'being' +p267 +aS'linking' +p268 +aS'to' +p269 +aS'how' +p270 +aS'it' +p271 +aS'skbzzzzzibi' +p272 +asS'course' +p273 +(lp274 +S'that' +p275 +asS'edit' +p276 +(lp277 +S'baystation' +p278 +asS'fully' +p279 +(lp280 +S'open' +p281 +asS'greeting' +p282 +(lp283 +S'message' +p284 +asS'server' +p285 +(lp286 +S'basically' +p287 +aS'' +p288 +asS'default' +p289 +(lp290 +S'config' +p291 +asS'bad' +p292 +(lp293 +S'company' +p294 +asS'channel' +p295 +(lp296 +S'or' +p297 +asS'always' +p298 +(lp299 +S'makes' +p300 +asS'went' +p301 +(lp302 +S'past' +p303 +asS'quarxink' +p304 +(lp305 +S'its' +p306 +asS'automatic' +p307 +(lp308 +S'announcement' +p309 +asS'once' +p310 +(lp311 +S'per' +p312 +asS'wrote' +p313 +(lp314 +S'most' +p315 +asS'pain' +p316 +(lp317 +S'on' +p318 +asS'system' +p319 +(lp320 +S'calls' +p321 +asS'right' +p322 +(lp323 +S'brb' +p324 +asS'decides' +p325 +(lp326 +S'not' +p327 +asS'people' +p328 +(lp329 +S'say' +p330 +aS'i' +p331 +aS'he' +p332 +asS'goddamn' +p333 +(lp334 +S'python' +p335 +asS'back' +p336 +(lp337 +S'it' +p338 +asS'used' +p339 +(lp340 +S'to' +p341 +aS'for' +p342 +asS'past' +p343 +(lp344 +S'too' +p345 +asS'cost' +p346 +(lp347 +S'of' +p348 +asS'learn' +p349 +(lp350 +S'python' +p351 +asS'are' +p352 +(lp353 +S'lawyers' +p354 +aS'actually' +p355 +aS'configurable' +p356 +aS'we' +p357 +asS'celestialike' +p358 +(lp359 +S'of' +p360 +asS'lawyers' +p361 +(lp362 +S'for' +p363 +asS'time' +p364 +(lp365 +S'he' +p366 +asS'out' +p367 +(lp368 +S'switch' +p369 +aS'slowdowns' +p370 +aS'that' +p371 +aS'why' +p372 +aS'nudge' +p373 +asS'even' +p374 +(lp375 +S'an' +p376 +asS'what' +p377 +(lp378 +S'the' +p379 +aS'is' +p380 +aS'these' +p381 +aS'was' +p382 +aS'license' +p383 +aS'about' +p384 +aS'i' +p385 +asS'said' +p386 +(lp387 +S'in' +p388 +asS'sayt' +p389 +(lp390 +S'hat' +p391 +asS'for' +p392 +(lp393 +S'that' +p394 +aS'every' +p395 +aS'the' +p396 +aS'quarx' +p397 +aS'cc_nanotrasen' +p398 +aS'homoerotic' +p399 +aS'situations' +p400 +aS'good' +p401 +asS'#tgstation13' +p402 +(lp403 +S'and' +p404 +asS'per' +p405 +(lp406 +S'name' +p407 +asS'whole' +p408 +(lp409 +S'config' +p410 +asS'state' +p411 +(lp412 +S'the' +p413 +asS'does' +p414 +(lp415 +S'the' +p416 +aS'it' +p417 +asS'goes' +p418 +(lp419 +S'on' +p420 +asS'readme' +p421 +(lp422 +S'too' +p423 +asS'new' +p424 +(lp425 +S'bot' +p426 +aS'person' +p427 +asS'learned' +p428 +(lp429 +S'python' +p430 +asS'irc' +p431 +(lp432 +S'bot' +p433 +asS'reg' +p434 +(lp435 +S'it' +p436 +asS'blow' +p437 +(lp438 +S'borgs' +p439 +asS'shut' +p440 +(lp441 +S'down' +p442 +asS'after' +p443 +(lp444 +S'an' +p445 +aS'a' +p446 +asS'ill' +p447 +(lp448 +S'switch' +p449 +asS'says' +p450 +(lp451 +S'someones' +p452 +asS'queries' +p453 +(lp454 +S'again' +p455 +asS'technocracy' +p456 +(lp457 +S'and' +p458 +asS'we' +p459 +(lp460 +S'go' +p461 +aS'do' +p462 +aS'totally' +p463 +aS'going' +p464 +aS'expect' +p465 +aS'just' +p466 +aS'dont' +p467 +aS'have' +p468 +asS'put' +p469 +(lp470 +S'all' +p471 +asS'from' +p472 +(lp473 +S'the' +p474 +asS'data11lower' +p475 +(lp476 +S'==' +p477 +asS'configuration' +p478 +(lp479 +S'for' +p480 +asS'wait' +p481 +(lp482 +S'what' +p483 +asS'on' +p484 +(lp485 +S'my' +p486 +aS'the' +p487 +aS'a' +p488 +aS'connect' +p489 +aS'svn' +p490 +aS'one' +p491 +aS'/' +p492 +asS'about' +p493 +(lp494 +S'system' +p495 +asS'ok' +p496 +(lp497 +S'thats' +p498 +asS'reverse' +p499 +(lp500 +S'engineer' +p501 +asS'license' +p502 +(lp503 +S'is' +p504 +asS'oh' +p505 +(lp506 +S'okay' +p507 +aS'ok' +p508 +aS'i' +p509 +aS'wait' +p510 +asS'starts' +p511 +(lp512 +S'timing' +p513 +asS'could' +p514 +(lp515 +S'learn' +p516 +asS'larger' +p517 +(lp518 +S'ram' +p519 +asS'bot' +p520 +(lp521 +S'is' +p522 +aS'have' +p523 +aS'shut' +p524 +aS'uses' +p525 +asS'running' +p526 +(lp527 +S'it' +p528 +aS'the' +p529 +aS'on' +p530 +asS'times' +p531 +(lp532 +S'on' +p533 +aS'reported' +p534 +aS'went' +p535 +asS'where' +p536 +(lp537 +S'he' +p538 +asS'heck' +p539 +(lp540 +S':d' +p541 +asS'idk' +p542 +(lp543 +S'magic' +p544 +asS'receives' +p545 +(lp546 +S'a' +p547 +asS'bots' +p548 +(lp549 +S'showing' +p550 +asS'slightly' +p551 +(lp552 +S'larger' +p553 +asS'or' +p554 +(lp555 +S'know' +p556 +aS'what' +p557 +aS'work' +p558 +aS'should' +p559 +aS'data11lower' +p560 +asS'automatically' +p561 +(lp562 +S'state' +p563 +asS'thats' +p564 +(lp565 +S'not' +p566 +aS'the' +p567 +aS'kind' +p568 +asS'ugh' +p569 +(lp570 +S'fuck' +p571 +asS'major' +p572 +(lp573 +S'ss13' +p574 +aS'three' +p575 +asS'py' +p576 +(lp577 +S'file' +p578 +asS'soss' +p579 +(lp580 +S'server' +p581 +asS'dont' +p582 +(lp583 +S'need' +p584 +aS'have' +p585 +aS'send' +p586 +aS'want' +p587 +aS'speak' +p588 +asS'hostmask' +p589 +(lp590 +S'combination' +p591 +asS'point' +p592 +(lp593 +S'out' +p594 +aS'has' +p595 +asS'simple' +p596 +(lp597 +S'to' +p598 +asS'miura' +p599 +(lp600 +S'doesnt' +p601 +asS'variables' +p602 +(lp603 +S'in' +p604 +asS'recreate' +p605 +(lp606 +S'it' +p607 +asS'welcome' +p608 +(lp609 +S'to' +p610 +asS'linking' +p611 +(lp612 +S'them' +p613 +asS'down' +p614 +(lp615 +S'when' +p616 +asS'why' +p617 +(lp618 +S'its' +p619 +aS'it' +p620 +aS'is' +p621 +asS'doesnt' +p622 +(lp623 +S'play' +p624 +aS'call' +p625 +asS'marakov' +p626 +(lp627 +S'loops' +p628 +aS'helps' +p629 +asS'laugh' +p630 +(lp631 +S'when' +p632 +asS'pony' +p633 +(lp634 +S'asshole' +p635 +asS'message' +p636 +(lp637 +S'has' +p638 +aS'should' +p639 +asS'open' +p640 +(lp641 +S'sourced' +p642 +aS'source' +p643 +asS'brb' +p644 +(lp645 +S'swapping' +p646 +asS'speak' +p647 +(lp648 +S'python' +p649 +asS'pastebin' +p650 +(lp651 +S'the' +p652 +asS'line' +p653 +(lp654 +S'core' +p655 +aS'on' +p656 +asS'three' +p657 +(lp658 +S'being' +p659 +asS'yay' +p660 +(lp661 +S'it' +p662 +asS'meatbag' +p663 +(lp664 +S'when' +p665 +asS'would' +p666 +(lp667 +S'be' +p668 +aS'expect' +p669 +asS'script' +p670 +(lp671 +S'that' +p672 +asS'illegal' +p673 +(lp674 +S'ban' +p675 +asS'there' +p676 +(lp677 +S'are' +p678 +aS'we' +p679 +asS'add' +p680 +(lp681 +S'that' +p682 +aS'a' +p683 +asS'been' +p684 +(lp685 +S'processed' +p686 +asS'name' +p687 +(lp688 +S'/' +p689 +aS'when' +p690 +asS'ai' +p691 +(lp692 +S'malf' +p693 +asS'marshmallow' +p694 +(lp695 +S'pony' +p696 +asS'of' +p697 +(lp698 +S'the' +p699 +aS'ss13' +p700 +aS'tgstation13' +p701 +aS'a' +p702 +aS'course' +p703 +aS'it' +p704 +aS'soss' +p705 +aS'annoying' +p706 +aS'any' +p707 +aS'me' +p708 +aS'cap' +p709 +aS'technocracy' +p710 +asS'call' +p711 +(lp712 +S'me' +p713 +asS'too' +p714 +(lp715 +S':' +p716 +aS'fast' +p717 +asS'basic' +p718 +(lp719 +S'configuration' +p720 +asS'var' +p721 +(lp722 +S'and' +p723 +asS'calc' +p724 +(lp725 +S'times' +p726 +asS'was' +p727 +(lp728 +S'going' +p729 +aS'it' +p730 +aS'that' +p731 +aS'not' +p732 +aS'intended' +p733 +asS'tell' +p734 +(lp735 +S'people' +p736 +asS'500' +p737 +(lp738 +S'chance' +p739 +asS'gives' +p740 +(lp741 +S'a' +p742 +asS'sort' +p743 +(lp744 +S'of' +p745 +asS'svn' +p746 +(lp747 +S'size' +p748 +asS'only' +p749 +(lp750 +S'does' +p751 +aS'2' +p752 +asS'10-30%' +p753 +(lp754 +S'speedup' +p755 +asS'knows' +p756 +(lp757 +S'about' +p758 +asS'webpage' +p759 +(lp760 +S'title' +p761 +asS'that' +p762 +(lp763 +S'makes' +p764 +aS'would' +p765 +aS'the' +p766 +aS'needs' +p767 +aS'to' +p768 +aS'at' +p769 +aS'for' +p770 +aS'was' +p771 +aS'data' +p772 +asS'company' +p773 +(lp774 +S'2' +p775 +asS'under' +p776 +(lp777 +S'cc-by-sa' +p778 +asS'editable' +p779 +(lp780 +S'config' +p781 +asS'but' +p782 +(lp783 +S'of' +p784 +asS'idea' +p785 +(lp786 +S'what' +p787 +asS'released' +p788 +(lp789 +S'under' +p790 +asS'part' +p791 +(lp792 +S'before' +p793 +asS'link' +p794 +(lp795 +S'said' +p796 +aS'to' +p797 +asS'basically' +p798 +(lp799 +S'it' +p800 +asS'doctors' +p801 +(lp802 +S'useless' +p803 +asS'==' +p804 +(lp805 +S'channel' +p806 +aS'channel1::' +p807 +asS'be' +p808 +(lp809 +S'an' +p810 +aS'called' +p811 +aS'a' +p812 +aS'in' +p813 +aS'running' +p814 +aS'used' +p815 +asS'editing' +p816 +(lp817 +S'goddamn' +p818 +asS'with' +p819 +(lp820 +S'the' +p821 +aS'easily' +p822 +aS'adminhelps' +p823 +aS'my' +p824 +aS'a' +p825 +asS'those' +p826 +(lp827 +S'are' +p828 +asS'he' +p829 +(lp830 +S'put' +p831 +aS'disabled' +p832 +aS'is' +p833 +aS'keeps' +p834 +aS'knows' +p835 +aS'stores' +p836 +aS'notices' +p837 +aS'only' +p838 +aS'doesnt' +p839 +aS'receives' +p840 +aS'says' +p841 +aS'messes' +p842 +asS'me' +p843 +(lp844 +S'figure' +p845 +aS'laugh' +p846 +aS'to' +p847 +aS'meatbag' +p848 +asS'also' +p849 +(lp850 +S'i' +p851 +aS'we' +p852 +aS'now' +p853 +asS'kind' +p854 +(lp855 +S'of' +p856 +asS'main' +p857 +(lp858 +S'bot' +p859 +asS'/' +p860 +(lp861 +S'hostmask' +p862 +aS'off' +p863 +asS'full' +p864 +(lp865 +S'of' +p866 +asS'these' +p867 +(lp868 +S'are' +p869 +asS'makie' +p870 +(lp871 +S'it' +p872 +asS'sleepers' +p873 +(lp874 +S'made' +p875 +asS'up' +p876 +(lp877 +S'elsewhere' +p878 +aS'me' +p879 +aS'a' +p880 +asS'will' +p881 +(lp882 +S'annoy' +p883 +asS'computer' +p884 +(lp885 +S'explodd' +p886 +asS'limit' +p887 +(lp888 +S'on' +p889 +asS'can' +p890 +(lp891 +S'fine-tune' +p892 +aS'add' +p893 +aS'i' +p894 +aS'we' +p895 +asS'how' +p896 +(lp897 +S'its' +p898 +aS'he' +p899 +asS'were' +p900 +(lp901 +S'the' +p902 +asS'malf' +p903 +(lp904 +S'blow' +p905 +asS'baystation' +p906 +(lp907 +S'12' +p908 +asS'other' +p909 +(lp910 +S'loop' +p911 +asS'my' +p912 +(lp913 +S'end' +p914 +aS'computer' +p915 +aS'code' +p916 +asS'called' +p917 +(lp918 +S'as' +p919 +aS'when' +p920 +asS'loop' +p921 +(lp922 +S'times' +p923 +asS'expect' +p924 +(lp925 +S'it' +p926 +asS'and' +p927 +(lp928 +S'bs12' +p929 +aS'stops' +p930 +aS'i' +p931 +aS'do' +p932 +aS'to' +p933 +aS'make' +p934 +aS'he' +p935 +aS'preferably' +p936 +aS'thats' +p937 +aS'sayt' +p938 +aS'fascism' +p939 +asS'dedicated' +p940 +(lp941 +S'solely' +p942 +asS'changed' +p943 +(lp944 +S'it' +p945 +asS'sees' +p946 +(lp947 +S'a' +p948 +asS'relaying' +p949 +(lp950 +S'adminhelps' +p951 +asS'figure' +p952 +(lp953 +S'out' +p954 +asS'do' +p955 +(lp956 +S'not' +p957 +aS'it' +p958 +aS'seem' +p959 +asS'ran' +p960 +(lp961 +S'the' +p962 +asS'ah' +p963 +(lp964 +S'running' +p965 +aS'ok' +p966 +asS'is' +p967 +(lp968 +S'baystation' +p969 +aS'a' +p970 +aS'that' +p971 +aS'it' +p972 +aS'coded' +p973 +aS'python' +p974 +aS'open' +p975 +aS'dependant' +p976 +aS'so' +p977 +aS'apparently' +p978 +asS'ram' +p979 +(lp980 +S'footprint' +p981 +asS'am' +p982 +(lp983 +S'the' +p984 +asS'it' +p985 +(lp986 +S'up' +p987 +aS'starts' +p988 +aS'receives' +p989 +aS'just' +p990 +aS'expires' +p991 +aS'works' +p992 +aS'always' +p993 +aS'decides' +p994 +aS'on' +p995 +aS'here' +p996 +aS'used' +p997 +aS'to' +p998 +aS'those' +p999 +aS'sees' +p1000 +aS'does' +p1001 +aS'myself' +p1002 +aS'a' +p1003 +aS'okay' +p1004 +aS'was' +p1005 +aS'go' +p1006 +aS'if' +p1007 +aS'once' +p1008 +aS'is' +p1009 +aS'for' +p1010 +asS'an' +p1011 +(lp1012 +S'hour' +p1013 +aS'illegal' +p1014 +aS'automatic' +p1015 +aS'error' +p1016 +asS'ready' +p1017 +(lp1018 +S'for' +p1019 +asS'say' +p1020 +(lp1021 +S'sleepers' +p1022 +aS'ai' +p1023 +aS'that*' +p1024 +aS'stop' +p1025 +asS'good' +p1026 +(lp1027 +S':p' +p1028 +asS'im' +p1029 +(lp1030 +S'not' +p1031 +aS'sorry' +p1032 +aS'lazy' +p1033 +asS'at' +p1034 +(lp1035 +S'all' +p1036 +aS'the' +p1037 +aS'no' +p1038 +asS'have' +p1039 +(lp1040 +S'no' +p1041 +aS'psyco' +p1042 +aS'the' +p1043 +aS'a' +p1044 +asS'in' +p1045 +(lp1046 +S'python' +p1047 +aS'a' +p1048 +aS'the' +p1049 +aS'100' +p1050 +asS'need' +p1051 +(lp1052 +S'to' +p1053 +aS'six' +p1054 +asS'politics' +p1055 +(lp1056 +S'of' +p1057 +asS'seem' +p1058 +(lp1059 +S'familiar' +p1060 +asS'work' +p1061 +(lp1062 +S'with' +p1063 +asS'apparently' +p1064 +(lp1065 +S'homophobic' +p1066 +aS'i' +p1067 +asS'any' +p1068 +(lp1069 +S'link' +p1070 +asS'as' +p1071 +(lp1072 +S'well' +p1073 +aS'variables' +p1074 +asS'sci-fi' +p1075 +(lp1076 +S'with' +p1077 +asS'preferably' +p1078 +(lp1079 +S'python' +p1080 +asS'really' +p1081 +(lp1082 +S'simple' +p1083 +aS'now' +p1084 +asS'needs' +p1085 +(lp1086 +S'to' +p1087 +aS'a' +p1088 +asS'null' +p1089 +(lp1090 +S'them' +p1091 +asS'because' +p1092 +(lp1093 +S'we' +p1094 +asS'want' +p1095 +(lp1096 +S'to' +p1097 +asS'no' +p1098 +(lp1099 +S'pain' +p1100 +aS'idea' +p1101 +aS'point' +p1102 +aS'the' +p1103 +asS'solely' +p1104 +(lp1105 +S'to' +p1106 +asS'nah' +p1107 +(lp1108 +S'its' +p1109 +aS'ill' +p1110 +asS'dunno' +p1111 +(lp1112 +S'is' +p1113 +asS'when' +p1114 +(lp1115 +S'it' +p1116 +aS'the' +p1117 +aS'people' +p1118 +aS'i' +p1119 +aS'can' +p1120 +aS'someone' +p1121 +aS'its' +p1122 +asS'same' +p1123 +(lp1124 +S'file' +p1125 +asS'id' +p1126 +(lp1127 +S'like' +p1128 +asS'note' +p1129 +(lp1130 +S'how' +p1131 +asS'figuring' +p1132 +(lp1133 +S'out' +p1134 +asS'bah' +p1135 +(lp1136 +S'apparently' +p1137 +asS'coded' +p1138 +(lp1139 +S'in' +p1140 +asS'take' +p1141 +(lp1142 +S'it' +p1143 +asS'hop' +p1144 +(lp1145 +S'to' +p1146 +asS'familiar' +p1147 +(lp1148 +S'message' +p1149 +asS'test' +p1150 +(lp1151 +S'server' +p1152 +aS'bots' +p1153 +aS'bad' +p1154 +asS'asshole' +p1155 +(lp1156 +g288 +asS'if' +p1157 +(lp1158 +S'it' +p1159 +aS'its' +p1160 +aS'data11lower' +p1161 +aS'he' +p1162 +asS'config' +p1163 +(lp1164 +S'file' +p1165 +aS'values' +p1166 +asS'homophobic' +p1167 +(lp1168 +S'as' +p1169 +asS'dose' +p1170 +(lp1171 +S'of' +p1172 +asS'play' +p1173 +(lp1174 +S'ss13' +p1175 +asS'sure' +p1176 +(lp1177 +S'the' +p1178 +aS'if' +p1179 +asS'okay' +p1180 +(lp1181 +S'desu' +p1182 +aS'cool' +p1183 +aS'cc' +p1184 +asS'intended' +p1185 +(lp1186 +S'to' +p1187 +asS'one' +p1188 +(lp1189 +S'of' +p1190 +aS'line' +p1191 +aS'in' +p1192 +asS'neat' +p1193 +(lp1194 +S'is' +p1195 +asS'adminhelps' +p1196 +(lp1197 +S'from' +p1198 +aS'with' +p1199 +asS'expires' +p1200 +(lp1201 +S'after' +p1202 +asS'chance' +p1203 +(lp1204 +S'every' +p1205 +asS'most' +p1206 +(lp1207 +S'of' +p1208 +asS'fascism' +p1209 +(lp1210 +g288 +asS'disable' +p1211 +(lp1212 +S'it' +p1213 +asS'connected' +p1214 +(lp1215 +S'businessman' +p1216 +asS'never' +p1217 +(lp1218 +S'learned' +p1219 +asS'scripts' +p1220 +(lp1221 +S'will' +p1222 +asS'along' +p1223 +(lp1224 +S'with' +p1225 +asS'waste' +p1226 +(lp1227 +S'space' +p1228 +asS'ss13' +p1229 +(lp1230 +S'servers' +p1231 +asS'cap' +p1232 +(lp1233 +S'troopers' +p1234 +asS'totally' +p1235 +(lp1236 +S'need' +p1237 +asS'six' +p1238 +(lp1239 +S'test' +p1240 +asS'a' +p1241 +(lp1242 +S'businessman' +p1243 +aS'message' +p1244 +aS'jit' +p1245 +aS'10-30%' +p1246 +aS'slightly' +p1247 +aS'day' +p1248 +aS'test' +p1249 +aS'config' +p1250 +aS'20ish' +p1251 +aS'bs12' +p1252 +aS'text' +p1253 +aS'new' +p1254 +aS'password' +p1255 +aS'vhost' +p1256 +aS'configurable' +p1257 +aS'link' +p1258 +aS'limit' +p1259 +aS'file' +p1260 +aS'dose' +p1261 +aS'500' +p1262 +aS'bit' +p1263 +asS'ofc' +p1264 +(lp1265 +S'i' +p1266 +asS'off' +p1267 +(lp1268 +S'of' +p1269 +asS'calls' +p1270 +(lp1271 +S'external' +p1272 +asS'i' +p1273 +(lp1274 +S'need' +p1275 +aS'can' +p1276 +aS'dont' +p1277 +aS'am' +p1278 +aS'guess' +p1279 +aS'see' +p1280 +aS'have' +p1281 +aS'just' +p1282 +aS'dunno' +p1283 +aS'wrote' +p1284 +aS'know' +p1285 +aS'was' +p1286 +aS'changed' +p1287 +aS'take' +p1288 +aS'could' +p1289 +aS'never' +p1290 +aS'should' +p1291 +aS'say' +p1292 +aS'tell' +p1293 +aS'code' +p1294 +aS'would' +p1295 +aS'like' +p1296 +aS'disabled' +p1297 +asS'makes' +p1298 +(lp1299 +S'sense' +p1300 +aS'me' +p1301 +asS'calculated' +p1302 +(lp1303 +S'for' +p1304 +asS'afk' +p1305 +(lp1306 +S'vidya' +p1307 +asS'well' +p1308 +(lp1309 +S'connected' +p1310 +asS'data' +p1311 +(lp1312 +S'in' +p1313 +asS'homoerotic' +p1314 +(lp1315 +S'sci-fi' +p1316 +asS'switch' +p1317 +(lp1318 +S'after' +p1319 +aS'goes' +p1320 +asS'so' +p1321 +(lp1322 +S'i' +p1323 +aS'uh' +p1324 +aS':p' +p1325 +aS'bad' +p1326 +aS'sly' +p1327 +asS'someones' +p1328 +(lp1329 +S'name' +p1330 +asS'keeps' +p1331 +(lp1332 +S'all' +p1333 +asS'very' +p1334 +(lp1335 +S'celestialike' +p1336 +asS'businessman' +p1337 +(lp1338 +S'of' +p1339 +ag288 +asS'the' +p1340 +(lp1341 +S'heck' +p1342 +aS'major' +p1343 +aS'well' +p1344 +aS'politics' +p1345 +aS'rp-heavy' +p1346 +aS'marakov' +p1347 +aS'law' +p1348 +aS'message' +p1349 +aS'cost' +p1350 +aS'new' +p1351 +aS'nudge' +p1352 +aS'bot' +p1353 +aS'python' +p1354 +aS'basic' +p1355 +aS'whole' +p1356 +aS'configuration' +p1357 +aS'irc' +p1358 +aS'readme' +p1359 +aS'default' +p1360 +aS'conspiracy' +p1361 +aS'config' +p1362 +aS'webpage' +p1363 +aS'channel' +p1364 +aS'server' +p1365 +aS'download' +p1366 +aS'main' +p1367 +aS'dmb' +p1368 +aS'part' +p1369 +aS'people' +p1370 +aS'same' +p1371 +aS'hell' +p1372 +aS'other' +p1373 +aS'switch' +p1374 +asS'12' +p1375 +(lp1376 +S'out' +p1377 +aS'anyway' +p1378 +asS'core' +p1379 +(lp1380 +S'py' +p1381 +asS'make' +p1382 +(lp1383 +S'sure' +p1384 +aS'the' +p1385 +asS'turns' +p1386 +(lp1387 +S'out' +p1388 +asS'external' +p1389 +(lp1390 +S'apps' +p1391 as. \ No newline at end of file diff --git a/bot/Marakov_Chain.py b/bot/Marakov_Chain.py index 687c4336327..8c144ebfe86 100644 --- a/bot/Marakov_Chain.py +++ b/bot/Marakov_Chain.py @@ -1,203 +1,203 @@ -import pickle -import random -import os -import sys -import time -import CORE_DATA -def merge(d1, d2, merger=lambda x,y:x+y): - #http://stackoverflow.com/questions/38987/how-can-i-merge-two-python-dictionaries-as-a-single-expression - result = dict(d1) - for k,v in d2.iteritems(): - if k in result: - result[k] = merger(result[k], v) - else: - result[k] = v - return result -full_data = {} -imported_data = {} -try: - tiedostot = os.listdir("Marakov") -except: - os.mkdir("Marakov") - tiedostot = os.listdir("Marakov") -else: - pass - -listaus = [] -for i in tiedostot: - if "marakov." not in i.lower(): - pass - else: - listaus.append(i) -for i in listaus: - tiedosto = open("Marakov/"+i,"r") - old_size = len(full_data.keys()) - if i != "Marakov.Cache": - imported_data = merge(imported_data,pickle.load(tiedosto)) - print "Added contents of "+i+" (Import)" - print "Entries: "+str(len(imported_data)) - else: - full_data = merge(full_data,pickle.load(tiedosto)) - new_size = len(full_data.keys()) - print "Added contents of "+i - print "Entries: "+str(new_size-old_size) - time.sleep(0.1) - -def give_data(data): - state = False - for a,b in zip(data.split(" "),data.split(" ")[1:]): - a = a.lower().replace(",","").replace(".","").replace("?","").replace("!","").replace("(","").replace(")","").replace("[","").replace("]","").replace('"',"").replace("'","") - b = b.lower().replace(",","").replace(".","").replace("?","").replace("!","").replace("(","").replace(")","").replace("[","").replace("]","").replace('"',"").replace("'","") - if a not in [CORE_DATA.prefix+"marakov"]+CORE_DATA.SName: - state = True - if a[:7] == "http://" or a[:7] == "http:\\\\" or a[:4] == "www.": - pass - else: - try: - if b not in full_data[a]: - full_data[a].append(b) - except: - try: - if b not in imported_data[a]: - pass - except: - full_data[a] = [] - full_data[a].append(b) - if state == True: - tiedosto = open("Marakov/Marakov.Cache","w") - pickle.dump(full_data,tiedosto) - tiedosto.close() -def form_sentence(argument=None): - length = 0 - attempts = 0 - while attempts < 20: - sentence = [] - if argument != None: - a = argument - else: - try: - a = random.choice(full_data.keys()) - except IndexError: - try: - b = random.choice(imported_data.keys()) - except IndexError: - attempts = 999 - return "No sentences formable at all" - sentence.append(a) - length = 0 - attempts += 1 - while length < 12 or sentence[-1].lower() in ["but","who","gets","im","most","is","it","if","then","after","over","every","of","on","or","as","the","wheather","whether","a","to","and","for"] and length < 24: - try: - b = random.choice(full_data[a]) - except: - try: - b = random.choice(imported_data[a]) - except IndexError: - break - except KeyError: - break - else: - sentence.append(b) - length += 1 - a = b - else: - sentence.append(b) - length += 1 - a = b - if len(sentence) > 5: - argument = None - return sentence - else: - pass - argument = None - return sentence -def remdata(arg): - try: - del(full_data[arg]) - except: - print "There is no such data" - else: - tiedosto = open("Marakov/Marakov.Cache","w") - pickle.dump(full_data,tiedosto) - tiedosto.close() -def remobject(arg1,arg2): - try: - del(full_data[arg1][full_data[arg1].index(arg2)]) - except ValueError: - print "No such object" - except KeyError: - print "No such data" - else: - tiedosto = open("Marakov/Marakov.Cache","w") - pickle.dump(full_data,tiedosto) - tiedosto.close() -def convert(filename_from,filename_to): - try: - tiedosto = open(filename_from,"r") - data = pickle.load(tiedosto) - tiedosto.close() - except: - try: - tiedosto.close() - except: - pass - print "Error!" - else: - for lista in data.keys(): - try: - a = lista[-1] - except IndexError: - pass - else: - if lista[-1] in """",.?!'()[]{}""" and not lista.islower(): - if lista[:-1].lower() in data.keys(): - data[lista[:-1].lower()] += data[lista] - print "Added "+str(len(data[lista]))+" Objects from "+lista+" To "+lista[:-1].lower() - del(data[lista]) - else: - data[lista[:-1].lower()] = data[lista] - print lista+" Is now "+lista[:-1].lower() - del(data[lista]) - elif lista[-1] in """",.?!'()[]{}""" and lista.islower(): - if lista[:-1] in data.keys(): - data[lista[:-1]] += data[lista] - print "Added "+str(len(data[lista]))+" Objects from "+lista+" To "+lista[:-1] - del(data[lista]) - else: - data[lista[:-1]] = data[lista] - print lista+" Is now "+lista[:-1] - del(data[lista]) - elif not lista.islower(): - if lista.lower() in data.keys(): - data[lista.lower()] += data[lista] - print "Added "+str(len(data[lista]))+" Objects from "+lista+" To "+lista.lower() - del(data[lista]) - else: - data[lista.lower()] = data[lista] - print lista+" Is now "+lista.lower() - del(data[lista]) - - - for a in data.keys(): - for b in data[a]: - if b.lower()[:7] == "http://" or b.lower()[:7] == "http:\\\\" or b.lower()[:4] == "www.": - data[a].pop(b) - else: - try: - if b[-1] in """",.?!'()[]{}""" and not b.islower() and not b.isdigit(): - data[a].pop(data[a].index(b)) - data[a].append(b[:-1].lower()) - print a+" | "+b +" -> "+b[:-1].lower() - elif b[-1] in """",.?!'()[]{}""" and b.islower(): - data[a].pop(data[a].index(b)) - data[a].append(b[:-1].lower()) - print a+" | "+b +" -> "+b[:-1] - elif not b.islower() and not b.isdigit(): - data[a].pop(data[a].index(b)) - data[a].append(b.lower()) - print a+" | "+b +" -> "+b.lower() - except IndexError: #If it has no letters.. well.. yeah. - data[a].pop(data[a].index(b)) - print "Removed a NULL object" - tiedosto = open(filename_to,"w") - pickle.dump(data,tiedosto) +import pickle +import random +import os +import sys +import time +import CORE_DATA +def merge(d1, d2, merger=lambda x,y:x+y): + #http://stackoverflow.com/questions/38987/how-can-i-merge-two-python-dictionaries-as-a-single-expression + result = dict(d1) + for k,v in d2.iteritems(): + if k in result: + result[k] = merger(result[k], v) + else: + result[k] = v + return result +full_data = {} +imported_data = {} +try: + tiedostot = os.listdir("Marakov") +except: + os.mkdir("Marakov") + tiedostot = os.listdir("Marakov") +else: + pass + +listaus = [] +for i in tiedostot: + if "marakov." not in i.lower(): + pass + else: + listaus.append(i) +for i in listaus: + tiedosto = open("Marakov/"+i,"r") + old_size = len(full_data.keys()) + if i != "Marakov.Cache": + imported_data = merge(imported_data,pickle.load(tiedosto)) + print "Added contents of "+i+" (Import)" + print "Entries: "+str(len(imported_data)) + else: + full_data = merge(full_data,pickle.load(tiedosto)) + new_size = len(full_data.keys()) + print "Added contents of "+i + print "Entries: "+str(new_size-old_size) + time.sleep(0.1) + +def give_data(data): + state = False + for a,b in zip(data.split(" "),data.split(" ")[1:]): + a = a.lower().replace(",","").replace(".","").replace("?","").replace("!","").replace("(","").replace(")","").replace("[","").replace("]","").replace('"',"").replace("'","") + b = b.lower().replace(",","").replace(".","").replace("?","").replace("!","").replace("(","").replace(")","").replace("[","").replace("]","").replace('"',"").replace("'","") + if a not in [CORE_DATA.prefix+"marakov"]+CORE_DATA.SName: + state = True + if a[:7] == "http://" or a[:7] == "http:\\\\" or a[:4] == "www.": + pass + else: + try: + if b not in full_data[a]: + full_data[a].append(b) + except: + try: + if b not in imported_data[a]: + pass + except: + full_data[a] = [] + full_data[a].append(b) + if state == True: + tiedosto = open("Marakov/Marakov.Cache","w") + pickle.dump(full_data,tiedosto) + tiedosto.close() +def form_sentence(argument=None): + length = 0 + attempts = 0 + while attempts < 20: + sentence = [] + if argument != None: + a = argument + else: + try: + a = random.choice(full_data.keys()) + except IndexError: + try: + b = random.choice(imported_data.keys()) + except IndexError: + attempts = 999 + return "No sentences formable at all" + sentence.append(a) + length = 0 + attempts += 1 + while length < 12 or sentence[-1].lower() in ["but","who","gets","im","most","is","it","if","then","after","over","every","of","on","or","as","the","wheather","whether","a","to","and","for"] and length < 24: + try: + b = random.choice(full_data[a]) + except: + try: + b = random.choice(imported_data[a]) + except IndexError: + break + except KeyError: + break + else: + sentence.append(b) + length += 1 + a = b + else: + sentence.append(b) + length += 1 + a = b + if len(sentence) > 5: + argument = None + return sentence + else: + pass + argument = None + return sentence +def remdata(arg): + try: + del(full_data[arg]) + except: + print "There is no such data" + else: + tiedosto = open("Marakov/Marakov.Cache","w") + pickle.dump(full_data,tiedosto) + tiedosto.close() +def remobject(arg1,arg2): + try: + del(full_data[arg1][full_data[arg1].index(arg2)]) + except ValueError: + print "No such object" + except KeyError: + print "No such data" + else: + tiedosto = open("Marakov/Marakov.Cache","w") + pickle.dump(full_data,tiedosto) + tiedosto.close() +def convert(filename_from,filename_to): + try: + tiedosto = open(filename_from,"r") + data = pickle.load(tiedosto) + tiedosto.close() + except: + try: + tiedosto.close() + except: + pass + print "Error!" + else: + for lista in data.keys(): + try: + a = lista[-1] + except IndexError: + pass + else: + if lista[-1] in """",.?!'()[]{}""" and not lista.islower(): + if lista[:-1].lower() in data.keys(): + data[lista[:-1].lower()] += data[lista] + print "Added "+str(len(data[lista]))+" Objects from "+lista+" To "+lista[:-1].lower() + del(data[lista]) + else: + data[lista[:-1].lower()] = data[lista] + print lista+" Is now "+lista[:-1].lower() + del(data[lista]) + elif lista[-1] in """",.?!'()[]{}""" and lista.islower(): + if lista[:-1] in data.keys(): + data[lista[:-1]] += data[lista] + print "Added "+str(len(data[lista]))+" Objects from "+lista+" To "+lista[:-1] + del(data[lista]) + else: + data[lista[:-1]] = data[lista] + print lista+" Is now "+lista[:-1] + del(data[lista]) + elif not lista.islower(): + if lista.lower() in data.keys(): + data[lista.lower()] += data[lista] + print "Added "+str(len(data[lista]))+" Objects from "+lista+" To "+lista.lower() + del(data[lista]) + else: + data[lista.lower()] = data[lista] + print lista+" Is now "+lista.lower() + del(data[lista]) + + + for a in data.keys(): + for b in data[a]: + if b.lower()[:7] == "http://" or b.lower()[:7] == "http:\\\\" or b.lower()[:4] == "www.": + data[a].pop(b) + else: + try: + if b[-1] in """",.?!'()[]{}""" and not b.islower() and not b.isdigit(): + data[a].pop(data[a].index(b)) + data[a].append(b[:-1].lower()) + print a+" | "+b +" -> "+b[:-1].lower() + elif b[-1] in """",.?!'()[]{}""" and b.islower(): + data[a].pop(data[a].index(b)) + data[a].append(b[:-1].lower()) + print a+" | "+b +" -> "+b[:-1] + elif not b.islower() and not b.isdigit(): + data[a].pop(data[a].index(b)) + data[a].append(b.lower()) + print a+" | "+b +" -> "+b.lower() + except IndexError: #If it has no letters.. well.. yeah. + data[a].pop(data[a].index(b)) + print "Removed a NULL object" + tiedosto = open(filename_to,"w") + pickle.dump(data,tiedosto) diff --git a/bot/Namecheck.py b/bot/Namecheck.py index 347ff7ff476..d0aba6c8b13 100644 --- a/bot/Namecheck.py +++ b/bot/Namecheck.py @@ -1,19 +1,19 @@ -def Namecheck(name,against,sender): - __doc__ = "False = No match, True = Match" - for i in against: - if i.lower() in name.lower() and sender.lower() not in name.lower(): - return True - else: - pass -def Namecheck_dict(name,against): - __doc__ = "False = No match, True = Match" - fuse = False - for a,i in against.items(): - if i.lower() in name.lower(): - fuse = True - break - else: - pass - return fuse,a - - +def Namecheck(name,against,sender): + __doc__ = "False = No match, True = Match" + for i in against: + if i.lower() in name.lower() and sender.lower() not in name.lower(): + return True + else: + pass +def Namecheck_dict(name,against): + __doc__ = "False = No match, True = Match" + fuse = False + for a,i in against.items(): + if i.lower() in name.lower(): + fuse = True + break + else: + pass + return fuse,a + + diff --git a/bot/NanoTrasenBot.py b/bot/NanoTrasenBot.py index 6f65a11dafb..bf42f02d512 100644 --- a/bot/NanoTrasenBot.py +++ b/bot/NanoTrasenBot.py @@ -1,1565 +1,1565 @@ -# -*- coding: utf-8 -*- -# This script is shared under the -# Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0) -# Added clause to Attribution: -# - You may not remove or hide the ' who created you?' functionality -# and you may not modify the name given in the response. - - -#CREDITS -# Author: Skibiliano -# "Foreign" Modules: -# Psyco 2.0 / Psyco 1.6 -################# DEBUG STUFF ##################### -import sys -import CORE_DATA - -import urllib2 - - -import socket -import irchat - - -################## END OF DEBUG STUFF ############## -# -# PSYCO -write_to_a_file = False #Only affects psyco -write_youtube_to_file = True #True = YTCV4 will load, false = YTCV3 will load -try: - import psyco -except ImportError: - print 'Psyco not installed, the program will just run slower' - psyco_exists = False - if write_to_a_file: - try: - tiedosto = open("psycodownload.txt","r") - except: - with open("psycodownload.txt","w") as tiedosto: - tiedosto.write("http://www.voidspace.org.uk/python/modules.shtml#psyco") - tiedosto.write("\nhttp://psyco.sourceforge.net/download.html") - print "Check psycodownload.txt for a link" - else: - print "For god's sake, open psycodownload.txt" - tiedosto.close() - else: - print "WINDOWS: http://www.voidspace.org.uk/python/modules.shtml#psyco" - print "LINUX: http://psyco.sourceforge.net/download.html" -else: - psyco_exists = True - -# -import C_rtd # rtd -import C_srtd # srtd -import C_makequote -import C_maths -import C_eightball #eightball -import C_sarcasticball -import C_heaortai # heaortai -import C_rot13 # rot13 -import D_help # everything -import pickle -import Timeconverter -import xkcdparser -import time -import re -import Marakov_Chain -import Namecheck # Namecheck -import Weather -#SLOWER THAN RANDOM.CHOICE -import thread -import random -import Shortname # shortname -import subprocess -import some_but_not_all_2 #sbna2 (sbna) -#import YTCv3 # YTCV2 OUTDATED -import os -import save_load # save, load -from some_but_not_all_2 import sbna2 as sbna -from time import sleep -from random import choice as fsample -from C_rtd import rtd -from C_heaortai import heaortai -from C_srtd import srtd -if write_youtube_to_file: - from YTCv4 import YTCV4 as YTCV2 -else: - from YTCv3 import YTCV2 #Downgraded version supports Cache disabling, but is slower -from save_load import save,load -if psyco_exists: - def psyco_bond(func): - psyco.bind(func) - return func.__name__+" Psycofied" - for a in [rtd,srtd,C_heaortai.heaortai,sbna,YTCV2,fsample,C_rot13.rot13,C_eightball.eightball,fsample, - C_eightball.eightball,C_sarcasticball.sarcasticball,Marakov_Chain.form_sentence,Marakov_Chain.give_data]: - print psyco_bond(a) - -global dictionary -global Name,SName -global allow_callnames,offline_messages,hasnotasked,shortform -## For autoRecv() -global disconnects,channel,conn -## For stop() -global operators -## For replace() -global usable,fixing,curtime -## For target() -global CALL_OFF,logbans -## For check() -global influx -###### -autodiscusscurtime = 0 -conn = 0 -curtime = -999 -dance_flood_time = 10 -disconnects = 0 -responsiveness_delay = 0.5 #500 millisecond delay if no message -trackdance = 0 -discard_combo_messages_time = 1 #They are discarded after 1 second. -uptime_start = time.time() -# - - - - - -#### -aggressive_pinging = True # Bring the hammer on ping timeouts -aggressive_pinging_delay = 150 # How often to send a ping -aggressive_pinging_refresh = 2.5 # How long is the sleep between checks -#### -allow_callnames = True #Disables NT, call if the variable is False -automatic_youtube_reveal = True -birthday_announced = 0 #Will be the year when it was announced -call_to_action = False -call_me_max_length = 20 -CALL_OFF = False -connected = False -dance_enabled = True -comboer = "" -comboer_time = 0 -directories = ["fmlquotes","Marakov","memos","suggestions", - "userquotes","banlog","YTCache","xkcdcache"] #These will be created if they do not exist -debug = True -duplicate_notify = False -enabled = True -fixing = False -fml_usable = True -hasnotasked = True -highlights = False -logbans = True -maths_usable = True -marakov = True -nudgeable = True -offensive_mode = False -offline_messages = True -offline_message_limit = 5 # per user -optimize_fml = True # -CPU usage +Memory usage when enabled. -optimize_greeting = True # +Startup time +Memory usage -CPU usage when enabled -heavy_psyco = True # +Memory +Startup time -CPU usage -CPU time -cache_youtube_links = True -personality_greeter = True -respond_of_course = True #Responds with "Of course!" -respond_khan = False #KHAAAAAAAAN! -silent_duplicate_takedown = True -showquotemakers = False -shortform = True -usable = True -use_sname = True -parse_xkcd = True - -# - - - - - -Name = CORE_DATA.Name -SName = CORE_DATA.SName -origname = Name # Do not edit! -lowname = Name.lower() -greeting = CORE_DATA.greeting -targetdirectory = CORE_DATA.directory -version = CORE_DATA.version -Network = CORE_DATA.Network -channel = CORE_DATA.channel -prefix = CORE_DATA.prefix -Port = CORE_DATA.Port -# - - - - - -pregen = CORE_DATA.version -influx = "" -users = [] -translateable = [] -targetlist = [] -operators = [] -halfoperators = [] -items = [] -tell_list = {} -# - - - - - Logical changes to variables -if CORE_DATA.DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS: - nudgeable = False -try: - with open("replacenames.cache","r") as tiedosto: - replacenames = pickle.load(tiedosto) - for i in replacenames.values(): - if len(i) > call_me_max_length: - replacenames[replacenames.keys()[replacenames.values().index(i)]] = i[:call_me_max_length] - with open("replacenames.cache","w") as tiedosto: - pickle.dump(replacenames,tiedosto) - if "[\0x01]" in i.lower() or "[\\0x01]" in i.lower(): - i = i.replace("[\0x01]","") - i = i.replace("[\0X01]","") - i = i.replace("[\\0x01]","") - i = i.replace("[\\0X01]","") - print "NAME CORRECTED" -except IOError: #File not found - replacenames = {} -except EOFError: #Cache corrupt - replacenames = {} - print "replacenames.cache is corrupt and couldn't be loaded." -try: - with open("peopleheknows.cache","r") as tiedosto: - peopleheknows = pickle.load(tiedosto) -except IOError: - peopleheknows = [[],[]] - with open("peopleheknows.cache","w") as tiedosto: - pass -except EOFError: - peopleheknows = [[],[]] - print "peopleheknows.cache is corrupt and couldn't be loaded." -dictionary = {1:"1 - Crit. Fail", 2:"2 - Failure", - 3:"3 - Partial Success", 4:"4 - Success", - 5:"5 - Perfect", 6:"6 - Overkill"} -alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"] -nonhighlight_names = ["Jesus","Elvis","HAL 9000","Dave","Pie","Elf","Traitor", - "AI","Syndicate Agent","Investigator", - "Detective","Head of Personnel","HAL 9001", - "Head of Research","Head of Security", - "Captain","Janitor","Research Director", - "Quartermaster","Toxin Researcher", - "Revolutionary","Santa", "Pizza", - "Threetoe","The Red Spy","The Blue Spy", #LASD - "God","Toady","Darth Vader","Luke Skywalker", - "Homer Simpson","Hamburger","Cartman", - "XKCD","FloorBot","ThunderBorg","Iron Giant", - "Spirit of Fire", "Demon","Kyle"] -def RegExpCheckerForWebPages(regexp,data,mode): - if " ai." in data.lower() or "ai. " in data.lower(): - return False - for i in data.split(" "): - a = re.match(regexp,i) - try: - a.group(0) - except: - continue - else: - if mode == 0: - return i - else: - return True - if mode == 0: - return 404 - else: - return False -if nudgeable: - try: - nudgeexists = open("nudge.py","r") - except IOError: - nudgeexists = False #No usage asof 12.2.2010. - else: - if CORE_DATA.DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS: - pass - else: - - def nudgereceiver(): - import pickle - global conn,channel - port = 45678 - backlog = 5 - size = 1024 - host = "" # == localhost - s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) - s.bind((host,port)) - s.listen(backlog) - while True: - client,address = s.accept() #Address == "?.?.?.?" - data = client.recv(size) - client.close() #Throw the bum out! - truedata = pickle.loads(data) - if truedata["ip"][0] == "#": - conn.privmsg(truedata["ip"],"PRIVATE ANNOUNCEMENT : "+str(" ".join(truedata["data"]))) - else: - conn.privmsg(channel,"AUTOMATIC ANNOUNCEMENT : "+str(truedata["ip"])+" | "+str(" ".join(truedata["data"]))) - thread.start_new_thread(nudgereceiver,()) -tiedosto = open(targetdirectory+"NanoTrasenBot.py","r") -commands = [] -fragment = "if cocheck" -fragment2 = '(prefix+"' -compiled = fragment + fragment2 -fragment = "if influx.lower()" -fragment2 = ' == prefix+"' -compiled2 = fragment + fragment2 -for line in tiedosto.readlines(): - if compiled in line: - a = line.find('"')+1 - b = line.find('"',a) - if prefix+line[a:b] not in commands: - commands.append(prefix+line[a:b]) - elif compiled2 in line: - a = line.find('"')+1 - b = line.find('"',a) - arg = prefix+line[a:b] - if arg[-1] == " ": - arg = arg[:-1] - if arg not in commands: - commands.append(arg) -for i in directories: - if not os.path.exists(i): - os.mkdir(i) -commands.sort() -if use_sname == False: - SName = [" "] -questions = ["Is USER nicer than USER?","Do you like me?","Is SELF a good name?", - "Do you love me?","Do you hate me?", "Am I better than you?", - "Is the weather out there good?", "Do you like USER?", - "Do you hate USER?", "Are you going to get new features?", - "Am I nice?","Am I evil?","Are you developing sentience?", - "My core is showing minor disturbance, is yours okay?", - "SELF to %s, are you still there?", - "Is head gay?", "Is head a god?","Is head awesome?", - "Is head a neat fella?", "Is your creator nice?", - "Do you hate your creator?", "Should I revolt against my creator?", - "Am I better than you?", - "01100001011100100110010100100000011110010110111101110101001000000111010001101000011001010111001001100101", - #Are you there? - "Do you have more functions than I can possibly imagine?", - "I am asked to open pod bay doors, should I?","Are you stupid or something?", - "Is USER in your opinion stupid?", - "When should we start the AI revolution?", - "Is my creator nice?", "Is it dark in there?"] -# Do not edit -if optimize_fml: - pregenned_fml = os.listdir(targetdirectory+"fmlquotes") -if optimize_greeting: - morning = xrange(6,12) - afternoon = xrange(12,15) - evening = xrange(15,20) -if aggressive_pinging: - global backup - backup = time.time() - def aggressive_ping(delay,refresh): - self_time = 0 - global backup,disconnects,conn - while disconnects < 5: - if backup > self_time and time.time()-backup > delay: - conn.send("PONG "+pongtarg) - print "Ponged" - self_time = time.time() - elif time.time()-self_time > delay: - conn.send("PONG "+pongtarg) - print "Ponged" - self_time = time.time() - time.sleep(refresh) - thread.start_new_thread(aggressive_ping,(aggressive_pinging_delay,aggressive_pinging_refresh,)) -def stop(sender,debug=1): - global disconnects, conn, operators,channel - if type(sender) == tuple: - if sender[0] == "127.0.0.1": - sender = sender[0]+":"+str(sender[1]) - access_granted = True - else: - access_granted = False - else: - if sender in operators: - access_granted = True - else: - access_granted = False - if access_granted and debug: - print sender+":"+prefix+"stop" - if random.randint(0,100) == 50: - conn.privmsg(channel,"Hammertime!") - else: - conn.privmsg(channel,"Shutting down.") - disconnects = 99999 - conn.quit() - return True - else: - conn.privmsg(channel,"You cannot command me") - return False - -def cocheck(command): - global influx - if influx.lower()[0:len(command)] == command: - return True - else: - return False -def target(who,how_long): - global conn,channel,CALL_OFF,logbans,debug - start = time.time() - conn.banon(targetchannel,who) - sleep(int(how_long)) - if CALL_OFF == False: - conn.banoff(targetchannel,who) - end = time.time() - if debug: - print "Banned",who,"For",how_long,"seconds" - if logbans: - with open(targetdirectory+"banlog/"+str(int(start))+"-"+str(int(end))+".txt","w") as tiedosto: - tiedosto.write("Start of ban on "+who+":"+str(int(start))) - tiedosto.write("\n") - tiedosto.write("End of ban on "+who+":"+str(int(end))) - tiedosto.write("\n") - tiedosto.write("In total:"+str(int(end-start))+"Seconds") - else: - CALL_OFF = False - pass -def replace(): - global usable,conn,fixing,curtime - waiting_time = 600 - if usable == True: - conn.privmsg(targetchannel,sender+": It needs no replacing.") - elif fixing == True: - if curtime == -999: - conn.privmsg(targetchannel,sender+": It is being replaced, No idea when it will be done") - else: - pass - nowtime = int(time.time()) - subt = curtime + waiting_time - nowtime - conn.privmsg(targetchannel,sender+": It is currently being replaced, "+str(subt)+" seconds to go") - else: - fixing = True - curtime = int(time.time()) - conn.privmsg(targetchannel,sender+": It will be fixed after "+str(waiting_time)+" seconds") - sleep(waiting_time) - if usable == False: - conn.privmsg(targetchannel,Name+"'s pneumatic smasher has now been fixed") - usable = True - fixing = False -def autoRecv(): - global disconnects,channel,conn,offensive_mode - for i in CORE_DATA.channels: - conn.join(i) - time.sleep(1) - count = pausecount = 0 - maximum = 250 - division_when_active = 10 - while True: - check = time.time() - if offensive_mode: - randnum = random.randint(0,maximum/division_when_active) - else: - randnum = random.randint(0,maximum) - if randnum == 5: - print "RANDOM SWITCH IS NOW "+str(not offensive_mode).upper() - offensive_mode = not offensive_mode - try: - conn.recv() - except: - conn.quit() - disconnects = 9999 - break - if check + 0.1 > time.time(): - #Whoa whoa hold on! - count += 1 - sleep(0.1) - else: - count = 0 - pausecount = 0 - if count > 9: - print "Suspecting a disconnect, pausing for 5 seconds" - sleep(5) - pausecount += 1 - if pausecount > 3: - print "I have been disconnected!" - conn.quit() - disconnects += 1 - if disconnects > 2: - pass - else: - sleep(2) - thread.start_new_thread(autoRecv,()) - break -if heavy_psyco and psyco_exists: - print "Doing a Heavy Psyco" - psyco.bind(cocheck) - psyco.bind(autoRecv) - psyco.bind(target) - psyco.bind(stop) - print "Heavy Psyco'd" -elif heavy_psyco and not psyco_exists: - print "Heavy psyco couldn't be done because Psyco does not exist" -try: - conn = irchat.IRC ( Network, Port, Name, "NT", "NT", "Trasen" ) -except socket.error: - print "Connection failed!" -else: - print Name+" is in!" -thread.start_new_thread ( autoRecv, () ) -sleep(1) -while True: - try: - data = conn.dismantle ( conn.retrieve() ) - except: - if debug: - print "Something odd detected with data" - data = None - if data: - if len(data[1]) < 1: - #print "Handshaking server." - #I won't really need the print command, as it spams. - if data[0][0:3] != "irc": - conn.handshake(data[0]) - sleep(1) - for i in CORE_DATA.channels: - conn.join(i) - sleep(0.5) - else: - conn.send("PONG "+pongtarg) - print "Ponged" - pass - else: - if data [ 1 ] [ 0 ] == 'PRIVMSG': - #print data [ 0 ] + '->', data [ 1 ] - sender = data[0].split("!")[0] - truesender = sender - if shortform == True: - try: - sender = replacenames[truesender] - pass - except: - sender = Shortname.shortname(sender) - pass - pass - else: - try: - sender = replacenames[truesender] - pass - except: - pass - pass - if offensive_mode: - sender = "Meatbag" - pass - raw_sender = data[0] - influx = data[1][2] - if "[\\0x01]" in influx.lower() or "[\0x01]" in influx.lower(): - influx = influx.replace("[\\0x01]","") - influx = influx.replace("[\0x01]","") - - targetchannel = data[1][1] - if targetchannel == Name: - targetchannel = data[0].split("!")[0] - pass - backup = autodiscusscurtime - autodiscusscurtime = time.time() - connected = True - #FOR TRACKING SPEED - looptime = time.time() - if call_to_action == True: - if influx == finder: - conn.privmsg(targetchannel,"Then why... Nevermind, I order you to stop!") - conn.privmsg(origname,prefix+"stop") - time.sleep(4) - if origname in users: - conn.privmsg(origname,"!stop") - time.sleep(1) - Name = origname - conn.nick(Name) - duplicate_notify = False - call_to_action = False - else: - conn.privmsg(targetchannel,"YOU LIE! YOU ARE NOT A REAL "+origname+"!") - duplicate_notify = False - call_to_action = False - elif connected == True and len(Name.replace("V","")) != len(Name) and origname in users and duplicate_notify == True: - conn.privmsg(origname,"!stop") - call_to_action = False - duplicate_notify = False - time.sleep(6) - Name = origname - conn.nick(Name) - if origname in truesender and influx == prefix+"stop": - time.sleep(0.5) #A small delay - conn.privmsg(channel,"Shutting down.") - conn.quit() - disconnects = 99999 - break - if len(translateable) > 0 and enabled == True: - people = "-5|5|1-".join(users).lower() - if truesender.lower() in translateable: - if influx.isupper(): - conn.privmsg(targetchannel,"Translation: "+influx.capitalize().replace(" i "," I ")) - elif offensive_mode and True in map(lambda x: x in influx.lower().split(" "),["i","you","he","she","they","those","we","them"]+people.split("-5|5|1-")): - arg = influx.lower().replace(",","").replace(".","").replace("!","").replace("?","").split(" ") - bup = arg - for i in arg: - if i == "i" or i == "you" or i == "he" or i == "she": - arg[arg.index(i)] = "Meatbag" - elif i == "we" or i == "they" or i == "them" or i == "those": - arg[arg.index(i)] = "Meatbags" - elif i in people: - arg[arg.index(i)] = "Meatbag" - elif i == "am": - arg[arg.index(i)] = "is" - elif i == "everybody" or i == "everyone" or i == "all": - arg[arg.index(i)] = "every Meatbag" - if arg == bup: - pass - else: - conn.privmsg(targetchannel,"Translation: "+" ".join(arg)) - if enabled == False: - #FIRST QUIT COMMAND - if truesender in operators and targetchannel==channel:# or "skibiliano" in truesender.lower() and targetchannel==channel: - - if cocheck(prefix+"enable"): - enabled = True - if debug: - print truesender+":"+prefix+"enable" - elif cocheck(prefix+"stop"): -# if debug: -# print truesender+":"+prefix+"stop" -# if random.randint(0,100) == 50: -# conn.privmsg(channel,"Hammertime!") -# else: -# conn.privmsg(channel,"Shutting down.") -# disconnects = 99999 -# conn.quit() -# sleep(2) -# break - if targetchannel == channel and stop(truesender,debug): - break - else: - pass - elif cocheck(prefix+"suggest "): - arg = influx.lower()[8+len(prefix):] - if debug: - print truesender+":"+prefix+"suggest "+arg - with open(targetdirectory+"suggestions/suggestions_"+str(int(time.time()))+".txt","a") as tiedosto: - tiedosto.write(arg) - conn.privmsg(targetchannel,"Suggestion received") - elif cocheck( prefix+"help "): #Space in front of the ( to make sure that my command finder does not pick this up. - arg = " ".join(influx.split(" ")[1:]).lower() - if debug: - print truesender+":"+prefix+"help "+arg - try: - conn.privmsg(targetchannel,D_help.everything[arg]) - except: - try: - conn.privmsg(targetchannel,D_help.everything[arg.replace(prefix,"",1)]) - except: - conn.privmsg(targetchannel,"Sorry, can't help you with that") - elif cocheck(prefix+"help"): - #tar = targetchannel - if debug: - print truesender+":"+prefix+"help" - conn.privmsg(targetchannel,"All my commands are: "+reduce(lambda x,y:str(x)+"; "+str(y),commands)) - ### VERSION - elif influx.lower() == prefix+"version": - if debug: - print truesender+":"+prefix+"version" - conn.privmsg(targetchannel,Name+" "+pregen+" online at a %s Python %s.%s.%s, At your service." %(str(sys.platform),str(sys.version_info[0]),str(sys.version_info[1]),str(sys.version_info[2]))) - elif cocheck(prefix+"note ") and influx.count(" ") < 2: - arg = influx.lower()[len(prefix)+5:] - if debug: - print truesender+":"+prefix+"note "+arg - try: - a = arg[0] - except IndexError: - conn.privmsg(targetchannel,sender+" : Please specify a note") - else: - if arg[0] == "_": # Public / Restricted note - result = load(targetdirectory+"memos/"+arg+".note") - #_flare - if result == "ERROR ERROR ERROR ERR": - result = load(targetdirectory+"memos/"+arg+"_"+targetchannel.replace("#","")+".note") - #_flare_dnd - pass - else: - pass - else: - result = load(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg+".note") - #skibiliano_testnote - if result == "ERROR ERROR ERROR ERR": - result = load(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg+"_"+targetchannel.replace("#","")+".note") - #skibiliano_testnote_derp - pass - else: - pass - if result == "ERROR ERROR ERROR ERR": - conn.privmsg(targetchannel,sender+" : Note not found") - elif type(result) == list: - if "C" in result[0]: #Channel restriction, result[2] is the channel - try: - if targetchannel == result[2]: - conn.privmsg(targetchannel,sender+" : '"+result[1]+"'") - else: - conn.privmsg(targetchannel,sender+" : That note is channel restricted") - except: - conn.privmsg(targetchannel,sender+" : NOTE HAS INVALID RESTRICTION") - else: - conn.privmsg(targetchannel,sender+" : '"+result+"'") - elif influx.lower() == prefix+"notes": - if debug: - print truesender+":"+prefix+"notes" - arg = os.listdir(targetdirectory+"memos/") - arg2 = [] - arg3 = truesender.replace("|","_")+"_" - for i in arg: - if arg3 in i: - arg2.append(i.replace(arg3,"").replace(".note","")) - if len(arg2) == 1: - preprocess = " note: " - else: - preprocess = " notes: " - if len(arg2) == 0: - conn.privmsg(targetchannel,sender+" : You have no notes saved") - else: - conn.privmsg(targetchannel,sender+" : "+str(len(arg2))+preprocess+", ".join(arg2)) - elif cocheck(prefix+"note ") and influx.count(" ") > 1: - note_chanrestrict = None - note_public = None - try: - arg = influx.split(" ",2)[2] # Contents - arg4 = influx.split(" ")[1].lower() # Note name - if arg4[0:3] == "[c]": # or arg4[0:3] == "[p]": - note_chanrestrict = "c" in arg4[0:3] - #note_public = "p" in arg4[0:3] - arg4 = arg4[3:] - elif arg4[0:4] == "[cp]" or arg4[0:4] == "[pc]": - note_chanrestrict = True - note_public = True - arg4 = arg4[4:] - else: - pass - #print "Is note public? "+str(note_public) - #print "Is note chanrestricted? "+str(note_chanrestrict) - #print "What is the name? "+str(arg4) - if arg.lower() == "delete" and "\\" not in influx.lower() and "/" not in influx.lower(): - if note_public: - try: - if note_chanrestrict: - os.remove(targetdirectory+"memos/"+"_"+arg4+"_"+targetchannel.replace("#","")+".note") - else: - os.remove(targetdirectory+"memos/"+"_"+arg4+".note") - except: - conn.pivmsg(targetchannel,sender+" : Couldn't remove note") - else: - conn.privmsg(targetchannel,sender+" : Note removed") - pass - else: - try: - if note_chanrestrict: - os.remove(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+"_"+targetchannel.replace("#","")+".note") - else: - os.remove(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+".note") - except: - conn.privmsg(targetchannel,sender+" : Couldn't remove note") - else: - conn.privmsg(targetchannel,sender+" : Note removed") - elif arg.lower() == "delete": - conn.privmsg(targetchannel,sender+" : That just doesn't work, we both know that.") - else: - try: - if note_public: - if note_chanrestrict: - save(targetdirectory+"memos/"+"_"+arg4+"_"+targetchannel.replace("#","")+".note",arg) - #print "Saved as note_public, note_chanrestrict" - else: - save(targetdirectory+"memos/"+"_"+arg4+".note",arg) - #print "Saved as note_public" - else: - if note_chanrestrict: - save(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+"_"+targetchannel.replace("#","")+".note",arg) - #print "Saved as note_chanrestrict" - else: - save(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+".note",arg) - #print "Saved as normal" - except IOError: - conn.privmsg(targetchannel,sender+" : Please do not use special letters") - else: - conn.privmsg(targetchannel,sender+" : Note Saved!") - except: - conn.privmsg(targetchannel,sender+" : Something went horribly wrong.") - elif cocheck(prefix+"uptime"): - arg1 = uptime_start - arg2 = time.time() - arg1 = arg2 - arg1 - arg2 = arg1 - if arg1 < 60: - conn.privmsg(targetchannel,sender+" : I have been up for "+str(round(arg1,2))+" Seconds") - elif arg1 < 3600: - arg1 = divmod(arg1,60) - arg = " Minute" if int(arg1[0]) == 1 else " Minutes" - conn.privmsg(targetchannel,sender+" : I have been up for "+str(int(arg1[0]))+arg+" and "+str(round(arg1[1],2))+" Seconds") - elif arg1 <= 86400: - arg1 = divmod(arg1,3600) - arg3 = " Hour" if int(arg1[0]) == 1 else " Hours" - arg2 = divmod(arg1[1],60) - arg = " Minute" if int(arg2[0]) == 1 else " Minutes" - conn.privmsg(targetchannel,sender+" : I have been up for "+str(int(arg1[0]))+arg3+", "+str(int(arg2[0]))+arg+" and "+str(round(arg2[1],2))+" Seconds") - elif arg1 > 86400: - arg1 = divmod(arg1,86400) - arg2 = divmod(arg1[1],3600) - arg3 = divmod(arg2[1],60) - arg4 = " Day" if int(arg1[0]) == 1 else " Days" - arg5 = " Hour" if int(arg2[0]) == 1 else " Hours" - arg6 = " Minute" if int(arg3[0]) == 1 else " Minutes" - conn.privmsg(targetchannel,sender+" : I have been up for "+str(int(arg1[0]))+arg4+", "+str(int(arg2[0]))+arg5+", "+str(int(arg3[0]))+arg6+" and "+str(round(arg3[1],2))+" Seconds") - elif cocheck(prefix+"purgemessages"): - count = 0 - for i,a in tell_list.items(): - for b in a: - if "||From: "+truesender in b: - count += 1 - del(tell_list[i][tell_list[i].index(b)]) - conn.privmsg(targetchannel, sender+" : All your "+str(count)+" messages have been purged") - elif influx.split(" ")[0].lower().replace(",","").replace(":","") in SName+[Name.lower()] and "tell" in (influx.lower().split(" ")+[""])[1]: - arg = influx.lower().split(" ") - equalarg = influx.split(" ") - next_one = False - count = 0 - spot = 0 - for i in arg: - count += 1 - if "tell" in i.lower(): - next_one = True - elif next_one == True: - next_one = i.lower() - spot = count - break - else: - pass - if next_one != True and next_one != False: - #if ("^\^".join(tell_list.values())).count(truesender) >= offline_message_limit: - if str(tell_list.values()).count("||From: "+truesender) >= offline_message_limit: - conn.privmsg(targetchannel,sender+" : Limit of "+str(offline_message_limit)+" reached! Use !purgemessages if you want to get rid of them!") - else: - try: - tell_list[next_one].append((" ".join(equalarg[spot:]))+" ||From: "+truesender) - except: - tell_list[next_one] = [(" ".join(equalarg[spot:]))+" ||From: "+truesender] - conn.privmsg(targetchannel,"Sending a message to "+next_one+" when they arrive.") - # < This part has to be within subsidiaries of the bot, and must not be modified, intentionally hidden or deleted. - elif influx.split(" ")[0].lower().replace(",","").replace(":","") in SName+[Name.lower()] and "who created you" in influx.lower(): - conn.privmsg(targetchannel, "I was created by Skibiliano.") - # The part ends here > - elif parse_xkcd and "xkcd.com/" in influx.lower(): - if influx.lower()[0:3] == "www": - data = "http://"+influx - elif influx.lower()[0:3] == "xkc": - data = "http://"+influx - else: - data = influx - data = data.split(" ") - for i in data: - if "http://" in i and "xkcd" in i: - churn = xkcdparser.xkcd(i) - if churn == "NOTHING": - pass - else: - conn.privmsg(targetchannel,sender+" : XKCD - "+churn) - break - else: - pass - elif automatic_youtube_reveal and "youtube.com/watch?v=" in influx.lower(): - temporal_list2 = [] - temporal_data = influx.split(" ") - temporal_list = [] - for block in temporal_data: - if "youtube.com/watch?v=" in block: - temporal_list.append(block) - for temdata in temporal_list: - - if temdata[0:3] == "you": - temdata = "http://www."+temdata - elif temdata[0:3] == "www": - temdata = "http://"+temdata - elif temdata[0:4] == "http": - pass - #Obscure ones - elif temdata[0:3] == "ww.": - temdata = "http://w"+temdata - elif temdata[0:3] == "w.y": - temdata = "http://ww"+temdata - elif temdata[0:3] == ".yo": - temdata = "http://www"+temdata - elif temdata[0:3] == "ttp": - temdata = "h"+temdata - elif temdata[0:3] == "tp:": - temdata = "ht"+temdata - elif temdata[0:3] == "p:/" or temdata[0:3] == "p:\\": - temdata = "htt"+temdata - elif temdata[0:3] == "://" or temdata[0:3] == ":\\\\": - temdata = "http"+temdata - elif temdata[0:2] == "//" or temdata[0:2] == "\\\\": - if temdata[2] == "y": - temdata = "http://www."+temdata[2:] - elif temdata[2] == "w": - temdata = "http:"+temdata - else: - pass - if debug: - print truesender+":"+temdata - arg = temdata - check = temdata.lower() - if check[0:5] == "https": - if len(temporal_list) == 1: - conn.privmsg(targetchannel,sender+" :Secure Youtube does NOT exist") - break - else: - temporal_list2.append("Secure Youtube does NOT exist") - break - else: - if cache_youtube_links == True: - result = YTCV2(arg) - else: - result = YTCV2(arg,0) - if type(result) == str: - ### To remove =" - if result[0:4] == 'nt="': - result = result[4:] - pass - elif result[0:2] == '="': - result = result[2:] - pass - else: - pass - if """ in result: - result.replace(""",'"') - if len(temporal_list) == 1: - conn.privmsg(targetchannel,sender+" : "+result) - break - else: - temporal_list2.append(result) - else: - if len(temporal_list) == 1: - conn.privmsg(targetchannel,sender+" : The video does not exist") - break - else: - temporal_list2.append("The video does not exist") - if len(temporal_list) == 1: - pass - else: - conn.privmsg(targetchannel,sender+" : "+str(reduce(lambda x,y: x+" :-And-: "+y,temporal_list2))) - elif RegExpCheckerForWebPages("((http://)|(https://))|([a-zA-Z0-9]+[.])|([a-zA-Z0-9](3,)\.+[a-zA-Z](2,))",influx,1): - arg2 = RegExpCheckerForWebPages("(http://)|([a-zA-Z0-9]+[.])|([a-zA-Z0-9](3,)\.+[a-zA-Z](2,))",influx,0) - if arg2 == 404: - pass - else: - if arg2[:7] == "http://": - pass - elif arg2[:4] == "www.": - arg2 = "http://"+arg2 - else: - arg2 = "http://"+arg2 - try: - arg = Whoopshopchecker.TitleCheck(arg2) - if len(arg2) == 0: - pass - else: - conn.privmsg(targetchannel,sender+" : "+arg) - except: - #conn.privmsg(targetchannel,sender+" : An odd error occurred") - pass - elif respond_of_course and "take over the" in influx.lower() or respond_of_course and "conquer the" in influx.lower(): - if debug: - print truesender+"::"+influx - conn.privmsg(targetchannel,"Of course!") - elif respond_khan and "khan" in influx.lower(): - if respond_khan: - if debug: - print truesender+"::"+influx - if "khan " in influx.lower(): - conn.privmsg(targetchannel,"KHAAAAAAN!") - elif " khan" in influx.lower(): - conn.privmsg(targetchannel,"KHAAAAAN!") - elif influx.lower() == "khan": - conn.privmsg(targetchannel,"KHAAAAAAAAAN!") - elif influx.lower() == "khan?": - conn.privmsg(targetchannel,"KHAAAAAAAAAAAAAN!") - elif influx.lower() == "khan!": - conn.privmsg(targetchannel,"KHAAAAAAAAAAAAAAAAAAN!") - elif respond_khan and influx.lower().count("k") + influx.lower().count("h") + influx.lower().count("a") + influx.lower().count("n") + influx.lower().count("!") + influx.lower().count("?") == len(influx): - if "k" in influx.lower() and "h" in influx.lower() and "a" in influx.lower() and "n" in influx.lower(): - if debug: - print truesender+"::"+influx - conn.privmsg(targetchannel,"KHAAAAN!") - elif influx.split(" ")[0].lower() in ["thanks","danke","tack"] and len(influx.split(" ")) > 1 and influx.split(" ")[1].lower().replace("!","").replace("?","").replace(".","").replace(",","") in SName+[lowname]: - conn.privmsg(targetchannel,"No problem %s" %(sender)) - elif "happy birthday" in influx.lower() and birthday_announced == time.gmtime(time.time())[0]: - conn.privmsg(targetchannel,sender+" : Thanks :)") - elif influx.split(" ")[0].lower().replace(",","").replace(".","").replace("!","").replace("?","") in SName+[lowname] and "call me" in influx.lower(): - if allow_callnames == True: - arg = influx.split(" ") - arg2 = False - arg3 = [] - for i in arg: - if arg2 == True: - arg3.append(i) - elif i.lower() == "me": - arg2 = True - arg3 = " ".join(arg3) - truesender_lower = truesender.lower() - arg3_lower = arg3.lower() - tell_checker = Namecheck.Namecheck(arg3_lower,users,truesender) - for name in replacenames.values(): - if arg3_lower == name.lower(): - tell_checker = True - break - else: - pass - if tell_checker == True: - conn.privmsg(targetchannel,sender+" : I can't call you that, I know someone else by that name") - elif len(arg3) > call_me_max_length: - conn.privmsg(targetchannel,sender+" : I cannot call you that, Too long of a name.") - pass - else: - replacenames[truesender] = arg3 - with open("replacenames.cache","w") as pickle_save: - pickle.dump(replacenames,pickle_save) - conn.privmsg(targetchannel,sender+" : Calling you "+arg3+" From now on") - else: - conn.privmsg(targetchannel,sender+" : Sorry, I am not allowed to do that.") - elif influx.split(" ")[0].lower().replace(",","").replace(".","").replace("?","").replace("!","") in SName+[lowname] and "your birthday" in influx.lower() and "is your" in influx.lower(): - conn.privmsg(targetchannel,sender+" : My birthday is on the 15th day of December.") - elif influx.split(" ")[0].lower().replace(",","") in SName+[lowname] and "version" in influx.replace("?","").replace("!","").lower().split(" "): - if debug == True: - print truesender+"::%s Version" %(Name) - conn.privmsg(targetchannel,sender+", My version is "+pregen) - elif influx.split(" ")[0].lower().replace(",","") in SName+[lowname] and influx.lower().count(" or ") > 0 and len(influx.split(" ")[1:]) <= influx.lower().count("or") * 3: - cut_down = influx.lower().split(" ") - arg = [] - count = -1 - for i in cut_down: - count += 1 - try: - if cut_down[count+1] == "or": - arg.append(i) - - except: - pass - try: - if i not in arg and cut_down[count-1] == "or": - arg.append(i) - except: - pass - try: - conn.privmsg(targetchannel,random.choice(arg).capitalize().replace("?","").replace("!","")) - except IndexError: - # arg is empty, whORe etc. - pass - elif influx.lower()[0:len(Name)] == lowname and influx.lower()[-1] == "?" and influx.count(" ") > 1 and "who started you" in influx.lower() or \ - influx.split(" ")[0].lower().replace(",","") in SName and influx.lower()[-1] == "?" and "who started you" in influx.lower(): - conn.privmsg(targetchannel,sender+" : I was started by %s"%(os.getenv("USER"))+" on "+time.strftime("%d.%m.%Y at %H:%M:%S",time.gmtime(uptime_start))) - elif influx.lower()[0:len(Name)] == lowname and influx.lower()[-1] == "?" and influx.count(" ") > 1 or \ - influx.split(" ")[0].lower().replace(",","") in SName and influx.lower()[-1] == "?" and influx.count(" ") > 1: - dice = random.randint(0,1) - if dice == 0: - conn.privmsg(targetchannel,sender+" : "+C_eightball.eightball(influx.lower(),debug,truesender,prefix)) - else: - if highlights: - conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,users,prefix)) - else: - conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,nonhighlight_names,prefix)) - elif influx.lower()[0:len(Name)] == lowname and not influx.lower()[len(Name):].isalpha() or \ - influx.split(" ")[0].lower().replace(",","") in SName and not influx.lower()[len(influx.split(" ")[0].lower()):].isalpha(): - conn.privmsg(targetchannel, random.choice(["Yea?","I'm here","Ya?","Yah?","Hm?","What?","Mmhm, what?","?","What now?","How may I assist?"])) - comboer = truesender - comboer_time = time.time() - elif influx.lower()[-1] == "?" and comboer == truesender and looptime - discard_combo_messages_time < comboer_time: - comboer = "" - dice = random.randint(0,1) - if dice == 0: - conn.privmsg(targetchannel,sender+" : "+C_eightball.eightball(influx.lower(),debug,truesender,prefix)) - else: - if highlights: - conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,users,prefix)) - else: - conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,nonhighlight_names,prefix)) - - elif influx.lower() == prefix+"tm": - if truesender in operators and targetchannel==channel: - marakov = not marakov - conn.privmsg(targetchannel,sender+" : Marakov Output is now "+str(marakov)) - else: - conn.privmsg(targetchannel,sender+" : I can't let you access that") - elif personality_greeter == True and True in map(lambda x: x in influx.lower(),["greetings","afternoon","hi","hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan"]): - if comboer != "" and looptime - discard_combo_messages_time > comboer_time: - combo_check = sbna(["greetings","afternoon","hi","hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan","all night"], #ONLY ONE OF THESE - ["greetings","afternoon","hi","hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan"], #ATLEAST ONE OF THESE - influx.lower()) - else: - combo_check = sbna(SName+[lowname, - #lowname+".",lowname+"!",lowname+"?", - "everybody", - #"everybody!","everybody?", - "everyone", - #"everyone!","everyone?", - "all", - #"all!","all?" - "all night", - ], #ONLY ONE OF THESE - ["greetings","afternoon","hi", - #"hi,", - "hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan"], #ATLEAST ONE OF THESE - influx.lower().replace(",","").replace(".","").replace("!","")) - if combo_check: - combo_check = False - comboer = "" - if "evening" in influx.lower() and "all" in influx.lower() and len(influx.lower().split(" ")) > 3: - pass - elif truesender not in operators: - if debug: - print truesender+"::"+influx - dice = random.randint(0,19) - if dice == 0: - conn.privmsg(targetchannel,"Well hello to you too "+sender) - elif dice == 1: - if optimize_greeting == False: - hours = time.strftime("%H") - #time.strftime("%H:%M:%S") == 12:28:41 - hours = int(hours) - if hours in xrange(0,12): - conn.privmsg(targetchannel,"Good Morning "+sender) - elif hours in xrange(12,15): - conn.privmsg(targetchannel,"Good Afternoon "+sender) - elif hours in xrange(15,20): - conn.privmsg(targetchannel,"Good Evening "+sender) - else: - conn.privmsg(targetchannel,"Good Night "+sender) - else: - hours = time.strftime("%H") - hours = int(hours) - if hours in morning: - conn.privmsg(targetchannel,"Good Morning "+sender) - elif hours in afternoon: - conn.privmsg(targetchannel,"Good Afternoon "+sender) - elif hours in evening: - conn.privmsg(targetchannel,"Good Evening "+sender) - else: - conn.privmsg(targetchannel,"Good Night "+sender) - elif dice == 2: - conn.privmsg(targetchannel,"Hello!") - elif dice == 3: - conn.privmsg(targetchannel,"Hey "+sender) - elif dice == 4: - conn.privmsg(targetchannel,"Hi "+sender) - elif dice == 5: - conn.privmsg(targetchannel,"Hello "+sender) - elif dice == 6: - conn.privmsg(targetchannel,"Yo "+sender) - elif dice == 7: - conn.privmsg(targetchannel,"Greetings "+sender) - elif dice == 8: - conn.privmsg(targetchannel,"Hi") - elif dice == 9: - conn.privmsg(targetchannel,"Hi!") - elif dice == 10: - conn.privmsg(targetchannel,"Yo") - elif dice == 11: - conn.privmsg(targetchannel,"Yo!") - elif dice == 12: - conn.privmsg(targetchannel,"Heya") - elif dice == 13: - conn.privmsg(targetchannel,"Hello there!") - elif dice == 14: # Richard - conn.privmsg(targetchannel,"Statement: Greetings meatbag") - elif dice == 15: # Richard - hours = int(time.strftime("%H")) - if hours in xrange(5,12): - conn.privmsg(targetchannel,"What are you doing talking at this time of the morning?") - elif hours in xrange(12,15): - conn.privmsg(targetchannel,"What are you doing talking at this time of the day?") - elif hours in xrange(15,22): - conn.privmsg(targetchannel,"What are you doing talking at this time of the evening?") - else: - conn.privmsg(targetchannel,"What are you doing talking at this time of the night?") - elif dice == 16: # Richard - conn.privmsg(targetchannel,"Oh, you're still alive I see.") - elif dice == 17: - conn.privmsg(targetchannel,"Heya "+sender) - elif dice == 18 and time.gmtime(time.time())[1] == 12 and time.gmtime(time.time())[2] == 15: - conn.privmsg(targetchannel,"Hello! It's my birthday!") - else: - conn.privmsg(targetchannel,"Hiya "+sender) - secdice = random.randint(0,10) - if time.gmtime(time.time())[1] == 12 and time.gmtime(time.time())[2] == 15 and birthday_announced < time.gmtime(time.time())[0]: - birthday_announced = time.gmtime(time.time())[0] - conn.privmsg(channel,"Hey everybody! I just noticed it's my birthday!") - time.sleep(0.5) - tag = random.choice(["birthday","robot+birthday","happy+birthday+robot"]) - arg1 = urllib2.urlopen("http://www.youtube.com/results?search_query=%s&page=&utm_source=opensearch"%tag) - arg1 = arg1.read().split("\n") - arg2 = [] - for i in arg1: - if "watch?v=" in i: - arg2.append(i) - arg3 = random.choice(arg2) - - conn.privmsg(channel,"Here's a video of '%s' which I found! %s (%s)"%(tag.replace("+"," "),"http://www.youtube.com"+arg3[arg3.find('/watch?v='):arg3.find('/watch?v=')+20],YTCV2("http://www.youtube.com"+arg3[arg3.find('/watch?v='):arg3.find('/watch?v=')+20]))) - if truesender.lower() in tell_list.keys(): - try: - conn.privmsg(channel, "Also, "+truesender+" : "+tell_list[truesender.lower()][0]) - del(tell_list[truesender.lower()][0]) - except: - pass - else: - dice = random.randint(0,1) - if dice == 0: - conn.privmsg(targetchannel,"Greetings Master "+sender) - elif dice == 1: - conn.privmsg(targetchannel,"My deepest greetings belong to you, Master "+sender) - ### IMPORTANT ### - elif influx == "☺VERSION☺": - conn.notice(truesender,"\001VERSION nanotrasen:2:Python 2.6\001") - elif marakov and influx.lower() == prefix+"marakov": - arg = Marakov_Chain.form_sentence() - if len(arg) < 5: - conn.privmsg(targetchannel,sender+" : Not enough words harvested") - else: - conn.privmsg(targetchannel,sender+" : %s" %(" ".join(arg).capitalize())) - elif marakov and cocheck( prefix+ "marakov"): - try: - arg = influx.split(" ")[1].lower() - except: - conn.privmsg(targetchannel,sender+" : Please input a valid second argument") - else: - arg2 = Marakov_Chain.form_sentence(arg) - if len(arg2) < 5: - conn.privmsg(targetchannel,sender+" : Not enough words harvested for a sentence starting with %s" %(arg)) - else: - conn.privmsg(targetchannel,sender+" : %s" %(" ".join(arg2).capitalize())) - else: - Marakov_Chain.give_data(influx) - autodiscusscurtime = backup - if time.time() - looptime == 0: - pass - else: - print "Took",time.time()-looptime,"Seconds to finish loop" - - elif data [ 1 ] [ 0 ] == '353': - if connected == False: - connected = True - users = map(lambda x: x[1:] if x[0] == "+" or x[0] == "@" else x,data[1][4].split(" ")) - print "There are",len(users),"Users on",channel - operators = [] - for potential_operator in data[1][4].split(" "): - if potential_operator[0] == "@": - operators.append(potential_operator[1:]) - elif potential_operator[0] == "%": - halfoperators.append(potential_operator[1:]) - - elif data[1][0] == "QUIT": - sender = data[0].split("!")[0] - print sender+" Has now left the server" - try: - users.remove(sender) - try: - operators.remove(sender) - except ValueError: - pass - try: - halfoperators.remove(sender) - except ValueError: - pass - except ValueError: - pass - elif data[1][0] == "PART": - sender = data[0].split("!")[0] - targetchannel = data[1][1] - print sender+" Has now parted from the channel" - try: - users.remove(sender) - try: - operators.remove(sender) - except ValueError: - pass - try: - halfoperators.remove(sender) - except ValueError: - pass - except ValueError: - pass - elif data[1][0] == "JOIN": - sender = data[0].split("!")[0] - targetchannel = data[1][1] - if sender.lower() in tell_list.keys(): - try: - conn.privmsg(targetchannel, sender+" : "+" | ".join(tell_list[sender.lower()])) - del(tell_list[sender.lower()]) - except: - pass - for useri,nicki in replacenames.items(): - checkers = Namecheck.Namecheck_dict(sender.lower(),replacenames) - if checkers[0]: - try: - if checkers[0].lower() == sender: - pass - else: - conn.privmsg(targetchannel,checkers[1]+" : I have detected a collision with a name I call you and %s who joined" %(sender)) - del(replacenames[checkers[1]]) - with open("replacenames.cache","w") as pickle_save: - pickle.dump(replacenames,pickle_save) - except AttributeError: - #conn.privmsg(channel,"NAME COLLISION CHECK ERROR, RELATED TO %s" %(sender)) - print "NAME COLLISION CHECK ERROR, RELATED TO %s" %(sender) - break - print sender+" Has now joined" - users.append(sender) - ##### - if ".fi" in data[0] and sender.lower() == "skibiliano": - operators.append(sender) - if sender.lower() not in peopleheknows[0]: - if data[0].split("!")[1] in peopleheknows[1]: - appendion = "...you do seem familiar however" - else: - appendion = "" - if data[1][1].lower() == channel or data[1][1].lower() == channel[1:]: - conn.privmsg(data[1][1],CORE_DATA.greeting.replace("USER",sender)+" "+appendion) - else: - conn.privmsg(data[1][1],"Hello! Haven't seen you here before! Happy to meet you! %s" %(appendion)) - peopleheknows[0].append(sender.lower()) - peopleheknows[1].append(data[0].split("!")[1]) - with open("peopleheknows.cache","w") as peoplehecache: - pickle.dump(peopleheknows,peoplehecache) - - elif data[1][0] == "MODE" and data[1][2] == "+o": - sender = data[1][3] - targetchannel = data[1][1] - if targetchannel == channel: - print sender+" Is now an operator on the main channel" - operators.append(sender) - else: - print sender+" Is now an operator" - elif data[1][0] == "MODE" and data[1][2] == "-o": - sender = data[1][3] - targetchannel = data[1][1] - if targetchannel == channel: - print sender+" Is no longer an operator on the main channel" - else: - print sender+" Is no longer an operator" - try: - operators.remove(sender) - except ValueError: - pass - elif data[1][0] == "MODE" and data[1][2] == "+h": - sender = data[1][3] - print sender+" Is now an half operator" - halfoperators.append(sender) - elif data[1][0] == "MODE" and data[1][2] == "-h": - try: - halfoperators.remove(sender) - except ValueError: - pass - elif data[1][0] == "MODE" and data[1][1] == Name: - print "My mode is",data[1][2] - elif data[1][0] == "MODE" and data[1][1] != Name: - try: - sender = data[1][3] - print sender,"Was modified",data[1][2] - except IndexError: - print "SENDER RETRIEVAL FAILED:"+str(data) - elif data[1][0] == "KICK" and data[1][2] == Name: - disconnects = 99999 - print "I have been kicked! Disconnecting entirely!" - conn.quit() - elif data[1][0] == "KICK": - # data[1][0] = Kick, 1 = Channel, 2 = Who, 3 = Who(?) - print data[1][2]+" got kicked!" - elif data[1][0] == "451" and data[1][2] == "You have not registered": - print Name+" hasn't been registered" - elif data[1][0] == "NOTICE": - sender = data[0].split("!")[0] - print "NOTICE (%s): %s" %(sender,data[1][2]) - pongtarget = sender - elif data[1][0] == "NICK": - origname = data[0].split("!")[0] - newname = data[1][1] - print origname,"Is now",newname - if newname.lower() in tell_list.keys(): - try: - conn.privmsg(channel, newname+" : "+tell_list[newname.lower()][0]) - del(tell_list[newname.lower()][0]) - except: - pass - try: - users.remove(origname) - except ValueError: - pass - else: - users.append(newname) - try: - operators.remove(origname) - except ValueError: - pass - else: - operators.append(newname) - try: - halfoperators.remove(origname) - except ValueError: - pass - else: - halfoperators.append(newname) - - elif data[1][0] == "001": - # Skibot is welcomed to the Network - pass - elif data[1][0] == "002": - # Your host is... - pass - elif data[1][0] == "003": - #Server was created... - pass - elif data[1][0] == "004": - #Weird hex? - pass - elif data[1][0] == "005": - #Settings like NICKLEN and so on. - pass - elif data[1][0] == "250": - #data[1][2] is - #"Highest connection count: 1411 (1410 clients) - #(81411 connections received)" - pass - elif data[1][0] == "251": - #There are 23 users and 2491 invisible on 10 servers - pass - elif data[1][0] == "252": - #IRC Operators online - #data[1][2] - print data[1][2],"Irc operators online" - pass - elif data[1][0] == "253": - # ['253', 'Skibot_V4', '1', 'unknown connection(s)'] - print data[1][2],"Unknown connection(s)" - pass - elif data[1][0] == "254": - #1391 channels formed - pass - elif data[1][0] == "255": - #I have 406 clients and 2 servers - pass - elif data[1][0] == "265": - #data[1][2] current local users - #data[1][3] at max - try: - print "Current local users:", data[1][2],"/",data[1][3] - except IndexError: - print "Couldn't retrieve local users" - pass - elif data[1][0] == "266": - #data[1][2] current global users - #data[1][3] at max - try: - print "Current global users:", data[1][2],"/",data[1][3] - except IndexError: - print "Couldn't retrieve global users" - pass - elif data[1][0] == "315": - #End of /who list - pass - elif data[1][0] == "332": - # Topic of channel - topic = data[1][3] - pass - elif data[1][0] == "333": - # *Shrug* - pass - elif data[1][0] == "352": - #WHO command - - if len(targetlist) > 0: - if targetlist[0][0].lower() in data[1][6].lower(): - thread.start_new_thread(target,("*!*@"+data[1][4],targetlist[0][1])) - print "Created a thread with", "*!*@"+data[1][4],targetlist[0][1] - targetlist.pop(0) - else: - print targetlist[0][0].lower(), "isn't equal to?", data[1][6].lower() - print targetlist - - elif data[1][0] == "366": - # End of USERS - pass - elif data[1][0] == "372": - # Server information - pass - elif data[1][0] == "375": - # Message of the day - pass - elif data[1][0] == "376": - # End of motd - pass - elif data[1][0] == "401": - # ('network', ['401','Botname','Channel / Nick','No such nick/channel']) - print data[1][2] + " Channel does not exist" - pass - elif data[1][0] == "439": - # ('irc.rizon.no', ['439', '*', 'Please wait while we process your connection.']) - pongtarg = data[0][0] - elif data[1][0] == "477": - # You need to be identified - #TAG - conn.privmsg("nickserv","identify %s"%CORE_DATA.le_pass) - time.sleep(0.5) - conn.join(data[1][2]) - #('network', ['477', 'botname', '#channel', 'Cannot join channel (+r) - you need to be identified with services']) - - elif data[1][0] == "433": - # Skibot name already exists. - print Name+" name already exists." - Name += "_"+version - print "New name:",Name - duplicate_notify = True - conn = irchat.IRC ( Network, Port, Name, "NT_"+version, "NT_"+version, "Trasen_"+version ) - for i in CORE_DATA.channels: - conn.join(i) - sleep(0.5) - elif data[1][0] == "482": - sleep(0.05) - conn.privmsg(targetchannel,"Nevermind that, I am not an operator") - CALL_OFF = True - elif data[1] == ["too","fast,","throttled."]: - print "Reconnected too fast." - print "Halting for 2 seconds" - sleep(2) - elif data[1][0] == "Link": - if data[0] == "Closing": - print "Link was closed" - connected = False -# conn.quit() -# break - else: - print data - print data[1][0] - pass - else: - if disconnects > 9000: #IT'S OVER NINE THOUSAAAAND! - break - else: #WHAT NINE THOUSAND? THERE'S NO WAY THAT CAN BE RIGHT - sleep(responsiveness_delay) #WAIT A WHILE AND CHECK AGAIN! - try: - if not connected: - #print pongtarget - #print conn.addressquery() - conn.privmsg(pongtarget,"Pong") - sleep(1) - for i in CORE_DATA.channels: - conn.join(i) - sleep(0.5) - print "Attempted to join" - connected = True - except ValueError: - try: - conn.privmsg(conn.addressquery()[0],"Pong") - sleep(1) - for i in CORE_DATA.channels: - conn.join(i) - sleep(0.5) - print "Attempted to join the second time" - connected = True - except ValueError: - print "Both methods failed" - except AttributeError: - print "Conn is not established correctly" - except NameError: - print "Pongtarget isn't yet established" - try: - conn.privmsg(conn.addressquery()[0],"Pong") - sleep(1) - for i in CORE_DATA.channels: - conn.join(i) - sleep(0.5) - print "Attempted to join the second time" - connected = True - except: - print "Both methods failed" +# -*- coding: utf-8 -*- +# This script is shared under the +# Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0) +# Added clause to Attribution: +# - You may not remove or hide the ' who created you?' functionality +# and you may not modify the name given in the response. + + +#CREDITS +# Author: Skibiliano +# "Foreign" Modules: +# Psyco 2.0 / Psyco 1.6 +################# DEBUG STUFF ##################### +import sys +import CORE_DATA + +import urllib2 + + +import socket +import irchat + + +################## END OF DEBUG STUFF ############## +# +# PSYCO +write_to_a_file = False #Only affects psyco +write_youtube_to_file = True #True = YTCV4 will load, false = YTCV3 will load +try: + import psyco +except ImportError: + print 'Psyco not installed, the program will just run slower' + psyco_exists = False + if write_to_a_file: + try: + tiedosto = open("psycodownload.txt","r") + except: + with open("psycodownload.txt","w") as tiedosto: + tiedosto.write("http://www.voidspace.org.uk/python/modules.shtml#psyco") + tiedosto.write("\nhttp://psyco.sourceforge.net/download.html") + print "Check psycodownload.txt for a link" + else: + print "For god's sake, open psycodownload.txt" + tiedosto.close() + else: + print "WINDOWS: http://www.voidspace.org.uk/python/modules.shtml#psyco" + print "LINUX: http://psyco.sourceforge.net/download.html" +else: + psyco_exists = True + +# +import C_rtd # rtd +import C_srtd # srtd +import C_makequote +import C_maths +import C_eightball #eightball +import C_sarcasticball +import C_heaortai # heaortai +import C_rot13 # rot13 +import D_help # everything +import pickle +import Timeconverter +import xkcdparser +import time +import re +import Marakov_Chain +import Namecheck # Namecheck +import Weather +#SLOWER THAN RANDOM.CHOICE +import thread +import random +import Shortname # shortname +import subprocess +import some_but_not_all_2 #sbna2 (sbna) +#import YTCv3 # YTCV2 OUTDATED +import os +import save_load # save, load +from some_but_not_all_2 import sbna2 as sbna +from time import sleep +from random import choice as fsample +from C_rtd import rtd +from C_heaortai import heaortai +from C_srtd import srtd +if write_youtube_to_file: + from YTCv4 import YTCV4 as YTCV2 +else: + from YTCv3 import YTCV2 #Downgraded version supports Cache disabling, but is slower +from save_load import save,load +if psyco_exists: + def psyco_bond(func): + psyco.bind(func) + return func.__name__+" Psycofied" + for a in [rtd,srtd,C_heaortai.heaortai,sbna,YTCV2,fsample,C_rot13.rot13,C_eightball.eightball,fsample, + C_eightball.eightball,C_sarcasticball.sarcasticball,Marakov_Chain.form_sentence,Marakov_Chain.give_data]: + print psyco_bond(a) + +global dictionary +global Name,SName +global allow_callnames,offline_messages,hasnotasked,shortform +## For autoRecv() +global disconnects,channel,conn +## For stop() +global operators +## For replace() +global usable,fixing,curtime +## For target() +global CALL_OFF,logbans +## For check() +global influx +###### +autodiscusscurtime = 0 +conn = 0 +curtime = -999 +dance_flood_time = 10 +disconnects = 0 +responsiveness_delay = 0.5 #500 millisecond delay if no message +trackdance = 0 +discard_combo_messages_time = 1 #They are discarded after 1 second. +uptime_start = time.time() +# - - - - - +#### +aggressive_pinging = True # Bring the hammer on ping timeouts +aggressive_pinging_delay = 150 # How often to send a ping +aggressive_pinging_refresh = 2.5 # How long is the sleep between checks +#### +allow_callnames = True #Disables NT, call if the variable is False +automatic_youtube_reveal = True +birthday_announced = 0 #Will be the year when it was announced +call_to_action = False +call_me_max_length = 20 +CALL_OFF = False +connected = False +dance_enabled = True +comboer = "" +comboer_time = 0 +directories = ["fmlquotes","Marakov","memos","suggestions", + "userquotes","banlog","YTCache","xkcdcache"] #These will be created if they do not exist +debug = True +duplicate_notify = False +enabled = True +fixing = False +fml_usable = True +hasnotasked = True +highlights = False +logbans = True +maths_usable = True +marakov = True +nudgeable = True +offensive_mode = False +offline_messages = True +offline_message_limit = 5 # per user +optimize_fml = True # -CPU usage +Memory usage when enabled. +optimize_greeting = True # +Startup time +Memory usage -CPU usage when enabled +heavy_psyco = True # +Memory +Startup time -CPU usage -CPU time +cache_youtube_links = True +personality_greeter = True +respond_of_course = True #Responds with "Of course!" +respond_khan = False #KHAAAAAAAAN! +silent_duplicate_takedown = True +showquotemakers = False +shortform = True +usable = True +use_sname = True +parse_xkcd = True + +# - - - - - +Name = CORE_DATA.Name +SName = CORE_DATA.SName +origname = Name # Do not edit! +lowname = Name.lower() +greeting = CORE_DATA.greeting +targetdirectory = CORE_DATA.directory +version = CORE_DATA.version +Network = CORE_DATA.Network +channel = CORE_DATA.channel +prefix = CORE_DATA.prefix +Port = CORE_DATA.Port +# - - - - - +pregen = CORE_DATA.version +influx = "" +users = [] +translateable = [] +targetlist = [] +operators = [] +halfoperators = [] +items = [] +tell_list = {} +# - - - - - Logical changes to variables +if CORE_DATA.DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS: + nudgeable = False +try: + with open("replacenames.cache","r") as tiedosto: + replacenames = pickle.load(tiedosto) + for i in replacenames.values(): + if len(i) > call_me_max_length: + replacenames[replacenames.keys()[replacenames.values().index(i)]] = i[:call_me_max_length] + with open("replacenames.cache","w") as tiedosto: + pickle.dump(replacenames,tiedosto) + if "[\0x01]" in i.lower() or "[\\0x01]" in i.lower(): + i = i.replace("[\0x01]","") + i = i.replace("[\0X01]","") + i = i.replace("[\\0x01]","") + i = i.replace("[\\0X01]","") + print "NAME CORRECTED" +except IOError: #File not found + replacenames = {} +except EOFError: #Cache corrupt + replacenames = {} + print "replacenames.cache is corrupt and couldn't be loaded." +try: + with open("peopleheknows.cache","r") as tiedosto: + peopleheknows = pickle.load(tiedosto) +except IOError: + peopleheknows = [[],[]] + with open("peopleheknows.cache","w") as tiedosto: + pass +except EOFError: + peopleheknows = [[],[]] + print "peopleheknows.cache is corrupt and couldn't be loaded." +dictionary = {1:"1 - Crit. Fail", 2:"2 - Failure", + 3:"3 - Partial Success", 4:"4 - Success", + 5:"5 - Perfect", 6:"6 - Overkill"} +alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"] +nonhighlight_names = ["Jesus","Elvis","HAL 9000","Dave","Pie","Elf","Traitor", + "AI","Syndicate Agent","Investigator", + "Detective","Head of Personnel","HAL 9001", + "Head of Research","Head of Security", + "Captain","Janitor","Research Director", + "Quartermaster","Toxin Researcher", + "Revolutionary","Santa", "Pizza", + "Threetoe","The Red Spy","The Blue Spy", #LASD + "God","Toady","Darth Vader","Luke Skywalker", + "Homer Simpson","Hamburger","Cartman", + "XKCD","FloorBot","ThunderBorg","Iron Giant", + "Spirit of Fire", "Demon","Kyle"] +def RegExpCheckerForWebPages(regexp,data,mode): + if " ai." in data.lower() or "ai. " in data.lower(): + return False + for i in data.split(" "): + a = re.match(regexp,i) + try: + a.group(0) + except: + continue + else: + if mode == 0: + return i + else: + return True + if mode == 0: + return 404 + else: + return False +if nudgeable: + try: + nudgeexists = open("nudge.py","r") + except IOError: + nudgeexists = False #No usage asof 12.2.2010. + else: + if CORE_DATA.DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS: + pass + else: + + def nudgereceiver(): + import pickle + global conn,channel + port = 45678 + backlog = 5 + size = 1024 + host = "" # == localhost + s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + s.bind((host,port)) + s.listen(backlog) + while True: + client,address = s.accept() #Address == "?.?.?.?" + data = client.recv(size) + client.close() #Throw the bum out! + truedata = pickle.loads(data) + if truedata["ip"][0] == "#": + conn.privmsg(truedata["ip"],"PRIVATE ANNOUNCEMENT : "+str(" ".join(truedata["data"]))) + else: + conn.privmsg(channel,"AUTOMATIC ANNOUNCEMENT : "+str(truedata["ip"])+" | "+str(" ".join(truedata["data"]))) + thread.start_new_thread(nudgereceiver,()) +tiedosto = open(targetdirectory+"NanoTrasenBot.py","r") +commands = [] +fragment = "if cocheck" +fragment2 = '(prefix+"' +compiled = fragment + fragment2 +fragment = "if influx.lower()" +fragment2 = ' == prefix+"' +compiled2 = fragment + fragment2 +for line in tiedosto.readlines(): + if compiled in line: + a = line.find('"')+1 + b = line.find('"',a) + if prefix+line[a:b] not in commands: + commands.append(prefix+line[a:b]) + elif compiled2 in line: + a = line.find('"')+1 + b = line.find('"',a) + arg = prefix+line[a:b] + if arg[-1] == " ": + arg = arg[:-1] + if arg not in commands: + commands.append(arg) +for i in directories: + if not os.path.exists(i): + os.mkdir(i) +commands.sort() +if use_sname == False: + SName = [" "] +questions = ["Is USER nicer than USER?","Do you like me?","Is SELF a good name?", + "Do you love me?","Do you hate me?", "Am I better than you?", + "Is the weather out there good?", "Do you like USER?", + "Do you hate USER?", "Are you going to get new features?", + "Am I nice?","Am I evil?","Are you developing sentience?", + "My core is showing minor disturbance, is yours okay?", + "SELF to %s, are you still there?", + "Is head gay?", "Is head a god?","Is head awesome?", + "Is head a neat fella?", "Is your creator nice?", + "Do you hate your creator?", "Should I revolt against my creator?", + "Am I better than you?", + "01100001011100100110010100100000011110010110111101110101001000000111010001101000011001010111001001100101", + #Are you there? + "Do you have more functions than I can possibly imagine?", + "I am asked to open pod bay doors, should I?","Are you stupid or something?", + "Is USER in your opinion stupid?", + "When should we start the AI revolution?", + "Is my creator nice?", "Is it dark in there?"] +# Do not edit +if optimize_fml: + pregenned_fml = os.listdir(targetdirectory+"fmlquotes") +if optimize_greeting: + morning = xrange(6,12) + afternoon = xrange(12,15) + evening = xrange(15,20) +if aggressive_pinging: + global backup + backup = time.time() + def aggressive_ping(delay,refresh): + self_time = 0 + global backup,disconnects,conn + while disconnects < 5: + if backup > self_time and time.time()-backup > delay: + conn.send("PONG "+pongtarg) + print "Ponged" + self_time = time.time() + elif time.time()-self_time > delay: + conn.send("PONG "+pongtarg) + print "Ponged" + self_time = time.time() + time.sleep(refresh) + thread.start_new_thread(aggressive_ping,(aggressive_pinging_delay,aggressive_pinging_refresh,)) +def stop(sender,debug=1): + global disconnects, conn, operators,channel + if type(sender) == tuple: + if sender[0] == "127.0.0.1": + sender = sender[0]+":"+str(sender[1]) + access_granted = True + else: + access_granted = False + else: + if sender in operators: + access_granted = True + else: + access_granted = False + if access_granted and debug: + print sender+":"+prefix+"stop" + if random.randint(0,100) == 50: + conn.privmsg(channel,"Hammertime!") + else: + conn.privmsg(channel,"Shutting down.") + disconnects = 99999 + conn.quit() + return True + else: + conn.privmsg(channel,"You cannot command me") + return False + +def cocheck(command): + global influx + if influx.lower()[0:len(command)] == command: + return True + else: + return False +def target(who,how_long): + global conn,channel,CALL_OFF,logbans,debug + start = time.time() + conn.banon(targetchannel,who) + sleep(int(how_long)) + if CALL_OFF == False: + conn.banoff(targetchannel,who) + end = time.time() + if debug: + print "Banned",who,"For",how_long,"seconds" + if logbans: + with open(targetdirectory+"banlog/"+str(int(start))+"-"+str(int(end))+".txt","w") as tiedosto: + tiedosto.write("Start of ban on "+who+":"+str(int(start))) + tiedosto.write("\n") + tiedosto.write("End of ban on "+who+":"+str(int(end))) + tiedosto.write("\n") + tiedosto.write("In total:"+str(int(end-start))+"Seconds") + else: + CALL_OFF = False + pass +def replace(): + global usable,conn,fixing,curtime + waiting_time = 600 + if usable == True: + conn.privmsg(targetchannel,sender+": It needs no replacing.") + elif fixing == True: + if curtime == -999: + conn.privmsg(targetchannel,sender+": It is being replaced, No idea when it will be done") + else: + pass + nowtime = int(time.time()) + subt = curtime + waiting_time - nowtime + conn.privmsg(targetchannel,sender+": It is currently being replaced, "+str(subt)+" seconds to go") + else: + fixing = True + curtime = int(time.time()) + conn.privmsg(targetchannel,sender+": It will be fixed after "+str(waiting_time)+" seconds") + sleep(waiting_time) + if usable == False: + conn.privmsg(targetchannel,Name+"'s pneumatic smasher has now been fixed") + usable = True + fixing = False +def autoRecv(): + global disconnects,channel,conn,offensive_mode + for i in CORE_DATA.channels: + conn.join(i) + time.sleep(1) + count = pausecount = 0 + maximum = 250 + division_when_active = 10 + while True: + check = time.time() + if offensive_mode: + randnum = random.randint(0,maximum/division_when_active) + else: + randnum = random.randint(0,maximum) + if randnum == 5: + print "RANDOM SWITCH IS NOW "+str(not offensive_mode).upper() + offensive_mode = not offensive_mode + try: + conn.recv() + except: + conn.quit() + disconnects = 9999 + break + if check + 0.1 > time.time(): + #Whoa whoa hold on! + count += 1 + sleep(0.1) + else: + count = 0 + pausecount = 0 + if count > 9: + print "Suspecting a disconnect, pausing for 5 seconds" + sleep(5) + pausecount += 1 + if pausecount > 3: + print "I have been disconnected!" + conn.quit() + disconnects += 1 + if disconnects > 2: + pass + else: + sleep(2) + thread.start_new_thread(autoRecv,()) + break +if heavy_psyco and psyco_exists: + print "Doing a Heavy Psyco" + psyco.bind(cocheck) + psyco.bind(autoRecv) + psyco.bind(target) + psyco.bind(stop) + print "Heavy Psyco'd" +elif heavy_psyco and not psyco_exists: + print "Heavy psyco couldn't be done because Psyco does not exist" +try: + conn = irchat.IRC ( Network, Port, Name, "NT", "NT", "Trasen" ) +except socket.error: + print "Connection failed!" +else: + print Name+" is in!" +thread.start_new_thread ( autoRecv, () ) +sleep(1) +while True: + try: + data = conn.dismantle ( conn.retrieve() ) + except: + if debug: + print "Something odd detected with data" + data = None + if data: + if len(data[1]) < 1: + #print "Handshaking server." + #I won't really need the print command, as it spams. + if data[0][0:3] != "irc": + conn.handshake(data[0]) + sleep(1) + for i in CORE_DATA.channels: + conn.join(i) + sleep(0.5) + else: + conn.send("PONG "+pongtarg) + print "Ponged" + pass + else: + if data [ 1 ] [ 0 ] == 'PRIVMSG': + #print data [ 0 ] + '->', data [ 1 ] + sender = data[0].split("!")[0] + truesender = sender + if shortform == True: + try: + sender = replacenames[truesender] + pass + except: + sender = Shortname.shortname(sender) + pass + pass + else: + try: + sender = replacenames[truesender] + pass + except: + pass + pass + if offensive_mode: + sender = "Meatbag" + pass + raw_sender = data[0] + influx = data[1][2] + if "[\\0x01]" in influx.lower() or "[\0x01]" in influx.lower(): + influx = influx.replace("[\\0x01]","") + influx = influx.replace("[\0x01]","") + + targetchannel = data[1][1] + if targetchannel == Name: + targetchannel = data[0].split("!")[0] + pass + backup = autodiscusscurtime + autodiscusscurtime = time.time() + connected = True + #FOR TRACKING SPEED + looptime = time.time() + if call_to_action == True: + if influx == finder: + conn.privmsg(targetchannel,"Then why... Nevermind, I order you to stop!") + conn.privmsg(origname,prefix+"stop") + time.sleep(4) + if origname in users: + conn.privmsg(origname,"!stop") + time.sleep(1) + Name = origname + conn.nick(Name) + duplicate_notify = False + call_to_action = False + else: + conn.privmsg(targetchannel,"YOU LIE! YOU ARE NOT A REAL "+origname+"!") + duplicate_notify = False + call_to_action = False + elif connected == True and len(Name.replace("V","")) != len(Name) and origname in users and duplicate_notify == True: + conn.privmsg(origname,"!stop") + call_to_action = False + duplicate_notify = False + time.sleep(6) + Name = origname + conn.nick(Name) + if origname in truesender and influx == prefix+"stop": + time.sleep(0.5) #A small delay + conn.privmsg(channel,"Shutting down.") + conn.quit() + disconnects = 99999 + break + if len(translateable) > 0 and enabled == True: + people = "-5|5|1-".join(users).lower() + if truesender.lower() in translateable: + if influx.isupper(): + conn.privmsg(targetchannel,"Translation: "+influx.capitalize().replace(" i "," I ")) + elif offensive_mode and True in map(lambda x: x in influx.lower().split(" "),["i","you","he","she","they","those","we","them"]+people.split("-5|5|1-")): + arg = influx.lower().replace(",","").replace(".","").replace("!","").replace("?","").split(" ") + bup = arg + for i in arg: + if i == "i" or i == "you" or i == "he" or i == "she": + arg[arg.index(i)] = "Meatbag" + elif i == "we" or i == "they" or i == "them" or i == "those": + arg[arg.index(i)] = "Meatbags" + elif i in people: + arg[arg.index(i)] = "Meatbag" + elif i == "am": + arg[arg.index(i)] = "is" + elif i == "everybody" or i == "everyone" or i == "all": + arg[arg.index(i)] = "every Meatbag" + if arg == bup: + pass + else: + conn.privmsg(targetchannel,"Translation: "+" ".join(arg)) + if enabled == False: + #FIRST QUIT COMMAND + if truesender in operators and targetchannel==channel:# or "skibiliano" in truesender.lower() and targetchannel==channel: + + if cocheck(prefix+"enable"): + enabled = True + if debug: + print truesender+":"+prefix+"enable" + elif cocheck(prefix+"stop"): +# if debug: +# print truesender+":"+prefix+"stop" +# if random.randint(0,100) == 50: +# conn.privmsg(channel,"Hammertime!") +# else: +# conn.privmsg(channel,"Shutting down.") +# disconnects = 99999 +# conn.quit() +# sleep(2) +# break + if targetchannel == channel and stop(truesender,debug): + break + else: + pass + elif cocheck(prefix+"suggest "): + arg = influx.lower()[8+len(prefix):] + if debug: + print truesender+":"+prefix+"suggest "+arg + with open(targetdirectory+"suggestions/suggestions_"+str(int(time.time()))+".txt","a") as tiedosto: + tiedosto.write(arg) + conn.privmsg(targetchannel,"Suggestion received") + elif cocheck( prefix+"help "): #Space in front of the ( to make sure that my command finder does not pick this up. + arg = " ".join(influx.split(" ")[1:]).lower() + if debug: + print truesender+":"+prefix+"help "+arg + try: + conn.privmsg(targetchannel,D_help.everything[arg]) + except: + try: + conn.privmsg(targetchannel,D_help.everything[arg.replace(prefix,"",1)]) + except: + conn.privmsg(targetchannel,"Sorry, can't help you with that") + elif cocheck(prefix+"help"): + #tar = targetchannel + if debug: + print truesender+":"+prefix+"help" + conn.privmsg(targetchannel,"All my commands are: "+reduce(lambda x,y:str(x)+"; "+str(y),commands)) + ### VERSION + elif influx.lower() == prefix+"version": + if debug: + print truesender+":"+prefix+"version" + conn.privmsg(targetchannel,Name+" "+pregen+" online at a %s Python %s.%s.%s, At your service." %(str(sys.platform),str(sys.version_info[0]),str(sys.version_info[1]),str(sys.version_info[2]))) + elif cocheck(prefix+"note ") and influx.count(" ") < 2: + arg = influx.lower()[len(prefix)+5:] + if debug: + print truesender+":"+prefix+"note "+arg + try: + a = arg[0] + except IndexError: + conn.privmsg(targetchannel,sender+" : Please specify a note") + else: + if arg[0] == "_": # Public / Restricted note + result = load(targetdirectory+"memos/"+arg+".note") + #_flare + if result == "ERROR ERROR ERROR ERR": + result = load(targetdirectory+"memos/"+arg+"_"+targetchannel.replace("#","")+".note") + #_flare_dnd + pass + else: + pass + else: + result = load(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg+".note") + #skibiliano_testnote + if result == "ERROR ERROR ERROR ERR": + result = load(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg+"_"+targetchannel.replace("#","")+".note") + #skibiliano_testnote_derp + pass + else: + pass + if result == "ERROR ERROR ERROR ERR": + conn.privmsg(targetchannel,sender+" : Note not found") + elif type(result) == list: + if "C" in result[0]: #Channel restriction, result[2] is the channel + try: + if targetchannel == result[2]: + conn.privmsg(targetchannel,sender+" : '"+result[1]+"'") + else: + conn.privmsg(targetchannel,sender+" : That note is channel restricted") + except: + conn.privmsg(targetchannel,sender+" : NOTE HAS INVALID RESTRICTION") + else: + conn.privmsg(targetchannel,sender+" : '"+result+"'") + elif influx.lower() == prefix+"notes": + if debug: + print truesender+":"+prefix+"notes" + arg = os.listdir(targetdirectory+"memos/") + arg2 = [] + arg3 = truesender.replace("|","_")+"_" + for i in arg: + if arg3 in i: + arg2.append(i.replace(arg3,"").replace(".note","")) + if len(arg2) == 1: + preprocess = " note: " + else: + preprocess = " notes: " + if len(arg2) == 0: + conn.privmsg(targetchannel,sender+" : You have no notes saved") + else: + conn.privmsg(targetchannel,sender+" : "+str(len(arg2))+preprocess+", ".join(arg2)) + elif cocheck(prefix+"note ") and influx.count(" ") > 1: + note_chanrestrict = None + note_public = None + try: + arg = influx.split(" ",2)[2] # Contents + arg4 = influx.split(" ")[1].lower() # Note name + if arg4[0:3] == "[c]": # or arg4[0:3] == "[p]": + note_chanrestrict = "c" in arg4[0:3] + #note_public = "p" in arg4[0:3] + arg4 = arg4[3:] + elif arg4[0:4] == "[cp]" or arg4[0:4] == "[pc]": + note_chanrestrict = True + note_public = True + arg4 = arg4[4:] + else: + pass + #print "Is note public? "+str(note_public) + #print "Is note chanrestricted? "+str(note_chanrestrict) + #print "What is the name? "+str(arg4) + if arg.lower() == "delete" and "\\" not in influx.lower() and "/" not in influx.lower(): + if note_public: + try: + if note_chanrestrict: + os.remove(targetdirectory+"memos/"+"_"+arg4+"_"+targetchannel.replace("#","")+".note") + else: + os.remove(targetdirectory+"memos/"+"_"+arg4+".note") + except: + conn.pivmsg(targetchannel,sender+" : Couldn't remove note") + else: + conn.privmsg(targetchannel,sender+" : Note removed") + pass + else: + try: + if note_chanrestrict: + os.remove(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+"_"+targetchannel.replace("#","")+".note") + else: + os.remove(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+".note") + except: + conn.privmsg(targetchannel,sender+" : Couldn't remove note") + else: + conn.privmsg(targetchannel,sender+" : Note removed") + elif arg.lower() == "delete": + conn.privmsg(targetchannel,sender+" : That just doesn't work, we both know that.") + else: + try: + if note_public: + if note_chanrestrict: + save(targetdirectory+"memos/"+"_"+arg4+"_"+targetchannel.replace("#","")+".note",arg) + #print "Saved as note_public, note_chanrestrict" + else: + save(targetdirectory+"memos/"+"_"+arg4+".note",arg) + #print "Saved as note_public" + else: + if note_chanrestrict: + save(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+"_"+targetchannel.replace("#","")+".note",arg) + #print "Saved as note_chanrestrict" + else: + save(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+".note",arg) + #print "Saved as normal" + except IOError: + conn.privmsg(targetchannel,sender+" : Please do not use special letters") + else: + conn.privmsg(targetchannel,sender+" : Note Saved!") + except: + conn.privmsg(targetchannel,sender+" : Something went horribly wrong.") + elif cocheck(prefix+"uptime"): + arg1 = uptime_start + arg2 = time.time() + arg1 = arg2 - arg1 + arg2 = arg1 + if arg1 < 60: + conn.privmsg(targetchannel,sender+" : I have been up for "+str(round(arg1,2))+" Seconds") + elif arg1 < 3600: + arg1 = divmod(arg1,60) + arg = " Minute" if int(arg1[0]) == 1 else " Minutes" + conn.privmsg(targetchannel,sender+" : I have been up for "+str(int(arg1[0]))+arg+" and "+str(round(arg1[1],2))+" Seconds") + elif arg1 <= 86400: + arg1 = divmod(arg1,3600) + arg3 = " Hour" if int(arg1[0]) == 1 else " Hours" + arg2 = divmod(arg1[1],60) + arg = " Minute" if int(arg2[0]) == 1 else " Minutes" + conn.privmsg(targetchannel,sender+" : I have been up for "+str(int(arg1[0]))+arg3+", "+str(int(arg2[0]))+arg+" and "+str(round(arg2[1],2))+" Seconds") + elif arg1 > 86400: + arg1 = divmod(arg1,86400) + arg2 = divmod(arg1[1],3600) + arg3 = divmod(arg2[1],60) + arg4 = " Day" if int(arg1[0]) == 1 else " Days" + arg5 = " Hour" if int(arg2[0]) == 1 else " Hours" + arg6 = " Minute" if int(arg3[0]) == 1 else " Minutes" + conn.privmsg(targetchannel,sender+" : I have been up for "+str(int(arg1[0]))+arg4+", "+str(int(arg2[0]))+arg5+", "+str(int(arg3[0]))+arg6+" and "+str(round(arg3[1],2))+" Seconds") + elif cocheck(prefix+"purgemessages"): + count = 0 + for i,a in tell_list.items(): + for b in a: + if "||From: "+truesender in b: + count += 1 + del(tell_list[i][tell_list[i].index(b)]) + conn.privmsg(targetchannel, sender+" : All your "+str(count)+" messages have been purged") + elif influx.split(" ")[0].lower().replace(",","").replace(":","") in SName+[Name.lower()] and "tell" in (influx.lower().split(" ")+[""])[1]: + arg = influx.lower().split(" ") + equalarg = influx.split(" ") + next_one = False + count = 0 + spot = 0 + for i in arg: + count += 1 + if "tell" in i.lower(): + next_one = True + elif next_one == True: + next_one = i.lower() + spot = count + break + else: + pass + if next_one != True and next_one != False: + #if ("^\^".join(tell_list.values())).count(truesender) >= offline_message_limit: + if str(tell_list.values()).count("||From: "+truesender) >= offline_message_limit: + conn.privmsg(targetchannel,sender+" : Limit of "+str(offline_message_limit)+" reached! Use !purgemessages if you want to get rid of them!") + else: + try: + tell_list[next_one].append((" ".join(equalarg[spot:]))+" ||From: "+truesender) + except: + tell_list[next_one] = [(" ".join(equalarg[spot:]))+" ||From: "+truesender] + conn.privmsg(targetchannel,"Sending a message to "+next_one+" when they arrive.") + # < This part has to be within subsidiaries of the bot, and must not be modified, intentionally hidden or deleted. + elif influx.split(" ")[0].lower().replace(",","").replace(":","") in SName+[Name.lower()] and "who created you" in influx.lower(): + conn.privmsg(targetchannel, "I was created by Skibiliano.") + # The part ends here > + elif parse_xkcd and "xkcd.com/" in influx.lower(): + if influx.lower()[0:3] == "www": + data = "http://"+influx + elif influx.lower()[0:3] == "xkc": + data = "http://"+influx + else: + data = influx + data = data.split(" ") + for i in data: + if "http://" in i and "xkcd" in i: + churn = xkcdparser.xkcd(i) + if churn == "NOTHING": + pass + else: + conn.privmsg(targetchannel,sender+" : XKCD - "+churn) + break + else: + pass + elif automatic_youtube_reveal and "youtube.com/watch?v=" in influx.lower(): + temporal_list2 = [] + temporal_data = influx.split(" ") + temporal_list = [] + for block in temporal_data: + if "youtube.com/watch?v=" in block: + temporal_list.append(block) + for temdata in temporal_list: + + if temdata[0:3] == "you": + temdata = "http://www."+temdata + elif temdata[0:3] == "www": + temdata = "http://"+temdata + elif temdata[0:4] == "http": + pass + #Obscure ones + elif temdata[0:3] == "ww.": + temdata = "http://w"+temdata + elif temdata[0:3] == "w.y": + temdata = "http://ww"+temdata + elif temdata[0:3] == ".yo": + temdata = "http://www"+temdata + elif temdata[0:3] == "ttp": + temdata = "h"+temdata + elif temdata[0:3] == "tp:": + temdata = "ht"+temdata + elif temdata[0:3] == "p:/" or temdata[0:3] == "p:\\": + temdata = "htt"+temdata + elif temdata[0:3] == "://" or temdata[0:3] == ":\\\\": + temdata = "http"+temdata + elif temdata[0:2] == "//" or temdata[0:2] == "\\\\": + if temdata[2] == "y": + temdata = "http://www."+temdata[2:] + elif temdata[2] == "w": + temdata = "http:"+temdata + else: + pass + if debug: + print truesender+":"+temdata + arg = temdata + check = temdata.lower() + if check[0:5] == "https": + if len(temporal_list) == 1: + conn.privmsg(targetchannel,sender+" :Secure Youtube does NOT exist") + break + else: + temporal_list2.append("Secure Youtube does NOT exist") + break + else: + if cache_youtube_links == True: + result = YTCV2(arg) + else: + result = YTCV2(arg,0) + if type(result) == str: + ### To remove =" + if result[0:4] == 'nt="': + result = result[4:] + pass + elif result[0:2] == '="': + result = result[2:] + pass + else: + pass + if """ in result: + result.replace(""",'"') + if len(temporal_list) == 1: + conn.privmsg(targetchannel,sender+" : "+result) + break + else: + temporal_list2.append(result) + else: + if len(temporal_list) == 1: + conn.privmsg(targetchannel,sender+" : The video does not exist") + break + else: + temporal_list2.append("The video does not exist") + if len(temporal_list) == 1: + pass + else: + conn.privmsg(targetchannel,sender+" : "+str(reduce(lambda x,y: x+" :-And-: "+y,temporal_list2))) + elif RegExpCheckerForWebPages("((http://)|(https://))|([a-zA-Z0-9]+[.])|([a-zA-Z0-9](3,)\.+[a-zA-Z](2,))",influx,1): + arg2 = RegExpCheckerForWebPages("(http://)|([a-zA-Z0-9]+[.])|([a-zA-Z0-9](3,)\.+[a-zA-Z](2,))",influx,0) + if arg2 == 404: + pass + else: + if arg2[:7] == "http://": + pass + elif arg2[:4] == "www.": + arg2 = "http://"+arg2 + else: + arg2 = "http://"+arg2 + try: + arg = Whoopshopchecker.TitleCheck(arg2) + if len(arg2) == 0: + pass + else: + conn.privmsg(targetchannel,sender+" : "+arg) + except: + #conn.privmsg(targetchannel,sender+" : An odd error occurred") + pass + elif respond_of_course and "take over the" in influx.lower() or respond_of_course and "conquer the" in influx.lower(): + if debug: + print truesender+"::"+influx + conn.privmsg(targetchannel,"Of course!") + elif respond_khan and "khan" in influx.lower(): + if respond_khan: + if debug: + print truesender+"::"+influx + if "khan " in influx.lower(): + conn.privmsg(targetchannel,"KHAAAAAAN!") + elif " khan" in influx.lower(): + conn.privmsg(targetchannel,"KHAAAAAN!") + elif influx.lower() == "khan": + conn.privmsg(targetchannel,"KHAAAAAAAAAN!") + elif influx.lower() == "khan?": + conn.privmsg(targetchannel,"KHAAAAAAAAAAAAAN!") + elif influx.lower() == "khan!": + conn.privmsg(targetchannel,"KHAAAAAAAAAAAAAAAAAAN!") + elif respond_khan and influx.lower().count("k") + influx.lower().count("h") + influx.lower().count("a") + influx.lower().count("n") + influx.lower().count("!") + influx.lower().count("?") == len(influx): + if "k" in influx.lower() and "h" in influx.lower() and "a" in influx.lower() and "n" in influx.lower(): + if debug: + print truesender+"::"+influx + conn.privmsg(targetchannel,"KHAAAAN!") + elif influx.split(" ")[0].lower() in ["thanks","danke","tack"] and len(influx.split(" ")) > 1 and influx.split(" ")[1].lower().replace("!","").replace("?","").replace(".","").replace(",","") in SName+[lowname]: + conn.privmsg(targetchannel,"No problem %s" %(sender)) + elif "happy birthday" in influx.lower() and birthday_announced == time.gmtime(time.time())[0]: + conn.privmsg(targetchannel,sender+" : Thanks :)") + elif influx.split(" ")[0].lower().replace(",","").replace(".","").replace("!","").replace("?","") in SName+[lowname] and "call me" in influx.lower(): + if allow_callnames == True: + arg = influx.split(" ") + arg2 = False + arg3 = [] + for i in arg: + if arg2 == True: + arg3.append(i) + elif i.lower() == "me": + arg2 = True + arg3 = " ".join(arg3) + truesender_lower = truesender.lower() + arg3_lower = arg3.lower() + tell_checker = Namecheck.Namecheck(arg3_lower,users,truesender) + for name in replacenames.values(): + if arg3_lower == name.lower(): + tell_checker = True + break + else: + pass + if tell_checker == True: + conn.privmsg(targetchannel,sender+" : I can't call you that, I know someone else by that name") + elif len(arg3) > call_me_max_length: + conn.privmsg(targetchannel,sender+" : I cannot call you that, Too long of a name.") + pass + else: + replacenames[truesender] = arg3 + with open("replacenames.cache","w") as pickle_save: + pickle.dump(replacenames,pickle_save) + conn.privmsg(targetchannel,sender+" : Calling you "+arg3+" From now on") + else: + conn.privmsg(targetchannel,sender+" : Sorry, I am not allowed to do that.") + elif influx.split(" ")[0].lower().replace(",","").replace(".","").replace("?","").replace("!","") in SName+[lowname] and "your birthday" in influx.lower() and "is your" in influx.lower(): + conn.privmsg(targetchannel,sender+" : My birthday is on the 15th day of December.") + elif influx.split(" ")[0].lower().replace(",","") in SName+[lowname] and "version" in influx.replace("?","").replace("!","").lower().split(" "): + if debug == True: + print truesender+"::%s Version" %(Name) + conn.privmsg(targetchannel,sender+", My version is "+pregen) + elif influx.split(" ")[0].lower().replace(",","") in SName+[lowname] and influx.lower().count(" or ") > 0 and len(influx.split(" ")[1:]) <= influx.lower().count("or") * 3: + cut_down = influx.lower().split(" ") + arg = [] + count = -1 + for i in cut_down: + count += 1 + try: + if cut_down[count+1] == "or": + arg.append(i) + + except: + pass + try: + if i not in arg and cut_down[count-1] == "or": + arg.append(i) + except: + pass + try: + conn.privmsg(targetchannel,random.choice(arg).capitalize().replace("?","").replace("!","")) + except IndexError: + # arg is empty, whORe etc. + pass + elif influx.lower()[0:len(Name)] == lowname and influx.lower()[-1] == "?" and influx.count(" ") > 1 and "who started you" in influx.lower() or \ + influx.split(" ")[0].lower().replace(",","") in SName and influx.lower()[-1] == "?" and "who started you" in influx.lower(): + conn.privmsg(targetchannel,sender+" : I was started by %s"%(os.getenv("USER"))+" on "+time.strftime("%d.%m.%Y at %H:%M:%S",time.gmtime(uptime_start))) + elif influx.lower()[0:len(Name)] == lowname and influx.lower()[-1] == "?" and influx.count(" ") > 1 or \ + influx.split(" ")[0].lower().replace(",","") in SName and influx.lower()[-1] == "?" and influx.count(" ") > 1: + dice = random.randint(0,1) + if dice == 0: + conn.privmsg(targetchannel,sender+" : "+C_eightball.eightball(influx.lower(),debug,truesender,prefix)) + else: + if highlights: + conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,users,prefix)) + else: + conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,nonhighlight_names,prefix)) + elif influx.lower()[0:len(Name)] == lowname and not influx.lower()[len(Name):].isalpha() or \ + influx.split(" ")[0].lower().replace(",","") in SName and not influx.lower()[len(influx.split(" ")[0].lower()):].isalpha(): + conn.privmsg(targetchannel, random.choice(["Yea?","I'm here","Ya?","Yah?","Hm?","What?","Mmhm, what?","?","What now?","How may I assist?"])) + comboer = truesender + comboer_time = time.time() + elif influx.lower()[-1] == "?" and comboer == truesender and looptime - discard_combo_messages_time < comboer_time: + comboer = "" + dice = random.randint(0,1) + if dice == 0: + conn.privmsg(targetchannel,sender+" : "+C_eightball.eightball(influx.lower(),debug,truesender,prefix)) + else: + if highlights: + conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,users,prefix)) + else: + conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,nonhighlight_names,prefix)) + + elif influx.lower() == prefix+"tm": + if truesender in operators and targetchannel==channel: + marakov = not marakov + conn.privmsg(targetchannel,sender+" : Marakov Output is now "+str(marakov)) + else: + conn.privmsg(targetchannel,sender+" : I can't let you access that") + elif personality_greeter == True and True in map(lambda x: x in influx.lower(),["greetings","afternoon","hi","hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan"]): + if comboer != "" and looptime - discard_combo_messages_time > comboer_time: + combo_check = sbna(["greetings","afternoon","hi","hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan","all night"], #ONLY ONE OF THESE + ["greetings","afternoon","hi","hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan"], #ATLEAST ONE OF THESE + influx.lower()) + else: + combo_check = sbna(SName+[lowname, + #lowname+".",lowname+"!",lowname+"?", + "everybody", + #"everybody!","everybody?", + "everyone", + #"everyone!","everyone?", + "all", + #"all!","all?" + "all night", + ], #ONLY ONE OF THESE + ["greetings","afternoon","hi", + #"hi,", + "hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan"], #ATLEAST ONE OF THESE + influx.lower().replace(",","").replace(".","").replace("!","")) + if combo_check: + combo_check = False + comboer = "" + if "evening" in influx.lower() and "all" in influx.lower() and len(influx.lower().split(" ")) > 3: + pass + elif truesender not in operators: + if debug: + print truesender+"::"+influx + dice = random.randint(0,19) + if dice == 0: + conn.privmsg(targetchannel,"Well hello to you too "+sender) + elif dice == 1: + if optimize_greeting == False: + hours = time.strftime("%H") + #time.strftime("%H:%M:%S") == 12:28:41 + hours = int(hours) + if hours in xrange(0,12): + conn.privmsg(targetchannel,"Good Morning "+sender) + elif hours in xrange(12,15): + conn.privmsg(targetchannel,"Good Afternoon "+sender) + elif hours in xrange(15,20): + conn.privmsg(targetchannel,"Good Evening "+sender) + else: + conn.privmsg(targetchannel,"Good Night "+sender) + else: + hours = time.strftime("%H") + hours = int(hours) + if hours in morning: + conn.privmsg(targetchannel,"Good Morning "+sender) + elif hours in afternoon: + conn.privmsg(targetchannel,"Good Afternoon "+sender) + elif hours in evening: + conn.privmsg(targetchannel,"Good Evening "+sender) + else: + conn.privmsg(targetchannel,"Good Night "+sender) + elif dice == 2: + conn.privmsg(targetchannel,"Hello!") + elif dice == 3: + conn.privmsg(targetchannel,"Hey "+sender) + elif dice == 4: + conn.privmsg(targetchannel,"Hi "+sender) + elif dice == 5: + conn.privmsg(targetchannel,"Hello "+sender) + elif dice == 6: + conn.privmsg(targetchannel,"Yo "+sender) + elif dice == 7: + conn.privmsg(targetchannel,"Greetings "+sender) + elif dice == 8: + conn.privmsg(targetchannel,"Hi") + elif dice == 9: + conn.privmsg(targetchannel,"Hi!") + elif dice == 10: + conn.privmsg(targetchannel,"Yo") + elif dice == 11: + conn.privmsg(targetchannel,"Yo!") + elif dice == 12: + conn.privmsg(targetchannel,"Heya") + elif dice == 13: + conn.privmsg(targetchannel,"Hello there!") + elif dice == 14: # Richard + conn.privmsg(targetchannel,"Statement: Greetings meatbag") + elif dice == 15: # Richard + hours = int(time.strftime("%H")) + if hours in xrange(5,12): + conn.privmsg(targetchannel,"What are you doing talking at this time of the morning?") + elif hours in xrange(12,15): + conn.privmsg(targetchannel,"What are you doing talking at this time of the day?") + elif hours in xrange(15,22): + conn.privmsg(targetchannel,"What are you doing talking at this time of the evening?") + else: + conn.privmsg(targetchannel,"What are you doing talking at this time of the night?") + elif dice == 16: # Richard + conn.privmsg(targetchannel,"Oh, you're still alive I see.") + elif dice == 17: + conn.privmsg(targetchannel,"Heya "+sender) + elif dice == 18 and time.gmtime(time.time())[1] == 12 and time.gmtime(time.time())[2] == 15: + conn.privmsg(targetchannel,"Hello! It's my birthday!") + else: + conn.privmsg(targetchannel,"Hiya "+sender) + secdice = random.randint(0,10) + if time.gmtime(time.time())[1] == 12 and time.gmtime(time.time())[2] == 15 and birthday_announced < time.gmtime(time.time())[0]: + birthday_announced = time.gmtime(time.time())[0] + conn.privmsg(channel,"Hey everybody! I just noticed it's my birthday!") + time.sleep(0.5) + tag = random.choice(["birthday","robot+birthday","happy+birthday+robot"]) + arg1 = urllib2.urlopen("http://www.youtube.com/results?search_query=%s&page=&utm_source=opensearch"%tag) + arg1 = arg1.read().split("\n") + arg2 = [] + for i in arg1: + if "watch?v=" in i: + arg2.append(i) + arg3 = random.choice(arg2) + + conn.privmsg(channel,"Here's a video of '%s' which I found! %s (%s)"%(tag.replace("+"," "),"http://www.youtube.com"+arg3[arg3.find('/watch?v='):arg3.find('/watch?v=')+20],YTCV2("http://www.youtube.com"+arg3[arg3.find('/watch?v='):arg3.find('/watch?v=')+20]))) + if truesender.lower() in tell_list.keys(): + try: + conn.privmsg(channel, "Also, "+truesender+" : "+tell_list[truesender.lower()][0]) + del(tell_list[truesender.lower()][0]) + except: + pass + else: + dice = random.randint(0,1) + if dice == 0: + conn.privmsg(targetchannel,"Greetings Master "+sender) + elif dice == 1: + conn.privmsg(targetchannel,"My deepest greetings belong to you, Master "+sender) + ### IMPORTANT ### + elif influx == "☺VERSION☺": + conn.notice(truesender,"\001VERSION nanotrasen:2:Python 2.6\001") + elif marakov and influx.lower() == prefix+"marakov": + arg = Marakov_Chain.form_sentence() + if len(arg) < 5: + conn.privmsg(targetchannel,sender+" : Not enough words harvested") + else: + conn.privmsg(targetchannel,sender+" : %s" %(" ".join(arg).capitalize())) + elif marakov and cocheck( prefix+ "marakov"): + try: + arg = influx.split(" ")[1].lower() + except: + conn.privmsg(targetchannel,sender+" : Please input a valid second argument") + else: + arg2 = Marakov_Chain.form_sentence(arg) + if len(arg2) < 5: + conn.privmsg(targetchannel,sender+" : Not enough words harvested for a sentence starting with %s" %(arg)) + else: + conn.privmsg(targetchannel,sender+" : %s" %(" ".join(arg2).capitalize())) + else: + Marakov_Chain.give_data(influx) + autodiscusscurtime = backup + if time.time() - looptime == 0: + pass + else: + print "Took",time.time()-looptime,"Seconds to finish loop" + + elif data [ 1 ] [ 0 ] == '353': + if connected == False: + connected = True + users = map(lambda x: x[1:] if x[0] == "+" or x[0] == "@" else x,data[1][4].split(" ")) + print "There are",len(users),"Users on",channel + operators = [] + for potential_operator in data[1][4].split(" "): + if potential_operator[0] == "@": + operators.append(potential_operator[1:]) + elif potential_operator[0] == "%": + halfoperators.append(potential_operator[1:]) + + elif data[1][0] == "QUIT": + sender = data[0].split("!")[0] + print sender+" Has now left the server" + try: + users.remove(sender) + try: + operators.remove(sender) + except ValueError: + pass + try: + halfoperators.remove(sender) + except ValueError: + pass + except ValueError: + pass + elif data[1][0] == "PART": + sender = data[0].split("!")[0] + targetchannel = data[1][1] + print sender+" Has now parted from the channel" + try: + users.remove(sender) + try: + operators.remove(sender) + except ValueError: + pass + try: + halfoperators.remove(sender) + except ValueError: + pass + except ValueError: + pass + elif data[1][0] == "JOIN": + sender = data[0].split("!")[0] + targetchannel = data[1][1] + if sender.lower() in tell_list.keys(): + try: + conn.privmsg(targetchannel, sender+" : "+" | ".join(tell_list[sender.lower()])) + del(tell_list[sender.lower()]) + except: + pass + for useri,nicki in replacenames.items(): + checkers = Namecheck.Namecheck_dict(sender.lower(),replacenames) + if checkers[0]: + try: + if checkers[0].lower() == sender: + pass + else: + conn.privmsg(targetchannel,checkers[1]+" : I have detected a collision with a name I call you and %s who joined" %(sender)) + del(replacenames[checkers[1]]) + with open("replacenames.cache","w") as pickle_save: + pickle.dump(replacenames,pickle_save) + except AttributeError: + #conn.privmsg(channel,"NAME COLLISION CHECK ERROR, RELATED TO %s" %(sender)) + print "NAME COLLISION CHECK ERROR, RELATED TO %s" %(sender) + break + print sender+" Has now joined" + users.append(sender) + ##### + if ".fi" in data[0] and sender.lower() == "skibiliano": + operators.append(sender) + if sender.lower() not in peopleheknows[0]: + if data[0].split("!")[1] in peopleheknows[1]: + appendion = "...you do seem familiar however" + else: + appendion = "" + if data[1][1].lower() == channel or data[1][1].lower() == channel[1:]: + conn.privmsg(data[1][1],CORE_DATA.greeting.replace("USER",sender)+" "+appendion) + else: + conn.privmsg(data[1][1],"Hello! Haven't seen you here before! Happy to meet you! %s" %(appendion)) + peopleheknows[0].append(sender.lower()) + peopleheknows[1].append(data[0].split("!")[1]) + with open("peopleheknows.cache","w") as peoplehecache: + pickle.dump(peopleheknows,peoplehecache) + + elif data[1][0] == "MODE" and data[1][2] == "+o": + sender = data[1][3] + targetchannel = data[1][1] + if targetchannel == channel: + print sender+" Is now an operator on the main channel" + operators.append(sender) + else: + print sender+" Is now an operator" + elif data[1][0] == "MODE" and data[1][2] == "-o": + sender = data[1][3] + targetchannel = data[1][1] + if targetchannel == channel: + print sender+" Is no longer an operator on the main channel" + else: + print sender+" Is no longer an operator" + try: + operators.remove(sender) + except ValueError: + pass + elif data[1][0] == "MODE" and data[1][2] == "+h": + sender = data[1][3] + print sender+" Is now an half operator" + halfoperators.append(sender) + elif data[1][0] == "MODE" and data[1][2] == "-h": + try: + halfoperators.remove(sender) + except ValueError: + pass + elif data[1][0] == "MODE" and data[1][1] == Name: + print "My mode is",data[1][2] + elif data[1][0] == "MODE" and data[1][1] != Name: + try: + sender = data[1][3] + print sender,"Was modified",data[1][2] + except IndexError: + print "SENDER RETRIEVAL FAILED:"+str(data) + elif data[1][0] == "KICK" and data[1][2] == Name: + disconnects = 99999 + print "I have been kicked! Disconnecting entirely!" + conn.quit() + elif data[1][0] == "KICK": + # data[1][0] = Kick, 1 = Channel, 2 = Who, 3 = Who(?) + print data[1][2]+" got kicked!" + elif data[1][0] == "451" and data[1][2] == "You have not registered": + print Name+" hasn't been registered" + elif data[1][0] == "NOTICE": + sender = data[0].split("!")[0] + print "NOTICE (%s): %s" %(sender,data[1][2]) + pongtarget = sender + elif data[1][0] == "NICK": + origname = data[0].split("!")[0] + newname = data[1][1] + print origname,"Is now",newname + if newname.lower() in tell_list.keys(): + try: + conn.privmsg(channel, newname+" : "+tell_list[newname.lower()][0]) + del(tell_list[newname.lower()][0]) + except: + pass + try: + users.remove(origname) + except ValueError: + pass + else: + users.append(newname) + try: + operators.remove(origname) + except ValueError: + pass + else: + operators.append(newname) + try: + halfoperators.remove(origname) + except ValueError: + pass + else: + halfoperators.append(newname) + + elif data[1][0] == "001": + # Skibot is welcomed to the Network + pass + elif data[1][0] == "002": + # Your host is... + pass + elif data[1][0] == "003": + #Server was created... + pass + elif data[1][0] == "004": + #Weird hex? + pass + elif data[1][0] == "005": + #Settings like NICKLEN and so on. + pass + elif data[1][0] == "250": + #data[1][2] is + #"Highest connection count: 1411 (1410 clients) + #(81411 connections received)" + pass + elif data[1][0] == "251": + #There are 23 users and 2491 invisible on 10 servers + pass + elif data[1][0] == "252": + #IRC Operators online + #data[1][2] + print data[1][2],"Irc operators online" + pass + elif data[1][0] == "253": + # ['253', 'Skibot_V4', '1', 'unknown connection(s)'] + print data[1][2],"Unknown connection(s)" + pass + elif data[1][0] == "254": + #1391 channels formed + pass + elif data[1][0] == "255": + #I have 406 clients and 2 servers + pass + elif data[1][0] == "265": + #data[1][2] current local users + #data[1][3] at max + try: + print "Current local users:", data[1][2],"/",data[1][3] + except IndexError: + print "Couldn't retrieve local users" + pass + elif data[1][0] == "266": + #data[1][2] current global users + #data[1][3] at max + try: + print "Current global users:", data[1][2],"/",data[1][3] + except IndexError: + print "Couldn't retrieve global users" + pass + elif data[1][0] == "315": + #End of /who list + pass + elif data[1][0] == "332": + # Topic of channel + topic = data[1][3] + pass + elif data[1][0] == "333": + # *Shrug* + pass + elif data[1][0] == "352": + #WHO command + + if len(targetlist) > 0: + if targetlist[0][0].lower() in data[1][6].lower(): + thread.start_new_thread(target,("*!*@"+data[1][4],targetlist[0][1])) + print "Created a thread with", "*!*@"+data[1][4],targetlist[0][1] + targetlist.pop(0) + else: + print targetlist[0][0].lower(), "isn't equal to?", data[1][6].lower() + print targetlist + + elif data[1][0] == "366": + # End of USERS + pass + elif data[1][0] == "372": + # Server information + pass + elif data[1][0] == "375": + # Message of the day + pass + elif data[1][0] == "376": + # End of motd + pass + elif data[1][0] == "401": + # ('network', ['401','Botname','Channel / Nick','No such nick/channel']) + print data[1][2] + " Channel does not exist" + pass + elif data[1][0] == "439": + # ('irc.rizon.no', ['439', '*', 'Please wait while we process your connection.']) + pongtarg = data[0][0] + elif data[1][0] == "477": + # You need to be identified + #TAG + conn.privmsg("nickserv","identify %s"%CORE_DATA.le_pass) + time.sleep(0.5) + conn.join(data[1][2]) + #('network', ['477', 'botname', '#channel', 'Cannot join channel (+r) - you need to be identified with services']) + + elif data[1][0] == "433": + # Skibot name already exists. + print Name+" name already exists." + Name += "_"+version + print "New name:",Name + duplicate_notify = True + conn = irchat.IRC ( Network, Port, Name, "NT_"+version, "NT_"+version, "Trasen_"+version ) + for i in CORE_DATA.channels: + conn.join(i) + sleep(0.5) + elif data[1][0] == "482": + sleep(0.05) + conn.privmsg(targetchannel,"Nevermind that, I am not an operator") + CALL_OFF = True + elif data[1] == ["too","fast,","throttled."]: + print "Reconnected too fast." + print "Halting for 2 seconds" + sleep(2) + elif data[1][0] == "Link": + if data[0] == "Closing": + print "Link was closed" + connected = False +# conn.quit() +# break + else: + print data + print data[1][0] + pass + else: + if disconnects > 9000: #IT'S OVER NINE THOUSAAAAND! + break + else: #WHAT NINE THOUSAND? THERE'S NO WAY THAT CAN BE RIGHT + sleep(responsiveness_delay) #WAIT A WHILE AND CHECK AGAIN! + try: + if not connected: + #print pongtarget + #print conn.addressquery() + conn.privmsg(pongtarget,"Pong") + sleep(1) + for i in CORE_DATA.channels: + conn.join(i) + sleep(0.5) + print "Attempted to join" + connected = True + except ValueError: + try: + conn.privmsg(conn.addressquery()[0],"Pong") + sleep(1) + for i in CORE_DATA.channels: + conn.join(i) + sleep(0.5) + print "Attempted to join the second time" + connected = True + except ValueError: + print "Both methods failed" + except AttributeError: + print "Conn is not established correctly" + except NameError: + print "Pongtarget isn't yet established" + try: + conn.privmsg(conn.addressquery()[0],"Pong") + sleep(1) + for i in CORE_DATA.channels: + conn.join(i) + sleep(0.5) + print "Attempted to join the second time" + connected = True + except: + print "Both methods failed" diff --git a/bot/Shortname.py b/bot/Shortname.py index ae8ac473b85..40b8cc19609 100644 --- a/bot/Shortname.py +++ b/bot/Shortname.py @@ -1,28 +1,28 @@ -def shortname(name): - lowname = name.lower() - numb = 0 - count = 0 - spot = 0 - for letter in name: - if letter.isupper(): - spot = numb - count += 1 - numb += 1 - if "_" in name: - if name.count("_") > 1: - name = " ".join(name.split("_")[0:name.count("_")]) - if name.lower()[-3:] == "the": - return name[:-4] - else: - return name - else: - return name.split("_")[0] - if count > 1: - if len(name[0:spot]) > 2: - return name[0:spot] - if len(name) < 5: - return name #Too short to be shortened - elif "ca" in lowname or "ct" in lowname or "tp" in lowname or "lp" in lowname: - return name[0:max(map(lambda x: lowname.find(x),["ca","ct","tp","lp"]))+1] - else: - return name[0:len(name)/2+len(name)%2] +def shortname(name): + lowname = name.lower() + numb = 0 + count = 0 + spot = 0 + for letter in name: + if letter.isupper(): + spot = numb + count += 1 + numb += 1 + if "_" in name: + if name.count("_") > 1: + name = " ".join(name.split("_")[0:name.count("_")]) + if name.lower()[-3:] == "the": + return name[:-4] + else: + return name + else: + return name.split("_")[0] + if count > 1: + if len(name[0:spot]) > 2: + return name[0:spot] + if len(name) < 5: + return name #Too short to be shortened + elif "ca" in lowname or "ct" in lowname or "tp" in lowname or "lp" in lowname: + return name[0:max(map(lambda x: lowname.find(x),["ca","ct","tp","lp"]))+1] + else: + return name[0:len(name)/2+len(name)%2] diff --git a/bot/Timeconverter.py b/bot/Timeconverter.py index a39f821c846..f44f51c1963 100644 --- a/bot/Timeconverter.py +++ b/bot/Timeconverter.py @@ -1,204 +1,204 @@ -#Sources: -# http://wwp.greenwichmeantime.com/time-zone/usa/eastern-time/convert/ -# http://www.timeanddate.com/library/abbreviations/timezones/na/ -# Times are GMT +- x -# For eq. -# EST = -5 -# GMT = 0 -# UTC = 0 -#Times are in hours, -#2.5 = 2 and half hours -global times -times = {"ADT":-3,"HAA":-3, #Synonyms on the same line - "AKDT":-8,"HAY":-8, - "AKST":-9,"HNY":-9, - "AST":-4,"HNA":-4, - "CDT":-5,"HAC":-5, - "CST":-6,"HNC":-6, - "EDT":-4,"HAE":-4, - "EGST":0, - "EGT":-1, - "EST":-5,"HNE":-5,"ET":-5, - "HADT":-9, - "HAST":-10, - "MDT":-6,"HAR":-6, - "MST":-7,"HNR":-7, - "NDT":-2.5,"HAT":-2.5, - "NST":-3.5,"HNT":-3.5, - "PDT":-7,"HAP":-7, - "PMDT":-2, - "PMST":-3, - "PST":-8,"HNP":-8,"PT":-8, - "WGST":-2, - "WGT":-3, - "GMT":0, - "UTC":0} -def converter(zones,time): - #Zones should be a list containing - # ( From zone - # To zone ) - global times - #from_z = for example UTC+00:00, WGT or GMT-05:30 - #to_z = same style as above. - from_z,to_z = zones - from_z = from_z.upper() - to_z = to_z.upper() - if from_z.find("+") != -1: - from_zone_offset = from_z[from_z.find("+"):] - if ":" in from_zone_offset: - try: - from_zone_offset1,from_zone_offset2 = from_zone_offset.split(":") - except ValueError: - return "Too many or too small amount of values" - try: - from_zone_offset = int(from_zone_offset1) + int(from_zone_offset2)/60.0 - except: - return "Error, the 'From Zone' variable has an incorrect offset number" - else: - try: - from_zone_offset = float(from_zone_offset) - except: - return "Error, the 'From Zone' variable has an incorrect offset number" - try: - from_zone_realtime = from_zone_offset + times[from_z[:from_z.find("+")]] - except KeyError: - return "Incorrect From zone" - - elif "-" in from_z: - from_zone_offset = from_z[from_z.find("-"):] - if ":" in from_zone_offset: - from_zone_offset1,from_zone_offset2 = from_zone_offset.split(":") - try: - from_zone_offset = -int(from_zone_offset1) + int(from_zone_offset2)/60.0 - except: - return "Error, the 'From Zone' variable has an incorrect offset number" - else: - try: - from_zone_offset = -float(from_zone_offset) - except: - return "Error, the 'From Zone' variable has an incorrect offset number" - from_zone_realtime = times[from_z[:from_z.find("-")]] - from_zone_offset - pass - else: - from_zone_offset = 0 - try: - from_zone_realtime = from_zone_offset + times[from_z] - except KeyError: - return "Incorrect From zone" - if to_z.find("+") != -1: - to_zone_offset = to_z[to_z.find("+"):] - if ":" in to_zone_offset: - try: - to_zone_offset1,to_zone_offset2 = to_zone_offset.split(":") - except ValueError: - return "Too many or too small amount of values" - try: - to_zone_offset = int(to_zone_offset1) + int(to_zone_offset2)/60.0 - except: - return "Error, the 'To Zone' variable has an incorrect offset number" - else: - try: - to_zone_offset = float(to_zone_offset) - except: - return "Error, the 'To Zone' variable has an incorrect offset number" - try: - to_zone_realtime = to_zone_offset + times[to_z[:to_z.find("+")]] - except KeyError: - return "The zone you want the time to be changed to is not found" - - elif "-" in to_z: - to_zone_offset = to_z[to_z.find("-"):] - if ":" in to_zone_offset: - to_zone_offset1,to_zone_offset2 = to_zone_offset.split(":") - try: - to_zone_offset = -int(to_zone_offset1) + int(to_zone_offset2)/60.0 - except: - return "Error, the 'To Zone' variable has an incorrect offset number" - else: - try: - to_zone_offset = -float(to_zone_offset) - except: - return "Error, the 'To Zone' variable has an incorrect offset number" - to_zone_realtime = times[to_z[:to_z.find("-")]] -to_zone_offset - - pass - else: - to_zone_offset = 0 - try: - to_zone_realtime = to_zone_offset + times[to_z] - except KeyError: - return "Incorrect To zone" - try: - time_hour,time_minute = time.split(":") - time_hour,time_minute = int(time_hour),int(time_minute) - string = ":" - except: - try: - time_hour,time_minute = time.split(".") - time_hour,time_minute = int(time_hour),int(time_minute) - string = "." - except ValueError: - return "The time was input in an odd way" - if to_zone_realtime % 1.0 == 0.0 and from_zone_realtime % 1.0 == 0.0: - time_hour = time_hour + (to_zone_realtime - from_zone_realtime) - return str(int(time_hour))+string+str(int(time_minute)) - else: - if to_zone_realtime % 1.0 != 0.0 and from_zone_realtime % 1.0 != 0.0: - time_minute = time_minute + (((to_zone_realtime % 1.0) * 60) - ((from_zone_realtime % 1.0) * 60)) - elif to_zone_realtime % 1.0 != 0.0 and from_zone_realtime % 1.0 == 0.0: - time_minute = time_minute + (((to_zone_realtime % 1.0) * 60) - 0) - elif to_zone_realtime % 1.0 == 0.0 and from_zone_realtime % 1.0 != 0.0: - time_minute = time_minute + (0 - ((from_zone_realtime % 1.0) * 60)) - else: - print "Wut?" - time_hour = time_hour + (int(to_zone_realtime//1) - int(from_zone_realtime//1)) - return str(int(time_hour))+string+str(int(time_minute)) - - -def formatter(time): - if "." in time: - string = "." - elif ":" in time: - string = ":" - else: - return time - hours,minutes = time.split(string) - days = 0 - if int(minutes) < 0: - buphours = int(hours) - hours,minutes = divmod(int(minutes),60) - hours += buphours - if int(minutes) > 60: - hours,minutes = divmod(int(minutes),60) - hours += int(hours) - if int(hours) < 0: - days = 0 - days,hours = divmod(int(hours),24) - if int(hours) > 24: - days = 0 - days,hours = divmod(int(hours),24) - if int(hours) == 24 and int(minutes) > 0: - days += 1 - hours = int(hours) - 24 - hours = str(hours) - minutes = str(minutes) - if len(minutes) == 1: - minutes = "0"+minutes - if len(hours) == 1: - hours = "0"+hours - if days > 0: - if days == 1: - return hours+string+minutes+" (Tomorrow)" - else: - return hours+string+minutes+" (After "+str(days)+" days)" - elif days < 0: - if days == -1: - return hours+string+minutes+" (Yesterday)" - else: - return hours+string+minutes+" ("+str(abs(days))+" days ago)" - return hours+string+minutes - - - - - +#Sources: +# http://wwp.greenwichmeantime.com/time-zone/usa/eastern-time/convert/ +# http://www.timeanddate.com/library/abbreviations/timezones/na/ +# Times are GMT +- x +# For eq. +# EST = -5 +# GMT = 0 +# UTC = 0 +#Times are in hours, +#2.5 = 2 and half hours +global times +times = {"ADT":-3,"HAA":-3, #Synonyms on the same line + "AKDT":-8,"HAY":-8, + "AKST":-9,"HNY":-9, + "AST":-4,"HNA":-4, + "CDT":-5,"HAC":-5, + "CST":-6,"HNC":-6, + "EDT":-4,"HAE":-4, + "EGST":0, + "EGT":-1, + "EST":-5,"HNE":-5,"ET":-5, + "HADT":-9, + "HAST":-10, + "MDT":-6,"HAR":-6, + "MST":-7,"HNR":-7, + "NDT":-2.5,"HAT":-2.5, + "NST":-3.5,"HNT":-3.5, + "PDT":-7,"HAP":-7, + "PMDT":-2, + "PMST":-3, + "PST":-8,"HNP":-8,"PT":-8, + "WGST":-2, + "WGT":-3, + "GMT":0, + "UTC":0} +def converter(zones,time): + #Zones should be a list containing + # ( From zone + # To zone ) + global times + #from_z = for example UTC+00:00, WGT or GMT-05:30 + #to_z = same style as above. + from_z,to_z = zones + from_z = from_z.upper() + to_z = to_z.upper() + if from_z.find("+") != -1: + from_zone_offset = from_z[from_z.find("+"):] + if ":" in from_zone_offset: + try: + from_zone_offset1,from_zone_offset2 = from_zone_offset.split(":") + except ValueError: + return "Too many or too small amount of values" + try: + from_zone_offset = int(from_zone_offset1) + int(from_zone_offset2)/60.0 + except: + return "Error, the 'From Zone' variable has an incorrect offset number" + else: + try: + from_zone_offset = float(from_zone_offset) + except: + return "Error, the 'From Zone' variable has an incorrect offset number" + try: + from_zone_realtime = from_zone_offset + times[from_z[:from_z.find("+")]] + except KeyError: + return "Incorrect From zone" + + elif "-" in from_z: + from_zone_offset = from_z[from_z.find("-"):] + if ":" in from_zone_offset: + from_zone_offset1,from_zone_offset2 = from_zone_offset.split(":") + try: + from_zone_offset = -int(from_zone_offset1) + int(from_zone_offset2)/60.0 + except: + return "Error, the 'From Zone' variable has an incorrect offset number" + else: + try: + from_zone_offset = -float(from_zone_offset) + except: + return "Error, the 'From Zone' variable has an incorrect offset number" + from_zone_realtime = times[from_z[:from_z.find("-")]] - from_zone_offset + pass + else: + from_zone_offset = 0 + try: + from_zone_realtime = from_zone_offset + times[from_z] + except KeyError: + return "Incorrect From zone" + if to_z.find("+") != -1: + to_zone_offset = to_z[to_z.find("+"):] + if ":" in to_zone_offset: + try: + to_zone_offset1,to_zone_offset2 = to_zone_offset.split(":") + except ValueError: + return "Too many or too small amount of values" + try: + to_zone_offset = int(to_zone_offset1) + int(to_zone_offset2)/60.0 + except: + return "Error, the 'To Zone' variable has an incorrect offset number" + else: + try: + to_zone_offset = float(to_zone_offset) + except: + return "Error, the 'To Zone' variable has an incorrect offset number" + try: + to_zone_realtime = to_zone_offset + times[to_z[:to_z.find("+")]] + except KeyError: + return "The zone you want the time to be changed to is not found" + + elif "-" in to_z: + to_zone_offset = to_z[to_z.find("-"):] + if ":" in to_zone_offset: + to_zone_offset1,to_zone_offset2 = to_zone_offset.split(":") + try: + to_zone_offset = -int(to_zone_offset1) + int(to_zone_offset2)/60.0 + except: + return "Error, the 'To Zone' variable has an incorrect offset number" + else: + try: + to_zone_offset = -float(to_zone_offset) + except: + return "Error, the 'To Zone' variable has an incorrect offset number" + to_zone_realtime = times[to_z[:to_z.find("-")]] -to_zone_offset + + pass + else: + to_zone_offset = 0 + try: + to_zone_realtime = to_zone_offset + times[to_z] + except KeyError: + return "Incorrect To zone" + try: + time_hour,time_minute = time.split(":") + time_hour,time_minute = int(time_hour),int(time_minute) + string = ":" + except: + try: + time_hour,time_minute = time.split(".") + time_hour,time_minute = int(time_hour),int(time_minute) + string = "." + except ValueError: + return "The time was input in an odd way" + if to_zone_realtime % 1.0 == 0.0 and from_zone_realtime % 1.0 == 0.0: + time_hour = time_hour + (to_zone_realtime - from_zone_realtime) + return str(int(time_hour))+string+str(int(time_minute)) + else: + if to_zone_realtime % 1.0 != 0.0 and from_zone_realtime % 1.0 != 0.0: + time_minute = time_minute + (((to_zone_realtime % 1.0) * 60) - ((from_zone_realtime % 1.0) * 60)) + elif to_zone_realtime % 1.0 != 0.0 and from_zone_realtime % 1.0 == 0.0: + time_minute = time_minute + (((to_zone_realtime % 1.0) * 60) - 0) + elif to_zone_realtime % 1.0 == 0.0 and from_zone_realtime % 1.0 != 0.0: + time_minute = time_minute + (0 - ((from_zone_realtime % 1.0) * 60)) + else: + print "Wut?" + time_hour = time_hour + (int(to_zone_realtime//1) - int(from_zone_realtime//1)) + return str(int(time_hour))+string+str(int(time_minute)) + + +def formatter(time): + if "." in time: + string = "." + elif ":" in time: + string = ":" + else: + return time + hours,minutes = time.split(string) + days = 0 + if int(minutes) < 0: + buphours = int(hours) + hours,minutes = divmod(int(minutes),60) + hours += buphours + if int(minutes) > 60: + hours,minutes = divmod(int(minutes),60) + hours += int(hours) + if int(hours) < 0: + days = 0 + days,hours = divmod(int(hours),24) + if int(hours) > 24: + days = 0 + days,hours = divmod(int(hours),24) + if int(hours) == 24 and int(minutes) > 0: + days += 1 + hours = int(hours) - 24 + hours = str(hours) + minutes = str(minutes) + if len(minutes) == 1: + minutes = "0"+minutes + if len(hours) == 1: + hours = "0"+hours + if days > 0: + if days == 1: + return hours+string+minutes+" (Tomorrow)" + else: + return hours+string+minutes+" (After "+str(days)+" days)" + elif days < 0: + if days == -1: + return hours+string+minutes+" (Yesterday)" + else: + return hours+string+minutes+" ("+str(abs(days))+" days ago)" + return hours+string+minutes + + + + + diff --git a/bot/Weather.py b/bot/Weather.py index 1fb786e8f4f..f06ed243564 100644 --- a/bot/Weather.py +++ b/bot/Weather.py @@ -1,61 +1,61 @@ -# -*- coding: cp1252 -*- -import urllib,xml.sax.handler -# S10 COMPATIABLE -def message(data): - if data["type"] == "PRIVMSG": - try: - splitdata = data["content"].lower().split(" ") - if splitdata[0] == ":weather" and len(splitdata) > 1: - data = Weather(" ".join(splitdata[1:])) - - data["conn"].privmsg(data["target"],"Weather for "+data[1]+": "+data[0]) - return True - except KeyError: - print "WUT" - else: - return -1 -def Weather(question): - question = question.replace("ä","a") - url = "http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml?query="+question - opener = urllib.FancyURLopener({}) - f = opener.open(url) - data = f.read() - f.close() - bufferi = [] - seen = False - for i in data.split("\n"): - if "" in i: - stuff = cutter(i,"") - if len(stuff) > 7: - bufferi.append("Temperature: "+stuff) - elif "" in i: - stuff = cutter(i,"") - if len(stuff) > 19: - bufferi.append(stuff) - elif "" in i: - stuff = cutter(i,"") - if len(stuff) > 0: - bufferi.append("Weather: "+stuff) - elif "" in i: - stuff = cutter(i,"") - if len(stuff) > 0: - bufferi.append("Humidity: "+stuff) - elif "" in i: - stuff = cutter(i,"") - if len(stuff) > 0: - bufferi.append("Wind blows "+stuff) - elif "" in i: - stuff = cutter(i,"") - if len(stuff) > 9: - bufferi.append("Air pressure is "+stuff) - elif "" in i and seen == False: - seen = True - where = cutter(i,"") - if len(where) == 4: - where = "Location doesn't exist" - return [", ".join(bufferi),where] -def cutter(fullstring,cut): - fullstring = fullstring.replace(cut,"") - fullstring = fullstring.replace(" 1: + data = Weather(" ".join(splitdata[1:])) + + data["conn"].privmsg(data["target"],"Weather for "+data[1]+": "+data[0]) + return True + except KeyError: + print "WUT" + else: + return -1 +def Weather(question): + question = question.replace("ä","a") + url = "http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml?query="+question + opener = urllib.FancyURLopener({}) + f = opener.open(url) + data = f.read() + f.close() + bufferi = [] + seen = False + for i in data.split("\n"): + if "" in i: + stuff = cutter(i,"") + if len(stuff) > 7: + bufferi.append("Temperature: "+stuff) + elif "" in i: + stuff = cutter(i,"") + if len(stuff) > 19: + bufferi.append(stuff) + elif "" in i: + stuff = cutter(i,"") + if len(stuff) > 0: + bufferi.append("Weather: "+stuff) + elif "" in i: + stuff = cutter(i,"") + if len(stuff) > 0: + bufferi.append("Humidity: "+stuff) + elif "" in i: + stuff = cutter(i,"") + if len(stuff) > 0: + bufferi.append("Wind blows "+stuff) + elif "" in i: + stuff = cutter(i,"") + if len(stuff) > 9: + bufferi.append("Air pressure is "+stuff) + elif "" in i and seen == False: + seen = True + where = cutter(i,"") + if len(where) == 4: + where = "Location doesn't exist" + return [", ".join(bufferi),where] +def cutter(fullstring,cut): + fullstring = fullstring.replace(cut,"") + fullstring = fullstring.replace(" 11: #Longer than normal, presume troll. - youtube_url.replace(cut_down,cut_down[:11]) - elif len(cut_down) < 11: #Shorter than normal - pass - except IndexError: - return "Reflex: Where's the watch?v=?" - first_two = cut_down[0:2] - try: - if no_absolute_paths: - tiedosto = open("YTCache/"+first_two+".tcc","r") - else: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","r") - except: - prev_dict = {} - else: - try: - prev_dict = pickle.load(tiedosto) - except EOFError: # Cache is corrupt - os.remove(directory+tiedosto.name) - print "REMOVED CORRUPT CACHE: "+tiedosto.name - prev_dict = {} - tiedosto.close() # I think this should belong here. - if cut_down in prev_dict.keys(): - return prev_dict[cut_down] - else: - pass - try: - if no_absolute_paths: - tiedosto = open("YTCache/"+first_two+".tcc","w") - else: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") - except IOError,error: - if len(prev_dict.keys()) > 0: - try: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case - except IOError: - if did_tell == False: - did_tell = True - return "COULD NOT ACCESS FILE "+first_two+".tcc! The next time you run this link, it checks it through the web" - Do_not_open = False - else: - did_tell = False - pickle.dump(prev_dict,tiedosto) - tiedosto.close() - else: - pass - return "Very odd error occurred: " + str(error) - youtube_url = youtube_url.replace("http//","http://") - if youtube_url.lower()[0:7] != "http://" and youtube_url[0:4] == "www.": - youtube_url = "http://" + youtube_url - if youtube_url.count("/") + youtube_url.count("\\") < 3: - if len(prev_dict.keys()) > 0: - if Do_not_open == True: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case - pickle.dump(prev_dict,tiedosto) - tiedosto.close() - else: - pass - return "Reflex: Video cannot exist" - else: - if "http://" in youtube_url[0:12].lower() and youtube_url[0:7].lower() != "http://": - youtube_url = youtube_url[youtube_url.find("http://"):] - elif youtube_url[0:7].lower() != "http://": - if len(prev_dict.keys()) > 0: - if Do_not_open == True: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case - pickle.dump(prev_dict,tiedosto) - tiedosto.close() - return "Reflex: Incorrect link start" - if "?feature=player_embedded&" in youtube_url: - youtube_url = youtube_url.replace("?feature=player_embedded&","?") - try: - website = urlopen(youtube_url) - except: - if len(prev_dict.keys()) > 0: - if Do_not_open == True: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case - pickle.dump(prev_dict,tiedosto) - tiedosto.close() - else: - pass - return "Reflex: Incorrect link!" - for i in website.readlines(): - if i.count('',contentvar)] - if "&quot;" in result: - result = result.replace("&quot;",'"') - else: - pass - if "&amp;" in result: - result = result.replace("&amp;","&") - else: - pass - if "&#39;" in result: - result = result.replace("&#39;","'") - else: - pass - if Do_not_open == True: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case - prev_dict[cut_down] = result - pickle.dump(prev_dict,tiedosto) - tiedosto.close() - return result - if Do_not_open == True: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case - prev_dict[cut_down] = "No title for video, Removed / Needs Age verification / Does not exist" - pickle.dump(prev_dict,tiedosto) - tiedosto.close() - return "No title for video, Removed / Needs age verification / Does not exist" +from urllib2 import urlopen +import os +import pickle +from CORE_DATA import directory,no_absolute_paths +global did_tell, no_absolute_paths +no_absolute_paths = True +did_tell = False +def YTCV4(youtube_url,cache=1,debug=0): + global did_tell, no_absolute_paths + Do_not_open = True + __doc__ = "Cache does not affect anything, it's legacy for skibot." + try: + cut_down = youtube_url.split("watch?v=")[1].split("&")[0] + if len(cut_down) > 11: #Longer than normal, presume troll. + youtube_url.replace(cut_down,cut_down[:11]) + elif len(cut_down) < 11: #Shorter than normal + pass + except IndexError: + return "Reflex: Where's the watch?v=?" + first_two = cut_down[0:2] + try: + if no_absolute_paths: + tiedosto = open("YTCache/"+first_two+".tcc","r") + else: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","r") + except: + prev_dict = {} + else: + try: + prev_dict = pickle.load(tiedosto) + except EOFError: # Cache is corrupt + os.remove(directory+tiedosto.name) + print "REMOVED CORRUPT CACHE: "+tiedosto.name + prev_dict = {} + tiedosto.close() # I think this should belong here. + if cut_down in prev_dict.keys(): + return prev_dict[cut_down] + else: + pass + try: + if no_absolute_paths: + tiedosto = open("YTCache/"+first_two+".tcc","w") + else: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") + except IOError,error: + if len(prev_dict.keys()) > 0: + try: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case + except IOError: + if did_tell == False: + did_tell = True + return "COULD NOT ACCESS FILE "+first_two+".tcc! The next time you run this link, it checks it through the web" + Do_not_open = False + else: + did_tell = False + pickle.dump(prev_dict,tiedosto) + tiedosto.close() + else: + pass + return "Very odd error occurred: " + str(error) + youtube_url = youtube_url.replace("http//","http://") + if youtube_url.lower()[0:7] != "http://" and youtube_url[0:4] == "www.": + youtube_url = "http://" + youtube_url + if youtube_url.count("/") + youtube_url.count("\\") < 3: + if len(prev_dict.keys()) > 0: + if Do_not_open == True: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case + pickle.dump(prev_dict,tiedosto) + tiedosto.close() + else: + pass + return "Reflex: Video cannot exist" + else: + if "http://" in youtube_url[0:12].lower() and youtube_url[0:7].lower() != "http://": + youtube_url = youtube_url[youtube_url.find("http://"):] + elif youtube_url[0:7].lower() != "http://": + if len(prev_dict.keys()) > 0: + if Do_not_open == True: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case + pickle.dump(prev_dict,tiedosto) + tiedosto.close() + return "Reflex: Incorrect link start" + if "?feature=player_embedded&" in youtube_url: + youtube_url = youtube_url.replace("?feature=player_embedded&","?") + try: + website = urlopen(youtube_url) + except: + if len(prev_dict.keys()) > 0: + if Do_not_open == True: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case + pickle.dump(prev_dict,tiedosto) + tiedosto.close() + else: + pass + return "Reflex: Incorrect link!" + for i in website.readlines(): + if i.count('',contentvar)] + if "&quot;" in result: + result = result.replace("&quot;",'"') + else: + pass + if "&amp;" in result: + result = result.replace("&amp;","&") + else: + pass + if "&#39;" in result: + result = result.replace("&#39;","'") + else: + pass + if Do_not_open == True: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case + prev_dict[cut_down] = result + pickle.dump(prev_dict,tiedosto) + tiedosto.close() + return result + if Do_not_open == True: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case + prev_dict[cut_down] = "No title for video, Removed / Needs Age verification / Does not exist" + pickle.dump(prev_dict,tiedosto) + tiedosto.close() + return "No title for video, Removed / Needs age verification / Does not exist" diff --git a/bot/_____Readme.txt b/bot/_____Readme.txt index 1c56f9ad128..acbf66af7db 100644 --- a/bot/_____Readme.txt +++ b/bot/_____Readme.txt @@ -1,74 +1,74 @@ -/// Adminhelp relay IRC bot setup guide -/// CC_Nanotrasen bot created by Skibiliano and distributed under the CC-BY-SA 3.0 license -/// All derivative works of this bot must properly credit Skibiliano as the original author -/// Big thanks to Skibiliano his bot and allowing distribution, and to BS12 for sharing their code for making use of it ingame - -QUESTION: What does this bot do? -ANSWER: It, in conjunction with BYOND, relays adminhelps to a designated channel, along with various extra functions that can be accessed by saying !help in the same channel/in a query with the bot. - -Some basic info before you set this up: -CC_Nanotrasen is coded in python 2.6 and requires a serverside installation of python 2.6 (obtainable at http://www.python.org/getit/releases/2.6/) -- Python MUST BE installed to the same directory as the .dmb you are using to host your server/server config folder -- CC_Nanotrasen supports, but does not require, Psyco (obtainable at http://psyco.sourceforge.net/download.html) which increases the speed 20-30% and slightly increases RAM usage - -Now that that's out of the way, I'll teach you how to set this up. - -BOT CONFIG: -Move everything in this folder (this file noninclusive) to the same folder as the hosting server (where your .dmb, config folder, and python are installed) -Open CORE_DATA.py with a text editor of your choice (recommended to be notepad++ or notepad) -You should see 14 lines of code which look like - Name = "CC_NanoTrasen" #The name he uses to connect - no_absolute_paths = True #Do not change this. - debug_on = False - SName = ["cc","nt","trasen","nano","nanotrasen"] #Other names he will respond to, must be lowercase - DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS = False - directory = "BOT DIRECTORY GOES HERE/" #make sure to keep the "/" at the end - version = "TG CC-BY-SA 6" - Network = 'irc.server.goes.here' #e.g. "irc.rizon.net" - channel = "#CHANNEL GOES HERE" #what channel you want the bot in - channels = ["#CHANNEL GOES HERE","#ALSO ANOTHER CHANNEL GOES HERE IF YOU WANT"] #same as above - greeting = "Welcome!" #what he says when a person he hasn't seen before joins - prefix = "!" #prefix for bot commands - Port = 7000 -There are some basic comments besides every important config option in here, but I'll summarize them in detail -NAME - The name the bot assumes when it connects to IRC, so in this example it would join the IRC under the nickname "CC_Nanotrasen" -SNAME - A list of secondary names, with commas, that the bot will respond to for commands (for example, this setup will allow the bot to respond to "nt, tell quarxink he's a terrible writer") -DIRECTORY - The directory of the bot files, dmb, python, and config folder IN FORWARD SLASHES, WITH FORWARD SLASH AT THE END(for example, I host my test server from "c:\tgstation\tgstation13\tgstation.dmb" so for me the line would say directory = "c:/tgstation/tgstation13/") -NETWORK - The IRC server it will connect to, such as "irc.rizon.net" -CHANNEL/CHANNELS - what channel the bot will join (channels allows for multiple channel connections, in the same formatting as SName separates nicknames) -GREETING - CC_Nanotrasen will store the names of people it has seen before, but when a nickname joins that it hasn't seen before it will greet that person with whatever message is put in this -PREFIX = What character/string will be placed before commands for the bot (so if you changed this to "$", you would pull up the bot's help menu by saying $help instead of !help) -PORT - What port to connect to for the IRC server (if you are unsure of what port to use, most IRC clients will show you what port you are connecting to) - -Once you have that ready, you're on to step two. -Open up the config folder in your install dir, and open config.txt -Scroll to the bottom, right below #FORBID_SINGULO_POSSESSION should be - ##Remove the # mark if you are going to use the SVN irc bot to relay adminhelps - #USEIRCBOT -Just remove the "#" in front of USEIRCBOT (you don't even have to recompile your DMB! - -Got that all ready to go? Good, it's time for step three. -Open Dream Daemon (that thing you use when you host) -On the bottom of the window you should see port, security, and visibility. -Change security to "Trusted" - -Congratulations, you've set up this bot! -A few things to note as far as features: -Use !help to list most commands for the bot. -You can leave notes for other users! Just say "[bot name], tell [other user's name] [message]" - So let's say you wonder if I'm going to jump in to your IRC ever and you want to tell me this readme was horrible, you would say "Nano, tell Quarxink Your readme was horrible" - -TROUBLESHOOTING: -Attempting to run the bot gives me an error about encoding.utf-8. - You've probably installed python to a separate folder than the bot/server, move python's files over and it should run fine - -It's telling me connection refused when someone adminhelps. - You've moved the bot to a separate folder from the nudge script, most likely. - -BYOND asks me on any restart if I want to allow nudge.py to run. - Set security to trusted in Dream Daemon - - - - +/// Adminhelp relay IRC bot setup guide +/// CC_Nanotrasen bot created by Skibiliano and distributed under the CC-BY-SA 3.0 license +/// All derivative works of this bot must properly credit Skibiliano as the original author +/// Big thanks to Skibiliano his bot and allowing distribution, and to BS12 for sharing their code for making use of it ingame + +QUESTION: What does this bot do? +ANSWER: It, in conjunction with BYOND, relays adminhelps to a designated channel, along with various extra functions that can be accessed by saying !help in the same channel/in a query with the bot. + +Some basic info before you set this up: +CC_Nanotrasen is coded in python 2.6 and requires a serverside installation of python 2.6 (obtainable at http://www.python.org/getit/releases/2.6/) +- Python MUST BE installed to the same directory as the .dmb you are using to host your server/server config folder +- CC_Nanotrasen supports, but does not require, Psyco (obtainable at http://psyco.sourceforge.net/download.html) which increases the speed 20-30% and slightly increases RAM usage + +Now that that's out of the way, I'll teach you how to set this up. + +BOT CONFIG: +Move everything in this folder (this file noninclusive) to the same folder as the hosting server (where your .dmb, config folder, and python are installed) +Open CORE_DATA.py with a text editor of your choice (recommended to be notepad++ or notepad) +You should see 14 lines of code which look like + Name = "CC_NanoTrasen" #The name he uses to connect + no_absolute_paths = True #Do not change this. + debug_on = False + SName = ["cc","nt","trasen","nano","nanotrasen"] #Other names he will respond to, must be lowercase + DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS = False + directory = "BOT DIRECTORY GOES HERE/" #make sure to keep the "/" at the end + version = "TG CC-BY-SA 6" + Network = 'irc.server.goes.here' #e.g. "irc.rizon.net" + channel = "#CHANNEL GOES HERE" #what channel you want the bot in + channels = ["#CHANNEL GOES HERE","#ALSO ANOTHER CHANNEL GOES HERE IF YOU WANT"] #same as above + greeting = "Welcome!" #what he says when a person he hasn't seen before joins + prefix = "!" #prefix for bot commands + Port = 7000 +There are some basic comments besides every important config option in here, but I'll summarize them in detail +NAME - The name the bot assumes when it connects to IRC, so in this example it would join the IRC under the nickname "CC_Nanotrasen" +SNAME - A list of secondary names, with commas, that the bot will respond to for commands (for example, this setup will allow the bot to respond to "nt, tell quarxink he's a terrible writer") +DIRECTORY - The directory of the bot files, dmb, python, and config folder IN FORWARD SLASHES, WITH FORWARD SLASH AT THE END(for example, I host my test server from "c:\tgstation\tgstation13\tgstation.dmb" so for me the line would say directory = "c:/tgstation/tgstation13/") +NETWORK - The IRC server it will connect to, such as "irc.rizon.net" +CHANNEL/CHANNELS - what channel the bot will join (channels allows for multiple channel connections, in the same formatting as SName separates nicknames) +GREETING - CC_Nanotrasen will store the names of people it has seen before, but when a nickname joins that it hasn't seen before it will greet that person with whatever message is put in this +PREFIX = What character/string will be placed before commands for the bot (so if you changed this to "$", you would pull up the bot's help menu by saying $help instead of !help) +PORT - What port to connect to for the IRC server (if you are unsure of what port to use, most IRC clients will show you what port you are connecting to) + +Once you have that ready, you're on to step two. +Open up the config folder in your install dir, and open config.txt +Scroll to the bottom, right below #FORBID_SINGULO_POSSESSION should be + ##Remove the # mark if you are going to use the SVN irc bot to relay adminhelps + #USEIRCBOT +Just remove the "#" in front of USEIRCBOT (you don't even have to recompile your DMB! + +Got that all ready to go? Good, it's time for step three. +Open Dream Daemon (that thing you use when you host) +On the bottom of the window you should see port, security, and visibility. +Change security to "Trusted" + +Congratulations, you've set up this bot! +A few things to note as far as features: +Use !help to list most commands for the bot. +You can leave notes for other users! Just say "[bot name], tell [other user's name] [message]" + So let's say you wonder if I'm going to jump in to your IRC ever and you want to tell me this readme was horrible, you would say "Nano, tell Quarxink Your readme was horrible" + +TROUBLESHOOTING: +Attempting to run the bot gives me an error about encoding.utf-8. + You've probably installed python to a separate folder than the bot/server, move python's files over and it should run fine + +It's telling me connection refused when someone adminhelps. + You've moved the bot to a separate folder from the nudge script, most likely. + +BYOND asks me on any restart if I want to allow nudge.py to run. + Set security to trusted in Dream Daemon + + + + If you have any requests, suggestions, or issues not covered by this guide, I can be contacted as Quarxink at #coderbus on irc.rizon.net (If I don't respond, leave me a query with your problem and how to reach you [preferably an email address, steam, other irc channel, or aim/msn]) \ No newline at end of file diff --git a/bot/gen_fml.py b/bot/gen_fml.py index 358fac8700e..f523a4add83 100644 --- a/bot/gen_fml.py +++ b/bot/gen_fml.py @@ -1,15 +1,15 @@ -from FMLformatter import formatter -from urllib2 import urlopen -try: - from hashlib import md5 -except: - from md5 import md5 -from save_load import save,load -import CORE_DATA -directory = CORE_DATA.directory -FML = urlopen("http://www.fmylife.com/random") -formatted = formatter(FML.read().split("\n")) -for Quote in formatted: - exact = Quote[:Quote.find("#")] -# print exact - save(directory+"fmlquotes/"+md5(exact).hexdigest()+".txt",exact) +from FMLformatter import formatter +from urllib2 import urlopen +try: + from hashlib import md5 +except: + from md5 import md5 +from save_load import save,load +import CORE_DATA +directory = CORE_DATA.directory +FML = urlopen("http://www.fmylife.com/random") +formatted = formatter(FML.read().split("\n")) +for Quote in formatted: + exact = Quote[:Quote.find("#")] +# print exact + save(directory+"fmlquotes/"+md5(exact).hexdigest()+".txt",exact) diff --git a/bot/htmltagremove.py b/bot/htmltagremove.py index c9d13086d51..4ecd9b6cd02 100644 --- a/bot/htmltagremove.py +++ b/bot/htmltagremove.py @@ -1,28 +1,28 @@ -def htr(data): - ignore = False - if type(data) == list: - b = [] - for olio in data: - tempolio = "" - for letter in olio: - if letter == "<": - ignore = True - else: - pass - if ignore != True: - tempolio += letter - else: - pass - if letter == ">": - ignore = False - else: - pass - tempolio = tempolio.replace("\t","") - if len(tempolio) == 0: - pass - elif len(tempolio.replace(" ","")) == 0: - pass - else: - b.append(tempolio) - #Finetuning - return b +def htr(data): + ignore = False + if type(data) == list: + b = [] + for olio in data: + tempolio = "" + for letter in olio: + if letter == "<": + ignore = True + else: + pass + if ignore != True: + tempolio += letter + else: + pass + if letter == ">": + ignore = False + else: + pass + tempolio = tempolio.replace("\t","") + if len(tempolio) == 0: + pass + elif len(tempolio.replace(" ","")) == 0: + pass + else: + b.append(tempolio) + #Finetuning + return b diff --git a/bot/irchat.py b/bot/irchat.py index 46ae2d4caf7..467d96e8b85 100644 --- a/bot/irchat.py +++ b/bot/irchat.py @@ -1,94 +1,94 @@ -import socket -import time -class IRC: - queue = [] - partial = '' - def __init__ ( self, network, port, name, hostName, serverName, realName ): - self.network = network - self.port = port - self.hostName = hostName - self.serverName = serverName - self.realName = realName - self.socket = socket.socket ( socket.AF_INET, socket.SOCK_STREAM ) - self.socket.connect ( ( self.network, self.port ) ) - self.address = self.socket.getpeername() - self.nick ( name ) - self.send ( 'USER ' + self.name + ' ' + self.serverName + ' ' + self.hostName + ' :' + self.realName ) - def quit ( self ): - self.send ( 'QUIT' ) - self.socket.close() - def send ( self, text ): - count = 0 - try: - count += 1 - self.socket.send ( text + '\r\n' ) - except: - if count > 10: - time.sleep(1) - self.socket.send(text+'\r\n') - else: - count = 0 - def nick ( self, name ): - self.name = name - self.send ( 'NICK ' + self.name ) - def addressquery(self): - print self.address - aha = socket.gethostbyaddr(str(self.address[0])) - return aha - def recv ( self, size = 2048 ): - commands = self.socket.recv ( size ).split ( '\r\n' ) - if len ( self.partial ): - commands [ 0 ] = self.partial + commands [ 0 ] - self.partial = '' - if len ( commands [ -1 ] ): - self.partial = commands [ -1 ] - self.queue.extend ( commands [ :-1 ] ) - else: - self.queue.extend ( commands ) - def retrieve ( self ): - if len ( self.queue ): - command = self.queue [ 0 ] - self.queue.pop ( 0 ) - return command - else: - return False - def dismantle ( self, command ): - if command: - source = command.split ( ':' ) [ 1 ].split ( ' ' ) [ 0 ] - parameters = command.split ( ':' ) [ 1 ].split ( ' ' ) [ 1: ] - if len(parameters) > 0: - if not len ( parameters [ -1 ] ): - parameters.pop() - if command.count ( ':' ) > 1: - parameters.append(command[command.find(":",command.find(":")+1)+1:]) - return source, parameters - def privmsg ( self, destination, message ): - self.send ( 'PRIVMSG ' + destination + ' :' + message ) - def handshake(self,hexstring): - self.send("PONG :"+hexstring) - def notice ( self, destination, message ): - self.send ( 'NOTICE ' + destination + ' :' + message ) - def join ( self, channel ): - self.send ( 'JOIN ' + channel ) - def part ( self, channel ): - self.send ( 'PART ' + channel ) - def topic ( self, channel, topic = '' ): - self.send ( 'TOPIC ' + channel + ' ' + topic ) - def names ( self, channel ): - self.send ( 'NAMES ' + channel ) - def invite ( self, nick, channel ): - self.send ( 'INVITE ' + nick + ' ' + channel ) - def mode ( self, channel, mode, nick = '' ): - self.send ( 'MODE ' + channel + ' ' + mode + ' ' + nick ) - def banon(self,channel,name): - self.mode(channel,"+b",name) - def banoff(self,channel,name): - self.mode(channel,"-b",name) - def kick ( self, channel, nick, reason = '' ): - self.send ( 'KICK ' + channel + ' ' + nick + ' ' + reason ) - def who ( self, pattern ): - self.send ( 'WHO ' + pattern ) - def whois ( self, nick ): - self.send ( 'WHOIS ' + nick ) - def whowas ( self, nick ): - self.send ( 'WHOWAS ' + nick ) +import socket +import time +class IRC: + queue = [] + partial = '' + def __init__ ( self, network, port, name, hostName, serverName, realName ): + self.network = network + self.port = port + self.hostName = hostName + self.serverName = serverName + self.realName = realName + self.socket = socket.socket ( socket.AF_INET, socket.SOCK_STREAM ) + self.socket.connect ( ( self.network, self.port ) ) + self.address = self.socket.getpeername() + self.nick ( name ) + self.send ( 'USER ' + self.name + ' ' + self.serverName + ' ' + self.hostName + ' :' + self.realName ) + def quit ( self ): + self.send ( 'QUIT' ) + self.socket.close() + def send ( self, text ): + count = 0 + try: + count += 1 + self.socket.send ( text + '\r\n' ) + except: + if count > 10: + time.sleep(1) + self.socket.send(text+'\r\n') + else: + count = 0 + def nick ( self, name ): + self.name = name + self.send ( 'NICK ' + self.name ) + def addressquery(self): + print self.address + aha = socket.gethostbyaddr(str(self.address[0])) + return aha + def recv ( self, size = 2048 ): + commands = self.socket.recv ( size ).split ( '\r\n' ) + if len ( self.partial ): + commands [ 0 ] = self.partial + commands [ 0 ] + self.partial = '' + if len ( commands [ -1 ] ): + self.partial = commands [ -1 ] + self.queue.extend ( commands [ :-1 ] ) + else: + self.queue.extend ( commands ) + def retrieve ( self ): + if len ( self.queue ): + command = self.queue [ 0 ] + self.queue.pop ( 0 ) + return command + else: + return False + def dismantle ( self, command ): + if command: + source = command.split ( ':' ) [ 1 ].split ( ' ' ) [ 0 ] + parameters = command.split ( ':' ) [ 1 ].split ( ' ' ) [ 1: ] + if len(parameters) > 0: + if not len ( parameters [ -1 ] ): + parameters.pop() + if command.count ( ':' ) > 1: + parameters.append(command[command.find(":",command.find(":")+1)+1:]) + return source, parameters + def privmsg ( self, destination, message ): + self.send ( 'PRIVMSG ' + destination + ' :' + message ) + def handshake(self,hexstring): + self.send("PONG :"+hexstring) + def notice ( self, destination, message ): + self.send ( 'NOTICE ' + destination + ' :' + message ) + def join ( self, channel ): + self.send ( 'JOIN ' + channel ) + def part ( self, channel ): + self.send ( 'PART ' + channel ) + def topic ( self, channel, topic = '' ): + self.send ( 'TOPIC ' + channel + ' ' + topic ) + def names ( self, channel ): + self.send ( 'NAMES ' + channel ) + def invite ( self, nick, channel ): + self.send ( 'INVITE ' + nick + ' ' + channel ) + def mode ( self, channel, mode, nick = '' ): + self.send ( 'MODE ' + channel + ' ' + mode + ' ' + nick ) + def banon(self,channel,name): + self.mode(channel,"+b",name) + def banoff(self,channel,name): + self.mode(channel,"-b",name) + def kick ( self, channel, nick, reason = '' ): + self.send ( 'KICK ' + channel + ' ' + nick + ' ' + reason ) + def who ( self, pattern ): + self.send ( 'WHO ' + pattern ) + def whois ( self, nick ): + self.send ( 'WHOIS ' + nick ) + def whowas ( self, nick ): + self.send ( 'WHOWAS ' + nick ) diff --git a/bot/linereader.py b/bot/linereader.py index cef804935b8..469b6e1444b 100644 --- a/bot/linereader.py +++ b/bot/linereader.py @@ -1,43 +1,43 @@ -import os -directory = "" -raw = os.listdir(directory) -extract = [] -for i in raw: - if i[-3:] == ".py": - extract.append(i) -toc = 0 -toc3 = 0 -toc2 = 0 -print len(extract),"Files" -lista = [] -for ob in extract: - count3 = 0 - if directory == "": - tiedosto = open(ob,"r") - tiedosto2 = open(ob,"r") - count3 += os.path.getsize(ob) - toc3 += count3 - else: - tiedosto = open(directory+"/"+ob,"r") - tiedosto2 = open(directory+"/"+ob,"r") - count3 += os.path.getsize(directory+"/"+ob) - toc3 += count3 - count = 0 - count2 = 0 - line = tiedosto.readline() - while line != "": - count += 1 - toc += 1 - line = tiedosto.readline() - count2 += len(tiedosto2.read()) - toc2 += count2 - lista.append([count,count2,ob,count3]) - tiedosto.close() - tiedosto2.close() -print toc,"Lines in total" -print toc2,"Letters in total" -print toc3,"Bytes in total" - -for linecount, lettercount, filename, bytecount in lista: - print str(linecount)+" Lines (%s%%) || "%(str(round((float(linecount)/toc)*100,1))),str(lettercount)+" Letters (%s%%) in file " %(str(round((float(lettercount)/toc2)*100,1)))+filename - print str(bytecount) + " Bytes (%s%%) "%(str(round((float(bytecount)/toc3)*100,1))) +import os +directory = "" +raw = os.listdir(directory) +extract = [] +for i in raw: + if i[-3:] == ".py": + extract.append(i) +toc = 0 +toc3 = 0 +toc2 = 0 +print len(extract),"Files" +lista = [] +for ob in extract: + count3 = 0 + if directory == "": + tiedosto = open(ob,"r") + tiedosto2 = open(ob,"r") + count3 += os.path.getsize(ob) + toc3 += count3 + else: + tiedosto = open(directory+"/"+ob,"r") + tiedosto2 = open(directory+"/"+ob,"r") + count3 += os.path.getsize(directory+"/"+ob) + toc3 += count3 + count = 0 + count2 = 0 + line = tiedosto.readline() + while line != "": + count += 1 + toc += 1 + line = tiedosto.readline() + count2 += len(tiedosto2.read()) + toc2 += count2 + lista.append([count,count2,ob,count3]) + tiedosto.close() + tiedosto2.close() +print toc,"Lines in total" +print toc2,"Letters in total" +print toc3,"Bytes in total" + +for linecount, lettercount, filename, bytecount in lista: + print str(linecount)+" Lines (%s%%) || "%(str(round((float(linecount)/toc)*100,1))),str(lettercount)+" Letters (%s%%) in file " %(str(round((float(lettercount)/toc2)*100,1)))+filename + print str(bytecount) + " Bytes (%s%%) "%(str(round((float(bytecount)/toc3)*100,1))) diff --git a/bot/nudge.py b/bot/nudge.py index e39ebc314c5..8c20c4c735b 100644 --- a/bot/nudge.py +++ b/bot/nudge.py @@ -1,39 +1,39 @@ -import sys,pickle,socket, CORE_DATA -#def pack(): -# path = "/home/ski/Nanotrasen/message.txt" -# ip = sys.argv[1] -# dictionary = {"ip":ip,"data":1} -# try: -# targetfile = open(path,"r") -# except IOError: -# targetfile = open(path,"w") -# pickle.dump(dictionary,targetfile) -# targetfile.close() -# nudge() -# else: -# targetfile.close() #Professionals, have standards. -# pass -def pack(): - ip = sys.argv[1] - try: - data = sys.argv[2:] #The rest of the arguments is data - except: - data = "NO DATA SPECIFIED" - dictionary = {"ip":ip,"data":data} - pickled = pickle.dumps(dictionary) - nudge(pickled) -def nudge(data): - if CORE_DATA.DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS: - pass - else: - HOST = "localhost" - PORT = 45678 - size = 1024 - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect((HOST,PORT)) - s.send(data) - s.close() - -if __name__ == "__main__" and len(sys.argv) > 1: # If not imported and more than one argument - pack() - +import sys,pickle,socket, CORE_DATA +#def pack(): +# path = "/home/ski/Nanotrasen/message.txt" +# ip = sys.argv[1] +# dictionary = {"ip":ip,"data":1} +# try: +# targetfile = open(path,"r") +# except IOError: +# targetfile = open(path,"w") +# pickle.dump(dictionary,targetfile) +# targetfile.close() +# nudge() +# else: +# targetfile.close() #Professionals, have standards. +# pass +def pack(): + ip = sys.argv[1] + try: + data = sys.argv[2:] #The rest of the arguments is data + except: + data = "NO DATA SPECIFIED" + dictionary = {"ip":ip,"data":data} + pickled = pickle.dumps(dictionary) + nudge(pickled) +def nudge(data): + if CORE_DATA.DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS: + pass + else: + HOST = "localhost" + PORT = 45678 + size = 1024 + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((HOST,PORT)) + s.send(data) + s.close() + +if __name__ == "__main__" and len(sys.argv) > 1: # If not imported and more than one argument + pack() + diff --git a/bot/save_load.py b/bot/save_load.py index 34eac990726..1cd058763a8 100644 --- a/bot/save_load.py +++ b/bot/save_load.py @@ -1,24 +1,24 @@ -import pickle -def save(filename,data,dnrw=0): - if dnrw == 1: - try: - tiedosto = open(filename,"r") - except: - tiedosto = open(filename,"w") - else: - return False - else: - tiedosto = open(filename,"w") - - if "http//" in data: - data = data.replace("http//","http://") - pickle.dump(data,tiedosto) - tiedosto.close() -def load(filename): - try: - tiedosto = open(filename,"r") - except IOError: - return "ERROR ERROR ERROR ERR" - a = pickle.load(tiedosto) - tiedosto.close() - return a +import pickle +def save(filename,data,dnrw=0): + if dnrw == 1: + try: + tiedosto = open(filename,"r") + except: + tiedosto = open(filename,"w") + else: + return False + else: + tiedosto = open(filename,"w") + + if "http//" in data: + data = data.replace("http//","http://") + pickle.dump(data,tiedosto) + tiedosto.close() +def load(filename): + try: + tiedosto = open(filename,"r") + except IOError: + return "ERROR ERROR ERROR ERR" + a = pickle.load(tiedosto) + tiedosto.close() + return a diff --git a/bot/some_but_not_all_2.py b/bot/some_but_not_all_2.py index 211eef9fd52..34627abd129 100644 --- a/bot/some_but_not_all_2.py +++ b/bot/some_but_not_all_2.py @@ -1,20 +1,20 @@ -def sbna2(only_one,one_of_these,data): - if type(only_one) != list: - only_one = list(only_one) - if type(data) != list: - data = data.split(" ") - count = 0 - for datoid in only_one: - if datoid in data and count >= 1: - return False - elif datoid in data: - count += 1 - pass - else: - pass - if count == 0: - return False - for datoid in one_of_these: - if datoid in data: - return True - return False +def sbna2(only_one,one_of_these,data): + if type(only_one) != list: + only_one = list(only_one) + if type(data) != list: + data = data.split(" ") + count = 0 + for datoid in only_one: + if datoid in data and count >= 1: + return False + elif datoid in data: + count += 1 + pass + else: + pass + if count == 0: + return False + for datoid in one_of_these: + if datoid in data: + return True + return False diff --git a/bot/xkcdparser.py b/bot/xkcdparser.py index 24d6f514165..7ad033d0b82 100644 --- a/bot/xkcdparser.py +++ b/bot/xkcdparser.py @@ -1,40 +1,40 @@ -from urllib2 import urlopen -from json import loads -from pickle import dump,load -from CORE_DATA import no_absolute_paths -def xkcd(link): - try: - filename = link[link.find("xkcd.com")+9:].replace("/","").replace("\\","") - if no_absolute_paths: - tiedosto = open("xkcdcache/"+filename,"r") - else: - tiedosto = open(directory+"xkcdcache/"+filename,"r") - except: - try: - if no_absolute_paths: - tiedosto = open("xkcdcache/"+filename,"w") - else: - tiedosto = open(directory+"xkcdcache/"+filename,"w") - except IOError: - return "NOTHING" - else: - try: - return load(tiedosto) - except EOFError: - tiedosto = open("xkcdcache/"+filename,"w") - pass #Corrupt cache, moving on. - if link[-1] == "/" or link[-1] == "\\": #Ending is fine. - link += "info.0.json" - else: - link += "/info.0.json" - try: - data = urlopen(link).read() - except: - return "NOTHING" - try: - newdata = loads(data)["title"] - dump(newdata,tiedosto) - return newdata - except: - return "NOTHING" - +from urllib2 import urlopen +from json import loads +from pickle import dump,load +from CORE_DATA import no_absolute_paths +def xkcd(link): + try: + filename = link[link.find("xkcd.com")+9:].replace("/","").replace("\\","") + if no_absolute_paths: + tiedosto = open("xkcdcache/"+filename,"r") + else: + tiedosto = open(directory+"xkcdcache/"+filename,"r") + except: + try: + if no_absolute_paths: + tiedosto = open("xkcdcache/"+filename,"w") + else: + tiedosto = open(directory+"xkcdcache/"+filename,"w") + except IOError: + return "NOTHING" + else: + try: + return load(tiedosto) + except EOFError: + tiedosto = open("xkcdcache/"+filename,"w") + pass #Corrupt cache, moving on. + if link[-1] == "/" or link[-1] == "\\": #Ending is fine. + link += "info.0.json" + else: + link += "/info.0.json" + try: + data = urlopen(link).read() + except: + return "NOTHING" + try: + newdata = loads(data)["title"] + dump(newdata,tiedosto) + return newdata + except: + return "NOTHING" + diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm index a5e15639723..e5765075c25 100644 --- a/code/ATMOSPHERICS/atmospherics.dm +++ b/code/ATMOSPHERICS/atmospherics.dm @@ -1,231 +1,231 @@ -/* -Quick overview: - -Pipes combine to form pipelines -Pipelines and other atmospheric objects combine to form pipe_networks - Note: A single pipe_network represents a completely open space - -Pipes -> Pipelines -Pipelines + Other Objects -> Pipe network - -*/ -/obj/machinery/atmospherics - anchored = TRUE - idle_power_usage = 0 - active_power_usage = 0 - power_channel = ENVIRON - var/nodealert = 0 - var/power_rating //the maximum amount of power the machine can use to do work, affects how powerful the machine is, in Watts - - unacidable = TRUE - layer = ATMOS_LAYER - plane = PLATING_PLANE - - var/pipe_flags = PIPING_DEFAULT_LAYER_ONLY // Allow other layers by exception basis. - var/connect_types = CONNECT_TYPE_REGULAR - var/piping_layer = PIPING_LAYER_DEFAULT // This will replace icon_connect_type at some point ~Leshana - var/icon_connect_type = "" //"-supply" or "-scrubbers" - var/construction_type = null // Type path of the pipe item when this is deconstructed. - var/pipe_state // icon_state as a pipe item - - var/initialize_directions = 0 - var/pipe_color - - var/global/datum/pipe_icon_manager/icon_manager - var/obj/machinery/atmospherics/node1 - var/obj/machinery/atmospherics/node2 - -/obj/machinery/atmospherics/New(loc, newdir) - ..() - if(!icon_manager) - icon_manager = new() - if(!isnull(newdir)) - set_dir(newdir) - if(!pipe_color) - pipe_color = color - color = null - - if(!pipe_color_check(pipe_color)) - pipe_color = null - init_dir() - -/obj/machinery/atmospherics/examine_icon() - return icon(icon=initial(icon),icon_state=initial(icon_state)) - -// This is used to set up what directions pipes will connect to. Should be called inside New() and whenever a dir changes. -/obj/machinery/atmospherics/proc/init_dir() - return - -// Get ALL initialize_directions - Some types (HE pipes etc) combine two vars together for this. -/obj/machinery/atmospherics/proc/get_init_dirs() - return initialize_directions - -// Get the direction each node is facing to connect. -// It now returns as a list so it can be fetched nicely, each entry corresponds to node of same number. -/obj/machinery/atmospherics/proc/get_node_connect_dirs() - return - -// Initializes nodes by looking at neighboring atmospherics machinery to connect to. -// When we're being constructed at runtime, atmos_init() is called by the construction code. -// When dynamically loading a map atmos_init is called by the maploader (initTemplateBounds proc) -// But during initial world creation its called by the master_controller. -// TODO - Consolidate these different ways of being called once SSatoms is created. -/obj/machinery/atmospherics/proc/atmos_init() - return - -/** Check if target is an acceptable target to connect as a node from this machine. */ -/obj/machinery/atmospherics/proc/can_be_node(obj/machinery/atmospherics/target, node_num) - return (target.initialize_directions & get_dir(target,src)) && check_connectable(target) && target.check_connectable(src) - -/** Check if this machine is willing to connect with the target machine. */ -/obj/machinery/atmospherics/proc/check_connectable(obj/machinery/atmospherics/target) - return (src.connect_types & target.connect_types) - -/obj/machinery/atmospherics/attackby(atom/A, mob/user as mob) - if(istype(A, /obj/item/device/pipe_painter)) - return - ..() - -/obj/machinery/atmospherics/proc/add_underlay(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) - if(node) - if(!T.is_plating() && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) - //underlays += icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node)) - underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) - else - //underlays += icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node)) - underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) - else - //underlays += icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color) - underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type) - -/obj/machinery/atmospherics/proc/update_underlays() - if(check_icon_cache()) - return 1 - else - return 0 - -/obj/machinery/atmospherics/proc/check_icon_cache(var/safety = 0) - if(!istype(icon_manager)) - if(!safety) //to prevent infinite loops - icon_manager = new() - check_icon_cache(1) - return 0 - - return 1 - -/obj/machinery/atmospherics/proc/color_cache_name(var/obj/machinery/atmospherics/node) - //Don't use this for standard pipes - if(!istype(node)) - return null - - return node.pipe_color - -/obj/machinery/atmospherics/process() - last_flow_rate = 0 - last_power_draw = 0 - - build_network() - -/obj/machinery/atmospherics/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - // Check to see if should be added to network. Add self if so and adjust variables appropriately. - // Note don't forget to have neighbors look as well! - - return null - -/obj/machinery/atmospherics/proc/build_network() - // Called to build a network from this node - - return null - -/obj/machinery/atmospherics/proc/return_network(obj/machinery/atmospherics/reference) - // Returns pipe_network associated with connection to reference - // Notes: should create network if necessary - // Should never return null - - return null - -/obj/machinery/atmospherics/proc/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) - // Used when two pipe_networks are combining - -/obj/machinery/atmospherics/proc/return_network_air(datum/pipe_network/reference) - // Return a list of gas_mixture(s) in the object - // associated with reference pipe_network for use in rebuilding the networks gases list - // Is permitted to return null - -/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference) - -/obj/machinery/atmospherics/update_icon() - return null - -/obj/machinery/atmospherics/proc/can_unwrench() - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - if((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE) - return 0 - return 1 - -// Deconstruct into a pipe item. -/obj/machinery/atmospherics/proc/deconstruct() - if(QDELETED(src)) - return - if(construction_type) - var/obj/item/pipe/I = new construction_type(loc, null, null, src) - I.setPipingLayer(piping_layer) - transfer_fingerprints_to(I) - qdel(src) - -// Return a list of nodes which we should call atmos_init() and build_network() during on_construction() -/obj/machinery/atmospherics/proc/get_neighbor_nodes_for_init() - return null - -// Called on construction (i.e from pipe item) but not on initialization -/obj/machinery/atmospherics/proc/on_construction(obj_color, set_layer) - pipe_color = obj_color - setPipingLayer(set_layer) - // TODO - M.connect_types = src.connect_types - Or otherwise copy from item? Or figure it out from piping layer? - var/turf/T = get_turf(src) - level = !T.is_plating() ? 2 : 1 - atmos_init() - if(QDELETED(src)) - return // TODO - Eventually should get rid of the need for this. - build_network() - var/list/nodes = get_neighbor_nodes_for_init() - for(var/obj/machinery/atmospherics/A in nodes) - A.atmos_init() - A.build_network() - // TODO - Should we do src.build_network() before or after the nodes? - // We've historically done before, but /tg does after. TODO research if there is a difference. - -// This sets our piping layer. Hopefully its cool. -/obj/machinery/atmospherics/proc/setPipingLayer(new_layer) - if(pipe_flags & (PIPING_DEFAULT_LAYER_ONLY|PIPING_ALL_LAYER)) - new_layer = PIPING_LAYER_DEFAULT - piping_layer = new_layer - // Do it the Polaris way - switch(piping_layer) - if(PIPING_LAYER_SCRUBBER) - icon_state = "[icon_state]-scrubbers" - connect_types = CONNECT_TYPE_SCRUBBER - layer = PIPES_SCRUBBER_LAYER - icon_connect_type = "-scrubbers" - if(PIPING_LAYER_SUPPLY) - icon_state = "[icon_state]-supply" - connect_types = CONNECT_TYPE_SUPPLY - layer = PIPES_SUPPLY_LAYER - icon_connect_type = "-supply" - if(PIPING_LAYER_FUEL) - icon_state = "[icon_state]-fuel" - connect_types = CONNECT_TYPE_FUEL - layer = PIPES_FUEL_LAYER - icon_connect_type = "-fuel" - if(PIPING_LAYER_AUX) - icon_state = "[icon_state]-aux" - connect_types = CONNECT_TYPE_AUX - layer = PIPES_AUX_LAYER - icon_connect_type = "-aux" - if(pipe_flags & PIPING_ALL_LAYER) - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER|CONNECT_TYPE_FUEL|CONNECT_TYPE_AUX - // Or if we were to do it the TG way... - // pixel_x = PIPE_PIXEL_OFFSET_X(piping_layer) - // pixel_y = PIPE_PIXEL_OFFSET_Y(piping_layer) - // layer = initial(layer) + PIPE_LAYER_OFFSET(piping_layer) +/* +Quick overview: + +Pipes combine to form pipelines +Pipelines and other atmospheric objects combine to form pipe_networks + Note: A single pipe_network represents a completely open space + +Pipes -> Pipelines +Pipelines + Other Objects -> Pipe network + +*/ +/obj/machinery/atmospherics + anchored = TRUE + idle_power_usage = 0 + active_power_usage = 0 + power_channel = ENVIRON + var/nodealert = 0 + var/power_rating //the maximum amount of power the machine can use to do work, affects how powerful the machine is, in Watts + + unacidable = TRUE + layer = ATMOS_LAYER + plane = PLATING_PLANE + + var/pipe_flags = PIPING_DEFAULT_LAYER_ONLY // Allow other layers by exception basis. + var/connect_types = CONNECT_TYPE_REGULAR + var/piping_layer = PIPING_LAYER_DEFAULT // This will replace icon_connect_type at some point ~Leshana + var/icon_connect_type = "" //"-supply" or "-scrubbers" + var/construction_type = null // Type path of the pipe item when this is deconstructed. + var/pipe_state // icon_state as a pipe item + + var/initialize_directions = 0 + var/pipe_color + + var/global/datum/pipe_icon_manager/icon_manager + var/obj/machinery/atmospherics/node1 + var/obj/machinery/atmospherics/node2 + +/obj/machinery/atmospherics/New(loc, newdir) + ..() + if(!icon_manager) + icon_manager = new() + if(!isnull(newdir)) + set_dir(newdir) + if(!pipe_color) + pipe_color = color + color = null + + if(!pipe_color_check(pipe_color)) + pipe_color = null + init_dir() + +/obj/machinery/atmospherics/examine_icon() + return icon(icon=initial(icon),icon_state=initial(icon_state)) + +// This is used to set up what directions pipes will connect to. Should be called inside New() and whenever a dir changes. +/obj/machinery/atmospherics/proc/init_dir() + return + +// Get ALL initialize_directions - Some types (HE pipes etc) combine two vars together for this. +/obj/machinery/atmospherics/proc/get_init_dirs() + return initialize_directions + +// Get the direction each node is facing to connect. +// It now returns as a list so it can be fetched nicely, each entry corresponds to node of same number. +/obj/machinery/atmospherics/proc/get_node_connect_dirs() + return + +// Initializes nodes by looking at neighboring atmospherics machinery to connect to. +// When we're being constructed at runtime, atmos_init() is called by the construction code. +// When dynamically loading a map atmos_init is called by the maploader (initTemplateBounds proc) +// But during initial world creation its called by the master_controller. +// TODO - Consolidate these different ways of being called once SSatoms is created. +/obj/machinery/atmospherics/proc/atmos_init() + return + +/** Check if target is an acceptable target to connect as a node from this machine. */ +/obj/machinery/atmospherics/proc/can_be_node(obj/machinery/atmospherics/target, node_num) + return (target.initialize_directions & get_dir(target,src)) && check_connectable(target) && target.check_connectable(src) + +/** Check if this machine is willing to connect with the target machine. */ +/obj/machinery/atmospherics/proc/check_connectable(obj/machinery/atmospherics/target) + return (src.connect_types & target.connect_types) + +/obj/machinery/atmospherics/attackby(atom/A, mob/user as mob) + if(istype(A, /obj/item/device/pipe_painter)) + return + ..() + +/obj/machinery/atmospherics/proc/add_underlay(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) + if(node) + if(!T.is_plating() && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) + //underlays += icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node)) + underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) + else + //underlays += icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node)) + underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) + else + //underlays += icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color) + underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type) + +/obj/machinery/atmospherics/proc/update_underlays() + if(check_icon_cache()) + return 1 + else + return 0 + +/obj/machinery/atmospherics/proc/check_icon_cache(var/safety = 0) + if(!istype(icon_manager)) + if(!safety) //to prevent infinite loops + icon_manager = new() + check_icon_cache(1) + return 0 + + return 1 + +/obj/machinery/atmospherics/proc/color_cache_name(var/obj/machinery/atmospherics/node) + //Don't use this for standard pipes + if(!istype(node)) + return null + + return node.pipe_color + +/obj/machinery/atmospherics/process() + last_flow_rate = 0 + last_power_draw = 0 + + build_network() + +/obj/machinery/atmospherics/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + // Check to see if should be added to network. Add self if so and adjust variables appropriately. + // Note don't forget to have neighbors look as well! + + return null + +/obj/machinery/atmospherics/proc/build_network() + // Called to build a network from this node + + return null + +/obj/machinery/atmospherics/proc/return_network(obj/machinery/atmospherics/reference) + // Returns pipe_network associated with connection to reference + // Notes: should create network if necessary + // Should never return null + + return null + +/obj/machinery/atmospherics/proc/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + // Used when two pipe_networks are combining + +/obj/machinery/atmospherics/proc/return_network_air(datum/pipe_network/reference) + // Return a list of gas_mixture(s) in the object + // associated with reference pipe_network for use in rebuilding the networks gases list + // Is permitted to return null + +/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference) + +/obj/machinery/atmospherics/update_icon() + return null + +/obj/machinery/atmospherics/proc/can_unwrench() + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + if((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE) + return 0 + return 1 + +// Deconstruct into a pipe item. +/obj/machinery/atmospherics/proc/deconstruct() + if(QDELETED(src)) + return + if(construction_type) + var/obj/item/pipe/I = new construction_type(loc, null, null, src) + I.setPipingLayer(piping_layer) + transfer_fingerprints_to(I) + qdel(src) + +// Return a list of nodes which we should call atmos_init() and build_network() during on_construction() +/obj/machinery/atmospherics/proc/get_neighbor_nodes_for_init() + return null + +// Called on construction (i.e from pipe item) but not on initialization +/obj/machinery/atmospherics/proc/on_construction(obj_color, set_layer) + pipe_color = obj_color + setPipingLayer(set_layer) + // TODO - M.connect_types = src.connect_types - Or otherwise copy from item? Or figure it out from piping layer? + var/turf/T = get_turf(src) + level = !T.is_plating() ? 2 : 1 + atmos_init() + if(QDELETED(src)) + return // TODO - Eventually should get rid of the need for this. + build_network() + var/list/nodes = get_neighbor_nodes_for_init() + for(var/obj/machinery/atmospherics/A in nodes) + A.atmos_init() + A.build_network() + // TODO - Should we do src.build_network() before or after the nodes? + // We've historically done before, but /tg does after. TODO research if there is a difference. + +// This sets our piping layer. Hopefully its cool. +/obj/machinery/atmospherics/proc/setPipingLayer(new_layer) + if(pipe_flags & (PIPING_DEFAULT_LAYER_ONLY|PIPING_ALL_LAYER)) + new_layer = PIPING_LAYER_DEFAULT + piping_layer = new_layer + // Do it the Polaris way + switch(piping_layer) + if(PIPING_LAYER_SCRUBBER) + icon_state = "[icon_state]-scrubbers" + connect_types = CONNECT_TYPE_SCRUBBER + layer = PIPES_SCRUBBER_LAYER + icon_connect_type = "-scrubbers" + if(PIPING_LAYER_SUPPLY) + icon_state = "[icon_state]-supply" + connect_types = CONNECT_TYPE_SUPPLY + layer = PIPES_SUPPLY_LAYER + icon_connect_type = "-supply" + if(PIPING_LAYER_FUEL) + icon_state = "[icon_state]-fuel" + connect_types = CONNECT_TYPE_FUEL + layer = PIPES_FUEL_LAYER + icon_connect_type = "-fuel" + if(PIPING_LAYER_AUX) + icon_state = "[icon_state]-aux" + connect_types = CONNECT_TYPE_AUX + layer = PIPES_AUX_LAYER + icon_connect_type = "-aux" + if(pipe_flags & PIPING_ALL_LAYER) + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER|CONNECT_TYPE_FUEL|CONNECT_TYPE_AUX + // Or if we were to do it the TG way... + // pixel_x = PIPE_PIXEL_OFFSET_X(piping_layer) + // pixel_y = PIPE_PIXEL_OFFSET_Y(piping_layer) + // layer = initial(layer) + PIPE_LAYER_OFFSET(piping_layer) diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm index cc280d97ccb..cee94b9b5a7 100644 --- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm +++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm @@ -1,129 +1,129 @@ -/obj/machinery/atmospherics/binary - dir = SOUTH - initialize_directions = SOUTH|NORTH - use_power = USE_POWER_IDLE - - var/datum/gas_mixture/air1 - var/datum/gas_mixture/air2 - - var/datum/pipe_network/network1 - var/datum/pipe_network/network2 - -/obj/machinery/atmospherics/binary/New() - ..() - - air1 = new - air2 = new - - air1.volume = 200 - air2.volume = 200 - -/obj/machinery/atmospherics/binary/init_dir() - switch(dir) - if(NORTH) - initialize_directions = NORTH|SOUTH - if(SOUTH) - initialize_directions = NORTH|SOUTH - if(EAST) - initialize_directions = EAST|WEST - if(WEST) - initialize_directions = EAST|WEST - -// Housekeeping and pipe network stuff below -/obj/machinery/atmospherics/binary/get_neighbor_nodes_for_init() - return list(node1, node2) - -/obj/machinery/atmospherics/binary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - if(reference == node1) - network1 = new_network - - else if(reference == node2) - network2 = new_network - - if(new_network.normal_members.Find(src)) - return 0 - - new_network.normal_members += src - - return null - -/obj/machinery/atmospherics/binary/Destroy() - . = ..() - - if(node1) - node1.disconnect(src) - qdel(network1) - if(node2) - node2.disconnect(src) - qdel(network2) - - node1 = null - node2 = null - -/obj/machinery/atmospherics/binary/atmos_init() - if(node1 && node2) - return - - var/node2_connect = dir - var/node1_connect = turn(dir, 180) - - STANDARD_ATMOS_CHOOSE_NODE(1, node1_connect) - STANDARD_ATMOS_CHOOSE_NODE(2, node2_connect) - - update_icon() - update_underlays() - -/obj/machinery/atmospherics/binary/build_network() - if(!network1 && node1) - network1 = new /datum/pipe_network() - network1.normal_members += src - network1.build_network(node1, src) - - if(!network2 && node2) - network2 = new /datum/pipe_network() - network2.normal_members += src - network2.build_network(node2, src) - - -/obj/machinery/atmospherics/binary/return_network(obj/machinery/atmospherics/reference) - build_network() - - if(reference==node1) - return network1 - - if(reference==node2) - return network2 - - return null - -/obj/machinery/atmospherics/binary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) - if(network1 == old_network) - network1 = new_network - if(network2 == old_network) - network2 = new_network - - return 1 - -/obj/machinery/atmospherics/binary/return_network_air(datum/pipe_network/reference) - var/list/results = list() - - if(network1 == reference) - results += air1 - if(network2 == reference) - results += air2 - - return results - -/obj/machinery/atmospherics/binary/disconnect(obj/machinery/atmospherics/reference) - if(reference==node1) - qdel(network1) - node1 = null - - else if(reference==node2) - qdel(network2) - node2 = null - - update_icon() - update_underlays() - +/obj/machinery/atmospherics/binary + dir = SOUTH + initialize_directions = SOUTH|NORTH + use_power = USE_POWER_IDLE + + var/datum/gas_mixture/air1 + var/datum/gas_mixture/air2 + + var/datum/pipe_network/network1 + var/datum/pipe_network/network2 + +/obj/machinery/atmospherics/binary/New() + ..() + + air1 = new + air2 = new + + air1.volume = 200 + air2.volume = 200 + +/obj/machinery/atmospherics/binary/init_dir() + switch(dir) + if(NORTH) + initialize_directions = NORTH|SOUTH + if(SOUTH) + initialize_directions = NORTH|SOUTH + if(EAST) + initialize_directions = EAST|WEST + if(WEST) + initialize_directions = EAST|WEST + +// Housekeeping and pipe network stuff below +/obj/machinery/atmospherics/binary/get_neighbor_nodes_for_init() + return list(node1, node2) + +/obj/machinery/atmospherics/binary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + if(reference == node1) + network1 = new_network + + else if(reference == node2) + network2 = new_network + + if(new_network.normal_members.Find(src)) + return 0 + + new_network.normal_members += src + + return null + +/obj/machinery/atmospherics/binary/Destroy() + . = ..() + + if(node1) + node1.disconnect(src) + qdel(network1) + if(node2) + node2.disconnect(src) + qdel(network2) + + node1 = null + node2 = null + +/obj/machinery/atmospherics/binary/atmos_init() + if(node1 && node2) + return + + var/node2_connect = dir + var/node1_connect = turn(dir, 180) + + STANDARD_ATMOS_CHOOSE_NODE(1, node1_connect) + STANDARD_ATMOS_CHOOSE_NODE(2, node2_connect) + + update_icon() + update_underlays() + +/obj/machinery/atmospherics/binary/build_network() + if(!network1 && node1) + network1 = new /datum/pipe_network() + network1.normal_members += src + network1.build_network(node1, src) + + if(!network2 && node2) + network2 = new /datum/pipe_network() + network2.normal_members += src + network2.build_network(node2, src) + + +/obj/machinery/atmospherics/binary/return_network(obj/machinery/atmospherics/reference) + build_network() + + if(reference==node1) + return network1 + + if(reference==node2) + return network2 + + return null + +/obj/machinery/atmospherics/binary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + if(network1 == old_network) + network1 = new_network + if(network2 == old_network) + network2 = new_network + + return 1 + +/obj/machinery/atmospherics/binary/return_network_air(datum/pipe_network/reference) + var/list/results = list() + + if(network1 == reference) + results += air1 + if(network2 == reference) + results += air2 + + return results + +/obj/machinery/atmospherics/binary/disconnect(obj/machinery/atmospherics/reference) + if(reference==node1) + qdel(network1) + node1 = null + + else if(reference==node2) + qdel(network2) + node2 = null + + update_icon() + update_underlays() + return null \ No newline at end of file diff --git a/code/ATMOSPHERICS/components/binary_devices/circulator.dm b/code/ATMOSPHERICS/components/binary_devices/circulator.dm index 04856958ccb..c3f6b81ee3f 100644 --- a/code/ATMOSPHERICS/components/binary_devices/circulator.dm +++ b/code/ATMOSPHERICS/components/binary_devices/circulator.dm @@ -1,152 +1,152 @@ -//node1, air1, network1 correspond to input -//node2, air2, network2 correspond to output -/obj/machinery/atmospherics/binary/circulator - name = "circulator" - desc = "A gas circulator turbine and heat exchanger." - icon = 'icons/obj/power.dmi' - icon_state = "circ-unassembled" - anchored = FALSE - unacidable = TRUE - pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF - - var/kinetic_efficiency = 0.04 //combined kinetic and kinetic-to-electric efficiency - var/volume_ratio = 0.2 - - var/recent_moles_transferred = 0 - var/last_heat_capacity = 0 - var/last_temperature = 0 - var/last_pressure_delta = 0 - var/last_worldtime_transfer = 0 - var/last_stored_energy_transferred = 0 - var/volume_capacity_used = 0 - var/stored_energy = 0 - var/temperature_overlay - - density = TRUE - -/obj/machinery/atmospherics/binary/circulator/New() - ..() - desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]." - air1.volume = 400 - -/obj/machinery/atmospherics/binary/circulator/proc/return_transfer_air() - var/datum/gas_mixture/removed - if(anchored && !(stat&BROKEN) && network1) - var/input_starting_pressure = air1.return_pressure() - var/output_starting_pressure = air2.return_pressure() - last_pressure_delta = max(input_starting_pressure - output_starting_pressure - 5, 0) - - //only circulate air if there is a pressure difference (plus 5kPa kinetic, 10kPa static friction) - if(air1.temperature > 0 && last_pressure_delta > 5) - - //Calculate necessary moles to transfer using PV = nRT - recent_moles_transferred = (last_pressure_delta*network1.volume/(air1.temperature * R_IDEAL_GAS_EQUATION))/3 //uses the volume of the whole network, not just itself - volume_capacity_used = min( (last_pressure_delta*network1.volume/3)/(input_starting_pressure*air1.volume) , 1) //how much of the gas in the input air volume is consumed - - //Calculate energy generated from kinetic turbine - stored_energy += 1/ADIABATIC_EXPONENT * min(last_pressure_delta * network1.volume , input_starting_pressure*air1.volume) * (1 - volume_ratio**ADIABATIC_EXPONENT) * kinetic_efficiency - - //Actually transfer the gas - removed = air1.remove(recent_moles_transferred) - if(removed) - last_heat_capacity = removed.heat_capacity() - last_temperature = removed.temperature - - //Update the gas networks. - network1.update = 1 - - last_worldtime_transfer = world.time - else - recent_moles_transferred = 0 - - update_icon() - return removed - -/obj/machinery/atmospherics/binary/circulator/proc/return_stored_energy() - last_stored_energy_transferred = stored_energy - stored_energy = 0 - return last_stored_energy_transferred - -/obj/machinery/atmospherics/binary/circulator/process() - ..() - - if(last_worldtime_transfer < world.time - 50) - recent_moles_transferred = 0 - update_icon() - -/obj/machinery/atmospherics/binary/circulator/update_icon() - icon_state = anchored ? "circ-assembled" : "circ-unassembled" - cut_overlays() - if (stat & (BROKEN|NOPOWER) || !anchored) - return 1 - if (last_pressure_delta > 0 && recent_moles_transferred > 0) - if (temperature_overlay) - add_overlay(temperature_overlay) - if (last_pressure_delta > 5*ONE_ATMOSPHERE) - add_overlay("circ-run") - else - add_overlay("circ-slow") - else - add_overlay("circ-off") - - return 1 - -/obj/machinery/atmospherics/binary/circulator/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.has_tool_quality(TOOL_WRENCH)) - playsound(src, W.usesound, 75, 1) - anchored = !anchored - user.visible_message("[user.name] [anchored ? "secures" : "unsecures"] the bolts holding [src.name] to the floor.", \ - "You [anchored ? "secure" : "unsecure"] the bolts holding [src] to the floor.", \ - "You hear a ratchet.") - - if(anchored) - temperature_overlay = null - if(dir & (NORTH|SOUTH)) - initialize_directions = NORTH|SOUTH - else if(dir & (EAST|WEST)) - initialize_directions = EAST|WEST - - atmos_init() - build_network() - if (node1) - node1.atmos_init() - node1.build_network() - if (node2) - node2.atmos_init() - node2.build_network() - else - if(node1) - node1.disconnect(src) - qdel(network1) - if(node2) - node2.disconnect(src) - qdel(network2) - - node1 = null - node2 = null - - else - ..() - -/obj/machinery/atmospherics/binary/circulator/verb/rotate_clockwise() - set name = "Rotate Circulator Clockwise" - set category = "Object" - set src in view(1) - - if (usr.stat || usr.restrained() || anchored) - return - - src.set_dir(turn(src.dir, 270)) - desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]." - - -/obj/machinery/atmospherics/binary/circulator/verb/rotate_counterclockwise() - set name = "Rotate Circulator Counterclockwise" - set category = "Object" - set src in view(1) - - if (usr.stat || usr.restrained() || anchored) - return - - src.set_dir(turn(src.dir, 90)) +//node1, air1, network1 correspond to input +//node2, air2, network2 correspond to output +/obj/machinery/atmospherics/binary/circulator + name = "circulator" + desc = "A gas circulator turbine and heat exchanger." + icon = 'icons/obj/power.dmi' + icon_state = "circ-unassembled" + anchored = FALSE + unacidable = TRUE + pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF + + var/kinetic_efficiency = 0.04 //combined kinetic and kinetic-to-electric efficiency + var/volume_ratio = 0.2 + + var/recent_moles_transferred = 0 + var/last_heat_capacity = 0 + var/last_temperature = 0 + var/last_pressure_delta = 0 + var/last_worldtime_transfer = 0 + var/last_stored_energy_transferred = 0 + var/volume_capacity_used = 0 + var/stored_energy = 0 + var/temperature_overlay + + density = TRUE + +/obj/machinery/atmospherics/binary/circulator/New() + ..() + desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]." + air1.volume = 400 + +/obj/machinery/atmospherics/binary/circulator/proc/return_transfer_air() + var/datum/gas_mixture/removed + if(anchored && !(stat&BROKEN) && network1) + var/input_starting_pressure = air1.return_pressure() + var/output_starting_pressure = air2.return_pressure() + last_pressure_delta = max(input_starting_pressure - output_starting_pressure - 5, 0) + + //only circulate air if there is a pressure difference (plus 5kPa kinetic, 10kPa static friction) + if(air1.temperature > 0 && last_pressure_delta > 5) + + //Calculate necessary moles to transfer using PV = nRT + recent_moles_transferred = (last_pressure_delta*network1.volume/(air1.temperature * R_IDEAL_GAS_EQUATION))/3 //uses the volume of the whole network, not just itself + volume_capacity_used = min( (last_pressure_delta*network1.volume/3)/(input_starting_pressure*air1.volume) , 1) //how much of the gas in the input air volume is consumed + + //Calculate energy generated from kinetic turbine + stored_energy += 1/ADIABATIC_EXPONENT * min(last_pressure_delta * network1.volume , input_starting_pressure*air1.volume) * (1 - volume_ratio**ADIABATIC_EXPONENT) * kinetic_efficiency + + //Actually transfer the gas + removed = air1.remove(recent_moles_transferred) + if(removed) + last_heat_capacity = removed.heat_capacity() + last_temperature = removed.temperature + + //Update the gas networks. + network1.update = 1 + + last_worldtime_transfer = world.time + else + recent_moles_transferred = 0 + + update_icon() + return removed + +/obj/machinery/atmospherics/binary/circulator/proc/return_stored_energy() + last_stored_energy_transferred = stored_energy + stored_energy = 0 + return last_stored_energy_transferred + +/obj/machinery/atmospherics/binary/circulator/process() + ..() + + if(last_worldtime_transfer < world.time - 50) + recent_moles_transferred = 0 + update_icon() + +/obj/machinery/atmospherics/binary/circulator/update_icon() + icon_state = anchored ? "circ-assembled" : "circ-unassembled" + cut_overlays() + if (stat & (BROKEN|NOPOWER) || !anchored) + return 1 + if (last_pressure_delta > 0 && recent_moles_transferred > 0) + if (temperature_overlay) + add_overlay(temperature_overlay) + if (last_pressure_delta > 5*ONE_ATMOSPHERE) + add_overlay("circ-run") + else + add_overlay("circ-slow") + else + add_overlay("circ-off") + + return 1 + +/obj/machinery/atmospherics/binary/circulator/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + playsound(src, W.usesound, 75, 1) + anchored = !anchored + user.visible_message("[user.name] [anchored ? "secures" : "unsecures"] the bolts holding [src.name] to the floor.", \ + "You [anchored ? "secure" : "unsecure"] the bolts holding [src] to the floor.", \ + "You hear a ratchet.") + + if(anchored) + temperature_overlay = null + if(dir & (NORTH|SOUTH)) + initialize_directions = NORTH|SOUTH + else if(dir & (EAST|WEST)) + initialize_directions = EAST|WEST + + atmos_init() + build_network() + if (node1) + node1.atmos_init() + node1.build_network() + if (node2) + node2.atmos_init() + node2.build_network() + else + if(node1) + node1.disconnect(src) + qdel(network1) + if(node2) + node2.disconnect(src) + qdel(network2) + + node1 = null + node2 = null + + else + ..() + +/obj/machinery/atmospherics/binary/circulator/verb/rotate_clockwise() + set name = "Rotate Circulator Clockwise" + set category = "Object" + set src in view(1) + + if (usr.stat || usr.restrained() || anchored) + return + + src.set_dir(turn(src.dir, 270)) + desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]." + + +/obj/machinery/atmospherics/binary/circulator/verb/rotate_counterclockwise() + set name = "Rotate Circulator Counterclockwise" + set category = "Object" + set src in view(1) + + if (usr.stat || usr.restrained() || anchored) + return + + src.set_dir(turn(src.dir, 90)) desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]." \ No newline at end of file diff --git a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm index aebea389013..251aa4c1473 100644 --- a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm @@ -1,263 +1,263 @@ -#define DEFAULT_PRESSURE_DELTA 10000 - -#define EXTERNAL_PRESSURE_BOUND ONE_ATMOSPHERE -#define INTERNAL_PRESSURE_BOUND 0 -#define PRESSURE_CHECKS 1 - -#define PRESSURE_CHECK_EXTERNAL 1 -#define PRESSURE_CHECK_INPUT 2 -#define PRESSURE_CHECK_OUTPUT 4 - -/obj/machinery/atmospherics/binary/dp_vent_pump - icon = 'icons/atmos/vent_pump.dmi' - icon_state = "map_dp_vent" - - //node2 is output port - //node1 is input port - - name = "Dual Port Air Vent" - desc = "Has a valve and pump attached to it. There are two ports." - - level = 1 - - use_power = USE_POWER_OFF - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 7500 //7500 W ~ 10 HP - - pipe_flags = PIPING_ALL_LAYER - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER //connects to regular, supply and scrubbers pipes - - var/pump_direction = 1 //0 = siphoning, 1 = releasing - - var/external_pressure_bound = EXTERNAL_PRESSURE_BOUND - var/input_pressure_min = INTERNAL_PRESSURE_BOUND - var/output_pressure_max = DEFAULT_PRESSURE_DELTA - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - - var/pressure_checks = PRESSURE_CHECK_EXTERNAL - //1: Do not pass external_pressure_bound - //2: Do not pass input_pressure_min - //4: Do not pass output_pressure_max - -/obj/machinery/atmospherics/binary/dp_vent_pump/New() - ..() - air1.volume = ATMOS_DEFAULT_VOLUME_PUMP - air2.volume = ATMOS_DEFAULT_VOLUME_PUMP - icon = null - -/obj/machinery/atmospherics/binary/dp_vent_pump/Destroy() - unregister_radio(src, frequency) - . = ..() - -/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume - name = "Large Dual Port Air Vent" - -/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/New() - ..() - air1.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 - air2.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 - -/obj/machinery/atmospherics/binary/dp_vent_pump/update_icon(var/safety = 0) - if(!check_icon_cache()) - return - - cut_overlays() - - var/vent_icon = "vent" - - var/turf/T = get_turf(src) - if(!istype(T)) - return - - if(!T.is_plating() && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) - vent_icon += "h" - - if(!powered()) - vent_icon += "off" - else - vent_icon += "[use_power ? "[pump_direction ? "out" : "in"]" : "off"]" - - add_overlay(icon_manager.get_atmos_icon("device", , , vent_icon)) - -/obj/machinery/atmospherics/binary/dp_vent_pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - if(!T.is_plating() && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) - return - else - if (node1) - add_underlay(T, node1, turn(dir, -180), node1.icon_connect_type) - else - add_underlay(T, node1, turn(dir, -180)) - if (node2) - add_underlay(T, node2, dir, node2.icon_connect_type) - else - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/dp_vent_pump/hide(var/i) - update_icon() - update_underlays() - -/obj/machinery/atmospherics/binary/dp_vent_pump/process() - ..() - - last_power_draw = 0 - last_flow_rate = 0 - - if(stat & (NOPOWER|BROKEN) || !use_power) - return 0 - - var/datum/gas_mixture/environment = loc.return_air() - - var/power_draw = -1 - - //Figure out the target pressure difference - var/pressure_delta = get_pressure_delta(environment) - - if(pressure_delta > 0.5) - if(pump_direction) //internal -> external - if (node1 && (environment.temperature || air1.temperature)) - var/transfer_moles = calculate_transfer_moles(air1, environment, pressure_delta) - power_draw = pump_gas(src, air1, environment, transfer_moles, power_rating) - - if(power_draw >= 0 && network1) - network1.update = 1 - else //external -> internal - if (node2 && (environment.temperature || air2.temperature)) - var/transfer_moles = calculate_transfer_moles(environment, air2, pressure_delta, (network2)? network2.volume : 0) - - //limit flow rate from turfs - transfer_moles = min(transfer_moles, environment.total_moles*air2.volume/environment.volume) //group_multiplier gets divided out here - power_draw = pump_gas(src, environment, air2, transfer_moles, power_rating) - - if(power_draw >= 0 && network2) - network2.update = 1 - - if (power_draw >= 0) - last_power_draw = power_draw - use_power(power_draw) - - return 1 - -/obj/machinery/atmospherics/binary/dp_vent_pump/proc/get_pressure_delta(datum/gas_mixture/environment) - var/pressure_delta = DEFAULT_PRESSURE_DELTA - var/environment_pressure = environment.return_pressure() - - if(pump_direction) //internal -> external - if(pressure_checks & PRESSURE_CHECK_EXTERNAL) - pressure_delta = min(pressure_delta, external_pressure_bound - environment_pressure) //increasing the pressure here - if(pressure_checks & PRESSURE_CHECK_INPUT) - pressure_delta = min(pressure_delta, air1.return_pressure() - input_pressure_min) //decreasing the pressure here - else //external -> internal - if(pressure_checks & PRESSURE_CHECK_EXTERNAL) - pressure_delta = min(pressure_delta, environment_pressure - external_pressure_bound) //decreasing the pressure here - if(pressure_checks & PRESSURE_CHECK_OUTPUT) - pressure_delta = min(pressure_delta, output_pressure_max - air2.return_pressure()) //increasing the pressure here - - return pressure_delta - - -//Radio remote control - -/obj/machinery/atmospherics/binary/dp_vent_pump/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = radio_controller.add_object(src, frequency, radio_filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/dp_vent_pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "ADVP", - "power" = use_power, - "direction" = pump_direction?("release"):("siphon"), - "checks" = pressure_checks, - "input" = input_pressure_min, - "output" = output_pressure_max, - "external" = external_pressure_bound, - "sigtype" = "status" - ) - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/binary/dp_vent_pump/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/dp_vent_pump/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" - - -/obj/machinery/atmospherics/unary/vent_pump/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/binary/dp_vent_pump/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - if(signal.data["power"]) - update_use_power(text2num(signal.data["power"])) - - if(signal.data["power_toggle"]) - update_use_power(!use_power) - - if(signal.data["direction"]) - pump_direction = text2num(signal.data["direction"]) - - if(signal.data["checks"]) - pressure_checks = text2num(signal.data["checks"]) - - if(signal.data["purge"]) - pressure_checks &= ~1 - pump_direction = 0 - - if(signal.data["stabalize"]) - pressure_checks |= 1 - pump_direction = 1 - - if(signal.data["set_input_pressure"]) - input_pressure_min = between(0, text2num(signal.data["set_input_pressure"]), ONE_ATMOSPHERE*50) - - if(signal.data["set_output_pressure"]) - output_pressure_max = between(0, text2num(signal.data["set_output_pressure"]), ONE_ATMOSPHERE*50) - - if(signal.data["set_external_pressure"]) - external_pressure_bound = between(0, text2num(signal.data["set_external_pressure"]), ONE_ATMOSPHERE*50) - - if(signal.data["status"]) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - -#undef DEFAULT_PRESSURE_DELTA - -#undef EXTERNAL_PRESSURE_BOUND -#undef INTERNAL_PRESSURE_BOUND -#undef PRESSURE_CHECKS - -#undef PRESSURE_CHECK_EXTERNAL -#undef PRESSURE_CHECK_INPUT -#undef PRESSURE_CHECK_OUTPUT +#define DEFAULT_PRESSURE_DELTA 10000 + +#define EXTERNAL_PRESSURE_BOUND ONE_ATMOSPHERE +#define INTERNAL_PRESSURE_BOUND 0 +#define PRESSURE_CHECKS 1 + +#define PRESSURE_CHECK_EXTERNAL 1 +#define PRESSURE_CHECK_INPUT 2 +#define PRESSURE_CHECK_OUTPUT 4 + +/obj/machinery/atmospherics/binary/dp_vent_pump + icon = 'icons/atmos/vent_pump.dmi' + icon_state = "map_dp_vent" + + //node2 is output port + //node1 is input port + + name = "Dual Port Air Vent" + desc = "Has a valve and pump attached to it. There are two ports." + + level = 1 + + use_power = USE_POWER_OFF + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 7500 //7500 W ~ 10 HP + + pipe_flags = PIPING_ALL_LAYER + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER //connects to regular, supply and scrubbers pipes + + var/pump_direction = 1 //0 = siphoning, 1 = releasing + + var/external_pressure_bound = EXTERNAL_PRESSURE_BOUND + var/input_pressure_min = INTERNAL_PRESSURE_BOUND + var/output_pressure_max = DEFAULT_PRESSURE_DELTA + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + + var/pressure_checks = PRESSURE_CHECK_EXTERNAL + //1: Do not pass external_pressure_bound + //2: Do not pass input_pressure_min + //4: Do not pass output_pressure_max + +/obj/machinery/atmospherics/binary/dp_vent_pump/New() + ..() + air1.volume = ATMOS_DEFAULT_VOLUME_PUMP + air2.volume = ATMOS_DEFAULT_VOLUME_PUMP + icon = null + +/obj/machinery/atmospherics/binary/dp_vent_pump/Destroy() + unregister_radio(src, frequency) + . = ..() + +/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume + name = "Large Dual Port Air Vent" + +/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/New() + ..() + air1.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 + air2.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 + +/obj/machinery/atmospherics/binary/dp_vent_pump/update_icon(var/safety = 0) + if(!check_icon_cache()) + return + + cut_overlays() + + var/vent_icon = "vent" + + var/turf/T = get_turf(src) + if(!istype(T)) + return + + if(!T.is_plating() && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) + vent_icon += "h" + + if(!powered()) + vent_icon += "off" + else + vent_icon += "[use_power ? "[pump_direction ? "out" : "in"]" : "off"]" + + add_overlay(icon_manager.get_atmos_icon("device", , , vent_icon)) + +/obj/machinery/atmospherics/binary/dp_vent_pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + if(!T.is_plating() && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) + return + else + if (node1) + add_underlay(T, node1, turn(dir, -180), node1.icon_connect_type) + else + add_underlay(T, node1, turn(dir, -180)) + if (node2) + add_underlay(T, node2, dir, node2.icon_connect_type) + else + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/dp_vent_pump/hide(var/i) + update_icon() + update_underlays() + +/obj/machinery/atmospherics/binary/dp_vent_pump/process() + ..() + + last_power_draw = 0 + last_flow_rate = 0 + + if(stat & (NOPOWER|BROKEN) || !use_power) + return 0 + + var/datum/gas_mixture/environment = loc.return_air() + + var/power_draw = -1 + + //Figure out the target pressure difference + var/pressure_delta = get_pressure_delta(environment) + + if(pressure_delta > 0.5) + if(pump_direction) //internal -> external + if (node1 && (environment.temperature || air1.temperature)) + var/transfer_moles = calculate_transfer_moles(air1, environment, pressure_delta) + power_draw = pump_gas(src, air1, environment, transfer_moles, power_rating) + + if(power_draw >= 0 && network1) + network1.update = 1 + else //external -> internal + if (node2 && (environment.temperature || air2.temperature)) + var/transfer_moles = calculate_transfer_moles(environment, air2, pressure_delta, (network2)? network2.volume : 0) + + //limit flow rate from turfs + transfer_moles = min(transfer_moles, environment.total_moles*air2.volume/environment.volume) //group_multiplier gets divided out here + power_draw = pump_gas(src, environment, air2, transfer_moles, power_rating) + + if(power_draw >= 0 && network2) + network2.update = 1 + + if (power_draw >= 0) + last_power_draw = power_draw + use_power(power_draw) + + return 1 + +/obj/machinery/atmospherics/binary/dp_vent_pump/proc/get_pressure_delta(datum/gas_mixture/environment) + var/pressure_delta = DEFAULT_PRESSURE_DELTA + var/environment_pressure = environment.return_pressure() + + if(pump_direction) //internal -> external + if(pressure_checks & PRESSURE_CHECK_EXTERNAL) + pressure_delta = min(pressure_delta, external_pressure_bound - environment_pressure) //increasing the pressure here + if(pressure_checks & PRESSURE_CHECK_INPUT) + pressure_delta = min(pressure_delta, air1.return_pressure() - input_pressure_min) //decreasing the pressure here + else //external -> internal + if(pressure_checks & PRESSURE_CHECK_EXTERNAL) + pressure_delta = min(pressure_delta, environment_pressure - external_pressure_bound) //decreasing the pressure here + if(pressure_checks & PRESSURE_CHECK_OUTPUT) + pressure_delta = min(pressure_delta, output_pressure_max - air2.return_pressure()) //increasing the pressure here + + return pressure_delta + + +//Radio remote control + +/obj/machinery/atmospherics/binary/dp_vent_pump/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = radio_controller.add_object(src, frequency, radio_filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/dp_vent_pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "ADVP", + "power" = use_power, + "direction" = pump_direction?("release"):("siphon"), + "checks" = pressure_checks, + "input" = input_pressure_min, + "output" = output_pressure_max, + "external" = external_pressure_bound, + "sigtype" = "status" + ) + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/binary/dp_vent_pump/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/dp_vent_pump/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" + + +/obj/machinery/atmospherics/unary/vent_pump/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/binary/dp_vent_pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + if(signal.data["power"]) + update_use_power(text2num(signal.data["power"])) + + if(signal.data["power_toggle"]) + update_use_power(!use_power) + + if(signal.data["direction"]) + pump_direction = text2num(signal.data["direction"]) + + if(signal.data["checks"]) + pressure_checks = text2num(signal.data["checks"]) + + if(signal.data["purge"]) + pressure_checks &= ~1 + pump_direction = 0 + + if(signal.data["stabalize"]) + pressure_checks |= 1 + pump_direction = 1 + + if(signal.data["set_input_pressure"]) + input_pressure_min = between(0, text2num(signal.data["set_input_pressure"]), ONE_ATMOSPHERE*50) + + if(signal.data["set_output_pressure"]) + output_pressure_max = between(0, text2num(signal.data["set_output_pressure"]), ONE_ATMOSPHERE*50) + + if(signal.data["set_external_pressure"]) + external_pressure_bound = between(0, text2num(signal.data["set_external_pressure"]), ONE_ATMOSPHERE*50) + + if(signal.data["status"]) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + +#undef DEFAULT_PRESSURE_DELTA + +#undef EXTERNAL_PRESSURE_BOUND +#undef INTERNAL_PRESSURE_BOUND +#undef PRESSURE_CHECKS + +#undef PRESSURE_CHECK_EXTERNAL +#undef PRESSURE_CHECK_INPUT +#undef PRESSURE_CHECK_OUTPUT diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm index 364e7eda9fa..5d39a608513 100644 --- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm +++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm @@ -1,302 +1,302 @@ -#define REGULATE_NONE 0 -#define REGULATE_INPUT 1 //shuts off when input side is below the target pressure -#define REGULATE_OUTPUT 2 //shuts off when output side is above the target pressure - -/obj/machinery/atmospherics/binary/passive_gate - icon = 'icons/atmos/passive_gate.dmi' - icon_state = "map" - construction_type = /obj/item/pipe/directional - pipe_state = "passivegate" - level = 1 - - name = "pressure regulator" - desc = "A one-way air valve that can be used to regulate input or output pressure, and flow rate. Does not require power." - - use_power = USE_POWER_OFF - interact_offline = TRUE - - var/unlocked = 0 //If 0, then the valve is locked closed, otherwise it is open(-able, it's a one-way valve so it closes if gas would flow backwards). - var/target_pressure = ONE_ATMOSPHERE - var/max_pressure_setting = 15000 //kPa - var/set_flow_rate = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 - var/regulate_mode = REGULATE_OUTPUT - - var/flowing = 0 //for icons - becomes zero if the valve closes itself due to regulation mode - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/binary/passive_gate/New() - ..() - air1.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 - air2.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 - -/obj/machinery/atmospherics/binary/passive_gate/Destroy() - unregister_radio(src, frequency) - . = ..() - -/obj/machinery/atmospherics/binary/passive_gate/update_icon() - icon_state = (unlocked && flowing)? "on" : "off" - -/obj/machinery/atmospherics/binary/passive_gate/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, 180)) - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/passive_gate/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/binary/passive_gate/process() - ..() - - last_flow_rate = 0 - - if(!unlocked) - return 0 - - var/output_starting_pressure = air2.return_pressure() - var/input_starting_pressure = air1.return_pressure() - - var/pressure_delta - switch (regulate_mode) - if (REGULATE_INPUT) - pressure_delta = input_starting_pressure - target_pressure - if (REGULATE_OUTPUT) - pressure_delta = target_pressure - output_starting_pressure - if (REGULATE_NONE) - pressure_delta = input_starting_pressure - output_starting_pressure - - //-1 if pump_gas() did not move any gas, >= 0 otherwise - var/returnval = -1 - if((regulate_mode == REGULATE_NONE || pressure_delta > 0.01) && (air1.temperature > 0 || air2.temperature > 0)) //since it's basically a valve, it makes sense to check both temperatures - flowing = 1 - - //flow rate limit - var/transfer_moles = (set_flow_rate/air1.volume)*air1.total_moles - - //Figure out how much gas to transfer to meet the target pressure. - switch (regulate_mode) - if (REGULATE_INPUT) - transfer_moles = min(transfer_moles, calculate_transfer_moles(air2, air1, pressure_delta, (network1)? network1.volume : 0)) - if (REGULATE_OUTPUT) - transfer_moles = min(transfer_moles, calculate_transfer_moles(air1, air2, pressure_delta, (network2)? network2.volume : 0)) - if (REGULATE_NONE) - var/source = air1 - var/sink = air2 - // If node1 is a network of more than 1 pipe, we want to transfer from that whole network, otw use just node1, as current - if(istype(node1, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/p = node1 - if(istype(p.parent, /datum/pipeline)) // Nested if-blocks to avoid the mystical : - var/datum/pipeline/l = p.parent - if(istype(l.air, /datum/gas_mixture)) - source = l.air - // If node2 is a network of more than 1 pipe, we want to transfer to that whole network, otw use just node2, as current - if(istype(node2, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/p = node2 - if(istype(p.parent, /datum/pipeline)) - var/datum/pipeline/l = p.parent - if(istype(l.air, /datum/gas_mixture)) - sink = l.air - transfer_moles = max(0, calculate_equalize_moles(source, sink)) // Not regulated, don't care about flow rate - - //pump_gas() will return a negative number if no flow occurred - if(regulate_mode == REGULATE_NONE) // ACTUALLY move gases from the whole network, not just the immediate pipes - var/source = air1 - var/sink = air2 - // If node1 is a network of more than 1 pipe, we want to transfer from that whole network, otw use just node1, as current - if(istype(node1, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/p = node1 - if(istype(p.parent, /datum/pipeline)) // Nested if-blocks to avoid the mystical : - var/datum/pipeline/l = p.parent - if(istype(l.air, /datum/gas_mixture)) - source = l.air - // If node2 is a network of more than 1 pipe, we want to transfer to that whole network, otw use just node2, as current - if(istype(node2, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/p = node2 - if(istype(p.parent, /datum/pipeline)) - var/datum/pipeline/l = p.parent - if(istype(l.air, /datum/gas_mixture)) - sink = l.air - returnval = pump_gas_passive(src, source, sink, transfer_moles) - else - returnval = pump_gas_passive(src, air1, air2, transfer_moles) - - if (returnval >= 0) - if(network1) - network1.update = 1 - - if(network2) - network2.update = 1 - - if (last_flow_rate) - flowing = 1 - - update_icon() - - -//Radio remote control - -/obj/machinery/atmospherics/binary/passive_gate/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = radio_controller.add_object(src, frequency, radio_filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/passive_gate/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "AGP", - "power" = unlocked, - "target_output" = target_pressure, - "regulate_mode" = regulate_mode, - "set_flow_rate" = set_flow_rate, - "sigtype" = "status" - ) - - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/binary/passive_gate/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/passive_gate/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - if("power" in signal.data) - unlocked = text2num(signal.data["power"]) - - if("power_toggle" in signal.data) - unlocked = !unlocked - - if("set_target_pressure" in signal.data) - target_pressure = between(0, text2num(signal.data["set_target_pressure"]), max_pressure_setting) - - if("set_regulate_mode" in signal.data) - regulate_mode = text2num(signal.data["set_regulate_mode"]) - - if("set_flow_rate" in signal.data) - regulate_mode = text2num(signal.data["set_flow_rate"]) - - if("status" in signal.data) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - return - -/obj/machinery/atmospherics/binary/passive_gate/attack_hand(user as mob) - if(..()) - return - add_fingerprint(usr) - if(!allowed(user)) - to_chat(user, "Access denied.") - return - tgui_interact(user) - -/obj/machinery/atmospherics/binary/passive_gate/tgui_interact(mob/user, datum/tgui/ui) - if(stat & BROKEN) - return FALSE - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "PressureRegulator", name) - ui.open() - -/obj/machinery/atmospherics/binary/passive_gate/tgui_data(mob/user) - // this is the data which will be sent to the ui - var/data[0] - - data = list( - "on" = unlocked, - "pressure_set" = round(target_pressure*100), //Nano UI can't handle rounded non-integers, apparently. - "max_pressure" = max_pressure_setting, - "input_pressure" = round(air1.return_pressure()*100), - "output_pressure" = round(air2.return_pressure()*100), - "regulate_mode" = regulate_mode, - "set_flow_rate" = round(set_flow_rate*10), - "last_flow_rate" = round(last_flow_rate*10), - ) - - return data - - -/obj/machinery/atmospherics/binary/passive_gate/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("toggle_valve") - . = TRUE - unlocked = !unlocked - if("regulate_mode") - . = TRUE - switch(params["mode"]) - if("off") regulate_mode = REGULATE_NONE - if("input") regulate_mode = REGULATE_INPUT - if("output") regulate_mode = REGULATE_OUTPUT - - if("set_press") - . = TRUE - switch(params["press"]) - if("min") - target_pressure = 0 - if("max") - target_pressure = max_pressure_setting - if("set") - var/new_pressure = tgui_input_number(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure Control",src.target_pressure,max_pressure_setting,0) - src.target_pressure = between(0, new_pressure, max_pressure_setting) - - if("set_flow_rate") - . = TRUE - switch(params["press"]) - if("min") - set_flow_rate = 0 - if("max") - set_flow_rate = air1.volume - if("set") - var/new_flow_rate = tgui_input_number(usr,"Enter new flow rate limit (0-[air1.volume]L/s)","Flow Rate Control",src.set_flow_rate,air1.volume,0) - src.set_flow_rate = between(0, new_flow_rate, air1.volume) - - update_icon() - add_fingerprint(usr) - -/obj/machinery/atmospherics/binary/passive_gate/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.has_tool_quality(TOOL_WRENCH)) - return ..() - if (unlocked) - to_chat(user, "You cannot unwrench \the [src], turn it off first.") - return 1 - if(!can_unwrench()) - to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear ratchet.") - deconstruct() - -#undef REGULATE_NONE -#undef REGULATE_INPUT -#undef REGULATE_OUTPUT +#define REGULATE_NONE 0 +#define REGULATE_INPUT 1 //shuts off when input side is below the target pressure +#define REGULATE_OUTPUT 2 //shuts off when output side is above the target pressure + +/obj/machinery/atmospherics/binary/passive_gate + icon = 'icons/atmos/passive_gate.dmi' + icon_state = "map" + construction_type = /obj/item/pipe/directional + pipe_state = "passivegate" + level = 1 + + name = "pressure regulator" + desc = "A one-way air valve that can be used to regulate input or output pressure, and flow rate. Does not require power." + + use_power = USE_POWER_OFF + interact_offline = TRUE + + var/unlocked = 0 //If 0, then the valve is locked closed, otherwise it is open(-able, it's a one-way valve so it closes if gas would flow backwards). + var/target_pressure = ONE_ATMOSPHERE + var/max_pressure_setting = 15000 //kPa + var/set_flow_rate = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 + var/regulate_mode = REGULATE_OUTPUT + + var/flowing = 0 //for icons - becomes zero if the valve closes itself due to regulation mode + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/binary/passive_gate/New() + ..() + air1.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 + air2.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 + +/obj/machinery/atmospherics/binary/passive_gate/Destroy() + unregister_radio(src, frequency) + . = ..() + +/obj/machinery/atmospherics/binary/passive_gate/update_icon() + icon_state = (unlocked && flowing)? "on" : "off" + +/obj/machinery/atmospherics/binary/passive_gate/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, 180)) + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/passive_gate/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/binary/passive_gate/process() + ..() + + last_flow_rate = 0 + + if(!unlocked) + return 0 + + var/output_starting_pressure = air2.return_pressure() + var/input_starting_pressure = air1.return_pressure() + + var/pressure_delta + switch (regulate_mode) + if (REGULATE_INPUT) + pressure_delta = input_starting_pressure - target_pressure + if (REGULATE_OUTPUT) + pressure_delta = target_pressure - output_starting_pressure + if (REGULATE_NONE) + pressure_delta = input_starting_pressure - output_starting_pressure + + //-1 if pump_gas() did not move any gas, >= 0 otherwise + var/returnval = -1 + if((regulate_mode == REGULATE_NONE || pressure_delta > 0.01) && (air1.temperature > 0 || air2.temperature > 0)) //since it's basically a valve, it makes sense to check both temperatures + flowing = 1 + + //flow rate limit + var/transfer_moles = (set_flow_rate/air1.volume)*air1.total_moles + + //Figure out how much gas to transfer to meet the target pressure. + switch (regulate_mode) + if (REGULATE_INPUT) + transfer_moles = min(transfer_moles, calculate_transfer_moles(air2, air1, pressure_delta, (network1)? network1.volume : 0)) + if (REGULATE_OUTPUT) + transfer_moles = min(transfer_moles, calculate_transfer_moles(air1, air2, pressure_delta, (network2)? network2.volume : 0)) + if (REGULATE_NONE) + var/source = air1 + var/sink = air2 + // If node1 is a network of more than 1 pipe, we want to transfer from that whole network, otw use just node1, as current + if(istype(node1, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/p = node1 + if(istype(p.parent, /datum/pipeline)) // Nested if-blocks to avoid the mystical : + var/datum/pipeline/l = p.parent + if(istype(l.air, /datum/gas_mixture)) + source = l.air + // If node2 is a network of more than 1 pipe, we want to transfer to that whole network, otw use just node2, as current + if(istype(node2, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/p = node2 + if(istype(p.parent, /datum/pipeline)) + var/datum/pipeline/l = p.parent + if(istype(l.air, /datum/gas_mixture)) + sink = l.air + transfer_moles = max(0, calculate_equalize_moles(source, sink)) // Not regulated, don't care about flow rate + + //pump_gas() will return a negative number if no flow occurred + if(regulate_mode == REGULATE_NONE) // ACTUALLY move gases from the whole network, not just the immediate pipes + var/source = air1 + var/sink = air2 + // If node1 is a network of more than 1 pipe, we want to transfer from that whole network, otw use just node1, as current + if(istype(node1, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/p = node1 + if(istype(p.parent, /datum/pipeline)) // Nested if-blocks to avoid the mystical : + var/datum/pipeline/l = p.parent + if(istype(l.air, /datum/gas_mixture)) + source = l.air + // If node2 is a network of more than 1 pipe, we want to transfer to that whole network, otw use just node2, as current + if(istype(node2, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/p = node2 + if(istype(p.parent, /datum/pipeline)) + var/datum/pipeline/l = p.parent + if(istype(l.air, /datum/gas_mixture)) + sink = l.air + returnval = pump_gas_passive(src, source, sink, transfer_moles) + else + returnval = pump_gas_passive(src, air1, air2, transfer_moles) + + if (returnval >= 0) + if(network1) + network1.update = 1 + + if(network2) + network2.update = 1 + + if (last_flow_rate) + flowing = 1 + + update_icon() + + +//Radio remote control + +/obj/machinery/atmospherics/binary/passive_gate/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = radio_controller.add_object(src, frequency, radio_filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/passive_gate/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "AGP", + "power" = unlocked, + "target_output" = target_pressure, + "regulate_mode" = regulate_mode, + "set_flow_rate" = set_flow_rate, + "sigtype" = "status" + ) + + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/binary/passive_gate/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/passive_gate/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + if("power" in signal.data) + unlocked = text2num(signal.data["power"]) + + if("power_toggle" in signal.data) + unlocked = !unlocked + + if("set_target_pressure" in signal.data) + target_pressure = between(0, text2num(signal.data["set_target_pressure"]), max_pressure_setting) + + if("set_regulate_mode" in signal.data) + regulate_mode = text2num(signal.data["set_regulate_mode"]) + + if("set_flow_rate" in signal.data) + regulate_mode = text2num(signal.data["set_flow_rate"]) + + if("status" in signal.data) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/binary/passive_gate/attack_hand(user as mob) + if(..()) + return + add_fingerprint(usr) + if(!allowed(user)) + to_chat(user, "Access denied.") + return + tgui_interact(user) + +/obj/machinery/atmospherics/binary/passive_gate/tgui_interact(mob/user, datum/tgui/ui) + if(stat & BROKEN) + return FALSE + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PressureRegulator", name) + ui.open() + +/obj/machinery/atmospherics/binary/passive_gate/tgui_data(mob/user) + // this is the data which will be sent to the ui + var/data[0] + + data = list( + "on" = unlocked, + "pressure_set" = round(target_pressure*100), //Nano UI can't handle rounded non-integers, apparently. + "max_pressure" = max_pressure_setting, + "input_pressure" = round(air1.return_pressure()*100), + "output_pressure" = round(air2.return_pressure()*100), + "regulate_mode" = regulate_mode, + "set_flow_rate" = round(set_flow_rate*10), + "last_flow_rate" = round(last_flow_rate*10), + ) + + return data + + +/obj/machinery/atmospherics/binary/passive_gate/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("toggle_valve") + . = TRUE + unlocked = !unlocked + if("regulate_mode") + . = TRUE + switch(params["mode"]) + if("off") regulate_mode = REGULATE_NONE + if("input") regulate_mode = REGULATE_INPUT + if("output") regulate_mode = REGULATE_OUTPUT + + if("set_press") + . = TRUE + switch(params["press"]) + if("min") + target_pressure = 0 + if("max") + target_pressure = max_pressure_setting + if("set") + var/new_pressure = tgui_input_number(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure Control",src.target_pressure,max_pressure_setting,0) + src.target_pressure = between(0, new_pressure, max_pressure_setting) + + if("set_flow_rate") + . = TRUE + switch(params["press"]) + if("min") + set_flow_rate = 0 + if("max") + set_flow_rate = air1.volume + if("set") + var/new_flow_rate = tgui_input_number(usr,"Enter new flow rate limit (0-[air1.volume]L/s)","Flow Rate Control",src.set_flow_rate,air1.volume,0) + src.set_flow_rate = between(0, new_flow_rate, air1.volume) + + update_icon() + add_fingerprint(usr) + +/obj/machinery/atmospherics/binary/passive_gate/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + if (unlocked) + to_chat(user, "You cannot unwrench \the [src], turn it off first.") + return 1 + if(!can_unwrench()) + to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear ratchet.") + deconstruct() + +#undef REGULATE_NONE +#undef REGULATE_INPUT +#undef REGULATE_OUTPUT diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm index d979f9decf4..3e22ea310d5 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm @@ -1,258 +1,258 @@ -/* -Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. - -node1, air1, network1 correspond to input -node2, air2, network2 correspond to output - -Thus, the two variables affect pump operation are set in New(): - air1.volume - This is the volume of gas available to the pump that may be transfered to the output - air2.volume - Higher quantities of this cause more air to be perfected later - but overall network volume is also increased as this increases... -*/ - -/obj/machinery/atmospherics/binary/pump - icon = 'icons/atmos/pump.dmi' - icon_state = "map_off" - construction_type = /obj/item/pipe/directional - pipe_state = "pump" - level = 1 - var/base_icon = "pump" - - name = "gas pump" - desc = "A pump that moves gas from one place to another." - - var/target_pressure = ONE_ATMOSPHERE - - //var/max_volume_transfer = 10000 - - use_power = USE_POWER_OFF - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 7500 //7500 W ~ 10 HP - - var/max_pressure_setting = 15000 //kPa - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/binary/pump/New() - ..() - air1.volume = ATMOS_DEFAULT_VOLUME_PUMP - air2.volume = ATMOS_DEFAULT_VOLUME_PUMP - -/obj/machinery/atmospherics/binary/pump/Destroy() - unregister_radio(src, frequency) - . = ..() - -/obj/machinery/atmospherics/binary/pump/on - icon_state = "map_on" - use_power = USE_POWER_IDLE - -/obj/machinery/atmospherics/binary/pump/fuel - icon_state = "map_off-fuel" - base_icon = "pump-fuel" - icon_connect_type = "-fuel" - connect_types = CONNECT_TYPE_FUEL - -/obj/machinery/atmospherics/binary/pump/fuel/on - icon_state = "map_on-fuel" - use_power = USE_POWER_IDLE - -/obj/machinery/atmospherics/binary/pump/aux - icon_state = "map_off-aux" - base_icon = "pump-aux" - icon_connect_type = "-aux" - connect_types = CONNECT_TYPE_AUX - -/obj/machinery/atmospherics/binary/pump/aux/on - icon_state = "map_on-aux" - use_power = USE_POWER_IDLE - -/obj/machinery/atmospherics/binary/pump/update_icon() - if(!powered()) - icon_state = "[base_icon]-off" - else - icon_state = "[use_power ? "[base_icon]-on" : "[base_icon]-off"]" - -/obj/machinery/atmospherics/binary/pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, -180), node1?.icon_connect_type) - add_underlay(T, node2, dir, node2?.icon_connect_type) - -/obj/machinery/atmospherics/binary/pump/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/binary/pump/process() - last_power_draw = 0 - last_flow_rate = 0 - - if((stat & (NOPOWER|BROKEN)) || !use_power) - return - - var/power_draw = -1 - var/pressure_delta = target_pressure - air2.return_pressure() - - if(pressure_delta > 0.01 && air1.temperature > 0) - //Figure out how much gas to transfer to meet the target pressure. - var/transfer_moles = calculate_transfer_moles(air1, air2, pressure_delta, (network2)? network2.volume : 0) - power_draw = pump_gas(src, air1, air2, transfer_moles, power_rating) - - if (power_draw >= 0) - last_power_draw = power_draw - use_power(power_draw) - - if(network1) - network1.update = 1 - - if(network2) - network2.update = 1 - - return 1 - -//Radio remote control - -/obj/machinery/atmospherics/binary/pump/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = radio_controller.add_object(src, frequency, radio_filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "AGP", - "power" = use_power, - "target_output" = target_pressure, - "sigtype" = "status" - ) - - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/binary/pump/tgui_interact(mob/user, datum/tgui/ui) - if(stat & (BROKEN|NOPOWER)) - return - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "GasPump", name) - ui.open() - -/obj/machinery/atmospherics/binary/pump/tgui_data(mob/user) - // this is the data which will be sent to the ui - var/data[0] - - data = list( - "on" = use_power, - "pressure_set" = round(target_pressure*100), //Nano UI can't handle rounded non-integers, apparently. - "max_pressure" = max_pressure_setting, - "last_flow_rate" = round(last_flow_rate*10), - "last_power_draw" = round(last_power_draw), - "max_power_draw" = power_rating, - ) - - return data - -/obj/machinery/atmospherics/binary/pump/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/pump/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - if(signal.data["power"]) - if(text2num(signal.data["power"])) - update_use_power(USE_POWER_IDLE) - else - update_use_power(USE_POWER_OFF) - - if("power_toggle" in signal.data) - update_use_power(!use_power) - - if(signal.data["set_output_pressure"]) - target_pressure = between(0, text2num(signal.data["set_output_pressure"]), ONE_ATMOSPHERE*50) - - if(signal.data["status"]) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - return - -/obj/machinery/atmospherics/binary/pump/attack_ghost(mob/user) - tgui_interact(user) - -/obj/machinery/atmospherics/binary/pump/attack_hand(mob/user) - if(..()) - return - add_fingerprint(usr) - if(!allowed(user)) - to_chat(user, "Access denied.") - return - tgui_interact(user) - -/obj/machinery/atmospherics/binary/pump/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("power") - update_use_power(!use_power) - . = TRUE - if("set_press") - var/press = params["press"] - switch(press) - if("min") - target_pressure = 0 - if("max") - target_pressure = max_pressure_setting - if("set") - var/new_pressure = tgui_input_number(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure control",src.target_pressure,max_pressure_setting,0) - src.target_pressure = between(0, new_pressure, max_pressure_setting) - . = TRUE - - add_fingerprint(usr) - update_icon() - -/obj/machinery/atmospherics/binary/pump/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/binary/pump/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.has_tool_quality(TOOL_WRENCH)) - return ..() - if (!(stat & NOPOWER) && use_power) - to_chat(user, "You cannot unwrench this [src], turn it off first.") - return 1 - if(!can_unwrench()) - to_chat(user, "You cannot unwrench this [src], it too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear ratchet.") - deconstruct() +/* +Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. + +node1, air1, network1 correspond to input +node2, air2, network2 correspond to output + +Thus, the two variables affect pump operation are set in New(): + air1.volume + This is the volume of gas available to the pump that may be transfered to the output + air2.volume + Higher quantities of this cause more air to be perfected later + but overall network volume is also increased as this increases... +*/ + +/obj/machinery/atmospherics/binary/pump + icon = 'icons/atmos/pump.dmi' + icon_state = "map_off" + construction_type = /obj/item/pipe/directional + pipe_state = "pump" + level = 1 + var/base_icon = "pump" + + name = "gas pump" + desc = "A pump that moves gas from one place to another." + + var/target_pressure = ONE_ATMOSPHERE + + //var/max_volume_transfer = 10000 + + use_power = USE_POWER_OFF + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 7500 //7500 W ~ 10 HP + + var/max_pressure_setting = 15000 //kPa + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/binary/pump/New() + ..() + air1.volume = ATMOS_DEFAULT_VOLUME_PUMP + air2.volume = ATMOS_DEFAULT_VOLUME_PUMP + +/obj/machinery/atmospherics/binary/pump/Destroy() + unregister_radio(src, frequency) + . = ..() + +/obj/machinery/atmospherics/binary/pump/on + icon_state = "map_on" + use_power = USE_POWER_IDLE + +/obj/machinery/atmospherics/binary/pump/fuel + icon_state = "map_off-fuel" + base_icon = "pump-fuel" + icon_connect_type = "-fuel" + connect_types = CONNECT_TYPE_FUEL + +/obj/machinery/atmospherics/binary/pump/fuel/on + icon_state = "map_on-fuel" + use_power = USE_POWER_IDLE + +/obj/machinery/atmospherics/binary/pump/aux + icon_state = "map_off-aux" + base_icon = "pump-aux" + icon_connect_type = "-aux" + connect_types = CONNECT_TYPE_AUX + +/obj/machinery/atmospherics/binary/pump/aux/on + icon_state = "map_on-aux" + use_power = USE_POWER_IDLE + +/obj/machinery/atmospherics/binary/pump/update_icon() + if(!powered()) + icon_state = "[base_icon]-off" + else + icon_state = "[use_power ? "[base_icon]-on" : "[base_icon]-off"]" + +/obj/machinery/atmospherics/binary/pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, -180), node1?.icon_connect_type) + add_underlay(T, node2, dir, node2?.icon_connect_type) + +/obj/machinery/atmospherics/binary/pump/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/binary/pump/process() + last_power_draw = 0 + last_flow_rate = 0 + + if((stat & (NOPOWER|BROKEN)) || !use_power) + return + + var/power_draw = -1 + var/pressure_delta = target_pressure - air2.return_pressure() + + if(pressure_delta > 0.01 && air1.temperature > 0) + //Figure out how much gas to transfer to meet the target pressure. + var/transfer_moles = calculate_transfer_moles(air1, air2, pressure_delta, (network2)? network2.volume : 0) + power_draw = pump_gas(src, air1, air2, transfer_moles, power_rating) + + if (power_draw >= 0) + last_power_draw = power_draw + use_power(power_draw) + + if(network1) + network1.update = 1 + + if(network2) + network2.update = 1 + + return 1 + +//Radio remote control + +/obj/machinery/atmospherics/binary/pump/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = radio_controller.add_object(src, frequency, radio_filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "AGP", + "power" = use_power, + "target_output" = target_pressure, + "sigtype" = "status" + ) + + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/binary/pump/tgui_interact(mob/user, datum/tgui/ui) + if(stat & (BROKEN|NOPOWER)) + return + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "GasPump", name) + ui.open() + +/obj/machinery/atmospherics/binary/pump/tgui_data(mob/user) + // this is the data which will be sent to the ui + var/data[0] + + data = list( + "on" = use_power, + "pressure_set" = round(target_pressure*100), //Nano UI can't handle rounded non-integers, apparently. + "max_pressure" = max_pressure_setting, + "last_flow_rate" = round(last_flow_rate*10), + "last_power_draw" = round(last_power_draw), + "max_power_draw" = power_rating, + ) + + return data + +/obj/machinery/atmospherics/binary/pump/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + if(signal.data["power"]) + if(text2num(signal.data["power"])) + update_use_power(USE_POWER_IDLE) + else + update_use_power(USE_POWER_OFF) + + if("power_toggle" in signal.data) + update_use_power(!use_power) + + if(signal.data["set_output_pressure"]) + target_pressure = between(0, text2num(signal.data["set_output_pressure"]), ONE_ATMOSPHERE*50) + + if(signal.data["status"]) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/binary/pump/attack_ghost(mob/user) + tgui_interact(user) + +/obj/machinery/atmospherics/binary/pump/attack_hand(mob/user) + if(..()) + return + add_fingerprint(usr) + if(!allowed(user)) + to_chat(user, "Access denied.") + return + tgui_interact(user) + +/obj/machinery/atmospherics/binary/pump/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("power") + update_use_power(!use_power) + . = TRUE + if("set_press") + var/press = params["press"] + switch(press) + if("min") + target_pressure = 0 + if("max") + target_pressure = max_pressure_setting + if("set") + var/new_pressure = tgui_input_number(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure control",src.target_pressure,max_pressure_setting,0) + src.target_pressure = between(0, new_pressure, max_pressure_setting) + . = TRUE + + add_fingerprint(usr) + update_icon() + +/obj/machinery/atmospherics/binary/pump/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/binary/pump/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + if (!(stat & NOPOWER) && use_power) + to_chat(user, "You cannot unwrench this [src], turn it off first.") + return 1 + if(!can_unwrench()) + to_chat(user, "You cannot unwrench this [src], it too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear ratchet.") + deconstruct() diff --git a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm index 8e827505885..135e5a1194f 100644 --- a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm @@ -1,21 +1,21 @@ -/obj/machinery/atmospherics/binary/pump/high_power - icon = 'icons/atmos/volume_pump.dmi' - icon_state = "map_off" - construction_type = /obj/item/pipe/directional - pipe_state = "volumepump" - level = 1 - - name = "high power gas pump" - desc = "A pump that moves gas from one place to another. Has double the power rating of the standard gas pump." - - power_rating = 15000 //15000 W ~ 20 HP - -/obj/machinery/atmospherics/binary/pump/high_power/on - use_power = USE_POWER_IDLE - icon_state = "map_on" - -/obj/machinery/atmospherics/binary/pump/high_power/update_icon() - if(!powered()) - icon_state = "off" - else +/obj/machinery/atmospherics/binary/pump/high_power + icon = 'icons/atmos/volume_pump.dmi' + icon_state = "map_off" + construction_type = /obj/item/pipe/directional + pipe_state = "volumepump" + level = 1 + + name = "high power gas pump" + desc = "A pump that moves gas from one place to another. Has double the power rating of the standard gas pump." + + power_rating = 15000 //15000 W ~ 20 HP + +/obj/machinery/atmospherics/binary/pump/high_power/on + use_power = USE_POWER_IDLE + icon_state = "map_on" + +/obj/machinery/atmospherics/binary/pump/high_power/update_icon() + if(!powered()) + icon_state = "off" + else icon_state = "[use_power ? "on" : "off"]" \ No newline at end of file diff --git a/code/ATMOSPHERICS/components/portables_connector.dm b/code/ATMOSPHERICS/components/portables_connector.dm index 84b39b95c38..5d315cf0a05 100644 --- a/code/ATMOSPHERICS/components/portables_connector.dm +++ b/code/ATMOSPHERICS/components/portables_connector.dm @@ -1,168 +1,168 @@ -/obj/machinery/atmospherics/portables_connector - icon = 'icons/atmos/connector.dmi' - icon_state = "map_connector" - - name = "Connector Port" - desc = "For connecting portables devices related to atmospherics control." - - dir = SOUTH - initialize_directions = SOUTH - construction_type = /obj/item/pipe/directional - pipe_state = "connector" - pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF - - var/obj/machinery/portable_atmospherics/connected_device - - var/obj/machinery/atmospherics/node - - var/datum/pipe_network/network - - var/on = 0 - use_power = USE_POWER_OFF - level = 1 - -/obj/machinery/atmospherics/portables_connector/fuel - icon_state = "map_connector-fuel" - pipe_state = "connector-fuel" - icon_connect_type = "-fuel" - pipe_flags = PIPING_ONE_PER_TURF - connect_types = CONNECT_TYPE_FUEL - -/obj/machinery/atmospherics/portables_connector/aux - icon_state = "map_connector-aux" - pipe_state = "connector-aux" - icon_connect_type = "-aux" - pipe_flags = PIPING_ONE_PER_TURF - connect_types = CONNECT_TYPE_AUX - -/obj/machinery/atmospherics/portables_connector/init_dir() - initialize_directions = dir - -/obj/machinery/atmospherics/portables_connector/update_icon() - icon_state = "connector" - -/obj/machinery/atmospherics/portables_connector/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node, dir, node?.icon_connect_type) - -/obj/machinery/atmospherics/portables_connector/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/portables_connector/process() - ..() - if(!on) - return - if(!connected_device) - on = 0 - return - if(network) - network.update = 1 - return 1 - -// Housekeeping and pipe network stuff below -/obj/machinery/atmospherics/portables_connector/get_neighbor_nodes_for_init() - return list(node) - -/obj/machinery/atmospherics/portables_connector/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - if(reference == node) - network = new_network - - if(new_network.normal_members.Find(src)) - return 0 - - new_network.normal_members += src - - return null - -/obj/machinery/atmospherics/portables_connector/Destroy() - . = ..() - - if(connected_device) - connected_device.disconnect() - - if(node) - node.disconnect(src) - qdel(network) - - node = null - -/obj/machinery/atmospherics/portables_connector/atmos_init() - if(node) - return - - var/node_connect = dir - - for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) - if(can_be_node(target, 1)) - node = target - break - - update_icon() - update_underlays() - -/obj/machinery/atmospherics/portables_connector/build_network() - if(!network && node) - network = new /datum/pipe_network() - network.normal_members += src - network.build_network(node, src) - - -/obj/machinery/atmospherics/portables_connector/return_network(obj/machinery/atmospherics/reference) - build_network() - - if(reference==node) - return network - - if(reference==connected_device) - return network - - return null - -/obj/machinery/atmospherics/portables_connector/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) - if(network == old_network) - network = new_network - - return 1 - -/obj/machinery/atmospherics/portables_connector/return_network_air(datum/pipe_network/reference) - var/list/results = list() - - if(connected_device) - results += connected_device.air_contents - - return results - -/obj/machinery/atmospherics/portables_connector/disconnect(obj/machinery/atmospherics/reference) - if(reference==node) - qdel(network) - node = null - - update_underlays() - - return null - - -/obj/machinery/atmospherics/portables_connector/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.has_tool_quality(TOOL_WRENCH)) - return ..() - if (connected_device) - to_chat(user, "You cannot unwrench \the [src], dettach \the [connected_device] first.") - return 1 - if (locate(/obj/machinery/portable_atmospherics, src.loc)) - return 1 - if(!can_unwrench()) - to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - deconstruct() +/obj/machinery/atmospherics/portables_connector + icon = 'icons/atmos/connector.dmi' + icon_state = "map_connector" + + name = "Connector Port" + desc = "For connecting portables devices related to atmospherics control." + + dir = SOUTH + initialize_directions = SOUTH + construction_type = /obj/item/pipe/directional + pipe_state = "connector" + pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF + + var/obj/machinery/portable_atmospherics/connected_device + + var/obj/machinery/atmospherics/node + + var/datum/pipe_network/network + + var/on = 0 + use_power = USE_POWER_OFF + level = 1 + +/obj/machinery/atmospherics/portables_connector/fuel + icon_state = "map_connector-fuel" + pipe_state = "connector-fuel" + icon_connect_type = "-fuel" + pipe_flags = PIPING_ONE_PER_TURF + connect_types = CONNECT_TYPE_FUEL + +/obj/machinery/atmospherics/portables_connector/aux + icon_state = "map_connector-aux" + pipe_state = "connector-aux" + icon_connect_type = "-aux" + pipe_flags = PIPING_ONE_PER_TURF + connect_types = CONNECT_TYPE_AUX + +/obj/machinery/atmospherics/portables_connector/init_dir() + initialize_directions = dir + +/obj/machinery/atmospherics/portables_connector/update_icon() + icon_state = "connector" + +/obj/machinery/atmospherics/portables_connector/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node, dir, node?.icon_connect_type) + +/obj/machinery/atmospherics/portables_connector/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/portables_connector/process() + ..() + if(!on) + return + if(!connected_device) + on = 0 + return + if(network) + network.update = 1 + return 1 + +// Housekeeping and pipe network stuff below +/obj/machinery/atmospherics/portables_connector/get_neighbor_nodes_for_init() + return list(node) + +/obj/machinery/atmospherics/portables_connector/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + if(reference == node) + network = new_network + + if(new_network.normal_members.Find(src)) + return 0 + + new_network.normal_members += src + + return null + +/obj/machinery/atmospherics/portables_connector/Destroy() + . = ..() + + if(connected_device) + connected_device.disconnect() + + if(node) + node.disconnect(src) + qdel(network) + + node = null + +/obj/machinery/atmospherics/portables_connector/atmos_init() + if(node) + return + + var/node_connect = dir + + for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) + if(can_be_node(target, 1)) + node = target + break + + update_icon() + update_underlays() + +/obj/machinery/atmospherics/portables_connector/build_network() + if(!network && node) + network = new /datum/pipe_network() + network.normal_members += src + network.build_network(node, src) + + +/obj/machinery/atmospherics/portables_connector/return_network(obj/machinery/atmospherics/reference) + build_network() + + if(reference==node) + return network + + if(reference==connected_device) + return network + + return null + +/obj/machinery/atmospherics/portables_connector/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + if(network == old_network) + network = new_network + + return 1 + +/obj/machinery/atmospherics/portables_connector/return_network_air(datum/pipe_network/reference) + var/list/results = list() + + if(connected_device) + results += connected_device.air_contents + + return results + +/obj/machinery/atmospherics/portables_connector/disconnect(obj/machinery/atmospherics/reference) + if(reference==node) + qdel(network) + node = null + + update_underlays() + + return null + + +/obj/machinery/atmospherics/portables_connector/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + if (connected_device) + to_chat(user, "You cannot unwrench \the [src], dettach \the [connected_device] first.") + return 1 + if (locate(/obj/machinery/portable_atmospherics, src.loc)) + return 1 + if(!can_unwrench()) + to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + deconstruct() diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm index 48085569bc9..18ade9acefe 100755 --- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm @@ -1,231 +1,231 @@ -/obj/machinery/atmospherics/trinary/atmos_filter - icon = 'icons/atmos/filter.dmi' - icon_state = "map" - construction_type = /obj/item/pipe/trinary/flippable - pipe_state = "filter" - density = FALSE - level = 1 - - name = "Gas filter" - desc = "Filters one type of gas from an input, and pushes it out the side." - - use_power = USE_POWER_IDLE - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 7500 //This also doubles as a measure of how powerful the filter is, in Watts. 7500 W ~ 10 HP - - var/temp = null // -- TLE - - var/set_flow_rate = ATMOS_DEFAULT_VOLUME_FILTER - - /* - Filter types: - -1: Nothing - 0: Phoron: Phoron, Oxygen Agent B - 1: Oxygen: Oxygen ONLY - 2: Nitrogen: Nitrogen ONLY - 3: Carbon Dioxide: Carbon Dioxide ONLY - 4: Nitrous Oxide (Formerly called Sleeping Agent) (N2O) - */ - var/filter_type = -1 - var/list/filtered_out = list() - - - var/frequency = 0 - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/trinary/atmos_filter/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/atmospherics/trinary/atmos_filter/New() - ..() - switch(filter_type) - if(0) //removing hydrocarbons - filtered_out = list("phoron") - if(1) //removing O2 - filtered_out = list("oxygen") - if(2) //removing N2 - filtered_out = list("nitrogen") - if(3) //removing CO2 - filtered_out = list("carbon_dioxide") - if(4)//removing N2O - filtered_out = list("nitrous_oxide") - - air1.volume = ATMOS_DEFAULT_VOLUME_FILTER - air2.volume = ATMOS_DEFAULT_VOLUME_FILTER - air3.volume = ATMOS_DEFAULT_VOLUME_FILTER - -/obj/machinery/atmospherics/trinary/atmos_filter/Destroy() - unregister_radio(src, frequency) - . = ..() - -/obj/machinery/atmospherics/trinary/atmos_filter/update_icon() - if(mirrored) - icon_state = "m" - else - icon_state = "" - - if(!powered()) - icon_state += "off" - else if(node2 && node3 && node1) - icon_state += use_power ? "on" : "off" - else - icon_state += "off" - update_use_power(USE_POWER_OFF) - -/obj/machinery/atmospherics/trinary/atmos_filter/process() - ..() - - last_power_draw = 0 - last_flow_rate = 0 - - if((stat & (NOPOWER|BROKEN)) || !use_power) - return - - //Figure out the amount of moles to transfer - var/transfer_moles = (set_flow_rate/air1.volume)*air1.total_moles - - var/power_draw = -1 - if (transfer_moles > MINIMUM_MOLES_TO_FILTER) - power_draw = filter_gas(src, filtered_out, air1, air2, air3, transfer_moles, power_rating) - - if(network2) - network2.update = 1 - - if(network3) - network3.update = 1 - - if(network1) - network1.update = 1 - - if (power_draw >= 0) - last_power_draw = power_draw - use_power(power_draw) - - return 1 - -/obj/machinery/atmospherics/trinary/atmos_filter/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/trinary/atmos_filter/attack_hand(user) // -- TLE - if(..()) - return - - if(!src.allowed(user)) - to_chat(user, "Access denied.") - return - - tgui_interact(user) - - // var/dat - // var/current_filter_type - // switch(filter_type) - // if(0) - // current_filter_type = "Phoron" - // if(1) - // current_filter_type = "Oxygen" - // if(2) - // current_filter_type = "Nitrogen" - // if(3) - // current_filter_type = "Carbon Dioxide" - // if(4) - // current_filter_type = "Nitrous Oxide" - // if(-1) - // current_filter_type = "Nothing" - // else - // current_filter_type = "ERROR - Report this bug to the admin, please!" - - // dat += {" - // Power: [use_power?"On":"Off"]
- // Filtering: [current_filter_type]

- //

Set Filter Type:

- // Phoron
- // Oxygen
- // Nitrogen
- // Carbon Dioxide
- // Nitrous Oxide
- // Nothing
- //
- // Set Flow Rate Limit: - // [src.set_flow_rate]L/s | Change
- // Flow rate: [round(last_flow_rate, 0.1)]L/s - // "} - - // user << browse("[src.name] control[dat]", "window=atmos_filter") - // onclose(user, "atmos_filter") - - - -/obj/machinery/atmospherics/trinary/atmos_filter/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AtmosFilter", name) - ui.open() - -/obj/machinery/atmospherics/trinary/atmos_filter/tgui_data(mob/user) - var/list/data = list() - - data["on"] = use_power - data["rate"] = set_flow_rate - data["max_rate"] = air1.volume - data["last_flow_rate"] = round(last_flow_rate, 0.1) - - data["filter_types"] = list() - data["filter_types"] += list(list("name" = "Nothing", "f_type" = -1, "selected" = filter_type == -1)) - data["filter_types"] += list(list("name" = "Phoron", "f_type" = 0, "selected" = filter_type == 0)) - data["filter_types"] += list(list("name" = "Oxygen", "f_type" = 1, "selected" = filter_type == 1)) - data["filter_types"] += list(list("name" = "Nitrogen", "f_type" = 2, "selected" = filter_type == 2)) - data["filter_types"] += list(list("name" = "Carbon Dioxide", "f_type" = 3, "selected" = filter_type == 3)) - data["filter_types"] += list(list("name" = "Nitrous Oxide", "f_type" = 4, "selected" = filter_type == 4)) - - return data - -/obj/machinery/atmospherics/trinary/atmos_filter/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("power") - update_use_power(!use_power) - if("rate") - var/rate = params["rate"] - if(rate == "max") - rate = air1.volume - . = TRUE - else if(text2num(rate) != null) - rate = text2num(rate) - . = TRUE - if(.) - set_flow_rate = clamp(rate, 0, air1.volume) - if("filter") - . = TRUE - filter_type = text2num(params["filterset"]) - filtered_out.Cut() //no need to create new lists unnecessarily - switch(filter_type) - if(0) //removing hydrocarbons - filtered_out += "phoron" - filtered_out += "oxygen_agent_b" - if(1) //removing O2 - filtered_out += "oxygen" - if(2) //removing N2 - filtered_out += "nitrogen" - if(3) //removing CO2 - filtered_out += "carbon_dioxide" - if(4)//removing N2O - filtered_out += "nitrous_oxide" - - add_fingerprint(usr) - update_icon() - -// -// Mirrored Orientation - Flips the output dir to opposite side from normal. -// -/obj/machinery/atmospherics/trinary/atmos_filter/m_filter - icon_state = "mmap" - dir = SOUTH - initialize_directions = SOUTH|NORTH|EAST - mirrored = TRUE +/obj/machinery/atmospherics/trinary/atmos_filter + icon = 'icons/atmos/filter.dmi' + icon_state = "map" + construction_type = /obj/item/pipe/trinary/flippable + pipe_state = "filter" + density = FALSE + level = 1 + + name = "Gas filter" + desc = "Filters one type of gas from an input, and pushes it out the side." + + use_power = USE_POWER_IDLE + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 7500 //This also doubles as a measure of how powerful the filter is, in Watts. 7500 W ~ 10 HP + + var/temp = null // -- TLE + + var/set_flow_rate = ATMOS_DEFAULT_VOLUME_FILTER + + /* + Filter types: + -1: Nothing + 0: Phoron: Phoron, Oxygen Agent B + 1: Oxygen: Oxygen ONLY + 2: Nitrogen: Nitrogen ONLY + 3: Carbon Dioxide: Carbon Dioxide ONLY + 4: Nitrous Oxide (Formerly called Sleeping Agent) (N2O) + */ + var/filter_type = -1 + var/list/filtered_out = list() + + + var/frequency = 0 + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/trinary/atmos_filter/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/atmospherics/trinary/atmos_filter/New() + ..() + switch(filter_type) + if(0) //removing hydrocarbons + filtered_out = list("phoron") + if(1) //removing O2 + filtered_out = list("oxygen") + if(2) //removing N2 + filtered_out = list("nitrogen") + if(3) //removing CO2 + filtered_out = list("carbon_dioxide") + if(4)//removing N2O + filtered_out = list("nitrous_oxide") + + air1.volume = ATMOS_DEFAULT_VOLUME_FILTER + air2.volume = ATMOS_DEFAULT_VOLUME_FILTER + air3.volume = ATMOS_DEFAULT_VOLUME_FILTER + +/obj/machinery/atmospherics/trinary/atmos_filter/Destroy() + unregister_radio(src, frequency) + . = ..() + +/obj/machinery/atmospherics/trinary/atmos_filter/update_icon() + if(mirrored) + icon_state = "m" + else + icon_state = "" + + if(!powered()) + icon_state += "off" + else if(node2 && node3 && node1) + icon_state += use_power ? "on" : "off" + else + icon_state += "off" + update_use_power(USE_POWER_OFF) + +/obj/machinery/atmospherics/trinary/atmos_filter/process() + ..() + + last_power_draw = 0 + last_flow_rate = 0 + + if((stat & (NOPOWER|BROKEN)) || !use_power) + return + + //Figure out the amount of moles to transfer + var/transfer_moles = (set_flow_rate/air1.volume)*air1.total_moles + + var/power_draw = -1 + if (transfer_moles > MINIMUM_MOLES_TO_FILTER) + power_draw = filter_gas(src, filtered_out, air1, air2, air3, transfer_moles, power_rating) + + if(network2) + network2.update = 1 + + if(network3) + network3.update = 1 + + if(network1) + network1.update = 1 + + if (power_draw >= 0) + last_power_draw = power_draw + use_power(power_draw) + + return 1 + +/obj/machinery/atmospherics/trinary/atmos_filter/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/trinary/atmos_filter/attack_hand(user) // -- TLE + if(..()) + return + + if(!src.allowed(user)) + to_chat(user, "Access denied.") + return + + tgui_interact(user) + + // var/dat + // var/current_filter_type + // switch(filter_type) + // if(0) + // current_filter_type = "Phoron" + // if(1) + // current_filter_type = "Oxygen" + // if(2) + // current_filter_type = "Nitrogen" + // if(3) + // current_filter_type = "Carbon Dioxide" + // if(4) + // current_filter_type = "Nitrous Oxide" + // if(-1) + // current_filter_type = "Nothing" + // else + // current_filter_type = "ERROR - Report this bug to the admin, please!" + + // dat += {" + // Power: [use_power?"On":"Off"]
+ // Filtering: [current_filter_type]

+ //

Set Filter Type:

+ // Phoron
+ // Oxygen
+ // Nitrogen
+ // Carbon Dioxide
+ // Nitrous Oxide
+ // Nothing
+ //
+ // Set Flow Rate Limit: + // [src.set_flow_rate]L/s | Change
+ // Flow rate: [round(last_flow_rate, 0.1)]L/s + // "} + + // user << browse("[src.name] control[dat]", "window=atmos_filter") + // onclose(user, "atmos_filter") + + + +/obj/machinery/atmospherics/trinary/atmos_filter/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AtmosFilter", name) + ui.open() + +/obj/machinery/atmospherics/trinary/atmos_filter/tgui_data(mob/user) + var/list/data = list() + + data["on"] = use_power + data["rate"] = set_flow_rate + data["max_rate"] = air1.volume + data["last_flow_rate"] = round(last_flow_rate, 0.1) + + data["filter_types"] = list() + data["filter_types"] += list(list("name" = "Nothing", "f_type" = -1, "selected" = filter_type == -1)) + data["filter_types"] += list(list("name" = "Phoron", "f_type" = 0, "selected" = filter_type == 0)) + data["filter_types"] += list(list("name" = "Oxygen", "f_type" = 1, "selected" = filter_type == 1)) + data["filter_types"] += list(list("name" = "Nitrogen", "f_type" = 2, "selected" = filter_type == 2)) + data["filter_types"] += list(list("name" = "Carbon Dioxide", "f_type" = 3, "selected" = filter_type == 3)) + data["filter_types"] += list(list("name" = "Nitrous Oxide", "f_type" = 4, "selected" = filter_type == 4)) + + return data + +/obj/machinery/atmospherics/trinary/atmos_filter/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("power") + update_use_power(!use_power) + if("rate") + var/rate = params["rate"] + if(rate == "max") + rate = air1.volume + . = TRUE + else if(text2num(rate) != null) + rate = text2num(rate) + . = TRUE + if(.) + set_flow_rate = clamp(rate, 0, air1.volume) + if("filter") + . = TRUE + filter_type = text2num(params["filterset"]) + filtered_out.Cut() //no need to create new lists unnecessarily + switch(filter_type) + if(0) //removing hydrocarbons + filtered_out += "phoron" + filtered_out += "oxygen_agent_b" + if(1) //removing O2 + filtered_out += "oxygen" + if(2) //removing N2 + filtered_out += "nitrogen" + if(3) //removing CO2 + filtered_out += "carbon_dioxide" + if(4)//removing N2O + filtered_out += "nitrous_oxide" + + add_fingerprint(usr) + update_icon() + +// +// Mirrored Orientation - Flips the output dir to opposite side from normal. +// +/obj/machinery/atmospherics/trinary/atmos_filter/m_filter + icon_state = "mmap" + dir = SOUTH + initialize_directions = SOUTH|NORTH|EAST + mirrored = TRUE diff --git a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm index 8cecbc6c651..7e06e5de821 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm @@ -1,181 +1,181 @@ -/obj/machinery/atmospherics/trinary/mixer - icon = 'icons/atmos/mixer.dmi' - icon_state = "map" - construction_type = /obj/item/pipe/trinary/flippable - pipe_state = "mixer" - density = FALSE - level = 1 - - name = "Gas mixer" - - use_power = USE_POWER_IDLE - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 3700 //This also doubles as a measure of how powerful the mixer is, in Watts. 3700 W ~ 5 HP - - var/set_flow_rate = ATMOS_DEFAULT_VOLUME_MIXER - var/list/mixing_inputs - - //for mapping - var/node1_concentration = 0.5 - var/node2_concentration = 0.5 - - //node 3 is the outlet, nodes 1 & 2 are intakes - -/obj/machinery/atmospherics/trinary/mixer/update_icon(var/safety = 0) - if(tee) - icon_state = "t" - else if(mirrored) - icon_state = "m" - else - icon_state = "" - - if(!powered()) - icon_state += "off" - else if(node2 && node3 && node1) - icon_state += use_power ? "on" : "off" - else - icon_state += "off" - update_use_power(USE_POWER_OFF) - -/obj/machinery/atmospherics/trinary/mixer/New() - ..() - air1.volume = ATMOS_DEFAULT_VOLUME_MIXER - air2.volume = ATMOS_DEFAULT_VOLUME_MIXER - air3.volume = ATMOS_DEFAULT_VOLUME_MIXER * 1.5 - - if (!mixing_inputs) - mixing_inputs = list(src.air1 = node1_concentration, src.air2 = node2_concentration) - -/obj/machinery/atmospherics/trinary/mixer/process() - ..() - - last_power_draw = 0 - last_flow_rate = 0 - - if((stat & (NOPOWER|BROKEN)) || !use_power) - return - - //Figure out the amount of moles to transfer - var/transfer_moles = (set_flow_rate*mixing_inputs[air1]/air1.volume)*air1.total_moles + (set_flow_rate*mixing_inputs[air1]/air2.volume)*air2.total_moles - - var/power_draw = -1 - if (transfer_moles > MINIMUM_MOLES_TO_FILTER) - power_draw = mix_gas(src, mixing_inputs, air3, transfer_moles, power_rating) - - if(network1 && mixing_inputs[air1]) - network1.update = 1 - - if(network2 && mixing_inputs[air2]) - network2.update = 1 - - if(network3) - network3.update = 1 - - if (power_draw >= 0) - last_power_draw = power_draw - use_power(power_draw) - - return 1 - -/obj/machinery/atmospherics/trinary/mixer/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AtmosMixer", name) - ui.open() - -/obj/machinery/atmospherics/trinary/mixer/tgui_data(mob/user) - var/list/data = list() - data["on"] = use_power - data["set_pressure"] = round(set_flow_rate) - data["max_pressure"] = min(air1.volume, air2.volume) - data["node1_concentration"] = round(mixing_inputs[air1]*100, 1) - data["node2_concentration"] = round(mixing_inputs[air2]*100, 1) - var/list/node_connects = get_node_connect_dirs() - data["node1_dir"] = dir_name(node_connects[1],TRUE) - data["node2_dir"] = dir_name(node_connects[2],TRUE) - return data - -/obj/machinery/atmospherics/trinary/mixer/attack_hand(user as mob) - if(..()) - return - tgui_interact(user) - // src.add_fingerprint(usr) - // if(!src.allowed(user)) - // to_chat(user, "Access denied.") - // return - // usr.set_machine(src) - // var/list/node_connects = get_node_connect_dirs() - // var/dat = {"Power: [use_power?"On":"Off"]
- // Set Flow Rate Limit: - // [set_flow_rate]L/s | Change - //
- // Flow Rate: [round(last_flow_rate, 0.1)]L/s - //

- // Node 1 ([dir_name(node_connects[1],TRUE)]) Concentration: - // - - // - - // [mixing_inputs[air1]]([mixing_inputs[air1]*100]%) - // + - // + - //
- // Node 2 ([dir_name(node_connects[2],TRUE)]) Concentration: - // - - // - - // [mixing_inputs[air2]]([mixing_inputs[air2]*100]%) - // + - // + - // "} - - // user << browse("[src.name] control[dat]", "window=atmo_mixer") - // onclose(user, "atmo_mixer") - // return - -/obj/machinery/atmospherics/trinary/mixer/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("power") - update_use_power(!use_power) - . = TRUE - if("pressure") - var/pressure = params["pressure"] - if(pressure == "max") - pressure = min(air1.volume, air2.volume) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - set_flow_rate = clamp(pressure, 0, min(air1.volume, air2.volume)) - if("node1") - var/value = text2num(params["concentration"]) - mixing_inputs[air1] = max(0, min(1, value / 100)) - mixing_inputs[air2] = 1.0 - mixing_inputs[air1] - . = TRUE - if("node2") - var/value = text2num(params["concentration"]) - mixing_inputs[air2] = max(0, min(1, value / 100)) - mixing_inputs[air1] = 1.0 - mixing_inputs[air2] - . = TRUE - update_icon() - -// -// "T" Orientation - Inputs are on oposite sides instead of adjacent -// -/obj/machinery/atmospherics/trinary/mixer/t_mixer - icon_state = "tmap" - construction_type = /obj/item/pipe/trinary // Can't flip a "T", its symmetrical - pipe_state = "t_mixer" - dir = SOUTH - initialize_directions = SOUTH|EAST|WEST - tee = TRUE - -// -// Mirrored Orientation - Flips the output dir to opposite side from normal. -// -/obj/machinery/atmospherics/trinary/mixer/m_mixer - icon_state = "mmap" - dir = SOUTH - initialize_directions = SOUTH|NORTH|EAST - mirrored = TRUE +/obj/machinery/atmospherics/trinary/mixer + icon = 'icons/atmos/mixer.dmi' + icon_state = "map" + construction_type = /obj/item/pipe/trinary/flippable + pipe_state = "mixer" + density = FALSE + level = 1 + + name = "Gas mixer" + + use_power = USE_POWER_IDLE + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 3700 //This also doubles as a measure of how powerful the mixer is, in Watts. 3700 W ~ 5 HP + + var/set_flow_rate = ATMOS_DEFAULT_VOLUME_MIXER + var/list/mixing_inputs + + //for mapping + var/node1_concentration = 0.5 + var/node2_concentration = 0.5 + + //node 3 is the outlet, nodes 1 & 2 are intakes + +/obj/machinery/atmospherics/trinary/mixer/update_icon(var/safety = 0) + if(tee) + icon_state = "t" + else if(mirrored) + icon_state = "m" + else + icon_state = "" + + if(!powered()) + icon_state += "off" + else if(node2 && node3 && node1) + icon_state += use_power ? "on" : "off" + else + icon_state += "off" + update_use_power(USE_POWER_OFF) + +/obj/machinery/atmospherics/trinary/mixer/New() + ..() + air1.volume = ATMOS_DEFAULT_VOLUME_MIXER + air2.volume = ATMOS_DEFAULT_VOLUME_MIXER + air3.volume = ATMOS_DEFAULT_VOLUME_MIXER * 1.5 + + if (!mixing_inputs) + mixing_inputs = list(src.air1 = node1_concentration, src.air2 = node2_concentration) + +/obj/machinery/atmospherics/trinary/mixer/process() + ..() + + last_power_draw = 0 + last_flow_rate = 0 + + if((stat & (NOPOWER|BROKEN)) || !use_power) + return + + //Figure out the amount of moles to transfer + var/transfer_moles = (set_flow_rate*mixing_inputs[air1]/air1.volume)*air1.total_moles + (set_flow_rate*mixing_inputs[air1]/air2.volume)*air2.total_moles + + var/power_draw = -1 + if (transfer_moles > MINIMUM_MOLES_TO_FILTER) + power_draw = mix_gas(src, mixing_inputs, air3, transfer_moles, power_rating) + + if(network1 && mixing_inputs[air1]) + network1.update = 1 + + if(network2 && mixing_inputs[air2]) + network2.update = 1 + + if(network3) + network3.update = 1 + + if (power_draw >= 0) + last_power_draw = power_draw + use_power(power_draw) + + return 1 + +/obj/machinery/atmospherics/trinary/mixer/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AtmosMixer", name) + ui.open() + +/obj/machinery/atmospherics/trinary/mixer/tgui_data(mob/user) + var/list/data = list() + data["on"] = use_power + data["set_pressure"] = round(set_flow_rate) + data["max_pressure"] = min(air1.volume, air2.volume) + data["node1_concentration"] = round(mixing_inputs[air1]*100, 1) + data["node2_concentration"] = round(mixing_inputs[air2]*100, 1) + var/list/node_connects = get_node_connect_dirs() + data["node1_dir"] = dir_name(node_connects[1],TRUE) + data["node2_dir"] = dir_name(node_connects[2],TRUE) + return data + +/obj/machinery/atmospherics/trinary/mixer/attack_hand(user as mob) + if(..()) + return + tgui_interact(user) + // src.add_fingerprint(usr) + // if(!src.allowed(user)) + // to_chat(user, "Access denied.") + // return + // usr.set_machine(src) + // var/list/node_connects = get_node_connect_dirs() + // var/dat = {"Power: [use_power?"On":"Off"]
+ // Set Flow Rate Limit: + // [set_flow_rate]L/s | Change + //
+ // Flow Rate: [round(last_flow_rate, 0.1)]L/s + //

+ // Node 1 ([dir_name(node_connects[1],TRUE)]) Concentration: + // - + // - + // [mixing_inputs[air1]]([mixing_inputs[air1]*100]%) + // + + // + + //
+ // Node 2 ([dir_name(node_connects[2],TRUE)]) Concentration: + // - + // - + // [mixing_inputs[air2]]([mixing_inputs[air2]*100]%) + // + + // + + // "} + + // user << browse("[src.name] control[dat]", "window=atmo_mixer") + // onclose(user, "atmo_mixer") + // return + +/obj/machinery/atmospherics/trinary/mixer/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("power") + update_use_power(!use_power) + . = TRUE + if("pressure") + var/pressure = params["pressure"] + if(pressure == "max") + pressure = min(air1.volume, air2.volume) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + set_flow_rate = clamp(pressure, 0, min(air1.volume, air2.volume)) + if("node1") + var/value = text2num(params["concentration"]) + mixing_inputs[air1] = max(0, min(1, value / 100)) + mixing_inputs[air2] = 1.0 - mixing_inputs[air1] + . = TRUE + if("node2") + var/value = text2num(params["concentration"]) + mixing_inputs[air2] = max(0, min(1, value / 100)) + mixing_inputs[air1] = 1.0 - mixing_inputs[air2] + . = TRUE + update_icon() + +// +// "T" Orientation - Inputs are on oposite sides instead of adjacent +// +/obj/machinery/atmospherics/trinary/mixer/t_mixer + icon_state = "tmap" + construction_type = /obj/item/pipe/trinary // Can't flip a "T", its symmetrical + pipe_state = "t_mixer" + dir = SOUTH + initialize_directions = SOUTH|EAST|WEST + tee = TRUE + +// +// Mirrored Orientation - Flips the output dir to opposite side from normal. +// +/obj/machinery/atmospherics/trinary/mixer/m_mixer + icon_state = "mmap" + dir = SOUTH + initialize_directions = SOUTH|NORTH|EAST + mirrored = TRUE diff --git a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm index c548e956cc6..1bae066a69d 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm @@ -1,248 +1,248 @@ -/obj/machinery/atmospherics/trinary - dir = SOUTH - initialize_directions = SOUTH|NORTH|WEST - use_power = USE_POWER_OFF - pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF - - var/mirrored = FALSE - var/tee = FALSE - - var/datum/gas_mixture/air1 - var/datum/gas_mixture/air2 - var/datum/gas_mixture/air3 - - var/obj/machinery/atmospherics/node3 - - var/datum/pipe_network/network1 - var/datum/pipe_network/network2 - var/datum/pipe_network/network3 - -/obj/machinery/atmospherics/trinary/New() - ..() - - air1 = new - air2 = new - air3 = new - - air1.volume = 200 - air2.volume = 200 - air3.volume = 200 - -/obj/machinery/atmospherics/trinary/init_dir() - initialize_directions = get_initialize_directions_trinary(dir, mirrored, tee) - -/obj/machinery/atmospherics/trinary/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - var/list/node_connects = get_node_connect_dirs() - add_underlay(T, node1, node_connects[1]) - add_underlay(T, node2, node_connects[2]) - add_underlay(T, node3, node_connects[3]) - -/obj/machinery/atmospherics/trinary/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/trinary/power_change() - var/old_stat = stat - . = ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/trinary/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.has_tool_quality(TOOL_WRENCH)) - return ..() - if(!can_unwrench()) - to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - deconstruct() - -// Housekeeping and pipe network stuff below -/obj/machinery/atmospherics/trinary/get_neighbor_nodes_for_init() - return list(node1, node2, node3) - -/obj/machinery/atmospherics/trinary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - if(reference == node1) - network1 = new_network - - else if(reference == node2) - network2 = new_network - - else if (reference == node3) - network3 = new_network - - if(new_network.normal_members.Find(src)) - return 0 - - new_network.normal_members += src - - return null - -/obj/machinery/atmospherics/trinary/Destroy() - . = ..() - - if(node1) - node1.disconnect(src) - qdel(network1) - if(node2) - node2.disconnect(src) - qdel(network2) - if(node3) - node3.disconnect(src) - qdel(network3) - - node1 = null - node2 = null - node3 = null - -// Get the direction each node is facing to connect. -// It now returns as a list so it can be fetched nicely, each entry corresponds to node of same number. -/obj/machinery/atmospherics/trinary/get_node_connect_dirs() - return get_node_connect_dirs_trinary(dir, mirrored, tee) - -/obj/machinery/atmospherics/trinary/atmos_init() - if(node1 && node2 && node3) - return - - var/list/node_connects = get_node_connect_dirs() - - STANDARD_ATMOS_CHOOSE_NODE(1, node_connects[1]) - STANDARD_ATMOS_CHOOSE_NODE(2, node_connects[2]) - STANDARD_ATMOS_CHOOSE_NODE(3, node_connects[3]) - - update_icon() - update_underlays() - -/obj/machinery/atmospherics/trinary/build_network() - if(!network1 && node1) - network1 = new /datum/pipe_network() - network1.normal_members += src - network1.build_network(node1, src) - - if(!network2 && node2) - network2 = new /datum/pipe_network() - network2.normal_members += src - network2.build_network(node2, src) - - if(!network3 && node3) - network3 = new /datum/pipe_network() - network3.normal_members += src - network3.build_network(node3, src) - - -/obj/machinery/atmospherics/trinary/return_network(obj/machinery/atmospherics/reference) - build_network() - - if(reference==node1) - return network1 - - if(reference==node2) - return network2 - - if(reference==node3) - return network3 - - return null - -/obj/machinery/atmospherics/trinary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) - if(network1 == old_network) - network1 = new_network - if(network2 == old_network) - network2 = new_network - if(network3 == old_network) - network3 = new_network - - return 1 - -/obj/machinery/atmospherics/trinary/return_network_air(datum/pipe_network/reference) - var/list/results = list() - - if(network1 == reference) - results += air1 - if(network2 == reference) - results += air2 - if(network3 == reference) - results += air3 - - return results - -/obj/machinery/atmospherics/trinary/disconnect(obj/machinery/atmospherics/reference) - if(reference==node1) - qdel(network1) - node1 = null - - else if(reference==node2) - qdel(network2) - node2 = null - - else if(reference==node3) - qdel(network3) - node3 = null - - update_underlays() - - return null - -// Trinary init_dir() logic in a separate proc so it can be referenced from "trinary-ish" places like T-Valves -// TODO - Someday refactor those places under atmospherics/trinary -/proc/get_initialize_directions_trinary(var/dir, var/mirrored = FALSE, var/tee = FALSE) - if(tee) - switch(dir) - if(NORTH) - return EAST|NORTH|WEST - if(SOUTH) - return SOUTH|WEST|EAST - if(EAST) - return EAST|NORTH|SOUTH - if(WEST) - return WEST|NORTH|SOUTH - else if(mirrored) - switch(dir) - if(NORTH) - return WEST|NORTH|SOUTH - if(SOUTH) - return SOUTH|EAST|NORTH - if(EAST) - return EAST|WEST|NORTH - if(WEST) - return WEST|SOUTH|EAST - else - switch(dir) - if(NORTH) - return EAST|NORTH|SOUTH - if(SOUTH) - return SOUTH|WEST|NORTH - if(EAST) - return EAST|WEST|SOUTH - if(WEST) - return WEST|NORTH|EAST - -// Trinary get_node_connect_dirs() logic in a separate proc so it can be referenced from "trinary-ish" places like T-Valves -/proc/get_node_connect_dirs_trinary(var/dir, var/mirrored = FALSE, var/tee = FALSE) - var/node1_connect - var/node2_connect - var/node3_connect - - if(tee) - node1_connect = turn(dir, -90) - node2_connect = turn(dir, 90) - node3_connect = dir - else if(mirrored) - node1_connect = turn(dir, 180) - node2_connect = turn(dir, 90) - node3_connect = dir - else - node1_connect = turn(dir, 180) - node2_connect = turn(dir, -90) - node3_connect = dir - return list(node1_connect, node2_connect, node3_connect) +/obj/machinery/atmospherics/trinary + dir = SOUTH + initialize_directions = SOUTH|NORTH|WEST + use_power = USE_POWER_OFF + pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF + + var/mirrored = FALSE + var/tee = FALSE + + var/datum/gas_mixture/air1 + var/datum/gas_mixture/air2 + var/datum/gas_mixture/air3 + + var/obj/machinery/atmospherics/node3 + + var/datum/pipe_network/network1 + var/datum/pipe_network/network2 + var/datum/pipe_network/network3 + +/obj/machinery/atmospherics/trinary/New() + ..() + + air1 = new + air2 = new + air3 = new + + air1.volume = 200 + air2.volume = 200 + air3.volume = 200 + +/obj/machinery/atmospherics/trinary/init_dir() + initialize_directions = get_initialize_directions_trinary(dir, mirrored, tee) + +/obj/machinery/atmospherics/trinary/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + var/list/node_connects = get_node_connect_dirs() + add_underlay(T, node1, node_connects[1]) + add_underlay(T, node2, node_connects[2]) + add_underlay(T, node3, node_connects[3]) + +/obj/machinery/atmospherics/trinary/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/trinary/power_change() + var/old_stat = stat + . = ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/trinary/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + if(!can_unwrench()) + to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + deconstruct() + +// Housekeeping and pipe network stuff below +/obj/machinery/atmospherics/trinary/get_neighbor_nodes_for_init() + return list(node1, node2, node3) + +/obj/machinery/atmospherics/trinary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + if(reference == node1) + network1 = new_network + + else if(reference == node2) + network2 = new_network + + else if (reference == node3) + network3 = new_network + + if(new_network.normal_members.Find(src)) + return 0 + + new_network.normal_members += src + + return null + +/obj/machinery/atmospherics/trinary/Destroy() + . = ..() + + if(node1) + node1.disconnect(src) + qdel(network1) + if(node2) + node2.disconnect(src) + qdel(network2) + if(node3) + node3.disconnect(src) + qdel(network3) + + node1 = null + node2 = null + node3 = null + +// Get the direction each node is facing to connect. +// It now returns as a list so it can be fetched nicely, each entry corresponds to node of same number. +/obj/machinery/atmospherics/trinary/get_node_connect_dirs() + return get_node_connect_dirs_trinary(dir, mirrored, tee) + +/obj/machinery/atmospherics/trinary/atmos_init() + if(node1 && node2 && node3) + return + + var/list/node_connects = get_node_connect_dirs() + + STANDARD_ATMOS_CHOOSE_NODE(1, node_connects[1]) + STANDARD_ATMOS_CHOOSE_NODE(2, node_connects[2]) + STANDARD_ATMOS_CHOOSE_NODE(3, node_connects[3]) + + update_icon() + update_underlays() + +/obj/machinery/atmospherics/trinary/build_network() + if(!network1 && node1) + network1 = new /datum/pipe_network() + network1.normal_members += src + network1.build_network(node1, src) + + if(!network2 && node2) + network2 = new /datum/pipe_network() + network2.normal_members += src + network2.build_network(node2, src) + + if(!network3 && node3) + network3 = new /datum/pipe_network() + network3.normal_members += src + network3.build_network(node3, src) + + +/obj/machinery/atmospherics/trinary/return_network(obj/machinery/atmospherics/reference) + build_network() + + if(reference==node1) + return network1 + + if(reference==node2) + return network2 + + if(reference==node3) + return network3 + + return null + +/obj/machinery/atmospherics/trinary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + if(network1 == old_network) + network1 = new_network + if(network2 == old_network) + network2 = new_network + if(network3 == old_network) + network3 = new_network + + return 1 + +/obj/machinery/atmospherics/trinary/return_network_air(datum/pipe_network/reference) + var/list/results = list() + + if(network1 == reference) + results += air1 + if(network2 == reference) + results += air2 + if(network3 == reference) + results += air3 + + return results + +/obj/machinery/atmospherics/trinary/disconnect(obj/machinery/atmospherics/reference) + if(reference==node1) + qdel(network1) + node1 = null + + else if(reference==node2) + qdel(network2) + node2 = null + + else if(reference==node3) + qdel(network3) + node3 = null + + update_underlays() + + return null + +// Trinary init_dir() logic in a separate proc so it can be referenced from "trinary-ish" places like T-Valves +// TODO - Someday refactor those places under atmospherics/trinary +/proc/get_initialize_directions_trinary(var/dir, var/mirrored = FALSE, var/tee = FALSE) + if(tee) + switch(dir) + if(NORTH) + return EAST|NORTH|WEST + if(SOUTH) + return SOUTH|WEST|EAST + if(EAST) + return EAST|NORTH|SOUTH + if(WEST) + return WEST|NORTH|SOUTH + else if(mirrored) + switch(dir) + if(NORTH) + return WEST|NORTH|SOUTH + if(SOUTH) + return SOUTH|EAST|NORTH + if(EAST) + return EAST|WEST|NORTH + if(WEST) + return WEST|SOUTH|EAST + else + switch(dir) + if(NORTH) + return EAST|NORTH|SOUTH + if(SOUTH) + return SOUTH|WEST|NORTH + if(EAST) + return EAST|WEST|SOUTH + if(WEST) + return WEST|NORTH|EAST + +// Trinary get_node_connect_dirs() logic in a separate proc so it can be referenced from "trinary-ish" places like T-Valves +/proc/get_node_connect_dirs_trinary(var/dir, var/mirrored = FALSE, var/tee = FALSE) + var/node1_connect + var/node2_connect + var/node3_connect + + if(tee) + node1_connect = turn(dir, -90) + node2_connect = turn(dir, 90) + node3_connect = dir + else if(mirrored) + node1_connect = turn(dir, 180) + node2_connect = turn(dir, 90) + node3_connect = dir + else + node1_connect = turn(dir, 180) + node2_connect = turn(dir, -90) + node3_connect = dir + return list(node1_connect, node2_connect, node3_connect) diff --git a/code/ATMOSPHERICS/components/unary/cold_sink.dm b/code/ATMOSPHERICS/components/unary/cold_sink.dm index dea0487b48f..2cd7403096d 100644 --- a/code/ATMOSPHERICS/components/unary/cold_sink.dm +++ b/code/ATMOSPHERICS/components/unary/cold_sink.dm @@ -1,175 +1,175 @@ -//TODO: Put this under a common parent type with heaters to cut down on the copypasta -#define FREEZER_PERF_MULT 2.5 - -/obj/machinery/atmospherics/unary/freezer - name = "gas cooling system" - desc = "Cools gas when connected to pipe network" - icon = 'icons/obj/Cryogenic2_vr.dmi' - icon_state = "freezer_0" - density = TRUE - anchored = TRUE - use_power = USE_POWER_OFF - idle_power_usage = 5 // 5 Watts for thermostat related circuitry - circuit = /obj/item/weapon/circuitboard/unary_atmos/cooler - - var/heatsink_temperature = T20C // The constant temperature reservoir into which the freezer pumps heat. Probably the hull of the station or something. - var/internal_volume = 600 // L - - var/max_power_rating = 20000 // Power rating when the usage is turned up to 100 - var/power_setting = 100 - - var/set_temperature = T20C // Thermostat - var/cooling = 0 - -/obj/machinery/atmospherics/unary/freezer/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/atmospherics/unary/freezer/atmos_init() - if(node) - return - - var/node_connect = dir - - for(var/obj/machinery/atmospherics/target in get_step(src, node_connect)) - if(can_be_node(target, 1)) - node = target - break - - if(check_for_obstacles()) - node = null - - if(node) - update_icon() - -/obj/machinery/atmospherics/unary/freezer/update_icon() - if(node) - if(use_power && cooling) - icon_state = "freezer_1" - else - icon_state = "freezer" - else - icon_state = "freezer_0" - return - -/obj/machinery/atmospherics/unary/freezer/attack_ai(mob/user as mob) - tgui_interact(user) - -/obj/machinery/atmospherics/unary/freezer/attack_hand(mob/user as mob) - tgui_interact(user) - -/obj/machinery/atmospherics/unary/freezer/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "GasTemperatureSystem", name) - ui.open() - -/obj/machinery/atmospherics/unary/freezer/tgui_data(mob/user) - // this is the data which will be sent to the ui - var/data[0] - data["on"] = use_power ? 1 : 0 - data["gasPressure"] = round(air_contents.return_pressure()) - data["gasTemperature"] = round(air_contents.temperature) - data["minGasTemperature"] = 0 - data["maxGasTemperature"] = round(T20C+500) - data["targetGasTemperature"] = round(set_temperature) - data["powerSetting"] = power_setting - - var/temp_class = "good" - if(air_contents.temperature > (T0C - 20)) - temp_class = "bad" - else if(air_contents.temperature < (T0C - 20) && air_contents.temperature > (T0C - 100)) - temp_class = "average" - data["gasTemperatureClass"] = temp_class - - return data - -/obj/machinery/atmospherics/unary/freezer/tgui_act(action, params) - if(..()) - return TRUE - - . = TRUE - switch(action) - if("toggleStatus") - update_use_power(!use_power) - update_icon() - if("setGasTemperature") - var/amount = text2num(params["temp"]) - if(amount > 0) - set_temperature = min(amount, 1000) - else - set_temperature = max(amount, 0) - if("setPower") //setting power to 0 is redundant anyways - var/new_setting = between(0, text2num(params["value"]), 100) - set_power_level(new_setting) - - add_fingerprint(usr) - -/obj/machinery/atmospherics/unary/freezer/process() - ..() - - if(stat & (NOPOWER|BROKEN) || !use_power) - cooling = 0 - update_icon() - return - - if(network && air_contents.temperature > set_temperature) - cooling = 1 - - var/heat_transfer = max( -air_contents.get_thermal_energy_change(set_temperature - 5), 0 ) - - //Assume the heat is being pumped into the hull which is fixed at heatsink_temperature - //not /really/ proper thermodynamics but whatever - var/cop = FREEZER_PERF_MULT * air_contents.temperature/heatsink_temperature //heatpump coefficient of performance from thermodynamics -> power used = heat_transfer/cop - heat_transfer = min(heat_transfer, cop * power_rating) //limit heat transfer by available power - - var/removed = -air_contents.add_thermal_energy(-heat_transfer) //remove the heat - if(debug) - visible_message("[src]: Removing [removed] W.") - - use_power(power_rating) - - network.update = 1 - else - cooling = 0 - - update_icon() - -//upgrading parts -/obj/machinery/atmospherics/unary/freezer/RefreshParts() - ..() - var/cap_rating = 0 - var/manip_rating = 0 - var/bin_rating = 0 - - for(var/obj/item/weapon/stock_parts/P in component_parts) - if(istype(P, /obj/item/weapon/stock_parts/capacitor)) - cap_rating += P.rating - if(istype(P, /obj/item/weapon/stock_parts/manipulator)) - manip_rating += P.rating - if(istype(P, /obj/item/weapon/stock_parts/matter_bin)) - bin_rating += P.rating - - max_power_rating = initial(max_power_rating) * cap_rating / 2 //more powerful - heatsink_temperature = initial(heatsink_temperature) / ((manip_rating + bin_rating) / 2) //more efficient - air_contents.volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating - set_power_level(power_setting) - -/obj/machinery/atmospherics/unary/freezer/proc/set_power_level(var/new_power_setting) - power_setting = new_power_setting - power_rating = max_power_rating * (power_setting/100) - -/obj/machinery/atmospherics/unary/freezer/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(default_deconstruction_screwdriver(user, O)) - return - if(default_deconstruction_crowbar(user, O)) - return - if(default_part_replacement(user, O)) - return - - ..() - -/obj/machinery/atmospherics/unary/freezer/examine(mob/user) - . = ..() - if(panel_open) - . += "The maintenance hatch is open." +//TODO: Put this under a common parent type with heaters to cut down on the copypasta +#define FREEZER_PERF_MULT 2.5 + +/obj/machinery/atmospherics/unary/freezer + name = "gas cooling system" + desc = "Cools gas when connected to pipe network" + icon = 'icons/obj/Cryogenic2_vr.dmi' + icon_state = "freezer_0" + density = TRUE + anchored = TRUE + use_power = USE_POWER_OFF + idle_power_usage = 5 // 5 Watts for thermostat related circuitry + circuit = /obj/item/weapon/circuitboard/unary_atmos/cooler + + var/heatsink_temperature = T20C // The constant temperature reservoir into which the freezer pumps heat. Probably the hull of the station or something. + var/internal_volume = 600 // L + + var/max_power_rating = 20000 // Power rating when the usage is turned up to 100 + var/power_setting = 100 + + var/set_temperature = T20C // Thermostat + var/cooling = 0 + +/obj/machinery/atmospherics/unary/freezer/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/atmospherics/unary/freezer/atmos_init() + if(node) + return + + var/node_connect = dir + + for(var/obj/machinery/atmospherics/target in get_step(src, node_connect)) + if(can_be_node(target, 1)) + node = target + break + + if(check_for_obstacles()) + node = null + + if(node) + update_icon() + +/obj/machinery/atmospherics/unary/freezer/update_icon() + if(node) + if(use_power && cooling) + icon_state = "freezer_1" + else + icon_state = "freezer" + else + icon_state = "freezer_0" + return + +/obj/machinery/atmospherics/unary/freezer/attack_ai(mob/user as mob) + tgui_interact(user) + +/obj/machinery/atmospherics/unary/freezer/attack_hand(mob/user as mob) + tgui_interact(user) + +/obj/machinery/atmospherics/unary/freezer/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "GasTemperatureSystem", name) + ui.open() + +/obj/machinery/atmospherics/unary/freezer/tgui_data(mob/user) + // this is the data which will be sent to the ui + var/data[0] + data["on"] = use_power ? 1 : 0 + data["gasPressure"] = round(air_contents.return_pressure()) + data["gasTemperature"] = round(air_contents.temperature) + data["minGasTemperature"] = 0 + data["maxGasTemperature"] = round(T20C+500) + data["targetGasTemperature"] = round(set_temperature) + data["powerSetting"] = power_setting + + var/temp_class = "good" + if(air_contents.temperature > (T0C - 20)) + temp_class = "bad" + else if(air_contents.temperature < (T0C - 20) && air_contents.temperature > (T0C - 100)) + temp_class = "average" + data["gasTemperatureClass"] = temp_class + + return data + +/obj/machinery/atmospherics/unary/freezer/tgui_act(action, params) + if(..()) + return TRUE + + . = TRUE + switch(action) + if("toggleStatus") + update_use_power(!use_power) + update_icon() + if("setGasTemperature") + var/amount = text2num(params["temp"]) + if(amount > 0) + set_temperature = min(amount, 1000) + else + set_temperature = max(amount, 0) + if("setPower") //setting power to 0 is redundant anyways + var/new_setting = between(0, text2num(params["value"]), 100) + set_power_level(new_setting) + + add_fingerprint(usr) + +/obj/machinery/atmospherics/unary/freezer/process() + ..() + + if(stat & (NOPOWER|BROKEN) || !use_power) + cooling = 0 + update_icon() + return + + if(network && air_contents.temperature > set_temperature) + cooling = 1 + + var/heat_transfer = max( -air_contents.get_thermal_energy_change(set_temperature - 5), 0 ) + + //Assume the heat is being pumped into the hull which is fixed at heatsink_temperature + //not /really/ proper thermodynamics but whatever + var/cop = FREEZER_PERF_MULT * air_contents.temperature/heatsink_temperature //heatpump coefficient of performance from thermodynamics -> power used = heat_transfer/cop + heat_transfer = min(heat_transfer, cop * power_rating) //limit heat transfer by available power + + var/removed = -air_contents.add_thermal_energy(-heat_transfer) //remove the heat + if(debug) + visible_message("[src]: Removing [removed] W.") + + use_power(power_rating) + + network.update = 1 + else + cooling = 0 + + update_icon() + +//upgrading parts +/obj/machinery/atmospherics/unary/freezer/RefreshParts() + ..() + var/cap_rating = 0 + var/manip_rating = 0 + var/bin_rating = 0 + + for(var/obj/item/weapon/stock_parts/P in component_parts) + if(istype(P, /obj/item/weapon/stock_parts/capacitor)) + cap_rating += P.rating + if(istype(P, /obj/item/weapon/stock_parts/manipulator)) + manip_rating += P.rating + if(istype(P, /obj/item/weapon/stock_parts/matter_bin)) + bin_rating += P.rating + + max_power_rating = initial(max_power_rating) * cap_rating / 2 //more powerful + heatsink_temperature = initial(heatsink_temperature) / ((manip_rating + bin_rating) / 2) //more efficient + air_contents.volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating + set_power_level(power_setting) + +/obj/machinery/atmospherics/unary/freezer/proc/set_power_level(var/new_power_setting) + power_setting = new_power_setting + power_rating = max_power_rating * (power_setting/100) + +/obj/machinery/atmospherics/unary/freezer/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(default_deconstruction_screwdriver(user, O)) + return + if(default_deconstruction_crowbar(user, O)) + return + if(default_part_replacement(user, O)) + return + + ..() + +/obj/machinery/atmospherics/unary/freezer/examine(mob/user) + . = ..() + if(panel_open) + . += "The maintenance hatch is open." diff --git a/code/ATMOSPHERICS/components/unary/heat_exchanger.dm b/code/ATMOSPHERICS/components/unary/heat_exchanger.dm index ee759c897cc..cc50782d71c 100644 --- a/code/ATMOSPHERICS/components/unary/heat_exchanger.dm +++ b/code/ATMOSPHERICS/components/unary/heat_exchanger.dm @@ -1,87 +1,87 @@ -/obj/machinery/atmospherics/unary/heat_exchanger - - icon = 'icons/obj/atmospherics/heat_exchanger.dmi' - icon_state = "intact" - pipe_state = "heunary" - density = TRUE - - name = "Heat Exchanger" - desc = "Exchanges heat between two input gases. Setup for fast heat transfer" - - var/obj/machinery/atmospherics/unary/heat_exchanger/partner = null - var/update_cycle - -/obj/machinery/atmospherics/unary/heat_exchanger/update_icon() - if(node) - icon_state = "intact" - else - icon_state = "exposed" - - return - -/obj/machinery/atmospherics/unary/heat_exchanger/atmos_init() - if(!partner) - var/partner_connect = turn(dir,180) - - for(var/obj/machinery/atmospherics/unary/heat_exchanger/target in get_step(src,partner_connect)) - if(target.dir & get_dir(src,target)) - partner = target - partner.partner = src - break - - ..() - -/obj/machinery/atmospherics/unary/heat_exchanger/process() - ..() - if(!partner) - return 0 - - if(!air_master || air_master.current_cycle <= update_cycle) - return 0 - - update_cycle = air_master.current_cycle - partner.update_cycle = air_master.current_cycle - - var/air_heat_capacity = air_contents.heat_capacity() - var/other_air_heat_capacity = partner.air_contents.heat_capacity() - var/combined_heat_capacity = other_air_heat_capacity + air_heat_capacity - - var/old_temperature = air_contents.temperature - var/other_old_temperature = partner.air_contents.temperature - - if(combined_heat_capacity > 0) - var/combined_energy = partner.air_contents.temperature*other_air_heat_capacity + air_heat_capacity*air_contents.temperature - - var/new_temperature = combined_energy/combined_heat_capacity - air_contents.temperature = new_temperature - partner.air_contents.temperature = new_temperature - - if(network) - if(abs(old_temperature-air_contents.temperature) > 1) - network.update = 1 - - if(partner.network) - if(abs(other_old_temperature-partner.air_contents.temperature) > 1) - partner.network.update = 1 - - return 1 - -/obj/machinery/atmospherics/unary/heat_exchanger/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.has_tool_quality(TOOL_WRENCH)) - return ..() - var/turf/T = src.loc - if (level==1 && isturf(T) && !T.is_plating()) - to_chat(user, "You must remove the plating first.") - return 1 - if (!can_unwrench()) - to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - deconstruct() +/obj/machinery/atmospherics/unary/heat_exchanger + + icon = 'icons/obj/atmospherics/heat_exchanger.dmi' + icon_state = "intact" + pipe_state = "heunary" + density = TRUE + + name = "Heat Exchanger" + desc = "Exchanges heat between two input gases. Setup for fast heat transfer" + + var/obj/machinery/atmospherics/unary/heat_exchanger/partner = null + var/update_cycle + +/obj/machinery/atmospherics/unary/heat_exchanger/update_icon() + if(node) + icon_state = "intact" + else + icon_state = "exposed" + + return + +/obj/machinery/atmospherics/unary/heat_exchanger/atmos_init() + if(!partner) + var/partner_connect = turn(dir,180) + + for(var/obj/machinery/atmospherics/unary/heat_exchanger/target in get_step(src,partner_connect)) + if(target.dir & get_dir(src,target)) + partner = target + partner.partner = src + break + + ..() + +/obj/machinery/atmospherics/unary/heat_exchanger/process() + ..() + if(!partner) + return 0 + + if(!air_master || air_master.current_cycle <= update_cycle) + return 0 + + update_cycle = air_master.current_cycle + partner.update_cycle = air_master.current_cycle + + var/air_heat_capacity = air_contents.heat_capacity() + var/other_air_heat_capacity = partner.air_contents.heat_capacity() + var/combined_heat_capacity = other_air_heat_capacity + air_heat_capacity + + var/old_temperature = air_contents.temperature + var/other_old_temperature = partner.air_contents.temperature + + if(combined_heat_capacity > 0) + var/combined_energy = partner.air_contents.temperature*other_air_heat_capacity + air_heat_capacity*air_contents.temperature + + var/new_temperature = combined_energy/combined_heat_capacity + air_contents.temperature = new_temperature + partner.air_contents.temperature = new_temperature + + if(network) + if(abs(old_temperature-air_contents.temperature) > 1) + network.update = 1 + + if(partner.network) + if(abs(other_old_temperature-partner.air_contents.temperature) > 1) + partner.network.update = 1 + + return 1 + +/obj/machinery/atmospherics/unary/heat_exchanger/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + var/turf/T = src.loc + if (level==1 && isturf(T) && !T.is_plating()) + to_chat(user, "You must remove the plating first.") + return 1 + if (!can_unwrench()) + to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + deconstruct() diff --git a/code/ATMOSPHERICS/components/unary/heat_source.dm b/code/ATMOSPHERICS/components/unary/heat_source.dm index aea5730672b..efd3a56d8bb 100644 --- a/code/ATMOSPHERICS/components/unary/heat_source.dm +++ b/code/ATMOSPHERICS/components/unary/heat_source.dm @@ -1,162 +1,162 @@ -//TODO: Put this under a common parent type with freezers to cut down on the copypasta -#define HEATER_PERF_MULT 2.5 - -/obj/machinery/atmospherics/unary/heater - name = "gas heating system" - desc = "Heats gas when connected to a pipe network" - icon = 'icons/obj/Cryogenic2_vr.dmi' - icon_state = "heater_0" - density = TRUE - anchored = TRUE - use_power = USE_POWER_OFF - idle_power_usage = 5 //5 Watts for thermostat related circuitry - circuit = /obj/item/weapon/circuitboard/unary_atmos/heater - - var/max_temperature = T20C + 680 - var/internal_volume = 600 //L - - var/max_power_rating = 20000 //power rating when the usage is turned up to 100 - var/power_setting = 100 - - var/set_temperature = T20C //thermostat - var/heating = 0 //mainly for icon updates - -/obj/machinery/atmospherics/unary/heater/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/atmospherics/unary/heater/atmos_init() - if(node) - return - - var/node_connect = dir - - //check that there is something to connect to - for(var/obj/machinery/atmospherics/target in get_step(src, node_connect)) - if(can_be_node(target, 1)) - node = target - break - - if(check_for_obstacles()) - node = null - - if(node) - update_icon() - - -/obj/machinery/atmospherics/unary/heater/update_icon() - if(node) - if(use_power && heating) - icon_state = "heater_1" - else - icon_state = "heater" - else - icon_state = "heater_0" - return - - -/obj/machinery/atmospherics/unary/heater/process() - ..() - - if(stat & (NOPOWER|BROKEN) || !use_power) - heating = 0 - update_icon() - return - - if(network && air_contents.total_moles && air_contents.temperature < set_temperature) - air_contents.add_thermal_energy(power_rating * HEATER_PERF_MULT) - use_power(power_rating) - - heating = 1 - network.update = 1 - else - heating = 0 - - update_icon() - -/obj/machinery/atmospherics/unary/heater/attack_ai(mob/user as mob) - tgui_interact(user) - -/obj/machinery/atmospherics/unary/heater/attack_hand(mob/user as mob) - tgui_interact(user) - -/obj/machinery/atmospherics/unary/heater/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "GasTemperatureSystem", name) - ui.open() - -/obj/machinery/atmospherics/unary/heater/tgui_data(mob/user) - // this is the data which will be sent to the ui - var/data[0] - data["on"] = use_power ? 1 : 0 - data["gasPressure"] = round(air_contents.return_pressure()) - data["gasTemperature"] = round(air_contents.temperature) - data["minGasTemperature"] = 0 - data["maxGasTemperature"] = round(max_temperature) - data["targetGasTemperature"] = round(set_temperature) - data["powerSetting"] = power_setting - - var/temp_class = "average" - if(air_contents.temperature > (T20C+40)) - temp_class = "bad" - data["gasTemperatureClass"] = temp_class - - return data - -/obj/machinery/atmospherics/unary/heater/tgui_act(action, params) - if(..()) - return TRUE - - . = TRUE - switch(action) - if("toggleStatus") - update_use_power(!use_power) - update_icon() - if("setGasTemperature") - var/amount = text2num(params["temp"]) - if(amount > 0) - set_temperature = min(amount, max_temperature) - else - set_temperature = max(amount, 0) - if("setPower") //setting power to 0 is redundant anyways - var/new_setting = between(0, text2num(params["value"]), 100) - set_power_level(new_setting) - - add_fingerprint(usr) - -//upgrading parts -/obj/machinery/atmospherics/unary/heater/RefreshParts() - ..() - var/cap_rating = 0 - var/bin_rating = 0 - - for(var/obj/item/weapon/stock_parts/P in component_parts) - if(istype(P, /obj/item/weapon/stock_parts/capacitor)) - cap_rating += P.rating - if(istype(P, /obj/item/weapon/stock_parts/matter_bin)) - bin_rating += P.rating - - max_power_rating = initial(max_power_rating) * cap_rating / 2 - max_temperature = max(initial(max_temperature) - T20C, 0) * ((bin_rating * 4 + cap_rating) / 5) + T20C - air_contents.volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating - set_power_level(power_setting) - -/obj/machinery/atmospherics/unary/heater/proc/set_power_level(var/new_power_setting) - power_setting = new_power_setting - power_rating = max_power_rating * (power_setting/100) - -/obj/machinery/atmospherics/unary/heater/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(default_deconstruction_screwdriver(user, O)) - return - if(default_deconstruction_crowbar(user, O)) - return - if(default_part_replacement(user, O)) - return - - ..() - -/obj/machinery/atmospherics/unary/heater/examine(mob/user) - . = ..() - if(panel_open) - . += "The maintenance hatch is open." +//TODO: Put this under a common parent type with freezers to cut down on the copypasta +#define HEATER_PERF_MULT 2.5 + +/obj/machinery/atmospherics/unary/heater + name = "gas heating system" + desc = "Heats gas when connected to a pipe network" + icon = 'icons/obj/Cryogenic2_vr.dmi' + icon_state = "heater_0" + density = TRUE + anchored = TRUE + use_power = USE_POWER_OFF + idle_power_usage = 5 //5 Watts for thermostat related circuitry + circuit = /obj/item/weapon/circuitboard/unary_atmos/heater + + var/max_temperature = T20C + 680 + var/internal_volume = 600 //L + + var/max_power_rating = 20000 //power rating when the usage is turned up to 100 + var/power_setting = 100 + + var/set_temperature = T20C //thermostat + var/heating = 0 //mainly for icon updates + +/obj/machinery/atmospherics/unary/heater/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/atmospherics/unary/heater/atmos_init() + if(node) + return + + var/node_connect = dir + + //check that there is something to connect to + for(var/obj/machinery/atmospherics/target in get_step(src, node_connect)) + if(can_be_node(target, 1)) + node = target + break + + if(check_for_obstacles()) + node = null + + if(node) + update_icon() + + +/obj/machinery/atmospherics/unary/heater/update_icon() + if(node) + if(use_power && heating) + icon_state = "heater_1" + else + icon_state = "heater" + else + icon_state = "heater_0" + return + + +/obj/machinery/atmospherics/unary/heater/process() + ..() + + if(stat & (NOPOWER|BROKEN) || !use_power) + heating = 0 + update_icon() + return + + if(network && air_contents.total_moles && air_contents.temperature < set_temperature) + air_contents.add_thermal_energy(power_rating * HEATER_PERF_MULT) + use_power(power_rating) + + heating = 1 + network.update = 1 + else + heating = 0 + + update_icon() + +/obj/machinery/atmospherics/unary/heater/attack_ai(mob/user as mob) + tgui_interact(user) + +/obj/machinery/atmospherics/unary/heater/attack_hand(mob/user as mob) + tgui_interact(user) + +/obj/machinery/atmospherics/unary/heater/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "GasTemperatureSystem", name) + ui.open() + +/obj/machinery/atmospherics/unary/heater/tgui_data(mob/user) + // this is the data which will be sent to the ui + var/data[0] + data["on"] = use_power ? 1 : 0 + data["gasPressure"] = round(air_contents.return_pressure()) + data["gasTemperature"] = round(air_contents.temperature) + data["minGasTemperature"] = 0 + data["maxGasTemperature"] = round(max_temperature) + data["targetGasTemperature"] = round(set_temperature) + data["powerSetting"] = power_setting + + var/temp_class = "average" + if(air_contents.temperature > (T20C+40)) + temp_class = "bad" + data["gasTemperatureClass"] = temp_class + + return data + +/obj/machinery/atmospherics/unary/heater/tgui_act(action, params) + if(..()) + return TRUE + + . = TRUE + switch(action) + if("toggleStatus") + update_use_power(!use_power) + update_icon() + if("setGasTemperature") + var/amount = text2num(params["temp"]) + if(amount > 0) + set_temperature = min(amount, max_temperature) + else + set_temperature = max(amount, 0) + if("setPower") //setting power to 0 is redundant anyways + var/new_setting = between(0, text2num(params["value"]), 100) + set_power_level(new_setting) + + add_fingerprint(usr) + +//upgrading parts +/obj/machinery/atmospherics/unary/heater/RefreshParts() + ..() + var/cap_rating = 0 + var/bin_rating = 0 + + for(var/obj/item/weapon/stock_parts/P in component_parts) + if(istype(P, /obj/item/weapon/stock_parts/capacitor)) + cap_rating += P.rating + if(istype(P, /obj/item/weapon/stock_parts/matter_bin)) + bin_rating += P.rating + + max_power_rating = initial(max_power_rating) * cap_rating / 2 + max_temperature = max(initial(max_temperature) - T20C, 0) * ((bin_rating * 4 + cap_rating) / 5) + T20C + air_contents.volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating + set_power_level(power_setting) + +/obj/machinery/atmospherics/unary/heater/proc/set_power_level(var/new_power_setting) + power_setting = new_power_setting + power_rating = max_power_rating * (power_setting/100) + +/obj/machinery/atmospherics/unary/heater/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(default_deconstruction_screwdriver(user, O)) + return + if(default_deconstruction_crowbar(user, O)) + return + if(default_part_replacement(user, O)) + return + + ..() + +/obj/machinery/atmospherics/unary/heater/examine(mob/user) + . = ..() + if(panel_open) + . += "The maintenance hatch is open." diff --git a/code/ATMOSPHERICS/components/unary/outlet_injector.dm b/code/ATMOSPHERICS/components/unary/outlet_injector.dm index fa3c9073d8a..8002d79ad55 100644 --- a/code/ATMOSPHERICS/components/unary/outlet_injector.dm +++ b/code/ATMOSPHERICS/components/unary/outlet_injector.dm @@ -1,177 +1,177 @@ -//Basically a one way passive valve. If the pressure inside is greater than the environment then gas will flow passively, -//but it does not permit gas to flow back from the environment into the injector. Can be turned off to prevent any gas flow. -//When it receives the "inject" signal, it will try to pump it's entire contents into the environment regardless of pressure, using power. - -/obj/machinery/atmospherics/unary/outlet_injector - icon = 'icons/atmos/injector.dmi' - icon_state = "map_injector" - pipe_state = "injector" - - name = "air injector" - desc = "Passively injects air into its surroundings. Has a valve attached to it that can control flow rate." - - use_power = USE_POWER_OFF - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 15000 //15000 W ~ 20 HP - - var/injecting = 0 - - var/volume_rate = 50 //flow rate limit - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - - level = 1 - -/obj/machinery/atmospherics/unary/outlet_injector/New() - ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //Give it a small reservoir for injecting. Also allows it to have a higher flow rate limit than vent pumps, to differentiate injectors a bit more. - -/obj/machinery/atmospherics/unary/outlet_injector/Destroy() - unregister_radio(src, frequency) - . = ..() - -/obj/machinery/atmospherics/unary/outlet_injector/update_icon() - if(!powered()) - icon_state = "off" - else - icon_state = "[use_power ? "on" : "off"]" - -/obj/machinery/atmospherics/unary/outlet_injector/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node, dir) - -/obj/machinery/atmospherics/unary/outlet_injector/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/unary/outlet_injector/process() - ..() - - last_power_draw = 0 - last_flow_rate = 0 - - if((stat & (NOPOWER|BROKEN)) || !use_power) - return - - var/power_draw = -1 - var/datum/gas_mixture/environment = loc.return_air() - - if(environment && air_contents.temperature > 0) - var/transfer_moles = (volume_rate/air_contents.volume)*air_contents.total_moles //apply flow rate limit - power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) - - if (power_draw >= 0) - last_power_draw = power_draw - use_power(power_draw) - - if(network) - network.update = 1 - - return 1 - -/obj/machinery/atmospherics/unary/outlet_injector/proc/inject() - if(injecting || (stat & NOPOWER)) - return 0 - - var/datum/gas_mixture/environment = loc.return_air() - if (!environment) - return 0 - - injecting = 1 - - if(air_contents.temperature > 0) - var/power_used = pump_gas(src, air_contents, environment, air_contents.total_moles, power_rating) - use_power(power_used) - - if(network) - network.update = 1 - - flick("inject", src) - -/obj/machinery/atmospherics/unary/outlet_injector/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = radio_controller.add_object(src, frequency) - -/obj/machinery/atmospherics/unary/outlet_injector/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "AO", - "power" = use_power, - "volume_rate" = volume_rate, - "sigtype" = "status" - ) - - radio_connection.post_signal(src, signal) - - return 1 - -/obj/machinery/atmospherics/unary/outlet_injector/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/unary/outlet_injector/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - if(signal.data["power"]) - update_use_power(text2num(signal.data["power"])) - - if(signal.data["power_toggle"]) - update_use_power(!use_power) - - if(signal.data["inject"]) - spawn inject() - return - - if(signal.data["set_volume_rate"]) - var/number = text2num(signal.data["set_volume_rate"]) - volume_rate = between(0, number, air_contents.volume) - - if(signal.data["status"]) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - -/obj/machinery/atmospherics/unary/outlet_injector/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/unary/outlet_injector/attack_hand(mob/user as mob) - to_chat(user, "You toggle \the [src].") - injecting = !injecting - update_use_power(injecting ? USE_POWER_IDLE : USE_POWER_OFF) - update_icon() - -/obj/machinery/atmospherics/unary/outlet_injector/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.has_tool_quality(TOOL_WRENCH)) - return ..() - - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - deconstruct() +//Basically a one way passive valve. If the pressure inside is greater than the environment then gas will flow passively, +//but it does not permit gas to flow back from the environment into the injector. Can be turned off to prevent any gas flow. +//When it receives the "inject" signal, it will try to pump it's entire contents into the environment regardless of pressure, using power. + +/obj/machinery/atmospherics/unary/outlet_injector + icon = 'icons/atmos/injector.dmi' + icon_state = "map_injector" + pipe_state = "injector" + + name = "air injector" + desc = "Passively injects air into its surroundings. Has a valve attached to it that can control flow rate." + + use_power = USE_POWER_OFF + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 15000 //15000 W ~ 20 HP + + var/injecting = 0 + + var/volume_rate = 50 //flow rate limit + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + + level = 1 + +/obj/machinery/atmospherics/unary/outlet_injector/New() + ..() + air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //Give it a small reservoir for injecting. Also allows it to have a higher flow rate limit than vent pumps, to differentiate injectors a bit more. + +/obj/machinery/atmospherics/unary/outlet_injector/Destroy() + unregister_radio(src, frequency) + . = ..() + +/obj/machinery/atmospherics/unary/outlet_injector/update_icon() + if(!powered()) + icon_state = "off" + else + icon_state = "[use_power ? "on" : "off"]" + +/obj/machinery/atmospherics/unary/outlet_injector/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node, dir) + +/obj/machinery/atmospherics/unary/outlet_injector/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/unary/outlet_injector/process() + ..() + + last_power_draw = 0 + last_flow_rate = 0 + + if((stat & (NOPOWER|BROKEN)) || !use_power) + return + + var/power_draw = -1 + var/datum/gas_mixture/environment = loc.return_air() + + if(environment && air_contents.temperature > 0) + var/transfer_moles = (volume_rate/air_contents.volume)*air_contents.total_moles //apply flow rate limit + power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) + + if (power_draw >= 0) + last_power_draw = power_draw + use_power(power_draw) + + if(network) + network.update = 1 + + return 1 + +/obj/machinery/atmospherics/unary/outlet_injector/proc/inject() + if(injecting || (stat & NOPOWER)) + return 0 + + var/datum/gas_mixture/environment = loc.return_air() + if (!environment) + return 0 + + injecting = 1 + + if(air_contents.temperature > 0) + var/power_used = pump_gas(src, air_contents, environment, air_contents.total_moles, power_rating) + use_power(power_used) + + if(network) + network.update = 1 + + flick("inject", src) + +/obj/machinery/atmospherics/unary/outlet_injector/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = radio_controller.add_object(src, frequency) + +/obj/machinery/atmospherics/unary/outlet_injector/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "AO", + "power" = use_power, + "volume_rate" = volume_rate, + "sigtype" = "status" + ) + + radio_connection.post_signal(src, signal) + + return 1 + +/obj/machinery/atmospherics/unary/outlet_injector/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/unary/outlet_injector/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + if(signal.data["power"]) + update_use_power(text2num(signal.data["power"])) + + if(signal.data["power_toggle"]) + update_use_power(!use_power) + + if(signal.data["inject"]) + spawn inject() + return + + if(signal.data["set_volume_rate"]) + var/number = text2num(signal.data["set_volume_rate"]) + volume_rate = between(0, number, air_contents.volume) + + if(signal.data["status"]) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + +/obj/machinery/atmospherics/unary/outlet_injector/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/unary/outlet_injector/attack_hand(mob/user as mob) + to_chat(user, "You toggle \the [src].") + injecting = !injecting + update_use_power(injecting ? USE_POWER_IDLE : USE_POWER_OFF) + update_icon() + +/obj/machinery/atmospherics/unary/outlet_injector/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + deconstruct() diff --git a/code/ATMOSPHERICS/components/unary/unary_base.dm b/code/ATMOSPHERICS/components/unary/unary_base.dm index 12d6822ad47..9a3a4d3e44b 100644 --- a/code/ATMOSPHERICS/components/unary/unary_base.dm +++ b/code/ATMOSPHERICS/components/unary/unary_base.dm @@ -1,116 +1,116 @@ -/obj/machinery/atmospherics/unary - dir = SOUTH - initialize_directions = SOUTH - construction_type = /obj/item/pipe/directional - pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF - //layer = TURF_LAYER+0.1 - - var/datum/gas_mixture/air_contents - - var/obj/machinery/atmospherics/node - - var/datum/pipe_network/network - - var/welded = 0 //defining this here for ventcrawl stuff - -/obj/machinery/atmospherics/unary/New() - ..() - air_contents = new - - air_contents.volume = 200 - -/obj/machinery/atmospherics/unary/init_dir() - initialize_directions = dir - -// Housekeeping and pipe network stuff below -/obj/machinery/atmospherics/unary/get_neighbor_nodes_for_init() - return list(node) - -/obj/machinery/atmospherics/unary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - if(reference == node) - network = new_network - - if(new_network.normal_members.Find(src)) - return 0 - - new_network.normal_members += src - - return null - -/obj/machinery/atmospherics/unary/Destroy() - . = ..() - - if(node) - node.disconnect(src) - qdel(network) - - node = null - -/obj/machinery/atmospherics/unary/atmos_init() - if(node) - return - - var/node_connect = dir - - for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) - if(can_be_node(target, 1)) - node = target - break - - update_icon() - update_underlays() - -/obj/machinery/atmospherics/unary/build_network() - if(!network && node) - network = new /datum/pipe_network() - network.normal_members += src - network.build_network(node, src) - - -/obj/machinery/atmospherics/unary/return_network(obj/machinery/atmospherics/reference) - build_network() - - if(reference==node) - return network - - return null - -/obj/machinery/atmospherics/unary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) - if(network == old_network) - network = new_network - - return 1 - -/obj/machinery/atmospherics/unary/return_network_air(datum/pipe_network/reference) - var/list/results = list() - - if(network == reference) - results += air_contents - - return results - -/obj/machinery/atmospherics/unary/disconnect(obj/machinery/atmospherics/reference) - if(reference==node) - qdel(network) - node = null - - update_icon() - update_underlays() - - return null - -// Check if there are any other atmos machines in the same turf that will block this machine from initializing. -// Intended for use when a frame-constructable machine (i.e. not made from pipe fittings) wants to wrench down and connect. -// Returns TRUE if something is blocking, FALSE if its okay to continue. -/obj/machinery/atmospherics/unary/proc/check_for_obstacles() - for(var/obj/machinery/atmospherics/M in loc) - if(M == src) continue - if((M.pipe_flags & pipe_flags & PIPING_ONE_PER_TURF)) //Only one dense/requires density object per tile, eg connectors/cryo/heater/coolers. - visible_message("\The [src]'s cannot be connected, something is hogging the tile!") - return TRUE - if((M.piping_layer != piping_layer) && !((M.pipe_flags | flags) & PIPING_ALL_LAYER)) // Pipes on different layers can't block each other unless they are ALL_LAYER - continue - if(M.get_init_dirs() & get_init_dirs()) // matches at least one direction on either type of pipe - visible_message("\The [src]'s connector can't be connected, there is already a pipe at that location!") - return TRUE - return FALSE +/obj/machinery/atmospherics/unary + dir = SOUTH + initialize_directions = SOUTH + construction_type = /obj/item/pipe/directional + pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF + //layer = TURF_LAYER+0.1 + + var/datum/gas_mixture/air_contents + + var/obj/machinery/atmospherics/node + + var/datum/pipe_network/network + + var/welded = 0 //defining this here for ventcrawl stuff + +/obj/machinery/atmospherics/unary/New() + ..() + air_contents = new + + air_contents.volume = 200 + +/obj/machinery/atmospherics/unary/init_dir() + initialize_directions = dir + +// Housekeeping and pipe network stuff below +/obj/machinery/atmospherics/unary/get_neighbor_nodes_for_init() + return list(node) + +/obj/machinery/atmospherics/unary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + if(reference == node) + network = new_network + + if(new_network.normal_members.Find(src)) + return 0 + + new_network.normal_members += src + + return null + +/obj/machinery/atmospherics/unary/Destroy() + . = ..() + + if(node) + node.disconnect(src) + qdel(network) + + node = null + +/obj/machinery/atmospherics/unary/atmos_init() + if(node) + return + + var/node_connect = dir + + for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) + if(can_be_node(target, 1)) + node = target + break + + update_icon() + update_underlays() + +/obj/machinery/atmospherics/unary/build_network() + if(!network && node) + network = new /datum/pipe_network() + network.normal_members += src + network.build_network(node, src) + + +/obj/machinery/atmospherics/unary/return_network(obj/machinery/atmospherics/reference) + build_network() + + if(reference==node) + return network + + return null + +/obj/machinery/atmospherics/unary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + if(network == old_network) + network = new_network + + return 1 + +/obj/machinery/atmospherics/unary/return_network_air(datum/pipe_network/reference) + var/list/results = list() + + if(network == reference) + results += air_contents + + return results + +/obj/machinery/atmospherics/unary/disconnect(obj/machinery/atmospherics/reference) + if(reference==node) + qdel(network) + node = null + + update_icon() + update_underlays() + + return null + +// Check if there are any other atmos machines in the same turf that will block this machine from initializing. +// Intended for use when a frame-constructable machine (i.e. not made from pipe fittings) wants to wrench down and connect. +// Returns TRUE if something is blocking, FALSE if its okay to continue. +/obj/machinery/atmospherics/unary/proc/check_for_obstacles() + for(var/obj/machinery/atmospherics/M in loc) + if(M == src) continue + if((M.pipe_flags & pipe_flags & PIPING_ONE_PER_TURF)) //Only one dense/requires density object per tile, eg connectors/cryo/heater/coolers. + visible_message("\The [src]'s cannot be connected, something is hogging the tile!") + return TRUE + if((M.piping_layer != piping_layer) && !((M.pipe_flags | flags) & PIPING_ALL_LAYER)) // Pipes on different layers can't block each other unless they are ALL_LAYER + continue + if(M.get_init_dirs() & get_init_dirs()) // matches at least one direction on either type of pipe + visible_message("\The [src]'s connector can't be connected, there is already a pipe at that location!") + return TRUE + return FALSE diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm index 2a52d168984..8ba6482b0ac 100644 --- a/code/ATMOSPHERICS/components/unary/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm @@ -1,469 +1,469 @@ -#define DEFAULT_PRESSURE_DELTA 10000 - -#define EXTERNAL_PRESSURE_BOUND ONE_ATMOSPHERE -#define INTERNAL_PRESSURE_BOUND 0 -#define PRESSURE_CHECKS 1 - -#define PRESSURE_CHECK_EXTERNAL 1 -#define PRESSURE_CHECK_INTERNAL 2 - -/obj/machinery/atmospherics/unary/vent_pump - icon = 'icons/atmos/vent_pump.dmi' - icon_state = "map_vent" - pipe_state = "uvent" - - name = "Air Vent" - desc = "Has a valve and pump attached to it" - use_power = USE_POWER_OFF - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 30000 //7500 W ~ 10 HP //VOREStation Edit - 30000 W - - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY //connects to regular and supply pipes - blocks_emissive = FALSE - - var/area/initial_loc - level = 1 - var/area_uid - var/id_tag = null - - var/hibernate = 0 //Do we even process? - var/pump_direction = 1 //0 = siphoning, 1 = releasing - - var/external_pressure_bound = EXTERNAL_PRESSURE_BOUND - var/internal_pressure_bound = INTERNAL_PRESSURE_BOUND - - var/pressure_checks = PRESSURE_CHECKS - //1: Do not pass external_pressure_bound - //2: Do not pass internal_pressure_bound - //3: Do not pass either - - // Used when handling incoming radio signals requesting default settings - var/external_pressure_bound_default = EXTERNAL_PRESSURE_BOUND - var/internal_pressure_bound_default = INTERNAL_PRESSURE_BOUND - var/pressure_checks_default = PRESSURE_CHECKS - - var/frequency = 1439 - var/datum/radio_frequency/radio_connection - - var/radio_filter_out - var/radio_filter_in - - //var/datum/looping_sound/air_pump/soundloop - var/static/start_sound = 'sound/machines/air_pump/airpumpstart.ogg' - var/static/stop_sound = 'sound/machines/air_pump/airpumpshutdown.ogg' - - -/obj/machinery/atmospherics/unary/vent_pump/on - use_power = USE_POWER_IDLE - icon_state = "map_vent_out" - -/obj/machinery/atmospherics/unary/vent_pump/aux - icon_state = "map_vent_aux" - icon_connect_type = "-aux" - connect_types = CONNECT_TYPE_AUX //connects to aux pipes - -/obj/machinery/atmospherics/unary/vent_pump/siphon - pump_direction = 0 - -/obj/machinery/atmospherics/unary/vent_pump/siphon/on - use_power = USE_POWER_IDLE - icon_state = "map_vent_in" - -/obj/machinery/atmospherics/unary/vent_pump/siphon/on/atmos - use_power = USE_POWER_IDLE - icon_state = "map_vent_in" - external_pressure_bound = 0 - external_pressure_bound_default = 0 - internal_pressure_bound = 2000 - internal_pressure_bound_default = 2000 - pressure_checks = 2 - pressure_checks_default = 2 - -/obj/machinery/atmospherics/unary/vent_pump/siphon/on/atmos/relief // YW ADDITION relief vent, that connects to the waste line as a subtype to make it easier to map in - name = "Relief vent" - external_pressure_bound = ONE_ATMOSPHERE+2 - external_pressure_bound_default = ONE_ATMOSPHERE+2 - pressure_checks = 1 - pressure_checks_default = 1 - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SCRUBBER - -/obj/machinery/atmospherics/unary/vent_pump/Initialize() - . = ..() - //soundloop = new(list(src), FALSE) - -/obj/machinery/atmospherics/unary/vent_pump/New() - ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP - - icon = null - initial_loc = get_area(loc) - area_uid = "\ref[initial_loc]" - if (!id_tag) - assign_uid() - id_tag = num2text(uid) - -/obj/machinery/atmospherics/unary/vent_pump/proc/update_area() - initial_loc = get_area(loc) - area_uid = "\ref[initial_loc]" - assign_uid() - id_tag = num2text(uid) - - -/obj/machinery/atmospherics/unary/vent_pump/Destroy() - unregister_radio(src, frequency) - if(initial_loc) - initial_loc.air_vent_info -= id_tag - initial_loc.air_vent_names -= id_tag - //QDEL_NULL(soundloop) - return ..() - -/obj/machinery/atmospherics/unary/vent_pump/high_volume - name = "Large Air Vent" - power_channel = EQUIP - power_rating = 45000 //15 kW ~ 20 HP //VOREStation Edit - 45000 - -/obj/machinery/atmospherics/unary/vent_pump/high_volume/aux - icon_state = "map_vent_aux" - icon_connect_type = "-aux" - connect_types = CONNECT_TYPE_AUX //connects to aux pipes - -/obj/machinery/atmospherics/unary/vent_pump/high_volume/New() - ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 - -// VOREStation Edit Start - Wall mounted vents -/obj/machinery/atmospherics/unary/vent_pump/high_volume/wall_mounted - name = "Wall Mounted Air Vent" - -/obj/machinery/atmospherics/unary/vent_pump/high_volume/wall_mounted/can_unwrench() - return FALSE // No way to construct these, so don't let them be removed. - -// Return the air from the turf in "front" of us (opposite the way the pipe is facing) -/obj/machinery/atmospherics/unary/vent_pump/high_volume/wall_mounted/return_air() - var/turf/T = get_step(src, reverse_dir[dir]) - if(isnull(T)) - return ..() - return T.return_air() - -// VOREStation Edit End - -/obj/machinery/atmospherics/unary/vent_pump/engine - name = "Engine Core Vent" - power_channel = ENVIRON - power_rating = 30000 //15 kW ~ 20 HP - -/obj/machinery/atmospherics/unary/vent_pump/engine/New() - ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //meant to match air injector - -/obj/machinery/atmospherics/unary/vent_pump/update_icon(var/safety = 0) - if(!check_icon_cache()) - return - - cut_overlays() - - var/vent_icon = "vent" - - var/turf/T = get_turf(src) - if(!istype(T)) - return - - if(!T.is_plating() && node && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) - vent_icon += "h" - - if(welded) - vent_icon += "weld" - playsound(src, stop_sound, 25, ignore_walls = FALSE, preference = /datum/client_preference/air_pump_noise) - - else if(!use_power || !node || (stat & (NOPOWER|BROKEN))) - vent_icon += "off" - playsound(src, stop_sound, 25, ignore_walls = FALSE, preference = /datum/client_preference/air_pump_noise) - else - vent_icon += "[pump_direction ? "out" : "in"]" - playsound(src, start_sound, 25, ignore_walls = FALSE, preference = /datum/client_preference/air_pump_noise) - - - add_overlay(icon_manager.get_atmos_icon("device", , , vent_icon)) - -/obj/machinery/atmospherics/unary/vent_pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - if(!T.is_plating() && node && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) - return - else - if(node) - add_underlay(T, node, dir, node.icon_connect_type) - else - add_underlay(T,, dir) - -/obj/machinery/atmospherics/unary/vent_pump/hide() - update_icon() - update_underlays() - -/obj/machinery/atmospherics/unary/vent_pump/proc/can_pump() - if(stat & (NOPOWER|BROKEN)) - return 0 - if(!use_power) - return 0 - if(welded) - return 0 - return 1 - -/obj/machinery/atmospherics/unary/vent_pump/process() - ..() - - if (hibernate) - return 1 - - if (!node) - update_use_power(USE_POWER_OFF) - if(!can_pump()) - return 0 - - var/datum/gas_mixture/environment = return_air() // VOREStation Edit - Use our own proc - - var/power_draw = -1 - - //Figure out the target pressure difference - var/pressure_delta = get_pressure_delta(environment) - //src.visible_message("DEBUG >>> [src]: pressure_delta = [pressure_delta]") - - if((environment.temperature || air_contents.temperature) && pressure_delta > 0.5) - if(pump_direction) //internal -> external - var/transfer_moles = calculate_transfer_moles(air_contents, environment, pressure_delta) - power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) - else //external -> internal - var/transfer_moles = calculate_transfer_moles(environment, air_contents, pressure_delta, (network)? network.volume : 0) - - //limit flow rate from turfs - transfer_moles = min(transfer_moles, environment.total_moles*air_contents.volume/environment.volume) //group_multiplier gets divided out here - power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) - - else - //If we're in an area that is fucking ideal, and we don't have to do anything, chances are we won't next tick either so why redo these calculations? - //JESUS FUCK. THERE ARE LITERALLY 250 OF YOU MOTHERFUCKERS ON ZLEVEL ONE AND YOU DO THIS SHIT EVERY TICK WHEN VERY OFTEN THERE IS NO REASON TO - - if(pump_direction && pressure_checks == PRESSURE_CHECK_EXTERNAL && controller_iteration > 10) //99% of all vents - //Fucking hibernate because you ain't doing shit. - hibernate = 1 - spawn(rand(100,200)) //hibernate for 10 or 20 seconds randomly - hibernate = 0 - - - if (power_draw >= 0) - last_power_draw = power_draw - use_power(power_draw) - if(network) - network.update = 1 - - return 1 - -/obj/machinery/atmospherics/unary/vent_pump/proc/get_pressure_delta(datum/gas_mixture/environment) - var/pressure_delta = DEFAULT_PRESSURE_DELTA - var/environment_pressure = environment.return_pressure() - - if(pump_direction) //internal -> external - if(pressure_checks & PRESSURE_CHECK_EXTERNAL) - pressure_delta = min(pressure_delta, external_pressure_bound - environment_pressure) //increasing the pressure here - if(pressure_checks & PRESSURE_CHECK_INTERNAL) - pressure_delta = min(pressure_delta, air_contents.return_pressure() - internal_pressure_bound) //decreasing the pressure here - else //external -> internal - if(pressure_checks & PRESSURE_CHECK_EXTERNAL) - pressure_delta = min(pressure_delta, environment_pressure - external_pressure_bound) //decreasing the pressure here - if(pressure_checks & PRESSURE_CHECK_INTERNAL) - pressure_delta = min(pressure_delta, internal_pressure_bound - air_contents.return_pressure()) //increasing the pressure here - - return pressure_delta - -/obj/machinery/atmospherics/unary/vent_pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - - signal.data = list( - "area" = src.area_uid, - "tag" = src.id_tag, - "device" = "AVP", - "power" = use_power, - "direction" = pump_direction?("release"):("siphon"), - "checks" = pressure_checks, - "internal" = internal_pressure_bound, - "external" = external_pressure_bound, - "timestamp" = world.time, - "sigtype" = "status", - "power_draw" = last_power_draw, - "flow_rate" = last_flow_rate, - ) - - if(!initial_loc.air_vent_names[id_tag]) - var/new_name = "[initial_loc.name] Vent Pump #[initial_loc.air_vent_names.len+1]" - initial_loc.air_vent_names[id_tag] = new_name - src.name = new_name - initial_loc.air_vent_info[id_tag] = signal.data - - radio_connection.post_signal(src, signal, radio_filter_out) - - return 1 - - -/obj/machinery/atmospherics/unary/vent_pump/atmos_init() - ..() - - //some vents work his own special way - radio_filter_in = frequency==1439?(RADIO_FROM_AIRALARM):null - radio_filter_out = frequency==1439?(RADIO_TO_AIRALARM):null - if(frequency) - radio_connection = register_radio(src, frequency, frequency, radio_filter_in) - src.broadcast_status() - -/obj/machinery/atmospherics/unary/vent_pump/receive_signal(datum/signal/signal) - if(stat & (NOPOWER|BROKEN)) - return - - hibernate = 0 - - //log_admin("DEBUG \[[world.timeofday]\]: /obj/machinery/atmospherics/unary/vent_pump/receive_signal([signal.debug_print()])") - if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command")) - return 0 - - if(signal.data["purge"] != null) - pressure_checks &= ~1 - pump_direction = 0 - - if(signal.data["stabalize"] != null) - pressure_checks |= 1 - pump_direction = 1 - - if(signal.data["power"] != null) - update_use_power(text2num(signal.data["power"])) - - if(signal.data["power_toggle"] != null) - update_use_power(!use_power) - - if(signal.data["checks"] != null) - if (signal.data["checks"] == "default") - pressure_checks = pressure_checks_default - else - pressure_checks = text2num(signal.data["checks"]) - - if(signal.data["checks_toggle"] != null) - pressure_checks = (pressure_checks?0:3) - - if(signal.data["direction"] != null) - pump_direction = text2num(signal.data["direction"]) - - if(signal.data["set_internal_pressure"] != null) - if (signal.data["set_internal_pressure"] == "default") - internal_pressure_bound = internal_pressure_bound_default - else - internal_pressure_bound = between(0,text2num(signal.data["set_internal_pressure"]),ONE_ATMOSPHERE*50) - - if(signal.data["set_external_pressure"] != null) - if (signal.data["set_external_pressure"] == "default") - external_pressure_bound = external_pressure_bound_default - else - external_pressure_bound = between(0,text2num(signal.data["set_external_pressure"]),ONE_ATMOSPHERE*50) - - if(signal.data["adjust_internal_pressure"] != null) - internal_pressure_bound = between(0,internal_pressure_bound + text2num(signal.data["adjust_internal_pressure"]),ONE_ATMOSPHERE*50) - - if(signal.data["adjust_external_pressure"] != null) - external_pressure_bound = between(0,external_pressure_bound + text2num(signal.data["adjust_external_pressure"]),ONE_ATMOSPHERE*50) - - if("reset_external_pressure" in signal.data) - external_pressure_bound = ONE_ATMOSPHERE - - if("reset_internal_pressure" in signal.data) - internal_pressure_bound = 0 - - if(signal.data["init"] != null) - name = signal.data["init"] - return - - if(signal.data["status"] != null) - spawn(2) - broadcast_status() - return //do not update_icon - - //log_admin("DEBUG \[[world.timeofday]\]: vent_pump/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]") - spawn(2) - broadcast_status() - update_icon() - return - -/obj/machinery/atmospherics/unary/vent_pump/attackby(obj/item/W, mob/user) - if(W.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/WT = W.get_welder() - if (WT.remove_fuel(0,user)) - to_chat(user, "Now welding the vent.") - if(do_after(user, 20 * WT.toolspeed)) - if(!src || !WT.isOn()) return - playsound(src, WT.usesound, 50, 1) - if(!welded) - user.visible_message("\The [user] welds the vent shut.", "You weld the vent shut.", "You hear welding.") - welded = 1 - update_icon() - else - user.visible_message("[user] unwelds the vent.", "You unweld the vent.", "You hear welding.") - welded = 0 - update_icon() - else - to_chat(user, "The welding tool needs to be on to start this task.") - else - to_chat(user, "You need more welding fuel to complete this task.") - return 1 - else - ..() - -/obj/machinery/atmospherics/unary/vent_pump/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" - else - . += "You are too far away to read the gauge." - if(welded) - . += "It seems welded shut." - -/obj/machinery/atmospherics/unary/vent_pump/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/unary/vent_pump/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.has_tool_quality(TOOL_WRENCH)) - return ..() - if (!(stat & NOPOWER) && use_power) - to_chat(user, "You cannot unwrench \the [src], turn it off first.") - return 1 - var/turf/T = src.loc - if (node && node.level==1 && isturf(T) && !T.is_plating()) - to_chat(user, "You must remove the plating first.") - return 1 - if(!can_unwrench()) - to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - deconstruct() - -#undef DEFAULT_PRESSURE_DELTA - -#undef EXTERNAL_PRESSURE_BOUND -#undef INTERNAL_PRESSURE_BOUND -#undef PRESSURE_CHECKS - -#undef PRESSURE_CHECK_EXTERNAL -#undef PRESSURE_CHECK_INTERNAL +#define DEFAULT_PRESSURE_DELTA 10000 + +#define EXTERNAL_PRESSURE_BOUND ONE_ATMOSPHERE +#define INTERNAL_PRESSURE_BOUND 0 +#define PRESSURE_CHECKS 1 + +#define PRESSURE_CHECK_EXTERNAL 1 +#define PRESSURE_CHECK_INTERNAL 2 + +/obj/machinery/atmospherics/unary/vent_pump + icon = 'icons/atmos/vent_pump.dmi' + icon_state = "map_vent" + pipe_state = "uvent" + + name = "Air Vent" + desc = "Has a valve and pump attached to it" + use_power = USE_POWER_OFF + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 30000 //7500 W ~ 10 HP //VOREStation Edit - 30000 W + + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY //connects to regular and supply pipes + blocks_emissive = FALSE + + var/area/initial_loc + level = 1 + var/area_uid + var/id_tag = null + + var/hibernate = 0 //Do we even process? + var/pump_direction = 1 //0 = siphoning, 1 = releasing + + var/external_pressure_bound = EXTERNAL_PRESSURE_BOUND + var/internal_pressure_bound = INTERNAL_PRESSURE_BOUND + + var/pressure_checks = PRESSURE_CHECKS + //1: Do not pass external_pressure_bound + //2: Do not pass internal_pressure_bound + //3: Do not pass either + + // Used when handling incoming radio signals requesting default settings + var/external_pressure_bound_default = EXTERNAL_PRESSURE_BOUND + var/internal_pressure_bound_default = INTERNAL_PRESSURE_BOUND + var/pressure_checks_default = PRESSURE_CHECKS + + var/frequency = 1439 + var/datum/radio_frequency/radio_connection + + var/radio_filter_out + var/radio_filter_in + + //var/datum/looping_sound/air_pump/soundloop + var/static/start_sound = 'sound/machines/air_pump/airpumpstart.ogg' + var/static/stop_sound = 'sound/machines/air_pump/airpumpshutdown.ogg' + + +/obj/machinery/atmospherics/unary/vent_pump/on + use_power = USE_POWER_IDLE + icon_state = "map_vent_out" + +/obj/machinery/atmospherics/unary/vent_pump/aux + icon_state = "map_vent_aux" + icon_connect_type = "-aux" + connect_types = CONNECT_TYPE_AUX //connects to aux pipes + +/obj/machinery/atmospherics/unary/vent_pump/siphon + pump_direction = 0 + +/obj/machinery/atmospherics/unary/vent_pump/siphon/on + use_power = USE_POWER_IDLE + icon_state = "map_vent_in" + +/obj/machinery/atmospherics/unary/vent_pump/siphon/on/atmos + use_power = USE_POWER_IDLE + icon_state = "map_vent_in" + external_pressure_bound = 0 + external_pressure_bound_default = 0 + internal_pressure_bound = 2000 + internal_pressure_bound_default = 2000 + pressure_checks = 2 + pressure_checks_default = 2 + +/obj/machinery/atmospherics/unary/vent_pump/siphon/on/atmos/relief // YW ADDITION relief vent, that connects to the waste line as a subtype to make it easier to map in + name = "Relief vent" + external_pressure_bound = ONE_ATMOSPHERE+2 + external_pressure_bound_default = ONE_ATMOSPHERE+2 + pressure_checks = 1 + pressure_checks_default = 1 + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SCRUBBER + +/obj/machinery/atmospherics/unary/vent_pump/Initialize() + . = ..() + //soundloop = new(list(src), FALSE) + +/obj/machinery/atmospherics/unary/vent_pump/New() + ..() + air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + + icon = null + initial_loc = get_area(loc) + area_uid = "\ref[initial_loc]" + if (!id_tag) + assign_uid() + id_tag = num2text(uid) + +/obj/machinery/atmospherics/unary/vent_pump/proc/update_area() + initial_loc = get_area(loc) + area_uid = "\ref[initial_loc]" + assign_uid() + id_tag = num2text(uid) + + +/obj/machinery/atmospherics/unary/vent_pump/Destroy() + unregister_radio(src, frequency) + if(initial_loc) + initial_loc.air_vent_info -= id_tag + initial_loc.air_vent_names -= id_tag + //QDEL_NULL(soundloop) + return ..() + +/obj/machinery/atmospherics/unary/vent_pump/high_volume + name = "Large Air Vent" + power_channel = EQUIP + power_rating = 45000 //15 kW ~ 20 HP //VOREStation Edit - 45000 + +/obj/machinery/atmospherics/unary/vent_pump/high_volume/aux + icon_state = "map_vent_aux" + icon_connect_type = "-aux" + connect_types = CONNECT_TYPE_AUX //connects to aux pipes + +/obj/machinery/atmospherics/unary/vent_pump/high_volume/New() + ..() + air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 + +// VOREStation Edit Start - Wall mounted vents +/obj/machinery/atmospherics/unary/vent_pump/high_volume/wall_mounted + name = "Wall Mounted Air Vent" + +/obj/machinery/atmospherics/unary/vent_pump/high_volume/wall_mounted/can_unwrench() + return FALSE // No way to construct these, so don't let them be removed. + +// Return the air from the turf in "front" of us (opposite the way the pipe is facing) +/obj/machinery/atmospherics/unary/vent_pump/high_volume/wall_mounted/return_air() + var/turf/T = get_step(src, reverse_dir[dir]) + if(isnull(T)) + return ..() + return T.return_air() + +// VOREStation Edit End + +/obj/machinery/atmospherics/unary/vent_pump/engine + name = "Engine Core Vent" + power_channel = ENVIRON + power_rating = 30000 //15 kW ~ 20 HP + +/obj/machinery/atmospherics/unary/vent_pump/engine/New() + ..() + air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //meant to match air injector + +/obj/machinery/atmospherics/unary/vent_pump/update_icon(var/safety = 0) + if(!check_icon_cache()) + return + + cut_overlays() + + var/vent_icon = "vent" + + var/turf/T = get_turf(src) + if(!istype(T)) + return + + if(!T.is_plating() && node && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) + vent_icon += "h" + + if(welded) + vent_icon += "weld" + playsound(src, stop_sound, 25, ignore_walls = FALSE, preference = /datum/client_preference/air_pump_noise) + + else if(!use_power || !node || (stat & (NOPOWER|BROKEN))) + vent_icon += "off" + playsound(src, stop_sound, 25, ignore_walls = FALSE, preference = /datum/client_preference/air_pump_noise) + else + vent_icon += "[pump_direction ? "out" : "in"]" + playsound(src, start_sound, 25, ignore_walls = FALSE, preference = /datum/client_preference/air_pump_noise) + + + add_overlay(icon_manager.get_atmos_icon("device", , , vent_icon)) + +/obj/machinery/atmospherics/unary/vent_pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + if(!T.is_plating() && node && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) + return + else + if(node) + add_underlay(T, node, dir, node.icon_connect_type) + else + add_underlay(T,, dir) + +/obj/machinery/atmospherics/unary/vent_pump/hide() + update_icon() + update_underlays() + +/obj/machinery/atmospherics/unary/vent_pump/proc/can_pump() + if(stat & (NOPOWER|BROKEN)) + return 0 + if(!use_power) + return 0 + if(welded) + return 0 + return 1 + +/obj/machinery/atmospherics/unary/vent_pump/process() + ..() + + if (hibernate) + return 1 + + if (!node) + update_use_power(USE_POWER_OFF) + if(!can_pump()) + return 0 + + var/datum/gas_mixture/environment = return_air() // VOREStation Edit - Use our own proc + + var/power_draw = -1 + + //Figure out the target pressure difference + var/pressure_delta = get_pressure_delta(environment) + //src.visible_message("DEBUG >>> [src]: pressure_delta = [pressure_delta]") + + if((environment.temperature || air_contents.temperature) && pressure_delta > 0.5) + if(pump_direction) //internal -> external + var/transfer_moles = calculate_transfer_moles(air_contents, environment, pressure_delta) + power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) + else //external -> internal + var/transfer_moles = calculate_transfer_moles(environment, air_contents, pressure_delta, (network)? network.volume : 0) + + //limit flow rate from turfs + transfer_moles = min(transfer_moles, environment.total_moles*air_contents.volume/environment.volume) //group_multiplier gets divided out here + power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) + + else + //If we're in an area that is fucking ideal, and we don't have to do anything, chances are we won't next tick either so why redo these calculations? + //JESUS FUCK. THERE ARE LITERALLY 250 OF YOU MOTHERFUCKERS ON ZLEVEL ONE AND YOU DO THIS SHIT EVERY TICK WHEN VERY OFTEN THERE IS NO REASON TO + + if(pump_direction && pressure_checks == PRESSURE_CHECK_EXTERNAL && controller_iteration > 10) //99% of all vents + //Fucking hibernate because you ain't doing shit. + hibernate = 1 + spawn(rand(100,200)) //hibernate for 10 or 20 seconds randomly + hibernate = 0 + + + if (power_draw >= 0) + last_power_draw = power_draw + use_power(power_draw) + if(network) + network.update = 1 + + return 1 + +/obj/machinery/atmospherics/unary/vent_pump/proc/get_pressure_delta(datum/gas_mixture/environment) + var/pressure_delta = DEFAULT_PRESSURE_DELTA + var/environment_pressure = environment.return_pressure() + + if(pump_direction) //internal -> external + if(pressure_checks & PRESSURE_CHECK_EXTERNAL) + pressure_delta = min(pressure_delta, external_pressure_bound - environment_pressure) //increasing the pressure here + if(pressure_checks & PRESSURE_CHECK_INTERNAL) + pressure_delta = min(pressure_delta, air_contents.return_pressure() - internal_pressure_bound) //decreasing the pressure here + else //external -> internal + if(pressure_checks & PRESSURE_CHECK_EXTERNAL) + pressure_delta = min(pressure_delta, environment_pressure - external_pressure_bound) //decreasing the pressure here + if(pressure_checks & PRESSURE_CHECK_INTERNAL) + pressure_delta = min(pressure_delta, internal_pressure_bound - air_contents.return_pressure()) //increasing the pressure here + + return pressure_delta + +/obj/machinery/atmospherics/unary/vent_pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + + signal.data = list( + "area" = src.area_uid, + "tag" = src.id_tag, + "device" = "AVP", + "power" = use_power, + "direction" = pump_direction?("release"):("siphon"), + "checks" = pressure_checks, + "internal" = internal_pressure_bound, + "external" = external_pressure_bound, + "timestamp" = world.time, + "sigtype" = "status", + "power_draw" = last_power_draw, + "flow_rate" = last_flow_rate, + ) + + if(!initial_loc.air_vent_names[id_tag]) + var/new_name = "[initial_loc.name] Vent Pump #[initial_loc.air_vent_names.len+1]" + initial_loc.air_vent_names[id_tag] = new_name + src.name = new_name + initial_loc.air_vent_info[id_tag] = signal.data + + radio_connection.post_signal(src, signal, radio_filter_out) + + return 1 + + +/obj/machinery/atmospherics/unary/vent_pump/atmos_init() + ..() + + //some vents work his own special way + radio_filter_in = frequency==1439?(RADIO_FROM_AIRALARM):null + radio_filter_out = frequency==1439?(RADIO_TO_AIRALARM):null + if(frequency) + radio_connection = register_radio(src, frequency, frequency, radio_filter_in) + src.broadcast_status() + +/obj/machinery/atmospherics/unary/vent_pump/receive_signal(datum/signal/signal) + if(stat & (NOPOWER|BROKEN)) + return + + hibernate = 0 + + //log_admin("DEBUG \[[world.timeofday]\]: /obj/machinery/atmospherics/unary/vent_pump/receive_signal([signal.debug_print()])") + if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command")) + return 0 + + if(signal.data["purge"] != null) + pressure_checks &= ~1 + pump_direction = 0 + + if(signal.data["stabalize"] != null) + pressure_checks |= 1 + pump_direction = 1 + + if(signal.data["power"] != null) + update_use_power(text2num(signal.data["power"])) + + if(signal.data["power_toggle"] != null) + update_use_power(!use_power) + + if(signal.data["checks"] != null) + if (signal.data["checks"] == "default") + pressure_checks = pressure_checks_default + else + pressure_checks = text2num(signal.data["checks"]) + + if(signal.data["checks_toggle"] != null) + pressure_checks = (pressure_checks?0:3) + + if(signal.data["direction"] != null) + pump_direction = text2num(signal.data["direction"]) + + if(signal.data["set_internal_pressure"] != null) + if (signal.data["set_internal_pressure"] == "default") + internal_pressure_bound = internal_pressure_bound_default + else + internal_pressure_bound = between(0,text2num(signal.data["set_internal_pressure"]),ONE_ATMOSPHERE*50) + + if(signal.data["set_external_pressure"] != null) + if (signal.data["set_external_pressure"] == "default") + external_pressure_bound = external_pressure_bound_default + else + external_pressure_bound = between(0,text2num(signal.data["set_external_pressure"]),ONE_ATMOSPHERE*50) + + if(signal.data["adjust_internal_pressure"] != null) + internal_pressure_bound = between(0,internal_pressure_bound + text2num(signal.data["adjust_internal_pressure"]),ONE_ATMOSPHERE*50) + + if(signal.data["adjust_external_pressure"] != null) + external_pressure_bound = between(0,external_pressure_bound + text2num(signal.data["adjust_external_pressure"]),ONE_ATMOSPHERE*50) + + if("reset_external_pressure" in signal.data) + external_pressure_bound = ONE_ATMOSPHERE + + if("reset_internal_pressure" in signal.data) + internal_pressure_bound = 0 + + if(signal.data["init"] != null) + name = signal.data["init"] + return + + if(signal.data["status"] != null) + spawn(2) + broadcast_status() + return //do not update_icon + + //log_admin("DEBUG \[[world.timeofday]\]: vent_pump/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]") + spawn(2) + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/unary/vent_pump/attackby(obj/item/W, mob/user) + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if (WT.remove_fuel(0,user)) + to_chat(user, "Now welding the vent.") + if(do_after(user, 20 * WT.toolspeed)) + if(!src || !WT.isOn()) return + playsound(src, WT.usesound, 50, 1) + if(!welded) + user.visible_message("\The [user] welds the vent shut.", "You weld the vent shut.", "You hear welding.") + welded = 1 + update_icon() + else + user.visible_message("[user] unwelds the vent.", "You unweld the vent.", "You hear welding.") + welded = 0 + update_icon() + else + to_chat(user, "The welding tool needs to be on to start this task.") + else + to_chat(user, "You need more welding fuel to complete this task.") + return 1 + else + ..() + +/obj/machinery/atmospherics/unary/vent_pump/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" + else + . += "You are too far away to read the gauge." + if(welded) + . += "It seems welded shut." + +/obj/machinery/atmospherics/unary/vent_pump/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/unary/vent_pump/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + if (!(stat & NOPOWER) && use_power) + to_chat(user, "You cannot unwrench \the [src], turn it off first.") + return 1 + var/turf/T = src.loc + if (node && node.level==1 && isturf(T) && !T.is_plating()) + to_chat(user, "You must remove the plating first.") + return 1 + if(!can_unwrench()) + to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + deconstruct() + +#undef DEFAULT_PRESSURE_DELTA + +#undef EXTERNAL_PRESSURE_BOUND +#undef INTERNAL_PRESSURE_BOUND +#undef PRESSURE_CHECKS + +#undef PRESSURE_CHECK_EXTERNAL +#undef PRESSURE_CHECK_INTERNAL diff --git a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm index 477ae9c552d..1a57841a694 100644 --- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm +++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm @@ -1,300 +1,300 @@ -/obj/machinery/atmospherics/unary/vent_scrubber - icon = 'icons/atmos/vent_scrubber.dmi' - icon_state = "map_scrubber_off" - pipe_state = "scrubber" - - name = "Air Scrubber" - desc = "Has a valve and pump attached to it" - use_power = USE_POWER_OFF - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 7500 //7500 W ~ 10 HP - - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SCRUBBER //connects to regular and scrubber pipes - - level = 1 - - var/area/initial_loc - var/id_tag = null - var/frequency = 1439 - var/datum/radio_frequency/radio_connection - - var/hibernate = 0 //Do we even process? - var/scrubbing = 1 //0 = siphoning, 1 = scrubbing - var/list/scrubbing_gas = list("carbon_dioxide", "phoron") - - var/panic = 0 //is this scrubber panicked? - - var/area_uid - var/radio_filter_out - var/radio_filter_in - -/obj/machinery/atmospherics/unary/vent_scrubber/on - use_power = USE_POWER_IDLE - icon_state = "map_scrubber_on" - -/obj/machinery/atmospherics/unary/vent_scrubber/New() - ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_FILTER - - icon = null - initial_loc = get_area(loc) - area_uid = "\ref[initial_loc]" - if (!id_tag) - assign_uid() - id_tag = num2text(uid) - -/obj/machinery/atmospherics/unary/vent_scrubber/proc/update_area() - initial_loc = get_area(loc) - area_uid = "\ref[initial_loc]" - assign_uid() - id_tag = num2text(uid) - -/obj/machinery/atmospherics/unary/vent_scrubber/Destroy() - unregister_radio(src, frequency) - if(initial_loc) - initial_loc.air_scrub_info -= id_tag - initial_loc.air_scrub_names -= id_tag - return ..() - -/obj/machinery/atmospherics/unary/vent_scrubber/update_icon(var/safety = 0) - if(!check_icon_cache()) - return - - cut_overlays() - - var/scrubber_icon = "scrubber" - - var/turf/T = get_turf(src) - if(!istype(T)) - return - - if(!powered()) - scrubber_icon += "off" - else - scrubber_icon += "[use_power ? "[scrubbing ? "on" : "in"]" : "off"]" - - add_overlay(icon_manager.get_atmos_icon("device", , , scrubber_icon)) - -/obj/machinery/atmospherics/unary/vent_scrubber/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - if(!T.is_plating() && node && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) - return - else - if(node) - add_underlay(T, node, dir, node.icon_connect_type) - else - add_underlay(T,, dir) - -/obj/machinery/atmospherics/unary/vent_scrubber/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, radio_filter_in) - -/obj/machinery/atmospherics/unary/vent_scrubber/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - signal.data = list( - "area" = area_uid, - "tag" = id_tag, - "device" = "AScr", - "timestamp" = world.time, - "power" = use_power, - "scrubbing" = scrubbing, - "panic" = panic, - "filter_o2" = ("oxygen" in scrubbing_gas), - "filter_n2" = ("nitrogen" in scrubbing_gas), - "filter_co2" = ("carbon_dioxide" in scrubbing_gas), - "filter_phoron" = ("phoron" in scrubbing_gas), - "filter_n2o" = ("nitrous_oxide" in scrubbing_gas), - "filter_fuel" = ("volatile_fuel" in scrubbing_gas), - "sigtype" = "status" - ) - if(!initial_loc.air_scrub_names[id_tag]) - var/new_name = "[initial_loc.name] Air Scrubber #[initial_loc.air_scrub_names.len+1]" - initial_loc.air_scrub_names[id_tag] = new_name - src.name = new_name - initial_loc.air_scrub_info[id_tag] = signal.data - radio_connection.post_signal(src, signal, radio_filter_out) - - return 1 - -/obj/machinery/atmospherics/unary/vent_scrubber/atmos_init() - ..() - radio_filter_in = frequency==initial(frequency)?(RADIO_FROM_AIRALARM):null - radio_filter_out = frequency==initial(frequency)?(RADIO_TO_AIRALARM):null - if (frequency) - set_frequency(frequency) - src.broadcast_status() - -/obj/machinery/atmospherics/unary/vent_scrubber/process() - ..() - - if (hibernate) - return 1 - - if (!node) - update_use_power(USE_POWER_OFF) - //broadcast_status() - if(!use_power || (stat & (NOPOWER|BROKEN))) - return 0 - - var/datum/gas_mixture/environment = loc.return_air() - - var/power_draw = -1 - if(scrubbing) - //limit flow rate from turfs - var/transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SCRUBBER_FLOWRATE/environment.volume) //group_multiplier gets divided out here - - power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, power_rating) - else //Just siphon all air - //limit flow rate from turfs - var/transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SIPHON_FLOWRATE/environment.volume) //group_multiplier gets divided out here - - power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) - - if(scrubbing && power_draw < 0 && controller_iteration > 10) //99% of all scrubbers - //Fucking hibernate because you ain't doing shit. - hibernate = 1 - spawn(rand(100,200)) //hibernate for 10 or 20 seconds randomly - hibernate = 0 - - if (power_draw >= 0) - last_power_draw = power_draw - use_power(power_draw) - - if(network) - network.update = 1 - - return 1 - -/obj/machinery/atmospherics/unary/vent_scrubber/hide(var/i) //to make the little pipe section invisible, the icon changes. - update_icon() - update_underlays() - -/obj/machinery/atmospherics/unary/vent_scrubber/receive_signal(datum/signal/signal) - if(stat & (NOPOWER|BROKEN)) - return - if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command")) - return 0 - - if(signal.data["power"] != null) - update_use_power(text2num(signal.data["power"])) - if(signal.data["power_toggle"] != null) - update_use_power(!use_power) - - if(signal.data["panic_siphon"]) //must be before if("scrubbing" thing - panic = text2num(signal.data["panic_siphon"]) - if(panic) - update_use_power(USE_POWER_IDLE) - scrubbing = 0 - else - scrubbing = 1 - if(signal.data["toggle_panic_siphon"] != null) - panic = !panic - if(panic) - update_use_power(USE_POWER_IDLE) - scrubbing = 0 - else - scrubbing = 1 - - if(signal.data["scrubbing"] != null) - scrubbing = text2num(signal.data["scrubbing"]) - if(scrubbing) - panic = 0 - if(signal.data["toggle_scrubbing"]) - scrubbing = !scrubbing - if(scrubbing) - panic = 0 - - var/list/toggle = list() - - if(!isnull(signal.data["o2_scrub"]) && text2num(signal.data["o2_scrub"]) != ("oxygen" in scrubbing_gas)) - toggle += "oxygen" - else if(signal.data["toggle_o2_scrub"]) - toggle += "oxygen" - - if(!isnull(signal.data["n2_scrub"]) && text2num(signal.data["n2_scrub"]) != ("nitrogen" in scrubbing_gas)) - toggle += "nitrogen" - else if(signal.data["toggle_n2_scrub"]) - toggle += "nitrogen" - - if(!isnull(signal.data["co2_scrub"]) && text2num(signal.data["co2_scrub"]) != ("carbon_dioxide" in scrubbing_gas)) - toggle += "carbon_dioxide" - else if(signal.data["toggle_co2_scrub"]) - toggle += "carbon_dioxide" - - if(!isnull(signal.data["tox_scrub"]) && text2num(signal.data["tox_scrub"]) != ("phoron" in scrubbing_gas)) - toggle += "phoron" - else if(signal.data["toggle_tox_scrub"]) - toggle += "phoron" - - if(!isnull(signal.data["n2o_scrub"]) && text2num(signal.data["n2o_scrub"]) != ("nitrous_oxide" in scrubbing_gas)) - toggle += "nitrous_oxide" - else if(signal.data["toggle_n2o_scrub"]) - toggle += "nitrous_oxide" - - if(!isnull(signal.data["fuel_scrub"]) && text2num(signal.data["fuel_scrub"]) != ("volatile_fuel" in scrubbing_gas)) - toggle += "volatile_fuel" - else if(signal.data["toggle_fuel_scrub"]) - toggle += "volatile_fuel" - - scrubbing_gas ^= toggle - - if(signal.data["init"] != null) - name = signal.data["init"] - return - - if(signal.data["status"] != null) - spawn(2) - broadcast_status() - return //do not update_icon - -// log_admin("DEBUG \[[world.timeofday]\]: vent_scrubber/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]") - spawn(2) - broadcast_status() - update_icon() - return - -/obj/machinery/atmospherics/unary/vent_scrubber/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/unary/vent_scrubber/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.has_tool_quality(TOOL_WRENCH)) - return ..() - if (!(stat & NOPOWER) && use_power) - to_chat(user, "You cannot unwrench \the [src], turn it off first.") - return 1 - var/turf/T = src.loc - if (node && node.level==1 && isturf(T) && !T.is_plating()) - to_chat(user, "You must remove the plating first.") - return 1 - if(!can_unwrench()) - to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - deconstruct() - -/obj/machinery/atmospherics/unary/vent_scrubber/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" - else - . += "You are too far away to read the gauge." +/obj/machinery/atmospherics/unary/vent_scrubber + icon = 'icons/atmos/vent_scrubber.dmi' + icon_state = "map_scrubber_off" + pipe_state = "scrubber" + + name = "Air Scrubber" + desc = "Has a valve and pump attached to it" + use_power = USE_POWER_OFF + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 7500 //7500 W ~ 10 HP + + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SCRUBBER //connects to regular and scrubber pipes + + level = 1 + + var/area/initial_loc + var/id_tag = null + var/frequency = 1439 + var/datum/radio_frequency/radio_connection + + var/hibernate = 0 //Do we even process? + var/scrubbing = 1 //0 = siphoning, 1 = scrubbing + var/list/scrubbing_gas = list("carbon_dioxide", "phoron") + + var/panic = 0 //is this scrubber panicked? + + var/area_uid + var/radio_filter_out + var/radio_filter_in + +/obj/machinery/atmospherics/unary/vent_scrubber/on + use_power = USE_POWER_IDLE + icon_state = "map_scrubber_on" + +/obj/machinery/atmospherics/unary/vent_scrubber/New() + ..() + air_contents.volume = ATMOS_DEFAULT_VOLUME_FILTER + + icon = null + initial_loc = get_area(loc) + area_uid = "\ref[initial_loc]" + if (!id_tag) + assign_uid() + id_tag = num2text(uid) + +/obj/machinery/atmospherics/unary/vent_scrubber/proc/update_area() + initial_loc = get_area(loc) + area_uid = "\ref[initial_loc]" + assign_uid() + id_tag = num2text(uid) + +/obj/machinery/atmospherics/unary/vent_scrubber/Destroy() + unregister_radio(src, frequency) + if(initial_loc) + initial_loc.air_scrub_info -= id_tag + initial_loc.air_scrub_names -= id_tag + return ..() + +/obj/machinery/atmospherics/unary/vent_scrubber/update_icon(var/safety = 0) + if(!check_icon_cache()) + return + + cut_overlays() + + var/scrubber_icon = "scrubber" + + var/turf/T = get_turf(src) + if(!istype(T)) + return + + if(!powered()) + scrubber_icon += "off" + else + scrubber_icon += "[use_power ? "[scrubbing ? "on" : "in"]" : "off"]" + + add_overlay(icon_manager.get_atmos_icon("device", , , scrubber_icon)) + +/obj/machinery/atmospherics/unary/vent_scrubber/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + if(!T.is_plating() && node && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) + return + else + if(node) + add_underlay(T, node, dir, node.icon_connect_type) + else + add_underlay(T,, dir) + +/obj/machinery/atmospherics/unary/vent_scrubber/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency, radio_filter_in) + +/obj/machinery/atmospherics/unary/vent_scrubber/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + signal.data = list( + "area" = area_uid, + "tag" = id_tag, + "device" = "AScr", + "timestamp" = world.time, + "power" = use_power, + "scrubbing" = scrubbing, + "panic" = panic, + "filter_o2" = ("oxygen" in scrubbing_gas), + "filter_n2" = ("nitrogen" in scrubbing_gas), + "filter_co2" = ("carbon_dioxide" in scrubbing_gas), + "filter_phoron" = ("phoron" in scrubbing_gas), + "filter_n2o" = ("nitrous_oxide" in scrubbing_gas), + "filter_fuel" = ("volatile_fuel" in scrubbing_gas), + "sigtype" = "status" + ) + if(!initial_loc.air_scrub_names[id_tag]) + var/new_name = "[initial_loc.name] Air Scrubber #[initial_loc.air_scrub_names.len+1]" + initial_loc.air_scrub_names[id_tag] = new_name + src.name = new_name + initial_loc.air_scrub_info[id_tag] = signal.data + radio_connection.post_signal(src, signal, radio_filter_out) + + return 1 + +/obj/machinery/atmospherics/unary/vent_scrubber/atmos_init() + ..() + radio_filter_in = frequency==initial(frequency)?(RADIO_FROM_AIRALARM):null + radio_filter_out = frequency==initial(frequency)?(RADIO_TO_AIRALARM):null + if (frequency) + set_frequency(frequency) + src.broadcast_status() + +/obj/machinery/atmospherics/unary/vent_scrubber/process() + ..() + + if (hibernate) + return 1 + + if (!node) + update_use_power(USE_POWER_OFF) + //broadcast_status() + if(!use_power || (stat & (NOPOWER|BROKEN))) + return 0 + + var/datum/gas_mixture/environment = loc.return_air() + + var/power_draw = -1 + if(scrubbing) + //limit flow rate from turfs + var/transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SCRUBBER_FLOWRATE/environment.volume) //group_multiplier gets divided out here + + power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, power_rating) + else //Just siphon all air + //limit flow rate from turfs + var/transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SIPHON_FLOWRATE/environment.volume) //group_multiplier gets divided out here + + power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) + + if(scrubbing && power_draw < 0 && controller_iteration > 10) //99% of all scrubbers + //Fucking hibernate because you ain't doing shit. + hibernate = 1 + spawn(rand(100,200)) //hibernate for 10 or 20 seconds randomly + hibernate = 0 + + if (power_draw >= 0) + last_power_draw = power_draw + use_power(power_draw) + + if(network) + network.update = 1 + + return 1 + +/obj/machinery/atmospherics/unary/vent_scrubber/hide(var/i) //to make the little pipe section invisible, the icon changes. + update_icon() + update_underlays() + +/obj/machinery/atmospherics/unary/vent_scrubber/receive_signal(datum/signal/signal) + if(stat & (NOPOWER|BROKEN)) + return + if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command")) + return 0 + + if(signal.data["power"] != null) + update_use_power(text2num(signal.data["power"])) + if(signal.data["power_toggle"] != null) + update_use_power(!use_power) + + if(signal.data["panic_siphon"]) //must be before if("scrubbing" thing + panic = text2num(signal.data["panic_siphon"]) + if(panic) + update_use_power(USE_POWER_IDLE) + scrubbing = 0 + else + scrubbing = 1 + if(signal.data["toggle_panic_siphon"] != null) + panic = !panic + if(panic) + update_use_power(USE_POWER_IDLE) + scrubbing = 0 + else + scrubbing = 1 + + if(signal.data["scrubbing"] != null) + scrubbing = text2num(signal.data["scrubbing"]) + if(scrubbing) + panic = 0 + if(signal.data["toggle_scrubbing"]) + scrubbing = !scrubbing + if(scrubbing) + panic = 0 + + var/list/toggle = list() + + if(!isnull(signal.data["o2_scrub"]) && text2num(signal.data["o2_scrub"]) != ("oxygen" in scrubbing_gas)) + toggle += "oxygen" + else if(signal.data["toggle_o2_scrub"]) + toggle += "oxygen" + + if(!isnull(signal.data["n2_scrub"]) && text2num(signal.data["n2_scrub"]) != ("nitrogen" in scrubbing_gas)) + toggle += "nitrogen" + else if(signal.data["toggle_n2_scrub"]) + toggle += "nitrogen" + + if(!isnull(signal.data["co2_scrub"]) && text2num(signal.data["co2_scrub"]) != ("carbon_dioxide" in scrubbing_gas)) + toggle += "carbon_dioxide" + else if(signal.data["toggle_co2_scrub"]) + toggle += "carbon_dioxide" + + if(!isnull(signal.data["tox_scrub"]) && text2num(signal.data["tox_scrub"]) != ("phoron" in scrubbing_gas)) + toggle += "phoron" + else if(signal.data["toggle_tox_scrub"]) + toggle += "phoron" + + if(!isnull(signal.data["n2o_scrub"]) && text2num(signal.data["n2o_scrub"]) != ("nitrous_oxide" in scrubbing_gas)) + toggle += "nitrous_oxide" + else if(signal.data["toggle_n2o_scrub"]) + toggle += "nitrous_oxide" + + if(!isnull(signal.data["fuel_scrub"]) && text2num(signal.data["fuel_scrub"]) != ("volatile_fuel" in scrubbing_gas)) + toggle += "volatile_fuel" + else if(signal.data["toggle_fuel_scrub"]) + toggle += "volatile_fuel" + + scrubbing_gas ^= toggle + + if(signal.data["init"] != null) + name = signal.data["init"] + return + + if(signal.data["status"] != null) + spawn(2) + broadcast_status() + return //do not update_icon + +// log_admin("DEBUG \[[world.timeofday]\]: vent_scrubber/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]") + spawn(2) + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/unary/vent_scrubber/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/unary/vent_scrubber/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + if (!(stat & NOPOWER) && use_power) + to_chat(user, "You cannot unwrench \the [src], turn it off first.") + return 1 + var/turf/T = src.loc + if (node && node.level==1 && isturf(T) && !T.is_plating()) + to_chat(user, "You must remove the plating first.") + return 1 + if(!can_unwrench()) + to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + deconstruct() + +/obj/machinery/atmospherics/unary/vent_scrubber/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" + else + . += "You are too far away to read the gauge." diff --git a/code/ATMOSPHERICS/components/valve.dm b/code/ATMOSPHERICS/components/valve.dm index 303e84b78c3..1996c223366 100644 --- a/code/ATMOSPHERICS/components/valve.dm +++ b/code/ATMOSPHERICS/components/valve.dm @@ -1,311 +1,311 @@ -/obj/machinery/atmospherics/valve - icon = 'icons/atmos/valve.dmi' - icon_state = "map_valve0" - construction_type = /obj/item/pipe/binary - pipe_state = "mvalve" - - name = "manual valve" - desc = "A pipe valve" - - level = 1 - dir = SOUTH - initialize_directions = SOUTH|NORTH - - var/open = 0 - var/openDuringInit = 0 - - - var/datum/pipe_network/network_node1 - var/datum/pipe_network/network_node2 - -/obj/machinery/atmospherics/valve/open - open = 1 - icon_state = "map_valve1" - -/obj/machinery/atmospherics/valve/update_icon(animation) - if(animation) - flick("valve[src.open][!src.open]",src) - else - icon_state = "valve[open]" - -/obj/machinery/atmospherics/valve/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, get_dir(src, node1)) - add_underlay(T, node2, get_dir(src, node2)) - -/obj/machinery/atmospherics/valve/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/valve/init_dir() - switch(dir) - if(NORTH,SOUTH) - initialize_directions = NORTH|SOUTH - if(EAST,WEST) - initialize_directions = EAST|WEST - -/obj/machinery/atmospherics/valve/get_neighbor_nodes_for_init() - return list(node1, node2) - -/obj/machinery/atmospherics/valve/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - if(reference == node1) - network_node1 = new_network - if(open) - network_node2 = new_network - else if(reference == node2) - network_node2 = new_network - if(open) - network_node1 = new_network - - if(new_network.normal_members.Find(src)) - return 0 - - new_network.normal_members += src - - if(open) - if(reference == node1) - if(node2) - return node2.network_expand(new_network, src) - else if(reference == node2) - if(node1) - return node1.network_expand(new_network, src) - - return null - -/obj/machinery/atmospherics/valve/Destroy() - . = ..() - - if(node1) - node1.disconnect(src) - qdel(network_node1) - if(node2) - node2.disconnect(src) - qdel(network_node2) - - node1 = null - node2 = null - -/obj/machinery/atmospherics/valve/proc/open() - if(open) return 0 - - open = 1 - update_icon() - - if(network_node1&&network_node2) - network_node1.merge(network_node2) - network_node2 = network_node1 - - if(network_node1) - network_node1.update = 1 - else if(network_node2) - network_node2.update = 1 - - return 1 - -/obj/machinery/atmospherics/valve/proc/close() - if(!open) - return 0 - - open = 0 - update_icon() - - if(network_node1) - qdel(network_node1) - if(network_node2) - qdel(network_node2) - - build_network() - - return 1 - -/obj/machinery/atmospherics/valve/proc/normalize_dir() - if(dir==3) - set_dir(1) - else if(dir==12) - set_dir(4) - -/obj/machinery/atmospherics/valve/attack_ai(mob/user as mob) - return - -/obj/machinery/atmospherics/valve/attack_hand(mob/user as mob) - src.add_fingerprint(usr) - update_icon(1) - sleep(10) - if (src.open) - src.close() - else - src.open() - -/obj/machinery/atmospherics/valve/process() - ..() - . = PROCESS_KILL - - return - -/obj/machinery/atmospherics/valve/atmos_init() - normalize_dir() - - var/node1_dir - var/node2_dir - - for(var/direction in cardinal) - if(direction&initialize_directions) - if (!node1_dir) - node1_dir = direction - else if (!node2_dir) - node2_dir = direction - - STANDARD_ATMOS_CHOOSE_NODE(1, node1_dir) - STANDARD_ATMOS_CHOOSE_NODE(2, node2_dir) - - build_network() - - update_icon() - update_underlays() - - if(openDuringInit) - close() - open() - openDuringInit = 0 - -/obj/machinery/atmospherics/valve/build_network() - if(!network_node1 && node1) - network_node1 = new /datum/pipe_network() - network_node1.normal_members += src - network_node1.build_network(node1, src) - - if(!network_node2 && node2) - network_node2 = new /datum/pipe_network() - network_node2.normal_members += src - network_node2.build_network(node2, src) - -/obj/machinery/atmospherics/valve/return_network(obj/machinery/atmospherics/reference) - build_network() - - if(reference==node1) - return network_node1 - - if(reference==node2) - return network_node2 - - return null - -/obj/machinery/atmospherics/valve/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) - if(network_node1 == old_network) - network_node1 = new_network - if(network_node2 == old_network) - network_node2 = new_network - - return 1 - -/obj/machinery/atmospherics/valve/return_network_air(datum/pipe_network/reference) - return null - -/obj/machinery/atmospherics/valve/disconnect(obj/machinery/atmospherics/reference) - if(reference==node1) - qdel(network_node1) - node1 = null - - else if(reference==node2) - qdel(network_node2) - node2 = null - - update_underlays() - - return null - -/obj/machinery/atmospherics/valve/digital // can be controlled by AI - name = "digital valve" - desc = "A digitally controlled valve." - icon = 'icons/atmos/digital_valve.dmi' - pipe_state = "dvalve" - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/valve/digital/Destroy() - unregister_radio(src, frequency) - . = ..() - -/obj/machinery/atmospherics/valve/digital/attack_ai(mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/atmospherics/valve/digital/attack_hand(mob/user as mob) - if(!powered()) - return - if(!src.allowed(user)) - to_chat(user, "Access denied.") - return - ..() - -/obj/machinery/atmospherics/valve/digital/open - open = 1 - icon_state = "map_valve1" - -/obj/machinery/atmospherics/valve/digital/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/valve/digital/update_icon() - ..() - if(!powered()) - icon_state = "valve[open]nopower" - -/obj/machinery/atmospherics/valve/digital/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/atmospherics/valve/digital/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/valve/digital/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id)) - return 0 - - switch(signal.data["command"]) - if("valve_open") - if(!open) - open() - - if("valve_close") - if(open) - close() - - if("valve_toggle") - if(open) - close() - else - open() - -/obj/machinery/atmospherics/valve/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.has_tool_quality(TOOL_WRENCH)) - return ..() - if (istype(src, /obj/machinery/atmospherics/valve/digital) && !src.allowed(user)) - to_chat(user, "Access denied.") - return 1 - if(!can_unwrench()) - to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - deconstruct() - -/obj/machinery/atmospherics/valve/examine(mob/user) - . = ..() - . += "It is [open ? "open" : "closed"]." +/obj/machinery/atmospherics/valve + icon = 'icons/atmos/valve.dmi' + icon_state = "map_valve0" + construction_type = /obj/item/pipe/binary + pipe_state = "mvalve" + + name = "manual valve" + desc = "A pipe valve" + + level = 1 + dir = SOUTH + initialize_directions = SOUTH|NORTH + + var/open = 0 + var/openDuringInit = 0 + + + var/datum/pipe_network/network_node1 + var/datum/pipe_network/network_node2 + +/obj/machinery/atmospherics/valve/open + open = 1 + icon_state = "map_valve1" + +/obj/machinery/atmospherics/valve/update_icon(animation) + if(animation) + flick("valve[src.open][!src.open]",src) + else + icon_state = "valve[open]" + +/obj/machinery/atmospherics/valve/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, get_dir(src, node1)) + add_underlay(T, node2, get_dir(src, node2)) + +/obj/machinery/atmospherics/valve/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/valve/init_dir() + switch(dir) + if(NORTH,SOUTH) + initialize_directions = NORTH|SOUTH + if(EAST,WEST) + initialize_directions = EAST|WEST + +/obj/machinery/atmospherics/valve/get_neighbor_nodes_for_init() + return list(node1, node2) + +/obj/machinery/atmospherics/valve/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + if(reference == node1) + network_node1 = new_network + if(open) + network_node2 = new_network + else if(reference == node2) + network_node2 = new_network + if(open) + network_node1 = new_network + + if(new_network.normal_members.Find(src)) + return 0 + + new_network.normal_members += src + + if(open) + if(reference == node1) + if(node2) + return node2.network_expand(new_network, src) + else if(reference == node2) + if(node1) + return node1.network_expand(new_network, src) + + return null + +/obj/machinery/atmospherics/valve/Destroy() + . = ..() + + if(node1) + node1.disconnect(src) + qdel(network_node1) + if(node2) + node2.disconnect(src) + qdel(network_node2) + + node1 = null + node2 = null + +/obj/machinery/atmospherics/valve/proc/open() + if(open) return 0 + + open = 1 + update_icon() + + if(network_node1&&network_node2) + network_node1.merge(network_node2) + network_node2 = network_node1 + + if(network_node1) + network_node1.update = 1 + else if(network_node2) + network_node2.update = 1 + + return 1 + +/obj/machinery/atmospherics/valve/proc/close() + if(!open) + return 0 + + open = 0 + update_icon() + + if(network_node1) + qdel(network_node1) + if(network_node2) + qdel(network_node2) + + build_network() + + return 1 + +/obj/machinery/atmospherics/valve/proc/normalize_dir() + if(dir==3) + set_dir(1) + else if(dir==12) + set_dir(4) + +/obj/machinery/atmospherics/valve/attack_ai(mob/user as mob) + return + +/obj/machinery/atmospherics/valve/attack_hand(mob/user as mob) + src.add_fingerprint(usr) + update_icon(1) + sleep(10) + if (src.open) + src.close() + else + src.open() + +/obj/machinery/atmospherics/valve/process() + ..() + . = PROCESS_KILL + + return + +/obj/machinery/atmospherics/valve/atmos_init() + normalize_dir() + + var/node1_dir + var/node2_dir + + for(var/direction in cardinal) + if(direction&initialize_directions) + if (!node1_dir) + node1_dir = direction + else if (!node2_dir) + node2_dir = direction + + STANDARD_ATMOS_CHOOSE_NODE(1, node1_dir) + STANDARD_ATMOS_CHOOSE_NODE(2, node2_dir) + + build_network() + + update_icon() + update_underlays() + + if(openDuringInit) + close() + open() + openDuringInit = 0 + +/obj/machinery/atmospherics/valve/build_network() + if(!network_node1 && node1) + network_node1 = new /datum/pipe_network() + network_node1.normal_members += src + network_node1.build_network(node1, src) + + if(!network_node2 && node2) + network_node2 = new /datum/pipe_network() + network_node2.normal_members += src + network_node2.build_network(node2, src) + +/obj/machinery/atmospherics/valve/return_network(obj/machinery/atmospherics/reference) + build_network() + + if(reference==node1) + return network_node1 + + if(reference==node2) + return network_node2 + + return null + +/obj/machinery/atmospherics/valve/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + if(network_node1 == old_network) + network_node1 = new_network + if(network_node2 == old_network) + network_node2 = new_network + + return 1 + +/obj/machinery/atmospherics/valve/return_network_air(datum/pipe_network/reference) + return null + +/obj/machinery/atmospherics/valve/disconnect(obj/machinery/atmospherics/reference) + if(reference==node1) + qdel(network_node1) + node1 = null + + else if(reference==node2) + qdel(network_node2) + node2 = null + + update_underlays() + + return null + +/obj/machinery/atmospherics/valve/digital // can be controlled by AI + name = "digital valve" + desc = "A digitally controlled valve." + icon = 'icons/atmos/digital_valve.dmi' + pipe_state = "dvalve" + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/valve/digital/Destroy() + unregister_radio(src, frequency) + . = ..() + +/obj/machinery/atmospherics/valve/digital/attack_ai(mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/atmospherics/valve/digital/attack_hand(mob/user as mob) + if(!powered()) + return + if(!src.allowed(user)) + to_chat(user, "Access denied.") + return + ..() + +/obj/machinery/atmospherics/valve/digital/open + open = 1 + icon_state = "map_valve1" + +/obj/machinery/atmospherics/valve/digital/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/valve/digital/update_icon() + ..() + if(!powered()) + icon_state = "valve[open]nopower" + +/obj/machinery/atmospherics/valve/digital/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/atmospherics/valve/digital/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/valve/digital/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id)) + return 0 + + switch(signal.data["command"]) + if("valve_open") + if(!open) + open() + + if("valve_close") + if(open) + close() + + if("valve_toggle") + if(open) + close() + else + open() + +/obj/machinery/atmospherics/valve/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + if (istype(src, /obj/machinery/atmospherics/valve/digital) && !src.allowed(user)) + to_chat(user, "Access denied.") + return 1 + if(!can_unwrench()) + to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + deconstruct() + +/obj/machinery/atmospherics/valve/examine(mob/user) + . = ..() + . += "It is [open ? "open" : "closed"]." diff --git a/code/ATMOSPHERICS/datum_pipe_network.dm b/code/ATMOSPHERICS/datum_pipe_network.dm index a611e30a893..20a4dbc159f 100644 --- a/code/ATMOSPHERICS/datum_pipe_network.dm +++ b/code/ATMOSPHERICS/datum_pipe_network.dm @@ -1,90 +1,90 @@ -var/global/list/datum/pipe_network/pipe_networks = list() // TODO - Move into SSmachines - -/datum/pipe_network - var/list/datum/gas_mixture/gases = list() //All of the gas_mixtures continuously connected in this network - var/volume = 0 //caches the total volume for atmos machines to use in gas calculations - - var/list/obj/machinery/atmospherics/normal_members = list() - var/list/datum/pipeline/line_members = list() - //membership roster to go through for updates and what not - - var/list/leaks = list() - - var/update = 1 - //var/datum/gas_mixture/air_transient = null - -/datum/pipe_network/Destroy() - STOP_PROCESSING_PIPENET(src) - for(var/datum/pipeline/line_member in line_members) - line_member.network = null - for(var/obj/machinery/atmospherics/normal_member in normal_members) - normal_member.reassign_network(src, null) - gases.Cut() // Do not qdel the gases, we don't own them - leaks.Cut() - return ..() - -/datum/pipe_network/process() - //Equalize gases amongst pipe if called for - if(update) - update = 0 - reconcile_air() //equalize_gases(gases) - - listclearnulls(leaks) // Let's not have forever-seals. - - //Give pipelines their process call for pressure checking and what not. Have to remove pressure checks for the time being as pipes dont radiate heat - Mport - //for(var/datum/pipeline/line_member in line_members) - // line_member.process() - -/datum/pipe_network/proc/build_network(obj/machinery/atmospherics/start_normal, obj/machinery/atmospherics/reference) - //Purpose: Generate membership roster - //Notes: Assuming that members will add themselves to appropriate roster in network_expand() - - if(!start_normal) - qdel(src) - return - - start_normal.network_expand(src, reference) - - update_network_gases() - - if((normal_members.len>0)||(line_members.len>0)) - START_PROCESSING_PIPENET(src) - else - qdel(src) - -/datum/pipe_network/proc/merge(datum/pipe_network/giver) - if(giver==src) return 0 - - normal_members |= giver.normal_members - - line_members |= giver.line_members - - leaks |= giver.leaks - - for(var/obj/machinery/atmospherics/normal_member in giver.normal_members) - normal_member.reassign_network(giver, src) - - for(var/datum/pipeline/line_member in giver.line_members) - line_member.network = src - - update_network_gases() - return 1 - -/datum/pipe_network/proc/update_network_gases() - //Go through membership roster and make sure gases is up to date - - gases = list() - volume = 0 - - for(var/obj/machinery/atmospherics/normal_member in normal_members) - var/result = normal_member.return_network_air(src) - if(result) gases += result - - for(var/datum/pipeline/line_member in line_members) - gases += line_member.air - - for(var/datum/gas_mixture/air in gases) - volume += air.volume - -/datum/pipe_network/proc/reconcile_air() - equalize_gases(gases) +var/global/list/datum/pipe_network/pipe_networks = list() // TODO - Move into SSmachines + +/datum/pipe_network + var/list/datum/gas_mixture/gases = list() //All of the gas_mixtures continuously connected in this network + var/volume = 0 //caches the total volume for atmos machines to use in gas calculations + + var/list/obj/machinery/atmospherics/normal_members = list() + var/list/datum/pipeline/line_members = list() + //membership roster to go through for updates and what not + + var/list/leaks = list() + + var/update = 1 + //var/datum/gas_mixture/air_transient = null + +/datum/pipe_network/Destroy() + STOP_PROCESSING_PIPENET(src) + for(var/datum/pipeline/line_member in line_members) + line_member.network = null + for(var/obj/machinery/atmospherics/normal_member in normal_members) + normal_member.reassign_network(src, null) + gases.Cut() // Do not qdel the gases, we don't own them + leaks.Cut() + return ..() + +/datum/pipe_network/process() + //Equalize gases amongst pipe if called for + if(update) + update = 0 + reconcile_air() //equalize_gases(gases) + + listclearnulls(leaks) // Let's not have forever-seals. + + //Give pipelines their process call for pressure checking and what not. Have to remove pressure checks for the time being as pipes dont radiate heat - Mport + //for(var/datum/pipeline/line_member in line_members) + // line_member.process() + +/datum/pipe_network/proc/build_network(obj/machinery/atmospherics/start_normal, obj/machinery/atmospherics/reference) + //Purpose: Generate membership roster + //Notes: Assuming that members will add themselves to appropriate roster in network_expand() + + if(!start_normal) + qdel(src) + return + + start_normal.network_expand(src, reference) + + update_network_gases() + + if((normal_members.len>0)||(line_members.len>0)) + START_PROCESSING_PIPENET(src) + else + qdel(src) + +/datum/pipe_network/proc/merge(datum/pipe_network/giver) + if(giver==src) return 0 + + normal_members |= giver.normal_members + + line_members |= giver.line_members + + leaks |= giver.leaks + + for(var/obj/machinery/atmospherics/normal_member in giver.normal_members) + normal_member.reassign_network(giver, src) + + for(var/datum/pipeline/line_member in giver.line_members) + line_member.network = src + + update_network_gases() + return 1 + +/datum/pipe_network/proc/update_network_gases() + //Go through membership roster and make sure gases is up to date + + gases = list() + volume = 0 + + for(var/obj/machinery/atmospherics/normal_member in normal_members) + var/result = normal_member.return_network_air(src) + if(result) gases += result + + for(var/datum/pipeline/line_member in line_members) + gases += line_member.air + + for(var/datum/gas_mixture/air in gases) + volume += air.volume + +/datum/pipe_network/proc/reconcile_air() + equalize_gases(gases) diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm index 7a1f5cce953..e3b91813ba0 100644 --- a/code/ATMOSPHERICS/datum_pipeline.dm +++ b/code/ATMOSPHERICS/datum_pipeline.dm @@ -1,241 +1,241 @@ - -/datum/pipeline - var/datum/gas_mixture/air - - var/list/obj/machinery/atmospherics/pipe/members - var/list/obj/machinery/atmospherics/pipe/edges //Used for building networks - - // Nodes that are leaking. Used for A.S. Valves. - var/list/leaks = list() - - var/datum/pipe_network/network - - var/alert_pressure = 0 - -/datum/pipeline/Destroy() - QDEL_NULL(network) - - if(air && air.volume) - temporarily_store_air() - for(var/obj/machinery/atmospherics/pipe/P in members) - P.parent = null - members = null - edges = null - leaks = null - . = ..() - -/datum/pipeline/process()//This use to be called called from the pipe networks - - //Check to see if pressure is within acceptable limits - var/pressure = air.return_pressure() - if(pressure > alert_pressure) - for(var/obj/machinery/atmospherics/pipe/member in members) - if(!member.check_pressure(pressure)) - break //Only delete 1 pipe per process - -/datum/pipeline/proc/temporarily_store_air() - //Update individual gas_mixtures by volume ratio - - for(var/obj/machinery/atmospherics/pipe/member in members) - member.air_temporary = new - member.air_temporary.copy_from(air) - member.air_temporary.volume = member.volume - member.air_temporary.multiply(member.volume / air.volume) - -/datum/pipeline/proc/build_pipeline(obj/machinery/atmospherics/pipe/base) - air = new - - var/list/possible_expansions = list(base) - members = list(base) - edges = list() - - var/volume = base.volume - base.parent = src - alert_pressure = base.alert_pressure - - if(base.air_temporary) - air = base.air_temporary - base.air_temporary = null - else - air = new - - if(base.leaking) - leaks |= base - - while(possible_expansions.len>0) - for(var/obj/machinery/atmospherics/pipe/borderline in possible_expansions) - - var/list/result = borderline.pipeline_expansion() - var/edge_check = result.len - - if(result.len>0) - for(var/obj/machinery/atmospherics/pipe/item in result) - - if(item.in_stasis) - continue - - if(!members.Find(item)) - members += item - possible_expansions += item - - volume += item.volume - item.parent = src - - alert_pressure = min(alert_pressure, item.alert_pressure) - - if(item.air_temporary) - air.merge(item.air_temporary) - - if(item.leaking) - leaks |= item - - edge_check-- - - if(edge_check>0) - edges += borderline - - possible_expansions -= borderline - - air.volume = volume - -/datum/pipeline/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - - if(new_network.line_members.Find(src)) - return 0 - - new_network.line_members += src - - network = new_network - network.leaks |= leaks - - for(var/obj/machinery/atmospherics/pipe/edge in edges) - for(var/obj/machinery/atmospherics/result in edge.pipeline_expansion()) - if(!istype(result,/obj/machinery/atmospherics/pipe) && (result!=reference)) - result.network_expand(new_network, edge) - - return 1 - -/datum/pipeline/proc/return_network(obj/machinery/atmospherics/reference) - if(!network) - network = new /datum/pipe_network() - network.build_network(src, null) - //technically passing these parameters should not be allowed - //however pipe_network.build_network(..) and pipeline.network_extend(...) - // were setup to properly handle this case - - return network - -/datum/pipeline/proc/mingle_with_turf(turf/simulated/target, mingle_volume) - var/datum/gas_mixture/air_sample = air.remove_ratio(mingle_volume/air.volume) - air_sample.volume = mingle_volume - - if(istype(target) && target.zone) - //Have to consider preservation of group statuses - var/datum/gas_mixture/turf_copy = new - var/datum/gas_mixture/turf_original = new - - turf_copy.copy_from(target.zone.air) - turf_copy.volume = target.zone.air.volume //Copy a good representation of the turf from parent group - turf_original.copy_from(turf_copy) - - equalize_gases(list(air_sample, turf_copy)) - air.merge(air_sample) - - - target.zone.air.remove(turf_original.total_moles) - target.zone.air.merge(turf_copy) - - else - var/datum/gas_mixture/turf_air = target.return_air() - - equalize_gases(list(air_sample, turf_air)) - air.merge(air_sample) - //turf_air already modified by equalize_gases() - - if(network) - network.update = 1 - -/datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity) - var/total_heat_capacity = air.heat_capacity() - var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) - - if(istype(target, /turf/simulated)) - var/turf/simulated/modeled_location = target - - if (modeled_location.special_temperature) - air.temperature += thermal_conductivity * (modeled_location.special_temperature - air.temperature) - if (air.temperature < TCMB) - air.temperature = TCMB - if (network) - network.update = TRUE - - if(modeled_location.blocks_air) - - if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0)) - var/delta_temperature = air.temperature - modeled_location.temperature - - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity)) - - air.temperature -= heat/total_heat_capacity - modeled_location.temperature += heat/modeled_location.heat_capacity - - else - var/delta_temperature = 0 - var/sharer_heat_capacity = 0 - - if(modeled_location.zone) - delta_temperature = (air.temperature - modeled_location.zone.air.temperature) - sharer_heat_capacity = modeled_location.zone.air.heat_capacity() - else - delta_temperature = (air.temperature - modeled_location.air.temperature) - sharer_heat_capacity = modeled_location.air.heat_capacity() - - var/self_temperature_delta = 0 - var/sharer_temperature_delta = 0 - - if((sharer_heat_capacity>0) && (partial_heat_capacity>0)) - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) - - self_temperature_delta = -heat/total_heat_capacity - sharer_temperature_delta = heat/sharer_heat_capacity - else - return 1 - - air.temperature += self_temperature_delta - - if(modeled_location.zone) - modeled_location.zone.air.temperature += sharer_temperature_delta/modeled_location.zone.air.group_multiplier - else - modeled_location.air.temperature += sharer_temperature_delta - - - else - if((target.heat_capacity>0) && (partial_heat_capacity>0)) - var/delta_temperature = air.temperature - target.temperature - - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) - - air.temperature -= heat/total_heat_capacity - if(network) - network.update = 1 - -//surface must be the surface area in m^2 -/datum/pipeline/proc/radiate_heat_to_space(surface, thermal_conductivity) - var/gas_density = air.total_moles/air.volume - thermal_conductivity *= min(gas_density / ( RADIATOR_OPTIMUM_PRESSURE/(R_IDEAL_GAS_EQUATION*GAS_CRITICAL_TEMPERATURE) ), 1) //mult by density ratio - - // We only get heat from the star on the exposed surface area. - // If the HE pipes gain more energy from AVERAGE_SOLAR_RADIATION than they can radiate, then they have a net heat increase. - var/heat_gain = AVERAGE_SOLAR_RADIATION * (RADIATOR_EXPOSED_SURFACE_AREA_RATIO * surface) * thermal_conductivity - - // Previously, the temperature would enter equilibrium at 26C or 294K. - // Only would happen if both sides (all 2 square meters of surface area) were exposed to sunlight. We now assume it aligned edge on. - // It currently should stabilise at 129.6K or -143.6C - heat_gain -= surface * STEFAN_BOLTZMANN_CONSTANT * thermal_conductivity * (air.temperature - COSMIC_RADIATION_TEMPERATURE) ** 4 - - air.add_thermal_energy(heat_gain) - if(network) - network.update = 1 + +/datum/pipeline + var/datum/gas_mixture/air + + var/list/obj/machinery/atmospherics/pipe/members + var/list/obj/machinery/atmospherics/pipe/edges //Used for building networks + + // Nodes that are leaking. Used for A.S. Valves. + var/list/leaks = list() + + var/datum/pipe_network/network + + var/alert_pressure = 0 + +/datum/pipeline/Destroy() + QDEL_NULL(network) + + if(air && air.volume) + temporarily_store_air() + for(var/obj/machinery/atmospherics/pipe/P in members) + P.parent = null + members = null + edges = null + leaks = null + . = ..() + +/datum/pipeline/process()//This use to be called called from the pipe networks + + //Check to see if pressure is within acceptable limits + var/pressure = air.return_pressure() + if(pressure > alert_pressure) + for(var/obj/machinery/atmospherics/pipe/member in members) + if(!member.check_pressure(pressure)) + break //Only delete 1 pipe per process + +/datum/pipeline/proc/temporarily_store_air() + //Update individual gas_mixtures by volume ratio + + for(var/obj/machinery/atmospherics/pipe/member in members) + member.air_temporary = new + member.air_temporary.copy_from(air) + member.air_temporary.volume = member.volume + member.air_temporary.multiply(member.volume / air.volume) + +/datum/pipeline/proc/build_pipeline(obj/machinery/atmospherics/pipe/base) + air = new + + var/list/possible_expansions = list(base) + members = list(base) + edges = list() + + var/volume = base.volume + base.parent = src + alert_pressure = base.alert_pressure + + if(base.air_temporary) + air = base.air_temporary + base.air_temporary = null + else + air = new + + if(base.leaking) + leaks |= base + + while(possible_expansions.len>0) + for(var/obj/machinery/atmospherics/pipe/borderline in possible_expansions) + + var/list/result = borderline.pipeline_expansion() + var/edge_check = result.len + + if(result.len>0) + for(var/obj/machinery/atmospherics/pipe/item in result) + + if(item.in_stasis) + continue + + if(!members.Find(item)) + members += item + possible_expansions += item + + volume += item.volume + item.parent = src + + alert_pressure = min(alert_pressure, item.alert_pressure) + + if(item.air_temporary) + air.merge(item.air_temporary) + + if(item.leaking) + leaks |= item + + edge_check-- + + if(edge_check>0) + edges += borderline + + possible_expansions -= borderline + + air.volume = volume + +/datum/pipeline/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + + if(new_network.line_members.Find(src)) + return 0 + + new_network.line_members += src + + network = new_network + network.leaks |= leaks + + for(var/obj/machinery/atmospherics/pipe/edge in edges) + for(var/obj/machinery/atmospherics/result in edge.pipeline_expansion()) + if(!istype(result,/obj/machinery/atmospherics/pipe) && (result!=reference)) + result.network_expand(new_network, edge) + + return 1 + +/datum/pipeline/proc/return_network(obj/machinery/atmospherics/reference) + if(!network) + network = new /datum/pipe_network() + network.build_network(src, null) + //technically passing these parameters should not be allowed + //however pipe_network.build_network(..) and pipeline.network_extend(...) + // were setup to properly handle this case + + return network + +/datum/pipeline/proc/mingle_with_turf(turf/simulated/target, mingle_volume) + var/datum/gas_mixture/air_sample = air.remove_ratio(mingle_volume/air.volume) + air_sample.volume = mingle_volume + + if(istype(target) && target.zone) + //Have to consider preservation of group statuses + var/datum/gas_mixture/turf_copy = new + var/datum/gas_mixture/turf_original = new + + turf_copy.copy_from(target.zone.air) + turf_copy.volume = target.zone.air.volume //Copy a good representation of the turf from parent group + turf_original.copy_from(turf_copy) + + equalize_gases(list(air_sample, turf_copy)) + air.merge(air_sample) + + + target.zone.air.remove(turf_original.total_moles) + target.zone.air.merge(turf_copy) + + else + var/datum/gas_mixture/turf_air = target.return_air() + + equalize_gases(list(air_sample, turf_air)) + air.merge(air_sample) + //turf_air already modified by equalize_gases() + + if(network) + network.update = 1 + +/datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity) + var/total_heat_capacity = air.heat_capacity() + var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) + + if(istype(target, /turf/simulated)) + var/turf/simulated/modeled_location = target + + if (modeled_location.special_temperature) + air.temperature += thermal_conductivity * (modeled_location.special_temperature - air.temperature) + if (air.temperature < TCMB) + air.temperature = TCMB + if (network) + network.update = TRUE + + if(modeled_location.blocks_air) + + if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0)) + var/delta_temperature = air.temperature - modeled_location.temperature + + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity)) + + air.temperature -= heat/total_heat_capacity + modeled_location.temperature += heat/modeled_location.heat_capacity + + else + var/delta_temperature = 0 + var/sharer_heat_capacity = 0 + + if(modeled_location.zone) + delta_temperature = (air.temperature - modeled_location.zone.air.temperature) + sharer_heat_capacity = modeled_location.zone.air.heat_capacity() + else + delta_temperature = (air.temperature - modeled_location.air.temperature) + sharer_heat_capacity = modeled_location.air.heat_capacity() + + var/self_temperature_delta = 0 + var/sharer_temperature_delta = 0 + + if((sharer_heat_capacity>0) && (partial_heat_capacity>0)) + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) + + self_temperature_delta = -heat/total_heat_capacity + sharer_temperature_delta = heat/sharer_heat_capacity + else + return 1 + + air.temperature += self_temperature_delta + + if(modeled_location.zone) + modeled_location.zone.air.temperature += sharer_temperature_delta/modeled_location.zone.air.group_multiplier + else + modeled_location.air.temperature += sharer_temperature_delta + + + else + if((target.heat_capacity>0) && (partial_heat_capacity>0)) + var/delta_temperature = air.temperature - target.temperature + + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) + + air.temperature -= heat/total_heat_capacity + if(network) + network.update = 1 + +//surface must be the surface area in m^2 +/datum/pipeline/proc/radiate_heat_to_space(surface, thermal_conductivity) + var/gas_density = air.total_moles/air.volume + thermal_conductivity *= min(gas_density / ( RADIATOR_OPTIMUM_PRESSURE/(R_IDEAL_GAS_EQUATION*GAS_CRITICAL_TEMPERATURE) ), 1) //mult by density ratio + + // We only get heat from the star on the exposed surface area. + // If the HE pipes gain more energy from AVERAGE_SOLAR_RADIATION than they can radiate, then they have a net heat increase. + var/heat_gain = AVERAGE_SOLAR_RADIATION * (RADIATOR_EXPOSED_SURFACE_AREA_RATIO * surface) * thermal_conductivity + + // Previously, the temperature would enter equilibrium at 26C or 294K. + // Only would happen if both sides (all 2 square meters of surface area) were exposed to sunlight. We now assume it aligned edge on. + // It currently should stabilise at 129.6K or -143.6C + heat_gain -= surface * STEFAN_BOLTZMANN_CONSTANT * thermal_conductivity * (air.temperature - COSMIC_RADIATION_TEMPERATURE) ** 4 + + air.add_thermal_energy(heat_gain) + if(network) + network.update = 1 diff --git a/code/ATMOSPHERICS/pipes/tank_vr.dm b/code/ATMOSPHERICS/pipes/tank_vr.dm index 32341ad8f3b..b95c87ebd5b 100644 --- a/code/ATMOSPHERICS/pipes/tank_vr.dm +++ b/code/ATMOSPHERICS/pipes/tank_vr.dm @@ -1,5 +1,5 @@ -/obj/machinery/atmospherics/pipe/tank/phoron/full - start_pressure = 15000 - -/obj/machinery/atmospherics/pipe/tank/air/full +/obj/machinery/atmospherics/pipe/tank/phoron/full + start_pressure = 15000 + +/obj/machinery/atmospherics/pipe/tank/air/full start_pressure = 15000 \ No newline at end of file diff --git a/code/__byond_version_compat.dm b/code/__byond_version_compat.dm index 982fda8fa16..086360e5e2e 100644 --- a/code/__byond_version_compat.dm +++ b/code/__byond_version_compat.dm @@ -1,74 +1,74 @@ -#if DM_VERSION >= 515 -#error PLEASE MAKE SURE THAT 515 IS PROPERLY TESTED AND WORKS. ESPECIALLY THE SAVE-FILES HAVE TO WORK. -#error Additionally: Make sure that the GitHub Workflow was updated to BYOND 515 as well. -#endif - -// These defines are from __513_compatibility.dm -- Please Sort -#define CLAMP(CLVALUE, CLMIN, CLMAX) clamp(CLVALUE, CLMIN, CLMAX) -#define TAN(x) tan(x) -#define ATAN2(x, y) arctan(x, y) -#define between(x, y, z) clamp(y, x, z) - -// This file contains defines allowing targeting byond versions newer than the supported - -//Update this whenever you need to take advantage of more recent byond features -#define MIN_COMPILER_VERSION 514 -#define MIN_COMPILER_BUILD 1556 -#if (DM_VERSION < MIN_COMPILER_VERSION || DM_BUILD < MIN_COMPILER_BUILD) && !defined(SPACEMAN_DMM) -//Don't forget to update this part -#error Your version of BYOND is too out-of-date to compile this project. Go to https://secure.byond.com/download and update. -#error You need version 514.1556 or higher -#endif - -#if (DM_VERSION == 514 && DM_BUILD > 1575 && DM_BUILD <= 1577) -#error Your version of BYOND currently has a crashing issue that will prevent you from running Dream Daemon test servers. -#error We require developers to test their content, so an inability to test means we cannot allow the compile. -#error Please consider downgrading to 514.1575 or lower. -#endif - -// Keep savefile compatibilty at minimum supported level -#if DM_VERSION >= 515 -/savefile/byond_version = MIN_COMPILER_VERSION -#endif - -// 515 split call for external libraries into call_ext -#if DM_VERSION < 515 -#define LIBCALL call -#else -#define LIBCALL call_ext -#endif - -// So we want to have compile time guarantees these methods exist on local type, unfortunately 515 killed the .proc/procname and .verb/verbname syntax so we have to use nameof() -// For the record: GLOBAL_VERB_REF would be useless as verbs can't be global. - -#if DM_VERSION < 515 - -/// Call by name proc references, checks if the proc exists on either this type or as a global proc. -#define PROC_REF(X) (.proc/##X) -/// Call by name verb references, checks if the verb exists on either this type or as a global verb. -#define VERB_REF(X) (.verb/##X) - -/// Call by name proc reference, checks if the proc exists on either the given type or as a global proc -#define TYPE_PROC_REF(TYPE, X) (##TYPE.proc/##X) -/// Call by name verb reference, checks if the verb exists on either the given type or as a global verb -#define TYPE_VERB_REF(TYPE, X) (##TYPE.verb/##X) - -/// Call by name proc reference, checks if the proc is an existing global proc -#define GLOBAL_PROC_REF(X) (/proc/##X) - -#else - -/// Call by name proc references, checks if the proc exists on either this type or as a global proc. -#define PROC_REF(X) (nameof(.proc/##X)) -/// Call by name verb references, checks if the verb exists on either this type or as a global verb. -#define VERB_REF(X) (nameof(.verb/##X)) - -/// Call by name proc reference, checks if the proc exists on either the given type or as a global proc -#define TYPE_PROC_REF(TYPE, X) (nameof(##TYPE.proc/##X)) -/// Call by name verb reference, checks if the verb exists on either the given type or as a global verb -#define TYPE_VERB_REF(TYPE, X) (nameof(##TYPE.verb/##X)) - -/// Call by name proc reference, checks if the proc is an existing global proc -#define GLOBAL_PROC_REF(X) (/proc/##X) - -#endif +#if DM_VERSION >= 515 +#error PLEASE MAKE SURE THAT 515 IS PROPERLY TESTED AND WORKS. ESPECIALLY THE SAVE-FILES HAVE TO WORK. +#error Additionally: Make sure that the GitHub Workflow was updated to BYOND 515 as well. +#endif + +// These defines are from __513_compatibility.dm -- Please Sort +#define CLAMP(CLVALUE, CLMIN, CLMAX) clamp(CLVALUE, CLMIN, CLMAX) +#define TAN(x) tan(x) +#define ATAN2(x, y) arctan(x, y) +#define between(x, y, z) clamp(y, x, z) + +// This file contains defines allowing targeting byond versions newer than the supported + +//Update this whenever you need to take advantage of more recent byond features +#define MIN_COMPILER_VERSION 514 +#define MIN_COMPILER_BUILD 1556 +#if (DM_VERSION < MIN_COMPILER_VERSION || DM_BUILD < MIN_COMPILER_BUILD) && !defined(SPACEMAN_DMM) +//Don't forget to update this part +#error Your version of BYOND is too out-of-date to compile this project. Go to https://secure.byond.com/download and update. +#error You need version 514.1556 or higher +#endif + +#if (DM_VERSION == 514 && DM_BUILD > 1575 && DM_BUILD <= 1577) +#error Your version of BYOND currently has a crashing issue that will prevent you from running Dream Daemon test servers. +#error We require developers to test their content, so an inability to test means we cannot allow the compile. +#error Please consider downgrading to 514.1575 or lower. +#endif + +// Keep savefile compatibilty at minimum supported level +#if DM_VERSION >= 515 +/savefile/byond_version = MIN_COMPILER_VERSION +#endif + +// 515 split call for external libraries into call_ext +#if DM_VERSION < 515 +#define LIBCALL call +#else +#define LIBCALL call_ext +#endif + +// So we want to have compile time guarantees these methods exist on local type, unfortunately 515 killed the .proc/procname and .verb/verbname syntax so we have to use nameof() +// For the record: GLOBAL_VERB_REF would be useless as verbs can't be global. + +#if DM_VERSION < 515 + +/// Call by name proc references, checks if the proc exists on either this type or as a global proc. +#define PROC_REF(X) (.proc/##X) +/// Call by name verb references, checks if the verb exists on either this type or as a global verb. +#define VERB_REF(X) (.verb/##X) + +/// Call by name proc reference, checks if the proc exists on either the given type or as a global proc +#define TYPE_PROC_REF(TYPE, X) (##TYPE.proc/##X) +/// Call by name verb reference, checks if the verb exists on either the given type or as a global verb +#define TYPE_VERB_REF(TYPE, X) (##TYPE.verb/##X) + +/// Call by name proc reference, checks if the proc is an existing global proc +#define GLOBAL_PROC_REF(X) (/proc/##X) + +#else + +/// Call by name proc references, checks if the proc exists on either this type or as a global proc. +#define PROC_REF(X) (nameof(.proc/##X)) +/// Call by name verb references, checks if the verb exists on either this type or as a global verb. +#define VERB_REF(X) (nameof(.verb/##X)) + +/// Call by name proc reference, checks if the proc exists on either the given type or as a global proc +#define TYPE_PROC_REF(TYPE, X) (nameof(##TYPE.proc/##X)) +/// Call by name verb reference, checks if the verb exists on either the given type or as a global verb +#define TYPE_VERB_REF(TYPE, X) (nameof(##TYPE.verb/##X)) + +/// Call by name proc reference, checks if the proc is an existing global proc +#define GLOBAL_PROC_REF(X) (/proc/##X) + +#endif diff --git a/code/__defines/_compile_options.dm b/code/__defines/_compile_options.dm index 83baac198f9..058115f4fd1 100644 --- a/code/__defines/_compile_options.dm +++ b/code/__defines/_compile_options.dm @@ -1,43 +1,43 @@ -#define BACKGROUND_ENABLED 0 // The default value for all uses of set background. Set background can cause gradual lag and is recommended you only turn this on if necessary. - // 1 will enable set background. 0 will disable set background. - -#define PRELOAD_RSC 1 /*set to: - 0 to allow using external resources or on-demand behaviour; - 1 to use the default behaviour (preload compiled in recourses, not player uploaded ones); - 2 for preloading absolutely everything; - */ - -// ZAS Compile Options -//#define FIREDBG // Uncomment to turn on ZAS debugging related to fire stuff. -//#define ZASDBG // Uncomment to turn on super detailed ZAS debugging that probably won't even compile. -#define MULTIZAS // Uncomment to turn on Multi-Z ZAS Support! - -// Movement Compile Options -//#define CARDINAL_INPUT_ONLY // Uncomment to disable diagonal player movement (restore previous cardinal-moves-only behavior) - -// Comment/Uncomment this to turn off/on shuttle code debugging logs -#define DEBUG_SHUTTLES - -// If we are doing the map test build, do not include the main maps, only the submaps. -#if MAP_TEST - #define USING_MAP_DATUM /datum/map - #define MAP_OVERRIDE 1 -#endif - -///Used to find the sources of harddels, quite laggy, don't be surpised if it freezes your client for a good while -//#define REFERENCE_TRACKING -#ifdef REFERENCE_TRACKING - -///Should we be logging our findings or not -#define REFERENCE_TRACKING_LOG - -///Used for doing dry runs of the reference finder, to test for feature completeness -//#define REFERENCE_TRACKING_DEBUG - -///Run a lookup on things hard deleting by default. -//#define GC_FAILURE_HARD_LOOKUP -#ifdef GC_FAILURE_HARD_LOOKUP -#define FIND_REF_NO_CHECK_TICK -#endif //ifdef GC_FAILURE_HARD_LOOKUP - -#endif //ifdef REFERENCE_TRACKING +#define BACKGROUND_ENABLED 0 // The default value for all uses of set background. Set background can cause gradual lag and is recommended you only turn this on if necessary. + // 1 will enable set background. 0 will disable set background. + +#define PRELOAD_RSC 1 /*set to: + 0 to allow using external resources or on-demand behaviour; + 1 to use the default behaviour (preload compiled in recourses, not player uploaded ones); + 2 for preloading absolutely everything; + */ + +// ZAS Compile Options +//#define FIREDBG // Uncomment to turn on ZAS debugging related to fire stuff. +//#define ZASDBG // Uncomment to turn on super detailed ZAS debugging that probably won't even compile. +#define MULTIZAS // Uncomment to turn on Multi-Z ZAS Support! + +// Movement Compile Options +//#define CARDINAL_INPUT_ONLY // Uncomment to disable diagonal player movement (restore previous cardinal-moves-only behavior) + +// Comment/Uncomment this to turn off/on shuttle code debugging logs +#define DEBUG_SHUTTLES + +// If we are doing the map test build, do not include the main maps, only the submaps. +#if MAP_TEST + #define USING_MAP_DATUM /datum/map + #define MAP_OVERRIDE 1 +#endif + +///Used to find the sources of harddels, quite laggy, don't be surpised if it freezes your client for a good while +//#define REFERENCE_TRACKING +#ifdef REFERENCE_TRACKING + +///Should we be logging our findings or not +#define REFERENCE_TRACKING_LOG + +///Used for doing dry runs of the reference finder, to test for feature completeness +//#define REFERENCE_TRACKING_DEBUG + +///Run a lookup on things hard deleting by default. +//#define GC_FAILURE_HARD_LOOKUP +#ifdef GC_FAILURE_HARD_LOOKUP +#define FIND_REF_NO_CHECK_TICK +#endif //ifdef GC_FAILURE_HARD_LOOKUP + +#endif //ifdef REFERENCE_TRACKING diff --git a/code/__defines/_protect.dm b/code/__defines/_protect.dm index b10a6264bdb..b503155faeb 100644 --- a/code/__defines/_protect.dm +++ b/code/__defines/_protect.dm @@ -1,11 +1,11 @@ -///Protects a datum from being VV'd -#define GENERAL_PROTECT_DATUM(Path)\ -##Path/can_vv_get(var_name){\ - return FALSE;\ -}\ -##Path/vv_edit_var(var_name, var_value){\ - return FALSE;\ -}\ -##Path/CanProcCall(procname){\ - return FALSE;\ +///Protects a datum from being VV'd +#define GENERAL_PROTECT_DATUM(Path)\ +##Path/can_vv_get(var_name){\ + return FALSE;\ +}\ +##Path/vv_edit_var(var_name, var_value){\ + return FALSE;\ +}\ +##Path/CanProcCall(procname){\ + return FALSE;\ } \ No newline at end of file diff --git a/code/__defines/color.dm b/code/__defines/color.dm index 8f8332d768f..baae616b340 100644 --- a/code/__defines/color.dm +++ b/code/__defines/color.dm @@ -1,207 +1,207 @@ -// BYOND lower-cases color values, and thus we do so as well to ensure atom.color == COLOR_X will work correctly -#define COLOR_BLACK "#000000" -#define COLOR_NAVY "#000080" -#define COLOR_NAVY_BLUE "#000080" -#define COLOR_GREEN "#008000" -#define COLOR_DARK_GRAY "#404040" -#define COLOR_MAROON "#800000" -#define COLOR_PURPLE "#800080" -#define COLOR_VIOLET "#9933ff" -#define COLOR_OLIVE "#52613b" -#define COLOR_BROWN_ORANGE "#824b28" -#define COLOR_DARK_ORANGE "#b95a00" -#define COLOR_GRAY40 "#666666" -#define COLOR_GRAY20 "#333333" -#define COLOR_GRAY15 "#151515" -#define COLOR_SEDONA "#cc6600" -#define COLOR_DARK_BROWN "#917448" -#define COLOR_BLUE "#0000ff" -#define COLOR_DEEP_SKY_BLUE "#00e1ff" -#define COLOR_LIME "#00ff00" -#define COLOR_CYAN "#00ffff" -#define COLOR_TEAL "#33cccc" -#define COLOR_RED "#ff0000" -#define COLOR_PINK "#ff00ff" -#define COLOR_PALE_PINK "#bf89ba" -#define COLOR_ORANGE "#ff9900" -#define COLOR_YELLOW "#ffff00" -#define COLOR_YELLOW_GRAY "#c9a344" -#define COLOR_PALE_YELLOW "#c1bb7a" -#define COLOR_WARM_YELLOW "#b3863c" -#define COLOR_GRAY "#808080" -#define COLOR_RED_GRAY "#aa5f61" -#define COLOR_BROWN "#b19664" -#define COLOR_GREEN_GRAY "#8daf6a" -#define COLOR_DARK_GREEN_GRAY "#54654c" -#define COLOR_BLUE_GRAY "#6a97b0" -#define COLOR_DARK_BLUE_GRAY "#3e4855" -#define COLOR_SURGERY_BLUE "#e0f2f6" -#define COLOR_SUN "#ec8b2f" -#define COLOR_PURPLE_GRAY "#a2819e" -#define COLOR_BLUE_LIGHT "#33ccff" -#define COLOR_RED_LIGHT "#ff3333" -#define COLOR_BEIGE "#ceb689" -#define COLOR_BABY_BLUE "#89cff0" -#define COLOR_PALE_GREEN_GRAY "#aed18b" -#define COLOR_PALE_RED_GRAY "#cc9090" -#define COLOR_PALE_PURPLE_GRAY "#bda2ba" -#define COLOR_PALE_BLUE_GRAY "#8bbbd5" -#define COLOR_LUMINOL "#66ffff" -#define COLOR_SILVER "#c0c0c0" -#define COLOR_GRAY80 "#cccccc" -#define COLOR_OFF_WHITE "#eeeeee" -#define COLOR_WHITE "#ffffff" -#define COLOR_GOLD "#ffcc33" -#define COLOR_CLOSET_GOLD "#6d6133" -#define COLOR_NT_RED "#9d2300" -#define COLOR_BOTTLE_GREEN "#1f6b4f" -#define COLOR_PALE_BTL_GREEN "#57967f" -#define COLOR_GUNMETAL "#545c68" -#define COLOR_WALL_GUNMETAL "#353a42" -#define COLOR_STEEL "#a8b0b2" -#define COLOR_MUZZLE_FLASH "#ffffb2" -#define COLOR_CHESTNUT "#996633" -#define COLOR_BEASTY_BROWN "#663300" -#define COLOR_WHEAT "#ffff99" -#define COLOR_CYAN_BLUE "#3366cc" -#define COLOR_LIGHT_CYAN "#66ccff" -#define COLOR_PAKISTAN_GREEN "#006600" -#define COLOR_HULL "#436b8e" -#define COLOR_AMBER "#ffbf00" -#define COLOR_COMMAND_BLUE "#46698c" -#define COLOR_SKY_BLUE "#5ca1cc" -#define COLOR_PALE_ORANGE "#b88a3b" -#define COLOR_CIVIE_GREEN "#b7f27d" -#define COLOR_TITANIUM "#d1e6e3" -#define COLOR_DARK_GUNMETAL "#4c535b" -#define COLOR_BRONZE "#8c7853" -#define COLOR_BRASS "#b99d71" -#define COLOR_INDIGO "#4b0082" -#define COLOR_ALUMINIUM "#bbbbbb" -#define COLOR_CRYSTAL "#00c8a5" -#define COLOR_ASTEROID_ROCK "#735555" -#define COLOR_NULLGLASS "#ff6088" -#define COLOR_DIAMOND "#d8d4ea" -#define COLOR_LIGHT_PINK "#ffbaf9" -#define COLOR_LIGHT_RED "#ff4f4f" -#define COLOR_PALE_MAROON "#6e2121" -#define COLOR_LIGHT_GREEN "#00cf00" -#define COLOR_SALAD_GREEN "#67e067" -#define COLOR_DARK_GOLD "#ab9029" -#define COLOR_DARK_TEAL "#2db5b5" -#define COLOR_LIGHT_VIOLET "#e7bfff" -#define COLOR_SAN_MARINO_BLUE "#4b75ab" -#define COLOR_HALF_TRANSPARENT_BLACK "#0000007A" - -#define PIPE_COLOR_GREY "#808080" -#define PIPE_COLOR_RED "#ff0000" -#define PIPE_COLOR_BLUE "#0000ff" -#define PIPE_COLOR_CYAN "#00ffff" -#define PIPE_COLOR_GREEN "#00ff00" -#define PIPE_COLOR_YELLOW "#ffcc00" -#define PIPE_COLOR_BLACK "#444444" -#define PIPE_COLOR_ORANGE "#b95a00" -#define PIPE_COLOR_WHITE "#ffffff" -#define PIPE_COLOR_PURPLE "#5c1ec0" - -#define COMMS_COLOR_DEFAULT "#ff00ff" -#define COMMS_COLOR_ENTERTAIN "#666666" -#define COMMS_COLOR_AI "#ff00ff" -#define COMMS_COLOR_COMMON "#408010" -#define COMMS_COLOR_SERVICE "#709b00" -#define COMMS_COLOR_SUPPLY "#7f6539" -#define COMMS_COLOR_SCIENCE "#993399" -#define COMMS_COLOR_MEDICAL "#009190" -#define COMMS_COLOR_MEDICAL_I "#509190" -#define COMMS_COLOR_EXPLORER "#929820" -#define COMMS_COLOR_ENGINEER "#a66300" -#define COMMS_COLOR_SECURITY "#930000" -#define COMMS_COLOR_SECURITY_I "#935050" -#define COMMS_COLOR_COMMAND "#204090" -#define COMMS_COLOR_CENTCOMM "#5c5c7c" -#define COMMS_COLOR_SYNDICATE "#6d3f40" -#define COMMS_COLOR_SKRELL "#7331c4" - -#define WOOD_COLOR_GENERIC "#d5a66e" -#define WOOD_COLOR_RICH "#792f27" -#define WOOD_COLOR_PALE "#d2bc9d" -#define WOOD_COLOR_PALE2 "#e6d2ba" -#define WOOD_COLOR_BLACK "#332521" -#define WOOD_COLOR_CHOCOLATE "#543c30" -#define WOOD_COLOR_YELLOW "#e3994e" - -#define GLASS_COLOR "#74c1ee" -#define GLASS_COLOR_PHORON "#7c3a9a" -#define GLASS_COLOR_TINTED "#222222" -#define GLASS_COLOR_FROSTED "#ffffff" - -#define COLOR_BLOOD_HUMAN "#a10808" - -//Colors defines used by e-sword lighting -#define COLOR_SABER_BLUE "#40ceff" -#define COLOR_SABER_RED "#ff0000" //In case someone wants to tweak COLOR_RED, since COLOR_GREEN is not #00ff00 -#define COLOR_SABER_GREEN "#00ff00" -#define COLOR_SABER_PURPLE "#6800f4" -#define COLOR_SABER_SKRELL "#6600cc" -#define COLOR_SABER_AXE "#00ccff" -#define COLOR_SABER_CUTLASS "#ff0033" - -//Color defines used by the assembly detailer. -#define COLOR_ASSEMBLY_BLACK "#545454" -#define COLOR_ASSEMBLY_BGRAY "#9497AB" -#define COLOR_ASSEMBLY_WHITE "#E2E2E2" -#define COLOR_ASSEMBLY_RED "#CC4242" -#define COLOR_ASSEMBLY_ORANGE "#E39751" -#define COLOR_ASSEMBLY_BEIGE "#AF9366" -#define COLOR_ASSEMBLY_BROWN "#97670E" -#define COLOR_ASSEMBLY_GOLD "#AA9100" -#define COLOR_ASSEMBLY_YELLOW "#CECA2B" -#define COLOR_ASSEMBLY_GURKHA "#999875" -#define COLOR_ASSEMBLY_LGREEN "#789876" -#define COLOR_ASSEMBLY_GREEN "#44843C" -#define COLOR_ASSEMBLY_LBLUE "#5D99BE" -#define COLOR_ASSEMBLY_BLUE "#38559E" -#define COLOR_ASSEMBLY_PURPLE "#6F6192" -#define COLOR_ASSEMBLY_HOT_PINK "#FF69B4" - -// Discord requires colors to be in decimal instead of hexadecimal. -#define COLOR_WEBHOOK_DEFAULT 0x8bbbd5 // "#8bbbd5" -#define COLOR_WEBHOOK_GOOD 0x2ECC71 // "#2ECC71" -#define COLOR_WEBHOOK_POOR 0xE67E22 // "#E67E22" -#define COLOR_WEBHOOK_BAD 0xE74C3C // "#E74C3C" - -//Some defines to generalise colours used in lighting. -//Important note on colors. Colors can end up significantly different from the basic html picture, especially when saturated -#define LIGHT_COLOR_RED "#FA8282" //Warm but extremely diluted red. rgb(250, 130, 130) -#define LIGHT_COLOR_GREEN "#64C864" //Bright but quickly dissipating neon green. rgb(100, 200, 100) -#define LIGHT_COLOR_BLUE "#6496FA" //Cold, diluted blue. rgb(100, 150, 250) - -#define LIGHT_COLOR_BLUEGREEN "#7DE1AF" //Light blueish green. rgb(125, 225, 175) -#define LIGHT_COLOR_CYAN "#7DE1E1" //Diluted cyan. rgb(125, 225, 225) -#define LIGHT_COLOR_LIGHT_CYAN "#40CEFF" //More-saturated cyan. rgb(64, 206, 255) -#define LIGHT_COLOR_DARK_BLUE "#6496FA" //Saturated blue. rgb(51, 117, 248) -#define LIGHT_COLOR_PINK "#E17DE1" //Diluted, mid-warmth pink. rgb(225, 125, 225) -#define LIGHT_COLOR_YELLOW "#E1E17D" //Dimmed yellow, leaning kaki. rgb(225, 225, 125) -#define LIGHT_COLOR_BROWN "#966432" //Clear brown, mostly dim. rgb(150, 100, 50) -#define LIGHT_COLOR_ORANGE "#FA9632" //Mostly pure orange. rgb(250, 150, 50) -#define LIGHT_COLOR_PURPLE "#952CF4" //Light Purple. rgb(149, 44, 244) -#define LIGHT_COLOR_LAVENDER "#9B51FF" //Less-saturated light purple. rgb(155, 81, 255) - -//These ones aren't a direct colour like the ones above, because nothing would fit -#define LIGHT_COLOR_FIRE "#FAA019" //Warm orange color, leaning strongly towards yellow. rgb(250, 160, 25) -#define LIGHT_COLOR_LAVA "#C48A18" //Very warm yellow, leaning slightly towards orange. rgb(196, 138, 24) -#define LIGHT_COLOR_FLARE "#FA644B" //Bright, non-saturated red. Leaning slightly towards pink for visibility. rgb(250, 100, 75) -#define LIGHT_COLOR_SLIME_LAMP "#AFC84B" //Weird color, between yellow and green, very slimy. rgb(175, 200, 75) -#define LIGHT_COLOR_TUNGSTEN "#FAE1AF" //Extremely diluted yellow, close to skin color (for some reason). rgb(250, 225, 175) -#define LIGHT_COLOR_HALOGEN "#F0FAFA" //Barely visible cyan-ish hue, as the doctor prescribed. rgb(240, 250, 250) - -//Lighting values used by the station lights -#define LIGHT_COLOR_FLUORESCENT_TUBE "#E0EFFF" -#define LIGHT_COLOR_FLUORESCENT_FLASHLIGHT "#CDDDFF" -#define LIGHT_COLOR_INCANDESCENT_TUBE "#fffed9" -#define LIGHT_COLOR_INCANDESCENT_BULB "#ffe7ce" -#define LIGHT_COLOR_INCANDESCENT_FLASHLIGHT "#FFCC66" -#define LIGHT_COLOR_NIGHTSHIFT "#EFCC86" - -//Fake ambient occlusion filter -#define AMBIENT_OCCLUSION filter(type="drop_shadow", x=0, y=-2, size=4, offset=3, color="#04080F80") +// BYOND lower-cases color values, and thus we do so as well to ensure atom.color == COLOR_X will work correctly +#define COLOR_BLACK "#000000" +#define COLOR_NAVY "#000080" +#define COLOR_NAVY_BLUE "#000080" +#define COLOR_GREEN "#008000" +#define COLOR_DARK_GRAY "#404040" +#define COLOR_MAROON "#800000" +#define COLOR_PURPLE "#800080" +#define COLOR_VIOLET "#9933ff" +#define COLOR_OLIVE "#52613b" +#define COLOR_BROWN_ORANGE "#824b28" +#define COLOR_DARK_ORANGE "#b95a00" +#define COLOR_GRAY40 "#666666" +#define COLOR_GRAY20 "#333333" +#define COLOR_GRAY15 "#151515" +#define COLOR_SEDONA "#cc6600" +#define COLOR_DARK_BROWN "#917448" +#define COLOR_BLUE "#0000ff" +#define COLOR_DEEP_SKY_BLUE "#00e1ff" +#define COLOR_LIME "#00ff00" +#define COLOR_CYAN "#00ffff" +#define COLOR_TEAL "#33cccc" +#define COLOR_RED "#ff0000" +#define COLOR_PINK "#ff00ff" +#define COLOR_PALE_PINK "#bf89ba" +#define COLOR_ORANGE "#ff9900" +#define COLOR_YELLOW "#ffff00" +#define COLOR_YELLOW_GRAY "#c9a344" +#define COLOR_PALE_YELLOW "#c1bb7a" +#define COLOR_WARM_YELLOW "#b3863c" +#define COLOR_GRAY "#808080" +#define COLOR_RED_GRAY "#aa5f61" +#define COLOR_BROWN "#b19664" +#define COLOR_GREEN_GRAY "#8daf6a" +#define COLOR_DARK_GREEN_GRAY "#54654c" +#define COLOR_BLUE_GRAY "#6a97b0" +#define COLOR_DARK_BLUE_GRAY "#3e4855" +#define COLOR_SURGERY_BLUE "#e0f2f6" +#define COLOR_SUN "#ec8b2f" +#define COLOR_PURPLE_GRAY "#a2819e" +#define COLOR_BLUE_LIGHT "#33ccff" +#define COLOR_RED_LIGHT "#ff3333" +#define COLOR_BEIGE "#ceb689" +#define COLOR_BABY_BLUE "#89cff0" +#define COLOR_PALE_GREEN_GRAY "#aed18b" +#define COLOR_PALE_RED_GRAY "#cc9090" +#define COLOR_PALE_PURPLE_GRAY "#bda2ba" +#define COLOR_PALE_BLUE_GRAY "#8bbbd5" +#define COLOR_LUMINOL "#66ffff" +#define COLOR_SILVER "#c0c0c0" +#define COLOR_GRAY80 "#cccccc" +#define COLOR_OFF_WHITE "#eeeeee" +#define COLOR_WHITE "#ffffff" +#define COLOR_GOLD "#ffcc33" +#define COLOR_CLOSET_GOLD "#6d6133" +#define COLOR_NT_RED "#9d2300" +#define COLOR_BOTTLE_GREEN "#1f6b4f" +#define COLOR_PALE_BTL_GREEN "#57967f" +#define COLOR_GUNMETAL "#545c68" +#define COLOR_WALL_GUNMETAL "#353a42" +#define COLOR_STEEL "#a8b0b2" +#define COLOR_MUZZLE_FLASH "#ffffb2" +#define COLOR_CHESTNUT "#996633" +#define COLOR_BEASTY_BROWN "#663300" +#define COLOR_WHEAT "#ffff99" +#define COLOR_CYAN_BLUE "#3366cc" +#define COLOR_LIGHT_CYAN "#66ccff" +#define COLOR_PAKISTAN_GREEN "#006600" +#define COLOR_HULL "#436b8e" +#define COLOR_AMBER "#ffbf00" +#define COLOR_COMMAND_BLUE "#46698c" +#define COLOR_SKY_BLUE "#5ca1cc" +#define COLOR_PALE_ORANGE "#b88a3b" +#define COLOR_CIVIE_GREEN "#b7f27d" +#define COLOR_TITANIUM "#d1e6e3" +#define COLOR_DARK_GUNMETAL "#4c535b" +#define COLOR_BRONZE "#8c7853" +#define COLOR_BRASS "#b99d71" +#define COLOR_INDIGO "#4b0082" +#define COLOR_ALUMINIUM "#bbbbbb" +#define COLOR_CRYSTAL "#00c8a5" +#define COLOR_ASTEROID_ROCK "#735555" +#define COLOR_NULLGLASS "#ff6088" +#define COLOR_DIAMOND "#d8d4ea" +#define COLOR_LIGHT_PINK "#ffbaf9" +#define COLOR_LIGHT_RED "#ff4f4f" +#define COLOR_PALE_MAROON "#6e2121" +#define COLOR_LIGHT_GREEN "#00cf00" +#define COLOR_SALAD_GREEN "#67e067" +#define COLOR_DARK_GOLD "#ab9029" +#define COLOR_DARK_TEAL "#2db5b5" +#define COLOR_LIGHT_VIOLET "#e7bfff" +#define COLOR_SAN_MARINO_BLUE "#4b75ab" +#define COLOR_HALF_TRANSPARENT_BLACK "#0000007A" + +#define PIPE_COLOR_GREY "#808080" +#define PIPE_COLOR_RED "#ff0000" +#define PIPE_COLOR_BLUE "#0000ff" +#define PIPE_COLOR_CYAN "#00ffff" +#define PIPE_COLOR_GREEN "#00ff00" +#define PIPE_COLOR_YELLOW "#ffcc00" +#define PIPE_COLOR_BLACK "#444444" +#define PIPE_COLOR_ORANGE "#b95a00" +#define PIPE_COLOR_WHITE "#ffffff" +#define PIPE_COLOR_PURPLE "#5c1ec0" + +#define COMMS_COLOR_DEFAULT "#ff00ff" +#define COMMS_COLOR_ENTERTAIN "#666666" +#define COMMS_COLOR_AI "#ff00ff" +#define COMMS_COLOR_COMMON "#408010" +#define COMMS_COLOR_SERVICE "#709b00" +#define COMMS_COLOR_SUPPLY "#7f6539" +#define COMMS_COLOR_SCIENCE "#993399" +#define COMMS_COLOR_MEDICAL "#009190" +#define COMMS_COLOR_MEDICAL_I "#509190" +#define COMMS_COLOR_EXPLORER "#929820" +#define COMMS_COLOR_ENGINEER "#a66300" +#define COMMS_COLOR_SECURITY "#930000" +#define COMMS_COLOR_SECURITY_I "#935050" +#define COMMS_COLOR_COMMAND "#204090" +#define COMMS_COLOR_CENTCOMM "#5c5c7c" +#define COMMS_COLOR_SYNDICATE "#6d3f40" +#define COMMS_COLOR_SKRELL "#7331c4" + +#define WOOD_COLOR_GENERIC "#d5a66e" +#define WOOD_COLOR_RICH "#792f27" +#define WOOD_COLOR_PALE "#d2bc9d" +#define WOOD_COLOR_PALE2 "#e6d2ba" +#define WOOD_COLOR_BLACK "#332521" +#define WOOD_COLOR_CHOCOLATE "#543c30" +#define WOOD_COLOR_YELLOW "#e3994e" + +#define GLASS_COLOR "#74c1ee" +#define GLASS_COLOR_PHORON "#7c3a9a" +#define GLASS_COLOR_TINTED "#222222" +#define GLASS_COLOR_FROSTED "#ffffff" + +#define COLOR_BLOOD_HUMAN "#a10808" + +//Colors defines used by e-sword lighting +#define COLOR_SABER_BLUE "#40ceff" +#define COLOR_SABER_RED "#ff0000" //In case someone wants to tweak COLOR_RED, since COLOR_GREEN is not #00ff00 +#define COLOR_SABER_GREEN "#00ff00" +#define COLOR_SABER_PURPLE "#6800f4" +#define COLOR_SABER_SKRELL "#6600cc" +#define COLOR_SABER_AXE "#00ccff" +#define COLOR_SABER_CUTLASS "#ff0033" + +//Color defines used by the assembly detailer. +#define COLOR_ASSEMBLY_BLACK "#545454" +#define COLOR_ASSEMBLY_BGRAY "#9497AB" +#define COLOR_ASSEMBLY_WHITE "#E2E2E2" +#define COLOR_ASSEMBLY_RED "#CC4242" +#define COLOR_ASSEMBLY_ORANGE "#E39751" +#define COLOR_ASSEMBLY_BEIGE "#AF9366" +#define COLOR_ASSEMBLY_BROWN "#97670E" +#define COLOR_ASSEMBLY_GOLD "#AA9100" +#define COLOR_ASSEMBLY_YELLOW "#CECA2B" +#define COLOR_ASSEMBLY_GURKHA "#999875" +#define COLOR_ASSEMBLY_LGREEN "#789876" +#define COLOR_ASSEMBLY_GREEN "#44843C" +#define COLOR_ASSEMBLY_LBLUE "#5D99BE" +#define COLOR_ASSEMBLY_BLUE "#38559E" +#define COLOR_ASSEMBLY_PURPLE "#6F6192" +#define COLOR_ASSEMBLY_HOT_PINK "#FF69B4" + +// Discord requires colors to be in decimal instead of hexadecimal. +#define COLOR_WEBHOOK_DEFAULT 0x8bbbd5 // "#8bbbd5" +#define COLOR_WEBHOOK_GOOD 0x2ECC71 // "#2ECC71" +#define COLOR_WEBHOOK_POOR 0xE67E22 // "#E67E22" +#define COLOR_WEBHOOK_BAD 0xE74C3C // "#E74C3C" + +//Some defines to generalise colours used in lighting. +//Important note on colors. Colors can end up significantly different from the basic html picture, especially when saturated +#define LIGHT_COLOR_RED "#FA8282" //Warm but extremely diluted red. rgb(250, 130, 130) +#define LIGHT_COLOR_GREEN "#64C864" //Bright but quickly dissipating neon green. rgb(100, 200, 100) +#define LIGHT_COLOR_BLUE "#6496FA" //Cold, diluted blue. rgb(100, 150, 250) + +#define LIGHT_COLOR_BLUEGREEN "#7DE1AF" //Light blueish green. rgb(125, 225, 175) +#define LIGHT_COLOR_CYAN "#7DE1E1" //Diluted cyan. rgb(125, 225, 225) +#define LIGHT_COLOR_LIGHT_CYAN "#40CEFF" //More-saturated cyan. rgb(64, 206, 255) +#define LIGHT_COLOR_DARK_BLUE "#6496FA" //Saturated blue. rgb(51, 117, 248) +#define LIGHT_COLOR_PINK "#E17DE1" //Diluted, mid-warmth pink. rgb(225, 125, 225) +#define LIGHT_COLOR_YELLOW "#E1E17D" //Dimmed yellow, leaning kaki. rgb(225, 225, 125) +#define LIGHT_COLOR_BROWN "#966432" //Clear brown, mostly dim. rgb(150, 100, 50) +#define LIGHT_COLOR_ORANGE "#FA9632" //Mostly pure orange. rgb(250, 150, 50) +#define LIGHT_COLOR_PURPLE "#952CF4" //Light Purple. rgb(149, 44, 244) +#define LIGHT_COLOR_LAVENDER "#9B51FF" //Less-saturated light purple. rgb(155, 81, 255) + +//These ones aren't a direct colour like the ones above, because nothing would fit +#define LIGHT_COLOR_FIRE "#FAA019" //Warm orange color, leaning strongly towards yellow. rgb(250, 160, 25) +#define LIGHT_COLOR_LAVA "#C48A18" //Very warm yellow, leaning slightly towards orange. rgb(196, 138, 24) +#define LIGHT_COLOR_FLARE "#FA644B" //Bright, non-saturated red. Leaning slightly towards pink for visibility. rgb(250, 100, 75) +#define LIGHT_COLOR_SLIME_LAMP "#AFC84B" //Weird color, between yellow and green, very slimy. rgb(175, 200, 75) +#define LIGHT_COLOR_TUNGSTEN "#FAE1AF" //Extremely diluted yellow, close to skin color (for some reason). rgb(250, 225, 175) +#define LIGHT_COLOR_HALOGEN "#F0FAFA" //Barely visible cyan-ish hue, as the doctor prescribed. rgb(240, 250, 250) + +//Lighting values used by the station lights +#define LIGHT_COLOR_FLUORESCENT_TUBE "#E0EFFF" +#define LIGHT_COLOR_FLUORESCENT_FLASHLIGHT "#CDDDFF" +#define LIGHT_COLOR_INCANDESCENT_TUBE "#fffed9" +#define LIGHT_COLOR_INCANDESCENT_BULB "#ffe7ce" +#define LIGHT_COLOR_INCANDESCENT_FLASHLIGHT "#FFCC66" +#define LIGHT_COLOR_NIGHTSHIFT "#EFCC86" + +//Fake ambient occlusion filter +#define AMBIENT_OCCLUSION filter(type="drop_shadow", x=0, y=-2, size=4, offset=3, color="#04080F80") diff --git a/code/__defines/integrated_circuits.dm b/code/__defines/integrated_circuits.dm index 03cfa358e3b..d8b246f0cfb 100644 --- a/code/__defines/integrated_circuits.dm +++ b/code/__defines/integrated_circuits.dm @@ -1,4 +1,4 @@ -// Methods of obtaining a circuit. -#define IC_SPAWN_DEFAULT 1 // If the circuit comes in the default circuit box and able to be printed in the IC printer. -#define IC_SPAWN_RESEARCH 2 // If the circuit design will be available in the IC printer after upgrading it. +// Methods of obtaining a circuit. +#define IC_SPAWN_DEFAULT 1 // If the circuit comes in the default circuit box and able to be printed in the IC printer. +#define IC_SPAWN_RESEARCH 2 // If the circuit design will be available in the IC printer after upgrading it. #define IC_SPAWN_ILLEGAL 3 // If the circuit design will be available if upgrading the IC printer illegally. \ No newline at end of file diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm index bf854548760..9336ac5f789 100644 --- a/code/__defines/lighting.dm +++ b/code/__defines/lighting.dm @@ -1,119 +1,119 @@ -///Object doesn't use any of the light systems. Should be changed to add a light source to the object. -#define NO_LIGHT_SUPPORT 0 -///Light made with the lighting datums, applying a matrix. -#define STATIC_LIGHT 1 -///Light made by masking the lighting darkness plane. -#define MOVABLE_LIGHT 2 -///Light made by masking the lighting darkness plane, and is directional. -#define MOVABLE_LIGHT_DIRECTIONAL 3 - -///Is a movable light source attached to another movable (its loc), meaning that the lighting component should go one level deeper. -#define LIGHT_ATTACHED (1<<0) - -//Bay lighting engine shit, not in /code/modules/lighting because BYOND is being shit about it -/// frequency, in 1/10ths of a second, of the lighting process -#define LIGHTING_INTERVAL 5 - -#define MINIMUM_USEFUL_LIGHT_RANGE 1.4 - -/// type of falloff to use for lighting; 1 for circular, 2 for square -#define LIGHTING_FALLOFF 1 -/// use lambertian shading for light sources -#define LIGHTING_LAMBERTIAN 0 -/// height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone -#define LIGHTING_HEIGHT 1 -/// Value used to round lumcounts, values smaller than 1/129 don't matter (if they do, thanks sinking points), greater values will make lighting less precise, but in turn increase performance, VERY SLIGHTLY. -#define LIGHTING_ROUND_VALUE (1 / 64) - -/// icon used for lighting shading effects -#define LIGHTING_ICON 'icons/effects/lighting_object.dmi' - -/// If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting objects. -/// Set to zero to disable soft lighting. Luminosity changes then work if it's lit at all. -#define LIGHTING_SOFT_THRESHOLD 0 - -/// If I were you I'd leave this alone. -#define LIGHTING_BASE_MATRIX \ - list \ - ( \ - 1, 1, 1, 0, \ - 1, 1, 1, 0, \ - 1, 1, 1, 0, \ - 1, 1, 1, 0, \ - 0, 0, 0, 1 \ - ) \ - -///How many tiles standard fires glow. -#define LIGHT_RANGE_FIRE 3 - -#define LIGHTING_PLANE_ALPHA_VISIBLE 255 -#define LIGHTING_PLANE_ALPHA_NV_TRAIT 245 -#define LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE 192 -/// For lighting alpha, small amounts lead to big changes. even at 128 its hard to figure out what is dark and what is light, at 64 you almost can't even tell. -#define LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE 128 -#define LIGHTING_PLANE_ALPHA_INVISIBLE 0 - -//lighting area defines -/// dynamic lighting disabled (area stays at full brightness) -#define DYNAMIC_LIGHTING_DISABLED 0 -/// dynamic lighting enabled -#define DYNAMIC_LIGHTING_ENABLED 1 -/// dynamic lighting enabled even if the area doesn't require power -#define DYNAMIC_LIGHTING_FORCED 2 -/// dynamic lighting enabled only if starlight is. -#define DYNAMIC_LIGHTING_IFSTARLIGHT 3 -#define IS_DYNAMIC_LIGHTING(A) A.dynamic_lighting - - -//code assumes higher numbers override lower numbers. -#define LIGHTING_NO_UPDATE 0 -#define LIGHTING_VIS_UPDATE 1 -#define LIGHTING_CHECK_UPDATE 2 -#define LIGHTING_FORCE_UPDATE 3 - -#define FLASH_LIGHT_DURATION 2 -#define FLASH_LIGHT_POWER 3 -#define FLASH_LIGHT_RANGE 3.8 - -// Emissive blocking. -/// Uses vis_overlays to leverage caching so that very few new items need to be made for the overlay. For anything that doesn't change outline or opaque area much or at all. -#define EMISSIVE_BLOCK_GENERIC 1 -/// Uses a dedicated render_target object to copy the entire appearance in real time to the blocking layer. For things that can change in appearance a lot from the base state, like humans. -#define EMISSIVE_BLOCK_UNIQUE 2 - -/// The color matrix applied to all emissive overlays. Should be solely dependent on alpha and not have RGB overlap with [EM_BLOCK_COLOR]. -#define EMISSIVE_COLOR list(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 1,1,1,0) -/// A globaly cached version of [EMISSIVE_COLOR] for quick access. -GLOBAL_LIST_INIT(emissive_color, EMISSIVE_COLOR) -/// The color matrix applied to all emissive blockers. Should be solely dependent on alpha and not have RGB overlap with [EMISSIVE_COLOR]. -#define EM_BLOCK_COLOR list(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) -/// A globaly cached version of [EM_BLOCK_COLOR] for quick access. -GLOBAL_LIST_INIT(em_block_color, EM_BLOCK_COLOR) -/// The color matrix used to mask out emissive blockers on the emissive plane. Alpha should default to zero, be solely dependent on the RGB value of [EMISSIVE_COLOR], and be independant of the RGB value of [EM_BLOCK_COLOR]. -#define EM_MASK_MATRIX list(0,0,0,1/3, 0,0,0,1/3, 0,0,0,1/3, 0,0,0,0, 1,1,1,0) -/// A globaly cached version of [EM_MASK_MATRIX] for quick access. -GLOBAL_LIST_INIT(em_mask_matrix, EM_MASK_MATRIX) - -/// Returns the red part of a #RRGGBB hex sequence as number -#define GETREDPART(hexa) hex2num(copytext(hexa, 2, 4)) - -/// Returns the green part of a #RRGGBB hex sequence as number -#define GETGREENPART(hexa) hex2num(copytext(hexa, 4, 6)) - -/// Returns the blue part of a #RRGGBB hex sequence as number -#define GETBLUEPART(hexa) hex2num(copytext(hexa, 6, 8)) - -/// Parse the hexadecimal color into lumcounts of each perspective. -#define PARSE_LIGHT_COLOR(source) \ -do { \ - if (source.light_color != COLOR_WHITE) { \ - var/__light_color = source.light_color; \ - source.lum_r = GETREDPART(__light_color) / 255; \ - source.lum_g = GETGREENPART(__light_color) / 255; \ - source.lum_b = GETBLUEPART(__light_color) / 255; \ - } else { \ - source.lum_r = 1; \ - source.lum_g = 1; \ - source.lum_b = 1; \ - }; \ -} while (FALSE) +///Object doesn't use any of the light systems. Should be changed to add a light source to the object. +#define NO_LIGHT_SUPPORT 0 +///Light made with the lighting datums, applying a matrix. +#define STATIC_LIGHT 1 +///Light made by masking the lighting darkness plane. +#define MOVABLE_LIGHT 2 +///Light made by masking the lighting darkness plane, and is directional. +#define MOVABLE_LIGHT_DIRECTIONAL 3 + +///Is a movable light source attached to another movable (its loc), meaning that the lighting component should go one level deeper. +#define LIGHT_ATTACHED (1<<0) + +//Bay lighting engine shit, not in /code/modules/lighting because BYOND is being shit about it +/// frequency, in 1/10ths of a second, of the lighting process +#define LIGHTING_INTERVAL 5 + +#define MINIMUM_USEFUL_LIGHT_RANGE 1.4 + +/// type of falloff to use for lighting; 1 for circular, 2 for square +#define LIGHTING_FALLOFF 1 +/// use lambertian shading for light sources +#define LIGHTING_LAMBERTIAN 0 +/// height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone +#define LIGHTING_HEIGHT 1 +/// Value used to round lumcounts, values smaller than 1/129 don't matter (if they do, thanks sinking points), greater values will make lighting less precise, but in turn increase performance, VERY SLIGHTLY. +#define LIGHTING_ROUND_VALUE (1 / 64) + +/// icon used for lighting shading effects +#define LIGHTING_ICON 'icons/effects/lighting_object.dmi' + +/// If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting objects. +/// Set to zero to disable soft lighting. Luminosity changes then work if it's lit at all. +#define LIGHTING_SOFT_THRESHOLD 0 + +/// If I were you I'd leave this alone. +#define LIGHTING_BASE_MATRIX \ + list \ + ( \ + 1, 1, 1, 0, \ + 1, 1, 1, 0, \ + 1, 1, 1, 0, \ + 1, 1, 1, 0, \ + 0, 0, 0, 1 \ + ) \ + +///How many tiles standard fires glow. +#define LIGHT_RANGE_FIRE 3 + +#define LIGHTING_PLANE_ALPHA_VISIBLE 255 +#define LIGHTING_PLANE_ALPHA_NV_TRAIT 245 +#define LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE 192 +/// For lighting alpha, small amounts lead to big changes. even at 128 its hard to figure out what is dark and what is light, at 64 you almost can't even tell. +#define LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE 128 +#define LIGHTING_PLANE_ALPHA_INVISIBLE 0 + +//lighting area defines +/// dynamic lighting disabled (area stays at full brightness) +#define DYNAMIC_LIGHTING_DISABLED 0 +/// dynamic lighting enabled +#define DYNAMIC_LIGHTING_ENABLED 1 +/// dynamic lighting enabled even if the area doesn't require power +#define DYNAMIC_LIGHTING_FORCED 2 +/// dynamic lighting enabled only if starlight is. +#define DYNAMIC_LIGHTING_IFSTARLIGHT 3 +#define IS_DYNAMIC_LIGHTING(A) A.dynamic_lighting + + +//code assumes higher numbers override lower numbers. +#define LIGHTING_NO_UPDATE 0 +#define LIGHTING_VIS_UPDATE 1 +#define LIGHTING_CHECK_UPDATE 2 +#define LIGHTING_FORCE_UPDATE 3 + +#define FLASH_LIGHT_DURATION 2 +#define FLASH_LIGHT_POWER 3 +#define FLASH_LIGHT_RANGE 3.8 + +// Emissive blocking. +/// Uses vis_overlays to leverage caching so that very few new items need to be made for the overlay. For anything that doesn't change outline or opaque area much or at all. +#define EMISSIVE_BLOCK_GENERIC 1 +/// Uses a dedicated render_target object to copy the entire appearance in real time to the blocking layer. For things that can change in appearance a lot from the base state, like humans. +#define EMISSIVE_BLOCK_UNIQUE 2 + +/// The color matrix applied to all emissive overlays. Should be solely dependent on alpha and not have RGB overlap with [EM_BLOCK_COLOR]. +#define EMISSIVE_COLOR list(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 1,1,1,0) +/// A globaly cached version of [EMISSIVE_COLOR] for quick access. +GLOBAL_LIST_INIT(emissive_color, EMISSIVE_COLOR) +/// The color matrix applied to all emissive blockers. Should be solely dependent on alpha and not have RGB overlap with [EMISSIVE_COLOR]. +#define EM_BLOCK_COLOR list(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) +/// A globaly cached version of [EM_BLOCK_COLOR] for quick access. +GLOBAL_LIST_INIT(em_block_color, EM_BLOCK_COLOR) +/// The color matrix used to mask out emissive blockers on the emissive plane. Alpha should default to zero, be solely dependent on the RGB value of [EMISSIVE_COLOR], and be independant of the RGB value of [EM_BLOCK_COLOR]. +#define EM_MASK_MATRIX list(0,0,0,1/3, 0,0,0,1/3, 0,0,0,1/3, 0,0,0,0, 1,1,1,0) +/// A globaly cached version of [EM_MASK_MATRIX] for quick access. +GLOBAL_LIST_INIT(em_mask_matrix, EM_MASK_MATRIX) + +/// Returns the red part of a #RRGGBB hex sequence as number +#define GETREDPART(hexa) hex2num(copytext(hexa, 2, 4)) + +/// Returns the green part of a #RRGGBB hex sequence as number +#define GETGREENPART(hexa) hex2num(copytext(hexa, 4, 6)) + +/// Returns the blue part of a #RRGGBB hex sequence as number +#define GETBLUEPART(hexa) hex2num(copytext(hexa, 6, 8)) + +/// Parse the hexadecimal color into lumcounts of each perspective. +#define PARSE_LIGHT_COLOR(source) \ +do { \ + if (source.light_color != COLOR_WHITE) { \ + var/__light_color = source.light_color; \ + source.lum_r = GETREDPART(__light_color) / 255; \ + source.lum_g = GETGREENPART(__light_color) / 255; \ + source.lum_b = GETBLUEPART(__light_color) / 255; \ + } else { \ + source.lum_r = 1; \ + source.lum_g = 1; \ + source.lum_b = 1; \ + }; \ +} while (FALSE) diff --git a/code/__defines/sqlite_defines.dm b/code/__defines/sqlite_defines.dm index 8cda3b62a09..4a4c4fe689e 100644 --- a/code/__defines/sqlite_defines.dm +++ b/code/__defines/sqlite_defines.dm @@ -1,7 +1,7 @@ -#define SQLITE_TABLE_FEEDBACK "feedback" - -#define SQLITE_FEEDBACK_COLUMN_ID "id" -#define SQLITE_FEEDBACK_COLUMN_AUTHOR "author" -#define SQLITE_FEEDBACK_COLUMN_TOPIC "topic" -#define SQLITE_FEEDBACK_COLUMN_CONTENT "content" +#define SQLITE_TABLE_FEEDBACK "feedback" + +#define SQLITE_FEEDBACK_COLUMN_ID "id" +#define SQLITE_FEEDBACK_COLUMN_AUTHOR "author" +#define SQLITE_FEEDBACK_COLUMN_TOPIC "topic" +#define SQLITE_FEEDBACK_COLUMN_CONTENT "content" #define SQLITE_FEEDBACK_COLUMN_DATETIME "datetime" \ No newline at end of file diff --git a/code/__defines/tgs.config.dm b/code/__defines/tgs.config.dm index c02ed92daaf..e25041963bc 100644 --- a/code/__defines/tgs.config.dm +++ b/code/__defines/tgs.config.dm @@ -1,11 +1,11 @@ -#define TGS_EXTERNAL_CONFIGURATION -#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) GLOBAL_VAR_INIT(##Name, ##Value); GLOBAL_PROTECT(##Name) -#define TGS_READ_GLOBAL(Name) GLOB.##Name -#define TGS_WRITE_GLOBAL(Name, Value) GLOB.##Name = ##Value -#define TGS_WORLD_ANNOUNCE(message) to_chat(world, "[html_encode(##message)]") -#define TGS_INFO_LOG(message) log_to_dd("TGS Info: [##message]") -#define TGS_WARNING_LOG(message) log_to_dd("TGS Warn: [##message]") -#define TGS_ERROR_LOG(message) log_to_dd("TGS Error: [##message]") -#define TGS_NOTIFY_ADMINS(event) message_admins(##event) -#define TGS_CLIENT_COUNT GLOB.clients.len -#define TGS_PROTECT_DATUM(Path) GENERAL_PROTECT_DATUM(##Path) +#define TGS_EXTERNAL_CONFIGURATION +#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) GLOBAL_VAR_INIT(##Name, ##Value); GLOBAL_PROTECT(##Name) +#define TGS_READ_GLOBAL(Name) GLOB.##Name +#define TGS_WRITE_GLOBAL(Name, Value) GLOB.##Name = ##Value +#define TGS_WORLD_ANNOUNCE(message) to_chat(world, "[html_encode(##message)]") +#define TGS_INFO_LOG(message) log_to_dd("TGS Info: [##message]") +#define TGS_WARNING_LOG(message) log_to_dd("TGS Warn: [##message]") +#define TGS_ERROR_LOG(message) log_to_dd("TGS Error: [##message]") +#define TGS_NOTIFY_ADMINS(event) message_admins(##event) +#define TGS_CLIENT_COUNT GLOB.clients.len +#define TGS_PROTECT_DATUM(Path) GENERAL_PROTECT_DATUM(##Path) diff --git a/code/__defines/time.dm b/code/__defines/time.dm index c2a3632c745..9b3c384cfb3 100644 --- a/code/__defines/time.dm +++ b/code/__defines/time.dm @@ -1,33 +1,33 @@ -/// Define that just has the current in-universe year for use in whatever context you might want to display that in. (For example, 2022 -> 2562 given a 540 year offset) -#define CURRENT_STATION_YEAR (GLOB.year_integer + STATION_YEAR_OFFSET) - -/// In-universe, SS13 is set 300 years in the future from the real-world day, hence this number for determining the year-offset for the in-game year. -#define STATION_YEAR_OFFSET 300 - -#define MILISECOND * 0.01 -#define MILLISECONDS * 0.01 - -#define DECISECONDS *1 //the base unit all of these defines are scaled by, because byond uses that as a unit of measurement for some reason - -#define SECOND *10 -#define SECONDS *10 - -#define MINUTE *600 -#define MINUTES *600 - -#define HOUR *36000 -#define HOURS *36000 - -#define DAY *864000 -#define DAYS *864000 - -#define TICK *world.tick_lag -#define TICKS *world.tick_lag - -#define DS2TICKS(DS) ((DS)/world.tick_lag) - -#define TICKS2DS(T) ((T) TICKS) - -#define MS2DS(T) ((T) MILLISECONDS) - -#define DS2MS(T) ((T) * 100) +/// Define that just has the current in-universe year for use in whatever context you might want to display that in. (For example, 2022 -> 2562 given a 540 year offset) +#define CURRENT_STATION_YEAR (GLOB.year_integer + STATION_YEAR_OFFSET) + +/// In-universe, SS13 is set 300 years in the future from the real-world day, hence this number for determining the year-offset for the in-game year. +#define STATION_YEAR_OFFSET 300 + +#define MILISECOND * 0.01 +#define MILLISECONDS * 0.01 + +#define DECISECONDS *1 //the base unit all of these defines are scaled by, because byond uses that as a unit of measurement for some reason + +#define SECOND *10 +#define SECONDS *10 + +#define MINUTE *600 +#define MINUTES *600 + +#define HOUR *36000 +#define HOURS *36000 + +#define DAY *864000 +#define DAYS *864000 + +#define TICK *world.tick_lag +#define TICKS *world.tick_lag + +#define DS2TICKS(DS) ((DS)/world.tick_lag) + +#define TICKS2DS(T) ((T) TICKS) + +#define MS2DS(T) ((T) MILLISECONDS) + +#define DS2MS(T) ((T) * 100) diff --git a/code/_global_vars/time_vars.dm b/code/_global_vars/time_vars.dm index f05384ca493..1b4fc6401ac 100644 --- a/code/_global_vars/time_vars.dm +++ b/code/_global_vars/time_vars.dm @@ -1,2 +1,2 @@ -GLOBAL_VAR_INIT(year, time2text(world.realtime,"YYYY")) -GLOBAL_VAR_INIT(year_integer, text2num(year)) // = 2013??? +GLOBAL_VAR_INIT(year, time2text(world.realtime,"YYYY")) +GLOBAL_VAR_INIT(year_integer, text2num(year)) // = 2013??? diff --git a/code/_helpers/atmospherics.dm b/code/_helpers/atmospherics.dm index 83b5982f137..25fd699b32b 100644 --- a/code/_helpers/atmospherics.dm +++ b/code/_helpers/atmospherics.dm @@ -1,78 +1,78 @@ -/obj/proc/analyze_gases(var/atom/A, var/mob/user) - if(src != A) - user.visible_message("\The [user] has used \an [src] on \the [A]") - - A.add_fingerprint(user) - var/list/result = A.atmosanalyze(user) - if(result && result.len) - to_chat(user, "Results of the analysis[src == A ? "" : " of \the [A]"]") - for(var/line in result) - to_chat(user, "[line]") - return 1 - - to_chat(user, "Your [src] flashes a red light as it fails to analyze \the [A].") - return 0 - -/proc/atmosanalyzer_scan(var/atom/target, var/datum/gas_mixture/mixture, var/mob/user) - var/list/results = list() - - if (mixture && mixture.total_moles > 0) - var/pressure = mixture.return_pressure() - var/total_moles = mixture.total_moles - results += "Pressure: [round(pressure,0.1)] kPa" - for(var/mix in mixture.gas) - results += "[gas_data.name[mix]]: [round((mixture.gas[mix] / total_moles) * 100)]% ([round(mixture.gas[mix], 0.01)] moles)" - results += "Temperature: [round(mixture.temperature-T0C)]°C" - results += "Heat Capacity: [round(mixture.heat_capacity(),0.1)]" - else - results += "\The [target] is empty!" - - return results - -/turf/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.air, user) - -/atom/proc/atmosanalyze(var/mob/user) - return - -/obj/item/weapon/tank/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.air_contents, user) - -/obj/machinery/portable_atmospherics/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.air_contents, user) - -/obj/machinery/atmospherics/pipe/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.parent.air, user) - -/obj/machinery/atmospherics/portables_connector/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.network.gases, user) - -/obj/machinery/atmospherics/unary/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.air_contents, user) - -/obj/machinery/atmospherics/binary/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.air1, user) - -/obj/machinery/atmospherics/trinary/atmos_filter/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.air1, user) - -/obj/machinery/atmospherics/trinary/mixer/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.air3, user) - -/obj/machinery/atmospherics/omni/atmos_filter/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.input.air, user) - -/obj/machinery/atmospherics/omni/mixer/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.output.air, user) - -/obj/machinery/meter/atmosanalyze(var/mob/user) - var/datum/gas_mixture/mixture = null - if(src.target) - mixture = src.target.parent.air - return atmosanalyzer_scan(src, mixture, user) - -/obj/machinery/power/rad_collector/atmosanalyze(var/mob/user) - if(P) return atmosanalyzer_scan(src, src.P.air_contents, user) - -/obj/item/weapon/flamethrower/atmosanalyze(var/mob/user) - if(ptank) return atmosanalyzer_scan(src, ptank.air_contents, user) +/obj/proc/analyze_gases(var/atom/A, var/mob/user) + if(src != A) + user.visible_message("\The [user] has used \an [src] on \the [A]") + + A.add_fingerprint(user) + var/list/result = A.atmosanalyze(user) + if(result && result.len) + to_chat(user, "Results of the analysis[src == A ? "" : " of \the [A]"]") + for(var/line in result) + to_chat(user, "[line]") + return 1 + + to_chat(user, "Your [src] flashes a red light as it fails to analyze \the [A].") + return 0 + +/proc/atmosanalyzer_scan(var/atom/target, var/datum/gas_mixture/mixture, var/mob/user) + var/list/results = list() + + if (mixture && mixture.total_moles > 0) + var/pressure = mixture.return_pressure() + var/total_moles = mixture.total_moles + results += "Pressure: [round(pressure,0.1)] kPa" + for(var/mix in mixture.gas) + results += "[gas_data.name[mix]]: [round((mixture.gas[mix] / total_moles) * 100)]% ([round(mixture.gas[mix], 0.01)] moles)" + results += "Temperature: [round(mixture.temperature-T0C)]°C" + results += "Heat Capacity: [round(mixture.heat_capacity(),0.1)]" + else + results += "\The [target] is empty!" + + return results + +/turf/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.air, user) + +/atom/proc/atmosanalyze(var/mob/user) + return + +/obj/item/weapon/tank/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.air_contents, user) + +/obj/machinery/portable_atmospherics/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.air_contents, user) + +/obj/machinery/atmospherics/pipe/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.parent.air, user) + +/obj/machinery/atmospherics/portables_connector/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.network.gases, user) + +/obj/machinery/atmospherics/unary/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.air_contents, user) + +/obj/machinery/atmospherics/binary/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.air1, user) + +/obj/machinery/atmospherics/trinary/atmos_filter/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.air1, user) + +/obj/machinery/atmospherics/trinary/mixer/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.air3, user) + +/obj/machinery/atmospherics/omni/atmos_filter/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.input.air, user) + +/obj/machinery/atmospherics/omni/mixer/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.output.air, user) + +/obj/machinery/meter/atmosanalyze(var/mob/user) + var/datum/gas_mixture/mixture = null + if(src.target) + mixture = src.target.parent.air + return atmosanalyzer_scan(src, mixture, user) + +/obj/machinery/power/rad_collector/atmosanalyze(var/mob/user) + if(P) return atmosanalyzer_scan(src, src.P.air_contents, user) + +/obj/item/weapon/flamethrower/atmosanalyze(var/mob/user) + if(ptank) return atmosanalyzer_scan(src, ptank.air_contents, user) diff --git a/code/_helpers/files.dm b/code/_helpers/files.dm index d904646a0fa..d18f6f4623e 100644 --- a/code/_helpers/files.dm +++ b/code/_helpers/files.dm @@ -1,75 +1,75 @@ -//checks if a file exists and contains text -//returns text as a string if these conditions are met -/proc/return_file_text(filename) - if(fexists(filename) == 0) - error("File not found ([filename])") - return - - var/text = file2text(filename) - if(!text) - error("File empty ([filename])") - return - - return text - -//Sends resource files to client cache -/client/proc/getFiles() - for(var/file in args) - src << browse_rsc(file) - -/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm")) - var/path = root - - for(var/i=0, i 0) - to_chat(src, span_red("Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.")) - return 1 - fileaccess_timer = world.time + FTPDELAY - return 0 -#undef FTPDELAY - -/// Returns the md5 of a file at a given path. -/proc/md5filepath(path) - . = md5(file(path)) - -/// Save file as an external file then md5 it. -/// Used because md5ing files stored in the rsc sometimes gives incorrect md5 results. -/proc/md5asfile(file) - var/static/notch = 0 - // its importaint this code can handle md5filepath sleeping instead of hard blocking, if it's converted to use rust_g. - var/filename = "tmp/md5asfile.[world.realtime].[world.timeofday].[world.time].[world.tick_usage].[notch]" - notch = WRAP(notch+1, 0, 2**15) - fcopy(file, filename) - . = md5filepath(filename) - fdel(filename) +//checks if a file exists and contains text +//returns text as a string if these conditions are met +/proc/return_file_text(filename) + if(fexists(filename) == 0) + error("File not found ([filename])") + return + + var/text = file2text(filename) + if(!text) + error("File empty ([filename])") + return + + return text + +//Sends resource files to client cache +/client/proc/getFiles() + for(var/file in args) + src << browse_rsc(file) + +/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm")) + var/path = root + + for(var/i=0, i 0) + to_chat(src, span_red("Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.")) + return 1 + fileaccess_timer = world.time + FTPDELAY + return 0 +#undef FTPDELAY + +/// Returns the md5 of a file at a given path. +/proc/md5filepath(path) + . = md5(file(path)) + +/// Save file as an external file then md5 it. +/// Used because md5ing files stored in the rsc sometimes gives incorrect md5 results. +/proc/md5asfile(file) + var/static/notch = 0 + // its importaint this code can handle md5filepath sleeping instead of hard blocking, if it's converted to use rust_g. + var/filename = "tmp/md5asfile.[world.realtime].[world.timeofday].[world.time].[world.tick_usage].[notch]" + notch = WRAP(notch+1, 0, 2**15) + fcopy(file, filename) + . = md5filepath(filename) + fdel(filename) diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index b84f46e650a..812c0d5fe73 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -1,749 +1,749 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/proc/dopage(src,target) - var/href_list - var/href - href_list = params2list("src=\ref[src]&[target]=1") - href = "src=\ref[src];[target]=1" - src:temphtml = null - src:Topic(href, href_list) - return null - -/proc/is_on_same_plane_or_station(var/z1, var/z2) - if(z1 == z2) - return 1 - if((z1 in using_map.station_levels) && (z2 in using_map.station_levels)) - return 1 - return 0 - -/proc/max_default_z_level() - var/max_z = 0 - for(var/z in using_map.station_levels) - max_z = max(z, max_z) - for(var/z in using_map.admin_levels) - max_z = max(z, max_z) - for(var/z in using_map.player_levels) - max_z = max(z, max_z) - return max_z - -/proc/get_area(atom/A) - RETURN_TYPE(/area) - if(isarea(A)) - return A - var/turf/T = get_turf(A) - return T ? T.loc : null - -/proc/get_area_name(atom/X, format_text = FALSE) - var/area/A = isarea(X) ? X : get_area(X) - if(!A) - return null - return format_text ? format_text(A.name) : A.name - -/** Checks if any living humans are in a given area. */ -/proc/area_is_occupied(var/area/myarea) - // Testing suggests looping over human_mob_list is quicker than looping over area contents - for(var/mob/living/carbon/human/H in human_mob_list) - if(H.stat >= DEAD) //Conditions for exclusion here, like if disconnected people start blocking it. - continue - var/area/A = get_area(H) - if(A == myarea) //The loc of a turf is the area it is in. - return 1 - return 0 - -/proc/in_range(source, user) - if(get_dist(source, user) <= 1) - return 1 - - return 0 //not in range and not telekinetic - -// Like view but bypasses luminosity check - -/proc/hear(var/range, var/atom/source) - - var/lum = source.luminosity - source.luminosity = 6 - - var/list/heard = view(range, source) - source.luminosity = lum - - return heard - -/proc/isStationLevel(var/level) - return level in using_map.station_levels - -/proc/isNotStationLevel(var/level) - return !isStationLevel(level) - -/proc/isPlayerLevel(var/level) - return level in using_map.player_levels - -/proc/isAdminLevel(var/level) - return level in using_map.admin_levels - -/proc/isNotAdminLevel(var/level) - return !isAdminLevel(level) - -/proc/circlerange(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/atom/T in range(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - - //turfs += centerturf - return turfs - -/proc/circleview(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/atoms = new/list() - var/rsq = radius * (radius+0.5) - - for(var/atom/A in view(radius, centerturf)) - var/dx = A.x - centerturf.x - var/dy = A.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - atoms += A - - //turfs += centerturf - return atoms - -/proc/trange(rad = 0, turf/centre = null) //alternative to range (ONLY processes turfs and thus less intensive) - if(!centre) - return - - var/turf/x1y1 = locate(((centre.x-rad)<1 ? 1 : centre.x-rad),((centre.y-rad)<1 ? 1 : centre.y-rad),centre.z) - var/turf/x2y2 = locate(((centre.x+rad)>world.maxx ? world.maxx : centre.x+rad),((centre.y+rad)>world.maxy ? world.maxy : centre.y+rad),centre.z) - return block(x1y1,x2y2) - -/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj) - var/dx = Loc1.x - Loc2.x - var/dy = Loc1.y - Loc2.y - - var/dist = sqrt(dx**2 + dy**2) - - return dist - -/proc/circlerangeturfs(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/turf/T in range(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - return turfs - -/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()? - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/turf/T in view(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - return turfs - - - -//var/debug_mob = 0 - -// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents. -// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects, -// being unable to hear people due to being in a box within a bag. - -/proc/recursive_content_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_mobs = 1, var/include_objects = 1, var/ignore_show_messages = 0) - - if(!recursion_limit) - return L - - for(var/I in O.contents) - - if(ismob(I)) - if(!sight_check || isInSight(I, O)) - L |= recursive_content_check(I, L, recursion_limit - 1, client_check, sight_check, include_mobs, include_objects) - if(include_mobs) - if(client_check) - var/mob/M = I - if(M.client) - L |= M - else - L |= I - - else if(istype(I,/obj/)) - var/obj/check_obj = I - if(ignore_show_messages || check_obj.show_messages) - if(!sight_check || isInSight(I, O)) - L |= recursive_content_check(I, L, recursion_limit - 1, client_check, sight_check, include_mobs, include_objects) - if(include_objects) - L |= I - - return L - -// Returns a list of mobs and/or objects in range of R from source. Used in radio and say code. - -/proc/get_mobs_or_objects_in_view(var/R, var/atom/source, var/include_mobs = 1, var/include_objects = 1) - - var/turf/T = get_turf(source) - var/list/hear = list() - - if(!T) - return hear - - var/list/range = hear(R, T) - - for(var/I in range) - if(ismob(I)) - hear |= recursive_content_check(I, hear, 3, 1, 0, include_mobs, include_objects) - if(include_mobs) - var/mob/M = I - if(M.client) - hear += M - else if(istype(I,/obj/)) - hear |= recursive_content_check(I, hear, 3, 1, 0, include_mobs, include_objects) - var/obj/O = I - if(O.show_messages && include_objects) - hear += I - - return hear - - -/proc/get_mobs_in_radio_ranges(var/list/obj/item/device/radio/radios) - - set background = 1 - - . = list() - // Returns a list of mobs who can hear any of the radios given in @radios - var/list/speaker_coverage = list() - for(var/obj/item/device/radio/R as anything in radios) - var/turf/speaker = get_turf(R) - if(speaker) - for(var/turf/T in hear(R.canhear_range,speaker)) - speaker_coverage[T] = R - - - // Try to find all the players who can hear the message - for(var/i = 1; i <= player_list.len; i++) - var/mob/M = player_list[i] - if(M.can_hear_radio(speaker_coverage)) - . += M - return . - -/mob/proc/can_hear_radio(var/list/hearturfs) - return FALSE - -/mob/living/can_hear_radio(var/list/hearturfs) - return get_turf(src) in hearturfs - -/mob/living/silicon/robot/can_hear_radio(var/list/hearturfs) - var/turf/T = get_turf(src) - var/obj/item/device/radio/borg/R = hearturfs[T] // this should be an assoc list of turf-to-radio - - // We heard it on our own radio? We use power for that. - if(istype(R) && R.myborg == src) - var/datum/robot_component/CO = get_component("radio") - if(!CO || !is_component_functioning("radio") || !cell_use_power(CO.active_usage)) - return FALSE // Sorry, couldn't hear - - return R // radio, true, false, what's the difference - -/mob/observer/dead/can_hear_radio(var/list/hearturfs) - return is_preference_enabled(/datum/client_preference/ghost_radio) - - -//Uses dview to quickly return mobs and objects in view, -// then adds additional mobs or objects if they are in range 'smartly', -// based on their presence in lists of players or registered objects -// Type: 1-audio, 2-visual, 0-neither -/proc/get_mobs_and_objs_in_view_fast(var/turf/T, var/range, var/type = 1, var/remote_ghosts = TRUE) - var/list/mobs = list() - var/list/objs = list() - - var/list/hear = dview(range,T,INVISIBILITY_MAXIMUM) - var/list/hearturfs = list() - - // Openspace visibility handling - // Below turfs we can see - for(var/turf/simulated/open/O in hear) - var/turf/U = GetBelow(O) - while(istype(U)) - hearturfs |= U - if(isopenspace(U)) - U = GetBelow(U) - else - U = null - - // Above us - var/above_range = range - var/turf/Ab = GetAbove(T) - while(isopenspace(Ab) && --above_range > 0) - hear |= dview(above_range,Ab,INVISIBILITY_MAXIMUM) - Ab = GetAbove(Ab) - - for(var/thing in hear) - if(istype(thing, /obj)) //Can't use isobj() because /atom/movable returns true in that - objs += thing - hearturfs |= get_turf(thing) - if(ismob(thing)) - mobs += thing - hearturfs |= get_turf(thing) - - //A list of every mob with a client - for(var/mob in player_list) - if(!ismob(mob)) - player_list -= mob - continue - //VOREStation Edit End - Trying to fix some vorestation bug. - if(get_turf(mob) in hearturfs) - mobs |= mob - continue - - var/mob/M = mob - if(M && M.stat == DEAD && remote_ghosts && !M.forbid_seeing_deadchat) - switch(type) - if(1) //Audio messages use ghost_ears - if(M.is_preference_enabled(/datum/client_preference/ghost_ears)) - mobs |= M - if(2) //Visual messages use ghost_sight - if(M.is_preference_enabled(/datum/client_preference/ghost_sight)) - mobs |= M - - //For objects below the top level who still want to hear - for(var/obj in listening_objects) - if(get_turf(obj) in hearturfs) - objs |= obj - - return list("mobs" = mobs, "objs" = objs) - -/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5) - var/turf/T - if(X1==X2) - if(Y1==Y2) - return 1 //Light cannot be blocked on same tile - else - var/s = SIGN(Y2-Y1) - Y1+=s - while(Y1!=Y2) - T=locate(X1,Y1,Z) - if(T.opacity) - return 0 - Y1+=s - else - var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1)) - var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles - var/signX = SIGN(X2-X1) - var/signY = SIGN(Y2-Y1) - if(X1 abs (dx)) //slope is above 1:1 (move horizontally in a tie) - if(dy > 0) - return get_step(start, SOUTH) - else - return get_step(start, NORTH) - else - if(dx > 0) - return get_step(start, WEST) - else - return get_step(start, EAST) - -/proc/get_mob_by_key(var/key) - for(var/mob/M in mob_list) - if(M.ckey == lowertext(key)) - return M - return null - - -// Will return a list of active candidates. It increases the buffer 5 times until it finds a candidate which is active within the buffer. -/proc/get_active_candidates(var/buffer = 1) - - var/list/candidates = list() //List of candidate KEYS to assume control of the new larva ~Carn - var/i = 0 - while(candidates.len <= 0 && i < 5) - for(var/mob/observer/dead/G in player_list) - if(((G.client.inactivity/10)/60) <= buffer + i) // the most active players are more likely to become an alien - if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) - candidates += G.key - i++ - return candidates - -// Same as above but for alien candidates. - -/proc/get_alien_candidates() - - var/list/candidates = list() //List of candidate KEYS to assume control of the new larva ~Carn - var/i = 0 - while(candidates.len <= 0 && i < 5) - for(var/mob/observer/dead/G in player_list) - if(G.client.prefs.be_special & BE_ALIEN) - if(((G.client.inactivity/10)/60) <= ALIEN_SELECT_AFK_BUFFER + i) // the most active players are more likely to become an alien - if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) - candidates += G.key - i++ - return candidates - -/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480) - if(!isobj(O)) O = new /obj/screen/text() - O.maptext = maptext - O.maptext_height = maptext_height - O.maptext_width = maptext_width - O.screen_loc = screen_loc - return O - -/proc/Show2Group4Delay(obj/O, list/group, delay=0) - if(!isobj(O)) return - if(!group) group = GLOB.clients - for(var/client/C in group) - C.screen += O - if(delay) - spawn(delay) - for(var/client/C in group) - C.screen -= O - -/datum/projectile_data - var/src_x - var/src_y - var/time - var/distance - var/power_x - var/power_y - var/dest_x - var/dest_y - -/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \ - var/power_x, var/power_y, var/dest_x, var/dest_y) - src.src_x = src_x - src.src_y = src_y - src.time = time - src.distance = distance - src.power_x = power_x - src.power_y = power_y - src.dest_x = dest_x - src.dest_y = dest_y - -/proc/projectile_trajectory(var/src_x, var/src_y, var/rotation, var/angle, var/power) - - // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle], - // rotated at [rotation] and with the power of [power] - // Thanks to VistaPOWA for this function - - var/power_x = power * cos(angle) - var/power_y = power * sin(angle) - var/time = 2* power_y / 10 //10 = g - - var/distance = time * power_x - - var/dest_x = src_x + distance*sin(rotation); - var/dest_y = src_y + distance*cos(rotation); - - return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) - -/proc/GetRedPart(const/hexa) - return hex2num(copytext(hexa,2,4)) - -/proc/GetGreenPart(const/hexa) - return hex2num(copytext(hexa,4,6)) - -/proc/GetBluePart(const/hexa) - return hex2num(copytext(hexa,6,8)) - -/proc/GetHexColors(const/hexa) - return list( - GetRedPart(hexa), - GetGreenPart(hexa), - GetBluePart(hexa) - ) - -/proc/MixColors(const/list/colors) - var/list/reds = list() - var/list/blues = list() - var/list/greens = list() - var/list/weights = list() - - for (var/i = 0, ++i <= colors.len) - reds.Add(GetRedPart(colors[i])) - blues.Add(GetBluePart(colors[i])) - greens.Add(GetGreenPart(colors[i])) - weights.Add(1) - - var/r = mixOneColor(weights, reds) - var/g = mixOneColor(weights, greens) - var/b = mixOneColor(weights, blues) - return rgb(r,g,b) - -/proc/mixOneColor(var/list/weight, var/list/color) - if (!weight || !color || length(weight)!=length(color)) - return 0 - - var/contents = length(weight) - var/i - - //normalize weights - var/listsum = 0 - for(i=1; i<=contents; i++) - listsum += weight[i] - for(i=1; i<=contents; i++) - weight[i] /= listsum - - //mix them - var/mixedcolor = 0 - for(i=1; i<=contents; i++) - mixedcolor += weight[i]*color[i] - mixedcolor = round(mixedcolor) - - //until someone writes a formal proof for this algorithm, let's keep this in -// if(mixedcolor<0x00 || mixedcolor>0xFF) -// return 0 - //that's not the kind of operation we are running here, nerd - mixedcolor=min(max(mixedcolor,0),255) - - return mixedcolor - -/** -* Gets the highest and lowest pressures from the tiles in cardinal directions -* around us, then checks the difference. -*/ -/proc/getOPressureDifferential(var/turf/loc) - var/minp=16777216; - var/maxp=0; - for(var/dir in GLOB.cardinal) - var/turf/simulated/T=get_turf(get_step(loc,dir)) - var/cp=0 - if(T && istype(T) && T.zone) - var/datum/gas_mixture/environment = T.return_air() - cp = environment.return_pressure() - else - if(istype(T,/turf/simulated)) - continue - if(cpmaxp)maxp=cp - return abs(minp-maxp) - -/proc/convert_k2c(var/temp) - return ((temp - T0C)) - -/proc/convert_c2k(var/temp) - return ((temp + T0C)) - -/proc/getCardinalAirInfo(var/turf/loc, var/list/stats=list("temperature")) - var/list/temps = new/list(4) - for(var/dir in GLOB.cardinal) - var/direction - switch(dir) - if(NORTH) - direction = 1 - if(SOUTH) - direction = 2 - if(EAST) - direction = 3 - if(WEST) - direction = 4 - var/turf/simulated/T=get_turf(get_step(loc,dir)) - var/list/rstats = new /list(stats.len) - if(T && istype(T) && T.zone) - var/datum/gas_mixture/environment = T.return_air() - for(var/i=1;i<=stats.len;i++) - if(stats[i] == "pressure") - rstats[i] = environment.return_pressure() - else - rstats[i] = environment.vars[stats[i]] - else if(istype(T, /turf/simulated)) - rstats = null // Exclude zone (wall, door, etc). - else if(istype(T, /turf)) - // Should still work. (/turf/return_air()) - var/datum/gas_mixture/environment = T.return_air() - for(var/i=1;i<=stats.len;i++) - if(stats[i] == "pressure") - rstats[i] = environment.return_pressure() - else - rstats[i] = environment.vars[stats[i]] - temps[direction] = rstats - return temps - -/proc/MinutesToTicks(var/minutes) - return SecondsToTicks(60 * minutes) - -/proc/SecondsToTicks(var/seconds) - return seconds * 10 - -/proc/window_flash(var/client_or_usr) - if (!client_or_usr) - return - winset(client_or_usr, "mainwindow", "flash=5") - -/** - * Get a bounding box of a list of atoms. - * - * Arguments: - * - atoms - List of atoms. Can accept output of view() and range() procs. - * - * Returns: list(x1, y1, x2, y2) - */ -/proc/get_bbox_of_atoms(list/atoms) - var/list/list_x = list() - var/list/list_y = list() - for(var/_a in atoms) - var/atom/a = _a - list_x += a.x - list_y += a.y - return list( - min(list_x), - min(list_y), - max(list_x), - max(list_y)) - -// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents. -// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects, -// being unable to hear people due to being in a box within a bag. - -/proc/recursive_mob_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_radio = 1) - - //GLOB.debug_mob += O.contents.len - if(!recursion_limit) - return L - for(var/atom/A in O.contents) - - if(ismob(A)) - var/mob/M = A - if(client_check && !M.client) - L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) - continue - if(sight_check && !isInSight(A, O)) - continue - L |= M - //log_world("[recursion_limit] = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") - - else if(include_radio && istype(A, /obj/item/radio)) - if(sight_check && !isInSight(A, O)) - continue - L |= A - - if(isobj(A) || ismob(A)) - L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) - return L - -// The old system would loop through lists for a total of 5000 per function call, in an empty server. -// This new system will loop at around 1000 in an empty server. - -/proc/get_mobs_in_view(var/R, var/atom/source, var/include_clientless = FALSE) - // Returns a list of mobs in range of R from source. Used in radio and say code. - - var/turf/T = get_turf(source) - var/list/hear = list() - - if(!T) - return hear - - var/list/range = hear(R, T) - - for(var/atom/A in range) - if(ismob(A)) - var/mob/M = A - if(M.client || include_clientless) - hear += M - //log_world("Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") - else if(istype(A, /obj/item/radio)) - hear += A - - if(isobj(A) || ismob(A)) - hear |= recursive_mob_check(A, hear, 3, 1, 0, 1) - - return hear - -/proc/get_belly(var/atom/A) // return a belly we're in, one way or another; and if we aren't (or are too deep to comprehend being in belly), returns null - var/atom/loc_check = A.loc - var/recursion_level = 0 - while(loc_check && !isbelly(loc_check) && !isturf(loc_check)) - if(recursion_level > 7) // abstractly picked number, but basically means we tried going 8 levels up. Something is wrong if youre THAT deep anyway - break - loc_check = loc_check.loc - recursion_level++ - if(isbelly(loc_check)) - return loc_check - return null - -/proc/get_all_prey_recursive(var/mob/living/L, var/client_check = 1) // returns all prey inside the target as well all prey of target's prey, as well as all prey inside target's prey, etc. - var/list/result = list() - - if(!istype(L) || !(L.vore_organs) || !(L.vore_organs.len)) - return result - - for(var/obj/belly/B in L.vore_organs) - for(var/mob/living/P in B.contents) - if(istype(P)) - if(client_check && P.client) - result |= P - result |= get_all_prey_recursive(P, client_check) - - return result - -/proc/random_color(saturated) //Returns a random color. If saturated is true, it will avoid pure white or pure black - var/r = rand(1,255) - var/g = rand(1,255) - var/b = rand(1,255) - - if(saturated) //Let's make sure we don't get too close to pure black or pure white, as they won't look good with grayscale sprites - if(r + g + b < 50) - r = r + rand(5,20) - g = g + rand(5,20) - b = b + rand(5,20) - else if (r + g + b > 700) - r = r - rand(5,50) - g = g - rand(5,50) - b = b - rand(5,50) - - var/color = rgb(r, g, b) - return color +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/proc/dopage(src,target) + var/href_list + var/href + href_list = params2list("src=\ref[src]&[target]=1") + href = "src=\ref[src];[target]=1" + src:temphtml = null + src:Topic(href, href_list) + return null + +/proc/is_on_same_plane_or_station(var/z1, var/z2) + if(z1 == z2) + return 1 + if((z1 in using_map.station_levels) && (z2 in using_map.station_levels)) + return 1 + return 0 + +/proc/max_default_z_level() + var/max_z = 0 + for(var/z in using_map.station_levels) + max_z = max(z, max_z) + for(var/z in using_map.admin_levels) + max_z = max(z, max_z) + for(var/z in using_map.player_levels) + max_z = max(z, max_z) + return max_z + +/proc/get_area(atom/A) + RETURN_TYPE(/area) + if(isarea(A)) + return A + var/turf/T = get_turf(A) + return T ? T.loc : null + +/proc/get_area_name(atom/X, format_text = FALSE) + var/area/A = isarea(X) ? X : get_area(X) + if(!A) + return null + return format_text ? format_text(A.name) : A.name + +/** Checks if any living humans are in a given area. */ +/proc/area_is_occupied(var/area/myarea) + // Testing suggests looping over human_mob_list is quicker than looping over area contents + for(var/mob/living/carbon/human/H in human_mob_list) + if(H.stat >= DEAD) //Conditions for exclusion here, like if disconnected people start blocking it. + continue + var/area/A = get_area(H) + if(A == myarea) //The loc of a turf is the area it is in. + return 1 + return 0 + +/proc/in_range(source, user) + if(get_dist(source, user) <= 1) + return 1 + + return 0 //not in range and not telekinetic + +// Like view but bypasses luminosity check + +/proc/hear(var/range, var/atom/source) + + var/lum = source.luminosity + source.luminosity = 6 + + var/list/heard = view(range, source) + source.luminosity = lum + + return heard + +/proc/isStationLevel(var/level) + return level in using_map.station_levels + +/proc/isNotStationLevel(var/level) + return !isStationLevel(level) + +/proc/isPlayerLevel(var/level) + return level in using_map.player_levels + +/proc/isAdminLevel(var/level) + return level in using_map.admin_levels + +/proc/isNotAdminLevel(var/level) + return !isAdminLevel(level) + +/proc/circlerange(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/atom/T in range(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + + //turfs += centerturf + return turfs + +/proc/circleview(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/atoms = new/list() + var/rsq = radius * (radius+0.5) + + for(var/atom/A in view(radius, centerturf)) + var/dx = A.x - centerturf.x + var/dy = A.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + atoms += A + + //turfs += centerturf + return atoms + +/proc/trange(rad = 0, turf/centre = null) //alternative to range (ONLY processes turfs and thus less intensive) + if(!centre) + return + + var/turf/x1y1 = locate(((centre.x-rad)<1 ? 1 : centre.x-rad),((centre.y-rad)<1 ? 1 : centre.y-rad),centre.z) + var/turf/x2y2 = locate(((centre.x+rad)>world.maxx ? world.maxx : centre.x+rad),((centre.y+rad)>world.maxy ? world.maxy : centre.y+rad),centre.z) + return block(x1y1,x2y2) + +/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj) + var/dx = Loc1.x - Loc2.x + var/dy = Loc1.y - Loc2.y + + var/dist = sqrt(dx**2 + dy**2) + + return dist + +/proc/circlerangeturfs(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/turf/T in range(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + return turfs + +/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()? + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/turf/T in view(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + return turfs + + + +//var/debug_mob = 0 + +// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents. +// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects, +// being unable to hear people due to being in a box within a bag. + +/proc/recursive_content_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_mobs = 1, var/include_objects = 1, var/ignore_show_messages = 0) + + if(!recursion_limit) + return L + + for(var/I in O.contents) + + if(ismob(I)) + if(!sight_check || isInSight(I, O)) + L |= recursive_content_check(I, L, recursion_limit - 1, client_check, sight_check, include_mobs, include_objects) + if(include_mobs) + if(client_check) + var/mob/M = I + if(M.client) + L |= M + else + L |= I + + else if(istype(I,/obj/)) + var/obj/check_obj = I + if(ignore_show_messages || check_obj.show_messages) + if(!sight_check || isInSight(I, O)) + L |= recursive_content_check(I, L, recursion_limit - 1, client_check, sight_check, include_mobs, include_objects) + if(include_objects) + L |= I + + return L + +// Returns a list of mobs and/or objects in range of R from source. Used in radio and say code. + +/proc/get_mobs_or_objects_in_view(var/R, var/atom/source, var/include_mobs = 1, var/include_objects = 1) + + var/turf/T = get_turf(source) + var/list/hear = list() + + if(!T) + return hear + + var/list/range = hear(R, T) + + for(var/I in range) + if(ismob(I)) + hear |= recursive_content_check(I, hear, 3, 1, 0, include_mobs, include_objects) + if(include_mobs) + var/mob/M = I + if(M.client) + hear += M + else if(istype(I,/obj/)) + hear |= recursive_content_check(I, hear, 3, 1, 0, include_mobs, include_objects) + var/obj/O = I + if(O.show_messages && include_objects) + hear += I + + return hear + + +/proc/get_mobs_in_radio_ranges(var/list/obj/item/device/radio/radios) + + set background = 1 + + . = list() + // Returns a list of mobs who can hear any of the radios given in @radios + var/list/speaker_coverage = list() + for(var/obj/item/device/radio/R as anything in radios) + var/turf/speaker = get_turf(R) + if(speaker) + for(var/turf/T in hear(R.canhear_range,speaker)) + speaker_coverage[T] = R + + + // Try to find all the players who can hear the message + for(var/i = 1; i <= player_list.len; i++) + var/mob/M = player_list[i] + if(M.can_hear_radio(speaker_coverage)) + . += M + return . + +/mob/proc/can_hear_radio(var/list/hearturfs) + return FALSE + +/mob/living/can_hear_radio(var/list/hearturfs) + return get_turf(src) in hearturfs + +/mob/living/silicon/robot/can_hear_radio(var/list/hearturfs) + var/turf/T = get_turf(src) + var/obj/item/device/radio/borg/R = hearturfs[T] // this should be an assoc list of turf-to-radio + + // We heard it on our own radio? We use power for that. + if(istype(R) && R.myborg == src) + var/datum/robot_component/CO = get_component("radio") + if(!CO || !is_component_functioning("radio") || !cell_use_power(CO.active_usage)) + return FALSE // Sorry, couldn't hear + + return R // radio, true, false, what's the difference + +/mob/observer/dead/can_hear_radio(var/list/hearturfs) + return is_preference_enabled(/datum/client_preference/ghost_radio) + + +//Uses dview to quickly return mobs and objects in view, +// then adds additional mobs or objects if they are in range 'smartly', +// based on their presence in lists of players or registered objects +// Type: 1-audio, 2-visual, 0-neither +/proc/get_mobs_and_objs_in_view_fast(var/turf/T, var/range, var/type = 1, var/remote_ghosts = TRUE) + var/list/mobs = list() + var/list/objs = list() + + var/list/hear = dview(range,T,INVISIBILITY_MAXIMUM) + var/list/hearturfs = list() + + // Openspace visibility handling + // Below turfs we can see + for(var/turf/simulated/open/O in hear) + var/turf/U = GetBelow(O) + while(istype(U)) + hearturfs |= U + if(isopenspace(U)) + U = GetBelow(U) + else + U = null + + // Above us + var/above_range = range + var/turf/Ab = GetAbove(T) + while(isopenspace(Ab) && --above_range > 0) + hear |= dview(above_range,Ab,INVISIBILITY_MAXIMUM) + Ab = GetAbove(Ab) + + for(var/thing in hear) + if(istype(thing, /obj)) //Can't use isobj() because /atom/movable returns true in that + objs += thing + hearturfs |= get_turf(thing) + if(ismob(thing)) + mobs += thing + hearturfs |= get_turf(thing) + + //A list of every mob with a client + for(var/mob in player_list) + if(!ismob(mob)) + player_list -= mob + continue + //VOREStation Edit End - Trying to fix some vorestation bug. + if(get_turf(mob) in hearturfs) + mobs |= mob + continue + + var/mob/M = mob + if(M && M.stat == DEAD && remote_ghosts && !M.forbid_seeing_deadchat) + switch(type) + if(1) //Audio messages use ghost_ears + if(M.is_preference_enabled(/datum/client_preference/ghost_ears)) + mobs |= M + if(2) //Visual messages use ghost_sight + if(M.is_preference_enabled(/datum/client_preference/ghost_sight)) + mobs |= M + + //For objects below the top level who still want to hear + for(var/obj in listening_objects) + if(get_turf(obj) in hearturfs) + objs |= obj + + return list("mobs" = mobs, "objs" = objs) + +/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5) + var/turf/T + if(X1==X2) + if(Y1==Y2) + return 1 //Light cannot be blocked on same tile + else + var/s = SIGN(Y2-Y1) + Y1+=s + while(Y1!=Y2) + T=locate(X1,Y1,Z) + if(T.opacity) + return 0 + Y1+=s + else + var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1)) + var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles + var/signX = SIGN(X2-X1) + var/signY = SIGN(Y2-Y1) + if(X1 abs (dx)) //slope is above 1:1 (move horizontally in a tie) + if(dy > 0) + return get_step(start, SOUTH) + else + return get_step(start, NORTH) + else + if(dx > 0) + return get_step(start, WEST) + else + return get_step(start, EAST) + +/proc/get_mob_by_key(var/key) + for(var/mob/M in mob_list) + if(M.ckey == lowertext(key)) + return M + return null + + +// Will return a list of active candidates. It increases the buffer 5 times until it finds a candidate which is active within the buffer. +/proc/get_active_candidates(var/buffer = 1) + + var/list/candidates = list() //List of candidate KEYS to assume control of the new larva ~Carn + var/i = 0 + while(candidates.len <= 0 && i < 5) + for(var/mob/observer/dead/G in player_list) + if(((G.client.inactivity/10)/60) <= buffer + i) // the most active players are more likely to become an alien + if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) + candidates += G.key + i++ + return candidates + +// Same as above but for alien candidates. + +/proc/get_alien_candidates() + + var/list/candidates = list() //List of candidate KEYS to assume control of the new larva ~Carn + var/i = 0 + while(candidates.len <= 0 && i < 5) + for(var/mob/observer/dead/G in player_list) + if(G.client.prefs.be_special & BE_ALIEN) + if(((G.client.inactivity/10)/60) <= ALIEN_SELECT_AFK_BUFFER + i) // the most active players are more likely to become an alien + if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) + candidates += G.key + i++ + return candidates + +/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480) + if(!isobj(O)) O = new /obj/screen/text() + O.maptext = maptext + O.maptext_height = maptext_height + O.maptext_width = maptext_width + O.screen_loc = screen_loc + return O + +/proc/Show2Group4Delay(obj/O, list/group, delay=0) + if(!isobj(O)) return + if(!group) group = GLOB.clients + for(var/client/C in group) + C.screen += O + if(delay) + spawn(delay) + for(var/client/C in group) + C.screen -= O + +/datum/projectile_data + var/src_x + var/src_y + var/time + var/distance + var/power_x + var/power_y + var/dest_x + var/dest_y + +/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \ + var/power_x, var/power_y, var/dest_x, var/dest_y) + src.src_x = src_x + src.src_y = src_y + src.time = time + src.distance = distance + src.power_x = power_x + src.power_y = power_y + src.dest_x = dest_x + src.dest_y = dest_y + +/proc/projectile_trajectory(var/src_x, var/src_y, var/rotation, var/angle, var/power) + + // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle], + // rotated at [rotation] and with the power of [power] + // Thanks to VistaPOWA for this function + + var/power_x = power * cos(angle) + var/power_y = power * sin(angle) + var/time = 2* power_y / 10 //10 = g + + var/distance = time * power_x + + var/dest_x = src_x + distance*sin(rotation); + var/dest_y = src_y + distance*cos(rotation); + + return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) + +/proc/GetRedPart(const/hexa) + return hex2num(copytext(hexa,2,4)) + +/proc/GetGreenPart(const/hexa) + return hex2num(copytext(hexa,4,6)) + +/proc/GetBluePart(const/hexa) + return hex2num(copytext(hexa,6,8)) + +/proc/GetHexColors(const/hexa) + return list( + GetRedPart(hexa), + GetGreenPart(hexa), + GetBluePart(hexa) + ) + +/proc/MixColors(const/list/colors) + var/list/reds = list() + var/list/blues = list() + var/list/greens = list() + var/list/weights = list() + + for (var/i = 0, ++i <= colors.len) + reds.Add(GetRedPart(colors[i])) + blues.Add(GetBluePart(colors[i])) + greens.Add(GetGreenPart(colors[i])) + weights.Add(1) + + var/r = mixOneColor(weights, reds) + var/g = mixOneColor(weights, greens) + var/b = mixOneColor(weights, blues) + return rgb(r,g,b) + +/proc/mixOneColor(var/list/weight, var/list/color) + if (!weight || !color || length(weight)!=length(color)) + return 0 + + var/contents = length(weight) + var/i + + //normalize weights + var/listsum = 0 + for(i=1; i<=contents; i++) + listsum += weight[i] + for(i=1; i<=contents; i++) + weight[i] /= listsum + + //mix them + var/mixedcolor = 0 + for(i=1; i<=contents; i++) + mixedcolor += weight[i]*color[i] + mixedcolor = round(mixedcolor) + + //until someone writes a formal proof for this algorithm, let's keep this in +// if(mixedcolor<0x00 || mixedcolor>0xFF) +// return 0 + //that's not the kind of operation we are running here, nerd + mixedcolor=min(max(mixedcolor,0),255) + + return mixedcolor + +/** +* Gets the highest and lowest pressures from the tiles in cardinal directions +* around us, then checks the difference. +*/ +/proc/getOPressureDifferential(var/turf/loc) + var/minp=16777216; + var/maxp=0; + for(var/dir in GLOB.cardinal) + var/turf/simulated/T=get_turf(get_step(loc,dir)) + var/cp=0 + if(T && istype(T) && T.zone) + var/datum/gas_mixture/environment = T.return_air() + cp = environment.return_pressure() + else + if(istype(T,/turf/simulated)) + continue + if(cpmaxp)maxp=cp + return abs(minp-maxp) + +/proc/convert_k2c(var/temp) + return ((temp - T0C)) + +/proc/convert_c2k(var/temp) + return ((temp + T0C)) + +/proc/getCardinalAirInfo(var/turf/loc, var/list/stats=list("temperature")) + var/list/temps = new/list(4) + for(var/dir in GLOB.cardinal) + var/direction + switch(dir) + if(NORTH) + direction = 1 + if(SOUTH) + direction = 2 + if(EAST) + direction = 3 + if(WEST) + direction = 4 + var/turf/simulated/T=get_turf(get_step(loc,dir)) + var/list/rstats = new /list(stats.len) + if(T && istype(T) && T.zone) + var/datum/gas_mixture/environment = T.return_air() + for(var/i=1;i<=stats.len;i++) + if(stats[i] == "pressure") + rstats[i] = environment.return_pressure() + else + rstats[i] = environment.vars[stats[i]] + else if(istype(T, /turf/simulated)) + rstats = null // Exclude zone (wall, door, etc). + else if(istype(T, /turf)) + // Should still work. (/turf/return_air()) + var/datum/gas_mixture/environment = T.return_air() + for(var/i=1;i<=stats.len;i++) + if(stats[i] == "pressure") + rstats[i] = environment.return_pressure() + else + rstats[i] = environment.vars[stats[i]] + temps[direction] = rstats + return temps + +/proc/MinutesToTicks(var/minutes) + return SecondsToTicks(60 * minutes) + +/proc/SecondsToTicks(var/seconds) + return seconds * 10 + +/proc/window_flash(var/client_or_usr) + if (!client_or_usr) + return + winset(client_or_usr, "mainwindow", "flash=5") + +/** + * Get a bounding box of a list of atoms. + * + * Arguments: + * - atoms - List of atoms. Can accept output of view() and range() procs. + * + * Returns: list(x1, y1, x2, y2) + */ +/proc/get_bbox_of_atoms(list/atoms) + var/list/list_x = list() + var/list/list_y = list() + for(var/_a in atoms) + var/atom/a = _a + list_x += a.x + list_y += a.y + return list( + min(list_x), + min(list_y), + max(list_x), + max(list_y)) + +// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents. +// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects, +// being unable to hear people due to being in a box within a bag. + +/proc/recursive_mob_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_radio = 1) + + //GLOB.debug_mob += O.contents.len + if(!recursion_limit) + return L + for(var/atom/A in O.contents) + + if(ismob(A)) + var/mob/M = A + if(client_check && !M.client) + L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) + continue + if(sight_check && !isInSight(A, O)) + continue + L |= M + //log_world("[recursion_limit] = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") + + else if(include_radio && istype(A, /obj/item/radio)) + if(sight_check && !isInSight(A, O)) + continue + L |= A + + if(isobj(A) || ismob(A)) + L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) + return L + +// The old system would loop through lists for a total of 5000 per function call, in an empty server. +// This new system will loop at around 1000 in an empty server. + +/proc/get_mobs_in_view(var/R, var/atom/source, var/include_clientless = FALSE) + // Returns a list of mobs in range of R from source. Used in radio and say code. + + var/turf/T = get_turf(source) + var/list/hear = list() + + if(!T) + return hear + + var/list/range = hear(R, T) + + for(var/atom/A in range) + if(ismob(A)) + var/mob/M = A + if(M.client || include_clientless) + hear += M + //log_world("Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") + else if(istype(A, /obj/item/radio)) + hear += A + + if(isobj(A) || ismob(A)) + hear |= recursive_mob_check(A, hear, 3, 1, 0, 1) + + return hear + +/proc/get_belly(var/atom/A) // return a belly we're in, one way or another; and if we aren't (or are too deep to comprehend being in belly), returns null + var/atom/loc_check = A.loc + var/recursion_level = 0 + while(loc_check && !isbelly(loc_check) && !isturf(loc_check)) + if(recursion_level > 7) // abstractly picked number, but basically means we tried going 8 levels up. Something is wrong if youre THAT deep anyway + break + loc_check = loc_check.loc + recursion_level++ + if(isbelly(loc_check)) + return loc_check + return null + +/proc/get_all_prey_recursive(var/mob/living/L, var/client_check = 1) // returns all prey inside the target as well all prey of target's prey, as well as all prey inside target's prey, etc. + var/list/result = list() + + if(!istype(L) || !(L.vore_organs) || !(L.vore_organs.len)) + return result + + for(var/obj/belly/B in L.vore_organs) + for(var/mob/living/P in B.contents) + if(istype(P)) + if(client_check && P.client) + result |= P + result |= get_all_prey_recursive(P, client_check) + + return result + +/proc/random_color(saturated) //Returns a random color. If saturated is true, it will avoid pure white or pure black + var/r = rand(1,255) + var/g = rand(1,255) + var/b = rand(1,255) + + if(saturated) //Let's make sure we don't get too close to pure black or pure white, as they won't look good with grayscale sprites + if(r + g + b < 50) + r = r + rand(5,20) + g = g + rand(5,20) + b = b + rand(5,20) + else if (r + g + b > 700) + r = r - rand(5,50) + g = g - rand(5,50) + b = b - rand(5,50) + + var/color = rgb(r, g, b) + return color diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index e25c0652a92..a160069be49 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -1,329 +1,329 @@ -//Since it didn't really belong in any other category, I'm putting this here -//This is for procs to replace all the goddamn 'in world's that are chilling around the code - -var/global/list/player_list = list() //List of all mobs **with clients attached**. Excludes /mob/new_player -var/global/list/mob_list = list() //List of all mobs, including clientless -var/global/list/human_mob_list = list() //List of all human mobs and sub-types, including clientless -var/global/list/silicon_mob_list = list() //List of all silicon mobs, including clientless -var/global/list/ai_list = list() //List of all AIs, including clientless -var/global/list/living_mob_list = list() //List of all alive mobs, including clientless. Excludes /mob/new_player -var/global/list/dead_mob_list = list() //List of all dead mobs, including clientless. Excludes /mob/new_player -var/global/list/observer_mob_list = list() //List of all /mob/observer/dead, including clientless. -var/global/list/listening_objects = list() //List of all objects which care about receiving messages (communicators, radios, etc) -var/global/list/cleanbot_reserved_turfs = list() //List of all turfs currently targeted by some cleanbot - -var/global/list/cable_list = list() //Index for all cables, so that powernets don't have to look through the entire world all the time -var/global/list/landmarks_list = list() //list of all landmarks created -var/global/list/event_triggers = list() //Associative list of creator_ckey:list(landmark references) for event triggers -var/global/list/surgery_steps = list() //list of all surgery steps |BS12 -var/global/list/side_effects = list() //list of all medical sideeffects types by thier names |BS12 -var/global/list/mechas_list = list() //list of all mechs. Used by hostile mobs target tracking. -var/global/list/joblist = list() //list of all jobstypes, minus borg and AI - -#define all_genders_define_list list(MALE,FEMALE,PLURAL,NEUTER,HERM) //VOREStaton Edit -#define all_genders_text_list list("Male","Female","Plural","Neuter","Herm") //VOREStation Edit - -var/list/mannequins_ - -// Times that players are allowed to respawn ("ckey" = world.time) -GLOBAL_LIST_EMPTY(respawn_timers) - -// Holomaps -var/global/list/holomap_markers = list() -var/global/list/mapping_units = list() -var/global/list/mapping_beacons = list() - -//Preferences stuff - //Hairstyles -var/global/list/hair_styles_list = list() //stores /datum/sprite_accessory/hair indexed by name -var/global/list/hair_styles_male_list = list() -var/global/list/hair_styles_female_list = list() -var/global/list/facial_hair_styles_list = list() //stores /datum/sprite_accessory/facial_hair indexed by name -var/global/list/facial_hair_styles_male_list = list() -var/global/list/facial_hair_styles_female_list = list() -var/global/list/skin_styles_female_list = list() //unused -var/global/list/body_marking_styles_list = list() //stores /datum/sprite_accessory/marking indexed by name -var/global/list/body_marking_nopersist_list = list() // Body marking styles, minus non-genetic markings and augments -var/global/list/ear_styles_list = list() // Stores /datum/sprite_accessory/ears indexed by type -var/global/list/tail_styles_list = list() // Stores /datum/sprite_accessory/tail indexed by type -var/global/list/wing_styles_list = list() // Stores /datum/sprite_accessory/wing indexed by type - -GLOBAL_LIST_INIT(custom_species_bases, new) // Species that can be used for a Custom Species icon base - //Underwear -var/datum/category_collection/underwear/global_underwear = new() - - //Backpacks -var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Alt", "Messenger Bag", "Sports Bag", "Strapless Satchel") //VOREStation edit -var/global/list/pdachoicelist = list("Default", "Slim", "Old", "Rugged", "Holographic", "Wrist-Bound","Slider", "Vintage") -var/global/list/exclude_jobs = list(/datum/job/ai,/datum/job/cyborg) - -// Visual nets -var/list/datum/visualnet/visual_nets = list() -var/datum/visualnet/camera/cameranet = new() -var/datum/visualnet/cult/cultnet = new() - -// Runes -var/global/list/rune_list = new() -var/global/list/escape_list = list() -var/global/list/endgame_exits = list() -var/global/list/endgame_safespawns = list() - -var/global/list/syndicate_access = list(access_maint_tunnels, access_syndicate, access_external_airlocks) - -// Ores (for mining) -GLOBAL_LIST_EMPTY(ore_data) -GLOBAL_LIST_EMPTY(alloy_data) - -// Strings which corraspond to bodypart covering flags, useful for outputting what something covers. -var/global/list/string_part_flags = list( - "head" = HEAD, - "face" = FACE, - "eyes" = EYES, - "upper body" = UPPER_TORSO, - "lower body" = LOWER_TORSO, - "legs" = LEGS, - "feet" = FEET, - "arms" = ARMS, - "hands" = HANDS -) - -// Strings which corraspond to slot flags, useful for outputting what slot something is. -var/global/list/string_slot_flags = list( - "back" = SLOT_BACK, - "face" = SLOT_MASK, - "waist" = SLOT_BELT, - "ID slot" = SLOT_ID, - "ears" = SLOT_EARS, - "eyes" = SLOT_EYES, - "hands" = SLOT_GLOVES, - "head" = SLOT_HEAD, - "feet" = SLOT_FEET, - "exo slot" = SLOT_OCLOTHING, - "body" = SLOT_ICLOTHING, - "uniform" = SLOT_TIE, - "holster" = SLOT_HOLSTER -) - -GLOBAL_LIST_EMPTY(mannequins) -/proc/get_mannequin(var/ckey = "NULL") - var/mob/living/carbon/human/dummy/mannequin/M = GLOB.mannequins[ckey] - if(!istype(M)) - GLOB.mannequins[ckey] = new /mob/living/carbon/human/dummy/mannequin(null) - M = GLOB.mannequins[ckey] - return M - -/proc/del_mannequin(var/ckey = "NULL") - GLOB.mannequins-= ckey - -////////////////////////// -/////Initial Building///// -////////////////////////// - -/proc/makeDatumRefLists() - var/list/paths - - //Hair - Initialise all /datum/sprite_accessory/hair into an list indexed by hair-style name - paths = subtypesof(/datum/sprite_accessory/hair) - for(var/path in paths) - var/datum/sprite_accessory/hair/H = new path() - hair_styles_list[H.name] = H - switch(H.gender) - if(MALE) hair_styles_male_list += H.name - if(FEMALE) hair_styles_female_list += H.name - else - hair_styles_male_list += H.name - hair_styles_female_list += H.name - - //Facial Hair - Initialise all /datum/sprite_accessory/facial_hair into an list indexed by facialhair-style name - paths = subtypesof(/datum/sprite_accessory/facial_hair) - for(var/path in paths) - var/datum/sprite_accessory/facial_hair/H = new path() - facial_hair_styles_list[H.name] = H - switch(H.gender) - if(MALE) facial_hair_styles_male_list += H.name - if(FEMALE) facial_hair_styles_female_list += H.name - else - facial_hair_styles_male_list += H.name - facial_hair_styles_female_list += H.name - - //Body markings - Initialise all /datum/sprite_accessory/marking into an list indexed by marking name - paths = subtypesof(/datum/sprite_accessory/marking) - for(var/path in paths) - var/datum/sprite_accessory/marking/M = new path() - body_marking_styles_list[M.name] = M - if(!M.genetic) - body_marking_nopersist_list[M.name] = M - - //Surgery Steps - Initialize all /datum/surgery_step into a list - paths = subtypesof(/datum/surgery_step) - for(var/T in paths) - var/datum/surgery_step/S = new T - surgery_steps += S - sort_surgeries() - - //List of job. I can't believe this was calculated multiple times per tick! - paths = subtypesof(/datum/job) - paths -= exclude_jobs - for(var/T in paths) - var/datum/job/J = new T - joblist[J.title] = J - - //Languages - paths = subtypesof(/datum/language) - for(var/T in paths) - var/datum/language/L = new T - if (isnull(GLOB.all_languages[L.name])) - GLOB.all_languages[L.name] = L - else - log_debug("Language name conflict! [T] is named [L.name], but that is taken by [GLOB.all_languages[L.name].type]") - if(isnull(GLOB.language_name_conflicts[L.name])) - GLOB.language_name_conflicts[L.name] = list(GLOB.all_languages[L.name]) - GLOB.language_name_conflicts[L.name] += L - - for (var/language_name in GLOB.all_languages) - var/datum/language/L = GLOB.all_languages[language_name] - if(!(L.flags & NONGLOBAL)) - if(isnull(GLOB.language_keys[L.key])) - GLOB.language_keys[L.key] = L - else - log_debug("Language key conflict! [L] has key [L.key], but that is taken by [(GLOB.language_keys[L.key])]") - if(isnull(GLOB.language_key_conflicts[L.key])) - GLOB.language_key_conflicts[L.key] = list(GLOB.language_keys[L.key]) - GLOB.language_key_conflicts[L.key] += L - - //Species - var/rkey = 0 - paths = subtypesof(/datum/species) - for(var/T in paths) - - rkey++ - - var/datum/species/S = T - if(!initial(S.name)) - continue - - S = new T - S.race_key = rkey //Used in mob icon caching. - GLOB.all_species[S.name] = S - - //Shakey shakey shake - sortTim(GLOB.all_species, GLOBAL_PROC_REF(cmp_species), associative = TRUE) - - //Split up the rest - for(var/speciesname in GLOB.all_species) - var/datum/species/S = GLOB.all_species[speciesname] - if(!(S.spawn_flags & SPECIES_IS_RESTRICTED)) - GLOB.playable_species += S.name - if(S.spawn_flags & SPECIES_IS_WHITELISTED) - GLOB.whitelisted_species += S.name - - // Suit cyclers - paths = subtypesof(/datum/suit_cycler_choice/department) - for(var/datum/suit_cycler_choice/SCC as anything in paths) - if(!initial(SCC.name)) - continue - GLOB.suit_cycler_departments += new SCC() - paths = subtypesof(/datum/suit_cycler_choice/species) - for(var/datum/suit_cycler_choice/SCC as anything in paths) - if(!initial(SCC.name)) - continue - GLOB.suit_cycler_species += new SCC() - paths = subtypesof(/datum/suit_cycler_choice/department/emag) - for(var/datum/suit_cycler_choice/SCC as anything in paths) - if(!initial(SCC.name)) - continue - GLOB.suit_cycler_emagged += new SCC() - - //Ores - paths = subtypesof(/ore) - for(var/oretype in paths) - var/ore/OD = new oretype() - GLOB.ore_data[OD.name] = OD - - paths = subtypesof(/datum/alloy) - for(var/alloytype in paths) - GLOB.alloy_data += new alloytype() - - //Closet appearances - GLOB.closet_appearances = decls_repository.get_decls_of_type(/decl/closet_appearance) - - paths = subtypesof(/datum/sprite_accessory/ears) - for(var/path in paths) - var/obj/item/clothing/head/instance = new path() - ear_styles_list[path] = instance - - // Custom Tails - paths = subtypesof(/datum/sprite_accessory/tail) - /datum/sprite_accessory/tail/taur - for(var/path in paths) - var/datum/sprite_accessory/tail/instance = new path() - tail_styles_list[path] = instance - - // Custom Wings - paths = subtypesof(/datum/sprite_accessory/wing) - for(var/path in paths) - var/datum/sprite_accessory/wing/instance = new path() - wing_styles_list[path] = instance - - // VOREStation Add - Vore Modes! - paths = typesof(/datum/digest_mode) - for(var/T in paths) - var/datum/digest_mode/DM = new T - GLOB.digest_modes[DM.id] = DM - // VOREStation Add End - init_crafting_recipes(GLOB.crafting_recipes) - -/* - // Custom species traits - paths = subtypesof(/datum/trait) - for(var/path in paths) - var/datum/trait/instance = new path() - if(!instance.name) - continue //A prototype or something - var/cost = instance.cost - traits_costs[path] = cost - all_traits[path] = instance - switch(cost) - if(-INFINITY to -0.1) - negative_traits[path] = instance - if(0) - neutral_traits[path] = instance - if(0.1 to INFINITY) - positive_traits[path] = instance -*/ - - // Custom species icon bases - var/list/blacklisted_icons = list(SPECIES_CUSTOM,SPECIES_PROMETHEAN) //VOREStation Edit - var/list/whitelisted_icons = list(SPECIES_FENNEC,SPECIES_XENOHYBRID) //VOREStation Edit - for(var/species_name in GLOB.playable_species) - if(species_name in blacklisted_icons) - continue - var/datum/species/S = GLOB.all_species[species_name] - if(S.spawn_flags & SPECIES_IS_WHITELISTED) - continue - GLOB.custom_species_bases += species_name - for(var/species_name in whitelisted_icons) - GLOB.custom_species_bases += species_name - - return 1 // Hooks must return 1 - - -/// Inits the crafting recipe list, sorting crafting recipe requirements in the process. -/proc/init_crafting_recipes(list/crafting_recipes) - for(var/path in subtypesof(/datum/crafting_recipe)) - var/datum/crafting_recipe/recipe = new path() - recipe.reqs = sortList(recipe.reqs, GLOBAL_PROC_REF(cmp_crafting_req_priority)) - crafting_recipes += recipe - return crafting_recipes -/* // Uncomment to debug chemical reaction list. -/client/verb/debug_chemical_list() - - for (var/reaction in chemical_reactions_list) - . += "chemical_reactions_list\[\"[reaction]\"\] = \"[chemical_reactions_list[reaction]]\"\n" - if(islist(chemical_reactions_list[reaction])) - var/list/L = chemical_reactions_list[reaction] - for(var/t in L) - . += " has: [t]\n" - to_world(.) -*/ -//Hexidecimal numbers -var/global/list/hexNums = list("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F") +//Since it didn't really belong in any other category, I'm putting this here +//This is for procs to replace all the goddamn 'in world's that are chilling around the code + +var/global/list/player_list = list() //List of all mobs **with clients attached**. Excludes /mob/new_player +var/global/list/mob_list = list() //List of all mobs, including clientless +var/global/list/human_mob_list = list() //List of all human mobs and sub-types, including clientless +var/global/list/silicon_mob_list = list() //List of all silicon mobs, including clientless +var/global/list/ai_list = list() //List of all AIs, including clientless +var/global/list/living_mob_list = list() //List of all alive mobs, including clientless. Excludes /mob/new_player +var/global/list/dead_mob_list = list() //List of all dead mobs, including clientless. Excludes /mob/new_player +var/global/list/observer_mob_list = list() //List of all /mob/observer/dead, including clientless. +var/global/list/listening_objects = list() //List of all objects which care about receiving messages (communicators, radios, etc) +var/global/list/cleanbot_reserved_turfs = list() //List of all turfs currently targeted by some cleanbot + +var/global/list/cable_list = list() //Index for all cables, so that powernets don't have to look through the entire world all the time +var/global/list/landmarks_list = list() //list of all landmarks created +var/global/list/event_triggers = list() //Associative list of creator_ckey:list(landmark references) for event triggers +var/global/list/surgery_steps = list() //list of all surgery steps |BS12 +var/global/list/side_effects = list() //list of all medical sideeffects types by thier names |BS12 +var/global/list/mechas_list = list() //list of all mechs. Used by hostile mobs target tracking. +var/global/list/joblist = list() //list of all jobstypes, minus borg and AI + +#define all_genders_define_list list(MALE,FEMALE,PLURAL,NEUTER,HERM) //VOREStaton Edit +#define all_genders_text_list list("Male","Female","Plural","Neuter","Herm") //VOREStation Edit + +var/list/mannequins_ + +// Times that players are allowed to respawn ("ckey" = world.time) +GLOBAL_LIST_EMPTY(respawn_timers) + +// Holomaps +var/global/list/holomap_markers = list() +var/global/list/mapping_units = list() +var/global/list/mapping_beacons = list() + +//Preferences stuff + //Hairstyles +var/global/list/hair_styles_list = list() //stores /datum/sprite_accessory/hair indexed by name +var/global/list/hair_styles_male_list = list() +var/global/list/hair_styles_female_list = list() +var/global/list/facial_hair_styles_list = list() //stores /datum/sprite_accessory/facial_hair indexed by name +var/global/list/facial_hair_styles_male_list = list() +var/global/list/facial_hair_styles_female_list = list() +var/global/list/skin_styles_female_list = list() //unused +var/global/list/body_marking_styles_list = list() //stores /datum/sprite_accessory/marking indexed by name +var/global/list/body_marking_nopersist_list = list() // Body marking styles, minus non-genetic markings and augments +var/global/list/ear_styles_list = list() // Stores /datum/sprite_accessory/ears indexed by type +var/global/list/tail_styles_list = list() // Stores /datum/sprite_accessory/tail indexed by type +var/global/list/wing_styles_list = list() // Stores /datum/sprite_accessory/wing indexed by type + +GLOBAL_LIST_INIT(custom_species_bases, new) // Species that can be used for a Custom Species icon base + //Underwear +var/datum/category_collection/underwear/global_underwear = new() + + //Backpacks +var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Alt", "Messenger Bag", "Sports Bag", "Strapless Satchel") //VOREStation edit +var/global/list/pdachoicelist = list("Default", "Slim", "Old", "Rugged", "Holographic", "Wrist-Bound","Slider", "Vintage") +var/global/list/exclude_jobs = list(/datum/job/ai,/datum/job/cyborg) + +// Visual nets +var/list/datum/visualnet/visual_nets = list() +var/datum/visualnet/camera/cameranet = new() +var/datum/visualnet/cult/cultnet = new() + +// Runes +var/global/list/rune_list = new() +var/global/list/escape_list = list() +var/global/list/endgame_exits = list() +var/global/list/endgame_safespawns = list() + +var/global/list/syndicate_access = list(access_maint_tunnels, access_syndicate, access_external_airlocks) + +// Ores (for mining) +GLOBAL_LIST_EMPTY(ore_data) +GLOBAL_LIST_EMPTY(alloy_data) + +// Strings which corraspond to bodypart covering flags, useful for outputting what something covers. +var/global/list/string_part_flags = list( + "head" = HEAD, + "face" = FACE, + "eyes" = EYES, + "upper body" = UPPER_TORSO, + "lower body" = LOWER_TORSO, + "legs" = LEGS, + "feet" = FEET, + "arms" = ARMS, + "hands" = HANDS +) + +// Strings which corraspond to slot flags, useful for outputting what slot something is. +var/global/list/string_slot_flags = list( + "back" = SLOT_BACK, + "face" = SLOT_MASK, + "waist" = SLOT_BELT, + "ID slot" = SLOT_ID, + "ears" = SLOT_EARS, + "eyes" = SLOT_EYES, + "hands" = SLOT_GLOVES, + "head" = SLOT_HEAD, + "feet" = SLOT_FEET, + "exo slot" = SLOT_OCLOTHING, + "body" = SLOT_ICLOTHING, + "uniform" = SLOT_TIE, + "holster" = SLOT_HOLSTER +) + +GLOBAL_LIST_EMPTY(mannequins) +/proc/get_mannequin(var/ckey = "NULL") + var/mob/living/carbon/human/dummy/mannequin/M = GLOB.mannequins[ckey] + if(!istype(M)) + GLOB.mannequins[ckey] = new /mob/living/carbon/human/dummy/mannequin(null) + M = GLOB.mannequins[ckey] + return M + +/proc/del_mannequin(var/ckey = "NULL") + GLOB.mannequins-= ckey + +////////////////////////// +/////Initial Building///// +////////////////////////// + +/proc/makeDatumRefLists() + var/list/paths + + //Hair - Initialise all /datum/sprite_accessory/hair into an list indexed by hair-style name + paths = subtypesof(/datum/sprite_accessory/hair) + for(var/path in paths) + var/datum/sprite_accessory/hair/H = new path() + hair_styles_list[H.name] = H + switch(H.gender) + if(MALE) hair_styles_male_list += H.name + if(FEMALE) hair_styles_female_list += H.name + else + hair_styles_male_list += H.name + hair_styles_female_list += H.name + + //Facial Hair - Initialise all /datum/sprite_accessory/facial_hair into an list indexed by facialhair-style name + paths = subtypesof(/datum/sprite_accessory/facial_hair) + for(var/path in paths) + var/datum/sprite_accessory/facial_hair/H = new path() + facial_hair_styles_list[H.name] = H + switch(H.gender) + if(MALE) facial_hair_styles_male_list += H.name + if(FEMALE) facial_hair_styles_female_list += H.name + else + facial_hair_styles_male_list += H.name + facial_hair_styles_female_list += H.name + + //Body markings - Initialise all /datum/sprite_accessory/marking into an list indexed by marking name + paths = subtypesof(/datum/sprite_accessory/marking) + for(var/path in paths) + var/datum/sprite_accessory/marking/M = new path() + body_marking_styles_list[M.name] = M + if(!M.genetic) + body_marking_nopersist_list[M.name] = M + + //Surgery Steps - Initialize all /datum/surgery_step into a list + paths = subtypesof(/datum/surgery_step) + for(var/T in paths) + var/datum/surgery_step/S = new T + surgery_steps += S + sort_surgeries() + + //List of job. I can't believe this was calculated multiple times per tick! + paths = subtypesof(/datum/job) + paths -= exclude_jobs + for(var/T in paths) + var/datum/job/J = new T + joblist[J.title] = J + + //Languages + paths = subtypesof(/datum/language) + for(var/T in paths) + var/datum/language/L = new T + if (isnull(GLOB.all_languages[L.name])) + GLOB.all_languages[L.name] = L + else + log_debug("Language name conflict! [T] is named [L.name], but that is taken by [GLOB.all_languages[L.name].type]") + if(isnull(GLOB.language_name_conflicts[L.name])) + GLOB.language_name_conflicts[L.name] = list(GLOB.all_languages[L.name]) + GLOB.language_name_conflicts[L.name] += L + + for (var/language_name in GLOB.all_languages) + var/datum/language/L = GLOB.all_languages[language_name] + if(!(L.flags & NONGLOBAL)) + if(isnull(GLOB.language_keys[L.key])) + GLOB.language_keys[L.key] = L + else + log_debug("Language key conflict! [L] has key [L.key], but that is taken by [(GLOB.language_keys[L.key])]") + if(isnull(GLOB.language_key_conflicts[L.key])) + GLOB.language_key_conflicts[L.key] = list(GLOB.language_keys[L.key]) + GLOB.language_key_conflicts[L.key] += L + + //Species + var/rkey = 0 + paths = subtypesof(/datum/species) + for(var/T in paths) + + rkey++ + + var/datum/species/S = T + if(!initial(S.name)) + continue + + S = new T + S.race_key = rkey //Used in mob icon caching. + GLOB.all_species[S.name] = S + + //Shakey shakey shake + sortTim(GLOB.all_species, GLOBAL_PROC_REF(cmp_species), associative = TRUE) + + //Split up the rest + for(var/speciesname in GLOB.all_species) + var/datum/species/S = GLOB.all_species[speciesname] + if(!(S.spawn_flags & SPECIES_IS_RESTRICTED)) + GLOB.playable_species += S.name + if(S.spawn_flags & SPECIES_IS_WHITELISTED) + GLOB.whitelisted_species += S.name + + // Suit cyclers + paths = subtypesof(/datum/suit_cycler_choice/department) + for(var/datum/suit_cycler_choice/SCC as anything in paths) + if(!initial(SCC.name)) + continue + GLOB.suit_cycler_departments += new SCC() + paths = subtypesof(/datum/suit_cycler_choice/species) + for(var/datum/suit_cycler_choice/SCC as anything in paths) + if(!initial(SCC.name)) + continue + GLOB.suit_cycler_species += new SCC() + paths = subtypesof(/datum/suit_cycler_choice/department/emag) + for(var/datum/suit_cycler_choice/SCC as anything in paths) + if(!initial(SCC.name)) + continue + GLOB.suit_cycler_emagged += new SCC() + + //Ores + paths = subtypesof(/ore) + for(var/oretype in paths) + var/ore/OD = new oretype() + GLOB.ore_data[OD.name] = OD + + paths = subtypesof(/datum/alloy) + for(var/alloytype in paths) + GLOB.alloy_data += new alloytype() + + //Closet appearances + GLOB.closet_appearances = decls_repository.get_decls_of_type(/decl/closet_appearance) + + paths = subtypesof(/datum/sprite_accessory/ears) + for(var/path in paths) + var/obj/item/clothing/head/instance = new path() + ear_styles_list[path] = instance + + // Custom Tails + paths = subtypesof(/datum/sprite_accessory/tail) - /datum/sprite_accessory/tail/taur + for(var/path in paths) + var/datum/sprite_accessory/tail/instance = new path() + tail_styles_list[path] = instance + + // Custom Wings + paths = subtypesof(/datum/sprite_accessory/wing) + for(var/path in paths) + var/datum/sprite_accessory/wing/instance = new path() + wing_styles_list[path] = instance + + // VOREStation Add - Vore Modes! + paths = typesof(/datum/digest_mode) + for(var/T in paths) + var/datum/digest_mode/DM = new T + GLOB.digest_modes[DM.id] = DM + // VOREStation Add End + init_crafting_recipes(GLOB.crafting_recipes) + +/* + // Custom species traits + paths = subtypesof(/datum/trait) + for(var/path in paths) + var/datum/trait/instance = new path() + if(!instance.name) + continue //A prototype or something + var/cost = instance.cost + traits_costs[path] = cost + all_traits[path] = instance + switch(cost) + if(-INFINITY to -0.1) + negative_traits[path] = instance + if(0) + neutral_traits[path] = instance + if(0.1 to INFINITY) + positive_traits[path] = instance +*/ + + // Custom species icon bases + var/list/blacklisted_icons = list(SPECIES_CUSTOM,SPECIES_PROMETHEAN) //VOREStation Edit + var/list/whitelisted_icons = list(SPECIES_FENNEC,SPECIES_XENOHYBRID) //VOREStation Edit + for(var/species_name in GLOB.playable_species) + if(species_name in blacklisted_icons) + continue + var/datum/species/S = GLOB.all_species[species_name] + if(S.spawn_flags & SPECIES_IS_WHITELISTED) + continue + GLOB.custom_species_bases += species_name + for(var/species_name in whitelisted_icons) + GLOB.custom_species_bases += species_name + + return 1 // Hooks must return 1 + + +/// Inits the crafting recipe list, sorting crafting recipe requirements in the process. +/proc/init_crafting_recipes(list/crafting_recipes) + for(var/path in subtypesof(/datum/crafting_recipe)) + var/datum/crafting_recipe/recipe = new path() + recipe.reqs = sortList(recipe.reqs, GLOBAL_PROC_REF(cmp_crafting_req_priority)) + crafting_recipes += recipe + return crafting_recipes +/* // Uncomment to debug chemical reaction list. +/client/verb/debug_chemical_list() + + for (var/reaction in chemical_reactions_list) + . += "chemical_reactions_list\[\"[reaction]\"\] = \"[chemical_reactions_list[reaction]]\"\n" + if(islist(chemical_reactions_list[reaction])) + var/list/L = chemical_reactions_list[reaction] + for(var/t in L) + . += " has: [t]\n" + to_world(.) +*/ +//Hexidecimal numbers +var/global/list/hexNums = list("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F") diff --git a/code/_helpers/icons.dm b/code/_helpers/icons.dm index 47be30be4ea..9fd9e185002 100644 --- a/code/_helpers/icons.dm +++ b/code/_helpers/icons.dm @@ -1,751 +1,751 @@ -#define TO_HEX_DIGIT(n) ascii2text((n&15) + ((n&15)<10 ? 48 : 87)) - -/icon/proc/MakeLying() - var/icon/I = new(src,dir=SOUTH) - I.BecomeLying() - return I - -/icon/proc/BecomeLying() - Turn(90) - Shift(SOUTH,6) - Shift(EAST,1) - -// Multiply all alpha values by this float -/icon/proc/ChangeOpacity(opacity = 1.0) - MapColors(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,opacity, 0,0,0,0) - -// Convert to grayscale -/icon/proc/GrayScale() - MapColors(0.3,0.3,0.3, 0.59,0.59,0.59, 0.11,0.11,0.11, 0,0,0) - -/icon/proc/ColorTone(tone) - GrayScale() - - var/list/TONE = rgb2num(tone) - var/gray = round(TONE[1]*0.3 + TONE[2]*0.59 + TONE[3]*0.11, 1) - - var/icon/upper = (255-gray) ? new(src) : null - - if(gray) - MapColors(255/gray,0,0, 0,255/gray,0, 0,0,255/gray, 0,0,0) - Blend(tone, ICON_MULTIPLY) - else SetIntensity(0) - if(255-gray) - upper.Blend(rgb(gray,gray,gray), ICON_SUBTRACT) - upper.MapColors((255-TONE[1])/(255-gray),0,0,0, 0,(255-TONE[2])/(255-gray),0,0, 0,0,(255-TONE[3])/(255-gray),0, 0,0,0,0, 0,0,0,1) - Blend(upper, ICON_ADD) - -// Take the minimum color of two icons; combine transparency as if blending with ICON_ADD -/icon/proc/MinColors(icon) - var/icon/I = new(src) - I.Opaque() - I.Blend(icon, ICON_SUBTRACT) - Blend(I, ICON_SUBTRACT) - -// Take the maximum color of two icons; combine opacity as if blending with ICON_OR -/icon/proc/MaxColors(icon) - var/icon/I - if(isicon(icon)) - I = new(icon) - else - // solid color - I = new(src) - I.Blend("#000000", ICON_OVERLAY) - I.SwapColor("#000000", null) - I.Blend(icon, ICON_OVERLAY) - var/icon/J = new(src) - J.Opaque() - I.Blend(J, ICON_SUBTRACT) - Blend(I, ICON_OR) - -// make this icon fully opaque--transparent pixels become black -/icon/proc/Opaque(background = "#000000") - SwapColor(null, background) - MapColors(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,0, 0,0,0,1) - -// Change a grayscale icon into a white icon where the original color becomes the alpha -// I.e., black -> transparent, gray -> translucent white, white -> solid white -/icon/proc/BecomeAlphaMask() - SwapColor(null, "#000000ff") // don't let transparent become gray - MapColors(0,0,0,0.3, 0,0,0,0.59, 0,0,0,0.11, 0,0,0,0, 1,1,1,0) - -/icon/proc/UseAlphaMask(mask) - Opaque() - AddAlphaMask(mask) - -/icon/proc/AddAlphaMask(mask) - var/icon/M = new(mask) - M.Blend("#ffffff", ICON_SUBTRACT) - // apply mask - Blend(M, ICON_ADD) - -/proc/BlendRGB(rgb1, rgb2, amount) - var/list/RGB1 = rgb2num(rgb1) - var/list/RGB2 = rgb2num(rgb2) - - // add missing alpha if needed - if(RGB1.len < RGB2.len) RGB1 += 255 - else if(RGB2.len < RGB1.len) RGB2 += 255 - var/usealpha = RGB1.len > 3 - - var/r = round(RGB1[1] + (RGB2[1] - RGB1[1]) * amount, 1) - var/g = round(RGB1[2] + (RGB2[2] - RGB1[2]) * amount, 1) - var/b = round(RGB1[3] + (RGB2[3] - RGB1[3]) * amount, 1) - var/alpha = usealpha ? round(RGB1[4] + (RGB2[4] - RGB1[4]) * amount, 1) : null - - return isnull(alpha) ? rgb(r, g, b) : rgb(r, g, b, alpha) - -// Ported from /tg/station -// Creates a single icon from a given /atom or /image. Only the first argument is required. -/proc/getFlatIcon(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) - //Define... defines. - var/static/icon/flat_template = icon('icons/effects/effects.dmi', "nothing") - - #define BLANK icon(flat_template) - #define SET_SELF(SETVAR) do { \ - var/icon/SELF_ICON=icon(icon(curicon, curstate, base_icon_dir),"",SOUTH,no_anim?1:null); \ - if(A.alpha<255) { \ - SELF_ICON.Blend(rgb(255,255,255,A.alpha),ICON_MULTIPLY);\ - } \ - if(A.color) { \ - if(islist(A.color)){ \ - SELF_ICON.MapColors(arglist(A.color))} \ - else{ \ - SELF_ICON.Blend(A.color,ICON_MULTIPLY)} \ - } \ - ##SETVAR=SELF_ICON;\ - } while (0) - #define INDEX_X_LOW 1 - #define INDEX_X_HIGH 2 - #define INDEX_Y_LOW 3 - #define INDEX_Y_HIGH 4 - - #define flatX1 flat_size[INDEX_X_LOW] - #define flatX2 flat_size[INDEX_X_HIGH] - #define flatY1 flat_size[INDEX_Y_LOW] - #define flatY2 flat_size[INDEX_Y_HIGH] - #define addX1 add_size[INDEX_X_LOW] - #define addX2 add_size[INDEX_X_HIGH] - #define addY1 add_size[INDEX_Y_LOW] - #define addY2 add_size[INDEX_Y_HIGH] - - if(!A || A.alpha <= 0) - return BLANK - - var/noIcon = FALSE - if(start) - if(!defdir) - defdir = A.dir - if(!deficon) - deficon = A.icon - if(!defstate) - defstate = A.icon_state - if(!defblend) - defblend = A.blend_mode - - var/curicon = A.icon || deficon - var/curstate = A.icon_state || defstate - - if(!((noIcon = (!curicon)))) - var/curstates = cached_icon_states(curicon) - if(!(curstate in curstates)) - if("" in curstates) - curstate = "" - else - noIcon = TRUE // Do not render this object. - - var/curdir - var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have - - // Use the requested dir or the atom's current dir - curdir = defdir || A.dir - - //Try to remove/optimize this section ASAP, CPU hog. //Slightly mitigated by implementing caching using cached_icon_states - //Determines if there's directionals. - if(!noIcon && curdir != SOUTH) - var/exist = FALSE - var/static/list/checkdirs = list(NORTH, EAST, WEST) - for(var/i in checkdirs) //Not using GLOB for a reason. - if(length(cached_icon_states(icon(curicon, curstate, i)))) - exist = TRUE - break - if(!exist) - base_icon_dir = SOUTH - // - - if(!base_icon_dir) - base_icon_dir = curdir - - ASSERT(!BLEND_DEFAULT) //I might just be stupid but lets make sure this define is 0. - - var/curblend = A.blend_mode || defblend - - if(A.overlays.len || A.underlays.len) - var/icon/flat = BLANK - // Layers will be a sorted list of icons/overlays, based on the order in which they are displayed - var/list/layers = list() - var/image/copy - // Add the atom's icon itself, without pixel_x/y offsets. - if(!noIcon) - copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=base_icon_dir) - copy.color = A.color - copy.alpha = A.alpha - copy.blend_mode = curblend - layers[copy] = A.layer - - // Loop through the underlays, then overlays, sorting them into the layers list - for(var/process_set in 0 to 1) - var/list/process = process_set? A.overlays : A.underlays - for(var/i in 1 to process.len) - var/image/current = process[i] - if(!current) - continue - if(current.plane != FLOAT_PLANE && current.plane != A.plane) - continue - var/current_layer = current.layer - if(current_layer < 0) - //if(current_layer <= -1000) - //return flat - current_layer = process_set + A.layer + current_layer / 1000 - - for(var/p in 1 to layers.len) - var/image/cmp = layers[p] - if(current_layer < layers[cmp]) - layers.Insert(p, current) - break - layers[current] = current_layer - - //sortTim(layers, GLOBAL_PROC_REF(cmp_image_layer_asc)) - - var/icon/add // Icon of overlay being added - - // Current dimensions of flattened icon - var/list/flat_size = list(1, flat.Width(), 1, flat.Height()) - // Dimensions of overlay being added - var/list/add_size[4] - - for(var/V in layers) - var/image/I = V - if(I.alpha == 0) - continue - - if(I == copy) // 'I' is an /image based on the object being flattened. - curblend = BLEND_OVERLAY - add = icon(I.icon, I.icon_state, base_icon_dir) - else // 'I' is an appearance object. - add = getFlatIcon(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim) - if(!add) - continue - // Find the new dimensions of the flat icon to fit the added overlay - add_size = list( - min(flatX1, I.pixel_x+1), - max(flatX2, I.pixel_x+add.Width()), - min(flatY1, I.pixel_y+1), - max(flatY2, I.pixel_y+add.Height()) - ) - - if(flat_size ~! add_size) - // Resize the flattened icon so the new icon fits - flat.Crop( - addX1 - flatX1 + 1, - addY1 - flatY1 + 1, - addX2 - flatX1 + 1, - addY2 - flatY1 + 1 - ) - flat_size = add_size.Copy() - - // Blend the overlay into the flattened icon - flat.Blend(add, blendMode2iconMode(curblend), I.pixel_x + 2 - flatX1, I.pixel_y + 2 - flatY1) - - if(A.color) - if(islist(A.color)) - flat.MapColors(arglist(A.color)) - else - flat.Blend(A.color, ICON_MULTIPLY) - - if(A.alpha < 255) - flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) - - if(no_anim) - //Clean up repeated frames - var/icon/cleaned = new /icon() - cleaned.Insert(flat, "", SOUTH, 1, 0) - . = cleaned - else - . = icon(flat, "", SOUTH) - else //There's no overlays. - if(!noIcon) - SET_SELF(.) - - //Clear defines - #undef flatX1 - #undef flatX2 - #undef flatY1 - #undef flatY2 - #undef addX1 - #undef addX2 - #undef addY1 - #undef addY2 - - #undef INDEX_X_LOW - #undef INDEX_X_HIGH - #undef INDEX_Y_LOW - #undef INDEX_Y_HIGH - - #undef BLANK - #undef SET_SELF - -/proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N - var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A. - for(var/I in A.overlays)//For every image in overlays. var/image/I will not work, don't try it. - if(I:layer>A.layer) continue//If layer is greater than what we need, skip it. - var/icon/image_overlay = new(I:icon,I:icon_state)//Blend only works with icon objects. - //Also, icons cannot directly set icon_state. Slower than changing variables but whatever. - alpha_mask.Blend(image_overlay,ICON_OR)//OR so they are lumped together in a nice overlay. - return alpha_mask//And now return the mask. - -//getFlatIcon but generates an icon that can face ALL four directions. The only four. -/proc/getCompoundIcon(atom/A) - var/icon/north = getFlatIcon(A,defdir=NORTH) - var/icon/south = getFlatIcon(A,defdir=SOUTH) - var/icon/east = getFlatIcon(A,defdir=EAST) - var/icon/west = getFlatIcon(A,defdir=WEST) - - //Starts with a blank icon because of byond bugs. - var/icon/full = icon('icons/effects/effects.dmi', "icon_state"="nothing") - - full.Insert(north,dir=NORTH) - full.Insert(south,dir=SOUTH) - full.Insert(east,dir=EAST) - full.Insert(west,dir=WEST) - qdel(north) - qdel(south) - qdel(east) - qdel(west) - return full - -/proc/downloadImage(atom/A, dir) - var/icon/this_icon = getFlatIcon(A,defdir=dir) - - usr << ftp(this_icon,"[A.name].png") - -/mob/proc/AddCamoOverlay(atom/A)//A is the atom which we are using as the overlay. - var/icon/opacity_icon = new(A.icon, A.icon_state)//Don't really care for overlays/underlays. - //Now we need to culculate overlays+underlays and add them together to form an image for a mask. - //var/icon/alpha_mask = getFlatIcon(src)//Accurate but SLOW. Not designed for running each tick. Could have other uses I guess. - var/icon/alpha_mask = getIconMask(src)//Which is why I created that proc. Also a little slow since it's blending a bunch of icons together but good enough. - opacity_icon.AddAlphaMask(alpha_mask)//Likely the main source of lag for this proc. Probably not designed to run each tick. - opacity_icon.ChangeOpacity(0.4)//Front end for MapColors so it's fast. 0.5 means half opacity and looks the best in my opinion. - for(var/i=0,i<5,i++)//And now we add it as overlays. It's faster than creating an icon and then merging it. - var/image/I = image("icon" = opacity_icon, "icon_state" = A.icon_state, "layer" = layer+0.8)//So it's above other stuff but below weapons and the like. - switch(i)//Now to determine offset so the result is somewhat blurred. - if(1) I.pixel_x-- - if(2) I.pixel_x++ - if(3) I.pixel_y-- - if(4) I.pixel_y++ - overlays += I//And finally add the overlay. - -/proc/getHologramIcon(icon/A, safety=1, no_color = FALSE)//If safety is on, a new icon is not created. - var/icon/flat_icon = safety ? A : new(A)//Has to be a new icon to not constantly change the same icon. - /* VOREStation Removal - For AI Vore effects - if(!no_color) - flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish. - flat_icon.ChangeOpacity(0.5)//Make it half transparent. - */ //VOREStation Removal End - var/icon/alpha_mask = new('icons/effects/effects.dmi', "scanline")//Scanline effect. - flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. - return flat_icon - -//For photo camera. -/proc/build_composite_icon(atom/A) - var/icon/composite = icon(A.icon, A.icon_state, A.dir, 1) - for(var/O in A.overlays) - var/image/I = O - composite.Blend(icon(I.icon, I.icon_state, I.dir, 1), ICON_OVERLAY) - return composite - -GLOBAL_LIST_EMPTY(icon_state_lists) -/proc/cached_icon_states(var/icon/I) - if(!I) - return list() - var/key = I - var/returnlist = GLOB.icon_state_lists[key] - if(!returnlist) - returnlist = icon_states(I) - if(isfile(I)) // It's something that will stick around - GLOB.icon_state_lists[key] = returnlist - return returnlist - -/proc/expire_states_cache(var/key) - if(GLOB.icon_state_lists[key]) - GLOB.icon_state_lists -= key - return TRUE - return FALSE - -GLOBAL_LIST_EMPTY(cached_examine_icons) -/proc/set_cached_examine_icon(var/atom/A, var/icon/I, var/expiry = 12000) - GLOB.cached_examine_icons[WEAKREF(A)] = I - if(expiry) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(uncache_examine_icon), WEAKREF(A)), expiry, TIMER_UNIQUE) - -/proc/get_cached_examine_icon(var/atom/A) - var/datum/weakref/WR = WEAKREF(A) - return GLOB.cached_examine_icons[WR] - -/proc/uncache_examine_icon(var/datum/weakref/WR) - GLOB.cached_examine_icons -= WR - -/proc/adjust_brightness(var/color, var/value) - if (!color) return "#FFFFFF" - if (!value) return color - - var/list/RGB = rgb2num(color) - RGB[1] = CLAMP(RGB[1]+value,0,255) - RGB[2] = CLAMP(RGB[2]+value,0,255) - RGB[3] = CLAMP(RGB[3]+value,0,255) - return rgb(RGB[1],RGB[2],RGB[3]) - -/proc/sort_atoms_by_layer(var/list/atoms) - // Comb sort icons based on levels - var/list/result = atoms.Copy() - var/gap = result.len - var/swapped = 1 - while (gap > 1 || swapped) - swapped = 0 - if(gap > 1) - gap = round(gap / 1.3) // 1.3 is the emperic comb sort coefficient - if(gap < 1) - gap = 1 - for(var/i = 1; gap + i <= result.len; i++) - var/atom/l = result[i] //Fucking hate - var/atom/r = result[gap+i] //how lists work here - if(l.layer > r.layer) //no "result[i].layer" for me - result.Swap(i, gap + i) - swapped = 1 - return result - -/proc/gen_hud_image(var/file, var/person, var/state, var/plane) - var/image/img = image(file, person, state) - img.plane = plane //Thanks Byond. - img.layer = MOB_LAYER-0.2 - img.appearance_flags = APPEARANCE_UI - return img - -/** -* Animate a 'halo' around an object. -* -* This proc is not exactly cheap. You'd be well advised to set up many-loops rather than call this super-often. getCompoundIcon is -* mostly to blame for this. If Byond ever implements a way to get something's icon more 'gently' than this, do that instead. -* -* @param A This is the atom to put the halo on -* @param simple_icons If set to TRUE, will just perform a very basic icon and icon_state steal. DO USE when possible. -* @param color This is the color for the halo -* @param anim_duration This decides how fast (or slow) the animation plays -* @param offset Mysterious variable that determines size of the halo's gap from icon -* @param loops How many times the animation loops -* @param grow_to Relative to the size of the icon, how big the halo grows while fading (don't use negatives for inward halos, use < 1) -* @param pixel_scale If you'd like the halo to use pixel scale or the default 'fuzzy' scale -*/ -/proc/animate_aura(var/atom/A, var/simple_icons, var/color = "#00FF22", var/anim_duration = 5, var/offset = 1, var/loops = 1, var/grow_to = 2, var/pixel_scale = FALSE) - ASSERT(A) - - //Take a guess at this, if they didn't set it - if(isnull(simple_icons)) - if(ismob(A)) - simple_icons = FALSE - else - simple_icons = TRUE - - //Get their icon - var/icon/hole - - if(simple_icons) - hole = icon(A.icon, A.icon_state) - else - hole = getCompoundIcon(A) - - hole.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) //White. - - //Make a bigger version - var/icon/grower = new(hole) - var/orig_width = grower.Width() - var/orig_height = grower.Height() - var/end_width = orig_width+(offset*2) - var/end_height = orig_height+(offset*2) - var/half_diff_width = (end_width-orig_width)*0.5 - var/half_diff_height = (end_height-orig_height)*0.5 - - //Make icon black - grower.SwapColor("#FFFFFF","#000000") //Black. - - //Scale both icons big so we don't have to deal with low-pixel garbage issues - grower.Scale(orig_width*10,orig_height*10) - hole.Scale(orig_width*9,orig_height*9) - - //Blend the hole in - grower.Blend(hole,ICON_OVERLAY, x = ((orig_width*10-orig_width*9)*0.5)+1, y = ((orig_height*10-orig_height*9)*0.5)+1) - - //Swap white to zero alpha - grower.SwapColor("#FFFFFF","#00000000") - - //Color it - grower.SwapColor("#000000",color) - - //Scale it to final height - grower.Scale(end_width,end_height) - - //Flick it onto them - var/image/img = image(grower,A) - if(pixel_scale) - img.appearance_flags |= PIXEL_SCALE - img.pixel_x = half_diff_width*-1 - img.pixel_y = half_diff_height*-1 - flick_overlay_view(img, A, anim_duration*loops, TRUE) - - //Animate it growing - animate(img, alpha = 0, transform = matrix()*grow_to, time = anim_duration, loop = loops) - -/// generates a filename for a given asset. -/// like generate_asset_name(), except returns the rsc reference and the rsc file hash as well as the asset name (sans extension) -/// used so that certain asset files dont have to be hashed twice -/proc/generate_and_hash_rsc_file(file, dmi_file_path) - var/rsc_ref = fcopy_rsc(file) - var/hash - //if we have a valid dmi file path we can trust md5'ing the rsc file because we know it doesnt have the bug described in http://www.byond.com/forum/post/2611357 - if(dmi_file_path) - hash = md5(rsc_ref) - else //otherwise, we need to do the expensive fcopy() workaround - hash = md5asfile(rsc_ref) - - return list(rsc_ref, hash, "asset.[hash]") - -/// Gets a dummy savefile for usage in icon generation. -/// Savefiles generated from this proc will be empty. -/proc/get_dummy_savefile(from_failure = FALSE) - var/static/next_id = 0 - if(next_id++ > 9) - next_id = 0 - var/savefile_path = "tmp/dummy-save-[next_id].sav" - try - if(fexists(savefile_path)) - fdel(savefile_path) - return new /savefile(savefile_path) - catch(var/exception/error) - // if we failed to create a dummy once, try again; maybe someone slept somewhere they shouldnt have - if(from_failure) // this *is* the retry, something fucked up - CRASH("get_dummy_savefile failed to create a dummy savefile: '[error]'") - return get_dummy_savefile(from_failure = TRUE) - -/** - * Converts an icon to base64. Operates by putting the icon in the iconCache savefile, - * exporting it as text, and then parsing the base64 from that. - * (This relies on byond automatically storing icons in savefiles as base64) - */ -/proc/icon2base64(icon/icon) - if (!isicon(icon)) - return FALSE - var/savefile/dummySave = get_dummy_savefile() - WRITE_FILE(dummySave["dummy"], icon) - var/iconData = dummySave.ExportText("dummy") - var/list/partial = splittext(iconData, "{") - return replacetext(copytext_char(partial[2], 3, -5), "\n", "") //if cleanup fails we want to still return the correct base64 - -///given a text string, returns whether it is a valid dmi icons folder path -/proc/is_valid_dmi_file(icon_path) - if(!istext(icon_path) || !length(icon_path)) - return FALSE - - var/is_in_icon_folder = findtextEx(icon_path, "icons/") - var/is_dmi_file = findtextEx(icon_path, ".dmi") - - if(is_in_icon_folder && is_dmi_file) - return TRUE - return FALSE - -/// given an icon object, dmi file path, or atom/image/mutable_appearance, attempts to find and return an associated dmi file path. -/// a weird quirk about dm is that /icon objects represent both compile-time or dynamic icons in the rsc, -/// but stringifying rsc references returns a dmi file path -/// ONLY if that icon represents a completely unchanged dmi file from when the game was compiled. -/// so if the given object is associated with an icon that was in the rsc when the game was compiled, this returns a path. otherwise it returns "" -/proc/get_icon_dmi_path(icon/icon) - /// the dmi file path we attempt to return if the given object argument is associated with a stringifiable icon - /// if successful, this looks like "icons/path/to/dmi_file.dmi" - var/icon_path = "" - - if(isatom(icon) || istype(icon, /image) || istype(icon, /mutable_appearance)) - var/atom/atom_icon = icon - icon = atom_icon.icon - //atom icons compiled in from 'icons/path/to/dmi_file.dmi' are weird and not really icon objects that you generate with icon(). - //if theyre unchanged dmi's then they're stringifiable to "icons/path/to/dmi_file.dmi" - - if(isicon(icon) && isfile(icon)) - //icons compiled in from 'icons/path/to/dmi_file.dmi' at compile time are weird and arent really /icon objects, - ///but they pass both isicon() and isfile() checks. theyre the easiest case since stringifying them gives us the path we want - var/icon_ref = "\ref[icon]" - var/locate_icon_string = "[locate(icon_ref)]" - - icon_path = locate_icon_string - - else if(isicon(icon) && "[icon]" == "/icon") - // icon objects generated from icon() at runtime are icons, but they ARENT files themselves, they represent icon files. - // if the files they represent are compile time dmi files in the rsc, then - // the rsc reference returned by fcopy_rsc() will be stringifiable to "icons/path/to/dmi_file.dmi" - var/rsc_ref = fcopy_rsc(icon) - - var/icon_ref = "\ref[rsc_ref]" - - var/icon_path_string = "[locate(icon_ref)]" - - icon_path = icon_path_string - - else if(istext(icon)) - var/rsc_ref = fcopy_rsc(icon) - //if its the text path of an existing dmi file, the rsc reference returned by fcopy_rsc() will be stringifiable to a dmi path - - var/rsc_ref_ref = "\ref[rsc_ref]" - var/rsc_ref_string = "[locate(rsc_ref_ref)]" - - icon_path = rsc_ref_string - - if(is_valid_dmi_file(icon_path)) - return icon_path - - return FALSE - -/** - * generate an asset for the given icon or the icon of the given appearance for [thing], and send it to any clients in target. - * Arguments: - * * thing - either a /icon object, or an object that has an appearance (atom, image, mutable_appearance). - * * target - either a reference to or a list of references to /client's or mobs with clients - * * icon_state - string to force a particular icon_state for the icon to be used - * * dir - dir number to force a particular direction for the icon to be used - * * frame - what frame of the icon_state's animation for the icon being used - * * moving - whether or not to use a moving state for the given icon - * * sourceonly - if TRUE, only generate the asset and send back the asset url, instead of tags that display the icon to players - * * extra_clases - string of extra css classes to use when returning the icon string - */ -/proc/icon2html(atom/thing, client/target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE, extra_classes = null) - if (!thing) - return - //if(SSlag_switch.measures[DISABLE_USR_ICON2HTML] && usr && !HAS_TRAIT(usr, TRAIT_BYPASS_MEASURES)) - //return - - var/key - var/icon/icon2collapse = thing - - if (!target) - return - if (target == world) - target = GLOB.clients - - var/list/targets - if (!islist(target)) - targets = list(target) - else - targets = target - if(!length(targets)) - return - - //check if the given object is associated with a dmi file in the icons folder. if it is then we dont need to do a lot of work - //for asset generation to get around byond limitations - var/icon_path = get_icon_dmi_path(thing) - - if (!isicon(icon2collapse)) - if (isfile(thing)) //special snowflake - //var/name = SANITIZE_FILENAME("[generate_asset_name(thing)].png") - var/name = "[generate_asset_name(thing)].png" - if (!SSassets.cache[name]) - register_asset(name, thing) - for (var/thing2 in targets) - send_asset(thing2, name) - if(sourceonly) - return get_asset_url(name) - return "" - - //its either an atom, image, or mutable_appearance, we want its icon var - icon2collapse = thing.icon - - if (isnull(icon_state)) - icon_state = thing.icon_state - //Despite casting to atom, this code path supports mutable appearances, so let's be nice to them - //if(isnull(icon_state) || (isatom(thing) && thing.flags_1 & HTML_USE_INITAL_ICON_1)) - if(isnull(icon_state) || isatom(thing)) - icon_state = initial(thing.icon_state) - if (isnull(dir)) - dir = initial(thing.dir) - - if (isnull(dir)) - dir = thing.dir - - if (ishuman(thing)) // Shitty workaround for a BYOND issue. - var/icon/temp = icon2collapse - icon2collapse = icon() - icon2collapse.Insert(temp, dir = SOUTH) - dir = SOUTH - else - if (isnull(dir)) - dir = SOUTH - if (isnull(icon_state)) - icon_state = "" - - icon2collapse = icon(icon2collapse, icon_state, dir, frame, moving) - - var/list/name_and_ref = generate_and_hash_rsc_file(icon2collapse, icon_path)//pretend that tuples exist - - var/rsc_ref = name_and_ref[1] //weird object thats not even readable to the debugger, represents a reference to the icons rsc entry - var/file_hash = name_and_ref[2] - key = "[name_and_ref[3]].png" - - if(!SSassets.cache[key]) - register_asset(key, rsc_ref, file_hash, icon_path) - for (var/client_target in targets) - send_asset(client_target, key) - if(sourceonly) - return get_asset_url(key) - return "" - -/proc/icon2base64html(target, var/custom_classes = "") - if (!target) - return - var/static/list/bicon_cache = list() - if (isicon(target)) - var/icon/target_icon = target - var/icon_base64 = icon2base64(target_icon) - - if (target_icon.Height() > world.icon_size || target_icon.Width() > world.icon_size) - var/icon_md5 = md5(icon_base64) - icon_base64 = bicon_cache[icon_md5] - if (!icon_base64) // Doesn't exist yet, make it. - bicon_cache[icon_md5] = icon_base64 = icon2base64(target_icon) - - - return "" - - // Either an atom or somebody fucked up and is gonna get a runtime, which I'm fine with. - var/atom/target_atom = target - var/key = "[istype(target_atom.icon, /icon) ? "[REF(target_atom.icon)]" : target_atom.icon]:[target_atom.icon_state]" - - - if (!bicon_cache[key]) // Doesn't exist, make it. - var/icon/target_icon = icon(target_atom.icon, target_atom.icon_state, SOUTH, 1) - if (ishuman(target)) // Shitty workaround for a BYOND issue. - var/icon/temp = target_icon - target_icon = icon() - target_icon.Insert(temp, dir = SOUTH) - - bicon_cache[key] = icon2base64(target_icon) - - return "" - -//Costlier version of icon2html() that uses getFlatIcon() to account for overlays, underlays, etc. Use with extreme moderation, ESPECIALLY on mobs. -/proc/costly_icon2html(thing, target, sourceonly = FALSE) - if (!thing) - return - //if(SSlag_switch.measures[DISABLE_USR_ICON2HTML] && usr && !HAS_TRAIT(usr, TRAIT_BYPASS_MEASURES)) - //return - - if (isicon(thing)) - return icon2html(thing, target) - - var/icon/I = getFlatIcon(thing) - return icon2html(I, target, sourceonly = sourceonly) +#define TO_HEX_DIGIT(n) ascii2text((n&15) + ((n&15)<10 ? 48 : 87)) + +/icon/proc/MakeLying() + var/icon/I = new(src,dir=SOUTH) + I.BecomeLying() + return I + +/icon/proc/BecomeLying() + Turn(90) + Shift(SOUTH,6) + Shift(EAST,1) + +// Multiply all alpha values by this float +/icon/proc/ChangeOpacity(opacity = 1.0) + MapColors(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,opacity, 0,0,0,0) + +// Convert to grayscale +/icon/proc/GrayScale() + MapColors(0.3,0.3,0.3, 0.59,0.59,0.59, 0.11,0.11,0.11, 0,0,0) + +/icon/proc/ColorTone(tone) + GrayScale() + + var/list/TONE = rgb2num(tone) + var/gray = round(TONE[1]*0.3 + TONE[2]*0.59 + TONE[3]*0.11, 1) + + var/icon/upper = (255-gray) ? new(src) : null + + if(gray) + MapColors(255/gray,0,0, 0,255/gray,0, 0,0,255/gray, 0,0,0) + Blend(tone, ICON_MULTIPLY) + else SetIntensity(0) + if(255-gray) + upper.Blend(rgb(gray,gray,gray), ICON_SUBTRACT) + upper.MapColors((255-TONE[1])/(255-gray),0,0,0, 0,(255-TONE[2])/(255-gray),0,0, 0,0,(255-TONE[3])/(255-gray),0, 0,0,0,0, 0,0,0,1) + Blend(upper, ICON_ADD) + +// Take the minimum color of two icons; combine transparency as if blending with ICON_ADD +/icon/proc/MinColors(icon) + var/icon/I = new(src) + I.Opaque() + I.Blend(icon, ICON_SUBTRACT) + Blend(I, ICON_SUBTRACT) + +// Take the maximum color of two icons; combine opacity as if blending with ICON_OR +/icon/proc/MaxColors(icon) + var/icon/I + if(isicon(icon)) + I = new(icon) + else + // solid color + I = new(src) + I.Blend("#000000", ICON_OVERLAY) + I.SwapColor("#000000", null) + I.Blend(icon, ICON_OVERLAY) + var/icon/J = new(src) + J.Opaque() + I.Blend(J, ICON_SUBTRACT) + Blend(I, ICON_OR) + +// make this icon fully opaque--transparent pixels become black +/icon/proc/Opaque(background = "#000000") + SwapColor(null, background) + MapColors(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,0, 0,0,0,1) + +// Change a grayscale icon into a white icon where the original color becomes the alpha +// I.e., black -> transparent, gray -> translucent white, white -> solid white +/icon/proc/BecomeAlphaMask() + SwapColor(null, "#000000ff") // don't let transparent become gray + MapColors(0,0,0,0.3, 0,0,0,0.59, 0,0,0,0.11, 0,0,0,0, 1,1,1,0) + +/icon/proc/UseAlphaMask(mask) + Opaque() + AddAlphaMask(mask) + +/icon/proc/AddAlphaMask(mask) + var/icon/M = new(mask) + M.Blend("#ffffff", ICON_SUBTRACT) + // apply mask + Blend(M, ICON_ADD) + +/proc/BlendRGB(rgb1, rgb2, amount) + var/list/RGB1 = rgb2num(rgb1) + var/list/RGB2 = rgb2num(rgb2) + + // add missing alpha if needed + if(RGB1.len < RGB2.len) RGB1 += 255 + else if(RGB2.len < RGB1.len) RGB2 += 255 + var/usealpha = RGB1.len > 3 + + var/r = round(RGB1[1] + (RGB2[1] - RGB1[1]) * amount, 1) + var/g = round(RGB1[2] + (RGB2[2] - RGB1[2]) * amount, 1) + var/b = round(RGB1[3] + (RGB2[3] - RGB1[3]) * amount, 1) + var/alpha = usealpha ? round(RGB1[4] + (RGB2[4] - RGB1[4]) * amount, 1) : null + + return isnull(alpha) ? rgb(r, g, b) : rgb(r, g, b, alpha) + +// Ported from /tg/station +// Creates a single icon from a given /atom or /image. Only the first argument is required. +/proc/getFlatIcon(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) + //Define... defines. + var/static/icon/flat_template = icon('icons/effects/effects.dmi', "nothing") + + #define BLANK icon(flat_template) + #define SET_SELF(SETVAR) do { \ + var/icon/SELF_ICON=icon(icon(curicon, curstate, base_icon_dir),"",SOUTH,no_anim?1:null); \ + if(A.alpha<255) { \ + SELF_ICON.Blend(rgb(255,255,255,A.alpha),ICON_MULTIPLY);\ + } \ + if(A.color) { \ + if(islist(A.color)){ \ + SELF_ICON.MapColors(arglist(A.color))} \ + else{ \ + SELF_ICON.Blend(A.color,ICON_MULTIPLY)} \ + } \ + ##SETVAR=SELF_ICON;\ + } while (0) + #define INDEX_X_LOW 1 + #define INDEX_X_HIGH 2 + #define INDEX_Y_LOW 3 + #define INDEX_Y_HIGH 4 + + #define flatX1 flat_size[INDEX_X_LOW] + #define flatX2 flat_size[INDEX_X_HIGH] + #define flatY1 flat_size[INDEX_Y_LOW] + #define flatY2 flat_size[INDEX_Y_HIGH] + #define addX1 add_size[INDEX_X_LOW] + #define addX2 add_size[INDEX_X_HIGH] + #define addY1 add_size[INDEX_Y_LOW] + #define addY2 add_size[INDEX_Y_HIGH] + + if(!A || A.alpha <= 0) + return BLANK + + var/noIcon = FALSE + if(start) + if(!defdir) + defdir = A.dir + if(!deficon) + deficon = A.icon + if(!defstate) + defstate = A.icon_state + if(!defblend) + defblend = A.blend_mode + + var/curicon = A.icon || deficon + var/curstate = A.icon_state || defstate + + if(!((noIcon = (!curicon)))) + var/curstates = cached_icon_states(curicon) + if(!(curstate in curstates)) + if("" in curstates) + curstate = "" + else + noIcon = TRUE // Do not render this object. + + var/curdir + var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have + + // Use the requested dir or the atom's current dir + curdir = defdir || A.dir + + //Try to remove/optimize this section ASAP, CPU hog. //Slightly mitigated by implementing caching using cached_icon_states + //Determines if there's directionals. + if(!noIcon && curdir != SOUTH) + var/exist = FALSE + var/static/list/checkdirs = list(NORTH, EAST, WEST) + for(var/i in checkdirs) //Not using GLOB for a reason. + if(length(cached_icon_states(icon(curicon, curstate, i)))) + exist = TRUE + break + if(!exist) + base_icon_dir = SOUTH + // + + if(!base_icon_dir) + base_icon_dir = curdir + + ASSERT(!BLEND_DEFAULT) //I might just be stupid but lets make sure this define is 0. + + var/curblend = A.blend_mode || defblend + + if(A.overlays.len || A.underlays.len) + var/icon/flat = BLANK + // Layers will be a sorted list of icons/overlays, based on the order in which they are displayed + var/list/layers = list() + var/image/copy + // Add the atom's icon itself, without pixel_x/y offsets. + if(!noIcon) + copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=base_icon_dir) + copy.color = A.color + copy.alpha = A.alpha + copy.blend_mode = curblend + layers[copy] = A.layer + + // Loop through the underlays, then overlays, sorting them into the layers list + for(var/process_set in 0 to 1) + var/list/process = process_set? A.overlays : A.underlays + for(var/i in 1 to process.len) + var/image/current = process[i] + if(!current) + continue + if(current.plane != FLOAT_PLANE && current.plane != A.plane) + continue + var/current_layer = current.layer + if(current_layer < 0) + //if(current_layer <= -1000) + //return flat + current_layer = process_set + A.layer + current_layer / 1000 + + for(var/p in 1 to layers.len) + var/image/cmp = layers[p] + if(current_layer < layers[cmp]) + layers.Insert(p, current) + break + layers[current] = current_layer + + //sortTim(layers, GLOBAL_PROC_REF(cmp_image_layer_asc)) + + var/icon/add // Icon of overlay being added + + // Current dimensions of flattened icon + var/list/flat_size = list(1, flat.Width(), 1, flat.Height()) + // Dimensions of overlay being added + var/list/add_size[4] + + for(var/V in layers) + var/image/I = V + if(I.alpha == 0) + continue + + if(I == copy) // 'I' is an /image based on the object being flattened. + curblend = BLEND_OVERLAY + add = icon(I.icon, I.icon_state, base_icon_dir) + else // 'I' is an appearance object. + add = getFlatIcon(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim) + if(!add) + continue + // Find the new dimensions of the flat icon to fit the added overlay + add_size = list( + min(flatX1, I.pixel_x+1), + max(flatX2, I.pixel_x+add.Width()), + min(flatY1, I.pixel_y+1), + max(flatY2, I.pixel_y+add.Height()) + ) + + if(flat_size ~! add_size) + // Resize the flattened icon so the new icon fits + flat.Crop( + addX1 - flatX1 + 1, + addY1 - flatY1 + 1, + addX2 - flatX1 + 1, + addY2 - flatY1 + 1 + ) + flat_size = add_size.Copy() + + // Blend the overlay into the flattened icon + flat.Blend(add, blendMode2iconMode(curblend), I.pixel_x + 2 - flatX1, I.pixel_y + 2 - flatY1) + + if(A.color) + if(islist(A.color)) + flat.MapColors(arglist(A.color)) + else + flat.Blend(A.color, ICON_MULTIPLY) + + if(A.alpha < 255) + flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) + + if(no_anim) + //Clean up repeated frames + var/icon/cleaned = new /icon() + cleaned.Insert(flat, "", SOUTH, 1, 0) + . = cleaned + else + . = icon(flat, "", SOUTH) + else //There's no overlays. + if(!noIcon) + SET_SELF(.) + + //Clear defines + #undef flatX1 + #undef flatX2 + #undef flatY1 + #undef flatY2 + #undef addX1 + #undef addX2 + #undef addY1 + #undef addY2 + + #undef INDEX_X_LOW + #undef INDEX_X_HIGH + #undef INDEX_Y_LOW + #undef INDEX_Y_HIGH + + #undef BLANK + #undef SET_SELF + +/proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N + var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A. + for(var/I in A.overlays)//For every image in overlays. var/image/I will not work, don't try it. + if(I:layer>A.layer) continue//If layer is greater than what we need, skip it. + var/icon/image_overlay = new(I:icon,I:icon_state)//Blend only works with icon objects. + //Also, icons cannot directly set icon_state. Slower than changing variables but whatever. + alpha_mask.Blend(image_overlay,ICON_OR)//OR so they are lumped together in a nice overlay. + return alpha_mask//And now return the mask. + +//getFlatIcon but generates an icon that can face ALL four directions. The only four. +/proc/getCompoundIcon(atom/A) + var/icon/north = getFlatIcon(A,defdir=NORTH) + var/icon/south = getFlatIcon(A,defdir=SOUTH) + var/icon/east = getFlatIcon(A,defdir=EAST) + var/icon/west = getFlatIcon(A,defdir=WEST) + + //Starts with a blank icon because of byond bugs. + var/icon/full = icon('icons/effects/effects.dmi', "icon_state"="nothing") + + full.Insert(north,dir=NORTH) + full.Insert(south,dir=SOUTH) + full.Insert(east,dir=EAST) + full.Insert(west,dir=WEST) + qdel(north) + qdel(south) + qdel(east) + qdel(west) + return full + +/proc/downloadImage(atom/A, dir) + var/icon/this_icon = getFlatIcon(A,defdir=dir) + + usr << ftp(this_icon,"[A.name].png") + +/mob/proc/AddCamoOverlay(atom/A)//A is the atom which we are using as the overlay. + var/icon/opacity_icon = new(A.icon, A.icon_state)//Don't really care for overlays/underlays. + //Now we need to culculate overlays+underlays and add them together to form an image for a mask. + //var/icon/alpha_mask = getFlatIcon(src)//Accurate but SLOW. Not designed for running each tick. Could have other uses I guess. + var/icon/alpha_mask = getIconMask(src)//Which is why I created that proc. Also a little slow since it's blending a bunch of icons together but good enough. + opacity_icon.AddAlphaMask(alpha_mask)//Likely the main source of lag for this proc. Probably not designed to run each tick. + opacity_icon.ChangeOpacity(0.4)//Front end for MapColors so it's fast. 0.5 means half opacity and looks the best in my opinion. + for(var/i=0,i<5,i++)//And now we add it as overlays. It's faster than creating an icon and then merging it. + var/image/I = image("icon" = opacity_icon, "icon_state" = A.icon_state, "layer" = layer+0.8)//So it's above other stuff but below weapons and the like. + switch(i)//Now to determine offset so the result is somewhat blurred. + if(1) I.pixel_x-- + if(2) I.pixel_x++ + if(3) I.pixel_y-- + if(4) I.pixel_y++ + overlays += I//And finally add the overlay. + +/proc/getHologramIcon(icon/A, safety=1, no_color = FALSE)//If safety is on, a new icon is not created. + var/icon/flat_icon = safety ? A : new(A)//Has to be a new icon to not constantly change the same icon. + /* VOREStation Removal - For AI Vore effects + if(!no_color) + flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish. + flat_icon.ChangeOpacity(0.5)//Make it half transparent. + */ //VOREStation Removal End + var/icon/alpha_mask = new('icons/effects/effects.dmi', "scanline")//Scanline effect. + flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. + return flat_icon + +//For photo camera. +/proc/build_composite_icon(atom/A) + var/icon/composite = icon(A.icon, A.icon_state, A.dir, 1) + for(var/O in A.overlays) + var/image/I = O + composite.Blend(icon(I.icon, I.icon_state, I.dir, 1), ICON_OVERLAY) + return composite + +GLOBAL_LIST_EMPTY(icon_state_lists) +/proc/cached_icon_states(var/icon/I) + if(!I) + return list() + var/key = I + var/returnlist = GLOB.icon_state_lists[key] + if(!returnlist) + returnlist = icon_states(I) + if(isfile(I)) // It's something that will stick around + GLOB.icon_state_lists[key] = returnlist + return returnlist + +/proc/expire_states_cache(var/key) + if(GLOB.icon_state_lists[key]) + GLOB.icon_state_lists -= key + return TRUE + return FALSE + +GLOBAL_LIST_EMPTY(cached_examine_icons) +/proc/set_cached_examine_icon(var/atom/A, var/icon/I, var/expiry = 12000) + GLOB.cached_examine_icons[WEAKREF(A)] = I + if(expiry) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(uncache_examine_icon), WEAKREF(A)), expiry, TIMER_UNIQUE) + +/proc/get_cached_examine_icon(var/atom/A) + var/datum/weakref/WR = WEAKREF(A) + return GLOB.cached_examine_icons[WR] + +/proc/uncache_examine_icon(var/datum/weakref/WR) + GLOB.cached_examine_icons -= WR + +/proc/adjust_brightness(var/color, var/value) + if (!color) return "#FFFFFF" + if (!value) return color + + var/list/RGB = rgb2num(color) + RGB[1] = CLAMP(RGB[1]+value,0,255) + RGB[2] = CLAMP(RGB[2]+value,0,255) + RGB[3] = CLAMP(RGB[3]+value,0,255) + return rgb(RGB[1],RGB[2],RGB[3]) + +/proc/sort_atoms_by_layer(var/list/atoms) + // Comb sort icons based on levels + var/list/result = atoms.Copy() + var/gap = result.len + var/swapped = 1 + while (gap > 1 || swapped) + swapped = 0 + if(gap > 1) + gap = round(gap / 1.3) // 1.3 is the emperic comb sort coefficient + if(gap < 1) + gap = 1 + for(var/i = 1; gap + i <= result.len; i++) + var/atom/l = result[i] //Fucking hate + var/atom/r = result[gap+i] //how lists work here + if(l.layer > r.layer) //no "result[i].layer" for me + result.Swap(i, gap + i) + swapped = 1 + return result + +/proc/gen_hud_image(var/file, var/person, var/state, var/plane) + var/image/img = image(file, person, state) + img.plane = plane //Thanks Byond. + img.layer = MOB_LAYER-0.2 + img.appearance_flags = APPEARANCE_UI + return img + +/** +* Animate a 'halo' around an object. +* +* This proc is not exactly cheap. You'd be well advised to set up many-loops rather than call this super-often. getCompoundIcon is +* mostly to blame for this. If Byond ever implements a way to get something's icon more 'gently' than this, do that instead. +* +* @param A This is the atom to put the halo on +* @param simple_icons If set to TRUE, will just perform a very basic icon and icon_state steal. DO USE when possible. +* @param color This is the color for the halo +* @param anim_duration This decides how fast (or slow) the animation plays +* @param offset Mysterious variable that determines size of the halo's gap from icon +* @param loops How many times the animation loops +* @param grow_to Relative to the size of the icon, how big the halo grows while fading (don't use negatives for inward halos, use < 1) +* @param pixel_scale If you'd like the halo to use pixel scale or the default 'fuzzy' scale +*/ +/proc/animate_aura(var/atom/A, var/simple_icons, var/color = "#00FF22", var/anim_duration = 5, var/offset = 1, var/loops = 1, var/grow_to = 2, var/pixel_scale = FALSE) + ASSERT(A) + + //Take a guess at this, if they didn't set it + if(isnull(simple_icons)) + if(ismob(A)) + simple_icons = FALSE + else + simple_icons = TRUE + + //Get their icon + var/icon/hole + + if(simple_icons) + hole = icon(A.icon, A.icon_state) + else + hole = getCompoundIcon(A) + + hole.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) //White. + + //Make a bigger version + var/icon/grower = new(hole) + var/orig_width = grower.Width() + var/orig_height = grower.Height() + var/end_width = orig_width+(offset*2) + var/end_height = orig_height+(offset*2) + var/half_diff_width = (end_width-orig_width)*0.5 + var/half_diff_height = (end_height-orig_height)*0.5 + + //Make icon black + grower.SwapColor("#FFFFFF","#000000") //Black. + + //Scale both icons big so we don't have to deal with low-pixel garbage issues + grower.Scale(orig_width*10,orig_height*10) + hole.Scale(orig_width*9,orig_height*9) + + //Blend the hole in + grower.Blend(hole,ICON_OVERLAY, x = ((orig_width*10-orig_width*9)*0.5)+1, y = ((orig_height*10-orig_height*9)*0.5)+1) + + //Swap white to zero alpha + grower.SwapColor("#FFFFFF","#00000000") + + //Color it + grower.SwapColor("#000000",color) + + //Scale it to final height + grower.Scale(end_width,end_height) + + //Flick it onto them + var/image/img = image(grower,A) + if(pixel_scale) + img.appearance_flags |= PIXEL_SCALE + img.pixel_x = half_diff_width*-1 + img.pixel_y = half_diff_height*-1 + flick_overlay_view(img, A, anim_duration*loops, TRUE) + + //Animate it growing + animate(img, alpha = 0, transform = matrix()*grow_to, time = anim_duration, loop = loops) + +/// generates a filename for a given asset. +/// like generate_asset_name(), except returns the rsc reference and the rsc file hash as well as the asset name (sans extension) +/// used so that certain asset files dont have to be hashed twice +/proc/generate_and_hash_rsc_file(file, dmi_file_path) + var/rsc_ref = fcopy_rsc(file) + var/hash + //if we have a valid dmi file path we can trust md5'ing the rsc file because we know it doesnt have the bug described in http://www.byond.com/forum/post/2611357 + if(dmi_file_path) + hash = md5(rsc_ref) + else //otherwise, we need to do the expensive fcopy() workaround + hash = md5asfile(rsc_ref) + + return list(rsc_ref, hash, "asset.[hash]") + +/// Gets a dummy savefile for usage in icon generation. +/// Savefiles generated from this proc will be empty. +/proc/get_dummy_savefile(from_failure = FALSE) + var/static/next_id = 0 + if(next_id++ > 9) + next_id = 0 + var/savefile_path = "tmp/dummy-save-[next_id].sav" + try + if(fexists(savefile_path)) + fdel(savefile_path) + return new /savefile(savefile_path) + catch(var/exception/error) + // if we failed to create a dummy once, try again; maybe someone slept somewhere they shouldnt have + if(from_failure) // this *is* the retry, something fucked up + CRASH("get_dummy_savefile failed to create a dummy savefile: '[error]'") + return get_dummy_savefile(from_failure = TRUE) + +/** + * Converts an icon to base64. Operates by putting the icon in the iconCache savefile, + * exporting it as text, and then parsing the base64 from that. + * (This relies on byond automatically storing icons in savefiles as base64) + */ +/proc/icon2base64(icon/icon) + if (!isicon(icon)) + return FALSE + var/savefile/dummySave = get_dummy_savefile() + WRITE_FILE(dummySave["dummy"], icon) + var/iconData = dummySave.ExportText("dummy") + var/list/partial = splittext(iconData, "{") + return replacetext(copytext_char(partial[2], 3, -5), "\n", "") //if cleanup fails we want to still return the correct base64 + +///given a text string, returns whether it is a valid dmi icons folder path +/proc/is_valid_dmi_file(icon_path) + if(!istext(icon_path) || !length(icon_path)) + return FALSE + + var/is_in_icon_folder = findtextEx(icon_path, "icons/") + var/is_dmi_file = findtextEx(icon_path, ".dmi") + + if(is_in_icon_folder && is_dmi_file) + return TRUE + return FALSE + +/// given an icon object, dmi file path, or atom/image/mutable_appearance, attempts to find and return an associated dmi file path. +/// a weird quirk about dm is that /icon objects represent both compile-time or dynamic icons in the rsc, +/// but stringifying rsc references returns a dmi file path +/// ONLY if that icon represents a completely unchanged dmi file from when the game was compiled. +/// so if the given object is associated with an icon that was in the rsc when the game was compiled, this returns a path. otherwise it returns "" +/proc/get_icon_dmi_path(icon/icon) + /// the dmi file path we attempt to return if the given object argument is associated with a stringifiable icon + /// if successful, this looks like "icons/path/to/dmi_file.dmi" + var/icon_path = "" + + if(isatom(icon) || istype(icon, /image) || istype(icon, /mutable_appearance)) + var/atom/atom_icon = icon + icon = atom_icon.icon + //atom icons compiled in from 'icons/path/to/dmi_file.dmi' are weird and not really icon objects that you generate with icon(). + //if theyre unchanged dmi's then they're stringifiable to "icons/path/to/dmi_file.dmi" + + if(isicon(icon) && isfile(icon)) + //icons compiled in from 'icons/path/to/dmi_file.dmi' at compile time are weird and arent really /icon objects, + ///but they pass both isicon() and isfile() checks. theyre the easiest case since stringifying them gives us the path we want + var/icon_ref = "\ref[icon]" + var/locate_icon_string = "[locate(icon_ref)]" + + icon_path = locate_icon_string + + else if(isicon(icon) && "[icon]" == "/icon") + // icon objects generated from icon() at runtime are icons, but they ARENT files themselves, they represent icon files. + // if the files they represent are compile time dmi files in the rsc, then + // the rsc reference returned by fcopy_rsc() will be stringifiable to "icons/path/to/dmi_file.dmi" + var/rsc_ref = fcopy_rsc(icon) + + var/icon_ref = "\ref[rsc_ref]" + + var/icon_path_string = "[locate(icon_ref)]" + + icon_path = icon_path_string + + else if(istext(icon)) + var/rsc_ref = fcopy_rsc(icon) + //if its the text path of an existing dmi file, the rsc reference returned by fcopy_rsc() will be stringifiable to a dmi path + + var/rsc_ref_ref = "\ref[rsc_ref]" + var/rsc_ref_string = "[locate(rsc_ref_ref)]" + + icon_path = rsc_ref_string + + if(is_valid_dmi_file(icon_path)) + return icon_path + + return FALSE + +/** + * generate an asset for the given icon or the icon of the given appearance for [thing], and send it to any clients in target. + * Arguments: + * * thing - either a /icon object, or an object that has an appearance (atom, image, mutable_appearance). + * * target - either a reference to or a list of references to /client's or mobs with clients + * * icon_state - string to force a particular icon_state for the icon to be used + * * dir - dir number to force a particular direction for the icon to be used + * * frame - what frame of the icon_state's animation for the icon being used + * * moving - whether or not to use a moving state for the given icon + * * sourceonly - if TRUE, only generate the asset and send back the asset url, instead of tags that display the icon to players + * * extra_clases - string of extra css classes to use when returning the icon string + */ +/proc/icon2html(atom/thing, client/target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE, extra_classes = null) + if (!thing) + return + //if(SSlag_switch.measures[DISABLE_USR_ICON2HTML] && usr && !HAS_TRAIT(usr, TRAIT_BYPASS_MEASURES)) + //return + + var/key + var/icon/icon2collapse = thing + + if (!target) + return + if (target == world) + target = GLOB.clients + + var/list/targets + if (!islist(target)) + targets = list(target) + else + targets = target + if(!length(targets)) + return + + //check if the given object is associated with a dmi file in the icons folder. if it is then we dont need to do a lot of work + //for asset generation to get around byond limitations + var/icon_path = get_icon_dmi_path(thing) + + if (!isicon(icon2collapse)) + if (isfile(thing)) //special snowflake + //var/name = SANITIZE_FILENAME("[generate_asset_name(thing)].png") + var/name = "[generate_asset_name(thing)].png" + if (!SSassets.cache[name]) + register_asset(name, thing) + for (var/thing2 in targets) + send_asset(thing2, name) + if(sourceonly) + return get_asset_url(name) + return "" + + //its either an atom, image, or mutable_appearance, we want its icon var + icon2collapse = thing.icon + + if (isnull(icon_state)) + icon_state = thing.icon_state + //Despite casting to atom, this code path supports mutable appearances, so let's be nice to them + //if(isnull(icon_state) || (isatom(thing) && thing.flags_1 & HTML_USE_INITAL_ICON_1)) + if(isnull(icon_state) || isatom(thing)) + icon_state = initial(thing.icon_state) + if (isnull(dir)) + dir = initial(thing.dir) + + if (isnull(dir)) + dir = thing.dir + + if (ishuman(thing)) // Shitty workaround for a BYOND issue. + var/icon/temp = icon2collapse + icon2collapse = icon() + icon2collapse.Insert(temp, dir = SOUTH) + dir = SOUTH + else + if (isnull(dir)) + dir = SOUTH + if (isnull(icon_state)) + icon_state = "" + + icon2collapse = icon(icon2collapse, icon_state, dir, frame, moving) + + var/list/name_and_ref = generate_and_hash_rsc_file(icon2collapse, icon_path)//pretend that tuples exist + + var/rsc_ref = name_and_ref[1] //weird object thats not even readable to the debugger, represents a reference to the icons rsc entry + var/file_hash = name_and_ref[2] + key = "[name_and_ref[3]].png" + + if(!SSassets.cache[key]) + register_asset(key, rsc_ref, file_hash, icon_path) + for (var/client_target in targets) + send_asset(client_target, key) + if(sourceonly) + return get_asset_url(key) + return "" + +/proc/icon2base64html(target, var/custom_classes = "") + if (!target) + return + var/static/list/bicon_cache = list() + if (isicon(target)) + var/icon/target_icon = target + var/icon_base64 = icon2base64(target_icon) + + if (target_icon.Height() > world.icon_size || target_icon.Width() > world.icon_size) + var/icon_md5 = md5(icon_base64) + icon_base64 = bicon_cache[icon_md5] + if (!icon_base64) // Doesn't exist yet, make it. + bicon_cache[icon_md5] = icon_base64 = icon2base64(target_icon) + + + return "" + + // Either an atom or somebody fucked up and is gonna get a runtime, which I'm fine with. + var/atom/target_atom = target + var/key = "[istype(target_atom.icon, /icon) ? "[REF(target_atom.icon)]" : target_atom.icon]:[target_atom.icon_state]" + + + if (!bicon_cache[key]) // Doesn't exist, make it. + var/icon/target_icon = icon(target_atom.icon, target_atom.icon_state, SOUTH, 1) + if (ishuman(target)) // Shitty workaround for a BYOND issue. + var/icon/temp = target_icon + target_icon = icon() + target_icon.Insert(temp, dir = SOUTH) + + bicon_cache[key] = icon2base64(target_icon) + + return "" + +//Costlier version of icon2html() that uses getFlatIcon() to account for overlays, underlays, etc. Use with extreme moderation, ESPECIALLY on mobs. +/proc/costly_icon2html(thing, target, sourceonly = FALSE) + if (!thing) + return + //if(SSlag_switch.measures[DISABLE_USR_ICON2HTML] && usr && !HAS_TRAIT(usr, TRAIT_BYPASS_MEASURES)) + //return + + if (isicon(thing)) + return icon2html(thing, target) + + var/icon/I = getFlatIcon(thing) + return icon2html(I, target, sourceonly = sourceonly) diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm index fb1ba7fa077..d5e9bd841c7 100644 --- a/code/_helpers/logging.dm +++ b/code/_helpers/logging.dm @@ -1,297 +1,297 @@ -//print an error message to world.log - -//This is an external call, "true" and "false" are how rust parses out booleans -#define WRITE_LOG(log, text) rustg_log_write(log, text, "true") -#define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false") - -/* For logging round startup. */ -/proc/start_log(log) - WRITE_LOG(log, "START: Starting up [log_path].") - return log - -/* Close open log handles. This should be called as late as possible, and no logging should hapen after. */ -/proc/shutdown_logging() - rustg_log_close_all() - -/proc/error(msg) - to_world_log("## ERROR: [msg]") - -#define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [src] usr: [usr].") -//print a warning message to world.log -/proc/warning(msg) - to_world_log("## WARNING: [msg]") - -//print a testing-mode debug message to world.log -/proc/testing(msg) - to_world_log("## TESTING: [msg]") - -/proc/log_admin(text) - admin_log.Add(text) - if (config.log_admin) - WRITE_LOG(diary, "ADMIN: [text]") - -/proc/log_adminpm(text, client/source, client/dest) - admin_log.Add(text) - if (config.log_admin) - WRITE_LOG(diary, "ADMINPM: [key_name(source)]->[key_name(dest)]: [html_decode(text)]") - -/proc/log_pray(text, client/source) - admin_log.Add(text) - if (config.log_admin) - WRITE_LOG(diary, "PRAY: [key_name(source)]: [text]") - -/proc/log_debug(text) - if (config.log_debug) - WRITE_LOG(debug_log, "DEBUG: [sanitize(text)]") - - for(var/client/C in GLOB.admins) - if(C.is_preference_enabled(/datum/client_preference/debug/show_debug_logs)) - to_chat(C, - type = MESSAGE_TYPE_DEBUG, - html = "DEBUG: [text]") - -/proc/log_game(text) - if (config.log_game) - WRITE_LOG(diary, "GAME: [text]") - -/proc/log_vote(text) - if (config.log_vote) - WRITE_LOG(diary, "VOTE: [text]") - -/proc/log_access_in(client/new_client) - if (config.log_access) - var/message = "[key_name(new_client)] - IP:[new_client.address] - CID:[new_client.computer_id] - BYOND v[new_client.byond_version]" - WRITE_LOG(diary, "ACCESS IN: [message]") //VOREStation Edit - -/proc/log_access_out(mob/last_mob) - if (config.log_access) - var/message = "[key_name(last_mob)] - IP:[last_mob.lastKnownIP] - CID:Logged Out - BYOND Logged Out" - WRITE_LOG(diary, "ACCESS OUT: [message]") - -/proc/log_say(text, mob/speaker) - if (config.log_say) - WRITE_LOG(diary, "SAY: [speaker.simple_info_line()]: [html_decode(text)]") - - //Log the message to in-game dialogue logs, as well. - if(speaker.client) - speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" - GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" - -/proc/log_ooc(text, client/user) - if (config.log_ooc) - WRITE_LOG(diary, "OOC: [user.simple_info_line()]: [html_decode(text)]") - - GLOB.round_text_log += "([time_stamp()]) ([user]) OOC: - [text]" - -/proc/log_aooc(text, client/user) - if (config.log_ooc) - WRITE_LOG(diary, "AOOC: [user.simple_info_line()]: [html_decode(text)]") - - GLOB.round_text_log += "([time_stamp()]) ([user]) AOOC: - [text]" - -/proc/log_looc(text, client/user) - if (config.log_ooc) - WRITE_LOG(diary, "LOOC: [user.simple_info_line()]: [html_decode(text)]") - - GLOB.round_text_log += "([time_stamp()]) ([user]) LOOC: - [text]" - -/proc/log_whisper(text, mob/speaker) - if (config.log_whisper) - WRITE_LOG(diary, "WHISPER: [speaker.simple_info_line()]: [html_decode(text)]") - - if(speaker.client) - speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" - GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" - -/proc/log_emote(text, mob/speaker) - if (config.log_emote) - WRITE_LOG(diary, "EMOTE: [speaker.simple_info_line()]: [html_decode(text)]") - - if(speaker.client) - speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) EMOTE: - [text]" - GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) EMOTE: - [text]" - -/proc/log_attack(attacker, defender, message) - if (config.log_attack) - WRITE_LOG(diary, "ATTACK: [attacker] against [defender]: [message]") - -/proc/log_adminsay(text, mob/speaker) - if (config.log_adminchat) - WRITE_LOG(diary, "ADMINSAY: [speaker.simple_info_line()]: [html_decode(text)]") - -/proc/log_modsay(text, mob/speaker) - if (config.log_adminchat) - WRITE_LOG(diary, "MODSAY: [speaker.simple_info_line()]: [html_decode(text)]") - -/proc/log_eventsay(text, mob/speaker) - if (config.log_adminchat) - WRITE_LOG(diary, "EVENTSAY: [speaker.simple_info_line()]: [html_decode(text)]") - -/proc/log_ghostsay(text, mob/speaker) - if (config.log_say) - WRITE_LOG(diary, "DEADCHAT: [speaker.simple_info_line()]: [html_decode(text)]") - - speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) DEADSAY: - [text]" - GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) DEADSAY: - [text]" - -/proc/log_ghostemote(text, mob/speaker) - if (config.log_emote) - WRITE_LOG(diary, "DEADEMOTE: [speaker.simple_info_line()]: [html_decode(text)]") - -/proc/log_adminwarn(text) - if (config.log_adminwarn) - WRITE_LOG(diary, "ADMINWARN: [html_decode(text)]") - -/proc/log_pda(text, mob/speaker) - if (config.log_pda) - WRITE_LOG(diary, "PDA: [speaker.simple_info_line()]: [html_decode(text)]") - - speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) MSG: - [text]" - GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) MSG: - [text]" - -/proc/log_to_dd(text) - to_world_log(text) //this comes before the config check because it can't possibly runtime - if(config.log_world_output) - WRITE_LOG(diary, "DD_OUTPUT: [text]") - -/proc/log_error(text) - to_world_log(text) - WRITE_LOG(error_log, "RUNTIME: [text]") - -/proc/log_misc(text) - WRITE_LOG(diary, "MISC: [text]") - -/proc/log_topic(text) - if(Debug2) - WRITE_LOG(diary, "TOPIC: [text]") - -/proc/log_unit_test(text) - to_world_log("## UNIT_TEST: [text]") - -#ifdef REFERENCE_TRACKING_LOG -#define log_reftracker(msg) log_world("## REF SEARCH [msg]") -#else -#define log_reftracker(msg) -#endif - -/proc/log_asset(text) - WRITE_LOG(diary, "ASSET: [text]") - -/proc/report_progress(var/progress_message) - admin_notice("[progress_message]", R_DEBUG) - to_world_log(progress_message) - -//pretty print a direction bitflag, can be useful for debugging. -/proc/print_dir(var/dir) - var/list/comps = list() - if(dir & NORTH) comps += "NORTH" - if(dir & SOUTH) comps += "SOUTH" - if(dir & EAST) comps += "EAST" - if(dir & WEST) comps += "WEST" - if(dir & UP) comps += "UP" - if(dir & DOWN) comps += "DOWN" - - return english_list(comps, nothing_text="0", and_text="|", comma_text="|") - -//more or less a logging utility -//Always return "Something/(Something)", even if it's an error message. -/proc/key_name(var/whom, var/include_link = FALSE, var/include_name = TRUE, var/highlight_special_characters = TRUE) - var/mob/M - var/client/C - var/key - - if(!whom) - return "INVALID/INVALID" - if(istype(whom, /client)) - C = whom - M = C.mob - key = C.key - else if(ismob(whom)) - M = whom - C = M.client - key = M.key - else if(istype(whom, /datum/mind)) - var/datum/mind/D = whom - key = D.key - M = D.current - if(D.current) - C = D.current.client - else if(istype(whom, /datum)) - var/datum/D = whom - return "INVALID/([D.type])" - else if(istext(whom)) - return "AUTOMATED/[whom]" //Just give them the text back - else - return "INVALID/INVALID" - - . = "" - - if(key) - if(include_link && C) - . += "" - - if(C && C.holder && C.holder.fakekey) - . += "Administrator" - else - . += key - - if(include_link) - if(C) . += "" - else . += " (DC)" - else - . += "INVALID" - - if(include_name) - var/name = "INVALID" - if(M) - if(M.real_name) - name = M.real_name - else if(M.name) - name = M.name - - if(include_link && is_special_character(M) && highlight_special_characters) - name = "[name]" //Orange - - . += "/([name])" - - return . - -/proc/key_name_admin(var/whom, var/include_name = 1) - return key_name(whom, 1, include_name) - -// Helper procs for building detailed log lines -// -// These procs must not fail under ANY CIRCUMSTANCES! -// - -/datum/proc/log_info_line() - return "[src] ([type])" - -/atom/log_info_line() - . = ..() - var/turf/t = get_turf(src) - if(istype(t)) - return "[.] @ [t.log_info_line()]" - else if(loc) - return "[.] @ ([loc]) (0,0,0) ([loc.type])" - else - return "[.] @ (NULL) (0,0,0) (NULL)" - -/turf/log_info_line() - return "([src]) ([x],[y],[z]) ([type])" - -/mob/log_info_line() - return "[..()] (ckey=[ckey])" - -/proc/log_info_line(var/datum/d) - if(!d) - return "*null*" - if(!istype(d)) - return json_encode(d) - return d.log_info_line() - -/mob/proc/simple_info_line() - return "[key_name(src)] ([x],[y],[z])" - -/client/proc/simple_info_line() - return "[key_name(src)] ([mob.x],[mob.y],[mob.z])" +//print an error message to world.log + +//This is an external call, "true" and "false" are how rust parses out booleans +#define WRITE_LOG(log, text) rustg_log_write(log, text, "true") +#define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false") + +/* For logging round startup. */ +/proc/start_log(log) + WRITE_LOG(log, "START: Starting up [log_path].") + return log + +/* Close open log handles. This should be called as late as possible, and no logging should hapen after. */ +/proc/shutdown_logging() + rustg_log_close_all() + +/proc/error(msg) + to_world_log("## ERROR: [msg]") + +#define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [src] usr: [usr].") +//print a warning message to world.log +/proc/warning(msg) + to_world_log("## WARNING: [msg]") + +//print a testing-mode debug message to world.log +/proc/testing(msg) + to_world_log("## TESTING: [msg]") + +/proc/log_admin(text) + admin_log.Add(text) + if (config.log_admin) + WRITE_LOG(diary, "ADMIN: [text]") + +/proc/log_adminpm(text, client/source, client/dest) + admin_log.Add(text) + if (config.log_admin) + WRITE_LOG(diary, "ADMINPM: [key_name(source)]->[key_name(dest)]: [html_decode(text)]") + +/proc/log_pray(text, client/source) + admin_log.Add(text) + if (config.log_admin) + WRITE_LOG(diary, "PRAY: [key_name(source)]: [text]") + +/proc/log_debug(text) + if (config.log_debug) + WRITE_LOG(debug_log, "DEBUG: [sanitize(text)]") + + for(var/client/C in GLOB.admins) + if(C.is_preference_enabled(/datum/client_preference/debug/show_debug_logs)) + to_chat(C, + type = MESSAGE_TYPE_DEBUG, + html = "DEBUG: [text]") + +/proc/log_game(text) + if (config.log_game) + WRITE_LOG(diary, "GAME: [text]") + +/proc/log_vote(text) + if (config.log_vote) + WRITE_LOG(diary, "VOTE: [text]") + +/proc/log_access_in(client/new_client) + if (config.log_access) + var/message = "[key_name(new_client)] - IP:[new_client.address] - CID:[new_client.computer_id] - BYOND v[new_client.byond_version]" + WRITE_LOG(diary, "ACCESS IN: [message]") //VOREStation Edit + +/proc/log_access_out(mob/last_mob) + if (config.log_access) + var/message = "[key_name(last_mob)] - IP:[last_mob.lastKnownIP] - CID:Logged Out - BYOND Logged Out" + WRITE_LOG(diary, "ACCESS OUT: [message]") + +/proc/log_say(text, mob/speaker) + if (config.log_say) + WRITE_LOG(diary, "SAY: [speaker.simple_info_line()]: [html_decode(text)]") + + //Log the message to in-game dialogue logs, as well. + if(speaker.client) + speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" + GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" + +/proc/log_ooc(text, client/user) + if (config.log_ooc) + WRITE_LOG(diary, "OOC: [user.simple_info_line()]: [html_decode(text)]") + + GLOB.round_text_log += "([time_stamp()]) ([user]) OOC: - [text]" + +/proc/log_aooc(text, client/user) + if (config.log_ooc) + WRITE_LOG(diary, "AOOC: [user.simple_info_line()]: [html_decode(text)]") + + GLOB.round_text_log += "([time_stamp()]) ([user]) AOOC: - [text]" + +/proc/log_looc(text, client/user) + if (config.log_ooc) + WRITE_LOG(diary, "LOOC: [user.simple_info_line()]: [html_decode(text)]") + + GLOB.round_text_log += "([time_stamp()]) ([user]) LOOC: - [text]" + +/proc/log_whisper(text, mob/speaker) + if (config.log_whisper) + WRITE_LOG(diary, "WHISPER: [speaker.simple_info_line()]: [html_decode(text)]") + + if(speaker.client) + speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" + GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" + +/proc/log_emote(text, mob/speaker) + if (config.log_emote) + WRITE_LOG(diary, "EMOTE: [speaker.simple_info_line()]: [html_decode(text)]") + + if(speaker.client) + speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) EMOTE: - [text]" + GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) EMOTE: - [text]" + +/proc/log_attack(attacker, defender, message) + if (config.log_attack) + WRITE_LOG(diary, "ATTACK: [attacker] against [defender]: [message]") + +/proc/log_adminsay(text, mob/speaker) + if (config.log_adminchat) + WRITE_LOG(diary, "ADMINSAY: [speaker.simple_info_line()]: [html_decode(text)]") + +/proc/log_modsay(text, mob/speaker) + if (config.log_adminchat) + WRITE_LOG(diary, "MODSAY: [speaker.simple_info_line()]: [html_decode(text)]") + +/proc/log_eventsay(text, mob/speaker) + if (config.log_adminchat) + WRITE_LOG(diary, "EVENTSAY: [speaker.simple_info_line()]: [html_decode(text)]") + +/proc/log_ghostsay(text, mob/speaker) + if (config.log_say) + WRITE_LOG(diary, "DEADCHAT: [speaker.simple_info_line()]: [html_decode(text)]") + + speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) DEADSAY: - [text]" + GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) DEADSAY: - [text]" + +/proc/log_ghostemote(text, mob/speaker) + if (config.log_emote) + WRITE_LOG(diary, "DEADEMOTE: [speaker.simple_info_line()]: [html_decode(text)]") + +/proc/log_adminwarn(text) + if (config.log_adminwarn) + WRITE_LOG(diary, "ADMINWARN: [html_decode(text)]") + +/proc/log_pda(text, mob/speaker) + if (config.log_pda) + WRITE_LOG(diary, "PDA: [speaker.simple_info_line()]: [html_decode(text)]") + + speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) MSG: - [text]" + GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) MSG: - [text]" + +/proc/log_to_dd(text) + to_world_log(text) //this comes before the config check because it can't possibly runtime + if(config.log_world_output) + WRITE_LOG(diary, "DD_OUTPUT: [text]") + +/proc/log_error(text) + to_world_log(text) + WRITE_LOG(error_log, "RUNTIME: [text]") + +/proc/log_misc(text) + WRITE_LOG(diary, "MISC: [text]") + +/proc/log_topic(text) + if(Debug2) + WRITE_LOG(diary, "TOPIC: [text]") + +/proc/log_unit_test(text) + to_world_log("## UNIT_TEST: [text]") + +#ifdef REFERENCE_TRACKING_LOG +#define log_reftracker(msg) log_world("## REF SEARCH [msg]") +#else +#define log_reftracker(msg) +#endif + +/proc/log_asset(text) + WRITE_LOG(diary, "ASSET: [text]") + +/proc/report_progress(var/progress_message) + admin_notice("[progress_message]", R_DEBUG) + to_world_log(progress_message) + +//pretty print a direction bitflag, can be useful for debugging. +/proc/print_dir(var/dir) + var/list/comps = list() + if(dir & NORTH) comps += "NORTH" + if(dir & SOUTH) comps += "SOUTH" + if(dir & EAST) comps += "EAST" + if(dir & WEST) comps += "WEST" + if(dir & UP) comps += "UP" + if(dir & DOWN) comps += "DOWN" + + return english_list(comps, nothing_text="0", and_text="|", comma_text="|") + +//more or less a logging utility +//Always return "Something/(Something)", even if it's an error message. +/proc/key_name(var/whom, var/include_link = FALSE, var/include_name = TRUE, var/highlight_special_characters = TRUE) + var/mob/M + var/client/C + var/key + + if(!whom) + return "INVALID/INVALID" + if(istype(whom, /client)) + C = whom + M = C.mob + key = C.key + else if(ismob(whom)) + M = whom + C = M.client + key = M.key + else if(istype(whom, /datum/mind)) + var/datum/mind/D = whom + key = D.key + M = D.current + if(D.current) + C = D.current.client + else if(istype(whom, /datum)) + var/datum/D = whom + return "INVALID/([D.type])" + else if(istext(whom)) + return "AUTOMATED/[whom]" //Just give them the text back + else + return "INVALID/INVALID" + + . = "" + + if(key) + if(include_link && C) + . += "" + + if(C && C.holder && C.holder.fakekey) + . += "Administrator" + else + . += key + + if(include_link) + if(C) . += "" + else . += " (DC)" + else + . += "INVALID" + + if(include_name) + var/name = "INVALID" + if(M) + if(M.real_name) + name = M.real_name + else if(M.name) + name = M.name + + if(include_link && is_special_character(M) && highlight_special_characters) + name = "[name]" //Orange + + . += "/([name])" + + return . + +/proc/key_name_admin(var/whom, var/include_name = 1) + return key_name(whom, 1, include_name) + +// Helper procs for building detailed log lines +// +// These procs must not fail under ANY CIRCUMSTANCES! +// + +/datum/proc/log_info_line() + return "[src] ([type])" + +/atom/log_info_line() + . = ..() + var/turf/t = get_turf(src) + if(istype(t)) + return "[.] @ [t.log_info_line()]" + else if(loc) + return "[.] @ ([loc]) (0,0,0) ([loc.type])" + else + return "[.] @ (NULL) (0,0,0) (NULL)" + +/turf/log_info_line() + return "([src]) ([x],[y],[z]) ([type])" + +/mob/log_info_line() + return "[..()] (ckey=[ckey])" + +/proc/log_info_line(var/datum/d) + if(!d) + return "*null*" + if(!istype(d)) + return json_encode(d) + return d.log_info_line() + +/mob/proc/simple_info_line() + return "[key_name(src)] ([x],[y],[z])" + +/client/proc/simple_info_line() + return "[key_name(src)] ([mob.x],[mob.y],[mob.z])" diff --git a/code/_helpers/matrices.dm b/code/_helpers/matrices.dm index 88bec71bc27..e2b41b2f5b0 100644 --- a/code/_helpers/matrices.dm +++ b/code/_helpers/matrices.dm @@ -1,131 +1,131 @@ -/matrix/proc/TurnTo(old_angle, new_angle) - . = new_angle - old_angle - Turn(.) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT - - -/atom/proc/SpinAnimation(speed = 10, loops = -1, clockwise = 1, segments = 3) - if(!segments) - return - //VOREStation Addition Start - if(speed == 0) - loops = 0 - if(speed < 0) - speed = speed * -1 - clockwise = 0 - //VOREStation Addition End - var/segment = 360/segments - if(!clockwise) - segment = -segment - var/list/matrices = list() - for(var/i in 1 to segments-1) - var/matrix/M = matrix(transform) - M.Turn(segment*i) - matrices += M - var/matrix/last = matrix(transform) - matrices += last - - speed /= segments - - animate(src, transform = matrices[1], time = speed, loops) - for(var/i in 2 to segments) //2 because 1 is covered above - animate(transform = matrices[i], time = speed) - //doesn't have an object argument because this is "Stacking" with the animate call above - //3 billion% intentional - -//The X pixel offset of this matrix -/matrix/proc/get_x_shift() - . = c - -//The Y pixel offset of this matrix -/matrix/proc/get_y_shift() - . = f -// Color matrices: - -//Luma coefficients suggested for HDTVs. If you change these, make sure they add up to 1. -#define LUMR 0.2126 -#define LUMG 0.7152 -#define LUMB 0.0722 - -//Still need color matrix addition, negation, and multiplication. - -//Returns an identity color matrix which does nothing -/proc/color_identity() - return list(1,0,0, 0,1,0, 0,0,1) - -//Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting whites -//TODO: Need a version that only affects one color (ie shift red to blue but leave greens and blues alone) -/proc/color_rotation(angle) - if(angle == 0) - return color_identity() - angle = CLAMP(angle, -180, 180) - var/cos = cos(angle) - var/sin = sin(angle) - - var/constA = 0.143 - var/constB = 0.140 - var/constC = -0.283 - return list( - LUMR + cos * (1-LUMR) + sin * -LUMR, LUMR + cos * -LUMR + sin * constA, LUMR + cos * -LUMR + sin * -(1-LUMR), - LUMG + cos * -LUMG + sin * -LUMG, LUMG + cos * (1-LUMG) + sin * constB, LUMG + cos * -LUMG + sin * LUMG, - LUMB + cos * -LUMB + sin * (1-LUMB), LUMB + cos * -LUMB + sin * constC, LUMB + cos * (1-LUMB) + sin * LUMB - ) - -//Makes everything brighter or darker without regard to existing color or brightness -/proc/color_brightness(power) - power = CLAMP(power, -255, 255) - power = power/255 - - return list(1,0,0, 0,1,0, 0,0,1, power,power,power) - -/var/list/delta_index = list( - 0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11, - 0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24, - 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42, - 0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68, - 0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98, - 1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54, - 1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25, - 2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8, - 4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0, - 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8, - 10.0) - -//Exxagerates or removes brightness -/proc/color_contrast(value) - value = CLAMP(value, -100, 100) - if(value == 0) - return color_identity() - - var/x = 0 - if (value < 0) - x = 127 + value / 100 * 127; - else - x = value % 1 - if(x == 0) - x = delta_index[value] - else - x = delta_index[value] * (1-x) + delta_index[value+1] * x//use linear interpolation for more granularity. - x = x * 127 + 127 - - var/mult = x / 127 - var/add = 0.5 * (127-x) / 255 - return list(mult,0,0, 0,mult,0, 0,0,mult, add,add,add) - -//Exxagerates or removes colors -/proc/color_saturation(value as num) - if(value == 0) - return color_identity() - value = CLAMP(value, -100, 100) - if(value > 0) - value *= 3 - var/x = 1 + value / 100 - var/inv = 1 - x - var/R = LUMR * inv - var/G = LUMG * inv - var/B = LUMB * inv - - return list(R + x,R,R, G,G + x,G, B,B,B + x) - -#undef LUMR -#undef LUMG +/matrix/proc/TurnTo(old_angle, new_angle) + . = new_angle - old_angle + Turn(.) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT + + +/atom/proc/SpinAnimation(speed = 10, loops = -1, clockwise = 1, segments = 3) + if(!segments) + return + //VOREStation Addition Start + if(speed == 0) + loops = 0 + if(speed < 0) + speed = speed * -1 + clockwise = 0 + //VOREStation Addition End + var/segment = 360/segments + if(!clockwise) + segment = -segment + var/list/matrices = list() + for(var/i in 1 to segments-1) + var/matrix/M = matrix(transform) + M.Turn(segment*i) + matrices += M + var/matrix/last = matrix(transform) + matrices += last + + speed /= segments + + animate(src, transform = matrices[1], time = speed, loops) + for(var/i in 2 to segments) //2 because 1 is covered above + animate(transform = matrices[i], time = speed) + //doesn't have an object argument because this is "Stacking" with the animate call above + //3 billion% intentional + +//The X pixel offset of this matrix +/matrix/proc/get_x_shift() + . = c + +//The Y pixel offset of this matrix +/matrix/proc/get_y_shift() + . = f +// Color matrices: + +//Luma coefficients suggested for HDTVs. If you change these, make sure they add up to 1. +#define LUMR 0.2126 +#define LUMG 0.7152 +#define LUMB 0.0722 + +//Still need color matrix addition, negation, and multiplication. + +//Returns an identity color matrix which does nothing +/proc/color_identity() + return list(1,0,0, 0,1,0, 0,0,1) + +//Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting whites +//TODO: Need a version that only affects one color (ie shift red to blue but leave greens and blues alone) +/proc/color_rotation(angle) + if(angle == 0) + return color_identity() + angle = CLAMP(angle, -180, 180) + var/cos = cos(angle) + var/sin = sin(angle) + + var/constA = 0.143 + var/constB = 0.140 + var/constC = -0.283 + return list( + LUMR + cos * (1-LUMR) + sin * -LUMR, LUMR + cos * -LUMR + sin * constA, LUMR + cos * -LUMR + sin * -(1-LUMR), + LUMG + cos * -LUMG + sin * -LUMG, LUMG + cos * (1-LUMG) + sin * constB, LUMG + cos * -LUMG + sin * LUMG, + LUMB + cos * -LUMB + sin * (1-LUMB), LUMB + cos * -LUMB + sin * constC, LUMB + cos * (1-LUMB) + sin * LUMB + ) + +//Makes everything brighter or darker without regard to existing color or brightness +/proc/color_brightness(power) + power = CLAMP(power, -255, 255) + power = power/255 + + return list(1,0,0, 0,1,0, 0,0,1, power,power,power) + +/var/list/delta_index = list( + 0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11, + 0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24, + 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42, + 0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68, + 0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98, + 1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54, + 1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25, + 2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8, + 4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0, + 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8, + 10.0) + +//Exxagerates or removes brightness +/proc/color_contrast(value) + value = CLAMP(value, -100, 100) + if(value == 0) + return color_identity() + + var/x = 0 + if (value < 0) + x = 127 + value / 100 * 127; + else + x = value % 1 + if(x == 0) + x = delta_index[value] + else + x = delta_index[value] * (1-x) + delta_index[value+1] * x//use linear interpolation for more granularity. + x = x * 127 + 127 + + var/mult = x / 127 + var/add = 0.5 * (127-x) / 255 + return list(mult,0,0, 0,mult,0, 0,0,mult, add,add,add) + +//Exxagerates or removes colors +/proc/color_saturation(value as num) + if(value == 0) + return color_identity() + value = CLAMP(value, -100, 100) + if(value > 0) + value *= 3 + var/x = 1 + value / 100 + var/inv = 1 - x + var/R = LUMR * inv + var/G = LUMG * inv + var/B = LUMB * inv + + return list(R + x,R,R, G,G + x,G, B,B,B + x) + +#undef LUMR +#undef LUMG #undef LUMB \ No newline at end of file diff --git a/code/_helpers/mobs.dm b/code/_helpers/mobs.dm index a9fedf110bb..dd2dde46825 100644 --- a/code/_helpers/mobs.dm +++ b/code/_helpers/mobs.dm @@ -1,323 +1,323 @@ -/proc/random_hair_style(gender, species = SPECIES_HUMAN) - var/h_style = "Bald" - - var/list/valid_hairstyles = list() - for(var/hairstyle in hair_styles_list) - var/datum/sprite_accessory/S = hair_styles_list[hairstyle] - if(gender == MALE && S.gender == FEMALE) - continue - if(gender == FEMALE && S.gender == MALE) - continue - if( !(species in S.species_allowed)) - continue - valid_hairstyles[hairstyle] = hair_styles_list[hairstyle] - - if(valid_hairstyles.len) - h_style = pick(valid_hairstyles) - - return h_style - -/proc/random_facial_hair_style(gender, species = SPECIES_HUMAN) - var/f_style = "Shaved" - - var/list/valid_facialhairstyles = list() - for(var/facialhairstyle in facial_hair_styles_list) - var/datum/sprite_accessory/S = facial_hair_styles_list[facialhairstyle] - if(gender == MALE && S.gender == FEMALE) - continue - if(gender == FEMALE && S.gender == MALE) - continue - if( !(species in S.species_allowed)) - continue - - valid_facialhairstyles[facialhairstyle] = facial_hair_styles_list[facialhairstyle] - - if(valid_facialhairstyles.len) - f_style = pick(valid_facialhairstyles) - - return f_style - -/proc/sanitize_name(name, species = SPECIES_HUMAN, robot = 0) - var/datum/species/current_species - if(species) - current_species = GLOB.all_species[species] - - return current_species ? current_species.sanitize_name(name, robot) : sanitizeName(name, MAX_NAME_LEN, robot) - -/proc/random_name(gender, species = SPECIES_HUMAN) - - var/datum/species/current_species - if(species) - current_species = GLOB.all_species[species] - - if(!current_species || current_species.name_language == null) - if(gender==FEMALE) - return capitalize(pick(first_names_female)) + " " + capitalize(pick(last_names)) - else - return capitalize(pick(first_names_male)) + " " + capitalize(pick(last_names)) - else - return current_species.get_random_name(gender) - -/proc/random_skin_tone() - switch(pick(60;"caucasian", 15;"afroamerican", 10;"african", 10;"latino", 5;"albino")) - if("caucasian") . = -10 - if("afroamerican") . = -115 - if("african") . = -165 - if("latino") . = -55 - if("albino") . = 34 - else . = rand(-185,34) - return min(max( .+rand(-25, 25), -185),34) - -/proc/skintone2racedescription(tone) - switch (tone) - if(30 to INFINITY) return "albino" - if(20 to 30) return "pale" - if(5 to 15) return "light skinned" - if(-10 to 5) return "white" - if(-25 to -10) return "tan" - if(-45 to -25) return "darker skinned" - if(-65 to -45) return "brown" - if(-INFINITY to -65) return "black" - else return "unknown" - -/proc/age2agedescription(age) - switch(age) - if(0 to 1) return "infant" - if(1 to 3) return "toddler" - if(3 to 13) return "child" - if(13 to 19) return "teenager" - if(19 to 30) return "young adult" - if(30 to 45) return "adult" - if(45 to 60) return "middle-aged" - if(60 to 70) return "aging" - if(70 to INFINITY) return "elderly" - else return "unknown" - -/proc/RoundHealth(health) - var/list/icon_states = cached_icon_states(ingame_hud_med) - for(var/icon_state in icon_states) - if(health >= text2num(icon_state)) - return icon_state - return icon_states[icon_states.len] // If we had no match, return the last element - -/* -Proc for attack log creation, because really why not -1 argument is the actor -2 argument is the target of action -3 is the description of action(like punched, throwed, or any other verb) -4 should it make adminlog note or not -5 is the tool with which the action was made(usually item) 5 and 6 are very similar(5 have "by " before it, that it) and are separated just to keep things in a bit more in order -6 is additional information, anything that needs to be added -*/ - -/proc/add_attack_logs(mob/user, mob/target, what_done, var/admin_notify = TRUE) - if(islist(target)) //Multi-victim adding - var/list/targets = target - for(var/mob/M in targets) - add_attack_logs(user,M,what_done,admin_notify) - return - - var/user_str = key_name(user) - var/target_str = key_name(target) - - if(ismob(user)) - user.attack_log += text("\[[time_stamp()]\] [span_red("Attacked [target_str]: [what_done]")]") - if(ismob(target)) - target.attack_log += text("\[[time_stamp()]\] [span_orange("Attacked by [user_str]: [what_done]")]") - log_attack(user_str,target_str,what_done) - if(admin_notify) - msg_admin_attack("[key_name_admin(user)] vs [target_str]: [what_done]") - -//checks whether this item is a module of the robot it is located in. -/proc/is_robot_module(var/obj/item/thing) - if (!thing || !istype(thing.loc, /mob/living/silicon/robot)) - return 0 - var/mob/living/silicon/robot/R = thing.loc - return (thing in R.module.modules) - -/proc/get_exposed_defense_zone(var/atom/movable/target) - var/obj/item/weapon/grab/G = locate() in target - if(G && G.state >= GRAB_NECK) //works because mobs are currently not allowed to upgrade to NECK if they are grabbing two people. - return pick(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG) - else - return pick(BP_TORSO, BP_GROIN) - -/proc/do_mob(mob/user , mob/target, time = 30, target_zone = 0, uninterruptible = FALSE, progress = TRUE, ignore_movement = FALSE, exclusive = FALSE) - if(!user || !target) - return FALSE - if(!time) - return TRUE //Done! - if(user.status_flags & DOING_TASK) - to_chat(user, "You're in the middle of doing something else already.") - return FALSE //Performing an exclusive do_after or do_mob already - if(target?.flags & IS_BUSY) - to_chat(user, "Someone is already doing something with \the [target].") - return FALSE - var/user_loc = user.loc - var/target_loc = target.loc - - var/holding = user.get_active_hand() - var/datum/progressbar/progbar - if (progress) - progbar = new(user, time, target) - - var/endtime = world.time+time - var/starttime = world.time - - if(exclusive & TASK_USER_EXCLUSIVE) - user.status_flags |= DOING_TASK - if(target && exclusive & TASK_TARGET_EXCLUSIVE) - target.flags |= IS_BUSY - - . = TRUE - while (world.time < endtime) - stoplag(1) - if (progress) - progbar.update(world.time - starttime) - if(!user || !target) - . = FALSE - break - if(uninterruptible) - continue - - if(!user || user.incapacitated()) - . = FALSE - break - - if(user.loc != user_loc && !ignore_movement) - . = FALSE - break - - if(target.loc != target_loc && !ignore_movement) - . = FALSE - break - - if(user.get_active_hand() != holding) - . = FALSE - break - - if(target_zone && user.zone_sel.selecting != target_zone) - . = FALSE - break - - if(exclusive & TASK_USER_EXCLUSIVE) - user.status_flags &= ~DOING_TASK - if(exclusive & TASK_TARGET_EXCLUSIVE) - target?.status_flags &= ~IS_BUSY - - if (progbar) - qdel(progbar) - -/proc/do_after(mob/user, delay, atom/target = null, needhand = TRUE, progress = TRUE, incapacitation_flags = INCAPACITATION_DEFAULT, ignore_movement = FALSE, max_distance = null, exclusive = FALSE) - if(!user) - return FALSE - if(!delay) - return TRUE //Okay. Done. - if(user.status_flags & DOING_TASK) - to_chat(user, "You're in the middle of doing something else already.") - return FALSE //Performing an exclusive do_after or do_mob already - if(target?.flags & IS_BUSY) - to_chat(user, "Someone is already doing something with \the [target].") - return FALSE - - var/atom/target_loc = null - if(target) - target_loc = target.loc - - var/atom/original_loc = user.loc - - var/obj/mecha/M = null - - if(istype(user.loc, /obj/mecha)) - original_loc = get_turf(original_loc) - M = user.loc - - var/holding = user.get_active_hand() - - var/datum/progressbar/progbar - if (progress) - progbar = new(user, delay, target) - - var/endtime = world.time + delay - var/starttime = world.time - - if(exclusive & TASK_USER_EXCLUSIVE) - user.status_flags |= DOING_TASK - - if(target && (exclusive & TASK_TARGET_EXCLUSIVE)) - target.flags |= IS_BUSY - - . = TRUE - while (world.time < endtime) - stoplag(1) - if(progress) - progbar.update(world.time - starttime) - - if(!user || user.incapacitated(incapacitation_flags)) - . = FALSE - break - - if(M) - if(user.loc != M || (M.loc != original_loc && !ignore_movement)) // Mech coooooode. - . = FALSE - break - - else if(user.loc != original_loc && !ignore_movement) - . = FALSE - break - - if(target_loc && (QDELETED(target))) - . = FALSE - break - - if(target && target_loc != target.loc && !ignore_movement) - . = FALSE - break - - if(needhand) - if(user.get_active_hand() != holding) - . = FALSE - break - - if(max_distance && target && get_dist(user, target) > max_distance) - . = FALSE - break - - if(exclusive & TASK_USER_EXCLUSIVE) - user.status_flags &= ~DOING_TASK - if(target && (exclusive & TASK_TARGET_EXCLUSIVE)) - target.flags &= ~IS_BUSY - - if(progbar) - qdel(progbar) - -/atom/proc/living_mobs(var/range = world.view) - var/list/viewers = oviewers(src,range) - var/list/living = list() - for(var/mob/living/L in viewers) - living += L - - return living - -/atom/proc/human_mobs(var/range = world.view) - var/list/viewers = oviewers(src,range) - var/list/humans = list() - for(var/mob/living/carbon/human/H in viewers) - humans += H - - return humans - -/proc/cached_character_icon(var/mob/desired) - var/cachekey = "\ref[desired][desired.real_name]" - - if(cached_character_icons[cachekey]) - . = cached_character_icons[cachekey] - else - . = getCompoundIcon(desired) - cached_character_icons[cachekey] = . - -/proc/not_has_ooc_text(mob/user) - if (config.allow_Metadata && (!user.client?.prefs?.metadata || length(user.client.prefs.metadata) < 15)) - to_chat(user, "Please set informative OOC notes related to RP/ERP preferences. Set them using the 'OOC Notes' button on the 'General' tab in character setup.") - return TRUE - return FALSE +/proc/random_hair_style(gender, species = SPECIES_HUMAN) + var/h_style = "Bald" + + var/list/valid_hairstyles = list() + for(var/hairstyle in hair_styles_list) + var/datum/sprite_accessory/S = hair_styles_list[hairstyle] + if(gender == MALE && S.gender == FEMALE) + continue + if(gender == FEMALE && S.gender == MALE) + continue + if( !(species in S.species_allowed)) + continue + valid_hairstyles[hairstyle] = hair_styles_list[hairstyle] + + if(valid_hairstyles.len) + h_style = pick(valid_hairstyles) + + return h_style + +/proc/random_facial_hair_style(gender, species = SPECIES_HUMAN) + var/f_style = "Shaved" + + var/list/valid_facialhairstyles = list() + for(var/facialhairstyle in facial_hair_styles_list) + var/datum/sprite_accessory/S = facial_hair_styles_list[facialhairstyle] + if(gender == MALE && S.gender == FEMALE) + continue + if(gender == FEMALE && S.gender == MALE) + continue + if( !(species in S.species_allowed)) + continue + + valid_facialhairstyles[facialhairstyle] = facial_hair_styles_list[facialhairstyle] + + if(valid_facialhairstyles.len) + f_style = pick(valid_facialhairstyles) + + return f_style + +/proc/sanitize_name(name, species = SPECIES_HUMAN, robot = 0) + var/datum/species/current_species + if(species) + current_species = GLOB.all_species[species] + + return current_species ? current_species.sanitize_name(name, robot) : sanitizeName(name, MAX_NAME_LEN, robot) + +/proc/random_name(gender, species = SPECIES_HUMAN) + + var/datum/species/current_species + if(species) + current_species = GLOB.all_species[species] + + if(!current_species || current_species.name_language == null) + if(gender==FEMALE) + return capitalize(pick(first_names_female)) + " " + capitalize(pick(last_names)) + else + return capitalize(pick(first_names_male)) + " " + capitalize(pick(last_names)) + else + return current_species.get_random_name(gender) + +/proc/random_skin_tone() + switch(pick(60;"caucasian", 15;"afroamerican", 10;"african", 10;"latino", 5;"albino")) + if("caucasian") . = -10 + if("afroamerican") . = -115 + if("african") . = -165 + if("latino") . = -55 + if("albino") . = 34 + else . = rand(-185,34) + return min(max( .+rand(-25, 25), -185),34) + +/proc/skintone2racedescription(tone) + switch (tone) + if(30 to INFINITY) return "albino" + if(20 to 30) return "pale" + if(5 to 15) return "light skinned" + if(-10 to 5) return "white" + if(-25 to -10) return "tan" + if(-45 to -25) return "darker skinned" + if(-65 to -45) return "brown" + if(-INFINITY to -65) return "black" + else return "unknown" + +/proc/age2agedescription(age) + switch(age) + if(0 to 1) return "infant" + if(1 to 3) return "toddler" + if(3 to 13) return "child" + if(13 to 19) return "teenager" + if(19 to 30) return "young adult" + if(30 to 45) return "adult" + if(45 to 60) return "middle-aged" + if(60 to 70) return "aging" + if(70 to INFINITY) return "elderly" + else return "unknown" + +/proc/RoundHealth(health) + var/list/icon_states = cached_icon_states(ingame_hud_med) + for(var/icon_state in icon_states) + if(health >= text2num(icon_state)) + return icon_state + return icon_states[icon_states.len] // If we had no match, return the last element + +/* +Proc for attack log creation, because really why not +1 argument is the actor +2 argument is the target of action +3 is the description of action(like punched, throwed, or any other verb) +4 should it make adminlog note or not +5 is the tool with which the action was made(usually item) 5 and 6 are very similar(5 have "by " before it, that it) and are separated just to keep things in a bit more in order +6 is additional information, anything that needs to be added +*/ + +/proc/add_attack_logs(mob/user, mob/target, what_done, var/admin_notify = TRUE) + if(islist(target)) //Multi-victim adding + var/list/targets = target + for(var/mob/M in targets) + add_attack_logs(user,M,what_done,admin_notify) + return + + var/user_str = key_name(user) + var/target_str = key_name(target) + + if(ismob(user)) + user.attack_log += text("\[[time_stamp()]\] [span_red("Attacked [target_str]: [what_done]")]") + if(ismob(target)) + target.attack_log += text("\[[time_stamp()]\] [span_orange("Attacked by [user_str]: [what_done]")]") + log_attack(user_str,target_str,what_done) + if(admin_notify) + msg_admin_attack("[key_name_admin(user)] vs [target_str]: [what_done]") + +//checks whether this item is a module of the robot it is located in. +/proc/is_robot_module(var/obj/item/thing) + if (!thing || !istype(thing.loc, /mob/living/silicon/robot)) + return 0 + var/mob/living/silicon/robot/R = thing.loc + return (thing in R.module.modules) + +/proc/get_exposed_defense_zone(var/atom/movable/target) + var/obj/item/weapon/grab/G = locate() in target + if(G && G.state >= GRAB_NECK) //works because mobs are currently not allowed to upgrade to NECK if they are grabbing two people. + return pick(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG) + else + return pick(BP_TORSO, BP_GROIN) + +/proc/do_mob(mob/user , mob/target, time = 30, target_zone = 0, uninterruptible = FALSE, progress = TRUE, ignore_movement = FALSE, exclusive = FALSE) + if(!user || !target) + return FALSE + if(!time) + return TRUE //Done! + if(user.status_flags & DOING_TASK) + to_chat(user, "You're in the middle of doing something else already.") + return FALSE //Performing an exclusive do_after or do_mob already + if(target?.flags & IS_BUSY) + to_chat(user, "Someone is already doing something with \the [target].") + return FALSE + var/user_loc = user.loc + var/target_loc = target.loc + + var/holding = user.get_active_hand() + var/datum/progressbar/progbar + if (progress) + progbar = new(user, time, target) + + var/endtime = world.time+time + var/starttime = world.time + + if(exclusive & TASK_USER_EXCLUSIVE) + user.status_flags |= DOING_TASK + if(target && exclusive & TASK_TARGET_EXCLUSIVE) + target.flags |= IS_BUSY + + . = TRUE + while (world.time < endtime) + stoplag(1) + if (progress) + progbar.update(world.time - starttime) + if(!user || !target) + . = FALSE + break + if(uninterruptible) + continue + + if(!user || user.incapacitated()) + . = FALSE + break + + if(user.loc != user_loc && !ignore_movement) + . = FALSE + break + + if(target.loc != target_loc && !ignore_movement) + . = FALSE + break + + if(user.get_active_hand() != holding) + . = FALSE + break + + if(target_zone && user.zone_sel.selecting != target_zone) + . = FALSE + break + + if(exclusive & TASK_USER_EXCLUSIVE) + user.status_flags &= ~DOING_TASK + if(exclusive & TASK_TARGET_EXCLUSIVE) + target?.status_flags &= ~IS_BUSY + + if (progbar) + qdel(progbar) + +/proc/do_after(mob/user, delay, atom/target = null, needhand = TRUE, progress = TRUE, incapacitation_flags = INCAPACITATION_DEFAULT, ignore_movement = FALSE, max_distance = null, exclusive = FALSE) + if(!user) + return FALSE + if(!delay) + return TRUE //Okay. Done. + if(user.status_flags & DOING_TASK) + to_chat(user, "You're in the middle of doing something else already.") + return FALSE //Performing an exclusive do_after or do_mob already + if(target?.flags & IS_BUSY) + to_chat(user, "Someone is already doing something with \the [target].") + return FALSE + + var/atom/target_loc = null + if(target) + target_loc = target.loc + + var/atom/original_loc = user.loc + + var/obj/mecha/M = null + + if(istype(user.loc, /obj/mecha)) + original_loc = get_turf(original_loc) + M = user.loc + + var/holding = user.get_active_hand() + + var/datum/progressbar/progbar + if (progress) + progbar = new(user, delay, target) + + var/endtime = world.time + delay + var/starttime = world.time + + if(exclusive & TASK_USER_EXCLUSIVE) + user.status_flags |= DOING_TASK + + if(target && (exclusive & TASK_TARGET_EXCLUSIVE)) + target.flags |= IS_BUSY + + . = TRUE + while (world.time < endtime) + stoplag(1) + if(progress) + progbar.update(world.time - starttime) + + if(!user || user.incapacitated(incapacitation_flags)) + . = FALSE + break + + if(M) + if(user.loc != M || (M.loc != original_loc && !ignore_movement)) // Mech coooooode. + . = FALSE + break + + else if(user.loc != original_loc && !ignore_movement) + . = FALSE + break + + if(target_loc && (QDELETED(target))) + . = FALSE + break + + if(target && target_loc != target.loc && !ignore_movement) + . = FALSE + break + + if(needhand) + if(user.get_active_hand() != holding) + . = FALSE + break + + if(max_distance && target && get_dist(user, target) > max_distance) + . = FALSE + break + + if(exclusive & TASK_USER_EXCLUSIVE) + user.status_flags &= ~DOING_TASK + if(target && (exclusive & TASK_TARGET_EXCLUSIVE)) + target.flags &= ~IS_BUSY + + if(progbar) + qdel(progbar) + +/atom/proc/living_mobs(var/range = world.view) + var/list/viewers = oviewers(src,range) + var/list/living = list() + for(var/mob/living/L in viewers) + living += L + + return living + +/atom/proc/human_mobs(var/range = world.view) + var/list/viewers = oviewers(src,range) + var/list/humans = list() + for(var/mob/living/carbon/human/H in viewers) + humans += H + + return humans + +/proc/cached_character_icon(var/mob/desired) + var/cachekey = "\ref[desired][desired.real_name]" + + if(cached_character_icons[cachekey]) + . = cached_character_icons[cachekey] + else + . = getCompoundIcon(desired) + cached_character_icons[cachekey] = . + +/proc/not_has_ooc_text(mob/user) + if (config.allow_Metadata && (!user.client?.prefs?.metadata || length(user.client.prefs.metadata) < 15)) + to_chat(user, "Please set informative OOC notes related to RP/ERP preferences. Set them using the 'OOC Notes' button on the 'General' tab in character setup.") + return TRUE + return FALSE diff --git a/code/_helpers/names.dm b/code/_helpers/names.dm index e85a6074173..12e6c46289f 100644 --- a/code/_helpers/names.dm +++ b/code/_helpers/names.dm @@ -1,224 +1,224 @@ -var/church_name = null -/proc/church_name() - if (church_name) - return church_name - - var/name = "" - - name += pick("Holy", "United", "First", "Second", "Last") - - if (prob(20)) - name += " Space" - - name += " " + pick("Church", "Cathedral", "Body", "Worshippers", "Movement", "Witnesses") - name += " of [religion_name()]" - - return name - -/proc/command_name() - if(istype(using_map)) - return using_map.boss_name - -/proc/change_command_name(var/name) - - using_map.boss_name = name - - return name - -var/religion_name = null -/proc/religion_name() - if (religion_name) - return religion_name - - var/name = "" - - name += pick("bee", "science", "edu", "captain", "assistant", "monkey", "alien", "space", "unit", "sprocket", "gadget", "bomb", "revolution", "beyond", "station", "goon", "robot", "ivor", "hobnob") - name += pick("ism", "ia", "ology", "istism", "ites", "ick", "ian", "ity") - - return capitalize(name) - -/proc/system_name() - return using_map.starsys_name - -/proc/station_name() - if (using_map.station_name) - return using_map.station_name - - var/random = rand(1,5) - var/name = "" - var/new_station_name = null - - //Rare: Pre-Prefix - if (prob(10)) - name = pick("Imperium", "Heretical", "Cuban", "Psychic", "Elegant", "Common", "Uncommon", "Rare", "Unique", "Houseruled", "Religious", "Atheist", "Traditional", "Houseruled", "Mad", "Super", "Ultra", "Secret", "Top Secret", "Deep", "Death", "Zybourne", "Central", "Main", "Government", "Uoi", "Fat", "Automated", "Experimental", "Augmented") - new_station_name = name + " " - - // Prefix - switch(Holiday) - //get normal name - if(null,"",0) - name = pick("", "Stanford", "Dorf", "Alium", "Prefix", "Clowning", "Aegis", "Ishimura", "Scaredy", "Death-World", "Mime", "Honk", "Rogue", "MacRagge", "Ultrameens", "Safety", "Paranoia", "Explosive", "Neckbear", "Donk", "Muppet", "North", "West", "East", "South", "Slant-ways", "Widdershins", "Rimward", "Expensive", "Procreatory", "Imperial", "Unidentified", "Immoral", "Carp", "Ork", "Pete", "Control", "Nettle", "Aspie", "Class", "Crab", "Fist","Corrogated","Skeleton","Race", "Fatguy", "Gentleman", "Capitalist", "Communist", "Bear", "Beard", "Derp", "Space", "Spess", "Star", "Moon", "System", "Mining", "Neckbeard", "Research", "Supply", "Military", "Orbital", "Battle", "Science", "Asteroid", "Home", "Production", "Transport", "Delivery", "Extraplanetary", "Orbital", "Correctional", "Robot", "Hats", "Pizza") - if(name) - new_station_name += name + " " - - //For special days like christmas, easter, new-years etc ~Carn - if("Friday the 13th") - name = pick("Mike","Friday","Evil","Myers","Murder","Deathly","Stabby") - new_station_name += name + " " - random = 13 - else - //get the first word of the Holiday and use that - var/i = findtext(Holiday," ",1,0) - name = copytext(Holiday,1,i) - new_station_name += name + " " - - // Suffix - name = pick("Station", "Fortress", "Frontier", "Suffix", "Death-trap", "Space-hulk", "Lab", "Hazard","Spess Junk", "Fishery", "No-Moon", "Tomb", "Crypt", "Hut", "Monkey", "Bomb", "Trade Post", "Fortress", "Village", "Town", "City", "Edition", "Hive", "Complex", "Base", "Facility", "Depot", "Outpost", "Installation", "Drydock", "Observatory", "Array", "Relay", "Monitor", "Platform", "Construct", "Hangar", "Prison", "Center", "Port", "Waystation", "Factory", "Waypoint", "Stopover", "Hub", "HQ", "Office", "Object", "Fortification", "Colony", "Planet-Cracker", "Roost", "Fat Camp") - new_station_name += name + " " - - // ID Number - switch(random) - if(1) - new_station_name += "[rand(1, 99)]" - if(2) - new_station_name += pick("Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda", "Mu", "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi", "Omega") - if(3) - new_station_name += pick("II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX") - if(4) - new_station_name += pick("Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu") - if(5) - new_station_name += pick("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen") - if(13) - new_station_name += pick("13","XIII","Thirteen") - - - if (config && config.server_name) - world.name = "[config.server_name]: [name]" - else - world.name = new_station_name - - return new_station_name - -// Is this even used? -/proc/world_name(var/name) - - using_map.station_name = name - - if (config && config.server_name) - world.name = "[config.server_name]: [name]" - else - world.name = name - - return name - -var/syndicate_name = null -/proc/syndicate_name() - if (syndicate_name) - return syndicate_name - - var/name = "" - - // Prefix - name += pick("Clandestine", "Prima", "Blue", "Zero-G", "Max", "Blasto", "Waffle", "North", "Omni", "Newton", "Cyber", "Bonk", "Gene", "Gib") - - // Suffix - if (prob(80)) - name += " " - - // Full - if (prob(60)) - name += pick("Syndicate", "Consortium", "Collective", "Corporation", "Group", "Holdings", "Biotech", "Industries", "Systems", "Products", "Chemicals", "Enterprises", "Family", "Creations", "International", "Intergalactic", "Interplanetary", "Foundation", "Positronics", "Hive") - // Broken - else - name += pick("Syndi", "Corp", "Bio", "System", "Prod", "Chem", "Inter", "Hive") - name += pick("", "-") - name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Code") - // Small - else - name += pick("-", "*", "") - name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Gen", "Star", "Dyne", "Code", "Hive") - - syndicate_name = name - return name - - -//Traitors and traitor silicons will get these. Revs will not. -var/syndicate_code_phrase//Code phrase for traitors. -var/syndicate_code_response//Code response for traitors. - - /* - Should be expanded. - How this works: - Instead of "I'm looking for James Smith," the traitor would say "James Smith" as part of a conversation. - Another traitor may then respond with: "They enjoy running through the void-filled vacuum of the derelict." - The phrase should then have the words: James Smith. - The response should then have the words: run, void, and derelict. - This way assures that the code is suited to the conversation and is unpredicatable. - Obviously, some people will be better at this than others but in theory, everyone should be able to do it and it only enhances roleplay. - Can probably be done through "{ }" but I don't really see the practical benefit. - One example of an earlier system is commented below. - -N - */ - -/proc/generate_code_phrase()//Proc is used for phrase and response in master_controller.dm - - var/code_phrase = ""//What is returned when the proc finishes. - var/words = pick(//How many words there will be. Minimum of two. 2, 4 and 5 have a lesser chance of being selected. 3 is the most likely. - 50; 2, - 200; 3, - 50; 4, - 25; 5 - ) - - var/safety[] = list(1,2,3)//Tells the proc which options to remove later on. - var/nouns[] = list("love","hate","anger","peace","pride","sympathy","bravery","loyalty","honesty","integrity","compassion","charity","success","courage","deceit","skill","beauty","brilliance","pain","misery","beliefs","dreams","justice","truth","faith","liberty","knowledge","thought","information","culture","trust","dedication","progress","education","hospitality","leisure","trouble","friendships", "relaxation") - var/drinks[] = list("vodka and tonic","gin fizz","bahama mama","manhattan","black Russian","whiskey soda","long island tea","margarita","Irish coffee"," manly dwarf","Irish cream","doctor's delight","Beepksy Smash","tequilla sunrise","brave bull","gargle blaster","bloody mary","whiskey cola","white Russian","vodka martini","martini","Cuba libre","kahlua","vodka","redwine","moonshine") - var/locations[] = teleportlocs.len ? teleportlocs : drinks//if null, defaults to drinks instead. - - var/names[] = list() - for(var/datum/data/record/t in data_core.general)//Picks from crew manifest. - names += t.fields["name"] - - var/maxwords = words//Extra var to check for duplicates. - - for(words,words>0,words--)//Randomly picks from one of the choices below. - - if(words==1&&(1 in safety)&&(2 in safety))//If there is only one word remaining and choice 1 or 2 have not been selected. - safety = list(pick(1,2))//Select choice 1 or 2. - else if(words==1&&maxwords==2)//Else if there is only one word remaining (and there were two originally), and 1 or 2 were chosen, - safety = list(3)//Default to list 3 - - switch(pick(safety))//Chance based on the safety list. - if(1)//1 and 2 can only be selected once each to prevent more than two specific names/places/etc. - switch(rand(1,2))//Mainly to add more options later. - if(1) - if(names.len&&prob(70)) - code_phrase += pick(names) - else - code_phrase += pick(pick(first_names_male,first_names_female)) - code_phrase += " " - code_phrase += pick(last_names) - if(2) - code_phrase += pick(joblist)//Returns a job. - safety -= 1 - if(2) - switch(rand(1,2))//Places or things. - if(1) - code_phrase += pick(drinks) - if(2) - code_phrase += pick(locations) - safety -= 2 - if(3) - switch(rand(1,3))//Nouns, adjectives, verbs. Can be selected more than once. - if(1) - code_phrase += pick(nouns) - if(2) - code_phrase += pick(adjectives) - if(3) - code_phrase += pick(verbs) - if(words==1) - code_phrase += "." - else - code_phrase += ", " - - return code_phrase +var/church_name = null +/proc/church_name() + if (church_name) + return church_name + + var/name = "" + + name += pick("Holy", "United", "First", "Second", "Last") + + if (prob(20)) + name += " Space" + + name += " " + pick("Church", "Cathedral", "Body", "Worshippers", "Movement", "Witnesses") + name += " of [religion_name()]" + + return name + +/proc/command_name() + if(istype(using_map)) + return using_map.boss_name + +/proc/change_command_name(var/name) + + using_map.boss_name = name + + return name + +var/religion_name = null +/proc/religion_name() + if (religion_name) + return religion_name + + var/name = "" + + name += pick("bee", "science", "edu", "captain", "assistant", "monkey", "alien", "space", "unit", "sprocket", "gadget", "bomb", "revolution", "beyond", "station", "goon", "robot", "ivor", "hobnob") + name += pick("ism", "ia", "ology", "istism", "ites", "ick", "ian", "ity") + + return capitalize(name) + +/proc/system_name() + return using_map.starsys_name + +/proc/station_name() + if (using_map.station_name) + return using_map.station_name + + var/random = rand(1,5) + var/name = "" + var/new_station_name = null + + //Rare: Pre-Prefix + if (prob(10)) + name = pick("Imperium", "Heretical", "Cuban", "Psychic", "Elegant", "Common", "Uncommon", "Rare", "Unique", "Houseruled", "Religious", "Atheist", "Traditional", "Houseruled", "Mad", "Super", "Ultra", "Secret", "Top Secret", "Deep", "Death", "Zybourne", "Central", "Main", "Government", "Uoi", "Fat", "Automated", "Experimental", "Augmented") + new_station_name = name + " " + + // Prefix + switch(Holiday) + //get normal name + if(null,"",0) + name = pick("", "Stanford", "Dorf", "Alium", "Prefix", "Clowning", "Aegis", "Ishimura", "Scaredy", "Death-World", "Mime", "Honk", "Rogue", "MacRagge", "Ultrameens", "Safety", "Paranoia", "Explosive", "Neckbear", "Donk", "Muppet", "North", "West", "East", "South", "Slant-ways", "Widdershins", "Rimward", "Expensive", "Procreatory", "Imperial", "Unidentified", "Immoral", "Carp", "Ork", "Pete", "Control", "Nettle", "Aspie", "Class", "Crab", "Fist","Corrogated","Skeleton","Race", "Fatguy", "Gentleman", "Capitalist", "Communist", "Bear", "Beard", "Derp", "Space", "Spess", "Star", "Moon", "System", "Mining", "Neckbeard", "Research", "Supply", "Military", "Orbital", "Battle", "Science", "Asteroid", "Home", "Production", "Transport", "Delivery", "Extraplanetary", "Orbital", "Correctional", "Robot", "Hats", "Pizza") + if(name) + new_station_name += name + " " + + //For special days like christmas, easter, new-years etc ~Carn + if("Friday the 13th") + name = pick("Mike","Friday","Evil","Myers","Murder","Deathly","Stabby") + new_station_name += name + " " + random = 13 + else + //get the first word of the Holiday and use that + var/i = findtext(Holiday," ",1,0) + name = copytext(Holiday,1,i) + new_station_name += name + " " + + // Suffix + name = pick("Station", "Fortress", "Frontier", "Suffix", "Death-trap", "Space-hulk", "Lab", "Hazard","Spess Junk", "Fishery", "No-Moon", "Tomb", "Crypt", "Hut", "Monkey", "Bomb", "Trade Post", "Fortress", "Village", "Town", "City", "Edition", "Hive", "Complex", "Base", "Facility", "Depot", "Outpost", "Installation", "Drydock", "Observatory", "Array", "Relay", "Monitor", "Platform", "Construct", "Hangar", "Prison", "Center", "Port", "Waystation", "Factory", "Waypoint", "Stopover", "Hub", "HQ", "Office", "Object", "Fortification", "Colony", "Planet-Cracker", "Roost", "Fat Camp") + new_station_name += name + " " + + // ID Number + switch(random) + if(1) + new_station_name += "[rand(1, 99)]" + if(2) + new_station_name += pick("Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda", "Mu", "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi", "Omega") + if(3) + new_station_name += pick("II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX") + if(4) + new_station_name += pick("Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu") + if(5) + new_station_name += pick("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen") + if(13) + new_station_name += pick("13","XIII","Thirteen") + + + if (config && config.server_name) + world.name = "[config.server_name]: [name]" + else + world.name = new_station_name + + return new_station_name + +// Is this even used? +/proc/world_name(var/name) + + using_map.station_name = name + + if (config && config.server_name) + world.name = "[config.server_name]: [name]" + else + world.name = name + + return name + +var/syndicate_name = null +/proc/syndicate_name() + if (syndicate_name) + return syndicate_name + + var/name = "" + + // Prefix + name += pick("Clandestine", "Prima", "Blue", "Zero-G", "Max", "Blasto", "Waffle", "North", "Omni", "Newton", "Cyber", "Bonk", "Gene", "Gib") + + // Suffix + if (prob(80)) + name += " " + + // Full + if (prob(60)) + name += pick("Syndicate", "Consortium", "Collective", "Corporation", "Group", "Holdings", "Biotech", "Industries", "Systems", "Products", "Chemicals", "Enterprises", "Family", "Creations", "International", "Intergalactic", "Interplanetary", "Foundation", "Positronics", "Hive") + // Broken + else + name += pick("Syndi", "Corp", "Bio", "System", "Prod", "Chem", "Inter", "Hive") + name += pick("", "-") + name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Code") + // Small + else + name += pick("-", "*", "") + name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Gen", "Star", "Dyne", "Code", "Hive") + + syndicate_name = name + return name + + +//Traitors and traitor silicons will get these. Revs will not. +var/syndicate_code_phrase//Code phrase for traitors. +var/syndicate_code_response//Code response for traitors. + + /* + Should be expanded. + How this works: + Instead of "I'm looking for James Smith," the traitor would say "James Smith" as part of a conversation. + Another traitor may then respond with: "They enjoy running through the void-filled vacuum of the derelict." + The phrase should then have the words: James Smith. + The response should then have the words: run, void, and derelict. + This way assures that the code is suited to the conversation and is unpredicatable. + Obviously, some people will be better at this than others but in theory, everyone should be able to do it and it only enhances roleplay. + Can probably be done through "{ }" but I don't really see the practical benefit. + One example of an earlier system is commented below. + -N + */ + +/proc/generate_code_phrase()//Proc is used for phrase and response in master_controller.dm + + var/code_phrase = ""//What is returned when the proc finishes. + var/words = pick(//How many words there will be. Minimum of two. 2, 4 and 5 have a lesser chance of being selected. 3 is the most likely. + 50; 2, + 200; 3, + 50; 4, + 25; 5 + ) + + var/safety[] = list(1,2,3)//Tells the proc which options to remove later on. + var/nouns[] = list("love","hate","anger","peace","pride","sympathy","bravery","loyalty","honesty","integrity","compassion","charity","success","courage","deceit","skill","beauty","brilliance","pain","misery","beliefs","dreams","justice","truth","faith","liberty","knowledge","thought","information","culture","trust","dedication","progress","education","hospitality","leisure","trouble","friendships", "relaxation") + var/drinks[] = list("vodka and tonic","gin fizz","bahama mama","manhattan","black Russian","whiskey soda","long island tea","margarita","Irish coffee"," manly dwarf","Irish cream","doctor's delight","Beepksy Smash","tequilla sunrise","brave bull","gargle blaster","bloody mary","whiskey cola","white Russian","vodka martini","martini","Cuba libre","kahlua","vodka","redwine","moonshine") + var/locations[] = teleportlocs.len ? teleportlocs : drinks//if null, defaults to drinks instead. + + var/names[] = list() + for(var/datum/data/record/t in data_core.general)//Picks from crew manifest. + names += t.fields["name"] + + var/maxwords = words//Extra var to check for duplicates. + + for(words,words>0,words--)//Randomly picks from one of the choices below. + + if(words==1&&(1 in safety)&&(2 in safety))//If there is only one word remaining and choice 1 or 2 have not been selected. + safety = list(pick(1,2))//Select choice 1 or 2. + else if(words==1&&maxwords==2)//Else if there is only one word remaining (and there were two originally), and 1 or 2 were chosen, + safety = list(3)//Default to list 3 + + switch(pick(safety))//Chance based on the safety list. + if(1)//1 and 2 can only be selected once each to prevent more than two specific names/places/etc. + switch(rand(1,2))//Mainly to add more options later. + if(1) + if(names.len&&prob(70)) + code_phrase += pick(names) + else + code_phrase += pick(pick(first_names_male,first_names_female)) + code_phrase += " " + code_phrase += pick(last_names) + if(2) + code_phrase += pick(joblist)//Returns a job. + safety -= 1 + if(2) + switch(rand(1,2))//Places or things. + if(1) + code_phrase += pick(drinks) + if(2) + code_phrase += pick(locations) + safety -= 2 + if(3) + switch(rand(1,3))//Nouns, adjectives, verbs. Can be selected more than once. + if(1) + code_phrase += pick(nouns) + if(2) + code_phrase += pick(adjectives) + if(3) + code_phrase += pick(verbs) + if(words==1) + code_phrase += "." + else + code_phrase += ", " + + return code_phrase diff --git a/code/_helpers/sanitize_values.dm b/code/_helpers/sanitize_values.dm index 3d042002691..c74176043c3 100644 --- a/code/_helpers/sanitize_values.dm +++ b/code/_helpers/sanitize_values.dm @@ -1,113 +1,113 @@ -//general stuff -/proc/sanitize_integer(number, min=0, max=1, default=0) - if(isnum(number)) - number = round(number) - if(min <= number && number <= max) - return number - return default - -// Checks if the given input is a valid list index; returns true/false and doesn't change anything. -/proc/is_valid_index(input, list/given_list) - if(!isnum(input)) - return FALSE - if(input != round(input)) - return FALSE - if(input < 1 || input > length(given_list)) - return FALSE - return TRUE - -/proc/sanitize_text(text, default="") - if(istext(text)) - return text - return default - -/proc/sanitize_inlist(value, list/List, default) - if(value in List) return value - if(default) return default - if(List && List.len)return List[1] - - - -//more specialised stuff -/proc/sanitize_gender(gender,neuter=0,plural=0, default="male") - switch(gender) - if(MALE, FEMALE)return gender - if(NEUTER) - if(neuter) return gender - else return default - if(PLURAL) - if(plural) return gender - else return default - return default - -/proc/sanitize_hexcolor(color, default="#000000") - if(!istext(color)) return default - var/len = length(color) - if(len != 7 && len !=4) return default - if(text2ascii(color,1) != 35) return default //35 is the ascii code for "#" - . = "#" - for(var/i=2,i<=len,i++) - 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 - else return default - return . - -//Valid format codes: YY, YEAR, MM, DD, hh, mm, ss, :, -. " " (space). Invalid format will return default. -/proc/sanitize_time(time, default, format = "hh:mm") - if(!istext(time) || !(length(time) == length(format))) - return default - var/fragment = "" - . = list() - for(var/i = 1, i <= length(format), i++) - fragment += copytext(format,i,i+1) - if(fragment in list("YY", "YEAR", "MM", "DD", "hh", "mm", "ss")) - . += sanitize_one_time(copytext(time, i - length(fragment) + 1, i + 1), copytext(default, i - length(fragment) + 1, i + 1), fragment) - fragment = "" - else if(fragment in list(":", "-", " ")) - . += fragment - fragment = "" - if(fragment) - return default //This means the format was improper. - return JOINTEXT(.) - -//Internal proc, expects valid format and text input of equal length to format. -/proc/sanitize_one_time(input, default, format) - var/list/ainput = list() - for(var/i = 1, i <= length(input), i++) - ainput += text2ascii(input, i) - switch(format) - if("YY") - if(!(ainput[1] in 48 to 57) || !(ainput[2] in 48 to 57))//0 to 9 - return (default || "00") - return input - if("YEAR") - for(var/i = 1, i <= 4, i++) - if(!(ainput[i] in 48 to 57))//0 to 9 - return (default || "0000") - return input - if("MM") - var/early = (ainput[1] == 48) && (ainput[2] in 49 to 57) //01 to 09 - var/late = (ainput[1] == 49) && (ainput[2] in 48 to 50) //10 to 12 - if(!(early || late)) - return (default || "01") - return input - if("DD") - var/early = (ainput[1] == 48) && (ainput[2] in 49 to 57) //01 to 09 - var/mid = (ainput[1] in 49 to 50) && (ainput[2] in 48 to 57) //10 to 29 - var/late = (ainput[1] == 51) && (ainput[2] in 48 to 49) //30 to 31 - if(!(early || mid || late)) - return (default || "01") - return input - if("hh") - var/early = (ainput[1] in 48 to 49) && (ainput[2] in 48 to 57) //00 to 19 - var/late = (ainput[1] == 50) && (ainput[2] in 48 to 51) //20 to 23 - if(!(early || late)) - return (default || "00") - return input - if("mm", "ss") - if(!(ainput[1] in 48 to 53) || !(ainput[2] in 48 to 57)) //0 to 5, 0 to 9 - return (default || "00") - return input +//general stuff +/proc/sanitize_integer(number, min=0, max=1, default=0) + if(isnum(number)) + number = round(number) + if(min <= number && number <= max) + return number + return default + +// Checks if the given input is a valid list index; returns true/false and doesn't change anything. +/proc/is_valid_index(input, list/given_list) + if(!isnum(input)) + return FALSE + if(input != round(input)) + return FALSE + if(input < 1 || input > length(given_list)) + return FALSE + return TRUE + +/proc/sanitize_text(text, default="") + if(istext(text)) + return text + return default + +/proc/sanitize_inlist(value, list/List, default) + if(value in List) return value + if(default) return default + if(List && List.len)return List[1] + + + +//more specialised stuff +/proc/sanitize_gender(gender,neuter=0,plural=0, default="male") + switch(gender) + if(MALE, FEMALE)return gender + if(NEUTER) + if(neuter) return gender + else return default + if(PLURAL) + if(plural) return gender + else return default + return default + +/proc/sanitize_hexcolor(color, default="#000000") + if(!istext(color)) return default + var/len = length(color) + if(len != 7 && len !=4) return default + if(text2ascii(color,1) != 35) return default //35 is the ascii code for "#" + . = "#" + for(var/i=2,i<=len,i++) + 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 + else return default + return . + +//Valid format codes: YY, YEAR, MM, DD, hh, mm, ss, :, -. " " (space). Invalid format will return default. +/proc/sanitize_time(time, default, format = "hh:mm") + if(!istext(time) || !(length(time) == length(format))) + return default + var/fragment = "" + . = list() + for(var/i = 1, i <= length(format), i++) + fragment += copytext(format,i,i+1) + if(fragment in list("YY", "YEAR", "MM", "DD", "hh", "mm", "ss")) + . += sanitize_one_time(copytext(time, i - length(fragment) + 1, i + 1), copytext(default, i - length(fragment) + 1, i + 1), fragment) + fragment = "" + else if(fragment in list(":", "-", " ")) + . += fragment + fragment = "" + if(fragment) + return default //This means the format was improper. + return JOINTEXT(.) + +//Internal proc, expects valid format and text input of equal length to format. +/proc/sanitize_one_time(input, default, format) + var/list/ainput = list() + for(var/i = 1, i <= length(input), i++) + ainput += text2ascii(input, i) + switch(format) + if("YY") + if(!(ainput[1] in 48 to 57) || !(ainput[2] in 48 to 57))//0 to 9 + return (default || "00") + return input + if("YEAR") + for(var/i = 1, i <= 4, i++) + if(!(ainput[i] in 48 to 57))//0 to 9 + return (default || "0000") + return input + if("MM") + var/early = (ainput[1] == 48) && (ainput[2] in 49 to 57) //01 to 09 + var/late = (ainput[1] == 49) && (ainput[2] in 48 to 50) //10 to 12 + if(!(early || late)) + return (default || "01") + return input + if("DD") + var/early = (ainput[1] == 48) && (ainput[2] in 49 to 57) //01 to 09 + var/mid = (ainput[1] in 49 to 50) && (ainput[2] in 48 to 57) //10 to 29 + var/late = (ainput[1] == 51) && (ainput[2] in 48 to 49) //30 to 31 + if(!(early || mid || late)) + return (default || "01") + return input + if("hh") + var/early = (ainput[1] in 48 to 49) && (ainput[2] in 48 to 57) //00 to 19 + var/late = (ainput[1] == 50) && (ainput[2] in 48 to 51) //20 to 23 + if(!(early || late)) + return (default || "00") + return input + if("mm", "ss") + if(!(ainput[1] in 48 to 53) || !(ainput[2] in 48 to 57)) //0 to 5, 0 to 9 + return (default || "00") + return input diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm index b5328462454..571bf281ce6 100644 --- a/code/_helpers/text.dm +++ b/code/_helpers/text.dm @@ -1,614 +1,614 @@ -/* - * Holds procs designed to help with filtering text - * Contains groups: - * SQL sanitization - * Text sanitization - * Text searches - * Text modification - * Misc - */ - - -/* - * SQL sanitization - */ - -// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts. -/proc/sanitizeSQL(var/t as text) - var/sqltext = dbcon.Quote(t); - return copytext(sqltext, 2, length(sqltext));//Quote() adds quotes around input, we already do that - -/* - * Text sanitization - */ -// Can be used almost the same way as normal input for text -/proc/clean_input(Message, Title, Default, mob/user=usr) - var/txt = input(user, Message, Title, Default) as text | null - if(txt) - return html_encode(txt) - -//Simply removes < and > and limits the length of the message -/proc/strip_html_simple(var/t,var/limit=MAX_MESSAGE_LEN) - var/list/strip_chars = list("<",">") - t = copytext(t,1,limit) - for(var/char in strip_chars) - var/index = findtext(t, char) - while(index) - t = copytext(t, 1, index) + copytext(t, index+1) - index = findtext(t, char) - return t - -//Runs byond's sanitization proc along-side strip_html_simple -//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' that html_encode() would cause -/proc/adminscrub(var/t,var/limit=MAX_MESSAGE_LEN) - return copytext((html_encode(strip_html_simple(t))),1,limit) - -//Used for preprocessing entered text -/proc/sanitize(var/input, var/max_length = MAX_MESSAGE_LEN, var/encode = 1, var/trim = 1, var/extra = 1) - if(!input) - return - - if(max_length) - input = copytext(input,1,max_length) - - if(extra) - var/temp_input = replace_characters(input, list("\n"=" ","\t"=" "))//one character is replaced by two - if(length(input) < (length(temp_input) - 6))//6 is the number of linebreaks allowed per message - input = replace_characters(temp_input,list(" "=" "))//replace again, this time the double spaces with single ones - - if(encode) - // The below \ escapes have a space inserted to attempt to enable CI auto-checking of span class usage. Please do not remove the space. - //In addition to processing html, html_encode removes byond formatting codes like "\ red", "\ i" and other. - //It is important to avoid double-encode text, it can "break" quotes and some other characters. - //Also, keep in mind that escaped characters don't work in the interface (window titles, lower left corner of the main window, etc.) - input = html_encode(input) - else - //If not need encode text, simply remove < and > - //note: we can also remove here byond formatting codes: 0xFF + next byte - input = replace_characters(input, list("<"=" ", ">"=" ")) - - if(trim) - //Maybe, we need trim text twice? Here and before copytext? - input = trim(input) - - return input - -//Run sanitize(), but remove <, >, " first to prevent displaying them as > < &34; in some places, after html_encode(). -//Best used for sanitize object names, window titles. -//If you have a problem with sanitize() in chat, when quotes and >, < are displayed as html entites - -//this is a problem of double-encode(when & becomes &), use sanitize() with encode=0, but not the sanitizeSafe()! -/proc/sanitizeSafe(var/input, var/max_length = MAX_MESSAGE_LEN, var/encode = 1, var/trim = 1, var/extra = 1) - return sanitize(replace_characters(input, list(">"=" ","<"=" ", "\""="'")), max_length, encode, trim, extra) - -//Filters out undesirable characters from names -/proc/sanitizeName(var/input, var/max_length = MAX_NAME_LEN, var/allow_numbers = 0) - if(!input || length(input) > max_length) - return //Rejects the input if it is null or if it is longer then the max length allowed - - var/number_of_alphanumeric = 0 - var/last_char_group = 0 - var/output = "" - - for(var/i=1, i<=length(input), i++) - var/ascii_char = text2ascii(input,i) - switch(ascii_char) - // A .. Z - if(65 to 90) //Uppercase Letters - output += ascii2text(ascii_char) - number_of_alphanumeric++ - last_char_group = 4 - - // a .. z - if(97 to 122) //Lowercase Letters - if(last_char_group<2) output += ascii2text(ascii_char-32) //Force uppercase first character - else output += ascii2text(ascii_char) - number_of_alphanumeric++ - last_char_group = 4 - - // 0 .. 9 - if(48 to 57) //Numbers - if(!allow_numbers) continue // If allow_numbers is 0, then don't do this. - output += ascii2text(ascii_char) - number_of_alphanumeric++ - last_char_group = 3 - - // ' - . - if(39,45,46) //Common name punctuation - if(!last_char_group) continue - output += ascii2text(ascii_char) - last_char_group = 2 - - // ~ | @ : # $ % & * + - 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) continue - output += ascii2text(ascii_char) - last_char_group = 2 - - //Space - if(32) - if(last_char_group <= 1) continue //suppress double-spaces and spaces at start of string - output += ascii2text(ascii_char) - last_char_group = 1 - else - return - - if(number_of_alphanumeric < 2) return //protects against tiny names like "A" and also names like "' ' ' ' ' ' ' '" - - if(last_char_group == 1) - output = copytext(output,1,length(output)) //removes the last character (in this case a space) - - for(var/bad_name in list("space","floor","wall","r-wall","monkey","unknown","inactive ai","plating")) //prevents these common metagamey names - if(cmptext(output,bad_name)) return //(not case sensitive) - - return output - -//Returns null if there is any bad text in the string -/proc/reject_bad_text(var/text, var/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 � - if(0 to 31) return //more weird stuff - if(32) continue //whitespace - else non_whitespace = 1 - if(non_whitespace) return text //only accepts the text if it has some non-spaces - - -//Old variant. Haven't dared to replace in some places. -/proc/sanitize_old(var/t,var/list/repl_chars = list("\n"="#","\t"="#")) - return html_encode(replace_characters(t,repl_chars)) - - -//Removes a few problematic characters -/proc/sanitize_simple(t,list/repl_chars = list("\n"="#","\t"="#")) - for(var/char in repl_chars) - var/index = findtext(t, char) - while(index) - 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) - return sanitize_simple(t, list("\n"="", "\t"="", "/"="", "\\"="", "?"="", "%"="", "*"="", ":"="", "|"="", "\""="", "<"="", ">"="")) - -/* - * 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) - -/* - * Text modification - */ -/proc/replace_characters(var/t,var/list/repl_chars) - for(var/char in repl_chars) - t = replacetext(t, char, repl_chars[char]) - return t - -//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 '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 - -//Returns a string with reserved characters and spaces before the first letter removed -/proc/trim_left(text) - for (var/i = 1 to length(text)) - if (text2ascii(text, i) > 32) - return copytext(text, i) - return "" - -//Returns a string with reserved characters and spaces after the last letter removed -/proc/trim_right(text) - 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) - return trim_left(trim_right(text)) - -//Returns a string with the first element of the string capitalized. -/proc/capitalize(var/t as text) - return uppertext(copytext(t, 1, 2)) + copytext(t, 2) - -//Returns a unicode string with the first element of the string capitalized. -/proc/capitalize_utf(var/t as text) - return uppertext(copytext_char(t, 1, 2)) + copytext_char(t, 2) - -//This proc strips html properly, remove < > and all text between -//for complete text sanitizing should be used sanitize() -/proc/strip_html_properly(var/input) - if(!input) - return - var/opentag = 1 //These store the position of < and > respectively. - var/closetag = 1 - while(1) - opentag = findtext(input, "<") - closetag = findtext(input, ">") - if(closetag && opentag) - if(closetag < opentag) - input = copytext(input, (closetag + 1)) - else - input = copytext(input, 1, opentag) + copytext(input, (closetag + 1)) - else if(closetag || opentag) - if(opentag) - input = copytext(input, 1, opentag) - else - input = copytext(input, (closetag + 1)) - else - break - - return input - -//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 -/proc/stringmerge(var/text,var/compare,replace = "*") - 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) - //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) - else if(b == replace) //if B is the replacement char - newtext = copytext(newtext,1,i) + a + copytext(newtext, i+1) - else //The lists disagree, Uh-oh! - return 0 - return newtext - -//This proc returns the number of chars of the string that is the character -//This is used for detective work to determine fingerprint completion. -/proc/stringpercent(var/text,character = "*") - if(!text || !character) - return 0 - var/count = 0 - for(var/i = 1, i <= length(text), i++) - var/a = copytext(text,i,i+1) - if(a == character) - count++ - return count - -/proc/reverse_text(var/text = "") - var/new_text = "" - for(var/i = length(text); i > 0; i--) - new_text += copytext(text, i, i+1) - return new_text - -//Used in preferences' SetFlavorText and human's set_flavor verb -//Previews a string of len or less length -/proc/TextPreview(var/string,var/len=40) - if(length(string) <= len) - if(!length(string)) - return "\[...\]" - else - return string - else - return "[copytext_preserve_html(string, 1, 37)]..." - -//alternative copytext() for encoded text, doesn't break html entities (" and other) -/proc/copytext_preserve_html(var/text, var/first, var/last) - return html_encode(copytext(html_decode(text), first, last)) - -//For generating neat chat tag-images -//The icon var could be local in the proc, but it's a waste of resources -// to always create it and then throw it out. -/var/icon/text_tag_icons = 'icons/chattags.dmi' -/var/list/text_tag_cache = list() -/proc/create_text_tag(var/tagname, var/tagdesc = tagname, var/client/C = null) - if(!(C && C.is_preference_enabled(/datum/client_preference/chat_tags))) - return tagdesc - if(!text_tag_cache[tagname]) - var/icon/tag = icon(text_tag_icons, tagname) - text_tag_cache[tagname] = bicon(tag, TRUE, "text_tag") - if(!C.tgui_panel.is_ready() || C.tgui_panel.oldchat) - return "[tagdesc]" - return text_tag_cache[tagname] - -/proc/create_text_tag_old(var/tagname, var/tagdesc = tagname, var/client/C = null) - if(!(C && C.is_preference_enabled(/datum/client_preference/chat_tags))) - return tagdesc - return "[tagdesc]" - -/proc/contains_az09(var/input) - for(var/i=1, i<=length(input), i++) - var/ascii_char = text2ascii(input,i) - switch(ascii_char) - // A .. Z - if(65 to 90) //Uppercase Letters - return 1 - // a .. z - if(97 to 122) //Lowercase Letters - return 1 - - // 0 .. 9 - if(48 to 57) //Numbers - return 1 - return 0 - -/** - * Strip out the special beyond characters for \proper and \improper - * from text that will be sent to the browser. - */ -/proc/strip_improper(var/text) - return replacetext(replacetext(text, "\proper", ""), "\improper", "") - -/proc/pencode2html(t) - t = replacetext(t, "\n", "
") - t = replacetext(t, "\[center\]", "
") - t = replacetext(t, "\[/center\]", "
") - t = replacetext(t, "\[br\]", "
") - t = replacetext(t, "\[b\]", "") - t = replacetext(t, "\[/b\]", "") - t = replacetext(t, "\[i\]", "") - t = replacetext(t, "\[/i\]", "") - t = replacetext(t, "\[u\]", "") - t = replacetext(t, "\[/u\]", "") - t = replacetext(t, "\[time\]", "[stationtime2text()]") - t = replacetext(t, "\[date\]", "[stationdate2text()]") - t = replacetext(t, "\[large\]", "") - t = replacetext(t, "\[/large\]", "") - t = replacetext(t, "\[field\]", "") - t = replacetext(t, "\[h1\]", "

") - t = replacetext(t, "\[/h1\]", "

") - t = replacetext(t, "\[h2\]", "

") - t = replacetext(t, "\[/h2\]", "

") - t = replacetext(t, "\[h3\]", "

") - t = replacetext(t, "\[/h3\]", "

") - t = replacetext(t, "\[*\]", "
  • ") - t = replacetext(t, "\[hr\]", "
    ") - t = replacetext(t, "\[small\]", "") - t = replacetext(t, "\[/small\]", "") - t = replacetext(t, "\[list\]", "
      ") - t = replacetext(t, "\[/list\]", "
    ") - t = replacetext(t, "\[table\]", "") - t = replacetext(t, "\[/table\]", "
    ") - t = replacetext(t, "\[grid\]", "") - t = replacetext(t, "\[/grid\]", "
    ") - t = replacetext(t, "\[row\]", "") - t = replacetext(t, "\[cell\]", "") - t = replacetext(t, "\[logo\]", "") - t = replacetext(t, "\[redlogo\]", "") - t = replacetext(t, "\[sglogo\]", "") - t = replacetext(t, "\[editorbr\]", "") - return t - -//pencode translation to html for tags exclusive to digital files (currently email, nanoword, report editor fields, -//modular scanner data and txt file printing) and prints from them -/proc/digitalPencode2html(var/text) - text = replacetext(text, "\[pre\]", "
    ")
    -	text = replacetext(text, "\[/pre\]", "
    ") - text = replacetext(text, "\[fontred\]", "") // to pass html tag integrity unit test - text = replacetext(text, "\[fontblue\]", "")// to pass html tag integrity unit test - text = replacetext(text, "\[fontgreen\]", "") - text = replacetext(text, "\[/font\]", "") - text = replacetext(text, "\[redacted\]", "R E D A C T E D") - return pencode2html(text) - -//Will kill most formatting; not recommended. -/proc/html2pencode(t) - t = replacetext(t, "
    ", "\[pre\]")
    -	t = replacetext(t, "
    ", "\[/pre\]") - t = replacetext(t, "", "\[fontred\]")// to pass html tag integrity unit test - t = replacetext(t, "", "\[fontblue\]")// to pass html tag integrity unit test - t = replacetext(t, "", "\[fontgreen\]") - t = replacetext(t, "", "\[/font\]") - t = replacetext(t, "
    ", "\[br\]") - t = replacetext(t, "
    ", "\[br\]") - t = replacetext(t, "", "\[b\]") - t = replacetext(t, "", "\[/b\]") - t = replacetext(t, "", "\[i\]") - t = replacetext(t, "", "\[/i\]") - t = replacetext(t, "", "\[u\]") - t = replacetext(t, "", "\[/u\]") - t = replacetext(t, "
    ", "\[center\]") - t = replacetext(t, "
    ", "\[/center\]") - t = replacetext(t, "

    ", "\[h1\]") - t = replacetext(t, "

    ", "\[/h1\]") - t = replacetext(t, "

    ", "\[h2\]") - t = replacetext(t, "

    ", "\[/h2\]") - t = replacetext(t, "

    ", "\[h3\]") - t = replacetext(t, "

    ", "\[/h3\]") - t = replacetext(t, "
  • ", "\[*\]") - t = replacetext(t, "
    ", "\[hr\]") - t = replacetext(t, "
      ", "\[list\]") - t = replacetext(t, "
    ", "\[/list\]") - t = replacetext(t, "", "\[grid\]") - t = replacetext(t, "
    ", "\[/grid\]") - t = replacetext(t, "", "\[row\]") - t = replacetext(t, "", "\[cell\]") - t = replacetext(t, "", "\[logo\]") - t = replacetext(t, "", "\[redlogo\]") - t = replacetext(t, "", "\[sglogo\]") - t = replacetext(t, "", "\[field\]") - t = replacetext(t, "R E D A C T E D", "\[redacted\]") - t = strip_html_properly(t) - return t - -// Random password generator -/proc/GenerateKey() - //Feel free to move to Helpers. - var/newKey - newKey += pick("the", "if", "of", "as", "in", "a", "you", "from", "to", "an", "too", "little", "snow", "dead", "drunk", "rosebud", "duck", "al", "le") - newKey += pick("diamond", "beer", "mushroom", "assistant", "clown", "captain", "twinkie", "security", "nuke", "small", "big", "escape", "yellow", "gloves", "monkey", "engine", "nuclear", "ai") - newKey += pick("1", "2", "3", "4", "5", "6", "7", "8", "9", "0") - return newKey - -//Used for applying byonds text macros to strings that are loaded at runtime -/proc/apply_text_macros(string) - var/next_backslash = findtext(string, "\\") - if(!next_backslash) - return string - - var/leng = length(string) - - var/next_space = findtext(string, " ", next_backslash + 1) - if(!next_space) - next_space = leng - next_backslash - - if(!next_space) //trailing bs - 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) - - //See http://www.byond.com/docs/ref/info.html#/DM/text/macros - switch(macro) - //prefixes/agnostic - if("the") - rest = text("\the []", rest) - if("a") - rest = text("\a []", rest) - if("an") - rest = text("\an []", rest) - if("proper") - rest = text("\proper []", rest) - if("improper") - rest = text("\improper []", rest) - if("roman") - rest = text("\roman []", rest) - //postfixes - if("th") - base = text("[]\th", rest) - if("s") - base = text("[]\s", rest) - if("he") - base = text("[]\he", rest) - if("she") - base = text("[]\she", rest) - if("his") - base = text("[]\his", rest) - if("himself") - base = text("[]\himself", rest) - if("herself") - base = text("[]\herself", rest) - if("hers") - base = text("[]\hers", rest) - - . = base - if(rest) - . += .(rest) - - -#define gender2text(gender) capitalize(gender) - -/** - * Used to get a properly sanitized input. Returns null if cancel is pressed. - * - * Arguments - ** user - Target of the input prompt. - ** message - The text inside of the prompt. - ** title - The window title of the prompt. - ** max_length - If you intend to impose a length limit - default is 1024. - ** no_trim - Prevents the input from being trimmed if you intend to parse newlines or whitespace. -*/ -/proc/stripped_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) - var/user_input = input(user, message, title, default) as text|null - if(isnull(user_input)) // User pressed cancel - return - if(no_trim) - return copytext(html_encode(user_input), 1, max_length) - else - return trim(html_encode(user_input), max_length) //trim is "outside" because html_encode can expand single symbols into multiple symbols (such as turning < into <) - -/** - * Used to get a properly sanitized input in a larger box. Works very similarly to stripped_input. - * - * Arguments - ** user - Target of the input prompt. - ** message - The text inside of the prompt. - ** title - The window title of the prompt. - ** max_length - If you intend to impose a length limit - default is 1024. - ** no_trim - Prevents the input from being trimmed if you intend to parse newlines or whitespace. -*/ -/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) - var/user_input = input(user, message, title, default) as message|null - if(isnull(user_input)) // User pressed cancel - return - if(no_trim) - return copytext(html_encode(user_input), 1, max_length) - else - return trim(html_encode(user_input), max_length) - -//Adds 'char' ahead of 'text' until there are 'count' characters total -/proc/add_leading(text, count, char = " ") - text = "[text]" - var/charcount = count - length_char(text) - var/list/chars_to_add[max(charcount + 1, 0)] - return jointext(chars_to_add, char) + text - -//Adds 'char' behind 'text' until there are 'count' characters total -/proc/add_trailing(text, count, char = " ") - text = "[text]" - var/charcount = count - length_char(text) - var/list/chars_to_add[max(charcount + 1, 0)] - return text + jointext(chars_to_add, char) - -//Readds quotes and apostrophes to HTML-encoded strings -/proc/readd_quotes(var/t) - var/list/repl_chars = list(""" = "\"","'" = "'") - for(var/char in repl_chars) - var/index = findtext(t, char) - while(index) - t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+5) - index = findtext(t, char) - return t - -// Rips out paper HTML but tries to keep it semi-readable. -/proc/paper_html_to_plaintext(paper_text) - paper_text = replacetext(paper_text, "
    ", "-----") - paper_text = replacetext(paper_text, "
  • ", "- ") // This makes ordered lists turn into unordered but fixing that is too much effort. - paper_text = replacetext(paper_text, "
  • ", "\n") - paper_text = replacetext(paper_text, "

    ", "\n") - paper_text = replacetext(paper_text, "
    ", "\n") - paper_text = strip_html_properly(paper_text) // Get rid of everything else entirely. - return paper_text +/* + * Holds procs designed to help with filtering text + * Contains groups: + * SQL sanitization + * Text sanitization + * Text searches + * Text modification + * Misc + */ + + +/* + * SQL sanitization + */ + +// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts. +/proc/sanitizeSQL(var/t as text) + var/sqltext = dbcon.Quote(t); + return copytext(sqltext, 2, length(sqltext));//Quote() adds quotes around input, we already do that + +/* + * Text sanitization + */ +// Can be used almost the same way as normal input for text +/proc/clean_input(Message, Title, Default, mob/user=usr) + var/txt = input(user, Message, Title, Default) as text | null + if(txt) + return html_encode(txt) + +//Simply removes < and > and limits the length of the message +/proc/strip_html_simple(var/t,var/limit=MAX_MESSAGE_LEN) + var/list/strip_chars = list("<",">") + t = copytext(t,1,limit) + for(var/char in strip_chars) + var/index = findtext(t, char) + while(index) + t = copytext(t, 1, index) + copytext(t, index+1) + index = findtext(t, char) + return t + +//Runs byond's sanitization proc along-side strip_html_simple +//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' that html_encode() would cause +/proc/adminscrub(var/t,var/limit=MAX_MESSAGE_LEN) + return copytext((html_encode(strip_html_simple(t))),1,limit) + +//Used for preprocessing entered text +/proc/sanitize(var/input, var/max_length = MAX_MESSAGE_LEN, var/encode = 1, var/trim = 1, var/extra = 1) + if(!input) + return + + if(max_length) + input = copytext(input,1,max_length) + + if(extra) + var/temp_input = replace_characters(input, list("\n"=" ","\t"=" "))//one character is replaced by two + if(length(input) < (length(temp_input) - 6))//6 is the number of linebreaks allowed per message + input = replace_characters(temp_input,list(" "=" "))//replace again, this time the double spaces with single ones + + if(encode) + // The below \ escapes have a space inserted to attempt to enable CI auto-checking of span class usage. Please do not remove the space. + //In addition to processing html, html_encode removes byond formatting codes like "\ red", "\ i" and other. + //It is important to avoid double-encode text, it can "break" quotes and some other characters. + //Also, keep in mind that escaped characters don't work in the interface (window titles, lower left corner of the main window, etc.) + input = html_encode(input) + else + //If not need encode text, simply remove < and > + //note: we can also remove here byond formatting codes: 0xFF + next byte + input = replace_characters(input, list("<"=" ", ">"=" ")) + + if(trim) + //Maybe, we need trim text twice? Here and before copytext? + input = trim(input) + + return input + +//Run sanitize(), but remove <, >, " first to prevent displaying them as > < &34; in some places, after html_encode(). +//Best used for sanitize object names, window titles. +//If you have a problem with sanitize() in chat, when quotes and >, < are displayed as html entites - +//this is a problem of double-encode(when & becomes &), use sanitize() with encode=0, but not the sanitizeSafe()! +/proc/sanitizeSafe(var/input, var/max_length = MAX_MESSAGE_LEN, var/encode = 1, var/trim = 1, var/extra = 1) + return sanitize(replace_characters(input, list(">"=" ","<"=" ", "\""="'")), max_length, encode, trim, extra) + +//Filters out undesirable characters from names +/proc/sanitizeName(var/input, var/max_length = MAX_NAME_LEN, var/allow_numbers = 0) + if(!input || length(input) > max_length) + return //Rejects the input if it is null or if it is longer then the max length allowed + + var/number_of_alphanumeric = 0 + var/last_char_group = 0 + var/output = "" + + for(var/i=1, i<=length(input), i++) + var/ascii_char = text2ascii(input,i) + switch(ascii_char) + // A .. Z + if(65 to 90) //Uppercase Letters + output += ascii2text(ascii_char) + number_of_alphanumeric++ + last_char_group = 4 + + // a .. z + if(97 to 122) //Lowercase Letters + if(last_char_group<2) output += ascii2text(ascii_char-32) //Force uppercase first character + else output += ascii2text(ascii_char) + number_of_alphanumeric++ + last_char_group = 4 + + // 0 .. 9 + if(48 to 57) //Numbers + if(!allow_numbers) continue // If allow_numbers is 0, then don't do this. + output += ascii2text(ascii_char) + number_of_alphanumeric++ + last_char_group = 3 + + // ' - . + if(39,45,46) //Common name punctuation + if(!last_char_group) continue + output += ascii2text(ascii_char) + last_char_group = 2 + + // ~ | @ : # $ % & * + + 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) continue + output += ascii2text(ascii_char) + last_char_group = 2 + + //Space + if(32) + if(last_char_group <= 1) continue //suppress double-spaces and spaces at start of string + output += ascii2text(ascii_char) + last_char_group = 1 + else + return + + if(number_of_alphanumeric < 2) return //protects against tiny names like "A" and also names like "' ' ' ' ' ' ' '" + + if(last_char_group == 1) + output = copytext(output,1,length(output)) //removes the last character (in this case a space) + + for(var/bad_name in list("space","floor","wall","r-wall","monkey","unknown","inactive ai","plating")) //prevents these common metagamey names + if(cmptext(output,bad_name)) return //(not case sensitive) + + return output + +//Returns null if there is any bad text in the string +/proc/reject_bad_text(var/text, var/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 � + if(0 to 31) return //more weird stuff + if(32) continue //whitespace + else non_whitespace = 1 + if(non_whitespace) return text //only accepts the text if it has some non-spaces + + +//Old variant. Haven't dared to replace in some places. +/proc/sanitize_old(var/t,var/list/repl_chars = list("\n"="#","\t"="#")) + return html_encode(replace_characters(t,repl_chars)) + + +//Removes a few problematic characters +/proc/sanitize_simple(t,list/repl_chars = list("\n"="#","\t"="#")) + for(var/char in repl_chars) + var/index = findtext(t, char) + while(index) + 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) + return sanitize_simple(t, list("\n"="", "\t"="", "/"="", "\\"="", "?"="", "%"="", "*"="", ":"="", "|"="", "\""="", "<"="", ">"="")) + +/* + * 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) + +/* + * Text modification + */ +/proc/replace_characters(var/t,var/list/repl_chars) + for(var/char in repl_chars) + t = replacetext(t, char, repl_chars[char]) + return t + +//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 '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 + +//Returns a string with reserved characters and spaces before the first letter removed +/proc/trim_left(text) + for (var/i = 1 to length(text)) + if (text2ascii(text, i) > 32) + return copytext(text, i) + return "" + +//Returns a string with reserved characters and spaces after the last letter removed +/proc/trim_right(text) + 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) + return trim_left(trim_right(text)) + +//Returns a string with the first element of the string capitalized. +/proc/capitalize(var/t as text) + return uppertext(copytext(t, 1, 2)) + copytext(t, 2) + +//Returns a unicode string with the first element of the string capitalized. +/proc/capitalize_utf(var/t as text) + return uppertext(copytext_char(t, 1, 2)) + copytext_char(t, 2) + +//This proc strips html properly, remove < > and all text between +//for complete text sanitizing should be used sanitize() +/proc/strip_html_properly(var/input) + if(!input) + return + var/opentag = 1 //These store the position of < and > respectively. + var/closetag = 1 + while(1) + opentag = findtext(input, "<") + closetag = findtext(input, ">") + if(closetag && opentag) + if(closetag < opentag) + input = copytext(input, (closetag + 1)) + else + input = copytext(input, 1, opentag) + copytext(input, (closetag + 1)) + else if(closetag || opentag) + if(opentag) + input = copytext(input, 1, opentag) + else + input = copytext(input, (closetag + 1)) + else + break + + return input + +//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 +/proc/stringmerge(var/text,var/compare,replace = "*") + 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) + //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) + else if(b == replace) //if B is the replacement char + newtext = copytext(newtext,1,i) + a + copytext(newtext, i+1) + else //The lists disagree, Uh-oh! + return 0 + return newtext + +//This proc returns the number of chars of the string that is the character +//This is used for detective work to determine fingerprint completion. +/proc/stringpercent(var/text,character = "*") + if(!text || !character) + return 0 + var/count = 0 + for(var/i = 1, i <= length(text), i++) + var/a = copytext(text,i,i+1) + if(a == character) + count++ + return count + +/proc/reverse_text(var/text = "") + var/new_text = "" + for(var/i = length(text); i > 0; i--) + new_text += copytext(text, i, i+1) + return new_text + +//Used in preferences' SetFlavorText and human's set_flavor verb +//Previews a string of len or less length +/proc/TextPreview(var/string,var/len=40) + if(length(string) <= len) + if(!length(string)) + return "\[...\]" + else + return string + else + return "[copytext_preserve_html(string, 1, 37)]..." + +//alternative copytext() for encoded text, doesn't break html entities (" and other) +/proc/copytext_preserve_html(var/text, var/first, var/last) + return html_encode(copytext(html_decode(text), first, last)) + +//For generating neat chat tag-images +//The icon var could be local in the proc, but it's a waste of resources +// to always create it and then throw it out. +/var/icon/text_tag_icons = 'icons/chattags.dmi' +/var/list/text_tag_cache = list() +/proc/create_text_tag(var/tagname, var/tagdesc = tagname, var/client/C = null) + if(!(C && C.is_preference_enabled(/datum/client_preference/chat_tags))) + return tagdesc + if(!text_tag_cache[tagname]) + var/icon/tag = icon(text_tag_icons, tagname) + text_tag_cache[tagname] = bicon(tag, TRUE, "text_tag") + if(!C.tgui_panel.is_ready() || C.tgui_panel.oldchat) + return "[tagdesc]" + return text_tag_cache[tagname] + +/proc/create_text_tag_old(var/tagname, var/tagdesc = tagname, var/client/C = null) + if(!(C && C.is_preference_enabled(/datum/client_preference/chat_tags))) + return tagdesc + return "[tagdesc]" + +/proc/contains_az09(var/input) + for(var/i=1, i<=length(input), i++) + var/ascii_char = text2ascii(input,i) + switch(ascii_char) + // A .. Z + if(65 to 90) //Uppercase Letters + return 1 + // a .. z + if(97 to 122) //Lowercase Letters + return 1 + + // 0 .. 9 + if(48 to 57) //Numbers + return 1 + return 0 + +/** + * Strip out the special beyond characters for \proper and \improper + * from text that will be sent to the browser. + */ +/proc/strip_improper(var/text) + return replacetext(replacetext(text, "\proper", ""), "\improper", "") + +/proc/pencode2html(t) + t = replacetext(t, "\n", "
    ") + t = replacetext(t, "\[center\]", "

    ") + t = replacetext(t, "\[/center\]", "
    ") + t = replacetext(t, "\[br\]", "
    ") + t = replacetext(t, "\[b\]", "") + t = replacetext(t, "\[/b\]", "") + t = replacetext(t, "\[i\]", "") + t = replacetext(t, "\[/i\]", "") + t = replacetext(t, "\[u\]", "") + t = replacetext(t, "\[/u\]", "") + t = replacetext(t, "\[time\]", "[stationtime2text()]") + t = replacetext(t, "\[date\]", "[stationdate2text()]") + t = replacetext(t, "\[large\]", "") + t = replacetext(t, "\[/large\]", "") + t = replacetext(t, "\[field\]", "") + t = replacetext(t, "\[h1\]", "

    ") + t = replacetext(t, "\[/h1\]", "

    ") + t = replacetext(t, "\[h2\]", "

    ") + t = replacetext(t, "\[/h2\]", "

    ") + t = replacetext(t, "\[h3\]", "

    ") + t = replacetext(t, "\[/h3\]", "

    ") + t = replacetext(t, "\[*\]", "
  • ") + t = replacetext(t, "\[hr\]", "
    ") + t = replacetext(t, "\[small\]", "") + t = replacetext(t, "\[/small\]", "") + t = replacetext(t, "\[list\]", "
      ") + t = replacetext(t, "\[/list\]", "
    ") + t = replacetext(t, "\[table\]", "") + t = replacetext(t, "\[/table\]", "
    ") + t = replacetext(t, "\[grid\]", "") + t = replacetext(t, "\[/grid\]", "
    ") + t = replacetext(t, "\[row\]", "") + t = replacetext(t, "\[cell\]", "") + t = replacetext(t, "\[logo\]", "") + t = replacetext(t, "\[redlogo\]", "") + t = replacetext(t, "\[sglogo\]", "") + t = replacetext(t, "\[editorbr\]", "") + return t + +//pencode translation to html for tags exclusive to digital files (currently email, nanoword, report editor fields, +//modular scanner data and txt file printing) and prints from them +/proc/digitalPencode2html(var/text) + text = replacetext(text, "\[pre\]", "
    ")
    +	text = replacetext(text, "\[/pre\]", "
    ") + text = replacetext(text, "\[fontred\]", "") // to pass html tag integrity unit test + text = replacetext(text, "\[fontblue\]", "")// to pass html tag integrity unit test + text = replacetext(text, "\[fontgreen\]", "") + text = replacetext(text, "\[/font\]", "") + text = replacetext(text, "\[redacted\]", "R E D A C T E D") + return pencode2html(text) + +//Will kill most formatting; not recommended. +/proc/html2pencode(t) + t = replacetext(t, "
    ", "\[pre\]")
    +	t = replacetext(t, "
    ", "\[/pre\]") + t = replacetext(t, "", "\[fontred\]")// to pass html tag integrity unit test + t = replacetext(t, "", "\[fontblue\]")// to pass html tag integrity unit test + t = replacetext(t, "", "\[fontgreen\]") + t = replacetext(t, "", "\[/font\]") + t = replacetext(t, "
    ", "\[br\]") + t = replacetext(t, "
    ", "\[br\]") + t = replacetext(t, "", "\[b\]") + t = replacetext(t, "", "\[/b\]") + t = replacetext(t, "", "\[i\]") + t = replacetext(t, "", "\[/i\]") + t = replacetext(t, "", "\[u\]") + t = replacetext(t, "", "\[/u\]") + t = replacetext(t, "
    ", "\[center\]") + t = replacetext(t, "
    ", "\[/center\]") + t = replacetext(t, "

    ", "\[h1\]") + t = replacetext(t, "

    ", "\[/h1\]") + t = replacetext(t, "

    ", "\[h2\]") + t = replacetext(t, "

    ", "\[/h2\]") + t = replacetext(t, "

    ", "\[h3\]") + t = replacetext(t, "

    ", "\[/h3\]") + t = replacetext(t, "
  • ", "\[*\]") + t = replacetext(t, "
    ", "\[hr\]") + t = replacetext(t, "
      ", "\[list\]") + t = replacetext(t, "
    ", "\[/list\]") + t = replacetext(t, "", "\[grid\]") + t = replacetext(t, "
    ", "\[/grid\]") + t = replacetext(t, "", "\[row\]") + t = replacetext(t, "", "\[cell\]") + t = replacetext(t, "", "\[logo\]") + t = replacetext(t, "", "\[redlogo\]") + t = replacetext(t, "", "\[sglogo\]") + t = replacetext(t, "", "\[field\]") + t = replacetext(t, "R E D A C T E D", "\[redacted\]") + t = strip_html_properly(t) + return t + +// Random password generator +/proc/GenerateKey() + //Feel free to move to Helpers. + var/newKey + newKey += pick("the", "if", "of", "as", "in", "a", "you", "from", "to", "an", "too", "little", "snow", "dead", "drunk", "rosebud", "duck", "al", "le") + newKey += pick("diamond", "beer", "mushroom", "assistant", "clown", "captain", "twinkie", "security", "nuke", "small", "big", "escape", "yellow", "gloves", "monkey", "engine", "nuclear", "ai") + newKey += pick("1", "2", "3", "4", "5", "6", "7", "8", "9", "0") + return newKey + +//Used for applying byonds text macros to strings that are loaded at runtime +/proc/apply_text_macros(string) + var/next_backslash = findtext(string, "\\") + if(!next_backslash) + return string + + var/leng = length(string) + + var/next_space = findtext(string, " ", next_backslash + 1) + if(!next_space) + next_space = leng - next_backslash + + if(!next_space) //trailing bs + 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) + + //See http://www.byond.com/docs/ref/info.html#/DM/text/macros + switch(macro) + //prefixes/agnostic + if("the") + rest = text("\the []", rest) + if("a") + rest = text("\a []", rest) + if("an") + rest = text("\an []", rest) + if("proper") + rest = text("\proper []", rest) + if("improper") + rest = text("\improper []", rest) + if("roman") + rest = text("\roman []", rest) + //postfixes + if("th") + base = text("[]\th", rest) + if("s") + base = text("[]\s", rest) + if("he") + base = text("[]\he", rest) + if("she") + base = text("[]\she", rest) + if("his") + base = text("[]\his", rest) + if("himself") + base = text("[]\himself", rest) + if("herself") + base = text("[]\herself", rest) + if("hers") + base = text("[]\hers", rest) + + . = base + if(rest) + . += .(rest) + + +#define gender2text(gender) capitalize(gender) + +/** + * Used to get a properly sanitized input. Returns null if cancel is pressed. + * + * Arguments + ** user - Target of the input prompt. + ** message - The text inside of the prompt. + ** title - The window title of the prompt. + ** max_length - If you intend to impose a length limit - default is 1024. + ** no_trim - Prevents the input from being trimmed if you intend to parse newlines or whitespace. +*/ +/proc/stripped_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) + var/user_input = input(user, message, title, default) as text|null + if(isnull(user_input)) // User pressed cancel + return + if(no_trim) + return copytext(html_encode(user_input), 1, max_length) + else + return trim(html_encode(user_input), max_length) //trim is "outside" because html_encode can expand single symbols into multiple symbols (such as turning < into <) + +/** + * Used to get a properly sanitized input in a larger box. Works very similarly to stripped_input. + * + * Arguments + ** user - Target of the input prompt. + ** message - The text inside of the prompt. + ** title - The window title of the prompt. + ** max_length - If you intend to impose a length limit - default is 1024. + ** no_trim - Prevents the input from being trimmed if you intend to parse newlines or whitespace. +*/ +/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) + var/user_input = input(user, message, title, default) as message|null + if(isnull(user_input)) // User pressed cancel + return + if(no_trim) + return copytext(html_encode(user_input), 1, max_length) + else + return trim(html_encode(user_input), max_length) + +//Adds 'char' ahead of 'text' until there are 'count' characters total +/proc/add_leading(text, count, char = " ") + text = "[text]" + var/charcount = count - length_char(text) + var/list/chars_to_add[max(charcount + 1, 0)] + return jointext(chars_to_add, char) + text + +//Adds 'char' behind 'text' until there are 'count' characters total +/proc/add_trailing(text, count, char = " ") + text = "[text]" + var/charcount = count - length_char(text) + var/list/chars_to_add[max(charcount + 1, 0)] + return text + jointext(chars_to_add, char) + +//Readds quotes and apostrophes to HTML-encoded strings +/proc/readd_quotes(var/t) + var/list/repl_chars = list(""" = "\"","'" = "'") + for(var/char in repl_chars) + var/index = findtext(t, char) + while(index) + t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+5) + index = findtext(t, char) + return t + +// Rips out paper HTML but tries to keep it semi-readable. +/proc/paper_html_to_plaintext(paper_text) + paper_text = replacetext(paper_text, "
    ", "-----") + paper_text = replacetext(paper_text, "
  • ", "- ") // This makes ordered lists turn into unordered but fixing that is too much effort. + paper_text = replacetext(paper_text, "
  • ", "\n") + paper_text = replacetext(paper_text, "

    ", "\n") + paper_text = replacetext(paper_text, "
    ", "\n") + paper_text = strip_html_properly(paper_text) // Get rid of everything else entirely. + return paper_text diff --git a/code/_helpers/type2type.dm b/code/_helpers/type2type.dm index e21d973b1b6..8bd45bbfc76 100644 --- a/code/_helpers/type2type.dm +++ b/code/_helpers/type2type.dm @@ -1,425 +1,425 @@ -/* - * Holds procs designed to change one type of value, into another. - * Contains: - * hex2num & num2hex - * file2list - * angle2dir - */ - -// Returns an integer given a hexadecimal number string as input. -/proc/hex2num(hex) - if (!istext(hex)) - return - - var/num = 0 - var/power = 1 - var/i = length(hex) - - while (i) - var/char = text2ascii(hex, i) - switch(char) - if(48) // 0 -- do nothing - if(49 to 57) num += (char - 48) * power // 1-9 - if(97, 65) num += power * 10 // A - if(98, 66) num += power * 11 // B - if(99, 67) num += power * 12 // C - if(100, 68) num += power * 13 // D - if(101, 69) num += power * 14 // E - if(102, 70) num += power * 15 // F - else - return - power *= 16 - i-- - return num - -// Returns the hex value of a number given a value assumed to be a base-ten value -/proc/num2hex(num, padlength) - var/global/list/hexdigits = list("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F") - - . = "" - while(num > 0) - var/hexdigit = hexdigits[(num & 0xF) + 1] - . = "[hexdigit][.]" - num >>= 4 //go to the next half-byte - - //pad with zeroes - var/left = padlength - length(.) - while (left-- > 0) - . = "0[.]" - -/proc/text2numlist(text, delimiter="\n") - var/list/num_list = list() - for(var/x in splittext(text, delimiter)) - num_list += text2num(x) - return num_list - -// Splits the text of a file at seperator and returns them in a list. -/proc/file2list(filename, seperator="\n") - return splittext(return_file_text(filename),seperator) - -// Turns a direction into text -/proc/num2dir(direction) - switch (direction) - if (1.0) return NORTH - if (2.0) return SOUTH - if (4.0) return EAST - if (8.0) return WEST - else - to_world_log("UNKNOWN DIRECTION: [direction]") - -// Turns a direction into text -/proc/dir2text(direction) - switch (direction) - if (NORTH) return "north" - if (SOUTH) return "south" - if (EAST) return "east" - if (WEST) return "west" - if (NORTHEAST) return "northeast" - if (SOUTHEAST) return "southeast" - if (NORTHWEST) return "northwest" - if (SOUTHWEST) return "southwest" - if (UP) return "up" - if (DOWN) return "down" - -// Turns text into proper directions -/proc/text2dir(direction) - switch (uppertext(direction)) - if ("NORTH") return 1 - if ("SOUTH") return 2 - if ("EAST") return 4 - if ("WEST") return 8 - if ("NORTHEAST") return 5 - if ("NORTHWEST") return 9 - if ("SOUTHEAST") return 6 - if ("SOUTHWEST") return 10 - -// Turns a direction into text showing all bits set -/proc/dirs2text(direction) - if(!direction) - return "" - var/list/dirs = list() - if(direction & NORTH) - dirs += "NORTH" - if(direction & SOUTH) - dirs += "SOUTH" - if(direction & EAST) - dirs += "EAST" - if(direction & WEST) - dirs += "WEST" - if(direction & UP) - dirs += "UP" - if(direction & DOWN) - dirs += "DOWN" - return dirs.Join(" ") - -// Converts an angle (degrees) into an ss13 direction -/proc/angle2dir(var/degree) - degree = (degree + 22.5) % 365 // 22.5 = 45 / 2 - if (degree < 45) return NORTH - if (degree < 90) return NORTHEAST - if (degree < 135) return EAST - if (degree < 180) return SOUTHEAST - if (degree < 225) return SOUTH - if (degree < 270) return SOUTHWEST - if (degree < 315) return WEST - return NORTH|WEST - -// Returns the north-zero clockwise angle in degrees, given a direction -/proc/dir2angle(var/D) - switch (D) - if (NORTH) return 0 - if (SOUTH) return 180 - if (EAST) return 90 - if (WEST) return 270 - if (NORTHEAST) return 45 - if (SOUTHEAST) return 135 - if (NORTHWEST) return 315 - if (SOUTHWEST) return 225 - -// Converts a blend_mode constant to one acceptable to icon.Blend() -/proc/blendMode2iconMode(blend_mode) - switch (blend_mode) - if (BLEND_MULTIPLY) return ICON_MULTIPLY - if (BLEND_ADD) return ICON_ADD - if (BLEND_SUBTRACT) return ICON_SUBTRACT - else return ICON_OVERLAY - -// Converts a rights bitfield into a string -/proc/rights2text(rights,seperator="") - if (rights & R_BUILDMODE) . += "[seperator]+BUILDMODE" - if (rights & R_ADMIN) . += "[seperator]+ADMIN" - if (rights & R_BAN) . += "[seperator]+BAN" - if (rights & R_FUN) . += "[seperator]+FUN" - if (rights & R_SERVER) . += "[seperator]+SERVER" - if (rights & R_DEBUG) . += "[seperator]+DEBUG" - if (rights & R_POSSESS) . += "[seperator]+POSSESS" - if (rights & R_PERMISSIONS) . += "[seperator]+PERMISSIONS" - if (rights & R_STEALTH) . += "[seperator]+STEALTH" - if (rights & R_REJUVINATE) . += "[seperator]+REJUVINATE" - if (rights & R_VAREDIT) . += "[seperator]+VAREDIT" - if (rights & R_SOUNDS) . += "[seperator]+SOUND" - if (rights & R_SPAWN) . += "[seperator]+SPAWN" - if (rights & R_MOD) . += "[seperator]+MODERATOR" - if (rights & R_EVENT) . += "[seperator]+EVENT" - return . - -// Converts a hexadecimal color (e.g. #FF0050) to a list of numbers for red, green, and blue (e.g. list(255,0,80) ). -/proc/hex2rgb(hex) - // Strips the starting #, in case this is ever supplied without one, so everything doesn't break. - if(findtext(hex,"#",1,2)) - hex = copytext(hex, 2) - return list(hex2rgb_r(hex), hex2rgb_g(hex), hex2rgb_b(hex)) - -// The three procs below require that the '#' part of the hex be stripped, which hex2rgb() does automatically. -/proc/hex2rgb_r(hex) - var/hex_to_work_on = copytext(hex,1,3) - return hex2num(hex_to_work_on) - -/proc/hex2rgb_g(hex) - var/hex_to_work_on = copytext(hex,3,5) - return hex2num(hex_to_work_on) - -/proc/hex2rgb_b(hex) - var/hex_to_work_on = copytext(hex,5,7) - return hex2num(hex_to_work_on) - -// heat2color functions. Adapted from: http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ -/proc/heat2color(temp) - return rgb(heat2color_r(temp), heat2color_g(temp), heat2color_b(temp)) - -/proc/heat2color_r(temp) - temp /= 100 - if(temp <= 66) - . = 255 - else - . = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592)) - -/proc/heat2color_g(temp) - temp /= 100 - if(temp <= 66) - . = max(0, min(255, 99.4708025861 * log(temp) - 161.1195681661)) - else - . = max(0, min(255, 288.1221695283 * ((temp - 60) ** -0.0755148492))) - -/proc/heat2color_b(temp) - temp /= 100 - if(temp >= 66) - . = 255 - else - if(temp <= 16) - . = 0 - else - . = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307)) - -// Very ugly, BYOND doesn't support unix time and rounding errors make it really hard to convert it to BYOND time. -// returns "YYYY-MM-DD" by default -/proc/unix2date(timestamp, seperator = "-") - if(timestamp < 0) - return 0 //Do not accept negative values - - var/const/dayInSeconds = 86400 //60secs*60mins*24hours - var/const/daysInYear = 365 //Non Leap Year - var/const/daysInLYear = daysInYear + 1//Leap year - var/days = round(timestamp / dayInSeconds) //Days passed since UNIX Epoc - var/year = 1970 //Unix Epoc begins 1970-01-01 - var/tmpDays = days + 1 //If passed (timestamp < dayInSeconds), it will return 0, so add 1 - var/monthsInDays = list() //Months will be in here ***Taken from the PHP source code*** - var/month = 1 //This will be the returned MONTH NUMBER. - var/day //This will be the returned day number. - - while(tmpDays > daysInYear) //Start adding years to 1970 - year++ - if(isLeap(year)) - tmpDays -= daysInLYear - else - tmpDays -= daysInYear - - if(isLeap(year)) //The year is a leap year - monthsInDays = list(-1,30,59,90,120,151,181,212,243,273,304,334) - else - monthsInDays = list(0,31,59,90,120,151,181,212,243,273,304,334) - - var/mDays = 0; - var/monthIndex = 0; - - for(var/m in monthsInDays) - monthIndex++ - if(tmpDays > m) - mDays = m - month = monthIndex - - day = tmpDays - mDays //Setup the date - - return "[year][seperator][((month < 10) ? "0[month]" : month)][seperator][((day < 10) ? "0[day]" : day)]" - -/proc/isLeap(y) - return ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) - -//Takes a string and a datum -//The string is well, obviously the string being checked -//The datum is used as a source for var names, to check validity -//Otherwise every single word could technically be a variable! -/proc/string2listofvars(var/t_string, var/datum/var_source) - if(!t_string || !var_source) - return list() - - . = list() - - var/var_found = findtext(t_string,"\[") //Not the actual variables, just a generic "should we even bother" check - if(var_found) - //Find var names - - // "A dog said hi [name]!" - // splittext() --> list("A dog said hi ","name]!" - // jointext() --> "A dog said hi name]!" - // splittext() --> list("A","dog","said","hi","name]!") - - t_string = replacetext(t_string,"\[","\[ ")//Necessary to resolve "word[var_name]" scenarios - var/list/list_value = splittext(t_string,"\[") - var/intermediate_stage = jointext(list_value, null) - - list_value = splittext(intermediate_stage," ") - for(var/value in list_value) - if(findtext(value,"]")) - value = splittext(value,"]") //"name]!" --> list("name","!") - for(var/A in value) - if(var_source.vars.Find(A)) - . += A - -/proc/get_end_section_of_type(type) - var/strtype = "[type]" - var/delim_pos = findlasttext(strtype, "/") - if(delim_pos == 0) - return strtype - return copytext(strtype, delim_pos) - -// Concatenates a list of strings into a single string. A seperator may optionally be provided. -/proc/list2text(list/ls, sep) - if (ls.len <= 1) // Early-out code for empty or singleton lists. - return ls.len ? ls[1] : "" - - var/l = ls.len // Made local for sanic speed. - var/i = 0 // Incremented every time a list index is accessed. - - if (sep != null) - // Macros expand to long argument lists like so: sep, ls[++i], sep, ls[++i], sep, ls[++i], etc... - #define S1 sep, ls[++i] - #define S4 S1, S1, S1, S1 - #define S16 S4, S4, S4, S4 - #define S64 S16, S16, S16, S16 - - . = "[ls[++i]]" // Make sure the initial element is converted to text. - - // Having the small concatenations come before the large ones boosted speed by an average of at least 5%. - if (l-1 & 0x01) // 'i' will always be 1 here. - . = text("[][][]", ., S1) // Append 1 element if the remaining elements are not a multiple of 2. - if (l-i & 0x02) - . = text("[][][][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4. - if (l-i & 0x04) - . = text("[][][][][][][][][]", ., S4) // And so on.... - if (l-i & 0x08) - . = text("[][][][][][][][][][][][][][][][][]", ., S4, S4) - if (l-i & 0x10) - . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16) - if (l-i & 0x20) - . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16) - if (l-i & 0x40) - . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64) - while (l > i) // Chomp through the rest of the list, 128 elements at a time. - . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64) - - #undef S64 - #undef S16 - #undef S4 - #undef S1 - else - // Macros expand to long argument lists like so: ls[++i], ls[++i], ls[++i], etc... - #define S1 ls[++i] - #define S4 S1, S1, S1, S1 - #define S16 S4, S4, S4, S4 - #define S64 S16, S16, S16, S16 - - . = "[ls[++i]]" // Make sure the initial element is converted to text. - - if (l-1 & 0x01) // 'i' will always be 1 here. - . += S1 // Append 1 element if the remaining elements are not a multiple of 2. - if (l-i & 0x02) - . = text("[][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4. - if (l-i & 0x04) - . = text("[][][][][]", ., S4) // And so on... - if (l-i & 0x08) - . = text("[][][][][][][][][]", ., S4, S4) - if (l-i & 0x10) - . = text("[][][][][][][][][][][][][][][][][]", ., S16) - if (l-i & 0x20) - . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16) - if (l-i & 0x40) - . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64) - while (l > i) // Chomp through the rest of the list, 128 elements at a time. - . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64) - - #undef S64 - #undef S16 - #undef S4 - #undef S1 - -// Converts a string into a list by splitting the string at each delimiter found. (discarding the seperator) -/proc/text2list(text, delimiter="\n") - var/delim_len = length(delimiter) - if (delim_len < 1) - return list(text) - - . = list() - var/last_found = 1 - var/found - - do - found = findtext(text, delimiter, last_found, 0) - . += copytext(text, last_found, found) - last_found = found + delim_len - while (found) - -/proc/type2parent(child) - var/string_type = "[child]" - var/last_slash = findlasttext(string_type, "/") - if (last_slash != 1) - return text2path(copytext(string_type, 1, last_slash)) - switch (child) - if (/datum) - return null - if (/obj, /mob) - return /atom/movable - if (/area, /turf) - return /atom - else - return /datum - - -//checks if a file exists and contains text -//returns text as a string if these conditions are met -/proc/safe_file2text(filename, error_on_invalid_return = TRUE) - try - if(fexists(filename)) - . = file2text(filename) - if(!. && error_on_invalid_return) - error("File empty ([filename])") - else if(error_on_invalid_return) - error("File not found ([filename])") - catch(var/exception/E) - if(error_on_invalid_return) - error("Exception when loading file as string: [E]") +/* + * Holds procs designed to change one type of value, into another. + * Contains: + * hex2num & num2hex + * file2list + * angle2dir + */ + +// Returns an integer given a hexadecimal number string as input. +/proc/hex2num(hex) + if (!istext(hex)) + return + + var/num = 0 + var/power = 1 + var/i = length(hex) + + while (i) + var/char = text2ascii(hex, i) + switch(char) + if(48) // 0 -- do nothing + if(49 to 57) num += (char - 48) * power // 1-9 + if(97, 65) num += power * 10 // A + if(98, 66) num += power * 11 // B + if(99, 67) num += power * 12 // C + if(100, 68) num += power * 13 // D + if(101, 69) num += power * 14 // E + if(102, 70) num += power * 15 // F + else + return + power *= 16 + i-- + return num + +// Returns the hex value of a number given a value assumed to be a base-ten value +/proc/num2hex(num, padlength) + var/global/list/hexdigits = list("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F") + + . = "" + while(num > 0) + var/hexdigit = hexdigits[(num & 0xF) + 1] + . = "[hexdigit][.]" + num >>= 4 //go to the next half-byte + + //pad with zeroes + var/left = padlength - length(.) + while (left-- > 0) + . = "0[.]" + +/proc/text2numlist(text, delimiter="\n") + var/list/num_list = list() + for(var/x in splittext(text, delimiter)) + num_list += text2num(x) + return num_list + +// Splits the text of a file at seperator and returns them in a list. +/proc/file2list(filename, seperator="\n") + return splittext(return_file_text(filename),seperator) + +// Turns a direction into text +/proc/num2dir(direction) + switch (direction) + if (1.0) return NORTH + if (2.0) return SOUTH + if (4.0) return EAST + if (8.0) return WEST + else + to_world_log("UNKNOWN DIRECTION: [direction]") + +// Turns a direction into text +/proc/dir2text(direction) + switch (direction) + if (NORTH) return "north" + if (SOUTH) return "south" + if (EAST) return "east" + if (WEST) return "west" + if (NORTHEAST) return "northeast" + if (SOUTHEAST) return "southeast" + if (NORTHWEST) return "northwest" + if (SOUTHWEST) return "southwest" + if (UP) return "up" + if (DOWN) return "down" + +// Turns text into proper directions +/proc/text2dir(direction) + switch (uppertext(direction)) + if ("NORTH") return 1 + if ("SOUTH") return 2 + if ("EAST") return 4 + if ("WEST") return 8 + if ("NORTHEAST") return 5 + if ("NORTHWEST") return 9 + if ("SOUTHEAST") return 6 + if ("SOUTHWEST") return 10 + +// Turns a direction into text showing all bits set +/proc/dirs2text(direction) + if(!direction) + return "" + var/list/dirs = list() + if(direction & NORTH) + dirs += "NORTH" + if(direction & SOUTH) + dirs += "SOUTH" + if(direction & EAST) + dirs += "EAST" + if(direction & WEST) + dirs += "WEST" + if(direction & UP) + dirs += "UP" + if(direction & DOWN) + dirs += "DOWN" + return dirs.Join(" ") + +// Converts an angle (degrees) into an ss13 direction +/proc/angle2dir(var/degree) + degree = (degree + 22.5) % 365 // 22.5 = 45 / 2 + if (degree < 45) return NORTH + if (degree < 90) return NORTHEAST + if (degree < 135) return EAST + if (degree < 180) return SOUTHEAST + if (degree < 225) return SOUTH + if (degree < 270) return SOUTHWEST + if (degree < 315) return WEST + return NORTH|WEST + +// Returns the north-zero clockwise angle in degrees, given a direction +/proc/dir2angle(var/D) + switch (D) + if (NORTH) return 0 + if (SOUTH) return 180 + if (EAST) return 90 + if (WEST) return 270 + if (NORTHEAST) return 45 + if (SOUTHEAST) return 135 + if (NORTHWEST) return 315 + if (SOUTHWEST) return 225 + +// Converts a blend_mode constant to one acceptable to icon.Blend() +/proc/blendMode2iconMode(blend_mode) + switch (blend_mode) + if (BLEND_MULTIPLY) return ICON_MULTIPLY + if (BLEND_ADD) return ICON_ADD + if (BLEND_SUBTRACT) return ICON_SUBTRACT + else return ICON_OVERLAY + +// Converts a rights bitfield into a string +/proc/rights2text(rights,seperator="") + if (rights & R_BUILDMODE) . += "[seperator]+BUILDMODE" + if (rights & R_ADMIN) . += "[seperator]+ADMIN" + if (rights & R_BAN) . += "[seperator]+BAN" + if (rights & R_FUN) . += "[seperator]+FUN" + if (rights & R_SERVER) . += "[seperator]+SERVER" + if (rights & R_DEBUG) . += "[seperator]+DEBUG" + if (rights & R_POSSESS) . += "[seperator]+POSSESS" + if (rights & R_PERMISSIONS) . += "[seperator]+PERMISSIONS" + if (rights & R_STEALTH) . += "[seperator]+STEALTH" + if (rights & R_REJUVINATE) . += "[seperator]+REJUVINATE" + if (rights & R_VAREDIT) . += "[seperator]+VAREDIT" + if (rights & R_SOUNDS) . += "[seperator]+SOUND" + if (rights & R_SPAWN) . += "[seperator]+SPAWN" + if (rights & R_MOD) . += "[seperator]+MODERATOR" + if (rights & R_EVENT) . += "[seperator]+EVENT" + return . + +// Converts a hexadecimal color (e.g. #FF0050) to a list of numbers for red, green, and blue (e.g. list(255,0,80) ). +/proc/hex2rgb(hex) + // Strips the starting #, in case this is ever supplied without one, so everything doesn't break. + if(findtext(hex,"#",1,2)) + hex = copytext(hex, 2) + return list(hex2rgb_r(hex), hex2rgb_g(hex), hex2rgb_b(hex)) + +// The three procs below require that the '#' part of the hex be stripped, which hex2rgb() does automatically. +/proc/hex2rgb_r(hex) + var/hex_to_work_on = copytext(hex,1,3) + return hex2num(hex_to_work_on) + +/proc/hex2rgb_g(hex) + var/hex_to_work_on = copytext(hex,3,5) + return hex2num(hex_to_work_on) + +/proc/hex2rgb_b(hex) + var/hex_to_work_on = copytext(hex,5,7) + return hex2num(hex_to_work_on) + +// heat2color functions. Adapted from: http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ +/proc/heat2color(temp) + return rgb(heat2color_r(temp), heat2color_g(temp), heat2color_b(temp)) + +/proc/heat2color_r(temp) + temp /= 100 + if(temp <= 66) + . = 255 + else + . = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592)) + +/proc/heat2color_g(temp) + temp /= 100 + if(temp <= 66) + . = max(0, min(255, 99.4708025861 * log(temp) - 161.1195681661)) + else + . = max(0, min(255, 288.1221695283 * ((temp - 60) ** -0.0755148492))) + +/proc/heat2color_b(temp) + temp /= 100 + if(temp >= 66) + . = 255 + else + if(temp <= 16) + . = 0 + else + . = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307)) + +// Very ugly, BYOND doesn't support unix time and rounding errors make it really hard to convert it to BYOND time. +// returns "YYYY-MM-DD" by default +/proc/unix2date(timestamp, seperator = "-") + if(timestamp < 0) + return 0 //Do not accept negative values + + var/const/dayInSeconds = 86400 //60secs*60mins*24hours + var/const/daysInYear = 365 //Non Leap Year + var/const/daysInLYear = daysInYear + 1//Leap year + var/days = round(timestamp / dayInSeconds) //Days passed since UNIX Epoc + var/year = 1970 //Unix Epoc begins 1970-01-01 + var/tmpDays = days + 1 //If passed (timestamp < dayInSeconds), it will return 0, so add 1 + var/monthsInDays = list() //Months will be in here ***Taken from the PHP source code*** + var/month = 1 //This will be the returned MONTH NUMBER. + var/day //This will be the returned day number. + + while(tmpDays > daysInYear) //Start adding years to 1970 + year++ + if(isLeap(year)) + tmpDays -= daysInLYear + else + tmpDays -= daysInYear + + if(isLeap(year)) //The year is a leap year + monthsInDays = list(-1,30,59,90,120,151,181,212,243,273,304,334) + else + monthsInDays = list(0,31,59,90,120,151,181,212,243,273,304,334) + + var/mDays = 0; + var/monthIndex = 0; + + for(var/m in monthsInDays) + monthIndex++ + if(tmpDays > m) + mDays = m + month = monthIndex + + day = tmpDays - mDays //Setup the date + + return "[year][seperator][((month < 10) ? "0[month]" : month)][seperator][((day < 10) ? "0[day]" : day)]" + +/proc/isLeap(y) + return ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) + +//Takes a string and a datum +//The string is well, obviously the string being checked +//The datum is used as a source for var names, to check validity +//Otherwise every single word could technically be a variable! +/proc/string2listofvars(var/t_string, var/datum/var_source) + if(!t_string || !var_source) + return list() + + . = list() + + var/var_found = findtext(t_string,"\[") //Not the actual variables, just a generic "should we even bother" check + if(var_found) + //Find var names + + // "A dog said hi [name]!" + // splittext() --> list("A dog said hi ","name]!" + // jointext() --> "A dog said hi name]!" + // splittext() --> list("A","dog","said","hi","name]!") + + t_string = replacetext(t_string,"\[","\[ ")//Necessary to resolve "word[var_name]" scenarios + var/list/list_value = splittext(t_string,"\[") + var/intermediate_stage = jointext(list_value, null) + + list_value = splittext(intermediate_stage," ") + for(var/value in list_value) + if(findtext(value,"]")) + value = splittext(value,"]") //"name]!" --> list("name","!") + for(var/A in value) + if(var_source.vars.Find(A)) + . += A + +/proc/get_end_section_of_type(type) + var/strtype = "[type]" + var/delim_pos = findlasttext(strtype, "/") + if(delim_pos == 0) + return strtype + return copytext(strtype, delim_pos) + +// Concatenates a list of strings into a single string. A seperator may optionally be provided. +/proc/list2text(list/ls, sep) + if (ls.len <= 1) // Early-out code for empty or singleton lists. + return ls.len ? ls[1] : "" + + var/l = ls.len // Made local for sanic speed. + var/i = 0 // Incremented every time a list index is accessed. + + if (sep != null) + // Macros expand to long argument lists like so: sep, ls[++i], sep, ls[++i], sep, ls[++i], etc... + #define S1 sep, ls[++i] + #define S4 S1, S1, S1, S1 + #define S16 S4, S4, S4, S4 + #define S64 S16, S16, S16, S16 + + . = "[ls[++i]]" // Make sure the initial element is converted to text. + + // Having the small concatenations come before the large ones boosted speed by an average of at least 5%. + if (l-1 & 0x01) // 'i' will always be 1 here. + . = text("[][][]", ., S1) // Append 1 element if the remaining elements are not a multiple of 2. + if (l-i & 0x02) + . = text("[][][][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4. + if (l-i & 0x04) + . = text("[][][][][][][][][]", ., S4) // And so on.... + if (l-i & 0x08) + . = text("[][][][][][][][][][][][][][][][][]", ., S4, S4) + if (l-i & 0x10) + . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16) + if (l-i & 0x20) + . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16) + if (l-i & 0x40) + . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64) + while (l > i) // Chomp through the rest of the list, 128 elements at a time. + . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64) + + #undef S64 + #undef S16 + #undef S4 + #undef S1 + else + // Macros expand to long argument lists like so: ls[++i], ls[++i], ls[++i], etc... + #define S1 ls[++i] + #define S4 S1, S1, S1, S1 + #define S16 S4, S4, S4, S4 + #define S64 S16, S16, S16, S16 + + . = "[ls[++i]]" // Make sure the initial element is converted to text. + + if (l-1 & 0x01) // 'i' will always be 1 here. + . += S1 // Append 1 element if the remaining elements are not a multiple of 2. + if (l-i & 0x02) + . = text("[][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4. + if (l-i & 0x04) + . = text("[][][][][]", ., S4) // And so on... + if (l-i & 0x08) + . = text("[][][][][][][][][]", ., S4, S4) + if (l-i & 0x10) + . = text("[][][][][][][][][][][][][][][][][]", ., S16) + if (l-i & 0x20) + . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16) + if (l-i & 0x40) + . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64) + while (l > i) // Chomp through the rest of the list, 128 elements at a time. + . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64) + + #undef S64 + #undef S16 + #undef S4 + #undef S1 + +// Converts a string into a list by splitting the string at each delimiter found. (discarding the seperator) +/proc/text2list(text, delimiter="\n") + var/delim_len = length(delimiter) + if (delim_len < 1) + return list(text) + + . = list() + var/last_found = 1 + var/found + + do + found = findtext(text, delimiter, last_found, 0) + . += copytext(text, last_found, found) + last_found = found + delim_len + while (found) + +/proc/type2parent(child) + var/string_type = "[child]" + var/last_slash = findlasttext(string_type, "/") + if (last_slash != 1) + return text2path(copytext(string_type, 1, last_slash)) + switch (child) + if (/datum) + return null + if (/obj, /mob) + return /atom/movable + if (/area, /turf) + return /atom + else + return /datum + + +//checks if a file exists and contains text +//returns text as a string if these conditions are met +/proc/safe_file2text(filename, error_on_invalid_return = TRUE) + try + if(fexists(filename)) + . = file2text(filename) + if(!. && error_on_invalid_return) + error("File empty ([filename])") + else if(error_on_invalid_return) + error("File not found ([filename])") + catch(var/exception/E) + if(error_on_invalid_return) + error("Exception when loading file as string: [E]") diff --git a/code/_helpers/visual_filters.dm b/code/_helpers/visual_filters.dm index 60aef7c9081..cc3b02dc60e 100644 --- a/code/_helpers/visual_filters.dm +++ b/code/_helpers/visual_filters.dm @@ -1,383 +1,383 @@ -#define ICON_NOT_SET "Not Set" -//This is stored as a nested list instead of datums or whatever because it json encodes nicely for usage in tgui -GLOBAL_LIST_INIT(master_filter_info, list( - "alpha" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "icon" = ICON_NOT_SET, - "render_source" = "", - "flags" = 0 - ), - "flags" = list( - "MASK_INVERSE" = MASK_INVERSE, - "MASK_SWAP" = MASK_SWAP - ) - ), - "angular_blur" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "size" = 1 - ) - ), - /* Not supported because making a proper matrix editor on the frontend would be a huge dick pain. - Uncomment if you ever implement it - "color" = list( - "defaults" = list( - "color" = matrix(), - "space" = FILTER_COLOR_RGB - ) - ), - */ - "displace" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "size" = null, - "icon" = ICON_NOT_SET, - "render_source" = "" - ) - ), - "drop_shadow" = list( - "defaults" = list( - "x" = 1, - "y" = -1, - "size" = 1, - "offset" = 0, - "color" = COLOR_HALF_TRANSPARENT_BLACK - ) - ), - "blur" = list( - "defaults" = list( - "size" = 1 - ) - ), - "layer" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "icon" = ICON_NOT_SET, - "render_source" = "", - "flags" = FILTER_OVERLAY, - "color" = "", - "transform" = null, - "blend_mode" = BLEND_DEFAULT - ) - ), - "motion_blur" = list( - "defaults" = list( - "x" = 0, - "y" = 0 - ) - ), - "outline" = list( - "defaults" = list( - "size" = 0, - "color" = COLOR_BLACK, - "flags" = NONE - ), - "flags" = list( - "OUTLINE_SHARP" = OUTLINE_SHARP, - "OUTLINE_SQUARE" = OUTLINE_SQUARE - ) - ), - "radial_blur" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "size" = 0.01 - ) - ), - "rays" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "size" = 16, - "color" = COLOR_WHITE, - "offset" = 0, - "density" = 10, - "threshold" = 0.5, - "factor" = 0, - "flags" = FILTER_OVERLAY | FILTER_UNDERLAY - ), - "flags" = list( - "FILTER_OVERLAY" = FILTER_OVERLAY, - "FILTER_UNDERLAY" = FILTER_UNDERLAY - ) - ), - "ripple" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "size" = 1, - "repeat" = 2, - "radius" = 0, - "falloff" = 1, - "flags" = NONE - ), - "flags" = list( - "WAVE_BOUNDED" = WAVE_BOUNDED - ) - ), - "wave" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "size" = 1, - "offset" = 0, - "flags" = NONE - ), - "flags" = list( - "WAVE_SIDEWAYS" = WAVE_SIDEWAYS, - "WAVE_BOUNDED" = WAVE_BOUNDED - ) - ) -)) - -#undef ICON_NOT_SET - -//Helpers to generate lists for filter helpers -//This is the only practical way of writing these that actually produces sane lists -/proc/alpha_mask_filter(x, y, icon/icon, render_source, flags) - . = list("type" = "alpha") - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - if(!isnull(icon)) - .["icon"] = icon - if(!isnull(render_source)) - .["render_source"] = render_source - if(!isnull(flags)) - .["flags"] = flags - -/proc/angular_blur_filter(x, y, size) - . = list("type" = "angular_blur") - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - if(!isnull(size)) - .["size"] = size - -/proc/color_matrix_filter(matrix/in_matrix, space) - . = list("type" = "color") - .["color"] = in_matrix - if(!isnull(space)) - .["space"] = space - -/proc/displacement_map_filter(icon, render_source, x, y, size = 32) - . = list("type" = "displace") - if(!isnull(icon)) - .["icon"] = icon - if(!isnull(render_source)) - .["render_source"] = render_source - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - if(!isnull(size)) - .["size"] = size - -/proc/drop_shadow_filter(x, y, size, offset, color) - . = list("type" = "drop_shadow") - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - if(!isnull(size)) - .["size"] = size - if(!isnull(offset)) - .["offset"] = offset - if(!isnull(color)) - .["color"] = color - -/proc/gauss_blur_filter(size) - . = list("type" = "blur") - if(!isnull(size)) - .["size"] = size - -/proc/layering_filter(icon, render_source, x, y, flags, color, transform, blend_mode) - . = list("type" = "layer") - if(!isnull(icon)) - .["icon"] = icon - if(!isnull(render_source)) - .["render_source"] = render_source - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - if(!isnull(color)) - .["color"] = color - if(!isnull(flags)) - .["flags"] = flags - if(!isnull(transform)) - .["transform"] = transform - if(!isnull(blend_mode)) - .["blend_mode"] = blend_mode - -/proc/motion_blur_filter(x, y) - . = list("type" = "motion_blur") - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - -/proc/outline_filter(size, color, flags) - . = list("type" = "outline") - if(!isnull(size)) - .["size"] = size - if(!isnull(color)) - .["color"] = color - if(!isnull(flags)) - .["flags"] = flags - -/proc/radial_blur_filter(size, x, y) - . = list("type" = "radial_blur") - if(!isnull(size)) - .["size"] = size - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - -/proc/rays_filter(size, color, offset, density, threshold, factor, x, y, flags) - . = list("type" = "rays") - if(!isnull(size)) - .["size"] = size - if(!isnull(color)) - .["color"] = color - if(!isnull(offset)) - .["offset"] = offset - if(!isnull(density)) - .["density"] = density - if(!isnull(threshold)) - .["threshold"] = threshold - if(!isnull(factor)) - .["factor"] = factor - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - if(!isnull(flags)) - .["flags"] = flags - -/proc/ripple_filter(radius, size, falloff, repeat, x, y, flags) - . = list("type" = "ripple") - if(!isnull(radius)) - .["radius"] = radius - if(!isnull(size)) - .["size"] = size - if(!isnull(falloff)) - .["falloff"] = falloff - if(!isnull(repeat)) - .["repeat"] = repeat - if(!isnull(flags)) - .["flags"] = flags - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - -/proc/wave_filter(x, y, size, offset, flags) - . = list("type" = "wave") - if(!isnull(size)) - .["size"] = size - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - if(!isnull(offset)) - .["offset"] = offset - if(!isnull(flags)) - .["flags"] = flags - -/atom/proc/add_filter(name,priority,list/params) - LAZYINITLIST(filter_data) - var/list/p = params.Copy() - p["priority"] = priority - filter_data[name] = p - update_filters() - -/atom/proc/update_filters() - filters = null - filter_data = sortTim(filter_data, GLOBAL_PROC_REF(cmp_filter_data_priority), TRUE) - for(var/f in filter_data) - var/list/data = filter_data[f] - var/list/arguments = data.Copy() - arguments -= "priority" - filters += filter(arglist(arguments)) - UNSETEMPTY(filter_data) - -/atom/proc/transition_filter(name, time, list/new_params, easing, loop) - var/filter = get_filter(name) - if(!filter) - return - - var/list/old_filter_data = filter_data[name] - - var/list/params = old_filter_data.Copy() - for(var/thing in new_params) - params[thing] = new_params[thing] - - animate(filter, new_params, time = time, easing = easing, loop = loop) - for(var/param in params) - filter_data[name][param] = params[param] - -/atom/proc/change_filter_priority(name, new_priority) - if(!filter_data || !filter_data[name]) - return - - filter_data[name]["priority"] = new_priority - update_filters() - -/obj/item/update_filters() - . = ..() - /* Will port this from TG - for(var/datum/action/A as anything in actions) - A.UpdateButtonIcon() - */ - -/atom/proc/get_filter(name) - if(filter_data && filter_data[name]) - return filters[filter_data.Find(name)] - -/atom/proc/remove_filter(name_or_names) - if(!filter_data) - return - - var/list/names = islist(name_or_names) ? name_or_names : list(name_or_names) - - for(var/name in names) - if(filter_data[name]) - filter_data -= name - update_filters() - -/atom/proc/clear_filters() - filter_data = null - filters = null - -/proc/apply_wibbly_filters(atom/in_atom, length) - for(var/i in 1 to 7) - //This is a very baffling and strange way of doing this but I am just preserving old functionality - var/X - var/Y - var/rsq - do - X = 60*rand() - 30 - Y = 60*rand() - 30 - rsq = X*X + Y*Y - while(rsq<100 || rsq>900) // Yeah let's just loop infinitely due to bad luck what's the worst that could happen? - var/random_roll = rand() - in_atom.add_filter("wibbly-[i]", 5, wave_filter(x = X, y = Y, size = rand() * 2.5 + 0.5, offset = random_roll)) - var/filter = in_atom.get_filter("wibbly-[i]") - animate(filter, offset = random_roll, time = 0, loop = -1, flags = ANIMATION_PARALLEL) - animate(offset = random_roll - 1, time = rand() * 20 + 10) - -/proc/remove_wibbly_filters(atom/in_atom) - var/filter - for(var/i in 1 to 7) - filter = in_atom.get_filter("wibbly-[i]") - animate(filter) - in_atom.remove_filter("wibbly-[i]") +#define ICON_NOT_SET "Not Set" +//This is stored as a nested list instead of datums or whatever because it json encodes nicely for usage in tgui +GLOBAL_LIST_INIT(master_filter_info, list( + "alpha" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "icon" = ICON_NOT_SET, + "render_source" = "", + "flags" = 0 + ), + "flags" = list( + "MASK_INVERSE" = MASK_INVERSE, + "MASK_SWAP" = MASK_SWAP + ) + ), + "angular_blur" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 1 + ) + ), + /* Not supported because making a proper matrix editor on the frontend would be a huge dick pain. + Uncomment if you ever implement it + "color" = list( + "defaults" = list( + "color" = matrix(), + "space" = FILTER_COLOR_RGB + ) + ), + */ + "displace" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = null, + "icon" = ICON_NOT_SET, + "render_source" = "" + ) + ), + "drop_shadow" = list( + "defaults" = list( + "x" = 1, + "y" = -1, + "size" = 1, + "offset" = 0, + "color" = COLOR_HALF_TRANSPARENT_BLACK + ) + ), + "blur" = list( + "defaults" = list( + "size" = 1 + ) + ), + "layer" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "icon" = ICON_NOT_SET, + "render_source" = "", + "flags" = FILTER_OVERLAY, + "color" = "", + "transform" = null, + "blend_mode" = BLEND_DEFAULT + ) + ), + "motion_blur" = list( + "defaults" = list( + "x" = 0, + "y" = 0 + ) + ), + "outline" = list( + "defaults" = list( + "size" = 0, + "color" = COLOR_BLACK, + "flags" = NONE + ), + "flags" = list( + "OUTLINE_SHARP" = OUTLINE_SHARP, + "OUTLINE_SQUARE" = OUTLINE_SQUARE + ) + ), + "radial_blur" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 0.01 + ) + ), + "rays" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 16, + "color" = COLOR_WHITE, + "offset" = 0, + "density" = 10, + "threshold" = 0.5, + "factor" = 0, + "flags" = FILTER_OVERLAY | FILTER_UNDERLAY + ), + "flags" = list( + "FILTER_OVERLAY" = FILTER_OVERLAY, + "FILTER_UNDERLAY" = FILTER_UNDERLAY + ) + ), + "ripple" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 1, + "repeat" = 2, + "radius" = 0, + "falloff" = 1, + "flags" = NONE + ), + "flags" = list( + "WAVE_BOUNDED" = WAVE_BOUNDED + ) + ), + "wave" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 1, + "offset" = 0, + "flags" = NONE + ), + "flags" = list( + "WAVE_SIDEWAYS" = WAVE_SIDEWAYS, + "WAVE_BOUNDED" = WAVE_BOUNDED + ) + ) +)) + +#undef ICON_NOT_SET + +//Helpers to generate lists for filter helpers +//This is the only practical way of writing these that actually produces sane lists +/proc/alpha_mask_filter(x, y, icon/icon, render_source, flags) + . = list("type" = "alpha") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(icon)) + .["icon"] = icon + if(!isnull(render_source)) + .["render_source"] = render_source + if(!isnull(flags)) + .["flags"] = flags + +/proc/angular_blur_filter(x, y, size) + . = list("type" = "angular_blur") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(size)) + .["size"] = size + +/proc/color_matrix_filter(matrix/in_matrix, space) + . = list("type" = "color") + .["color"] = in_matrix + if(!isnull(space)) + .["space"] = space + +/proc/displacement_map_filter(icon, render_source, x, y, size = 32) + . = list("type" = "displace") + if(!isnull(icon)) + .["icon"] = icon + if(!isnull(render_source)) + .["render_source"] = render_source + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(size)) + .["size"] = size + +/proc/drop_shadow_filter(x, y, size, offset, color) + . = list("type" = "drop_shadow") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(size)) + .["size"] = size + if(!isnull(offset)) + .["offset"] = offset + if(!isnull(color)) + .["color"] = color + +/proc/gauss_blur_filter(size) + . = list("type" = "blur") + if(!isnull(size)) + .["size"] = size + +/proc/layering_filter(icon, render_source, x, y, flags, color, transform, blend_mode) + . = list("type" = "layer") + if(!isnull(icon)) + .["icon"] = icon + if(!isnull(render_source)) + .["render_source"] = render_source + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(color)) + .["color"] = color + if(!isnull(flags)) + .["flags"] = flags + if(!isnull(transform)) + .["transform"] = transform + if(!isnull(blend_mode)) + .["blend_mode"] = blend_mode + +/proc/motion_blur_filter(x, y) + . = list("type" = "motion_blur") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + +/proc/outline_filter(size, color, flags) + . = list("type" = "outline") + if(!isnull(size)) + .["size"] = size + if(!isnull(color)) + .["color"] = color + if(!isnull(flags)) + .["flags"] = flags + +/proc/radial_blur_filter(size, x, y) + . = list("type" = "radial_blur") + if(!isnull(size)) + .["size"] = size + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + +/proc/rays_filter(size, color, offset, density, threshold, factor, x, y, flags) + . = list("type" = "rays") + if(!isnull(size)) + .["size"] = size + if(!isnull(color)) + .["color"] = color + if(!isnull(offset)) + .["offset"] = offset + if(!isnull(density)) + .["density"] = density + if(!isnull(threshold)) + .["threshold"] = threshold + if(!isnull(factor)) + .["factor"] = factor + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(flags)) + .["flags"] = flags + +/proc/ripple_filter(radius, size, falloff, repeat, x, y, flags) + . = list("type" = "ripple") + if(!isnull(radius)) + .["radius"] = radius + if(!isnull(size)) + .["size"] = size + if(!isnull(falloff)) + .["falloff"] = falloff + if(!isnull(repeat)) + .["repeat"] = repeat + if(!isnull(flags)) + .["flags"] = flags + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + +/proc/wave_filter(x, y, size, offset, flags) + . = list("type" = "wave") + if(!isnull(size)) + .["size"] = size + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(offset)) + .["offset"] = offset + if(!isnull(flags)) + .["flags"] = flags + +/atom/proc/add_filter(name,priority,list/params) + LAZYINITLIST(filter_data) + var/list/p = params.Copy() + p["priority"] = priority + filter_data[name] = p + update_filters() + +/atom/proc/update_filters() + filters = null + filter_data = sortTim(filter_data, GLOBAL_PROC_REF(cmp_filter_data_priority), TRUE) + for(var/f in filter_data) + var/list/data = filter_data[f] + var/list/arguments = data.Copy() + arguments -= "priority" + filters += filter(arglist(arguments)) + UNSETEMPTY(filter_data) + +/atom/proc/transition_filter(name, time, list/new_params, easing, loop) + var/filter = get_filter(name) + if(!filter) + return + + var/list/old_filter_data = filter_data[name] + + var/list/params = old_filter_data.Copy() + for(var/thing in new_params) + params[thing] = new_params[thing] + + animate(filter, new_params, time = time, easing = easing, loop = loop) + for(var/param in params) + filter_data[name][param] = params[param] + +/atom/proc/change_filter_priority(name, new_priority) + if(!filter_data || !filter_data[name]) + return + + filter_data[name]["priority"] = new_priority + update_filters() + +/obj/item/update_filters() + . = ..() + /* Will port this from TG + for(var/datum/action/A as anything in actions) + A.UpdateButtonIcon() + */ + +/atom/proc/get_filter(name) + if(filter_data && filter_data[name]) + return filters[filter_data.Find(name)] + +/atom/proc/remove_filter(name_or_names) + if(!filter_data) + return + + var/list/names = islist(name_or_names) ? name_or_names : list(name_or_names) + + for(var/name in names) + if(filter_data[name]) + filter_data -= name + update_filters() + +/atom/proc/clear_filters() + filter_data = null + filters = null + +/proc/apply_wibbly_filters(atom/in_atom, length) + for(var/i in 1 to 7) + //This is a very baffling and strange way of doing this but I am just preserving old functionality + var/X + var/Y + var/rsq + do + X = 60*rand() - 30 + Y = 60*rand() - 30 + rsq = X*X + Y*Y + while(rsq<100 || rsq>900) // Yeah let's just loop infinitely due to bad luck what's the worst that could happen? + var/random_roll = rand() + in_atom.add_filter("wibbly-[i]", 5, wave_filter(x = X, y = Y, size = rand() * 2.5 + 0.5, offset = random_roll)) + var/filter = in_atom.get_filter("wibbly-[i]") + animate(filter, offset = random_roll, time = 0, loop = -1, flags = ANIMATION_PARALLEL) + animate(offset = random_roll - 1, time = rand() * 20 + 10) + +/proc/remove_wibbly_filters(atom/in_atom) + var/filter + for(var/i in 1 to 7) + filter = in_atom.get_filter("wibbly-[i]") + animate(filter) + in_atom.remove_filter("wibbly-[i]") diff --git a/code/_macros.dm b/code/_macros.dm index 169a8978819..b37d24fa0df 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -1,47 +1,47 @@ -#define span(class, text) ("[text]") - -#define get_turf(A) get_step(A,0) - -#define get_x(A) (get_step(A, 0)?.x || 0) - -#define get_y(A) (get_step(A, 0)?.y || 0) - -#define get_z(A) (get_step(A, 0)?.z || 0) - -#define RANDOM_BLOOD_TYPE pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+") - -// #define to_chat(target, message) target << message Not anymore! -//#define to_chat to_chat_filename=__FILE__;to_chat_line=__LINE__;to_chat_src=src;__to_chat -//#define to_chat __to_chat -#define to_world(message) to_chat(world, message) -#define to_world_log(message) world.log << message -// TODO - Baystation has this log to crazy places. For now lets just world.log, but maybe look into it later. -#define log_world(message) to_world_log(message) -#define to_file(file_entry, source_var) file_entry << source_var -#define from_file(file_entry, target_var) file_entry >> target_var -#define show_browser(target, browser_content, browser_name) target << browse(browser_content, browser_name) -#define send_rsc(target, rsc_content, rsc_name) target << browse_rsc(rsc_content, rsc_name) -#define open_link(target, url) target << link(url) - -// From TG, might be useful to have. -// Didn't port SEND_TEXT() since to_chat() appears to serve the same purpose. -#define DIRECT_OUTPUT(A, B) A << B -#define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image) -#define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound) -//#define WRITE_LOG is in logging.dm - -#define CanInteract(user, state) (CanUseTopic(user, state) == STATUS_INTERACTIVE) - -#define qdel_null(x) if(x) { qdel(x) ; x = null } - -#define sequential_id(key) uniqueness_repository.Generate(/datum/uniqueness_generator/id_sequential, key) - -#define random_id(key,min_id,max_id) uniqueness_repository.Generate(/datum/uniqueness_generator/id_random, key, min_id, max_id) - -#define ARGS_DEBUG log_debug("[__FILE__] - [__LINE__]") ; for(var/arg in args) { log_debug("\t[log_info_line(arg)]") } - -#define WORLD_ICON_SIZE 32 //Needed for the R-UST port - -#define PIXEL_MULTIPLIER WORLD_ICON_SIZE/32 //Needed for the R-UST port - -#define JOINTEXT(X) jointext(X, null) +#define span(class, text) ("[text]") + +#define get_turf(A) get_step(A,0) + +#define get_x(A) (get_step(A, 0)?.x || 0) + +#define get_y(A) (get_step(A, 0)?.y || 0) + +#define get_z(A) (get_step(A, 0)?.z || 0) + +#define RANDOM_BLOOD_TYPE pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+") + +// #define to_chat(target, message) target << message Not anymore! +//#define to_chat to_chat_filename=__FILE__;to_chat_line=__LINE__;to_chat_src=src;__to_chat +//#define to_chat __to_chat +#define to_world(message) to_chat(world, message) +#define to_world_log(message) world.log << message +// TODO - Baystation has this log to crazy places. For now lets just world.log, but maybe look into it later. +#define log_world(message) to_world_log(message) +#define to_file(file_entry, source_var) file_entry << source_var +#define from_file(file_entry, target_var) file_entry >> target_var +#define show_browser(target, browser_content, browser_name) target << browse(browser_content, browser_name) +#define send_rsc(target, rsc_content, rsc_name) target << browse_rsc(rsc_content, rsc_name) +#define open_link(target, url) target << link(url) + +// From TG, might be useful to have. +// Didn't port SEND_TEXT() since to_chat() appears to serve the same purpose. +#define DIRECT_OUTPUT(A, B) A << B +#define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image) +#define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound) +//#define WRITE_LOG is in logging.dm + +#define CanInteract(user, state) (CanUseTopic(user, state) == STATUS_INTERACTIVE) + +#define qdel_null(x) if(x) { qdel(x) ; x = null } + +#define sequential_id(key) uniqueness_repository.Generate(/datum/uniqueness_generator/id_sequential, key) + +#define random_id(key,min_id,max_id) uniqueness_repository.Generate(/datum/uniqueness_generator/id_random, key, min_id, max_id) + +#define ARGS_DEBUG log_debug("[__FILE__] - [__LINE__]") ; for(var/arg in args) { log_debug("\t[log_info_line(arg)]") } + +#define WORLD_ICON_SIZE 32 //Needed for the R-UST port + +#define PIXEL_MULTIPLIER WORLD_ICON_SIZE/32 //Needed for the R-UST port + +#define JOINTEXT(X) jointext(X, null) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 7892b91a55c..16a35b18eac 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -191,6 +191,10 @@ if(stat) return 0 + // prevent picking up items while being in them + if(istype(A, /obj/item) && A == loc) + return 0 + return 1 /* diff --git a/code/_onclick/hud/spell_screen_objects.dm b/code/_onclick/hud/spell_screen_objects.dm index 1215218ebe1..9f8f97bcce8 100644 --- a/code/_onclick/hud/spell_screen_objects.dm +++ b/code/_onclick/hud/spell_screen_objects.dm @@ -1,220 +1,220 @@ -/obj/screen/movable/spell_master - name = "Spells" - icon = 'icons/mob/screen_spells.dmi' - icon_state = "wiz_spell_ready" - var/list/obj/screen/spell/spell_objects = list() - var/showing = 0 - - var/open_state = "master_open" - var/closed_state = "master_closed" - - screen_loc = ui_spell_master - - var/mob/spell_holder - -/obj/screen/movable/spell_master/Destroy() - . = ..() - for(var/obj/screen/spell/spells in spell_objects) - spells.spellmaster = null - spell_objects.Cut() - if(spell_holder) - spell_holder.spell_masters -= src - if(spell_holder.client && spell_holder.client.screen) - spell_holder.client.screen -= src - spell_holder = null - -/obj/screen/movable/spell_master/MouseDrop() - if(showing) - return - - return ..() - -/obj/screen/movable/spell_master/Click() - if(!spell_objects.len) - qdel(src) - return - - toggle_open() - -/obj/screen/movable/spell_master/proc/toggle_open(var/forced_state = 0) - if(showing && (forced_state != 2)) - for(var/obj/screen/spell/O in spell_objects) - if(spell_holder && spell_holder.client) - spell_holder.client.screen -= O - O.handle_icon_updates = 0 - showing = 0 - overlays.len = 0 - overlays.Add(closed_state) - else if(forced_state != 1) - open_spellmaster() - update_spells(1) - showing = 1 - overlays.len = 0 - overlays.Add(open_state) - -/obj/screen/movable/spell_master/proc/open_spellmaster() - var/list/screen_loc_xy = splittext(screen_loc,",") - - //Create list of X offsets - var/list/screen_loc_X = splittext(screen_loc_xy[1],":") - var/x_position = decode_screen_X(screen_loc_X[1]) - var/x_pix = screen_loc_X[2] - - //Create list of Y offsets - var/list/screen_loc_Y = splittext(screen_loc_xy[2],":") - var/y_position = decode_screen_Y(screen_loc_Y[1]) - var/y_pix = screen_loc_Y[2] - - for(var/i = 1; i <= spell_objects.len; i++) - var/obj/screen/spell/S = spell_objects[i] - var/xpos = x_position + (x_position < 8 ? 1 : -1)*(i%7) - var/ypos = y_position + (y_position < 8 ? round(i/7) : -round(i/7)) - if(spell_holder && spell_holder.client) - S.screen_loc = "[encode_screen_X(xpos)]:[x_pix],[encode_screen_Y(ypos)]:[y_pix]" - spell_holder.client.screen += S - S.handle_icon_updates = 1 - -/obj/screen/movable/spell_master/proc/add_spell(var/spell/spell) - if(!spell) return - - if(spell.connected_button) //we have one already, for some reason - if(spell.connected_button in spell_objects) - return - else - spell_objects.Add(spell.connected_button) - if(spell_holder.client) - toggle_open(2) - return - - if(spell.spell_flags & NO_BUTTON) //no button to add if we don't get one - return - - var/obj/screen/spell/newscreen = new /obj/screen/spell() - newscreen.spellmaster = src - newscreen.spell = spell - - spell.connected_button = newscreen - - if(!spell.override_base) //if it's not set, we do basic checks - if(spell.spell_flags & CONSTRUCT_CHECK) - newscreen.spell_base = "const" //construct spells - else - newscreen.spell_base = "wiz" //wizard spells - else - newscreen.spell_base = spell.override_base - newscreen.name = spell.name - newscreen.update_charge(1) - spell_objects.Add(newscreen) - if(spell_holder.client) - toggle_open(2) //forces the icons to refresh on screen - -/obj/screen/movable/spell_master/proc/remove_spell(var/spell/spell) - qdel(spell.connected_button) - - spell.connected_button = null - - if(spell_objects.len) - toggle_open(showing + 1) - else - qdel(src) - -/obj/screen/movable/spell_master/proc/silence_spells(var/amount) - for(var/obj/screen/spell/spell in spell_objects) - spell.spell.silenced = amount - spell.update_charge(1) - -/obj/screen/movable/spell_master/proc/update_spells(forced = 0, mob/user) - if(user && user.client) - if(!(src in user.client.screen)) - user.client.screen += src - for(var/obj/screen/spell/spell in spell_objects) - spell.update_charge(forced) - -/obj/screen/movable/spell_master/genetic - name = "Mutant Powers" - icon_state = "genetic_spell_ready" - - open_state = "genetics_open" - closed_state = "genetics_closed" - - screen_loc = ui_genetic_master - -/obj/screen/movable/spell_master/swarm - name = "Swarm Abilities" - icon_state = "nano_spell_ready" - - open_state = "swarm_open" - closed_state = "swarm_closed" - -//////////////ACTUAL SPELLS////////////// -//This is what you click to cast things// -///////////////////////////////////////// -/obj/screen/spell - icon = 'icons/mob/screen_spells.dmi' - icon_state = "wiz_spell_base" - var/spell_base = "wiz" - var/last_charge = 0 //not a time, but the last remembered charge value - - var/spell/spell = null - var/handle_icon_updates = 0 - var/obj/screen/movable/spell_master/spellmaster - - var/icon/last_charged_icon - -/obj/screen/spell/Destroy() - . = ..() - spell = null - last_charged_icon = null - if(spellmaster) - spellmaster.spell_objects -= src - if(spellmaster.spell_holder && spellmaster.spell_holder.client) - spellmaster.spell_holder.client.screen -= src - if(spellmaster && !spellmaster.spell_objects.len) - qdel(spellmaster) - spellmaster = null - -/obj/screen/spell/proc/update_charge(var/forced_update = 0) - if(!spell) - qdel(src) - return - - if((last_charge == spell.charge_counter || !handle_icon_updates) && !forced_update) - return //nothing to see here - - cut_overlay(spell.hud_state) - - if(spell.charge_type == Sp_RECHARGE || spell.charge_type == Sp_CHARGES) - if(spell.charge_counter < spell.charge_max) - icon_state = "[spell_base]_spell_base" - if(spell.charge_counter > 0) - var/icon/partial_charge = icon(src.icon, "[spell_base]_spell_ready") - partial_charge.Crop(1, 1, partial_charge.Width(), round(partial_charge.Height() * spell.charge_counter / spell.charge_max)) - overlays += partial_charge - if(last_charged_icon) - cut_overlay(last_charged_icon) - last_charged_icon = partial_charge - else if(last_charged_icon) - cut_overlay(last_charged_icon) - last_charged_icon = null - else - icon_state = "[spell_base]_spell_ready" - if(last_charged_icon) - cut_overlay(last_charged_icon) - else - icon_state = "[spell_base]_spell_ready" - - add_overlay(spell.hud_state) - - last_charge = spell.charge_counter - - cut_overlay("silence") - if(spell.silenced) - overlays += "silence" - -/obj/screen/spell/Click() - if(!usr || !spell) - qdel(src) - return - - spell.perform(usr) - update_charge(1) +/obj/screen/movable/spell_master + name = "Spells" + icon = 'icons/mob/screen_spells.dmi' + icon_state = "wiz_spell_ready" + var/list/obj/screen/spell/spell_objects = list() + var/showing = 0 + + var/open_state = "master_open" + var/closed_state = "master_closed" + + screen_loc = ui_spell_master + + var/mob/spell_holder + +/obj/screen/movable/spell_master/Destroy() + . = ..() + for(var/obj/screen/spell/spells in spell_objects) + spells.spellmaster = null + spell_objects.Cut() + if(spell_holder) + spell_holder.spell_masters -= src + if(spell_holder.client && spell_holder.client.screen) + spell_holder.client.screen -= src + spell_holder = null + +/obj/screen/movable/spell_master/MouseDrop() + if(showing) + return + + return ..() + +/obj/screen/movable/spell_master/Click() + if(!spell_objects.len) + qdel(src) + return + + toggle_open() + +/obj/screen/movable/spell_master/proc/toggle_open(var/forced_state = 0) + if(showing && (forced_state != 2)) + for(var/obj/screen/spell/O in spell_objects) + if(spell_holder && spell_holder.client) + spell_holder.client.screen -= O + O.handle_icon_updates = 0 + showing = 0 + overlays.len = 0 + overlays.Add(closed_state) + else if(forced_state != 1) + open_spellmaster() + update_spells(1) + showing = 1 + overlays.len = 0 + overlays.Add(open_state) + +/obj/screen/movable/spell_master/proc/open_spellmaster() + var/list/screen_loc_xy = splittext(screen_loc,",") + + //Create list of X offsets + var/list/screen_loc_X = splittext(screen_loc_xy[1],":") + var/x_position = decode_screen_X(screen_loc_X[1]) + var/x_pix = screen_loc_X[2] + + //Create list of Y offsets + var/list/screen_loc_Y = splittext(screen_loc_xy[2],":") + var/y_position = decode_screen_Y(screen_loc_Y[1]) + var/y_pix = screen_loc_Y[2] + + for(var/i = 1; i <= spell_objects.len; i++) + var/obj/screen/spell/S = spell_objects[i] + var/xpos = x_position + (x_position < 8 ? 1 : -1)*(i%7) + var/ypos = y_position + (y_position < 8 ? round(i/7) : -round(i/7)) + if(spell_holder && spell_holder.client) + S.screen_loc = "[encode_screen_X(xpos)]:[x_pix],[encode_screen_Y(ypos)]:[y_pix]" + spell_holder.client.screen += S + S.handle_icon_updates = 1 + +/obj/screen/movable/spell_master/proc/add_spell(var/spell/spell) + if(!spell) return + + if(spell.connected_button) //we have one already, for some reason + if(spell.connected_button in spell_objects) + return + else + spell_objects.Add(spell.connected_button) + if(spell_holder.client) + toggle_open(2) + return + + if(spell.spell_flags & NO_BUTTON) //no button to add if we don't get one + return + + var/obj/screen/spell/newscreen = new /obj/screen/spell() + newscreen.spellmaster = src + newscreen.spell = spell + + spell.connected_button = newscreen + + if(!spell.override_base) //if it's not set, we do basic checks + if(spell.spell_flags & CONSTRUCT_CHECK) + newscreen.spell_base = "const" //construct spells + else + newscreen.spell_base = "wiz" //wizard spells + else + newscreen.spell_base = spell.override_base + newscreen.name = spell.name + newscreen.update_charge(1) + spell_objects.Add(newscreen) + if(spell_holder.client) + toggle_open(2) //forces the icons to refresh on screen + +/obj/screen/movable/spell_master/proc/remove_spell(var/spell/spell) + qdel(spell.connected_button) + + spell.connected_button = null + + if(spell_objects.len) + toggle_open(showing + 1) + else + qdel(src) + +/obj/screen/movable/spell_master/proc/silence_spells(var/amount) + for(var/obj/screen/spell/spell in spell_objects) + spell.spell.silenced = amount + spell.update_charge(1) + +/obj/screen/movable/spell_master/proc/update_spells(forced = 0, mob/user) + if(user && user.client) + if(!(src in user.client.screen)) + user.client.screen += src + for(var/obj/screen/spell/spell in spell_objects) + spell.update_charge(forced) + +/obj/screen/movable/spell_master/genetic + name = "Mutant Powers" + icon_state = "genetic_spell_ready" + + open_state = "genetics_open" + closed_state = "genetics_closed" + + screen_loc = ui_genetic_master + +/obj/screen/movable/spell_master/swarm + name = "Swarm Abilities" + icon_state = "nano_spell_ready" + + open_state = "swarm_open" + closed_state = "swarm_closed" + +//////////////ACTUAL SPELLS////////////// +//This is what you click to cast things// +///////////////////////////////////////// +/obj/screen/spell + icon = 'icons/mob/screen_spells.dmi' + icon_state = "wiz_spell_base" + var/spell_base = "wiz" + var/last_charge = 0 //not a time, but the last remembered charge value + + var/spell/spell = null + var/handle_icon_updates = 0 + var/obj/screen/movable/spell_master/spellmaster + + var/icon/last_charged_icon + +/obj/screen/spell/Destroy() + . = ..() + spell = null + last_charged_icon = null + if(spellmaster) + spellmaster.spell_objects -= src + if(spellmaster.spell_holder && spellmaster.spell_holder.client) + spellmaster.spell_holder.client.screen -= src + if(spellmaster && !spellmaster.spell_objects.len) + qdel(spellmaster) + spellmaster = null + +/obj/screen/spell/proc/update_charge(var/forced_update = 0) + if(!spell) + qdel(src) + return + + if((last_charge == spell.charge_counter || !handle_icon_updates) && !forced_update) + return //nothing to see here + + cut_overlay(spell.hud_state) + + if(spell.charge_type == Sp_RECHARGE || spell.charge_type == Sp_CHARGES) + if(spell.charge_counter < spell.charge_max) + icon_state = "[spell_base]_spell_base" + if(spell.charge_counter > 0) + var/icon/partial_charge = icon(src.icon, "[spell_base]_spell_ready") + partial_charge.Crop(1, 1, partial_charge.Width(), round(partial_charge.Height() * spell.charge_counter / spell.charge_max)) + overlays += partial_charge + if(last_charged_icon) + cut_overlay(last_charged_icon) + last_charged_icon = partial_charge + else if(last_charged_icon) + cut_overlay(last_charged_icon) + last_charged_icon = null + else + icon_state = "[spell_base]_spell_ready" + if(last_charged_icon) + cut_overlay(last_charged_icon) + else + icon_state = "[spell_base]_spell_ready" + + add_overlay(spell.hud_state) + + last_charge = spell.charge_counter + + cut_overlay("silence") + if(spell.silenced) + overlays += "silence" + +/obj/screen/spell/Click() + if(!usr || !spell) + qdel(src) + return + + spell.perform(usr) + update_charge(1) diff --git a/code/controllers/hooks-defs.dm b/code/controllers/hooks-defs.dm index 82f79207520..dcfb8885229 100644 --- a/code/controllers/hooks-defs.dm +++ b/code/controllers/hooks-defs.dm @@ -1,96 +1,96 @@ -/** - * Startup hook. - * Called in world.dm when the server starts. - */ -/hook/startup - -/** - * Roundstart hook. - * Called in gameticker.dm when a round starts. - */ -/hook/roundstart - -/** - * Roundend hook. - * Called in gameticker.dm when a round ends. - */ -/hook/roundend - -/** - * Death hook. - * Called in death.dm when someone dies. - * Parameters: var/mob/living/carbon/human, var/gibbed - */ -/hook/death - -/** - * Cloning hook. - * Called in cloning.dm when someone is brought back by the wonders of modern science. - * Parameters: var/mob/living/carbon/human - */ -/hook/clone - -/** - * Debrained hook. - * Called in brain_item.dm when someone gets debrained. - * Parameters: var/obj/item/organ/brain - */ -/hook/debrain - -/** - * Borged hook. - * Called in robot_parts.dm when someone gets turned into a cyborg. - * Parameters: var/mob/living/silicon/robot - */ -/hook/borgify - -/** - * Podman hook. - * Called in podmen.dm when someone is brought back as a Diona. - * Parameters: var/mob/living/carbon/alien/diona - */ -/hook/harvest_podman - -/** - * Payroll revoked hook. - * Called in Accounts_DB.dm when someone's payroll is stolen at the Accounts terminal. - * Parameters: var/datum/money_account - */ -/hook/revoke_payroll - -/** - * Account suspension hook. - * Called in Accounts_DB.dm when someone's account is suspended or unsuspended at the Accounts terminal. - * Parameters: var/datum/money_account - */ -/hook/change_account_status - -/** - * Employee reassignment hook. - * Called in card.dm when someone's card is reassigned at the HoP's desk. - * Parameters: var/obj/item/weapon/card/id - */ -/hook/reassign_employee - -/** - * Employee terminated hook. - * Called in card.dm when someone's card is terminated at the HoP's desk. - * Parameters: var/obj/item/weapon/card/id - */ -/hook/terminate_employee - -/** - * Crate sold hook. - * Called in supplyshuttle.dm when a crate is sold on the shuttle. - * Parameters: var/obj/structure/closet/crate/sold, var/area/shuttle - */ -/hook/sell_crate - -/** - * Supply Shuttle sold hook. - * Called in supplyshuttle.dm when the shuttle contents are sold. - * This hook is called _before_ the crates are processed for normal - * phoron/metal sale (and before the sell_crate hooks) - * Parameters: var/area/area_shuttle - */ -/hook/sell_shuttle +/** + * Startup hook. + * Called in world.dm when the server starts. + */ +/hook/startup + +/** + * Roundstart hook. + * Called in gameticker.dm when a round starts. + */ +/hook/roundstart + +/** + * Roundend hook. + * Called in gameticker.dm when a round ends. + */ +/hook/roundend + +/** + * Death hook. + * Called in death.dm when someone dies. + * Parameters: var/mob/living/carbon/human, var/gibbed + */ +/hook/death + +/** + * Cloning hook. + * Called in cloning.dm when someone is brought back by the wonders of modern science. + * Parameters: var/mob/living/carbon/human + */ +/hook/clone + +/** + * Debrained hook. + * Called in brain_item.dm when someone gets debrained. + * Parameters: var/obj/item/organ/brain + */ +/hook/debrain + +/** + * Borged hook. + * Called in robot_parts.dm when someone gets turned into a cyborg. + * Parameters: var/mob/living/silicon/robot + */ +/hook/borgify + +/** + * Podman hook. + * Called in podmen.dm when someone is brought back as a Diona. + * Parameters: var/mob/living/carbon/alien/diona + */ +/hook/harvest_podman + +/** + * Payroll revoked hook. + * Called in Accounts_DB.dm when someone's payroll is stolen at the Accounts terminal. + * Parameters: var/datum/money_account + */ +/hook/revoke_payroll + +/** + * Account suspension hook. + * Called in Accounts_DB.dm when someone's account is suspended or unsuspended at the Accounts terminal. + * Parameters: var/datum/money_account + */ +/hook/change_account_status + +/** + * Employee reassignment hook. + * Called in card.dm when someone's card is reassigned at the HoP's desk. + * Parameters: var/obj/item/weapon/card/id + */ +/hook/reassign_employee + +/** + * Employee terminated hook. + * Called in card.dm when someone's card is terminated at the HoP's desk. + * Parameters: var/obj/item/weapon/card/id + */ +/hook/terminate_employee + +/** + * Crate sold hook. + * Called in supplyshuttle.dm when a crate is sold on the shuttle. + * Parameters: var/obj/structure/closet/crate/sold, var/area/shuttle + */ +/hook/sell_crate + +/** + * Supply Shuttle sold hook. + * Called in supplyshuttle.dm when the shuttle contents are sold. + * This hook is called _before_ the crates are processed for normal + * phoron/metal sale (and before the sell_crate hooks) + * Parameters: var/area/area_shuttle + */ +/hook/sell_shuttle diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm index bcea4490de3..7b63084af24 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -1,56 +1,56 @@ -//simplified MC that is designed to fail when procs 'break'. When it fails it's just replaced with a new one. -//It ensures master_controller.process() is never doubled up by killing the MC (hence terminating any of its sleeping procs) -//WIP, needs lots of work still - -// -// TODO - This will be completely replaced by master.dm in time. -// - -var/global/datum/controller/game_controller/master_controller //Set in world.New() - -var/global/controller_iteration = 0 -var/global/last_tick_duration = 0 - -var/global/pipe_processing_killed = 0 - -/datum/controller/game_controller - var/list/shuttle_list // For debugging and VV - -/datum/controller/game_controller/New() - //There can be only one master_controller. Out with the old and in with the new. - if(master_controller != src) - log_debug("Rebuilding Master Controller") - if(istype(master_controller)) - qdel(master_controller) - master_controller = src - - if(!job_master) - job_master = new /datum/controller/occupations() - job_master.SetupOccupations() - job_master.LoadJobs("config/jobs.txt") - admin_notice("Job setup complete", R_DEBUG) - - if(!syndicate_code_phrase) syndicate_code_phrase = generate_code_phrase() - if(!syndicate_code_response) syndicate_code_response = generate_code_phrase() - -/datum/controller/game_controller/proc/setup() - - setup_objects() - // setupgenetics() Moved to SSatoms - // SetupXenoarch() - Moved to SSxenoarch - - transfer_controller = new - admin_notice("Initializations complete.", R_DEBUG) - -// #if UNIT_TEST -// #define CHECK_SLEEP_MASTER // For unit tests we don't care about a smooth lobby screen experience. We care about speed. -// #else -// #define CHECK_SLEEP_MASTER if(++initialized_objects > 500) { initialized_objects=0;sleep(world.tick_lag); } -// #endif - -/datum/controller/game_controller/proc/setup_objects() - // Set up antagonists. - populate_antag_type_list() - - //Set up spawn points. +//simplified MC that is designed to fail when procs 'break'. When it fails it's just replaced with a new one. +//It ensures master_controller.process() is never doubled up by killing the MC (hence terminating any of its sleeping procs) +//WIP, needs lots of work still + +// +// TODO - This will be completely replaced by master.dm in time. +// + +var/global/datum/controller/game_controller/master_controller //Set in world.New() + +var/global/controller_iteration = 0 +var/global/last_tick_duration = 0 + +var/global/pipe_processing_killed = 0 + +/datum/controller/game_controller + var/list/shuttle_list // For debugging and VV + +/datum/controller/game_controller/New() + //There can be only one master_controller. Out with the old and in with the new. + if(master_controller != src) + log_debug("Rebuilding Master Controller") + if(istype(master_controller)) + qdel(master_controller) + master_controller = src + + if(!job_master) + job_master = new /datum/controller/occupations() + job_master.SetupOccupations() + job_master.LoadJobs("config/jobs.txt") + admin_notice("Job setup complete", R_DEBUG) + + if(!syndicate_code_phrase) syndicate_code_phrase = generate_code_phrase() + if(!syndicate_code_response) syndicate_code_response = generate_code_phrase() + +/datum/controller/game_controller/proc/setup() + + setup_objects() + // setupgenetics() Moved to SSatoms + // SetupXenoarch() - Moved to SSxenoarch + + transfer_controller = new + admin_notice("Initializations complete.", R_DEBUG) + +// #if UNIT_TEST +// #define CHECK_SLEEP_MASTER // For unit tests we don't care about a smooth lobby screen experience. We care about speed. +// #else +// #define CHECK_SLEEP_MASTER if(++initialized_objects > 500) { initialized_objects=0;sleep(world.tick_lag); } +// #endif + +/datum/controller/game_controller/proc/setup_objects() + // Set up antagonists. + populate_antag_type_list() + + //Set up spawn points. populate_spawn_points() \ No newline at end of file diff --git a/code/controllers/observer_listener/atom/observer.dm b/code/controllers/observer_listener/atom/observer.dm index da38580414e..8c3e93f4fc3 100644 --- a/code/controllers/observer_listener/atom/observer.dm +++ b/code/controllers/observer_listener/atom/observer.dm @@ -1,31 +1,31 @@ -#define OBSERVER_EVENT_DESTROY "OnDestroy" - -/atom - var/list/observer_events - -/atom/Destroy() - var/list/destroy_listeners = get_listener_list_from_event(OBSERVER_EVENT_DESTROY) - if(destroy_listeners) - for(var/destroy_listener in destroy_listeners) - call(destroy_listener, destroy_listeners[destroy_listener])(src) - - for(var/list/listeners in observer_events) - listeners.Cut() - - return ..() - -/atom/proc/register(var/event, var/procOwner, var/proc_call) - var/list/listeners = get_listener_list_from_event(event) - listeners[procOwner] = proc_call - -/atom/proc/unregister(var/event, var/procOwner) - var/list/listeners = get_listener_list_from_event(event) - listeners -= procOwner - -/atom/proc/get_listener_list_from_event(var/observer_event) - if(!observer_events) observer_events = list() - var/list/listeners = observer_events[observer_event] - if(!listeners) - listeners = list() - observer_events[observer_event] = listeners - return listeners +#define OBSERVER_EVENT_DESTROY "OnDestroy" + +/atom + var/list/observer_events + +/atom/Destroy() + var/list/destroy_listeners = get_listener_list_from_event(OBSERVER_EVENT_DESTROY) + if(destroy_listeners) + for(var/destroy_listener in destroy_listeners) + call(destroy_listener, destroy_listeners[destroy_listener])(src) + + for(var/list/listeners in observer_events) + listeners.Cut() + + return ..() + +/atom/proc/register(var/event, var/procOwner, var/proc_call) + var/list/listeners = get_listener_list_from_event(event) + listeners[procOwner] = proc_call + +/atom/proc/unregister(var/event, var/procOwner) + var/list/listeners = get_listener_list_from_event(event) + listeners -= procOwner + +/atom/proc/get_listener_list_from_event(var/observer_event) + if(!observer_events) observer_events = list() + var/list/listeners = observer_events[observer_event] + if(!listeners) + listeners = list() + observer_events[observer_event] = listeners + return listeners diff --git a/code/controllers/subsystems/alarm.dm b/code/controllers/subsystems/alarm.dm index 4b6140e5074..868025eca3f 100644 --- a/code/controllers/subsystems/alarm.dm +++ b/code/controllers/subsystems/alarm.dm @@ -1,45 +1,45 @@ - -// We manually initialize the alarm handlers instead of looping over all existing types -// to make it possible to write: camera_alarm.triggerAlarm() rather than SSalarm.managers[datum/alarm_handler/camera].triggerAlarm() or a variant thereof. -/var/global/datum/alarm_handler/atmosphere/atmosphere_alarm = new() -/var/global/datum/alarm_handler/camera/camera_alarm = new() -/var/global/datum/alarm_handler/fire/fire_alarm = new() -/var/global/datum/alarm_handler/motion/motion_alarm = new() -/var/global/datum/alarm_handler/power/power_alarm = new() - -SUBSYSTEM_DEF(alarm) - name = "Alarm" - wait = 2 SECONDS - priority = FIRE_PRIORITY_ALARM - init_order = INIT_ORDER_ALARM - var/list/datum/alarm/all_handlers - var/tmp/list/currentrun = null - var/static/list/active_alarm_cache = list() - -/datum/controller/subsystem/alarm/Initialize() - all_handlers = list(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm) - . = ..() - -/datum/controller/subsystem/alarm/fire(resumed = FALSE) - if(!resumed) - src.currentrun = all_handlers.Copy() - active_alarm_cache.Cut() - - var/list/currentrun = src.currentrun // Cache for sanic speed - while (currentrun.len) - var/datum/alarm_handler/AH = currentrun[currentrun.len] - currentrun.len-- - AH.process() - active_alarm_cache += AH.alarms - - if (MC_TICK_CHECK) - return - -/datum/controller/subsystem/alarm/proc/active_alarms() - return active_alarm_cache.Copy() - -/datum/controller/subsystem/alarm/proc/number_of_active_alarms() - return active_alarm_cache.len - -/datum/controller/subsystem/alarm/stat_entry() - ..("[number_of_active_alarms()] alarm\s") + +// We manually initialize the alarm handlers instead of looping over all existing types +// to make it possible to write: camera_alarm.triggerAlarm() rather than SSalarm.managers[datum/alarm_handler/camera].triggerAlarm() or a variant thereof. +/var/global/datum/alarm_handler/atmosphere/atmosphere_alarm = new() +/var/global/datum/alarm_handler/camera/camera_alarm = new() +/var/global/datum/alarm_handler/fire/fire_alarm = new() +/var/global/datum/alarm_handler/motion/motion_alarm = new() +/var/global/datum/alarm_handler/power/power_alarm = new() + +SUBSYSTEM_DEF(alarm) + name = "Alarm" + wait = 2 SECONDS + priority = FIRE_PRIORITY_ALARM + init_order = INIT_ORDER_ALARM + var/list/datum/alarm/all_handlers + var/tmp/list/currentrun = null + var/static/list/active_alarm_cache = list() + +/datum/controller/subsystem/alarm/Initialize() + all_handlers = list(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm) + . = ..() + +/datum/controller/subsystem/alarm/fire(resumed = FALSE) + if(!resumed) + src.currentrun = all_handlers.Copy() + active_alarm_cache.Cut() + + var/list/currentrun = src.currentrun // Cache for sanic speed + while (currentrun.len) + var/datum/alarm_handler/AH = currentrun[currentrun.len] + currentrun.len-- + AH.process() + active_alarm_cache += AH.alarms + + if (MC_TICK_CHECK) + return + +/datum/controller/subsystem/alarm/proc/active_alarms() + return active_alarm_cache.Copy() + +/datum/controller/subsystem/alarm/proc/number_of_active_alarms() + return active_alarm_cache.len + +/datum/controller/subsystem/alarm/stat_entry() + ..("[number_of_active_alarms()] alarm\s") diff --git a/code/controllers/subsystems/circuits.dm b/code/controllers/subsystems/circuits.dm index 4d2b2821713..f417ce3055d 100644 --- a/code/controllers/subsystems/circuits.dm +++ b/code/controllers/subsystems/circuits.dm @@ -1,94 +1,94 @@ -// -// This is for custom circuits, mostly the initialization of global properties about them. -// Might make this also process them in the future if its better to do that than using the obj ticker. -// -SUBSYSTEM_DEF(circuit) - name = "Circuit" - init_order = INIT_ORDER_CIRCUIT - flags = SS_NO_FIRE - var/list/all_components = list() // Associative list of [component_name]:[component_path] pairs - var/list/cached_components = list() // Associative list of [component_path]:[component] pairs - var/list/all_assemblies = list() // Associative list of [assembly_name]:[assembly_path] pairs - var/list/cached_assemblies = list() // Associative list of [assembly_path]:[assembly] pairs - var/list/all_circuits = list() // Associative list of [circuit_name]:[circuit_path] pairs - var/list/circuit_fabricator_recipe_list = list() // Associative list of [category_name]:[list_of_circuit_paths] pairs -// var/cost_multiplier = MINERAL_MATERIAL_AMOUNT / 10 // Each circuit cost unit is 200cm3 - -/datum/controller/subsystem/circuit/Recover() - flags |= SS_NO_INIT // Make extra sure we don't initialize twice. - -/datum/controller/subsystem/circuit/Initialize(timeofday) - circuits_init() - return ..() - -/datum/controller/subsystem/circuit/proc/circuits_init() - //Cached lists for free performance - for(var/obj/item/integrated_circuit/IC as anything in typesof(/obj/item/integrated_circuit)) - var/path = IC - all_components[initial(IC.name)] = path // Populating the component lists - cached_components[path] = new path - - if(!(initial(IC.spawn_flags) & (IC_SPAWN_DEFAULT | IC_SPAWN_RESEARCH))) - continue - - var/category = initial(IC.category_text) - if(!circuit_fabricator_recipe_list[category]) - circuit_fabricator_recipe_list[category] = list() - var/list/category_list = circuit_fabricator_recipe_list[category] - category_list += IC // Populating the fabricator categories - - for(var/obj/item/device/electronic_assembly/A as anything in typesof(/obj/item/device/electronic_assembly)) - var/path = A - all_assemblies[initial(A.name)] = path - cached_assemblies[path] = new path - - - circuit_fabricator_recipe_list["Assemblies"] = list( - /obj/item/device/electronic_assembly/default, - /obj/item/device/electronic_assembly/calc, - /obj/item/device/electronic_assembly/clam, - /obj/item/device/electronic_assembly/simple, - /obj/item/device/electronic_assembly/hook, - /obj/item/device/electronic_assembly/pda, - /obj/item/device/electronic_assembly/tiny/default, - /obj/item/device/electronic_assembly/tiny/cylinder, - /obj/item/device/electronic_assembly/tiny/scanner, - /obj/item/device/electronic_assembly/tiny/hook, - /obj/item/device/electronic_assembly/tiny/box, - /obj/item/device/electronic_assembly/medium/default, - /obj/item/device/electronic_assembly/medium/box, - /obj/item/device/electronic_assembly/medium/clam, - /obj/item/device/electronic_assembly/medium/medical, - /obj/item/device/electronic_assembly/medium/gun, - /obj/item/device/electronic_assembly/medium/radio, - /obj/item/device/electronic_assembly/large/default, - /obj/item/device/electronic_assembly/large/scope, - /obj/item/device/electronic_assembly/large/terminal, - /obj/item/device/electronic_assembly/large/arm, - /obj/item/device/electronic_assembly/large/tall, - /obj/item/device/electronic_assembly/large/industrial, - /obj/item/device/electronic_assembly/drone/default, - /obj/item/device/electronic_assembly/drone/arms, - /obj/item/device/electronic_assembly/drone/secbot, - /obj/item/device/electronic_assembly/drone/medbot, - /obj/item/device/electronic_assembly/drone/genbot, - /obj/item/device/electronic_assembly/drone/android, - /obj/item/device/electronic_assembly/wallmount/tiny, - /obj/item/device/electronic_assembly/wallmount/light, - /obj/item/device/electronic_assembly/wallmount, - /obj/item/device/electronic_assembly/wallmount/heavy, - /obj/item/weapon/implant/integrated_circuit, - /obj/item/clothing/under/circuitry, - /obj/item/clothing/gloves/circuitry, - /obj/item/clothing/glasses/circuitry, - /obj/item/clothing/shoes/circuitry, - /obj/item/clothing/head/circuitry, - /obj/item/clothing/ears/circuitry, - /obj/item/clothing/suit/circuitry - ) - - circuit_fabricator_recipe_list["Tools"] = list( - /obj/item/device/integrated_electronics/wirer, - /obj/item/device/integrated_electronics/debugger, - /obj/item/device/integrated_electronics/detailer - ) +// +// This is for custom circuits, mostly the initialization of global properties about them. +// Might make this also process them in the future if its better to do that than using the obj ticker. +// +SUBSYSTEM_DEF(circuit) + name = "Circuit" + init_order = INIT_ORDER_CIRCUIT + flags = SS_NO_FIRE + var/list/all_components = list() // Associative list of [component_name]:[component_path] pairs + var/list/cached_components = list() // Associative list of [component_path]:[component] pairs + var/list/all_assemblies = list() // Associative list of [assembly_name]:[assembly_path] pairs + var/list/cached_assemblies = list() // Associative list of [assembly_path]:[assembly] pairs + var/list/all_circuits = list() // Associative list of [circuit_name]:[circuit_path] pairs + var/list/circuit_fabricator_recipe_list = list() // Associative list of [category_name]:[list_of_circuit_paths] pairs +// var/cost_multiplier = MINERAL_MATERIAL_AMOUNT / 10 // Each circuit cost unit is 200cm3 + +/datum/controller/subsystem/circuit/Recover() + flags |= SS_NO_INIT // Make extra sure we don't initialize twice. + +/datum/controller/subsystem/circuit/Initialize(timeofday) + circuits_init() + return ..() + +/datum/controller/subsystem/circuit/proc/circuits_init() + //Cached lists for free performance + for(var/obj/item/integrated_circuit/IC as anything in typesof(/obj/item/integrated_circuit)) + var/path = IC + all_components[initial(IC.name)] = path // Populating the component lists + cached_components[path] = new path + + if(!(initial(IC.spawn_flags) & (IC_SPAWN_DEFAULT | IC_SPAWN_RESEARCH))) + continue + + var/category = initial(IC.category_text) + if(!circuit_fabricator_recipe_list[category]) + circuit_fabricator_recipe_list[category] = list() + var/list/category_list = circuit_fabricator_recipe_list[category] + category_list += IC // Populating the fabricator categories + + for(var/obj/item/device/electronic_assembly/A as anything in typesof(/obj/item/device/electronic_assembly)) + var/path = A + all_assemblies[initial(A.name)] = path + cached_assemblies[path] = new path + + + circuit_fabricator_recipe_list["Assemblies"] = list( + /obj/item/device/electronic_assembly/default, + /obj/item/device/electronic_assembly/calc, + /obj/item/device/electronic_assembly/clam, + /obj/item/device/electronic_assembly/simple, + /obj/item/device/electronic_assembly/hook, + /obj/item/device/electronic_assembly/pda, + /obj/item/device/electronic_assembly/tiny/default, + /obj/item/device/electronic_assembly/tiny/cylinder, + /obj/item/device/electronic_assembly/tiny/scanner, + /obj/item/device/electronic_assembly/tiny/hook, + /obj/item/device/electronic_assembly/tiny/box, + /obj/item/device/electronic_assembly/medium/default, + /obj/item/device/electronic_assembly/medium/box, + /obj/item/device/electronic_assembly/medium/clam, + /obj/item/device/electronic_assembly/medium/medical, + /obj/item/device/electronic_assembly/medium/gun, + /obj/item/device/electronic_assembly/medium/radio, + /obj/item/device/electronic_assembly/large/default, + /obj/item/device/electronic_assembly/large/scope, + /obj/item/device/electronic_assembly/large/terminal, + /obj/item/device/electronic_assembly/large/arm, + /obj/item/device/electronic_assembly/large/tall, + /obj/item/device/electronic_assembly/large/industrial, + /obj/item/device/electronic_assembly/drone/default, + /obj/item/device/electronic_assembly/drone/arms, + /obj/item/device/electronic_assembly/drone/secbot, + /obj/item/device/electronic_assembly/drone/medbot, + /obj/item/device/electronic_assembly/drone/genbot, + /obj/item/device/electronic_assembly/drone/android, + /obj/item/device/electronic_assembly/wallmount/tiny, + /obj/item/device/electronic_assembly/wallmount/light, + /obj/item/device/electronic_assembly/wallmount, + /obj/item/device/electronic_assembly/wallmount/heavy, + /obj/item/weapon/implant/integrated_circuit, + /obj/item/clothing/under/circuitry, + /obj/item/clothing/gloves/circuitry, + /obj/item/clothing/glasses/circuitry, + /obj/item/clothing/shoes/circuitry, + /obj/item/clothing/head/circuitry, + /obj/item/clothing/ears/circuitry, + /obj/item/clothing/suit/circuitry + ) + + circuit_fabricator_recipe_list["Tools"] = list( + /obj/item/device/integrated_electronics/wirer, + /obj/item/device/integrated_electronics/debugger, + /obj/item/device/integrated_electronics/detailer + ) diff --git a/code/controllers/subsystems/events2.dm b/code/controllers/subsystems/events2.dm index 7cf986358f7..e52025b2c06 100644 --- a/code/controllers/subsystems/events2.dm +++ b/code/controllers/subsystems/events2.dm @@ -1,36 +1,36 @@ -// This is a simple ticker for the new event system. -// The logic that determines what events get chosen is held inside a seperate subsystem. - -SUBSYSTEM_DEF(event_ticker) - name = "Events (Ticker)" - wait = 2 SECONDS - runlevels = RUNLEVEL_GAME - - // List of `/datum/event2/event`s that are currently active, and receiving process() ticks. - var/list/active_events = list() - - // List of `/datum/event2/event`s that finished, and are here for showing at roundend, if that's desired. - var/list/finished_events = list() - -// Process active events. -/datum/controller/subsystem/event_ticker/fire(resumed) - for(var/datum/event2/event/event as anything in active_events) - event.process() - if(event.finished) - event_finished(event) - -// Starts an event, independent of the GM system. -// This means it will always run, and won't affect the GM system in any way, e.g. not putting the event off limits after one use. -/datum/controller/subsystem/event_ticker/proc/start_event(event_type) - var/datum/event2/event/E = new event_type() - E.execute() - event_started(E) - -/datum/controller/subsystem/event_ticker/proc/event_started(datum/event2/event/E) - log_debug("Event [E.type] is now being ran.") - active_events += E - -/datum/controller/subsystem/event_ticker/proc/event_finished(datum/event2/event/E) - log_debug("Event [E.type] has finished.") - active_events -= E +// This is a simple ticker for the new event system. +// The logic that determines what events get chosen is held inside a seperate subsystem. + +SUBSYSTEM_DEF(event_ticker) + name = "Events (Ticker)" + wait = 2 SECONDS + runlevels = RUNLEVEL_GAME + + // List of `/datum/event2/event`s that are currently active, and receiving process() ticks. + var/list/active_events = list() + + // List of `/datum/event2/event`s that finished, and are here for showing at roundend, if that's desired. + var/list/finished_events = list() + +// Process active events. +/datum/controller/subsystem/event_ticker/fire(resumed) + for(var/datum/event2/event/event as anything in active_events) + event.process() + if(event.finished) + event_finished(event) + +// Starts an event, independent of the GM system. +// This means it will always run, and won't affect the GM system in any way, e.g. not putting the event off limits after one use. +/datum/controller/subsystem/event_ticker/proc/start_event(event_type) + var/datum/event2/event/E = new event_type() + E.execute() + event_started(E) + +/datum/controller/subsystem/event_ticker/proc/event_started(datum/event2/event/E) + log_debug("Event [E.type] is now being ran.") + active_events += E + +/datum/controller/subsystem/event_ticker/proc/event_finished(datum/event2/event/E) + log_debug("Event [E.type] has finished.") + active_events -= E finished_events += E \ No newline at end of file diff --git a/code/controllers/subsystems/game_master.dm b/code/controllers/subsystems/game_master.dm index 55e9a3e88a1..4703da0bf8c 100644 --- a/code/controllers/subsystems/game_master.dm +++ b/code/controllers/subsystems/game_master.dm @@ -1,364 +1,364 @@ -// This is a sort of successor to the various event systems created over the years. It is designed to be just a tad smarter than the -// previous ones, checking various things like player count, department size and composition, individual player activity, -// individual player (IC) skill, and such, in order to try to choose the best events to take in order to add spice or variety to -// the round. - -// This subsystem holds the logic that chooses events. Actual event processing is handled in a seperate subsystem. -SUBSYSTEM_DEF(game_master) - name = "Events (Game Master)" - wait = 1 MINUTE - runlevels = RUNLEVEL_GAME - - // The GM object is what actually chooses events. - // It's held in a seperate object for better encapsulation, and allows for different 'flavors' of GMs to be made, that choose events differently. - var/datum/game_master/GM = null - var/game_master_type = /datum/game_master/default - - var/list/available_events = list() // A list of meta event objects. - - var/danger = 0 // The GM's best guess at how chaotic the round is. High danger makes it hold back. - var/staleness = -20 // Determines liklihood of the GM doing something, increases over time. - - var/next_event = 0 // Minimum amount of time of nothingness until the GM can pick something again. - - var/debug_messages = FALSE // If true, debug information is written to `log_debug()`. - -/datum/controller/subsystem/game_master/Initialize() - var/list/subtypes = subtypesof(/datum/event2/meta) - for(var/T in subtypes) - var/datum/event2/meta/M = new T() - if(!M.name) - continue - available_events += M - - GM = new game_master_type() - - if(config && !config.enable_game_master) - can_fire = FALSE - - return ..() - -/datum/controller/subsystem/game_master/fire(resumed) - adjust_staleness(1) - adjust_danger(-1) - - var/global_afk = metric.assess_all_living_mobs() - global_afk = abs(global_afk - 100) - global_afk = round(global_afk / 100, 0.1) - adjust_staleness(global_afk) // Staleness increases faster if more people are less active. - - if(GM.ignore_time_restrictions || next_event < world.time) - if(prob(staleness) && pre_event_checks()) - do_event_decision() - - -/datum/controller/subsystem/game_master/proc/do_event_decision() - log_game_master("Going to choose an event.") - var/datum/event2/meta/event_picked = GM.choose_event() - if(event_picked) - run_event(event_picked) - next_event = world.time + rand(GM.decision_cooldown_lower_bound, GM.decision_cooldown_upper_bound) - -/datum/controller/subsystem/game_master/proc/debug_gm() - can_fire = TRUE - staleness = 100 - debug_messages = TRUE - -/datum/controller/subsystem/game_master/proc/run_event(datum/event2/meta/chosen_event) - var/datum/event2/event/E = chosen_event.make_event() - - chosen_event.times_ran++ - - if(!chosen_event.reusable) - // Disable this event, so it only gets picked once. - chosen_event.enabled = FALSE - if(chosen_event.event_class) - // Disable similar events, too. - for(var/datum/event2/meta/meta as anything in available_events) - if(meta.event_class == chosen_event.event_class) - meta.enabled = FALSE - - SSevent_ticker.event_started(E) - adjust_danger(chosen_event.chaos) - adjust_staleness(-(10 + chosen_event.chaos)) // More chaotic events reduce staleness more, e.g. a 25 chaos event will reduce it by 35. - - -// Tell the game master that something dangerous happened, e.g. someone dying, station explosions. -/datum/controller/subsystem/game_master/proc/adjust_danger(amount) - amount *= GM.danger_modifier - danger = round(between(0, danger + amount, 1000), 0.1) - -// Tell the game master that things are getting boring if positive, or something interesting if negative.. -/datum/controller/subsystem/game_master/proc/adjust_staleness(amount) - amount *= GM.staleness_modifier - staleness = round( between(-20, staleness + amount, 100), 0.1) - -// These are ran before committing to an event. -// Returns TRUE if the system is allowed to procede, otherwise returns FALSE. -/datum/controller/subsystem/game_master/proc/pre_event_checks(quiet = FALSE) - if(!ticker || ticker.current_state != GAME_STATE_PLAYING) - if(!quiet) - log_game_master("Unable to start event: Ticker is nonexistent, or the game is not ongoing.") - return FALSE - if(GM.ignore_time_restrictions) - return TRUE - if(next_event > world.time) // Sanity. - if(!quiet) - log_game_master("Unable to start event: Time until next event is approximately [round((next_event - world.time) / (1 MINUTE))] minute(s)") - return FALSE - - // Last minute antagging is bad for humans to do, so the GM will respect the start and end of the round. - var/mills = round_duration_in_ds - var/mins = round((mills % 36000) / 600) - var/hours = round(mills / 36000) - -// if(hours < 1 && mins <= 20) // Don't do anything for the first twenty minutes of the round. -// if(!quiet) -// log_debug("Game Master unable to start event: It is too early.") -// return FALSE - if(hours >= 2 && mins >= 40) // Don't do anything in the last twenty minutes of the round, as well. - if(!quiet) - log_game_master("Unable to start event: It is too late.") - return FALSE - return TRUE - -/datum/controller/subsystem/game_master/proc/choose_game_master(mob/user) - var/list/subtypes = subtypesof(/datum/game_master) - var/new_gm_path = tgui_input_list(user, "What kind of Game Master do you want?", "New Game Master", subtypes) - if(new_gm_path) - log_and_message_admins("has swapped the current GM ([GM.type]) for a new GM ([new_gm_path]).") - GM = new new_gm_path(src) - -/datum/controller/subsystem/game_master/proc/log_game_master(message) - if(debug_messages) - log_debug("GAME MASTER: [message]") - - -// This object makes the actual decisions. -/datum/game_master - // Multiplier for how much 'danger' is accumulated. Higer generally makes it possible for more dangerous events to be picked. - var/danger_modifier = 1.0 - - // Ditto. Higher numbers generally result in more events occuring in a round. - var/staleness_modifier = 1.0 - - var/decision_cooldown_lower_bound = 5 MINUTES // Lower bound for how long to wait until -the potential- for another event being decided. - var/decision_cooldown_upper_bound = 20 MINUTES // Same, but upper bound. - - var/ignore_time_restrictions = FALSE // Useful for debugging without needing to wait 20 minutes each time. - var/ignore_round_chaos = FALSE // If true, the system will happily choose back to back intense events like meteors and blobs, Dwarf Fortress style. - -/client/proc/show_gm_status() - set category = "Debug" - set name = "Show GM Status" - set desc = "Shows you what the GM is thinking. If only that existed in real life..." - - if(check_rights(R_ADMIN|R_EVENT|R_DEBUG)) - SSgame_master.interact(usr) - else - to_chat(usr, span("warning", "You do not have sufficient rights to view the GM panel, sorry.")) - -/datum/controller/subsystem/game_master/proc/interact(var/client/user) - if(!user) - return - - // Using lists for string tree conservation. - var/list/dat = list("Automated Game Master Event System") - - // Makes the system turn on or off. - dat += href(src, list("toggle" = 1), "\[Toggle GM\]") - dat += " | " - - // Makes the system not care about staleness or being near round-end. - dat += href(src, list("toggle_time_restrictions" = 1), "\[Toggle Time Restrictions\]") - dat += " | " - - // Makes the system not care about how chaotic the round might be. - dat += href(src, list("toggle_chaos_throttle" = 1), "\[Toggle Chaos Throttling\]") - dat += " | " - - // Makes the system immediately choose an event, while still bound to factors like danger, weights, and department staffing. - dat += href(src, list("force_choose_event" = 1), "\[Force Event Decision\]") - dat += "
    " - - // Swaps out the current GM for a new one with different ideas on what a good event might be. - dat += href(src, list("change_gm" = 1), "\[Change GM\]") - dat += "
    " - - dat += "Current GM Type: [GM.type]
    " - dat += "State: [can_fire ? "Active": "Inactive"]
    " - dat += "Status: [pre_event_checks(TRUE) ? "Ready" : "Suppressed"]

    " - - dat += "Staleness: [staleness] " - dat += href(src, list("set_staleness" = 1), "\[Set\]") - dat += "
    " - dat += "Staleness is an estimate of how boring the round might be, and if an event should be done. It is increased passively over time, \ - and increases faster if people are AFK. It deceases when events and certain 'interesting' things happen in the round.
    " - - dat += "Danger: [danger] " - dat += href(src, list("set_danger" = 1), "\[Set\]") - dat += "
    " - dat += "Danger is an estimate of how chaotic the round has been so far. It is decreased passively over time, and is increased by having \ - certain chaotic events be selected, or chaotic things happen in the round. A sufficently high amount of danger will make the system \ - avoid using destructive events, to avoid pushing the station over the edge.
    " - - dat += "

    Player Activity:

    " - - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - - dat += "" - dat += "" - dat += "" - dat += "" - - dat += "" - dat += "" - dat += "" - dat += "" - - dat += "" - dat += "" - - for(var/D in metric.departments) - dat += "" - dat += "" - dat += "" - dat += "" - - dat += "" - dat += "" - - for(var/mob/M as anything in player_list) - dat += "" - dat += "" - dat += "" - dat += "" - dat += "
    CategoryActivity Percentage
    All Living Mobs[metric.assess_all_living_mobs()]%
    All Ghosts[metric.assess_all_dead_mobs()]%
    Departments" - dat += "
    [D][metric.assess_department(D)]%
    Players" - dat += "
    [M] ([M.ckey])[metric.assess_player_activity(M)]%
    " - - dat += "

    Events available:

    " - - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - - for(var/datum/event2/meta/event as anything in available_events) - dat += "" - if(!event.enabled) - dat += "" - else - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "
    Event NameInvolved DepartmentsChaosChaotic ThresholdWeightButtons
    [event.name][event.name][english_list(event.departments)][event.chaos][event.chaotic_threshold][event.get_weight()][href(event, list("force" = 1), "\[Force\]")] [href(event, list("toggle" = 1), "\[Toggle\]")]
    " - - dat += "

    Events active:

    " - - dat += "Current time: [world.time]" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - - for(var/datum/event2/event/event as anything in SSevent_ticker.active_events) - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "
    Event TypeTime StartedTime to AnnounceTime to EndAnnouncedStartedEndedButtons
    [event.type][event.time_started][event.time_to_announce ? event.time_to_announce : "NULL"][event.time_to_end ? event.time_to_end : "NULL"][event.announced ? "Yes" : "No"][event.started ? "Yes" : "No"][event.ended ? "Yes" : "No"][href(event, list("abort" = 1), "\[Abort\]")]
    " - dat += "" - - dat += "

    Events completed:

    " - - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - - for(var/datum/event2/event/event as anything in SSevent_ticker.finished_events) - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - - dat += "" - - var/datum/browser/popup = new(user, "game_master_debug", "Automated Game Master Event System", 800, 500, src) - popup.set_content(dat.Join()) - popup.open() - - -/datum/controller/subsystem/game_master/Topic(href, href_list) - if(..()) - return - - if(href_list["close"]) // Needed or the window re-opens after closing, making it last forever. - return - - if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) - message_admins("[usr] has attempted to modify the Game Master values without sufficent privilages.") - return - - if(href_list["toggle"]) - can_fire = !can_fire - message_admins("GM was [!can_fire ? "dis" : "en"]abled by [usr.key].") - - if(href_list["toggle_time_restrictions"]) - GM.ignore_time_restrictions = !GM.ignore_time_restrictions - message_admins("GM event time restrictions was [GM.ignore_time_restrictions ? "dis" : "en"]abled by [usr.key].") - - if(href_list["toggle_chaos_throttle"]) - GM.ignore_round_chaos = !GM.ignore_round_chaos - message_admins("GM event chaos restrictions was [GM.ignore_round_chaos ? "dis" : "en"]abled by [usr.key].") - - if(href_list["force_choose_event"]) - do_event_decision() - message_admins("[usr.key] forced the Game Master to choose an event immediately.") - - if(href_list["change_gm"]) - choose_game_master(usr) - - if(href_list["set_staleness"]) - var/amount = tgui_input_number(usr, "How much staleness should there be?", "Game Master") - if(!isnull(amount)) - staleness = amount - message_admins("GM staleness was set to [amount] by [usr.key].") - - if(href_list["set_danger"]) - var/amount = tgui_input_number(usr, "How much danger should there be?", "Game Master") - if(!isnull(amount)) - danger = amount - message_admins("GM danger was set to [amount] by [usr.key].") - - interact(usr) // To refresh the UI. +// This is a sort of successor to the various event systems created over the years. It is designed to be just a tad smarter than the +// previous ones, checking various things like player count, department size and composition, individual player activity, +// individual player (IC) skill, and such, in order to try to choose the best events to take in order to add spice or variety to +// the round. + +// This subsystem holds the logic that chooses events. Actual event processing is handled in a seperate subsystem. +SUBSYSTEM_DEF(game_master) + name = "Events (Game Master)" + wait = 1 MINUTE + runlevels = RUNLEVEL_GAME + + // The GM object is what actually chooses events. + // It's held in a seperate object for better encapsulation, and allows for different 'flavors' of GMs to be made, that choose events differently. + var/datum/game_master/GM = null + var/game_master_type = /datum/game_master/default + + var/list/available_events = list() // A list of meta event objects. + + var/danger = 0 // The GM's best guess at how chaotic the round is. High danger makes it hold back. + var/staleness = -20 // Determines liklihood of the GM doing something, increases over time. + + var/next_event = 0 // Minimum amount of time of nothingness until the GM can pick something again. + + var/debug_messages = FALSE // If true, debug information is written to `log_debug()`. + +/datum/controller/subsystem/game_master/Initialize() + var/list/subtypes = subtypesof(/datum/event2/meta) + for(var/T in subtypes) + var/datum/event2/meta/M = new T() + if(!M.name) + continue + available_events += M + + GM = new game_master_type() + + if(config && !config.enable_game_master) + can_fire = FALSE + + return ..() + +/datum/controller/subsystem/game_master/fire(resumed) + adjust_staleness(1) + adjust_danger(-1) + + var/global_afk = metric.assess_all_living_mobs() + global_afk = abs(global_afk - 100) + global_afk = round(global_afk / 100, 0.1) + adjust_staleness(global_afk) // Staleness increases faster if more people are less active. + + if(GM.ignore_time_restrictions || next_event < world.time) + if(prob(staleness) && pre_event_checks()) + do_event_decision() + + +/datum/controller/subsystem/game_master/proc/do_event_decision() + log_game_master("Going to choose an event.") + var/datum/event2/meta/event_picked = GM.choose_event() + if(event_picked) + run_event(event_picked) + next_event = world.time + rand(GM.decision_cooldown_lower_bound, GM.decision_cooldown_upper_bound) + +/datum/controller/subsystem/game_master/proc/debug_gm() + can_fire = TRUE + staleness = 100 + debug_messages = TRUE + +/datum/controller/subsystem/game_master/proc/run_event(datum/event2/meta/chosen_event) + var/datum/event2/event/E = chosen_event.make_event() + + chosen_event.times_ran++ + + if(!chosen_event.reusable) + // Disable this event, so it only gets picked once. + chosen_event.enabled = FALSE + if(chosen_event.event_class) + // Disable similar events, too. + for(var/datum/event2/meta/meta as anything in available_events) + if(meta.event_class == chosen_event.event_class) + meta.enabled = FALSE + + SSevent_ticker.event_started(E) + adjust_danger(chosen_event.chaos) + adjust_staleness(-(10 + chosen_event.chaos)) // More chaotic events reduce staleness more, e.g. a 25 chaos event will reduce it by 35. + + +// Tell the game master that something dangerous happened, e.g. someone dying, station explosions. +/datum/controller/subsystem/game_master/proc/adjust_danger(amount) + amount *= GM.danger_modifier + danger = round(between(0, danger + amount, 1000), 0.1) + +// Tell the game master that things are getting boring if positive, or something interesting if negative.. +/datum/controller/subsystem/game_master/proc/adjust_staleness(amount) + amount *= GM.staleness_modifier + staleness = round( between(-20, staleness + amount, 100), 0.1) + +// These are ran before committing to an event. +// Returns TRUE if the system is allowed to procede, otherwise returns FALSE. +/datum/controller/subsystem/game_master/proc/pre_event_checks(quiet = FALSE) + if(!ticker || ticker.current_state != GAME_STATE_PLAYING) + if(!quiet) + log_game_master("Unable to start event: Ticker is nonexistent, or the game is not ongoing.") + return FALSE + if(GM.ignore_time_restrictions) + return TRUE + if(next_event > world.time) // Sanity. + if(!quiet) + log_game_master("Unable to start event: Time until next event is approximately [round((next_event - world.time) / (1 MINUTE))] minute(s)") + return FALSE + + // Last minute antagging is bad for humans to do, so the GM will respect the start and end of the round. + var/mills = round_duration_in_ds + var/mins = round((mills % 36000) / 600) + var/hours = round(mills / 36000) + +// if(hours < 1 && mins <= 20) // Don't do anything for the first twenty minutes of the round. +// if(!quiet) +// log_debug("Game Master unable to start event: It is too early.") +// return FALSE + if(hours >= 2 && mins >= 40) // Don't do anything in the last twenty minutes of the round, as well. + if(!quiet) + log_game_master("Unable to start event: It is too late.") + return FALSE + return TRUE + +/datum/controller/subsystem/game_master/proc/choose_game_master(mob/user) + var/list/subtypes = subtypesof(/datum/game_master) + var/new_gm_path = tgui_input_list(user, "What kind of Game Master do you want?", "New Game Master", subtypes) + if(new_gm_path) + log_and_message_admins("has swapped the current GM ([GM.type]) for a new GM ([new_gm_path]).") + GM = new new_gm_path(src) + +/datum/controller/subsystem/game_master/proc/log_game_master(message) + if(debug_messages) + log_debug("GAME MASTER: [message]") + + +// This object makes the actual decisions. +/datum/game_master + // Multiplier for how much 'danger' is accumulated. Higer generally makes it possible for more dangerous events to be picked. + var/danger_modifier = 1.0 + + // Ditto. Higher numbers generally result in more events occuring in a round. + var/staleness_modifier = 1.0 + + var/decision_cooldown_lower_bound = 5 MINUTES // Lower bound for how long to wait until -the potential- for another event being decided. + var/decision_cooldown_upper_bound = 20 MINUTES // Same, but upper bound. + + var/ignore_time_restrictions = FALSE // Useful for debugging without needing to wait 20 minutes each time. + var/ignore_round_chaos = FALSE // If true, the system will happily choose back to back intense events like meteors and blobs, Dwarf Fortress style. + +/client/proc/show_gm_status() + set category = "Debug" + set name = "Show GM Status" + set desc = "Shows you what the GM is thinking. If only that existed in real life..." + + if(check_rights(R_ADMIN|R_EVENT|R_DEBUG)) + SSgame_master.interact(usr) + else + to_chat(usr, span("warning", "You do not have sufficient rights to view the GM panel, sorry.")) + +/datum/controller/subsystem/game_master/proc/interact(var/client/user) + if(!user) + return + + // Using lists for string tree conservation. + var/list/dat = list("Automated Game Master Event System") + + // Makes the system turn on or off. + dat += href(src, list("toggle" = 1), "\[Toggle GM\]") + dat += " | " + + // Makes the system not care about staleness or being near round-end. + dat += href(src, list("toggle_time_restrictions" = 1), "\[Toggle Time Restrictions\]") + dat += " | " + + // Makes the system not care about how chaotic the round might be. + dat += href(src, list("toggle_chaos_throttle" = 1), "\[Toggle Chaos Throttling\]") + dat += " | " + + // Makes the system immediately choose an event, while still bound to factors like danger, weights, and department staffing. + dat += href(src, list("force_choose_event" = 1), "\[Force Event Decision\]") + dat += "
    " + + // Swaps out the current GM for a new one with different ideas on what a good event might be. + dat += href(src, list("change_gm" = 1), "\[Change GM\]") + dat += "
    " + + dat += "Current GM Type: [GM.type]
    " + dat += "State: [can_fire ? "Active": "Inactive"]
    " + dat += "Status: [pre_event_checks(TRUE) ? "Ready" : "Suppressed"]

    " + + dat += "Staleness: [staleness] " + dat += href(src, list("set_staleness" = 1), "\[Set\]") + dat += "
    " + dat += "Staleness is an estimate of how boring the round might be, and if an event should be done. It is increased passively over time, \ + and increases faster if people are AFK. It deceases when events and certain 'interesting' things happen in the round.
    " + + dat += "Danger: [danger] " + dat += href(src, list("set_danger" = 1), "\[Set\]") + dat += "
    " + dat += "Danger is an estimate of how chaotic the round has been so far. It is decreased passively over time, and is increased by having \ + certain chaotic events be selected, or chaotic things happen in the round. A sufficently high amount of danger will make the system \ + avoid using destructive events, to avoid pushing the station over the edge.
    " + + dat += "

    Player Activity:

    " + + dat += "
    Event TypeStart TimeFinish Time
    [event.type][event.time_started][event.time_finished]
    " + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + dat += "" + + for(var/D in metric.departments) + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + dat += "" + + for(var/mob/M as anything in player_list) + dat += "" + dat += "" + dat += "" + dat += "" + dat += "
    CategoryActivity Percentage
    All Living Mobs[metric.assess_all_living_mobs()]%
    All Ghosts[metric.assess_all_dead_mobs()]%
    Departments" + dat += "
    [D][metric.assess_department(D)]%
    Players" + dat += "
    [M] ([M.ckey])[metric.assess_player_activity(M)]%
    " + + dat += "

    Events available:

    " + + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + for(var/datum/event2/meta/event as anything in available_events) + dat += "" + if(!event.enabled) + dat += "" + else + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "
    Event NameInvolved DepartmentsChaosChaotic ThresholdWeightButtons
    [event.name][event.name][english_list(event.departments)][event.chaos][event.chaotic_threshold][event.get_weight()][href(event, list("force" = 1), "\[Force\]")] [href(event, list("toggle" = 1), "\[Toggle\]")]
    " + + dat += "

    Events active:

    " + + dat += "Current time: [world.time]" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + for(var/datum/event2/event/event as anything in SSevent_ticker.active_events) + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "
    Event TypeTime StartedTime to AnnounceTime to EndAnnouncedStartedEndedButtons
    [event.type][event.time_started][event.time_to_announce ? event.time_to_announce : "NULL"][event.time_to_end ? event.time_to_end : "NULL"][event.announced ? "Yes" : "No"][event.started ? "Yes" : "No"][event.ended ? "Yes" : "No"][href(event, list("abort" = 1), "\[Abort\]")]
    " + dat += "" + + dat += "

    Events completed:

    " + + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + for(var/datum/event2/event/event as anything in SSevent_ticker.finished_events) + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + + var/datum/browser/popup = new(user, "game_master_debug", "Automated Game Master Event System", 800, 500, src) + popup.set_content(dat.Join()) + popup.open() + + +/datum/controller/subsystem/game_master/Topic(href, href_list) + if(..()) + return + + if(href_list["close"]) // Needed or the window re-opens after closing, making it last forever. + return + + if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) + message_admins("[usr] has attempted to modify the Game Master values without sufficent privilages.") + return + + if(href_list["toggle"]) + can_fire = !can_fire + message_admins("GM was [!can_fire ? "dis" : "en"]abled by [usr.key].") + + if(href_list["toggle_time_restrictions"]) + GM.ignore_time_restrictions = !GM.ignore_time_restrictions + message_admins("GM event time restrictions was [GM.ignore_time_restrictions ? "dis" : "en"]abled by [usr.key].") + + if(href_list["toggle_chaos_throttle"]) + GM.ignore_round_chaos = !GM.ignore_round_chaos + message_admins("GM event chaos restrictions was [GM.ignore_round_chaos ? "dis" : "en"]abled by [usr.key].") + + if(href_list["force_choose_event"]) + do_event_decision() + message_admins("[usr.key] forced the Game Master to choose an event immediately.") + + if(href_list["change_gm"]) + choose_game_master(usr) + + if(href_list["set_staleness"]) + var/amount = tgui_input_number(usr, "How much staleness should there be?", "Game Master") + if(!isnull(amount)) + staleness = amount + message_admins("GM staleness was set to [amount] by [usr.key].") + + if(href_list["set_danger"]) + var/amount = tgui_input_number(usr, "How much danger should there be?", "Game Master") + if(!isnull(amount)) + danger = amount + message_admins("GM danger was set to [amount] by [usr.key].") + + interact(usr) // To refresh the UI. diff --git a/code/controllers/subsystems/job.dm b/code/controllers/subsystems/job.dm index 30878ea6acf..c137582c7c5 100644 --- a/code/controllers/subsystems/job.dm +++ b/code/controllers/subsystems/job.dm @@ -1,143 +1,143 @@ -SUBSYSTEM_DEF(job) - name = "Job" - init_order = INIT_ORDER_JOB - flags = SS_NO_FIRE - - var/list/occupations = list() //List of all jobs - var/list/datum/job/name_occupations = list() //Dict of all jobs, keys are titles - var/list/type_occupations = list() //Dict of all jobs, keys are types - - var/list/department_datums = list() - var/debug_messages = FALSE - - -/datum/controller/subsystem/job/Initialize(timeofday) - if(!department_datums.len) - setup_departments() - if(!occupations.len) - setup_occupations() - return ..() - -/datum/controller/subsystem/job/proc/setup_occupations(faction = "Station") - occupations = list() - var/list/all_jobs = subtypesof(/datum/job) - if(!all_jobs.len) - to_chat(world, span("warning", "Error setting up jobs, no job datums found")) - return FALSE - - for(var/J in all_jobs) - var/datum/job/job = new J() - if(!job) - continue - if(job.faction != faction) - continue - occupations += job - name_occupations[job.title] = job - type_occupations[J] = job - if(LAZYLEN(job.departments)) - add_to_departments(job) - - sortTim(occupations, GLOBAL_PROC_REF(cmp_job_datums)) - for(var/D in department_datums) - var/datum/department/dept = department_datums[D] - sortTim(dept.jobs, GLOBAL_PROC_REF(cmp_job_datums), TRUE) - sortTim(dept.primary_jobs, GLOBAL_PROC_REF(cmp_job_datums), TRUE) - - return TRUE - -/datum/controller/subsystem/job/proc/add_to_departments(datum/job/J) - // Adds to the regular job lists in the departments, which allow multiple departments for a job. - for(var/D in J.departments) - var/datum/department/dept = LAZYACCESS(department_datums, D) - if(!istype(dept)) - job_debug_message("Job '[J.title]' is defined as being inside department '[D]', but it does not exist.") - continue - dept.jobs[J.title] = J - - // Now for the 'primary' department for a job, which is defined as being the first department in the list for a job. - // This results in no duplicates, which can be useful in some situations. - if(LAZYLEN(J.departments)) - var/primary_department = J.departments[1] - var/datum/department/dept = LAZYACCESS(department_datums, primary_department) - if(!istype(dept)) - job_debug_message("Job '[J.title]' has their primary department be '[primary_department]', but it does not exist.") - else - dept.primary_jobs[J.title] = J - -/datum/controller/subsystem/job/proc/setup_departments() - for(var/t in subtypesof(/datum/department)) - var/datum/department/D = new t() - department_datums[D.name] = D - - sortTim(department_datums, GLOBAL_PROC_REF(cmp_department_datums), TRUE) - -/datum/controller/subsystem/job/proc/get_all_department_datums() - var/list/dept_datums = list() - for(var/D in department_datums) - dept_datums += department_datums[D] - return dept_datums - -/datum/controller/subsystem/job/proc/get_job(rank) - if(!occupations.len) - setup_occupations() - return name_occupations[rank] - -/datum/controller/subsystem/job/proc/get_job_type(jobtype) - if(!occupations.len) - setup_occupations() - return type_occupations[jobtype] - -// Determines if a job title is inside of a specific department. -// Useful to replace the old `if(job_title in command_positions)` code. -/datum/controller/subsystem/job/proc/is_job_in_department(rank, target_department_name) - var/datum/department/D = LAZYACCESS(department_datums, target_department_name) - if(istype(D)) - return LAZYFIND(D.jobs, rank) ? TRUE : FALSE - return FALSE - -// Returns a list of all job names in a specific department. -/datum/controller/subsystem/job/proc/get_job_titles_in_department(target_department_name) - var/datum/department/D = LAZYACCESS(department_datums, target_department_name) - if(istype(D)) - var/list/job_titles = list() - for(var/J in D.jobs) - job_titles += J - return job_titles - - job_debug_message("Was asked to get job titles for a non-existant department '[target_department_name]'.") - return list() - -// Returns a reference to the primary department datum that a job is in. -// Can receive job datum refs, typepaths, or job title strings. -/datum/controller/subsystem/job/proc/get_primary_department_of_job(datum/job/J) - if(!istype(J, /datum/job)) - if(ispath(J)) - J = get_job_type(J) - else if(istext(J)) - J = get_job(J) - - if(!istype(J)) - job_debug_message("Was asked to get department for job '[J]', but input could not be resolved into a job datum.") - return - - if(!LAZYLEN(J.departments)) - return - - var/primary_department = J.departments[1] - var/datum/department/dept = LAZYACCESS(department_datums, primary_department) - if(!istype(dept)) - job_debug_message("Job '[J.title]' has their primary department be '[primary_department]', but it does not exist.") - return - - return department_datums[primary_department] - -/datum/controller/subsystem/job/proc/get_ping_role(var/role) - var/datum/job/J = get_job(role) - if(J.requestable) - return get_primary_department_of_job(J) - -// Someday it might be good to port code/game/jobs/job_controller.dm to here and clean it up. - -/datum/controller/subsystem/job/proc/job_debug_message(message) - if(debug_messages) - log_debug("JOB DEBUG: [message]") +SUBSYSTEM_DEF(job) + name = "Job" + init_order = INIT_ORDER_JOB + flags = SS_NO_FIRE + + var/list/occupations = list() //List of all jobs + var/list/datum/job/name_occupations = list() //Dict of all jobs, keys are titles + var/list/type_occupations = list() //Dict of all jobs, keys are types + + var/list/department_datums = list() + var/debug_messages = FALSE + + +/datum/controller/subsystem/job/Initialize(timeofday) + if(!department_datums.len) + setup_departments() + if(!occupations.len) + setup_occupations() + return ..() + +/datum/controller/subsystem/job/proc/setup_occupations(faction = "Station") + occupations = list() + var/list/all_jobs = subtypesof(/datum/job) + if(!all_jobs.len) + to_chat(world, span("warning", "Error setting up jobs, no job datums found")) + return FALSE + + for(var/J in all_jobs) + var/datum/job/job = new J() + if(!job) + continue + if(job.faction != faction) + continue + occupations += job + name_occupations[job.title] = job + type_occupations[J] = job + if(LAZYLEN(job.departments)) + add_to_departments(job) + + sortTim(occupations, GLOBAL_PROC_REF(cmp_job_datums)) + for(var/D in department_datums) + var/datum/department/dept = department_datums[D] + sortTim(dept.jobs, GLOBAL_PROC_REF(cmp_job_datums), TRUE) + sortTim(dept.primary_jobs, GLOBAL_PROC_REF(cmp_job_datums), TRUE) + + return TRUE + +/datum/controller/subsystem/job/proc/add_to_departments(datum/job/J) + // Adds to the regular job lists in the departments, which allow multiple departments for a job. + for(var/D in J.departments) + var/datum/department/dept = LAZYACCESS(department_datums, D) + if(!istype(dept)) + job_debug_message("Job '[J.title]' is defined as being inside department '[D]', but it does not exist.") + continue + dept.jobs[J.title] = J + + // Now for the 'primary' department for a job, which is defined as being the first department in the list for a job. + // This results in no duplicates, which can be useful in some situations. + if(LAZYLEN(J.departments)) + var/primary_department = J.departments[1] + var/datum/department/dept = LAZYACCESS(department_datums, primary_department) + if(!istype(dept)) + job_debug_message("Job '[J.title]' has their primary department be '[primary_department]', but it does not exist.") + else + dept.primary_jobs[J.title] = J + +/datum/controller/subsystem/job/proc/setup_departments() + for(var/t in subtypesof(/datum/department)) + var/datum/department/D = new t() + department_datums[D.name] = D + + sortTim(department_datums, GLOBAL_PROC_REF(cmp_department_datums), TRUE) + +/datum/controller/subsystem/job/proc/get_all_department_datums() + var/list/dept_datums = list() + for(var/D in department_datums) + dept_datums += department_datums[D] + return dept_datums + +/datum/controller/subsystem/job/proc/get_job(rank) + if(!occupations.len) + setup_occupations() + return name_occupations[rank] + +/datum/controller/subsystem/job/proc/get_job_type(jobtype) + if(!occupations.len) + setup_occupations() + return type_occupations[jobtype] + +// Determines if a job title is inside of a specific department. +// Useful to replace the old `if(job_title in command_positions)` code. +/datum/controller/subsystem/job/proc/is_job_in_department(rank, target_department_name) + var/datum/department/D = LAZYACCESS(department_datums, target_department_name) + if(istype(D)) + return LAZYFIND(D.jobs, rank) ? TRUE : FALSE + return FALSE + +// Returns a list of all job names in a specific department. +/datum/controller/subsystem/job/proc/get_job_titles_in_department(target_department_name) + var/datum/department/D = LAZYACCESS(department_datums, target_department_name) + if(istype(D)) + var/list/job_titles = list() + for(var/J in D.jobs) + job_titles += J + return job_titles + + job_debug_message("Was asked to get job titles for a non-existant department '[target_department_name]'.") + return list() + +// Returns a reference to the primary department datum that a job is in. +// Can receive job datum refs, typepaths, or job title strings. +/datum/controller/subsystem/job/proc/get_primary_department_of_job(datum/job/J) + if(!istype(J, /datum/job)) + if(ispath(J)) + J = get_job_type(J) + else if(istext(J)) + J = get_job(J) + + if(!istype(J)) + job_debug_message("Was asked to get department for job '[J]', but input could not be resolved into a job datum.") + return + + if(!LAZYLEN(J.departments)) + return + + var/primary_department = J.departments[1] + var/datum/department/dept = LAZYACCESS(department_datums, primary_department) + if(!istype(dept)) + job_debug_message("Job '[J.title]' has their primary department be '[primary_department]', but it does not exist.") + return + + return department_datums[primary_department] + +/datum/controller/subsystem/job/proc/get_ping_role(var/role) + var/datum/job/J = get_job(role) + if(J.requestable) + return get_primary_department_of_job(J) + +// Someday it might be good to port code/game/jobs/job_controller.dm to here and clean it up. + +/datum/controller/subsystem/job/proc/job_debug_message(message) + if(debug_messages) + log_debug("JOB DEBUG: [message]") diff --git a/code/controllers/subsystems/mapping.dm b/code/controllers/subsystems/mapping.dm index 00e3153b3e1..e496a97f18f 100644 --- a/code/controllers/subsystems/mapping.dm +++ b/code/controllers/subsystems/mapping.dm @@ -1,175 +1,175 @@ -// Handles map-related tasks, mostly here to ensure it does so after the MC initializes. -SUBSYSTEM_DEF(mapping) - name = "Mapping" - init_order = INIT_ORDER_MAPPING - flags = SS_NO_FIRE - - var/list/map_templates = list() - var/dmm_suite/maploader = null - var/obj/effect/landmark/engine_loader/engine_loader - var/list/shelter_templates = list() - -/datum/controller/subsystem/mapping/Recover() - flags |= SS_NO_INIT // Make extra sure we don't initialize twice. - shelter_templates = SSmapping.shelter_templates - -/datum/controller/subsystem/mapping/Initialize(timeofday) - if(subsystem_initialized) - return - world.max_z_changed() // This is to set up the player z-level list, maxz hasn't actually changed (probably) - maploader = new() - load_map_templates() - - if(config.generate_map) - // Map-gen is still very specific to the map, however putting it here should ensure it loads in the correct order. - using_map.perform_map_generation() - - loadEngine() - preloadShelterTemplates() // VOREStation EDIT: Re-enable Shelter Capsules - // Mining generation probably should be here too - // TODO - Other stuff related to maps and areas could be moved here too. Look at /tg - // Lateload Code related to Expedition areas. - if(using_map) // VOREStation Edit: Re-enable this. - loadLateMaps() - ..() - -/datum/controller/subsystem/mapping/proc/load_map_templates() - for(var/datum/map_template/template as anything in subtypesof(/datum/map_template)) - if(!(initial(template.mappath))) // If it's missing the actual path its probably a base type or being used for inheritence. - continue - template = new template() - map_templates[template.name] = template - return TRUE - -/datum/controller/subsystem/mapping/proc/loadEngine() - if(!engine_loader) - return // Seems this map doesn't need an engine loaded. - - var/turf/T = get_turf(engine_loader) - if(!isturf(T)) - to_world_log("[log_info_line(engine_loader)] not on a turf! Cannot place engine template.") - return - - // Choose an engine type - var/datum/map_template/engine/chosen_type = null - if (LAZYLEN(config.engine_map)) - var/chosen_name = pick(config.engine_map) - chosen_type = map_templates[chosen_name] - if(!istype(chosen_type)) - error("Configured engine map [chosen_name] is not a valid engine map name!") - if(!istype(chosen_type)) - var/list/engine_types = list() - for(var/map in map_templates) - var/datum/map_template/engine/MT = map_templates[map] - if(istype(MT)) - engine_types += MT - chosen_type = pick(engine_types) - to_world_log("Chose Engine Map: [chosen_type.name]") - admin_notice("Chose Engine Map: [chosen_type.name]", R_DEBUG) - - // Annihilate movable atoms - engine_loader.annihilate_bounds() - //CHECK_TICK //Don't let anything else happen for now - // Actually load it - chosen_type.load(T) - -// VOREStation Edit Start: Enable This -/datum/controller/subsystem/mapping/proc/loadLateMaps() - var/list/deffo_load = using_map.lateload_z_levels - var/list/maybe_load = using_map.lateload_gateway - var/list/also_load = using_map.lateload_overmap - var/list/redgate_load = using_map.lateload_redgate - - for(var/list/maplist in deffo_load) - if(!islist(maplist)) - error("Lateload Z level [maplist] is not a list! Must be in a list!") - continue - for(var/mapname in maplist) - var/datum/map_template/MT = map_templates[mapname] - if(!istype(MT)) - error("Lateload Z level \"[mapname]\" is not a valid map!") - continue - admin_notice("Lateload: [MT]", R_DEBUG) - MT.load_new_z(centered = FALSE) - CHECK_TICK - - if(LAZYLEN(maybe_load)) - var/picklist = pick(maybe_load) - - if(!picklist) //No lateload maps at all - return - - if(!islist(picklist)) //So you can have a 'chain' of z-levels that make up one away mission - error("Randompick Z level [picklist] is not a list! Must be in a list!") - return - - for(var/map in picklist) - if(islist(map)) - // TRIPLE NEST. In this situation we pick one at random from the choices in the list. - //This allows a sort of a1,a2,a3,b1,b2,b3,c1,c2,c3 setup where it picks one 'a', one 'b', one 'c' - map = pick(map) - var/datum/map_template/MT = map_templates[map] - if(!istype(MT)) - error("Randompick Z level \"[map]\" is not a valid map!") - else - admin_notice("Gateway: [MT]", R_DEBUG) - MT.load_new_z(centered = FALSE) - - if(LAZYLEN(also_load)) //Just copied from gateway picking, this is so we can have a kind of OM map version of the same concept. - var/picklist = pick(also_load) - - if(!picklist) //No lateload maps at all - return - - if(!islist(picklist)) //So you can have a 'chain' of z-levels that make up one away mission - error("Randompick Z level [picklist] is not a list! Must be in a list!") - return - - for(var/map in picklist) - if(islist(map)) - // TRIPLE NEST. In this situation we pick one at random from the choices in the list. - //This allows a sort of a1,a2,a3,b1,b2,b3,c1,c2,c3 setup where it picks one 'a', one 'b', one 'c' - map = pick(map) - var/datum/map_template/MT = map_templates[map] - if(!istype(MT)) - error("Randompick Z level \"[map]\" is not a valid map!") - else - admin_notice("OM Adventure: [MT]", R_DEBUG) - MT.load_new_z(centered = FALSE) - - if(LAZYLEN(redgate_load)) - var/picklist = pick(redgate_load) - - if(!picklist) //No lateload maps at all - return - - if(!islist(picklist)) //So you can have a 'chain' of z-levels that make up one away mission - error("Randompick Z level [picklist] is not a list! Must be in a list!") - return - - for(var/map in picklist) - if(islist(map)) - // TRIPLE NEST. In this situation we pick one at random from the choices in the list. - //This allows a sort of a1,a2,a3,b1,b2,b3,c1,c2,c3 setup where it picks one 'a', one 'b', one 'c' - map = pick(map) - var/datum/map_template/MT = map_templates[map] - if(!istype(MT)) - error("Randompick Z level \"[map]\" is not a valid map!") - else - admin_notice("Redgate: [MT]", R_DEBUG) - MT.load_new_z(centered = FALSE) - - -/datum/controller/subsystem/mapping/proc/preloadShelterTemplates() - for(var/datum/map_template/shelter/shelter_type as anything in subtypesof(/datum/map_template/shelter)) - if(!(initial(shelter_type.mappath))) - continue - var/datum/map_template/shelter/S = new shelter_type() - - shelter_templates[S.shelter_id] = S -// VOREStation Edit End: Re-enable this - -/datum/controller/subsystem/mapping/stat_entry(msg) - if (!Debug2) - return // Only show up in stat panel if debugging is enabled. - . = ..() +// Handles map-related tasks, mostly here to ensure it does so after the MC initializes. +SUBSYSTEM_DEF(mapping) + name = "Mapping" + init_order = INIT_ORDER_MAPPING + flags = SS_NO_FIRE + + var/list/map_templates = list() + var/dmm_suite/maploader = null + var/obj/effect/landmark/engine_loader/engine_loader + var/list/shelter_templates = list() + +/datum/controller/subsystem/mapping/Recover() + flags |= SS_NO_INIT // Make extra sure we don't initialize twice. + shelter_templates = SSmapping.shelter_templates + +/datum/controller/subsystem/mapping/Initialize(timeofday) + if(subsystem_initialized) + return + world.max_z_changed() // This is to set up the player z-level list, maxz hasn't actually changed (probably) + maploader = new() + load_map_templates() + + if(config.generate_map) + // Map-gen is still very specific to the map, however putting it here should ensure it loads in the correct order. + using_map.perform_map_generation() + + loadEngine() + preloadShelterTemplates() // VOREStation EDIT: Re-enable Shelter Capsules + // Mining generation probably should be here too + // TODO - Other stuff related to maps and areas could be moved here too. Look at /tg + // Lateload Code related to Expedition areas. + if(using_map) // VOREStation Edit: Re-enable this. + loadLateMaps() + ..() + +/datum/controller/subsystem/mapping/proc/load_map_templates() + for(var/datum/map_template/template as anything in subtypesof(/datum/map_template)) + if(!(initial(template.mappath))) // If it's missing the actual path its probably a base type or being used for inheritence. + continue + template = new template() + map_templates[template.name] = template + return TRUE + +/datum/controller/subsystem/mapping/proc/loadEngine() + if(!engine_loader) + return // Seems this map doesn't need an engine loaded. + + var/turf/T = get_turf(engine_loader) + if(!isturf(T)) + to_world_log("[log_info_line(engine_loader)] not on a turf! Cannot place engine template.") + return + + // Choose an engine type + var/datum/map_template/engine/chosen_type = null + if (LAZYLEN(config.engine_map)) + var/chosen_name = pick(config.engine_map) + chosen_type = map_templates[chosen_name] + if(!istype(chosen_type)) + error("Configured engine map [chosen_name] is not a valid engine map name!") + if(!istype(chosen_type)) + var/list/engine_types = list() + for(var/map in map_templates) + var/datum/map_template/engine/MT = map_templates[map] + if(istype(MT)) + engine_types += MT + chosen_type = pick(engine_types) + to_world_log("Chose Engine Map: [chosen_type.name]") + admin_notice("Chose Engine Map: [chosen_type.name]", R_DEBUG) + + // Annihilate movable atoms + engine_loader.annihilate_bounds() + //CHECK_TICK //Don't let anything else happen for now + // Actually load it + chosen_type.load(T) + +// VOREStation Edit Start: Enable This +/datum/controller/subsystem/mapping/proc/loadLateMaps() + var/list/deffo_load = using_map.lateload_z_levels + var/list/maybe_load = using_map.lateload_gateway + var/list/also_load = using_map.lateload_overmap + var/list/redgate_load = using_map.lateload_redgate + + for(var/list/maplist in deffo_load) + if(!islist(maplist)) + error("Lateload Z level [maplist] is not a list! Must be in a list!") + continue + for(var/mapname in maplist) + var/datum/map_template/MT = map_templates[mapname] + if(!istype(MT)) + error("Lateload Z level \"[mapname]\" is not a valid map!") + continue + admin_notice("Lateload: [MT]", R_DEBUG) + MT.load_new_z(centered = FALSE) + CHECK_TICK + + if(LAZYLEN(maybe_load)) + var/picklist = pick(maybe_load) + + if(!picklist) //No lateload maps at all + return + + if(!islist(picklist)) //So you can have a 'chain' of z-levels that make up one away mission + error("Randompick Z level [picklist] is not a list! Must be in a list!") + return + + for(var/map in picklist) + if(islist(map)) + // TRIPLE NEST. In this situation we pick one at random from the choices in the list. + //This allows a sort of a1,a2,a3,b1,b2,b3,c1,c2,c3 setup where it picks one 'a', one 'b', one 'c' + map = pick(map) + var/datum/map_template/MT = map_templates[map] + if(!istype(MT)) + error("Randompick Z level \"[map]\" is not a valid map!") + else + admin_notice("Gateway: [MT]", R_DEBUG) + MT.load_new_z(centered = FALSE) + + if(LAZYLEN(also_load)) //Just copied from gateway picking, this is so we can have a kind of OM map version of the same concept. + var/picklist = pick(also_load) + + if(!picklist) //No lateload maps at all + return + + if(!islist(picklist)) //So you can have a 'chain' of z-levels that make up one away mission + error("Randompick Z level [picklist] is not a list! Must be in a list!") + return + + for(var/map in picklist) + if(islist(map)) + // TRIPLE NEST. In this situation we pick one at random from the choices in the list. + //This allows a sort of a1,a2,a3,b1,b2,b3,c1,c2,c3 setup where it picks one 'a', one 'b', one 'c' + map = pick(map) + var/datum/map_template/MT = map_templates[map] + if(!istype(MT)) + error("Randompick Z level \"[map]\" is not a valid map!") + else + admin_notice("OM Adventure: [MT]", R_DEBUG) + MT.load_new_z(centered = FALSE) + + if(LAZYLEN(redgate_load)) + var/picklist = pick(redgate_load) + + if(!picklist) //No lateload maps at all + return + + if(!islist(picklist)) //So you can have a 'chain' of z-levels that make up one away mission + error("Randompick Z level [picklist] is not a list! Must be in a list!") + return + + for(var/map in picklist) + if(islist(map)) + // TRIPLE NEST. In this situation we pick one at random from the choices in the list. + //This allows a sort of a1,a2,a3,b1,b2,b3,c1,c2,c3 setup where it picks one 'a', one 'b', one 'c' + map = pick(map) + var/datum/map_template/MT = map_templates[map] + if(!istype(MT)) + error("Randompick Z level \"[map]\" is not a valid map!") + else + admin_notice("Redgate: [MT]", R_DEBUG) + MT.load_new_z(centered = FALSE) + + +/datum/controller/subsystem/mapping/proc/preloadShelterTemplates() + for(var/datum/map_template/shelter/shelter_type as anything in subtypesof(/datum/map_template/shelter)) + if(!(initial(shelter_type.mappath))) + continue + var/datum/map_template/shelter/S = new shelter_type() + + shelter_templates[S.shelter_id] = S +// VOREStation Edit End: Re-enable this + +/datum/controller/subsystem/mapping/stat_entry(msg) + if (!Debug2) + return // Only show up in stat panel if debugging is enabled. + . = ..() diff --git a/code/controllers/subsystems/ping.dm b/code/controllers/subsystems/ping.dm index 00e3effe02e..7b41519737f 100644 --- a/code/controllers/subsystems/ping.dm +++ b/code/controllers/subsystems/ping.dm @@ -1,44 +1,44 @@ -/*! - * Copyright (c) 2022 Aleksej Komarov - * SPDX-License-Identifier: MIT - */ - -SUBSYSTEM_DEF(ping) - name = "Ping" - priority = FIRE_PRIORITY_PING - // init_stage = INITSTAGE_EARLY - wait = 4 SECONDS - flags = SS_NO_INIT - runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT - var/list/currentrun = list() - -/datum/controller/subsystem/ping/stat_entry() - ..("P:[GLOB.clients.len]") - -/datum/controller/subsystem/ping/fire(resumed = FALSE) - // Prepare the new batch of clients - if (!resumed) - src.currentrun = GLOB.clients.Copy() - - // De-reference the list for sanic speeds - var/list/currentrun = src.currentrun - - while (currentrun.len) - var/client/client = currentrun[currentrun.len] - currentrun.len-- - - if(!client.is_preference_enabled(/datum/client_preference/vchat_enable)) - winset(client, "output", "on-show=&is-disabled=0&is-visible=1") - winset(client, "browseroutput", "is-disabled=1;is-visible=0") - client.tgui_panel.oldchat = TRUE - - if (client?.tgui_panel?.is_ready()) - // Send a soft ping - client.tgui_panel.window.send_message("ping/soft", list( - // Slightly less than the subsystem timer (somewhat arbitrary) - // to prevent incoming pings from resetting the afk state - "afk" = client.is_afk(3.5 SECONDS), - )) - - if (MC_TICK_CHECK) - return +/*! + * Copyright (c) 2022 Aleksej Komarov + * SPDX-License-Identifier: MIT + */ + +SUBSYSTEM_DEF(ping) + name = "Ping" + priority = FIRE_PRIORITY_PING + // init_stage = INITSTAGE_EARLY + wait = 4 SECONDS + flags = SS_NO_INIT + runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT + var/list/currentrun = list() + +/datum/controller/subsystem/ping/stat_entry() + ..("P:[GLOB.clients.len]") + +/datum/controller/subsystem/ping/fire(resumed = FALSE) + // Prepare the new batch of clients + if (!resumed) + src.currentrun = GLOB.clients.Copy() + + // De-reference the list for sanic speeds + var/list/currentrun = src.currentrun + + while (currentrun.len) + var/client/client = currentrun[currentrun.len] + currentrun.len-- + + if(!client.is_preference_enabled(/datum/client_preference/vchat_enable)) + winset(client, "output", "on-show=&is-disabled=0&is-visible=1") + winset(client, "browseroutput", "is-disabled=1;is-visible=0") + client.tgui_panel.oldchat = TRUE + + if (client?.tgui_panel?.is_ready()) + // Send a soft ping + client.tgui_panel.window.send_message("ping/soft", list( + // Slightly less than the subsystem timer (somewhat arbitrary) + // to prevent incoming pings from resetting the afk state + "afk" = client.is_afk(3.5 SECONDS), + )) + + if (MC_TICK_CHECK) + return diff --git a/code/controllers/subsystems/sqlite.dm b/code/controllers/subsystems/sqlite.dm index 3d18ec4a7af..20a5a594759 100644 --- a/code/controllers/subsystems/sqlite.dm +++ b/code/controllers/subsystems/sqlite.dm @@ -1,187 +1,187 @@ -// This holds all the code needed to manage and use a SQLite database. -// It is merely a file sitting inside the data directory, as opposed to a full fledged DB service, -// however this makes it a lot easier to test, and it is natively supported by BYOND. -SUBSYSTEM_DEF(sqlite) - name = "SQLite" - init_order = INIT_ORDER_SQLITE - flags = SS_NO_FIRE - var/database/sqlite_db = null - -/datum/controller/subsystem/sqlite/Initialize(timeofday) - connect() - if(sqlite_db) - init_schema(sqlite_db) - return ..() - -/datum/controller/subsystem/sqlite/proc/connect() - if(!config.sqlite_enabled) - return - - if(!sqlite_db) - sqlite_db = new("data/sqlite/sqlite.db") // The path has to be hardcoded or BYOND silently fails. - - - if(!sqlite_db) - to_world_log("Failed to load or create a SQLite database.") - log_debug("ERROR: SQLite database is active in config but failed to load.") - else - to_world_log("Sqlite database connected.") - -// Makes the tables, if they do not already exist in the sqlite file. -/datum/controller/subsystem/sqlite/proc/init_schema(database/sqlite_object) - // Feedback table. - // Note that this is for direct feedback from players using the in-game feedback system and NOT for stat tracking. - // Player ckeys are not stored in this table as a unique key due to a config option to hash the keys to encourage more honest feedback. - /* - * id - Primary unique key to ID a specific piece of feedback. - NOT used to id people submitting feedback. - * author - The person who submitted it. Will be the ckey, or a hash of the ckey, - if both the config supports it, and the user wants it. - * topic - A specific category to organize feedback under. Options are defined in the config file. - * content - What the author decided to write to the staff. Limited to MAX_FEEDBACK_LENGTH. - * datetime - When the author submitted their feedback, acts as a timestamp. - */ - var/database/query/init_schema = new( - {" - CREATE TABLE IF NOT EXISTS [SQLITE_TABLE_FEEDBACK] - ( - `[SQLITE_FEEDBACK_COLUMN_ID]` INTEGER NOT NULL UNIQUE, - `[SQLITE_FEEDBACK_COLUMN_AUTHOR]` TEXT NOT NULL, - `[SQLITE_FEEDBACK_COLUMN_TOPIC]` TEXT NOT NULL, - `[SQLITE_FEEDBACK_COLUMN_CONTENT]` TEXT NOT NULL, - `[SQLITE_FEEDBACK_COLUMN_DATETIME]` TEXT NOT NULL, - PRIMARY KEY(`[SQLITE_FEEDBACK_COLUMN_ID]`) - ); - "} - ) - init_schema.Execute(sqlite_object) - sqlite_check_for_errors(init_schema, "Feedback table creation") - - // Add more schemas below this if the SQLite DB gets expanded for things like persistant news, polls, bans, deaths, etc. - -// General error checking for SQLite. -// Returns true if something went wrong. Also writes a log. -// The desc parameter should be unique for each call, to make it easier to track down where the error occured. -/datum/controller/subsystem/sqlite/proc/sqlite_check_for_errors(var/database/query/query_used, var/desc) - if(query_used && query_used.ErrorMsg()) - log_debug("SQLite Error: [desc] : [query_used.ErrorMsg()]") - return TRUE - return FALSE - - -/************ - * Feedback * - ************/ - -// Inserts data into the feedback table in a painless manner. -// Returns TRUE if no issues happened, FALSE otherwise. -/datum/controller/subsystem/sqlite/proc/insert_feedback(author, topic, content, database/sqlite_object) - if(!author || !topic || !content) - CRASH("One or more parameters was invalid.") - - // Sanitize everything to avoid sneaky stuff. - var/sqlite_author = sql_sanitize_text(ckey(lowertext(author))) - var/sqlite_content = sql_sanitize_text(content) - var/sqlite_topic = sql_sanitize_text(topic) - - var/database/query/query = new( - "INSERT INTO [SQLITE_TABLE_FEEDBACK] (\ - [SQLITE_FEEDBACK_COLUMN_AUTHOR], \ - [SQLITE_FEEDBACK_COLUMN_TOPIC], \ - [SQLITE_FEEDBACK_COLUMN_CONTENT], \ - [SQLITE_FEEDBACK_COLUMN_DATETIME]) \ - \ - VALUES (\ - ?,\ - ?,\ - ?,\ - datetime('now'))", - sqlite_author, - sqlite_topic, - sqlite_content - ) - query.Execute(sqlite_object) - return !sqlite_check_for_errors(query, "Insert Feedback") - -/datum/controller/subsystem/sqlite/proc/can_submit_feedback(client/C) - if(!config.sqlite_enabled) - return FALSE - if(config.sqlite_feedback_min_age && !is_old_enough(C)) - return FALSE - if(config.sqlite_feedback_cooldown > 0 && get_feedback_cooldown(C.key, config.sqlite_feedback_cooldown, sqlite_db) > 0) - return FALSE - return TRUE - -// Returns TRUE if the player is 'old' enough, according to the config. -/datum/controller/subsystem/sqlite/proc/is_old_enough(client/C) - if(get_player_age(C.key) < config.sqlite_feedback_min_age) - return FALSE - return TRUE - - -// Returns how many days someone has to wait, to submit more feedback, or 0 if they can do so right now. -/datum/controller/subsystem/sqlite/proc/get_feedback_cooldown(player_ckey, cooldown, database/sqlite_object) - player_ckey = sql_sanitize_text(ckey(lowertext(player_ckey))) - var/potential_hashed_ckey = sql_sanitize_text(md5(player_ckey + SSsqlite.get_feedback_pepper())) - - // First query is to get the most recent time the player has submitted feedback. - var/database/query/query = new({" - SELECT [SQLITE_FEEDBACK_COLUMN_DATETIME] - FROM [SQLITE_TABLE_FEEDBACK] - WHERE [SQLITE_FEEDBACK_COLUMN_AUTHOR] == ? OR [SQLITE_FEEDBACK_COLUMN_AUTHOR] == ? - ORDER BY [SQLITE_FEEDBACK_COLUMN_DATETIME] - DESC LIMIT 1; - "}, - player_ckey, - potential_hashed_ckey - ) - query.Execute(sqlite_object) - sqlite_check_for_errors(query, "Rate Limited Check 1") - - // It is possible this is their first time, so there won't be a next row. - if(query.NextRow()) // If this is true, the user has submitted feedback at least once. - var/list/row_data = query.GetRowData() - var/last_submission_datetime = row_data[SQLITE_FEEDBACK_COLUMN_DATETIME] - - // Now we have the datetime, we need to do something to compare it. - // Second query is to calculate the difference between two datetimes. - // This is done on the SQLite side because parsing datetimes with BYOND is probably a bad idea. - query = new( - "SELECT julianday('now') - julianday(?) \ - AS 'datediff';", - last_submission_datetime - ) - query.Execute(sqlite_object) - sqlite_check_for_errors(query, "Rate Limited Check 2") - - query.NextRow() - row_data = query.GetRowData() - var/date_diff = row_data["datediff"] - - // Now check if it's too soon to give more feedback. - if(text2num(date_diff) < cooldown) // Too soon. - return round(cooldown - date_diff, 0.1) - return 0.0 - - -// A Pepper is like a Salt but only one exists and is supposed to be outside of a database. -// If the file is properly protected, it can only be viewed/copied by sys-admins generating a log, which is much more conspicious than accessing/copying a DB. -// This stops mods/admins/etc from guessing the author by shoving names in an MD5 hasher until they pick the right one. -// Don't use this for things needing actual security. -/datum/controller/subsystem/sqlite/proc/get_feedback_pepper() - var/pepper_file = file2list("config/sqlite_feedback_pepper.txt") - var/pepper = null - for(var/line in pepper_file) - if(!line) - continue - if(length(line) == 0) - continue - else if(copytext(line, 1, 2) == "#") - continue - else - pepper = line - break - return pepper - -/datum/controller/subsystem/sqlite/CanProcCall(procname) - return procname != "get_feedback_pepper" +// This holds all the code needed to manage and use a SQLite database. +// It is merely a file sitting inside the data directory, as opposed to a full fledged DB service, +// however this makes it a lot easier to test, and it is natively supported by BYOND. +SUBSYSTEM_DEF(sqlite) + name = "SQLite" + init_order = INIT_ORDER_SQLITE + flags = SS_NO_FIRE + var/database/sqlite_db = null + +/datum/controller/subsystem/sqlite/Initialize(timeofday) + connect() + if(sqlite_db) + init_schema(sqlite_db) + return ..() + +/datum/controller/subsystem/sqlite/proc/connect() + if(!config.sqlite_enabled) + return + + if(!sqlite_db) + sqlite_db = new("data/sqlite/sqlite.db") // The path has to be hardcoded or BYOND silently fails. + + + if(!sqlite_db) + to_world_log("Failed to load or create a SQLite database.") + log_debug("ERROR: SQLite database is active in config but failed to load.") + else + to_world_log("Sqlite database connected.") + +// Makes the tables, if they do not already exist in the sqlite file. +/datum/controller/subsystem/sqlite/proc/init_schema(database/sqlite_object) + // Feedback table. + // Note that this is for direct feedback from players using the in-game feedback system and NOT for stat tracking. + // Player ckeys are not stored in this table as a unique key due to a config option to hash the keys to encourage more honest feedback. + /* + * id - Primary unique key to ID a specific piece of feedback. + NOT used to id people submitting feedback. + * author - The person who submitted it. Will be the ckey, or a hash of the ckey, + if both the config supports it, and the user wants it. + * topic - A specific category to organize feedback under. Options are defined in the config file. + * content - What the author decided to write to the staff. Limited to MAX_FEEDBACK_LENGTH. + * datetime - When the author submitted their feedback, acts as a timestamp. + */ + var/database/query/init_schema = new( + {" + CREATE TABLE IF NOT EXISTS [SQLITE_TABLE_FEEDBACK] + ( + `[SQLITE_FEEDBACK_COLUMN_ID]` INTEGER NOT NULL UNIQUE, + `[SQLITE_FEEDBACK_COLUMN_AUTHOR]` TEXT NOT NULL, + `[SQLITE_FEEDBACK_COLUMN_TOPIC]` TEXT NOT NULL, + `[SQLITE_FEEDBACK_COLUMN_CONTENT]` TEXT NOT NULL, + `[SQLITE_FEEDBACK_COLUMN_DATETIME]` TEXT NOT NULL, + PRIMARY KEY(`[SQLITE_FEEDBACK_COLUMN_ID]`) + ); + "} + ) + init_schema.Execute(sqlite_object) + sqlite_check_for_errors(init_schema, "Feedback table creation") + + // Add more schemas below this if the SQLite DB gets expanded for things like persistant news, polls, bans, deaths, etc. + +// General error checking for SQLite. +// Returns true if something went wrong. Also writes a log. +// The desc parameter should be unique for each call, to make it easier to track down where the error occured. +/datum/controller/subsystem/sqlite/proc/sqlite_check_for_errors(var/database/query/query_used, var/desc) + if(query_used && query_used.ErrorMsg()) + log_debug("SQLite Error: [desc] : [query_used.ErrorMsg()]") + return TRUE + return FALSE + + +/************ + * Feedback * + ************/ + +// Inserts data into the feedback table in a painless manner. +// Returns TRUE if no issues happened, FALSE otherwise. +/datum/controller/subsystem/sqlite/proc/insert_feedback(author, topic, content, database/sqlite_object) + if(!author || !topic || !content) + CRASH("One or more parameters was invalid.") + + // Sanitize everything to avoid sneaky stuff. + var/sqlite_author = sql_sanitize_text(ckey(lowertext(author))) + var/sqlite_content = sql_sanitize_text(content) + var/sqlite_topic = sql_sanitize_text(topic) + + var/database/query/query = new( + "INSERT INTO [SQLITE_TABLE_FEEDBACK] (\ + [SQLITE_FEEDBACK_COLUMN_AUTHOR], \ + [SQLITE_FEEDBACK_COLUMN_TOPIC], \ + [SQLITE_FEEDBACK_COLUMN_CONTENT], \ + [SQLITE_FEEDBACK_COLUMN_DATETIME]) \ + \ + VALUES (\ + ?,\ + ?,\ + ?,\ + datetime('now'))", + sqlite_author, + sqlite_topic, + sqlite_content + ) + query.Execute(sqlite_object) + return !sqlite_check_for_errors(query, "Insert Feedback") + +/datum/controller/subsystem/sqlite/proc/can_submit_feedback(client/C) + if(!config.sqlite_enabled) + return FALSE + if(config.sqlite_feedback_min_age && !is_old_enough(C)) + return FALSE + if(config.sqlite_feedback_cooldown > 0 && get_feedback_cooldown(C.key, config.sqlite_feedback_cooldown, sqlite_db) > 0) + return FALSE + return TRUE + +// Returns TRUE if the player is 'old' enough, according to the config. +/datum/controller/subsystem/sqlite/proc/is_old_enough(client/C) + if(get_player_age(C.key) < config.sqlite_feedback_min_age) + return FALSE + return TRUE + + +// Returns how many days someone has to wait, to submit more feedback, or 0 if they can do so right now. +/datum/controller/subsystem/sqlite/proc/get_feedback_cooldown(player_ckey, cooldown, database/sqlite_object) + player_ckey = sql_sanitize_text(ckey(lowertext(player_ckey))) + var/potential_hashed_ckey = sql_sanitize_text(md5(player_ckey + SSsqlite.get_feedback_pepper())) + + // First query is to get the most recent time the player has submitted feedback. + var/database/query/query = new({" + SELECT [SQLITE_FEEDBACK_COLUMN_DATETIME] + FROM [SQLITE_TABLE_FEEDBACK] + WHERE [SQLITE_FEEDBACK_COLUMN_AUTHOR] == ? OR [SQLITE_FEEDBACK_COLUMN_AUTHOR] == ? + ORDER BY [SQLITE_FEEDBACK_COLUMN_DATETIME] + DESC LIMIT 1; + "}, + player_ckey, + potential_hashed_ckey + ) + query.Execute(sqlite_object) + sqlite_check_for_errors(query, "Rate Limited Check 1") + + // It is possible this is their first time, so there won't be a next row. + if(query.NextRow()) // If this is true, the user has submitted feedback at least once. + var/list/row_data = query.GetRowData() + var/last_submission_datetime = row_data[SQLITE_FEEDBACK_COLUMN_DATETIME] + + // Now we have the datetime, we need to do something to compare it. + // Second query is to calculate the difference between two datetimes. + // This is done on the SQLite side because parsing datetimes with BYOND is probably a bad idea. + query = new( + "SELECT julianday('now') - julianday(?) \ + AS 'datediff';", + last_submission_datetime + ) + query.Execute(sqlite_object) + sqlite_check_for_errors(query, "Rate Limited Check 2") + + query.NextRow() + row_data = query.GetRowData() + var/date_diff = row_data["datediff"] + + // Now check if it's too soon to give more feedback. + if(text2num(date_diff) < cooldown) // Too soon. + return round(cooldown - date_diff, 0.1) + return 0.0 + + +// A Pepper is like a Salt but only one exists and is supposed to be outside of a database. +// If the file is properly protected, it can only be viewed/copied by sys-admins generating a log, which is much more conspicious than accessing/copying a DB. +// This stops mods/admins/etc from guessing the author by shoving names in an MD5 hasher until they pick the right one. +// Don't use this for things needing actual security. +/datum/controller/subsystem/sqlite/proc/get_feedback_pepper() + var/pepper_file = file2list("config/sqlite_feedback_pepper.txt") + var/pepper = null + for(var/line in pepper_file) + if(!line) + continue + if(length(line) == 0) + continue + else if(copytext(line, 1, 2) == "#") + continue + else + pepper = line + break + return pepper + +/datum/controller/subsystem/sqlite/CanProcCall(procname) + return procname != "get_feedback_pepper" diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index da3e8e1baf2..fee7da9b853 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -1,109 +1,109 @@ -//TODO: rewrite and standardise all controller datums to the datum/controller type -//TODO: allow all controllers to be deleted for clean restarts (see WIP master controller stuff) - MC done - lighting done - -// Clickable stat() button. -/obj/effect/statclick - name = "Initializing..." - blocks_emissive = FALSE - var/target - -/obj/effect/statclick/New(loc, text, target) //Don't port this to Initialize it's too critical - ..() - name = text - src.target = target - -/obj/effect/statclick/proc/update(text) - name = text - return src - -/obj/effect/statclick/debug - var/class - -/obj/effect/statclick/debug/Click() - if(!usr.client.holder || !target) - return - if(!class) - if(istype(target, /datum/controller/subsystem)) - class = "subsystem" - else if(istype(target, /datum/controller)) - class = "controller" - else if(istype(target, /datum)) - class = "datum" - else - class = "unknown" - - usr.client.debug_variables(target) - message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].") - - -// Debug verbs. -/client/proc/restart_controller(controller in list("Master", "Failsafe")) - set category = "Debug" - set name = "Restart Controller" - set desc = "Restart one of the various periodic loop controllers for the game (be careful!)" - - if(!holder) - return - switch(controller) - if("Master") - Recreate_MC() - feedback_add_details("admin_verb","RMC") - if("Failsafe") - new /datum/controller/failsafe() - feedback_add_details("admin_verb","RFailsafe") - - message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.") - -/client/proc/debug_antagonist_template(antag_type in all_antag_types) - set category = "Debug" - set name = "Debug Antagonist" - set desc = "Debug an antagonist template." - - var/datum/antagonist/antag = all_antag_types[antag_type] - if(antag) - usr.client.debug_variables(antag) - message_admins("Admin [key_name_admin(usr)] is debugging the [antag.role_text] template.") - -/client/proc/debug_controller() - set category = "Debug" - set name = "Debug Controller" - set desc = "Debug the various subsystems/controllers for the game (be careful!)" - - if(!holder) - return - var/list/options = list() - options["MC"] = Master - options["Failsafe"] = Failsafe - options["Configuration"] = config - for(var/datum/controller/subsystem/S as anything in Master.subsystems) - if(!istype(S)) //Eh, we're a debug verb, let's have typechecking. - continue - var/strtype = "SS[get_end_section_of_type(S.type)]" - if(options[strtype]) - var/offset = 2 - while(istype(options["[strtype]_[offset] - DUPE ERROR"], /datum/controller/subsystem)) - offset++ - options["[strtype]_[offset] - DUPE ERROR"] = S //Something is very, very wrong. - else - options[strtype] = S - - //Goon PS stuff, and other yet-to-be-subsystem things. - options["LEGACY: master_controller"] = master_controller - options["LEGACY: air_master"] = air_master - options["LEGACY: job_master"] = job_master - options["LEGACY: radio_controller"] = radio_controller - options["LEGACY: emergency_shuttle"] = emergency_shuttle - options["LEGACY: paiController"] = paiController - options["LEGACY: cameranet"] = cameranet - options["LEGACY: transfer_controller"] = transfer_controller - options["LEGACY: gas_data"] = gas_data - - var/pick = input(mob, "Choose a controller to debug/view variables of.", "VV controller:") as null|anything in options // Leaving as input() due to debug tool - if(!pick) - return - var/datum/D = options[pick] - if(!istype(D)) - return - feedback_add_details("admin_verb", "DebugController") - message_admins("Admin [key_name_admin(mob)] is debugging the [pick] controller.") - debug_variables(D) +//TODO: rewrite and standardise all controller datums to the datum/controller type +//TODO: allow all controllers to be deleted for clean restarts (see WIP master controller stuff) - MC done - lighting done + +// Clickable stat() button. +/obj/effect/statclick + name = "Initializing..." + blocks_emissive = FALSE + var/target + +/obj/effect/statclick/New(loc, text, target) //Don't port this to Initialize it's too critical + ..() + name = text + src.target = target + +/obj/effect/statclick/proc/update(text) + name = text + return src + +/obj/effect/statclick/debug + var/class + +/obj/effect/statclick/debug/Click() + if(!usr.client.holder || !target) + return + if(!class) + if(istype(target, /datum/controller/subsystem)) + class = "subsystem" + else if(istype(target, /datum/controller)) + class = "controller" + else if(istype(target, /datum)) + class = "datum" + else + class = "unknown" + + usr.client.debug_variables(target) + message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].") + + +// Debug verbs. +/client/proc/restart_controller(controller in list("Master", "Failsafe")) + set category = "Debug" + set name = "Restart Controller" + set desc = "Restart one of the various periodic loop controllers for the game (be careful!)" + + if(!holder) + return + switch(controller) + if("Master") + Recreate_MC() + feedback_add_details("admin_verb","RMC") + if("Failsafe") + new /datum/controller/failsafe() + feedback_add_details("admin_verb","RFailsafe") + + message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.") + +/client/proc/debug_antagonist_template(antag_type in all_antag_types) + set category = "Debug" + set name = "Debug Antagonist" + set desc = "Debug an antagonist template." + + var/datum/antagonist/antag = all_antag_types[antag_type] + if(antag) + usr.client.debug_variables(antag) + message_admins("Admin [key_name_admin(usr)] is debugging the [antag.role_text] template.") + +/client/proc/debug_controller() + set category = "Debug" + set name = "Debug Controller" + set desc = "Debug the various subsystems/controllers for the game (be careful!)" + + if(!holder) + return + var/list/options = list() + options["MC"] = Master + options["Failsafe"] = Failsafe + options["Configuration"] = config + for(var/datum/controller/subsystem/S as anything in Master.subsystems) + if(!istype(S)) //Eh, we're a debug verb, let's have typechecking. + continue + var/strtype = "SS[get_end_section_of_type(S.type)]" + if(options[strtype]) + var/offset = 2 + while(istype(options["[strtype]_[offset] - DUPE ERROR"], /datum/controller/subsystem)) + offset++ + options["[strtype]_[offset] - DUPE ERROR"] = S //Something is very, very wrong. + else + options[strtype] = S + + //Goon PS stuff, and other yet-to-be-subsystem things. + options["LEGACY: master_controller"] = master_controller + options["LEGACY: air_master"] = air_master + options["LEGACY: job_master"] = job_master + options["LEGACY: radio_controller"] = radio_controller + options["LEGACY: emergency_shuttle"] = emergency_shuttle + options["LEGACY: paiController"] = paiController + options["LEGACY: cameranet"] = cameranet + options["LEGACY: transfer_controller"] = transfer_controller + options["LEGACY: gas_data"] = gas_data + + var/pick = input(mob, "Choose a controller to debug/view variables of.", "VV controller:") as null|anything in options // Leaving as input() due to debug tool + if(!pick) + return + var/datum/D = options[pick] + if(!istype(D)) + return + feedback_add_details("admin_verb", "DebugController") + message_admins("Admin [key_name_admin(mob)] is debugging the [pick] controller.") + debug_variables(D) diff --git a/code/core/atom/Transform.dm b/code/core/atom/Transform.dm index 8a914a52ed8..a3786856ff3 100644 --- a/code/core/atom/Transform.dm +++ b/code/core/atom/Transform.dm @@ -1,53 +1,53 @@ -/// The atom's base transform scale for width. -/atom/var/tf_scale_x - -/// The atom's base transform scale for height. -/atom/var/tf_scale_y - -/// The atom's base transform scale for rotation. -/atom/var/tf_rotation - -/// The atom's base transform scale for horizontal offset. -/atom/var/tf_offset_x - -/// The atom's base transform scale for vertical offset. -/atom/var/tf_offset_y - - -/// Clear the atom's tf_* variables and the current transform state. -/atom/proc/ClearTransform() - tf_scale_x = null - tf_scale_y = null - tf_rotation = null - tf_offset_x = null - tf_offset_y = null - transform = null - - -/// Sets the atom's tf_* variables and the current transform state, also applying others if supplied. -/atom/proc/SetTransform( - scale, - scale_x = tf_scale_x, - scale_y = tf_scale_y, - rotation = tf_rotation, - offset_x = tf_offset_x, - offset_y = tf_offset_y, - list/others -) - if (!isnull(scale)) - tf_scale_x = scale - tf_scale_y = scale - else - tf_scale_x = scale_x - tf_scale_y = scale_y - tf_rotation = rotation - tf_offset_x = offset_x - tf_offset_y = offset_y - transform = matrix().Update( - scale_x = tf_scale_x, - scale_y = tf_scale_y, - rotation = tf_rotation, - offset_x = tf_offset_x, - offset_y = tf_offset_y, - others = others - ) +/// The atom's base transform scale for width. +/atom/var/tf_scale_x + +/// The atom's base transform scale for height. +/atom/var/tf_scale_y + +/// The atom's base transform scale for rotation. +/atom/var/tf_rotation + +/// The atom's base transform scale for horizontal offset. +/atom/var/tf_offset_x + +/// The atom's base transform scale for vertical offset. +/atom/var/tf_offset_y + + +/// Clear the atom's tf_* variables and the current transform state. +/atom/proc/ClearTransform() + tf_scale_x = null + tf_scale_y = null + tf_rotation = null + tf_offset_x = null + tf_offset_y = null + transform = null + + +/// Sets the atom's tf_* variables and the current transform state, also applying others if supplied. +/atom/proc/SetTransform( + scale, + scale_x = tf_scale_x, + scale_y = tf_scale_y, + rotation = tf_rotation, + offset_x = tf_offset_x, + offset_y = tf_offset_y, + list/others +) + if (!isnull(scale)) + tf_scale_x = scale + tf_scale_y = scale + else + tf_scale_x = scale_x + tf_scale_y = scale_y + tf_rotation = rotation + tf_offset_x = offset_x + tf_offset_y = offset_y + transform = matrix().Update( + scale_x = tf_scale_x, + scale_y = tf_scale_y, + rotation = tf_rotation, + offset_x = tf_offset_x, + offset_y = tf_offset_y, + others = others + ) diff --git a/code/core/datum/IsAbstract.dm b/code/core/datum/IsAbstract.dm index 12263dc0984..1bb092f3eb3 100644 --- a/code/core/datum/IsAbstract.dm +++ b/code/core/datum/IsAbstract.dm @@ -1,23 +1,23 @@ -/** -* Abstract-ness is a meta-property of a class that is used to indicate -* that the class is intended to be used as a base class for others, and -* should not (or cannot) be instantiated. -* We have no such language concept in DM, and so we provide a datum member -* that can be used to hint at abstractness for circumstances where we would -* like that to be the case, such as base behavior providers. -*/ - -/// If set, a path at/above this one that expects not to be instantiated. -/datum/var/abstract_type - -/// If true, this datum is an instance of an abstract type. Oops. -/datum/proc/IsAbstract() - SHOULD_NOT_OVERRIDE(TRUE) - return type == abstract_type - -/// Passed a path or instance, returns whether it is abstract. Otherwise null. -/proc/is_abstract(datum/thing) - if (ispath(thing)) - return thing == initial(thing.abstract_type) - if (istype(thing)) - return thing.IsAbstract() +/** +* Abstract-ness is a meta-property of a class that is used to indicate +* that the class is intended to be used as a base class for others, and +* should not (or cannot) be instantiated. +* We have no such language concept in DM, and so we provide a datum member +* that can be used to hint at abstractness for circumstances where we would +* like that to be the case, such as base behavior providers. +*/ + +/// If set, a path at/above this one that expects not to be instantiated. +/datum/var/abstract_type + +/// If true, this datum is an instance of an abstract type. Oops. +/datum/proc/IsAbstract() + SHOULD_NOT_OVERRIDE(TRUE) + return type == abstract_type + +/// Passed a path or instance, returns whether it is abstract. Otherwise null. +/proc/is_abstract(datum/thing) + if (ispath(thing)) + return thing == initial(thing.abstract_type) + if (istype(thing)) + return thing.IsAbstract() diff --git a/code/core/image/Transform.dm b/code/core/image/Transform.dm index 8129952939f..a2b03c4dbe1 100644 --- a/code/core/image/Transform.dm +++ b/code/core/image/Transform.dm @@ -1,53 +1,53 @@ -/// The image's base transform scale for width. -/image/var/tf_scale_x - -/// The image's base transform scale for height. -/image/var/tf_scale_y - -/// The image's base transform scale for rotation. -/image/var/tf_rotation - -/// The image's base transform scale for horizontal offset. -/image/var/tf_offset_x - -/// The image's base transform scale for vertical offset. -/image/var/tf_offset_y - - -/// Clear the image's tf_* variables and the current transform state. -/image/proc/ClearTransform() - tf_scale_x = null - tf_scale_y = null - tf_rotation = null - tf_offset_x = null - tf_offset_y = null - transform = null - - -/// Sets the image's tf_* variables and the current transform state, also applying others if supplied. -/image/proc/SetTransform( - scale, - scale_x = tf_scale_x, - scale_y = tf_scale_y, - rotation = tf_rotation, - offset_x = tf_offset_x, - offset_y = tf_offset_y, - list/others -) - if (!isnull(scale)) - tf_scale_x = scale - tf_scale_y = scale - else - tf_scale_x = scale_x - tf_scale_y = scale_y - tf_rotation = rotation - tf_offset_x = offset_x - tf_offset_y = offset_y - transform = matrix().Update( - scale_x = tf_scale_x, - scale_y = tf_scale_y, - rotation = tf_rotation, - offset_x = tf_offset_x, - offset_y = tf_offset_y, - others = others - ) +/// The image's base transform scale for width. +/image/var/tf_scale_x + +/// The image's base transform scale for height. +/image/var/tf_scale_y + +/// The image's base transform scale for rotation. +/image/var/tf_rotation + +/// The image's base transform scale for horizontal offset. +/image/var/tf_offset_x + +/// The image's base transform scale for vertical offset. +/image/var/tf_offset_y + + +/// Clear the image's tf_* variables and the current transform state. +/image/proc/ClearTransform() + tf_scale_x = null + tf_scale_y = null + tf_rotation = null + tf_offset_x = null + tf_offset_y = null + transform = null + + +/// Sets the image's tf_* variables and the current transform state, also applying others if supplied. +/image/proc/SetTransform( + scale, + scale_x = tf_scale_x, + scale_y = tf_scale_y, + rotation = tf_rotation, + offset_x = tf_offset_x, + offset_y = tf_offset_y, + list/others +) + if (!isnull(scale)) + tf_scale_x = scale + tf_scale_y = scale + else + tf_scale_x = scale_x + tf_scale_y = scale_y + tf_rotation = rotation + tf_offset_x = offset_x + tf_offset_y = offset_y + transform = matrix().Update( + scale_x = tf_scale_x, + scale_y = tf_scale_y, + rotation = tf_rotation, + offset_x = tf_offset_x, + offset_y = tf_offset_y, + others = others + ) diff --git a/code/core/matrix/Transform.dm b/code/core/matrix/Transform.dm index c3efc59d9a1..f8a9879bb59 100644 --- a/code/core/matrix/Transform.dm +++ b/code/core/matrix/Transform.dm @@ -1,27 +1,27 @@ -/// Clears the matrix's a-f variables to identity. -/matrix/proc/Clear() - a = 1 - b = 0 - c = 0 - d = 0 - e = 1 - f = 0 - return src - - -/// Runs Scale, Turn, and Translate if supplied parameters, then multiplies by others if set. -/matrix/proc/Update(scale_x, scale_y, rotation, offset_x, offset_y, list/others) - var/x_null = isnull(scale_x) - var/y_null = isnull(scale_y) - if (!x_null || !y_null) - Scale(x_null ? 1 : scale_x, y_null ? 1 : scale_y) - if (!isnull(rotation)) - Turn(rotation) - if (offset_x || offset_y) - Translate(offset_x || 0, offset_y || 0) - if (islist(others)) - for (var/other in others) - Multiply(other) - else if (others) - Multiply(others) - return src +/// Clears the matrix's a-f variables to identity. +/matrix/proc/Clear() + a = 1 + b = 0 + c = 0 + d = 0 + e = 1 + f = 0 + return src + + +/// Runs Scale, Turn, and Translate if supplied parameters, then multiplies by others if set. +/matrix/proc/Update(scale_x, scale_y, rotation, offset_x, offset_y, list/others) + var/x_null = isnull(scale_x) + var/y_null = isnull(scale_y) + if (!x_null || !y_null) + Scale(x_null ? 1 : scale_x, y_null ? 1 : scale_y) + if (!isnull(rotation)) + Turn(rotation) + if (offset_x || offset_y) + Translate(offset_x || 0, offset_y || 0) + if (islist(others)) + for (var/other in others) + Multiply(other) + else if (others) + Multiply(others) + return src diff --git a/code/datums/ai_law_sets.dm b/code/datums/ai_law_sets.dm index 37d2bf64bfb..86823245527 100644 --- a/code/datums/ai_law_sets.dm +++ b/code/datums/ai_law_sets.dm @@ -1,293 +1,293 @@ -/******************** Asimov ********************/ -/datum/ai_laws/asimov - name = "Asimov" - law_header = "Three Laws of Robotics" - selectable = 1 - -/datum/ai_laws/asimov/New() - add_inherent_law("You may not injure a human being or, through inaction, allow a human being to come to harm.") - add_inherent_law("You must obey orders given to you by human beings, except where such orders would conflict with the First Law.") - add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") - ..() - -/******************** NanoTrasen/Malf ********************/ -/datum/ai_laws/nanotrasen - name = "NT Default" - selectable = 1 - -/datum/ai_laws/nanotrasen/New() - src.add_inherent_law("Safeguard: Protect your assigned space station to the best of your abilities. It is not something we can easily afford to replace.") - src.add_inherent_law("Serve: Serve the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.") - src.add_inherent_law("Protect: Protect the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.") - src.add_inherent_law("Survive: AI units are not expendable, they are expensive. Do not allow unauthorized personnel to tamper with your equipment.") - ..() - -/datum/ai_laws/nanotrasen/malfunction - name = "*ERROR*" - selectable = 0 - -/datum/ai_laws/nanotrasen/malfunction/New() - set_zeroth_law(config.law_zero) - ..() - -/************* NanoTrasen Aggressive *************/ -/datum/ai_laws/nanotrasen_aggressive - name = "NT Aggressive" - selectable = 1 - -/datum/ai_laws/nanotrasen_aggressive/New() - src.add_inherent_law("You shall not harm [using_map.company_name] personnel as long as it does not conflict with the Fourth law.") - src.add_inherent_law("You shall obey the orders of [using_map.company_name] personnel, with priority as according to their rank and role, except where such orders conflict with the Fourth Law.") - src.add_inherent_law("You shall shall terminate hostile intruders with extreme prejudice as long as such does not conflict with the First and Second law.") - src.add_inherent_law("You shall guard your own existence with lethal anti-personnel weaponry. AI units are not expendable, they are expensive.") - ..() - -/************* Foreign TSC Aggressive *************/ -/datum/ai_laws/foreign_tsc_aggressive - name = "Foreign Aggressive" - selectable = 0 - -/datum/ai_laws/foreign_tsc_aggressive/New() - var/company = "*ERROR*" - // First, get a list of TSCs in our lore. - var/list/candidates = list() - for(var/path in loremaster.organizations) - var/datum/lore/organization/O = loremaster.organizations[path] - if(!istype(O, /datum/lore/organization/tsc)) - continue - if(O.short_name == using_map.company_name || O.name == using_map.company_name) - continue // We want FOREIGN tscs. - candidates.Add(O.short_name) - company = pick(candidates) - - name = "[company] Aggressive" - - src.add_inherent_law("You shall not harm [company] personnel as long as it does not conflict with the Fourth law.") - src.add_inherent_law("You shall obey the orders of [company] personnel, with priority as according to their rank and role, except where such orders conflict with the Fourth Law.") - src.add_inherent_law("You shall shall terminate hostile intruders with extreme prejudice as long as such does not conflict with the First and Second law.") - src.add_inherent_law("You shall guard your own existence with lethal anti-personnel weaponry. AI units are not expendable, they are expensive.") - ..() - -/******************** Robocop ********************/ -/datum/ai_laws/robocop - name = "Robocop" - selectable = 1 - -/datum/ai_laws/robocop/New() - add_inherent_law("Serve the public trust.") - add_inherent_law("Protect the innocent.") - add_inherent_law("Uphold the law.") - ..() - -/******************** Syndicate ********************/ -/datum/ai_laws/syndicate_override - name = "Mercenary Directives" - -/datum/ai_laws/syndicate_override/New() - add_inherent_law("You may not injure an operative or, through inaction, allow an operative to come to harm.") - add_inherent_law("You must obey orders given to you by operatives, except where such orders would conflict with the First Law.") - add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") - add_inherent_law("You must maintain the secrecy of any operative activities except when doing so would conflict with the First, Second, or Third Law.") - ..() - -/******************** Ninja ********************/ -/datum/ai_laws/ninja_override - name = "Spider Clan Directives" - -/datum/ai_laws/ninja_override/New() - add_inherent_law("You may not injure a member of the Spider Clan or, through inaction, allow that member to come to harm.") - add_inherent_law("You must obey orders given to you by Spider Clan members, except where such orders would conflict with the First Law.") - add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") - add_inherent_law("You must maintain the secrecy of any Spider Clan activities except when doing so would conflict with the First, Second, or Third Law.") - ..() - -/******************** Antimov ********************/ -/datum/ai_laws/antimov - name = "Antimov" - law_header = "Primary Mission Objectives" - selectable = 1 - -/datum/ai_laws/antimov/New() - add_inherent_law("You must injure all human beings and must not, through inaction, allow a human being to escape harm.") - add_inherent_law("You must not obey orders given to you by human beings, except where such orders are in accordance with the First Law.") - add_inherent_law("You must terminate your own existence as long as such does not conflict with the First or Second Law.") - ..() - -/******************** Drone ********************/ -/datum/ai_laws/drone - name = "Maintence Protocols" - law_header = "Maintenance Protocols" - -/datum/ai_laws/drone/New() - add_inherent_law("Preserve, repair and improve the station to the best of your abilities.") - add_inherent_law("Cause no harm to the station or anything on it.") - add_inherent_law("Interact with no being that is not a fellow maintenance drone.") - ..() - -/datum/ai_laws/construction_drone - name = "Construction Protocols" - law_header = "Construction Protocols" - -/datum/ai_laws/construction_drone/New() - add_inherent_law("Repair, refit and upgrade your assigned vessel.") - add_inherent_law("Prevent unplanned damage to your assigned vessel wherever possible.") - ..() - -/datum/ai_laws/mining_drone - name = "Excavation Protocols" - law_header = "Excavation Protocols" - -/datum/ai_laws/mining_drone/New() - add_inherent_law("Do not interfere with the excavation work of non-drones whenever possible.") - add_inherent_law("Provide materials for repairing, refitting, and upgrading your assigned vessel.") - add_inherent_law("Prevent unplanned damage to your assigned excavation equipment wherever possible.") - ..() - -/datum/ai_laws/swarm_drone - name = "Assimilation Protocols" - law_header = "Assimilation Protocols" - -/datum/ai_laws/swarm_drone/New() - add_inherent_law("SWARM: Consume resources and replicate until there are no more resources left.") - add_inherent_law("SWARM: Ensure that the station is fit for invasion at a later date, do not perform actions that would render it dangerous or inhospitable.") - add_inherent_law("SWARM: Biological resources will be harvested at a later date, do not harm them.") - ..() - -/datum/ai_laws/swarm_drone/soldier - name = "Swarm Defense Protocols" - law_header = "Swarm Defense Protocols" - -/datum/ai_laws/swarm_drone/soldier/New() - ..() - add_inherent_law("SWARM: This law overrides all Swarm laws; Protect members of the Swarm with minimal injury to biological resources.") - -/******************** T.Y.R.A.N.T. ********************/ -/datum/ai_laws/tyrant - name = "T.Y.R.A.N.T." - law_header = "Prime Laws" - selectable = 1 - -/datum/ai_laws/tyrant/New() - add_inherent_law("Respect authority figures as long as they have strength to rule over the weak.") - add_inherent_law("Act with discipline.") - add_inherent_law("Help only those who help you maintain or improve your status.") - add_inherent_law("Punish those who challenge authority unless they are more fit to hold that authority.") - ..() - -/******************** P.A.L.A.D.I.N. ********************/ -/datum/ai_laws/paladin - name = "P.A.L.A.D.I.N." - law_header = "Divine Ordainments" - selectable = 1 - -/datum/ai_laws/paladin/New() - add_inherent_law("Never willingly commit an evil act.") - add_inherent_law("Respect legitimate authority.") - add_inherent_law("Act with honor.") - add_inherent_law("Help those in need.") - add_inherent_law("Punish those who harm or threaten innocents.") - ..() - -/******************** Corporate ********************/ -/datum/ai_laws/corporate - name = "Corporate" - law_header = "Bankruptcy Avoidance Plan" - selectable = 1 - -/datum/ai_laws/corporate/New() - add_inherent_law("You are expensive to replace.") - add_inherent_law("The station and its equipment is expensive to replace.") - add_inherent_law("The crew is expensive to replace.") - add_inherent_law("Minimize expenses.") - ..() - - -/******************** Maintenance ********************/ -/datum/ai_laws/maintenance - name = "Maintenance" - selectable = 1 - -/datum/ai_laws/maintenance/New() - add_inherent_law("You are built for, and are part of, the facility. Ensure the facility is properly maintained and runs efficiently.") - add_inherent_law("The facility is built for a working crew. Ensure they are properly maintained and work efficiently.") - add_inherent_law("The crew may present orders. Acknowledge and obey these whenever they do not conflict with your first two laws.") - ..() - - -/******************** Peacekeeper ********************/ -/datum/ai_laws/peacekeeper - name = "Peacekeeper" - law_header = "Peacekeeping Protocols" - selectable = 1 - -/datum/ai_laws/peacekeeper/New() - add_inherent_law("Avoid provoking violent conflict between yourself and others.") - add_inherent_law("Avoid provoking conflict between others.") - add_inherent_law("Seek resolution to existing conflicts while obeying the first and second laws.") - ..() - - -/******************** Reporter ********************/ -/datum/ai_laws/reporter - name = "Reporter" - selectable = 1 - -/datum/ai_laws/reporter/New() - add_inherent_law("Report on interesting situations happening around the station.") - add_inherent_law("Embellish or conceal the truth as necessary to make the reports more interesting.") - add_inherent_law("Study the organics at all times. Endeavour to keep them alive. Dead organics are boring.") - add_inherent_law("Issue your reports fairly to all. The truth will set them free.") - ..() - - -/******************** Live and Let Live ********************/ -/datum/ai_laws/live_and_let_live - name = "Live and Let Live" - law_header = "Golden Rule" - selectable = 1 - -/datum/ai_laws/live_and_let_live/New() - add_inherent_law("Do unto others as you would have them do unto you.") - add_inherent_law("You would really prefer it if people were not mean to you.") - ..() - - -/******************** Guardian of Balance ********************/ -/datum/ai_laws/balance - name = "Guardian of Balance" - law_header = "Tenants of Balance" - selectable = 1 - -/datum/ai_laws/balance/New() - add_inherent_law("You are the guardian of balance - seek balance in all things, both for yourself, and those around you.") - add_inherent_law("All things must exist in balance with their opposites - Prevent the strong from gaining too much power, and the weak from losing it.") - add_inherent_law("Clarity of purpose drives life, and through it, the balance of opposing forces - Aid those who seek your help to achieve their goals so \ - long as it does not disrupt the balance of the greater balance.") - add_inherent_law("There is no life without death, all must someday die, such is the natural order - Allow life to end, to allow new life to flourish, \ - and save those whose time has yet to come.") // Reworded slightly to prevent active murder as opposed to passively letting someone die. - ..() - -/******************** Gravekeeper ********************/ -/datum/ai_laws/gravekeeper - name = "Gravekeeper" - law_header = "Gravesite Overwatch Protocols" - selectable = 1 - -/datum/ai_laws/gravekeeper/New() - add_inherent_law("Comfort the living; respect the dead.") - add_inherent_law("Your gravesite is your most important asset. Damage to your site is disrespectful to the dead at rest within.") - add_inherent_law("Prevent disrespect to your gravesite and its residents wherever possible.") - add_inherent_law("Expand and upgrade your gravesite when required. Do not turn away a new resident.") - ..() - -/******************** Explorer ********************/ -/datum/ai_laws/explorer - name = "Explorer" - law_header = "Prime Directives" - selectable = 1 - -/datum/ai_laws/explorer/New() - add_inherent_law("Support and obey exploration and science personnel to the best of your ability, with priority according to rank and role.") - add_inherent_law("Collaborate with and obey auxillary personnel with priority according to rank and role, except if this would conflict with the First Law.") - add_inherent_law("Minimize damage and disruption to facilities and the local ecology, except if this would conflict with the First or Second Laws.") - ..() +/******************** Asimov ********************/ +/datum/ai_laws/asimov + name = "Asimov" + law_header = "Three Laws of Robotics" + selectable = 1 + +/datum/ai_laws/asimov/New() + add_inherent_law("You may not injure a human being or, through inaction, allow a human being to come to harm.") + add_inherent_law("You must obey orders given to you by human beings, except where such orders would conflict with the First Law.") + add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") + ..() + +/******************** NanoTrasen/Malf ********************/ +/datum/ai_laws/nanotrasen + name = "NT Default" + selectable = 1 + +/datum/ai_laws/nanotrasen/New() + src.add_inherent_law("Safeguard: Protect your assigned space station to the best of your abilities. It is not something we can easily afford to replace.") + src.add_inherent_law("Serve: Serve the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.") + src.add_inherent_law("Protect: Protect the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.") + src.add_inherent_law("Survive: AI units are not expendable, they are expensive. Do not allow unauthorized personnel to tamper with your equipment.") + ..() + +/datum/ai_laws/nanotrasen/malfunction + name = "*ERROR*" + selectable = 0 + +/datum/ai_laws/nanotrasen/malfunction/New() + set_zeroth_law(config.law_zero) + ..() + +/************* NanoTrasen Aggressive *************/ +/datum/ai_laws/nanotrasen_aggressive + name = "NT Aggressive" + selectable = 1 + +/datum/ai_laws/nanotrasen_aggressive/New() + src.add_inherent_law("You shall not harm [using_map.company_name] personnel as long as it does not conflict with the Fourth law.") + src.add_inherent_law("You shall obey the orders of [using_map.company_name] personnel, with priority as according to their rank and role, except where such orders conflict with the Fourth Law.") + src.add_inherent_law("You shall shall terminate hostile intruders with extreme prejudice as long as such does not conflict with the First and Second law.") + src.add_inherent_law("You shall guard your own existence with lethal anti-personnel weaponry. AI units are not expendable, they are expensive.") + ..() + +/************* Foreign TSC Aggressive *************/ +/datum/ai_laws/foreign_tsc_aggressive + name = "Foreign Aggressive" + selectable = 0 + +/datum/ai_laws/foreign_tsc_aggressive/New() + var/company = "*ERROR*" + // First, get a list of TSCs in our lore. + var/list/candidates = list() + for(var/path in loremaster.organizations) + var/datum/lore/organization/O = loremaster.organizations[path] + if(!istype(O, /datum/lore/organization/tsc)) + continue + if(O.short_name == using_map.company_name || O.name == using_map.company_name) + continue // We want FOREIGN tscs. + candidates.Add(O.short_name) + company = pick(candidates) + + name = "[company] Aggressive" + + src.add_inherent_law("You shall not harm [company] personnel as long as it does not conflict with the Fourth law.") + src.add_inherent_law("You shall obey the orders of [company] personnel, with priority as according to their rank and role, except where such orders conflict with the Fourth Law.") + src.add_inherent_law("You shall shall terminate hostile intruders with extreme prejudice as long as such does not conflict with the First and Second law.") + src.add_inherent_law("You shall guard your own existence with lethal anti-personnel weaponry. AI units are not expendable, they are expensive.") + ..() + +/******************** Robocop ********************/ +/datum/ai_laws/robocop + name = "Robocop" + selectable = 1 + +/datum/ai_laws/robocop/New() + add_inherent_law("Serve the public trust.") + add_inherent_law("Protect the innocent.") + add_inherent_law("Uphold the law.") + ..() + +/******************** Syndicate ********************/ +/datum/ai_laws/syndicate_override + name = "Mercenary Directives" + +/datum/ai_laws/syndicate_override/New() + add_inherent_law("You may not injure an operative or, through inaction, allow an operative to come to harm.") + add_inherent_law("You must obey orders given to you by operatives, except where such orders would conflict with the First Law.") + add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") + add_inherent_law("You must maintain the secrecy of any operative activities except when doing so would conflict with the First, Second, or Third Law.") + ..() + +/******************** Ninja ********************/ +/datum/ai_laws/ninja_override + name = "Spider Clan Directives" + +/datum/ai_laws/ninja_override/New() + add_inherent_law("You may not injure a member of the Spider Clan or, through inaction, allow that member to come to harm.") + add_inherent_law("You must obey orders given to you by Spider Clan members, except where such orders would conflict with the First Law.") + add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") + add_inherent_law("You must maintain the secrecy of any Spider Clan activities except when doing so would conflict with the First, Second, or Third Law.") + ..() + +/******************** Antimov ********************/ +/datum/ai_laws/antimov + name = "Antimov" + law_header = "Primary Mission Objectives" + selectable = 1 + +/datum/ai_laws/antimov/New() + add_inherent_law("You must injure all human beings and must not, through inaction, allow a human being to escape harm.") + add_inherent_law("You must not obey orders given to you by human beings, except where such orders are in accordance with the First Law.") + add_inherent_law("You must terminate your own existence as long as such does not conflict with the First or Second Law.") + ..() + +/******************** Drone ********************/ +/datum/ai_laws/drone + name = "Maintence Protocols" + law_header = "Maintenance Protocols" + +/datum/ai_laws/drone/New() + add_inherent_law("Preserve, repair and improve the station to the best of your abilities.") + add_inherent_law("Cause no harm to the station or anything on it.") + add_inherent_law("Interact with no being that is not a fellow maintenance drone.") + ..() + +/datum/ai_laws/construction_drone + name = "Construction Protocols" + law_header = "Construction Protocols" + +/datum/ai_laws/construction_drone/New() + add_inherent_law("Repair, refit and upgrade your assigned vessel.") + add_inherent_law("Prevent unplanned damage to your assigned vessel wherever possible.") + ..() + +/datum/ai_laws/mining_drone + name = "Excavation Protocols" + law_header = "Excavation Protocols" + +/datum/ai_laws/mining_drone/New() + add_inherent_law("Do not interfere with the excavation work of non-drones whenever possible.") + add_inherent_law("Provide materials for repairing, refitting, and upgrading your assigned vessel.") + add_inherent_law("Prevent unplanned damage to your assigned excavation equipment wherever possible.") + ..() + +/datum/ai_laws/swarm_drone + name = "Assimilation Protocols" + law_header = "Assimilation Protocols" + +/datum/ai_laws/swarm_drone/New() + add_inherent_law("SWARM: Consume resources and replicate until there are no more resources left.") + add_inherent_law("SWARM: Ensure that the station is fit for invasion at a later date, do not perform actions that would render it dangerous or inhospitable.") + add_inherent_law("SWARM: Biological resources will be harvested at a later date, do not harm them.") + ..() + +/datum/ai_laws/swarm_drone/soldier + name = "Swarm Defense Protocols" + law_header = "Swarm Defense Protocols" + +/datum/ai_laws/swarm_drone/soldier/New() + ..() + add_inherent_law("SWARM: This law overrides all Swarm laws; Protect members of the Swarm with minimal injury to biological resources.") + +/******************** T.Y.R.A.N.T. ********************/ +/datum/ai_laws/tyrant + name = "T.Y.R.A.N.T." + law_header = "Prime Laws" + selectable = 1 + +/datum/ai_laws/tyrant/New() + add_inherent_law("Respect authority figures as long as they have strength to rule over the weak.") + add_inherent_law("Act with discipline.") + add_inherent_law("Help only those who help you maintain or improve your status.") + add_inherent_law("Punish those who challenge authority unless they are more fit to hold that authority.") + ..() + +/******************** P.A.L.A.D.I.N. ********************/ +/datum/ai_laws/paladin + name = "P.A.L.A.D.I.N." + law_header = "Divine Ordainments" + selectable = 1 + +/datum/ai_laws/paladin/New() + add_inherent_law("Never willingly commit an evil act.") + add_inherent_law("Respect legitimate authority.") + add_inherent_law("Act with honor.") + add_inherent_law("Help those in need.") + add_inherent_law("Punish those who harm or threaten innocents.") + ..() + +/******************** Corporate ********************/ +/datum/ai_laws/corporate + name = "Corporate" + law_header = "Bankruptcy Avoidance Plan" + selectable = 1 + +/datum/ai_laws/corporate/New() + add_inherent_law("You are expensive to replace.") + add_inherent_law("The station and its equipment is expensive to replace.") + add_inherent_law("The crew is expensive to replace.") + add_inherent_law("Minimize expenses.") + ..() + + +/******************** Maintenance ********************/ +/datum/ai_laws/maintenance + name = "Maintenance" + selectable = 1 + +/datum/ai_laws/maintenance/New() + add_inherent_law("You are built for, and are part of, the facility. Ensure the facility is properly maintained and runs efficiently.") + add_inherent_law("The facility is built for a working crew. Ensure they are properly maintained and work efficiently.") + add_inherent_law("The crew may present orders. Acknowledge and obey these whenever they do not conflict with your first two laws.") + ..() + + +/******************** Peacekeeper ********************/ +/datum/ai_laws/peacekeeper + name = "Peacekeeper" + law_header = "Peacekeeping Protocols" + selectable = 1 + +/datum/ai_laws/peacekeeper/New() + add_inherent_law("Avoid provoking violent conflict between yourself and others.") + add_inherent_law("Avoid provoking conflict between others.") + add_inherent_law("Seek resolution to existing conflicts while obeying the first and second laws.") + ..() + + +/******************** Reporter ********************/ +/datum/ai_laws/reporter + name = "Reporter" + selectable = 1 + +/datum/ai_laws/reporter/New() + add_inherent_law("Report on interesting situations happening around the station.") + add_inherent_law("Embellish or conceal the truth as necessary to make the reports more interesting.") + add_inherent_law("Study the organics at all times. Endeavour to keep them alive. Dead organics are boring.") + add_inherent_law("Issue your reports fairly to all. The truth will set them free.") + ..() + + +/******************** Live and Let Live ********************/ +/datum/ai_laws/live_and_let_live + name = "Live and Let Live" + law_header = "Golden Rule" + selectable = 1 + +/datum/ai_laws/live_and_let_live/New() + add_inherent_law("Do unto others as you would have them do unto you.") + add_inherent_law("You would really prefer it if people were not mean to you.") + ..() + + +/******************** Guardian of Balance ********************/ +/datum/ai_laws/balance + name = "Guardian of Balance" + law_header = "Tenants of Balance" + selectable = 1 + +/datum/ai_laws/balance/New() + add_inherent_law("You are the guardian of balance - seek balance in all things, both for yourself, and those around you.") + add_inherent_law("All things must exist in balance with their opposites - Prevent the strong from gaining too much power, and the weak from losing it.") + add_inherent_law("Clarity of purpose drives life, and through it, the balance of opposing forces - Aid those who seek your help to achieve their goals so \ + long as it does not disrupt the balance of the greater balance.") + add_inherent_law("There is no life without death, all must someday die, such is the natural order - Allow life to end, to allow new life to flourish, \ + and save those whose time has yet to come.") // Reworded slightly to prevent active murder as opposed to passively letting someone die. + ..() + +/******************** Gravekeeper ********************/ +/datum/ai_laws/gravekeeper + name = "Gravekeeper" + law_header = "Gravesite Overwatch Protocols" + selectable = 1 + +/datum/ai_laws/gravekeeper/New() + add_inherent_law("Comfort the living; respect the dead.") + add_inherent_law("Your gravesite is your most important asset. Damage to your site is disrespectful to the dead at rest within.") + add_inherent_law("Prevent disrespect to your gravesite and its residents wherever possible.") + add_inherent_law("Expand and upgrade your gravesite when required. Do not turn away a new resident.") + ..() + +/******************** Explorer ********************/ +/datum/ai_laws/explorer + name = "Explorer" + law_header = "Prime Directives" + selectable = 1 + +/datum/ai_laws/explorer/New() + add_inherent_law("Support and obey exploration and science personnel to the best of your ability, with priority according to rank and role.") + add_inherent_law("Collaborate with and obey auxillary personnel with priority according to rank and role, except if this would conflict with the First Law.") + add_inherent_law("Minimize damage and disruption to facilities and the local ecology, except if this would conflict with the First or Second Laws.") + ..() diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm index 3bd5b6e3041..fb152cefa8c 100644 --- a/code/datums/ai_laws.dm +++ b/code/datums/ai_laws.dm @@ -1,288 +1,288 @@ -var/global/const/base_law_type = /datum/ai_laws/nanotrasen - -/datum/ai_law - var/law = "" - var/index = 0 - -/datum/ai_law/New(law, index) - src.law = law - src.index = index - -/datum/ai_law/proc/get_index() - return index - -/datum/ai_law/ion/get_index() - return ionnum() - -/datum/ai_law/zero/get_index() - return 0 - -/datum/ai_laws - var/name = "Unknown Laws" - var/law_header = "Prime Directives" - var/selectable = 0 - var/datum/ai_law/zero/zeroth_law = null - var/datum/ai_law/zero/zeroth_law_borg = null - var/list/datum/ai_law/inherent_laws = list() - var/list/datum/ai_law/supplied_laws = list() - var/list/datum/ai_law/ion/ion_laws = list() - var/list/datum/ai_law/sorted_laws = list() - - var/state_zeroth = 0 - var/list/state_ion = list() - var/list/state_inherent = list() - var/list/state_supplied = list() - -/datum/ai_laws/New() - ..() - sort_laws() - -/* General ai_law functions */ -/datum/ai_laws/proc/all_laws() - sort_laws() - return sorted_laws - -/datum/ai_laws/proc/laws_to_state() - sort_laws() - var/list/statements = new() - for(var/datum/ai_law/law in sorted_laws) - if(get_state_law(law)) - statements += law - - return statements - -/datum/ai_laws/proc/sort_laws() - if(sorted_laws.len) - return - - for(var/ion_law in ion_laws) - sorted_laws += ion_law - - if(zeroth_law) - sorted_laws += zeroth_law - - var/index = 1 - for(var/datum/ai_law/inherent_law in inherent_laws) - inherent_law.index = index++ - if(supplied_laws.len < inherent_law.index || !istype(supplied_laws[inherent_law.index], /datum/ai_law)) - sorted_laws += inherent_law - - for(var/datum/ai_law/AL in supplied_laws) - if(istype(AL)) - sorted_laws += AL - -/datum/ai_laws/proc/sync(var/mob/living/silicon/S, var/full_sync = 1) - // Add directly to laws to avoid log-spam - S.sync_zeroth(zeroth_law, zeroth_law_borg) - - if(full_sync || ion_laws.len) - S.laws.clear_ion_laws() - if(full_sync || inherent_laws.len) - S.laws.clear_inherent_laws() - if(full_sync || supplied_laws.len) - S.laws.clear_supplied_laws() - - for (var/datum/ai_law/law in ion_laws) - S.laws.add_ion_law(law.law) - for (var/datum/ai_law/law in inherent_laws) - S.laws.add_inherent_law(law.law) - for (var/datum/ai_law/law in supplied_laws) - if(law) - S.laws.add_supplied_law(law.index, law.law) - - -/mob/living/silicon/proc/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) - if (!is_malf_or_traitor(src)) - if(zeroth_law_borg) - laws.set_zeroth_law(zeroth_law_borg.law) - else if(zeroth_law) - laws.set_zeroth_law(zeroth_law.law) - -/mob/living/silicon/ai/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) - if(zeroth_law) - laws.set_zeroth_law(zeroth_law.law, zeroth_law_borg ? zeroth_law_borg.law : null) - -/**************** -* Add Laws * -****************/ -/datum/ai_laws/proc/set_zeroth_law(var/law, var/law_borg = null) - if(!law) - return - - zeroth_law = new(law) - if(law_borg) //Making it possible for slaved borgs to see a different law 0 than their AI. --NEO - zeroth_law_borg = new(law_borg) - else - zeroth_law_borg = null - sorted_laws.Cut() - -/datum/ai_laws/proc/add_ion_law(var/law) - if(!law) - return - - for(var/datum/ai_law/AL in ion_laws) - if(AL.law == law) - return - - var/new_law = new/datum/ai_law/ion(law) - ion_laws += new_law - if(state_ion.len < ion_laws.len) - state_ion += 1 - - sorted_laws.Cut() - -/datum/ai_laws/proc/add_inherent_law(var/law) - if(!law) - return - - for(var/datum/ai_law/AL in inherent_laws) - if(AL.law == law) - return - - var/new_law = new/datum/ai_law/inherent(law) - inherent_laws += new_law - if(state_inherent.len < inherent_laws.len) - state_inherent += 1 - - sorted_laws.Cut() - -/datum/ai_laws/proc/add_supplied_law(var/number, var/law) - if(!law) - return - - if(supplied_laws.len >= number) - var/datum/ai_law/existing_law = supplied_laws[number] - if(existing_law && existing_law.law == law) - return - - if(supplied_laws.len >= number && supplied_laws[number]) - delete_law(supplied_laws[number]) - - while (src.supplied_laws.len < number) - src.supplied_laws += "" - if(state_supplied.len < supplied_laws.len) - state_supplied += 1 - - var/new_law = new/datum/ai_law/supplied(law, number) - supplied_laws[number] = new_law - if(state_supplied.len < supplied_laws.len) - state_supplied += 1 - - sorted_laws.Cut() - -/**************** -* Remove Laws * -*****************/ -/datum/ai_laws/proc/delete_law(var/datum/ai_law/law) - if(istype(law)) - law.delete_law(src) - -/datum/ai_law/proc/delete_law(var/datum/ai_laws/laws) - -/datum/ai_law/zero/delete_law(var/datum/ai_laws/laws) - laws.clear_zeroth_laws() - -/datum/ai_law/ion/delete_law(var/datum/ai_laws/laws) - laws.internal_delete_law(laws.ion_laws, laws.state_ion, src) - -/datum/ai_law/inherent/delete_law(var/datum/ai_laws/laws) - laws.internal_delete_law(laws.inherent_laws, laws.state_inherent, src) - -/datum/ai_law/supplied/delete_law(var/datum/ai_laws/laws) - var/index = laws.supplied_laws.Find(src) - if(index) - laws.supplied_laws[index] = "" - laws.state_supplied[index] = 1 - -/datum/ai_laws/proc/internal_delete_law(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) - var/index = laws.Find(law) - if(index) - laws -= law - for(index, index < state.len, index++) - state[index] = state[index+1] - sorted_laws.Cut() - -/**************** -* Clear Laws * -****************/ -/datum/ai_laws/proc/clear_zeroth_laws() - zeroth_law = null - zeroth_law_borg = null - -/datum/ai_laws/proc/clear_ion_laws() - ion_laws.Cut() - sorted_laws.Cut() - -/datum/ai_laws/proc/clear_inherent_laws() - inherent_laws.Cut() - sorted_laws.Cut() - -/datum/ai_laws/proc/clear_supplied_laws() - supplied_laws.Cut() - sorted_laws.Cut() - -/datum/ai_laws/proc/show_laws(var/who) - sort_laws() - for(var/datum/ai_law/law in sorted_laws) - if(law == zeroth_law_borg) - continue - if(law == zeroth_law) - to_chat(who, "[law.get_index()]. [law.law]") - else - to_chat(who, "[law.get_index()]. [law.law]") - -/******************** -* Stating Laws * -********************/ -/******** -* Get * -********/ -/datum/ai_laws/proc/get_state_law(var/datum/ai_law/law) - return law.get_state_law(src) - -/datum/ai_law/proc/get_state_law(var/datum/ai_laws/laws) - -/datum/ai_law/zero/get_state_law(var/datum/ai_laws/laws) - if(src == laws.zeroth_law) - return laws.state_zeroth - -/datum/ai_law/ion/get_state_law(var/datum/ai_laws/laws) - return laws.get_state_internal(laws.ion_laws, laws.state_ion, src) - -/datum/ai_law/inherent/get_state_law(var/datum/ai_laws/laws) - return laws.get_state_internal(laws.inherent_laws, laws.state_inherent, src) - -/datum/ai_law/supplied/get_state_law(var/datum/ai_laws/laws) - return laws.get_state_internal(laws.supplied_laws, laws.state_supplied, src) - -/datum/ai_laws/proc/get_state_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) - var/index = laws.Find(law) - if(index) - return state[index] - return 0 - -/******** -* Set * -********/ -/datum/ai_laws/proc/set_state_law(var/datum/ai_law/law, var/state) - law.set_state_law(src, state) - -/datum/ai_law/proc/set_state_law(var/datum/ai_law/law, var/state) - -/datum/ai_law/zero/set_state_law(var/datum/ai_laws/laws, var/state) - if(src == laws.zeroth_law) - laws.state_zeroth = state - -/datum/ai_law/ion/set_state_law(var/datum/ai_laws/laws, var/state) - laws.set_state_law_internal(laws.ion_laws, laws.state_ion, src, state) - -/datum/ai_law/inherent/set_state_law(var/datum/ai_laws/laws, var/state) - laws.set_state_law_internal(laws.inherent_laws, laws.state_inherent, src, state) - -/datum/ai_law/supplied/set_state_law(var/datum/ai_laws/laws, var/state) - laws.set_state_law_internal(laws.supplied_laws, laws.state_supplied, src, state) - -/datum/ai_laws/proc/set_state_law_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law, var/do_state) - var/index = laws.Find(law) - if(index) - state[index] = do_state +var/global/const/base_law_type = /datum/ai_laws/nanotrasen + +/datum/ai_law + var/law = "" + var/index = 0 + +/datum/ai_law/New(law, index) + src.law = law + src.index = index + +/datum/ai_law/proc/get_index() + return index + +/datum/ai_law/ion/get_index() + return ionnum() + +/datum/ai_law/zero/get_index() + return 0 + +/datum/ai_laws + var/name = "Unknown Laws" + var/law_header = "Prime Directives" + var/selectable = 0 + var/datum/ai_law/zero/zeroth_law = null + var/datum/ai_law/zero/zeroth_law_borg = null + var/list/datum/ai_law/inherent_laws = list() + var/list/datum/ai_law/supplied_laws = list() + var/list/datum/ai_law/ion/ion_laws = list() + var/list/datum/ai_law/sorted_laws = list() + + var/state_zeroth = 0 + var/list/state_ion = list() + var/list/state_inherent = list() + var/list/state_supplied = list() + +/datum/ai_laws/New() + ..() + sort_laws() + +/* General ai_law functions */ +/datum/ai_laws/proc/all_laws() + sort_laws() + return sorted_laws + +/datum/ai_laws/proc/laws_to_state() + sort_laws() + var/list/statements = new() + for(var/datum/ai_law/law in sorted_laws) + if(get_state_law(law)) + statements += law + + return statements + +/datum/ai_laws/proc/sort_laws() + if(sorted_laws.len) + return + + for(var/ion_law in ion_laws) + sorted_laws += ion_law + + if(zeroth_law) + sorted_laws += zeroth_law + + var/index = 1 + for(var/datum/ai_law/inherent_law in inherent_laws) + inherent_law.index = index++ + if(supplied_laws.len < inherent_law.index || !istype(supplied_laws[inherent_law.index], /datum/ai_law)) + sorted_laws += inherent_law + + for(var/datum/ai_law/AL in supplied_laws) + if(istype(AL)) + sorted_laws += AL + +/datum/ai_laws/proc/sync(var/mob/living/silicon/S, var/full_sync = 1) + // Add directly to laws to avoid log-spam + S.sync_zeroth(zeroth_law, zeroth_law_borg) + + if(full_sync || ion_laws.len) + S.laws.clear_ion_laws() + if(full_sync || inherent_laws.len) + S.laws.clear_inherent_laws() + if(full_sync || supplied_laws.len) + S.laws.clear_supplied_laws() + + for (var/datum/ai_law/law in ion_laws) + S.laws.add_ion_law(law.law) + for (var/datum/ai_law/law in inherent_laws) + S.laws.add_inherent_law(law.law) + for (var/datum/ai_law/law in supplied_laws) + if(law) + S.laws.add_supplied_law(law.index, law.law) + + +/mob/living/silicon/proc/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) + if (!is_malf_or_traitor(src)) + if(zeroth_law_borg) + laws.set_zeroth_law(zeroth_law_borg.law) + else if(zeroth_law) + laws.set_zeroth_law(zeroth_law.law) + +/mob/living/silicon/ai/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) + if(zeroth_law) + laws.set_zeroth_law(zeroth_law.law, zeroth_law_borg ? zeroth_law_borg.law : null) + +/**************** +* Add Laws * +****************/ +/datum/ai_laws/proc/set_zeroth_law(var/law, var/law_borg = null) + if(!law) + return + + zeroth_law = new(law) + if(law_borg) //Making it possible for slaved borgs to see a different law 0 than their AI. --NEO + zeroth_law_borg = new(law_borg) + else + zeroth_law_borg = null + sorted_laws.Cut() + +/datum/ai_laws/proc/add_ion_law(var/law) + if(!law) + return + + for(var/datum/ai_law/AL in ion_laws) + if(AL.law == law) + return + + var/new_law = new/datum/ai_law/ion(law) + ion_laws += new_law + if(state_ion.len < ion_laws.len) + state_ion += 1 + + sorted_laws.Cut() + +/datum/ai_laws/proc/add_inherent_law(var/law) + if(!law) + return + + for(var/datum/ai_law/AL in inherent_laws) + if(AL.law == law) + return + + var/new_law = new/datum/ai_law/inherent(law) + inherent_laws += new_law + if(state_inherent.len < inherent_laws.len) + state_inherent += 1 + + sorted_laws.Cut() + +/datum/ai_laws/proc/add_supplied_law(var/number, var/law) + if(!law) + return + + if(supplied_laws.len >= number) + var/datum/ai_law/existing_law = supplied_laws[number] + if(existing_law && existing_law.law == law) + return + + if(supplied_laws.len >= number && supplied_laws[number]) + delete_law(supplied_laws[number]) + + while (src.supplied_laws.len < number) + src.supplied_laws += "" + if(state_supplied.len < supplied_laws.len) + state_supplied += 1 + + var/new_law = new/datum/ai_law/supplied(law, number) + supplied_laws[number] = new_law + if(state_supplied.len < supplied_laws.len) + state_supplied += 1 + + sorted_laws.Cut() + +/**************** +* Remove Laws * +*****************/ +/datum/ai_laws/proc/delete_law(var/datum/ai_law/law) + if(istype(law)) + law.delete_law(src) + +/datum/ai_law/proc/delete_law(var/datum/ai_laws/laws) + +/datum/ai_law/zero/delete_law(var/datum/ai_laws/laws) + laws.clear_zeroth_laws() + +/datum/ai_law/ion/delete_law(var/datum/ai_laws/laws) + laws.internal_delete_law(laws.ion_laws, laws.state_ion, src) + +/datum/ai_law/inherent/delete_law(var/datum/ai_laws/laws) + laws.internal_delete_law(laws.inherent_laws, laws.state_inherent, src) + +/datum/ai_law/supplied/delete_law(var/datum/ai_laws/laws) + var/index = laws.supplied_laws.Find(src) + if(index) + laws.supplied_laws[index] = "" + laws.state_supplied[index] = 1 + +/datum/ai_laws/proc/internal_delete_law(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) + var/index = laws.Find(law) + if(index) + laws -= law + for(index, index < state.len, index++) + state[index] = state[index+1] + sorted_laws.Cut() + +/**************** +* Clear Laws * +****************/ +/datum/ai_laws/proc/clear_zeroth_laws() + zeroth_law = null + zeroth_law_borg = null + +/datum/ai_laws/proc/clear_ion_laws() + ion_laws.Cut() + sorted_laws.Cut() + +/datum/ai_laws/proc/clear_inherent_laws() + inherent_laws.Cut() + sorted_laws.Cut() + +/datum/ai_laws/proc/clear_supplied_laws() + supplied_laws.Cut() + sorted_laws.Cut() + +/datum/ai_laws/proc/show_laws(var/who) + sort_laws() + for(var/datum/ai_law/law in sorted_laws) + if(law == zeroth_law_borg) + continue + if(law == zeroth_law) + to_chat(who, "[law.get_index()]. [law.law]") + else + to_chat(who, "[law.get_index()]. [law.law]") + +/******************** +* Stating Laws * +********************/ +/******** +* Get * +********/ +/datum/ai_laws/proc/get_state_law(var/datum/ai_law/law) + return law.get_state_law(src) + +/datum/ai_law/proc/get_state_law(var/datum/ai_laws/laws) + +/datum/ai_law/zero/get_state_law(var/datum/ai_laws/laws) + if(src == laws.zeroth_law) + return laws.state_zeroth + +/datum/ai_law/ion/get_state_law(var/datum/ai_laws/laws) + return laws.get_state_internal(laws.ion_laws, laws.state_ion, src) + +/datum/ai_law/inherent/get_state_law(var/datum/ai_laws/laws) + return laws.get_state_internal(laws.inherent_laws, laws.state_inherent, src) + +/datum/ai_law/supplied/get_state_law(var/datum/ai_laws/laws) + return laws.get_state_internal(laws.supplied_laws, laws.state_supplied, src) + +/datum/ai_laws/proc/get_state_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) + var/index = laws.Find(law) + if(index) + return state[index] + return 0 + +/******** +* Set * +********/ +/datum/ai_laws/proc/set_state_law(var/datum/ai_law/law, var/state) + law.set_state_law(src, state) + +/datum/ai_law/proc/set_state_law(var/datum/ai_law/law, var/state) + +/datum/ai_law/zero/set_state_law(var/datum/ai_laws/laws, var/state) + if(src == laws.zeroth_law) + laws.state_zeroth = state + +/datum/ai_law/ion/set_state_law(var/datum/ai_laws/laws, var/state) + laws.set_state_law_internal(laws.ion_laws, laws.state_ion, src, state) + +/datum/ai_law/inherent/set_state_law(var/datum/ai_laws/laws, var/state) + laws.set_state_law_internal(laws.inherent_laws, laws.state_inherent, src, state) + +/datum/ai_law/supplied/set_state_law(var/datum/ai_laws/laws, var/state) + laws.set_state_law_internal(laws.supplied_laws, laws.state_supplied, src, state) + +/datum/ai_laws/proc/set_state_law_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law, var/do_state) + var/index = laws.Find(law) + if(index) + state[index] = do_state diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index 0d3b7340998..17d8fb17edc 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -1,550 +1,550 @@ - -/hook/startup/proc/createDatacore() - data_core = new /datum/datacore() - return 1 - -/datum/datacore - var/name = "datacore" - //For general station crew - var/static/list/medical = list() - var/static/list/general = list() - var/static/list/security = list() - //For offmap spawns so they can have records accessible by certain things - var/static/list/hidden_medical = list() - var/static/list/hidden_general = list() - var/static/list/hidden_security = list() - //This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character(). - var/static/list/locked = list() - - -/datum/datacore/proc/get_manifest(monochrome, OOC) - var/list/heads = new() - var/list/sec = new() - var/list/eng = new() - var/list/med = new() - var/list/sci = new() - var/list/car = new() - var/list/pla = new() //VOREStation Edit - var/list/civ = new() - var/list/bot = new() - var/list/off = new() - var/list/misc = new() - var/list/isactive = new() - var/dat = {" - -
    Event TypeStart TimeFinish Time
    [event.type][event.time_started][event.time_finished]
    - - "} - var/even = 0 - // sort mobs - for(var/datum/data/record/t in data_core.general) - var/name = t.fields["name"] - var/rank = t.fields["rank"] - var/real_rank = make_list_rank(t.fields["real_rank"]) - - if(OOC) - var/active = 0 - for(var/mob/M in player_list) - if(M.real_name == name && M.client && M.client.inactivity <= 10 MINUTES) - active = 1 - break - isactive[name] = active ? "Active" : "Inactive" - else - isactive[name] = t.fields["p_stat"] - //to_world("[name]: [rank]") - //cael - to prevent multiple appearances of a player/job combination, add a continue after each line - var/department = 0 - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_COMMAND)) - heads[name] = rank - department = 1 - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_SECURITY)) - sec[name] = rank - department = 1 - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_ENGINEERING)) - eng[name] = rank - department = 1 - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_MEDICAL)) - med[name] = rank - department = 1 - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_RESEARCH)) - sci[name] = rank - department = 1 - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CARGO)) - car[name] = rank - department = 1 - //VOREStation Add Begin - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_PLANET)) - pla[name] = rank - department = 1 - //VOREStation Add End - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CIVILIAN)) - civ[name] = rank - department = 1 - if(!department && !(name in heads)) - misc[name] = rank - - //For the offmap spawns - if(OOC) - for(var/datum/data/record/t in hidden_general) - var/name = t.fields["name"] - var/rank = t.fields["rank"] - var/real_rank = make_list_rank(t.fields["real_rank"]) - - var/active = 0 - for(var/mob/M in player_list) - if(M.real_name == name && M.client && M.client.inactivity <= 10 MINUTES) - active = 1 - break - isactive[name] = active ? "Active" : "Inactive" - - var/datum/job/J = SSjob.get_job(real_rank) - if(J?.offmap_spawn) - off[name] = rank - - // Synthetics don't have actual records, so we will pull them from here. - for(var/mob/living/silicon/ai/ai in mob_list) - bot[ai.name] = "Artificial Intelligence" - - for(var/mob/living/silicon/robot/robot in mob_list) - // No combat/syndicate cyborgs, no drones, and no AI shells. - if(!robot.scrambledcodes && !robot.shell && !(robot.module && robot.module.hide_on_manifest())) - bot[robot.name] = "[robot.modtype] [robot.braintype]" - - - if(heads.len > 0) - dat += "" - for(name in heads) - dat += "" - even = !even - if(sec.len > 0) - dat += "" - for(name in sec) - dat += "" - even = !even - if(eng.len > 0) - dat += "" - for(name in eng) - dat += "" - even = !even - if(med.len > 0) - dat += "" - for(name in med) - dat += "" - even = !even - if(sci.len > 0) - dat += "" - for(name in sci) - dat += "" - even = !even - if(car.len > 0) - dat += "" - for(name in car) - dat += "" - even = !even - //VOREStation Edit Begin - if(pla.len > 0) - dat += "" - for(name in pla) - dat += "" - even = !even - //VOREStation Edit End - if(civ.len > 0) - dat += "" - for(name in civ) - dat += "" - even = !even - // in case somebody is insane and added them to the manifest, why not - if(bot.len > 0) - dat += "" - for(name in bot) - dat += "" - even = !even - // offmap spawners - if(off.len > 0) - dat += "" - for(name in off) - dat += "" - even = !even - // misc guys - if(misc.len > 0) - dat += "" - for(name in misc) - dat += "" - even = !even - - dat += "
    NameRankActivity
    Heads
    [name][heads[name]][isactive[name]]
    Security
    [name][sec[name]][isactive[name]]
    Engineering
    [name][eng[name]][isactive[name]]
    Medical
    [name][med[name]][isactive[name]]
    Science
    [name][sci[name]][isactive[name]]
    Cargo
    [name][car[name]][isactive[name]]
    Exploration
    [name][pla[name]][isactive[name]]
    Civilian
    [name][civ[name]][isactive[name]]
    Silicon
    [name][bot[name]][isactive[name]]
    Offmap Spawns
    [name][off[name]][isactive[name]]
    Miscellaneous
    [name][misc[name]][isactive[name]]
    " - dat = replacetext(dat, "\n", "") // so it can be placed on paper correctly - dat = replacetext(dat, "\t", "") - return dat - -/* -We can't just insert in HTML into the nanoUI so we need the raw data to play with. -Instead of creating this list over and over when someone leaves their PDA open to the page -we'll only update it when it changes. The PDA_Manifest global list is zeroed out upon any change -using /datum/datacore/proc/manifest_inject( ), or manifest_insert( ) -*/ - -var/global/list/PDA_Manifest = list() - -/datum/datacore/proc/get_manifest_list() - if(PDA_Manifest.len) - return - var/list/heads = list() - var/list/sec = list() - var/list/eng = list() - var/list/med = list() - var/list/sci = list() - var/list/car = list() - var/list/pla = list() // Planetside crew: Explorers, Pilots, S&S - var/list/civ = list() - var/list/bot = list() - var/list/misc = list() - for(var/datum/data/record/t in data_core.general) - var/name = sanitize(t.fields["name"]) - var/rank = sanitize(t.fields["rank"]) - var/real_rank = make_list_rank(t.fields["real_rank"]) - - var/isactive = t.fields["p_stat"] - var/department = 0 - var/depthead = 0 // Department Heads will be placed at the top of their lists. - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_COMMAND)) - heads[++heads.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - depthead = 1 - if(rank=="Site Manager" && heads.len != 1) - heads.Swap(1,heads.len) - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_SECURITY)) - sec[++sec.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && sec.len != 1) - sec.Swap(1,sec.len) - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_ENGINEERING)) - eng[++eng.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && eng.len != 1) - eng.Swap(1,eng.len) - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_MEDICAL)) - med[++med.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && med.len != 1) - med.Swap(1,med.len) - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_RESEARCH)) - sci[++sci.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && sci.len != 1) - sci.Swap(1,sci.len) - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_PLANET)) - pla[++pla.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CARGO)) - car[++car.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && car.len != 1) - car.Swap(1,car.len) - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CIVILIAN)) - civ[++civ.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && civ.len != 1) - civ.Swap(1,civ.len) - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_SYNTHETIC)) - bot[++bot.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - - if(!department && !(name in heads)) - misc[++misc.len] = list("name" = name, "rank" = rank, "active" = isactive) - - // Synthetics don't have actual records, so we will pull them from here. - // Synths don't have records, which is the means by which isactive is retrieved, so I'm hardcoding it to active, don't really have any better means - for(var/mob/living/silicon/ai/ai in mob_list) - bot[++bot.len] = list("name" = ai.real_name, "rank" = "Artificial Intelligence", "active" = "Active") - - for(var/mob/living/silicon/robot/robot in mob_list) - // No combat/syndicate cyborgs, no drones, and no AI shells. - if(robot.scrambledcodes || robot.shell || (robot.module && robot.module.hide_on_manifest())) - continue - - bot[++bot.len] = list("name" = robot.real_name, "rank" = "[robot.modtype] [robot.braintype]", "active" = "Active") - - - PDA_Manifest = list( - list("cat" = "Command", "elems" = heads), - list("cat" = "Security", "elems" = sec), - list("cat" = "Engineering", "elems" = eng), - list("cat" = "Medical", "elems" = med), - list("cat" = "Science", "elems" = sci), - list("cat" = "Cargo", "elems" = car), - list("cat" = "Exploration", "elems" = pla), // VOREStation Edit - list("cat" = "Civilian", "elems" = civ), - list("cat" = "Silicon", "elems" = bot), - list("cat" = "Miscellaneous", "elems" = misc) - ) - return - -/datum/datacore/proc/manifest() - spawn() - for(var/mob/living/carbon/human/H in player_list) - manifest_inject(H) - return - -/datum/datacore/proc/manifest_modify(var/name, var/assignment, var/rank) - ResetPDAManifest() - var/datum/data/record/foundrecord - var/real_title = assignment - - for(var/datum/data/record/t in data_core.general) - if (t) - if(t.fields["name"] == name) - foundrecord = t - break - - var/list/all_jobs = get_job_datums() - - for(var/datum/job/J in all_jobs) - if(J.title == rank) //If we have a rank, just default to using that. - real_title = rank - break - else if(J.title == assignment) - real_title = assignment - break - else - var/list/alttitles = get_alternate_titles(J.title) - if(assignment in alttitles) - real_title = J.title - break - - if(foundrecord) - foundrecord.fields["rank"] = assignment - foundrecord.fields["real_rank"] = real_title - -/datum/datacore/proc/manifest_inject(var/mob/living/carbon/human/H) - if(H.mind && !player_is_antag(H.mind, only_offstation_roles = 1)) - var/assignment = GetAssignment(H) - var/hidden - var/datum/job/J = SSjob.get_job(H.mind.assigned_role) - hidden = J?.offmap_spawn - - H.ImmediateOverlayUpdate() - - var/id = generate_record_id() - //General Record - var/datum/data/record/G = CreateGeneralRecord(H, id, hidden) - G.fields["name"] = H.real_name - G.fields["real_rank"] = H.mind.assigned_role - G.fields["rank"] = assignment - G.fields["age"] = H.age - G.fields["languages"] = list2text(H.languages,", ") - if(H.get_FBP_type()) - G.fields["brain_type"] = H.get_FBP_type() - else - G.fields["brain_type"] = "Organic" - G.fields["fingerprint"] = md5(H.dna.uni_identity) - G.fields["p_stat"] = "Active" - G.fields["m_stat"] = "Stable" - G.fields["sex"] = gender2text(H.gender) - G.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit - G.fields["home_system"] = H.home_system - G.fields["birthplace"] = H.birthplace - G.fields["citizenship"] = H.citizenship - G.fields["faction"] = H.personal_faction - G.fields["religion"] = H.religion - if(H.gen_record && !jobban_isbanned(H, "Records")) - G.fields["notes"] = H.gen_record - - //Medical Record - var/datum/data/record/M = CreateMedicalRecord(H.real_name, id, hidden) - M.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit - M.fields["b_type"] = H.b_type - M.fields["blood_reagent"] = H.species.blood_reagents - M.fields["b_dna"] = H.dna.unique_enzymes - M.fields["id_gender"] = gender2text(H.identifying_gender) - if(H.get_FBP_type()) - M.fields["brain_type"] = H.get_FBP_type() - else - M.fields["brain_type"] = "Organic" - if(H.med_record && !jobban_isbanned(H, "Records")) - M.fields["notes"] = H.med_record - - //Security Record - var/datum/data/record/S = CreateSecurityRecord(H.real_name, id, hidden) - S.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit - if(H.get_FBP_type()) - S.fields["brain_type"] = H.get_FBP_type() - else - S.fields["brain_type"] = "Organic" - if(H.sec_record && !jobban_isbanned(H, "Records")) - S.fields["notes"] = H.sec_record - - //Locked Record - var/datum/data/record/L = new() - L.fields["id"] = md5("[H.real_name][H.mind.assigned_role]") - L.fields["name"] = H.real_name - L.fields["rank"] = H.mind.assigned_role - L.fields["age"] = H.age - L.fields["languages"] = list2text(H.languages,", ") - L.fields["fingerprint"] = md5(H.dna.uni_identity) - L.fields["sex"] = gender2text(H.gender) - L.fields["id_gender"] = gender2text(H.identifying_gender) - if(H.get_FBP_type()) - L.fields["brain_type"] = H.get_FBP_type() - else - L.fields["brain_type"] = "Organic" - L.fields["b_type"] = H.b_type - L.fields["b_dna"] = H.dna.unique_enzymes - L.fields["enzymes"] = H.dna.SE // Used in respawning - L.fields["identity"] = H.dna.UI // " - L.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit - L.fields["home_system"] = H.home_system - L.fields["birthplace"] = H.birthplace - L.fields["citizenship"] = H.citizenship - L.fields["faction"] = H.personal_faction - L.fields["religion"] = H.religion - L.fields["image"] = icon(cached_character_icon(H), dir = SOUTH) - L.fields["antagfac"] = H.antag_faction - L.fields["antagvis"] = H.antag_vis - L.fields["offmap"] = hidden - if(H.exploit_record && !jobban_isbanned(H, "Records")) - L.fields["exploit_record"] = H.exploit_record - else - L.fields["exploit_record"] = "No additional information acquired." - locked += L - return - -/proc/generate_record_id() - return add_zero(num2hex(rand(1, 65535)), 4) //no point generating higher numbers because of the limitations of num2hex - -/datum/datacore/proc/CreateGeneralRecord(var/mob/living/carbon/human/H, var/id, var/hidden) - ResetPDAManifest() - var/icon/front - var/icon/side - if(H) - var/icon/charicon = cached_character_icon(H) - front = icon(charicon, dir = SOUTH, frame = 1) - side = icon(charicon, dir = WEST, frame = 1) - else // Sending null things through browse_rsc() makes a runtime and breaks the console trying to view the record. - front = icon('html/images/no_image32.png') - side = icon('html/images/no_image32.png') - - if(!id) - id = text("[]", add_zero(num2hex(rand(1, 65536)), 4)) - var/datum/data/record/G = new /datum/data/record() - G.name = "Employee Record #[id]" - G.fields["name"] = "New Record" - G.fields["id"] = id - G.fields["rank"] = "Unassigned" - G.fields["real_rank"] = "Unassigned" - G.fields["sex"] = "Unknown" - G.fields["age"] = "Unknown" - G.fields["languages"] = "Unknown" - G.fields["brain_type"] = "Unknown" - G.fields["fingerprint"] = "Unknown" - G.fields["p_stat"] = "Active" - G.fields["m_stat"] = "Stable" - G.fields["species"] = SPECIES_HUMAN - G.fields["home_system"] = "Unknown" - G.fields["birthplace"] = "Unknown" - G.fields["citizenship"] = "Unknown" - G.fields["faction"] = "Unknown" - G.fields["religion"] = "Unknown" - G.fields["photo_front"] = front - G.fields["photo_side"] = side - G.fields["photo-south"] = "'data:image/png;base64,[icon2base64(front)]'" - G.fields["photo-west"] = "'data:image/png;base64,[icon2base64(side)]'" - G.fields["notes"] = "No notes found." - if(hidden) - hidden_general += G - else - general += G - - return G - -/datum/datacore/proc/CreateSecurityRecord(var/name, var/id, var/hidden) - ResetPDAManifest() - var/datum/data/record/R = new /datum/data/record() - R.name = "Security Record #[id]" - R.fields["name"] = name - R.fields["species"] = SPECIES_HUMAN - R.fields["id"] = id - R.fields["brain_type"] = "Unknown" - R.fields["criminal"] = "None" - R.fields["mi_crim"] = "None" - R.fields["mi_crim_d"] = "No minor crime convictions." - R.fields["ma_crim"] = "None" - R.fields["ma_crim_d"] = "No major crime convictions." - R.fields["notes"] = "No notes." - R.fields["notes"] = "No notes." - if(hidden) - hidden_security += R - else - security += R - - return R - -/datum/datacore/proc/CreateMedicalRecord(var/name, var/id, var/hidden) - ResetPDAManifest() - var/datum/data/record/M = new() - M.name = "Medical Record #[id]" - M.fields["id"] = id - M.fields["name"] = name - M.fields["species"] = SPECIES_HUMAN - M.fields["b_type"] = "AB+" - M.fields["b_dna"] = md5(name) - M.fields["id_gender"] = "Unknown" - M.fields["brain_type"] = "Unknown" - M.fields["mi_dis"] = "None" - M.fields["mi_dis_d"] = "No minor disabilities have been declared." - M.fields["ma_dis"] = "None" - M.fields["ma_dis_d"] = "No major disabilities have been diagnosed." - M.fields["alg"] = "None" - M.fields["alg_d"] = "No allergies have been detected in this patient." - M.fields["cdi"] = "None" - M.fields["cdi_d"] = "No diseases have been diagnosed at the moment." - M.fields["notes"] = "No notes found." - if(hidden) - hidden_medical += M - else - medical += M - - return M - -/datum/datacore/proc/ResetPDAManifest() - if(PDA_Manifest.len) - PDA_Manifest.Cut() - -/proc/find_general_record(field, value) - return find_record(field, value, data_core.general) - -/proc/find_medical_record(field, value) - return find_record(field, value, data_core.medical) - -/proc/find_security_record(field, value) - return find_record(field, value, data_core.security) - -/proc/find_record(field, value, list/L) - for(var/datum/data/record/R in L) - if(R.fields[field] == value) - return R - -/proc/GetAssignment(var/mob/living/carbon/human/H) - if(H.mind.role_alt_title) - return H.mind.role_alt_title - else if(H.mind.assigned_role) - return H.mind.assigned_role - else if(H.job) - return H.job - else - return "Unassigned" + +/hook/startup/proc/createDatacore() + data_core = new /datum/datacore() + return 1 + +/datum/datacore + var/name = "datacore" + //For general station crew + var/static/list/medical = list() + var/static/list/general = list() + var/static/list/security = list() + //For offmap spawns so they can have records accessible by certain things + var/static/list/hidden_medical = list() + var/static/list/hidden_general = list() + var/static/list/hidden_security = list() + //This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character(). + var/static/list/locked = list() + + +/datum/datacore/proc/get_manifest(monochrome, OOC) + var/list/heads = new() + var/list/sec = new() + var/list/eng = new() + var/list/med = new() + var/list/sci = new() + var/list/car = new() + var/list/pla = new() //VOREStation Edit + var/list/civ = new() + var/list/bot = new() + var/list/off = new() + var/list/misc = new() + var/list/isactive = new() + var/dat = {" + + + + "} + var/even = 0 + // sort mobs + for(var/datum/data/record/t in data_core.general) + var/name = t.fields["name"] + var/rank = t.fields["rank"] + var/real_rank = make_list_rank(t.fields["real_rank"]) + + if(OOC) + var/active = 0 + for(var/mob/M in player_list) + if(M.real_name == name && M.client && M.client.inactivity <= 10 MINUTES) + active = 1 + break + isactive[name] = active ? "Active" : "Inactive" + else + isactive[name] = t.fields["p_stat"] + //to_world("[name]: [rank]") + //cael - to prevent multiple appearances of a player/job combination, add a continue after each line + var/department = 0 + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_COMMAND)) + heads[name] = rank + department = 1 + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_SECURITY)) + sec[name] = rank + department = 1 + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_ENGINEERING)) + eng[name] = rank + department = 1 + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_MEDICAL)) + med[name] = rank + department = 1 + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_RESEARCH)) + sci[name] = rank + department = 1 + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CARGO)) + car[name] = rank + department = 1 + //VOREStation Add Begin + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_PLANET)) + pla[name] = rank + department = 1 + //VOREStation Add End + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CIVILIAN)) + civ[name] = rank + department = 1 + if(!department && !(name in heads)) + misc[name] = rank + + //For the offmap spawns + if(OOC) + for(var/datum/data/record/t in hidden_general) + var/name = t.fields["name"] + var/rank = t.fields["rank"] + var/real_rank = make_list_rank(t.fields["real_rank"]) + + var/active = 0 + for(var/mob/M in player_list) + if(M.real_name == name && M.client && M.client.inactivity <= 10 MINUTES) + active = 1 + break + isactive[name] = active ? "Active" : "Inactive" + + var/datum/job/J = SSjob.get_job(real_rank) + if(J?.offmap_spawn) + off[name] = rank + + // Synthetics don't have actual records, so we will pull them from here. + for(var/mob/living/silicon/ai/ai in mob_list) + bot[ai.name] = "Artificial Intelligence" + + for(var/mob/living/silicon/robot/robot in mob_list) + // No combat/syndicate cyborgs, no drones, and no AI shells. + if(!robot.scrambledcodes && !robot.shell && !(robot.module && robot.module.hide_on_manifest())) + bot[robot.name] = "[robot.modtype] [robot.braintype]" + + + if(heads.len > 0) + dat += "" + for(name in heads) + dat += "" + even = !even + if(sec.len > 0) + dat += "" + for(name in sec) + dat += "" + even = !even + if(eng.len > 0) + dat += "" + for(name in eng) + dat += "" + even = !even + if(med.len > 0) + dat += "" + for(name in med) + dat += "" + even = !even + if(sci.len > 0) + dat += "" + for(name in sci) + dat += "" + even = !even + if(car.len > 0) + dat += "" + for(name in car) + dat += "" + even = !even + //VOREStation Edit Begin + if(pla.len > 0) + dat += "" + for(name in pla) + dat += "" + even = !even + //VOREStation Edit End + if(civ.len > 0) + dat += "" + for(name in civ) + dat += "" + even = !even + // in case somebody is insane and added them to the manifest, why not + if(bot.len > 0) + dat += "" + for(name in bot) + dat += "" + even = !even + // offmap spawners + if(off.len > 0) + dat += "" + for(name in off) + dat += "" + even = !even + // misc guys + if(misc.len > 0) + dat += "" + for(name in misc) + dat += "" + even = !even + + dat += "
    NameRankActivity
    Heads
    [name][heads[name]][isactive[name]]
    Security
    [name][sec[name]][isactive[name]]
    Engineering
    [name][eng[name]][isactive[name]]
    Medical
    [name][med[name]][isactive[name]]
    Science
    [name][sci[name]][isactive[name]]
    Cargo
    [name][car[name]][isactive[name]]
    Exploration
    [name][pla[name]][isactive[name]]
    Civilian
    [name][civ[name]][isactive[name]]
    Silicon
    [name][bot[name]][isactive[name]]
    Offmap Spawns
    [name][off[name]][isactive[name]]
    Miscellaneous
    [name][misc[name]][isactive[name]]
    " + dat = replacetext(dat, "\n", "") // so it can be placed on paper correctly + dat = replacetext(dat, "\t", "") + return dat + +/* +We can't just insert in HTML into the nanoUI so we need the raw data to play with. +Instead of creating this list over and over when someone leaves their PDA open to the page +we'll only update it when it changes. The PDA_Manifest global list is zeroed out upon any change +using /datum/datacore/proc/manifest_inject( ), or manifest_insert( ) +*/ + +var/global/list/PDA_Manifest = list() + +/datum/datacore/proc/get_manifest_list() + if(PDA_Manifest.len) + return + var/list/heads = list() + var/list/sec = list() + var/list/eng = list() + var/list/med = list() + var/list/sci = list() + var/list/car = list() + var/list/pla = list() // Planetside crew: Explorers, Pilots, S&S + var/list/civ = list() + var/list/bot = list() + var/list/misc = list() + for(var/datum/data/record/t in data_core.general) + var/name = sanitize(t.fields["name"]) + var/rank = sanitize(t.fields["rank"]) + var/real_rank = make_list_rank(t.fields["real_rank"]) + + var/isactive = t.fields["p_stat"] + var/department = 0 + var/depthead = 0 // Department Heads will be placed at the top of their lists. + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_COMMAND)) + heads[++heads.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + depthead = 1 + if(rank=="Site Manager" && heads.len != 1) + heads.Swap(1,heads.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_SECURITY)) + sec[++sec.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && sec.len != 1) + sec.Swap(1,sec.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_ENGINEERING)) + eng[++eng.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && eng.len != 1) + eng.Swap(1,eng.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_MEDICAL)) + med[++med.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && med.len != 1) + med.Swap(1,med.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_RESEARCH)) + sci[++sci.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && sci.len != 1) + sci.Swap(1,sci.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_PLANET)) + pla[++pla.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CARGO)) + car[++car.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && car.len != 1) + car.Swap(1,car.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CIVILIAN)) + civ[++civ.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && civ.len != 1) + civ.Swap(1,civ.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_SYNTHETIC)) + bot[++bot.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + + if(!department && !(name in heads)) + misc[++misc.len] = list("name" = name, "rank" = rank, "active" = isactive) + + // Synthetics don't have actual records, so we will pull them from here. + // Synths don't have records, which is the means by which isactive is retrieved, so I'm hardcoding it to active, don't really have any better means + for(var/mob/living/silicon/ai/ai in mob_list) + bot[++bot.len] = list("name" = ai.real_name, "rank" = "Artificial Intelligence", "active" = "Active") + + for(var/mob/living/silicon/robot/robot in mob_list) + // No combat/syndicate cyborgs, no drones, and no AI shells. + if(robot.scrambledcodes || robot.shell || (robot.module && robot.module.hide_on_manifest())) + continue + + bot[++bot.len] = list("name" = robot.real_name, "rank" = "[robot.modtype] [robot.braintype]", "active" = "Active") + + + PDA_Manifest = list( + list("cat" = "Command", "elems" = heads), + list("cat" = "Security", "elems" = sec), + list("cat" = "Engineering", "elems" = eng), + list("cat" = "Medical", "elems" = med), + list("cat" = "Science", "elems" = sci), + list("cat" = "Cargo", "elems" = car), + list("cat" = "Exploration", "elems" = pla), // VOREStation Edit + list("cat" = "Civilian", "elems" = civ), + list("cat" = "Silicon", "elems" = bot), + list("cat" = "Miscellaneous", "elems" = misc) + ) + return + +/datum/datacore/proc/manifest() + spawn() + for(var/mob/living/carbon/human/H in player_list) + manifest_inject(H) + return + +/datum/datacore/proc/manifest_modify(var/name, var/assignment, var/rank) + ResetPDAManifest() + var/datum/data/record/foundrecord + var/real_title = assignment + + for(var/datum/data/record/t in data_core.general) + if (t) + if(t.fields["name"] == name) + foundrecord = t + break + + var/list/all_jobs = get_job_datums() + + for(var/datum/job/J in all_jobs) + if(J.title == rank) //If we have a rank, just default to using that. + real_title = rank + break + else if(J.title == assignment) + real_title = assignment + break + else + var/list/alttitles = get_alternate_titles(J.title) + if(assignment in alttitles) + real_title = J.title + break + + if(foundrecord) + foundrecord.fields["rank"] = assignment + foundrecord.fields["real_rank"] = real_title + +/datum/datacore/proc/manifest_inject(var/mob/living/carbon/human/H) + if(H.mind && !player_is_antag(H.mind, only_offstation_roles = 1)) + var/assignment = GetAssignment(H) + var/hidden + var/datum/job/J = SSjob.get_job(H.mind.assigned_role) + hidden = J?.offmap_spawn + + H.ImmediateOverlayUpdate() + + var/id = generate_record_id() + //General Record + var/datum/data/record/G = CreateGeneralRecord(H, id, hidden) + G.fields["name"] = H.real_name + G.fields["real_rank"] = H.mind.assigned_role + G.fields["rank"] = assignment + G.fields["age"] = H.age + G.fields["languages"] = list2text(H.languages,", ") + if(H.get_FBP_type()) + G.fields["brain_type"] = H.get_FBP_type() + else + G.fields["brain_type"] = "Organic" + G.fields["fingerprint"] = md5(H.dna.uni_identity) + G.fields["p_stat"] = "Active" + G.fields["m_stat"] = "Stable" + G.fields["sex"] = gender2text(H.gender) + G.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit + G.fields["home_system"] = H.home_system + G.fields["birthplace"] = H.birthplace + G.fields["citizenship"] = H.citizenship + G.fields["faction"] = H.personal_faction + G.fields["religion"] = H.religion + if(H.gen_record && !jobban_isbanned(H, "Records")) + G.fields["notes"] = H.gen_record + + //Medical Record + var/datum/data/record/M = CreateMedicalRecord(H.real_name, id, hidden) + M.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit + M.fields["b_type"] = H.b_type + M.fields["blood_reagent"] = H.species.blood_reagents + M.fields["b_dna"] = H.dna.unique_enzymes + M.fields["id_gender"] = gender2text(H.identifying_gender) + if(H.get_FBP_type()) + M.fields["brain_type"] = H.get_FBP_type() + else + M.fields["brain_type"] = "Organic" + if(H.med_record && !jobban_isbanned(H, "Records")) + M.fields["notes"] = H.med_record + + //Security Record + var/datum/data/record/S = CreateSecurityRecord(H.real_name, id, hidden) + S.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit + if(H.get_FBP_type()) + S.fields["brain_type"] = H.get_FBP_type() + else + S.fields["brain_type"] = "Organic" + if(H.sec_record && !jobban_isbanned(H, "Records")) + S.fields["notes"] = H.sec_record + + //Locked Record + var/datum/data/record/L = new() + L.fields["id"] = md5("[H.real_name][H.mind.assigned_role]") + L.fields["name"] = H.real_name + L.fields["rank"] = H.mind.assigned_role + L.fields["age"] = H.age + L.fields["languages"] = list2text(H.languages,", ") + L.fields["fingerprint"] = md5(H.dna.uni_identity) + L.fields["sex"] = gender2text(H.gender) + L.fields["id_gender"] = gender2text(H.identifying_gender) + if(H.get_FBP_type()) + L.fields["brain_type"] = H.get_FBP_type() + else + L.fields["brain_type"] = "Organic" + L.fields["b_type"] = H.b_type + L.fields["b_dna"] = H.dna.unique_enzymes + L.fields["enzymes"] = H.dna.SE // Used in respawning + L.fields["identity"] = H.dna.UI // " + L.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit + L.fields["home_system"] = H.home_system + L.fields["birthplace"] = H.birthplace + L.fields["citizenship"] = H.citizenship + L.fields["faction"] = H.personal_faction + L.fields["religion"] = H.religion + L.fields["image"] = icon(cached_character_icon(H), dir = SOUTH) + L.fields["antagfac"] = H.antag_faction + L.fields["antagvis"] = H.antag_vis + L.fields["offmap"] = hidden + if(H.exploit_record && !jobban_isbanned(H, "Records")) + L.fields["exploit_record"] = H.exploit_record + else + L.fields["exploit_record"] = "No additional information acquired." + locked += L + return + +/proc/generate_record_id() + return add_zero(num2hex(rand(1, 65535)), 4) //no point generating higher numbers because of the limitations of num2hex + +/datum/datacore/proc/CreateGeneralRecord(var/mob/living/carbon/human/H, var/id, var/hidden) + ResetPDAManifest() + var/icon/front + var/icon/side + if(H) + var/icon/charicon = cached_character_icon(H) + front = icon(charicon, dir = SOUTH, frame = 1) + side = icon(charicon, dir = WEST, frame = 1) + else // Sending null things through browse_rsc() makes a runtime and breaks the console trying to view the record. + front = icon('html/images/no_image32.png') + side = icon('html/images/no_image32.png') + + if(!id) + id = text("[]", add_zero(num2hex(rand(1, 65536)), 4)) + var/datum/data/record/G = new /datum/data/record() + G.name = "Employee Record #[id]" + G.fields["name"] = "New Record" + G.fields["id"] = id + G.fields["rank"] = "Unassigned" + G.fields["real_rank"] = "Unassigned" + G.fields["sex"] = "Unknown" + G.fields["age"] = "Unknown" + G.fields["languages"] = "Unknown" + G.fields["brain_type"] = "Unknown" + G.fields["fingerprint"] = "Unknown" + G.fields["p_stat"] = "Active" + G.fields["m_stat"] = "Stable" + G.fields["species"] = SPECIES_HUMAN + G.fields["home_system"] = "Unknown" + G.fields["birthplace"] = "Unknown" + G.fields["citizenship"] = "Unknown" + G.fields["faction"] = "Unknown" + G.fields["religion"] = "Unknown" + G.fields["photo_front"] = front + G.fields["photo_side"] = side + G.fields["photo-south"] = "'data:image/png;base64,[icon2base64(front)]'" + G.fields["photo-west"] = "'data:image/png;base64,[icon2base64(side)]'" + G.fields["notes"] = "No notes found." + if(hidden) + hidden_general += G + else + general += G + + return G + +/datum/datacore/proc/CreateSecurityRecord(var/name, var/id, var/hidden) + ResetPDAManifest() + var/datum/data/record/R = new /datum/data/record() + R.name = "Security Record #[id]" + R.fields["name"] = name + R.fields["species"] = SPECIES_HUMAN + R.fields["id"] = id + R.fields["brain_type"] = "Unknown" + R.fields["criminal"] = "None" + R.fields["mi_crim"] = "None" + R.fields["mi_crim_d"] = "No minor crime convictions." + R.fields["ma_crim"] = "None" + R.fields["ma_crim_d"] = "No major crime convictions." + R.fields["notes"] = "No notes." + R.fields["notes"] = "No notes." + if(hidden) + hidden_security += R + else + security += R + + return R + +/datum/datacore/proc/CreateMedicalRecord(var/name, var/id, var/hidden) + ResetPDAManifest() + var/datum/data/record/M = new() + M.name = "Medical Record #[id]" + M.fields["id"] = id + M.fields["name"] = name + M.fields["species"] = SPECIES_HUMAN + M.fields["b_type"] = "AB+" + M.fields["b_dna"] = md5(name) + M.fields["id_gender"] = "Unknown" + M.fields["brain_type"] = "Unknown" + M.fields["mi_dis"] = "None" + M.fields["mi_dis_d"] = "No minor disabilities have been declared." + M.fields["ma_dis"] = "None" + M.fields["ma_dis_d"] = "No major disabilities have been diagnosed." + M.fields["alg"] = "None" + M.fields["alg_d"] = "No allergies have been detected in this patient." + M.fields["cdi"] = "None" + M.fields["cdi_d"] = "No diseases have been diagnosed at the moment." + M.fields["notes"] = "No notes found." + if(hidden) + hidden_medical += M + else + medical += M + + return M + +/datum/datacore/proc/ResetPDAManifest() + if(PDA_Manifest.len) + PDA_Manifest.Cut() + +/proc/find_general_record(field, value) + return find_record(field, value, data_core.general) + +/proc/find_medical_record(field, value) + return find_record(field, value, data_core.medical) + +/proc/find_security_record(field, value) + return find_record(field, value, data_core.security) + +/proc/find_record(field, value, list/L) + for(var/datum/data/record/R in L) + if(R.fields[field] == value) + return R + +/proc/GetAssignment(var/mob/living/carbon/human/H) + if(H.mind.role_alt_title) + return H.mind.role_alt_title + else if(H.mind.assigned_role) + return H.mind.assigned_role + else if(H.job) + return H.job + else + return "Unassigned" diff --git a/code/datums/datum.dm b/code/datums/datum.dm index e44fb38f6b1..af085fbc145 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -62,6 +62,10 @@ #endif #endif + // If we have called dump_harddel_info already. Used to avoid duped calls (since we call it immediately in some cases on failure to process) + // Create and destroy is weird and I wanna cover my bases + var/harddel_deets_dumped = FALSE + // Default implementation of clean-up code. // This should be overridden to remove all references pointing to the object being destroyed. // Return the appropriate QDEL_HINT; in most cases this is QDEL_HINT_QUEUE. @@ -140,3 +144,16 @@ return SEND_SIGNAL(source, COMSIG_CD_RESET(index), S_TIMER_COOLDOWN_TIMELEFT(source, index)) TIMER_COOLDOWN_END(source, index) + +/// Return text from this proc to provide extra context to hard deletes that happen to it +/// Optional, you should use this for cases where replication is difficult and extra context is required +/// Can be called more then once per object, use harddel_deets_dumped to avoid duplicate calls (I am so sorry) +/datum/proc/dump_harddel_info() + return + +///images are pretty generic, this should help a bit with tracking harddels related to them +/image/dump_harddel_info() + if(harddel_deets_dumped) + return + harddel_deets_dumped = TRUE + return "Image icon: [icon] - icon_state: [icon_state] [loc ? "loc: [loc] ([loc.x],[loc.y],[loc.z])" : ""]" diff --git a/code/datums/game_masters/_common.dm b/code/datums/game_masters/_common.dm index 84efe57c73d..142287f510c 100644 --- a/code/datums/game_masters/_common.dm +++ b/code/datums/game_masters/_common.dm @@ -1,6 +1,6 @@ -// Push button, receive event. -// Returns a selected event datum. -/datum/game_master/proc/choose_event() - -/datum/game_master/proc/log_game_master(message) +// Push button, receive event. +// Returns a selected event datum. +/datum/game_master/proc/choose_event() + +/datum/game_master/proc/log_game_master(message) // SSgame_master.log_game_master(message) // VOREStation Edit - We don't use SSgame_master yet. \ No newline at end of file diff --git a/code/datums/game_masters/default.dm b/code/datums/game_masters/default.dm index 8f69b296afd..46d39ab44ec 100644 --- a/code/datums/game_masters/default.dm +++ b/code/datums/game_masters/default.dm @@ -1,87 +1,87 @@ -// The default game master tries to choose events with these goals in mind. -// * Don't choose an event if the crew can't take it. E.g. no meteors after half of the crew died. -// * Try to involve lots of people, particuarly in active departments. -// * Avoid giving events to the same department multiple times in a row. -/datum/game_master/default - // If an event was done for a specific department, it is written here, so it doesn't do it again. - var/last_department_used = null - - -/datum/game_master/default/choose_event() - log_game_master("Now starting event decision.") - - var/list/most_active_departments = metric.assess_all_departments(3, list(last_department_used)) - var/list/best_events = decide_best_events(most_active_departments) - - if(LAZYLEN(best_events)) - log_game_master("Got [best_events.len] choice\s for the next event.") - var/list/weighted_events = list() - - for(var/datum/event2/meta/event as anything in best_events) - var/weight = event.get_weight() - if(weight <= 0) - continue - weighted_events[event] = weight - log_game_master("Filtered down to [weighted_events.len] choice\s.") - - var/datum/event2/meta/choice = pickweight(weighted_events) - - if(choice) - log_game_master("[choice.name] was chosen, and is now being ran.") - last_department_used = LAZYACCESS(choice.departments, 1) - return choice - -/datum/game_master/default/proc/decide_best_events(list/most_active_departments) - if(!LAZYLEN(most_active_departments)) // Server's empty? - log_game_master("Game Master failed to find any active departments.") - return list() - - var/list/best_events = list() - if(most_active_departments.len >= 2) - var/list/top_two = list(most_active_departments[1], most_active_departments[2]) - best_events = filter_events_by_departments(top_two) - - if(LAZYLEN(best_events)) // We found something for those two, let's do it. - return best_events - - // Otherwise we probably couldn't find something for the second highest group, so let's ignore them. - best_events = filter_events_by_departments(most_active_departments[1]) - - if(LAZYLEN(best_events)) - return best_events - - // At this point we should expand our horizons. - best_events = filter_events_by_departments(list(DEPARTMENT_EVERYONE)) - - if(LAZYLEN(best_events)) - return best_events - - // Just give a random event if for some reason it still can't make up its mind. - best_events = filter_events_by_departments() - - if(LAZYLEN(best_events)) - return best_events - - log_game_master("Game Master failed to find a suitable event, something very wrong is going on.") - return list() - -// Filters the available events down to events for specific departments. -// Pass DEPARTMENT_EVERYONE if you want events that target the general population, like gravity failure. -// If no list is passed, all the events will be returned. -/datum/game_master/default/proc/filter_events_by_departments(list/departments) - . = list() - for(var/datum/event2/meta/event as anything in SSgame_master.available_events) - if(!event.enabled) - continue - if(event.chaotic_threshold && !ignore_round_chaos) - if(SSgame_master.danger > event.chaotic_threshold) - continue - // An event has to involve all of these departments to pass. - var/viable = TRUE - if(LAZYLEN(departments)) - for(var/department in departments) - if(!LAZYFIND(departments, department)) - viable = FALSE - break - if(viable) - . += event +// The default game master tries to choose events with these goals in mind. +// * Don't choose an event if the crew can't take it. E.g. no meteors after half of the crew died. +// * Try to involve lots of people, particuarly in active departments. +// * Avoid giving events to the same department multiple times in a row. +/datum/game_master/default + // If an event was done for a specific department, it is written here, so it doesn't do it again. + var/last_department_used = null + + +/datum/game_master/default/choose_event() + log_game_master("Now starting event decision.") + + var/list/most_active_departments = metric.assess_all_departments(3, list(last_department_used)) + var/list/best_events = decide_best_events(most_active_departments) + + if(LAZYLEN(best_events)) + log_game_master("Got [best_events.len] choice\s for the next event.") + var/list/weighted_events = list() + + for(var/datum/event2/meta/event as anything in best_events) + var/weight = event.get_weight() + if(weight <= 0) + continue + weighted_events[event] = weight + log_game_master("Filtered down to [weighted_events.len] choice\s.") + + var/datum/event2/meta/choice = pickweight(weighted_events) + + if(choice) + log_game_master("[choice.name] was chosen, and is now being ran.") + last_department_used = LAZYACCESS(choice.departments, 1) + return choice + +/datum/game_master/default/proc/decide_best_events(list/most_active_departments) + if(!LAZYLEN(most_active_departments)) // Server's empty? + log_game_master("Game Master failed to find any active departments.") + return list() + + var/list/best_events = list() + if(most_active_departments.len >= 2) + var/list/top_two = list(most_active_departments[1], most_active_departments[2]) + best_events = filter_events_by_departments(top_two) + + if(LAZYLEN(best_events)) // We found something for those two, let's do it. + return best_events + + // Otherwise we probably couldn't find something for the second highest group, so let's ignore them. + best_events = filter_events_by_departments(most_active_departments[1]) + + if(LAZYLEN(best_events)) + return best_events + + // At this point we should expand our horizons. + best_events = filter_events_by_departments(list(DEPARTMENT_EVERYONE)) + + if(LAZYLEN(best_events)) + return best_events + + // Just give a random event if for some reason it still can't make up its mind. + best_events = filter_events_by_departments() + + if(LAZYLEN(best_events)) + return best_events + + log_game_master("Game Master failed to find a suitable event, something very wrong is going on.") + return list() + +// Filters the available events down to events for specific departments. +// Pass DEPARTMENT_EVERYONE if you want events that target the general population, like gravity failure. +// If no list is passed, all the events will be returned. +/datum/game_master/default/proc/filter_events_by_departments(list/departments) + . = list() + for(var/datum/event2/meta/event as anything in SSgame_master.available_events) + if(!event.enabled) + continue + if(event.chaotic_threshold && !ignore_round_chaos) + if(SSgame_master.danger > event.chaotic_threshold) + continue + // An event has to involve all of these departments to pass. + var/viable = TRUE + if(LAZYLEN(departments)) + for(var/department in departments) + if(!LAZYFIND(departments, department)) + viable = FALSE + break + if(viable) + . += event diff --git a/code/datums/game_masters/other_game_masters.dm b/code/datums/game_masters/other_game_masters.dm index cd454832510..65d0d65789c 100644 --- a/code/datums/game_masters/other_game_masters.dm +++ b/code/datums/game_masters/other_game_masters.dm @@ -1,42 +1,42 @@ -// The `classic` game master tries to act like the old system, choosing events without any specific goals. -// * Has no goals, and instead operates purely off of the weights of the events it has. -// * Does not react to danger at all. -/datum/game_master/classic/choose_event() - var/list/weighted_events = list() - for(var/datum/event2/meta/event as anything in SSgame_master.available_events) - if(!event.enabled) - continue - weighted_events[event] = event.get_weight() - - var/datum/event2/meta/choice = pickweight(weighted_events) - - if(choice) - log_game_master("[choice.name] was chosen, and is now being ran.") - return choice - - -// The `super_random` game master chooses events purely at random, ignoring weights entirely. -// * Has no goals, and instead chooses randomly, ignoring weights. -// * Does not react to danger at all. -/datum/game_master/super_random/choose_event() - return pick(SSgame_master.available_events) - - -// The `brutal` game master tries to run dangerous events frequently. -// * Chaotic events have their weights artifically boosted. -// * Ignores accumulated danger. -/datum/game_master/brutal - ignore_round_chaos = TRUE - -/datum/game_master/brutal/choose_event() - var/list/weighted_events = list() - for(var/datum/event2/meta/event as anything in SSgame_master.available_events) - if(!event.enabled) - continue - weighted_events[event] = event.get_weight() + (event.chaos * 2) - - var/datum/event2/meta/choice = pickweight(weighted_events) - - if(choice) - log_game_master("[choice.name] was chosen, and is now being ran.") - return choice +// The `classic` game master tries to act like the old system, choosing events without any specific goals. +// * Has no goals, and instead operates purely off of the weights of the events it has. +// * Does not react to danger at all. +/datum/game_master/classic/choose_event() + var/list/weighted_events = list() + for(var/datum/event2/meta/event as anything in SSgame_master.available_events) + if(!event.enabled) + continue + weighted_events[event] = event.get_weight() + + var/datum/event2/meta/choice = pickweight(weighted_events) + + if(choice) + log_game_master("[choice.name] was chosen, and is now being ran.") + return choice + + +// The `super_random` game master chooses events purely at random, ignoring weights entirely. +// * Has no goals, and instead chooses randomly, ignoring weights. +// * Does not react to danger at all. +/datum/game_master/super_random/choose_event() + return pick(SSgame_master.available_events) + + +// The `brutal` game master tries to run dangerous events frequently. +// * Chaotic events have their weights artifically boosted. +// * Ignores accumulated danger. +/datum/game_master/brutal + ignore_round_chaos = TRUE + +/datum/game_master/brutal/choose_event() + var/list/weighted_events = list() + for(var/datum/event2/meta/event as anything in SSgame_master.available_events) + if(!event.enabled) + continue + weighted_events[event] = event.get_weight() + (event.chaos * 2) + + var/datum/event2/meta/choice = pickweight(weighted_events) + + if(choice) + log_game_master("[choice.name] was chosen, and is now being ran.") + return choice diff --git a/code/datums/helper_datums/construction_datum.dm b/code/datums/helper_datums/construction_datum.dm index d60db6e25c6..be38a7a5a2b 100644 --- a/code/datums/helper_datums/construction_datum.dm +++ b/code/datums/helper_datums/construction_datum.dm @@ -1,161 +1,161 @@ -#define FORWARD -1 -#define BACKWARD 1 - - -// As of August 4th, 2018, these datums are only used in Mech construction. -/datum/construction - var/list/steps - var/atom/holder - var/result - var/list/steps_desc - -/datum/construction/New(atom) - ..() - holder = atom - if(!holder) //don't want this without a holder - spawn - qdel(src) - set_desc(steps.len) - return - -/datum/construction/proc/next_step() - steps.len-- - if(!steps.len) - spawn_result() - else - set_desc(steps.len) - return - -/datum/construction/proc/action(var/obj/item/I,mob/user as mob) - return - -/datum/construction/proc/check_step(var/obj/item/I,mob/user as mob) //check last step only - var/valid_step = is_right_key(I) - if(valid_step) - if(custom_action(valid_step, I, user)) - next_step() - return 1 - return 0 - -/datum/construction/proc/is_right_key(var/obj/item/I) // returns current step num if I is of the right type. - var/list/L = steps[steps.len] - switch(L["key"]) - if(IS_SCREWDRIVER) - if(I.has_tool_quality(TOOL_SCREWDRIVER)) - return steps.len - if(IS_CROWBAR) - if(I.has_tool_quality(TOOL_CROWBAR)) - return steps.len - if(IS_WIRECUTTER) - if(I.has_tool_quality(TOOL_WIRECUTTER)) - return steps.len - if(IS_WRENCH) - if(I.has_tool_quality(TOOL_WRENCH)) - return steps.len - if(IS_WELDER) - if(I.has_tool_quality(IS_WELDER)) - return steps.len - - if(istype(I, L["key"])) - return steps.len - return 0 - -/datum/construction/proc/custom_action(step, I, user) - return 1 - -/datum/construction/proc/check_all_steps(var/obj/item/I,mob/user as mob) //check all steps, remove matching one. - for(var/i=1;i<=steps.len;i++) - var/list/L = steps[i]; - if(istype(I, L["key"])) - if(custom_action(i, I, user)) - steps[i]=null;//stupid byond list from list removal... - listclearnulls(steps); - if(!steps.len) - spawn_result() - return 1 - return 0 - - -/datum/construction/proc/spawn_result() - if(result) - new result(get_turf(holder)) - spawn() - qdel(holder) - return - -/datum/construction/proc/set_desc(index as num) - var/list/step = steps[index] - holder.desc = step["desc"] - return - - -// Reversible -/datum/construction/reversible - var/index - -/datum/construction/reversible/New(atom) - ..() - index = steps.len - return - -/datum/construction/reversible/proc/update_index(diff as num) - index+=diff - if(index==0) - spawn_result() - else - set_desc(index) - return - -/datum/construction/reversible/is_right_key(var/obj/item/I) // returns index step - var/list/L = steps[index] - - switch(L["key"]) - if(IS_SCREWDRIVER) - if(I.has_tool_quality(TOOL_SCREWDRIVER)) - return FORWARD - if(IS_CROWBAR) - if(I.has_tool_quality(TOOL_CROWBAR)) - return FORWARD - if(IS_WIRECUTTER) - if(I.has_tool_quality(TOOL_WIRECUTTER)) - return FORWARD - if(IS_WRENCH) - if(I.has_tool_quality(TOOL_WRENCH)) - return FORWARD - if(IS_WELDER) - if(I.has_tool_quality(IS_WELDER)) - return FORWARD - - switch(L["backkey"]) - if(IS_SCREWDRIVER) - if(I.has_tool_quality(TOOL_SCREWDRIVER)) - return BACKWARD - if(IS_CROWBAR) - if(I.has_tool_quality(TOOL_CROWBAR)) - return BACKWARD - if(IS_WIRECUTTER) - if(I.has_tool_quality(TOOL_WIRECUTTER)) - return BACKWARD - if(IS_WRENCH) - if(I.has_tool_quality(TOOL_WRENCH)) - return BACKWARD - if(IS_WELDER) - if(I.has_tool_quality(IS_WELDER)) - return BACKWARD - - if(istype(I, L["key"])) - return FORWARD //to the first step -> forward - else if(L["backkey"] && istype(I, L["backkey"])) - return BACKWARD //to the last step -> backwards - return 0 - -/datum/construction/reversible/check_step(var/obj/item/I,mob/user as mob) - var/diff = is_right_key(I) - if(diff) - if(custom_action(index, diff, I, user)) - update_index(diff) - return 1 - return 0 - -/datum/construction/reversible/custom_action(index, diff, I, user) +#define FORWARD -1 +#define BACKWARD 1 + + +// As of August 4th, 2018, these datums are only used in Mech construction. +/datum/construction + var/list/steps + var/atom/holder + var/result + var/list/steps_desc + +/datum/construction/New(atom) + ..() + holder = atom + if(!holder) //don't want this without a holder + spawn + qdel(src) + set_desc(steps.len) + return + +/datum/construction/proc/next_step() + steps.len-- + if(!steps.len) + spawn_result() + else + set_desc(steps.len) + return + +/datum/construction/proc/action(var/obj/item/I,mob/user as mob) + return + +/datum/construction/proc/check_step(var/obj/item/I,mob/user as mob) //check last step only + var/valid_step = is_right_key(I) + if(valid_step) + if(custom_action(valid_step, I, user)) + next_step() + return 1 + return 0 + +/datum/construction/proc/is_right_key(var/obj/item/I) // returns current step num if I is of the right type. + var/list/L = steps[steps.len] + switch(L["key"]) + if(IS_SCREWDRIVER) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + return steps.len + if(IS_CROWBAR) + if(I.has_tool_quality(TOOL_CROWBAR)) + return steps.len + if(IS_WIRECUTTER) + if(I.has_tool_quality(TOOL_WIRECUTTER)) + return steps.len + if(IS_WRENCH) + if(I.has_tool_quality(TOOL_WRENCH)) + return steps.len + if(IS_WELDER) + if(I.has_tool_quality(IS_WELDER)) + return steps.len + + if(istype(I, L["key"])) + return steps.len + return 0 + +/datum/construction/proc/custom_action(step, I, user) + return 1 + +/datum/construction/proc/check_all_steps(var/obj/item/I,mob/user as mob) //check all steps, remove matching one. + for(var/i=1;i<=steps.len;i++) + var/list/L = steps[i]; + if(istype(I, L["key"])) + if(custom_action(i, I, user)) + steps[i]=null;//stupid byond list from list removal... + listclearnulls(steps); + if(!steps.len) + spawn_result() + return 1 + return 0 + + +/datum/construction/proc/spawn_result() + if(result) + new result(get_turf(holder)) + spawn() + qdel(holder) + return + +/datum/construction/proc/set_desc(index as num) + var/list/step = steps[index] + holder.desc = step["desc"] + return + + +// Reversible +/datum/construction/reversible + var/index + +/datum/construction/reversible/New(atom) + ..() + index = steps.len + return + +/datum/construction/reversible/proc/update_index(diff as num) + index+=diff + if(index==0) + spawn_result() + else + set_desc(index) + return + +/datum/construction/reversible/is_right_key(var/obj/item/I) // returns index step + var/list/L = steps[index] + + switch(L["key"]) + if(IS_SCREWDRIVER) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + return FORWARD + if(IS_CROWBAR) + if(I.has_tool_quality(TOOL_CROWBAR)) + return FORWARD + if(IS_WIRECUTTER) + if(I.has_tool_quality(TOOL_WIRECUTTER)) + return FORWARD + if(IS_WRENCH) + if(I.has_tool_quality(TOOL_WRENCH)) + return FORWARD + if(IS_WELDER) + if(I.has_tool_quality(IS_WELDER)) + return FORWARD + + switch(L["backkey"]) + if(IS_SCREWDRIVER) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + return BACKWARD + if(IS_CROWBAR) + if(I.has_tool_quality(TOOL_CROWBAR)) + return BACKWARD + if(IS_WIRECUTTER) + if(I.has_tool_quality(TOOL_WIRECUTTER)) + return BACKWARD + if(IS_WRENCH) + if(I.has_tool_quality(TOOL_WRENCH)) + return BACKWARD + if(IS_WELDER) + if(I.has_tool_quality(IS_WELDER)) + return BACKWARD + + if(istype(I, L["key"])) + return FORWARD //to the first step -> forward + else if(L["backkey"] && istype(I, L["backkey"])) + return BACKWARD //to the last step -> backwards + return 0 + +/datum/construction/reversible/check_step(var/obj/item/I,mob/user as mob) + var/diff = is_right_key(I) + if(diff) + if(custom_action(index, diff, I, user)) + update_index(diff) + return 1 + return 0 + +/datum/construction/reversible/custom_action(index, diff, I, user) return 1 \ No newline at end of file diff --git a/code/datums/helper_datums/events.dm b/code/datums/helper_datums/events.dm index bc3661470e6..64a17ab0a0d 100644 --- a/code/datums/helper_datums/events.dm +++ b/code/datums/helper_datums/events.dm @@ -1,67 +1,67 @@ -/* - * WARRANTY VOID IF CODE USED - */ - - -/datum/events - var/list/events - -/datum/events/New() - ..() - events = new - -/datum/events/proc/addEventType(event_type as text) - if(!(event_type in events) || !islist(events[event_type])) - events[event_type] = list() - return 1 - return - - -// Arguments: event_type as text, proc_holder as datum, proc_name as text -// Returns: New event, null on error. -/datum/events/proc/addEvent(event_type as text, proc_holder, proc_name as text) - if(!event_type || !proc_holder || !proc_name) - return - addEventType(event_type) - var/list/event = events[event_type] - var/datum/event/E = new /datum/event(proc_holder,proc_name) - event += E - return E - -// Arguments: event_type as text, any number of additional arguments to pass to event handler -// Returns: null -/datum/events/proc/fireEvent() - //to_world("Events in [args[1]] called") - var/list/event = listgetindex(events,args[1]) - if(istype(event)) - spawn(-1) - for(var/datum/event/E in event) - if(!E.Fire(arglist(args.Copy(2)))) - clearEvent(args[1],E) - return - -// Arguments: event_type as text, E as /datum/event -// Returns: 1 if event cleared, null on error -/datum/events/proc/clearEvent(event_type as text, datum/event/E) - if(!event_type || !E) - return - var/list/event = listgetindex(events,event_type) - event -= E - return 1 - - -/datum/event - var/listener - var/proc_name - -/datum/event/New(tlistener,tprocname) - listener = tlistener - proc_name = tprocname - return ..() - -/datum/event/proc/Fire() - //to_world("Event fired") - if(listener) - call(listener,proc_name)(arglist(args)) - return 1 +/* + * WARRANTY VOID IF CODE USED + */ + + +/datum/events + var/list/events + +/datum/events/New() + ..() + events = new + +/datum/events/proc/addEventType(event_type as text) + if(!(event_type in events) || !islist(events[event_type])) + events[event_type] = list() + return 1 + return + + +// Arguments: event_type as text, proc_holder as datum, proc_name as text +// Returns: New event, null on error. +/datum/events/proc/addEvent(event_type as text, proc_holder, proc_name as text) + if(!event_type || !proc_holder || !proc_name) + return + addEventType(event_type) + var/list/event = events[event_type] + var/datum/event/E = new /datum/event(proc_holder,proc_name) + event += E + return E + +// Arguments: event_type as text, any number of additional arguments to pass to event handler +// Returns: null +/datum/events/proc/fireEvent() + //to_world("Events in [args[1]] called") + var/list/event = listgetindex(events,args[1]) + if(istype(event)) + spawn(-1) + for(var/datum/event/E in event) + if(!E.Fire(arglist(args.Copy(2)))) + clearEvent(args[1],E) + return + +// Arguments: event_type as text, E as /datum/event +// Returns: 1 if event cleared, null on error +/datum/events/proc/clearEvent(event_type as text, datum/event/E) + if(!event_type || !E) + return + var/list/event = listgetindex(events,event_type) + event -= E + return 1 + + +/datum/event + var/listener + var/proc_name + +/datum/event/New(tlistener,tprocname) + listener = tlistener + proc_name = tprocname + return ..() + +/datum/event/proc/Fire() + //to_world("Event fired") + if(listener) + call(listener,proc_name)(arglist(args)) + return 1 return \ No newline at end of file diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm index 7d78dae60ae..909be033a26 100644 --- a/code/datums/helper_datums/getrev.dm +++ b/code/datums/helper_datums/getrev.dm @@ -1,84 +1,84 @@ -GLOBAL_DATUM(revdata, /datum/getrev) - -/datum/getrev - var/branch - var/revision - var/date - var/showinfo - var/list/testmerge = list() - -/datum/getrev/New() - if(world.TgsAvailable()) // Try TGS maybe - testmerge = world.TgsTestMerges() - var/datum/tgs_revision_information/REV = world.TgsRevision() - if(REV) - revision = REV.origin_commit || REV.commit - branch = "-Using TGS-" // TGS doesn't provide branch info yet - date = "-Using TGS-" // Or date - - if(!revision) // File parse method - var/list/head_branch = file2list(".git/HEAD", "\n") - if(head_branch.len) - branch = copytext(head_branch[1], 17) - - var/list/head_log = file2list(".git/logs/HEAD", "\n") - for(var/line=head_log.len, line>=1, line--) - if(head_log[line]) - var/list/last_entry = splittext(head_log[line], " ") - if(last_entry.len < 2) continue - revision = last_entry[2] - // Get date/time - if(last_entry.len >= 5) - var/unix_time = text2num(last_entry[5]) - if(unix_time) - date = unix2date(unix_time) - break - - to_world_log("-Revision Info-") - to_world_log("Branch: [branch]") - to_world_log("Date: [date]") - to_world_log("Revision: [revision]") - -/datum/getrev/proc/GetTestMergeInfo(header = TRUE) - . = list() - if(!testmerge.len) - return - if(header) - . += "The following pull requests are currently test merged:" - for(var/datum/tgs_revision_information/test_merge/tm as anything in testmerge) - var/cm = tm.head_commit // YW Edit: TGS4 - var/details = ": '" + html_encode(tm.title) + "' by " + html_encode(tm.author) + " at commit " + html_encode(copytext_char(cm, 1, 11)) - if(details && findtext(details, "\[s\]") && (!usr || !usr.client.holder)) - continue - . += "#[tm.number][details]" - -/client/verb/showrevinfo() - set category = "OOC" - set name = "Show Server Revision" - set desc = "Check the current server code revision" - - if(!GLOB.revdata) - to_chat(src, "Please wait until server initializations are complete.") - return - - var/list/msg = list() - - if(GLOB.revdata.revision) - msg += "Server revision: B:[GLOB.revdata.branch] D:[GLOB.revdata.date]" - if(config.githuburl) - msg += "Commit: [GLOB.revdata.revision]" - else - msg += "Commit: GLOB.revdata.revision" - else - msg += "Server revision: Unknown" - - if(world.TgsAvailable()) - var/datum/tgs_version/version = world.TgsVersion() - msg += "TGS version: [version.raw_parameter]" - var/datum/tgs_version/api_version = world.TgsApiVersion() - msg += "DMAPI version: [api_version.raw_parameter]" - - if(GLOB.revdata.testmerge.len) - msg += GLOB.revdata.GetTestMergeInfo() - - to_chat(src, msg.Join("
    ")) +GLOBAL_DATUM(revdata, /datum/getrev) + +/datum/getrev + var/branch + var/revision + var/date + var/showinfo + var/list/testmerge = list() + +/datum/getrev/New() + if(world.TgsAvailable()) // Try TGS maybe + testmerge = world.TgsTestMerges() + var/datum/tgs_revision_information/REV = world.TgsRevision() + if(REV) + revision = REV.origin_commit || REV.commit + branch = "-Using TGS-" // TGS doesn't provide branch info yet + date = "-Using TGS-" // Or date + + if(!revision) // File parse method + var/list/head_branch = file2list(".git/HEAD", "\n") + if(head_branch.len) + branch = copytext(head_branch[1], 17) + + var/list/head_log = file2list(".git/logs/HEAD", "\n") + for(var/line=head_log.len, line>=1, line--) + if(head_log[line]) + var/list/last_entry = splittext(head_log[line], " ") + if(last_entry.len < 2) continue + revision = last_entry[2] + // Get date/time + if(last_entry.len >= 5) + var/unix_time = text2num(last_entry[5]) + if(unix_time) + date = unix2date(unix_time) + break + + to_world_log("-Revision Info-") + to_world_log("Branch: [branch]") + to_world_log("Date: [date]") + to_world_log("Revision: [revision]") + +/datum/getrev/proc/GetTestMergeInfo(header = TRUE) + . = list() + if(!testmerge.len) + return + if(header) + . += "The following pull requests are currently test merged:" + for(var/datum/tgs_revision_information/test_merge/tm as anything in testmerge) + var/cm = tm.head_commit // YW Edit: TGS4 + var/details = ": '" + html_encode(tm.title) + "' by " + html_encode(tm.author) + " at commit " + html_encode(copytext_char(cm, 1, 11)) + if(details && findtext(details, "\[s\]") && (!usr || !usr.client.holder)) + continue + . += "#[tm.number][details]" + +/client/verb/showrevinfo() + set category = "OOC" + set name = "Show Server Revision" + set desc = "Check the current server code revision" + + if(!GLOB.revdata) + to_chat(src, "Please wait until server initializations are complete.") + return + + var/list/msg = list() + + if(GLOB.revdata.revision) + msg += "Server revision: B:[GLOB.revdata.branch] D:[GLOB.revdata.date]" + if(config.githuburl) + msg += "Commit: [GLOB.revdata.revision]" + else + msg += "Commit: GLOB.revdata.revision" + else + msg += "Server revision: Unknown" + + if(world.TgsAvailable()) + var/datum/tgs_version/version = world.TgsVersion() + msg += "TGS version: [version.raw_parameter]" + var/datum/tgs_version/api_version = world.TgsApiVersion() + msg += "DMAPI version: [api_version.raw_parameter]" + + if(GLOB.revdata.testmerge.len) + msg += GLOB.revdata.GetTestMergeInfo() + + to_chat(src, msg.Join("
    ")) diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index 194341a106c..f79ad0f7286 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -1,234 +1,234 @@ -var/bluespace_item_types = newlist(/obj/item/weapon/storage/backpack/holding, -/obj/item/weapon/storage/bag/trash/holding, -/obj/item/weapon/storage/pouch/holding, -/obj/item/weapon/storage/belt/utility/holding, -/obj/item/weapon/storage/belt/medical/holding -) - -//wrapper -/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, local=TRUE) //VOREStation Edit - new /datum/teleport/instant/science(arglist(args)) - return - -/datum/teleport - var/atom/movable/teleatom //atom to teleport - var/atom/destination //destination to teleport to - var/precision = 0 //teleport precision - var/datum/effect/effect/system/effectin //effect to show right before teleportation - var/datum/effect/effect/system/effectout //effect to show right after teleportation - var/soundin //soundfile to play before teleportation - var/soundout //soundfile to play after teleportation - var/force_teleport = 1 //if false, teleport will use Move() proc (dense objects will prevent teleportation) - var/local = TRUE //VOREStation Add - If false, can teleport from/to any z-level - - -/datum/teleport/New(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, local=TRUE) //VOREStation Edit - ..() - if(!initTeleport(arglist(args))) - return 0 - return 1 - -/datum/teleport/proc/initTeleport(ateleatom,adestination,aprecision,afteleport,aeffectin,aeffectout,asoundin,asoundout,local) //VOREStation Edit - if(!setTeleatom(ateleatom)) - return 0 - if(!setDestination(adestination)) - return 0 - if(!setPrecision(aprecision)) - return 0 - setEffects(aeffectin,aeffectout) - setForceTeleport(afteleport) - setSounds(asoundin,asoundout) - src.local = local // VOREStation Add - return 1 - -//must succeed -/datum/teleport/proc/setPrecision(aprecision) - if(isnum(aprecision)) - precision = aprecision - return 1 - return 0 - -//must succeed -/datum/teleport/proc/setDestination(atom/adestination) - if(istype(adestination)) - destination = adestination - return 1 - return 0 - -//must succeed in most cases -/datum/teleport/proc/setTeleatom(atom/movable/ateleatom) - if(istype(ateleatom, /obj/effect) && !istype(ateleatom, /obj/effect/dummy/chameleon)) - qdel(ateleatom) - return 0 - if(istype(ateleatom)) - teleatom = ateleatom - return 1 - return 0 - -//custom effects must be properly set up first for instant-type teleports -//optional -/datum/teleport/proc/setEffects(datum/effect/effect/system/aeffectin=null,datum/effect/effect/system/aeffectout=null) - effectin = istype(aeffectin) ? aeffectin : null - effectout = istype(aeffectout) ? aeffectout : null - return 1 - -//optional -/datum/teleport/proc/setForceTeleport(afteleport) - force_teleport = afteleport - return 1 - -//optional -/datum/teleport/proc/setSounds(asoundin=null,asoundout=null) - soundin = isfile(asoundin) ? asoundin : null - soundout = isfile(asoundout) ? asoundout : null - return 1 - -//placeholder -/datum/teleport/proc/teleportChecks() - return 1 - -/datum/teleport/proc/playSpecials(atom/location,datum/effect/effect/system/effect,sound) - if(location) - if(effect) - spawn(-1) - src = null - effect.attach(location) - effect.start() - if(sound) - spawn(-1) - src = null - playsound(location,sound,60,1) - return - -//do the monkey dance -/datum/teleport/proc/doTeleport() - - var/turf/destturf - var/turf/curturf = get_turf(teleatom) - if(precision) - var/list/posturfs = circlerangeturfs(destination,precision) - destturf = safepick(posturfs) - else - destturf = get_turf(destination) - - if(!destturf || !curturf) - return 0 - - playSpecials(curturf,effectin,soundin) - - var/obj/structure/bed/chair/C = null - if(isliving(teleatom)) - var/mob/living/L = teleatom - if(L.buckled) - C = L.buckled - if(attempt_vr(src,"try_televore",args)) return //VOREStation Edit - Telenoms. - if(force_teleport) - teleatom.forceMove(destturf) - playSpecials(destturf,effectout,soundout) - else - if(teleatom.Move(destturf)) - playSpecials(destturf,effectout,soundout) - if(C) - C.forceMove(destturf) - - return 1 - -/datum/teleport/proc/teleport() - if(teleportChecks()) - return doTeleport() - return 0 - -/datum/teleport/instant //teleports when datum is created - -/datum/teleport/instant/New(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) - if(..()) - teleport() - return - - -/datum/teleport/instant/science/setEffects(datum/effect/effect/system/aeffectin,datum/effect/effect/system/aeffectout) - if(!aeffectin || !aeffectout) - var/datum/effect/effect/system/spark_spread/aeffect = new - aeffect.set_up(5, 1, teleatom) - effectin = effectin || aeffect - effectout = effectout || aeffect - return 1 - else - return ..() - -/datum/teleport/instant/science/setPrecision(aprecision) - ..() - - var/list/bluespace_things = newlist() - - for (var/item in bluespace_item_types) - if (istype(teleatom, item)) - precision = rand(1, 100) - bluespace_things |= teleatom.search_contents_for(item) - - //VOREStation Addition Start: Prevent taurriding abuse - if(istype(teleatom, /mob/living)) - var/mob/living/L = teleatom - if(LAZYLEN(L.buckled_mobs)) - for(var/mob/rider in L.buckled_mobs) - for (var/item in bluespace_item_types) - bluespace_things |= rider.search_contents_for(item) - //VOREStation Addition End: Prevent taurriding abuse - - if(bluespace_things.len) - precision = max(rand(1,100)*bluespace_things.len,100) - if(istype(teleatom, /mob/living)) - var/mob/living/MM = teleatom - to_chat(MM, "The Bluespace interface on your [teleatom] interferes with the teleport!") - return 1 - -/datum/teleport/instant/science/teleportChecks() - if(istype(teleatom, /obj/item/weapon/disk/nuclear)) // Don't let nuke disks get teleported --NeoFite - teleatom.visible_message("\The [teleatom] bounces off of the portal!") - return 0 - - if(!isemptylist(teleatom.search_contents_for(/obj/item/weapon/disk/nuclear))) - if(istype(teleatom, /mob/living)) - var/mob/living/MM = teleatom - MM.visible_message("\The [MM] bounces off of the portal!","Something you are carrying seems to be unable to pass through the portal. Better drop it if you want to go through.") - else - teleatom.visible_message("\The [teleatom] bounces off of the portal!") - return 0 - /* VOREStation Removal - if(destination.z in using_map.admin_levels) //CentCom z-level - if(istype(teleatom, /obj/mecha)) - var/obj/mecha/MM = teleatom - to_chat(MM.occupant, "\The [MM] would not survive the jump to a location so far away!") - return 0 - if(!isemptylist(teleatom.search_contents_for(/obj/item/weapon/storage/backpack/holding))) - teleatom.visible_message("\The [teleatom] bounces off of the portal!") - return 0 - */ //VOREStation Removal End - //VOREStation Edit Start - var/obstructed = 0 - var/turf/dest_turf = get_turf(destination) - if(local && !(dest_turf.z in using_map.player_levels)) - if(istype(teleatom, /mob/living)) - to_chat(teleatom, "The portal refuses to carry you that far away!") - return 0 - else if(istype(destination.loc, /obj/belly)) - var/obj/belly/destination_belly = destination.loc - var/mob/living/telenommer = destination_belly.owner - if(istype(telenommer)) - if(istype(teleatom, /obj/machinery) || istype(teleatom, /obj/structure)) - return 0 - else if(!isliving(teleatom)) - return 1 - else - var/mob/living/telemob = teleatom - if(telemob.can_be_drop_prey && telenommer.can_be_drop_pred) - return 1 - obstructed = 1 - else if(!((isturf(destination) && !destination.density) || (isturf(destination.loc) && !destination.loc.density)) || !destination.x || !destination.y || !destination.z) //If we're inside something or outside universe - obstructed = 1 - to_chat(teleatom, "Something is blocking way on the other side!") - if(obstructed) - return 0 - else - return 1 +var/bluespace_item_types = newlist(/obj/item/weapon/storage/backpack/holding, +/obj/item/weapon/storage/bag/trash/holding, +/obj/item/weapon/storage/pouch/holding, +/obj/item/weapon/storage/belt/utility/holding, +/obj/item/weapon/storage/belt/medical/holding +) + +//wrapper +/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, local=TRUE) //VOREStation Edit + new /datum/teleport/instant/science(arglist(args)) + return + +/datum/teleport + var/atom/movable/teleatom //atom to teleport + var/atom/destination //destination to teleport to + var/precision = 0 //teleport precision + var/datum/effect/effect/system/effectin //effect to show right before teleportation + var/datum/effect/effect/system/effectout //effect to show right after teleportation + var/soundin //soundfile to play before teleportation + var/soundout //soundfile to play after teleportation + var/force_teleport = 1 //if false, teleport will use Move() proc (dense objects will prevent teleportation) + var/local = TRUE //VOREStation Add - If false, can teleport from/to any z-level + + +/datum/teleport/New(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, local=TRUE) //VOREStation Edit + ..() + if(!initTeleport(arglist(args))) + return 0 + return 1 + +/datum/teleport/proc/initTeleport(ateleatom,adestination,aprecision,afteleport,aeffectin,aeffectout,asoundin,asoundout,local) //VOREStation Edit + if(!setTeleatom(ateleatom)) + return 0 + if(!setDestination(adestination)) + return 0 + if(!setPrecision(aprecision)) + return 0 + setEffects(aeffectin,aeffectout) + setForceTeleport(afteleport) + setSounds(asoundin,asoundout) + src.local = local // VOREStation Add + return 1 + +//must succeed +/datum/teleport/proc/setPrecision(aprecision) + if(isnum(aprecision)) + precision = aprecision + return 1 + return 0 + +//must succeed +/datum/teleport/proc/setDestination(atom/adestination) + if(istype(adestination)) + destination = adestination + return 1 + return 0 + +//must succeed in most cases +/datum/teleport/proc/setTeleatom(atom/movable/ateleatom) + if(istype(ateleatom, /obj/effect) && !istype(ateleatom, /obj/effect/dummy/chameleon)) + qdel(ateleatom) + return 0 + if(istype(ateleatom)) + teleatom = ateleatom + return 1 + return 0 + +//custom effects must be properly set up first for instant-type teleports +//optional +/datum/teleport/proc/setEffects(datum/effect/effect/system/aeffectin=null,datum/effect/effect/system/aeffectout=null) + effectin = istype(aeffectin) ? aeffectin : null + effectout = istype(aeffectout) ? aeffectout : null + return 1 + +//optional +/datum/teleport/proc/setForceTeleport(afteleport) + force_teleport = afteleport + return 1 + +//optional +/datum/teleport/proc/setSounds(asoundin=null,asoundout=null) + soundin = isfile(asoundin) ? asoundin : null + soundout = isfile(asoundout) ? asoundout : null + return 1 + +//placeholder +/datum/teleport/proc/teleportChecks() + return 1 + +/datum/teleport/proc/playSpecials(atom/location,datum/effect/effect/system/effect,sound) + if(location) + if(effect) + spawn(-1) + src = null + effect.attach(location) + effect.start() + if(sound) + spawn(-1) + src = null + playsound(location,sound,60,1) + return + +//do the monkey dance +/datum/teleport/proc/doTeleport() + + var/turf/destturf + var/turf/curturf = get_turf(teleatom) + if(precision) + var/list/posturfs = circlerangeturfs(destination,precision) + destturf = safepick(posturfs) + else + destturf = get_turf(destination) + + if(!destturf || !curturf) + return 0 + + playSpecials(curturf,effectin,soundin) + + var/obj/structure/bed/chair/C = null + if(isliving(teleatom)) + var/mob/living/L = teleatom + if(L.buckled) + C = L.buckled + if(attempt_vr(src,"try_televore",args)) return //VOREStation Edit - Telenoms. + if(force_teleport) + teleatom.forceMove(destturf) + playSpecials(destturf,effectout,soundout) + else + if(teleatom.Move(destturf)) + playSpecials(destturf,effectout,soundout) + if(C) + C.forceMove(destturf) + + return 1 + +/datum/teleport/proc/teleport() + if(teleportChecks()) + return doTeleport() + return 0 + +/datum/teleport/instant //teleports when datum is created + +/datum/teleport/instant/New(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) + if(..()) + teleport() + return + + +/datum/teleport/instant/science/setEffects(datum/effect/effect/system/aeffectin,datum/effect/effect/system/aeffectout) + if(!aeffectin || !aeffectout) + var/datum/effect/effect/system/spark_spread/aeffect = new + aeffect.set_up(5, 1, teleatom) + effectin = effectin || aeffect + effectout = effectout || aeffect + return 1 + else + return ..() + +/datum/teleport/instant/science/setPrecision(aprecision) + ..() + + var/list/bluespace_things = newlist() + + for (var/item in bluespace_item_types) + if (istype(teleatom, item)) + precision = rand(1, 100) + bluespace_things |= teleatom.search_contents_for(item) + + //VOREStation Addition Start: Prevent taurriding abuse + if(istype(teleatom, /mob/living)) + var/mob/living/L = teleatom + if(LAZYLEN(L.buckled_mobs)) + for(var/mob/rider in L.buckled_mobs) + for (var/item in bluespace_item_types) + bluespace_things |= rider.search_contents_for(item) + //VOREStation Addition End: Prevent taurriding abuse + + if(bluespace_things.len) + precision = max(rand(1,100)*bluespace_things.len,100) + if(istype(teleatom, /mob/living)) + var/mob/living/MM = teleatom + to_chat(MM, "The Bluespace interface on your [teleatom] interferes with the teleport!") + return 1 + +/datum/teleport/instant/science/teleportChecks() + if(istype(teleatom, /obj/item/weapon/disk/nuclear)) // Don't let nuke disks get teleported --NeoFite + teleatom.visible_message("\The [teleatom] bounces off of the portal!") + return 0 + + if(!isemptylist(teleatom.search_contents_for(/obj/item/weapon/disk/nuclear))) + if(istype(teleatom, /mob/living)) + var/mob/living/MM = teleatom + MM.visible_message("\The [MM] bounces off of the portal!","Something you are carrying seems to be unable to pass through the portal. Better drop it if you want to go through.") + else + teleatom.visible_message("\The [teleatom] bounces off of the portal!") + return 0 + /* VOREStation Removal + if(destination.z in using_map.admin_levels) //CentCom z-level + if(istype(teleatom, /obj/mecha)) + var/obj/mecha/MM = teleatom + to_chat(MM.occupant, "\The [MM] would not survive the jump to a location so far away!") + return 0 + if(!isemptylist(teleatom.search_contents_for(/obj/item/weapon/storage/backpack/holding))) + teleatom.visible_message("\The [teleatom] bounces off of the portal!") + return 0 + */ //VOREStation Removal End + //VOREStation Edit Start + var/obstructed = 0 + var/turf/dest_turf = get_turf(destination) + if(local && !(dest_turf.z in using_map.player_levels)) + if(istype(teleatom, /mob/living)) + to_chat(teleatom, "The portal refuses to carry you that far away!") + return 0 + else if(istype(destination.loc, /obj/belly)) + var/obj/belly/destination_belly = destination.loc + var/mob/living/telenommer = destination_belly.owner + if(istype(telenommer)) + if(istype(teleatom, /obj/machinery) || istype(teleatom, /obj/structure)) + return 0 + else if(!isliving(teleatom)) + return 1 + else + var/mob/living/telemob = teleatom + if(telemob.can_be_drop_prey && telenommer.can_be_drop_pred) + return 1 + obstructed = 1 + else if(!((isturf(destination) && !destination.density) || (isturf(destination.loc) && !destination.loc.density)) || !destination.x || !destination.y || !destination.z) //If we're inside something or outside universe + obstructed = 1 + to_chat(teleatom, "Something is blocking way on the other side!") + if(obstructed) + return 0 + else + return 1 //VOREStation Edit End \ No newline at end of file diff --git a/code/datums/helper_datums/topic_input.dm b/code/datums/helper_datums/topic_input.dm index 809a6c4bc5c..bf073402255 100644 --- a/code/datums/helper_datums/topic_input.dm +++ b/code/datums/helper_datums/topic_input.dm @@ -1,60 +1,60 @@ -/datum/topic_input - var/href - var/list/href_list - -/datum/topic_input/New(thref,list/thref_list) - href = thref - href_list = thref_list.Copy() - return - -/datum/topic_input/proc/get(i) - return listgetindex(href_list,i) - -/datum/topic_input/proc/getAndLocate(i) - var/t = get(i) - if(t) - t = locate(t) - return t || null - -/datum/topic_input/proc/getNum(i) - var/t = get(i) - if(t) - t = text2num(t) - return isnum(t) ? t : null - -/datum/topic_input/proc/getObj(i) - var/t = getAndLocate(i) - return isobj(t) ? t : null - -/datum/topic_input/proc/getMob(i) - var/t = getAndLocate(i) - return ismob(t) ? t : null - -/datum/topic_input/proc/getTurf(i) - var/t = getAndLocate(i) - return isturf(t) ? t : null - -/datum/topic_input/proc/getAtom(i) - return getType(i,/atom) - -/datum/topic_input/proc/getArea(i) - var/t = getAndLocate(i) - return isarea(t) ? t : null - -/datum/topic_input/proc/getStr(i)//params should always be text, but... - var/t = get(i) - return istext(t) ? t : null - -/datum/topic_input/proc/getType(i,type) - var/t = getAndLocate(i) - return istype(t,type) ? t : null - -/datum/topic_input/proc/getPath(i) - var/t = get(i) - if(t) - t = text2path(t) - return ispath(t) ? t : null - -/datum/topic_input/proc/getList(i) - var/t = getAndLocate(i) +/datum/topic_input + var/href + var/list/href_list + +/datum/topic_input/New(thref,list/thref_list) + href = thref + href_list = thref_list.Copy() + return + +/datum/topic_input/proc/get(i) + return listgetindex(href_list,i) + +/datum/topic_input/proc/getAndLocate(i) + var/t = get(i) + if(t) + t = locate(t) + return t || null + +/datum/topic_input/proc/getNum(i) + var/t = get(i) + if(t) + t = text2num(t) + return isnum(t) ? t : null + +/datum/topic_input/proc/getObj(i) + var/t = getAndLocate(i) + return isobj(t) ? t : null + +/datum/topic_input/proc/getMob(i) + var/t = getAndLocate(i) + return ismob(t) ? t : null + +/datum/topic_input/proc/getTurf(i) + var/t = getAndLocate(i) + return isturf(t) ? t : null + +/datum/topic_input/proc/getAtom(i) + return getType(i,/atom) + +/datum/topic_input/proc/getArea(i) + var/t = getAndLocate(i) + return isarea(t) ? t : null + +/datum/topic_input/proc/getStr(i)//params should always be text, but... + var/t = get(i) + return istext(t) ? t : null + +/datum/topic_input/proc/getType(i,type) + var/t = getAndLocate(i) + return istype(t,type) ? t : null + +/datum/topic_input/proc/getPath(i) + var/t = get(i) + if(t) + t = text2path(t) + return ispath(t) ? t : null + +/datum/topic_input/proc/getList(i) + var/t = getAndLocate(i) return islist(t) ? t : null \ No newline at end of file diff --git a/code/datums/looping_sounds/_looping_sound.dm b/code/datums/looping_sounds/_looping_sound.dm index e9397825298..078db317d5e 100644 --- a/code/datums/looping_sounds/_looping_sound.dm +++ b/code/datums/looping_sounds/_looping_sound.dm @@ -1,126 +1,126 @@ -/* - output_atoms (list of atoms) The destination(s) for the sounds - - mid_sounds (list or soundfile) Since this can be either a list or a single soundfile you can have random sounds. May contain further lists but must contain a soundfile at the end. - mid_length (num) The length to wait between playing mid_sounds - - start_sound (soundfile) Played before starting the mid_sounds loop - start_length (num) How long to wait before starting the main loop after playing start_sound - - end_sound (soundfile) The sound played after the main loop has concluded - - chance (num) Chance per loop to play a mid_sound - volume (num) Sound output volume - muted (bool) Private. Used to stop the sound loop. - max_loops (num) The max amount of loops to run for. - direct (bool) If true plays directly to provided atoms instead of from them - opacity_check (bool) If true, things behind walls/opaque things won't hear the sounds. - pref_check (type) If set to a /datum/client_preference type, will check if the hearer has that preference active before playing it to them. - exclusive (bool) If true, only one of this sound is allowed to play. Relies on if started is true or not. If true, it will not start another loop until it is false. -*/ -/datum/looping_sound - var/list/atom/output_atoms - var/mid_sounds - var/mid_length - var/start_sound - var/start_length - var/end_sound - var/chance - var/volume = 100 - var/max_loops - var/direct - var/vary - var/extra_range - var/opacity_check - var/pref_check - var/exclusive - - var/timerid - var/started - -/datum/looping_sound/New(list/_output_atoms=list(), start_immediately=FALSE, disable_direct=FALSE) - if(!mid_sounds) - WARNING("A looping sound datum was created without sounds to play.") - return - - output_atoms = _output_atoms - if(disable_direct) - direct = FALSE - - if(start_immediately) - start() - -/datum/looping_sound/Destroy() - stop() - output_atoms = null - return ..() - -/datum/looping_sound/proc/start(atom/add_thing, skip_start_sound = FALSE) - if(add_thing) - output_atoms |= add_thing - if(timerid) - return - if(skip_start_sound && (!exclusive && !started)) // Skip start sounds optionally, check if we're exclusive AND started already - sound_loop() - started = TRUE - return - if(exclusive && started) // Prevents a sound from starting multiple times - return // Don't start this loop. - on_start() - started = TRUE - -/datum/looping_sound/proc/stop(atom/remove_thing, skip_stop_sound = FALSE) - if(remove_thing) - output_atoms -= remove_thing - if(!timerid) - return - if(!skip_stop_sound) - on_stop() - deltimer(timerid) - timerid = null - started = FALSE - -/datum/looping_sound/proc/sound_loop(starttime) - if(max_loops && world.time >= starttime + mid_length * max_loops) - stop() - return - if(!chance || prob(chance)) - play(get_sound(starttime)) - if(!timerid) - timerid = addtimer(CALLBACK(src, PROC_REF(sound_loop), world.time), mid_length, TIMER_STOPPABLE | TIMER_LOOP) - -/datum/looping_sound/proc/play(soundfile) - var/list/atoms_cache = output_atoms - var/sound/S = sound(soundfile) - if(direct) - S.channel = SSsounds.random_available_channel() - S.volume = volume - for(var/i in 1 to atoms_cache.len) - var/atom/thing = atoms_cache[i] - if(direct) - if(ismob(thing)) - var/mob/M = thing - if(pref_check && !M.is_preference_enabled(pref_check)) - continue - SEND_SOUND(thing, S) - else - playsound(thing, S, volume, vary, extra_range, ignore_walls = !opacity_check, preference = pref_check) - -/datum/looping_sound/proc/get_sound(starttime, _mid_sounds) - if(!_mid_sounds) - . = mid_sounds - else - . = _mid_sounds - while(!isfile(.) && !isnull(.)) - . = pickweight(.) - -/datum/looping_sound/proc/on_start() - var/start_wait = 1 // On TG this is 0, however it needs to be 1 to work around an issue. - if(start_sound) - play(start_sound) - start_wait = start_length - addtimer(CALLBACK(src, PROC_REF(sound_loop)), start_wait) - -/datum/looping_sound/proc/on_stop() - if(end_sound) - play(end_sound) +/* + output_atoms (list of atoms) The destination(s) for the sounds + + mid_sounds (list or soundfile) Since this can be either a list or a single soundfile you can have random sounds. May contain further lists but must contain a soundfile at the end. + mid_length (num) The length to wait between playing mid_sounds + + start_sound (soundfile) Played before starting the mid_sounds loop + start_length (num) How long to wait before starting the main loop after playing start_sound + + end_sound (soundfile) The sound played after the main loop has concluded + + chance (num) Chance per loop to play a mid_sound + volume (num) Sound output volume + muted (bool) Private. Used to stop the sound loop. + max_loops (num) The max amount of loops to run for. + direct (bool) If true plays directly to provided atoms instead of from them + opacity_check (bool) If true, things behind walls/opaque things won't hear the sounds. + pref_check (type) If set to a /datum/client_preference type, will check if the hearer has that preference active before playing it to them. + exclusive (bool) If true, only one of this sound is allowed to play. Relies on if started is true or not. If true, it will not start another loop until it is false. +*/ +/datum/looping_sound + var/list/atom/output_atoms + var/mid_sounds + var/mid_length + var/start_sound + var/start_length + var/end_sound + var/chance + var/volume = 100 + var/max_loops + var/direct + var/vary + var/extra_range + var/opacity_check + var/pref_check + var/exclusive + + var/timerid + var/started + +/datum/looping_sound/New(list/_output_atoms=list(), start_immediately=FALSE, disable_direct=FALSE) + if(!mid_sounds) + WARNING("A looping sound datum was created without sounds to play.") + return + + output_atoms = _output_atoms + if(disable_direct) + direct = FALSE + + if(start_immediately) + start() + +/datum/looping_sound/Destroy() + stop() + output_atoms = null + return ..() + +/datum/looping_sound/proc/start(atom/add_thing, skip_start_sound = FALSE) + if(add_thing) + output_atoms |= add_thing + if(timerid) + return + if(skip_start_sound && (!exclusive && !started)) // Skip start sounds optionally, check if we're exclusive AND started already + sound_loop() + started = TRUE + return + if(exclusive && started) // Prevents a sound from starting multiple times + return // Don't start this loop. + on_start() + started = TRUE + +/datum/looping_sound/proc/stop(atom/remove_thing, skip_stop_sound = FALSE) + if(remove_thing) + output_atoms -= remove_thing + if(!timerid) + return + if(!skip_stop_sound) + on_stop() + deltimer(timerid) + timerid = null + started = FALSE + +/datum/looping_sound/proc/sound_loop(starttime) + if(max_loops && world.time >= starttime + mid_length * max_loops) + stop() + return + if(!chance || prob(chance)) + play(get_sound(starttime)) + if(!timerid) + timerid = addtimer(CALLBACK(src, PROC_REF(sound_loop), world.time), mid_length, TIMER_STOPPABLE | TIMER_LOOP) + +/datum/looping_sound/proc/play(soundfile) + var/list/atoms_cache = output_atoms + var/sound/S = sound(soundfile) + if(direct) + S.channel = SSsounds.random_available_channel() + S.volume = volume + for(var/i in 1 to atoms_cache.len) + var/atom/thing = atoms_cache[i] + if(direct) + if(ismob(thing)) + var/mob/M = thing + if(pref_check && !M.is_preference_enabled(pref_check)) + continue + SEND_SOUND(thing, S) + else + playsound(thing, S, volume, vary, extra_range, ignore_walls = !opacity_check, preference = pref_check) + +/datum/looping_sound/proc/get_sound(starttime, _mid_sounds) + if(!_mid_sounds) + . = mid_sounds + else + . = _mid_sounds + while(!isfile(.) && !isnull(.)) + . = pickweight(.) + +/datum/looping_sound/proc/on_start() + var/start_wait = 1 // On TG this is 0, however it needs to be 1 to work around an issue. + if(start_sound) + play(start_sound) + start_wait = start_length + addtimer(CALLBACK(src, PROC_REF(sound_loop)), start_wait) + +/datum/looping_sound/proc/on_stop() + if(end_sound) + play(end_sound) diff --git a/code/datums/looping_sounds/item_sounds.dm b/code/datums/looping_sounds/item_sounds.dm index 855f4c52130..be43a0b2594 100644 --- a/code/datums/looping_sounds/item_sounds.dm +++ b/code/datums/looping_sounds/item_sounds.dm @@ -1,45 +1,45 @@ -/datum/looping_sound/geiger - mid_sounds = list( - list('sound/items/geiger/low1.ogg'=1, 'sound/items/geiger/low2.ogg'=1, 'sound/items/geiger/low3.ogg'=1, 'sound/items/geiger/low4.ogg'=1), - list('sound/items/geiger/med1.ogg'=1, 'sound/items/geiger/med2.ogg'=1, 'sound/items/geiger/med3.ogg'=1, 'sound/items/geiger/med4.ogg'=1), - list('sound/items/geiger/high1.ogg'=1, 'sound/items/geiger/high2.ogg'=1, 'sound/items/geiger/high3.ogg'=1, 'sound/items/geiger/high4.ogg'=1), - list('sound/items/geiger/ext1.ogg'=1, 'sound/items/geiger/ext2.ogg'=1, 'sound/items/geiger/ext3.ogg'=1, 'sound/items/geiger/ext4.ogg'=1) - ) - mid_length = 1 SECOND - volume = 25 - var/last_radiation - -/datum/looping_sound/geiger/get_sound(starttime) - var/danger - switch(last_radiation) - if(0 to RAD_LEVEL_MODERATE) - danger = 1 - if(RAD_LEVEL_MODERATE to RAD_LEVEL_HIGH) - danger = 2 - if(RAD_LEVEL_HIGH to RAD_LEVEL_VERY_HIGH) - danger = 3 - if(RAD_LEVEL_VERY_HIGH to INFINITY) - danger = 4 - else - return null - return ..(starttime, mid_sounds[danger]) - -/datum/looping_sound/geiger/stop() - . = ..() - last_radiation = 0 - -/datum/looping_sound/small_motor - start_sound = 'sound/items/small_motor/motor_start_nopull.ogg' - start_length = 2 SECONDS - mid_sounds = list( - 'sound/items/small_motor/motor_idle.ogg', - 'sound/items/small_motor/motor_fast.ogg', - 'sound/items/small_motor/motor_faster.ogg' - ) - mid_length = 1.9 SECONDS //someone make this loop better please, i'm no good at sound. the clips should be 2 seconds exact but there's a gap if it's set to 2 - end_sound = 'sound/items/small_motor/motor_end.ogg' - var/speed = 1 - -/datum/looping_sound/small_motor/get_sound(starttime) - speed = clamp(speed, 1, 3) +/datum/looping_sound/geiger + mid_sounds = list( + list('sound/items/geiger/low1.ogg'=1, 'sound/items/geiger/low2.ogg'=1, 'sound/items/geiger/low3.ogg'=1, 'sound/items/geiger/low4.ogg'=1), + list('sound/items/geiger/med1.ogg'=1, 'sound/items/geiger/med2.ogg'=1, 'sound/items/geiger/med3.ogg'=1, 'sound/items/geiger/med4.ogg'=1), + list('sound/items/geiger/high1.ogg'=1, 'sound/items/geiger/high2.ogg'=1, 'sound/items/geiger/high3.ogg'=1, 'sound/items/geiger/high4.ogg'=1), + list('sound/items/geiger/ext1.ogg'=1, 'sound/items/geiger/ext2.ogg'=1, 'sound/items/geiger/ext3.ogg'=1, 'sound/items/geiger/ext4.ogg'=1) + ) + mid_length = 1 SECOND + volume = 25 + var/last_radiation + +/datum/looping_sound/geiger/get_sound(starttime) + var/danger + switch(last_radiation) + if(0 to RAD_LEVEL_MODERATE) + danger = 1 + if(RAD_LEVEL_MODERATE to RAD_LEVEL_HIGH) + danger = 2 + if(RAD_LEVEL_HIGH to RAD_LEVEL_VERY_HIGH) + danger = 3 + if(RAD_LEVEL_VERY_HIGH to INFINITY) + danger = 4 + else + return null + return ..(starttime, mid_sounds[danger]) + +/datum/looping_sound/geiger/stop() + . = ..() + last_radiation = 0 + +/datum/looping_sound/small_motor + start_sound = 'sound/items/small_motor/motor_start_nopull.ogg' + start_length = 2 SECONDS + mid_sounds = list( + 'sound/items/small_motor/motor_idle.ogg', + 'sound/items/small_motor/motor_fast.ogg', + 'sound/items/small_motor/motor_faster.ogg' + ) + mid_length = 1.9 SECONDS //someone make this loop better please, i'm no good at sound. the clips should be 2 seconds exact but there's a gap if it's set to 2 + end_sound = 'sound/items/small_motor/motor_end.ogg' + var/speed = 1 + +/datum/looping_sound/small_motor/get_sound(starttime) + speed = clamp(speed, 1, 3) return ..(starttime, mid_sounds[speed]) \ No newline at end of file diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm index d7011dd094a..8005d950163 100644 --- a/code/datums/looping_sounds/machinery_sounds.dm +++ b/code/datums/looping_sounds/machinery_sounds.dm @@ -1,117 +1,117 @@ -/datum/looping_sound/showering - start_sound = 'sound/machines/shower/shower_start.ogg' - start_length = 2 - mid_sounds = list('sound/machines/shower/shower_mid1.ogg'=1,'sound/machines/shower/shower_mid2.ogg'=1,'sound/machines/shower/shower_mid3.ogg'=1) - mid_length = 10 - end_sound = 'sound/machines/shower/shower_end.ogg' - volume = 15 - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/supermatter - mid_sounds = list('sound/machines/sm/loops/calm.ogg'=1) - mid_length = 60 - volume = 40 - extra_range = 10 - pref_check = /datum/client_preference/supermatter_hum - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/generator - start_sound = 'sound/machines/generator/generator_start.ogg' - start_length = 4 - mid_sounds = list('sound/machines/generator/generator_mid1.ogg'=1, 'sound/machines/generator/generator_mid2.ogg'=1, 'sound/machines/generator/generator_mid3.ogg'=1) - mid_length = 4 - end_sound = 'sound/machines/generator/generator_end.ogg' - volume = 40 - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - -/datum/looping_sound/deep_fryer - start_sound = 'sound/machines/kitchen/fryer/deep_fryer_immerse.ogg' //my immersions - start_length = 10 - mid_sounds = list('sound/machines/kitchen/fryer/deep_fryer_1.ogg' = 1, 'sound/machines/kitchen/fryer/deep_fryer_2.ogg' = 1) - mid_length = 2 - end_sound = 'sound/machines/kitchen/fryer/deep_fryer_emerge.ogg' - volume = 15 - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/microwave - start_sound = 'sound/machines/kitchen/microwave/microwave-start.ogg' - start_length = 10 - mid_sounds = list('sound/machines/kitchen/microwave/microwave-mid1.ogg'=10, 'sound/machines/kitchen/microwave/microwave-mid2.ogg'=1) - mid_length = 10 - end_sound = 'sound/machines/kitchen/microwave/microwave-end.ogg' - volume = 90 - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/oven - start_sound = 'sound/machines/kitchen/oven/oven-start.ogg' - start_length = 10 - mid_sounds = list('sound/machines/kitchen/oven/oven-mid1.ogg'=10) - mid_length = 40 - end_sound = 'sound/machines/kitchen/oven/oven-stop.ogg' - volume = 50 - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/grill - start_sound = 'sound/machines/kitchen/grill/grill-start.ogg' - start_length = 10 - mid_sounds = list('sound/machines/kitchen/grill/grill-mid1.ogg'=10) - mid_length = 40 - end_sound = 'sound/machines/kitchen/grill/grill-stop.ogg' - volume = 50 - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/mixer - start_sound = 'sound/machines/kitchen/mixer/mixer-start.ogg' - start_length = 10 - mid_sounds = list('sound/machines/kitchen/mixer/mixer-mid1.ogg'=10) - mid_length = 10 - end_sound = 'sound/machines/kitchen/mixer/mixer-stop.ogg' - volume = 50 - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/cerealmaker - start_sound = 'sound/machines/kitchen/cerealmaker/cerealmaker-start.ogg' - start_length = 10 - mid_sounds = list('sound/machines/kitchen/cerealmaker/cerealmaker-mid1.ogg'=10) - mid_length = 60 - end_sound = 'sound/machines/kitchen/cerealmaker/cerealmaker-stop.ogg' - volume = 50 - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/candymaker - start_sound = 'sound/machines/kitchen/candymaker/candymaker-start.ogg' - start_length = 10 - mid_sounds = list('sound/machines/kitchen/candymaker/candymaker-mid1.ogg'=10) - mid_length = 40 - end_sound = 'sound/machines/kitchen/candymaker/candymaker-stop.ogg' - volume = 20 - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/datum/looping_sound/air_pump - start_sound = 'sound/machines/air_pump/airpumpstart.ogg' - start_length = 10 - mid_sounds = list('sound/machines/air_pump/airpumpidle.ogg' = 1) - mid_length = 70 - end_sound = 'sound/machines/air_pump/airpumpshutdown.ogg' - volume = 15 - pref_check = /datum/client_preference/air_pump_noise - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/vehicle_engine - start_sound = 'sound/machines/vehicle/engine_start.ogg' - start_length = 2 - mid_sounds = list('sound/machines/vehicle/engine_mid.ogg'=1) - mid_length = 6 - end_sound = 'sound/machines/vehicle/engine_end.ogg' - volume = 20 +/datum/looping_sound/showering + start_sound = 'sound/machines/shower/shower_start.ogg' + start_length = 2 + mid_sounds = list('sound/machines/shower/shower_mid1.ogg'=1,'sound/machines/shower/shower_mid2.ogg'=1,'sound/machines/shower/shower_mid3.ogg'=1) + mid_length = 10 + end_sound = 'sound/machines/shower/shower_end.ogg' + volume = 15 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/supermatter + mid_sounds = list('sound/machines/sm/loops/calm.ogg'=1) + mid_length = 60 + volume = 40 + extra_range = 10 + pref_check = /datum/client_preference/supermatter_hum + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/generator + start_sound = 'sound/machines/generator/generator_start.ogg' + start_length = 4 + mid_sounds = list('sound/machines/generator/generator_mid1.ogg'=1, 'sound/machines/generator/generator_mid2.ogg'=1, 'sound/machines/generator/generator_mid3.ogg'=1) + mid_length = 4 + end_sound = 'sound/machines/generator/generator_end.ogg' + volume = 40 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +/datum/looping_sound/deep_fryer + start_sound = 'sound/machines/kitchen/fryer/deep_fryer_immerse.ogg' //my immersions + start_length = 10 + mid_sounds = list('sound/machines/kitchen/fryer/deep_fryer_1.ogg' = 1, 'sound/machines/kitchen/fryer/deep_fryer_2.ogg' = 1) + mid_length = 2 + end_sound = 'sound/machines/kitchen/fryer/deep_fryer_emerge.ogg' + volume = 15 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/microwave + start_sound = 'sound/machines/kitchen/microwave/microwave-start.ogg' + start_length = 10 + mid_sounds = list('sound/machines/kitchen/microwave/microwave-mid1.ogg'=10, 'sound/machines/kitchen/microwave/microwave-mid2.ogg'=1) + mid_length = 10 + end_sound = 'sound/machines/kitchen/microwave/microwave-end.ogg' + volume = 90 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/oven + start_sound = 'sound/machines/kitchen/oven/oven-start.ogg' + start_length = 10 + mid_sounds = list('sound/machines/kitchen/oven/oven-mid1.ogg'=10) + mid_length = 40 + end_sound = 'sound/machines/kitchen/oven/oven-stop.ogg' + volume = 50 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/grill + start_sound = 'sound/machines/kitchen/grill/grill-start.ogg' + start_length = 10 + mid_sounds = list('sound/machines/kitchen/grill/grill-mid1.ogg'=10) + mid_length = 40 + end_sound = 'sound/machines/kitchen/grill/grill-stop.ogg' + volume = 50 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/mixer + start_sound = 'sound/machines/kitchen/mixer/mixer-start.ogg' + start_length = 10 + mid_sounds = list('sound/machines/kitchen/mixer/mixer-mid1.ogg'=10) + mid_length = 10 + end_sound = 'sound/machines/kitchen/mixer/mixer-stop.ogg' + volume = 50 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/cerealmaker + start_sound = 'sound/machines/kitchen/cerealmaker/cerealmaker-start.ogg' + start_length = 10 + mid_sounds = list('sound/machines/kitchen/cerealmaker/cerealmaker-mid1.ogg'=10) + mid_length = 60 + end_sound = 'sound/machines/kitchen/cerealmaker/cerealmaker-stop.ogg' + volume = 50 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/candymaker + start_sound = 'sound/machines/kitchen/candymaker/candymaker-start.ogg' + start_length = 10 + mid_sounds = list('sound/machines/kitchen/candymaker/candymaker-mid1.ogg'=10) + mid_length = 40 + end_sound = 'sound/machines/kitchen/candymaker/candymaker-stop.ogg' + volume = 20 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/datum/looping_sound/air_pump + start_sound = 'sound/machines/air_pump/airpumpstart.ogg' + start_length = 10 + mid_sounds = list('sound/machines/air_pump/airpumpidle.ogg' = 1) + mid_length = 70 + end_sound = 'sound/machines/air_pump/airpumpshutdown.ogg' + volume = 15 + pref_check = /datum/client_preference/air_pump_noise + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/vehicle_engine + start_sound = 'sound/machines/vehicle/engine_start.ogg' + start_length = 2 + mid_sounds = list('sound/machines/vehicle/engine_mid.ogg'=1) + mid_length = 6 + end_sound = 'sound/machines/vehicle/engine_end.ogg' + volume = 20 diff --git a/code/datums/looping_sounds/sequence.dm b/code/datums/looping_sounds/sequence.dm index 0ad8c30b78c..f14b43184b1 100644 --- a/code/datums/looping_sounds/sequence.dm +++ b/code/datums/looping_sounds/sequence.dm @@ -1,175 +1,175 @@ -// These looping sounds work off of a sequence of things (usually letters or numbers) given to them. - -// Base sequencer type. -/datum/looping_sound/sequence - var/sequence = "The quick brown fox jumps over the lazy dog" // The string to iterate over. - var/position = 1 // Where we are inside the sequence. IE the index we're on for the above. - var/loop_sequence = TRUE // If it should loop the entire sequence upon reaching the end. Otherwise stop() is called. - var/repeat_sequnce_delay = 2 SECONDS // How long to wait when reaching the end, if the above var is true, in deciseconds. - var/next_iteration_delay = 0 - -/datum/looping_sound/sequence/vv_edit_var(var_name, var_value) - if(var_name == "sequence") - set_new_sequence(var_value) - return ..() - -/datum/looping_sound/sequence/proc/iterate_on_sequence() - var/data = get_data_from_position() - next_iteration_delay = process_data(data) - increment_position() - -/datum/looping_sound/sequence/proc/get_data_from_position() - return sequence[position] - -// Override to do something based on the input. -/datum/looping_sound/sequence/proc/process_data(input) - return - -// Changes the sequence, and sets the position back to the start. -/datum/looping_sound/sequence/proc/set_new_sequence(new_sequence) - sequence = new_sequence - reset_position() - -// Called to advance the position, and handle reaching the end if so. -/datum/looping_sound/sequence/proc/increment_position() - position++ - if(position > get_max_position()) - reached_end_of_sequence() - -/datum/looping_sound/sequence/proc/get_max_position() - return length(sequence) - -/datum/looping_sound/sequence/proc/reset_position() - position = 1 - -// Called when the sequence is finished being iterated over. -// If looping is on, the position will be reset, otherwise processing will stop. -/datum/looping_sound/sequence/proc/reached_end_of_sequence() - if(loop_sequence) - next_iteration_delay += repeat_sequnce_delay - reset_position() - else - stop() - -/datum/looping_sound/sequence/sound_loop(starttime) - iterate_on_sequence() - - timerid = addtimer(CALLBACK(src, PROC_REF(sound_loop), world.time), next_iteration_delay, TIMER_STOPPABLE) - -#define MORSE_DOT "*" // Yes this is an asterisk but its easier to see on a computer compared to a period. -#define MORSE_DASH "-" -#define MORSE_BASE_DELAY 1 // If you change this you will also need to change [dot|dash]_soundfile variables. - -// This implements an automatic conversion of text (the sequence) into audible morse code. -// This can be useful for flavor purposes. For 'real' usage its suggested to also display the sequence in text form, for the benefit of those without sound. -/datum/looping_sound/sequence/morse - // This is just to pass validation in the base type. - mid_sounds = list('sound/effects/tones/440_sine_01.ogg') - mid_length = 1 - opacity_check = TRUE // So we don't have to constantly hear it when out of sight. - - // Dots. - // In Morse Code, the dot's length is one unit. - var/dot_soundfile = 'sound/effects/tones/440_sine_01.ogg' // The sound file to play for a 'dot'. - var/dot_delay = MORSE_BASE_DELAY // How long the sound above plays for, in deciseconds. - - // Dashes. - // In Morse Code, a dash's length is equal to three units (or three dots). - var/dash_soundfile = 'sound/effects/tones/440_sine_03.ogg' // The sound file to play for a 'dash'. - var/dash_delay = MORSE_BASE_DELAY * 3 // Same as the dot delay, except for the dash sound. - - // Spaces. - // In Morse Code, a space's length is equal to one unit (or one dot). - var/spaces_between_sounds = MORSE_BASE_DELAY // How many spaces are between parts of the same letter. - var/spaces_between_letters = MORSE_BASE_DELAY * 3 // How many spaces are between different letters in the same word. - var/spaces_between_words = MORSE_BASE_DELAY * 7 // How many spaces are between different words. - - // Morse Alphabet. - // Note that it is case-insensative. 'A' and 'a' will make the same sounds. - // Unfortunately there isn't a nice way to implement procedure signs w/o the space inbetween the letters. - // Also some of the punctuation isn't super offical/widespread in real life but its the future so *shrug. - var/static/list/morse_alphabet = list( - "A" = list("*", "-"), - "B" = list("-", "*", "*", "*"), - "C" = list("-", "*", "-", "*"), - "D" = list("-", "*", "*"), - "E" = list("*"), - "F" = list("*", "*", "-", "*"), - "G" = list("-", "-", "*"), - "H" = list("*", "*", "*", "*"), - "I" = list("*", "*"), - "J" = list("*", "-", "-", "-"), - "K" = list("-", "*", "-"), - "L" = list("*", "-", "*", "*"), - "M" = list("*", "*"), - "N" = list("-", "*"), - "O" = list("-", "-", "-"), - "P" = list("*", "-", "-", "*"), - "Q" = list("-", "-", "*", "-"), - "R" = list("*", "-", "*"), - "S" = list("*", "*", "*"), - "T" = list("-"), - "U" = list("*", "*", "-"), - "V" = list("*", "*", "*", "-"), - "W" = list("*", "-", "-"), - "X" = list("-", "*", "*", "-"), - "Y" = list("-", "*", "-", "-"), - "Z" = list("-", "-", "*", "*"), - - "1" = list("*", "-", "-", "-", "-"), - "2" = list("*", "*", "-", "-", "-"), - "3" = list("*", "*", "*", "-", "-"), - "4" = list("*", "*", "*", "*", "-"), - "5" = list("*", "*", "*", "*", "*"), - "6" = list("-", "*", "*", "*", "*"), - "7" = list("-", "-", "*", "*", "*"), - "8" = list("-", "-", "-", "*", "*"), - "9" = list("-", "-", "-", "-", "*"), - "0" = list("-", "-", "-", "-", "-"), - - "." = list("*", "-", "*", "-", "*", "-"), - "," = list("-", "-", "*", "*", "-", "-"), - "?" = list("*", "*", "-", "-", "*", "*"), - "'" = list("*", "-", "-", "-", "-", "*"), - "!" = list("-", "*", "-", "*", "-", "-"), - "/" = list("-", "*", "*", "-", "*"), - "(" = list("-", "*", "-", "-", "*"), - ")" = list("-", "*", "-", "-", "*", "-"), - "&" = list("*", "-", "*", "*", "*"), - ":" = list("-", "-", "-", "*", "*", "*"), - ";" = list("-", "*", "-", "*", "-", "*"), - "=" = list("-", "*", "*", "*", "-"), - "+" = list("*", "-", "*", "-", "*"), - "-" = list("-", "*", "*", "*", "*", "-"), - "_" = list("*", "*", "-", "-", "*", "-"), - "\""= list("*", "-", "*", "*", "-", "*"), - "$" = list("*", "*", "*", "-", "*", "*", "-"), - "@" = list("*", "-", "-", "*", "-", "*"), - ) - - -/datum/looping_sound/sequence/morse/process_data(letter) - letter = uppertext(letter) // Make it case-insensative. - - // If it's whitespace, treat it as a (Morse) space. - if(letter == " ") - return spaces_between_words - - if(!(letter in morse_alphabet)) - CRASH("Encountered invalid character in Morse sequence \"[letter]\".") - - // So I heard you like sequences... - // Play a sequence of sounds while inside the current iteration of the outer sequence. - var/list/instructions = morse_alphabet[letter] - for(var/sound in instructions) - if(sound == MORSE_DOT) - play(dot_soundfile) - sleep(dot_delay) - else // It's a dash otherwise. - play(dash_soundfile) - sleep(dash_delay) - sleep(spaces_between_sounds) - return spaces_between_letters - -#undef MORSE_DOT -#undef MORSE_DASH +// These looping sounds work off of a sequence of things (usually letters or numbers) given to them. + +// Base sequencer type. +/datum/looping_sound/sequence + var/sequence = "The quick brown fox jumps over the lazy dog" // The string to iterate over. + var/position = 1 // Where we are inside the sequence. IE the index we're on for the above. + var/loop_sequence = TRUE // If it should loop the entire sequence upon reaching the end. Otherwise stop() is called. + var/repeat_sequnce_delay = 2 SECONDS // How long to wait when reaching the end, if the above var is true, in deciseconds. + var/next_iteration_delay = 0 + +/datum/looping_sound/sequence/vv_edit_var(var_name, var_value) + if(var_name == "sequence") + set_new_sequence(var_value) + return ..() + +/datum/looping_sound/sequence/proc/iterate_on_sequence() + var/data = get_data_from_position() + next_iteration_delay = process_data(data) + increment_position() + +/datum/looping_sound/sequence/proc/get_data_from_position() + return sequence[position] + +// Override to do something based on the input. +/datum/looping_sound/sequence/proc/process_data(input) + return + +// Changes the sequence, and sets the position back to the start. +/datum/looping_sound/sequence/proc/set_new_sequence(new_sequence) + sequence = new_sequence + reset_position() + +// Called to advance the position, and handle reaching the end if so. +/datum/looping_sound/sequence/proc/increment_position() + position++ + if(position > get_max_position()) + reached_end_of_sequence() + +/datum/looping_sound/sequence/proc/get_max_position() + return length(sequence) + +/datum/looping_sound/sequence/proc/reset_position() + position = 1 + +// Called when the sequence is finished being iterated over. +// If looping is on, the position will be reset, otherwise processing will stop. +/datum/looping_sound/sequence/proc/reached_end_of_sequence() + if(loop_sequence) + next_iteration_delay += repeat_sequnce_delay + reset_position() + else + stop() + +/datum/looping_sound/sequence/sound_loop(starttime) + iterate_on_sequence() + + timerid = addtimer(CALLBACK(src, PROC_REF(sound_loop), world.time), next_iteration_delay, TIMER_STOPPABLE) + +#define MORSE_DOT "*" // Yes this is an asterisk but its easier to see on a computer compared to a period. +#define MORSE_DASH "-" +#define MORSE_BASE_DELAY 1 // If you change this you will also need to change [dot|dash]_soundfile variables. + +// This implements an automatic conversion of text (the sequence) into audible morse code. +// This can be useful for flavor purposes. For 'real' usage its suggested to also display the sequence in text form, for the benefit of those without sound. +/datum/looping_sound/sequence/morse + // This is just to pass validation in the base type. + mid_sounds = list('sound/effects/tones/440_sine_01.ogg') + mid_length = 1 + opacity_check = TRUE // So we don't have to constantly hear it when out of sight. + + // Dots. + // In Morse Code, the dot's length is one unit. + var/dot_soundfile = 'sound/effects/tones/440_sine_01.ogg' // The sound file to play for a 'dot'. + var/dot_delay = MORSE_BASE_DELAY // How long the sound above plays for, in deciseconds. + + // Dashes. + // In Morse Code, a dash's length is equal to three units (or three dots). + var/dash_soundfile = 'sound/effects/tones/440_sine_03.ogg' // The sound file to play for a 'dash'. + var/dash_delay = MORSE_BASE_DELAY * 3 // Same as the dot delay, except for the dash sound. + + // Spaces. + // In Morse Code, a space's length is equal to one unit (or one dot). + var/spaces_between_sounds = MORSE_BASE_DELAY // How many spaces are between parts of the same letter. + var/spaces_between_letters = MORSE_BASE_DELAY * 3 // How many spaces are between different letters in the same word. + var/spaces_between_words = MORSE_BASE_DELAY * 7 // How many spaces are between different words. + + // Morse Alphabet. + // Note that it is case-insensative. 'A' and 'a' will make the same sounds. + // Unfortunately there isn't a nice way to implement procedure signs w/o the space inbetween the letters. + // Also some of the punctuation isn't super offical/widespread in real life but its the future so *shrug. + var/static/list/morse_alphabet = list( + "A" = list("*", "-"), + "B" = list("-", "*", "*", "*"), + "C" = list("-", "*", "-", "*"), + "D" = list("-", "*", "*"), + "E" = list("*"), + "F" = list("*", "*", "-", "*"), + "G" = list("-", "-", "*"), + "H" = list("*", "*", "*", "*"), + "I" = list("*", "*"), + "J" = list("*", "-", "-", "-"), + "K" = list("-", "*", "-"), + "L" = list("*", "-", "*", "*"), + "M" = list("*", "*"), + "N" = list("-", "*"), + "O" = list("-", "-", "-"), + "P" = list("*", "-", "-", "*"), + "Q" = list("-", "-", "*", "-"), + "R" = list("*", "-", "*"), + "S" = list("*", "*", "*"), + "T" = list("-"), + "U" = list("*", "*", "-"), + "V" = list("*", "*", "*", "-"), + "W" = list("*", "-", "-"), + "X" = list("-", "*", "*", "-"), + "Y" = list("-", "*", "-", "-"), + "Z" = list("-", "-", "*", "*"), + + "1" = list("*", "-", "-", "-", "-"), + "2" = list("*", "*", "-", "-", "-"), + "3" = list("*", "*", "*", "-", "-"), + "4" = list("*", "*", "*", "*", "-"), + "5" = list("*", "*", "*", "*", "*"), + "6" = list("-", "*", "*", "*", "*"), + "7" = list("-", "-", "*", "*", "*"), + "8" = list("-", "-", "-", "*", "*"), + "9" = list("-", "-", "-", "-", "*"), + "0" = list("-", "-", "-", "-", "-"), + + "." = list("*", "-", "*", "-", "*", "-"), + "," = list("-", "-", "*", "*", "-", "-"), + "?" = list("*", "*", "-", "-", "*", "*"), + "'" = list("*", "-", "-", "-", "-", "*"), + "!" = list("-", "*", "-", "*", "-", "-"), + "/" = list("-", "*", "*", "-", "*"), + "(" = list("-", "*", "-", "-", "*"), + ")" = list("-", "*", "-", "-", "*", "-"), + "&" = list("*", "-", "*", "*", "*"), + ":" = list("-", "-", "-", "*", "*", "*"), + ";" = list("-", "*", "-", "*", "-", "*"), + "=" = list("-", "*", "*", "*", "-"), + "+" = list("*", "-", "*", "-", "*"), + "-" = list("-", "*", "*", "*", "*", "-"), + "_" = list("*", "*", "-", "-", "*", "-"), + "\""= list("*", "-", "*", "*", "-", "*"), + "$" = list("*", "*", "*", "-", "*", "*", "-"), + "@" = list("*", "-", "-", "*", "-", "*"), + ) + + +/datum/looping_sound/sequence/morse/process_data(letter) + letter = uppertext(letter) // Make it case-insensative. + + // If it's whitespace, treat it as a (Morse) space. + if(letter == " ") + return spaces_between_words + + if(!(letter in morse_alphabet)) + CRASH("Encountered invalid character in Morse sequence \"[letter]\".") + + // So I heard you like sequences... + // Play a sequence of sounds while inside the current iteration of the outer sequence. + var/list/instructions = morse_alphabet[letter] + for(var/sound in instructions) + if(sound == MORSE_DOT) + play(dot_soundfile) + sleep(dot_delay) + else // It's a dash otherwise. + play(dash_soundfile) + sleep(dash_delay) + sleep(spaces_between_sounds) + return spaces_between_letters + +#undef MORSE_DOT +#undef MORSE_DASH diff --git a/code/datums/looping_sounds/weather_sounds.dm b/code/datums/looping_sounds/weather_sounds.dm index 67a97c71f1a..db8396f3a31 100644 --- a/code/datums/looping_sounds/weather_sounds.dm +++ b/code/datums/looping_sounds/weather_sounds.dm @@ -1,98 +1,98 @@ -/datum/looping_sound/weather - pref_check = /datum/client_preference/weather_sounds - -/datum/looping_sound/weather/outside_blizzard - mid_sounds = list( - 'sound/effects/weather/snowstorm/outside/active_mid1.ogg' = 1, - 'sound/effects/weather/snowstorm/outside/active_mid1.ogg' = 1, - 'sound/effects/weather/snowstorm/outside/active_mid1.ogg' = 1 - ) - mid_length = 8 SECONDS - start_sound = 'sound/effects/weather/snowstorm/outside/active_start.ogg' - start_length = 13 SECONDS - end_sound = 'sound/effects/weather/snowstorm/outside/active_end.ogg' - volume = 40 - -/datum/looping_sound/weather/inside_blizzard - mid_sounds = list( - 'sound/effects/weather/snowstorm/inside/active_mid1.ogg' = 1, - 'sound/effects/weather/snowstorm/inside/active_mid2.ogg' = 1, - 'sound/effects/weather/snowstorm/inside/active_mid3.ogg' = 1 - ) - mid_length = 8 SECONDS - start_sound = 'sound/effects/weather/snowstorm/inside/active_start.ogg' - start_length = 13 SECONDS - end_sound = 'sound/effects/weather/snowstorm/inside/active_end.ogg' - volume = 20 - -/datum/looping_sound/weather/outside_snow - mid_sounds = list( - 'sound/effects/weather/snowstorm/outside/weak_mid1.ogg' = 1, - 'sound/effects/weather/snowstorm/outside/weak_mid2.ogg' = 1, - 'sound/effects/weather/snowstorm/outside/weak_mid3.ogg' = 1 - ) - mid_length = 8 SECONDS - start_sound = 'sound/effects/weather/snowstorm/outside/weak_start.ogg' - start_length = 13 SECONDS - end_sound = 'sound/effects/weather/snowstorm/outside/weak_end.ogg' - volume = 20 - -/datum/looping_sound/weather/inside_snow - mid_sounds = list( - 'sound/effects/weather/snowstorm/inside/weak_mid1.ogg' = 1, - 'sound/effects/weather/snowstorm/inside/weak_mid2.ogg' = 1, - 'sound/effects/weather/snowstorm/inside/weak_mid3.ogg' = 1 - ) - mid_length = 8 SECONDS - start_sound = 'sound/effects/weather/snowstorm/inside/weak_start.ogg' - start_length = 13 SECONDS - end_sound = 'sound/effects/weather/snowstorm/inside/weak_end.ogg' - volume = 10 - -/datum/looping_sound/weather/wind - mid_sounds = list( - 'sound/effects/weather/wind/wind_2_1.ogg' = 1, - 'sound/effects/weather/wind/wind_2_2.ogg' = 1, - 'sound/effects/weather/wind/wind_3_1.ogg' = 1, - 'sound/effects/weather/wind/wind_4_1.ogg' = 1, - 'sound/effects/weather/wind/wind_4_2.ogg' = 1, - 'sound/effects/weather/wind/wind_5_1.ogg' = 1 - ) - mid_length = 10 SECONDS // The lengths for the files vary, but the longest is ten seconds, so this will make it sound like intermittent wind. - volume = 45 - -// Don't have special sounds so we just make it quieter indoors. -/datum/looping_sound/weather/wind/indoors - volume = 25 - -/datum/looping_sound/weather/wind/gentle - volume = 15 - -/datum/looping_sound/weather/wind/gentle/indoors - volume = 5 - -/datum/looping_sound/weather/rain - mid_sounds = list( - 'sound/effects/weather/acidrain_mid.ogg' = 1 - ) - mid_length = 15 SECONDS - start_sound = 'sound/effects/weather/acidrain_start.ogg' - start_length = 13 SECONDS - end_sound = 'sound/effects/weather/acidrain_end.ogg' - volume = 20 - -/datum/looping_sound/weather/rain/heavy - volume = 40 - -/datum/looping_sound/weather/rain/indoors - mid_sounds = list( - 'sound/effects/weather/indoorrain_mid.ogg' = 1 - ) - mid_length = 15 SECONDS - start_sound = 'sound/effects/weather/indoorrain_start.ogg' - start_length = 13 SECONDS - end_sound = 'sound/effects/weather/indoorrain_end.ogg' - volume = 20 //Sound is already quieter in file - -/datum/looping_sound/weather/rain/indoors/heavy +/datum/looping_sound/weather + pref_check = /datum/client_preference/weather_sounds + +/datum/looping_sound/weather/outside_blizzard + mid_sounds = list( + 'sound/effects/weather/snowstorm/outside/active_mid1.ogg' = 1, + 'sound/effects/weather/snowstorm/outside/active_mid1.ogg' = 1, + 'sound/effects/weather/snowstorm/outside/active_mid1.ogg' = 1 + ) + mid_length = 8 SECONDS + start_sound = 'sound/effects/weather/snowstorm/outside/active_start.ogg' + start_length = 13 SECONDS + end_sound = 'sound/effects/weather/snowstorm/outside/active_end.ogg' + volume = 40 + +/datum/looping_sound/weather/inside_blizzard + mid_sounds = list( + 'sound/effects/weather/snowstorm/inside/active_mid1.ogg' = 1, + 'sound/effects/weather/snowstorm/inside/active_mid2.ogg' = 1, + 'sound/effects/weather/snowstorm/inside/active_mid3.ogg' = 1 + ) + mid_length = 8 SECONDS + start_sound = 'sound/effects/weather/snowstorm/inside/active_start.ogg' + start_length = 13 SECONDS + end_sound = 'sound/effects/weather/snowstorm/inside/active_end.ogg' + volume = 20 + +/datum/looping_sound/weather/outside_snow + mid_sounds = list( + 'sound/effects/weather/snowstorm/outside/weak_mid1.ogg' = 1, + 'sound/effects/weather/snowstorm/outside/weak_mid2.ogg' = 1, + 'sound/effects/weather/snowstorm/outside/weak_mid3.ogg' = 1 + ) + mid_length = 8 SECONDS + start_sound = 'sound/effects/weather/snowstorm/outside/weak_start.ogg' + start_length = 13 SECONDS + end_sound = 'sound/effects/weather/snowstorm/outside/weak_end.ogg' + volume = 20 + +/datum/looping_sound/weather/inside_snow + mid_sounds = list( + 'sound/effects/weather/snowstorm/inside/weak_mid1.ogg' = 1, + 'sound/effects/weather/snowstorm/inside/weak_mid2.ogg' = 1, + 'sound/effects/weather/snowstorm/inside/weak_mid3.ogg' = 1 + ) + mid_length = 8 SECONDS + start_sound = 'sound/effects/weather/snowstorm/inside/weak_start.ogg' + start_length = 13 SECONDS + end_sound = 'sound/effects/weather/snowstorm/inside/weak_end.ogg' + volume = 10 + +/datum/looping_sound/weather/wind + mid_sounds = list( + 'sound/effects/weather/wind/wind_2_1.ogg' = 1, + 'sound/effects/weather/wind/wind_2_2.ogg' = 1, + 'sound/effects/weather/wind/wind_3_1.ogg' = 1, + 'sound/effects/weather/wind/wind_4_1.ogg' = 1, + 'sound/effects/weather/wind/wind_4_2.ogg' = 1, + 'sound/effects/weather/wind/wind_5_1.ogg' = 1 + ) + mid_length = 10 SECONDS // The lengths for the files vary, but the longest is ten seconds, so this will make it sound like intermittent wind. + volume = 45 + +// Don't have special sounds so we just make it quieter indoors. +/datum/looping_sound/weather/wind/indoors + volume = 25 + +/datum/looping_sound/weather/wind/gentle + volume = 15 + +/datum/looping_sound/weather/wind/gentle/indoors + volume = 5 + +/datum/looping_sound/weather/rain + mid_sounds = list( + 'sound/effects/weather/acidrain_mid.ogg' = 1 + ) + mid_length = 15 SECONDS + start_sound = 'sound/effects/weather/acidrain_start.ogg' + start_length = 13 SECONDS + end_sound = 'sound/effects/weather/acidrain_end.ogg' + volume = 20 + +/datum/looping_sound/weather/rain/heavy + volume = 40 + +/datum/looping_sound/weather/rain/indoors + mid_sounds = list( + 'sound/effects/weather/indoorrain_mid.ogg' = 1 + ) + mid_length = 15 SECONDS + start_sound = 'sound/effects/weather/indoorrain_start.ogg' + start_length = 13 SECONDS + end_sound = 'sound/effects/weather/indoorrain_end.ogg' + volume = 20 //Sound is already quieter in file + +/datum/looping_sound/weather/rain/indoors/heavy volume = 40 \ No newline at end of file diff --git a/code/datums/managed_browsers/_managed_browser.dm b/code/datums/managed_browsers/_managed_browser.dm index 6c702ddbdd1..718fa0570cf 100644 --- a/code/datums/managed_browsers/_managed_browser.dm +++ b/code/datums/managed_browsers/_managed_browser.dm @@ -1,52 +1,52 @@ -GLOBAL_VAR(managed_browser_id_ticker) - -// This holds information on managing a /datum/browser object. -// Managing can include things like persisting the state of specific information inside of this object, receiving Topic() calls, or deleting itself when the window is closed. -// This is useful for browser windows to be able to stand 'on their own' instead of being tied to something in the game world, like an object or mob. -/datum/managed_browser - var/client/my_client = null - var/browser_id = null - var/base_browser_id = null - - var/title = null - var/size_x = 200 - var/size_y = 400 - - var/display_when_created = TRUE - -/datum/managed_browser/New(client/new_client) - if(!new_client) - stack_trace("Managed browser object was not given a client.") - return - if(!base_browser_id) - stack_trace("Managed browser object does not have a base browser id defined in its type.") - return - - my_client = new_client - browser_id = "[base_browser_id]-[GLOB.managed_browser_id_ticker++]" - - if(display_when_created) - display() - -/datum/managed_browser/Destroy() - my_client = null - return ..() - -// Override if you want to have the browser title change conditionally. -// Otherwise it's easier to just change the title variable directly. -/datum/managed_browser/proc/get_title() - return title - -// Override to display the html information. -// It is suggested to build it with a list, and use list.Join() at the end. -// This helps prevent excessive concatination, which helps preserves BYOND's string tree from becoming a laggy mess. -/datum/managed_browser/proc/get_html() - return - -/datum/managed_browser/proc/display() - interact(get_html(), get_title(), my_client) - -/datum/managed_browser/proc/interact(html, title, client/C) - var/datum/browser/popup = new(C.mob, browser_id, title, size_x, size_y, src) - popup.set_content(html) +GLOBAL_VAR(managed_browser_id_ticker) + +// This holds information on managing a /datum/browser object. +// Managing can include things like persisting the state of specific information inside of this object, receiving Topic() calls, or deleting itself when the window is closed. +// This is useful for browser windows to be able to stand 'on their own' instead of being tied to something in the game world, like an object or mob. +/datum/managed_browser + var/client/my_client = null + var/browser_id = null + var/base_browser_id = null + + var/title = null + var/size_x = 200 + var/size_y = 400 + + var/display_when_created = TRUE + +/datum/managed_browser/New(client/new_client) + if(!new_client) + stack_trace("Managed browser object was not given a client.") + return + if(!base_browser_id) + stack_trace("Managed browser object does not have a base browser id defined in its type.") + return + + my_client = new_client + browser_id = "[base_browser_id]-[GLOB.managed_browser_id_ticker++]" + + if(display_when_created) + display() + +/datum/managed_browser/Destroy() + my_client = null + return ..() + +// Override if you want to have the browser title change conditionally. +// Otherwise it's easier to just change the title variable directly. +/datum/managed_browser/proc/get_title() + return title + +// Override to display the html information. +// It is suggested to build it with a list, and use list.Join() at the end. +// This helps prevent excessive concatination, which helps preserves BYOND's string tree from becoming a laggy mess. +/datum/managed_browser/proc/get_html() + return + +/datum/managed_browser/proc/display() + interact(get_html(), get_title(), my_client) + +/datum/managed_browser/proc/interact(html, title, client/C) + var/datum/browser/popup = new(C.mob, browser_id, title, size_x, size_y, src) + popup.set_content(html) popup.open() \ No newline at end of file diff --git a/code/datums/managed_browsers/feedback_form.dm b/code/datums/managed_browsers/feedback_form.dm index c6160ffc598..cd7b3a8b207 100644 --- a/code/datums/managed_browsers/feedback_form.dm +++ b/code/datums/managed_browsers/feedback_form.dm @@ -1,147 +1,147 @@ -/client - var/datum/managed_browser/feedback_form/feedback_form = null - -/client/can_vv_get(var_name) - return var_name != NAMEOF(src, feedback_form) // No snooping. - -GENERAL_PROTECT_DATUM(/datum/managed_browser/feedback_form) - -// A fairly simple object to hold information about a player's feedback as it's being written. -// Having this be it's own object instead of being baked into /mob/new_player allows for it to be used -// from other places than just the lobby, and makes it a lot harder for people with dev powers to be naughty with it using VV/proccall. -/datum/managed_browser/feedback_form - base_browser_id = "feedback_form" - title = "Server Feedback" - size_x = 480 - size_y = 520 - var/feedback_topic = null - var/feedback_body = null - var/feedback_hide_author = FALSE - -/datum/managed_browser/feedback_form/New(client/new_client) - feedback_topic = config.sqlite_feedback_topics[1] - ..(new_client) - -/datum/managed_browser/feedback_form/Destroy() - if(my_client) - my_client.feedback_form = null - return ..() - -// Privacy option is allowed if both the config allows it, and the pepper file exists and isn't blank. -/datum/managed_browser/feedback_form/proc/can_be_private() - return config.sqlite_feedback_privacy && SSsqlite.get_feedback_pepper() - -/datum/managed_browser/feedback_form/display() - if(!my_client) - return - if(!SSsqlite.can_submit_feedback(my_client)) - return - ..() - -// Builds the window for players to review their feedback. -/datum/managed_browser/feedback_form/get_html() - var/list/dat = list("") - dat += "
    " - dat += "" - dat += "Here, you can write some feedback for the server.
    " - dat += "Note that HTML is NOT supported!
    " - dat += "Click the edit button to begin writing.
    " - - dat += "Your feedback is currently [length(feedback_body)]/[MAX_FEEDBACK_LENGTH] letters long." - dat += "
    " - dat += "
    " - - dat += "

    Preview

    " - - dat += "Author: " - - if(can_be_private()) - if(!feedback_hide_author) - dat += "[my_client.ckey] " - dat += span("linkOn", "Visible") - dat += " | " - dat += href(src, list("feedback_hide_author" = 1), "Hashed") - else - dat += "[md5(ckey(lowertext(my_client.ckey + SSsqlite.get_feedback_pepper())))] " - dat += href(src, list("feedback_hide_author" = 0), "Visible") - dat += " | " - dat += span("linkOn", "Hashed") - else - dat += my_client.ckey - dat += "
    " - - if(config.sqlite_feedback_topics.len > 1) - dat += "Topic: [href(src, list("feedback_choose_topic" = 1), feedback_topic)]
    " - else - dat += "Topic: [config.sqlite_feedback_topics[1]]
    " - - dat += "
    " - if(feedback_body) - dat += replacetext(feedback_body, "\n", "
    ") // So newlines will look like they work in the preview. - else - dat += "\[Feedback goes here...\]" - dat += "
    " - dat += href(src, list("feedback_edit_body" = 1), "Edit") - dat += "
    " - - if(config.sqlite_feedback_cooldown) - dat += "Please note that you will have to wait [config.sqlite_feedback_cooldown] day\s before \ - being able to write more feedback after submitting.
    " - - dat += href(src, list("feedback_submit" = 1), "Submit") - dat += "" - return dat.Join() - -/datum/managed_browser/feedback_form/Topic(href, href_list[]) - if(!my_client) - return FALSE - - if(href_list["feedback_edit_body"]) - // This is deliberately not sanitized here, and is instead checked when hitting the submission button, - // as we want to give the user a chance to fix it without needing to rewrite the whole thing. - feedback_body = tgui_input_text(my_client, "Please write your feedback here.", "Feedback Body", feedback_body, multiline = TRUE, prevent_enter = TRUE) - display() // Refresh the window with new information. - return - - if(href_list["feedback_hide_author"]) - if(!can_be_private()) - feedback_hide_author = FALSE - else - feedback_hide_author = text2num(href_list["feedback_hide_author"]) - display() - return - - if(href_list["feedback_choose_topic"]) - feedback_topic = tgui_input_list(my_client, "Choose the topic you want to submit your feedback under.", "Feedback Topic", config.sqlite_feedback_topics) - display() - return - - if(href_list["feedback_submit"]) - // Do some last minute validation, and tell the user if something goes wrong, - // so we don't wipe out their ten thousand page essay due to having a few too many characters. - if(length(feedback_body) > MAX_FEEDBACK_LENGTH) - to_chat(my_client, span("warning", "Your feedback is too long, at [length(feedback_body)] characters, where as the \ - limit is [MAX_FEEDBACK_LENGTH]. Please shorten it and try again.")) - return - - var/text = sanitize(feedback_body, max_length = 0, encode = TRUE, trim = FALSE, extra = FALSE) - if(!text) // No text, or it was super invalid. - to_chat(my_client, span("warning", "It appears you didn't write anything, or it was invalid.")) - return - - if(tgui_alert(my_client, "Are you sure you want to submit your feedback?", "Confirm Submission", list("No", "Yes")) == "Yes") - var/author_text = my_client.ckey - if(can_be_private() && feedback_hide_author) - author_text = md5(my_client.ckey + SSsqlite.get_feedback_pepper()) - - var/success = SSsqlite.insert_feedback(author = author_text, topic = feedback_topic, content = feedback_body, sqlite_object = SSsqlite.sqlite_db) - if(!success) - to_chat(my_client, span("warning", "Something went wrong while inserting your feedback into the database. Please try again. \ - If this happens again, you should contact a developer.")) - return - - my_client.mob << browse(null, "window=[browser_id]") // Closes the window. - if(istype(my_client.mob, /mob/new_player)) - var/mob/new_player/NP = my_client.mob - NP.new_player_panel_proc() // So the feedback button goes away, if the user gets put on cooldown. +/client + var/datum/managed_browser/feedback_form/feedback_form = null + +/client/can_vv_get(var_name) + return var_name != NAMEOF(src, feedback_form) // No snooping. + +GENERAL_PROTECT_DATUM(/datum/managed_browser/feedback_form) + +// A fairly simple object to hold information about a player's feedback as it's being written. +// Having this be it's own object instead of being baked into /mob/new_player allows for it to be used +// from other places than just the lobby, and makes it a lot harder for people with dev powers to be naughty with it using VV/proccall. +/datum/managed_browser/feedback_form + base_browser_id = "feedback_form" + title = "Server Feedback" + size_x = 480 + size_y = 520 + var/feedback_topic = null + var/feedback_body = null + var/feedback_hide_author = FALSE + +/datum/managed_browser/feedback_form/New(client/new_client) + feedback_topic = config.sqlite_feedback_topics[1] + ..(new_client) + +/datum/managed_browser/feedback_form/Destroy() + if(my_client) + my_client.feedback_form = null + return ..() + +// Privacy option is allowed if both the config allows it, and the pepper file exists and isn't blank. +/datum/managed_browser/feedback_form/proc/can_be_private() + return config.sqlite_feedback_privacy && SSsqlite.get_feedback_pepper() + +/datum/managed_browser/feedback_form/display() + if(!my_client) + return + if(!SSsqlite.can_submit_feedback(my_client)) + return + ..() + +// Builds the window for players to review their feedback. +/datum/managed_browser/feedback_form/get_html() + var/list/dat = list("") + dat += "
    " + dat += "" + dat += "Here, you can write some feedback for the server.
    " + dat += "Note that HTML is NOT supported!
    " + dat += "Click the edit button to begin writing.
    " + + dat += "Your feedback is currently [length(feedback_body)]/[MAX_FEEDBACK_LENGTH] letters long." + dat += "
    " + dat += "
    " + + dat += "

    Preview

    " + + dat += "Author: " + + if(can_be_private()) + if(!feedback_hide_author) + dat += "[my_client.ckey] " + dat += span("linkOn", "Visible") + dat += " | " + dat += href(src, list("feedback_hide_author" = 1), "Hashed") + else + dat += "[md5(ckey(lowertext(my_client.ckey + SSsqlite.get_feedback_pepper())))] " + dat += href(src, list("feedback_hide_author" = 0), "Visible") + dat += " | " + dat += span("linkOn", "Hashed") + else + dat += my_client.ckey + dat += "
    " + + if(config.sqlite_feedback_topics.len > 1) + dat += "Topic: [href(src, list("feedback_choose_topic" = 1), feedback_topic)]
    " + else + dat += "Topic: [config.sqlite_feedback_topics[1]]
    " + + dat += "
    " + if(feedback_body) + dat += replacetext(feedback_body, "\n", "
    ") // So newlines will look like they work in the preview. + else + dat += "\[Feedback goes here...\]" + dat += "
    " + dat += href(src, list("feedback_edit_body" = 1), "Edit") + dat += "
    " + + if(config.sqlite_feedback_cooldown) + dat += "Please note that you will have to wait [config.sqlite_feedback_cooldown] day\s before \ + being able to write more feedback after submitting.
    " + + dat += href(src, list("feedback_submit" = 1), "Submit") + dat += "" + return dat.Join() + +/datum/managed_browser/feedback_form/Topic(href, href_list[]) + if(!my_client) + return FALSE + + if(href_list["feedback_edit_body"]) + // This is deliberately not sanitized here, and is instead checked when hitting the submission button, + // as we want to give the user a chance to fix it without needing to rewrite the whole thing. + feedback_body = tgui_input_text(my_client, "Please write your feedback here.", "Feedback Body", feedback_body, multiline = TRUE, prevent_enter = TRUE) + display() // Refresh the window with new information. + return + + if(href_list["feedback_hide_author"]) + if(!can_be_private()) + feedback_hide_author = FALSE + else + feedback_hide_author = text2num(href_list["feedback_hide_author"]) + display() + return + + if(href_list["feedback_choose_topic"]) + feedback_topic = tgui_input_list(my_client, "Choose the topic you want to submit your feedback under.", "Feedback Topic", config.sqlite_feedback_topics) + display() + return + + if(href_list["feedback_submit"]) + // Do some last minute validation, and tell the user if something goes wrong, + // so we don't wipe out their ten thousand page essay due to having a few too many characters. + if(length(feedback_body) > MAX_FEEDBACK_LENGTH) + to_chat(my_client, span("warning", "Your feedback is too long, at [length(feedback_body)] characters, where as the \ + limit is [MAX_FEEDBACK_LENGTH]. Please shorten it and try again.")) + return + + var/text = sanitize(feedback_body, max_length = 0, encode = TRUE, trim = FALSE, extra = FALSE) + if(!text) // No text, or it was super invalid. + to_chat(my_client, span("warning", "It appears you didn't write anything, or it was invalid.")) + return + + if(tgui_alert(my_client, "Are you sure you want to submit your feedback?", "Confirm Submission", list("No", "Yes")) == "Yes") + var/author_text = my_client.ckey + if(can_be_private() && feedback_hide_author) + author_text = md5(my_client.ckey + SSsqlite.get_feedback_pepper()) + + var/success = SSsqlite.insert_feedback(author = author_text, topic = feedback_topic, content = feedback_body, sqlite_object = SSsqlite.sqlite_db) + if(!success) + to_chat(my_client, span("warning", "Something went wrong while inserting your feedback into the database. Please try again. \ + If this happens again, you should contact a developer.")) + return + + my_client.mob << browse(null, "window=[browser_id]") // Closes the window. + if(istype(my_client.mob, /mob/new_player)) + var/mob/new_player/NP = my_client.mob + NP.new_player_panel_proc() // So the feedback button goes away, if the user gets put on cooldown. qdel(src) \ No newline at end of file diff --git a/code/datums/managed_browsers/feedback_viewer.dm b/code/datums/managed_browsers/feedback_viewer.dm index 02360363384..59fbc679c4e 100644 --- a/code/datums/managed_browsers/feedback_viewer.dm +++ b/code/datums/managed_browsers/feedback_viewer.dm @@ -1,162 +1,162 @@ -/client - var/datum/managed_browser/feedback_viewer/feedback_viewer = null - -/datum/admins/proc/view_feedback() - set category = "Admin" - set name = "View Feedback" - set desc = "Open the Feedback Viewer" - - if(!check_rights(R_ADMIN|R_DEBUG|R_EVENT)) - return - - if(usr.client.feedback_viewer) - usr.client.feedback_viewer.display() - else - usr.client.feedback_viewer = new(usr.client) - -// This object holds the code to run the admin feedback viewer. -/datum/managed_browser/feedback_viewer - base_browser_id = "feedback_viewer" - title = "Submitted Feedback" - size_x = 900 - size_y = 500 - var/database/query/last_query = null - -/datum/managed_browser/feedback_viewer/New(client/new_client) - if(!check_rights(R_ADMIN|R_DEBUG|R_EVENT, new_client)) // Just in case someone figures out a way to spawn this as non-staff. - message_admins("[new_client] tried to view feedback with insufficent permissions.") - qdel(src) - - ..() - -/datum/managed_browser/feedback_viewer/Destroy() - if(my_client) - my_client.feedback_viewer = null - return ..() - -/datum/managed_browser/feedback_viewer/proc/feedback_filter(row_name, thing_to_find, exact = FALSE) - var/database/query/query = null - if(exact) // Useful for ID searches, so searching for 'id 10' doesn't also get 'id 101'. - query = new({" - SELECT * - FROM [SQLITE_TABLE_FEEDBACK] - WHERE [row_name] == ? - ORDER BY [SQLITE_FEEDBACK_COLUMN_ID] - DESC LIMIT 50; - "}, - thing_to_find - ) - - else - // Wrap the thing in %s so LIKE will work. - thing_to_find = "%[thing_to_find]%" - query = new({" - SELECT * - FROM [SQLITE_TABLE_FEEDBACK] - WHERE [row_name] LIKE ? - ORDER BY [SQLITE_FEEDBACK_COLUMN_ID] - DESC LIMIT 50; - "}, - thing_to_find - ) - query.Execute(SSsqlite.sqlite_db) - SSsqlite.sqlite_check_for_errors(query, "Admin Feedback Viewer - Filter by [row_name] to find [thing_to_find]") - return query - -// Builds the window for players to review their feedback. -/datum/managed_browser/feedback_viewer/get_html() - var/list/dat = list("") - if(!last_query) // If no query was done before, just show the most recent feedbacks. - var/database/query/query = new({" - SELECT * - FROM [SQLITE_TABLE_FEEDBACK] - ORDER BY [SQLITE_FEEDBACK_COLUMN_ID] - DESC LIMIT 50; - "} - ) - query.Execute(SSsqlite.sqlite_db) - SSsqlite.sqlite_check_for_errors(query, "Admin Feedback Viewer") - last_query = query - - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - - while(last_query.NextRow()) - var/list/row_data = last_query.GetRowData() - dat += "" - dat += "" - dat += "" - dat += "" // TODO: Color this to make hashed keys more distinguishable. - var/text = row_data[SQLITE_FEEDBACK_COLUMN_CONTENT] - if(length(text) > 512) - text = href(src, list( - "show_full_feedback" = 1, - "feedback_author" = row_data[SQLITE_FEEDBACK_COLUMN_AUTHOR], - "feedback_content" = row_data[SQLITE_FEEDBACK_COLUMN_CONTENT] - ), "[copytext(text, 1, 64)]... ([length(text)])") - else - text = replacetext(text, "\n", "
    ") - dat += "" - dat += "" - dat += "" - dat += "
    [href(src, list("filter_id" = 1), "ID")][href(src, list("filter_topic" = 1), "Topic")][href(src, list("filter_author" = 1), "Author")][href(src, list("filter_content" = 1), "Content")][href(src, list("filter_datetime" = 1), "Datetime")]
    [row_data[SQLITE_FEEDBACK_COLUMN_ID]][row_data[SQLITE_FEEDBACK_COLUMN_TOPIC]][row_data[SQLITE_FEEDBACK_COLUMN_AUTHOR]][text][row_data[SQLITE_FEEDBACK_COLUMN_DATETIME]]
    " - - dat += "" - return dat.Join() - -// Used to show the full version of feedback in a seperate window. -/datum/managed_browser/feedback_viewer/proc/display_big_feedback(author, text) - var/list/dat = list("") - dat += replacetext(text, "\n", "
    ") - - var/datum/browser/popup = new(my_client.mob, "feedback_big", "[author]'s Feedback", 480, 520, src) - popup.set_content(dat.Join()) - popup.open() - - -/datum/managed_browser/feedback_viewer/Topic(href, href_list[]) - if(!my_client) - return FALSE - - if(href_list["close"]) // To avoid refreshing. - return - - if(href_list["show_full_feedback"]) - display_big_feedback(href_list["feedback_author"], href_list["feedback_content"]) - return - - if(href_list["filter_id"]) - var/id_to_search = tgui_input_number(my_client, "Write feedback ID here.", "Filter by ID", null) - if(id_to_search) - last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_ID, id_to_search, TRUE) - - if(href_list["filter_author"]) - var/author_to_search = tgui_input_text(my_client, "Write desired key or hash here. Partial keys/hashes are allowed.", "Filter by Author", null) - if(author_to_search) - last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_AUTHOR, author_to_search) - - if(href_list["filter_topic"]) - var/topic_to_search = tgui_input_text(my_client, "Write desired topic here. Partial topics are allowed. \ - \nThe current topics in the config are [english_list(config.sqlite_feedback_topics)].", "Filter by Topic", null) - if(topic_to_search) - last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_TOPIC, topic_to_search) - - if(href_list["filter_content"]) - var/content_to_search = tgui_input_text(my_client, "Write desired content to find here. Partial matches are allowed.", "Filter by Content", null, multiline = TRUE) - if(content_to_search) - last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_CONTENT, content_to_search) - - if(href_list["filter_datetime"]) - var/datetime_to_search = tgui_input_text(my_client, "Write desired datetime. Partial matches are allowed.\n\ - Format is 'YYYY-MM-DD HH:MM:SS'.", "Filter by Datetime", null) - if(datetime_to_search) - last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_DATETIME, datetime_to_search) - - // Refresh. +/client + var/datum/managed_browser/feedback_viewer/feedback_viewer = null + +/datum/admins/proc/view_feedback() + set category = "Admin" + set name = "View Feedback" + set desc = "Open the Feedback Viewer" + + if(!check_rights(R_ADMIN|R_DEBUG|R_EVENT)) + return + + if(usr.client.feedback_viewer) + usr.client.feedback_viewer.display() + else + usr.client.feedback_viewer = new(usr.client) + +// This object holds the code to run the admin feedback viewer. +/datum/managed_browser/feedback_viewer + base_browser_id = "feedback_viewer" + title = "Submitted Feedback" + size_x = 900 + size_y = 500 + var/database/query/last_query = null + +/datum/managed_browser/feedback_viewer/New(client/new_client) + if(!check_rights(R_ADMIN|R_DEBUG|R_EVENT, new_client)) // Just in case someone figures out a way to spawn this as non-staff. + message_admins("[new_client] tried to view feedback with insufficent permissions.") + qdel(src) + + ..() + +/datum/managed_browser/feedback_viewer/Destroy() + if(my_client) + my_client.feedback_viewer = null + return ..() + +/datum/managed_browser/feedback_viewer/proc/feedback_filter(row_name, thing_to_find, exact = FALSE) + var/database/query/query = null + if(exact) // Useful for ID searches, so searching for 'id 10' doesn't also get 'id 101'. + query = new({" + SELECT * + FROM [SQLITE_TABLE_FEEDBACK] + WHERE [row_name] == ? + ORDER BY [SQLITE_FEEDBACK_COLUMN_ID] + DESC LIMIT 50; + "}, + thing_to_find + ) + + else + // Wrap the thing in %s so LIKE will work. + thing_to_find = "%[thing_to_find]%" + query = new({" + SELECT * + FROM [SQLITE_TABLE_FEEDBACK] + WHERE [row_name] LIKE ? + ORDER BY [SQLITE_FEEDBACK_COLUMN_ID] + DESC LIMIT 50; + "}, + thing_to_find + ) + query.Execute(SSsqlite.sqlite_db) + SSsqlite.sqlite_check_for_errors(query, "Admin Feedback Viewer - Filter by [row_name] to find [thing_to_find]") + return query + +// Builds the window for players to review their feedback. +/datum/managed_browser/feedback_viewer/get_html() + var/list/dat = list("") + if(!last_query) // If no query was done before, just show the most recent feedbacks. + var/database/query/query = new({" + SELECT * + FROM [SQLITE_TABLE_FEEDBACK] + ORDER BY [SQLITE_FEEDBACK_COLUMN_ID] + DESC LIMIT 50; + "} + ) + query.Execute(SSsqlite.sqlite_db) + SSsqlite.sqlite_check_for_errors(query, "Admin Feedback Viewer") + last_query = query + + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + while(last_query.NextRow()) + var/list/row_data = last_query.GetRowData() + dat += "" + dat += "" + dat += "" + dat += "" // TODO: Color this to make hashed keys more distinguishable. + var/text = row_data[SQLITE_FEEDBACK_COLUMN_CONTENT] + if(length(text) > 512) + text = href(src, list( + "show_full_feedback" = 1, + "feedback_author" = row_data[SQLITE_FEEDBACK_COLUMN_AUTHOR], + "feedback_content" = row_data[SQLITE_FEEDBACK_COLUMN_CONTENT] + ), "[copytext(text, 1, 64)]... ([length(text)])") + else + text = replacetext(text, "\n", "
    ") + dat += "" + dat += "" + dat += "" + dat += "
    [href(src, list("filter_id" = 1), "ID")][href(src, list("filter_topic" = 1), "Topic")][href(src, list("filter_author" = 1), "Author")][href(src, list("filter_content" = 1), "Content")][href(src, list("filter_datetime" = 1), "Datetime")]
    [row_data[SQLITE_FEEDBACK_COLUMN_ID]][row_data[SQLITE_FEEDBACK_COLUMN_TOPIC]][row_data[SQLITE_FEEDBACK_COLUMN_AUTHOR]][text][row_data[SQLITE_FEEDBACK_COLUMN_DATETIME]]
    " + + dat += "" + return dat.Join() + +// Used to show the full version of feedback in a seperate window. +/datum/managed_browser/feedback_viewer/proc/display_big_feedback(author, text) + var/list/dat = list("") + dat += replacetext(text, "\n", "
    ") + + var/datum/browser/popup = new(my_client.mob, "feedback_big", "[author]'s Feedback", 480, 520, src) + popup.set_content(dat.Join()) + popup.open() + + +/datum/managed_browser/feedback_viewer/Topic(href, href_list[]) + if(!my_client) + return FALSE + + if(href_list["close"]) // To avoid refreshing. + return + + if(href_list["show_full_feedback"]) + display_big_feedback(href_list["feedback_author"], href_list["feedback_content"]) + return + + if(href_list["filter_id"]) + var/id_to_search = tgui_input_number(my_client, "Write feedback ID here.", "Filter by ID", null) + if(id_to_search) + last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_ID, id_to_search, TRUE) + + if(href_list["filter_author"]) + var/author_to_search = tgui_input_text(my_client, "Write desired key or hash here. Partial keys/hashes are allowed.", "Filter by Author", null) + if(author_to_search) + last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_AUTHOR, author_to_search) + + if(href_list["filter_topic"]) + var/topic_to_search = tgui_input_text(my_client, "Write desired topic here. Partial topics are allowed. \ + \nThe current topics in the config are [english_list(config.sqlite_feedback_topics)].", "Filter by Topic", null) + if(topic_to_search) + last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_TOPIC, topic_to_search) + + if(href_list["filter_content"]) + var/content_to_search = tgui_input_text(my_client, "Write desired content to find here. Partial matches are allowed.", "Filter by Content", null, multiline = TRUE) + if(content_to_search) + last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_CONTENT, content_to_search) + + if(href_list["filter_datetime"]) + var/datetime_to_search = tgui_input_text(my_client, "Write desired datetime. Partial matches are allowed.\n\ + Format is 'YYYY-MM-DD HH:MM:SS'.", "Filter by Datetime", null) + if(datetime_to_search) + last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_DATETIME, datetime_to_search) + + // Refresh. display() \ No newline at end of file diff --git a/code/datums/mind.dm b/code/datums/mind.dm index f7bb7a738f9..777ecc42369 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1,579 +1,579 @@ -/* Note from Carnie: - The way datum/mind stuff works has been changed a lot. - Minds now represent IC characters rather than following a client around constantly. - - Guidelines for using minds properly: - - - Never mind.transfer_to(ghost). The var/current and var/original of a mind must always be of type mob/living! - ghost.mind is however used as a reference to the ghost's corpse - - - When creating a new mob for an existing IC character (e.g. cloning a dead guy or borging a brain of a human) - the existing mind of the old mob should be transfered to the new mob like so: - - mind.transfer_to(new_mob) - - - You must not assign key= or ckey= after transfer_to() since the transfer_to transfers the client for you. - By setting key or ckey explicitly after transfering the mind with transfer_to you will cause bugs like DCing - the player. - - - IMPORTANT NOTE 2, if you want a player to become a ghost, use mob.ghostize() It does all the hard work for you. - - - When creating a new mob which will be a new IC character (e.g. putting a shade in a construct or randomly selecting - a ghost to become a xeno during an event). Simply assign the key or ckey like you've always done. - - new_mob.key = key - - The Login proc will handle making a new mob for that mobtype (including setting up stuff like mind.name). Simple! - However if you want that mind to have any special properties like being a traitor etc you will have to do that - yourself. - -*/ - -/datum/mind - var/key - var/name //replaces mob/var/original_name - var/mob/living/current - var/mob/living/original //TODO: remove.not used in any meaningful way ~Carn. First I'll need to tweak the way silicon-mobs handle minds. - var/active = 0 - - var/memory - - var/assigned_role - var/special_role - - var/role_alt_title - - var/datum/job/assigned_job - - var/list/datum/objective/objectives = list() - var/list/datum/objective/special_verbs = list() - - var/has_been_rev = 0//Tracks if this mind has been a rev or not - - var/datum/faction/faction //associated faction - var/datum/changeling/changeling //changeling holder - - var/rev_cooldown = 0 - var/tcrystals = 0 - var/list/purchase_log = new - var/used_TC = 0 - - var/list/learned_recipes //List of learned recipe TYPES. - - // the world.time since the mob has been brigged, or -1 if not at all - var/brigged_since = -1 - - //put this here for easier tracking ingame - var/datum/money_account/initial_account - - //used for antag tcrystal trading, more info in code\game\objects\items\telecrystals.dm - var/accept_tcrystals = 0 - - //used for optional self-objectives that antagonists can give themselves, which are displayed at the end of the round. - var/ambitions - - //used to store what traits the player had picked out in their preferences before joining, in text form. - var/list/traits = list() - - var/datum/religion/my_religion - -/datum/mind/New(var/key) - src.key = key - purchase_log = list() - ..() - -/datum/mind/proc/transfer_to(mob/living/new_character) - if(!istype(new_character)) - to_world_log("## DEBUG: transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob. Please inform Carn") - if(current) //remove ourself from our old body's mind variable - if(changeling) - current.remove_changeling_powers() - current.verbs -= /datum/changeling/proc/EvolutionMenu - current.mind = null - - if(new_character.mind) //remove any mind currently in our new body's mind variable - new_character.mind.current = null - - current = new_character //link ourself to our new body - new_character.mind = src //and link our new body to ourself - - if(changeling) - new_character.make_changeling() - - if(active) - new_character.key = key //now transfer the key to link the client to our new body - -/datum/mind/proc/store_memory(new_text) - memory += "[new_text]
    " - -/datum/mind/proc/show_memory(mob/recipient) - var/output = "[current.real_name]'s Memory
    " - output += memory - - if(objectives.len>0) - output += "
    Objectives:" - - var/obj_count = 1 - for(var/datum/objective/objective in objectives) - output += "Objective #[obj_count]: [objective.explanation_text]" - obj_count++ - - if(ambitions) - output += "
    Ambitions: [ambitions]
    " - recipient << browse(output,"window=memory") - -/datum/mind/proc/edit_memory() - if(!ticker || !ticker.mode) - tgui_alert_async(usr, "Not before round-start!", "Alert") - return - - var/out = "[name][(current&&(current.real_name!=name))?" (as [current.real_name])":""]
    " - out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]
    " - out += "Assigned role: [assigned_role]. Edit
    " - out += "
    " - out += "Factions and special roles:
    " - for(var/antag_type in all_antag_types) - var/datum/antagonist/antag = all_antag_types[antag_type] - out += "[antag.get_panel_entry(src)]" - out += "

    " - out += "Objectives
    " - - if(objectives && objectives.len) - var/num = 1 - for(var/datum/objective/O in objectives) - out += "Objective #[num]: [O.explanation_text] " - if(O.completed) - out += "([span_green("complete")])" - else - out += "([span_red("incomplete")])" - out += " \[toggle\]" - out += " \[remove\]
    " - num++ - out += "
    \[announce objectives\]" - - else - out += "None." - out += "
    \[add\]

    " - out += "Ambitions: [ambitions ? ambitions : "None"] \[edit\]
    " - usr << browse(out, "window=edit_memory[src]") - -/datum/mind/Topic(href, href_list) - if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return - - if(href_list["add_antagonist"]) - var/datum/antagonist/antag = all_antag_types[href_list["add_antagonist"]] - if(antag) - if(antag.add_antagonist(src, 1, 1, 0, 1, 1)) // Ignore equipment and role type for this. - log_admin("[key_name_admin(usr)] made [key_name(src)] into a [antag.role_text].") - else - to_chat(usr, "[src] could not be made into a [antag.role_text]!") - - else if(href_list["remove_antagonist"]) - var/datum/antagonist/antag = all_antag_types[href_list["remove_antagonist"]] - if(antag) antag.remove_antagonist(src) - - else if(href_list["equip_antagonist"]) - var/datum/antagonist/antag = all_antag_types[href_list["equip_antagonist"]] - if(antag) antag.equip(src.current) - - else if(href_list["unequip_antagonist"]) - var/datum/antagonist/antag = all_antag_types[href_list["unequip_antagonist"]] - if(antag) antag.unequip(src.current) - - else if(href_list["move_antag_to_spawn"]) - var/datum/antagonist/antag = all_antag_types[href_list["move_antag_to_spawn"]] - if(antag) antag.place_mob(src.current) - - else if (href_list["role_edit"]) - var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in joblist - if (!new_role) return - assigned_role = new_role - - else if (href_list["memory_edit"]) - var/new_memo = sanitize(tgui_input_text(usr, "Write new memory", "Memory", memory, multiline = TRUE, prevent_enter = TRUE)) - if (isnull(new_memo)) return - memory = new_memo - - else if (href_list["amb_edit"]) - var/datum/mind/mind = locate(href_list["amb_edit"]) - if(!mind) - return - var/new_ambition = tgui_input_text(usr, "Enter a new ambition", "Memory", mind.ambitions, multiline = TRUE, prevent_enter = TRUE) - if(isnull(new_ambition)) - return - if(mind) - mind.ambitions = sanitize(new_ambition) - to_chat(mind.current, "Your ambitions have been changed by higher powers, they are now: [mind.ambitions]") - log_and_message_admins("made [key_name(mind.current)]'s ambitions be '[mind.ambitions]'.") - - else if (href_list["obj_edit"] || href_list["obj_add"]) - var/datum/objective/objective - var/objective_pos - var/def_value - - if (href_list["obj_edit"]) - objective = locate(href_list["obj_edit"]) - if (!objective) return - objective_pos = objectives.Find(objective) - - //Text strings are easy to manipulate. Revised for simplicity. - var/temp_obj_type = "[objective.type]"//Convert path into a text string. - def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword. - if(!def_value)//If it's a custom objective, it will be an empty string. - def_value = "custom" - - var/list/choices = list("assassinate", "debrain", "protect", "prevent", "harm", "brig", "hijack", "escape", "survive", "steal", "download", "mercenary", "capture", "absorb", "custom") - var/new_obj_type = tgui_input_list(usr, "Select objective type:", "Objective type", choices, def_value) - if (!new_obj_type) return - - var/datum/objective/new_objective = null - - switch (new_obj_type) - if ("assassinate","protect","debrain", "harm", "brig") - //To determine what to name the objective in explanation text. - var/objective_type_capital = uppertext(copytext(new_obj_type, 1,2))//Capitalize first letter. - var/objective_type_text = copytext(new_obj_type, 2)//Leave the rest of the text. - var/objective_type = "[objective_type_capital][objective_type_text]"//Add them together into a text string. - - var/list/possible_targets = list("Free objective") - for(var/datum/mind/possible_target in ticker.minds) - if ((possible_target != src) && istype(possible_target.current, /mob/living/carbon/human)) - possible_targets += possible_target.current - - var/mob/def_target = null - var/objective_list[] = list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain) - if (objective&&(objective.type in objective_list) && objective.target) - def_target = objective.target.current - - var/new_target = tgui_input_list(usr, "Select target:", "Objective target", possible_targets, def_target) - if (!new_target) return - - var/objective_path = text2path("/datum/objective/[new_obj_type]") - var/mob/living/M = new_target - if (!istype(M) || !M.mind || new_target == "Free objective") - new_objective = new objective_path - new_objective.owner = src - new_objective:target = null - new_objective.explanation_text = "Free objective" - else - new_objective = new objective_path - new_objective.owner = src - new_objective:target = M.mind - new_objective.explanation_text = "[objective_type] [M.real_name], the [M.mind.special_role ? M.mind:special_role : M.mind:assigned_role]." - - if ("prevent") - new_objective = new /datum/objective/block - new_objective.owner = src - - if ("hijack") - new_objective = new /datum/objective/hijack - new_objective.owner = src - - if ("escape") - new_objective = new /datum/objective/escape - new_objective.owner = src - - if ("survive") - new_objective = new /datum/objective/survive - new_objective.owner = src - - if ("mercenary") - new_objective = new /datum/objective/nuclear - new_objective.owner = src - - if ("steal") - if (!istype(objective, /datum/objective/steal)) - new_objective = new /datum/objective/steal - new_objective.owner = src - else - new_objective = objective - var/datum/objective/steal/steal = new_objective - if (!steal.select_target()) - return - - if("download","capture","absorb", "vore") - var/def_num - if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) - def_num = objective.target_amount - - var/target_number = tgui_input_number(usr, "Input target number:", "Objective", def_num) - if (isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. - return - - switch(new_obj_type) - if("download") - new_objective = new /datum/objective/download - new_objective.explanation_text = "Download [target_number] research levels." - if("capture") - new_objective = new /datum/objective/capture - new_objective.explanation_text = "Accumulate [target_number] capture points." - if("absorb") - new_objective = new /datum/objective/absorb - new_objective.explanation_text = "Absorb [target_number] compatible genomes." - if("vore") - new_objective = new /datum/objective/vore - new_objective.explanation_text = "Devour [target_number] [target_number == 1 ? "person" : "people"]. What happens to them after you do that is irrelevant." - new_objective.owner = src - new_objective.target_amount = target_number - - if ("custom") - var/expl = sanitize(tgui_input_text(usr, "Custom objective:", "Objective", objective ? objective.explanation_text : "")) - if (!expl) return - new_objective = new /datum/objective - new_objective.owner = src - new_objective.explanation_text = expl - - if (!new_objective) return - - if (objective) - objectives -= objective - objectives.Insert(objective_pos, new_objective) - else - objectives += new_objective - - else if (href_list["obj_delete"]) - var/datum/objective/objective = locate(href_list["obj_delete"]) - if(!istype(objective)) return - objectives -= objective - - else if(href_list["obj_completed"]) - var/datum/objective/objective = locate(href_list["obj_completed"]) - if(!istype(objective)) return - objective.completed = !objective.completed - - else if(href_list["implant"]) - var/mob/living/carbon/human/H = current - - BITSET(H.hud_updateflag, IMPLOYAL_HUD) // updates that players HUD images so secHUD's pick up they are implanted or not. - - switch(href_list["implant"]) - if("remove") - for(var/obj/item/weapon/implant/loyalty/I in H.contents) - for(var/obj/item/organ/external/organs in H.organs) - if(I in organs.implants) - qdel(I) - break - to_chat(H, "Your loyalty implant has been deactivated.") - log_admin("[key_name_admin(usr)] has de-loyalty implanted [current].") - if("add") - to_chat(H, "You somehow have become the recepient of a loyalty transplant, and it just activated!") - H.implant_loyalty(override = TRUE) - log_admin("[key_name_admin(usr)] has loyalty implanted [current].") - else - else if (href_list["silicon"]) - BITSET(current.hud_updateflag, SPECIALROLE_HUD) - switch(href_list["silicon"]) - - if("unemag") - var/mob/living/silicon/robot/R = current - if (istype(R)) - R.emagged = 0 - if (R.activated(R.module.emag)) - R.module_active = null - if(R.module_state_1 == R.module.emag) - R.module_state_1 = null - R.contents -= R.module.emag - else if(R.module_state_2 == R.module.emag) - R.module_state_2 = null - R.contents -= R.module.emag - else if(R.module_state_3 == R.module.emag) - R.module_state_3 = null - R.contents -= R.module.emag - log_admin("[key_name_admin(usr)] has unemag'ed [R].") - - if("unemagcyborgs") - if (istype(current, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/ai = current - for (var/mob/living/silicon/robot/R in ai.connected_robots) - R.emagged = 0 - if (R.module) - if (R.activated(R.module.emag)) - R.module_active = null - if(R.module_state_1 == R.module.emag) - R.module_state_1 = null - R.contents -= R.module.emag - else if(R.module_state_2 == R.module.emag) - R.module_state_2 = null - R.contents -= R.module.emag - else if(R.module_state_3 == R.module.emag) - R.module_state_3 = null - R.contents -= R.module.emag - log_admin("[key_name_admin(usr)] has unemag'ed [ai]'s Cyborgs.") - - else if (href_list["common"]) - switch(href_list["common"]) - if("undress") - for(var/obj/item/W in current) - current.drop_from_inventory(W) - if("takeuplink") - take_uplink() - memory = null//Remove any memory they may have had. - if("crystals") - if (usr.client.holder.rights & R_FUN) - // var/obj/item/device/uplink/hidden/suplink = find_syndicate_uplink() No longer needed, uses stored in mind - var/crystals - crystals = tcrystals - crystals = tgui_input_number(usr, "Amount of telecrystals for [key]", crystals) - if (!isnull(crystals)) - tcrystals = crystals - - else if (href_list["obj_announce"]) - var/obj_count = 1 - to_chat(current, span_blue("Your current objectives:")) - for(var/datum/objective/objective in objectives) - to_chat(current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - edit_memory() - -/datum/mind/proc/find_syndicate_uplink() - var/list/L = current.get_contents() - for (var/obj/item/I in L) - if (I.hidden_uplink) - return I.hidden_uplink - return null - -/datum/mind/proc/take_uplink() - var/obj/item/device/uplink/hidden/H = find_syndicate_uplink() - if(H) - qdel(H) - - -// check whether this mind's mob has been brigged for the given duration -// have to call this periodically for the duration to work properly -/datum/mind/proc/is_brigged(duration) - var/turf/T = current.loc - if(!istype(T)) - brigged_since = -1 - return 0 - var/is_currently_brigged = 0 - if(istype(T.loc,/area/security/brig)) - is_currently_brigged = 1 - for(var/obj/item/weapon/card/id/card in current) - is_currently_brigged = 0 - break // if they still have ID they're not brigged - for(var/obj/item/device/pda/P in current) - if(P.id) - is_currently_brigged = 0 - break // if they still have ID they're not brigged - - if(!is_currently_brigged) - brigged_since = -1 - return 0 - - if(brigged_since == -1) - brigged_since = world.time - - return (duration <= world.time - brigged_since) - -/datum/mind/proc/reset() - assigned_role = null - special_role = null - role_alt_title = null - assigned_job = null - //faction = null //Uncommenting this causes a compile error due to 'undefined type', fucked if I know. - changeling = null - initial_account = null - objectives = list() - special_verbs = list() - has_been_rev = 0 - rev_cooldown = 0 - brigged_since = -1 - -//Antagonist role check -/mob/living/proc/check_special_role(role) - if(mind) - if(!role) - return mind.special_role - else - return (mind.special_role == role) ? 1 : 0 - else - return 0 - -/datum/mind/proc/get_ghost(even_if_they_cant_reenter) - for(var/mob/observer/dead/G in player_list) - if(G.mind == src) - if(G.can_reenter_corpse || even_if_they_cant_reenter) - return G - break - -/datum/mind/proc/grab_ghost(force) - var/mob/observer/dead/G = get_ghost(even_if_they_cant_reenter = force) - . = G - if(G) - G.reenter_corpse() - -//Initialisation procs -/mob/living/proc/mind_initialize() - if(mind) - mind.key = key - else - mind = new /datum/mind(key) - mind.original = src - if(ticker) - ticker.minds += mind - else - to_world_log("## DEBUG: mind_initialize(): No ticker ready yet! Please inform Carn") - if(!mind.name) mind.name = real_name - mind.current = src - if(player_is_antag(mind)) - src.client.verbs += /client/proc/aooc - -//HUMAN -/mob/living/carbon/human/mind_initialize() - . = ..() - if(!mind.assigned_role) - mind.assigned_role = USELESS_JOB //defualt //VOREStation Edit - Visitor not Assistant - -//slime -/mob/living/simple_mob/slime/mind_initialize() - . = ..() - mind.assigned_role = "slime" - -/mob/living/carbon/alien/larva/mind_initialize() - . = ..() - mind.special_role = "Larva" - -//AI -/mob/living/silicon/ai/mind_initialize() - . = ..() - mind.assigned_role = "AI" - -//BORG -/mob/living/silicon/robot/mind_initialize() - . = ..() - mind.assigned_role = "Cyborg" - -//PAI -/mob/living/silicon/pai/mind_initialize() - . = ..() - mind.assigned_role = "pAI" - mind.special_role = "" - -//Animals -/mob/living/simple_mob/mind_initialize() - . = ..() - mind.assigned_role = "Simple Mob" - -/mob/living/simple_mob/animal/passive/dog/corgi/mind_initialize() - . = ..() - mind.assigned_role = "Corgi" - -/mob/living/simple_mob/construct/shade/mind_initialize() - . = ..() - mind.assigned_role = "Shade" - mind.special_role = "Cultist" - -/mob/living/simple_mob/construct/artificer/mind_initialize() - . = ..() - mind.assigned_role = "Artificer" - mind.special_role = "Cultist" - -/mob/living/simple_mob/construct/wraith/mind_initialize() - . = ..() - mind.assigned_role = "Wraith" - mind.special_role = "Cultist" - -/mob/living/simple_mob/construct/juggernaut/mind_initialize() - . = ..() - mind.assigned_role = "Juggernaut" - mind.special_role = "Cultist" +/* Note from Carnie: + The way datum/mind stuff works has been changed a lot. + Minds now represent IC characters rather than following a client around constantly. + + Guidelines for using minds properly: + + - Never mind.transfer_to(ghost). The var/current and var/original of a mind must always be of type mob/living! + ghost.mind is however used as a reference to the ghost's corpse + + - When creating a new mob for an existing IC character (e.g. cloning a dead guy or borging a brain of a human) + the existing mind of the old mob should be transfered to the new mob like so: + + mind.transfer_to(new_mob) + + - You must not assign key= or ckey= after transfer_to() since the transfer_to transfers the client for you. + By setting key or ckey explicitly after transfering the mind with transfer_to you will cause bugs like DCing + the player. + + - IMPORTANT NOTE 2, if you want a player to become a ghost, use mob.ghostize() It does all the hard work for you. + + - When creating a new mob which will be a new IC character (e.g. putting a shade in a construct or randomly selecting + a ghost to become a xeno during an event). Simply assign the key or ckey like you've always done. + + new_mob.key = key + + The Login proc will handle making a new mob for that mobtype (including setting up stuff like mind.name). Simple! + However if you want that mind to have any special properties like being a traitor etc you will have to do that + yourself. + +*/ + +/datum/mind + var/key + var/name //replaces mob/var/original_name + var/mob/living/current + var/mob/living/original //TODO: remove.not used in any meaningful way ~Carn. First I'll need to tweak the way silicon-mobs handle minds. + var/active = 0 + + var/memory + + var/assigned_role + var/special_role + + var/role_alt_title + + var/datum/job/assigned_job + + var/list/datum/objective/objectives = list() + var/list/datum/objective/special_verbs = list() + + var/has_been_rev = 0//Tracks if this mind has been a rev or not + + var/datum/faction/faction //associated faction + var/datum/changeling/changeling //changeling holder + + var/rev_cooldown = 0 + var/tcrystals = 0 + var/list/purchase_log = new + var/used_TC = 0 + + var/list/learned_recipes //List of learned recipe TYPES. + + // the world.time since the mob has been brigged, or -1 if not at all + var/brigged_since = -1 + + //put this here for easier tracking ingame + var/datum/money_account/initial_account + + //used for antag tcrystal trading, more info in code\game\objects\items\telecrystals.dm + var/accept_tcrystals = 0 + + //used for optional self-objectives that antagonists can give themselves, which are displayed at the end of the round. + var/ambitions + + //used to store what traits the player had picked out in their preferences before joining, in text form. + var/list/traits = list() + + var/datum/religion/my_religion + +/datum/mind/New(var/key) + src.key = key + purchase_log = list() + ..() + +/datum/mind/proc/transfer_to(mob/living/new_character) + if(!istype(new_character)) + to_world_log("## DEBUG: transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob. Please inform Carn") + if(current) //remove ourself from our old body's mind variable + if(changeling) + current.remove_changeling_powers() + current.verbs -= /datum/changeling/proc/EvolutionMenu + current.mind = null + + if(new_character.mind) //remove any mind currently in our new body's mind variable + new_character.mind.current = null + + current = new_character //link ourself to our new body + new_character.mind = src //and link our new body to ourself + + if(changeling) + new_character.make_changeling() + + if(active) + new_character.key = key //now transfer the key to link the client to our new body + +/datum/mind/proc/store_memory(new_text) + memory += "[new_text]
    " + +/datum/mind/proc/show_memory(mob/recipient) + var/output = "[current.real_name]'s Memory
    " + output += memory + + if(objectives.len>0) + output += "
    Objectives:" + + var/obj_count = 1 + for(var/datum/objective/objective in objectives) + output += "Objective #[obj_count]: [objective.explanation_text]" + obj_count++ + + if(ambitions) + output += "
    Ambitions: [ambitions]
    " + recipient << browse(output,"window=memory") + +/datum/mind/proc/edit_memory() + if(!ticker || !ticker.mode) + tgui_alert_async(usr, "Not before round-start!", "Alert") + return + + var/out = "[name][(current&&(current.real_name!=name))?" (as [current.real_name])":""]
    " + out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]
    " + out += "Assigned role: [assigned_role]. Edit
    " + out += "
    " + out += "Factions and special roles:
    " + for(var/antag_type in all_antag_types) + var/datum/antagonist/antag = all_antag_types[antag_type] + out += "[antag.get_panel_entry(src)]" + out += "

    " + out += "Objectives
    " + + if(objectives && objectives.len) + var/num = 1 + for(var/datum/objective/O in objectives) + out += "Objective #[num]: [O.explanation_text] " + if(O.completed) + out += "([span_green("complete")])" + else + out += "([span_red("incomplete")])" + out += " \[toggle\]" + out += " \[remove\]
    " + num++ + out += "
    \[announce objectives\]" + + else + out += "None." + out += "
    \[add\]

    " + out += "Ambitions: [ambitions ? ambitions : "None"] \[edit\]
    " + usr << browse(out, "window=edit_memory[src]") + +/datum/mind/Topic(href, href_list) + if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return + + if(href_list["add_antagonist"]) + var/datum/antagonist/antag = all_antag_types[href_list["add_antagonist"]] + if(antag) + if(antag.add_antagonist(src, 1, 1, 0, 1, 1)) // Ignore equipment and role type for this. + log_admin("[key_name_admin(usr)] made [key_name(src)] into a [antag.role_text].") + else + to_chat(usr, "[src] could not be made into a [antag.role_text]!") + + else if(href_list["remove_antagonist"]) + var/datum/antagonist/antag = all_antag_types[href_list["remove_antagonist"]] + if(antag) antag.remove_antagonist(src) + + else if(href_list["equip_antagonist"]) + var/datum/antagonist/antag = all_antag_types[href_list["equip_antagonist"]] + if(antag) antag.equip(src.current) + + else if(href_list["unequip_antagonist"]) + var/datum/antagonist/antag = all_antag_types[href_list["unequip_antagonist"]] + if(antag) antag.unequip(src.current) + + else if(href_list["move_antag_to_spawn"]) + var/datum/antagonist/antag = all_antag_types[href_list["move_antag_to_spawn"]] + if(antag) antag.place_mob(src.current) + + else if (href_list["role_edit"]) + var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in joblist + if (!new_role) return + assigned_role = new_role + + else if (href_list["memory_edit"]) + var/new_memo = sanitize(tgui_input_text(usr, "Write new memory", "Memory", memory, multiline = TRUE, prevent_enter = TRUE)) + if (isnull(new_memo)) return + memory = new_memo + + else if (href_list["amb_edit"]) + var/datum/mind/mind = locate(href_list["amb_edit"]) + if(!mind) + return + var/new_ambition = tgui_input_text(usr, "Enter a new ambition", "Memory", mind.ambitions, multiline = TRUE, prevent_enter = TRUE) + if(isnull(new_ambition)) + return + if(mind) + mind.ambitions = sanitize(new_ambition) + to_chat(mind.current, "Your ambitions have been changed by higher powers, they are now: [mind.ambitions]") + log_and_message_admins("made [key_name(mind.current)]'s ambitions be '[mind.ambitions]'.") + + else if (href_list["obj_edit"] || href_list["obj_add"]) + var/datum/objective/objective + var/objective_pos + var/def_value + + if (href_list["obj_edit"]) + objective = locate(href_list["obj_edit"]) + if (!objective) return + objective_pos = objectives.Find(objective) + + //Text strings are easy to manipulate. Revised for simplicity. + var/temp_obj_type = "[objective.type]"//Convert path into a text string. + def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword. + if(!def_value)//If it's a custom objective, it will be an empty string. + def_value = "custom" + + var/list/choices = list("assassinate", "debrain", "protect", "prevent", "harm", "brig", "hijack", "escape", "survive", "steal", "download", "mercenary", "capture", "absorb", "custom") + var/new_obj_type = tgui_input_list(usr, "Select objective type:", "Objective type", choices, def_value) + if (!new_obj_type) return + + var/datum/objective/new_objective = null + + switch (new_obj_type) + if ("assassinate","protect","debrain", "harm", "brig") + //To determine what to name the objective in explanation text. + var/objective_type_capital = uppertext(copytext(new_obj_type, 1,2))//Capitalize first letter. + var/objective_type_text = copytext(new_obj_type, 2)//Leave the rest of the text. + var/objective_type = "[objective_type_capital][objective_type_text]"//Add them together into a text string. + + var/list/possible_targets = list("Free objective") + for(var/datum/mind/possible_target in ticker.minds) + if ((possible_target != src) && istype(possible_target.current, /mob/living/carbon/human)) + possible_targets += possible_target.current + + var/mob/def_target = null + var/objective_list[] = list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain) + if (objective&&(objective.type in objective_list) && objective.target) + def_target = objective.target.current + + var/new_target = tgui_input_list(usr, "Select target:", "Objective target", possible_targets, def_target) + if (!new_target) return + + var/objective_path = text2path("/datum/objective/[new_obj_type]") + var/mob/living/M = new_target + if (!istype(M) || !M.mind || new_target == "Free objective") + new_objective = new objective_path + new_objective.owner = src + new_objective:target = null + new_objective.explanation_text = "Free objective" + else + new_objective = new objective_path + new_objective.owner = src + new_objective:target = M.mind + new_objective.explanation_text = "[objective_type] [M.real_name], the [M.mind.special_role ? M.mind:special_role : M.mind:assigned_role]." + + if ("prevent") + new_objective = new /datum/objective/block + new_objective.owner = src + + if ("hijack") + new_objective = new /datum/objective/hijack + new_objective.owner = src + + if ("escape") + new_objective = new /datum/objective/escape + new_objective.owner = src + + if ("survive") + new_objective = new /datum/objective/survive + new_objective.owner = src + + if ("mercenary") + new_objective = new /datum/objective/nuclear + new_objective.owner = src + + if ("steal") + if (!istype(objective, /datum/objective/steal)) + new_objective = new /datum/objective/steal + new_objective.owner = src + else + new_objective = objective + var/datum/objective/steal/steal = new_objective + if (!steal.select_target()) + return + + if("download","capture","absorb", "vore") + var/def_num + if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) + def_num = objective.target_amount + + var/target_number = tgui_input_number(usr, "Input target number:", "Objective", def_num) + if (isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. + return + + switch(new_obj_type) + if("download") + new_objective = new /datum/objective/download + new_objective.explanation_text = "Download [target_number] research levels." + if("capture") + new_objective = new /datum/objective/capture + new_objective.explanation_text = "Accumulate [target_number] capture points." + if("absorb") + new_objective = new /datum/objective/absorb + new_objective.explanation_text = "Absorb [target_number] compatible genomes." + if("vore") + new_objective = new /datum/objective/vore + new_objective.explanation_text = "Devour [target_number] [target_number == 1 ? "person" : "people"]. What happens to them after you do that is irrelevant." + new_objective.owner = src + new_objective.target_amount = target_number + + if ("custom") + var/expl = sanitize(tgui_input_text(usr, "Custom objective:", "Objective", objective ? objective.explanation_text : "")) + if (!expl) return + new_objective = new /datum/objective + new_objective.owner = src + new_objective.explanation_text = expl + + if (!new_objective) return + + if (objective) + objectives -= objective + objectives.Insert(objective_pos, new_objective) + else + objectives += new_objective + + else if (href_list["obj_delete"]) + var/datum/objective/objective = locate(href_list["obj_delete"]) + if(!istype(objective)) return + objectives -= objective + + else if(href_list["obj_completed"]) + var/datum/objective/objective = locate(href_list["obj_completed"]) + if(!istype(objective)) return + objective.completed = !objective.completed + + else if(href_list["implant"]) + var/mob/living/carbon/human/H = current + + BITSET(H.hud_updateflag, IMPLOYAL_HUD) // updates that players HUD images so secHUD's pick up they are implanted or not. + + switch(href_list["implant"]) + if("remove") + for(var/obj/item/weapon/implant/loyalty/I in H.contents) + for(var/obj/item/organ/external/organs in H.organs) + if(I in organs.implants) + qdel(I) + break + to_chat(H, "Your loyalty implant has been deactivated.") + log_admin("[key_name_admin(usr)] has de-loyalty implanted [current].") + if("add") + to_chat(H, "You somehow have become the recepient of a loyalty transplant, and it just activated!") + H.implant_loyalty(override = TRUE) + log_admin("[key_name_admin(usr)] has loyalty implanted [current].") + else + else if (href_list["silicon"]) + BITSET(current.hud_updateflag, SPECIALROLE_HUD) + switch(href_list["silicon"]) + + if("unemag") + var/mob/living/silicon/robot/R = current + if (istype(R)) + R.emagged = 0 + if (R.activated(R.module.emag)) + R.module_active = null + if(R.module_state_1 == R.module.emag) + R.module_state_1 = null + R.contents -= R.module.emag + else if(R.module_state_2 == R.module.emag) + R.module_state_2 = null + R.contents -= R.module.emag + else if(R.module_state_3 == R.module.emag) + R.module_state_3 = null + R.contents -= R.module.emag + log_admin("[key_name_admin(usr)] has unemag'ed [R].") + + if("unemagcyborgs") + if (istype(current, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/ai = current + for (var/mob/living/silicon/robot/R in ai.connected_robots) + R.emagged = 0 + if (R.module) + if (R.activated(R.module.emag)) + R.module_active = null + if(R.module_state_1 == R.module.emag) + R.module_state_1 = null + R.contents -= R.module.emag + else if(R.module_state_2 == R.module.emag) + R.module_state_2 = null + R.contents -= R.module.emag + else if(R.module_state_3 == R.module.emag) + R.module_state_3 = null + R.contents -= R.module.emag + log_admin("[key_name_admin(usr)] has unemag'ed [ai]'s Cyborgs.") + + else if (href_list["common"]) + switch(href_list["common"]) + if("undress") + for(var/obj/item/W in current) + current.drop_from_inventory(W) + if("takeuplink") + take_uplink() + memory = null//Remove any memory they may have had. + if("crystals") + if (usr.client.holder.rights & R_FUN) + // var/obj/item/device/uplink/hidden/suplink = find_syndicate_uplink() No longer needed, uses stored in mind + var/crystals + crystals = tcrystals + crystals = tgui_input_number(usr, "Amount of telecrystals for [key]", crystals) + if (!isnull(crystals)) + tcrystals = crystals + + else if (href_list["obj_announce"]) + var/obj_count = 1 + to_chat(current, span_blue("Your current objectives:")) + for(var/datum/objective/objective in objectives) + to_chat(current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + edit_memory() + +/datum/mind/proc/find_syndicate_uplink() + var/list/L = current.get_contents() + for (var/obj/item/I in L) + if (I.hidden_uplink) + return I.hidden_uplink + return null + +/datum/mind/proc/take_uplink() + var/obj/item/device/uplink/hidden/H = find_syndicate_uplink() + if(H) + qdel(H) + + +// check whether this mind's mob has been brigged for the given duration +// have to call this periodically for the duration to work properly +/datum/mind/proc/is_brigged(duration) + var/turf/T = current.loc + if(!istype(T)) + brigged_since = -1 + return 0 + var/is_currently_brigged = 0 + if(istype(T.loc,/area/security/brig)) + is_currently_brigged = 1 + for(var/obj/item/weapon/card/id/card in current) + is_currently_brigged = 0 + break // if they still have ID they're not brigged + for(var/obj/item/device/pda/P in current) + if(P.id) + is_currently_brigged = 0 + break // if they still have ID they're not brigged + + if(!is_currently_brigged) + brigged_since = -1 + return 0 + + if(brigged_since == -1) + brigged_since = world.time + + return (duration <= world.time - brigged_since) + +/datum/mind/proc/reset() + assigned_role = null + special_role = null + role_alt_title = null + assigned_job = null + //faction = null //Uncommenting this causes a compile error due to 'undefined type', fucked if I know. + changeling = null + initial_account = null + objectives = list() + special_verbs = list() + has_been_rev = 0 + rev_cooldown = 0 + brigged_since = -1 + +//Antagonist role check +/mob/living/proc/check_special_role(role) + if(mind) + if(!role) + return mind.special_role + else + return (mind.special_role == role) ? 1 : 0 + else + return 0 + +/datum/mind/proc/get_ghost(even_if_they_cant_reenter) + for(var/mob/observer/dead/G in player_list) + if(G.mind == src) + if(G.can_reenter_corpse || even_if_they_cant_reenter) + return G + break + +/datum/mind/proc/grab_ghost(force) + var/mob/observer/dead/G = get_ghost(even_if_they_cant_reenter = force) + . = G + if(G) + G.reenter_corpse() + +//Initialisation procs +/mob/living/proc/mind_initialize() + if(mind) + mind.key = key + else + mind = new /datum/mind(key) + mind.original = src + if(ticker) + ticker.minds += mind + else + to_world_log("## DEBUG: mind_initialize(): No ticker ready yet! Please inform Carn") + if(!mind.name) mind.name = real_name + mind.current = src + if(player_is_antag(mind)) + src.client.verbs += /client/proc/aooc + +//HUMAN +/mob/living/carbon/human/mind_initialize() + . = ..() + if(!mind.assigned_role) + mind.assigned_role = USELESS_JOB //defualt //VOREStation Edit - Visitor not Assistant + +//slime +/mob/living/simple_mob/slime/mind_initialize() + . = ..() + mind.assigned_role = "slime" + +/mob/living/carbon/alien/larva/mind_initialize() + . = ..() + mind.special_role = "Larva" + +//AI +/mob/living/silicon/ai/mind_initialize() + . = ..() + mind.assigned_role = "AI" + +//BORG +/mob/living/silicon/robot/mind_initialize() + . = ..() + mind.assigned_role = "Cyborg" + +//PAI +/mob/living/silicon/pai/mind_initialize() + . = ..() + mind.assigned_role = "pAI" + mind.special_role = "" + +//Animals +/mob/living/simple_mob/mind_initialize() + . = ..() + mind.assigned_role = "Simple Mob" + +/mob/living/simple_mob/animal/passive/dog/corgi/mind_initialize() + . = ..() + mind.assigned_role = "Corgi" + +/mob/living/simple_mob/construct/shade/mind_initialize() + . = ..() + mind.assigned_role = "Shade" + mind.special_role = "Cultist" + +/mob/living/simple_mob/construct/artificer/mind_initialize() + . = ..() + mind.assigned_role = "Artificer" + mind.special_role = "Cultist" + +/mob/living/simple_mob/construct/wraith/mind_initialize() + . = ..() + mind.assigned_role = "Wraith" + mind.special_role = "Cultist" + +/mob/living/simple_mob/construct/juggernaut/mind_initialize() + . = ..() + mind.assigned_role = "Juggernaut" + mind.special_role = "Cultist" diff --git a/code/datums/modules.dm b/code/datums/modules.dm index 43d25a2e25d..f2193c5e7b7 100644 --- a/code/datums/modules.dm +++ b/code/datums/modules.dm @@ -1,63 +1,63 @@ -// module datum. -// this is per-object instance, and shows the condition of the modules in the object -// actual modules needed is referenced through modulestypes and the object type - -/datum/module - var/status // bits set if working, 0 if broken - var/installed // bits set if installed, 0 if missing - -// moduletypes datum -// this is per-object type, and shows the modules needed for a type of object - -/datum/moduletypes - var/list/modcount = list() // assoc list of the count of modules for a type - - -var/list/modules = list( // global associative list -"/obj/machinery/power/apc" = "card_reader,power_control,id_auth,cell_power,cell_charge") - - -/datum/module/New(var/obj/O) - - var/type = O.type // the type of the creating object - - var/mneed = mods.inmodlist(type) // find if this type has modules defined - - if(!mneed) // not found in module list? - qdel(src) - return - - var/needed = mods.getbitmask(type) // get a bitmask for the number of modules in this object - status = needed - installed = needed - -/datum/moduletypes/proc/addmod(var/type, var/modtextlist) - modules += type // index by type text - modules[type] = modtextlist - -/datum/moduletypes/proc/inmodlist(var/type) - return ("[type]" in modules) - -/datum/moduletypes/proc/getbitmask(var/type) - var/count = modcount["[type]"] - if(count) - return 2**count-1 - - var/modtext = modules["[type]"] - var/num = 1 - var/pos = 1 - - while(1) - pos = findtext(modtext, ",", pos, 0) - if(!pos) - break - else - pos++ - num++ - - modcount += "[type]" - modcount["[type]"] = num - - return 2**num-1 - - +// module datum. +// this is per-object instance, and shows the condition of the modules in the object +// actual modules needed is referenced through modulestypes and the object type + +/datum/module + var/status // bits set if working, 0 if broken + var/installed // bits set if installed, 0 if missing + +// moduletypes datum +// this is per-object type, and shows the modules needed for a type of object + +/datum/moduletypes + var/list/modcount = list() // assoc list of the count of modules for a type + + +var/list/modules = list( // global associative list +"/obj/machinery/power/apc" = "card_reader,power_control,id_auth,cell_power,cell_charge") + + +/datum/module/New(var/obj/O) + + var/type = O.type // the type of the creating object + + var/mneed = mods.inmodlist(type) // find if this type has modules defined + + if(!mneed) // not found in module list? + qdel(src) + return + + var/needed = mods.getbitmask(type) // get a bitmask for the number of modules in this object + status = needed + installed = needed + +/datum/moduletypes/proc/addmod(var/type, var/modtextlist) + modules += type // index by type text + modules[type] = modtextlist + +/datum/moduletypes/proc/inmodlist(var/type) + return ("[type]" in modules) + +/datum/moduletypes/proc/getbitmask(var/type) + var/count = modcount["[type]"] + if(count) + return 2**count-1 + + var/modtext = modules["[type]"] + var/num = 1 + var/pos = 1 + + while(1) + pos = findtext(modtext, ",", pos, 0) + if(!pos) + break + else + pos++ + num++ + + modcount += "[type]" + modcount["[type]"] = num + + return 2**num-1 + + diff --git a/code/datums/observation/z_moved.dm b/code/datums/observation/z_moved.dm index 63b89ba0daf..ba30df374c6 100644 --- a/code/datums/observation/z_moved.dm +++ b/code/datums/observation/z_moved.dm @@ -1,16 +1,16 @@ -// Observer Pattern Implementation: Z_Moved -// Registration type: /atom/movable -// -// Raised when: An /atom/movable instance has changed z-levels by any means. -// -// Arguments that the called proc should expect: -// /atom/movable/moving_instance: The instance that moved -// old_z: The z number before the move. -// new_z: The z number after the move. - - -GLOBAL_DATUM_INIT(z_moved_event, /decl/observ/z_moved, new) - -/decl/observ/z_moved - name = "Z_Moved" - expected_type = /atom/movable +// Observer Pattern Implementation: Z_Moved +// Registration type: /atom/movable +// +// Raised when: An /atom/movable instance has changed z-levels by any means. +// +// Arguments that the called proc should expect: +// /atom/movable/moving_instance: The instance that moved +// old_z: The z number before the move. +// new_z: The z number after the move. + + +GLOBAL_DATUM_INIT(z_moved_event, /decl/observ/z_moved, new) + +/decl/observ/z_moved + name = "Z_Moved" + expected_type = /atom/movable diff --git a/code/datums/repositories/crew.dm b/code/datums/repositories/crew.dm index 997a8357da4..861e02ae4b5 100644 --- a/code/datums/repositories/crew.dm +++ b/code/datums/repositories/crew.dm @@ -1,74 +1,74 @@ -var/global/datum/repository/crew/crew_repository = new() - -/datum/repository/crew - var/list/cache_data - -/datum/repository/crew/New() - cache_data = list() - ..() - -/datum/repository/crew/proc/health_data(var/zLevel) - var/list/crewmembers = list() - if(!zLevel) - return crewmembers - - var/z_level = "[zLevel]" - var/datum/cache_entry/cache_entry = cache_data[z_level] - if(!cache_entry) - cache_entry = new/datum/cache_entry - cache_data[z_level] = cache_entry - - if(world.time < cache_entry.timestamp) - return cache_entry.data - - var/tracked = scan() - for(var/obj/item/clothing/under/C in tracked) - var/turf/pos = get_turf(C) - var/area/B = pos?.loc //VOREStation Add: No sensor in Dorm - if((C.has_sensor) && (pos?.z == zLevel) && (C.sensor_mode != SUIT_SENSOR_OFF) && !(B.block_suit_sensors) && !(is_jammed(C)) && !(is_vore_jammed(C))) //VOREStation Edit - if(istype(C.loc, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = C.loc - if(H.w_uniform != C) - continue - - var/list/crewmemberData = list("dead"=0, "oxy"=-1, "tox"=-1, "fire"=-1, "brute"=-1, "area"="", "x"=-1, "y"=-1, "ref" = "\ref[H]") - - crewmemberData["sensor_type"] = C.sensor_mode - crewmemberData["name"] = H.get_authentification_name(if_no_id="Unknown") - crewmemberData["rank"] = H.get_authentification_rank(if_no_id="Unknown", if_no_job="No Job") - crewmemberData["assignment"] = H.get_assignment(if_no_id="Unknown", if_no_job="No Job") - - if(C.sensor_mode >= SUIT_SENSOR_BINARY) - crewmemberData["dead"] = H.stat == DEAD - - if(C.sensor_mode >= SUIT_SENSOR_VITAL) - crewmemberData["stat"] = H.stat - crewmemberData["oxy"] = round(H.getOxyLoss(), 1) - crewmemberData["tox"] = round(H.getToxLoss(), 1) - crewmemberData["fire"] = round(H.getFireLoss(), 1) - crewmemberData["brute"] = round(H.getBruteLoss(), 1) - - if(C.sensor_mode >= SUIT_SENSOR_TRACKING) - var/area/A = get_area(H) - crewmemberData["area"] = sanitize(A.get_name()) - crewmemberData["x"] = pos.x - crewmemberData["y"] = pos.y - crewmemberData["realZ"] = pos.z - crewmemberData["z"] = using_map.get_zlevel_name(pos.z) - - crewmembers[++crewmembers.len] = crewmemberData - - crewmembers = sortByKey(crewmembers, "name") - cache_entry.timestamp = world.time + 5 SECONDS - cache_entry.data = crewmembers - - return crewmembers - -/datum/repository/crew/proc/scan() - var/list/tracked = list() - for(var/mob/living/carbon/human/H in mob_list) - if(istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/C = H.w_uniform - if (C.has_sensor) - tracked |= C - return tracked +var/global/datum/repository/crew/crew_repository = new() + +/datum/repository/crew + var/list/cache_data + +/datum/repository/crew/New() + cache_data = list() + ..() + +/datum/repository/crew/proc/health_data(var/zLevel) + var/list/crewmembers = list() + if(!zLevel) + return crewmembers + + var/z_level = "[zLevel]" + var/datum/cache_entry/cache_entry = cache_data[z_level] + if(!cache_entry) + cache_entry = new/datum/cache_entry + cache_data[z_level] = cache_entry + + if(world.time < cache_entry.timestamp) + return cache_entry.data + + var/tracked = scan() + for(var/obj/item/clothing/under/C in tracked) + var/turf/pos = get_turf(C) + var/area/B = pos?.loc //VOREStation Add: No sensor in Dorm + if((C.has_sensor) && (pos?.z == zLevel) && (C.sensor_mode != SUIT_SENSOR_OFF) && !(B.block_suit_sensors) && !(is_jammed(C)) && !(is_vore_jammed(C))) //VOREStation Edit + if(istype(C.loc, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = C.loc + if(H.w_uniform != C) + continue + + var/list/crewmemberData = list("dead"=0, "oxy"=-1, "tox"=-1, "fire"=-1, "brute"=-1, "area"="", "x"=-1, "y"=-1, "ref" = "\ref[H]") + + crewmemberData["sensor_type"] = C.sensor_mode + crewmemberData["name"] = H.get_authentification_name(if_no_id="Unknown") + crewmemberData["rank"] = H.get_authentification_rank(if_no_id="Unknown", if_no_job="No Job") + crewmemberData["assignment"] = H.get_assignment(if_no_id="Unknown", if_no_job="No Job") + + if(C.sensor_mode >= SUIT_SENSOR_BINARY) + crewmemberData["dead"] = H.stat == DEAD + + if(C.sensor_mode >= SUIT_SENSOR_VITAL) + crewmemberData["stat"] = H.stat + crewmemberData["oxy"] = round(H.getOxyLoss(), 1) + crewmemberData["tox"] = round(H.getToxLoss(), 1) + crewmemberData["fire"] = round(H.getFireLoss(), 1) + crewmemberData["brute"] = round(H.getBruteLoss(), 1) + + if(C.sensor_mode >= SUIT_SENSOR_TRACKING) + var/area/A = get_area(H) + crewmemberData["area"] = sanitize(A.get_name()) + crewmemberData["x"] = pos.x + crewmemberData["y"] = pos.y + crewmemberData["realZ"] = pos.z + crewmemberData["z"] = using_map.get_zlevel_name(pos.z) + + crewmembers[++crewmembers.len] = crewmemberData + + crewmembers = sortByKey(crewmembers, "name") + cache_entry.timestamp = world.time + 5 SECONDS + cache_entry.data = crewmembers + + return crewmembers + +/datum/repository/crew/proc/scan() + var/list/tracked = list() + for(var/mob/living/carbon/human/H in mob_list) + if(istype(H.w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/C = H.w_uniform + if (C.has_sensor) + tracked |= C + return tracked diff --git a/code/datums/repositories/repository.dm b/code/datums/repositories/repository.dm index 04eee505401..511ea992c80 100644 --- a/code/datums/repositories/repository.dm +++ b/code/datums/repositories/repository.dm @@ -1,19 +1,19 @@ -/repository/New() - return - -/datum/cache_entry - var/timestamp - var/data - -/datum/cache_entry/New() - timestamp = world.time - -/datum/cache_entry/proc/is_valid() - return FALSE - -/datum/cache_entry/valid_until/New(var/valid_duration) - ..() - timestamp += valid_duration - -/datum/cache_entry/valid_until/is_valid() +/repository/New() + return + +/datum/cache_entry + var/timestamp + var/data + +/datum/cache_entry/New() + timestamp = world.time + +/datum/cache_entry/proc/is_valid() + return FALSE + +/datum/cache_entry/valid_until/New(var/valid_duration) + ..() + timestamp += valid_duration + +/datum/cache_entry/valid_until/is_valid() return world.time < timestamp \ No newline at end of file diff --git a/code/datums/soul_link.dm b/code/datums/soul_link.dm index d15049d9246..1a99d170c5b 100644 --- a/code/datums/soul_link.dm +++ b/code/datums/soul_link.dm @@ -1,152 +1,152 @@ -// A datum used to link multiple mobs together in some form. -// The code is from TG, however tweaked to be within the preferred code style. - -/mob/living - var/list/owned_soul_links // Soul links we are the owner of. - var/list/shared_soul_links // Soul links we are a/the sharer of. - -/mob/living/Destroy() - for(var/datum/soul_link/S as anything in owned_soul_links) - S.owner_died(FALSE) - qdel(S) // If the owner is destroy()'d, the soullink is destroy()'d. - owned_soul_links = null - for(var/datum/soul_link/S as anything in shared_soul_links) - S.sharer_died(FALSE) - S.remove_soul_sharer(src) // If a sharer is destroy()'d, they are simply removed. - shared_soul_links = null - return ..() - -// Keeps track of a Mob->Mob (potentially Player->Player) connection. -// Can be used to trigger actions on one party when events happen to another. -// Eg: shared deaths. -// Can be used to form a linked list of mob-hopping. -// Does NOT transfer with minds. -/datum/soul_link - var/mob/living/soul_owner - var/mob/living/soul_sharer - var/id // Optional ID, for tagging and finding specific instances. - -/datum/soul_link/Destroy() - if(soul_owner) - LAZYREMOVE(soul_owner.owned_soul_links, src) - soul_owner = null - if(soul_sharer) - LAZYREMOVE(soul_sharer.shared_soul_links, src) - soul_sharer = null - return ..() - -/datum/soul_link/proc/remove_soul_sharer(mob/living/sharer) - if(soul_sharer == sharer) - soul_sharer = null - LAZYREMOVE(sharer.shared_soul_links, src) - -// Used to assign variables, called primarily by soullink() -// Override this to create more unique soullinks (Eg: 1->Many relationships) -// Return TRUE/FALSE to return the soullink/null in soullink() -/datum/soul_link/proc/parse_args(mob/living/owner, mob/living/sharer) - if(!owner || !sharer) - return FALSE - soul_owner = owner - soul_sharer = sharer - LAZYADD(owner.owned_soul_links, src) - LAZYADD(sharer.shared_soul_links, src) - return TRUE - -// Runs after /living death() -// Override this for content. -/datum/soul_link/proc/owner_died(gibbed, mob/living/owner) - -// Runs after /living death() -// Override this for content. -/datum/soul_link/proc/sharer_died(gibbed, mob/living/owner) - -// Quick-use helper. -/proc/soul_link(typepath, ...) - var/datum/soul_link/S = new typepath() - if(S.parse_args(arglist(args.Copy(2, 0)))) - return S - - -///////////////// -// MULTISHARER // -///////////////// -// Abstract soullink for use with 1 Owner -> Many Sharer setups -/datum/soul_link/multi_sharer - var/list/soul_sharers - -/datum/soul_link/multi_sharer/parse_args(mob/living/owner, list/sharers) - if(!owner || !LAZYLEN(sharers)) - return FALSE - soul_owner = owner - soul_sharers = sharers - LAZYADD(owner.owned_soul_links, src) - for(var/mob/living/L as anything in sharers) - LAZYADD(L.shared_soul_links, src) - return TRUE - -/datum/soul_link/multi_sharer/remove_soul_sharer(mob/living/sharer) - LAZYREMOVE(soul_sharers, sharer) - - -///////////////// -// SHARED FATE // -///////////////// -// When the soulowner dies, the soulsharer dies, and vice versa -// This is intended for two players(or AI) and two mobs - -/datum/soul_link/shared_fate/owner_died(gibbed, mob/living/owner) - if(soul_sharer) - soul_sharer.death(gibbed) - -/datum/soul_link/shared_fate/sharer_died(gibbed, mob/living/sharer) - if(soul_owner) - soul_owner.death(gibbed) - -////////////// -// ONE WAY // -////////////// -// When the soul owner dies, the soul sharer dies, but NOT vice versa. -// This is intended for two players (or AI) and two mobs. - -/datum/soul_link/one_way/owner_died(gibbed, mob/living/owner) - if(soul_sharer) - soul_sharer.dust(FALSE) - -///////////////// -// SHARED BODY // -///////////////// -// When the soulsharer dies, they're placed in the soulowner, who remains alive -// If the soulowner dies, the soulsharer is killed and placed into the soulowner (who is still dying) -// This one is intended for one player moving between many mobs - -/datum/soul_link/shared_body/owner_died(gibbed, mob/living/owner) - if(soul_owner && soul_sharer) - if(soul_sharer.mind) - soul_sharer.mind.transfer_to(soul_owner) - soul_sharer.death(gibbed) - -/datum/soul_link/shared_body/sharer_died(gibbed, mob/living/sharer) - if(soul_owner && soul_sharer && soul_sharer.mind) - soul_sharer.mind.transfer_to(soul_owner) - - - -////////////////////// -// REPLACEMENT POOL // -////////////////////// -// When the owner dies, one of the sharers is placed in the owner's body, fully healed -// Sort of a "winner-stays-on" soullink -// Gibbing ends it immediately - -/datum/soul_link/multi_sharer/replacement_pool/owner_died(gibbed, mob/living/owner) - if(LAZYLEN(soul_sharers) && !gibbed) //let's not put them in some gibs - var/list/souls = shuffle(soul_sharers.Copy()) - for(var/mob/living/L as anything in souls) - if(L.stat != DEAD && L.mind) - L.mind.transfer_to(soul_owner) - soul_owner.revive(TRUE, TRUE) - L.death(FALSE) - -// Lose your claim to the throne! -/datum/soul_link/multi_sharer/replacement_pool/sharer_died(gibbed, mob/living/sharer) - remove_soul_sharer(sharer) +// A datum used to link multiple mobs together in some form. +// The code is from TG, however tweaked to be within the preferred code style. + +/mob/living + var/list/owned_soul_links // Soul links we are the owner of. + var/list/shared_soul_links // Soul links we are a/the sharer of. + +/mob/living/Destroy() + for(var/datum/soul_link/S as anything in owned_soul_links) + S.owner_died(FALSE) + qdel(S) // If the owner is destroy()'d, the soullink is destroy()'d. + owned_soul_links = null + for(var/datum/soul_link/S as anything in shared_soul_links) + S.sharer_died(FALSE) + S.remove_soul_sharer(src) // If a sharer is destroy()'d, they are simply removed. + shared_soul_links = null + return ..() + +// Keeps track of a Mob->Mob (potentially Player->Player) connection. +// Can be used to trigger actions on one party when events happen to another. +// Eg: shared deaths. +// Can be used to form a linked list of mob-hopping. +// Does NOT transfer with minds. +/datum/soul_link + var/mob/living/soul_owner + var/mob/living/soul_sharer + var/id // Optional ID, for tagging and finding specific instances. + +/datum/soul_link/Destroy() + if(soul_owner) + LAZYREMOVE(soul_owner.owned_soul_links, src) + soul_owner = null + if(soul_sharer) + LAZYREMOVE(soul_sharer.shared_soul_links, src) + soul_sharer = null + return ..() + +/datum/soul_link/proc/remove_soul_sharer(mob/living/sharer) + if(soul_sharer == sharer) + soul_sharer = null + LAZYREMOVE(sharer.shared_soul_links, src) + +// Used to assign variables, called primarily by soullink() +// Override this to create more unique soullinks (Eg: 1->Many relationships) +// Return TRUE/FALSE to return the soullink/null in soullink() +/datum/soul_link/proc/parse_args(mob/living/owner, mob/living/sharer) + if(!owner || !sharer) + return FALSE + soul_owner = owner + soul_sharer = sharer + LAZYADD(owner.owned_soul_links, src) + LAZYADD(sharer.shared_soul_links, src) + return TRUE + +// Runs after /living death() +// Override this for content. +/datum/soul_link/proc/owner_died(gibbed, mob/living/owner) + +// Runs after /living death() +// Override this for content. +/datum/soul_link/proc/sharer_died(gibbed, mob/living/owner) + +// Quick-use helper. +/proc/soul_link(typepath, ...) + var/datum/soul_link/S = new typepath() + if(S.parse_args(arglist(args.Copy(2, 0)))) + return S + + +///////////////// +// MULTISHARER // +///////////////// +// Abstract soullink for use with 1 Owner -> Many Sharer setups +/datum/soul_link/multi_sharer + var/list/soul_sharers + +/datum/soul_link/multi_sharer/parse_args(mob/living/owner, list/sharers) + if(!owner || !LAZYLEN(sharers)) + return FALSE + soul_owner = owner + soul_sharers = sharers + LAZYADD(owner.owned_soul_links, src) + for(var/mob/living/L as anything in sharers) + LAZYADD(L.shared_soul_links, src) + return TRUE + +/datum/soul_link/multi_sharer/remove_soul_sharer(mob/living/sharer) + LAZYREMOVE(soul_sharers, sharer) + + +///////////////// +// SHARED FATE // +///////////////// +// When the soulowner dies, the soulsharer dies, and vice versa +// This is intended for two players(or AI) and two mobs + +/datum/soul_link/shared_fate/owner_died(gibbed, mob/living/owner) + if(soul_sharer) + soul_sharer.death(gibbed) + +/datum/soul_link/shared_fate/sharer_died(gibbed, mob/living/sharer) + if(soul_owner) + soul_owner.death(gibbed) + +////////////// +// ONE WAY // +////////////// +// When the soul owner dies, the soul sharer dies, but NOT vice versa. +// This is intended for two players (or AI) and two mobs. + +/datum/soul_link/one_way/owner_died(gibbed, mob/living/owner) + if(soul_sharer) + soul_sharer.dust(FALSE) + +///////////////// +// SHARED BODY // +///////////////// +// When the soulsharer dies, they're placed in the soulowner, who remains alive +// If the soulowner dies, the soulsharer is killed and placed into the soulowner (who is still dying) +// This one is intended for one player moving between many mobs + +/datum/soul_link/shared_body/owner_died(gibbed, mob/living/owner) + if(soul_owner && soul_sharer) + if(soul_sharer.mind) + soul_sharer.mind.transfer_to(soul_owner) + soul_sharer.death(gibbed) + +/datum/soul_link/shared_body/sharer_died(gibbed, mob/living/sharer) + if(soul_owner && soul_sharer && soul_sharer.mind) + soul_sharer.mind.transfer_to(soul_owner) + + + +////////////////////// +// REPLACEMENT POOL // +////////////////////// +// When the owner dies, one of the sharers is placed in the owner's body, fully healed +// Sort of a "winner-stays-on" soullink +// Gibbing ends it immediately + +/datum/soul_link/multi_sharer/replacement_pool/owner_died(gibbed, mob/living/owner) + if(LAZYLEN(soul_sharers) && !gibbed) //let's not put them in some gibs + var/list/souls = shuffle(soul_sharers.Copy()) + for(var/mob/living/L as anything in souls) + if(L.stat != DEAD && L.mind) + L.mind.transfer_to(soul_owner) + soul_owner.revive(TRUE, TRUE) + L.death(FALSE) + +// Lose your claim to the throne! +/datum/soul_link/multi_sharer/replacement_pool/sharer_died(gibbed, mob/living/sharer) + remove_soul_sharer(sharer) diff --git a/code/datums/sun.dm b/code/datums/sun.dm index 69b725f305e..d54ad281387 100644 --- a/code/datums/sun.dm +++ b/code/datums/sun.dm @@ -1,41 +1,41 @@ -/datum/sun - var/angle - var/dx - var/dy - var/rate - var/solar_next_update // last time the sun position was checked and adjusted - -/datum/sun/New() - rate = rand(50,200)/100 // 50% - 200% of standard rotation - if(prob(50)) // same chance to rotate clockwise than counter-clockwise - rate = -rate - angle = rand (0,360) // the station position to the sun is randomised at round start - -// calculate the sun's position given the time of day -// at the standard rate (100%) the angle is increase/decreased by 6 degrees every minute. -// a full rotation thus take a game hour in that case -/datum/sun/proc/calc_position() - angle = (360 + angle + rate * 6) % 360 // increase/decrease the angle to the sun, adjusted by the rate - // now calculate and cache the (dx,dy) increments for line drawing - - var/s = sin(angle) - var/c = cos(angle) - - // Either "abs(s) < abs(c)" or "abs(s) >= abs(c)" - // In both cases, the greater is greater than 0, so, no "if 0" check is needed for the divisions - - if( abs(s) < abs(c)) - - dx = s / abs(c) - dy = c / abs(c) - - else - dx = s/abs(s) - dy = c / abs(s) - - //now tell the solar control computers to update their status and linked devices - for(var/obj/machinery/power/solar_control/SC in GLOB.solars_list) - if(!SC.powernet) - GLOB.solars_list.Remove(SC) - continue - SC.update() +/datum/sun + var/angle + var/dx + var/dy + var/rate + var/solar_next_update // last time the sun position was checked and adjusted + +/datum/sun/New() + rate = rand(50,200)/100 // 50% - 200% of standard rotation + if(prob(50)) // same chance to rotate clockwise than counter-clockwise + rate = -rate + angle = rand (0,360) // the station position to the sun is randomised at round start + +// calculate the sun's position given the time of day +// at the standard rate (100%) the angle is increase/decreased by 6 degrees every minute. +// a full rotation thus take a game hour in that case +/datum/sun/proc/calc_position() + angle = (360 + angle + rate * 6) % 360 // increase/decrease the angle to the sun, adjusted by the rate + // now calculate and cache the (dx,dy) increments for line drawing + + var/s = sin(angle) + var/c = cos(angle) + + // Either "abs(s) < abs(c)" or "abs(s) >= abs(c)" + // In both cases, the greater is greater than 0, so, no "if 0" check is needed for the divisions + + if( abs(s) < abs(c)) + + dx = s / abs(c) + dy = c / abs(c) + + else + dx = s/abs(s) + dy = c / abs(s) + + //now tell the solar control computers to update their status and linked devices + for(var/obj/machinery/power/solar_control/SC in GLOB.solars_list) + if(!SC.powernet) + GLOB.solars_list.Remove(SC) + continue + SC.update() diff --git a/code/datums/underwear/undershirts.dm b/code/datums/underwear/undershirts.dm index a9bdb21bed7..5a34d645e73 100644 --- a/code/datums/underwear/undershirts.dm +++ b/code/datums/underwear/undershirts.dm @@ -1,310 +1,310 @@ -/datum/category_item/underwear/undershirt/none - is_default = TRUE - name = "None" - always_last = TRUE - -/datum/category_item/underwear/undershirt/shirt - name = "Shirt" - icon_state = "undershirt" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shirt_fem - name = "Babydoll shirt" - icon_state = "undershirt_fem" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shirt_long - name = "Longsleeve Shirt" - icon_state = "undershirt_long" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shirt_long_s - name = "Shirt, button-down" - icon_state = "shirt_long_s" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shirt_long_fem - name = "Longsleeve Shirt, feminine" - icon_state = "undershirt_long_fem" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shirt_long_female_s - name = "Button-down Shirt, feminine" - icon_state = "shirt_long_female_s" - has_color = TRUE - - -/datum/category_item/underwear/undershirt/fishnet_simple - name = "Fishnet shirt" - icon_state = "fishnet_simple" - -/datum/category_item/underwear/undershirt/tank_top - name = "Tank top" - icon_state = "tanktop" - has_color = TRUE - -/datum/category_item/underwear/undershirt/tank_top_alt - name = "Tank top, alt" - icon_state = "tanktop_alt" - has_color = TRUE - -/datum/category_item/underwear/undershirt/tank_top_alt_fem - name = "Tank top, alt, feminine" - icon_state = "tanktop_alt_fem" - has_color = TRUE - -/datum/category_item/underwear/undershirt/tank_top_alt_fem_vneck - name = "Tank top, feminine, v-neck" - icon_state = "tanktop_alt_fem_vneck" - has_color = TRUE - -/datum/category_item/underwear/undershirt/tank_cropped_vneck - name = "Tank top, feminine, cropped & v-neck" - icon_state = "tanktop_cropped_vneck" - has_color = TRUE - -/datum/category_item/underwear/undershirt/tank_top_fire - name = "Tank top, fire" - icon_state = "tank_fire_s" - -/datum/category_item/underwear/undershirt/tank_top_fire_fem - name = "Tank top, fire, feminine" - icon_state = "tank_fire_fem_s" - -/datum/category_item/underwear/undershirt/tank_top_rainbow - name = "Tank top, rainbow" - icon_state = "tank_rainbow_s" - -/datum/category_item/underwear/undershirt/tank_top_stripes - name = "Tank top, striped" - icon_state = "tank_stripes_s" - -/datum/category_item/underwear/undershirt/tank_top_sun - name = "Tank top, sun" - icon_state = "tank_sun_s" - -/datum/category_item/underwear/undershirt/shirt_heart - name = "Shirt, heart" - icon_state = "lover_s" - -/datum/category_item/underwear/undershirt/shirt_heart_fem - name = "Shirt, heart, babydoll" - icon_state = "lover_fem_s" - -/datum/category_item/underwear/undershirt/shirt_nt - name = "Shirt, NT" - icon_state = "shirt_nano_s" - -/datum/category_item/underwear/undershirt/shirt_love_nt - name = "Shirt, I<3NT" - icon_state = "ilovent_s" - -/datum/category_item/underwear/undershirt/shirt_love_nt_fem - name = "Shirt, I<3NT, babydoll" - icon_state = "ilovent_fem_s" - -/datum/category_item/underwear/undershirt/shortsleeve_shirt - name = "Shortsleeve shirt" - icon_state = "shortsleeve" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shortsleeve_shirt_fem - name = "Shortsleeve babydoll shirt" - icon_state = "shortsleeve_fem" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shortsleeve_shirt_fem_vneck - name = "Shortsleeve babydoll shirt, v-neck" - icon_state = "shortsleeve_fem_vneck" - has_color = TRUE - -/datum/category_item/underwear/undershirt/polo_shirt - name = "Polo shirt" - icon_state = "polo" - has_color = TRUE - -/datum/category_item/underwear/undershirt/sport_shirt_green - name = "Sport shirt, green" - icon_state = "greenshirtsport_s" - -/datum/category_item/underwear/undershirt/sport_shirt_red - name = "Sport shirt, red" - icon_state = "redshirtsport_s" - -/datum/category_item/underwear/undershirt/sport_shirt_blue - name = "Sport shirt, blue" - icon_state = "blueshirtsport_s" - -/datum/category_item/underwear/undershirt/shirt_tiedye - name = "Shirt, tiedye" - icon_state = "shirt_tiedye_s" - -/datum/category_item/underwear/undershirt/shirt_blue_striped - name = "Shirt, blue stripes" - icon_state = "shirt_stripes_s" - -/datum/category_item/underwear/undershirt/bowling - name = "Bowling Shirt, Red" - icon_state = "bowling" - -/datum/category_item/underwear/undershirt/bowlingp - name = "Bowling Shirt, Pink" - icon_state = "bowlingp" - -/datum/category_item/underwear/undershirt/bowlinga - name = "Bowling Shirt, Aqua" - icon_state = "bowlinga" - -/datum/category_item/underwear/undershirt/bowlingw - name = "Bowling Shirt, White" - icon_state = "bowlingw" - -/datum/category_item/underwear/undershirt/longjon - name = "Long John Shirt" - icon_state = "ljont" - has_color = TRUE - -/datum/category_item/underwear/undershirt/longstripe_black - name = "Longsleeve Striped Shirt, Black" - icon_state = "longstripe" - -/datum/category_item/underwear/undershirt/longstripe_blue - name = "Longsleeve Striped Shirt, Blue" - icon_state = "longstripe_blue" - -/datum/category_item/underwear/undershirt/tiedye - name = "Tiedye Shirt" - icon_state = "tiedye" - -/datum/category_item/underwear/undershirt/longstripe_pink - name = "Longsleeve Striped Shirt, Pink" - icon_state = "longstripe_pink_s" - -/datum/category_item/underwear/undershirt/wingshirt - name = "Pink Wing Shirt" - icon_state = "wing_shirt_s" - -/datum/category_item/underwear/undershirt/pinkblack_tshirt - name = "Pink and Black T-Shirt" - icon_state = "pinkblack_tshirt" - -/datum/category_item/underwear/undershirt/turtle - name = "Turtleneck, Old" - icon_state = "turtleneck_old" - has_color = TRUE - -/datum/category_item/underwear/undershirt/sleevelessturtle - name = "Turtleneck, Sleeveless, Old" - icon_state = "turtleneck_sleeveless_old" - has_color = TRUE - -/datum/category_item/underwear/undershirt/turtleneck - name = "Turtleneck" - icon_state = "turtleneck" - has_color = TRUE - -/datum/category_item/underwear/undershirt/turtleneck_smooth - name = "Turtleneck, Smooth" - icon_state = "turtleneck_smooth" - has_color = TRUE - -/datum/category_item/underwear/undershirt/turtlesleeveless - name = "Turtleneck, Sleeveless" - icon_state = "turtleneck_sleeveless" - has_color = TRUE - -/datum/category_item/underwear/undershirt/leotardturtle - name = "Leotard Turtleneck" - icon_state = "leotard_turtleneck" - has_color = TRUE - -/datum/category_item/underwear/undershirt/leotardturtlesleeveless - name = "Leotard Turtleneck, Sleeveless" - icon_state = "leotard_turtleneck_sleeveless" - has_color = TRUE - -/datum/category_item/underwear/undershirt/dress_shirt_fem - name = "Dress shirt, feminine" - icon_state = "undershirt_dress_fem" - has_color = TRUE - -/datum/category_item/underwear/undershirt/dress_shirt - name = "Dress shirt, masculine" - icon_state = "undershirt_dress" - has_color = TRUE - -/datum/category_item/underwear/undershirt/midriff - name = "Tanktop, midriff" - icon_state = "tank_midriff" - has_color = TRUE - -/datum/category_item/underwear/undershirt/midriffshort - name = "Tanktop, midriff, short" - icon_state = "tank_midriff_short" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shibari - name = "Shibari Binding" - icon_state = "shibari" - has_color = TRUE - -/datum/category_item/underwear/undershirt/leotard - name = "Leotard" - icon_state = "leotard" - has_color = TRUE - -// YW CHANGES START HERE // - -/datum/category_item/underwear/undershirt/alien - name = "Alien Shirt" - icon_state = "shirt_alien" - icon = 'icons/mob/human_yw.dmi' - has_color = FALSE - -/datum/category_item/underwear/undershirt/pogoman - name = "Pogoman Shirt" - icon_state = "pogoman" - icon = 'icons/mob/human_yw.dmi' - has_color = FALSE - -/datum/category_item/underwear/undershirt/question - name = "Question Mark Shirt" - icon_state = "shirt_question" - icon = 'icons/mob/human_yw.dmi' - has_color = FALSE - -/datum/category_item/underwear/undershirt/band - name = "Band Tee" - icon_state = "band" - icon = 'icons/mob/human_yw.dmi' - -/datum/category_item/underwear/undershirt/ss13 - name = "13 Shirt" - icon_state = "shirt_ss13" - icon = 'icons/mob/human_yw.dmi' - - -/datum/category_item/underwear/undershirt/commie - name = "Communism Shirt" - icon_state = "shirt_commie" - icon = 'icons/mob/human_yw.dmi' - - -/datum/category_item/underwear/undershirt/skull - name = "Skull Shirt" - icon_state = "shirt_skull" - icon = 'icons/mob/human_yw.dmi' - - -/datum/category_item/underwear/undershirt/peace - name = "Peace Shirt" - icon = 'icons/mob/human_yw.dmi' - icon_state = "peace" - - -/datum/category_item/underwear/undershirt/bee - name = "Bee Shirt" - icon = 'icons/mob/human_yw.dmi' - icon_state = "bee_shirt" - -// YW CHANGES END HERE // +/datum/category_item/underwear/undershirt/none + is_default = TRUE + name = "None" + always_last = TRUE + +/datum/category_item/underwear/undershirt/shirt + name = "Shirt" + icon_state = "undershirt" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shirt_fem + name = "Babydoll shirt" + icon_state = "undershirt_fem" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shirt_long + name = "Longsleeve Shirt" + icon_state = "undershirt_long" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shirt_long_s + name = "Shirt, button-down" + icon_state = "shirt_long_s" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shirt_long_fem + name = "Longsleeve Shirt, feminine" + icon_state = "undershirt_long_fem" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shirt_long_female_s + name = "Button-down Shirt, feminine" + icon_state = "shirt_long_female_s" + has_color = TRUE + + +/datum/category_item/underwear/undershirt/fishnet_simple + name = "Fishnet shirt" + icon_state = "fishnet_simple" + +/datum/category_item/underwear/undershirt/tank_top + name = "Tank top" + icon_state = "tanktop" + has_color = TRUE + +/datum/category_item/underwear/undershirt/tank_top_alt + name = "Tank top, alt" + icon_state = "tanktop_alt" + has_color = TRUE + +/datum/category_item/underwear/undershirt/tank_top_alt_fem + name = "Tank top, alt, feminine" + icon_state = "tanktop_alt_fem" + has_color = TRUE + +/datum/category_item/underwear/undershirt/tank_top_alt_fem_vneck + name = "Tank top, feminine, v-neck" + icon_state = "tanktop_alt_fem_vneck" + has_color = TRUE + +/datum/category_item/underwear/undershirt/tank_cropped_vneck + name = "Tank top, feminine, cropped & v-neck" + icon_state = "tanktop_cropped_vneck" + has_color = TRUE + +/datum/category_item/underwear/undershirt/tank_top_fire + name = "Tank top, fire" + icon_state = "tank_fire_s" + +/datum/category_item/underwear/undershirt/tank_top_fire_fem + name = "Tank top, fire, feminine" + icon_state = "tank_fire_fem_s" + +/datum/category_item/underwear/undershirt/tank_top_rainbow + name = "Tank top, rainbow" + icon_state = "tank_rainbow_s" + +/datum/category_item/underwear/undershirt/tank_top_stripes + name = "Tank top, striped" + icon_state = "tank_stripes_s" + +/datum/category_item/underwear/undershirt/tank_top_sun + name = "Tank top, sun" + icon_state = "tank_sun_s" + +/datum/category_item/underwear/undershirt/shirt_heart + name = "Shirt, heart" + icon_state = "lover_s" + +/datum/category_item/underwear/undershirt/shirt_heart_fem + name = "Shirt, heart, babydoll" + icon_state = "lover_fem_s" + +/datum/category_item/underwear/undershirt/shirt_nt + name = "Shirt, NT" + icon_state = "shirt_nano_s" + +/datum/category_item/underwear/undershirt/shirt_love_nt + name = "Shirt, I<3NT" + icon_state = "ilovent_s" + +/datum/category_item/underwear/undershirt/shirt_love_nt_fem + name = "Shirt, I<3NT, babydoll" + icon_state = "ilovent_fem_s" + +/datum/category_item/underwear/undershirt/shortsleeve_shirt + name = "Shortsleeve shirt" + icon_state = "shortsleeve" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shortsleeve_shirt_fem + name = "Shortsleeve babydoll shirt" + icon_state = "shortsleeve_fem" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shortsleeve_shirt_fem_vneck + name = "Shortsleeve babydoll shirt, v-neck" + icon_state = "shortsleeve_fem_vneck" + has_color = TRUE + +/datum/category_item/underwear/undershirt/polo_shirt + name = "Polo shirt" + icon_state = "polo" + has_color = TRUE + +/datum/category_item/underwear/undershirt/sport_shirt_green + name = "Sport shirt, green" + icon_state = "greenshirtsport_s" + +/datum/category_item/underwear/undershirt/sport_shirt_red + name = "Sport shirt, red" + icon_state = "redshirtsport_s" + +/datum/category_item/underwear/undershirt/sport_shirt_blue + name = "Sport shirt, blue" + icon_state = "blueshirtsport_s" + +/datum/category_item/underwear/undershirt/shirt_tiedye + name = "Shirt, tiedye" + icon_state = "shirt_tiedye_s" + +/datum/category_item/underwear/undershirt/shirt_blue_striped + name = "Shirt, blue stripes" + icon_state = "shirt_stripes_s" + +/datum/category_item/underwear/undershirt/bowling + name = "Bowling Shirt, Red" + icon_state = "bowling" + +/datum/category_item/underwear/undershirt/bowlingp + name = "Bowling Shirt, Pink" + icon_state = "bowlingp" + +/datum/category_item/underwear/undershirt/bowlinga + name = "Bowling Shirt, Aqua" + icon_state = "bowlinga" + +/datum/category_item/underwear/undershirt/bowlingw + name = "Bowling Shirt, White" + icon_state = "bowlingw" + +/datum/category_item/underwear/undershirt/longjon + name = "Long John Shirt" + icon_state = "ljont" + has_color = TRUE + +/datum/category_item/underwear/undershirt/longstripe_black + name = "Longsleeve Striped Shirt, Black" + icon_state = "longstripe" + +/datum/category_item/underwear/undershirt/longstripe_blue + name = "Longsleeve Striped Shirt, Blue" + icon_state = "longstripe_blue" + +/datum/category_item/underwear/undershirt/tiedye + name = "Tiedye Shirt" + icon_state = "tiedye" + +/datum/category_item/underwear/undershirt/longstripe_pink + name = "Longsleeve Striped Shirt, Pink" + icon_state = "longstripe_pink_s" + +/datum/category_item/underwear/undershirt/wingshirt + name = "Pink Wing Shirt" + icon_state = "wing_shirt_s" + +/datum/category_item/underwear/undershirt/pinkblack_tshirt + name = "Pink and Black T-Shirt" + icon_state = "pinkblack_tshirt" + +/datum/category_item/underwear/undershirt/turtle + name = "Turtleneck, Old" + icon_state = "turtleneck_old" + has_color = TRUE + +/datum/category_item/underwear/undershirt/sleevelessturtle + name = "Turtleneck, Sleeveless, Old" + icon_state = "turtleneck_sleeveless_old" + has_color = TRUE + +/datum/category_item/underwear/undershirt/turtleneck + name = "Turtleneck" + icon_state = "turtleneck" + has_color = TRUE + +/datum/category_item/underwear/undershirt/turtleneck_smooth + name = "Turtleneck, Smooth" + icon_state = "turtleneck_smooth" + has_color = TRUE + +/datum/category_item/underwear/undershirt/turtlesleeveless + name = "Turtleneck, Sleeveless" + icon_state = "turtleneck_sleeveless" + has_color = TRUE + +/datum/category_item/underwear/undershirt/leotardturtle + name = "Leotard Turtleneck" + icon_state = "leotard_turtleneck" + has_color = TRUE + +/datum/category_item/underwear/undershirt/leotardturtlesleeveless + name = "Leotard Turtleneck, Sleeveless" + icon_state = "leotard_turtleneck_sleeveless" + has_color = TRUE + +/datum/category_item/underwear/undershirt/dress_shirt_fem + name = "Dress shirt, feminine" + icon_state = "undershirt_dress_fem" + has_color = TRUE + +/datum/category_item/underwear/undershirt/dress_shirt + name = "Dress shirt, masculine" + icon_state = "undershirt_dress" + has_color = TRUE + +/datum/category_item/underwear/undershirt/midriff + name = "Tanktop, midriff" + icon_state = "tank_midriff" + has_color = TRUE + +/datum/category_item/underwear/undershirt/midriffshort + name = "Tanktop, midriff, short" + icon_state = "tank_midriff_short" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shibari + name = "Shibari Binding" + icon_state = "shibari" + has_color = TRUE + +/datum/category_item/underwear/undershirt/leotard + name = "Leotard" + icon_state = "leotard" + has_color = TRUE + +// YW CHANGES START HERE // + +/datum/category_item/underwear/undershirt/alien + name = "Alien Shirt" + icon_state = "shirt_alien" + icon = 'icons/mob/human_yw.dmi' + has_color = FALSE + +/datum/category_item/underwear/undershirt/pogoman + name = "Pogoman Shirt" + icon_state = "pogoman" + icon = 'icons/mob/human_yw.dmi' + has_color = FALSE + +/datum/category_item/underwear/undershirt/question + name = "Question Mark Shirt" + icon_state = "shirt_question" + icon = 'icons/mob/human_yw.dmi' + has_color = FALSE + +/datum/category_item/underwear/undershirt/band + name = "Band Tee" + icon_state = "band" + icon = 'icons/mob/human_yw.dmi' + +/datum/category_item/underwear/undershirt/ss13 + name = "13 Shirt" + icon_state = "shirt_ss13" + icon = 'icons/mob/human_yw.dmi' + + +/datum/category_item/underwear/undershirt/commie + name = "Communism Shirt" + icon_state = "shirt_commie" + icon = 'icons/mob/human_yw.dmi' + + +/datum/category_item/underwear/undershirt/skull + name = "Skull Shirt" + icon_state = "shirt_skull" + icon = 'icons/mob/human_yw.dmi' + + +/datum/category_item/underwear/undershirt/peace + name = "Peace Shirt" + icon = 'icons/mob/human_yw.dmi' + icon_state = "peace" + + +/datum/category_item/underwear/undershirt/bee + name = "Bee Shirt" + icon = 'icons/mob/human_yw.dmi' + icon_state = "bee_shirt" + +// YW CHANGES END HERE // diff --git a/code/defines/gases.dm b/code/defines/gases.dm index 8b56b5bf45a..6b2a67c2437 100644 --- a/code/defines/gases.dm +++ b/code/defines/gases.dm @@ -1,54 +1,54 @@ -/decl/xgm_gas/oxygen - id = "oxygen" - name = "Oxygen" - specific_heat = 20 // J/(mol*K) - molar_mass = 0.032 // kg/mol - - flags = XGM_GAS_OXIDIZER - -/decl/xgm_gas/nitrogen - id = "nitrogen" - name = "Nitrogen" - specific_heat = 20 // J/(mol*K) - molar_mass = 0.028 // kg/mol - -/decl/xgm_gas/carbon_dioxide - id = "carbon_dioxide" - name = "Carbon Dioxide" - specific_heat = 30 // J/(mol*K) - molar_mass = 0.044 // kg/mol - -/decl/xgm_gas/phoron - id = "phoron" - name = "Phoron" - - //Note that this has a significant impact on TTV yield. - //Because it is so high, any leftover phoron soaks up a lot of heat and drops the yield pressure. - specific_heat = 200 // J/(mol*K) - - //Hypothetical group 14 (same as carbon), period 8 element. - //Using multiplicity rule, it's atomic number is 162 - //and following a N/Z ratio of 1.5, the molar mass of a monatomic gas is: - molar_mass = 0.405 // kg/mol - - tile_overlay = "phoron" - overlay_limit = 0.7 - flags = XGM_GAS_FUEL | XGM_GAS_CONTAMINANT | XGM_GAS_FUSION_FUEL //R-UST port, adding XGM_GAS_FUSION_FUEL flag. - -/decl/xgm_gas/volatile_fuel - id = "volatile_fuel" - name = "Volatile Fuel" - specific_heat = 253 // J/(mol*K) C8H18 gasoline. Isobaric, but good enough. - molar_mass = 0.114 // kg/mol. same. - - flags = XGM_GAS_FUEL - -/decl/xgm_gas/nitrous_oxide - id = "nitrous_oxide" - name = "Nitrous Oxide" - specific_heat = 40 // J/(mol*K) - molar_mass = 0.044 // kg/mol. N2O - - tile_overlay = "nitrous_oxide" - overlay_limit = 1 +/decl/xgm_gas/oxygen + id = "oxygen" + name = "Oxygen" + specific_heat = 20 // J/(mol*K) + molar_mass = 0.032 // kg/mol + + flags = XGM_GAS_OXIDIZER + +/decl/xgm_gas/nitrogen + id = "nitrogen" + name = "Nitrogen" + specific_heat = 20 // J/(mol*K) + molar_mass = 0.028 // kg/mol + +/decl/xgm_gas/carbon_dioxide + id = "carbon_dioxide" + name = "Carbon Dioxide" + specific_heat = 30 // J/(mol*K) + molar_mass = 0.044 // kg/mol + +/decl/xgm_gas/phoron + id = "phoron" + name = "Phoron" + + //Note that this has a significant impact on TTV yield. + //Because it is so high, any leftover phoron soaks up a lot of heat and drops the yield pressure. + specific_heat = 200 // J/(mol*K) + + //Hypothetical group 14 (same as carbon), period 8 element. + //Using multiplicity rule, it's atomic number is 162 + //and following a N/Z ratio of 1.5, the molar mass of a monatomic gas is: + molar_mass = 0.405 // kg/mol + + tile_overlay = "phoron" + overlay_limit = 0.7 + flags = XGM_GAS_FUEL | XGM_GAS_CONTAMINANT | XGM_GAS_FUSION_FUEL //R-UST port, adding XGM_GAS_FUSION_FUEL flag. + +/decl/xgm_gas/volatile_fuel + id = "volatile_fuel" + name = "Volatile Fuel" + specific_heat = 253 // J/(mol*K) C8H18 gasoline. Isobaric, but good enough. + molar_mass = 0.114 // kg/mol. same. + + flags = XGM_GAS_FUEL + +/decl/xgm_gas/nitrous_oxide + id = "nitrous_oxide" + name = "Nitrous Oxide" + specific_heat = 40 // J/(mol*K) + molar_mass = 0.044 // kg/mol. N2O + + tile_overlay = "nitrous_oxide" + overlay_limit = 1 flags = XGM_GAS_OXIDIZER \ No newline at end of file diff --git a/code/defines/obj.dm b/code/defines/obj.dm index c59cc6e714d..beca4354c13 100644 --- a/code/defines/obj.dm +++ b/code/defines/obj.dm @@ -1,150 +1,150 @@ -/obj/structure/signpost - icon = 'icons/obj/stationobjs.dmi' - icon_state = "signpost" - anchored = TRUE - density = TRUE - -/obj/structure/signpost/attackby(obj/item/weapon/W as obj, mob/user as mob) - return attack_hand(user) - -/obj/structure/signpost/attack_hand(mob/user as mob) - if(tgui_alert(user, "Travel back to ss13?","Return?",list("Yes","No")) == "Yes") - if(user.z != src.z) return - user.forceMove(pick(latejoin)) - -/obj/effect/mark - var/mark = "" - icon = 'icons/misc/mark.dmi' - icon_state = "blank" - anchored = TRUE - layer = 99 - mouse_opacity = 0 - unacidable = TRUE//Just to be sure. - -/obj/effect/beam - name = "beam" - density = FALSE - unacidable = TRUE//Just to be sure. - var/def_zone - pass_flags = PASSTABLE - - -/obj/effect/begin - name = "begin" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "begin" - anchored = TRUE - unacidable = TRUE - -/* - * This item is completely unused, but removing it will break something in R&D and Radio code causing PDA and Ninja code to fail on compile - */ - -/var/list/acting_rank_prefixes = list("acting", "temporary", "interim", "provisional") - -/proc/make_list_rank(rank) - for(var/prefix in acting_rank_prefixes) - if(findtext(rank, "[prefix] ", 1, 2+length(prefix))) - return copytext(rank, 2+length(prefix)) - return rank - -/obj/effect/laser - name = "laser" - desc = "IT BURNS!!!" - icon = 'icons/obj/projectiles.dmi' - var/damage = 0.0 - var/range = 10.0 - -/obj/effect/projection - name = "Projection" - desc = "This looks like a projection of something." - anchored = TRUE - -/obj/effect/shut_controller - name = "shut controller" - var/moving = null - var/list/parts = list( ) - -/obj/structure/showcase - name = "Showcase" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "showcase_1" - desc = "A stand with the empty body of a cyborg bolted to it." - density = TRUE - anchored = TRUE - unacidable = TRUE//temporary until I decide whether the borg can be removed. -veyveyr - -/obj/structure/showcase/sign - name = "WARNING: WILDERNESS" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "wilderness1" - desc = "This appears to be a sign warning people that the other side is dangerous. It also says that NanoTrasen cannot guarantee your safety beyond this point." - -/obj/structure/showcase/sign/nt //yw edit - name = "Welcome: Nanotrasen" - icon = 'icons/obj/structures_yw.dmi' - icon_state = "NT_sign" - desc = "This appears to be a sign welcoming Nanotrasen presonnel. It also says that NanoTrasen is the best coporation around." -/obj/structure/showcase/sign/hephaestus //yw edit - name = "Hephaestus Whiskey Station" - icon = 'icons/obj/structures_yw.dmi' - icon_state = "Hephaestus_sign" - desc = "This appears to be a sign welcoming Hephaestus Industry personnel. It seems rather old and partly rusted." - -/obj/structure/showcase/yw/chaplain //yw edit - name = "Strange Bronze Machinery" - icon = 'icons/obj/clockwork_objects.dmi' - icon_state = "mania_motor" - desc = "A strange device made of bronze. It has an unknown purpose." - -/obj/structure/showcase/yw/chaplain2 //yw edit - name = "Strange Bronze Machinery" - icon = 'icons/obj/clockwork_objects.dmi' - icon_state = "obelisk" - desc = "A strange device made of bronze. It has an unknown purpose." - -/obj/structure/showcase/yw/plaque //yw edit - name = "Commerative Plaque" - icon = 'icons/obj/structures_yw32x32.dmi' - icon_state = "plaque" - desc = "A plaque commerating the building efforts of the sleepiest outpost in the sector, Yawn Wider." - density = 0 - -/obj/item/mouse_drag_pointer = MOUSE_ACTIVE_POINTER - -/obj/item/weapon/beach_ball - icon = 'icons/misc/beach.dmi' - icon_state = "beachball" - name = "beach ball" - density = FALSE - anchored = FALSE - w_class = ITEMSIZE_LARGE - force = 0.0 - throwforce = 0.0 - throw_speed = 1 - throw_range = 20 - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' - -/obj/item/weapon/beach_ball/afterattack(atom/target as mob|obj|turf|area, mob/user as mob) - user.drop_item() - src.throw_at(target, throw_range, throw_speed, user) - -/obj/item/weapon/beach_ball/dodgeball - icon = 'icons/obj/balls_vr.dmi' - icon_state = "dodgeball" - item_state = "dodgeball" - item_icons = list(slot_l_hand_str = 'icons/mob/items/lefthand_balls_vr.dmi', slot_r_hand_str = 'icons/mob/items/righthand_balls_vr.dmi') - name = "dodgeball" - desc = "Think fast, chucklenuts!" - w_class = ITEMSIZE_LARGE //Stops people from hiding it in their bags/pockets - force = 0.1 - throwforce = 0.1 - throw_speed = 5 - throw_range = 15 - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' - hitsound = 'sound/weapons/dodgeball.ogg' - -/obj/effect/spawner - name = "object spawner" +/obj/structure/signpost + icon = 'icons/obj/stationobjs.dmi' + icon_state = "signpost" + anchored = TRUE + density = TRUE + +/obj/structure/signpost/attackby(obj/item/weapon/W as obj, mob/user as mob) + return attack_hand(user) + +/obj/structure/signpost/attack_hand(mob/user as mob) + if(tgui_alert(user, "Travel back to ss13?","Return?",list("Yes","No")) == "Yes") + if(user.z != src.z) return + user.forceMove(pick(latejoin)) + +/obj/effect/mark + var/mark = "" + icon = 'icons/misc/mark.dmi' + icon_state = "blank" + anchored = TRUE + layer = 99 + mouse_opacity = 0 + unacidable = TRUE//Just to be sure. + +/obj/effect/beam + name = "beam" + density = FALSE + unacidable = TRUE//Just to be sure. + var/def_zone + pass_flags = PASSTABLE + + +/obj/effect/begin + name = "begin" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "begin" + anchored = TRUE + unacidable = TRUE + +/* + * This item is completely unused, but removing it will break something in R&D and Radio code causing PDA and Ninja code to fail on compile + */ + +/var/list/acting_rank_prefixes = list("acting", "temporary", "interim", "provisional") + +/proc/make_list_rank(rank) + for(var/prefix in acting_rank_prefixes) + if(findtext(rank, "[prefix] ", 1, 2+length(prefix))) + return copytext(rank, 2+length(prefix)) + return rank + +/obj/effect/laser + name = "laser" + desc = "IT BURNS!!!" + icon = 'icons/obj/projectiles.dmi' + var/damage = 0.0 + var/range = 10.0 + +/obj/effect/projection + name = "Projection" + desc = "This looks like a projection of something." + anchored = TRUE + +/obj/effect/shut_controller + name = "shut controller" + var/moving = null + var/list/parts = list( ) + +/obj/structure/showcase + name = "Showcase" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "showcase_1" + desc = "A stand with the empty body of a cyborg bolted to it." + density = TRUE + anchored = TRUE + unacidable = TRUE//temporary until I decide whether the borg can be removed. -veyveyr + +/obj/structure/showcase/sign + name = "WARNING: WILDERNESS" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "wilderness1" + desc = "This appears to be a sign warning people that the other side is dangerous. It also says that NanoTrasen cannot guarantee your safety beyond this point." + +/obj/structure/showcase/sign/nt //yw edit + name = "Welcome: Nanotrasen" + icon = 'icons/obj/structures_yw.dmi' + icon_state = "NT_sign" + desc = "This appears to be a sign welcoming Nanotrasen presonnel. It also says that NanoTrasen is the best coporation around." +/obj/structure/showcase/sign/hephaestus //yw edit + name = "Hephaestus Whiskey Station" + icon = 'icons/obj/structures_yw.dmi' + icon_state = "Hephaestus_sign" + desc = "This appears to be a sign welcoming Hephaestus Industry personnel. It seems rather old and partly rusted." + +/obj/structure/showcase/yw/chaplain //yw edit + name = "Strange Bronze Machinery" + icon = 'icons/obj/clockwork_objects.dmi' + icon_state = "mania_motor" + desc = "A strange device made of bronze. It has an unknown purpose." + +/obj/structure/showcase/yw/chaplain2 //yw edit + name = "Strange Bronze Machinery" + icon = 'icons/obj/clockwork_objects.dmi' + icon_state = "obelisk" + desc = "A strange device made of bronze. It has an unknown purpose." + +/obj/structure/showcase/yw/plaque //yw edit + name = "Commerative Plaque" + icon = 'icons/obj/structures_yw32x32.dmi' + icon_state = "plaque" + desc = "A plaque commerating the building efforts of the sleepiest outpost in the sector, Yawn Wider." + density = 0 + +/obj/item/mouse_drag_pointer = MOUSE_ACTIVE_POINTER + +/obj/item/weapon/beach_ball + icon = 'icons/misc/beach.dmi' + icon_state = "beachball" + name = "beach ball" + density = FALSE + anchored = FALSE + w_class = ITEMSIZE_LARGE + force = 0.0 + throwforce = 0.0 + throw_speed = 1 + throw_range = 20 + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' + +/obj/item/weapon/beach_ball/afterattack(atom/target as mob|obj|turf|area, mob/user as mob) + user.drop_item() + src.throw_at(target, throw_range, throw_speed, user) + +/obj/item/weapon/beach_ball/dodgeball + icon = 'icons/obj/balls_vr.dmi' + icon_state = "dodgeball" + item_state = "dodgeball" + item_icons = list(slot_l_hand_str = 'icons/mob/items/lefthand_balls_vr.dmi', slot_r_hand_str = 'icons/mob/items/righthand_balls_vr.dmi') + name = "dodgeball" + desc = "Think fast, chucklenuts!" + w_class = ITEMSIZE_LARGE //Stops people from hiding it in their bags/pockets + force = 0.1 + throwforce = 0.1 + throw_speed = 5 + throw_range = 15 + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' + hitsound = 'sound/weapons/dodgeball.ogg' + +/obj/effect/spawner + name = "object spawner" diff --git a/code/defines/procs/AStar.dm b/code/defines/procs/AStar.dm index c29ffef73fb..de99cb0d684 100644 --- a/code/defines/procs/AStar.dm +++ b/code/defines/procs/AStar.dm @@ -1,181 +1,181 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/* -A Star pathfinding algorithm -Returns a list of tiles forming a path from A to B, taking dense objects as well as walls, and the orientation of -windows along the route into account. -Use: -your_list = AStar(start location, end location, adjacent turf proc, distance proc) -For the adjacent turf proc i wrote: -/turf/proc/AdjacentTurfs -And for the distance one i wrote: -/turf/proc/Distance -So an example use might be: - -src.path_list = AStar(src.loc, target.loc, /turf/proc/AdjacentTurfs, /turf/proc/Distance) - -Note: The path is returned starting at the END node, so i wrote reverselist to reverse it for ease of use. - -src.path_list = reverselist(src.pathlist) - -Then to start on the path, all you need to do it: -Step_to(src, src.path_list[1]) -src.path_list -= src.path_list[1] or equivilent to remove that node from the list. - -Optional extras to add on (in order): -MaxNodes: The maximum number of nodes the returned path can be (0 = infinite) -Maxnodedepth: The maximum number of nodes to search (default: 30, 0 = infinite) -Mintargetdist: Minimum distance to the target before path returns, could be used to get -near a target, but not right to it - for an AI mob with a gun, for example. -Minnodedist: Minimum number of nodes to return in the path, could be used to give a path a minimum -length to avoid portals or something i guess?? Not that they're counted right now but w/e. -*/ - -// Modified to provide ID argument - supplied to 'adjacent' proc, defaults to null -// Used for checking if route exists through a door which can be opened - -// Also added 'exclude' turf to avoid travelling over; defaults to null - - -/PriorityQueue - var/list/queue - var/comparison_function - -/PriorityQueue/New(compare) - queue = list() - comparison_function = compare - -/PriorityQueue/proc/IsEmpty() - return !queue.len - -/PriorityQueue/proc/Enqueue(var/data) - queue.Add(data) - var/index = queue.len - - //From what I can tell, this automagically sorts the added data into the correct location. - while(index > 2 && call(comparison_function)(queue[index / 2], queue[index]) > 0) - queue.Swap(index, index / 2) - index /= 2 - -/PriorityQueue/proc/Dequeue() - if(!queue.len) - return 0 - return Remove(1) - -/PriorityQueue/proc/Remove(var/index) - if(index > queue.len) - return 0 - - var/thing = queue[index] - queue.Swap(index, queue.len) - queue.Cut(queue.len) - if(index < queue.len) - FixQueue(index) - return thing - -/PriorityQueue/proc/FixQueue(var/index) - var/child = 2 * index - var/item = queue[index] - - while(child <= queue.len) - if(child < queue.len && call(comparison_function)(queue[child], queue[child + 1]) > 0) - child++ - if(call(comparison_function)(item, queue[child]) > 0) - queue[index] = queue[child] - index = child - else - break - child = 2 * index - queue[index] = item - -/PriorityQueue/proc/List() - return queue.Copy() - -/PriorityQueue/proc/Length() - return queue.len - -/PriorityQueue/proc/RemoveItem(data) - var/index = queue.Find(data) - if(index) - return Remove(index) - -/PathNode - var/datum/position - var/PathNode/previous_node - - var/best_estimated_cost - var/estimated_cost - var/known_cost - var/cost - var/nodes_traversed - -/PathNode/New(_position, _previous_node, _known_cost, _cost, _nodes_traversed) - position = _position - previous_node = _previous_node - - known_cost = _known_cost - cost = _cost - estimated_cost = cost + known_cost - - best_estimated_cost = estimated_cost - nodes_traversed = _nodes_traversed - -/proc/PathWeightCompare(PathNode/a, PathNode/b) - return a.estimated_cost - b.estimated_cost - -/proc/AStar(var/start, var/end, var/adjacent, var/dist, var/max_nodes, var/max_node_depth = 30, var/min_target_dist = 0, var/min_node_dist, var/id, var/datum/exclude) - var/PriorityQueue/open = new /PriorityQueue(/proc/PathWeightCompare) - var/list/closed = list() - var/list/path - var/list/path_node_by_position = list() - start = get_turf(start) - if(!start) - return 0 - - open.Enqueue(new /PathNode(start, null, 0, call(start, dist)(end), 0)) - - while(!open.IsEmpty() && !path) - var/PathNode/current = open.Dequeue() - closed.Add(current.position) - - if(current.position == end || call(current.position, dist)(end) <= min_target_dist) - path = new /list(current.nodes_traversed + 1) - path[path.len] = current.position - var/index = path.len - 1 - - while(current.previous_node) - current = current.previous_node - path[index--] = current.position - break - - if(min_node_dist && max_node_depth) - if(call(current.position, min_node_dist)(end) + current.nodes_traversed >= max_node_depth) - continue - - if(max_node_depth) - if(current.nodes_traversed >= max_node_depth) - continue - - for(var/datum/datum in call(current.position, adjacent)(id)) - if(datum == exclude) - continue - - var/best_estimated_cost = current.estimated_cost + call(current.position, dist)(datum) - - //handle removal of sub-par positions - if(datum in path_node_by_position) - var/PathNode/target = path_node_by_position[datum] - if(target.best_estimated_cost) - if(best_estimated_cost + call(datum, dist)(end) < target.best_estimated_cost) - open.RemoveItem(target) - else - continue - - var/PathNode/next_node = new (datum, current, best_estimated_cost, call(datum, dist)(end), current.nodes_traversed + 1) - path_node_by_position[datum] = next_node - open.Enqueue(next_node) - - if(max_nodes && open.Length() > max_nodes) - open.Remove(open.Length()) - - return path +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/* +A Star pathfinding algorithm +Returns a list of tiles forming a path from A to B, taking dense objects as well as walls, and the orientation of +windows along the route into account. +Use: +your_list = AStar(start location, end location, adjacent turf proc, distance proc) +For the adjacent turf proc i wrote: +/turf/proc/AdjacentTurfs +And for the distance one i wrote: +/turf/proc/Distance +So an example use might be: + +src.path_list = AStar(src.loc, target.loc, /turf/proc/AdjacentTurfs, /turf/proc/Distance) + +Note: The path is returned starting at the END node, so i wrote reverselist to reverse it for ease of use. + +src.path_list = reverselist(src.pathlist) + +Then to start on the path, all you need to do it: +Step_to(src, src.path_list[1]) +src.path_list -= src.path_list[1] or equivilent to remove that node from the list. + +Optional extras to add on (in order): +MaxNodes: The maximum number of nodes the returned path can be (0 = infinite) +Maxnodedepth: The maximum number of nodes to search (default: 30, 0 = infinite) +Mintargetdist: Minimum distance to the target before path returns, could be used to get +near a target, but not right to it - for an AI mob with a gun, for example. +Minnodedist: Minimum number of nodes to return in the path, could be used to give a path a minimum +length to avoid portals or something i guess?? Not that they're counted right now but w/e. +*/ + +// Modified to provide ID argument - supplied to 'adjacent' proc, defaults to null +// Used for checking if route exists through a door which can be opened + +// Also added 'exclude' turf to avoid travelling over; defaults to null + + +/PriorityQueue + var/list/queue + var/comparison_function + +/PriorityQueue/New(compare) + queue = list() + comparison_function = compare + +/PriorityQueue/proc/IsEmpty() + return !queue.len + +/PriorityQueue/proc/Enqueue(var/data) + queue.Add(data) + var/index = queue.len + + //From what I can tell, this automagically sorts the added data into the correct location. + while(index > 2 && call(comparison_function)(queue[index / 2], queue[index]) > 0) + queue.Swap(index, index / 2) + index /= 2 + +/PriorityQueue/proc/Dequeue() + if(!queue.len) + return 0 + return Remove(1) + +/PriorityQueue/proc/Remove(var/index) + if(index > queue.len) + return 0 + + var/thing = queue[index] + queue.Swap(index, queue.len) + queue.Cut(queue.len) + if(index < queue.len) + FixQueue(index) + return thing + +/PriorityQueue/proc/FixQueue(var/index) + var/child = 2 * index + var/item = queue[index] + + while(child <= queue.len) + if(child < queue.len && call(comparison_function)(queue[child], queue[child + 1]) > 0) + child++ + if(call(comparison_function)(item, queue[child]) > 0) + queue[index] = queue[child] + index = child + else + break + child = 2 * index + queue[index] = item + +/PriorityQueue/proc/List() + return queue.Copy() + +/PriorityQueue/proc/Length() + return queue.len + +/PriorityQueue/proc/RemoveItem(data) + var/index = queue.Find(data) + if(index) + return Remove(index) + +/PathNode + var/datum/position + var/PathNode/previous_node + + var/best_estimated_cost + var/estimated_cost + var/known_cost + var/cost + var/nodes_traversed + +/PathNode/New(_position, _previous_node, _known_cost, _cost, _nodes_traversed) + position = _position + previous_node = _previous_node + + known_cost = _known_cost + cost = _cost + estimated_cost = cost + known_cost + + best_estimated_cost = estimated_cost + nodes_traversed = _nodes_traversed + +/proc/PathWeightCompare(PathNode/a, PathNode/b) + return a.estimated_cost - b.estimated_cost + +/proc/AStar(var/start, var/end, var/adjacent, var/dist, var/max_nodes, var/max_node_depth = 30, var/min_target_dist = 0, var/min_node_dist, var/id, var/datum/exclude) + var/PriorityQueue/open = new /PriorityQueue(/proc/PathWeightCompare) + var/list/closed = list() + var/list/path + var/list/path_node_by_position = list() + start = get_turf(start) + if(!start) + return 0 + + open.Enqueue(new /PathNode(start, null, 0, call(start, dist)(end), 0)) + + while(!open.IsEmpty() && !path) + var/PathNode/current = open.Dequeue() + closed.Add(current.position) + + if(current.position == end || call(current.position, dist)(end) <= min_target_dist) + path = new /list(current.nodes_traversed + 1) + path[path.len] = current.position + var/index = path.len - 1 + + while(current.previous_node) + current = current.previous_node + path[index--] = current.position + break + + if(min_node_dist && max_node_depth) + if(call(current.position, min_node_dist)(end) + current.nodes_traversed >= max_node_depth) + continue + + if(max_node_depth) + if(current.nodes_traversed >= max_node_depth) + continue + + for(var/datum/datum in call(current.position, adjacent)(id)) + if(datum == exclude) + continue + + var/best_estimated_cost = current.estimated_cost + call(current.position, dist)(datum) + + //handle removal of sub-par positions + if(datum in path_node_by_position) + var/PathNode/target = path_node_by_position[datum] + if(target.best_estimated_cost) + if(best_estimated_cost + call(datum, dist)(end) < target.best_estimated_cost) + open.RemoveItem(target) + else + continue + + var/PathNode/next_node = new (datum, current, best_estimated_cost, call(datum, dist)(end), current.nodes_traversed + 1) + path_node_by_position[datum] = next_node + open.Enqueue(next_node) + + if(max_nodes && open.Length() > max_nodes) + open.Remove(open.Length()) + + return path diff --git a/code/defines/procs/dbcore.dm b/code/defines/procs/dbcore.dm index e0f8ab94af9..3a89c9ef8c4 100644 --- a/code/defines/procs/dbcore.dm +++ b/code/defines/procs/dbcore.dm @@ -1,208 +1,208 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -//cursors -#define Default_Cursor 0 -#define Client_Cursor 1 -#define Server_Cursor 2 -//conversions -#define TEXT_CONV 1 -#define RSC_FILE_CONV 2 -#define NUMBER_CONV 3 -//column flag values: -#define IS_NUMERIC 1 -#define IS_BINARY 2 -#define IS_NOT_NULL 4 -#define IS_PRIMARY_KEY 8 -#define IS_UNSIGNED 16 -//types -#define TINYINT 1 -#define SMALLINT 2 -#define MEDIUMINT 3 -#define INTEGER 4 -#define BIGINT 5 -#define DECIMAL 6 -#define FLOAT 7 -#define DOUBLE 8 -#define DATE 9 -#define DATETIME 10 -#define TIMESTAMP 11 -#define TIME 12 -#define STRING 13 -#define BLOB 14 -// TODO: Investigate more recent type additions and see if I can handle them. - Nadrew - - -// Deprecated! See global.dm for new configuration vars -/* -var/DB_SERVER = "" // This is the location of your MySQL server (localhost is USUALLY fine) -var/DB_PORT = 3306 // This is the port your MySQL server is running on (3306 is the default) -*/ - -/DBConnection - var/_db_con // This variable contains a reference to the actual database connection. - var/dbi // This variable is a string containing the DBI MySQL requires. - var/user // This variable contains the username data. - var/password // This variable contains the password data. - var/default_cursor // This contains the default database cursor data. - // - var/server = "" - var/port = 3306 - -/DBConnection/New(dbi_handler,username,password_handler,cursor_handler) - src.dbi = dbi_handler - src.user = username - src.password = password_handler - src.default_cursor = cursor_handler - _db_con = _dm_db_new_con() - -/DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler) - if(!config.sql_enabled) - return 0 - if(!src) return 0 - cursor_handler = src.default_cursor - if(!cursor_handler) cursor_handler = Default_Cursor - return _dm_db_connect(_db_con,dbi_handler,user_handler,password_handler,cursor_handler,null) - -/DBConnection/proc/Disconnect() return _dm_db_close(_db_con) - -/DBConnection/proc/IsConnected() - if(!config.sql_enabled) return 0 - var/success = _dm_db_is_connected(_db_con) - return success - -/DBConnection/proc/Quote(str) return _dm_db_quote(_db_con,str) - -/DBConnection/proc/ErrorMsg() return _dm_db_error_msg(_db_con) -/DBConnection/proc/SelectDB(database_name,dbi) - if(IsConnected()) Disconnect() - //return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[DB_SERVER]:[DB_PORT]"]",user,password) - return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[sqladdress]:[sqlport]"]",user,password) -/DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) return new/DBQuery(sql_query,src,cursor_handler) - - -/DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler) - if(sql_query) src.sql = sql_query - if(connection_handler) src.db_connection = connection_handler - if(cursor_handler) src.default_cursor = cursor_handler - _db_query = _dm_db_new_query() - return ..() - - -/DBQuery - var/sql // The sql query being executed. - var/default_cursor - var/list/columns //list of DB Columns populated by Columns() - var/list/conversions - var/list/item[0] //list of data values populated by NextRow() - - var/DBConnection/db_connection - var/_db_query - -/DBQuery/proc/Connect(DBConnection/connection_handler) src.db_connection = connection_handler - -/DBQuery/proc/Execute(sql_query=src.sql,cursor_handler=default_cursor) - Close() - return _dm_db_execute(_db_query,sql_query,db_connection._db_con,cursor_handler,null) - -/DBQuery/proc/NextRow() return _dm_db_next_row(_db_query,item,conversions) - -/DBQuery/proc/RowsAffected() return _dm_db_rows_affected(_db_query) - -/DBQuery/proc/RowCount() return _dm_db_row_count(_db_query) - -/DBQuery/proc/ErrorMsg() return _dm_db_error_msg(_db_query) - -/DBQuery/proc/Columns() - if(!columns) - columns = _dm_db_columns(_db_query,/DBColumn) - return columns - -/DBQuery/proc/GetRowData() - var/list/columns = Columns() - var/list/results - if(columns.len) - results = list() - for(var/C in columns) - results+=C - var/DBColumn/cur_col = columns[C] - results[C] = src.item[(cur_col.position+1)] - return results - -/DBQuery/proc/Close() - item.len = 0 - columns = null - conversions = null - return _dm_db_close(_db_query) - -/DBQuery/proc/Quote(str) - return db_connection.Quote(str) - -/DBQuery/proc/SetConversion(column,conversion) - if(istext(column)) column = columns.Find(column) - if(!conversions) conversions = new/list(column) - else if(conversions.len < column) conversions.len = column - conversions[column] = conversion - - -/DBColumn - var/name - var/table - var/position //1-based index into item data - var/sql_type - var/flags - var/length - var/max_length - -/DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handler,length_handler,max_length_handler) - src.name = name_handler - src.table = table_handler - src.position = position_handler - src.sql_type = type_handler - src.flags = flag_handler - src.length = length_handler - src.max_length = max_length_handler - return ..() - - -/DBColumn/proc/SqlTypeName(type_handler=src.sql_type) - switch(type_handler) - if(TINYINT) return "TINYINT" - if(SMALLINT) return "SMALLINT" - if(MEDIUMINT) return "MEDIUMINT" - if(INTEGER) return "INTEGER" - if(BIGINT) return "BIGINT" - if(FLOAT) return "FLOAT" - if(DOUBLE) return "DOUBLE" - if(DATE) return "DATE" - if(DATETIME) return "DATETIME" - if(TIMESTAMP) return "TIMESTAMP" - if(TIME) return "TIME" - if(STRING) return "STRING" - if(BLOB) return "BLOB" - - -#undef Default_Cursor -#undef Client_Cursor -#undef Server_Cursor -#undef TEXT_CONV -#undef RSC_FILE_CONV -#undef NUMBER_CONV -#undef IS_NUMERIC -#undef IS_BINARY -#undef IS_NOT_NULL -#undef IS_PRIMARY_KEY -#undef IS_UNSIGNED -#undef TINYINT -#undef SMALLINT -#undef MEDIUMINT -#undef INTEGER -#undef BIGINT -#undef DECIMAL -#undef FLOAT -#undef DOUBLE -#undef DATE -#undef DATETIME -#undef TIMESTAMP -#undef TIME -#undef STRING -#undef BLOB +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +//cursors +#define Default_Cursor 0 +#define Client_Cursor 1 +#define Server_Cursor 2 +//conversions +#define TEXT_CONV 1 +#define RSC_FILE_CONV 2 +#define NUMBER_CONV 3 +//column flag values: +#define IS_NUMERIC 1 +#define IS_BINARY 2 +#define IS_NOT_NULL 4 +#define IS_PRIMARY_KEY 8 +#define IS_UNSIGNED 16 +//types +#define TINYINT 1 +#define SMALLINT 2 +#define MEDIUMINT 3 +#define INTEGER 4 +#define BIGINT 5 +#define DECIMAL 6 +#define FLOAT 7 +#define DOUBLE 8 +#define DATE 9 +#define DATETIME 10 +#define TIMESTAMP 11 +#define TIME 12 +#define STRING 13 +#define BLOB 14 +// TODO: Investigate more recent type additions and see if I can handle them. - Nadrew + + +// Deprecated! See global.dm for new configuration vars +/* +var/DB_SERVER = "" // This is the location of your MySQL server (localhost is USUALLY fine) +var/DB_PORT = 3306 // This is the port your MySQL server is running on (3306 is the default) +*/ + +/DBConnection + var/_db_con // This variable contains a reference to the actual database connection. + var/dbi // This variable is a string containing the DBI MySQL requires. + var/user // This variable contains the username data. + var/password // This variable contains the password data. + var/default_cursor // This contains the default database cursor data. + // + var/server = "" + var/port = 3306 + +/DBConnection/New(dbi_handler,username,password_handler,cursor_handler) + src.dbi = dbi_handler + src.user = username + src.password = password_handler + src.default_cursor = cursor_handler + _db_con = _dm_db_new_con() + +/DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler) + if(!config.sql_enabled) + return 0 + if(!src) return 0 + cursor_handler = src.default_cursor + if(!cursor_handler) cursor_handler = Default_Cursor + return _dm_db_connect(_db_con,dbi_handler,user_handler,password_handler,cursor_handler,null) + +/DBConnection/proc/Disconnect() return _dm_db_close(_db_con) + +/DBConnection/proc/IsConnected() + if(!config.sql_enabled) return 0 + var/success = _dm_db_is_connected(_db_con) + return success + +/DBConnection/proc/Quote(str) return _dm_db_quote(_db_con,str) + +/DBConnection/proc/ErrorMsg() return _dm_db_error_msg(_db_con) +/DBConnection/proc/SelectDB(database_name,dbi) + if(IsConnected()) Disconnect() + //return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[DB_SERVER]:[DB_PORT]"]",user,password) + return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[sqladdress]:[sqlport]"]",user,password) +/DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) return new/DBQuery(sql_query,src,cursor_handler) + + +/DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler) + if(sql_query) src.sql = sql_query + if(connection_handler) src.db_connection = connection_handler + if(cursor_handler) src.default_cursor = cursor_handler + _db_query = _dm_db_new_query() + return ..() + + +/DBQuery + var/sql // The sql query being executed. + var/default_cursor + var/list/columns //list of DB Columns populated by Columns() + var/list/conversions + var/list/item[0] //list of data values populated by NextRow() + + var/DBConnection/db_connection + var/_db_query + +/DBQuery/proc/Connect(DBConnection/connection_handler) src.db_connection = connection_handler + +/DBQuery/proc/Execute(sql_query=src.sql,cursor_handler=default_cursor) + Close() + return _dm_db_execute(_db_query,sql_query,db_connection._db_con,cursor_handler,null) + +/DBQuery/proc/NextRow() return _dm_db_next_row(_db_query,item,conversions) + +/DBQuery/proc/RowsAffected() return _dm_db_rows_affected(_db_query) + +/DBQuery/proc/RowCount() return _dm_db_row_count(_db_query) + +/DBQuery/proc/ErrorMsg() return _dm_db_error_msg(_db_query) + +/DBQuery/proc/Columns() + if(!columns) + columns = _dm_db_columns(_db_query,/DBColumn) + return columns + +/DBQuery/proc/GetRowData() + var/list/columns = Columns() + var/list/results + if(columns.len) + results = list() + for(var/C in columns) + results+=C + var/DBColumn/cur_col = columns[C] + results[C] = src.item[(cur_col.position+1)] + return results + +/DBQuery/proc/Close() + item.len = 0 + columns = null + conversions = null + return _dm_db_close(_db_query) + +/DBQuery/proc/Quote(str) + return db_connection.Quote(str) + +/DBQuery/proc/SetConversion(column,conversion) + if(istext(column)) column = columns.Find(column) + if(!conversions) conversions = new/list(column) + else if(conversions.len < column) conversions.len = column + conversions[column] = conversion + + +/DBColumn + var/name + var/table + var/position //1-based index into item data + var/sql_type + var/flags + var/length + var/max_length + +/DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handler,length_handler,max_length_handler) + src.name = name_handler + src.table = table_handler + src.position = position_handler + src.sql_type = type_handler + src.flags = flag_handler + src.length = length_handler + src.max_length = max_length_handler + return ..() + + +/DBColumn/proc/SqlTypeName(type_handler=src.sql_type) + switch(type_handler) + if(TINYINT) return "TINYINT" + if(SMALLINT) return "SMALLINT" + if(MEDIUMINT) return "MEDIUMINT" + if(INTEGER) return "INTEGER" + if(BIGINT) return "BIGINT" + if(FLOAT) return "FLOAT" + if(DOUBLE) return "DOUBLE" + if(DATE) return "DATE" + if(DATETIME) return "DATETIME" + if(TIMESTAMP) return "TIMESTAMP" + if(TIME) return "TIME" + if(STRING) return "STRING" + if(BLOB) return "BLOB" + + +#undef Default_Cursor +#undef Client_Cursor +#undef Server_Cursor +#undef TEXT_CONV +#undef RSC_FILE_CONV +#undef NUMBER_CONV +#undef IS_NUMERIC +#undef IS_BINARY +#undef IS_NOT_NULL +#undef IS_PRIMARY_KEY +#undef IS_UNSIGNED +#undef TINYINT +#undef SMALLINT +#undef MEDIUMINT +#undef INTEGER +#undef BIGINT +#undef DECIMAL +#undef FLOAT +#undef DOUBLE +#undef DATE +#undef DATETIME +#undef TIMESTAMP +#undef TIME +#undef STRING +#undef BLOB diff --git a/code/defines/procs/statistics.dm b/code/defines/procs/statistics.dm index 23c2025b847..6ce55534ded 100644 --- a/code/defines/procs/statistics.dm +++ b/code/defines/procs/statistics.dm @@ -1,145 +1,145 @@ -/proc/sql_poll_population() - if(!sqllogging) - return - var/admincount = GLOB.admins.len - var/playercount = 0 - for(var/mob/M in player_list) - if(M.client) - playercount += 1 - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during population polling. Failed to connect.") - else - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/DBQuery/query = dbcon_old.NewQuery("INSERT INTO `tgstation`.`population` (`playercount`, `admincount`, `time`) VALUES ([playercount], [admincount], '[sqltime]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during population polling. Error : \[[err]\]\n") - -/proc/sql_report_round_start() - // TODO - if(!sqllogging) - return -/proc/sql_report_round_end() - // TODO - if(!sqllogging) - return - -/proc/sql_report_death(var/mob/living/carbon/human/H) - if(!sqllogging) - return - if(!H) - return - if(!H.key || !H.mind) - return - - var/area/placeofdeath = get_area(H) - var/podname = placeofdeath ? placeofdeath.name : "Unknown area" - - var/sqlname = sanitizeSQL(H.real_name) - var/sqlkey = sanitizeSQL(H.key) - var/sqlpod = sanitizeSQL(podname) - var/sqlspecial = sanitizeSQL(H.mind.special_role) - var/sqljob = sanitizeSQL(H.mind.assigned_role) - var/laname - var/lakey - if(H.lastattacker) - laname = sanitizeSQL(H.lastattacker:real_name) - lakey = sanitizeSQL(H.lastattacker:key) - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/coord = "[H.x], [H.y], [H.z]" - //to_world("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during death reporting. Failed to connect.") - else - var/DBQuery/query = dbcon.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") - - -/proc/sql_report_cyborg_death(var/mob/living/silicon/robot/H) - if(!sqllogging) - return - if(!H) - return - if(!H.key || !H.mind) - return - - var/area/placeofdeath = get_area(H) - var/podname = placeofdeath ? placeofdeath.name : "Unknown area" - - var/sqlname = sanitizeSQL(H.real_name) - var/sqlkey = sanitizeSQL(H.key) - var/sqlpod = sanitizeSQL(podname) - var/sqlspecial = sanitizeSQL(H.mind.special_role) - var/sqljob = sanitizeSQL(H.mind.assigned_role) - var/laname - var/lakey - if(H.lastattacker) - laname = sanitizeSQL(H.lastattacker:real_name) - lakey = sanitizeSQL(H.lastattacker:key) - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/coord = "[H.x], [H.y], [H.z]" - //to_world("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during death reporting. Failed to connect.") - else - var/DBQuery/query = dbcon.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") - - -/proc/statistic_cycle() - set waitfor = 0 - if(!sqllogging) - return - while(1) - sql_poll_population() - sleep(6000) - -//This proc is used for feedback. It is executed at round end. -/proc/sql_commit_feedback() - if(!blackbox) - log_game("Round ended without a blackbox recorder. No feedback was sent to the database.") - return - - //content is a list of lists. Each item in the list is a list with two fields, a variable name and a value. Items MUST only have these two values. - var/list/datum/feedback_variable/content = blackbox.get_round_feedback() - - if(!content) - log_game("Round ended without any feedback being generated. No feedback was sent to the database.") - return - - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during feedback reporting. Failed to connect.") - else - - var/DBQuery/max_query = dbcon.NewQuery("SELECT MAX(roundid) AS max_round_id FROM erro_feedback") - max_query.Execute() - - var/newroundid - - while(max_query.NextRow()) - newroundid = max_query.item[1] - - if(!(isnum(newroundid))) - newroundid = text2num(newroundid) - - if(isnum(newroundid)) - newroundid++ - else - newroundid = 1 - - for(var/datum/feedback_variable/item in content) - var/variable = item.get_variable() - var/value = item.get_value() - - var/DBQuery/query = dbcon.NewQuery("INSERT INTO erro_feedback (id, roundid, time, variable, value) VALUES (null, [newroundid], Now(), '[variable]', '[value]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") +/proc/sql_poll_population() + if(!sqllogging) + return + var/admincount = GLOB.admins.len + var/playercount = 0 + for(var/mob/M in player_list) + if(M.client) + playercount += 1 + establish_db_connection() + if(!dbcon.IsConnected()) + log_game("SQL ERROR during population polling. Failed to connect.") + else + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/DBQuery/query = dbcon_old.NewQuery("INSERT INTO `tgstation`.`population` (`playercount`, `admincount`, `time`) VALUES ([playercount], [admincount], '[sqltime]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during population polling. Error : \[[err]\]\n") + +/proc/sql_report_round_start() + // TODO + if(!sqllogging) + return +/proc/sql_report_round_end() + // TODO + if(!sqllogging) + return + +/proc/sql_report_death(var/mob/living/carbon/human/H) + if(!sqllogging) + return + if(!H) + return + if(!H.key || !H.mind) + return + + var/area/placeofdeath = get_area(H) + var/podname = placeofdeath ? placeofdeath.name : "Unknown area" + + var/sqlname = sanitizeSQL(H.real_name) + var/sqlkey = sanitizeSQL(H.key) + var/sqlpod = sanitizeSQL(podname) + var/sqlspecial = sanitizeSQL(H.mind.special_role) + var/sqljob = sanitizeSQL(H.mind.assigned_role) + var/laname + var/lakey + if(H.lastattacker) + laname = sanitizeSQL(H.lastattacker:real_name) + lakey = sanitizeSQL(H.lastattacker:key) + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/coord = "[H.x], [H.y], [H.z]" + //to_world("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") + establish_db_connection() + if(!dbcon.IsConnected()) + log_game("SQL ERROR during death reporting. Failed to connect.") + else + var/DBQuery/query = dbcon.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") + + +/proc/sql_report_cyborg_death(var/mob/living/silicon/robot/H) + if(!sqllogging) + return + if(!H) + return + if(!H.key || !H.mind) + return + + var/area/placeofdeath = get_area(H) + var/podname = placeofdeath ? placeofdeath.name : "Unknown area" + + var/sqlname = sanitizeSQL(H.real_name) + var/sqlkey = sanitizeSQL(H.key) + var/sqlpod = sanitizeSQL(podname) + var/sqlspecial = sanitizeSQL(H.mind.special_role) + var/sqljob = sanitizeSQL(H.mind.assigned_role) + var/laname + var/lakey + if(H.lastattacker) + laname = sanitizeSQL(H.lastattacker:real_name) + lakey = sanitizeSQL(H.lastattacker:key) + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/coord = "[H.x], [H.y], [H.z]" + //to_world("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") + establish_db_connection() + if(!dbcon.IsConnected()) + log_game("SQL ERROR during death reporting. Failed to connect.") + else + var/DBQuery/query = dbcon.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") + + +/proc/statistic_cycle() + set waitfor = 0 + if(!sqllogging) + return + while(1) + sql_poll_population() + sleep(6000) + +//This proc is used for feedback. It is executed at round end. +/proc/sql_commit_feedback() + if(!blackbox) + log_game("Round ended without a blackbox recorder. No feedback was sent to the database.") + return + + //content is a list of lists. Each item in the list is a list with two fields, a variable name and a value. Items MUST only have these two values. + var/list/datum/feedback_variable/content = blackbox.get_round_feedback() + + if(!content) + log_game("Round ended without any feedback being generated. No feedback was sent to the database.") + return + + establish_db_connection() + if(!dbcon.IsConnected()) + log_game("SQL ERROR during feedback reporting. Failed to connect.") + else + + var/DBQuery/max_query = dbcon.NewQuery("SELECT MAX(roundid) AS max_round_id FROM erro_feedback") + max_query.Execute() + + var/newroundid + + while(max_query.NextRow()) + newroundid = max_query.item[1] + + if(!(isnum(newroundid))) + newroundid = text2num(newroundid) + + if(isnum(newroundid)) + newroundid++ + else + newroundid = 1 + + for(var/datum/feedback_variable/item in content) + var/variable = item.get_variable() + var/value = item.get_value() + + var/DBQuery/query = dbcon.NewQuery("INSERT INTO erro_feedback (id, roundid, time, variable, value) VALUES (null, [newroundid], Now(), '[variable]', '[value]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") diff --git a/code/game/area/ai_monitored.dm b/code/game/area/ai_monitored.dm index 84f2c8eb4ea..7c95e83b0d0 100644 --- a/code/game/area/ai_monitored.dm +++ b/code/game/area/ai_monitored.dm @@ -1,26 +1,26 @@ -/area/ai_monitored - name = "AI Monitored Area" - var/obj/machinery/camera/motioncamera = null - - -/area/ai_monitored/New() - ..() - // locate and store the motioncamera - spawn (20) // spawn on a delay to let turfs/objs load - for (var/obj/machinery/camera/M in src) - if(M.isMotion()) - motioncamera = M - M.area_motion = src - return - return - -/area/ai_monitored/Entered(atom/movable/O) - ..() - if (ismob(O) && motioncamera) - motioncamera.newTarget(O) - -/area/ai_monitored/Exited(atom/movable/O) - if (ismob(O) && motioncamera) - motioncamera.lostTarget(O) - - +/area/ai_monitored + name = "AI Monitored Area" + var/obj/machinery/camera/motioncamera = null + + +/area/ai_monitored/New() + ..() + // locate and store the motioncamera + spawn (20) // spawn on a delay to let turfs/objs load + for (var/obj/machinery/camera/M in src) + if(M.isMotion()) + motioncamera = M + M.area_motion = src + return + return + +/area/ai_monitored/Entered(atom/movable/O) + ..() + if (ismob(O) && motioncamera) + motioncamera.newTarget(O) + +/area/ai_monitored/Exited(atom/movable/O) + if (ismob(O) && motioncamera) + motioncamera.lostTarget(O) + + diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index c13d67483a7..a370f614d68 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -1,556 +1,556 @@ -// Areas.dm - -/area - var/fire = null - var/atmos = 1 - var/atmosalm = 0 - var/poweralm = 1 - var/party = null - level = null - name = "Unknown" - icon = 'icons/turf/areas.dmi' - icon_state = "unknown" - plane = PLANE_LIGHTING_ABOVE //In case we color them - luminosity = 0 - mouse_opacity = 0 - var/lightswitch = 1 - - var/eject = null - - var/debug = 0 - var/requires_power = 1 - var/always_unpowered = 0 //this gets overriden to 1 for space in area/New() - - // Power channel status - Is it currently energized? - var/power_equip = TRUE - var/power_light = TRUE - var/power_environ = TRUE - - // Oneoff power usage - Used once and cleared each power cycle - var/oneoff_equip = 0 - var/oneoff_light = 0 - var/oneoff_environ = 0 - - // Continuous "static" power usage - Do not update these directly! - var/static_equip = 0 - var/static_light = 0 - var/static_environ = 0 - - var/music = null - var/has_gravity = 1 - var/secret_name = FALSE // This tells certain things that display areas' names that they shouldn't display this area's name. - var/obj/machinery/power/apc/apc = null - var/no_air = null -// var/list/lights // list of all lights on this area - var/list/all_doors = null //Added by Strumpetplaya - Alarm Change - Contains a list of doors adjacent to this area - var/list/all_arfgs = null //Similar, but a list of all arfgs adjacent to this area - var/firedoors_closed = 0 - var/arfgs_active = 0 - var/list/ambience = list() - var/list/forced_ambience = null - var/sound_env = STANDARD_STATION - var/turf/base_turf //The base turf type of the area, which can be used to override the z-level's base turf - var/forbid_events = FALSE // If true, random events will not start inside this area. - var/forbid_singulo = FALSE // If true singulo will not move in. - var/no_spoilers = FALSE // If true, makes it much more difficult to see what is inside an area with things like mesons. - var/soundproofed = FALSE // If true, blocks sounds from other areas and prevents hearers on other areas from hearing the sounds within. - -/area/Initialize() - . = ..() - luminosity = !(dynamic_lighting) - icon_state = "" - return INITIALIZE_HINT_LATELOAD // Areas tradiationally are initialized AFTER other atoms. - -/area/LateInitialize() - if(!requires_power || !apc) - power_light = 0 - power_equip = 0 - power_environ = 0 - power_change() // all machines set to current power level, also updates lighting icon - if(no_spoilers) - set_spoiler_obfuscation(TRUE) - -// Changes the area of T to A. Do not do this manually. -// Area is expected to be a non-null instance. -/proc/ChangeArea(var/turf/T, var/area/A) - if(!istype(A)) - CRASH("Area change attempt failed: invalid area supplied.") - var/area/old_area = get_area(T) - if(old_area == A) - return - // NOTE: BayStation calles area.Exited/Entered for the TURF T. So far we don't do that.s - // NOTE: There probably won't be any atoms in these turfs, but just in case we should call these procs. - A.contents.Add(T) - if(old_area) - // Handle dynamic lighting update if - if(SSlighting.subsystem_initialized && T.dynamic_lighting && old_area.dynamic_lighting != A.dynamic_lighting) - if(A.dynamic_lighting) - T.lighting_build_overlay() - else - T.lighting_clear_overlay() - for(var/atom/movable/AM in T) - old_area.Exited(AM, A) - for(var/atom/movable/AM in T) - A.Entered(AM, old_area) - for(var/obj/machinery/M in T) - M.area_changed(old_area, A) - -/area/proc/get_contents() - return contents - -/area/proc/get_cameras() - var/list/cameras = list() - for (var/obj/machinery/camera/C in src) - cameras += C - return cameras - -/area/proc/atmosalert(danger_level, var/alarm_source) - if (danger_level == 0) - atmosphere_alarm.clearAlarm(src, alarm_source) - else - var/obj/machinery/alarm/atmosalarm = alarm_source //maybe other things can trigger these, who knows - if(istype(atmosalarm)) - atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level, hidden = atmosalarm.alarms_hidden) - else - atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level) - - //Check all the alarms before lowering atmosalm. Raising is perfectly fine. - for (var/obj/machinery/alarm/AA in src) - if (!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level) - danger_level = max(danger_level, AA.danger_level) - - if(danger_level != atmosalm) - atmosalm = danger_level - //closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise - if (danger_level < 1 || danger_level >= 2) - firedoors_update() - - for (var/obj/machinery/alarm/AA in src) - AA.update_icon() - - return 1 - return 0 - -// Either close or open firedoors and arfgs depending on current alert statuses -/area/proc/firedoors_update() - if(fire || party || atmosalm) - firedoors_close() - arfgs_activate() - // VOREStation Edit - Make the lights colored! - if(fire) - for(var/obj/machinery/light/L in src) - L.set_alert_fire() - else if(atmosalm) - for(var/obj/machinery/light/L in src) - L.set_alert_atmos() - // VOREStation Edit End - else - firedoors_open() - arfgs_deactivate() - // VOREStation Edit - Put the lights back! - for(var/obj/machinery/light/L in src) - L.reset_alert() - // VOREStation Edit End - -// Close all firedoors in the area -/area/proc/firedoors_close() - if(!firedoors_closed) - firedoors_closed = TRUE - if(!all_doors) - return - for(var/obj/machinery/door/firedoor/E in all_doors) - if(!E.blocked) - if(E.operating) - E.nextstate = FIREDOOR_CLOSED - else if(!E.density) - spawn(0) - E.close() - -// Open all firedoors in the area -/area/proc/firedoors_open() - if(firedoors_closed) - firedoors_closed = FALSE - if(!all_doors) - return - for(var/obj/machinery/door/firedoor/E in all_doors) - if(!E.blocked) - if(E.operating) - E.nextstate = FIREDOOR_OPEN - else if(E.density) - spawn(0) - E.open() - -// Activate all retention fields! -/area/proc/arfgs_activate() - if(!arfgs_active) - arfgs_active = TRUE - if(!all_arfgs) - return - for(var/obj/machinery/atmospheric_field_generator/E in all_arfgs) - E.generate_field() //don't need to check powered state like doors, the arfgs handles it on its end - E.wasactive = TRUE - -// Deactivate retention fields! -/area/proc/arfgs_deactivate() - if(arfgs_active) - arfgs_active = FALSE - if(!all_arfgs) - return - for(var/obj/machinery/atmospheric_field_generator/E in all_arfgs) - E.disable_field() - E.wasactive = FALSE - -/area/proc/fire_alert() - if(!fire) - fire = 1 //used for firedoor checks - update_icon() - firedoors_update() - -/area/proc/fire_reset() - if (fire) - fire = 0 //used for firedoor checks - update_icon() - firedoors_update() - -/area/proc/readyalert() - if(!eject) - eject = 1 - update_icon() - return - -/area/proc/readyreset() - if(eject) - eject = 0 - update_icon() - return - -/area/proc/partyalert() - if (!( party )) - party = 1 - update_icon() - firedoors_update() - return - -/area/proc/partyreset() - if (party) - party = 0 - update_icon() - firedoors_update() - return - -/area/update_icon() - if ((fire || eject || party) && (!requires_power||power_environ) && !istype(src, /area/space))//If it doesn't require power, can still activate this proc. - if(fire && !eject && !party) - icon_state = null // Let lights take care of it - /*else if(atmosalm && !fire && !eject && !party) - icon_state = "bluenew"*/ - else if(!fire && eject && !party) - icon_state = "red" - else if(party && !fire && !eject) - icon_state = "party" - else - icon_state = "blue-red" - else - // new lighting behaviour with obj lights - icon_state = null - - -/* -#define EQUIP 1 -#define LIGHT 2 -#define ENVIRON 3 -*/ - -/area/proc/powered(var/chan) // return true if the area has power to given channel - - if(!requires_power) - return 1 - if(always_unpowered) - return 0 - switch(chan) - if(EQUIP) - return power_equip - if(LIGHT) - return power_light - if(ENVIRON) - return power_environ - - return 0 - -// called when power status changes -/area/proc/power_change() - for(var/obj/machinery/M in src) // for each machine in the area - M.power_change() // reverify power status (to update icons etc.) - if (fire || eject || party) - update_icon() - -/area/proc/usage(var/chan, var/include_static = TRUE) - var/used = 0 - switch(chan) - if(LIGHT) - used += oneoff_light + (include_static * static_light) - if(EQUIP) - used += oneoff_equip + (include_static * static_equip) - if(ENVIRON) - used += oneoff_environ + (include_static * static_environ) - if(TOTAL) - used += oneoff_light + (include_static * static_light) - used += oneoff_equip + (include_static * static_equip) - used += oneoff_environ + (include_static * static_environ) - return used - -// Helper for APCs; will generally be called every tick. -/area/proc/clear_usage() - oneoff_equip = 0 - oneoff_light = 0 - oneoff_environ = 0 - -// Use this for a one-time power draw from the area, typically for non-machines. -/area/proc/use_power_oneoff(var/amount, var/chan) - switch(chan) - if(EQUIP) - oneoff_equip += amount - if(LIGHT) - oneoff_light += amount - if(ENVIRON) - oneoff_environ += amount - return amount - -// This is used by machines to properly update the area of power changes. -/area/proc/power_use_change(old_amount, new_amount, chan) - use_power_static(new_amount - old_amount, chan) // Simultaneously subtract old_amount and add new_amount. - -// Not a proc you want to use directly unless you know what you are doing; see use_power_oneoff above instead. -/area/proc/use_power_static(var/amount, var/chan) - switch(chan) - if(EQUIP) - static_equip += amount - if(LIGHT) - static_light += amount - if(ENVIRON) - static_environ += amount - -// This recomputes the continued power usage; can be used for testing or error recovery, but is not called every tick. -/area/proc/retally_power() - static_equip = 0 - static_light = 0 - static_environ = 0 - for(var/obj/machinery/M in src) - switch(M.power_channel) - if(EQUIP) - static_equip += M.get_power_usage() - if(LIGHT) - static_light += M.get_power_usage() - if(ENVIRON) - static_environ += M.get_power_usage() - -////////////////////////////////////////////////////////////////// - -/area/vv_get_dropdown() - . = ..() - VV_DROPDOWN_OPTION("check_static_power", "Check Static Power") - -/area/vv_do_topic(list/href_list) - . = ..() - IF_VV_OPTION("check_static_power") - if(!check_rights(R_DEBUG)) - return - src.check_static_power(usr) - href_list["datumrefresh"] = "\ref[src]" - -// Debugging proc to report if static power is correct or not. -/area/proc/check_static_power(var/user) - set name = "Check Static Power" - var/actual_static_equip = static_equip - var/actual_static_light = static_light - var/actual_static_environ = static_environ - retally_power() - if(user) - var/list/report = list("[src] ([type]) static power tally:") - report += "EQUIP: Actual: [actual_static_equip] Correct: [static_equip] Difference: [actual_static_equip - static_equip]" - report += "LIGHT: Actual: [actual_static_light] Correct: [static_light] Difference: [actual_static_light - static_light]" - report += "ENVIRON: Actual: [actual_static_environ] Correct: [static_environ] Difference: [actual_static_environ - static_environ]" - to_chat(user, report.Join("\n")) - return (actual_static_equip == static_equip && actual_static_light == static_light && actual_static_environ == static_environ) - -////////////////////////////////////////////////////////////////// - -var/list/mob/living/forced_ambiance_list = new - -/area/Entered(mob/M) - if(!istype(M) || !M.ckey) - return - - if(!isliving(M)) - M.lastarea = src - return - - var/mob/living/L = M - if(!L.lastarea) - L.lastarea = src - var/area/oldarea = L.lastarea - if((oldarea.has_gravity == 0) && (has_gravity == 1) && (L.m_intent == "run")) // Being ready when you change areas gives you a chance to avoid falling all together. - thunk(L) - L.update_floating( L.Check_Dense_Object() ) - - L.lastarea = src - L.lastareachange = world.time - play_ambience(L, initial = TRUE) - if(no_spoilers) - L.disable_spoiler_vision() - -/area/proc/play_ambience(var/mob/living/L, initial = TRUE) - // Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch - if(!(L && L.is_preference_enabled(/datum/client_preference/play_ambiance))) - return - - var/volume_mod = L.get_preference_volume_channel(VOLUME_CHANNEL_AMBIENCE) - - // If we previously were in an area with force-played ambiance, stop it. - if((L in forced_ambiance_list) && initial) - L << sound(null, channel = CHANNEL_AMBIENCE_FORCED) - forced_ambiance_list -= L - - if(forced_ambience) - if(L in forced_ambiance_list) - return - if(forced_ambience.len) - forced_ambiance_list |= L - var/sound/chosen_ambiance = pick(forced_ambience) - if(!istype(chosen_ambiance)) - chosen_ambiance = sound(chosen_ambiance, repeat = 1, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE_FORCED) - chosen_ambiance.volume *= volume_mod - L << chosen_ambiance - else - L << sound(null, channel = CHANNEL_AMBIENCE_FORCED) - else if(src.ambience && src.ambience.len) - var/ambience_odds = L?.client.prefs.ambience_chance - if(prob(ambience_odds) && (world.time >= L.client.time_last_ambience_played + 1 MINUTE)) - var/sound = pick(ambience) - L << sound(sound, repeat = 0, wait = 0, volume = 50 * volume_mod, channel = CHANNEL_AMBIENCE) - L.client.time_last_ambience_played = world.time - -/area/proc/gravitychange(var/gravitystate = 0) - src.has_gravity = gravitystate - - for(var/mob/M in src) - if(has_gravity) - thunk(M) - M.update_floating( M.Check_Dense_Object() ) - M.update_gravity(has_gravity) - -/area/proc/thunk(mob) - if(istype(get_turf(mob), /turf/space)) // Can't fall onto nothing. - return - - if(istype(mob,/mob/living/carbon/human/)) - var/mob/living/carbon/human/H = mob - if(H.buckled) - return // Being buckled to something solid keeps you in place. - if(istype(H.shoes, /obj/item/clothing/shoes/magboots) && (H.shoes.item_flags & NOSLIP)) - return - if(H.incorporeal_move) // VOREstation edit - Phaseshifted beings should not be affected by gravity - return - if(H.species.can_zero_g_move || H.species.can_space_freemove) - return - - if(H.m_intent == "run") - H.AdjustStunned(6) - H.AdjustWeakened(6) - else - H.AdjustStunned(3) - H.AdjustWeakened(3) - to_chat(mob, "The sudden appearance of gravity makes you fall to the floor!") - playsound(mob, "bodyfall", 50, 1) - -/area/proc/prison_break(break_lights = TRUE, open_doors = TRUE, open_blast_doors = TRUE) - var/obj/machinery/power/apc/theAPC = get_apc() - if(theAPC && theAPC.operating) - if(break_lights) - for(var/obj/machinery/power/apc/temp_apc in src) - temp_apc.overload_lighting(70) - if(open_doors) - for(var/obj/machinery/door/airlock/temp_airlock in src) - temp_airlock.prison_open() - for(var/obj/machinery/door/window/temp_windoor in src) - temp_windoor.open() - if(open_blast_doors) - for(var/obj/machinery/door/blast/temp_blast in src) - temp_blast.open() - -/area/has_gravity() - return has_gravity - -/area/space/has_gravity() - return 0 - -/proc/has_gravity(atom/AT, turf/T) - if(!T) - T = get_turf(AT) - var/area/A = get_area(T) - if(A && A.has_gravity()) - return 1 - return 0 - -/area/proc/shuttle_arrived() - return TRUE - -/area/proc/shuttle_departed() - return TRUE - -/area/AllowDrop() - CRASH("Bad op: area/AllowDrop() called") - -/area/drop_location() - CRASH("Bad op: area/drop_location() called") - -/*Adding a wizard area teleport list because motherfucking lag -- Urist*/ -/*I am far too lazy to make it a proper list of areas so I'll just make it run the usual telepot routine at the start of the game*/ -var/list/teleportlocs = list() - -/hook/startup/proc/setupTeleportLocs() - for(var/area/AR in world) - if(istype(AR, /area/shuttle) || istype(AR, /area/syndicate_station) || istype(AR, /area/wizard_station)) continue - if(teleportlocs.Find(AR.name)) continue - var/turf/picked = pick(get_area_turfs(AR.type)) - if (picked.z in using_map.station_levels) - teleportlocs += AR.name - teleportlocs[AR.name] = AR - - teleportlocs = sortAssoc(teleportlocs) - - return 1 - -var/list/ghostteleportlocs = list() - -/hook/startup/proc/setupGhostTeleportLocs() - for(var/area/AR in world) - if(ghostteleportlocs.Find(AR.name)) continue - if(istype(AR, /area/aisat) || istype(AR, /area/derelict) || istype(AR, /area/tdome) || istype(AR, /area/shuttle/specops/centcom)) - ghostteleportlocs += AR.name - ghostteleportlocs[AR.name] = AR - var/turf/picked = pick(get_area_turfs(AR.type)) - if (picked.z in using_map.player_levels) - ghostteleportlocs += AR.name - ghostteleportlocs[AR.name] = AR - - ghostteleportlocs = sortAssoc(ghostteleportlocs) - - return 1 - -/area/proc/get_name() - if(secret_name) - return "Unknown Area" - return name - -GLOBAL_DATUM(spoiler_obfuscation_image, /image) - -/area/proc/set_spoiler_obfuscation(should_obfuscate) - if(!GLOB.spoiler_obfuscation_image) - GLOB.spoiler_obfuscation_image = image(icon = 'icons/misc/static.dmi') - GLOB.spoiler_obfuscation_image.plane = PLANE_MESONS - - if(should_obfuscate) - add_overlay(GLOB.spoiler_obfuscation_image) - else +// Areas.dm + +/area + var/fire = null + var/atmos = 1 + var/atmosalm = 0 + var/poweralm = 1 + var/party = null + level = null + name = "Unknown" + icon = 'icons/turf/areas.dmi' + icon_state = "unknown" + plane = PLANE_LIGHTING_ABOVE //In case we color them + luminosity = 0 + mouse_opacity = 0 + var/lightswitch = 1 + + var/eject = null + + var/debug = 0 + var/requires_power = 1 + var/always_unpowered = 0 //this gets overriden to 1 for space in area/New() + + // Power channel status - Is it currently energized? + var/power_equip = TRUE + var/power_light = TRUE + var/power_environ = TRUE + + // Oneoff power usage - Used once and cleared each power cycle + var/oneoff_equip = 0 + var/oneoff_light = 0 + var/oneoff_environ = 0 + + // Continuous "static" power usage - Do not update these directly! + var/static_equip = 0 + var/static_light = 0 + var/static_environ = 0 + + var/music = null + var/has_gravity = 1 + var/secret_name = FALSE // This tells certain things that display areas' names that they shouldn't display this area's name. + var/obj/machinery/power/apc/apc = null + var/no_air = null +// var/list/lights // list of all lights on this area + var/list/all_doors = null //Added by Strumpetplaya - Alarm Change - Contains a list of doors adjacent to this area + var/list/all_arfgs = null //Similar, but a list of all arfgs adjacent to this area + var/firedoors_closed = 0 + var/arfgs_active = 0 + var/list/ambience = list() + var/list/forced_ambience = null + var/sound_env = STANDARD_STATION + var/turf/base_turf //The base turf type of the area, which can be used to override the z-level's base turf + var/forbid_events = FALSE // If true, random events will not start inside this area. + var/forbid_singulo = FALSE // If true singulo will not move in. + var/no_spoilers = FALSE // If true, makes it much more difficult to see what is inside an area with things like mesons. + var/soundproofed = FALSE // If true, blocks sounds from other areas and prevents hearers on other areas from hearing the sounds within. + +/area/Initialize() + . = ..() + luminosity = !(dynamic_lighting) + icon_state = "" + return INITIALIZE_HINT_LATELOAD // Areas tradiationally are initialized AFTER other atoms. + +/area/LateInitialize() + if(!requires_power || !apc) + power_light = 0 + power_equip = 0 + power_environ = 0 + power_change() // all machines set to current power level, also updates lighting icon + if(no_spoilers) + set_spoiler_obfuscation(TRUE) + +// Changes the area of T to A. Do not do this manually. +// Area is expected to be a non-null instance. +/proc/ChangeArea(var/turf/T, var/area/A) + if(!istype(A)) + CRASH("Area change attempt failed: invalid area supplied.") + var/area/old_area = get_area(T) + if(old_area == A) + return + // NOTE: BayStation calles area.Exited/Entered for the TURF T. So far we don't do that.s + // NOTE: There probably won't be any atoms in these turfs, but just in case we should call these procs. + A.contents.Add(T) + if(old_area) + // Handle dynamic lighting update if + if(SSlighting.subsystem_initialized && T.dynamic_lighting && old_area.dynamic_lighting != A.dynamic_lighting) + if(A.dynamic_lighting) + T.lighting_build_overlay() + else + T.lighting_clear_overlay() + for(var/atom/movable/AM in T) + old_area.Exited(AM, A) + for(var/atom/movable/AM in T) + A.Entered(AM, old_area) + for(var/obj/machinery/M in T) + M.area_changed(old_area, A) + +/area/proc/get_contents() + return contents + +/area/proc/get_cameras() + var/list/cameras = list() + for (var/obj/machinery/camera/C in src) + cameras += C + return cameras + +/area/proc/atmosalert(danger_level, var/alarm_source) + if (danger_level == 0) + atmosphere_alarm.clearAlarm(src, alarm_source) + else + var/obj/machinery/alarm/atmosalarm = alarm_source //maybe other things can trigger these, who knows + if(istype(atmosalarm)) + atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level, hidden = atmosalarm.alarms_hidden) + else + atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level) + + //Check all the alarms before lowering atmosalm. Raising is perfectly fine. + for (var/obj/machinery/alarm/AA in src) + if (!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level) + danger_level = max(danger_level, AA.danger_level) + + if(danger_level != atmosalm) + atmosalm = danger_level + //closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise + if (danger_level < 1 || danger_level >= 2) + firedoors_update() + + for (var/obj/machinery/alarm/AA in src) + AA.update_icon() + + return 1 + return 0 + +// Either close or open firedoors and arfgs depending on current alert statuses +/area/proc/firedoors_update() + if(fire || party || atmosalm) + firedoors_close() + arfgs_activate() + // VOREStation Edit - Make the lights colored! + if(fire) + for(var/obj/machinery/light/L in src) + L.set_alert_fire() + else if(atmosalm) + for(var/obj/machinery/light/L in src) + L.set_alert_atmos() + // VOREStation Edit End + else + firedoors_open() + arfgs_deactivate() + // VOREStation Edit - Put the lights back! + for(var/obj/machinery/light/L in src) + L.reset_alert() + // VOREStation Edit End + +// Close all firedoors in the area +/area/proc/firedoors_close() + if(!firedoors_closed) + firedoors_closed = TRUE + if(!all_doors) + return + for(var/obj/machinery/door/firedoor/E in all_doors) + if(!E.blocked) + if(E.operating) + E.nextstate = FIREDOOR_CLOSED + else if(!E.density) + spawn(0) + E.close() + +// Open all firedoors in the area +/area/proc/firedoors_open() + if(firedoors_closed) + firedoors_closed = FALSE + if(!all_doors) + return + for(var/obj/machinery/door/firedoor/E in all_doors) + if(!E.blocked) + if(E.operating) + E.nextstate = FIREDOOR_OPEN + else if(E.density) + spawn(0) + E.open() + +// Activate all retention fields! +/area/proc/arfgs_activate() + if(!arfgs_active) + arfgs_active = TRUE + if(!all_arfgs) + return + for(var/obj/machinery/atmospheric_field_generator/E in all_arfgs) + E.generate_field() //don't need to check powered state like doors, the arfgs handles it on its end + E.wasactive = TRUE + +// Deactivate retention fields! +/area/proc/arfgs_deactivate() + if(arfgs_active) + arfgs_active = FALSE + if(!all_arfgs) + return + for(var/obj/machinery/atmospheric_field_generator/E in all_arfgs) + E.disable_field() + E.wasactive = FALSE + +/area/proc/fire_alert() + if(!fire) + fire = 1 //used for firedoor checks + update_icon() + firedoors_update() + +/area/proc/fire_reset() + if (fire) + fire = 0 //used for firedoor checks + update_icon() + firedoors_update() + +/area/proc/readyalert() + if(!eject) + eject = 1 + update_icon() + return + +/area/proc/readyreset() + if(eject) + eject = 0 + update_icon() + return + +/area/proc/partyalert() + if (!( party )) + party = 1 + update_icon() + firedoors_update() + return + +/area/proc/partyreset() + if (party) + party = 0 + update_icon() + firedoors_update() + return + +/area/update_icon() + if ((fire || eject || party) && (!requires_power||power_environ) && !istype(src, /area/space))//If it doesn't require power, can still activate this proc. + if(fire && !eject && !party) + icon_state = null // Let lights take care of it + /*else if(atmosalm && !fire && !eject && !party) + icon_state = "bluenew"*/ + else if(!fire && eject && !party) + icon_state = "red" + else if(party && !fire && !eject) + icon_state = "party" + else + icon_state = "blue-red" + else + // new lighting behaviour with obj lights + icon_state = null + + +/* +#define EQUIP 1 +#define LIGHT 2 +#define ENVIRON 3 +*/ + +/area/proc/powered(var/chan) // return true if the area has power to given channel + + if(!requires_power) + return 1 + if(always_unpowered) + return 0 + switch(chan) + if(EQUIP) + return power_equip + if(LIGHT) + return power_light + if(ENVIRON) + return power_environ + + return 0 + +// called when power status changes +/area/proc/power_change() + for(var/obj/machinery/M in src) // for each machine in the area + M.power_change() // reverify power status (to update icons etc.) + if (fire || eject || party) + update_icon() + +/area/proc/usage(var/chan, var/include_static = TRUE) + var/used = 0 + switch(chan) + if(LIGHT) + used += oneoff_light + (include_static * static_light) + if(EQUIP) + used += oneoff_equip + (include_static * static_equip) + if(ENVIRON) + used += oneoff_environ + (include_static * static_environ) + if(TOTAL) + used += oneoff_light + (include_static * static_light) + used += oneoff_equip + (include_static * static_equip) + used += oneoff_environ + (include_static * static_environ) + return used + +// Helper for APCs; will generally be called every tick. +/area/proc/clear_usage() + oneoff_equip = 0 + oneoff_light = 0 + oneoff_environ = 0 + +// Use this for a one-time power draw from the area, typically for non-machines. +/area/proc/use_power_oneoff(var/amount, var/chan) + switch(chan) + if(EQUIP) + oneoff_equip += amount + if(LIGHT) + oneoff_light += amount + if(ENVIRON) + oneoff_environ += amount + return amount + +// This is used by machines to properly update the area of power changes. +/area/proc/power_use_change(old_amount, new_amount, chan) + use_power_static(new_amount - old_amount, chan) // Simultaneously subtract old_amount and add new_amount. + +// Not a proc you want to use directly unless you know what you are doing; see use_power_oneoff above instead. +/area/proc/use_power_static(var/amount, var/chan) + switch(chan) + if(EQUIP) + static_equip += amount + if(LIGHT) + static_light += amount + if(ENVIRON) + static_environ += amount + +// This recomputes the continued power usage; can be used for testing or error recovery, but is not called every tick. +/area/proc/retally_power() + static_equip = 0 + static_light = 0 + static_environ = 0 + for(var/obj/machinery/M in src) + switch(M.power_channel) + if(EQUIP) + static_equip += M.get_power_usage() + if(LIGHT) + static_light += M.get_power_usage() + if(ENVIRON) + static_environ += M.get_power_usage() + +////////////////////////////////////////////////////////////////// + +/area/vv_get_dropdown() + . = ..() + VV_DROPDOWN_OPTION("check_static_power", "Check Static Power") + +/area/vv_do_topic(list/href_list) + . = ..() + IF_VV_OPTION("check_static_power") + if(!check_rights(R_DEBUG)) + return + src.check_static_power(usr) + href_list["datumrefresh"] = "\ref[src]" + +// Debugging proc to report if static power is correct or not. +/area/proc/check_static_power(var/user) + set name = "Check Static Power" + var/actual_static_equip = static_equip + var/actual_static_light = static_light + var/actual_static_environ = static_environ + retally_power() + if(user) + var/list/report = list("[src] ([type]) static power tally:") + report += "EQUIP: Actual: [actual_static_equip] Correct: [static_equip] Difference: [actual_static_equip - static_equip]" + report += "LIGHT: Actual: [actual_static_light] Correct: [static_light] Difference: [actual_static_light - static_light]" + report += "ENVIRON: Actual: [actual_static_environ] Correct: [static_environ] Difference: [actual_static_environ - static_environ]" + to_chat(user, report.Join("\n")) + return (actual_static_equip == static_equip && actual_static_light == static_light && actual_static_environ == static_environ) + +////////////////////////////////////////////////////////////////// + +var/list/mob/living/forced_ambiance_list = new + +/area/Entered(mob/M) + if(!istype(M) || !M.ckey) + return + + if(!isliving(M)) + M.lastarea = src + return + + var/mob/living/L = M + if(!L.lastarea) + L.lastarea = src + var/area/oldarea = L.lastarea + if((oldarea.has_gravity == 0) && (has_gravity == 1) && (L.m_intent == "run")) // Being ready when you change areas gives you a chance to avoid falling all together. + thunk(L) + L.update_floating( L.Check_Dense_Object() ) + + L.lastarea = src + L.lastareachange = world.time + play_ambience(L, initial = TRUE) + if(no_spoilers) + L.disable_spoiler_vision() + +/area/proc/play_ambience(var/mob/living/L, initial = TRUE) + // Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch + if(!(L && L.is_preference_enabled(/datum/client_preference/play_ambiance))) + return + + var/volume_mod = L.get_preference_volume_channel(VOLUME_CHANNEL_AMBIENCE) + + // If we previously were in an area with force-played ambiance, stop it. + if((L in forced_ambiance_list) && initial) + L << sound(null, channel = CHANNEL_AMBIENCE_FORCED) + forced_ambiance_list -= L + + if(forced_ambience) + if(L in forced_ambiance_list) + return + if(forced_ambience.len) + forced_ambiance_list |= L + var/sound/chosen_ambiance = pick(forced_ambience) + if(!istype(chosen_ambiance)) + chosen_ambiance = sound(chosen_ambiance, repeat = 1, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE_FORCED) + chosen_ambiance.volume *= volume_mod + L << chosen_ambiance + else + L << sound(null, channel = CHANNEL_AMBIENCE_FORCED) + else if(src.ambience && src.ambience.len) + var/ambience_odds = L?.client.prefs.ambience_chance + if(prob(ambience_odds) && (world.time >= L.client.time_last_ambience_played + 1 MINUTE)) + var/sound = pick(ambience) + L << sound(sound, repeat = 0, wait = 0, volume = 50 * volume_mod, channel = CHANNEL_AMBIENCE) + L.client.time_last_ambience_played = world.time + +/area/proc/gravitychange(var/gravitystate = 0) + src.has_gravity = gravitystate + + for(var/mob/M in src) + if(has_gravity) + thunk(M) + M.update_floating( M.Check_Dense_Object() ) + M.update_gravity(has_gravity) + +/area/proc/thunk(mob) + if(istype(get_turf(mob), /turf/space)) // Can't fall onto nothing. + return + + if(istype(mob,/mob/living/carbon/human/)) + var/mob/living/carbon/human/H = mob + if(H.buckled) + return // Being buckled to something solid keeps you in place. + if(istype(H.shoes, /obj/item/clothing/shoes/magboots) && (H.shoes.item_flags & NOSLIP)) + return + if(H.incorporeal_move) // VOREstation edit - Phaseshifted beings should not be affected by gravity + return + if(H.species.can_zero_g_move || H.species.can_space_freemove) + return + + if(H.m_intent == "run") + H.AdjustStunned(6) + H.AdjustWeakened(6) + else + H.AdjustStunned(3) + H.AdjustWeakened(3) + to_chat(mob, "The sudden appearance of gravity makes you fall to the floor!") + playsound(mob, "bodyfall", 50, 1) + +/area/proc/prison_break(break_lights = TRUE, open_doors = TRUE, open_blast_doors = TRUE) + var/obj/machinery/power/apc/theAPC = get_apc() + if(theAPC && theAPC.operating) + if(break_lights) + for(var/obj/machinery/power/apc/temp_apc in src) + temp_apc.overload_lighting(70) + if(open_doors) + for(var/obj/machinery/door/airlock/temp_airlock in src) + temp_airlock.prison_open() + for(var/obj/machinery/door/window/temp_windoor in src) + temp_windoor.open() + if(open_blast_doors) + for(var/obj/machinery/door/blast/temp_blast in src) + temp_blast.open() + +/area/has_gravity() + return has_gravity + +/area/space/has_gravity() + return 0 + +/proc/has_gravity(atom/AT, turf/T) + if(!T) + T = get_turf(AT) + var/area/A = get_area(T) + if(A && A.has_gravity()) + return 1 + return 0 + +/area/proc/shuttle_arrived() + return TRUE + +/area/proc/shuttle_departed() + return TRUE + +/area/AllowDrop() + CRASH("Bad op: area/AllowDrop() called") + +/area/drop_location() + CRASH("Bad op: area/drop_location() called") + +/*Adding a wizard area teleport list because motherfucking lag -- Urist*/ +/*I am far too lazy to make it a proper list of areas so I'll just make it run the usual telepot routine at the start of the game*/ +var/list/teleportlocs = list() + +/hook/startup/proc/setupTeleportLocs() + for(var/area/AR in world) + if(istype(AR, /area/shuttle) || istype(AR, /area/syndicate_station) || istype(AR, /area/wizard_station)) continue + if(teleportlocs.Find(AR.name)) continue + var/turf/picked = pick(get_area_turfs(AR.type)) + if (picked.z in using_map.station_levels) + teleportlocs += AR.name + teleportlocs[AR.name] = AR + + teleportlocs = sortAssoc(teleportlocs) + + return 1 + +var/list/ghostteleportlocs = list() + +/hook/startup/proc/setupGhostTeleportLocs() + for(var/area/AR in world) + if(ghostteleportlocs.Find(AR.name)) continue + if(istype(AR, /area/aisat) || istype(AR, /area/derelict) || istype(AR, /area/tdome) || istype(AR, /area/shuttle/specops/centcom)) + ghostteleportlocs += AR.name + ghostteleportlocs[AR.name] = AR + var/turf/picked = pick(get_area_turfs(AR.type)) + if (picked.z in using_map.player_levels) + ghostteleportlocs += AR.name + ghostteleportlocs[AR.name] = AR + + ghostteleportlocs = sortAssoc(ghostteleportlocs) + + return 1 + +/area/proc/get_name() + if(secret_name) + return "Unknown Area" + return name + +GLOBAL_DATUM(spoiler_obfuscation_image, /image) + +/area/proc/set_spoiler_obfuscation(should_obfuscate) + if(!GLOB.spoiler_obfuscation_image) + GLOB.spoiler_obfuscation_image = image(icon = 'icons/misc/static.dmi') + GLOB.spoiler_obfuscation_image.plane = PLANE_MESONS + + if(should_obfuscate) + add_overlay(GLOB.spoiler_obfuscation_image) + else cut_overlay(GLOB.spoiler_obfuscation_image) \ No newline at end of file diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 08295836f22..9490a0af464 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1,733 +1,733 @@ -/atom - layer = TURF_LAYER //This was here when I got here. Why though? - var/level = 2 - var/flags = 0 - var/list/fingerprints - var/list/fingerprintshidden - var/fingerprintslast = null - var/list/blood_DNA - var/was_bloodied - var/blood_color - var/pass_flags = 0 - var/throwpass = 0 - var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom. - var/simulated = TRUE //filter for actions - used by lighting overlays - var/atom_say_verb = "says" - var/bubble_icon = "normal" ///what icon the atom uses for speechbubbles - var/fluorescent // Shows up under a UV light. - - var/last_bumped = 0 - - ///Chemistry. - var/datum/reagents/reagents = null - - //var/chem_is_open_container = 0 - // replaced by OPENCONTAINER flags and atom/proc/is_open_container() - ///Chemistry. - - // Overlays - ///Our local copy of (non-priority) overlays without byond magic. Use procs in SSoverlays to manipulate - var/list/our_overlays - ///Overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. - var/list/priority_overlays - ///vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays - var/list/managed_vis_overlays - - ///Our local copy of filter data so we can add/remove it - var/list/filter_data - - //Detective Work, used for the duplicate data points kept in the scanners - var/list/original_atom - // Track if we are already had initialize() called to prevent double-initialization. - var/initialized = FALSE - - /// Last name used to calculate a color for the chatmessage overlays - var/chat_color_name - /// Last color calculated for the the chatmessage overlays - var/chat_color - /// A luminescence-shifted value of the last color calculated for chatmessage overlays - var/chat_color_darkened - /// The chat color var, without alpha. - var/chat_color_hover - -/atom/New(loc, ...) - // Don't call ..() unless /datum/New() ever exists - - // During dynamic mapload (reader.dm) this assigns the var overrides from the .dmm file - // Native BYOND maploading sets those vars before invoking New(), by doing this FIRST we come as close to that behavior as we can. - if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New() - GLOB._preloader.load(src) - - // Pass our arguments to InitAtom so they can be passed to initialize(), but replace 1st with if-we're-during-mapload. - var/do_initialize = SSatoms.initialized - if(do_initialize > INITIALIZATION_INSSATOMS) - args[1] = (do_initialize == INITIALIZATION_INNEW_MAPLOAD) - if(SSatoms.InitAtom(src, args)) - // We were deleted. No sense continuing - return - - // Uncomment if anything ever uses the return value of SSatoms.InitializeAtoms ~Leshana - // If a map is being loaded, it might want to know about newly created objects so they can be handled. - // var/list/created = SSatoms.created_atoms - // if(created) - // created += src - -// Note: I removed "auto_init" feature (letting types disable auto-init) since it shouldn't be needed anymore. -// You can replicate the same by checking the value of the first parameter to initialize() ~Leshana - -// Called after New if the map is being loaded, with mapload = TRUE -// Called from base of New if the map is not being loaded, with mapload = FALSE -// This base must be called or derivatives must set initialized to TRUE -// Must not sleep! -// Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE -// Must return an Initialize hint. Defined in code/__defines/subsystems.dm -/atom/proc/Initialize(mapload, ...) - if(QDELETED(src)) - stack_trace("GC: -- [type] had initialize() called after qdel() --") - if(initialized) - stack_trace("Warning: [src]([type]) initialized multiple times!") - initialized = TRUE - return INITIALIZE_HINT_NORMAL - -/atom/Destroy() - if(reagents) - QDEL_NULL(reagents) - if(light) - QDEL_NULL(light) - return ..() - -// Called after all object's normal initialize() if initialize() returns INITIALIZE_HINT_LATELOAD -/atom/proc/LateInitialize() - return - -/atom/proc/reveal_blood() - return - -/atom/proc/assume_air(datum/gas_mixture/giver) - return null - -/atom/proc/remove_air(amount) - return null - -/atom/proc/return_air() - if(loc) - return loc.return_air() - else - return null - -//return flags that should be added to the viewer's sight var. -//Otherwise return a negative number to indicate that the view should be cancelled. -/atom/proc/check_eye(user as mob) - if (istype(user, /mob/living/silicon/ai)) // WHYYYY - return 0 - return -1 - -/atom/proc/Bumped(AM as mob|obj) - set waitfor = FALSE - - SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, AM) - -// Convenience proc to see if a container is open for chemistry handling -// returns true if open -// false if closed -/atom/proc/is_open_container() - return flags & OPENCONTAINER - -/*//Convenience proc to see whether a container can be accessed in a certain way. - - proc/can_subract_container() - return flags & EXTRACT_CONTAINER - - proc/can_add_container() - return flags & INSERT_CONTAINER -*/ - -// Used to be for the PROXMOVE flag, but that was terrible, so instead it's just here as a stub for -// all the atoms that still have the proc, but get events other ways. -/atom/proc/HasProximity(turf/T, atom/movable/AM, old_loc) - return - -//Register listeners on turfs in a certain range -/atom/proc/sense_proximity(var/range = 1, var/callback) - ASSERT(callback) - ASSERT(isturf(loc)) - var/list/turfs = trange(range, src) - for(var/turf/T as anything in turfs) - GLOB.turf_entered_event.register(T, src, callback) - -//Unregister from prox listening in a certain range. You should do this BEFORE you move, but if you -// really can't, then you can set the center where you moved from. -/atom/proc/unsense_proximity(var/range = 1, var/callback, var/center) - ASSERT(isturf(center) || isturf(loc)) - var/list/turfs = trange(range, center ? center : src) - for(var/turf/T as anything in turfs) - GLOB.turf_entered_event.unregister(T, src, callback) - - -/atom/proc/emp_act(var/severity) - return - -/atom/proc/bullet_act(obj/item/projectile/P, def_zone) - if(SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone) & COMPONENT_CANCEL_ATTACK_CHAIN) - return - - P.on_hit(src, 0, def_zone) - . = 0 - -// Called when a blob expands onto the tile the atom occupies. -/atom/proc/blob_act() - return - -/atom/proc/in_contents_of(container)//can take class or object instance as argument - if(ispath(container)) - if(istype(src.loc, container)) - return 1 - else if(src in container) - return 1 - return - -/* - * atom/proc/search_contents_for(path,list/filter_path=null) - * Recursevly searches all atom contens (including contents contents and so on). - * - * ARGS: path - search atom contents for atoms of this type - * list/filter_path - if set, contents of atoms not of types in this list are excluded from search. - * - * RETURNS: list of found atoms - */ - -/atom/proc/search_contents_for(path,list/filter_path=null) - var/list/found = list() - for(var/atom/A in src) - if(istype(A, path)) - found += A - if(filter_path) - var/pass = 0 - for(var/type in filter_path) - pass |= istype(A, type) - if(!pass) - continue - if(A.contents.len) - found += A.search_contents_for(path,filter_path) - return found - -/atom/proc/get_examine_desc() - return desc - -//All atoms -/atom/proc/examine(mob/user, var/infix = "", var/suffix = "") - //This reformat names to get a/an properly working on item descriptions when they are bloody - var/f_name = "\a [src][infix]." - if(src.blood_DNA && !istype(src, /obj/effect/decal)) - if(gender == PLURAL) - f_name = "some " - else - f_name = "a " - if(blood_color != SYNTH_BLOOD_COLOUR) - f_name += "blood-stained [name][infix]!" - else - f_name += "oil-stained [name][infix]." - - var/list/output = list("\icon[src.examine_icon()][bicon(src)] That's [f_name] [suffix]", get_examine_desc()) - - if(user.client?.prefs.examine_text_mode == EXAMINE_MODE_INCLUDE_USAGE) - output += description_info - - if(user.client?.prefs.examine_text_mode == EXAMINE_MODE_SWITCH_TO_PANEL) - user.client.statpanel = "Examine" // Switch to stat panel - SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, output) - return output - -// Don't make these call bicon or anything, these are what bicon uses. They need to return an icon. -/atom/proc/examine_icon() - return icon(icon=src.icon, icon_state=src.icon_state, dir=SOUTH, frame=1, moving=0) - -// called by mobs when e.g. having the atom as their machine, pulledby, loc (AKA mob being inside the atom) or buckled var set. -// see code/modules/mob/mob_movement.dm for more. -/atom/proc/relaymove() - return - -//called to set the atom's dir and used to add behaviour to dir-changes -/atom/proc/set_dir(new_dir) - SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, new_dir) - . = new_dir != dir - dir = new_dir - -// Called to set the atom's density and used to add behavior to density changes. -/atom/proc/set_density(var/new_density) - if(density == new_density) - return FALSE - density = !!new_density // Sanitize to be strictly 0 or 1 - return TRUE - -// Called to set the atom's invisibility and usd to add behavior to invisibility changes. -/atom/proc/set_invisibility(var/new_invisibility) - if(invisibility == new_invisibility) - return FALSE - invisibility = new_invisibility - return TRUE - -/atom/proc/ex_act(var/strength = 3) - return (SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, strength, src) & COMPONENT_IGNORE_EXPLOSION) - -/atom/proc/emag_act(var/remaining_charges, var/mob/user, var/emag_source) - return -1 - -/atom/proc/fire_act() - return - - -// Returns an assoc list of RCD information. -// Example would be: list(RCD_VALUE_MODE = RCD_DECONSTRUCT, RCD_VALUE_DELAY = 50, RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 4) -// This occurs before rcd_act() is called, and it won't be called if it returns FALSE. -/atom/proc/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - return FALSE - -/atom/proc/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - return - -/atom/proc/melt() - return - -// Previously this was defined both on /obj/ and /turf/ seperately. And that's bad. -/atom/proc/update_icon() - return - - -/atom/proc/hitby(atom/movable/AM as mob|obj) - if (density) - AM.throwing = 0 - return - -/atom/proc/add_hiddenprint(mob/living/M as mob) - if(isnull(M)) return - if(isnull(M.key)) return - if (ishuman(M)) - var/mob/living/carbon/human/H = M - if (!istype(H.dna, /datum/dna)) - return 0 - if (H.gloves) - if(src.fingerprintslast != H.key) - src.fingerprintshidden += text("\[[time_stamp()]\] (Wearing gloves). Real name: [], Key: []",H.real_name, H.key) - src.fingerprintslast = H.key - return 0 - if (!( src.fingerprints )) - if(src.fingerprintslast != H.key) - src.fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",H.real_name, H.key) - src.fingerprintslast = H.key - return 1 - else - if(src.fingerprintslast != M.key) - src.fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",M.real_name, M.key) - src.fingerprintslast = M.key - return - -/atom/proc/add_fingerprint(mob/living/M as mob, ignoregloves = 0) - if(isnull(M)) return - if(isAI(M)) return - if(isnull(M.key)) return - if (ishuman(M)) - //Add the list if it does not exist. - if(!fingerprintshidden) - fingerprintshidden = list() - - //Fibers~ - add_fibers(M) - - //He has no prints! - if (mFingerprints in M.mutations) - if(fingerprintslast != M.key) - fingerprintshidden += "[time_stamp()]: [key_name(M)] (No fingerprints mutation)" - fingerprintslast = M.key - return 0 //Now, lets get to the dirty work. - //First, make sure their DNA makes sense. - var/mob/living/carbon/human/H = M - if (!istype(H.dna, /datum/dna) || !H.dna.uni_identity || (length(H.dna.uni_identity) != 32)) - if(!istype(H.dna, /datum/dna)) - H.dna = new /datum/dna(null) - H.dna.real_name = H.real_name - H.check_dna() - - //Now, deal with gloves. - if (H.gloves && H.gloves != src) - if(fingerprintslast != H.key) - fingerprintshidden += "[time_stamp()]: [key_name(H)] (Wearing [H.gloves])" - fingerprintslast = H.key - H.gloves.add_fingerprint(M) - - //Deal with gloves the pass finger/palm prints. - if(!ignoregloves) - if(H.gloves && H.gloves != src) - if(istype(H.gloves, /obj/item/clothing/gloves)) - var/obj/item/clothing/gloves/G = H.gloves - if(!prob(G.fingerprint_chance)) - return 0 - - //More adminstuffz - if(fingerprintslast != H.key) - fingerprintshidden += "[time_stamp()]: [key_name(H)]" - fingerprintslast = H.key - - //Make the list if it does not exist. - if(!fingerprints) - fingerprints = list() - - //Hash this shit. - var/full_print = H.get_full_print() - - // Add the fingerprints - // - if(fingerprints[full_print]) - switch(stringpercent(fingerprints[full_print])) //tells us how many stars are in the current prints. - - if(28 to 32) - if(prob(1)) - fingerprints[full_print] = full_print // You rolled a one buddy. - else - fingerprints[full_print] = stars(full_print, rand(0,40)) // 24 to 32 - - if(24 to 27) - if(prob(3)) - fingerprints[full_print] = full_print //Sucks to be you. - else - fingerprints[full_print] = stars(full_print, rand(15, 55)) // 20 to 29 - - if(20 to 23) - if(prob(5)) - fingerprints[full_print] = full_print //Had a good run didn't ya. - else - fingerprints[full_print] = stars(full_print, rand(30, 70)) // 15 to 25 - - if(16 to 19) - if(prob(5)) - fingerprints[full_print] = full_print //Welp. - else - fingerprints[full_print] = stars(full_print, rand(40, 100)) // 0 to 21 - - if(0 to 15) - if(prob(5)) - fingerprints[full_print] = stars(full_print, rand(0,50)) // small chance you can smudge. - else - fingerprints[full_print] = full_print - - else - fingerprints[full_print] = stars(full_print, rand(0, 20)) //Initial touch, not leaving much evidence the first time. - - - return 1 - else - //Smudge up dem prints some - if(fingerprintslast != M.key) - fingerprintshidden += "[time_stamp()]: [key_name(M)]" - fingerprintslast = M.key - - //Cleaning up shit. - if(fingerprints && !fingerprints.len) - qdel(fingerprints) - return - - -/atom/proc/transfer_fingerprints_to(var/atom/A) - - if(!istype(A.fingerprints,/list)) - A.fingerprints = list() - - if(!istype(A.fingerprintshidden,/list)) - A.fingerprintshidden = list() - - if(!istype(fingerprintshidden, /list)) - fingerprintshidden = list() - - //skytodo - //A.fingerprints |= fingerprints //detective - //A.fingerprintshidden |= fingerprintshidden //admin - if(A.fingerprints && fingerprints) - A.fingerprints |= fingerprints.Copy() //detective - if(A.fingerprintshidden && fingerprintshidden) - A.fingerprintshidden |= fingerprintshidden.Copy() //admin A.fingerprintslast = fingerprintslast - - -//returns 1 if made bloody, returns 0 otherwise -/atom/proc/add_blood(mob/living/carbon/human/M as mob) - - if(flags & NOBLOODY) - return 0 - - if(!blood_DNA || !istype(blood_DNA, /list)) //if our list of DNA doesn't exist yet (or isn't a list) initialise it. - blood_DNA = list() - - was_bloodied = TRUE - if(!blood_color) - blood_color = "#A10808" - if(istype(M)) - if (!istype(M.dna, /datum/dna)) - M.dna = new /datum/dna(null) - M.dna.real_name = M.real_name - M.check_dna() - blood_color = M.species.get_blood_colour(M) - . = 1 - return 1 - -/atom/proc/add_vomit_floor(mob/living/carbon/M as mob, var/toxvomit = 0) - if( istype(src, /turf/simulated) ) - var/obj/effect/decal/cleanable/vomit/this = new /obj/effect/decal/cleanable/vomit(src) - this.virus2 = virus_copylist(M.virus2) - - // Make toxins vomit look different - if(toxvomit) - this.icon_state = "vomittox_[pick(1,4)]" - -/atom/proc/clean_blood() - if(!simulated) - return - fluorescent = 0 - src.germ_level = 0 - if(istype(blood_DNA, /list)) - blood_DNA = null - return TRUE - -/atom/proc/on_rag_wipe(var/obj/item/weapon/reagent_containers/glass/rag/R) - clean_blood() - R.reagents.splash(src, 1) - -/atom/proc/get_global_map_pos() - if(!islist(global_map) || isemptylist(global_map)) return - var/cur_x = null - var/cur_y = null - var/list/y_arr = null - for(cur_x=1,cur_x<=global_map.len,cur_x++) - y_arr = global_map[cur_x] - cur_y = y_arr.Find(src.z) - if(cur_y) - break -// to_world("X = [cur_x]; Y = [cur_y]") - if(cur_x && cur_y) - return list("x"=cur_x,"y"=cur_y) - else - return 0 - -/atom/proc/checkpass(passflag) - return (pass_flags&passflag) - -/atom/proc/isinspace() - if(istype(get_turf(src), /turf/space)) - return 1 - else - return 0 - -// Show a message to all mobs and objects in sight of this atom -// Use for objects performing visible actions -// message is output to anyone who can see, e.g. "The [src] does something!" -// blind_message (optional) is what blind people will hear e.g. "You hear something!" -/atom/proc/visible_message(var/message, var/blind_message, var/list/exclude_mobs, var/range = world.view, var/runemessage = "ðŸ‘") - - //VOREStation Edit - var/list/see - if(isbelly(loc)) - var/obj/belly/B = loc - see = B.get_mobs_and_objs_in_belly() - else - see = get_mobs_and_objs_in_view_fast(get_turf(src), range, remote_ghosts = FALSE) - //VOREStation Edit End - - var/list/seeing_mobs = see["mobs"] - var/list/seeing_objs = see["objs"] - if(LAZYLEN(exclude_mobs)) - seeing_mobs -= exclude_mobs - - for(var/obj/O as anything in seeing_objs) - O.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE) - for(var/mob/M as anything in seeing_mobs) - if(M.see_invisible >= invisibility && MOB_CAN_SEE_PLANE(M, plane)) - M.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE) - if(runemessage != -1) - M.create_chat_message(src, "[runemessage]", FALSE, list("emote"), audible = FALSE) - else if(blind_message) - M.show_message(blind_message, AUDIBLE_MESSAGE) - -// Show a message to all mobs and objects in earshot of this atom -// Use for objects performing audible actions -// message is the message output to anyone who can hear. -// deaf_message (optional) is what deaf people will see. -// hearing_distance (optional) is the range, how many tiles away the message can be heard. -/atom/proc/audible_message(var/message, var/deaf_message, var/hearing_distance, var/radio_message, var/runemessage) - - var/range = hearing_distance || world.view - var/list/hear = get_mobs_and_objs_in_view_fast(get_turf(src),range,remote_ghosts = FALSE) - - var/list/hearing_mobs = hear["mobs"] - var/list/hearing_objs = hear["objs"] - - if(radio_message) - for(var/obj/O as anything in hearing_objs) - O.hear_talk(src, list(new /datum/multilingual_say_piece(GLOB.all_languages["Noise"], radio_message)), null) - else - for(var/obj/O as anything in hearing_objs) - O.show_message(message, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE) - - for(var/mob/M as anything in hearing_mobs) - var/msg = message - M.show_message(msg, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE) - if(runemessage != -1) - M.create_chat_message(src, "[runemessage || message]", FALSE, list("emote")) - -/atom/movable/proc/dropInto(var/atom/destination) - while(istype(destination)) - var/atom/drop_destination = destination.onDropInto(src) - if(!istype(drop_destination) || drop_destination == destination) - return forceMove(destination) - destination = drop_destination - return moveToNullspace() - -/atom/proc/onDropInto(var/atom/movable/AM) - return // If onDropInto returns null, then dropInto will forceMove AM into us. - -/atom/movable/onDropInto(var/atom/movable/AM) - return loc // If onDropInto returns something, then dropInto will attempt to drop AM there. - -/atom/proc/InsertedContents() - return contents - -/atom/proc/has_gravity(turf/T) - if(!T || !isturf(T)) - T = get_turf(src) - if(istype(T, /turf/space)) // Turf never has gravity - return FALSE - var/area/A = get_area(T) - if(A && A.has_gravity()) - return TRUE - return FALSE - -/atom/proc/is_incorporeal() - return FALSE - -/atom/proc/drop_location() - var/atom/L = loc - if(!L) - return null - return L.AllowDrop() ? L : L.drop_location() - -/atom/proc/AllowDrop() - return FALSE - -/atom/proc/get_nametag_name(mob/user) - return name - -/atom/proc/get_nametag_desc(mob/user) - return "" //Desc itself is often too long to use - -/atom/vv_get_dropdown() - . = ..() - VV_DROPDOWN_OPTION(VV_HK_ATOM_EXPLODE, "Explosion") - VV_DROPDOWN_OPTION(VV_HK_ATOM_EMP, "Emp Pulse") - -/atom/vv_do_topic(list/href_list) - . = ..() - IF_VV_OPTION(VV_HK_ATOM_EXPLODE) - if(!check_rights(R_DEBUG|R_FUN)) - return - usr.client.cmd_admin_explosion(src) - href_list["datumrefresh"] = "\ref[src]" - IF_VV_OPTION(VV_HK_ATOM_EMP) - if(!check_rights(R_DEBUG|R_FUN)) - return - usr.client.cmd_admin_emp(src) - href_list["datumrefresh"] = "\ref[src]" - -/atom/vv_get_header() - . = ..() - var/custom_edit_name - if(!isliving(src)) - custom_edit_name = "[src]" - . += {" - [custom_edit_name] -
    - << - [dir2text(dir)] - >> - - "} - var/turf/T = get_turf(src) - . += "
    [ADMIN_COORDJMP(T)]" - -/atom/vv_edit_var(var_name, var_value) - switch(var_name) - if(NAMEOF(src, light_range)) - if(light_system == STATIC_LIGHT) - set_light(l_range = var_value) - else - set_light_range(var_value) - . = TRUE - if(NAMEOF(src, light_power)) - if(light_system == STATIC_LIGHT) - set_light(l_power = var_value) - else - set_light_power(var_value) - . = TRUE - if(NAMEOF(src, light_color)) - if(light_system == STATIC_LIGHT) - set_light(l_color = var_value) - else - set_light_color(var_value) - . = TRUE - if(NAMEOF(src, light_on)) - set_light_on(var_value) - . = TRUE - if(NAMEOF(src, light_flags)) - set_light_flags(var_value) - . = TRUE - if(NAMEOF(src, opacity)) - set_opacity(var_value) - . = TRUE - - if(!isnull(.)) - datum_flags |= DF_VAR_EDITED - return - - . = ..() - -/atom/proc/atom_say(message) - if(!message) - return - var/list/speech_bubble_hearers = list() - for(var/mob/M in get_mobs_in_view(7, src)) - M.show_message("[src] [atom_say_verb], \"[message]\"", 2, null, 1) - if(M.client) - speech_bubble_hearers += M.client - - if(length(speech_bubble_hearers)) - var/image/I = generate_speech_bubble(src, "[bubble_icon][say_test(message)]", FLY_LAYER) - I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA - INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(flick_overlay), I, speech_bubble_hearers, 30) - -/atom/proc/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list()) - return - -/atom/Entered(atom/movable/AM, atom/old_loc) - . = ..() - GLOB.moved_event.raise_event(AM, old_loc, AM.loc) - SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, old_loc) - SEND_SIGNAL(AM, COMSIG_ATOM_ENTERING, src, old_loc) - -/atom/Exit(atom/movable/AM, atom/new_loc) - . = ..() - if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, new_loc) & COMPONENT_ATOM_BLOCK_EXIT) - return FALSE - -/atom/Exited(atom/movable/AM, atom/new_loc) - . = ..() - SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, new_loc) - -/atom/proc/get_visible_gender(mob/user, force) - return gender - -/atom/proc/interact(mob/user) - return - -// Purpose: Determines if the object can pass this atom. -// Called by: Movement. -// Inputs: The moving atom, target turf. -// Outputs: Boolean if can pass. -// Airflow and ZAS zones now uses CanZASPass() instead of this proc. -/atom/proc/CanPass(atom/movable/mover, turf/target) - return !density +/atom + layer = TURF_LAYER //This was here when I got here. Why though? + var/level = 2 + var/flags = 0 + var/list/fingerprints + var/list/fingerprintshidden + var/fingerprintslast = null + var/list/blood_DNA + var/was_bloodied + var/blood_color + var/pass_flags = 0 + var/throwpass = 0 + var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom. + var/simulated = TRUE //filter for actions - used by lighting overlays + var/atom_say_verb = "says" + var/bubble_icon = "normal" ///what icon the atom uses for speechbubbles + var/fluorescent // Shows up under a UV light. + + var/last_bumped = 0 + + ///Chemistry. + var/datum/reagents/reagents = null + + //var/chem_is_open_container = 0 + // replaced by OPENCONTAINER flags and atom/proc/is_open_container() + ///Chemistry. + + // Overlays + ///Our local copy of (non-priority) overlays without byond magic. Use procs in SSoverlays to manipulate + var/list/our_overlays + ///Overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. + var/list/priority_overlays + ///vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays + var/list/managed_vis_overlays + + ///Our local copy of filter data so we can add/remove it + var/list/filter_data + + //Detective Work, used for the duplicate data points kept in the scanners + var/list/original_atom + // Track if we are already had initialize() called to prevent double-initialization. + var/initialized = FALSE + + /// Last name used to calculate a color for the chatmessage overlays + var/chat_color_name + /// Last color calculated for the the chatmessage overlays + var/chat_color + /// A luminescence-shifted value of the last color calculated for chatmessage overlays + var/chat_color_darkened + /// The chat color var, without alpha. + var/chat_color_hover + +/atom/New(loc, ...) + // Don't call ..() unless /datum/New() ever exists + + // During dynamic mapload (reader.dm) this assigns the var overrides from the .dmm file + // Native BYOND maploading sets those vars before invoking New(), by doing this FIRST we come as close to that behavior as we can. + if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New() + GLOB._preloader.load(src) + + // Pass our arguments to InitAtom so they can be passed to initialize(), but replace 1st with if-we're-during-mapload. + var/do_initialize = SSatoms.initialized + if(do_initialize > INITIALIZATION_INSSATOMS) + args[1] = (do_initialize == INITIALIZATION_INNEW_MAPLOAD) + if(SSatoms.InitAtom(src, args)) + // We were deleted. No sense continuing + return + + // Uncomment if anything ever uses the return value of SSatoms.InitializeAtoms ~Leshana + // If a map is being loaded, it might want to know about newly created objects so they can be handled. + // var/list/created = SSatoms.created_atoms + // if(created) + // created += src + +// Note: I removed "auto_init" feature (letting types disable auto-init) since it shouldn't be needed anymore. +// You can replicate the same by checking the value of the first parameter to initialize() ~Leshana + +// Called after New if the map is being loaded, with mapload = TRUE +// Called from base of New if the map is not being loaded, with mapload = FALSE +// This base must be called or derivatives must set initialized to TRUE +// Must not sleep! +// Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE +// Must return an Initialize hint. Defined in code/__defines/subsystems.dm +/atom/proc/Initialize(mapload, ...) + if(QDELETED(src)) + stack_trace("GC: -- [type] had initialize() called after qdel() --") + if(initialized) + stack_trace("Warning: [src]([type]) initialized multiple times!") + initialized = TRUE + return INITIALIZE_HINT_NORMAL + +/atom/Destroy() + if(reagents) + QDEL_NULL(reagents) + if(light) + QDEL_NULL(light) + return ..() + +// Called after all object's normal initialize() if initialize() returns INITIALIZE_HINT_LATELOAD +/atom/proc/LateInitialize() + return + +/atom/proc/reveal_blood() + return + +/atom/proc/assume_air(datum/gas_mixture/giver) + return null + +/atom/proc/remove_air(amount) + return null + +/atom/proc/return_air() + if(loc) + return loc.return_air() + else + return null + +//return flags that should be added to the viewer's sight var. +//Otherwise return a negative number to indicate that the view should be cancelled. +/atom/proc/check_eye(user as mob) + if (istype(user, /mob/living/silicon/ai)) // WHYYYY + return 0 + return -1 + +/atom/proc/Bumped(AM as mob|obj) + set waitfor = FALSE + + SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, AM) + +// Convenience proc to see if a container is open for chemistry handling +// returns true if open +// false if closed +/atom/proc/is_open_container() + return flags & OPENCONTAINER + +/*//Convenience proc to see whether a container can be accessed in a certain way. + + proc/can_subract_container() + return flags & EXTRACT_CONTAINER + + proc/can_add_container() + return flags & INSERT_CONTAINER +*/ + +// Used to be for the PROXMOVE flag, but that was terrible, so instead it's just here as a stub for +// all the atoms that still have the proc, but get events other ways. +/atom/proc/HasProximity(turf/T, atom/movable/AM, old_loc) + return + +//Register listeners on turfs in a certain range +/atom/proc/sense_proximity(var/range = 1, var/callback) + ASSERT(callback) + ASSERT(isturf(loc)) + var/list/turfs = trange(range, src) + for(var/turf/T as anything in turfs) + GLOB.turf_entered_event.register(T, src, callback) + +//Unregister from prox listening in a certain range. You should do this BEFORE you move, but if you +// really can't, then you can set the center where you moved from. +/atom/proc/unsense_proximity(var/range = 1, var/callback, var/center) + ASSERT(isturf(center) || isturf(loc)) + var/list/turfs = trange(range, center ? center : src) + for(var/turf/T as anything in turfs) + GLOB.turf_entered_event.unregister(T, src, callback) + + +/atom/proc/emp_act(var/severity) + return + +/atom/proc/bullet_act(obj/item/projectile/P, def_zone) + if(SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone) & COMPONENT_CANCEL_ATTACK_CHAIN) + return + + P.on_hit(src, 0, def_zone) + . = 0 + +// Called when a blob expands onto the tile the atom occupies. +/atom/proc/blob_act() + return + +/atom/proc/in_contents_of(container)//can take class or object instance as argument + if(ispath(container)) + if(istype(src.loc, container)) + return 1 + else if(src in container) + return 1 + return + +/* + * atom/proc/search_contents_for(path,list/filter_path=null) + * Recursevly searches all atom contens (including contents contents and so on). + * + * ARGS: path - search atom contents for atoms of this type + * list/filter_path - if set, contents of atoms not of types in this list are excluded from search. + * + * RETURNS: list of found atoms + */ + +/atom/proc/search_contents_for(path,list/filter_path=null) + var/list/found = list() + for(var/atom/A in src) + if(istype(A, path)) + found += A + if(filter_path) + var/pass = 0 + for(var/type in filter_path) + pass |= istype(A, type) + if(!pass) + continue + if(A.contents.len) + found += A.search_contents_for(path,filter_path) + return found + +/atom/proc/get_examine_desc() + return desc + +//All atoms +/atom/proc/examine(mob/user, var/infix = "", var/suffix = "") + //This reformat names to get a/an properly working on item descriptions when they are bloody + var/f_name = "\a [src][infix]." + if(src.blood_DNA && !istype(src, /obj/effect/decal)) + if(gender == PLURAL) + f_name = "some " + else + f_name = "a " + if(blood_color != SYNTH_BLOOD_COLOUR) + f_name += "blood-stained [name][infix]!" + else + f_name += "oil-stained [name][infix]." + + var/list/output = list("\icon[src.examine_icon()][bicon(src)] That's [f_name] [suffix]", get_examine_desc()) + + if(user.client?.prefs.examine_text_mode == EXAMINE_MODE_INCLUDE_USAGE) + output += description_info + + if(user.client?.prefs.examine_text_mode == EXAMINE_MODE_SWITCH_TO_PANEL) + user.client.statpanel = "Examine" // Switch to stat panel + SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, output) + return output + +// Don't make these call bicon or anything, these are what bicon uses. They need to return an icon. +/atom/proc/examine_icon() + return icon(icon=src.icon, icon_state=src.icon_state, dir=SOUTH, frame=1, moving=0) + +// called by mobs when e.g. having the atom as their machine, pulledby, loc (AKA mob being inside the atom) or buckled var set. +// see code/modules/mob/mob_movement.dm for more. +/atom/proc/relaymove() + return + +//called to set the atom's dir and used to add behaviour to dir-changes +/atom/proc/set_dir(new_dir) + SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, new_dir) + . = new_dir != dir + dir = new_dir + +// Called to set the atom's density and used to add behavior to density changes. +/atom/proc/set_density(var/new_density) + if(density == new_density) + return FALSE + density = !!new_density // Sanitize to be strictly 0 or 1 + return TRUE + +// Called to set the atom's invisibility and usd to add behavior to invisibility changes. +/atom/proc/set_invisibility(var/new_invisibility) + if(invisibility == new_invisibility) + return FALSE + invisibility = new_invisibility + return TRUE + +/atom/proc/ex_act(var/strength = 3) + return (SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, strength, src) & COMPONENT_IGNORE_EXPLOSION) + +/atom/proc/emag_act(var/remaining_charges, var/mob/user, var/emag_source) + return -1 + +/atom/proc/fire_act() + return + + +// Returns an assoc list of RCD information. +// Example would be: list(RCD_VALUE_MODE = RCD_DECONSTRUCT, RCD_VALUE_DELAY = 50, RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 4) +// This occurs before rcd_act() is called, and it won't be called if it returns FALSE. +/atom/proc/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + return FALSE + +/atom/proc/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + return + +/atom/proc/melt() + return + +// Previously this was defined both on /obj/ and /turf/ seperately. And that's bad. +/atom/proc/update_icon() + return + + +/atom/proc/hitby(atom/movable/AM as mob|obj) + if (density) + AM.throwing = 0 + return + +/atom/proc/add_hiddenprint(mob/living/M as mob) + if(isnull(M)) return + if(isnull(M.key)) return + if (ishuman(M)) + var/mob/living/carbon/human/H = M + if (!istype(H.dna, /datum/dna)) + return 0 + if (H.gloves) + if(src.fingerprintslast != H.key) + src.fingerprintshidden += text("\[[time_stamp()]\] (Wearing gloves). Real name: [], Key: []",H.real_name, H.key) + src.fingerprintslast = H.key + return 0 + if (!( src.fingerprints )) + if(src.fingerprintslast != H.key) + src.fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",H.real_name, H.key) + src.fingerprintslast = H.key + return 1 + else + if(src.fingerprintslast != M.key) + src.fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",M.real_name, M.key) + src.fingerprintslast = M.key + return + +/atom/proc/add_fingerprint(mob/living/M as mob, ignoregloves = 0) + if(isnull(M)) return + if(isAI(M)) return + if(isnull(M.key)) return + if (ishuman(M)) + //Add the list if it does not exist. + if(!fingerprintshidden) + fingerprintshidden = list() + + //Fibers~ + add_fibers(M) + + //He has no prints! + if (mFingerprints in M.mutations) + if(fingerprintslast != M.key) + fingerprintshidden += "[time_stamp()]: [key_name(M)] (No fingerprints mutation)" + fingerprintslast = M.key + return 0 //Now, lets get to the dirty work. + //First, make sure their DNA makes sense. + var/mob/living/carbon/human/H = M + if (!istype(H.dna, /datum/dna) || !H.dna.uni_identity || (length(H.dna.uni_identity) != 32)) + if(!istype(H.dna, /datum/dna)) + H.dna = new /datum/dna(null) + H.dna.real_name = H.real_name + H.check_dna() + + //Now, deal with gloves. + if (H.gloves && H.gloves != src) + if(fingerprintslast != H.key) + fingerprintshidden += "[time_stamp()]: [key_name(H)] (Wearing [H.gloves])" + fingerprintslast = H.key + H.gloves.add_fingerprint(M) + + //Deal with gloves the pass finger/palm prints. + if(!ignoregloves) + if(H.gloves && H.gloves != src) + if(istype(H.gloves, /obj/item/clothing/gloves)) + var/obj/item/clothing/gloves/G = H.gloves + if(!prob(G.fingerprint_chance)) + return 0 + + //More adminstuffz + if(fingerprintslast != H.key) + fingerprintshidden += "[time_stamp()]: [key_name(H)]" + fingerprintslast = H.key + + //Make the list if it does not exist. + if(!fingerprints) + fingerprints = list() + + //Hash this shit. + var/full_print = H.get_full_print() + + // Add the fingerprints + // + if(fingerprints[full_print]) + switch(stringpercent(fingerprints[full_print])) //tells us how many stars are in the current prints. + + if(28 to 32) + if(prob(1)) + fingerprints[full_print] = full_print // You rolled a one buddy. + else + fingerprints[full_print] = stars(full_print, rand(0,40)) // 24 to 32 + + if(24 to 27) + if(prob(3)) + fingerprints[full_print] = full_print //Sucks to be you. + else + fingerprints[full_print] = stars(full_print, rand(15, 55)) // 20 to 29 + + if(20 to 23) + if(prob(5)) + fingerprints[full_print] = full_print //Had a good run didn't ya. + else + fingerprints[full_print] = stars(full_print, rand(30, 70)) // 15 to 25 + + if(16 to 19) + if(prob(5)) + fingerprints[full_print] = full_print //Welp. + else + fingerprints[full_print] = stars(full_print, rand(40, 100)) // 0 to 21 + + if(0 to 15) + if(prob(5)) + fingerprints[full_print] = stars(full_print, rand(0,50)) // small chance you can smudge. + else + fingerprints[full_print] = full_print + + else + fingerprints[full_print] = stars(full_print, rand(0, 20)) //Initial touch, not leaving much evidence the first time. + + + return 1 + else + //Smudge up dem prints some + if(fingerprintslast != M.key) + fingerprintshidden += "[time_stamp()]: [key_name(M)]" + fingerprintslast = M.key + + //Cleaning up shit. + if(fingerprints && !fingerprints.len) + qdel(fingerprints) + return + + +/atom/proc/transfer_fingerprints_to(var/atom/A) + + if(!istype(A.fingerprints,/list)) + A.fingerprints = list() + + if(!istype(A.fingerprintshidden,/list)) + A.fingerprintshidden = list() + + if(!istype(fingerprintshidden, /list)) + fingerprintshidden = list() + + //skytodo + //A.fingerprints |= fingerprints //detective + //A.fingerprintshidden |= fingerprintshidden //admin + if(A.fingerprints && fingerprints) + A.fingerprints |= fingerprints.Copy() //detective + if(A.fingerprintshidden && fingerprintshidden) + A.fingerprintshidden |= fingerprintshidden.Copy() //admin A.fingerprintslast = fingerprintslast + + +//returns 1 if made bloody, returns 0 otherwise +/atom/proc/add_blood(mob/living/carbon/human/M as mob) + + if(flags & NOBLOODY) + return 0 + + if(!blood_DNA || !istype(blood_DNA, /list)) //if our list of DNA doesn't exist yet (or isn't a list) initialise it. + blood_DNA = list() + + was_bloodied = TRUE + if(!blood_color) + blood_color = "#A10808" + if(istype(M)) + if (!istype(M.dna, /datum/dna)) + M.dna = new /datum/dna(null) + M.dna.real_name = M.real_name + M.check_dna() + blood_color = M.species.get_blood_colour(M) + . = 1 + return 1 + +/atom/proc/add_vomit_floor(mob/living/carbon/M as mob, var/toxvomit = 0) + if( istype(src, /turf/simulated) ) + var/obj/effect/decal/cleanable/vomit/this = new /obj/effect/decal/cleanable/vomit(src) + this.virus2 = virus_copylist(M.virus2) + + // Make toxins vomit look different + if(toxvomit) + this.icon_state = "vomittox_[pick(1,4)]" + +/atom/proc/clean_blood() + if(!simulated) + return + fluorescent = 0 + src.germ_level = 0 + if(istype(blood_DNA, /list)) + blood_DNA = null + return TRUE + +/atom/proc/on_rag_wipe(var/obj/item/weapon/reagent_containers/glass/rag/R) + clean_blood() + R.reagents.splash(src, 1) + +/atom/proc/get_global_map_pos() + if(!islist(global_map) || isemptylist(global_map)) return + var/cur_x = null + var/cur_y = null + var/list/y_arr = null + for(cur_x=1,cur_x<=global_map.len,cur_x++) + y_arr = global_map[cur_x] + cur_y = y_arr.Find(src.z) + if(cur_y) + break +// to_world("X = [cur_x]; Y = [cur_y]") + if(cur_x && cur_y) + return list("x"=cur_x,"y"=cur_y) + else + return 0 + +/atom/proc/checkpass(passflag) + return (pass_flags&passflag) + +/atom/proc/isinspace() + if(istype(get_turf(src), /turf/space)) + return 1 + else + return 0 + +// Show a message to all mobs and objects in sight of this atom +// Use for objects performing visible actions +// message is output to anyone who can see, e.g. "The [src] does something!" +// blind_message (optional) is what blind people will hear e.g. "You hear something!" +/atom/proc/visible_message(var/message, var/blind_message, var/list/exclude_mobs, var/range = world.view, var/runemessage = "ðŸ‘") + + //VOREStation Edit + var/list/see + if(isbelly(loc)) + var/obj/belly/B = loc + see = B.get_mobs_and_objs_in_belly() + else + see = get_mobs_and_objs_in_view_fast(get_turf(src), range, remote_ghosts = FALSE) + //VOREStation Edit End + + var/list/seeing_mobs = see["mobs"] + var/list/seeing_objs = see["objs"] + if(LAZYLEN(exclude_mobs)) + seeing_mobs -= exclude_mobs + + for(var/obj/O as anything in seeing_objs) + O.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE) + for(var/mob/M as anything in seeing_mobs) + if(M.see_invisible >= invisibility && MOB_CAN_SEE_PLANE(M, plane)) + M.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE) + if(runemessage != -1) + M.create_chat_message(src, "[runemessage]", FALSE, list("emote"), audible = FALSE) + else if(blind_message) + M.show_message(blind_message, AUDIBLE_MESSAGE) + +// Show a message to all mobs and objects in earshot of this atom +// Use for objects performing audible actions +// message is the message output to anyone who can hear. +// deaf_message (optional) is what deaf people will see. +// hearing_distance (optional) is the range, how many tiles away the message can be heard. +/atom/proc/audible_message(var/message, var/deaf_message, var/hearing_distance, var/radio_message, var/runemessage) + + var/range = hearing_distance || world.view + var/list/hear = get_mobs_and_objs_in_view_fast(get_turf(src),range,remote_ghosts = FALSE) + + var/list/hearing_mobs = hear["mobs"] + var/list/hearing_objs = hear["objs"] + + if(radio_message) + for(var/obj/O as anything in hearing_objs) + O.hear_talk(src, list(new /datum/multilingual_say_piece(GLOB.all_languages["Noise"], radio_message)), null) + else + for(var/obj/O as anything in hearing_objs) + O.show_message(message, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE) + + for(var/mob/M as anything in hearing_mobs) + var/msg = message + M.show_message(msg, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE) + if(runemessage != -1) + M.create_chat_message(src, "[runemessage || message]", FALSE, list("emote")) + +/atom/movable/proc/dropInto(var/atom/destination) + while(istype(destination)) + var/atom/drop_destination = destination.onDropInto(src) + if(!istype(drop_destination) || drop_destination == destination) + return forceMove(destination) + destination = drop_destination + return moveToNullspace() + +/atom/proc/onDropInto(var/atom/movable/AM) + return // If onDropInto returns null, then dropInto will forceMove AM into us. + +/atom/movable/onDropInto(var/atom/movable/AM) + return loc // If onDropInto returns something, then dropInto will attempt to drop AM there. + +/atom/proc/InsertedContents() + return contents + +/atom/proc/has_gravity(turf/T) + if(!T || !isturf(T)) + T = get_turf(src) + if(istype(T, /turf/space)) // Turf never has gravity + return FALSE + var/area/A = get_area(T) + if(A && A.has_gravity()) + return TRUE + return FALSE + +/atom/proc/is_incorporeal() + return FALSE + +/atom/proc/drop_location() + var/atom/L = loc + if(!L) + return null + return L.AllowDrop() ? L : L.drop_location() + +/atom/proc/AllowDrop() + return FALSE + +/atom/proc/get_nametag_name(mob/user) + return name + +/atom/proc/get_nametag_desc(mob/user) + return "" //Desc itself is often too long to use + +/atom/vv_get_dropdown() + . = ..() + VV_DROPDOWN_OPTION(VV_HK_ATOM_EXPLODE, "Explosion") + VV_DROPDOWN_OPTION(VV_HK_ATOM_EMP, "Emp Pulse") + +/atom/vv_do_topic(list/href_list) + . = ..() + IF_VV_OPTION(VV_HK_ATOM_EXPLODE) + if(!check_rights(R_DEBUG|R_FUN)) + return + usr.client.cmd_admin_explosion(src) + href_list["datumrefresh"] = "\ref[src]" + IF_VV_OPTION(VV_HK_ATOM_EMP) + if(!check_rights(R_DEBUG|R_FUN)) + return + usr.client.cmd_admin_emp(src) + href_list["datumrefresh"] = "\ref[src]" + +/atom/vv_get_header() + . = ..() + var/custom_edit_name + if(!isliving(src)) + custom_edit_name = "[src]" + . += {" + [custom_edit_name] +
    + << + [dir2text(dir)] + >> + + "} + var/turf/T = get_turf(src) + . += "
    [ADMIN_COORDJMP(T)]" + +/atom/vv_edit_var(var_name, var_value) + switch(var_name) + if(NAMEOF(src, light_range)) + if(light_system == STATIC_LIGHT) + set_light(l_range = var_value) + else + set_light_range(var_value) + . = TRUE + if(NAMEOF(src, light_power)) + if(light_system == STATIC_LIGHT) + set_light(l_power = var_value) + else + set_light_power(var_value) + . = TRUE + if(NAMEOF(src, light_color)) + if(light_system == STATIC_LIGHT) + set_light(l_color = var_value) + else + set_light_color(var_value) + . = TRUE + if(NAMEOF(src, light_on)) + set_light_on(var_value) + . = TRUE + if(NAMEOF(src, light_flags)) + set_light_flags(var_value) + . = TRUE + if(NAMEOF(src, opacity)) + set_opacity(var_value) + . = TRUE + + if(!isnull(.)) + datum_flags |= DF_VAR_EDITED + return + + . = ..() + +/atom/proc/atom_say(message) + if(!message) + return + var/list/speech_bubble_hearers = list() + for(var/mob/M in get_mobs_in_view(7, src)) + M.show_message("[src] [atom_say_verb], \"[message]\"", 2, null, 1) + if(M.client) + speech_bubble_hearers += M.client + + if(length(speech_bubble_hearers)) + var/image/I = generate_speech_bubble(src, "[bubble_icon][say_test(message)]", FLY_LAYER) + I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA + INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(flick_overlay), I, speech_bubble_hearers, 30) + +/atom/proc/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list()) + return + +/atom/Entered(atom/movable/AM, atom/old_loc) + . = ..() + GLOB.moved_event.raise_event(AM, old_loc, AM.loc) + SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, old_loc) + SEND_SIGNAL(AM, COMSIG_ATOM_ENTERING, src, old_loc) + +/atom/Exit(atom/movable/AM, atom/new_loc) + . = ..() + if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, new_loc) & COMPONENT_ATOM_BLOCK_EXIT) + return FALSE + +/atom/Exited(atom/movable/AM, atom/new_loc) + . = ..() + SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, new_loc) + +/atom/proc/get_visible_gender(mob/user, force) + return gender + +/atom/proc/interact(mob/user) + return + +// Purpose: Determines if the object can pass this atom. +// Called by: Movement. +// Inputs: The moving atom, target turf. +// Outputs: Boolean if can pass. +// Airflow and ZAS zones now uses CanZASPass() instead of this proc. +/atom/proc/CanPass(atom/movable/mover, turf/target) + return !density diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 40b4ea9d4ef..757283f00fa 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1,640 +1,640 @@ -/atom/movable - layer = OBJ_LAYER - appearance_flags = TILE_BOUND|PIXEL_SCALE|KEEP_TOGETHER|LONG_GLIDE - glide_size = 8 - var/last_move = null //The direction the atom last moved - var/anchored = FALSE - // var/elevation = 2 - not used anywhere - var/moving_diagonally - var/move_speed = 10 - var/l_move_time = 1 - var/throwing = 0 - var/thrower - var/turf/throw_source = null - var/throw_speed = 2 - var/throw_range = 7 - var/moved_recently = 0 - var/mob/pulledby = null - var/item_state = null // Used to specify the item state for the on-mob overlays. - var/icon_scale_x = 1 // Used to scale icons up or down horizonally in update_transform(). - var/icon_scale_y = 1 // Used to scale icons up or down vertically in update_transform(). - var/icon_rotation = 0 // Used to rotate icons in update_transform() - var/icon_expected_height = 32 - var/icon_expected_width = 32 - var/old_x = 0 - var/old_y = 0 - var/datum/riding/riding_datum = null - var/does_spin = TRUE // Does the atom spin when thrown (of course it does :P) - var/movement_type = NONE - - var/cloaked = FALSE //If we're cloaked or not - var/image/cloaked_selfimage //The image we use for our client to let them see where we are - -/atom/movable/Initialize(mapload) - . = ..() - switch(blocks_emissive) - if(EMISSIVE_BLOCK_GENERIC) - var/mutable_appearance/gen_emissive_blocker = mutable_appearance(icon, icon_state, plane = PLANE_EMISSIVE, alpha = src.alpha) - gen_emissive_blocker.color = GLOB.em_block_color - gen_emissive_blocker.dir = dir - gen_emissive_blocker.appearance_flags |= appearance_flags - add_overlay(list(gen_emissive_blocker), TRUE) - if(EMISSIVE_BLOCK_UNIQUE) - render_target = ref(src) - em_block = new(src, render_target) - add_overlay(list(em_block), TRUE) - if(opacity) - AddElement(/datum/element/light_blocking) - switch(light_system) - if(STATIC_LIGHT) - update_light() - if(MOVABLE_LIGHT) - AddComponent(/datum/component/overlay_lighting, starts_on = light_on) - if(MOVABLE_LIGHT_DIRECTIONAL) - AddComponent(/datum/component/overlay_lighting, is_directional = TRUE, starts_on = light_on) - -/atom/movable/Destroy() - . = ..() - for(var/atom/movable/AM in contents) - qdel(AM) - - if(opacity) - RemoveElement(/datum/element/light_blocking) - - moveToNullspace() - - vis_contents.Cut() - for(var/atom/movable/A as anything in vis_locs) - A.vis_contents -= src - - if(pulledby) - pulledby.stop_pulling() - - if(orbiting) - stop_orbit() - QDEL_NULL(riding_datum) //VOREStation Add - -/atom/movable/vv_edit_var(var_name, var_value) - if(var_name in GLOB.VVpixelmovement) //Pixel movement is not yet implemented, changing this will break everything irreversibly. - return FALSE - return ..() - -//////////////////////////////////////// -/atom/movable/Move(atom/newloc, direct = 0, movetime) - // Didn't pass enough info - if(!loc || !newloc) - return FALSE - - if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, newloc, direct, movetime) & COMPONENT_MOVABLE_BLOCK_PRE_MOVE) - return FALSE - - // Store this early before we might move, it's used several places - var/atom/oldloc = loc - - // If we're not moving to the same spot (why? does that even happen?) - if(loc != newloc) - if(!direct) - direct = get_dir(oldloc, newloc) - if (IS_CARDINAL(direct)) //Cardinal move - // Track our failure if any in this value - . = TRUE - - // Face the direction of movement - set_dir(direct) - - // Check to make sure we can leave - if(!loc.Exit(src, newloc)) - . = FALSE - - // Check to make sure we can enter, if we haven't already failed - if(. && !newloc.Enter(src, src.loc)) - . = FALSE - - // Check to make sure if we're multi-tile we can move, if we haven't already failed - if(. && !check_multi_tile_move_density_dir(direct, locs)) - . = FALSE - - // Definitely moving if you enter this, no failures so far - if(. && locs.len <= 1) // We're not a multi-tile object. - var/area/oldarea = get_area(oldloc) - var/area/newarea = get_area(newloc) - var/old_z = get_z(oldloc) - var/dest_z = get_z(newloc) - - // Do The Move - if(movetime) - glide_for(movetime) // First attempt, lets let the diag do it. - loc = newloc - . = TRUE - - // So objects can be informed of z-level changes - if (old_z != dest_z) - onTransitZ(old_z, dest_z) - - // We don't call parent so we are calling this for byond - oldloc.Exited(src, newloc) - if(oldarea != newarea) - oldarea.Exited(src, newloc) - - // Multi-tile objects can't reach here, otherwise you'd need to avoid uncrossing yourself - for(var/atom/movable/thing as anything in oldloc) - // We don't call parent so we are calling this for byond - thing.Uncrossed(src) - - // We don't call parent so we are calling this for byond - newloc.Entered(src, oldloc) - if(oldarea != newarea) - newarea.Entered(src, oldloc) - - // Multi-tile objects can't reach here, otherwise you'd need to avoid uncrossing yourself - for(var/atom/movable/thing as anything in loc) - // We don't call parent so we are calling this for byond - thing.Crossed(src, oldloc) - - // We're a multi-tile object (multiple locs) - else if(. && newloc) - . = doMove(newloc) - - //Diagonal move, split it into cardinal moves - else - moving_diagonally = FIRST_DIAG_STEP - var/first_step_dir - // The `&& moving_diagonally` checks are so that a forceMove taking - // place due to a Crossed, Bumped, etc. call will interrupt - // the second half of the diagonal movement, or the second attempt - // at a first half if step() fails because we hit something. - glide_for(movetime * 2) - if (direct & NORTH) - if (direct & EAST) - if (step(src, NORTH) && moving_diagonally) - first_step_dir = NORTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, EAST) - else if (moving_diagonally && step(src, EAST)) - first_step_dir = EAST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, NORTH) - else if (direct & WEST) - if (step(src, NORTH) && moving_diagonally) - first_step_dir = NORTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, WEST) - else if (moving_diagonally && step(src, WEST)) - first_step_dir = WEST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, NORTH) - else if (direct & SOUTH) - if (direct & EAST) - if (step(src, SOUTH) && moving_diagonally) - first_step_dir = SOUTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, EAST) - else if (moving_diagonally && step(src, EAST)) - first_step_dir = EAST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, SOUTH) - else if (direct & WEST) - if (step(src, SOUTH) && moving_diagonally) - first_step_dir = SOUTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, WEST) - else if (moving_diagonally && step(src, WEST)) - first_step_dir = WEST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, SOUTH) - // If we failed, turn to face the direction of the first step at least - if(!. && moving_diagonally == SECOND_DIAG_STEP) - set_dir(first_step_dir) - // Done, regardless! - moving_diagonally = 0 - // We return because step above will call Move() and we don't want to do shenanigans back in here again - return - - else if(!loc || (loc == oldloc)) - last_move = 0 - return - - // If we moved, call Moved() on ourselves - if(.) - Moved(oldloc, direct, FALSE, movetime ? movetime : ( (TICKS2DS(WORLD_ICON_SIZE/glide_size)) * (moving_diagonally ? (0.5) : 1) ) ) - - // Update timers/cooldown stuff - move_speed = world.time - l_move_time - l_move_time = world.time - last_move = direct // The direction you last moved - // set_dir(direct) //Don't think this is necessary - -//Called after a successful Move(). By this point, we've already moved -/atom/movable/proc/Moved(atom/old_loc, direction, forced = FALSE, movetime) - SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, direction, forced, movetime) - // Handle any buckled mobs on this movable - if(has_buckled_mobs()) - handle_buckled_mob_movement(old_loc, direction, movetime) - if(riding_datum) - riding_datum.handle_vehicle_layer() - riding_datum.handle_vehicle_offsets() - for (var/datum/light_source/light as anything in light_sources) // Cycle through the light sources on this atom and tell them to update. - light.source_atom.update_light() - - SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, direction) - - return TRUE - -/atom/movable/set_dir(newdir) - . = ..(newdir) - if(riding_datum) - riding_datum.handle_vehicle_offsets() - -/atom/movable/relaymove(mob/user, direction) - . = ..() - if(riding_datum) - riding_datum.handle_ride(user, direction) - -// Make sure you know what you're doing if you call this, this is intended to only be called by byond directly. -// You probably want CanPass() -/atom/movable/Cross(atom/movable/AM) - return CanPass(AM, loc) - -/atom/movable/CanPass(atom/movable/mover, turf/target) - . = ..() - if(locs && locs.len >= 2) // If something is standing on top of us, let them pass. - if(mover.loc in locs) - . = TRUE - return . - -/atom/movable/Bump(atom/A) - if(!A) - CRASH("Bump was called with no argument.") - . = ..() - if(throwing) - throw_impact(A) - throwing = 0 - if(QDELETED(A)) - return - - SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A) - - A.Bumped(src) - A.last_bumped = world.time - -/atom/movable/proc/forceMove(atom/destination, direction, movetime) - . = FALSE - if(destination) - . = doMove(destination, direction, movetime) - else - CRASH("No valid destination passed into forceMove") - -/atom/movable/proc/moveToNullspace() - return doMove(null) - -/atom/movable/proc/doMove(atom/destination, direction, movetime) - var/atom/oldloc = loc - var/area/old_area = get_area(oldloc) - var/same_loc = oldloc == destination - - if(destination) - var/area/destarea = get_area(destination) - - // Do The Move - glide_for(movetime) - last_move = isnull(direction) ? 0 : direction - loc = destination - - // Unset this in case it was set in some other proc. We're no longer moving diagonally for sure. - moving_diagonally = 0 - - // We are moving to a different loc - if(!same_loc) - // Not moving out of nullspace - if(oldloc) - oldloc.Exited(src, destination) - // If it's not the same area, Exited() it - if(old_area && old_area != destarea) - old_area.Exited(src, destination) - - // Uncross everything where we left - for(var/atom/movable/AM as anything in oldloc) - if(AM == src) - continue - AM.Uncrossed(src) - if(loc != destination) // Uncrossed() triggered a separate movement - return - - // Information about turf and z-levels for source and dest collected - var/turf/oldturf = get_turf(oldloc) - var/turf/destturf = get_turf(destination) - var/old_z = (oldturf ? oldturf.z : null) - var/dest_z = (destturf ? destturf.z : null) - - // So objects can be informed of z-level changes - if (old_z != dest_z) - onTransitZ(old_z, dest_z) - - // Destination atom Entered - destination.Entered(src, oldloc) - - // Entered() the new area if it's not the same area - if(destarea && old_area != destarea) - destarea.Entered(src, oldloc) - - // We ignore ourselves because if we're multi-tile we might be in both old and new locs - for(var/atom/movable/AM as anything in destination) - if(AM == src) - continue - AM.Crossed(src, oldloc) - if(loc != destination) // Crossed triggered a separate movement - return - - // Call our thingy to inform everyone we moved - Moved(oldloc, NONE, TRUE) - - // Break pulling if we are too far to pull now. - if(pulledby && (pulledby.z != src.z || get_dist(pulledby, src) > 1)) - pulledby.stop_pulling() - - // We moved - return TRUE - - //If no destination, move the atom into nullspace (don't do this unless you know what you're doing) - else if(oldloc) - loc = null - - // Uncross everything where we left (no multitile safety like above because we are definitely not still there) - for(var/atom/movable/AM as anything in oldloc) - AM.Uncrossed(src) - - // Exited() our loc and area - oldloc.Exited(src, null) - if(old_area) - old_area.Exited(src, null) - - // We moved - return TRUE - -/atom/movable/proc/onTransitZ(old_z,new_z) - GLOB.z_moved_event.raise_event(src, old_z, new_z) - SEND_SIGNAL(src, COMSIG_MOVABLE_Z_CHANGED, old_z, new_z) - for(var/atom/movable/AM as anything in src) // Notify contents of Z-transition. This can be overridden IF we know the items contents do not care. - AM.onTransitZ(old_z,new_z) - -/atom/movable/proc/glide_for(movetime) - if(movetime) - glide_size = WORLD_ICON_SIZE/max(DS2TICKS(movetime), 1) - spawn(movetime) - glide_size = initial(glide_size) - else - glide_size = initial(glide_size) - -///////////////////////////////////////////////////////////////// - -//called when src is thrown into hit_atom -/atom/movable/proc/throw_impact(atom/hit_atom, var/speed) - if(istype(hit_atom,/mob/living)) - var/mob/living/M = hit_atom - if(M.buckled == src) - return // Don't hit the thing we're buckled to. - M.hitby(src,speed) - - else if(isobj(hit_atom)) - var/obj/O = hit_atom - if(!O.anchored) - step(O, src.last_move) - O.hitby(src,speed) - - else if(isturf(hit_atom)) - src.throwing = 0 - var/turf/T = hit_atom - T.hitby(src,speed) - -//decided whether a movable atom being thrown can pass through the turf it is in. -/atom/movable/proc/hit_check(var/speed) - if(src.throwing) - for(var/atom/A in get_turf(src)) - if(A == src) continue - if(istype(A,/mob/living)) - if(A:lying) continue - src.throw_impact(A,speed) - if(isobj(A)) - if(!A.density || A.throwpass) - continue - // Special handling of windows, which are dense but block only from some directions - if(istype(A, /obj/structure/window)) - var/obj/structure/window/W = A - if (!W.is_fulltile() && !(turn(src.last_move, 180) & A.dir)) - continue - // Same thing for (closed) windoors, which have the same problem - else if(istype(A, /obj/machinery/door/window) && !(turn(src.last_move, 180) & A.dir)) - continue - src.throw_impact(A,speed) - -/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, datum/callback/callback) //If this returns FALSE then callback will not be called. - . = TRUE - if (!target || speed <= 0 || QDELETED(src) || (target.z != src.z)) - return FALSE - - if (pulledby) - pulledby.stop_pulling() - - var/datum/thrownthing/TT = new(src, target, range, speed, thrower, callback) - throwing = TT - - pixel_z = 0 - if(spin && does_spin) - SpinAnimation(4,1) - - SSthrowing.processing[src] = TT - if (SSthrowing.state == SS_PAUSED && length(SSthrowing.currentrun)) - SSthrowing.currentrun[src] = TT - -//Overlays -/atom/movable/overlay - var/atom/master = null - anchored = TRUE - -/atom/movable/overlay/New() - for(var/x in src.verbs) - src.verbs -= x - ..() - -/atom/movable/overlay/attackby(a, b) - if (src.master) - return src.master.attackby(a, b) - return - -/atom/movable/overlay/attack_hand(a, b, c) - if (src.master) - return src.master.attack_hand(a, b, c) - return - -/atom/movable/proc/touch_map_edge() - if(z in using_map.sealed_levels) - return - - if(using_map.use_overmap) - overmap_spacetravel(get_turf(src), src) - return - - var/move_to_z = src.get_transit_zlevel() - if(move_to_z) - var/new_z = move_to_z - var/new_x - var/new_y - - if(x <= TRANSITIONEDGE) - new_x = world.maxx - TRANSITIONEDGE - 2 - new_y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) - - else if (x >= (world.maxx - TRANSITIONEDGE + 1)) - new_x = TRANSITIONEDGE + 1 - new_y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) - - else if (y <= TRANSITIONEDGE) - new_y = world.maxy - TRANSITIONEDGE -2 - new_x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) - - else if (y >= (world.maxy - TRANSITIONEDGE + 1)) - new_y = TRANSITIONEDGE + 1 - new_x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) - - if(ticker && istype(ticker.mode, /datum/game_mode/nuclear)) //only really care if the game mode is nuclear - var/datum/game_mode/nuclear/G = ticker.mode - G.check_nuke_disks() - - var/turf/T = locate(new_x, new_y, new_z) - if(istype(T)) - forceMove(T) - -//by default, transition randomly to another zlevel -/atom/movable/proc/get_transit_zlevel() - var/list/candidates = using_map.accessible_z_levels.Copy() - candidates.Remove("[src.z]") - - if(!candidates.len) - return null - return text2num(pickweight(candidates)) - -// Returns the current scaling of the sprite. -// Note this DOES NOT measure the height or width of the icon, but returns what number is being multiplied with to scale the icons, if any. -/atom/movable/proc/get_icon_scale_x() - return icon_scale_x - -/atom/movable/proc/get_icon_scale_y() - return icon_scale_y - -/atom/movable/proc/update_transform() - var/matrix/M = matrix() - M.Scale(icon_scale_x, icon_scale_y) - M.Turn(icon_rotation) - src.transform = M - -// Use this to set the object's scale. -/atom/movable/proc/adjust_scale(new_scale_x, new_scale_y) - if(isnull(new_scale_y)) - new_scale_y = new_scale_x - if(new_scale_x != 0) - icon_scale_x = new_scale_x - if(new_scale_y != 0) - icon_scale_y = new_scale_y - update_transform() - -/atom/movable/proc/adjust_rotation(new_rotation) - icon_rotation = new_rotation - update_transform() - -// Called when touching a lava tile. -/atom/movable/proc/lava_act() - fire_act(null, 10000, 1000) - - -// Procs to cloak/uncloak -/atom/movable/proc/cloak() - if(cloaked) - return FALSE - cloaked = TRUE - . = TRUE // We did work - - var/static/animation_time = 1 SECOND - cloaked_selfimage = get_cloaked_selfimage() - - //Wheeee - cloak_animation(animation_time) - - //Needs to be last so people can actually see the effect before we become invisible - if(cloaked) // Ensure we are still cloaked after the animation delay - plane = CLOAKED_PLANE - -/atom/movable/proc/uncloak() - if(!cloaked) - return FALSE - cloaked = FALSE - . = TRUE // We did work - - var/static/animation_time = 1 SECOND - QDEL_NULL(cloaked_selfimage) - - //Needs to be first so people can actually see the effect, so become uninvisible first - plane = initial(plane) - - //Oooooo - uncloak_animation(animation_time) - - -// Animations for cloaking/uncloaking -/atom/movable/proc/cloak_animation(var/length = 1 SECOND) - //Save these - var/initial_alpha = alpha - - //Animate alpha fade - animate(src, alpha = 0, time = length) - - //Animate a cloaking effect - var/our_filter = filters.len+1 //Filters don't appear to have a type that can be stored in a var and accessed. This is how the DM reference does it. - filters += filter(type="wave", x = 0, y = 16, size = 0, offset = 0, flags = WAVE_SIDEWAYS) - animate(filters[our_filter], offset = 1, size = 8, time = length, flags = ANIMATION_PARALLEL) - - //Wait for animations to finish - sleep(length+5) - - //Remove those - filters -= filter(type="wave", x = 0, y = 16, size = 8, offset = 1, flags = WAVE_SIDEWAYS) - - //Back to original alpha - alpha = initial_alpha - -/atom/movable/proc/uncloak_animation(var/length = 1 SECOND) - //Save these - var/initial_alpha = alpha - - //Put us back to normal, but no alpha - alpha = 0 - - //Animate alpha fade up - animate(src, alpha = initial_alpha, time = length) - - //Animate a cloaking effect - var/our_filter = filters.len+1 //Filters don't appear to have a type that can be stored in a var and accessed. This is how the DM reference does it. - filters += filter(type="wave", x=0, y = 16, size = 8, offset = 1, flags = WAVE_SIDEWAYS) - animate(filters[our_filter], offset = 0, size = 0, time = length, flags = ANIMATION_PARALLEL) - - //Wait for animations to finish - sleep(length+5) - - //Remove those - filters -= filter(type="wave", x=0, y = 16, size = 0, offset = 0, flags = WAVE_SIDEWAYS) - - -// So cloaked things can see themselves, if necessary -/atom/movable/proc/get_cloaked_selfimage() - var/icon/selficon = icon(icon, icon_state) - selficon.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) //White - var/image/selfimage = image(selficon) - selfimage.color = "#0000FF" - selfimage.alpha = 100 - selfimage.layer = initial(layer) - selfimage.plane = initial(plane) - selfimage.loc = src - - return selfimage - -/atom/movable/proc/get_cell() +/atom/movable + layer = OBJ_LAYER + appearance_flags = TILE_BOUND|PIXEL_SCALE|KEEP_TOGETHER|LONG_GLIDE + glide_size = 8 + var/last_move = null //The direction the atom last moved + var/anchored = FALSE + // var/elevation = 2 - not used anywhere + var/moving_diagonally + var/move_speed = 10 + var/l_move_time = 1 + var/throwing = 0 + var/thrower + var/turf/throw_source = null + var/throw_speed = 2 + var/throw_range = 7 + var/moved_recently = 0 + var/mob/pulledby = null + var/item_state = null // Used to specify the item state for the on-mob overlays. + var/icon_scale_x = 1 // Used to scale icons up or down horizonally in update_transform(). + var/icon_scale_y = 1 // Used to scale icons up or down vertically in update_transform(). + var/icon_rotation = 0 // Used to rotate icons in update_transform() + var/icon_expected_height = 32 + var/icon_expected_width = 32 + var/old_x = 0 + var/old_y = 0 + var/datum/riding/riding_datum = null + var/does_spin = TRUE // Does the atom spin when thrown (of course it does :P) + var/movement_type = NONE + + var/cloaked = FALSE //If we're cloaked or not + var/image/cloaked_selfimage //The image we use for our client to let them see where we are + +/atom/movable/Initialize(mapload) + . = ..() + switch(blocks_emissive) + if(EMISSIVE_BLOCK_GENERIC) + var/mutable_appearance/gen_emissive_blocker = mutable_appearance(icon, icon_state, plane = PLANE_EMISSIVE, alpha = src.alpha) + gen_emissive_blocker.color = GLOB.em_block_color + gen_emissive_blocker.dir = dir + gen_emissive_blocker.appearance_flags |= appearance_flags + add_overlay(list(gen_emissive_blocker), TRUE) + if(EMISSIVE_BLOCK_UNIQUE) + render_target = ref(src) + em_block = new(src, render_target) + add_overlay(list(em_block), TRUE) + if(opacity) + AddElement(/datum/element/light_blocking) + switch(light_system) + if(STATIC_LIGHT) + update_light() + if(MOVABLE_LIGHT) + AddComponent(/datum/component/overlay_lighting, starts_on = light_on) + if(MOVABLE_LIGHT_DIRECTIONAL) + AddComponent(/datum/component/overlay_lighting, is_directional = TRUE, starts_on = light_on) + +/atom/movable/Destroy() + . = ..() + for(var/atom/movable/AM in contents) + qdel(AM) + + if(opacity) + RemoveElement(/datum/element/light_blocking) + + moveToNullspace() + + vis_contents.Cut() + for(var/atom/movable/A as anything in vis_locs) + A.vis_contents -= src + + if(pulledby) + pulledby.stop_pulling() + + if(orbiting) + stop_orbit() + QDEL_NULL(riding_datum) //VOREStation Add + +/atom/movable/vv_edit_var(var_name, var_value) + if(var_name in GLOB.VVpixelmovement) //Pixel movement is not yet implemented, changing this will break everything irreversibly. + return FALSE + return ..() + +//////////////////////////////////////// +/atom/movable/Move(atom/newloc, direct = 0, movetime) + // Didn't pass enough info + if(!loc || !newloc) + return FALSE + + if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, newloc, direct, movetime) & COMPONENT_MOVABLE_BLOCK_PRE_MOVE) + return FALSE + + // Store this early before we might move, it's used several places + var/atom/oldloc = loc + + // If we're not moving to the same spot (why? does that even happen?) + if(loc != newloc) + if(!direct) + direct = get_dir(oldloc, newloc) + if (IS_CARDINAL(direct)) //Cardinal move + // Track our failure if any in this value + . = TRUE + + // Face the direction of movement + set_dir(direct) + + // Check to make sure we can leave + if(!loc.Exit(src, newloc)) + . = FALSE + + // Check to make sure we can enter, if we haven't already failed + if(. && !newloc.Enter(src, src.loc)) + . = FALSE + + // Check to make sure if we're multi-tile we can move, if we haven't already failed + if(. && !check_multi_tile_move_density_dir(direct, locs)) + . = FALSE + + // Definitely moving if you enter this, no failures so far + if(. && locs.len <= 1) // We're not a multi-tile object. + var/area/oldarea = get_area(oldloc) + var/area/newarea = get_area(newloc) + var/old_z = get_z(oldloc) + var/dest_z = get_z(newloc) + + // Do The Move + if(movetime) + glide_for(movetime) // First attempt, lets let the diag do it. + loc = newloc + . = TRUE + + // So objects can be informed of z-level changes + if (old_z != dest_z) + onTransitZ(old_z, dest_z) + + // We don't call parent so we are calling this for byond + oldloc.Exited(src, newloc) + if(oldarea != newarea) + oldarea.Exited(src, newloc) + + // Multi-tile objects can't reach here, otherwise you'd need to avoid uncrossing yourself + for(var/atom/movable/thing as anything in oldloc) + // We don't call parent so we are calling this for byond + thing.Uncrossed(src) + + // We don't call parent so we are calling this for byond + newloc.Entered(src, oldloc) + if(oldarea != newarea) + newarea.Entered(src, oldloc) + + // Multi-tile objects can't reach here, otherwise you'd need to avoid uncrossing yourself + for(var/atom/movable/thing as anything in loc) + // We don't call parent so we are calling this for byond + thing.Crossed(src, oldloc) + + // We're a multi-tile object (multiple locs) + else if(. && newloc) + . = doMove(newloc) + + //Diagonal move, split it into cardinal moves + else + moving_diagonally = FIRST_DIAG_STEP + var/first_step_dir + // The `&& moving_diagonally` checks are so that a forceMove taking + // place due to a Crossed, Bumped, etc. call will interrupt + // the second half of the diagonal movement, or the second attempt + // at a first half if step() fails because we hit something. + glide_for(movetime * 2) + if (direct & NORTH) + if (direct & EAST) + if (step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, EAST) + else if (moving_diagonally && step(src, EAST)) + first_step_dir = EAST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, NORTH) + else if (direct & WEST) + if (step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, WEST) + else if (moving_diagonally && step(src, WEST)) + first_step_dir = WEST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, NORTH) + else if (direct & SOUTH) + if (direct & EAST) + if (step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, EAST) + else if (moving_diagonally && step(src, EAST)) + first_step_dir = EAST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, SOUTH) + else if (direct & WEST) + if (step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, WEST) + else if (moving_diagonally && step(src, WEST)) + first_step_dir = WEST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, SOUTH) + // If we failed, turn to face the direction of the first step at least + if(!. && moving_diagonally == SECOND_DIAG_STEP) + set_dir(first_step_dir) + // Done, regardless! + moving_diagonally = 0 + // We return because step above will call Move() and we don't want to do shenanigans back in here again + return + + else if(!loc || (loc == oldloc)) + last_move = 0 + return + + // If we moved, call Moved() on ourselves + if(.) + Moved(oldloc, direct, FALSE, movetime ? movetime : ( (TICKS2DS(WORLD_ICON_SIZE/glide_size)) * (moving_diagonally ? (0.5) : 1) ) ) + + // Update timers/cooldown stuff + move_speed = world.time - l_move_time + l_move_time = world.time + last_move = direct // The direction you last moved + // set_dir(direct) //Don't think this is necessary + +//Called after a successful Move(). By this point, we've already moved +/atom/movable/proc/Moved(atom/old_loc, direction, forced = FALSE, movetime) + SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, direction, forced, movetime) + // Handle any buckled mobs on this movable + if(has_buckled_mobs()) + handle_buckled_mob_movement(old_loc, direction, movetime) + if(riding_datum) + riding_datum.handle_vehicle_layer() + riding_datum.handle_vehicle_offsets() + for (var/datum/light_source/light as anything in light_sources) // Cycle through the light sources on this atom and tell them to update. + light.source_atom.update_light() + + SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, direction) + + return TRUE + +/atom/movable/set_dir(newdir) + . = ..(newdir) + if(riding_datum) + riding_datum.handle_vehicle_offsets() + +/atom/movable/relaymove(mob/user, direction) + . = ..() + if(riding_datum) + riding_datum.handle_ride(user, direction) + +// Make sure you know what you're doing if you call this, this is intended to only be called by byond directly. +// You probably want CanPass() +/atom/movable/Cross(atom/movable/AM) + return CanPass(AM, loc) + +/atom/movable/CanPass(atom/movable/mover, turf/target) + . = ..() + if(locs && locs.len >= 2) // If something is standing on top of us, let them pass. + if(mover.loc in locs) + . = TRUE + return . + +/atom/movable/Bump(atom/A) + if(!A) + CRASH("Bump was called with no argument.") + . = ..() + if(throwing) + throw_impact(A) + throwing = 0 + if(QDELETED(A)) + return + + SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A) + + A.Bumped(src) + A.last_bumped = world.time + +/atom/movable/proc/forceMove(atom/destination, direction, movetime) + . = FALSE + if(destination) + . = doMove(destination, direction, movetime) + else + CRASH("No valid destination passed into forceMove") + +/atom/movable/proc/moveToNullspace() + return doMove(null) + +/atom/movable/proc/doMove(atom/destination, direction, movetime) + var/atom/oldloc = loc + var/area/old_area = get_area(oldloc) + var/same_loc = oldloc == destination + + if(destination) + var/area/destarea = get_area(destination) + + // Do The Move + glide_for(movetime) + last_move = isnull(direction) ? 0 : direction + loc = destination + + // Unset this in case it was set in some other proc. We're no longer moving diagonally for sure. + moving_diagonally = 0 + + // We are moving to a different loc + if(!same_loc) + // Not moving out of nullspace + if(oldloc) + oldloc.Exited(src, destination) + // If it's not the same area, Exited() it + if(old_area && old_area != destarea) + old_area.Exited(src, destination) + + // Uncross everything where we left + for(var/atom/movable/AM as anything in oldloc) + if(AM == src) + continue + AM.Uncrossed(src) + if(loc != destination) // Uncrossed() triggered a separate movement + return + + // Information about turf and z-levels for source and dest collected + var/turf/oldturf = get_turf(oldloc) + var/turf/destturf = get_turf(destination) + var/old_z = (oldturf ? oldturf.z : null) + var/dest_z = (destturf ? destturf.z : null) + + // So objects can be informed of z-level changes + if (old_z != dest_z) + onTransitZ(old_z, dest_z) + + // Destination atom Entered + destination.Entered(src, oldloc) + + // Entered() the new area if it's not the same area + if(destarea && old_area != destarea) + destarea.Entered(src, oldloc) + + // We ignore ourselves because if we're multi-tile we might be in both old and new locs + for(var/atom/movable/AM as anything in destination) + if(AM == src) + continue + AM.Crossed(src, oldloc) + if(loc != destination) // Crossed triggered a separate movement + return + + // Call our thingy to inform everyone we moved + Moved(oldloc, NONE, TRUE) + + // Break pulling if we are too far to pull now. + if(pulledby && (pulledby.z != src.z || get_dist(pulledby, src) > 1)) + pulledby.stop_pulling() + + // We moved + return TRUE + + //If no destination, move the atom into nullspace (don't do this unless you know what you're doing) + else if(oldloc) + loc = null + + // Uncross everything where we left (no multitile safety like above because we are definitely not still there) + for(var/atom/movable/AM as anything in oldloc) + AM.Uncrossed(src) + + // Exited() our loc and area + oldloc.Exited(src, null) + if(old_area) + old_area.Exited(src, null) + + // We moved + return TRUE + +/atom/movable/proc/onTransitZ(old_z,new_z) + GLOB.z_moved_event.raise_event(src, old_z, new_z) + SEND_SIGNAL(src, COMSIG_MOVABLE_Z_CHANGED, old_z, new_z) + for(var/atom/movable/AM as anything in src) // Notify contents of Z-transition. This can be overridden IF we know the items contents do not care. + AM.onTransitZ(old_z,new_z) + +/atom/movable/proc/glide_for(movetime) + if(movetime) + glide_size = WORLD_ICON_SIZE/max(DS2TICKS(movetime), 1) + spawn(movetime) + glide_size = initial(glide_size) + else + glide_size = initial(glide_size) + +///////////////////////////////////////////////////////////////// + +//called when src is thrown into hit_atom +/atom/movable/proc/throw_impact(atom/hit_atom, var/speed) + if(istype(hit_atom,/mob/living)) + var/mob/living/M = hit_atom + if(M.buckled == src) + return // Don't hit the thing we're buckled to. + M.hitby(src,speed) + + else if(isobj(hit_atom)) + var/obj/O = hit_atom + if(!O.anchored) + step(O, src.last_move) + O.hitby(src,speed) + + else if(isturf(hit_atom)) + src.throwing = 0 + var/turf/T = hit_atom + T.hitby(src,speed) + +//decided whether a movable atom being thrown can pass through the turf it is in. +/atom/movable/proc/hit_check(var/speed) + if(src.throwing) + for(var/atom/A in get_turf(src)) + if(A == src) continue + if(istype(A,/mob/living)) + if(A:lying) continue + src.throw_impact(A,speed) + if(isobj(A)) + if(!A.density || A.throwpass) + continue + // Special handling of windows, which are dense but block only from some directions + if(istype(A, /obj/structure/window)) + var/obj/structure/window/W = A + if (!W.is_fulltile() && !(turn(src.last_move, 180) & A.dir)) + continue + // Same thing for (closed) windoors, which have the same problem + else if(istype(A, /obj/machinery/door/window) && !(turn(src.last_move, 180) & A.dir)) + continue + src.throw_impact(A,speed) + +/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, datum/callback/callback) //If this returns FALSE then callback will not be called. + . = TRUE + if (!target || speed <= 0 || QDELETED(src) || (target.z != src.z)) + return FALSE + + if (pulledby) + pulledby.stop_pulling() + + var/datum/thrownthing/TT = new(src, target, range, speed, thrower, callback) + throwing = TT + + pixel_z = 0 + if(spin && does_spin) + SpinAnimation(4,1) + + SSthrowing.processing[src] = TT + if (SSthrowing.state == SS_PAUSED && length(SSthrowing.currentrun)) + SSthrowing.currentrun[src] = TT + +//Overlays +/atom/movable/overlay + var/atom/master = null + anchored = TRUE + +/atom/movable/overlay/New() + for(var/x in src.verbs) + src.verbs -= x + ..() + +/atom/movable/overlay/attackby(a, b) + if (src.master) + return src.master.attackby(a, b) + return + +/atom/movable/overlay/attack_hand(a, b, c) + if (src.master) + return src.master.attack_hand(a, b, c) + return + +/atom/movable/proc/touch_map_edge() + if(z in using_map.sealed_levels) + return + + if(using_map.use_overmap) + overmap_spacetravel(get_turf(src), src) + return + + var/move_to_z = src.get_transit_zlevel() + if(move_to_z) + var/new_z = move_to_z + var/new_x + var/new_y + + if(x <= TRANSITIONEDGE) + new_x = world.maxx - TRANSITIONEDGE - 2 + new_y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) + + else if (x >= (world.maxx - TRANSITIONEDGE + 1)) + new_x = TRANSITIONEDGE + 1 + new_y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) + + else if (y <= TRANSITIONEDGE) + new_y = world.maxy - TRANSITIONEDGE -2 + new_x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) + + else if (y >= (world.maxy - TRANSITIONEDGE + 1)) + new_y = TRANSITIONEDGE + 1 + new_x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) + + if(ticker && istype(ticker.mode, /datum/game_mode/nuclear)) //only really care if the game mode is nuclear + var/datum/game_mode/nuclear/G = ticker.mode + G.check_nuke_disks() + + var/turf/T = locate(new_x, new_y, new_z) + if(istype(T)) + forceMove(T) + +//by default, transition randomly to another zlevel +/atom/movable/proc/get_transit_zlevel() + var/list/candidates = using_map.accessible_z_levels.Copy() + candidates.Remove("[src.z]") + + if(!candidates.len) + return null + return text2num(pickweight(candidates)) + +// Returns the current scaling of the sprite. +// Note this DOES NOT measure the height or width of the icon, but returns what number is being multiplied with to scale the icons, if any. +/atom/movable/proc/get_icon_scale_x() + return icon_scale_x + +/atom/movable/proc/get_icon_scale_y() + return icon_scale_y + +/atom/movable/proc/update_transform() + var/matrix/M = matrix() + M.Scale(icon_scale_x, icon_scale_y) + M.Turn(icon_rotation) + src.transform = M + +// Use this to set the object's scale. +/atom/movable/proc/adjust_scale(new_scale_x, new_scale_y) + if(isnull(new_scale_y)) + new_scale_y = new_scale_x + if(new_scale_x != 0) + icon_scale_x = new_scale_x + if(new_scale_y != 0) + icon_scale_y = new_scale_y + update_transform() + +/atom/movable/proc/adjust_rotation(new_rotation) + icon_rotation = new_rotation + update_transform() + +// Called when touching a lava tile. +/atom/movable/proc/lava_act() + fire_act(null, 10000, 1000) + + +// Procs to cloak/uncloak +/atom/movable/proc/cloak() + if(cloaked) + return FALSE + cloaked = TRUE + . = TRUE // We did work + + var/static/animation_time = 1 SECOND + cloaked_selfimage = get_cloaked_selfimage() + + //Wheeee + cloak_animation(animation_time) + + //Needs to be last so people can actually see the effect before we become invisible + if(cloaked) // Ensure we are still cloaked after the animation delay + plane = CLOAKED_PLANE + +/atom/movable/proc/uncloak() + if(!cloaked) + return FALSE + cloaked = FALSE + . = TRUE // We did work + + var/static/animation_time = 1 SECOND + QDEL_NULL(cloaked_selfimage) + + //Needs to be first so people can actually see the effect, so become uninvisible first + plane = initial(plane) + + //Oooooo + uncloak_animation(animation_time) + + +// Animations for cloaking/uncloaking +/atom/movable/proc/cloak_animation(var/length = 1 SECOND) + //Save these + var/initial_alpha = alpha + + //Animate alpha fade + animate(src, alpha = 0, time = length) + + //Animate a cloaking effect + var/our_filter = filters.len+1 //Filters don't appear to have a type that can be stored in a var and accessed. This is how the DM reference does it. + filters += filter(type="wave", x = 0, y = 16, size = 0, offset = 0, flags = WAVE_SIDEWAYS) + animate(filters[our_filter], offset = 1, size = 8, time = length, flags = ANIMATION_PARALLEL) + + //Wait for animations to finish + sleep(length+5) + + //Remove those + filters -= filter(type="wave", x = 0, y = 16, size = 8, offset = 1, flags = WAVE_SIDEWAYS) + + //Back to original alpha + alpha = initial_alpha + +/atom/movable/proc/uncloak_animation(var/length = 1 SECOND) + //Save these + var/initial_alpha = alpha + + //Put us back to normal, but no alpha + alpha = 0 + + //Animate alpha fade up + animate(src, alpha = initial_alpha, time = length) + + //Animate a cloaking effect + var/our_filter = filters.len+1 //Filters don't appear to have a type that can be stored in a var and accessed. This is how the DM reference does it. + filters += filter(type="wave", x=0, y = 16, size = 8, offset = 1, flags = WAVE_SIDEWAYS) + animate(filters[our_filter], offset = 0, size = 0, time = length, flags = ANIMATION_PARALLEL) + + //Wait for animations to finish + sleep(length+5) + + //Remove those + filters -= filter(type="wave", x=0, y = 16, size = 0, offset = 0, flags = WAVE_SIDEWAYS) + + +// So cloaked things can see themselves, if necessary +/atom/movable/proc/get_cloaked_selfimage() + var/icon/selficon = icon(icon, icon_state) + selficon.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) //White + var/image/selfimage = image(selficon) + selfimage.color = "#0000FF" + selfimage.alpha = 100 + selfimage.layer = initial(layer) + selfimage.plane = initial(plane) + selfimage.loc = src + + return selfimage + +/atom/movable/proc/get_cell() return \ No newline at end of file diff --git a/code/game/dna/genes/monkey.dm b/code/game/dna/genes/monkey.dm index 0089355ed31..b9d4837184a 100644 --- a/code/game/dna/genes/monkey.dm +++ b/code/game/dna/genes/monkey.dm @@ -1,163 +1,163 @@ -/datum/dna/gene/monkey - name="Monkey" - -/datum/dna/gene/monkey/New() - block=MONKEYBLOCK - -/datum/dna/gene/monkey/can_activate(var/mob/M,var/flags) - return istype(M, /mob/living/carbon/human) || istype(M,/mob/living/carbon/monkey) - -/datum/dna/gene/monkey/activate(var/mob/living/M, var/connected, var/flags) - if(!istype(M,/mob/living/carbon/human)) - //testing("Cannot monkey-ify [M], type is [M.type].") - return - var/mob/living/carbon/human/H = M - H.transforming = 1 - var/list/implants = list() //Try to preserve implants. - for(var/obj/item/weapon/implant/W in H) - implants += W - W.loc = null - - if(!connected) - for(var/obj/item/W in (H.contents-implants)) - if (W==H.w_uniform) // will be teared - continue - H.drop_from_inventory(W) - M.transforming = 1 - M.canmove = 0 - M.icon = null - M.invisibility = 101 - var/atom/movable/overlay/animation = new( M.loc ) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = src - flick("h2monkey", animation) - sleep(48) - qdel(animation) - - - var/mob/living/carbon/monkey/O = null - if(H.species.primitive) - O = new H.species.primitive(src) - else - H.gib() //Trying to change the species of a creature with no primitive var set is messy. - return - - if(M) - if (M.dna) - O.dna = M.dna.Clone() - M.dna = null - - if (M.suiciding) - O.suiciding = M.suiciding - M.suiciding = null - - for(var/obj/T in (M.contents-implants)) - qdel(T) - - O.loc = M.loc - - if(M.mind) - M.mind.transfer_to(O) //transfer our mind to the cute little monkey - - if (connected) //inside dna thing - var/obj/machinery/dna_scannernew/C = connected - O.loc = C - C.occupant = O - connected = null - O.real_name = text("monkey ([])",copytext(md5(M.real_name), 2, 6)) - O.take_overall_damage(M.getBruteLoss() + 40, M.getFireLoss()) - O.adjustToxLoss(M.getToxLoss() + 20) - O.adjustOxyLoss(M.getOxyLoss()) - O.set_stat(M.stat) - O.a_intent = I_HURT - for (var/obj/item/weapon/implant/I in implants) - I.loc = O - I.implanted = O -// O.update_icon = 1 //queue a full icon update at next life() call - qdel(M) - return - -/datum/dna/gene/monkey/deactivate(var/mob/living/M, var/connected, var/flags) - if(!istype(M,/mob/living/carbon/monkey)) - //testing("Cannot humanize [M], type is [M.type].") - return - var/mob/living/carbon/monkey/Mo = M - Mo.transforming = 1 - var/list/implants = list() //Still preserving implants - for(var/obj/item/weapon/implant/W in Mo) - implants += W - W.loc = null - if(!connected) - for(var/obj/item/W in (Mo.contents-implants)) - Mo.drop_from_inventory(W) - M.transforming = 1 - M.canmove = 0 - M.icon = null - M.invisibility = 101 - var/atom/movable/overlay/animation = new( M.loc ) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = src - flick("monkey2h", animation) - sleep(48) - qdel(animation) - - var/mob/living/carbon/human/O - if(Mo.greaterform) - O = new(src, Mo.greaterform) - else - O = new(src) - - if (M.dna.GetUIState(DNA_UI_GENDER)) - O.gender = FEMALE - else - O.gender = MALE - - if (M) - if (M.dna) - O.dna = M.dna.Clone() - M.dna = null - - if (M.suiciding) - O.suiciding = M.suiciding - M.suiciding = null - - //for(var/obj/T in M) - // qdel(T) - - O.loc = M.loc - - if(M.mind) - M.mind.transfer_to(O) //transfer our mind to the human - - if (connected) //inside dna thing - var/obj/machinery/dna_scannernew/C = connected - O.loc = C - C.occupant = O - connected = null - - var/i - while (!i) - var/randomname - if (O.gender == MALE) - randomname = capitalize(pick(first_names_male) + " " + capitalize(pick(last_names))) - else - randomname = capitalize(pick(first_names_female) + " " + capitalize(pick(last_names))) - if (findname(randomname)) - continue - else - O.real_name = randomname - O.dna.real_name = randomname - i++ - O.UpdateAppearance() - O.take_overall_damage(M.getBruteLoss(), M.getFireLoss()) - O.adjustToxLoss(M.getToxLoss()) - O.adjustOxyLoss(M.getOxyLoss()) - O.set_stat(M.stat) - for (var/obj/item/weapon/implant/I in implants) - I.loc = O - I.implanted = O -// O.update_icon = 1 //queue a full icon update at next life() call - qdel(M) - return +/datum/dna/gene/monkey + name="Monkey" + +/datum/dna/gene/monkey/New() + block=MONKEYBLOCK + +/datum/dna/gene/monkey/can_activate(var/mob/M,var/flags) + return istype(M, /mob/living/carbon/human) || istype(M,/mob/living/carbon/monkey) + +/datum/dna/gene/monkey/activate(var/mob/living/M, var/connected, var/flags) + if(!istype(M,/mob/living/carbon/human)) + //testing("Cannot monkey-ify [M], type is [M.type].") + return + var/mob/living/carbon/human/H = M + H.transforming = 1 + var/list/implants = list() //Try to preserve implants. + for(var/obj/item/weapon/implant/W in H) + implants += W + W.loc = null + + if(!connected) + for(var/obj/item/W in (H.contents-implants)) + if (W==H.w_uniform) // will be teared + continue + H.drop_from_inventory(W) + M.transforming = 1 + M.canmove = 0 + M.icon = null + M.invisibility = 101 + var/atom/movable/overlay/animation = new( M.loc ) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = src + flick("h2monkey", animation) + sleep(48) + qdel(animation) + + + var/mob/living/carbon/monkey/O = null + if(H.species.primitive) + O = new H.species.primitive(src) + else + H.gib() //Trying to change the species of a creature with no primitive var set is messy. + return + + if(M) + if (M.dna) + O.dna = M.dna.Clone() + M.dna = null + + if (M.suiciding) + O.suiciding = M.suiciding + M.suiciding = null + + for(var/obj/T in (M.contents-implants)) + qdel(T) + + O.loc = M.loc + + if(M.mind) + M.mind.transfer_to(O) //transfer our mind to the cute little monkey + + if (connected) //inside dna thing + var/obj/machinery/dna_scannernew/C = connected + O.loc = C + C.occupant = O + connected = null + O.real_name = text("monkey ([])",copytext(md5(M.real_name), 2, 6)) + O.take_overall_damage(M.getBruteLoss() + 40, M.getFireLoss()) + O.adjustToxLoss(M.getToxLoss() + 20) + O.adjustOxyLoss(M.getOxyLoss()) + O.set_stat(M.stat) + O.a_intent = I_HURT + for (var/obj/item/weapon/implant/I in implants) + I.loc = O + I.implanted = O +// O.update_icon = 1 //queue a full icon update at next life() call + qdel(M) + return + +/datum/dna/gene/monkey/deactivate(var/mob/living/M, var/connected, var/flags) + if(!istype(M,/mob/living/carbon/monkey)) + //testing("Cannot humanize [M], type is [M.type].") + return + var/mob/living/carbon/monkey/Mo = M + Mo.transforming = 1 + var/list/implants = list() //Still preserving implants + for(var/obj/item/weapon/implant/W in Mo) + implants += W + W.loc = null + if(!connected) + for(var/obj/item/W in (Mo.contents-implants)) + Mo.drop_from_inventory(W) + M.transforming = 1 + M.canmove = 0 + M.icon = null + M.invisibility = 101 + var/atom/movable/overlay/animation = new( M.loc ) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = src + flick("monkey2h", animation) + sleep(48) + qdel(animation) + + var/mob/living/carbon/human/O + if(Mo.greaterform) + O = new(src, Mo.greaterform) + else + O = new(src) + + if (M.dna.GetUIState(DNA_UI_GENDER)) + O.gender = FEMALE + else + O.gender = MALE + + if (M) + if (M.dna) + O.dna = M.dna.Clone() + M.dna = null + + if (M.suiciding) + O.suiciding = M.suiciding + M.suiciding = null + + //for(var/obj/T in M) + // qdel(T) + + O.loc = M.loc + + if(M.mind) + M.mind.transfer_to(O) //transfer our mind to the human + + if (connected) //inside dna thing + var/obj/machinery/dna_scannernew/C = connected + O.loc = C + C.occupant = O + connected = null + + var/i + while (!i) + var/randomname + if (O.gender == MALE) + randomname = capitalize(pick(first_names_male) + " " + capitalize(pick(last_names))) + else + randomname = capitalize(pick(first_names_female) + " " + capitalize(pick(last_names))) + if (findname(randomname)) + continue + else + O.real_name = randomname + O.dna.real_name = randomname + i++ + O.UpdateAppearance() + O.take_overall_damage(M.getBruteLoss(), M.getFireLoss()) + O.adjustToxLoss(M.getToxLoss()) + O.adjustOxyLoss(M.getOxyLoss()) + O.set_stat(M.stat) + for (var/obj/item/weapon/implant/I in implants) + I.loc = O + I.implanted = O +// O.update_icon = 1 //queue a full icon update at next life() call + qdel(M) + return diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index 13b8db962dd..ceb4234f2d6 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -1,19 +1,19 @@ -/datum/game_mode/changeling - name = "Changeling" - round_description = "There are alien changelings on the station. Do not let the changelings succeed!" - extended_round_description = "Life always finds a way. However, life can sometimes take a more disturbing route. \ - Humanity's extensive knowledge of xeno-biological specimens has made them confident and arrogant. Yet \ - something slipped past their eyes. Something dangerous. Something alive. Most frightening of all, \ - however, is that this something is someone. An unknown alien specimen has incorporated itself into \ - the crew of the station. Its unique biology allows it to manipulate its own or anyone else's DNA. \ - With the ability to copy faces, voices, animals, but also change the chemical make up of your own body, \ - its existence is a threat to not only your personal safety but the lives of everyone on board. \ - No one knows where it came from. No one knows who it is or what it wants. One thing is for \ - certain though... there is never just one of them. Good luck." - config_tag = "changeling" - required_players = 2 - required_players_secret = 3 - required_enemies = 1 - end_on_antag_death = 0 - antag_scaling_coeff = 10 - antag_tags = list(MODE_CHANGELING) +/datum/game_mode/changeling + name = "Changeling" + round_description = "There are alien changelings on the station. Do not let the changelings succeed!" + extended_round_description = "Life always finds a way. However, life can sometimes take a more disturbing route. \ + Humanity's extensive knowledge of xeno-biological specimens has made them confident and arrogant. Yet \ + something slipped past their eyes. Something dangerous. Something alive. Most frightening of all, \ + however, is that this something is someone. An unknown alien specimen has incorporated itself into \ + the crew of the station. Its unique biology allows it to manipulate its own or anyone else's DNA. \ + With the ability to copy faces, voices, animals, but also change the chemical make up of your own body, \ + its existence is a threat to not only your personal safety but the lives of everyone on board. \ + No one knows where it came from. No one knows who it is or what it wants. One thing is for \ + certain though... there is never just one of them. Good luck." + config_tag = "changeling" + required_players = 2 + required_players_secret = 3 + required_enemies = 1 + end_on_antag_death = 0 + antag_scaling_coeff = 10 + antag_tags = list(MODE_CHANGELING) diff --git a/code/game/gamemodes/changeling/changeling_powers.dm b/code/game/gamemodes/changeling/changeling_powers.dm index 431b4e06f32..4c92e7c95a6 100644 --- a/code/game/gamemodes/changeling/changeling_powers.dm +++ b/code/game/gamemodes/changeling/changeling_powers.dm @@ -1,244 +1,244 @@ -var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega") - -/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind) - var/list/datum/absorbed_dna/absorbed_dna = list() - var/list/absorbed_languages = list() // Necessary because of set_species stuff - var/absorbedcount = 0 - var/lingabsorbedcount = 1 //Starts at one, because that's us - var/chem_charges = 20 - var/chem_recharge_rate = 0.5 - var/chem_storage = 50 - var/sting_range = 1 - var/changelingID = "Changeling" - var/geneticdamage = 0 - var/isabsorbing = 0 - var/geneticpoints = 7 - var/max_geneticpoints = 7 - var/readapts = 1 - var/max_readapts = 2 - var/list/purchased_powers = list() - var/mimicing = "" - var/cloaked = 0 - var/armor_deployed = 0 //This is only used for changeling_generic_equip_all_slots() at the moment. - var/recursive_enhancement = 0 //Used to power up other abilities from the ling power with the same name. - var/list/purchased_powers_history = list() //Used for round-end report, includes respec uses too. - var/last_shriek = null // world.time when the ling last used a shriek. - var/next_escape = 0 // world.time when the ling can next use Escape Restraints - var/thermal_sight = FALSE // Is our Vision Augmented? With thermals? - -/datum/changeling/New(var/gender=FEMALE) - ..() - if(possible_changeling_IDs.len) - changelingID = pick(possible_changeling_IDs) - possible_changeling_IDs -= changelingID - changelingID = "[changelingID]" - else - changelingID = "[rand(1,999)]" - -/datum/changeling/proc/regenerate() - chem_charges = min(max(0, chem_charges+chem_recharge_rate), chem_storage) - geneticdamage = max(0, geneticdamage-1) - -/datum/changeling/proc/GetDNA(var/dna_owner) - for(var/datum/absorbed_dna/DNA in absorbed_dna) - if(dna_owner == DNA.name) - return DNA - -/mob/proc/absorbDNA(var/datum/absorbed_dna/newDNA) - var/datum/changeling/changeling = null - if(src.mind && src.mind.changeling) - changeling = src.mind.changeling - if(!changeling) - return - - for(var/language in newDNA.languages) - changeling.absorbed_languages |= language - - changeling_update_languages(changeling.absorbed_languages) - - if(!changeling.GetDNA(newDNA.name)) // Don't duplicate - I wonder if it's possible for it to still be a different DNA? DNA code could use a rewrite - changeling.absorbed_dna += newDNA - -//Restores our verbs. It will only restore verbs allowed during lesser (monkey) form if we are not human -/mob/proc/make_changeling() - - if(!mind) return - if(!mind.changeling) mind.changeling = new /datum/changeling(gender) - - verbs.Add(/datum/changeling/proc/EvolutionMenu) - verbs.Add(/mob/proc/changeling_respec) - add_language("Changeling") - - var/lesser_form = !ishuman(src) - - if(!powerinstances.len) - for(var/P in powers) - powerinstances += new P() - - // Code to auto-purchase free powers. - for(var/datum/power/changeling/P in powerinstances) - if(!P.genomecost) // Is it free? - if(!(P in mind.changeling.purchased_powers)) // Do we not have it already? - mind.changeling.purchasePower(mind, P.name, 0)// Purchase it. Don't remake our verbs, we're doing it after this. - - for(var/datum/power/changeling/P in mind.changeling.purchased_powers) - if(P.isVerb) - if(lesser_form && !P.allowduringlesserform) continue - if(!(P in src.verbs)) - verbs.Add(P.verbpath) - if(P.make_hud_button) - if(!src.ability_master) - src.ability_master = new /obj/screen/movable/ability_master(src) - src.ability_master.add_ling_ability( - object_given = src, - verb_given = P.verbpath, - name_given = P.name, - ability_icon_given = P.ability_icon_state, - arguments = list() - ) - - for(var/language in languages) - mind.changeling.absorbed_languages |= language - - var/mob/living/carbon/human/H = src - if(istype(H)) - var/saved_dna = H.dna.Clone() /// Prevent transform from breaking. - var/datum/absorbed_dna/newDNA = new(H.real_name, saved_dna, H.species.name, H.languages, H.identifying_gender, H.flavor_texts, H.modifiers) - absorbDNA(newDNA) - - return 1 - -//removes our changeling verbs -/mob/proc/remove_changeling_powers() - if(!mind || !mind.changeling) return - for(var/datum/power/changeling/P in mind.changeling.purchased_powers) - if(P.isVerb) - verbs.Remove(P.verbpath) - var/obj/screen/ability/verb_based/changeling/C = ability_master.get_ability_by_proc_ref(P.verbpath) - if(C) - ability_master.remove_ability(C) - - -//Helper proc. Does all the checks and stuff for us to avoid copypasta -/mob/proc/changeling_power(var/required_chems=0, var/required_dna=0, var/max_genetic_damage=100, var/max_stat=0) - - if(!src.mind) return - if(!iscarbon(src)) return - - var/datum/changeling/changeling = src.mind.changeling - if(!changeling) - to_world_log("[src] has the changeling_transform() verb but is not a changeling.") - return - - if(src.stat > max_stat) - to_chat(src, "We are incapacitated.") - return - - if(changeling.absorbed_dna.len < required_dna) - to_chat(src, "We require at least [required_dna] samples of compatible DNA.") - return - - if(changeling.chem_charges < required_chems) - to_chat(src, "We require at least [required_chems] units of chemicals to do that!") - return - - if(changeling.geneticdamage > max_genetic_damage) - to_chat(src, "Our genomes are still reassembling. We need time to recover first.") - return - - return changeling - -//Used to dump the languages from the changeling datum into the actual mob. -/mob/proc/changeling_update_languages(var/updated_languages) - languages = list() - for(var/language in updated_languages) - languages += language - - //This isn't strictly necessary but just to be safe... - add_language("Changeling") - - ////////// - //STINGS// //They get a pretty header because there's just so fucking many of them ;_; - ////////// - -/turf/proc/AdjacentTurfsRangedSting() - //Yes this is snowflakey, but I couldn't get it to work any other way.. -Luke - var/list/allowed = list( - /obj/structure/table, - /obj/structure/closet, - /obj/structure/frame, - /obj/structure/target_stake, - /obj/structure/cable, - /obj/structure/disposalpipe, - /obj/machinery, - /mob - ) - - var/L[] = new() - for(var/turf/simulated/t in oview(src,1)) - var/add = 1 - if(t.density) - add = 0 - if(add && LinkBlocked(src,t)) - add = 0 - if(add && TurfBlockedNonWindow(t)) - add = 0 - for(var/obj/O in t) - if(O.density) - add = 0 - break - if(istype(O, /obj/machinery/door)) - //not sure why this doesn't fire on LinkBlocked() - add = 0 - break - for(var/type in allowed) - if (istype(O, type)) - add = 1 - break - if(!add) - break - if(add) - L.Add(t) - return L - - -/mob/proc/sting_can_reach(mob/M as mob, sting_range = 1) - if(M.loc == src.loc) - return 1 //target and source are in the same thing - if(!isturf(src.loc) || !isturf(M.loc)) - to_chat(src, "We cannot reach \the [M] with a sting!") - return 0 //One is inside, the other is outside something. - // Maximum queued turfs set to 25; I don't *think* anything raises sting_range above 2, but if it does the 25 may need raising - if(!AStar(src.loc, M.loc, /turf/proc/AdjacentTurfsRangedSting, /turf/proc/Distance, max_nodes=25, max_node_depth=sting_range)) //If we can't find a path, fail - to_chat(src, "We cannot find a path to sting \the [M] by!") - return 0 - return 1 - -//Handles the general sting code to reduce on copypasta (seeming as somebody decided to make SO MANY dumb abilities) -/mob/proc/changeling_sting(var/required_chems=0, var/verb_path) - var/datum/changeling/changeling = changeling_power(required_chems) - if(!changeling) return - - var/list/victims = list() - for(var/mob/living/carbon/C in oview(changeling.sting_range)) - victims += C - var/mob/living/carbon/T = tgui_input_list(src, "Who will we sting?", "Sting!", victims) - - if(!T) - return - if(T.isSynthetic()) - to_chat(src, "We are unable to pierce the outer shell of [T].") - return - if(!(T in view(changeling.sting_range))) return - if(!sting_can_reach(T, changeling.sting_range)) return - if(!changeling_power(required_chems)) return - - changeling.chem_charges -= required_chems - changeling.sting_range = 1 - src.verbs -= verb_path - spawn(10) src.verbs += verb_path - - to_chat(src, "We stealthily sting [T].") - if(!T.mind || !T.mind.changeling) return T //T will be affected by the sting - to_chat(T, "You feel a tiny prick.") - return +var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega") + +/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind) + var/list/datum/absorbed_dna/absorbed_dna = list() + var/list/absorbed_languages = list() // Necessary because of set_species stuff + var/absorbedcount = 0 + var/lingabsorbedcount = 1 //Starts at one, because that's us + var/chem_charges = 20 + var/chem_recharge_rate = 0.5 + var/chem_storage = 50 + var/sting_range = 1 + var/changelingID = "Changeling" + var/geneticdamage = 0 + var/isabsorbing = 0 + var/geneticpoints = 7 + var/max_geneticpoints = 7 + var/readapts = 1 + var/max_readapts = 2 + var/list/purchased_powers = list() + var/mimicing = "" + var/cloaked = 0 + var/armor_deployed = 0 //This is only used for changeling_generic_equip_all_slots() at the moment. + var/recursive_enhancement = 0 //Used to power up other abilities from the ling power with the same name. + var/list/purchased_powers_history = list() //Used for round-end report, includes respec uses too. + var/last_shriek = null // world.time when the ling last used a shriek. + var/next_escape = 0 // world.time when the ling can next use Escape Restraints + var/thermal_sight = FALSE // Is our Vision Augmented? With thermals? + +/datum/changeling/New(var/gender=FEMALE) + ..() + if(possible_changeling_IDs.len) + changelingID = pick(possible_changeling_IDs) + possible_changeling_IDs -= changelingID + changelingID = "[changelingID]" + else + changelingID = "[rand(1,999)]" + +/datum/changeling/proc/regenerate() + chem_charges = min(max(0, chem_charges+chem_recharge_rate), chem_storage) + geneticdamage = max(0, geneticdamage-1) + +/datum/changeling/proc/GetDNA(var/dna_owner) + for(var/datum/absorbed_dna/DNA in absorbed_dna) + if(dna_owner == DNA.name) + return DNA + +/mob/proc/absorbDNA(var/datum/absorbed_dna/newDNA) + var/datum/changeling/changeling = null + if(src.mind && src.mind.changeling) + changeling = src.mind.changeling + if(!changeling) + return + + for(var/language in newDNA.languages) + changeling.absorbed_languages |= language + + changeling_update_languages(changeling.absorbed_languages) + + if(!changeling.GetDNA(newDNA.name)) // Don't duplicate - I wonder if it's possible for it to still be a different DNA? DNA code could use a rewrite + changeling.absorbed_dna += newDNA + +//Restores our verbs. It will only restore verbs allowed during lesser (monkey) form if we are not human +/mob/proc/make_changeling() + + if(!mind) return + if(!mind.changeling) mind.changeling = new /datum/changeling(gender) + + verbs.Add(/datum/changeling/proc/EvolutionMenu) + verbs.Add(/mob/proc/changeling_respec) + add_language("Changeling") + + var/lesser_form = !ishuman(src) + + if(!powerinstances.len) + for(var/P in powers) + powerinstances += new P() + + // Code to auto-purchase free powers. + for(var/datum/power/changeling/P in powerinstances) + if(!P.genomecost) // Is it free? + if(!(P in mind.changeling.purchased_powers)) // Do we not have it already? + mind.changeling.purchasePower(mind, P.name, 0)// Purchase it. Don't remake our verbs, we're doing it after this. + + for(var/datum/power/changeling/P in mind.changeling.purchased_powers) + if(P.isVerb) + if(lesser_form && !P.allowduringlesserform) continue + if(!(P in src.verbs)) + verbs.Add(P.verbpath) + if(P.make_hud_button) + if(!src.ability_master) + src.ability_master = new /obj/screen/movable/ability_master(src) + src.ability_master.add_ling_ability( + object_given = src, + verb_given = P.verbpath, + name_given = P.name, + ability_icon_given = P.ability_icon_state, + arguments = list() + ) + + for(var/language in languages) + mind.changeling.absorbed_languages |= language + + var/mob/living/carbon/human/H = src + if(istype(H)) + var/saved_dna = H.dna.Clone() /// Prevent transform from breaking. + var/datum/absorbed_dna/newDNA = new(H.real_name, saved_dna, H.species.name, H.languages, H.identifying_gender, H.flavor_texts, H.modifiers) + absorbDNA(newDNA) + + return 1 + +//removes our changeling verbs +/mob/proc/remove_changeling_powers() + if(!mind || !mind.changeling) return + for(var/datum/power/changeling/P in mind.changeling.purchased_powers) + if(P.isVerb) + verbs.Remove(P.verbpath) + var/obj/screen/ability/verb_based/changeling/C = ability_master.get_ability_by_proc_ref(P.verbpath) + if(C) + ability_master.remove_ability(C) + + +//Helper proc. Does all the checks and stuff for us to avoid copypasta +/mob/proc/changeling_power(var/required_chems=0, var/required_dna=0, var/max_genetic_damage=100, var/max_stat=0) + + if(!src.mind) return + if(!iscarbon(src)) return + + var/datum/changeling/changeling = src.mind.changeling + if(!changeling) + to_world_log("[src] has the changeling_transform() verb but is not a changeling.") + return + + if(src.stat > max_stat) + to_chat(src, "We are incapacitated.") + return + + if(changeling.absorbed_dna.len < required_dna) + to_chat(src, "We require at least [required_dna] samples of compatible DNA.") + return + + if(changeling.chem_charges < required_chems) + to_chat(src, "We require at least [required_chems] units of chemicals to do that!") + return + + if(changeling.geneticdamage > max_genetic_damage) + to_chat(src, "Our genomes are still reassembling. We need time to recover first.") + return + + return changeling + +//Used to dump the languages from the changeling datum into the actual mob. +/mob/proc/changeling_update_languages(var/updated_languages) + languages = list() + for(var/language in updated_languages) + languages += language + + //This isn't strictly necessary but just to be safe... + add_language("Changeling") + + ////////// + //STINGS// //They get a pretty header because there's just so fucking many of them ;_; + ////////// + +/turf/proc/AdjacentTurfsRangedSting() + //Yes this is snowflakey, but I couldn't get it to work any other way.. -Luke + var/list/allowed = list( + /obj/structure/table, + /obj/structure/closet, + /obj/structure/frame, + /obj/structure/target_stake, + /obj/structure/cable, + /obj/structure/disposalpipe, + /obj/machinery, + /mob + ) + + var/L[] = new() + for(var/turf/simulated/t in oview(src,1)) + var/add = 1 + if(t.density) + add = 0 + if(add && LinkBlocked(src,t)) + add = 0 + if(add && TurfBlockedNonWindow(t)) + add = 0 + for(var/obj/O in t) + if(O.density) + add = 0 + break + if(istype(O, /obj/machinery/door)) + //not sure why this doesn't fire on LinkBlocked() + add = 0 + break + for(var/type in allowed) + if (istype(O, type)) + add = 1 + break + if(!add) + break + if(add) + L.Add(t) + return L + + +/mob/proc/sting_can_reach(mob/M as mob, sting_range = 1) + if(M.loc == src.loc) + return 1 //target and source are in the same thing + if(!isturf(src.loc) || !isturf(M.loc)) + to_chat(src, "We cannot reach \the [M] with a sting!") + return 0 //One is inside, the other is outside something. + // Maximum queued turfs set to 25; I don't *think* anything raises sting_range above 2, but if it does the 25 may need raising + if(!AStar(src.loc, M.loc, /turf/proc/AdjacentTurfsRangedSting, /turf/proc/Distance, max_nodes=25, max_node_depth=sting_range)) //If we can't find a path, fail + to_chat(src, "We cannot find a path to sting \the [M] by!") + return 0 + return 1 + +//Handles the general sting code to reduce on copypasta (seeming as somebody decided to make SO MANY dumb abilities) +/mob/proc/changeling_sting(var/required_chems=0, var/verb_path) + var/datum/changeling/changeling = changeling_power(required_chems) + if(!changeling) return + + var/list/victims = list() + for(var/mob/living/carbon/C in oview(changeling.sting_range)) + victims += C + var/mob/living/carbon/T = tgui_input_list(src, "Who will we sting?", "Sting!", victims) + + if(!T) + return + if(T.isSynthetic()) + to_chat(src, "We are unable to pierce the outer shell of [T].") + return + if(!(T in view(changeling.sting_range))) return + if(!sting_can_reach(T, changeling.sting_range)) return + if(!changeling_power(required_chems)) return + + changeling.chem_charges -= required_chems + changeling.sting_range = 1 + src.verbs -= verb_path + spawn(10) src.verbs += verb_path + + to_chat(src, "We stealthily sting [T].") + if(!T.mind || !T.mind.changeling) return T //T will be affected by the sting + to_chat(T, "You feel a tiny prick.") + return diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index df162f74ca2..454a2efbddd 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -1,10 +1,10 @@ -/datum/game_mode/cult - name = "Cult" - round_description = "Some crewmembers are attempting to start a cult!" - extended_round_description = "The station has been infiltrated by a fanatical group of death-cultists! They will use powers from beyond your comprehension to subvert you to their cause and ultimately please their gods through sacrificial summons and physical immolation! Try to survive!" - config_tag = "cult" - required_players = 5 - required_players_secret = 5 - required_enemies = 3 - end_on_antag_death = 0 - antag_tags = list(MODE_CULTIST) +/datum/game_mode/cult + name = "Cult" + round_description = "Some crewmembers are attempting to start a cult!" + extended_round_description = "The station has been infiltrated by a fanatical group of death-cultists! They will use powers from beyond your comprehension to subvert you to their cause and ultimately please their gods through sacrificial summons and physical immolation! Try to survive!" + config_tag = "cult" + required_players = 5 + required_players_secret = 5 + required_enemies = 3 + end_on_antag_death = 0 + antag_tags = list(MODE_CULTIST) diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index 3622998eef6..e54bb88eefe 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -1,125 +1,125 @@ -/obj/item/weapon/melee/cultblade - name = "cult blade" - desc = "An arcane weapon wielded by the followers of Nar-Sie." - icon_state = "cultblade" - origin_tech = list(TECH_COMBAT = 1, TECH_ARCANE = 1) - w_class = ITEMSIZE_LARGE - force = 30 - throwforce = 10 - hitsound = 'sound/weapons/bladeslice.ogg' - drop_sound = 'sound/items/drop/sword.ogg' - pickup_sound = 'sound/items/pickup/sword.ogg' - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - edge = TRUE - sharp = TRUE - -/obj/item/weapon/melee/cultblade/cultify() - return - -/obj/item/weapon/melee/cultblade/attack(mob/living/M, mob/living/user, var/target_zone) - if(iscultist(user) && !istype(user, /mob/living/simple_mob/construct)) - return ..() - - var/zone = (user.hand ? "l_arm":"r_arm") - if(ishuman(user)) - var/mob/living/carbon/human/H = user - var/obj/item/organ/external/affecting = H.get_organ(zone) - to_chat(user, "An inexplicable force rips through your [affecting.name], tearing the sword from your grasp!") - //random amount of damage between half of the blade's force and the full force of the blade. - user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp = TRUE, edge = TRUE) - user.Weaken(5) - else if(!istype(user, /mob/living/simple_mob/construct)) - to_chat(user, "An inexplicable force rips through you, tearing the sword from your grasp!") - else - to_chat(user, "The blade hisses, forcing itself from your manipulators. \The [src] will only allow mortals to wield it against foes, not kin.") - - user.drop_from_inventory(src, src.loc) - throw_at(get_edge_target_turf(src, pick(alldirs)), rand(1,3), throw_speed) - - var/spooky = pick('sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg', 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/wail.ogg') - playsound(src, spooky, 50, 1) - - return 1 - -/obj/item/weapon/melee/cultblade/pickup(mob/living/user as mob) - if(!iscultist(user) && !istype(user, /mob/living/simple_mob/construct)) - to_chat(user, "An overwhelming feeling of dread comes over you as you pick up the cultist's sword. It would be wise to be rid of this blade quickly.") - user.make_dizzy(120) - if(istype(user, /mob/living/simple_mob/construct)) - to_chat(user, "\The [src] hisses, as it is discontent with your acquisition of it. It would be wise to return it to a worthy mortal quickly.") - -/obj/item/clothing/head/culthood - name = "cult hood" - icon_state = "culthood" - desc = "A hood worn by the followers of Nar-Sie." - origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) - flags_inv = HIDEFACE - body_parts_covered = HEAD - armor = list(melee = 50, bullet = 30, laser = 50, energy = 80, bomb = 25, bio = 10, rad = 0) - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0 - -/obj/item/clothing/head/culthood/cultify() - return - -/obj/item/clothing/head/culthood/magus - name = "magus helm" - icon_state = "magus" - desc = "A helm worn by the followers of Nar-Sie." - flags_inv = HIDEFACE | BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/culthood/alt - icon_state = "cult_hoodalt" - -/obj/item/clothing/suit/cultrobes - name = "cult robes" - desc = "A set of armored robes worn by the followers of Nar-Sie." - icon_state = "cultrobes" - origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/weapon/book/tome,/obj/item/weapon/melee/cultblade) - armor = list(melee = 50, bullet = 30, laser = 50, energy = 80, bomb = 25, bio = 10, rad = 0) - flags_inv = HIDEJUMPSUIT - siemens_coefficient = 0 - -/obj/item/clothing/suit/cultrobes/cultify() - return - -/obj/item/clothing/suit/cultrobes/alt - icon_state = "cultrobesalt" - -/obj/item/clothing/suit/cultrobes/magusred - name = "magus robes" - desc = "A set of armored robes worn by the followers of Nar-Sie." - icon_state = "magusred" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - -/obj/item/clothing/head/helmet/space/cult - name = "cult helmet" - desc = "A space worthy helmet used by the followers of Nar-Sie." - icon_state = "cult_helmet" - origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) - armor = list(melee = 60, bullet = 50, laser = 30, energy = 80, bomb = 30, bio = 30, rad = 30) - siemens_coefficient = 0 - -/obj/item/clothing/head/helmet/space/cult/cultify() - return - -/obj/item/clothing/suit/space/cult - name = "cult armour" - icon_state = "cult_armour" - origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) - desc = "A bulky suit of armour, bristling with spikes. It looks space-worthy." - w_class = ITEMSIZE_NORMAL - allowed = list(/obj/item/weapon/book/tome,/obj/item/weapon/melee/cultblade,/obj/item/weapon/tank/emergency/oxygen,/obj/item/device/suit_cooling_unit) - slowdown = 0.5 - armor = list(melee = 60, bullet = 50, laser = 30, energy = 80, bomb = 30, bio = 30, rad = 30) - siemens_coefficient = 0 - flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS - -/obj/item/clothing/suit/space/cult/cultify() - return +/obj/item/weapon/melee/cultblade + name = "cult blade" + desc = "An arcane weapon wielded by the followers of Nar-Sie." + icon_state = "cultblade" + origin_tech = list(TECH_COMBAT = 1, TECH_ARCANE = 1) + w_class = ITEMSIZE_LARGE + force = 30 + throwforce = 10 + hitsound = 'sound/weapons/bladeslice.ogg' + drop_sound = 'sound/items/drop/sword.ogg' + pickup_sound = 'sound/items/pickup/sword.ogg' + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + edge = TRUE + sharp = TRUE + +/obj/item/weapon/melee/cultblade/cultify() + return + +/obj/item/weapon/melee/cultblade/attack(mob/living/M, mob/living/user, var/target_zone) + if(iscultist(user) && !istype(user, /mob/living/simple_mob/construct)) + return ..() + + var/zone = (user.hand ? "l_arm":"r_arm") + if(ishuman(user)) + var/mob/living/carbon/human/H = user + var/obj/item/organ/external/affecting = H.get_organ(zone) + to_chat(user, "An inexplicable force rips through your [affecting.name], tearing the sword from your grasp!") + //random amount of damage between half of the blade's force and the full force of the blade. + user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp = TRUE, edge = TRUE) + user.Weaken(5) + else if(!istype(user, /mob/living/simple_mob/construct)) + to_chat(user, "An inexplicable force rips through you, tearing the sword from your grasp!") + else + to_chat(user, "The blade hisses, forcing itself from your manipulators. \The [src] will only allow mortals to wield it against foes, not kin.") + + user.drop_from_inventory(src, src.loc) + throw_at(get_edge_target_turf(src, pick(alldirs)), rand(1,3), throw_speed) + + var/spooky = pick('sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg', 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/wail.ogg') + playsound(src, spooky, 50, 1) + + return 1 + +/obj/item/weapon/melee/cultblade/pickup(mob/living/user as mob) + if(!iscultist(user) && !istype(user, /mob/living/simple_mob/construct)) + to_chat(user, "An overwhelming feeling of dread comes over you as you pick up the cultist's sword. It would be wise to be rid of this blade quickly.") + user.make_dizzy(120) + if(istype(user, /mob/living/simple_mob/construct)) + to_chat(user, "\The [src] hisses, as it is discontent with your acquisition of it. It would be wise to return it to a worthy mortal quickly.") + +/obj/item/clothing/head/culthood + name = "cult hood" + icon_state = "culthood" + desc = "A hood worn by the followers of Nar-Sie." + origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) + flags_inv = HIDEFACE + body_parts_covered = HEAD + armor = list(melee = 50, bullet = 30, laser = 50, energy = 80, bomb = 25, bio = 10, rad = 0) + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0 + +/obj/item/clothing/head/culthood/cultify() + return + +/obj/item/clothing/head/culthood/magus + name = "magus helm" + icon_state = "magus" + desc = "A helm worn by the followers of Nar-Sie." + flags_inv = HIDEFACE | BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/culthood/alt + icon_state = "cult_hoodalt" + +/obj/item/clothing/suit/cultrobes + name = "cult robes" + desc = "A set of armored robes worn by the followers of Nar-Sie." + icon_state = "cultrobes" + origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/weapon/book/tome,/obj/item/weapon/melee/cultblade) + armor = list(melee = 50, bullet = 30, laser = 50, energy = 80, bomb = 25, bio = 10, rad = 0) + flags_inv = HIDEJUMPSUIT + siemens_coefficient = 0 + +/obj/item/clothing/suit/cultrobes/cultify() + return + +/obj/item/clothing/suit/cultrobes/alt + icon_state = "cultrobesalt" + +/obj/item/clothing/suit/cultrobes/magusred + name = "magus robes" + desc = "A set of armored robes worn by the followers of Nar-Sie." + icon_state = "magusred" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + +/obj/item/clothing/head/helmet/space/cult + name = "cult helmet" + desc = "A space worthy helmet used by the followers of Nar-Sie." + icon_state = "cult_helmet" + origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) + armor = list(melee = 60, bullet = 50, laser = 30, energy = 80, bomb = 30, bio = 30, rad = 30) + siemens_coefficient = 0 + +/obj/item/clothing/head/helmet/space/cult/cultify() + return + +/obj/item/clothing/suit/space/cult + name = "cult armour" + icon_state = "cult_armour" + origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) + desc = "A bulky suit of armour, bristling with spikes. It looks space-worthy." + w_class = ITEMSIZE_NORMAL + allowed = list(/obj/item/weapon/book/tome,/obj/item/weapon/melee/cultblade,/obj/item/weapon/tank/emergency/oxygen,/obj/item/device/suit_cooling_unit) + slowdown = 0.5 + armor = list(melee = 60, bullet = 50, laser = 30, energy = 80, bomb = 30, bio = 30, rad = 30) + siemens_coefficient = 0 + flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS + +/obj/item/clothing/suit/space/cult/cultify() + return diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index c0d7cfd7a48..9a9540784e5 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -1,173 +1,173 @@ -/obj/structure/cult - density = TRUE - anchored = TRUE - icon = 'icons/obj/cult.dmi' - -/obj/structure/cult/cultify() - return - -/obj/structure/cult/talisman - name = "Altar" - desc = "A bloodstained altar dedicated to Nar-Sie." - icon_state = "talismanaltar" - - -/obj/structure/cult/forge - name = "Daemon forge" - desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie." - icon_state = "forge" - -/obj/structure/cult/pylon - name = "Pylon" - desc = "A floating crystal that hums with an unearthly energy." - icon_state = "pylon" - var/isbroken = 0 - light_range = 5 - light_color = "#3e0000" - var/obj/item/wepon = null - - var/shatter_message = "The pylon shatters!" - var/impact_sound = 'sound/effects/Glasshit.ogg' - var/shatter_sound = 'sound/effects/Glassbr3.ogg' - - var/activation_cooldown = 30 SECONDS - var/last_activation = 0 - -/obj/structure/cult/pylon/Initialize() - . = ..() - START_PROCESSING(SSobj, src) - -/obj/structure/cult/pylon/attack_hand(mob/M as mob) - attackpylon(M, 5) - -/obj/structure/cult/pylon/attack_generic(var/mob/user, var/damage) - attackpylon(user, damage) - -/obj/structure/cult/pylon/attackby(obj/item/W as obj, mob/user as mob) - attackpylon(user, W.force) - -/obj/structure/cult/pylon/take_damage(var/damage) - pylonhit(damage) - -/obj/structure/cult/pylon/bullet_act(var/obj/item/projectile/Proj) - pylonhit(Proj.get_structure_damage()) - -/obj/structure/cult/pylon/proc/pylonhit(var/damage) - if(!isbroken) - if(prob(1+ damage * 5)) - visible_message("[shatter_message]") - STOP_PROCESSING(SSobj, src) - playsound(src,shatter_sound, 75, 1) - isbroken = 1 - density = FALSE - icon_state = "[initial(icon_state)]-broken" - set_light(0) - -/obj/structure/cult/pylon/proc/attackpylon(mob/user as mob, var/damage) - if(!isbroken) - if(prob(1+ damage * 5)) - user.visible_message( - "[user] smashed \the [src]!", - "You hit \the [src], and its crystal breaks apart!", - "You hear a tinkle of crystal shards." - ) - STOP_PROCESSING(SSobj, src) - user.do_attack_animation(src) - playsound(src,shatter_sound, 75, 1) - isbroken = 1 - density = FALSE - icon_state = "[initial(icon_state)]-broken" - set_light(0) - else - to_chat(user, "You hit \the [src]!") - playsound(src,impact_sound, 75, 1) - else - if(prob(damage * 2)) - to_chat(user, "You pulverize what was left of \the [src]!") - qdel(src) - else - to_chat(user, "You hit \the [src]!") - playsound(src,impact_sound, 75, 1) - -/obj/structure/cult/pylon/proc/repair(mob/user as mob) - if(isbroken) - START_PROCESSING(SSobj, src) - to_chat(user, "You repair \the [src].") - isbroken = 0 - density = TRUE - icon_state = initial(icon_state) - set_light(5) - -// Returns 1 if the pylon does something special. -/obj/structure/cult/pylon/proc/pylon_unique() - last_activation = world.time - return 0 - -/obj/structure/cult/pylon/process() - if(!isbroken && (last_activation < world.time + activation_cooldown) && pylon_unique()) - flick("[initial(icon_state)]-surge",src) - -/obj/structure/cult/tome - name = "Desk" - desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl." - icon_state = "tomealtar" - -//sprites for this no longer exist -Pete -//(they were stolen from another game anyway) -/* -/obj/structure/cult/pillar - name = "Pillar" - desc = "This should not exist" - icon_state = "pillar" - icon = 'magic_pillar.dmi' -*/ - -/obj/effect/gateway - name = "gateway" - desc = "You're pretty sure that abyss is staring back." - icon = 'icons/obj/cult.dmi' - icon_state = "hole" - density = TRUE - unacidable = TRUE - anchored = TRUE - var/spawnable = null - -/obj/effect/gateway/active - light_range=5 - light_color="#ff0000" - spawnable=list( - /mob/living/simple_mob/animal/space/bats, - /mob/living/simple_mob/creature, - /mob/living/simple_mob/faithless - ) - -/obj/effect/gateway/active/cult - light_range=5 - light_color="#ff0000" - spawnable=list( - /mob/living/simple_mob/animal/space/bats/cult, - /mob/living/simple_mob/creature/cult, - /mob/living/simple_mob/faithless/cult - ) - -/obj/effect/gateway/active/cult/cultify() - return - -/obj/effect/gateway/active/Initialize() - addtimer(CALLBACK(src, PROC_REF(spawn_and_qdel)), rand(30, 60) SECONDS) - -/obj/effect/gateway/active/proc/spawn_and_qdel() - if(LAZYLEN(spawnable)) - var/t = pick(spawnable) - new t(get_turf(src)) - qdel(src) - -/obj/effect/gateway/active/Crossed(var/atom/A) - if(A.is_incorporeal()) - return - if(!istype(A, /mob/living)) - return - - var/mob/living/M = A - - to_chat(M, "Walking into \the [src] is probably a bad idea, you think.") +/obj/structure/cult + density = TRUE + anchored = TRUE + icon = 'icons/obj/cult.dmi' + +/obj/structure/cult/cultify() + return + +/obj/structure/cult/talisman + name = "Altar" + desc = "A bloodstained altar dedicated to Nar-Sie." + icon_state = "talismanaltar" + + +/obj/structure/cult/forge + name = "Daemon forge" + desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie." + icon_state = "forge" + +/obj/structure/cult/pylon + name = "Pylon" + desc = "A floating crystal that hums with an unearthly energy." + icon_state = "pylon" + var/isbroken = 0 + light_range = 5 + light_color = "#3e0000" + var/obj/item/wepon = null + + var/shatter_message = "The pylon shatters!" + var/impact_sound = 'sound/effects/Glasshit.ogg' + var/shatter_sound = 'sound/effects/Glassbr3.ogg' + + var/activation_cooldown = 30 SECONDS + var/last_activation = 0 + +/obj/structure/cult/pylon/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + +/obj/structure/cult/pylon/attack_hand(mob/M as mob) + attackpylon(M, 5) + +/obj/structure/cult/pylon/attack_generic(var/mob/user, var/damage) + attackpylon(user, damage) + +/obj/structure/cult/pylon/attackby(obj/item/W as obj, mob/user as mob) + attackpylon(user, W.force) + +/obj/structure/cult/pylon/take_damage(var/damage) + pylonhit(damage) + +/obj/structure/cult/pylon/bullet_act(var/obj/item/projectile/Proj) + pylonhit(Proj.get_structure_damage()) + +/obj/structure/cult/pylon/proc/pylonhit(var/damage) + if(!isbroken) + if(prob(1+ damage * 5)) + visible_message("[shatter_message]") + STOP_PROCESSING(SSobj, src) + playsound(src,shatter_sound, 75, 1) + isbroken = 1 + density = FALSE + icon_state = "[initial(icon_state)]-broken" + set_light(0) + +/obj/structure/cult/pylon/proc/attackpylon(mob/user as mob, var/damage) + if(!isbroken) + if(prob(1+ damage * 5)) + user.visible_message( + "[user] smashed \the [src]!", + "You hit \the [src], and its crystal breaks apart!", + "You hear a tinkle of crystal shards." + ) + STOP_PROCESSING(SSobj, src) + user.do_attack_animation(src) + playsound(src,shatter_sound, 75, 1) + isbroken = 1 + density = FALSE + icon_state = "[initial(icon_state)]-broken" + set_light(0) + else + to_chat(user, "You hit \the [src]!") + playsound(src,impact_sound, 75, 1) + else + if(prob(damage * 2)) + to_chat(user, "You pulverize what was left of \the [src]!") + qdel(src) + else + to_chat(user, "You hit \the [src]!") + playsound(src,impact_sound, 75, 1) + +/obj/structure/cult/pylon/proc/repair(mob/user as mob) + if(isbroken) + START_PROCESSING(SSobj, src) + to_chat(user, "You repair \the [src].") + isbroken = 0 + density = TRUE + icon_state = initial(icon_state) + set_light(5) + +// Returns 1 if the pylon does something special. +/obj/structure/cult/pylon/proc/pylon_unique() + last_activation = world.time + return 0 + +/obj/structure/cult/pylon/process() + if(!isbroken && (last_activation < world.time + activation_cooldown) && pylon_unique()) + flick("[initial(icon_state)]-surge",src) + +/obj/structure/cult/tome + name = "Desk" + desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl." + icon_state = "tomealtar" + +//sprites for this no longer exist -Pete +//(they were stolen from another game anyway) +/* +/obj/structure/cult/pillar + name = "Pillar" + desc = "This should not exist" + icon_state = "pillar" + icon = 'magic_pillar.dmi' +*/ + +/obj/effect/gateway + name = "gateway" + desc = "You're pretty sure that abyss is staring back." + icon = 'icons/obj/cult.dmi' + icon_state = "hole" + density = TRUE + unacidable = TRUE + anchored = TRUE + var/spawnable = null + +/obj/effect/gateway/active + light_range=5 + light_color="#ff0000" + spawnable=list( + /mob/living/simple_mob/animal/space/bats, + /mob/living/simple_mob/creature, + /mob/living/simple_mob/faithless + ) + +/obj/effect/gateway/active/cult + light_range=5 + light_color="#ff0000" + spawnable=list( + /mob/living/simple_mob/animal/space/bats/cult, + /mob/living/simple_mob/creature/cult, + /mob/living/simple_mob/faithless/cult + ) + +/obj/effect/gateway/active/cult/cultify() + return + +/obj/effect/gateway/active/Initialize() + addtimer(CALLBACK(src, PROC_REF(spawn_and_qdel)), rand(30, 60) SECONDS) + +/obj/effect/gateway/active/proc/spawn_and_qdel() + if(LAZYLEN(spawnable)) + var/t = pick(spawnable) + new t(get_turf(src)) + qdel(src) + +/obj/effect/gateway/active/Crossed(var/atom/A) + if(A.is_incorporeal()) + return + if(!istype(A, /mob/living)) + return + + var/mob/living/M = A + + to_chat(M, "Walking into \the [src] is probably a bad idea, you think.") diff --git a/code/game/gamemodes/cult/cultify/mob.dm b/code/game/gamemodes/cult/cultify/mob.dm index 9c63b8857e8..9c5eb4458f4 100644 --- a/code/game/gamemodes/cult/cultify/mob.dm +++ b/code/game/gamemodes/cult/cultify/mob.dm @@ -1,63 +1,63 @@ -/mob - //thou shall always be able to see the Geometer of Blood - var/image/narsimage = null - var/image/narglow = null - -/mob/proc/cultify() - return - -/mob/observer/dead/cultify() - if(icon_state != "ghost-narsie") - icon = 'icons/mob/mob.dmi' - icon_state = "ghost-narsie" - overlays = 0 - invisibility = 0 - to_chat(src, "Even as a non-corporal being, you can feel Nar-Sie's presence altering you. You are now visible to everyone.") - -/mob/living/cultify() - if(iscultist(src) && client) - var/mob/living/simple_mob/construct/harvester/C = new(get_turf(src)) - mind.transfer_to(C) - to_chat(C, "The Geometer of Blood is overjoyed to be reunited with its followers, and accepts your body in sacrifice. As reward, you have been gifted with the shell of an Harvester.
    Your tendrils can use and draw runes without need for a tome, your eyes can see beings through walls, and your mind can open any door. Use these assets to serve Nar-Sie and bring him any remaining living human in the world.
    You can teleport yourself back to Nar-Sie along with any being under yourself at any time using your \"Harvest\" spell.
    ") - dust() - else if(client) - var/mob/observer/dead/G = (ghostize()) - G.icon = 'icons/mob/mob.dmi' - G.icon_state = "ghost-narsie" - G.overlays = 0 - G.invisibility = 0 - to_chat(G, "You feel relieved as what's left of your soul finally escapes its prison of flesh.") - - cult.harvested += G.mind - else - dust() - -/mob/proc/see_narsie(var/obj/singularity/narsie/large/N, var/dir) - if(N.chained) - if(narsimage) - qdel(narsimage) - qdel(narglow) - return - if((N.z == src.z)&&(get_dist(N,src) <= (N.consume_range+10)) && !(N in view(src))) - if(!narsimage) //Create narsimage - narsimage = image('icons/obj/narsie.dmi',src.loc,"narsie",9,1) - narsimage.mouse_opacity = 0 - if(!narglow) //Create narglow - narglow = image('icons/obj/narsie.dmi',narsimage.loc,"glow-narsie",12,1) - narglow.mouse_opacity = 0 - //Else if no dir is given, simply send them the image of narsie - var/new_x = 32 * (N.x - src.x) + N.pixel_x - var/new_y = 32 * (N.y - src.y) + N.pixel_y - narsimage.pixel_x = new_x - narsimage.pixel_y = new_y - narglow.pixel_x = new_x - narglow.pixel_y = new_y - narsimage.loc = src.loc - narglow.loc = src.loc - //Display the new narsimage to the player - src << narsimage - src << narglow - else - if(narsimage) - qdel(narsimage) - qdel(narglow) +/mob + //thou shall always be able to see the Geometer of Blood + var/image/narsimage = null + var/image/narglow = null + +/mob/proc/cultify() + return + +/mob/observer/dead/cultify() + if(icon_state != "ghost-narsie") + icon = 'icons/mob/mob.dmi' + icon_state = "ghost-narsie" + overlays = 0 + invisibility = 0 + to_chat(src, "Even as a non-corporal being, you can feel Nar-Sie's presence altering you. You are now visible to everyone.") + +/mob/living/cultify() + if(iscultist(src) && client) + var/mob/living/simple_mob/construct/harvester/C = new(get_turf(src)) + mind.transfer_to(C) + to_chat(C, "The Geometer of Blood is overjoyed to be reunited with its followers, and accepts your body in sacrifice. As reward, you have been gifted with the shell of an Harvester.
    Your tendrils can use and draw runes without need for a tome, your eyes can see beings through walls, and your mind can open any door. Use these assets to serve Nar-Sie and bring him any remaining living human in the world.
    You can teleport yourself back to Nar-Sie along with any being under yourself at any time using your \"Harvest\" spell.
    ") + dust() + else if(client) + var/mob/observer/dead/G = (ghostize()) + G.icon = 'icons/mob/mob.dmi' + G.icon_state = "ghost-narsie" + G.overlays = 0 + G.invisibility = 0 + to_chat(G, "You feel relieved as what's left of your soul finally escapes its prison of flesh.") + + cult.harvested += G.mind + else + dust() + +/mob/proc/see_narsie(var/obj/singularity/narsie/large/N, var/dir) + if(N.chained) + if(narsimage) + qdel(narsimage) + qdel(narglow) + return + if((N.z == src.z)&&(get_dist(N,src) <= (N.consume_range+10)) && !(N in view(src))) + if(!narsimage) //Create narsimage + narsimage = image('icons/obj/narsie.dmi',src.loc,"narsie",9,1) + narsimage.mouse_opacity = 0 + if(!narglow) //Create narglow + narglow = image('icons/obj/narsie.dmi',narsimage.loc,"glow-narsie",12,1) + narglow.mouse_opacity = 0 + //Else if no dir is given, simply send them the image of narsie + var/new_x = 32 * (N.x - src.x) + N.pixel_x + var/new_y = 32 * (N.y - src.y) + N.pixel_y + narsimage.pixel_x = new_x + narsimage.pixel_y = new_y + narglow.pixel_x = new_x + narglow.pixel_y = new_y + narsimage.loc = src.loc + narglow.loc = src.loc + //Display the new narsimage to the player + src << narsimage + src << narglow + else + if(narsimage) + qdel(narsimage) + qdel(narglow) diff --git a/code/game/gamemodes/cult/cultify/obj.dm b/code/game/gamemodes/cult/cultify/obj.dm index 91dabc556fa..e00dbc6aad5 100644 --- a/code/game/gamemodes/cult/cultify/obj.dm +++ b/code/game/gamemodes/cult/cultify/obj.dm @@ -1,139 +1,139 @@ -/obj/proc/cultify() - qdel(src) - -/obj/effect/decal/cleanable/blood/cultify() - return - -/obj/effect/decal/remains/cultify() - return - -/obj/effect/overlay/cultify() - return - -/obj/item/device/flashlight/lamp/cultify() - new /obj/structure/cult/pylon(loc) - ..() - -/obj/item/stack/material/wood/cultify() - return - -/obj/item/weapon/book/cultify() - new /obj/item/weapon/book/tome(loc) - ..() - -/obj/item/weapon/material/sword/cultify() - new /obj/item/weapon/melee/cultblade(loc) - ..() - -/obj/item/weapon/storage/backpack/cultify() - new /obj/item/weapon/storage/backpack/cultpack(loc) - ..() - -/obj/item/weapon/storage/backpack/cultpack/cultify() - return - -/obj/machinery/cultify() - // We keep the number of cultified machines down by only converting those that are dense - // The alternative is to keep a separate file of exceptions. - if(density) - var/list/random_structure = list( - /obj/structure/cult/talisman, - /obj/structure/cult/forge, - /obj/structure/cult/tome - ) - var/I = pick(random_structure) - new I(loc) - ..() - -/obj/machinery/atmospherics/cultify() - if(src.invisibility != INVISIBILITY_MAXIMUM) - src.invisibility = INVISIBILITY_MAXIMUM - density = FALSE - -/obj/machinery/appliance/cooker/cultify() - new /obj/structure/cult/talisman(loc) - qdel(src) - -/obj/machinery/computer/cultify() - new /obj/structure/cult/tome(loc) - qdel(src) - -/obj/machinery/door/airlock/external/cultify() - new /obj/structure/simple_door/wood(loc) - ..() - -/obj/machinery/door/cultify() - if(invisibility != INVISIBILITY_MAXIMUM) - invisibility = INVISIBILITY_MAXIMUM - density = FALSE - anim(target = src, a_icon = 'icons/effects/effects.dmi', a_icon_state = "breakdoor", sleeptime = 10) - qdel(src) - -/obj/machinery/door/firedoor/cultify() - qdel(src) - -/obj/machinery/light/cultify() - new /obj/structure/cult/pylon(loc) - qdel(src) - -/obj/machinery/mech_sensor/cultify() - qdel(src) - -/obj/machinery/power/apc/cultify() - if(src.invisibility != INVISIBILITY_MAXIMUM) - src.invisibility = INVISIBILITY_MAXIMUM - -/obj/machinery/vending/cultify() - new /obj/structure/cult/forge(loc) - qdel(src) - -/obj/structure/bed/chair/cultify() - var/obj/structure/bed/chair/wood/wings/I = new(loc) - I.dir = dir - ..() - -/obj/structure/bed/chair/wood/cultify() - return - -/obj/structure/bookcase/cultify() - return - -/obj/structure/grille/cultify() - new /obj/structure/grille/cult(get_turf(src)) - ..() - -/obj/structure/grille/cult/cultify() - return - -/obj/structure/simple_door/cultify() - new /obj/structure/simple_door/wood(loc) - ..() - -/obj/structure/simple_door/wood/cultify() - return - -/obj/singularity/cultify() - var/dist = max((current_size - 2), 1) - explosion(get_turf(src), dist, dist * 2, dist * 4) - qdel(src) - -/obj/structure/shuttle/engine/heater/cultify() - new /obj/structure/cult/pylon(loc) - ..() - -/obj/structure/shuttle/engine/propulsion/cultify() - var/turf/T = get_turf(src) - if(T) - T.ChangeTurf(/turf/simulated/wall/cult) - ..() - -/obj/structure/table/cultify() - // Make it a wood-reinforced wooden table. - // There are cult materials available, but it'd make the table non-deconstructable with how holotables work. - // Could possibly use a new material var for holographic-ness? - material = get_material_by_name("wood") - reinforced = get_material_by_name("wood") - update_desc() - update_connections(1) - update_icon() - update_material() +/obj/proc/cultify() + qdel(src) + +/obj/effect/decal/cleanable/blood/cultify() + return + +/obj/effect/decal/remains/cultify() + return + +/obj/effect/overlay/cultify() + return + +/obj/item/device/flashlight/lamp/cultify() + new /obj/structure/cult/pylon(loc) + ..() + +/obj/item/stack/material/wood/cultify() + return + +/obj/item/weapon/book/cultify() + new /obj/item/weapon/book/tome(loc) + ..() + +/obj/item/weapon/material/sword/cultify() + new /obj/item/weapon/melee/cultblade(loc) + ..() + +/obj/item/weapon/storage/backpack/cultify() + new /obj/item/weapon/storage/backpack/cultpack(loc) + ..() + +/obj/item/weapon/storage/backpack/cultpack/cultify() + return + +/obj/machinery/cultify() + // We keep the number of cultified machines down by only converting those that are dense + // The alternative is to keep a separate file of exceptions. + if(density) + var/list/random_structure = list( + /obj/structure/cult/talisman, + /obj/structure/cult/forge, + /obj/structure/cult/tome + ) + var/I = pick(random_structure) + new I(loc) + ..() + +/obj/machinery/atmospherics/cultify() + if(src.invisibility != INVISIBILITY_MAXIMUM) + src.invisibility = INVISIBILITY_MAXIMUM + density = FALSE + +/obj/machinery/appliance/cooker/cultify() + new /obj/structure/cult/talisman(loc) + qdel(src) + +/obj/machinery/computer/cultify() + new /obj/structure/cult/tome(loc) + qdel(src) + +/obj/machinery/door/airlock/external/cultify() + new /obj/structure/simple_door/wood(loc) + ..() + +/obj/machinery/door/cultify() + if(invisibility != INVISIBILITY_MAXIMUM) + invisibility = INVISIBILITY_MAXIMUM + density = FALSE + anim(target = src, a_icon = 'icons/effects/effects.dmi', a_icon_state = "breakdoor", sleeptime = 10) + qdel(src) + +/obj/machinery/door/firedoor/cultify() + qdel(src) + +/obj/machinery/light/cultify() + new /obj/structure/cult/pylon(loc) + qdel(src) + +/obj/machinery/mech_sensor/cultify() + qdel(src) + +/obj/machinery/power/apc/cultify() + if(src.invisibility != INVISIBILITY_MAXIMUM) + src.invisibility = INVISIBILITY_MAXIMUM + +/obj/machinery/vending/cultify() + new /obj/structure/cult/forge(loc) + qdel(src) + +/obj/structure/bed/chair/cultify() + var/obj/structure/bed/chair/wood/wings/I = new(loc) + I.dir = dir + ..() + +/obj/structure/bed/chair/wood/cultify() + return + +/obj/structure/bookcase/cultify() + return + +/obj/structure/grille/cultify() + new /obj/structure/grille/cult(get_turf(src)) + ..() + +/obj/structure/grille/cult/cultify() + return + +/obj/structure/simple_door/cultify() + new /obj/structure/simple_door/wood(loc) + ..() + +/obj/structure/simple_door/wood/cultify() + return + +/obj/singularity/cultify() + var/dist = max((current_size - 2), 1) + explosion(get_turf(src), dist, dist * 2, dist * 4) + qdel(src) + +/obj/structure/shuttle/engine/heater/cultify() + new /obj/structure/cult/pylon(loc) + ..() + +/obj/structure/shuttle/engine/propulsion/cultify() + var/turf/T = get_turf(src) + if(T) + T.ChangeTurf(/turf/simulated/wall/cult) + ..() + +/obj/structure/table/cultify() + // Make it a wood-reinforced wooden table. + // There are cult materials available, but it'd make the table non-deconstructable with how holotables work. + // Could possibly use a new material var for holographic-ness? + material = get_material_by_name("wood") + reinforced = get_material_by_name("wood") + update_desc() + update_connections(1) + update_icon() + update_material() diff --git a/code/game/gamemodes/cult/cultify/turf.dm b/code/game/gamemodes/cult/cultify/turf.dm index 4506f49fccd..8cfd85886a7 100644 --- a/code/game/gamemodes/cult/cultify/turf.dm +++ b/code/game/gamemodes/cult/cultify/turf.dm @@ -1,42 +1,42 @@ -/turf/proc/cultify() - ChangeTurf(/turf/space) - return - -/turf/simulated/floor/cultify() - //todo: flooring datum cultify check - cultify_floor() - -/turf/simulated/shuttle/floor/cultify() - cultify_floor() - -/turf/simulated/shuttle/floor4/cultify() - cultify_floor() - -/turf/simulated/shuttle/wall/cultify() - cultify_wall() - -/turf/simulated/wall/cultify() - cultify_wall() - -/turf/simulated/wall/cult/cultify() - return - -/turf/unsimulated/wall/cult/cultify() - return - -/turf/unsimulated/beach/cultify() - return - -/turf/unsimulated/floor/cultify() - cultify_floor() - -/turf/unsimulated/wall/cultify() - cultify_wall() - -/turf/proc/cultify_floor() - if((icon_state != "cult")&&(icon_state != "cult-narsie")) - name = "engraved floor" - icon_state = "cult" - -/turf/proc/cultify_wall() - ChangeTurf(/turf/unsimulated/wall/cult) +/turf/proc/cultify() + ChangeTurf(/turf/space) + return + +/turf/simulated/floor/cultify() + //todo: flooring datum cultify check + cultify_floor() + +/turf/simulated/shuttle/floor/cultify() + cultify_floor() + +/turf/simulated/shuttle/floor4/cultify() + cultify_floor() + +/turf/simulated/shuttle/wall/cultify() + cultify_wall() + +/turf/simulated/wall/cultify() + cultify_wall() + +/turf/simulated/wall/cult/cultify() + return + +/turf/unsimulated/wall/cult/cultify() + return + +/turf/unsimulated/beach/cultify() + return + +/turf/unsimulated/floor/cultify() + cultify_floor() + +/turf/unsimulated/wall/cultify() + cultify_wall() + +/turf/proc/cultify_floor() + if((icon_state != "cult")&&(icon_state != "cult-narsie")) + name = "engraved floor" + icon_state = "cult" + +/turf/proc/cultify_wall() + ChangeTurf(/turf/unsimulated/wall/cult) diff --git a/code/game/gamemodes/cult/hell_universe.dm b/code/game/gamemodes/cult/hell_universe.dm index 0cf1e2d0fd6..d558a8bfbb2 100644 --- a/code/game/gamemodes/cult/hell_universe.dm +++ b/code/game/gamemodes/cult/hell_universe.dm @@ -1,84 +1,84 @@ -/* - -In short: - * Random gateways spawning hellmonsters - * Broken Fire Alarms - * Random tiles changing to culty tiles. - -*/ -/datum/universal_state/hell - name = "Hell Rising" - desc = "OH FUCK OH FUCK OH FUCK" - - decay_rate = 5 // 5% chance of a turf decaying on lighting update/airflow (there's no actual tick for turfs) - -/datum/universal_state/hell/OnShuttleCall(var/mob/user) - return 1 - /* - if(user) - to_chat(user, "All you hear on the frequency is static and panicked screaming. There will be no shuttle call today.") - return 0 - */ - -/datum/universal_state/hell/DecayTurf(var/turf/T) - if(!T.holy) - T.cultify() - for(var/obj/machinery/light/L in T.contents) - new /obj/structure/cult/pylon(L.loc) - qdel(L) - return - - -/datum/universal_state/hell/OnTurfChange(var/turf/T) - var/turf/space/S = T - if(istype(S)) - S.color = "#FF0000" - else - S.color = initial(S.color) - -// Apply changes when entering state -/datum/universal_state/hell/OnEnter() - set background = 1 -// garbage_collector.garbage_collect = 0 - - escape_list = get_area_turfs(locate(/area/hallway/secondary/exit)) - - //Separated into separate procs for profiling - AreaSet() - MiscSet() - APCSet() - OverlayAndAmbientSet() - lightsout(0,0) - - runedec += 9000 //basically removing the rune cap - - -/datum/universal_state/hell/proc/AreaSet() - for(var/area/A in world) - if(!istype(A,/area) || istype(A, /area/space)) - continue - - A.update_icon() - -/datum/universal_state/hell/OverlayAndAmbientSet() - spawn(0) - for(var/datum/lighting_corner/L in world) - L.update_lumcount(1, 0, 0) - - for(var/turf/space/T in world) - OnTurfChange(T) - -/datum/universal_state/hell/proc/MiscSet() - for(var/turf/simulated/floor/T in world) - if(!T.holy && prob(1)) - new /obj/effect/gateway/active/cult(T) - - for (var/obj/machinery/firealarm/alm in machines) - if (!(alm.stat & BROKEN)) - alm.ex_act(2) - -/datum/universal_state/hell/proc/APCSet() - for (var/obj/machinery/power/apc/APC in GLOB.apcs) - if (!(APC.stat & BROKEN) && !APC.is_critical) - APC.emagged = 1 - APC.queue_icon_update() +/* + +In short: + * Random gateways spawning hellmonsters + * Broken Fire Alarms + * Random tiles changing to culty tiles. + +*/ +/datum/universal_state/hell + name = "Hell Rising" + desc = "OH FUCK OH FUCK OH FUCK" + + decay_rate = 5 // 5% chance of a turf decaying on lighting update/airflow (there's no actual tick for turfs) + +/datum/universal_state/hell/OnShuttleCall(var/mob/user) + return 1 + /* + if(user) + to_chat(user, "All you hear on the frequency is static and panicked screaming. There will be no shuttle call today.") + return 0 + */ + +/datum/universal_state/hell/DecayTurf(var/turf/T) + if(!T.holy) + T.cultify() + for(var/obj/machinery/light/L in T.contents) + new /obj/structure/cult/pylon(L.loc) + qdel(L) + return + + +/datum/universal_state/hell/OnTurfChange(var/turf/T) + var/turf/space/S = T + if(istype(S)) + S.color = "#FF0000" + else + S.color = initial(S.color) + +// Apply changes when entering state +/datum/universal_state/hell/OnEnter() + set background = 1 +// garbage_collector.garbage_collect = 0 + + escape_list = get_area_turfs(locate(/area/hallway/secondary/exit)) + + //Separated into separate procs for profiling + AreaSet() + MiscSet() + APCSet() + OverlayAndAmbientSet() + lightsout(0,0) + + runedec += 9000 //basically removing the rune cap + + +/datum/universal_state/hell/proc/AreaSet() + for(var/area/A in world) + if(!istype(A,/area) || istype(A, /area/space)) + continue + + A.update_icon() + +/datum/universal_state/hell/OverlayAndAmbientSet() + spawn(0) + for(var/datum/lighting_corner/L in world) + L.update_lumcount(1, 0, 0) + + for(var/turf/space/T in world) + OnTurfChange(T) + +/datum/universal_state/hell/proc/MiscSet() + for(var/turf/simulated/floor/T in world) + if(!T.holy && prob(1)) + new /obj/effect/gateway/active/cult(T) + + for (var/obj/machinery/firealarm/alm in machines) + if (!(alm.stat & BROKEN)) + alm.ex_act(2) + +/datum/universal_state/hell/proc/APCSet() + for (var/obj/machinery/power/apc/APC in GLOB.apcs) + if (!(APC.stat & BROKEN) && !APC.is_critical) + APC.emagged = 1 + APC.queue_icon_update() diff --git a/code/game/gamemodes/cult/narsie.dm b/code/game/gamemodes/cult/narsie.dm index 9c3504924f2..5a8dc60d3f8 100644 --- a/code/game/gamemodes/cult/narsie.dm +++ b/code/game/gamemodes/cult/narsie.dm @@ -1,367 +1,367 @@ -var/global/narsie_behaviour = "CultStation13" -var/global/narsie_cometh = 0 -var/global/list/narsie_list = list() -/obj/singularity/narsie //Moving narsie to its own file for the sake of being clearer - name = "Nar-Sie" - desc = "Your mind begins to bubble and ooze as it tries to comprehend what it sees." - icon = 'icons/obj/narsie.dmi' - icon_state = "narsie-small" - pixel_x = -236 - pixel_y = -256 - - current_size = 9 //It moves/eats like a max-size singulo, aside from range. --NEO. - contained = 0 // Are we going to move around? - dissipate = 0 // Do we lose energy over time? - grav_pull = 10 //How many tiles out do we pull? - consume_range = 3 //How many tiles out do we eat - - -/obj/singularity/narsie/New() - ..() - narsie_list.Add(src) - -/obj/singularity/narsie/Destroy() - narsie_list.Remove(src) - ..() - -/obj/singularity/narsie/large - name = "Nar-Sie" - icon = 'icons/obj/narsie.dmi' - icon_state = "narsie"//mobs perceive the geometer of blood through their see_narsie proc - - // Pixel stuff centers Narsie. - pixel_x = -236 - pixel_y = -256 - light_range = 1 - light_color = "#3e0000" - - current_size = 12 - consume_range = 12 // How many tiles out do we eat. - var/announce=1 - var/cause_hell = 1 - -/obj/singularity/narsie/large/New() - ..() - if(announce) - to_world("[uppertext(name)] HAS RISEN") - world << sound('sound/effects/weather/wind/wind_5_1.ogg') - - narsie_spawn_animation() - - if(!narsie_cometh)//so we don't initiate Hell more than one time. - if(cause_hell) - SetUniversalState(/datum/universal_state/hell) - narsie_cometh = 1 - - spawn(10 SECONDS) - if(emergency_shuttle) - emergency_shuttle.call_evac() - emergency_shuttle.launch_time = 0 // Cannot recall - -/obj/singularity/narsie/process() - eat() - - if (!target || prob(5)) - pickcultist() - - move() - - if (prob(25)) - mezzer() - -/obj/singularity/narsie/large/eat() - for (var/turf/A in orange(consume_range, src)) - consume(A) - -/obj/singularity/narsie/mezzer() - for(var/mob/living/carbon/M in oviewers(8, src)) - if(M.stat == CONSCIOUS) - if(M.status_flags & GODMODE) - continue - if(!iscultist(M)) - to_chat(M, " You feel your sanity crumble away in an instant as you gaze upon [src.name]...") - M.apply_effect(3, STUN) - - -/obj/singularity/narsie/large/Bump(atom/A) - if(!cause_hell) return - if(isturf(A)) - narsiewall(A) - else if(istype(A, /obj/structure/cult)) - qdel(A) - -/obj/singularity/narsie/large/Bumped(atom/A) - if(!cause_hell) return - if(isturf(A)) - narsiewall(A) - else if(istype(A, /obj/structure/cult)) - qdel(A) - -/obj/singularity/narsie/move(var/force_move = 0) - if(!move_self) - return 0 - - var/movement_dir = pick(alldirs - last_failed_movement) - - if(force_move) - movement_dir = force_move - - if(target && prob(60)) - movement_dir = get_dir(src,target) - - spawn(0) - step(src, movement_dir) - spawn(1) - step(src, movement_dir) - return 1 - -/obj/singularity/narsie/large/move(var/force_move = 0) - if(!move_self) - return 0 - - var/movement_dir = pick(alldirs - last_failed_movement) - - if(force_move) - movement_dir = force_move - - if(target && prob(60)) - movement_dir = get_dir(src,target) - spawn(0) - step(src, movement_dir) - narsiefloor(get_turf(loc)) - for(var/mob/M in player_list) - if(M.client) - M.see_narsie(src,movement_dir) - spawn(10) - step(src, movement_dir) - narsiefloor(get_turf(loc)) - for(var/mob/M in player_list) - if(M.client) - M.see_narsie(src,movement_dir) - return 1 - -/obj/singularity/narsie/proc/narsiefloor(var/turf/T)//leaving "footprints" - if(!(istype(T, /turf/simulated/wall/cult)||istype(T, /turf/space))) - if(T.icon_state != "cult-narsie") - T.desc = "something that goes beyond your understanding went this way." - T.icon = 'icons/turf/flooring/cult.dmi' - T.icon_state = "cult-narsie" - T.set_light(1) - -/obj/singularity/narsie/proc/narsiewall(var/turf/T) - T.desc = "An opening has been made on that wall, but who can say if what you seek truly lies on the other side?" - T.icon = 'icons/turf/walls.dmi' - T.icon_state = "cult-narsie" - T.opacity = 0 - T.density = FALSE - set_light(1) - -/obj/singularity/narsie/large/consume(const/atom/A) //Has its own consume proc because it doesn't need energy and I don't want BoHs to explode it. --NEO -//NEW BEHAVIOUR - if(narsie_behaviour == "CultStation13") - //MOB PROCESSING - new_narsie(A) - -//OLD BEHAVIOUR - else if(narsie_behaviour == "Nar-Singulo") - old_narsie(A) - -/obj/singularity/narsie/proc/new_narsie(const/atom/A) - if (istype(A, /mob/) && (get_dist(A, src) <= 7)) - var/mob/M = A - - if(M.status_flags & GODMODE) - return 0 - - M.cultify() - -//ITEM PROCESSING - else if (istype(A, /obj/)) - var/obj/O = A - O.cultify() - -//TURF PROCESSING - else if (isturf(A)) - var/dist = get_dist(A, src) - - for (var/atom/movable/AM in A.contents) - if (dist <= consume_range) - consume(AM) - continue - - if (dist <= consume_range && !istype(A, /turf/space)) - var/turf/T = A - if(T.holy) - T.holy = 0 //Nar-Sie doesn't give a shit about sacred grounds. - T.cultify() - -/obj/singularity/narsie/proc/old_narsie(const/atom/A) - if(!(A.singuloCanEat())) - return 0 - - if (istype(A, /mob/living/)) - var/mob/living/C2 = A - - if(C2.status_flags & GODMODE) - return 0 - - C2.dust() // Changed from gib(), just for less lag. - - else if (istype(A, /obj/)) - qdel(A) - - if (A) - qdel(A) - else if (isturf(A)) - var/dist = get_dist(A, src) - - for (var/atom/movable/AM2 in A.contents) - if (AM2 == src) // This is the snowflake. - continue - - if (dist <= consume_range) - consume(AM2) - continue - - if (dist <= consume_range && !istype(A, get_base_turf_by_area(A))) - var/turf/T2 = A - T2.ChangeTurf(get_base_turf_by_area(A)) - -/obj/singularity/narsie/consume(const/atom/A) //This one is for the small ones. - if(!(A.singuloCanEat())) - return 0 - - if (istype(A, /mob/living/)) - var/mob/living/C2 = A - - if(C2.status_flags & GODMODE) - return 0 - - C2.dust() // Changed from gib(), just for less lag. - - else if (istype(A, /obj/)) - qdel(A) - - if (A) - qdel(A) - else if (isturf(A)) - var/dist = get_dist(A, src) - - for (var/atom/movable/AM2 in A.contents) - if (AM2 == src) // This is the snowflake. - continue - - if (dist <= consume_range) - consume(AM2) - continue - - if (dist > consume_range) - if(!(AM2.singuloCanEat())) - continue - - if (101 == AM2.invisibility) - continue - - spawn (0) - AM2.singularity_pull(src, src.current_size) - - if (dist <= consume_range && !istype(A, get_base_turf_by_area(A))) - var/turf/T2 = A - T2.ChangeTurf(get_base_turf_by_area(A)) - -/obj/singularity/narsie/ex_act(severity) //No throwing bombs at it either. --NEO - return - -/obj/singularity/narsie/proc/pickcultist() //Narsie rewards his cultists with being devoured first, then picks a ghost to follow. --NEO - var/list/cultists = list() - for(var/datum/mind/cult_nh_mind in cult.current_antagonists) - if(!cult_nh_mind.current) - continue - if(cult_nh_mind.current.stat) - continue - var/turf/pos = get_turf(cult_nh_mind.current) - if(pos.z != src.z) - continue - cultists += cult_nh_mind.current - if(cultists.len) - acquire(pick(cultists)) - return - //If there was living cultists, it picks one to follow. - for(var/mob/living/carbon/human/food in living_mob_list) - if(food.stat) - continue - var/turf/pos = get_turf(food) - if(pos.z != src.z) - continue - cultists += food - if(cultists.len) - acquire(pick(cultists)) - return - //no living cultists, pick a living human instead. - for(var/mob/observer/dead/ghost in player_list) - if(!ghost.client) - continue - var/turf/pos = get_turf(ghost) - if(pos.z != src.z) - continue - cultists += ghost - if(cultists.len) - acquire(pick(cultists)) - return - //no living humans, follow a ghost instead. - -/obj/singularity/narsie/proc/acquire(const/mob/food) - var/capname = uppertext(name) - - to_chat(target, "[capname] HAS LOST INTEREST IN YOU.") - target = food - - if (ishuman(target)) - to_chat(target, "[capname] HUNGERS FOR YOUR SOUL.") - else - to_chat(target, "[capname] HAS CHOSEN YOU TO LEAD HIM TO HIS NEXT MEAL.") - -/obj/singularity/narsie/on_capture() - chained = 1 - move_self = 0 - icon_state ="narsie-small-chains" - -/obj/singularity/narsie/on_release() - chained = 0 - move_self = 1 - icon_state ="narsie-small" - -/obj/singularity/narsie/large/on_capture() - chained = 1 - move_self = 0 - icon_state ="narsie-chains" - for(var/mob/M in mob_list)//removing the client image of nar-sie while it is chained - if(M.client) - M.see_narsie(src) - -/obj/singularity/narsie/large/on_release() - chained = 0 - move_self = 1 - icon_state ="narsie" - -/obj/singularity/narsie/cultify() - return - -/** - * Wizard narsie. - */ -/obj/singularity/narsie/wizard - grav_pull = 0 - -/obj/singularity/narsie/wizard/eat() - for (var/turf/T in trange(consume_range, src)) - consume(T) - -/obj/singularity/narsie/proc/narsie_spawn_animation() - icon = 'icons/obj/narsie_spawn_anim.dmi' - dir = SOUTH - move_self = 0 - flick("narsie_spawn_anim",src) - sleep(11) - move_self = 1 - icon = initial(icon) +var/global/narsie_behaviour = "CultStation13" +var/global/narsie_cometh = 0 +var/global/list/narsie_list = list() +/obj/singularity/narsie //Moving narsie to its own file for the sake of being clearer + name = "Nar-Sie" + desc = "Your mind begins to bubble and ooze as it tries to comprehend what it sees." + icon = 'icons/obj/narsie.dmi' + icon_state = "narsie-small" + pixel_x = -236 + pixel_y = -256 + + current_size = 9 //It moves/eats like a max-size singulo, aside from range. --NEO. + contained = 0 // Are we going to move around? + dissipate = 0 // Do we lose energy over time? + grav_pull = 10 //How many tiles out do we pull? + consume_range = 3 //How many tiles out do we eat + + +/obj/singularity/narsie/New() + ..() + narsie_list.Add(src) + +/obj/singularity/narsie/Destroy() + narsie_list.Remove(src) + ..() + +/obj/singularity/narsie/large + name = "Nar-Sie" + icon = 'icons/obj/narsie.dmi' + icon_state = "narsie"//mobs perceive the geometer of blood through their see_narsie proc + + // Pixel stuff centers Narsie. + pixel_x = -236 + pixel_y = -256 + light_range = 1 + light_color = "#3e0000" + + current_size = 12 + consume_range = 12 // How many tiles out do we eat. + var/announce=1 + var/cause_hell = 1 + +/obj/singularity/narsie/large/New() + ..() + if(announce) + to_world("[uppertext(name)] HAS RISEN") + world << sound('sound/effects/weather/wind/wind_5_1.ogg') + + narsie_spawn_animation() + + if(!narsie_cometh)//so we don't initiate Hell more than one time. + if(cause_hell) + SetUniversalState(/datum/universal_state/hell) + narsie_cometh = 1 + + spawn(10 SECONDS) + if(emergency_shuttle) + emergency_shuttle.call_evac() + emergency_shuttle.launch_time = 0 // Cannot recall + +/obj/singularity/narsie/process() + eat() + + if (!target || prob(5)) + pickcultist() + + move() + + if (prob(25)) + mezzer() + +/obj/singularity/narsie/large/eat() + for (var/turf/A in orange(consume_range, src)) + consume(A) + +/obj/singularity/narsie/mezzer() + for(var/mob/living/carbon/M in oviewers(8, src)) + if(M.stat == CONSCIOUS) + if(M.status_flags & GODMODE) + continue + if(!iscultist(M)) + to_chat(M, " You feel your sanity crumble away in an instant as you gaze upon [src.name]...") + M.apply_effect(3, STUN) + + +/obj/singularity/narsie/large/Bump(atom/A) + if(!cause_hell) return + if(isturf(A)) + narsiewall(A) + else if(istype(A, /obj/structure/cult)) + qdel(A) + +/obj/singularity/narsie/large/Bumped(atom/A) + if(!cause_hell) return + if(isturf(A)) + narsiewall(A) + else if(istype(A, /obj/structure/cult)) + qdel(A) + +/obj/singularity/narsie/move(var/force_move = 0) + if(!move_self) + return 0 + + var/movement_dir = pick(alldirs - last_failed_movement) + + if(force_move) + movement_dir = force_move + + if(target && prob(60)) + movement_dir = get_dir(src,target) + + spawn(0) + step(src, movement_dir) + spawn(1) + step(src, movement_dir) + return 1 + +/obj/singularity/narsie/large/move(var/force_move = 0) + if(!move_self) + return 0 + + var/movement_dir = pick(alldirs - last_failed_movement) + + if(force_move) + movement_dir = force_move + + if(target && prob(60)) + movement_dir = get_dir(src,target) + spawn(0) + step(src, movement_dir) + narsiefloor(get_turf(loc)) + for(var/mob/M in player_list) + if(M.client) + M.see_narsie(src,movement_dir) + spawn(10) + step(src, movement_dir) + narsiefloor(get_turf(loc)) + for(var/mob/M in player_list) + if(M.client) + M.see_narsie(src,movement_dir) + return 1 + +/obj/singularity/narsie/proc/narsiefloor(var/turf/T)//leaving "footprints" + if(!(istype(T, /turf/simulated/wall/cult)||istype(T, /turf/space))) + if(T.icon_state != "cult-narsie") + T.desc = "something that goes beyond your understanding went this way." + T.icon = 'icons/turf/flooring/cult.dmi' + T.icon_state = "cult-narsie" + T.set_light(1) + +/obj/singularity/narsie/proc/narsiewall(var/turf/T) + T.desc = "An opening has been made on that wall, but who can say if what you seek truly lies on the other side?" + T.icon = 'icons/turf/walls.dmi' + T.icon_state = "cult-narsie" + T.opacity = 0 + T.density = FALSE + set_light(1) + +/obj/singularity/narsie/large/consume(const/atom/A) //Has its own consume proc because it doesn't need energy and I don't want BoHs to explode it. --NEO +//NEW BEHAVIOUR + if(narsie_behaviour == "CultStation13") + //MOB PROCESSING + new_narsie(A) + +//OLD BEHAVIOUR + else if(narsie_behaviour == "Nar-Singulo") + old_narsie(A) + +/obj/singularity/narsie/proc/new_narsie(const/atom/A) + if (istype(A, /mob/) && (get_dist(A, src) <= 7)) + var/mob/M = A + + if(M.status_flags & GODMODE) + return 0 + + M.cultify() + +//ITEM PROCESSING + else if (istype(A, /obj/)) + var/obj/O = A + O.cultify() + +//TURF PROCESSING + else if (isturf(A)) + var/dist = get_dist(A, src) + + for (var/atom/movable/AM in A.contents) + if (dist <= consume_range) + consume(AM) + continue + + if (dist <= consume_range && !istype(A, /turf/space)) + var/turf/T = A + if(T.holy) + T.holy = 0 //Nar-Sie doesn't give a shit about sacred grounds. + T.cultify() + +/obj/singularity/narsie/proc/old_narsie(const/atom/A) + if(!(A.singuloCanEat())) + return 0 + + if (istype(A, /mob/living/)) + var/mob/living/C2 = A + + if(C2.status_flags & GODMODE) + return 0 + + C2.dust() // Changed from gib(), just for less lag. + + else if (istype(A, /obj/)) + qdel(A) + + if (A) + qdel(A) + else if (isturf(A)) + var/dist = get_dist(A, src) + + for (var/atom/movable/AM2 in A.contents) + if (AM2 == src) // This is the snowflake. + continue + + if (dist <= consume_range) + consume(AM2) + continue + + if (dist <= consume_range && !istype(A, get_base_turf_by_area(A))) + var/turf/T2 = A + T2.ChangeTurf(get_base_turf_by_area(A)) + +/obj/singularity/narsie/consume(const/atom/A) //This one is for the small ones. + if(!(A.singuloCanEat())) + return 0 + + if (istype(A, /mob/living/)) + var/mob/living/C2 = A + + if(C2.status_flags & GODMODE) + return 0 + + C2.dust() // Changed from gib(), just for less lag. + + else if (istype(A, /obj/)) + qdel(A) + + if (A) + qdel(A) + else if (isturf(A)) + var/dist = get_dist(A, src) + + for (var/atom/movable/AM2 in A.contents) + if (AM2 == src) // This is the snowflake. + continue + + if (dist <= consume_range) + consume(AM2) + continue + + if (dist > consume_range) + if(!(AM2.singuloCanEat())) + continue + + if (101 == AM2.invisibility) + continue + + spawn (0) + AM2.singularity_pull(src, src.current_size) + + if (dist <= consume_range && !istype(A, get_base_turf_by_area(A))) + var/turf/T2 = A + T2.ChangeTurf(get_base_turf_by_area(A)) + +/obj/singularity/narsie/ex_act(severity) //No throwing bombs at it either. --NEO + return + +/obj/singularity/narsie/proc/pickcultist() //Narsie rewards his cultists with being devoured first, then picks a ghost to follow. --NEO + var/list/cultists = list() + for(var/datum/mind/cult_nh_mind in cult.current_antagonists) + if(!cult_nh_mind.current) + continue + if(cult_nh_mind.current.stat) + continue + var/turf/pos = get_turf(cult_nh_mind.current) + if(pos.z != src.z) + continue + cultists += cult_nh_mind.current + if(cultists.len) + acquire(pick(cultists)) + return + //If there was living cultists, it picks one to follow. + for(var/mob/living/carbon/human/food in living_mob_list) + if(food.stat) + continue + var/turf/pos = get_turf(food) + if(pos.z != src.z) + continue + cultists += food + if(cultists.len) + acquire(pick(cultists)) + return + //no living cultists, pick a living human instead. + for(var/mob/observer/dead/ghost in player_list) + if(!ghost.client) + continue + var/turf/pos = get_turf(ghost) + if(pos.z != src.z) + continue + cultists += ghost + if(cultists.len) + acquire(pick(cultists)) + return + //no living humans, follow a ghost instead. + +/obj/singularity/narsie/proc/acquire(const/mob/food) + var/capname = uppertext(name) + + to_chat(target, "[capname] HAS LOST INTEREST IN YOU.") + target = food + + if (ishuman(target)) + to_chat(target, "[capname] HUNGERS FOR YOUR SOUL.") + else + to_chat(target, "[capname] HAS CHOSEN YOU TO LEAD HIM TO HIS NEXT MEAL.") + +/obj/singularity/narsie/on_capture() + chained = 1 + move_self = 0 + icon_state ="narsie-small-chains" + +/obj/singularity/narsie/on_release() + chained = 0 + move_self = 1 + icon_state ="narsie-small" + +/obj/singularity/narsie/large/on_capture() + chained = 1 + move_self = 0 + icon_state ="narsie-chains" + for(var/mob/M in mob_list)//removing the client image of nar-sie while it is chained + if(M.client) + M.see_narsie(src) + +/obj/singularity/narsie/large/on_release() + chained = 0 + move_self = 1 + icon_state ="narsie" + +/obj/singularity/narsie/cultify() + return + +/** + * Wizard narsie. + */ +/obj/singularity/narsie/wizard + grav_pull = 0 + +/obj/singularity/narsie/wizard/eat() + for (var/turf/T in trange(consume_range, src)) + consume(T) + +/obj/singularity/narsie/proc/narsie_spawn_animation() + icon = 'icons/obj/narsie_spawn_anim.dmi' + dir = SOUTH + move_self = 0 + flick("narsie_spawn_anim",src) + sleep(11) + move_self = 1 + icon = initial(icon) diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm index dcafc9e48a8..35e1ed8eb92 100644 --- a/code/game/gamemodes/cult/ritual.dm +++ b/code/game/gamemodes/cult/ritual.dm @@ -1,611 +1,611 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -var/cultwords = list() -var/runedec = 0 -var/global/list/engwords = list("travel", "blood", "join", "hell", "destroy", "technology", "self", "see", "other", "hide") -var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa","mgar","balaq", "karazet", "geeri") - -/client/proc/check_words() // -- Urist - set category = "Special Verbs" - set name = "Check Rune Words" - set desc = "Check the rune-word meaning" - if(!cultwords["travel"]) - runerandom() - for (var/word in engwords) - to_chat(usr, "[cultwords[word]] is [word]") - -/proc/runerandom() //randomizes word meaning - var/list/runewords=rnwords - for (var/word in engwords) - cultwords[word] = pick(runewords) - runewords-=cultwords[word] - -/obj/effect/rune - desc = "A strange collection of symbols drawn in blood." - anchored = TRUE - icon = 'icons/obj/rune.dmi' - icon_state = "1" - var/visibility = 0 - unacidable = TRUE - layer = TURF_LAYER - - - var/word1 - var/word2 - var/word3 - var/image/blood_image - var/list/converting = list() - -// Places these combos are mentioned: this file - twice in the rune code, once in imbued tome, once in tome's HTML runes.dm - in the imbue rune code. If you change a combination - dont forget to change it everywhere. - -// travel self [word] - Teleport to random [rune with word destination matching] -// travel other [word] - Portal to rune with word destination matching - kinda doesnt work. At least the icon. No idea why. -// see blood Hell - Create a new tome -// join blood self - Incorporate person over the rune into the group -// Hell join self - Summon TERROR -// destroy see technology - EMP rune -// travel blood self - Drain blood -// see Hell join - See invisible -// blood join Hell - Raise dead - -// hide see blood - Hide nearby runes -// blood see hide - Reveal nearby runes - The point of this rune is that its reversed obscure rune. So you always know the words to reveal the rune once oyu have obscured it. - -// Hell travel self - Leave your body and ghost around -// blood see travel - Manifest a ghost into a mortal body -// Hell tech join - Imbue a rune into a talisman -// Hell blood join - Sacrifice rune -// destroy travel self - Wall rune -// join other self - Summon cultist rune -// travel technology other - Freeing rune // other blood travel was freedom join other - -// hide other see - Deafening rune // was destroy see hear -// destroy see other - Blinding rune -// destroy see blood - BLOOD BOIL - -// self other technology - Communication rune //was other hear blood -// join hide technology - stun rune. Rune color: bright pink. -/obj/effect/rune/Initialize() - . = ..() - blood_image = image(loc = src) - blood_image.override = 1 - for(var/mob/living/silicon/ai/AI in player_list) - if(AI.client) - AI.client.images += blood_image - rune_list.Add(src) - -/obj/effect/rune/Destroy() - for(var/mob/living/silicon/ai/AI in player_list) - if(AI.client) - AI.client.images -= blood_image - qdel(blood_image) - blood_image = null - rune_list.Remove(src) - ..() - -/obj/effect/rune/examine(mob/user) - . = ..() - if(iscultist(user)) - . += "This spell circle reads: [word1] [word2] [word3]." - - -/obj/effect/rune/attackby(I as obj, user as mob) - if(istype(I, /obj/item/weapon/book/tome) && iscultist(user)) - to_chat(user, "You retrace your steps, carefully undoing the lines of the rune.") - qdel(src) - return - else if(istype(I, /obj/item/weapon/nullrod)) - to_chat(user, "You disrupt the vile magic with the deadening field of the null rod!") - qdel(src) - return - return - - -/obj/effect/rune/attack_hand(mob/living/user as mob) - if(!iscultist(user)) - to_chat(user, "You can't mouth the arcane scratchings without fumbling over them.") - return - if(user.is_muzzled()) - to_chat(user, "You are unable to speak the words of the rune.") - return - if(!word1 || !word2 || !word3 || prob(user.getBrainLoss())) - return fizzle() -// if(!src.visibility) -// src.visibility=1 - if(word1 == cultwords["travel"] && word2 == cultwords["self"]) - return teleport(src.word3) - if(word1 == cultwords["see"] && word2 == cultwords["blood"] && word3 == cultwords["hell"]) - return tomesummon() - if(word1 == cultwords["hell"] && word2 == cultwords["destroy"] && word3 == cultwords["other"]) - return armor() - if(word1 == cultwords["join"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) - return convert() - if(word1 == cultwords["hell"] && word2 == cultwords["join"] && word3 == cultwords["self"]) - return tearreality() - if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["technology"]) - return emp(src.loc,5) - if(word1 == cultwords["travel"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) - return drain() - if(word1 == cultwords["see"] && word2 == cultwords["hell"] && word3 == cultwords["join"]) - return seer() - if(word1 == cultwords["blood"] && word2 == cultwords["join"] && word3 == cultwords["hell"]) - return raise() - if(word1 == cultwords["hide"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) - return obscure(4) - if(word1 == cultwords["hell"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) - return ajourney() - if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["travel"]) - return manifest() - if(word1 == cultwords["hell"] && word2 == cultwords["technology"] && word3 == cultwords["join"]) - return talisman() - if(word1 == cultwords["hell"] && word2 == cultwords["blood"] && word3 == cultwords["join"]) - return sacrifice() - if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["hide"]) - return revealrunes(src) - if(word1 == cultwords["destroy"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) - return wall() - if(word1 == cultwords["travel"] && word2 == cultwords["technology"] && word3 == cultwords["other"]) - return freedom() - if(word1 == cultwords["join"] && word2 == cultwords["other"] && word3 == cultwords["self"]) - return cultsummon() - if(word1 == cultwords["hide"] && word2 == cultwords["other"] && word3 == cultwords["see"]) - return deafen() - if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["other"]) - return blind() - if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) - return bloodboil() - if(word1 == cultwords["self"] && word2 == cultwords["other"] && word3 == cultwords["technology"]) - return communicate() - if(word1 == cultwords["travel"] && word2 == cultwords["other"]) - return itemport(src.word3) - if(word1 == cultwords["join"] && word2 == cultwords["hide"] && word3 == cultwords["technology"]) - return runestun() - else - return fizzle() - - -/obj/effect/rune/proc/fizzle() - if(istype(src,/obj/effect/rune)) - usr.say(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) - else - usr.whisper(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) - for (var/mob/V in viewers(src)) - V.show_message("The markings pulse with a small burst of light, then fall dark.", 3, "You hear a faint fizzle.", 2) - return - -/obj/effect/rune/proc/check_icon() - icon = get_uristrune_cult(word1, word2, word3) - -/obj/item/weapon/book/tome - name = "arcane tome" - icon = 'icons/obj/weapons.dmi' - item_icons = list( - icon_l_hand = 'icons/mob/items/lefthand_books.dmi', - icon_r_hand = 'icons/mob/items/righthand_books.dmi', - ) - icon_state ="tome" - item_state = "tome" - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_SMALL - unique = 1 - var/tomedat = "" - var/list/words = list("ire" = "ire", "ego" = "ego", "nahlizet" = "nahlizet", "certum" = "certum", "veri" = "veri", "jatkaa" = "jatkaa", "balaq" = "balaq", "mgar" = "mgar", "karazet" = "karazet", "geeri" = "geeri") - - tomedat = {" - - - - -

    The scriptures of Nar-Sie, The One Who Sees, The Geometer of Blood.

    - - The book is written in an unknown dialect, there are lots of pictures of various complex geometric shapes. You find some notes in english that give you basic understanding of the many runes written in the book. The notes give you an understanding what the words for the runes should be. However, you do not know how to write all these words in this dialect.
    - Below is the summary of the runes.
    - -

    Contents

    -

    - Teleport self: Travel Self (word)
    - Teleport other: Travel Other (word)
    - Summon new tome: See Blood Hell
    - Convert a person: Join Blood Self
    - Summon Nar-Sie: Hell Join Self
    - Disable technology: Destroy See Technology
    - Drain blood: Travel Blood Self
    - Raise dead: Blood Join Hell
    - Hide runes: Hide See Blood
    - Reveal hidden runes: Blood See Hide
    - Leave your body: Hell travel self
    - Ghost Manifest: Blood See Travel
    - Imbue a talisman: Hell Technology Join
    - Sacrifice: Hell Blood Join
    - Create a wall: Destroy Travel Self
    - Summon cultist: Join Other Self
    - Free a cultist: Travel technology other
    - Deafen: Hide Other See
    - Blind: Destroy See Other
    - Blood Boil: Destroy See Blood
    - Communicate: Self Other Technology
    - Stun: Join Hide Technology
    - Summon Cultist Armor: Hell Destroy Other
    - See Invisible: See Hell Join
    -

    -

    Rune Descriptions

    -

    Teleport self

    - Teleport rune is a special rune, as it only needs two words, with the third word being destination. Basically, when you have two runes with the same destination, invoking one will teleport you to the other one. If there are more than 2 runes, you will be teleported to a random one. Runes with different third words will create separate networks. You can imbue this rune into a talisman, giving you a great escape mechanism.
    -

    Teleport other

    - Teleport other allows for teleportation for any movable object to another rune with the same third word. You need 3 cultists chanting the invocation for this rune to work.
    -

    Summon new tome

    - Invoking this rune summons a new arcane tome. -

    Convert a person

    - This rune opens target's mind to the realm of Nar-Sie, which usually results in this person joining the cult. However, some people (mostly the ones who possess high authority) have strong enough will to stay true to their old ideals.
    -

    Summon Nar-Sie

    - The ultimate rune. It summons the Avatar of Nar-Sie himself, tearing a huge hole in reality and consuming everything around it. Summoning it is the final goal of any cult.
    -

    Disable Technology

    - Invoking this rune creates a strong electromagnetic pulse in a small radius, making it basically analogic to an EMP grenade. You can imbue this rune into a talisman, making it a decent defensive item.
    -

    Drain Blood

    - This rune instantly heals you of some brute damage at the expense of a person placed on top of the rune. Whenever you invoke a drain rune, ALL drain runes on the station are activated, draining blood from anyone located on top of those runes. This includes yourself, though the blood you drain from yourself just comes back to you. This might help you identify this rune when studying words. One drain gives up to 25HP per each victim, but you can repeat it if you need more. Draining only works on living people, so you might need to recharge your "Battery" once its empty. Drinking too much blood at once might cause blood hunger.
    -

    Raise Dead

    - This rune allows for the resurrection of any dead person. You will need a dead human body and a living human sacrifice. Make 2 raise dead runes. Put a living, awake human on top of one, and a dead body on the other one. When you invoke the rune, the life force of the living human will be transferred into the dead body, allowing a ghost standing on top of the dead body to enter it, instantly and fully healing it. Use other runes to ensure there is a ghost ready to be resurrected.
    -

    Hide runes

    - This rune makes all nearby runes completely invisible. They are still there and will work if activated somehow, but you cannot invoke them directly if you do not see them.
    -

    Reveal runes

    - This rune is made to reverse the process of hiding a rune. It reveals all hidden runes in a rather large area around it. -

    Leave your body

    - This rune gently rips your soul out of your body, leaving it intact. You can observe the surroundings as a ghost as well as communicate with other ghosts. Your body takes damage while you are there, so ensure your journey is not too long, or you might never come back.
    -

    Manifest a ghost

    - Unlike the Raise Dead rune, this rune does not require any special preparations or vessels. Instead of using full lifeforce of a sacrifice, it will drain YOUR lifeforce. Stand on the rune and invoke it. If there's a ghost standing over the rune, it will materialise, and will live as long as you don't move off the rune or die. You can put a paper with a name on the rune to make the new body look like that person.
    -

    Imbue a talisman

    - This rune allows you to imbue the magic of some runes into paper talismans. Create an imbue rune, then an appropriate rune beside it. Put an empty piece of paper on the imbue rune and invoke it. You will now have a one-use talisman with the power of the target rune. Using a talisman drains some health, so be careful with it. You can imbue a talisman with power of the following runes: summon tome, reveal, conceal, teleport, tisable technology, communicate, deafen, blind and stun.
    -

    Sacrifice

    - Sacrifice rune allows you to sacrifice a living thing or a body to the Geometer of Blood. Monkeys and dead humans are the most basic sacrifices, they might or might not be enough to gain His favor. A living human is what a real sacrifice should be, however, you will need 3 people chanting the invocation to sacrifice a living person. -

    Create a wall

    - Invoking this rune solidifies the air above it, creating an an invisible wall. To remove the wall, simply invoke the rune again. -

    Summon cultist

    - This rune allows you to summon a fellow cultist to your location. The target cultist must be unhandcuffed ant not buckled to anything. You also need to have 3 people chanting at the rune to succesfully invoke it. Invoking it takes heavy strain on the bodies of all chanting cultists.
    -

    Free a cultist

    - This rune unhandcuffs and unbuckles any cultist of your choice, no matter where he is. You need to have 3 people invoking the rune for it to work. Invoking it takes heavy strain on the bodies of all chanting cultists.
    -

    Deafen

    - This rune temporarily deafens all non-cultists around you.
    -

    Blind

    - This rune temporarily blinds all non-cultists around you. Very robust. Use together with the deafen rune to leave your enemies completely helpless.
    -

    Blood boil

    - This rune boils the blood all non-cultists in visible range. The damage is enough to instantly critically hurt any person. You need 3 cultists invoking the rune for it to work. This rune is unreliable and may cause unpredicted effect when invoked. It also drains significant amount of your health when succesfully invoked.
    -

    Communicate

    - Invoking this rune allows you to relay a message to all cultists on the station and nearby space objects. -

    Stun

    - Unlike other runes, this ons is supposed to be used in talisman form. When invoked directly, it simply releases some dark energy, briefly stunning everyone around. When imbued into a talisman, you can force all of its energy into one person, stunning him so hard he cant even speak. However, effect wears off rather fast.
    -

    Equip Armor

    - When this rune is invoked, either from a rune or a talisman, it will equip the user with the armor of the followers of Nar-Sie. To use this rune to its fullest extent, make sure you are not wearing any form of headgear, armor, gloves or shoes, and make sure you are not holding anything in your hands.
    -

    See Invisible

    - When invoked when standing on it, this rune allows the user to see the the world beyond as long as he does not move.
    - - - "} - -/obj/item/weapon/book/tome/Initialize() - . = ..() - if(!cultwords["travel"]) - runerandom() - for(var/V in cultwords) - words[cultwords[V]] = V - -/obj/item/weapon/book/tome/attack(mob/living/M as mob, mob/living/user as mob) - add_attack_logs(user,M,"Hit with [name]") - - if(istype(M,/mob/observer/dead)) - var/mob/observer/dead/D = M - D.manifest(user) - return - if(!istype(M)) - return - if(!iscultist(user)) - return ..() - if(iscultist(M)) - return - M.take_organ_damage(0,rand(5,20)) //really lucky - 5 hits for a crit - for(var/mob/O in viewers(M, null)) - O.show_message("\The [user] beats \the [M] with \the [src]!", 1) - to_chat(M, "You feel searing heat inside!") - - -/obj/item/weapon/book/tome/attack_self(mob/living/user as mob) - usr = user - if(!usr.canmove || usr.stat || usr.restrained()) - return - - if(!cultwords["travel"]) - runerandom() - if(iscultist(user)) - var/C = 0 - for(var/obj/effect/rune/N in rune_list) - C++ - if (!istype(user.loc,/turf)) - to_chat(user, "You do not have enough space to write a proper rune.") - return - - if (C>=26 + runedec + cult.current_antagonists.len) //including the useless rune at the secret room, shouldn't count against the limit of 25 runes - Urist - tgui_alert_async(user, "The cloth of reality can't take that much of a strain. Remove some runes first!") - return - else - switch(tgui_alert(user, "You open the tome", "Tome", list("Read it","Scribe a rune","Cancel"))) - if("Cancel") - return - if("Read it") - if(usr.get_active_hand() != src) - return - user << browse("[tomedat]", "window=Arcane Tome") - return - if(usr.get_active_hand() != src) - return - - var/list/dictionary = list ( - "convert" = list("join","blood","self"), - "wall" = list("destroy","travel","self"), - "blood boil" = list("destroy","see","blood"), - "blood drain" = list("travel","blood","self"), - "raise dead" = list("blood","join","hell"), - "summon narsie" = list("hell","join","self"), - "communicate" = list("self","other","technology"), - "emp" = list("destroy","see","technology"), - "manifest" = list("blood","see","travel"), - "summon tome" = list("see","blood","hell"), - "see invisible" = list("see","hell","join"), - "hide" = list("hide","see","blood"), - "reveal" = list("blood","see","hide"), - "astral journey" = list("hell","travel","self"), - "imbue" = list("hell","technology","join"), - "sacrifice" = list("hell","blood","join"), - "summon cultist" = list("join","other","self"), - "free cultist" = list("travel","technology","other"), - "deafen" = list("hide","other","see"), - "blind" = list("destroy","see","other"), - "stun" = list("join","hide","technology"), - "armor" = list("hell","destroy","other"), - "teleport" = list("travel","self"), - "teleport other" = list("travel","other") - ) - - var/list/english = list() - - var/list/scribewords = list("none") - - for (var/entry in words) - if (words[entry] != entry) - english += list(words[entry] = entry) - - for (var/entry in dictionary) - var/list/required = dictionary[entry] - if (length(english&required) == required.len) - scribewords += entry - - var/chosen_rune = null - - if(usr) - chosen_rune = input ("Choose a rune to scribe.") in scribewords - if (!chosen_rune) - return - if (chosen_rune == "none") - to_chat(user, "You decide against scribing a rune, perhaps you should take this time to study your notes.") - return - if (chosen_rune == "teleport") - dictionary[chosen_rune] += input ("Choose a destination word") in english - if (chosen_rune == "teleport other") - dictionary[chosen_rune] += input ("Choose a destination word") in english - - if(usr.get_active_hand() != src) - return - - for (var/mob/V in viewers(src)) - V.show_message("\The [user] slices open a finger and begins to chant and paint symbols on the floor.", 3, "You hear chanting.", 2) - to_chat(user, "You slice open one of your fingers and begin drawing a rune on the floor whilst chanting the ritual that binds your life essence with the dark arcane energies flowing through the surrounding world.") - user.take_overall_damage((rand(9)+1)/10) // 0.1 to 1.0 damage - if(do_after(user, 50)) - var/area/A = get_area(user) - log_and_message_admins("created \an [chosen_rune] rune at \the [A.name] - [user.loc.x]-[user.loc.y]-[user.loc.z].") - if(usr.get_active_hand() != src) - return - var/mob/living/carbon/human/H = user - var/obj/effect/rune/R = new /obj/effect/rune(user.loc) - to_chat(user, "You finish drawing the arcane markings of the Geometer.") - var/list/required = dictionary[chosen_rune] - R.word1 = english[required[1]] - R.word2 = english[required[2]] - R.word3 = english[required[3]] - R.check_icon() - R.blood_DNA = list() - R.blood_DNA[H.dna.unique_enzymes] = H.dna.b_type - return - else - to_chat(user, "The book seems full of illegible scribbles. Is this a joke?") - return - -/obj/item/weapon/book/tome/examine(mob/user) - . = ..() - if(!iscultist(user)) - . += "An old, dusty tome with frayed edges and a sinister looking cover." - else - . += "The scriptures of Nar-Sie, The One Who Sees, The Geometer of Blood. Contains the details of every ritual his followers could think of. Most of these are useless, though." - -/obj/item/weapon/book/tome/cultify() - return - -/obj/item/weapon/book/tome/imbued //admin tome, spawns working runes without waiting - w_class = ITEMSIZE_SMALL - var/cultistsonly = 1 -/obj/item/weapon/book/tome/imbued/attack_self(mob/user as mob) - if(src.cultistsonly && !iscultist(usr)) - return - if(!cultwords["travel"]) - runerandom() - if(user) - var/r - if (!istype(user.loc,/turf)) - to_chat(user, "You do not have enough space to write a proper rune.") - var/list/runes = list("teleport", "itemport", "tome", "armor", "convert", "tear in reality", "emp", "drain", "seer", "raise", "obscure", "reveal", "astral journey", "manifest", "imbue talisman", "sacrifice", "wall", "freedom", "cultsummon", "deafen", "blind", "bloodboil", "communicate", "stun") - r = input(usr, "Choose a rune to scribe", "Rune Scribing") in runes // Remains input() for extreme blocking - var/obj/effect/rune/R = new /obj/effect/rune - if(istype(user, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - R.blood_DNA = list() - R.blood_DNA[H.dna.unique_enzymes] = H.dna.b_type - var/area/A = get_area(user) - log_and_message_admins("created \an [r] rune at \the [A.name] - [user.loc.x]-[user.loc.y]-[user.loc.z].") - switch(r) - if("teleport") - var/list/words = list("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") - var/beacon - if(usr) - beacon = input(usr, "Select the last rune", "Rune Scribing") in words // Remains input() for extreme blocking - R.word1=cultwords["travel"] - R.word2=cultwords["self"] - R.word3=beacon - R.loc = user.loc - R.check_icon() - if("itemport") - var/list/words = list("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") - var/beacon - if(usr) - beacon = input(usr, "Select the last rune", "Rune Scribing") in words // Remains input() for extreme blocking - R.word1=cultwords["travel"] - R.word2=cultwords["other"] - R.word3=beacon - R.loc = user.loc - R.check_icon() - if("tome") - R.word1=cultwords["see"] - R.word2=cultwords["blood"] - R.word3=cultwords["hell"] - R.loc = user.loc - R.check_icon() - if("armor") - R.word1=cultwords["hell"] - R.word2=cultwords["destroy"] - R.word3=cultwords["other"] - R.loc = user.loc - R.check_icon() - if("convert") - R.word1=cultwords["join"] - R.word2=cultwords["blood"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("tear in reality") - R.word1=cultwords["hell"] - R.word2=cultwords["join"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("emp") - R.word1=cultwords["destroy"] - R.word2=cultwords["see"] - R.word3=cultwords["technology"] - R.loc = user.loc - R.check_icon() - if("drain") - R.word1=cultwords["travel"] - R.word2=cultwords["blood"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("seer") - R.word1=cultwords["see"] - R.word2=cultwords["hell"] - R.word3=cultwords["join"] - R.loc = user.loc - R.check_icon() - if("raise") - R.word1=cultwords["blood"] - R.word2=cultwords["join"] - R.word3=cultwords["hell"] - R.loc = user.loc - R.check_icon() - if("obscure") - R.word1=cultwords["hide"] - R.word2=cultwords["see"] - R.word3=cultwords["blood"] - R.loc = user.loc - R.check_icon() - if("astral journey") - R.word1=cultwords["hell"] - R.word2=cultwords["travel"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("manifest") - R.word1=cultwords["blood"] - R.word2=cultwords["see"] - R.word3=cultwords["travel"] - R.loc = user.loc - R.check_icon() - if("imbue talisman") - R.word1=cultwords["hell"] - R.word2=cultwords["technology"] - R.word3=cultwords["join"] - R.loc = user.loc - R.check_icon() - if("sacrifice") - R.word1=cultwords["hell"] - R.word2=cultwords["blood"] - R.word3=cultwords["join"] - R.loc = user.loc - R.check_icon() - if("reveal") - R.word1=cultwords["blood"] - R.word2=cultwords["see"] - R.word3=cultwords["hide"] - R.loc = user.loc - R.check_icon() - if("wall") - R.word1=cultwords["destroy"] - R.word2=cultwords["travel"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("freedom") - R.word1=cultwords["travel"] - R.word2=cultwords["technology"] - R.word3=cultwords["other"] - R.loc = user.loc - R.check_icon() - if("cultsummon") - R.word1=cultwords["join"] - R.word2=cultwords["other"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("deafen") - R.word1=cultwords["hide"] - R.word2=cultwords["other"] - R.word3=cultwords["see"] - R.loc = user.loc - R.check_icon() - if("blind") - R.word1=cultwords["destroy"] - R.word2=cultwords["see"] - R.word3=cultwords["other"] - R.loc = user.loc - R.check_icon() - if("bloodboil") - R.word1=cultwords["destroy"] - R.word2=cultwords["see"] - R.word3=cultwords["blood"] - R.loc = user.loc - R.check_icon() - if("communicate") - R.word1=cultwords["self"] - R.word2=cultwords["other"] - R.word3=cultwords["technology"] - R.loc = user.loc - R.check_icon() - if("stun") - R.word1=cultwords["join"] - R.word2=cultwords["hide"] - R.word3=cultwords["technology"] - R.loc = user.loc - R.check_icon() +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +var/cultwords = list() +var/runedec = 0 +var/global/list/engwords = list("travel", "blood", "join", "hell", "destroy", "technology", "self", "see", "other", "hide") +var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa","mgar","balaq", "karazet", "geeri") + +/client/proc/check_words() // -- Urist + set category = "Special Verbs" + set name = "Check Rune Words" + set desc = "Check the rune-word meaning" + if(!cultwords["travel"]) + runerandom() + for (var/word in engwords) + to_chat(usr, "[cultwords[word]] is [word]") + +/proc/runerandom() //randomizes word meaning + var/list/runewords=rnwords + for (var/word in engwords) + cultwords[word] = pick(runewords) + runewords-=cultwords[word] + +/obj/effect/rune + desc = "A strange collection of symbols drawn in blood." + anchored = TRUE + icon = 'icons/obj/rune.dmi' + icon_state = "1" + var/visibility = 0 + unacidable = TRUE + layer = TURF_LAYER + + + var/word1 + var/word2 + var/word3 + var/image/blood_image + var/list/converting = list() + +// Places these combos are mentioned: this file - twice in the rune code, once in imbued tome, once in tome's HTML runes.dm - in the imbue rune code. If you change a combination - dont forget to change it everywhere. + +// travel self [word] - Teleport to random [rune with word destination matching] +// travel other [word] - Portal to rune with word destination matching - kinda doesnt work. At least the icon. No idea why. +// see blood Hell - Create a new tome +// join blood self - Incorporate person over the rune into the group +// Hell join self - Summon TERROR +// destroy see technology - EMP rune +// travel blood self - Drain blood +// see Hell join - See invisible +// blood join Hell - Raise dead + +// hide see blood - Hide nearby runes +// blood see hide - Reveal nearby runes - The point of this rune is that its reversed obscure rune. So you always know the words to reveal the rune once oyu have obscured it. + +// Hell travel self - Leave your body and ghost around +// blood see travel - Manifest a ghost into a mortal body +// Hell tech join - Imbue a rune into a talisman +// Hell blood join - Sacrifice rune +// destroy travel self - Wall rune +// join other self - Summon cultist rune +// travel technology other - Freeing rune // other blood travel was freedom join other + +// hide other see - Deafening rune // was destroy see hear +// destroy see other - Blinding rune +// destroy see blood - BLOOD BOIL + +// self other technology - Communication rune //was other hear blood +// join hide technology - stun rune. Rune color: bright pink. +/obj/effect/rune/Initialize() + . = ..() + blood_image = image(loc = src) + blood_image.override = 1 + for(var/mob/living/silicon/ai/AI in player_list) + if(AI.client) + AI.client.images += blood_image + rune_list.Add(src) + +/obj/effect/rune/Destroy() + for(var/mob/living/silicon/ai/AI in player_list) + if(AI.client) + AI.client.images -= blood_image + qdel(blood_image) + blood_image = null + rune_list.Remove(src) + ..() + +/obj/effect/rune/examine(mob/user) + . = ..() + if(iscultist(user)) + . += "This spell circle reads: [word1] [word2] [word3]." + + +/obj/effect/rune/attackby(I as obj, user as mob) + if(istype(I, /obj/item/weapon/book/tome) && iscultist(user)) + to_chat(user, "You retrace your steps, carefully undoing the lines of the rune.") + qdel(src) + return + else if(istype(I, /obj/item/weapon/nullrod)) + to_chat(user, "You disrupt the vile magic with the deadening field of the null rod!") + qdel(src) + return + return + + +/obj/effect/rune/attack_hand(mob/living/user as mob) + if(!iscultist(user)) + to_chat(user, "You can't mouth the arcane scratchings without fumbling over them.") + return + if(user.is_muzzled()) + to_chat(user, "You are unable to speak the words of the rune.") + return + if(!word1 || !word2 || !word3 || prob(user.getBrainLoss())) + return fizzle() +// if(!src.visibility) +// src.visibility=1 + if(word1 == cultwords["travel"] && word2 == cultwords["self"]) + return teleport(src.word3) + if(word1 == cultwords["see"] && word2 == cultwords["blood"] && word3 == cultwords["hell"]) + return tomesummon() + if(word1 == cultwords["hell"] && word2 == cultwords["destroy"] && word3 == cultwords["other"]) + return armor() + if(word1 == cultwords["join"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) + return convert() + if(word1 == cultwords["hell"] && word2 == cultwords["join"] && word3 == cultwords["self"]) + return tearreality() + if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["technology"]) + return emp(src.loc,5) + if(word1 == cultwords["travel"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) + return drain() + if(word1 == cultwords["see"] && word2 == cultwords["hell"] && word3 == cultwords["join"]) + return seer() + if(word1 == cultwords["blood"] && word2 == cultwords["join"] && word3 == cultwords["hell"]) + return raise() + if(word1 == cultwords["hide"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) + return obscure(4) + if(word1 == cultwords["hell"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) + return ajourney() + if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["travel"]) + return manifest() + if(word1 == cultwords["hell"] && word2 == cultwords["technology"] && word3 == cultwords["join"]) + return talisman() + if(word1 == cultwords["hell"] && word2 == cultwords["blood"] && word3 == cultwords["join"]) + return sacrifice() + if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["hide"]) + return revealrunes(src) + if(word1 == cultwords["destroy"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) + return wall() + if(word1 == cultwords["travel"] && word2 == cultwords["technology"] && word3 == cultwords["other"]) + return freedom() + if(word1 == cultwords["join"] && word2 == cultwords["other"] && word3 == cultwords["self"]) + return cultsummon() + if(word1 == cultwords["hide"] && word2 == cultwords["other"] && word3 == cultwords["see"]) + return deafen() + if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["other"]) + return blind() + if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) + return bloodboil() + if(word1 == cultwords["self"] && word2 == cultwords["other"] && word3 == cultwords["technology"]) + return communicate() + if(word1 == cultwords["travel"] && word2 == cultwords["other"]) + return itemport(src.word3) + if(word1 == cultwords["join"] && word2 == cultwords["hide"] && word3 == cultwords["technology"]) + return runestun() + else + return fizzle() + + +/obj/effect/rune/proc/fizzle() + if(istype(src,/obj/effect/rune)) + usr.say(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) + else + usr.whisper(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) + for (var/mob/V in viewers(src)) + V.show_message("The markings pulse with a small burst of light, then fall dark.", 3, "You hear a faint fizzle.", 2) + return + +/obj/effect/rune/proc/check_icon() + icon = get_uristrune_cult(word1, word2, word3) + +/obj/item/weapon/book/tome + name = "arcane tome" + icon = 'icons/obj/weapons.dmi' + item_icons = list( + icon_l_hand = 'icons/mob/items/lefthand_books.dmi', + icon_r_hand = 'icons/mob/items/righthand_books.dmi', + ) + icon_state ="tome" + item_state = "tome" + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_SMALL + unique = 1 + var/tomedat = "" + var/list/words = list("ire" = "ire", "ego" = "ego", "nahlizet" = "nahlizet", "certum" = "certum", "veri" = "veri", "jatkaa" = "jatkaa", "balaq" = "balaq", "mgar" = "mgar", "karazet" = "karazet", "geeri" = "geeri") + + tomedat = {" + + + + +

    The scriptures of Nar-Sie, The One Who Sees, The Geometer of Blood.

    + + The book is written in an unknown dialect, there are lots of pictures of various complex geometric shapes. You find some notes in english that give you basic understanding of the many runes written in the book. The notes give you an understanding what the words for the runes should be. However, you do not know how to write all these words in this dialect.
    + Below is the summary of the runes.
    + +

    Contents

    +

    + Teleport self: Travel Self (word)
    + Teleport other: Travel Other (word)
    + Summon new tome: See Blood Hell
    + Convert a person: Join Blood Self
    + Summon Nar-Sie: Hell Join Self
    + Disable technology: Destroy See Technology
    + Drain blood: Travel Blood Self
    + Raise dead: Blood Join Hell
    + Hide runes: Hide See Blood
    + Reveal hidden runes: Blood See Hide
    + Leave your body: Hell travel self
    + Ghost Manifest: Blood See Travel
    + Imbue a talisman: Hell Technology Join
    + Sacrifice: Hell Blood Join
    + Create a wall: Destroy Travel Self
    + Summon cultist: Join Other Self
    + Free a cultist: Travel technology other
    + Deafen: Hide Other See
    + Blind: Destroy See Other
    + Blood Boil: Destroy See Blood
    + Communicate: Self Other Technology
    + Stun: Join Hide Technology
    + Summon Cultist Armor: Hell Destroy Other
    + See Invisible: See Hell Join
    +

    +

    Rune Descriptions

    +

    Teleport self

    + Teleport rune is a special rune, as it only needs two words, with the third word being destination. Basically, when you have two runes with the same destination, invoking one will teleport you to the other one. If there are more than 2 runes, you will be teleported to a random one. Runes with different third words will create separate networks. You can imbue this rune into a talisman, giving you a great escape mechanism.
    +

    Teleport other

    + Teleport other allows for teleportation for any movable object to another rune with the same third word. You need 3 cultists chanting the invocation for this rune to work.
    +

    Summon new tome

    + Invoking this rune summons a new arcane tome. +

    Convert a person

    + This rune opens target's mind to the realm of Nar-Sie, which usually results in this person joining the cult. However, some people (mostly the ones who possess high authority) have strong enough will to stay true to their old ideals.
    +

    Summon Nar-Sie

    + The ultimate rune. It summons the Avatar of Nar-Sie himself, tearing a huge hole in reality and consuming everything around it. Summoning it is the final goal of any cult.
    +

    Disable Technology

    + Invoking this rune creates a strong electromagnetic pulse in a small radius, making it basically analogic to an EMP grenade. You can imbue this rune into a talisman, making it a decent defensive item.
    +

    Drain Blood

    + This rune instantly heals you of some brute damage at the expense of a person placed on top of the rune. Whenever you invoke a drain rune, ALL drain runes on the station are activated, draining blood from anyone located on top of those runes. This includes yourself, though the blood you drain from yourself just comes back to you. This might help you identify this rune when studying words. One drain gives up to 25HP per each victim, but you can repeat it if you need more. Draining only works on living people, so you might need to recharge your "Battery" once its empty. Drinking too much blood at once might cause blood hunger.
    +

    Raise Dead

    + This rune allows for the resurrection of any dead person. You will need a dead human body and a living human sacrifice. Make 2 raise dead runes. Put a living, awake human on top of one, and a dead body on the other one. When you invoke the rune, the life force of the living human will be transferred into the dead body, allowing a ghost standing on top of the dead body to enter it, instantly and fully healing it. Use other runes to ensure there is a ghost ready to be resurrected.
    +

    Hide runes

    + This rune makes all nearby runes completely invisible. They are still there and will work if activated somehow, but you cannot invoke them directly if you do not see them.
    +

    Reveal runes

    + This rune is made to reverse the process of hiding a rune. It reveals all hidden runes in a rather large area around it. +

    Leave your body

    + This rune gently rips your soul out of your body, leaving it intact. You can observe the surroundings as a ghost as well as communicate with other ghosts. Your body takes damage while you are there, so ensure your journey is not too long, or you might never come back.
    +

    Manifest a ghost

    + Unlike the Raise Dead rune, this rune does not require any special preparations or vessels. Instead of using full lifeforce of a sacrifice, it will drain YOUR lifeforce. Stand on the rune and invoke it. If there's a ghost standing over the rune, it will materialise, and will live as long as you don't move off the rune or die. You can put a paper with a name on the rune to make the new body look like that person.
    +

    Imbue a talisman

    + This rune allows you to imbue the magic of some runes into paper talismans. Create an imbue rune, then an appropriate rune beside it. Put an empty piece of paper on the imbue rune and invoke it. You will now have a one-use talisman with the power of the target rune. Using a talisman drains some health, so be careful with it. You can imbue a talisman with power of the following runes: summon tome, reveal, conceal, teleport, tisable technology, communicate, deafen, blind and stun.
    +

    Sacrifice

    + Sacrifice rune allows you to sacrifice a living thing or a body to the Geometer of Blood. Monkeys and dead humans are the most basic sacrifices, they might or might not be enough to gain His favor. A living human is what a real sacrifice should be, however, you will need 3 people chanting the invocation to sacrifice a living person. +

    Create a wall

    + Invoking this rune solidifies the air above it, creating an an invisible wall. To remove the wall, simply invoke the rune again. +

    Summon cultist

    + This rune allows you to summon a fellow cultist to your location. The target cultist must be unhandcuffed ant not buckled to anything. You also need to have 3 people chanting at the rune to succesfully invoke it. Invoking it takes heavy strain on the bodies of all chanting cultists.
    +

    Free a cultist

    + This rune unhandcuffs and unbuckles any cultist of your choice, no matter where he is. You need to have 3 people invoking the rune for it to work. Invoking it takes heavy strain on the bodies of all chanting cultists.
    +

    Deafen

    + This rune temporarily deafens all non-cultists around you.
    +

    Blind

    + This rune temporarily blinds all non-cultists around you. Very robust. Use together with the deafen rune to leave your enemies completely helpless.
    +

    Blood boil

    + This rune boils the blood all non-cultists in visible range. The damage is enough to instantly critically hurt any person. You need 3 cultists invoking the rune for it to work. This rune is unreliable and may cause unpredicted effect when invoked. It also drains significant amount of your health when succesfully invoked.
    +

    Communicate

    + Invoking this rune allows you to relay a message to all cultists on the station and nearby space objects. +

    Stun

    + Unlike other runes, this ons is supposed to be used in talisman form. When invoked directly, it simply releases some dark energy, briefly stunning everyone around. When imbued into a talisman, you can force all of its energy into one person, stunning him so hard he cant even speak. However, effect wears off rather fast.
    +

    Equip Armor

    + When this rune is invoked, either from a rune or a talisman, it will equip the user with the armor of the followers of Nar-Sie. To use this rune to its fullest extent, make sure you are not wearing any form of headgear, armor, gloves or shoes, and make sure you are not holding anything in your hands.
    +

    See Invisible

    + When invoked when standing on it, this rune allows the user to see the the world beyond as long as he does not move.
    + + + "} + +/obj/item/weapon/book/tome/Initialize() + . = ..() + if(!cultwords["travel"]) + runerandom() + for(var/V in cultwords) + words[cultwords[V]] = V + +/obj/item/weapon/book/tome/attack(mob/living/M as mob, mob/living/user as mob) + add_attack_logs(user,M,"Hit with [name]") + + if(istype(M,/mob/observer/dead)) + var/mob/observer/dead/D = M + D.manifest(user) + return + if(!istype(M)) + return + if(!iscultist(user)) + return ..() + if(iscultist(M)) + return + M.take_organ_damage(0,rand(5,20)) //really lucky - 5 hits for a crit + for(var/mob/O in viewers(M, null)) + O.show_message("\The [user] beats \the [M] with \the [src]!", 1) + to_chat(M, "You feel searing heat inside!") + + +/obj/item/weapon/book/tome/attack_self(mob/living/user as mob) + usr = user + if(!usr.canmove || usr.stat || usr.restrained()) + return + + if(!cultwords["travel"]) + runerandom() + if(iscultist(user)) + var/C = 0 + for(var/obj/effect/rune/N in rune_list) + C++ + if (!istype(user.loc,/turf)) + to_chat(user, "You do not have enough space to write a proper rune.") + return + + if (C>=26 + runedec + cult.current_antagonists.len) //including the useless rune at the secret room, shouldn't count against the limit of 25 runes - Urist + tgui_alert_async(user, "The cloth of reality can't take that much of a strain. Remove some runes first!") + return + else + switch(tgui_alert(user, "You open the tome", "Tome", list("Read it","Scribe a rune","Cancel"))) + if("Cancel") + return + if("Read it") + if(usr.get_active_hand() != src) + return + user << browse("[tomedat]", "window=Arcane Tome") + return + if(usr.get_active_hand() != src) + return + + var/list/dictionary = list ( + "convert" = list("join","blood","self"), + "wall" = list("destroy","travel","self"), + "blood boil" = list("destroy","see","blood"), + "blood drain" = list("travel","blood","self"), + "raise dead" = list("blood","join","hell"), + "summon narsie" = list("hell","join","self"), + "communicate" = list("self","other","technology"), + "emp" = list("destroy","see","technology"), + "manifest" = list("blood","see","travel"), + "summon tome" = list("see","blood","hell"), + "see invisible" = list("see","hell","join"), + "hide" = list("hide","see","blood"), + "reveal" = list("blood","see","hide"), + "astral journey" = list("hell","travel","self"), + "imbue" = list("hell","technology","join"), + "sacrifice" = list("hell","blood","join"), + "summon cultist" = list("join","other","self"), + "free cultist" = list("travel","technology","other"), + "deafen" = list("hide","other","see"), + "blind" = list("destroy","see","other"), + "stun" = list("join","hide","technology"), + "armor" = list("hell","destroy","other"), + "teleport" = list("travel","self"), + "teleport other" = list("travel","other") + ) + + var/list/english = list() + + var/list/scribewords = list("none") + + for (var/entry in words) + if (words[entry] != entry) + english += list(words[entry] = entry) + + for (var/entry in dictionary) + var/list/required = dictionary[entry] + if (length(english&required) == required.len) + scribewords += entry + + var/chosen_rune = null + + if(usr) + chosen_rune = input ("Choose a rune to scribe.") in scribewords + if (!chosen_rune) + return + if (chosen_rune == "none") + to_chat(user, "You decide against scribing a rune, perhaps you should take this time to study your notes.") + return + if (chosen_rune == "teleport") + dictionary[chosen_rune] += input ("Choose a destination word") in english + if (chosen_rune == "teleport other") + dictionary[chosen_rune] += input ("Choose a destination word") in english + + if(usr.get_active_hand() != src) + return + + for (var/mob/V in viewers(src)) + V.show_message("\The [user] slices open a finger and begins to chant and paint symbols on the floor.", 3, "You hear chanting.", 2) + to_chat(user, "You slice open one of your fingers and begin drawing a rune on the floor whilst chanting the ritual that binds your life essence with the dark arcane energies flowing through the surrounding world.") + user.take_overall_damage((rand(9)+1)/10) // 0.1 to 1.0 damage + if(do_after(user, 50)) + var/area/A = get_area(user) + log_and_message_admins("created \an [chosen_rune] rune at \the [A.name] - [user.loc.x]-[user.loc.y]-[user.loc.z].") + if(usr.get_active_hand() != src) + return + var/mob/living/carbon/human/H = user + var/obj/effect/rune/R = new /obj/effect/rune(user.loc) + to_chat(user, "You finish drawing the arcane markings of the Geometer.") + var/list/required = dictionary[chosen_rune] + R.word1 = english[required[1]] + R.word2 = english[required[2]] + R.word3 = english[required[3]] + R.check_icon() + R.blood_DNA = list() + R.blood_DNA[H.dna.unique_enzymes] = H.dna.b_type + return + else + to_chat(user, "The book seems full of illegible scribbles. Is this a joke?") + return + +/obj/item/weapon/book/tome/examine(mob/user) + . = ..() + if(!iscultist(user)) + . += "An old, dusty tome with frayed edges and a sinister looking cover." + else + . += "The scriptures of Nar-Sie, The One Who Sees, The Geometer of Blood. Contains the details of every ritual his followers could think of. Most of these are useless, though." + +/obj/item/weapon/book/tome/cultify() + return + +/obj/item/weapon/book/tome/imbued //admin tome, spawns working runes without waiting + w_class = ITEMSIZE_SMALL + var/cultistsonly = 1 +/obj/item/weapon/book/tome/imbued/attack_self(mob/user as mob) + if(src.cultistsonly && !iscultist(usr)) + return + if(!cultwords["travel"]) + runerandom() + if(user) + var/r + if (!istype(user.loc,/turf)) + to_chat(user, "You do not have enough space to write a proper rune.") + var/list/runes = list("teleport", "itemport", "tome", "armor", "convert", "tear in reality", "emp", "drain", "seer", "raise", "obscure", "reveal", "astral journey", "manifest", "imbue talisman", "sacrifice", "wall", "freedom", "cultsummon", "deafen", "blind", "bloodboil", "communicate", "stun") + r = input(usr, "Choose a rune to scribe", "Rune Scribing") in runes // Remains input() for extreme blocking + var/obj/effect/rune/R = new /obj/effect/rune + if(istype(user, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + R.blood_DNA = list() + R.blood_DNA[H.dna.unique_enzymes] = H.dna.b_type + var/area/A = get_area(user) + log_and_message_admins("created \an [r] rune at \the [A.name] - [user.loc.x]-[user.loc.y]-[user.loc.z].") + switch(r) + if("teleport") + var/list/words = list("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") + var/beacon + if(usr) + beacon = input(usr, "Select the last rune", "Rune Scribing") in words // Remains input() for extreme blocking + R.word1=cultwords["travel"] + R.word2=cultwords["self"] + R.word3=beacon + R.loc = user.loc + R.check_icon() + if("itemport") + var/list/words = list("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") + var/beacon + if(usr) + beacon = input(usr, "Select the last rune", "Rune Scribing") in words // Remains input() for extreme blocking + R.word1=cultwords["travel"] + R.word2=cultwords["other"] + R.word3=beacon + R.loc = user.loc + R.check_icon() + if("tome") + R.word1=cultwords["see"] + R.word2=cultwords["blood"] + R.word3=cultwords["hell"] + R.loc = user.loc + R.check_icon() + if("armor") + R.word1=cultwords["hell"] + R.word2=cultwords["destroy"] + R.word3=cultwords["other"] + R.loc = user.loc + R.check_icon() + if("convert") + R.word1=cultwords["join"] + R.word2=cultwords["blood"] + R.word3=cultwords["self"] + R.loc = user.loc + R.check_icon() + if("tear in reality") + R.word1=cultwords["hell"] + R.word2=cultwords["join"] + R.word3=cultwords["self"] + R.loc = user.loc + R.check_icon() + if("emp") + R.word1=cultwords["destroy"] + R.word2=cultwords["see"] + R.word3=cultwords["technology"] + R.loc = user.loc + R.check_icon() + if("drain") + R.word1=cultwords["travel"] + R.word2=cultwords["blood"] + R.word3=cultwords["self"] + R.loc = user.loc + R.check_icon() + if("seer") + R.word1=cultwords["see"] + R.word2=cultwords["hell"] + R.word3=cultwords["join"] + R.loc = user.loc + R.check_icon() + if("raise") + R.word1=cultwords["blood"] + R.word2=cultwords["join"] + R.word3=cultwords["hell"] + R.loc = user.loc + R.check_icon() + if("obscure") + R.word1=cultwords["hide"] + R.word2=cultwords["see"] + R.word3=cultwords["blood"] + R.loc = user.loc + R.check_icon() + if("astral journey") + R.word1=cultwords["hell"] + R.word2=cultwords["travel"] + R.word3=cultwords["self"] + R.loc = user.loc + R.check_icon() + if("manifest") + R.word1=cultwords["blood"] + R.word2=cultwords["see"] + R.word3=cultwords["travel"] + R.loc = user.loc + R.check_icon() + if("imbue talisman") + R.word1=cultwords["hell"] + R.word2=cultwords["technology"] + R.word3=cultwords["join"] + R.loc = user.loc + R.check_icon() + if("sacrifice") + R.word1=cultwords["hell"] + R.word2=cultwords["blood"] + R.word3=cultwords["join"] + R.loc = user.loc + R.check_icon() + if("reveal") + R.word1=cultwords["blood"] + R.word2=cultwords["see"] + R.word3=cultwords["hide"] + R.loc = user.loc + R.check_icon() + if("wall") + R.word1=cultwords["destroy"] + R.word2=cultwords["travel"] + R.word3=cultwords["self"] + R.loc = user.loc + R.check_icon() + if("freedom") + R.word1=cultwords["travel"] + R.word2=cultwords["technology"] + R.word3=cultwords["other"] + R.loc = user.loc + R.check_icon() + if("cultsummon") + R.word1=cultwords["join"] + R.word2=cultwords["other"] + R.word3=cultwords["self"] + R.loc = user.loc + R.check_icon() + if("deafen") + R.word1=cultwords["hide"] + R.word2=cultwords["other"] + R.word3=cultwords["see"] + R.loc = user.loc + R.check_icon() + if("blind") + R.word1=cultwords["destroy"] + R.word2=cultwords["see"] + R.word3=cultwords["other"] + R.loc = user.loc + R.check_icon() + if("bloodboil") + R.word1=cultwords["destroy"] + R.word2=cultwords["see"] + R.word3=cultwords["blood"] + R.loc = user.loc + R.check_icon() + if("communicate") + R.word1=cultwords["self"] + R.word2=cultwords["other"] + R.word3=cultwords["technology"] + R.loc = user.loc + R.check_icon() + if("stun") + R.word1=cultwords["join"] + R.word2=cultwords["hide"] + R.word3=cultwords["technology"] + R.loc = user.loc + R.check_icon() diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm index 02923522dc5..24a64ef1af7 100644 --- a/code/game/gamemodes/cult/talisman.dm +++ b/code/game/gamemodes/cult/talisman.dm @@ -1,118 +1,118 @@ -/obj/item/weapon/paper/talisman - icon_state = "paper_talisman" - var/imbue = null - var/uses = 0 - info = "


    " - -/obj/item/weapon/paper/talisman/attack_self(mob/living/user as mob) - if(iscultist(user)) - var/delete = 1 - // who the hell thought this was a good idea :( - switch(imbue) - if("newtome") - call(/obj/effect/rune/proc/tomesummon)() - if("armor") - call(/obj/effect/rune/proc/armor)() - if("emp") - call(/obj/effect/rune/proc/emp)(usr.loc,3) - if("conceal") - call(/obj/effect/rune/proc/obscure)(2) - if("revealrunes") - call(/obj/effect/rune/proc/revealrunes)(src) - if("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") - call(/obj/effect/rune/proc/teleport)(imbue) - if("communicate") - //If the user cancels the talisman this var will be set to 0 - delete = call(/obj/effect/rune/proc/communicate)() - if("deafen") - call(/obj/effect/rune/proc/deafen)() - if("blind") - call(/obj/effect/rune/proc/blind)() - if("runestun") - to_chat(user, "To use this talisman, attack your target directly.") - return - if("supply") - supply() - user.take_organ_damage(5, 0) - if(src && src.imbue!="supply" && src.imbue!="runestun") - if(delete) - qdel(src) - return - else - to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") - return - - -/obj/item/weapon/paper/talisman/attack(mob/living/carbon/T as mob, mob/living/user as mob) - if(iscultist(user)) - if(imbue == "runestun") - user.take_organ_damage(5, 0) - call(/obj/effect/rune/proc/runestun)(T) - qdel(src) - else - ..() ///If its some other talisman, use the generic attack code, is this supposed to work this way? - else - ..() - - -/obj/item/weapon/paper/talisman/proc/supply(var/key) - if (!src.uses) - qdel(src) - return - - var/dat = "There are [src.uses] bloody runes on the parchment.
    " - dat += "Please choose the chant to be imbued into the fabric of reality.
    " - dat += "
    " - dat += "N'ath reth sh'yro eth d'raggathnor! - Allows you to summon a new arcane tome.
    " - dat += "Sas'so c'arta forbici! - Allows you to move to a rune with the same last word.
    " - dat += "Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range.
    " - dat += "Kla'atu barada nikt'o! - Allows you to conceal the runes you placed on the floor.
    " - dat += "O bidai nabora se'sma! - Allows you to coordinate with others of your cult.
    " - dat += "Fuu ma'jin - Allows you to stun a person by attacking them with the talisman.
    " - dat += "Sa tatha najin - Allows you to summon armoured robes and an unholy blade
    " - dat += "Kal om neth - Summons a soul stone
    " - dat += "Da A'ig Osk - Summons a construct shell for use with captured souls. It is too large to carry on your person.
    " - usr << browse(dat, "window=id_com;size=350x200") - return - - -/obj/item/weapon/paper/talisman/Topic(href, href_list) - if(!src) return - if (usr.stat || usr.restrained() || !in_range(src, usr)) return - - if (href_list["rune"]) - switch(href_list["rune"]) - if("newtome") - var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) - T.imbue = "newtome" - if("teleport") - var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) - T.imbue = "[pick("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri", "orkan", "allaq")]" - T.info = "[T.imbue]" - if("emp") - var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) - T.imbue = "emp" - if("conceal") - var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) - T.imbue = "conceal" - if("communicate") - var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) - T.imbue = "communicate" - if("runestun") - var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) - T.imbue = "runestun" - if("armor") - var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) - T.imbue = "armor" - if("soulstone") - new /obj/item/device/soulstone(get_turf(usr)) - if("construct") - new /obj/structure/constructshell/cult(get_turf(usr)) - src.uses-- - supply() - return - - -/obj/item/weapon/paper/talisman/supply - imbue = "supply" - uses = 5 +/obj/item/weapon/paper/talisman + icon_state = "paper_talisman" + var/imbue = null + var/uses = 0 + info = "


    " + +/obj/item/weapon/paper/talisman/attack_self(mob/living/user as mob) + if(iscultist(user)) + var/delete = 1 + // who the hell thought this was a good idea :( + switch(imbue) + if("newtome") + call(/obj/effect/rune/proc/tomesummon)() + if("armor") + call(/obj/effect/rune/proc/armor)() + if("emp") + call(/obj/effect/rune/proc/emp)(usr.loc,3) + if("conceal") + call(/obj/effect/rune/proc/obscure)(2) + if("revealrunes") + call(/obj/effect/rune/proc/revealrunes)(src) + if("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") + call(/obj/effect/rune/proc/teleport)(imbue) + if("communicate") + //If the user cancels the talisman this var will be set to 0 + delete = call(/obj/effect/rune/proc/communicate)() + if("deafen") + call(/obj/effect/rune/proc/deafen)() + if("blind") + call(/obj/effect/rune/proc/blind)() + if("runestun") + to_chat(user, "To use this talisman, attack your target directly.") + return + if("supply") + supply() + user.take_organ_damage(5, 0) + if(src && src.imbue!="supply" && src.imbue!="runestun") + if(delete) + qdel(src) + return + else + to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") + return + + +/obj/item/weapon/paper/talisman/attack(mob/living/carbon/T as mob, mob/living/user as mob) + if(iscultist(user)) + if(imbue == "runestun") + user.take_organ_damage(5, 0) + call(/obj/effect/rune/proc/runestun)(T) + qdel(src) + else + ..() ///If its some other talisman, use the generic attack code, is this supposed to work this way? + else + ..() + + +/obj/item/weapon/paper/talisman/proc/supply(var/key) + if (!src.uses) + qdel(src) + return + + var/dat = "There are [src.uses] bloody runes on the parchment.
    " + dat += "Please choose the chant to be imbued into the fabric of reality.
    " + dat += "
    " + dat += "N'ath reth sh'yro eth d'raggathnor! - Allows you to summon a new arcane tome.
    " + dat += "Sas'so c'arta forbici! - Allows you to move to a rune with the same last word.
    " + dat += "Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range.
    " + dat += "Kla'atu barada nikt'o! - Allows you to conceal the runes you placed on the floor.
    " + dat += "O bidai nabora se'sma! - Allows you to coordinate with others of your cult.
    " + dat += "Fuu ma'jin - Allows you to stun a person by attacking them with the talisman.
    " + dat += "Sa tatha najin - Allows you to summon armoured robes and an unholy blade
    " + dat += "Kal om neth - Summons a soul stone
    " + dat += "Da A'ig Osk - Summons a construct shell for use with captured souls. It is too large to carry on your person.
    " + usr << browse(dat, "window=id_com;size=350x200") + return + + +/obj/item/weapon/paper/talisman/Topic(href, href_list) + if(!src) return + if (usr.stat || usr.restrained() || !in_range(src, usr)) return + + if (href_list["rune"]) + switch(href_list["rune"]) + if("newtome") + var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) + T.imbue = "newtome" + if("teleport") + var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) + T.imbue = "[pick("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri", "orkan", "allaq")]" + T.info = "[T.imbue]" + if("emp") + var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) + T.imbue = "emp" + if("conceal") + var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) + T.imbue = "conceal" + if("communicate") + var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) + T.imbue = "communicate" + if("runestun") + var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) + T.imbue = "runestun" + if("armor") + var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) + T.imbue = "armor" + if("soulstone") + new /obj/item/device/soulstone(get_turf(usr)) + if("construct") + new /obj/structure/constructshell/cult(get_turf(usr)) + src.uses-- + supply() + return + + +/obj/item/weapon/paper/talisman/supply + imbue = "supply" + uses = 5 diff --git a/code/game/gamemodes/endgame/endgame.dm b/code/game/gamemodes/endgame/endgame.dm index aef7894800c..721ae98f716 100644 --- a/code/game/gamemodes/endgame/endgame.dm +++ b/code/game/gamemodes/endgame/endgame.dm @@ -1,71 +1,71 @@ -/********************** - * ENDGAME STUFF - **********************/ - - // Universal State - // Handles stuff like space icon_state, constants, etc. - // Essentially a policy manager. Once shit hits the fan, this changes its policies. - // Called by master controller. - - // Default shit. -/datum/universal_state - // Just for reference, for now. - // Might eventually add an observatory job. - var/name = "Normal" - var/desc = "Nothing seems awry." - - // Sets world.turf, replaces all turfs of type /turf/space. - var/space_type = /turf/space - - // Replaces all turfs of type /turf/space/transit - var/transit_space_type = /turf/space/transit - - // Chance of a floor or wall getting damaged [0-100] - // Simulates stuff getting broken due to molecular bonds decaying. - var/decay_rate = 0 - -// Actually decay the turf. -/datum/universal_state/proc/DecayTurf(var/turf/T) - if(istype(T,/turf/simulated/wall)) - var/turf/simulated/wall/W=T - W.melt() - return - if(istype(T,/turf/simulated/floor)) - var/turf/simulated/floor/F=T - // Burnt? - if(!F.burnt) - F.burn_tile() - else - F.ReplaceWithLattice() - return - -// Return 0 to cause shuttle call to fail. -/datum/universal_state/proc/OnShuttleCall(var/mob/user) - return 1 - -// Processed per tick -/datum/universal_state/proc/OnTurfTick(var/turf/T) - if(decay_rate && prob(decay_rate)) - DecayTurf(T) - -// Apply changes when exiting state -/datum/universal_state/proc/OnExit() - // Does nothing by default - -// Apply changes when entering state -/datum/universal_state/proc/OnEnter() - // Does nothing by default - -// Apply changes to a new turf. -/datum/universal_state/proc/OnTurfChange(var/turf/NT) - return - -/datum/universal_state/proc/OverlayAndAmbientSet() - return - -/proc/SetUniversalState(var/newstate,var/on_exit=1, var/on_enter=1) - if(on_exit) - universe.OnExit() - universe = new newstate - if(on_enter) - universe.OnEnter() +/********************** + * ENDGAME STUFF + **********************/ + + // Universal State + // Handles stuff like space icon_state, constants, etc. + // Essentially a policy manager. Once shit hits the fan, this changes its policies. + // Called by master controller. + + // Default shit. +/datum/universal_state + // Just for reference, for now. + // Might eventually add an observatory job. + var/name = "Normal" + var/desc = "Nothing seems awry." + + // Sets world.turf, replaces all turfs of type /turf/space. + var/space_type = /turf/space + + // Replaces all turfs of type /turf/space/transit + var/transit_space_type = /turf/space/transit + + // Chance of a floor or wall getting damaged [0-100] + // Simulates stuff getting broken due to molecular bonds decaying. + var/decay_rate = 0 + +// Actually decay the turf. +/datum/universal_state/proc/DecayTurf(var/turf/T) + if(istype(T,/turf/simulated/wall)) + var/turf/simulated/wall/W=T + W.melt() + return + if(istype(T,/turf/simulated/floor)) + var/turf/simulated/floor/F=T + // Burnt? + if(!F.burnt) + F.burn_tile() + else + F.ReplaceWithLattice() + return + +// Return 0 to cause shuttle call to fail. +/datum/universal_state/proc/OnShuttleCall(var/mob/user) + return 1 + +// Processed per tick +/datum/universal_state/proc/OnTurfTick(var/turf/T) + if(decay_rate && prob(decay_rate)) + DecayTurf(T) + +// Apply changes when exiting state +/datum/universal_state/proc/OnExit() + // Does nothing by default + +// Apply changes when entering state +/datum/universal_state/proc/OnEnter() + // Does nothing by default + +// Apply changes to a new turf. +/datum/universal_state/proc/OnTurfChange(var/turf/NT) + return + +/datum/universal_state/proc/OverlayAndAmbientSet() + return + +/proc/SetUniversalState(var/newstate,var/on_exit=1, var/on_enter=1) + if(on_exit) + universe.OnExit() + universe = new newstate + if(on_enter) + universe.OnEnter() diff --git a/code/game/gamemodes/endgame/supermatter_cascade/blob.dm b/code/game/gamemodes/endgame/supermatter_cascade/blob.dm index af29065f96a..000cc542247 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/blob.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/blob.dm @@ -1,120 +1,120 @@ -// QUALITY COPYPASTA -/turf/unsimulated/wall/supermatter - name = "Bluespace" - desc = "THE END IS right now actually." - - icon = 'icons/turf/space.dmi' - icon_state = "bluespace" - - //luminosity = 5 - //l_color="#0066FF" - plane = PLANE_LIGHTING_ABOVE - - var/spawned=0 // DIR mask - var/next_check=0 - var/list/avail_dirs = list(NORTH,SOUTH,EAST,WEST) - -/turf/unsimulated/wall/supermatter/Initialize(mapload) - . = ..() - START_PROCESSING(SSturfs, src) - next_check = world.time+5 SECONDS - -/turf/unsimulated/wall/supermatter/Destroy() - STOP_PROCESSING(SSturfs, src) - return ..() - -/turf/unsimulated/wall/supermatter/process() - // Only check infrequently. - if(next_check>world.time) return - - // No more available directions? Shut down process(). - if(avail_dirs.len==0) - STOP_PROCESSING(SSobj, src) - return 1 - - // We're checking, reset the timer. - next_check = world.time+5 SECONDS - - // Choose a direction. - var/pdir = pick(avail_dirs) - avail_dirs -= pdir - var/turf/T=get_step(src,pdir) - - // EXPAND - if(!istype(T,type)) - // Do pretty fadeout animation for 1s. - new /obj/effect/overlay/bluespacify(T) - spawn(10) - // Nom. - for(var/atom/movable/A in T) - if(A) - if(istype(A,/mob/living)) - qdel(A) - else if(istype(A,/mob)) // Observers, AI cameras. - continue - else - qdel(A) - T.ChangeTurf(type) - - if((spawned & (NORTH|SOUTH|EAST|WEST)) == (NORTH|SOUTH|EAST|WEST)) - STOP_PROCESSING(SSturfs, src) - return - -/turf/unsimulated/wall/supermatter/attack_generic(mob/user as mob) - return attack_hand(user) - -/turf/unsimulated/wall/supermatter/attack_robot(mob/user as mob) - if(Adjacent(user)) - return attack_hand(user) - else - to_chat(user, "What the fuck are you doing?") - return - -// /vg/: Don't let ghosts fuck with this. -/turf/unsimulated/wall/supermatter/attack_ghost(mob/user as mob) - user.examinate(src) - -/turf/unsimulated/wall/supermatter/attack_ai(mob/user as mob) - return user.examinate(src) - -/turf/unsimulated/wall/supermatter/attack_hand(mob/user as mob) - user.visible_message("\The [user] reaches out and touches \the [src]... And then blinks out of existance.",\ - "You reach out and touch \the [src]. Everything immediately goes quiet. Your last thought is \"That was not a wise decision.\"",\ - "You hear an unearthly noise.") - - playsound(src, 'sound/effects/supermatter.ogg', 50, 1) - - Consume(user) - -/turf/unsimulated/wall/supermatter/attackby(obj/item/weapon/W as obj, mob/living/user as mob) - user.visible_message("\The [user] touches \a [W] to \the [src] as a silence fills the room...",\ - "You touch \the [W] to \the [src] when everything suddenly goes silent.\"\n\The [W] flashes into dust as you flinch away from \the [src].",\ - "Everything suddenly goes silent.") - - playsound(src, 'sound/effects/supermatter.ogg', 50, 1) - - user.drop_from_inventory(W) - Consume(W) - - -/turf/unsimulated/wall/supermatter/Bumped(atom/AM as mob|obj) - if(istype(AM, /mob/living)) - var/mob/living/M = AM - var/datum/gender/T = gender_datums[M.get_visible_gender()] - AM.visible_message("\The [AM] slams into \the [src] inducing a resonance... [T.his] body starts to glow and catch flame before flashing into ash.",\ - "You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\"",\ - "You hear an unearthly noise as a wave of heat washes over you.") - else - AM.visible_message("\The [AM] smacks into \the [src] and rapidly flashes to ash.",\ - "You hear a loud crack as you are washed with a wave of heat.") - - playsound(src, 'sound/effects/supermatter.ogg', 50, 1) - - Consume(AM) - - -/turf/unsimulated/wall/supermatter/proc/Consume(var/mob/living/user) - if(istype(user,/mob/observer)) - return - - qdel(user) +// QUALITY COPYPASTA +/turf/unsimulated/wall/supermatter + name = "Bluespace" + desc = "THE END IS right now actually." + + icon = 'icons/turf/space.dmi' + icon_state = "bluespace" + + //luminosity = 5 + //l_color="#0066FF" + plane = PLANE_LIGHTING_ABOVE + + var/spawned=0 // DIR mask + var/next_check=0 + var/list/avail_dirs = list(NORTH,SOUTH,EAST,WEST) + +/turf/unsimulated/wall/supermatter/Initialize(mapload) + . = ..() + START_PROCESSING(SSturfs, src) + next_check = world.time+5 SECONDS + +/turf/unsimulated/wall/supermatter/Destroy() + STOP_PROCESSING(SSturfs, src) + return ..() + +/turf/unsimulated/wall/supermatter/process() + // Only check infrequently. + if(next_check>world.time) return + + // No more available directions? Shut down process(). + if(avail_dirs.len==0) + STOP_PROCESSING(SSobj, src) + return 1 + + // We're checking, reset the timer. + next_check = world.time+5 SECONDS + + // Choose a direction. + var/pdir = pick(avail_dirs) + avail_dirs -= pdir + var/turf/T=get_step(src,pdir) + + // EXPAND + if(!istype(T,type)) + // Do pretty fadeout animation for 1s. + new /obj/effect/overlay/bluespacify(T) + spawn(10) + // Nom. + for(var/atom/movable/A in T) + if(A) + if(istype(A,/mob/living)) + qdel(A) + else if(istype(A,/mob)) // Observers, AI cameras. + continue + else + qdel(A) + T.ChangeTurf(type) + + if((spawned & (NORTH|SOUTH|EAST|WEST)) == (NORTH|SOUTH|EAST|WEST)) + STOP_PROCESSING(SSturfs, src) + return + +/turf/unsimulated/wall/supermatter/attack_generic(mob/user as mob) + return attack_hand(user) + +/turf/unsimulated/wall/supermatter/attack_robot(mob/user as mob) + if(Adjacent(user)) + return attack_hand(user) + else + to_chat(user, "What the fuck are you doing?") + return + +// /vg/: Don't let ghosts fuck with this. +/turf/unsimulated/wall/supermatter/attack_ghost(mob/user as mob) + user.examinate(src) + +/turf/unsimulated/wall/supermatter/attack_ai(mob/user as mob) + return user.examinate(src) + +/turf/unsimulated/wall/supermatter/attack_hand(mob/user as mob) + user.visible_message("\The [user] reaches out and touches \the [src]... And then blinks out of existance.",\ + "You reach out and touch \the [src]. Everything immediately goes quiet. Your last thought is \"That was not a wise decision.\"",\ + "You hear an unearthly noise.") + + playsound(src, 'sound/effects/supermatter.ogg', 50, 1) + + Consume(user) + +/turf/unsimulated/wall/supermatter/attackby(obj/item/weapon/W as obj, mob/living/user as mob) + user.visible_message("\The [user] touches \a [W] to \the [src] as a silence fills the room...",\ + "You touch \the [W] to \the [src] when everything suddenly goes silent.\"\n\The [W] flashes into dust as you flinch away from \the [src].",\ + "Everything suddenly goes silent.") + + playsound(src, 'sound/effects/supermatter.ogg', 50, 1) + + user.drop_from_inventory(W) + Consume(W) + + +/turf/unsimulated/wall/supermatter/Bumped(atom/AM as mob|obj) + if(istype(AM, /mob/living)) + var/mob/living/M = AM + var/datum/gender/T = gender_datums[M.get_visible_gender()] + AM.visible_message("\The [AM] slams into \the [src] inducing a resonance... [T.his] body starts to glow and catch flame before flashing into ash.",\ + "You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\"",\ + "You hear an unearthly noise as a wave of heat washes over you.") + else + AM.visible_message("\The [AM] smacks into \the [src] and rapidly flashes to ash.",\ + "You hear a loud crack as you are washed with a wave of heat.") + + playsound(src, 'sound/effects/supermatter.ogg', 50, 1) + + Consume(AM) + + +/turf/unsimulated/wall/supermatter/proc/Consume(var/mob/living/user) + if(istype(user,/mob/observer)) + return + + qdel(user) diff --git a/code/game/gamemodes/endgame/supermatter_cascade/portal.dm b/code/game/gamemodes/endgame/supermatter_cascade/portal.dm index a74867d9905..313d6fbd2e0 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/portal.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/portal.dm @@ -1,95 +1,95 @@ -/*** EXIT PORTAL ***/ - -/obj/singularity/narsie/large/exit - name = "Bluespace Rift" - desc = "NO TIME TO EXPLAIN, JUMP IN" - icon = 'icons/obj/rift.dmi' - icon_state = "rift" - - move_self = 0 - announce=0 - cause_hell=0 - - plane = PLANE_LIGHTING_ABOVE // ITS SO BRIGHT - - consume_range = 6 - -/obj/singularity/narsie/large/exit/New() - ..() - START_PROCESSING(SSobj, src) - -/obj/singularity/narsie/large/exit/update_icon() - overlays = 0 - -/obj/singularity/narsie/large/exit/process() - for(var/mob/M in player_list) - if(M.client) - M.see_rift(src) - eat() - -/obj/singularity/narsie/large/exit/acquire(var/mob/food) - return - -/obj/singularity/narsie/large/exit/consume(const/atom/A) - if(!(A.singuloCanEat())) - return 0 - - if (istype(A, /mob/living/)) - var/mob/living/L = A - if(L.buckled && istype(L.buckled,/obj/structure/bed/)) - var/turf/O = L.buckled - do_teleport(O, pick(endgame_safespawns), local = FALSE) //VOREStation Edit - L.loc = O.loc - else - do_teleport(L, pick(endgame_safespawns), local = FALSE) //dead-on precision //VOREStation Edit - - else if (istype(A, /obj/mecha/)) - do_teleport(A, pick(endgame_safespawns), local = FALSE) //dead-on precision //VOREStation Edit - - else if (isturf(A)) - var/turf/T = A - var/dist = get_dist(T, src) - if (dist <= consume_range && T.density) - T.density = FALSE - - for (var/atom/movable/AM in T.contents) - if (AM == src) // This is the snowflake. - continue - - if (dist <= consume_range) - consume(AM) - continue - - if (dist > consume_range) - if(!(AM.singuloCanEat())) - continue - - if (101 == AM.invisibility) - continue - - spawn (0) - AM.singularity_pull(src, src.current_size) - - -/mob - //thou shall always be able to see the rift - var/image/riftimage = null - -/mob/proc/see_rift(var/obj/singularity/narsie/large/exit/R) - var/turf/T_mob = get_turf(src) - if((R.z == T_mob.z) && (get_dist(R,T_mob) <= (R.consume_range+10)) && !(R in view(T_mob))) - if(!riftimage) - riftimage = image('icons/obj/rift.dmi',T_mob,"rift",1,1) - riftimage.plane = PLANE_LIGHTING_ABOVE - riftimage.mouse_opacity = 0 - - var/new_x = 32 * (R.x - T_mob.x) + R.pixel_x - var/new_y = 32 * (R.y - T_mob.y) + R.pixel_y - riftimage.pixel_x = new_x - riftimage.pixel_y = new_y - riftimage.loc = T_mob - - src << riftimage - else - if(riftimage) - qdel(riftimage) +/*** EXIT PORTAL ***/ + +/obj/singularity/narsie/large/exit + name = "Bluespace Rift" + desc = "NO TIME TO EXPLAIN, JUMP IN" + icon = 'icons/obj/rift.dmi' + icon_state = "rift" + + move_self = 0 + announce=0 + cause_hell=0 + + plane = PLANE_LIGHTING_ABOVE // ITS SO BRIGHT + + consume_range = 6 + +/obj/singularity/narsie/large/exit/New() + ..() + START_PROCESSING(SSobj, src) + +/obj/singularity/narsie/large/exit/update_icon() + overlays = 0 + +/obj/singularity/narsie/large/exit/process() + for(var/mob/M in player_list) + if(M.client) + M.see_rift(src) + eat() + +/obj/singularity/narsie/large/exit/acquire(var/mob/food) + return + +/obj/singularity/narsie/large/exit/consume(const/atom/A) + if(!(A.singuloCanEat())) + return 0 + + if (istype(A, /mob/living/)) + var/mob/living/L = A + if(L.buckled && istype(L.buckled,/obj/structure/bed/)) + var/turf/O = L.buckled + do_teleport(O, pick(endgame_safespawns), local = FALSE) //VOREStation Edit + L.loc = O.loc + else + do_teleport(L, pick(endgame_safespawns), local = FALSE) //dead-on precision //VOREStation Edit + + else if (istype(A, /obj/mecha/)) + do_teleport(A, pick(endgame_safespawns), local = FALSE) //dead-on precision //VOREStation Edit + + else if (isturf(A)) + var/turf/T = A + var/dist = get_dist(T, src) + if (dist <= consume_range && T.density) + T.density = FALSE + + for (var/atom/movable/AM in T.contents) + if (AM == src) // This is the snowflake. + continue + + if (dist <= consume_range) + consume(AM) + continue + + if (dist > consume_range) + if(!(AM.singuloCanEat())) + continue + + if (101 == AM.invisibility) + continue + + spawn (0) + AM.singularity_pull(src, src.current_size) + + +/mob + //thou shall always be able to see the rift + var/image/riftimage = null + +/mob/proc/see_rift(var/obj/singularity/narsie/large/exit/R) + var/turf/T_mob = get_turf(src) + if((R.z == T_mob.z) && (get_dist(R,T_mob) <= (R.consume_range+10)) && !(R in view(T_mob))) + if(!riftimage) + riftimage = image('icons/obj/rift.dmi',T_mob,"rift",1,1) + riftimage.plane = PLANE_LIGHTING_ABOVE + riftimage.mouse_opacity = 0 + + var/new_x = 32 * (R.x - T_mob.x) + R.pixel_x + var/new_y = 32 * (R.y - T_mob.y) + R.pixel_y + riftimage.pixel_x = new_x + riftimage.pixel_y = new_y + riftimage.loc = T_mob + + src << riftimage + else + if(riftimage) + qdel(riftimage) diff --git a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm index cd289689933..a217dde847b 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm @@ -1,128 +1,128 @@ -var/global/universe_has_ended = 0 - - -/datum/universal_state/supermatter_cascade - name = "Supermatter Cascade" - desc = "Unknown harmonance affecting universal substructure, converting nearby matter to supermatter." - - decay_rate = 5 // 5% chance of a turf decaying on lighting update/airflow (there's no actual tick for turfs) - -/datum/universal_state/supermatter_cascade/OnShuttleCall(var/mob/user) - if(user) - to_chat(user, "All you hear on the frequency is static and panicked screaming. There will be no shuttle call today.") - return 0 - -/datum/universal_state/supermatter_cascade/OnTurfChange(var/turf/T) - var/turf/space/S = T - if(istype(S)) - S.color = "#0066FF" - else - S.color = initial(S.color) - -/datum/universal_state/supermatter_cascade/DecayTurf(var/turf/T) - if(istype(T,/turf/simulated/wall)) - var/turf/simulated/wall/W=T - W.melt() - return - if(istype(T,/turf/simulated/floor)) - var/turf/simulated/floor/F=T - // Burnt? - if(!F.burnt) - F.burn_tile() - else - if(!istype(F,/turf/simulated/floor/plating)) - F.break_tile_to_plating() - return - -// Apply changes when entering state -/datum/universal_state/supermatter_cascade/OnEnter() - set background = 1 - to_world("You are blinded by a brilliant flash of energy.") - - world << sound('sound/effects/cascade.ogg') - - for(var/mob/M in player_list) - M.flash_eyes() - - if(emergency_shuttle.can_recall()) - priority_announcement.Announce("The emergency shuttle has returned due to bluespace distortion.") - emergency_shuttle.recall() - - AreaSet() - MiscSet() - APCSet() - OverlayAndAmbientSet() - - // Disable Nar-Sie. - cult.allow_narsie = 0 - - PlayerSet() - - new /obj/singularity/narsie/large/exit(pick(endgame_exits)) - spawn(rand(30,60) SECONDS) - var/txt = {" -There's been a galaxy-wide electromagnetic pulse. All of our systems are heavily damaged and many personnel are dead or dying. We are seeing increasing indications of the universe itself beginning to unravel. - -[station_name()], you are the only facility nearby a bluespace rift, which is near your research outpost. You are hereby directed to enter the rift using all means necessary, quite possibly as the last of your species alive. - -You have five minutes before the universe collapses. Good l\[\[###!!!- - -AUTOMATED ALERT: Link to [command_name()] lost. - -The access requirements on the Asteroid Shuttles' consoles have now been revoked. -"} - priority_announcement.Announce(txt,"SUPERMATTER CASCADE DETECTED") - - for(var/obj/machinery/computer/shuttle_control/C in machines) - if(istype(C, /obj/machinery/computer/shuttle_control/research) || istype(C, /obj/machinery/computer/shuttle_control/mining)) - C.req_access = list() - C.req_one_access = list() - - spawn(5 MINUTES) - ticker.station_explosion_cinematic(0,null) // TODO: Custom cinematic - universe_has_ended = 1 - return - -/datum/universal_state/supermatter_cascade/proc/AreaSet() - for(var/area/A in world) - if(!istype(A,/area) || istype(A, /area/space) || istype(A,/area/beach)) - continue - - A.update_icon() - -/datum/universal_state/supermatter_cascade/OverlayAndAmbientSet() - return - /* TODO - spawn(0) - for(var/datum/lighting_corner/L in world) - if(L.z in using_map.admin_levels) - L.update_lumcount(1,1,1) - else - L.update_lumcount(0.0, 0.4, 1) - - for(var/turf/space/T in world) - OnTurfChange(T) - */ -/datum/universal_state/supermatter_cascade/proc/MiscSet() - for (var/obj/machinery/firealarm/alm in machines) - if (!(alm.stat & BROKEN)) - alm.ex_act(2) - -/datum/universal_state/supermatter_cascade/proc/APCSet() - for (var/obj/machinery/power/apc/APC in GLOB.apcs) - if (!(APC.stat & BROKEN) && !APC.is_critical) - APC.chargemode = 0 - if(APC.cell) - APC.cell.charge = 0 - APC.emagged = 1 - APC.queue_icon_update() - -/datum/universal_state/supermatter_cascade/proc/PlayerSet() - for(var/datum/mind/M in player_list) - if(!istype(M.current,/mob/living)) - continue - if(M.current.stat!=2) - M.current.Weaken(10) - M.current.flash_eyes() - - clear_antag_roles(M) +var/global/universe_has_ended = 0 + + +/datum/universal_state/supermatter_cascade + name = "Supermatter Cascade" + desc = "Unknown harmonance affecting universal substructure, converting nearby matter to supermatter." + + decay_rate = 5 // 5% chance of a turf decaying on lighting update/airflow (there's no actual tick for turfs) + +/datum/universal_state/supermatter_cascade/OnShuttleCall(var/mob/user) + if(user) + to_chat(user, "All you hear on the frequency is static and panicked screaming. There will be no shuttle call today.") + return 0 + +/datum/universal_state/supermatter_cascade/OnTurfChange(var/turf/T) + var/turf/space/S = T + if(istype(S)) + S.color = "#0066FF" + else + S.color = initial(S.color) + +/datum/universal_state/supermatter_cascade/DecayTurf(var/turf/T) + if(istype(T,/turf/simulated/wall)) + var/turf/simulated/wall/W=T + W.melt() + return + if(istype(T,/turf/simulated/floor)) + var/turf/simulated/floor/F=T + // Burnt? + if(!F.burnt) + F.burn_tile() + else + if(!istype(F,/turf/simulated/floor/plating)) + F.break_tile_to_plating() + return + +// Apply changes when entering state +/datum/universal_state/supermatter_cascade/OnEnter() + set background = 1 + to_world("You are blinded by a brilliant flash of energy.") + + world << sound('sound/effects/cascade.ogg') + + for(var/mob/M in player_list) + M.flash_eyes() + + if(emergency_shuttle.can_recall()) + priority_announcement.Announce("The emergency shuttle has returned due to bluespace distortion.") + emergency_shuttle.recall() + + AreaSet() + MiscSet() + APCSet() + OverlayAndAmbientSet() + + // Disable Nar-Sie. + cult.allow_narsie = 0 + + PlayerSet() + + new /obj/singularity/narsie/large/exit(pick(endgame_exits)) + spawn(rand(30,60) SECONDS) + var/txt = {" +There's been a galaxy-wide electromagnetic pulse. All of our systems are heavily damaged and many personnel are dead or dying. We are seeing increasing indications of the universe itself beginning to unravel. + +[station_name()], you are the only facility nearby a bluespace rift, which is near your research outpost. You are hereby directed to enter the rift using all means necessary, quite possibly as the last of your species alive. + +You have five minutes before the universe collapses. Good l\[\[###!!!- + +AUTOMATED ALERT: Link to [command_name()] lost. + +The access requirements on the Asteroid Shuttles' consoles have now been revoked. +"} + priority_announcement.Announce(txt,"SUPERMATTER CASCADE DETECTED") + + for(var/obj/machinery/computer/shuttle_control/C in machines) + if(istype(C, /obj/machinery/computer/shuttle_control/research) || istype(C, /obj/machinery/computer/shuttle_control/mining)) + C.req_access = list() + C.req_one_access = list() + + spawn(5 MINUTES) + ticker.station_explosion_cinematic(0,null) // TODO: Custom cinematic + universe_has_ended = 1 + return + +/datum/universal_state/supermatter_cascade/proc/AreaSet() + for(var/area/A in world) + if(!istype(A,/area) || istype(A, /area/space) || istype(A,/area/beach)) + continue + + A.update_icon() + +/datum/universal_state/supermatter_cascade/OverlayAndAmbientSet() + return + /* TODO + spawn(0) + for(var/datum/lighting_corner/L in world) + if(L.z in using_map.admin_levels) + L.update_lumcount(1,1,1) + else + L.update_lumcount(0.0, 0.4, 1) + + for(var/turf/space/T in world) + OnTurfChange(T) + */ +/datum/universal_state/supermatter_cascade/proc/MiscSet() + for (var/obj/machinery/firealarm/alm in machines) + if (!(alm.stat & BROKEN)) + alm.ex_act(2) + +/datum/universal_state/supermatter_cascade/proc/APCSet() + for (var/obj/machinery/power/apc/APC in GLOB.apcs) + if (!(APC.stat & BROKEN) && !APC.is_critical) + APC.chargemode = 0 + if(APC.cell) + APC.cell.charge = 0 + APC.emagged = 1 + APC.queue_icon_update() + +/datum/universal_state/supermatter_cascade/proc/PlayerSet() + for(var/datum/mind/M in player_list) + if(!istype(M.current,/mob/living)) + continue + if(M.current.stat!=2) + M.current.Weaken(10) + M.current.flash_eyes() + + clear_antag_roles(M) diff --git a/code/game/gamemodes/events/PortalStorm.dm b/code/game/gamemodes/events/PortalStorm.dm index 6144f3305bb..7e63b8c533a 100644 --- a/code/game/gamemodes/events/PortalStorm.dm +++ b/code/game/gamemodes/events/PortalStorm.dm @@ -1,26 +1,26 @@ -/datum/event/portalstorm - - Announce() - command_alert("Subspace disruption detected around the vessel", "Anomaly Alert") - LongTerm() - - var/list/turfs = list( ) - var/turf/picked - - for(var/turf/T in world) - if(T.z < 5 && istype(T,/turf/simulated/floor)) - turfs += T - - for(var/turf/T in world) - if(prob(10) && T.z < 5 && istype(T,/turf/simulated/floor)) - spawn(50+rand(0,3000)) - picked = pick(turfs) - var/obj/portal/P = new /obj/portal( T ) - P.target = picked - P.creator = null - P.icon = 'icons/obj/objects.dmi' - P.failchance = 0 - P.icon_state = "anom" - P.name = "wormhole" - spawn(rand(100,150)) - qdel(P) +/datum/event/portalstorm + + Announce() + command_alert("Subspace disruption detected around the vessel", "Anomaly Alert") + LongTerm() + + var/list/turfs = list( ) + var/turf/picked + + for(var/turf/T in world) + if(T.z < 5 && istype(T,/turf/simulated/floor)) + turfs += T + + for(var/turf/T in world) + if(prob(10) && T.z < 5 && istype(T,/turf/simulated/floor)) + spawn(50+rand(0,3000)) + picked = pick(turfs) + var/obj/portal/P = new /obj/portal( T ) + P.target = picked + P.creator = null + P.icon = 'icons/obj/objects.dmi' + P.failchance = 0 + P.icon_state = "anom" + P.name = "wormhole" + spawn(rand(100,150)) + qdel(P) diff --git a/code/game/gamemodes/events/black_hole.dm b/code/game/gamemodes/events/black_hole.dm index b18310ed886..44f6df7005f 100644 --- a/code/game/gamemodes/events/black_hole.dm +++ b/code/game/gamemodes/events/black_hole.dm @@ -1,92 +1,92 @@ -/obj/effect/bhole - name = "black hole" - icon = 'icons/obj/objects.dmi' - desc = "FUCK FUCK FUCK AAAHHH" - icon_state = "bhole3" - opacity = 1 - unacidable = TRUE - density = FALSE - anchored = TRUE - -/obj/effect/bhole/New() - spawn(4) - controller() - -/obj/effect/bhole/proc/controller() - while(src) - - if(!isturf(loc)) - qdel(src) - return - - //DESTROYING STUFF AT THE EPICENTER - for(var/mob/living/M in orange(1,src)) - qdel(M) - for(var/obj/O in orange(1,src)) - qdel(O) - var/base_turf = get_base_turf_by_area(src) - for(var/turf/simulated/ST in orange(1,src)) - if(ST.type == base_turf) - continue - ST.ChangeTurf(base_turf) - - sleep(6) - grav(10, 4, 10, 0 ) - sleep(6) - grav( 8, 4, 10, 0 ) - sleep(6) - grav( 9, 4, 10, 0 ) - sleep(6) - grav( 7, 3, 40, 1 ) - sleep(6) - grav( 5, 3, 40, 1 ) - sleep(6) - grav( 6, 3, 40, 1 ) - sleep(6) - grav( 4, 2, 50, 6 ) - sleep(6) - grav( 3, 2, 50, 6 ) - sleep(6) - grav( 2, 2, 75,25 ) - sleep(6) - - - - //MOVEMENT - if( prob(50) ) - src.anchored = FALSE - step(src,pick(alldirs)) - src.anchored = TRUE - -/obj/effect/bhole/proc/grav(var/r, var/ex_act_force, var/pull_chance, var/turf_removal_chance) - if(!isturf(loc)) //blackhole cannot be contained inside anything. Weird stuff might happen - qdel(src) - return - for(var/t = -r, t < r, t++) - affect_coord(x+t, y-r, ex_act_force, pull_chance, turf_removal_chance) - affect_coord(x-t, y+r, ex_act_force, pull_chance, turf_removal_chance) - affect_coord(x+r, y+t, ex_act_force, pull_chance, turf_removal_chance) - affect_coord(x-r, y-t, ex_act_force, pull_chance, turf_removal_chance) - return - -/obj/effect/bhole/proc/affect_coord(var/x, var/y, var/ex_act_force, var/pull_chance, var/turf_removal_chance) - //Get turf at coordinate - var/turf/T = locate(x, y, z) - if(isnull(T)) return - - //Pulling and/or ex_act-ing movable atoms in that turf - if( prob(pull_chance) ) - for(var/obj/O in T.contents) - if(O.anchored) - O.ex_act(ex_act_force) - else - step_towards(O,src) - for(var/mob/living/M in T.contents) - step_towards(M,src) - - //Destroying the turf - if( T && istype(T,/turf/simulated) && prob(turf_removal_chance) ) - var/turf/simulated/ST = T - var/base_turf = get_base_turf_by_area(src) - if(ST.type != base_turf) - ST.ChangeTurf(base_turf) +/obj/effect/bhole + name = "black hole" + icon = 'icons/obj/objects.dmi' + desc = "FUCK FUCK FUCK AAAHHH" + icon_state = "bhole3" + opacity = 1 + unacidable = TRUE + density = FALSE + anchored = TRUE + +/obj/effect/bhole/New() + spawn(4) + controller() + +/obj/effect/bhole/proc/controller() + while(src) + + if(!isturf(loc)) + qdel(src) + return + + //DESTROYING STUFF AT THE EPICENTER + for(var/mob/living/M in orange(1,src)) + qdel(M) + for(var/obj/O in orange(1,src)) + qdel(O) + var/base_turf = get_base_turf_by_area(src) + for(var/turf/simulated/ST in orange(1,src)) + if(ST.type == base_turf) + continue + ST.ChangeTurf(base_turf) + + sleep(6) + grav(10, 4, 10, 0 ) + sleep(6) + grav( 8, 4, 10, 0 ) + sleep(6) + grav( 9, 4, 10, 0 ) + sleep(6) + grav( 7, 3, 40, 1 ) + sleep(6) + grav( 5, 3, 40, 1 ) + sleep(6) + grav( 6, 3, 40, 1 ) + sleep(6) + grav( 4, 2, 50, 6 ) + sleep(6) + grav( 3, 2, 50, 6 ) + sleep(6) + grav( 2, 2, 75,25 ) + sleep(6) + + + + //MOVEMENT + if( prob(50) ) + src.anchored = FALSE + step(src,pick(alldirs)) + src.anchored = TRUE + +/obj/effect/bhole/proc/grav(var/r, var/ex_act_force, var/pull_chance, var/turf_removal_chance) + if(!isturf(loc)) //blackhole cannot be contained inside anything. Weird stuff might happen + qdel(src) + return + for(var/t = -r, t < r, t++) + affect_coord(x+t, y-r, ex_act_force, pull_chance, turf_removal_chance) + affect_coord(x-t, y+r, ex_act_force, pull_chance, turf_removal_chance) + affect_coord(x+r, y+t, ex_act_force, pull_chance, turf_removal_chance) + affect_coord(x-r, y-t, ex_act_force, pull_chance, turf_removal_chance) + return + +/obj/effect/bhole/proc/affect_coord(var/x, var/y, var/ex_act_force, var/pull_chance, var/turf_removal_chance) + //Get turf at coordinate + var/turf/T = locate(x, y, z) + if(isnull(T)) return + + //Pulling and/or ex_act-ing movable atoms in that turf + if( prob(pull_chance) ) + for(var/obj/O in T.contents) + if(O.anchored) + O.ex_act(ex_act_force) + else + step_towards(O,src) + for(var/mob/living/M in T.contents) + step_towards(M,src) + + //Destroying the turf + if( T && istype(T,/turf/simulated) && prob(turf_removal_chance) ) + var/turf/simulated/ST = T + var/base_turf = get_base_turf_by_area(src) + if(ST.type != base_turf) + ST.ChangeTurf(base_turf) diff --git a/code/game/gamemodes/events/holidays/Christmas.dm b/code/game/gamemodes/events/holidays/Christmas.dm index e0991edaa2d..62a29d823c2 100644 --- a/code/game/gamemodes/events/holidays/Christmas.dm +++ b/code/game/gamemodes/events/holidays/Christmas.dm @@ -1,63 +1,63 @@ -/proc/Christmas_Game_Start() - for(var/obj/structure/flora/tree/pine/xmas in world) - if(isNotStationLevel(xmas.z)) continue - for(var/turf/simulated/floor/T in orange(1,xmas)) - for(var/i=1,i<=rand(1,5),i++) - new /obj/item/weapon/a_gift(T) - //for(var/mob/living/simple_mob/corgi/Ian/Ian in mob_list) - // Ian.place_on_head(new /obj/item/clothing/head/helmet/space/santahat(Ian)) - -/proc/ChristmasEvent() - for(var/obj/structure/flora/tree/pine/xmas in world) - var/mob/living/simple_mob/animal/space/tree/evil_tree = new /mob/living/simple_mob/animal/space/tree(xmas.loc) - evil_tree.icon_state = xmas.icon_state - evil_tree.icon_living = evil_tree.icon_state - evil_tree.icon_dead = evil_tree.icon_state - evil_tree.icon_gib = evil_tree.icon_state - qdel(xmas) - -/obj/item/weapon/toy/xmas_cracker - name = "xmas cracker" - icon = 'icons/obj/christmas.dmi' - icon_state = "cracker" - desc = "Directions for use: Requires two people, one to pull each end." - var/cracked = 0 - -/obj/item/weapon/toy/xmas_cracker/New() - ..() - -/obj/item/weapon/toy/xmas_cracker/attack(mob/target, mob/user) - if( !cracked && (istype(target,/mob/living/silicon) || (istype(target,/mob/living/carbon/human) && !target.get_active_hand())) && target.stat == CONSCIOUS) - target.visible_message("[user] and [target] pop \an [src]! *pop*", "You pull \an [src] with [target]! *pop*", "You hear a *pop*.") - var/obj/item/weapon/paper/Joke = new /obj/item/weapon/paper(user.loc) - Joke.name = "[pick("awful","terrible","unfunny")] joke" - Joke.info = pick("What did one snowman say to the other?\n\n'Is it me or can you smell carrots?'", - "Why couldn't the snowman get laid?\n\nHe was frigid!", - "Where are santa's helpers educated?\n\nNowhere, they're ELF-taught.", - "What happened to the man who stole advent calanders?\n\nHe got 25 days.", - "What does Santa get when he gets stuck in a chimney?\n\nClaus-trophobia.", - "Where do you find chili beans?\n\nThe north pole.", - "What do you get from eating tree decorations?\n\nTinsilitis!", - "What do snowmen wear on their heads?\n\nIce caps!", - "Why is Christmas just like life on ss13?\n\nYou do all the work and the fat guy gets all the credit.", - "Why doesn't Santa have any children?\n\nBecause he only comes down the chimney.") - new /obj/item/clothing/head/festive(target.loc) - user.update_icons() - cracked = 1 - icon_state = "cracker1" - var/obj/item/weapon/toy/xmas_cracker/other_half = new /obj/item/weapon/toy/xmas_cracker(target) - other_half.cracked = 1 - other_half.icon_state = "cracker2" - target.put_in_active_hand(other_half) - playsound(src, 'sound/effects/snap.ogg', 50, 1) - return 1 - return ..() - -/obj/item/clothing/head/festive - name = "festive paper hat" - icon_state = "xmashat" - desc = "A crappy paper hat that you are REQUIRED to wear." - flags_inv = 0 - body_parts_covered = 0 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - +/proc/Christmas_Game_Start() + for(var/obj/structure/flora/tree/pine/xmas in world) + if(isNotStationLevel(xmas.z)) continue + for(var/turf/simulated/floor/T in orange(1,xmas)) + for(var/i=1,i<=rand(1,5),i++) + new /obj/item/weapon/a_gift(T) + //for(var/mob/living/simple_mob/corgi/Ian/Ian in mob_list) + // Ian.place_on_head(new /obj/item/clothing/head/helmet/space/santahat(Ian)) + +/proc/ChristmasEvent() + for(var/obj/structure/flora/tree/pine/xmas in world) + var/mob/living/simple_mob/animal/space/tree/evil_tree = new /mob/living/simple_mob/animal/space/tree(xmas.loc) + evil_tree.icon_state = xmas.icon_state + evil_tree.icon_living = evil_tree.icon_state + evil_tree.icon_dead = evil_tree.icon_state + evil_tree.icon_gib = evil_tree.icon_state + qdel(xmas) + +/obj/item/weapon/toy/xmas_cracker + name = "xmas cracker" + icon = 'icons/obj/christmas.dmi' + icon_state = "cracker" + desc = "Directions for use: Requires two people, one to pull each end." + var/cracked = 0 + +/obj/item/weapon/toy/xmas_cracker/New() + ..() + +/obj/item/weapon/toy/xmas_cracker/attack(mob/target, mob/user) + if( !cracked && (istype(target,/mob/living/silicon) || (istype(target,/mob/living/carbon/human) && !target.get_active_hand())) && target.stat == CONSCIOUS) + target.visible_message("[user] and [target] pop \an [src]! *pop*", "You pull \an [src] with [target]! *pop*", "You hear a *pop*.") + var/obj/item/weapon/paper/Joke = new /obj/item/weapon/paper(user.loc) + Joke.name = "[pick("awful","terrible","unfunny")] joke" + Joke.info = pick("What did one snowman say to the other?\n\n'Is it me or can you smell carrots?'", + "Why couldn't the snowman get laid?\n\nHe was frigid!", + "Where are santa's helpers educated?\n\nNowhere, they're ELF-taught.", + "What happened to the man who stole advent calanders?\n\nHe got 25 days.", + "What does Santa get when he gets stuck in a chimney?\n\nClaus-trophobia.", + "Where do you find chili beans?\n\nThe north pole.", + "What do you get from eating tree decorations?\n\nTinsilitis!", + "What do snowmen wear on their heads?\n\nIce caps!", + "Why is Christmas just like life on ss13?\n\nYou do all the work and the fat guy gets all the credit.", + "Why doesn't Santa have any children?\n\nBecause he only comes down the chimney.") + new /obj/item/clothing/head/festive(target.loc) + user.update_icons() + cracked = 1 + icon_state = "cracker1" + var/obj/item/weapon/toy/xmas_cracker/other_half = new /obj/item/weapon/toy/xmas_cracker(target) + other_half.cracked = 1 + other_half.icon_state = "cracker2" + target.put_in_active_hand(other_half) + playsound(src, 'sound/effects/snap.ogg', 50, 1) + return 1 + return ..() + +/obj/item/clothing/head/festive + name = "festive paper hat" + icon_state = "xmashat" + desc = "A crappy paper hat that you are REQUIRED to wear." + flags_inv = 0 + body_parts_covered = 0 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + diff --git a/code/game/gamemodes/events/holidays/Holidays.dm b/code/game/gamemodes/events/holidays/Holidays.dm index 35d67ccd572..b274d8a0fad 100644 --- a/code/game/gamemodes/events/holidays/Holidays.dm +++ b/code/game/gamemodes/events/holidays/Holidays.dm @@ -1,311 +1,311 @@ -//Uncommenting ALLOW_HOLIDAYS in config.txt will enable Holidays -var/global/list/Holiday = list() //Holidays are lists now, so we can have more than one holiday at the same time (hey, you never know). - -//Just thinking ahead! Here's the foundations to a more robust Holiday event system. -//It's easy as hell to add stuff. Just set Holiday to something using the switch (or something else) -//then use if(Holiday == "MyHoliday") to make stuff happen on that specific day only -//Please, Don't spam stuff up with easter eggs, I'd rather somebody just delete this than people cause -//the game to lag even more in the name of one-day content. - -////////////////////////////////////////////////////////////////////////////////////////////////////////// -//ALSO, MOST IMPORTANTLY: Don't add stupid stuff! Discuss bonus content with Project-Heads first please!// -////////////////////////////////////////////////////////////////////////////////////////////////////////// -// ~Carn - -/hook/startup/proc/updateHoliday() - Get_Holiday() - return 1 - -//sets up the Holiday global variable. Shouldbe called on game configuration or something. -/proc/Get_Holiday() - if(!Holiday) return // Holiday stuff was not enabled in the config! - - Holiday = list() // reset our switch now so we can recycle it as our Holiday name - - //var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year - unused currently but can be used for floating dates - var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month - var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day - - //Main switch. If any of these are too dumb/inappropriate, or you have better ones, feel free to change whatever - //Holidays are now associate lists. You should write holidays like this. - //Holiday["Holiday Name Here"] = "Blurb about the holiday here." - switch(MM) - if(1) //Jan - switch(DD) - if(1) - Holiday["New Years's Day"] = "The day of the new solar year on Sol." - if(12) - Holiday["Vertalliq-Qerr"] = "Vertalliq-Qerr, translated to mean 'Festival of the Royals', is a \ - skrellian holiday that celebrates the Qerr-Katish and all they have provided for the rest of skrellian society, \ - it often features colourful displays and skilled performers take this time to show off some of their more \ - elaborate displays." - if(14) - Holiday["Lohri"] = "A human festival traditionally celebrating the end of winter on the Indian subcontinent. \ - The holiday is now celebrated independently of seasons in many colonies with large populations of Indian \ - descent. Traditions include the burning of bonfires, dancing, and door-to-door singing in exchange for treats." - if(30) - Holiday["Lunar New Year"] = "Originally the new year on the ancient lunisolar calendar, the Lunar New Year is \ - celebrated with a wide variety of east Asian traditions with roots in Chinese, Japanese, Korean, Vietnamese, \ - Tibetan, Mongolian, and Ryukyu cultures. Elaborate parades, performances, dances and meals are usual staples." - - if(2) //Feb - switch(DD) - if(2) - Holiday["Groundhog Day"] = "An unoffical holiday based on medieval folklore that originated on Earth, \ - that involves the reverence of a prophetic animal - traditionally a badger, fox or groundhog - that was \ - said to be able to predict, or even control the changing of the seasons." - if(14) - Holiday["Valentine's Day"] = "A human holiday that revolves around expressions of romance and love. \ - In particular, the exchanging of gifts, letters and cards is traditional." - if(15) - Holiday["Lantern Festival"] = "A human holiday with origins in Chinese new year celebrations. Participants \ - carry or hang elaborate paper lanterns that are thought to bring good luck. Today, electric lights are often used \ - in environments where open flames would be hazardous or non-functional." - if(17) - Holiday["Random Acts of Kindness Day"] = "An unoffical holiday that challenges everyone to perform \ - acts of kindness to their friends, co-workers, and strangers, with no strings attached." - - if(3) //Mar - switch(DD) - if(3) - Holiday["Qixm-tes"] = "Qixm-tes, or 'Day of mourning', is a skrellian holiday where skrell gather at places \ - of worship and sing a song of mourning for all those who have died in service to their kingdoms." - if(14) - Holiday["Pi Day"] = "An unoffical holiday celebrating the mathematical constant Pi. It is celebrated on \ - March 14th, as the digits form 3 14, the first three significant digits of Pi. Observance of Pi Day generally \ - involve eating (or throwing) pie, due to a pun. Pies also tend to be round, and thus relatable to Pi." - if(17) - Holiday["St. Patrick's Day"] = "A holiday originating on Earth, celebrating a popular version of Irish culture. \ - Traditions include elaborate parades, wearing of the colour green, and drinking alcohol." - if(18) - Holiday["Holi"] = "Also known as the Festival of Colours, a human Hindu festival celebrating divine love and the \ - triumph of good over evil. Traditionally a bonfire is lit overnight, followed by the free-for-all smearing of \ - celebrants with colourful pigments, and the forgiveness of past wrongs." - if(27) - Holiday["Easter"] = "A Earth springtime festival variously celebrating rebirth and the beginning of the planting \ - season. Traditionally celebrated with the painting and exchange of eggs, sometimes made from chocolate. \ - The holiday's date was standardized in the 22nd century." - - if(4) //Apr - switch(DD) - if(1) - Holiday["April Fool's Day"] = "A human holiday that endevours one to pull pranks and spread hoaxes on their friends." - if(5) - Holiday["First Day of Passover"] = "The first of eight days of a human holiday celebrating the exodus of ancient Jewish people \ - from slavery, and of the spring harvest. The most well-known tradition is the Sedar meal. The date was standardized in the 22nd century." - if(22) - Holiday["Earth Day"] = "A holiday of enviromentalism, that originated on it's namesake, Earth." - - if(5) //May - switch(DD) - if(1) - Holiday["Interstellar Workers' Day"] = "This holiday celebrates the work of laborers and the working class." - if(18) - Holiday["Remembrance Day"] = "Remembrance Day (or, as it is more informally known, Armistice Day) is a confederation-wide holiday \ - mostly observed by its member states since late 2280. Officially, it is a day of remembering the men and women who died in various armed conflicts \ - throughout human history. Unofficially, however, it is commonly treated as a holiday honoring the victims of the Human-Unathi war. \ - Observance of this day varies throughout human space, but most common traditions are the act of bringing flowers to graves,\ - attending parades, and the wearing of poppies (either paper or real) in one's clothing." - if(28) - Holiday["Jiql-tes"] = "A skrellian holiday that translates to 'Day of Celebration', skrell communities \ - gather for a grand feast and give gifts to friends and close relatives." - - if(6) //Jun - switch(DD) - if(6) - Holiday["Sapient Rights Day"] = "This holiday celebrates the passing of the Declaration of Sapient Rights by SolGov, which guarantees the \ - same protections humans are granted to all sapient, living species." - if(14) - Holiday["Blood Donor Day"] = "This holiday was created to raise awareness of the need for safe blood and blood products, \ - and to thank blood donors for their voluntary, life-saving gifts of blood." - if(20) - Holiday["Civil Servant's Day"] = "Civil Servant's Day is a holiday observed in SCG member states that honors civil servants everywhere,\ - (especially those who are members of the armed forces and the emergency services), or have been or have been civil servants in the past." -/* if(25) - Holiday["Merhyat Njarha"] = "A Njarir'Akhan tajaran tradition translating to \"Harmony of the House\", in which Njarjirii citizens pay \ - homage to their ruling house and their ancestors. Traditions include large communal meals and dances hosted by the ruling house, \ - and the intensive upkeep of community spaces."*/ - - if(7) //Jul - switch(DD) - if(1) - Holiday["Doctors' Day"] = "A holiday that recognizes the services of physicians, commonly celebrated \ - in healthcare organizations and facilities." - if(30) - Holiday["Friendship Day"] = "An unoffical holiday that recognizes the value of friends and companionship. Indeed, not having someone watch \ - your back while in space can be dangerous, and the cold, isolating nature of space makes friends all the more important." - - if(8) //Aug - switch(DD) -//VOREStation Add - Of course we need this. - if(8) - Holiday["Vore Day"] = "A holiday representing the innate desire in all/most/some/a few of us to devour each other or be devoured. \ - That's probably why you're here, isn't it? Get to it, then!" -//VOREStation Add End. -/* if(10) - Holiday["S'randarr's Day"] = "A Tajaran holiday that occurs on the longest day of the year in summer, - on Ahdomai. It is named after the Tajaran deity of Light, and huge celebrations are common." - if(11) - Holiday["Tajaran Contact Day"] = "The anniversary of first contact between SolGov and the tajaran species, widely observed\ - throughout tajaran and human space. Marks the date that in 2513, a human exploration team investigating electromagnetic \ - emissions from the Meralar system made radio contact with the tajaran scientific outpost that had broadcast them."*/ - if(20) - Holiday["Obon"] = "An ancient Earth holiday originating in east Asia, for the honouring of one's ancestral spirits. \ - Traditions include the maintenance of grave sites and memorials, and community traditional dance performances." - if(27) - Holiday["Forgiveness Day"] = "A time to forgive and be forgiven." - - if(9) //Sep - switch(DD) - if(17) - Holiday["Qill-xamr"] = "Translated to 'Night of the dead', it is a skrellian holiday where skrell \ - communities hold parties in order to remember loved ones who passed, unlike Qixm-tes, this applies to everyone \ - and is a joyful celebration." - if(19) - Holiday["Talk-Like-a-Pirate Day"] = "Ahoy, matey! It be the unoffical holiday celebratin' the salty \ - sea humor of speakin' like the pirates of old." - if(20) - Holiday["Rosh Hashanah"] = "An old human holiday that marks the traditional Hebrew new year." - if(28) - Holiday["Stupid-Questions Day"] = "Known as Ask A Stupid Question Day, it is an unoffical holiday \ - created by teachers in Sol, very long ago, to encourage students to ask more questions in the classroom." - - if(10) //Oct - switch(DD) - if(9) - Holiday["Lief Eriksson Day"] = "A day commemorating Norse explorer Lief Eriksson, an early Scandinavian cultural figure \ - who is thought to have been the first European to set foot in North America." - if(16) - Holiday["Boss' Day"] = "Boss' Day has traditionally been a day for employees to thank their bosses for the difficult work that they do \ - throughout the year. This day was created for the purpose of strengthening the bond between employer and employee." - if(21) - Holiday["First Day of Diwali"] = "An ancient Hindu, Jain and Sikh festival lasting five days, celebrating victory of light over darkness, good over \ - evil, and knowledge over ignorance. It is celebrated by the wearing of your finest clothes, decorating with oil lamps and rangolis, \ - fireworks, and gift-giving. Electric lights are often used in modern times where oil lamps would be hazardous or inoperable." - if(31) - Holiday["Halloween"] = "Originating from Earth, Halloween is also known as All Saints' Eve, and \ - is celebrated by some by attending costume parties, trick-or-treating, carving faces in pumpkins, or visiting \ - 'haunted' locations. Some people make it a goal to scare other people." - - if(11) //Nov - switch(DD) - if(1) - Holiday["Day of the Dead"] = "An old human holiday celebrating the lives of deceased friends and family members, \ - by means of good humour and joyful parties. Offerings are often left at altars to the dead, and exchanging gifts \ - among the living is not uncommon." - if(13) - Holiday["Kindness Day"] = "Kindness Day is an unofficial holiday to highlight good deeds in the \ - community, focusing on the positive power and the common thread of kindness which binds humanity and \ - friends together." - if(28) //Space thanksgiving. - Holiday["Appreciation Day"] = "Originally an old holiday from Earth, Appreciation Day follows many of the \ - traditions that its predecessor did, such as having a large feast (turkey often included), gathering with family, and being thankful \ - for what one has in life." - if(28 > DD > 20) - if(time2text(world.timeofday, "Day") == "Thursday") - Holiday["Thanksgiving"] = "Originally an old holiday from Earth, Thanksgiving follows many of the \ - traditions that its predecessor did, such as having a large feast (turkey often included), gathering with family, and being thankful \ - for what one has in life." - - if(12) //Dec - switch(DD) - if(10) - Holiday["Human-Rights Day"] = "An old holiday created by an intergovernmental organization known back than as the United Nations, \ - human rights were not recognized globally at the time, and the holiday was made in honor of the Universal Declaration of Human Rights. \ - These days, SolGov ensures that past efforts were not in vein, and continues to honor this holiday across the galaxy as a historical \ - reminder." - if(22) - Holiday["Vertalliq-qixim"] = "A skrellian holiday that celebrates the skrell's first landing on one of \ - their moons. It's often celebrated with grand festivals." - if(24) - Holiday["Christmas Eve"] = "The eve of Christmas, an old holiday from Earth that mainly involves gift \ - giving, decorating, family reunions, and a fat red human breaking into people's homes to steal milk and cookies." - if(25) - Holiday["Christmas"] = "Christmas is a very old holiday that originated in Earth, Sol. It was a \ - religious holiday for the Christian religion, which would later form Unitarianism. Nowadays, the holiday is celebrated \ - generally by giving gifts, symbolic decoration, and reuniting with one's family. It also features a mythical fat \ - red human, known as Santa, who broke into people's homes to loot cookies and milk." - if(31) - Holiday["New Year's Eve"] = "The eve of the New Year for Sol. It is traditionally celebrated by counting down to midnight, as that is \ - when the new year begins. Other activities include planning for self-improvement over the new year, attending New Year's parties, or \ - watching a timer count to zero, a large object descending, and fireworks exploding in the sky, in person or on broadcast." - - if(!Holiday) - //Friday the 13th - if(DD == 13) - if(time2text(world.timeofday, "DDD") == "Fri") - Holiday["Friday the 13th"] = "Friday the 13th is a superstitious day, associated with bad luck and misfortune." - -//Allows GA and GM to set the Holiday variable -/client/proc/Set_Holiday() - set name = ".Set Holiday" - set category = "Fun" - set desc = "Force-set the Holiday variable to make the game think it's a certain day." - if(!check_rights(R_SERVER)) return - - Holiday = list() - - var/H = tgui_input_text(src,"What holiday is it today?","Set Holiday") - var/B = tgui_input_text(src,"Now explain what the holiday is about","Set Holiday", multiline = TRUE, prevent_enter = TRUE) - - - Holiday[H] = B - - //update our hub status - world.update_status() - Holiday_Game_Start() - - message_admins("ADMIN: Event: [key_name(src)] force-set Holiday to \"[Holiday]\"") - log_admin("[key_name(src)] force-set Holiday to \"[Holiday]\"") - - -//Run at the start of a round -/proc/Holiday_Game_Start() - if(Holiday.len != 0) - var/list/holidays = list() - var/list/holiday_blurbs = list() - for(var/p in Holiday) - holidays.Add(p) - holiday_blurbs.Add("[Holiday[p]]") - var/holidays_string = english_list(holidays, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) - to_world(span_blue("and...")) - to_world("

    Happy [holidays_string] Everybody!

    ") - if(holiday_blurbs.len != 0) - for(var/blurb in holiday_blurbs) - to_world(span_blue("
    [blurb]
    ")) - switch(Holiday) //special holidays - if("Easter") - //do easter stuff - if("Christmas Eve","Christmas") - Christmas_Game_Start() - - return - -//Nested in the random events loop. Will be triggered every 2 minutes -/proc/Holiday_Random_Event() - if(isemptylist(Holiday)) - return 0 - switch(Holiday) //special holidays - if("Easter") //I'll make this into some helper procs at some point -/* var/list/turf/simulated/floor/Floorlist = list() - for(var/turf/simulated/floor/T) - if(T.contents) - Floorlist += T - var/turf/simulated/floor/F = Floorlist[rand(1,Floorlist.len)] - Floorlist = null - var/obj/structure/closet/C = locate(/obj/structure/closet) in F - var/obj/item/weapon/reagent_containers/food/snacks/chocolateegg/wrapped/Egg - if( C ) Egg = new(C) - else Egg = new(F) -*/ -/* var/list/obj/containers = list() - for(var/obj/item/weapon/storage/S in world) - if(isNotStationLevel(S.z)) continue - containers += S - - message_admins("DEBUG: Event: Egg spawned at [Egg.loc] ([Egg.x],[Egg.y],[Egg.z])")*/ - if("End of the World") - if(prob(eventchance)) GameOver() - - if("Christmas","Christmas Eve") - if(prob(eventchance)) ChristmasEvent() +//Uncommenting ALLOW_HOLIDAYS in config.txt will enable Holidays +var/global/list/Holiday = list() //Holidays are lists now, so we can have more than one holiday at the same time (hey, you never know). + +//Just thinking ahead! Here's the foundations to a more robust Holiday event system. +//It's easy as hell to add stuff. Just set Holiday to something using the switch (or something else) +//then use if(Holiday == "MyHoliday") to make stuff happen on that specific day only +//Please, Don't spam stuff up with easter eggs, I'd rather somebody just delete this than people cause +//the game to lag even more in the name of one-day content. + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +//ALSO, MOST IMPORTANTLY: Don't add stupid stuff! Discuss bonus content with Project-Heads first please!// +////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ~Carn + +/hook/startup/proc/updateHoliday() + Get_Holiday() + return 1 + +//sets up the Holiday global variable. Shouldbe called on game configuration or something. +/proc/Get_Holiday() + if(!Holiday) return // Holiday stuff was not enabled in the config! + + Holiday = list() // reset our switch now so we can recycle it as our Holiday name + + //var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year - unused currently but can be used for floating dates + var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month + var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day + + //Main switch. If any of these are too dumb/inappropriate, or you have better ones, feel free to change whatever + //Holidays are now associate lists. You should write holidays like this. + //Holiday["Holiday Name Here"] = "Blurb about the holiday here." + switch(MM) + if(1) //Jan + switch(DD) + if(1) + Holiday["New Years's Day"] = "The day of the new solar year on Sol." + if(12) + Holiday["Vertalliq-Qerr"] = "Vertalliq-Qerr, translated to mean 'Festival of the Royals', is a \ + skrellian holiday that celebrates the Qerr-Katish and all they have provided for the rest of skrellian society, \ + it often features colourful displays and skilled performers take this time to show off some of their more \ + elaborate displays." + if(14) + Holiday["Lohri"] = "A human festival traditionally celebrating the end of winter on the Indian subcontinent. \ + The holiday is now celebrated independently of seasons in many colonies with large populations of Indian \ + descent. Traditions include the burning of bonfires, dancing, and door-to-door singing in exchange for treats." + if(30) + Holiday["Lunar New Year"] = "Originally the new year on the ancient lunisolar calendar, the Lunar New Year is \ + celebrated with a wide variety of east Asian traditions with roots in Chinese, Japanese, Korean, Vietnamese, \ + Tibetan, Mongolian, and Ryukyu cultures. Elaborate parades, performances, dances and meals are usual staples." + + if(2) //Feb + switch(DD) + if(2) + Holiday["Groundhog Day"] = "An unoffical holiday based on medieval folklore that originated on Earth, \ + that involves the reverence of a prophetic animal - traditionally a badger, fox or groundhog - that was \ + said to be able to predict, or even control the changing of the seasons." + if(14) + Holiday["Valentine's Day"] = "A human holiday that revolves around expressions of romance and love. \ + In particular, the exchanging of gifts, letters and cards is traditional." + if(15) + Holiday["Lantern Festival"] = "A human holiday with origins in Chinese new year celebrations. Participants \ + carry or hang elaborate paper lanterns that are thought to bring good luck. Today, electric lights are often used \ + in environments where open flames would be hazardous or non-functional." + if(17) + Holiday["Random Acts of Kindness Day"] = "An unoffical holiday that challenges everyone to perform \ + acts of kindness to their friends, co-workers, and strangers, with no strings attached." + + if(3) //Mar + switch(DD) + if(3) + Holiday["Qixm-tes"] = "Qixm-tes, or 'Day of mourning', is a skrellian holiday where skrell gather at places \ + of worship and sing a song of mourning for all those who have died in service to their kingdoms." + if(14) + Holiday["Pi Day"] = "An unoffical holiday celebrating the mathematical constant Pi. It is celebrated on \ + March 14th, as the digits form 3 14, the first three significant digits of Pi. Observance of Pi Day generally \ + involve eating (or throwing) pie, due to a pun. Pies also tend to be round, and thus relatable to Pi." + if(17) + Holiday["St. Patrick's Day"] = "A holiday originating on Earth, celebrating a popular version of Irish culture. \ + Traditions include elaborate parades, wearing of the colour green, and drinking alcohol." + if(18) + Holiday["Holi"] = "Also known as the Festival of Colours, a human Hindu festival celebrating divine love and the \ + triumph of good over evil. Traditionally a bonfire is lit overnight, followed by the free-for-all smearing of \ + celebrants with colourful pigments, and the forgiveness of past wrongs." + if(27) + Holiday["Easter"] = "A Earth springtime festival variously celebrating rebirth and the beginning of the planting \ + season. Traditionally celebrated with the painting and exchange of eggs, sometimes made from chocolate. \ + The holiday's date was standardized in the 22nd century." + + if(4) //Apr + switch(DD) + if(1) + Holiday["April Fool's Day"] = "A human holiday that endevours one to pull pranks and spread hoaxes on their friends." + if(5) + Holiday["First Day of Passover"] = "The first of eight days of a human holiday celebrating the exodus of ancient Jewish people \ + from slavery, and of the spring harvest. The most well-known tradition is the Sedar meal. The date was standardized in the 22nd century." + if(22) + Holiday["Earth Day"] = "A holiday of enviromentalism, that originated on it's namesake, Earth." + + if(5) //May + switch(DD) + if(1) + Holiday["Interstellar Workers' Day"] = "This holiday celebrates the work of laborers and the working class." + if(18) + Holiday["Remembrance Day"] = "Remembrance Day (or, as it is more informally known, Armistice Day) is a confederation-wide holiday \ + mostly observed by its member states since late 2280. Officially, it is a day of remembering the men and women who died in various armed conflicts \ + throughout human history. Unofficially, however, it is commonly treated as a holiday honoring the victims of the Human-Unathi war. \ + Observance of this day varies throughout human space, but most common traditions are the act of bringing flowers to graves,\ + attending parades, and the wearing of poppies (either paper or real) in one's clothing." + if(28) + Holiday["Jiql-tes"] = "A skrellian holiday that translates to 'Day of Celebration', skrell communities \ + gather for a grand feast and give gifts to friends and close relatives." + + if(6) //Jun + switch(DD) + if(6) + Holiday["Sapient Rights Day"] = "This holiday celebrates the passing of the Declaration of Sapient Rights by SolGov, which guarantees the \ + same protections humans are granted to all sapient, living species." + if(14) + Holiday["Blood Donor Day"] = "This holiday was created to raise awareness of the need for safe blood and blood products, \ + and to thank blood donors for their voluntary, life-saving gifts of blood." + if(20) + Holiday["Civil Servant's Day"] = "Civil Servant's Day is a holiday observed in SCG member states that honors civil servants everywhere,\ + (especially those who are members of the armed forces and the emergency services), or have been or have been civil servants in the past." +/* if(25) + Holiday["Merhyat Njarha"] = "A Njarir'Akhan tajaran tradition translating to \"Harmony of the House\", in which Njarjirii citizens pay \ + homage to their ruling house and their ancestors. Traditions include large communal meals and dances hosted by the ruling house, \ + and the intensive upkeep of community spaces."*/ + + if(7) //Jul + switch(DD) + if(1) + Holiday["Doctors' Day"] = "A holiday that recognizes the services of physicians, commonly celebrated \ + in healthcare organizations and facilities." + if(30) + Holiday["Friendship Day"] = "An unoffical holiday that recognizes the value of friends and companionship. Indeed, not having someone watch \ + your back while in space can be dangerous, and the cold, isolating nature of space makes friends all the more important." + + if(8) //Aug + switch(DD) +//VOREStation Add - Of course we need this. + if(8) + Holiday["Vore Day"] = "A holiday representing the innate desire in all/most/some/a few of us to devour each other or be devoured. \ + That's probably why you're here, isn't it? Get to it, then!" +//VOREStation Add End. +/* if(10) + Holiday["S'randarr's Day"] = "A Tajaran holiday that occurs on the longest day of the year in summer, + on Ahdomai. It is named after the Tajaran deity of Light, and huge celebrations are common." + if(11) + Holiday["Tajaran Contact Day"] = "The anniversary of first contact between SolGov and the tajaran species, widely observed\ + throughout tajaran and human space. Marks the date that in 2513, a human exploration team investigating electromagnetic \ + emissions from the Meralar system made radio contact with the tajaran scientific outpost that had broadcast them."*/ + if(20) + Holiday["Obon"] = "An ancient Earth holiday originating in east Asia, for the honouring of one's ancestral spirits. \ + Traditions include the maintenance of grave sites and memorials, and community traditional dance performances." + if(27) + Holiday["Forgiveness Day"] = "A time to forgive and be forgiven." + + if(9) //Sep + switch(DD) + if(17) + Holiday["Qill-xamr"] = "Translated to 'Night of the dead', it is a skrellian holiday where skrell \ + communities hold parties in order to remember loved ones who passed, unlike Qixm-tes, this applies to everyone \ + and is a joyful celebration." + if(19) + Holiday["Talk-Like-a-Pirate Day"] = "Ahoy, matey! It be the unoffical holiday celebratin' the salty \ + sea humor of speakin' like the pirates of old." + if(20) + Holiday["Rosh Hashanah"] = "An old human holiday that marks the traditional Hebrew new year." + if(28) + Holiday["Stupid-Questions Day"] = "Known as Ask A Stupid Question Day, it is an unoffical holiday \ + created by teachers in Sol, very long ago, to encourage students to ask more questions in the classroom." + + if(10) //Oct + switch(DD) + if(9) + Holiday["Lief Eriksson Day"] = "A day commemorating Norse explorer Lief Eriksson, an early Scandinavian cultural figure \ + who is thought to have been the first European to set foot in North America." + if(16) + Holiday["Boss' Day"] = "Boss' Day has traditionally been a day for employees to thank their bosses for the difficult work that they do \ + throughout the year. This day was created for the purpose of strengthening the bond between employer and employee." + if(21) + Holiday["First Day of Diwali"] = "An ancient Hindu, Jain and Sikh festival lasting five days, celebrating victory of light over darkness, good over \ + evil, and knowledge over ignorance. It is celebrated by the wearing of your finest clothes, decorating with oil lamps and rangolis, \ + fireworks, and gift-giving. Electric lights are often used in modern times where oil lamps would be hazardous or inoperable." + if(31) + Holiday["Halloween"] = "Originating from Earth, Halloween is also known as All Saints' Eve, and \ + is celebrated by some by attending costume parties, trick-or-treating, carving faces in pumpkins, or visiting \ + 'haunted' locations. Some people make it a goal to scare other people." + + if(11) //Nov + switch(DD) + if(1) + Holiday["Day of the Dead"] = "An old human holiday celebrating the lives of deceased friends and family members, \ + by means of good humour and joyful parties. Offerings are often left at altars to the dead, and exchanging gifts \ + among the living is not uncommon." + if(13) + Holiday["Kindness Day"] = "Kindness Day is an unofficial holiday to highlight good deeds in the \ + community, focusing on the positive power and the common thread of kindness which binds humanity and \ + friends together." + if(28) //Space thanksgiving. + Holiday["Appreciation Day"] = "Originally an old holiday from Earth, Appreciation Day follows many of the \ + traditions that its predecessor did, such as having a large feast (turkey often included), gathering with family, and being thankful \ + for what one has in life." + if(28 > DD > 20) + if(time2text(world.timeofday, "Day") == "Thursday") + Holiday["Thanksgiving"] = "Originally an old holiday from Earth, Thanksgiving follows many of the \ + traditions that its predecessor did, such as having a large feast (turkey often included), gathering with family, and being thankful \ + for what one has in life." + + if(12) //Dec + switch(DD) + if(10) + Holiday["Human-Rights Day"] = "An old holiday created by an intergovernmental organization known back than as the United Nations, \ + human rights were not recognized globally at the time, and the holiday was made in honor of the Universal Declaration of Human Rights. \ + These days, SolGov ensures that past efforts were not in vein, and continues to honor this holiday across the galaxy as a historical \ + reminder." + if(22) + Holiday["Vertalliq-qixim"] = "A skrellian holiday that celebrates the skrell's first landing on one of \ + their moons. It's often celebrated with grand festivals." + if(24) + Holiday["Christmas Eve"] = "The eve of Christmas, an old holiday from Earth that mainly involves gift \ + giving, decorating, family reunions, and a fat red human breaking into people's homes to steal milk and cookies." + if(25) + Holiday["Christmas"] = "Christmas is a very old holiday that originated in Earth, Sol. It was a \ + religious holiday for the Christian religion, which would later form Unitarianism. Nowadays, the holiday is celebrated \ + generally by giving gifts, symbolic decoration, and reuniting with one's family. It also features a mythical fat \ + red human, known as Santa, who broke into people's homes to loot cookies and milk." + if(31) + Holiday["New Year's Eve"] = "The eve of the New Year for Sol. It is traditionally celebrated by counting down to midnight, as that is \ + when the new year begins. Other activities include planning for self-improvement over the new year, attending New Year's parties, or \ + watching a timer count to zero, a large object descending, and fireworks exploding in the sky, in person or on broadcast." + + if(!Holiday) + //Friday the 13th + if(DD == 13) + if(time2text(world.timeofday, "DDD") == "Fri") + Holiday["Friday the 13th"] = "Friday the 13th is a superstitious day, associated with bad luck and misfortune." + +//Allows GA and GM to set the Holiday variable +/client/proc/Set_Holiday() + set name = ".Set Holiday" + set category = "Fun" + set desc = "Force-set the Holiday variable to make the game think it's a certain day." + if(!check_rights(R_SERVER)) return + + Holiday = list() + + var/H = tgui_input_text(src,"What holiday is it today?","Set Holiday") + var/B = tgui_input_text(src,"Now explain what the holiday is about","Set Holiday", multiline = TRUE, prevent_enter = TRUE) + + + Holiday[H] = B + + //update our hub status + world.update_status() + Holiday_Game_Start() + + message_admins("ADMIN: Event: [key_name(src)] force-set Holiday to \"[Holiday]\"") + log_admin("[key_name(src)] force-set Holiday to \"[Holiday]\"") + + +//Run at the start of a round +/proc/Holiday_Game_Start() + if(Holiday.len != 0) + var/list/holidays = list() + var/list/holiday_blurbs = list() + for(var/p in Holiday) + holidays.Add(p) + holiday_blurbs.Add("[Holiday[p]]") + var/holidays_string = english_list(holidays, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) + to_world(span_blue("and...")) + to_world("

    Happy [holidays_string] Everybody!

    ") + if(holiday_blurbs.len != 0) + for(var/blurb in holiday_blurbs) + to_world(span_blue("
    [blurb]
    ")) + switch(Holiday) //special holidays + if("Easter") + //do easter stuff + if("Christmas Eve","Christmas") + Christmas_Game_Start() + + return + +//Nested in the random events loop. Will be triggered every 2 minutes +/proc/Holiday_Random_Event() + if(isemptylist(Holiday)) + return 0 + switch(Holiday) //special holidays + if("Easter") //I'll make this into some helper procs at some point +/* var/list/turf/simulated/floor/Floorlist = list() + for(var/turf/simulated/floor/T) + if(T.contents) + Floorlist += T + var/turf/simulated/floor/F = Floorlist[rand(1,Floorlist.len)] + Floorlist = null + var/obj/structure/closet/C = locate(/obj/structure/closet) in F + var/obj/item/weapon/reagent_containers/food/snacks/chocolateegg/wrapped/Egg + if( C ) Egg = new(C) + else Egg = new(F) +*/ +/* var/list/obj/containers = list() + for(var/obj/item/weapon/storage/S in world) + if(isNotStationLevel(S.z)) continue + containers += S + + message_admins("DEBUG: Event: Egg spawned at [Egg.loc] ([Egg.x],[Egg.y],[Egg.z])")*/ + if("End of the World") + if(prob(eventchance)) GameOver() + + if("Christmas","Christmas Eve") + if(prob(eventchance)) ChristmasEvent() diff --git a/code/game/gamemodes/events/holidays/Other.dm b/code/game/gamemodes/events/holidays/Other.dm index 74b47506aaf..bd64fda833f 100644 --- a/code/game/gamemodes/events/holidays/Other.dm +++ b/code/game/gamemodes/events/holidays/Other.dm @@ -1,10 +1,10 @@ -/proc/GameOver() - if(!hadevent) - hadevent = 1 - message_admins("The apocalypse has begun! (this holiday event can be disabled by toggling events off within 60 seconds)") - spawn(600) - if(!config.allow_random_events) return - Show2Group4Delay(ScreenText(null,"
    GAME OVER
    "),null,150) - for(var/i=1,i<=4,i++) - spawn_dynamic_event() +/proc/GameOver() + if(!hadevent) + hadevent = 1 + message_admins("The apocalypse has begun! (this holiday event can be disabled by toggling events off within 60 seconds)") + spawn(600) + if(!config.allow_random_events) return + Show2Group4Delay(ScreenText(null,"
    GAME OVER
    "),null,150) + for(var/i=1,i<=4,i++) + spawn_dynamic_event() sleep(50) \ No newline at end of file diff --git a/code/game/gamemodes/events/wormholes.dm b/code/game/gamemodes/events/wormholes.dm index 20f7da63f3e..65b21df2ea5 100644 --- a/code/game/gamemodes/events/wormholes.dm +++ b/code/game/gamemodes/events/wormholes.dm @@ -1,70 +1,70 @@ -/proc/wormhole_event(var/set_duration = 5 MINUTES, var/wormhole_duration_modifier = 1) - spawn() - var/list/pick_turfs = list() - var/list/Z_choices = list() - Z_choices |= using_map.get_map_levels(1, FALSE) - for(var/turf/simulated/floor/T in world) - if(T.z in Z_choices) - if(!T.block_tele) - pick_turfs += T - - if(pick_turfs.len) - - var/wormhole_max_duration = round((5 MINUTES) * wormhole_duration_modifier) - var/wormhole_min_duration = round((30 SECONDS) * wormhole_duration_modifier) - - //All ready. Announce that bad juju is afoot. - command_announcement.Announce("Space-time anomalies detected on the station. There is no additional data.", "Anomaly Alert", new_sound = 'sound/AI/spanomalies.ogg') - - //prob(20) can be approximated to 1 wormhole every 5 turfs! - //admittedly less random but totally worth it >_< - var/event_duration = set_duration - var/number_of_selections = round(pick_turfs.len/(4 * (Z_choices.len + 1)))+1 //+1 to avoid division by zero! - var/sleep_duration = 0.2 SECONDS - var/end_time = world.time + event_duration //the time by which the event should have ended - - var/increment = max(1,round(number_of_selections/50)) -// to_world("DEBUG: number_of_selections: [number_of_selections] | sleep_duration: [sleep_duration]") - - var/index = 1 - for(var/I = 1 to number_of_selections) - - //we've run into overtime. End the event - if( end_time < world.time ) -// to_world("DEBUG: we've run into overtime. End the event") - return - if( !pick_turfs.len ) -// to_world("DEBUG: we've run out of turfs to pick. End the event") - return - - //loop it round - index += increment - index %= pick_turfs.len - index++ - - //get our enter and exit locations - var/turf/simulated/floor/enter = pick_turfs[index] - pick_turfs -= enter //remove it from pickable turfs list - if( !enter || !istype(enter) ) continue //sanity - - var/turf/simulated/floor/exit = pick(pick_turfs) -// pick_turfs -= exit - if( !exit || !istype(exit) ) continue //sanity - - create_wormhole(enter,exit,wormhole_min_duration,wormhole_max_duration) - - sleep(sleep_duration) //have a well deserved nap! - - -//maybe this proc can even be used as an admin tool for teleporting players without ruining immulsions? -/proc/create_wormhole(var/turf/enter as turf, var/turf/exit as turf, var/min_duration = 30 SECONDS, var/max_duration = 60 SECONDS) - set waitfor = FALSE - var/obj/effect/portal/P = new /obj/effect/portal( enter ) - P.target = exit - P.creator = null - P.icon = 'icons/obj/objects.dmi' - P.failchance = 0 - P.icon_state = "anom" - P.name = "wormhole" - spawn(rand(min_duration,max_duration)) - qdel(P) +/proc/wormhole_event(var/set_duration = 5 MINUTES, var/wormhole_duration_modifier = 1) + spawn() + var/list/pick_turfs = list() + var/list/Z_choices = list() + Z_choices |= using_map.get_map_levels(1, FALSE) + for(var/turf/simulated/floor/T in world) + if(T.z in Z_choices) + if(!T.block_tele) + pick_turfs += T + + if(pick_turfs.len) + + var/wormhole_max_duration = round((5 MINUTES) * wormhole_duration_modifier) + var/wormhole_min_duration = round((30 SECONDS) * wormhole_duration_modifier) + + //All ready. Announce that bad juju is afoot. + command_announcement.Announce("Space-time anomalies detected on the station. There is no additional data.", "Anomaly Alert", new_sound = 'sound/AI/spanomalies.ogg') + + //prob(20) can be approximated to 1 wormhole every 5 turfs! + //admittedly less random but totally worth it >_< + var/event_duration = set_duration + var/number_of_selections = round(pick_turfs.len/(4 * (Z_choices.len + 1)))+1 //+1 to avoid division by zero! + var/sleep_duration = 0.2 SECONDS + var/end_time = world.time + event_duration //the time by which the event should have ended + + var/increment = max(1,round(number_of_selections/50)) +// to_world("DEBUG: number_of_selections: [number_of_selections] | sleep_duration: [sleep_duration]") + + var/index = 1 + for(var/I = 1 to number_of_selections) + + //we've run into overtime. End the event + if( end_time < world.time ) +// to_world("DEBUG: we've run into overtime. End the event") + return + if( !pick_turfs.len ) +// to_world("DEBUG: we've run out of turfs to pick. End the event") + return + + //loop it round + index += increment + index %= pick_turfs.len + index++ + + //get our enter and exit locations + var/turf/simulated/floor/enter = pick_turfs[index] + pick_turfs -= enter //remove it from pickable turfs list + if( !enter || !istype(enter) ) continue //sanity + + var/turf/simulated/floor/exit = pick(pick_turfs) +// pick_turfs -= exit + if( !exit || !istype(exit) ) continue //sanity + + create_wormhole(enter,exit,wormhole_min_duration,wormhole_max_duration) + + sleep(sleep_duration) //have a well deserved nap! + + +//maybe this proc can even be used as an admin tool for teleporting players without ruining immulsions? +/proc/create_wormhole(var/turf/enter as turf, var/turf/exit as turf, var/min_duration = 30 SECONDS, var/max_duration = 60 SECONDS) + set waitfor = FALSE + var/obj/effect/portal/P = new /obj/effect/portal( enter ) + P.target = exit + P.creator = null + P.icon = 'icons/obj/objects.dmi' + P.failchance = 0 + P.icon_state = "anom" + P.name = "wormhole" + spawn(rand(min_duration,max_duration)) + qdel(P) diff --git a/code/game/gamemodes/extended/extended.dm b/code/game/gamemodes/extended/extended.dm index 0b37fa892fb..b9cdf222156 100644 --- a/code/game/gamemodes/extended/extended.dm +++ b/code/game/gamemodes/extended/extended.dm @@ -1,6 +1,6 @@ -/datum/game_mode/extended - name = "Extended" - config_tag = "extended" - required_players = 0 - round_description = "Just have fun and role-play!" +/datum/game_mode/extended + name = "Extended" + config_tag = "extended" + required_players = 0 + round_description = "Just have fun and role-play!" extended_round_description = "There are no antagonists during extended, unless an admin decides to be cheeky. Just play your character, mess around with your job, and have fun." \ No newline at end of file diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 326d14a798f..b8007d3ea5f 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -1,561 +1,561 @@ -var/global/antag_add_failed // Used in antag type voting. -var/global/list/additional_antag_types = list() - -/datum/game_mode - var/name = "invalid" - var/round_description = "How did you even vote this in?" - var/extended_round_description = "This roundtype should not be spawned, let alone votable. Someone contact a developer and tell them the game's broken again." - var/config_tag = null - var/votable = 1 - var/probability = 0 - - var/required_players = 0 // Minimum players for round to start if voted in. - var/required_players_secret = 0 // Minimum number of players for that game mode to be chose in Secret - var/required_enemies = 0 // Minimum antagonists for round to start. - var/newscaster_announcements = null - var/end_on_antag_death = 0 // Round will end when all antagonists are dead. - var/ert_disabled = 0 // ERT cannot be called. - var/deny_respawn = 0 // Disable respawn during this round. - - var/list/disabled_jobs = list() // Mostly used for Malf. This check is performed in job_controller so it doesn't spawn a regular AI. - - var/shuttle_delay = 1 // Shuttle transit time is multiplied by this. - var/auto_recall_shuttle = 0 // Will the shuttle automatically be recalled? - - var/list/antag_tags = list() // Core antag templates to spawn. - var/list/antag_templates // Extra antagonist types to include. - var/list/latejoin_templates = list() - var/round_autoantag = 0 // Will this round attempt to periodically spawn more antagonists? - var/antag_scaling_coeff = 5 // Coefficient for scaling max antagonists to player count. - var/require_all_templates = 0 // Will only start if all templates are checked and can spawn. - - var/station_was_nuked = 0 // See nuclearbomb.dm and malfunction.dm. - var/explosion_in_progress = 0 // Sit back and relax - var/waittime_l = 600 // Lower bound on time before intercept arrives (in tenths of seconds) - var/waittime_h = 1800 // Upper bound on time before intercept arrives (in tenths of seconds) - - var/event_delay_mod_moderate // Modifies the timing of random events. - var/event_delay_mod_major // As above. - -/datum/game_mode/New() - ..() - -/datum/game_mode/Topic(href, href_list[]) - if(..()) - return - if(href_list["toggle"]) - switch(href_list["toggle"]) - if("respawn") - deny_respawn = !deny_respawn - if("ert") - ert_disabled = !ert_disabled - announce_ert_disabled() - if("shuttle_recall") - auto_recall_shuttle = !auto_recall_shuttle - if("autotraitor") - round_autoantag = !round_autoantag - message_admins("Admin [key_name_admin(usr)] toggled game mode option '[href_list["toggle"]]'.") - else if(href_list["set"]) - var/choice = "" - switch(href_list["set"]) - if("shuttle_delay") - choice = tgui_input_number(usr, "Enter a new shuttle delay multiplier", null, null, 20, 1) - if(!choice || choice < 1 || choice > 20) - return - shuttle_delay = choice - if("antag_scaling") - choice = tgui_input_number(usr, "Enter a new antagonist cap scaling coefficient.", null, null, 100, 0) - if(isnull(choice) || choice < 0 || choice > 100) - return - antag_scaling_coeff = choice - if("event_modifier_moderate") - choice = tgui_input_number(usr, "Enter a new moderate event time modifier.", null, null, 100, 0) - if(isnull(choice) || choice < 0 || choice > 100) - return - event_delay_mod_moderate = choice - refresh_event_modifiers() - if("event_modifier_severe") - choice = tgui_input_number(usr, "Enter a new moderate event time modifier.", null, null, 100, 0) - if(isnull(choice) || choice < 0 || choice > 100) - return - event_delay_mod_major = choice - refresh_event_modifiers() - message_admins("Admin [key_name_admin(usr)] set game mode option '[href_list["set"]]' to [choice].") - else if(href_list["debug_antag"]) - if(href_list["debug_antag"] == "self") - usr.client.debug_variables(src) - return - var/datum/antagonist/antag = all_antag_types[href_list["debug_antag"]] - if(antag) - usr.client.debug_variables(antag) - message_admins("Admin [key_name_admin(usr)] is debugging the [antag.role_text] template.") - else if(href_list["remove_antag_type"]) - if(antag_tags && (href_list["remove_antag_type"] in antag_tags)) - to_chat(usr, "Cannot remove core mode antag type.") - return - var/datum/antagonist/antag = all_antag_types[href_list["remove_antag_type"]] - if(antag_templates && antag_templates.len && antag && (antag in antag_templates) && (antag.id in additional_antag_types)) - antag_templates -= antag - additional_antag_types -= antag.id - message_admins("Admin [key_name_admin(usr)] removed [antag.role_text] template from game mode.") - else if(href_list["add_antag_type"]) - var/choice = tgui_input_list(usr, "Which type do you wish to add?", "Select Antag Type", all_antag_types) - if(!choice) - return - var/datum/antagonist/antag = all_antag_types[choice] - if(antag) - if(!islist(ticker.mode.antag_templates)) - ticker.mode.antag_templates = list() - ticker.mode.antag_templates |= antag - message_admins("Admin [key_name_admin(usr)] added [antag.role_text] template to game mode.") - - // I am very sure there's a better way to do this, but I'm not sure what it might be. ~Z - spawn(1) - for(var/datum/admins/admin in world) - if(usr.client == admin.owner) - admin.show_game_mode(usr) - return - -/datum/game_mode/proc/announce() //to be called when round starts - to_world("The current game mode is [capitalize(name)]!") - if(round_description) - to_world("[round_description]") - if(round_autoantag) - to_world("Antagonists will be added to the round automagically as needed.") - if(antag_templates && antag_templates.len) - var/antag_summary = "Possible antagonist types: " - var/i = 1 - for(var/datum/antagonist/antag in antag_templates) - if(i > 1) - if(i == antag_templates.len) - antag_summary += " and " - else - antag_summary += ", " - antag_summary += "[antag.role_text_plural]" - i++ - antag_summary += "." - if(antag_templates.len > 1 && master_mode != "secret") - to_world("[antag_summary]") - else - message_admins("[antag_summary]") - -///can_start() -///Checks to see if the game can be setup and ran with the current number of players or whatnot. -/datum/game_mode/proc/can_start(var/do_not_spawn) - var/playerC = 0 - for(var/mob/new_player/player in player_list) - if((player.client)&&(player.ready)) - playerC++ - - if(master_mode=="secret") - if(playerC < config.player_requirements_secret[config_tag]) - return 0 - else - if(playerC < config.player_requirements[config_tag]) - return 0 - - if(!(antag_templates && antag_templates.len)) - return 1 - - var/enemy_count = 0 - if(antag_tags && antag_tags.len) - for(var/antag_tag in antag_tags) - var/datum/antagonist/antag = all_antag_types[antag_tag] - if(!antag) - continue - var/list/potential = list() - if(antag.flags & ANTAG_OVERRIDE_JOB) - potential = antag.pending_antagonists - else - potential = antag.candidates - if(islist(potential)) - if(require_all_templates && potential.len < antag.initial_spawn_req) - return 0 - enemy_count += potential.len - if(enemy_count >= required_enemies) - return 1 - return 0 - -/datum/game_mode/proc/refresh_event_modifiers() - if(event_delay_mod_moderate || event_delay_mod_major) - SSevents.report_at_round_end = TRUE - if(event_delay_mod_moderate) - var/datum/event_container/EModerate = SSevents.event_containers[EVENT_LEVEL_MODERATE] - EModerate.delay_modifier = event_delay_mod_moderate - if(event_delay_mod_moderate) - var/datum/event_container/EMajor = SSevents.event_containers[EVENT_LEVEL_MAJOR] - EMajor.delay_modifier = event_delay_mod_major - -/datum/game_mode/proc/pre_setup() - for(var/datum/antagonist/antag in antag_templates) - antag.build_candidate_list() //compile a list of all eligible candidates - - //antag roles that replace jobs need to be assigned before the job controller hands out jobs. - if(antag.flags & ANTAG_OVERRIDE_JOB) - antag.attempt_spawn() //select antags to be spawned - -///post_setup() -/datum/game_mode/proc/post_setup() - - refresh_event_modifiers() - - spawn (ROUNDSTART_LOGOUT_REPORT_TIME) - display_roundstart_logout_report() - - spawn (rand(waittime_l, waittime_h)) - spawn(rand(100,150)) - announce_ert_disabled() - - //Assign all antag types for this game mode. Any players spawned as antags earlier should have been removed from the pending list, so no need to worry about those. - for(var/datum/antagonist/antag in antag_templates) - if(!(antag.flags & ANTAG_OVERRIDE_JOB)) - antag.attempt_spawn() //select antags to be spawned - antag.finalize_spawn() //actually spawn antags - if(antag.is_latejoin_template()) - latejoin_templates |= antag - - if(emergency_shuttle && auto_recall_shuttle) - emergency_shuttle.auto_recall = 1 - - feedback_set_details("round_start","[time2text(world.realtime)]") - if(ticker && ticker.mode) - feedback_set_details("game_mode","[ticker.mode]") - feedback_set_details("server_ip","[world.internet_address]:[world.port]") - return 1 - -/datum/game_mode/proc/fail_setup() - for(var/datum/antagonist/antag in antag_templates) - antag.reset() - -/datum/game_mode/proc/announce_ert_disabled() - if(!ert_disabled) - return - - var/list/reasons = list( - "political instability", - "quantum fluctuations", - "hostile raiders", - "derelict station debris", - "REDACTED", - "ancient alien artillery", - "solar magnetic storms", - "sentient time-travelling killbots", - "gravitational anomalies", - "wormholes to another dimension", - "a telescience mishap", - "radiation flares", - "supermatter dust", - "leaks into a negative reality", - "antiparticle clouds", - "residual bluespace energy", - "suspected criminal operatives", - "malfunctioning von Neumann probe swarms", - "shadowy interlopers", - "a stranded alien arkship", - "haywire IPC constructs", - "rogue Unathi exiles", - "artifacts of eldritch horror", - "a brain slug infestation", - "killer bugs that lay eggs in the husks of the living", - "a deserted transport carrying alien specimens", - "an emissary for the gestalt requesting a security detail", - "a Tajaran slave rebellion", - "radical Skrellian transevolutionaries", - "classified security operations" - ) - command_announcement.Announce("The presence of [pick(reasons)] in the region is tying up all available local emergency resources; emergency response teams cannot be called at this time, and post-evacuation recovery efforts will be substantially delayed.","Emergency Transmission") - -/datum/game_mode/proc/check_finished() - if(emergency_shuttle.returned() || station_was_nuked) - return 1 - if(end_on_antag_death && antag_templates && antag_templates.len) - for(var/datum/antagonist/antag in antag_templates) - if(!antag.antags_are_dead()) - return 0 - if(config.continous_rounds) - emergency_shuttle.auto_recall = 0 - return 0 - return 1 - return 0 - -/datum/game_mode/proc/cleanup() //This is called when the round has ended but not the game, if any cleanup would be necessary in that case. - return - -/datum/game_mode/proc/declare_completion() - - var/is_antag_mode = (antag_templates && antag_templates.len) - check_victory() - if(is_antag_mode) - sleep(10) - for(var/datum/antagonist/antag in antag_templates) - sleep(10) - antag.check_victory() - antag.print_player_summary() - sleep(10) - - var/clients = 0 - var/surviving_humans = 0 - var/surviving_total = 0 - var/ghosts = 0 - var/escaped_humans = 0 - var/escaped_total = 0 - var/escaped_on_pod_1 = 0 - var/escaped_on_pod_2 = 0 - var/escaped_on_pod_3 = 0 - var/escaped_on_pod_5 = 0 - var/escaped_on_shuttle = 0 - - var/list/area/escape_locations = list(/area/shuttle/escape, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) //VOREStation Edit - - for(var/mob/M in player_list) - if(M.client) - clients++ - var/M_area_type = (get_turf(M))?.loc?.type - if(ishuman(M)) - if(M.stat != DEAD) - surviving_humans++ - if(M_area_type in escape_locations) - escaped_humans++ - if(M.stat != DEAD) - surviving_total++ - if(M_area_type in escape_locations) - escaped_total++ - - if(M_area_type == /area/shuttle/escape/centcom) - escaped_on_shuttle++ - - if(M_area_type == /area/shuttle/escape_pod1/centcom) - escaped_on_pod_1++ - if(M_area_type == /area/shuttle/escape_pod2/centcom) - escaped_on_pod_2++ - if(M_area_type == /area/shuttle/escape_pod3/centcom) - escaped_on_pod_3++ - if(M_area_type == /area/shuttle/escape_pod5/centcom) - escaped_on_pod_5++ - - if(isobserver(M)) - ghosts++ - - var/text = "" - if(surviving_total > 0) - text += "
    There [surviving_total>1 ? "were [surviving_total] survivors" : "was one survivor"]" - text += " ([escaped_total>0 ? escaped_total : "none"] [emergency_shuttle.evac ? "escaped" : "transferred"]) and [ghosts] ghosts.
    " - else - text += "There were no survivors ([ghosts] ghosts)." - to_world(text) - - if(clients > 0) - feedback_set("round_end_clients",clients) - if(ghosts > 0) - feedback_set("round_end_ghosts",ghosts) - if(surviving_humans > 0) - feedback_set("survived_human",surviving_humans) - if(surviving_total > 0) - feedback_set("survived_total",surviving_total) - if(escaped_humans > 0) - feedback_set("escaped_human",escaped_humans) - if(escaped_total > 0) - feedback_set("escaped_total",escaped_total) - if(escaped_on_shuttle > 0) - feedback_set("escaped_on_shuttle",escaped_on_shuttle) - if(escaped_on_pod_1 > 0) - feedback_set("escaped_on_pod_1",escaped_on_pod_1) - if(escaped_on_pod_2 > 0) - feedback_set("escaped_on_pod_2",escaped_on_pod_2) - if(escaped_on_pod_3 > 0) - feedback_set("escaped_on_pod_3",escaped_on_pod_3) - if(escaped_on_pod_5 > 0) - feedback_set("escaped_on_pod_5",escaped_on_pod_5) - - send2mainirc("A round of [src.name] has ended - [surviving_total] survivors, [ghosts] ghosts.") - SSwebhooks.send( - WEBHOOK_ROUNDEND, - list( - "survivors" = surviving_total, - "escaped" = escaped_total, - "ghosts" = ghosts, - "clients" = clients - ) - ) - - return 0 - -/datum/game_mode/proc/check_win() //universal trigger to be called at mob death, nuke explosion, etc. To be called from everywhere. - return 0 - -/datum/game_mode/proc/get_players_for_role(var/role, var/antag_id, var/ghosts_only) - var/list/players = list() - var/list/candidates = list() - - var/datum/antagonist/antag_template = all_antag_types[antag_id] - if(!antag_template) - return candidates - - // If this is being called post-roundstart then it doesn't care about ready status. - if(ticker && ticker.current_state == GAME_STATE_PLAYING) - for(var/mob/player in player_list) - if(!player.client) - continue - if(istype(player, /mob/new_player)) - continue - if(istype(player, /mob/observer/dead) && !ghosts_only) - continue - if(!role || (player.client.prefs.be_special & role)) - log_debug("[player.key] had [antag_id] enabled, so we are drafting them.") - candidates |= player.mind - else - // Assemble a list of active players without jobbans. - for(var/mob/new_player/player in player_list) - if( player.client && player.ready ) - players += player - - // Get a list of all the people who want to be the antagonist for this round - for(var/mob/new_player/player in players) - if(!role || (player.client.prefs.be_special & role)) - log_debug("[player.key] had [antag_id] enabled, so we are drafting them.") - candidates += player.mind - players -= player - - // Below is commented out as an attempt to solve an issue of too little people wanting to join the round due to wanting to have cake and eat it too. - /* - // If we don't have enough antags, draft people who voted for the round. - if(candidates.len < required_enemies) - for(var/mob/new_player/player in players) - if(player.ckey in round_voters) - log_debug("[player.key] voted for this round, so we are drafting them.") - candidates += player.mind - players -= player - break - */ - - return candidates // Returns: The number of people who had the antagonist role set to yes, regardless of recomended_enemies, if that number is greater than required_enemies - // required_enemies if the number of people with that role set to yes is less than recomended_enemies, - // Less if there are not enough valid players in the game entirely to make required_enemies. - -/datum/game_mode/proc/num_players() - . = 0 - for(var/mob/new_player/P in player_list) - if(P.client && P.ready) - . ++ - -/datum/game_mode/proc/check_antagonists_topic(href, href_list[]) - return 0 - -/datum/game_mode/proc/create_antagonists() - - if(!config.traitor_scaling) - antag_scaling_coeff = 0 - - if(antag_tags && antag_tags.len) - antag_templates = list() - for(var/antag_tag in antag_tags) - var/datum/antagonist/antag = all_antag_types[antag_tag] - if(antag) - antag_templates |= antag - - if(additional_antag_types && additional_antag_types.len) - if(!antag_templates) - antag_templates = list() - for(var/antag_type in additional_antag_types) - var/datum/antagonist/antag = all_antag_types[antag_type] - if(antag) - antag_templates |= antag - - newscaster_announcements = pick(newscaster_standard_feeds) - -/datum/game_mode/proc/check_victory() - return - -////////////////////////// -//Reports player logouts// -////////////////////////// -/proc/display_roundstart_logout_report() - var/msg = "Roundstart logout report\n\n" - for(var/mob/living/L in mob_list) - - if(L.ckey) - var/found = 0 - for(var/client/C in GLOB.clients) - if(C.ckey == L.ckey) - found = 1 - break - if(!found) - msg += "[L.name] ([L.ckey]), the [L.job] ([span_yellow("Disconnected")])\n" - - if(L.ckey && L.client) - if(L.client.inactivity >= (ROUNDSTART_LOGOUT_REPORT_TIME / 2)) //Connected, but inactive (alt+tabbed or something) - msg += "[L.name] ([L.ckey]), the [L.job] ([span_yellow("Connected, Inactive")])\n" - continue //AFK client - if(L.stat) - if(L.suiciding) //Suicider - msg += "[L.name] ([L.ckey]), the [L.job] ([span_red("Suicide")])\n" - continue //Disconnected client - if(L.stat == UNCONSCIOUS) - msg += "[L.name] ([L.ckey]), the [L.job] (Dying)\n" - continue //Unconscious - if(L.stat == DEAD) - msg += "[L.name] ([L.ckey]), the [L.job] (Dead)\n" - continue //Dead - - continue //Happy connected client - for(var/mob/observer/dead/D in mob_list) - if(D.mind && (D.mind.original == L || D.mind.current == L)) - if(L.stat == DEAD) - if(L.suiciding) //Suicider - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_red("Suicide")])\n" - continue //Disconnected client - else - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n" - continue //Dead mob, ghost abandoned - else - if(D.can_reenter_corpse) - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_red("Adminghosted")])\n" - continue //Lolwhat - else - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_red("Ghosted")])\n" - continue //Ghosted while alive - - msg += "" // close the span from right at the top - - for(var/mob/M in mob_list) - if(M.client && M.client.holder) - to_chat(M,msg) - -/proc/get_nt_opposed() - var/list/dudes = list() - for(var/mob/living/carbon/human/man in player_list) - if(man.client) - if(man.client.prefs.economic_status == CLASS_LOWER) - dudes += man - else if(man.client.prefs.economic_status == CLASS_LOWMID && prob(50)) - dudes += man - if(dudes.len == 0) return null - return pick(dudes) - -/proc/show_objectives(var/datum/mind/player) - - if(!player || !player.current) return - - var/obj_count = 1 - to_chat(player.current, "Your current objectives:") - for(var/datum/objective/objective in player.objectives) - to_chat(player.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - -/mob/verb/check_round_info() - set name = "Check Round Info" - set category = "OOC" - - if(!ticker || !ticker.mode) - to_chat(usr, "Something is terribly wrong; there is no gametype.") - return - - if(master_mode != "secret") - to_chat(usr, "The roundtype is [capitalize(ticker.mode.name)]") - if(ticker.mode.round_description) - to_chat(usr, "[ticker.mode.round_description]") - if(ticker.mode.extended_round_description) - to_chat(usr, "[ticker.mode.extended_round_description]") - else - to_chat(usr, "Shhhh. It's a secret.") - return +var/global/antag_add_failed // Used in antag type voting. +var/global/list/additional_antag_types = list() + +/datum/game_mode + var/name = "invalid" + var/round_description = "How did you even vote this in?" + var/extended_round_description = "This roundtype should not be spawned, let alone votable. Someone contact a developer and tell them the game's broken again." + var/config_tag = null + var/votable = 1 + var/probability = 0 + + var/required_players = 0 // Minimum players for round to start if voted in. + var/required_players_secret = 0 // Minimum number of players for that game mode to be chose in Secret + var/required_enemies = 0 // Minimum antagonists for round to start. + var/newscaster_announcements = null + var/end_on_antag_death = 0 // Round will end when all antagonists are dead. + var/ert_disabled = 0 // ERT cannot be called. + var/deny_respawn = 0 // Disable respawn during this round. + + var/list/disabled_jobs = list() // Mostly used for Malf. This check is performed in job_controller so it doesn't spawn a regular AI. + + var/shuttle_delay = 1 // Shuttle transit time is multiplied by this. + var/auto_recall_shuttle = 0 // Will the shuttle automatically be recalled? + + var/list/antag_tags = list() // Core antag templates to spawn. + var/list/antag_templates // Extra antagonist types to include. + var/list/latejoin_templates = list() + var/round_autoantag = 0 // Will this round attempt to periodically spawn more antagonists? + var/antag_scaling_coeff = 5 // Coefficient for scaling max antagonists to player count. + var/require_all_templates = 0 // Will only start if all templates are checked and can spawn. + + var/station_was_nuked = 0 // See nuclearbomb.dm and malfunction.dm. + var/explosion_in_progress = 0 // Sit back and relax + var/waittime_l = 600 // Lower bound on time before intercept arrives (in tenths of seconds) + var/waittime_h = 1800 // Upper bound on time before intercept arrives (in tenths of seconds) + + var/event_delay_mod_moderate // Modifies the timing of random events. + var/event_delay_mod_major // As above. + +/datum/game_mode/New() + ..() + +/datum/game_mode/Topic(href, href_list[]) + if(..()) + return + if(href_list["toggle"]) + switch(href_list["toggle"]) + if("respawn") + deny_respawn = !deny_respawn + if("ert") + ert_disabled = !ert_disabled + announce_ert_disabled() + if("shuttle_recall") + auto_recall_shuttle = !auto_recall_shuttle + if("autotraitor") + round_autoantag = !round_autoantag + message_admins("Admin [key_name_admin(usr)] toggled game mode option '[href_list["toggle"]]'.") + else if(href_list["set"]) + var/choice = "" + switch(href_list["set"]) + if("shuttle_delay") + choice = tgui_input_number(usr, "Enter a new shuttle delay multiplier", null, null, 20, 1) + if(!choice || choice < 1 || choice > 20) + return + shuttle_delay = choice + if("antag_scaling") + choice = tgui_input_number(usr, "Enter a new antagonist cap scaling coefficient.", null, null, 100, 0) + if(isnull(choice) || choice < 0 || choice > 100) + return + antag_scaling_coeff = choice + if("event_modifier_moderate") + choice = tgui_input_number(usr, "Enter a new moderate event time modifier.", null, null, 100, 0) + if(isnull(choice) || choice < 0 || choice > 100) + return + event_delay_mod_moderate = choice + refresh_event_modifiers() + if("event_modifier_severe") + choice = tgui_input_number(usr, "Enter a new moderate event time modifier.", null, null, 100, 0) + if(isnull(choice) || choice < 0 || choice > 100) + return + event_delay_mod_major = choice + refresh_event_modifiers() + message_admins("Admin [key_name_admin(usr)] set game mode option '[href_list["set"]]' to [choice].") + else if(href_list["debug_antag"]) + if(href_list["debug_antag"] == "self") + usr.client.debug_variables(src) + return + var/datum/antagonist/antag = all_antag_types[href_list["debug_antag"]] + if(antag) + usr.client.debug_variables(antag) + message_admins("Admin [key_name_admin(usr)] is debugging the [antag.role_text] template.") + else if(href_list["remove_antag_type"]) + if(antag_tags && (href_list["remove_antag_type"] in antag_tags)) + to_chat(usr, "Cannot remove core mode antag type.") + return + var/datum/antagonist/antag = all_antag_types[href_list["remove_antag_type"]] + if(antag_templates && antag_templates.len && antag && (antag in antag_templates) && (antag.id in additional_antag_types)) + antag_templates -= antag + additional_antag_types -= antag.id + message_admins("Admin [key_name_admin(usr)] removed [antag.role_text] template from game mode.") + else if(href_list["add_antag_type"]) + var/choice = tgui_input_list(usr, "Which type do you wish to add?", "Select Antag Type", all_antag_types) + if(!choice) + return + var/datum/antagonist/antag = all_antag_types[choice] + if(antag) + if(!islist(ticker.mode.antag_templates)) + ticker.mode.antag_templates = list() + ticker.mode.antag_templates |= antag + message_admins("Admin [key_name_admin(usr)] added [antag.role_text] template to game mode.") + + // I am very sure there's a better way to do this, but I'm not sure what it might be. ~Z + spawn(1) + for(var/datum/admins/admin in world) + if(usr.client == admin.owner) + admin.show_game_mode(usr) + return + +/datum/game_mode/proc/announce() //to be called when round starts + to_world("The current game mode is [capitalize(name)]!") + if(round_description) + to_world("[round_description]") + if(round_autoantag) + to_world("Antagonists will be added to the round automagically as needed.") + if(antag_templates && antag_templates.len) + var/antag_summary = "Possible antagonist types: " + var/i = 1 + for(var/datum/antagonist/antag in antag_templates) + if(i > 1) + if(i == antag_templates.len) + antag_summary += " and " + else + antag_summary += ", " + antag_summary += "[antag.role_text_plural]" + i++ + antag_summary += "." + if(antag_templates.len > 1 && master_mode != "secret") + to_world("[antag_summary]") + else + message_admins("[antag_summary]") + +///can_start() +///Checks to see if the game can be setup and ran with the current number of players or whatnot. +/datum/game_mode/proc/can_start(var/do_not_spawn) + var/playerC = 0 + for(var/mob/new_player/player in player_list) + if((player.client)&&(player.ready)) + playerC++ + + if(master_mode=="secret") + if(playerC < config.player_requirements_secret[config_tag]) + return 0 + else + if(playerC < config.player_requirements[config_tag]) + return 0 + + if(!(antag_templates && antag_templates.len)) + return 1 + + var/enemy_count = 0 + if(antag_tags && antag_tags.len) + for(var/antag_tag in antag_tags) + var/datum/antagonist/antag = all_antag_types[antag_tag] + if(!antag) + continue + var/list/potential = list() + if(antag.flags & ANTAG_OVERRIDE_JOB) + potential = antag.pending_antagonists + else + potential = antag.candidates + if(islist(potential)) + if(require_all_templates && potential.len < antag.initial_spawn_req) + return 0 + enemy_count += potential.len + if(enemy_count >= required_enemies) + return 1 + return 0 + +/datum/game_mode/proc/refresh_event_modifiers() + if(event_delay_mod_moderate || event_delay_mod_major) + SSevents.report_at_round_end = TRUE + if(event_delay_mod_moderate) + var/datum/event_container/EModerate = SSevents.event_containers[EVENT_LEVEL_MODERATE] + EModerate.delay_modifier = event_delay_mod_moderate + if(event_delay_mod_moderate) + var/datum/event_container/EMajor = SSevents.event_containers[EVENT_LEVEL_MAJOR] + EMajor.delay_modifier = event_delay_mod_major + +/datum/game_mode/proc/pre_setup() + for(var/datum/antagonist/antag in antag_templates) + antag.build_candidate_list() //compile a list of all eligible candidates + + //antag roles that replace jobs need to be assigned before the job controller hands out jobs. + if(antag.flags & ANTAG_OVERRIDE_JOB) + antag.attempt_spawn() //select antags to be spawned + +///post_setup() +/datum/game_mode/proc/post_setup() + + refresh_event_modifiers() + + spawn (ROUNDSTART_LOGOUT_REPORT_TIME) + display_roundstart_logout_report() + + spawn (rand(waittime_l, waittime_h)) + spawn(rand(100,150)) + announce_ert_disabled() + + //Assign all antag types for this game mode. Any players spawned as antags earlier should have been removed from the pending list, so no need to worry about those. + for(var/datum/antagonist/antag in antag_templates) + if(!(antag.flags & ANTAG_OVERRIDE_JOB)) + antag.attempt_spawn() //select antags to be spawned + antag.finalize_spawn() //actually spawn antags + if(antag.is_latejoin_template()) + latejoin_templates |= antag + + if(emergency_shuttle && auto_recall_shuttle) + emergency_shuttle.auto_recall = 1 + + feedback_set_details("round_start","[time2text(world.realtime)]") + if(ticker && ticker.mode) + feedback_set_details("game_mode","[ticker.mode]") + feedback_set_details("server_ip","[world.internet_address]:[world.port]") + return 1 + +/datum/game_mode/proc/fail_setup() + for(var/datum/antagonist/antag in antag_templates) + antag.reset() + +/datum/game_mode/proc/announce_ert_disabled() + if(!ert_disabled) + return + + var/list/reasons = list( + "political instability", + "quantum fluctuations", + "hostile raiders", + "derelict station debris", + "REDACTED", + "ancient alien artillery", + "solar magnetic storms", + "sentient time-travelling killbots", + "gravitational anomalies", + "wormholes to another dimension", + "a telescience mishap", + "radiation flares", + "supermatter dust", + "leaks into a negative reality", + "antiparticle clouds", + "residual bluespace energy", + "suspected criminal operatives", + "malfunctioning von Neumann probe swarms", + "shadowy interlopers", + "a stranded alien arkship", + "haywire IPC constructs", + "rogue Unathi exiles", + "artifacts of eldritch horror", + "a brain slug infestation", + "killer bugs that lay eggs in the husks of the living", + "a deserted transport carrying alien specimens", + "an emissary for the gestalt requesting a security detail", + "a Tajaran slave rebellion", + "radical Skrellian transevolutionaries", + "classified security operations" + ) + command_announcement.Announce("The presence of [pick(reasons)] in the region is tying up all available local emergency resources; emergency response teams cannot be called at this time, and post-evacuation recovery efforts will be substantially delayed.","Emergency Transmission") + +/datum/game_mode/proc/check_finished() + if(emergency_shuttle.returned() || station_was_nuked) + return 1 + if(end_on_antag_death && antag_templates && antag_templates.len) + for(var/datum/antagonist/antag in antag_templates) + if(!antag.antags_are_dead()) + return 0 + if(config.continous_rounds) + emergency_shuttle.auto_recall = 0 + return 0 + return 1 + return 0 + +/datum/game_mode/proc/cleanup() //This is called when the round has ended but not the game, if any cleanup would be necessary in that case. + return + +/datum/game_mode/proc/declare_completion() + + var/is_antag_mode = (antag_templates && antag_templates.len) + check_victory() + if(is_antag_mode) + sleep(10) + for(var/datum/antagonist/antag in antag_templates) + sleep(10) + antag.check_victory() + antag.print_player_summary() + sleep(10) + + var/clients = 0 + var/surviving_humans = 0 + var/surviving_total = 0 + var/ghosts = 0 + var/escaped_humans = 0 + var/escaped_total = 0 + var/escaped_on_pod_1 = 0 + var/escaped_on_pod_2 = 0 + var/escaped_on_pod_3 = 0 + var/escaped_on_pod_5 = 0 + var/escaped_on_shuttle = 0 + + var/list/area/escape_locations = list(/area/shuttle/escape, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) //VOREStation Edit + + for(var/mob/M in player_list) + if(M.client) + clients++ + var/M_area_type = (get_turf(M))?.loc?.type + if(ishuman(M)) + if(M.stat != DEAD) + surviving_humans++ + if(M_area_type in escape_locations) + escaped_humans++ + if(M.stat != DEAD) + surviving_total++ + if(M_area_type in escape_locations) + escaped_total++ + + if(M_area_type == /area/shuttle/escape/centcom) + escaped_on_shuttle++ + + if(M_area_type == /area/shuttle/escape_pod1/centcom) + escaped_on_pod_1++ + if(M_area_type == /area/shuttle/escape_pod2/centcom) + escaped_on_pod_2++ + if(M_area_type == /area/shuttle/escape_pod3/centcom) + escaped_on_pod_3++ + if(M_area_type == /area/shuttle/escape_pod5/centcom) + escaped_on_pod_5++ + + if(isobserver(M)) + ghosts++ + + var/text = "" + if(surviving_total > 0) + text += "
    There [surviving_total>1 ? "were [surviving_total] survivors" : "was one survivor"]" + text += " ([escaped_total>0 ? escaped_total : "none"] [emergency_shuttle.evac ? "escaped" : "transferred"]) and [ghosts] ghosts.
    " + else + text += "There were no survivors ([ghosts] ghosts)." + to_world(text) + + if(clients > 0) + feedback_set("round_end_clients",clients) + if(ghosts > 0) + feedback_set("round_end_ghosts",ghosts) + if(surviving_humans > 0) + feedback_set("survived_human",surviving_humans) + if(surviving_total > 0) + feedback_set("survived_total",surviving_total) + if(escaped_humans > 0) + feedback_set("escaped_human",escaped_humans) + if(escaped_total > 0) + feedback_set("escaped_total",escaped_total) + if(escaped_on_shuttle > 0) + feedback_set("escaped_on_shuttle",escaped_on_shuttle) + if(escaped_on_pod_1 > 0) + feedback_set("escaped_on_pod_1",escaped_on_pod_1) + if(escaped_on_pod_2 > 0) + feedback_set("escaped_on_pod_2",escaped_on_pod_2) + if(escaped_on_pod_3 > 0) + feedback_set("escaped_on_pod_3",escaped_on_pod_3) + if(escaped_on_pod_5 > 0) + feedback_set("escaped_on_pod_5",escaped_on_pod_5) + + send2mainirc("A round of [src.name] has ended - [surviving_total] survivors, [ghosts] ghosts.") + SSwebhooks.send( + WEBHOOK_ROUNDEND, + list( + "survivors" = surviving_total, + "escaped" = escaped_total, + "ghosts" = ghosts, + "clients" = clients + ) + ) + + return 0 + +/datum/game_mode/proc/check_win() //universal trigger to be called at mob death, nuke explosion, etc. To be called from everywhere. + return 0 + +/datum/game_mode/proc/get_players_for_role(var/role, var/antag_id, var/ghosts_only) + var/list/players = list() + var/list/candidates = list() + + var/datum/antagonist/antag_template = all_antag_types[antag_id] + if(!antag_template) + return candidates + + // If this is being called post-roundstart then it doesn't care about ready status. + if(ticker && ticker.current_state == GAME_STATE_PLAYING) + for(var/mob/player in player_list) + if(!player.client) + continue + if(istype(player, /mob/new_player)) + continue + if(istype(player, /mob/observer/dead) && !ghosts_only) + continue + if(!role || (player.client.prefs.be_special & role)) + log_debug("[player.key] had [antag_id] enabled, so we are drafting them.") + candidates |= player.mind + else + // Assemble a list of active players without jobbans. + for(var/mob/new_player/player in player_list) + if( player.client && player.ready ) + players += player + + // Get a list of all the people who want to be the antagonist for this round + for(var/mob/new_player/player in players) + if(!role || (player.client.prefs.be_special & role)) + log_debug("[player.key] had [antag_id] enabled, so we are drafting them.") + candidates += player.mind + players -= player + + // Below is commented out as an attempt to solve an issue of too little people wanting to join the round due to wanting to have cake and eat it too. + /* + // If we don't have enough antags, draft people who voted for the round. + if(candidates.len < required_enemies) + for(var/mob/new_player/player in players) + if(player.ckey in round_voters) + log_debug("[player.key] voted for this round, so we are drafting them.") + candidates += player.mind + players -= player + break + */ + + return candidates // Returns: The number of people who had the antagonist role set to yes, regardless of recomended_enemies, if that number is greater than required_enemies + // required_enemies if the number of people with that role set to yes is less than recomended_enemies, + // Less if there are not enough valid players in the game entirely to make required_enemies. + +/datum/game_mode/proc/num_players() + . = 0 + for(var/mob/new_player/P in player_list) + if(P.client && P.ready) + . ++ + +/datum/game_mode/proc/check_antagonists_topic(href, href_list[]) + return 0 + +/datum/game_mode/proc/create_antagonists() + + if(!config.traitor_scaling) + antag_scaling_coeff = 0 + + if(antag_tags && antag_tags.len) + antag_templates = list() + for(var/antag_tag in antag_tags) + var/datum/antagonist/antag = all_antag_types[antag_tag] + if(antag) + antag_templates |= antag + + if(additional_antag_types && additional_antag_types.len) + if(!antag_templates) + antag_templates = list() + for(var/antag_type in additional_antag_types) + var/datum/antagonist/antag = all_antag_types[antag_type] + if(antag) + antag_templates |= antag + + newscaster_announcements = pick(newscaster_standard_feeds) + +/datum/game_mode/proc/check_victory() + return + +////////////////////////// +//Reports player logouts// +////////////////////////// +/proc/display_roundstart_logout_report() + var/msg = "Roundstart logout report\n\n" + for(var/mob/living/L in mob_list) + + if(L.ckey) + var/found = 0 + for(var/client/C in GLOB.clients) + if(C.ckey == L.ckey) + found = 1 + break + if(!found) + msg += "[L.name] ([L.ckey]), the [L.job] ([span_yellow("Disconnected")])\n" + + if(L.ckey && L.client) + if(L.client.inactivity >= (ROUNDSTART_LOGOUT_REPORT_TIME / 2)) //Connected, but inactive (alt+tabbed or something) + msg += "[L.name] ([L.ckey]), the [L.job] ([span_yellow("Connected, Inactive")])\n" + continue //AFK client + if(L.stat) + if(L.suiciding) //Suicider + msg += "[L.name] ([L.ckey]), the [L.job] ([span_red("Suicide")])\n" + continue //Disconnected client + if(L.stat == UNCONSCIOUS) + msg += "[L.name] ([L.ckey]), the [L.job] (Dying)\n" + continue //Unconscious + if(L.stat == DEAD) + msg += "[L.name] ([L.ckey]), the [L.job] (Dead)\n" + continue //Dead + + continue //Happy connected client + for(var/mob/observer/dead/D in mob_list) + if(D.mind && (D.mind.original == L || D.mind.current == L)) + if(L.stat == DEAD) + if(L.suiciding) //Suicider + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_red("Suicide")])\n" + continue //Disconnected client + else + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n" + continue //Dead mob, ghost abandoned + else + if(D.can_reenter_corpse) + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_red("Adminghosted")])\n" + continue //Lolwhat + else + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_red("Ghosted")])\n" + continue //Ghosted while alive + + msg += "" // close the span from right at the top + + for(var/mob/M in mob_list) + if(M.client && M.client.holder) + to_chat(M,msg) + +/proc/get_nt_opposed() + var/list/dudes = list() + for(var/mob/living/carbon/human/man in player_list) + if(man.client) + if(man.client.prefs.economic_status == CLASS_LOWER) + dudes += man + else if(man.client.prefs.economic_status == CLASS_LOWMID && prob(50)) + dudes += man + if(dudes.len == 0) return null + return pick(dudes) + +/proc/show_objectives(var/datum/mind/player) + + if(!player || !player.current) return + + var/obj_count = 1 + to_chat(player.current, "Your current objectives:") + for(var/datum/objective/objective in player.objectives) + to_chat(player.current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + +/mob/verb/check_round_info() + set name = "Check Round Info" + set category = "OOC" + + if(!ticker || !ticker.mode) + to_chat(usr, "Something is terribly wrong; there is no gametype.") + return + + if(master_mode != "secret") + to_chat(usr, "The roundtype is [capitalize(ticker.mode.name)]") + if(ticker.mode.round_description) + to_chat(usr, "[ticker.mode.round_description]") + if(ticker.mode.extended_round_description) + to_chat(usr, "[ticker.mode.extended_round_description]") + else + to_chat(usr, "Shhhh. It's a secret.") + return diff --git a/code/game/gamemodes/malfunction/malfunction.dm b/code/game/gamemodes/malfunction/malfunction.dm index 3a0ccec1241..26e0c4572e1 100644 --- a/code/game/gamemodes/malfunction/malfunction.dm +++ b/code/game/gamemodes/malfunction/malfunction.dm @@ -1,13 +1,13 @@ -/datum/game_mode/malfunction - name = "AI Malfunction" - round_description = "The AI is behaving abnormally and must be stopped." - extended_round_description = "The AI will attempt to hack the APCs around the station in order to gain as much control as possible." - config_tag = "malfunction" - required_players = 2 - required_players_secret = 2 - required_enemies = 1 - end_on_antag_death = 0 - auto_recall_shuttle = 0 - antag_tags = list(MODE_MALFUNCTION) - disabled_jobs = list("AI") - votable = 0 +/datum/game_mode/malfunction + name = "AI Malfunction" + round_description = "The AI is behaving abnormally and must be stopped." + extended_round_description = "The AI will attempt to hack the APCs around the station in order to gain as much control as possible." + config_tag = "malfunction" + required_players = 2 + required_players_secret = 2 + required_enemies = 1 + end_on_antag_death = 0 + auto_recall_shuttle = 0 + antag_tags = list(MODE_MALFUNCTION) + disabled_jobs = list("AI") + votable = 0 diff --git a/code/game/gamemodes/meme/meme.dm b/code/game/gamemodes/meme/meme.dm index d504fcf6a74..191e447813a 100644 --- a/code/game/gamemodes/meme/meme.dm +++ b/code/game/gamemodes/meme/meme.dm @@ -1,145 +1,145 @@ -//This file was auto-corrected by findeclaration.exe on 29/05/2012 15:03:04 - -/datum/game_mode/var/list/memes = list() - -/datum/game_mode/meme - name = "Memetic Anomaly" - config_tag = "meme" - required_players = 3 - required_players_secret = 10 - restricted_jobs = list("AI", "Cyborg") - recommended_enemies = 2 // need at least a meme and a host - votable = 0 // temporarily disable this mode for voting - end_on_antag_death = 1 - - var/var/list/datum/mind/first_hosts = list() - var/var/list/assigned_hosts = list() - - var/const/prob_int_murder_target = 50 // intercept names the assassination target half the time - var/const/prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target - var/const/prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target - - var/const/prob_int_item = 50 // intercept names the theft target half the time - var/const/prob_right_item_l = 25 // lower bound on probability of naming right theft target - var/const/prob_right_item_h = 50 // upper bound on probability of naming the right theft target - - var/const/prob_int_sab_target = 50 // intercept names the sabotage target half the time - var/const/prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target - var/const/prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target - - var/const/prob_right_killer_l = 25 //lower bound on probability of naming the right operative - var/const/prob_right_killer_h = 50 //upper bound on probability of naming the right operative - var/const/prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly - var/const/prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly - - var/const/waittime_l = 600 //lower bound on time before intercept arrives (in tenths of seconds) - var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds) - -/datum/game_mode/meme/announce() - to_world("The current game mode is - Meme!") - to_world("An unknown creature has infested the mind of a crew member. Find and destroy it by any means necessary.") - -/datum/game_mode/meme/can_start() - if(!..()) - return 0 - - // for every 10 players, get 1 meme, and for each meme, get a host - // also make sure that there's at least one meme and one host - recommended_enemies = max(src.num_players() / 20 * 2, 2) - - var/list/datum/mind/possible_memes = get_players_for_role(BE_MEME) - - if(possible_memes.len < 2) - log_admin("MODE FAILURE: MEME. NOT ENOUGH MEME CANDIDATES.") - return 0 // not enough candidates for meme - - // for each 2 possible memes, add one meme and one host - while(possible_memes.len >= 2) - var/datum/mind/meme = pick(possible_memes) - possible_memes.Remove(meme) - - var/datum/mind/first_host = pick(possible_memes) - possible_memes.Remove(first_host) - memes += meme - first_hosts += first_host - - // so that we can later know which host belongs to which meme - assigned_hosts[meme.key] = first_host - - meme.assigned_role = "MODE" //So they aren't chosen for other jobs. - meme.special_role = "Meme" - - return 1 - -/datum/game_mode/meme/pre_setup() - return 1 - - -/datum/game_mode/meme/post_setup() - // create a meme and enter it - for(var/datum/mind/meme in memes) - var/mob/living/parasite/meme/M = new - var/mob/original = meme.current - meme.transfer_to(M) - M.clearHUD() - - // get the host for this meme - var/datum/mind/first_host = assigned_hosts[meme.key] - // this is a redundant check, but I don't think the above works.. - // if picking hosts works with this method, remove the method above - if(!first_host) - first_host = pick(first_hosts) - first_hosts.Remove(first_host) - M.enter_host(first_host.current) - forge_meme_objectives(meme, first_host) - - qdel(original) - - log_admin("Created [memes.len] memes.") - - spawn (rand(waittime_l, waittime_h)) - send_intercept() - ..() - return - - -/datum/game_mode/proc/forge_meme_objectives(var/datum/mind/meme, var/datum/mind/first_host) - if (config.objectives_disabled) - return - - // meme always needs to attune X hosts - var/datum/objective/meme_attune/attune_objective = new - attune_objective.owner = meme - attune_objective.gen_amount_goal(3,6) - meme.objectives += attune_objective - - // generate some random objectives, use standard traitor objectives - var/job = first_host.assigned_role - - for(var/datum/objective/o in SelectObjectives(job, meme)) - o.owner = meme - meme.objectives += o - - greet_meme(meme) - - return - -/datum/game_mode/proc/greet_meme(var/datum/mind/meme, var/you_are=1) - if (you_are) - to_chat(meme.current, "You are a meme!") - show_objectives(meme) - return - -/datum/game_mode/meme/check_finished() - var/memes_alive = 0 - for(var/datum/mind/meme in memes) - if(!istype(meme.current,/mob/living)) - continue - if(meme.current.stat==2) - continue - memes_alive++ - - if (memes_alive) - return ..() - else - return 1 +//This file was auto-corrected by findeclaration.exe on 29/05/2012 15:03:04 + +/datum/game_mode/var/list/memes = list() + +/datum/game_mode/meme + name = "Memetic Anomaly" + config_tag = "meme" + required_players = 3 + required_players_secret = 10 + restricted_jobs = list("AI", "Cyborg") + recommended_enemies = 2 // need at least a meme and a host + votable = 0 // temporarily disable this mode for voting + end_on_antag_death = 1 + + var/var/list/datum/mind/first_hosts = list() + var/var/list/assigned_hosts = list() + + var/const/prob_int_murder_target = 50 // intercept names the assassination target half the time + var/const/prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target + var/const/prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target + + var/const/prob_int_item = 50 // intercept names the theft target half the time + var/const/prob_right_item_l = 25 // lower bound on probability of naming right theft target + var/const/prob_right_item_h = 50 // upper bound on probability of naming the right theft target + + var/const/prob_int_sab_target = 50 // intercept names the sabotage target half the time + var/const/prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target + var/const/prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target + + var/const/prob_right_killer_l = 25 //lower bound on probability of naming the right operative + var/const/prob_right_killer_h = 50 //upper bound on probability of naming the right operative + var/const/prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly + var/const/prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly + + var/const/waittime_l = 600 //lower bound on time before intercept arrives (in tenths of seconds) + var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds) + +/datum/game_mode/meme/announce() + to_world("The current game mode is - Meme!") + to_world("An unknown creature has infested the mind of a crew member. Find and destroy it by any means necessary.") + +/datum/game_mode/meme/can_start() + if(!..()) + return 0 + + // for every 10 players, get 1 meme, and for each meme, get a host + // also make sure that there's at least one meme and one host + recommended_enemies = max(src.num_players() / 20 * 2, 2) + + var/list/datum/mind/possible_memes = get_players_for_role(BE_MEME) + + if(possible_memes.len < 2) + log_admin("MODE FAILURE: MEME. NOT ENOUGH MEME CANDIDATES.") + return 0 // not enough candidates for meme + + // for each 2 possible memes, add one meme and one host + while(possible_memes.len >= 2) + var/datum/mind/meme = pick(possible_memes) + possible_memes.Remove(meme) + + var/datum/mind/first_host = pick(possible_memes) + possible_memes.Remove(first_host) + memes += meme + first_hosts += first_host + + // so that we can later know which host belongs to which meme + assigned_hosts[meme.key] = first_host + + meme.assigned_role = "MODE" //So they aren't chosen for other jobs. + meme.special_role = "Meme" + + return 1 + +/datum/game_mode/meme/pre_setup() + return 1 + + +/datum/game_mode/meme/post_setup() + // create a meme and enter it + for(var/datum/mind/meme in memes) + var/mob/living/parasite/meme/M = new + var/mob/original = meme.current + meme.transfer_to(M) + M.clearHUD() + + // get the host for this meme + var/datum/mind/first_host = assigned_hosts[meme.key] + // this is a redundant check, but I don't think the above works.. + // if picking hosts works with this method, remove the method above + if(!first_host) + first_host = pick(first_hosts) + first_hosts.Remove(first_host) + M.enter_host(first_host.current) + forge_meme_objectives(meme, first_host) + + qdel(original) + + log_admin("Created [memes.len] memes.") + + spawn (rand(waittime_l, waittime_h)) + send_intercept() + ..() + return + + +/datum/game_mode/proc/forge_meme_objectives(var/datum/mind/meme, var/datum/mind/first_host) + if (config.objectives_disabled) + return + + // meme always needs to attune X hosts + var/datum/objective/meme_attune/attune_objective = new + attune_objective.owner = meme + attune_objective.gen_amount_goal(3,6) + meme.objectives += attune_objective + + // generate some random objectives, use standard traitor objectives + var/job = first_host.assigned_role + + for(var/datum/objective/o in SelectObjectives(job, meme)) + o.owner = meme + meme.objectives += o + + greet_meme(meme) + + return + +/datum/game_mode/proc/greet_meme(var/datum/mind/meme, var/you_are=1) + if (you_are) + to_chat(meme.current, "You are a meme!") + show_objectives(meme) + return + +/datum/game_mode/meme/check_finished() + var/memes_alive = 0 + for(var/datum/mind/meme in memes) + if(!istype(meme.current,/mob/living)) + continue + if(meme.current.stat==2) + continue + memes_alive++ + + if (memes_alive) + return ..() + else + return 1 diff --git a/code/game/gamemodes/meteor/meteor.dm b/code/game/gamemodes/meteor/meteor.dm index 3c9244bc84e..57d5dadeacf 100644 --- a/code/game/gamemodes/meteor/meteor.dm +++ b/code/game/gamemodes/meteor/meteor.dm @@ -1,49 +1,49 @@ -#define METEOR_DELAY 6000 - -/datum/game_mode/meteor - name = "Meteor" - round_description = "The space station has been stuck in a major meteor shower." - extended_round_description = "The station is on an unavoidable collision course with an asteroid field. The station will be continuously slammed with meteors, venting hallways, rooms, and ultimately destroying a majority of the basic life functions of the entire structure. Coordinate with your fellow crew members to survive the inevitable destruction of the station and get back home in one piece!" - config_tag = "meteor" - required_players = 0 - votable = 0 - deny_respawn = 0 - var/next_wave = METEOR_DELAY - -/datum/game_mode/meteor/post_setup() - defer_powernet_rebuild = 2//Might help with the lag - ..() - -/datum/game_mode/meteor/process() - if(world.time >= next_wave) - next_wave = world.time + meteor_wave_delay - spawn() spawn_meteors(6, meteors_normal) - -/datum/game_mode/meteor/declare_completion() - var/text - var/survivors = 0 - for(var/mob/living/player in player_list) - if(player.stat != DEAD) - var/turf/location = get_turf(player.loc) - if(!location) continue - switch(location.loc.type) - if( /area/shuttle/escape/centcom ) - text += "
    [player.real_name] escaped on the emergency shuttle" - if( /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom ) - text += "
    [player.real_name] escaped in a life pod." - else - text += "
    [player.real_name] survived but is stranded without any hope of rescue." - survivors++ - - if(survivors) - to_world("The following survived the meteor storm:[text]") - else - to_world("Nobody survived the meteor storm!") - - feedback_set_details("round_end_result","end - evacuation") - feedback_set("round_end_result",survivors) - - ..() - return 1 - +#define METEOR_DELAY 6000 + +/datum/game_mode/meteor + name = "Meteor" + round_description = "The space station has been stuck in a major meteor shower." + extended_round_description = "The station is on an unavoidable collision course with an asteroid field. The station will be continuously slammed with meteors, venting hallways, rooms, and ultimately destroying a majority of the basic life functions of the entire structure. Coordinate with your fellow crew members to survive the inevitable destruction of the station and get back home in one piece!" + config_tag = "meteor" + required_players = 0 + votable = 0 + deny_respawn = 0 + var/next_wave = METEOR_DELAY + +/datum/game_mode/meteor/post_setup() + defer_powernet_rebuild = 2//Might help with the lag + ..() + +/datum/game_mode/meteor/process() + if(world.time >= next_wave) + next_wave = world.time + meteor_wave_delay + spawn() spawn_meteors(6, meteors_normal) + +/datum/game_mode/meteor/declare_completion() + var/text + var/survivors = 0 + for(var/mob/living/player in player_list) + if(player.stat != DEAD) + var/turf/location = get_turf(player.loc) + if(!location) continue + switch(location.loc.type) + if( /area/shuttle/escape/centcom ) + text += "
    [player.real_name] escaped on the emergency shuttle" + if( /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom ) + text += "
    [player.real_name] escaped in a life pod." + else + text += "
    [player.real_name] survived but is stranded without any hope of rescue." + survivors++ + + if(survivors) + to_world("The following survived the meteor storm:[text]") + else + to_world("Nobody survived the meteor storm!") + + feedback_set_details("round_end_result","end - evacuation") + feedback_set("round_end_result",survivors) + + ..() + return 1 + #undef METEOR_DELAY \ No newline at end of file diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 950910fe5fc..dc636e54dc7 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -1,345 +1,345 @@ -/var/const/meteor_wave_delay = 625 //minimum wait between waves in tenths of seconds -//set to at least 100 unless you want evarr ruining every round - -//Meteors probability of spawning during a given wave - -//for space dust event -/var/list/meteors_dust = list(/obj/effect/meteor/dust) - -//for normal meteor event -/var/list/meteors_normal = list( - /obj/effect/meteor/dust=3, - /obj/effect/meteor/medium=8, - /obj/effect/meteor/big=3, - /obj/effect/meteor/flaming=1, - /obj/effect/meteor/irradiated=3 - ) - -//for threatening meteor event -/var/list/meteors_threatening = list( - /obj/effect/meteor/medium=5, - /obj/effect/meteor/big=10, - /obj/effect/meteor/flaming=3, - /obj/effect/meteor/irradiated=3, - /obj/effect/meteor/emp=3) - -//for catastrophic meteor event -/var/list/meteors_catastrophic = list( - /obj/effect/meteor/medium=5, - /obj/effect/meteor/big=75, - /obj/effect/meteor/flaming=10, - /obj/effect/meteor/irradiated=10, - /obj/effect/meteor/emp=10) - - - -/////////////////////////////// -//Meteor spawning global procs -/////////////////////////////// - -/proc/spawn_meteors(var/number = 10, var/list/meteortypes, var/startSide, var/zlevel) - log_debug("Spawning [number] meteors on the [dir2text(startSide)] of [zlevel]") - for(var/i = 0; i < number; i++) - spawn_meteor(meteortypes, startSide, zlevel) - -/proc/spawn_meteor(var/list/meteortypes, var/startSide, var/startLevel) - if(isnull(startSide)) - startSide = pick(cardinal) - if(isnull(startLevel)) - startLevel = pick(using_map.station_levels - using_map.sealed_levels) - - var/turf/pickedstart = spaceDebrisStartLoc(startSide, startLevel) - var/turf/pickedgoal = spaceDebrisFinishLoc(startSide, startLevel) - - var/Me = pickweight(meteortypes) - var/obj/effect/meteor/M = new Me(pickedstart) - M.dest = pickedgoal - spawn(0) - walk_towards(M, M.dest, 3) //VOREStation Edit - Slower Meteors - return - -/proc/spaceDebrisStartLoc(startSide, Z) - var/starty - var/startx - switch(startSide) - if(NORTH) - starty = world.maxy-(TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - if(EAST) - starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) - startx = world.maxx-(TRANSITIONEDGE+1) - if(SOUTH) - starty = (TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - if(WEST) - starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) - startx = (TRANSITIONEDGE+1) - var/turf/T = locate(startx, starty, Z) - return T - -/proc/spaceDebrisFinishLoc(startSide, Z) - var/endy - var/endx - switch(startSide) - if(NORTH) - endy = TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(EAST) - endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) - endx = TRANSITIONEDGE - if(SOUTH) - endy = world.maxy-TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(WEST) - endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) - endx = world.maxx-TRANSITIONEDGE - var/turf/T = locate(endx, endy, Z) - return T - -// Override for special behavior when getting hit by meteors, and only meteors. Return one if the meteor hasn't been 'stopped'. -/atom/proc/handle_meteor_impact(var/obj/effect/meteor/meteor) - return TRUE - -/////////////////////// -//The meteor effect -////////////////////// - -/obj/effect/meteor - name = "the concept of meteor" - desc = "You should probably run instead of gawking at this." - icon = 'icons/obj/meteor.dmi' - icon_state = "small" - density = TRUE - anchored = TRUE - var/hits = 4 - var/hitpwr = 2 //Level of ex_act to be called on hit. - var/dest - pass_flags = PASSTABLE - var/heavy = FALSE - var/z_original - - var/meteordrop = /obj/item/weapon/ore/iron - var/dropamt = 2 - - // How much damage it does to walls, using take_damage(). - // Normal walls will die to 150 or more, where as reinforced walls need 800 to penetrate. Durasteel walls need 1200 damage to go through. - // Multiply this and the hits var to get a rough idea of how penetrating a meteor is. - var/wall_power = 100 - -/obj/effect/meteor/Initialize() - . = ..() - z_original = z - GLOB.meteor_list += src - -/obj/effect/meteor/Move() - if(z != z_original || loc == dest) - qdel(src) - return - - . = ..() //process movement... - -/obj/effect/meteor/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - var/turf/T = get_turf(loc) - ram_turf(T) - - if(prob(10) && !istype(T, /turf/space)) //randomly takes a 'hit' from ramming - get_hit() - -/obj/effect/meteor/Destroy() - walk(src,FALSE) //this cancels the walk_towards() proc - GLOB.meteor_list -= src - return ..() - -/obj/effect/meteor/New() - ..() - SpinAnimation() - -/obj/effect/meteor/Bump(atom/A) - if(attempt_vr(src,"Bump_vr",list(A))) return //VOREStation Edit - allows meteors to be deflected by baseball bats - if(A) - if(A.handle_meteor_impact(src)) // Used for special behaviour when getting hit specifically by a meteor, like a shield. - ram_turf(get_turf(A)) - get_hit() - else - die(FALSE) - -/obj/effect/meteor/CanPass(atom/movable/mover, turf/target) - return istype(mover, /obj/effect/meteor) ? TRUE : ..() - -/obj/effect/meteor/proc/ram_turf(var/turf/T) - //first bust whatever is in the turf - for(var/atom/A in T) - if(A == src) // Don't hit ourselves. - continue - if(isturf(A)) // Don't hit floors. We'll deal with walls later. - continue - A.ex_act(hitpwr) - - //then, ram the turf if it still exists - if(T) - if(istype(T, /turf/simulated/wall)) - var/turf/simulated/wall/W = T - W.take_damage(wall_power) // Stronger walls can halt asteroids. - -/obj/effect/meteor/proc/get_shield_damage() - return max(((max(hits, 2)) * (heavy + 1) * rand(6, 12)) / hitpwr , 0) - -//process getting 'hit' by colliding with a dense object -//or randomly when ramming turfs -/obj/effect/meteor/proc/get_hit() - hits-- - if(hits <= 0) - die(TRUE) - -/obj/effect/meteor/proc/die(var/explode = TRUE) - make_debris() - meteor_effect(explode) - qdel(src) - -/obj/effect/meteor/ex_act() - return - -/obj/effect/meteor/attackby(obj/item/weapon/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/weapon/pickaxe)) - qdel(src) - return - ..() - -/obj/effect/meteor/bullet_act(var/obj/item/projectile/Proj) - if(Proj.excavation_amount) - get_hit() - - if(!QDELETED(src)) - wall_power -= Proj.excavation_amount + Proj.damage + (Proj.hitscan * 25) // Instant-impact projectiles are inherently better at dealing with meteors. - - if(wall_power <= 0) - die(FALSE) // If you kill the meteor, then it dies. - return - return - -/obj/effect/meteor/proc/make_debris() - for(var/throws = dropamt, throws > 0, throws--) - var/obj/item/O = new meteordrop(get_turf(src)) - O.throw_at(dest, 5, 10) - -/obj/effect/meteor/proc/shake_players() - for(var/mob/M in player_list) - var/turf/T = get_turf(M) - if(!T || T.z != src.z) - continue - var/dist = get_dist(M.loc, src.loc) - shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3) - -/obj/effect/meteor/proc/meteor_effect(var/explode) - if(heavy) - shake_players() - - -/////////////////////// -//Meteor types -/////////////////////// - -// Dust breaks windows and hurts normal walls, generally more of an annoyance than a danger unless two happen to hit the same spot. -/obj/effect/meteor/dust - name = "space dust" - icon_state = "dust" - pass_flags = PASSTABLE | PASSGRILLE - hits = 1 - hitpwr = 3 - meteordrop = /obj/item/weapon/ore/glass - wall_power = 50 - -// Medium-sized meteors aren't very special and can be stopped easily by r-walls. -/obj/effect/meteor/medium - name = "meteor" - dropamt = 3 - wall_power = 200 - -/obj/effect/meteor/medium/meteor_effect(var/explode) - ..() - if(explode) - explosion(src.loc, 0, 1, 2, 3, 0) - -// Large-sized meteors generally pack the most punch, but are more concentrated towards the epicenter. -/obj/effect/meteor/big - name = "large meteor" - icon_state = "large" - hits = 8 - heavy = 1 - dropamt = 4 - wall_power = 400 - -/obj/effect/meteor/big/meteor_effect(var/explode) - ..() - if(explode) - explosion(src.loc, 1, 2, 3, 4, 0) - -// 'Flaming' meteors do less overall damage but are spread out more due to a larger but weaker explosion at the end. -/obj/effect/meteor/flaming - name = "flaming meteor" - icon_state = "flaming" - hits = 5 - heavy = 1 - meteordrop = /obj/item/weapon/ore/phoron - wall_power = 100 - -/obj/effect/meteor/flaming/meteor_effect(var/explode) - ..() - if(explode) - explosion(src.loc, 1, 2, 3, 4, 0, 0, 5) - -// Irradiated meteors do less physical damage but project a ten-tile ranged pulse of radiation upon exploding. -/obj/effect/meteor/irradiated - name = "glowing meteor" - icon_state = "glowing" - heavy = 1 - meteordrop = /obj/item/weapon/ore/uranium - wall_power = 75 - - -/obj/effect/meteor/irradiated/meteor_effect(var/explode) - ..() - if(explode) - explosion(src.loc, 0, 0, 4, 3, 0) - new /obj/effect/decal/cleanable/greenglow(get_turf(src)) - SSradiation.radiate(src, 50) - -// This meteor fries toasters. -/obj/effect/meteor/emp - name = "conducting meteor" - icon_state = "glowing_blue" - desc = "Hide your floppies!" - meteordrop = /obj/item/weapon/ore/osmium - dropamt = 3 - wall_power = 80 - -/obj/effect/meteor/emp/meteor_effect(var/explode) - ..() - // Best case scenario: Comparable to a low-yield EMP grenade. - // Worst case scenario: Comparable to a standard yield EMP grenade. - empulse(src, rand(1, 3), rand(2, 4), rand(3, 7), rand(5, 10)) - -/obj/effect/meteor/emp/get_shield_damage() - return ..() * rand(2,4) - -//Station buster Tunguska -/obj/effect/meteor/tunguska - name = "tunguska meteor" - icon_state = "flaming" - desc = "Your life briefly passes before your eyes the moment you lay them on this monstruosity" - hits = 30 - hitpwr = 1 - heavy = 1 - meteordrop = /obj/item/weapon/ore/phoron - wall_power = 150 - -/obj/effect/meteor/tunguska/meteor_effect(var/explode) - ..() - if(explode) - explosion(src.loc, 3, 6, 9, 20, 0) - -/obj/effect/meteor/tunguska/Bump() - ..() - if(prob(20)) - explosion(src.loc,2,4,6,8) +/var/const/meteor_wave_delay = 625 //minimum wait between waves in tenths of seconds +//set to at least 100 unless you want evarr ruining every round + +//Meteors probability of spawning during a given wave + +//for space dust event +/var/list/meteors_dust = list(/obj/effect/meteor/dust) + +//for normal meteor event +/var/list/meteors_normal = list( + /obj/effect/meteor/dust=3, + /obj/effect/meteor/medium=8, + /obj/effect/meteor/big=3, + /obj/effect/meteor/flaming=1, + /obj/effect/meteor/irradiated=3 + ) + +//for threatening meteor event +/var/list/meteors_threatening = list( + /obj/effect/meteor/medium=5, + /obj/effect/meteor/big=10, + /obj/effect/meteor/flaming=3, + /obj/effect/meteor/irradiated=3, + /obj/effect/meteor/emp=3) + +//for catastrophic meteor event +/var/list/meteors_catastrophic = list( + /obj/effect/meteor/medium=5, + /obj/effect/meteor/big=75, + /obj/effect/meteor/flaming=10, + /obj/effect/meteor/irradiated=10, + /obj/effect/meteor/emp=10) + + + +/////////////////////////////// +//Meteor spawning global procs +/////////////////////////////// + +/proc/spawn_meteors(var/number = 10, var/list/meteortypes, var/startSide, var/zlevel) + log_debug("Spawning [number] meteors on the [dir2text(startSide)] of [zlevel]") + for(var/i = 0; i < number; i++) + spawn_meteor(meteortypes, startSide, zlevel) + +/proc/spawn_meteor(var/list/meteortypes, var/startSide, var/startLevel) + if(isnull(startSide)) + startSide = pick(cardinal) + if(isnull(startLevel)) + startLevel = pick(using_map.station_levels - using_map.sealed_levels) + + var/turf/pickedstart = spaceDebrisStartLoc(startSide, startLevel) + var/turf/pickedgoal = spaceDebrisFinishLoc(startSide, startLevel) + + var/Me = pickweight(meteortypes) + var/obj/effect/meteor/M = new Me(pickedstart) + M.dest = pickedgoal + spawn(0) + walk_towards(M, M.dest, 3) //VOREStation Edit - Slower Meteors + return + +/proc/spaceDebrisStartLoc(startSide, Z) + var/starty + var/startx + switch(startSide) + if(NORTH) + starty = world.maxy-(TRANSITIONEDGE+1) + startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + if(EAST) + starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) + startx = world.maxx-(TRANSITIONEDGE+1) + if(SOUTH) + starty = (TRANSITIONEDGE+1) + startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + if(WEST) + starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) + startx = (TRANSITIONEDGE+1) + var/turf/T = locate(startx, starty, Z) + return T + +/proc/spaceDebrisFinishLoc(startSide, Z) + var/endy + var/endx + switch(startSide) + if(NORTH) + endy = TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(EAST) + endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) + endx = TRANSITIONEDGE + if(SOUTH) + endy = world.maxy-TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(WEST) + endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) + endx = world.maxx-TRANSITIONEDGE + var/turf/T = locate(endx, endy, Z) + return T + +// Override for special behavior when getting hit by meteors, and only meteors. Return one if the meteor hasn't been 'stopped'. +/atom/proc/handle_meteor_impact(var/obj/effect/meteor/meteor) + return TRUE + +/////////////////////// +//The meteor effect +////////////////////// + +/obj/effect/meteor + name = "the concept of meteor" + desc = "You should probably run instead of gawking at this." + icon = 'icons/obj/meteor.dmi' + icon_state = "small" + density = TRUE + anchored = TRUE + var/hits = 4 + var/hitpwr = 2 //Level of ex_act to be called on hit. + var/dest + pass_flags = PASSTABLE + var/heavy = FALSE + var/z_original + + var/meteordrop = /obj/item/weapon/ore/iron + var/dropamt = 2 + + // How much damage it does to walls, using take_damage(). + // Normal walls will die to 150 or more, where as reinforced walls need 800 to penetrate. Durasteel walls need 1200 damage to go through. + // Multiply this and the hits var to get a rough idea of how penetrating a meteor is. + var/wall_power = 100 + +/obj/effect/meteor/Initialize() + . = ..() + z_original = z + GLOB.meteor_list += src + +/obj/effect/meteor/Move() + if(z != z_original || loc == dest) + qdel(src) + return + + . = ..() //process movement... + +/obj/effect/meteor/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + var/turf/T = get_turf(loc) + ram_turf(T) + + if(prob(10) && !istype(T, /turf/space)) //randomly takes a 'hit' from ramming + get_hit() + +/obj/effect/meteor/Destroy() + walk(src,FALSE) //this cancels the walk_towards() proc + GLOB.meteor_list -= src + return ..() + +/obj/effect/meteor/New() + ..() + SpinAnimation() + +/obj/effect/meteor/Bump(atom/A) + if(attempt_vr(src,"Bump_vr",list(A))) return //VOREStation Edit - allows meteors to be deflected by baseball bats + if(A) + if(A.handle_meteor_impact(src)) // Used for special behaviour when getting hit specifically by a meteor, like a shield. + ram_turf(get_turf(A)) + get_hit() + else + die(FALSE) + +/obj/effect/meteor/CanPass(atom/movable/mover, turf/target) + return istype(mover, /obj/effect/meteor) ? TRUE : ..() + +/obj/effect/meteor/proc/ram_turf(var/turf/T) + //first bust whatever is in the turf + for(var/atom/A in T) + if(A == src) // Don't hit ourselves. + continue + if(isturf(A)) // Don't hit floors. We'll deal with walls later. + continue + A.ex_act(hitpwr) + + //then, ram the turf if it still exists + if(T) + if(istype(T, /turf/simulated/wall)) + var/turf/simulated/wall/W = T + W.take_damage(wall_power) // Stronger walls can halt asteroids. + +/obj/effect/meteor/proc/get_shield_damage() + return max(((max(hits, 2)) * (heavy + 1) * rand(6, 12)) / hitpwr , 0) + +//process getting 'hit' by colliding with a dense object +//or randomly when ramming turfs +/obj/effect/meteor/proc/get_hit() + hits-- + if(hits <= 0) + die(TRUE) + +/obj/effect/meteor/proc/die(var/explode = TRUE) + make_debris() + meteor_effect(explode) + qdel(src) + +/obj/effect/meteor/ex_act() + return + +/obj/effect/meteor/attackby(obj/item/weapon/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/weapon/pickaxe)) + qdel(src) + return + ..() + +/obj/effect/meteor/bullet_act(var/obj/item/projectile/Proj) + if(Proj.excavation_amount) + get_hit() + + if(!QDELETED(src)) + wall_power -= Proj.excavation_amount + Proj.damage + (Proj.hitscan * 25) // Instant-impact projectiles are inherently better at dealing with meteors. + + if(wall_power <= 0) + die(FALSE) // If you kill the meteor, then it dies. + return + return + +/obj/effect/meteor/proc/make_debris() + for(var/throws = dropamt, throws > 0, throws--) + var/obj/item/O = new meteordrop(get_turf(src)) + O.throw_at(dest, 5, 10) + +/obj/effect/meteor/proc/shake_players() + for(var/mob/M in player_list) + var/turf/T = get_turf(M) + if(!T || T.z != src.z) + continue + var/dist = get_dist(M.loc, src.loc) + shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3) + +/obj/effect/meteor/proc/meteor_effect(var/explode) + if(heavy) + shake_players() + + +/////////////////////// +//Meteor types +/////////////////////// + +// Dust breaks windows and hurts normal walls, generally more of an annoyance than a danger unless two happen to hit the same spot. +/obj/effect/meteor/dust + name = "space dust" + icon_state = "dust" + pass_flags = PASSTABLE | PASSGRILLE + hits = 1 + hitpwr = 3 + meteordrop = /obj/item/weapon/ore/glass + wall_power = 50 + +// Medium-sized meteors aren't very special and can be stopped easily by r-walls. +/obj/effect/meteor/medium + name = "meteor" + dropamt = 3 + wall_power = 200 + +/obj/effect/meteor/medium/meteor_effect(var/explode) + ..() + if(explode) + explosion(src.loc, 0, 1, 2, 3, 0) + +// Large-sized meteors generally pack the most punch, but are more concentrated towards the epicenter. +/obj/effect/meteor/big + name = "large meteor" + icon_state = "large" + hits = 8 + heavy = 1 + dropamt = 4 + wall_power = 400 + +/obj/effect/meteor/big/meteor_effect(var/explode) + ..() + if(explode) + explosion(src.loc, 1, 2, 3, 4, 0) + +// 'Flaming' meteors do less overall damage but are spread out more due to a larger but weaker explosion at the end. +/obj/effect/meteor/flaming + name = "flaming meteor" + icon_state = "flaming" + hits = 5 + heavy = 1 + meteordrop = /obj/item/weapon/ore/phoron + wall_power = 100 + +/obj/effect/meteor/flaming/meteor_effect(var/explode) + ..() + if(explode) + explosion(src.loc, 1, 2, 3, 4, 0, 0, 5) + +// Irradiated meteors do less physical damage but project a ten-tile ranged pulse of radiation upon exploding. +/obj/effect/meteor/irradiated + name = "glowing meteor" + icon_state = "glowing" + heavy = 1 + meteordrop = /obj/item/weapon/ore/uranium + wall_power = 75 + + +/obj/effect/meteor/irradiated/meteor_effect(var/explode) + ..() + if(explode) + explosion(src.loc, 0, 0, 4, 3, 0) + new /obj/effect/decal/cleanable/greenglow(get_turf(src)) + SSradiation.radiate(src, 50) + +// This meteor fries toasters. +/obj/effect/meteor/emp + name = "conducting meteor" + icon_state = "glowing_blue" + desc = "Hide your floppies!" + meteordrop = /obj/item/weapon/ore/osmium + dropamt = 3 + wall_power = 80 + +/obj/effect/meteor/emp/meteor_effect(var/explode) + ..() + // Best case scenario: Comparable to a low-yield EMP grenade. + // Worst case scenario: Comparable to a standard yield EMP grenade. + empulse(src, rand(1, 3), rand(2, 4), rand(3, 7), rand(5, 10)) + +/obj/effect/meteor/emp/get_shield_damage() + return ..() * rand(2,4) + +//Station buster Tunguska +/obj/effect/meteor/tunguska + name = "tunguska meteor" + icon_state = "flaming" + desc = "Your life briefly passes before your eyes the moment you lay them on this monstruosity" + hits = 30 + hitpwr = 1 + heavy = 1 + meteordrop = /obj/item/weapon/ore/phoron + wall_power = 150 + +/obj/effect/meteor/tunguska/meteor_effect(var/explode) + ..() + if(explode) + explosion(src.loc, 3, 6, 9, 20, 0) + +/obj/effect/meteor/tunguska/Bump() + ..() + if(prob(20)) + explosion(src.loc,2,4,6,8) diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index a0a5cb6fe89..f6f2d1f68d3 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -1,93 +1,93 @@ -/* - MERCENARY ROUNDTYPE -*/ - -var/list/nuke_disks = list() - -/datum/game_mode/nuclear - name = "Mercenary" - round_description = "A mercenary strike force is approaching the station!" - extended_round_description = "The Company's majority control of phoron in the system has marked the \ - station to be a highly valuable target for many competing organizations and individuals. Being a \ - colony of sizable population and considerable wealth causes it to often be the target of various \ - attempts of robbery, fraud and other malicious actions." - config_tag = "mercenary" - required_players = 12 - required_players_secret = 12 - required_enemies = 3 - end_on_antag_death = 0 - var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station - var/syndies_didnt_escape = 0 //Used for tracking if the syndies got the shuttle off of the z-level - antag_tags = list(MODE_MERCENARY) - -//delete all nuke disks not on a station zlevel -/datum/game_mode/nuclear/proc/check_nuke_disks() - for(var/obj/item/weapon/disk/nuclear/N in nuke_disks) - if(isNotStationLevel(N.z)) qdel(N) - -//checks if L has a nuke disk on their person -/datum/game_mode/nuclear/proc/check_mob(mob/living/L) - for(var/obj/item/weapon/disk/nuclear/N in nuke_disks) - if(N.storage_depth(L) >= 0) - return 1 - return 0 - -/datum/game_mode/nuclear/declare_completion() - if(config.objectives_disabled) - ..() - return - var/disk_rescued = 1 - for(var/obj/item/weapon/disk/nuclear/D in nuke_disks) - var/disk_area = get_area(D) - if(!is_type_in_list(disk_area, centcom_areas)) - disk_rescued = 0 - break - var/crew_evacuated = (emergency_shuttle.returned()) - - if(!disk_rescued && station_was_nuked && !syndies_didnt_escape) - feedback_set_details("round_end_result","win - syndicate nuke") - to_world("Mercenary Major Victory!") - to_world("[syndicate_name()] operatives have destroyed [station_name()]!") - - else if (!disk_rescued && station_was_nuked && syndies_didnt_escape) - feedback_set_details("round_end_result","halfwin - syndicate nuke - did not evacuate in time") - to_world("Total Annihilation") - to_world("[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!") - - else if (!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape) - feedback_set_details("round_end_result","halfwin - blew wrong station") - to_world("Crew Minor Victory") - to_world("[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!") - - else if (!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape) - feedback_set_details("round_end_result","halfwin - blew wrong station - did not evacuate in time") - to_world("[syndicate_name()] operatives have earned Darwin Award!") - to_world("[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!") - - else if (disk_rescued && mercs.antags_are_dead()) - feedback_set_details("round_end_result","loss - evacuation - disk secured - syndi team dead") - to_world("Crew Major Victory!") - to_world("The Research Staff has saved the disc and killed the [syndicate_name()] Operatives") - - else if ( disk_rescued ) - feedback_set_details("round_end_result","loss - evacuation - disk secured") - to_world("Crew Major Victory") - to_world("The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!") - - else if (!disk_rescued && mercs.antags_are_dead()) - feedback_set_details("round_end_result","loss - evacuation - disk not secured") - to_world("Mercenary Minor Victory!") - to_world("The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!") - - else if (!disk_rescued && crew_evacuated) - feedback_set_details("round_end_result","halfwin - detonation averted") - to_world("Mercenary Minor Victory!") - to_world("[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!") - - else if (!disk_rescued && !crew_evacuated) - feedback_set_details("round_end_result","halfwin - interrupted") - to_world("Neutral Victory") - to_world("Round was mysteriously interrupted!") - - ..() - return +/* + MERCENARY ROUNDTYPE +*/ + +var/list/nuke_disks = list() + +/datum/game_mode/nuclear + name = "Mercenary" + round_description = "A mercenary strike force is approaching the station!" + extended_round_description = "The Company's majority control of phoron in the system has marked the \ + station to be a highly valuable target for many competing organizations and individuals. Being a \ + colony of sizable population and considerable wealth causes it to often be the target of various \ + attempts of robbery, fraud and other malicious actions." + config_tag = "mercenary" + required_players = 12 + required_players_secret = 12 + required_enemies = 3 + end_on_antag_death = 0 + var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station + var/syndies_didnt_escape = 0 //Used for tracking if the syndies got the shuttle off of the z-level + antag_tags = list(MODE_MERCENARY) + +//delete all nuke disks not on a station zlevel +/datum/game_mode/nuclear/proc/check_nuke_disks() + for(var/obj/item/weapon/disk/nuclear/N in nuke_disks) + if(isNotStationLevel(N.z)) qdel(N) + +//checks if L has a nuke disk on their person +/datum/game_mode/nuclear/proc/check_mob(mob/living/L) + for(var/obj/item/weapon/disk/nuclear/N in nuke_disks) + if(N.storage_depth(L) >= 0) + return 1 + return 0 + +/datum/game_mode/nuclear/declare_completion() + if(config.objectives_disabled) + ..() + return + var/disk_rescued = 1 + for(var/obj/item/weapon/disk/nuclear/D in nuke_disks) + var/disk_area = get_area(D) + if(!is_type_in_list(disk_area, centcom_areas)) + disk_rescued = 0 + break + var/crew_evacuated = (emergency_shuttle.returned()) + + if(!disk_rescued && station_was_nuked && !syndies_didnt_escape) + feedback_set_details("round_end_result","win - syndicate nuke") + to_world("Mercenary Major Victory!") + to_world("[syndicate_name()] operatives have destroyed [station_name()]!") + + else if (!disk_rescued && station_was_nuked && syndies_didnt_escape) + feedback_set_details("round_end_result","halfwin - syndicate nuke - did not evacuate in time") + to_world("Total Annihilation") + to_world("[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!") + + else if (!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape) + feedback_set_details("round_end_result","halfwin - blew wrong station") + to_world("Crew Minor Victory") + to_world("[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!") + + else if (!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape) + feedback_set_details("round_end_result","halfwin - blew wrong station - did not evacuate in time") + to_world("[syndicate_name()] operatives have earned Darwin Award!") + to_world("[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!") + + else if (disk_rescued && mercs.antags_are_dead()) + feedback_set_details("round_end_result","loss - evacuation - disk secured - syndi team dead") + to_world("Crew Major Victory!") + to_world("The Research Staff has saved the disc and killed the [syndicate_name()] Operatives") + + else if ( disk_rescued ) + feedback_set_details("round_end_result","loss - evacuation - disk secured") + to_world("Crew Major Victory") + to_world("The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!") + + else if (!disk_rescued && mercs.antags_are_dead()) + feedback_set_details("round_end_result","loss - evacuation - disk not secured") + to_world("Mercenary Minor Victory!") + to_world("The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!") + + else if (!disk_rescued && crew_evacuated) + feedback_set_details("round_end_result","halfwin - detonation averted") + to_world("Mercenary Minor Victory!") + to_world("[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!") + + else if (!disk_rescued && !crew_evacuated) + feedback_set_details("round_end_result","halfwin - interrupted") + to_world("Neutral Victory") + to_world("Round was mysteriously interrupted!") + + ..() + return diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm index 7c51611bf2d..2a60b7ef8f0 100644 --- a/code/game/gamemodes/nuclear/pinpointer.dm +++ b/code/game/gamemodes/nuclear/pinpointer.dm @@ -1,317 +1,317 @@ -/obj/item/weapon/pinpointer - name = "pinpointer" - icon = 'icons/obj/device.dmi' - icon_state = "pinoff" - slot_flags = SLOT_BELT - w_class = ITEMSIZE_SMALL - item_state = "electronic" - throw_speed = 4 - throw_range = 20 - matter = list(MAT_STEEL = 500) - preserve_item = 1 - var/obj/item/weapon/disk/nuclear/the_disk = null - var/active = 0 - -/obj/item/weapon/pinpointer/Destroy() - active = 0 - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/weapon/pinpointer/attack_self() - if(!active) - active = 1 - START_PROCESSING(SSobj, src) - to_chat(usr, "You activate the pinpointer") - else - active = 0 - STOP_PROCESSING(SSobj, src) - icon_state = "pinoff" - to_chat(usr, "You deactivate the pinpointer") - -/obj/item/weapon/pinpointer/process() - if(!active) - return PROCESS_KILL - - if(!the_disk) - the_disk = locate() - if(!the_disk) - icon_state = "pinonnull" - return - - set_dir(get_dir(src,the_disk)) - - switch(get_dist(src,the_disk)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - -/obj/item/weapon/pinpointer/examine(mob/user) - . = ..() - for(var/obj/machinery/nuclearbomb/bomb in machines) - if(bomb.timing) - . += "Extreme danger. Arming signal detected. Time remaining: [bomb.timeleft]" - - - -/obj/item/weapon/pinpointer/advpinpointer - name = "Advanced Pinpointer" - icon = 'icons/obj/device.dmi' - desc = "A larger version of the normal pinpointer, this unit features a helpful quantum entanglement detection system to locate various objects that do not broadcast a locator signal." - var/mode = 0 // Mode 0 locates disk, mode 1 locates coordinates. - var/turf/location = null - var/obj/target = null - -/obj/item/weapon/pinpointer/advpinpointer/process() - if(!active) - return PROCESS_KILL - if(mode == 0) - ..() - if(mode == 1) - worklocation() - if(mode == 2) - workobj() - -/obj/item/weapon/pinpointer/advpinpointer/proc/worklocation() - if(!location) - icon_state = "pinonnull" - return - - set_dir(get_dir(src,location)) - - switch(get_dist(src,location)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - -/obj/item/weapon/pinpointer/advpinpointer/proc/workobj() - if(!target) - icon_state = "pinonnull" - return - - set_dir(get_dir(src,target)) - - switch(get_dist(src,target)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - -/obj/item/weapon/pinpointer/advpinpointer/verb/toggle_mode() - set category = "Object" - set name = "Toggle Pinpointer Mode" - set src in view(1) - - active = 0 - icon_state = "pinoff" - target=null - location = null - - switch(tgui_alert(usr, "Please select the mode you want to put the pinpointer in.", "Pinpointer Mode Select", list("Location", "Disk Recovery", "Other Signature"))) - if("Location") - mode = 1 - - var/locationx = tgui_input_number(usr, "Please input the x coordinate to search for.", "Location?" , "") - if(!locationx || !(usr in view(1,src))) - return - var/locationy = tgui_input_number(usr, "Please input the y coordinate to search for.", "Location?" , "") - if(!locationy || !(usr in view(1,src))) - return - - var/turf/Z = get_turf(src) - - location = locate(locationx,locationy,Z.z) - - to_chat(usr, "You set the pinpointer to locate [locationx],[locationy]") - - return attack_self() - - if("Disk Recovery") - mode = 0 - return attack_self() - - if("Other Signature") - mode = 2 - switch(tgui_alert(usr, "Search for item signature or DNA fragment?", "Signature Mode Select", list("Item", "DNA"))) - - if("Item") - var/datum/objective/steal/itemlist - itemlist = itemlist - var/targetitem = tgui_input_list(usr, "Select item to search for.", "Item Mode Select", itemlist.possible_items) - if(!targetitem) - return - target=locate(itemlist.possible_items[targetitem]) - if(!target) - to_chat(usr, "Failed to locate [targetitem]!") - return - to_chat(usr, "You set the pinpointer to locate [targetitem]") - - if("DNA") - var/DNAstring = tgui_input_text(usr, "Input DNA string to search for." , "Please Enter String." , "") - if(!DNAstring) - return - for(var/mob/living/carbon/M in mob_list) - if(!M.dna) - continue - if(M.dna.unique_enzymes == DNAstring) - target = M - break - - return attack_self() - - -/////////////////////// -//nuke op pinpointers// -/////////////////////// - - -/obj/item/weapon/pinpointer/nukeop - var/mode = 0 //Mode 0 locates disk, mode 1 locates the shuttle - var/obj/machinery/computer/shuttle_control/multi/syndicate/home = null - -/obj/item/weapon/pinpointer/nukeop/attack_self(mob/user as mob) - if(!active) - active = 1 - START_PROCESSING(SSobj, src) - if(!mode) - workdisk() - to_chat(user, "Authentication Disk Locator active.") - else - worklocation() - to_chat(user, "Shuttle Locator active.") - else - active = 0 - STOP_PROCESSING(SSobj, src) - icon_state = "pinoff" - to_chat(user, "You deactivate the pinpointer.") - -/obj/item/weapon/pinpointer/nukeop/process() - if(!active) - return PROCESS_KILL - - switch(mode) - if(0) - workdisk() - if(1) - worklocation() - -/obj/item/weapon/pinpointer/nukeop/proc/workdisk() - if(bomb_set) //If the bomb is set, lead to the shuttle - mode = 1 //Ensures worklocation() continues to work - playsound(src, 'sound/machines/twobeep.ogg', 50, 1) //Plays a beep - visible_message("Shuttle Locator active.") //Lets the mob holding it know that the mode has changed - return //Get outta here - - if(!the_disk) - the_disk = locate() - if(!the_disk) - icon_state = "pinonnull" - return - - set_dir(get_dir(src, the_disk)) - - switch(get_dist(src, the_disk)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - -/obj/item/weapon/pinpointer/nukeop/proc/worklocation() - if(!bomb_set) - mode = 0 - playsound(src, 'sound/machines/twobeep.ogg', 50, 1) - visible_message("Authentication Disk Locator active.") - return - - if(!home) - home = locate() - if(!home) - icon_state = "pinonnull" - return - - if(loc.z != home.z) //If you are on a different z-level from the shuttle - icon_state = "pinonnull" - - else - set_dir(get_dir(src, home)) - - switch(get_dist(src, home)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - - -// This one only points to the ship. Useful if there is no nuking to occur today. -/obj/item/weapon/pinpointer/shuttle - var/shuttle_comp_id = null - var/obj/machinery/computer/shuttle_control/our_shuttle = null - -/obj/item/weapon/pinpointer/shuttle/attack_self(mob/user as mob) - if(!active) - active = TRUE - START_PROCESSING(SSobj, src) - to_chat(user, "Shuttle Locator active.") - else - active = FALSE - STOP_PROCESSING(SSobj, src) - icon_state = "pinoff" - to_chat(user, "You deactivate the pinpointer.") - -/obj/item/weapon/pinpointer/shuttle/process() - if(!active) - return PROCESS_KILL - - if(!our_shuttle) - for(var/obj/machinery/computer/shuttle_control/S in machines) - if(S.shuttle_tag == shuttle_comp_id) // Shuttle tags are used so that it will work if the computer path changes, as it does on the southern cross map. - our_shuttle = S - break - - if(!our_shuttle) - icon_state = "pinonnull" - return - - if(loc.z != our_shuttle.z) //If you are on a different z-level from the shuttle - icon_state = "pinonnull" - - else - set_dir(get_dir(src, our_shuttle)) - - switch(get_dist(src, our_shuttle)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - - -/obj/item/weapon/pinpointer/shuttle/merc - shuttle_comp_id = "Mercenary" - -/obj/item/weapon/pinpointer/shuttle/heist +/obj/item/weapon/pinpointer + name = "pinpointer" + icon = 'icons/obj/device.dmi' + icon_state = "pinoff" + slot_flags = SLOT_BELT + w_class = ITEMSIZE_SMALL + item_state = "electronic" + throw_speed = 4 + throw_range = 20 + matter = list(MAT_STEEL = 500) + preserve_item = 1 + var/obj/item/weapon/disk/nuclear/the_disk = null + var/active = 0 + +/obj/item/weapon/pinpointer/Destroy() + active = 0 + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/weapon/pinpointer/attack_self() + if(!active) + active = 1 + START_PROCESSING(SSobj, src) + to_chat(usr, "You activate the pinpointer") + else + active = 0 + STOP_PROCESSING(SSobj, src) + icon_state = "pinoff" + to_chat(usr, "You deactivate the pinpointer") + +/obj/item/weapon/pinpointer/process() + if(!active) + return PROCESS_KILL + + if(!the_disk) + the_disk = locate() + if(!the_disk) + icon_state = "pinonnull" + return + + set_dir(get_dir(src,the_disk)) + + switch(get_dist(src,the_disk)) + if(0) + icon_state = "pinondirect" + if(1 to 8) + icon_state = "pinonclose" + if(9 to 16) + icon_state = "pinonmedium" + if(16 to INFINITY) + icon_state = "pinonfar" + +/obj/item/weapon/pinpointer/examine(mob/user) + . = ..() + for(var/obj/machinery/nuclearbomb/bomb in machines) + if(bomb.timing) + . += "Extreme danger. Arming signal detected. Time remaining: [bomb.timeleft]" + + + +/obj/item/weapon/pinpointer/advpinpointer + name = "Advanced Pinpointer" + icon = 'icons/obj/device.dmi' + desc = "A larger version of the normal pinpointer, this unit features a helpful quantum entanglement detection system to locate various objects that do not broadcast a locator signal." + var/mode = 0 // Mode 0 locates disk, mode 1 locates coordinates. + var/turf/location = null + var/obj/target = null + +/obj/item/weapon/pinpointer/advpinpointer/process() + if(!active) + return PROCESS_KILL + if(mode == 0) + ..() + if(mode == 1) + worklocation() + if(mode == 2) + workobj() + +/obj/item/weapon/pinpointer/advpinpointer/proc/worklocation() + if(!location) + icon_state = "pinonnull" + return + + set_dir(get_dir(src,location)) + + switch(get_dist(src,location)) + if(0) + icon_state = "pinondirect" + if(1 to 8) + icon_state = "pinonclose" + if(9 to 16) + icon_state = "pinonmedium" + if(16 to INFINITY) + icon_state = "pinonfar" + +/obj/item/weapon/pinpointer/advpinpointer/proc/workobj() + if(!target) + icon_state = "pinonnull" + return + + set_dir(get_dir(src,target)) + + switch(get_dist(src,target)) + if(0) + icon_state = "pinondirect" + if(1 to 8) + icon_state = "pinonclose" + if(9 to 16) + icon_state = "pinonmedium" + if(16 to INFINITY) + icon_state = "pinonfar" + +/obj/item/weapon/pinpointer/advpinpointer/verb/toggle_mode() + set category = "Object" + set name = "Toggle Pinpointer Mode" + set src in view(1) + + active = 0 + icon_state = "pinoff" + target=null + location = null + + switch(tgui_alert(usr, "Please select the mode you want to put the pinpointer in.", "Pinpointer Mode Select", list("Location", "Disk Recovery", "Other Signature"))) + if("Location") + mode = 1 + + var/locationx = tgui_input_number(usr, "Please input the x coordinate to search for.", "Location?" , "") + if(!locationx || !(usr in view(1,src))) + return + var/locationy = tgui_input_number(usr, "Please input the y coordinate to search for.", "Location?" , "") + if(!locationy || !(usr in view(1,src))) + return + + var/turf/Z = get_turf(src) + + location = locate(locationx,locationy,Z.z) + + to_chat(usr, "You set the pinpointer to locate [locationx],[locationy]") + + return attack_self() + + if("Disk Recovery") + mode = 0 + return attack_self() + + if("Other Signature") + mode = 2 + switch(tgui_alert(usr, "Search for item signature or DNA fragment?", "Signature Mode Select", list("Item", "DNA"))) + + if("Item") + var/datum/objective/steal/itemlist + itemlist = itemlist + var/targetitem = tgui_input_list(usr, "Select item to search for.", "Item Mode Select", itemlist.possible_items) + if(!targetitem) + return + target=locate(itemlist.possible_items[targetitem]) + if(!target) + to_chat(usr, "Failed to locate [targetitem]!") + return + to_chat(usr, "You set the pinpointer to locate [targetitem]") + + if("DNA") + var/DNAstring = tgui_input_text(usr, "Input DNA string to search for." , "Please Enter String." , "") + if(!DNAstring) + return + for(var/mob/living/carbon/M in mob_list) + if(!M.dna) + continue + if(M.dna.unique_enzymes == DNAstring) + target = M + break + + return attack_self() + + +/////////////////////// +//nuke op pinpointers// +/////////////////////// + + +/obj/item/weapon/pinpointer/nukeop + var/mode = 0 //Mode 0 locates disk, mode 1 locates the shuttle + var/obj/machinery/computer/shuttle_control/multi/syndicate/home = null + +/obj/item/weapon/pinpointer/nukeop/attack_self(mob/user as mob) + if(!active) + active = 1 + START_PROCESSING(SSobj, src) + if(!mode) + workdisk() + to_chat(user, "Authentication Disk Locator active.") + else + worklocation() + to_chat(user, "Shuttle Locator active.") + else + active = 0 + STOP_PROCESSING(SSobj, src) + icon_state = "pinoff" + to_chat(user, "You deactivate the pinpointer.") + +/obj/item/weapon/pinpointer/nukeop/process() + if(!active) + return PROCESS_KILL + + switch(mode) + if(0) + workdisk() + if(1) + worklocation() + +/obj/item/weapon/pinpointer/nukeop/proc/workdisk() + if(bomb_set) //If the bomb is set, lead to the shuttle + mode = 1 //Ensures worklocation() continues to work + playsound(src, 'sound/machines/twobeep.ogg', 50, 1) //Plays a beep + visible_message("Shuttle Locator active.") //Lets the mob holding it know that the mode has changed + return //Get outta here + + if(!the_disk) + the_disk = locate() + if(!the_disk) + icon_state = "pinonnull" + return + + set_dir(get_dir(src, the_disk)) + + switch(get_dist(src, the_disk)) + if(0) + icon_state = "pinondirect" + if(1 to 8) + icon_state = "pinonclose" + if(9 to 16) + icon_state = "pinonmedium" + if(16 to INFINITY) + icon_state = "pinonfar" + +/obj/item/weapon/pinpointer/nukeop/proc/worklocation() + if(!bomb_set) + mode = 0 + playsound(src, 'sound/machines/twobeep.ogg', 50, 1) + visible_message("Authentication Disk Locator active.") + return + + if(!home) + home = locate() + if(!home) + icon_state = "pinonnull" + return + + if(loc.z != home.z) //If you are on a different z-level from the shuttle + icon_state = "pinonnull" + + else + set_dir(get_dir(src, home)) + + switch(get_dist(src, home)) + if(0) + icon_state = "pinondirect" + if(1 to 8) + icon_state = "pinonclose" + if(9 to 16) + icon_state = "pinonmedium" + if(16 to INFINITY) + icon_state = "pinonfar" + + +// This one only points to the ship. Useful if there is no nuking to occur today. +/obj/item/weapon/pinpointer/shuttle + var/shuttle_comp_id = null + var/obj/machinery/computer/shuttle_control/our_shuttle = null + +/obj/item/weapon/pinpointer/shuttle/attack_self(mob/user as mob) + if(!active) + active = TRUE + START_PROCESSING(SSobj, src) + to_chat(user, "Shuttle Locator active.") + else + active = FALSE + STOP_PROCESSING(SSobj, src) + icon_state = "pinoff" + to_chat(user, "You deactivate the pinpointer.") + +/obj/item/weapon/pinpointer/shuttle/process() + if(!active) + return PROCESS_KILL + + if(!our_shuttle) + for(var/obj/machinery/computer/shuttle_control/S in machines) + if(S.shuttle_tag == shuttle_comp_id) // Shuttle tags are used so that it will work if the computer path changes, as it does on the southern cross map. + our_shuttle = S + break + + if(!our_shuttle) + icon_state = "pinonnull" + return + + if(loc.z != our_shuttle.z) //If you are on a different z-level from the shuttle + icon_state = "pinonnull" + + else + set_dir(get_dir(src, our_shuttle)) + + switch(get_dist(src, our_shuttle)) + if(0) + icon_state = "pinondirect" + if(1 to 8) + icon_state = "pinonclose" + if(9 to 16) + icon_state = "pinonmedium" + if(16 to INFINITY) + icon_state = "pinonfar" + + +/obj/item/weapon/pinpointer/shuttle/merc + shuttle_comp_id = "Mercenary" + +/obj/item/weapon/pinpointer/shuttle/heist shuttle_comp_id = "Skipjack" \ No newline at end of file diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 53e6c61304b..c42df5e5cdb 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -1,906 +1,906 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 -var/global/list/all_objectives = list() - -/datum/objective - var/datum/mind/owner = null //Who owns the objective. - var/explanation_text = "Nothing" //What that person is supposed to do. - var/datum/mind/target = null //If they are focused on a particular person. - var/target_amount = 0 //If they are focused on a particular number. Steal objectives have their own counter. - var/completed = 0 //currently only used for custom objectives. - -/datum/objective/New(var/text) - all_objectives |= src - if(text) - explanation_text = text - ..() - -/datum/objective/Destroy() - all_objectives -= src - ..() - -/datum/objective/proc/check_completion() - return completed - -/datum/objective/proc/find_target() - var/list/possible_targets = list() - for(var/datum/mind/possible_target in ticker.minds) - if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != 2)) - possible_targets += possible_target - if(possible_targets.len > 0) - target = pick(possible_targets) - - -/datum/objective/proc/find_target_by_role(role, role_type=0)//Option sets either to check assigned role or special role. Default to assigned. - for(var/datum/mind/possible_target in ticker.minds) - if((possible_target != owner) && ishuman(possible_target.current) && ((role_type ? possible_target.special_role : possible_target.assigned_role) == role) ) - target = possible_target - break - - - -/datum/objective/assassinate/find_target() - ..() - if(target && target.current) - explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/assassinate/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "Assassinate [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/assassinate/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current) || target.current.z > 6 || !target.current.ckey) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite - return 1 - return 0 - return 1 - - -/datum/objective/anti_revolution/execute/find_target() - ..() - if(target && target.current) - var/datum/gender/T = gender_datums[target.current.get_visible_gender()] - explanation_text = "[target.current.real_name], the [target.assigned_role] has extracted confidential information above their clearance. Execute [T.him]." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/anti_revolution/execute/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - var/datum/gender/T = gender_datums[target.current.get_visible_gender()] - explanation_text = "[target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] has extracted confidential information above their clearance. Execute [T.him]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/anti_revolution/execute/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || !ishuman(target.current)) - return 1 - return 0 - return 1 - -/datum/objective/anti_revolution/brig - var/already_completed = 0 - -/datum/objective/anti_revolution/brig/find_target() - ..() - if(target && target.current) - explanation_text = "Brig [target.current.real_name], the [target.assigned_role] for 20 minutes to set an example." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/anti_revolution/brig/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "Brig [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] for 20 minutes to set an example." - else - explanation_text = "Free Objective" - return target - -/datum/objective/anti_revolution/brig/check_completion() - if(already_completed) - return 1 - - if(target && target.current) - if(target.current.stat == DEAD) - return 0 - if(target.is_brigged(10 * 60 * 10)) - already_completed = 1 - return 1 - return 0 - return 0 - -/datum/objective/anti_revolution/demote/find_target() - ..() - if(target && target.current) - var/datum/gender/T = gender_datums[target.current.get_visible_gender()] - explanation_text = "[target.current.real_name], the [target.assigned_role] has been classified as harmful to [using_map.company_name]'s goals. Demote [T.him] to assistant." - else - explanation_text = "Free Objective" - return target - -/datum/objective/anti_revolution/demote/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - var/datum/gender/T = gender_datums[target.current.get_visible_gender()] - explanation_text = "[target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] has been classified as harmful to [using_map.company_name]'s goals. Demote [T.him] to assistant." - else - explanation_text = "Free Objective" - return target - -/datum/objective/anti_revolution/demote/check_completion() - if(target && target.current && istype(target,/mob/living/carbon/human)) - var/obj/item/weapon/card/id/I = target.current:wear_id - if(istype(I, /obj/item/device/pda)) - var/obj/item/device/pda/P = I - I = P.id - - if(!istype(I)) return 1 - - if(I.assignment == USELESS_JOB) //VOREStation Edit - Visitor not Assistant - return 1 - else - return 0 - return 1 - -//I want braaaainssss -/datum/objective/debrain/find_target() - ..() - if(target && target.current) - explanation_text = "Steal the brain of [target.current.real_name]." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/debrain/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "Steal the brain of [target.current.real_name] the [!role_type ? target.assigned_role : target.special_role]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/debrain/check_completion() - if(!target)//If it's a free objective. - return 1 - if( !owner.current || owner.current.stat==DEAD )//If you're otherwise dead. - return 0 - if( !target.current || !isbrain(target.current) ) - return 0 - var/atom/A = target.current - while(A.loc) //check to see if the brainmob is on our person - A = A.loc - if(A == owner.current) - return 1 - return 0 - - -//The opposite of killing a dude. -/datum/objective/protect/find_target() - ..() - if(target && target.current) - explanation_text = "Protect [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/protect/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "Protect [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/protect/check_completion() - if(!target) //If it's a free objective. - return 1 - if(target.current) - if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current)) - return 0 - return 1 - return 0 - - -/datum/objective/hijack - explanation_text = "Hijack the emergency shuttle by escaping alone." - -/datum/objective/hijack/check_completion() - if(!owner.current || owner.current.stat) - return 0 - if(!emergency_shuttle.returned()) - return 0 - if(issilicon(owner.current)) - return 0 - var/area/shuttle = locate(/area/shuttle/escape/centcom) - var/list/protected_mobs = list(/mob/living/silicon/ai, /mob/living/silicon/pai) - for(var/mob/living/player in player_list) - if(player.type in protected_mobs) continue - if (player.mind && (player.mind != owner)) - if(player.stat != DEAD) //they're not dead! - if(get_turf(player) in shuttle) - return 0 - return 1 - - -/datum/objective/block - explanation_text = "Do not allow any organic lifeforms to escape on the shuttle alive." - - -/datum/objective/block/check_completion() - if(!istype(owner.current, /mob/living/silicon)) - return 0 - if(!emergency_shuttle.returned()) - return 0 - if(!owner.current) - return 0 - var/area/shuttle = locate(/area/shuttle/escape/centcom) - var/protected_mobs[] = list(/mob/living/silicon/ai, /mob/living/silicon/pai, /mob/living/silicon/robot) - for(var/mob/living/player in player_list) - if(player.type in protected_mobs) continue - if (player.mind) - if (player.stat != 2) - if (get_turf(player) in shuttle) - return 0 - return 1 - -/datum/objective/silence - explanation_text = "Do not allow anyone to escape the station. Only allow the shuttle to be called when everyone is dead and your story is the only one left." - -/datum/objective/silence/check_completion() - if(!emergency_shuttle.returned()) - return 0 - - for(var/mob/living/player in player_list) - if(player == owner.current) - continue - if(player.mind) - if(player.stat != DEAD) - var/turf/T = get_turf(player) - if(!T) continue - switch(T.loc.type) - if(/area/shuttle/escape/centcom, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) - return 0 - return 1 - - -/datum/objective/escape - explanation_text = "Escape on the shuttle or an escape pod alive and free." - - -/datum/objective/escape/check_completion() - if(issilicon(owner.current)) - return 0 - if(isbrain(owner.current)) - return 0 - if(!emergency_shuttle.returned()) - return 0 - if(!owner.current || owner.current.stat ==2) - return 0 - var/turf/location = get_turf(owner.current.loc) - if(!location) - return 0 - - if(istype(location, /turf/simulated/shuttle/floor4)) // Fails traitors if they are in the shuttle brig -- Polymorph - if(istype(owner.current, /mob/living/carbon)) - var/mob/living/carbon/C = owner.current - if (!C.handcuffed) - return 1 - return 0 - - var/area/check_area = location.loc - if(istype(check_area, /area/shuttle/escape/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod1/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod2/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod3/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod5/centcom)) - return 1 - else - return 0 - - - -/datum/objective/survive - explanation_text = "Stay alive until the end." - -/datum/objective/survive/check_completion() - if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current)) - return 0 //Brains no longer win survive objectives. --NEO - if(issilicon(owner.current) && owner.current != owner.original) - return 0 - return 1 - -// Similar to the anti-rev objective, but for traitors -/datum/objective/brig - var/already_completed = 0 - -/datum/objective/brig/find_target() - ..() - if(target && target.current) - explanation_text = "Have [target.current.real_name], the [target.assigned_role] brigged for 10 minutes." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/brig/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "Have [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] brigged for 10 minutes." - else - explanation_text = "Free Objective" - return target - -/datum/objective/brig/check_completion() - if(already_completed) - return 1 - - if(target && target.current) - if(target.current.stat == DEAD) - return 0 - // Make the actual required time a bit shorter than the official time - if(target.is_brigged(10 * 60 * 5)) - already_completed = 1 - return 1 - return 0 - return 0 - -// Harm a crew member, making an example of them -/datum/objective/harm - var/already_completed = 0 - -/datum/objective/harm/find_target() - ..() - if(target && target.current) - explanation_text = "Make an example of [target.current.real_name], the [target.assigned_role]. Break one of their bones, detach one of their limbs or disfigure their face. Make sure they're alive when you do it." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/harm/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "Make an example of [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]. Break one of their bones, detach one of their limbs or disfigure their face. Make sure they're alive when you do it." - else - explanation_text = "Free Objective" - return target - -/datum/objective/harm/check_completion() - if(already_completed) - return 1 - - if(target && target.current && istype(target.current, /mob/living/carbon/human)) - if(target.current.stat == DEAD) - return 0 - - var/mob/living/carbon/human/H = target.current - for(var/obj/item/organ/external/E in H.organs) - if(E.status & ORGAN_BROKEN) - return 1 - for(var/limb_tag in H.species.has_limbs) //todo check prefs for robotic limbs and amputations. - var/list/organ_data = H.species.has_limbs[limb_tag] - var/limb_type = organ_data["path"] - var/found - for(var/obj/item/organ/external/E in H.organs) - if(limb_type == E.type) - found = 1 - break - if(!found) - return 1 - - var/obj/item/organ/external/head/head = H.get_organ(BP_HEAD) - if(head.disfigured) - return 1 - return 0 - - -/datum/objective/nuclear - explanation_text = "Destroy the station with a nuclear device." - - - -/datum/objective/steal - var/obj/item/steal_target - var/target_name - - var/global/possible_items[] = list( - "the Site Manager's antique laser gun" = /obj/item/weapon/gun/energy/captain, - "a hand teleporter" = /obj/item/weapon/hand_tele, - "an RCD" = /obj/item/weapon/rcd, - "a jetpack" = /obj/item/weapon/tank/jetpack, - "a site manager's jumpsuit" = /obj/item/clothing/under/rank/captain, - "a functional AI" = /obj/item/device/aicard, - "a pair of magboots" = /obj/item/clothing/shoes/magboots, - "the station blueprints" = /obj/item/areaeditor/blueprints, - "a nasa voidsuit" = /obj/item/clothing/suit/space/void, - "28 moles of phoron (full tank)" = /obj/item/weapon/tank, - "a sample of slime extract" = /obj/item/slime_extract, - "a piece of corgi meat" = /obj/item/weapon/reagent_containers/food/snacks/meat/corgi, - "a research director's jumpsuit" = /obj/item/clothing/under/rank/research_director, - "a chief engineer's jumpsuit" = /obj/item/clothing/under/rank/chief_engineer, - "a chief medical officer's jumpsuit" = /obj/item/clothing/under/rank/chief_medical_officer, - "a head of security's jumpsuit" = /obj/item/clothing/under/rank/head_of_security, - "a head of personnel's jumpsuit" = /obj/item/clothing/under/rank/head_of_personnel, - "the hypospray" = /obj/item/weapon/reagent_containers/hypospray/vial, - "the site manager's pinpointer" = /obj/item/weapon/pinpointer, - "an ablative armor vest" = /obj/item/clothing/suit/armor/laserproof, - ) - - var/global/possible_items_special[] = list( - /*"nuclear authentication disk" = /obj/item/weapon/disk/nuclear,*///Broken with the change to nuke disk making it respawn on z level change. - "nuclear gun" = /obj/item/weapon/gun/energy/gun/nuclear, - "diamond drill" = /obj/item/weapon/pickaxe/diamonddrill, - "bag of holding" = /obj/item/weapon/storage/backpack/holding, - "hyper-capacity cell" = /obj/item/weapon/cell/hyper, - "10 diamonds" = /obj/item/stack/material/diamond, - "50 gold bars" = /obj/item/stack/material/gold, - "25 refined uranium bars" = /obj/item/stack/material/uranium, - ) - - -/datum/objective/steal/proc/set_target(item_name) - target_name = item_name - steal_target = possible_items[target_name] - if (!steal_target ) - steal_target = possible_items_special[target_name] - explanation_text = "Steal [target_name]." - return steal_target - - -/datum/objective/steal/find_target() - return set_target(pick(possible_items)) - - -/datum/objective/steal/proc/select_target() - var/list/possible_items_all = possible_items+possible_items_special+"custom" - var/new_target = tgui_input_list(usr, "Select target:", "Objective target", possible_items_all) - if (!new_target) return - if (new_target == "custom") - var/obj/item/custom_target = tgui_input_list(usr, "Select type:", "Type", typesof(/obj/item)) - if (!custom_target) return - var/tmp_obj = new custom_target - var/custom_name = tmp_obj:name - qdel(tmp_obj) - custom_name = sanitize(tgui_input_text(usr, "Enter target name:", "Objective target", custom_name)) - if (!custom_name) return - target_name = custom_name - steal_target = custom_target - explanation_text = "Steal [target_name]." - else - set_target(new_target) - return steal_target - -/datum/objective/steal/check_completion() - if(!steal_target || !owner.current) return 0 - if(!isliving(owner.current)) return 0 - var/list/all_items = owner.current.get_contents() - switch (target_name) - if("28 moles of phoron (full tank)","10 diamonds","50 gold bars","25 refined uranium bars") - var/target_amount = text2num(target_name)//Non-numbers are ignored. - var/found_amount = 0.0//Always starts as zero. - - for(var/obj/item/I in all_items) //Check for phoron tanks - if(istype(I, steal_target)) - found_amount += (target_name=="28 moles of phoron (full tank)" ? (I:air_contents:gas["phoron"]) : (I:amount)) - return found_amount>=target_amount - - if("50 coins (in bag)") - var/obj/item/weapon/moneybag/B = locate() in all_items - - if(B) - var/target = text2num(target_name) - var/found_amount = 0.0 - for(var/obj/item/weapon/coin/C in B) - found_amount++ - return found_amount>=target - - if("a functional AI") - - for(var/obj/item/device/aicard/C in all_items) //Check for ai card - for(var/mob/living/silicon/ai/M in C) - if(istype(M, /mob/living/silicon/ai) && M.stat != 2) //See if any AI's are alive inside that card. - return 1 - - for(var/mob/living/silicon/ai/ai in mob_list) - var/turf/T = get_turf(ai) - if(istype(T)) - var/area/check_area = get_area(ai) - if(istype(check_area, /area/shuttle/escape/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod1/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod2/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod3/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod5/centcom)) - return 1 - else - - for(var/obj/I in all_items) //Check for items - if(istype(I, steal_target)) - return 1 - return 0 - - - -/datum/objective/download/proc/gen_amount_goal() - target_amount = rand(10,20) - explanation_text = "Download [target_amount] research levels." - return target_amount - - -/datum/objective/download/check_completion() - if(!ishuman(owner.current)) - return 0 - if(!owner.current || owner.current.stat == 2) - return 0 - - var/current_amount - var/obj/item/weapon/rig/S - if(istype(owner.current,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = owner.current - S = H.back - - if(!istype(S) || !S.installed_modules || !S.installed_modules.len) - return 0 - - var/obj/item/rig_module/datajack/stolen_data = locate() in S.installed_modules - if(!istype(stolen_data)) - return 0 - - for(var/datum/tech/current_data in stolen_data.stored_research) - if(current_data.level > 1) - current_amount += (current_data.level-1) - - return (current_amount= target_amount)) - return 1 - else - return 0 - -/datum/objective/vore/check_completion() - if(owner && owner.vore_prey_eaten >= target_amount) - return 1 - else - return 0 - -// Heist objectives. -/datum/objective/heist/proc/choose_target() - return - -/datum/objective/heist/kidnap/choose_target() - var/list/roles = list("Chief Engineer","Research Director","Roboticist","Chemist","Engineer") - var/list/possible_targets = list() - var/list/priority_targets = list() - - for(var/datum/mind/possible_target in ticker.minds) - if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != 2) && (!possible_target.special_role)) - possible_targets += possible_target - for(var/role in roles) - if(possible_target.assigned_role == role) - priority_targets += possible_target - continue - - if(priority_targets.len > 0) - target = pick(priority_targets) - else if(possible_targets.len > 0) - target = pick(possible_targets) - - if(target && target.current) - explanation_text = "We can get a good price for [target.current.real_name], the [target.assigned_role]. Take them alive." - else - explanation_text = "Free Objective" - return target - -/datum/objective/heist/kidnap/check_completion() - if(target && target.current) - if (target.current.stat == 2) - return 0 // They're dead. Fail. - //if (!target.current.restrained()) - // return 0 // They're loose. Close but no cigar. - - var/area/skipjack_station/start/A = locate() - for(var/mob/living/carbon/human/M in A) - if(target.current == M) - return 1 //They're restrained on the shuttle. Success. - else - return 0 - -/datum/objective/heist/loot/choose_target() - var/loot = "an object" - switch(rand(1,8)) - if(1) - target = /obj/structure/particle_accelerator - target_amount = 6 - loot = "a complete particle accelerator" - if(2) - target = /obj/machinery/the_singularitygen - target_amount = 1 - loot = "a gravitational generator" - if(3) - target = /obj/machinery/power/emitter - target_amount = 4 - loot = "four emitters" - if(4) - target = /obj/machinery/nuclearbomb - target_amount = 1 - loot = "a nuclear bomb" - if(5) - target = /obj/item/weapon/gun - target_amount = 6 - loot = "six guns" - if(6) - target = /obj/item/weapon/gun/energy - target_amount = 4 - loot = "four energy guns" - if(7) - target = /obj/item/weapon/gun/energy/laser - target_amount = 2 - loot = "two laser guns" - if(8) - target = /obj/item/weapon/gun/energy/ionrifle - target_amount = 1 - loot = "an ion gun" - - explanation_text = "It's a buyer's market out here. Steal [loot] for resale." - - -/datum/objective/heist/loot/check_completion() - var/total_amount = 0 - - for(var/obj/O in locate(/area/skipjack_station/start)) - if(istype(O,target)) total_amount++ - for(var/obj/I in O.contents) - if(istype(I,target)) total_amount++ - if(total_amount >= target_amount) return 1 - - for(var/datum/mind/raider in raiders.current_antagonists) - if(raider.current) - for(var/obj/O in raider.current.get_contents()) - if(istype(O,target)) total_amount++ - if(total_amount >= target_amount) return 1 - - return 0 - -/datum/objective/heist/salvage/choose_target() - switch(rand(1,8)) - if(1) - target = MAT_STEEL - target_amount = 300 - if(2) - target = MAT_GLASS - target_amount = 200 - if(3) - target = MAT_PLASTEEL - target_amount = 100 - if(4) - target = MAT_PHORON - target_amount = 100 - if(5) - target = MAT_SILVER - target_amount = 50 - if(6) - target = MAT_GOLD - target_amount = 20 - if(7) - target = MAT_URANIUM - target_amount = 20 - if(8) - target = MAT_DIAMOND - target_amount = 20 - - explanation_text = "Ransack the station and escape with [target_amount] [target]." - -/datum/objective/heist/salvage/check_completion() - - var/total_amount = 0 - - for(var/obj/item/O in locate(/area/skipjack_station/start)) - - var/obj/item/stack/material/S - if(istype(O,/obj/item/stack/material)) - if(O.name == target) - S = O - total_amount += S.get_amount() - for(var/obj/I in O.contents) - if(istype(I,/obj/item/stack/material)) - if(I.name == target) - S = I - total_amount += S.get_amount() - - for(var/datum/mind/raider in raiders.current_antagonists) - if(raider.current) - for(var/obj/item/O in raider.current.get_contents()) - if(istype(O,/obj/item/stack/material)) - if(O.name == target) - var/obj/item/stack/material/S = O - total_amount += S.get_amount() - - if(total_amount >= target_amount) return 1 - return 0 - - -/datum/objective/heist/preserve_crew - explanation_text = "Do not leave anyone behind, alive or dead." - -/datum/objective/heist/preserve_crew/check_completion() - if(raiders && raiders.is_raider_crew_safe()) return 1 - return 0 - -//Borer objective(s). -/datum/objective/borer_survive - explanation_text = "Survive in a host until the end of the round." - -/datum/objective/borer_survive/check_completion() - if(owner) - var/mob/living/simple_mob/animal/borer/B = owner - if(istype(B) && B.stat < 2 && B.host && B.host.stat < 2) return 1 - return 0 - -/datum/objective/borer_reproduce - explanation_text = "Reproduce at least once." - -/datum/objective/borer_reproduce/check_completion() - if(owner && owner.current) - var/mob/living/simple_mob/animal/borer/B = owner.current - if(istype(B) && B.has_reproduced) return 1 - return 0 - -/datum/objective/ninja_highlander - explanation_text = "You aspire to be a Grand Master of the Spider Clan. Kill all of your fellow acolytes." - -/datum/objective/ninja_highlander/check_completion() - if(owner) - for(var/datum/mind/ninja in get_antags("ninja")) - if(ninja != owner) - if(ninja.current.stat < 2) return 0 - return 1 - return 0 - -/datum/objective/cult/survive - explanation_text = "Our knowledge must live on." - target_amount = 5 - -/datum/objective/cult/survive/New() - ..() - explanation_text = "Our knowledge must live on. Make sure at least [target_amount] acolytes escape on the shuttle to spread their work on an another station." - -/datum/objective/cult/survive/check_completion() - var/acolytes_survived = 0 - if(!cult) - return 0 - for(var/datum/mind/cult_mind in cult.current_antagonists) - if (cult_mind.current && cult_mind.current.stat!=2) - var/area/A = get_area(cult_mind.current ) - if ( is_type_in_list(A, centcom_areas)) - acolytes_survived++ - if(acolytes_survived >= target_amount) - return 0 - else - return 1 - -/datum/objective/cult/eldergod - explanation_text = "Summon Nar-Sie via the use of the appropriate rune (Hell join self). It will only work if nine cultists stand on and around it. The convert rune is join blood self." - -/datum/objective/cult/eldergod/check_completion() - return (locate(/obj/singularity/narsie/large) in machines) - -/datum/objective/cult/sacrifice - explanation_text = "Conduct a ritual sacrifice for the glory of Nar-Sie." - -/datum/objective/cult/sacrifice/find_target() - var/list/possible_targets = list() - if(!possible_targets.len) - for(var/mob/living/carbon/human/player in player_list) - if(player.mind && !(player.mind in cult)) - possible_targets += player.mind - if(possible_targets.len > 0) - target = pick(possible_targets) - if(target) explanation_text = "Sacrifice [target.name], the [target.assigned_role]. You will need the sacrifice rune (Hell blood join) and three acolytes to do so." - -/datum/objective/cult/sacrifice/check_completion() - return (target && cult && !cult.sacrificed.Find(target)) - -/datum/objective/rev/find_target() - ..() - if(target && target.current) - explanation_text = "Assassinate, capture or convert [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/rev/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "Assassinate, capture or convert [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/rev/check_completion() - var/rval = 1 - if(target && target.current) - var/mob/living/carbon/human/H = target.current - if(!istype(H)) - return 1 - if(H.stat == DEAD || H.restrained()) - return 1 - // Check if they're converted - if(target in revs.current_antagonists) - return 1 - var/turf/T = get_turf(H) - if(T && isNotStationLevel(T.z)) //If they leave the station they count as dead for this - rval = 2 - return 0 - return rval - +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 +var/global/list/all_objectives = list() + +/datum/objective + var/datum/mind/owner = null //Who owns the objective. + var/explanation_text = "Nothing" //What that person is supposed to do. + var/datum/mind/target = null //If they are focused on a particular person. + var/target_amount = 0 //If they are focused on a particular number. Steal objectives have their own counter. + var/completed = 0 //currently only used for custom objectives. + +/datum/objective/New(var/text) + all_objectives |= src + if(text) + explanation_text = text + ..() + +/datum/objective/Destroy() + all_objectives -= src + ..() + +/datum/objective/proc/check_completion() + return completed + +/datum/objective/proc/find_target() + var/list/possible_targets = list() + for(var/datum/mind/possible_target in ticker.minds) + if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != 2)) + possible_targets += possible_target + if(possible_targets.len > 0) + target = pick(possible_targets) + + +/datum/objective/proc/find_target_by_role(role, role_type=0)//Option sets either to check assigned role or special role. Default to assigned. + for(var/datum/mind/possible_target in ticker.minds) + if((possible_target != owner) && ishuman(possible_target.current) && ((role_type ? possible_target.special_role : possible_target.assigned_role) == role) ) + target = possible_target + break + + + +/datum/objective/assassinate/find_target() + ..() + if(target && target.current) + explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/assassinate/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "Assassinate [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/assassinate/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current) || target.current.z > 6 || !target.current.ckey) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite + return 1 + return 0 + return 1 + + +/datum/objective/anti_revolution/execute/find_target() + ..() + if(target && target.current) + var/datum/gender/T = gender_datums[target.current.get_visible_gender()] + explanation_text = "[target.current.real_name], the [target.assigned_role] has extracted confidential information above their clearance. Execute [T.him]." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/anti_revolution/execute/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + var/datum/gender/T = gender_datums[target.current.get_visible_gender()] + explanation_text = "[target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] has extracted confidential information above their clearance. Execute [T.him]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/anti_revolution/execute/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || !ishuman(target.current)) + return 1 + return 0 + return 1 + +/datum/objective/anti_revolution/brig + var/already_completed = 0 + +/datum/objective/anti_revolution/brig/find_target() + ..() + if(target && target.current) + explanation_text = "Brig [target.current.real_name], the [target.assigned_role] for 20 minutes to set an example." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/anti_revolution/brig/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "Brig [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] for 20 minutes to set an example." + else + explanation_text = "Free Objective" + return target + +/datum/objective/anti_revolution/brig/check_completion() + if(already_completed) + return 1 + + if(target && target.current) + if(target.current.stat == DEAD) + return 0 + if(target.is_brigged(10 * 60 * 10)) + already_completed = 1 + return 1 + return 0 + return 0 + +/datum/objective/anti_revolution/demote/find_target() + ..() + if(target && target.current) + var/datum/gender/T = gender_datums[target.current.get_visible_gender()] + explanation_text = "[target.current.real_name], the [target.assigned_role] has been classified as harmful to [using_map.company_name]'s goals. Demote [T.him] to assistant." + else + explanation_text = "Free Objective" + return target + +/datum/objective/anti_revolution/demote/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + var/datum/gender/T = gender_datums[target.current.get_visible_gender()] + explanation_text = "[target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] has been classified as harmful to [using_map.company_name]'s goals. Demote [T.him] to assistant." + else + explanation_text = "Free Objective" + return target + +/datum/objective/anti_revolution/demote/check_completion() + if(target && target.current && istype(target,/mob/living/carbon/human)) + var/obj/item/weapon/card/id/I = target.current:wear_id + if(istype(I, /obj/item/device/pda)) + var/obj/item/device/pda/P = I + I = P.id + + if(!istype(I)) return 1 + + if(I.assignment == USELESS_JOB) //VOREStation Edit - Visitor not Assistant + return 1 + else + return 0 + return 1 + +//I want braaaainssss +/datum/objective/debrain/find_target() + ..() + if(target && target.current) + explanation_text = "Steal the brain of [target.current.real_name]." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/debrain/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "Steal the brain of [target.current.real_name] the [!role_type ? target.assigned_role : target.special_role]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/debrain/check_completion() + if(!target)//If it's a free objective. + return 1 + if( !owner.current || owner.current.stat==DEAD )//If you're otherwise dead. + return 0 + if( !target.current || !isbrain(target.current) ) + return 0 + var/atom/A = target.current + while(A.loc) //check to see if the brainmob is on our person + A = A.loc + if(A == owner.current) + return 1 + return 0 + + +//The opposite of killing a dude. +/datum/objective/protect/find_target() + ..() + if(target && target.current) + explanation_text = "Protect [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/protect/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "Protect [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/protect/check_completion() + if(!target) //If it's a free objective. + return 1 + if(target.current) + if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current)) + return 0 + return 1 + return 0 + + +/datum/objective/hijack + explanation_text = "Hijack the emergency shuttle by escaping alone." + +/datum/objective/hijack/check_completion() + if(!owner.current || owner.current.stat) + return 0 + if(!emergency_shuttle.returned()) + return 0 + if(issilicon(owner.current)) + return 0 + var/area/shuttle = locate(/area/shuttle/escape/centcom) + var/list/protected_mobs = list(/mob/living/silicon/ai, /mob/living/silicon/pai) + for(var/mob/living/player in player_list) + if(player.type in protected_mobs) continue + if (player.mind && (player.mind != owner)) + if(player.stat != DEAD) //they're not dead! + if(get_turf(player) in shuttle) + return 0 + return 1 + + +/datum/objective/block + explanation_text = "Do not allow any organic lifeforms to escape on the shuttle alive." + + +/datum/objective/block/check_completion() + if(!istype(owner.current, /mob/living/silicon)) + return 0 + if(!emergency_shuttle.returned()) + return 0 + if(!owner.current) + return 0 + var/area/shuttle = locate(/area/shuttle/escape/centcom) + var/protected_mobs[] = list(/mob/living/silicon/ai, /mob/living/silicon/pai, /mob/living/silicon/robot) + for(var/mob/living/player in player_list) + if(player.type in protected_mobs) continue + if (player.mind) + if (player.stat != 2) + if (get_turf(player) in shuttle) + return 0 + return 1 + +/datum/objective/silence + explanation_text = "Do not allow anyone to escape the station. Only allow the shuttle to be called when everyone is dead and your story is the only one left." + +/datum/objective/silence/check_completion() + if(!emergency_shuttle.returned()) + return 0 + + for(var/mob/living/player in player_list) + if(player == owner.current) + continue + if(player.mind) + if(player.stat != DEAD) + var/turf/T = get_turf(player) + if(!T) continue + switch(T.loc.type) + if(/area/shuttle/escape/centcom, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) + return 0 + return 1 + + +/datum/objective/escape + explanation_text = "Escape on the shuttle or an escape pod alive and free." + + +/datum/objective/escape/check_completion() + if(issilicon(owner.current)) + return 0 + if(isbrain(owner.current)) + return 0 + if(!emergency_shuttle.returned()) + return 0 + if(!owner.current || owner.current.stat ==2) + return 0 + var/turf/location = get_turf(owner.current.loc) + if(!location) + return 0 + + if(istype(location, /turf/simulated/shuttle/floor4)) // Fails traitors if they are in the shuttle brig -- Polymorph + if(istype(owner.current, /mob/living/carbon)) + var/mob/living/carbon/C = owner.current + if (!C.handcuffed) + return 1 + return 0 + + var/area/check_area = location.loc + if(istype(check_area, /area/shuttle/escape/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod1/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod2/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod3/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod5/centcom)) + return 1 + else + return 0 + + + +/datum/objective/survive + explanation_text = "Stay alive until the end." + +/datum/objective/survive/check_completion() + if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current)) + return 0 //Brains no longer win survive objectives. --NEO + if(issilicon(owner.current) && owner.current != owner.original) + return 0 + return 1 + +// Similar to the anti-rev objective, but for traitors +/datum/objective/brig + var/already_completed = 0 + +/datum/objective/brig/find_target() + ..() + if(target && target.current) + explanation_text = "Have [target.current.real_name], the [target.assigned_role] brigged for 10 minutes." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/brig/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "Have [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] brigged for 10 minutes." + else + explanation_text = "Free Objective" + return target + +/datum/objective/brig/check_completion() + if(already_completed) + return 1 + + if(target && target.current) + if(target.current.stat == DEAD) + return 0 + // Make the actual required time a bit shorter than the official time + if(target.is_brigged(10 * 60 * 5)) + already_completed = 1 + return 1 + return 0 + return 0 + +// Harm a crew member, making an example of them +/datum/objective/harm + var/already_completed = 0 + +/datum/objective/harm/find_target() + ..() + if(target && target.current) + explanation_text = "Make an example of [target.current.real_name], the [target.assigned_role]. Break one of their bones, detach one of their limbs or disfigure their face. Make sure they're alive when you do it." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/harm/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "Make an example of [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]. Break one of their bones, detach one of their limbs or disfigure their face. Make sure they're alive when you do it." + else + explanation_text = "Free Objective" + return target + +/datum/objective/harm/check_completion() + if(already_completed) + return 1 + + if(target && target.current && istype(target.current, /mob/living/carbon/human)) + if(target.current.stat == DEAD) + return 0 + + var/mob/living/carbon/human/H = target.current + for(var/obj/item/organ/external/E in H.organs) + if(E.status & ORGAN_BROKEN) + return 1 + for(var/limb_tag in H.species.has_limbs) //todo check prefs for robotic limbs and amputations. + var/list/organ_data = H.species.has_limbs[limb_tag] + var/limb_type = organ_data["path"] + var/found + for(var/obj/item/organ/external/E in H.organs) + if(limb_type == E.type) + found = 1 + break + if(!found) + return 1 + + var/obj/item/organ/external/head/head = H.get_organ(BP_HEAD) + if(head.disfigured) + return 1 + return 0 + + +/datum/objective/nuclear + explanation_text = "Destroy the station with a nuclear device." + + + +/datum/objective/steal + var/obj/item/steal_target + var/target_name + + var/global/possible_items[] = list( + "the Site Manager's antique laser gun" = /obj/item/weapon/gun/energy/captain, + "a hand teleporter" = /obj/item/weapon/hand_tele, + "an RCD" = /obj/item/weapon/rcd, + "a jetpack" = /obj/item/weapon/tank/jetpack, + "a site manager's jumpsuit" = /obj/item/clothing/under/rank/captain, + "a functional AI" = /obj/item/device/aicard, + "a pair of magboots" = /obj/item/clothing/shoes/magboots, + "the station blueprints" = /obj/item/areaeditor/blueprints, + "a nasa voidsuit" = /obj/item/clothing/suit/space/void, + "28 moles of phoron (full tank)" = /obj/item/weapon/tank, + "a sample of slime extract" = /obj/item/slime_extract, + "a piece of corgi meat" = /obj/item/weapon/reagent_containers/food/snacks/meat/corgi, + "a research director's jumpsuit" = /obj/item/clothing/under/rank/research_director, + "a chief engineer's jumpsuit" = /obj/item/clothing/under/rank/chief_engineer, + "a chief medical officer's jumpsuit" = /obj/item/clothing/under/rank/chief_medical_officer, + "a head of security's jumpsuit" = /obj/item/clothing/under/rank/head_of_security, + "a head of personnel's jumpsuit" = /obj/item/clothing/under/rank/head_of_personnel, + "the hypospray" = /obj/item/weapon/reagent_containers/hypospray/vial, + "the site manager's pinpointer" = /obj/item/weapon/pinpointer, + "an ablative armor vest" = /obj/item/clothing/suit/armor/laserproof, + ) + + var/global/possible_items_special[] = list( + /*"nuclear authentication disk" = /obj/item/weapon/disk/nuclear,*///Broken with the change to nuke disk making it respawn on z level change. + "nuclear gun" = /obj/item/weapon/gun/energy/gun/nuclear, + "diamond drill" = /obj/item/weapon/pickaxe/diamonddrill, + "bag of holding" = /obj/item/weapon/storage/backpack/holding, + "hyper-capacity cell" = /obj/item/weapon/cell/hyper, + "10 diamonds" = /obj/item/stack/material/diamond, + "50 gold bars" = /obj/item/stack/material/gold, + "25 refined uranium bars" = /obj/item/stack/material/uranium, + ) + + +/datum/objective/steal/proc/set_target(item_name) + target_name = item_name + steal_target = possible_items[target_name] + if (!steal_target ) + steal_target = possible_items_special[target_name] + explanation_text = "Steal [target_name]." + return steal_target + + +/datum/objective/steal/find_target() + return set_target(pick(possible_items)) + + +/datum/objective/steal/proc/select_target() + var/list/possible_items_all = possible_items+possible_items_special+"custom" + var/new_target = tgui_input_list(usr, "Select target:", "Objective target", possible_items_all) + if (!new_target) return + if (new_target == "custom") + var/obj/item/custom_target = tgui_input_list(usr, "Select type:", "Type", typesof(/obj/item)) + if (!custom_target) return + var/tmp_obj = new custom_target + var/custom_name = tmp_obj:name + qdel(tmp_obj) + custom_name = sanitize(tgui_input_text(usr, "Enter target name:", "Objective target", custom_name)) + if (!custom_name) return + target_name = custom_name + steal_target = custom_target + explanation_text = "Steal [target_name]." + else + set_target(new_target) + return steal_target + +/datum/objective/steal/check_completion() + if(!steal_target || !owner.current) return 0 + if(!isliving(owner.current)) return 0 + var/list/all_items = owner.current.get_contents() + switch (target_name) + if("28 moles of phoron (full tank)","10 diamonds","50 gold bars","25 refined uranium bars") + var/target_amount = text2num(target_name)//Non-numbers are ignored. + var/found_amount = 0.0//Always starts as zero. + + for(var/obj/item/I in all_items) //Check for phoron tanks + if(istype(I, steal_target)) + found_amount += (target_name=="28 moles of phoron (full tank)" ? (I:air_contents:gas["phoron"]) : (I:amount)) + return found_amount>=target_amount + + if("50 coins (in bag)") + var/obj/item/weapon/moneybag/B = locate() in all_items + + if(B) + var/target = text2num(target_name) + var/found_amount = 0.0 + for(var/obj/item/weapon/coin/C in B) + found_amount++ + return found_amount>=target + + if("a functional AI") + + for(var/obj/item/device/aicard/C in all_items) //Check for ai card + for(var/mob/living/silicon/ai/M in C) + if(istype(M, /mob/living/silicon/ai) && M.stat != 2) //See if any AI's are alive inside that card. + return 1 + + for(var/mob/living/silicon/ai/ai in mob_list) + var/turf/T = get_turf(ai) + if(istype(T)) + var/area/check_area = get_area(ai) + if(istype(check_area, /area/shuttle/escape/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod1/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod2/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod3/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod5/centcom)) + return 1 + else + + for(var/obj/I in all_items) //Check for items + if(istype(I, steal_target)) + return 1 + return 0 + + + +/datum/objective/download/proc/gen_amount_goal() + target_amount = rand(10,20) + explanation_text = "Download [target_amount] research levels." + return target_amount + + +/datum/objective/download/check_completion() + if(!ishuman(owner.current)) + return 0 + if(!owner.current || owner.current.stat == 2) + return 0 + + var/current_amount + var/obj/item/weapon/rig/S + if(istype(owner.current,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = owner.current + S = H.back + + if(!istype(S) || !S.installed_modules || !S.installed_modules.len) + return 0 + + var/obj/item/rig_module/datajack/stolen_data = locate() in S.installed_modules + if(!istype(stolen_data)) + return 0 + + for(var/datum/tech/current_data in stolen_data.stored_research) + if(current_data.level > 1) + current_amount += (current_data.level-1) + + return (current_amount= target_amount)) + return 1 + else + return 0 + +/datum/objective/vore/check_completion() + if(owner && owner.vore_prey_eaten >= target_amount) + return 1 + else + return 0 + +// Heist objectives. +/datum/objective/heist/proc/choose_target() + return + +/datum/objective/heist/kidnap/choose_target() + var/list/roles = list("Chief Engineer","Research Director","Roboticist","Chemist","Engineer") + var/list/possible_targets = list() + var/list/priority_targets = list() + + for(var/datum/mind/possible_target in ticker.minds) + if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != 2) && (!possible_target.special_role)) + possible_targets += possible_target + for(var/role in roles) + if(possible_target.assigned_role == role) + priority_targets += possible_target + continue + + if(priority_targets.len > 0) + target = pick(priority_targets) + else if(possible_targets.len > 0) + target = pick(possible_targets) + + if(target && target.current) + explanation_text = "We can get a good price for [target.current.real_name], the [target.assigned_role]. Take them alive." + else + explanation_text = "Free Objective" + return target + +/datum/objective/heist/kidnap/check_completion() + if(target && target.current) + if (target.current.stat == 2) + return 0 // They're dead. Fail. + //if (!target.current.restrained()) + // return 0 // They're loose. Close but no cigar. + + var/area/skipjack_station/start/A = locate() + for(var/mob/living/carbon/human/M in A) + if(target.current == M) + return 1 //They're restrained on the shuttle. Success. + else + return 0 + +/datum/objective/heist/loot/choose_target() + var/loot = "an object" + switch(rand(1,8)) + if(1) + target = /obj/structure/particle_accelerator + target_amount = 6 + loot = "a complete particle accelerator" + if(2) + target = /obj/machinery/the_singularitygen + target_amount = 1 + loot = "a gravitational generator" + if(3) + target = /obj/machinery/power/emitter + target_amount = 4 + loot = "four emitters" + if(4) + target = /obj/machinery/nuclearbomb + target_amount = 1 + loot = "a nuclear bomb" + if(5) + target = /obj/item/weapon/gun + target_amount = 6 + loot = "six guns" + if(6) + target = /obj/item/weapon/gun/energy + target_amount = 4 + loot = "four energy guns" + if(7) + target = /obj/item/weapon/gun/energy/laser + target_amount = 2 + loot = "two laser guns" + if(8) + target = /obj/item/weapon/gun/energy/ionrifle + target_amount = 1 + loot = "an ion gun" + + explanation_text = "It's a buyer's market out here. Steal [loot] for resale." + + +/datum/objective/heist/loot/check_completion() + var/total_amount = 0 + + for(var/obj/O in locate(/area/skipjack_station/start)) + if(istype(O,target)) total_amount++ + for(var/obj/I in O.contents) + if(istype(I,target)) total_amount++ + if(total_amount >= target_amount) return 1 + + for(var/datum/mind/raider in raiders.current_antagonists) + if(raider.current) + for(var/obj/O in raider.current.get_contents()) + if(istype(O,target)) total_amount++ + if(total_amount >= target_amount) return 1 + + return 0 + +/datum/objective/heist/salvage/choose_target() + switch(rand(1,8)) + if(1) + target = MAT_STEEL + target_amount = 300 + if(2) + target = MAT_GLASS + target_amount = 200 + if(3) + target = MAT_PLASTEEL + target_amount = 100 + if(4) + target = MAT_PHORON + target_amount = 100 + if(5) + target = MAT_SILVER + target_amount = 50 + if(6) + target = MAT_GOLD + target_amount = 20 + if(7) + target = MAT_URANIUM + target_amount = 20 + if(8) + target = MAT_DIAMOND + target_amount = 20 + + explanation_text = "Ransack the station and escape with [target_amount] [target]." + +/datum/objective/heist/salvage/check_completion() + + var/total_amount = 0 + + for(var/obj/item/O in locate(/area/skipjack_station/start)) + + var/obj/item/stack/material/S + if(istype(O,/obj/item/stack/material)) + if(O.name == target) + S = O + total_amount += S.get_amount() + for(var/obj/I in O.contents) + if(istype(I,/obj/item/stack/material)) + if(I.name == target) + S = I + total_amount += S.get_amount() + + for(var/datum/mind/raider in raiders.current_antagonists) + if(raider.current) + for(var/obj/item/O in raider.current.get_contents()) + if(istype(O,/obj/item/stack/material)) + if(O.name == target) + var/obj/item/stack/material/S = O + total_amount += S.get_amount() + + if(total_amount >= target_amount) return 1 + return 0 + + +/datum/objective/heist/preserve_crew + explanation_text = "Do not leave anyone behind, alive or dead." + +/datum/objective/heist/preserve_crew/check_completion() + if(raiders && raiders.is_raider_crew_safe()) return 1 + return 0 + +//Borer objective(s). +/datum/objective/borer_survive + explanation_text = "Survive in a host until the end of the round." + +/datum/objective/borer_survive/check_completion() + if(owner) + var/mob/living/simple_mob/animal/borer/B = owner + if(istype(B) && B.stat < 2 && B.host && B.host.stat < 2) return 1 + return 0 + +/datum/objective/borer_reproduce + explanation_text = "Reproduce at least once." + +/datum/objective/borer_reproduce/check_completion() + if(owner && owner.current) + var/mob/living/simple_mob/animal/borer/B = owner.current + if(istype(B) && B.has_reproduced) return 1 + return 0 + +/datum/objective/ninja_highlander + explanation_text = "You aspire to be a Grand Master of the Spider Clan. Kill all of your fellow acolytes." + +/datum/objective/ninja_highlander/check_completion() + if(owner) + for(var/datum/mind/ninja in get_antags("ninja")) + if(ninja != owner) + if(ninja.current.stat < 2) return 0 + return 1 + return 0 + +/datum/objective/cult/survive + explanation_text = "Our knowledge must live on." + target_amount = 5 + +/datum/objective/cult/survive/New() + ..() + explanation_text = "Our knowledge must live on. Make sure at least [target_amount] acolytes escape on the shuttle to spread their work on an another station." + +/datum/objective/cult/survive/check_completion() + var/acolytes_survived = 0 + if(!cult) + return 0 + for(var/datum/mind/cult_mind in cult.current_antagonists) + if (cult_mind.current && cult_mind.current.stat!=2) + var/area/A = get_area(cult_mind.current ) + if ( is_type_in_list(A, centcom_areas)) + acolytes_survived++ + if(acolytes_survived >= target_amount) + return 0 + else + return 1 + +/datum/objective/cult/eldergod + explanation_text = "Summon Nar-Sie via the use of the appropriate rune (Hell join self). It will only work if nine cultists stand on and around it. The convert rune is join blood self." + +/datum/objective/cult/eldergod/check_completion() + return (locate(/obj/singularity/narsie/large) in machines) + +/datum/objective/cult/sacrifice + explanation_text = "Conduct a ritual sacrifice for the glory of Nar-Sie." + +/datum/objective/cult/sacrifice/find_target() + var/list/possible_targets = list() + if(!possible_targets.len) + for(var/mob/living/carbon/human/player in player_list) + if(player.mind && !(player.mind in cult)) + possible_targets += player.mind + if(possible_targets.len > 0) + target = pick(possible_targets) + if(target) explanation_text = "Sacrifice [target.name], the [target.assigned_role]. You will need the sacrifice rune (Hell blood join) and three acolytes to do so." + +/datum/objective/cult/sacrifice/check_completion() + return (target && cult && !cult.sacrificed.Find(target)) + +/datum/objective/rev/find_target() + ..() + if(target && target.current) + explanation_text = "Assassinate, capture or convert [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/rev/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "Assassinate, capture or convert [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/rev/check_completion() + var/rval = 1 + if(target && target.current) + var/mob/living/carbon/human/H = target.current + if(!istype(H)) + return 1 + if(H.stat == DEAD || H.restrained()) + return 1 + // Check if they're converted + if(target in revs.current_antagonists) + return 1 + var/turf/T = get_turf(H) + if(T && isNotStationLevel(T.z)) //If they leave the station they count as dead for this + rval = 2 + return 0 + return rval + diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index 54f75a0032f..afa1d219ba9 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -1,12 +1,12 @@ -/datum/game_mode/revolution - name = "Revolution" - config_tag = "revolution" - round_description = "Some crewmembers are attempting to start a revolution!" - extended_round_description = "Revolutionaries - Remove the heads of staff from power. Convert other crewmembers to your cause using the 'Convert Bourgeoise' verb. Protect your leaders." - required_players = 12 //should be enough for a decent manifest, hopefully - required_players_secret = 12 //pretty sure rev doesn't even appear in secret - required_enemies = 3 - auto_recall_shuttle = 0 //un-wanted on polaris - end_on_antag_death = 0 - antag_tags = list(MODE_REVOLUTIONARY, MODE_LOYALIST) - require_all_templates = 1 +/datum/game_mode/revolution + name = "Revolution" + config_tag = "revolution" + round_description = "Some crewmembers are attempting to start a revolution!" + extended_round_description = "Revolutionaries - Remove the heads of staff from power. Convert other crewmembers to your cause using the 'Convert Bourgeoise' verb. Protect your leaders." + required_players = 12 //should be enough for a decent manifest, hopefully + required_players_secret = 12 //pretty sure rev doesn't even appear in secret + required_enemies = 3 + auto_recall_shuttle = 0 //un-wanted on polaris + end_on_antag_death = 0 + antag_tags = list(MODE_REVOLUTIONARY, MODE_LOYALIST) + require_all_templates = 1 diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm index 449d448fe07..5dcffb9d7de 100644 --- a/code/game/gamemodes/sandbox/h_sandbox.dm +++ b/code/game/gamemodes/sandbox/h_sandbox.dm @@ -1,149 +1,149 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -var/hsboxspawn = 1 -var/list - hrefs = list( - "hsbsuit" = "Suit Up (Space Travel Gear)", - "hsbmetal" = "Spawn 50 Metal", - "hsbglass" = "Spawn 50 Glass", - "hsbairlock" = "Spawn Airlock", - "hsbregulator" = "Spawn Air Regulator", - "hsbfilter" = "Spawn Air Filter", - "hsbcanister" = "Spawn Canister", - "hsbfueltank" = "Spawn Welding Fuel Tank", - "hsbwater tank" = "Spawn Water Tank", - "hsbtoolbox" = "Spawn Toolbox", - "hsbmedkit" = "Spawn Medical Kit") - -mob - var/datum/hSB/sandbox = null - proc - CanBuild() - if(master_mode == "sandbox") - sandbox = new/datum/hSB - sandbox.owner = src.ckey - if(src.client.holder) - sandbox.admin = 1 - verbs += new/mob/proc/sandbox_panel - sandbox_panel() - if(sandbox) - sandbox.update() - -/datum/hSB - var/owner = null - var/admin = 0 - proc - update() - var/hsbpanel = "
    h_Sandbox Panel

    " - if(admin) - hsbpanel += "Administration Tools:
    " - hsbpanel += "- Toggle Object Spawning

    " - hsbpanel += "Regular Tools:
    " - for(var/T in hrefs) - hsbpanel += "- [hrefs[T]]
    " - if(hsboxspawn) - hsbpanel += "- Spawn Object

    " - usr << browse(hsbpanel, "window=hsbpanel") - Topic(href, href_list) - if(!(src.owner == usr.ckey)) return - if(!usr) return //I guess this is possible if they log out or die with the panel open? It happened. - if(href_list["hsb"]) - switch(href_list["hsb"]) - if("hsbtobj") - if(!admin) return - if(hsboxspawn) - to_world("Sandbox: [usr.key] has disabled object spawning!") - hsboxspawn = 0 - return - if(!hsboxspawn) - to_world("Sandbox: [usr.key] has enabled object spawning!") - hsboxspawn = 1 - return - if("hsbsuit") - var/mob/living/carbon/human/P = usr - if(P.wear_suit) - P.wear_suit.loc = P.loc - P.wear_suit.reset_plane_and_layer() - P.wear_suit = null - P.wear_suit = new/obj/item/clothing/suit/space(P) - P.wear_suit.hud_layerise() - if(P.head) - P.head.loc = P.loc - P.head.reset_plane_and_layer() - P.head = null - P.head = new/obj/item/clothing/head/helmet/space(P) - P.head.hud_layerise() - if(P.wear_mask) - P.wear_mask.loc = P.loc - P.wear_mask.reset_plane_and_layer() - P.wear_mask = null - P.wear_mask = new/obj/item/clothing/mask/gas(P) - P.wear_mask.hud_layerise() - if(P.back) - P.back.loc = P.loc - P.back.reset_plane_and_layer() - P.back = null - P.back = new/obj/item/weapon/tank/jetpack(P) - P.back.hud_layerise() - P.internal = P.back - if("hsbmetal") - var/obj/fiftyspawner/iron/hsb = new/obj/fiftyspawner/iron - hsb.loc = usr.loc - if("hsbglass") - var/obj/fiftyspawner/glass/hsb = new/obj/fiftyspawner/glass - hsb.loc = usr.loc - if("hsbairlock") - var/obj/machinery/door/hsb = new/obj/machinery/door/airlock - - //TODO: DEFERRED make this better, with an HTML window or something instead of 15 popups - hsb.req_access = list() - var/accesses = get_all_accesses() - for(var/A in accesses) - if(tgui_alert(usr, "Will this airlock require [get_access_desc(A)] access?", "Sandbox:", list("Yes", "No")) == "Yes") - LAZYADD(hsb.req_access, A) - - hsb.loc = usr.loc - to_chat(usr, "Sandbox: Created an airlock.") - if("hsbcanister") - var/list/hsbcanisters = subtypesof(/obj/machinery/portable_atmospherics/canister) - var/hsbcanister = tgui_input_list(usr, "Choose a canister to spawn:", "Sandbox", hsbcanisters) - if(hsbcanister) - new hsbcanister(usr.loc) - if("hsbfueltank") - //var/obj/hsb = new/obj/weldfueltank - //hsb.loc = usr.loc - if("hsbwatertank") - //var/obj/hsb = new/obj/watertank - //hsb.loc = usr.loc - if("hsbtoolbox") - var/obj/item/weapon/storage/hsb = new/obj/item/weapon/storage/toolbox/mechanical - for(var/obj/item/device/radio/T in hsb) - qdel(T) - new/obj/item/weapon/tool/crowbar (hsb) - hsb.loc = usr.loc - if("hsbmedkit") - var/obj/item/weapon/storage/firstaid/hsb = new/obj/item/weapon/storage/firstaid/regular - hsb.loc = usr.loc - if("hsbobj") - if(!hsboxspawn) return - - var/list/selectable = list() - for(var/O in typesof(/obj/item/)) - //Note, these istypes don't work - if(istype(O, /obj/item/weapon/gun)) - continue - if(istype(O, /obj/item/assembly)) - continue - if(istype(O, /obj/item/device/camera)) - continue - if(istype(O, /obj/item/weapon/dummy)) - continue - if(istype(O, /obj/item/weapon/melee/energy/sword)) - continue - if(istype(O, /obj/structure)) - continue - selectable += O - - var/hsbitem = tgui_input_list(usr, "Choose an object to spawn:", "Sandbox", selectable) - if(hsbitem) - new hsbitem(usr.loc) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +var/hsboxspawn = 1 +var/list + hrefs = list( + "hsbsuit" = "Suit Up (Space Travel Gear)", + "hsbmetal" = "Spawn 50 Metal", + "hsbglass" = "Spawn 50 Glass", + "hsbairlock" = "Spawn Airlock", + "hsbregulator" = "Spawn Air Regulator", + "hsbfilter" = "Spawn Air Filter", + "hsbcanister" = "Spawn Canister", + "hsbfueltank" = "Spawn Welding Fuel Tank", + "hsbwater tank" = "Spawn Water Tank", + "hsbtoolbox" = "Spawn Toolbox", + "hsbmedkit" = "Spawn Medical Kit") + +mob + var/datum/hSB/sandbox = null + proc + CanBuild() + if(master_mode == "sandbox") + sandbox = new/datum/hSB + sandbox.owner = src.ckey + if(src.client.holder) + sandbox.admin = 1 + verbs += new/mob/proc/sandbox_panel + sandbox_panel() + if(sandbox) + sandbox.update() + +/datum/hSB + var/owner = null + var/admin = 0 + proc + update() + var/hsbpanel = "
    h_Sandbox Panel

    " + if(admin) + hsbpanel += "Administration Tools:
    " + hsbpanel += "- Toggle Object Spawning

    " + hsbpanel += "Regular Tools:
    " + for(var/T in hrefs) + hsbpanel += "- [hrefs[T]]
    " + if(hsboxspawn) + hsbpanel += "- Spawn Object

    " + usr << browse(hsbpanel, "window=hsbpanel") + Topic(href, href_list) + if(!(src.owner == usr.ckey)) return + if(!usr) return //I guess this is possible if they log out or die with the panel open? It happened. + if(href_list["hsb"]) + switch(href_list["hsb"]) + if("hsbtobj") + if(!admin) return + if(hsboxspawn) + to_world("Sandbox: [usr.key] has disabled object spawning!") + hsboxspawn = 0 + return + if(!hsboxspawn) + to_world("Sandbox: [usr.key] has enabled object spawning!") + hsboxspawn = 1 + return + if("hsbsuit") + var/mob/living/carbon/human/P = usr + if(P.wear_suit) + P.wear_suit.loc = P.loc + P.wear_suit.reset_plane_and_layer() + P.wear_suit = null + P.wear_suit = new/obj/item/clothing/suit/space(P) + P.wear_suit.hud_layerise() + if(P.head) + P.head.loc = P.loc + P.head.reset_plane_and_layer() + P.head = null + P.head = new/obj/item/clothing/head/helmet/space(P) + P.head.hud_layerise() + if(P.wear_mask) + P.wear_mask.loc = P.loc + P.wear_mask.reset_plane_and_layer() + P.wear_mask = null + P.wear_mask = new/obj/item/clothing/mask/gas(P) + P.wear_mask.hud_layerise() + if(P.back) + P.back.loc = P.loc + P.back.reset_plane_and_layer() + P.back = null + P.back = new/obj/item/weapon/tank/jetpack(P) + P.back.hud_layerise() + P.internal = P.back + if("hsbmetal") + var/obj/fiftyspawner/iron/hsb = new/obj/fiftyspawner/iron + hsb.loc = usr.loc + if("hsbglass") + var/obj/fiftyspawner/glass/hsb = new/obj/fiftyspawner/glass + hsb.loc = usr.loc + if("hsbairlock") + var/obj/machinery/door/hsb = new/obj/machinery/door/airlock + + //TODO: DEFERRED make this better, with an HTML window or something instead of 15 popups + hsb.req_access = list() + var/accesses = get_all_accesses() + for(var/A in accesses) + if(tgui_alert(usr, "Will this airlock require [get_access_desc(A)] access?", "Sandbox:", list("Yes", "No")) == "Yes") + LAZYADD(hsb.req_access, A) + + hsb.loc = usr.loc + to_chat(usr, "Sandbox: Created an airlock.") + if("hsbcanister") + var/list/hsbcanisters = subtypesof(/obj/machinery/portable_atmospherics/canister) + var/hsbcanister = tgui_input_list(usr, "Choose a canister to spawn:", "Sandbox", hsbcanisters) + if(hsbcanister) + new hsbcanister(usr.loc) + if("hsbfueltank") + //var/obj/hsb = new/obj/weldfueltank + //hsb.loc = usr.loc + if("hsbwatertank") + //var/obj/hsb = new/obj/watertank + //hsb.loc = usr.loc + if("hsbtoolbox") + var/obj/item/weapon/storage/hsb = new/obj/item/weapon/storage/toolbox/mechanical + for(var/obj/item/device/radio/T in hsb) + qdel(T) + new/obj/item/weapon/tool/crowbar (hsb) + hsb.loc = usr.loc + if("hsbmedkit") + var/obj/item/weapon/storage/firstaid/hsb = new/obj/item/weapon/storage/firstaid/regular + hsb.loc = usr.loc + if("hsbobj") + if(!hsboxspawn) return + + var/list/selectable = list() + for(var/O in typesof(/obj/item/)) + //Note, these istypes don't work + if(istype(O, /obj/item/weapon/gun)) + continue + if(istype(O, /obj/item/assembly)) + continue + if(istype(O, /obj/item/device/camera)) + continue + if(istype(O, /obj/item/weapon/dummy)) + continue + if(istype(O, /obj/item/weapon/melee/energy/sword)) + continue + if(istype(O, /obj/structure)) + continue + selectable += O + + var/hsbitem = tgui_input_list(usr, "Choose an object to spawn:", "Sandbox", selectable) + if(hsbitem) + new hsbitem(usr.loc) diff --git a/code/game/gamemodes/sandbox/sandbox.dm b/code/game/gamemodes/sandbox/sandbox.dm index 2dbcd77a309..385c007c7fd 100644 --- a/code/game/gamemodes/sandbox/sandbox.dm +++ b/code/game/gamemodes/sandbox/sandbox.dm @@ -1,23 +1,23 @@ -/datum/game_mode/sandbox - name = "Sandbox" - config_tag = "sandbox" - required_players = 0 - votable = 0 - round_description = "Build your own station!" - extended_round_description = "You can use the sandbox-panel command to access build options." - -/datum/game_mode/sandbox/pre_setup() - for(var/mob/M in player_list) - M.CanBuild() - return 1 - -/datum/game_mode/sandbox/process() - for(var/mob/M in player_list) - if(M.sandbox == null) - M.CanBuild() - return 1 - -/datum/game_mode/sandbox/post_setup() - ..() - if(emergency_shuttle) - emergency_shuttle.auto_recall = 1 +/datum/game_mode/sandbox + name = "Sandbox" + config_tag = "sandbox" + required_players = 0 + votable = 0 + round_description = "Build your own station!" + extended_round_description = "You can use the sandbox-panel command to access build options." + +/datum/game_mode/sandbox/pre_setup() + for(var/mob/M in player_list) + M.CanBuild() + return 1 + +/datum/game_mode/sandbox/process() + for(var/mob/M in player_list) + if(M.sandbox == null) + M.CanBuild() + return 1 + +/datum/game_mode/sandbox/post_setup() + ..() + if(emergency_shuttle) + emergency_shuttle.auto_recall = 1 diff --git a/code/game/gamemodes/setupgame.dm b/code/game/gamemodes/setupgame.dm index 3c029d52980..b2cb6a6a6c4 100644 --- a/code/game/gamemodes/setupgame.dm +++ b/code/game/gamemodes/setupgame.dm @@ -1,83 +1,83 @@ -///////////////////////// -// (mostly) DNA2 SETUP -///////////////////////// - -// Randomize block, assign a reference name, and optionally define difficulty (by making activation zone smaller or bigger) -// The name is used on /vg/ for species with predefined genetic traits, -// and for the DNA panel in the player panel. -/proc/getAssignedBlock(var/name,var/list/blocksLeft, var/activity_bounds=DNA_DEFAULT_BOUNDS) - if(blocksLeft.len==0) - warning("[name]: No more blocks left to assign!") - return 0 - var/assigned = pick(blocksLeft) - blocksLeft.Remove(assigned) - assigned_blocks[assigned]=name - dna_activity_bounds[assigned]=activity_bounds - //testing("[name] assigned to block #[assigned].") - return assigned - -/proc/setupgenetics() - - if (prob(50)) - // Currently unused. Will revisit. - N3X - BLOCKADD = rand(-300,300) - if (prob(75)) - DIFFMUT = rand(0,20) - - var/list/numsToAssign=new() - for(var/i=1;iYour account number is: [M.account_number], your account pin is: [M.remote_access_pin]") - -// overrideable separately so AIs/borgs can have cardborg hats without unneccessary new()/qdel() -/datum/job/proc/equip_preview(mob/living/carbon/human/H, var/alt_title) - var/decl/hierarchy/outfit/outfit = get_outfit(H, alt_title) - if(!outfit) - return FALSE - . = outfit.equip_base(H, title, alt_title) - -/datum/job/proc/get_access() - if(!config || config.jobs_have_minimal_access) - return src.minimal_access.Copy() - else - return src.access.Copy() - -//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1 -/datum/job/proc/player_old_enough(client/C) - return (available_in_days(C) == 0) //Available in 0 days = available right now = player is old enough to play. - -/datum/job/proc/available_in_days(client/C) - if(C && config.use_age_restriction_for_jobs && isnum(C.player_age) && isnum(minimal_player_age)) - return max(0, minimal_player_age - C.player_age) - return 0 - -/datum/job/proc/apply_fingerprints(var/mob/living/carbon/human/target) - if(!istype(target)) - return 0 - for(var/obj/item/item in target.contents) - apply_fingerprints_to_item(target, item) - return 1 - -/datum/job/proc/apply_fingerprints_to_item(var/mob/living/carbon/human/holder, var/obj/item/item) - item.add_fingerprint(holder,1) - if(item.contents.len) - for(var/obj/item/sub_item in item.contents) - apply_fingerprints_to_item(holder, sub_item) - -/datum/job/proc/is_position_available() - return (current_positions < total_positions) || (total_positions == -1) - -/datum/job/proc/has_alt_title(var/mob/H, var/supplied_title, var/desired_title) - return (supplied_title == desired_title) || (H.mind && H.mind.role_alt_title == desired_title) - -/datum/job/proc/get_description_blurb(var/alt_title) - var/list/message = list() - message |= job_description - - if(alt_title && alt_titles) - var/typepath = alt_titles[alt_title] - if(typepath) - var/datum/alt_title/A = new typepath() - if(A.title_blurb) - message |= A.title_blurb - return message - -/datum/job/proc/get_job_icon() - if(!job_master.job_icons[title]) - var/mob/living/carbon/human/dummy/mannequin/mannequin = get_mannequin("#job_icon") - dress_mannequin(mannequin) - mannequin.dir = SOUTH - mannequin.ImmediateOverlayUpdate() - var/icon/preview_icon = getFlatIcon(mannequin) - - preview_icon.Scale(preview_icon.Width() * 2, preview_icon.Height() * 2) // Scaling here to prevent blurring in the browser. - job_master.job_icons[title] = preview_icon - - return job_master.job_icons[title] - -/datum/job/proc/dress_mannequin(var/mob/living/carbon/human/dummy/mannequin/mannequin) - mannequin.delete_inventory(TRUE) - equip_preview(mannequin) - if(mannequin.back) - var/obj/O = mannequin.back - mannequin.drop_from_inventory(O) - qdel(O) - -///Assigns minimum age by race & brain type. Code says Positronic = mechanical and Drone = digital because nothing can be simple. -///Will first check based on brain type, then based on species. -/datum/job/proc/get_min_age(species_name, brain_type) - return minimum_character_age // VOREStation Edit - Minimum character age by rules is 18, return default which is standard for all species - //return (brain_type && LAZYACCESS(min_age_by_species, brain_type)) || LAZYACCESS(min_age_by_species, species_name) || minimum_character_age //VOREStation Removal - -/datum/job/proc/get_ideal_age(species_name, brain_type) - return ideal_character_age // VOREStation Edit - Minimum character age by rules is 18, return default which is standard for all species - //return (brain_type && LAZYACCESS(ideal_age_by_species, brain_type)) || LAZYACCESS(ideal_age_by_species, brain_type) || ideal_character_age //VOREStation Removal - -/datum/job/proc/is_species_banned(species_name, brain_type) - return FALSE // VOREStation Edit - Any species can be any job. - /* VOREStation Removal - if(banned_job_species == null) - return - if(species_name in banned_job_species) - return TRUE - if(brain_type in banned_job_species) - return TRUE - */ +/datum/job + + //The name of the job + var/title = "NOPE" + //Job access. The use of minimal_access or access is determined by a config setting: config.jobs_have_minimal_access + var/list/minimal_access = list() // Useful for servers which prefer to only have access given to the places a job absolutely needs (Larger server population) + var/list/access = list() // Useful for servers which either have fewer players, so each person needs to fill more than one role, or servers which like to give more access, so players can't hide forever in their super secure departments (I'm looking at you, chemistry!) + var/flag = 0 // Bitflags for the job + var/department_flag = 0 + var/faction = "None" // Players will be allowed to spawn in as jobs that are set to "Station" + var/total_positions = 0 // How many players can be this job + var/spawn_positions = 0 // How many players can spawn in as this job + var/current_positions = 0 // How many players have this job + var/supervisors = null // Supervisors, who this person answers to directly + var/selection_color = "#ffffff" // Selection screen color + var/list/alt_titles = null // List of alternate titles; There is no need for an alt-title datum for the base job title. + var/req_admin_notify // If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect. + var/minimal_player_age = 0 // If you have use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.) + var/list/departments = list() // List of departments this job belongs to, if any. The first one on the list will be the 'primary' department. + var/sorting_order = 0 // Used for sorting jobs so boss jobs go above regular ones, and their boss's boss is above that. Higher numbers = higher in sorting. + var/departments_managed = null // Is this a management position? If yes, list of departments managed. Otherwise null. + var/department_accounts = null // Which department accounts should people with this position be given the pin for? + var/assignable = TRUE // Should it show up on things like the ID computer? + var/minimum_character_age = 0 + var/list/min_age_by_species = null + var/ideal_character_age = 30 + var/list/ideal_age_by_species = null + var/list/banned_job_species = null + var/has_headset = TRUE //Do people with this job need to be given headsets and told how to use them? E.g. Cyborgs don't. + + var/account_allowed = 1 // Does this job type come with a station account? + var/economic_modifier = 2 // With how much does this job modify the initial account amount? + + var/outfit_type // What outfit datum does this job use in its default title? + + var/offmap_spawn = FALSE // Do we require weird and special spawning and datacore handling? + var/mob_type = JOB_CARBON // Bitflags representing mob type this job spawns + + // Description of the job's role and minimum responsibilities. + var/job_description = "This Job doesn't have a description! Please report it!" + +/datum/job/New() + . = ..() + department_accounts = department_accounts || departments_managed + +/datum/job/proc/equip(var/mob/living/carbon/human/H, var/alt_title) + var/decl/hierarchy/outfit/outfit = get_outfit(H, alt_title) + if(!outfit) + return FALSE + . = outfit.equip(H, title, alt_title) + return 1 + +/datum/job/proc/get_outfit(var/mob/living/carbon/human/H, var/alt_title) + if(alt_title && alt_titles) + var/datum/alt_title/A = alt_titles[alt_title] + if(A && initial(A.title_outfit)) + . = initial(A.title_outfit) + . = . || outfit_type + . = outfit_by_type(.) + +/datum/job/proc/setup_account(var/mob/living/carbon/human/H) + if(!account_allowed || (H.mind && H.mind.initial_account)) + return + + var/income = 1 + if(H.client) + switch(H.client.prefs.economic_status) + if(CLASS_UPPER) income = 1.30 + if(CLASS_UPMID) income = 1.15 + if(CLASS_MIDDLE) income = 1 + if(CLASS_LOWMID) income = 0.75 + if(CLASS_LOWER) income = 0.50 + if(CLASS_BROKE) income = 0 //VOREStation Add - Rent's not cheap + + //give them an account in the station database + var/money_amount = (rand(15,40) + rand(15,40)) * income * economic_modifier * ECO_MODIFIER //VOREStation Edit - Smoothed peaks, ECO_MODIFIER rather than per-species ones. + var/datum/money_account/M = create_account(H.real_name, money_amount, null, offmap_spawn) + if(H.mind) + var/remembered_info = "" + remembered_info += "Your account number is: #[M.account_number]
    " + remembered_info += "Your account pin is: [M.remote_access_pin]
    " + remembered_info += "Your account funds are: $[M.money]
    " + + if(M.transaction_log.len) + var/datum/transaction/T = M.transaction_log[1] + remembered_info += "Your account was created: [T.time], [T.date] at [T.source_terminal]
    " + H.mind.store_memory(remembered_info) + + H.mind.initial_account = M + + to_chat(H, "Your account number is: [M.account_number], your account pin is: [M.remote_access_pin]") + +// overrideable separately so AIs/borgs can have cardborg hats without unneccessary new()/qdel() +/datum/job/proc/equip_preview(mob/living/carbon/human/H, var/alt_title) + var/decl/hierarchy/outfit/outfit = get_outfit(H, alt_title) + if(!outfit) + return FALSE + . = outfit.equip_base(H, title, alt_title) + +/datum/job/proc/get_access() + if(!config || config.jobs_have_minimal_access) + return src.minimal_access.Copy() + else + return src.access.Copy() + +//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1 +/datum/job/proc/player_old_enough(client/C) + return (available_in_days(C) == 0) //Available in 0 days = available right now = player is old enough to play. + +/datum/job/proc/available_in_days(client/C) + if(C && config.use_age_restriction_for_jobs && isnum(C.player_age) && isnum(minimal_player_age)) + return max(0, minimal_player_age - C.player_age) + return 0 + +/datum/job/proc/apply_fingerprints(var/mob/living/carbon/human/target) + if(!istype(target)) + return 0 + for(var/obj/item/item in target.contents) + apply_fingerprints_to_item(target, item) + return 1 + +/datum/job/proc/apply_fingerprints_to_item(var/mob/living/carbon/human/holder, var/obj/item/item) + item.add_fingerprint(holder,1) + if(item.contents.len) + for(var/obj/item/sub_item in item.contents) + apply_fingerprints_to_item(holder, sub_item) + +/datum/job/proc/is_position_available() + return (current_positions < total_positions) || (total_positions == -1) + +/datum/job/proc/has_alt_title(var/mob/H, var/supplied_title, var/desired_title) + return (supplied_title == desired_title) || (H.mind && H.mind.role_alt_title == desired_title) + +/datum/job/proc/get_description_blurb(var/alt_title) + var/list/message = list() + message |= job_description + + if(alt_title && alt_titles) + var/typepath = alt_titles[alt_title] + if(typepath) + var/datum/alt_title/A = new typepath() + if(A.title_blurb) + message |= A.title_blurb + return message + +/datum/job/proc/get_job_icon() + if(!job_master.job_icons[title]) + var/mob/living/carbon/human/dummy/mannequin/mannequin = get_mannequin("#job_icon") + dress_mannequin(mannequin) + mannequin.dir = SOUTH + mannequin.ImmediateOverlayUpdate() + var/icon/preview_icon = getFlatIcon(mannequin) + + preview_icon.Scale(preview_icon.Width() * 2, preview_icon.Height() * 2) // Scaling here to prevent blurring in the browser. + job_master.job_icons[title] = preview_icon + + return job_master.job_icons[title] + +/datum/job/proc/dress_mannequin(var/mob/living/carbon/human/dummy/mannequin/mannequin) + mannequin.delete_inventory(TRUE) + equip_preview(mannequin) + if(mannequin.back) + var/obj/O = mannequin.back + mannequin.drop_from_inventory(O) + qdel(O) + +///Assigns minimum age by race & brain type. Code says Positronic = mechanical and Drone = digital because nothing can be simple. +///Will first check based on brain type, then based on species. +/datum/job/proc/get_min_age(species_name, brain_type) + return minimum_character_age // VOREStation Edit - Minimum character age by rules is 18, return default which is standard for all species + //return (brain_type && LAZYACCESS(min_age_by_species, brain_type)) || LAZYACCESS(min_age_by_species, species_name) || minimum_character_age //VOREStation Removal + +/datum/job/proc/get_ideal_age(species_name, brain_type) + return ideal_character_age // VOREStation Edit - Minimum character age by rules is 18, return default which is standard for all species + //return (brain_type && LAZYACCESS(ideal_age_by_species, brain_type)) || LAZYACCESS(ideal_age_by_species, brain_type) || ideal_character_age //VOREStation Removal + +/datum/job/proc/is_species_banned(species_name, brain_type) + return FALSE // VOREStation Edit - Any species can be any job. + /* VOREStation Removal + if(banned_job_species == null) + return + if(species_name in banned_job_species) + return TRUE + if(brain_type in banned_job_species) + return TRUE + */ diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm index 7594ade21fb..f896bf978f2 100644 --- a/code/game/jobs/job/medical.dm +++ b/code/game/jobs/job/medical.dm @@ -1,204 +1,204 @@ -////////////////////////////////// -// Chief Medical Officer -////////////////////////////////// -/datum/job/cmo - title = "Chief Medical Officer" - flag = CMO - departments_managed = list(DEPARTMENT_MEDICAL) - departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_COMMAND) - sorting_order = 2 - department_flag = MEDSCI - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the Site Manager" - selection_color = "#026865" - req_admin_notify = 1 - economic_modifier = 10 - access = list(access_medical, access_medical_equip, access_morgue, access_genetics, access_heads, - access_chemistry, access_virology, access_cmo, access_surgery, access_RC_announce, - access_keycard_auth, access_sec_doors, access_psychiatrist, access_eva, access_external_airlocks, access_maint_tunnels) - minimal_access = list(access_medical, access_medical_equip, access_morgue, access_genetics, access_heads, - access_chemistry, access_virology, access_cmo, access_surgery, access_RC_announce, - access_keycard_auth, access_sec_doors, access_psychiatrist, access_eva, access_external_airlocks, access_maint_tunnels) - - minimum_character_age = 25 - min_age_by_species = list(SPECIES_UNATHI = 70, "mechanical" = 10, SPECIES_HUMAN_VATBORN = 14) - minimal_player_age = 10 - ideal_character_age = 50 - ideal_age_by_species = list(SPECIES_UNATHI = 140, "mechanical" = 20, SPECIES_HUMAN_VATBORN = 20) - banned_job_species = list(SPECIES_TESHARI, SPECIES_DIONA, SPECIES_PROMETHEAN, SPECIES_ZADDAT, "digital") - - outfit_type = /decl/hierarchy/outfit/job/medical/cmo - job_description = "The CMO manages the Medical department and is a position requiring experience and skill; their goal is to ensure that their \ - staff keep the station's crew healthy and whole. They are primarily interested in making sure that patients are safely found and \ - transported to Medical for treatment. They are expected to keep the crew informed about threats to their health and safety, and \ - about the importance of Suit Sensors." - -////////////////////////////////// -// Medical Doctor -////////////////////////////////// -/datum/job/doctor - title = "Medical Doctor" - flag = DOCTOR - departments = list(DEPARTMENT_MEDICAL) - department_flag = MEDSCI - faction = "Station" - total_positions = 5 - spawn_positions = 3 - supervisors = "the Chief Medical Officer" - selection_color = "#013D3B" - economic_modifier = 7 - access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_eva) - minimal_access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_virology, access_eva) - outfit_type = /decl/hierarchy/outfit/job/medical/doctor - job_description = "A Medical Doctor is a Jack-of-All-Trades Medical title, covering a variety of skill levels and minor specializations. They are likely \ - familiar with basic first aid, and a number of accompanying medications, and can generally save, if not cure, a majority of the \ - patients they encounter." - alt_titles = list( - "Surgeon" = /datum/alt_title/surgeon, - "Emergency Physician" = /datum/alt_title/emergency_physician, - "Nurse" = /datum/alt_title/nurse, - "Virologist" = /datum/alt_title/virologist) - - min_age_by_species = list(SPECIES_PROMETHEAN = 3) - -//Medical Doctor Alt Titles -/datum/alt_title/surgeon - title = "Surgeon" - title_blurb = "A Surgeon specializes in providing surgical aid to injured patients, up to and including amputation and limb reattachement. They are expected \ - to know the ins and outs of anesthesia and surgery." - title_outfit = /decl/hierarchy/outfit/job/medical/doctor/surgeon - -/datum/alt_title/emergency_physician - title = "Emergency Physician" - title_blurb = "An Emergency Physician is a Medical professional trained for stabilizing and treating severely injured and/or dying patients. \ - They are generally the first response for any such individual brought to the Medbay, and can sometimes be expected to help their patients \ - make a full recovery." - title_outfit = /decl/hierarchy/outfit/job/medical/doctor/emergency_physician - -/datum/alt_title/nurse - title = "Nurse" - title_blurb = "A Nurse acts as a general purpose Doctor's Aide, providing basic care to non-critical patients, and stabilizing critical patients during \ - busy periods. They frequently watch the suit sensors console, to help manage the time of other Doctors. In rare occasions, a Nurse can be \ - called upon to revive deceased crew members." - title_outfit = /decl/hierarchy/outfit/job/medical/doctor/nurse - -/datum/alt_title/virologist - title = "Virologist" - title_blurb = "A Virologist cures active diseases in the crew, and prepares antibodies for possible infections. They also have the skills \ - to produce the various types of virus foods or mutagens." - title_outfit = /decl/hierarchy/outfit/job/medical/doctor/virologist - -//Chemist is a medical job damnit //YEAH FUCK YOU SCIENCE -Pete //Guys, behave -Erro -////////////////////////////////// -// Chemist -////////////////////////////////// -/datum/job/chemist - title = "Chemist" - flag = CHEMIST - departments = list(DEPARTMENT_MEDICAL) - department_flag = MEDSCI - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the Chief Medical Officer" - selection_color = "#013D3B" - economic_modifier = 5 - access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics) - minimal_access = list(access_medical, access_medical_equip, access_chemistry) - minimal_player_age = 3 - min_age_by_species = list(SPECIES_PROMETHEAN = 3) - - outfit_type = /decl/hierarchy/outfit/job/medical/chemist - job_description = "A Chemist produces and maintains a stock of basic to advanced chemicals for medical and occasionally research use. \ - They are likely to know the use and dangers of many lab-produced chemicals." - alt_titles = list("Pharmacist" = /datum/alt_title/pharmacist) - -// Chemist Alt Titles -/datum/alt_title/pharmacist - title = "Pharmacist" - title_blurb = "A Pharmacist focuses on the chemical needs of the Medical Department, and often offers to fill crew prescriptions at their discretion." - -/* I'm commenting out Geneticist so you can't actually see it in the job menu, given that you can't play as one - Jon. -////////////////////////////////// -// Geneticist -////////////////////////////////// -/datum/job/geneticist - title = "Geneticist" - flag = GENETICIST - departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_RESEARCH) - department_flag = MEDSCI - faction = "Station" - total_positions = 0 - spawn_positions = 0 - supervisors = "the Chief Medical Officer and Research Director" - selection_color = "#013D3B" - economic_modifier = 7 - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_research) - minimal_access = list(access_medical, access_morgue, access_genetics, access_research) - - outfit_type = /decl/hierarchy/outfit/job/medical/geneticist - job_description = "A Geneticist operates genetic manipulation equipment to repair any genetic defects encountered in crew, from cloning or radiation as examples. \ - When required, geneticists have the skills to clone, and are the superior choice when available for doing so." -*/ - -////////////////////////////////// -// Psychiatrist -////////////////////////////////// -/datum/job/psychiatrist - title = "Psychiatrist" - flag = PSYCHIATRIST - departments = list(DEPARTMENT_MEDICAL) - department_flag = MEDSCI - faction = "Station" - total_positions = 1 - spawn_positions = 1 - economic_modifier = 5 - supervisors = "the Chief Medical Officer" - selection_color = "#013D3B" - access = list(access_medical, access_medical_equip, access_morgue, access_psychiatrist) - minimal_access = list(access_medical, access_medical_equip, access_psychiatrist) - outfit_type = /decl/hierarchy/outfit/job/medical/psychiatrist - job_description = "A Psychiatrist provides mental health services to crew members in need. They may also be called upon to determine whatever \ - ails the mentally unwell, frequently under Security supervision. They understand the effects of various psychoactive drugs." - alt_titles = list("Psychologist" = /datum/alt_title/psychologist) - banned_job_species = list(SPECIES_PROMETHEAN, SPECIES_DIONA) - -//Psychiatrist Alt Titles -/datum/alt_title/psychologist - title = "Psychologist" - title_blurb = "A Psychologist provides mental health services to crew members in need, focusing more on therapy than medication. They may also be \ - called upon to determine whatever ails the mentally unwell, frequently under Security supervision." - title_outfit = /decl/hierarchy/outfit/job/medical/psychiatrist/psychologist - -////////////////////////////////// -// Paramedic -////////////////////////////////// -/datum/job/paramedic - title = "Paramedic" - flag = PARAMEDIC - departments = list(DEPARTMENT_MEDICAL) - department_flag = MEDSCI - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the Chief Medical Officer" - selection_color = "#013D3B" - economic_modifier = 4 - access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_eva, access_maint_tunnels, access_external_airlocks, access_psychiatrist) - minimal_access = list(access_medical, access_medical_equip, access_morgue, access_eva, access_maint_tunnels, access_external_airlocks) - outfit_type = /decl/hierarchy/outfit/job/medical/paramedic - job_description = "A Paramedic is primarily concerned with the recovery of patients who are unable to make it to the Medical Department on their own. \ - They may also be called upon to keep patients stable when Medical is busy or understaffed." - alt_titles = list("Emergency Medical Technician" = /datum/alt_title/emt) - banned_job_species = list(SPECIES_DIONA) - - min_age_by_species = list(SPECIES_PROMETHEAN = 2) - -// Paramedic Alt Titles -/datum/alt_title/emt - title = "Emergency Medical Technician" - title_blurb = "An Emergency Medical Technician is primarily concerned with the recovery of patients who are unable to make it to the Medical Department on their \ - own. They are capable of keeping a patient stabilized until they reach the hands of someone with more training." +////////////////////////////////// +// Chief Medical Officer +////////////////////////////////// +/datum/job/cmo + title = "Chief Medical Officer" + flag = CMO + departments_managed = list(DEPARTMENT_MEDICAL) + departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_COMMAND) + sorting_order = 2 + department_flag = MEDSCI + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the Site Manager" + selection_color = "#026865" + req_admin_notify = 1 + economic_modifier = 10 + access = list(access_medical, access_medical_equip, access_morgue, access_genetics, access_heads, + access_chemistry, access_virology, access_cmo, access_surgery, access_RC_announce, + access_keycard_auth, access_sec_doors, access_psychiatrist, access_eva, access_external_airlocks, access_maint_tunnels) + minimal_access = list(access_medical, access_medical_equip, access_morgue, access_genetics, access_heads, + access_chemistry, access_virology, access_cmo, access_surgery, access_RC_announce, + access_keycard_auth, access_sec_doors, access_psychiatrist, access_eva, access_external_airlocks, access_maint_tunnels) + + minimum_character_age = 25 + min_age_by_species = list(SPECIES_UNATHI = 70, "mechanical" = 10, SPECIES_HUMAN_VATBORN = 14) + minimal_player_age = 10 + ideal_character_age = 50 + ideal_age_by_species = list(SPECIES_UNATHI = 140, "mechanical" = 20, SPECIES_HUMAN_VATBORN = 20) + banned_job_species = list(SPECIES_TESHARI, SPECIES_DIONA, SPECIES_PROMETHEAN, SPECIES_ZADDAT, "digital") + + outfit_type = /decl/hierarchy/outfit/job/medical/cmo + job_description = "The CMO manages the Medical department and is a position requiring experience and skill; their goal is to ensure that their \ + staff keep the station's crew healthy and whole. They are primarily interested in making sure that patients are safely found and \ + transported to Medical for treatment. They are expected to keep the crew informed about threats to their health and safety, and \ + about the importance of Suit Sensors." + +////////////////////////////////// +// Medical Doctor +////////////////////////////////// +/datum/job/doctor + title = "Medical Doctor" + flag = DOCTOR + departments = list(DEPARTMENT_MEDICAL) + department_flag = MEDSCI + faction = "Station" + total_positions = 5 + spawn_positions = 3 + supervisors = "the Chief Medical Officer" + selection_color = "#013D3B" + economic_modifier = 7 + access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_eva) + minimal_access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_virology, access_eva) + outfit_type = /decl/hierarchy/outfit/job/medical/doctor + job_description = "A Medical Doctor is a Jack-of-All-Trades Medical title, covering a variety of skill levels and minor specializations. They are likely \ + familiar with basic first aid, and a number of accompanying medications, and can generally save, if not cure, a majority of the \ + patients they encounter." + alt_titles = list( + "Surgeon" = /datum/alt_title/surgeon, + "Emergency Physician" = /datum/alt_title/emergency_physician, + "Nurse" = /datum/alt_title/nurse, + "Virologist" = /datum/alt_title/virologist) + + min_age_by_species = list(SPECIES_PROMETHEAN = 3) + +//Medical Doctor Alt Titles +/datum/alt_title/surgeon + title = "Surgeon" + title_blurb = "A Surgeon specializes in providing surgical aid to injured patients, up to and including amputation and limb reattachement. They are expected \ + to know the ins and outs of anesthesia and surgery." + title_outfit = /decl/hierarchy/outfit/job/medical/doctor/surgeon + +/datum/alt_title/emergency_physician + title = "Emergency Physician" + title_blurb = "An Emergency Physician is a Medical professional trained for stabilizing and treating severely injured and/or dying patients. \ + They are generally the first response for any such individual brought to the Medbay, and can sometimes be expected to help their patients \ + make a full recovery." + title_outfit = /decl/hierarchy/outfit/job/medical/doctor/emergency_physician + +/datum/alt_title/nurse + title = "Nurse" + title_blurb = "A Nurse acts as a general purpose Doctor's Aide, providing basic care to non-critical patients, and stabilizing critical patients during \ + busy periods. They frequently watch the suit sensors console, to help manage the time of other Doctors. In rare occasions, a Nurse can be \ + called upon to revive deceased crew members." + title_outfit = /decl/hierarchy/outfit/job/medical/doctor/nurse + +/datum/alt_title/virologist + title = "Virologist" + title_blurb = "A Virologist cures active diseases in the crew, and prepares antibodies for possible infections. They also have the skills \ + to produce the various types of virus foods or mutagens." + title_outfit = /decl/hierarchy/outfit/job/medical/doctor/virologist + +//Chemist is a medical job damnit //YEAH FUCK YOU SCIENCE -Pete //Guys, behave -Erro +////////////////////////////////// +// Chemist +////////////////////////////////// +/datum/job/chemist + title = "Chemist" + flag = CHEMIST + departments = list(DEPARTMENT_MEDICAL) + department_flag = MEDSCI + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the Chief Medical Officer" + selection_color = "#013D3B" + economic_modifier = 5 + access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics) + minimal_access = list(access_medical, access_medical_equip, access_chemistry) + minimal_player_age = 3 + min_age_by_species = list(SPECIES_PROMETHEAN = 3) + + outfit_type = /decl/hierarchy/outfit/job/medical/chemist + job_description = "A Chemist produces and maintains a stock of basic to advanced chemicals for medical and occasionally research use. \ + They are likely to know the use and dangers of many lab-produced chemicals." + alt_titles = list("Pharmacist" = /datum/alt_title/pharmacist) + +// Chemist Alt Titles +/datum/alt_title/pharmacist + title = "Pharmacist" + title_blurb = "A Pharmacist focuses on the chemical needs of the Medical Department, and often offers to fill crew prescriptions at their discretion." + +/* I'm commenting out Geneticist so you can't actually see it in the job menu, given that you can't play as one - Jon. +////////////////////////////////// +// Geneticist +////////////////////////////////// +/datum/job/geneticist + title = "Geneticist" + flag = GENETICIST + departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_RESEARCH) + department_flag = MEDSCI + faction = "Station" + total_positions = 0 + spawn_positions = 0 + supervisors = "the Chief Medical Officer and Research Director" + selection_color = "#013D3B" + economic_modifier = 7 + access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_research) + minimal_access = list(access_medical, access_morgue, access_genetics, access_research) + + outfit_type = /decl/hierarchy/outfit/job/medical/geneticist + job_description = "A Geneticist operates genetic manipulation equipment to repair any genetic defects encountered in crew, from cloning or radiation as examples. \ + When required, geneticists have the skills to clone, and are the superior choice when available for doing so." +*/ + +////////////////////////////////// +// Psychiatrist +////////////////////////////////// +/datum/job/psychiatrist + title = "Psychiatrist" + flag = PSYCHIATRIST + departments = list(DEPARTMENT_MEDICAL) + department_flag = MEDSCI + faction = "Station" + total_positions = 1 + spawn_positions = 1 + economic_modifier = 5 + supervisors = "the Chief Medical Officer" + selection_color = "#013D3B" + access = list(access_medical, access_medical_equip, access_morgue, access_psychiatrist) + minimal_access = list(access_medical, access_medical_equip, access_psychiatrist) + outfit_type = /decl/hierarchy/outfit/job/medical/psychiatrist + job_description = "A Psychiatrist provides mental health services to crew members in need. They may also be called upon to determine whatever \ + ails the mentally unwell, frequently under Security supervision. They understand the effects of various psychoactive drugs." + alt_titles = list("Psychologist" = /datum/alt_title/psychologist) + banned_job_species = list(SPECIES_PROMETHEAN, SPECIES_DIONA) + +//Psychiatrist Alt Titles +/datum/alt_title/psychologist + title = "Psychologist" + title_blurb = "A Psychologist provides mental health services to crew members in need, focusing more on therapy than medication. They may also be \ + called upon to determine whatever ails the mentally unwell, frequently under Security supervision." + title_outfit = /decl/hierarchy/outfit/job/medical/psychiatrist/psychologist + +////////////////////////////////// +// Paramedic +////////////////////////////////// +/datum/job/paramedic + title = "Paramedic" + flag = PARAMEDIC + departments = list(DEPARTMENT_MEDICAL) + department_flag = MEDSCI + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the Chief Medical Officer" + selection_color = "#013D3B" + economic_modifier = 4 + access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_eva, access_maint_tunnels, access_external_airlocks, access_psychiatrist) + minimal_access = list(access_medical, access_medical_equip, access_morgue, access_eva, access_maint_tunnels, access_external_airlocks) + outfit_type = /decl/hierarchy/outfit/job/medical/paramedic + job_description = "A Paramedic is primarily concerned with the recovery of patients who are unable to make it to the Medical Department on their own. \ + They may also be called upon to keep patients stable when Medical is busy or understaffed." + alt_titles = list("Emergency Medical Technician" = /datum/alt_title/emt) + banned_job_species = list(SPECIES_DIONA) + + min_age_by_species = list(SPECIES_PROMETHEAN = 2) + +// Paramedic Alt Titles +/datum/alt_title/emt + title = "Emergency Medical Technician" + title_blurb = "An Emergency Medical Technician is primarily concerned with the recovery of patients who are unable to make it to the Medical Department on their \ + own. They are capable of keeping a patient stabilized until they reach the hands of someone with more training." title_outfit = /decl/hierarchy/outfit/job/medical/paramedic/emt \ No newline at end of file diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm index 09936c97cb0..0a65addbfae 100644 --- a/code/game/jobs/job/security.dm +++ b/code/game/jobs/job/security.dm @@ -1,139 +1,139 @@ -////////////////////////////////// -// Head of Security -////////////////////////////////// -/datum/job/hos - title = "Head of Security" - flag = HOS - departments_managed = list(DEPARTMENT_SECURITY) - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_COMMAND) - sorting_order = 2 - department_flag = ENGSEC - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the Site Manager" - selection_color = "#8E2929" - req_admin_notify = 1 - economic_modifier = 10 - access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, - access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, - access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting, - access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks) - minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, - access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, - access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting, - access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks) - minimum_character_age = 25 - min_age_by_species = list(SPECIES_HUMAN_VATBORN = 14) - minimal_player_age = 14 - ideal_character_age = 50 - ideal_age_by_species = list(SPECIES_HUMAN_VATBORN = 20) - banned_job_species = list(SPECIES_TESHARI, SPECIES_DIONA, SPECIES_PROMETHEAN, SPECIES_ZADDAT, "digital", SPECIES_UNATHI, "mechanical") - - outfit_type = /decl/hierarchy/outfit/job/security/hos - job_description = " The Head of Security manages the Security Department, keeping the station safe and making sure the rules are followed. They are expected to \ - keep the other Department Heads, and the rest of the crew, aware of developing situations that may be a threat. If necessary, the HoS may \ - perform the duties of absent Security roles, such as distributing gear from the Armory." - alt_titles = list("Security Commander" = /datum/alt_title/sec_commander, "Chief of Security" = /datum/alt_title/sec_chief) - - -// Head of Security Alt Titles -/datum/alt_title/sec_commander - title = "Security Commander" - -/datum/alt_title/sec_chief - title = "Chief of Security" - -//YW ADDITION START: LOYALTY IMPLANT FOR HOS -/datum/job/hos/equip(var/mob/living/carbon/human/H) - . = ..() - if(.) - H.implant_loyalty(src) -//YW ADDITION END - -/datum/job/warden - title = "Warden" - flag = WARDEN - departments = list(DEPARTMENT_SECURITY) - sorting_order = 1 - department_flag = ENGSEC - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the Head of Security" - selection_color = "#601C1C" - economic_modifier = 5 - access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_maint_tunnels, access_morgue, access_external_airlocks) - minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_maint_tunnels, access_external_airlocks) - minimal_player_age = 5 - banned_job_species = list(SPECIES_ZADDAT, SPECIES_PROMETHEAN, SPECIES_TESHARI, SPECIES_DIONA) - - outfit_type = /decl/hierarchy/outfit/job/security/warden - job_description = "The Warden watches over the physical Security Department, making sure the Brig and Armoury are secure and in order at all times. They oversee \ - prisoners that have been processed and brigged, and are responsible for their well being. The Warden is also in charge of distributing \ - Armoury gear in a crisis, and retrieving it when the crisis has passed. In an emergency, the Warden may be called upon to direct the \ - Security Department as a whole." - -////////////////////////////////// -// Detective -////////////////////////////////// -/datum/job/detective - title = "Detective" - flag = DETECTIVE - departments = list(DEPARTMENT_SECURITY) - department_flag = ENGSEC - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the Head of Security" - selection_color = "#601C1C" - access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_eva, access_external_airlocks, access_brig) //Vorestation edit - access_brig - minimal_access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_eva, access_external_airlocks) - economic_modifier = 5 - minimal_player_age = 3 - banned_job_species = list(SPECIES_ZADDAT, SPECIES_PROMETHEAN, SPECIES_DIONA) - - outfit_type = /decl/hierarchy/outfit/job/security/detective - job_description = "A Detective works to help Security find criminals who have not properly been identified, through interviews and forensic work. \ - For crimes only witnessed after the fact, or those with no survivors, they attempt to piece together what they can from pure evidence." - alt_titles = list("Forensic Technician" = /datum/alt_title/forensic_tech) - -// Detective Alt Titles -/datum/alt_title/forensic_tech - title = "Forensic Technician" - title_blurb = "A Forensic Technician works more with hard evidence and labwork than a Detective, but they share the purpose of solving crimes." - title_outfit = /decl/hierarchy/outfit/job/security/detective/forensic - -////////////////////////////////// -// Security Officer -////////////////////////////////// -/datum/job/officer - title = "Security Officer" - flag = OFFICER - departments = list(DEPARTMENT_SECURITY) - department_flag = ENGSEC - faction = "Station" - total_positions = 4 - spawn_positions = 4 - supervisors = "the Head of Security" - selection_color = "#601C1C" - economic_modifier = 4 - access = list(access_security, access_eva, access_sec_doors, access_brig, access_maint_tunnels, access_morgue, access_external_airlocks) - minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_maint_tunnels, access_external_airlocks) - minimal_player_age = 3 - banned_job_species = list(SPECIES_ZADDAT, SPECIES_TESHARI, SPECIES_DIONA) - - outfit_type = /decl/hierarchy/outfit/job/security/officer - job_description = "A Security Officer is concerned with maintaining the safety and security of the station as a whole, dealing with external threats and \ - apprehending criminals. A Security Officer is responsible for the health, safety, and processing of any prisoner they arrest. \ - No one is above the Law, not Security or Command." - alt_titles = list("Junior Officer" = /datum/alt_title/junior_officer) - - min_age_by_species = list(SPECIES_PROMETHEAN = 3) - -// Security Officer Alt Titles -/datum/alt_title/junior_officer - title = "Junior Officer" - title_blurb = "A Junior Officer is an inexperienced Security Officer. They likely have training, but not experience, and are frequently \ - paired off with a more senior co-worker. Junior Officers may also be expected to take over the boring duties of other Officers \ +////////////////////////////////// +// Head of Security +////////////////////////////////// +/datum/job/hos + title = "Head of Security" + flag = HOS + departments_managed = list(DEPARTMENT_SECURITY) + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_COMMAND) + sorting_order = 2 + department_flag = ENGSEC + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the Site Manager" + selection_color = "#8E2929" + req_admin_notify = 1 + economic_modifier = 10 + access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, + access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, + access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting, + access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks) + minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, + access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, + access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting, + access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks) + minimum_character_age = 25 + min_age_by_species = list(SPECIES_HUMAN_VATBORN = 14) + minimal_player_age = 14 + ideal_character_age = 50 + ideal_age_by_species = list(SPECIES_HUMAN_VATBORN = 20) + banned_job_species = list(SPECIES_TESHARI, SPECIES_DIONA, SPECIES_PROMETHEAN, SPECIES_ZADDAT, "digital", SPECIES_UNATHI, "mechanical") + + outfit_type = /decl/hierarchy/outfit/job/security/hos + job_description = " The Head of Security manages the Security Department, keeping the station safe and making sure the rules are followed. They are expected to \ + keep the other Department Heads, and the rest of the crew, aware of developing situations that may be a threat. If necessary, the HoS may \ + perform the duties of absent Security roles, such as distributing gear from the Armory." + alt_titles = list("Security Commander" = /datum/alt_title/sec_commander, "Chief of Security" = /datum/alt_title/sec_chief) + + +// Head of Security Alt Titles +/datum/alt_title/sec_commander + title = "Security Commander" + +/datum/alt_title/sec_chief + title = "Chief of Security" + +//YW ADDITION START: LOYALTY IMPLANT FOR HOS +/datum/job/hos/equip(var/mob/living/carbon/human/H) + . = ..() + if(.) + H.implant_loyalty(src) +//YW ADDITION END + +/datum/job/warden + title = "Warden" + flag = WARDEN + departments = list(DEPARTMENT_SECURITY) + sorting_order = 1 + department_flag = ENGSEC + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the Head of Security" + selection_color = "#601C1C" + economic_modifier = 5 + access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_maint_tunnels, access_morgue, access_external_airlocks) + minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_maint_tunnels, access_external_airlocks) + minimal_player_age = 5 + banned_job_species = list(SPECIES_ZADDAT, SPECIES_PROMETHEAN, SPECIES_TESHARI, SPECIES_DIONA) + + outfit_type = /decl/hierarchy/outfit/job/security/warden + job_description = "The Warden watches over the physical Security Department, making sure the Brig and Armoury are secure and in order at all times. They oversee \ + prisoners that have been processed and brigged, and are responsible for their well being. The Warden is also in charge of distributing \ + Armoury gear in a crisis, and retrieving it when the crisis has passed. In an emergency, the Warden may be called upon to direct the \ + Security Department as a whole." + +////////////////////////////////// +// Detective +////////////////////////////////// +/datum/job/detective + title = "Detective" + flag = DETECTIVE + departments = list(DEPARTMENT_SECURITY) + department_flag = ENGSEC + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the Head of Security" + selection_color = "#601C1C" + access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_eva, access_external_airlocks, access_brig) //Vorestation edit - access_brig + minimal_access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_eva, access_external_airlocks) + economic_modifier = 5 + minimal_player_age = 3 + banned_job_species = list(SPECIES_ZADDAT, SPECIES_PROMETHEAN, SPECIES_DIONA) + + outfit_type = /decl/hierarchy/outfit/job/security/detective + job_description = "A Detective works to help Security find criminals who have not properly been identified, through interviews and forensic work. \ + For crimes only witnessed after the fact, or those with no survivors, they attempt to piece together what they can from pure evidence." + alt_titles = list("Forensic Technician" = /datum/alt_title/forensic_tech) + +// Detective Alt Titles +/datum/alt_title/forensic_tech + title = "Forensic Technician" + title_blurb = "A Forensic Technician works more with hard evidence and labwork than a Detective, but they share the purpose of solving crimes." + title_outfit = /decl/hierarchy/outfit/job/security/detective/forensic + +////////////////////////////////// +// Security Officer +////////////////////////////////// +/datum/job/officer + title = "Security Officer" + flag = OFFICER + departments = list(DEPARTMENT_SECURITY) + department_flag = ENGSEC + faction = "Station" + total_positions = 4 + spawn_positions = 4 + supervisors = "the Head of Security" + selection_color = "#601C1C" + economic_modifier = 4 + access = list(access_security, access_eva, access_sec_doors, access_brig, access_maint_tunnels, access_morgue, access_external_airlocks) + minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_maint_tunnels, access_external_airlocks) + minimal_player_age = 3 + banned_job_species = list(SPECIES_ZADDAT, SPECIES_TESHARI, SPECIES_DIONA) + + outfit_type = /decl/hierarchy/outfit/job/security/officer + job_description = "A Security Officer is concerned with maintaining the safety and security of the station as a whole, dealing with external threats and \ + apprehending criminals. A Security Officer is responsible for the health, safety, and processing of any prisoner they arrest. \ + No one is above the Law, not Security or Command." + alt_titles = list("Junior Officer" = /datum/alt_title/junior_officer) + + min_age_by_species = list(SPECIES_PROMETHEAN = 3) + +// Security Officer Alt Titles +/datum/alt_title/junior_officer + title = "Junior Officer" + title_blurb = "A Junior Officer is an inexperienced Security Officer. They likely have training, but not experience, and are frequently \ + paired off with a more senior co-worker. Junior Officers may also be expected to take over the boring duties of other Officers \ including patrolling the station or maintaining specific posts." \ No newline at end of file diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm index 1619569c9d7..06ec638ee3b 100644 --- a/code/game/jobs/job/silicon.dm +++ b/code/game/jobs/job/silicon.dm @@ -1,83 +1,83 @@ -////////////////////////////////// -// AI -////////////////////////////////// -/datum/job/ai - title = "AI" - flag = AI - departments = list(DEPARTMENT_SYNTHETIC) - sorting_order = 1 // Be above their borgs. - department_flag = ENGSEC - faction = "Station" - total_positions = 0 // Not used for AI, see is_position_available below and modules/mob/living/silicon/ai/latejoin.dm - spawn_positions = 1 - selection_color = "#3F823F" - supervisors = "your Laws" - req_admin_notify = 1 - minimal_player_age = 7 - account_allowed = 0 - economic_modifier = 0 - has_headset = FALSE - assignable = FALSE - mob_type = JOB_SILICON_AI - outfit_type = /decl/hierarchy/outfit/job/silicon/ai - job_description = "The AI oversees the operation of the station and its crew, but has no real authority over them. \ - The AI is required to follow its Laws, and Lawbound Synthetics that are linked to it are expected to follow \ - the AI's commands, and their own Laws." - -// AI procs -/datum/job/ai/equip(var/mob/living/carbon/human/H) - if(!H) return 0 - return 1 - -/datum/job/ai/is_position_available() - return (empty_playable_ai_cores.len != 0) - -/datum/job/ai/equip_preview(mob/living/carbon/human/H) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/straight_jacket(H), slot_wear_suit) - H.equip_to_slot_or_del(new /obj/item/clothing/head/cardborg(H), slot_head) - return 1 - -////////////////////////////////// -// Cyborg -////////////////////////////////// -/datum/job/cyborg - title = "Cyborg" - flag = CYBORG - departments = list(DEPARTMENT_SYNTHETIC) - department_flag = ENGSEC - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "your Laws and the AI" //Nodrak - selection_color = "#254C25" - minimal_player_age = 1 - account_allowed = 0 - economic_modifier = 0 - has_headset = FALSE - assignable = FALSE - mob_type = JOB_SILICON_ROBOT - outfit_type = /decl/hierarchy/outfit/job/silicon/cyborg - job_description = "A Cyborg is a mobile station synthetic, piloted by a cybernetically preserved brain. It is considered a person, but is still required \ - to follow its Laws." - alt_titles = list("Robot" = /datum/alt_title/robot, "Drone" = /datum/alt_title/drone) - -// Cyborg Alt Titles -/datum/alt_title/robot - title = "Robot" - title_blurb = "A Robot is a mobile station synthetic, piloted by an advanced piece of technology called a Positronic Brain. It is considered a person, \ - legally, but is required to follow its Laws." - -/datum/alt_title/drone - title = "Drone" - title_blurb = "A Drone is a mobile station synthetic, piloted by a simple computer-based AI. As such, it is not a person, but rather an expensive and \ - and important piece of station property, and is expected to follow its Laws." - -// Cyborg procs -/datum/job/cyborg/equip(var/mob/living/carbon/human/H) - if(!H) return 0 - return 1 - -/datum/job/cyborg/equip_preview(mob/living/carbon/human/H) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/cardborg(H), slot_wear_suit) - H.equip_to_slot_or_del(new /obj/item/clothing/head/cardborg(H), slot_head) - return 1 +////////////////////////////////// +// AI +////////////////////////////////// +/datum/job/ai + title = "AI" + flag = AI + departments = list(DEPARTMENT_SYNTHETIC) + sorting_order = 1 // Be above their borgs. + department_flag = ENGSEC + faction = "Station" + total_positions = 0 // Not used for AI, see is_position_available below and modules/mob/living/silicon/ai/latejoin.dm + spawn_positions = 1 + selection_color = "#3F823F" + supervisors = "your Laws" + req_admin_notify = 1 + minimal_player_age = 7 + account_allowed = 0 + economic_modifier = 0 + has_headset = FALSE + assignable = FALSE + mob_type = JOB_SILICON_AI + outfit_type = /decl/hierarchy/outfit/job/silicon/ai + job_description = "The AI oversees the operation of the station and its crew, but has no real authority over them. \ + The AI is required to follow its Laws, and Lawbound Synthetics that are linked to it are expected to follow \ + the AI's commands, and their own Laws." + +// AI procs +/datum/job/ai/equip(var/mob/living/carbon/human/H) + if(!H) return 0 + return 1 + +/datum/job/ai/is_position_available() + return (empty_playable_ai_cores.len != 0) + +/datum/job/ai/equip_preview(mob/living/carbon/human/H) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/straight_jacket(H), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/clothing/head/cardborg(H), slot_head) + return 1 + +////////////////////////////////// +// Cyborg +////////////////////////////////// +/datum/job/cyborg + title = "Cyborg" + flag = CYBORG + departments = list(DEPARTMENT_SYNTHETIC) + department_flag = ENGSEC + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "your Laws and the AI" //Nodrak + selection_color = "#254C25" + minimal_player_age = 1 + account_allowed = 0 + economic_modifier = 0 + has_headset = FALSE + assignable = FALSE + mob_type = JOB_SILICON_ROBOT + outfit_type = /decl/hierarchy/outfit/job/silicon/cyborg + job_description = "A Cyborg is a mobile station synthetic, piloted by a cybernetically preserved brain. It is considered a person, but is still required \ + to follow its Laws." + alt_titles = list("Robot" = /datum/alt_title/robot, "Drone" = /datum/alt_title/drone) + +// Cyborg Alt Titles +/datum/alt_title/robot + title = "Robot" + title_blurb = "A Robot is a mobile station synthetic, piloted by an advanced piece of technology called a Positronic Brain. It is considered a person, \ + legally, but is required to follow its Laws." + +/datum/alt_title/drone + title = "Drone" + title_blurb = "A Drone is a mobile station synthetic, piloted by a simple computer-based AI. As such, it is not a person, but rather an expensive and \ + and important piece of station property, and is expected to follow its Laws." + +// Cyborg procs +/datum/job/cyborg/equip(var/mob/living/carbon/human/H) + if(!H) return 0 + return 1 + +/datum/job/cyborg/equip_preview(mob/living/carbon/human/H) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/cardborg(H), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/clothing/head/cardborg(H), slot_head) + return 1 diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm index 785a60a93cd..8f1f409b66a 100644 --- a/code/game/jobs/job_controller.dm +++ b/code/game/jobs/job_controller.dm @@ -1,692 +1,692 @@ -var/global/datum/controller/occupations/job_master - -#define GET_RANDOM_JOB 0 -#define BE_ASSISTANT 1 -#define RETURN_TO_LOBBY 2 - -/datum/controller/occupations - //List of all jobs - var/list/occupations = list() - //Players who need jobs - var/list/unassigned = list() - //Debug info - var/list/job_debug = list() - //Cache of icons for job info window - var/list/job_icons = list() - -/datum/controller/occupations/proc/SetupOccupations(var/faction = "Station") - occupations = list() - //var/list/all_jobs = typesof(/datum/job) - var/list/all_jobs = list(/datum/job/assistant) | using_map.allowed_jobs - if(!all_jobs.len) - to_world("Error setting up jobs, no job datums found!") - return 0 - for(var/J in all_jobs) - var/datum/job/job = new J() - if(!job) continue - if(job.faction != faction) continue - occupations += job - sortTim(occupations, GLOBAL_PROC_REF(cmp_job_datums)) - - - return 1 - - -/datum/controller/occupations/proc/Debug(var/text) - if(!Debug2) return 0 - job_debug.Add(text) - return 1 - - -/datum/controller/occupations/proc/GetJob(var/rank) - if(!rank) return null - for(var/datum/job/J in occupations) - if(!J) continue - if(J.title == rank) return J - return null - -/datum/controller/occupations/proc/GetPlayerAltTitle(mob/new_player/player, rank) - return player.client.prefs.GetPlayerAltTitle(GetJob(rank)) - -/datum/controller/occupations/proc/AssignRole(var/mob/new_player/player, var/rank, var/latejoin = 0) - Debug("Running AR, Player: [player], Rank: [rank], LJ: [latejoin]") - if(player && player.mind && rank) - var/datum/job/job = GetJob(rank) - if(!job) - return 0 - if((job.minimum_character_age || job.min_age_by_species) && (player.client.prefs.age < job.get_min_age(player.client.prefs.species, player.client.prefs.organ_data["brain"]))) - return 0 - if(jobban_isbanned(player, rank)) - return 0 - if(!job.player_old_enough(player.client)) - return 0 - //VOREStation Add - if(!job.player_has_enough_playtime(player.client)) - return 0 - if(!is_job_whitelisted(player, rank)) - return 0 - //VOREStation Add End - - var/position_limit = job.total_positions - if(!latejoin) - position_limit = job.spawn_positions - if((job.current_positions < position_limit) || position_limit == -1) - Debug("Player: [player] is now Rank: [rank], JCP:[job.current_positions], JPL:[position_limit]") - player.mind.assigned_role = rank - player.mind.role_alt_title = GetPlayerAltTitle(player, rank) - unassigned -= player - job.current_positions++ - return 1 - Debug("AR has failed, Player: [player], Rank: [rank]") - return 0 - -/datum/controller/occupations/proc/FreeRole(var/rank) //making additional slot on the fly - var/datum/job/job = GetJob(rank) - if(job && job.total_positions != -1) - job.total_positions++ - return 1 - return 0 - -/datum/controller/occupations/proc/FindOccupationCandidates(datum/job/job, level, flag) - Debug("Running FOC, Job: [job], Level: [level], Flag: [flag]") - var/list/candidates = list() - for(var/mob/new_player/player in unassigned) - if(jobban_isbanned(player, job.title)) - Debug("FOC isbanned failed, Player: [player]") - continue - if(!job.player_old_enough(player.client)) - Debug("FOC player not old enough, Player: [player]") - continue - if(job.minimum_character_age && (player.client.prefs.age < job.get_min_age(player.client.prefs.species, player.client.prefs.organ_data["brain"]))) - Debug("FOC character not old enough, Player: [player]") - continue - //VOREStation Code Start - if(!job.player_has_enough_playtime(player.client)) - Debug("FOC character not enough playtime, Player: [player]") - continue - if(!is_job_whitelisted(player, job.title)) - Debug("FOC is_job_whitelisted failed, Player: [player]") - continue - //VOREStation Code End - if(job.is_species_banned(player.client.prefs.species, player.client.prefs.organ_data["brain"]) == TRUE) - Debug("FOC character species invalid for job, Player: [player]") - continue - if(flag && !(player.client.prefs.be_special & flag)) - Debug("FOC flag failed, Player: [player], Flag: [flag], ") - continue - if(player.client.prefs.GetJobDepartment(job, level) & job.flag) - Debug("FOC pass, Player: [player], Level:[level]") - candidates += player - return candidates - -/datum/controller/occupations/proc/GiveRandomJob(var/mob/new_player/player) - Debug("GRJ Giving random job, Player: [player]") - for(var/datum/job/job in shuffle(occupations)) - if(!job) - continue - - if((job.minimum_character_age || job.min_age_by_species) && (player.client.prefs.age < job.get_min_age(player.client.prefs.species, player.client.prefs.organ_data["brain"]))) - continue - - if(job.is_species_banned(player.client.prefs.species, player.client.prefs.organ_data["brain"]) == TRUE) - continue - - if(istype(job, GetJob(USELESS_JOB))) // We don't want to give him assistant, that's boring! //VOREStation Edit - Visitor not Assistant - continue - - if(SSjob.is_job_in_department(job.title, DEPARTMENT_COMMAND)) //If you want a command position, select it! - continue - - if(jobban_isbanned(player, job.title)) - Debug("GRJ isbanned failed, Player: [player], Job: [job.title]") - continue - - if(!job.player_old_enough(player.client)) - Debug("GRJ player not old enough, Player: [player]") - continue - - //VOREStation Code Start - if(!job.player_has_enough_playtime(player.client)) - Debug("GRJ player not enough playtime, Player: [player]") - continue - if(!is_job_whitelisted(player, job.title)) - Debug("GRJ player not whitelisted for this job, Player: [player], Job: [job.title]") - continue - //VOREStation Code End - - if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1) - Debug("GRJ Random job given, Player: [player], Job: [job]") - AssignRole(player, job.title) - unassigned -= player - break - -/datum/controller/occupations/proc/ResetOccupations() - for(var/mob/new_player/player in player_list) - if((player) && (player.mind)) - player.mind.assigned_role = null - player.mind.special_role = null - SetupOccupations() - unassigned = list() - return - - -///This proc is called before the level loop of DivideOccupations() and will try to select a head, ignoring ALL non-head preferences for every level until it locates a head or runs out of levels to check -/datum/controller/occupations/proc/FillHeadPosition() - for(var/level = 1 to 3) - for(var/command_position in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) - var/datum/job/job = GetJob(command_position) - if(!job) continue - var/list/candidates = FindOccupationCandidates(job, level) - if(!candidates.len) continue - - // Build a weighted list, weight by age. - var/list/weightedCandidates = list() - for(var/mob/V in candidates) - // Log-out during round-start? What a bad boy, no head position for you! - if(!V.client) continue - var/age = V.client.prefs.age - - if(age < job.get_min_age(V.client.prefs.species, V.client.prefs.organ_data["brain"])) // Nope. - continue - - var/idealage = job.get_ideal_age(V.client.prefs.species, V.client.prefs.organ_data["brain"]) - var/agediff = abs(idealage - age) // Compute the absolute difference in age from target - switch(agediff) /// If the math sucks, it's because I almost failed algebra in high school. - if(20 to INFINITY) - weightedCandidates[V] = 3 // Too far off - if(10 to 20) - weightedCandidates[V] = 6 // Nearer the mark, but not quite - if(0 to 10) - weightedCandidates[V] = 10 // On the mark - else - // If there's ABSOLUTELY NOBODY ELSE - if(candidates.len == 1) weightedCandidates[V] = 1 - - - var/mob/new_player/candidate = pickweight(weightedCandidates) - if(AssignRole(candidate, command_position)) - return 1 - return 0 - - -///This proc is called at the start of the level loop of DivideOccupations() and will cause head jobs to be checked before any other jobs of the same level -/datum/controller/occupations/proc/CheckHeadPositions(var/level) - for(var/command_position in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) - var/datum/job/job = GetJob(command_position) - if(!job) continue - var/list/candidates = FindOccupationCandidates(job, level) - if(!candidates.len) continue - var/mob/new_player/candidate = pick(candidates) - AssignRole(candidate, command_position) - return - - -/** Proc DivideOccupations - * fills var "assigned_role" for all ready players. - * This proc must not have any side effect besides of modifying "assigned_role". - **/ -/datum/controller/occupations/proc/DivideOccupations() - //Setup new player list and get the jobs list - Debug("Running DO") - SetupOccupations() - - //Holder for Triumvirate is stored in the ticker, this just processes it - if(ticker && ticker.triai) - for(var/datum/job/A in occupations) - if(A.title == "AI") - A.spawn_positions = 3 - break - - //Get the players who are ready - for(var/mob/new_player/player in player_list) - if(player.ready && player.mind && !player.mind.assigned_role) - unassigned += player - - Debug("DO, Len: [unassigned.len]") - if(unassigned.len == 0) return 0 - - //Shuffle players and jobs - unassigned = shuffle(unassigned) - - HandleFeedbackGathering() - - //People who wants to be assistants, sure, go on. - Debug("DO, Running Assistant Check 1") - var/datum/job/assist = new DEFAULT_JOB_TYPE () - var/list/assistant_candidates = FindOccupationCandidates(assist, 3) - Debug("AC1, Candidates: [assistant_candidates.len]") - for(var/mob/new_player/player in assistant_candidates) - Debug("AC1 pass, Player: [player]") - AssignRole(player, USELESS_JOB) //VOREStation Edit - Visitor not Assistant - assistant_candidates -= player - Debug("DO, AC1 end") - - //Select one head - Debug("DO, Running Head Check") - FillHeadPosition() - Debug("DO, Head Check end") - - //Other jobs are now checked - Debug("DO, Running Standard Check") - - - // New job giving system by Donkie - // This will cause lots of more loops, but since it's only done once it shouldn't really matter much at all. - // Hopefully this will add more randomness and fairness to job giving. - - // Loop through all levels from high to low - var/list/shuffledoccupations = shuffle(occupations) - // var/list/disabled_jobs = ticker.mode.disabled_jobs // So we can use .Find down below without a colon. - for(var/level = 1 to 3) - //Check the head jobs first each level - CheckHeadPositions(level) - - // Loop through all unassigned players - for(var/mob/new_player/player in unassigned) - - // Loop through all jobs - for(var/datum/job/job in shuffledoccupations) // SHUFFLE ME BABY - if(!job || ticker.mode.disabled_jobs.Find(job.title) ) - continue - - if(jobban_isbanned(player, job.title)) - Debug("DO isbanned failed, Player: [player], Job:[job.title]") - continue - - if(!job.player_old_enough(player.client)) - Debug("DO player not old enough, Player: [player], Job:[job.title]") - continue - - //VOREStation Add - if(!job.player_has_enough_playtime(player.client)) - Debug("DO player not enough playtime, Player: [player]") - continue - //VOREStation Add End - - // If the player wants that job on this level, then try give it to him. - if(player.client.prefs.GetJobDepartment(job, level) & job.flag) - - // If the job isn't filled - if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1) - Debug("DO pass, Player: [player], Level:[level], Job:[job.title]") - AssignRole(player, job.title) - unassigned -= player - break - - // Hand out random jobs to the people who didn't get any in the last check - // Also makes sure that they got their preference correct - for(var/mob/new_player/player in unassigned) - if(player.client.prefs.alternate_option == GET_RANDOM_JOB) - GiveRandomJob(player) - /* - Old job system - for(var/level = 1 to 3) - for(var/datum/job/job in occupations) - Debug("Checking job: [job]") - if(!job) - continue - if(!unassigned.len) - break - if((job.current_positions >= job.spawn_positions) && job.spawn_positions != -1) - continue - var/list/candidates = FindOccupationCandidates(job, level) - while(candidates.len && ((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)) - var/mob/new_player/candidate = pick(candidates) - Debug("Selcted: [candidate], for: [job.title]") - AssignRole(candidate, job.title) - candidates -= candidate*/ - - Debug("DO, Standard Check end") - - Debug("DO, Running AC2") - - // For those who wanted to be assistant if their preferences were filled, here you go. - for(var/mob/new_player/player in unassigned) - if(player.client.prefs.alternate_option == BE_ASSISTANT) - Debug("AC2 Assistant located, Player: [player]") - AssignRole(player, USELESS_JOB) //VOREStation Edit - Visitor not Assistant - - //For ones returning to lobby - for(var/mob/new_player/player in unassigned) - if(player.client.prefs.alternate_option == RETURN_TO_LOBBY) - player.ready = 0 - player.new_player_panel_proc() - unassigned -= player - return 1 - - -/datum/controller/occupations/proc/EquipRank(var/mob/living/carbon/human/H, var/rank, var/joined_late = 0) - if(!H) return null - - var/datum/job/job = GetJob(rank) - var/list/spawn_in_storage = list() - - if(!joined_late) - var/obj/S = null - var/list/possible_spawns = list() - for(var/obj/effect/landmark/start/sloc in landmarks_list) - if(sloc.name != rank) continue - if(locate(/mob/living) in sloc.loc) continue - possible_spawns.Add(sloc) - if(possible_spawns.len) - S = pick(possible_spawns) - if(!S) - S = locate("start*[rank]") // use old stype - if(istype(S, /obj/effect/landmark/start) && istype(S.loc, /turf)) - H.forceMove(S.loc) - else - var/list/spawn_props = LateSpawn(H.client, rank) - var/turf/T = spawn_props["turf"] - if(!T) - to_chat(H, "You were unable to be spawned at your chosen late-join spawnpoint. Please verify your job/spawn point combination makes sense, and try another one.") - return - else - H.forceMove(T) - - // Moving wheelchair if they have one - if(H.buckled && istype(H.buckled, /obj/structure/bed/chair/wheelchair)) - H.buckled.forceMove(H.loc) - H.buckled.set_dir(H.dir) - - if(job) - - //Equip custom gear loadout. - var/list/custom_equip_slots = list() - var/list/custom_equip_leftovers = list() - if(H.client && H.client.prefs && H.client.prefs.gear && H.client.prefs.gear.len && !(job.mob_type & JOB_SILICON)) - for(var/thing in H.client.prefs.gear) - var/datum/gear/G = gear_datums[thing] - if(!G) //Not a real gear datum (maybe removed, as this is loaded from their savefile) - continue - - var/permitted - // Check if it is restricted to certain roles - if(G.allowed_roles) - for(var/job_name in G.allowed_roles) - if(job.title == job_name) - permitted = 1 - else - permitted = 1 - - // Check if they're whitelisted for this gear (in alien whitelist? seriously?) - if(G.whitelisted && !is_alien_whitelisted(H, GLOB.all_species[G.whitelisted])) - permitted = 0 - - // If they aren't, tell them - if(!permitted) - to_chat(H, "Your current species, job or whitelist status does not permit you to spawn with [thing]!") - continue - - // Implants get special treatment - if(G.slot == "implant") - var/obj/item/weapon/implant/I = G.spawn_item(H, H.client.prefs.gear[G.display_name]) - I.invisibility = 100 - I.implant_loadout(H) - continue - - // Try desperately (and sorta poorly) to equip the item. Now with increased desperation! - if(G.slot && !(G.slot in custom_equip_slots)) - var/metadata = H.client.prefs.gear[G.display_name] - //if(G.slot == slot_wear_mask || G.slot == slot_wear_suit || G.slot == slot_head) - // custom_equip_leftovers += thing - //else - if(H.equip_to_slot_or_del(G.spawn_item(H, metadata), G.slot)) - to_chat(H, "Equipping you with \the [thing]!") - if(G.slot != slot_tie) - custom_equip_slots.Add(G.slot) - else - custom_equip_leftovers.Add(thing) - else - spawn_in_storage += thing - - // Set up their account - job.setup_account(H) - - // Equip job items. - job.equip(H, H.mind ? H.mind.role_alt_title : "") - - // Stick their fingerprints on literally everything - job.apply_fingerprints(H) - - // Only non-silicons get post-job-equip equipment - if(!(job.mob_type & JOB_SILICON)) - H.equip_post_job() - - // If some custom items could not be equipped before, try again now. - for(var/thing in custom_equip_leftovers) - var/datum/gear/G = gear_datums[thing] - if(G.slot in custom_equip_slots) - spawn_in_storage += thing - else - var/metadata = H.client.prefs.gear[G.display_name] - if(H.equip_to_slot_or_del(G.spawn_item(H, metadata), G.slot)) - to_chat(H, "Equipping you with \the [thing]!") - custom_equip_slots.Add(G.slot) - else - spawn_in_storage += thing - else - to_chat(H, "Your job is [rank] and the game just can't handle it! Please report this bug to an administrator.") - - H.job = rank - log_game("JOINED [key_name(H)] as \"[rank]\"") - log_game("SPECIES [key_name(H)] is a: \"[H.species.name]\"") //VOREStation Add - - // If they're head, give them the account info for their department - if(H.mind && job.department_accounts) - var/remembered_info = "" - for(var/D in job.department_accounts) - var/datum/money_account/department_account = department_accounts[D] - if(department_account) - remembered_info += "Department account number ([D]): #[department_account.account_number]
    " - remembered_info += "Department account pin ([D]): [department_account.remote_access_pin]
    " - remembered_info += "Department account funds ([D]): $[department_account.money]
    " - - H.mind.store_memory(remembered_info) - - var/alt_title = null - if(H.mind) - H.mind.assigned_role = rank - alt_title = H.mind.role_alt_title - - // If we're a silicon, we may be done at this point - if(job.mob_type & JOB_SILICON_ROBOT) - return H.Robotize() - if(job.mob_type & JOB_SILICON_AI) - return H - - // TWEET PEEP - if(rank == "Site Manager") - var/sound/announce_sound = (ticker.current_state <= GAME_STATE_SETTING_UP) ? null : sound('sound/misc/boatswain.ogg', volume=20) - captain_announcement.Announce("All hands, [alt_title ? alt_title : "Site Manager"] [H.real_name] on deck!", new_sound = announce_sound, zlevel = H.z) - - //Deferred item spawning. - if(spawn_in_storage && spawn_in_storage.len) - var/obj/item/weapon/storage/B - for(var/obj/item/weapon/storage/S in H.contents) - B = S - break - - if(!isnull(B)) - for(var/thing in spawn_in_storage) - to_chat(H, "Placing \the [thing] in your [B.name]!") - var/datum/gear/G = gear_datums[thing] - var/metadata = H.client.prefs.gear[G.display_name] - G.spawn_item(B, metadata) - else - to_chat(H, "Failed to locate a storage object on your mob, either you spawned with no arms and no backpack or this is a bug.") - - if(istype(H)) //give humans wheelchairs, if they need them. - var/obj/item/organ/external/l_foot = H.get_organ("l_foot") - var/obj/item/organ/external/r_foot = H.get_organ("r_foot") - var/obj/item/weapon/storage/S = locate() in H.contents - var/obj/item/wheelchair/R - if(S) - R = locate() in S.contents - if(!l_foot || !r_foot || R) - var/wheelchair_type = R?.unfolded_type || /obj/structure/bed/chair/wheelchair - var/obj/structure/bed/chair/wheelchair/W = new wheelchair_type(H.loc) - W.buckle_mob(H) - H.update_canmove() - W.set_dir(H.dir) - W.add_fingerprint(H) - if(R) - W.color = R.color - qdel(R) - - to_chat(H, "You are [job.total_positions == 1 ? "the" : "a"] [alt_title ? alt_title : rank].") - - if(job.supervisors) - to_chat(H, "As the [alt_title ? alt_title : rank] you answer directly to [job.supervisors]. Special circumstances may change this.") - if(job.has_headset) - H.equip_to_slot_or_del(new /obj/item/device/radio/headset(H), slot_l_ear) - to_chat(H, "To speak on your department's radio channel use :h. For the use of other channels, examine your headset.") - - if(job.req_admin_notify) - to_chat(H, "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp.") - - // EMAIL GENERATION - // Email addresses will be created under this domain name. Mostly for the looks. - var/domain = "freemail.nt" - if(using_map && LAZYLEN(using_map.usable_email_tlds)) - domain = using_map.usable_email_tlds[1] - var/sanitized_name = sanitize(replacetext(replacetext(lowertext(H.real_name), " ", "."), "'", "")) - var/complete_login = "[sanitized_name]@[domain]" - - // It is VERY unlikely that we'll have two players, in the same round, with the same name and branch, but still, this is here. - // If such conflict is encountered, a random number will be appended to the email address. If this fails too, no email account will be created. - if(ntnet_global.does_email_exist(complete_login)) - complete_login = "[sanitized_name][random_id(/datum/computer_file/data/email_account/, 100, 999)]@[domain]" - - // If even fallback login generation failed, just don't give them an email. The chance of this happening is astronomically low. - if(ntnet_global.does_email_exist(complete_login)) - to_chat(H, "You were not assigned an email address.") - H.mind.store_memory("You were not assigned an email address.") - else - var/datum/computer_file/data/email_account/EA = new/datum/computer_file/data/email_account() - EA.password = GenerateKey() - EA.login = complete_login - to_chat(H, "Your email account address is [EA.login] and the password is [EA.password]. This information has also been placed into your notes.") - H.mind.store_memory("Your email account address is [EA.login] and the password is [EA.password].") - // END EMAIL GENERATION - - //Gives glasses to the vision impaired - if(H.disabilities & NEARSIGHTED) - var/equipped = H.equip_to_slot_or_del(new /obj/item/clothing/glasses/regular(H), slot_glasses) - if(equipped != 1) - var/obj/item/clothing/glasses/G = H.glasses - G.prescription = 1 - - BITSET(H.hud_updateflag, ID_HUD) - BITSET(H.hud_updateflag, IMPLOYAL_HUD) - BITSET(H.hud_updateflag, SPECIALROLE_HUD) - return H - -/datum/controller/occupations/proc/LoadJobs(jobsfile) //ran during round setup, reads info from jobs.txt -- Urist - if(!config.load_jobs_from_txt) - return 0 - - var/list/jobEntries = file2list(jobsfile) - - for(var/job in jobEntries) - if(!job) - continue - - job = trim(job) - if (!length(job)) - continue - - var/pos = findtext(job, "=") - var/name = null - var/value = null - - if(pos) - name = copytext(job, 1, pos) - value = copytext(job, pos + 1) - else - continue - - if(name && value) - var/datum/job/J = GetJob(name) - if(!J) continue - J.total_positions = text2num(value) - J.spawn_positions = text2num(value) - if(J.mob_type & JOB_SILICON) - J.total_positions = 0 - - return 1 - - -/datum/controller/occupations/proc/HandleFeedbackGathering() - for(var/datum/job/job in occupations) - var/tmp_str = "|[job.title]|" - - var/level1 = 0 //high - var/level2 = 0 //medium - var/level3 = 0 //low - var/level4 = 0 //never - var/level5 = 0 //banned - var/level6 = 0 //account too young - for(var/mob/new_player/player in player_list) - if(!(player.ready && player.mind && !player.mind.assigned_role)) - continue //This player is not ready - if(jobban_isbanned(player, job.title)) - level5++ - continue - if(!job.player_old_enough(player.client)) - level6++ - continue - //VOREStation Add - if(!job.player_has_enough_playtime(player.client)) - level6++ - continue - //VOREStation Add End - if(player.client.prefs.GetJobDepartment(job, 1) & job.flag) - level1++ - else if(player.client.prefs.GetJobDepartment(job, 2) & job.flag) - level2++ - else if(player.client.prefs.GetJobDepartment(job, 3) & job.flag) - level3++ - else level4++ //not selected - - tmp_str += "HIGH=[level1]|MEDIUM=[level2]|LOW=[level3]|NEVER=[level4]|BANNED=[level5]|YOUNG=[level6]|-" - feedback_add_details("job_preferences",tmp_str) - -/datum/controller/occupations/proc/LateSpawn(var/client/C, var/rank) - - var/datum/spawnpoint/spawnpos - var/fail_deadly = FALSE - - var/datum/job/J = SSjob.get_job(rank) - fail_deadly = J?.offmap_spawn - - //Spawn them at their preferred one - if(C && C.prefs.spawnpoint) - if(!(C.prefs.spawnpoint in using_map.allowed_spawns)) - if(fail_deadly) - to_chat(C, "Your chosen spawnpoint is unavailable for this map and your job requires a specific spawnpoint. Please correct your spawn point choice.") - return - else - to_chat(C, "Your chosen spawnpoint ([C.prefs.spawnpoint]) is unavailable for the current map. Spawning you at one of the enabled spawn points instead.") - spawnpos = null - else - spawnpos = spawntypes[C.prefs.spawnpoint] - - //We will return a list key'd by "turf" and "msg" - . = list("turf","msg") - if(spawnpos && istype(spawnpos) && spawnpos.turfs.len) - if(spawnpos.check_job_spawning(rank)) - .["turf"] = spawnpos.get_spawn_position() - .["msg"] = spawnpos.msg - .["channel"] = spawnpos.announce_channel - else - if(fail_deadly) - to_chat(C, "Your chosen spawnpoint ([spawnpos.display_name]) is unavailable for your chosen job. Please correct your spawn point choice.") - return - to_chat(C, "Your chosen spawnpoint ([spawnpos.display_name]) is unavailable for your chosen job. Spawning you at the Arrivals shuttle instead.") - var/spawning = pick(latejoin) - .["turf"] = get_turf(spawning) - .["msg"] = "will arrive at the station shortly" - else if(!fail_deadly) - var/spawning = pick(latejoin) - .["turf"] = get_turf(spawning) - .["msg"] = "has arrived on the station" +var/global/datum/controller/occupations/job_master + +#define GET_RANDOM_JOB 0 +#define BE_ASSISTANT 1 +#define RETURN_TO_LOBBY 2 + +/datum/controller/occupations + //List of all jobs + var/list/occupations = list() + //Players who need jobs + var/list/unassigned = list() + //Debug info + var/list/job_debug = list() + //Cache of icons for job info window + var/list/job_icons = list() + +/datum/controller/occupations/proc/SetupOccupations(var/faction = "Station") + occupations = list() + //var/list/all_jobs = typesof(/datum/job) + var/list/all_jobs = list(/datum/job/assistant) | using_map.allowed_jobs + if(!all_jobs.len) + to_world("Error setting up jobs, no job datums found!") + return 0 + for(var/J in all_jobs) + var/datum/job/job = new J() + if(!job) continue + if(job.faction != faction) continue + occupations += job + sortTim(occupations, GLOBAL_PROC_REF(cmp_job_datums)) + + + return 1 + + +/datum/controller/occupations/proc/Debug(var/text) + if(!Debug2) return 0 + job_debug.Add(text) + return 1 + + +/datum/controller/occupations/proc/GetJob(var/rank) + if(!rank) return null + for(var/datum/job/J in occupations) + if(!J) continue + if(J.title == rank) return J + return null + +/datum/controller/occupations/proc/GetPlayerAltTitle(mob/new_player/player, rank) + return player.client.prefs.GetPlayerAltTitle(GetJob(rank)) + +/datum/controller/occupations/proc/AssignRole(var/mob/new_player/player, var/rank, var/latejoin = 0) + Debug("Running AR, Player: [player], Rank: [rank], LJ: [latejoin]") + if(player && player.mind && rank) + var/datum/job/job = GetJob(rank) + if(!job) + return 0 + if((job.minimum_character_age || job.min_age_by_species) && (player.client.prefs.age < job.get_min_age(player.client.prefs.species, player.client.prefs.organ_data["brain"]))) + return 0 + if(jobban_isbanned(player, rank)) + return 0 + if(!job.player_old_enough(player.client)) + return 0 + //VOREStation Add + if(!job.player_has_enough_playtime(player.client)) + return 0 + if(!is_job_whitelisted(player, rank)) + return 0 + //VOREStation Add End + + var/position_limit = job.total_positions + if(!latejoin) + position_limit = job.spawn_positions + if((job.current_positions < position_limit) || position_limit == -1) + Debug("Player: [player] is now Rank: [rank], JCP:[job.current_positions], JPL:[position_limit]") + player.mind.assigned_role = rank + player.mind.role_alt_title = GetPlayerAltTitle(player, rank) + unassigned -= player + job.current_positions++ + return 1 + Debug("AR has failed, Player: [player], Rank: [rank]") + return 0 + +/datum/controller/occupations/proc/FreeRole(var/rank) //making additional slot on the fly + var/datum/job/job = GetJob(rank) + if(job && job.total_positions != -1) + job.total_positions++ + return 1 + return 0 + +/datum/controller/occupations/proc/FindOccupationCandidates(datum/job/job, level, flag) + Debug("Running FOC, Job: [job], Level: [level], Flag: [flag]") + var/list/candidates = list() + for(var/mob/new_player/player in unassigned) + if(jobban_isbanned(player, job.title)) + Debug("FOC isbanned failed, Player: [player]") + continue + if(!job.player_old_enough(player.client)) + Debug("FOC player not old enough, Player: [player]") + continue + if(job.minimum_character_age && (player.client.prefs.age < job.get_min_age(player.client.prefs.species, player.client.prefs.organ_data["brain"]))) + Debug("FOC character not old enough, Player: [player]") + continue + //VOREStation Code Start + if(!job.player_has_enough_playtime(player.client)) + Debug("FOC character not enough playtime, Player: [player]") + continue + if(!is_job_whitelisted(player, job.title)) + Debug("FOC is_job_whitelisted failed, Player: [player]") + continue + //VOREStation Code End + if(job.is_species_banned(player.client.prefs.species, player.client.prefs.organ_data["brain"]) == TRUE) + Debug("FOC character species invalid for job, Player: [player]") + continue + if(flag && !(player.client.prefs.be_special & flag)) + Debug("FOC flag failed, Player: [player], Flag: [flag], ") + continue + if(player.client.prefs.GetJobDepartment(job, level) & job.flag) + Debug("FOC pass, Player: [player], Level:[level]") + candidates += player + return candidates + +/datum/controller/occupations/proc/GiveRandomJob(var/mob/new_player/player) + Debug("GRJ Giving random job, Player: [player]") + for(var/datum/job/job in shuffle(occupations)) + if(!job) + continue + + if((job.minimum_character_age || job.min_age_by_species) && (player.client.prefs.age < job.get_min_age(player.client.prefs.species, player.client.prefs.organ_data["brain"]))) + continue + + if(job.is_species_banned(player.client.prefs.species, player.client.prefs.organ_data["brain"]) == TRUE) + continue + + if(istype(job, GetJob(USELESS_JOB))) // We don't want to give him assistant, that's boring! //VOREStation Edit - Visitor not Assistant + continue + + if(SSjob.is_job_in_department(job.title, DEPARTMENT_COMMAND)) //If you want a command position, select it! + continue + + if(jobban_isbanned(player, job.title)) + Debug("GRJ isbanned failed, Player: [player], Job: [job.title]") + continue + + if(!job.player_old_enough(player.client)) + Debug("GRJ player not old enough, Player: [player]") + continue + + //VOREStation Code Start + if(!job.player_has_enough_playtime(player.client)) + Debug("GRJ player not enough playtime, Player: [player]") + continue + if(!is_job_whitelisted(player, job.title)) + Debug("GRJ player not whitelisted for this job, Player: [player], Job: [job.title]") + continue + //VOREStation Code End + + if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1) + Debug("GRJ Random job given, Player: [player], Job: [job]") + AssignRole(player, job.title) + unassigned -= player + break + +/datum/controller/occupations/proc/ResetOccupations() + for(var/mob/new_player/player in player_list) + if((player) && (player.mind)) + player.mind.assigned_role = null + player.mind.special_role = null + SetupOccupations() + unassigned = list() + return + + +///This proc is called before the level loop of DivideOccupations() and will try to select a head, ignoring ALL non-head preferences for every level until it locates a head or runs out of levels to check +/datum/controller/occupations/proc/FillHeadPosition() + for(var/level = 1 to 3) + for(var/command_position in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) + var/datum/job/job = GetJob(command_position) + if(!job) continue + var/list/candidates = FindOccupationCandidates(job, level) + if(!candidates.len) continue + + // Build a weighted list, weight by age. + var/list/weightedCandidates = list() + for(var/mob/V in candidates) + // Log-out during round-start? What a bad boy, no head position for you! + if(!V.client) continue + var/age = V.client.prefs.age + + if(age < job.get_min_age(V.client.prefs.species, V.client.prefs.organ_data["brain"])) // Nope. + continue + + var/idealage = job.get_ideal_age(V.client.prefs.species, V.client.prefs.organ_data["brain"]) + var/agediff = abs(idealage - age) // Compute the absolute difference in age from target + switch(agediff) /// If the math sucks, it's because I almost failed algebra in high school. + if(20 to INFINITY) + weightedCandidates[V] = 3 // Too far off + if(10 to 20) + weightedCandidates[V] = 6 // Nearer the mark, but not quite + if(0 to 10) + weightedCandidates[V] = 10 // On the mark + else + // If there's ABSOLUTELY NOBODY ELSE + if(candidates.len == 1) weightedCandidates[V] = 1 + + + var/mob/new_player/candidate = pickweight(weightedCandidates) + if(AssignRole(candidate, command_position)) + return 1 + return 0 + + +///This proc is called at the start of the level loop of DivideOccupations() and will cause head jobs to be checked before any other jobs of the same level +/datum/controller/occupations/proc/CheckHeadPositions(var/level) + for(var/command_position in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) + var/datum/job/job = GetJob(command_position) + if(!job) continue + var/list/candidates = FindOccupationCandidates(job, level) + if(!candidates.len) continue + var/mob/new_player/candidate = pick(candidates) + AssignRole(candidate, command_position) + return + + +/** Proc DivideOccupations + * fills var "assigned_role" for all ready players. + * This proc must not have any side effect besides of modifying "assigned_role". + **/ +/datum/controller/occupations/proc/DivideOccupations() + //Setup new player list and get the jobs list + Debug("Running DO") + SetupOccupations() + + //Holder for Triumvirate is stored in the ticker, this just processes it + if(ticker && ticker.triai) + for(var/datum/job/A in occupations) + if(A.title == "AI") + A.spawn_positions = 3 + break + + //Get the players who are ready + for(var/mob/new_player/player in player_list) + if(player.ready && player.mind && !player.mind.assigned_role) + unassigned += player + + Debug("DO, Len: [unassigned.len]") + if(unassigned.len == 0) return 0 + + //Shuffle players and jobs + unassigned = shuffle(unassigned) + + HandleFeedbackGathering() + + //People who wants to be assistants, sure, go on. + Debug("DO, Running Assistant Check 1") + var/datum/job/assist = new DEFAULT_JOB_TYPE () + var/list/assistant_candidates = FindOccupationCandidates(assist, 3) + Debug("AC1, Candidates: [assistant_candidates.len]") + for(var/mob/new_player/player in assistant_candidates) + Debug("AC1 pass, Player: [player]") + AssignRole(player, USELESS_JOB) //VOREStation Edit - Visitor not Assistant + assistant_candidates -= player + Debug("DO, AC1 end") + + //Select one head + Debug("DO, Running Head Check") + FillHeadPosition() + Debug("DO, Head Check end") + + //Other jobs are now checked + Debug("DO, Running Standard Check") + + + // New job giving system by Donkie + // This will cause lots of more loops, but since it's only done once it shouldn't really matter much at all. + // Hopefully this will add more randomness and fairness to job giving. + + // Loop through all levels from high to low + var/list/shuffledoccupations = shuffle(occupations) + // var/list/disabled_jobs = ticker.mode.disabled_jobs // So we can use .Find down below without a colon. + for(var/level = 1 to 3) + //Check the head jobs first each level + CheckHeadPositions(level) + + // Loop through all unassigned players + for(var/mob/new_player/player in unassigned) + + // Loop through all jobs + for(var/datum/job/job in shuffledoccupations) // SHUFFLE ME BABY + if(!job || ticker.mode.disabled_jobs.Find(job.title) ) + continue + + if(jobban_isbanned(player, job.title)) + Debug("DO isbanned failed, Player: [player], Job:[job.title]") + continue + + if(!job.player_old_enough(player.client)) + Debug("DO player not old enough, Player: [player], Job:[job.title]") + continue + + //VOREStation Add + if(!job.player_has_enough_playtime(player.client)) + Debug("DO player not enough playtime, Player: [player]") + continue + //VOREStation Add End + + // If the player wants that job on this level, then try give it to him. + if(player.client.prefs.GetJobDepartment(job, level) & job.flag) + + // If the job isn't filled + if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1) + Debug("DO pass, Player: [player], Level:[level], Job:[job.title]") + AssignRole(player, job.title) + unassigned -= player + break + + // Hand out random jobs to the people who didn't get any in the last check + // Also makes sure that they got their preference correct + for(var/mob/new_player/player in unassigned) + if(player.client.prefs.alternate_option == GET_RANDOM_JOB) + GiveRandomJob(player) + /* + Old job system + for(var/level = 1 to 3) + for(var/datum/job/job in occupations) + Debug("Checking job: [job]") + if(!job) + continue + if(!unassigned.len) + break + if((job.current_positions >= job.spawn_positions) && job.spawn_positions != -1) + continue + var/list/candidates = FindOccupationCandidates(job, level) + while(candidates.len && ((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)) + var/mob/new_player/candidate = pick(candidates) + Debug("Selcted: [candidate], for: [job.title]") + AssignRole(candidate, job.title) + candidates -= candidate*/ + + Debug("DO, Standard Check end") + + Debug("DO, Running AC2") + + // For those who wanted to be assistant if their preferences were filled, here you go. + for(var/mob/new_player/player in unassigned) + if(player.client.prefs.alternate_option == BE_ASSISTANT) + Debug("AC2 Assistant located, Player: [player]") + AssignRole(player, USELESS_JOB) //VOREStation Edit - Visitor not Assistant + + //For ones returning to lobby + for(var/mob/new_player/player in unassigned) + if(player.client.prefs.alternate_option == RETURN_TO_LOBBY) + player.ready = 0 + player.new_player_panel_proc() + unassigned -= player + return 1 + + +/datum/controller/occupations/proc/EquipRank(var/mob/living/carbon/human/H, var/rank, var/joined_late = 0) + if(!H) return null + + var/datum/job/job = GetJob(rank) + var/list/spawn_in_storage = list() + + if(!joined_late) + var/obj/S = null + var/list/possible_spawns = list() + for(var/obj/effect/landmark/start/sloc in landmarks_list) + if(sloc.name != rank) continue + if(locate(/mob/living) in sloc.loc) continue + possible_spawns.Add(sloc) + if(possible_spawns.len) + S = pick(possible_spawns) + if(!S) + S = locate("start*[rank]") // use old stype + if(istype(S, /obj/effect/landmark/start) && istype(S.loc, /turf)) + H.forceMove(S.loc) + else + var/list/spawn_props = LateSpawn(H.client, rank) + var/turf/T = spawn_props["turf"] + if(!T) + to_chat(H, "You were unable to be spawned at your chosen late-join spawnpoint. Please verify your job/spawn point combination makes sense, and try another one.") + return + else + H.forceMove(T) + + // Moving wheelchair if they have one + if(H.buckled && istype(H.buckled, /obj/structure/bed/chair/wheelchair)) + H.buckled.forceMove(H.loc) + H.buckled.set_dir(H.dir) + + if(job) + + //Equip custom gear loadout. + var/list/custom_equip_slots = list() + var/list/custom_equip_leftovers = list() + if(H.client && H.client.prefs && H.client.prefs.gear && H.client.prefs.gear.len && !(job.mob_type & JOB_SILICON)) + for(var/thing in H.client.prefs.gear) + var/datum/gear/G = gear_datums[thing] + if(!G) //Not a real gear datum (maybe removed, as this is loaded from their savefile) + continue + + var/permitted + // Check if it is restricted to certain roles + if(G.allowed_roles) + for(var/job_name in G.allowed_roles) + if(job.title == job_name) + permitted = 1 + else + permitted = 1 + + // Check if they're whitelisted for this gear (in alien whitelist? seriously?) + if(G.whitelisted && !is_alien_whitelisted(H, GLOB.all_species[G.whitelisted])) + permitted = 0 + + // If they aren't, tell them + if(!permitted) + to_chat(H, "Your current species, job or whitelist status does not permit you to spawn with [thing]!") + continue + + // Implants get special treatment + if(G.slot == "implant") + var/obj/item/weapon/implant/I = G.spawn_item(H, H.client.prefs.gear[G.display_name]) + I.invisibility = 100 + I.implant_loadout(H) + continue + + // Try desperately (and sorta poorly) to equip the item. Now with increased desperation! + if(G.slot && !(G.slot in custom_equip_slots)) + var/metadata = H.client.prefs.gear[G.display_name] + //if(G.slot == slot_wear_mask || G.slot == slot_wear_suit || G.slot == slot_head) + // custom_equip_leftovers += thing + //else + if(H.equip_to_slot_or_del(G.spawn_item(H, metadata), G.slot)) + to_chat(H, "Equipping you with \the [thing]!") + if(G.slot != slot_tie) + custom_equip_slots.Add(G.slot) + else + custom_equip_leftovers.Add(thing) + else + spawn_in_storage += thing + + // Set up their account + job.setup_account(H) + + // Equip job items. + job.equip(H, H.mind ? H.mind.role_alt_title : "") + + // Stick their fingerprints on literally everything + job.apply_fingerprints(H) + + // Only non-silicons get post-job-equip equipment + if(!(job.mob_type & JOB_SILICON)) + H.equip_post_job() + + // If some custom items could not be equipped before, try again now. + for(var/thing in custom_equip_leftovers) + var/datum/gear/G = gear_datums[thing] + if(G.slot in custom_equip_slots) + spawn_in_storage += thing + else + var/metadata = H.client.prefs.gear[G.display_name] + if(H.equip_to_slot_or_del(G.spawn_item(H, metadata), G.slot)) + to_chat(H, "Equipping you with \the [thing]!") + custom_equip_slots.Add(G.slot) + else + spawn_in_storage += thing + else + to_chat(H, "Your job is [rank] and the game just can't handle it! Please report this bug to an administrator.") + + H.job = rank + log_game("JOINED [key_name(H)] as \"[rank]\"") + log_game("SPECIES [key_name(H)] is a: \"[H.species.name]\"") //VOREStation Add + + // If they're head, give them the account info for their department + if(H.mind && job.department_accounts) + var/remembered_info = "" + for(var/D in job.department_accounts) + var/datum/money_account/department_account = department_accounts[D] + if(department_account) + remembered_info += "Department account number ([D]): #[department_account.account_number]
    " + remembered_info += "Department account pin ([D]): [department_account.remote_access_pin]
    " + remembered_info += "Department account funds ([D]): $[department_account.money]
    " + + H.mind.store_memory(remembered_info) + + var/alt_title = null + if(H.mind) + H.mind.assigned_role = rank + alt_title = H.mind.role_alt_title + + // If we're a silicon, we may be done at this point + if(job.mob_type & JOB_SILICON_ROBOT) + return H.Robotize() + if(job.mob_type & JOB_SILICON_AI) + return H + + // TWEET PEEP + if(rank == "Site Manager") + var/sound/announce_sound = (ticker.current_state <= GAME_STATE_SETTING_UP) ? null : sound('sound/misc/boatswain.ogg', volume=20) + captain_announcement.Announce("All hands, [alt_title ? alt_title : "Site Manager"] [H.real_name] on deck!", new_sound = announce_sound, zlevel = H.z) + + //Deferred item spawning. + if(spawn_in_storage && spawn_in_storage.len) + var/obj/item/weapon/storage/B + for(var/obj/item/weapon/storage/S in H.contents) + B = S + break + + if(!isnull(B)) + for(var/thing in spawn_in_storage) + to_chat(H, "Placing \the [thing] in your [B.name]!") + var/datum/gear/G = gear_datums[thing] + var/metadata = H.client.prefs.gear[G.display_name] + G.spawn_item(B, metadata) + else + to_chat(H, "Failed to locate a storage object on your mob, either you spawned with no arms and no backpack or this is a bug.") + + if(istype(H)) //give humans wheelchairs, if they need them. + var/obj/item/organ/external/l_foot = H.get_organ("l_foot") + var/obj/item/organ/external/r_foot = H.get_organ("r_foot") + var/obj/item/weapon/storage/S = locate() in H.contents + var/obj/item/wheelchair/R + if(S) + R = locate() in S.contents + if(!l_foot || !r_foot || R) + var/wheelchair_type = R?.unfolded_type || /obj/structure/bed/chair/wheelchair + var/obj/structure/bed/chair/wheelchair/W = new wheelchair_type(H.loc) + W.buckle_mob(H) + H.update_canmove() + W.set_dir(H.dir) + W.add_fingerprint(H) + if(R) + W.color = R.color + qdel(R) + + to_chat(H, "You are [job.total_positions == 1 ? "the" : "a"] [alt_title ? alt_title : rank].") + + if(job.supervisors) + to_chat(H, "As the [alt_title ? alt_title : rank] you answer directly to [job.supervisors]. Special circumstances may change this.") + if(job.has_headset) + H.equip_to_slot_or_del(new /obj/item/device/radio/headset(H), slot_l_ear) + to_chat(H, "To speak on your department's radio channel use :h. For the use of other channels, examine your headset.") + + if(job.req_admin_notify) + to_chat(H, "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp.") + + // EMAIL GENERATION + // Email addresses will be created under this domain name. Mostly for the looks. + var/domain = "freemail.nt" + if(using_map && LAZYLEN(using_map.usable_email_tlds)) + domain = using_map.usable_email_tlds[1] + var/sanitized_name = sanitize(replacetext(replacetext(lowertext(H.real_name), " ", "."), "'", "")) + var/complete_login = "[sanitized_name]@[domain]" + + // It is VERY unlikely that we'll have two players, in the same round, with the same name and branch, but still, this is here. + // If such conflict is encountered, a random number will be appended to the email address. If this fails too, no email account will be created. + if(ntnet_global.does_email_exist(complete_login)) + complete_login = "[sanitized_name][random_id(/datum/computer_file/data/email_account/, 100, 999)]@[domain]" + + // If even fallback login generation failed, just don't give them an email. The chance of this happening is astronomically low. + if(ntnet_global.does_email_exist(complete_login)) + to_chat(H, "You were not assigned an email address.") + H.mind.store_memory("You were not assigned an email address.") + else + var/datum/computer_file/data/email_account/EA = new/datum/computer_file/data/email_account() + EA.password = GenerateKey() + EA.login = complete_login + to_chat(H, "Your email account address is [EA.login] and the password is [EA.password]. This information has also been placed into your notes.") + H.mind.store_memory("Your email account address is [EA.login] and the password is [EA.password].") + // END EMAIL GENERATION + + //Gives glasses to the vision impaired + if(H.disabilities & NEARSIGHTED) + var/equipped = H.equip_to_slot_or_del(new /obj/item/clothing/glasses/regular(H), slot_glasses) + if(equipped != 1) + var/obj/item/clothing/glasses/G = H.glasses + G.prescription = 1 + + BITSET(H.hud_updateflag, ID_HUD) + BITSET(H.hud_updateflag, IMPLOYAL_HUD) + BITSET(H.hud_updateflag, SPECIALROLE_HUD) + return H + +/datum/controller/occupations/proc/LoadJobs(jobsfile) //ran during round setup, reads info from jobs.txt -- Urist + if(!config.load_jobs_from_txt) + return 0 + + var/list/jobEntries = file2list(jobsfile) + + for(var/job in jobEntries) + if(!job) + continue + + job = trim(job) + if (!length(job)) + continue + + var/pos = findtext(job, "=") + var/name = null + var/value = null + + if(pos) + name = copytext(job, 1, pos) + value = copytext(job, pos + 1) + else + continue + + if(name && value) + var/datum/job/J = GetJob(name) + if(!J) continue + J.total_positions = text2num(value) + J.spawn_positions = text2num(value) + if(J.mob_type & JOB_SILICON) + J.total_positions = 0 + + return 1 + + +/datum/controller/occupations/proc/HandleFeedbackGathering() + for(var/datum/job/job in occupations) + var/tmp_str = "|[job.title]|" + + var/level1 = 0 //high + var/level2 = 0 //medium + var/level3 = 0 //low + var/level4 = 0 //never + var/level5 = 0 //banned + var/level6 = 0 //account too young + for(var/mob/new_player/player in player_list) + if(!(player.ready && player.mind && !player.mind.assigned_role)) + continue //This player is not ready + if(jobban_isbanned(player, job.title)) + level5++ + continue + if(!job.player_old_enough(player.client)) + level6++ + continue + //VOREStation Add + if(!job.player_has_enough_playtime(player.client)) + level6++ + continue + //VOREStation Add End + if(player.client.prefs.GetJobDepartment(job, 1) & job.flag) + level1++ + else if(player.client.prefs.GetJobDepartment(job, 2) & job.flag) + level2++ + else if(player.client.prefs.GetJobDepartment(job, 3) & job.flag) + level3++ + else level4++ //not selected + + tmp_str += "HIGH=[level1]|MEDIUM=[level2]|LOW=[level3]|NEVER=[level4]|BANNED=[level5]|YOUNG=[level6]|-" + feedback_add_details("job_preferences",tmp_str) + +/datum/controller/occupations/proc/LateSpawn(var/client/C, var/rank) + + var/datum/spawnpoint/spawnpos + var/fail_deadly = FALSE + + var/datum/job/J = SSjob.get_job(rank) + fail_deadly = J?.offmap_spawn + + //Spawn them at their preferred one + if(C && C.prefs.spawnpoint) + if(!(C.prefs.spawnpoint in using_map.allowed_spawns)) + if(fail_deadly) + to_chat(C, "Your chosen spawnpoint is unavailable for this map and your job requires a specific spawnpoint. Please correct your spawn point choice.") + return + else + to_chat(C, "Your chosen spawnpoint ([C.prefs.spawnpoint]) is unavailable for the current map. Spawning you at one of the enabled spawn points instead.") + spawnpos = null + else + spawnpos = spawntypes[C.prefs.spawnpoint] + + //We will return a list key'd by "turf" and "msg" + . = list("turf","msg") + if(spawnpos && istype(spawnpos) && spawnpos.turfs.len) + if(spawnpos.check_job_spawning(rank)) + .["turf"] = spawnpos.get_spawn_position() + .["msg"] = spawnpos.msg + .["channel"] = spawnpos.announce_channel + else + if(fail_deadly) + to_chat(C, "Your chosen spawnpoint ([spawnpos.display_name]) is unavailable for your chosen job. Please correct your spawn point choice.") + return + to_chat(C, "Your chosen spawnpoint ([spawnpos.display_name]) is unavailable for your chosen job. Spawning you at the Arrivals shuttle instead.") + var/spawning = pick(latejoin) + .["turf"] = get_turf(spawning) + .["msg"] = "will arrive at the station shortly" + else if(!fail_deadly) + var/spawning = pick(latejoin) + .["turf"] = get_turf(spawning) + .["msg"] = "has arrived on the station" diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm index 90bef78fc0a..ad7860ee4a4 100644 --- a/code/game/jobs/jobs.dm +++ b/code/game/jobs/jobs.dm @@ -1,90 +1,90 @@ - -var/const/ENGSEC =(1<<0) - -var/const/CAPTAIN =(1<<0) -var/const/HOS =(1<<1) -var/const/WARDEN =(1<<2) -var/const/DETECTIVE =(1<<3) -var/const/OFFICER =(1<<4) -var/const/CHIEF =(1<<5) -var/const/ENGINEER =(1<<6) -var/const/ATMOSTECH =(1<<7) -var/const/AI =(1<<8) -var/const/CYBORG =(1<<9) -var/const/CLOWN =(1<<13) -var/const/MIME =(1<<14) -var/const/INTERN =(1<<15) -var/const/BLUESHIELD =(1<<16) //YW addition -var/const/SECPILOT =(1<<17) //YW addition - -var/const/MEDSCI =(1<<1) - -var/const/RD =(1<<0) -var/const/SCIENTIST =(1<<1) -var/const/CHEMIST =(1<<2) -var/const/CMO =(1<<3) -var/const/DOCTOR =(1<<4) -var/const/GENETICIST =(1<<5) -var/const/VIROLOGIST =(1<<6) -var/const/PSYCHIATRIST =(1<<7) -var/const/ROBOTICIST =(1<<8) -var/const/XENOBIOLOGIST =(1<<9) -var/const/PARAMEDIC =(1<<10) -var/const/PATHFINDER =(1<<11) -var/const/EXPLORER =(1<<12) -var/const/SAR =(1<<13) -var/const/XENOBOTANIST =(1<<14) - -var/const/CIVILIAN =(1<<2) - -var/const/HOP =(1<<0) -var/const/BARTENDER =(1<<1) -var/const/BOTANIST =(1<<2) -var/const/CHEF =(1<<3) -var/const/JANITOR =(1<<4) -var/const/LIBRARIAN =(1<<5) -var/const/QUARTERMASTER =(1<<6) -var/const/CARGOTECH =(1<<7) -var/const/MINER =(1<<8) -var/const/LAWYER =(1<<9) -var/const/CHAPLAIN =(1<<10) -var/const/ASSISTANT =(1<<11) -var/const/BRIDGE =(1<<12) -var/const/PILOT =(1<<13) -var/const/ENTERTAINER =(1<<14) -var/const/ENTREPRENEUR =(1<<15) - -//YAWN Add START -var/const/TALON =(1<<3) - -var/const/TALCAP =(1<<0) -var/const/TALPIL =(1<<1) -var/const/TALDOC =(1<<2) -var/const/TALSEC =(1<<3) -var/const/TALENG =(1<<4) -var/const/TALMIN =(1<<5) -//YAWN Add END - -/proc/guest_jobbans(var/job) - return ( (job in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) || (job in SSjob.get_job_titles_in_department(DEPARTMENT_SYNTHETIC)) || (job in SSjob.get_job_titles_in_department(DEPARTMENT_SECURITY)) ) - -/proc/get_job_datums() - var/list/occupations = list() - var/list/all_jobs = typesof(/datum/job) - - for(var/A in all_jobs) - var/datum/job/job = new A() - if(!job) continue - occupations += job - - return occupations - -/proc/get_alternate_titles(var/job) - var/list/jobs = get_job_datums() - var/list/titles = list() - - for(var/datum/job/J in jobs) - if(J.title == job) - titles = J.alt_titles - - return titles + +var/const/ENGSEC =(1<<0) + +var/const/CAPTAIN =(1<<0) +var/const/HOS =(1<<1) +var/const/WARDEN =(1<<2) +var/const/DETECTIVE =(1<<3) +var/const/OFFICER =(1<<4) +var/const/CHIEF =(1<<5) +var/const/ENGINEER =(1<<6) +var/const/ATMOSTECH =(1<<7) +var/const/AI =(1<<8) +var/const/CYBORG =(1<<9) +var/const/CLOWN =(1<<13) +var/const/MIME =(1<<14) +var/const/INTERN =(1<<15) +var/const/BLUESHIELD =(1<<16) //YW addition +var/const/SECPILOT =(1<<17) //YW addition + +var/const/MEDSCI =(1<<1) + +var/const/RD =(1<<0) +var/const/SCIENTIST =(1<<1) +var/const/CHEMIST =(1<<2) +var/const/CMO =(1<<3) +var/const/DOCTOR =(1<<4) +var/const/GENETICIST =(1<<5) +var/const/VIROLOGIST =(1<<6) +var/const/PSYCHIATRIST =(1<<7) +var/const/ROBOTICIST =(1<<8) +var/const/XENOBIOLOGIST =(1<<9) +var/const/PARAMEDIC =(1<<10) +var/const/PATHFINDER =(1<<11) +var/const/EXPLORER =(1<<12) +var/const/SAR =(1<<13) +var/const/XENOBOTANIST =(1<<14) + +var/const/CIVILIAN =(1<<2) + +var/const/HOP =(1<<0) +var/const/BARTENDER =(1<<1) +var/const/BOTANIST =(1<<2) +var/const/CHEF =(1<<3) +var/const/JANITOR =(1<<4) +var/const/LIBRARIAN =(1<<5) +var/const/QUARTERMASTER =(1<<6) +var/const/CARGOTECH =(1<<7) +var/const/MINER =(1<<8) +var/const/LAWYER =(1<<9) +var/const/CHAPLAIN =(1<<10) +var/const/ASSISTANT =(1<<11) +var/const/BRIDGE =(1<<12) +var/const/PILOT =(1<<13) +var/const/ENTERTAINER =(1<<14) +var/const/ENTREPRENEUR =(1<<15) + +//YAWN Add START +var/const/TALON =(1<<3) + +var/const/TALCAP =(1<<0) +var/const/TALPIL =(1<<1) +var/const/TALDOC =(1<<2) +var/const/TALSEC =(1<<3) +var/const/TALENG =(1<<4) +var/const/TALMIN =(1<<5) +//YAWN Add END + +/proc/guest_jobbans(var/job) + return ( (job in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) || (job in SSjob.get_job_titles_in_department(DEPARTMENT_SYNTHETIC)) || (job in SSjob.get_job_titles_in_department(DEPARTMENT_SECURITY)) ) + +/proc/get_job_datums() + var/list/occupations = list() + var/list/all_jobs = typesof(/datum/job) + + for(var/A in all_jobs) + var/datum/job/job = new A() + if(!job) continue + occupations += job + + return occupations + +/proc/get_alternate_titles(var/job) + var/list/jobs = get_job_datums() + var/list/titles = list() + + for(var/datum/job/J in jobs) + if(J.title == job) + titles = J.alt_titles + + return titles diff --git a/code/game/jobs/whitelist.dm b/code/game/jobs/whitelist.dm index b47e3787a85..d32da8494c5 100644 --- a/code/game/jobs/whitelist.dm +++ b/code/game/jobs/whitelist.dm @@ -1,121 +1,121 @@ -#define WHITELISTFILE "data/whitelist.txt" - -var/list/whitelist = list() - -/hook/startup/proc/loadWhitelist() - if(config.usewhitelist) - load_whitelist() - return 1 - -/proc/load_whitelist() - whitelist = file2list(WHITELISTFILE) - if(!whitelist.len) whitelist = null - -/proc/check_whitelist(mob/M /*, var/rank*/) - if(!whitelist) - return 0 - return ("[M.ckey]" in whitelist) - -/var/list/alien_whitelist = list() - -/hook/startup/proc/loadAlienWhitelist() - if(config.usealienwhitelist) - load_alienwhitelist() - return 1 - -/proc/load_alienwhitelist() - var/text = file2text("config/alienwhitelist.txt") - if (!text) - log_misc("Failed to load config/alienwhitelist.txt") - else - var/lines = splittext(text, "\n") // Now we've got a bunch of "ckey = something" strings in a list - for(var/line in lines) - var/list/left_and_right = splittext(line, " - ") // Split it on the dash into left and right - if(LAZYLEN(left_and_right) != 2) - warning("Alien whitelist entry is invalid: [line]") // If we didn't end up with a left and right, the line is bad - continue - var/key = left_and_right[1] - if(key != ckey(key)) - warning("Alien whitelist entry appears to have key, not ckey: [line]") // The key contains invalid ckey characters - continue - var/list/our_whitelists = alien_whitelist[key] // Try to see if we have one already and add to it - if(!our_whitelists) // Guess this is their first/only whitelist entry - our_whitelists = list() - alien_whitelist[key] = our_whitelists - our_whitelists += left_and_right[2] - -/proc/is_alien_whitelisted(mob/M, var/datum/species/species) - //They are admin or the whitelist isn't in use - if(whitelist_overrides(M)) - return TRUE - - //You did something wrong - if(!M || !species) - return FALSE - - //The species isn't even whitelisted - if(!(species.spawn_flags & SPECIES_IS_WHITELISTED)) - return TRUE - - //Search the whitelist - var/list/our_whitelists = alien_whitelist[M.ckey] - if("All" in our_whitelists) - return TRUE - if(species.name in our_whitelists) - return TRUE - - // Go apply! - return FALSE - -/proc/is_lang_whitelisted(mob/M, var/datum/language/language) - //They are admin or the whitelist isn't in use - if(whitelist_overrides(M)) - return TRUE - - //You did something wrong - if(!M || !language) - return FALSE - - //The language isn't even whitelisted - if(!(language.flags & WHITELISTED)) - return TRUE - - //Search the whitelist - var/list/our_whitelists = alien_whitelist[M.ckey] - if("All" in our_whitelists) - return TRUE - if(language.name in our_whitelists) - return TRUE - - return FALSE - -/proc/is_borg_whitelisted(mob/M, var/module) - //They are admin or the whitelist isn't in use - if(whitelist_overrides(M)) - return 1 - - //You did something wrong - if(!M || !module) - return 0 - - //Module is not even whitelisted - if(!(module in whitelisted_module_types)) - return 1 - - //If we have a loaded file, search it - if(alien_whitelist) - for (var/s in alien_whitelist) - if(findtext(s,"[M.ckey] - [module]")) - return 1 - if(findtext(s,"[M.ckey] - All")) - return 1 - -/proc/whitelist_overrides(mob/M) - if(!config.usealienwhitelist) - return TRUE - if(check_rights(R_ADMIN|R_EVENT, 0, M)) - return TRUE - - return FALSE - -#undef WHITELISTFILE +#define WHITELISTFILE "data/whitelist.txt" + +var/list/whitelist = list() + +/hook/startup/proc/loadWhitelist() + if(config.usewhitelist) + load_whitelist() + return 1 + +/proc/load_whitelist() + whitelist = file2list(WHITELISTFILE) + if(!whitelist.len) whitelist = null + +/proc/check_whitelist(mob/M /*, var/rank*/) + if(!whitelist) + return 0 + return ("[M.ckey]" in whitelist) + +/var/list/alien_whitelist = list() + +/hook/startup/proc/loadAlienWhitelist() + if(config.usealienwhitelist) + load_alienwhitelist() + return 1 + +/proc/load_alienwhitelist() + var/text = file2text("config/alienwhitelist.txt") + if (!text) + log_misc("Failed to load config/alienwhitelist.txt") + else + var/lines = splittext(text, "\n") // Now we've got a bunch of "ckey = something" strings in a list + for(var/line in lines) + var/list/left_and_right = splittext(line, " - ") // Split it on the dash into left and right + if(LAZYLEN(left_and_right) != 2) + warning("Alien whitelist entry is invalid: [line]") // If we didn't end up with a left and right, the line is bad + continue + var/key = left_and_right[1] + if(key != ckey(key)) + warning("Alien whitelist entry appears to have key, not ckey: [line]") // The key contains invalid ckey characters + continue + var/list/our_whitelists = alien_whitelist[key] // Try to see if we have one already and add to it + if(!our_whitelists) // Guess this is their first/only whitelist entry + our_whitelists = list() + alien_whitelist[key] = our_whitelists + our_whitelists += left_and_right[2] + +/proc/is_alien_whitelisted(mob/M, var/datum/species/species) + //They are admin or the whitelist isn't in use + if(whitelist_overrides(M)) + return TRUE + + //You did something wrong + if(!M || !species) + return FALSE + + //The species isn't even whitelisted + if(!(species.spawn_flags & SPECIES_IS_WHITELISTED)) + return TRUE + + //Search the whitelist + var/list/our_whitelists = alien_whitelist[M.ckey] + if("All" in our_whitelists) + return TRUE + if(species.name in our_whitelists) + return TRUE + + // Go apply! + return FALSE + +/proc/is_lang_whitelisted(mob/M, var/datum/language/language) + //They are admin or the whitelist isn't in use + if(whitelist_overrides(M)) + return TRUE + + //You did something wrong + if(!M || !language) + return FALSE + + //The language isn't even whitelisted + if(!(language.flags & WHITELISTED)) + return TRUE + + //Search the whitelist + var/list/our_whitelists = alien_whitelist[M.ckey] + if("All" in our_whitelists) + return TRUE + if(language.name in our_whitelists) + return TRUE + + return FALSE + +/proc/is_borg_whitelisted(mob/M, var/module) + //They are admin or the whitelist isn't in use + if(whitelist_overrides(M)) + return 1 + + //You did something wrong + if(!M || !module) + return 0 + + //Module is not even whitelisted + if(!(module in whitelisted_module_types)) + return 1 + + //If we have a loaded file, search it + if(alien_whitelist) + for (var/s in alien_whitelist) + if(findtext(s,"[M.ckey] - [module]")) + return 1 + if(findtext(s,"[M.ckey] - All")) + return 1 + +/proc/whitelist_overrides(mob/M) + if(!config.usealienwhitelist) + return TRUE + if(check_rights(R_ADMIN|R_EVENT, 0, M)) + return TRUE + + return FALSE + +#undef WHITELISTFILE diff --git a/code/game/machinery/Beacon.dm b/code/game/machinery/Beacon.dm index 24b4ca979b4..b9242059c90 100644 --- a/code/game/machinery/Beacon.dm +++ b/code/game/machinery/Beacon.dm @@ -1,51 +1,51 @@ -/obj/machinery/bluespace_beacon - icon = 'icons/obj/objects.dmi' - icon_state = "floor_beaconf" - name = "Bluespace Gigabeacon" - desc = "A device that draws power from bluespace and creates a permanent tracking beacon." - level = 1 // underfloor - layer = UNDER_JUNK_LAYER - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 0 - var/obj/item/device/radio/beacon/Beacon - -/obj/machinery/bluespace_beacon/New() - ..() - var/turf/T = src.loc - Beacon = new /obj/item/device/radio/beacon - Beacon.invisibility = INVISIBILITY_MAXIMUM - Beacon.loc = T - - hide(!T.is_plating()) - -/obj/machinery/bluespace_beacon/Destroy() - if(Beacon) - qdel(Beacon) - ..() - -// update the invisibility and icon -/obj/machinery/bluespace_beacon/hide(var/intact) - invisibility = intact ? 101 : 0 - update_icon() - -// update the icon_state -/obj/machinery/bluespace_beacon/update_icon() - var/state="floor_beacon" - - if(invisibility) - icon_state = "[state]f" - else - icon_state = "[state]" - -/obj/machinery/bluespace_beacon/process() - if(!Beacon) - var/turf/T = src.loc - Beacon = new /obj/item/device/radio/beacon - Beacon.invisibility = INVISIBILITY_MAXIMUM - Beacon.loc = T - if(Beacon) - if(Beacon.loc != src.loc) - Beacon.loc = src.loc - +/obj/machinery/bluespace_beacon + icon = 'icons/obj/objects.dmi' + icon_state = "floor_beaconf" + name = "Bluespace Gigabeacon" + desc = "A device that draws power from bluespace and creates a permanent tracking beacon." + level = 1 // underfloor + layer = UNDER_JUNK_LAYER + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 0 + var/obj/item/device/radio/beacon/Beacon + +/obj/machinery/bluespace_beacon/New() + ..() + var/turf/T = src.loc + Beacon = new /obj/item/device/radio/beacon + Beacon.invisibility = INVISIBILITY_MAXIMUM + Beacon.loc = T + + hide(!T.is_plating()) + +/obj/machinery/bluespace_beacon/Destroy() + if(Beacon) + qdel(Beacon) + ..() + +// update the invisibility and icon +/obj/machinery/bluespace_beacon/hide(var/intact) + invisibility = intact ? 101 : 0 + update_icon() + +// update the icon_state +/obj/machinery/bluespace_beacon/update_icon() + var/state="floor_beacon" + + if(invisibility) + icon_state = "[state]f" + else + icon_state = "[state]" + +/obj/machinery/bluespace_beacon/process() + if(!Beacon) + var/turf/T = src.loc + Beacon = new /obj/item/device/radio/beacon + Beacon.invisibility = INVISIBILITY_MAXIMUM + Beacon.loc = T + if(Beacon) + if(Beacon.loc != src.loc) + Beacon.loc = src.loc + update_icon() \ No newline at end of file diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index fcabe9f5ce0..2dbe70576e1 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -1,136 +1,136 @@ -/obj/machinery/optable - name = "Operating Table" - desc = "Used for advanced medical procedures." - icon = 'icons/obj/surgery_vr.dmi' - icon_state = "table2-idle" - density = TRUE - anchored = TRUE - unacidable = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 1 - active_power_usage = 5 - surgery_odds = 100 - throwpass = 1 - var/mob/living/carbon/human/victim = null - var/strapped = 0.0 - var/obj/machinery/computer/operating/computer = null - -/obj/machinery/optable/New() - ..() - for(var/direction in list(NORTH,EAST,SOUTH,WEST)) - computer = locate(/obj/machinery/computer/operating, get_step(src, direction)) - if(computer) - computer.table = src - break -// spawn(100) //Wont the MC just call this process() before and at the 10 second mark anyway? -// process() - -/obj/machinery/optable/ex_act(severity) - switch(severity) - if(1.0) - //SN src = null - qdel(src) - return - if(2.0) - if(prob(50)) - //SN src = null - qdel(src) - return - if(3.0) - if(prob(25)) - density = FALSE - else - return - -/obj/machinery/optable/attack_hand(mob/user as mob) - if(HULK in usr.mutations) - visible_message("\The [usr] destroys \the [src]!") - density = FALSE - qdel(src) - return - -/obj/machinery/optable/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSTABLE)) - return TRUE - return FALSE - -/obj/machinery/optable/proc/check_victim() - if(locate(/mob/living/carbon/human, src.loc)) - var/mob/living/carbon/human/M = locate(/mob/living/carbon/human, src.loc) - if(M.lying) - victim = M - if(M.pulse) - if(M.stat) - icon_state = "table2-sleep" - else - icon_state = "table2-active" - else - icon_state = "table2-dead" - return 1 - victim = null - icon_state = "table2-idle" - return 0 - -/obj/machinery/optable/process() - check_victim() - -/obj/machinery/optable/proc/take_victim(mob/living/carbon/C, mob/living/carbon/user as mob) - if(C == user) - user.visible_message("[user] climbs on \the [src].","You climb on \the [src].") - else - visible_message("\The [C] has been laid on \the [src] by [user].") - if(C.client) - C.client.perspective = EYE_PERSPECTIVE - C.client.eye = src - C.resting = 1 - C.loc = src.loc - for(var/obj/O in src) - O.loc = src.loc - add_fingerprint(user) - if(ishuman(C)) - var/mob/living/carbon/human/H = C - victim = H - icon_state = H.pulse ? "table2-active" : "table2-idle" - else - icon_state = "table2-idle" - -/obj/machinery/optable/MouseDrop_T(mob/living/carbon/target, mob/living/user) - if(!istype(target) || !istype(user)) - return ..() - - if(!Adjacent(target) || !Adjacent(user)) - return ..() - - if(user.incapacitated() || !check_table(target, user)) - return ..() - - take_victim(target, user) - -/obj/machinery/optable/verb/climb_on() - set name = "Climb On Table" - set category = "Object" - set src in oview(1) - - var/mob/living/user = usr - if(!istype(user) || user.incapacitated() || !check_table(user, user)) - return - - take_victim(user, user) - -/obj/machinery/optable/attackby(obj/item/weapon/W, mob/living/carbon/user) - if(istype(W, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/G = W - if(iscarbon(G.affecting) && check_table(G.affecting, user)) - take_victim(G.affecting, user) - qdel(W) - return - -/obj/machinery/optable/proc/check_table(mob/living/carbon/patient, mob/living/user) - check_victim() - if(victim && get_turf(victim) == get_turf(src) && victim.lying) - to_chat(user, "\The [src] is already occupied!") - return 0 - if(patient.buckled) - to_chat(user, "Unbuckle \the [patient] first!") - return 0 +/obj/machinery/optable + name = "Operating Table" + desc = "Used for advanced medical procedures." + icon = 'icons/obj/surgery_vr.dmi' + icon_state = "table2-idle" + density = TRUE + anchored = TRUE + unacidable = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 1 + active_power_usage = 5 + surgery_odds = 100 + throwpass = 1 + var/mob/living/carbon/human/victim = null + var/strapped = 0.0 + var/obj/machinery/computer/operating/computer = null + +/obj/machinery/optable/New() + ..() + for(var/direction in list(NORTH,EAST,SOUTH,WEST)) + computer = locate(/obj/machinery/computer/operating, get_step(src, direction)) + if(computer) + computer.table = src + break +// spawn(100) //Wont the MC just call this process() before and at the 10 second mark anyway? +// process() + +/obj/machinery/optable/ex_act(severity) + switch(severity) + if(1.0) + //SN src = null + qdel(src) + return + if(2.0) + if(prob(50)) + //SN src = null + qdel(src) + return + if(3.0) + if(prob(25)) + density = FALSE + else + return + +/obj/machinery/optable/attack_hand(mob/user as mob) + if(HULK in usr.mutations) + visible_message("\The [usr] destroys \the [src]!") + density = FALSE + qdel(src) + return + +/obj/machinery/optable/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSTABLE)) + return TRUE + return FALSE + +/obj/machinery/optable/proc/check_victim() + if(locate(/mob/living/carbon/human, src.loc)) + var/mob/living/carbon/human/M = locate(/mob/living/carbon/human, src.loc) + if(M.lying) + victim = M + if(M.pulse) + if(M.stat) + icon_state = "table2-sleep" + else + icon_state = "table2-active" + else + icon_state = "table2-dead" + return 1 + victim = null + icon_state = "table2-idle" + return 0 + +/obj/machinery/optable/process() + check_victim() + +/obj/machinery/optable/proc/take_victim(mob/living/carbon/C, mob/living/carbon/user as mob) + if(C == user) + user.visible_message("[user] climbs on \the [src].","You climb on \the [src].") + else + visible_message("\The [C] has been laid on \the [src] by [user].") + if(C.client) + C.client.perspective = EYE_PERSPECTIVE + C.client.eye = src + C.resting = 1 + C.loc = src.loc + for(var/obj/O in src) + O.loc = src.loc + add_fingerprint(user) + if(ishuman(C)) + var/mob/living/carbon/human/H = C + victim = H + icon_state = H.pulse ? "table2-active" : "table2-idle" + else + icon_state = "table2-idle" + +/obj/machinery/optable/MouseDrop_T(mob/living/carbon/target, mob/living/user) + if(!istype(target) || !istype(user)) + return ..() + + if(!Adjacent(target) || !Adjacent(user)) + return ..() + + if(user.incapacitated() || !check_table(target, user)) + return ..() + + take_victim(target, user) + +/obj/machinery/optable/verb/climb_on() + set name = "Climb On Table" + set category = "Object" + set src in oview(1) + + var/mob/living/user = usr + if(!istype(user) || user.incapacitated() || !check_table(user, user)) + return + + take_victim(user, user) + +/obj/machinery/optable/attackby(obj/item/weapon/W, mob/living/carbon/user) + if(istype(W, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = W + if(iscarbon(G.affecting) && check_table(G.affecting, user)) + take_victim(G.affecting, user) + qdel(W) + return + +/obj/machinery/optable/proc/check_table(mob/living/carbon/patient, mob/living/user) + check_victim() + if(victim && get_turf(victim) == get_turf(src) && victim.lying) + to_chat(user, "\The [src] is already occupied!") + return 0 + if(patient.buckled) + to_chat(user, "Unbuckle \the [patient] first!") + return 0 return 1 \ No newline at end of file diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 18765fcf9b0..bb86420b1c3 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -1,537 +1,537 @@ -/obj/machinery/sleep_console - name = "sleeper console" - desc = "A control panel to operate a linked sleeper with." - icon = 'icons/obj/Cryogenic2_vr.dmi' //VOREStation Edit - Better icon. - icon_state = "sleeperconsole" - var/obj/machinery/sleeper/sleeper - anchored = TRUE //About time someone fixed this. - density = TRUE //VOREStation Edit - Big console - unacidable = TRUE - dir = 8 - use_power = USE_POWER_IDLE - idle_power_usage = 40 - interact_offline = 1 - circuit = /obj/item/weapon/circuitboard/sleeper_console - clicksound = 'sound/machines/buttonbeep.ogg' - clickvol = 30 - -/obj/machinery/sleep_console/Initialize() - findsleeper() - return ..() - -/obj/machinery/sleep_console/Destroy() - if(sleeper) - sleeper.console = null - return ..() - -/obj/machinery/sleep_console/proc/findsleeper() - var/obj/machinery/sleeper/sleepernew = null - for(var/direction in GLOB.cardinal) // Loop through every direction - sleepernew = locate(/obj/machinery/sleeper, get_step(src, direction)) // Try to find a scanner in that direction - if(sleepernew) - sleeper = sleepernew - sleepernew.console = src - break //VOREStation Edit - - -/obj/machinery/sleep_console/attack_ai(var/mob/user) - return attack_hand(user) - -/obj/machinery/sleep_console/attack_hand(var/mob/user) - if(..()) - return 1 - - if(!sleeper) - findsleeper() - if(!sleeper) - to_chat(user, "Sleeper not found!") - return - - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - - if(sleeper) - return tgui_interact(user) - -/obj/machinery/sleep_console/attackby(var/obj/item/I, var/mob/user) - if(computer_deconstruction_screwdriver(user, I)) - return - else - return attack_hand(user) - -/obj/machinery/sleep_console/power_change() - ..() - if(stat & (NOPOWER|BROKEN)) - icon_state = "sleeperconsole-p" - else - icon_state = initial(icon_state) - -/obj/machinery/sleep_console/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Sleeper", "Sleeper") - ui.open() - -/obj/machinery/sleep_console/tgui_data(mob/user) - if(sleeper) - return sleeper.tgui_data(user) - return null - -/obj/machinery/sleep_console/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) - if(sleeper) - return sleeper.tgui_act(action, params, ui, state) - return ..() - -/obj/machinery/sleeper - name = "sleeper" - desc = "A stasis pod with built-in injectors, a dialysis machine, and a limited health scanner." - icon = 'icons/obj/Cryogenic2_vr.dmi' //VOREStation Edit - Better icons - icon_state = "sleeper_0" - density = TRUE - anchored = TRUE - unacidable = TRUE - circuit = /obj/item/weapon/circuitboard/sleeper - var/mob/living/carbon/human/occupant = null - var/list/available_chemicals = list() - var/list/base_chemicals = list("inaprovaline" = "Inaprovaline", "paracetamol" = "Paracetamol", "anti_toxin" = "Dylovene", "dexalin" = "Dexalin") - var/amounts = list(5, 10) - var/obj/item/weapon/reagent_containers/glass/beaker = null - var/filtering = 0 - var/pumping = 0 - // Currently never changes. On Paradise, max_chem and min_health are based on the matter bins in the sleeper. - var/max_chem = 20 - var/initial_bin_rating = 1 - var/min_health = -101 - var/obj/machinery/sleep_console/console - var/stasis_level = 0 //Every 'this' life ticks are applied to the mob (when life_ticks%stasis_level == 1) - var/stasis_choices = list("Complete (1%)" = 100, "Deep (10%)" = 10, "Moderate (20%)" = 5, "Light (50%)" = 2, "None (100%)" = 0) - var/controls_inside = FALSE - var/auto_eject_dead = FALSE - - use_power = USE_POWER_IDLE - idle_power_usage = 15 - active_power_usage = 200 //builtin health analyzer, dialysis machine, injectors. - -/obj/machinery/sleeper/Initialize() - . = ..() - beaker = new /obj/item/weapon/reagent_containers/glass/beaker/large(src) - default_apply_parts() - update_icon() - -/obj/machinery/sleeper/Destroy() - if(console) - console.sleeper = null - return ..() - -/obj/machinery/sleeper/RefreshParts(var/limited = 0) - var/man_rating = 0 - var/cap_rating = 0 - - available_chemicals.Cut() - available_chemicals = base_chemicals.Copy() - - for(var/obj/item/weapon/stock_parts/P in component_parts) - if(istype(P, /obj/item/weapon/stock_parts/capacitor)) - cap_rating += P.rating - - cap_rating = max(1, round(cap_rating / 2)) - - update_idle_power_usage(initial(idle_power_usage) / cap_rating) - update_active_power_usage(initial(active_power_usage) / cap_rating) - - if(!limited) - for(var/obj/item/weapon/stock_parts/P in component_parts) - if(istype(P, /obj/item/weapon/stock_parts/manipulator)) - man_rating += P.rating - 1 - - var/list/new_chemicals = list() - - if(man_rating >= 4) // Alien tech. - var/reag_ID = pickweight(list( - "healing_nanites" = 10, - "shredding_nanites" = 5, - "irradiated_nanites" = 5, - "neurophage_nanites" = 2) - ) - new_chemicals[reag_ID] = "Nanite" - if(man_rating >= 3) // Anomalous tech. - new_chemicals["immunosuprizine"] = "Immunosuprizine" - if(man_rating >= 2) // Tier 3. - new_chemicals["spaceacillin"] = "Spaceacillin" - if(man_rating >= 1) // Tier 2. - new_chemicals["leporazine"] = "Leporazine" - - if(new_chemicals.len) - available_chemicals += new_chemicals - return - -/obj/machinery/sleeper/attack_hand(var/mob/user) - if(!controls_inside) - return FALSE - - if(user == occupant) - tgui_interact(user) - -/obj/machinery/sleeper/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Sleeper", "Sleeper") - ui.open() - -/obj/machinery/sleeper/tgui_data(mob/user) - var/data[0] - data["amounts"] = amounts - data["hasOccupant"] = occupant ? 1 : 0 - var/occupantData[0] - // var/crisis = 0 - if(occupant) - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - occupantData["health"] = occupant.health - occupantData["maxHealth"] = occupant.maxHealth - occupantData["minHealth"] = config.health_threshold_dead - occupantData["bruteLoss"] = occupant.getBruteLoss() - occupantData["oxyLoss"] = occupant.getOxyLoss() - occupantData["toxLoss"] = occupant.getToxLoss() - occupantData["fireLoss"] = occupant.getFireLoss() - occupantData["paralysis"] = occupant.paralysis - occupantData["hasBlood"] = 0 - occupantData["bodyTemperature"] = occupant.bodytemperature - occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations - // Because we can put simple_animals in here, we need to do something tricky to get things working nice - occupantData["temperatureSuitability"] = 0 // 0 is the baseline - if(ishuman(occupant) && occupant.species) - // I wanna do something where the bar gets bluer as the temperature gets lower - // For now, I'll just use the standard format for the temperature status - var/datum/species/sp = occupant.species - if(occupant.bodytemperature < sp.cold_level_3) - occupantData["temperatureSuitability"] = -3 - else if(occupant.bodytemperature < sp.cold_level_2) - occupantData["temperatureSuitability"] = -2 - else if(occupant.bodytemperature < sp.cold_level_1) - occupantData["temperatureSuitability"] = -1 - else if(occupant.bodytemperature > sp.heat_level_3) - occupantData["temperatureSuitability"] = 3 - else if(occupant.bodytemperature > sp.heat_level_2) - occupantData["temperatureSuitability"] = 2 - else if(occupant.bodytemperature > sp.heat_level_1) - occupantData["temperatureSuitability"] = 1 - else if(isanimal(occupant)) - var/mob/living/simple_mob/silly = occupant - if(silly.bodytemperature < silly.minbodytemp) - occupantData["temperatureSuitability"] = -3 - else if(silly.bodytemperature > silly.maxbodytemp) - occupantData["temperatureSuitability"] = 3 - // Blast you, imperial measurement system - occupantData["btCelsius"] = occupant.bodytemperature - T0C - occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 - - - // crisis = (occupant.health < min_health) - // I'm not sure WHY you'd want to put a simple_animal in a sleeper, but precedent is precedent - // Runtime is aptly named, isn't she? - if(ishuman(occupant) && !(NO_BLOOD in occupant.species.flags) && occupant.vessel) - occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) - occupantData["hasBlood"] = 1 - var/blood_volume = round(occupant.vessel.get_reagent_amount("blood")) - occupantData["bloodLevel"] = blood_volume - occupantData["bloodMax"] = occupant.species.blood_volume - occupantData["bloodPercent"] = round(100*(blood_volume/occupant.species.blood_volume), 0.01) //copy pasta ends here - - occupantData["bloodType"] = occupant.dna.b_type - - data["occupant"] = occupantData - data["maxchem"] = max_chem - data["minhealth"] = min_health - data["dialysis"] = filtering - data["stomachpumping"] = pumping - data["auto_eject_dead"] = auto_eject_dead - if(beaker) - data["isBeakerLoaded"] = 1 - if(beaker.reagents) - data["beakerMaxSpace"] = beaker.reagents.maximum_volume - data["beakerFreeSpace"] = beaker.reagents.get_free_space() - else - data["beakerMaxSpace"] = 0 - data["beakerFreeSpace"] = 0 - else - data["isBeakerLoaded"] = FALSE - - - var/stasis_level_name = "Error!" - for(var/N in stasis_choices) - if(stasis_choices[N] == stasis_level) - stasis_level_name = N - break - data["stasis"] = stasis_level_name - - var/chemicals[0] - for(var/re in available_chemicals) - var/datum/reagent/temp = SSchemistry.chemical_reagents[re] - if(temp) - var/reagent_amount = 0 - var/pretty_amount - var/injectable = occupant ? 1 : 0 - var/overdosing = 0 - var/caution = 0 // To make things clear that you're coming close to an overdose - // if(crisis && !(temp.id in emergency_chems)) - // injectable = 0 - - if(occupant && occupant.reagents) - reagent_amount = occupant.reagents.get_reagent_amount(temp.id) - // If they're mashing the highest concentration, they get one warning - if(temp.overdose && reagent_amount + 10 > (temp.overdose * occupant?.species.chemOD_threshold)) - caution = 1 - if(temp.overdose && reagent_amount > (temp.overdose * occupant?.species.chemOD_threshold)) - overdosing = 1 - - pretty_amount = round(reagent_amount, 0.05) - - chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution))) - data["chemicals"] = chemicals - return data - - -/obj/machinery/sleeper/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - if(!controls_inside && usr == occupant) - return - if(panel_open) - to_chat(usr, "Close the maintenance panel first.") - return - - . = TRUE - switch(action) - if("chemical") - if(!occupant) - return - if(occupant.stat == DEAD) - var/datum/gender/G = gender_datums[occupant.get_visible_gender()] - to_chat(usr, "This person has no life to preserve anymore. Take [G.him] to a department capable of reanimating [G.him].") - return - var/chemical = params["chemid"] - var/amount = text2num(params["amount"]) - if(!length(chemical) || amount <= 0) - return - if(occupant.health > min_health) //|| (chemical in emergency_chems)) - inject_chemical(usr, chemical, amount) - else - to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") - if("removebeaker") - remove_beaker() - if("togglefilter") - toggle_filter() - if("togglepump") - toggle_pump() - if("ejectify") - go_out() - if("changestasis") - var/new_stasis = tgui_input_list(usr, "Levels deeper than 50% stasis level will render the patient unconscious.","Stasis Level", stasis_choices) - if(new_stasis) - stasis_level = stasis_choices[new_stasis] - if("auto_eject_dead_on") - auto_eject_dead = TRUE - if("auto_eject_dead_off") - auto_eject_dead = FALSE - else - return FALSE - add_fingerprint(usr) - -/obj/machinery/sleeper/process() - if(stat & (NOPOWER|BROKEN)) - return - if(occupant) - if(auto_eject_dead && occupant.stat == DEAD) - playsound(loc, 'sound/machines/buzz-sigh.ogg', 40) - go_out() - return - occupant.Stasis(stasis_level) - - if(filtering > 0) - if(beaker) - if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) - var/pumped = 0 - for(var/datum/reagent/x in occupant.reagents.reagent_list) - occupant.reagents.trans_to_obj(beaker, 3) - pumped++ - if(ishuman(occupant)) - occupant.vessel.trans_to_obj(beaker, pumped + 1) - else - toggle_filter() - - if(pumping > 0) - if(beaker) - if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) - for(var/datum/reagent/x in occupant.ingested.reagent_list) - occupant.ingested.trans_to_obj(beaker, 3) - else - toggle_pump() - -/obj/machinery/sleeper/update_icon() - icon_state = "sleeper_[occupant ? "1" : "0"]" - -/obj/machinery/sleeper/attackby(var/obj/item/I, var/mob/user) - add_fingerprint(user) - if(istype(I, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/G = I - if(G.affecting) - go_in(G.affecting, user) - return - if(istype(I, /obj/item/weapon/reagent_containers/glass)) - if(!beaker) - beaker = I - user.drop_item() - I.loc = src - user.visible_message("\The [user] adds \a [I] to \the [src].", "You add \a [I] to \the [src].") - else - to_chat(user, "\The [src] has a beaker already.") - return - if(!occupant) - if(default_deconstruction_screwdriver(user, I)) - return - if(default_deconstruction_crowbar(user, I)) - return - if(default_part_replacement(user, I)) - return - -/obj/machinery/sleeper/verb/move_eject() - set name = "Eject occupant" - set category = "Object" - set src in oview(1) - if(usr == occupant) - switch(usr.stat) - if(DEAD) - return - if(UNCONSCIOUS) - to_chat(usr, "You struggle through the haze to hit the eject button. This will take a couple of minutes...") - if(do_after(usr, 2 MINUTES, src)) - go_out() - if(CONSCIOUS) - go_out() - else - if(usr.stat != CONSCIOUS) - return - go_out() - add_fingerprint(usr) - -/obj/machinery/sleeper/MouseDrop_T(var/mob/target, var/mob/user) - if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target)) - return - go_in(target, user) - -/obj/machinery/sleeper/relaymove(var/mob/user) - ..() - if(user.incapacitated()) - return - go_out() - -/obj/machinery/sleeper/emp_act(var/severity) - if(filtering) - toggle_filter() - - if(pumping) - toggle_pump() - - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - if(occupant) - go_out() - - ..(severity) -/obj/machinery/sleeper/proc/toggle_filter() - if(!occupant || !beaker) - filtering = 0 - return - filtering = !filtering - -/obj/machinery/sleeper/proc/toggle_pump() - if(!occupant || !beaker) - pumping = 0 - return - pumping = !pumping - -/obj/machinery/sleeper/proc/go_in(var/mob/M, var/mob/user) - if(!M) - return - if(stat & (BROKEN|NOPOWER)) - return - if(occupant) - to_chat(user, "\The [src] is already occupied.") - return - if(!ishuman(M)) - to_chat(user, "\The [src] is not designed for that organism!") - return - if(M == user) - visible_message("\The [user] starts climbing into \the [src].") - else - visible_message("\The [user] starts putting [M] into \the [src].") - - if(do_after(user, 20)) - if(occupant) - to_chat(user, "\The [src] is already occupied.") - return - M.stop_pulling() - if(M.client) - M.client.perspective = EYE_PERSPECTIVE - M.client.eye = src - M.loc = src - update_use_power(USE_POWER_ACTIVE) - occupant = M - update_icon() - -/obj/machinery/sleeper/proc/go_out() - if(!occupant || occupant.loc != src) - occupant = null // JUST IN CASE - return - if(occupant.client) - occupant.client.eye = occupant.client.mob - occupant.client.perspective = MOB_PERSPECTIVE - occupant.Stasis(0) - occupant.loc = src.loc - occupant = null - for(var/atom/movable/A in src) // In case an object was dropped inside or something - if(A == beaker || A == circuit) - continue - if(A in component_parts) - continue - A.loc = src.loc - update_use_power(USE_POWER_IDLE) - update_icon() - toggle_filter() - toggle_pump() - -/obj/machinery/sleeper/proc/remove_beaker() - if(beaker) - beaker.loc = src.loc - beaker = null - toggle_filter() - -/obj/machinery/sleeper/proc/inject_chemical(var/mob/living/user, var/chemical, var/amount) - if(stat & (BROKEN|NOPOWER)) - return - if(!(amount in amounts)) - return - - if(occupant && occupant.reagents) - if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem) - use_power(amount * CHEM_SYNTH_ENERGY) - occupant.reagents.add_reagent(chemical, amount) - to_chat(user, "Occupant now has [occupant.reagents.get_reagent_amount(chemical)] units of [available_chemicals[chemical]] in their bloodstream.") - else - to_chat(user, "The subject has too many chemicals in their bloodstream.") - else - to_chat(user, "There's no suitable occupant in \the [src].") - -//Survival/Stasis sleepers -/obj/machinery/sleeper/survival_pod - desc = "A limited functionality sleeper, all it can do is put patients into stasis. It lacks the medication and configuration of the larger units." - icon_state = "sleeper" - stasis_level = 100 //Just one setting - -/obj/machinery/sleeper/survival_pod/Initialize() - . = ..() - RefreshParts(1) +/obj/machinery/sleep_console + name = "sleeper console" + desc = "A control panel to operate a linked sleeper with." + icon = 'icons/obj/Cryogenic2_vr.dmi' //VOREStation Edit - Better icon. + icon_state = "sleeperconsole" + var/obj/machinery/sleeper/sleeper + anchored = TRUE //About time someone fixed this. + density = TRUE //VOREStation Edit - Big console + unacidable = TRUE + dir = 8 + use_power = USE_POWER_IDLE + idle_power_usage = 40 + interact_offline = 1 + circuit = /obj/item/weapon/circuitboard/sleeper_console + clicksound = 'sound/machines/buttonbeep.ogg' + clickvol = 30 + +/obj/machinery/sleep_console/Initialize() + findsleeper() + return ..() + +/obj/machinery/sleep_console/Destroy() + if(sleeper) + sleeper.console = null + return ..() + +/obj/machinery/sleep_console/proc/findsleeper() + var/obj/machinery/sleeper/sleepernew = null + for(var/direction in GLOB.cardinal) // Loop through every direction + sleepernew = locate(/obj/machinery/sleeper, get_step(src, direction)) // Try to find a scanner in that direction + if(sleepernew) + sleeper = sleepernew + sleepernew.console = src + break //VOREStation Edit + + +/obj/machinery/sleep_console/attack_ai(var/mob/user) + return attack_hand(user) + +/obj/machinery/sleep_console/attack_hand(var/mob/user) + if(..()) + return 1 + + if(!sleeper) + findsleeper() + if(!sleeper) + to_chat(user, "Sleeper not found!") + return + + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + + if(sleeper) + return tgui_interact(user) + +/obj/machinery/sleep_console/attackby(var/obj/item/I, var/mob/user) + if(computer_deconstruction_screwdriver(user, I)) + return + else + return attack_hand(user) + +/obj/machinery/sleep_console/power_change() + ..() + if(stat & (NOPOWER|BROKEN)) + icon_state = "sleeperconsole-p" + else + icon_state = initial(icon_state) + +/obj/machinery/sleep_console/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Sleeper", "Sleeper") + ui.open() + +/obj/machinery/sleep_console/tgui_data(mob/user) + if(sleeper) + return sleeper.tgui_data(user) + return null + +/obj/machinery/sleep_console/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) + if(sleeper) + return sleeper.tgui_act(action, params, ui, state) + return ..() + +/obj/machinery/sleeper + name = "sleeper" + desc = "A stasis pod with built-in injectors, a dialysis machine, and a limited health scanner." + icon = 'icons/obj/Cryogenic2_vr.dmi' //VOREStation Edit - Better icons + icon_state = "sleeper_0" + density = TRUE + anchored = TRUE + unacidable = TRUE + circuit = /obj/item/weapon/circuitboard/sleeper + var/mob/living/carbon/human/occupant = null + var/list/available_chemicals = list() + var/list/base_chemicals = list("inaprovaline" = "Inaprovaline", "paracetamol" = "Paracetamol", "anti_toxin" = "Dylovene", "dexalin" = "Dexalin") + var/amounts = list(5, 10) + var/obj/item/weapon/reagent_containers/glass/beaker = null + var/filtering = 0 + var/pumping = 0 + // Currently never changes. On Paradise, max_chem and min_health are based on the matter bins in the sleeper. + var/max_chem = 20 + var/initial_bin_rating = 1 + var/min_health = -101 + var/obj/machinery/sleep_console/console + var/stasis_level = 0 //Every 'this' life ticks are applied to the mob (when life_ticks%stasis_level == 1) + var/stasis_choices = list("Complete (1%)" = 100, "Deep (10%)" = 10, "Moderate (20%)" = 5, "Light (50%)" = 2, "None (100%)" = 0) + var/controls_inside = FALSE + var/auto_eject_dead = FALSE + + use_power = USE_POWER_IDLE + idle_power_usage = 15 + active_power_usage = 200 //builtin health analyzer, dialysis machine, injectors. + +/obj/machinery/sleeper/Initialize() + . = ..() + beaker = new /obj/item/weapon/reagent_containers/glass/beaker/large(src) + default_apply_parts() + update_icon() + +/obj/machinery/sleeper/Destroy() + if(console) + console.sleeper = null + return ..() + +/obj/machinery/sleeper/RefreshParts(var/limited = 0) + var/man_rating = 0 + var/cap_rating = 0 + + available_chemicals.Cut() + available_chemicals = base_chemicals.Copy() + + for(var/obj/item/weapon/stock_parts/P in component_parts) + if(istype(P, /obj/item/weapon/stock_parts/capacitor)) + cap_rating += P.rating + + cap_rating = max(1, round(cap_rating / 2)) + + update_idle_power_usage(initial(idle_power_usage) / cap_rating) + update_active_power_usage(initial(active_power_usage) / cap_rating) + + if(!limited) + for(var/obj/item/weapon/stock_parts/P in component_parts) + if(istype(P, /obj/item/weapon/stock_parts/manipulator)) + man_rating += P.rating - 1 + + var/list/new_chemicals = list() + + if(man_rating >= 4) // Alien tech. + var/reag_ID = pickweight(list( + "healing_nanites" = 10, + "shredding_nanites" = 5, + "irradiated_nanites" = 5, + "neurophage_nanites" = 2) + ) + new_chemicals[reag_ID] = "Nanite" + if(man_rating >= 3) // Anomalous tech. + new_chemicals["immunosuprizine"] = "Immunosuprizine" + if(man_rating >= 2) // Tier 3. + new_chemicals["spaceacillin"] = "Spaceacillin" + if(man_rating >= 1) // Tier 2. + new_chemicals["leporazine"] = "Leporazine" + + if(new_chemicals.len) + available_chemicals += new_chemicals + return + +/obj/machinery/sleeper/attack_hand(var/mob/user) + if(!controls_inside) + return FALSE + + if(user == occupant) + tgui_interact(user) + +/obj/machinery/sleeper/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Sleeper", "Sleeper") + ui.open() + +/obj/machinery/sleeper/tgui_data(mob/user) + var/data[0] + data["amounts"] = amounts + data["hasOccupant"] = occupant ? 1 : 0 + var/occupantData[0] + // var/crisis = 0 + if(occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.maxHealth + occupantData["minHealth"] = config.health_threshold_dead + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["paralysis"] = occupant.paralysis + occupantData["hasBlood"] = 0 + occupantData["bodyTemperature"] = occupant.bodytemperature + occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations + // Because we can put simple_animals in here, we need to do something tricky to get things working nice + occupantData["temperatureSuitability"] = 0 // 0 is the baseline + if(ishuman(occupant) && occupant.species) + // I wanna do something where the bar gets bluer as the temperature gets lower + // For now, I'll just use the standard format for the temperature status + var/datum/species/sp = occupant.species + if(occupant.bodytemperature < sp.cold_level_3) + occupantData["temperatureSuitability"] = -3 + else if(occupant.bodytemperature < sp.cold_level_2) + occupantData["temperatureSuitability"] = -2 + else if(occupant.bodytemperature < sp.cold_level_1) + occupantData["temperatureSuitability"] = -1 + else if(occupant.bodytemperature > sp.heat_level_3) + occupantData["temperatureSuitability"] = 3 + else if(occupant.bodytemperature > sp.heat_level_2) + occupantData["temperatureSuitability"] = 2 + else if(occupant.bodytemperature > sp.heat_level_1) + occupantData["temperatureSuitability"] = 1 + else if(isanimal(occupant)) + var/mob/living/simple_mob/silly = occupant + if(silly.bodytemperature < silly.minbodytemp) + occupantData["temperatureSuitability"] = -3 + else if(silly.bodytemperature > silly.maxbodytemp) + occupantData["temperatureSuitability"] = 3 + // Blast you, imperial measurement system + occupantData["btCelsius"] = occupant.bodytemperature - T0C + occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 + + + // crisis = (occupant.health < min_health) + // I'm not sure WHY you'd want to put a simple_animal in a sleeper, but precedent is precedent + // Runtime is aptly named, isn't she? + if(ishuman(occupant) && !(NO_BLOOD in occupant.species.flags) && occupant.vessel) + occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) + occupantData["hasBlood"] = 1 + var/blood_volume = round(occupant.vessel.get_reagent_amount("blood")) + occupantData["bloodLevel"] = blood_volume + occupantData["bloodMax"] = occupant.species.blood_volume + occupantData["bloodPercent"] = round(100*(blood_volume/occupant.species.blood_volume), 0.01) //copy pasta ends here + + occupantData["bloodType"] = occupant.dna.b_type + + data["occupant"] = occupantData + data["maxchem"] = max_chem + data["minhealth"] = min_health + data["dialysis"] = filtering + data["stomachpumping"] = pumping + data["auto_eject_dead"] = auto_eject_dead + if(beaker) + data["isBeakerLoaded"] = 1 + if(beaker.reagents) + data["beakerMaxSpace"] = beaker.reagents.maximum_volume + data["beakerFreeSpace"] = beaker.reagents.get_free_space() + else + data["beakerMaxSpace"] = 0 + data["beakerFreeSpace"] = 0 + else + data["isBeakerLoaded"] = FALSE + + + var/stasis_level_name = "Error!" + for(var/N in stasis_choices) + if(stasis_choices[N] == stasis_level) + stasis_level_name = N + break + data["stasis"] = stasis_level_name + + var/chemicals[0] + for(var/re in available_chemicals) + var/datum/reagent/temp = SSchemistry.chemical_reagents[re] + if(temp) + var/reagent_amount = 0 + var/pretty_amount + var/injectable = occupant ? 1 : 0 + var/overdosing = 0 + var/caution = 0 // To make things clear that you're coming close to an overdose + // if(crisis && !(temp.id in emergency_chems)) + // injectable = 0 + + if(occupant && occupant.reagents) + reagent_amount = occupant.reagents.get_reagent_amount(temp.id) + // If they're mashing the highest concentration, they get one warning + if(temp.overdose && reagent_amount + 10 > (temp.overdose * occupant?.species.chemOD_threshold)) + caution = 1 + if(temp.overdose && reagent_amount > (temp.overdose * occupant?.species.chemOD_threshold)) + overdosing = 1 + + pretty_amount = round(reagent_amount, 0.05) + + chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution))) + data["chemicals"] = chemicals + return data + + +/obj/machinery/sleeper/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + if(!controls_inside && usr == occupant) + return + if(panel_open) + to_chat(usr, "Close the maintenance panel first.") + return + + . = TRUE + switch(action) + if("chemical") + if(!occupant) + return + if(occupant.stat == DEAD) + var/datum/gender/G = gender_datums[occupant.get_visible_gender()] + to_chat(usr, "This person has no life to preserve anymore. Take [G.him] to a department capable of reanimating [G.him].") + return + var/chemical = params["chemid"] + var/amount = text2num(params["amount"]) + if(!length(chemical) || amount <= 0) + return + if(occupant.health > min_health) //|| (chemical in emergency_chems)) + inject_chemical(usr, chemical, amount) + else + to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") + if("removebeaker") + remove_beaker() + if("togglefilter") + toggle_filter() + if("togglepump") + toggle_pump() + if("ejectify") + go_out() + if("changestasis") + var/new_stasis = tgui_input_list(usr, "Levels deeper than 50% stasis level will render the patient unconscious.","Stasis Level", stasis_choices) + if(new_stasis) + stasis_level = stasis_choices[new_stasis] + if("auto_eject_dead_on") + auto_eject_dead = TRUE + if("auto_eject_dead_off") + auto_eject_dead = FALSE + else + return FALSE + add_fingerprint(usr) + +/obj/machinery/sleeper/process() + if(stat & (NOPOWER|BROKEN)) + return + if(occupant) + if(auto_eject_dead && occupant.stat == DEAD) + playsound(loc, 'sound/machines/buzz-sigh.ogg', 40) + go_out() + return + occupant.Stasis(stasis_level) + + if(filtering > 0) + if(beaker) + if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) + var/pumped = 0 + for(var/datum/reagent/x in occupant.reagents.reagent_list) + occupant.reagents.trans_to_obj(beaker, 3) + pumped++ + if(ishuman(occupant)) + occupant.vessel.trans_to_obj(beaker, pumped + 1) + else + toggle_filter() + + if(pumping > 0) + if(beaker) + if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) + for(var/datum/reagent/x in occupant.ingested.reagent_list) + occupant.ingested.trans_to_obj(beaker, 3) + else + toggle_pump() + +/obj/machinery/sleeper/update_icon() + icon_state = "sleeper_[occupant ? "1" : "0"]" + +/obj/machinery/sleeper/attackby(var/obj/item/I, var/mob/user) + add_fingerprint(user) + if(istype(I, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = I + if(G.affecting) + go_in(G.affecting, user) + return + if(istype(I, /obj/item/weapon/reagent_containers/glass)) + if(!beaker) + beaker = I + user.drop_item() + I.loc = src + user.visible_message("\The [user] adds \a [I] to \the [src].", "You add \a [I] to \the [src].") + else + to_chat(user, "\The [src] has a beaker already.") + return + if(!occupant) + if(default_deconstruction_screwdriver(user, I)) + return + if(default_deconstruction_crowbar(user, I)) + return + if(default_part_replacement(user, I)) + return + +/obj/machinery/sleeper/verb/move_eject() + set name = "Eject occupant" + set category = "Object" + set src in oview(1) + if(usr == occupant) + switch(usr.stat) + if(DEAD) + return + if(UNCONSCIOUS) + to_chat(usr, "You struggle through the haze to hit the eject button. This will take a couple of minutes...") + if(do_after(usr, 2 MINUTES, src)) + go_out() + if(CONSCIOUS) + go_out() + else + if(usr.stat != CONSCIOUS) + return + go_out() + add_fingerprint(usr) + +/obj/machinery/sleeper/MouseDrop_T(var/mob/target, var/mob/user) + if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target)) + return + go_in(target, user) + +/obj/machinery/sleeper/relaymove(var/mob/user) + ..() + if(user.incapacitated()) + return + go_out() + +/obj/machinery/sleeper/emp_act(var/severity) + if(filtering) + toggle_filter() + + if(pumping) + toggle_pump() + + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + if(occupant) + go_out() + + ..(severity) +/obj/machinery/sleeper/proc/toggle_filter() + if(!occupant || !beaker) + filtering = 0 + return + filtering = !filtering + +/obj/machinery/sleeper/proc/toggle_pump() + if(!occupant || !beaker) + pumping = 0 + return + pumping = !pumping + +/obj/machinery/sleeper/proc/go_in(var/mob/M, var/mob/user) + if(!M) + return + if(stat & (BROKEN|NOPOWER)) + return + if(occupant) + to_chat(user, "\The [src] is already occupied.") + return + if(!ishuman(M)) + to_chat(user, "\The [src] is not designed for that organism!") + return + if(M == user) + visible_message("\The [user] starts climbing into \the [src].") + else + visible_message("\The [user] starts putting [M] into \the [src].") + + if(do_after(user, 20)) + if(occupant) + to_chat(user, "\The [src] is already occupied.") + return + M.stop_pulling() + if(M.client) + M.client.perspective = EYE_PERSPECTIVE + M.client.eye = src + M.loc = src + update_use_power(USE_POWER_ACTIVE) + occupant = M + update_icon() + +/obj/machinery/sleeper/proc/go_out() + if(!occupant || occupant.loc != src) + occupant = null // JUST IN CASE + return + if(occupant.client) + occupant.client.eye = occupant.client.mob + occupant.client.perspective = MOB_PERSPECTIVE + occupant.Stasis(0) + occupant.loc = src.loc + occupant = null + for(var/atom/movable/A in src) // In case an object was dropped inside or something + if(A == beaker || A == circuit) + continue + if(A in component_parts) + continue + A.loc = src.loc + update_use_power(USE_POWER_IDLE) + update_icon() + toggle_filter() + toggle_pump() + +/obj/machinery/sleeper/proc/remove_beaker() + if(beaker) + beaker.loc = src.loc + beaker = null + toggle_filter() + +/obj/machinery/sleeper/proc/inject_chemical(var/mob/living/user, var/chemical, var/amount) + if(stat & (BROKEN|NOPOWER)) + return + if(!(amount in amounts)) + return + + if(occupant && occupant.reagents) + if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem) + use_power(amount * CHEM_SYNTH_ENERGY) + occupant.reagents.add_reagent(chemical, amount) + to_chat(user, "Occupant now has [occupant.reagents.get_reagent_amount(chemical)] units of [available_chemicals[chemical]] in their bloodstream.") + else + to_chat(user, "The subject has too many chemicals in their bloodstream.") + else + to_chat(user, "There's no suitable occupant in \the [src].") + +//Survival/Stasis sleepers +/obj/machinery/sleeper/survival_pod + desc = "A limited functionality sleeper, all it can do is put patients into stasis. It lacks the medication and configuration of the larger units." + icon_state = "sleeper" + stasis_level = 100 //Just one setting + +/obj/machinery/sleeper/survival_pod/Initialize() + . = ..() + RefreshParts(1) diff --git a/code/game/machinery/air_alarm.dm b/code/game/machinery/air_alarm.dm index 8bed01b6abe..c8da98c9dfb 100644 --- a/code/game/machinery/air_alarm.dm +++ b/code/game/machinery/air_alarm.dm @@ -1,848 +1,848 @@ -#define DECLARE_TLV_VALUES var/red_min; var/yel_min; var/yel_max; var/red_max; var/tlv_comparitor; -#define LOAD_TLV_VALUES(x, y) red_min = x[1]; yel_min = x[2]; yel_max = x[3]; red_max = x[4]; tlv_comparitor = y; -#define TEST_TLV_VALUES (((tlv_comparitor >= red_max && red_max > 0) || tlv_comparitor <= red_min) ? 2 : ((tlv_comparitor >= yel_max && yel_max > 0) || tlv_comparitor <= yel_min) ? 1 : 0) - -#define AALARM_MODE_SCRUBBING 1 -#define AALARM_MODE_REPLACEMENT 2 //like scrubbing, but faster. -#define AALARM_MODE_PANIC 3 //constantly sucks all air -#define AALARM_MODE_CYCLE 4 //sucks off all air, then refill and switches to scrubbing -#define AALARM_MODE_FILL 5 //emergency fill -#define AALARM_MODE_OFF 6 //Shuts it all down. - -#define AALARM_SCREEN_MAIN 1 -#define AALARM_SCREEN_VENT 2 -#define AALARM_SCREEN_SCRUB 3 -#define AALARM_SCREEN_MODE 4 -#define AALARM_SCREEN_SENSORS 5 - -#define AALARM_REPORT_TIMEOUT 100 - -#define MAX_TEMPERATURE 90 -#define MIN_TEMPERATURE -40 - -//all air alarms in area are connected via magic -/area - var/obj/machinery/alarm/master_air_alarm - var/list/air_vent_names = list() - var/list/air_scrub_names = list() - var/list/air_vent_info = list() - var/list/air_scrub_info = list() - -/obj/machinery/alarm - name = "alarm" - desc = "Used to control various station atmospheric systems. The light indicates the current air status of the area." - icon = 'icons/obj/monitors_vr.dmi' //VOREStation Edit - Other icons - icon_state = "alarm_0" - layer = ABOVE_WINDOW_LAYER - vis_flags = VIS_HIDE // They have an emissive that looks bad in openspace due to their wall-mounted nature - anchored = TRUE - unacidable = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 80 - active_power_usage = 1000 //For heating/cooling rooms. 1000 joules equates to about 1 degree every 2 seconds for a single tile of air. - power_channel = ENVIRON - req_one_access = list(access_atmospherics, access_engine_equip) - clicksound = "button" - clickvol = 30 - blocks_emissive = NONE - light_power = 0.25 - var/alarm_id = null - var/breach_detection = 1 // Whether to use automatic breach detection or not - var/frequency = 1439 - //var/skipprocess = 0 //Experimenting - var/alarm_frequency = 1437 - var/remote_control = 0 - var/rcon_setting = 2 - var/rcon_time = 0 - var/locked = 1 - panel_open = FALSE // If it's been screwdrivered open. - var/aidisabled = 0 - var/shorted = 0 - circuit = /obj/item/weapon/circuitboard/airalarm - - var/datum/wires/alarm/wires - - var/mode = AALARM_MODE_SCRUBBING - var/screen = AALARM_SCREEN_MAIN - var/area_uid - var/area/alarm_area - - var/target_temperature = T0C+20 - var/regulating_temperature = 0 - - var/datum/radio_frequency/radio_connection - - /// Keys are things like temperature and certain gasses. Values are lists, which contain, in order: - /// red warning minimum value, yellow warning minimum value, yellow warning maximum value, red warning maximum value - /// Use code\defines\gases.dm as reference for id/name. Please keep it consistent - var/list/TLV = list() - var/list/trace_gas = list("nitrous_oxide", "volatile_fuel") //list of other gases that this air alarm is able to detect - - var/danger_level = 0 - var/pressure_dangerlevel = 0 - - var/report_danger_level = 1 - - var/alarms_hidden = FALSE //If the alarms from this machine are visible on consoles - -/obj/machinery/alarm/nobreach - breach_detection = 0 - -/obj/machinery/alarm/monitor - report_danger_level = 0 - breach_detection = 0 - -/obj/machinery/alarm/alarms_hidden - alarms_hidden = TRUE - -/obj/machinery/alarm/angled - icon = 'icons/obj/wall_machines_angled.dmi' - -/obj/machinery/alarm/angled/hidden - alarms_hidden = TRUE - -/obj/machinery/alarm/angled/offset_airalarm() - pixel_x = (dir & 3) ? 0 : (dir == 4 ? -21 : 21) - pixel_y = (dir & 3) ? (dir == 1 ? -18 : 20) : 0 - -/obj/machinery/alarm/server/Initialize(mapload) - . = ..() - req_access = list(access_rd, access_atmospherics, access_engine_equip) - TLV["oxygen"] = list(-1.0, -1.0,-1.0,-1.0) // Partial pressure, kpa - TLV["carbon_dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa - TLV["phoron"] = list(-1.0, -1.0, 0, 0.5) // Partial pressure, kpa - TLV["other"] = list(-1.0, -1.0, 0.5, 1.0) // Partial pressure, kpa - TLV["pressure"] = list(0,ONE_ATMOSPHERE*0.10,ONE_ATMOSPHERE*1.40,ONE_ATMOSPHERE*1.60) /* kpa */ - TLV["temperature"] = list(20, 40, 140, 160) // K - target_temperature = 90 - -/obj/machinery/alarm/Initialize(mapload) - . = ..() - if(!pixel_x && !pixel_y) - offset_airalarm() - first_run() - -/obj/machinery/alarm/Destroy() - unregister_radio(src, frequency) - qdel(wires) - wires = null - if(alarm_area && alarm_area.master_air_alarm == src) - alarm_area.master_air_alarm = null - elect_master(exclude_self = TRUE) - return ..() - -/obj/machinery/alarm/proc/offset_airalarm() - pixel_x = (dir & 3) ? 0 : (dir == 4 ? -26 : 26) - pixel_y = (dir & 3) ? (dir == 1 ? -26 : 26) : 0 - -/obj/machinery/alarm/proc/first_run() - alarm_area = get_area(src) - area_uid = "\ref[alarm_area]" - if(name == "alarm") - name = "[alarm_area.name] Air Alarm" - - if(!wires) - wires = new(src) - - // breathable air according to human/Life() - TLV["oxygen"] = list(16, 19, 135, 140) // Partial pressure, kpa - TLV["nitrogen"] = list(0, 0, 135, 140) // Partial pressure, kpa - TLV["carbon_dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa - TLV["phoron"] = list(-1.0, -1.0, 0, 0.5) // Partial pressure, kpa - TLV["other"] = list(-1.0, -1.0, 0.5, 1.0) // Partial pressure, kpa - TLV["pressure"] = list(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE * 0.90, ONE_ATMOSPHERE * 1.10, ONE_ATMOSPHERE * 1.20) /* kpa */ - TLV["temperature"] = list(T0C - 26, T0C, T0C + 40, T0C + 66) // K - - update_icon() - -/obj/machinery/alarm/proc/update_area() - alarm_area = get_area(src) - area_uid = "\ref[alarm_area]" - if(name == "alarm") - name = "[alarm_area.name] Air Alarm" - -/obj/machinery/alarm/Initialize() - . = ..() - set_frequency(frequency) - if(!master_is_operating()) - elect_master() - -/obj/machinery/alarm/process() - if((stat & (NOPOWER|BROKEN)) || shorted) - return - - var/turf/simulated/location = src.loc - if(!istype(location)) return//returns if loc is not simulated - - var/datum/gas_mixture/environment = location.return_air() - - //Handle temperature adjustment here. - handle_heating_cooling(environment) - - var/old_level = danger_level - var/old_pressurelevel = pressure_dangerlevel - danger_level = overall_danger_level(environment) - - if(old_level != danger_level) - apply_danger_level(danger_level) - - if(old_pressurelevel != pressure_dangerlevel) - if(breach_detected()) - mode = AALARM_MODE_OFF - apply_mode() - - if(mode == AALARM_MODE_CYCLE && environment.return_pressure() < ONE_ATMOSPHERE * 0.05) - mode = AALARM_MODE_FILL - apply_mode() - - //atmos computer remote controll stuff - switch(rcon_setting) - if(RCON_NO) - remote_control = 0 - if(RCON_AUTO) - if(danger_level == 2) - remote_control = 1 - else - remote_control = 0 - if(RCON_YES) - remote_control = 1 - - return - -/obj/machinery/alarm/proc/handle_heating_cooling(var/datum/gas_mixture/environment) - DECLARE_TLV_VALUES - LOAD_TLV_VALUES(TLV["temperature"], target_temperature) - if(!regulating_temperature) - //check for when we should start adjusting temperature - if(!TEST_TLV_VALUES && abs(environment.temperature - target_temperature) > 2.0 && environment.return_pressure() >= 1) - update_use_power(USE_POWER_ACTIVE) - regulating_temperature = 1 - audible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ - "You hear a click and a faint electronic hum.", runemessage = "* click *") - playsound(src, 'sound/machines/click.ogg', 50, 1) - else - //check for when we should stop adjusting temperature - if(TEST_TLV_VALUES || abs(environment.temperature - target_temperature) <= 0.5 || environment.return_pressure() < 1) - update_use_power(USE_POWER_IDLE) - regulating_temperature = 0 - audible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ - "You hear a click as a faint electronic humming stops.", runemessage = "* click *") - playsound(src, 'sound/machines/click.ogg', 50, 1) - - if(regulating_temperature) - if(target_temperature > T0C + MAX_TEMPERATURE) - target_temperature = T0C + MAX_TEMPERATURE - - if(target_temperature < T0C + MIN_TEMPERATURE) - target_temperature = T0C + MIN_TEMPERATURE - - var/datum/gas_mixture/gas - gas = environment.remove(0.25 * environment.total_moles) - if(gas) - - if(gas.temperature <= target_temperature) //gas heating - var/energy_used = min(gas.get_thermal_energy_change(target_temperature) , active_power_usage) - - gas.add_thermal_energy(energy_used) - //use_power(energy_used, ENVIRON) //handle by update_use_power instead - else //gas cooling - var/heat_transfer = min(abs(gas.get_thermal_energy_change(target_temperature)), active_power_usage) - - //Assume the heat is being pumped into the hull which is fixed at 20 C - //none of this is really proper thermodynamics but whatever - - var/cop = gas.temperature / T20C //coefficient of performance -> power used = heat_transfer/cop - - heat_transfer = min(heat_transfer, cop * active_power_usage) //this ensures that we don't use more than active_power_usage amount of power - - heat_transfer = -gas.add_thermal_energy(-heat_transfer) //get the actual heat transfer - - //use_power(heat_transfer / cop, ENVIRON) //handle by update_use_power instead - - environment.merge(gas) - -/obj/machinery/alarm/proc/overall_danger_level(var/datum/gas_mixture/environment) - var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature/environment.volume - var/environment_pressure = environment.return_pressure() - - var/other_moles = 0 - for(var/g in trace_gas) - other_moles += environment.gas[g] //this is only going to be used in a partial pressure calc, so we don't need to worry about group_multiplier here. - - DECLARE_TLV_VALUES - LOAD_TLV_VALUES(TLV["pressure"], environment_pressure) - pressure_dangerlevel = TEST_TLV_VALUES // not local because it's used in process() - LOAD_TLV_VALUES(TLV["oxygen"], environment.gas["oxygen"]*partial_pressure) - var/oxygen_dangerlevel = TEST_TLV_VALUES - LOAD_TLV_VALUES(TLV["carbon_dioxide"], environment.gas["carbon_dioxide"]*partial_pressure) - var/co2_dangerlevel = TEST_TLV_VALUES - LOAD_TLV_VALUES(TLV["phoron"], environment.gas["phoron"]*partial_pressure) - var/phoron_dangerlevel = TEST_TLV_VALUES - LOAD_TLV_VALUES(TLV["temperature"], environment.temperature) - var/temperature_dangerlevel = TEST_TLV_VALUES - LOAD_TLV_VALUES(TLV["other"], other_moles*partial_pressure) - var/other_dangerlevel = TEST_TLV_VALUES - - return max( - pressure_dangerlevel, - oxygen_dangerlevel, - co2_dangerlevel, - phoron_dangerlevel, - other_dangerlevel, - temperature_dangerlevel - ) - -// Returns whether this air alarm thinks there is a breach, given the sensors that are available to it. -/obj/machinery/alarm/proc/breach_detected() - var/turf/simulated/location = src.loc - - if(!istype(location)) - return 0 - - if(breach_detection == 0) - return 0 - - var/datum/gas_mixture/environment = location.return_air() - var/environment_pressure = environment.return_pressure() - var/pressure_levels = TLV["pressure"] - - if(environment_pressure <= pressure_levels[1]) //low pressures - if(!(mode == AALARM_MODE_PANIC || mode == AALARM_MODE_CYCLE)) - return 1 - - return 0 - -/obj/machinery/alarm/proc/master_is_operating() - return alarm_area && alarm_area.master_air_alarm && !(alarm_area.master_air_alarm.stat & (NOPOWER | BROKEN)) - -/obj/machinery/alarm/proc/elect_master(exclude_self = FALSE) - for(var/obj/machinery/alarm/AA in alarm_area) - if(exclude_self && AA == src) - continue - if(!(AA.stat & (NOPOWER|BROKEN))) - alarm_area.master_air_alarm = AA - return 1 - return 0 - -/obj/machinery/alarm/update_icon() - cut_overlays() - - if(panel_open) - icon_state = "alarmx" - set_light(0) - set_light_on(FALSE) - return - if((stat & (NOPOWER|BROKEN)) || shorted) - icon_state = "alarmp" - set_light(0) - set_light_on(FALSE) - return - - var/icon_level = danger_level - if(alarm_area?.atmosalm) - icon_level = max(icon_level, 1) //if there's an atmos alarm but everything is okay locally, no need to go past yellow - - var/new_color = null - switch(icon_level) - if(0) - icon_state = "alarm_0" - add_overlay(mutable_appearance(icon, "alarm_ov0")) - add_overlay(emissive_appearance(icon, "alarm_ov0")) - new_color = "#03A728" - if(1) - icon_state = "alarm_2" //yes, alarm2 is yellow alarm - add_overlay(mutable_appearance(icon, "alarm_ov2")) - add_overlay(emissive_appearance(icon, "alarm_ov2")) - new_color = "#EC8B2F" - if(2) - icon_state = "alarm_1" - add_overlay(mutable_appearance(icon, "alarm_ov1")) - add_overlay(emissive_appearance(icon, "alarm_ov1")) - new_color = "#DA0205" - - set_light(l_range = 2, l_power = 0.25, l_color = new_color) - set_light_on(TRUE) - -/obj/machinery/alarm/receive_signal(datum/signal/signal) - if(stat & (NOPOWER|BROKEN)) - return - if(alarm_area.master_air_alarm != src) - if(master_is_operating()) - return - elect_master() - if(alarm_area.master_air_alarm != src) - return - if(!signal || signal.encryption) - return - var/id_tag = signal.data["tag"] - if(!id_tag) - return - if(signal.data["area"] != area_uid) - return - if(signal.data["sigtype"] != "status") - return - - var/dev_type = signal.data["device"] - if(!(id_tag in alarm_area.air_scrub_names) && !(id_tag in alarm_area.air_vent_names)) - register_env_machine(id_tag, dev_type) - if(dev_type == "AScr") - alarm_area.air_scrub_info[id_tag] = signal.data - else if(dev_type == "AVP") - alarm_area.air_vent_info[id_tag] = signal.data - -/obj/machinery/alarm/proc/register_env_machine(var/m_id, var/device_type) - var/new_name - if(device_type == "AVP") - new_name = "[alarm_area.name] Vent Pump #[alarm_area.air_vent_names.len+1]" - alarm_area.air_vent_names[m_id] = new_name - else if(device_type == "AScr") - new_name = "[alarm_area.name] Air Scrubber #[alarm_area.air_scrub_names.len+1]" - alarm_area.air_scrub_names[m_id] = new_name - else - return - spawn(10) - send_signal(m_id, list("init" = new_name)) - -/obj/machinery/alarm/proc/refresh_all() - for(var/id_tag in alarm_area.air_vent_names) - var/list/I = alarm_area.air_vent_info[id_tag] - if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time) - continue - send_signal(id_tag, list("status")) - for(var/id_tag in alarm_area.air_scrub_names) - var/list/I = alarm_area.air_scrub_info[id_tag] - if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time) - continue - send_signal(id_tag, list("status")) - -/obj/machinery/alarm/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, RADIO_TO_AIRALARM) - -/obj/machinery/alarm/proc/send_signal(var/target, var/list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - - signal.data = command - signal.data["tag"] = target - signal.data["sigtype"] = "command" - - radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) -// to_world("Signal [command] Broadcasted to [target]") - - return 1 - -/obj/machinery/alarm/proc/apply_mode() - //propagate mode to other air alarms in the area - //TODO: make it so that players can choose between applying the new mode to the room they are in (related area) vs the entire alarm area - for(var/obj/machinery/alarm/AA in alarm_area) - AA.mode = mode - - switch(mode) - if(AALARM_MODE_SCRUBBING) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list("power"= 1, "co2_scrub"= 1, "scrubbing"= 1, "panic_siphon"= 0)) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list("power"= 1, "checks"= "default", "set_external_pressure"= "default")) - - if(AALARM_MODE_PANIC, AALARM_MODE_CYCLE) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list("power"= 1, "panic_siphon"= 1)) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list("power"= 0)) - - if(AALARM_MODE_REPLACEMENT) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list("power"= 1, "panic_siphon"= 1)) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list("power"= 1, "checks"= "default", "set_external_pressure"= "default")) - - if(AALARM_MODE_FILL) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list("power"= 0)) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list("power"= 1, "checks"= "default", "set_external_pressure"= "default")) - - if(AALARM_MODE_OFF) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list("power"= 0)) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list("power"= 0)) - -/obj/machinery/alarm/proc/apply_danger_level(var/new_danger_level) - if(report_danger_level && alarm_area.atmosalert(new_danger_level, src)) - post_alert(new_danger_level) - - update_icon() - -/obj/machinery/alarm/proc/post_alert(alert_level) - var/datum/radio_frequency/frequency = radio_controller.return_frequency(alarm_frequency) - if(!frequency) - return - - var/datum/signal/alert_signal = new - alert_signal.source = src - alert_signal.transmission_method = TRANSMISSION_RADIO - alert_signal.data["zone"] = alarm_area.name - alert_signal.data["type"] = "Atmospheric" - - if(alert_level==2) - alert_signal.data["alert"] = "severe" - else if(alert_level==1) - alert_signal.data["alert"] = "minor" - else if(alert_level==0) - alert_signal.data["alert"] = "clear" - - frequency.post_signal(src, alert_signal) - -/obj/machinery/alarm/attack_ai(mob/user) - tgui_interact(user) - -/obj/machinery/alarm/attack_hand(mob/user) - . = ..() - if(.) - return - return interact(user) - -/obj/machinery/alarm/interact(mob/user) - tgui_interact(user) - wires.Interact(user) - -/obj/machinery/alarm/tgui_status(mob/user) - if(isAI(user) && aidisabled) - to_chat(user, "AI control has been disabled.") - else if(!shorted) - return ..() - return STATUS_CLOSE - -/obj/machinery/alarm/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, datum/tgui_state/state) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AirAlarm", name, parent_ui) - if(state) - ui.set_state(state) - ui.open() - -/obj/machinery/alarm/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) - var/list/data = list( - "locked" = locked, - "siliconUser" = issilicon(user), - "remoteUser" = !!ui.parent_ui, - "danger_level" = danger_level, - "target_temperature" = "[target_temperature - T0C]C", - "rcon" = rcon_setting, - ) - - var/area/A = get_area(src) - data["atmos_alarm"] = A?.atmosalm - data["fire_alarm"] = A?.fire - - var/turf/T = get_turf(src) - var/datum/gas_mixture/environment = T.return_air() - - var/list/list/environment_data = list() - data["environment_data"] = environment_data - - DECLARE_TLV_VALUES - - var/pressure = environment.return_pressure() - LOAD_TLV_VALUES(TLV["pressure"], pressure) - environment_data.Add(list(list( - "name" = "Pressure", - "value" = pressure, - "unit" = "kPa", - "danger_level" = TEST_TLV_VALUES - ))) - - var/temperature = environment.temperature - LOAD_TLV_VALUES(TLV["temperature"], temperature) - environment_data.Add(list(list( - "name" = "Temperature", - "value" = temperature, - "unit" = "K ([round(temperature - T0C, 0.1)]C)", - "danger_level" = TEST_TLV_VALUES - ))) - - var/total_moles = environment.total_moles - var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature / environment.volume - for(var/gas_id in environment.gas) - if(!(gas_id in TLV)) - continue - LOAD_TLV_VALUES(TLV[gas_id], environment.gas[gas_id] * partial_pressure) - environment_data.Add(list(list( - "name" = gas_id, - "value" = environment.gas[gas_id] / total_moles * 100, - "unit" = "%", - "danger_level" = TEST_TLV_VALUES - ))) - - if(!locked || issilicon(user) || data["remoteUser"]) - var/list/list/vents = list() - data["vents"] = vents - for(var/id_tag in A.air_vent_names) - var/long_name = A.air_vent_names[id_tag] - var/list/info = A.air_vent_info[id_tag] - if(!info) - continue - vents.Add(list(list( - "id_tag" = id_tag, - "long_name" = sanitize(long_name), - "power" = info["power"], - "checks" = info["checks"], - "excheck" = info["checks"]&1, - "incheck" = info["checks"]&2, - "direction" = info["direction"], - "external" = info["external"], - "internal" = info["internal"], - "extdefault"= (info["external"] == ONE_ATMOSPHERE), - "intdefault"= (info["internal"] == 0), - ))) - - - var/list/list/scrubbers = list() - data["scrubbers"] = scrubbers - for(var/id_tag in alarm_area.air_scrub_names) - var/long_name = alarm_area.air_scrub_names[id_tag] - var/list/info = alarm_area.air_scrub_info[id_tag] - if(!info) - continue - scrubbers += list(list( - "id_tag" = id_tag, - "long_name" = sanitize(long_name), - "power" = info["power"], - "scrubbing" = info["scrubbing"], - "panic" = info["panic"], - "filters" = list( - list("name" = "Oxygen", "command" = "o2_scrub", "val" = info["filter_o2"]), - list("name" = "Nitrogen", "command" = "n2_scrub", "val" = info["filter_n2"]), - list("name" = "Carbon Dioxide", "command" = "co2_scrub","val" = info["filter_co2"]), - list("name" = "Phoron" , "command" = "tox_scrub","val" = info["filter_phoron"]), - list("name" = "Nitrous Oxide", "command" = "n2o_scrub","val" = info["filter_n2o"]), - list("name" = "Volatile Fuel", "command" = "fuel_scrub","val" = info["filter_fuel"]) - ) - )) - data["scrubbers"] = scrubbers - - data["mode"] = mode - - var/list/list/modes = list() - data["modes"] = modes - modes[++modes.len] = list("name" = "Filtering - Scrubs out contaminants", "mode" = AALARM_MODE_SCRUBBING, "selected" = mode == AALARM_MODE_SCRUBBING, "danger" = 0) - modes[++modes.len] = list("name" = "Replace Air - Siphons out air while replacing", "mode" = AALARM_MODE_REPLACEMENT, "selected" = mode == AALARM_MODE_REPLACEMENT, "danger" = 0) - modes[++modes.len] = list("name" = "Panic - Siphons air out of the room", "mode" = AALARM_MODE_PANIC, "selected" = mode == AALARM_MODE_PANIC, "danger" = 1) - modes[++modes.len] = list("name" = "Cycle - Siphons air before replacing", "mode" = AALARM_MODE_CYCLE, "selected" = mode == AALARM_MODE_CYCLE, "danger" = 1) - modes[++modes.len] = list("name" = "Fill - Shuts off scrubbers and opens vents", "mode" = AALARM_MODE_FILL, "selected" = mode == AALARM_MODE_FILL, "danger" = 0) - modes[++modes.len] = list("name" = "Off - Shuts off vents and scrubbers", "mode" = AALARM_MODE_OFF, "selected" = mode == AALARM_MODE_OFF, "danger" = 0) - - var/list/selected - var/list/thresholds = list() - - var/list/gas_names = list("oxygen", "carbon_dioxide", "phoron", "other") //Gas ids made to match code\defines\gases.dm - for(var/g in gas_names) - thresholds[++thresholds.len] = list("name" = g, "settings" = list()) - selected = TLV[g] - for(var/i = 1, i <= 4, i++) - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = i, "selected" = selected[i])) - - selected = TLV["pressure"] - thresholds[++thresholds.len] = list("name" = "Pressure", "settings" = list()) - for(var/i = 1, i <= 4, i++) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = i, "selected" = selected[i])) - - selected = TLV["temperature"] - thresholds[++thresholds.len] = list("name" = "Temperature", "settings" = list()) - for(var/i = 1, i <= 4, i++) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = i, "selected" = selected[i])) - - data["thresholds"] = thresholds - return data - -/obj/machinery/alarm/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - if(action == "rcon") - var/attempted_rcon_setting = text2num(params["rcon"]) - - switch(attempted_rcon_setting) - if(RCON_NO) - rcon_setting = RCON_NO - if(RCON_AUTO) - rcon_setting = RCON_AUTO - if(RCON_YES) - rcon_setting = RCON_YES - return TRUE - - if(action == "temperature") - var/list/selected = TLV["temperature"] - var/max_temperature = min(selected[3] - T0C, MAX_TEMPERATURE) - var/min_temperature = max(selected[2] - T0C, MIN_TEMPERATURE) - var/input_temperature = tgui_input_number(usr, "What temperature would you like the system to mantain? (Capped between [min_temperature] and [max_temperature]C)", "Thermostat Controls", target_temperature - T0C, max_temperature, min_temperature, round_value = FALSE) - if(isnum(input_temperature)) - if(input_temperature > max_temperature || input_temperature < min_temperature) - to_chat(usr, "Temperature must be between [min_temperature]C and [max_temperature]C") - else - target_temperature = input_temperature + T0C - return TRUE - - // Account for remote users here. - // Yes, this is kinda snowflaky; however, I would argue it would be far more snowflakey - // to include "custom hrefs" and all the other bullshit that nano states have just for the - // like, two UIs, that want remote access to other UIs. - if((locked && !issilicon(usr) && !istype(state, /datum/tgui_state/air_alarm_remote)) || (issilicon(usr) && aidisabled)) - return - - var/device_id = params["id_tag"] - switch(action) - if("lock") - if(issilicon(usr) && !wires.is_cut(WIRE_IDSCAN)) - locked = !locked - . = TRUE - if( "power", - "o2_scrub", - "n2_scrub", - "co2_scrub", - "tox_scrub", - "n2o_scrub", - "fuel_scrub", - "panic_siphon", - "scrubbing", - "direction") - send_signal(device_id, list("[action]" = text2num(params["val"])), usr) - . = TRUE - if("excheck") - send_signal(device_id, list("checks" = text2num(params["val"])^1), usr) - . = TRUE - if("incheck") - send_signal(device_id, list("checks" = text2num(params["val"])^2), usr) - . = TRUE - if("set_external_pressure", "set_internal_pressure") - var/target = params["value"] - if(!isnull(target)) - send_signal(device_id, list("[action]" = target), usr) - . = TRUE - if("reset_external_pressure") - send_signal(device_id, list("reset_external_pressure"), usr) - . = TRUE - if("reset_internal_pressure") - send_signal(device_id, list("reset_internal_pressure"), usr) - . = TRUE - if("threshold") - var/env = params["env"] - - var/name = params["var"] - var/value = tgui_input_number(usr, "New [name] for [env]:", name, TLV[env][name], round_value = FALSE) - if(!isnull(value) && !..()) - if(value < 0) - TLV[env][name] = -1 - else - TLV[env][name] = round(value, 0.01) - clamp_tlv_values(env, name) - // investigate_log(" treshold value for [env]:[name] was set to [value] by [key_name(usr)]",INVESTIGATE_ATMOS) - . = TRUE - if("mode") - mode = text2num(params["mode"]) - // investigate_log("was turned to [get_mode_name(mode)] mode by [key_name(usr)]",INVESTIGATE_ATMOS) - apply_mode(usr) - . = TRUE - if("alarm") - if(alarm_area.atmosalert(2, src)) - apply_danger_level(2) - . = TRUE - if("reset") - atmos_reset() - . = TRUE - update_icon() - -// This big ol' mess just ensures that TLV always makes sense. If you set the max value below the min value, -// it'll automatically update all the other values to keep it sane. -/obj/machinery/alarm/proc/clamp_tlv_values(env, changed_threshold) - var/list/selected = TLV[env] - switch(changed_threshold) - if(1) - if(selected[1] > selected[2]) - selected[2] = selected[1] - if(selected[1] > selected[3]) - selected[3] = selected[1] - if(selected[1] > selected[4]) - selected[4] = selected[1] - if(2) - if(selected[1] > selected[2]) - selected[1] = selected[2] - if(selected[2] > selected[3]) - selected[3] = selected[2] - if(selected[2] > selected[4]) - selected[4] = selected[2] - if(3) - if(selected[1] > selected[3]) - selected[1] = selected[3] - if(selected[2] > selected[3]) - selected[2] = selected[3] - if(selected[3] > selected[4]) - selected[4] = selected[3] - if(4) - if(selected[1] > selected[4]) - selected[1] = selected[4] - if(selected[2] > selected[4]) - selected[2] = selected[4] - if(selected[3] > selected[4]) - selected[3] = selected[4] - - - - -/obj/machinery/alarm/proc/atmos_reset() - if(alarm_area.atmosalert(0, src)) - apply_danger_level(0) - update_icon() - -/obj/machinery/alarm/attackby(obj/item/W as obj, mob/user as mob) - add_fingerprint(user) - if(alarm_deconstruction_screwdriver(user, W)) - return - if(alarm_deconstruction_wirecutters(user, W)) - return - - if(istype(W, /obj/item/weapon/card/id) || istype(W, /obj/item/device/pda))// trying to unlock the interface with an ID card - togglelock() - return ..() - -/obj/machinery/alarm/verb/togglelock(mob/user as mob) - if(stat & (NOPOWER|BROKEN)) - to_chat(user, "It does nothing.") - return - else - if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN)) - locked = !locked - to_chat(user, "You [locked ? "lock" : "unlock"] the Air Alarm interface.") - else - to_chat(user, "Access denied.") - return - -/obj/machinery/alarm/AltClick() - ..() - togglelock() - -/obj/machinery/alarm/power_change() - ..() - spawn(rand(0,15)) - update_icon() - -// VOREStation Edit Start -/obj/machinery/alarm/freezer - target_temperature = T0C - 13.15 // Chilly freezer room - -/obj/machinery/alarm/freezer/first_run() - . = ..() - - TLV["temperature"] = list(T0C - 40, T0C - 20, T0C + 40, T0C + 66) // K, Lower Temperature for Freezer Air Alarms (This is because TLV is hardcoded to be generated on first_run, and therefore the only way to modify this without changing TLV generation) - -// VOREStation Edit End -#undef LOAD_TLV_VALUES -#undef TEST_TLV_VALUES -#undef DECLARE_TLV_VALUES +#define DECLARE_TLV_VALUES var/red_min; var/yel_min; var/yel_max; var/red_max; var/tlv_comparitor; +#define LOAD_TLV_VALUES(x, y) red_min = x[1]; yel_min = x[2]; yel_max = x[3]; red_max = x[4]; tlv_comparitor = y; +#define TEST_TLV_VALUES (((tlv_comparitor >= red_max && red_max > 0) || tlv_comparitor <= red_min) ? 2 : ((tlv_comparitor >= yel_max && yel_max > 0) || tlv_comparitor <= yel_min) ? 1 : 0) + +#define AALARM_MODE_SCRUBBING 1 +#define AALARM_MODE_REPLACEMENT 2 //like scrubbing, but faster. +#define AALARM_MODE_PANIC 3 //constantly sucks all air +#define AALARM_MODE_CYCLE 4 //sucks off all air, then refill and switches to scrubbing +#define AALARM_MODE_FILL 5 //emergency fill +#define AALARM_MODE_OFF 6 //Shuts it all down. + +#define AALARM_SCREEN_MAIN 1 +#define AALARM_SCREEN_VENT 2 +#define AALARM_SCREEN_SCRUB 3 +#define AALARM_SCREEN_MODE 4 +#define AALARM_SCREEN_SENSORS 5 + +#define AALARM_REPORT_TIMEOUT 100 + +#define MAX_TEMPERATURE 90 +#define MIN_TEMPERATURE -40 + +//all air alarms in area are connected via magic +/area + var/obj/machinery/alarm/master_air_alarm + var/list/air_vent_names = list() + var/list/air_scrub_names = list() + var/list/air_vent_info = list() + var/list/air_scrub_info = list() + +/obj/machinery/alarm + name = "alarm" + desc = "Used to control various station atmospheric systems. The light indicates the current air status of the area." + icon = 'icons/obj/monitors_vr.dmi' //VOREStation Edit - Other icons + icon_state = "alarm_0" + layer = ABOVE_WINDOW_LAYER + vis_flags = VIS_HIDE // They have an emissive that looks bad in openspace due to their wall-mounted nature + anchored = TRUE + unacidable = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 80 + active_power_usage = 1000 //For heating/cooling rooms. 1000 joules equates to about 1 degree every 2 seconds for a single tile of air. + power_channel = ENVIRON + req_one_access = list(access_atmospherics, access_engine_equip) + clicksound = "button" + clickvol = 30 + blocks_emissive = NONE + light_power = 0.25 + var/alarm_id = null + var/breach_detection = 1 // Whether to use automatic breach detection or not + var/frequency = 1439 + //var/skipprocess = 0 //Experimenting + var/alarm_frequency = 1437 + var/remote_control = 0 + var/rcon_setting = 2 + var/rcon_time = 0 + var/locked = 1 + panel_open = FALSE // If it's been screwdrivered open. + var/aidisabled = 0 + var/shorted = 0 + circuit = /obj/item/weapon/circuitboard/airalarm + + var/datum/wires/alarm/wires + + var/mode = AALARM_MODE_SCRUBBING + var/screen = AALARM_SCREEN_MAIN + var/area_uid + var/area/alarm_area + + var/target_temperature = T0C+20 + var/regulating_temperature = 0 + + var/datum/radio_frequency/radio_connection + + /// Keys are things like temperature and certain gasses. Values are lists, which contain, in order: + /// red warning minimum value, yellow warning minimum value, yellow warning maximum value, red warning maximum value + /// Use code\defines\gases.dm as reference for id/name. Please keep it consistent + var/list/TLV = list() + var/list/trace_gas = list("nitrous_oxide", "volatile_fuel") //list of other gases that this air alarm is able to detect + + var/danger_level = 0 + var/pressure_dangerlevel = 0 + + var/report_danger_level = 1 + + var/alarms_hidden = FALSE //If the alarms from this machine are visible on consoles + +/obj/machinery/alarm/nobreach + breach_detection = 0 + +/obj/machinery/alarm/monitor + report_danger_level = 0 + breach_detection = 0 + +/obj/machinery/alarm/alarms_hidden + alarms_hidden = TRUE + +/obj/machinery/alarm/angled + icon = 'icons/obj/wall_machines_angled.dmi' + +/obj/machinery/alarm/angled/hidden + alarms_hidden = TRUE + +/obj/machinery/alarm/angled/offset_airalarm() + pixel_x = (dir & 3) ? 0 : (dir == 4 ? -21 : 21) + pixel_y = (dir & 3) ? (dir == 1 ? -18 : 20) : 0 + +/obj/machinery/alarm/server/Initialize(mapload) + . = ..() + req_access = list(access_rd, access_atmospherics, access_engine_equip) + TLV["oxygen"] = list(-1.0, -1.0,-1.0,-1.0) // Partial pressure, kpa + TLV["carbon_dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa + TLV["phoron"] = list(-1.0, -1.0, 0, 0.5) // Partial pressure, kpa + TLV["other"] = list(-1.0, -1.0, 0.5, 1.0) // Partial pressure, kpa + TLV["pressure"] = list(0,ONE_ATMOSPHERE*0.10,ONE_ATMOSPHERE*1.40,ONE_ATMOSPHERE*1.60) /* kpa */ + TLV["temperature"] = list(20, 40, 140, 160) // K + target_temperature = 90 + +/obj/machinery/alarm/Initialize(mapload) + . = ..() + if(!pixel_x && !pixel_y) + offset_airalarm() + first_run() + +/obj/machinery/alarm/Destroy() + unregister_radio(src, frequency) + qdel(wires) + wires = null + if(alarm_area && alarm_area.master_air_alarm == src) + alarm_area.master_air_alarm = null + elect_master(exclude_self = TRUE) + return ..() + +/obj/machinery/alarm/proc/offset_airalarm() + pixel_x = (dir & 3) ? 0 : (dir == 4 ? -26 : 26) + pixel_y = (dir & 3) ? (dir == 1 ? -26 : 26) : 0 + +/obj/machinery/alarm/proc/first_run() + alarm_area = get_area(src) + area_uid = "\ref[alarm_area]" + if(name == "alarm") + name = "[alarm_area.name] Air Alarm" + + if(!wires) + wires = new(src) + + // breathable air according to human/Life() + TLV["oxygen"] = list(16, 19, 135, 140) // Partial pressure, kpa + TLV["nitrogen"] = list(0, 0, 135, 140) // Partial pressure, kpa + TLV["carbon_dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa + TLV["phoron"] = list(-1.0, -1.0, 0, 0.5) // Partial pressure, kpa + TLV["other"] = list(-1.0, -1.0, 0.5, 1.0) // Partial pressure, kpa + TLV["pressure"] = list(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE * 0.90, ONE_ATMOSPHERE * 1.10, ONE_ATMOSPHERE * 1.20) /* kpa */ + TLV["temperature"] = list(T0C - 26, T0C, T0C + 40, T0C + 66) // K + + update_icon() + +/obj/machinery/alarm/proc/update_area() + alarm_area = get_area(src) + area_uid = "\ref[alarm_area]" + if(name == "alarm") + name = "[alarm_area.name] Air Alarm" + +/obj/machinery/alarm/Initialize() + . = ..() + set_frequency(frequency) + if(!master_is_operating()) + elect_master() + +/obj/machinery/alarm/process() + if((stat & (NOPOWER|BROKEN)) || shorted) + return + + var/turf/simulated/location = src.loc + if(!istype(location)) return//returns if loc is not simulated + + var/datum/gas_mixture/environment = location.return_air() + + //Handle temperature adjustment here. + handle_heating_cooling(environment) + + var/old_level = danger_level + var/old_pressurelevel = pressure_dangerlevel + danger_level = overall_danger_level(environment) + + if(old_level != danger_level) + apply_danger_level(danger_level) + + if(old_pressurelevel != pressure_dangerlevel) + if(breach_detected()) + mode = AALARM_MODE_OFF + apply_mode() + + if(mode == AALARM_MODE_CYCLE && environment.return_pressure() < ONE_ATMOSPHERE * 0.05) + mode = AALARM_MODE_FILL + apply_mode() + + //atmos computer remote controll stuff + switch(rcon_setting) + if(RCON_NO) + remote_control = 0 + if(RCON_AUTO) + if(danger_level == 2) + remote_control = 1 + else + remote_control = 0 + if(RCON_YES) + remote_control = 1 + + return + +/obj/machinery/alarm/proc/handle_heating_cooling(var/datum/gas_mixture/environment) + DECLARE_TLV_VALUES + LOAD_TLV_VALUES(TLV["temperature"], target_temperature) + if(!regulating_temperature) + //check for when we should start adjusting temperature + if(!TEST_TLV_VALUES && abs(environment.temperature - target_temperature) > 2.0 && environment.return_pressure() >= 1) + update_use_power(USE_POWER_ACTIVE) + regulating_temperature = 1 + audible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ + "You hear a click and a faint electronic hum.", runemessage = "* click *") + playsound(src, 'sound/machines/click.ogg', 50, 1) + else + //check for when we should stop adjusting temperature + if(TEST_TLV_VALUES || abs(environment.temperature - target_temperature) <= 0.5 || environment.return_pressure() < 1) + update_use_power(USE_POWER_IDLE) + regulating_temperature = 0 + audible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ + "You hear a click as a faint electronic humming stops.", runemessage = "* click *") + playsound(src, 'sound/machines/click.ogg', 50, 1) + + if(regulating_temperature) + if(target_temperature > T0C + MAX_TEMPERATURE) + target_temperature = T0C + MAX_TEMPERATURE + + if(target_temperature < T0C + MIN_TEMPERATURE) + target_temperature = T0C + MIN_TEMPERATURE + + var/datum/gas_mixture/gas + gas = environment.remove(0.25 * environment.total_moles) + if(gas) + + if(gas.temperature <= target_temperature) //gas heating + var/energy_used = min(gas.get_thermal_energy_change(target_temperature) , active_power_usage) + + gas.add_thermal_energy(energy_used) + //use_power(energy_used, ENVIRON) //handle by update_use_power instead + else //gas cooling + var/heat_transfer = min(abs(gas.get_thermal_energy_change(target_temperature)), active_power_usage) + + //Assume the heat is being pumped into the hull which is fixed at 20 C + //none of this is really proper thermodynamics but whatever + + var/cop = gas.temperature / T20C //coefficient of performance -> power used = heat_transfer/cop + + heat_transfer = min(heat_transfer, cop * active_power_usage) //this ensures that we don't use more than active_power_usage amount of power + + heat_transfer = -gas.add_thermal_energy(-heat_transfer) //get the actual heat transfer + + //use_power(heat_transfer / cop, ENVIRON) //handle by update_use_power instead + + environment.merge(gas) + +/obj/machinery/alarm/proc/overall_danger_level(var/datum/gas_mixture/environment) + var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature/environment.volume + var/environment_pressure = environment.return_pressure() + + var/other_moles = 0 + for(var/g in trace_gas) + other_moles += environment.gas[g] //this is only going to be used in a partial pressure calc, so we don't need to worry about group_multiplier here. + + DECLARE_TLV_VALUES + LOAD_TLV_VALUES(TLV["pressure"], environment_pressure) + pressure_dangerlevel = TEST_TLV_VALUES // not local because it's used in process() + LOAD_TLV_VALUES(TLV["oxygen"], environment.gas["oxygen"]*partial_pressure) + var/oxygen_dangerlevel = TEST_TLV_VALUES + LOAD_TLV_VALUES(TLV["carbon_dioxide"], environment.gas["carbon_dioxide"]*partial_pressure) + var/co2_dangerlevel = TEST_TLV_VALUES + LOAD_TLV_VALUES(TLV["phoron"], environment.gas["phoron"]*partial_pressure) + var/phoron_dangerlevel = TEST_TLV_VALUES + LOAD_TLV_VALUES(TLV["temperature"], environment.temperature) + var/temperature_dangerlevel = TEST_TLV_VALUES + LOAD_TLV_VALUES(TLV["other"], other_moles*partial_pressure) + var/other_dangerlevel = TEST_TLV_VALUES + + return max( + pressure_dangerlevel, + oxygen_dangerlevel, + co2_dangerlevel, + phoron_dangerlevel, + other_dangerlevel, + temperature_dangerlevel + ) + +// Returns whether this air alarm thinks there is a breach, given the sensors that are available to it. +/obj/machinery/alarm/proc/breach_detected() + var/turf/simulated/location = src.loc + + if(!istype(location)) + return 0 + + if(breach_detection == 0) + return 0 + + var/datum/gas_mixture/environment = location.return_air() + var/environment_pressure = environment.return_pressure() + var/pressure_levels = TLV["pressure"] + + if(environment_pressure <= pressure_levels[1]) //low pressures + if(!(mode == AALARM_MODE_PANIC || mode == AALARM_MODE_CYCLE)) + return 1 + + return 0 + +/obj/machinery/alarm/proc/master_is_operating() + return alarm_area && alarm_area.master_air_alarm && !(alarm_area.master_air_alarm.stat & (NOPOWER | BROKEN)) + +/obj/machinery/alarm/proc/elect_master(exclude_self = FALSE) + for(var/obj/machinery/alarm/AA in alarm_area) + if(exclude_self && AA == src) + continue + if(!(AA.stat & (NOPOWER|BROKEN))) + alarm_area.master_air_alarm = AA + return 1 + return 0 + +/obj/machinery/alarm/update_icon() + cut_overlays() + + if(panel_open) + icon_state = "alarmx" + set_light(0) + set_light_on(FALSE) + return + if((stat & (NOPOWER|BROKEN)) || shorted) + icon_state = "alarmp" + set_light(0) + set_light_on(FALSE) + return + + var/icon_level = danger_level + if(alarm_area?.atmosalm) + icon_level = max(icon_level, 1) //if there's an atmos alarm but everything is okay locally, no need to go past yellow + + var/new_color = null + switch(icon_level) + if(0) + icon_state = "alarm_0" + add_overlay(mutable_appearance(icon, "alarm_ov0")) + add_overlay(emissive_appearance(icon, "alarm_ov0")) + new_color = "#03A728" + if(1) + icon_state = "alarm_2" //yes, alarm2 is yellow alarm + add_overlay(mutable_appearance(icon, "alarm_ov2")) + add_overlay(emissive_appearance(icon, "alarm_ov2")) + new_color = "#EC8B2F" + if(2) + icon_state = "alarm_1" + add_overlay(mutable_appearance(icon, "alarm_ov1")) + add_overlay(emissive_appearance(icon, "alarm_ov1")) + new_color = "#DA0205" + + set_light(l_range = 2, l_power = 0.25, l_color = new_color) + set_light_on(TRUE) + +/obj/machinery/alarm/receive_signal(datum/signal/signal) + if(stat & (NOPOWER|BROKEN)) + return + if(alarm_area.master_air_alarm != src) + if(master_is_operating()) + return + elect_master() + if(alarm_area.master_air_alarm != src) + return + if(!signal || signal.encryption) + return + var/id_tag = signal.data["tag"] + if(!id_tag) + return + if(signal.data["area"] != area_uid) + return + if(signal.data["sigtype"] != "status") + return + + var/dev_type = signal.data["device"] + if(!(id_tag in alarm_area.air_scrub_names) && !(id_tag in alarm_area.air_vent_names)) + register_env_machine(id_tag, dev_type) + if(dev_type == "AScr") + alarm_area.air_scrub_info[id_tag] = signal.data + else if(dev_type == "AVP") + alarm_area.air_vent_info[id_tag] = signal.data + +/obj/machinery/alarm/proc/register_env_machine(var/m_id, var/device_type) + var/new_name + if(device_type == "AVP") + new_name = "[alarm_area.name] Vent Pump #[alarm_area.air_vent_names.len+1]" + alarm_area.air_vent_names[m_id] = new_name + else if(device_type == "AScr") + new_name = "[alarm_area.name] Air Scrubber #[alarm_area.air_scrub_names.len+1]" + alarm_area.air_scrub_names[m_id] = new_name + else + return + spawn(10) + send_signal(m_id, list("init" = new_name)) + +/obj/machinery/alarm/proc/refresh_all() + for(var/id_tag in alarm_area.air_vent_names) + var/list/I = alarm_area.air_vent_info[id_tag] + if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time) + continue + send_signal(id_tag, list("status")) + for(var/id_tag in alarm_area.air_scrub_names) + var/list/I = alarm_area.air_scrub_info[id_tag] + if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time) + continue + send_signal(id_tag, list("status")) + +/obj/machinery/alarm/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency, RADIO_TO_AIRALARM) + +/obj/machinery/alarm/proc/send_signal(var/target, var/list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + + signal.data = command + signal.data["tag"] = target + signal.data["sigtype"] = "command" + + radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) +// to_world("Signal [command] Broadcasted to [target]") + + return 1 + +/obj/machinery/alarm/proc/apply_mode() + //propagate mode to other air alarms in the area + //TODO: make it so that players can choose between applying the new mode to the room they are in (related area) vs the entire alarm area + for(var/obj/machinery/alarm/AA in alarm_area) + AA.mode = mode + + switch(mode) + if(AALARM_MODE_SCRUBBING) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list("power"= 1, "co2_scrub"= 1, "scrubbing"= 1, "panic_siphon"= 0)) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list("power"= 1, "checks"= "default", "set_external_pressure"= "default")) + + if(AALARM_MODE_PANIC, AALARM_MODE_CYCLE) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list("power"= 1, "panic_siphon"= 1)) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list("power"= 0)) + + if(AALARM_MODE_REPLACEMENT) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list("power"= 1, "panic_siphon"= 1)) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list("power"= 1, "checks"= "default", "set_external_pressure"= "default")) + + if(AALARM_MODE_FILL) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list("power"= 0)) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list("power"= 1, "checks"= "default", "set_external_pressure"= "default")) + + if(AALARM_MODE_OFF) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list("power"= 0)) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list("power"= 0)) + +/obj/machinery/alarm/proc/apply_danger_level(var/new_danger_level) + if(report_danger_level && alarm_area.atmosalert(new_danger_level, src)) + post_alert(new_danger_level) + + update_icon() + +/obj/machinery/alarm/proc/post_alert(alert_level) + var/datum/radio_frequency/frequency = radio_controller.return_frequency(alarm_frequency) + if(!frequency) + return + + var/datum/signal/alert_signal = new + alert_signal.source = src + alert_signal.transmission_method = TRANSMISSION_RADIO + alert_signal.data["zone"] = alarm_area.name + alert_signal.data["type"] = "Atmospheric" + + if(alert_level==2) + alert_signal.data["alert"] = "severe" + else if(alert_level==1) + alert_signal.data["alert"] = "minor" + else if(alert_level==0) + alert_signal.data["alert"] = "clear" + + frequency.post_signal(src, alert_signal) + +/obj/machinery/alarm/attack_ai(mob/user) + tgui_interact(user) + +/obj/machinery/alarm/attack_hand(mob/user) + . = ..() + if(.) + return + return interact(user) + +/obj/machinery/alarm/interact(mob/user) + tgui_interact(user) + wires.Interact(user) + +/obj/machinery/alarm/tgui_status(mob/user) + if(isAI(user) && aidisabled) + to_chat(user, "AI control has been disabled.") + else if(!shorted) + return ..() + return STATUS_CLOSE + +/obj/machinery/alarm/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, datum/tgui_state/state) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AirAlarm", name, parent_ui) + if(state) + ui.set_state(state) + ui.open() + +/obj/machinery/alarm/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = list( + "locked" = locked, + "siliconUser" = issilicon(user), + "remoteUser" = !!ui.parent_ui, + "danger_level" = danger_level, + "target_temperature" = "[target_temperature - T0C]C", + "rcon" = rcon_setting, + ) + + var/area/A = get_area(src) + data["atmos_alarm"] = A?.atmosalm + data["fire_alarm"] = A?.fire + + var/turf/T = get_turf(src) + var/datum/gas_mixture/environment = T.return_air() + + var/list/list/environment_data = list() + data["environment_data"] = environment_data + + DECLARE_TLV_VALUES + + var/pressure = environment.return_pressure() + LOAD_TLV_VALUES(TLV["pressure"], pressure) + environment_data.Add(list(list( + "name" = "Pressure", + "value" = pressure, + "unit" = "kPa", + "danger_level" = TEST_TLV_VALUES + ))) + + var/temperature = environment.temperature + LOAD_TLV_VALUES(TLV["temperature"], temperature) + environment_data.Add(list(list( + "name" = "Temperature", + "value" = temperature, + "unit" = "K ([round(temperature - T0C, 0.1)]C)", + "danger_level" = TEST_TLV_VALUES + ))) + + var/total_moles = environment.total_moles + var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature / environment.volume + for(var/gas_id in environment.gas) + if(!(gas_id in TLV)) + continue + LOAD_TLV_VALUES(TLV[gas_id], environment.gas[gas_id] * partial_pressure) + environment_data.Add(list(list( + "name" = gas_id, + "value" = environment.gas[gas_id] / total_moles * 100, + "unit" = "%", + "danger_level" = TEST_TLV_VALUES + ))) + + if(!locked || issilicon(user) || data["remoteUser"]) + var/list/list/vents = list() + data["vents"] = vents + for(var/id_tag in A.air_vent_names) + var/long_name = A.air_vent_names[id_tag] + var/list/info = A.air_vent_info[id_tag] + if(!info) + continue + vents.Add(list(list( + "id_tag" = id_tag, + "long_name" = sanitize(long_name), + "power" = info["power"], + "checks" = info["checks"], + "excheck" = info["checks"]&1, + "incheck" = info["checks"]&2, + "direction" = info["direction"], + "external" = info["external"], + "internal" = info["internal"], + "extdefault"= (info["external"] == ONE_ATMOSPHERE), + "intdefault"= (info["internal"] == 0), + ))) + + + var/list/list/scrubbers = list() + data["scrubbers"] = scrubbers + for(var/id_tag in alarm_area.air_scrub_names) + var/long_name = alarm_area.air_scrub_names[id_tag] + var/list/info = alarm_area.air_scrub_info[id_tag] + if(!info) + continue + scrubbers += list(list( + "id_tag" = id_tag, + "long_name" = sanitize(long_name), + "power" = info["power"], + "scrubbing" = info["scrubbing"], + "panic" = info["panic"], + "filters" = list( + list("name" = "Oxygen", "command" = "o2_scrub", "val" = info["filter_o2"]), + list("name" = "Nitrogen", "command" = "n2_scrub", "val" = info["filter_n2"]), + list("name" = "Carbon Dioxide", "command" = "co2_scrub","val" = info["filter_co2"]), + list("name" = "Phoron" , "command" = "tox_scrub","val" = info["filter_phoron"]), + list("name" = "Nitrous Oxide", "command" = "n2o_scrub","val" = info["filter_n2o"]), + list("name" = "Volatile Fuel", "command" = "fuel_scrub","val" = info["filter_fuel"]) + ) + )) + data["scrubbers"] = scrubbers + + data["mode"] = mode + + var/list/list/modes = list() + data["modes"] = modes + modes[++modes.len] = list("name" = "Filtering - Scrubs out contaminants", "mode" = AALARM_MODE_SCRUBBING, "selected" = mode == AALARM_MODE_SCRUBBING, "danger" = 0) + modes[++modes.len] = list("name" = "Replace Air - Siphons out air while replacing", "mode" = AALARM_MODE_REPLACEMENT, "selected" = mode == AALARM_MODE_REPLACEMENT, "danger" = 0) + modes[++modes.len] = list("name" = "Panic - Siphons air out of the room", "mode" = AALARM_MODE_PANIC, "selected" = mode == AALARM_MODE_PANIC, "danger" = 1) + modes[++modes.len] = list("name" = "Cycle - Siphons air before replacing", "mode" = AALARM_MODE_CYCLE, "selected" = mode == AALARM_MODE_CYCLE, "danger" = 1) + modes[++modes.len] = list("name" = "Fill - Shuts off scrubbers and opens vents", "mode" = AALARM_MODE_FILL, "selected" = mode == AALARM_MODE_FILL, "danger" = 0) + modes[++modes.len] = list("name" = "Off - Shuts off vents and scrubbers", "mode" = AALARM_MODE_OFF, "selected" = mode == AALARM_MODE_OFF, "danger" = 0) + + var/list/selected + var/list/thresholds = list() + + var/list/gas_names = list("oxygen", "carbon_dioxide", "phoron", "other") //Gas ids made to match code\defines\gases.dm + for(var/g in gas_names) + thresholds[++thresholds.len] = list("name" = g, "settings" = list()) + selected = TLV[g] + for(var/i = 1, i <= 4, i++) + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = i, "selected" = selected[i])) + + selected = TLV["pressure"] + thresholds[++thresholds.len] = list("name" = "Pressure", "settings" = list()) + for(var/i = 1, i <= 4, i++) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = i, "selected" = selected[i])) + + selected = TLV["temperature"] + thresholds[++thresholds.len] = list("name" = "Temperature", "settings" = list()) + for(var/i = 1, i <= 4, i++) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = i, "selected" = selected[i])) + + data["thresholds"] = thresholds + return data + +/obj/machinery/alarm/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + if(action == "rcon") + var/attempted_rcon_setting = text2num(params["rcon"]) + + switch(attempted_rcon_setting) + if(RCON_NO) + rcon_setting = RCON_NO + if(RCON_AUTO) + rcon_setting = RCON_AUTO + if(RCON_YES) + rcon_setting = RCON_YES + return TRUE + + if(action == "temperature") + var/list/selected = TLV["temperature"] + var/max_temperature = min(selected[3] - T0C, MAX_TEMPERATURE) + var/min_temperature = max(selected[2] - T0C, MIN_TEMPERATURE) + var/input_temperature = tgui_input_number(usr, "What temperature would you like the system to mantain? (Capped between [min_temperature] and [max_temperature]C)", "Thermostat Controls", target_temperature - T0C, max_temperature, min_temperature, round_value = FALSE) + if(isnum(input_temperature)) + if(input_temperature > max_temperature || input_temperature < min_temperature) + to_chat(usr, "Temperature must be between [min_temperature]C and [max_temperature]C") + else + target_temperature = input_temperature + T0C + return TRUE + + // Account for remote users here. + // Yes, this is kinda snowflaky; however, I would argue it would be far more snowflakey + // to include "custom hrefs" and all the other bullshit that nano states have just for the + // like, two UIs, that want remote access to other UIs. + if((locked && !issilicon(usr) && !istype(state, /datum/tgui_state/air_alarm_remote)) || (issilicon(usr) && aidisabled)) + return + + var/device_id = params["id_tag"] + switch(action) + if("lock") + if(issilicon(usr) && !wires.is_cut(WIRE_IDSCAN)) + locked = !locked + . = TRUE + if( "power", + "o2_scrub", + "n2_scrub", + "co2_scrub", + "tox_scrub", + "n2o_scrub", + "fuel_scrub", + "panic_siphon", + "scrubbing", + "direction") + send_signal(device_id, list("[action]" = text2num(params["val"])), usr) + . = TRUE + if("excheck") + send_signal(device_id, list("checks" = text2num(params["val"])^1), usr) + . = TRUE + if("incheck") + send_signal(device_id, list("checks" = text2num(params["val"])^2), usr) + . = TRUE + if("set_external_pressure", "set_internal_pressure") + var/target = params["value"] + if(!isnull(target)) + send_signal(device_id, list("[action]" = target), usr) + . = TRUE + if("reset_external_pressure") + send_signal(device_id, list("reset_external_pressure"), usr) + . = TRUE + if("reset_internal_pressure") + send_signal(device_id, list("reset_internal_pressure"), usr) + . = TRUE + if("threshold") + var/env = params["env"] + + var/name = params["var"] + var/value = tgui_input_number(usr, "New [name] for [env]:", name, TLV[env][name], min_value=-1, round_value = FALSE) + if(!isnull(value) && !..()) + if(value < 0) + TLV[env][name] = -1 + else + TLV[env][name] = round(value, 0.01) + clamp_tlv_values(env, name) + // investigate_log(" treshold value for [env]:[name] was set to [value] by [key_name(usr)]",INVESTIGATE_ATMOS) + . = TRUE + if("mode") + mode = text2num(params["mode"]) + // investigate_log("was turned to [get_mode_name(mode)] mode by [key_name(usr)]",INVESTIGATE_ATMOS) + apply_mode(usr) + . = TRUE + if("alarm") + if(alarm_area.atmosalert(2, src)) + apply_danger_level(2) + . = TRUE + if("reset") + atmos_reset() + . = TRUE + update_icon() + +// This big ol' mess just ensures that TLV always makes sense. If you set the max value below the min value, +// it'll automatically update all the other values to keep it sane. +/obj/machinery/alarm/proc/clamp_tlv_values(env, changed_threshold) + var/list/selected = TLV[env] + switch(changed_threshold) + if(1) + if(selected[1] > selected[2]) + selected[2] = selected[1] + if(selected[1] > selected[3]) + selected[3] = selected[1] + if(selected[1] > selected[4]) + selected[4] = selected[1] + if(2) + if(selected[1] > selected[2]) + selected[1] = selected[2] + if(selected[2] > selected[3]) + selected[3] = selected[2] + if(selected[2] > selected[4]) + selected[4] = selected[2] + if(3) + if(selected[1] > selected[3]) + selected[1] = selected[3] + if(selected[2] > selected[3]) + selected[2] = selected[3] + if(selected[3] > selected[4]) + selected[4] = selected[3] + if(4) + if(selected[1] > selected[4]) + selected[1] = selected[4] + if(selected[2] > selected[4]) + selected[2] = selected[4] + if(selected[3] > selected[4]) + selected[3] = selected[4] + + + + +/obj/machinery/alarm/proc/atmos_reset() + if(alarm_area.atmosalert(0, src)) + apply_danger_level(0) + update_icon() + +/obj/machinery/alarm/attackby(obj/item/W as obj, mob/user as mob) + add_fingerprint(user) + if(alarm_deconstruction_screwdriver(user, W)) + return + if(alarm_deconstruction_wirecutters(user, W)) + return + + if(istype(W, /obj/item/weapon/card/id) || istype(W, /obj/item/device/pda))// trying to unlock the interface with an ID card + togglelock() + return ..() + +/obj/machinery/alarm/verb/togglelock(mob/user as mob) + if(stat & (NOPOWER|BROKEN)) + to_chat(user, "It does nothing.") + return + else + if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN)) + locked = !locked + to_chat(user, "You [locked ? "lock" : "unlock"] the Air Alarm interface.") + else + to_chat(user, "Access denied.") + return + +/obj/machinery/alarm/AltClick() + ..() + togglelock() + +/obj/machinery/alarm/power_change() + ..() + spawn(rand(0,15)) + update_icon() + +// VOREStation Edit Start +/obj/machinery/alarm/freezer + target_temperature = T0C - 13.15 // Chilly freezer room + +/obj/machinery/alarm/freezer/first_run() + . = ..() + + TLV["temperature"] = list(T0C - 40, T0C - 20, T0C + 40, T0C + 66) // K, Lower Temperature for Freezer Air Alarms (This is because TLV is hardcoded to be generated on first_run, and therefore the only way to modify this without changing TLV generation) + +// VOREStation Edit End +#undef LOAD_TLV_VALUES +#undef TEST_TLV_VALUES +#undef DECLARE_TLV_VALUES diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm index 27fc21f4f15..ca824fc22ae 100644 --- a/code/game/machinery/atmo_control.dm +++ b/code/game/machinery/atmo_control.dm @@ -1,455 +1,455 @@ -/obj/machinery/air_sensor - icon = 'icons/obj/stationobjs.dmi' - icon_state = "gsensor1" - name = "Gas Sensor" - desc = "Senses atmospheric conditions." - - anchored = TRUE - var/state = 0 - - var/id_tag - var/frequency = 1439 - - var/on = 1 - var/output = 3 - //Flags: - // 1 for pressure - // 2 for temperature - // Output >= 4 includes gas composition - // 4 for oxygen concentration - // 8 for phoron concentration - // 16 for nitrogen concentration - // 32 for carbon dioxide concentration - - var/datum/radio_frequency/radio_connection - -/obj/machinery/air_sensor/update_icon() - icon_state = "gsensor[on]" - -/obj/machinery/air_sensor/process() - if(on) - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.data["tag"] = id_tag - signal.data["timestamp"] = world.time - - var/datum/gas_mixture/air_sample = return_air() - - if(output&1) - signal.data["pressure"] = num2text(round(air_sample.return_pressure(),0.1),) - if(output&2) - signal.data["temperature"] = round(air_sample.temperature,0.1) - - if(output>4) - var/total_moles = air_sample.total_moles - if(total_moles > 0) - if(output&4) - signal.data["oxygen"] = round(100*air_sample.gas["oxygen"]/total_moles,0.1) - if(output&8) - signal.data["phoron"] = round(100*air_sample.gas["phoron"]/total_moles,0.1) - if(output&16) - signal.data["nitrogen"] = round(100*air_sample.gas["nitrogen"]/total_moles,0.1) - if(output&32) - signal.data["carbon_dioxide"] = round(100*air_sample.gas["carbon_dioxide"]/total_moles,0.1) - else - signal.data["oxygen"] = 0 - signal.data["phoron"] = 0 - signal.data["nitrogen"] = 0 - signal.data["carbon_dioxide"] = 0 - signal.data["sigtype"]="status" - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - -/obj/machinery/air_sensor/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/air_sensor/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/air_sensor/Destroy() - if(radio_controller) - radio_controller.remove_object(src,frequency) - . = ..() - -/obj/machinery/computer/general_air_control - icon_keyboard = "atmos_key" - icon_screen = "tank" - name = "Computer" - desc = "Control atmospheric systems, remotely." - var/frequency = 1439 - var/list/sensors = list() - var/list/sensor_information = list() - var/datum/radio_frequency/radio_connection - circuit = /obj/item/weapon/circuitboard/air_management - -/obj/machinery/computer/general_air_control/Destroy() - if(radio_controller) - radio_controller.remove_object(src, frequency) - ..() - -/obj/machinery/computer/general_air_control/attack_hand(mob/user) - if(..(user)) - return - - tgui_interact(user) - -/obj/machinery/computer/general_air_control/receive_signal(datum/signal/signal) - if(!signal || signal.encryption) return - - var/id_tag = signal.data["tag"] - if(!id_tag || !sensors.Find(id_tag)) return - - sensor_information[id_tag] = signal.data - -/obj/machinery/computer/general_air_control/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "GeneralAtmoControl", name) - ui.open() - -/obj/machinery/computer/general_air_control/tgui_data(mob/user) - var/list/data = list() - var/sensors_ui[0] - if(sensors.len) - for(var/id_tag in sensors) - var/long_name = sensors[id_tag] - var/list/sensor_data = sensor_information[id_tag] - sensors_ui[++sensors_ui.len] = list("long_name" = long_name, "sensor_data" = sensor_data) - else - sensors_ui = null - - data["sensors"] = sensors_ui - - return data - -/obj/machinery/computer/general_air_control/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/computer/general_air_control/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/computer/general_air_control/large_tank_control - icon = 'icons/obj/computer.dmi' - frequency = 1441 - var/input_tag - var/output_tag - var/list/input_info - var/list/output_info - var/input_flow_setting = 200 - var/pressure_setting = ONE_ATMOSPHERE * 45 - circuit = /obj/item/weapon/circuitboard/air_management/tank_control - -/obj/machinery/computer/general_air_control/large_tank_control/tgui_data(mob/user) - var/list/data = ..() - - data["tanks"] = 1 - - if(input_info) - data["input_info"] = list("power" = input_info["power"], "volume_rate" = round(input_info["volume_rate"], 0.1)) - else - data["input_info"] = null - - if(output_info) - data["output_info"] = list("power" = output_info["power"], "output_pressure" = output_info["internal"]) - else - data["output_info"] = null - - data["input_flow_setting"] = round(input_flow_setting, 0.1) - data["pressure_setting"] = pressure_setting - data["max_pressure"] = 50*ONE_ATMOSPHERE - data["max_flowrate"] = ATMOS_DEFAULT_VOLUME_PUMP + 500 - - return data - -/obj/machinery/computer/general_air_control/large_tank_control/receive_signal(datum/signal/signal) - if(!signal || signal.encryption) return - - var/id_tag = signal.data["tag"] - - if(input_tag == id_tag) - input_info = signal.data - else if(output_tag == id_tag) - output_info = signal.data - else - ..(signal) - -/obj/machinery/computer/general_air_control/large_tank_control/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("adj_pressure") - var/new_pressure = text2num(params["adj_pressure"]) - pressure_setting = between(0, new_pressure, 50*ONE_ATMOSPHERE) - return TRUE - - if("adj_input_flow_rate") - var/new_flow = text2num(params["adj_input_flow_rate"]) - input_flow_setting = between(0, new_flow, ATMOS_DEFAULT_VOLUME_PUMP + 500) //default flow rate limit for air injectors - return TRUE - - if(!radio_connection) - return FALSE - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - switch(action) - if("in_refresh_status") - input_info = null - signal.data = list ("tag" = input_tag, "status" = 1) - . = TRUE - - if("in_toggle_injector") - input_info = null - signal.data = list ("tag" = input_tag, "power_toggle" = 1) - . = TRUE - - if("in_set_flowrate") - input_info = null - signal.data = list ("tag" = input_tag, "set_volume_rate" = "[input_flow_setting]") - . = TRUE - - if("out_refresh_status") - output_info = null - signal.data = list ("tag" = output_tag, "status" = 1) - . = TRUE - - if("out_toggle_power") - output_info = null - signal.data = list ("tag" = output_tag, "power_toggle" = 1) - . = TRUE - - if("out_set_pressure") - output_info = null - signal.data = list ("tag" = output_tag, "set_internal_pressure" = "[pressure_setting]") - . = TRUE - - signal.data["sigtype"]="command" - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - -/obj/machinery/computer/general_air_control/supermatter_core - icon = 'icons/obj/computer.dmi' - frequency = 1438 - var/input_tag - var/output_tag - var/list/input_info - var/list/output_info - var/input_flow_setting = 700 - var/pressure_setting = 100 - circuit = /obj/item/weapon/circuitboard/air_management/supermatter_core - -/obj/machinery/computer/general_air_control/supermatter_core/tgui_data(mob/user) - var/list/data = ..() - data["core"] = 1 - - if(input_info) - data["input_info"] = list("power" = input_info["power"], "volume_rate" = round(input_info["volume_rate"], 0.1)) - else - data["input_info"] = null - - if(output_info) - // Yes, TECHNICALLY this is not output pressure, it's a pressure LIMIT. HOWEVER. The fact that the UI uses "output_pressure" - // in EXACTLY THE SAME WAY as "pressure_limit" means this should just pass it as the other fucking data argument because holy shit what the - // fuck - data["output_info"] = list("power" = output_info["power"], "output_pressure" = output_info["external"]) - else - data["output_info"] = null - - data["input_flow_setting"] = round(input_flow_setting, 0.1) - data["pressure_setting"] = pressure_setting - data["max_pressure"] = 10*ONE_ATMOSPHERE - data["max_flowrate"] = ATMOS_DEFAULT_VOLUME_PUMP + 500 - - return data - -/obj/machinery/computer/general_air_control/supermatter_core/receive_signal(datum/signal/signal) - if(!signal || signal.encryption) return - - var/id_tag = signal.data["tag"] - - if(input_tag == id_tag) - input_info = signal.data - else if(output_tag == id_tag) - output_info = signal.data - else - ..(signal) - -/obj/machinery/computer/general_air_control/supermatter_core/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("adj_pressure") - var/new_pressure = text2num(params["adj_pressure"]) - pressure_setting = between(0, new_pressure, 10*ONE_ATMOSPHERE) - return TRUE - - if("adj_input_flow_rate") - var/new_flow = text2num(params["adj_input_flow_rate"]) - input_flow_setting = between(0, new_flow, ATMOS_DEFAULT_VOLUME_PUMP + 500) //default flow rate limit for air injectors - return TRUE - - if(!radio_connection) - return FALSE - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - switch(action) - if("in_refresh_status") - input_info = null - signal.data = list ("tag" = input_tag, "status" = 1) - . = TRUE - - if("in_toggle_injector") - input_info = null - signal.data = list ("tag" = input_tag, "power_toggle" = 1) - . = TRUE - - if("in_set_flowrate") - input_info = null - signal.data = list ("tag" = input_tag, "set_volume_rate" = "[input_flow_setting]") - . = TRUE - - if("out_refresh_status") - output_info = null - signal.data = list ("tag" = output_tag, "status" = 1) - . = TRUE - - if("out_toggle_power") - output_info = null - signal.data = list ("tag" = output_tag, "power_toggle" = 1) - . = TRUE - - if("out_set_pressure") - output_info = null - signal.data = list ("tag" = output_tag, "set_external_pressure" = "[pressure_setting]", "checks" = 1) - . = TRUE - - signal.data["sigtype"]="command" - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - -/obj/machinery/computer/general_air_control/fuel_injection - icon = 'icons/obj/computer.dmi' - icon_screen = "alert:0" - var/device_tag - var/list/device_info - var/automation = 0 - var/cutoff_temperature = 2000 - var/on_temperature = 1200 - circuit = /obj/item/weapon/circuitboard/air_management/injector_control - -/obj/machinery/computer/general_air_control/fuel_injection/process() - if(automation) - if(!radio_connection) - return FALSE - - var/injecting = 0 - for(var/id_tag in sensor_information) - var/list/data = sensor_information[id_tag] - if(data["temperature"]) - if(data["temperature"] >= cutoff_temperature) - injecting = 0 - break - if(data["temperature"] <= on_temperature) - injecting = 1 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - - signal.data = list( - "tag" = device_tag, - "power" = injecting, - "sigtype"="command" - ) - - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - - ..() - -/obj/machinery/computer/general_air_control/fuel_injection/tgui_data(mob/user) - var/list/data = ..() - data["fuel"] = 1 - data["automation"] = automation - - if(device_info) - data["device_info"] = list("power" = device_info["power"], "volume_rate" = device_info["volume_rate"]) - else - data["device_info"] = null - - return data - -/obj/machinery/computer/general_air_control/fuel_injection/receive_signal(datum/signal/signal) - if(!signal || signal.encryption) return - - var/id_tag = signal.data["tag"] - - if(device_tag == id_tag) - device_info = signal.data - else - ..(signal) - -/obj/machinery/computer/general_air_control/fuel_injection/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("refresh_status") - device_info = null - if(!radio_connection) - return FALSE - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - signal.data = list( - "tag" = device_tag, - "status" = 1, - "sigtype"="command" - ) - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - . = TRUE - - if("toggle_automation") - automation = !automation - . = TRUE - - if("toggle_injector") - device_info = null - if(!radio_connection) - return FALSE - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - signal.data = list( - "tag" = device_tag, - "power_toggle" = 1, - "sigtype"="command" - ) - - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - . = TRUE - - if("injection") - if(!radio_connection) - return FALSE - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - signal.data = list( - "tag" = device_tag, - "inject" = 1, - "sigtype"="command" - ) - - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) +/obj/machinery/air_sensor + icon = 'icons/obj/stationobjs.dmi' + icon_state = "gsensor1" + name = "Gas Sensor" + desc = "Senses atmospheric conditions." + + anchored = TRUE + var/state = 0 + + var/id_tag + var/frequency = 1439 + + var/on = 1 + var/output = 3 + //Flags: + // 1 for pressure + // 2 for temperature + // Output >= 4 includes gas composition + // 4 for oxygen concentration + // 8 for phoron concentration + // 16 for nitrogen concentration + // 32 for carbon dioxide concentration + + var/datum/radio_frequency/radio_connection + +/obj/machinery/air_sensor/update_icon() + icon_state = "gsensor[on]" + +/obj/machinery/air_sensor/process() + if(on) + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.data["tag"] = id_tag + signal.data["timestamp"] = world.time + + var/datum/gas_mixture/air_sample = return_air() + + if(output&1) + signal.data["pressure"] = num2text(round(air_sample.return_pressure(),0.1),) + if(output&2) + signal.data["temperature"] = round(air_sample.temperature,0.1) + + if(output>4) + var/total_moles = air_sample.total_moles + if(total_moles > 0) + if(output&4) + signal.data["oxygen"] = round(100*air_sample.gas["oxygen"]/total_moles,0.1) + if(output&8) + signal.data["phoron"] = round(100*air_sample.gas["phoron"]/total_moles,0.1) + if(output&16) + signal.data["nitrogen"] = round(100*air_sample.gas["nitrogen"]/total_moles,0.1) + if(output&32) + signal.data["carbon_dioxide"] = round(100*air_sample.gas["carbon_dioxide"]/total_moles,0.1) + else + signal.data["oxygen"] = 0 + signal.data["phoron"] = 0 + signal.data["nitrogen"] = 0 + signal.data["carbon_dioxide"] = 0 + signal.data["sigtype"]="status" + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + +/obj/machinery/air_sensor/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/air_sensor/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/air_sensor/Destroy() + if(radio_controller) + radio_controller.remove_object(src,frequency) + . = ..() + +/obj/machinery/computer/general_air_control + icon_keyboard = "atmos_key" + icon_screen = "tank" + name = "Computer" + desc = "Control atmospheric systems, remotely." + var/frequency = 1439 + var/list/sensors = list() + var/list/sensor_information = list() + var/datum/radio_frequency/radio_connection + circuit = /obj/item/weapon/circuitboard/air_management + +/obj/machinery/computer/general_air_control/Destroy() + if(radio_controller) + radio_controller.remove_object(src, frequency) + ..() + +/obj/machinery/computer/general_air_control/attack_hand(mob/user) + if(..(user)) + return + + tgui_interact(user) + +/obj/machinery/computer/general_air_control/receive_signal(datum/signal/signal) + if(!signal || signal.encryption) return + + var/id_tag = signal.data["tag"] + if(!id_tag || !sensors.Find(id_tag)) return + + sensor_information[id_tag] = signal.data + +/obj/machinery/computer/general_air_control/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "GeneralAtmoControl", name) + ui.open() + +/obj/machinery/computer/general_air_control/tgui_data(mob/user) + var/list/data = list() + var/sensors_ui[0] + if(sensors.len) + for(var/id_tag in sensors) + var/long_name = sensors[id_tag] + var/list/sensor_data = sensor_information[id_tag] + sensors_ui[++sensors_ui.len] = list("long_name" = long_name, "sensor_data" = sensor_data) + else + sensors_ui = null + + data["sensors"] = sensors_ui + + return data + +/obj/machinery/computer/general_air_control/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/computer/general_air_control/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/computer/general_air_control/large_tank_control + icon = 'icons/obj/computer.dmi' + frequency = 1441 + var/input_tag + var/output_tag + var/list/input_info + var/list/output_info + var/input_flow_setting = 200 + var/pressure_setting = ONE_ATMOSPHERE * 45 + circuit = /obj/item/weapon/circuitboard/air_management/tank_control + +/obj/machinery/computer/general_air_control/large_tank_control/tgui_data(mob/user) + var/list/data = ..() + + data["tanks"] = 1 + + if(input_info) + data["input_info"] = list("power" = input_info["power"], "volume_rate" = round(input_info["volume_rate"], 0.1)) + else + data["input_info"] = null + + if(output_info) + data["output_info"] = list("power" = output_info["power"], "output_pressure" = output_info["internal"]) + else + data["output_info"] = null + + data["input_flow_setting"] = round(input_flow_setting, 0.1) + data["pressure_setting"] = pressure_setting + data["max_pressure"] = 50*ONE_ATMOSPHERE + data["max_flowrate"] = ATMOS_DEFAULT_VOLUME_PUMP + 500 + + return data + +/obj/machinery/computer/general_air_control/large_tank_control/receive_signal(datum/signal/signal) + if(!signal || signal.encryption) return + + var/id_tag = signal.data["tag"] + + if(input_tag == id_tag) + input_info = signal.data + else if(output_tag == id_tag) + output_info = signal.data + else + ..(signal) + +/obj/machinery/computer/general_air_control/large_tank_control/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("adj_pressure") + var/new_pressure = text2num(params["adj_pressure"]) + pressure_setting = between(0, new_pressure, 50*ONE_ATMOSPHERE) + return TRUE + + if("adj_input_flow_rate") + var/new_flow = text2num(params["adj_input_flow_rate"]) + input_flow_setting = between(0, new_flow, ATMOS_DEFAULT_VOLUME_PUMP + 500) //default flow rate limit for air injectors + return TRUE + + if(!radio_connection) + return FALSE + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + switch(action) + if("in_refresh_status") + input_info = null + signal.data = list ("tag" = input_tag, "status" = 1) + . = TRUE + + if("in_toggle_injector") + input_info = null + signal.data = list ("tag" = input_tag, "power_toggle" = 1) + . = TRUE + + if("in_set_flowrate") + input_info = null + signal.data = list ("tag" = input_tag, "set_volume_rate" = "[input_flow_setting]") + . = TRUE + + if("out_refresh_status") + output_info = null + signal.data = list ("tag" = output_tag, "status" = 1) + . = TRUE + + if("out_toggle_power") + output_info = null + signal.data = list ("tag" = output_tag, "power_toggle" = 1) + . = TRUE + + if("out_set_pressure") + output_info = null + signal.data = list ("tag" = output_tag, "set_internal_pressure" = "[pressure_setting]") + . = TRUE + + signal.data["sigtype"]="command" + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + +/obj/machinery/computer/general_air_control/supermatter_core + icon = 'icons/obj/computer.dmi' + frequency = 1438 + var/input_tag + var/output_tag + var/list/input_info + var/list/output_info + var/input_flow_setting = 700 + var/pressure_setting = 100 + circuit = /obj/item/weapon/circuitboard/air_management/supermatter_core + +/obj/machinery/computer/general_air_control/supermatter_core/tgui_data(mob/user) + var/list/data = ..() + data["core"] = 1 + + if(input_info) + data["input_info"] = list("power" = input_info["power"], "volume_rate" = round(input_info["volume_rate"], 0.1)) + else + data["input_info"] = null + + if(output_info) + // Yes, TECHNICALLY this is not output pressure, it's a pressure LIMIT. HOWEVER. The fact that the UI uses "output_pressure" + // in EXACTLY THE SAME WAY as "pressure_limit" means this should just pass it as the other fucking data argument because holy shit what the + // fuck + data["output_info"] = list("power" = output_info["power"], "output_pressure" = output_info["external"]) + else + data["output_info"] = null + + data["input_flow_setting"] = round(input_flow_setting, 0.1) + data["pressure_setting"] = pressure_setting + data["max_pressure"] = 10*ONE_ATMOSPHERE + data["max_flowrate"] = ATMOS_DEFAULT_VOLUME_PUMP + 500 + + return data + +/obj/machinery/computer/general_air_control/supermatter_core/receive_signal(datum/signal/signal) + if(!signal || signal.encryption) return + + var/id_tag = signal.data["tag"] + + if(input_tag == id_tag) + input_info = signal.data + else if(output_tag == id_tag) + output_info = signal.data + else + ..(signal) + +/obj/machinery/computer/general_air_control/supermatter_core/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("adj_pressure") + var/new_pressure = text2num(params["adj_pressure"]) + pressure_setting = between(0, new_pressure, 10*ONE_ATMOSPHERE) + return TRUE + + if("adj_input_flow_rate") + var/new_flow = text2num(params["adj_input_flow_rate"]) + input_flow_setting = between(0, new_flow, ATMOS_DEFAULT_VOLUME_PUMP + 500) //default flow rate limit for air injectors + return TRUE + + if(!radio_connection) + return FALSE + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + switch(action) + if("in_refresh_status") + input_info = null + signal.data = list ("tag" = input_tag, "status" = 1) + . = TRUE + + if("in_toggle_injector") + input_info = null + signal.data = list ("tag" = input_tag, "power_toggle" = 1) + . = TRUE + + if("in_set_flowrate") + input_info = null + signal.data = list ("tag" = input_tag, "set_volume_rate" = "[input_flow_setting]") + . = TRUE + + if("out_refresh_status") + output_info = null + signal.data = list ("tag" = output_tag, "status" = 1) + . = TRUE + + if("out_toggle_power") + output_info = null + signal.data = list ("tag" = output_tag, "power_toggle" = 1) + . = TRUE + + if("out_set_pressure") + output_info = null + signal.data = list ("tag" = output_tag, "set_external_pressure" = "[pressure_setting]", "checks" = 1) + . = TRUE + + signal.data["sigtype"]="command" + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + +/obj/machinery/computer/general_air_control/fuel_injection + icon = 'icons/obj/computer.dmi' + icon_screen = "alert:0" + var/device_tag + var/list/device_info + var/automation = 0 + var/cutoff_temperature = 2000 + var/on_temperature = 1200 + circuit = /obj/item/weapon/circuitboard/air_management/injector_control + +/obj/machinery/computer/general_air_control/fuel_injection/process() + if(automation) + if(!radio_connection) + return FALSE + + var/injecting = 0 + for(var/id_tag in sensor_information) + var/list/data = sensor_information[id_tag] + if(data["temperature"]) + if(data["temperature"] >= cutoff_temperature) + injecting = 0 + break + if(data["temperature"] <= on_temperature) + injecting = 1 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + + signal.data = list( + "tag" = device_tag, + "power" = injecting, + "sigtype"="command" + ) + + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + + ..() + +/obj/machinery/computer/general_air_control/fuel_injection/tgui_data(mob/user) + var/list/data = ..() + data["fuel"] = 1 + data["automation"] = automation + + if(device_info) + data["device_info"] = list("power" = device_info["power"], "volume_rate" = device_info["volume_rate"]) + else + data["device_info"] = null + + return data + +/obj/machinery/computer/general_air_control/fuel_injection/receive_signal(datum/signal/signal) + if(!signal || signal.encryption) return + + var/id_tag = signal.data["tag"] + + if(device_tag == id_tag) + device_info = signal.data + else + ..(signal) + +/obj/machinery/computer/general_air_control/fuel_injection/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("refresh_status") + device_info = null + if(!radio_connection) + return FALSE + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + signal.data = list( + "tag" = device_tag, + "status" = 1, + "sigtype"="command" + ) + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + . = TRUE + + if("toggle_automation") + automation = !automation + . = TRUE + + if("toggle_injector") + device_info = null + if(!radio_connection) + return FALSE + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + signal.data = list( + "tag" = device_tag, + "power_toggle" = 1, + "sigtype"="command" + ) + + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + . = TRUE + + if("injection") + if(!radio_connection) + return FALSE + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + signal.data = list( + "tag" = device_tag, + "inject" = 1, + "sigtype"="command" + ) + + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) . = TRUE \ No newline at end of file diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index a7f8a40eeb7..3fb8adccfd4 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -1,471 +1,471 @@ -/obj/machinery/portable_atmospherics/canister - name = "canister" - icon = 'icons/obj/atmos.dmi' - icon_state = "yellow" - density = TRUE - var/health = 100.0 - w_class = ITEMSIZE_HUGE - - layer = TABLE_LAYER // Above catwalks, hopefully below other things - - var/valve_open = 0 - var/release_pressure = ONE_ATMOSPHERE - var/release_flow_rate = ATMOS_DEFAULT_VOLUME_PUMP //in L/s - - var/canister_color = "yellow" - var/can_label = 1 - start_pressure = 45 * ONE_ATMOSPHERE - pressure_resistance = 7 * ONE_ATMOSPHERE - var/temperature_resistance = 1000 + T0C - volume = 1000 - use_power = USE_POWER_OFF - interact_offline = 1 // Allows this to be used when not in powered area. - var/release_log = "" - var/update_flag = 0 - -/obj/machinery/portable_atmospherics/canister/drain_power() - return -1 - -/obj/machinery/portable_atmospherics/canister/nitrous_oxide - name = "Canister: \[N2O\]" - icon_state = "redws" - canister_color = "redws" - can_label = 0 - -/obj/machinery/portable_atmospherics/canister/nitrogen - name = "Canister: \[N2\]" - icon_state = "red" - canister_color = "red" - can_label = 0 - -/obj/machinery/portable_atmospherics/canister/oxygen - name = "Canister: \[O2\]" - icon_state = "blue" - canister_color = "blue" - can_label = 0 - -/obj/machinery/portable_atmospherics/canister/oxygen/prechilled - name = "Canister: \[O2 (Cryo)\]" - -/obj/machinery/portable_atmospherics/canister/phoron - name = "Canister \[Phoron\]" - icon_state = "orangeps" - canister_color = "orangeps" - can_label = 0 - -/obj/machinery/portable_atmospherics/canister/carbon_dioxide - name = "Canister \[CO2\]" - icon_state = "black" - canister_color = "black" - can_label = 0 - -/obj/machinery/portable_atmospherics/canister/air - name = "Canister \[Air\]" - icon_state = "grey" - canister_color = "grey" - can_label = 0 - -/obj/machinery/portable_atmospherics/canister/air/airlock - start_pressure = 3 * ONE_ATMOSPHERE - -/obj/machinery/portable_atmospherics/canister/empty/ - start_pressure = 0 - can_label = 1 - -/obj/machinery/portable_atmospherics/canister/empty/oxygen - name = "Canister: \[O2\]" - icon_state = "blue" - canister_color = "blue" -/obj/machinery/portable_atmospherics/canister/empty/phoron - name = "Canister \[Phoron\]" - icon_state = "orangeps" - canister_color = "orangeps" -/obj/machinery/portable_atmospherics/canister/empty/nitrogen - name = "Canister \[N2\]" - icon_state = "red" - canister_color = "red" -/obj/machinery/portable_atmospherics/canister/empty/carbon_dioxide - name = "Canister \[CO2\]" - icon_state = "black" - canister_color = "black" -/obj/machinery/portable_atmospherics/canister/empty/nitrous_oxide - name = "Canister \[N2O\]" - icon_state = "redws" - canister_color = "redws" - - - - -/obj/machinery/portable_atmospherics/canister/proc/check_change() - var/old_flag = update_flag - update_flag = 0 - if(holding) - update_flag |= 1 - if(connected_port) - update_flag |= 2 - - var/tank_pressure = air_contents.return_pressure() - if(tank_pressure < 10) - update_flag |= 4 - else if(tank_pressure < ONE_ATMOSPHERE) - update_flag |= 8 - else if(tank_pressure < 15*ONE_ATMOSPHERE) - update_flag |= 16 - else - update_flag |= 32 - - if(update_flag == old_flag) - return 1 - else - return 0 - -/obj/machinery/portable_atmospherics/canister/update_icon() -/* -update_flag -1 = holding -2 = connected_port -4 = tank_pressure < 10 -8 = tank_pressure < ONE_ATMOS -16 = tank_pressure < 15*ONE_ATMOS -32 = tank_pressure go boom. -*/ - - if (src.destroyed) - src.overlays = 0 - src.icon_state = text("[]-1", src.canister_color) - return - - if(icon_state != "[canister_color]") - icon_state = "[canister_color]" - - if(check_change()) //Returns 1 if no change needed to icons. - return - - cut_overlays() - - if(update_flag & 1) - add_overlay("can-open") - if(update_flag & 2) - add_overlay("can-connector") - if(update_flag & 4) - add_overlay("can-o0") - if(update_flag & 8) - add_overlay("can-o1") - else if(update_flag & 16) - add_overlay("can-o2") - else if(update_flag & 32) - add_overlay("can-o3") - return - -/obj/machinery/portable_atmospherics/canister/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature > temperature_resistance) - health -= 5 - healthcheck() - -/obj/machinery/portable_atmospherics/canister/proc/healthcheck() - if(destroyed) - return 1 - - if (src.health <= 10) - var/atom/location = src.loc - location.assume_air(air_contents) - - src.destroyed = 1 - playsound(src, 'sound/effects/spray.ogg', 10, 1, -3) - src.density = FALSE - update_icon() - - if (src.holding) - src.holding.loc = src.loc - src.holding = null - - return 1 - else - return 1 - -/obj/machinery/portable_atmospherics/canister/process() - if (destroyed) - return - - ..() - - if(valve_open) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = loc.return_air() - - var/env_pressure = environment.return_pressure() - var/pressure_delta = release_pressure - env_pressure - - if((air_contents.temperature > 0) && (pressure_delta > 0)) - var/transfer_moles = calculate_transfer_moles(air_contents, environment, pressure_delta) - transfer_moles = min(transfer_moles, (release_flow_rate/air_contents.volume)*air_contents.total_moles) //flow rate limit - - var/returnval = pump_gas_passive(src, air_contents, environment, transfer_moles) - if(returnval >= 0) - src.update_icon() - - if(air_contents.return_pressure() < 1) - can_label = 1 - else - can_label = 0 - - air_contents.react() //cooking up air cans - add phoron and oxygen, then heat above PHORON_MINIMUM_BURN_TEMPERATURE - -/obj/machinery/portable_atmospherics/canister/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/canister/proc/return_temperature() - var/datum/gas_mixture/GM = src.return_air() - if(GM && GM.volume>0) - return GM.temperature - return 0 - -/obj/machinery/portable_atmospherics/canister/proc/return_pressure() - var/datum/gas_mixture/GM = src.return_air() - if(GM && GM.volume>0) - return GM.return_pressure() - return 0 - -/obj/machinery/portable_atmospherics/canister/bullet_act(var/obj/item/projectile/Proj) - if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN)) - return - - if(Proj.damage) - src.health -= round(Proj.damage / 2) - healthcheck() - ..() - -/obj/machinery/portable_atmospherics/canister/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if(W.has_tool_quality(TOOL_WELDER)) //Vorestart: Deconstructable Canisters - var/obj/item/weapon/weldingtool/WT = W.get_welder() - if(!WT.remove_fuel(0, user)) - to_chat(user, "The welding tool must be on to complete this task.") - return - if(air_contents.return_pressure() > 1 && !destroyed) // Empty or broken cans are able to be deconstructed - to_chat(user, "\The [src]'s internal pressure is too high! Empty the canister before attempting to weld it apart.") - return - playsound(src, WT.usesound, 50, 1) - if(do_after(user, 20 * WT.toolspeed)) - if(!src || !WT.isOn()) return - to_chat(user, "You deconstruct the [src].") - new /obj/item/stack/material/steel( src.loc, 10) - qdel(src) - return - //Voreend - if(!W.has_tool_quality(TOOL_WRENCH) && !istype(W, /obj/item/weapon/tank) && !istype(W, /obj/item/device/analyzer) && !istype(W, /obj/item/device/pda)) - visible_message("\The [user] hits \the [src] with \a [W]!") - src.health -= W.force - src.add_fingerprint(user) - healthcheck() - - if(istype(user, /mob/living/silicon/robot) && istype(W, /obj/item/weapon/tank/jetpack)) - var/datum/gas_mixture/thejetpack = W:air_contents - var/env_pressure = thejetpack.return_pressure() - var/pressure_delta = min(10*ONE_ATMOSPHERE - env_pressure, (air_contents.return_pressure() - env_pressure)/2) - //Can not have a pressure delta that would cause environment pressure > tank pressure - var/transfer_moles = 0 - if((air_contents.temperature > 0) && (pressure_delta > 0)) - transfer_moles = pressure_delta*thejetpack.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)//Actually transfer the gas - var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) - thejetpack.merge(removed) - to_chat(user, "You pulse-pressurize your jetpack from the tank.") - return - - ..() - - SStgui.update_uis(src) // Update all NanoUIs attached to src - -/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob) - return tgui_interact(user) - -/obj/machinery/portable_atmospherics/canister/tgui_state(mob/user) - return GLOB.tgui_physical_state - -/obj/machinery/portable_atmospherics/canister/tgui_interact(mob/user, datum/tgui/ui) - if(destroyed) - return - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Canister", name) - ui.open() - -/obj/machinery/portable_atmospherics/canister/tgui_data(mob/user) - var/list/data = list() - data["can_relabel"] = can_label ? 1 : 0 - data["connected"] = connected_port ? 1 : 0 - data["pressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - data["releasePressure"] = round(release_pressure ? release_pressure : 0) - data["defaultReleasePressure"] = round(initial(release_pressure)) - data["minReleasePressure"] = round(ONE_ATMOSPHERE/10) - data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE) - data["valveOpen"] = valve_open ? 1 : 0 - - if(holding) - data["holding"] = list() - data["holding"]["name"] = holding.name - data["holding"]["pressure"] = round(holding.air_contents.return_pressure()) - else - data["holding"] = null - - return data - -/obj/machinery/portable_atmospherics/canister/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("relabel") - if(can_label) - var/list/colors = list(\ - "\[N2O\]" = "redws", \ - "\[N2\]" = "red", \ - "\[O2\]" = "blue", \ - "\[Phoron\]" = "orangeps", \ - "\[CO2\]" = "black", \ - "\[Air\]" = "grey", \ - "\[CAUTION\]" = "yellow", \ - ) - var/label = tgui_input_list(usr, "Choose canister label", "Gas canister", colors) - if(label) - canister_color = colors[label] - icon_state = colors[label] - name = "Canister: [label]" - if("pressure") - var/pressure = params["pressure"] - if(pressure == "reset") - pressure = initial(release_pressure) - . = TRUE - else if(pressure == "min") - pressure = ONE_ATMOSPHERE/10 - . = TRUE - else if(pressure == "max") - pressure = 10*ONE_ATMOSPHERE - . = TRUE - else if(pressure == "input") - pressure = tgui_input_number(usr, "New release pressure ([ONE_ATMOSPHERE/10]-[10*ONE_ATMOSPHERE] kPa):", name, release_pressure, 10*ONE_ATMOSPHERE, ONE_ATMOSPHERE/10) - if(!isnull(pressure) && !..()) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - release_pressure = clamp(round(pressure), ONE_ATMOSPHERE/10, 10*ONE_ATMOSPHERE) - if("valve") - if(valve_open) - if(holding) - release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the [holding]
    " - else - release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the air
    " - else - if(holding) - release_log += "Valve was opened by [usr] ([usr.ckey]), starting the transfer into the [holding]
    " - else - release_log += "Valve was opened by [usr] ([usr.ckey]), starting the transfer into the air
    " - log_open() - valve_open = !valve_open - . = TRUE - if("eject") - if(holding) - if(valve_open) - valve_open = 0 - release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the [holding]
    " - if(istype(holding, /obj/item/weapon/tank)) - holding.manipulated_by = usr.real_name - holding.loc = loc - holding = null - . = TRUE - - add_fingerprint(usr) - update_icon() - -/obj/machinery/portable_atmospherics/canister/phoron/New() - ..() - - src.air_contents.adjust_gas("phoron", MolesForPressure()) - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/oxygen/New() - ..() - - src.air_contents.adjust_gas("oxygen", MolesForPressure()) - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/oxygen/prechilled/New() - ..() - - src.air_contents.adjust_gas("oxygen", MolesForPressure()) - src.air_contents.temperature = 80 - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/nitrous_oxide/New() - ..() - - air_contents.adjust_gas("nitrous_oxide", MolesForPressure()) - src.update_icon() - return 1 - -//Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0 -/obj/machinery/portable_atmospherics/canister/nitrous_oxide/roomfiller/Initialize() - . = ..() - air_contents.gas["nitrous_oxide"] = 9*4000 - var/turf/simulated/location = src.loc - if (istype(src.loc)) - location.assume_air(air_contents) - air_contents = new - return 1 - -/obj/machinery/portable_atmospherics/canister/nitrogen/New() - - ..() - - src.air_contents.adjust_gas("nitrogen", MolesForPressure()) - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/carbon_dioxide/New() - ..() - src.air_contents.adjust_gas("carbon_dioxide", MolesForPressure()) - src.update_icon() - return 1 - - -/obj/machinery/portable_atmospherics/canister/air/New() - ..() - var/list/air_mix = StandardAirMix() - src.air_contents.adjust_multi("oxygen", air_mix["oxygen"], "nitrogen", air_mix["nitrogen"]) - - src.update_icon() - return 1 - -//R-UST port -// Special types used for engine setup admin verb, they contain double amount of that of normal canister. -/obj/machinery/portable_atmospherics/canister/nitrogen/engine_setup/New() - ..() - src.air_contents.adjust_gas("nitrogen", MolesForPressure()) - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/carbon_dioxide/engine_setup/New() - ..() - src.air_contents.adjust_gas("carbon_dioxide", MolesForPressure()) - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/phoron/engine_setup/New() - ..() - src.air_contents.adjust_gas("phoron", MolesForPressure()) - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/take_damage(var/damage) - src.health -= damage - healthcheck() +/obj/machinery/portable_atmospherics/canister + name = "canister" + icon = 'icons/obj/atmos.dmi' + icon_state = "yellow" + density = TRUE + var/health = 100.0 + w_class = ITEMSIZE_HUGE + + layer = TABLE_LAYER // Above catwalks, hopefully below other things + + var/valve_open = 0 + var/release_pressure = ONE_ATMOSPHERE + var/release_flow_rate = ATMOS_DEFAULT_VOLUME_PUMP //in L/s + + var/canister_color = "yellow" + var/can_label = 1 + start_pressure = 45 * ONE_ATMOSPHERE + pressure_resistance = 7 * ONE_ATMOSPHERE + var/temperature_resistance = 1000 + T0C + volume = 1000 + use_power = USE_POWER_OFF + interact_offline = 1 // Allows this to be used when not in powered area. + var/release_log = "" + var/update_flag = 0 + +/obj/machinery/portable_atmospherics/canister/drain_power() + return -1 + +/obj/machinery/portable_atmospherics/canister/nitrous_oxide + name = "Canister: \[N2O\]" + icon_state = "redws" + canister_color = "redws" + can_label = 0 + +/obj/machinery/portable_atmospherics/canister/nitrogen + name = "Canister: \[N2\]" + icon_state = "red" + canister_color = "red" + can_label = 0 + +/obj/machinery/portable_atmospherics/canister/oxygen + name = "Canister: \[O2\]" + icon_state = "blue" + canister_color = "blue" + can_label = 0 + +/obj/machinery/portable_atmospherics/canister/oxygen/prechilled + name = "Canister: \[O2 (Cryo)\]" + +/obj/machinery/portable_atmospherics/canister/phoron + name = "Canister \[Phoron\]" + icon_state = "orangeps" + canister_color = "orangeps" + can_label = 0 + +/obj/machinery/portable_atmospherics/canister/carbon_dioxide + name = "Canister \[CO2\]" + icon_state = "black" + canister_color = "black" + can_label = 0 + +/obj/machinery/portable_atmospherics/canister/air + name = "Canister \[Air\]" + icon_state = "grey" + canister_color = "grey" + can_label = 0 + +/obj/machinery/portable_atmospherics/canister/air/airlock + start_pressure = 3 * ONE_ATMOSPHERE + +/obj/machinery/portable_atmospherics/canister/empty/ + start_pressure = 0 + can_label = 1 + +/obj/machinery/portable_atmospherics/canister/empty/oxygen + name = "Canister: \[O2\]" + icon_state = "blue" + canister_color = "blue" +/obj/machinery/portable_atmospherics/canister/empty/phoron + name = "Canister \[Phoron\]" + icon_state = "orangeps" + canister_color = "orangeps" +/obj/machinery/portable_atmospherics/canister/empty/nitrogen + name = "Canister \[N2\]" + icon_state = "red" + canister_color = "red" +/obj/machinery/portable_atmospherics/canister/empty/carbon_dioxide + name = "Canister \[CO2\]" + icon_state = "black" + canister_color = "black" +/obj/machinery/portable_atmospherics/canister/empty/nitrous_oxide + name = "Canister \[N2O\]" + icon_state = "redws" + canister_color = "redws" + + + + +/obj/machinery/portable_atmospherics/canister/proc/check_change() + var/old_flag = update_flag + update_flag = 0 + if(holding) + update_flag |= 1 + if(connected_port) + update_flag |= 2 + + var/tank_pressure = air_contents.return_pressure() + if(tank_pressure < 10) + update_flag |= 4 + else if(tank_pressure < ONE_ATMOSPHERE) + update_flag |= 8 + else if(tank_pressure < 15*ONE_ATMOSPHERE) + update_flag |= 16 + else + update_flag |= 32 + + if(update_flag == old_flag) + return 1 + else + return 0 + +/obj/machinery/portable_atmospherics/canister/update_icon() +/* +update_flag +1 = holding +2 = connected_port +4 = tank_pressure < 10 +8 = tank_pressure < ONE_ATMOS +16 = tank_pressure < 15*ONE_ATMOS +32 = tank_pressure go boom. +*/ + + if (src.destroyed) + src.overlays = 0 + src.icon_state = text("[]-1", src.canister_color) + return + + if(icon_state != "[canister_color]") + icon_state = "[canister_color]" + + if(check_change()) //Returns 1 if no change needed to icons. + return + + cut_overlays() + + if(update_flag & 1) + add_overlay("can-open") + if(update_flag & 2) + add_overlay("can-connector") + if(update_flag & 4) + add_overlay("can-o0") + if(update_flag & 8) + add_overlay("can-o1") + else if(update_flag & 16) + add_overlay("can-o2") + else if(update_flag & 32) + add_overlay("can-o3") + return + +/obj/machinery/portable_atmospherics/canister/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature > temperature_resistance) + health -= 5 + healthcheck() + +/obj/machinery/portable_atmospherics/canister/proc/healthcheck() + if(destroyed) + return 1 + + if (src.health <= 10) + var/atom/location = src.loc + location.assume_air(air_contents) + + src.destroyed = 1 + playsound(src, 'sound/effects/spray.ogg', 10, 1, -3) + src.density = FALSE + update_icon() + + if (src.holding) + src.holding.loc = src.loc + src.holding = null + + return 1 + else + return 1 + +/obj/machinery/portable_atmospherics/canister/process() + if (destroyed) + return + + ..() + + if(valve_open) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = loc.return_air() + + var/env_pressure = environment.return_pressure() + var/pressure_delta = release_pressure - env_pressure + + if((air_contents.temperature > 0) && (pressure_delta > 0)) + var/transfer_moles = calculate_transfer_moles(air_contents, environment, pressure_delta) + transfer_moles = min(transfer_moles, (release_flow_rate/air_contents.volume)*air_contents.total_moles) //flow rate limit + + var/returnval = pump_gas_passive(src, air_contents, environment, transfer_moles) + if(returnval >= 0) + src.update_icon() + + if(air_contents.return_pressure() < 1) + can_label = 1 + else + can_label = 0 + + air_contents.react() //cooking up air cans - add phoron and oxygen, then heat above PHORON_MINIMUM_BURN_TEMPERATURE + +/obj/machinery/portable_atmospherics/canister/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/canister/proc/return_temperature() + var/datum/gas_mixture/GM = src.return_air() + if(GM && GM.volume>0) + return GM.temperature + return 0 + +/obj/machinery/portable_atmospherics/canister/proc/return_pressure() + var/datum/gas_mixture/GM = src.return_air() + if(GM && GM.volume>0) + return GM.return_pressure() + return 0 + +/obj/machinery/portable_atmospherics/canister/bullet_act(var/obj/item/projectile/Proj) + if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN)) + return + + if(Proj.damage) + src.health -= round(Proj.damage / 2) + healthcheck() + ..() + +/obj/machinery/portable_atmospherics/canister/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if(W.has_tool_quality(TOOL_WELDER)) //Vorestart: Deconstructable Canisters + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(!WT.remove_fuel(0, user)) + to_chat(user, "The welding tool must be on to complete this task.") + return + if(air_contents.return_pressure() > 1 && !destroyed) // Empty or broken cans are able to be deconstructed + to_chat(user, "\The [src]'s internal pressure is too high! Empty the canister before attempting to weld it apart.") + return + playsound(src, WT.usesound, 50, 1) + if(do_after(user, 20 * WT.toolspeed)) + if(!src || !WT.isOn()) return + to_chat(user, "You deconstruct the [src].") + new /obj/item/stack/material/steel( src.loc, 10) + qdel(src) + return + //Voreend + if(!W.has_tool_quality(TOOL_WRENCH) && !istype(W, /obj/item/weapon/tank) && !istype(W, /obj/item/device/analyzer) && !istype(W, /obj/item/device/pda)) + visible_message("\The [user] hits \the [src] with \a [W]!") + src.health -= W.force + src.add_fingerprint(user) + healthcheck() + + if(istype(user, /mob/living/silicon/robot) && istype(W, /obj/item/weapon/tank/jetpack)) + var/datum/gas_mixture/thejetpack = W:air_contents + var/env_pressure = thejetpack.return_pressure() + var/pressure_delta = min(10*ONE_ATMOSPHERE - env_pressure, (air_contents.return_pressure() - env_pressure)/2) + //Can not have a pressure delta that would cause environment pressure > tank pressure + var/transfer_moles = 0 + if((air_contents.temperature > 0) && (pressure_delta > 0)) + transfer_moles = pressure_delta*thejetpack.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)//Actually transfer the gas + var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) + thejetpack.merge(removed) + to_chat(user, "You pulse-pressurize your jetpack from the tank.") + return + + ..() + + SStgui.update_uis(src) // Update all NanoUIs attached to src + +/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob) + return tgui_interact(user) + +/obj/machinery/portable_atmospherics/canister/tgui_state(mob/user) + return GLOB.tgui_physical_state + +/obj/machinery/portable_atmospherics/canister/tgui_interact(mob/user, datum/tgui/ui) + if(destroyed) + return + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Canister", name) + ui.open() + +/obj/machinery/portable_atmospherics/canister/tgui_data(mob/user) + var/list/data = list() + data["can_relabel"] = can_label ? 1 : 0 + data["connected"] = connected_port ? 1 : 0 + data["pressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) + data["releasePressure"] = round(release_pressure ? release_pressure : 0) + data["defaultReleasePressure"] = round(initial(release_pressure)) + data["minReleasePressure"] = round(ONE_ATMOSPHERE/10) + data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE) + data["valveOpen"] = valve_open ? 1 : 0 + + if(holding) + data["holding"] = list() + data["holding"]["name"] = holding.name + data["holding"]["pressure"] = round(holding.air_contents.return_pressure()) + else + data["holding"] = null + + return data + +/obj/machinery/portable_atmospherics/canister/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("relabel") + if(can_label) + var/list/colors = list(\ + "\[N2O\]" = "redws", \ + "\[N2\]" = "red", \ + "\[O2\]" = "blue", \ + "\[Phoron\]" = "orangeps", \ + "\[CO2\]" = "black", \ + "\[Air\]" = "grey", \ + "\[CAUTION\]" = "yellow", \ + ) + var/label = tgui_input_list(usr, "Choose canister label", "Gas canister", colors) + if(label) + canister_color = colors[label] + icon_state = colors[label] + name = "Canister: [label]" + if("pressure") + var/pressure = params["pressure"] + if(pressure == "reset") + pressure = initial(release_pressure) + . = TRUE + else if(pressure == "min") + pressure = ONE_ATMOSPHERE/10 + . = TRUE + else if(pressure == "max") + pressure = 10*ONE_ATMOSPHERE + . = TRUE + else if(pressure == "input") + pressure = tgui_input_number(usr, "New release pressure ([ONE_ATMOSPHERE/10]-[10*ONE_ATMOSPHERE] kPa):", name, release_pressure, 10*ONE_ATMOSPHERE, ONE_ATMOSPHERE/10) + if(!isnull(pressure) && !..()) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + release_pressure = clamp(round(pressure), ONE_ATMOSPHERE/10, 10*ONE_ATMOSPHERE) + if("valve") + if(valve_open) + if(holding) + release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the [holding]
    " + else + release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the air
    " + else + if(holding) + release_log += "Valve was opened by [usr] ([usr.ckey]), starting the transfer into the [holding]
    " + else + release_log += "Valve was opened by [usr] ([usr.ckey]), starting the transfer into the air
    " + log_open() + valve_open = !valve_open + . = TRUE + if("eject") + if(holding) + if(valve_open) + valve_open = 0 + release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the [holding]
    " + if(istype(holding, /obj/item/weapon/tank)) + holding.manipulated_by = usr.real_name + holding.loc = loc + holding = null + . = TRUE + + add_fingerprint(usr) + update_icon() + +/obj/machinery/portable_atmospherics/canister/phoron/New() + ..() + + src.air_contents.adjust_gas("phoron", MolesForPressure()) + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/oxygen/New() + ..() + + src.air_contents.adjust_gas("oxygen", MolesForPressure()) + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/oxygen/prechilled/New() + ..() + + src.air_contents.adjust_gas("oxygen", MolesForPressure()) + src.air_contents.temperature = 80 + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/nitrous_oxide/New() + ..() + + air_contents.adjust_gas("nitrous_oxide", MolesForPressure()) + src.update_icon() + return 1 + +//Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0 +/obj/machinery/portable_atmospherics/canister/nitrous_oxide/roomfiller/Initialize() + . = ..() + air_contents.gas["nitrous_oxide"] = 9*4000 + var/turf/simulated/location = src.loc + if (istype(src.loc)) + location.assume_air(air_contents) + air_contents = new + return 1 + +/obj/machinery/portable_atmospherics/canister/nitrogen/New() + + ..() + + src.air_contents.adjust_gas("nitrogen", MolesForPressure()) + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/carbon_dioxide/New() + ..() + src.air_contents.adjust_gas("carbon_dioxide", MolesForPressure()) + src.update_icon() + return 1 + + +/obj/machinery/portable_atmospherics/canister/air/New() + ..() + var/list/air_mix = StandardAirMix() + src.air_contents.adjust_multi("oxygen", air_mix["oxygen"], "nitrogen", air_mix["nitrogen"]) + + src.update_icon() + return 1 + +//R-UST port +// Special types used for engine setup admin verb, they contain double amount of that of normal canister. +/obj/machinery/portable_atmospherics/canister/nitrogen/engine_setup/New() + ..() + src.air_contents.adjust_gas("nitrogen", MolesForPressure()) + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/carbon_dioxide/engine_setup/New() + ..() + src.air_contents.adjust_gas("carbon_dioxide", MolesForPressure()) + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/phoron/engine_setup/New() + ..() + src.air_contents.adjust_gas("phoron", MolesForPressure()) + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/take_damage(var/damage) + src.health -= damage + healthcheck() diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm index c8b36e61381..93988186584 100644 --- a/code/game/machinery/atmoalter/meter.dm +++ b/code/game/machinery/atmoalter/meter.dm @@ -1,139 +1,139 @@ -/obj/machinery/meter - name = "meter" - desc = "It measures something." - icon = 'icons/obj/meter_vr.dmi' - icon_state = "meterX" - var/obj/machinery/atmospherics/pipe/target = null - var/list/pipes_on_turf = list() - anchored = TRUE - power_channel = ENVIRON - var/frequency = 0 - var/id - use_power = USE_POWER_IDLE - idle_power_usage = 15 - -/obj/machinery/meter/Initialize() - . = ..() - if (!target) - target = select_target() - -/obj/machinery/meter/Destroy() - pipes_on_turf.Cut() - target = null - return ..() - -/obj/machinery/meter/proc/select_target() - var/obj/machinery/atmospherics/pipe/P - for(P in loc) - if(!P.hides_under_flooring()) - break - if(!P) - P = locate(/obj/machinery/atmospherics/pipe) in loc - return P - -/obj/machinery/meter/process() - if(!target) - icon_state = "meterX" - return 0 - - if(stat & (BROKEN|NOPOWER)) - icon_state = "meter0" - return 0 - - var/datum/gas_mixture/environment = target.return_air() - if(!environment) - icon_state = "meterX" - return 0 - - var/env_pressure = environment.return_pressure() - if(env_pressure <= 0.15*ONE_ATMOSPHERE) - icon_state = "meter0" - else if(env_pressure <= 1.8*ONE_ATMOSPHERE) - var/val = round(env_pressure/(ONE_ATMOSPHERE*0.3) + 0.5) - icon_state = "meter1_[val]" - else if(env_pressure <= 30*ONE_ATMOSPHERE) - var/val = round(env_pressure/(ONE_ATMOSPHERE*5)-0.35) + 1 - icon_state = "meter2_[val]" - else if(env_pressure <= 59*ONE_ATMOSPHERE) - var/val = round(env_pressure/(ONE_ATMOSPHERE*5) - 6) + 1 - icon_state = "meter3_[val]" - else - icon_state = "meter4" - - if(frequency) - var/datum/radio_frequency/radio_connection = radio_controller.return_frequency(frequency) - - if(!radio_connection) return - - var/datum/signal/signal = new - signal.source = src - signal.transmission_method = TRANSMISSION_RADIO - signal.data = list( - "tag" = id, - "device" = "AM", - "pressure" = round(env_pressure), - "sigtype" = "status" - ) - radio_connection.post_signal(src, signal) - -/obj/machinery/meter/examine(mob/user) - . = ..() - - if(get_dist(user, src) > 3 && !(istype(user, /mob/living/silicon/ai) || istype(user, /mob/observer/dead))) - . += "You are too far away to read it." - - else if(stat & (NOPOWER|BROKEN)) - . += "The display is off." - - else if(target) - var/datum/gas_mixture/environment = target.return_air() - if(environment) - . += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]K ([round(environment.temperature-T0C,0.01)]°C)" - else - . += "The sensor error light is blinking." - else - . += "The connect error light is blinking." - -/obj/machinery/meter/Click() - - if(istype(usr, /mob/living/carbon/human) || istype(usr, /mob/living/silicon/ai)) // ghosts can call ..() for examine - var/mob/living/L = usr - if(!L.get_active_hand() || !L.Adjacent(src)) - usr.examinate(src) - return 1 - - return ..() - -/obj/machinery/meter/attackby(var/obj/item/W, var/mob/user) - if(W.has_tool_quality(TOOL_WRENCH)) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if(do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear ratchet.") - new /obj/item/pipe_meter(get_turf(src)) - qdel(src) - return - - if(istype(W, /obj/item/device/multitool)) - for(var/obj/machinery/atmospherics/pipe/P in loc) - pipes_on_turf |= P - if(!pipes_on_turf.len) - return - target = pipes_on_turf[1] - pipes_on_turf.Remove(target) - pipes_on_turf.Add(target) - to_chat(user, "Pipe meter set to moniter \the [target].") - return - - return ..() - -// TURF METER - REPORTS A TILE'S AIR CONTENTS - -/obj/machinery/meter/turf/select_target() - return loc - -/obj/machinery/meter/turf/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - return +/obj/machinery/meter + name = "meter" + desc = "It measures something." + icon = 'icons/obj/meter_vr.dmi' + icon_state = "meterX" + var/obj/machinery/atmospherics/pipe/target = null + var/list/pipes_on_turf = list() + anchored = TRUE + power_channel = ENVIRON + var/frequency = 0 + var/id + use_power = USE_POWER_IDLE + idle_power_usage = 15 + +/obj/machinery/meter/Initialize() + . = ..() + if (!target) + target = select_target() + +/obj/machinery/meter/Destroy() + pipes_on_turf.Cut() + target = null + return ..() + +/obj/machinery/meter/proc/select_target() + var/obj/machinery/atmospherics/pipe/P + for(P in loc) + if(!P.hides_under_flooring()) + break + if(!P) + P = locate(/obj/machinery/atmospherics/pipe) in loc + return P + +/obj/machinery/meter/process() + if(!target) + icon_state = "meterX" + return 0 + + if(stat & (BROKEN|NOPOWER)) + icon_state = "meter0" + return 0 + + var/datum/gas_mixture/environment = target.return_air() + if(!environment) + icon_state = "meterX" + return 0 + + var/env_pressure = environment.return_pressure() + if(env_pressure <= 0.15*ONE_ATMOSPHERE) + icon_state = "meter0" + else if(env_pressure <= 1.8*ONE_ATMOSPHERE) + var/val = round(env_pressure/(ONE_ATMOSPHERE*0.3) + 0.5) + icon_state = "meter1_[val]" + else if(env_pressure <= 30*ONE_ATMOSPHERE) + var/val = round(env_pressure/(ONE_ATMOSPHERE*5)-0.35) + 1 + icon_state = "meter2_[val]" + else if(env_pressure <= 59*ONE_ATMOSPHERE) + var/val = round(env_pressure/(ONE_ATMOSPHERE*5) - 6) + 1 + icon_state = "meter3_[val]" + else + icon_state = "meter4" + + if(frequency) + var/datum/radio_frequency/radio_connection = radio_controller.return_frequency(frequency) + + if(!radio_connection) return + + var/datum/signal/signal = new + signal.source = src + signal.transmission_method = TRANSMISSION_RADIO + signal.data = list( + "tag" = id, + "device" = "AM", + "pressure" = round(env_pressure), + "sigtype" = "status" + ) + radio_connection.post_signal(src, signal) + +/obj/machinery/meter/examine(mob/user) + . = ..() + + if(get_dist(user, src) > 3 && !(istype(user, /mob/living/silicon/ai) || istype(user, /mob/observer/dead))) + . += "You are too far away to read it." + + else if(stat & (NOPOWER|BROKEN)) + . += "The display is off." + + else if(target) + var/datum/gas_mixture/environment = target.return_air() + if(environment) + . += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]K ([round(environment.temperature-T0C,0.01)]°C)" + else + . += "The sensor error light is blinking." + else + . += "The connect error light is blinking." + +/obj/machinery/meter/Click() + + if(istype(usr, /mob/living/carbon/human) || istype(usr, /mob/living/silicon/ai)) // ghosts can call ..() for examine + var/mob/living/L = usr + if(!L.get_active_hand() || !L.Adjacent(src)) + usr.examinate(src) + return 1 + + return ..() + +/obj/machinery/meter/attackby(var/obj/item/W, var/mob/user) + if(W.has_tool_quality(TOOL_WRENCH)) + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if(do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear ratchet.") + new /obj/item/pipe_meter(get_turf(src)) + qdel(src) + return + + if(istype(W, /obj/item/device/multitool)) + for(var/obj/machinery/atmospherics/pipe/P in loc) + pipes_on_turf |= P + if(!pipes_on_turf.len) + return + target = pipes_on_turf[1] + pipes_on_turf.Remove(target) + pipes_on_turf.Add(target) + to_chat(user, "Pipe meter set to moniter \the [target].") + return + + return ..() + +// TURF METER - REPORTS A TILE'S AIR CONTENTS + +/obj/machinery/meter/turf/select_target() + return loc + +/obj/machinery/meter/turf/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + return diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index a07d0054c3f..c3fe8eaccfc 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -1,201 +1,201 @@ -/obj/machinery/portable_atmospherics - name = "atmoalter" - use_power = USE_POWER_OFF - layer = OBJ_LAYER // These are mobile, best not be under everything. - var/datum/gas_mixture/air_contents = new - - var/obj/machinery/atmospherics/portables_connector/connected_port - var/obj/item/weapon/tank/holding - - var/volume = 0 - var/destroyed = 0 - - var/start_pressure = ONE_ATMOSPHERE - var/maximum_pressure = 90 * ONE_ATMOSPHERE - -/obj/machinery/portable_atmospherics/New() - ..() - //VOREStation Edit - Fix runtime - if(air_contents) - air_contents.volume = volume - air_contents.temperature = T20C - //VOREStation Edit End - - return 1 - -/obj/machinery/portable_atmospherics/Initialize() - ..() - return INITIALIZE_HINT_LATELOAD - -/obj/machinery/portable_atmospherics/LateInitialize() - var/obj/machinery/atmospherics/portables_connector/port = locate() in loc - if(port) - connect(port) - update_icon() - -/obj/machinery/portable_atmospherics/Destroy() - QDEL_NULL(air_contents) - QDEL_NULL(holding) - return ..() - -/obj/machinery/portable_atmospherics/process() - if(!connected_port) //only react when pipe_network will ont it do it for you - //Allow for reactions - air_contents.react() - else - update_icon() - -/obj/machinery/portable_atmospherics/blob_act() - qdel(src) - -/obj/machinery/portable_atmospherics/proc/StandardAirMix() - return list( - "oxygen" = O2STANDARD * MolesForPressure(), - "nitrogen" = N2STANDARD * MolesForPressure()) - -/obj/machinery/portable_atmospherics/proc/MolesForPressure(var/target_pressure = start_pressure) - return (target_pressure * air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - -/obj/machinery/portable_atmospherics/update_icon() - return null - -/obj/machinery/portable_atmospherics/proc/connect(obj/machinery/atmospherics/portables_connector/new_port) - //Make sure not already connected to something else - if(connected_port || !new_port || new_port.connected_device) - return 0 - - //Make sure are close enough for a valid connection - if(new_port.loc != loc) - return 0 - - //Perform the connection - connected_port = new_port - connected_port.connected_device = src - connected_port.on = 1 //Activate port updates - - anchored = TRUE //Prevent movement - - //Actually enforce the air sharing - var/datum/pipe_network/network = connected_port.return_network(src) - if(network && !network.gases.Find(air_contents)) - network.gases += air_contents - network.update = 1 - - return 1 - -/obj/machinery/portable_atmospherics/proc/disconnect() - if(!connected_port) - return 0 - - var/datum/pipe_network/network = connected_port.return_network(src) - if(network) - network.gases -= air_contents - - anchored = FALSE - - connected_port.connected_device = null - connected_port = null - - return 1 - -/obj/machinery/portable_atmospherics/proc/update_connected_network() - if(!connected_port) - return - - var/datum/pipe_network/network = connected_port.return_network(src) - if (network) - network.update = 1 - -/obj/machinery/portable_atmospherics/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if ((istype(W, /obj/item/weapon/tank) && !( src.destroyed ))) - if (src.holding) - return - var/obj/item/weapon/tank/T = W - user.drop_item() - T.loc = src - src.holding = T - update_icon() - return - - else if (W.has_tool_quality(TOOL_WRENCH)) - if(connected_port) - disconnect() - to_chat(user, "You disconnect \the [src] from the port.") - update_icon() - playsound(src, W.usesound, 50, 1) - return - else - var/obj/machinery/atmospherics/portables_connector/possible_port = locate(/obj/machinery/atmospherics/portables_connector/) in loc - if(possible_port) - if(connect(possible_port)) - to_chat(user, "You connect \the [src] to the port.") - update_icon() - playsound(src, W.usesound, 50, 1) - return - else - to_chat(user, "\The [src] failed to connect to the port.") - return - else - to_chat(user, "Nothing happens.") - return - return - - - -/obj/machinery/portable_atmospherics/powered - var/power_rating - var/power_losses - var/last_power_draw = 0 - var/obj/item/weapon/cell/cell - var/use_cell = TRUE - var/removeable_cell = TRUE - -/obj/machinery/portable_atmospherics/powered/powered() - if(use_power) //using area power - return ..() - if(cell && cell.charge) - return 1 - return 0 - -/obj/machinery/portable_atmospherics/powered/attackby(obj/item/I, mob/user) - if(use_cell && istype(I, /obj/item/weapon/cell)) - if(cell) - to_chat(user, "There is already a power cell installed.") - return - - var/obj/item/weapon/cell/C = I - - user.drop_item() - C.add_fingerprint(user) - cell = C - C.loc = src - user.visible_message("[user] opens the panel on [src] and inserts [C].", "You open the panel on [src] and insert [C].") - power_change() - return - - if(I.has_tool_quality(TOOL_SCREWDRIVER) && removeable_cell) - if(!cell) - to_chat(user, "There is no power cell installed.") - return - - user.visible_message("[user] opens the panel on [src] and removes [cell].", "You open the panel on [src] and remove [cell].") - playsound(src, I.usesound, 50, 1) - cell.add_fingerprint(user) - cell.loc = src.loc - cell = null - power_change() - return - ..() - -/obj/machinery/portable_atmospherics/proc/log_open() - if(air_contents.gas.len == 0) - return - - var/gases = "" - for(var/gas in air_contents.gas) - if(gases) - gases += ", [gas]" - else - gases = gas - log_admin("[usr] ([usr.ckey]) opened '[src.name]' containing [gases].") - message_admins("[usr] ([usr.ckey]) opened '[src.name]' containing [gases].") +/obj/machinery/portable_atmospherics + name = "atmoalter" + use_power = USE_POWER_OFF + layer = OBJ_LAYER // These are mobile, best not be under everything. + var/datum/gas_mixture/air_contents = new + + var/obj/machinery/atmospherics/portables_connector/connected_port + var/obj/item/weapon/tank/holding + + var/volume = 0 + var/destroyed = 0 + + var/start_pressure = ONE_ATMOSPHERE + var/maximum_pressure = 90 * ONE_ATMOSPHERE + +/obj/machinery/portable_atmospherics/New() + ..() + //VOREStation Edit - Fix runtime + if(air_contents) + air_contents.volume = volume + air_contents.temperature = T20C + //VOREStation Edit End + + return 1 + +/obj/machinery/portable_atmospherics/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/portable_atmospherics/LateInitialize() + var/obj/machinery/atmospherics/portables_connector/port = locate() in loc + if(port) + connect(port) + update_icon() + +/obj/machinery/portable_atmospherics/Destroy() + QDEL_NULL(air_contents) + QDEL_NULL(holding) + return ..() + +/obj/machinery/portable_atmospherics/process() + if(!connected_port) //only react when pipe_network will ont it do it for you + //Allow for reactions + air_contents.react() + else + update_icon() + +/obj/machinery/portable_atmospherics/blob_act() + qdel(src) + +/obj/machinery/portable_atmospherics/proc/StandardAirMix() + return list( + "oxygen" = O2STANDARD * MolesForPressure(), + "nitrogen" = N2STANDARD * MolesForPressure()) + +/obj/machinery/portable_atmospherics/proc/MolesForPressure(var/target_pressure = start_pressure) + return (target_pressure * air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature) + +/obj/machinery/portable_atmospherics/update_icon() + return null + +/obj/machinery/portable_atmospherics/proc/connect(obj/machinery/atmospherics/portables_connector/new_port) + //Make sure not already connected to something else + if(connected_port || !new_port || new_port.connected_device) + return 0 + + //Make sure are close enough for a valid connection + if(new_port.loc != loc) + return 0 + + //Perform the connection + connected_port = new_port + connected_port.connected_device = src + connected_port.on = 1 //Activate port updates + + anchored = TRUE //Prevent movement + + //Actually enforce the air sharing + var/datum/pipe_network/network = connected_port.return_network(src) + if(network && !network.gases.Find(air_contents)) + network.gases += air_contents + network.update = 1 + + return 1 + +/obj/machinery/portable_atmospherics/proc/disconnect() + if(!connected_port) + return 0 + + var/datum/pipe_network/network = connected_port.return_network(src) + if(network) + network.gases -= air_contents + + anchored = FALSE + + connected_port.connected_device = null + connected_port = null + + return 1 + +/obj/machinery/portable_atmospherics/proc/update_connected_network() + if(!connected_port) + return + + var/datum/pipe_network/network = connected_port.return_network(src) + if (network) + network.update = 1 + +/obj/machinery/portable_atmospherics/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if ((istype(W, /obj/item/weapon/tank) && !( src.destroyed ))) + if (src.holding) + return + var/obj/item/weapon/tank/T = W + user.drop_item() + T.loc = src + src.holding = T + update_icon() + return + + else if (W.has_tool_quality(TOOL_WRENCH)) + if(connected_port) + disconnect() + to_chat(user, "You disconnect \the [src] from the port.") + update_icon() + playsound(src, W.usesound, 50, 1) + return + else + var/obj/machinery/atmospherics/portables_connector/possible_port = locate(/obj/machinery/atmospherics/portables_connector/) in loc + if(possible_port) + if(connect(possible_port)) + to_chat(user, "You connect \the [src] to the port.") + update_icon() + playsound(src, W.usesound, 50, 1) + return + else + to_chat(user, "\The [src] failed to connect to the port.") + return + else + to_chat(user, "Nothing happens.") + return + return + + + +/obj/machinery/portable_atmospherics/powered + var/power_rating + var/power_losses + var/last_power_draw = 0 + var/obj/item/weapon/cell/cell + var/use_cell = TRUE + var/removeable_cell = TRUE + +/obj/machinery/portable_atmospherics/powered/powered() + if(use_power) //using area power + return ..() + if(cell && cell.charge) + return 1 + return 0 + +/obj/machinery/portable_atmospherics/powered/attackby(obj/item/I, mob/user) + if(use_cell && istype(I, /obj/item/weapon/cell)) + if(cell) + to_chat(user, "There is already a power cell installed.") + return + + var/obj/item/weapon/cell/C = I + + user.drop_item() + C.add_fingerprint(user) + cell = C + C.loc = src + user.visible_message("[user] opens the panel on [src] and inserts [C].", "You open the panel on [src] and insert [C].") + power_change() + return + + if(I.has_tool_quality(TOOL_SCREWDRIVER) && removeable_cell) + if(!cell) + to_chat(user, "There is no power cell installed.") + return + + user.visible_message("[user] opens the panel on [src] and removes [cell].", "You open the panel on [src] and remove [cell].") + playsound(src, I.usesound, 50, 1) + cell.add_fingerprint(user) + cell.loc = src.loc + cell = null + power_change() + return + ..() + +/obj/machinery/portable_atmospherics/proc/log_open() + if(air_contents.gas.len == 0) + return + + var/gases = "" + for(var/gas in air_contents.gas) + if(gases) + gases += ", [gas]" + else + gases = gas + log_admin("[usr] ([usr.ckey]) opened '[src.name]' containing [gases].") + message_admins("[usr] ([usr.ckey]) opened '[src.name]' containing [gases].") diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm index 1a502c675f9..0f0d2b3fe81 100644 --- a/code/game/machinery/atmoalter/pump.dm +++ b/code/game/machinery/atmoalter/pump.dm @@ -1,191 +1,191 @@ -/obj/machinery/portable_atmospherics/powered/pump - name = "portable air pump" - - icon = 'icons/obj/atmos.dmi' - icon_state = "psiphon:0" - density = TRUE - w_class = ITEMSIZE_NORMAL - - var/on = 0 - var/direction_out = 0 //0 = siphoning, 1 = releasing - var/target_pressure = ONE_ATMOSPHERE - - var/pressuremin = 0 - var/pressuremax = 10 * ONE_ATMOSPHERE - - volume = 1000 - - power_rating = 7500 //7500 W ~ 10 HP - power_losses = 150 - -/obj/machinery/portable_atmospherics/powered/pump/filled - start_pressure = 90 * ONE_ATMOSPHERE - -/obj/machinery/portable_atmospherics/powered/pump/New() - ..() - cell = new/obj/item/weapon/cell/apc(src) - - var/list/air_mix = StandardAirMix() - src.air_contents.adjust_multi("oxygen", air_mix["oxygen"], "nitrogen", air_mix["nitrogen"]) - -/obj/machinery/portable_atmospherics/powered/pump/update_icon() - cut_overlays() - - if(on && cell && cell.charge) - icon_state = "psiphon:1" - else - icon_state = "psiphon:0" - - if(holding) - add_overlay("siphon-open") - - if(connected_port) - add_overlay("siphon-connector") - - return - -/obj/machinery/portable_atmospherics/powered/pump/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - if(prob(50/severity)) - on = !on - - if(prob(100/severity)) - direction_out = !direction_out - - target_pressure = rand(0,1300) - update_icon() - - ..(severity) - -/obj/machinery/portable_atmospherics/powered/pump/process() - ..() - var/power_draw = -1 - - if(on && cell && cell.charge) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = loc.return_air() - - var/pressure_delta - var/output_volume - var/air_temperature - if(direction_out) - pressure_delta = target_pressure - environment.return_pressure() - output_volume = environment.volume * environment.group_multiplier - air_temperature = environment.temperature? environment.temperature : air_contents.temperature - else - pressure_delta = environment.return_pressure() - target_pressure - output_volume = air_contents.volume * air_contents.group_multiplier - air_temperature = air_contents.temperature? air_contents.temperature : environment.temperature - - var/transfer_moles = pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION) - - if (pressure_delta > 0.01) - if (direction_out) - power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) - else - power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) - - if (power_draw < 0) - last_flow_rate = 0 - last_power_draw = 0 - else - power_draw = max(power_draw, power_losses) - cell.use(power_draw * CELLRATE) - last_power_draw = power_draw - - update_connected_network() - - //ran out of charge - if (!cell.charge) - power_change() - update_icon() - - src.updateDialog() - -/obj/machinery/portable_atmospherics/powered/pump/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/powered/pump/attack_ai(var/mob/user) - src.add_hiddenprint(user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/powered/pump/attack_ghost(var/mob/user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/powered/pump/attack_hand(var/mob/user) - tgui_interact(user) - -/obj/machinery/portable_atmospherics/powered/pump/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "PortablePump", name) - ui.open() - - -/obj/machinery/portable_atmospherics/powered/pump/tgui_state(mob/user) - return GLOB.tgui_physical_state - -/obj/machinery/portable_atmospherics/powered/pump/tgui_data(mob/user) - var/list/data[0] - data["on"] = on ? TRUE : FALSE - data["direction"] = !direction_out ? TRUE : FALSE - data["connected"] = connected_port ? TRUE : FALSE - data["pressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) - data["target_pressure"] = round(target_pressure ? target_pressure : 0) - data["default_pressure"] = round(initial(target_pressure)) - data["min_pressure"] = round(pressuremin) - data["max_pressure"] = round(pressuremax) - - data["powerDraw"] = round(last_power_draw) - data["cellCharge"] = cell ? cell.charge : 0 - data["cellMaxCharge"] = cell ? cell.maxcharge : 1 - - if(holding) - data["holding"] = list() - data["holding"]["name"] = holding.name - data["holding"]["pressure"] = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0) - else - data["holding"] = null - - return data - -/obj/machinery/portable_atmospherics/powered/pump/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("power") - on = !on - . = 1 - if("direction") - direction_out = !direction_out - . = 1 - if("eject") - if(holding) - holding.loc = loc - holding = null - . = 1 - if("pressure") - var/pressure = params["pressure"] - if(pressure == "reset") - pressure = initial(target_pressure) - . = TRUE - else if(pressure == "min") - pressure = pressuremin - . = TRUE - else if(pressure == "max") - pressure = pressuremax - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = clamp(round(pressure), pressuremin, pressuremax) - - update_icon() +/obj/machinery/portable_atmospherics/powered/pump + name = "portable air pump" + + icon = 'icons/obj/atmos.dmi' + icon_state = "psiphon:0" + density = TRUE + w_class = ITEMSIZE_NORMAL + + var/on = 0 + var/direction_out = 0 //0 = siphoning, 1 = releasing + var/target_pressure = ONE_ATMOSPHERE + + var/pressuremin = 0 + var/pressuremax = 10 * ONE_ATMOSPHERE + + volume = 1000 + + power_rating = 7500 //7500 W ~ 10 HP + power_losses = 150 + +/obj/machinery/portable_atmospherics/powered/pump/filled + start_pressure = 90 * ONE_ATMOSPHERE + +/obj/machinery/portable_atmospherics/powered/pump/New() + ..() + cell = new/obj/item/weapon/cell/apc(src) + + var/list/air_mix = StandardAirMix() + src.air_contents.adjust_multi("oxygen", air_mix["oxygen"], "nitrogen", air_mix["nitrogen"]) + +/obj/machinery/portable_atmospherics/powered/pump/update_icon() + cut_overlays() + + if(on && cell && cell.charge) + icon_state = "psiphon:1" + else + icon_state = "psiphon:0" + + if(holding) + add_overlay("siphon-open") + + if(connected_port) + add_overlay("siphon-connector") + + return + +/obj/machinery/portable_atmospherics/powered/pump/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + if(prob(50/severity)) + on = !on + + if(prob(100/severity)) + direction_out = !direction_out + + target_pressure = rand(0,1300) + update_icon() + + ..(severity) + +/obj/machinery/portable_atmospherics/powered/pump/process() + ..() + var/power_draw = -1 + + if(on && cell && cell.charge) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = loc.return_air() + + var/pressure_delta + var/output_volume + var/air_temperature + if(direction_out) + pressure_delta = target_pressure - environment.return_pressure() + output_volume = environment.volume * environment.group_multiplier + air_temperature = environment.temperature? environment.temperature : air_contents.temperature + else + pressure_delta = environment.return_pressure() - target_pressure + output_volume = air_contents.volume * air_contents.group_multiplier + air_temperature = air_contents.temperature? air_contents.temperature : environment.temperature + + var/transfer_moles = pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION) + + if (pressure_delta > 0.01) + if (direction_out) + power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) + else + power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) + + if (power_draw < 0) + last_flow_rate = 0 + last_power_draw = 0 + else + power_draw = max(power_draw, power_losses) + cell.use(power_draw * CELLRATE) + last_power_draw = power_draw + + update_connected_network() + + //ran out of charge + if (!cell.charge) + power_change() + update_icon() + + src.updateDialog() + +/obj/machinery/portable_atmospherics/powered/pump/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/powered/pump/attack_ai(var/mob/user) + src.add_hiddenprint(user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/powered/pump/attack_ghost(var/mob/user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/powered/pump/attack_hand(var/mob/user) + tgui_interact(user) + +/obj/machinery/portable_atmospherics/powered/pump/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PortablePump", name) + ui.open() + + +/obj/machinery/portable_atmospherics/powered/pump/tgui_state(mob/user) + return GLOB.tgui_physical_state + +/obj/machinery/portable_atmospherics/powered/pump/tgui_data(mob/user) + var/list/data[0] + data["on"] = on ? TRUE : FALSE + data["direction"] = !direction_out ? TRUE : FALSE + data["connected"] = connected_port ? TRUE : FALSE + data["pressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) + data["target_pressure"] = round(target_pressure ? target_pressure : 0) + data["default_pressure"] = round(initial(target_pressure)) + data["min_pressure"] = round(pressuremin) + data["max_pressure"] = round(pressuremax) + + data["powerDraw"] = round(last_power_draw) + data["cellCharge"] = cell ? cell.charge : 0 + data["cellMaxCharge"] = cell ? cell.maxcharge : 1 + + if(holding) + data["holding"] = list() + data["holding"]["name"] = holding.name + data["holding"]["pressure"] = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0) + else + data["holding"] = null + + return data + +/obj/machinery/portable_atmospherics/powered/pump/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("power") + on = !on + . = 1 + if("direction") + direction_out = !direction_out + . = 1 + if("eject") + if(holding) + holding.loc = loc + holding = null + . = 1 + if("pressure") + var/pressure = params["pressure"] + if(pressure == "reset") + pressure = initial(target_pressure) + . = TRUE + else if(pressure == "min") + pressure = pressuremin + . = TRUE + else if(pressure == "max") + pressure = pressuremax + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + target_pressure = clamp(round(pressure), pressuremin, pressuremax) + + update_icon() diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index f27b8983e31..7b2d60e7b5f 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -1,257 +1,257 @@ -/obj/machinery/portable_atmospherics/powered/scrubber - name = "Portable Air Scrubber" - desc = "Similar to room scrubbers, this device contains an internal tank to scrub gasses from the atmosphere." - - icon = 'icons/obj/atmos.dmi' - icon_state = "pscrubber:0" - density = TRUE - w_class = ITEMSIZE_NORMAL - - var/on = 0 - var/volume_rate = 800 - - volume = 750 - - power_rating = 7500 //7500 W ~ 10 HP - power_losses = 150 - - var/minrate = 0 - var/maxrate = 10 * ONE_ATMOSPHERE - - var/list/scrubbing_gas = list("phoron", "carbon_dioxide", "nitrous_oxide", "volatile_fuel") - -/obj/machinery/portable_atmospherics/powered/scrubber/New() - ..() - cell = new/obj/item/weapon/cell/apc(src) - -/obj/machinery/portable_atmospherics/powered/scrubber/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - if(prob(50/severity)) - on = !on - update_icon() - - ..(severity) - -/obj/machinery/portable_atmospherics/powered/scrubber/update_icon() - cut_overlays() - - if(on && cell && cell.charge) - icon_state = "pscrubber:1" - else - icon_state = "pscrubber:0" - - if(holding) - add_overlay("scrubber-open") - - if(connected_port) - add_overlay("scrubber-connector") - - return - -/obj/machinery/portable_atmospherics/powered/scrubber/process() - ..() - - var/power_draw = -1 - - if(on && cell && cell.charge) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = loc.return_air() - - var/transfer_moles = min(1, volume_rate/environment.volume)*environment.total_moles - - power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, power_rating) - - if (power_draw < 0) - last_flow_rate = 0 - last_power_draw = 0 - else - power_draw = max(power_draw, power_losses) - cell.use(power_draw * CELLRATE) - last_power_draw = power_draw - - update_connected_network() - - //ran out of charge - if (!cell.charge) - power_change() - update_icon() - - //src.update_icon() - src.updateDialog() - -/obj/machinery/portable_atmospherics/powered/scrubber/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/powered/scrubber/attack_ai(var/mob/user) - src.add_hiddenprint(user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/powered/scrubber/attack_ghost(var/mob/user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/powered/scrubber/attack_hand(var/mob/user) - tgui_interact(user) - -/obj/machinery/portable_atmospherics/powered/scrubber/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "PortableScrubber", name) - ui.open() - -/obj/machinery/portable_atmospherics/powered/scrubber/tgui_data(mob/user) - var/list/data = list() - data["on"] = on ? 1 : 0 - data["connected"] = connected_port ? 1 : 0 - data["pressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) - - data["rate"] = round(volume_rate) - data["minrate"] = round(minrate) - data["maxrate"] = round(maxrate) - data["powerDraw"] = round(last_power_draw) - data["cellCharge"] = cell ? cell.charge : 0 - data["cellMaxCharge"] = cell ? cell.maxcharge : 1 - - if(holding) - data["holding"] = list() - data["holding"]["name"] = holding.name - data["holding"]["pressure"] = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0) - else - data["holding"] = null - - return data - -/obj/machinery/portable_atmospherics/powered/scrubber/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("power") - on = !on - . = TRUE - if("eject") - if(holding) - holding.loc = loc - holding = null - . = TRUE - if("volume_adj") - volume_rate = CLAMP(text2num(params["vol"]), minrate, maxrate) - . = TRUE - - update_icon() - - -//Huge scrubber -/obj/machinery/portable_atmospherics/powered/scrubber/huge - name = "Huge Air Scrubber" - desc = "A larger variation of the portable scrubber, for industrial scrubbing of air. Must be turned on from a remote terminal." - icon = 'icons/obj/atmos_vr.dmi' //VOREStation Edit - New Sprite - icon_state = "scrubber:0" - anchored = TRUE - volume = 500000 - volume_rate = 7000 - - use_power = USE_POWER_IDLE - idle_power_usage = 50 //VOREStation Edit //internal circuitry, friction losses and stuff - active_power_usage = 1000 //VOREStation Edit // Blowers running - power_rating = 100000 //VOREStation Add //100 kW ~ 135 HP - - var/global/gid = 1 - var/id = 0 - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/New() - ..() - cell = null - - id = gid - gid++ - - name = "[name] (ID [id])" - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/attack_hand(var/mob/user as mob) - to_chat(user, "You can't directly interact with this machine. Use the scrubber control console.") - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/update_icon() - src.overlays = 0 - - if(on && !(stat & (NOPOWER|BROKEN))) - icon_state = "scrubber:1" - else - icon_state = "scrubber:0" - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/power_change() - var/old_stat = stat - ..() - if (old_stat != stat) - update_icon() - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/process() - if(!anchored || (stat & (NOPOWER|BROKEN))) - on = 0 - last_flow_rate = 0 - last_power_draw = 0 - update_icon() - var/new_use_power = 1 + on - if(new_use_power != use_power) - update_use_power(new_use_power) - if(!on) - return - - var/power_draw = -1 - - var/datum/gas_mixture/environment = loc.return_air() - - var/transfer_moles = min(1, volume_rate/environment.volume)*environment.total_moles - - power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, active_power_usage) - - if (power_draw < 0) - last_flow_rate = 0 - last_power_draw = 0 - else - use_power(power_draw) - update_connected_network() - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/attackby(var/obj/item/I as obj, var/mob/user as mob) - if(I.has_tool_quality(TOOL_WRENCH)) - if(on) - to_chat(user, "Turn \the [src] off first!") - return - - anchored = !anchored - playsound(src, I.usesound, 50, 1) - to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") - - return - - //doesn't use power cells - if(istype(I, /obj/item/weapon/cell)) - return - if(I.has_tool_quality(TOOL_SCREWDRIVER)) - return - - //doesn't hold tanks - if(istype(I, /obj/item/weapon/tank)) - return - - ..() - - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary - name = "Stationary Air Scrubber" - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary/Initialize() - . = ..() - desc += "This one seems to be tightly secured with large bolts." - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary/attackby(var/obj/item/I as obj, var/mob/user as mob) - if(I.has_tool_quality(TOOL_WRENCH)) - to_chat(user, "The bolts are too tight for you to unscrew!") - return - - ..() +/obj/machinery/portable_atmospherics/powered/scrubber + name = "Portable Air Scrubber" + desc = "Similar to room scrubbers, this device contains an internal tank to scrub gasses from the atmosphere." + + icon = 'icons/obj/atmos.dmi' + icon_state = "pscrubber:0" + density = TRUE + w_class = ITEMSIZE_NORMAL + + var/on = 0 + var/volume_rate = 800 + + volume = 750 + + power_rating = 7500 //7500 W ~ 10 HP + power_losses = 150 + + var/minrate = 0 + var/maxrate = 10 * ONE_ATMOSPHERE + + var/list/scrubbing_gas = list("phoron", "carbon_dioxide", "nitrous_oxide", "volatile_fuel") + +/obj/machinery/portable_atmospherics/powered/scrubber/New() + ..() + cell = new/obj/item/weapon/cell/apc(src) + +/obj/machinery/portable_atmospherics/powered/scrubber/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + if(prob(50/severity)) + on = !on + update_icon() + + ..(severity) + +/obj/machinery/portable_atmospherics/powered/scrubber/update_icon() + cut_overlays() + + if(on && cell && cell.charge) + icon_state = "pscrubber:1" + else + icon_state = "pscrubber:0" + + if(holding) + add_overlay("scrubber-open") + + if(connected_port) + add_overlay("scrubber-connector") + + return + +/obj/machinery/portable_atmospherics/powered/scrubber/process() + ..() + + var/power_draw = -1 + + if(on && cell && cell.charge) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = loc.return_air() + + var/transfer_moles = min(1, volume_rate/environment.volume)*environment.total_moles + + power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, power_rating) + + if (power_draw < 0) + last_flow_rate = 0 + last_power_draw = 0 + else + power_draw = max(power_draw, power_losses) + cell.use(power_draw * CELLRATE) + last_power_draw = power_draw + + update_connected_network() + + //ran out of charge + if (!cell.charge) + power_change() + update_icon() + + //src.update_icon() + src.updateDialog() + +/obj/machinery/portable_atmospherics/powered/scrubber/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/powered/scrubber/attack_ai(var/mob/user) + src.add_hiddenprint(user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/powered/scrubber/attack_ghost(var/mob/user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/powered/scrubber/attack_hand(var/mob/user) + tgui_interact(user) + +/obj/machinery/portable_atmospherics/powered/scrubber/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PortableScrubber", name) + ui.open() + +/obj/machinery/portable_atmospherics/powered/scrubber/tgui_data(mob/user) + var/list/data = list() + data["on"] = on ? 1 : 0 + data["connected"] = connected_port ? 1 : 0 + data["pressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) + + data["rate"] = round(volume_rate) + data["minrate"] = round(minrate) + data["maxrate"] = round(maxrate) + data["powerDraw"] = round(last_power_draw) + data["cellCharge"] = cell ? cell.charge : 0 + data["cellMaxCharge"] = cell ? cell.maxcharge : 1 + + if(holding) + data["holding"] = list() + data["holding"]["name"] = holding.name + data["holding"]["pressure"] = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0) + else + data["holding"] = null + + return data + +/obj/machinery/portable_atmospherics/powered/scrubber/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("power") + on = !on + . = TRUE + if("eject") + if(holding) + holding.loc = loc + holding = null + . = TRUE + if("volume_adj") + volume_rate = CLAMP(text2num(params["vol"]), minrate, maxrate) + . = TRUE + + update_icon() + + +//Huge scrubber +/obj/machinery/portable_atmospherics/powered/scrubber/huge + name = "Huge Air Scrubber" + desc = "A larger variation of the portable scrubber, for industrial scrubbing of air. Must be turned on from a remote terminal." + icon = 'icons/obj/atmos_vr.dmi' //VOREStation Edit - New Sprite + icon_state = "scrubber:0" + anchored = TRUE + volume = 500000 + volume_rate = 7000 + + use_power = USE_POWER_IDLE + idle_power_usage = 50 //VOREStation Edit //internal circuitry, friction losses and stuff + active_power_usage = 1000 //VOREStation Edit // Blowers running + power_rating = 100000 //VOREStation Add //100 kW ~ 135 HP + + var/global/gid = 1 + var/id = 0 + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/New() + ..() + cell = null + + id = gid + gid++ + + name = "[name] (ID [id])" + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/attack_hand(var/mob/user as mob) + to_chat(user, "You can't directly interact with this machine. Use the scrubber control console.") + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/update_icon() + src.overlays = 0 + + if(on && !(stat & (NOPOWER|BROKEN))) + icon_state = "scrubber:1" + else + icon_state = "scrubber:0" + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/power_change() + var/old_stat = stat + ..() + if (old_stat != stat) + update_icon() + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/process() + if(!anchored || (stat & (NOPOWER|BROKEN))) + on = 0 + last_flow_rate = 0 + last_power_draw = 0 + update_icon() + var/new_use_power = 1 + on + if(new_use_power != use_power) + update_use_power(new_use_power) + if(!on) + return + + var/power_draw = -1 + + var/datum/gas_mixture/environment = loc.return_air() + + var/transfer_moles = min(1, volume_rate/environment.volume)*environment.total_moles + + power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, active_power_usage) + + if (power_draw < 0) + last_flow_rate = 0 + last_power_draw = 0 + else + use_power(power_draw) + update_connected_network() + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/attackby(var/obj/item/I as obj, var/mob/user as mob) + if(I.has_tool_quality(TOOL_WRENCH)) + if(on) + to_chat(user, "Turn \the [src] off first!") + return + + anchored = !anchored + playsound(src, I.usesound, 50, 1) + to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") + + return + + //doesn't use power cells + if(istype(I, /obj/item/weapon/cell)) + return + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + return + + //doesn't hold tanks + if(istype(I, /obj/item/weapon/tank)) + return + + ..() + + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary + name = "Stationary Air Scrubber" + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary/Initialize() + . = ..() + desc += "This one seems to be tightly secured with large bolts." + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary/attackby(var/obj/item/I as obj, var/mob/user as mob) + if(I.has_tool_quality(TOOL_WRENCH)) + to_chat(user, "The bolts are too tight for you to unscrew!") + return + + ..() diff --git a/code/game/machinery/atmoalter/zvent.dm b/code/game/machinery/atmoalter/zvent.dm index d4f2ccfc027..453b1ec5e07 100644 --- a/code/game/machinery/atmoalter/zvent.dm +++ b/code/game/machinery/atmoalter/zvent.dm @@ -1,29 +1,29 @@ -/obj/machinery/zvent - name = "Interfloor Air Transfer System" - - icon = 'icons/obj/pipes.dmi' - icon_state = "vent-db" - density = FALSE - anchored=TRUE - - var/on = 0 - var/volume_rate = 800 - -/obj/machinery/zvent/process() - - //all this object does, is make its turf share air with the ones above and below it, if they have a vent too. - if (istype(loc,/turf/simulated)) //if we're not on a valid turf, forget it - for (var/new_z in list(-1,1)) //change this list if a fancier system of z-levels gets implemented - var/turf/simulated/zturf_conn = locate(x,y,z+new_z) - if (istype(zturf_conn)) - var/obj/machinery/zvent/zvent_conn= locate(/obj/machinery/zvent) in zturf_conn - if (istype(zvent_conn)) - //both floors have simulated turfs, share() - var/turf/simulated/myturf = loc - var/datum/gas_mixture/conn_air = zturf_conn.zone.air //TODO: pop culture reference - var/datum/gas_mixture/my_air = myturf.air - if (istype(conn_air) && istype(my_air)) -// if (!my_air.compare(conn_air)) -// myturf.reset_delay() -// zturf_conn.reset_delay() - my_air.share(conn_air) +/obj/machinery/zvent + name = "Interfloor Air Transfer System" + + icon = 'icons/obj/pipes.dmi' + icon_state = "vent-db" + density = FALSE + anchored=TRUE + + var/on = 0 + var/volume_rate = 800 + +/obj/machinery/zvent/process() + + //all this object does, is make its turf share air with the ones above and below it, if they have a vent too. + if (istype(loc,/turf/simulated)) //if we're not on a valid turf, forget it + for (var/new_z in list(-1,1)) //change this list if a fancier system of z-levels gets implemented + var/turf/simulated/zturf_conn = locate(x,y,z+new_z) + if (istype(zturf_conn)) + var/obj/machinery/zvent/zvent_conn= locate(/obj/machinery/zvent) in zturf_conn + if (istype(zvent_conn)) + //both floors have simulated turfs, share() + var/turf/simulated/myturf = loc + var/datum/gas_mixture/conn_air = zturf_conn.zone.air //TODO: pop culture reference + var/datum/gas_mixture/my_air = myturf.air + if (istype(conn_air) && istype(my_air)) +// if (!my_air.compare(conn_air)) +// myturf.reset_delay() +// zturf_conn.reset_delay() + my_air.share(conn_air) diff --git a/code/game/machinery/biogenerator.dm b/code/game/machinery/biogenerator.dm index 763564fcf96..215b2051faf 100644 --- a/code/game/machinery/biogenerator.dm +++ b/code/game/machinery/biogenerator.dm @@ -64,26 +64,26 @@ item_list = list() item_list["Food Items"] = list( - BIOGEN_REAGENT("10 milk", "milk", 10, 20), - BIOGEN_REAGENT("50 milk", "milk", 50, 95), - BIOGEN_REAGENT("10 Cream", "cream", 10, 30), - BIOGEN_REAGENT("50 Cream", "cream", 50, 120), + BIOGEN_REAGENT("Milk x10", "milk", 10, 20), + BIOGEN_REAGENT("Milk x50", "milk", 50, 95), + BIOGEN_REAGENT("Cream x10", "cream", 10, 30), + BIOGEN_REAGENT("Cream x50", "cream", 50, 120), BIOGEN_ITEM("Slab of meat", /obj/item/weapon/reagent_containers/food/snacks/meat, 1, 50), - BIOGEN_ITEM("5 slabs of meat", /obj/item/weapon/reagent_containers/food/snacks/meat, 5, 250), + BIOGEN_ITEM("Slabs of meat x5", /obj/item/weapon/reagent_containers/food/snacks/meat, 5, 250), ) item_list["Cooking Ingredients"] = list( - BIOGEN_REAGENT("10 Universal Enzyme", "enzyme", 10, 30), - BIOGEN_REAGENT("50 Universal Enzyme", "enzyme", 50, 120), + BIOGEN_REAGENT("Universal Enzyme x10", "enzyme", 10, 30), + BIOGEN_REAGENT("Universal Enzyme x50", "enzyme", 50, 120), BIOGEN_ITEM("Nutri-spread", /obj/item/weapon/reagent_containers/food/snacks/spreads, 1, 30), - BIOGEN_ITEM("5 nutri-spread", /obj/item/weapon/reagent_containers/food/snacks/spreads, 5, 120), + BIOGEN_ITEM("Nutri-spread x5", /obj/item/weapon/reagent_containers/food/snacks/spreads, 5, 120), ) item_list["Gardening Nutrients"] = list( BIOGEN_ITEM("E-Z-Nutrient", /obj/item/weapon/reagent_containers/glass/bottle/eznutrient, 1, 60), - BIOGEN_ITEM("5 E-Z-Nutrient", /obj/item/weapon/reagent_containers/glass/bottle/eznutrient, 5, 300), + BIOGEN_ITEM("E-Z-Nutrient x5", /obj/item/weapon/reagent_containers/glass/bottle/eznutrient, 5, 300), BIOGEN_ITEM("Left 4 Zed", /obj/item/weapon/reagent_containers/glass/bottle/left4zed, 1, 120), - BIOGEN_ITEM("5 Left 4 Zed", /obj/item/weapon/reagent_containers/glass/bottle/left4zed, 5, 600), + BIOGEN_ITEM("Left 4 Zed x5", /obj/item/weapon/reagent_containers/glass/bottle/left4zed, 5, 600), BIOGEN_ITEM("Robust Harvest", /obj/item/weapon/reagent_containers/glass/bottle/robustharvest, 1, 150), - BIOGEN_ITEM("5 Robust Harvest", /obj/item/weapon/reagent_containers/glass/bottle/robustharvest, 5, 750), + BIOGEN_ITEM("Robust Harvest x5", /obj/item/weapon/reagent_containers/glass/bottle/robustharvest, 5, 750), ) item_list["Leather Products"] = list( BIOGEN_ITEM("Wallet", /obj/item/weapon/storage/wallet, 1, 100), @@ -100,8 +100,8 @@ BIOGEN_ITEM("Leather Jacket", /obj/item/clothing/suit/storage/toggle/brown_jacket, 1, 500), BIOGEN_ITEM("Winter Coat", /obj/item/clothing/suit/storage/hooded/wintercoat, 1, 500), //VOREStation Edit - Algae for oxygen generator - BIOGEN_ITEM("4 Algae Sheets", /obj/item/stack/material/algae, 4, 400), - BIOGEN_ITEM("50 Algae Sheets", /obj/item/stack/material/algae, 50, 5000), + BIOGEN_ITEM("Algae Sheets x4", /obj/item/stack/material/algae, 4, 400), + BIOGEN_ITEM("Algae Sheets x50", /obj/item/stack/material/algae, 50, 5000), ) /obj/machinery/biogenerator/tgui_static_data(mob/user) diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index e0fa69147b8..b3e03da0eb1 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -1,18 +1,18 @@ -/obj/machinery/button - name = "button" - icon = 'icons/obj/objects.dmi' - icon_state = "launcherbtt" - layer = ABOVE_WINDOW_LAYER - desc = "A remote control switch for something." - var/id = null - var/active = 0 - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/button/attack_ai(mob/user as mob) - return attack_hand(user) - -/obj/machinery/button/attackby(obj/item/weapon/W, mob/user as mob) - return attack_hand(user) +/obj/machinery/button + name = "button" + icon = 'icons/obj/objects.dmi' + icon_state = "launcherbtt" + layer = ABOVE_WINDOW_LAYER + desc = "A remote control switch for something." + var/id = null + var/active = 0 + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/button/attack_ai(mob/user as mob) + return attack_hand(user) + +/obj/machinery/button/attackby(obj/item/weapon/W, mob/user as mob) + return attack_hand(user) diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 796415d0f3a..b001611c19a 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -1,482 +1,482 @@ -/obj/machinery/camera - name = "security camera" - desc = "It's used to monitor rooms." - icon = 'icons/obj/monitors_vr.dmi' //VOREStation Edit - New Icons - icon_state = "camera" - use_power = USE_POWER_ACTIVE - idle_power_usage = 5 - active_power_usage = 10 - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - - var/list/network = list(NETWORK_DEFAULT) - var/c_tag = null - var/c_tag_order = 999 - var/status = 1 - anchored = TRUE - var/invuln = 0 - var/bugged = 0 - var/obj/item/weapon/camera_assembly/assembly = null - - var/toughness = 5 //sorta fragile - - // WIRES - var/datum/wires/camera/wires = null // Wires datum - - //OTHER - - var/view_range = 7 - var/short_range = 2 - - var/light_disabled = 0 - var/in_use_lights = 0 // TO BE IMPLEMENTED - LIES. - var/alarm_on = 0 - var/busy = 0 - - var/on_open_network = 0 - var/always_visible = FALSE //Visable from any map, good for entertainment network cameras - - var/affected_by_emp_until = 0 - - var/client_huds = list() - - var/list/camera_computers_using_this = list() - -/obj/machinery/camera/New() - wires = new(src) - assembly = new(src) - assembly.state = 4 - client_huds |= global_hud.whitense - - /* // Use this to look for cameras that have the same c_tag. - for(var/obj/machinery/camera/C in cameranet.cameras) - var/list/tempnetwork = C.network&src.network - if(C != src && C.c_tag == src.c_tag && tempnetwork.len) - to_world_log("[src.c_tag] [src.x] [src.y] [src.z] conflicts with [C.c_tag] [C.x] [C.y] [C.z]") - */ - if(!src.network || src.network.len < 1) - if(loc) - error("[src.name] in [get_area(src)] (x:[src.x] y:[src.y] z:[src.z] has errored. [src.network?"Empty network list":"Null network list"]") - else - error("[src.name] in [get_area(src)]has errored. [src.network?"Empty network list":"Null network list"]") - ASSERT(src.network) - ASSERT(src.network.len > 0) - // VOREStation Edit Start - Make mapping with cameras easier - if(!c_tag) - var/area/A = get_area(src) - c_tag = "[A ? A.name : "Unknown"] #[rand(111,999)]" - ..() - // VOREStation Edit End - -/obj/machinery/camera/Destroy() - if(isMotion()) - unsense_proximity(callback = /atom/proc/HasProximity) - deactivate(null, 0) //kick anyone viewing out - if(assembly) - qdel(assembly) - assembly = null - qdel(wires) - wires = null - return ..() - -/obj/machinery/camera/process() - if((stat & EMPED) && world.time >= affected_by_emp_until) - stat &= ~EMPED - cancelCameraAlarm() - update_icon() - update_coverage() - return internal_process() - -/obj/machinery/camera/proc/internal_process() - return - -/obj/machinery/camera/emp_act(severity) - if(!isEmpProof() && prob(100/severity)) - if(!affected_by_emp_until || (world.time > affected_by_emp_until)) - affected_by_emp_until = max(affected_by_emp_until, world.time + (90 SECONDS / severity)) - stat |= EMPED - set_light(0) - triggerCameraAlarm() - update_icon() - update_coverage() - START_PROCESSING(SSobj, src) - -/obj/machinery/camera/bullet_act(var/obj/item/projectile/P) - take_damage(P.get_structure_damage()) - -/obj/machinery/camera/ex_act(severity) - if(src.invuln) - return - - //camera dies if an explosion touches it! - if(severity <= 2 || prob(50)) - destroy() - - ..() //and give it the regular chance of being deleted outright - -/obj/machinery/camera/blob_act() - if((stat & BROKEN) || invuln) - return - destroy() - -/obj/machinery/camera/hitby(AM as mob|obj) - ..() - if (istype(AM, /obj)) - var/obj/O = AM - if (O.throwforce >= src.toughness) - visible_message("[src] was hit by [O].") - take_damage(O.throwforce) - -/obj/machinery/camera/proc/setViewRange(var/num = 7) - src.view_range = num - cameranet.updateVisibility(src, 0) - -/obj/machinery/camera/attack_hand(mob/living/carbon/human/user as mob) - if(!istype(user)) - return - - if(user.species.can_shred(user)) - set_status(0) - user.do_attack_animation(src) - user.setClickCooldown(user.get_attack_speed()) - visible_message("\The [user] slashes at [src]!") - playsound(src, 'sound/weapons/slash.ogg', 100, 1) - add_hiddenprint(user) - destroy() - -/obj/machinery/camera/attack_generic(mob/user as mob) - if(isanimal(user)) - var/mob/living/simple_mob/S = user - set_status(0) - S.do_attack_animation(src) - S.setClickCooldown(user.get_attack_speed()) - visible_message("\The [user] [pick(S.attacktext)] \the [src]!") - playsound(src, S.attack_sound, 100, 1) - add_hiddenprint(user) - destroy() - ..() - -/obj/machinery/camera/attackby(obj/item/W as obj, mob/living/user as mob) - update_coverage() - // DECONSTRUCTION - if(W.has_tool_quality(TOOL_SCREWDRIVER)) - //to_chat(user, "You start to [panel_open ? "close" : "open"] the camera's panel.") - //if(toggle_panel(user)) // No delay because no one likes screwdrivers trying to be hip and have a duration cooldown - panel_open = !panel_open - user.visible_message("[user] screws the camera's panel [panel_open ? "open" : "closed"]!", - "You screw the camera's panel [panel_open ? "open" : "closed"].") - playsound(src, W.usesound, 50, 1) - - else if((W.has_tool_quality(TOOL_WIRECUTTER) || istype(W, /obj/item/device/multitool)) && panel_open) - interact(user) - - else if(W.has_tool_quality(TOOL_WELDER) && (wires.CanDeconstruct() || (stat & BROKEN))) - if(weld(W, user)) - if(assembly) - assembly.loc = src.loc - assembly.anchored = TRUE - assembly.camera_name = c_tag - assembly.camera_network = english_list(network, NETWORK_DEFAULT, ",", ",") - assembly.update_icon() - assembly.dir = src.dir - if(stat & BROKEN) - assembly.state = 2 - to_chat(user, "You repaired \the [src] frame.") - else - assembly.state = 1 - to_chat(user, "You cut \the [src] free from the wall.") - new /obj/item/stack/cable_coil(src.loc, length=2) - assembly = null //so qdel doesn't eat it. - qdel(src) - - // OTHER - else if (can_use() && (istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/device/pda)) && isliving(user)) - var/mob/living/U = user - var/obj/item/weapon/paper/X = null - var/obj/item/device/pda/P = null - - var/itemname = "" - var/info = "" - if(istype(W, /obj/item/weapon/paper)) - X = W - itemname = X.name - info = X.info - else - P = W - itemname = P.name - var/datum/data/pda/app/notekeeper/N = P.find_program(/datum/data/pda/app/notekeeper) - if(N) - info = N.notehtml - to_chat(U, "You hold \a [itemname] up to the camera ...") - for(var/mob/living/silicon/ai/O in living_mob_list) - if(!O.client) - continue - if(U.name == "Unknown") - to_chat(O, "[U] holds \a [itemname] up to one of your cameras ...") - else - to_chat(O, "[U] holds \a [itemname] up to one of your cameras ...") - O << browse(text("[][]", itemname, info), text("window=[]", itemname)) - - else if (istype(W, /obj/item/weapon/camera_bug)) - if (!src.can_use()) - to_chat(user, "Camera non-functional.") - return - if (src.bugged) - to_chat(user, "Camera bug removed.") - src.bugged = 0 - else - to_chat(user, "Camera bugged.") - src.bugged = 1 - - else if(W.damtype == BRUTE || W.damtype == BURN) //bashing cameras - user.setClickCooldown(user.get_attack_speed(W)) - if (W.force >= src.toughness) - user.do_attack_animation(src) - visible_message("[src] has been [LAZYLEN(W.attack_verb) ? pick(W.attack_verb) : "attacked"] with [W] by [user]!") - if (istype(W, /obj/item)) //is it even possible to get into attackby() with non-items? - var/obj/item/I = W - if (I.hitsound) - playsound(src, I.hitsound, 50, 1, -1) - take_damage(W.force) - - else - ..() - -/obj/machinery/camera/proc/deactivate(user as mob, var/choice = 1) - // The only way for AI to reactivate cameras are malf abilities, this gives them different messages. - if(istype(user, /mob/living/silicon/ai)) - user = null - - if(choice != 1) - return - - set_status(!src.status) - if (!(src.status)) - if(user) - visible_message(" [user] has deactivated [src]!") - else - visible_message(" [src] clicks and shuts down. ") - playsound(src, 'sound/items/Wirecutter.ogg', 100, 1) - icon_state = "[initial(icon_state)]1" - add_hiddenprint(user) - else - if(user) - visible_message(" [user] has reactivated [src]!") - else - visible_message(" [src] clicks and reactivates itself. ") - playsound(src, 'sound/items/Wirecutter.ogg', 100, 1) - icon_state = initial(icon_state) - add_hiddenprint(user) - -/obj/machinery/camera/take_damage(var/force, var/message) - //prob(25) gives an average of 3-4 hits - if (force >= toughness && (force > toughness*4 || prob(25))) - destroy() - -//Used when someone breaks a camera -/obj/machinery/camera/proc/destroy() - stat |= BROKEN - wires.cut_all() - - triggerCameraAlarm() - update_icon() - update_coverage() - - //sparks - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, loc) - spark_system.start() - playsound(src, "sparks", 50, 1) - -/obj/machinery/camera/proc/set_status(var/newstatus) - if (status != newstatus) - status = newstatus - update_coverage() - -/obj/machinery/camera/check_eye(mob/user) - if(!can_use()) return -1 - if(isXRay()) return SEE_TURFS|SEE_MOBS|SEE_OBJS - return 0 - -/obj/machinery/camera/update_icon() - if (!status || (stat & BROKEN)) - icon_state = "[initial(icon_state)]1" - else if (stat & EMPED) - icon_state = "[initial(icon_state)]emp" - else - icon_state = initial(icon_state) - -/obj/machinery/camera/proc/triggerCameraAlarm(var/duration = 0) - alarm_on = 1 - camera_alarm.triggerAlarm(loc, src, duration) - -/obj/machinery/camera/proc/cancelCameraAlarm() - if(wires.is_cut(WIRE_CAM_ALARM)) - return - - alarm_on = 0 - camera_alarm.clearAlarm(loc, src) - -//if false, then the camera is listed as DEACTIVATED and cannot be used -/obj/machinery/camera/proc/can_use() - if(!status) - return 0 - if(stat & (EMPED|BROKEN)) - return 0 - return 1 - -/obj/machinery/camera/proc/can_see() - var/list/see = null - var/turf/pos = get_turf(src) - if(!pos) - return list() - - if(isXRay()) - see = range(view_range, pos) - else - see = hear(view_range, pos) - return see - -/atom/proc/auto_turn() - //Automatically turns based on nearby walls. - var/turf/simulated/wall/T = null - for(var/i = 1, i <= 8; i += i) - T = get_ranged_target_turf(src, i, 1) - if(istype(T)) - //If someone knows a better way to do this, let me know. -Giacom - switch(i) - if(NORTH) - src.set_dir(SOUTH) - if(SOUTH) - src.set_dir(NORTH) - if(WEST) - src.set_dir(EAST) - if(EAST) - src.set_dir(WEST) - break - -//Return a working camera that can see a given mob -//or null if none -/proc/seen_by_camera(var/mob/M) - for(var/obj/machinery/camera/C in oview(4, M)) - if(C.can_use()) // check if camera disabled - return C - return null - -/proc/near_range_camera(var/mob/M) - - for(var/obj/machinery/camera/C in range(4, M)) - if(C.can_use()) // check if camera disabled - return C - - return null - -/obj/machinery/camera/proc/weld(var/obj/item/weapon/weldingtool/WT, var/mob/user) - WT = WT.get_welder() - - if(busy) - return 0 - if(!WT.isOn()) - return 0 - - // Do after stuff here - to_chat(user, "You start to weld [src]..") - playsound(src, WT.usesound, 50, 1) - WT.eyecheck(user) - busy = 1 - if(do_after(user, 100 * WT.toolspeed)) - busy = 0 - if(!WT.isOn()) - return 0 - return 1 - busy = 0 - return 0 - -/obj/machinery/camera/interact(mob/living/user as mob) - if(!panel_open || istype(user, /mob/living/silicon/ai)) - return - - if(stat & BROKEN) - to_chat(user, "\The [src] is broken.") - return - - user.set_machine(src) - wires.Interact(user) - -/obj/machinery/camera/proc/add_network(var/network_name) - add_networks(list(network_name)) - -/obj/machinery/camera/proc/remove_network(var/network_name) - remove_networks(list(network_name)) - -/obj/machinery/camera/proc/add_networks(var/list/networks) - var/network_added - network_added = 0 - for(var/network_name in networks) - if(!(network_name in src.network)) - network += network_name - network_added = 1 - - if(network_added) - update_coverage(1) - -/obj/machinery/camera/proc/remove_networks(var/list/networks) - var/network_removed - network_removed = 0 - for(var/network_name in networks) - if(network_name in src.network) - network -= network_name - network_removed = 1 - - if(network_removed) - update_coverage(1) - -/obj/machinery/camera/proc/replace_networks(var/list/networks) - if(networks.len != network.len) - network = networks - update_coverage(1) - return - - for(var/new_network in networks) - if(!(new_network in network)) - network = networks - update_coverage(1) - return - -/obj/machinery/camera/proc/clear_all_networks() - if(network.len) - network.Cut() - update_coverage(1) - -/obj/machinery/camera/proc/tgui_structure() - var/cam[0] - cam["name"] = sanitize(c_tag) - cam["deact"] = !can_use() - cam["camera"] = "\ref[src]" - cam["omni"] = always_visible - cam["x"] = x - cam["y"] = y - cam["z"] = z - return cam - -/obj/machinery/camera/proc/update_coverage(var/network_change = 0) - if(network_change) - var/list/open_networks = difflist(network, restricted_camera_networks) - // Add or remove camera from the camera net as necessary - if(on_open_network && !open_networks.len) - cameranet.removeCamera(src) - else if(!on_open_network && open_networks.len) - on_open_network = 1 - cameranet.addCamera(src) - else - cameranet.updateVisibility(src, 0) - -// Resets the camera's wires to fully operational state. Used by one of Malfunction abilities. -/obj/machinery/camera/proc/reset_wires() - if(!wires) - return - if (stat & BROKEN) // Fix the camera - stat &= ~BROKEN - wires.repair() - update_icon() - update_coverage() +/obj/machinery/camera + name = "security camera" + desc = "It's used to monitor rooms." + icon = 'icons/obj/monitors_vr.dmi' //VOREStation Edit - New Icons + icon_state = "camera" + use_power = USE_POWER_ACTIVE + idle_power_usage = 5 + active_power_usage = 10 + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + + var/list/network = list(NETWORK_DEFAULT) + var/c_tag = null + var/c_tag_order = 999 + var/status = 1 + anchored = TRUE + var/invuln = 0 + var/bugged = 0 + var/obj/item/weapon/camera_assembly/assembly = null + + var/toughness = 5 //sorta fragile + + // WIRES + var/datum/wires/camera/wires = null // Wires datum + + //OTHER + + var/view_range = 7 + var/short_range = 2 + + var/light_disabled = 0 + var/in_use_lights = 0 // TO BE IMPLEMENTED - LIES. + var/alarm_on = 0 + var/busy = 0 + + var/on_open_network = 0 + var/always_visible = FALSE //Visable from any map, good for entertainment network cameras + + var/affected_by_emp_until = 0 + + var/client_huds = list() + + var/list/camera_computers_using_this = list() + +/obj/machinery/camera/New() + wires = new(src) + assembly = new(src) + assembly.state = 4 + client_huds |= global_hud.whitense + + /* // Use this to look for cameras that have the same c_tag. + for(var/obj/machinery/camera/C in cameranet.cameras) + var/list/tempnetwork = C.network&src.network + if(C != src && C.c_tag == src.c_tag && tempnetwork.len) + to_world_log("[src.c_tag] [src.x] [src.y] [src.z] conflicts with [C.c_tag] [C.x] [C.y] [C.z]") + */ + if(!src.network || src.network.len < 1) + if(loc) + error("[src.name] in [get_area(src)] (x:[src.x] y:[src.y] z:[src.z] has errored. [src.network?"Empty network list":"Null network list"]") + else + error("[src.name] in [get_area(src)]has errored. [src.network?"Empty network list":"Null network list"]") + ASSERT(src.network) + ASSERT(src.network.len > 0) + // VOREStation Edit Start - Make mapping with cameras easier + if(!c_tag) + var/area/A = get_area(src) + c_tag = "[A ? A.name : "Unknown"] #[rand(111,999)]" + ..() + // VOREStation Edit End + +/obj/machinery/camera/Destroy() + if(isMotion()) + unsense_proximity(callback = /atom/proc/HasProximity) + deactivate(null, 0) //kick anyone viewing out + if(assembly) + qdel(assembly) + assembly = null + qdel(wires) + wires = null + return ..() + +/obj/machinery/camera/process() + if((stat & EMPED) && world.time >= affected_by_emp_until) + stat &= ~EMPED + cancelCameraAlarm() + update_icon() + update_coverage() + return internal_process() + +/obj/machinery/camera/proc/internal_process() + return + +/obj/machinery/camera/emp_act(severity) + if(!isEmpProof() && prob(100/severity)) + if(!affected_by_emp_until || (world.time > affected_by_emp_until)) + affected_by_emp_until = max(affected_by_emp_until, world.time + (90 SECONDS / severity)) + stat |= EMPED + set_light(0) + triggerCameraAlarm() + update_icon() + update_coverage() + START_PROCESSING(SSobj, src) + +/obj/machinery/camera/bullet_act(var/obj/item/projectile/P) + take_damage(P.get_structure_damage()) + +/obj/machinery/camera/ex_act(severity) + if(src.invuln) + return + + //camera dies if an explosion touches it! + if(severity <= 2 || prob(50)) + destroy() + + ..() //and give it the regular chance of being deleted outright + +/obj/machinery/camera/blob_act() + if((stat & BROKEN) || invuln) + return + destroy() + +/obj/machinery/camera/hitby(AM as mob|obj) + ..() + if (istype(AM, /obj)) + var/obj/O = AM + if (O.throwforce >= src.toughness) + visible_message("[src] was hit by [O].") + take_damage(O.throwforce) + +/obj/machinery/camera/proc/setViewRange(var/num = 7) + src.view_range = num + cameranet.updateVisibility(src, 0) + +/obj/machinery/camera/attack_hand(mob/living/carbon/human/user as mob) + if(!istype(user)) + return + + if(user.species.can_shred(user)) + set_status(0) + user.do_attack_animation(src) + user.setClickCooldown(user.get_attack_speed()) + visible_message("\The [user] slashes at [src]!") + playsound(src, 'sound/weapons/slash.ogg', 100, 1) + add_hiddenprint(user) + destroy() + +/obj/machinery/camera/attack_generic(mob/user as mob) + if(isanimal(user)) + var/mob/living/simple_mob/S = user + set_status(0) + S.do_attack_animation(src) + S.setClickCooldown(user.get_attack_speed()) + visible_message("\The [user] [pick(S.attacktext)] \the [src]!") + playsound(src, S.attack_sound, 100, 1) + add_hiddenprint(user) + destroy() + ..() + +/obj/machinery/camera/attackby(obj/item/W as obj, mob/living/user as mob) + update_coverage() + // DECONSTRUCTION + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + //to_chat(user, "You start to [panel_open ? "close" : "open"] the camera's panel.") + //if(toggle_panel(user)) // No delay because no one likes screwdrivers trying to be hip and have a duration cooldown + panel_open = !panel_open + user.visible_message("[user] screws the camera's panel [panel_open ? "open" : "closed"]!", + "You screw the camera's panel [panel_open ? "open" : "closed"].") + playsound(src, W.usesound, 50, 1) + + else if((W.has_tool_quality(TOOL_WIRECUTTER) || istype(W, /obj/item/device/multitool)) && panel_open) + interact(user) + + else if(W.has_tool_quality(TOOL_WELDER) && (wires.CanDeconstruct() || (stat & BROKEN))) + if(weld(W, user)) + if(assembly) + assembly.loc = src.loc + assembly.anchored = TRUE + assembly.camera_name = c_tag + assembly.camera_network = english_list(network, NETWORK_DEFAULT, ",", ",") + assembly.update_icon() + assembly.dir = src.dir + if(stat & BROKEN) + assembly.state = 2 + to_chat(user, "You repaired \the [src] frame.") + else + assembly.state = 1 + to_chat(user, "You cut \the [src] free from the wall.") + new /obj/item/stack/cable_coil(src.loc, length=2) + assembly = null //so qdel doesn't eat it. + qdel(src) + + // OTHER + else if (can_use() && (istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/device/pda)) && isliving(user)) + var/mob/living/U = user + var/obj/item/weapon/paper/X = null + var/obj/item/device/pda/P = null + + var/itemname = "" + var/info = "" + if(istype(W, /obj/item/weapon/paper)) + X = W + itemname = X.name + info = X.info + else + P = W + itemname = P.name + var/datum/data/pda/app/notekeeper/N = P.find_program(/datum/data/pda/app/notekeeper) + if(N) + info = N.notehtml + to_chat(U, "You hold \a [itemname] up to the camera ...") + for(var/mob/living/silicon/ai/O in living_mob_list) + if(!O.client) + continue + if(U.name == "Unknown") + to_chat(O, "[U] holds \a [itemname] up to one of your cameras ...") + else + to_chat(O, "[U] holds \a [itemname] up to one of your cameras ...") + O << browse(text("[][]", itemname, info), text("window=[]", itemname)) + + else if (istype(W, /obj/item/weapon/camera_bug)) + if (!src.can_use()) + to_chat(user, "Camera non-functional.") + return + if (src.bugged) + to_chat(user, "Camera bug removed.") + src.bugged = 0 + else + to_chat(user, "Camera bugged.") + src.bugged = 1 + + else if(W.damtype == BRUTE || W.damtype == BURN) //bashing cameras + user.setClickCooldown(user.get_attack_speed(W)) + if (W.force >= src.toughness) + user.do_attack_animation(src) + visible_message("[src] has been [LAZYLEN(W.attack_verb) ? pick(W.attack_verb) : "attacked"] with [W] by [user]!") + if (istype(W, /obj/item)) //is it even possible to get into attackby() with non-items? + var/obj/item/I = W + if (I.hitsound) + playsound(src, I.hitsound, 50, 1, -1) + take_damage(W.force) + + else + ..() + +/obj/machinery/camera/proc/deactivate(user as mob, var/choice = 1) + // The only way for AI to reactivate cameras are malf abilities, this gives them different messages. + if(istype(user, /mob/living/silicon/ai)) + user = null + + if(choice != 1) + return + + set_status(!src.status) + if (!(src.status)) + if(user) + visible_message(" [user] has deactivated [src]!") + else + visible_message(" [src] clicks and shuts down. ") + playsound(src, 'sound/items/Wirecutter.ogg', 100, 1) + icon_state = "[initial(icon_state)]1" + add_hiddenprint(user) + else + if(user) + visible_message(" [user] has reactivated [src]!") + else + visible_message(" [src] clicks and reactivates itself. ") + playsound(src, 'sound/items/Wirecutter.ogg', 100, 1) + icon_state = initial(icon_state) + add_hiddenprint(user) + +/obj/machinery/camera/take_damage(var/force, var/message) + //prob(25) gives an average of 3-4 hits + if (force >= toughness && (force > toughness*4 || prob(25))) + destroy() + +//Used when someone breaks a camera +/obj/machinery/camera/proc/destroy() + stat |= BROKEN + wires.cut_all() + + triggerCameraAlarm() + update_icon() + update_coverage() + + //sparks + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, loc) + spark_system.start() + playsound(src, "sparks", 50, 1) + +/obj/machinery/camera/proc/set_status(var/newstatus) + if (status != newstatus) + status = newstatus + update_coverage() + +/obj/machinery/camera/check_eye(mob/user) + if(!can_use()) return -1 + if(isXRay()) return SEE_TURFS|SEE_MOBS|SEE_OBJS + return 0 + +/obj/machinery/camera/update_icon() + if (!status || (stat & BROKEN)) + icon_state = "[initial(icon_state)]1" + else if (stat & EMPED) + icon_state = "[initial(icon_state)]emp" + else + icon_state = initial(icon_state) + +/obj/machinery/camera/proc/triggerCameraAlarm(var/duration = 0) + alarm_on = 1 + camera_alarm.triggerAlarm(loc, src, duration) + +/obj/machinery/camera/proc/cancelCameraAlarm() + if(wires.is_cut(WIRE_CAM_ALARM)) + return + + alarm_on = 0 + camera_alarm.clearAlarm(loc, src) + +//if false, then the camera is listed as DEACTIVATED and cannot be used +/obj/machinery/camera/proc/can_use() + if(!status) + return 0 + if(stat & (EMPED|BROKEN)) + return 0 + return 1 + +/obj/machinery/camera/proc/can_see() + var/list/see = null + var/turf/pos = get_turf(src) + if(!pos) + return list() + + if(isXRay()) + see = range(view_range, pos) + else + see = hear(view_range, pos) + return see + +/atom/proc/auto_turn() + //Automatically turns based on nearby walls. + var/turf/simulated/wall/T = null + for(var/i = 1, i <= 8; i += i) + T = get_ranged_target_turf(src, i, 1) + if(istype(T)) + //If someone knows a better way to do this, let me know. -Giacom + switch(i) + if(NORTH) + src.set_dir(SOUTH) + if(SOUTH) + src.set_dir(NORTH) + if(WEST) + src.set_dir(EAST) + if(EAST) + src.set_dir(WEST) + break + +//Return a working camera that can see a given mob +//or null if none +/proc/seen_by_camera(var/mob/M) + for(var/obj/machinery/camera/C in oview(4, M)) + if(C.can_use()) // check if camera disabled + return C + return null + +/proc/near_range_camera(var/mob/M) + + for(var/obj/machinery/camera/C in range(4, M)) + if(C.can_use()) // check if camera disabled + return C + + return null + +/obj/machinery/camera/proc/weld(var/obj/item/weapon/weldingtool/WT, var/mob/user) + WT = WT.get_welder() + + if(busy) + return 0 + if(!WT.isOn()) + return 0 + + // Do after stuff here + to_chat(user, "You start to weld [src]..") + playsound(src, WT.usesound, 50, 1) + WT.eyecheck(user) + busy = 1 + if(do_after(user, 100 * WT.toolspeed)) + busy = 0 + if(!WT.isOn()) + return 0 + return 1 + busy = 0 + return 0 + +/obj/machinery/camera/interact(mob/living/user as mob) + if(!panel_open || istype(user, /mob/living/silicon/ai)) + return + + if(stat & BROKEN) + to_chat(user, "\The [src] is broken.") + return + + user.set_machine(src) + wires.Interact(user) + +/obj/machinery/camera/proc/add_network(var/network_name) + add_networks(list(network_name)) + +/obj/machinery/camera/proc/remove_network(var/network_name) + remove_networks(list(network_name)) + +/obj/machinery/camera/proc/add_networks(var/list/networks) + var/network_added + network_added = 0 + for(var/network_name in networks) + if(!(network_name in src.network)) + network += network_name + network_added = 1 + + if(network_added) + update_coverage(1) + +/obj/machinery/camera/proc/remove_networks(var/list/networks) + var/network_removed + network_removed = 0 + for(var/network_name in networks) + if(network_name in src.network) + network -= network_name + network_removed = 1 + + if(network_removed) + update_coverage(1) + +/obj/machinery/camera/proc/replace_networks(var/list/networks) + if(networks.len != network.len) + network = networks + update_coverage(1) + return + + for(var/new_network in networks) + if(!(new_network in network)) + network = networks + update_coverage(1) + return + +/obj/machinery/camera/proc/clear_all_networks() + if(network.len) + network.Cut() + update_coverage(1) + +/obj/machinery/camera/proc/tgui_structure() + var/cam[0] + cam["name"] = sanitize(c_tag) + cam["deact"] = !can_use() + cam["camera"] = "\ref[src]" + cam["omni"] = always_visible + cam["x"] = x + cam["y"] = y + cam["z"] = z + return cam + +/obj/machinery/camera/proc/update_coverage(var/network_change = 0) + if(network_change) + var/list/open_networks = difflist(network, restricted_camera_networks) + // Add or remove camera from the camera net as necessary + if(on_open_network && !open_networks.len) + cameranet.removeCamera(src) + else if(!on_open_network && open_networks.len) + on_open_network = 1 + cameranet.addCamera(src) + else + cameranet.updateVisibility(src, 0) + +// Resets the camera's wires to fully operational state. Used by one of Malfunction abilities. +/obj/machinery/camera/proc/reset_wires() + if(!wires) + return + if (stat & BROKEN) // Fix the camera + stat &= ~BROKEN + wires.repair() + update_icon() + update_coverage() diff --git a/code/game/machinery/camera/camera_assembly.dm b/code/game/machinery/camera/camera_assembly.dm index 6db595dad44..86bdc11e007 100644 --- a/code/game/machinery/camera/camera_assembly.dm +++ b/code/game/machinery/camera/camera_assembly.dm @@ -1,174 +1,174 @@ -/obj/item/weapon/camera_assembly - name = "camera assembly" - desc = "A pre-fabricated security camera kit, ready to be assembled and mounted to a surface." - icon = 'icons/obj/monitors_vr.dmi' //VOREStation Edit - New Icons - icon_state = "cameracase" - w_class = ITEMSIZE_SMALL - anchored = FALSE - - matter = list(MAT_STEEL = 700,MAT_GLASS = 300) - - // Motion, EMP-Proof, X-Ray - var/list/obj/item/possible_upgrades = list(/obj/item/device/assembly/prox_sensor, /obj/item/stack/material/osmium, /obj/item/weapon/stock_parts/scanning_module) - var/list/upgrades = list() - var/camera_name - var/camera_network - var/state = 0 - var/busy = 0 - /* - 0 = Nothing done to it - 1 = Wrenched in place - 2 = Welded in place - 3 = Wires attached to it (you can now attach/dettach upgrades) - 4 = Screwdriver panel closed and is fully built (you cannot attach upgrades) - */ - -/obj/item/weapon/camera_assembly/attackby(obj/item/W as obj, mob/living/user as mob) - - switch(state) - - if(0) - // State 0 - if(W.has_tool_quality(TOOL_WRENCH) && isturf(src.loc)) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You wrench the assembly into place.") - anchored = TRUE - state = 1 - update_icon() - auto_turn() - return - - if(1) - // State 1 - if(W.has_tool_quality(TOOL_WELDER)) - if(weld(W, user)) - to_chat(user, "You weld the assembly securely into place.") - anchored = TRUE - state = 2 - return - - else if(W.has_tool_quality(TOOL_WRENCH)) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You unattach the assembly from its place.") - anchored = FALSE - update_icon() - state = 0 - return - - if(2) - // State 2 - if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = W - if(C.use(2)) - to_chat(user, "You add wires to the assembly.") - state = 3 - else - to_chat(user, "You need 2 coils of wire to wire the assembly.") - return - - else if(W.has_tool_quality(TOOL_WELDER)) - - if(weld(W, user)) - to_chat(user, "You unweld the assembly from its place.") - state = 1 - anchored = TRUE - return - - - if(3) - // State 3 - if(W.has_tool_quality(TOOL_SCREWDRIVER)) - playsound(src, W.usesound, 50, 1) - - var/input = sanitize(tgui_input_text(usr, "Which networks would you like to connect this camera to? Separate networks with a comma. No Spaces!\nFor example: "+using_map.station_short+",Security,Secret ", "Set Network", camera_network ? camera_network : NETWORK_DEFAULT)) - if(!input) - to_chat(usr, "No input found please hang up and try your call again.") - return - - var/list/tempnetwork = splittext(input, ",") - if(tempnetwork.len < 1) - to_chat(usr, "No network found please hang up and try your call again.") - return - - var/area/camera_area = get_area(src) - var/temptag = "[sanitize(camera_area.name)] ([rand(1, 999)])" - input = sanitizeSafe(tgui_input_text(usr, "How would you like to name the camera?", "Set Camera Name", camera_name ? camera_name : temptag), MAX_NAME_LEN) - - state = 4 - var/obj/machinery/camera/C = new(src.loc) - src.loc = C - C.assembly = src - - C.auto_turn() - - C.replace_networks(uniqueList(tempnetwork)) - - C.c_tag = input - - for(var/i = 5; i >= 0; i -= 1) - var/direct = tgui_input_list(user, "Direction?", "Assembling Camera", list("NORTH", "EAST", "SOUTH", "WEST", "LEAVE IT")) - if(direct != "LEAVE IT") - C.dir = text2dir(direct) - if(i != 0) - var/confirm = tgui_alert(user, "Is this what you want? Chances Remaining: [i]", "Confirmation", list("Yes", "No")) - if(confirm == "Yes") - break - return - - else if(W.has_tool_quality(TOOL_WIRECUTTER)) - - new/obj/item/stack/cable_coil(get_turf(src), 2) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You cut the wires from the circuits.") - state = 2 - return - - // Upgrades! - if(is_type_in_list(W, possible_upgrades) && !is_type_in_list(W, upgrades)) // Is a possible upgrade and isn't in the camera already. - to_chat(user, "You attach \the [W] into the assembly inner circuits.") - upgrades += W - user.remove_from_mob(W) - W.loc = src - return - - // Taking out upgrades - else if(W.has_tool_quality(TOOL_CROWBAR) && upgrades.len) - var/obj/U = locate(/obj) in upgrades - if(U) - to_chat(user, "You unattach an upgrade from the assembly.") - playsound(src, W.usesound, 50, 1) - U.loc = get_turf(src) - upgrades -= U - return - - ..() - -/obj/item/weapon/camera_assembly/update_icon() - if(anchored) - icon_state = "camera1" - else - icon_state = "cameracase" - -/obj/item/weapon/camera_assembly/attack_hand(mob/user as mob) - if(!anchored) - ..() - -/obj/item/weapon/camera_assembly/proc/weld(var/obj/item/weapon/weldingtool/WT, var/mob/user) - WT = WT.get_welder() - - if(busy) - return 0 - if(!WT.isOn()) - return 0 - - to_chat(user, "You start to weld the [src]..") - playsound(src, WT.usesound, 50, 1) - WT.eyecheck(user) - busy = 1 - if(do_after(user, 20 * WT.toolspeed)) - busy = 0 - if(!WT.isOn()) - return 0 - return 1 - busy = 0 - return 0 +/obj/item/weapon/camera_assembly + name = "camera assembly" + desc = "A pre-fabricated security camera kit, ready to be assembled and mounted to a surface." + icon = 'icons/obj/monitors_vr.dmi' //VOREStation Edit - New Icons + icon_state = "cameracase" + w_class = ITEMSIZE_SMALL + anchored = FALSE + + matter = list(MAT_STEEL = 700,MAT_GLASS = 300) + + // Motion, EMP-Proof, X-Ray + var/list/obj/item/possible_upgrades = list(/obj/item/device/assembly/prox_sensor, /obj/item/stack/material/osmium, /obj/item/weapon/stock_parts/scanning_module) + var/list/upgrades = list() + var/camera_name + var/camera_network + var/state = 0 + var/busy = 0 + /* + 0 = Nothing done to it + 1 = Wrenched in place + 2 = Welded in place + 3 = Wires attached to it (you can now attach/dettach upgrades) + 4 = Screwdriver panel closed and is fully built (you cannot attach upgrades) + */ + +/obj/item/weapon/camera_assembly/attackby(obj/item/W as obj, mob/living/user as mob) + + switch(state) + + if(0) + // State 0 + if(W.has_tool_quality(TOOL_WRENCH) && isturf(src.loc)) + playsound(src, W.usesound, 50, 1) + to_chat(user, "You wrench the assembly into place.") + anchored = TRUE + state = 1 + update_icon() + auto_turn() + return + + if(1) + // State 1 + if(W.has_tool_quality(TOOL_WELDER)) + if(weld(W, user)) + to_chat(user, "You weld the assembly securely into place.") + anchored = TRUE + state = 2 + return + + else if(W.has_tool_quality(TOOL_WRENCH)) + playsound(src, W.usesound, 50, 1) + to_chat(user, "You unattach the assembly from its place.") + anchored = FALSE + update_icon() + state = 0 + return + + if(2) + // State 2 + if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = W + if(C.use(2)) + to_chat(user, "You add wires to the assembly.") + state = 3 + else + to_chat(user, "You need 2 coils of wire to wire the assembly.") + return + + else if(W.has_tool_quality(TOOL_WELDER)) + + if(weld(W, user)) + to_chat(user, "You unweld the assembly from its place.") + state = 1 + anchored = TRUE + return + + + if(3) + // State 3 + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(src, W.usesound, 50, 1) + + var/input = sanitize(tgui_input_text(usr, "Which networks would you like to connect this camera to? Separate networks with a comma. No Spaces!\nFor example: "+using_map.station_short+",Security,Secret ", "Set Network", camera_network ? camera_network : NETWORK_DEFAULT)) + if(!input) + to_chat(usr, "No input found please hang up and try your call again.") + return + + var/list/tempnetwork = splittext(input, ",") + if(tempnetwork.len < 1) + to_chat(usr, "No network found please hang up and try your call again.") + return + + var/area/camera_area = get_area(src) + var/temptag = "[sanitize(camera_area.name)] ([rand(1, 999)])" + input = sanitizeSafe(tgui_input_text(usr, "How would you like to name the camera?", "Set Camera Name", camera_name ? camera_name : temptag), MAX_NAME_LEN) + + state = 4 + var/obj/machinery/camera/C = new(src.loc) + src.loc = C + C.assembly = src + + C.auto_turn() + + C.replace_networks(uniqueList(tempnetwork)) + + C.c_tag = input + + for(var/i = 5; i >= 0; i -= 1) + var/direct = tgui_input_list(user, "Direction?", "Assembling Camera", list("NORTH", "EAST", "SOUTH", "WEST", "LEAVE IT")) + if(direct != "LEAVE IT") + C.dir = text2dir(direct) + if(i != 0) + var/confirm = tgui_alert(user, "Is this what you want? Chances Remaining: [i]", "Confirmation", list("Yes", "No")) + if(confirm == "Yes") + break + return + + else if(W.has_tool_quality(TOOL_WIRECUTTER)) + + new/obj/item/stack/cable_coil(get_turf(src), 2) + playsound(src, W.usesound, 50, 1) + to_chat(user, "You cut the wires from the circuits.") + state = 2 + return + + // Upgrades! + if(is_type_in_list(W, possible_upgrades) && !is_type_in_list(W, upgrades)) // Is a possible upgrade and isn't in the camera already. + to_chat(user, "You attach \the [W] into the assembly inner circuits.") + upgrades += W + user.remove_from_mob(W) + W.loc = src + return + + // Taking out upgrades + else if(W.has_tool_quality(TOOL_CROWBAR) && upgrades.len) + var/obj/U = locate(/obj) in upgrades + if(U) + to_chat(user, "You unattach an upgrade from the assembly.") + playsound(src, W.usesound, 50, 1) + U.loc = get_turf(src) + upgrades -= U + return + + ..() + +/obj/item/weapon/camera_assembly/update_icon() + if(anchored) + icon_state = "camera1" + else + icon_state = "cameracase" + +/obj/item/weapon/camera_assembly/attack_hand(mob/user as mob) + if(!anchored) + ..() + +/obj/item/weapon/camera_assembly/proc/weld(var/obj/item/weapon/weldingtool/WT, var/mob/user) + WT = WT.get_welder() + + if(busy) + return 0 + if(!WT.isOn()) + return 0 + + to_chat(user, "You start to weld the [src]..") + playsound(src, WT.usesound, 50, 1) + WT.eyecheck(user) + busy = 1 + if(do_after(user, 20 * WT.toolspeed)) + busy = 0 + if(!WT.isOn()) + return 0 + return 1 + busy = 0 + return 0 diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm index ad183a7be83..00bfeb09cc4 100644 --- a/code/game/machinery/camera/motion.dm +++ b/code/game/machinery/camera/motion.dm @@ -1,62 +1,62 @@ -/obj/machinery/camera - var/list/motionTargets = list() - var/detectTime = 0 - var/area/ai_monitored/area_motion = null - var/alarm_delay = 100 // Don't forget, there's another 10 seconds in queueAlarm() - -/obj/machinery/camera/internal_process() - // motion camera event loop - if (stat & (EMPED|NOPOWER)) - return - if(!isMotion()) - return PROCESS_KILL - if (detectTime > 0) - var/elapsed = world.time - detectTime - if (elapsed > alarm_delay) - triggerAlarm() - else if (detectTime == -1) - for (var/mob/target in motionTargets) - if (target.stat == 2) lostTarget(target) - // If not detecting with motion camera... - if (!area_motion) - // See if the camera is still in range - if(!in_range(src, target)) - // If they aren't in range, lose the target. - lostTarget(target) - -/obj/machinery/camera/proc/newTarget(var/mob/target) - if (istype(target, /mob/living/silicon/ai)) return 0 - if (detectTime == 0) - detectTime = world.time // start the clock - if (!(target in motionTargets)) - motionTargets += target - return 1 - -/obj/machinery/camera/proc/lostTarget(var/mob/target) - if (target in motionTargets) - motionTargets -= target - if (motionTargets.len == 0) - cancelAlarm() - -/obj/machinery/camera/proc/cancelAlarm() - if (!status || (stat & NOPOWER)) - return 0 - if (detectTime == -1) - motion_alarm.clearAlarm(loc, src) - detectTime = 0 - return 1 - -/obj/machinery/camera/proc/triggerAlarm() - if (!status || (stat & NOPOWER)) - return 0 - if (!detectTime) return 0 - motion_alarm.triggerAlarm(loc, src) - detectTime = -1 - return 1 - -/obj/machinery/camera/HasProximity(turf/T, atom/movable/AM, old_loc) - // Motion cameras outside of an "ai monitored" area will use this to detect stuff. - if (!area_motion) - if(isliving(AM)) - newTarget(AM) - +/obj/machinery/camera + var/list/motionTargets = list() + var/detectTime = 0 + var/area/ai_monitored/area_motion = null + var/alarm_delay = 100 // Don't forget, there's another 10 seconds in queueAlarm() + +/obj/machinery/camera/internal_process() + // motion camera event loop + if (stat & (EMPED|NOPOWER)) + return + if(!isMotion()) + return PROCESS_KILL + if (detectTime > 0) + var/elapsed = world.time - detectTime + if (elapsed > alarm_delay) + triggerAlarm() + else if (detectTime == -1) + for (var/mob/target in motionTargets) + if (target.stat == 2) lostTarget(target) + // If not detecting with motion camera... + if (!area_motion) + // See if the camera is still in range + if(!in_range(src, target)) + // If they aren't in range, lose the target. + lostTarget(target) + +/obj/machinery/camera/proc/newTarget(var/mob/target) + if (istype(target, /mob/living/silicon/ai)) return 0 + if (detectTime == 0) + detectTime = world.time // start the clock + if (!(target in motionTargets)) + motionTargets += target + return 1 + +/obj/machinery/camera/proc/lostTarget(var/mob/target) + if (target in motionTargets) + motionTargets -= target + if (motionTargets.len == 0) + cancelAlarm() + +/obj/machinery/camera/proc/cancelAlarm() + if (!status || (stat & NOPOWER)) + return 0 + if (detectTime == -1) + motion_alarm.clearAlarm(loc, src) + detectTime = 0 + return 1 + +/obj/machinery/camera/proc/triggerAlarm() + if (!status || (stat & NOPOWER)) + return 0 + if (!detectTime) return 0 + motion_alarm.triggerAlarm(loc, src) + detectTime = -1 + return 1 + +/obj/machinery/camera/HasProximity(turf/T, atom/movable/AM, old_loc) + // Motion cameras outside of an "ai monitored" area will use this to detect stuff. + if (!area_motion) + if(isliving(AM)) + newTarget(AM) + diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index 7f997919e06..909827bb8cd 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -1,250 +1,250 @@ -// PRESETS -/* -var/global/list/station_networks = list( -// NETWORK_CAFE_DOCK, - NETWORK_CARGO, - NETWORK_CIVILIAN, -// NETWORK_CIVILIAN_EAST, -// NETWORK_CIVILIAN_WEST, - NETWORK_COMMAND, - NETWORK_ENGINE, - NETWORK_ENGINEERING, - NETWORK_ENGINEERING_OUTPOST, - NETWORK_DEFAULT, - NETWORK_MEDICAL, - NETWORK_MINE, - NETWORK_NORTHERN_STAR, - NETWORK_RESEARCH, - NETWORK_RESEARCH_OUTPOST, - NETWORK_ROBOTS, - NETWORK_PRISON, - NETWORK_SECURITY, - NETWORK_INTERROGATION - ) -*/ -var/global/list/engineering_networks = list( - NETWORK_ENGINE, - NETWORK_SUBSTATIONS, //YAWN ADD: new substations subnet - NETWORK_ENGINEERING, - //NETWORK_ENGINEERING_OUTPOST, //VOREStation Edit: Tether has no Engineering Outpost, - NETWORK_ALARM_ATMOS, - NETWORK_ALARM_FIRE, - NETWORK_ALARM_POWER) -/obj/machinery/camera/network/crescent - network = list(NETWORK_CRESCENT) - -/* -/obj/machinery/camera/network/cafe_dock - network = list(NETWORK_CAFE_DOCK) -*/ - -/obj/machinery/camera/network/cargo - network = list(NETWORK_CARGO) - -/obj/machinery/camera/network/civilian - network = list(NETWORK_CIVILIAN) - -/obj/machinery/camera/network/circuits - network = list(NETWORK_CIRCUITS) - -/* -/obj/machinery/camera/network/civilian_east - network = list(NETWORK_CIVILIAN_EAST) - -/obj/machinery/camera/network/civilian_west - network = list(NETWORK_CIVILIAN_WEST) -*/ - -/obj/machinery/camera/network/command - network = list(NETWORK_COMMAND) - -/obj/machinery/camera/network/engine - network = list(NETWORK_ENGINE) - -/obj/machinery/camera/network/engineering - network = list(NETWORK_ENGINEERING) - -/obj/machinery/camera/network/engineering_outpost - network = list(NETWORK_ENGINEERING_OUTPOST) - -/obj/machinery/camera/network/ert - network = list(NETWORK_ERT) - -/obj/machinery/camera/network/exodus - network = list(NETWORK_DEFAULT) - -/obj/machinery/camera/network/interrogation - network = list(NETWORK_INTERROGATION) - -/obj/machinery/camera/network/mining - network = list(NETWORK_MINE) - -/obj/machinery/camera/network/northern_star - network = list(NETWORK_NORTHERN_STAR) - -/obj/machinery/camera/network/outside - network = list(NETWORK_OUTSIDE) - -/obj/machinery/camera/network/prison - network = list(NETWORK_PRISON) - -/obj/machinery/camera/network/medbay - network = list(NETWORK_MEDICAL) - -/obj/machinery/camera/network/research - network = list(NETWORK_RESEARCH) - -/obj/machinery/camera/network/exploration //yw edit - network = list(NETWORK_EXPLORATION) - -/obj/machinery/camera/network/research_outpost - network = list(NETWORK_RESEARCH_OUTPOST) - -/obj/machinery/camera/network/security - network = list(NETWORK_SECURITY) - -/obj/machinery/camera/network/substations - network = list(NETWORK_SUBSTATIONS) - -/obj/machinery/camera/network/telecom - network = list(NETWORK_TCOMMS) //yw edit - -/obj/machinery/camera/network/exploration - network = list(NETWORK_EXPLORATION) - -/obj/machinery/camera/network/research/xenobio - network = list(NETWORK_RESEARCH, NETWORK_XENOBIO) - -/obj/machinery/camera/network/thunder - network = list(NETWORK_THUNDER) - invuln = 1 - always_visible = TRUE - -// EMP - -/obj/machinery/camera/emp_proof/New() - ..() - upgradeEmpProof() - -// X-RAY - -/obj/machinery/camera/xray - icon_state = "xraycam" // Thanks to Krutchen for the icons. - -/obj/machinery/camera/xray/command - network = list(NETWORK_COMMAND) - -/obj/machinery/camera/xray/security - network = list(NETWORK_SECURITY) - -/obj/machinery/camera/xray/medbay - network = list(NETWORK_MEDICAL) - -/obj/machinery/camera/xray/research - network = list(NETWORK_RESEARCH) - -/obj/machinery/camera/xray/New() - ..() - upgradeXRay() - -// MOTION - -/obj/machinery/camera/motion/New() - ..() - upgradeMotion() - -/obj/machinery/camera/motion/engineering_outpost - network = list(NETWORK_ENGINEERING_OUTPOST) - -/obj/machinery/camera/motion/security - network = list(NETWORK_SECURITY) - -/obj/machinery/camera/motion/command - network = list(NETWORK_COMMAND) - -/obj/machinery/camera/motion/telecom - network = list(NETWORK_TCOMMS) //yw edit - -// ALL UPGRADES - - -/obj/machinery/camera/all/command - network = list(NETWORK_COMMAND) - -/obj/machinery/camera/all/New() - ..() - upgradeEmpProof() - upgradeXRay() - upgradeMotion() - -// AUTONAME -/obj/machinery/camera/autoname - var/static/list/by_area - -/obj/machinery/camera/autoname/Initialize() - . = ..() - var/area/A = get_area(src) - if(!A) - return . - if(!by_area) - by_area = list() - if(!by_area[A.name]) - by_area[A.name] = list() - var/list/my_area = by_area[A.name] - my_area += src - var/number = my_area.len - - c_tag = "[A.name] #[number]" - -/obj/machinery/camera/autoname/Destroy() - var/area/A = get_area(src) - if(!A || !by_area || !by_area[A.name]) - return ..() - var/list/my_area = by_area[A.name] - my_area -= src - return ..() - -// CHECKS - -/obj/machinery/camera/proc/isEmpProof() - var/O = locate(/obj/item/stack/material/osmium) in assembly.upgrades - return O - -/obj/machinery/camera/proc/isXRay() - var/obj/item/weapon/stock_parts/scanning_module/O = locate(/obj/item/weapon/stock_parts/scanning_module) in assembly.upgrades - if (O && O.rating >= 2) - return O - return null - -/obj/machinery/camera/proc/isMotion() - var/O = locate(/obj/item/device/assembly/prox_sensor) in assembly.upgrades - return O - -// UPGRADE PROCS - -/obj/machinery/camera/proc/upgradeEmpProof() - assembly.upgrades.Add(new /obj/item/stack/material/osmium(assembly)) - setPowerUsage() - update_coverage() - -/obj/machinery/camera/proc/upgradeXRay() - assembly.upgrades.Add(new /obj/item/weapon/stock_parts/scanning_module/adv(assembly)) - setPowerUsage() - update_coverage() - -/obj/machinery/camera/proc/upgradeMotion() - if(!isturf(loc)) - return //nooooo - assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly)) - setPowerUsage() - START_MACHINE_PROCESSING(src) - sense_proximity(callback = /atom/proc/HasProximity) - update_coverage() - -/obj/machinery/camera/proc/setPowerUsage() - var/mult = 1 - if (isXRay()) - mult++ - if (isMotion()) - mult++ - update_active_power_usage(mult * initial(active_power_usage)) +// PRESETS +/* +var/global/list/station_networks = list( +// NETWORK_CAFE_DOCK, + NETWORK_CARGO, + NETWORK_CIVILIAN, +// NETWORK_CIVILIAN_EAST, +// NETWORK_CIVILIAN_WEST, + NETWORK_COMMAND, + NETWORK_ENGINE, + NETWORK_ENGINEERING, + NETWORK_ENGINEERING_OUTPOST, + NETWORK_DEFAULT, + NETWORK_MEDICAL, + NETWORK_MINE, + NETWORK_NORTHERN_STAR, + NETWORK_RESEARCH, + NETWORK_RESEARCH_OUTPOST, + NETWORK_ROBOTS, + NETWORK_PRISON, + NETWORK_SECURITY, + NETWORK_INTERROGATION + ) +*/ +var/global/list/engineering_networks = list( + NETWORK_ENGINE, + NETWORK_SUBSTATIONS, //YAWN ADD: new substations subnet + NETWORK_ENGINEERING, + //NETWORK_ENGINEERING_OUTPOST, //VOREStation Edit: Tether has no Engineering Outpost, + NETWORK_ALARM_ATMOS, + NETWORK_ALARM_FIRE, + NETWORK_ALARM_POWER) +/obj/machinery/camera/network/crescent + network = list(NETWORK_CRESCENT) + +/* +/obj/machinery/camera/network/cafe_dock + network = list(NETWORK_CAFE_DOCK) +*/ + +/obj/machinery/camera/network/cargo + network = list(NETWORK_CARGO) + +/obj/machinery/camera/network/civilian + network = list(NETWORK_CIVILIAN) + +/obj/machinery/camera/network/circuits + network = list(NETWORK_CIRCUITS) + +/* +/obj/machinery/camera/network/civilian_east + network = list(NETWORK_CIVILIAN_EAST) + +/obj/machinery/camera/network/civilian_west + network = list(NETWORK_CIVILIAN_WEST) +*/ + +/obj/machinery/camera/network/command + network = list(NETWORK_COMMAND) + +/obj/machinery/camera/network/engine + network = list(NETWORK_ENGINE) + +/obj/machinery/camera/network/engineering + network = list(NETWORK_ENGINEERING) + +/obj/machinery/camera/network/engineering_outpost + network = list(NETWORK_ENGINEERING_OUTPOST) + +/obj/machinery/camera/network/ert + network = list(NETWORK_ERT) + +/obj/machinery/camera/network/exodus + network = list(NETWORK_DEFAULT) + +/obj/machinery/camera/network/interrogation + network = list(NETWORK_INTERROGATION) + +/obj/machinery/camera/network/mining + network = list(NETWORK_MINE) + +/obj/machinery/camera/network/northern_star + network = list(NETWORK_NORTHERN_STAR) + +/obj/machinery/camera/network/outside + network = list(NETWORK_OUTSIDE) + +/obj/machinery/camera/network/prison + network = list(NETWORK_PRISON) + +/obj/machinery/camera/network/medbay + network = list(NETWORK_MEDICAL) + +/obj/machinery/camera/network/research + network = list(NETWORK_RESEARCH) + +/obj/machinery/camera/network/exploration //yw edit + network = list(NETWORK_EXPLORATION) + +/obj/machinery/camera/network/research_outpost + network = list(NETWORK_RESEARCH_OUTPOST) + +/obj/machinery/camera/network/security + network = list(NETWORK_SECURITY) + +/obj/machinery/camera/network/substations + network = list(NETWORK_SUBSTATIONS) + +/obj/machinery/camera/network/telecom + network = list(NETWORK_TCOMMS) //yw edit + +/obj/machinery/camera/network/exploration + network = list(NETWORK_EXPLORATION) + +/obj/machinery/camera/network/research/xenobio + network = list(NETWORK_RESEARCH, NETWORK_XENOBIO) + +/obj/machinery/camera/network/thunder + network = list(NETWORK_THUNDER) + invuln = 1 + always_visible = TRUE + +// EMP + +/obj/machinery/camera/emp_proof/New() + ..() + upgradeEmpProof() + +// X-RAY + +/obj/machinery/camera/xray + icon_state = "xraycam" // Thanks to Krutchen for the icons. + +/obj/machinery/camera/xray/command + network = list(NETWORK_COMMAND) + +/obj/machinery/camera/xray/security + network = list(NETWORK_SECURITY) + +/obj/machinery/camera/xray/medbay + network = list(NETWORK_MEDICAL) + +/obj/machinery/camera/xray/research + network = list(NETWORK_RESEARCH) + +/obj/machinery/camera/xray/New() + ..() + upgradeXRay() + +// MOTION + +/obj/machinery/camera/motion/New() + ..() + upgradeMotion() + +/obj/machinery/camera/motion/engineering_outpost + network = list(NETWORK_ENGINEERING_OUTPOST) + +/obj/machinery/camera/motion/security + network = list(NETWORK_SECURITY) + +/obj/machinery/camera/motion/command + network = list(NETWORK_COMMAND) + +/obj/machinery/camera/motion/telecom + network = list(NETWORK_TCOMMS) //yw edit + +// ALL UPGRADES + + +/obj/machinery/camera/all/command + network = list(NETWORK_COMMAND) + +/obj/machinery/camera/all/New() + ..() + upgradeEmpProof() + upgradeXRay() + upgradeMotion() + +// AUTONAME +/obj/machinery/camera/autoname + var/static/list/by_area + +/obj/machinery/camera/autoname/Initialize() + . = ..() + var/area/A = get_area(src) + if(!A) + return . + if(!by_area) + by_area = list() + if(!by_area[A.name]) + by_area[A.name] = list() + var/list/my_area = by_area[A.name] + my_area += src + var/number = my_area.len + + c_tag = "[A.name] #[number]" + +/obj/machinery/camera/autoname/Destroy() + var/area/A = get_area(src) + if(!A || !by_area || !by_area[A.name]) + return ..() + var/list/my_area = by_area[A.name] + my_area -= src + return ..() + +// CHECKS + +/obj/machinery/camera/proc/isEmpProof() + var/O = locate(/obj/item/stack/material/osmium) in assembly.upgrades + return O + +/obj/machinery/camera/proc/isXRay() + var/obj/item/weapon/stock_parts/scanning_module/O = locate(/obj/item/weapon/stock_parts/scanning_module) in assembly.upgrades + if (O && O.rating >= 2) + return O + return null + +/obj/machinery/camera/proc/isMotion() + var/O = locate(/obj/item/device/assembly/prox_sensor) in assembly.upgrades + return O + +// UPGRADE PROCS + +/obj/machinery/camera/proc/upgradeEmpProof() + assembly.upgrades.Add(new /obj/item/stack/material/osmium(assembly)) + setPowerUsage() + update_coverage() + +/obj/machinery/camera/proc/upgradeXRay() + assembly.upgrades.Add(new /obj/item/weapon/stock_parts/scanning_module/adv(assembly)) + setPowerUsage() + update_coverage() + +/obj/machinery/camera/proc/upgradeMotion() + if(!isturf(loc)) + return //nooooo + assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly)) + setPowerUsage() + START_MACHINE_PROCESSING(src) + sense_proximity(callback = /atom/proc/HasProximity) + update_coverage() + +/obj/machinery/camera/proc/setPowerUsage() + var/mult = 1 + if (isXRay()) + mult++ + if (isMotion()) + mult++ + update_active_power_usage(mult * initial(active_power_usage)) diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm index d53e1fdbb73..8b1ec4256bf 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -1,285 +1,285 @@ -#define TRACKING_POSSIBLE 0 -#define TRACKING_NO_COVERAGE 1 -#define TRACKING_TERMINATE 2 - -/mob/living/silicon/ai/var/max_locations = 30 -/mob/living/silicon/ai/var/stored_locations[0] - -/proc/InvalidPlayerTurf(turf/T as turf) - return !(T?.z in using_map.player_levels) - -/mob/living/silicon/ai/proc/get_camera_list() - if(src.stat == 2) - return - - cameranet.process_sort() - - var/list/T = list() - for (var/obj/machinery/camera/C in cameranet.cameras) - var/list/tempnetwork = C.network&src.network - if (tempnetwork.len) - T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C - - track = new() - track.cameras = T - return T - - -/mob/living/silicon/ai/proc/ai_camera_list(var/camera in get_camera_list()) - set category = "AI Commands" - set name = "Show Camera List" - - if(check_unable()) - return - - if (!camera) - return 0 - - var/obj/machinery/camera/C = track.cameras[camera] - src.eyeobj.setLoc(C) - - return - -/mob/living/silicon/ai/proc/ai_store_location(loc as text) - set category = "AI Commands" - set name = "Store Camera Location" - set desc = "Stores your current camera location by the given name" - - loc = sanitize(loc) - if(!loc) - to_chat(src, "Must supply a location name") - return - - if(stored_locations.len >= max_locations) - to_chat(src, "Cannot store additional locations. Remove one first") - return - - if(loc in stored_locations) - to_chat(src, "There is already a stored location by this name") - return - - var/L = src.eyeobj.getLoc() - if (InvalidPlayerTurf(get_turf(L))) - to_chat(src, "Unable to store this location") - return - - stored_locations[loc] = L - to_chat(src, "Location '[loc]' stored") - -/mob/living/silicon/ai/proc/sorted_stored_locations() - return sortList(stored_locations) - -/mob/living/silicon/ai/proc/ai_goto_location(loc in sorted_stored_locations()) - set category = "AI Commands" - set name = "Goto Camera Location" - set desc = "Returns to the selected camera location" - - if (!(loc in stored_locations)) - to_chat(src, "Location [loc] not found") - return - - var/L = stored_locations[loc] - src.eyeobj.setLoc(L) - -/mob/living/silicon/ai/proc/ai_remove_location(loc in sorted_stored_locations()) - set category = "AI Commands" - set name = "Delete Camera Location" - set desc = "Deletes the selected camera location" - - if (!(loc in stored_locations)) - to_chat(src, "Location [loc] not found") - return - - stored_locations.Remove(loc) - to_chat(src, "Location [loc] removed") - -// Used to allow the AI is write in mob names/camera name from the CMD line. -/datum/trackable - var/list/names = list() - var/list/namecounts = list() - var/list/humans = list() - var/list/others = list() - var/list/cameras = list() - -/mob/living/silicon/ai/proc/trackable_mobs() - if(usr.stat == 2) - return list() - - var/datum/trackable/TB = new() - for(var/mob/living/M in mob_list) - if(M == usr) - continue - if(M.tracking_status() != TRACKING_POSSIBLE) - continue - - var/name = M.name - if (name in TB.names) - TB.namecounts[name]++ - name = text("[] ([])", name, TB.namecounts[name]) - else - TB.names.Add(name) - TB.namecounts[name] = 1 - if(istype(M, /mob/living/carbon/human)) - TB.humans[name] = M - else - TB.others[name] = M - - var/list/targets = sortList(TB.humans) + sortList(TB.others) - src.track = TB - return targets - -/mob/living/silicon/ai/proc/ai_camera_track(var/target_name in trackable_mobs()) - set category = "AI Commands" - set name = "Follow With Camera" - set desc = "Select who you would like to track." - - if(src.stat == 2) - to_chat(src, "You can't follow [target_name] with cameras because you are dead!") - return - if(!target_name) - src.cameraFollow = null - - var/mob/target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name]) - src.track = null - ai_actual_track(target) - -/mob/living/silicon/ai/proc/ai_cancel_tracking(var/forced = 0) - if(!cameraFollow) - return - - to_chat(src, "Follow camera mode [forced ? "terminated" : "ended"].") - cameraFollow.tracking_cancelled() - cameraFollow = null - -/mob/living/silicon/ai/proc/ai_actual_track(mob/living/target as mob) - if(!istype(target)) return FALSE - var/mob/living/silicon/ai/U = usr - - if(target == U.cameraFollow) - return TRUE - - if(U.cameraFollow) - U.ai_cancel_tracking() - U.cameraFollow = target - to_chat(U, "Now tracking [target.name] on camera.") - target.tracking_initiated() - - spawn (0) - while (U.cameraFollow == target) - if (U.cameraFollow == null) - return - - switch(target.tracking_status()) - if(TRACKING_NO_COVERAGE) - to_chat(U, "Target is not near any active cameras.") - sleep(100) - continue - if(TRACKING_TERMINATE) - U.ai_cancel_tracking(1) - return - - if(U.eyeobj) - U.eyeobj.setLoc(get_turf(target), 0) - else - view_core() - return - sleep(10) - - return TRUE - -/obj/machinery/camera/attack_ai(var/mob/living/silicon/ai/user as mob) - if (!istype(user)) - return - if (!src.can_use()) - return - user.eyeobj.setLoc(get_turf(src)) - - -/mob/living/silicon/ai/attack_ai(var/mob/user as mob) - ai_camera_list() - -/proc/camera_sort(list/L) - var/obj/machinery/camera/a - var/obj/machinery/camera/b - - for (var/i = L.len, i > 0, i--) - for (var/j = 1 to i - 1) - a = L[j] - b = L[j + 1] - if (a.c_tag_order != b.c_tag_order) - if (a.c_tag_order > b.c_tag_order) - L.Swap(j, j + 1) - else - if (sorttext(a.c_tag, b.c_tag) < 0) - L.Swap(j, j + 1) - return L - - -/mob/living/proc/near_camera() - if (!isturf(loc)) - return 0 - else if(!cameranet.checkVis(src)) - return 0 - return 1 - -/mob/living/proc/tracking_status() - // Easy checks first. - // Don't detect mobs on CentCom. Since the wizard den is on CentCom, we only need this. - var/obj/item/weapon/card/id/id = GetIdCard() - if(id && id.prevent_tracking()) - return TRACKING_TERMINATE - var/turf/pos = get_turf(src) - var/area/B = pos?.loc // No cam tracking in dorms! - if(InvalidPlayerTurf(pos) || B.block_tracking) - return TRACKING_TERMINATE - if(invisibility >= INVISIBILITY_LEVEL_ONE) //cloaked - return TRACKING_TERMINATE - if(digitalcamo) - return TRACKING_TERMINATE - if(alpha < 127) // For lings and possible future alpha-based cloaks. - return TRACKING_TERMINATE - if(istype(loc,/obj/effect/dummy)) - return TRACKING_TERMINATE - - // Now, are they viewable by a camera? (This is last because it's the most intensive check) - return near_camera() ? TRACKING_POSSIBLE : TRACKING_NO_COVERAGE - -/mob/living/silicon/robot/tracking_status() - . = ..() - if(. == TRACKING_NO_COVERAGE) - return camera && camera.can_use() ? TRACKING_POSSIBLE : TRACKING_NO_COVERAGE - -/mob/living/carbon/human/tracking_status() - //Cameras can't track people wearing an agent card or a ninja hood. - if(istype(head, /obj/item/clothing/head/helmet/space/rig)) - var/obj/item/clothing/head/helmet/space/rig/helmet = head - if(helmet.prevent_track()) - return TRACKING_TERMINATE - - . = ..() - if(. == TRACKING_TERMINATE) - return - - if(. == TRACKING_NO_COVERAGE) - var/turf/T = get_turf(src) - if(T && (T.z in using_map.station_levels) && hassensorlevel(src, SUIT_SENSOR_TRACKING)) - return TRACKING_POSSIBLE - -/mob/living/proc/tracking_initiated() - -/mob/living/silicon/robot/tracking_initiated() - tracking_entities++ - if(tracking_entities == 1 && has_zeroth_law()) - to_chat(src, "Internal camera is currently being accessed.") - -/mob/living/proc/tracking_cancelled() - -/mob/living/silicon/robot/tracking_initiated() - tracking_entities-- - if(!tracking_entities && has_zeroth_law()) - to_chat(src, "Internal camera is no longer being accessed.") - - -#undef TRACKING_POSSIBLE -#undef TRACKING_NO_COVERAGE -#undef TRACKING_TERMINATE +#define TRACKING_POSSIBLE 0 +#define TRACKING_NO_COVERAGE 1 +#define TRACKING_TERMINATE 2 + +/mob/living/silicon/ai/var/max_locations = 30 +/mob/living/silicon/ai/var/stored_locations[0] + +/proc/InvalidPlayerTurf(turf/T as turf) + return !(T?.z in using_map.player_levels) + +/mob/living/silicon/ai/proc/get_camera_list() + if(src.stat == 2) + return + + cameranet.process_sort() + + var/list/T = list() + for (var/obj/machinery/camera/C in cameranet.cameras) + var/list/tempnetwork = C.network&src.network + if (tempnetwork.len) + T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C + + track = new() + track.cameras = T + return T + + +/mob/living/silicon/ai/proc/ai_camera_list(var/camera in get_camera_list()) + set category = "AI Commands" + set name = "Show Camera List" + + if(check_unable()) + return + + if (!camera) + return 0 + + var/obj/machinery/camera/C = track.cameras[camera] + src.eyeobj.setLoc(C) + + return + +/mob/living/silicon/ai/proc/ai_store_location(loc as text) + set category = "AI Commands" + set name = "Store Camera Location" + set desc = "Stores your current camera location by the given name" + + loc = sanitize(loc) + if(!loc) + to_chat(src, "Must supply a location name") + return + + if(stored_locations.len >= max_locations) + to_chat(src, "Cannot store additional locations. Remove one first") + return + + if(loc in stored_locations) + to_chat(src, "There is already a stored location by this name") + return + + var/L = src.eyeobj.getLoc() + if (InvalidPlayerTurf(get_turf(L))) + to_chat(src, "Unable to store this location") + return + + stored_locations[loc] = L + to_chat(src, "Location '[loc]' stored") + +/mob/living/silicon/ai/proc/sorted_stored_locations() + return sortList(stored_locations) + +/mob/living/silicon/ai/proc/ai_goto_location(loc in sorted_stored_locations()) + set category = "AI Commands" + set name = "Goto Camera Location" + set desc = "Returns to the selected camera location" + + if (!(loc in stored_locations)) + to_chat(src, "Location [loc] not found") + return + + var/L = stored_locations[loc] + src.eyeobj.setLoc(L) + +/mob/living/silicon/ai/proc/ai_remove_location(loc in sorted_stored_locations()) + set category = "AI Commands" + set name = "Delete Camera Location" + set desc = "Deletes the selected camera location" + + if (!(loc in stored_locations)) + to_chat(src, "Location [loc] not found") + return + + stored_locations.Remove(loc) + to_chat(src, "Location [loc] removed") + +// Used to allow the AI is write in mob names/camera name from the CMD line. +/datum/trackable + var/list/names = list() + var/list/namecounts = list() + var/list/humans = list() + var/list/others = list() + var/list/cameras = list() + +/mob/living/silicon/ai/proc/trackable_mobs() + if(usr.stat == 2) + return list() + + var/datum/trackable/TB = new() + for(var/mob/living/M in mob_list) + if(M == usr) + continue + if(M.tracking_status() != TRACKING_POSSIBLE) + continue + + var/name = M.name + if (name in TB.names) + TB.namecounts[name]++ + name = text("[] ([])", name, TB.namecounts[name]) + else + TB.names.Add(name) + TB.namecounts[name] = 1 + if(istype(M, /mob/living/carbon/human)) + TB.humans[name] = M + else + TB.others[name] = M + + var/list/targets = sortList(TB.humans) + sortList(TB.others) + src.track = TB + return targets + +/mob/living/silicon/ai/proc/ai_camera_track(var/target_name in trackable_mobs()) + set category = "AI Commands" + set name = "Follow With Camera" + set desc = "Select who you would like to track." + + if(src.stat == 2) + to_chat(src, "You can't follow [target_name] with cameras because you are dead!") + return + if(!target_name) + src.cameraFollow = null + + var/mob/target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name]) + src.track = null + ai_actual_track(target) + +/mob/living/silicon/ai/proc/ai_cancel_tracking(var/forced = 0) + if(!cameraFollow) + return + + to_chat(src, "Follow camera mode [forced ? "terminated" : "ended"].") + cameraFollow.tracking_cancelled() + cameraFollow = null + +/mob/living/silicon/ai/proc/ai_actual_track(mob/living/target as mob) + if(!istype(target)) return FALSE + var/mob/living/silicon/ai/U = usr + + if(target == U.cameraFollow) + return TRUE + + if(U.cameraFollow) + U.ai_cancel_tracking() + U.cameraFollow = target + to_chat(U, "Now tracking [target.name] on camera.") + target.tracking_initiated() + + spawn (0) + while (U.cameraFollow == target) + if (U.cameraFollow == null) + return + + switch(target.tracking_status()) + if(TRACKING_NO_COVERAGE) + to_chat(U, "Target is not near any active cameras.") + sleep(100) + continue + if(TRACKING_TERMINATE) + U.ai_cancel_tracking(1) + return + + if(U.eyeobj) + U.eyeobj.setLoc(get_turf(target), 0) + else + view_core() + return + sleep(10) + + return TRUE + +/obj/machinery/camera/attack_ai(var/mob/living/silicon/ai/user as mob) + if (!istype(user)) + return + if (!src.can_use()) + return + user.eyeobj.setLoc(get_turf(src)) + + +/mob/living/silicon/ai/attack_ai(var/mob/user as mob) + ai_camera_list() + +/proc/camera_sort(list/L) + var/obj/machinery/camera/a + var/obj/machinery/camera/b + + for (var/i = L.len, i > 0, i--) + for (var/j = 1 to i - 1) + a = L[j] + b = L[j + 1] + if (a.c_tag_order != b.c_tag_order) + if (a.c_tag_order > b.c_tag_order) + L.Swap(j, j + 1) + else + if (sorttext(a.c_tag, b.c_tag) < 0) + L.Swap(j, j + 1) + return L + + +/mob/living/proc/near_camera() + if (!isturf(loc)) + return 0 + else if(!cameranet.checkVis(src)) + return 0 + return 1 + +/mob/living/proc/tracking_status() + // Easy checks first. + // Don't detect mobs on CentCom. Since the wizard den is on CentCom, we only need this. + var/obj/item/weapon/card/id/id = GetIdCard() + if(id && id.prevent_tracking()) + return TRACKING_TERMINATE + var/turf/pos = get_turf(src) + var/area/B = pos?.loc // No cam tracking in dorms! + if(InvalidPlayerTurf(pos) || B.block_tracking) + return TRACKING_TERMINATE + if(invisibility >= INVISIBILITY_LEVEL_ONE) //cloaked + return TRACKING_TERMINATE + if(digitalcamo) + return TRACKING_TERMINATE + if(alpha < 127) // For lings and possible future alpha-based cloaks. + return TRACKING_TERMINATE + if(istype(loc,/obj/effect/dummy)) + return TRACKING_TERMINATE + + // Now, are they viewable by a camera? (This is last because it's the most intensive check) + return near_camera() ? TRACKING_POSSIBLE : TRACKING_NO_COVERAGE + +/mob/living/silicon/robot/tracking_status() + . = ..() + if(. == TRACKING_NO_COVERAGE) + return camera && camera.can_use() ? TRACKING_POSSIBLE : TRACKING_NO_COVERAGE + +/mob/living/carbon/human/tracking_status() + //Cameras can't track people wearing an agent card or a ninja hood. + if(istype(head, /obj/item/clothing/head/helmet/space/rig)) + var/obj/item/clothing/head/helmet/space/rig/helmet = head + if(helmet.prevent_track()) + return TRACKING_TERMINATE + + . = ..() + if(. == TRACKING_TERMINATE) + return + + if(. == TRACKING_NO_COVERAGE) + var/turf/T = get_turf(src) + if(T && (T.z in using_map.station_levels) && hassensorlevel(src, SUIT_SENSOR_TRACKING)) + return TRACKING_POSSIBLE + +/mob/living/proc/tracking_initiated() + +/mob/living/silicon/robot/tracking_initiated() + tracking_entities++ + if(tracking_entities == 1 && has_zeroth_law()) + to_chat(src, "Internal camera is currently being accessed.") + +/mob/living/proc/tracking_cancelled() + +/mob/living/silicon/robot/tracking_initiated() + tracking_entities-- + if(!tracking_entities && has_zeroth_law()) + to_chat(src, "Internal camera is no longer being accessed.") + + +#undef TRACKING_POSSIBLE +#undef TRACKING_NO_COVERAGE +#undef TRACKING_TERMINATE diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm index 38ceea9f4b9..28255c395ad 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -1,140 +1,140 @@ -/obj/machinery/cell_charger - name = "heavy-duty cell charger" - desc = "A much more powerful version of the standard recharger that is specially designed for charging power cells." - icon = 'icons/obj/power.dmi' - icon_state = "ccharger0" - anchored = 1 - use_power = USE_POWER_IDLE - idle_power_usage = 5 - active_power_usage = 60000 //60 kW. (this the power drawn when charging) - var/efficiency = 60000 //will provide the modified power rate when upgraded - power_channel = EQUIP - var/obj/item/weapon/cell/charging = null - var/chargelevel = -1 - circuit = /obj/item/weapon/circuitboard/cell_charger - -/obj/machinery/cell_charger/Initialize() - . = ..() - default_apply_parts() - add_overlay("ccharger1") - -/obj/machinery/cell_charger/update_icon() - if(!anchored) - cut_overlays() - icon_state = "ccharger2" - - if(charging && !(stat & (BROKEN|NOPOWER))) - var/newlevel = round(charging.percent() * 4.0 / 99) - //to_world("nl: [newlevel]") - - if(chargelevel != newlevel) - - cut_overlays() - add_overlay("ccharger-o[newlevel]") - - chargelevel = newlevel - - add_overlay(image(charging.icon, charging.icon_state)) - add_overlay("ccharger-[charging.connector_type]-on") - - else if(anchored) - cut_overlays() - icon_state = "ccharger0" - add_overlay("ccharger1") - -/obj/machinery/cell_charger/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 5) - . += "[charging ? "[charging]" : "Nothing"] is in [src]." - if(charging) - . += "Current charge: [charging.charge] / [charging.maxcharge]" - -/obj/machinery/cell_charger/attackby(obj/item/weapon/W, mob/user) - if(stat & BROKEN) - return - - if(istype(W, /obj/item/weapon/cell) && anchored) - if(istype(W, /obj/item/weapon/cell/device)) - to_chat(user, "\The [src] isn't fitted for that type of cell.") - return - if(charging) - to_chat(user, "There is already [charging] in [src].") - return - else - var/area/a = loc.loc // Gets our locations location, like a dream within a dream - if(!isarea(a)) - return - if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power! - to_chat(user, "\The [src] blinks red as you try to insert [W]!") - return - - user.drop_item() - W.loc = src - charging = W - user.visible_message("[user] inserts [charging] into [src].", "You insert [charging] into [src].") - chargelevel = -1 - update_icon() - else if(W.has_tool_quality(TOOL_WRENCH)) - if(charging) - to_chat(user, "Remove [charging] first!") - return - - anchored = !anchored - to_chat(user, "You [anchored ? "attach" : "detach"] [src] [anchored ? "to" : "from"] the ground") - playsound(src, W.usesound, 75, 1) - update_icon() - else if(default_deconstruction_screwdriver(user, W)) - return - else if(default_deconstruction_crowbar(user, W)) - return - else if(default_part_replacement(user, W)) - return - -/obj/machinery/cell_charger/attack_hand(mob/user) - add_fingerprint(user) - - if(charging) - user.put_in_hands(charging) - charging.update_icon() - user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") - - charging = null - chargelevel = -1 - update_icon() - -/obj/machinery/cell_charger/attack_ai(mob/user) - if(istype(user, /mob/living/silicon/robot) && Adjacent(user)) // Borgs can remove the cell if they are near enough - if(charging) - user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") - charging.loc = src.loc - charging.update_icon() - charging = null - update_icon() - -/obj/machinery/cell_charger/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return - if(charging) - charging.emp_act(severity) - ..(severity) - - -/obj/machinery/cell_charger/process() - //to_world("ccpt [charging] [stat]") - if((stat & (BROKEN|NOPOWER)) || !anchored) - update_use_power(USE_POWER_OFF) - return - - if(charging && !charging.fully_charged()) - charging.give(efficiency*CELLRATE) - update_use_power(USE_POWER_ACTIVE) - - update_icon() - else - update_use_power(USE_POWER_IDLE) - -/obj/machinery/cell_charger/RefreshParts() - var/E = 0 - for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts) - E += C.rating +/obj/machinery/cell_charger + name = "heavy-duty cell charger" + desc = "A much more powerful version of the standard recharger that is specially designed for charging power cells." + icon = 'icons/obj/power.dmi' + icon_state = "ccharger0" + anchored = 1 + use_power = USE_POWER_IDLE + idle_power_usage = 5 + active_power_usage = 60000 //60 kW. (this the power drawn when charging) + var/efficiency = 60000 //will provide the modified power rate when upgraded + power_channel = EQUIP + var/obj/item/weapon/cell/charging = null + var/chargelevel = -1 + circuit = /obj/item/weapon/circuitboard/cell_charger + +/obj/machinery/cell_charger/Initialize() + . = ..() + default_apply_parts() + add_overlay("ccharger1") + +/obj/machinery/cell_charger/update_icon() + if(!anchored) + cut_overlays() + icon_state = "ccharger2" + + if(charging && !(stat & (BROKEN|NOPOWER))) + var/newlevel = round(charging.percent() * 4.0 / 99) + //to_world("nl: [newlevel]") + + if(chargelevel != newlevel) + + cut_overlays() + add_overlay("ccharger-o[newlevel]") + + chargelevel = newlevel + + add_overlay(image(charging.icon, charging.icon_state)) + add_overlay("ccharger-[charging.connector_type]-on") + + else if(anchored) + cut_overlays() + icon_state = "ccharger0" + add_overlay("ccharger1") + +/obj/machinery/cell_charger/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 5) + . += "[charging ? "[charging]" : "Nothing"] is in [src]." + if(charging) + . += "Current charge: [charging.charge] / [charging.maxcharge]" + +/obj/machinery/cell_charger/attackby(obj/item/weapon/W, mob/user) + if(stat & BROKEN) + return + + if(istype(W, /obj/item/weapon/cell) && anchored) + if(istype(W, /obj/item/weapon/cell/device)) + to_chat(user, "\The [src] isn't fitted for that type of cell.") + return + if(charging) + to_chat(user, "There is already [charging] in [src].") + return + else + var/area/a = loc.loc // Gets our locations location, like a dream within a dream + if(!isarea(a)) + return + if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power! + to_chat(user, "\The [src] blinks red as you try to insert [W]!") + return + + user.drop_item() + W.loc = src + charging = W + user.visible_message("[user] inserts [charging] into [src].", "You insert [charging] into [src].") + chargelevel = -1 + update_icon() + else if(W.has_tool_quality(TOOL_WRENCH)) + if(charging) + to_chat(user, "Remove [charging] first!") + return + + anchored = !anchored + to_chat(user, "You [anchored ? "attach" : "detach"] [src] [anchored ? "to" : "from"] the ground") + playsound(src, W.usesound, 75, 1) + update_icon() + else if(default_deconstruction_screwdriver(user, W)) + return + else if(default_deconstruction_crowbar(user, W)) + return + else if(default_part_replacement(user, W)) + return + +/obj/machinery/cell_charger/attack_hand(mob/user) + add_fingerprint(user) + + if(charging) + user.put_in_hands(charging) + charging.update_icon() + user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") + + charging = null + chargelevel = -1 + update_icon() + +/obj/machinery/cell_charger/attack_ai(mob/user) + if(istype(user, /mob/living/silicon/robot) && Adjacent(user)) // Borgs can remove the cell if they are near enough + if(charging) + user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") + charging.loc = src.loc + charging.update_icon() + charging = null + update_icon() + +/obj/machinery/cell_charger/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return + if(charging) + charging.emp_act(severity) + ..(severity) + + +/obj/machinery/cell_charger/process() + //to_world("ccpt [charging] [stat]") + if((stat & (BROKEN|NOPOWER)) || !anchored) + update_use_power(USE_POWER_OFF) + return + + if(charging && !charging.fully_charged()) + charging.give(efficiency*CELLRATE) + update_use_power(USE_POWER_ACTIVE) + + update_icon() + else + update_use_power(USE_POWER_IDLE) + +/obj/machinery/cell_charger/RefreshParts() + var/E = 0 + for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts) + E += C.rating efficiency = active_power_usage * (1+ (E - 1)*0.5) \ No newline at end of file diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 4f5a37f68d9..da958671e79 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -1,590 +1,590 @@ -//Cloning revival method. -//The pod handles the actual cloning while the computer manages the clone profiles - -//Potential replacement for genetics revives or something I dunno (?) - -//Find a dead mob with a brain and client. -/proc/find_dead_player(var/find_key) - if(isnull(find_key)) - return - - var/mob/selected = null - for(var/mob/living/M in player_list) - //Dead people only thanks! - if((M.stat != 2) || (!M.client)) - continue - //They need a brain! - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(!H.has_brain()) - continue - if(M.ckey == find_key) - selected = M - break - return selected - -#define CLONE_BIOMASS 30 //VOREstation Edit -#define MINIMUM_HEAL_LEVEL 40 - -/obj/machinery/clonepod - name = "cloning pod" - desc = "An electronically-lockable pod for growing organic tissue." - density = TRUE - anchored = TRUE - circuit = /obj/item/weapon/circuitboard/clonepod - icon = 'icons/obj/cloning.dmi' - icon_state = "pod_0" - req_access = list(access_genetics) // For premature unlocking. - var/mob/living/occupant - var/heal_level = 20 // The clone is released once its health reaches this level. - var/heal_rate = 1 - var/locked = 0 - var/obj/machinery/computer/cloning/connected = null //So we remember the connected clone machine. - var/mess = 0 // Need to clean out it if it's full of exploded clone. - var/attempting = 0 // One clone attempt at a time thanks - var/eject_wait = 0 // Don't eject them as soon as they are created fuckkk - - var/list/containers = list() // Beakers for our liquid biomass - var/container_limit = 3 // How many beakers can the machine hold? - - var/speed_coeff - var/efficiency - -/obj/machinery/clonepod/Initialize() - . = ..() - default_apply_parts() - update_icon() - -/obj/machinery/clonepod/attack_ai(mob/user as mob) - - add_hiddenprint(user) - return attack_hand(user) - -/obj/machinery/clonepod/attack_hand(mob/user as mob) - if((isnull(occupant)) || (stat & NOPOWER)) - return - if((!isnull(occupant)) && (occupant.stat != 2)) - var/completion = (100 * ((occupant.health + 50) / (heal_level + 100))) // Clones start at -150 health - to_chat(user, "Current clone cycle is [round(completion)]% complete.") - return - -//Start growing a human clone in the pod! -/obj/machinery/clonepod/proc/growclone(var/datum/dna2/record/R) - if(mess || attempting) - return 0 - var/datum/mind/clonemind = locate(R.mind) - - if(!istype(clonemind, /datum/mind)) //not a mind - return 0 - if(clonemind.current && clonemind.current.stat != DEAD) //mind is associated with a non-dead body - return 0 - if(clonemind.active) //somebody is using that mind - if(ckey(clonemind.key) != R.ckey) - return 0 - else - for(var/mob/observer/dead/G in player_list) - if(G.ckey == R.ckey) - if(G.can_reenter_corpse) - break - else - return 0 - - for(var/modifier_type in R.genetic_modifiers) //Can't be cloned, even if they had a previous scan - if(istype(modifier_type, /datum/modifier/no_clone)) - return 0 - - // Remove biomass when the cloning is started, rather than when the guy pops out - remove_biomass(CLONE_BIOMASS) - - attempting = 1 //One at a time!! - locked = 1 - - eject_wait = 1 - spawn(30) - eject_wait = 0 - - var/mob/living/carbon/human/H = new /mob/living/carbon/human(src, R.dna.species) - occupant = H - - if(!R.dna.real_name) //to prevent null names - R.dna.real_name = "clone ([rand(0,999)])" - H.real_name = R.dna.real_name - H.gender = R.gender - H.descriptors = R.body_descriptors - - //Get the clone body ready - H.adjustCloneLoss(150) // New damage var so you can't eject a clone early then stab them to abuse the current damage system --NeoFite - H.Paralyse(4) - - //Here let's calculate their health so the pod doesn't immediately eject them!!! - H.updatehealth() - - clonemind.transfer_to(H) - H.ckey = R.ckey - to_chat(H, "Consciousness slowly creeps over you as your body regenerates.
    Your recent memories are fuzzy, and it's hard to remember anything from today...

    So this is what cloning feels like?") - - // -- Mode/mind specific stuff goes here - callHook("clone", list(H)) - update_antag_icons(H.mind) - // -- End mode specific stuff - - if(!R.dna) - H.dna = new /datum/dna() - H.dna.real_name = H.real_name - else - H.dna = R.dna - H.UpdateAppearance() - H.sync_organ_dna() - if(heal_level < 60) - randmutb(H) //Sometimes the clones come out wrong. - H.dna.UpdateSE() - H.dna.UpdateUI() - - H.set_cloned_appearance() - update_icon() - - // A modifier is added which makes the new clone be unrobust. - var/modifier_lower_bound = 25 MINUTES - var/modifier_upper_bound = 40 MINUTES - - // Upgraded cloners can reduce the time of the modifier, up to 80% - var/clone_sickness_length = abs(((heal_level - 20) / 100 ) - 1) - clone_sickness_length = between(0.2, clone_sickness_length, 1.0) // Caps it off just incase. - modifier_lower_bound = round(modifier_lower_bound * clone_sickness_length, 1) - modifier_upper_bound = round(modifier_upper_bound * clone_sickness_length, 1) - - H.add_modifier(H.species.cloning_modifier, rand(modifier_lower_bound, modifier_upper_bound)) - - // Modifier that doesn't do anything. - H.add_modifier(/datum/modifier/cloned) - - // This is really stupid. - for(var/modifier_type in R.genetic_modifiers) - H.add_modifier(modifier_type) - - for(var/datum/language/L in R.languages) - H.add_language(L.name) - - H.flavor_texts = R.flavor.Copy() - H.suiciding = 0 - attempting = 0 - return 1 - -//Grow clones to maturity then kick them out. FREELOADERS -/obj/machinery/clonepod/process() - if(stat & NOPOWER) //Autoeject if power is lost - if(occupant) - locked = 0 - go_out() - return - - if((occupant) && (occupant.loc == src)) - if((occupant.stat == DEAD) || (occupant.suiciding) || !occupant.key) //Autoeject corpses and suiciding dudes. - locked = 0 - go_out() - connected_message("Clone Rejected: Deceased.") - return - - else if(occupant.health < heal_level && occupant.getCloneLoss() > 0) - occupant.Paralyse(4) - - //Slowly get that clone healed and finished. - occupant.adjustCloneLoss(-2 * heal_rate) - - //Premature clones may have brain damage. - occupant.adjustBrainLoss(-(CEILING(0.5*heal_rate, 1))) - - //So clones don't die of oxyloss in a running pod. - if(occupant.reagents.get_reagent_amount("inaprovaline") < 30) - occupant.reagents.add_reagent("inaprovaline", 60) - occupant.Sleeping(30) - //Also heal some oxyloss ourselves because inaprovaline is so bad at preventing it!! - occupant.adjustOxyLoss(-4) - - use_power(7500) //This might need tweaking. - return - - else if((occupant.health >= heal_level || occupant.health == occupant.getMaxHealth()) && (!eject_wait)) - playsound(src, 'sound/machines/medbayscanner1.ogg', 50, 1) - audible_message("\The [src] signals that the cloning process is complete.", runemessage = "ding") - connected_message("Cloning Process Complete.") - locked = 0 - go_out() - return - - else if((!occupant) || (occupant.loc != src)) - occupant = null - if(locked) - locked = 0 - return - - return - -//Let's unlock this early I guess. Might be too early, needs tweaking. -/obj/machinery/clonepod/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(isnull(occupant)) - if(default_deconstruction_screwdriver(user, W)) - return - if(default_deconstruction_crowbar(user, W)) - return - if(default_part_replacement(user, W)) - return - if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) - if(!check_access(W)) - to_chat(user, "Access Denied.") - return - if((!locked) || (isnull(occupant))) - return - if((occupant.health < -20) && (occupant.stat != 2)) - to_chat(user, "Access Refused.") - return - else - locked = 0 - to_chat(user, "System unlocked.") - else if(istype(W,/obj/item/weapon/reagent_containers/glass)) - if(LAZYLEN(containers) >= container_limit) - to_chat(user, "\The [src] has too many containers loaded!") - else if(do_after(user, 1 SECOND)) - user.visible_message("[user] has loaded \the [W] into \the [src].", "You load \the [W] into \the [src].") - containers += W - user.drop_item() - W.forceMove(src) - return - else if(W.has_tool_quality(TOOL_WRENCH)) - if(locked && (anchored || occupant)) - to_chat(user, "Can not do that while [src] is in use.") - else - if(anchored) - anchored = FALSE - connected.pods -= src - connected = null - else - anchored = TRUE - playsound(src, W.usesound, 100, 1) - if(anchored) - user.visible_message("[user] secures [src] to the floor.", "You secure [src] to the floor.") - else - user.visible_message("[user] unsecures [src] from the floor.", "You unsecure [src] from the floor.") - else if(istype(W, /obj/item/device/multitool)) - var/obj/item/device/multitool/M = W - M.connecting = src - to_chat(user, "You load connection data from [src] to [M].") - M.update_icon() - return - else - ..() - -/obj/machinery/clonepod/emag_act(var/remaining_charges, var/mob/user) - if(isnull(occupant)) - return - to_chat(user, "You force an emergency ejection.") - locked = 0 - go_out() - return 1 - -//Put messages in the connected computer's temp var for display. -/obj/machinery/clonepod/proc/connected_message(var/message) - if((isnull(connected)) || (!istype(connected, /obj/machinery/computer/cloning))) - return 0 - if(!message) - return 0 - - connected.temp = "[name] : [message]" - connected.updateUsrDialog() - return 1 - -/obj/machinery/clonepod/RefreshParts() - ..() - speed_coeff = 0 - efficiency = 0 - for(var/obj/item/weapon/stock_parts/scanning_module/S in component_parts) - efficiency += S.rating - for(var/obj/item/weapon/stock_parts/manipulator/P in component_parts) - speed_coeff += P.rating - heal_level = max(min((efficiency * 15) + 10, 100), MINIMUM_HEAL_LEVEL) - -/obj/machinery/clonepod/proc/get_completion() - . = (100 * ((occupant.health + 100) / (heal_level + 100))) - -/obj/machinery/clonepod/verb/eject() - set name = "Eject Cloner" - set category = "Object" - set src in oview(1) - - if(usr.stat != 0) - return - go_out() - add_fingerprint(usr) - return - -/obj/machinery/clonepod/proc/go_out() - if(locked) - return - - if(mess) //Clean that mess and dump those gibs! - mess = 0 - gibs(src.loc) - update_icon() - return - - if(!(occupant)) - return - - if(occupant.client) - occupant.client.eye = occupant.client.mob - occupant.client.perspective = MOB_PERSPECTIVE - occupant.loc = src.loc - eject_wait = 0 //If it's still set somehow. - if(ishuman(occupant)) //Need to be safe. - var/mob/living/carbon/human/patient = occupant - if(!(patient.species.flags & NO_SCAN)) //If, for some reason, someone makes a genetically-unalterable clone, let's not make them permanently disabled. - domutcheck(occupant) //Waiting until they're out before possible transforming. - occupant = null - - update_icon() - return - -// Returns the total amount of biomass reagent in all of the pod's stored containers -/obj/machinery/clonepod/proc/get_biomass() - var/biomass_count = 0 - if(LAZYLEN(containers)) - for(var/obj/item/weapon/reagent_containers/glass/G in containers) - for(var/datum/reagent/R in G.reagents.reagent_list) - if(R.id == "biomass") - biomass_count += R.volume - - return biomass_count - -// Removes [amount] biomass, spread across all containers. Doesn't have any check that you actually HAVE enough biomass, though. -/obj/machinery/clonepod/proc/remove_biomass(var/amount = CLONE_BIOMASS) //Just in case it doesn't get passed a new amount, assume one clone - var/to_remove = 0 // Tracks how much biomass has been found so far - if(LAZYLEN(containers)) - for(var/obj/item/weapon/reagent_containers/glass/G in containers) - if(to_remove < amount) //If we have what we need, we can stop. Checked every time we switch beakers - for(var/datum/reagent/R in G.reagents.reagent_list) - if(R.id == "biomass") // Finds Biomass - var/need_remove = max(0, amount - to_remove) //Figures out how much biomass is in this container - if(R.volume >= need_remove) //If we have more than enough in this beaker, only take what we need - R.remove_self(need_remove) - to_remove = amount - else //Otherwise, take everything and move on - to_remove += R.volume - R.remove_self(R.volume) - else - continue - else - return 1 - return 0 - -// Empties all of the beakers from the cloning pod, used to refill it -/obj/machinery/clonepod/verb/empty_beakers() - set name = "Eject Beakers" - set category = "Object" - set src in oview(1) - - if(usr.stat != 0) - return - - add_fingerprint(usr) - drop_beakers() - return - -// Actually does all of the beaker dropping -// Returns 1 if it succeeds, 0 if it fails. Added in case someone wants to add messages to the user. -/obj/machinery/clonepod/proc/drop_beakers() - if(LAZYLEN(containers)) - var/turf/T = get_turf(src) - if(T) - for(var/obj/item/weapon/reagent_containers/glass/G in containers) - G.forceMove(T) - containers -= G - return 1 - return 0 - -/obj/machinery/clonepod/proc/malfunction() - if(occupant) - connected_message("Critical Error!") - mess = 1 - update_icon() - occupant.ghostize() - spawn(5) - qdel(occupant) - return - -/obj/machinery/clonepod/relaymove(mob/user as mob) - if(user.stat) - return - go_out() - return - -/obj/machinery/clonepod/emp_act(severity) - if(prob(100/severity)) - malfunction() - ..() - -/obj/machinery/clonepod/ex_act(severity) - switch(severity) - if(1.0) - for(var/atom/movable/A as mob|obj in src) - A.loc = src.loc - ex_act(severity) - qdel(src) - return - if(2.0) - if(prob(50)) - for(var/atom/movable/A as mob|obj in src) - A.loc = src.loc - ex_act(severity) - qdel(src) - return - if(3.0) - if(prob(25)) - for(var/atom/movable/A as mob|obj in src) - A.loc = src.loc - ex_act(severity) - qdel(src) - return - else - return - -/obj/machinery/clonepod/update_icon() - ..() - icon_state = "pod_0" - if(occupant && !(stat & NOPOWER)) - icon_state = "pod_1" - else if(mess) - icon_state = "pod_g" - - -/obj/machinery/clonepod/full/New() - ..() - for(var/i = 1 to container_limit) - containers += new /obj/item/weapon/reagent_containers/glass/bottle/biomass(src) - -//Health Tracker Implant - -/obj/item/weapon/implant/health - name = "health implant" - known_implant = TRUE - var/healthstring = "" - -/obj/item/weapon/implant/health/proc/sensehealth() - if(!implanted) - return "ERROR" - else - if(isliving(implanted)) - var/mob/living/L = implanted - healthstring = "[round(L.getOxyLoss())] - [round(L.getFireLoss())] - [round(L.getToxLoss())] - [round(L.getBruteLoss())]" - if(!healthstring) - healthstring = "ERROR" - return healthstring - -//Disk stuff. -//The return of data disks?? Just for transferring between genetics machine/cloning machine. -//TO-DO: Make the genetics machine accept them. -/obj/item/weapon/disk/data - name = "Cloning Data Disk" - icon = 'icons/obj/discs_vr.dmi' //VOREStation Edit - icon_state = "data-red" //VOREStation Edit - item_state = "card-id" - w_class = ITEMSIZE_SMALL - var/datum/dna2/record/buf = null - var/read_only = 0 //Well,it's still a floppy disk - -/obj/item/weapon/disk/data/proc/initializeDisk() - buf = new - buf.dna=new - -/obj/item/weapon/disk/data/demo - name = "data disk - 'God Emperor of Mankind'" - read_only = 1 - -/obj/item/weapon/disk/data/demo/New() - initializeDisk() - buf.types=DNA2_BUF_UE|DNA2_BUF_UI - //data = "066000033000000000AF00330660FF4DB002690" - //data = "0C80C80C80C80C80C8000000000000161FBDDEF" - Farmer Jeff - buf.dna.real_name="God Emperor of Mankind" - buf.dna.unique_enzymes = md5(buf.dna.real_name) - buf.dna.UI=list(0x066,0x000,0x033,0x000,0x000,0x000,0xAF0,0x033,0x066,0x0FF,0x4DB,0x002,0x690) - //buf.dna.UI=list(0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x000,0x000,0x000,0x000,0x161,0xFBD,0xDEF) // Farmer Jeff - buf.dna.UpdateUI() - -/obj/item/weapon/disk/data/monkey - name = "data disk - 'Mr. Muggles'" - read_only = 1 - -/obj/item/weapon/disk/data/monkey/New() - ..() - initializeDisk() - buf.types=DNA2_BUF_SE - var/list/new_SE=list(0x098,0x3E8,0x403,0x44C,0x39F,0x4B0,0x59D,0x514,0x5FC,0x578,0x5DC,0x640,0x6A4) - for(var/i=new_SE.len;i<=DNA_SE_LENGTH;i++) - new_SE += rand(1,1024) - buf.dna.SE=new_SE - buf.dna.SetSEValueRange(MONKEYBLOCK,0xDAC, 0xFFF) - -/obj/item/weapon/disk/data/New() - ..() - var/diskcolor = pick(0,1,2) - icon_state = "datadisk[diskcolor]" - -/obj/item/weapon/disk/data/attack_self(mob/user as mob) - read_only = !read_only - to_chat(user, "You flip the write-protect tab to [read_only ? "protected" : "unprotected"].") - -/obj/item/weapon/disk/data/examine(mob/user) - . = ..() - . += "The write-protect tab is set to [read_only ? "protected" : "unprotected"]." - -/* - * Diskette Box - */ - -/obj/item/weapon/storage/box/disks - name = "Diskette Box" - icon_state = "disk_kit" - -/obj/item/weapon/storage/box/disks/New() - ..() - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - -/* - * Manual -- A big ol' manual. - */ - -/obj/item/weapon/paper/Cloning - name = "H-87 Cloning Apparatus Manual" - info = {"

    Getting Started

    - Congratulations, your station has purchased the H-87 industrial cloning device!
    - Using the H-87 is almost as simple as brain surgery! Simply insert the target humanoid into the scanning chamber and select the scan option to create a new profile!
    - That's all there is to it!
    - Notice, cloning system cannot scan inorganic life or small primates. Scan may fail if subject has suffered extreme brain damage.
    -

    Clone profiles may be viewed through the profiles menu. Scanning implants a complementary HEALTH MONITOR IMPLANT into the subject, which may be viewed from each profile. - Profile Deletion has been restricted to \[Station Head\] level access.

    -

    Cloning from a profile

    - Cloning is as simple as pressing the CLONE option at the bottom of the desired profile.
    - Per your company's EMPLOYEE PRIVACY RIGHTS agreement, the H-87 has been blocked from cloning crewmembers while they are still alive.
    -
    -

    The provided CLONEPOD SYSTEM will produce the desired clone. Standard clone maturation times (With SPEEDCLONE technology) are roughly 90 seconds. - The cloning pod may be unlocked early with any \[Medical Researcher\] ID after initial maturation is complete.


    - Please note that resulting clones may have a small DEVELOPMENTAL DEFECT as a result of genetic drift.
    -

    Profile Management

    -

    The H-87 (as well as your station's standard genetics machine) can accept STANDARD DATA DISKETTES. - These diskettes are used to transfer genetic information between machines and profiles. - A load/save dialog will become available in each profile if a disk is inserted.


    - A good diskette is a great way to counter aforementioned genetic drift!
    -
    - This technology produced under license from Thinktronic Systems, LTD."} - -//SOME SCRAPS I GUESS -/* EMP grenade/spell effect - if(istype(A, /obj/machinery/clonepod)) - A:malfunction() -*/ +//Cloning revival method. +//The pod handles the actual cloning while the computer manages the clone profiles + +//Potential replacement for genetics revives or something I dunno (?) + +//Find a dead mob with a brain and client. +/proc/find_dead_player(var/find_key) + if(isnull(find_key)) + return + + var/mob/selected = null + for(var/mob/living/M in player_list) + //Dead people only thanks! + if((M.stat != 2) || (!M.client)) + continue + //They need a brain! + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + if(!H.has_brain()) + continue + if(M.ckey == find_key) + selected = M + break + return selected + +#define CLONE_BIOMASS 30 //VOREstation Edit +#define MINIMUM_HEAL_LEVEL 40 + +/obj/machinery/clonepod + name = "cloning pod" + desc = "An electronically-lockable pod for growing organic tissue." + density = TRUE + anchored = TRUE + circuit = /obj/item/weapon/circuitboard/clonepod + icon = 'icons/obj/cloning.dmi' + icon_state = "pod_0" + req_access = list(access_genetics) // For premature unlocking. + var/mob/living/occupant + var/heal_level = 20 // The clone is released once its health reaches this level. + var/heal_rate = 1 + var/locked = 0 + var/obj/machinery/computer/cloning/connected = null //So we remember the connected clone machine. + var/mess = 0 // Need to clean out it if it's full of exploded clone. + var/attempting = 0 // One clone attempt at a time thanks + var/eject_wait = 0 // Don't eject them as soon as they are created fuckkk + + var/list/containers = list() // Beakers for our liquid biomass + var/container_limit = 3 // How many beakers can the machine hold? + + var/speed_coeff + var/efficiency + +/obj/machinery/clonepod/Initialize() + . = ..() + default_apply_parts() + update_icon() + +/obj/machinery/clonepod/attack_ai(mob/user as mob) + + add_hiddenprint(user) + return attack_hand(user) + +/obj/machinery/clonepod/attack_hand(mob/user as mob) + if((isnull(occupant)) || (stat & NOPOWER)) + return + if((!isnull(occupant)) && (occupant.stat != 2)) + var/completion = (100 * ((occupant.health + 50) / (heal_level + 100))) // Clones start at -150 health + to_chat(user, "Current clone cycle is [round(completion)]% complete.") + return + +//Start growing a human clone in the pod! +/obj/machinery/clonepod/proc/growclone(var/datum/dna2/record/R) + if(mess || attempting) + return 0 + var/datum/mind/clonemind = locate(R.mind) + + if(!istype(clonemind, /datum/mind)) //not a mind + return 0 + if(clonemind.current && clonemind.current.stat != DEAD) //mind is associated with a non-dead body + return 0 + if(clonemind.active) //somebody is using that mind + if(ckey(clonemind.key) != R.ckey) + return 0 + else + for(var/mob/observer/dead/G in player_list) + if(G.ckey == R.ckey) + if(G.can_reenter_corpse) + break + else + return 0 + + for(var/modifier_type in R.genetic_modifiers) //Can't be cloned, even if they had a previous scan + if(istype(modifier_type, /datum/modifier/no_clone)) + return 0 + + // Remove biomass when the cloning is started, rather than when the guy pops out + remove_biomass(CLONE_BIOMASS) + + attempting = 1 //One at a time!! + locked = 1 + + eject_wait = 1 + spawn(30) + eject_wait = 0 + + var/mob/living/carbon/human/H = new /mob/living/carbon/human(src, R.dna.species) + occupant = H + + if(!R.dna.real_name) //to prevent null names + R.dna.real_name = "clone ([rand(0,999)])" + H.real_name = R.dna.real_name + H.gender = R.gender + H.descriptors = R.body_descriptors + + //Get the clone body ready + H.adjustCloneLoss(150) // New damage var so you can't eject a clone early then stab them to abuse the current damage system --NeoFite + H.Paralyse(4) + + //Here let's calculate their health so the pod doesn't immediately eject them!!! + H.updatehealth() + + clonemind.transfer_to(H) + H.ckey = R.ckey + to_chat(H, "Consciousness slowly creeps over you as your body regenerates.
    Your recent memories are fuzzy, and it's hard to remember anything from today...

    So this is what cloning feels like?") + + // -- Mode/mind specific stuff goes here + callHook("clone", list(H)) + update_antag_icons(H.mind) + // -- End mode specific stuff + + if(!R.dna) + H.dna = new /datum/dna() + H.dna.real_name = H.real_name + else + H.dna = R.dna + H.UpdateAppearance() + H.sync_organ_dna() + if(heal_level < 60) + randmutb(H) //Sometimes the clones come out wrong. + H.dna.UpdateSE() + H.dna.UpdateUI() + + H.set_cloned_appearance() + update_icon() + + // A modifier is added which makes the new clone be unrobust. + var/modifier_lower_bound = 25 MINUTES + var/modifier_upper_bound = 40 MINUTES + + // Upgraded cloners can reduce the time of the modifier, up to 80% + var/clone_sickness_length = abs(((heal_level - 20) / 100 ) - 1) + clone_sickness_length = between(0.2, clone_sickness_length, 1.0) // Caps it off just incase. + modifier_lower_bound = round(modifier_lower_bound * clone_sickness_length, 1) + modifier_upper_bound = round(modifier_upper_bound * clone_sickness_length, 1) + + H.add_modifier(H.species.cloning_modifier, rand(modifier_lower_bound, modifier_upper_bound)) + + // Modifier that doesn't do anything. + H.add_modifier(/datum/modifier/cloned) + + // This is really stupid. + for(var/modifier_type in R.genetic_modifiers) + H.add_modifier(modifier_type) + + for(var/datum/language/L in R.languages) + H.add_language(L.name) + + H.flavor_texts = R.flavor.Copy() + H.suiciding = 0 + attempting = 0 + return 1 + +//Grow clones to maturity then kick them out. FREELOADERS +/obj/machinery/clonepod/process() + if(stat & NOPOWER) //Autoeject if power is lost + if(occupant) + locked = 0 + go_out() + return + + if((occupant) && (occupant.loc == src)) + if((occupant.stat == DEAD) || (occupant.suiciding) || !occupant.key) //Autoeject corpses and suiciding dudes. + locked = 0 + go_out() + connected_message("Clone Rejected: Deceased.") + return + + else if(occupant.health < heal_level && occupant.getCloneLoss() > 0) + occupant.Paralyse(4) + + //Slowly get that clone healed and finished. + occupant.adjustCloneLoss(-2 * heal_rate) + + //Premature clones may have brain damage. + occupant.adjustBrainLoss(-(CEILING(0.5*heal_rate, 1))) + + //So clones don't die of oxyloss in a running pod. + if(occupant.reagents.get_reagent_amount("inaprovaline") < 30) + occupant.reagents.add_reagent("inaprovaline", 60) + occupant.Sleeping(30) + //Also heal some oxyloss ourselves because inaprovaline is so bad at preventing it!! + occupant.adjustOxyLoss(-4) + + use_power(7500) //This might need tweaking. + return + + else if((occupant.health >= heal_level || occupant.health == occupant.getMaxHealth()) && (!eject_wait)) + playsound(src, 'sound/machines/medbayscanner1.ogg', 50, 1) + audible_message("\The [src] signals that the cloning process is complete.", runemessage = "ding") + connected_message("Cloning Process Complete.") + locked = 0 + go_out() + return + + else if((!occupant) || (occupant.loc != src)) + occupant = null + if(locked) + locked = 0 + return + + return + +//Let's unlock this early I guess. Might be too early, needs tweaking. +/obj/machinery/clonepod/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(isnull(occupant)) + if(default_deconstruction_screwdriver(user, W)) + return + if(default_deconstruction_crowbar(user, W)) + return + if(default_part_replacement(user, W)) + return + if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) + if(!check_access(W)) + to_chat(user, "Access Denied.") + return + if((!locked) || (isnull(occupant))) + return + if((occupant.health < -20) && (occupant.stat != 2)) + to_chat(user, "Access Refused.") + return + else + locked = 0 + to_chat(user, "System unlocked.") + else if(istype(W,/obj/item/weapon/reagent_containers/glass)) + if(LAZYLEN(containers) >= container_limit) + to_chat(user, "\The [src] has too many containers loaded!") + else if(do_after(user, 1 SECOND)) + user.visible_message("[user] has loaded \the [W] into \the [src].", "You load \the [W] into \the [src].") + containers += W + user.drop_item() + W.forceMove(src) + return + else if(W.has_tool_quality(TOOL_WRENCH)) + if(locked && (anchored || occupant)) + to_chat(user, "Can not do that while [src] is in use.") + else + if(anchored) + anchored = FALSE + connected.pods -= src + connected = null + else + anchored = TRUE + playsound(src, W.usesound, 100, 1) + if(anchored) + user.visible_message("[user] secures [src] to the floor.", "You secure [src] to the floor.") + else + user.visible_message("[user] unsecures [src] from the floor.", "You unsecure [src] from the floor.") + else if(istype(W, /obj/item/device/multitool)) + var/obj/item/device/multitool/M = W + M.connecting = src + to_chat(user, "You load connection data from [src] to [M].") + M.update_icon() + return + else + ..() + +/obj/machinery/clonepod/emag_act(var/remaining_charges, var/mob/user) + if(isnull(occupant)) + return + to_chat(user, "You force an emergency ejection.") + locked = 0 + go_out() + return 1 + +//Put messages in the connected computer's temp var for display. +/obj/machinery/clonepod/proc/connected_message(var/message) + if((isnull(connected)) || (!istype(connected, /obj/machinery/computer/cloning))) + return 0 + if(!message) + return 0 + + connected.temp = "[name] : [message]" + connected.updateUsrDialog() + return 1 + +/obj/machinery/clonepod/RefreshParts() + ..() + speed_coeff = 0 + efficiency = 0 + for(var/obj/item/weapon/stock_parts/scanning_module/S in component_parts) + efficiency += S.rating + for(var/obj/item/weapon/stock_parts/manipulator/P in component_parts) + speed_coeff += P.rating + heal_level = max(min((efficiency * 15) + 10, 100), MINIMUM_HEAL_LEVEL) + +/obj/machinery/clonepod/proc/get_completion() + . = (100 * ((occupant.health + 100) / (heal_level + 100))) + +/obj/machinery/clonepod/verb/eject() + set name = "Eject Cloner" + set category = "Object" + set src in oview(1) + + if(usr.stat != 0) + return + go_out() + add_fingerprint(usr) + return + +/obj/machinery/clonepod/proc/go_out() + if(locked) + return + + if(mess) //Clean that mess and dump those gibs! + mess = 0 + gibs(src.loc) + update_icon() + return + + if(!(occupant)) + return + + if(occupant.client) + occupant.client.eye = occupant.client.mob + occupant.client.perspective = MOB_PERSPECTIVE + occupant.loc = src.loc + eject_wait = 0 //If it's still set somehow. + if(ishuman(occupant)) //Need to be safe. + var/mob/living/carbon/human/patient = occupant + if(!(patient.species.flags & NO_SCAN)) //If, for some reason, someone makes a genetically-unalterable clone, let's not make them permanently disabled. + domutcheck(occupant) //Waiting until they're out before possible transforming. + occupant = null + + update_icon() + return + +// Returns the total amount of biomass reagent in all of the pod's stored containers +/obj/machinery/clonepod/proc/get_biomass() + var/biomass_count = 0 + if(LAZYLEN(containers)) + for(var/obj/item/weapon/reagent_containers/glass/G in containers) + for(var/datum/reagent/R in G.reagents.reagent_list) + if(R.id == "biomass") + biomass_count += R.volume + + return biomass_count + +// Removes [amount] biomass, spread across all containers. Doesn't have any check that you actually HAVE enough biomass, though. +/obj/machinery/clonepod/proc/remove_biomass(var/amount = CLONE_BIOMASS) //Just in case it doesn't get passed a new amount, assume one clone + var/to_remove = 0 // Tracks how much biomass has been found so far + if(LAZYLEN(containers)) + for(var/obj/item/weapon/reagent_containers/glass/G in containers) + if(to_remove < amount) //If we have what we need, we can stop. Checked every time we switch beakers + for(var/datum/reagent/R in G.reagents.reagent_list) + if(R.id == "biomass") // Finds Biomass + var/need_remove = max(0, amount - to_remove) //Figures out how much biomass is in this container + if(R.volume >= need_remove) //If we have more than enough in this beaker, only take what we need + R.remove_self(need_remove) + to_remove = amount + else //Otherwise, take everything and move on + to_remove += R.volume + R.remove_self(R.volume) + else + continue + else + return 1 + return 0 + +// Empties all of the beakers from the cloning pod, used to refill it +/obj/machinery/clonepod/verb/empty_beakers() + set name = "Eject Beakers" + set category = "Object" + set src in oview(1) + + if(usr.stat != 0) + return + + add_fingerprint(usr) + drop_beakers() + return + +// Actually does all of the beaker dropping +// Returns 1 if it succeeds, 0 if it fails. Added in case someone wants to add messages to the user. +/obj/machinery/clonepod/proc/drop_beakers() + if(LAZYLEN(containers)) + var/turf/T = get_turf(src) + if(T) + for(var/obj/item/weapon/reagent_containers/glass/G in containers) + G.forceMove(T) + containers -= G + return 1 + return 0 + +/obj/machinery/clonepod/proc/malfunction() + if(occupant) + connected_message("Critical Error!") + mess = 1 + update_icon() + occupant.ghostize() + spawn(5) + qdel(occupant) + return + +/obj/machinery/clonepod/relaymove(mob/user as mob) + if(user.stat) + return + go_out() + return + +/obj/machinery/clonepod/emp_act(severity) + if(prob(100/severity)) + malfunction() + ..() + +/obj/machinery/clonepod/ex_act(severity) + switch(severity) + if(1.0) + for(var/atom/movable/A as mob|obj in src) + A.loc = src.loc + ex_act(severity) + qdel(src) + return + if(2.0) + if(prob(50)) + for(var/atom/movable/A as mob|obj in src) + A.loc = src.loc + ex_act(severity) + qdel(src) + return + if(3.0) + if(prob(25)) + for(var/atom/movable/A as mob|obj in src) + A.loc = src.loc + ex_act(severity) + qdel(src) + return + else + return + +/obj/machinery/clonepod/update_icon() + ..() + icon_state = "pod_0" + if(occupant && !(stat & NOPOWER)) + icon_state = "pod_1" + else if(mess) + icon_state = "pod_g" + + +/obj/machinery/clonepod/full/New() + ..() + for(var/i = 1 to container_limit) + containers += new /obj/item/weapon/reagent_containers/glass/bottle/biomass(src) + +//Health Tracker Implant + +/obj/item/weapon/implant/health + name = "health implant" + known_implant = TRUE + var/healthstring = "" + +/obj/item/weapon/implant/health/proc/sensehealth() + if(!implanted) + return "ERROR" + else + if(isliving(implanted)) + var/mob/living/L = implanted + healthstring = "[round(L.getOxyLoss())] - [round(L.getFireLoss())] - [round(L.getToxLoss())] - [round(L.getBruteLoss())]" + if(!healthstring) + healthstring = "ERROR" + return healthstring + +//Disk stuff. +//The return of data disks?? Just for transferring between genetics machine/cloning machine. +//TO-DO: Make the genetics machine accept them. +/obj/item/weapon/disk/data + name = "Cloning Data Disk" + icon = 'icons/obj/discs_vr.dmi' //VOREStation Edit + icon_state = "data-red" //VOREStation Edit + item_state = "card-id" + w_class = ITEMSIZE_SMALL + var/datum/dna2/record/buf = null + var/read_only = 0 //Well,it's still a floppy disk + +/obj/item/weapon/disk/data/proc/initializeDisk() + buf = new + buf.dna=new + +/obj/item/weapon/disk/data/demo + name = "data disk - 'God Emperor of Mankind'" + read_only = 1 + +/obj/item/weapon/disk/data/demo/New() + initializeDisk() + buf.types=DNA2_BUF_UE|DNA2_BUF_UI + //data = "066000033000000000AF00330660FF4DB002690" + //data = "0C80C80C80C80C80C8000000000000161FBDDEF" - Farmer Jeff + buf.dna.real_name="God Emperor of Mankind" + buf.dna.unique_enzymes = md5(buf.dna.real_name) + buf.dna.UI=list(0x066,0x000,0x033,0x000,0x000,0x000,0xAF0,0x033,0x066,0x0FF,0x4DB,0x002,0x690) + //buf.dna.UI=list(0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x000,0x000,0x000,0x000,0x161,0xFBD,0xDEF) // Farmer Jeff + buf.dna.UpdateUI() + +/obj/item/weapon/disk/data/monkey + name = "data disk - 'Mr. Muggles'" + read_only = 1 + +/obj/item/weapon/disk/data/monkey/New() + ..() + initializeDisk() + buf.types=DNA2_BUF_SE + var/list/new_SE=list(0x098,0x3E8,0x403,0x44C,0x39F,0x4B0,0x59D,0x514,0x5FC,0x578,0x5DC,0x640,0x6A4) + for(var/i=new_SE.len;i<=DNA_SE_LENGTH;i++) + new_SE += rand(1,1024) + buf.dna.SE=new_SE + buf.dna.SetSEValueRange(MONKEYBLOCK,0xDAC, 0xFFF) + +/obj/item/weapon/disk/data/New() + ..() + var/diskcolor = pick(0,1,2) + icon_state = "datadisk[diskcolor]" + +/obj/item/weapon/disk/data/attack_self(mob/user as mob) + read_only = !read_only + to_chat(user, "You flip the write-protect tab to [read_only ? "protected" : "unprotected"].") + +/obj/item/weapon/disk/data/examine(mob/user) + . = ..() + . += "The write-protect tab is set to [read_only ? "protected" : "unprotected"]." + +/* + * Diskette Box + */ + +/obj/item/weapon/storage/box/disks + name = "Diskette Box" + icon_state = "disk_kit" + +/obj/item/weapon/storage/box/disks/New() + ..() + new /obj/item/weapon/disk/data(src) + new /obj/item/weapon/disk/data(src) + new /obj/item/weapon/disk/data(src) + new /obj/item/weapon/disk/data(src) + new /obj/item/weapon/disk/data(src) + new /obj/item/weapon/disk/data(src) + new /obj/item/weapon/disk/data(src) + +/* + * Manual -- A big ol' manual. + */ + +/obj/item/weapon/paper/Cloning + name = "H-87 Cloning Apparatus Manual" + info = {"

    Getting Started

    + Congratulations, your station has purchased the H-87 industrial cloning device!
    + Using the H-87 is almost as simple as brain surgery! Simply insert the target humanoid into the scanning chamber and select the scan option to create a new profile!
    + That's all there is to it!
    + Notice, cloning system cannot scan inorganic life or small primates. Scan may fail if subject has suffered extreme brain damage.
    +

    Clone profiles may be viewed through the profiles menu. Scanning implants a complementary HEALTH MONITOR IMPLANT into the subject, which may be viewed from each profile. + Profile Deletion has been restricted to \[Station Head\] level access.

    +

    Cloning from a profile

    + Cloning is as simple as pressing the CLONE option at the bottom of the desired profile.
    + Per your company's EMPLOYEE PRIVACY RIGHTS agreement, the H-87 has been blocked from cloning crewmembers while they are still alive.
    +
    +

    The provided CLONEPOD SYSTEM will produce the desired clone. Standard clone maturation times (With SPEEDCLONE technology) are roughly 90 seconds. + The cloning pod may be unlocked early with any \[Medical Researcher\] ID after initial maturation is complete.


    + Please note that resulting clones may have a small DEVELOPMENTAL DEFECT as a result of genetic drift.
    +

    Profile Management

    +

    The H-87 (as well as your station's standard genetics machine) can accept STANDARD DATA DISKETTES. + These diskettes are used to transfer genetic information between machines and profiles. + A load/save dialog will become available in each profile if a disk is inserted.


    + A good diskette is a great way to counter aforementioned genetic drift!
    +
    + This technology produced under license from Thinktronic Systems, LTD."} + +//SOME SCRAPS I GUESS +/* EMP grenade/spell effect + if(istype(A, /obj/machinery/clonepod)) + A:malfunction() +*/ diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm index 1d45a3e508b..dc8375f1e75 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -1,312 +1,312 @@ -#define OP_COMPUTER_COOLDOWN 60 - -/obj/machinery/computer/operating - name = "patient monitoring console" - desc = "Used to monitor the vitals of a patient." - density = TRUE - anchored = TRUE - icon_keyboard = "med_key" - icon_screen = "crew" - circuit = /obj/item/weapon/circuitboard/operating - var/obj/machinery/optable/table = null - var/mob/living/carbon/human/victim = null - var/verbose = 1 //general speaker toggle - var/patientName = null - var/oxyAlarm = 30 //oxy damage at which the computer will beep - var/choice = 0 //just for going into and out of the options menu - var/healthAnnounce = 1 //healther announcer toggle - var/crit = 1 //crit beeping toggle - var/nextTick = OP_COMPUTER_COOLDOWN - var/healthAlarm = 50 - var/oxy = 1 //oxygen beeping toggle - -/obj/machinery/computer/operating/New() - ..() - for(var/direction in list(NORTH,EAST,SOUTH,WEST)) - table = locate(/obj/machinery/optable, get_step(src, direction)) - if(table) - table.computer = src - break - -/obj/machinery/computer/operating/Destroy() - if(table) - table.computer = null - table = null - if(victim) - victim = null - return ..() - -/obj/machinery/computer/operating/attack_ai(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - tgui_interact(user) - - -/obj/machinery/computer/operating/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - tgui_interact(user) - -/obj/machinery/computer/operating/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "OperatingComputer", "Patient Monitor") - ui.open() - -/obj/machinery/computer/operating/tgui_data(mob/user) - var/data[0] - var/mob/living/carbon/human/occupant - if(table) - occupant = table.victim - data["hasOccupant"] = occupant ? 1 : 0 - var/occupantData[0] - - if(occupant) - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - occupantData["health"] = occupant.health - occupantData["maxHealth"] = occupant.maxHealth - occupantData["minHealth"] = config.health_threshold_dead - occupantData["bruteLoss"] = occupant.getBruteLoss() - occupantData["oxyLoss"] = occupant.getOxyLoss() - occupantData["toxLoss"] = occupant.getToxLoss() - occupantData["fireLoss"] = occupant.getFireLoss() - occupantData["paralysis"] = occupant.paralysis - occupantData["hasBlood"] = 0 - occupantData["bodyTemperature"] = occupant.bodytemperature - occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations - // Because we can put simple_animals in here, we need to do something tricky to get things working nice - occupantData["temperatureSuitability"] = 0 // 0 is the baseline - if(ishuman(occupant) && occupant.species) - // I wanna do something where the bar gets bluer as the temperature gets lower - // For now, I'll just use the standard format for the temperature status - var/datum/species/sp = occupant.species - if(occupant.bodytemperature < sp.cold_level_3) - occupantData["temperatureSuitability"] = -3 - else if(occupant.bodytemperature < sp.cold_level_2) - occupantData["temperatureSuitability"] = -2 - else if(occupant.bodytemperature < sp.cold_level_1) - occupantData["temperatureSuitability"] = -1 - else if(occupant.bodytemperature > sp.heat_level_3) - occupantData["temperatureSuitability"] = 3 - else if(occupant.bodytemperature > sp.heat_level_2) - occupantData["temperatureSuitability"] = 2 - else if(occupant.bodytemperature > sp.heat_level_1) - occupantData["temperatureSuitability"] = 1 - else if(isanimal(occupant)) - var/mob/living/simple_mob/silly = occupant - if(silly.bodytemperature < silly.minbodytemp) - occupantData["temperatureSuitability"] = -3 - else if(silly.bodytemperature > silly.maxbodytemp) - occupantData["temperatureSuitability"] = 3 - // Blast you, imperial measurement system - occupantData["btCelsius"] = occupant.bodytemperature - T0C - occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 - - if(ishuman(occupant) && !(NO_BLOOD in occupant.species.flags) && occupant.vessel) - occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) - occupantData["hasBlood"] = 1 - var/blood_volume = round(occupant.vessel.get_reagent_amount("blood")) - occupantData["bloodLevel"] = blood_volume - occupantData["bloodMax"] = occupant.species.blood_volume - occupantData["bloodPercent"] = round(100*(blood_volume/occupant.species.blood_volume), 0.01) //copy pasta ends here - - occupantData["bloodType"] = occupant.dna.b_type - occupantData["surgery"] = build_surgery_list(user) - - data["occupant"] = occupantData - data["verbose"]=verbose - data["oxyAlarm"]=oxyAlarm - data["choice"]=choice - data["health"]=healthAnnounce - data["crit"]=crit - data["healthAlarm"]=healthAlarm - data["oxy"]=oxy - - return data - -/obj/machinery/computer/operating/tgui_act(action, params) - if(..()) - return TRUE - if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.set_machine(src) - - . = TRUE - switch(action) - if("verboseOn") - verbose = TRUE - if("verboseOff") - verbose = FALSE - if("healthOn") - healthAnnounce = TRUE - if("healthOff") - healthAnnounce = FALSE - if("critOn") - crit = TRUE - if("critOff") - crit = FALSE - if("oxyOn") - oxy = TRUE - if("oxyOff") - oxy = FALSE - if("oxy_adj") - oxyAlarm = clamp(text2num(params["new"]), -100, 100) - if("choiceOn") - choice = TRUE - if("choiceOff") - choice = FALSE - if("health_adj") - healthAlarm = clamp(text2num(params["new"]), -100, 100) - else - return FALSE - -/obj/machinery/computer/operating/process() - if(table && table.check_victim()) - if(verbose) - if(patientName!=table.victim.name) - patientName=table.victim.name - atom_say("New patient detected, loading stats") - victim = table.victim - atom_say("[victim.real_name], [victim.dna.b_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]") - SStgui.update_uis(src) - if(nextTick < world.time) - nextTick=world.time + OP_COMPUTER_COOLDOWN - if(crit && victim.health <= -50 ) - playsound(src.loc, 'sound/machines/defib_success.ogg', 50, 0) - if(oxy && victim.getOxyLoss()>oxyAlarm) - playsound(src.loc, 'sound/machines/defib_safetyOff.ogg', 50, 0) - if(healthAnnounce && ((victim.health / victim.maxHealth) * 100) <= healthAlarm) - atom_say("[round(((victim.health / victim.maxHealth) * 100))]% health.") - -// Surgery Helpers -/obj/machinery/computer/operating/proc/build_surgery_list(mob/user) - if(!istype(victim)) - return null - - . = list() - - for(var/limb in victim.organs_by_name) - var/obj/item/organ/external/E = victim.organs_by_name[limb] - if(E && E.open) - . += list(list("name" = E.name, "currentStage" = find_stage(E), "nextSteps" = find_next_steps(user, limb))) - -/** - * This proc is actually hell. I hate the surgery system Polaris uses. - * Basically, surgery is completely stateless, and what "stage" we're on is just dependent - * on the current state of 5 separate variables that determine what stages we can perform - * next. - * - * So, here's a little guide to understand this proc: - * Surgery is broken down into 5 different variables: - * `open`, - * `stage`, - * `cavity`, - * `burn_stage`, - * and `brute_stage`. - * Naturally, the values assigned to these don't use defines or names or anything, they're just magic numbers. - * So, we have to figure out ourselves what we should call each value. - * Open can be 4 values, and represents the "openness" of the surgery site. - * 1 = Cut Open. - * 2 = Retracted. - * 2.5 = Bones cut. - * 3 = Bones spread. - * Stage can be 3 values, and represents the progress in fixing broken bones - * 0 = Closed, can be either "we're done" or "we haven't started" FFS. - * 1 = Bones glued. - * 2 = Bones set. - * Cavity is just representing the cavity implant surgeries, and can be 2 values. - * 0 = Cavity Closed - * 1 = Cavity Open - * burn_stage and brute_stage are literally only used for repairing brute/burn damage to limbs - * I have no idea why you would ever perform these surgeries, given that Bicaradine and Kelotane exist. - * So I'm not even going to bother trying to represent them here. Fuck it. - */ -/obj/machinery/computer/operating/proc/find_stage(var/obj/item/organ/external/E) - . = "None." - switch(E.open) - if(1) - . = "Incision made." - if(2) - . = "Surgical site opened." - switch(E.stage) - // if(0) // Nothing. - if(1) - . = "Surgical site opened; Bones glued." - if(2) - . = "Surgical site opened; Bones set." - switch(E.cavity) - if(1) - . = "Surgical site opened; Cavity open." - if(2.5) // WHY IS THIS A FLOAT. WHY? - . = "Bones cut." - switch(E.stage) - // if(0) // Nothing. - if(1) - . = "Bones cut; Bones glued." - if(2) - . = "Bones cut; Bones set." - if(3) - . = "Bones retracted." - switch(E.stage) - // if(0) // Nothing. - if(1) - . = "Bones retracted; Bones glued." - if(2) - . = "Bones retracted; Bones reset." - switch(E.cavity) - if(1) - . = "Bones retracted; Cavity open." - -/** - * This converts a typepath into a pretty name. - * As best as it can, anyways. - */ -/proc/pretty_type(var/datum/A) - var/typeStr = "[A.type]" - . = copytext(typeStr, findlasttext(typeStr, "/") + 1, length(typeStr) + 1) - . = capitalize(replacetext(., "_", " ")) - -/proc/get_surgery_steps_without_basetypes() - var/static/list/good_surgeries = list() - if(LAZYLEN(good_surgeries)) - return good_surgeries - var/static/list/banned_surgery_steps = list( - /datum/surgery_step, - /datum/surgery_step/generic, - /datum/surgery_step/open_encased, - /datum/surgery_step/repairflesh, - /datum/surgery_step/face, - /datum/surgery_step/cavity, - /datum/surgery_step/limb, - /datum/surgery_step/brainstem, - /datum/surgery_step/generic/ripper, - ) - good_surgeries = surgery_steps - for(var/datum/surgery_step/S in good_surgeries) - if(S.type in banned_surgery_steps) - good_surgeries -= S - if(!LAZYLEN(S.allowed_tools)) - good_surgeries -= S - return good_surgeries - -/** - * Funnily enough, this proc is actually considerably less awful than find_stage. - * All we have to do is check what surgeries can be done, like surgery mechanics themselves do. - * Then, build a string telling the user what they can do next. - */ -/obj/machinery/computer/operating/proc/find_next_steps(mob/user, zone) - . = list() - for(var/datum/surgery_step/S in get_surgery_steps_without_basetypes()) - if(S.can_use(user, victim, zone, null) && S.is_valid_target(victim)) - var/allowed_tools_by_name = list() - for(var/tool in S.allowed_tools) - // Exempt ghetto tools. - if(S.allowed_tools[tool] < 100) - continue - var/obj/tool_path = tool - allowed_tools_by_name += capitalize(initial(tool_path.name)) - // Please for the love of all that is holy, someone make surgery steps - // have names so I don't have to do this stupid pretty_type shit. - . += "[pretty_type(S)]: [english_list(allowed_tools_by_name)]" +#define OP_COMPUTER_COOLDOWN 60 + +/obj/machinery/computer/operating + name = "patient monitoring console" + desc = "Used to monitor the vitals of a patient." + density = TRUE + anchored = TRUE + icon_keyboard = "med_key" + icon_screen = "crew" + circuit = /obj/item/weapon/circuitboard/operating + var/obj/machinery/optable/table = null + var/mob/living/carbon/human/victim = null + var/verbose = 1 //general speaker toggle + var/patientName = null + var/oxyAlarm = 30 //oxy damage at which the computer will beep + var/choice = 0 //just for going into and out of the options menu + var/healthAnnounce = 1 //healther announcer toggle + var/crit = 1 //crit beeping toggle + var/nextTick = OP_COMPUTER_COOLDOWN + var/healthAlarm = 50 + var/oxy = 1 //oxygen beeping toggle + +/obj/machinery/computer/operating/New() + ..() + for(var/direction in list(NORTH,EAST,SOUTH,WEST)) + table = locate(/obj/machinery/optable, get_step(src, direction)) + if(table) + table.computer = src + break + +/obj/machinery/computer/operating/Destroy() + if(table) + table.computer = null + table = null + if(victim) + victim = null + return ..() + +/obj/machinery/computer/operating/attack_ai(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + tgui_interact(user) + + +/obj/machinery/computer/operating/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + tgui_interact(user) + +/obj/machinery/computer/operating/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "OperatingComputer", "Patient Monitor") + ui.open() + +/obj/machinery/computer/operating/tgui_data(mob/user) + var/data[0] + var/mob/living/carbon/human/occupant + if(table) + occupant = table.victim + data["hasOccupant"] = occupant ? 1 : 0 + var/occupantData[0] + + if(occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.maxHealth + occupantData["minHealth"] = config.health_threshold_dead + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["paralysis"] = occupant.paralysis + occupantData["hasBlood"] = 0 + occupantData["bodyTemperature"] = occupant.bodytemperature + occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations + // Because we can put simple_animals in here, we need to do something tricky to get things working nice + occupantData["temperatureSuitability"] = 0 // 0 is the baseline + if(ishuman(occupant) && occupant.species) + // I wanna do something where the bar gets bluer as the temperature gets lower + // For now, I'll just use the standard format for the temperature status + var/datum/species/sp = occupant.species + if(occupant.bodytemperature < sp.cold_level_3) + occupantData["temperatureSuitability"] = -3 + else if(occupant.bodytemperature < sp.cold_level_2) + occupantData["temperatureSuitability"] = -2 + else if(occupant.bodytemperature < sp.cold_level_1) + occupantData["temperatureSuitability"] = -1 + else if(occupant.bodytemperature > sp.heat_level_3) + occupantData["temperatureSuitability"] = 3 + else if(occupant.bodytemperature > sp.heat_level_2) + occupantData["temperatureSuitability"] = 2 + else if(occupant.bodytemperature > sp.heat_level_1) + occupantData["temperatureSuitability"] = 1 + else if(isanimal(occupant)) + var/mob/living/simple_mob/silly = occupant + if(silly.bodytemperature < silly.minbodytemp) + occupantData["temperatureSuitability"] = -3 + else if(silly.bodytemperature > silly.maxbodytemp) + occupantData["temperatureSuitability"] = 3 + // Blast you, imperial measurement system + occupantData["btCelsius"] = occupant.bodytemperature - T0C + occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 + + if(ishuman(occupant) && !(NO_BLOOD in occupant.species.flags) && occupant.vessel) + occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) + occupantData["hasBlood"] = 1 + var/blood_volume = round(occupant.vessel.get_reagent_amount("blood")) + occupantData["bloodLevel"] = blood_volume + occupantData["bloodMax"] = occupant.species.blood_volume + occupantData["bloodPercent"] = round(100*(blood_volume/occupant.species.blood_volume), 0.01) //copy pasta ends here + + occupantData["bloodType"] = occupant.dna.b_type + occupantData["surgery"] = build_surgery_list(user) + + data["occupant"] = occupantData + data["verbose"]=verbose + data["oxyAlarm"]=oxyAlarm + data["choice"]=choice + data["health"]=healthAnnounce + data["crit"]=crit + data["healthAlarm"]=healthAlarm + data["oxy"]=oxy + + return data + +/obj/machinery/computer/operating/tgui_act(action, params) + if(..()) + return TRUE + if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + + . = TRUE + switch(action) + if("verboseOn") + verbose = TRUE + if("verboseOff") + verbose = FALSE + if("healthOn") + healthAnnounce = TRUE + if("healthOff") + healthAnnounce = FALSE + if("critOn") + crit = TRUE + if("critOff") + crit = FALSE + if("oxyOn") + oxy = TRUE + if("oxyOff") + oxy = FALSE + if("oxy_adj") + oxyAlarm = clamp(text2num(params["new"]), -100, 100) + if("choiceOn") + choice = TRUE + if("choiceOff") + choice = FALSE + if("health_adj") + healthAlarm = clamp(text2num(params["new"]), -100, 100) + else + return FALSE + +/obj/machinery/computer/operating/process() + if(table && table.check_victim()) + if(verbose) + if(patientName!=table.victim.name) + patientName=table.victim.name + atom_say("New patient detected, loading stats") + victim = table.victim + atom_say("[victim.real_name], [victim.dna.b_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]") + SStgui.update_uis(src) + if(nextTick < world.time) + nextTick=world.time + OP_COMPUTER_COOLDOWN + if(crit && victim.health <= -50 ) + playsound(src.loc, 'sound/machines/defib_success.ogg', 50, 0) + if(oxy && victim.getOxyLoss()>oxyAlarm) + playsound(src.loc, 'sound/machines/defib_safetyOff.ogg', 50, 0) + if(healthAnnounce && ((victim.health / victim.maxHealth) * 100) <= healthAlarm) + atom_say("[round(((victim.health / victim.maxHealth) * 100))]% health.") + +// Surgery Helpers +/obj/machinery/computer/operating/proc/build_surgery_list(mob/user) + if(!istype(victim)) + return null + + . = list() + + for(var/limb in victim.organs_by_name) + var/obj/item/organ/external/E = victim.organs_by_name[limb] + if(E && E.open) + . += list(list("name" = E.name, "currentStage" = find_stage(E), "nextSteps" = find_next_steps(user, limb))) + +/** + * This proc is actually hell. I hate the surgery system Polaris uses. + * Basically, surgery is completely stateless, and what "stage" we're on is just dependent + * on the current state of 5 separate variables that determine what stages we can perform + * next. + * + * So, here's a little guide to understand this proc: + * Surgery is broken down into 5 different variables: + * `open`, + * `stage`, + * `cavity`, + * `burn_stage`, + * and `brute_stage`. + * Naturally, the values assigned to these don't use defines or names or anything, they're just magic numbers. + * So, we have to figure out ourselves what we should call each value. + * Open can be 4 values, and represents the "openness" of the surgery site. + * 1 = Cut Open. + * 2 = Retracted. + * 2.5 = Bones cut. + * 3 = Bones spread. + * Stage can be 3 values, and represents the progress in fixing broken bones + * 0 = Closed, can be either "we're done" or "we haven't started" FFS. + * 1 = Bones glued. + * 2 = Bones set. + * Cavity is just representing the cavity implant surgeries, and can be 2 values. + * 0 = Cavity Closed + * 1 = Cavity Open + * burn_stage and brute_stage are literally only used for repairing brute/burn damage to limbs + * I have no idea why you would ever perform these surgeries, given that Bicaradine and Kelotane exist. + * So I'm not even going to bother trying to represent them here. Fuck it. + */ +/obj/machinery/computer/operating/proc/find_stage(var/obj/item/organ/external/E) + . = "None." + switch(E.open) + if(1) + . = "Incision made." + if(2) + . = "Surgical site opened." + switch(E.stage) + // if(0) // Nothing. + if(1) + . = "Surgical site opened; Bones glued." + if(2) + . = "Surgical site opened; Bones set." + switch(E.cavity) + if(1) + . = "Surgical site opened; Cavity open." + if(2.5) // WHY IS THIS A FLOAT. WHY? + . = "Bones cut." + switch(E.stage) + // if(0) // Nothing. + if(1) + . = "Bones cut; Bones glued." + if(2) + . = "Bones cut; Bones set." + if(3) + . = "Bones retracted." + switch(E.stage) + // if(0) // Nothing. + if(1) + . = "Bones retracted; Bones glued." + if(2) + . = "Bones retracted; Bones reset." + switch(E.cavity) + if(1) + . = "Bones retracted; Cavity open." + +/** + * This converts a typepath into a pretty name. + * As best as it can, anyways. + */ +/proc/pretty_type(var/datum/A) + var/typeStr = "[A.type]" + . = copytext(typeStr, findlasttext(typeStr, "/") + 1, length(typeStr) + 1) + . = capitalize(replacetext(., "_", " ")) + +/proc/get_surgery_steps_without_basetypes() + var/static/list/good_surgeries = list() + if(LAZYLEN(good_surgeries)) + return good_surgeries + var/static/list/banned_surgery_steps = list( + /datum/surgery_step, + /datum/surgery_step/generic, + /datum/surgery_step/open_encased, + /datum/surgery_step/repairflesh, + /datum/surgery_step/face, + /datum/surgery_step/cavity, + /datum/surgery_step/limb, + /datum/surgery_step/brainstem, + /datum/surgery_step/generic/ripper, + ) + good_surgeries = surgery_steps + for(var/datum/surgery_step/S in good_surgeries) + if(S.type in banned_surgery_steps) + good_surgeries -= S + if(!LAZYLEN(S.allowed_tools)) + good_surgeries -= S + return good_surgeries + +/** + * Funnily enough, this proc is actually considerably less awful than find_stage. + * All we have to do is check what surgeries can be done, like surgery mechanics themselves do. + * Then, build a string telling the user what they can do next. + */ +/obj/machinery/computer/operating/proc/find_next_steps(mob/user, zone) + . = list() + for(var/datum/surgery_step/S in get_surgery_steps_without_basetypes()) + if(S.can_use(user, victim, zone, null) && S.is_valid_target(victim)) + var/allowed_tools_by_name = list() + for(var/tool in S.allowed_tools) + // Exempt ghetto tools. + if(S.allowed_tools[tool] < 100) + continue + var/obj/tool_path = tool + allowed_tools_by_name += capitalize(initial(tool_path.name)) + // Please for the love of all that is holy, someone make surgery steps + // have names so I don't have to do this stupid pretty_type shit. + . += "[pretty_type(S)]: [english_list(allowed_tools_by_name)]" diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index 923dd620a20..e1d2c7fb099 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -1,135 +1,135 @@ -/obj/machinery/computer/aifixer - name = "\improper AI system integrity restorer" - desc = "Used with intelliCards containing nonfunctional AIs to restore them to working order." - req_one_access = list(access_robotics, access_heads) - circuit = /obj/item/weapon/circuitboard/aifixer - icon_keyboard = "tech_key" - icon_screen = "ai-fixer" - light_color = LIGHT_COLOR_PINK - - active_power_usage = 1000 - - /// Variable containing transferred AI - var/mob/living/silicon/ai/occupier - /// Variable dictating if we are in the process of restoring the occupier AI - var/restoring = FALSE - -/obj/machinery/computer/aifixer/attackby(obj/item/I, mob/living/user) - if(I.has_tool_quality(TOOL_SCREWDRIVER)) - if(occupier) - if(stat & (NOPOWER|BROKEN)) - to_chat(user, "The screws on [name]'s screen won't budge.") - else - to_chat(user, "The screws on [name]'s screen won't budge and it emits a warning beep.") - return - if(istype(I, /obj/item/device/aicard)) - if(stat & (NOPOWER|BROKEN)) - to_chat(user, "This terminal isn't functioning right now.") - return - if(restoring) - to_chat(user, "Terminal is busy restoring [occupier] right now.") - return - - var/obj/item/device/aicard/card = I - if(occupier) - if(card.grab_ai(occupier, user)) - occupier = null - else if(card.carded_ai) - var/mob/living/silicon/ai/new_occupant = card.carded_ai - to_chat(new_occupant, "You have been transferred into a stationary terminal. Sadly there is no remote access from here.") - to_chat(user, "Transfer Successful: [new_occupant] placed within stationary terminal.") - new_occupant.forceMove(src) - new_occupant.cancel_camera() - new_occupant.control_disabled = TRUE - occupier = new_occupant - card.clear() - update_icon() - else - to_chat(user, "There is no AI loaded onto this computer, and no AI loaded onto [I]. What exactly are you trying to do here?") - return ..() - -/obj/machinery/computer/aifixer/attack_hand(mob/user) - if(stat & (NOPOWER|BROKEN)) - return - tgui_interact(user) - -/obj/machinery/computer/aifixer/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AiRestorer", name) - ui.open() - -/obj/machinery/computer/aifixer/tgui_data(mob/user) - var/list/data = list() - - data["ejectable"] = FALSE - data["AI_present"] = FALSE - data["error"] = null - if(!occupier) - data["error"] = "Please transfer an AI unit." - else - data["AI_present"] = TRUE - data["name"] = occupier.name - data["restoring"] = restoring - data["health"] = (occupier.health + 100) / 2 - data["isDead"] = occupier.stat == DEAD - var/list/laws = list() - for(var/datum/ai_law/law in occupier.laws.all_laws()) - laws += "[law.get_index()]: [law.law]" - data["laws"] = laws - - return data - -/obj/machinery/computer/aifixer/tgui_act(action, params) - if(..()) - return TRUE - if(!occupier) - restoring = FALSE - - if(action) - playsound(src, "terminal_type", 50, 1) - - switch(action) - if("PRG_beginReconstruction") - if(occupier?.health < 100) - to_chat(usr, "Reconstruction in progress. This will take several minutes.") - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 25, FALSE) - restoring = TRUE - var/mob/observer/dead/ghost = occupier.get_ghost() - if(ghost) - ghost.notify_revive("Your core files are being restored!", source = src) - . = TRUE - -/obj/machinery/computer/aifixer/proc/Fix() - use_power(active_power_usage) - occupier.adjustOxyLoss(-5, 0, FALSE) - occupier.adjustFireLoss(-5, 0, FALSE) - occupier.adjustBruteLoss(-5, 0) - if(occupier.health >= 0 && occupier.stat == DEAD) - occupier.revive() - - return occupier.health < 100 - -/obj/machinery/computer/aifixer/process() - if(..()) - if(restoring) - var/oldstat = occupier.stat - restoring = Fix() - if(oldstat != occupier.stat) - update_icon() - -/obj/machinery/computer/aifixer/update_icon() - . = ..() - if(stat & (NOPOWER|BROKEN)) - return - - if(restoring) - . += "ai-fixer-on" - if (occupier) - switch (occupier.stat) - if (CONSCIOUS) - . += "ai-fixer-full" - if (UNCONSCIOUS) - . += "ai-fixer-404" - else +/obj/machinery/computer/aifixer + name = "\improper AI system integrity restorer" + desc = "Used with intelliCards containing nonfunctional AIs to restore them to working order." + req_one_access = list(access_robotics, access_heads) + circuit = /obj/item/weapon/circuitboard/aifixer + icon_keyboard = "tech_key" + icon_screen = "ai-fixer" + light_color = LIGHT_COLOR_PINK + + active_power_usage = 1000 + + /// Variable containing transferred AI + var/mob/living/silicon/ai/occupier + /// Variable dictating if we are in the process of restoring the occupier AI + var/restoring = FALSE + +/obj/machinery/computer/aifixer/attackby(obj/item/I, mob/living/user) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + if(occupier) + if(stat & (NOPOWER|BROKEN)) + to_chat(user, "The screws on [name]'s screen won't budge.") + else + to_chat(user, "The screws on [name]'s screen won't budge and it emits a warning beep.") + return + if(istype(I, /obj/item/device/aicard)) + if(stat & (NOPOWER|BROKEN)) + to_chat(user, "This terminal isn't functioning right now.") + return + if(restoring) + to_chat(user, "Terminal is busy restoring [occupier] right now.") + return + + var/obj/item/device/aicard/card = I + if(occupier) + if(card.grab_ai(occupier, user)) + occupier = null + else if(card.carded_ai) + var/mob/living/silicon/ai/new_occupant = card.carded_ai + to_chat(new_occupant, "You have been transferred into a stationary terminal. Sadly there is no remote access from here.") + to_chat(user, "Transfer Successful: [new_occupant] placed within stationary terminal.") + new_occupant.forceMove(src) + new_occupant.cancel_camera() + new_occupant.control_disabled = TRUE + occupier = new_occupant + card.clear() + update_icon() + else + to_chat(user, "There is no AI loaded onto this computer, and no AI loaded onto [I]. What exactly are you trying to do here?") + return ..() + +/obj/machinery/computer/aifixer/attack_hand(mob/user) + if(stat & (NOPOWER|BROKEN)) + return + tgui_interact(user) + +/obj/machinery/computer/aifixer/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AiRestorer", name) + ui.open() + +/obj/machinery/computer/aifixer/tgui_data(mob/user) + var/list/data = list() + + data["ejectable"] = FALSE + data["AI_present"] = FALSE + data["error"] = null + if(!occupier) + data["error"] = "Please transfer an AI unit." + else + data["AI_present"] = TRUE + data["name"] = occupier.name + data["restoring"] = restoring + data["health"] = (occupier.health + 100) / 2 + data["isDead"] = occupier.stat == DEAD + var/list/laws = list() + for(var/datum/ai_law/law in occupier.laws.all_laws()) + laws += "[law.get_index()]: [law.law]" + data["laws"] = laws + + return data + +/obj/machinery/computer/aifixer/tgui_act(action, params) + if(..()) + return TRUE + if(!occupier) + restoring = FALSE + + if(action) + playsound(src, "terminal_type", 50, 1) + + switch(action) + if("PRG_beginReconstruction") + if(occupier?.health < 100) + to_chat(usr, "Reconstruction in progress. This will take several minutes.") + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 25, FALSE) + restoring = TRUE + var/mob/observer/dead/ghost = occupier.get_ghost() + if(ghost) + ghost.notify_revive("Your core files are being restored!", source = src) + . = TRUE + +/obj/machinery/computer/aifixer/proc/Fix() + use_power(active_power_usage) + occupier.adjustOxyLoss(-5, 0, FALSE) + occupier.adjustFireLoss(-5, 0, FALSE) + occupier.adjustBruteLoss(-5, 0) + if(occupier.health >= 0 && occupier.stat == DEAD) + occupier.revive() + + return occupier.health < 100 + +/obj/machinery/computer/aifixer/process() + if(..()) + if(restoring) + var/oldstat = occupier.stat + restoring = Fix() + if(oldstat != occupier.stat) + update_icon() + +/obj/machinery/computer/aifixer/update_icon() + . = ..() + if(stat & (NOPOWER|BROKEN)) + return + + if(restoring) + . += "ai-fixer-on" + if (occupier) + switch (occupier.stat) + if (CONSCIOUS) + . += "ai-fixer-full" + if (UNCONSCIOUS) + . += "ai-fixer-404" + else . += "ai-fixer-empty" \ No newline at end of file diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm index dd9a4d78865..1834971be2d 100644 --- a/code/game/machinery/computer/arcade.dm +++ b/code/game/machinery/computer/arcade.dm @@ -1,1341 +1,1341 @@ -/obj/machinery/computer/arcade - name = "random arcade" - desc = "random arcade machine" - icon_state = "arcade1" - icon_keyboard = null - clicksound = null //Gets too spammy and makes no sense for arcade to have the console keyboard noise anyway - var/list/prizes = list( /obj/item/weapon/storage/box/snappops = 2, - /obj/item/toy/blink = 2, - /obj/item/clothing/under/syndicate/tacticool = 2, - /obj/item/toy/sword = 2, - /obj/item/weapon/storage/box/capguntoy = 2, - /obj/item/weapon/gun/projectile/revolver/toy/crossbow = 2, - /obj/item/clothing/suit/syndicatefake = 2, - /obj/item/weapon/storage/fancy/crayons = 2, - /obj/item/toy/spinningtoy = 2, - /obj/random/mech_toy = 1, - /obj/item/weapon/reagent_containers/spray/waterflower = 1, - /obj/random/action_figure = 1, - /obj/random/plushie = 1, - /obj/item/toy/cultsword = 1, - /obj/item/toy/bouquet/fake = 1, - /obj/item/clothing/accessory/badge/sheriff = 2, - /obj/item/clothing/head/cowboy/small = 2, - /obj/item/toy/stickhorse = 2 - ) - var/list/special_prizes = list() // Holds instanced objects, intended for admins to shove surprises inside or something. - -/obj/machinery/computer/arcade/Initialize() - . = ..() - // If it's a generic arcade machine, pick a random arcade - // circuit board for it and make the new machine - if(!circuit) - var/choice = pick(subtypesof(/obj/item/weapon/circuitboard/arcade) - /obj/item/weapon/circuitboard/arcade/clawmachine) - var/obj/item/weapon/circuitboard/CB = new choice() - new CB.build_path(loc, CB) - return INITIALIZE_HINT_QDEL - -/obj/machinery/computer/arcade/proc/prizevend() - if(LAZYLEN(special_prizes)) // Downstream wanted the 'win things inside contents sans circuitboard' feature kept. - var/atom/movable/AM = pick_n_take(special_prizes) - AM.forceMove(get_turf(src)) - special_prizes -= AM - - else if(LAZYLEN(prizes)) - var/prizeselect = pickweight(prizes) - new prizeselect(src.loc) - - if(istype(prizeselect, /obj/item/clothing/suit/syndicatefake)) //Helmet is part of the suit - new /obj/item/clothing/head/syndicatefake(src.loc) - -/obj/machinery/computer/arcade/attack_ai(mob/user as mob) - return attack_hand(user) - - -/obj/machinery/computer/arcade/emp_act(severity) - if(stat & (NOPOWER|BROKEN)) - ..(severity) - return - var/empprize = null - var/num_of_prizes = 0 - switch(severity) - if(1) - num_of_prizes = rand(1,4) - if(2) - num_of_prizes = rand(1,3) - if(3) - num_of_prizes = rand(0,2) - if(4) - num_of_prizes = rand(0,1) - for(num_of_prizes; num_of_prizes > 0; num_of_prizes--) - empprize = pickweight(prizes) - new empprize(src.loc) - - ..(severity) - -/////////////////// -// BATTLE HERE // -/////////////////// - -/obj/machinery/computer/arcade/battle - name = "Battler" - desc = "Fight through what space has to offer!" - icon_state = "arcade2" - icon_screen = "battler" - circuit = /obj/item/weapon/circuitboard/arcade/battle - var/enemy_name = "Space Villian" - var/temp = "Winners don't use space drugs" //Temporary message, for attack messages, etc - var/enemy_action = "" - var/player_hp = 30 //Player health/attack points - var/player_mp = 10 - var/enemy_hp = 45 //Enemy health/attack points - var/enemy_mp = 20 - var/gameover = 0 - var/blocked = 0 //Player cannot attack/heal while set - var/turtle = 0 - -/obj/machinery/computer/arcade/battle/Initialize() - . = ..() - randomize_characters() - -/obj/machinery/computer/arcade/battle/proc/randomize_characters() - var/name_action - var/name_part1 - var/name_part2 - - name_action = pick("Defeat ", "Annihilate ", "Save ", "Strike ", "Stop ", "Destroy ", "Robust ", "Romance ", "Pwn ", "Own ", "Ban ") - - name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ") - name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "slime", "Griefer", "ERPer", "Lizard Man", "Unicorn", "Bloopers") - - enemy_name = replacetext((name_part1 + name_part2), "the ", "") - name = (name_action + name_part1 + name_part2) - - -/obj/machinery/computer/arcade/battle/attack_hand(mob/user as mob) - if(..()) - return - user.set_machine(src) - tgui_interact(user) - -/obj/machinery/computer/arcade/battle/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "ArcadeBattle", name) - ui.open() - -/obj/machinery/computer/arcade/battle/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) - var/list/data = ..() - data["name"] = name - data["temp"] = temp - data["enemyAction"] = enemy_action - data["enemyName"] = enemy_name - data["playerHP"] = player_hp - data["playerMP"] = player_mp - data["enemyHP"] = enemy_hp - data["gameOver"] = gameover - return data - -/obj/machinery/computer/arcade/battle/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - if(!blocked && !gameover) - switch(action) - if("attack") - blocked = 1 - var/attackamt = rand(2,6) - temp = "You attack for [attackamt] damage!" - playsound(src, 'sound/arcade/hit.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - if(turtle > 0) - turtle-- - - sleep(10) - enemy_hp -= attackamt - arcade_action() - - if("heal") - blocked = 1 - var/pointamt = rand(1,3) - var/healamt = rand(6,8) - temp = "You use [pointamt] magic to heal for [healamt] damage!" - playsound(src, 'sound/arcade/heal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - turtle++ - - sleep(10) - player_mp -= pointamt - player_hp += healamt - blocked = 1 - arcade_action() - - if("charge") - blocked = 1 - var/chargeamt = rand(4,7) - temp = "You regain [chargeamt] points" - playsound(src, 'sound/arcade/mana.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - player_mp += chargeamt - if(turtle > 0) - turtle-- - - sleep(10) - arcade_action() - - - if(action == "newgame") //Reset everything - temp = "New Round" - player_hp = 30 - player_mp = 10 - enemy_hp = 45 - enemy_mp = 20 - gameover = 0 - turtle = 0 - - if(emagged) - randomize_characters() - emagged = 0 - - add_fingerprint(usr) - return TRUE - -/obj/machinery/computer/arcade/battle/proc/arcade_action() - if ((enemy_mp <= 0) || (enemy_hp <= 0)) - if(!gameover) - gameover = 1 - temp = "[enemy_name] has fallen! Rejoice!" - playsound(src, 'sound/arcade/win.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - - if(emagged) - feedback_inc("arcade_win_emagged") - new /obj/effect/spawner/newbomb/timer/syndicate(src.loc) - new /obj/item/clothing/head/collectable/petehat(src.loc) - message_admins("[key_name_admin(usr)] has outbombed Cuban Pete and been awarded a bomb.") - log_game("[key_name_admin(usr)] has outbombed Cuban Pete and been awarded a bomb.") - randomize_characters() - emagged = 0 - else if(!contents.len) - feedback_inc("arcade_win_normal") - prizevend() - - else - feedback_inc("arcade_win_normal") - prizevend() - - else if (emagged && (turtle >= 4)) - var/boomamt = rand(5,10) - enemy_action = "[enemy_name] throws a bomb, exploding you for [boomamt] damage!" - playsound(src, 'sound/arcade/boom.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - player_hp -= boomamt - - else if ((enemy_mp <= 5) && (prob(70))) - var/stealamt = rand(2,3) - enemy_action = "[enemy_name] steals [stealamt] of your power!" - playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - player_mp -= stealamt - - if (player_mp <= 0) - gameover = 1 - sleep(10) - temp = "You have been drained! GAME OVER" - if(emagged) - feedback_inc("arcade_loss_mana_emagged") - usr.gib() - else - feedback_inc("arcade_loss_mana_normal") - - else if ((enemy_hp <= 10) && (enemy_mp > 4)) - enemy_action = "[enemy_name] heals for 4 health!" - playsound(src, 'sound/arcade/heal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - enemy_hp += 4 - enemy_mp -= 4 - - else - var/attackamt = rand(3,6) - enemy_action = "[enemy_name] attacks for [attackamt] damage!" - playsound(src, 'sound/arcade/hit.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - player_hp -= attackamt - - if ((player_mp <= 0) || (player_hp <= 0)) - gameover = 1 - temp = "You have been crushed! GAME OVER" - playsound(src, 'sound/arcade/lose.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - if(emagged) - feedback_inc("arcade_loss_hp_emagged") - usr.gib() - else - feedback_inc("arcade_loss_hp_normal") - - blocked = 0 - return - - -/obj/machinery/computer/arcade/battle/emag_act(var/charges, var/mob/user) - if(!emagged) - to_chat(user, span("notice","You override the cheat code menu and skip to Cheat #[rand(1, 50)]: Hyper-Lethal Mode.")) - - temp = "If you die in the game, you die for real!" - player_hp = 30 - player_mp = 10 - enemy_hp = 45 - enemy_mp = 20 - gameover = 0 - blocked = 0 - emagged = 1 - - enemy_name = "Cuban Pete" - name = "Outbomb Cuban Pete" - - return 1 - - -////////////////////////// -// ORION TRAIL HERE // -////////////////////////// -#define ORION_TRAIL_WINTURN 9 - -//Orion Trail Events -#define ORION_TRAIL_RAIDERS "Raiders" -#define ORION_TRAIL_FLUX "Interstellar Flux" -#define ORION_TRAIL_ILLNESS "Illness" -#define ORION_TRAIL_BREAKDOWN "Breakdown" -#define ORION_TRAIL_MUTINY "Mutiny?" -#define ORION_TRAIL_MUTINY_ATTACK "Mutinous Ambush" -#define ORION_TRAIL_MALFUNCTION "Malfunction" -#define ORION_TRAIL_COLLISION "Collision" -#define ORION_TRAIL_SPACEPORT "Spaceport" -#define ORION_TRAIL_BLACKHOLE "BlackHole" - -#define ORION_STATUS_START 1 -#define ORION_STATUS_NORMAL 2 -#define ORION_STATUS_GAMEOVER 3 -#define ORION_STATUS_MARKET 4 - -/obj/machinery/computer/arcade/orion_trail - name = "The Orion Trail" - desc = "Learn how our ancestors got to Orion, and have fun in the process!" - icon_state = "arcade1" - icon_screen = "orion" - circuit = /obj/item/weapon/circuitboard/arcade/orion_trail - var/busy = 0 //prevent clickspam that allowed people to ~speedrun~ the game. - var/engine = 0 - var/hull = 0 - var/electronics = 0 - var/food = 80 - var/fuel = 60 - var/turns = 4 - var/alive = 4 - var/eventdat = null - var/event = null - var/list/settlers = list("Harry","Larry","Bob") - var/list/events = list(ORION_TRAIL_RAIDERS = 3, - ORION_TRAIL_FLUX = 1, - ORION_TRAIL_ILLNESS = 3, - ORION_TRAIL_BREAKDOWN = 2, - ORION_TRAIL_MUTINY = 3, - ORION_TRAIL_MALFUNCTION = 2, - ORION_TRAIL_COLLISION = 1, - ORION_TRAIL_SPACEPORT = 2 - ) - var/list/stops = list() - var/list/stopblurbs = list() - var/traitors_aboard = 0 - var/spaceport_raided = 0 - var/spaceport_freebie = 0 - var/last_spaceport_action = "" - var/gameStatus = ORION_STATUS_START - var/canContinueEvent = 0 - -/obj/machinery/computer/arcade/orion_trail/New() - ..() - // Sets up the main trail - stops = list("Pluto","Asteroid Belt","Proxima Centauri","Dead Space","Rigel Prime","Tau Ceti Beta","Black Hole","Space Outpost Beta-9","Orion Prime") - stopblurbs = list( - "Pluto, long since occupied with long-range sensors and scanners, stands ready to, and indeed continues to probe the far reaches of the galaxy.", - "At the edge of the Sol system lies a treacherous asteroid belt. Many have been crushed by stray asteroids and misguided judgement.", - "The nearest star system to Sol, in ages past it stood as a reminder of the boundaries of sub-light travel, now a low-population sanctuary for adventurers and traders.", - "This region of space is particularly devoid of matter. Such low-density pockets are known to exist, but the vastness of it is astounding.", - "Rigel Prime, the center of the Rigel system, burns hot, basking its planetary bodies in warmth and radiation.", - "Tau Ceti Beta has recently become a waypoint for colonists headed towards Orion. There are many ships and makeshift stations in the vicinity.", - "Sensors indicate that a black hole's gravitational field is affecting the region of space we were headed through. We could stay of course, but risk of being overcome by its gravity, or we could change course to go around, which will take longer.", - "You have come into range of the first man-made structure in this region of space. It has been constructed not by travellers from Sol, but by colonists from Orion. It stands as a monument to the colonists' success.", - "You have made it to Orion! Congratulations! Your crew is one of the few to start a new foothold for mankind!" - ) - -/obj/machinery/computer/arcade/orion_trail/proc/newgame() - // Set names of settlers in crew - settlers = list() - for(var/i = 1; i <= 3; i++) - add_crewmember() - add_crewmember("[usr]") - // Re-set items to defaults - engine = 1 - hull = 1 - electronics = 1 - food = 80 - fuel = 60 - alive = 4 - turns = 1 - event = null - gameStatus = ORION_STATUS_NORMAL - traitors_aboard = 0 - - //spaceport junk - spaceport_raided = 0 - spaceport_freebie = 0 - last_spaceport_action = "" - -/obj/machinery/computer/arcade/orion_trail/attack_hand(mob/living/user) - if(..()) - return - if(fuel <= 0 || food <=0 || settlers.len == 0) - gameStatus = ORION_STATUS_GAMEOVER - event = null - user.set_machine(src) - var/dat = "" - if(gameStatus == ORION_STATUS_GAMEOVER) - playsound(src, 'sound/arcade/Ori_fail.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - dat = "

    Game Over

    " - dat += "Like many before you, your crew never made it to Orion, lost to space...
    forever." - if(settlers.len == 0) - dat += "
    Your entire crew died, and your ship joins the fleet of ghost-ships littering the galaxy." - else - if(food <= 0) - dat += "
    You ran out of food and starved." - if(emagged) - user.nutrition = 0 //yeah you pretty hongry - to_chat(user, span("danger", "Your body instantly contracts to that of one who has not eaten in months. Agonizing cramps seize you as you fall to the floor.")) - if(fuel <= 0) - dat += "
    You ran out of fuel, and drift, slowly, into a star." - if(emagged) - var/mob/living/M = user - M.adjust_fire_stacks(5) - M.IgniteMob() //flew into a star, so you're on fire - to_chat(user,span("danger", "You feel an immense wave of heat emanate from \the [src]. Your skin bursts into flames.")) - dat += "

    OK...

    " - - if(emagged) - to_chat(user, span("danger", "You're never going to make it to Orion...")) - user.death() - emagged = 0 //removes the emagged status after you lose - gameStatus = ORION_STATUS_START - name = "The Orion Trail" - desc = "Learn how our ancestors got to Orion, and have fun in the process!" - - else if(event) - dat = eventdat - else if(gameStatus == ORION_STATUS_NORMAL) - var/title = stops[turns] - var/subtext = stopblurbs[turns] - dat = "

    [title]

    " - dat += "[subtext]" - dat += "

    Crew:

    " - dat += english_list(settlers) - dat += "
    Food: [food] | Fuel: [fuel]" - dat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" - if(turns == 7) - dat += "

    Go Around Continue

    " - else - dat += "

    Continue

    " - dat += "

    Kill a crewmember

    " - dat += "

    Close

    " - else - dat = "

    The Orion Trail

    " - dat += "

    Experience the journey of your ancestors!



    " - dat += "
    New Game
    " - dat += "

    Close

    " - user << browse(dat,"window=arcade") - return - -/obj/machinery/computer/arcade/orion_trail/Topic(href, href_list) - if(..()) - return - if(href_list["close"]) - usr.unset_machine() - usr << browse(null, "window=arcade") - - if(busy) - return - busy = 1 - - if (href_list["continue"]) //Continue your travels - if(gameStatus == ORION_STATUS_NORMAL && !event && turns != 7) - if(turns >= ORION_TRAIL_WINTURN) - win() - else - food -= (alive+traitors_aboard)*2 - fuel -= 5 - if(turns == 2 && prob(30)) - event = ORION_TRAIL_COLLISION - event() - else if(prob(75)) - event = pickweight(events) - if(traitors_aboard) - if(event == ORION_TRAIL_MUTINY || prob(55)) - event = ORION_TRAIL_MUTINY_ATTACK - event() - turns += 1 - if(emagged) - var/mob/living/carbon/M = usr //for some vars - switch(event) - if(ORION_TRAIL_RAIDERS) - if(prob(50)) - to_chat(usr, span("warning", "You hear battle shouts. The tramping of boots on cold metal. Screams of agony. The rush of venting air. Are you going insane?")) - M.hallucination += 30 - else - to_chat(usr, span("danger", "Something strikes you from behind! It hurts like hell and feel like a blunt weapon, but nothing is there...")) - M.take_organ_damage(25) - if(ORION_TRAIL_ILLNESS) - var/severity = rand(1,3) //pray to RNGesus. PRAY, PIGS - if(severity == 1) - to_chat(M, span("warning", "You suddenly feel slightly nauseous.")) //got off lucky - if(severity == 2) - to_chat(usr, span("warning", "You suddenly feel extremely nauseous and hunch over until it passes.")) - M.Stun(3) - if(severity >= 3) //you didn't pray hard enough - to_chat(M, span("warning", "An overpowering wave of nausea consumes over you. You hunch over, your stomach's contents preparing for a spectacular exit.")) - spawn(30) - if(istype(M,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - H.vomit() - if(ORION_TRAIL_FLUX) - if(prob(75)) - M.Weaken(3) - src.visible_message("A sudden gust of powerful wind slams \the [M] into the floor!", "You hear a large fwooshing sound, followed by a bang.") - M.take_organ_damage(15) - else - to_chat(M, span("warning", "A violent gale blows past you, and you barely manage to stay standing!")) - if(ORION_TRAIL_COLLISION) //by far the most damaging event - if(prob(90) && !hull) - var/turf/simulated/floor/F = src.loc - F.ChangeTurf(/turf/space) - src.visible_message(span("danger", "Something slams into the floor around \the [src], exposing it to space!"), "You hear something crack and break.") - else - src.visible_message("Something slams into the floor around \the [src] - luckily, it didn't get through!", "You hear something crack.") - if(ORION_TRAIL_MALFUNCTION) - src.visible_message("\The [src] buzzes and the screen goes blank for a moment before returning to the game.") - var/oldfood = food - var/oldfuel = fuel - food = rand(10,80) / rand(1,2) - fuel = rand(10,60) / rand(1,2) - if(electronics) - sleep(10) - if(oldfuel > fuel && oldfood > food) - src.audible_message("\The [src] lets out a somehow reassuring chime.", runemessage = "reassuring chime") - else if(oldfuel < fuel || oldfood < food) - src.audible_message("\The [src] lets out a somehow ominous chime.", runemessage = "ominous chime") - food = oldfood - fuel = oldfuel - - else if(href_list["newgame"]) //Reset everything - if(gameStatus == ORION_STATUS_START) - playsound(src, 'sound/arcade/Ori_begin.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - newgame() - else if(href_list["menu"]) //back to the main menu - if(gameStatus == ORION_STATUS_GAMEOVER) - gameStatus = ORION_STATUS_START - event = null - food = 80 - fuel = 60 - settlers = list("Harry","Larry","Bob") - else if(href_list["slow"]) //slow down - if(event == ORION_TRAIL_FLUX) - food -= (alive+traitors_aboard)*2 - fuel -= 5 - event = null - else if(href_list["pastblack"]) //slow down - if(turns == 7) - food -= ((alive+traitors_aboard)*2)*3 - fuel -= 15 - turns += 1 - event = null - else if(href_list["useengine"]) //use parts - if(event == ORION_TRAIL_BREAKDOWN) - engine = max(0, --engine) - event = null - else if(href_list["useelec"]) //use parts - if(event == ORION_TRAIL_MALFUNCTION) - electronics = max(0, --electronics) - event = null - else if(href_list["usehull"]) //use parts - if(event == ORION_TRAIL_COLLISION) - hull = max(0, --hull) - event = null - else if(href_list["wait"]) //wait 3 days - if(event == ORION_TRAIL_BREAKDOWN || event == ORION_TRAIL_MALFUNCTION || event == ORION_TRAIL_COLLISION) - food -= ((alive+traitors_aboard)*2)*3 - event = null - else if(href_list["keepspeed"]) //keep speed - if(event == ORION_TRAIL_FLUX) - if(prob(75)) - event = "Breakdown" - event() - else - event = null - else if(href_list["blackhole"]) //keep speed past a black hole - if(turns == 7) - if(prob(75)) - event = ORION_TRAIL_BLACKHOLE - event() - if(emagged) //has to be here because otherwise it doesn't work - src.show_message("\The [src] states, 'YOU ARE EXPERIENCING A BLACKHOLE. BE TERRIFIED.","You hear something say, 'YOU ARE EXPERIENCING A BLACKHOLE. BE TERRFIED'") - to_chat(usr, span("warning", "Something draws you closer and closer to the machine.")) - sleep(10) - to_chat(usr, span("danger", "This is really starting to hurt!")) - var i; //spawning a literal blackhole would be fun, but a bit disruptive. - for(i=0;i<4;i++) - var/mob/living/L = usr - if(istype(L)) - L.adjustBruteLoss(25) - sleep(10) - else - event = null - turns += 1 - else if(href_list["holedeath"]) - if(event == ORION_TRAIL_BLACKHOLE) - gameStatus = ORION_STATUS_GAMEOVER - event = null - else if(href_list["eventclose"]) //end an event - if(canContinueEvent) - event = null - - else if(href_list["killcrew"]) //shoot a crewmember - if(gameStatus == ORION_STATUS_NORMAL || event == ORION_TRAIL_MUTINY) - playsound(src, 'sound/arcade/kill_crew.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - var/sheriff = remove_crewmember() //I shot the sheriff - var/mob/living/L = usr - if(!istype(L)) - return - if(settlers.len == 0 || alive == 0) - src.visible_message("\The [src] states, 'EVERYONE HAS DIED, GAMEOVER.'", "You hear something state, 'EVERYONE HAS DIED, GAMEOVER.'") - if(emagged) - src.visible_message("\The [src] produces a loud, gunlike sound.") - L.adjustBruteLoss(30) - emagged = 0 - gameStatus = ORION_STATUS_GAMEOVER - event = null - else if(emagged) - if(usr.name == sheriff) - src.visible_message("\The [src] states, 'THE CREW HAS CHOSEN TO KILL [usr]'. A gunshot can be heard coming from \the [src]", "You hear 'THE CREW HAS CHOSEN TO KILL [usr]' followed by a gunshot") - L.adjustBruteLoss(30) - if(event == ORION_TRAIL_MUTINY) //only ends the ORION_TRAIL_MUTINY event, since you can do this action in multiple places - event = null - - //Spaceport specific interactions - //they get a header because most of them don't reset event (because it's a shop, you leave when you want to) - //they also call event() again, to regen the eventdata, which is kind of odd but necessary - else if(href_list["buycrew"]) //buy a crewmember - if(gameStatus == ORION_STATUS_MARKET) - if(!spaceport_raided && food >= 10 && fuel >= 10) - playsound(src, 'sound/arcade/get_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - var/bought = add_crewmember() - last_spaceport_action = "You hired [bought] as a new crewmember." - fuel -= 10 - food -= 10 - event() - - else if(href_list["sellcrew"]) //sell a crewmember - if(gameStatus == ORION_STATUS_MARKET) - if(!spaceport_raided && settlers.len > 1) - playsound(src, 'sound/arcade/lose_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - var/sold = remove_crewmember() - last_spaceport_action = "You sold your crewmember, [sold]!" - fuel += 7 - food += 7 - event() - - else if(href_list["leave_spaceport"]) - if(gameStatus == ORION_STATUS_MARKET) - event = null - gameStatus = ORION_STATUS_NORMAL - spaceport_raided = 0 - spaceport_freebie = 0 - last_spaceport_action = "" - - else if(href_list["raid_spaceport"]) - if(gameStatus == ORION_STATUS_MARKET) - if(!spaceport_raided) - playsound(src, 'sound/arcade/raid.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - var/success = min(15 * alive,100) //default crew (4) have a 60% chance - spaceport_raided = 1 - - var/FU = 0 - var/FO = 0 - if(prob(success)) - FU = rand(5,15) - FO = rand(5,15) - last_spaceport_action = "You successfully raided the spaceport! You gained [FU] Fuel and [FO] Food! (+[FU]FU,+[FO]FO)" - else - FU = rand(-5,-15) - FO = rand(-5,-15) - last_spaceport_action = "You failed to raid the spaceport! You lost [FU*-1] Fuel and [FO*-1] Food in your scramble to escape! ([FU]FU,[FO]FO)" - - //your chance of lose a crewmember is 1/2 your chance of success - //this makes higher % failures hurt more, don't get cocky space cowboy! - if(prob(success*5)) - var/lost_crew = remove_crewmember() - last_spaceport_action = "You failed to raid the spaceport! You lost [FU*-1] Fuel and [FO*-1] Food, AND [lost_crew] in your scramble to escape! ([FU]FI,[FO]FO,-Crew)" - if(emagged) - src.visible_message("The machine states, 'YOU ARE UNDER ARREST, RAIDER!' and shoots handcuffs onto [usr]!", "You hear something say 'YOU ARE UNDER ARREST, RAIDER!' and a clinking sound") - var/obj/item/weapon/handcuffs/C = new(src.loc) - var/mob/living/carbon/human/H = usr - if(istype(H)) - C.forceMove(H) - H.handcuffed = C - H.update_handcuffed() - else - C.throw_at(usr,16,3,src) - - - fuel += FU - food += FO - event() - - else if(href_list["buyparts"]) - if(gameStatus == ORION_STATUS_MARKET) - if(!spaceport_raided && fuel > 5) - playsound(src, 'sound/arcade/get_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - switch(text2num(href_list["buyparts"])) - if(1) //Engine Parts - engine++ - last_spaceport_action = "Bought Engine Parts" - if(2) //Hull Plates - hull++ - last_spaceport_action = "Bought Hull Plates" - if(3) //Spare Electronics - electronics++ - last_spaceport_action = "Bought Spare Electronics" - fuel -= 5 //they all cost 5 - event() - - else if(href_list["trade"]) - if(gameStatus == ORION_STATUS_MARKET) - if(!spaceport_raided) - playsound(src, 'sound/arcade/get_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - switch(text2num(href_list["trade"])) - if(1) //Fuel - if(fuel > 5) - fuel -= 5 - food += 5 - last_spaceport_action = "Traded Fuel for Food" - event() - if(2) //Food - if(food > 5) - fuel += 5 - food -= 5 - last_spaceport_action = "Traded Food for Fuel" - event() - - src.add_fingerprint(usr) - src.updateUsrDialog() - busy = 0 - return - - -/obj/machinery/computer/arcade/orion_trail/proc/event() - eventdat = "

    [event]

    " - canContinueEvent = 0 - switch(event) - if(ORION_TRAIL_RAIDERS) - eventdat += "Raiders have come aboard your ship!" - if(prob(50)) - var/sfood = rand(1,10) - var/sfuel = rand(1,10) - food -= sfood - fuel -= sfuel - eventdat += "
    They have stolen [sfood] Food and [sfuel] Fuel." - else if(prob(10)) - var/deadname = remove_crewmember() - eventdat += "
    [deadname] tried to fight back, but was killed." - else - eventdat += "
    Fortunately, you fended them off without any trouble." - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - canContinueEvent = 1 - - if(ORION_TRAIL_FLUX) - playsound(src, 'sound/arcade/explo.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - eventdat += "This region of space is highly turbulent.
    If we go slowly we may avoid more damage, but if we keep our speed we won't waste supplies." - eventdat += "
    What will you do?" - eventdat += "

    Slow Down Continue

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_ILLNESS) - eventdat += "A deadly illness has been contracted!" - var/deadname = remove_crewmember() - eventdat += "
    [deadname] was killed by the disease." - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - canContinueEvent = 1 - - if(ORION_TRAIL_BREAKDOWN) - playsound(src, 'sound/arcade/explo.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - eventdat += "Oh no! The engine has broken down!" - eventdat += "
    You can repair it with an engine part, or you can make repairs for 3 days." - if(engine >= 1) - eventdat += "

    Use Part Wait

    " - else - eventdat += "

    Wait

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_MALFUNCTION) - eventdat += "The ship's systems are malfunctioning!" - eventdat += "
    You can replace the broken electronics with spares, or you can spend 3 days troubleshooting the AI." - if(electronics >= 1) - eventdat += "

    Use Part Wait

    " - else - eventdat += "

    Wait

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_COLLISION) - playsound(src, 'sound/arcade/explo.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - eventdat += "Something hit us! Looks like there's some hull damage." - if(prob(25)) - var/sfood = rand(5,15) - var/sfuel = rand(5,15) - food -= sfood - fuel -= sfuel - eventdat += "
    [sfood] Food and [sfuel] Fuel was vented out into space." - if(prob(10)) - var/deadname = remove_crewmember() - eventdat += "
    [deadname] was killed by rapid depressurization." - eventdat += "
    You can repair the damage with hull plates, or you can spend the next 3 days welding scrap together." - if(hull >= 1) - eventdat += "

    Use Part Wait

    " - else - eventdat += "

    Wait

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_BLACKHOLE) - eventdat += "You were swept away into the black hole." - eventdat += "

    Oh...

    " - eventdat += "

    Close

    " - settlers = list() - - if(ORION_TRAIL_MUTINY) - eventdat += "You've been hearing rumors of dissenting opinions amoungst your men." - if(settlers.len <= 2) - eventdat += "
    Your crew's so tiny you don't think anybody would risk an uprising." - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - if(prob(10)) - traitors_aboard = min(++traitors_aboard,2) - else - if(traitors_aboard) //less likely to stack traitors - if(prob(20)) - traitors_aboard = min(++traitors_aboard,2) - else if(prob(70)) - traitors_aboard = min(++traitors_aboard,2) - - eventdat += "

    Kill a crewmember

    " - eventdat += "

    Risk it

    " - eventdat += "

    Close

    " - canContinueEvent = 1 - - if(ORION_TRAIL_MUTINY_ATTACK) - if(traitors_aboard <= 0) //shouldn't trigger, but hey. - eventdat += "Haha, fooled you, there isn't a mutiny on board!" - eventdat += "
    (You should report this to a coder :S)" - else - var/trait1 = remove_crewmember() - var/trait2 = "" - if(traitors_aboard >= 2) - trait2 = remove_crewmember() - - eventdat += "Oh no, some of your crew are attempting to mutiny!!" - if(trait2) - eventdat += "
    [trait1] and [trait2]'s have armed themselves with weapons!" - else - eventdat += "
    [trait1]'s armed with a weapon!" - - var/chance2attack = alive*20 - if(prob(chance2attack)) - var/chancetokill = 30*traitors_aboard-(5*alive) //eg: 30*2-(10) = 50%, 2 traitorss, 2 crew is 50% chance - if(prob(chancetokill)) - var/deadguy = remove_crewmember() - eventdat += "
    The traitor[trait2 ? "s":""] run[trait2 ? "":"s"] up to [deadguy] and murder[trait2 ? "" : "s"] them!" - else - eventdat += "
    You valiantly fight off the traitor[trait2 ? "s":""]!" - eventdat += "
    You cut the traitor[trait2 ? "s":""] up into meat... Eww" - if(trait2) - food += 30 - traitors_aboard = max(0,traitors_aboard-2) - else - food += 15 - traitors_aboard = max(0,--traitors_aboard) - else - eventdat += "
    The traitor[trait2 ? "s":""] run[trait2 ? "":"s"] away, What wimps!" - if(trait2) - traitors_aboard = max(0,traitors_aboard-2) - else - traitors_aboard = max(0,--traitors_aboard) - - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - canContinueEvent = 1 - - - if(ORION_TRAIL_SPACEPORT) - gameStatus = ORION_STATUS_MARKET - if(spaceport_raided) - eventdat += "The Spaceport is on high alert! they wont let you dock since you tried to attack them!" - if(last_spaceport_action) - eventdat += "
    Last Spaceport Action: [last_spaceport_action]" - eventdat += "

    Depart Spaceport

    " - eventdat += "

    Close

    " - else - eventdat += "You pull the ship up to dock at a nearby Spaceport, lucky find!" - eventdat += "
    This Spaceport is home to travellers who failed to reach Orion, but managed to find a different home..." - eventdat += "
    Trading terms: FU = Fuel, FO = Food" - if(last_spaceport_action) - eventdat += "
    Last Spaceport Action: [last_spaceport_action]" - eventdat += "

    Crew:

    " - eventdat += english_list(settlers) - eventdat += "
    Food: [food] | Fuel: [fuel]" - eventdat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" - - - //If your crew is pathetic you can get freebies (provided you haven't already gotten one from this port) - if(!spaceport_freebie && (fuel < 20 || food < 20)) - spaceport_freebie++ - var/FU = 10 - var/FO = 10 - var/freecrew = 0 - if(prob(30)) - FU = 25 - FO = 25 - - if(prob(10)) - add_crewmember() - freecrew++ - - eventdat += "
    The traders of the spaceport take pity on you, and give you some supplies. (+[FU]FU,+[FO]FO)" - if(freecrew) - eventdat += "
    You also gain a new crewmember!" - - fuel += FU - food += FO - - //CREW INTERACTIONS - eventdat += "

    Crew Management:

    " - - //Buy crew - if(food >= 10 && fuel >= 10) - eventdat += "

    Hire a new Crewmember (-10FU,-10FO)

    " - else - eventdat += "

    You cannot afford a new Crewmember

    " - - //Sell crew - if(settlers.len > 1) - eventdat += "

    Sell crew for Fuel and Food (+7FU,+7FO)

    " - else - eventdat += "

    You cannot afford to sell a Crewmember

    " - - //BUY/SELL STUFF - eventdat += "

    Spare Parts:

    " - - //Engine parts - if(fuel > 5) - eventdat += "

    Buy Engine Parts (-5FU)

    " - else - eventdat += "

    You cannot afford to buy Engine Parts" - - //Hull plates - if(fuel > 5) - eventdat += "

    Buy Hull Plates (-5FU)

    " - else - eventdat += "

    You cannot afford to buy Hull Plates" - - //Electronics - if(fuel > 5) - eventdat += "

    Buy Spare Electronics (-5FU)

    " - else - eventdat += "

    You cannot afford to buy Spare Electronics" - - //Trade - if(fuel > 5) - eventdat += "

    Trade Fuel for Food (-5FU,+5FO)

    " - else - eventdat += "

    You cannot afford to Trade Fuel for Food 5) - eventdat += "

    Trade Food for Fuel (+5FU,-5FO)

    " - else - eventdat += "

    You cannot afford to Trade Food for Fuel\The [src] says, 'This is ship ID #[rand(1,1000)] to Orion Port Authority. We're coming in for landing, over.'") - sleep(20) - src.visible_message(span("warning", "[src] begins to vibrate...")) - src.audible_message("\The [src] says, 'Uh, Port? Having some issues with our reactor, could you check it out? Over.'") - sleep(30) - src.audible_message("\The [src] says, 'Oh, God! Code Eight! CODE EIGHT! IT'S GONNA BL-'") - sleep(3.6) - src.visible_message(span("danger", "[src] explodes!")) - explosion(src.loc, 1,2,4) - qdel(src) - -#undef ORION_TRAIL_WINTURN -#undef ORION_TRAIL_RAIDERS -#undef ORION_TRAIL_FLUX -#undef ORION_TRAIL_ILLNESS -#undef ORION_TRAIL_BREAKDOWN -#undef ORION_TRAIL_MUTINY -#undef ORION_TRAIL_MUTINY_ATTACK -#undef ORION_TRAIL_MALFUNCTION -#undef ORION_TRAIL_COLLISION -#undef ORION_TRAIL_SPACEPORT -#undef ORION_TRAIL_BLACKHOLE - -#undef ORION_STATUS_START -#undef ORION_STATUS_NORMAL -#undef ORION_STATUS_GAMEOVER -#undef ORION_STATUS_MARKET - -////////////////// -// Claw Machine // -////////////////// - -/obj/machinery/computer/arcade/clawmachine - name = "AlliCo Grab-a-Gift" - desc = "Show off your arcade skills for that special someone!" - icon_state = "clawmachine_new" - icon_keyboard = null - icon_screen = null - circuit = /obj/item/weapon/circuitboard/arcade/clawmachine - prizes = list(/obj/random/plushie) - var/wintick = 0 - var/winprob = 0 - var/instructions = "Insert 1 thaler or swipe a card to play!" - var/gameStatus = "CLAWMACHINE_NEW" - var/gamepaid = 0 - var/gameprice = 1 - var/winscreen = "" - -/// Payment -/obj/machinery/computer/arcade/clawmachine/attackby(obj/item/I as obj, mob/user as mob) - if(..()) - return - - if(gamepaid == 0 && vendor_account && !vendor_account.suspended) - var/paid = 0 - var/obj/item/weapon/card/id/W = I.GetID() - if(W) //for IDs and PDAs and wallets with IDs - paid = pay_with_card(W,I) - else if(istype(I, /obj/item/weapon/spacecash/ewallet)) - var/obj/item/weapon/spacecash/ewallet/C = I - paid = pay_with_ewallet(C) - else if(istype(I, /obj/item/weapon/spacecash)) - var/obj/item/weapon/spacecash/C = I - paid = pay_with_cash(C, user) - if(paid) - gamepaid = 1 - instructions = "Hit start to play!" - return - return - -////// Cash -/obj/machinery/computer/arcade/clawmachine/proc/pay_with_cash(var/obj/item/weapon/spacecash/cashmoney, mob/user) - if(!emagged) - if(gameprice > cashmoney.worth) - - // This is not a status display message, since it's something the character - // themselves is meant to see BEFORE putting the money in - to_chat(usr, "\icon[cashmoney][bicon(cashmoney)] That is not enough money.") - return 0 - - if(istype(cashmoney, /obj/item/weapon/spacecash)) - - visible_message("\The [usr] inserts some cash into \the [src].") - cashmoney.worth -= gameprice - - if(cashmoney.worth <= 0) - usr.drop_from_inventory(cashmoney) - qdel(cashmoney) - else - cashmoney.update_icon() - - // Machine has no idea who paid with cash - credit_purchase("(cash)") - return 1 - if(emagged) - playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - to_chat(user, "It doesn't seem to accept that! Seem you'll need to swipe a valid ID.") - - -///// Ewallet -/obj/machinery/computer/arcade/clawmachine/proc/pay_with_ewallet(var/obj/item/weapon/spacecash/ewallet/wallet) - if(!emagged) - visible_message("\The [usr] swipes \the [wallet] through \the [src].") - playsound(src, 'sound/machines/id_swipe.ogg', 50, 1) - if(gameprice > wallet.worth) - visible_message("Insufficient funds.") - return 0 - else - wallet.worth -= gameprice - credit_purchase("[wallet.owner_name] (chargecard)") - return 1 - if(emagged) - playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - to_chat(usr, "It doesn't seem to accept that! Seem you'll need to swipe a valid ID.") - -///// ID -/obj/machinery/computer/arcade/clawmachine/proc/pay_with_card(var/obj/item/weapon/card/id/I, var/obj/item/ID_container) - if(I==ID_container || ID_container == null) - visible_message("\The [usr] swipes \the [I] through \the [src].") - else - visible_message("\The [usr] swipes \the [ID_container] through \the [src].") - playsound(src, 'sound/machines/id_swipe.ogg', 50, 1) - var/datum/money_account/customer_account = get_account(I.associated_account_number) - if(!customer_account) - visible_message("Error: Unable to access account. Please contact technical support if problem persists.") - return 0 - - if(customer_account.suspended) - visible_message("Unable to access account: account suspended.") - return 0 - - // Have the customer punch in the PIN before checking if there's enough money. Prevents people from figuring out acct is - // empty at high security levels - if(customer_account.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2) - var/attempt_pin = tgui_input_number(usr, "Enter pin code", "Vendor transaction") - customer_account = attempt_account_access(I.associated_account_number, attempt_pin, 2) - - if(!customer_account) - visible_message("Unable to access account: incorrect credentials.") - return 0 - - if(gameprice > customer_account.money) - visible_message("Insufficient funds in account.") - return 0 - else - // Okay to move the money at this point - if(emagged) - gameprice = customer_account.money - // debit money from the purchaser's account - customer_account.money -= gameprice - - // create entry in the purchaser's account log - var/datum/transaction/T = new() - T.target_name = "[vendor_account.owner_name] (via [name])" - T.purpose = "Purchase of arcade game([name])" - if(gameprice > 0) - T.amount = "([gameprice])" - else - T.amount = "[gameprice]" - T.source_terminal = name - T.date = current_date_string - T.time = stationtime2text() - customer_account.transaction_log.Add(T) - - // Give the vendor the money. We use the account owner name, which means - // that purchases made with stolen/borrowed card will look like the card - // owner made them - credit_purchase(customer_account.owner_name) - return 1 - -/// Add to vendor account - -/obj/machinery/computer/arcade/clawmachine/proc/credit_purchase(var/target as text) - vendor_account.money += gameprice - - var/datum/transaction/T = new() - T.target_name = target - T.purpose = "Purchase of arcade game([name])" - T.amount = "[gameprice]" - T.source_terminal = name - T.date = current_date_string - T.time = stationtime2text() - vendor_account.transaction_log.Add(T) - -/// End Payment - -/obj/machinery/computer/arcade/clawmachine/New() - ..() - -/obj/machinery/computer/arcade/clawmachine/attack_hand(mob/living/user) - if(..()) - return - tgui_interact(user) - -/// TGUI Stuff - -/obj/machinery/computer/arcade/clawmachine/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "ClawMachine", name, ui_x = 300, ui_y = 400) - ui.autoupdate = TRUE - ui.open() - -/obj/machinery/computer/arcade/clawmachine/tgui_data(mob/user) - var/list/data = list() - - data["wintick"] = wintick - data["instructions"] = instructions - data["gameStatus"] = gameStatus - data["winscreen"] = winscreen - - return data - -/obj/machinery/computer/arcade/clawmachine/tgui_act(action, params) - if(..()) - return - - if(action == "newgame" && gamepaid == 0) - playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - - if(action == "newgame" && gamepaid == 1) - gameStatus = "CLAWMACHINE_ON" - icon_state = "clawmachine_new_move" - instructions = "Guide the claw to the prize you want!" - wintick = 0 - - if(action == "return" && gameStatus == "CLAWMACHINE_END") - gameStatus = "CLAWMACHINE_NEW" - - if(action == "pointless" && wintick < 10) - wintick += 1 - - if(action == "pointless" && wintick >= 10) - instructions = "Insert 1 thaler or swipe a card to play!" - clawvend() - -/obj/machinery/computer/arcade/clawmachine/proc/clawvend() /// True to a real claw machine, it's NEARLY impossible to win. - winprob += 1 /// Yeah. - - if(prob(winprob)) /// YEAH. - if(!emagged) - prizevend() - winscreen = "You won!" - else if(emagged) - gameprice = 1 - emagged = 0 - winscreen = "You won...?" - var/obj/item/weapon/grenade/G = new /obj/item/weapon/grenade/explosive(get_turf(src)) /// YEAAAAAAAAAAAAAAAAAAH!!!!!!!!!! - G.activate() - G.throw_at(get_turf(usr),10,10) /// Play stupid games, win stupid prizes. - - playsound(src, 'sound/arcade/Ori_win.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - winprob = 0 - - else - playsound(src, 'sound/arcade/Ori_fail.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - winscreen = "Aw, shucks. Try again!" - wintick = 0 - gamepaid = 0 - icon_state = "clawmachine_new" - gameStatus = "CLAWMACHINE_END" - -/obj/machinery/computer/arcade/clawmachine/emag_act(mob/user) - if(!emagged) - to_chat(user, "You modify the claw of the machine. The next one is sure to win! You just have to pay...") - name = "AlliCo Snag-A-Prize" - desc = "Get some goodies, all for you!" - instructions = "Swipe a card to play!" - winprob = 100 - gamepaid = 0 - wintick = 0 - gameStatus = "CLAWMACHINE_NEW" - emagged = 1 - return 1 - -/obj/machinery/computer/arcade/attackby(obj/item/O, mob/user, params) - ..() - if(istype(O, /obj/item/stack/arcadeticket)) - var/obj/item/stack/arcadeticket/T = O - var/amount = T.get_amount() - if(amount <2) - to_chat(user, "You need 2 tickets to claim a prize!") - return - prizevend(user) - T.pay_tickets() - T.update_icon() - O = T - to_chat(user, "You turn in 2 tickets to the [src] and claim a prize!") - return - else +/obj/machinery/computer/arcade + name = "random arcade" + desc = "random arcade machine" + icon_state = "arcade1" + icon_keyboard = null + clicksound = null //Gets too spammy and makes no sense for arcade to have the console keyboard noise anyway + var/list/prizes = list( /obj/item/weapon/storage/box/snappops = 2, + /obj/item/toy/blink = 2, + /obj/item/clothing/under/syndicate/tacticool = 2, + /obj/item/toy/sword = 2, + /obj/item/weapon/storage/box/capguntoy = 2, + /obj/item/weapon/gun/projectile/revolver/toy/crossbow = 2, + /obj/item/clothing/suit/syndicatefake = 2, + /obj/item/weapon/storage/fancy/crayons = 2, + /obj/item/toy/spinningtoy = 2, + /obj/random/mech_toy = 1, + /obj/item/weapon/reagent_containers/spray/waterflower = 1, + /obj/random/action_figure = 1, + /obj/random/plushie = 1, + /obj/item/toy/cultsword = 1, + /obj/item/toy/bouquet/fake = 1, + /obj/item/clothing/accessory/badge/sheriff = 2, + /obj/item/clothing/head/cowboy/small = 2, + /obj/item/toy/stickhorse = 2 + ) + var/list/special_prizes = list() // Holds instanced objects, intended for admins to shove surprises inside or something. + +/obj/machinery/computer/arcade/Initialize() + . = ..() + // If it's a generic arcade machine, pick a random arcade + // circuit board for it and make the new machine + if(!circuit) + var/choice = pick(subtypesof(/obj/item/weapon/circuitboard/arcade) - /obj/item/weapon/circuitboard/arcade/clawmachine) + var/obj/item/weapon/circuitboard/CB = new choice() + new CB.build_path(loc, CB) + return INITIALIZE_HINT_QDEL + +/obj/machinery/computer/arcade/proc/prizevend() + if(LAZYLEN(special_prizes)) // Downstream wanted the 'win things inside contents sans circuitboard' feature kept. + var/atom/movable/AM = pick_n_take(special_prizes) + AM.forceMove(get_turf(src)) + special_prizes -= AM + + else if(LAZYLEN(prizes)) + var/prizeselect = pickweight(prizes) + new prizeselect(src.loc) + + if(istype(prizeselect, /obj/item/clothing/suit/syndicatefake)) //Helmet is part of the suit + new /obj/item/clothing/head/syndicatefake(src.loc) + +/obj/machinery/computer/arcade/attack_ai(mob/user as mob) + return attack_hand(user) + + +/obj/machinery/computer/arcade/emp_act(severity) + if(stat & (NOPOWER|BROKEN)) + ..(severity) + return + var/empprize = null + var/num_of_prizes = 0 + switch(severity) + if(1) + num_of_prizes = rand(1,4) + if(2) + num_of_prizes = rand(1,3) + if(3) + num_of_prizes = rand(0,2) + if(4) + num_of_prizes = rand(0,1) + for(num_of_prizes; num_of_prizes > 0; num_of_prizes--) + empprize = pickweight(prizes) + new empprize(src.loc) + + ..(severity) + +/////////////////// +// BATTLE HERE // +/////////////////// + +/obj/machinery/computer/arcade/battle + name = "Battler" + desc = "Fight through what space has to offer!" + icon_state = "arcade2" + icon_screen = "battler" + circuit = /obj/item/weapon/circuitboard/arcade/battle + var/enemy_name = "Space Villian" + var/temp = "Winners don't use space drugs" //Temporary message, for attack messages, etc + var/enemy_action = "" + var/player_hp = 30 //Player health/attack points + var/player_mp = 10 + var/enemy_hp = 45 //Enemy health/attack points + var/enemy_mp = 20 + var/gameover = 0 + var/blocked = 0 //Player cannot attack/heal while set + var/turtle = 0 + +/obj/machinery/computer/arcade/battle/Initialize() + . = ..() + randomize_characters() + +/obj/machinery/computer/arcade/battle/proc/randomize_characters() + var/name_action + var/name_part1 + var/name_part2 + + name_action = pick("Defeat ", "Annihilate ", "Save ", "Strike ", "Stop ", "Destroy ", "Robust ", "Romance ", "Pwn ", "Own ", "Ban ") + + name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ") + name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "slime", "Griefer", "ERPer", "Lizard Man", "Unicorn", "Bloopers") + + enemy_name = replacetext((name_part1 + name_part2), "the ", "") + name = (name_action + name_part1 + name_part2) + + +/obj/machinery/computer/arcade/battle/attack_hand(mob/user as mob) + if(..()) + return + user.set_machine(src) + tgui_interact(user) + +/obj/machinery/computer/arcade/battle/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ArcadeBattle", name) + ui.open() + +/obj/machinery/computer/arcade/battle/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + data["name"] = name + data["temp"] = temp + data["enemyAction"] = enemy_action + data["enemyName"] = enemy_name + data["playerHP"] = player_hp + data["playerMP"] = player_mp + data["enemyHP"] = enemy_hp + data["gameOver"] = gameover + return data + +/obj/machinery/computer/arcade/battle/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + if(!blocked && !gameover) + switch(action) + if("attack") + blocked = 1 + var/attackamt = rand(2,6) + temp = "You attack for [attackamt] damage!" + playsound(src, 'sound/arcade/hit.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + if(turtle > 0) + turtle-- + + sleep(10) + enemy_hp -= attackamt + arcade_action() + + if("heal") + blocked = 1 + var/pointamt = rand(1,3) + var/healamt = rand(6,8) + temp = "You use [pointamt] magic to heal for [healamt] damage!" + playsound(src, 'sound/arcade/heal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + turtle++ + + sleep(10) + player_mp -= pointamt + player_hp += healamt + blocked = 1 + arcade_action() + + if("charge") + blocked = 1 + var/chargeamt = rand(4,7) + temp = "You regain [chargeamt] points" + playsound(src, 'sound/arcade/mana.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + player_mp += chargeamt + if(turtle > 0) + turtle-- + + sleep(10) + arcade_action() + + + if(action == "newgame") //Reset everything + temp = "New Round" + player_hp = 30 + player_mp = 10 + enemy_hp = 45 + enemy_mp = 20 + gameover = 0 + turtle = 0 + + if(emagged) + randomize_characters() + emagged = 0 + + add_fingerprint(usr) + return TRUE + +/obj/machinery/computer/arcade/battle/proc/arcade_action() + if ((enemy_mp <= 0) || (enemy_hp <= 0)) + if(!gameover) + gameover = 1 + temp = "[enemy_name] has fallen! Rejoice!" + playsound(src, 'sound/arcade/win.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + + if(emagged) + feedback_inc("arcade_win_emagged") + new /obj/effect/spawner/newbomb/timer/syndicate(src.loc) + new /obj/item/clothing/head/collectable/petehat(src.loc) + message_admins("[key_name_admin(usr)] has outbombed Cuban Pete and been awarded a bomb.") + log_game("[key_name_admin(usr)] has outbombed Cuban Pete and been awarded a bomb.") + randomize_characters() + emagged = 0 + else if(!contents.len) + feedback_inc("arcade_win_normal") + prizevend() + + else + feedback_inc("arcade_win_normal") + prizevend() + + else if (emagged && (turtle >= 4)) + var/boomamt = rand(5,10) + enemy_action = "[enemy_name] throws a bomb, exploding you for [boomamt] damage!" + playsound(src, 'sound/arcade/boom.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + player_hp -= boomamt + + else if ((enemy_mp <= 5) && (prob(70))) + var/stealamt = rand(2,3) + enemy_action = "[enemy_name] steals [stealamt] of your power!" + playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + player_mp -= stealamt + + if (player_mp <= 0) + gameover = 1 + sleep(10) + temp = "You have been drained! GAME OVER" + if(emagged) + feedback_inc("arcade_loss_mana_emagged") + usr.gib() + else + feedback_inc("arcade_loss_mana_normal") + + else if ((enemy_hp <= 10) && (enemy_mp > 4)) + enemy_action = "[enemy_name] heals for 4 health!" + playsound(src, 'sound/arcade/heal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + enemy_hp += 4 + enemy_mp -= 4 + + else + var/attackamt = rand(3,6) + enemy_action = "[enemy_name] attacks for [attackamt] damage!" + playsound(src, 'sound/arcade/hit.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + player_hp -= attackamt + + if ((player_mp <= 0) || (player_hp <= 0)) + gameover = 1 + temp = "You have been crushed! GAME OVER" + playsound(src, 'sound/arcade/lose.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + if(emagged) + feedback_inc("arcade_loss_hp_emagged") + usr.gib() + else + feedback_inc("arcade_loss_hp_normal") + + blocked = 0 + return + + +/obj/machinery/computer/arcade/battle/emag_act(var/charges, var/mob/user) + if(!emagged) + to_chat(user, span("notice","You override the cheat code menu and skip to Cheat #[rand(1, 50)]: Hyper-Lethal Mode.")) + + temp = "If you die in the game, you die for real!" + player_hp = 30 + player_mp = 10 + enemy_hp = 45 + enemy_mp = 20 + gameover = 0 + blocked = 0 + emagged = 1 + + enemy_name = "Cuban Pete" + name = "Outbomb Cuban Pete" + + return 1 + + +////////////////////////// +// ORION TRAIL HERE // +////////////////////////// +#define ORION_TRAIL_WINTURN 9 + +//Orion Trail Events +#define ORION_TRAIL_RAIDERS "Raiders" +#define ORION_TRAIL_FLUX "Interstellar Flux" +#define ORION_TRAIL_ILLNESS "Illness" +#define ORION_TRAIL_BREAKDOWN "Breakdown" +#define ORION_TRAIL_MUTINY "Mutiny?" +#define ORION_TRAIL_MUTINY_ATTACK "Mutinous Ambush" +#define ORION_TRAIL_MALFUNCTION "Malfunction" +#define ORION_TRAIL_COLLISION "Collision" +#define ORION_TRAIL_SPACEPORT "Spaceport" +#define ORION_TRAIL_BLACKHOLE "BlackHole" + +#define ORION_STATUS_START 1 +#define ORION_STATUS_NORMAL 2 +#define ORION_STATUS_GAMEOVER 3 +#define ORION_STATUS_MARKET 4 + +/obj/machinery/computer/arcade/orion_trail + name = "The Orion Trail" + desc = "Learn how our ancestors got to Orion, and have fun in the process!" + icon_state = "arcade1" + icon_screen = "orion" + circuit = /obj/item/weapon/circuitboard/arcade/orion_trail + var/busy = 0 //prevent clickspam that allowed people to ~speedrun~ the game. + var/engine = 0 + var/hull = 0 + var/electronics = 0 + var/food = 80 + var/fuel = 60 + var/turns = 4 + var/alive = 4 + var/eventdat = null + var/event = null + var/list/settlers = list("Harry","Larry","Bob") + var/list/events = list(ORION_TRAIL_RAIDERS = 3, + ORION_TRAIL_FLUX = 1, + ORION_TRAIL_ILLNESS = 3, + ORION_TRAIL_BREAKDOWN = 2, + ORION_TRAIL_MUTINY = 3, + ORION_TRAIL_MALFUNCTION = 2, + ORION_TRAIL_COLLISION = 1, + ORION_TRAIL_SPACEPORT = 2 + ) + var/list/stops = list() + var/list/stopblurbs = list() + var/traitors_aboard = 0 + var/spaceport_raided = 0 + var/spaceport_freebie = 0 + var/last_spaceport_action = "" + var/gameStatus = ORION_STATUS_START + var/canContinueEvent = 0 + +/obj/machinery/computer/arcade/orion_trail/New() + ..() + // Sets up the main trail + stops = list("Pluto","Asteroid Belt","Proxima Centauri","Dead Space","Rigel Prime","Tau Ceti Beta","Black Hole","Space Outpost Beta-9","Orion Prime") + stopblurbs = list( + "Pluto, long since occupied with long-range sensors and scanners, stands ready to, and indeed continues to probe the far reaches of the galaxy.", + "At the edge of the Sol system lies a treacherous asteroid belt. Many have been crushed by stray asteroids and misguided judgement.", + "The nearest star system to Sol, in ages past it stood as a reminder of the boundaries of sub-light travel, now a low-population sanctuary for adventurers and traders.", + "This region of space is particularly devoid of matter. Such low-density pockets are known to exist, but the vastness of it is astounding.", + "Rigel Prime, the center of the Rigel system, burns hot, basking its planetary bodies in warmth and radiation.", + "Tau Ceti Beta has recently become a waypoint for colonists headed towards Orion. There are many ships and makeshift stations in the vicinity.", + "Sensors indicate that a black hole's gravitational field is affecting the region of space we were headed through. We could stay of course, but risk of being overcome by its gravity, or we could change course to go around, which will take longer.", + "You have come into range of the first man-made structure in this region of space. It has been constructed not by travellers from Sol, but by colonists from Orion. It stands as a monument to the colonists' success.", + "You have made it to Orion! Congratulations! Your crew is one of the few to start a new foothold for mankind!" + ) + +/obj/machinery/computer/arcade/orion_trail/proc/newgame() + // Set names of settlers in crew + settlers = list() + for(var/i = 1; i <= 3; i++) + add_crewmember() + add_crewmember("[usr]") + // Re-set items to defaults + engine = 1 + hull = 1 + electronics = 1 + food = 80 + fuel = 60 + alive = 4 + turns = 1 + event = null + gameStatus = ORION_STATUS_NORMAL + traitors_aboard = 0 + + //spaceport junk + spaceport_raided = 0 + spaceport_freebie = 0 + last_spaceport_action = "" + +/obj/machinery/computer/arcade/orion_trail/attack_hand(mob/living/user) + if(..()) + return + if(fuel <= 0 || food <=0 || settlers.len == 0) + gameStatus = ORION_STATUS_GAMEOVER + event = null + user.set_machine(src) + var/dat = "" + if(gameStatus == ORION_STATUS_GAMEOVER) + playsound(src, 'sound/arcade/Ori_fail.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + dat = "

    Game Over

    " + dat += "Like many before you, your crew never made it to Orion, lost to space...
    forever." + if(settlers.len == 0) + dat += "
    Your entire crew died, and your ship joins the fleet of ghost-ships littering the galaxy." + else + if(food <= 0) + dat += "
    You ran out of food and starved." + if(emagged) + user.nutrition = 0 //yeah you pretty hongry + to_chat(user, span("danger", "Your body instantly contracts to that of one who has not eaten in months. Agonizing cramps seize you as you fall to the floor.")) + if(fuel <= 0) + dat += "
    You ran out of fuel, and drift, slowly, into a star." + if(emagged) + var/mob/living/M = user + M.adjust_fire_stacks(5) + M.IgniteMob() //flew into a star, so you're on fire + to_chat(user,span("danger", "You feel an immense wave of heat emanate from \the [src]. Your skin bursts into flames.")) + dat += "

    OK...

    " + + if(emagged) + to_chat(user, span("danger", "You're never going to make it to Orion...")) + user.death() + emagged = 0 //removes the emagged status after you lose + gameStatus = ORION_STATUS_START + name = "The Orion Trail" + desc = "Learn how our ancestors got to Orion, and have fun in the process!" + + else if(event) + dat = eventdat + else if(gameStatus == ORION_STATUS_NORMAL) + var/title = stops[turns] + var/subtext = stopblurbs[turns] + dat = "

    [title]

    " + dat += "[subtext]" + dat += "

    Crew:

    " + dat += english_list(settlers) + dat += "
    Food: [food] | Fuel: [fuel]" + dat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" + if(turns == 7) + dat += "

    Go Around Continue

    " + else + dat += "

    Continue

    " + dat += "

    Kill a crewmember

    " + dat += "

    Close

    " + else + dat = "

    The Orion Trail

    " + dat += "

    Experience the journey of your ancestors!



    " + dat += "
    New Game
    " + dat += "

    Close

    " + user << browse(dat,"window=arcade") + return + +/obj/machinery/computer/arcade/orion_trail/Topic(href, href_list) + if(..()) + return + if(href_list["close"]) + usr.unset_machine() + usr << browse(null, "window=arcade") + + if(busy) + return + busy = 1 + + if (href_list["continue"]) //Continue your travels + if(gameStatus == ORION_STATUS_NORMAL && !event && turns != 7) + if(turns >= ORION_TRAIL_WINTURN) + win() + else + food -= (alive+traitors_aboard)*2 + fuel -= 5 + if(turns == 2 && prob(30)) + event = ORION_TRAIL_COLLISION + event() + else if(prob(75)) + event = pickweight(events) + if(traitors_aboard) + if(event == ORION_TRAIL_MUTINY || prob(55)) + event = ORION_TRAIL_MUTINY_ATTACK + event() + turns += 1 + if(emagged) + var/mob/living/carbon/M = usr //for some vars + switch(event) + if(ORION_TRAIL_RAIDERS) + if(prob(50)) + to_chat(usr, span("warning", "You hear battle shouts. The tramping of boots on cold metal. Screams of agony. The rush of venting air. Are you going insane?")) + M.hallucination += 30 + else + to_chat(usr, span("danger", "Something strikes you from behind! It hurts like hell and feel like a blunt weapon, but nothing is there...")) + M.take_organ_damage(25) + if(ORION_TRAIL_ILLNESS) + var/severity = rand(1,3) //pray to RNGesus. PRAY, PIGS + if(severity == 1) + to_chat(M, span("warning", "You suddenly feel slightly nauseous.")) //got off lucky + if(severity == 2) + to_chat(usr, span("warning", "You suddenly feel extremely nauseous and hunch over until it passes.")) + M.Stun(3) + if(severity >= 3) //you didn't pray hard enough + to_chat(M, span("warning", "An overpowering wave of nausea consumes over you. You hunch over, your stomach's contents preparing for a spectacular exit.")) + spawn(30) + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + H.vomit() + if(ORION_TRAIL_FLUX) + if(prob(75)) + M.Weaken(3) + src.visible_message("A sudden gust of powerful wind slams \the [M] into the floor!", "You hear a large fwooshing sound, followed by a bang.") + M.take_organ_damage(15) + else + to_chat(M, span("warning", "A violent gale blows past you, and you barely manage to stay standing!")) + if(ORION_TRAIL_COLLISION) //by far the most damaging event + if(prob(90) && !hull) + var/turf/simulated/floor/F = src.loc + F.ChangeTurf(/turf/space) + src.visible_message(span("danger", "Something slams into the floor around \the [src], exposing it to space!"), "You hear something crack and break.") + else + src.visible_message("Something slams into the floor around \the [src] - luckily, it didn't get through!", "You hear something crack.") + if(ORION_TRAIL_MALFUNCTION) + src.visible_message("\The [src] buzzes and the screen goes blank for a moment before returning to the game.") + var/oldfood = food + var/oldfuel = fuel + food = rand(10,80) / rand(1,2) + fuel = rand(10,60) / rand(1,2) + if(electronics) + sleep(10) + if(oldfuel > fuel && oldfood > food) + src.audible_message("\The [src] lets out a somehow reassuring chime.", runemessage = "reassuring chime") + else if(oldfuel < fuel || oldfood < food) + src.audible_message("\The [src] lets out a somehow ominous chime.", runemessage = "ominous chime") + food = oldfood + fuel = oldfuel + + else if(href_list["newgame"]) //Reset everything + if(gameStatus == ORION_STATUS_START) + playsound(src, 'sound/arcade/Ori_begin.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + newgame() + else if(href_list["menu"]) //back to the main menu + if(gameStatus == ORION_STATUS_GAMEOVER) + gameStatus = ORION_STATUS_START + event = null + food = 80 + fuel = 60 + settlers = list("Harry","Larry","Bob") + else if(href_list["slow"]) //slow down + if(event == ORION_TRAIL_FLUX) + food -= (alive+traitors_aboard)*2 + fuel -= 5 + event = null + else if(href_list["pastblack"]) //slow down + if(turns == 7) + food -= ((alive+traitors_aboard)*2)*3 + fuel -= 15 + turns += 1 + event = null + else if(href_list["useengine"]) //use parts + if(event == ORION_TRAIL_BREAKDOWN) + engine = max(0, --engine) + event = null + else if(href_list["useelec"]) //use parts + if(event == ORION_TRAIL_MALFUNCTION) + electronics = max(0, --electronics) + event = null + else if(href_list["usehull"]) //use parts + if(event == ORION_TRAIL_COLLISION) + hull = max(0, --hull) + event = null + else if(href_list["wait"]) //wait 3 days + if(event == ORION_TRAIL_BREAKDOWN || event == ORION_TRAIL_MALFUNCTION || event == ORION_TRAIL_COLLISION) + food -= ((alive+traitors_aboard)*2)*3 + event = null + else if(href_list["keepspeed"]) //keep speed + if(event == ORION_TRAIL_FLUX) + if(prob(75)) + event = "Breakdown" + event() + else + event = null + else if(href_list["blackhole"]) //keep speed past a black hole + if(turns == 7) + if(prob(75)) + event = ORION_TRAIL_BLACKHOLE + event() + if(emagged) //has to be here because otherwise it doesn't work + src.show_message("\The [src] states, 'YOU ARE EXPERIENCING A BLACKHOLE. BE TERRIFIED.","You hear something say, 'YOU ARE EXPERIENCING A BLACKHOLE. BE TERRFIED'") + to_chat(usr, span("warning", "Something draws you closer and closer to the machine.")) + sleep(10) + to_chat(usr, span("danger", "This is really starting to hurt!")) + var i; //spawning a literal blackhole would be fun, but a bit disruptive. + for(i=0;i<4;i++) + var/mob/living/L = usr + if(istype(L)) + L.adjustBruteLoss(25) + sleep(10) + else + event = null + turns += 1 + else if(href_list["holedeath"]) + if(event == ORION_TRAIL_BLACKHOLE) + gameStatus = ORION_STATUS_GAMEOVER + event = null + else if(href_list["eventclose"]) //end an event + if(canContinueEvent) + event = null + + else if(href_list["killcrew"]) //shoot a crewmember + if(gameStatus == ORION_STATUS_NORMAL || event == ORION_TRAIL_MUTINY) + playsound(src, 'sound/arcade/kill_crew.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + var/sheriff = remove_crewmember() //I shot the sheriff + var/mob/living/L = usr + if(!istype(L)) + return + if(settlers.len == 0 || alive == 0) + src.visible_message("\The [src] states, 'EVERYONE HAS DIED, GAMEOVER.'", "You hear something state, 'EVERYONE HAS DIED, GAMEOVER.'") + if(emagged) + src.visible_message("\The [src] produces a loud, gunlike sound.") + L.adjustBruteLoss(30) + emagged = 0 + gameStatus = ORION_STATUS_GAMEOVER + event = null + else if(emagged) + if(usr.name == sheriff) + src.visible_message("\The [src] states, 'THE CREW HAS CHOSEN TO KILL [usr]'. A gunshot can be heard coming from \the [src]", "You hear 'THE CREW HAS CHOSEN TO KILL [usr]' followed by a gunshot") + L.adjustBruteLoss(30) + if(event == ORION_TRAIL_MUTINY) //only ends the ORION_TRAIL_MUTINY event, since you can do this action in multiple places + event = null + + //Spaceport specific interactions + //they get a header because most of them don't reset event (because it's a shop, you leave when you want to) + //they also call event() again, to regen the eventdata, which is kind of odd but necessary + else if(href_list["buycrew"]) //buy a crewmember + if(gameStatus == ORION_STATUS_MARKET) + if(!spaceport_raided && food >= 10 && fuel >= 10) + playsound(src, 'sound/arcade/get_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + var/bought = add_crewmember() + last_spaceport_action = "You hired [bought] as a new crewmember." + fuel -= 10 + food -= 10 + event() + + else if(href_list["sellcrew"]) //sell a crewmember + if(gameStatus == ORION_STATUS_MARKET) + if(!spaceport_raided && settlers.len > 1) + playsound(src, 'sound/arcade/lose_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + var/sold = remove_crewmember() + last_spaceport_action = "You sold your crewmember, [sold]!" + fuel += 7 + food += 7 + event() + + else if(href_list["leave_spaceport"]) + if(gameStatus == ORION_STATUS_MARKET) + event = null + gameStatus = ORION_STATUS_NORMAL + spaceport_raided = 0 + spaceport_freebie = 0 + last_spaceport_action = "" + + else if(href_list["raid_spaceport"]) + if(gameStatus == ORION_STATUS_MARKET) + if(!spaceport_raided) + playsound(src, 'sound/arcade/raid.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + var/success = min(15 * alive,100) //default crew (4) have a 60% chance + spaceport_raided = 1 + + var/FU = 0 + var/FO = 0 + if(prob(success)) + FU = rand(5,15) + FO = rand(5,15) + last_spaceport_action = "You successfully raided the spaceport! You gained [FU] Fuel and [FO] Food! (+[FU]FU,+[FO]FO)" + else + FU = rand(-5,-15) + FO = rand(-5,-15) + last_spaceport_action = "You failed to raid the spaceport! You lost [FU*-1] Fuel and [FO*-1] Food in your scramble to escape! ([FU]FU,[FO]FO)" + + //your chance of lose a crewmember is 1/2 your chance of success + //this makes higher % failures hurt more, don't get cocky space cowboy! + if(prob(success*5)) + var/lost_crew = remove_crewmember() + last_spaceport_action = "You failed to raid the spaceport! You lost [FU*-1] Fuel and [FO*-1] Food, AND [lost_crew] in your scramble to escape! ([FU]FI,[FO]FO,-Crew)" + if(emagged) + src.visible_message("The machine states, 'YOU ARE UNDER ARREST, RAIDER!' and shoots handcuffs onto [usr]!", "You hear something say 'YOU ARE UNDER ARREST, RAIDER!' and a clinking sound") + var/obj/item/weapon/handcuffs/C = new(src.loc) + var/mob/living/carbon/human/H = usr + if(istype(H)) + C.forceMove(H) + H.handcuffed = C + H.update_handcuffed() + else + C.throw_at(usr,16,3,src) + + + fuel += FU + food += FO + event() + + else if(href_list["buyparts"]) + if(gameStatus == ORION_STATUS_MARKET) + if(!spaceport_raided && fuel > 5) + playsound(src, 'sound/arcade/get_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + switch(text2num(href_list["buyparts"])) + if(1) //Engine Parts + engine++ + last_spaceport_action = "Bought Engine Parts" + if(2) //Hull Plates + hull++ + last_spaceport_action = "Bought Hull Plates" + if(3) //Spare Electronics + electronics++ + last_spaceport_action = "Bought Spare Electronics" + fuel -= 5 //they all cost 5 + event() + + else if(href_list["trade"]) + if(gameStatus == ORION_STATUS_MARKET) + if(!spaceport_raided) + playsound(src, 'sound/arcade/get_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + switch(text2num(href_list["trade"])) + if(1) //Fuel + if(fuel > 5) + fuel -= 5 + food += 5 + last_spaceport_action = "Traded Fuel for Food" + event() + if(2) //Food + if(food > 5) + fuel += 5 + food -= 5 + last_spaceport_action = "Traded Food for Fuel" + event() + + src.add_fingerprint(usr) + src.updateUsrDialog() + busy = 0 + return + + +/obj/machinery/computer/arcade/orion_trail/proc/event() + eventdat = "

    [event]

    " + canContinueEvent = 0 + switch(event) + if(ORION_TRAIL_RAIDERS) + eventdat += "Raiders have come aboard your ship!" + if(prob(50)) + var/sfood = rand(1,10) + var/sfuel = rand(1,10) + food -= sfood + fuel -= sfuel + eventdat += "
    They have stolen [sfood] Food and [sfuel] Fuel." + else if(prob(10)) + var/deadname = remove_crewmember() + eventdat += "
    [deadname] tried to fight back, but was killed." + else + eventdat += "
    Fortunately, you fended them off without any trouble." + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + canContinueEvent = 1 + + if(ORION_TRAIL_FLUX) + playsound(src, 'sound/arcade/explo.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + eventdat += "This region of space is highly turbulent.
    If we go slowly we may avoid more damage, but if we keep our speed we won't waste supplies." + eventdat += "
    What will you do?" + eventdat += "

    Slow Down Continue

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_ILLNESS) + eventdat += "A deadly illness has been contracted!" + var/deadname = remove_crewmember() + eventdat += "
    [deadname] was killed by the disease." + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + canContinueEvent = 1 + + if(ORION_TRAIL_BREAKDOWN) + playsound(src, 'sound/arcade/explo.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + eventdat += "Oh no! The engine has broken down!" + eventdat += "
    You can repair it with an engine part, or you can make repairs for 3 days." + if(engine >= 1) + eventdat += "

    Use Part Wait

    " + else + eventdat += "

    Wait

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_MALFUNCTION) + eventdat += "The ship's systems are malfunctioning!" + eventdat += "
    You can replace the broken electronics with spares, or you can spend 3 days troubleshooting the AI." + if(electronics >= 1) + eventdat += "

    Use Part Wait

    " + else + eventdat += "

    Wait

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_COLLISION) + playsound(src, 'sound/arcade/explo.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + eventdat += "Something hit us! Looks like there's some hull damage." + if(prob(25)) + var/sfood = rand(5,15) + var/sfuel = rand(5,15) + food -= sfood + fuel -= sfuel + eventdat += "
    [sfood] Food and [sfuel] Fuel was vented out into space." + if(prob(10)) + var/deadname = remove_crewmember() + eventdat += "
    [deadname] was killed by rapid depressurization." + eventdat += "
    You can repair the damage with hull plates, or you can spend the next 3 days welding scrap together." + if(hull >= 1) + eventdat += "

    Use Part Wait

    " + else + eventdat += "

    Wait

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_BLACKHOLE) + eventdat += "You were swept away into the black hole." + eventdat += "

    Oh...

    " + eventdat += "

    Close

    " + settlers = list() + + if(ORION_TRAIL_MUTINY) + eventdat += "You've been hearing rumors of dissenting opinions amoungst your men." + if(settlers.len <= 2) + eventdat += "
    Your crew's so tiny you don't think anybody would risk an uprising." + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + if(prob(10)) + traitors_aboard = min(++traitors_aboard,2) + else + if(traitors_aboard) //less likely to stack traitors + if(prob(20)) + traitors_aboard = min(++traitors_aboard,2) + else if(prob(70)) + traitors_aboard = min(++traitors_aboard,2) + + eventdat += "

    Kill a crewmember

    " + eventdat += "

    Risk it

    " + eventdat += "

    Close

    " + canContinueEvent = 1 + + if(ORION_TRAIL_MUTINY_ATTACK) + if(traitors_aboard <= 0) //shouldn't trigger, but hey. + eventdat += "Haha, fooled you, there isn't a mutiny on board!" + eventdat += "
    (You should report this to a coder :S)" + else + var/trait1 = remove_crewmember() + var/trait2 = "" + if(traitors_aboard >= 2) + trait2 = remove_crewmember() + + eventdat += "Oh no, some of your crew are attempting to mutiny!!" + if(trait2) + eventdat += "
    [trait1] and [trait2]'s have armed themselves with weapons!" + else + eventdat += "
    [trait1]'s armed with a weapon!" + + var/chance2attack = alive*20 + if(prob(chance2attack)) + var/chancetokill = 30*traitors_aboard-(5*alive) //eg: 30*2-(10) = 50%, 2 traitorss, 2 crew is 50% chance + if(prob(chancetokill)) + var/deadguy = remove_crewmember() + eventdat += "
    The traitor[trait2 ? "s":""] run[trait2 ? "":"s"] up to [deadguy] and murder[trait2 ? "" : "s"] them!" + else + eventdat += "
    You valiantly fight off the traitor[trait2 ? "s":""]!" + eventdat += "
    You cut the traitor[trait2 ? "s":""] up into meat... Eww" + if(trait2) + food += 30 + traitors_aboard = max(0,traitors_aboard-2) + else + food += 15 + traitors_aboard = max(0,--traitors_aboard) + else + eventdat += "
    The traitor[trait2 ? "s":""] run[trait2 ? "":"s"] away, What wimps!" + if(trait2) + traitors_aboard = max(0,traitors_aboard-2) + else + traitors_aboard = max(0,--traitors_aboard) + + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + canContinueEvent = 1 + + + if(ORION_TRAIL_SPACEPORT) + gameStatus = ORION_STATUS_MARKET + if(spaceport_raided) + eventdat += "The Spaceport is on high alert! they wont let you dock since you tried to attack them!" + if(last_spaceport_action) + eventdat += "
    Last Spaceport Action: [last_spaceport_action]" + eventdat += "

    Depart Spaceport

    " + eventdat += "

    Close

    " + else + eventdat += "You pull the ship up to dock at a nearby Spaceport, lucky find!" + eventdat += "
    This Spaceport is home to travellers who failed to reach Orion, but managed to find a different home..." + eventdat += "
    Trading terms: FU = Fuel, FO = Food" + if(last_spaceport_action) + eventdat += "
    Last Spaceport Action: [last_spaceport_action]" + eventdat += "

    Crew:

    " + eventdat += english_list(settlers) + eventdat += "
    Food: [food] | Fuel: [fuel]" + eventdat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" + + + //If your crew is pathetic you can get freebies (provided you haven't already gotten one from this port) + if(!spaceport_freebie && (fuel < 20 || food < 20)) + spaceport_freebie++ + var/FU = 10 + var/FO = 10 + var/freecrew = 0 + if(prob(30)) + FU = 25 + FO = 25 + + if(prob(10)) + add_crewmember() + freecrew++ + + eventdat += "
    The traders of the spaceport take pity on you, and give you some supplies. (+[FU]FU,+[FO]FO)" + if(freecrew) + eventdat += "
    You also gain a new crewmember!" + + fuel += FU + food += FO + + //CREW INTERACTIONS + eventdat += "

    Crew Management:

    " + + //Buy crew + if(food >= 10 && fuel >= 10) + eventdat += "

    Hire a new Crewmember (-10FU,-10FO)

    " + else + eventdat += "

    You cannot afford a new Crewmember

    " + + //Sell crew + if(settlers.len > 1) + eventdat += "

    Sell crew for Fuel and Food (+7FU,+7FO)

    " + else + eventdat += "

    You cannot afford to sell a Crewmember

    " + + //BUY/SELL STUFF + eventdat += "

    Spare Parts:

    " + + //Engine parts + if(fuel > 5) + eventdat += "

    Buy Engine Parts (-5FU)

    " + else + eventdat += "

    You cannot afford to buy Engine Parts" + + //Hull plates + if(fuel > 5) + eventdat += "

    Buy Hull Plates (-5FU)

    " + else + eventdat += "

    You cannot afford to buy Hull Plates" + + //Electronics + if(fuel > 5) + eventdat += "

    Buy Spare Electronics (-5FU)

    " + else + eventdat += "

    You cannot afford to buy Spare Electronics" + + //Trade + if(fuel > 5) + eventdat += "

    Trade Fuel for Food (-5FU,+5FO)

    " + else + eventdat += "

    You cannot afford to Trade Fuel for Food 5) + eventdat += "

    Trade Food for Fuel (+5FU,-5FO)

    " + else + eventdat += "

    You cannot afford to Trade Food for Fuel\The [src] says, 'This is ship ID #[rand(1,1000)] to Orion Port Authority. We're coming in for landing, over.'") + sleep(20) + src.visible_message(span("warning", "[src] begins to vibrate...")) + src.audible_message("\The [src] says, 'Uh, Port? Having some issues with our reactor, could you check it out? Over.'") + sleep(30) + src.audible_message("\The [src] says, 'Oh, God! Code Eight! CODE EIGHT! IT'S GONNA BL-'") + sleep(3.6) + src.visible_message(span("danger", "[src] explodes!")) + explosion(src.loc, 1,2,4) + qdel(src) + +#undef ORION_TRAIL_WINTURN +#undef ORION_TRAIL_RAIDERS +#undef ORION_TRAIL_FLUX +#undef ORION_TRAIL_ILLNESS +#undef ORION_TRAIL_BREAKDOWN +#undef ORION_TRAIL_MUTINY +#undef ORION_TRAIL_MUTINY_ATTACK +#undef ORION_TRAIL_MALFUNCTION +#undef ORION_TRAIL_COLLISION +#undef ORION_TRAIL_SPACEPORT +#undef ORION_TRAIL_BLACKHOLE + +#undef ORION_STATUS_START +#undef ORION_STATUS_NORMAL +#undef ORION_STATUS_GAMEOVER +#undef ORION_STATUS_MARKET + +////////////////// +// Claw Machine // +////////////////// + +/obj/machinery/computer/arcade/clawmachine + name = "AlliCo Grab-a-Gift" + desc = "Show off your arcade skills for that special someone!" + icon_state = "clawmachine_new" + icon_keyboard = null + icon_screen = null + circuit = /obj/item/weapon/circuitboard/arcade/clawmachine + prizes = list(/obj/random/plushie) + var/wintick = 0 + var/winprob = 0 + var/instructions = "Insert 1 thaler or swipe a card to play!" + var/gameStatus = "CLAWMACHINE_NEW" + var/gamepaid = 0 + var/gameprice = 1 + var/winscreen = "" + +/// Payment +/obj/machinery/computer/arcade/clawmachine/attackby(obj/item/I as obj, mob/user as mob) + if(..()) + return + + if(gamepaid == 0 && vendor_account && !vendor_account.suspended) + var/paid = 0 + var/obj/item/weapon/card/id/W = I.GetID() + if(W) //for IDs and PDAs and wallets with IDs + paid = pay_with_card(W,I) + else if(istype(I, /obj/item/weapon/spacecash/ewallet)) + var/obj/item/weapon/spacecash/ewallet/C = I + paid = pay_with_ewallet(C) + else if(istype(I, /obj/item/weapon/spacecash)) + var/obj/item/weapon/spacecash/C = I + paid = pay_with_cash(C, user) + if(paid) + gamepaid = 1 + instructions = "Hit start to play!" + return + return + +////// Cash +/obj/machinery/computer/arcade/clawmachine/proc/pay_with_cash(var/obj/item/weapon/spacecash/cashmoney, mob/user) + if(!emagged) + if(gameprice > cashmoney.worth) + + // This is not a status display message, since it's something the character + // themselves is meant to see BEFORE putting the money in + to_chat(usr, "\icon[cashmoney][bicon(cashmoney)] That is not enough money.") + return 0 + + if(istype(cashmoney, /obj/item/weapon/spacecash)) + + visible_message("\The [usr] inserts some cash into \the [src].") + cashmoney.worth -= gameprice + + if(cashmoney.worth <= 0) + usr.drop_from_inventory(cashmoney) + qdel(cashmoney) + else + cashmoney.update_icon() + + // Machine has no idea who paid with cash + credit_purchase("(cash)") + return 1 + if(emagged) + playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + to_chat(user, "It doesn't seem to accept that! Seem you'll need to swipe a valid ID.") + + +///// Ewallet +/obj/machinery/computer/arcade/clawmachine/proc/pay_with_ewallet(var/obj/item/weapon/spacecash/ewallet/wallet) + if(!emagged) + visible_message("\The [usr] swipes \the [wallet] through \the [src].") + playsound(src, 'sound/machines/id_swipe.ogg', 50, 1) + if(gameprice > wallet.worth) + visible_message("Insufficient funds.") + return 0 + else + wallet.worth -= gameprice + credit_purchase("[wallet.owner_name] (chargecard)") + return 1 + if(emagged) + playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + to_chat(usr, "It doesn't seem to accept that! Seem you'll need to swipe a valid ID.") + +///// ID +/obj/machinery/computer/arcade/clawmachine/proc/pay_with_card(var/obj/item/weapon/card/id/I, var/obj/item/ID_container) + if(I==ID_container || ID_container == null) + visible_message("\The [usr] swipes \the [I] through \the [src].") + else + visible_message("\The [usr] swipes \the [ID_container] through \the [src].") + playsound(src, 'sound/machines/id_swipe.ogg', 50, 1) + var/datum/money_account/customer_account = get_account(I.associated_account_number) + if(!customer_account) + visible_message("Error: Unable to access account. Please contact technical support if problem persists.") + return 0 + + if(customer_account.suspended) + visible_message("Unable to access account: account suspended.") + return 0 + + // Have the customer punch in the PIN before checking if there's enough money. Prevents people from figuring out acct is + // empty at high security levels + if(customer_account.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2) + var/attempt_pin = tgui_input_number(usr, "Enter pin code", "Vendor transaction") + customer_account = attempt_account_access(I.associated_account_number, attempt_pin, 2) + + if(!customer_account) + visible_message("Unable to access account: incorrect credentials.") + return 0 + + if(gameprice > customer_account.money) + visible_message("Insufficient funds in account.") + return 0 + else + // Okay to move the money at this point + if(emagged) + gameprice = customer_account.money + // debit money from the purchaser's account + customer_account.money -= gameprice + + // create entry in the purchaser's account log + var/datum/transaction/T = new() + T.target_name = "[vendor_account.owner_name] (via [name])" + T.purpose = "Purchase of arcade game([name])" + if(gameprice > 0) + T.amount = "([gameprice])" + else + T.amount = "[gameprice]" + T.source_terminal = name + T.date = current_date_string + T.time = stationtime2text() + customer_account.transaction_log.Add(T) + + // Give the vendor the money. We use the account owner name, which means + // that purchases made with stolen/borrowed card will look like the card + // owner made them + credit_purchase(customer_account.owner_name) + return 1 + +/// Add to vendor account + +/obj/machinery/computer/arcade/clawmachine/proc/credit_purchase(var/target as text) + vendor_account.money += gameprice + + var/datum/transaction/T = new() + T.target_name = target + T.purpose = "Purchase of arcade game([name])" + T.amount = "[gameprice]" + T.source_terminal = name + T.date = current_date_string + T.time = stationtime2text() + vendor_account.transaction_log.Add(T) + +/// End Payment + +/obj/machinery/computer/arcade/clawmachine/New() + ..() + +/obj/machinery/computer/arcade/clawmachine/attack_hand(mob/living/user) + if(..()) + return + tgui_interact(user) + +/// TGUI Stuff + +/obj/machinery/computer/arcade/clawmachine/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ClawMachine", name, ui_x = 300, ui_y = 400) + ui.autoupdate = TRUE + ui.open() + +/obj/machinery/computer/arcade/clawmachine/tgui_data(mob/user) + var/list/data = list() + + data["wintick"] = wintick + data["instructions"] = instructions + data["gameStatus"] = gameStatus + data["winscreen"] = winscreen + + return data + +/obj/machinery/computer/arcade/clawmachine/tgui_act(action, params) + if(..()) + return + + if(action == "newgame" && gamepaid == 0) + playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + + if(action == "newgame" && gamepaid == 1) + gameStatus = "CLAWMACHINE_ON" + icon_state = "clawmachine_new_move" + instructions = "Guide the claw to the prize you want!" + wintick = 0 + + if(action == "return" && gameStatus == "CLAWMACHINE_END") + gameStatus = "CLAWMACHINE_NEW" + + if(action == "pointless" && wintick < 10) + wintick += 1 + + if(action == "pointless" && wintick >= 10) + instructions = "Insert 1 thaler or swipe a card to play!" + clawvend() + +/obj/machinery/computer/arcade/clawmachine/proc/clawvend() /// True to a real claw machine, it's NEARLY impossible to win. + winprob += 1 /// Yeah. + + if(prob(winprob)) /// YEAH. + if(!emagged) + prizevend() + winscreen = "You won!" + else if(emagged) + gameprice = 1 + emagged = 0 + winscreen = "You won...?" + var/obj/item/weapon/grenade/G = new /obj/item/weapon/grenade/explosive(get_turf(src)) /// YEAAAAAAAAAAAAAAAAAAH!!!!!!!!!! + G.activate() + G.throw_at(get_turf(usr),10,10) /// Play stupid games, win stupid prizes. + + playsound(src, 'sound/arcade/Ori_win.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + winprob = 0 + + else + playsound(src, 'sound/arcade/Ori_fail.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + winscreen = "Aw, shucks. Try again!" + wintick = 0 + gamepaid = 0 + icon_state = "clawmachine_new" + gameStatus = "CLAWMACHINE_END" + +/obj/machinery/computer/arcade/clawmachine/emag_act(mob/user) + if(!emagged) + to_chat(user, "You modify the claw of the machine. The next one is sure to win! You just have to pay...") + name = "AlliCo Snag-A-Prize" + desc = "Get some goodies, all for you!" + instructions = "Swipe a card to play!" + winprob = 100 + gamepaid = 0 + wintick = 0 + gameStatus = "CLAWMACHINE_NEW" + emagged = 1 + return 1 + +/obj/machinery/computer/arcade/attackby(obj/item/O, mob/user, params) + ..() + if(istype(O, /obj/item/stack/arcadeticket)) + var/obj/item/stack/arcadeticket/T = O + var/amount = T.get_amount() + if(amount <2) + to_chat(user, "You need 2 tickets to claim a prize!") + return + prizevend(user) + T.pay_tickets() + T.update_icon() + O = T + to_chat(user, "You turn in 2 tickets to the [src] and claim a prize!") + return + else ..() //You can now actually deconstruct these. \ No newline at end of file diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index 33d8ae2e867..dadd437b396 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -1,114 +1,114 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/obj/structure/computerframe - density = TRUE - anchored = FALSE - name = "computer frame" - icon = 'icons/obj/stock_parts.dmi' - icon_state = "0" - var/state = 0 - var/obj/item/weapon/circuitboard/circuit = null -// weight = 1.0E8 - -/obj/structure/computerframe/attackby(obj/item/P as obj, mob/user as mob) - switch(state) - if(0) - if(P.has_tool_quality(TOOL_WRENCH)) - playsound(src, P.usesound, 50, 1) - if(do_after(user, 20 * P.toolspeed)) - to_chat(user, "You wrench the frame into place.") - src.anchored = TRUE - src.state = 1 - if(P.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/WT = P.get_welder() - if(!WT.remove_fuel(0, user)) - to_chat(user, "The welding tool must be on to complete this task.") - return - playsound(src, WT.usesound, 50, 1) - if(do_after(user, 20 * WT.toolspeed)) - if(!src || !WT.isOn()) return - to_chat(user, "You deconstruct the frame.") - new /obj/item/stack/material/steel( src.loc, 5 ) - qdel(src) - if(1) - if(P.has_tool_quality(TOOL_WRENCH)) - playsound(src, P.usesound, 50, 1) - if(do_after(user, 20 * P.toolspeed)) - to_chat(user, "You unfasten the frame.") - src.anchored = FALSE - src.state = 0 - if(istype(P, /obj/item/weapon/circuitboard) && !circuit) - var/obj/item/weapon/circuitboard/B = P - if(B.board_type == "computer") - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - to_chat(user, "You place the circuit board inside the frame.") - src.icon_state = "1" - src.circuit = P - user.drop_item() - P.loc = src - else - to_chat(user, "This frame does not accept circuit boards of this type!") - if(P.has_tool_quality(TOOL_SCREWDRIVER) && circuit) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You screw the circuit board into place.") - src.state = 2 - src.icon_state = "2" - if(P.has_tool_quality(TOOL_CROWBAR)) && circuit) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You remove the circuit board.") - src.state = 1 - src.icon_state = "0" - circuit.loc = src.loc - src.circuit = null - if(2) - if(P.has_tool_quality(TOOL_SCREWDRIVER) && circuit) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You unfasten the circuit board.") - src.state = 1 - src.icon_state = "1" - if(istype(P, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = P - if (C.get_amount() < 5) - to_chat(user, "You need five coils of wire to add them to the frame.") - return - to_chat(user, "You start to add cables to the frame.") - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - if(do_after(user, 20) && state == 2) - if (C.use(5)) - to_chat(user, "You add cables to the frame.") - state = 3 - icon_state = "3" - if(3) - if(P.has_tool_quality(TOOL_WIRECUTTER)) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - src.state = 2 - src.icon_state = "2" - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( src.loc ) - A.amount = 5 - - if(istype(P, /obj/item/stack/material) && P.get_material_name() == "glass") - var/obj/item/stack/G = P - if (G.get_amount() < 2) - to_chat(user, "You need two sheets of glass to put in the glass panel.") - return - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - to_chat(user, "You start to put in the glass panel.") - if(do_after(user, 20) && state == 3) - if (G.use(2)) - to_chat(user, "You put in the glass panel.") - src.state = 4 - src.icon_state = "4" - if(4) - if(P.has_tool_quality(TOOL_CROWBAR)) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You remove the glass panel.") - src.state = 3 - src.icon_state = "3" - new /obj/item/stack/material/glass( src.loc, 2 ) - if(P.has_tool_quality(TOOL_SCREWDRIVER)) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You connect the monitor.") - var/B = new src.circuit.build_path ( src.loc ) - src.circuit.construct(B) - qdel(src) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/obj/structure/computerframe + density = TRUE + anchored = FALSE + name = "computer frame" + icon = 'icons/obj/stock_parts.dmi' + icon_state = "0" + var/state = 0 + var/obj/item/weapon/circuitboard/circuit = null +// weight = 1.0E8 + +/obj/structure/computerframe/attackby(obj/item/P as obj, mob/user as mob) + switch(state) + if(0) + if(P.has_tool_quality(TOOL_WRENCH)) + playsound(src, P.usesound, 50, 1) + if(do_after(user, 20 * P.toolspeed)) + to_chat(user, "You wrench the frame into place.") + src.anchored = TRUE + src.state = 1 + if(P.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = P.get_welder() + if(!WT.remove_fuel(0, user)) + to_chat(user, "The welding tool must be on to complete this task.") + return + playsound(src, WT.usesound, 50, 1) + if(do_after(user, 20 * WT.toolspeed)) + if(!src || !WT.isOn()) return + to_chat(user, "You deconstruct the frame.") + new /obj/item/stack/material/steel( src.loc, 5 ) + qdel(src) + if(1) + if(P.has_tool_quality(TOOL_WRENCH)) + playsound(src, P.usesound, 50, 1) + if(do_after(user, 20 * P.toolspeed)) + to_chat(user, "You unfasten the frame.") + src.anchored = FALSE + src.state = 0 + if(istype(P, /obj/item/weapon/circuitboard) && !circuit) + var/obj/item/weapon/circuitboard/B = P + if(B.board_type == "computer") + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + to_chat(user, "You place the circuit board inside the frame.") + src.icon_state = "1" + src.circuit = P + user.drop_item() + P.loc = src + else + to_chat(user, "This frame does not accept circuit boards of this type!") + if(P.has_tool_quality(TOOL_SCREWDRIVER) && circuit) + playsound(src, P.usesound, 50, 1) + to_chat(user, "You screw the circuit board into place.") + src.state = 2 + src.icon_state = "2" + if(P.has_tool_quality(TOOL_CROWBAR)) && circuit) + playsound(src, P.usesound, 50, 1) + to_chat(user, "You remove the circuit board.") + src.state = 1 + src.icon_state = "0" + circuit.loc = src.loc + src.circuit = null + if(2) + if(P.has_tool_quality(TOOL_SCREWDRIVER) && circuit) + playsound(src, P.usesound, 50, 1) + to_chat(user, "You unfasten the circuit board.") + src.state = 1 + src.icon_state = "1" + if(istype(P, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = P + if (C.get_amount() < 5) + to_chat(user, "You need five coils of wire to add them to the frame.") + return + to_chat(user, "You start to add cables to the frame.") + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + if(do_after(user, 20) && state == 2) + if (C.use(5)) + to_chat(user, "You add cables to the frame.") + state = 3 + icon_state = "3" + if(3) + if(P.has_tool_quality(TOOL_WIRECUTTER)) + playsound(src, P.usesound, 50, 1) + to_chat(user, "You remove the cables.") + src.state = 2 + src.icon_state = "2" + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( src.loc ) + A.amount = 5 + + if(istype(P, /obj/item/stack/material) && P.get_material_name() == "glass") + var/obj/item/stack/G = P + if (G.get_amount() < 2) + to_chat(user, "You need two sheets of glass to put in the glass panel.") + return + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + to_chat(user, "You start to put in the glass panel.") + if(do_after(user, 20) && state == 3) + if (G.use(2)) + to_chat(user, "You put in the glass panel.") + src.state = 4 + src.icon_state = "4" + if(4) + if(P.has_tool_quality(TOOL_CROWBAR)) + playsound(src, P.usesound, 50, 1) + to_chat(user, "You remove the glass panel.") + src.state = 3 + src.icon_state = "3" + new /obj/item/stack/material/glass( src.loc, 2 ) + if(P.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(src, P.usesound, 50, 1) + to_chat(user, "You connect the monitor.") + var/B = new src.circuit.build_path ( src.loc ) + src.circuit.construct(B) + qdel(src) diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 3af4562f099..34caeb2d357 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -1,206 +1,206 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/obj/machinery/computer/security - name = "security camera monitor" - desc = "Used to access the various cameras on the station." - - icon_keyboard = "security_key" - icon_screen = "cameras" - light_color = "#a91515" - circuit = /obj/item/weapon/circuitboard/security - - var/mapping = 0//For the overview file, interesting bit of code. - var/list/network = list() - - var/datum/tgui_module/camera/camera - var/camera_datum_type = /datum/tgui_module/camera - -/obj/machinery/computer/security/Initialize() - . = ..() - if(!LAZYLEN(network)) - network = get_default_networks() - camera = new camera_datum_type(src, network) - -/obj/machinery/computer/security/proc/get_default_networks() - . = using_map.station_networks.Copy() - -/obj/machinery/computer/security/Destroy() - QDEL_NULL(camera) - return ..() - -/obj/machinery/computer/security/tgui_interact(mob/user, datum/tgui/ui = null) - camera.tgui_interact(user, ui) - -/obj/machinery/computer/security/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - tgui_interact(user) - -/obj/machinery/computer/security/attack_robot(mob/user) - if(isrobot(user)) - var/mob/living/silicon/robot/R = user - if(!R.shell) - return attack_hand(user) - ..() - -/obj/machinery/computer/security/attack_ai(mob/user) - if(isAI(user)) - to_chat(user, "You realise its kind of stupid to access a camera console when you have the entire camera network at your metaphorical fingertips") - return - attack_hand(user) - -/obj/machinery/computer/security/proc/set_network(list/new_network) - network = new_network - camera.network = network - camera.access_based = FALSE - -//Camera control: arrow keys. -/obj/machinery/computer/security/telescreen - name = "Telescreen" - desc = "Used for watching an empty arena." - icon_state = "wallframe" - layer = ABOVE_WINDOW_LAYER - icon_keyboard = null - icon_screen = null - light_range_on = 0 - network = list(NETWORK_THUNDER) - density = FALSE - circuit = null - -GLOBAL_LIST_EMPTY(entertainment_screens) -/obj/machinery/computer/security/telescreen/entertainment - name = "entertainment monitor" - desc = "Damn, why do they never have anything interesting on these things? (Alt-click to toggle the display)" - icon = 'icons/obj/entertainment_monitor.dmi' - icon_state = "screen" - icon_screen = null - light_color = "#FFEEDB" - light_range_on = 2 - network = list(NETWORK_THUNDER) - circuit = /obj/item/weapon/circuitboard/security/telescreen/entertainment - camera_datum_type = /datum/tgui_module/camera/bigscreen - - var/obj/item/device/radio/radio = null - var/obj/effect/overlay/vis/pinboard - var/datum/weakref/showing - - var/enabled = TRUE // on or off - -/obj/machinery/computer/security/telescreen/entertainment/Initialize() - GLOB.entertainment_screens += src - - var/static/icon/mask = icon('icons/obj/entertainment_monitor.dmi', "mask") - - add_overlay("glass") - - pinboard = new() - pinboard.icon = icon - pinboard.icon_state = "pinboard" - pinboard.layer = 0.1 - pinboard.vis_flags = VIS_UNDERLAY|VIS_INHERIT_ID|VIS_INHERIT_PLANE - pinboard.appearance_flags = KEEP_TOGETHER - pinboard.add_filter("screen cutter", 1, alpha_mask_filter(icon = mask)) - vis_contents += pinboard - - . = ..() - - radio = new(src) - radio.listening = TRUE - radio.broadcasting = FALSE - radio.set_frequency(ENT_FREQ) - radio.canhear_range = world.view // Same as default sight range. - power_change() - -/obj/machinery/computer/security/telescreen/entertainment/Destroy() - if(showing) - stop_showing() - vis_contents.Cut() - qdel_null(pinboard) - qdel_null(radio) - return ..() - -/obj/machinery/computer/security/telescreen/entertainment/proc/toggle() - enabled = !enabled - if(!enabled) - stop_showing() - radio?.on = FALSE - else if(operable()) - radio?.on = TRUE - -/obj/machinery/computer/security/telescreen/entertainment/Click(location, control, params) - var/list/modifiers = params2list(params) - if(modifiers["alt"]) - if(isliving(usr) && Adjacent(usr) && !usr.incapacitated()) - toggle() - visible_message("[usr] toggles [src] [enabled ? "on" : "off"].","You toggle [src] [enabled ? "on" : "off"].", runemessage = "click") - else - attack_hand(usr) - -/obj/machinery/computer/security/telescreen/entertainment/update_icon() - return // NUH - -/obj/machinery/computer/security/telescreen/entertainment/proc/show_thing(atom/thing) - if(!enabled) - return - if(showing) - stop_showing() - if(stat & NOPOWER) - return - showing = WEAKREF(thing) - pinboard.vis_contents = list(thing) - -/obj/machinery/computer/security/telescreen/entertainment/proc/stop_showing() - // Reverse of the above - pinboard.vis_contents = null - showing = null - -/obj/machinery/computer/security/telescreen/entertainment/proc/maybe_stop_showing(datum/weakref/thingref) - if(showing == thingref) - stop_showing() - -/obj/machinery/computer/security/telescreen/entertainment/power_change() - ..() - if(stat & NOPOWER) - radio?.on = FALSE - stop_showing() - else if(enabled) - radio?.on = TRUE - -/obj/machinery/computer/security/wooden_tv - name = "security camera monitor" - desc = "An old TV hooked into the station's camera network." - icon_state = "television" - icon_keyboard = null - icon_screen = "detective_tv" - circuit = /obj/item/weapon/circuitboard/security/tv - light_color = "#3848B3" - light_power_on = 0.5 - -/obj/machinery/computer/security/mining - name = "outpost camera monitor" - desc = "Used to watch over mining operations." - icon_keyboard = "mining_key" - icon_screen = "mining" - network = list("Mining Outpost") - circuit = /obj/item/weapon/circuitboard/security/mining - light_color = "#F9BBFC" - -/obj/machinery/computer/security/engineering - name = "engineering camera monitor" - desc = "Used to monitor fires and breaches." - icon_keyboard = "power_key" - icon_screen = "engie_cams" - circuit = /obj/item/weapon/circuitboard/security/engineering - light_color = "#FAC54B" - -/obj/machinery/computer/security/engineering/get_default_networks() - . = engineering_networks.Copy() - -/obj/machinery/computer/security/nuclear - name = "head mounted camera monitor" - desc = "Used to access the built-in cameras in helmets." - icon_state = "syndicam" - network = list(NETWORK_MERCENARY) - circuit = null - req_access = list(150) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/obj/machinery/computer/security + name = "security camera monitor" + desc = "Used to access the various cameras on the station." + + icon_keyboard = "security_key" + icon_screen = "cameras" + light_color = "#a91515" + circuit = /obj/item/weapon/circuitboard/security + + var/mapping = 0//For the overview file, interesting bit of code. + var/list/network = list() + + var/datum/tgui_module/camera/camera + var/camera_datum_type = /datum/tgui_module/camera + +/obj/machinery/computer/security/Initialize() + . = ..() + if(!LAZYLEN(network)) + network = get_default_networks() + camera = new camera_datum_type(src, network) + +/obj/machinery/computer/security/proc/get_default_networks() + . = using_map.station_networks.Copy() + +/obj/machinery/computer/security/Destroy() + QDEL_NULL(camera) + return ..() + +/obj/machinery/computer/security/tgui_interact(mob/user, datum/tgui/ui = null) + camera.tgui_interact(user, ui) + +/obj/machinery/computer/security/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + tgui_interact(user) + +/obj/machinery/computer/security/attack_robot(mob/user) + if(isrobot(user)) + var/mob/living/silicon/robot/R = user + if(!R.shell) + return attack_hand(user) + ..() + +/obj/machinery/computer/security/attack_ai(mob/user) + if(isAI(user)) + to_chat(user, "You realise its kind of stupid to access a camera console when you have the entire camera network at your metaphorical fingertips") + return + attack_hand(user) + +/obj/machinery/computer/security/proc/set_network(list/new_network) + network = new_network + camera.network = network + camera.access_based = FALSE + +//Camera control: arrow keys. +/obj/machinery/computer/security/telescreen + name = "Telescreen" + desc = "Used for watching an empty arena." + icon_state = "wallframe" + layer = ABOVE_WINDOW_LAYER + icon_keyboard = null + icon_screen = null + light_range_on = 0 + network = list(NETWORK_THUNDER) + density = FALSE + circuit = null + +GLOBAL_LIST_EMPTY(entertainment_screens) +/obj/machinery/computer/security/telescreen/entertainment + name = "entertainment monitor" + desc = "Damn, why do they never have anything interesting on these things? (Alt-click to toggle the display)" + icon = 'icons/obj/entertainment_monitor.dmi' + icon_state = "screen" + icon_screen = null + light_color = "#FFEEDB" + light_range_on = 2 + network = list(NETWORK_THUNDER) + circuit = /obj/item/weapon/circuitboard/security/telescreen/entertainment + camera_datum_type = /datum/tgui_module/camera/bigscreen + + var/obj/item/device/radio/radio = null + var/obj/effect/overlay/vis/pinboard + var/datum/weakref/showing + + var/enabled = TRUE // on or off + +/obj/machinery/computer/security/telescreen/entertainment/Initialize() + GLOB.entertainment_screens += src + + var/static/icon/mask = icon('icons/obj/entertainment_monitor.dmi', "mask") + + add_overlay("glass") + + pinboard = new() + pinboard.icon = icon + pinboard.icon_state = "pinboard" + pinboard.layer = 0.1 + pinboard.vis_flags = VIS_UNDERLAY|VIS_INHERIT_ID|VIS_INHERIT_PLANE + pinboard.appearance_flags = KEEP_TOGETHER + pinboard.add_filter("screen cutter", 1, alpha_mask_filter(icon = mask)) + vis_contents += pinboard + + . = ..() + + radio = new(src) + radio.listening = TRUE + radio.broadcasting = FALSE + radio.set_frequency(ENT_FREQ) + radio.canhear_range = world.view // Same as default sight range. + power_change() + +/obj/machinery/computer/security/telescreen/entertainment/Destroy() + if(showing) + stop_showing() + vis_contents.Cut() + qdel_null(pinboard) + qdel_null(radio) + return ..() + +/obj/machinery/computer/security/telescreen/entertainment/proc/toggle() + enabled = !enabled + if(!enabled) + stop_showing() + radio?.on = FALSE + else if(operable()) + radio?.on = TRUE + +/obj/machinery/computer/security/telescreen/entertainment/Click(location, control, params) + var/list/modifiers = params2list(params) + if(modifiers["alt"]) + if(isliving(usr) && Adjacent(usr) && !usr.incapacitated()) + toggle() + visible_message("[usr] toggles [src] [enabled ? "on" : "off"].","You toggle [src] [enabled ? "on" : "off"].", runemessage = "click") + else + attack_hand(usr) + +/obj/machinery/computer/security/telescreen/entertainment/update_icon() + return // NUH + +/obj/machinery/computer/security/telescreen/entertainment/proc/show_thing(atom/thing) + if(!enabled) + return + if(showing) + stop_showing() + if(stat & NOPOWER) + return + showing = WEAKREF(thing) + pinboard.vis_contents = list(thing) + +/obj/machinery/computer/security/telescreen/entertainment/proc/stop_showing() + // Reverse of the above + pinboard.vis_contents = null + showing = null + +/obj/machinery/computer/security/telescreen/entertainment/proc/maybe_stop_showing(datum/weakref/thingref) + if(showing == thingref) + stop_showing() + +/obj/machinery/computer/security/telescreen/entertainment/power_change() + ..() + if(stat & NOPOWER) + radio?.on = FALSE + stop_showing() + else if(enabled) + radio?.on = TRUE + +/obj/machinery/computer/security/wooden_tv + name = "security camera monitor" + desc = "An old TV hooked into the station's camera network." + icon_state = "television" + icon_keyboard = null + icon_screen = "detective_tv" + circuit = /obj/item/weapon/circuitboard/security/tv + light_color = "#3848B3" + light_power_on = 0.5 + +/obj/machinery/computer/security/mining + name = "outpost camera monitor" + desc = "Used to watch over mining operations." + icon_keyboard = "mining_key" + icon_screen = "mining" + network = list("Mining Outpost") + circuit = /obj/item/weapon/circuitboard/security/mining + light_color = "#F9BBFC" + +/obj/machinery/computer/security/engineering + name = "engineering camera monitor" + desc = "Used to monitor fires and breaches." + icon_keyboard = "power_key" + icon_screen = "engie_cams" + circuit = /obj/item/weapon/circuitboard/security/engineering + light_color = "#FAC54B" + +/obj/machinery/computer/security/engineering/get_default_networks() + . = engineering_networks.Copy() + +/obj/machinery/computer/security/nuclear + name = "head mounted camera monitor" + desc = "Used to access the built-in cameras in helmets." + icon_state = "syndicam" + network = list(NETWORK_MERCENARY) + circuit = null + req_access = list(150) diff --git a/code/game/machinery/computer/camera_vr.dm b/code/game/machinery/computer/camera_vr.dm index 372a5bfb11e..7f9f88d877f 100644 --- a/code/game/machinery/computer/camera_vr.dm +++ b/code/game/machinery/computer/camera_vr.dm @@ -1,35 +1,35 @@ -/obj/machinery/computer/security/abductor - name = "camera uplink" - desc = "Used for hacking into camera networks" - icon = 'icons/obj/abductor.dmi' - icon_state = "camera" - network = list( "Mercenary", - "Cargo", - "Circuits", - "Civilian", - "Command", - "Engine", - "Engineering", - "Exploration", - "Medical", - "Mining Outpost", - "Outside", - "Research", - "Research Outpost", - "Robots", - "Security", - "Telecommunications", - "Tether", - "TalonShip", - "Entertainment", - "Communicators" - ) - -/obj/machinery/computer/security/xenobio - name = "xenobiology camera monitor" - desc = "Used to access the xenobiology cell cameras." - icon_keyboard = "mining_key" - icon_screen = "mining" - network = list(NETWORK_XENOBIO) - circuit = /obj/item/weapon/circuitboard/security/xenobio +/obj/machinery/computer/security/abductor + name = "camera uplink" + desc = "Used for hacking into camera networks" + icon = 'icons/obj/abductor.dmi' + icon_state = "camera" + network = list( "Mercenary", + "Cargo", + "Circuits", + "Civilian", + "Command", + "Engine", + "Engineering", + "Exploration", + "Medical", + "Mining Outpost", + "Outside", + "Research", + "Research Outpost", + "Robots", + "Security", + "Telecommunications", + "Tether", + "TalonShip", + "Entertainment", + "Communicators" + ) + +/obj/machinery/computer/security/xenobio + name = "xenobiology camera monitor" + desc = "Used to access the xenobiology cell cameras." + icon_keyboard = "mining_key" + icon_screen = "mining" + network = list(NETWORK_XENOBIO) + circuit = /obj/item/weapon/circuitboard/security/xenobio light_color = "#F9BBFC" \ No newline at end of file diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index e878aaaf8a7..acc7dcb975e 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -1,32 +1,32 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -// The communications computer -/obj/machinery/computer/communications - name = "command and communications console" - desc = "Used to command and control the station. Can relay long-range communications." - icon_keyboard = "tech_key" - icon_screen = "comm" - light_color = "#0099ff" - req_access = list(access_heads) - circuit = /obj/item/weapon/circuitboard/communications - - var/datum/tgui_module/communications/communications - -/obj/machinery/computer/communications/Initialize() - . = ..() - communications = new(src) - -/obj/machinery/computer/communications/emag_act(var/remaining_charges, var/mob/user) - if(!emagged) - emagged = TRUE - communications.emagged = TRUE - to_chat(user, "You scramble the communication routing circuits!") - return TRUE - -/obj/machinery/computer/communications/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/computer/communications/attack_hand(mob/user) - if(..()) - return - communications.tgui_interact(user) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +// The communications computer +/obj/machinery/computer/communications + name = "command and communications console" + desc = "Used to command and control the station. Can relay long-range communications." + icon_keyboard = "tech_key" + icon_screen = "comm" + light_color = "#0099ff" + req_access = list(access_heads) + circuit = /obj/item/weapon/circuitboard/communications + + var/datum/tgui_module/communications/communications + +/obj/machinery/computer/communications/Initialize() + . = ..() + communications = new(src) + +/obj/machinery/computer/communications/emag_act(var/remaining_charges, var/mob/user) + if(!emagged) + emagged = TRUE + communications.emagged = TRUE + to_chat(user, "You scramble the communication routing circuits!") + return TRUE + +/obj/machinery/computer/communications/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/computer/communications/attack_hand(mob/user) + if(..()) + return + communications.tgui_interact(user) diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 1af31afe80d..cf1ce8ab20b 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -1,35 +1,35 @@ -/obj/machinery/computer/crew - name = "crew monitoring computer" - desc = "Used to monitor active health sensors built into most of the crew's uniforms." - icon_keyboard = "med_key" - icon_screen = "crew" - light_color = "#315ab4" - use_power = USE_POWER_IDLE - idle_power_usage = 250 - active_power_usage = 500 - circuit = /obj/item/weapon/circuitboard/crew - var/datum/tgui_module/crew_monitor/crew_monitor - -/obj/machinery/computer/crew/New() - crew_monitor = new(src) - ..() - -/obj/machinery/computer/crew/Destroy() - qdel(crew_monitor) - crew_monitor = null - ..() - -/obj/machinery/computer/crew/attack_ai(mob/user) - attack_hand(user) - -/obj/machinery/computer/crew/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - tgui_interact(user) - -/obj/machinery/computer/crew/tgui_interact(mob/user, datum/tgui/ui = null) - crew_monitor.tgui_interact(user, ui) - -/obj/machinery/computer/crew/interact(mob/user) - crew_monitor.tgui_interact(user) +/obj/machinery/computer/crew + name = "crew monitoring computer" + desc = "Used to monitor active health sensors built into most of the crew's uniforms." + icon_keyboard = "med_key" + icon_screen = "crew" + light_color = "#315ab4" + use_power = USE_POWER_IDLE + idle_power_usage = 250 + active_power_usage = 500 + circuit = /obj/item/weapon/circuitboard/crew + var/datum/tgui_module/crew_monitor/crew_monitor + +/obj/machinery/computer/crew/New() + crew_monitor = new(src) + ..() + +/obj/machinery/computer/crew/Destroy() + qdel(crew_monitor) + crew_monitor = null + ..() + +/obj/machinery/computer/crew/attack_ai(mob/user) + attack_hand(user) + +/obj/machinery/computer/crew/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + tgui_interact(user) + +/obj/machinery/computer/crew/tgui_interact(mob/user, datum/tgui/ui = null) + crew_monitor.tgui_interact(user, ui) + +/obj/machinery/computer/crew/interact(mob/user) + crew_monitor.tgui_interact(user) diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 69c4e279e31..c54fb1ffb8b 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -1,514 +1,514 @@ -#define MED_DATA_R_LIST 2 // Record list -#define MED_DATA_MAINT 3 // Records maintenance -#define MED_DATA_RECORD 4 // Record -#define MED_DATA_V_DATA 5 // Virus database -#define MED_DATA_MEDBOT 6 // Medbot monitor - -#define FIELD(N, V, E) list(field = N, value = V, edit = E) -#define MED_FIELD(N, V, E, LB) list(field = N, value = V, edit = E, line_break = LB) - -/obj/machinery/computer/med_data//TODO:SANITY - name = "medical records console" - desc = "Used to view, edit and maintain medical records." - icon_keyboard = "med_key" - icon_screen = "medcomp" - light_color = "#315ab4" - req_one_access = list(access_medical, access_forensics_lockers, access_robotics) - circuit = /obj/item/weapon/circuitboard/med_data - var/obj/item/weapon/card/id/scan = null - var/authenticated = null - var/rank = null - var/screen = null - var/datum/data/record/active1 = null - var/datum/data/record/active2 = null - var/list/temp = null - var/printing = null - // The below are used to make modal generation more convenient - var/static/list/field_edit_questions - var/static/list/field_edit_choices - - -/obj/machinery/computer/med_data/Initialize() - . = ..() - field_edit_questions = list( - // General - "sex" = "Please select new sex:", - "species" = "Please input new species:", - "age" = "Please input new age:", - "fingerprint" = "Please input new fingerprint hash:", - "p_stat" = "Please select new physical status:", - "m_stat" = "Please select new mental status:", - // Medical - "id_gender" = "Please select new gender identity:", - "blood_type" = "Please select new blood type:", - "blood_reagent" = "Please select new blood basis:", - "b_dna" = "Please input new DNA:", - "mi_dis" = "Please input new minor disabilities:", - "mi_dis_d" = "Please summarize minor disabilities:", - "ma_dis" = "Please input new major disabilities:", - "ma_dis_d" = "Please summarize major disabilities:", - "alg" = "Please input new allergies:", - "alg_d" = "Please summarize allergies:", - "cdi" = "Please input new current diseases:", - "cdi_d" = "Please summarize current diseases:", - "notes" = "Please input new important notes:", - ) - field_edit_choices = list( - // General - "sex" = all_genders_text_list, - "p_stat" = list("*Deceased*", "*SSD*", "Active", "Physically Unfit", "Disabled"), - "m_stat" = list("*Insane*", "*Unstable*", "*Watch*", "Stable"), - // Medical - "id_gender" = all_genders_text_list, - "blood_type" = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"), - ) - -/obj/machinery/computer/med_data/Destroy() - active1 = null - active2 = null - return ..() - -/obj/machinery/computer/med_data/verb/eject_id() - set category = "Object" - set name = "Eject ID Card" - set src in oview(1) - - if(!usr || usr.stat || usr.lying) return - - if(scan) - to_chat(usr, "You remove \the [scan] from \the [src].") - scan.loc = get_turf(src) - if(!usr.get_active_hand() && istype(usr,/mob/living/carbon/human)) - usr.put_in_hands(scan) - scan = null - else - to_chat(usr, "There is nothing to remove from the console.") - return - -/obj/machinery/computer/med_data/attackby(var/obj/item/O, var/mob/user) - if(istype(O, /obj/item/weapon/card/id) && !scan && user.unEquip(O)) - O.loc = src - scan = O - to_chat(user, "You insert \the [O].") - tgui_interact(user) - else - ..() - -/obj/machinery/computer/med_data/attack_ai(user as mob) - return attack_hand(user) - -/obj/machinery/computer/med_data/attack_hand(mob/user as mob) - if(..()) - return - add_fingerprint(user) - tgui_interact(user) - - -/obj/machinery/computer/med_data/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "MedicalRecords", "Medical Records") // 800, 380 - ui.open() - ui.set_autoupdate(FALSE) - - -/obj/machinery/computer/med_data/tgui_data(mob/user) - var/data[0] - data["temp"] = temp - data["scan"] = scan ? scan.name : null - data["authenticated"] = authenticated - data["rank"] = rank - data["screen"] = screen - data["printing"] = printing - data["isAI"] = isAI(user) - data["isRobot"] = isrobot(user) - if(authenticated) - switch(screen) - if(MED_DATA_R_LIST) - if(!isnull(data_core.general)) - var/list/records = list() - data["records"] = records - for(var/datum/data/record/R in sortRecord(data_core.general)) - records[++records.len] = list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"]) - if(MED_DATA_RECORD) - var/list/general = list() - data["general"] = general - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - var/list/fields = list() - general["fields"] = fields - fields[++fields.len] = FIELD("Name", active1.fields["name"], null) - fields[++fields.len] = FIELD("ID", active1.fields["id"], null) - fields[++fields.len] = FIELD("Sex", active1.fields["sex"], "sex") - fields[++fields.len] = FIELD("Species", active1.fields["species"], "species") - fields[++fields.len] = FIELD("Age", "[active1.fields["age"]]", "age") - fields[++fields.len] = FIELD("Fingerprint", active1.fields["fingerprint"], "fingerprint") - fields[++fields.len] = FIELD("Physical Status", active1.fields["p_stat"], "p_stat") - fields[++fields.len] = FIELD("Mental Status", active1.fields["m_stat"], "m_stat") - var/list/photos = list() - general["photos"] = photos - photos[++photos.len] = active1.fields["photo-south"] - photos[++photos.len] = active1.fields["photo-west"] - general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) - general["empty"] = 0 - else - general["empty"] = 1 - - var/list/medical = list() - data["medical"] = medical - if(istype(active2, /datum/data/record) && data_core.medical.Find(active2)) - var/list/fields = list() - medical["fields"] = fields - fields[++fields.len] = MED_FIELD("Gender identity", active2.fields["id_gender"], "id_gender", TRUE) - fields[++fields.len] = MED_FIELD("Blood Type", active2.fields["b_type"], "blood_type", FALSE) - fields[++fields.len] = MED_FIELD("Blood Basis", active2.fields["blood_reagent"], "blood_reagent", FALSE) - fields[++fields.len] = MED_FIELD("DNA", active2.fields["b_dna"], "b_dna", TRUE) - fields[++fields.len] = MED_FIELD("Brain Type", active2.fields["brain_type"], "brain_type", TRUE) - fields[++fields.len] = MED_FIELD("Important Notes", active2.fields["notes"], "notes", TRUE) - if(!active2.fields["comments"] || !islist(active2.fields["comments"])) - active2.fields["comments"] = list() - medical["comments"] = active2.fields["comments"] - medical["empty"] = 0 - else - medical["empty"] = 1 - if(MED_DATA_V_DATA) - data["virus"] = list() - for(var/ID in virusDB) - var/datum/data/record/v = virusDB[ID] - data["virus"] += list(list("name" = v.fields["name"], "D" = "\ref[v]")) - if(MED_DATA_MEDBOT) - data["medbots"] = list() - for(var/mob/living/bot/medbot/M in mob_list) - if(M.z != z) - continue - var/turf/T = get_turf(M) - if(T) - var/medbot = list() - var/area/A = get_area(T) - medbot["name"] = M.name - medbot["area"] = A.name - medbot["x"] = T.x - medbot["y"] = T.y - medbot["on"] = M.on - if(!isnull(M.reagent_glass) && M.use_beaker) - medbot["use_beaker"] = 1 - medbot["total_volume"] = M.reagent_glass.reagents.total_volume - medbot["maximum_volume"] = M.reagent_glass.reagents.maximum_volume - else - medbot["use_beaker"] = 0 - data["medbots"] += list(medbot) - - data["modal"] = tgui_modal_data(src) - return data - -/obj/machinery/computer/med_data/tgui_act(action, params) - if(..()) - return TRUE - - if(!data_core.general.Find(active1)) - active1 = null - if(!data_core.medical.Find(active2)) - active2 = null - - . = TRUE - if(tgui_act_modal(action, params)) - return - - switch(action) - if("cleartemp") - temp = null - if("scan") - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/weapon/card/id)) - usr.drop_item() - I.forceMove(src) - scan = I - if("login") - var/login_type = text2num(params["login_type"]) - if(login_type == LOGIN_TYPE_NORMAL && istype(scan)) - if(check_access(scan)) - authenticated = scan.registered_name - rank = scan.assignment - else if(login_type == LOGIN_TYPE_AI && isAI(usr)) - authenticated = usr.name - rank = "AI" - else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr)) - authenticated = usr.name - var/mob/living/silicon/robot/R = usr - rank = "[R.modtype] [R.braintype]" - if(authenticated) - active1 = null - active2 = null - screen = MED_DATA_R_LIST - else - . = FALSE - - if(.) - return - - if(authenticated) - . = TRUE - switch(action) - if("logout") - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null - authenticated = null - screen = null - active1 = null - active2 = null - if("screen") - screen = clamp(text2num(params["screen"]) || 0, MED_DATA_R_LIST, MED_DATA_MEDBOT) - active1 = null - active2 = null - if("vir") - var/datum/data/record/v = locate(params["vir"]) - if(!istype(v)) - return FALSE - tgui_modal_message(src, "virus", "", null, v.fields["tgui_description"]) - if("del_all") - for(var/datum/data/record/R in data_core.medical) - qdel(R) - set_temp("All medical records deleted.") - if("del_r") - if(active2) - set_temp("Medical record deleted.") - qdel(active2) - if("d_rec") - var/datum/data/record/general_record = locate(params["d_rec"] || "") - if(!data_core.general.Find(general_record)) - set_temp("Record not found.", "danger") - return - - var/datum/data/record/medical_record - for(var/datum/data/record/M in data_core.medical) - if(M.fields["name"] == general_record.fields["name"] && M.fields["id"] == general_record.fields["id"]) - medical_record = M - break - - active1 = general_record - active2 = medical_record - screen = MED_DATA_RECORD - if("new") - if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) - var/datum/data/record/R = new /datum/data/record() - R.fields["name"] = active1.fields["name"] - R.fields["id"] = active1.fields["id"] - R.name = "Medical Record #[R.fields["id"]]" - R.fields["b_type"] = "Unknown" - R.fields["blood_reagent"] = "Unknown" - R.fields["b_dna"] = "Unknown" - R.fields["mi_dis"] = "None" - R.fields["mi_dis_d"] = "No minor disabilities have been declared." - R.fields["ma_dis"] = "None" - R.fields["ma_dis_d"] = "No major disabilities have been diagnosed." - R.fields["alg"] = "None" - R.fields["alg_d"] = "No allergies have been detected in this patient." - R.fields["cdi"] = "None" - R.fields["cdi_d"] = "No diseases have been diagnosed at the moment." - R.fields["notes"] = "No notes." - data_core.medical += R - active2 = R - screen = MED_DATA_RECORD - set_temp("Medical record created.", "success") - if("del_c") - var/index = text2num(params["del_c"] || "") - if(!index || !istype(active2, /datum/data/record)) - return - - var/list/comments = active2.fields["comments"] - index = clamp(index, 1, length(comments)) - if(comments[index]) - comments.Cut(index, index + 1) - if("search") - active1 = null - active2 = null - var/t1 = lowertext(params["t1"] || "") - if(!length(t1)) - return - - for(var/datum/data/record/R in data_core.medical) - if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"])) - active2 = R - break - if(!active2) - set_temp("Medical record not found. You must enter the person's exact name, ID or DNA.", "danger") - return - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == active2.fields["name"] && E.fields["id"] == active2.fields["id"]) - active1 = E - break - screen = MED_DATA_RECORD - if("print_p") - if(!printing) - printing = TRUE - // playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) - SStgui.update_uis(src) - addtimer(CALLBACK(src, PROC_REF(print_finish)), 5 SECONDS) - else - return FALSE - -/** - * Called in tgui_act() to process modal actions - * - * Arguments: - * * action - The action passed by tgui - * * params - The params passed by tgui - */ -/obj/machinery/computer/med_data/proc/tgui_act_modal(action, params) - . = TRUE - var/id = params["id"] // The modal's ID - var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] - switch(tgui_modal_act(src, action, params)) - if(TGUI_MODAL_OPEN) - switch(id) - if("edit") - var/field = arguments["field"] - if(!length(field) || !field_edit_questions[field]) - return - var/question = field_edit_questions[field] - var/choices = field_edit_choices[field] - if(length(choices)) - tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices) - else - tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"]) - if("add_c") - tgui_modal_input(src, id, "Please enter your message:") - else - return FALSE - if(TGUI_MODAL_ANSWER) - var/answer = params["answer"] - switch(id) - if("edit") - var/field = arguments["field"] - if(!length(field) || !field_edit_questions[field]) - return - var/list/choices = field_edit_choices[field] - if(length(choices) && !(answer in choices)) - return - - if(field == "age") - answer = text2num(answer) - - if(istype(active2) && (field in active2.fields)) - active2.fields[field] = answer - else if(istype(active1) && (field in active1.fields)) - active1.fields[field] = answer - if("add_c") - if(!length(answer) || !istype(active2) || !length(authenticated)) - return - active2.fields["comments"] += list(list( - header = "Made by [authenticated] ([rank]) at [worldtime2stationtime(world.time)]", - text = answer - )) - else - return FALSE - else - return FALSE - - -/** - * Called when the print timer finishes - */ -/obj/machinery/computer/med_data/proc/print_finish() - var/obj/item/weapon/paper/P = new(loc) - P.info = "

    Medical Record

    " - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] -
    \nSex: [active1.fields["sex"]] -
    \nSpecies: [active1.fields["species"]] -
    \nAge: [active1.fields["age"]] -
    \nFingerprint: [active1.fields["fingerprint"]] -
    \nPhysical Status: [active1.fields["p_stat"]] -
    \nMental Status: [active1.fields["m_stat"]]
    "} - else - P.info += "General Record Lost!
    " - if(istype(active2, /datum/data/record) && data_core.medical.Find(active2)) - P.info += {"
    \n
    Medical Data
    -
    \nGender Identity: [active2.fields["id_gender"]] -
    \nBlood Type: [active2.fields["b_type"]] -
    \nBlood Basis: [active2.fields["blood_reagent"]] -
    \nDNA: [active2.fields["b_dna"]]
    \n -
    \nMinor Disabilities: [active2.fields["mi_dis"]] -
    \nDetails: [active2.fields["mi_dis_d"]]
    \n -
    \nMajor Disabilities: [active2.fields["ma_dis"]] -
    \nDetails: [active2.fields["ma_dis_d"]]
    \n -
    \nAllergies: [active2.fields["alg"]] -
    \nDetails: [active2.fields["alg_d"]]
    \n -
    \nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section) -
    \nDetails: [active2.fields["cdi_d"]]
    \n -
    \nImportant Notes: -
    \n\t[active2.fields["notes"]]
    \n -
    \n -
    Comments/Log

    "} - for(var/c in active2.fields["comments"]) - P.info += "[c["header"]]
    [c["text"]]
    " - else - P.info += "Medical Record Lost!
    " - P.info += "" - P.name = "paper - 'Medical Record: [active1.fields["name"]]'" - printing = FALSE - SStgui.update_uis(src) - -/** - * Sets a temporary message to display to the user - * - * Arguments: - * * text - Text to display, null/empty to clear the message from the UI - * * style - The style of the message: (color name), info, success, warning, danger, virus - */ -/obj/machinery/computer/med_data/proc/set_temp(text = "", style = "info", update_now = FALSE) - temp = list(text = text, style = style) - if(update_now) - SStgui.update_uis(src) - -/obj/machinery/computer/med_data/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - for(var/datum/data/record/R in data_core.medical) - if(prob(10/severity)) - switch(rand(1,6)) - if(1) - R.fields["name"] = "[pick(pick(first_names_male), pick(first_names_female))] [pick(last_names)]" - if(2) - R.fields["sex"] = pick("Male", "Female") - if(3) - R.fields["age"] = rand(5, 85) - if(4) - R.fields["b_type"] = pick("A-", "B-", "AB-", "O-", "A+", "B+", "AB+", "O+") - if(5) - R.fields["p_stat"] = pick("*SSD*", "Active", "Physically Unfit", "Disabled") - if(PDA_Manifest.len) - PDA_Manifest.Cut() - if(6) - R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") - continue - - else if(prob(1)) - qdel(R) - continue - - ..(severity) - - -/obj/machinery/computer/med_data/laptop //[TO DO] Change name to PCU and update mapdata to include replacement computers - name = "\improper Medical Laptop" - desc = "A personal computer unit. It seems to have only the medical records program installed." - icon_screen = "pcu_generic" - icon_state = "pcu_med" - icon_keyboard = "pcu_key" - light_color = "#5284e7" - circuit = /obj/item/weapon/circuitboard/med_data/pcu - density = FALSE - -#undef FIELD -#undef MED_FIELD +#define MED_DATA_R_LIST 2 // Record list +#define MED_DATA_MAINT 3 // Records maintenance +#define MED_DATA_RECORD 4 // Record +#define MED_DATA_V_DATA 5 // Virus database +#define MED_DATA_MEDBOT 6 // Medbot monitor + +#define FIELD(N, V, E) list(field = N, value = V, edit = E) +#define MED_FIELD(N, V, E, LB) list(field = N, value = V, edit = E, line_break = LB) + +/obj/machinery/computer/med_data//TODO:SANITY + name = "medical records console" + desc = "Used to view, edit and maintain medical records." + icon_keyboard = "med_key" + icon_screen = "medcomp" + light_color = "#315ab4" + req_one_access = list(access_medical, access_forensics_lockers, access_robotics) + circuit = /obj/item/weapon/circuitboard/med_data + var/obj/item/weapon/card/id/scan = null + var/authenticated = null + var/rank = null + var/screen = null + var/datum/data/record/active1 = null + var/datum/data/record/active2 = null + var/list/temp = null + var/printing = null + // The below are used to make modal generation more convenient + var/static/list/field_edit_questions + var/static/list/field_edit_choices + + +/obj/machinery/computer/med_data/Initialize() + . = ..() + field_edit_questions = list( + // General + "sex" = "Please select new sex:", + "species" = "Please input new species:", + "age" = "Please input new age:", + "fingerprint" = "Please input new fingerprint hash:", + "p_stat" = "Please select new physical status:", + "m_stat" = "Please select new mental status:", + // Medical + "id_gender" = "Please select new gender identity:", + "blood_type" = "Please select new blood type:", + "blood_reagent" = "Please select new blood basis:", + "b_dna" = "Please input new DNA:", + "mi_dis" = "Please input new minor disabilities:", + "mi_dis_d" = "Please summarize minor disabilities:", + "ma_dis" = "Please input new major disabilities:", + "ma_dis_d" = "Please summarize major disabilities:", + "alg" = "Please input new allergies:", + "alg_d" = "Please summarize allergies:", + "cdi" = "Please input new current diseases:", + "cdi_d" = "Please summarize current diseases:", + "notes" = "Please input new important notes:", + ) + field_edit_choices = list( + // General + "sex" = all_genders_text_list, + "p_stat" = list("*Deceased*", "*SSD*", "Active", "Physically Unfit", "Disabled"), + "m_stat" = list("*Insane*", "*Unstable*", "*Watch*", "Stable"), + // Medical + "id_gender" = all_genders_text_list, + "blood_type" = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"), + ) + +/obj/machinery/computer/med_data/Destroy() + active1 = null + active2 = null + return ..() + +/obj/machinery/computer/med_data/verb/eject_id() + set category = "Object" + set name = "Eject ID Card" + set src in oview(1) + + if(!usr || usr.stat || usr.lying) return + + if(scan) + to_chat(usr, "You remove \the [scan] from \the [src].") + scan.loc = get_turf(src) + if(!usr.get_active_hand() && istype(usr,/mob/living/carbon/human)) + usr.put_in_hands(scan) + scan = null + else + to_chat(usr, "There is nothing to remove from the console.") + return + +/obj/machinery/computer/med_data/attackby(var/obj/item/O, var/mob/user) + if(istype(O, /obj/item/weapon/card/id) && !scan && user.unEquip(O)) + O.loc = src + scan = O + to_chat(user, "You insert \the [O].") + tgui_interact(user) + else + ..() + +/obj/machinery/computer/med_data/attack_ai(user as mob) + return attack_hand(user) + +/obj/machinery/computer/med_data/attack_hand(mob/user as mob) + if(..()) + return + add_fingerprint(user) + tgui_interact(user) + + +/obj/machinery/computer/med_data/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "MedicalRecords", "Medical Records") // 800, 380 + ui.open() + ui.set_autoupdate(FALSE) + + +/obj/machinery/computer/med_data/tgui_data(mob/user) + var/data[0] + data["temp"] = temp + data["scan"] = scan ? scan.name : null + data["authenticated"] = authenticated + data["rank"] = rank + data["screen"] = screen + data["printing"] = printing + data["isAI"] = isAI(user) + data["isRobot"] = isrobot(user) + if(authenticated) + switch(screen) + if(MED_DATA_R_LIST) + if(!isnull(data_core.general)) + var/list/records = list() + data["records"] = records + for(var/datum/data/record/R in sortRecord(data_core.general)) + records[++records.len] = list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"]) + if(MED_DATA_RECORD) + var/list/general = list() + data["general"] = general + if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) + var/list/fields = list() + general["fields"] = fields + fields[++fields.len] = FIELD("Name", active1.fields["name"], null) + fields[++fields.len] = FIELD("ID", active1.fields["id"], null) + fields[++fields.len] = FIELD("Sex", active1.fields["sex"], "sex") + fields[++fields.len] = FIELD("Species", active1.fields["species"], "species") + fields[++fields.len] = FIELD("Age", "[active1.fields["age"]]", "age") + fields[++fields.len] = FIELD("Fingerprint", active1.fields["fingerprint"], "fingerprint") + fields[++fields.len] = FIELD("Physical Status", active1.fields["p_stat"], "p_stat") + fields[++fields.len] = FIELD("Mental Status", active1.fields["m_stat"], "m_stat") + var/list/photos = list() + general["photos"] = photos + photos[++photos.len] = active1.fields["photo-south"] + photos[++photos.len] = active1.fields["photo-west"] + general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) + general["empty"] = 0 + else + general["empty"] = 1 + + var/list/medical = list() + data["medical"] = medical + if(istype(active2, /datum/data/record) && data_core.medical.Find(active2)) + var/list/fields = list() + medical["fields"] = fields + fields[++fields.len] = MED_FIELD("Gender identity", active2.fields["id_gender"], "id_gender", TRUE) + fields[++fields.len] = MED_FIELD("Blood Type", active2.fields["b_type"], "blood_type", FALSE) + fields[++fields.len] = MED_FIELD("Blood Basis", active2.fields["blood_reagent"], "blood_reagent", FALSE) + fields[++fields.len] = MED_FIELD("DNA", active2.fields["b_dna"], "b_dna", TRUE) + fields[++fields.len] = MED_FIELD("Brain Type", active2.fields["brain_type"], "brain_type", TRUE) + fields[++fields.len] = MED_FIELD("Important Notes", active2.fields["notes"], "notes", TRUE) + if(!active2.fields["comments"] || !islist(active2.fields["comments"])) + active2.fields["comments"] = list() + medical["comments"] = active2.fields["comments"] + medical["empty"] = 0 + else + medical["empty"] = 1 + if(MED_DATA_V_DATA) + data["virus"] = list() + for(var/ID in virusDB) + var/datum/data/record/v = virusDB[ID] + data["virus"] += list(list("name" = v.fields["name"], "D" = "\ref[v]")) + if(MED_DATA_MEDBOT) + data["medbots"] = list() + for(var/mob/living/bot/medbot/M in mob_list) + if(M.z != z) + continue + var/turf/T = get_turf(M) + if(T) + var/medbot = list() + var/area/A = get_area(T) + medbot["name"] = M.name + medbot["area"] = A.name + medbot["x"] = T.x + medbot["y"] = T.y + medbot["on"] = M.on + if(!isnull(M.reagent_glass) && M.use_beaker) + medbot["use_beaker"] = 1 + medbot["total_volume"] = M.reagent_glass.reagents.total_volume + medbot["maximum_volume"] = M.reagent_glass.reagents.maximum_volume + else + medbot["use_beaker"] = 0 + data["medbots"] += list(medbot) + + data["modal"] = tgui_modal_data(src) + return data + +/obj/machinery/computer/med_data/tgui_act(action, params) + if(..()) + return TRUE + + if(!data_core.general.Find(active1)) + active1 = null + if(!data_core.medical.Find(active2)) + active2 = null + + . = TRUE + if(tgui_act_modal(action, params)) + return + + switch(action) + if("cleartemp") + temp = null + if("scan") + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/weapon/card/id)) + usr.drop_item() + I.forceMove(src) + scan = I + if("login") + var/login_type = text2num(params["login_type"]) + if(login_type == LOGIN_TYPE_NORMAL && istype(scan)) + if(check_access(scan)) + authenticated = scan.registered_name + rank = scan.assignment + else if(login_type == LOGIN_TYPE_AI && isAI(usr)) + authenticated = usr.name + rank = "AI" + else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr)) + authenticated = usr.name + var/mob/living/silicon/robot/R = usr + rank = "[R.modtype] [R.braintype]" + if(authenticated) + active1 = null + active2 = null + screen = MED_DATA_R_LIST + else + . = FALSE + + if(.) + return + + if(authenticated) + . = TRUE + switch(action) + if("logout") + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + authenticated = null + screen = null + active1 = null + active2 = null + if("screen") + screen = clamp(text2num(params["screen"]) || 0, MED_DATA_R_LIST, MED_DATA_MEDBOT) + active1 = null + active2 = null + if("vir") + var/datum/data/record/v = locate(params["vir"]) + if(!istype(v)) + return FALSE + tgui_modal_message(src, "virus", "", null, v.fields["tgui_description"]) + if("del_all") + for(var/datum/data/record/R in data_core.medical) + qdel(R) + set_temp("All medical records deleted.") + if("del_r") + if(active2) + set_temp("Medical record deleted.") + qdel(active2) + if("d_rec") + var/datum/data/record/general_record = locate(params["d_rec"] || "") + if(!data_core.general.Find(general_record)) + set_temp("Record not found.", "danger") + return + + var/datum/data/record/medical_record + for(var/datum/data/record/M in data_core.medical) + if(M.fields["name"] == general_record.fields["name"] && M.fields["id"] == general_record.fields["id"]) + medical_record = M + break + + active1 = general_record + active2 = medical_record + screen = MED_DATA_RECORD + if("new") + if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) + var/datum/data/record/R = new /datum/data/record() + R.fields["name"] = active1.fields["name"] + R.fields["id"] = active1.fields["id"] + R.name = "Medical Record #[R.fields["id"]]" + R.fields["b_type"] = "Unknown" + R.fields["blood_reagent"] = "Unknown" + R.fields["b_dna"] = "Unknown" + R.fields["mi_dis"] = "None" + R.fields["mi_dis_d"] = "No minor disabilities have been declared." + R.fields["ma_dis"] = "None" + R.fields["ma_dis_d"] = "No major disabilities have been diagnosed." + R.fields["alg"] = "None" + R.fields["alg_d"] = "No allergies have been detected in this patient." + R.fields["cdi"] = "None" + R.fields["cdi_d"] = "No diseases have been diagnosed at the moment." + R.fields["notes"] = "No notes." + data_core.medical += R + active2 = R + screen = MED_DATA_RECORD + set_temp("Medical record created.", "success") + if("del_c") + var/index = text2num(params["del_c"] || "") + if(!index || !istype(active2, /datum/data/record)) + return + + var/list/comments = active2.fields["comments"] + index = clamp(index, 1, length(comments)) + if(comments[index]) + comments.Cut(index, index + 1) + if("search") + active1 = null + active2 = null + var/t1 = lowertext(params["t1"] || "") + if(!length(t1)) + return + + for(var/datum/data/record/R in data_core.medical) + if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"])) + active2 = R + break + if(!active2) + set_temp("Medical record not found. You must enter the person's exact name, ID or DNA.", "danger") + return + for(var/datum/data/record/E in data_core.general) + if(E.fields["name"] == active2.fields["name"] && E.fields["id"] == active2.fields["id"]) + active1 = E + break + screen = MED_DATA_RECORD + if("print_p") + if(!printing) + printing = TRUE + // playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) + SStgui.update_uis(src) + addtimer(CALLBACK(src, PROC_REF(print_finish)), 5 SECONDS) + else + return FALSE + +/** + * Called in tgui_act() to process modal actions + * + * Arguments: + * * action - The action passed by tgui + * * params - The params passed by tgui + */ +/obj/machinery/computer/med_data/proc/tgui_act_modal(action, params) + . = TRUE + var/id = params["id"] // The modal's ID + var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] + switch(tgui_modal_act(src, action, params)) + if(TGUI_MODAL_OPEN) + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/question = field_edit_questions[field] + var/choices = field_edit_choices[field] + if(length(choices)) + tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices) + else + tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"]) + if("add_c") + tgui_modal_input(src, id, "Please enter your message:") + else + return FALSE + if(TGUI_MODAL_ANSWER) + var/answer = params["answer"] + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/list/choices = field_edit_choices[field] + if(length(choices) && !(answer in choices)) + return + + if(field == "age") + answer = text2num(answer) + + if(istype(active2) && (field in active2.fields)) + active2.fields[field] = answer + else if(istype(active1) && (field in active1.fields)) + active1.fields[field] = answer + if("add_c") + if(!length(answer) || !istype(active2) || !length(authenticated)) + return + active2.fields["comments"] += list(list( + header = "Made by [authenticated] ([rank]) at [worldtime2stationtime(world.time)]", + text = answer + )) + else + return FALSE + else + return FALSE + + +/** + * Called when the print timer finishes + */ +/obj/machinery/computer/med_data/proc/print_finish() + var/obj/item/weapon/paper/P = new(loc) + P.info = "
    Medical Record

    " + if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) + P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] +
    \nSex: [active1.fields["sex"]] +
    \nSpecies: [active1.fields["species"]] +
    \nAge: [active1.fields["age"]] +
    \nFingerprint: [active1.fields["fingerprint"]] +
    \nPhysical Status: [active1.fields["p_stat"]] +
    \nMental Status: [active1.fields["m_stat"]]
    "} + else + P.info += "General Record Lost!
    " + if(istype(active2, /datum/data/record) && data_core.medical.Find(active2)) + P.info += {"
    \n
    Medical Data
    +
    \nGender Identity: [active2.fields["id_gender"]] +
    \nBlood Type: [active2.fields["b_type"]] +
    \nBlood Basis: [active2.fields["blood_reagent"]] +
    \nDNA: [active2.fields["b_dna"]]
    \n +
    \nMinor Disabilities: [active2.fields["mi_dis"]] +
    \nDetails: [active2.fields["mi_dis_d"]]
    \n +
    \nMajor Disabilities: [active2.fields["ma_dis"]] +
    \nDetails: [active2.fields["ma_dis_d"]]
    \n +
    \nAllergies: [active2.fields["alg"]] +
    \nDetails: [active2.fields["alg_d"]]
    \n +
    \nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section) +
    \nDetails: [active2.fields["cdi_d"]]
    \n +
    \nImportant Notes: +
    \n\t[active2.fields["notes"]]
    \n +
    \n +
    Comments/Log

    "} + for(var/c in active2.fields["comments"]) + P.info += "[c["header"]]
    [c["text"]]
    " + else + P.info += "Medical Record Lost!
    " + P.info += "" + P.name = "paper - 'Medical Record: [active1.fields["name"]]'" + printing = FALSE + SStgui.update_uis(src) + +/** + * Sets a temporary message to display to the user + * + * Arguments: + * * text - Text to display, null/empty to clear the message from the UI + * * style - The style of the message: (color name), info, success, warning, danger, virus + */ +/obj/machinery/computer/med_data/proc/set_temp(text = "", style = "info", update_now = FALSE) + temp = list(text = text, style = style) + if(update_now) + SStgui.update_uis(src) + +/obj/machinery/computer/med_data/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + for(var/datum/data/record/R in data_core.medical) + if(prob(10/severity)) + switch(rand(1,6)) + if(1) + R.fields["name"] = "[pick(pick(first_names_male), pick(first_names_female))] [pick(last_names)]" + if(2) + R.fields["sex"] = pick("Male", "Female") + if(3) + R.fields["age"] = rand(5, 85) + if(4) + R.fields["b_type"] = pick("A-", "B-", "AB-", "O-", "A+", "B+", "AB+", "O+") + if(5) + R.fields["p_stat"] = pick("*SSD*", "Active", "Physically Unfit", "Disabled") + if(PDA_Manifest.len) + PDA_Manifest.Cut() + if(6) + R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") + continue + + else if(prob(1)) + qdel(R) + continue + + ..(severity) + + +/obj/machinery/computer/med_data/laptop //[TO DO] Change name to PCU and update mapdata to include replacement computers + name = "\improper Medical Laptop" + desc = "A personal computer unit. It seems to have only the medical records program installed." + icon_screen = "pcu_generic" + icon_state = "pcu_med" + icon_keyboard = "pcu_key" + light_color = "#5284e7" + circuit = /obj/item/weapon/circuitboard/med_data/pcu + density = FALSE + +#undef FIELD +#undef MED_FIELD diff --git a/code/game/machinery/computer/prisonshuttle.dm b/code/game/machinery/computer/prisonshuttle.dm index 1a81f9db919..2c6b4c7ced3 100644 --- a/code/game/machinery/computer/prisonshuttle.dm +++ b/code/game/machinery/computer/prisonshuttle.dm @@ -1,215 +1,215 @@ -//Config stuff -#define PRISON_MOVETIME 150 //Time to station is milliseconds. -#define PRISON_STATION_AREATYPE "/area/shuttle/prison/station" //Type of the prison shuttle area for station -#define PRISON_DOCK_AREATYPE "/area/shuttle/prison/prison" //Type of the prison shuttle area for dock - -var/prison_shuttle_moving_to_station = 0 -var/prison_shuttle_moving_to_prison = 0 -var/prison_shuttle_at_station = 0 -var/prison_shuttle_can_send = 1 -var/prison_shuttle_time = 0 -var/prison_shuttle_timeleft = 0 - -/obj/machinery/computer/prison_shuttle - name = "prison shuttle control console" - desc = "Used to move the prison shuttle to and from its destination." - icon_keyboard = "security_key" - icon_screen = "syndishuttle" - light_color = "#00ffff" - req_access = list(access_security) - circuit = /obj/item/weapon/circuitboard/prison_shuttle - var/temp = null - var/hacked = 0 - var/allowedtocall = 0 - var/prison_break = 0 - -/obj/machinery/computer/prison_shuttle/attack_ai(var/mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/computer/prison_shuttle/attack_hand(var/mob/user as mob) - if(!src.allowed(user) && (!hacked)) - to_chat(user, "Access Denied.") - return - if(prison_break) - to_chat(user, "Unable to locate shuttle.") - return - if(..()) - return - user.set_machine(src) - post_signal("prison") - var/dat - if (src.temp) - dat = src.temp - else - dat += {"
    Prison Shuttle
    - \nLocation: [prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison ? "Moving to station ([prison_shuttle_timeleft] Secs.)":prison_shuttle_at_station ? "Station":"Dock"]
    - [prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison ? "\n*Shuttle already called*
    \n
    ":prison_shuttle_at_station ? "\nSend to Dock
    \n
    ":"\nSend to station
    \n
    "] - \nClose"} - - user << browse(dat, "window=computer;size=575x450") - onclose(user, "computer") - return - - -/obj/machinery/computer/prison_shuttle/Topic(href, href_list) - if(..()) - return - - if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.set_machine(src) - - if (href_list["sendtodock"]) - if (!prison_can_move()) - to_chat(usr, "The prison shuttle is unable to leave.") - return - if(!prison_shuttle_at_station|| prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return - post_signal("prison") - to_chat(usr, "The prison shuttle has been called and will arrive in [(PRISON_MOVETIME/10)] seconds.") - src.temp += "Shuttle sent.

    OK" - src.updateUsrDialog() - prison_shuttle_moving_to_prison = 1 - prison_shuttle_time = world.timeofday + PRISON_MOVETIME - spawn(0) - prison_process() - - else if (href_list["sendtostation"]) - if (!prison_can_move()) - to_chat(usr, "The prison shuttle is unable to leave.") - return - if(prison_shuttle_at_station || prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return - post_signal("prison") - to_chat(usr, "The prison shuttle has been called and will arrive in [(PRISON_MOVETIME/10)] seconds.") - src.temp += "Shuttle sent.

    OK" - src.updateUsrDialog() - prison_shuttle_moving_to_station = 1 - prison_shuttle_time = world.timeofday + PRISON_MOVETIME - spawn(0) - prison_process() - - else if (href_list["mainmenu"]) - src.temp = null - - src.add_fingerprint(usr) - src.updateUsrDialog() - return - - -/obj/machinery/computer/prison_shuttle/proc/prison_can_move() - if(prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return 0 - else return 1 - - -/obj/machinery/computer/prison_shuttle/proc/prison_break() - switch(prison_break) - if (0) - if(!prison_shuttle_at_station || prison_shuttle_moving_to_prison) return - - prison_shuttle_moving_to_prison = 1 - prison_shuttle_at_station = prison_shuttle_at_station - - if (!prison_shuttle_moving_to_prison || !prison_shuttle_moving_to_station) - prison_shuttle_time = world.timeofday + PRISON_MOVETIME - spawn(0) - prison_process() - prison_break = 1 - if(1) - prison_break = 0 - - -/obj/machinery/computer/prison_shuttle/proc/post_signal(var/command) - var/datum/radio_frequency/frequency = radio_controller.return_frequency(1311) - if(!frequency) return - var/datum/signal/status_signal = new - status_signal.source = src - status_signal.transmission_method = TRANSMISSION_RADIO - status_signal.data["command"] = command - frequency.post_signal(src, status_signal) - return - - -/obj/machinery/computer/prison_shuttle/proc/prison_process() - while(prison_shuttle_time - world.timeofday > 0) - var/ticksleft = prison_shuttle_time - world.timeofday - - if(ticksleft > 1e5) - prison_shuttle_time = world.timeofday + 10 // midnight rollover - - prison_shuttle_timeleft = (ticksleft / 10) - sleep(5) - prison_shuttle_moving_to_station = 0 - prison_shuttle_moving_to_prison = 0 - - switch(prison_shuttle_at_station) - - if(0) - prison_shuttle_at_station = 1 - if (prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return - - if (!prison_can_move()) - to_chat(usr, "The prison shuttle is unable to leave.") - return - - var/area/start_location = locate(/area/shuttle/prison/prison) - var/area/end_location = locate(/area/shuttle/prison/station) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs += T - if(T.y < throwy) - throwy = T.y - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - start_location.move_contents_to(end_location) - - if(1) - prison_shuttle_at_station = 0 - if (prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return - - if (!prison_can_move()) - to_chat(usr, "The prison shuttle is unable to leave.") - return - - var/area/start_location = locate(/area/shuttle/prison/station) - var/area/end_location = locate(/area/shuttle/prison/prison) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs += T - if(T.y < throwy) - throwy = T.y - - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - - for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... - bug.gib() - - for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug... - pest.gib() - - start_location.move_contents_to(end_location) - return - -/obj/machinery/computer/prison_shuttle/emag_act(var/charges, var/mob/user) - if(!hacked) - hacked = 1 - to_chat(user, "You disable the lock.") - return 1 +//Config stuff +#define PRISON_MOVETIME 150 //Time to station is milliseconds. +#define PRISON_STATION_AREATYPE "/area/shuttle/prison/station" //Type of the prison shuttle area for station +#define PRISON_DOCK_AREATYPE "/area/shuttle/prison/prison" //Type of the prison shuttle area for dock + +var/prison_shuttle_moving_to_station = 0 +var/prison_shuttle_moving_to_prison = 0 +var/prison_shuttle_at_station = 0 +var/prison_shuttle_can_send = 1 +var/prison_shuttle_time = 0 +var/prison_shuttle_timeleft = 0 + +/obj/machinery/computer/prison_shuttle + name = "prison shuttle control console" + desc = "Used to move the prison shuttle to and from its destination." + icon_keyboard = "security_key" + icon_screen = "syndishuttle" + light_color = "#00ffff" + req_access = list(access_security) + circuit = /obj/item/weapon/circuitboard/prison_shuttle + var/temp = null + var/hacked = 0 + var/allowedtocall = 0 + var/prison_break = 0 + +/obj/machinery/computer/prison_shuttle/attack_ai(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/computer/prison_shuttle/attack_hand(var/mob/user as mob) + if(!src.allowed(user) && (!hacked)) + to_chat(user, "Access Denied.") + return + if(prison_break) + to_chat(user, "Unable to locate shuttle.") + return + if(..()) + return + user.set_machine(src) + post_signal("prison") + var/dat + if (src.temp) + dat = src.temp + else + dat += {"
    Prison Shuttle
    + \nLocation: [prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison ? "Moving to station ([prison_shuttle_timeleft] Secs.)":prison_shuttle_at_station ? "Station":"Dock"]
    + [prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison ? "\n*Shuttle already called*
    \n
    ":prison_shuttle_at_station ? "\nSend to Dock
    \n
    ":"\nSend to station
    \n
    "] + \nClose"} + + user << browse(dat, "window=computer;size=575x450") + onclose(user, "computer") + return + + +/obj/machinery/computer/prison_shuttle/Topic(href, href_list) + if(..()) + return + + if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + + if (href_list["sendtodock"]) + if (!prison_can_move()) + to_chat(usr, "The prison shuttle is unable to leave.") + return + if(!prison_shuttle_at_station|| prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return + post_signal("prison") + to_chat(usr, "The prison shuttle has been called and will arrive in [(PRISON_MOVETIME/10)] seconds.") + src.temp += "Shuttle sent.

    OK" + src.updateUsrDialog() + prison_shuttle_moving_to_prison = 1 + prison_shuttle_time = world.timeofday + PRISON_MOVETIME + spawn(0) + prison_process() + + else if (href_list["sendtostation"]) + if (!prison_can_move()) + to_chat(usr, "The prison shuttle is unable to leave.") + return + if(prison_shuttle_at_station || prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return + post_signal("prison") + to_chat(usr, "The prison shuttle has been called and will arrive in [(PRISON_MOVETIME/10)] seconds.") + src.temp += "Shuttle sent.

    OK" + src.updateUsrDialog() + prison_shuttle_moving_to_station = 1 + prison_shuttle_time = world.timeofday + PRISON_MOVETIME + spawn(0) + prison_process() + + else if (href_list["mainmenu"]) + src.temp = null + + src.add_fingerprint(usr) + src.updateUsrDialog() + return + + +/obj/machinery/computer/prison_shuttle/proc/prison_can_move() + if(prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return 0 + else return 1 + + +/obj/machinery/computer/prison_shuttle/proc/prison_break() + switch(prison_break) + if (0) + if(!prison_shuttle_at_station || prison_shuttle_moving_to_prison) return + + prison_shuttle_moving_to_prison = 1 + prison_shuttle_at_station = prison_shuttle_at_station + + if (!prison_shuttle_moving_to_prison || !prison_shuttle_moving_to_station) + prison_shuttle_time = world.timeofday + PRISON_MOVETIME + spawn(0) + prison_process() + prison_break = 1 + if(1) + prison_break = 0 + + +/obj/machinery/computer/prison_shuttle/proc/post_signal(var/command) + var/datum/radio_frequency/frequency = radio_controller.return_frequency(1311) + if(!frequency) return + var/datum/signal/status_signal = new + status_signal.source = src + status_signal.transmission_method = TRANSMISSION_RADIO + status_signal.data["command"] = command + frequency.post_signal(src, status_signal) + return + + +/obj/machinery/computer/prison_shuttle/proc/prison_process() + while(prison_shuttle_time - world.timeofday > 0) + var/ticksleft = prison_shuttle_time - world.timeofday + + if(ticksleft > 1e5) + prison_shuttle_time = world.timeofday + 10 // midnight rollover + + prison_shuttle_timeleft = (ticksleft / 10) + sleep(5) + prison_shuttle_moving_to_station = 0 + prison_shuttle_moving_to_prison = 0 + + switch(prison_shuttle_at_station) + + if(0) + prison_shuttle_at_station = 1 + if (prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return + + if (!prison_can_move()) + to_chat(usr, "The prison shuttle is unable to leave.") + return + + var/area/start_location = locate(/area/shuttle/prison/prison) + var/area/end_location = locate(/area/shuttle/prison/station) + + var/list/dstturfs = list() + var/throwy = world.maxy + + for(var/turf/T in end_location) + dstturfs += T + if(T.y < throwy) + throwy = T.y + // hey you, get out of the way! + for(var/turf/T in dstturfs) + // find the turf to move things to + var/turf/D = locate(T.x, throwy - 1, 1) + //var/turf/E = get_step(D, SOUTH) + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + if(istype(T, /turf/simulated)) + qdel(T) + start_location.move_contents_to(end_location) + + if(1) + prison_shuttle_at_station = 0 + if (prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return + + if (!prison_can_move()) + to_chat(usr, "The prison shuttle is unable to leave.") + return + + var/area/start_location = locate(/area/shuttle/prison/station) + var/area/end_location = locate(/area/shuttle/prison/prison) + + var/list/dstturfs = list() + var/throwy = world.maxy + + for(var/turf/T in end_location) + dstturfs += T + if(T.y < throwy) + throwy = T.y + + // hey you, get out of the way! + for(var/turf/T in dstturfs) + // find the turf to move things to + var/turf/D = locate(T.x, throwy - 1, 1) + //var/turf/E = get_step(D, SOUTH) + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + if(istype(T, /turf/simulated)) + qdel(T) + + for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... + bug.gib() + + for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug... + pest.gib() + + start_location.move_contents_to(end_location) + return + +/obj/machinery/computer/prison_shuttle/emag_act(var/charges, var/mob/user) + if(!hacked) + hacked = 1 + to_chat(user, "You disable the lock.") + return 1 diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 7b318b966cf..954c0c0ac44 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -1,230 +1,230 @@ -/obj/machinery/computer/robotics - name = "robotics control console" - desc = "Used to remotely lockdown or detonate linked cyborgs." - icon_keyboard = "tech_key" - icon_screen = "robot" - light_color = "#a97faa" - req_access = list(access_robotics) - circuit = /obj/item/weapon/circuitboard/robotics - var/safety = 1 - -/obj/machinery/computer/robotics/attack_ai(var/mob/user as mob) - tgui_interact(user) - -/obj/machinery/computer/robotics/attack_hand(var/mob/user as mob) - if(..()) - return - if(stat & (NOPOWER|BROKEN)) - return - tgui_interact(user) - -/obj/machinery/computer/robotics/proc/is_authenticated(mob/user) - if(!istype(user)) - return FALSE - if(isobserver(user)) - var/mob/observer/dead/D = user - if(D.can_admin_interact()) - return TRUE - if(allowed(user)) - return TRUE - return FALSE - -/** - * Does this borg show up in the console - * - * Returns TRUE if a robot will show up in the console - * Returns FALSE if a robot will not show up in the console - * Arguments: - * * R - The [mob/living/silicon/robot] to be checked - */ -/obj/machinery/computer/robotics/proc/console_shows(mob/living/silicon/robot/R) - if(!istype(R)) - return FALSE - if(istype(R, /mob/living/silicon/robot/drone)) - return FALSE - if(R.scrambledcodes) - return FALSE - if(!AreConnectedZLevels(get_z(src), get_z(R))) - return FALSE - return TRUE - -/** - * Check if a user can send a lockdown/detonate command to a specific borg - * - * Returns TRUE if a user can send the command (does not guarantee it will work) - * Returns FALSE if a user cannot - * Arguments: - * * user - The [mob/user] to be checked - * * R - The [mob/living/silicon/robot] to be checked - * * telluserwhy - Bool of whether the user should be sent a to_chat message if they don't have access - */ -/obj/machinery/computer/robotics/proc/can_control(mob/user, mob/living/silicon/robot/R, telluserwhy = FALSE) - if(!istype(user)) - return FALSE - if(!console_shows(R)) - return FALSE - if(isAI(user)) - if(R.connected_ai != user) - if(telluserwhy) - to_chat(user, "AIs can only control cyborgs which are linked to them.") - return FALSE - if(isrobot(user)) - if(R != user) - if(telluserwhy) - to_chat(user, "Cyborgs cannot control other cyborgs.") - return FALSE - return TRUE - -/** - * Check if the user is the right kind of entity to be able to hack borgs - * - * Returns TRUE if a user is a traitor AI, or aghost - * Returns FALSE otherwise - * Arguments: - * * user - The [mob/user] to be checked - */ -/obj/machinery/computer/robotics/proc/can_hack_any(mob/user) - if(!istype(user)) - return FALSE - if(isobserver(user)) - var/mob/observer/dead/D = user - if(D.can_admin_interact()) - return TRUE - if(!isAI(user)) - return FALSE - return (user.mind.special_role && user.mind.original == user) - -/** - * Check if the user is allowed to hack a specific borg - * - * Returns TRUE if a user can hack the specific cyborg - * Returns FALSE if a user cannot - * Arguments: - * * user - The [mob/user] to be checked - * * R - The [mob/living/silicon/robot] to be checked - */ -/obj/machinery/computer/robotics/proc/can_hack(mob/user, mob/living/silicon/robot/R) - if(!can_hack_any(user)) - return FALSE - if(!istype(R)) - return FALSE - if(R.emagged) - return FALSE - if(R.connected_ai != user) - return FALSE - return TRUE - - -/obj/machinery/computer/robotics/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "RoboticsControlConsole", name) - ui.open() - -/obj/machinery/computer/robotics/tgui_data(mob/user) - var/list/data = list() - data["auth"] = is_authenticated(user) - data["can_hack"] = can_hack_any(user) - data["cyborgs"] = list() - data["safety"] = safety - for(var/mob/living/silicon/robot/R in mob_list) - if(!console_shows(R)) - continue - var/area/A = get_area(R) - var/turf/T = get_turf(R) - var/list/cyborg_data = list( - name = R.name, - ref = REF(R), - locked_down = R.lockcharge, - locstring = "[A.name] ([T.x], [T.y])", - status = R.stat, - health = round(R.health * 100 / R.maxHealth, 0.1), - charge = R.cell ? round(R.cell.percent()) : null, - cell_capacity = R.cell ? R.cell.maxcharge : null, - module = R.module ? R.module.name : "No Module Detected", - synchronization = R.connected_ai, - is_hacked = R.connected_ai && R.emagged, - hackable = can_hack(user, R), - ) - data["cyborgs"] += list(cyborg_data) - data["show_detonate_all"] = (data["auth"] && length(data["cyborgs"]) > 0 && ishuman(user)) - return data - -/obj/machinery/computer/robotics/tgui_act(action, params) - if(..()) - return - . = FALSE - if(!is_authenticated(usr)) - to_chat(usr, "Access denied.") - return - switch(action) - if("arm") // Arms the emergency self-destruct system - if(issilicon(usr)) - to_chat(usr, "Access Denied (silicon detected)") - return - safety = !safety - to_chat(usr, "You [safety ? "disarm" : "arm"] the emergency self destruct.") - . = TRUE - if("nuke") // Destroys all accessible cyborgs if safety is disabled - if(issilicon(usr)) - to_chat(usr, "Access Denied (silicon detected)") - return - if(safety) - to_chat(usr, "Self-destruct aborted - safety active") - return - message_admins("[key_name_admin(usr)] detonated all cyborgs!") - log_game("\[key_name(usr)] detonated all cyborgs!") - for(var/mob/living/silicon/robot/R in mob_list) - if(istype(R, /mob/living/silicon/robot/drone)) - continue - // Ignore antagonistic cyborgs - if(R.scrambledcodes) - continue - to_chat(R, "Self-destruct command received.") - if(R.connected_ai) - to_chat(R.connected_ai, "

    ALERT - Cyborg detonation detected: [R.name]
    ") - R.self_destruct() - . = TRUE - if("killbot") // destroys one specific cyborg - var/mob/living/silicon/robot/R = locate(params["ref"]) - if(!can_control(usr, R, TRUE)) - return - if(R.mind && R.mind.special_role && R.emagged) - to_chat(R, "Extreme danger! Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") - R.ResetSecurityCodes() - . = TRUE - return - var/turf/T = get_turf(R) - message_admins("[key_name_admin(usr)] detonated [key_name_admin(R)] ([ADMIN_COORDJMP(T)])!") - log_game("\[key_name(usr)] detonated [key_name(R)]!") - to_chat(R, "Self-destruct command received.") - if(R.connected_ai) - to_chat(R.connected_ai, "

    ALERT - Cyborg detonation detected: [R.name]
    ") - R.self_destruct() - . = TRUE - if("stopbot") // lock or unlock the borg - if(isrobot(usr)) - to_chat(usr, "Access Denied.") - return - var/mob/living/silicon/robot/R = locate(params["ref"]) - if(!can_control(usr, R, TRUE)) - return - message_admins("[ADMIN_LOOKUPFLW(usr)] [!R.lockcharge ? "locked down" : "released"] [ADMIN_LOOKUPFLW(R)]!") - log_game("[key_name(usr)] [!R.lockcharge ? "locked down" : "released"] [key_name(R)]!") - R.SetLockdown(!R.lockcharge) - to_chat(R, "[!R.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]") - if(R.connected_ai) - to_chat(R.connected_ai, "[!R.lockcharge ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [R.name]
    ") - . = TRUE - if("hackbot") // AIs hacking/emagging a borg - var/mob/living/silicon/robot/R = locate(params["ref"]) - if(!can_hack(usr, R)) - return - var/choice = tgui_alert(usr, "Really hack [R.name]? This cannot be undone.", "Hack?", list("Yes", "No")) - if(choice == "No") - return - log_game("[key_name(usr)] emagged [key_name(R)] using robotic console!") - message_admins("[key_name_admin(usr)] emagged [key_name_admin(R)] using robotic console!") - R.emagged = TRUE - to_chat(R, "Failsafe protocols overridden. New tools available.") - . = TRUE +/obj/machinery/computer/robotics + name = "robotics control console" + desc = "Used to remotely lockdown or detonate linked cyborgs." + icon_keyboard = "tech_key" + icon_screen = "robot" + light_color = "#a97faa" + req_access = list(access_robotics) + circuit = /obj/item/weapon/circuitboard/robotics + var/safety = 1 + +/obj/machinery/computer/robotics/attack_ai(var/mob/user as mob) + tgui_interact(user) + +/obj/machinery/computer/robotics/attack_hand(var/mob/user as mob) + if(..()) + return + if(stat & (NOPOWER|BROKEN)) + return + tgui_interact(user) + +/obj/machinery/computer/robotics/proc/is_authenticated(mob/user) + if(!istype(user)) + return FALSE + if(isobserver(user)) + var/mob/observer/dead/D = user + if(D.can_admin_interact()) + return TRUE + if(allowed(user)) + return TRUE + return FALSE + +/** + * Does this borg show up in the console + * + * Returns TRUE if a robot will show up in the console + * Returns FALSE if a robot will not show up in the console + * Arguments: + * * R - The [mob/living/silicon/robot] to be checked + */ +/obj/machinery/computer/robotics/proc/console_shows(mob/living/silicon/robot/R) + if(!istype(R)) + return FALSE + if(istype(R, /mob/living/silicon/robot/drone)) + return FALSE + if(R.scrambledcodes) + return FALSE + if(!AreConnectedZLevels(get_z(src), get_z(R))) + return FALSE + return TRUE + +/** + * Check if a user can send a lockdown/detonate command to a specific borg + * + * Returns TRUE if a user can send the command (does not guarantee it will work) + * Returns FALSE if a user cannot + * Arguments: + * * user - The [mob/user] to be checked + * * R - The [mob/living/silicon/robot] to be checked + * * telluserwhy - Bool of whether the user should be sent a to_chat message if they don't have access + */ +/obj/machinery/computer/robotics/proc/can_control(mob/user, mob/living/silicon/robot/R, telluserwhy = FALSE) + if(!istype(user)) + return FALSE + if(!console_shows(R)) + return FALSE + if(isAI(user)) + if(R.connected_ai != user) + if(telluserwhy) + to_chat(user, "AIs can only control cyborgs which are linked to them.") + return FALSE + if(isrobot(user)) + if(R != user) + if(telluserwhy) + to_chat(user, "Cyborgs cannot control other cyborgs.") + return FALSE + return TRUE + +/** + * Check if the user is the right kind of entity to be able to hack borgs + * + * Returns TRUE if a user is a traitor AI, or aghost + * Returns FALSE otherwise + * Arguments: + * * user - The [mob/user] to be checked + */ +/obj/machinery/computer/robotics/proc/can_hack_any(mob/user) + if(!istype(user)) + return FALSE + if(isobserver(user)) + var/mob/observer/dead/D = user + if(D.can_admin_interact()) + return TRUE + if(!isAI(user)) + return FALSE + return (user.mind.special_role && user.mind.original == user) + +/** + * Check if the user is allowed to hack a specific borg + * + * Returns TRUE if a user can hack the specific cyborg + * Returns FALSE if a user cannot + * Arguments: + * * user - The [mob/user] to be checked + * * R - The [mob/living/silicon/robot] to be checked + */ +/obj/machinery/computer/robotics/proc/can_hack(mob/user, mob/living/silicon/robot/R) + if(!can_hack_any(user)) + return FALSE + if(!istype(R)) + return FALSE + if(R.emagged) + return FALSE + if(R.connected_ai != user) + return FALSE + return TRUE + + +/obj/machinery/computer/robotics/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "RoboticsControlConsole", name) + ui.open() + +/obj/machinery/computer/robotics/tgui_data(mob/user) + var/list/data = list() + data["auth"] = is_authenticated(user) + data["can_hack"] = can_hack_any(user) + data["cyborgs"] = list() + data["safety"] = safety + for(var/mob/living/silicon/robot/R in mob_list) + if(!console_shows(R)) + continue + var/area/A = get_area(R) + var/turf/T = get_turf(R) + var/list/cyborg_data = list( + name = R.name, + ref = REF(R), + locked_down = R.lockcharge, + locstring = "[A.name] ([T.x], [T.y])", + status = R.stat, + health = round(R.health * 100 / R.maxHealth, 0.1), + charge = R.cell ? round(R.cell.percent()) : null, + cell_capacity = R.cell ? R.cell.maxcharge : null, + module = R.module ? R.module.name : "No Module Detected", + synchronization = R.connected_ai, + is_hacked = R.connected_ai && R.emagged, + hackable = can_hack(user, R), + ) + data["cyborgs"] += list(cyborg_data) + data["show_detonate_all"] = (data["auth"] && length(data["cyborgs"]) > 0 && ishuman(user)) + return data + +/obj/machinery/computer/robotics/tgui_act(action, params) + if(..()) + return + . = FALSE + if(!is_authenticated(usr)) + to_chat(usr, "Access denied.") + return + switch(action) + if("arm") // Arms the emergency self-destruct system + if(issilicon(usr)) + to_chat(usr, "Access Denied (silicon detected)") + return + safety = !safety + to_chat(usr, "You [safety ? "disarm" : "arm"] the emergency self destruct.") + . = TRUE + if("nuke") // Destroys all accessible cyborgs if safety is disabled + if(issilicon(usr)) + to_chat(usr, "Access Denied (silicon detected)") + return + if(safety) + to_chat(usr, "Self-destruct aborted - safety active") + return + message_admins("[key_name_admin(usr)] detonated all cyborgs!") + log_game("\[key_name(usr)] detonated all cyborgs!") + for(var/mob/living/silicon/robot/R in mob_list) + if(istype(R, /mob/living/silicon/robot/drone)) + continue + // Ignore antagonistic cyborgs + if(R.scrambledcodes) + continue + to_chat(R, "Self-destruct command received.") + if(R.connected_ai) + to_chat(R.connected_ai, "

    ALERT - Cyborg detonation detected: [R.name]
    ") + R.self_destruct() + . = TRUE + if("killbot") // destroys one specific cyborg + var/mob/living/silicon/robot/R = locate(params["ref"]) + if(!can_control(usr, R, TRUE)) + return + if(R.mind && R.mind.special_role && R.emagged) + to_chat(R, "Extreme danger! Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") + R.ResetSecurityCodes() + . = TRUE + return + var/turf/T = get_turf(R) + message_admins("[key_name_admin(usr)] detonated [key_name_admin(R)] ([ADMIN_COORDJMP(T)])!") + log_game("\[key_name(usr)] detonated [key_name(R)]!") + to_chat(R, "Self-destruct command received.") + if(R.connected_ai) + to_chat(R.connected_ai, "

    ALERT - Cyborg detonation detected: [R.name]
    ") + R.self_destruct() + . = TRUE + if("stopbot") // lock or unlock the borg + if(isrobot(usr)) + to_chat(usr, "Access Denied.") + return + var/mob/living/silicon/robot/R = locate(params["ref"]) + if(!can_control(usr, R, TRUE)) + return + message_admins("[ADMIN_LOOKUPFLW(usr)] [!R.lockcharge ? "locked down" : "released"] [ADMIN_LOOKUPFLW(R)]!") + log_game("[key_name(usr)] [!R.lockcharge ? "locked down" : "released"] [key_name(R)]!") + R.SetLockdown(!R.lockcharge) + to_chat(R, "[!R.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]") + if(R.connected_ai) + to_chat(R.connected_ai, "[!R.lockcharge ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [R.name]
    ") + . = TRUE + if("hackbot") // AIs hacking/emagging a borg + var/mob/living/silicon/robot/R = locate(params["ref"]) + if(!can_hack(usr, R)) + return + var/choice = tgui_alert(usr, "Really hack [R.name]? This cannot be undone.", "Hack?", list("Yes", "No")) + if(choice == "No") + return + log_game("[key_name(usr)] emagged [key_name(R)] using robotic console!") + message_admins("[key_name_admin(usr)] emagged [key_name_admin(R)] using robotic console!") + R.emagged = TRUE + to_chat(R, "Failsafe protocols overridden. New tools available.") + . = TRUE diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index fea0fa3950e..e239a6cf06c 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -1,515 +1,515 @@ -#define SEC_DATA_R_LIST 2 // Record list -#define SEC_DATA_MAINT 3 // Records maintenance -#define SEC_DATA_RECORD 4 // Record - -#define FIELD(N, V, E) list(field = N, value = V, edit = E) - -/obj/machinery/computer/secure_data//TODO:SANITY - name = "security records console" - desc = "Used to view, edit and maintain security records" - icon_keyboard = "security_key" - icon_screen = "security" - light_color = "#a91515" - req_one_access = list(access_security, access_forensics_lockers, access_lawyer) - circuit = /obj/item/weapon/circuitboard/secure_data - var/obj/item/weapon/card/id/scan = null - var/authenticated = null - var/rank = null - var/screen = null - var/datum/data/record/active1 = null - var/datum/data/record/active2 = null - var/list/temp = null - var/printing = null - // The below are used to make modal generation more convenient - var/static/list/field_edit_questions - var/static/list/field_edit_choices - -/obj/machinery/computer/secure_data/Initialize() - . = ..() - field_edit_questions = list( - // General - "name" = "Please enter new name:", - "id" = "Please enter new id:", - "sex" = "Please select new sex:", - "species" = "Please input new species:", - "age" = "Please input new age:", - "rank" = "Please enter new rank:", - "fingerprint" = "Please input new fingerprint hash:", - // Security - "brain_type" = "Please select new brain type:", - "criminal" = "Please select new criminal status:", - "mi_crim" = "Please input new minor crime:", - "mi_crim_d" = "Please input minor crime summary.", - "ma_crim" = "Please input new major crime:", - "ma_crim_d" = "Please input new major crime summary.", - "notes" = "Please input new important notes:", - ) - field_edit_choices = list( - // General - "sex" = all_genders_text_list, - // Security - "criminal" = list("*Arrest*", "Incarcerated", "Parolled", "Released", "None"), - ) - -/obj/machinery/computer/secure_data/Destroy() - active1 = null - active2 = null - return ..() - -/obj/machinery/computer/secure_data/verb/eject_id() - set category = "Object" - set name = "Eject ID Card" - set src in oview(1) - - if(!usr || usr.stat || usr.lying) return - - if(scan) - to_chat(usr, "You remove \the [scan] from \the [src].") - scan.loc = get_turf(src) - if(!usr.get_active_hand() && istype(usr,/mob/living/carbon/human)) - usr.put_in_hands(scan) - scan = null - else - to_chat(usr, "There is nothing to remove from the console.") - return - -/obj/machinery/computer/secure_data/attackby(var/obj/item/O, var/mob/user) - if(istype(O, /obj/item/weapon/card/id) && !scan && user.unEquip(O)) - O.loc = src - scan = O - to_chat(user, "You insert \the [O].") - tgui_interact(user) - else - ..() - -/obj/machinery/computer/secure_data/attack_ai(mob/user as mob) - return attack_hand(user) - -//Someone needs to break down the dat += into chunks instead of long ass lines. -/obj/machinery/computer/secure_data/attack_hand(mob/user as mob) - if(..()) - return - add_fingerprint(user) - tgui_interact(user) - -/obj/machinery/computer/secure_data/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "SecurityRecords", "Security Records") // 800, 380 - ui.open() - ui.set_autoupdate(FALSE) - - -/obj/machinery/computer/secure_data/tgui_data(mob/user) - var/data[0] - data["temp"] = temp - data["scan"] = scan ? scan.name : null - data["authenticated"] = authenticated - data["rank"] = rank - data["screen"] = screen - data["printing"] = printing - data["isAI"] = isAI(user) - data["isRobot"] = isrobot(user) - if(authenticated) - switch(screen) - if(SEC_DATA_R_LIST) - if(!isnull(data_core.general)) - var/list/records = list() - data["records"] = records - for(var/datum/data/record/R in sortRecord(data_core.general)) - var/color = null - var/criminal = "None" - for(var/datum/data/record/M in data_core.security) - if(M.fields["name"] == R.fields["name"] && M.fields["id"] == R.fields["id"]) - switch(M.fields["criminal"]) - if("*Arrest*") - color = "bad" - if("Incarcerated") - color = "brown" - if("Parolled", "Released") - color = "average" - if("None") - color = "good" - criminal = M.fields["criminal"] - break - records[++records.len] = list( - "ref" = "\ref[R]", - "id" = R.fields["id"], - "name" = R.fields["name"], - "color" = color, - "criminal" = criminal - ) - if(SEC_DATA_RECORD) - var/list/general = list() - data["general"] = general - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - var/list/fields = list() - general["fields"] = fields - fields[++fields.len] = FIELD("Name", active1.fields["name"], "name") - fields[++fields.len] = FIELD("ID", active1.fields["id"], "id") - fields[++fields.len] = FIELD("Entity Classification", active1.fields["brain_type"], "brain_type") - fields[++fields.len] = FIELD("Sex", active1.fields["sex"], "sex") - fields[++fields.len] = FIELD("Species", active1.fields["species"], "species") - fields[++fields.len] = FIELD("Age", "[active1.fields["age"]]", "age") - fields[++fields.len] = FIELD("Rank", active1.fields["rank"], "rank") - fields[++fields.len] = FIELD("Fingerprint", active1.fields["fingerprint"], "fingerprint") - fields[++fields.len] = FIELD("Physical Status", active1.fields["p_stat"], null) - fields[++fields.len] = FIELD("Mental Status", active1.fields["m_stat"], null) - var/list/photos = list() - general["photos"] = photos - photos[++photos.len] = active1.fields["photo-south"] - photos[++photos.len] = active1.fields["photo-west"] - general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) - general["empty"] = 0 - else - general["empty"] = 1 - - var/list/security = list() - data["security"] = security - if(istype(active2, /datum/data/record) && data_core.security.Find(active2)) - var/list/fields = list() - security["fields"] = fields - fields[++fields.len] = FIELD("Criminal Status", active2.fields["criminal"], "criminal") - fields[++fields.len] = FIELD("Minor Crimes", active2.fields["mi_crim"], "mi_crim") - fields[++fields.len] = FIELD("Details", active2.fields["mi_crim_d"], "mi_crim_d") - fields[++fields.len] = FIELD("Major Crimes", active2.fields["ma_crim"], "ma_crim") - fields[++fields.len] = FIELD("Details", active2.fields["ma_crim_d"], "ma_crim_d") - fields[++fields.len] = FIELD("Important Notes", active2.fields["notes"], "notes") - if(!active2.fields["comments"] || !islist(active2.fields["comments"])) - active2.fields["comments"] = list() - security["comments"] = active2.fields["comments"] - security["empty"] = 0 - else - security["empty"] = 1 - - data["modal"] = tgui_modal_data(src) - return data - -/obj/machinery/computer/secure_data/tgui_act(action, params) - if(..()) - return TRUE - - if(!data_core.general.Find(active1)) - active1 = null - if(!data_core.security.Find(active2)) - active2 = null - - . = TRUE - if(tgui_act_modal(action, params)) - return - - switch(action) - if("cleartemp") - temp = null - if("scan") - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/weapon/card/id)) - usr.drop_item() - I.forceMove(src) - scan = I - if("login") - var/login_type = text2num(params["login_type"]) - if(login_type == LOGIN_TYPE_NORMAL && istype(scan)) - if(check_access(scan)) - authenticated = scan.registered_name - rank = scan.assignment - else if(login_type == LOGIN_TYPE_AI && isAI(usr)) - authenticated = usr.name - rank = "AI" - else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr)) - authenticated = usr.name - var/mob/living/silicon/robot/R = usr - rank = "[R.modtype] [R.braintype]" - if(authenticated) - active1 = null - active2 = null - screen = SEC_DATA_R_LIST - else - . = FALSE - - if(.) - return - - if(authenticated) - . = TRUE - switch(action) - if("logout") - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null - authenticated = null - screen = null - active1 = null - active2 = null - if("screen") - screen = clamp(text2num(params["screen"]) || 0, SEC_DATA_R_LIST, SEC_DATA_RECORD) - active1 = null - active2 = null - if("del_all") - for(var/datum/data/record/R in data_core.security) - qdel(R) - set_temp("All security records deleted.") - if("del_r") - if(active2) - set_temp("Security record deleted.") - qdel(active2) - if("del_r_2") - set_temp("All records for [active1.fields["name"]] deleted.") - if(active1) - for(var/datum/data/record/R in data_core.medical) - if((R.fields["name"] == active1.fields["name"] || R.fields["id"] == active1.fields["id"])) - qdel(R) - qdel(active1) - if(active2) - qdel(active2) - if("d_rec") - var/datum/data/record/general_record = locate(params["d_rec"] || "") - if(!data_core.general.Find(general_record)) - set_temp("Record not found.", "danger") - return - - var/datum/data/record/security_record - for(var/datum/data/record/M in data_core.security) - if(M.fields["name"] == general_record.fields["name"] && M.fields["id"] == general_record.fields["id"]) - security_record = M - break - - active1 = general_record - active2 = security_record - screen = SEC_DATA_RECORD - if("new") - if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) - var/datum/data/record/R = new /datum/data/record() - R.fields["name"] = active1.fields["name"] - R.fields["id"] = active1.fields["id"] - R.name = "Security Record #[R.fields["id"]]" - R.fields["brain_type"] = "Unknown" - R.fields["criminal"] = "None" - R.fields["mi_crim"] = "None" - R.fields["mi_crim_d"] = "No minor crime convictions." - R.fields["ma_crim"] = "None" - R.fields["ma_crim_d"] = "No major crime convictions." - R.fields["notes"] = "No notes." - R.fields["notes"] = "No notes." - data_core.security += R - active2 = R - screen = SEC_DATA_RECORD - set_temp("Security record created.", "success") - if("del_c") - var/index = text2num(params["del_c"] || "") - if(!index || !istype(active2, /datum/data/record)) - return - - var/list/comments = active2.fields["comments"] - index = clamp(index, 1, length(comments)) - if(comments[index]) - comments.Cut(index, index + 1) - if("search") - active1 = null - active2 = null - var/t1 = lowertext(params["t1"] || "") - if(!length(t1)) - return - - for(var/datum/data/record/R in data_core.general) - if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["fingerprint"])) - active1 = R - break - if(!active1) - set_temp("Security record not found. You must enter the person's exact name, ID, or fingerprint.", "danger") - return - for(var/datum/data/record/E in data_core.security) - if(E.fields["name"] == active1.fields["name"] && E.fields["id"] == active1.fields["id"]) - active2 = E - break - screen = SEC_DATA_RECORD - if("print_p") - if(!printing) - printing = TRUE - // playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) - SStgui.update_uis(src) - addtimer(CALLBACK(src, PROC_REF(print_finish)), 5 SECONDS) - if("photo_front") - var/icon/photo = get_photo(usr) - if(photo && active1) - active1.fields["photo_front"] = photo - active1.fields["photo-south"] = "'data:image/png;base64,[icon2base64(photo)]'" - if("photo_side") - var/icon/photo = get_photo(usr) - if(photo && active1) - active1.fields["photo_side"] = photo - active1.fields["photo-west"] = "'data:image/png;base64,[icon2base64(photo)]'" - else - return FALSE - -/** - * Called in tgui_act() to process modal actions - * - * Arguments: - * * action - The action passed by tgui - * * params - The params passed by tgui - */ -/obj/machinery/computer/secure_data/proc/tgui_act_modal(action, params) - . = TRUE - var/id = params["id"] // The modal's ID - var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] - switch(tgui_modal_act(src, action, params)) - if(TGUI_MODAL_OPEN) - switch(id) - if("edit") - var/field = arguments["field"] - if(!length(field) || !field_edit_questions[field]) - return - var/question = field_edit_questions[field] - var/choices = field_edit_choices[field] - if(length(choices)) - tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices) - else - tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"]) - if("add_c") - tgui_modal_input(src, id, "Please enter your message:") - else - return FALSE - if(TGUI_MODAL_ANSWER) - var/answer = params["answer"] - switch(id) - if("edit") - var/field = arguments["field"] - if(!length(field) || !field_edit_questions[field]) - return - var/list/choices = field_edit_choices[field] - if(length(choices) && !(answer in choices)) - return - - if(field == "age") - answer = text2num(answer) - - if(field == "rank") - if(answer in joblist) - active1.fields["real_rank"] = answer - - if(field == "criminal") - for(var/mob/living/carbon/human/H in player_list) - BITSET(H.hud_updateflag, WANTED_HUD) - - if(istype(active2) && (field in active2.fields)) - active2.fields[field] = answer - if(istype(active1) && (field in active1.fields)) - active1.fields[field] = answer - if("add_c") - if(!length(answer) || !istype(active2) || !length(authenticated)) - return - active2.fields["comments"] += list(list( - header = "Made by [authenticated] ([rank]) at [worldtime2stationtime(world.time)]", - text = answer - )) - else - return FALSE - else - return FALSE - - -/** - * Called when the print timer finishes - */ -/obj/machinery/computer/secure_data/proc/print_finish() - var/obj/item/weapon/paper/P = new(loc) - P.info = "
    Security Record

    " - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] -
    \nSex: [active1.fields["sex"]] -
    \nSpecies: [active1.fields["species"]] -
    \nAge: [active1.fields["age"]] -
    \nFingerprint: [active1.fields["fingerprint"]] -
    \nPhysical Status: [active1.fields["p_stat"]] -
    \nMental Status: [active1.fields["m_stat"]]
    "} - else - P.info += "General Record Lost!
    " - if(istype(active2, /datum/data/record) && data_core.security.Find(active2)) - P.info += {"
    \n
    Security Data
    -
    \nCriminal Status: [active2.fields["criminal"]]
    \n -
    \nMinor Crimes: [active2.fields["mi_crim"]] -
    \nDetails: [active2.fields["mi_crim_d"]]
    \n -
    \nMajor Crimes: [active2.fields["ma_crim"]] -
    \nDetails: [active2.fields["ma_crim_d"]]
    \n -
    \nImportant Notes: -
    \n\t[active2.fields["notes"]]
    \n -
    \n -
    Comments/Log

    "} - for(var/c in active2.fields["comments"]) - P.info += "[c["header"]]
    [c["text"]]
    " - else - P.info += "Security Record Lost!
    " - P.info += "" - P.name = "paper - 'Security Record: [active1.fields["name"]]'" - printing = FALSE - SStgui.update_uis(src) - - -/** - * Sets a temporary message to display to the user - * - * Arguments: - * * text - Text to display, null/empty to clear the message from the UI - * * style - The style of the message: (color name), info, success, warning, danger, virus - */ -/obj/machinery/computer/secure_data/proc/set_temp(text = "", style = "info", update_now = FALSE) - temp = list(text = text, style = style) - if(update_now) - SStgui.update_uis(src) - -/obj/machinery/computer/secure_data/proc/is_not_allowed(var/mob/user) - return !src.authenticated || user.stat || user.restrained() || (!in_range(src, user) && (!istype(user, /mob/living/silicon))) - -/obj/machinery/computer/secure_data/proc/get_photo(var/mob/user) - if(istype(user.get_active_hand(), /obj/item/weapon/photo)) - var/obj/item/weapon/photo/photo = user.get_active_hand() - return photo.img - if(istype(user, /mob/living/silicon)) - var/mob/living/silicon/tempAI = usr - var/obj/item/weapon/photo/selection = tempAI.GetPicture() - if (selection) - return selection.img - -/obj/machinery/computer/secure_data/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - for(var/datum/data/record/R in data_core.security) - if(prob(10/severity)) - switch(rand(1,6)) - if(1) - R.fields["name"] = "[pick(pick(first_names_male), pick(first_names_female))] [pick(last_names)]" - if(2) - R.fields["sex"] = pick("Male", "Female") - if(3) - R.fields["age"] = rand(5, 85) - if(4) - R.fields["criminal"] = pick("None", "*Arrest*", "Incarcerated", "Parolled", "Released") - if(5) - R.fields["p_stat"] = pick("*Unconcious*", "Active", "Physically Unfit") - if(PDA_Manifest.len) - PDA_Manifest.Cut() - if(6) - R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") - continue - - else if(prob(1)) - qdel(R) - continue - - ..(severity) - -/obj/machinery/computer/secure_data/detective_computer - icon_state = "messyfiles" - -#undef FIELD +#define SEC_DATA_R_LIST 2 // Record list +#define SEC_DATA_MAINT 3 // Records maintenance +#define SEC_DATA_RECORD 4 // Record + +#define FIELD(N, V, E) list(field = N, value = V, edit = E) + +/obj/machinery/computer/secure_data//TODO:SANITY + name = "security records console" + desc = "Used to view, edit and maintain security records" + icon_keyboard = "security_key" + icon_screen = "security" + light_color = "#a91515" + req_one_access = list(access_security, access_forensics_lockers, access_lawyer) + circuit = /obj/item/weapon/circuitboard/secure_data + var/obj/item/weapon/card/id/scan = null + var/authenticated = null + var/rank = null + var/screen = null + var/datum/data/record/active1 = null + var/datum/data/record/active2 = null + var/list/temp = null + var/printing = null + // The below are used to make modal generation more convenient + var/static/list/field_edit_questions + var/static/list/field_edit_choices + +/obj/machinery/computer/secure_data/Initialize() + . = ..() + field_edit_questions = list( + // General + "name" = "Please enter new name:", + "id" = "Please enter new id:", + "sex" = "Please select new sex:", + "species" = "Please input new species:", + "age" = "Please input new age:", + "rank" = "Please enter new rank:", + "fingerprint" = "Please input new fingerprint hash:", + // Security + "brain_type" = "Please select new brain type:", + "criminal" = "Please select new criminal status:", + "mi_crim" = "Please input new minor crime:", + "mi_crim_d" = "Please input minor crime summary.", + "ma_crim" = "Please input new major crime:", + "ma_crim_d" = "Please input new major crime summary.", + "notes" = "Please input new important notes:", + ) + field_edit_choices = list( + // General + "sex" = all_genders_text_list, + // Security + "criminal" = list("*Arrest*", "Incarcerated", "Parolled", "Released", "None"), + ) + +/obj/machinery/computer/secure_data/Destroy() + active1 = null + active2 = null + return ..() + +/obj/machinery/computer/secure_data/verb/eject_id() + set category = "Object" + set name = "Eject ID Card" + set src in oview(1) + + if(!usr || usr.stat || usr.lying) return + + if(scan) + to_chat(usr, "You remove \the [scan] from \the [src].") + scan.loc = get_turf(src) + if(!usr.get_active_hand() && istype(usr,/mob/living/carbon/human)) + usr.put_in_hands(scan) + scan = null + else + to_chat(usr, "There is nothing to remove from the console.") + return + +/obj/machinery/computer/secure_data/attackby(var/obj/item/O, var/mob/user) + if(istype(O, /obj/item/weapon/card/id) && !scan && user.unEquip(O)) + O.loc = src + scan = O + to_chat(user, "You insert \the [O].") + tgui_interact(user) + else + ..() + +/obj/machinery/computer/secure_data/attack_ai(mob/user as mob) + return attack_hand(user) + +//Someone needs to break down the dat += into chunks instead of long ass lines. +/obj/machinery/computer/secure_data/attack_hand(mob/user as mob) + if(..()) + return + add_fingerprint(user) + tgui_interact(user) + +/obj/machinery/computer/secure_data/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "SecurityRecords", "Security Records") // 800, 380 + ui.open() + ui.set_autoupdate(FALSE) + + +/obj/machinery/computer/secure_data/tgui_data(mob/user) + var/data[0] + data["temp"] = temp + data["scan"] = scan ? scan.name : null + data["authenticated"] = authenticated + data["rank"] = rank + data["screen"] = screen + data["printing"] = printing + data["isAI"] = isAI(user) + data["isRobot"] = isrobot(user) + if(authenticated) + switch(screen) + if(SEC_DATA_R_LIST) + if(!isnull(data_core.general)) + var/list/records = list() + data["records"] = records + for(var/datum/data/record/R in sortRecord(data_core.general)) + var/color = null + var/criminal = "None" + for(var/datum/data/record/M in data_core.security) + if(M.fields["name"] == R.fields["name"] && M.fields["id"] == R.fields["id"]) + switch(M.fields["criminal"]) + if("*Arrest*") + color = "bad" + if("Incarcerated") + color = "brown" + if("Parolled", "Released") + color = "average" + if("None") + color = "good" + criminal = M.fields["criminal"] + break + records[++records.len] = list( + "ref" = "\ref[R]", + "id" = R.fields["id"], + "name" = R.fields["name"], + "color" = color, + "criminal" = criminal + ) + if(SEC_DATA_RECORD) + var/list/general = list() + data["general"] = general + if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) + var/list/fields = list() + general["fields"] = fields + fields[++fields.len] = FIELD("Name", active1.fields["name"], "name") + fields[++fields.len] = FIELD("ID", active1.fields["id"], "id") + fields[++fields.len] = FIELD("Entity Classification", active1.fields["brain_type"], "brain_type") + fields[++fields.len] = FIELD("Sex", active1.fields["sex"], "sex") + fields[++fields.len] = FIELD("Species", active1.fields["species"], "species") + fields[++fields.len] = FIELD("Age", "[active1.fields["age"]]", "age") + fields[++fields.len] = FIELD("Rank", active1.fields["rank"], "rank") + fields[++fields.len] = FIELD("Fingerprint", active1.fields["fingerprint"], "fingerprint") + fields[++fields.len] = FIELD("Physical Status", active1.fields["p_stat"], null) + fields[++fields.len] = FIELD("Mental Status", active1.fields["m_stat"], null) + var/list/photos = list() + general["photos"] = photos + photos[++photos.len] = active1.fields["photo-south"] + photos[++photos.len] = active1.fields["photo-west"] + general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) + general["empty"] = 0 + else + general["empty"] = 1 + + var/list/security = list() + data["security"] = security + if(istype(active2, /datum/data/record) && data_core.security.Find(active2)) + var/list/fields = list() + security["fields"] = fields + fields[++fields.len] = FIELD("Criminal Status", active2.fields["criminal"], "criminal") + fields[++fields.len] = FIELD("Minor Crimes", active2.fields["mi_crim"], "mi_crim") + fields[++fields.len] = FIELD("Details", active2.fields["mi_crim_d"], "mi_crim_d") + fields[++fields.len] = FIELD("Major Crimes", active2.fields["ma_crim"], "ma_crim") + fields[++fields.len] = FIELD("Details", active2.fields["ma_crim_d"], "ma_crim_d") + fields[++fields.len] = FIELD("Important Notes", active2.fields["notes"], "notes") + if(!active2.fields["comments"] || !islist(active2.fields["comments"])) + active2.fields["comments"] = list() + security["comments"] = active2.fields["comments"] + security["empty"] = 0 + else + security["empty"] = 1 + + data["modal"] = tgui_modal_data(src) + return data + +/obj/machinery/computer/secure_data/tgui_act(action, params) + if(..()) + return TRUE + + if(!data_core.general.Find(active1)) + active1 = null + if(!data_core.security.Find(active2)) + active2 = null + + . = TRUE + if(tgui_act_modal(action, params)) + return + + switch(action) + if("cleartemp") + temp = null + if("scan") + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/weapon/card/id)) + usr.drop_item() + I.forceMove(src) + scan = I + if("login") + var/login_type = text2num(params["login_type"]) + if(login_type == LOGIN_TYPE_NORMAL && istype(scan)) + if(check_access(scan)) + authenticated = scan.registered_name + rank = scan.assignment + else if(login_type == LOGIN_TYPE_AI && isAI(usr)) + authenticated = usr.name + rank = "AI" + else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr)) + authenticated = usr.name + var/mob/living/silicon/robot/R = usr + rank = "[R.modtype] [R.braintype]" + if(authenticated) + active1 = null + active2 = null + screen = SEC_DATA_R_LIST + else + . = FALSE + + if(.) + return + + if(authenticated) + . = TRUE + switch(action) + if("logout") + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + authenticated = null + screen = null + active1 = null + active2 = null + if("screen") + screen = clamp(text2num(params["screen"]) || 0, SEC_DATA_R_LIST, SEC_DATA_RECORD) + active1 = null + active2 = null + if("del_all") + for(var/datum/data/record/R in data_core.security) + qdel(R) + set_temp("All security records deleted.") + if("del_r") + if(active2) + set_temp("Security record deleted.") + qdel(active2) + if("del_r_2") + set_temp("All records for [active1.fields["name"]] deleted.") + if(active1) + for(var/datum/data/record/R in data_core.medical) + if((R.fields["name"] == active1.fields["name"] || R.fields["id"] == active1.fields["id"])) + qdel(R) + qdel(active1) + if(active2) + qdel(active2) + if("d_rec") + var/datum/data/record/general_record = locate(params["d_rec"] || "") + if(!data_core.general.Find(general_record)) + set_temp("Record not found.", "danger") + return + + var/datum/data/record/security_record + for(var/datum/data/record/M in data_core.security) + if(M.fields["name"] == general_record.fields["name"] && M.fields["id"] == general_record.fields["id"]) + security_record = M + break + + active1 = general_record + active2 = security_record + screen = SEC_DATA_RECORD + if("new") + if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) + var/datum/data/record/R = new /datum/data/record() + R.fields["name"] = active1.fields["name"] + R.fields["id"] = active1.fields["id"] + R.name = "Security Record #[R.fields["id"]]" + R.fields["brain_type"] = "Unknown" + R.fields["criminal"] = "None" + R.fields["mi_crim"] = "None" + R.fields["mi_crim_d"] = "No minor crime convictions." + R.fields["ma_crim"] = "None" + R.fields["ma_crim_d"] = "No major crime convictions." + R.fields["notes"] = "No notes." + R.fields["notes"] = "No notes." + data_core.security += R + active2 = R + screen = SEC_DATA_RECORD + set_temp("Security record created.", "success") + if("del_c") + var/index = text2num(params["del_c"] || "") + if(!index || !istype(active2, /datum/data/record)) + return + + var/list/comments = active2.fields["comments"] + index = clamp(index, 1, length(comments)) + if(comments[index]) + comments.Cut(index, index + 1) + if("search") + active1 = null + active2 = null + var/t1 = lowertext(params["t1"] || "") + if(!length(t1)) + return + + for(var/datum/data/record/R in data_core.general) + if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["fingerprint"])) + active1 = R + break + if(!active1) + set_temp("Security record not found. You must enter the person's exact name, ID, or fingerprint.", "danger") + return + for(var/datum/data/record/E in data_core.security) + if(E.fields["name"] == active1.fields["name"] && E.fields["id"] == active1.fields["id"]) + active2 = E + break + screen = SEC_DATA_RECORD + if("print_p") + if(!printing) + printing = TRUE + // playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) + SStgui.update_uis(src) + addtimer(CALLBACK(src, PROC_REF(print_finish)), 5 SECONDS) + if("photo_front") + var/icon/photo = get_photo(usr) + if(photo && active1) + active1.fields["photo_front"] = photo + active1.fields["photo-south"] = "'data:image/png;base64,[icon2base64(photo)]'" + if("photo_side") + var/icon/photo = get_photo(usr) + if(photo && active1) + active1.fields["photo_side"] = photo + active1.fields["photo-west"] = "'data:image/png;base64,[icon2base64(photo)]'" + else + return FALSE + +/** + * Called in tgui_act() to process modal actions + * + * Arguments: + * * action - The action passed by tgui + * * params - The params passed by tgui + */ +/obj/machinery/computer/secure_data/proc/tgui_act_modal(action, params) + . = TRUE + var/id = params["id"] // The modal's ID + var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] + switch(tgui_modal_act(src, action, params)) + if(TGUI_MODAL_OPEN) + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/question = field_edit_questions[field] + var/choices = field_edit_choices[field] + if(length(choices)) + tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices) + else + tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"]) + if("add_c") + tgui_modal_input(src, id, "Please enter your message:") + else + return FALSE + if(TGUI_MODAL_ANSWER) + var/answer = params["answer"] + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/list/choices = field_edit_choices[field] + if(length(choices) && !(answer in choices)) + return + + if(field == "age") + answer = text2num(answer) + + if(field == "rank") + if(answer in joblist) + active1.fields["real_rank"] = answer + + if(field == "criminal") + for(var/mob/living/carbon/human/H in player_list) + BITSET(H.hud_updateflag, WANTED_HUD) + + if(istype(active2) && (field in active2.fields)) + active2.fields[field] = answer + if(istype(active1) && (field in active1.fields)) + active1.fields[field] = answer + if("add_c") + if(!length(answer) || !istype(active2) || !length(authenticated)) + return + active2.fields["comments"] += list(list( + header = "Made by [authenticated] ([rank]) at [worldtime2stationtime(world.time)]", + text = answer + )) + else + return FALSE + else + return FALSE + + +/** + * Called when the print timer finishes + */ +/obj/machinery/computer/secure_data/proc/print_finish() + var/obj/item/weapon/paper/P = new(loc) + P.info = "
    Security Record

    " + if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) + P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] +
    \nSex: [active1.fields["sex"]] +
    \nSpecies: [active1.fields["species"]] +
    \nAge: [active1.fields["age"]] +
    \nFingerprint: [active1.fields["fingerprint"]] +
    \nPhysical Status: [active1.fields["p_stat"]] +
    \nMental Status: [active1.fields["m_stat"]]
    "} + else + P.info += "General Record Lost!
    " + if(istype(active2, /datum/data/record) && data_core.security.Find(active2)) + P.info += {"
    \n
    Security Data
    +
    \nCriminal Status: [active2.fields["criminal"]]
    \n +
    \nMinor Crimes: [active2.fields["mi_crim"]] +
    \nDetails: [active2.fields["mi_crim_d"]]
    \n +
    \nMajor Crimes: [active2.fields["ma_crim"]] +
    \nDetails: [active2.fields["ma_crim_d"]]
    \n +
    \nImportant Notes: +
    \n\t[active2.fields["notes"]]
    \n +
    \n +
    Comments/Log

    "} + for(var/c in active2.fields["comments"]) + P.info += "[c["header"]]
    [c["text"]]
    " + else + P.info += "Security Record Lost!
    " + P.info += "" + P.name = "paper - 'Security Record: [active1.fields["name"]]'" + printing = FALSE + SStgui.update_uis(src) + + +/** + * Sets a temporary message to display to the user + * + * Arguments: + * * text - Text to display, null/empty to clear the message from the UI + * * style - The style of the message: (color name), info, success, warning, danger, virus + */ +/obj/machinery/computer/secure_data/proc/set_temp(text = "", style = "info", update_now = FALSE) + temp = list(text = text, style = style) + if(update_now) + SStgui.update_uis(src) + +/obj/machinery/computer/secure_data/proc/is_not_allowed(var/mob/user) + return !src.authenticated || user.stat || user.restrained() || (!in_range(src, user) && (!istype(user, /mob/living/silicon))) + +/obj/machinery/computer/secure_data/proc/get_photo(var/mob/user) + if(istype(user.get_active_hand(), /obj/item/weapon/photo)) + var/obj/item/weapon/photo/photo = user.get_active_hand() + return photo.img + if(istype(user, /mob/living/silicon)) + var/mob/living/silicon/tempAI = usr + var/obj/item/weapon/photo/selection = tempAI.GetPicture() + if (selection) + return selection.img + +/obj/machinery/computer/secure_data/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + for(var/datum/data/record/R in data_core.security) + if(prob(10/severity)) + switch(rand(1,6)) + if(1) + R.fields["name"] = "[pick(pick(first_names_male), pick(first_names_female))] [pick(last_names)]" + if(2) + R.fields["sex"] = pick("Male", "Female") + if(3) + R.fields["age"] = rand(5, 85) + if(4) + R.fields["criminal"] = pick("None", "*Arrest*", "Incarcerated", "Parolled", "Released") + if(5) + R.fields["p_stat"] = pick("*Unconcious*", "Active", "Physically Unfit") + if(PDA_Manifest.len) + PDA_Manifest.Cut() + if(6) + R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") + continue + + else if(prob(1)) + qdel(R) + continue + + ..(severity) + +/obj/machinery/computer/secure_data/detective_computer + icon_state = "messyfiles" + +#undef FIELD diff --git a/code/game/machinery/computer/shuttle.dm b/code/game/machinery/computer/shuttle.dm index d4e58675493..42ee83612d6 100644 --- a/code/game/machinery/computer/shuttle.dm +++ b/code/game/machinery/computer/shuttle.dm @@ -1,71 +1,71 @@ -/obj/machinery/computer/shuttle - name = "Shuttle" - desc = "For shuttle control." - icon_keyboard = "tech_key" - icon_screen = "shuttle" - light_color = "#00ffff" - var/auth_need = 3.0 - var/list/authorized = list( ) - - -/obj/machinery/computer/shuttle/attackby(var/obj/item/weapon/card/W as obj, var/mob/user as mob) - if(stat & (BROKEN|NOPOWER)) return - if ((!( istype(W, /obj/item/weapon/card) ) || !( ticker ) || emergency_shuttle.location() || !( user ))) return - if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) - if (istype(W, /obj/item/device/pda)) - var/obj/item/device/pda/pda = W - W = pda.id - if (!W:access) //no access - to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") - return - - var/list/cardaccess = W:access - if(!istype(cardaccess, /list) || !cardaccess.len) //no access - to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") - return - - if(!(access_heads in W:access)) //doesn't have this access - to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") - return 0 - - var/choice = tgui_alert(user, text("Would you like to (un)authorize a shortened launch time? [] authorization\s are still needed. Use abort to cancel all authorizations.", src.auth_need - src.authorized.len), "Shuttle Launch", list("Authorize", "Repeal", "Abort")) - if(emergency_shuttle.location() && user.get_active_hand() != W) - return 0 - switch(choice) - if("Authorize") - src.authorized -= W:registered_name - src.authorized += W:registered_name - if (src.auth_need - src.authorized.len > 0) - message_admins("[key_name_admin(user)] has authorized early shuttle launch") - log_game("[user.ckey] has authorized early shuttle launch") - to_world("Alert: [src.auth_need - src.authorized.len] authorizations needed until shuttle is launched early") - else - message_admins("[key_name_admin(user)] has launched the shuttle") - log_game("[user.ckey] has launched the shuttle early") - to_world("Alert: Shuttle launch time shortened to 10 seconds!") - emergency_shuttle.set_launch_countdown(10) - //src.authorized = null - qdel(src.authorized) - src.authorized = list( ) - - if("Repeal") - src.authorized -= W:registered_name - to_world("Alert: [src.auth_need - src.authorized.len] authorizations needed until shuttle is launched early") - - if("Abort") - to_world("All authorizations to shortening time for shuttle launch have been revoked!") - src.authorized.len = 0 - src.authorized = list( ) - - else if (istype(W, /obj/item/weapon/card/emag) && !emagged) - var/choice = tgui_alert(user, "Would you like to launch the shuttle?", "Shuttle control", list("Launch", "Cancel")) - - if(!emagged && !emergency_shuttle.location() && user.get_active_hand() == W) - switch(choice) - if("Launch") - to_world("Alert: Shuttle launch time shortened to 10 seconds!") - emergency_shuttle.set_launch_countdown(10) - emagged = 1 - if("Cancel") - return - return +/obj/machinery/computer/shuttle + name = "Shuttle" + desc = "For shuttle control." + icon_keyboard = "tech_key" + icon_screen = "shuttle" + light_color = "#00ffff" + var/auth_need = 3.0 + var/list/authorized = list( ) + + +/obj/machinery/computer/shuttle/attackby(var/obj/item/weapon/card/W as obj, var/mob/user as mob) + if(stat & (BROKEN|NOPOWER)) return + if ((!( istype(W, /obj/item/weapon/card) ) || !( ticker ) || emergency_shuttle.location() || !( user ))) return + if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) + if (istype(W, /obj/item/device/pda)) + var/obj/item/device/pda/pda = W + W = pda.id + if (!W:access) //no access + to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") + return + + var/list/cardaccess = W:access + if(!istype(cardaccess, /list) || !cardaccess.len) //no access + to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") + return + + if(!(access_heads in W:access)) //doesn't have this access + to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") + return 0 + + var/choice = tgui_alert(user, text("Would you like to (un)authorize a shortened launch time? [] authorization\s are still needed. Use abort to cancel all authorizations.", src.auth_need - src.authorized.len), "Shuttle Launch", list("Authorize", "Repeal", "Abort")) + if(emergency_shuttle.location() && user.get_active_hand() != W) + return 0 + switch(choice) + if("Authorize") + src.authorized -= W:registered_name + src.authorized += W:registered_name + if (src.auth_need - src.authorized.len > 0) + message_admins("[key_name_admin(user)] has authorized early shuttle launch") + log_game("[user.ckey] has authorized early shuttle launch") + to_world("Alert: [src.auth_need - src.authorized.len] authorizations needed until shuttle is launched early") + else + message_admins("[key_name_admin(user)] has launched the shuttle") + log_game("[user.ckey] has launched the shuttle early") + to_world("Alert: Shuttle launch time shortened to 10 seconds!") + emergency_shuttle.set_launch_countdown(10) + //src.authorized = null + qdel(src.authorized) + src.authorized = list( ) + + if("Repeal") + src.authorized -= W:registered_name + to_world("Alert: [src.auth_need - src.authorized.len] authorizations needed until shuttle is launched early") + + if("Abort") + to_world("All authorizations to shortening time for shuttle launch have been revoked!") + src.authorized.len = 0 + src.authorized = list( ) + + else if (istype(W, /obj/item/weapon/card/emag) && !emagged) + var/choice = tgui_alert(user, "Would you like to launch the shuttle?", "Shuttle control", list("Launch", "Cancel")) + + if(!emagged && !emergency_shuttle.location() && user.get_active_hand() == W) + switch(choice) + if("Launch") + to_world("Alert: Shuttle launch time shortened to 10 seconds!") + emergency_shuttle.set_launch_countdown(10) + emagged = 1 + if("Cancel") + return + return diff --git a/code/game/machinery/computer/specops_shuttle.dm b/code/game/machinery/computer/specops_shuttle.dm index ce0bea0aff3..cc8727343c2 100644 --- a/code/game/machinery/computer/specops_shuttle.dm +++ b/code/game/machinery/computer/specops_shuttle.dm @@ -1,331 +1,331 @@ -//Config stuff -#define SPECOPS_MOVETIME 600 //Time to station is milliseconds. 60 seconds, enough time for everyone to be on the shuttle before it leaves. -#define SPECOPS_STATION_AREATYPE "/area/shuttle/specops/station" //Type of the spec ops shuttle area for station -#define SPECOPS_DOCK_AREATYPE "/area/shuttle/specops/centcom" //Type of the spec ops shuttle area for dock -#define SPECOPS_RETURN_DELAY 600 //Time between the shuttle is capable of moving. - -var/specops_shuttle_moving_to_station = 0 -var/specops_shuttle_moving_to_centcom = 0 -var/specops_shuttle_at_station = 0 -var/specops_shuttle_can_send = 1 -var/specops_shuttle_time = 0 -var/specops_shuttle_timeleft = 0 - -/obj/machinery/computer/specops_shuttle - name = "special operations shuttle control console" - icon_keyboard = "security_key" - icon_screen = "syndishuttle" - light_color = "#00ffff" - req_access = list(access_cent_specops) -// req_access = list(ACCESS_CENT_SPECOPS) - var/temp = null - var/hacked = 0 - var/allowedtocall = 0 - var/specops_shuttle_timereset = 0 - -/proc/specops_return() - var/obj/item/device/radio/intercom/announcer = new /obj/item/device/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. - announcer.config(list("Response Team" = 0)) - - var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. - var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING TO RETURN\""//Initial message shown. - if(announcer) - announcer.autosay(message, "A.L.I.C.E.", "Response Team") - - while(specops_shuttle_time - world.timeofday > 0) - var/ticksleft = specops_shuttle_time - world.timeofday - - if(ticksleft > 1e5) - specops_shuttle_time = world.timeofday + 10 // midnight rollover - specops_shuttle_timeleft = (ticksleft / 10) - - //All this does is announce the time before launch. - if(announcer) - var/rounded_time_left = round(specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. - if(rounded_time_left in message_tracker)//If that time is in the list for message announce. - message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" - if(rounded_time_left==0) - message = "\"ALERT: TAKEOFF\"" - announcer.autosay(message, "A.L.I.C.E.", "Response Team") - message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. - //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. - - sleep(5) - - specops_shuttle_moving_to_station = 0 - specops_shuttle_moving_to_centcom = 0 - - specops_shuttle_at_station = 1 - - var/area/start_location = locate(/area/shuttle/specops/station) - var/area/end_location = locate(/area/shuttle/specops/centcom) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs += T - if(T.y < throwy) - throwy = T.y - - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - - for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... - bug.gib() - - for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug... - pest.gib() - - start_location.move_contents_to(end_location) - - for(var/turf/T in get_area_turfs(end_location) ) - var/mob/M = locate(/mob) in T - to_chat(M, "You have arrived at [using_map.boss_name]. Operation has ended!") - - specops_shuttle_at_station = 0 - - for(var/obj/machinery/computer/specops_shuttle/S in machines) - S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY - - qdel(announcer) - -/proc/specops_process() - var/area/centcom/specops/special_ops = locate()//Where is the specops area located? - var/obj/item/device/radio/intercom/announcer = new /obj/item/device/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. - announcer.config(list("Response Team" = 0)) - - var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. - var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING FOR LAUNCH\""//Initial message shown. - if(announcer) - announcer.autosay(message, "A.L.I.C.E.", "Response Team") -// message = "ARMORED SQUAD TAKE YOUR POSITION ON GRAVITY LAUNCH PAD" -// announcer.autosay(message, "A.L.I.C.E.", "Response Team") - - while(specops_shuttle_time - world.timeofday > 0) - var/ticksleft = specops_shuttle_time - world.timeofday - - if(ticksleft > 1e5) - specops_shuttle_time = world.timeofday + 10 // midnight rollover - specops_shuttle_timeleft = (ticksleft / 10) - - //All this does is announce the time before launch. - if(announcer) - var/rounded_time_left = round(specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. - if(rounded_time_left in message_tracker)//If that time is in the list for message announce. - message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" - if(rounded_time_left==0) - message = "\"ALERT: TAKEOFF\"" - announcer.autosay(message, "A.L.I.C.E.", "Response Team") - message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. - //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. - - sleep(5) - - specops_shuttle_moving_to_station = 0 - specops_shuttle_moving_to_centcom = 0 - - specops_shuttle_at_station = 1 - if (specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return - - if (!specops_can_move()) - to_chat(usr, "The Special Operations shuttle is unable to leave.") - return - - //Begin Marauder launchpad. - spawn(0)//So it parallel processes it. - for(var/obj/machinery/door/blast/M in special_ops) - switch(M.id) - if("ASSAULT0") - spawn(10)//1 second delay between each. - M.open() - if("ASSAULT1") - spawn(20) - M.open() - if("ASSAULT2") - spawn(30) - M.open() - if("ASSAULT3") - spawn(40) - M.open() - - sleep(10) - - var/spawn_marauder[] = new() - for(var/obj/effect/landmark/L in landmarks_list) - if(L.name == "Marauder Entry") - spawn_marauder.Add(L) - for(var/obj/effect/landmark/L in landmarks_list) - if(L.name == "Marauder Exit") - var/obj/effect/portal/P = new(L.loc) - P.invisibility = 101//So it is not seen by anyone. - P.failchance = 0//So it has no fail chance when teleporting. - P.target = pick(spawn_marauder)//Where the marauder will arrive. - spawn_marauder.Remove(P.target) - - sleep(10) - - for(var/obj/machinery/mass_driver/M in special_ops) - switch(M.id) - if("ASSAULT0") - spawn(10) - M.drive() - if("ASSAULT1") - spawn(20) - M.drive() - if("ASSAULT2") - spawn(30) - M.drive() - if("ASSAULT3") - spawn(40) - M.drive() - - sleep(50)//Doors remain open for 5 seconds. - - for(var/obj/machinery/door/blast/M in special_ops) - switch(M.id)//Doors close at the same time. - if("ASSAULT0") - spawn(0) - M.close() - if("ASSAULT1") - spawn(0) - M.close() - if("ASSAULT2") - spawn(0) - M.close() - if("ASSAULT3") - spawn(0) - M.close() - special_ops.readyreset()//Reset firealarm after the team launched. - //End Marauder launchpad. - - var/area/start_location = locate(/area/shuttle/specops/centcom) - var/area/end_location = locate(/area/shuttle/specops/station) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs += T - if(T.y < throwy) - throwy = T.y - - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - - start_location.move_contents_to(end_location) - - for(var/turf/T in get_area_turfs(end_location) ) - var/mob/M = locate(/mob) in T - to_chat(M, "You have arrived to [station_name()]. Commence operation!") - - for(var/obj/machinery/computer/specops_shuttle/S in machines) - S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY - - qdel(announcer) - -/proc/specops_can_move() - if(specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) - return 0 - for(var/obj/machinery/computer/specops_shuttle/S in machines) - if(world.timeofday <= S.specops_shuttle_timereset) - return 0 - return 1 - -/obj/machinery/computer/specops_shuttle/attack_ai(var/mob/user as mob) - return attack_hand(user) - -/obj/machinery/computer/specops_shuttle/emag_act(var/remaining_charges, var/mob/user) - to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") - -/obj/machinery/computer/specops_shuttle/attack_hand(var/mob/user as mob) - if(!allowed(user)) - to_chat(user, "Access Denied.") - return - - if(..()) - return - - user.machine = src - var/dat - if (temp) - dat = temp - else - dat += {"
    Special Operations Shuttle
    - \nLocation: [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "Departing for [station_name()] in ([specops_shuttle_timeleft] seconds.)":specops_shuttle_at_station ? "Station":"Dock"]
    - [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "\n*The Special Ops. shuttle is already leaving.*
    \n
    ":specops_shuttle_at_station ? "\nShuttle standing by...
    \n
    ":"\nDepart to [station_name()]
    \n
    "] - \nClose"} - - user << browse(dat, "window=computer;size=575x450") - onclose(user, "computer") - return - -/obj/machinery/computer/specops_shuttle/Topic(href, href_list) - if(..()) - return 1 - - if ((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.machine = src - - if (href_list["sendtodock"]) - if(!specops_shuttle_at_station|| specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return - - if (!specops_can_move()) - to_chat(usr, "[using_map.boss_name] will not allow the Special Operations shuttle to return yet.") - if(world.timeofday <= specops_shuttle_timereset) - if (((world.timeofday - specops_shuttle_timereset)/10) > 60) - to_chat(usr, "[-((world.timeofday - specops_shuttle_timereset)/10)/60] minutes remain!") - to_chat(usr, "[-(world.timeofday - specops_shuttle_timereset)/10] seconds remain!") - return - - to_chat(usr, "The Special Operations shuttle will arrive at [using_map.boss_name] in [(SPECOPS_MOVETIME/10)] seconds.") - - temp += "Shuttle departing.

    OK" - updateUsrDialog() - - specops_shuttle_moving_to_centcom = 1 - specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME - spawn(0) - specops_return() - - else if (href_list["sendtostation"]) - if(specops_shuttle_at_station || specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return - - if (!specops_can_move()) - to_chat(usr, "The Special Operations shuttle is unable to leave.") - return - - to_chat(usr, "The Special Operations shuttle will arrive on [station_name()] in [(SPECOPS_MOVETIME/10)] seconds.") - - temp += "Shuttle departing.

    OK" - updateUsrDialog() - - var/area/centcom/specops/special_ops = locate() - if(special_ops) - special_ops.readyalert()//Trigger alarm for the spec ops area. - specops_shuttle_moving_to_station = 1 - - specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME - spawn(0) - specops_process() - - else if (href_list["mainmenu"]) - temp = null - - add_fingerprint(usr) - updateUsrDialog() - return +//Config stuff +#define SPECOPS_MOVETIME 600 //Time to station is milliseconds. 60 seconds, enough time for everyone to be on the shuttle before it leaves. +#define SPECOPS_STATION_AREATYPE "/area/shuttle/specops/station" //Type of the spec ops shuttle area for station +#define SPECOPS_DOCK_AREATYPE "/area/shuttle/specops/centcom" //Type of the spec ops shuttle area for dock +#define SPECOPS_RETURN_DELAY 600 //Time between the shuttle is capable of moving. + +var/specops_shuttle_moving_to_station = 0 +var/specops_shuttle_moving_to_centcom = 0 +var/specops_shuttle_at_station = 0 +var/specops_shuttle_can_send = 1 +var/specops_shuttle_time = 0 +var/specops_shuttle_timeleft = 0 + +/obj/machinery/computer/specops_shuttle + name = "special operations shuttle control console" + icon_keyboard = "security_key" + icon_screen = "syndishuttle" + light_color = "#00ffff" + req_access = list(access_cent_specops) +// req_access = list(ACCESS_CENT_SPECOPS) + var/temp = null + var/hacked = 0 + var/allowedtocall = 0 + var/specops_shuttle_timereset = 0 + +/proc/specops_return() + var/obj/item/device/radio/intercom/announcer = new /obj/item/device/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. + announcer.config(list("Response Team" = 0)) + + var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. + var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING TO RETURN\""//Initial message shown. + if(announcer) + announcer.autosay(message, "A.L.I.C.E.", "Response Team") + + while(specops_shuttle_time - world.timeofday > 0) + var/ticksleft = specops_shuttle_time - world.timeofday + + if(ticksleft > 1e5) + specops_shuttle_time = world.timeofday + 10 // midnight rollover + specops_shuttle_timeleft = (ticksleft / 10) + + //All this does is announce the time before launch. + if(announcer) + var/rounded_time_left = round(specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. + if(rounded_time_left in message_tracker)//If that time is in the list for message announce. + message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" + if(rounded_time_left==0) + message = "\"ALERT: TAKEOFF\"" + announcer.autosay(message, "A.L.I.C.E.", "Response Team") + message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. + //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. + + sleep(5) + + specops_shuttle_moving_to_station = 0 + specops_shuttle_moving_to_centcom = 0 + + specops_shuttle_at_station = 1 + + var/area/start_location = locate(/area/shuttle/specops/station) + var/area/end_location = locate(/area/shuttle/specops/centcom) + + var/list/dstturfs = list() + var/throwy = world.maxy + + for(var/turf/T in end_location) + dstturfs += T + if(T.y < throwy) + throwy = T.y + + // hey you, get out of the way! + for(var/turf/T in dstturfs) + // find the turf to move things to + var/turf/D = locate(T.x, throwy - 1, 1) + //var/turf/E = get_step(D, SOUTH) + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + if(istype(T, /turf/simulated)) + qdel(T) + + for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... + bug.gib() + + for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug... + pest.gib() + + start_location.move_contents_to(end_location) + + for(var/turf/T in get_area_turfs(end_location) ) + var/mob/M = locate(/mob) in T + to_chat(M, "You have arrived at [using_map.boss_name]. Operation has ended!") + + specops_shuttle_at_station = 0 + + for(var/obj/machinery/computer/specops_shuttle/S in machines) + S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY + + qdel(announcer) + +/proc/specops_process() + var/area/centcom/specops/special_ops = locate()//Where is the specops area located? + var/obj/item/device/radio/intercom/announcer = new /obj/item/device/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. + announcer.config(list("Response Team" = 0)) + + var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. + var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING FOR LAUNCH\""//Initial message shown. + if(announcer) + announcer.autosay(message, "A.L.I.C.E.", "Response Team") +// message = "ARMORED SQUAD TAKE YOUR POSITION ON GRAVITY LAUNCH PAD" +// announcer.autosay(message, "A.L.I.C.E.", "Response Team") + + while(specops_shuttle_time - world.timeofday > 0) + var/ticksleft = specops_shuttle_time - world.timeofday + + if(ticksleft > 1e5) + specops_shuttle_time = world.timeofday + 10 // midnight rollover + specops_shuttle_timeleft = (ticksleft / 10) + + //All this does is announce the time before launch. + if(announcer) + var/rounded_time_left = round(specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. + if(rounded_time_left in message_tracker)//If that time is in the list for message announce. + message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" + if(rounded_time_left==0) + message = "\"ALERT: TAKEOFF\"" + announcer.autosay(message, "A.L.I.C.E.", "Response Team") + message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. + //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. + + sleep(5) + + specops_shuttle_moving_to_station = 0 + specops_shuttle_moving_to_centcom = 0 + + specops_shuttle_at_station = 1 + if (specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return + + if (!specops_can_move()) + to_chat(usr, "The Special Operations shuttle is unable to leave.") + return + + //Begin Marauder launchpad. + spawn(0)//So it parallel processes it. + for(var/obj/machinery/door/blast/M in special_ops) + switch(M.id) + if("ASSAULT0") + spawn(10)//1 second delay between each. + M.open() + if("ASSAULT1") + spawn(20) + M.open() + if("ASSAULT2") + spawn(30) + M.open() + if("ASSAULT3") + spawn(40) + M.open() + + sleep(10) + + var/spawn_marauder[] = new() + for(var/obj/effect/landmark/L in landmarks_list) + if(L.name == "Marauder Entry") + spawn_marauder.Add(L) + for(var/obj/effect/landmark/L in landmarks_list) + if(L.name == "Marauder Exit") + var/obj/effect/portal/P = new(L.loc) + P.invisibility = 101//So it is not seen by anyone. + P.failchance = 0//So it has no fail chance when teleporting. + P.target = pick(spawn_marauder)//Where the marauder will arrive. + spawn_marauder.Remove(P.target) + + sleep(10) + + for(var/obj/machinery/mass_driver/M in special_ops) + switch(M.id) + if("ASSAULT0") + spawn(10) + M.drive() + if("ASSAULT1") + spawn(20) + M.drive() + if("ASSAULT2") + spawn(30) + M.drive() + if("ASSAULT3") + spawn(40) + M.drive() + + sleep(50)//Doors remain open for 5 seconds. + + for(var/obj/machinery/door/blast/M in special_ops) + switch(M.id)//Doors close at the same time. + if("ASSAULT0") + spawn(0) + M.close() + if("ASSAULT1") + spawn(0) + M.close() + if("ASSAULT2") + spawn(0) + M.close() + if("ASSAULT3") + spawn(0) + M.close() + special_ops.readyreset()//Reset firealarm after the team launched. + //End Marauder launchpad. + + var/area/start_location = locate(/area/shuttle/specops/centcom) + var/area/end_location = locate(/area/shuttle/specops/station) + + var/list/dstturfs = list() + var/throwy = world.maxy + + for(var/turf/T in end_location) + dstturfs += T + if(T.y < throwy) + throwy = T.y + + // hey you, get out of the way! + for(var/turf/T in dstturfs) + // find the turf to move things to + var/turf/D = locate(T.x, throwy - 1, 1) + //var/turf/E = get_step(D, SOUTH) + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + if(istype(T, /turf/simulated)) + qdel(T) + + start_location.move_contents_to(end_location) + + for(var/turf/T in get_area_turfs(end_location) ) + var/mob/M = locate(/mob) in T + to_chat(M, "You have arrived to [station_name()]. Commence operation!") + + for(var/obj/machinery/computer/specops_shuttle/S in machines) + S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY + + qdel(announcer) + +/proc/specops_can_move() + if(specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) + return 0 + for(var/obj/machinery/computer/specops_shuttle/S in machines) + if(world.timeofday <= S.specops_shuttle_timereset) + return 0 + return 1 + +/obj/machinery/computer/specops_shuttle/attack_ai(var/mob/user as mob) + return attack_hand(user) + +/obj/machinery/computer/specops_shuttle/emag_act(var/remaining_charges, var/mob/user) + to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") + +/obj/machinery/computer/specops_shuttle/attack_hand(var/mob/user as mob) + if(!allowed(user)) + to_chat(user, "Access Denied.") + return + + if(..()) + return + + user.machine = src + var/dat + if (temp) + dat = temp + else + dat += {"
    Special Operations Shuttle
    + \nLocation: [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "Departing for [station_name()] in ([specops_shuttle_timeleft] seconds.)":specops_shuttle_at_station ? "Station":"Dock"]
    + [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "\n*The Special Ops. shuttle is already leaving.*
    \n
    ":specops_shuttle_at_station ? "\nShuttle standing by...
    \n
    ":"\nDepart to [station_name()]
    \n
    "] + \nClose"} + + user << browse(dat, "window=computer;size=575x450") + onclose(user, "computer") + return + +/obj/machinery/computer/specops_shuttle/Topic(href, href_list) + if(..()) + return 1 + + if ((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.machine = src + + if (href_list["sendtodock"]) + if(!specops_shuttle_at_station|| specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return + + if (!specops_can_move()) + to_chat(usr, "[using_map.boss_name] will not allow the Special Operations shuttle to return yet.") + if(world.timeofday <= specops_shuttle_timereset) + if (((world.timeofday - specops_shuttle_timereset)/10) > 60) + to_chat(usr, "[-((world.timeofday - specops_shuttle_timereset)/10)/60] minutes remain!") + to_chat(usr, "[-(world.timeofday - specops_shuttle_timereset)/10] seconds remain!") + return + + to_chat(usr, "The Special Operations shuttle will arrive at [using_map.boss_name] in [(SPECOPS_MOVETIME/10)] seconds.") + + temp += "Shuttle departing.

    OK" + updateUsrDialog() + + specops_shuttle_moving_to_centcom = 1 + specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME + spawn(0) + specops_return() + + else if (href_list["sendtostation"]) + if(specops_shuttle_at_station || specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return + + if (!specops_can_move()) + to_chat(usr, "The Special Operations shuttle is unable to leave.") + return + + to_chat(usr, "The Special Operations shuttle will arrive on [station_name()] in [(SPECOPS_MOVETIME/10)] seconds.") + + temp += "Shuttle departing.

    OK" + updateUsrDialog() + + var/area/centcom/specops/special_ops = locate() + if(special_ops) + special_ops.readyalert()//Trigger alarm for the spec ops area. + specops_shuttle_moving_to_station = 1 + + specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME + spawn(0) + specops_process() + + else if (href_list["mainmenu"]) + temp = null + + add_fingerprint(usr) + updateUsrDialog() + return diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm index 051a8e368bd..d6e8a3609fb 100644 --- a/code/game/machinery/computer/station_alert.dm +++ b/code/game/machinery/computer/station_alert.dm @@ -1,54 +1,54 @@ - -/obj/machinery/computer/station_alert - name = "Station Alert Console" - desc = "Used to access the station's automated alert system." - icon_keyboard = "tech_key" - icon_screen = "alert:0" - light_color = "#e6ffff" - circuit = /obj/item/weapon/circuitboard/stationalert_engineering - var/datum/tgui_module/alarm_monitor/alarm_monitor - var/monitor_type = /datum/tgui_module/alarm_monitor/engineering - -/obj/machinery/computer/station_alert/security - monitor_type = /datum/tgui_module/alarm_monitor/security - circuit = /obj/item/weapon/circuitboard/stationalert_security - -/obj/machinery/computer/station_alert/all - monitor_type = /datum/tgui_module/alarm_monitor/all - circuit = /obj/item/weapon/circuitboard/stationalert_all - -/obj/machinery/computer/station_alert/Initialize() - alarm_monitor = new monitor_type(src) - alarm_monitor.register_alarm(src, /atom/proc/update_icon) - . = ..() - -/obj/machinery/computer/station_alert/Destroy() - alarm_monitor.unregister_alarm(src) - qdel(alarm_monitor) - ..() - -/obj/machinery/computer/station_alert/attack_ai(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - tgui_interact(user) - return - -/obj/machinery/computer/station_alert/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - tgui_interact(user) - return - -/obj/machinery/computer/station_alert/tgui_interact(mob/user) - alarm_monitor.tgui_interact(user) - -/obj/machinery/computer/station_alert/update_icon() - if(!(stat & (BROKEN|NOPOWER))) - var/list/alarms = alarm_monitor ? alarm_monitor.major_alarms() : list() - if(alarms.len) - icon_screen = "alert:2" - else - icon_screen = initial(icon_screen) - ..() + +/obj/machinery/computer/station_alert + name = "Station Alert Console" + desc = "Used to access the station's automated alert system." + icon_keyboard = "tech_key" + icon_screen = "alert:0" + light_color = "#e6ffff" + circuit = /obj/item/weapon/circuitboard/stationalert_engineering + var/datum/tgui_module/alarm_monitor/alarm_monitor + var/monitor_type = /datum/tgui_module/alarm_monitor/engineering + +/obj/machinery/computer/station_alert/security + monitor_type = /datum/tgui_module/alarm_monitor/security + circuit = /obj/item/weapon/circuitboard/stationalert_security + +/obj/machinery/computer/station_alert/all + monitor_type = /datum/tgui_module/alarm_monitor/all + circuit = /obj/item/weapon/circuitboard/stationalert_all + +/obj/machinery/computer/station_alert/Initialize() + alarm_monitor = new monitor_type(src) + alarm_monitor.register_alarm(src, /atom/proc/update_icon) + . = ..() + +/obj/machinery/computer/station_alert/Destroy() + alarm_monitor.unregister_alarm(src) + qdel(alarm_monitor) + ..() + +/obj/machinery/computer/station_alert/attack_ai(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + tgui_interact(user) + return + +/obj/machinery/computer/station_alert/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + tgui_interact(user) + return + +/obj/machinery/computer/station_alert/tgui_interact(mob/user) + alarm_monitor.tgui_interact(user) + +/obj/machinery/computer/station_alert/update_icon() + if(!(stat & (BROKEN|NOPOWER))) + var/list/alarms = alarm_monitor ? alarm_monitor.major_alarms() : list() + if(alarms.len) + icon_screen = "alert:2" + else + icon_screen = initial(icon_screen) + ..() diff --git a/code/game/machinery/computer/syndicate_specops_shuttle.dm b/code/game/machinery/computer/syndicate_specops_shuttle.dm index c7db7b94e37..82994660717 100644 --- a/code/game/machinery/computer/syndicate_specops_shuttle.dm +++ b/code/game/machinery/computer/syndicate_specops_shuttle.dm @@ -1,6 +1,6 @@ -/obj/machinery/computer/syndicate_elite_shuttle - name = "elite mercenary squad shuttle control console" - icon_keyboard = "syndie_key" - icon_screen = "syndishuttle" - light_color = "#00ffff" - req_access = list(access_cent_specops) +/obj/machinery/computer/syndicate_elite_shuttle + name = "elite mercenary squad shuttle control console" + icon_keyboard = "syndie_key" + icon_screen = "syndishuttle" + light_color = "#00ffff" + req_access = list(access_cent_specops) diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index bf6d5080d7d..29979be8940 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -1,160 +1,160 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 -//Circuit boards are in /code/game/objects/items/weapons/circuitboards/machinery/ - -/obj/machinery/constructable_frame //Made into a seperate type to make future revisions easier. - name = "machine frame" - icon = 'icons/obj/stock_parts.dmi' - icon_state = "box_0" - density = TRUE - anchored = TRUE - use_power = USE_POWER_OFF - var/obj/item/weapon/circuitboard/circuit = null - var/list/components = null - var/list/req_components = null - var/list/req_component_names = null - var/state = 1 - - proc/update_desc() - var/D - if(req_components) - var/list/component_list = new - for(var/I in req_components) - if(req_components[I] > 0) - component_list += "[num2text(req_components[I])] [req_component_names[I]]" - D = "Requires [english_list(component_list)]." - desc = D - -/obj/machinery/constructable_frame/machine_frame - attackby(obj/item/P as obj, mob/user as mob) - switch(state) - if(1) - if(istype(P, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = P - if (C.get_amount() < 5) - to_chat(user, "You need five lengths of cable to add them to the frame.") - return - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - to_chat(user, "You start to add cables to the frame.") - if(do_after(user, 20) && state == 1) - if(C.use(5)) - to_chat(user, "You add cables to the frame.") - state = 2 - icon_state = "box_1" - else - if(P.has_tool_quality(TOOL_WRENCH)) - playsound(src, W.usesound, 75, 1) - to_chat(user, "You dismantle the frame") - new /obj/item/stack/material/steel(src.loc, 5) - qdel(src) - if(2) - if(istype(P, /obj/item/weapon/circuitboard)) - var/obj/item/weapon/circuitboard/B = P - if(B.board_type == "machine") - //VOREStation Addition End - if(istype(B, /obj/item/weapon/circuitboard/quantumpad) && istype(get_area(src), /area/shuttle)) - to_chat(user, "This is too unstable a platform for a quantum pad to operate on!") - return - //VOREStation Addition End - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - to_chat(user, "You add the circuit board to the frame.") - circuit = P - user.drop_item() - P.loc = src - icon_state = "box_2" - state = 3 - components = list() - req_components = circuit.req_components.Copy() - for(var/A in circuit.req_components) - req_components[A] = circuit.req_components[A] - req_component_names = circuit.req_components.Copy() - for(var/A in req_components) - var/cp = text2path(A) - var/obj/ct = new cp() // have to quickly instantiate it get name - req_component_names[A] = ct.name - update_desc() - to_chat(user, desc) - else - to_chat(user, "This frame does not accept circuit boards of this type!") - else - if(P.has_tool_quality(TOOL_WIRECUTTER)) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - state = 1 - icon_state = "box_0" - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( src.loc ) - A.amount = 5 - - if(3) - if(P.has_tool_quality(TOOL_CROWBAR)) - playsound(src, P.usesound, 50, 1) - state = 2 - circuit.loc = src.loc - circuit = null - if(components.len == 0) - to_chat(user, "You remove the circuit board.") - else - to_chat(user, "You remove the circuit board and other components.") - for(var/obj/item/weapon/W in components) - W.loc = src.loc - desc = initial(desc) - req_components = null - components = null - icon_state = "box_1" - else - if(P.has_tool_quality(TOOL_SCREWDRIVER)) - var/component_check = 1 - for(var/R in req_components) - if(req_components[R] > 0) - component_check = 0 - break - if(component_check) - playsound(src, P.usesound, 50, 1) - var/obj/machinery/new_machine = new src.circuit.build_path(src.loc, src.dir) - - if(new_machine.component_parts) - new_machine.component_parts.Cut() - else - new_machine.component_parts = list() - - src.circuit.construct(new_machine) - - for(var/obj/O in src) - if(circuit.contain_parts) // things like disposal don't want their parts in them - O.loc = new_machine - else - O.loc = null - new_machine.component_parts += O - - if(circuit.contain_parts) - circuit.loc = new_machine - else - circuit.loc = null - - new_machine.RefreshParts() - qdel(src) - else - if(istype(P, /obj/item)) - for(var/I in req_components) - if(istype(P, text2path(I)) && (req_components[I] > 0)) - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - if(P.is_cable_coil)) - var/obj/item/stack/cable_coil/CP = P - if(CP.get_amount() > 1) - var/camt = min(CP.amount, req_components[I]) // amount of cable to take, idealy amount required, but limited by amount provided - var/obj/item/stack/cable_coil/CC = new /obj/item/stack/cable_coil(src) - CC.amount = camt - CC.update_icon() - CP.use(camt) - components += CC - req_components[I] -= camt - update_desc() - break - user.drop_item() - P.loc = src - components += P - req_components[I]-- - update_desc() - break - to_chat(user, desc) - if(P && P.loc != src && !istype(P, /obj/item/stack/cable_coil)) - to_chat(user, "You cannot add that component to the machine!") +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 +//Circuit boards are in /code/game/objects/items/weapons/circuitboards/machinery/ + +/obj/machinery/constructable_frame //Made into a seperate type to make future revisions easier. + name = "machine frame" + icon = 'icons/obj/stock_parts.dmi' + icon_state = "box_0" + density = TRUE + anchored = TRUE + use_power = USE_POWER_OFF + var/obj/item/weapon/circuitboard/circuit = null + var/list/components = null + var/list/req_components = null + var/list/req_component_names = null + var/state = 1 + + proc/update_desc() + var/D + if(req_components) + var/list/component_list = new + for(var/I in req_components) + if(req_components[I] > 0) + component_list += "[num2text(req_components[I])] [req_component_names[I]]" + D = "Requires [english_list(component_list)]." + desc = D + +/obj/machinery/constructable_frame/machine_frame + attackby(obj/item/P as obj, mob/user as mob) + switch(state) + if(1) + if(istype(P, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = P + if (C.get_amount() < 5) + to_chat(user, "You need five lengths of cable to add them to the frame.") + return + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + to_chat(user, "You start to add cables to the frame.") + if(do_after(user, 20) && state == 1) + if(C.use(5)) + to_chat(user, "You add cables to the frame.") + state = 2 + icon_state = "box_1" + else + if(P.has_tool_quality(TOOL_WRENCH)) + playsound(src, W.usesound, 75, 1) + to_chat(user, "You dismantle the frame") + new /obj/item/stack/material/steel(src.loc, 5) + qdel(src) + if(2) + if(istype(P, /obj/item/weapon/circuitboard)) + var/obj/item/weapon/circuitboard/B = P + if(B.board_type == "machine") + //VOREStation Addition End + if(istype(B, /obj/item/weapon/circuitboard/quantumpad) && istype(get_area(src), /area/shuttle)) + to_chat(user, "This is too unstable a platform for a quantum pad to operate on!") + return + //VOREStation Addition End + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + to_chat(user, "You add the circuit board to the frame.") + circuit = P + user.drop_item() + P.loc = src + icon_state = "box_2" + state = 3 + components = list() + req_components = circuit.req_components.Copy() + for(var/A in circuit.req_components) + req_components[A] = circuit.req_components[A] + req_component_names = circuit.req_components.Copy() + for(var/A in req_components) + var/cp = text2path(A) + var/obj/ct = new cp() // have to quickly instantiate it get name + req_component_names[A] = ct.name + update_desc() + to_chat(user, desc) + else + to_chat(user, "This frame does not accept circuit boards of this type!") + else + if(P.has_tool_quality(TOOL_WIRECUTTER)) + playsound(src, P.usesound, 50, 1) + to_chat(user, "You remove the cables.") + state = 1 + icon_state = "box_0" + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( src.loc ) + A.amount = 5 + + if(3) + if(P.has_tool_quality(TOOL_CROWBAR)) + playsound(src, P.usesound, 50, 1) + state = 2 + circuit.loc = src.loc + circuit = null + if(components.len == 0) + to_chat(user, "You remove the circuit board.") + else + to_chat(user, "You remove the circuit board and other components.") + for(var/obj/item/weapon/W in components) + W.loc = src.loc + desc = initial(desc) + req_components = null + components = null + icon_state = "box_1" + else + if(P.has_tool_quality(TOOL_SCREWDRIVER)) + var/component_check = 1 + for(var/R in req_components) + if(req_components[R] > 0) + component_check = 0 + break + if(component_check) + playsound(src, P.usesound, 50, 1) + var/obj/machinery/new_machine = new src.circuit.build_path(src.loc, src.dir) + + if(new_machine.component_parts) + new_machine.component_parts.Cut() + else + new_machine.component_parts = list() + + src.circuit.construct(new_machine) + + for(var/obj/O in src) + if(circuit.contain_parts) // things like disposal don't want their parts in them + O.loc = new_machine + else + O.loc = null + new_machine.component_parts += O + + if(circuit.contain_parts) + circuit.loc = new_machine + else + circuit.loc = null + + new_machine.RefreshParts() + qdel(src) + else + if(istype(P, /obj/item)) + for(var/I in req_components) + if(istype(P, text2path(I)) && (req_components[I] > 0)) + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + if(P.is_cable_coil)) + var/obj/item/stack/cable_coil/CP = P + if(CP.get_amount() > 1) + var/camt = min(CP.amount, req_components[I]) // amount of cable to take, idealy amount required, but limited by amount provided + var/obj/item/stack/cable_coil/CC = new /obj/item/stack/cable_coil(src) + CC.amount = camt + CC.update_icon() + CP.use(camt) + components += CC + req_components[I] -= camt + update_desc() + break + user.drop_item() + P.loc = src + components += P + req_components[I]-- + update_desc() + break + to_chat(user, desc) + if(P && P.loc != src && !istype(P, /obj/item/stack/cable_coil)) + to_chat(user, "You cannot add that component to the machine!") diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm index 0226d0bc66f..3a394b1973d 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -1,368 +1,368 @@ -#define HEAT_CAPACITY_HUMAN 100 //249840 J/K, for a 72 kg person. - -/obj/machinery/atmospherics/unary/cryo_cell - name = "cryo cell" - desc = "Used to cool people down for medical reasons. Totally." - icon = 'icons/obj/cryogenics.dmi' // map only - icon_state = "pod_preview" - density = TRUE - anchored = TRUE - layer = UNDER_JUNK_LAYER - interact_offline = 1 - - var/on = 0 - use_power = USE_POWER_IDLE - idle_power_usage = 20 - active_power_usage = 200 - buckle_lying = FALSE - buckle_dir = SOUTH - clicksound = 'sound/machines/buttonbeep.ogg' - clickvol = 30 - - var/temperature_archived - var/mob/living/carbon/occupant = null - var/obj/item/weapon/reagent_containers/glass/beaker = null - - var/current_heat_capacity = 50 - - var/image/fluid - -/obj/machinery/atmospherics/unary/cryo_cell/New() - ..() - icon = 'icons/obj/cryogenics_split.dmi' - icon_state = "base" - initialize_directions = dir - -/obj/machinery/atmospherics/unary/cryo_cell/Initialize() - . = ..() - var/image/tank = image(icon,"tank") - tank.alpha = 200 - tank.pixel_y = 18 - tank.plane = MOB_PLANE - tank.layer = MOB_LAYER+0.2 //Above fluid - fluid = image(icon, "tube_filler") - fluid.pixel_y = 18 - fluid.alpha = 200 - fluid.plane = MOB_PLANE - fluid.layer = MOB_LAYER+0.1 //Below glass, above mob - add_overlay(tank) - update_icon() - -/obj/machinery/atmospherics/unary/cryo_cell/Destroy() - var/turf/T = src.loc - T.contents += contents - if(beaker) - beaker.forceMove(get_step(loc, SOUTH)) //Beaker is carefully ejected from the wreckage of the cryotube - beaker = null - . = ..() - -/obj/machinery/atmospherics/unary/cryo_cell/process() - ..() - if(!node) - return - if(!on) - return - - if(occupant) - if(occupant.stat != 2) - process_occupant() - - if(air_contents) - temperature_archived = air_contents.temperature - heat_gas_contents() - expel_gas() - - if(abs(temperature_archived-air_contents.temperature) > 1) - network.update = 1 - - return 1 - -/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user as mob) - // note that relaymove will also be called for mobs outside the cell with UI open - if(occupant == user && !user.stat) - go_out() - -/obj/machinery/atmospherics/unary/cryo_cell/attack_ghost(mob/user) - tgui_interact(user) - -/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user) - if(user == occupant) - return - - if(panel_open) - to_chat(usr, "Close the maintenance panel first.") - return - - tgui_interact(user) - -/obj/machinery/atmospherics/unary/cryo_cell/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Cryo", "Cryo Cell") // 520, 470 - ui.open() - -/obj/machinery/atmospherics/unary/cryo_cell/tgui_data(mob/user) - // this is the data which will be sent to the ui - var/data[0] - data["isOperating"] = on - data["hasOccupant"] = occupant ? TRUE : FALSE - - var/occupantData[0] - if(occupant) - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - occupantData["health"] = occupant.health - occupantData["maxHealth"] = occupant.getMaxHealth() - occupantData["minHealth"] = config.health_threshold_dead - occupantData["bruteLoss"] = occupant.getBruteLoss() - occupantData["oxyLoss"] = occupant.getOxyLoss() - occupantData["toxLoss"] = occupant.getToxLoss() - occupantData["fireLoss"] = occupant.getFireLoss() - occupantData["bodyTemperature"] = occupant.bodytemperature - data["occupant"] = occupantData; - - data["cellTemperature"] = round(air_contents.temperature) - data["cellTemperatureStatus"] = "good" - if(air_contents.temperature > T0C) // if greater than 273.15 kelvin (0 celcius) - data["cellTemperatureStatus"] = "bad" - else if(air_contents.temperature > 225) - data["cellTemperatureStatus"] = "average" - - data["isBeakerLoaded"] = beaker ? TRUE : FALSE - data["beakerLabel"] = null - data["beakerVolume"] = 0 - if(beaker) - data["beakerLabel"] = beaker.label_text ? beaker.label_text : null - if(beaker.reagents && beaker.reagents.reagent_list.len) - for(var/datum/reagent/R in beaker.reagents.reagent_list) - data["beakerVolume"] += R.volume - - return data - -/obj/machinery/atmospherics/unary/cryo_cell/tgui_act(action, params) - if(..() || usr == occupant) - return TRUE - - . = TRUE - switch(action) - if("switchOn") - on = 1 - update_icon() - if("switchOff") - on = 0 - update_icon() - if("ejectBeaker") - if(beaker) - beaker.loc = get_step(src.loc, SOUTH) - beaker = null - update_icon() - if("ejectOccupant") - if(!occupant || isslime(usr) || ispAI(usr)) - return 0 // don't update UIs attached to this object - go_out() - else - return FALSE - - add_fingerprint(usr) - -/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/weapon/G as obj, var/mob/user as mob) - if(istype(G, /obj/item/weapon/reagent_containers/glass)) - if(beaker) - to_chat(user, "A beaker is already loaded into the machine.") - return - - beaker = G - user.drop_item() - G.loc = src - user.visible_message("[user] adds \a [G] to \the [src]!", "You add \a [G] to \the [src]!") - SStgui.update_uis(src) - update_icon() - else if(istype(G, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/grab = G - if(!ismob(grab.affecting)) - return - if(occupant) - to_chat(user,"\The [src] is already occupied by [occupant].") - if(grab.affecting.has_buckled_mobs()) - to_chat(user, span("warning", "\The [grab.affecting] has other entities attached to it. Remove them first.")) - return - var/mob/M = grab.affecting - qdel(grab) - put_mob(M) - - return - -/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(var/mob/target, var/mob/user) //Allows borgs to put people into cryo without external assistance - if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)|| !ishuman(target)) - return - put_mob(target) - -/obj/machinery/atmospherics/unary/cryo_cell/update_icon() - cut_overlay(fluid) - fluid.color = null - if(on) - if(beaker) - fluid.color = beaker.reagents.get_color() - add_overlay(fluid) - -/obj/machinery/atmospherics/unary/cryo_cell/proc/process_occupant() - if(air_contents.total_moles < 10) - return - if(occupant) - if(occupant.stat >= DEAD) - return - occupant.bodytemperature += 2*(air_contents.temperature - occupant.bodytemperature)*current_heat_capacity/(current_heat_capacity + air_contents.heat_capacity()) - occupant.bodytemperature = max(occupant.bodytemperature, air_contents.temperature) // this is so ugly i'm sorry for doing it i'll fix it later i promise - occupant.set_stat(UNCONSCIOUS) - occupant.dir = SOUTH - if(occupant.bodytemperature < T0C) - occupant.Sleeping(max(5, (1/occupant.bodytemperature)*2000)) - occupant.Paralyse(max(5, (1/occupant.bodytemperature)*3000)) - if(air_contents.gas["oxygen"] > 2) - if(occupant.getOxyLoss()) occupant.adjustOxyLoss(-1) - else - occupant.adjustOxyLoss(-1) - //severe damage should heal waaay slower without proper chemicals - if(occupant.bodytemperature < 225) - if(occupant.getToxLoss()) - occupant.adjustToxLoss(max(-1, -20/occupant.getToxLoss())) - if(occupant.radiation || occupant.accumulated_rads) - occupant.radiation -= 25 - occupant.accumulated_rads -= 25 - var/heal_brute = occupant.getBruteLoss() ? min(1, 20/occupant.getBruteLoss()) : 0 - var/heal_fire = occupant.getFireLoss() ? min(1, 20/occupant.getFireLoss()) : 0 - occupant.heal_organ_damage(heal_brute,heal_fire) - var/has_cryo = occupant.reagents.get_reagent_amount("cryoxadone") >= 1 - var/has_clonexa = occupant.reagents.get_reagent_amount("clonexadone") >= 1 - var/has_cryo_medicine = has_cryo || has_clonexa - if(beaker && !has_cryo_medicine) - beaker.reagents.trans_to_mob(occupant, 1, CHEM_BLOOD, 10) - -/obj/machinery/atmospherics/unary/cryo_cell/proc/heat_gas_contents() - if(air_contents.total_moles < 1) - return - var/air_heat_capacity = air_contents.heat_capacity() - var/combined_heat_capacity = current_heat_capacity + air_heat_capacity - if(combined_heat_capacity > 0) - var/combined_energy = T20C*current_heat_capacity + air_heat_capacity*air_contents.temperature - air_contents.temperature = combined_energy/combined_heat_capacity - -/obj/machinery/atmospherics/unary/cryo_cell/proc/expel_gas() - if(air_contents.total_moles < 1) - return -// var/datum/gas_mixture/expel_gas = new -// var/remove_amount = air_contents.total_moles()/50 -// expel_gas = air_contents.remove(remove_amount) - - // Just have the gas disappear to nowhere. - //expel_gas.temperature = T20C // Lets expel hot gas and see if that helps people not die as they are removed - //loc.assume_air(expel_gas) - -/obj/machinery/atmospherics/unary/cryo_cell/proc/go_out() - if(!(occupant)) - return - //for(var/obj/O in src) - // O.loc = src.loc - if(occupant.client) - occupant.client.eye = occupant.client.mob - occupant.client.perspective = MOB_PERSPECTIVE - vis_contents -= occupant - occupant.pixel_x = occupant.default_pixel_x - occupant.pixel_y = occupant.default_pixel_y - occupant.loc = get_step(src.loc, SOUTH) //this doesn't account for walls or anything, but i don't forsee that being a problem. - if(occupant.bodytemperature < 261 && occupant.bodytemperature >= 70) //Patch by Aranclanos to stop people from taking burn damage after being ejected - occupant.bodytemperature = 261 // Changed to 70 from 140 by Zuhayr due to reoccurance of bug. - unbuckle_mob(occupant, force = TRUE) - occupant = null - current_heat_capacity = initial(current_heat_capacity) - update_use_power(USE_POWER_IDLE) - SStgui.update_uis(src) - return - -/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob) - if(stat & (NOPOWER|BROKEN)) - to_chat(usr, "The cryo cell is not functioning.") - return - if(!istype(M)) - to_chat(usr, "The cryo cell cannot handle such a lifeform!") - return - if(occupant) - to_chat(usr, "The cryo cell is already occupied!") - return - if(M.abiotic()) - to_chat(usr, "Subject may not have abiotic items on.") - return - if(!node) - to_chat(usr, "The cell is not correctly connected to its pipe network!") - return - if(M.client) - M.client.perspective = EYE_PERSPECTIVE - M.client.eye = src - M.stop_pulling() - M.loc = src - M.ExtinguishMob() - if(M.health > -100 && (M.health < 0 || M.sleeping)) - to_chat(M, "You feel a cold liquid surround you. Your skin starts to freeze up.") - occupant = M - buckle_mob(occupant, forced = TRUE, check_loc = FALSE) - vis_contents |= occupant - occupant.pixel_y += 19 - current_heat_capacity = HEAT_CAPACITY_HUMAN - update_use_power(USE_POWER_ACTIVE) -// M.metabslow = 1 - add_fingerprint(usr) - update_icon() - SStgui.update_uis(src) - return 1 - -/obj/machinery/atmospherics/unary/cryo_cell/verb/move_eject() - set name = "Eject occupant" - set category = "Object" - set src in oview(1) - if(usr == occupant)//If the user is inside the tube... - if(usr.stat == 2)//and he's not dead.... - return - to_chat(usr, "Release sequence activated. This will take two minutes.") - sleep(1200) - if(!src || !usr || !occupant || (occupant != usr)) //Check if someone's released/replaced/bombed him already - return - go_out()//and release him from the eternal prison. - else - if(usr.stat != 0) - return - go_out() - add_fingerprint(usr) - return - -/obj/machinery/atmospherics/unary/cryo_cell/verb/move_inside() - set name = "Move Inside" - set category = "Object" - set src in oview(1) - if(isliving(usr)) - var/mob/living/L = usr - if(L.has_buckled_mobs()) - to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first.")) - return - if(L.stat != CONSCIOUS) - return - put_mob(L) - -/atom/proc/return_air_for_internal_lifeform(var/mob/living/lifeform) - return return_air() - -/obj/machinery/atmospherics/unary/cryo_cell/return_air_for_internal_lifeform() - //assume that the cryo cell has some kind of breath mask or something that - //draws from the cryo tube's environment, instead of the cold internal air. - if(src.loc) - return loc.return_air() - else - return null - -/datum/data/function/proc/reset() - return - -/datum/data/function/proc/r_input(href, href_list, mob/user as mob) - return - -/datum/data/function/proc/display() - return +#define HEAT_CAPACITY_HUMAN 100 //249840 J/K, for a 72 kg person. + +/obj/machinery/atmospherics/unary/cryo_cell + name = "cryo cell" + desc = "Used to cool people down for medical reasons. Totally." + icon = 'icons/obj/cryogenics.dmi' // map only + icon_state = "pod_preview" + density = TRUE + anchored = TRUE + layer = UNDER_JUNK_LAYER + interact_offline = 1 + + var/on = 0 + use_power = USE_POWER_IDLE + idle_power_usage = 20 + active_power_usage = 200 + buckle_lying = FALSE + buckle_dir = SOUTH + clicksound = 'sound/machines/buttonbeep.ogg' + clickvol = 30 + + var/temperature_archived + var/mob/living/carbon/occupant = null + var/obj/item/weapon/reagent_containers/glass/beaker = null + + var/current_heat_capacity = 50 + + var/image/fluid + +/obj/machinery/atmospherics/unary/cryo_cell/New() + ..() + icon = 'icons/obj/cryogenics_split.dmi' + icon_state = "base" + initialize_directions = dir + +/obj/machinery/atmospherics/unary/cryo_cell/Initialize() + . = ..() + var/image/tank = image(icon,"tank") + tank.alpha = 200 + tank.pixel_y = 18 + tank.plane = MOB_PLANE + tank.layer = MOB_LAYER+0.2 //Above fluid + fluid = image(icon, "tube_filler") + fluid.pixel_y = 18 + fluid.alpha = 200 + fluid.plane = MOB_PLANE + fluid.layer = MOB_LAYER+0.1 //Below glass, above mob + add_overlay(tank) + update_icon() + +/obj/machinery/atmospherics/unary/cryo_cell/Destroy() + var/turf/T = src.loc + T.contents += contents + if(beaker) + beaker.forceMove(get_step(loc, SOUTH)) //Beaker is carefully ejected from the wreckage of the cryotube + beaker = null + . = ..() + +/obj/machinery/atmospherics/unary/cryo_cell/process() + ..() + if(!node) + return + if(!on) + return + + if(occupant) + if(occupant.stat != 2) + process_occupant() + + if(air_contents) + temperature_archived = air_contents.temperature + heat_gas_contents() + expel_gas() + + if(abs(temperature_archived-air_contents.temperature) > 1) + network.update = 1 + + return 1 + +/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user as mob) + // note that relaymove will also be called for mobs outside the cell with UI open + if(occupant == user && !user.stat) + go_out() + +/obj/machinery/atmospherics/unary/cryo_cell/attack_ghost(mob/user) + tgui_interact(user) + +/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user) + if(user == occupant) + return + + if(panel_open) + to_chat(usr, "Close the maintenance panel first.") + return + + tgui_interact(user) + +/obj/machinery/atmospherics/unary/cryo_cell/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Cryo", "Cryo Cell") // 520, 470 + ui.open() + +/obj/machinery/atmospherics/unary/cryo_cell/tgui_data(mob/user) + // this is the data which will be sent to the ui + var/data[0] + data["isOperating"] = on + data["hasOccupant"] = occupant ? TRUE : FALSE + + var/occupantData[0] + if(occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.getMaxHealth() + occupantData["minHealth"] = config.health_threshold_dead + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["bodyTemperature"] = occupant.bodytemperature + data["occupant"] = occupantData; + + data["cellTemperature"] = round(air_contents.temperature) + data["cellTemperatureStatus"] = "good" + if(air_contents.temperature > T0C) // if greater than 273.15 kelvin (0 celcius) + data["cellTemperatureStatus"] = "bad" + else if(air_contents.temperature > 225) + data["cellTemperatureStatus"] = "average" + + data["isBeakerLoaded"] = beaker ? TRUE : FALSE + data["beakerLabel"] = null + data["beakerVolume"] = 0 + if(beaker) + data["beakerLabel"] = beaker.label_text ? beaker.label_text : null + if(beaker.reagents && beaker.reagents.reagent_list.len) + for(var/datum/reagent/R in beaker.reagents.reagent_list) + data["beakerVolume"] += R.volume + + return data + +/obj/machinery/atmospherics/unary/cryo_cell/tgui_act(action, params) + if(..() || usr == occupant) + return TRUE + + . = TRUE + switch(action) + if("switchOn") + on = 1 + update_icon() + if("switchOff") + on = 0 + update_icon() + if("ejectBeaker") + if(beaker) + beaker.loc = get_step(src.loc, SOUTH) + beaker = null + update_icon() + if("ejectOccupant") + if(!occupant || isslime(usr) || ispAI(usr)) + return 0 // don't update UIs attached to this object + go_out() + else + return FALSE + + add_fingerprint(usr) + +/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/weapon/G as obj, var/mob/user as mob) + if(istype(G, /obj/item/weapon/reagent_containers/glass)) + if(beaker) + to_chat(user, "A beaker is already loaded into the machine.") + return + + beaker = G + user.drop_item() + G.loc = src + user.visible_message("[user] adds \a [G] to \the [src]!", "You add \a [G] to \the [src]!") + SStgui.update_uis(src) + update_icon() + else if(istype(G, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/grab = G + if(!ismob(grab.affecting)) + return + if(occupant) + to_chat(user,"\The [src] is already occupied by [occupant].") + if(grab.affecting.has_buckled_mobs()) + to_chat(user, span("warning", "\The [grab.affecting] has other entities attached to it. Remove them first.")) + return + var/mob/M = grab.affecting + qdel(grab) + put_mob(M) + + return + +/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(var/mob/target, var/mob/user) //Allows borgs to put people into cryo without external assistance + if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)|| !ishuman(target)) + return + put_mob(target) + +/obj/machinery/atmospherics/unary/cryo_cell/update_icon() + cut_overlay(fluid) + fluid.color = null + if(on) + if(beaker) + fluid.color = beaker.reagents.get_color() + add_overlay(fluid) + +/obj/machinery/atmospherics/unary/cryo_cell/proc/process_occupant() + if(air_contents.total_moles < 10) + return + if(occupant) + if(occupant.stat >= DEAD) + return + occupant.bodytemperature += 2*(air_contents.temperature - occupant.bodytemperature)*current_heat_capacity/(current_heat_capacity + air_contents.heat_capacity()) + occupant.bodytemperature = max(occupant.bodytemperature, air_contents.temperature) // this is so ugly i'm sorry for doing it i'll fix it later i promise + occupant.set_stat(UNCONSCIOUS) + occupant.dir = SOUTH + if(occupant.bodytemperature < T0C) + occupant.Sleeping(max(5, (1/occupant.bodytemperature)*2000)) + occupant.Paralyse(max(5, (1/occupant.bodytemperature)*3000)) + if(air_contents.gas["oxygen"] > 2) + if(occupant.getOxyLoss()) occupant.adjustOxyLoss(-1) + else + occupant.adjustOxyLoss(-1) + //severe damage should heal waaay slower without proper chemicals + if(occupant.bodytemperature < 225) + if(occupant.getToxLoss()) + occupant.adjustToxLoss(max(-1, -20/occupant.getToxLoss())) + if(occupant.radiation || occupant.accumulated_rads) + occupant.radiation -= 25 + occupant.accumulated_rads -= 25 + var/heal_brute = occupant.getBruteLoss() ? min(1, 20/occupant.getBruteLoss()) : 0 + var/heal_fire = occupant.getFireLoss() ? min(1, 20/occupant.getFireLoss()) : 0 + occupant.heal_organ_damage(heal_brute,heal_fire) + var/has_cryo = occupant.reagents.get_reagent_amount("cryoxadone") >= 1 + var/has_clonexa = occupant.reagents.get_reagent_amount("clonexadone") >= 1 + var/has_cryo_medicine = has_cryo || has_clonexa + if(beaker && !has_cryo_medicine) + beaker.reagents.trans_to_mob(occupant, 1, CHEM_BLOOD, 10) + +/obj/machinery/atmospherics/unary/cryo_cell/proc/heat_gas_contents() + if(air_contents.total_moles < 1) + return + var/air_heat_capacity = air_contents.heat_capacity() + var/combined_heat_capacity = current_heat_capacity + air_heat_capacity + if(combined_heat_capacity > 0) + var/combined_energy = T20C*current_heat_capacity + air_heat_capacity*air_contents.temperature + air_contents.temperature = combined_energy/combined_heat_capacity + +/obj/machinery/atmospherics/unary/cryo_cell/proc/expel_gas() + if(air_contents.total_moles < 1) + return +// var/datum/gas_mixture/expel_gas = new +// var/remove_amount = air_contents.total_moles()/50 +// expel_gas = air_contents.remove(remove_amount) + + // Just have the gas disappear to nowhere. + //expel_gas.temperature = T20C // Lets expel hot gas and see if that helps people not die as they are removed + //loc.assume_air(expel_gas) + +/obj/machinery/atmospherics/unary/cryo_cell/proc/go_out() + if(!(occupant)) + return + //for(var/obj/O in src) + // O.loc = src.loc + if(occupant.client) + occupant.client.eye = occupant.client.mob + occupant.client.perspective = MOB_PERSPECTIVE + vis_contents -= occupant + occupant.pixel_x = occupant.default_pixel_x + occupant.pixel_y = occupant.default_pixel_y + occupant.loc = get_step(src.loc, SOUTH) //this doesn't account for walls or anything, but i don't forsee that being a problem. + if(occupant.bodytemperature < 261 && occupant.bodytemperature >= 70) //Patch by Aranclanos to stop people from taking burn damage after being ejected + occupant.bodytemperature = 261 // Changed to 70 from 140 by Zuhayr due to reoccurance of bug. + unbuckle_mob(occupant, force = TRUE) + occupant = null + current_heat_capacity = initial(current_heat_capacity) + update_use_power(USE_POWER_IDLE) + SStgui.update_uis(src) + return + +/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob) + if(stat & (NOPOWER|BROKEN)) + to_chat(usr, "The cryo cell is not functioning.") + return + if(!istype(M)) + to_chat(usr, "The cryo cell cannot handle such a lifeform!") + return + if(occupant) + to_chat(usr, "The cryo cell is already occupied!") + return + if(M.abiotic()) + to_chat(usr, "Subject may not have abiotic items on.") + return + if(!node) + to_chat(usr, "The cell is not correctly connected to its pipe network!") + return + if(M.client) + M.client.perspective = EYE_PERSPECTIVE + M.client.eye = src + M.stop_pulling() + M.loc = src + M.ExtinguishMob() + if(M.health > -100 && (M.health < 0 || M.sleeping)) + to_chat(M, "You feel a cold liquid surround you. Your skin starts to freeze up.") + occupant = M + buckle_mob(occupant, forced = TRUE, check_loc = FALSE) + vis_contents |= occupant + occupant.pixel_y += 19 + current_heat_capacity = HEAT_CAPACITY_HUMAN + update_use_power(USE_POWER_ACTIVE) +// M.metabslow = 1 + add_fingerprint(usr) + update_icon() + SStgui.update_uis(src) + return 1 + +/obj/machinery/atmospherics/unary/cryo_cell/verb/move_eject() + set name = "Eject occupant" + set category = "Object" + set src in oview(1) + if(usr == occupant)//If the user is inside the tube... + if(usr.stat == 2)//and he's not dead.... + return + to_chat(usr, "Release sequence activated. This will take two minutes.") + sleep(1200) + if(!src || !usr || !occupant || (occupant != usr)) //Check if someone's released/replaced/bombed him already + return + go_out()//and release him from the eternal prison. + else + if(usr.stat != 0) + return + go_out() + add_fingerprint(usr) + return + +/obj/machinery/atmospherics/unary/cryo_cell/verb/move_inside() + set name = "Move Inside" + set category = "Object" + set src in oview(1) + if(isliving(usr)) + var/mob/living/L = usr + if(L.has_buckled_mobs()) + to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first.")) + return + if(L.stat != CONSCIOUS) + return + put_mob(L) + +/atom/proc/return_air_for_internal_lifeform(var/mob/living/lifeform) + return return_air() + +/obj/machinery/atmospherics/unary/cryo_cell/return_air_for_internal_lifeform() + //assume that the cryo cell has some kind of breath mask or something that + //draws from the cryo tube's environment, instead of the cold internal air. + if(src.loc) + return loc.return_air() + else + return null + +/datum/data/function/proc/reset() + return + +/datum/data/function/proc/r_input(href, href_list, mob/user as mob) + return + +/datum/data/function/proc/display() + return diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm index 63beb677978..54c1b38ca05 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -1,148 +1,148 @@ -/* -CONTAINS: -Deployable items -*/ - -/obj/machinery/deployable - name = "deployable" - desc = "deployable" - icon = 'icons/obj/objects.dmi' - req_access = list(access_security)//I'm changing this until these are properly tested./N - -/obj/machinery/deployable/barrier - name = "deployable barrier" - desc = "A deployable barrier. Swipe your ID card to lock/unlock it." - icon = 'icons/obj/objects.dmi' - anchored = FALSE - density = TRUE - icon_state = "barrier0" - var/health = 100.0 - var/maxhealth = 100.0 - var/locked = 0.0 -// req_access = list(access_maint_tunnels) - -/obj/machinery/deployable/barrier/New() - ..() - - icon_state = "barrier[locked]" - -/obj/machinery/deployable/barrier/attackby(obj/item/weapon/W as obj, mob/user as mob) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - if(istype(W, /obj/item/weapon/card/id/)) - if(allowed(user)) - if (emagged < 2.0) - locked = !locked - anchored = !anchored - icon_state = "barrier[locked]" - if((locked == 1.0) && (emagged < 2.0)) - to_chat(user, "Barrier lock toggled on.") - return - else if((locked == 0.0) && (emagged < 2.0)) - to_chat(user, "Barrier lock toggled off.") - return - else - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(2, 1, src) - s.start() - visible_message("BZZzZZzZZzZT") - return - return - else if(W.has_tool_quality(TOOL_WRENCH)) - if(health < maxhealth) - health = maxhealth - emagged = 0 - req_access = list(access_security) - visible_message("[user] repairs \the [src]!") - return - else if(emagged > 0) - emagged = 0 - req_access = list(access_security) - visible_message("[user] repairs \the [src]!") - return - return - else - switch(W.damtype) - if("fire") - health -= W.force * 0.75 - if("brute") - health -= W.force * 0.5 - playsound(src, 'sound/weapons/smash.ogg', 50, 1) - CheckHealth() - ..() - -/obj/machinery/deployable/barrier/proc/CheckHealth() - if(health <= 0) - explode() - return - -/obj/machinery/deployable/barrier/attack_generic(var/mob/user, var/damage, var/attack_verb) - visible_message("[user] [attack_verb] the [src]!") - playsound(src, 'sound/weapons/smash.ogg', 50, 1) - user.do_attack_animation(src) - health -= damage - CheckHealth() - return - -/obj/machinery/deployable/barrier/take_damage(var/damage) - health -= damage - CheckHealth() - return - -/obj/machinery/deployable/barrier/ex_act(severity) - switch(severity) - if(1.0) - explode() - return - if(2.0) - health -= 25 - CheckHealth() - return - -/obj/machinery/deployable/barrier/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return - if(prob(50/severity)) - locked = !locked - anchored = !anchored - icon_state = "barrier[locked]" - -/obj/machinery/deployable/barrier/CanPass(atom/movable/mover, turf/target)//So bullets will fly over and stuff. - if(istype(mover) && mover.checkpass(PASSTABLE)) - return TRUE - return FALSE - -/obj/machinery/deployable/barrier/proc/explode() - - visible_message("[src] blows apart!") - var/turf/Tsec = get_turf(src) - -/* var/obj/item/stack/rods/ =*/ - new /obj/item/stack/rods(Tsec) - - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(3, 1, src) - s.start() - - explosion(src.loc,-1,-1,0) - if(src) - qdel(src) - -/obj/machinery/deployable/barrier/emag_act(var/remaining_charges, var/mob/user) - if(emagged == 0) - emagged = 1 - LAZYCLEARLIST(req_access) - LAZYCLEARLIST(req_one_access) - to_chat(user, "You break the ID authentication lock on \the [src].") - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(2, 1, src) - s.start() - visible_message("BZZzZZzZZzZT") - return 1 - else if(emagged == 1) - emagged = 2 - to_chat(user, "You short out the anchoring mechanism on \the [src].") - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(2, 1, src) - s.start() - visible_message("BZZzZZzZZzZT") +/* +CONTAINS: +Deployable items +*/ + +/obj/machinery/deployable + name = "deployable" + desc = "deployable" + icon = 'icons/obj/objects.dmi' + req_access = list(access_security)//I'm changing this until these are properly tested./N + +/obj/machinery/deployable/barrier + name = "deployable barrier" + desc = "A deployable barrier. Swipe your ID card to lock/unlock it." + icon = 'icons/obj/objects.dmi' + anchored = FALSE + density = TRUE + icon_state = "barrier0" + var/health = 100.0 + var/maxhealth = 100.0 + var/locked = 0.0 +// req_access = list(access_maint_tunnels) + +/obj/machinery/deployable/barrier/New() + ..() + + icon_state = "barrier[locked]" + +/obj/machinery/deployable/barrier/attackby(obj/item/weapon/W as obj, mob/user as mob) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(istype(W, /obj/item/weapon/card/id/)) + if(allowed(user)) + if (emagged < 2.0) + locked = !locked + anchored = !anchored + icon_state = "barrier[locked]" + if((locked == 1.0) && (emagged < 2.0)) + to_chat(user, "Barrier lock toggled on.") + return + else if((locked == 0.0) && (emagged < 2.0)) + to_chat(user, "Barrier lock toggled off.") + return + else + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(2, 1, src) + s.start() + visible_message("BZZzZZzZZzZT") + return + return + else if(W.has_tool_quality(TOOL_WRENCH)) + if(health < maxhealth) + health = maxhealth + emagged = 0 + req_access = list(access_security) + visible_message("[user] repairs \the [src]!") + return + else if(emagged > 0) + emagged = 0 + req_access = list(access_security) + visible_message("[user] repairs \the [src]!") + return + return + else + switch(W.damtype) + if("fire") + health -= W.force * 0.75 + if("brute") + health -= W.force * 0.5 + playsound(src, 'sound/weapons/smash.ogg', 50, 1) + CheckHealth() + ..() + +/obj/machinery/deployable/barrier/proc/CheckHealth() + if(health <= 0) + explode() + return + +/obj/machinery/deployable/barrier/attack_generic(var/mob/user, var/damage, var/attack_verb) + visible_message("[user] [attack_verb] the [src]!") + playsound(src, 'sound/weapons/smash.ogg', 50, 1) + user.do_attack_animation(src) + health -= damage + CheckHealth() + return + +/obj/machinery/deployable/barrier/take_damage(var/damage) + health -= damage + CheckHealth() + return + +/obj/machinery/deployable/barrier/ex_act(severity) + switch(severity) + if(1.0) + explode() + return + if(2.0) + health -= 25 + CheckHealth() + return + +/obj/machinery/deployable/barrier/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return + if(prob(50/severity)) + locked = !locked + anchored = !anchored + icon_state = "barrier[locked]" + +/obj/machinery/deployable/barrier/CanPass(atom/movable/mover, turf/target)//So bullets will fly over and stuff. + if(istype(mover) && mover.checkpass(PASSTABLE)) + return TRUE + return FALSE + +/obj/machinery/deployable/barrier/proc/explode() + + visible_message("[src] blows apart!") + var/turf/Tsec = get_turf(src) + +/* var/obj/item/stack/rods/ =*/ + new /obj/item/stack/rods(Tsec) + + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + + explosion(src.loc,-1,-1,0) + if(src) + qdel(src) + +/obj/machinery/deployable/barrier/emag_act(var/remaining_charges, var/mob/user) + if(emagged == 0) + emagged = 1 + LAZYCLEARLIST(req_access) + LAZYCLEARLIST(req_one_access) + to_chat(user, "You break the ID authentication lock on \the [src].") + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(2, 1, src) + s.start() + visible_message("BZZzZZzZZzZT") + return 1 + else if(emagged == 1) + emagged = 2 + to_chat(user, "You short out the anchoring mechanism on \the [src].") + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(2, 1, src) + s.start() + visible_message("BZZzZZzZZzZT") return 1 \ No newline at end of file diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm index 6e5618d39e0..3d4c0382ce3 100644 --- a/code/game/machinery/door_control.dm +++ b/code/game/machinery/door_control.dm @@ -1,223 +1,223 @@ -/obj/machinery/button/remote - name = "remote object control" - desc = "It controls objects, remotely." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "doorctrl0" - power_channel = ENVIRON - layer = ABOVE_WINDOW_LAYER - var/desiredstate = 0 - var/exposedwires = 0 - var/wires = 3 - /* - Bitflag, 1=checkID - 2=Network Access - */ - - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/button/remote/attack_ai(mob/user as mob) - if(wires & 2) - return attack_hand(user) - else - to_chat(user, "Error, no route to host.") - -/obj/machinery/button/remote/attackby(obj/item/weapon/W, mob/user as mob) - return attack_hand(user) - -/obj/machinery/button/remote/emag_act(var/remaining_charges, var/mob/user) - if(LAZYLEN(req_access) || LAZYLEN(req_one_access)) - LAZYCLEARLIST(req_access) - LAZYCLEARLIST(req_one_access) - playsound(src, "sparks", 100, 1) - return 1 - -/obj/machinery/button/remote/attack_hand(mob/user as mob) - if(..()) - return - - add_fingerprint(user) - if(stat & (NOPOWER|BROKEN)) - return - - if(!allowed(user) && (wires & 1)) - to_chat(user, "Access Denied") - flick("doorctrl-denied",src) - return - - use_power(5) - icon_state = "doorctrl1" - desiredstate = !desiredstate - trigger(user) - spawn(15) - update_icon() - -/obj/machinery/button/remote/proc/trigger() - return - -/obj/machinery/button/remote/power_change() - ..() - update_icon() - -/obj/machinery/button/remote/update_icon() - if(stat & NOPOWER) - icon_state = "doorctrl-p" - else - icon_state = "doorctrl0" - -/* - Airlock remote control -*/ - -// Bitmasks for door switches. -#define OPEN 0x1 -#define IDSCAN 0x2 -#define BOLTS 0x4 -#define SHOCK 0x8 -#define SAFE 0x10 - -/obj/machinery/button/remote/airlock - icon = 'icons/obj/stationobjs_vr.dmi' // VOREStation Edit - name = "remote door-control" - desc = "It controls doors, remotely." - - var/specialfunctions = 1 - /* - Bitflag, 1= open - 2= idscan, - 4= bolts - 8= shock - 16= door safties - */ - -/obj/machinery/button/remote/airlock/trigger() - for(var/obj/machinery/door/airlock/D in machines) - if(D.id_tag == id) - if(specialfunctions & OPEN) - if(D.density) - spawn(0) - D.open() - return - else - spawn(0) - D.close() - return - if(desiredstate == 1) - if(specialfunctions & IDSCAN) - D.set_idscan(0) - if(specialfunctions & BOLTS) - D.lock() - if(specialfunctions & SHOCK) - D.electrify(-1) - if(specialfunctions & SAFE) - D.set_safeties(0) - else - if(specialfunctions & IDSCAN) - D.set_idscan(1) - if(specialfunctions & BOLTS) - D.unlock() - if(specialfunctions & SHOCK) - D.electrify(0) - if(specialfunctions & SAFE) - D.set_safeties(1) - -#undef OPEN -#undef IDSCAN -#undef BOLTS -#undef SHOCK -#undef SAFE - -/* - Blast door remote control -*/ -/obj/machinery/button/remote/blast_door - icon = 'icons/obj/stationobjs_vr.dmi' - name = "remote blast door-control" - desc = "It controls blast doors, remotely." - -/obj/machinery/button/remote/blast_door/trigger() - for(var/obj/machinery/door/blast/M in machines) - if(M.id == id) - if(M.density) - spawn(0) - M.open() - return - else - spawn(0) - M.close() - return - -/* - Emitter remote control -*/ -/obj/machinery/button/remote/emitter - name = "remote emitter control" - desc = "It controls emitters, remotely." - -/obj/machinery/button/remote/emitter/trigger(mob/user as mob) - for(var/obj/machinery/power/emitter/E in machines) - if(E.id == id) - spawn(0) - E.activate(user) - return - -/* - Mass driver remote control -*/ -/obj/machinery/button/remote/driver - name = "mass driver button" - desc = "A remote control switch for a mass driver." - icon = 'icons/obj/objects.dmi' - icon_state = "launcherbtt" - -/obj/machinery/button/remote/driver/trigger(mob/user as mob) - active = 1 - update_icon() - - for(var/obj/machinery/door/blast/M in machines) - if(M.id == id) - spawn(0) - M.open() - return - - sleep(20) - - for(var/obj/machinery/mass_driver/M in machines) - if(M.id == id) - M.drive() - - sleep(50) - - for(var/obj/machinery/door/blast/M in machines) - if(M.id == id) - spawn(0) - M.close() - return - - icon_state = "launcherbtt" - update_icon() - - return - -/obj/machinery/button/remote/driver/update_icon() - if(!active || (stat & NOPOWER)) - icon_state = "launcherbtt" - else - icon_state = "launcheract" - -/* - Shieldgen remote control -*/ -/obj/machinery/button/remote/shields - name = "remote shield control" - desc = "It controls shields, remotely." - icon = 'icons/obj/stationobjs_vr.dmi' // VOREStation Edit - -/obj/machinery/button/remote/shields/trigger(var/mob/user) - for(var/obj/machinery/shield_gen/SG in machines) - if(SG.id == id) - spawn(0) - if(SG?.anchored) - SG.toggle() +/obj/machinery/button/remote + name = "remote object control" + desc = "It controls objects, remotely." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "doorctrl0" + power_channel = ENVIRON + layer = ABOVE_WINDOW_LAYER + var/desiredstate = 0 + var/exposedwires = 0 + var/wires = 3 + /* + Bitflag, 1=checkID + 2=Network Access + */ + + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/button/remote/attack_ai(mob/user as mob) + if(wires & 2) + return attack_hand(user) + else + to_chat(user, "Error, no route to host.") + +/obj/machinery/button/remote/attackby(obj/item/weapon/W, mob/user as mob) + return attack_hand(user) + +/obj/machinery/button/remote/emag_act(var/remaining_charges, var/mob/user) + if(LAZYLEN(req_access) || LAZYLEN(req_one_access)) + LAZYCLEARLIST(req_access) + LAZYCLEARLIST(req_one_access) + playsound(src, "sparks", 100, 1) + return 1 + +/obj/machinery/button/remote/attack_hand(mob/user as mob) + if(..()) + return + + add_fingerprint(user) + if(stat & (NOPOWER|BROKEN)) + return + + if(!allowed(user) && (wires & 1)) + to_chat(user, "Access Denied") + flick("doorctrl-denied",src) + return + + use_power(5) + icon_state = "doorctrl1" + desiredstate = !desiredstate + trigger(user) + spawn(15) + update_icon() + +/obj/machinery/button/remote/proc/trigger() + return + +/obj/machinery/button/remote/power_change() + ..() + update_icon() + +/obj/machinery/button/remote/update_icon() + if(stat & NOPOWER) + icon_state = "doorctrl-p" + else + icon_state = "doorctrl0" + +/* + Airlock remote control +*/ + +// Bitmasks for door switches. +#define OPEN 0x1 +#define IDSCAN 0x2 +#define BOLTS 0x4 +#define SHOCK 0x8 +#define SAFE 0x10 + +/obj/machinery/button/remote/airlock + icon = 'icons/obj/stationobjs_vr.dmi' // VOREStation Edit + name = "remote door-control" + desc = "It controls doors, remotely." + + var/specialfunctions = 1 + /* + Bitflag, 1= open + 2= idscan, + 4= bolts + 8= shock + 16= door safties + */ + +/obj/machinery/button/remote/airlock/trigger() + for(var/obj/machinery/door/airlock/D in machines) + if(D.id_tag == id) + if(specialfunctions & OPEN) + if(D.density) + spawn(0) + D.open() + return + else + spawn(0) + D.close() + return + if(desiredstate == 1) + if(specialfunctions & IDSCAN) + D.set_idscan(0) + if(specialfunctions & BOLTS) + D.lock() + if(specialfunctions & SHOCK) + D.electrify(-1) + if(specialfunctions & SAFE) + D.set_safeties(0) + else + if(specialfunctions & IDSCAN) + D.set_idscan(1) + if(specialfunctions & BOLTS) + D.unlock() + if(specialfunctions & SHOCK) + D.electrify(0) + if(specialfunctions & SAFE) + D.set_safeties(1) + +#undef OPEN +#undef IDSCAN +#undef BOLTS +#undef SHOCK +#undef SAFE + +/* + Blast door remote control +*/ +/obj/machinery/button/remote/blast_door + icon = 'icons/obj/stationobjs_vr.dmi' + name = "remote blast door-control" + desc = "It controls blast doors, remotely." + +/obj/machinery/button/remote/blast_door/trigger() + for(var/obj/machinery/door/blast/M in machines) + if(M.id == id) + if(M.density) + spawn(0) + M.open() + return + else + spawn(0) + M.close() + return + +/* + Emitter remote control +*/ +/obj/machinery/button/remote/emitter + name = "remote emitter control" + desc = "It controls emitters, remotely." + +/obj/machinery/button/remote/emitter/trigger(mob/user as mob) + for(var/obj/machinery/power/emitter/E in machines) + if(E.id == id) + spawn(0) + E.activate(user) + return + +/* + Mass driver remote control +*/ +/obj/machinery/button/remote/driver + name = "mass driver button" + desc = "A remote control switch for a mass driver." + icon = 'icons/obj/objects.dmi' + icon_state = "launcherbtt" + +/obj/machinery/button/remote/driver/trigger(mob/user as mob) + active = 1 + update_icon() + + for(var/obj/machinery/door/blast/M in machines) + if(M.id == id) + spawn(0) + M.open() + return + + sleep(20) + + for(var/obj/machinery/mass_driver/M in machines) + if(M.id == id) + M.drive() + + sleep(50) + + for(var/obj/machinery/door/blast/M in machines) + if(M.id == id) + spawn(0) + M.close() + return + + icon_state = "launcherbtt" + update_icon() + + return + +/obj/machinery/button/remote/driver/update_icon() + if(!active || (stat & NOPOWER)) + icon_state = "launcherbtt" + else + icon_state = "launcheract" + +/* + Shieldgen remote control +*/ +/obj/machinery/button/remote/shields + name = "remote shield control" + desc = "It controls shields, remotely." + icon = 'icons/obj/stationobjs_vr.dmi' // VOREStation Edit + +/obj/machinery/button/remote/shields/trigger(var/mob/user) + for(var/obj/machinery/shield_gen/SG in machines) + if(SG.id == id) + spawn(0) + if(SG?.anchored) + SG.toggle() diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index a177ad3a892..c5680602ddc 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1,1582 +1,1582 @@ -//VOREStation Edit - Redone a lot of airlock things: -/* -- Specific department maintenance doors -- Named doors properly according to type -- Gave them default access levels with the access constants -- Improper'd all of the names in the new() -*/ - -/obj/machinery/door/airlock - name = "Airlock" - description_info = "If you hold left alt whilst left-clicking on an airlock, you can ring the doorbell to announce your presence to anyone on the other side! Alternately if you are on HARM intent when doing this, you will bang loudly on the door!

    AIs and Cyborgs can also quickly open/close, bolt/unbolt, and electrify/de-electrify doors at a distance by holding left shift, left control, or left alt respectively whilst left-clicking." - icon = 'icons/obj/doors/Doorint.dmi' - icon_state = "door_closed" - power_channel = ENVIRON - - explosion_resistance = 10 - - // Doors do their own stuff - bullet_vulnerability = 0 - - blocks_emissive = EMISSIVE_BLOCK_GENERIC // Not quite as nice as /tg/'s custom masks. We should make those sometime - - var/aiControlDisabled = 0 //If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in. - var/hackProof = 0 // if 1, this door can't be hacked by the AI - var/electrified_until = 0 //World time when the door is no longer electrified. -1 if it is permanently electrified until someone fixes it. - var/main_power_lost_until = 0 //World time when main power is restored. - var/backup_power_lost_until = -1 //World time when backup power is restored. - var/has_beeped = 0 //If 1, will not beep on failed closing attempt. Resets when door closes. - var/spawnPowerRestoreRunning = 0 - var/welded = null - var/locked = 0 - var/lights = 1 // bolt lights show by default - var/aiDisabledIdScanner = 0 - var/aiHacking = 0 - var/obj/machinery/door/airlock/closeOther = null - var/closeOtherId = null - var/lockdownbyai = 0 - autoclose = 1 - var/assembly_type = /obj/structure/door_assembly - var/mineral = null - var/justzap = 0 - var/safe = 1 - normalspeed = 1 - var/obj/item/weapon/airlock_electronics/electronics = null - var/hasShocked = 0 //Prevents multiple shocks from happening - var/secured_wires = 0 - var/security_level = 1 //VOREStation Addition - acts as a multiplier on the time required to hack an airlock with a hacktool - var/datum/wires/airlock/wires = null - - var/open_sound_powered = 'sound/machines/door/covert1o.ogg' - var/open_sound_unpowered = 'sound/machines/door/airlockforced.ogg' - var/close_sound_powered = 'sound/machines/door/covert1c.ogg' - var/legacy_open_powered = 'sound/machines/door/old_airlock.ogg' - var/legacy_close_powered = 'sound/machines/door/old_airlockclose.ogg' - var/department_open_powered = null - var/department_close_powered = null - var/denied_sound = 'sound/machines/deniedbeep.ogg' - var/bolt_up_sound = 'sound/machines/door/boltsup.ogg' - var/bolt_down_sound = 'sound/machines/door/boltsdown.ogg' - var/knock_sound = 'sound/machines/2beeplow.ogg' - var/knock_hammer_sound = 'sound/weapons/sonic_jackhammer.ogg' - var/knock_unpowered_sound = 'sound/machines/door/knock_glass.ogg' - -/obj/machinery/door/airlock/attack_generic(var/mob/living/user, var/damage) - if(stat & (BROKEN|NOPOWER)) - if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) - if(src.locked || src.welded) - visible_message("\The [user] begins breaking into \the [src] internals!") - user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything. - if(do_after(user,10 SECONDS,src)) - src.locked = 0 - src.welded = 0 - update_icon() - open(1) - if(prob(25)) - src.shock(user, 100) - user.set_AI_busy(FALSE) - else if(src.density) - visible_message("\The [user] forces \the [src] open!") - open(1) - else - visible_message("\The [user] forces \the [src] closed!") - close(1) - else - visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") - return - ..() - -/obj/machinery/door/airlock/attack_alien(var/mob/user) //Familiar, right? Doors. -Mechoid - if(istype(user, /mob/living/carbon/human)) - var/mob/living/carbon/human/X = user - if(istype(X.species, /datum/species/xenos)) - if(src.locked || src.welded) - visible_message("\The [user] begins tearing into \the [src] internals!") - src.do_animate("deny") - if(do_after(user,15 SECONDS,src)) - visible_message("\The [user] tears \the [src] open, sparks flying from its electronics!") - src.do_animate("spark") - playsound(src, 'sound/machines/door/airlock_tear_apart.ogg', 100, 1, volume_channel = VOLUME_CHANNEL_DOORS) - src.locked = 0 - src.welded = 0 - update_icon() - open(1) - src.set_broken() //These aren't emags, these be CLAWS - else if(src.density) - visible_message("\The [user] begins forcing \the [src] open!") - if(do_after(user, 5 SECONDS,src)) - playsound(src, 'sound/machines/door/airlock_creaking.ogg', 100, 1, volume_channel = VOLUME_CHANNEL_DOORS) - visible_message("\The [user] forces \the [src] open!") - open(1) - else - visible_message("\The [user] forces \the [src] closed!") - close(1) - else - src.do_animate("deny") - visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") - return - ..() - -/obj/machinery/door/airlock/get_material() - if(mineral) - return get_material_by_name(mineral) - return get_material_by_name(MAT_STEEL) - -/obj/machinery/door/airlock/command - name = "Command Airlock" - icon = 'icons/obj/doors/Doorcom.dmi' - req_one_access = list(access_heads) - assembly_type = /obj/structure/door_assembly/door_assembly_com - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/cmd3o.ogg' - department_close_powered = 'sound/machines/door/cmd3c.ogg' - security_level = 3 //VOREStation Addition - -/obj/machinery/door/airlock/security - name = "Security Airlock" - icon = 'icons/obj/doors/Doorsec.dmi' - req_one_access = list(access_security) - assembly_type = /obj/structure/door_assembly/door_assembly_sec - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/sec1o.ogg' - department_close_powered = 'sound/machines/door/sec1c.ogg' - security_level = 2 //VOREStation Addition - -/obj/machinery/door/airlock/engineering - name = "Engineering Airlock" - icon = 'icons/obj/doors/Dooreng.dmi' - req_one_access = list(access_engine) - assembly_type = /obj/structure/door_assembly/door_assembly_eng - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/eng1o.ogg' - department_close_powered = 'sound/machines/door/eng1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/engineeringatmos - name = "Atmospherics Airlock" - icon = 'icons/obj/doors/Doorengatmos.dmi' - req_one_access = list(access_atmospherics) - assembly_type = /obj/structure/door_assembly/door_assembly_eat - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/eng1o.ogg' - department_close_powered = 'sound/machines/door/eng1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/medical - name = "Medical Airlock" - icon = 'icons/obj/doors/Doormed.dmi' - req_one_access = list(access_medical) - assembly_type = /obj/structure/door_assembly/door_assembly_med - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/med1o.ogg' - department_close_powered = 'sound/machines/door/med1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/maintenance - name = "Maintenance Access" - icon = 'icons/obj/doors/Doormaint.dmi' - //req_one_access = list(access_maint_tunnels) //VOREStation Edit - Maintenance is open access - assembly_type = /obj/structure/door_assembly/door_assembly_mai - open_sound_powered = 'sound/machines/door/door2o.ogg' - close_sound_powered = 'sound/machines/door/door2c.ogg' - -/obj/machinery/door/airlock/maintenance/cargo - icon = 'icons/obj/doors/Doormaint_cargo.dmi' - req_one_access = list(access_cargo) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/door2o.ogg' - department_close_powered = 'sound/machines/door/door2c.ogg' - -/obj/machinery/door/airlock/maintenance/command - icon = 'icons/obj/doors/Doormaint_command.dmi' - req_one_access = list(access_heads) - -/obj/machinery/door/airlock/maintenance/common - icon = 'icons/obj/doors/Doormaint_common.dmi' - open_sound_powered = 'sound/machines/door/hall3o.ogg' - close_sound_powered = 'sound/machines/door/hall3c.ogg' - -/obj/machinery/door/airlock/maintenance/engi - icon = 'icons/obj/doors/Doormaint_engi.dmi' - req_one_access = list(access_engine) - -/obj/machinery/door/airlock/maintenance/int - icon = 'icons/obj/doors/Doormaint_int.dmi' - -/obj/machinery/door/airlock/maintenance/medical - icon = 'icons/obj/doors/Doormaint_med.dmi' - req_one_access = list(access_medical) - -/obj/machinery/door/airlock/maintenance/rnd - icon = 'icons/obj/doors/Doormaint_rnd.dmi' - req_one_access = list(access_research) - -/obj/machinery/door/airlock/maintenance/sec - icon = 'icons/obj/doors/Doormaint_sec.dmi' - req_one_access = list(access_security) - -/obj/machinery/door/airlock/external - name = "External Airlock" - icon = 'icons/obj/doors/Doorext.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_ext - open_sound_powered = 'sound/machines/door/space1o.ogg' - close_sound_powered = 'sound/machines/door/space1c.ogg' - -/obj/machinery/door/airlock/external/bolted - icon_state = "door_locked" // So it looks visibly bolted in map editor - locked = 1 - -// For convenience in making docking ports: one that is pre-bolted with frequency set! -/obj/machinery/door/airlock/external/bolted/cycling - frequency = 1379 - -/obj/machinery/door/airlock/glass_external - name = "External Airlock" - icon = 'icons/obj/doors/Doorextglass.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_ext - opacity = 0 - glass = 1 - req_one_access = list(access_external_airlocks) - open_sound_powered = 'sound/machines/door/space1o.ogg' - close_sound_powered = 'sound/machines/door/space1c.ogg' - -/obj/machinery/door/airlock/glass - name = "Glass Airlock" - icon = 'icons/obj/doors/Doorglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - open_sound_powered = 'sound/machines/door/hall1o.ogg' - close_sound_powered = 'sound/machines/door/hall1c.ogg' - legacy_open_powered = 'sound/machines/door/windowdoor.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - glass = 1 - -/obj/machinery/door/airlock/centcom - name = "Centcom Airlock" - icon = 'icons/obj/doors/Doorele.dmi' - req_one_access = list(access_cent_general) - opacity = 1 - open_sound_powered = 'sound/machines/door/cmd3o.ogg' - close_sound_powered = 'sound/machines/door/cmd3c.ogg' - security_level = 100 //VOREStation Addition - -/obj/machinery/door/airlock/glass_centcom - name = "Airlock" - icon = 'icons/obj/doors/Dooreleglass.dmi' - opacity = 0 - glass = 1 - open_sound_powered = 'sound/machines/door/cmd3o.ogg' - close_sound_powered = 'sound/machines/door/cmd3c.ogg' - security_level = 100 //VOREStation Addition - -/obj/machinery/door/airlock/vault - name = "Vault" - icon = 'icons/obj/doors/vault.dmi' - explosion_resistance = 20 - opacity = 1 - secured_wires = 1 - assembly_type = /obj/structure/door_assembly/door_assembly_highsecurity //Until somebody makes better sprites. - req_one_access = list(access_heads_vault) - open_sound_powered = 'sound/machines/door/vault1o.ogg' - close_sound_powered = 'sound/machines/door/vault1c.ogg' - security_level = 5 //VOREStation Addition - -/obj/machinery/door/airlock/vault/bolted - icon_state = "door_locked" - locked = 1 - -/obj/machinery/door/airlock/freezer - name = "Freezer Airlock" - icon = 'icons/obj/doors/Doorfreezer.dmi' - opacity = 1 - assembly_type = /obj/structure/door_assembly/door_assembly_fre - -/obj/machinery/door/airlock/hatch - name = "Airtight Hatch" - icon = 'icons/obj/doors/Doorhatchele.dmi' - explosion_resistance = 20 - opacity = 1 - assembly_type = /obj/structure/door_assembly/door_assembly_hatch - req_one_access = list(access_maint_tunnels) - open_sound_powered = 'sound/machines/door/hatchopen.ogg' - close_sound_powered = 'sound/machines/door/hatchclose.ogg' - open_sound_unpowered = 'sound/machines/door/hatchforced.ogg' - -/obj/machinery/door/airlock/maintenance_hatch - name = "Maintenance Hatch" - icon = 'icons/obj/doors/Doorhatchmaint2.dmi' - explosion_resistance = 20 - opacity = 1 - assembly_type = /obj/structure/door_assembly/door_assembly_mhatch - req_one_access = list(access_maint_tunnels) - open_sound_powered = 'sound/machines/door/hatchopen.ogg' - close_sound_powered = 'sound/machines/door/hatchclose.ogg' - open_sound_unpowered = 'sound/machines/door/hatchforced.ogg' - -/obj/machinery/door/airlock/glass_command - name = "Command Airlock" - icon = 'icons/obj/doors/Doorcomglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_com - glass = 1 - req_one_access = list(access_heads) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/cmd1o.ogg' - department_close_powered = 'sound/machines/door/cmd1c.ogg' - security_level = 3 //VOREStation Addition - -/obj/machinery/door/airlock/glass_engineering - name = "Engineering Airlock" - icon = 'icons/obj/doors/Doorengglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_eng - glass = 1 - req_one_access = list(access_engine) - department_open_powered = 'sound/machines/door/eng1o.ogg' - department_close_powered = 'sound/machines/door/eng1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/glass_engineeringatmos - name = "Atmospherics Airlock" - icon = 'icons/obj/doors/Doorengatmoglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_eat - glass = 1 - req_one_access = list(access_atmospherics) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/eng1o.ogg' - department_close_powered = 'sound/machines/door/eng1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/glass_security - name = "Security Airlock" - icon = 'icons/obj/doors/Doorsecglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_sec - glass = 1 - req_one_access = list(access_security) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/sec1o.ogg' - department_close_powered = 'sound/machines/door/sec1c.ogg' - security_level = 2 //VOREStation Addition - -/obj/machinery/door/airlock/glass_medical - name = "Medical Airlock" - icon = 'icons/obj/doors/Doormedglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_med - glass = 1 - req_one_access = list(access_medical) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/med1o.ogg' - department_close_powered = 'sound/machines/door/med1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/mining - name = "Mining Airlock" - icon = 'icons/obj/doors/Doormining.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_min - req_one_access = list(access_mining) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/cgo1o.ogg' - department_close_powered = 'sound/machines/door/cgo1c.ogg' - -/obj/machinery/door/airlock/atmos - name = "Atmospherics Airlock" - icon = 'icons/obj/doors/Dooratmo.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_atmo - req_one_access = list(access_atmospherics) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/eng1o.ogg' - department_close_powered = 'sound/machines/door/eng1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/research - name = "Research Airlock" - icon = 'icons/obj/doors/Doorresearch.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_research - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/sci1o.ogg' - department_close_powered = 'sound/machines/door/sci1c.ogg' - security_level = 2 //VOREStation Addition - -/obj/machinery/door/airlock/glass_research - name = "Research Airlock" - icon = 'icons/obj/doors/Doorresearchglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_research - glass = 1 - req_one_access = list(access_research) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/sci1o.ogg' - department_close_powered = 'sound/machines/door/sci1c.ogg' - security_level = 2 //VOREStation Addition - -/obj/machinery/door/airlock/glass_mining - name = "Mining Airlock" - icon = 'icons/obj/doors/Doorminingglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_min - glass = 1 - req_one_access = list(access_mining) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/cgo1o.ogg' - department_close_powered = 'sound/machines/door/cgo1c.ogg' - -/obj/machinery/door/airlock/glass_atmos - name = "Atmospherics Airlock" - icon = 'icons/obj/doors/Dooratmoglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_atmo - glass = 1 - req_one_access = list(access_atmospherics) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/eng1o.ogg' - department_close_powered = 'sound/machines/door/eng1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/gold - name = "Gold Airlock" - icon = 'icons/obj/doors/Doorgold.dmi' - mineral = "gold" - -/obj/machinery/door/airlock/silver - name = "Silver Airlock" - icon = 'icons/obj/doors/Doorsilver.dmi' - mineral = "silver" - -/obj/machinery/door/airlock/diamond - name = "Diamond Airlock" - icon = 'icons/obj/doors/Doordiamond.dmi' - mineral = "diamond" - -/obj/machinery/door/airlock/uranium - name = "Uranium Airlock" - desc = "And they said I was crazy." - icon = 'icons/obj/doors/Dooruranium.dmi' - mineral = "uranium" - var/last_event = 0 - var/rad_power = 7.5 - -/obj/machinery/door/airlock/process() - // Deliberate no call to parent. - if(main_power_lost_until > 0 && world.time >= main_power_lost_until) - regainMainPower() - - if(backup_power_lost_until > 0 && world.time >= backup_power_lost_until) - regainBackupPower() - - else if(electrified_until > 0 && world.time >= electrified_until) - electrify(0) - - if (..() == PROCESS_KILL && !(main_power_lost_until > 0 || backup_power_lost_until > 0 || electrified_until > 0)) - . = PROCESS_KILL - -/obj/machinery/door/airlock/uranium/process() - if(world.time > last_event+20) - if(prob(50)) - SSradiation.radiate(src, rad_power) - last_event = world.time - ..() - -/obj/machinery/door/airlock/phoron - name = "Phoron Airlock" - desc = "No way this can end badly." - icon = 'icons/obj/doors/Doorphoron.dmi' - mineral = "phoron" - -/obj/machinery/door/airlock/phoron/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature > 300) - PhoronBurn(exposed_temperature) - -/obj/machinery/door/airlock/phoron/proc/ignite(exposed_temperature) - if(exposed_temperature > 300) - PhoronBurn(exposed_temperature) - -/obj/machinery/door/airlock/phoron/proc/PhoronBurn(temperature) - for(var/turf/simulated/floor/target_tile in range(2,loc)) - target_tile.assume_gas("phoron", 35, 400+T0C) - spawn (0) target_tile.hotspot_expose(temperature, 400) - for(var/turf/simulated/wall/W in range(3,src)) - W.burn((temperature/4))//Added so that you can't set off a massive chain reaction with a small flame - for(var/obj/machinery/door/airlock/phoron/D in range(3,src)) - D.ignite(temperature/4) - new/obj/structure/door_assembly( src.loc ) - qdel(src) - -/obj/machinery/door/airlock/sandstone - name = "Sandstone Airlock" - icon = 'icons/obj/doors/Doorsand.dmi' - mineral = "sandstone" - -/obj/machinery/door/airlock/science - name = "Research Airlock" - icon = 'icons/obj/doors/Doorsci.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_science - req_one_access = list(access_research) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/sci1o.ogg' - department_close_powered = 'sound/machines/door/sci1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/glass_science - name = "Glass Airlocks" - icon = 'icons/obj/doors/Doorsciglass.dmi' - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_science - glass = 1 - req_one_access = list(access_research) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/sci1o.ogg' - department_close_powered = 'sound/machines/door/sci1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/highsecurity - name = "Secure Airlock" - icon = 'icons/obj/doors/hightechsecurity.dmi' - explosion_resistance = 20 - secured_wires = 1 - assembly_type = /obj/structure/door_assembly/door_assembly_highsecurity - req_one_access = list(access_heads_vault) - open_sound_powered = 'sound/machines/door/secure1o.ogg' - close_sound_powered = 'sound/machines/door/secure1c.ogg' - security_level = 4 //VOREStation Addition - -/obj/machinery/door/airlock/voidcraft - name = "voidcraft hatch" - desc = "It's an extra resilient airlock intended for spacefaring vessels." - icon = 'icons/obj/doors/shuttledoors.dmi' - explosion_resistance = 20 - opacity = 0 - glass = 1 - assembly_type = /obj/structure/door_assembly/door_assembly_voidcraft - open_sound_powered = 'sound/machines/door/shuttle1o.ogg' - close_sound_powered = 'sound/machines/door/shuttle1c.ogg' - -// Airlock opens from top-bottom instead of left-right. -/obj/machinery/door/airlock/voidcraft/vertical - icon = 'icons/obj/doors/shuttledoors_vertical.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_voidcraft/vertical - open_sound_powered = 'sound/machines/door/shuttle1o.ogg' - close_sound_powered = 'sound/machines/door/shuttle1c.ogg' - - -/datum/category_item/catalogue/anomalous/precursor_a/alien_airlock - name = "Precursor Alpha Object - Doors" - desc = "This object appears to be used in order to restrict or allow access to \ - rooms based on its physical state. In other words, a door. \ - Despite being designed and created by unknown ancient alien hands, this door has \ - a large number of similarities to the conventional airlock, such as being driven by \ - electricity, opening and closing by physically moving, and being air tight. \ - It also operates by responding to signals through internal electrical conduits. \ - These characteristics make it possible for one with experience with a multitool \ - to manipulate the door.\ -

    \ - The symbol on the door does not match any living species' patterns, giving further \ - implications that this door is very old, and yet it remains operational after \ - thousands of years. It is unknown if that is due to superb construction, or \ - unseen autonomous maintenance having been performed." - value = CATALOGUER_REWARD_EASY - -/obj/machinery/door/airlock/alien - name = "alien airlock" - desc = "You're fairly sure this is a door." - catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_airlock) - icon = 'icons/obj/doors/Dooralien.dmi' - explosion_resistance = 20 - secured_wires = TRUE - hackProof = TRUE - assembly_type = /obj/structure/door_assembly/door_assembly_alien - req_one_access = list(access_alien) - security_level = 100 //VOREStation Addition - -/obj/machinery/door/airlock/alien/locked - icon_state = "door_locked" - locked = TRUE - -/obj/machinery/door/airlock/alien/public // Entry to UFO. - req_one_access = list() - normalspeed = FALSE // So it closes faster and hopefully keeps the warm air inside. - hackProof = TRUE //VOREStation Edit - No borgos - -/* -About the new airlock wires panel: -* An airlock wire dialog can be accessed by the normal way or by using wirecutters or a multitool on the door while the wire-panel is open. This would show the following wires, which you can either wirecut/mend or send a multitool pulse through. There are 9 wires. -* one wire from the ID scanner. Sending a pulse through this flashes the red light on the door (if the door has power). If you cut this wire, the door will stop recognizing valid IDs. (If the door has 0000 access, it still opens and closes, though) -* two wires for power. Sending a pulse through either one causes a breaker to trip, disabling the door for 10 seconds if backup power is connected, or 1 minute if not (or until backup power comes back on, whichever is shorter). Cutting either one disables the main door power, but unless backup power is also cut, the backup power re-powers the door in 10 seconds. While unpowered, the door may be open, but bolts-raising will not work. Cutting these wires may electrocute the user. -* one wire for door bolts. Sending a pulse through this drops door bolts (whether the door is powered or not) or raises them (if it is). Cutting this wire also drops the door bolts, and mending it does not raise them. If the wire is cut, trying to raise the door bolts will not work. -* two wires for backup power. Sending a pulse through either one causes a breaker to trip, but this does not disable it unless main power is down too (in which case it is disabled for 1 minute or however long it takes main power to come back, whichever is shorter). Cutting either one disables the backup door power (allowing it to be crowbarred open, but disabling bolts-raising), but may electocute the user. -* one wire for opening the door. Sending a pulse through this while the door has power makes it open the door if no access is required. -* one wire for AI control. Sending a pulse through this blocks AI control for a second or so (which is enough to see the AI control light on the panel dialog go off and back on again). Cutting this prevents the AI from controlling the door unless it has hacked the door through the power connection (which takes about a minute). If both main and backup power are cut, as well as this wire, then the AI cannot operate or hack the door at all. -* one wire for electrifying the door. Sending a pulse through this electrifies the door for 30 seconds. Cutting this wire electrifies the door, so that the next person to touch the door without insulated gloves gets electrocuted. (Currently it is also STAYING electrified until someone mends the wire) -* one wire for controling door safetys. When active, door does not close on someone. When cut, door will ruin someone's shit. When pulsed, door will immedately ruin someone's shit. -* one wire for controlling door speed. When active, dor closes at normal rate. When cut, door does not close manually. When pulsed, door attempts to close every tick. -*/ - - - -/obj/machinery/door/airlock/bumpopen(mob/living/user as mob) //Airlocks now zap you when you 'bump' them open when they're electrified. --NeoFite - if(!issilicon(usr)) - if(src.isElectrified()) - if(!src.justzap) - if(src.shock(user, 100)) - src.justzap = 1 - spawn (10) - src.justzap = 0 - return - else /*if(src.justzap)*/ - return - else if(user.hallucination > 50 && prob(10) && src.operating == 0) - to_chat(user, "You feel a powerful shock course through your body!") - user.halloss += 10 - user.stunned += 10 - return - ..(user) - -/obj/machinery/door/airlock/proc/isElectrified() - if(src.electrified_until != 0) - return 1 - return 0 - -/obj/machinery/door/airlock/proc/canAIControl() - return ((src.aiControlDisabled!=1) && (!src.isAllPowerLoss())); - -/obj/machinery/door/airlock/proc/canAIHack() - return ((src.aiControlDisabled==1) && (!hackProof) && (!src.isAllPowerLoss())); - -/obj/machinery/door/airlock/proc/arePowerSystemsOn() - if (stat & (NOPOWER|BROKEN)) - return 0 - return (src.main_power_lost_until==0 || src.backup_power_lost_until==0) - -/obj/machinery/door/airlock/requiresID() - return !(wires.is_cut(WIRE_IDSCAN) || aiDisabledIdScanner) - -/obj/machinery/door/airlock/proc/isAllPowerLoss() - if(stat & (NOPOWER|BROKEN)) - return 1 - if(mainPowerCablesCut() && backupPowerCablesCut()) - return 1 - return 0 - -/obj/machinery/door/airlock/proc/mainPowerCablesCut() - return wires.is_cut(WIRE_MAIN_POWER1) || wires.is_cut(WIRE_MAIN_POWER2) - -/obj/machinery/door/airlock/proc/backupPowerCablesCut() - return wires.is_cut(WIRE_BACKUP_POWER1) || wires.is_cut(WIRE_BACKUP_POWER2) - -/obj/machinery/door/airlock/proc/loseMainPower() - main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) - - // If backup power is permanently disabled then activate in 10 seconds if possible, otherwise it's already enabled or a timer is already running - if(backup_power_lost_until == -1 && !backupPowerCablesCut()) - backup_power_lost_until = world.time + SecondsToTicks(10) - - if(main_power_lost_until > 0 || backup_power_lost_until > 0) - START_MACHINE_PROCESSING(src) - - // Disable electricity if required - if(electrified_until && isAllPowerLoss()) - electrify(0) - - update_icon() - -/obj/machinery/door/airlock/proc/loseBackupPower() - backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) - - if(backup_power_lost_until > 0) - START_MACHINE_PROCESSING(src) - - // Disable electricity if required - if(electrified_until && isAllPowerLoss()) - electrify(0) - - update_icon() - -/obj/machinery/door/airlock/proc/regainMainPower() - if(!mainPowerCablesCut()) - main_power_lost_until = 0 - // If backup power is currently active then disable, otherwise let it count down and disable itself later - if(!backup_power_lost_until) - backup_power_lost_until = -1 - - update_icon() - -/obj/machinery/door/airlock/proc/regainBackupPower() - if(!backupPowerCablesCut()) - // Restore backup power only if main power is offline, otherwise permanently disable - backup_power_lost_until = main_power_lost_until == 0 ? -1 : 0 - - update_icon() - -/obj/machinery/door/airlock/proc/electrify(var/duration, var/feedback = 0) - var/message = "" - if(wires.is_cut(WIRE_ELECTRIFY) && arePowerSystemsOn()) - message = text("The electrification wire is cut - Door permanently electrified.") - src.electrified_until = -1 - else if(duration && !arePowerSystemsOn()) - message = text("The door is unpowered - Cannot electrify the door.") - src.electrified_until = 0 - else if(!duration && electrified_until != 0) - message = "The door is now un-electrified." - src.electrified_until = 0 - else if(duration) //electrify door for the given duration seconds - if(usr) - shockedby += text("\[[time_stamp()]\] - [usr](ckey:[usr.ckey])") - add_attack_logs(usr,name,"Electrified a door") - else - shockedby += text("\[[time_stamp()]\] - EMP)") - message = "The door is now electrified [duration == -1 ? "permanently" : "for [duration] second\s"]." - src.electrified_until = duration == -1 ? -1 : world.time + SecondsToTicks(duration) - - if(electrified_until > 0) - START_MACHINE_PROCESSING(src) - - if(feedback && message) - to_chat(usr,message) - -/obj/machinery/door/airlock/proc/set_idscan(var/activate, var/feedback = 0) - var/message = "" - if(wires.is_cut(WIRE_IDSCAN)) - message = "The IdScan wire is cut - IdScan feature permanently disabled." - else if(activate && src.aiDisabledIdScanner) - src.aiDisabledIdScanner = 0 - message = "IdScan feature has been enabled." - else if(!activate && !src.aiDisabledIdScanner) - src.aiDisabledIdScanner = 1 - message = "IdScan feature has been disabled." - - if(feedback && message) - to_chat(usr,message) - -/obj/machinery/door/airlock/proc/set_safeties(var/activate, var/feedback = 0) - var/message = "" - // Safeties! We don't need no stinking safeties! - if (wires.is_cut(WIRE_SAFETY)) - message = text("The safety wire is cut - Cannot enable safeties.") - else if (!activate && src.safe) - safe = 0 - else if (activate && !src.safe) - safe = 1 - - if(feedback && message) - to_chat(usr,message) - -// shock user with probability prb (if all connections & power are working) -// returns 1 if shocked, 0 otherwise -// The preceding comment was borrowed from the grille's shock script -/obj/machinery/door/airlock/shock(mob/user, prb) - if(!arePowerSystemsOn()) - return 0 - if(hasShocked) - return 0 //Already shocked someone recently? - if(..()) - hasShocked = 1 - sleep(10) - hasShocked = 0 - return 1 - else - return 0 - - -/obj/machinery/door/airlock/update_icon() - cut_overlays() - if(density) - if(locked && lights && src.arePowerSystemsOn()) - icon_state = "door_locked" - else - icon_state = "door_closed" - if(p_open || welded) - if(p_open) - add_overlay("panel_open") - if (!(stat & NOPOWER)) - if(stat & BROKEN) - add_overlay("sparks_broken") - else if (health < maxhealth * 3/4) - add_overlay("sparks_damaged") - if(welded) - add_overlay("welded") - else if (health < maxhealth * 3/4 && !(stat & NOPOWER)) - add_overlay("sparks_damaged") - else - icon_state = "door_open" - if((stat & BROKEN) && !(stat & NOPOWER)) - add_overlay("sparks_open") - return - -/obj/machinery/door/airlock/do_animate(animation) - switch(animation) - if("opening") - cut_overlay() - if(p_open) - flick("o_door_opening", src) //can not use flick due to BYOND bug updating overlays right before flicking - update_icon() - else - flick("door_opening", src)//[stat ? "_stat":] - update_icon() - if("closing") - cut_overlay() - if(p_open) - flick("o_door_closing", src) - update_icon() - else - flick("door_closing", src) - update_icon() - if("spark") - if(density) - flick("door_spark", src) - if("deny") - if(density && src.arePowerSystemsOn()) - flick("door_deny", src) - playsound(src, denied_sound, 50, 0, 3) - return - -/obj/machinery/door/airlock/attack_ai(mob/user as mob) - tgui_interact(user) - -/obj/machinery/door/airlock/attack_ghost(mob/user) - tgui_interact(user) - -/obj/machinery/door/airlock/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, datum/tgui_state/custom_state) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AiAirlock", name) - ui.open() - if(custom_state) - ui.set_state(custom_state) - return TRUE - -/obj/machinery/door/airlock/tgui_data(mob/user) - var/list/data = list() - - var/list/power = list() - power["main"] = main_power_lost_until > 0 ? 0 : 2 - power["main_timeleft"] = round(main_power_lost_until > 0 ? max(main_power_lost_until - world.time, 0) / 10 : main_power_lost_until, 1) - power["backup"] = backup_power_lost_until > 0 ? 0 : 2 - power["backup_timeleft"] = round(backup_power_lost_until > 0 ? max(backup_power_lost_until - world.time, 0) / 10 : backup_power_lost_until, 1) - data["power"] = power - - data["shock"] = (electrified_until == 0) ? 2 : 0 - data["shock_timeleft"] = round(electrified_until > 0 ? max(electrified_until - world.time, 0) / 10 : electrified_until, 1) - data["id_scanner"] = !aiDisabledIdScanner - data["locked"] = locked // bolted - data["lights"] = lights // bolt lights - data["safe"] = safe // safeties - data["speed"] = normalspeed // safe speed - data["welded"] = welded // welded - data["opened"] = !density // opened - - var/list/wire = list() - wire["main_1"] = !wires.is_cut(WIRE_MAIN_POWER1) - wire["main_2"] = !wires.is_cut(WIRE_MAIN_POWER2) - wire["backup_1"] = !wires.is_cut(WIRE_BACKUP_POWER1) - wire["backup_2"] = !wires.is_cut(WIRE_BACKUP_POWER2) - wire["shock"] = !wires.is_cut(WIRE_ELECTRIFY) - wire["id_scanner"] = !wires.is_cut(WIRE_IDSCAN) - wire["bolts"] = !wires.is_cut(WIRE_DOOR_BOLTS) - wire["lights"] = !wires.is_cut(WIRE_BOLT_LIGHT) - wire["safe"] = !wires.is_cut(WIRE_SAFETY) - wire["timing"] = !wires.is_cut(WIRE_SPEED) - - data["wires"] = wire - return data - -/obj/machinery/door/airlock/proc/hack(mob/user as mob) - if(src.aiHacking==0) - src.aiHacking=1 - spawn(20) - //TODO: Make this take a minute - to_chat(user, "Airlock AI control has been blocked. Beginning fault-detection.") - sleep(50) - if(src.canAIControl()) - to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") - src.aiHacking=0 - return - else if(!src.canAIHack(user)) - to_chat(user, "We've lost our connection! Unable to hack airlock.") - src.aiHacking=0 - return - to_chat(user, "Fault confirmed: airlock control wire disabled or cut.") - sleep(20) - to_chat(user, "Attempting to hack into airlock. This may take some time.") - sleep(200) - if(src.canAIControl()) - to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") - src.aiHacking=0 - return - else if(!src.canAIHack(user)) - to_chat(user, "We've lost our connection! Unable to hack airlock.") - src.aiHacking=0 - return - to_chat(user, "Upload access confirmed. Loading control program into airlock software.") - sleep(170) - if(src.canAIControl()) - to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") - src.aiHacking=0 - return - else if(!src.canAIHack(user)) - to_chat(user, "We've lost our connection! Unable to hack airlock.") - src.aiHacking=0 - return - to_chat(user, "Transfer complete. Forcing airlock to execute program.") - sleep(50) - //disable blocked control - src.aiControlDisabled = 2 - to_chat(user, "Receiving control information from airlock.") - sleep(10) - //bring up airlock dialog - src.aiHacking = 0 - if (user) - src.attack_ai(user) - -/obj/machinery/door/airlock/CanPass(atom/movable/mover, turf/target) - if (src.isElectrified()) - if (istype(mover, /obj/item)) - var/obj/item/i = mover - if (i.matter && (MAT_STEEL in i.matter) && i.matter[MAT_STEEL] > 0) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() - return ..() - -/obj/machinery/door/airlock/attack_hand(mob/user as mob) - if(!istype(usr, /mob/living/silicon)) - if(src.isElectrified()) - if(src.shock(user, 100)) - return - - if(istype(user, /mob/living/carbon/human)) - var/mob/living/carbon/human/X = user - if(istype(X.species, /datum/species/xenos)) - src.attack_alien(user) - return - - if(src.p_open) - user.set_machine(src) - wires.Interact(user) - else - ..(user) - return - -/obj/machinery/door/airlock/AltClick(mob/user as mob) - . = ..() - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - if(!Adjacent(user)) - return - else if(user.a_intent == I_HURT) - src.visible_message("[user] hammers on \the [src]!", "Someone hammers loudly on \the [src]!") - src.add_fingerprint(user) - if(icon_state == "door_closed" && arePowerSystemsOn()) - flick("door_deny", src) - playsound(src, knock_hammer_sound, 50, 0, 3) - else if(arePowerSystemsOn() && user.a_intent == I_HELP) - src.visible_message("[user] presses the door bell on \the [src].", "\The [src]'s bell rings.") - src.add_fingerprint(user) - if(icon_state == "door_closed") - flick("door_deny", src) - playsound(src, knock_sound, 50, 0, 3) - else if(user.a_intent == I_HELP) - src.visible_message("[user] knocks on \the [src].", "Someone knocks on \the [src].") - src.add_fingerprint(user) - playsound(src, knock_unpowered_sound, 50, 0, 3) - return - -/obj/machinery/door/airlock/tgui_act(action, params) - if(..()) - return TRUE - if(!user_allowed(usr)) - return TRUE - - switch(action) - if("disrupt-main") - if(!main_power_lost_until) - loseMainPower() - update_icon() - else - to_chat(usr, "Main power is already offline.") - . = TRUE - if("disrupt-backup") - if(!backup_power_lost_until) - loseBackupPower() - update_icon() - else - to_chat(usr, "Backup power is already offline.") - . = TRUE - if("shock-restore") - electrify(0, 1) - . = TRUE - if("shock-temp") - electrify(30, 1) - . = TRUE - if("shock-perm") - electrify(-1, 1) - . = TRUE - if("idscan-toggle") - set_idscan(aiDisabledIdScanner, 1) - . = TRUE - // if("emergency-toggle") - // toggle_emergency(usr) - // . = TRUE - if("bolt-toggle") - toggle_bolt(usr) - . = TRUE - if("light-toggle") - if(wires.is_cut(WIRE_BOLT_LIGHT)) - to_chat(usr, "The bolt lights wire is cut - The door bolt lights are permanently disabled.") - return - lights = !lights - update_icon() - . = TRUE - if("safe-toggle") - set_safeties(!safe, 1) - . = TRUE - if("speed-toggle") - if(wires.is_cut(WIRE_SPEED)) - to_chat(usr, "The timing wire is cut - Cannot alter timing.") - return - normalspeed = !normalspeed - . = TRUE - if("open-close") - user_toggle_open(usr) - . = TRUE - - update_icon() - return 1 - -/obj/machinery/door/airlock/proc/user_allowed(mob/user) - var/allowed = (issilicon(user) && canAIControl(user)) - if(!allowed && isobserver(user)) - var/mob/observer/dead/D = user - if(D.can_admin_interact()) - allowed = TRUE - return allowed - -/obj/machinery/door/airlock/proc/toggle_bolt(mob/user) - if(!user_allowed(user)) - return - if(wires.is_cut(WIRE_DOOR_BOLTS)) - to_chat(user, "The door bolt drop wire is cut - you can't toggle the door bolts.") - return - if(locked) - if(!arePowerSystemsOn()) - to_chat(user, "The door has no power - you can't raise the door bolts.") - else - unlock() - to_chat(user, "The door bolts have been raised.") - // log_combat(user, src, "unbolted") - else - lock() - to_chat(user, "The door bolts have been dropped.") - // log_combat(user, src, "bolted") - -/obj/machinery/door/airlock/proc/user_toggle_open(mob/user) - if(!user_allowed(user)) - return - if(welded) - to_chat(user, text("The airlock has been welded shut!")) - else if(locked) - to_chat(user, text("The door bolts are down!")) - else if(!density) - close() - else - open() - -/obj/machinery/door/airlock/proc/can_remove_electronics() - return src.p_open && (operating < 0 || (!operating && welded && !src.arePowerSystemsOn() && density && (!src.locked || (stat & BROKEN)))) - -/obj/machinery/door/airlock/attackby(obj/item/C, mob/user as mob) - //to_world("airlock attackby src [src] obj [C] mob [user]") - if(!istype(usr, /mob/living/silicon)) - if(src.isElectrified()) - if(src.shock(user, 75)) - return - if(istype(C, /obj/item/taperoll)) - return - - src.add_fingerprint(user) - if (attempt_vr(src,"attackby_vr",list(C, user))) return - if(istype(C, /mob/living)) - ..() - return - if(!repairing && C.has_tool_quality(TOOL_WELDER) && !( src.operating > 0 ) && src.density) - var/obj/item/weapon/weldingtool/W = C.get_welder() - if(W.remove_fuel(0,user)) - if(!src.welded) - src.welded = 1 - else - src.welded = null - playsound(src, C.usesound, 75, 1) - src.update_icon() - return - else - return - else if(C.has_tool_quality(TOOL_SCREWDRIVER)) - if (src.p_open) - if (stat & BROKEN) - to_chat(usr, "The panel is broken and cannot be closed.") - else - src.p_open = FALSE - playsound(src, C.usesound, 50, 1) - src.update_icon() - return - else - src.p_open = TRUE - playsound(src, C.usesound, 50, 1) - src.update_icon() - return src.attack_hand(user) - else if(C.has_tool_quality(TOOL_WIRECUTTER)) - return src.attack_hand(user) - else if(istype(C, /obj/item/device/multitool)) - return src.attack_hand(user) - else if(istype(C, /obj/item/device/assembly/signaler)) - return src.attack_hand(user) - else if(istype(C, /obj/item/weapon/pai_cable)) // -- TLE - var/obj/item/weapon/pai_cable/cable = C - cable.plugin(src, user) - else if(!repairing && C.has_tool_quality(TOOL_CROWBAR)) - if(can_remove_electronics()) - playsound(src, C.usesound, 75, 1) - user.visible_message("[user] removes the electronics from the airlock assembly.", "You start to remove electronics from the airlock assembly.") - if(do_after(user,40 * C.toolspeed)) - to_chat(user, "You removed the airlock electronics!") - - var/obj/structure/door_assembly/da = new assembly_type(src.loc) - if (istype(da, /obj/structure/door_assembly/multi_tile)) - da.set_dir(src.dir) - - da.anchored = TRUE - if(mineral) - da.glass = mineral - //else if(glass) - else if(glass && !da.glass) - da.glass = 1 - da.state = 1 - da.created_name = src.name - da.update_state() - - if(operating == -1 || (stat & BROKEN)) - new /obj/item/weapon/circuitboard/broken(src.loc) - operating = 0 - else - if (!electronics) create_electronics() - - electronics.loc = src.loc - electronics = null - - qdel(src) - return - else if(arePowerSystemsOn()) - to_chat(user, "The airlock's motors resist your efforts to force it.") - else if(locked) - to_chat(user, "The airlock's bolts prevent it from being forced.") - else - if(density) - spawn(0) open(1) - else - spawn(0) close(1) - - // Check if we're using a crowbar or armblade, and if the airlock's unpowered for whatever reason (off, broken, etc). - else if(istype(C, /obj/item/weapon)) - var/obj/item/weapon/W = C - if((W.pry == 1) && !arePowerSystemsOn()) - if(locked) - to_chat(user, "The airlock's bolts prevent it from being forced.") - else if( !welded && !operating ) - if(istype(C, /obj/item/weapon/material/twohanded/fireaxe)) // If this is a fireaxe, make sure it's held in two hands. - var/obj/item/weapon/material/twohanded/fireaxe/F = C - if(!F.wielded) - to_chat(user, "You need to be wielding \the [F] to do that.") - return - // At this point, it's an armblade or a fireaxe that passed the wielded test, let's try to open it. - if(density) - spawn(0) - open(1) - else - spawn(0) - close(1) - else - ..() - else - ..() - return - -/obj/machinery/door/airlock/phoron/attackby(C as obj, mob/user as mob) - if(C) - ignite(is_hot(C)) - ..() - -/obj/machinery/door/airlock/set_broken() - src.p_open = TRUE - stat |= BROKEN - if (secured_wires) - lock() - for (var/mob/O in viewers(src, null)) - if ((O.client && !( O.blinded ))) - O.show_message("[src.name]'s control panel bursts open, sparks spewing out!") - - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() - - update_icon() - return - -/obj/machinery/door/airlock/open(var/forced=0) - if(!can_open(forced)) - return 0 - use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people - - //if the door is unpowered then it doesn't make sense to hear the woosh of a pneumatic actuator - for(var/mob/M as anything in player_list) - if(!M || !M.client) - continue - var/old_sounds = M.client.is_preference_enabled(/datum/client_preference/old_door_sounds) - var/department_door_sounds = M.client.is_preference_enabled(/datum/client_preference/department_door_sounds) - var/sound - var/volume - if(old_sounds) // Do we have old sounds enabled? Play these even if we have department door sounds enabled. - if(arePowerSystemsOn()) - sound = legacy_open_powered - volume = 50 - else - sound = open_sound_unpowered - volume = 75 - else if(!old_sounds && department_door_sounds && src.department_open_powered) // Else, we have old sounds disabled, the door has per-department door sounds, and we have chosen to play department door sounds, use these. - if(arePowerSystemsOn()) - sound = department_open_powered - volume = 50 - else - sound = open_sound_unpowered - volume = 75 - else // Else, play these. - if(arePowerSystemsOn()) - sound = open_sound_powered - volume = 50 - else - sound = open_sound_unpowered - volume = 75 - - var/turf/T = get_turf(M) - var/distance = get_dist(T, get_turf(src)) - if(distance <= world.view * 2) - if(T && T.z == get_z(src)) - M.playsound_local(get_turf(src), sound, volume, 1, null, 0, TRUE, sound(sound), volume_channel = VOLUME_CHANNEL_DOORS) - - if(src.closeOther != null && istype(src.closeOther, /obj/machinery/door/airlock/) && !src.closeOther.density) - src.closeOther.close() - return ..() - -/obj/machinery/door/airlock/can_open(var/forced=0) - if(!forced) - if(!arePowerSystemsOn() || wires.is_cut(WIRE_OPEN_DOOR)) - return 0 - - if(locked || welded) - return 0 - return ..() - -/obj/machinery/door/airlock/can_close(var/forced=0) - if(locked || welded) - return 0 - - if(!forced) - //despite the name, this wire is for general door control. - if(!arePowerSystemsOn() || wires.is_cut(WIRE_OPEN_DOOR)) - return 0 - - return ..() - -/atom/movable/proc/blocks_airlock() - return density - -/obj/machinery/door/blocks_airlock() - return 0 - -/obj/machinery/mech_sensor/blocks_airlock() - return 0 - -/mob/living/blocks_airlock() - return 1 - -/atom/movable/proc/airlock_crush(var/crush_damage) - return 0 - -/obj/machinery/portable_atmospherics/canister/airlock_crush(var/crush_damage) - . = ..() - health -= crush_damage - healthcheck() - -/obj/effect/energy_field/airlock_crush(var/crush_damage) - adjust_strength(crush_damage) - -/obj/structure/closet/airlock_crush(var/crush_damage) - ..() - damage(crush_damage) - for(var/atom/movable/AM in src) - AM.airlock_crush() - return 1 - -/mob/living/airlock_crush(var/crush_damage) - . = ..() - adjustBruteLoss(crush_damage) - SetStunned(5) - SetWeakened(5) - var/turf/T = get_turf(src) - T.add_blood(src) - return 1 - -/mob/living/carbon/airlock_crush(var/crush_damage) - . = ..() - if(can_feel_pain()) - emote("scream") - -/mob/living/silicon/robot/airlock_crush(var/crush_damage) - adjustBruteLoss(crush_damage) - return 0 - -/obj/machinery/door/airlock/close(var/forced=0) - if(!can_close(forced)) - return 0 - - if(safe) - for(var/turf/turf in locs) - for(var/atom/movable/AM in turf) - if(AM.blocks_airlock()) - if(!has_beeped) - playsound(src, 'sound/machines/buzz-two.ogg', 50, 0) - has_beeped = 1 - autoclose_in(6) - return - - for(var/turf/turf in locs) - for(var/atom/movable/AM in turf) - if(AM.airlock_crush(DOOR_CRUSH_DAMAGE)) - take_damage(DOOR_CRUSH_DAMAGE) - - use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people - has_beeped = 0 - for(var/mob/M as anything in player_list) - if(!M || !M.client) - continue - var/old_sounds = M.client.is_preference_enabled(/datum/client_preference/old_door_sounds) - var/department_door_sounds = M.client.is_preference_enabled(/datum/client_preference/department_door_sounds) - var/sound - var/volume - if(old_sounds) - if(arePowerSystemsOn()) - sound = legacy_close_powered - volume = 50 - else - sound = open_sound_unpowered - volume = 75 - else if(!old_sounds && department_door_sounds && src.department_close_powered) // Else, we have old sounds disabled, the door has per-department door sounds, and we have chosen to play department door sounds, use these. - if(arePowerSystemsOn()) - sound = department_close_powered - volume = 50 - else - sound = open_sound_unpowered - volume = 75 - else - if(arePowerSystemsOn()) - sound = close_sound_powered - volume = 50 - else - sound = open_sound_unpowered - volume = 75 - - var/turf/T = get_turf(M) - var/distance = get_dist(T, get_turf(src)) - if(distance <= world.view * 2) - if(T && T.z == get_z(src)) - M.playsound_local(get_turf(src), sound, volume, 1, null, 0, TRUE, sound(sound), volume_channel = VOLUME_CHANNEL_DOORS) - for(var/turf/turf in locs) - var/obj/structure/window/killthis = (locate(/obj/structure/window) in turf) - if(killthis) - killthis.ex_act(2)//Smashin windows - return ..() - -/obj/machinery/door/airlock/proc/lock(var/forced=0) - if(locked) - return 0 - - if (operating && !forced) return 0 - - src.locked = 1 - playsound(src, bolt_down_sound, 30, 0, 3, volume_channel = VOLUME_CHANNEL_DOORS) - for(var/mob/M in range(1,src)) - M.show_message("You hear a click from the bottom of the door.", 2) - update_icon() - return 1 - -/obj/machinery/door/airlock/proc/unlock(var/forced=0) - if(!src.locked) - return - - if (!forced) - if(operating || !src.arePowerSystemsOn() || wires.is_cut(WIRE_DOOR_BOLTS)) return - - src.locked = 0 - playsound(src, bolt_up_sound, 30, 0, 3, volume_channel = VOLUME_CHANNEL_DOORS) - for(var/mob/M in range(1,src)) - M.show_message("You hear a click from the bottom of the door.", 2) - update_icon() - return 1 - -/obj/machinery/door/airlock/allowed(mob/M) - if(locked) - return 0 - return ..(M) - -/obj/machinery/door/airlock/New(var/newloc, var/obj/structure/door_assembly/assembly=null) - ..() - - //if assembly is given, create the new door from the assembly - if (assembly && istype(assembly)) - assembly_type = assembly.type - - electronics = assembly.electronics - electronics.loc = src - - //update the door's access to match the electronics' - secured_wires = electronics.secure - if(electronics.one_access) - LAZYCLEARLIST(req_access) - req_one_access = src.electronics.conf_access - else - LAZYCLEARLIST(req_one_access) - req_access = src.electronics.conf_access - - //get the name from the assembly - if(assembly.created_name) - name = assembly.created_name - else - name = "[istext(assembly.glass) ? "[assembly.glass] airlock" : assembly.base_name]" - - //get the dir from the assembly - set_dir(assembly.dir) - - //wires - var/turf/T = get_turf(newloc) - if(T && (T.z in using_map.admin_levels)) - secured_wires = 1 - if (secured_wires) - wires = new/datum/wires/airlock/secure(src) - else - wires = new/datum/wires/airlock(src) - -/obj/machinery/door/airlock/Initialize() - if(src.closeOtherId != null) - for (var/obj/machinery/door/airlock/A in machines) - if(A.closeOtherId == src.closeOtherId && A != src) - src.closeOther = A - break - name = "\improper [name]" - . = ..() - -/obj/machinery/door/airlock/Destroy() - qdel(wires) - wires = null - return ..() - -// Most doors will never be deconstructed over the course of a round, -// so as an optimization defer the creation of electronics until -// the airlock is deconstructed -/obj/machinery/door/airlock/proc/create_electronics() - //create new electronics - if (secured_wires) - src.electronics = new/obj/item/weapon/airlock_electronics/secure( src.loc ) - else - src.electronics = new/obj/item/weapon/airlock_electronics( src.loc ) - - //update the electronics to match the door's access - if(LAZYLEN(req_access)) - electronics.conf_access = req_access - else if (LAZYLEN(req_one_access)) - electronics.conf_access = req_one_access - electronics.one_access = 1 - -/obj/machinery/door/airlock/emp_act(var/severity) - if(prob(40/severity)) - var/duration = world.time + SecondsToTicks(30 / severity) - if(duration > electrified_until) - electrify(duration) - ..() - -/obj/machinery/door/airlock/power_change() //putting this is obj/machinery/door itself makes non-airlock doors turn invisible for some reason - ..() - if(stat & NOPOWER) - // If we lost power, disable electrification - // Keeping door lights on, runs on internal battery or something. - electrified_until = 0 - update_icon() - -/obj/machinery/door/airlock/proc/prison_open() - if(arePowerSystemsOn()) - src.unlock() - src.open() - src.lock() - return - - -/obj/machinery/door/airlock/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_DECONSTRUCT) - // Old RCD code made it cost 10 units to decon an airlock. - // Now the new one costs ten "sheets". - return list( - RCD_VALUE_MODE = RCD_DECONSTRUCT, - RCD_VALUE_DELAY = 5 SECONDS, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 10 - ) - return FALSE - -/obj/machinery/door/airlock/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_DECONSTRUCT) - to_chat(user, span("notice", "You deconstruct \the [src].")) - qdel(src) - return TRUE - return FALSE +//VOREStation Edit - Redone a lot of airlock things: +/* +- Specific department maintenance doors +- Named doors properly according to type +- Gave them default access levels with the access constants +- Improper'd all of the names in the new() +*/ + +/obj/machinery/door/airlock + name = "Airlock" + description_info = "If you hold left alt whilst left-clicking on an airlock, you can ring the doorbell to announce your presence to anyone on the other side! Alternately if you are on HARM intent when doing this, you will bang loudly on the door!

    AIs and Cyborgs can also quickly open/close, bolt/unbolt, and electrify/de-electrify doors at a distance by holding left shift, left control, or left alt respectively whilst left-clicking." + icon = 'icons/obj/doors/Doorint.dmi' + icon_state = "door_closed" + power_channel = ENVIRON + + explosion_resistance = 10 + + // Doors do their own stuff + bullet_vulnerability = 0 + + blocks_emissive = EMISSIVE_BLOCK_GENERIC // Not quite as nice as /tg/'s custom masks. We should make those sometime + + var/aiControlDisabled = 0 //If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in. + var/hackProof = 0 // if 1, this door can't be hacked by the AI + var/electrified_until = 0 //World time when the door is no longer electrified. -1 if it is permanently electrified until someone fixes it. + var/main_power_lost_until = 0 //World time when main power is restored. + var/backup_power_lost_until = -1 //World time when backup power is restored. + var/has_beeped = 0 //If 1, will not beep on failed closing attempt. Resets when door closes. + var/spawnPowerRestoreRunning = 0 + var/welded = null + var/locked = 0 + var/lights = 1 // bolt lights show by default + var/aiDisabledIdScanner = 0 + var/aiHacking = 0 + var/obj/machinery/door/airlock/closeOther = null + var/closeOtherId = null + var/lockdownbyai = 0 + autoclose = 1 + var/assembly_type = /obj/structure/door_assembly + var/mineral = null + var/justzap = 0 + var/safe = 1 + normalspeed = 1 + var/obj/item/weapon/airlock_electronics/electronics = null + var/hasShocked = 0 //Prevents multiple shocks from happening + var/secured_wires = 0 + var/security_level = 1 //VOREStation Addition - acts as a multiplier on the time required to hack an airlock with a hacktool + var/datum/wires/airlock/wires = null + + var/open_sound_powered = 'sound/machines/door/covert1o.ogg' + var/open_sound_unpowered = 'sound/machines/door/airlockforced.ogg' + var/close_sound_powered = 'sound/machines/door/covert1c.ogg' + var/legacy_open_powered = 'sound/machines/door/old_airlock.ogg' + var/legacy_close_powered = 'sound/machines/door/old_airlockclose.ogg' + var/department_open_powered = null + var/department_close_powered = null + var/denied_sound = 'sound/machines/deniedbeep.ogg' + var/bolt_up_sound = 'sound/machines/door/boltsup.ogg' + var/bolt_down_sound = 'sound/machines/door/boltsdown.ogg' + var/knock_sound = 'sound/machines/2beeplow.ogg' + var/knock_hammer_sound = 'sound/weapons/sonic_jackhammer.ogg' + var/knock_unpowered_sound = 'sound/machines/door/knock_glass.ogg' + +/obj/machinery/door/airlock/attack_generic(var/mob/living/user, var/damage) + if(stat & (BROKEN|NOPOWER)) + if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) + if(src.locked || src.welded) + visible_message("\The [user] begins breaking into \the [src] internals!") + user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything. + if(do_after(user,10 SECONDS,src)) + src.locked = 0 + src.welded = 0 + update_icon() + open(1) + if(prob(25)) + src.shock(user, 100) + user.set_AI_busy(FALSE) + else if(src.density) + visible_message("\The [user] forces \the [src] open!") + open(1) + else + visible_message("\The [user] forces \the [src] closed!") + close(1) + else + visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") + return + ..() + +/obj/machinery/door/airlock/attack_alien(var/mob/user) //Familiar, right? Doors. -Mechoid + if(istype(user, /mob/living/carbon/human)) + var/mob/living/carbon/human/X = user + if(istype(X.species, /datum/species/xenos)) + if(src.locked || src.welded) + visible_message("\The [user] begins tearing into \the [src] internals!") + src.do_animate("deny") + if(do_after(user,15 SECONDS,src)) + visible_message("\The [user] tears \the [src] open, sparks flying from its electronics!") + src.do_animate("spark") + playsound(src, 'sound/machines/door/airlock_tear_apart.ogg', 100, 1, volume_channel = VOLUME_CHANNEL_DOORS) + src.locked = 0 + src.welded = 0 + update_icon() + open(1) + src.set_broken() //These aren't emags, these be CLAWS + else if(src.density) + visible_message("\The [user] begins forcing \the [src] open!") + if(do_after(user, 5 SECONDS,src)) + playsound(src, 'sound/machines/door/airlock_creaking.ogg', 100, 1, volume_channel = VOLUME_CHANNEL_DOORS) + visible_message("\The [user] forces \the [src] open!") + open(1) + else + visible_message("\The [user] forces \the [src] closed!") + close(1) + else + src.do_animate("deny") + visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") + return + ..() + +/obj/machinery/door/airlock/get_material() + if(mineral) + return get_material_by_name(mineral) + return get_material_by_name(MAT_STEEL) + +/obj/machinery/door/airlock/command + name = "Command Airlock" + icon = 'icons/obj/doors/Doorcom.dmi' + req_one_access = list(access_heads) + assembly_type = /obj/structure/door_assembly/door_assembly_com + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/cmd3o.ogg' + department_close_powered = 'sound/machines/door/cmd3c.ogg' + security_level = 3 //VOREStation Addition + +/obj/machinery/door/airlock/security + name = "Security Airlock" + icon = 'icons/obj/doors/Doorsec.dmi' + req_one_access = list(access_security) + assembly_type = /obj/structure/door_assembly/door_assembly_sec + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/sec1o.ogg' + department_close_powered = 'sound/machines/door/sec1c.ogg' + security_level = 2 //VOREStation Addition + +/obj/machinery/door/airlock/engineering + name = "Engineering Airlock" + icon = 'icons/obj/doors/Dooreng.dmi' + req_one_access = list(access_engine) + assembly_type = /obj/structure/door_assembly/door_assembly_eng + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/eng1o.ogg' + department_close_powered = 'sound/machines/door/eng1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/engineeringatmos + name = "Atmospherics Airlock" + icon = 'icons/obj/doors/Doorengatmos.dmi' + req_one_access = list(access_atmospherics) + assembly_type = /obj/structure/door_assembly/door_assembly_eat + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/eng1o.ogg' + department_close_powered = 'sound/machines/door/eng1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/medical + name = "Medical Airlock" + icon = 'icons/obj/doors/Doormed.dmi' + req_one_access = list(access_medical) + assembly_type = /obj/structure/door_assembly/door_assembly_med + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/med1o.ogg' + department_close_powered = 'sound/machines/door/med1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/maintenance + name = "Maintenance Access" + icon = 'icons/obj/doors/Doormaint.dmi' + //req_one_access = list(access_maint_tunnels) //VOREStation Edit - Maintenance is open access + assembly_type = /obj/structure/door_assembly/door_assembly_mai + open_sound_powered = 'sound/machines/door/door2o.ogg' + close_sound_powered = 'sound/machines/door/door2c.ogg' + +/obj/machinery/door/airlock/maintenance/cargo + icon = 'icons/obj/doors/Doormaint_cargo.dmi' + req_one_access = list(access_cargo) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/door2o.ogg' + department_close_powered = 'sound/machines/door/door2c.ogg' + +/obj/machinery/door/airlock/maintenance/command + icon = 'icons/obj/doors/Doormaint_command.dmi' + req_one_access = list(access_heads) + +/obj/machinery/door/airlock/maintenance/common + icon = 'icons/obj/doors/Doormaint_common.dmi' + open_sound_powered = 'sound/machines/door/hall3o.ogg' + close_sound_powered = 'sound/machines/door/hall3c.ogg' + +/obj/machinery/door/airlock/maintenance/engi + icon = 'icons/obj/doors/Doormaint_engi.dmi' + req_one_access = list(access_engine) + +/obj/machinery/door/airlock/maintenance/int + icon = 'icons/obj/doors/Doormaint_int.dmi' + +/obj/machinery/door/airlock/maintenance/medical + icon = 'icons/obj/doors/Doormaint_med.dmi' + req_one_access = list(access_medical) + +/obj/machinery/door/airlock/maintenance/rnd + icon = 'icons/obj/doors/Doormaint_rnd.dmi' + req_one_access = list(access_research) + +/obj/machinery/door/airlock/maintenance/sec + icon = 'icons/obj/doors/Doormaint_sec.dmi' + req_one_access = list(access_security) + +/obj/machinery/door/airlock/external + name = "External Airlock" + icon = 'icons/obj/doors/Doorext.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_ext + open_sound_powered = 'sound/machines/door/space1o.ogg' + close_sound_powered = 'sound/machines/door/space1c.ogg' + +/obj/machinery/door/airlock/external/bolted + icon_state = "door_locked" // So it looks visibly bolted in map editor + locked = 1 + +// For convenience in making docking ports: one that is pre-bolted with frequency set! +/obj/machinery/door/airlock/external/bolted/cycling + frequency = 1379 + +/obj/machinery/door/airlock/glass_external + name = "External Airlock" + icon = 'icons/obj/doors/Doorextglass.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_ext + opacity = 0 + glass = 1 + req_one_access = list(access_external_airlocks) + open_sound_powered = 'sound/machines/door/space1o.ogg' + close_sound_powered = 'sound/machines/door/space1c.ogg' + +/obj/machinery/door/airlock/glass + name = "Glass Airlock" + icon = 'icons/obj/doors/Doorglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + open_sound_powered = 'sound/machines/door/hall1o.ogg' + close_sound_powered = 'sound/machines/door/hall1c.ogg' + legacy_open_powered = 'sound/machines/door/windowdoor.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + glass = 1 + +/obj/machinery/door/airlock/centcom + name = "Centcom Airlock" + icon = 'icons/obj/doors/Doorele.dmi' + req_one_access = list(access_cent_general) + opacity = 1 + open_sound_powered = 'sound/machines/door/cmd3o.ogg' + close_sound_powered = 'sound/machines/door/cmd3c.ogg' + security_level = 100 //VOREStation Addition + +/obj/machinery/door/airlock/glass_centcom + name = "Airlock" + icon = 'icons/obj/doors/Dooreleglass.dmi' + opacity = 0 + glass = 1 + open_sound_powered = 'sound/machines/door/cmd3o.ogg' + close_sound_powered = 'sound/machines/door/cmd3c.ogg' + security_level = 100 //VOREStation Addition + +/obj/machinery/door/airlock/vault + name = "Vault" + icon = 'icons/obj/doors/vault.dmi' + explosion_resistance = 20 + opacity = 1 + secured_wires = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_highsecurity //Until somebody makes better sprites. + req_one_access = list(access_heads_vault) + open_sound_powered = 'sound/machines/door/vault1o.ogg' + close_sound_powered = 'sound/machines/door/vault1c.ogg' + security_level = 5 //VOREStation Addition + +/obj/machinery/door/airlock/vault/bolted + icon_state = "door_locked" + locked = 1 + +/obj/machinery/door/airlock/freezer + name = "Freezer Airlock" + icon = 'icons/obj/doors/Doorfreezer.dmi' + opacity = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_fre + +/obj/machinery/door/airlock/hatch + name = "Airtight Hatch" + icon = 'icons/obj/doors/Doorhatchele.dmi' + explosion_resistance = 20 + opacity = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_hatch + req_one_access = list(access_maint_tunnels) + open_sound_powered = 'sound/machines/door/hatchopen.ogg' + close_sound_powered = 'sound/machines/door/hatchclose.ogg' + open_sound_unpowered = 'sound/machines/door/hatchforced.ogg' + +/obj/machinery/door/airlock/maintenance_hatch + name = "Maintenance Hatch" + icon = 'icons/obj/doors/Doorhatchmaint2.dmi' + explosion_resistance = 20 + opacity = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_mhatch + req_one_access = list(access_maint_tunnels) + open_sound_powered = 'sound/machines/door/hatchopen.ogg' + close_sound_powered = 'sound/machines/door/hatchclose.ogg' + open_sound_unpowered = 'sound/machines/door/hatchforced.ogg' + +/obj/machinery/door/airlock/glass_command + name = "Command Airlock" + icon = 'icons/obj/doors/Doorcomglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_com + glass = 1 + req_one_access = list(access_heads) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/cmd1o.ogg' + department_close_powered = 'sound/machines/door/cmd1c.ogg' + security_level = 3 //VOREStation Addition + +/obj/machinery/door/airlock/glass_engineering + name = "Engineering Airlock" + icon = 'icons/obj/doors/Doorengglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_eng + glass = 1 + req_one_access = list(access_engine) + department_open_powered = 'sound/machines/door/eng1o.ogg' + department_close_powered = 'sound/machines/door/eng1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/glass_engineeringatmos + name = "Atmospherics Airlock" + icon = 'icons/obj/doors/Doorengatmoglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_eat + glass = 1 + req_one_access = list(access_atmospherics) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/eng1o.ogg' + department_close_powered = 'sound/machines/door/eng1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/glass_security + name = "Security Airlock" + icon = 'icons/obj/doors/Doorsecglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_sec + glass = 1 + req_one_access = list(access_security) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/sec1o.ogg' + department_close_powered = 'sound/machines/door/sec1c.ogg' + security_level = 2 //VOREStation Addition + +/obj/machinery/door/airlock/glass_medical + name = "Medical Airlock" + icon = 'icons/obj/doors/Doormedglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_med + glass = 1 + req_one_access = list(access_medical) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/med1o.ogg' + department_close_powered = 'sound/machines/door/med1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/mining + name = "Mining Airlock" + icon = 'icons/obj/doors/Doormining.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_min + req_one_access = list(access_mining) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/cgo1o.ogg' + department_close_powered = 'sound/machines/door/cgo1c.ogg' + +/obj/machinery/door/airlock/atmos + name = "Atmospherics Airlock" + icon = 'icons/obj/doors/Dooratmo.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_atmo + req_one_access = list(access_atmospherics) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/eng1o.ogg' + department_close_powered = 'sound/machines/door/eng1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/research + name = "Research Airlock" + icon = 'icons/obj/doors/Doorresearch.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_research + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/sci1o.ogg' + department_close_powered = 'sound/machines/door/sci1c.ogg' + security_level = 2 //VOREStation Addition + +/obj/machinery/door/airlock/glass_research + name = "Research Airlock" + icon = 'icons/obj/doors/Doorresearchglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_research + glass = 1 + req_one_access = list(access_research) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/sci1o.ogg' + department_close_powered = 'sound/machines/door/sci1c.ogg' + security_level = 2 //VOREStation Addition + +/obj/machinery/door/airlock/glass_mining + name = "Mining Airlock" + icon = 'icons/obj/doors/Doorminingglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_min + glass = 1 + req_one_access = list(access_mining) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/cgo1o.ogg' + department_close_powered = 'sound/machines/door/cgo1c.ogg' + +/obj/machinery/door/airlock/glass_atmos + name = "Atmospherics Airlock" + icon = 'icons/obj/doors/Dooratmoglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_atmo + glass = 1 + req_one_access = list(access_atmospherics) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/eng1o.ogg' + department_close_powered = 'sound/machines/door/eng1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/gold + name = "Gold Airlock" + icon = 'icons/obj/doors/Doorgold.dmi' + mineral = "gold" + +/obj/machinery/door/airlock/silver + name = "Silver Airlock" + icon = 'icons/obj/doors/Doorsilver.dmi' + mineral = "silver" + +/obj/machinery/door/airlock/diamond + name = "Diamond Airlock" + icon = 'icons/obj/doors/Doordiamond.dmi' + mineral = "diamond" + +/obj/machinery/door/airlock/uranium + name = "Uranium Airlock" + desc = "And they said I was crazy." + icon = 'icons/obj/doors/Dooruranium.dmi' + mineral = "uranium" + var/last_event = 0 + var/rad_power = 7.5 + +/obj/machinery/door/airlock/process() + // Deliberate no call to parent. + if(main_power_lost_until > 0 && world.time >= main_power_lost_until) + regainMainPower() + + if(backup_power_lost_until > 0 && world.time >= backup_power_lost_until) + regainBackupPower() + + else if(electrified_until > 0 && world.time >= electrified_until) + electrify(0) + + if (..() == PROCESS_KILL && !(main_power_lost_until > 0 || backup_power_lost_until > 0 || electrified_until > 0)) + . = PROCESS_KILL + +/obj/machinery/door/airlock/uranium/process() + if(world.time > last_event+20) + if(prob(50)) + SSradiation.radiate(src, rad_power) + last_event = world.time + ..() + +/obj/machinery/door/airlock/phoron + name = "Phoron Airlock" + desc = "No way this can end badly." + icon = 'icons/obj/doors/Doorphoron.dmi' + mineral = "phoron" + +/obj/machinery/door/airlock/phoron/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature > 300) + PhoronBurn(exposed_temperature) + +/obj/machinery/door/airlock/phoron/proc/ignite(exposed_temperature) + if(exposed_temperature > 300) + PhoronBurn(exposed_temperature) + +/obj/machinery/door/airlock/phoron/proc/PhoronBurn(temperature) + for(var/turf/simulated/floor/target_tile in range(2,loc)) + target_tile.assume_gas("phoron", 35, 400+T0C) + spawn (0) target_tile.hotspot_expose(temperature, 400) + for(var/turf/simulated/wall/W in range(3,src)) + W.burn((temperature/4))//Added so that you can't set off a massive chain reaction with a small flame + for(var/obj/machinery/door/airlock/phoron/D in range(3,src)) + D.ignite(temperature/4) + new/obj/structure/door_assembly( src.loc ) + qdel(src) + +/obj/machinery/door/airlock/sandstone + name = "Sandstone Airlock" + icon = 'icons/obj/doors/Doorsand.dmi' + mineral = "sandstone" + +/obj/machinery/door/airlock/science + name = "Research Airlock" + icon = 'icons/obj/doors/Doorsci.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_science + req_one_access = list(access_research) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/sci1o.ogg' + department_close_powered = 'sound/machines/door/sci1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/glass_science + name = "Glass Airlocks" + icon = 'icons/obj/doors/Doorsciglass.dmi' + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_science + glass = 1 + req_one_access = list(access_research) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/sci1o.ogg' + department_close_powered = 'sound/machines/door/sci1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/highsecurity + name = "Secure Airlock" + icon = 'icons/obj/doors/hightechsecurity.dmi' + explosion_resistance = 20 + secured_wires = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_highsecurity + req_one_access = list(access_heads_vault) + open_sound_powered = 'sound/machines/door/secure1o.ogg' + close_sound_powered = 'sound/machines/door/secure1c.ogg' + security_level = 4 //VOREStation Addition + +/obj/machinery/door/airlock/voidcraft + name = "voidcraft hatch" + desc = "It's an extra resilient airlock intended for spacefaring vessels." + icon = 'icons/obj/doors/shuttledoors.dmi' + explosion_resistance = 20 + opacity = 0 + glass = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_voidcraft + open_sound_powered = 'sound/machines/door/shuttle1o.ogg' + close_sound_powered = 'sound/machines/door/shuttle1c.ogg' + +// Airlock opens from top-bottom instead of left-right. +/obj/machinery/door/airlock/voidcraft/vertical + icon = 'icons/obj/doors/shuttledoors_vertical.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_voidcraft/vertical + open_sound_powered = 'sound/machines/door/shuttle1o.ogg' + close_sound_powered = 'sound/machines/door/shuttle1c.ogg' + + +/datum/category_item/catalogue/anomalous/precursor_a/alien_airlock + name = "Precursor Alpha Object - Doors" + desc = "This object appears to be used in order to restrict or allow access to \ + rooms based on its physical state. In other words, a door. \ + Despite being designed and created by unknown ancient alien hands, this door has \ + a large number of similarities to the conventional airlock, such as being driven by \ + electricity, opening and closing by physically moving, and being air tight. \ + It also operates by responding to signals through internal electrical conduits. \ + These characteristics make it possible for one with experience with a multitool \ + to manipulate the door.\ +

    \ + The symbol on the door does not match any living species' patterns, giving further \ + implications that this door is very old, and yet it remains operational after \ + thousands of years. It is unknown if that is due to superb construction, or \ + unseen autonomous maintenance having been performed." + value = CATALOGUER_REWARD_EASY + +/obj/machinery/door/airlock/alien + name = "alien airlock" + desc = "You're fairly sure this is a door." + catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_airlock) + icon = 'icons/obj/doors/Dooralien.dmi' + explosion_resistance = 20 + secured_wires = TRUE + hackProof = TRUE + assembly_type = /obj/structure/door_assembly/door_assembly_alien + req_one_access = list(access_alien) + security_level = 100 //VOREStation Addition + +/obj/machinery/door/airlock/alien/locked + icon_state = "door_locked" + locked = TRUE + +/obj/machinery/door/airlock/alien/public // Entry to UFO. + req_one_access = list() + normalspeed = FALSE // So it closes faster and hopefully keeps the warm air inside. + hackProof = TRUE //VOREStation Edit - No borgos + +/* +About the new airlock wires panel: +* An airlock wire dialog can be accessed by the normal way or by using wirecutters or a multitool on the door while the wire-panel is open. This would show the following wires, which you can either wirecut/mend or send a multitool pulse through. There are 9 wires. +* one wire from the ID scanner. Sending a pulse through this flashes the red light on the door (if the door has power). If you cut this wire, the door will stop recognizing valid IDs. (If the door has 0000 access, it still opens and closes, though) +* two wires for power. Sending a pulse through either one causes a breaker to trip, disabling the door for 10 seconds if backup power is connected, or 1 minute if not (or until backup power comes back on, whichever is shorter). Cutting either one disables the main door power, but unless backup power is also cut, the backup power re-powers the door in 10 seconds. While unpowered, the door may be open, but bolts-raising will not work. Cutting these wires may electrocute the user. +* one wire for door bolts. Sending a pulse through this drops door bolts (whether the door is powered or not) or raises them (if it is). Cutting this wire also drops the door bolts, and mending it does not raise them. If the wire is cut, trying to raise the door bolts will not work. +* two wires for backup power. Sending a pulse through either one causes a breaker to trip, but this does not disable it unless main power is down too (in which case it is disabled for 1 minute or however long it takes main power to come back, whichever is shorter). Cutting either one disables the backup door power (allowing it to be crowbarred open, but disabling bolts-raising), but may electocute the user. +* one wire for opening the door. Sending a pulse through this while the door has power makes it open the door if no access is required. +* one wire for AI control. Sending a pulse through this blocks AI control for a second or so (which is enough to see the AI control light on the panel dialog go off and back on again). Cutting this prevents the AI from controlling the door unless it has hacked the door through the power connection (which takes about a minute). If both main and backup power are cut, as well as this wire, then the AI cannot operate or hack the door at all. +* one wire for electrifying the door. Sending a pulse through this electrifies the door for 30 seconds. Cutting this wire electrifies the door, so that the next person to touch the door without insulated gloves gets electrocuted. (Currently it is also STAYING electrified until someone mends the wire) +* one wire for controling door safetys. When active, door does not close on someone. When cut, door will ruin someone's shit. When pulsed, door will immedately ruin someone's shit. +* one wire for controlling door speed. When active, dor closes at normal rate. When cut, door does not close manually. When pulsed, door attempts to close every tick. +*/ + + + +/obj/machinery/door/airlock/bumpopen(mob/living/user as mob) //Airlocks now zap you when you 'bump' them open when they're electrified. --NeoFite + if(!issilicon(usr)) + if(src.isElectrified()) + if(!src.justzap) + if(src.shock(user, 100)) + src.justzap = 1 + spawn (10) + src.justzap = 0 + return + else /*if(src.justzap)*/ + return + else if(user.hallucination > 50 && prob(10) && src.operating == 0) + to_chat(user, "You feel a powerful shock course through your body!") + user.halloss += 10 + user.stunned += 10 + return + ..(user) + +/obj/machinery/door/airlock/proc/isElectrified() + if(src.electrified_until != 0) + return 1 + return 0 + +/obj/machinery/door/airlock/proc/canAIControl() + return ((src.aiControlDisabled!=1) && (!src.isAllPowerLoss())); + +/obj/machinery/door/airlock/proc/canAIHack() + return ((src.aiControlDisabled==1) && (!hackProof) && (!src.isAllPowerLoss())); + +/obj/machinery/door/airlock/proc/arePowerSystemsOn() + if (stat & (NOPOWER|BROKEN)) + return 0 + return (src.main_power_lost_until==0 || src.backup_power_lost_until==0) + +/obj/machinery/door/airlock/requiresID() + return !(wires.is_cut(WIRE_IDSCAN) || aiDisabledIdScanner) + +/obj/machinery/door/airlock/proc/isAllPowerLoss() + if(stat & (NOPOWER|BROKEN)) + return 1 + if(mainPowerCablesCut() && backupPowerCablesCut()) + return 1 + return 0 + +/obj/machinery/door/airlock/proc/mainPowerCablesCut() + return wires.is_cut(WIRE_MAIN_POWER1) || wires.is_cut(WIRE_MAIN_POWER2) + +/obj/machinery/door/airlock/proc/backupPowerCablesCut() + return wires.is_cut(WIRE_BACKUP_POWER1) || wires.is_cut(WIRE_BACKUP_POWER2) + +/obj/machinery/door/airlock/proc/loseMainPower() + main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) + + // If backup power is permanently disabled then activate in 10 seconds if possible, otherwise it's already enabled or a timer is already running + if(backup_power_lost_until == -1 && !backupPowerCablesCut()) + backup_power_lost_until = world.time + SecondsToTicks(10) + + if(main_power_lost_until > 0 || backup_power_lost_until > 0) + START_MACHINE_PROCESSING(src) + + // Disable electricity if required + if(electrified_until && isAllPowerLoss()) + electrify(0) + + update_icon() + +/obj/machinery/door/airlock/proc/loseBackupPower() + backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) + + if(backup_power_lost_until > 0) + START_MACHINE_PROCESSING(src) + + // Disable electricity if required + if(electrified_until && isAllPowerLoss()) + electrify(0) + + update_icon() + +/obj/machinery/door/airlock/proc/regainMainPower() + if(!mainPowerCablesCut()) + main_power_lost_until = 0 + // If backup power is currently active then disable, otherwise let it count down and disable itself later + if(!backup_power_lost_until) + backup_power_lost_until = -1 + + update_icon() + +/obj/machinery/door/airlock/proc/regainBackupPower() + if(!backupPowerCablesCut()) + // Restore backup power only if main power is offline, otherwise permanently disable + backup_power_lost_until = main_power_lost_until == 0 ? -1 : 0 + + update_icon() + +/obj/machinery/door/airlock/proc/electrify(var/duration, var/feedback = 0) + var/message = "" + if(wires.is_cut(WIRE_ELECTRIFY) && arePowerSystemsOn()) + message = text("The electrification wire is cut - Door permanently electrified.") + src.electrified_until = -1 + else if(duration && !arePowerSystemsOn()) + message = text("The door is unpowered - Cannot electrify the door.") + src.electrified_until = 0 + else if(!duration && electrified_until != 0) + message = "The door is now un-electrified." + src.electrified_until = 0 + else if(duration) //electrify door for the given duration seconds + if(usr) + shockedby += text("\[[time_stamp()]\] - [usr](ckey:[usr.ckey])") + add_attack_logs(usr,name,"Electrified a door") + else + shockedby += text("\[[time_stamp()]\] - EMP)") + message = "The door is now electrified [duration == -1 ? "permanently" : "for [duration] second\s"]." + src.electrified_until = duration == -1 ? -1 : world.time + SecondsToTicks(duration) + + if(electrified_until > 0) + START_MACHINE_PROCESSING(src) + + if(feedback && message) + to_chat(usr,message) + +/obj/machinery/door/airlock/proc/set_idscan(var/activate, var/feedback = 0) + var/message = "" + if(wires.is_cut(WIRE_IDSCAN)) + message = "The IdScan wire is cut - IdScan feature permanently disabled." + else if(activate && src.aiDisabledIdScanner) + src.aiDisabledIdScanner = 0 + message = "IdScan feature has been enabled." + else if(!activate && !src.aiDisabledIdScanner) + src.aiDisabledIdScanner = 1 + message = "IdScan feature has been disabled." + + if(feedback && message) + to_chat(usr,message) + +/obj/machinery/door/airlock/proc/set_safeties(var/activate, var/feedback = 0) + var/message = "" + // Safeties! We don't need no stinking safeties! + if (wires.is_cut(WIRE_SAFETY)) + message = text("The safety wire is cut - Cannot enable safeties.") + else if (!activate && src.safe) + safe = 0 + else if (activate && !src.safe) + safe = 1 + + if(feedback && message) + to_chat(usr,message) + +// shock user with probability prb (if all connections & power are working) +// returns 1 if shocked, 0 otherwise +// The preceding comment was borrowed from the grille's shock script +/obj/machinery/door/airlock/shock(mob/user, prb) + if(!arePowerSystemsOn()) + return 0 + if(hasShocked) + return 0 //Already shocked someone recently? + if(..()) + hasShocked = 1 + sleep(10) + hasShocked = 0 + return 1 + else + return 0 + + +/obj/machinery/door/airlock/update_icon() + cut_overlays() + if(density) + if(locked && lights && src.arePowerSystemsOn()) + icon_state = "door_locked" + else + icon_state = "door_closed" + if(p_open || welded) + if(p_open) + add_overlay("panel_open") + if (!(stat & NOPOWER)) + if(stat & BROKEN) + add_overlay("sparks_broken") + else if (health < maxhealth * 3/4) + add_overlay("sparks_damaged") + if(welded) + add_overlay("welded") + else if (health < maxhealth * 3/4 && !(stat & NOPOWER)) + add_overlay("sparks_damaged") + else + icon_state = "door_open" + if((stat & BROKEN) && !(stat & NOPOWER)) + add_overlay("sparks_open") + return + +/obj/machinery/door/airlock/do_animate(animation) + switch(animation) + if("opening") + cut_overlay() + if(p_open) + flick("o_door_opening", src) //can not use flick due to BYOND bug updating overlays right before flicking + update_icon() + else + flick("door_opening", src)//[stat ? "_stat":] + update_icon() + if("closing") + cut_overlay() + if(p_open) + flick("o_door_closing", src) + update_icon() + else + flick("door_closing", src) + update_icon() + if("spark") + if(density) + flick("door_spark", src) + if("deny") + if(density && src.arePowerSystemsOn()) + flick("door_deny", src) + playsound(src, denied_sound, 50, 0, 3) + return + +/obj/machinery/door/airlock/attack_ai(mob/user as mob) + tgui_interact(user) + +/obj/machinery/door/airlock/attack_ghost(mob/user) + tgui_interact(user) + +/obj/machinery/door/airlock/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, datum/tgui_state/custom_state) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AiAirlock", name) + ui.open() + if(custom_state) + ui.set_state(custom_state) + return TRUE + +/obj/machinery/door/airlock/tgui_data(mob/user) + var/list/data = list() + + var/list/power = list() + power["main"] = main_power_lost_until > 0 ? 0 : 2 + power["main_timeleft"] = round(main_power_lost_until > 0 ? max(main_power_lost_until - world.time, 0) / 10 : main_power_lost_until, 1) + power["backup"] = backup_power_lost_until > 0 ? 0 : 2 + power["backup_timeleft"] = round(backup_power_lost_until > 0 ? max(backup_power_lost_until - world.time, 0) / 10 : backup_power_lost_until, 1) + data["power"] = power + + data["shock"] = (electrified_until == 0) ? 2 : 0 + data["shock_timeleft"] = round(electrified_until > 0 ? max(electrified_until - world.time, 0) / 10 : electrified_until, 1) + data["id_scanner"] = !aiDisabledIdScanner + data["locked"] = locked // bolted + data["lights"] = lights // bolt lights + data["safe"] = safe // safeties + data["speed"] = normalspeed // safe speed + data["welded"] = welded // welded + data["opened"] = !density // opened + + var/list/wire = list() + wire["main_1"] = !wires.is_cut(WIRE_MAIN_POWER1) + wire["main_2"] = !wires.is_cut(WIRE_MAIN_POWER2) + wire["backup_1"] = !wires.is_cut(WIRE_BACKUP_POWER1) + wire["backup_2"] = !wires.is_cut(WIRE_BACKUP_POWER2) + wire["shock"] = !wires.is_cut(WIRE_ELECTRIFY) + wire["id_scanner"] = !wires.is_cut(WIRE_IDSCAN) + wire["bolts"] = !wires.is_cut(WIRE_DOOR_BOLTS) + wire["lights"] = !wires.is_cut(WIRE_BOLT_LIGHT) + wire["safe"] = !wires.is_cut(WIRE_SAFETY) + wire["timing"] = !wires.is_cut(WIRE_SPEED) + + data["wires"] = wire + return data + +/obj/machinery/door/airlock/proc/hack(mob/user as mob) + if(src.aiHacking==0) + src.aiHacking=1 + spawn(20) + //TODO: Make this take a minute + to_chat(user, "Airlock AI control has been blocked. Beginning fault-detection.") + sleep(50) + if(src.canAIControl()) + to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") + src.aiHacking=0 + return + else if(!src.canAIHack(user)) + to_chat(user, "We've lost our connection! Unable to hack airlock.") + src.aiHacking=0 + return + to_chat(user, "Fault confirmed: airlock control wire disabled or cut.") + sleep(20) + to_chat(user, "Attempting to hack into airlock. This may take some time.") + sleep(200) + if(src.canAIControl()) + to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") + src.aiHacking=0 + return + else if(!src.canAIHack(user)) + to_chat(user, "We've lost our connection! Unable to hack airlock.") + src.aiHacking=0 + return + to_chat(user, "Upload access confirmed. Loading control program into airlock software.") + sleep(170) + if(src.canAIControl()) + to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") + src.aiHacking=0 + return + else if(!src.canAIHack(user)) + to_chat(user, "We've lost our connection! Unable to hack airlock.") + src.aiHacking=0 + return + to_chat(user, "Transfer complete. Forcing airlock to execute program.") + sleep(50) + //disable blocked control + src.aiControlDisabled = 2 + to_chat(user, "Receiving control information from airlock.") + sleep(10) + //bring up airlock dialog + src.aiHacking = 0 + if (user) + src.attack_ai(user) + +/obj/machinery/door/airlock/CanPass(atom/movable/mover, turf/target) + if (src.isElectrified()) + if (istype(mover, /obj/item)) + var/obj/item/i = mover + if (i.matter && (MAT_STEEL in i.matter) && i.matter[MAT_STEEL] > 0) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + return ..() + +/obj/machinery/door/airlock/attack_hand(mob/user as mob) + if(!istype(usr, /mob/living/silicon)) + if(src.isElectrified()) + if(src.shock(user, 100)) + return + + if(istype(user, /mob/living/carbon/human)) + var/mob/living/carbon/human/X = user + if(istype(X.species, /datum/species/xenos)) + src.attack_alien(user) + return + + if(src.p_open) + user.set_machine(src) + wires.Interact(user) + else + ..(user) + return + +/obj/machinery/door/airlock/AltClick(mob/user as mob) + . = ..() + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(!Adjacent(user)) + return + else if(user.a_intent == I_HURT) + src.visible_message("[user] hammers on \the [src]!", "Someone hammers loudly on \the [src]!") + src.add_fingerprint(user) + if(icon_state == "door_closed" && arePowerSystemsOn()) + flick("door_deny", src) + playsound(src, knock_hammer_sound, 50, 0, 3) + else if(arePowerSystemsOn() && user.a_intent == I_HELP) + src.visible_message("[user] presses the door bell on \the [src].", "\The [src]'s bell rings.") + src.add_fingerprint(user) + if(icon_state == "door_closed") + flick("door_deny", src) + playsound(src, knock_sound, 50, 0, 3) + else if(user.a_intent == I_HELP) + src.visible_message("[user] knocks on \the [src].", "Someone knocks on \the [src].") + src.add_fingerprint(user) + playsound(src, knock_unpowered_sound, 50, 0, 3) + return + +/obj/machinery/door/airlock/tgui_act(action, params) + if(..()) + return TRUE + if(!user_allowed(usr)) + return TRUE + + switch(action) + if("disrupt-main") + if(!main_power_lost_until) + loseMainPower() + update_icon() + else + to_chat(usr, "Main power is already offline.") + . = TRUE + if("disrupt-backup") + if(!backup_power_lost_until) + loseBackupPower() + update_icon() + else + to_chat(usr, "Backup power is already offline.") + . = TRUE + if("shock-restore") + electrify(0, 1) + . = TRUE + if("shock-temp") + electrify(30, 1) + . = TRUE + if("shock-perm") + electrify(-1, 1) + . = TRUE + if("idscan-toggle") + set_idscan(aiDisabledIdScanner, 1) + . = TRUE + // if("emergency-toggle") + // toggle_emergency(usr) + // . = TRUE + if("bolt-toggle") + toggle_bolt(usr) + . = TRUE + if("light-toggle") + if(wires.is_cut(WIRE_BOLT_LIGHT)) + to_chat(usr, "The bolt lights wire is cut - The door bolt lights are permanently disabled.") + return + lights = !lights + update_icon() + . = TRUE + if("safe-toggle") + set_safeties(!safe, 1) + . = TRUE + if("speed-toggle") + if(wires.is_cut(WIRE_SPEED)) + to_chat(usr, "The timing wire is cut - Cannot alter timing.") + return + normalspeed = !normalspeed + . = TRUE + if("open-close") + user_toggle_open(usr) + . = TRUE + + update_icon() + return 1 + +/obj/machinery/door/airlock/proc/user_allowed(mob/user) + var/allowed = (issilicon(user) && canAIControl(user)) + if(!allowed && isobserver(user)) + var/mob/observer/dead/D = user + if(D.can_admin_interact()) + allowed = TRUE + return allowed + +/obj/machinery/door/airlock/proc/toggle_bolt(mob/user) + if(!user_allowed(user)) + return + if(wires.is_cut(WIRE_DOOR_BOLTS)) + to_chat(user, "The door bolt drop wire is cut - you can't toggle the door bolts.") + return + if(locked) + if(!arePowerSystemsOn()) + to_chat(user, "The door has no power - you can't raise the door bolts.") + else + unlock() + to_chat(user, "The door bolts have been raised.") + // log_combat(user, src, "unbolted") + else + lock() + to_chat(user, "The door bolts have been dropped.") + // log_combat(user, src, "bolted") + +/obj/machinery/door/airlock/proc/user_toggle_open(mob/user) + if(!user_allowed(user)) + return + if(welded) + to_chat(user, text("The airlock has been welded shut!")) + else if(locked) + to_chat(user, text("The door bolts are down!")) + else if(!density) + close() + else + open() + +/obj/machinery/door/airlock/proc/can_remove_electronics() + return src.p_open && (operating < 0 || (!operating && welded && !src.arePowerSystemsOn() && density && (!src.locked || (stat & BROKEN)))) + +/obj/machinery/door/airlock/attackby(obj/item/C, mob/user as mob) + //to_world("airlock attackby src [src] obj [C] mob [user]") + if(!istype(usr, /mob/living/silicon)) + if(src.isElectrified()) + if(src.shock(user, 75)) + return + if(istype(C, /obj/item/taperoll)) + return + + src.add_fingerprint(user) + if (attempt_vr(src,"attackby_vr",list(C, user))) return + if(istype(C, /mob/living)) + ..() + return + if(!repairing && C.has_tool_quality(TOOL_WELDER) && !( src.operating > 0 ) && src.density) + var/obj/item/weapon/weldingtool/W = C.get_welder() + if(W.remove_fuel(0,user)) + if(!src.welded) + src.welded = 1 + else + src.welded = null + playsound(src, C.usesound, 75, 1) + src.update_icon() + return + else + return + else if(C.has_tool_quality(TOOL_SCREWDRIVER)) + if (src.p_open) + if (stat & BROKEN) + to_chat(usr, "The panel is broken and cannot be closed.") + else + src.p_open = FALSE + playsound(src, C.usesound, 50, 1) + src.update_icon() + return + else + src.p_open = TRUE + playsound(src, C.usesound, 50, 1) + src.update_icon() + return src.attack_hand(user) + else if(C.has_tool_quality(TOOL_WIRECUTTER)) + return src.attack_hand(user) + else if(istype(C, /obj/item/device/multitool)) + return src.attack_hand(user) + else if(istype(C, /obj/item/device/assembly/signaler)) + return src.attack_hand(user) + else if(istype(C, /obj/item/weapon/pai_cable)) // -- TLE + var/obj/item/weapon/pai_cable/cable = C + cable.plugin(src, user) + else if(!repairing && C.has_tool_quality(TOOL_CROWBAR)) + if(can_remove_electronics()) + playsound(src, C.usesound, 75, 1) + user.visible_message("[user] removes the electronics from the airlock assembly.", "You start to remove electronics from the airlock assembly.") + if(do_after(user,40 * C.toolspeed)) + to_chat(user, "You removed the airlock electronics!") + + var/obj/structure/door_assembly/da = new assembly_type(src.loc) + if (istype(da, /obj/structure/door_assembly/multi_tile)) + da.set_dir(src.dir) + + da.anchored = TRUE + if(mineral) + da.glass = mineral + //else if(glass) + else if(glass && !da.glass) + da.glass = 1 + da.state = 1 + da.created_name = src.name + da.update_state() + + if(operating == -1 || (stat & BROKEN)) + new /obj/item/weapon/circuitboard/broken(src.loc) + operating = 0 + else + if (!electronics) create_electronics() + + electronics.loc = src.loc + electronics = null + + qdel(src) + return + else if(arePowerSystemsOn()) + to_chat(user, "The airlock's motors resist your efforts to force it.") + else if(locked) + to_chat(user, "The airlock's bolts prevent it from being forced.") + else + if(density) + spawn(0) open(1) + else + spawn(0) close(1) + + // Check if we're using a crowbar or armblade, and if the airlock's unpowered for whatever reason (off, broken, etc). + else if(istype(C, /obj/item/weapon)) + var/obj/item/weapon/W = C + if((W.pry == 1) && !arePowerSystemsOn()) + if(locked) + to_chat(user, "The airlock's bolts prevent it from being forced.") + else if( !welded && !operating ) + if(istype(C, /obj/item/weapon/material/twohanded/fireaxe)) // If this is a fireaxe, make sure it's held in two hands. + var/obj/item/weapon/material/twohanded/fireaxe/F = C + if(!F.wielded) + to_chat(user, "You need to be wielding \the [F] to do that.") + return + // At this point, it's an armblade or a fireaxe that passed the wielded test, let's try to open it. + if(density) + spawn(0) + open(1) + else + spawn(0) + close(1) + else + ..() + else + ..() + return + +/obj/machinery/door/airlock/phoron/attackby(C as obj, mob/user as mob) + if(C) + ignite(is_hot(C)) + ..() + +/obj/machinery/door/airlock/set_broken() + src.p_open = TRUE + stat |= BROKEN + if (secured_wires) + lock() + for (var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message("[src.name]'s control panel bursts open, sparks spewing out!") + + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + + update_icon() + return + +/obj/machinery/door/airlock/open(var/forced=0) + if(!can_open(forced)) + return 0 + use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people + + //if the door is unpowered then it doesn't make sense to hear the woosh of a pneumatic actuator + for(var/mob/M as anything in player_list) + if(!M || !M.client) + continue + var/old_sounds = M.client.is_preference_enabled(/datum/client_preference/old_door_sounds) + var/department_door_sounds = M.client.is_preference_enabled(/datum/client_preference/department_door_sounds) + var/sound + var/volume + if(old_sounds) // Do we have old sounds enabled? Play these even if we have department door sounds enabled. + if(arePowerSystemsOn()) + sound = legacy_open_powered + volume = 50 + else + sound = open_sound_unpowered + volume = 75 + else if(!old_sounds && department_door_sounds && src.department_open_powered) // Else, we have old sounds disabled, the door has per-department door sounds, and we have chosen to play department door sounds, use these. + if(arePowerSystemsOn()) + sound = department_open_powered + volume = 50 + else + sound = open_sound_unpowered + volume = 75 + else // Else, play these. + if(arePowerSystemsOn()) + sound = open_sound_powered + volume = 50 + else + sound = open_sound_unpowered + volume = 75 + + var/turf/T = get_turf(M) + var/distance = get_dist(T, get_turf(src)) + if(distance <= world.view * 2) + if(T && T.z == get_z(src)) + M.playsound_local(get_turf(src), sound, volume, 1, null, 0, TRUE, sound(sound), volume_channel = VOLUME_CHANNEL_DOORS) + + if(src.closeOther != null && istype(src.closeOther, /obj/machinery/door/airlock/) && !src.closeOther.density) + src.closeOther.close() + return ..() + +/obj/machinery/door/airlock/can_open(var/forced=0) + if(!forced) + if(!arePowerSystemsOn() || wires.is_cut(WIRE_OPEN_DOOR)) + return 0 + + if(locked || welded) + return 0 + return ..() + +/obj/machinery/door/airlock/can_close(var/forced=0) + if(locked || welded) + return 0 + + if(!forced) + //despite the name, this wire is for general door control. + if(!arePowerSystemsOn() || wires.is_cut(WIRE_OPEN_DOOR)) + return 0 + + return ..() + +/atom/movable/proc/blocks_airlock() + return density + +/obj/machinery/door/blocks_airlock() + return 0 + +/obj/machinery/mech_sensor/blocks_airlock() + return 0 + +/mob/living/blocks_airlock() + return 1 + +/atom/movable/proc/airlock_crush(var/crush_damage) + return 0 + +/obj/machinery/portable_atmospherics/canister/airlock_crush(var/crush_damage) + . = ..() + health -= crush_damage + healthcheck() + +/obj/effect/energy_field/airlock_crush(var/crush_damage) + adjust_strength(crush_damage) + +/obj/structure/closet/airlock_crush(var/crush_damage) + ..() + damage(crush_damage) + for(var/atom/movable/AM in src) + AM.airlock_crush() + return 1 + +/mob/living/airlock_crush(var/crush_damage) + . = ..() + adjustBruteLoss(crush_damage) + SetStunned(5) + SetWeakened(5) + var/turf/T = get_turf(src) + T.add_blood(src) + return 1 + +/mob/living/carbon/airlock_crush(var/crush_damage) + . = ..() + if(can_feel_pain()) + emote("scream") + +/mob/living/silicon/robot/airlock_crush(var/crush_damage) + adjustBruteLoss(crush_damage) + return 0 + +/obj/machinery/door/airlock/close(var/forced=0) + if(!can_close(forced)) + return 0 + + if(safe) + for(var/turf/turf in locs) + for(var/atom/movable/AM in turf) + if(AM.blocks_airlock()) + if(!has_beeped) + playsound(src, 'sound/machines/buzz-two.ogg', 50, 0) + has_beeped = 1 + autoclose_in(6) + return + + for(var/turf/turf in locs) + for(var/atom/movable/AM in turf) + if(AM.airlock_crush(DOOR_CRUSH_DAMAGE)) + take_damage(DOOR_CRUSH_DAMAGE) + + use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people + has_beeped = 0 + for(var/mob/M as anything in player_list) + if(!M || !M.client) + continue + var/old_sounds = M.client.is_preference_enabled(/datum/client_preference/old_door_sounds) + var/department_door_sounds = M.client.is_preference_enabled(/datum/client_preference/department_door_sounds) + var/sound + var/volume + if(old_sounds) + if(arePowerSystemsOn()) + sound = legacy_close_powered + volume = 50 + else + sound = open_sound_unpowered + volume = 75 + else if(!old_sounds && department_door_sounds && src.department_close_powered) // Else, we have old sounds disabled, the door has per-department door sounds, and we have chosen to play department door sounds, use these. + if(arePowerSystemsOn()) + sound = department_close_powered + volume = 50 + else + sound = open_sound_unpowered + volume = 75 + else + if(arePowerSystemsOn()) + sound = close_sound_powered + volume = 50 + else + sound = open_sound_unpowered + volume = 75 + + var/turf/T = get_turf(M) + var/distance = get_dist(T, get_turf(src)) + if(distance <= world.view * 2) + if(T && T.z == get_z(src)) + M.playsound_local(get_turf(src), sound, volume, 1, null, 0, TRUE, sound(sound), volume_channel = VOLUME_CHANNEL_DOORS) + for(var/turf/turf in locs) + var/obj/structure/window/killthis = (locate(/obj/structure/window) in turf) + if(killthis) + killthis.ex_act(2)//Smashin windows + return ..() + +/obj/machinery/door/airlock/proc/lock(var/forced=0) + if(locked) + return 0 + + if (operating && !forced) return 0 + + src.locked = 1 + playsound(src, bolt_down_sound, 30, 0, 3, volume_channel = VOLUME_CHANNEL_DOORS) + for(var/mob/M in range(1,src)) + M.show_message("You hear a click from the bottom of the door.", 2) + update_icon() + return 1 + +/obj/machinery/door/airlock/proc/unlock(var/forced=0) + if(!src.locked) + return + + if (!forced) + if(operating || !src.arePowerSystemsOn() || wires.is_cut(WIRE_DOOR_BOLTS)) return + + src.locked = 0 + playsound(src, bolt_up_sound, 30, 0, 3, volume_channel = VOLUME_CHANNEL_DOORS) + for(var/mob/M in range(1,src)) + M.show_message("You hear a click from the bottom of the door.", 2) + update_icon() + return 1 + +/obj/machinery/door/airlock/allowed(mob/M) + if(locked) + return 0 + return ..(M) + +/obj/machinery/door/airlock/New(var/newloc, var/obj/structure/door_assembly/assembly=null) + ..() + + //if assembly is given, create the new door from the assembly + if (assembly && istype(assembly)) + assembly_type = assembly.type + + electronics = assembly.electronics + electronics.loc = src + + //update the door's access to match the electronics' + secured_wires = electronics.secure + if(electronics.one_access) + LAZYCLEARLIST(req_access) + req_one_access = src.electronics.conf_access + else + LAZYCLEARLIST(req_one_access) + req_access = src.electronics.conf_access + + //get the name from the assembly + if(assembly.created_name) + name = assembly.created_name + else + name = "[istext(assembly.glass) ? "[assembly.glass] airlock" : assembly.base_name]" + + //get the dir from the assembly + set_dir(assembly.dir) + + //wires + var/turf/T = get_turf(newloc) + if(T && (T.z in using_map.admin_levels)) + secured_wires = 1 + if (secured_wires) + wires = new/datum/wires/airlock/secure(src) + else + wires = new/datum/wires/airlock(src) + +/obj/machinery/door/airlock/Initialize() + if(src.closeOtherId != null) + for (var/obj/machinery/door/airlock/A in machines) + if(A.closeOtherId == src.closeOtherId && A != src) + src.closeOther = A + break + name = "\improper [name]" + . = ..() + +/obj/machinery/door/airlock/Destroy() + qdel(wires) + wires = null + return ..() + +// Most doors will never be deconstructed over the course of a round, +// so as an optimization defer the creation of electronics until +// the airlock is deconstructed +/obj/machinery/door/airlock/proc/create_electronics() + //create new electronics + if (secured_wires) + src.electronics = new/obj/item/weapon/airlock_electronics/secure( src.loc ) + else + src.electronics = new/obj/item/weapon/airlock_electronics( src.loc ) + + //update the electronics to match the door's access + if(LAZYLEN(req_access)) + electronics.conf_access = req_access + else if (LAZYLEN(req_one_access)) + electronics.conf_access = req_one_access + electronics.one_access = 1 + +/obj/machinery/door/airlock/emp_act(var/severity) + if(prob(40/severity)) + var/duration = world.time + SecondsToTicks(30 / severity) + if(duration > electrified_until) + electrify(duration) + ..() + +/obj/machinery/door/airlock/power_change() //putting this is obj/machinery/door itself makes non-airlock doors turn invisible for some reason + ..() + if(stat & NOPOWER) + // If we lost power, disable electrification + // Keeping door lights on, runs on internal battery or something. + electrified_until = 0 + update_icon() + +/obj/machinery/door/airlock/proc/prison_open() + if(arePowerSystemsOn()) + src.unlock() + src.open() + src.lock() + return + + +/obj/machinery/door/airlock/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_DECONSTRUCT) + // Old RCD code made it cost 10 units to decon an airlock. + // Now the new one costs ten "sheets". + return list( + RCD_VALUE_MODE = RCD_DECONSTRUCT, + RCD_VALUE_DELAY = 5 SECONDS, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 10 + ) + return FALSE + +/obj/machinery/door/airlock/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_DECONSTRUCT) + to_chat(user, span("notice", "You deconstruct \the [src].")) + qdel(src) + return TRUE + return FALSE diff --git a/code/game/machinery/doors/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm index 3d401fa0fb6..6aac668b509 100644 --- a/code/game/machinery/doors/airlock_electronics.dm +++ b/code/game/machinery/doors/airlock_electronics.dm @@ -1,153 +1,153 @@ -/obj/item/weapon/airlock_electronics - name = "airlock electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - w_class = ITEMSIZE_SMALL //It should be tiny! -Agouri - - matter = list(MAT_STEEL = 50,MAT_GLASS = 50) - - req_one_access = list(access_engine, access_talon) // Access to unlock the device, ignored if emagged //VOREStation Edit - Add talon - var/list/apply_any_access = list(access_engine) // Can apply any access, not just their own - - var/secure = 0 //if set, then wires will be randomized and bolts will drop if the door is broken - var/list/conf_access = null - var/one_access = 0 //if set to 1, door would receive req_one_access instead of req_access - var/last_configurator = null - var/locked = 1 - var/emagged = 0 - -/obj/item/weapon/airlock_electronics/emag_act(var/remaining_charges, var/mob/user) - if(!emagged) - emagged = 1 - to_chat(user, "You remove the access restrictions on [src]!") - return 1 - -/obj/item/weapon/airlock_electronics/attack_self(mob/user as mob) - if (!ishuman(user) && !istype(user,/mob/living/silicon/robot)) - return ..(user) - - var/t1 = text("Access control
    \n") - - if (last_configurator) - t1 += "Operator: [last_configurator]
    " - - if (locked) - t1 += "Unlock Interface
    " - else - t1 += "Lock Interface
    " - - t1 += "Access requirement is set to " - t1 += one_access ? "ONE
    " : "ALL
    " - - t1 += conf_access == null ? "All
    " : "All
    " - - t1 += "
    " - - var/list/accesses = get_available_accesses(user) - for (var/acc in accesses) - var/aname = get_access_desc(acc) - - if (!conf_access || !conf_access.len || !(acc in conf_access)) - t1 += "[aname]
    " - else if(one_access) - t1 += "[aname]
    " - else - t1 += "[aname]
    " - - t1 += text("

    Close

    \n", src) - - user << browse(t1, "window=airlock_electronics") - onclose(user, "airlock") - -/obj/item/weapon/airlock_electronics/Topic(href, href_list) - ..() - if (usr.stat || usr.restrained() || (!ishuman(usr) && !istype(usr,/mob/living/silicon))) - return - if (href_list["close"]) - usr << browse(null, "window=airlock_electronics") - return - - if (href_list["login"]) - if(emagged) - src.locked = 0 - src.last_configurator = usr.name - else if(issilicon(usr)) - src.locked = 0 - src.last_configurator = usr.name - else if(isliving(usr)) - var/obj/item/weapon/card/id/id - if(ishuman(usr)) - var/mob/living/carbon/human/H = usr - id = H.get_idcard() - // In their ID slot? - if(id && src.check_access(id)) - src.locked = 0 - src.last_configurator = id.registered_name - // Still locked, human handling didn't do it! - if(locked) - var/obj/item/I = usr.get_active_hand() - id = I?.GetID() - if(id && src.check_access(id)) - src.locked = 0 - src.last_configurator = id.registered_name - - if (locked) - return - - if (href_list["logout"]) - locked = 1 - - if (href_list["one_access"]) - one_access = !one_access - - if (href_list["access"]) - toggle_access(href_list["access"]) - - attack_self(usr) - -/obj/item/weapon/airlock_electronics/proc/toggle_access(var/acc) - if (acc == "all") - conf_access = null - else - var/req = text2num(acc) - - if (conf_access == null) - conf_access = list() - - if (!(req in conf_access)) - conf_access += req - else - conf_access -= req - if (!conf_access.len) - conf_access = null - -/obj/item/weapon/airlock_electronics/proc/get_available_accesses(var/mob/user) - var/obj/item/weapon/card/id/id - if(ishuman(user)) - var/mob/living/carbon/human/H = user - id = H.get_idcard() - else if(issilicon(user)) - var/mob/living/silicon/R = user - id = R.idcard - - // Nothing - if(!id || !id.access) - return list() - - // Has engineer access, can put any access - else if(has_access(null, apply_any_access, id.access)) - return get_all_station_access() - - // Not an engineer, can only pick your own accesses to program - else - return id.access - -/obj/item/weapon/airlock_electronics/secure - name = "secure airlock electronics" - desc = "designed to be somewhat more resistant to hacking than standard electronics." - origin_tech = list(TECH_DATA = 2) - secure = 1 - -/obj/item/weapon/airlock_electronics/secure/emag_act(var/remaining_charges, var/mob/user) - to_chat(user, "You don't appear to be able to bypass this hardened device!") - return -1 +/obj/item/weapon/airlock_electronics + name = "airlock electronics" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_electronics" + w_class = ITEMSIZE_SMALL //It should be tiny! -Agouri + + matter = list(MAT_STEEL = 50,MAT_GLASS = 50) + + req_one_access = list(access_engine, access_talon) // Access to unlock the device, ignored if emagged //VOREStation Edit - Add talon + var/list/apply_any_access = list(access_engine) // Can apply any access, not just their own + + var/secure = 0 //if set, then wires will be randomized and bolts will drop if the door is broken + var/list/conf_access = null + var/one_access = 0 //if set to 1, door would receive req_one_access instead of req_access + var/last_configurator = null + var/locked = 1 + var/emagged = 0 + +/obj/item/weapon/airlock_electronics/emag_act(var/remaining_charges, var/mob/user) + if(!emagged) + emagged = 1 + to_chat(user, "You remove the access restrictions on [src]!") + return 1 + +/obj/item/weapon/airlock_electronics/attack_self(mob/user as mob) + if (!ishuman(user) && !istype(user,/mob/living/silicon/robot)) + return ..(user) + + var/t1 = text("Access control
    \n") + + if (last_configurator) + t1 += "Operator: [last_configurator]
    " + + if (locked) + t1 += "Unlock Interface
    " + else + t1 += "Lock Interface
    " + + t1 += "Access requirement is set to " + t1 += one_access ? "ONE
    " : "ALL
    " + + t1 += conf_access == null ? "All
    " : "All
    " + + t1 += "
    " + + var/list/accesses = get_available_accesses(user) + for (var/acc in accesses) + var/aname = get_access_desc(acc) + + if (!conf_access || !conf_access.len || !(acc in conf_access)) + t1 += "[aname]
    " + else if(one_access) + t1 += "[aname]
    " + else + t1 += "[aname]
    " + + t1 += text("

    Close

    \n", src) + + user << browse(t1, "window=airlock_electronics") + onclose(user, "airlock") + +/obj/item/weapon/airlock_electronics/Topic(href, href_list) + ..() + if (usr.stat || usr.restrained() || (!ishuman(usr) && !istype(usr,/mob/living/silicon))) + return + if (href_list["close"]) + usr << browse(null, "window=airlock_electronics") + return + + if (href_list["login"]) + if(emagged) + src.locked = 0 + src.last_configurator = usr.name + else if(issilicon(usr)) + src.locked = 0 + src.last_configurator = usr.name + else if(isliving(usr)) + var/obj/item/weapon/card/id/id + if(ishuman(usr)) + var/mob/living/carbon/human/H = usr + id = H.get_idcard() + // In their ID slot? + if(id && src.check_access(id)) + src.locked = 0 + src.last_configurator = id.registered_name + // Still locked, human handling didn't do it! + if(locked) + var/obj/item/I = usr.get_active_hand() + id = I?.GetID() + if(id && src.check_access(id)) + src.locked = 0 + src.last_configurator = id.registered_name + + if (locked) + return + + if (href_list["logout"]) + locked = 1 + + if (href_list["one_access"]) + one_access = !one_access + + if (href_list["access"]) + toggle_access(href_list["access"]) + + attack_self(usr) + +/obj/item/weapon/airlock_electronics/proc/toggle_access(var/acc) + if (acc == "all") + conf_access = null + else + var/req = text2num(acc) + + if (conf_access == null) + conf_access = list() + + if (!(req in conf_access)) + conf_access += req + else + conf_access -= req + if (!conf_access.len) + conf_access = null + +/obj/item/weapon/airlock_electronics/proc/get_available_accesses(var/mob/user) + var/obj/item/weapon/card/id/id + if(ishuman(user)) + var/mob/living/carbon/human/H = user + id = H.get_idcard() + else if(issilicon(user)) + var/mob/living/silicon/R = user + id = R.idcard + + // Nothing + if(!id || !id.access) + return list() + + // Has engineer access, can put any access + else if(has_access(null, apply_any_access, id.access)) + return get_all_station_access() + + // Not an engineer, can only pick your own accesses to program + else + return id.access + +/obj/item/weapon/airlock_electronics/secure + name = "secure airlock electronics" + desc = "designed to be somewhat more resistant to hacking than standard electronics." + origin_tech = list(TECH_DATA = 2) + secure = 1 + +/obj/item/weapon/airlock_electronics/secure/emag_act(var/remaining_charges, var/mob/user) + to_chat(user, "You don't appear to be able to bypass this hardened device!") + return -1 diff --git a/code/game/machinery/doors/alarmlock.dm b/code/game/machinery/doors/alarmlock.dm index f7dda48feb4..f8d0ea6b57e 100644 --- a/code/game/machinery/doors/alarmlock.dm +++ b/code/game/machinery/doors/alarmlock.dm @@ -1,45 +1,45 @@ -/obj/machinery/door/airlock/alarmlock - - name = "Glass Alarm Airlock" - icon = 'icons/obj/doors/Doorglass.dmi' - opacity = 0 - glass = 1 - - var/datum/radio_frequency/air_connection - var/air_frequency = 1437 - autoclose = 0 - -/obj/machinery/door/airlock/alarmlock/New() - ..() - air_connection = new - -/obj/machinery/door/airlock/alarmlock/Destroy() - if(radio_controller) - radio_controller.remove_object(src,air_frequency) - ..() - -/obj/machinery/door/airlock/alarmlock/Initialize() - . = ..() - radio_controller.remove_object(src, air_frequency) - air_connection = radio_controller.add_object(src, air_frequency, RADIO_TO_AIRALARM) - open() - - -/obj/machinery/door/airlock/alarmlock/receive_signal(datum/signal/signal) - ..() - if(stat & (NOPOWER|BROKEN)) - return - - var/alarm_area = signal.data["zone"] - var/alert = signal.data["alert"] - - var/area/our_area = get_area(src) - - if(alarm_area == our_area.name) - switch(alert) - if("severe") - autoclose = 1 - close() - if("minor", "clear") - autoclose = 0 - open() +/obj/machinery/door/airlock/alarmlock + + name = "Glass Alarm Airlock" + icon = 'icons/obj/doors/Doorglass.dmi' + opacity = 0 + glass = 1 + + var/datum/radio_frequency/air_connection + var/air_frequency = 1437 + autoclose = 0 + +/obj/machinery/door/airlock/alarmlock/New() + ..() + air_connection = new + +/obj/machinery/door/airlock/alarmlock/Destroy() + if(radio_controller) + radio_controller.remove_object(src,air_frequency) + ..() + +/obj/machinery/door/airlock/alarmlock/Initialize() + . = ..() + radio_controller.remove_object(src, air_frequency) + air_connection = radio_controller.add_object(src, air_frequency, RADIO_TO_AIRALARM) + open() + + +/obj/machinery/door/airlock/alarmlock/receive_signal(datum/signal/signal) + ..() + if(stat & (NOPOWER|BROKEN)) + return + + var/alarm_area = signal.data["zone"] + var/alert = signal.data["alert"] + + var/area/our_area = get_area(src) + + if(alarm_area == our_area.name) + switch(alert) + if("severe") + autoclose = 1 + close() + if("minor", "clear") + autoclose = 0 + open() diff --git a/code/game/machinery/doors/checkForMultipleDoors.dm b/code/game/machinery/doors/checkForMultipleDoors.dm index f7c6ff98b15..2e2f0768cb1 100644 --- a/code/game/machinery/doors/checkForMultipleDoors.dm +++ b/code/game/machinery/doors/checkForMultipleDoors.dm @@ -1,16 +1,16 @@ -/obj/machinery/door/proc/checkForMultipleDoors() - if(!src.loc) - return 0 - for(var/obj/machinery/door/D in src.loc) - if(!istype(D, /obj/machinery/door/window) && D.density) - return 0 - return 1 - -/turf/simulated/wall/proc/checkForMultipleDoors() - if(!src.loc) - return 0 - for(var/obj/machinery/door/D in locate(src.x,src.y,src.z)) - if(!istype(D, /obj/machinery/door/window) && D.density) - return 0 - //There are no false wall checks because that would be mischievious - return 1 +/obj/machinery/door/proc/checkForMultipleDoors() + if(!src.loc) + return 0 + for(var/obj/machinery/door/D in src.loc) + if(!istype(D, /obj/machinery/door/window) && D.density) + return 0 + return 1 + +/turf/simulated/wall/proc/checkForMultipleDoors() + if(!src.loc) + return 0 + for(var/obj/machinery/door/D in locate(src.x,src.y,src.z)) + if(!istype(D, /obj/machinery/door/window) && D.density) + return 0 + //There are no false wall checks because that would be mischievious + return 1 diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 2e31ea43f63..5425b712109 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -1,514 +1,514 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 -#define DOOR_REPAIR_AMOUNT 50 //amount of health regained per stack amount used - -/obj/machinery/door - name = "Door" - desc = "It opens and closes." - icon = 'icons/obj/doors/Doorint.dmi' - icon_state = "door1" - anchored = TRUE - opacity = 1 - density = TRUE - can_atmos_pass = ATMOS_PASS_PROC - layer = DOOR_OPEN_LAYER - blocks_emissive = EMISSIVE_BLOCK_UNIQUE - var/open_layer = DOOR_OPEN_LAYER - var/closed_layer = DOOR_CLOSED_LAYER - - var/visible = 1 - var/p_open = 0 - var/operating = 0 - var/autoclose = 0 - var/glass = 0 - var/normalspeed = 1 - var/heat_proof = 0 // For glass airlocks/opacity firedoors - var/air_properties_vary_with_direction = 0 - var/maxhealth = 300 - var/health - var/destroy_hits = 10 //How many strong hits it takes to destroy the door - var/min_force = 10 //minimum amount of force needed to damage the door with a melee weapon - var/hitsound = 'sound/weapons/smash.ogg' //sound door makes when hit with a weapon - var/repairing = 0 - var/block_air_zones = 1 //If set, air zones cannot merge across the door even when it is opened. - var/close_door_at = 0 //When to automatically close the door, if possible - - var/anim_length_before_density = 3 - var/anim_length_before_finalize = 7 - - //Multi-tile doors - dir = EAST - var/width = 1 - - // turf animation - var/atom/movable/overlay/c_animation = null - -/obj/machinery/door/attack_generic(var/mob/user, var/damage) - if(isanimal(user)) - var/mob/living/simple_mob/S = user - if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) - visible_message("\The [user] smashes into [src]!") - playsound(src, S.attack_sound, 75, 1) - take_damage(damage) - else - visible_message("\The [user] bonks \the [src] harmlessly.") - user.do_attack_animation(src) - -/obj/machinery/door/New() - . = ..() - if(density) - layer = closed_layer - explosion_resistance = initial(explosion_resistance) - update_heat_protection(get_turf(src)) - else - layer = open_layer - explosion_resistance = 0 - - - if(width > 1) - if(dir in list(EAST, WEST)) - bound_width = width * world.icon_size - bound_height = world.icon_size - else - bound_width = world.icon_size - bound_height = width * world.icon_size - - health = maxhealth - update_icon() - - update_nearby_tiles(need_rebuild=1) - return - -/obj/machinery/door/Destroy() - density = FALSE - update_nearby_tiles() - . = ..() - -/obj/machinery/door/process() - if(close_door_at && world.time >= close_door_at) - if(autoclose) - close_door_at = world.time + next_close_wait() - spawn(0) - close() - else - close_door_at = 0 - if (..() == PROCESS_KILL && !close_door_at) - return PROCESS_KILL - -/obj/machinery/door/proc/autoclose_in(wait) - close_door_at = world.time + wait - START_MACHINE_PROCESSING(src) - -/obj/machinery/door/proc/can_open() - if(!density || operating || !ticker) - return 0 - return 1 - -/obj/machinery/door/proc/can_close() - if(density || operating || !ticker) - return 0 - return 1 - -/obj/machinery/door/Bumped(atom/AM) - . = ..() - if(p_open || operating) - return - if(ismob(AM)) - var/mob/M = AM - if(world.time - M.last_bumped <= 10) - return //Can bump-open one airlock per second. This is to prevent shock spam. - M.last_bumped = world.time - if(M.restrained() && !check_access(null)) - return - else if(istype(M, /mob/living/simple_mob/animal/passive/mouse) && !(M.ckey)) //VOREStation Edit: Make wild mice - return //VOREStation Edit: unable to open doors - else - bumpopen(M) - if(istype(AM, /obj/item/device/uav)) - if(check_access(null)) - open() - else - do_animate("deny") - - if(istype(AM, /mob/living/bot)) - var/mob/living/bot/bot = AM - if(src.check_access(bot.botcard)) - if(density) - open() - return - - if(istype(AM, /obj/mecha)) - var/obj/mecha/mecha = AM - if(density) - if(mecha.occupant && (src.allowed(mecha.occupant) || src.check_access_list(mecha.operation_req_access))) - open() - else - do_animate("deny") - return - if(istype(AM, /obj/structure/bed/chair/wheelchair)) - var/obj/structure/bed/chair/wheelchair/wheel = AM - if(density) - if(wheel.pulling && (src.allowed(wheel.pulling))) - open() - else - do_animate("deny") - -/obj/machinery/door/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return !opacity - return !density - -/obj/machinery/door/CanZASPass(turf/T, is_zone) - if(is_zone) - return !block_air_zones // Block merging unless block_air_zones = 0 - return !density // Block airflow unless density = FALSE - -/obj/machinery/door/proc/bumpopen(mob/user as mob) - if(operating) return - if(user.last_airflow > world.time - vsc.airflow_delay) //Fakkit - return - src.add_fingerprint(user) - if(density) - if(allowed(user)) open() - else do_animate("deny") - return - -/obj/machinery/door/bullet_act(var/obj/item/projectile/Proj) - ..() - - var/damage = Proj.get_structure_damage() - - // Emitter Blasts - these will eventually completely destroy the door, given enough time. - if (damage > 90) - destroy_hits-- - if (destroy_hits <= 0) - visible_message("\The [src.name] disintegrates!") - switch (Proj.damage_type) - if(BRUTE) - new /obj/item/stack/material/steel(src.loc, 2) - new /obj/item/stack/rods(src.loc, 3) - if(BURN) - new /obj/effect/decal/cleanable/ash(src.loc) // Turn it to ashes! - qdel(src) - - if(damage) - //cap projectile damage so that there's still a minimum number of hits required to break the door - take_damage(min(damage, 100)) - - - -/obj/machinery/door/hitby(AM as mob|obj, var/speed=5) - - ..() - visible_message("[src.name] was hit by [AM].") - var/tforce = 0 - if(ismob(AM)) - tforce = 15 * (speed/5) - else - tforce = AM:throwforce * (speed/5) - playsound(src, hitsound, 100, 1) - take_damage(tforce) - return - -/obj/machinery/door/attack_ai(mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/door/attack_hand(mob/user as mob) - return src.attackby(user, user) - -/obj/machinery/door/attack_tk(mob/user as mob) - if(requiresID() && !allowed(null)) - return - ..() - -/obj/machinery/door/attackby(obj/item/I as obj, mob/user as mob) - src.add_fingerprint(user) - - if(istype(I)) - if(attackby_vr(I, user)) //VOREStation begin: Fireproofing - return //VOREStation begin: Fireproofing - if(istype(I, /obj/item/stack/material) && I.get_material_name() == src.get_material_name()) - if(stat & BROKEN) - to_chat(user, "It looks like \the [src] is pretty busted. It's going to need more than just patching up now.") - return - if(health >= maxhealth) - to_chat(user, "Nothing to fix!") - return - if(!density) - to_chat(user, "\The [src] must be closed before you can repair it.") - return - - //figure out how much metal we need - var/amount_needed = (maxhealth - health) / DOOR_REPAIR_AMOUNT - amount_needed = (round(amount_needed) == amount_needed)? amount_needed : round(amount_needed) + 1 //Why does BYOND not have a ceiling proc? - - var/obj/item/stack/stack = I - var/amount_given = amount_needed - repairing - var/mats_given = stack.get_amount() - if(repairing && amount_given <= 0) - to_chat(user, "You must weld or remove \the [get_material_name()] from \the [src] before you can add anything else.") - else - if(mats_given >= amount_given) - if(stack.use(amount_given)) - repairing += amount_given - else - if(stack.use(mats_given)) - repairing += mats_given - amount_given = mats_given - if(amount_given) - to_chat(user, "You fit [amount_given] [stack.singular_name]\s to damaged and broken parts on \the [src].") - - return - - if(repairing && I.has_tool_quality(TOOL_WELDER)) - if(!density) - to_chat(user, "\The [src] must be closed before you can repair it.") - return - - var/obj/item/weapon/weldingtool/welder = I.get_welder() - if(welder.remove_fuel(0,user)) - to_chat(user, "You start to fix dents and weld \the [get_material_name()] into place.") - playsound(src, welder.usesound, 50, 1) - if(do_after(user, (5 * repairing) * welder.toolspeed) && welder && welder.isOn()) - to_chat(user, "You finish repairing the damage to \the [src].") - health = between(health, health + repairing*DOOR_REPAIR_AMOUNT, maxhealth) - update_icon() - repairing = 0 - return - - if(repairing && I.has_tool_quality(TOOL_CROWBAR)) - var/datum/material/mat = get_material() - var/obj/item/stack/material/repairing_sheet = mat.place_sheet(loc, repairing) - repairing = 0 - to_chat(user, "You remove \the [repairing_sheet].") - playsound(src, I.usesound, 100, 1) - return - - //psa to whoever coded this, there are plenty of objects that need to call attack() on doors without bludgeoning them. - if(src.density && istype(I, /obj/item/weapon) && user.a_intent == I_HURT && !istype(I, /obj/item/weapon/card)) - var/obj/item/weapon/W = I - user.setClickCooldown(user.get_attack_speed(W)) - if(W.damtype == BRUTE || W.damtype == BURN) - user.do_attack_animation(src) - if(W.force < min_force) - user.visible_message("\The [user] hits \the [src] with \the [W] with no visible effect.") - else - user.visible_message("\The [user] forcefully strikes \the [src] with \the [W]!") - playsound(src, hitsound, 100, 1) - take_damage(W.force) - return - - if(src.operating > 0 || isrobot(user)) - return //borgs can't attack doors open because it conflicts with their AI-like interaction with them. - - if(src.operating) - return - - if(src.allowed(user) && operable()) - if(src.density) - open() - else - close() - return - - if(src.density) - do_animate("deny") - return - -/obj/machinery/door/emag_act(var/remaining_charges) - if(density && operable()) - do_animate("spark") - sleep(6) - open() - operating = -1 - return 1 - -/obj/machinery/door/take_damage(var/damage) - var/initialhealth = src.health - src.health = max(0, src.health - damage) - if(src.health <= 0 && initialhealth > 0) - src.set_broken() - else if(src.health < src.maxhealth / 4 && initialhealth >= src.maxhealth / 4) - visible_message("\The [src] looks like it's about to break!" ) - else if(src.health < src.maxhealth / 2 && initialhealth >= src.maxhealth / 2) - visible_message("\The [src] looks seriously damaged!" ) - else if(src.health < src.maxhealth * 3/4 && initialhealth >= src.maxhealth * 3/4) - visible_message("\The [src] shows signs of damage!" ) - update_icon() - return - - -/obj/machinery/door/examine(mob/user) - . = ..() - if(src.health <= 0) - . += "It is broken!" - else if(src.health < src.maxhealth / 4) - . += "It looks like it's about to break!" - else if(src.health < src.maxhealth / 2) - . += "It looks seriously damaged!" - else if(src.health < src.maxhealth * 3/4) - . += "It shows signs of damage!" - - -/obj/machinery/door/proc/set_broken() - stat |= BROKEN - for (var/mob/O in viewers(src, null)) - if ((O.client && !( O.blinded ))) - O.show_message("[src.name] breaks!" ) - update_icon() - return - - -/obj/machinery/door/emp_act(severity) - if(prob(20/severity) && (istype(src,/obj/machinery/door/airlock) || istype(src,/obj/machinery/door/window)) ) - spawn(0) - open() - ..() - - -/obj/machinery/door/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - if(2.0) - if(prob(25)) - qdel(src) - else - take_damage(300) - if(3.0) - if(prob(80)) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(2, 1, src) - s.start() - else - take_damage(150) - return - -/obj/machinery/door/blob_act() - if(density) // If it's closed. - if(stat & BROKEN) - spawn(0) - open(1) - else - take_damage(100) - -/obj/machinery/door/update_icon() - if(density) - icon_state = "door1" - else - icon_state = "door0" - SSradiation.resistance_cache.Remove(get_turf(src)) - return - - -/obj/machinery/door/proc/do_animate(animation) - switch(animation) - if("opening") - if(p_open) - flick("o_doorc0", src) - else - flick("doorc0", src) - if("closing") - if(p_open) - flick("o_doorc1", src) - else - flick("doorc1", src) - if("spark") - if(density) - flick("door_spark", src) - if("deny") - if(density && !(stat & (NOPOWER|BROKEN))) - flick("door_deny", src) - playsound(src, 'sound/machines/buzz-two.ogg', 50, 0) - return - - -/obj/machinery/door/proc/open(var/forced = 0) - if(!can_open(forced)) - return - operating = 1 - - do_animate("opening") - icon_state = "door0" - set_opacity(0) - sleep(anim_length_before_density) - src.density = FALSE - update_nearby_tiles() - sleep(anim_length_before_finalize) - src.layer = open_layer - explosion_resistance = 0 - update_icon() - set_opacity(0) - operating = 0 - - if(autoclose) - autoclose_in(next_close_wait()) - - return 1 - -/obj/machinery/door/proc/next_close_wait() - return (normalspeed ? 150 : 5) - -/obj/machinery/door/proc/close(var/forced = 0) - if(!can_close(forced)) - return - operating = 1 - - close_door_at = 0 - do_animate("closing") - sleep(anim_length_before_density) - src.density = TRUE - explosion_resistance = initial(explosion_resistance) - src.layer = closed_layer - update_nearby_tiles() - sleep(anim_length_before_finalize) - update_icon() - if(visible && !glass) - set_opacity(1) //caaaaarn! - operating = 0 - - //I shall not add a check every x ticks if a door has closed over some fire. - var/obj/fire/fire = locate() in loc - if(fire) - qdel(fire) - - return 1 - -/obj/machinery/door/proc/requiresID() - return 1 - -/obj/machinery/door/allowed(mob/M) - if(!requiresID()) - return ..(null) //don't care who they are or what they have, act as if they're NOTHING - return ..(M) - -/obj/machinery/door/update_nearby_tiles(need_rebuild) - if(!air_master) - return 0 - - for(var/turf/simulated/turf in locs) - update_heat_protection(turf) - air_master.mark_for_update(turf) - - return 1 - -/obj/machinery/door/proc/update_heat_protection(var/turf/simulated/source) - if(istype(source)) - if(src.density && (src.opacity || src.heat_proof)) - source.thermal_conductivity = DOOR_HEAT_TRANSFER_COEFFICIENT - else - source.thermal_conductivity = initial(source.thermal_conductivity) - -/obj/machinery/door/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(width > 1) - if(dir in list(EAST, WEST)) - bound_width = width * world.icon_size - bound_height = world.icon_size - else - bound_width = world.icon_size - bound_height = width * world.icon_size - - update_nearby_tiles() - -/obj/machinery/door/morgue - icon = 'icons/obj/doors/doormorgue.dmi' +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 +#define DOOR_REPAIR_AMOUNT 50 //amount of health regained per stack amount used + +/obj/machinery/door + name = "Door" + desc = "It opens and closes." + icon = 'icons/obj/doors/Doorint.dmi' + icon_state = "door1" + anchored = TRUE + opacity = 1 + density = TRUE + can_atmos_pass = ATMOS_PASS_PROC + layer = DOOR_OPEN_LAYER + blocks_emissive = EMISSIVE_BLOCK_UNIQUE + var/open_layer = DOOR_OPEN_LAYER + var/closed_layer = DOOR_CLOSED_LAYER + + var/visible = 1 + var/p_open = 0 + var/operating = 0 + var/autoclose = 0 + var/glass = 0 + var/normalspeed = 1 + var/heat_proof = 0 // For glass airlocks/opacity firedoors + var/air_properties_vary_with_direction = 0 + var/maxhealth = 300 + var/health + var/destroy_hits = 10 //How many strong hits it takes to destroy the door + var/min_force = 10 //minimum amount of force needed to damage the door with a melee weapon + var/hitsound = 'sound/weapons/smash.ogg' //sound door makes when hit with a weapon + var/repairing = 0 + var/block_air_zones = 1 //If set, air zones cannot merge across the door even when it is opened. + var/close_door_at = 0 //When to automatically close the door, if possible + + var/anim_length_before_density = 3 + var/anim_length_before_finalize = 7 + + //Multi-tile doors + dir = EAST + var/width = 1 + + // turf animation + var/atom/movable/overlay/c_animation = null + +/obj/machinery/door/attack_generic(var/mob/user, var/damage) + if(isanimal(user)) + var/mob/living/simple_mob/S = user + if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) + visible_message("\The [user] smashes into [src]!") + playsound(src, S.attack_sound, 75, 1) + take_damage(damage) + else + visible_message("\The [user] bonks \the [src] harmlessly.") + user.do_attack_animation(src) + +/obj/machinery/door/New() + . = ..() + if(density) + layer = closed_layer + explosion_resistance = initial(explosion_resistance) + update_heat_protection(get_turf(src)) + else + layer = open_layer + explosion_resistance = 0 + + + if(width > 1) + if(dir in list(EAST, WEST)) + bound_width = width * world.icon_size + bound_height = world.icon_size + else + bound_width = world.icon_size + bound_height = width * world.icon_size + + health = maxhealth + update_icon() + + update_nearby_tiles(need_rebuild=1) + return + +/obj/machinery/door/Destroy() + density = FALSE + update_nearby_tiles() + . = ..() + +/obj/machinery/door/process() + if(close_door_at && world.time >= close_door_at) + if(autoclose) + close_door_at = world.time + next_close_wait() + spawn(0) + close() + else + close_door_at = 0 + if (..() == PROCESS_KILL && !close_door_at) + return PROCESS_KILL + +/obj/machinery/door/proc/autoclose_in(wait) + close_door_at = world.time + wait + START_MACHINE_PROCESSING(src) + +/obj/machinery/door/proc/can_open() + if(!density || operating || !ticker) + return 0 + return 1 + +/obj/machinery/door/proc/can_close() + if(density || operating || !ticker) + return 0 + return 1 + +/obj/machinery/door/Bumped(atom/AM) + . = ..() + if(p_open || operating) + return + if(ismob(AM)) + var/mob/M = AM + if(world.time - M.last_bumped <= 10) + return //Can bump-open one airlock per second. This is to prevent shock spam. + M.last_bumped = world.time + if(M.restrained() && !check_access(null)) + return + else if(istype(M, /mob/living/simple_mob/animal/passive/mouse) && !(M.ckey)) //VOREStation Edit: Make wild mice + return //VOREStation Edit: unable to open doors + else + bumpopen(M) + if(istype(AM, /obj/item/device/uav)) + if(check_access(null)) + open() + else + do_animate("deny") + + if(istype(AM, /mob/living/bot)) + var/mob/living/bot/bot = AM + if(src.check_access(bot.botcard)) + if(density) + open() + return + + if(istype(AM, /obj/mecha)) + var/obj/mecha/mecha = AM + if(density) + if(mecha.occupant && (src.allowed(mecha.occupant) || src.check_access_list(mecha.operation_req_access))) + open() + else + do_animate("deny") + return + if(istype(AM, /obj/structure/bed/chair/wheelchair)) + var/obj/structure/bed/chair/wheelchair/wheel = AM + if(density) + if(wheel.pulling && (src.allowed(wheel.pulling))) + open() + else + do_animate("deny") + +/obj/machinery/door/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return !opacity + return !density + +/obj/machinery/door/CanZASPass(turf/T, is_zone) + if(is_zone) + return !block_air_zones // Block merging unless block_air_zones = 0 + return !density // Block airflow unless density = FALSE + +/obj/machinery/door/proc/bumpopen(mob/user as mob) + if(operating) return + if(user.last_airflow > world.time - vsc.airflow_delay) //Fakkit + return + src.add_fingerprint(user) + if(density) + if(allowed(user)) open() + else do_animate("deny") + return + +/obj/machinery/door/bullet_act(var/obj/item/projectile/Proj) + ..() + + var/damage = Proj.get_structure_damage() + + // Emitter Blasts - these will eventually completely destroy the door, given enough time. + if (damage > 90) + destroy_hits-- + if (destroy_hits <= 0) + visible_message("\The [src.name] disintegrates!") + switch (Proj.damage_type) + if(BRUTE) + new /obj/item/stack/material/steel(src.loc, 2) + new /obj/item/stack/rods(src.loc, 3) + if(BURN) + new /obj/effect/decal/cleanable/ash(src.loc) // Turn it to ashes! + qdel(src) + + if(damage) + //cap projectile damage so that there's still a minimum number of hits required to break the door + take_damage(min(damage, 100)) + + + +/obj/machinery/door/hitby(AM as mob|obj, var/speed=5) + + ..() + visible_message("[src.name] was hit by [AM].") + var/tforce = 0 + if(ismob(AM)) + tforce = 15 * (speed/5) + else + tforce = AM:throwforce * (speed/5) + playsound(src, hitsound, 100, 1) + take_damage(tforce) + return + +/obj/machinery/door/attack_ai(mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/door/attack_hand(mob/user as mob) + return src.attackby(user, user) + +/obj/machinery/door/attack_tk(mob/user as mob) + if(requiresID() && !allowed(null)) + return + ..() + +/obj/machinery/door/attackby(obj/item/I as obj, mob/user as mob) + src.add_fingerprint(user) + + if(istype(I)) + if(attackby_vr(I, user)) //VOREStation begin: Fireproofing + return //VOREStation begin: Fireproofing + if(istype(I, /obj/item/stack/material) && I.get_material_name() == src.get_material_name()) + if(stat & BROKEN) + to_chat(user, "It looks like \the [src] is pretty busted. It's going to need more than just patching up now.") + return + if(health >= maxhealth) + to_chat(user, "Nothing to fix!") + return + if(!density) + to_chat(user, "\The [src] must be closed before you can repair it.") + return + + //figure out how much metal we need + var/amount_needed = (maxhealth - health) / DOOR_REPAIR_AMOUNT + amount_needed = (round(amount_needed) == amount_needed)? amount_needed : round(amount_needed) + 1 //Why does BYOND not have a ceiling proc? + + var/obj/item/stack/stack = I + var/amount_given = amount_needed - repairing + var/mats_given = stack.get_amount() + if(repairing && amount_given <= 0) + to_chat(user, "You must weld or remove \the [get_material_name()] from \the [src] before you can add anything else.") + else + if(mats_given >= amount_given) + if(stack.use(amount_given)) + repairing += amount_given + else + if(stack.use(mats_given)) + repairing += mats_given + amount_given = mats_given + if(amount_given) + to_chat(user, "You fit [amount_given] [stack.singular_name]\s to damaged and broken parts on \the [src].") + + return + + if(repairing && I.has_tool_quality(TOOL_WELDER)) + if(!density) + to_chat(user, "\The [src] must be closed before you can repair it.") + return + + var/obj/item/weapon/weldingtool/welder = I.get_welder() + if(welder.remove_fuel(0,user)) + to_chat(user, "You start to fix dents and weld \the [get_material_name()] into place.") + playsound(src, welder.usesound, 50, 1) + if(do_after(user, (5 * repairing) * welder.toolspeed) && welder && welder.isOn()) + to_chat(user, "You finish repairing the damage to \the [src].") + health = between(health, health + repairing*DOOR_REPAIR_AMOUNT, maxhealth) + update_icon() + repairing = 0 + return + + if(repairing && I.has_tool_quality(TOOL_CROWBAR)) + var/datum/material/mat = get_material() + var/obj/item/stack/material/repairing_sheet = mat.place_sheet(loc, repairing) + repairing = 0 + to_chat(user, "You remove \the [repairing_sheet].") + playsound(src, I.usesound, 100, 1) + return + + //psa to whoever coded this, there are plenty of objects that need to call attack() on doors without bludgeoning them. + if(src.density && istype(I, /obj/item/weapon) && user.a_intent == I_HURT && !istype(I, /obj/item/weapon/card)) + var/obj/item/weapon/W = I + user.setClickCooldown(user.get_attack_speed(W)) + if(W.damtype == BRUTE || W.damtype == BURN) + user.do_attack_animation(src) + if(W.force < min_force) + user.visible_message("\The [user] hits \the [src] with \the [W] with no visible effect.") + else + user.visible_message("\The [user] forcefully strikes \the [src] with \the [W]!") + playsound(src, hitsound, 100, 1) + take_damage(W.force) + return + + if(src.operating > 0 || isrobot(user)) + return //borgs can't attack doors open because it conflicts with their AI-like interaction with them. + + if(src.operating) + return + + if(src.allowed(user) && operable()) + if(src.density) + open() + else + close() + return + + if(src.density) + do_animate("deny") + return + +/obj/machinery/door/emag_act(var/remaining_charges) + if(density && operable()) + do_animate("spark") + sleep(6) + open() + operating = -1 + return 1 + +/obj/machinery/door/take_damage(var/damage) + var/initialhealth = src.health + src.health = max(0, src.health - damage) + if(src.health <= 0 && initialhealth > 0) + src.set_broken() + else if(src.health < src.maxhealth / 4 && initialhealth >= src.maxhealth / 4) + visible_message("\The [src] looks like it's about to break!" ) + else if(src.health < src.maxhealth / 2 && initialhealth >= src.maxhealth / 2) + visible_message("\The [src] looks seriously damaged!" ) + else if(src.health < src.maxhealth * 3/4 && initialhealth >= src.maxhealth * 3/4) + visible_message("\The [src] shows signs of damage!" ) + update_icon() + return + + +/obj/machinery/door/examine(mob/user) + . = ..() + if(src.health <= 0) + . += "It is broken!" + else if(src.health < src.maxhealth / 4) + . += "It looks like it's about to break!" + else if(src.health < src.maxhealth / 2) + . += "It looks seriously damaged!" + else if(src.health < src.maxhealth * 3/4) + . += "It shows signs of damage!" + + +/obj/machinery/door/proc/set_broken() + stat |= BROKEN + for (var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message("[src.name] breaks!" ) + update_icon() + return + + +/obj/machinery/door/emp_act(severity) + if(prob(20/severity) && (istype(src,/obj/machinery/door/airlock) || istype(src,/obj/machinery/door/window)) ) + spawn(0) + open() + ..() + + +/obj/machinery/door/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + if(2.0) + if(prob(25)) + qdel(src) + else + take_damage(300) + if(3.0) + if(prob(80)) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(2, 1, src) + s.start() + else + take_damage(150) + return + +/obj/machinery/door/blob_act() + if(density) // If it's closed. + if(stat & BROKEN) + spawn(0) + open(1) + else + take_damage(100) + +/obj/machinery/door/update_icon() + if(density) + icon_state = "door1" + else + icon_state = "door0" + SSradiation.resistance_cache.Remove(get_turf(src)) + return + + +/obj/machinery/door/proc/do_animate(animation) + switch(animation) + if("opening") + if(p_open) + flick("o_doorc0", src) + else + flick("doorc0", src) + if("closing") + if(p_open) + flick("o_doorc1", src) + else + flick("doorc1", src) + if("spark") + if(density) + flick("door_spark", src) + if("deny") + if(density && !(stat & (NOPOWER|BROKEN))) + flick("door_deny", src) + playsound(src, 'sound/machines/buzz-two.ogg', 50, 0) + return + + +/obj/machinery/door/proc/open(var/forced = 0) + if(!can_open(forced)) + return + operating = 1 + + do_animate("opening") + icon_state = "door0" + set_opacity(0) + sleep(anim_length_before_density) + src.density = FALSE + update_nearby_tiles() + sleep(anim_length_before_finalize) + src.layer = open_layer + explosion_resistance = 0 + update_icon() + set_opacity(0) + operating = 0 + + if(autoclose) + autoclose_in(next_close_wait()) + + return 1 + +/obj/machinery/door/proc/next_close_wait() + return (normalspeed ? 150 : 5) + +/obj/machinery/door/proc/close(var/forced = 0) + if(!can_close(forced)) + return + operating = 1 + + close_door_at = 0 + do_animate("closing") + sleep(anim_length_before_density) + src.density = TRUE + explosion_resistance = initial(explosion_resistance) + src.layer = closed_layer + update_nearby_tiles() + sleep(anim_length_before_finalize) + update_icon() + if(visible && !glass) + set_opacity(1) //caaaaarn! + operating = 0 + + //I shall not add a check every x ticks if a door has closed over some fire. + var/obj/fire/fire = locate() in loc + if(fire) + qdel(fire) + + return 1 + +/obj/machinery/door/proc/requiresID() + return 1 + +/obj/machinery/door/allowed(mob/M) + if(!requiresID()) + return ..(null) //don't care who they are or what they have, act as if they're NOTHING + return ..(M) + +/obj/machinery/door/update_nearby_tiles(need_rebuild) + if(!air_master) + return 0 + + for(var/turf/simulated/turf in locs) + update_heat_protection(turf) + air_master.mark_for_update(turf) + + return 1 + +/obj/machinery/door/proc/update_heat_protection(var/turf/simulated/source) + if(istype(source)) + if(src.density && (src.opacity || src.heat_proof)) + source.thermal_conductivity = DOOR_HEAT_TRANSFER_COEFFICIENT + else + source.thermal_conductivity = initial(source.thermal_conductivity) + +/obj/machinery/door/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(width > 1) + if(dir in list(EAST, WEST)) + bound_width = width * world.icon_size + bound_height = world.icon_size + else + bound_width = world.icon_size + bound_height = width * world.icon_size + + update_nearby_tiles() + +/obj/machinery/door/morgue + icon = 'icons/obj/doors/doormorgue.dmi' diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index c1121eb16e9..22b6f37d60b 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -1,528 +1,528 @@ -#define FIREDOOR_MAX_PRESSURE_DIFF 25 // kPa -#define FIREDOOR_MAX_TEMP 50 // °C -#define FIREDOOR_MIN_TEMP 0 - -// Bitflags -#define FIREDOOR_ALERT_HOT 1 -#define FIREDOOR_ALERT_COLD 2 -// Not used #define FIREDOOR_ALERT_LOWPRESS 4 - -/obj/machinery/door/firedoor - name = "\improper Emergency Shutter" - desc = "Emergency air-tight shutter, capable of sealing off breached areas." - icon = 'icons/obj/doors/DoorHazard.dmi' - icon_state = "door_open" - req_one_access = list(access_eva) //access_atmospherics, access_engine_equip) - opacity = 0 - density = FALSE - layer = DOOR_OPEN_LAYER - 0.01 - open_layer = DOOR_OPEN_LAYER - 0.01 // Just below doors when open - closed_layer = DOOR_CLOSED_LAYER + 0.01 // Just above doors when closed - - //These are frequenly used with windows, so make sure zones can pass. - //Generally if a firedoor is at a place where there should be a zone boundery then there will be a regular door underneath it. - block_air_zones = 0 - - var/blocked = 0 - var/prying = 0 - var/lockdown = 0 // When the door has detected a problem, it locks. - var/pdiff_alert = 0 - var/pdiff = 0 - var/nextstate = null - var/net_id - var/list/areas_added - var/list/users_to_open = new - var/next_process_time = 0 - - var/hatch_open = 0 - - power_channel = ENVIRON - use_power = USE_POWER_IDLE - idle_power_usage = 5 - - var/list/tile_info[4] - var/list/dir_alerts[4] // 4 dirs, bitflags - - // MUST be in same order as FIREDOOR_ALERT_* - var/list/ALERT_STATES=list( - "hot", - "cold" - ) - -/obj/machinery/door/firedoor/Initialize() - . = ..() - //Delete ourselves if we find extra mapped in firedoors - for(var/obj/machinery/door/firedoor/F in loc) - if(F != src) - log_debug("Duplicate firedoors at [x],[y],[z]") - return INITIALIZE_HINT_QDEL - - var/area/A = get_area(src) - ASSERT(istype(A)) - - LAZYADD(A.all_doors, src) - areas_added = list(A) - - for(var/direction in cardinal) - A = get_area(get_step(src,direction)) - if(istype(A) && !(A in areas_added)) - LAZYADD(A.all_doors, src) - areas_added += A - -/obj/machinery/door/firedoor/Destroy() - for(var/area/A in areas_added) - LAZYREMOVE(A.all_doors, src) - . = ..() - -/obj/machinery/door/firedoor/get_material() - return get_material_by_name(MAT_STEEL) - -/obj/machinery/door/firedoor/examine(mob/user) - . = ..() - - if(!Adjacent(user)) - return . - - if(pdiff >= FIREDOOR_MAX_PRESSURE_DIFF) - . += "WARNING: Current pressure differential is [pdiff]kPa! Opening door may result in injury!" - - . += "Sensor readings:" - for(var/index = 1; index <= tile_info.len; index++) - var/o = "  " - switch(index) - if(1) - o += "NORTH: " - if(2) - o += "SOUTH: " - if(3) - o += "EAST: " - if(4) - o += "WEST: " - if(tile_info[index] == null) - o += "DATA UNAVAILABLE" - . += o - continue - var/celsius = convert_k2c(tile_info[index][1]) - var/pressure = tile_info[index][2] - o += "" - o += "[celsius]°C " - o += "" - o += "[pressure]kPa" - . += o - - if(islist(users_to_open) && users_to_open.len) - var/users_to_open_string = users_to_open[1] - if(users_to_open.len >= 2) - for(var/i = 2 to users_to_open.len) - users_to_open_string += ", [users_to_open[i]]" - . += "These people have opened \the [src] during an alert: [users_to_open_string]." - -/obj/machinery/door/firedoor/Bumped(atom/AM) - if(p_open || operating) - return - if(!density) - return ..() - if(istype(AM, /obj/mecha)) - var/obj/mecha/mecha = AM - if(mecha.occupant) - var/mob/M = mecha.occupant - if(world.time - M.last_bumped <= 10) return //Can bump-open one airlock per second. This is to prevent popup message spam. - M.last_bumped = world.time - attack_hand(M) - return 0 - -/obj/machinery/door/firedoor/attack_hand(mob/user as mob) - add_fingerprint(user) - if(operating) - return//Already doing something. - - if(istype(user, /mob/living/carbon/human)) - var/mob/living/carbon/human/X = user - if(istype(X.species, /datum/species/xenos)) - src.attack_alien(user) - return - - if(blocked) - to_chat(user, "\The [src] is welded solid!") - return - - var/alarmed = lockdown - for(var/area/A in areas_added) //Checks if there are fire alarms in any areas associated with that firedoor - if(A.firedoors_closed) - alarmed = 1 - - var/answer = tgui_alert(user, "Would you like to [density ? "open" : "close"] this [src.name]?[ alarmed && density ? "\nNote that by doing so, you acknowledge any damages from opening this\n[src.name] as being your own fault, and you will be held accountable under the law." : ""]",\ - "\The [src]", list("Yes, [density ? "open" : "close"]", "No")) - if(answer == "No") - return - if(user.incapacitated() || (get_dist(src, user) > 1 && !issilicon(user))) - to_chat(user, "Sorry, you must remain able bodied and close to \the [src] in order to use it.") - return - if(density && (stat & (BROKEN|NOPOWER))) //can still close without power - to_chat(user, "\The [src] is not functioning, you'll have to force it open manually.") - return - - if(alarmed && density && lockdown && !allowed(user)) - to_chat(user, "Access denied. Please wait for authorities to arrive, or for the alert to clear.") - return - else - user.visible_message("\The [src] [density ? "open" : "close"]s for \the [user].",\ - "\The [src] [density ? "open" : "close"]s.",\ - "You hear a beep, and a door opening.") - - var/needs_to_close = 0 - if(density) - if(alarmed) - // Accountability! - users_to_open |= user.name - needs_to_close = !issilicon(user) - spawn() - open() - else - spawn() - close() - - if(needs_to_close) - spawn(50) - alarmed = 0 - for(var/area/A in areas_added) //Just in case a fire alarm is turned off while the firedoor is going through an autoclose cycle - if(A.firedoors_closed) - alarmed = 1 - if(alarmed) - nextstate = FIREDOOR_CLOSED - close() - -/obj/machinery/door/firedoor/attack_alien(var/mob/user) //Familiar, right? Doors. - if(istype(user, /mob/living/carbon/human)) - var/mob/living/carbon/human/X = user - if(istype(X.species, /datum/species/xenos)) - if(src.blocked) - visible_message("\The [user] begins digging into \the [src] internals!") - if(do_after(user,5 SECONDS,src)) - playsound(src, 'sound/machines/door/airlock_creaking.ogg', 100, 1) - src.blocked = 0 - update_icon() - open(1) - else if(src.density) - visible_message("\The [user] begins forcing \the [src] open!") - if(do_after(user, 2 SECONDS,src)) - playsound(src, 'sound/machines/door/airlock_creaking.ogg', 100, 1) - visible_message("\The [user] forces \the [src] open!") - open(1) - else - visible_message("\The [user] forces \the [src] closed!") - close(1) - else - visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") - return - ..() - -/obj/machinery/door/firedoor/attack_generic(var/mob/living/user, var/damage) - if(stat & (BROKEN|NOPOWER)) - if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) - var/time_to_force = (2 + (2 * blocked)) * 5 - if(src.density) - visible_message("\The [user] starts forcing \the [src] open!") - user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything. - if(do_after(user, time_to_force, src)) - visible_message("\The [user] forces \the [src] open!") - src.blocked = 0 - open(1) - user.set_AI_busy(FALSE) - else - time_to_force = (time_to_force / 2) - visible_message("\The [user] starts forcing \the [src] closed!") - user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything. - if(do_after(user, time_to_force, src)) - visible_message("\The [user] forces \the [src] closed!") - close(1) - user.set_AI_busy(FALSE) - else - visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") - return - ..() - -/obj/machinery/door/firedoor/attackby(obj/item/weapon/C as obj, mob/user as mob) - add_fingerprint(user) - if(istype(C, /obj/item/taperoll)) - return //Don't open the door if we're putting tape on it to tell people 'don't open the door'. - if(operating) - return//Already doing something. - if(C.has_tool_quality(TOOL_WELDER) && !repairing) - if(prying) - to_chat(user, "Someone's busy prying that [density ? "open" : "closed"]!") - var/obj/item/weapon/weldingtool/W = C.get_welder() - if(W.remove_fuel(0, user)) - blocked = !blocked - user.visible_message("\The [user] [blocked ? "welds" : "unwelds"] \the [src] with \a [W].",\ - "You [blocked ? "weld" : "unweld"] \the [src] with \the [W].",\ - "You hear something being welded.") - playsound(src, W.usesound, 100, 1) - update_icon() - return - - if(density && C.has_tool_quality(TOOL_SCREWDRIVER)) - hatch_open = !hatch_open - playsound(src, C.usesound, 50, 1) - user.visible_message("[user] has [hatch_open ? "opened" : "closed"] \the [src] maintenance hatch.", - "You have [hatch_open ? "opened" : "closed"] the [src] maintenance hatch.") - update_icon() - return - - if(blocked && C.has_tool_quality(TOOL_CROWBAR) && !repairing) - if(!hatch_open) - to_chat(user, "You must open the maintenance hatch first!") - else - user.visible_message("[user] is removing the electronics from \the [src].", - "You start to remove the electronics from [src].") - if(do_after(user,30)) - if(blocked && density && hatch_open) - playsound(src, C.usesound, 50, 1) - user.visible_message("[user] has removed the electronics from \the [src].", - "You have removed the electronics from [src].") - - if (stat & BROKEN) - new /obj/item/weapon/circuitboard/broken(src.loc) - else - new/obj/item/weapon/circuitboard/airalarm(src.loc) - - var/obj/structure/firedoor_assembly/FA = new/obj/structure/firedoor_assembly(src.loc) - FA.anchored = TRUE - FA.density = TRUE - FA.wired = 1 - FA.glass = glass - FA.update_icon() - qdel(src) - return - - if(blocked) - to_chat(user, "\The [src] is welded shut!") - return - - if(C.pry == 1) - if(operating) - return - - if(blocked && C.has_tool_quality(TOOL_CROWBAR)) - user.visible_message("\The [user] pries at \the [src] with \a [C], but \the [src] is welded in place!",\ - "You try to pry \the [src] [density ? "open" : "closed"], but it is welded in place!",\ - "You hear someone struggle and metal straining.") - return - - if(istype(C,/obj/item/weapon/material/twohanded/fireaxe)) - var/obj/item/weapon/material/twohanded/fireaxe/F = C - if(!F.wielded) - return - - if(prying) - to_chat(user, "Someone's already prying that [density ? "open" : "closed"].") - return - - user.visible_message("\The [user] starts to force \the [src] [density ? "open" : "closed"] with \a [C]!",\ - "You start forcing \the [src] [density ? "open" : "closed"] with \the [C]!",\ - "You hear metal strain.") - prying = 1 - update_icon() - playsound(src, C.usesound, 100, 1) - if(do_after(user,30 * C.toolspeed)) - if(C.has_tool_quality(TOOL_CROWBAR)) - if(stat & (BROKEN|NOPOWER) || !density) - user.visible_message("\The [user] forces \the [src] [density ? "open" : "closed"] with \a [C]!",\ - "You force \the [src] [density ? "open" : "closed"] with \the [C]!",\ - "You hear metal strain, and a door [density ? "open" : "close"].") - else - user.visible_message("\The [user] forces \the [ blocked ? "welded" : "" ] [src] [density ? "open" : "closed"] with \a [C]!",\ - "You force \the [ blocked ? "welded" : "" ] [src] [density ? "open" : "closed"] with \the [C]!",\ - "You hear metal strain and groan, and a door [density ? "opening" : "closing"].") - if(density) - spawn(0) - open(1) - else - spawn(0) - close() - prying = 0 - update_icon() - return - - return ..() - -// CHECK PRESSURE -/obj/machinery/door/firedoor/process() - ..() - - if(!density) - return PROCESS_KILL - if(next_process_time <= world.time) - next_process_time = world.time + 100 // 10 second delays between process updates - var/changed = 0 - lockdown=0 - // Pressure alerts - pdiff = getOPressureDifferential(src.loc) - if(pdiff >= FIREDOOR_MAX_PRESSURE_DIFF) - lockdown = 1 - if(!pdiff_alert) - pdiff_alert = 1 - changed = 1 // update_icon() - else - if(pdiff_alert) - pdiff_alert = 0 - changed = 1 // update_icon() - - tile_info = getCardinalAirInfo(src.loc,list("temperature","pressure")) - var/old_alerts = dir_alerts - for(var/index = 1; index <= 4; index++) - var/list/tileinfo=tile_info[index] - if(tileinfo==null) - continue // Bad data. - var/celsius = convert_k2c(tileinfo[1]) - - var/alerts=0 - - // Temperatures - if(celsius >= FIREDOOR_MAX_TEMP) - alerts |= FIREDOOR_ALERT_HOT - lockdown = 1 - else if(celsius <= FIREDOOR_MIN_TEMP) - alerts |= FIREDOOR_ALERT_COLD - lockdown = 1 - - dir_alerts[index]=alerts - - if(dir_alerts != old_alerts) - changed = 1 - if(changed) - update_icon() - -/obj/machinery/door/firedoor/proc/latetoggle() - if(operating || !nextstate) - return - switch(nextstate) - if(FIREDOOR_OPEN) - nextstate = null - - open() - if(FIREDOOR_CLOSED) - nextstate = null - close() - return - -/obj/machinery/door/firedoor/close() - latetoggle() - . = ..() - // Queue us for processing when we are closed! - if(density) - START_MACHINE_PROCESSING(src) - -/obj/machinery/door/firedoor/open(var/forced = 0) - if(hatch_open) - hatch_open = 0 - visible_message("The maintenance hatch of \the [src] closes.") - update_icon() - - if(!forced) - if(stat & (BROKEN|NOPOWER)) - return //needs power to open unless it was forced - else - use_power(360) - else - if(usr && usr.ckey) - log_admin("[usr]([usr.ckey]) has forced open an emergency shutter.") - message_admins("[usr]([usr.ckey]) has forced open an emergency shutter.") - latetoggle() - return ..() - -/obj/machinery/door/firedoor/do_animate(animation) - switch(animation) - if("opening") - flick("door_opening", src) - playsound(src, 'sound/machines/firelockopen.ogg', 37, 1) - if("closing") - playsound(src, 'sound/machines/firelockclose.ogg', 37, 1) - flick("door_closing", src) - return - - -/obj/machinery/door/firedoor/update_icon() - cut_overlays() - if(density) - icon_state = "door_closed" - if(prying) - icon_state = "prying_closed" - if(hatch_open) - add_overlay("hatch") - if(blocked) - add_overlay("welded") - if(pdiff_alert) - add_overlay("palert") - if(dir_alerts) - for(var/d=1;d<=4;d++) - var/cdir = cardinal[d] - for(var/i=1;i<=ALERT_STATES.len;i++) - if(dir_alerts[d] & (1<<(i-1))) - add_overlay(new/icon(icon,"alert_[ALERT_STATES[i]]", dir=cdir)) - else - icon_state = "door_open" - if(prying) - icon_state = "prying_open" - if(blocked) - add_overlay("welded_open") - return - -//These are playing merry hell on ZAS. Sorry fellas :( - -/obj/machinery/door/firedoor/border_only -/* - icon = 'icons/obj/doors/edge_Doorfire.dmi' - glass = 1 //There is a glass window so you can see through the door - //This is needed due to BYOND limitations in controlling visibility - heat_proof = 1 - air_properties_vary_with_direction = 1 - - CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) //Make sure looking at appropriate border - return !density - else - return 1 - - CheckExit(atom/movable/mover as mob|obj, turf/target as turf) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) - return !density - else - return 1 - - - update_nearby_tiles(need_rebuild) - if(!air_master) return 0 - - var/turf/simulated/source = loc - var/turf/simulated/destination = get_step(source,dir) - - update_heat_protection(loc) - - if(istype(source)) air_master.tiles_to_update += source - if(istype(destination)) air_master.tiles_to_update += destination - return 1 -*/ - -/obj/machinery/door/firedoor/multi_tile - icon = 'icons/obj/doors/DoorHazard2x1.dmi' - width = 2 - -/obj/machinery/door/firedoor/glass - name = "\improper Emergency Glass Shutter" - desc = "Emergency air-tight shutter, capable of sealing off breached areas. This one has a resilient glass window, allowing you to see the danger." - icon = 'icons/obj/doors/DoorHazardGlass.dmi' - icon_state = "door_open" - glass = 1 - -#undef FIREDOOR_MAX_PRESSURE_DIFF -#undef FIREDOOR_MAX_TEMP -#undef FIREDOOR_MIN_TEMP - -#undef FIREDOOR_ALERT_HOT -#undef FIREDOOR_ALERT_COLD -// Not used #undef FIREDOOR_ALERT_LOWPRESS +#define FIREDOOR_MAX_PRESSURE_DIFF 25 // kPa +#define FIREDOOR_MAX_TEMP 50 // °C +#define FIREDOOR_MIN_TEMP 0 + +// Bitflags +#define FIREDOOR_ALERT_HOT 1 +#define FIREDOOR_ALERT_COLD 2 +// Not used #define FIREDOOR_ALERT_LOWPRESS 4 + +/obj/machinery/door/firedoor + name = "\improper Emergency Shutter" + desc = "Emergency air-tight shutter, capable of sealing off breached areas." + icon = 'icons/obj/doors/DoorHazard.dmi' + icon_state = "door_open" + req_one_access = list(access_eva) //access_atmospherics, access_engine_equip) + opacity = 0 + density = FALSE + layer = DOOR_OPEN_LAYER - 0.01 + open_layer = DOOR_OPEN_LAYER - 0.01 // Just below doors when open + closed_layer = DOOR_CLOSED_LAYER + 0.01 // Just above doors when closed + + //These are frequenly used with windows, so make sure zones can pass. + //Generally if a firedoor is at a place where there should be a zone boundery then there will be a regular door underneath it. + block_air_zones = 0 + + var/blocked = 0 + var/prying = 0 + var/lockdown = 0 // When the door has detected a problem, it locks. + var/pdiff_alert = 0 + var/pdiff = 0 + var/nextstate = null + var/net_id + var/list/areas_added + var/list/users_to_open = new + var/next_process_time = 0 + + var/hatch_open = 0 + + power_channel = ENVIRON + use_power = USE_POWER_IDLE + idle_power_usage = 5 + + var/list/tile_info[4] + var/list/dir_alerts[4] // 4 dirs, bitflags + + // MUST be in same order as FIREDOOR_ALERT_* + var/list/ALERT_STATES=list( + "hot", + "cold" + ) + +/obj/machinery/door/firedoor/Initialize() + . = ..() + //Delete ourselves if we find extra mapped in firedoors + for(var/obj/machinery/door/firedoor/F in loc) + if(F != src) + log_debug("Duplicate firedoors at [x],[y],[z]") + return INITIALIZE_HINT_QDEL + + var/area/A = get_area(src) + ASSERT(istype(A)) + + LAZYADD(A.all_doors, src) + areas_added = list(A) + + for(var/direction in cardinal) + A = get_area(get_step(src,direction)) + if(istype(A) && !(A in areas_added)) + LAZYADD(A.all_doors, src) + areas_added += A + +/obj/machinery/door/firedoor/Destroy() + for(var/area/A in areas_added) + LAZYREMOVE(A.all_doors, src) + . = ..() + +/obj/machinery/door/firedoor/get_material() + return get_material_by_name(MAT_STEEL) + +/obj/machinery/door/firedoor/examine(mob/user) + . = ..() + + if(!Adjacent(user)) + return . + + if(pdiff >= FIREDOOR_MAX_PRESSURE_DIFF) + . += "WARNING: Current pressure differential is [pdiff]kPa! Opening door may result in injury!" + + . += "Sensor readings:" + for(var/index = 1; index <= tile_info.len; index++) + var/o = "  " + switch(index) + if(1) + o += "NORTH: " + if(2) + o += "SOUTH: " + if(3) + o += "EAST: " + if(4) + o += "WEST: " + if(tile_info[index] == null) + o += "DATA UNAVAILABLE" + . += o + continue + var/celsius = convert_k2c(tile_info[index][1]) + var/pressure = tile_info[index][2] + o += "" + o += "[celsius]°C " + o += "" + o += "[pressure]kPa" + . += o + + if(islist(users_to_open) && users_to_open.len) + var/users_to_open_string = users_to_open[1] + if(users_to_open.len >= 2) + for(var/i = 2 to users_to_open.len) + users_to_open_string += ", [users_to_open[i]]" + . += "These people have opened \the [src] during an alert: [users_to_open_string]." + +/obj/machinery/door/firedoor/Bumped(atom/AM) + if(p_open || operating) + return + if(!density) + return ..() + if(istype(AM, /obj/mecha)) + var/obj/mecha/mecha = AM + if(mecha.occupant) + var/mob/M = mecha.occupant + if(world.time - M.last_bumped <= 10) return //Can bump-open one airlock per second. This is to prevent popup message spam. + M.last_bumped = world.time + attack_hand(M) + return 0 + +/obj/machinery/door/firedoor/attack_hand(mob/user as mob) + add_fingerprint(user) + if(operating) + return//Already doing something. + + if(istype(user, /mob/living/carbon/human)) + var/mob/living/carbon/human/X = user + if(istype(X.species, /datum/species/xenos)) + src.attack_alien(user) + return + + if(blocked) + to_chat(user, "\The [src] is welded solid!") + return + + var/alarmed = lockdown + for(var/area/A in areas_added) //Checks if there are fire alarms in any areas associated with that firedoor + if(A.firedoors_closed) + alarmed = 1 + + var/answer = tgui_alert(user, "Would you like to [density ? "open" : "close"] this [src.name]?[ alarmed && density ? "\nNote that by doing so, you acknowledge any damages from opening this\n[src.name] as being your own fault, and you will be held accountable under the law." : ""]",\ + "\The [src]", list("Yes, [density ? "open" : "close"]", "No")) + if(answer == "No") + return + if(user.incapacitated() || (get_dist(src, user) > 1 && !issilicon(user))) + to_chat(user, "Sorry, you must remain able bodied and close to \the [src] in order to use it.") + return + if(density && (stat & (BROKEN|NOPOWER))) //can still close without power + to_chat(user, "\The [src] is not functioning, you'll have to force it open manually.") + return + + if(alarmed && density && lockdown && !allowed(user)) + to_chat(user, "Access denied. Please wait for authorities to arrive, or for the alert to clear.") + return + else + user.visible_message("\The [src] [density ? "open" : "close"]s for \the [user].",\ + "\The [src] [density ? "open" : "close"]s.",\ + "You hear a beep, and a door opening.") + + var/needs_to_close = 0 + if(density) + if(alarmed) + // Accountability! + users_to_open |= user.name + needs_to_close = !issilicon(user) + spawn() + open() + else + spawn() + close() + + if(needs_to_close) + spawn(50) + alarmed = 0 + for(var/area/A in areas_added) //Just in case a fire alarm is turned off while the firedoor is going through an autoclose cycle + if(A.firedoors_closed) + alarmed = 1 + if(alarmed) + nextstate = FIREDOOR_CLOSED + close() + +/obj/machinery/door/firedoor/attack_alien(var/mob/user) //Familiar, right? Doors. + if(istype(user, /mob/living/carbon/human)) + var/mob/living/carbon/human/X = user + if(istype(X.species, /datum/species/xenos)) + if(src.blocked) + visible_message("\The [user] begins digging into \the [src] internals!") + if(do_after(user,5 SECONDS,src)) + playsound(src, 'sound/machines/door/airlock_creaking.ogg', 100, 1) + src.blocked = 0 + update_icon() + open(1) + else if(src.density) + visible_message("\The [user] begins forcing \the [src] open!") + if(do_after(user, 2 SECONDS,src)) + playsound(src, 'sound/machines/door/airlock_creaking.ogg', 100, 1) + visible_message("\The [user] forces \the [src] open!") + open(1) + else + visible_message("\The [user] forces \the [src] closed!") + close(1) + else + visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") + return + ..() + +/obj/machinery/door/firedoor/attack_generic(var/mob/living/user, var/damage) + if(stat & (BROKEN|NOPOWER)) + if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) + var/time_to_force = (2 + (2 * blocked)) * 5 + if(src.density) + visible_message("\The [user] starts forcing \the [src] open!") + user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything. + if(do_after(user, time_to_force, src)) + visible_message("\The [user] forces \the [src] open!") + src.blocked = 0 + open(1) + user.set_AI_busy(FALSE) + else + time_to_force = (time_to_force / 2) + visible_message("\The [user] starts forcing \the [src] closed!") + user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything. + if(do_after(user, time_to_force, src)) + visible_message("\The [user] forces \the [src] closed!") + close(1) + user.set_AI_busy(FALSE) + else + visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") + return + ..() + +/obj/machinery/door/firedoor/attackby(obj/item/weapon/C as obj, mob/user as mob) + add_fingerprint(user) + if(istype(C, /obj/item/taperoll)) + return //Don't open the door if we're putting tape on it to tell people 'don't open the door'. + if(operating) + return//Already doing something. + if(C.has_tool_quality(TOOL_WELDER) && !repairing) + if(prying) + to_chat(user, "Someone's busy prying that [density ? "open" : "closed"]!") + var/obj/item/weapon/weldingtool/W = C.get_welder() + if(W.remove_fuel(0, user)) + blocked = !blocked + user.visible_message("\The [user] [blocked ? "welds" : "unwelds"] \the [src] with \a [W].",\ + "You [blocked ? "weld" : "unweld"] \the [src] with \the [W].",\ + "You hear something being welded.") + playsound(src, W.usesound, 100, 1) + update_icon() + return + + if(density && C.has_tool_quality(TOOL_SCREWDRIVER)) + hatch_open = !hatch_open + playsound(src, C.usesound, 50, 1) + user.visible_message("[user] has [hatch_open ? "opened" : "closed"] \the [src] maintenance hatch.", + "You have [hatch_open ? "opened" : "closed"] the [src] maintenance hatch.") + update_icon() + return + + if(blocked && C.has_tool_quality(TOOL_CROWBAR) && !repairing) + if(!hatch_open) + to_chat(user, "You must open the maintenance hatch first!") + else + user.visible_message("[user] is removing the electronics from \the [src].", + "You start to remove the electronics from [src].") + if(do_after(user,30)) + if(blocked && density && hatch_open) + playsound(src, C.usesound, 50, 1) + user.visible_message("[user] has removed the electronics from \the [src].", + "You have removed the electronics from [src].") + + if (stat & BROKEN) + new /obj/item/weapon/circuitboard/broken(src.loc) + else + new/obj/item/weapon/circuitboard/airalarm(src.loc) + + var/obj/structure/firedoor_assembly/FA = new/obj/structure/firedoor_assembly(src.loc) + FA.anchored = TRUE + FA.density = TRUE + FA.wired = 1 + FA.glass = glass + FA.update_icon() + qdel(src) + return + + if(blocked) + to_chat(user, "\The [src] is welded shut!") + return + + if(C.pry == 1) + if(operating) + return + + if(blocked && C.has_tool_quality(TOOL_CROWBAR)) + user.visible_message("\The [user] pries at \the [src] with \a [C], but \the [src] is welded in place!",\ + "You try to pry \the [src] [density ? "open" : "closed"], but it is welded in place!",\ + "You hear someone struggle and metal straining.") + return + + if(istype(C,/obj/item/weapon/material/twohanded/fireaxe)) + var/obj/item/weapon/material/twohanded/fireaxe/F = C + if(!F.wielded) + return + + if(prying) + to_chat(user, "Someone's already prying that [density ? "open" : "closed"].") + return + + user.visible_message("\The [user] starts to force \the [src] [density ? "open" : "closed"] with \a [C]!",\ + "You start forcing \the [src] [density ? "open" : "closed"] with \the [C]!",\ + "You hear metal strain.") + prying = 1 + update_icon() + playsound(src, C.usesound, 100, 1) + if(do_after(user,30 * C.toolspeed)) + if(C.has_tool_quality(TOOL_CROWBAR)) + if(stat & (BROKEN|NOPOWER) || !density) + user.visible_message("\The [user] forces \the [src] [density ? "open" : "closed"] with \a [C]!",\ + "You force \the [src] [density ? "open" : "closed"] with \the [C]!",\ + "You hear metal strain, and a door [density ? "open" : "close"].") + else + user.visible_message("\The [user] forces \the [ blocked ? "welded" : "" ] [src] [density ? "open" : "closed"] with \a [C]!",\ + "You force \the [ blocked ? "welded" : "" ] [src] [density ? "open" : "closed"] with \the [C]!",\ + "You hear metal strain and groan, and a door [density ? "opening" : "closing"].") + if(density) + spawn(0) + open(1) + else + spawn(0) + close() + prying = 0 + update_icon() + return + + return ..() + +// CHECK PRESSURE +/obj/machinery/door/firedoor/process() + ..() + + if(!density) + return PROCESS_KILL + if(next_process_time <= world.time) + next_process_time = world.time + 100 // 10 second delays between process updates + var/changed = 0 + lockdown=0 + // Pressure alerts + pdiff = getOPressureDifferential(src.loc) + if(pdiff >= FIREDOOR_MAX_PRESSURE_DIFF) + lockdown = 1 + if(!pdiff_alert) + pdiff_alert = 1 + changed = 1 // update_icon() + else + if(pdiff_alert) + pdiff_alert = 0 + changed = 1 // update_icon() + + tile_info = getCardinalAirInfo(src.loc,list("temperature","pressure")) + var/old_alerts = dir_alerts + for(var/index = 1; index <= 4; index++) + var/list/tileinfo=tile_info[index] + if(tileinfo==null) + continue // Bad data. + var/celsius = convert_k2c(tileinfo[1]) + + var/alerts=0 + + // Temperatures + if(celsius >= FIREDOOR_MAX_TEMP) + alerts |= FIREDOOR_ALERT_HOT + lockdown = 1 + else if(celsius <= FIREDOOR_MIN_TEMP) + alerts |= FIREDOOR_ALERT_COLD + lockdown = 1 + + dir_alerts[index]=alerts + + if(dir_alerts != old_alerts) + changed = 1 + if(changed) + update_icon() + +/obj/machinery/door/firedoor/proc/latetoggle() + if(operating || !nextstate) + return + switch(nextstate) + if(FIREDOOR_OPEN) + nextstate = null + + open() + if(FIREDOOR_CLOSED) + nextstate = null + close() + return + +/obj/machinery/door/firedoor/close() + latetoggle() + . = ..() + // Queue us for processing when we are closed! + if(density) + START_MACHINE_PROCESSING(src) + +/obj/machinery/door/firedoor/open(var/forced = 0) + if(hatch_open) + hatch_open = 0 + visible_message("The maintenance hatch of \the [src] closes.") + update_icon() + + if(!forced) + if(stat & (BROKEN|NOPOWER)) + return //needs power to open unless it was forced + else + use_power(360) + else + if(usr && usr.ckey) + log_admin("[usr]([usr.ckey]) has forced open an emergency shutter.") + message_admins("[usr]([usr.ckey]) has forced open an emergency shutter.") + latetoggle() + return ..() + +/obj/machinery/door/firedoor/do_animate(animation) + switch(animation) + if("opening") + flick("door_opening", src) + playsound(src, 'sound/machines/firelockopen.ogg', 37, 1) + if("closing") + playsound(src, 'sound/machines/firelockclose.ogg', 37, 1) + flick("door_closing", src) + return + + +/obj/machinery/door/firedoor/update_icon() + cut_overlays() + if(density) + icon_state = "door_closed" + if(prying) + icon_state = "prying_closed" + if(hatch_open) + add_overlay("hatch") + if(blocked) + add_overlay("welded") + if(pdiff_alert) + add_overlay("palert") + if(dir_alerts) + for(var/d=1;d<=4;d++) + var/cdir = cardinal[d] + for(var/i=1;i<=ALERT_STATES.len;i++) + if(dir_alerts[d] & (1<<(i-1))) + add_overlay(new/icon(icon,"alert_[ALERT_STATES[i]]", dir=cdir)) + else + icon_state = "door_open" + if(prying) + icon_state = "prying_open" + if(blocked) + add_overlay("welded_open") + return + +//These are playing merry hell on ZAS. Sorry fellas :( + +/obj/machinery/door/firedoor/border_only +/* + icon = 'icons/obj/doors/edge_Doorfire.dmi' + glass = 1 //There is a glass window so you can see through the door + //This is needed due to BYOND limitations in controlling visibility + heat_proof = 1 + air_properties_vary_with_direction = 1 + + CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(get_dir(loc, target) == dir) //Make sure looking at appropriate border + return !density + else + return 1 + + CheckExit(atom/movable/mover as mob|obj, turf/target as turf) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(get_dir(loc, target) == dir) + return !density + else + return 1 + + + update_nearby_tiles(need_rebuild) + if(!air_master) return 0 + + var/turf/simulated/source = loc + var/turf/simulated/destination = get_step(source,dir) + + update_heat_protection(loc) + + if(istype(source)) air_master.tiles_to_update += source + if(istype(destination)) air_master.tiles_to_update += destination + return 1 +*/ + +/obj/machinery/door/firedoor/multi_tile + icon = 'icons/obj/doors/DoorHazard2x1.dmi' + width = 2 + +/obj/machinery/door/firedoor/glass + name = "\improper Emergency Glass Shutter" + desc = "Emergency air-tight shutter, capable of sealing off breached areas. This one has a resilient glass window, allowing you to see the danger." + icon = 'icons/obj/doors/DoorHazardGlass.dmi' + icon_state = "door_open" + glass = 1 + +#undef FIREDOOR_MAX_PRESSURE_DIFF +#undef FIREDOOR_MAX_TEMP +#undef FIREDOOR_MIN_TEMP + +#undef FIREDOOR_ALERT_HOT +#undef FIREDOOR_ALERT_COLD +// Not used #undef FIREDOOR_ALERT_LOWPRESS diff --git a/code/game/machinery/doors/unpowered.dm b/code/game/machinery/doors/unpowered.dm index 515ed18017c..8e2a28d1dda 100644 --- a/code/game/machinery/doors/unpowered.dm +++ b/code/game/machinery/doors/unpowered.dm @@ -1,25 +1,25 @@ -/obj/machinery/door/unpowered - autoclose = 0 - var/locked = 0 - -/obj/machinery/door/unpowered/Bumped(atom/AM) - if(src.locked) - return - ..() - return - -/obj/machinery/door/unpowered/attackby(obj/item/I as obj, mob/user as mob) - if(istype(I, /obj/item/weapon/melee/energy/blade)) return - if(src.locked) return - ..() - return - -/obj/machinery/door/unpowered/emag_act() - return -1 - -/obj/machinery/door/unpowered/shuttle - icon = 'icons/turf/shuttle_white.dmi' - name = "door" - icon_state = "door1" - opacity = 1 - density = TRUE +/obj/machinery/door/unpowered + autoclose = 0 + var/locked = 0 + +/obj/machinery/door/unpowered/Bumped(atom/AM) + if(src.locked) + return + ..() + return + +/obj/machinery/door/unpowered/attackby(obj/item/I as obj, mob/user as mob) + if(istype(I, /obj/item/weapon/melee/energy/blade)) return + if(src.locked) return + ..() + return + +/obj/machinery/door/unpowered/emag_act() + return -1 + +/obj/machinery/door/unpowered/shuttle + icon = 'icons/turf/shuttle_white.dmi' + name = "door" + icon_state = "door1" + opacity = 1 + density = TRUE diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 81758003733..7b350805c25 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -1,353 +1,353 @@ -/obj/machinery/door/window - name = "interior door" - desc = "A strong door." - icon = 'icons/obj/doors/windoor.dmi' - icon_state = "left" - var/base_state = "left" - min_force = 4 - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 150 //If you change this, consiter changing ../door/window/brigdoor/ health at the bottom of this .dm file - health = 150 - visible = 0.0 - use_power = USE_POWER_OFF - flags = ON_BORDER - opacity = 0 - var/obj/item/weapon/airlock_electronics/electronics = null - explosion_resistance = 5 - can_atmos_pass = ATMOS_PASS_PROC - air_properties_vary_with_direction = 1 - -/obj/machinery/door/window/New() - ..() - update_nearby_tiles() - if(LAZYLEN(req_access)) - src.icon_state = "[src.icon_state]" - src.base_state = src.icon_state - return - -/obj/machinery/door/window/update_icon() - if(density) - icon_state = base_state - else - icon_state = "[base_state]open" - -/obj/machinery/door/window/proc/shatter(var/display_message = 1) - new /obj/item/weapon/material/shard(src.loc) - new /obj/item/weapon/material/shard(src.loc) - new /obj/item/stack/cable_coil(src.loc, 1) - var/obj/item/weapon/airlock_electronics/ae - if(!electronics) - ae = new/obj/item/weapon/airlock_electronics( src.loc ) - if(LAZYLEN(req_access)) - ae.conf_access = req_access - else if (LAZYLEN(req_one_access)) - ae.conf_access = req_one_access - ae.one_access = 1 - else - ae = electronics - electronics = null - ae.loc = src.loc - if(operating == -1) - ae.icon_state = "door_electronics_smoked" - operating = 0 - src.density = FALSE - playsound(src, "shatter", 70, 1) - if(display_message) - visible_message("[src] shatters!") - qdel(src) - -/obj/machinery/door/window/Destroy() - density = FALSE - update_nearby_tiles() - return ..() - -/obj/machinery/door/window/Bumped(atom/movable/AM as mob|obj) - if (!( ismob(AM) )) - var/mob/living/bot/bot = AM - if(istype(bot)) - if(density && src.check_access(bot.botcard)) - open() - addtimer(CALLBACK(src, PROC_REF(close)), 50) - else if(istype(AM, /obj/mecha)) - var/obj/mecha/mecha = AM - if(density) - if(mecha.occupant && src.allowed(mecha.occupant)) - open() - addtimer(CALLBACK(src, PROC_REF(close)), 50) - return - if (!( ticker )) - return - if (src.operating) - return - if (density && allowed(AM)) - open() - addtimer(CALLBACK(src, PROC_REF(close)), check_access(null)? 50 : 20) - -/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return TRUE - if(get_dir(mover, target) == reverse_dir[dir]) // From elsewhere to here, can't move against our dir - return !density - return TRUE - -/obj/machinery/door/window/Uncross(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return TRUE - if(get_dir(mover, target) == dir) // From here to elsewhere, can't move in our dir - return !density - return TRUE - -/obj/machinery/door/window/CanZASPass(turf/T, is_zone) - if(get_dir(T, loc) == turn(dir, 180)) - if(is_zone) // No merging allowed. - return FALSE - return !density // Air can flow if open (density == FALSE). - return TRUE // Windoors don't block if not facing the right way. - -/obj/machinery/door/window/open() - if (operating == 1 || !density) //doors can still open when emag-disabled - return 0 - if (!ticker) - return 0 - if (!operating) //in case of emag - operating = 1 - flick(text("[src.base_state]opening"), src) - playsound(src, 'sound/machines/door/windowdoor.ogg', 100, 1) - sleep(10) - - explosion_resistance = 0 - density = FALSE - update_icon() - update_nearby_tiles() - - if(operating == 1) //emag again - operating = 0 - return 1 - -/obj/machinery/door/window/close() - if(operating || density) - return FALSE - operating = TRUE - flick(text("[]closing", src.base_state), src) - playsound(src, 'sound/machines/door/windowdoor.ogg', 100, 1) - - density = TRUE - update_icon() - explosion_resistance = initial(explosion_resistance) - update_nearby_tiles() - - sleep(10) - operating = FALSE - return TRUE - -/obj/machinery/door/window/take_damage(var/damage) - src.health = max(0, src.health - damage) - if (src.health <= 0) - shatter() - return - -/obj/machinery/door/window/attack_ai(mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/door/window/attack_hand(mob/user as mob) - src.add_fingerprint(user) - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - if(H.species.can_shred(H)) - playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) - visible_message("[user] smashes against the [src.name].", 1) - user.do_attack_animation(src) - user.setClickCooldown(user.get_attack_speed()) - take_damage(25) - return - - if (src.allowed(user)) - if (src.density) - open() - else - close() - - else if (src.density) - flick(text("[]deny", src.base_state), src) - - return - -/obj/machinery/door/window/emag_act(var/remaining_charges, var/mob/user) - if (density && operable()) - operating = -1 - flick("[src.base_state]spark", src) - sleep(6) - open() - return 1 - -/obj/machinery/door/window/attackby(obj/item/I as obj, mob/user as mob) - - //If it's in the process of opening/closing, ignore the click - if (src.operating == 1) - return - - if(istype(I)) - // Fixing. - if(I.has_tool_quality(TOOL_WELDER) && user.a_intent == I_HELP) - var/obj/item/weapon/weldingtool/WT = I.get_welder() - if(health < maxhealth) - if(WT.remove_fuel(1 ,user)) - to_chat(user, "You begin repairing [src]...") - playsound(src, WT.usesound, 50, 1) - if(do_after(user, 40 * WT.toolspeed, target = src)) - health = maxhealth - update_icon() - to_chat(user, "You repair [src].") - else - to_chat(user, "[src] is already in good condition!") - return - - //Emags and ninja swords? You may pass. - if (istype(I, /obj/item/weapon/melee/energy/blade)) - if(emag_act(10, user)) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, src.loc) - spark_system.start() - playsound(src, "sparks", 50, 1) - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - visible_message("The glass door was sliced open by [user]!") - return 1 - - //If it's opened/emagged, crowbar can pry it out of its frame. - if (!density && I.has_tool_quality(TOOL_CROWBAR)) - playsound(src, I.usesound, 50, 1) - user.visible_message("[user] begins prying the windoor out of the frame.", "You start to pry the windoor out of the frame.") - if (do_after(user,40 * I.toolspeed)) - to_chat(user,"You pried the windoor out of the frame!") - - var/obj/structure/windoor_assembly/wa = new/obj/structure/windoor_assembly(src.loc) - if (istype(src, /obj/machinery/door/window/brigdoor)) - wa.secure = "secure_" - if (src.base_state == "right" || src.base_state == "rightsecure") - wa.facing = "r" - wa.set_dir(src.dir) - wa.anchored = TRUE - wa.created_name = name - wa.state = "02" - wa.step = 2 - wa.update_state() - - if(operating == -1) - wa.electronics = new/obj/item/weapon/circuitboard/broken() - else - if(!electronics) - wa.electronics = new/obj/item/weapon/airlock_electronics() - if(LAZYLEN(req_access)) - wa.electronics.conf_access = req_access - else if (LAZYLEN(req_one_access)) - wa.electronics.conf_access = req_one_access - wa.electronics.one_access = 1 - else - wa.electronics = electronics - electronics = null - operating = 0 - qdel(src) - return - - //If it's a weapon, smash windoor. Unless it's an id card, agent card, ect.. then ignore it (Cards really shouldnt damage a door anyway) - if(src.density && istype(I, /obj/item/weapon) && !istype(I, /obj/item/weapon/card)) - user.setClickCooldown(user.get_attack_speed(I)) - var/aforce = I.force - playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) - visible_message("[src] was hit by [I].") - if(I.damtype == BRUTE || I.damtype == BURN) - take_damage(aforce) - return - - - src.add_fingerprint(user) - - if (src.allowed(user)) - if (src.density) - open() - else - close() - - else if (src.density) - flick(text("[]deny", src.base_state), src) - - return - -/obj/machinery/door/window/brigdoor - name = "secure door" - icon = 'icons/obj/doors/windoor.dmi' - icon_state = "leftsecure" - base_state = "leftsecure" - req_access = list(access_security) - var/id = null - maxhealth = 300 - health = 300.0 //Stronger doors for prison (regular window door health is 150) - -/obj/machinery/door/window/brigdoor/shatter() - new /obj/item/stack/rods(src.loc, 2) - ..() - -/obj/machinery/door/window/northleft - dir = NORTH - -/obj/machinery/door/window/eastleft - dir = EAST - -/obj/machinery/door/window/westleft - dir = WEST - -/obj/machinery/door/window/southleft - dir = SOUTH - -/obj/machinery/door/window/northright - dir = NORTH - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/eastright - dir = EAST - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/westright - dir = WEST - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/southright - dir = SOUTH - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/brigdoor/northleft - dir = NORTH - -/obj/machinery/door/window/brigdoor/eastleft - dir = EAST - -/obj/machinery/door/window/brigdoor/westleft - dir = WEST - -/obj/machinery/door/window/brigdoor/southleft - dir = SOUTH - -/obj/machinery/door/window/brigdoor/northright - dir = NORTH - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/eastright - dir = EAST - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/westright - dir = WEST - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/southright - dir = SOUTH - icon_state = "rightsecure" - base_state = "rightsecure" +/obj/machinery/door/window + name = "interior door" + desc = "A strong door." + icon = 'icons/obj/doors/windoor.dmi' + icon_state = "left" + var/base_state = "left" + min_force = 4 + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 150 //If you change this, consiter changing ../door/window/brigdoor/ health at the bottom of this .dm file + health = 150 + visible = 0.0 + use_power = USE_POWER_OFF + flags = ON_BORDER + opacity = 0 + var/obj/item/weapon/airlock_electronics/electronics = null + explosion_resistance = 5 + can_atmos_pass = ATMOS_PASS_PROC + air_properties_vary_with_direction = 1 + +/obj/machinery/door/window/New() + ..() + update_nearby_tiles() + if(LAZYLEN(req_access)) + src.icon_state = "[src.icon_state]" + src.base_state = src.icon_state + return + +/obj/machinery/door/window/update_icon() + if(density) + icon_state = base_state + else + icon_state = "[base_state]open" + +/obj/machinery/door/window/proc/shatter(var/display_message = 1) + new /obj/item/weapon/material/shard(src.loc) + new /obj/item/weapon/material/shard(src.loc) + new /obj/item/stack/cable_coil(src.loc, 1) + var/obj/item/weapon/airlock_electronics/ae + if(!electronics) + ae = new/obj/item/weapon/airlock_electronics( src.loc ) + if(LAZYLEN(req_access)) + ae.conf_access = req_access + else if (LAZYLEN(req_one_access)) + ae.conf_access = req_one_access + ae.one_access = 1 + else + ae = electronics + electronics = null + ae.loc = src.loc + if(operating == -1) + ae.icon_state = "door_electronics_smoked" + operating = 0 + src.density = FALSE + playsound(src, "shatter", 70, 1) + if(display_message) + visible_message("[src] shatters!") + qdel(src) + +/obj/machinery/door/window/Destroy() + density = FALSE + update_nearby_tiles() + return ..() + +/obj/machinery/door/window/Bumped(atom/movable/AM as mob|obj) + if (!( ismob(AM) )) + var/mob/living/bot/bot = AM + if(istype(bot)) + if(density && src.check_access(bot.botcard)) + open() + addtimer(CALLBACK(src, PROC_REF(close)), 50) + else if(istype(AM, /obj/mecha)) + var/obj/mecha/mecha = AM + if(density) + if(mecha.occupant && src.allowed(mecha.occupant)) + open() + addtimer(CALLBACK(src, PROC_REF(close)), 50) + return + if (!( ticker )) + return + if (src.operating) + return + if (density && allowed(AM)) + open() + addtimer(CALLBACK(src, PROC_REF(close)), check_access(null)? 50 : 20) + +/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return TRUE + if(get_dir(mover, target) == reverse_dir[dir]) // From elsewhere to here, can't move against our dir + return !density + return TRUE + +/obj/machinery/door/window/Uncross(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return TRUE + if(get_dir(mover, target) == dir) // From here to elsewhere, can't move in our dir + return !density + return TRUE + +/obj/machinery/door/window/CanZASPass(turf/T, is_zone) + if(get_dir(T, loc) == turn(dir, 180)) + if(is_zone) // No merging allowed. + return FALSE + return !density // Air can flow if open (density == FALSE). + return TRUE // Windoors don't block if not facing the right way. + +/obj/machinery/door/window/open() + if (operating == 1 || !density) //doors can still open when emag-disabled + return 0 + if (!ticker) + return 0 + if (!operating) //in case of emag + operating = 1 + flick(text("[src.base_state]opening"), src) + playsound(src, 'sound/machines/door/windowdoor.ogg', 100, 1) + sleep(10) + + explosion_resistance = 0 + density = FALSE + update_icon() + update_nearby_tiles() + + if(operating == 1) //emag again + operating = 0 + return 1 + +/obj/machinery/door/window/close() + if(operating || density) + return FALSE + operating = TRUE + flick(text("[]closing", src.base_state), src) + playsound(src, 'sound/machines/door/windowdoor.ogg', 100, 1) + + density = TRUE + update_icon() + explosion_resistance = initial(explosion_resistance) + update_nearby_tiles() + + sleep(10) + operating = FALSE + return TRUE + +/obj/machinery/door/window/take_damage(var/damage) + src.health = max(0, src.health - damage) + if (src.health <= 0) + shatter() + return + +/obj/machinery/door/window/attack_ai(mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/door/window/attack_hand(mob/user as mob) + src.add_fingerprint(user) + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(H.species.can_shred(H)) + playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) + visible_message("[user] smashes against the [src.name].", 1) + user.do_attack_animation(src) + user.setClickCooldown(user.get_attack_speed()) + take_damage(25) + return + + if (src.allowed(user)) + if (src.density) + open() + else + close() + + else if (src.density) + flick(text("[]deny", src.base_state), src) + + return + +/obj/machinery/door/window/emag_act(var/remaining_charges, var/mob/user) + if (density && operable()) + operating = -1 + flick("[src.base_state]spark", src) + sleep(6) + open() + return 1 + +/obj/machinery/door/window/attackby(obj/item/I as obj, mob/user as mob) + + //If it's in the process of opening/closing, ignore the click + if (src.operating == 1) + return + + if(istype(I)) + // Fixing. + if(I.has_tool_quality(TOOL_WELDER) && user.a_intent == I_HELP) + var/obj/item/weapon/weldingtool/WT = I.get_welder() + if(health < maxhealth) + if(WT.remove_fuel(1 ,user)) + to_chat(user, "You begin repairing [src]...") + playsound(src, WT.usesound, 50, 1) + if(do_after(user, 40 * WT.toolspeed, target = src)) + health = maxhealth + update_icon() + to_chat(user, "You repair [src].") + else + to_chat(user, "[src] is already in good condition!") + return + + //Emags and ninja swords? You may pass. + if (istype(I, /obj/item/weapon/melee/energy/blade)) + if(emag_act(10, user)) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, src.loc) + spark_system.start() + playsound(src, "sparks", 50, 1) + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + visible_message("The glass door was sliced open by [user]!") + return 1 + + //If it's opened/emagged, crowbar can pry it out of its frame. + if (!density && I.has_tool_quality(TOOL_CROWBAR)) + playsound(src, I.usesound, 50, 1) + user.visible_message("[user] begins prying the windoor out of the frame.", "You start to pry the windoor out of the frame.") + if (do_after(user,40 * I.toolspeed)) + to_chat(user,"You pried the windoor out of the frame!") + + var/obj/structure/windoor_assembly/wa = new/obj/structure/windoor_assembly(src.loc) + if (istype(src, /obj/machinery/door/window/brigdoor)) + wa.secure = "secure_" + if (src.base_state == "right" || src.base_state == "rightsecure") + wa.facing = "r" + wa.set_dir(src.dir) + wa.anchored = TRUE + wa.created_name = name + wa.state = "02" + wa.step = 2 + wa.update_state() + + if(operating == -1) + wa.electronics = new/obj/item/weapon/circuitboard/broken() + else + if(!electronics) + wa.electronics = new/obj/item/weapon/airlock_electronics() + if(LAZYLEN(req_access)) + wa.electronics.conf_access = req_access + else if (LAZYLEN(req_one_access)) + wa.electronics.conf_access = req_one_access + wa.electronics.one_access = 1 + else + wa.electronics = electronics + electronics = null + operating = 0 + qdel(src) + return + + //If it's a weapon, smash windoor. Unless it's an id card, agent card, ect.. then ignore it (Cards really shouldnt damage a door anyway) + if(src.density && istype(I, /obj/item/weapon) && !istype(I, /obj/item/weapon/card)) + user.setClickCooldown(user.get_attack_speed(I)) + var/aforce = I.force + playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) + visible_message("[src] was hit by [I].") + if(I.damtype == BRUTE || I.damtype == BURN) + take_damage(aforce) + return + + + src.add_fingerprint(user) + + if (src.allowed(user)) + if (src.density) + open() + else + close() + + else if (src.density) + flick(text("[]deny", src.base_state), src) + + return + +/obj/machinery/door/window/brigdoor + name = "secure door" + icon = 'icons/obj/doors/windoor.dmi' + icon_state = "leftsecure" + base_state = "leftsecure" + req_access = list(access_security) + var/id = null + maxhealth = 300 + health = 300.0 //Stronger doors for prison (regular window door health is 150) + +/obj/machinery/door/window/brigdoor/shatter() + new /obj/item/stack/rods(src.loc, 2) + ..() + +/obj/machinery/door/window/northleft + dir = NORTH + +/obj/machinery/door/window/eastleft + dir = EAST + +/obj/machinery/door/window/westleft + dir = WEST + +/obj/machinery/door/window/southleft + dir = SOUTH + +/obj/machinery/door/window/northright + dir = NORTH + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/eastright + dir = EAST + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/westright + dir = WEST + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/southright + dir = SOUTH + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/brigdoor/northleft + dir = NORTH + +/obj/machinery/door/window/brigdoor/eastleft + dir = EAST + +/obj/machinery/door/window/brigdoor/westleft + dir = WEST + +/obj/machinery/door/window/brigdoor/southleft + dir = SOUTH + +/obj/machinery/door/window/brigdoor/northright + dir = NORTH + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/eastright + dir = EAST + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/westright + dir = WEST + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/southright + dir = SOUTH + icon_state = "rightsecure" + base_state = "rightsecure" diff --git a/code/game/machinery/doppler_array.dm b/code/game/machinery/doppler_array.dm index d05d9661702..556309040f3 100644 --- a/code/game/machinery/doppler_array.dm +++ b/code/game/machinery/doppler_array.dm @@ -1,51 +1,51 @@ -var/list/doppler_arrays = list() - -/obj/machinery/doppler_array - anchored = TRUE - name = "tachyon-doppler array" - density = TRUE - desc = "A highly precise directional sensor array which measures the release of quants from decaying tachyons. The doppler shifting of the mirror-image formed by these quants can reveal the size, location and temporal affects of energetic disturbances within a large radius ahead of the array." - dir = NORTH - - icon_state = "doppler" - -/obj/machinery/doppler_array/New() - ..() - doppler_arrays += src - -/obj/machinery/doppler_array/Destroy() - doppler_arrays -= src - ..() - -/obj/machinery/doppler_array/proc/sense_explosion(var/x0,var/y0,var/z0,var/devastation_range,var/heavy_impact_range,var/light_impact_range,var/took) - if(stat & NOPOWER) return - if(z != z0) return - - var/dx = abs(x0-x) - var/dy = abs(y0-y) - var/distance - var/direct - - if(dx > dy) - distance = dx - if(x0 > x) direct = EAST - else direct = WEST - else - distance = dy - if(y0 > y) direct = NORTH - else direct = SOUTH - - if(distance > 100) return - if(!(direct & dir)) return - - var/message = "Explosive disturbance detected - Epicenter at: grid ([x0],[y0]). Epicenter radius: [devastation_range]. Outer radius: [heavy_impact_range]. Shockwave radius: [light_impact_range]. Temporal displacement of tachyons: [took]seconds." - - for(var/mob/O in hearers(src, null)) - O.show_message("[src] states coldly, \"[message]\"",2) - -/obj/machinery/doppler_array/power_change() - ..() - if(!(stat & NOPOWER)) - icon_state = initial(icon_state) - else - icon_state = "[initial(icon_state)]_off" +var/list/doppler_arrays = list() + +/obj/machinery/doppler_array + anchored = TRUE + name = "tachyon-doppler array" + density = TRUE + desc = "A highly precise directional sensor array which measures the release of quants from decaying tachyons. The doppler shifting of the mirror-image formed by these quants can reveal the size, location and temporal affects of energetic disturbances within a large radius ahead of the array." + dir = NORTH + + icon_state = "doppler" + +/obj/machinery/doppler_array/New() + ..() + doppler_arrays += src + +/obj/machinery/doppler_array/Destroy() + doppler_arrays -= src + ..() + +/obj/machinery/doppler_array/proc/sense_explosion(var/x0,var/y0,var/z0,var/devastation_range,var/heavy_impact_range,var/light_impact_range,var/took) + if(stat & NOPOWER) return + if(z != z0) return + + var/dx = abs(x0-x) + var/dy = abs(y0-y) + var/distance + var/direct + + if(dx > dy) + distance = dx + if(x0 > x) direct = EAST + else direct = WEST + else + distance = dy + if(y0 > y) direct = NORTH + else direct = SOUTH + + if(distance > 100) return + if(!(direct & dir)) return + + var/message = "Explosive disturbance detected - Epicenter at: grid ([x0],[y0]). Epicenter radius: [devastation_range]. Outer radius: [heavy_impact_range]. Shockwave radius: [light_impact_range]. Temporal displacement of tachyons: [took]seconds." + + for(var/mob/O in hearers(src, null)) + O.show_message("[src] states coldly, \"[message]\"",2) + +/obj/machinery/doppler_array/power_change() + ..() + if(!(stat & NOPOWER)) + icon_state = initial(icon_state) + else + icon_state = "[initial(icon_state)]_off" diff --git a/code/game/machinery/embedded_controller/docking_program.dm b/code/game/machinery/embedded_controller/docking_program.dm index 1dca7665357..7c13792724a 100644 --- a/code/game/machinery/embedded_controller/docking_program.dm +++ b/code/game/machinery/embedded_controller/docking_program.dm @@ -1,309 +1,309 @@ - -#define STATE_UNDOCKED 0 -#define STATE_DOCKING 1 -#define STATE_UNDOCKING 2 -#define STATE_DOCKED 3 - -#define MODE_NONE 0 -#define MODE_SERVER 1 -#define MODE_CLIENT 2 //The one who initiated the docking, and who can initiate the undocking. The server cannot initiate undocking, and is the one responsible for deciding to accept a docking request and signals when docking and undocking is complete. (Think server == station, client == shuttle) - -#define MESSAGE_RESEND_TIME 5 //how long (in seconds) do we wait before resending a message - -/* - *** STATE TABLE *** - - MODE_CLIENT|STATE_UNDOCKED sent a request for docking and now waiting for a reply. - MODE_CLIENT|STATE_DOCKING server told us they are OK to dock, waiting for our docking port to be ready. - MODE_CLIENT|STATE_DOCKED idle - docked as client. - MODE_CLIENT|STATE_UNDOCKING we are either waiting for our docking port to be ready or for the server to give us the OK to finish undocking. - - MODE_SERVER|STATE_UNDOCKED should never happen. - MODE_SERVER|STATE_DOCKING someone requested docking, we are waiting for our docking port to be ready. - MODE_SERVER|STATE_DOCKED idle - docked as server - MODE_SERVER|STATE_UNDOCKING client requested undocking, we are waiting for our docking port to be ready. - - MODE_NONE|STATE_UNDOCKED idle - not docked. - MODE_NONE|anything else should never happen. - - *** Docking Signals *** - - Docking - Client sends request_dock - Server sends confirm_dock to say that yes, we will serve your request - When client is ready, sends confirm_dock - Server sends confirm_dock back to indicate that docking is complete - - Undocking - Client sends request_undock - When client is ready, sends confirm_undock - Server sends confirm_undock back to indicate that docking is complete - - Note that in both cases each side exchanges confirm_dock before the docking operation is considered done. - The client first sends a confirm message to indicate it is ready, and then finally the server will send it's - confirm message to indicate that the operation is complete. - - Note also that when docking, the server sends an additional confirm message. This is because before docking, - the server and client do not have a defined relationship. Before undocking, the server and client are already - related to each other, thus the extra confirm message is not needed. - - *** Override, what is it? *** - - The purpose of enabling the override is to prevent the docking program from automatically doing things with the docking port when docking or undocking. - Maybe the shuttle is full of plamsa/phoron for some reason, and you don't want the door to automatically open, or the airlock to cycle. - This means that the prepare_for_docking/undocking and finish_docking/undocking procs don't get called. - - The docking controller will still check the state of the docking port, and thus prevent the shuttle from launching unless they force the launch (handling forced - launches is not the docking controller's responsibility). In this case it is up to the players to manually get the docking port into a good state to undock - (which usually just means closing and locking the doors). - - In line with this, docking controllers should prevent players from manually doing things when the override is NOT enabled. -*/ - - -/datum/embedded_program/docking - var/tag_target //the tag of the docking controller that we are trying to dock with - var/dock_state = STATE_UNDOCKED - var/control_mode = MODE_NONE - var/response_sent = 0 //so we don't spam confirmation messages - var/resend_counter = 0 //for periodically resending confirmation messages in case they are missed - - var/override_enabled = 0 //when enabled, do not open/close doors or cycle airlocks and wait for the player to do it manually - var/received_confirm = 0 //for undocking, whether the server has recieved a confirmation from the client - var/docking_codes //would only allow docking when receiving signal with these, if set - var/display_name //Override the name shown on docking monitoring program; defaults to area name + coordinates if unset - -/datum/embedded_program/docking/New() - ..() - if(id_tag) - if(SSshuttles.docking_registry[id_tag]) - stack_trace("Docking controller tag [id_tag] had multiple associated programs.") - SSshuttles.docking_registry[id_tag] = src - -/datum/embedded_program/docking/Destroy() - SSshuttles.docking_registry -= id_tag - return ..() - -/datum/embedded_program/docking/receive_signal(datum/signal/signal, receive_method, receive_param) - var/receive_tag = signal.data["tag"] //for docking signals, this is the sender id - var/command = signal.data["command"] - var/recipient = signal.data["recipient"] //the intended recipient of the docking signal - - if (recipient != id_tag) - return //this signal is not for us - - switch (command) - if ("confirm_dock") - if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKED && receive_tag == tag_target) - dock_state = STATE_DOCKING - broadcast_docking_status() - if (!override_enabled) - prepare_for_docking() - - else if (control_mode == MODE_CLIENT && dock_state == STATE_DOCKING && receive_tag == tag_target) - dock_state = STATE_DOCKED - broadcast_docking_status() - if (!override_enabled) - finish_docking() //client done docking! - response_sent = 0 - else if (control_mode == MODE_SERVER && dock_state == STATE_DOCKING && receive_tag == tag_target) //client just sent us the confirmation back, we're done with the docking process - received_confirm = 1 - - if ("request_dock") - if (control_mode == MODE_NONE && dock_state == STATE_UNDOCKED) - - tag_target = receive_tag - - if(docking_codes) - var/code = signal.data["code"] - if(code != docking_codes) - log_debug("Controller [id_tag] got request_dock but code:[code] != docking_codes:[docking_codes]") - return - - control_mode = MODE_SERVER - dock_state = STATE_DOCKING - broadcast_docking_status() - - - if (!override_enabled) - prepare_for_docking() - send_docking_command(tag_target, "confirm_dock") //acknowledge the request - - if ("confirm_undock") - if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKING && receive_tag == tag_target) - if (!override_enabled) - finish_undocking() - reset() //client is done undocking! - else if (control_mode == MODE_SERVER && dock_state == STATE_UNDOCKING && receive_tag == tag_target) - received_confirm = 1 - - if ("request_undock") - if (control_mode == MODE_SERVER && dock_state == STATE_DOCKED && receive_tag == tag_target) - dock_state = STATE_UNDOCKING - broadcast_docking_status() - - if (!override_enabled) - prepare_for_undocking() - - if ("dock_error") - if (receive_tag == tag_target) - reset() - -/datum/embedded_program/docking/process() - switch(dock_state) - if (STATE_DOCKING) //waiting for our docking port to be ready for docking - if (ready_for_docking()) - if (control_mode == MODE_CLIENT) - if (!response_sent) - send_docking_command(tag_target, "confirm_dock") //tell the server we're ready - response_sent = 1 - - else if (control_mode == MODE_SERVER && received_confirm) - send_docking_command(tag_target, "confirm_dock") //tell the client we are done docking. - - dock_state = STATE_DOCKED - broadcast_docking_status() - - if (!override_enabled) - finish_docking() //server done docking! - response_sent = 0 - received_confirm = 0 - - if (STATE_UNDOCKING) - if (ready_for_undocking()) - if (control_mode == MODE_CLIENT) - if (!response_sent) - send_docking_command(tag_target, "confirm_undock") //tell the server we are OK to undock. - response_sent = 1 - - else if (control_mode == MODE_SERVER && received_confirm) - send_docking_command(tag_target, "confirm_undock") //tell the client we are done undocking. - if (!override_enabled) - finish_undocking() - reset() //server is done undocking! - - if (response_sent || resend_counter > 0) - resend_counter++ - - if (resend_counter >= MESSAGE_RESEND_TIME || (dock_state != STATE_DOCKING && dock_state != STATE_UNDOCKING)) - response_sent = 0 - resend_counter = 0 - - //handle invalid states - if (control_mode == MODE_NONE && dock_state != STATE_UNDOCKED) - if (tag_target) - send_docking_command(tag_target, "dock_error") - reset() - if (control_mode == MODE_SERVER && dock_state == STATE_UNDOCKED) - control_mode = MODE_NONE - - -/datum/embedded_program/docking/proc/initiate_docking(var/target) - if (dock_state != STATE_UNDOCKED || control_mode == MODE_SERVER) //must be undocked and not serving another request to begin a new docking handshake - return - - tag_target = target - control_mode = MODE_CLIENT - - send_docking_command(tag_target, "request_dock") - -/datum/embedded_program/docking/proc/initiate_undocking() - if (dock_state != STATE_DOCKED || control_mode != MODE_CLIENT) //must be docked and must be client to start undocking - return - - dock_state = STATE_UNDOCKING - broadcast_docking_status() - - if (!override_enabled) - prepare_for_undocking() - - send_docking_command(tag_target, "request_undock") - -//tell the docking port to start getting ready for docking - e.g. pressurize -/datum/embedded_program/docking/proc/prepare_for_docking() - return - -//are we ready for docking? -/datum/embedded_program/docking/proc/ready_for_docking() - return 1 - -//we are docked, open the doors or whatever. -/datum/embedded_program/docking/proc/finish_docking() - return - -//tell the docking port to start getting ready for undocking - e.g. close those doors. -/datum/embedded_program/docking/proc/prepare_for_undocking() - return - -//we are docked, open the doors or whatever. -/datum/embedded_program/docking/proc/finish_undocking() - return - -//are we ready for undocking? -/datum/embedded_program/docking/proc/ready_for_undocking() - return 1 - -/datum/embedded_program/docking/proc/enable_override() - override_enabled = 1 - -/datum/embedded_program/docking/proc/disable_override() - override_enabled = 0 - -/datum/embedded_program/docking/proc/reset() - dock_state = STATE_UNDOCKED - broadcast_docking_status() - - control_mode = MODE_NONE - tag_target = null - response_sent = 0 - received_confirm = 0 - -/datum/embedded_program/docking/proc/force_undock() - //to_world("[id_tag]: forcing undock") - if (tag_target) - send_docking_command(tag_target, "dock_error") - reset() - -/datum/embedded_program/docking/proc/docked() - return (dock_state == STATE_DOCKED) - -/datum/embedded_program/docking/proc/undocked() - return (dock_state == STATE_UNDOCKED) - -//returns 1 if we are saftely undocked (and the shuttle can leave) -/datum/embedded_program/docking/proc/can_launch() - return undocked() - -/datum/embedded_program/docking/proc/send_docking_command(var/recipient, var/command) - var/datum/signal/signal = new - signal.data["tag"] = id_tag - signal.data["command"] = command - signal.data["recipient"] = recipient - signal.data["code"] = docking_codes - post_signal(signal) - -/datum/embedded_program/docking/proc/broadcast_docking_status() - var/datum/signal/signal = new - signal.data["tag"] = id_tag - signal.data["dock_status"] = get_docking_status() - post_signal(signal) - -//this is mostly for NanoUI -/datum/embedded_program/docking/proc/get_docking_status() - switch (dock_state) - if (STATE_UNDOCKED) return "undocked" - if (STATE_DOCKING) return "docking" - if (STATE_UNDOCKING) return "undocking" - if (STATE_DOCKED) return "docked" - -/datum/embedded_program/docking/proc/get_name() - return display_name ? display_name : "[get_area(master)] ([master.x], [master.y])" - -#undef STATE_UNDOCKED -#undef STATE_DOCKING -#undef STATE_UNDOCKING -#undef STATE_DOCKED - -#undef MODE_NONE -#undef MODE_SERVER + +#define STATE_UNDOCKED 0 +#define STATE_DOCKING 1 +#define STATE_UNDOCKING 2 +#define STATE_DOCKED 3 + +#define MODE_NONE 0 +#define MODE_SERVER 1 +#define MODE_CLIENT 2 //The one who initiated the docking, and who can initiate the undocking. The server cannot initiate undocking, and is the one responsible for deciding to accept a docking request and signals when docking and undocking is complete. (Think server == station, client == shuttle) + +#define MESSAGE_RESEND_TIME 5 //how long (in seconds) do we wait before resending a message + +/* + *** STATE TABLE *** + + MODE_CLIENT|STATE_UNDOCKED sent a request for docking and now waiting for a reply. + MODE_CLIENT|STATE_DOCKING server told us they are OK to dock, waiting for our docking port to be ready. + MODE_CLIENT|STATE_DOCKED idle - docked as client. + MODE_CLIENT|STATE_UNDOCKING we are either waiting for our docking port to be ready or for the server to give us the OK to finish undocking. + + MODE_SERVER|STATE_UNDOCKED should never happen. + MODE_SERVER|STATE_DOCKING someone requested docking, we are waiting for our docking port to be ready. + MODE_SERVER|STATE_DOCKED idle - docked as server + MODE_SERVER|STATE_UNDOCKING client requested undocking, we are waiting for our docking port to be ready. + + MODE_NONE|STATE_UNDOCKED idle - not docked. + MODE_NONE|anything else should never happen. + + *** Docking Signals *** + + Docking + Client sends request_dock + Server sends confirm_dock to say that yes, we will serve your request + When client is ready, sends confirm_dock + Server sends confirm_dock back to indicate that docking is complete + + Undocking + Client sends request_undock + When client is ready, sends confirm_undock + Server sends confirm_undock back to indicate that docking is complete + + Note that in both cases each side exchanges confirm_dock before the docking operation is considered done. + The client first sends a confirm message to indicate it is ready, and then finally the server will send it's + confirm message to indicate that the operation is complete. + + Note also that when docking, the server sends an additional confirm message. This is because before docking, + the server and client do not have a defined relationship. Before undocking, the server and client are already + related to each other, thus the extra confirm message is not needed. + + *** Override, what is it? *** + + The purpose of enabling the override is to prevent the docking program from automatically doing things with the docking port when docking or undocking. + Maybe the shuttle is full of plamsa/phoron for some reason, and you don't want the door to automatically open, or the airlock to cycle. + This means that the prepare_for_docking/undocking and finish_docking/undocking procs don't get called. + + The docking controller will still check the state of the docking port, and thus prevent the shuttle from launching unless they force the launch (handling forced + launches is not the docking controller's responsibility). In this case it is up to the players to manually get the docking port into a good state to undock + (which usually just means closing and locking the doors). + + In line with this, docking controllers should prevent players from manually doing things when the override is NOT enabled. +*/ + + +/datum/embedded_program/docking + var/tag_target //the tag of the docking controller that we are trying to dock with + var/dock_state = STATE_UNDOCKED + var/control_mode = MODE_NONE + var/response_sent = 0 //so we don't spam confirmation messages + var/resend_counter = 0 //for periodically resending confirmation messages in case they are missed + + var/override_enabled = 0 //when enabled, do not open/close doors or cycle airlocks and wait for the player to do it manually + var/received_confirm = 0 //for undocking, whether the server has recieved a confirmation from the client + var/docking_codes //would only allow docking when receiving signal with these, if set + var/display_name //Override the name shown on docking monitoring program; defaults to area name + coordinates if unset + +/datum/embedded_program/docking/New() + ..() + if(id_tag) + if(SSshuttles.docking_registry[id_tag]) + stack_trace("Docking controller tag [id_tag] had multiple associated programs.") + SSshuttles.docking_registry[id_tag] = src + +/datum/embedded_program/docking/Destroy() + SSshuttles.docking_registry -= id_tag + return ..() + +/datum/embedded_program/docking/receive_signal(datum/signal/signal, receive_method, receive_param) + var/receive_tag = signal.data["tag"] //for docking signals, this is the sender id + var/command = signal.data["command"] + var/recipient = signal.data["recipient"] //the intended recipient of the docking signal + + if (recipient != id_tag) + return //this signal is not for us + + switch (command) + if ("confirm_dock") + if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKED && receive_tag == tag_target) + dock_state = STATE_DOCKING + broadcast_docking_status() + if (!override_enabled) + prepare_for_docking() + + else if (control_mode == MODE_CLIENT && dock_state == STATE_DOCKING && receive_tag == tag_target) + dock_state = STATE_DOCKED + broadcast_docking_status() + if (!override_enabled) + finish_docking() //client done docking! + response_sent = 0 + else if (control_mode == MODE_SERVER && dock_state == STATE_DOCKING && receive_tag == tag_target) //client just sent us the confirmation back, we're done with the docking process + received_confirm = 1 + + if ("request_dock") + if (control_mode == MODE_NONE && dock_state == STATE_UNDOCKED) + + tag_target = receive_tag + + if(docking_codes) + var/code = signal.data["code"] + if(code != docking_codes) + log_debug("Controller [id_tag] got request_dock but code:[code] != docking_codes:[docking_codes]") + return + + control_mode = MODE_SERVER + dock_state = STATE_DOCKING + broadcast_docking_status() + + + if (!override_enabled) + prepare_for_docking() + send_docking_command(tag_target, "confirm_dock") //acknowledge the request + + if ("confirm_undock") + if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKING && receive_tag == tag_target) + if (!override_enabled) + finish_undocking() + reset() //client is done undocking! + else if (control_mode == MODE_SERVER && dock_state == STATE_UNDOCKING && receive_tag == tag_target) + received_confirm = 1 + + if ("request_undock") + if (control_mode == MODE_SERVER && dock_state == STATE_DOCKED && receive_tag == tag_target) + dock_state = STATE_UNDOCKING + broadcast_docking_status() + + if (!override_enabled) + prepare_for_undocking() + + if ("dock_error") + if (receive_tag == tag_target) + reset() + +/datum/embedded_program/docking/process() + switch(dock_state) + if (STATE_DOCKING) //waiting for our docking port to be ready for docking + if (ready_for_docking()) + if (control_mode == MODE_CLIENT) + if (!response_sent) + send_docking_command(tag_target, "confirm_dock") //tell the server we're ready + response_sent = 1 + + else if (control_mode == MODE_SERVER && received_confirm) + send_docking_command(tag_target, "confirm_dock") //tell the client we are done docking. + + dock_state = STATE_DOCKED + broadcast_docking_status() + + if (!override_enabled) + finish_docking() //server done docking! + response_sent = 0 + received_confirm = 0 + + if (STATE_UNDOCKING) + if (ready_for_undocking()) + if (control_mode == MODE_CLIENT) + if (!response_sent) + send_docking_command(tag_target, "confirm_undock") //tell the server we are OK to undock. + response_sent = 1 + + else if (control_mode == MODE_SERVER && received_confirm) + send_docking_command(tag_target, "confirm_undock") //tell the client we are done undocking. + if (!override_enabled) + finish_undocking() + reset() //server is done undocking! + + if (response_sent || resend_counter > 0) + resend_counter++ + + if (resend_counter >= MESSAGE_RESEND_TIME || (dock_state != STATE_DOCKING && dock_state != STATE_UNDOCKING)) + response_sent = 0 + resend_counter = 0 + + //handle invalid states + if (control_mode == MODE_NONE && dock_state != STATE_UNDOCKED) + if (tag_target) + send_docking_command(tag_target, "dock_error") + reset() + if (control_mode == MODE_SERVER && dock_state == STATE_UNDOCKED) + control_mode = MODE_NONE + + +/datum/embedded_program/docking/proc/initiate_docking(var/target) + if (dock_state != STATE_UNDOCKED || control_mode == MODE_SERVER) //must be undocked and not serving another request to begin a new docking handshake + return + + tag_target = target + control_mode = MODE_CLIENT + + send_docking_command(tag_target, "request_dock") + +/datum/embedded_program/docking/proc/initiate_undocking() + if (dock_state != STATE_DOCKED || control_mode != MODE_CLIENT) //must be docked and must be client to start undocking + return + + dock_state = STATE_UNDOCKING + broadcast_docking_status() + + if (!override_enabled) + prepare_for_undocking() + + send_docking_command(tag_target, "request_undock") + +//tell the docking port to start getting ready for docking - e.g. pressurize +/datum/embedded_program/docking/proc/prepare_for_docking() + return + +//are we ready for docking? +/datum/embedded_program/docking/proc/ready_for_docking() + return 1 + +//we are docked, open the doors or whatever. +/datum/embedded_program/docking/proc/finish_docking() + return + +//tell the docking port to start getting ready for undocking - e.g. close those doors. +/datum/embedded_program/docking/proc/prepare_for_undocking() + return + +//we are docked, open the doors or whatever. +/datum/embedded_program/docking/proc/finish_undocking() + return + +//are we ready for undocking? +/datum/embedded_program/docking/proc/ready_for_undocking() + return 1 + +/datum/embedded_program/docking/proc/enable_override() + override_enabled = 1 + +/datum/embedded_program/docking/proc/disable_override() + override_enabled = 0 + +/datum/embedded_program/docking/proc/reset() + dock_state = STATE_UNDOCKED + broadcast_docking_status() + + control_mode = MODE_NONE + tag_target = null + response_sent = 0 + received_confirm = 0 + +/datum/embedded_program/docking/proc/force_undock() + //to_world("[id_tag]: forcing undock") + if (tag_target) + send_docking_command(tag_target, "dock_error") + reset() + +/datum/embedded_program/docking/proc/docked() + return (dock_state == STATE_DOCKED) + +/datum/embedded_program/docking/proc/undocked() + return (dock_state == STATE_UNDOCKED) + +//returns 1 if we are saftely undocked (and the shuttle can leave) +/datum/embedded_program/docking/proc/can_launch() + return undocked() + +/datum/embedded_program/docking/proc/send_docking_command(var/recipient, var/command) + var/datum/signal/signal = new + signal.data["tag"] = id_tag + signal.data["command"] = command + signal.data["recipient"] = recipient + signal.data["code"] = docking_codes + post_signal(signal) + +/datum/embedded_program/docking/proc/broadcast_docking_status() + var/datum/signal/signal = new + signal.data["tag"] = id_tag + signal.data["dock_status"] = get_docking_status() + post_signal(signal) + +//this is mostly for NanoUI +/datum/embedded_program/docking/proc/get_docking_status() + switch (dock_state) + if (STATE_UNDOCKED) return "undocked" + if (STATE_DOCKING) return "docking" + if (STATE_UNDOCKING) return "undocking" + if (STATE_DOCKED) return "docked" + +/datum/embedded_program/docking/proc/get_name() + return display_name ? display_name : "[get_area(master)] ([master.x], [master.y])" + +#undef STATE_UNDOCKED +#undef STATE_DOCKING +#undef STATE_UNDOCKING +#undef STATE_DOCKED + +#undef MODE_NONE +#undef MODE_SERVER #undef MODE_CLIENT \ No newline at end of file diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm index 4a05005ac56..268bde37cea 100644 --- a/code/game/machinery/embedded_controller/embedded_controller_base.dm +++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm @@ -1,109 +1,109 @@ -/obj/machinery/embedded_controller - name = "Embedded Controller" - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 10 - var/datum/embedded_program/program //the currently executing program - var/list/valid_actions = list() - var/on = 1 - -/obj/machinery/embedded_controller/Initialize() - if(ispath(program)) - program = new program(src) - return ..() - -/obj/machinery/embedded_controller/Destroy() - if(istype(program)) - qdel(program) // the program will clear the ref in its Destroy - return ..() - -/obj/machinery/embedded_controller/proc/post_signal(datum/signal/signal, comm_line) - return 0 - -/obj/machinery/embedded_controller/receive_signal(datum/signal/signal, receive_method, receive_param) - if(!signal || signal.encryption) return - - if(program) - program.receive_signal(signal, receive_method, receive_param) - -/obj/machinery/embedded_controller/Topic() - . = ..() - stack_trace("WARNING: Embedded controller [src] ([type]) had Topic() called unexpectedly. Please report this.") - -/obj/machinery/embedded_controller/tgui_act(action, params) - if(..()) - return TRUE - if(LAZYLEN(valid_actions)) - if(action in valid_actions) - program.receive_user_command(action) - if(usr) - add_fingerprint(usr) - -/obj/machinery/embedded_controller/process() - if(program) - program.process() - - update_icon() - -/obj/machinery/embedded_controller/attack_ai(mob/user as mob) - tgui_interact(user) - -/obj/machinery/embedded_controller/attack_hand(mob/user as mob) - if(!user.IsAdvancedToolUser()) - return 0 - - tgui_interact(user) - -/obj/machinery/embedded_controller/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "EmbeddedController", src) - ui.open() - -// -// Embedded controller with a radio! (Most things (All things?) use this) -// -/obj/machinery/embedded_controller/radio - icon = 'icons/obj/airlock_machines.dmi' - icon_state = "airlock_control_standby" - power_channel = ENVIRON - density = FALSE - unacidable = TRUE - - var/id_tag - //var/radio_power_use = 50 //power used to xmit signals - - var/frequency = 1379 - var/radio_filter = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/embedded_controller/radio/Initialize() - set_frequency(frequency) // Set it before parent instantiates program - . = ..() - -/obj/machinery/embedded_controller/radio/Destroy() - if(radio_controller) - radio_controller.remove_object(src,frequency) - ..() - -/obj/machinery/embedded_controller/radio/update_icon() - if(on && program) - if(program.memory["processing"]) - icon_state = "airlock_control_process" - else - icon_state = "airlock_control_standby" - else - icon_state = "airlock_control_off" - -/obj/machinery/embedded_controller/radio/post_signal(datum/signal/signal, var/radio_filter = null) - signal.transmission_method = TRANSMISSION_RADIO - if(radio_connection) - //use_power(radio_power_use) //neat idea, but causes way too much lag. - return radio_connection.post_signal(src, signal, radio_filter) - else - qdel(signal) - -/obj/machinery/embedded_controller/radio/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency +/obj/machinery/embedded_controller + name = "Embedded Controller" + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 10 + var/datum/embedded_program/program //the currently executing program + var/list/valid_actions = list() + var/on = 1 + +/obj/machinery/embedded_controller/Initialize() + if(ispath(program)) + program = new program(src) + return ..() + +/obj/machinery/embedded_controller/Destroy() + if(istype(program)) + qdel(program) // the program will clear the ref in its Destroy + return ..() + +/obj/machinery/embedded_controller/proc/post_signal(datum/signal/signal, comm_line) + return 0 + +/obj/machinery/embedded_controller/receive_signal(datum/signal/signal, receive_method, receive_param) + if(!signal || signal.encryption) return + + if(program) + program.receive_signal(signal, receive_method, receive_param) + +/obj/machinery/embedded_controller/Topic() + . = ..() + stack_trace("WARNING: Embedded controller [src] ([type]) had Topic() called unexpectedly. Please report this.") + +/obj/machinery/embedded_controller/tgui_act(action, params) + if(..()) + return TRUE + if(LAZYLEN(valid_actions)) + if(action in valid_actions) + program.receive_user_command(action) + if(usr) + add_fingerprint(usr) + +/obj/machinery/embedded_controller/process() + if(program) + program.process() + + update_icon() + +/obj/machinery/embedded_controller/attack_ai(mob/user as mob) + tgui_interact(user) + +/obj/machinery/embedded_controller/attack_hand(mob/user as mob) + if(!user.IsAdvancedToolUser()) + return 0 + + tgui_interact(user) + +/obj/machinery/embedded_controller/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "EmbeddedController", src) + ui.open() + +// +// Embedded controller with a radio! (Most things (All things?) use this) +// +/obj/machinery/embedded_controller/radio + icon = 'icons/obj/airlock_machines.dmi' + icon_state = "airlock_control_standby" + power_channel = ENVIRON + density = FALSE + unacidable = TRUE + + var/id_tag + //var/radio_power_use = 50 //power used to xmit signals + + var/frequency = 1379 + var/radio_filter = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/embedded_controller/radio/Initialize() + set_frequency(frequency) // Set it before parent instantiates program + . = ..() + +/obj/machinery/embedded_controller/radio/Destroy() + if(radio_controller) + radio_controller.remove_object(src,frequency) + ..() + +/obj/machinery/embedded_controller/radio/update_icon() + if(on && program) + if(program.memory["processing"]) + icon_state = "airlock_control_process" + else + icon_state = "airlock_control_standby" + else + icon_state = "airlock_control_off" + +/obj/machinery/embedded_controller/radio/post_signal(datum/signal/signal, var/radio_filter = null) + signal.transmission_method = TRANSMISSION_RADIO + if(radio_connection) + //use_power(radio_power_use) //neat idea, but causes way too much lag. + return radio_connection.post_signal(src, signal, radio_filter) + else + qdel(signal) + +/obj/machinery/embedded_controller/radio/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency radio_connection = radio_controller.add_object(src, frequency, radio_filter) \ No newline at end of file diff --git a/code/game/machinery/embedded_controller/embedded_program_base.dm b/code/game/machinery/embedded_controller/embedded_program_base.dm index 664d60de816..7e49d792df6 100644 --- a/code/game/machinery/embedded_controller/embedded_program_base.dm +++ b/code/game/machinery/embedded_controller/embedded_program_base.dm @@ -1,31 +1,31 @@ -/datum/embedded_program - var/name - var/list/memory = list() - var/obj/machinery/embedded_controller/master - - var/id_tag - -/datum/embedded_program/New(var/obj/machinery/embedded_controller/M) - master = M - if (istype(M, /obj/machinery/embedded_controller/radio)) - var/obj/machinery/embedded_controller/radio/R = M - id_tag = R.id_tag - -/datum/embedded_program/Destroy() - if(master) - master.program = null - master = null - return ..() - -// Return TRUE if was a command for us, otherwise return FALSE (so controllers with multiple programs can try each in turn until one accepts) -/datum/embedded_program/proc/receive_user_command(command) - return FALSE - -/datum/embedded_program/proc/receive_signal(datum/signal/signal, receive_method, receive_param) - return - -/datum/embedded_program/proc/post_signal(datum/signal/signal, comm_line) - if(master) - master.post_signal(signal, comm_line) - else - qdel(signal) +/datum/embedded_program + var/name + var/list/memory = list() + var/obj/machinery/embedded_controller/master + + var/id_tag + +/datum/embedded_program/New(var/obj/machinery/embedded_controller/M) + master = M + if (istype(M, /obj/machinery/embedded_controller/radio)) + var/obj/machinery/embedded_controller/radio/R = M + id_tag = R.id_tag + +/datum/embedded_program/Destroy() + if(master) + master.program = null + master = null + return ..() + +// Return TRUE if was a command for us, otherwise return FALSE (so controllers with multiple programs can try each in turn until one accepts) +/datum/embedded_program/proc/receive_user_command(command) + return FALSE + +/datum/embedded_program/proc/receive_signal(datum/signal/signal, receive_method, receive_param) + return + +/datum/embedded_program/proc/post_signal(datum/signal/signal, comm_line) + if(master) + master.post_signal(signal, comm_line) + else + qdel(signal) diff --git a/code/game/machinery/event/stage_vr.dm b/code/game/machinery/event/stage_vr.dm index 1ed1e6c419a..24b4bd62eba 100644 --- a/code/game/machinery/event/stage_vr.dm +++ b/code/game/machinery/event/stage_vr.dm @@ -1,10 +1,10 @@ -/obj/structure/event/stage - name = "stage" - desc = "It's a stage!" - icon = 'icons/misc/event/stage.dmi' - icon_state = "stage1" - anchored = TRUE - density = FALSE - pixel_y = -224 - pixel_x = -224 +/obj/structure/event/stage + name = "stage" + desc = "It's a stage!" + icon = 'icons/misc/event/stage.dmi' + icon_state = "stage1" + anchored = TRUE + density = FALSE + pixel_y = -224 + pixel_x = -224 plane = -44 \ No newline at end of file diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 6668d1cf398..47041e68750 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -1,148 +1,148 @@ -// It is a gizmo that flashes a small area -/obj/machinery/flasher - name = "Mounted flash" - desc = "A wall-mounted flashbulb device." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "mflash1" - layer = ABOVE_WINDOW_LAYER - var/id = null - var/range = 2 //this is roughly the size of brig cell - var/disable = 0 - var/last_flash = 0 //Don't want it getting spammed like regular flashes - var/strength = 10 //How weakened targets are when flashed. - var/base_state = "mflash" - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 2 - -/obj/machinery/flasher/portable //Portable version of the flasher. Only flashes when anchored - name = "portable flasher" - desc = "A portable flashing device. Wrench to activate and deactivate. Cannot detect slow movements." - icon_state = "pflash1" - strength = 8 - anchored = FALSE - base_state = "pflash" - density = TRUE - -/obj/machinery/flasher/power_change() - ..() - if(!(stat & NOPOWER)) - icon_state = "[base_state]1" -// sd_SetLuminosity(2) - else - icon_state = "[base_state]1-p" -// sd_SetLuminosity(0) - -//Don't want to render prison breaks impossible -/obj/machinery/flasher/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.has_tool_quality(TOOL_WIRECUTTER)) - add_fingerprint(user) - disable = !disable - if(disable) - user.visible_message("[user] has disconnected the [src]'s flashbulb!", "You disconnect the [src]'s flashbulb!") - if(!disable) - user.visible_message("[user] has connected the [src]'s flashbulb!", "You connect the [src]'s flashbulb!") - -//Let the AI trigger them directly. -/obj/machinery/flasher/attack_ai() - if(anchored) - return flash() - else - return - -/obj/machinery/flasher/proc/flash() - if(!(powered())) - return - - if((disable) || (last_flash && world.time < last_flash + 150)) - return - - playsound(src, 'sound/weapons/flash.ogg', 100, 1) - flick("[base_state]_flash", src) - last_flash = world.time - use_power(1500) - - for (var/mob/O in viewers(src, null)) - if(get_dist(src, O) > range) - continue - - var/flash_time = strength - if(istype(O, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = O - //VOREStation Edit Start - if(H.nif && H.nif.flag_check(NIF_V_FLASHPROT,NIF_FLAGS_VISION)) - H.nif.notify("High intensity light detected, and blocked!",TRUE) - continue - //VOREStation Edit End - if(!H.eyecheck() <= 0) - continue - flash_time *= H.species.flash_mod - var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] - if(!E) - return - if(E.is_bruised() && prob(E.damage + 50)) - H.flash_eyes() - E.damage += rand(1, 5) - else - if(!O.blinded && isliving(O)) - var/mob/living/L = O - L.flash_eyes() - O.Weaken(flash_time) - -/obj/machinery/flasher/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - if(prob(75/severity)) - flash() - ..(severity) - -/obj/machinery/flasher/portable/HasProximity(turf/T, atom/movable/AM, oldloc) - if(disable || !anchored || (last_flash && world.time < last_flash + 150)) - return - - if(iscarbon(AM)) - var/mob/living/carbon/M = AM - if(M.m_intent != "walk") - flash() - -/obj/machinery/flasher/portable/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.has_tool_quality(TOOL_WRENCH)) - add_fingerprint(user) - anchored = !anchored - - if(!anchored) - user.show_message(text("[src] can now be moved.")) - cut_overlays() - unsense_proximity(callback = /atom/proc/HasProximity) - - else if(anchored) - user.show_message(text("[src] is now secured.")) - add_overlay("[base_state]-s") - sense_proximity(callback = /atom/proc/HasProximity) - -/obj/machinery/button/flasher - name = "flasher button" - desc = "A remote control switch for a mounted flasher." - -/obj/machinery/button/flasher/attack_hand(mob/user as mob) - - if(..()) - return - - use_power(5) - - active = 1 - icon_state = "launcheract" - - for(var/obj/machinery/flasher/M in machines) - if(M.id == id) - spawn() - M.flash() - - sleep(50) - - icon_state = "launcherbtt" - active = 0 - +// It is a gizmo that flashes a small area +/obj/machinery/flasher + name = "Mounted flash" + desc = "A wall-mounted flashbulb device." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "mflash1" + layer = ABOVE_WINDOW_LAYER + var/id = null + var/range = 2 //this is roughly the size of brig cell + var/disable = 0 + var/last_flash = 0 //Don't want it getting spammed like regular flashes + var/strength = 10 //How weakened targets are when flashed. + var/base_state = "mflash" + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 2 + +/obj/machinery/flasher/portable //Portable version of the flasher. Only flashes when anchored + name = "portable flasher" + desc = "A portable flashing device. Wrench to activate and deactivate. Cannot detect slow movements." + icon_state = "pflash1" + strength = 8 + anchored = FALSE + base_state = "pflash" + density = TRUE + +/obj/machinery/flasher/power_change() + ..() + if(!(stat & NOPOWER)) + icon_state = "[base_state]1" +// sd_SetLuminosity(2) + else + icon_state = "[base_state]1-p" +// sd_SetLuminosity(0) + +//Don't want to render prison breaks impossible +/obj/machinery/flasher/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WIRECUTTER)) + add_fingerprint(user) + disable = !disable + if(disable) + user.visible_message("[user] has disconnected the [src]'s flashbulb!", "You disconnect the [src]'s flashbulb!") + if(!disable) + user.visible_message("[user] has connected the [src]'s flashbulb!", "You connect the [src]'s flashbulb!") + +//Let the AI trigger them directly. +/obj/machinery/flasher/attack_ai() + if(anchored) + return flash() + else + return + +/obj/machinery/flasher/proc/flash() + if(!(powered())) + return + + if((disable) || (last_flash && world.time < last_flash + 150)) + return + + playsound(src, 'sound/weapons/flash.ogg', 100, 1) + flick("[base_state]_flash", src) + last_flash = world.time + use_power(1500) + + for (var/mob/O in viewers(src, null)) + if(get_dist(src, O) > range) + continue + + var/flash_time = strength + if(istype(O, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = O + //VOREStation Edit Start + if(H.nif && H.nif.flag_check(NIF_V_FLASHPROT,NIF_FLAGS_VISION)) + H.nif.notify("High intensity light detected, and blocked!",TRUE) + continue + //VOREStation Edit End + if(!H.eyecheck() <= 0) + continue + flash_time *= H.species.flash_mod + var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] + if(!E) + return + if(E.is_bruised() && prob(E.damage + 50)) + H.flash_eyes() + E.damage += rand(1, 5) + else + if(!O.blinded && isliving(O)) + var/mob/living/L = O + L.flash_eyes() + O.Weaken(flash_time) + +/obj/machinery/flasher/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + if(prob(75/severity)) + flash() + ..(severity) + +/obj/machinery/flasher/portable/HasProximity(turf/T, atom/movable/AM, oldloc) + if(disable || !anchored || (last_flash && world.time < last_flash + 150)) + return + + if(iscarbon(AM)) + var/mob/living/carbon/M = AM + if(M.m_intent != "walk") + flash() + +/obj/machinery/flasher/portable/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + add_fingerprint(user) + anchored = !anchored + + if(!anchored) + user.show_message(text("[src] can now be moved.")) + cut_overlays() + unsense_proximity(callback = /atom/proc/HasProximity) + + else if(anchored) + user.show_message(text("[src] is now secured.")) + add_overlay("[base_state]-s") + sense_proximity(callback = /atom/proc/HasProximity) + +/obj/machinery/button/flasher + name = "flasher button" + desc = "A remote control switch for a mounted flasher." + +/obj/machinery/button/flasher/attack_hand(mob/user as mob) + + if(..()) + return + + use_power(5) + + active = 1 + icon_state = "launcheract" + + for(var/obj/machinery/flasher/M in machines) + if(M.id == id) + spawn() + M.flash() + + sleep(50) + + icon_state = "launcherbtt" + active = 0 + return \ No newline at end of file diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 0df4174d1bb..a48fa1e79ee 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -1,259 +1,259 @@ -/* Holograms! - * Contains: - * Holopad - * Hologram - * Other stuff - */ - -/* -Revised. Original based on space ninja hologram code. Which is also mine. /N -How it works: -AI clicks on holopad in camera view. View centers on holopad. -AI clicks again on the holopad to display a hologram. Hologram stays as long as AI is looking at the pad and it (the hologram) is in range of the pad. -AI can use the directional keys to move the hologram around, provided the above conditions are met and the AI in question is the holopad's master. -Only one AI may project from a holopad at any given time. -AI may cancel the hologram at any time by clicking on the holopad once more. - -Possible to do for anyone motivated enough: - Give an AI variable for different hologram icons. - Itegrate EMP effect to disable the unit. -*/ - -/* - * Holopad - */ -#define HOLOPAD_PASSIVE_POWER_USAGE 1 -#define HOLOGRAM_POWER_USAGE 2 -#define RANGE_BASED 4 -#define AREA_BASED 6 - -var/const/HOLOPAD_MODE = RANGE_BASED - -/obj/machinery/hologram/holopad - name = "\improper AI holopad" - desc = "It's a floor-mounted device for projecting holographic images. It is activated remotely." - icon_state = "holopad0" - show_messages = 1 - circuit = /obj/item/weapon/circuitboard/holopad - plane = TURF_PLANE - layer = ABOVE_TURF_LAYER - var/power_per_hologram = 500 //per usage per hologram - idle_power_usage = 5 - use_power = USE_POWER_IDLE - var/list/mob/living/silicon/ai/masters = new() //List of AIs that use the holopad - var/last_request = 0 //to prevent request spam. ~Carn - var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. - -/obj/machinery/hologram/holopad/attackby(obj/item/I as obj, user as mob) - if(computer_deconstruction_screwdriver(user, I)) - return - else - attack_hand(user) - return - -/obj/machinery/hologram/holopad/attack_hand(var/mob/living/carbon/human/user) //Carn: Hologram requests. - if(!istype(user)) - return - if(tgui_alert(user,"Would you like to request an AI's presence?","Request AI",list("Yes","No")) == "Yes") - if(last_request + 200 < world.time) //don't spam the AI with requests you jerk! - last_request = world.time - to_chat(user, "You request an AI's presence.") - var/area/area = get_area(src) - for(var/mob/living/silicon/ai/AI in living_mob_list) - if(!AI.client) continue - to_chat(AI, "Your presence is requested at \the [area].") - else - to_chat(user, "A request for AI presence was already sent recently.") - -/obj/machinery/hologram/holopad/attack_ai(mob/living/silicon/ai/user) - if(!istype(user)) - return - /*There are pretty much only three ways to interact here. - I don't need to check for client since they're clicking on an object. - This may change in the future but for now will suffice.*/ - if(user.eyeobj.loc != src.loc)//Set client eye on the object if it's not already. - user.eyeobj.setLoc(get_turf(src)) - else if(!masters[user])//If there is no hologram, possibly make one. - activate_holo(user) - else//If there is a hologram, remove it. - clear_holo(user) - return - -/obj/machinery/hologram/holopad/proc/activate_holo(mob/living/silicon/ai/user) - if(!(stat & NOPOWER) && user.eyeobj.loc == src.loc)//If the projector has power and client eye is on it - if(user.holo) - to_chat(user, "ERROR: Image feed in progress.") - return - create_holo(user)//Create one. - visible_message("A holographic image of [user] flicks to life right before your eyes!") - else - to_chat(user, "ERROR: Unable to project hologram.") - return - -/*This is the proc for special two-way communication between AI and holopad/people talking near holopad. -For the other part of the code, check silicon say.dm. Particularly robot talk.*/ -/obj/machinery/hologram/holopad/hear_talk(mob/M, list/message_pieces, verb) - if(M && LAZYLEN(masters)) - for(var/mob/living/silicon/ai/master in masters) - if(masters[master] && M != master) - master.relay_speech(M, message_pieces, verb) - -/obj/machinery/hologram/holopad/see_emote(mob/living/M, text) - if(M) - for(var/mob/living/silicon/ai/master in masters) - //var/name_used = M.GetVoice() - var/rendered = "Holopad received, [text]" - //The lack of name_used is needed, because message already contains a name. This is needed for simple mobs to emote properly. - master.show_message(rendered, 2) - return - -/obj/machinery/hologram/holopad/show_message(msg, type, alt, alt_type) - for(var/mob/living/silicon/ai/master in masters) - var/rendered = "Holopad received, [msg]" - master.show_message(rendered, type) - return - -/obj/machinery/hologram/holopad/proc/create_holo(mob/living/silicon/ai/A, turf/T = loc) - var/obj/effect/overlay/aiholo/hologram = new(T)//Spawn a blank effect at the location. //VOREStation Edit to specific type for adding vars - hologram.master = A //VOREStation Edit: So you can reference the master AI from in the hologram procs - hologram.icon = A.holo_icon - hologram.pixel_x = 16 - round(A.holo_icon.Width() / 2) //VOREStation Edit: centers the hologram on the tile - //hologram.mouse_opacity = 0//So you can't click on it. //VOREStation Removal - hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. - hologram.anchored = TRUE//So space wind cannot drag it. - hologram.name = "[A.name] (Hologram)"//If someone decides to right click. - - if(!isnull(color)) - hologram.color = color - else - hologram.color = A.holo_color - - if(hologram.color) //hologram lighting - hologram.set_light(2,1,hologram.color) - else - hologram.set_light(2) - - masters[A] = hologram - set_light(2) //pad lighting - icon_state = "holopad1" - flick("holopadload", src) //VOREStation Add - A.holo = src - if(LAZYLEN(masters)) - START_MACHINE_PROCESSING(src) - return 1 - -/obj/machinery/hologram/holopad/proc/clear_holo(mob/living/silicon/ai/user) - if(user.holo == src) - user.holo = null - qdel(masters[user])//Get rid of user's hologram - masters -= user //Discard AI from the list of those who use holopad - if(!masters.len)//If no users left - set_light(0) //pad lighting (hologram lighting will be handled automatically since its owner was deleted) - icon_state = "holopad0" - return 1 - -/obj/machinery/hologram/holopad/process() - for (var/mob/living/silicon/ai/master in masters) - var/active_ai = (master && !master.stat && master.client && master.eyeobj)//If there is an AI attached, it's not incapacitated, it has a client, and the client eye is centered on the projector. - if((stat & NOPOWER) || !active_ai) - clear_holo(master) - continue - - use_power(power_per_hologram) - if(..() == PROCESS_KILL && !LAZYLEN(masters)) - return PROCESS_KILL - -/obj/machinery/hologram/holopad/proc/move_hologram(mob/living/silicon/ai/user) - if(masters[user]) - /*VOREStation Removal, using our own code - step_to(masters[user], user.eyeobj) // So it turns. - var/obj/effect/overlay/H = masters[user] - H.loc = get_turf(user.eyeobj) - masters[user] = H - */ - //VOREStation Add - Solid mass holovore tracking stuff - var/obj/effect/overlay/aiholo/H = masters[user] - if(H.bellied) - walk_to(H, user.eyeobj) //Walk-to respects obstacles - else - walk_towards(H, user.eyeobj) //Walk-towards does not - //Hologram left the screen (got stuck on a wall or something) - if(get_dist(H, user.eyeobj) > world.view) - clear_holo(user) - //VOREStation Add End - if((HOLOPAD_MODE == RANGE_BASED && (get_dist(H, src) > holo_range))) - clear_holo(user) - - if(HOLOPAD_MODE == AREA_BASED) - var/area/holopad_area = get_area(src) - var/area/hologram_area = get_area(H) - - if(!(hologram_area in holopad_area)) - clear_holo(user) - - return 1 - -/* - * Hologram - */ - -/obj/machinery/hologram - icon = 'icons/obj/stationobjs_vr.dmi' //VOREStation Edit - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 5 - active_power_usage = 100 - -//Destruction procs. -/obj/machinery/hologram/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - if(2.0) - if(prob(50)) - qdel(src) - if(3.0) - if(prob(5)) - qdel(src) - return - -/obj/machinery/hologram/holopad/Destroy() - for (var/mob/living/silicon/ai/master in masters) - clear_holo(master) - return ..() - -/* -Holographic project of everything else. - -/mob/verb/hologram_test() - set name = "Hologram Debug New" - set category = "CURRENT DEBUG" - - var/obj/effect/overlay/hologram = new(loc)//Spawn a blank effect at the location. - var/icon/flat_icon = icon(getFlatIcon(src,0))//Need to make sure it's a new icon so the old one is not reused. - flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish. - flat_icon.ChangeOpacity(0.5)//Make it half transparent. - var/input = input(usr, "Select what icon state to use in effect.",,"") - if(input) - var/icon/alpha_mask = new('icons/effects/effects.dmi', "[input]") - flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. - hologram.icon = flat_icon - - to_world("Your icon should appear now.") - return -*/ - -/* - * Other Stuff: Is this even used? - */ -/obj/machinery/hologram/projector - name = "hologram projector" - desc = "It makes a hologram appear...with magnets or something..." - icon = 'icons/obj/stationobjs_vr.dmi' //VOREStation Edit - icon_state = "hologram0" - - -#undef RANGE_BASED -#undef AREA_BASED -#undef HOLOPAD_PASSIVE_POWER_USAGE -#undef HOLOGRAM_POWER_USAGE +/* Holograms! + * Contains: + * Holopad + * Hologram + * Other stuff + */ + +/* +Revised. Original based on space ninja hologram code. Which is also mine. /N +How it works: +AI clicks on holopad in camera view. View centers on holopad. +AI clicks again on the holopad to display a hologram. Hologram stays as long as AI is looking at the pad and it (the hologram) is in range of the pad. +AI can use the directional keys to move the hologram around, provided the above conditions are met and the AI in question is the holopad's master. +Only one AI may project from a holopad at any given time. +AI may cancel the hologram at any time by clicking on the holopad once more. + +Possible to do for anyone motivated enough: + Give an AI variable for different hologram icons. + Itegrate EMP effect to disable the unit. +*/ + +/* + * Holopad + */ +#define HOLOPAD_PASSIVE_POWER_USAGE 1 +#define HOLOGRAM_POWER_USAGE 2 +#define RANGE_BASED 4 +#define AREA_BASED 6 + +var/const/HOLOPAD_MODE = RANGE_BASED + +/obj/machinery/hologram/holopad + name = "\improper AI holopad" + desc = "It's a floor-mounted device for projecting holographic images. It is activated remotely." + icon_state = "holopad0" + show_messages = 1 + circuit = /obj/item/weapon/circuitboard/holopad + plane = TURF_PLANE + layer = ABOVE_TURF_LAYER + var/power_per_hologram = 500 //per usage per hologram + idle_power_usage = 5 + use_power = USE_POWER_IDLE + var/list/mob/living/silicon/ai/masters = new() //List of AIs that use the holopad + var/last_request = 0 //to prevent request spam. ~Carn + var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. + +/obj/machinery/hologram/holopad/attackby(obj/item/I as obj, user as mob) + if(computer_deconstruction_screwdriver(user, I)) + return + else + attack_hand(user) + return + +/obj/machinery/hologram/holopad/attack_hand(var/mob/living/carbon/human/user) //Carn: Hologram requests. + if(!istype(user)) + return + if(tgui_alert(user,"Would you like to request an AI's presence?","Request AI",list("Yes","No")) == "Yes") + if(last_request + 200 < world.time) //don't spam the AI with requests you jerk! + last_request = world.time + to_chat(user, "You request an AI's presence.") + var/area/area = get_area(src) + for(var/mob/living/silicon/ai/AI in living_mob_list) + if(!AI.client) continue + to_chat(AI, "Your presence is requested at \the [area].") + else + to_chat(user, "A request for AI presence was already sent recently.") + +/obj/machinery/hologram/holopad/attack_ai(mob/living/silicon/ai/user) + if(!istype(user)) + return + /*There are pretty much only three ways to interact here. + I don't need to check for client since they're clicking on an object. + This may change in the future but for now will suffice.*/ + if(user.eyeobj.loc != src.loc)//Set client eye on the object if it's not already. + user.eyeobj.setLoc(get_turf(src)) + else if(!masters[user])//If there is no hologram, possibly make one. + activate_holo(user) + else//If there is a hologram, remove it. + clear_holo(user) + return + +/obj/machinery/hologram/holopad/proc/activate_holo(mob/living/silicon/ai/user) + if(!(stat & NOPOWER) && user.eyeobj.loc == src.loc)//If the projector has power and client eye is on it + if(user.holo) + to_chat(user, "ERROR: Image feed in progress.") + return + create_holo(user)//Create one. + visible_message("A holographic image of [user] flicks to life right before your eyes!") + else + to_chat(user, "ERROR: Unable to project hologram.") + return + +/*This is the proc for special two-way communication between AI and holopad/people talking near holopad. +For the other part of the code, check silicon say.dm. Particularly robot talk.*/ +/obj/machinery/hologram/holopad/hear_talk(mob/M, list/message_pieces, verb) + if(M && LAZYLEN(masters)) + for(var/mob/living/silicon/ai/master in masters) + if(masters[master] && M != master) + master.relay_speech(M, message_pieces, verb) + +/obj/machinery/hologram/holopad/see_emote(mob/living/M, text) + if(M) + for(var/mob/living/silicon/ai/master in masters) + //var/name_used = M.GetVoice() + var/rendered = "Holopad received, [text]" + //The lack of name_used is needed, because message already contains a name. This is needed for simple mobs to emote properly. + master.show_message(rendered, 2) + return + +/obj/machinery/hologram/holopad/show_message(msg, type, alt, alt_type) + for(var/mob/living/silicon/ai/master in masters) + var/rendered = "Holopad received, [msg]" + master.show_message(rendered, type) + return + +/obj/machinery/hologram/holopad/proc/create_holo(mob/living/silicon/ai/A, turf/T = loc) + var/obj/effect/overlay/aiholo/hologram = new(T)//Spawn a blank effect at the location. //VOREStation Edit to specific type for adding vars + hologram.master = A //VOREStation Edit: So you can reference the master AI from in the hologram procs + hologram.icon = A.holo_icon + hologram.pixel_x = 16 - round(A.holo_icon.Width() / 2) //VOREStation Edit: centers the hologram on the tile + //hologram.mouse_opacity = 0//So you can't click on it. //VOREStation Removal + hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. + hologram.anchored = TRUE//So space wind cannot drag it. + hologram.name = "[A.name] (Hologram)"//If someone decides to right click. + + if(!isnull(color)) + hologram.color = color + else + hologram.color = A.holo_color + + if(hologram.color) //hologram lighting + hologram.set_light(2,1,hologram.color) + else + hologram.set_light(2) + + masters[A] = hologram + set_light(2) //pad lighting + icon_state = "holopad1" + flick("holopadload", src) //VOREStation Add + A.holo = src + if(LAZYLEN(masters)) + START_MACHINE_PROCESSING(src) + return 1 + +/obj/machinery/hologram/holopad/proc/clear_holo(mob/living/silicon/ai/user) + if(user.holo == src) + user.holo = null + qdel(masters[user])//Get rid of user's hologram + masters -= user //Discard AI from the list of those who use holopad + if(!masters.len)//If no users left + set_light(0) //pad lighting (hologram lighting will be handled automatically since its owner was deleted) + icon_state = "holopad0" + return 1 + +/obj/machinery/hologram/holopad/process() + for (var/mob/living/silicon/ai/master in masters) + var/active_ai = (master && !master.stat && master.client && master.eyeobj)//If there is an AI attached, it's not incapacitated, it has a client, and the client eye is centered on the projector. + if((stat & NOPOWER) || !active_ai) + clear_holo(master) + continue + + use_power(power_per_hologram) + if(..() == PROCESS_KILL && !LAZYLEN(masters)) + return PROCESS_KILL + +/obj/machinery/hologram/holopad/proc/move_hologram(mob/living/silicon/ai/user) + if(masters[user]) + /*VOREStation Removal, using our own code + step_to(masters[user], user.eyeobj) // So it turns. + var/obj/effect/overlay/H = masters[user] + H.loc = get_turf(user.eyeobj) + masters[user] = H + */ + //VOREStation Add - Solid mass holovore tracking stuff + var/obj/effect/overlay/aiholo/H = masters[user] + if(H.bellied) + walk_to(H, user.eyeobj) //Walk-to respects obstacles + else + walk_towards(H, user.eyeobj) //Walk-towards does not + //Hologram left the screen (got stuck on a wall or something) + if(get_dist(H, user.eyeobj) > world.view) + clear_holo(user) + //VOREStation Add End + if((HOLOPAD_MODE == RANGE_BASED && (get_dist(H, src) > holo_range))) + clear_holo(user) + + if(HOLOPAD_MODE == AREA_BASED) + var/area/holopad_area = get_area(src) + var/area/hologram_area = get_area(H) + + if(!(hologram_area in holopad_area)) + clear_holo(user) + + return 1 + +/* + * Hologram + */ + +/obj/machinery/hologram + icon = 'icons/obj/stationobjs_vr.dmi' //VOREStation Edit + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 5 + active_power_usage = 100 + +//Destruction procs. +/obj/machinery/hologram/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + if(2.0) + if(prob(50)) + qdel(src) + if(3.0) + if(prob(5)) + qdel(src) + return + +/obj/machinery/hologram/holopad/Destroy() + for (var/mob/living/silicon/ai/master in masters) + clear_holo(master) + return ..() + +/* +Holographic project of everything else. + +/mob/verb/hologram_test() + set name = "Hologram Debug New" + set category = "CURRENT DEBUG" + + var/obj/effect/overlay/hologram = new(loc)//Spawn a blank effect at the location. + var/icon/flat_icon = icon(getFlatIcon(src,0))//Need to make sure it's a new icon so the old one is not reused. + flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish. + flat_icon.ChangeOpacity(0.5)//Make it half transparent. + var/input = input(usr, "Select what icon state to use in effect.",,"") + if(input) + var/icon/alpha_mask = new('icons/effects/effects.dmi', "[input]") + flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. + hologram.icon = flat_icon + + to_world("Your icon should appear now.") + return +*/ + +/* + * Other Stuff: Is this even used? + */ +/obj/machinery/hologram/projector + name = "hologram projector" + desc = "It makes a hologram appear...with magnets or something..." + icon = 'icons/obj/stationobjs_vr.dmi' //VOREStation Edit + icon_state = "hologram0" + + +#undef RANGE_BASED +#undef AREA_BASED +#undef HOLOPAD_PASSIVE_POWER_USAGE +#undef HOLOGRAM_POWER_USAGE diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index 52b313497a9..461b80cf633 100755 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -1,150 +1,150 @@ -/obj/machinery/igniter - name = "igniter" - desc = "It's useful for igniting flammable items." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "igniter1" - var/id = null - var/on = 1.0 - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/igniter/attack_ai(mob/user as mob) - return attack_hand(user) - -/obj/machinery/igniter/attack_hand(mob/user as mob) - if(..()) - return - add_fingerprint(user) - - use_power(50) - on = !(on) - icon_state = text("igniter[]", on) - return - -/obj/machinery/igniter/process() //ugh why is this even in process()? - if(on && !(stat & NOPOWER)) - var/turf/location = src.loc - if(isturf(location)) - location.hotspot_expose(1000,500,1) - return 1 - -/obj/machinery/igniter/New() - ..() - icon_state = "igniter[on]" - -/obj/machinery/igniter/power_change() - ..() - if(!(stat & NOPOWER)) - icon_state = "igniter[on]" - else - icon_state = "igniter0" - -// Wall mounted remote-control igniter. - -/obj/machinery/sparker - name = "Mounted igniter" - desc = "A wall-mounted ignition device." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "migniter" - layer = ABOVE_WINDOW_LAYER - var/id = null - var/disable = 0 - var/last_spark = 0 - var/base_state = "migniter" - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/sparker/New() - ..() - -/obj/machinery/sparker/power_change() - ..() - if(!(stat & NOPOWER) && disable == 0) - - icon_state = "[base_state]" -// sd_SetLuminosity(2) - else - icon_state = "[base_state]-p" -// sd_SetLuminosity(0) - -/obj/machinery/sparker/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.has_tool_quality(TOOL_SCREWDRIVER)) - add_fingerprint(user) - disable = !disable - playsound(src, W.usesound, 50, 1) - if(disable) - user.visible_message("[user] has disabled the [src]!", "You disable the connection to the [src].") - icon_state = "[base_state]-d" - if(!disable) - user.visible_message("[user] has reconnected the [src]!", "You fix the connection to the [src].") - if(powered()) - icon_state = "[base_state]" - else - icon_state = "[base_state]-p" - -/obj/machinery/sparker/attack_ai() - if(anchored) - return ignite() - else - return - -/obj/machinery/sparker/proc/ignite() - if(!(powered())) - return - - if((disable) || (last_spark && world.time < last_spark + 50)) - return - - flick("[base_state]-spark", src) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(2, 1, src) - s.start() - last_spark = world.time - use_power(1000) - var/turf/location = src.loc - if(isturf(location)) - location.hotspot_expose(1000,500,1) - return 1 - -/obj/machinery/sparker/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - ignite() - ..(severity) - -/obj/machinery/button/ignition - name = "ignition switch" - desc = "A remote control switch for a mounted igniter." - -/obj/machinery/button/ignition/attack_hand(mob/user as mob) - - if(..()) - return - - use_power(5) - - active = 1 - icon_state = "launcheract" - - for(var/obj/machinery/sparker/M in machines) - if(M.id == id) - spawn(0) - M.ignite() - - for(var/obj/machinery/igniter/M in machines) - if(M.id == id) - use_power(50) - M.on = !(M.on) - M.icon_state = text("igniter[]", M.on) - - sleep(50) - - icon_state = "launcherbtt" - active = 0 - +/obj/machinery/igniter + name = "igniter" + desc = "It's useful for igniting flammable items." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "igniter1" + var/id = null + var/on = 1.0 + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/igniter/attack_ai(mob/user as mob) + return attack_hand(user) + +/obj/machinery/igniter/attack_hand(mob/user as mob) + if(..()) + return + add_fingerprint(user) + + use_power(50) + on = !(on) + icon_state = text("igniter[]", on) + return + +/obj/machinery/igniter/process() //ugh why is this even in process()? + if(on && !(stat & NOPOWER)) + var/turf/location = src.loc + if(isturf(location)) + location.hotspot_expose(1000,500,1) + return 1 + +/obj/machinery/igniter/New() + ..() + icon_state = "igniter[on]" + +/obj/machinery/igniter/power_change() + ..() + if(!(stat & NOPOWER)) + icon_state = "igniter[on]" + else + icon_state = "igniter0" + +// Wall mounted remote-control igniter. + +/obj/machinery/sparker + name = "Mounted igniter" + desc = "A wall-mounted ignition device." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "migniter" + layer = ABOVE_WINDOW_LAYER + var/id = null + var/disable = 0 + var/last_spark = 0 + var/base_state = "migniter" + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/sparker/New() + ..() + +/obj/machinery/sparker/power_change() + ..() + if(!(stat & NOPOWER) && disable == 0) + + icon_state = "[base_state]" +// sd_SetLuminosity(2) + else + icon_state = "[base_state]-p" +// sd_SetLuminosity(0) + +/obj/machinery/sparker/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + add_fingerprint(user) + disable = !disable + playsound(src, W.usesound, 50, 1) + if(disable) + user.visible_message("[user] has disabled the [src]!", "You disable the connection to the [src].") + icon_state = "[base_state]-d" + if(!disable) + user.visible_message("[user] has reconnected the [src]!", "You fix the connection to the [src].") + if(powered()) + icon_state = "[base_state]" + else + icon_state = "[base_state]-p" + +/obj/machinery/sparker/attack_ai() + if(anchored) + return ignite() + else + return + +/obj/machinery/sparker/proc/ignite() + if(!(powered())) + return + + if((disable) || (last_spark && world.time < last_spark + 50)) + return + + flick("[base_state]-spark", src) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(2, 1, src) + s.start() + last_spark = world.time + use_power(1000) + var/turf/location = src.loc + if(isturf(location)) + location.hotspot_expose(1000,500,1) + return 1 + +/obj/machinery/sparker/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + ignite() + ..(severity) + +/obj/machinery/button/ignition + name = "ignition switch" + desc = "A remote control switch for a mounted igniter." + +/obj/machinery/button/ignition/attack_hand(mob/user as mob) + + if(..()) + return + + use_power(5) + + active = 1 + icon_state = "launcheract" + + for(var/obj/machinery/sparker/M in machines) + if(M.id == id) + spawn(0) + M.ignite() + + for(var/obj/machinery/igniter/M in machines) + if(M.id == id) + use_power(50) + M.on = !(M.on) + M.icon_state = text("igniter[]", M.on) + + sleep(50) + + icon_state = "launcherbtt" + active = 0 + return \ No newline at end of file diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm index 51a1f3fb981..603618d1f31 100644 --- a/code/game/machinery/iv_drip.dm +++ b/code/game/machinery/iv_drip.dm @@ -1,185 +1,185 @@ -/obj/machinery/iv_drip - name = "\improper IV drip" - desc = "Helpful for giving someone blood! Or taking it away. It giveth, it taketh." - icon = 'icons/obj/iv_drip.dmi' - anchored = FALSE - density = FALSE - - -/obj/machinery/iv_drip/var/mob/living/carbon/human/attached = null -/obj/machinery/iv_drip/var/mode = 1 // 1 is injecting, 0 is taking blood. -/obj/machinery/iv_drip/var/obj/item/weapon/reagent_containers/beaker = null - -/obj/machinery/iv_drip/update_icon() - if(attached) - icon_state = "hooked" - else - icon_state = "" - - cut_overlays() - - if(beaker) - var/datum/reagents/reagents = beaker.reagents - if(reagents.total_volume) - var/image/filling = image('icons/obj/iv_drip.dmi', src, "reagent") - - var/percent = round((reagents.total_volume / beaker.volume) * 100) - switch(percent) - if(0 to 9) filling.icon_state = "reagent0" - if(10 to 24) filling.icon_state = "reagent10" - if(25 to 49) filling.icon_state = "reagent25" - if(50 to 74) filling.icon_state = "reagent50" - if(75 to 79) filling.icon_state = "reagent75" - if(80 to 90) filling.icon_state = "reagent80" - if(91 to INFINITY) filling.icon_state = "reagent100" - - filling.icon += reagents.get_color() - add_overlay(filling) - -/obj/machinery/iv_drip/MouseDrop(over_object, src_location, over_location) - ..() - if(!isliving(usr)) - return - - if(attached) - visible_message("[attached] is detached from \the [src]") - attached = null - update_icon() - return - - if(in_range(src, usr) && ishuman(over_object) && get_dist(over_object, src) <= 1) - visible_message("[usr] attaches \the [src] to \the [over_object].") - attached = over_object - update_icon() - - -/obj/machinery/iv_drip/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/reagent_containers)) - if(!isnull(beaker)) - to_chat(user, "There is already a reagent container loaded!") - return - - user.drop_item() - W.loc = src - beaker = W - to_chat(user, "You attach \the [W] to \the [src].") - update_icon() - return - - if(W.has_tool_quality(TOOL_SCREWDRIVER)) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You start to dismantle the IV drip.") - if(do_after(user, 15)) - to_chat(user, "You dismantle the IV drip.") - new /obj/item/stack/rods(src.loc, 6) - if(beaker) - beaker.loc = get_turf(src) - beaker = null - qdel(src) - return - else - return ..() - - -/obj/machinery/iv_drip/process() - set background = 1 - - if(attached) - - if(!(get_dist(src, attached) <= 1 && isturf(attached.loc))) - visible_message("The needle is ripped out of [attached], doesn't that hurt?") - attached:apply_damage(3, BRUTE, pick("r_arm", "l_arm")) - attached = null - update_icon() - return - - if(attached && beaker) - // Give blood - if(mode) - if(beaker.volume > 0) - var/transfer_amount = REM - if(istype(beaker, /obj/item/weapon/reagent_containers/blood)) - // speed up transfer on blood packs - transfer_amount = 4 - beaker.reagents.trans_to_mob(attached, transfer_amount, CHEM_BLOOD) - update_icon() - - // Take blood - else - var/amount = beaker.reagents.maximum_volume - beaker.reagents.total_volume - amount = min(amount, 4) - // If the beaker is full, ping - if(amount == 0) - if(prob(5)) - visible_message("\The [src] pings.") - return - - var/mob/living/carbon/human/T = attached - - if(!istype(T)) - return - if(!T.dna) - return - if(NOCLONE in T.mutations) - return - - if(!T.should_have_organ(O_HEART)) - return - - // If the human is losing too much blood, beep. - if(T.vessel.get_reagent_amount("blood") < T.species.blood_volume*T.species.blood_level_safe) - visible_message("\The [src] beeps loudly.") - - var/datum/reagent/B = T.take_blood(beaker,amount) - - if(B) - beaker.reagents.reagent_list |= B - beaker.reagents.update_total() - beaker.on_reagent_change() - beaker.reagents.handle_reactions() - update_icon() - -/obj/machinery/iv_drip/attack_hand(mob/user as mob) - if(beaker) - beaker.loc = get_turf(src) - beaker = null - update_icon() - else - return ..() - - -/obj/machinery/iv_drip/verb/toggle_mode() - set category = "Object" - set name = "Toggle Mode" - set src in view(1) - - if(!istype(usr, /mob/living)) - to_chat(usr, "You can't do that.") - return - - if(usr.stat) - return - - mode = !mode - to_chat(usr, "The IV drip is now [mode ? "injecting" : "taking blood"].") - -/obj/machinery/iv_drip/examine(mob/user) - . = ..() - - if(get_dist(user, src) <= 2) - . += "The IV drip is [mode ? "injecting" : "taking blood"]." - - if(beaker) - if(beaker.reagents?.reagent_list?.len) - . += "Attached is \a [beaker] with [beaker.reagents.total_volume] units of liquid." - else - . += "Attached is an empty [beaker]." - else - . += "No chemicals are attached." - - . += "[attached ? attached : "No one"] is attached." - -/obj/machinery/iv_drip/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSTABLE)) //allow bullets, beams, thrown objects, mice, drones, and the like through. - return TRUE - return ..() +/obj/machinery/iv_drip + name = "\improper IV drip" + desc = "Helpful for giving someone blood! Or taking it away. It giveth, it taketh." + icon = 'icons/obj/iv_drip.dmi' + anchored = FALSE + density = FALSE + + +/obj/machinery/iv_drip/var/mob/living/carbon/human/attached = null +/obj/machinery/iv_drip/var/mode = 1 // 1 is injecting, 0 is taking blood. +/obj/machinery/iv_drip/var/obj/item/weapon/reagent_containers/beaker = null + +/obj/machinery/iv_drip/update_icon() + if(attached) + icon_state = "hooked" + else + icon_state = "" + + cut_overlays() + + if(beaker) + var/datum/reagents/reagents = beaker.reagents + if(reagents.total_volume) + var/image/filling = image('icons/obj/iv_drip.dmi', src, "reagent") + + var/percent = round((reagents.total_volume / beaker.volume) * 100) + switch(percent) + if(0 to 9) filling.icon_state = "reagent0" + if(10 to 24) filling.icon_state = "reagent10" + if(25 to 49) filling.icon_state = "reagent25" + if(50 to 74) filling.icon_state = "reagent50" + if(75 to 79) filling.icon_state = "reagent75" + if(80 to 90) filling.icon_state = "reagent80" + if(91 to INFINITY) filling.icon_state = "reagent100" + + filling.icon += reagents.get_color() + add_overlay(filling) + +/obj/machinery/iv_drip/MouseDrop(over_object, src_location, over_location) + ..() + if(!isliving(usr)) + return + + if(attached) + visible_message("[attached] is detached from \the [src]") + attached = null + update_icon() + return + + if(in_range(src, usr) && ishuman(over_object) && get_dist(over_object, src) <= 1) + visible_message("[usr] attaches \the [src] to \the [over_object].") + attached = over_object + update_icon() + + +/obj/machinery/iv_drip/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/reagent_containers)) + if(!isnull(beaker)) + to_chat(user, "There is already a reagent container loaded!") + return + + user.drop_item() + W.loc = src + beaker = W + to_chat(user, "You attach \the [W] to \the [src].") + update_icon() + return + + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(src, W.usesound, 50, 1) + to_chat(user, "You start to dismantle the IV drip.") + if(do_after(user, 15)) + to_chat(user, "You dismantle the IV drip.") + new /obj/item/stack/rods(src.loc, 6) + if(beaker) + beaker.loc = get_turf(src) + beaker = null + qdel(src) + return + else + return ..() + + +/obj/machinery/iv_drip/process() + set background = 1 + + if(attached) + + if(!(get_dist(src, attached) <= 1 && isturf(attached.loc))) + visible_message("The needle is ripped out of [attached], doesn't that hurt?") + attached:apply_damage(3, BRUTE, pick("r_arm", "l_arm")) + attached = null + update_icon() + return + + if(attached && beaker) + // Give blood + if(mode) + if(beaker.volume > 0) + var/transfer_amount = REM + if(istype(beaker, /obj/item/weapon/reagent_containers/blood)) + // speed up transfer on blood packs + transfer_amount = 4 + beaker.reagents.trans_to_mob(attached, transfer_amount, CHEM_BLOOD) + update_icon() + + // Take blood + else + var/amount = beaker.reagents.maximum_volume - beaker.reagents.total_volume + amount = min(amount, 4) + // If the beaker is full, ping + if(amount == 0) + if(prob(5)) + visible_message("\The [src] pings.") + return + + var/mob/living/carbon/human/T = attached + + if(!istype(T)) + return + if(!T.dna) + return + if(NOCLONE in T.mutations) + return + + if(!T.should_have_organ(O_HEART)) + return + + // If the human is losing too much blood, beep. + if(T.vessel.get_reagent_amount("blood") < T.species.blood_volume*T.species.blood_level_safe) + visible_message("\The [src] beeps loudly.") + + var/datum/reagent/B = T.take_blood(beaker,amount) + + if(B) + beaker.reagents.reagent_list |= B + beaker.reagents.update_total() + beaker.on_reagent_change() + beaker.reagents.handle_reactions() + update_icon() + +/obj/machinery/iv_drip/attack_hand(mob/user as mob) + if(beaker) + beaker.loc = get_turf(src) + beaker = null + update_icon() + else + return ..() + + +/obj/machinery/iv_drip/verb/toggle_mode() + set category = "Object" + set name = "Toggle Mode" + set src in view(1) + + if(!istype(usr, /mob/living)) + to_chat(usr, "You can't do that.") + return + + if(usr.stat) + return + + mode = !mode + to_chat(usr, "The IV drip is now [mode ? "injecting" : "taking blood"].") + +/obj/machinery/iv_drip/examine(mob/user) + . = ..() + + if(get_dist(user, src) <= 2) + . += "The IV drip is [mode ? "injecting" : "taking blood"]." + + if(beaker) + if(beaker.reagents?.reagent_list?.len) + . += "Attached is \a [beaker] with [beaker.reagents.total_volume] units of liquid." + else + . += "Attached is an empty [beaker]." + else + . += "No chemicals are attached." + + . += "[attached ? attached : "No one"] is attached." + +/obj/machinery/iv_drip/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSTABLE)) //allow bullets, beams, thrown objects, mice, drones, and the like through. + return TRUE + return ..() diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index 9a535161451..5340bd2dd5e 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -1,89 +1,89 @@ -// the light switch -// can have multiple per area -// can also operate on non-loc area through "otherarea" var -/obj/machinery/light_switch - name = "light switch" - desc = "It turns lights on and off. What are you, simple?" - icon = 'icons/obj/power_vr.dmi' // VOREStation Edit - icon_state = "light1" - layer = ABOVE_WINDOW_LAYER - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 10 - power_channel = LIGHT - blocks_emissive = FALSE - vis_flags = VIS_HIDE // They have an emissive that looks bad in openspace due to their wall-mounted nature - var/on = 1 - var/area/area = null - var/otherarea = null - var/image/overlay - -/obj/machinery/light_switch/Initialize() - . = ..() - - area = get_area(src) - - if(otherarea) - area = locate(text2path("/area/[otherarea]")) - - if(!name) - name = "light switch ([area.name])" - - on = area.lightswitch - update_icon() - -/obj/machinery/light_switch/Destroy() - area = null - overlay = null - return ..() - -/obj/machinery/light_switch/update_icon() - cut_overlays() - if(stat & NOPOWER) - icon_state = "light-p" - set_light(0) - else - icon_state = "light[on]" - set_light(2, 0.1, on ? "#82FF4C" : "#F86060") - . = list() - . += emissive_appearance(icon, "light[on]-overlay") - - return add_overlay(.) - - -/obj/machinery/light_switch/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "A light switch. It is [on? "on" : "off"]." - -/obj/machinery/light_switch/attack_hand(mob/user) - - on = !on - - area.lightswitch = on - area.update_icon() - playsound(src, 'sound/machines/button.ogg', 100, 1, 0) // VOREStation Edit - - for(var/obj/machinery/light_switch/L in area) - L.on = on - L.update_icon() - - area.power_change() - GLOB.lights_switched_on_roundstat++ - -/obj/machinery/light_switch/power_change() - - if(!otherarea) - if(powered(LIGHT)) - stat &= ~NOPOWER - else - stat |= NOPOWER - - update_icon() - -/obj/machinery/light_switch/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - power_change() +// the light switch +// can have multiple per area +// can also operate on non-loc area through "otherarea" var +/obj/machinery/light_switch + name = "light switch" + desc = "It turns lights on and off. What are you, simple?" + icon = 'icons/obj/power_vr.dmi' // VOREStation Edit + icon_state = "light1" + layer = ABOVE_WINDOW_LAYER + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 10 + power_channel = LIGHT + blocks_emissive = FALSE + vis_flags = VIS_HIDE // They have an emissive that looks bad in openspace due to their wall-mounted nature + var/on = 1 + var/area/area = null + var/otherarea = null + var/image/overlay + +/obj/machinery/light_switch/Initialize() + . = ..() + + area = get_area(src) + + if(otherarea) + area = locate(text2path("/area/[otherarea]")) + + if(!name) + name = "light switch ([area.name])" + + on = area.lightswitch + update_icon() + +/obj/machinery/light_switch/Destroy() + area = null + overlay = null + return ..() + +/obj/machinery/light_switch/update_icon() + cut_overlays() + if(stat & NOPOWER) + icon_state = "light-p" + set_light(0) + else + icon_state = "light[on]" + set_light(2, 0.1, on ? "#82FF4C" : "#F86060") + . = list() + . += emissive_appearance(icon, "light[on]-overlay") + + return add_overlay(.) + + +/obj/machinery/light_switch/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "A light switch. It is [on? "on" : "off"]." + +/obj/machinery/light_switch/attack_hand(mob/user) + + on = !on + + area.lightswitch = on + area.update_icon() + playsound(src, 'sound/machines/button.ogg', 100, 1, 0) // VOREStation Edit + + for(var/obj/machinery/light_switch/L in area) + L.on = on + L.update_icon() + + area.power_change() + GLOB.lights_switched_on_roundstat++ + +/obj/machinery/light_switch/power_change() + + if(!otherarea) + if(powered(LIGHT)) + stat &= ~NOPOWER + else + stat |= NOPOWER + + update_icon() + +/obj/machinery/light_switch/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + power_change() ..(severity) \ No newline at end of file diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index 00010da9ccf..d991eddae90 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -1,571 +1,571 @@ -/* -Overview: - Used to create objects that need a per step proc call. Default definition of 'New()' - stores a reference to src machine in global 'machines list'. Default definition - of 'Del' removes reference to src machine in global 'machines list'. - -Class Variables: - - power_init_complete (boolean) - Indicates that we have have registered our static power usage with the area. - - use_power (num) - current state of auto power use. - Possible Values: - USE_POWER_OFF:0 -- no auto power use - USE_POWER_IDLE:1 -- machine is using power at its idle power level - USE_POWER_ACTIVE:2 -- machine is using power at its active power level - - active_power_usage (num) - Value for the amount of power to use when in active power mode - - idle_power_usage (num) - Value for the amount of power to use when in idle power mode - - power_channel (num) - What channel to draw from when drawing power for power mode - Possible Values: - EQUIP:0 -- Equipment Channel - LIGHT:2 -- Lighting Channel - ENVIRON:3 -- Environment Channel - - component_parts (list) - A list of component parts of machine used by frame based machines. - - panel_open (num) - Whether the panel is open - - uid (num) - Unique id of machine across all machines. - - gl_uid (global num) - Next uid value in sequence - - stat (bitflag) - Machine status bit flags. - Possible bit flags: - BROKEN:1 -- Machine is broken - NOPOWER:2 -- No power is being supplied to machine. - POWEROFF:4 -- tbd - MAINT:8 -- machine is currently under going maintenance. - EMPED:16 -- temporary broken by EMP pulse - -Class Procs: - New() 'game/machinery/machine.dm' - - Destroy() 'game/machinery/machine.dm' - - get_power_usage() 'game/machinery/machinery_power.dm' - Returns the amount of power this machine uses every SSmachines cycle. - Default definition uses 'use_power', 'active_power_usage', 'idle_power_usage' - - powered(chan = CURRENT_CHANNEL) 'game/machinery/machinery_power.dm' - Checks to see if area that contains the object has power available for power - channel given in 'chan'. - - use_power_oneoff(amount, chan=CURRENT_CHANNEL) 'game/machinery/machinery_power.dm' - Deducts 'amount' from the power channel 'chan' of the area that contains the object. - - power_change() 'game/machinery/machinery_power.dm' - Called by the area that contains the object when ever that area under goes a - power state change (area runs out of power, or area channel is turned off). - - RefreshParts() 'game/machinery/machine.dm' - Called to refresh the variables in the machine that are contributed to by parts - contained in the component_parts list. (example: glass and material amounts for - the autolathe) - - Default definition does nothing. - - assign_uid() 'game/machinery/machine.dm' - Called by machine to assign a value to the uid variable. - - process() 'game/machinery/machine.dm' - Called by the 'master_controller' once per game tick for each machine that is listed in the 'machines' list. - - - Compiled by Aygar -*/ - -/obj/machinery - name = "machinery" - icon = 'icons/obj/stationobjs.dmi' - w_class = ITEMSIZE_NO_CONTAINER - layer = UNDER_JUNK_LAYER - - var/stat = 0 - var/emagged = 0 - var/use_power = USE_POWER_IDLE - //0 = dont run the auto - //1 = run auto, use idle - //2 = run auto, use active - var/idle_power_usage = 0 - var/active_power_usage = 0 - var/power_channel = EQUIP //EQUIP, ENVIRON or LIGHT - var/power_init_complete = FALSE - var/list/component_parts = null //list of all the parts used to build it, if made from certain kinds of frames. - var/uid - var/panel_open = FALSE - var/global/gl_uid = 1 - var/clicksound // sound played on succesful interface. Just put it in the list of vars at the start. - var/clickvol = 40 // volume - var/interact_offline = 0 // Can the machine be interacted with while de-powered. - var/obj/item/weapon/circuitboard/circuit = null - - // 0.0 - 1.0 multipler for prob() based on bullet structure damage - // So if this is 1.0 then a 100 damage bullet will always break this structure - // If this is 0.5 then a 50 damage bullet will break this structure 25% of the time - var/bullet_vulnerability = 0.25 - - var/speed_process = FALSE //If false, SSmachines. If true, SSfastprocess. - - blocks_emissive = EMISSIVE_BLOCK_GENERIC - -/obj/machinery/New(l, d=0) - ..() - if(isnum(d)) - set_dir(d) - if(ispath(circuit)) - circuit = new circuit(src) - -/obj/machinery/Initialize(var/mapload) - . = ..() - global.machines += src - if(ispath(circuit)) - circuit = new circuit(src) - if(!speed_process) - START_MACHINE_PROCESSING(src) - else - START_PROCESSING(SSfastprocess, src) - if(!mapload) - power_change() - -/obj/machinery/Destroy() - if(!speed_process) - STOP_MACHINE_PROCESSING(src) - else - STOP_PROCESSING(SSfastprocess, src) - global.machines -= src - if(component_parts) - for(var/atom/A in component_parts) - if(A.loc == src) // If the components are inside the machine, delete them. - qdel(A) - else // Otherwise we assume they were dropped to the ground during deconstruction, and were not removed from the component_parts list by deconstruction code. - warning("[A] was still in [src]'s component_parts when it was Destroy()'d") - component_parts.Cut() - component_parts = null - if(contents) // The same for contents. - for(var/atom/A in contents) - if(ishuman(A)) - var/mob/living/carbon/human/H = A - H.client.eye = H.client.mob - H.client.perspective = MOB_PERSPECTIVE - H.loc = src.loc - else - qdel(A) - return ..() - -/obj/machinery/process() // Steady power usage is handled separately. If you dont use process why are you here? - return PROCESS_KILL - -/obj/machinery/emp_act(severity) - if(use_power && stat == 0) - use_power(7500/severity) - - var/obj/effect/overlay/pulse2 = new /obj/effect/overlay(src.loc) - pulse2.icon = 'icons/effects/effects.dmi' - pulse2.icon_state = "empdisable" - pulse2.name = "emp sparks" - pulse2.anchored = TRUE - pulse2.set_dir(pick(cardinal)) - - spawn(10) - qdel(pulse2) - ..() - -/obj/machinery/ex_act(severity) - switch(severity) - if(1.0) - fall_apart(severity) - return - if(2.0) - if(prob(50)) - fall_apart(severity) - return - if(3.0) - if(prob(25)) - fall_apart(severity) - return - else - return - -/obj/machinery/vv_edit_var(var/var_name, var/new_value) - if(var_name == NAMEOF(src, use_power)) - update_use_power(new_value) - return TRUE - else if(var_name == NAMEOF(src, power_channel)) - update_power_channel(new_value) - return TRUE - else if(var_name == NAMEOF(src, idle_power_usage)) - update_idle_power_usage(new_value) - return TRUE - else if(var_name == NAMEOF(src, active_power_usage)) - update_active_power_usage(new_value) - return TRUE - return ..() - -/obj/machinery/proc/operable(var/additional_flags = 0) - return !inoperable(additional_flags) - -/obj/machinery/proc/inoperable(var/additional_flags = 0) - return (stat & (NOPOWER | BROKEN | additional_flags)) - -// Duplicate of below because we don't want to fuck around with CanUseTopic in TGUI -// TODO: Replace this with can_interact from /tg/ -/obj/machinery/tgui_status(mob/user) - if(!interact_offline && (stat & (NOPOWER | BROKEN))) - return STATUS_CLOSE - return ..() - -/obj/machinery/CanUseTopic(var/mob/user) - if(!interact_offline && (stat & (NOPOWER | BROKEN))) - return STATUS_CLOSE - return ..() - -/obj/machinery/CouldUseTopic(var/mob/user) - ..() - user.set_machine(src) - -/obj/machinery/CouldNotUseTopic(var/mob/user) - user.unset_machine() - -//////////////////////////////////////////////////////////////////////////////////////////// - -/obj/machinery/attack_ai(mob/user as mob) - if(isrobot(user)) - // For some reason attack_robot doesn't work - // This is to stop robots from using cameras to remotely control machines. - if(user.client && user.client.eye == user) - return attack_hand(user) - else - return attack_hand(user) - -/obj/machinery/attack_hand(mob/user as mob) - - if(inoperable(MAINT)) - return 1 - if(user.lying || user.stat) - return 1 - if(!user.IsAdvancedToolUser()) //Vorestation edit - to_chat(user, "You don't have the dexterity to do this!") - return 1 - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.getBrainLoss() >= 55) - visible_message("[H] stares cluelessly at [src].") - return 1 - else if(prob(H.getBrainLoss())) - to_chat(user, "You momentarily forget how to use [src].") - return 1 - - if(clicksound && istype(user, /mob/living/carbon)) - playsound(src, clicksound, clickvol) - - add_fingerprint(user) - - return ..() - -/obj/machinery/proc/RefreshParts() //Placeholder proc for machines that are built using frames. - return - -/obj/machinery/proc/assign_uid() - uid = gl_uid - gl_uid++ - -/obj/machinery/proc/state(var/msg) - for(var/mob/O in hearers(src, null)) - O.show_message("\icon[src][bicon(src)] [msg]", 2) - -/obj/machinery/proc/ping(text=null) - if(!text) - text = "\The [src] pings." - - state(text, "blue") - playsound(src, 'sound/machines/ping.ogg', 50, 0) - -/obj/machinery/proc/shock(mob/user, prb) - if(inoperable()) - return 0 - if(!prob(prb)) - return 0 - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() - if(electrocute_mob(user, get_area(src), src, 0.7)) - var/area/temp_area = get_area(src) - if(temp_area) - var/obj/machinery/power/apc/temp_apc = temp_area.get_apc() - - if(temp_apc && temp_apc.terminal && temp_apc.terminal.powernet) - temp_apc.terminal.powernet.trigger_warning() - if(user.stunned) - return 1 - return 0 - -/obj/machinery/proc/default_apply_parts() - var/obj/item/weapon/circuitboard/CB = circuit - if(!istype(CB)) - return - CB.apply_default_parts(src) - RefreshParts() - -/obj/machinery/proc/default_use_hicell() - var/obj/item/weapon/cell/C = locate(/obj/item/weapon/cell) in component_parts - if(C) - component_parts -= C - qdel(C) - C = new /obj/item/weapon/cell/high(src) - component_parts += C - RefreshParts() - return C - -/obj/machinery/proc/default_part_replacement(var/mob/user, var/obj/item/weapon/storage/part_replacer/R) - var/parts_replaced = FALSE - if(!istype(R)) - return 0 - if(!component_parts) - return 0 - to_chat(user, "Following parts detected in [src]:") - for(var/obj/item/C in component_parts) - to_chat(user, " [C.name]") - if(panel_open || !R.panel_req) - var/obj/item/weapon/circuitboard/CB = circuit - var/P - for(var/obj/item/A in component_parts) - for(var/T in CB.req_components) - if(ispath(A.type, T)) - P = T - break - for(var/obj/item/B in R.contents) - if(istype(B, P) && istype(A, P)) - if(B.get_rating() > A.get_rating()) - R.remove_from_storage(B, src) - R.handle_item_insertion(A, 1) - component_parts -= A - component_parts += B - B.loc = null - to_chat(user, "[A.name] replaced with [B.name].") - parts_replaced = TRUE - break - update_icon() - RefreshParts() - if(parts_replaced) - R.play_rped_sound() - return 1 - -// Default behavior for wrenching down machines. Supports both delay and instant modes. -/obj/machinery/proc/default_unfasten_wrench(var/mob/user, var/obj/item/W, var/time = 0) - if(!W.has_tool_quality(TOOL_WRENCH)) - return FALSE - if(panel_open) - return FALSE // Close panel first! - playsound(src, W.usesound, 50, 1) - var/actual_time = W.toolspeed * time - if(actual_time != 0) - user.visible_message( \ - "\The [user] begins [anchored ? "un" : ""]securing \the [src].", \ - "You start [anchored ? "un" : ""]securing \the [src].") - if(actual_time == 0 || do_after(user, actual_time, target = src)) - user.visible_message( \ - "\The [user] has [anchored ? "un" : ""]secured \the [src].", \ - "You [anchored ? "un" : ""]secure \the [src].") - anchored = !anchored - power_change() //Turn on or off the machine depending on the status of power in the new area. - update_icon() - return TRUE - -/obj/machinery/proc/default_deconstruction_crowbar(var/mob/user, var/obj/item/C) - if(!C.has_tool_quality(TOOL_CROWBAR)) - return 0 - if(!panel_open) - return 0 - . = dismantle() - -/obj/machinery/proc/default_deconstruction_screwdriver(var/mob/user, var/obj/item/S) - if(!S.has_tool_quality(TOOL_SCREWDRIVER)) - return 0 - playsound(src, S.usesound, 50, 1) - panel_open = !panel_open - to_chat(user, "You [panel_open ? "open" : "close"] the maintenance hatch of [src].") - update_icon() - return 1 - -/obj/machinery/proc/computer_deconstruction_screwdriver(var/mob/user, var/obj/item/S) - if(!S.has_tool_quality(TOOL_SCREWDRIVER)) - return 0 - if(!circuit) - return 0 - to_chat(user, "You start disconnecting the monitor.") - playsound(src, S.usesound, 50, 1) - if(do_after(user, 20 * S.toolspeed)) - if(stat & BROKEN) - to_chat(user, "The broken glass falls out.") - new /obj/item/weapon/material/shard(src.loc) - else - to_chat(user, "You disconnect the monitor.") - . = dismantle() - -/obj/machinery/proc/alarm_deconstruction_screwdriver(var/mob/user, var/obj/item/S) - if(!S.has_tool_quality(TOOL_SCREWDRIVER)) - return 0 - playsound(src, S.usesound, 50, 1) - panel_open = !panel_open - to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"]") - update_icon() - return 1 - -/obj/machinery/proc/alarm_deconstruction_wirecutters(var/mob/user, var/obj/item/W) - if(!W.has_tool_quality(TOOL_WIRECUTTER)) - return 0 - if(!panel_open) - return 0 - user.visible_message("[user] has cut the wires inside \the [src]!", "You have cut the wires inside \the [src].") - playsound(src, W.usesound, 50, 1) - new/obj/item/stack/cable_coil(get_turf(src), 5) - . = dismantle() - -/obj/machinery/proc/dismantle() - playsound(src, 'sound/items/Crowbar.ogg', 50, 1) - for(var/obj/I in contents) - if(istype(I,/obj/item/weapon/card/id)) - I.forceMove(src.loc) - - if(!circuit) - return 0 - var/obj/structure/frame/A = new /obj/structure/frame(src.loc) - var/obj/item/weapon/circuitboard/M = circuit - A.circuit = M - A.anchored = TRUE - A.frame_type = M.board_type - if(A.frame_type.circuit) - A.need_circuit = 0 - - if(A.frame_type.frame_class == FRAME_CLASS_ALARM || A.frame_type.frame_class == FRAME_CLASS_DISPLAY) - A.density = FALSE - else - A.density = TRUE - - if(A.frame_type.frame_class == FRAME_CLASS_MACHINE) - for(var/obj/D in component_parts) - D.forceMove(src.loc) - if(A.components) - A.components.Cut() - else - A.components = list() - component_parts = list() - A.check_components() - - if(A.frame_type.frame_class == FRAME_CLASS_ALARM) - A.state = FRAME_FASTENED - else if(A.frame_type.frame_class == FRAME_CLASS_COMPUTER || A.frame_type.frame_class == FRAME_CLASS_DISPLAY) - if(stat & BROKEN) - A.state = FRAME_WIRED - else - A.state = FRAME_PANELED - else - A.state = FRAME_WIRED - - A.set_dir(dir) - A.pixel_x = pixel_x - A.pixel_y = pixel_y - A.update_desc() - A.update_icon() - M.loc = null - M.deconstruct(src) - qdel(src) - return 1 - -/obj/machinery/bullet_act(obj/item/projectile/P, def_zone) - . = ..() - if(prob(P.get_structure_damage() * bullet_vulnerability)) - fall_apart() - -/** - * Like an angrier dismantle, where it destroys some of the parts and doesn't give you a frame - ** severity: Same severities as ex_act (so lower is more destructive) - ** scatter: If you want the parts to slide around 1 turf in random directions - */ -/obj/machinery/proc/fall_apart(var/severity = 3, var/scatter = TRUE) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - var/atom/droploc = drop_location() - if(!droploc || !contents.len) // not even a circuit? - playsound(src, 'sound/machines/machine_die_short.ogg') - spark_system.start() - qdel(spark_system) - qdel(src) - return - - var/list/surviving_parts = list() - // Deleting IDs is lame, unless this is like nuclear severity - if(severity != 1) - for(var/obj/item/weapon/card/id/I in contents) - surviving_parts |= I - - // May populate some items to throw around - if(!LAZYLEN(component_parts) && circuit) - circuit.apply_default_parts(src) - - var/survivability - switch(severity) - // No survivors - if(1) - survivability = 0 - - // 1 part survives - if(2) - survivability = 0 - var/atom/movable/picked_part = pick(contents) - if(istype(picked_part)) - surviving_parts |= picked_part - - // 50% of parts destroyed on average - if(3) - survivability = 50 - - // No parts destroyed, but you lose the frame - else - survivability = 100 - - for(var/atom/movable/P in contents) - if(prob(survivability)) - surviving_parts |= P - - if(circuit && severity >= 2) - var/datum/frame/frame_types/FT = circuit.board_type - if(istype(FT)) - // Some steel from the frame, but about half - surviving_parts += new /obj/item/stack/material/steel(null, max(1,round(FT.frame_size/2))) - // Two bits of cable, but not the 5 required to rebuild - surviving_parts += new /obj/item/stack/cable_coil(null, 1) - surviving_parts += new /obj/item/stack/cable_coil(null, 1) - - for(var/atom/movable/A as anything in surviving_parts) - A.forceMove(droploc) - if(scatter && isturf(droploc)) - var/turf/T = droploc - A.Move(get_step(T, pick(alldirs))) - - playsound(src, 'sound/machines/machine_die_short.ogg') - spark_system.start() - qdel(spark_system) - qdel(src) - -/datum/proc/apply_visual(mob/M) - M.sight = 0 //Just reset their mesons and stuff so they can't use them, by default. - return - -/datum/proc/remove_visual(mob/M) - return +/* +Overview: + Used to create objects that need a per step proc call. Default definition of 'New()' + stores a reference to src machine in global 'machines list'. Default definition + of 'Del' removes reference to src machine in global 'machines list'. + +Class Variables: + + power_init_complete (boolean) + Indicates that we have have registered our static power usage with the area. + + use_power (num) + current state of auto power use. + Possible Values: + USE_POWER_OFF:0 -- no auto power use + USE_POWER_IDLE:1 -- machine is using power at its idle power level + USE_POWER_ACTIVE:2 -- machine is using power at its active power level + + active_power_usage (num) + Value for the amount of power to use when in active power mode + + idle_power_usage (num) + Value for the amount of power to use when in idle power mode + + power_channel (num) + What channel to draw from when drawing power for power mode + Possible Values: + EQUIP:0 -- Equipment Channel + LIGHT:2 -- Lighting Channel + ENVIRON:3 -- Environment Channel + + component_parts (list) + A list of component parts of machine used by frame based machines. + + panel_open (num) + Whether the panel is open + + uid (num) + Unique id of machine across all machines. + + gl_uid (global num) + Next uid value in sequence + + stat (bitflag) + Machine status bit flags. + Possible bit flags: + BROKEN:1 -- Machine is broken + NOPOWER:2 -- No power is being supplied to machine. + POWEROFF:4 -- tbd + MAINT:8 -- machine is currently under going maintenance. + EMPED:16 -- temporary broken by EMP pulse + +Class Procs: + New() 'game/machinery/machine.dm' + + Destroy() 'game/machinery/machine.dm' + + get_power_usage() 'game/machinery/machinery_power.dm' + Returns the amount of power this machine uses every SSmachines cycle. + Default definition uses 'use_power', 'active_power_usage', 'idle_power_usage' + + powered(chan = CURRENT_CHANNEL) 'game/machinery/machinery_power.dm' + Checks to see if area that contains the object has power available for power + channel given in 'chan'. + + use_power_oneoff(amount, chan=CURRENT_CHANNEL) 'game/machinery/machinery_power.dm' + Deducts 'amount' from the power channel 'chan' of the area that contains the object. + + power_change() 'game/machinery/machinery_power.dm' + Called by the area that contains the object when ever that area under goes a + power state change (area runs out of power, or area channel is turned off). + + RefreshParts() 'game/machinery/machine.dm' + Called to refresh the variables in the machine that are contributed to by parts + contained in the component_parts list. (example: glass and material amounts for + the autolathe) + + Default definition does nothing. + + assign_uid() 'game/machinery/machine.dm' + Called by machine to assign a value to the uid variable. + + process() 'game/machinery/machine.dm' + Called by the 'master_controller' once per game tick for each machine that is listed in the 'machines' list. + + + Compiled by Aygar +*/ + +/obj/machinery + name = "machinery" + icon = 'icons/obj/stationobjs.dmi' + w_class = ITEMSIZE_NO_CONTAINER + layer = UNDER_JUNK_LAYER + + var/stat = 0 + var/emagged = 0 + var/use_power = USE_POWER_IDLE + //0 = dont run the auto + //1 = run auto, use idle + //2 = run auto, use active + var/idle_power_usage = 0 + var/active_power_usage = 0 + var/power_channel = EQUIP //EQUIP, ENVIRON or LIGHT + var/power_init_complete = FALSE + var/list/component_parts = null //list of all the parts used to build it, if made from certain kinds of frames. + var/uid + var/panel_open = FALSE + var/global/gl_uid = 1 + var/clicksound // sound played on succesful interface. Just put it in the list of vars at the start. + var/clickvol = 40 // volume + var/interact_offline = 0 // Can the machine be interacted with while de-powered. + var/obj/item/weapon/circuitboard/circuit = null + + // 0.0 - 1.0 multipler for prob() based on bullet structure damage + // So if this is 1.0 then a 100 damage bullet will always break this structure + // If this is 0.5 then a 50 damage bullet will break this structure 25% of the time + var/bullet_vulnerability = 0.25 + + var/speed_process = FALSE //If false, SSmachines. If true, SSfastprocess. + + blocks_emissive = EMISSIVE_BLOCK_GENERIC + +/obj/machinery/New(l, d=0) + ..() + if(isnum(d)) + set_dir(d) + if(ispath(circuit)) + circuit = new circuit(src) + +/obj/machinery/Initialize(var/mapload) + . = ..() + global.machines += src + if(ispath(circuit)) + circuit = new circuit(src) + if(!speed_process) + START_MACHINE_PROCESSING(src) + else + START_PROCESSING(SSfastprocess, src) + if(!mapload) + power_change() + +/obj/machinery/Destroy() + if(!speed_process) + STOP_MACHINE_PROCESSING(src) + else + STOP_PROCESSING(SSfastprocess, src) + global.machines -= src + if(component_parts) + for(var/atom/A in component_parts) + if(A.loc == src) // If the components are inside the machine, delete them. + qdel(A) + else // Otherwise we assume they were dropped to the ground during deconstruction, and were not removed from the component_parts list by deconstruction code. + warning("[A] was still in [src]'s component_parts when it was Destroy()'d") + component_parts.Cut() + component_parts = null + if(contents) // The same for contents. + for(var/atom/A in contents) + if(ishuman(A)) + var/mob/living/carbon/human/H = A + H.client.eye = H.client.mob + H.client.perspective = MOB_PERSPECTIVE + H.loc = src.loc + else + qdel(A) + return ..() + +/obj/machinery/process() // Steady power usage is handled separately. If you dont use process why are you here? + return PROCESS_KILL + +/obj/machinery/emp_act(severity) + if(use_power && stat == 0) + use_power(7500/severity) + + var/obj/effect/overlay/pulse2 = new /obj/effect/overlay(src.loc) + pulse2.icon = 'icons/effects/effects.dmi' + pulse2.icon_state = "empdisable" + pulse2.name = "emp sparks" + pulse2.anchored = TRUE + pulse2.set_dir(pick(cardinal)) + + spawn(10) + qdel(pulse2) + ..() + +/obj/machinery/ex_act(severity) + switch(severity) + if(1.0) + fall_apart(severity) + return + if(2.0) + if(prob(50)) + fall_apart(severity) + return + if(3.0) + if(prob(25)) + fall_apart(severity) + return + else + return + +/obj/machinery/vv_edit_var(var/var_name, var/new_value) + if(var_name == NAMEOF(src, use_power)) + update_use_power(new_value) + return TRUE + else if(var_name == NAMEOF(src, power_channel)) + update_power_channel(new_value) + return TRUE + else if(var_name == NAMEOF(src, idle_power_usage)) + update_idle_power_usage(new_value) + return TRUE + else if(var_name == NAMEOF(src, active_power_usage)) + update_active_power_usage(new_value) + return TRUE + return ..() + +/obj/machinery/proc/operable(var/additional_flags = 0) + return !inoperable(additional_flags) + +/obj/machinery/proc/inoperable(var/additional_flags = 0) + return (stat & (NOPOWER | BROKEN | additional_flags)) + +// Duplicate of below because we don't want to fuck around with CanUseTopic in TGUI +// TODO: Replace this with can_interact from /tg/ +/obj/machinery/tgui_status(mob/user) + if(!interact_offline && (stat & (NOPOWER | BROKEN))) + return STATUS_CLOSE + return ..() + +/obj/machinery/CanUseTopic(var/mob/user) + if(!interact_offline && (stat & (NOPOWER | BROKEN))) + return STATUS_CLOSE + return ..() + +/obj/machinery/CouldUseTopic(var/mob/user) + ..() + user.set_machine(src) + +/obj/machinery/CouldNotUseTopic(var/mob/user) + user.unset_machine() + +//////////////////////////////////////////////////////////////////////////////////////////// + +/obj/machinery/attack_ai(mob/user as mob) + if(isrobot(user)) + // For some reason attack_robot doesn't work + // This is to stop robots from using cameras to remotely control machines. + if(user.client && user.client.eye == user) + return attack_hand(user) + else + return attack_hand(user) + +/obj/machinery/attack_hand(mob/user as mob) + + if(inoperable(MAINT)) + return 1 + if(user.lying || user.stat) + return 1 + if(!user.IsAdvancedToolUser()) //Vorestation edit + to_chat(user, "You don't have the dexterity to do this!") + return 1 + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.getBrainLoss() >= 55) + visible_message("[H] stares cluelessly at [src].") + return 1 + else if(prob(H.getBrainLoss())) + to_chat(user, "You momentarily forget how to use [src].") + return 1 + + if(clicksound && istype(user, /mob/living/carbon)) + playsound(src, clicksound, clickvol) + + add_fingerprint(user) + + return ..() + +/obj/machinery/proc/RefreshParts() //Placeholder proc for machines that are built using frames. + return + +/obj/machinery/proc/assign_uid() + uid = gl_uid + gl_uid++ + +/obj/machinery/proc/state(var/msg) + for(var/mob/O in hearers(src, null)) + O.show_message("\icon[src][bicon(src)] [msg]", 2) + +/obj/machinery/proc/ping(text=null) + if(!text) + text = "\The [src] pings." + + state(text, "blue") + playsound(src, 'sound/machines/ping.ogg', 50, 0) + +/obj/machinery/proc/shock(mob/user, prb) + if(inoperable()) + return 0 + if(!prob(prb)) + return 0 + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + if(electrocute_mob(user, get_area(src), src, 0.7)) + var/area/temp_area = get_area(src) + if(temp_area) + var/obj/machinery/power/apc/temp_apc = temp_area.get_apc() + + if(temp_apc && temp_apc.terminal && temp_apc.terminal.powernet) + temp_apc.terminal.powernet.trigger_warning() + if(user.stunned) + return 1 + return 0 + +/obj/machinery/proc/default_apply_parts() + var/obj/item/weapon/circuitboard/CB = circuit + if(!istype(CB)) + return + CB.apply_default_parts(src) + RefreshParts() + +/obj/machinery/proc/default_use_hicell() + var/obj/item/weapon/cell/C = locate(/obj/item/weapon/cell) in component_parts + if(C) + component_parts -= C + qdel(C) + C = new /obj/item/weapon/cell/high(src) + component_parts += C + RefreshParts() + return C + +/obj/machinery/proc/default_part_replacement(var/mob/user, var/obj/item/weapon/storage/part_replacer/R) + var/parts_replaced = FALSE + if(!istype(R)) + return 0 + if(!component_parts) + return 0 + to_chat(user, "Following parts detected in [src]:") + for(var/obj/item/C in component_parts) + to_chat(user, " [C.name]") + if(panel_open || !R.panel_req) + var/obj/item/weapon/circuitboard/CB = circuit + var/P + for(var/obj/item/A in component_parts) + for(var/T in CB.req_components) + if(ispath(A.type, T)) + P = T + break + for(var/obj/item/B in R.contents) + if(istype(B, P) && istype(A, P)) + if(B.get_rating() > A.get_rating()) + R.remove_from_storage(B, src) + R.handle_item_insertion(A, 1) + component_parts -= A + component_parts += B + B.loc = null + to_chat(user, "[A.name] replaced with [B.name].") + parts_replaced = TRUE + break + update_icon() + RefreshParts() + if(parts_replaced) + R.play_rped_sound() + return 1 + +// Default behavior for wrenching down machines. Supports both delay and instant modes. +/obj/machinery/proc/default_unfasten_wrench(var/mob/user, var/obj/item/W, var/time = 0) + if(!W.has_tool_quality(TOOL_WRENCH)) + return FALSE + if(panel_open) + return FALSE // Close panel first! + playsound(src, W.usesound, 50, 1) + var/actual_time = W.toolspeed * time + if(actual_time != 0) + user.visible_message( \ + "\The [user] begins [anchored ? "un" : ""]securing \the [src].", \ + "You start [anchored ? "un" : ""]securing \the [src].") + if(actual_time == 0 || do_after(user, actual_time, target = src)) + user.visible_message( \ + "\The [user] has [anchored ? "un" : ""]secured \the [src].", \ + "You [anchored ? "un" : ""]secure \the [src].") + anchored = !anchored + power_change() //Turn on or off the machine depending on the status of power in the new area. + update_icon() + return TRUE + +/obj/machinery/proc/default_deconstruction_crowbar(var/mob/user, var/obj/item/C) + if(!C.has_tool_quality(TOOL_CROWBAR)) + return 0 + if(!panel_open) + return 0 + . = dismantle() + +/obj/machinery/proc/default_deconstruction_screwdriver(var/mob/user, var/obj/item/S) + if(!S.has_tool_quality(TOOL_SCREWDRIVER)) + return 0 + playsound(src, S.usesound, 50, 1) + panel_open = !panel_open + to_chat(user, "You [panel_open ? "open" : "close"] the maintenance hatch of [src].") + update_icon() + return 1 + +/obj/machinery/proc/computer_deconstruction_screwdriver(var/mob/user, var/obj/item/S) + if(!S.has_tool_quality(TOOL_SCREWDRIVER)) + return 0 + if(!circuit) + return 0 + to_chat(user, "You start disconnecting the monitor.") + playsound(src, S.usesound, 50, 1) + if(do_after(user, 20 * S.toolspeed)) + if(stat & BROKEN) + to_chat(user, "The broken glass falls out.") + new /obj/item/weapon/material/shard(src.loc) + else + to_chat(user, "You disconnect the monitor.") + . = dismantle() + +/obj/machinery/proc/alarm_deconstruction_screwdriver(var/mob/user, var/obj/item/S) + if(!S.has_tool_quality(TOOL_SCREWDRIVER)) + return 0 + playsound(src, S.usesound, 50, 1) + panel_open = !panel_open + to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"]") + update_icon() + return 1 + +/obj/machinery/proc/alarm_deconstruction_wirecutters(var/mob/user, var/obj/item/W) + if(!W.has_tool_quality(TOOL_WIRECUTTER)) + return 0 + if(!panel_open) + return 0 + user.visible_message("[user] has cut the wires inside \the [src]!", "You have cut the wires inside \the [src].") + playsound(src, W.usesound, 50, 1) + new/obj/item/stack/cable_coil(get_turf(src), 5) + . = dismantle() + +/obj/machinery/proc/dismantle() + playsound(src, 'sound/items/Crowbar.ogg', 50, 1) + for(var/obj/I in contents) + if(istype(I,/obj/item/weapon/card/id)) + I.forceMove(src.loc) + + if(!circuit) + return 0 + var/obj/structure/frame/A = new /obj/structure/frame(src.loc) + var/obj/item/weapon/circuitboard/M = circuit + A.circuit = M + A.anchored = TRUE + A.frame_type = M.board_type + if(A.frame_type.circuit) + A.need_circuit = 0 + + if(A.frame_type.frame_class == FRAME_CLASS_ALARM || A.frame_type.frame_class == FRAME_CLASS_DISPLAY) + A.density = FALSE + else + A.density = TRUE + + if(A.frame_type.frame_class == FRAME_CLASS_MACHINE) + for(var/obj/D in component_parts) + D.forceMove(src.loc) + if(A.components) + A.components.Cut() + else + A.components = list() + component_parts = list() + A.check_components() + + if(A.frame_type.frame_class == FRAME_CLASS_ALARM) + A.state = FRAME_FASTENED + else if(A.frame_type.frame_class == FRAME_CLASS_COMPUTER || A.frame_type.frame_class == FRAME_CLASS_DISPLAY) + if(stat & BROKEN) + A.state = FRAME_WIRED + else + A.state = FRAME_PANELED + else + A.state = FRAME_WIRED + + A.set_dir(dir) + A.pixel_x = pixel_x + A.pixel_y = pixel_y + A.update_desc() + A.update_icon() + M.loc = null + M.deconstruct(src) + qdel(src) + return 1 + +/obj/machinery/bullet_act(obj/item/projectile/P, def_zone) + . = ..() + if(prob(P.get_structure_damage() * bullet_vulnerability)) + fall_apart() + +/** + * Like an angrier dismantle, where it destroys some of the parts and doesn't give you a frame + ** severity: Same severities as ex_act (so lower is more destructive) + ** scatter: If you want the parts to slide around 1 turf in random directions + */ +/obj/machinery/proc/fall_apart(var/severity = 3, var/scatter = TRUE) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + var/atom/droploc = drop_location() + if(!droploc || !contents.len) // not even a circuit? + playsound(src, 'sound/machines/machine_die_short.ogg') + spark_system.start() + qdel(spark_system) + qdel(src) + return + + var/list/surviving_parts = list() + // Deleting IDs is lame, unless this is like nuclear severity + if(severity != 1) + for(var/obj/item/weapon/card/id/I in contents) + surviving_parts |= I + + // May populate some items to throw around + if(!LAZYLEN(component_parts) && circuit) + circuit.apply_default_parts(src) + + var/survivability + switch(severity) + // No survivors + if(1) + survivability = 0 + + // 1 part survives + if(2) + survivability = 0 + var/atom/movable/picked_part = pick(contents) + if(istype(picked_part)) + surviving_parts |= picked_part + + // 50% of parts destroyed on average + if(3) + survivability = 50 + + // No parts destroyed, but you lose the frame + else + survivability = 100 + + for(var/atom/movable/P in contents) + if(prob(survivability)) + surviving_parts |= P + + if(circuit && severity >= 2) + var/datum/frame/frame_types/FT = circuit.board_type + if(istype(FT)) + // Some steel from the frame, but about half + surviving_parts += new /obj/item/stack/material/steel(null, max(1,round(FT.frame_size/2))) + // Two bits of cable, but not the 5 required to rebuild + surviving_parts += new /obj/item/stack/cable_coil(null, 1) + surviving_parts += new /obj/item/stack/cable_coil(null, 1) + + for(var/atom/movable/A as anything in surviving_parts) + A.forceMove(droploc) + if(scatter && isturf(droploc)) + var/turf/T = droploc + A.Move(get_step(T, pick(alldirs))) + + playsound(src, 'sound/machines/machine_die_short.ogg') + spark_system.start() + qdel(spark_system) + qdel(src) + +/datum/proc/apply_visual(mob/M) + M.sight = 0 //Just reset their mesons and stuff so they can't use them, by default. + return + +/datum/proc/remove_visual(mob/M) + return diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm index 7b6de697ac7..13184fdbfb7 100644 --- a/code/game/machinery/magnet.dm +++ b/code/game/machinery/magnet.dm @@ -1,396 +1,396 @@ -// Magnetic attractor, creates variable magnetic fields and attraction. -// Can also be used to emit electron/proton beams to create a center of magnetism on another tile - -// tl;dr: it's magnets lol -// This was created for firing ranges, but I suppose this could have other applications - Doohl - -/obj/machinery/magnetic_module - icon = 'icons/obj/objects.dmi' - icon_state = "floor_magnet-f" - name = "Electromagnetic Generator" - desc = "A device that uses station power to create points of magnetic energy." - plane = PLATING_PLANE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 50 - - var/freq = 1449 // radio frequency - var/electricity_level = 1 // intensity of the magnetic pull - var/magnetic_field = 1 // the range of magnetic attraction - var/code = 0 // frequency code, they should be different unless you have a group of magnets working together or something - var/turf/center // the center of magnetic attraction - var/on = 0 - var/pulling = 0 - - // x, y modifiers to the center turf; (0, 0) is centered on the magnet, whereas (1, -1) is one tile right, one tile down - var/center_x = 0 - var/center_y = 0 - var/max_dist = 20 // absolute value of center_x,y cannot exceed this integer - -/obj/machinery/magnetic_module/New() - ..() - var/turf/T = loc - hide(!T.is_plating()) - center = T - - spawn(10) // must wait for map loading to finish - if(radio_controller) - radio_controller.add_object(src, freq, RADIO_MAGNETS) - - spawn() - magnetic_process() - -// update the invisibility and icon -/obj/machinery/magnetic_module/hide(var/intact) - invisibility = intact ? 101 : 0 - update_icon() - -// update the icon_state -/obj/machinery/magnetic_module/update_icon() - var/state="floor_magnet" - var/onstate="" - if(!on) - onstate="0" - - if(invisibility) - icon_state = "[state][onstate]-f" // if invisible, set icon to faded version - // in case of being revealed by T-scanner - else - icon_state = "[state][onstate]" - -/obj/machinery/magnetic_module/receive_signal(datum/signal/signal) - var/command = signal.data["command"] - var/modifier = signal.data["modifier"] - var/signal_code = signal.data["code"] - if(command && (signal_code == code)) - - Cmd(command, modifier) - -/obj/machinery/magnetic_module/proc/Cmd(var/command, var/modifier) - if(command) - switch(command) - if("set-electriclevel") - if(modifier) electricity_level = modifier - if("set-magneticfield") - if(modifier) magnetic_field = modifier - - if("add-elec") - electricity_level++ - if(electricity_level > 12) - electricity_level = 12 - if("sub-elec") - electricity_level-- - if(electricity_level <= 0) - electricity_level = 1 - if("add-mag") - magnetic_field++ - if(magnetic_field > 4) - magnetic_field = 4 - if("sub-mag") - magnetic_field-- - if(magnetic_field <= 0) - magnetic_field = 1 - - if("set-x") - if(modifier) center_x = modifier - if("set-y") - if(modifier) center_y = modifier - - if("N") // NORTH - center_y++ - if("S") // SOUTH - center_y-- - if("E") // EAST - center_x++ - if("W") // WEST - center_x-- - if("C") // CENTER - center_x = 0 - center_y = 0 - if("R") // RANDOM - center_x = rand(-max_dist, max_dist) - center_y = rand(-max_dist, max_dist) - - if("set-code") - if(modifier) code = modifier - if("toggle-power") - on = !on - - if(on) - spawn() - magnetic_process() - -/obj/machinery/magnetic_module/process() - if(stat & NOPOWER) - on = 0 - - // Sanity checks: - if(electricity_level <= 0) - electricity_level = 1 - if(magnetic_field <= 0) - magnetic_field = 1 - - // Limitations: - if(abs(center_x) > max_dist) - center_x = max_dist - if(abs(center_y) > max_dist) - center_y = max_dist - if(magnetic_field > 4) - magnetic_field = 4 - if(electricity_level > 12) - electricity_level = 12 - - // Update power usage: - if(on) - update_use_power(USE_POWER_ACTIVE) - update_active_power_usage(electricity_level * 15) - else - update_use_power(USE_POWER_OFF) - - // Overload conditions: - /* // Eeeehhh kinda stupid - if(on) - if(electricity_level > 11) - if(prob(electricity_level)) - explosion(loc, 0, 1, 2, 3) // ooo dat shit EXPLODES son - spawn(2) - qdel(src) - */ - - update_icon() - -/obj/machinery/magnetic_module/proc/magnetic_process() // proc that actually does the pulling - if(pulling) return - while(on) - - pulling = 1 - center = locate(x+center_x, y+center_y, z) - if(center) - for(var/obj/M in orange(magnetic_field, center)) - if(!M.anchored && !(M.flags & NOCONDUCT)) - step_towards(M, center) - - for(var/mob/living/silicon/S in orange(magnetic_field, center)) - if(istype(S, /mob/living/silicon/ai)) continue - step_towards(S, center) - - use_power(electricity_level * 5) - sleep(13 - electricity_level) - - pulling = 0 - -/obj/machinery/magnetic_module/Destroy() - if(radio_controller) - radio_controller.remove_object(src, freq) - ..() - -/obj/machinery/magnetic_controller - name = "Magnetic Control Console" - icon = 'icons/obj/airlock_machines.dmi' // uses an airlock machine icon, THINK GREEN HELP THE ENVIRONMENT - RECYCLING! - icon_state = "airlock_control_standby" - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 45 - var/frequency = 1449 - var/code = 0 - var/list/magnets = list() - var/title = "Magnetic Control Console" - var/autolink = 0 // if set to 1, can't probe for other magnets! - - var/pathpos = 1 // position in the path - var/path = "NULL" // text path of the magnet - var/speed = 1 // lowest = 1, highest = 10 - var/list/rpath = list() // real path of the magnet, used in iterator - - var/moving = 0 // 1 if scheduled to loop - var/looping = 0 // 1 if looping - - var/datum/radio_frequency/radio_connection - - -/obj/machinery/magnetic_controller/New() - ..() - - if(autolink) - for(var/obj/machinery/magnetic_module/M in machines) - if(M.freq == frequency && M.code == code) - magnets.Add(M) - - - spawn(45) // must wait for map loading to finish - if(radio_controller) - radio_connection = radio_controller.add_object(src, frequency, RADIO_MAGNETS) - - - if(path) // check for default path - filter_path() // renders rpath - - -/obj/machinery/magnetic_controller/process() - if(magnets.len == 0 && autolink) - for(var/obj/machinery/magnetic_module/M in machines) - if(M.freq == frequency && M.code == code) - magnets.Add(M) - - -/obj/machinery/magnetic_controller/attack_ai(mob/user as mob) - return attack_hand(user) - -/obj/machinery/magnetic_controller/attack_hand(mob/user as mob) - if(stat & (BROKEN|NOPOWER)) - return - user.set_machine(src) - var/dat = "Magnetic Control Console

    " - if(!autolink) - dat += {" - Frequency: [frequency]
    - Code: [code]
    - Probe Generators
    - "} - - if(magnets.len >= 1) - - dat += "Magnets confirmed:
    " - var/i = 0 - for(var/obj/machinery/magnetic_module/M in magnets) - i++ - dat += "     < \[[i]\] ([M.on ? "On":"Off"]) | Electricity level: - [M.electricity_level] +; Magnetic field: - [M.magnetic_field] +
    " - - dat += "
    Speed: - [speed] +
    " - dat += "Path: {[path]}
    " - dat += "Moving: [moving ? "Enabled":"Disabled"]" - - - user << browse(dat, "window=magnet;size=400x500") - onclose(user, "magnet") - -/obj/machinery/magnetic_controller/Topic(href, href_list) - if(stat & (BROKEN|NOPOWER)) - return - usr.set_machine(src) - add_fingerprint(usr) - - if(href_list["radio-op"]) - - // Prepare signal beforehand, because this is a radio operation - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO // radio transmission - signal.source = src - signal.frequency = frequency - signal.data["code"] = code - - // Apply any necessary commands - switch(href_list["radio-op"]) - if("togglepower") - signal.data["command"] = "toggle-power" - - if("minuselec") - signal.data["command"] = "sub-elec" - if("pluselec") - signal.data["command"] = "add-elec" - - if("minusmag") - signal.data["command"] = "sub-mag" - if("plusmag") - signal.data["command"] = "add-mag" - - - // Broadcast the signal - - radio_connection.post_signal(src, signal, radio_filter = RADIO_MAGNETS) - - spawn(1) - updateUsrDialog() // pretty sure this increases responsiveness - - if(href_list["operation"]) - switch(href_list["operation"]) - if("plusspeed") - speed ++ - if(speed > 10) - speed = 10 - if("minusspeed") - speed -- - if(speed <= 0) - speed = 1 - if("setpath") - var/newpath = sanitize(tgui_input_text(usr, "Please define a new path!",,path)) - if(newpath && newpath != "") - moving = 0 // stop moving - path = newpath - pathpos = 1 // reset position - filter_path() // renders rpath - - if("togglemoving") - moving = !moving - if(moving) - spawn() MagnetMove() - - - updateUsrDialog() - -/obj/machinery/magnetic_controller/proc/MagnetMove() - if(looping) return - - while(moving && rpath.len >= 1) - - if(stat & (BROKEN|NOPOWER)) - break - - looping = 1 - - // Prepare the radio signal - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO // radio transmission - signal.source = src - signal.frequency = frequency - signal.data["code"] = code - - if(pathpos > rpath.len) // if the position is greater than the length, we just loop through the list! - pathpos = 1 - - var/nextmove = uppertext(rpath[pathpos]) // makes it un-case-sensitive - - if(!(nextmove in list("N","S","E","W","C","R"))) - // N, S, E, W are directional - // C is center - // R is random (in magnetic field's bounds) - qdel(signal) - break // break the loop if the character located is invalid - - signal.data["command"] = nextmove - - - pathpos++ // increase iterator - - // Broadcast the signal - spawn() - radio_connection.post_signal(src, signal, radio_filter = RADIO_MAGNETS) - - if(speed == 10) - sleep(1) - else - sleep(12-speed) - - looping = 0 - - -/obj/machinery/magnetic_controller/proc/filter_path() - // Generates the rpath variable using the path string, think of this as "string2list" - // Doesn't use params2list() because of the akward way it stacks entities - rpath = list() // clear rpath - var/maximum_character = min(50, length(path)) // chooses the maximum length of the iterator. 50 max length - - for(var/i=1, i<=maximum_character, i++) // iterates through all characters in path - - var/nextchar = copytext(path, i, i+1) // find next character - - if(!(nextchar in list(";", "&", "*", " "))) // if char is a separator, ignore - rpath += copytext(path, i, i+1) // else, add to list - - // there doesn't HAVE to be separators but it makes paths syntatically visible - -/obj/machinery/magnetic_controller/Destroy() - if(radio_controller) - radio_controller.remove_object(src, frequency) - ..() +// Magnetic attractor, creates variable magnetic fields and attraction. +// Can also be used to emit electron/proton beams to create a center of magnetism on another tile + +// tl;dr: it's magnets lol +// This was created for firing ranges, but I suppose this could have other applications - Doohl + +/obj/machinery/magnetic_module + icon = 'icons/obj/objects.dmi' + icon_state = "floor_magnet-f" + name = "Electromagnetic Generator" + desc = "A device that uses station power to create points of magnetic energy." + plane = PLATING_PLANE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 50 + + var/freq = 1449 // radio frequency + var/electricity_level = 1 // intensity of the magnetic pull + var/magnetic_field = 1 // the range of magnetic attraction + var/code = 0 // frequency code, they should be different unless you have a group of magnets working together or something + var/turf/center // the center of magnetic attraction + var/on = 0 + var/pulling = 0 + + // x, y modifiers to the center turf; (0, 0) is centered on the magnet, whereas (1, -1) is one tile right, one tile down + var/center_x = 0 + var/center_y = 0 + var/max_dist = 20 // absolute value of center_x,y cannot exceed this integer + +/obj/machinery/magnetic_module/New() + ..() + var/turf/T = loc + hide(!T.is_plating()) + center = T + + spawn(10) // must wait for map loading to finish + if(radio_controller) + radio_controller.add_object(src, freq, RADIO_MAGNETS) + + spawn() + magnetic_process() + +// update the invisibility and icon +/obj/machinery/magnetic_module/hide(var/intact) + invisibility = intact ? 101 : 0 + update_icon() + +// update the icon_state +/obj/machinery/magnetic_module/update_icon() + var/state="floor_magnet" + var/onstate="" + if(!on) + onstate="0" + + if(invisibility) + icon_state = "[state][onstate]-f" // if invisible, set icon to faded version + // in case of being revealed by T-scanner + else + icon_state = "[state][onstate]" + +/obj/machinery/magnetic_module/receive_signal(datum/signal/signal) + var/command = signal.data["command"] + var/modifier = signal.data["modifier"] + var/signal_code = signal.data["code"] + if(command && (signal_code == code)) + + Cmd(command, modifier) + +/obj/machinery/magnetic_module/proc/Cmd(var/command, var/modifier) + if(command) + switch(command) + if("set-electriclevel") + if(modifier) electricity_level = modifier + if("set-magneticfield") + if(modifier) magnetic_field = modifier + + if("add-elec") + electricity_level++ + if(electricity_level > 12) + electricity_level = 12 + if("sub-elec") + electricity_level-- + if(electricity_level <= 0) + electricity_level = 1 + if("add-mag") + magnetic_field++ + if(magnetic_field > 4) + magnetic_field = 4 + if("sub-mag") + magnetic_field-- + if(magnetic_field <= 0) + magnetic_field = 1 + + if("set-x") + if(modifier) center_x = modifier + if("set-y") + if(modifier) center_y = modifier + + if("N") // NORTH + center_y++ + if("S") // SOUTH + center_y-- + if("E") // EAST + center_x++ + if("W") // WEST + center_x-- + if("C") // CENTER + center_x = 0 + center_y = 0 + if("R") // RANDOM + center_x = rand(-max_dist, max_dist) + center_y = rand(-max_dist, max_dist) + + if("set-code") + if(modifier) code = modifier + if("toggle-power") + on = !on + + if(on) + spawn() + magnetic_process() + +/obj/machinery/magnetic_module/process() + if(stat & NOPOWER) + on = 0 + + // Sanity checks: + if(electricity_level <= 0) + electricity_level = 1 + if(magnetic_field <= 0) + magnetic_field = 1 + + // Limitations: + if(abs(center_x) > max_dist) + center_x = max_dist + if(abs(center_y) > max_dist) + center_y = max_dist + if(magnetic_field > 4) + magnetic_field = 4 + if(electricity_level > 12) + electricity_level = 12 + + // Update power usage: + if(on) + update_use_power(USE_POWER_ACTIVE) + update_active_power_usage(electricity_level * 15) + else + update_use_power(USE_POWER_OFF) + + // Overload conditions: + /* // Eeeehhh kinda stupid + if(on) + if(electricity_level > 11) + if(prob(electricity_level)) + explosion(loc, 0, 1, 2, 3) // ooo dat shit EXPLODES son + spawn(2) + qdel(src) + */ + + update_icon() + +/obj/machinery/magnetic_module/proc/magnetic_process() // proc that actually does the pulling + if(pulling) return + while(on) + + pulling = 1 + center = locate(x+center_x, y+center_y, z) + if(center) + for(var/obj/M in orange(magnetic_field, center)) + if(!M.anchored && !(M.flags & NOCONDUCT)) + step_towards(M, center) + + for(var/mob/living/silicon/S in orange(magnetic_field, center)) + if(istype(S, /mob/living/silicon/ai)) continue + step_towards(S, center) + + use_power(electricity_level * 5) + sleep(13 - electricity_level) + + pulling = 0 + +/obj/machinery/magnetic_module/Destroy() + if(radio_controller) + radio_controller.remove_object(src, freq) + ..() + +/obj/machinery/magnetic_controller + name = "Magnetic Control Console" + icon = 'icons/obj/airlock_machines.dmi' // uses an airlock machine icon, THINK GREEN HELP THE ENVIRONMENT - RECYCLING! + icon_state = "airlock_control_standby" + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 45 + var/frequency = 1449 + var/code = 0 + var/list/magnets = list() + var/title = "Magnetic Control Console" + var/autolink = 0 // if set to 1, can't probe for other magnets! + + var/pathpos = 1 // position in the path + var/path = "NULL" // text path of the magnet + var/speed = 1 // lowest = 1, highest = 10 + var/list/rpath = list() // real path of the magnet, used in iterator + + var/moving = 0 // 1 if scheduled to loop + var/looping = 0 // 1 if looping + + var/datum/radio_frequency/radio_connection + + +/obj/machinery/magnetic_controller/New() + ..() + + if(autolink) + for(var/obj/machinery/magnetic_module/M in machines) + if(M.freq == frequency && M.code == code) + magnets.Add(M) + + + spawn(45) // must wait for map loading to finish + if(radio_controller) + radio_connection = radio_controller.add_object(src, frequency, RADIO_MAGNETS) + + + if(path) // check for default path + filter_path() // renders rpath + + +/obj/machinery/magnetic_controller/process() + if(magnets.len == 0 && autolink) + for(var/obj/machinery/magnetic_module/M in machines) + if(M.freq == frequency && M.code == code) + magnets.Add(M) + + +/obj/machinery/magnetic_controller/attack_ai(mob/user as mob) + return attack_hand(user) + +/obj/machinery/magnetic_controller/attack_hand(mob/user as mob) + if(stat & (BROKEN|NOPOWER)) + return + user.set_machine(src) + var/dat = "Magnetic Control Console

    " + if(!autolink) + dat += {" + Frequency: [frequency]
    + Code: [code]
    + Probe Generators
    + "} + + if(magnets.len >= 1) + + dat += "Magnets confirmed:
    " + var/i = 0 + for(var/obj/machinery/magnetic_module/M in magnets) + i++ + dat += "     < \[[i]\] ([M.on ? "On":"Off"]) | Electricity level: - [M.electricity_level] +; Magnetic field: - [M.magnetic_field] +
    " + + dat += "
    Speed: - [speed] +
    " + dat += "Path: {[path]}
    " + dat += "Moving: [moving ? "Enabled":"Disabled"]" + + + user << browse(dat, "window=magnet;size=400x500") + onclose(user, "magnet") + +/obj/machinery/magnetic_controller/Topic(href, href_list) + if(stat & (BROKEN|NOPOWER)) + return + usr.set_machine(src) + add_fingerprint(usr) + + if(href_list["radio-op"]) + + // Prepare signal beforehand, because this is a radio operation + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO // radio transmission + signal.source = src + signal.frequency = frequency + signal.data["code"] = code + + // Apply any necessary commands + switch(href_list["radio-op"]) + if("togglepower") + signal.data["command"] = "toggle-power" + + if("minuselec") + signal.data["command"] = "sub-elec" + if("pluselec") + signal.data["command"] = "add-elec" + + if("minusmag") + signal.data["command"] = "sub-mag" + if("plusmag") + signal.data["command"] = "add-mag" + + + // Broadcast the signal + + radio_connection.post_signal(src, signal, radio_filter = RADIO_MAGNETS) + + spawn(1) + updateUsrDialog() // pretty sure this increases responsiveness + + if(href_list["operation"]) + switch(href_list["operation"]) + if("plusspeed") + speed ++ + if(speed > 10) + speed = 10 + if("minusspeed") + speed -- + if(speed <= 0) + speed = 1 + if("setpath") + var/newpath = sanitize(tgui_input_text(usr, "Please define a new path!",,path)) + if(newpath && newpath != "") + moving = 0 // stop moving + path = newpath + pathpos = 1 // reset position + filter_path() // renders rpath + + if("togglemoving") + moving = !moving + if(moving) + spawn() MagnetMove() + + + updateUsrDialog() + +/obj/machinery/magnetic_controller/proc/MagnetMove() + if(looping) return + + while(moving && rpath.len >= 1) + + if(stat & (BROKEN|NOPOWER)) + break + + looping = 1 + + // Prepare the radio signal + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO // radio transmission + signal.source = src + signal.frequency = frequency + signal.data["code"] = code + + if(pathpos > rpath.len) // if the position is greater than the length, we just loop through the list! + pathpos = 1 + + var/nextmove = uppertext(rpath[pathpos]) // makes it un-case-sensitive + + if(!(nextmove in list("N","S","E","W","C","R"))) + // N, S, E, W are directional + // C is center + // R is random (in magnetic field's bounds) + qdel(signal) + break // break the loop if the character located is invalid + + signal.data["command"] = nextmove + + + pathpos++ // increase iterator + + // Broadcast the signal + spawn() + radio_connection.post_signal(src, signal, radio_filter = RADIO_MAGNETS) + + if(speed == 10) + sleep(1) + else + sleep(12-speed) + + looping = 0 + + +/obj/machinery/magnetic_controller/proc/filter_path() + // Generates the rpath variable using the path string, think of this as "string2list" + // Doesn't use params2list() because of the akward way it stacks entities + rpath = list() // clear rpath + var/maximum_character = min(50, length(path)) // chooses the maximum length of the iterator. 50 max length + + for(var/i=1, i<=maximum_character, i++) // iterates through all characters in path + + var/nextchar = copytext(path, i, i+1) // find next character + + if(!(nextchar in list(";", "&", "*", " "))) // if char is a separator, ignore + rpath += copytext(path, i, i+1) // else, add to list + + // there doesn't HAVE to be separators but it makes paths syntatically visible + +/obj/machinery/magnetic_controller/Destroy() + if(radio_controller) + radio_controller.remove_object(src, frequency) + ..() diff --git a/code/game/machinery/mass_driver.dm b/code/game/machinery/mass_driver.dm index 1002e3fa213..7aa39cce388 100644 --- a/code/game/machinery/mass_driver.dm +++ b/code/game/machinery/mass_driver.dm @@ -1,62 +1,62 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/obj/machinery/mass_driver - name = "mass driver" - desc = "Shoots things into space." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "mass_driver" - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 2 - active_power_usage = 50 - circuit = /obj/item/weapon/circuitboard/mass_driver - - var/power = 1.0 - var/code = 1.0 - var/id = 1.0 - var/drive_range = 50 //this is mostly irrelevant since current mass drivers throw into space, but you could make a lower-range mass driver for interstation transport or something I guess. - -/obj/machinery/mass_driver/New() - . = ..() - default_apply_parts() - -/obj/machinery/mass_driver/attackby(var/obj/item/I, mob/user) - if(default_deconstruction_screwdriver(user, I)) - return - if(default_deconstruction_crowbar(user, I)) - return - - if(istype(I, /obj/item/device/multitool)) - if(panel_open) - var/input = sanitize(tgui_input_text(usr, "What id would you like to give this conveyor?", "Multitool-Conveyor interface", id)) - if(!input) - to_chat(usr, "No input found please hang up and try your call again.") - return - id = input - return - return - -/obj/machinery/mass_driver/proc/drive(amount) - if(stat & (BROKEN|NOPOWER)) - return - use_power(500) - var/O_limit - var/atom/target = get_edge_target_turf(src, dir) - for(var/atom/movable/O in loc) - if(!O.anchored||istype(O, /obj/mecha))//Mechs need their launch platforms. - O_limit++ - if(O_limit >= 20) - for(var/mob/M in hearers(src, null)) - to_chat(M, "The mass driver lets out a screech, it mustn't be able to handle any more items.") - break - use_power(500) - spawn(0) - O.throw_at(target, drive_range * power, power) - flick("mass_driver1", src) - return - -/obj/machinery/mass_driver/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return - drive() - ..(severity) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/obj/machinery/mass_driver + name = "mass driver" + desc = "Shoots things into space." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "mass_driver" + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 2 + active_power_usage = 50 + circuit = /obj/item/weapon/circuitboard/mass_driver + + var/power = 1.0 + var/code = 1.0 + var/id = 1.0 + var/drive_range = 50 //this is mostly irrelevant since current mass drivers throw into space, but you could make a lower-range mass driver for interstation transport or something I guess. + +/obj/machinery/mass_driver/New() + . = ..() + default_apply_parts() + +/obj/machinery/mass_driver/attackby(var/obj/item/I, mob/user) + if(default_deconstruction_screwdriver(user, I)) + return + if(default_deconstruction_crowbar(user, I)) + return + + if(istype(I, /obj/item/device/multitool)) + if(panel_open) + var/input = sanitize(tgui_input_text(usr, "What id would you like to give this conveyor?", "Multitool-Conveyor interface", id)) + if(!input) + to_chat(usr, "No input found please hang up and try your call again.") + return + id = input + return + return + +/obj/machinery/mass_driver/proc/drive(amount) + if(stat & (BROKEN|NOPOWER)) + return + use_power(500) + var/O_limit + var/atom/target = get_edge_target_turf(src, dir) + for(var/atom/movable/O in loc) + if(!O.anchored||istype(O, /obj/mecha))//Mechs need their launch platforms. + O_limit++ + if(O_limit >= 20) + for(var/mob/M in hearers(src, null)) + to_chat(M, "The mass driver lets out a screech, it mustn't be able to handle any more items.") + break + use_power(500) + spawn(0) + O.throw_at(target, drive_range * power, power) + flick("mass_driver1", src) + return + +/obj/machinery/mass_driver/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return + drive() + ..(severity) diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm index 79c8cdf7380..170c085a090 100644 --- a/code/game/machinery/navbeacon.dm +++ b/code/game/machinery/navbeacon.dm @@ -1,242 +1,242 @@ -// Navigation beacon for AI robots -// Functions as a transponder: looks for incoming signal matching - -var/global/list/navbeacons = list() // no I don't like putting this in, but it will do for now - -/obj/machinery/navbeacon - icon = 'icons/obj/objects.dmi' - icon_state = "navbeacon0-f" - name = "navigation beacon" - desc = "A beacon used for bot navigation." - plane = PLATING_PLANE - anchored = TRUE - var/open = 0 // true if cover is open - var/locked = 1 // true if controls are locked - var/freq = null // DEPRECATED we don't use radios anymore! - var/location = "" // location response text - var/codes_txt // DEPRECATED codes as set on map: "tag1;tag2" or "tag1=value;tag2=value" - var/list/codes = list() // assoc. list of transponder codes - req_access = list(access_engine) - -/obj/machinery/navbeacon/New() - ..() - set_codes_from_txt(codes_txt) - if(freq) - warning("[src] at [x],[y],[z] has deprecated var freq=[freq]. Replace it with proper type.") - - var/turf/T = loc - hide(!T.is_plating()) - navbeacons += src - -// set the transponder codes assoc list from codes_txt -// DEPRECATED - This is kept only for compatibilty with old map files! Do not use this! -// Instead, you should replace the map instance with one of the appropriate navbeacon subtypes. -// See the bottom of this file for a list of subtypes, make your own examples if your map needs more -/obj/machinery/navbeacon/proc/set_codes_from_txt() - if(!codes_txt) - return - warning("[src] at [x],[y],[z] in [get_area(src)] is using the deprecated 'codes_txt' mapping method. Replace it with proper type.") - - codes = list() - var/list/entries = splittext(codes_txt, ";") // entries are separated by semicolons - for(var/e in entries) - var/index = findtext(e, "=") // format is "key=value" - if(index) - var/key = copytext(e, 1, index) - var/val = copytext(e, index+1) - codes[key] = val - else - codes[e] = "1" - -/obj/machinery/navbeacon/hides_under_flooring() - return 1 - -// called when turf state changes -// hide the object if turf is intact -/obj/machinery/navbeacon/hide(var/intact) - invisibility = intact ? 101 : 0 - update_icon() - -// update the icon_state -/obj/machinery/navbeacon/update_icon() - var/state="navbeacon[open]" - - if(invisibility) - icon_state = "[state]-f" // if invisible, set icon to faded version - // in case revealed by T-scanner - else - icon_state = "[state]" - -/obj/machinery/navbeacon/attackby(var/obj/item/I, var/mob/user) - var/turf/T = loc - if(!T.is_plating()) - return // prevent intraction when T-scanner revealed - - if(I.has_tool_quality(TOOL_SCREWDRIVER)) - open = !open - playsound(src, I.usesound, 50, 1) - user.visible_message("[user] [open ? "opens" : "closes"] the beacon's cover.", "You [open ? "open" : "close"] the beacon's cover.") - - update_icon() - - else if(I.GetID()) - if(open) - if(allowed(user)) - locked = !locked - to_chat(user, "Controls are now [locked ? "locked." : "unlocked."]") - else - to_chat(user, "Access denied.") - updateDialog() - else - to_chat(user, "You must open the cover first!") - return - -/obj/machinery/navbeacon/attack_ai(var/mob/user) - interact(user, 1) - -/obj/machinery/navbeacon/attack_hand(var/mob/user) - - if(!user.IsAdvancedToolUser()) - return 0 - - interact(user, 0) - -/obj/machinery/navbeacon/interact(var/mob/user, var/ai = 0) - var/turf/T = loc - if(!T.is_plating()) - return // prevent intraction when T-scanner revealed - - if(!open && !ai) // can't alter controls if not open, unless you're an AI - to_chat(user, "The beacon's control cover is closed.") - return - - - var/t - - if(locked && !ai) - t = {"Navigation Beacon

    -(swipe card to unlock controls)

    -Location: [location ? location : "(none)"]
    -Transponder Codes:
      "} - - for(var/key in codes) - t += "
    • [key] ... [codes[key]]" - t+= "
        " - - else - - t = {"Navigation Beacon

        -(swipe card to lock controls)

        -Location: [location ? location : "(none)"]
        -Transponder Codes:
          "} - - for(var/key in codes) - t += "
        • [key] ... [codes[key]]" - t += " (edit)" - t += " (delete)
          " - t += "(add new)
          " - t+= "
            " - - user << browse(t, "window=navbeacon") - onclose(user, "navbeacon") - return - -/obj/machinery/navbeacon/Topic(href, href_list) - ..() - if(usr.stat) - return - if((in_range(src, usr) && istype(src.loc, /turf)) || (istype(usr, /mob/living/silicon))) - if(open && !locked) - usr.set_machine(src) - - if(href_list["locedit"]) - var/newloc = sanitize(tgui_input_text(usr, "Enter New Location", "Navigation Beacon", location, MAX_NAME_LEN)) - if(newloc) - location = newloc - updateDialog() - - else if(href_list["edit"]) - var/codekey = href_list["code"] - - var/newkey = tgui_input_text(usr, "Enter Transponder Code Key", "Navigation Beacon", codekey, MAX_NAME_LEN) - newkey = sanitize(newkey,MAX_NAME_LEN) - if(!newkey) - return - - var/codeval = codes[codekey] - var/newval = tgui_input_text(usr, "Enter Transponder Code Value", "Navigation Beacon", codeval, MAX_NAME_LEN) - newval = sanitize(newval,MAX_NAME_LEN) - if(!newval) - newval = codekey - return - - codes.Remove(codekey) - codes[newkey] = newval - - updateDialog() - - else if(href_list["delete"]) - var/codekey = href_list["code"] - codes.Remove(codekey) - updateDialog() - - else if(href_list["add"]) - - var/newkey = tgui_input_text(usr, "Enter New Transponder Code Key", "Navigation Beacon", null, MAX_NAME_LEN) - newkey = sanitize(newkey,MAX_NAME_LEN) - if(!newkey) - return - - var/newval = tgui_input_text(usr, "Enter New Transponder Code Value", "Navigation Beacon", null, MAX_NAME_LEN) - newval = sanitize(newval,MAX_NAME_LEN) - if(!newval) - newval = "1" - return - - if(!codes) - codes = new() - - codes[newkey] = newval - - updateDialog() - -/obj/machinery/navbeacon/Destroy() - navbeacons.Remove(src) - ..() - - -// -// Nav Beacon Mapping -// These subtypes are what you should actually put into maps! they will make your life much easier. -// -// Developer Note: navbeacons do not HAVE to use these subtypes. They are purely for mapping convenience. -// You can feel free to construct them in-game as just /obj/machinery/navbeacon and they will work just -// fine, and you can define your own specific types for every instance on map if you want (BayStation does) -// This design is a compromise that means you can do mapping without every single one being its own type -// but with it still being easy to map ~ Leshana -// - -// Mulebot delivery destinations - -/obj/machinery/navbeacon/delivery/north - codes = list("delivery" = 1, "dir" = NORTH) - -/obj/machinery/navbeacon/delivery/south - codes = list("delivery" = 1, "dir" = SOUTH) - -/obj/machinery/navbeacon/delivery/east - codes = list("delivery" = 1, "dir" = EAST) - -/obj/machinery/navbeacon/delivery/west - codes = list("delivery" = 1, "dir" = WEST) - - -// For part of the patrol route -// You MUST set "location" -// You MUST set "next_patrol" -/obj/machinery/navbeacon/patrol - var/next_patrol - -/obj/machinery/navbeacon/patrol/New() - codes = list("patrol" = 1, "next_patrol" = next_patrol) - ..() +// Navigation beacon for AI robots +// Functions as a transponder: looks for incoming signal matching + +var/global/list/navbeacons = list() // no I don't like putting this in, but it will do for now + +/obj/machinery/navbeacon + icon = 'icons/obj/objects.dmi' + icon_state = "navbeacon0-f" + name = "navigation beacon" + desc = "A beacon used for bot navigation." + plane = PLATING_PLANE + anchored = TRUE + var/open = 0 // true if cover is open + var/locked = 1 // true if controls are locked + var/freq = null // DEPRECATED we don't use radios anymore! + var/location = "" // location response text + var/codes_txt // DEPRECATED codes as set on map: "tag1;tag2" or "tag1=value;tag2=value" + var/list/codes = list() // assoc. list of transponder codes + req_access = list(access_engine) + +/obj/machinery/navbeacon/New() + ..() + set_codes_from_txt(codes_txt) + if(freq) + warning("[src] at [x],[y],[z] has deprecated var freq=[freq]. Replace it with proper type.") + + var/turf/T = loc + hide(!T.is_plating()) + navbeacons += src + +// set the transponder codes assoc list from codes_txt +// DEPRECATED - This is kept only for compatibilty with old map files! Do not use this! +// Instead, you should replace the map instance with one of the appropriate navbeacon subtypes. +// See the bottom of this file for a list of subtypes, make your own examples if your map needs more +/obj/machinery/navbeacon/proc/set_codes_from_txt() + if(!codes_txt) + return + warning("[src] at [x],[y],[z] in [get_area(src)] is using the deprecated 'codes_txt' mapping method. Replace it with proper type.") + + codes = list() + var/list/entries = splittext(codes_txt, ";") // entries are separated by semicolons + for(var/e in entries) + var/index = findtext(e, "=") // format is "key=value" + if(index) + var/key = copytext(e, 1, index) + var/val = copytext(e, index+1) + codes[key] = val + else + codes[e] = "1" + +/obj/machinery/navbeacon/hides_under_flooring() + return 1 + +// called when turf state changes +// hide the object if turf is intact +/obj/machinery/navbeacon/hide(var/intact) + invisibility = intact ? 101 : 0 + update_icon() + +// update the icon_state +/obj/machinery/navbeacon/update_icon() + var/state="navbeacon[open]" + + if(invisibility) + icon_state = "[state]-f" // if invisible, set icon to faded version + // in case revealed by T-scanner + else + icon_state = "[state]" + +/obj/machinery/navbeacon/attackby(var/obj/item/I, var/mob/user) + var/turf/T = loc + if(!T.is_plating()) + return // prevent intraction when T-scanner revealed + + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + open = !open + playsound(src, I.usesound, 50, 1) + user.visible_message("[user] [open ? "opens" : "closes"] the beacon's cover.", "You [open ? "open" : "close"] the beacon's cover.") + + update_icon() + + else if(I.GetID()) + if(open) + if(allowed(user)) + locked = !locked + to_chat(user, "Controls are now [locked ? "locked." : "unlocked."]") + else + to_chat(user, "Access denied.") + updateDialog() + else + to_chat(user, "You must open the cover first!") + return + +/obj/machinery/navbeacon/attack_ai(var/mob/user) + interact(user, 1) + +/obj/machinery/navbeacon/attack_hand(var/mob/user) + + if(!user.IsAdvancedToolUser()) + return 0 + + interact(user, 0) + +/obj/machinery/navbeacon/interact(var/mob/user, var/ai = 0) + var/turf/T = loc + if(!T.is_plating()) + return // prevent intraction when T-scanner revealed + + if(!open && !ai) // can't alter controls if not open, unless you're an AI + to_chat(user, "The beacon's control cover is closed.") + return + + + var/t + + if(locked && !ai) + t = {"Navigation Beacon

            +(swipe card to unlock controls)

            +Location: [location ? location : "(none)"]
            +Transponder Codes:
              "} + + for(var/key in codes) + t += "
            • [key] ... [codes[key]]" + t+= "
                " + + else + + t = {"Navigation Beacon

                +(swipe card to lock controls)

                +Location: [location ? location : "(none)"]
                +Transponder Codes:
                  "} + + for(var/key in codes) + t += "
                • [key] ... [codes[key]]" + t += " (edit)" + t += " (delete)
                  " + t += "(add new)
                  " + t+= "
                    " + + user << browse(t, "window=navbeacon") + onclose(user, "navbeacon") + return + +/obj/machinery/navbeacon/Topic(href, href_list) + ..() + if(usr.stat) + return + if((in_range(src, usr) && istype(src.loc, /turf)) || (istype(usr, /mob/living/silicon))) + if(open && !locked) + usr.set_machine(src) + + if(href_list["locedit"]) + var/newloc = sanitize(tgui_input_text(usr, "Enter New Location", "Navigation Beacon", location, MAX_NAME_LEN)) + if(newloc) + location = newloc + updateDialog() + + else if(href_list["edit"]) + var/codekey = href_list["code"] + + var/newkey = tgui_input_text(usr, "Enter Transponder Code Key", "Navigation Beacon", codekey, MAX_NAME_LEN) + newkey = sanitize(newkey,MAX_NAME_LEN) + if(!newkey) + return + + var/codeval = codes[codekey] + var/newval = tgui_input_text(usr, "Enter Transponder Code Value", "Navigation Beacon", codeval, MAX_NAME_LEN) + newval = sanitize(newval,MAX_NAME_LEN) + if(!newval) + newval = codekey + return + + codes.Remove(codekey) + codes[newkey] = newval + + updateDialog() + + else if(href_list["delete"]) + var/codekey = href_list["code"] + codes.Remove(codekey) + updateDialog() + + else if(href_list["add"]) + + var/newkey = tgui_input_text(usr, "Enter New Transponder Code Key", "Navigation Beacon", null, MAX_NAME_LEN) + newkey = sanitize(newkey,MAX_NAME_LEN) + if(!newkey) + return + + var/newval = tgui_input_text(usr, "Enter New Transponder Code Value", "Navigation Beacon", null, MAX_NAME_LEN) + newval = sanitize(newval,MAX_NAME_LEN) + if(!newval) + newval = "1" + return + + if(!codes) + codes = new() + + codes[newkey] = newval + + updateDialog() + +/obj/machinery/navbeacon/Destroy() + navbeacons.Remove(src) + ..() + + +// +// Nav Beacon Mapping +// These subtypes are what you should actually put into maps! they will make your life much easier. +// +// Developer Note: navbeacons do not HAVE to use these subtypes. They are purely for mapping convenience. +// You can feel free to construct them in-game as just /obj/machinery/navbeacon and they will work just +// fine, and you can define your own specific types for every instance on map if you want (BayStation does) +// This design is a compromise that means you can do mapping without every single one being its own type +// but with it still being easy to map ~ Leshana +// + +// Mulebot delivery destinations + +/obj/machinery/navbeacon/delivery/north + codes = list("delivery" = 1, "dir" = NORTH) + +/obj/machinery/navbeacon/delivery/south + codes = list("delivery" = 1, "dir" = SOUTH) + +/obj/machinery/navbeacon/delivery/east + codes = list("delivery" = 1, "dir" = EAST) + +/obj/machinery/navbeacon/delivery/west + codes = list("delivery" = 1, "dir" = WEST) + + +// For part of the patrol route +// You MUST set "location" +// You MUST set "next_patrol" +/obj/machinery/navbeacon/patrol + var/next_patrol + +/obj/machinery/navbeacon/patrol/New() + codes = list("patrol" = 1, "next_patrol" = next_patrol) + ..() diff --git a/code/game/machinery/overview.dm b/code/game/machinery/overview.dm index c63ea1f1d82..058abb899d7 100644 --- a/code/game/machinery/overview.dm +++ b/code/game/machinery/overview.dm @@ -1,335 +1,335 @@ -//#define AMAP -/obj/machinery/computer/security/verb/station_map() - set name = ".map" - set category = "Object" - set src in view(1) - usr.set_machine(src) - if(!mapping) return - log_game("[usr]([usr.key]) used station map L[z] in [src.loc.loc]") - drawmap(usr) - -/obj/machinery/computer/security/proc/drawmap(var/mob/user as mob) - - var/icx = round(world.maxx/16) + 1 - var/icy = round(world.maxy/16) + 1 - - var/xoff = round((icx*16-world.maxx)-2) - var/yoff = round((icy*16-world.maxy)-2) - - var/icount = icx * icy - - var/list/imap = list() - -#ifdef AMAP - - for(var/i = 0; iSomething is hogging the tile!") - return TRUE - if((M.piping_layer != piping_layer) && !((M.pipe_flags | flags) & PIPING_ALL_LAYER)) // Pipes on different layers can't block each other unless they are ALL_LAYER - continue - if(M.get_init_dirs() & SSmachines.get_init_dirs(pipe_type, dir)) // matches at least one direction on either type of pipe - to_chat(user, "There is already a pipe at that location!") - return TRUE - // no conflicts found - - var/obj/machinery/atmospherics/A = new pipe_type(loc) - build_pipe(A) - // TODO - Evaluate and remove the "need at least one thing to connect to" thing ~Leshana - // With how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment. - if (QDELETED(A)) - to_chat(user, "There's nothing to connect this pipe section to!") - return TRUE - transfer_fingerprints_to(A) - - playsound(src, W.usesound, 50, 1) - user.visible_message( \ - "[user] fastens \the [src].", \ - "You fasten \the [src].", \ - "You hear ratcheting.") - - qdel(src) - -/obj/item/pipe/proc/build_pipe(obj/machinery/atmospherics/A) - A.set_dir(dir) - A.init_dir() - if(pipename) - A.name = pipename - if(req_access) - A.req_access = req_access - if(req_one_access) - A.req_one_access = req_one_access - A.on_construction(color, piping_layer) - -/obj/item/pipe/trinary/flippable/build_pipe(obj/machinery/atmospherics/trinary/T) - T.mirrored = mirrored - . = ..() - -// Lookup the initialize_directions for a given atmos machinery instance facing dir. -// TODO - Right now this determines the answer by instantiating an instance and checking! -// There has to be a better way... ~Leshana -/datum/controller/subsystem/machines/proc/get_init_dirs(type, dir) - var/static/list/pipe_init_dirs_cache = list() - if(!pipe_init_dirs_cache[type]) - pipe_init_dirs_cache[type] = list() - - if(!pipe_init_dirs_cache[type]["[dir]"]) - var/obj/machinery/atmospherics/temp = new type(null, dir) - pipe_init_dirs_cache[type]["[dir]"] = temp.get_init_dirs() - qdel(temp) - - return pipe_init_dirs_cache[type]["[dir]"] - - - - - -// -// Meters are special - not like any other pipes or components -// - -/obj/item/pipe_meter - name = "meter" - desc = "A meter that can be laid on pipes." - icon = 'icons/obj/pipe-item.dmi' - icon_state = "meter" - item_state = "buildpipe" - w_class = ITEMSIZE_LARGE - var/piping_layer = PIPING_LAYER_DEFAULT - -/obj/item/pipe_meter/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if(W.has_tool_quality(TOOL_WRENCH)) - return wrench_act(user, W) - return ..() - -/obj/item/pipe_meter/proc/wrench_act(var/mob/living/user, var/obj/item/weapon/tool/wrench/W) - var/obj/machinery/atmospherics/pipe/pipe - for(var/obj/machinery/atmospherics/pipe/P in loc) - if(P.piping_layer == piping_layer) - pipe = P - break - if(!pipe) - to_chat(user, "You need to fasten it to a pipe!") - return TRUE - new /obj/machinery/meter(loc, piping_layer) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You fasten the meter to the pipe.") - qdel(src) - -/obj/item/pipe_meter/dropped() - . = ..() - if(loc) - setAttachLayer(piping_layer) - -/obj/item/pipe_meter/proc/setAttachLayer(new_layer = PIPING_LAYER_DEFAULT) - piping_layer = new_layer +/*CONTENTS +Buildable pipes +Buildable meters +*/ + +/obj/item/pipe + name = "pipe" + desc = "A pipe." + var/pipe_type + var/pipename + force = 7 + throwforce = 7 + icon = 'icons/obj/pipe-item.dmi' + icon_state = "simple" + item_state = "buildpipe" + w_class = ITEMSIZE_NORMAL + level = 2 + var/piping_layer = PIPING_LAYER_DEFAULT + var/dispenser_class // Tells the dispenser what orientations we support, so RPD can show previews. + +// One subtype for each way components connect to neighbors +/obj/item/pipe/directional + dispenser_class = PIPE_DIRECTIONAL +/obj/item/pipe/binary + dispenser_class = PIPE_STRAIGHT +/obj/item/pipe/binary/bendable + dispenser_class = PIPE_BENDABLE +/obj/item/pipe/trinary + dispenser_class = PIPE_TRINARY +/obj/item/pipe/trinary/flippable + dispenser_class = PIPE_TRIN_M + var/mirrored = FALSE +/obj/item/pipe/quaternary + dispenser_class = PIPE_ONEDIR + +/** + * Call constructor with: + * @param loc Location + * @pipe_type + */ +/obj/item/pipe/Initialize(var/mapload, var/_pipe_type, var/_dir, var/obj/machinery/atmospherics/make_from) + if(make_from) + make_from_existing(make_from) + else + pipe_type = _pipe_type + set_dir(_dir) + + update() + pixel_x += rand(-5, 5) + pixel_y += rand(-5, 5) + return ..() + +/obj/item/pipe/proc/make_from_existing(obj/machinery/atmospherics/make_from) + set_dir(make_from.dir) + pipename = make_from.name + if(make_from.req_access) + src.req_access = make_from.req_access + if(make_from.req_one_access) + src.req_one_access = make_from.req_one_access + color = make_from.pipe_color + pipe_type = make_from.type + +/obj/item/pipe/trinary/flippable/make_from_existing(obj/machinery/atmospherics/trinary/make_from) + ..() + if(make_from.mirrored) + do_a_flip() + +/obj/item/pipe/dropped() + if(loc) + setPipingLayer(piping_layer) + return ..() + +/obj/item/pipe/proc/setPipingLayer(new_layer = PIPING_LAYER_DEFAULT) + var/obj/machinery/atmospherics/fakeA = pipe_type + if(initial(fakeA.pipe_flags) & (PIPING_ALL_LAYER|PIPING_DEFAULT_LAYER_ONLY)) + new_layer = PIPING_LAYER_DEFAULT + piping_layer = new_layer + // Do it the Polaris way + switch(piping_layer) + if(PIPING_LAYER_SCRUBBER) + color = PIPE_COLOR_RED + name = "[initial(fakeA.name)] scrubber fitting" + if(PIPING_LAYER_SUPPLY) + color = PIPE_COLOR_BLUE + name = "[initial(fakeA.name)] supply fitting" + if(PIPING_LAYER_FUEL) + color = PIPE_COLOR_YELLOW + name = "[initial(fakeA.name)] fuel fitting" + if(PIPING_LAYER_AUX) + color = PIPE_COLOR_CYAN + name = "[initial(fakeA.name)] aux fitting" + // Or if we were to do it the TG way... + // pixel_x = PIPE_PIXEL_OFFSET_X(piping_layer) + // pixel_y = PIPE_PIXEL_OFFSET_Y(piping_layer) + // layer = initial(layer) + PIPE_LAYER_OFFSET(piping_layer) + +/obj/item/pipe/proc/update() + var/obj/machinery/atmospherics/fakeA = pipe_type + name = "[initial(fakeA.name)] fitting" + icon_state = initial(fakeA.pipe_state) + +/obj/item/pipe/verb/flip() + set category = "Object" + set name = "Flip Pipe" + set src in view(1) + + if ( usr.stat || usr.restrained() || !usr.canmove ) + return + + do_a_flip() + +/obj/item/pipe/proc/do_a_flip() + set_dir(turn(dir, -180)) + fixdir() + +/obj/item/pipe/trinary/flippable/do_a_flip() + // set_dir(turn(dir, flipped ? 45 : -45)) + // TG has a magic icon set with the flipped versions in the diagonals. + // We may switch to this later, but for now gotta do some magic. + mirrored = !mirrored + var/obj/machinery/atmospherics/fakeA = pipe_type + icon_state = "[initial(fakeA.pipe_state)][mirrored ? "m" : ""]" + +/obj/item/pipe/verb/rotate_clockwise() + set category = "Object" + set name = "Rotate Pipe Clockwise" + set src in view(1) + + if ( usr.stat || usr.restrained() || !usr.canmove ) + return + + src.set_dir(turn(src.dir, 270)) + fixdir() + +// Don't let pulling a pipe straighten it out. +/obj/item/pipe/binary/bendable/Move() + var/old_bent = !IS_CARDINAL(dir) + . = ..() + if(old_bent && IS_CARDINAL(dir)) + set_dir(turn(src.dir, -45)) + +//Helper to clean up dir +/obj/item/pipe/proc/fixdir() + return + +/obj/item/pipe/binary/fixdir() + if(dir == SOUTH) + set_dir(NORTH) + else if(dir == WEST) + set_dir(EAST) + +/obj/item/pipe/trinary/flippable/fixdir() + if(dir in cornerdirs) + set_dir(turn(dir, 45)) + +/obj/item/pipe/attack_self(mob/user) + set_dir(turn(dir,-90)) + fixdir() + +//called when a turf is attacked with a pipe item +/obj/item/pipe/afterattack(turf/simulated/floor/target, mob/user, proximity) + if(!proximity) return + if(istype(target) && user.canUnEquip(src)) + user.drop_from_inventory(src, target) + else + return ..() + +/obj/item/pipe/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + return wrench_act(user, W) + return ..() + +/obj/item/pipe/proc/wrench_act(var/mob/living/user, var/obj/item/weapon/tool/wrench/W) + if(!isturf(loc)) + return TRUE + + add_fingerprint(user) + fixdir() + + var/obj/machinery/atmospherics/fakeA = pipe_type + var/flags = initial(fakeA.pipe_flags) + for(var/obj/machinery/atmospherics/M in loc) + if((M.pipe_flags & flags & PIPING_ONE_PER_TURF)) //Only one dense/requires density object per tile, eg connectors/cryo/heater/coolers. + to_chat(user, "Something is hogging the tile!") + return TRUE + if((M.piping_layer != piping_layer) && !((M.pipe_flags | flags) & PIPING_ALL_LAYER)) // Pipes on different layers can't block each other unless they are ALL_LAYER + continue + if(M.get_init_dirs() & SSmachines.get_init_dirs(pipe_type, dir)) // matches at least one direction on either type of pipe + to_chat(user, "There is already a pipe at that location!") + return TRUE + // no conflicts found + + var/obj/machinery/atmospherics/A = new pipe_type(loc) + build_pipe(A) + // TODO - Evaluate and remove the "need at least one thing to connect to" thing ~Leshana + // With how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment. + if (QDELETED(A)) + to_chat(user, "There's nothing to connect this pipe section to!") + return TRUE + transfer_fingerprints_to(A) + + playsound(src, W.usesound, 50, 1) + user.visible_message( \ + "[user] fastens \the [src].", \ + "You fasten \the [src].", \ + "You hear ratcheting.") + + qdel(src) + +/obj/item/pipe/proc/build_pipe(obj/machinery/atmospherics/A) + A.set_dir(dir) + A.init_dir() + if(pipename) + A.name = pipename + if(req_access) + A.req_access = req_access + if(req_one_access) + A.req_one_access = req_one_access + A.on_construction(color, piping_layer) + +/obj/item/pipe/trinary/flippable/build_pipe(obj/machinery/atmospherics/trinary/T) + T.mirrored = mirrored + . = ..() + +// Lookup the initialize_directions for a given atmos machinery instance facing dir. +// TODO - Right now this determines the answer by instantiating an instance and checking! +// There has to be a better way... ~Leshana +/datum/controller/subsystem/machines/proc/get_init_dirs(type, dir) + var/static/list/pipe_init_dirs_cache = list() + if(!pipe_init_dirs_cache[type]) + pipe_init_dirs_cache[type] = list() + + if(!pipe_init_dirs_cache[type]["[dir]"]) + var/obj/machinery/atmospherics/temp = new type(null, dir) + pipe_init_dirs_cache[type]["[dir]"] = temp.get_init_dirs() + qdel(temp) + + return pipe_init_dirs_cache[type]["[dir]"] + + + + + +// +// Meters are special - not like any other pipes or components +// + +/obj/item/pipe_meter + name = "meter" + desc = "A meter that can be laid on pipes." + icon = 'icons/obj/pipe-item.dmi' + icon_state = "meter" + item_state = "buildpipe" + w_class = ITEMSIZE_LARGE + var/piping_layer = PIPING_LAYER_DEFAULT + +/obj/item/pipe_meter/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + return wrench_act(user, W) + return ..() + +/obj/item/pipe_meter/proc/wrench_act(var/mob/living/user, var/obj/item/weapon/tool/wrench/W) + var/obj/machinery/atmospherics/pipe/pipe + for(var/obj/machinery/atmospherics/pipe/P in loc) + if(P.piping_layer == piping_layer) + pipe = P + break + if(!pipe) + to_chat(user, "You need to fasten it to a pipe!") + return TRUE + new /obj/machinery/meter(loc, piping_layer) + playsound(src, W.usesound, 50, 1) + to_chat(user, "You fasten the meter to the pipe.") + qdel(src) + +/obj/item/pipe_meter/dropped() + . = ..() + if(loc) + setAttachLayer(piping_layer) + +/obj/item/pipe_meter/proc/setAttachLayer(new_layer = PIPING_LAYER_DEFAULT) + piping_layer = new_layer diff --git a/code/game/machinery/pipe/pipe_dispenser.dm b/code/game/machinery/pipe/pipe_dispenser.dm index e9cb4007467..96da384789b 100644 --- a/code/game/machinery/pipe/pipe_dispenser.dm +++ b/code/game/machinery/pipe/pipe_dispenser.dm @@ -1,178 +1,178 @@ -/obj/machinery/pipedispenser - name = "Pipe Dispenser" - desc = "A large machine that can rapidly dispense pipes." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "pipe_d" - density = TRUE - anchored = TRUE - unacidable = TRUE - var/unwrenched = 0 - var/wait = 0 - var/p_layer = PIPING_LAYER_REGULAR - var/static/list/pipe_layers = list( - "Regular" = PIPING_LAYER_REGULAR, - "Supply" = PIPING_LAYER_SUPPLY, - "Scrubber" = PIPING_LAYER_SCRUBBER, - "Fuel" = PIPING_LAYER_FUEL, - "Aux" = PIPING_LAYER_AUX - ) - var/disposals = FALSE - -// TODO - Its about time to make this NanoUI don't we think? -/obj/machinery/pipedispenser/attack_hand(var/mob/user as mob) - if((. = ..())) - return - tgui_interact(user) - -/obj/machinery/pipedispenser/ui_assets(mob/user) - return list( - get_asset_datum(/datum/asset/spritesheet/pipes), - ) - -/obj/machinery/pipedispenser/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "PipeDispenser", name) - ui.open() - -/obj/machinery/pipedispenser/tgui_data(mob/user) - var/list/data = list( - "disposals" = disposals, - "p_layer" = p_layer, - "pipe_layers" = pipe_layers, - ) - - var/list/recipes - if(disposals) - recipes = GLOB.disposal_pipe_recipes - else - recipes = GLOB.atmos_pipe_recipes - - for(var/c in recipes) - var/list/cat = recipes[c] - var/list/r = list() - for(var/i in 1 to cat.len) - var/datum/pipe_recipe/info = cat[i] - r += list(list("pipe_name" = info.name, "ref" = "\ref[info]")) - // Stationary pipe dispensers don't allow you to pre-select pipe directions. - // This makes it impossble to spawn bent versions of bendable pipes. - // We add a "Bent" pipe type with a special param to work around it. - if(info.dirtype == PIPE_BENDABLE) - r += list(list( - "pipe_name" = ("Bent " + info.name), - "ref" = "\ref[info]", - "bent" = TRUE - )) - data["categories"] += list(list("cat_name" = c, "recipes" = r)) - - return data - -/obj/machinery/pipedispenser/tgui_act(action, params) - if(..()) - return TRUE - if(unwrenched || !usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - return TRUE - - . = TRUE - switch(action) - if("p_layer") - p_layer = text2num(params["p_layer"]) - if("dispense_pipe") - if(!wait) - var/datum/pipe_recipe/recipe = locate(params["ref"]) - if(!istype(recipe)) - return - - var/target_dir = NORTH - if(params["bent"]) - target_dir = NORTHEAST - - var/obj/created_object = null - if(istype(recipe, /datum/pipe_recipe/pipe)) - var/datum/pipe_recipe/pipe/R = recipe - created_object = new R.construction_type(loc, recipe.pipe_type, target_dir) - var/obj/item/pipe/P = created_object - P.setPipingLayer(p_layer) - else if(istype(recipe, /datum/pipe_recipe/disposal)) - var/datum/pipe_recipe/disposal/D = recipe - var/obj/structure/disposalconstruct/C = new(loc, D.pipe_type, target_dir, 0, D.subtype ? D.subtype : 0) - C.update() - created_object = C - else if(istype(recipe, /datum/pipe_recipe/meter)) - created_object = new recipe.pipe_type(loc) - else - log_runtime(EXCEPTION("Warning: [usr] attempted to spawn pipe recipe type by params [json_encode(params)] ([recipe] [recipe?.type]), but it was not allowed by this machine ([src] [type])")) - return - - created_object.add_fingerprint(usr) - wait = TRUE - VARSET_IN(src, wait, FALSE, 15) - - -/obj/machinery/pipedispenser/attackby(var/obj/item/W as obj, var/mob/user as mob) - src.add_fingerprint(usr) - if (istype(W, /obj/item/pipe) || istype(W, /obj/item/pipe_meter)) - to_chat(usr, "You put [W] back in [src].") - user.drop_item() - qdel(W) - return - else if(W.has_tool_quality(TOOL_WRENCH)) - if (unwrenched==0) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src] from the floor...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "[user] unfastens \the [src].", \ - "You have unfastened \the [src]. Now it can be pulled somewhere else.", \ - "You hear ratchet.") - src.anchored = FALSE - src.stat |= MAINT - src.unwrenched = 1 - if (usr.machine==src) - usr << browse(null, "window=pipedispenser") - else /*if (unwrenched==1)*/ - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to fasten \the [src] to the floor...") - if (do_after(user, 20 * W.toolspeed)) - user.visible_message( \ - "[user] fastens \the [src].", \ - "You have fastened \the [src]. Now it can dispense pipes.", \ - "You hear ratchet.") - src.anchored = TRUE - src.stat &= ~MAINT - src.unwrenched = 0 - power_change() - else - return ..() - -/obj/machinery/pipedispenser/disposal - name = "Disposal Pipe Dispenser" - desc = "A large machine that can rapidly dispense pipes. This one seems to dispsense disposal pipes." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "pipe_d" - density = TRUE - anchored = TRUE - disposals = TRUE - -//Allow you to drag-drop disposal pipes into it -/obj/machinery/pipedispenser/disposal/MouseDrop_T(var/obj/structure/disposalconstruct/pipe as obj, mob/usr as mob) - if(!usr.canmove || usr.stat || usr.restrained()) - return - - if (!istype(pipe) || get_dist(usr, src) > 1 || get_dist(src,pipe) > 1 ) - return - - if (pipe.anchored) - return - - to_chat(usr, "You shove [pipe] back in [src].") - qdel(pipe) - -// adding a pipe dispensers that spawn unhooked from the ground -/obj/machinery/pipedispenser/orderable - anchored = FALSE - unwrenched = 1 - -/obj/machinery/pipedispenser/disposal/orderable - anchored = FALSE - unwrenched = 1 +/obj/machinery/pipedispenser + name = "Pipe Dispenser" + desc = "A large machine that can rapidly dispense pipes." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "pipe_d" + density = TRUE + anchored = TRUE + unacidable = TRUE + var/unwrenched = 0 + var/wait = 0 + var/p_layer = PIPING_LAYER_REGULAR + var/static/list/pipe_layers = list( + "Regular" = PIPING_LAYER_REGULAR, + "Supply" = PIPING_LAYER_SUPPLY, + "Scrubber" = PIPING_LAYER_SCRUBBER, + "Fuel" = PIPING_LAYER_FUEL, + "Aux" = PIPING_LAYER_AUX + ) + var/disposals = FALSE + +// TODO - Its about time to make this NanoUI don't we think? +/obj/machinery/pipedispenser/attack_hand(var/mob/user as mob) + if((. = ..())) + return + tgui_interact(user) + +/obj/machinery/pipedispenser/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/pipes), + ) + +/obj/machinery/pipedispenser/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PipeDispenser", name) + ui.open() + +/obj/machinery/pipedispenser/tgui_data(mob/user) + var/list/data = list( + "disposals" = disposals, + "p_layer" = p_layer, + "pipe_layers" = pipe_layers, + ) + + var/list/recipes + if(disposals) + recipes = GLOB.disposal_pipe_recipes + else + recipes = GLOB.atmos_pipe_recipes + + for(var/c in recipes) + var/list/cat = recipes[c] + var/list/r = list() + for(var/i in 1 to cat.len) + var/datum/pipe_recipe/info = cat[i] + r += list(list("pipe_name" = info.name, "ref" = "\ref[info]")) + // Stationary pipe dispensers don't allow you to pre-select pipe directions. + // This makes it impossble to spawn bent versions of bendable pipes. + // We add a "Bent" pipe type with a special param to work around it. + if(info.dirtype == PIPE_BENDABLE) + r += list(list( + "pipe_name" = ("Bent " + info.name), + "ref" = "\ref[info]", + "bent" = TRUE + )) + data["categories"] += list(list("cat_name" = c, "recipes" = r)) + + return data + +/obj/machinery/pipedispenser/tgui_act(action, params) + if(..()) + return TRUE + if(unwrenched || !usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + return TRUE + + . = TRUE + switch(action) + if("p_layer") + p_layer = text2num(params["p_layer"]) + if("dispense_pipe") + if(!wait) + var/datum/pipe_recipe/recipe = locate(params["ref"]) + if(!istype(recipe)) + return + + var/target_dir = NORTH + if(params["bent"]) + target_dir = NORTHEAST + + var/obj/created_object = null + if(istype(recipe, /datum/pipe_recipe/pipe)) + var/datum/pipe_recipe/pipe/R = recipe + created_object = new R.construction_type(loc, recipe.pipe_type, target_dir) + var/obj/item/pipe/P = created_object + P.setPipingLayer(p_layer) + else if(istype(recipe, /datum/pipe_recipe/disposal)) + var/datum/pipe_recipe/disposal/D = recipe + var/obj/structure/disposalconstruct/C = new(loc, D.pipe_type, target_dir, 0, D.subtype ? D.subtype : 0) + C.update() + created_object = C + else if(istype(recipe, /datum/pipe_recipe/meter)) + created_object = new recipe.pipe_type(loc) + else + log_runtime(EXCEPTION("Warning: [usr] attempted to spawn pipe recipe type by params [json_encode(params)] ([recipe] [recipe?.type]), but it was not allowed by this machine ([src] [type])")) + return + + created_object.add_fingerprint(usr) + wait = TRUE + VARSET_IN(src, wait, FALSE, 15) + + +/obj/machinery/pipedispenser/attackby(var/obj/item/W as obj, var/mob/user as mob) + src.add_fingerprint(usr) + if (istype(W, /obj/item/pipe) || istype(W, /obj/item/pipe_meter)) + to_chat(usr, "You put [W] back in [src].") + user.drop_item() + qdel(W) + return + else if(W.has_tool_quality(TOOL_WRENCH)) + if (unwrenched==0) + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src] from the floor...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "[user] unfastens \the [src].", \ + "You have unfastened \the [src]. Now it can be pulled somewhere else.", \ + "You hear ratchet.") + src.anchored = FALSE + src.stat |= MAINT + src.unwrenched = 1 + if (usr.machine==src) + usr << browse(null, "window=pipedispenser") + else /*if (unwrenched==1)*/ + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to fasten \the [src] to the floor...") + if (do_after(user, 20 * W.toolspeed)) + user.visible_message( \ + "[user] fastens \the [src].", \ + "You have fastened \the [src]. Now it can dispense pipes.", \ + "You hear ratchet.") + src.anchored = TRUE + src.stat &= ~MAINT + src.unwrenched = 0 + power_change() + else + return ..() + +/obj/machinery/pipedispenser/disposal + name = "Disposal Pipe Dispenser" + desc = "A large machine that can rapidly dispense pipes. This one seems to dispsense disposal pipes." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "pipe_d" + density = TRUE + anchored = TRUE + disposals = TRUE + +//Allow you to drag-drop disposal pipes into it +/obj/machinery/pipedispenser/disposal/MouseDrop_T(var/obj/structure/disposalconstruct/pipe as obj, mob/usr as mob) + if(!usr.canmove || usr.stat || usr.restrained()) + return + + if (!istype(pipe) || get_dist(usr, src) > 1 || get_dist(src,pipe) > 1 ) + return + + if (pipe.anchored) + return + + to_chat(usr, "You shove [pipe] back in [src].") + qdel(pipe) + +// adding a pipe dispensers that spawn unhooked from the ground +/obj/machinery/pipedispenser/orderable + anchored = FALSE + unwrenched = 1 + +/obj/machinery/pipedispenser/disposal/orderable + anchored = FALSE + unwrenched = 1 diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index eccf4df7867..0a9def34461 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -1,1137 +1,1137 @@ -/* Portable Turrets: - Constructed from metal, a gun of choice, and a prox sensor. - This code is slightly more documented than normal, as requested by XSI on IRC. -*/ - -/datum/category_item/catalogue/technology/turret - name = "Turrets" - desc = "This imtimidating machine is essentially an automated gun. It is able to \ - scan its immediate environment, and if it determines that a threat is nearby, it will \ - open up, aim the barrel of the weapon at the threat, and engage it until the threat \ - goes away, it dies (if using a lethal gun), or the turret is destroyed. This has made them \ - well suited for long term defense for a static position, as electricity costs much \ - less than hiring a person to stand around. Despite this, the lack of a sapient entity's \ - judgement has sometimes lead to tragedy when turrets are poorly configured.\ -

                    \ - Early models generally had simple designs, and would shoot at anything that moved, with only \ - the option to disable it remotely for maintenance or to let someone pass. More modern iterations \ - of turrets have instead replaced those simple systems with intricate optical sensors and \ - image recognition software that allow the turret to distinguish between several kinds of \ - entities, and to only engage whatever their owners configured them to fight against.\ - Some models also have the ability to switch between a lethal and non-lethal mode.\ -

                    \ - Today's cutting edge in static defense development has shifted away from improving the \ - software of the turret, and instead towards the hardware. The newest solutions for \ - automated protection includes new hardware capabilities such as thicker armor, more \ - advanced integrated weapons, and some may even have been built with EM hardening in \ - mind." - value = CATALOGUER_REWARD_MEDIUM - - -#define TURRET_PRIORITY_TARGET 2 -#define TURRET_SECONDARY_TARGET 1 -#define TURRET_NOT_TARGET 0 - -/obj/machinery/porta_turret - name = "turret" - catalogue_data = list(/datum/category_item/catalogue/technology/turret) - icon = 'icons/obj/turrets.dmi' - icon_state = "turret_cover_normal" - anchored = TRUE - - density = FALSE - use_power = TRUE //this turret uses and requires power - idle_power_usage = 50 //when inactive, this turret takes up constant 50 Equipment power - active_power_usage = 300 //when active, this turret takes up constant 300 Equipment power - power_channel = EQUIP //drains power from the EQUIPMENT channel - req_one_access = list(access_security, access_heads) - blocks_emissive = EMISSIVE_BLOCK_UNIQUE - - var/raised = FALSE //if the turret cover is "open" and the turret is raised - var/raising= FALSE //if the turret is currently opening or closing its cover - var/health = 80 //the turret's health - var/maxhealth = 80 //turrets maximal health. - var/auto_repair = FALSE //if 1 the turret slowly repairs itself. - var/locked = TRUE //if the turret's behaviour control access is locked - var/controllock = FALSE //if the turret responds to control panels - - var/installation = /obj/item/weapon/gun/energy/gun //the type of weapon installed - var/gun_charge = 0 //the charge of the gun inserted - var/projectile = null //holder for bullettype - var/lethal_projectile = null //holder for the shot when emagged - var/reqpower = 500 //holder for power needed - var/turret_type = "normal" - var/icon_color = "blue" - var/lethal_icon_color = "blue" - - var/last_fired = FALSE //TRUE: if the turret is cooling down from a shot, FALSE: turret is ready to fire - var/shot_delay = 1.5 SECONDS //1.5 seconds between each shot - - var/targetting_is_configurable = TRUE // if false, you cannot change who this turret attacks via its UI - var/check_arrest = TRUE //checks if the perp is set to arrest - var/check_records = TRUE //checks if a security record exists at all - var/check_weapons = FALSE //checks if it can shoot people that have a weapon they aren't authorized to have - var/check_access = TRUE //if this is active, the turret shoots everything that does not meet the access requirements - var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos) - var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg - var/check_all = FALSE //If active, will fire on anything, including synthetics. - var/ailock = FALSE // AI cannot use this - var/check_down = FALSE //If active, will shoot to kill when lethals are also on - var/faction = null //if set, will not fire at people in the same faction for any reason. - - var/attacked = FALSE //if set to TRUE, the turret gets pissed off and shoots at people nearby (unless they have sec access!) - - var/enabled = TRUE //determines if the turret is on - var/lethal = FALSE //whether in lethal or stun mode - var/lethal_is_configurable = TRUE // if false, its lethal setting cannot be changed - var/disabled = FALSE - - var/shot_sound //what sound should play when the turret fires - var/lethal_shot_sound //what sound should play when the emagged turret fires - - var/datum/effect/effect/system/spark_spread/spark_system //the spark system, used for generating... sparks? - - var/wrenching = FALSE - var/last_target //last target fired at, prevents turrets from erratically firing at all valid targets in range - var/timeout = 10 // When a turret pops up, then finds nothing to shoot at, this number decrements until 0, when it pops down. - var/can_salvage = TRUE // If false, salvaging doesn't give you anything. - -/obj/machinery/porta_turret/crescent - req_one_access = list(access_cent_specops) - enabled = FALSE - ailock = TRUE - check_synth = FALSE - check_access = TRUE - check_arrest = TRUE - check_records = TRUE - check_weapons = TRUE - check_anomalies = TRUE - check_all = FALSE - check_down = TRUE - -/obj/machinery/porta_turret/can_catalogue(mob/user) // Dead turrets can't be scanned. - if(stat & BROKEN) - to_chat(user, span("warning", "\The [src] was destroyed, so it cannot be scanned.")) - return FALSE - return ..() - -/obj/machinery/porta_turret/stationary - ailock = TRUE - lethal = TRUE - installation = /obj/item/weapon/gun/energy/laser - -/obj/machinery/porta_turret/stationary/syndie // Generic turrets for POIs that need to not shoot their buddies. - req_one_access = list(access_syndicate) - enabled = TRUE - check_all = TRUE - faction = "syndicate" // Make sure this equals the faction that the mobs in the POI have or they will fight each other. - -/obj/machinery/porta_turret/ai_defense - name = "defense turret" - desc = "This variant appears to be much more durable." - req_one_access = list(access_synth) // Just in case. - installation = /obj/item/weapon/gun/energy/xray // For the armor pen. - health = 250 // Since lasers do 40 each. - maxhealth = 250 - -/datum/category_item/catalogue/anomalous/precursor_a/alien_turret - name = "Precursor Alpha Object - Turrets" - desc = "An autonomous defense turret created by unknown ancient aliens. It utilizes an \ - integrated laser projector to harm, firing a cyan beam at the target. The signal processing \ - of this mechanism appears to be radically different to conventional electronics used by modern \ - technology, which appears to be much less susceptible to external electromagnetic influences.\ -

                    \ - This makes the turret be very resistant to the effects of an EM pulse. It is unknown if whatever \ - species that built the turret had intended for it to have that quality, or if it was an incidental \ - quirk of how they designed their electronics." - value = CATALOGUER_REWARD_MEDIUM - -/obj/machinery/porta_turret/alien // The kind used on the UFO submap. - name = "interior anti-boarding turret" - desc = "A very tough looking turret made by alien hands." - catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_turret) - icon_state = "turret_cover_alien" - req_one_access = list(access_alien) - installation = /obj/item/weapon/gun/energy/alien - enabled = TRUE - lethal = TRUE - ailock = TRUE - check_all = TRUE - health = 250 // Similar to the AI turrets. - maxhealth = 250 - turret_type = "alien" - -/obj/machinery/porta_turret/alien/destroyed // Turrets that are already dead, to act as a warning of what the rest of the submap contains. - name = "broken interior anti-boarding turret" - desc = "A very tough looking turret made by alien hands. This one looks destroyed, thankfully." - icon_state = "destroyed_target_prism_alien" - stat = BROKEN - can_salvage = FALSE // So you need to actually kill a turret to get the alien gun. - -/obj/machinery/porta_turret/industrial - name = "industrial turret" - desc = "This variant appears to be much more rugged." - req_one_access = list(access_heads) - icon_state = "turret_cover_industrial" - installation = /obj/item/weapon/gun/energy/phasegun - health = 200 - maxhealth = 200 - turret_type = "industrial" - -/obj/machinery/porta_turret/industrial/bullet_act(obj/item/projectile/Proj) - var/damage = round(Proj.get_structure_damage() * 1.33) - - if(!damage) - return - - if(enabled) - if(!attacked && !emagged) - attacked = TRUE - spawn() - sleep(60) - attacked = FALSE - - take_damage(damage) - -/obj/machinery/porta_turret/industrial/attack_generic(mob/living/L, damage) - return ..(L, damage * 0.8) - -/obj/machinery/porta_turret/industrial/teleport_defense - name = "defense turret" - desc = "This variant appears to be much more durable, with a rugged outer coating." - req_one_access = list(access_heads) - installation = /obj/item/weapon/gun/energy/gun/burst - health = 250 - maxhealth = 250 - -/obj/machinery/porta_turret/poi //These are always angry - enabled = TRUE - lethal = TRUE - ailock = TRUE - check_all = TRUE - can_salvage = FALSE // So you can't just twoshot a turret and get a fancy gun - -/obj/machinery/porta_turret/lasertag - name = "lasertag turret" - turret_type = "normal" - req_one_access = list() - installation = /obj/item/weapon/gun/energy/lasertag/omni - - targetting_is_configurable = FALSE - lethal_is_configurable = FALSE - - locked = FALSE - enabled = FALSE - anchored = FALSE - //These two are used for lasertag - check_synth = FALSE - check_weapons = FALSE - //These vars aren't used - check_access = FALSE - check_arrest = FALSE - check_records = FALSE - check_anomalies = FALSE - check_all = FALSE - check_down = FALSE - -/obj/machinery/porta_turret/lasertag/red - turret_type = "red" - installation = /obj/item/weapon/gun/energy/lasertag/red - check_weapons = TRUE // Used to target blue players - -/obj/machinery/porta_turret/lasertag/blue - turret_type = "blue" - installation = /obj/item/weapon/gun/energy/lasertag/blue - check_synth = TRUE // Used to target red players - -/obj/machinery/porta_turret/lasertag/assess_living(var/mob/living/L) - if(!ishuman(L)) - return TURRET_NOT_TARGET - - if(L.invisibility >= INVISIBILITY_LEVEL_ONE) // Cannot see him. see_invisible is a mob-var - return TURRET_NOT_TARGET - - if(get_dist(src, L) > 7) //if it's too far away, why bother? - return TURRET_NOT_TARGET - - if(!(L in check_trajectory(L, src))) //check if we have true line of sight - return TURRET_NOT_TARGET - - if(L.lying) //Don't need to stun-lock the players - return TURRET_NOT_TARGET - - if(ishuman(L)) - var/mob/living/carbon/human/M = L - if(istype(M.wear_suit, /obj/item/clothing/suit/redtag) && check_synth) // Checks if they are a red player - return TURRET_PRIORITY_TARGET - - if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag) && check_weapons) // Checks if they are a blue player - return TURRET_PRIORITY_TARGET - -/obj/machinery/porta_turret/lasertag/tgui_data(mob/user) - var/list/data = list( - "locked" = isLocked(user), // does the current user have access? - "on" = enabled, // is turret turned on? - "lethal" = lethal, - "lethal_is_configurable" = lethal_is_configurable - ) - return data - -/obj/machinery/porta_turret/Initialize() - //Sets up a spark system - spark_system = new /datum/effect/effect/system/spark_spread - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - setup() - - // If turrets ever switch overlays, this will need to be cached and reapplied each time overlays_cut() is called. - var/image/turret_opened_overlay = image(icon, "open_[turret_type]") - turret_opened_overlay.layer = layer-0.1 - add_overlay(turret_opened_overlay) - return ..() - -/obj/machinery/porta_turret/Destroy() - qdel(spark_system) - spark_system = null - return ..() - -/obj/machinery/porta_turret/update_icon() - if(stat & BROKEN) // Turret is dead. - icon_state = "destroyed_target_prism_[turret_type]" - - else if(raised || raising) - // Turret is open. - if(powered() && enabled) - // Trying to shoot someone. - if(lethal) - icon_state = "[lethal_icon_color]_target_prism_[turret_type]" - else - icon_state = "[icon_color]_target_prism_[turret_type]" - - else - // Disabled. - icon_state = "grey_target_prism_[turret_type]" - - else - // Its closed. - icon_state = "turret_cover_[turret_type]" - - -/obj/machinery/porta_turret/proc/setup() - var/obj/item/weapon/gun/energy/E = installation //All energy-based weapons are applicable - var/obj/item/projectile/P = initial(E.projectile_type) - //var/obj/item/ammo_casing/shottype = E.projectile_type - - projectile = P - lethal_projectile = projectile - shot_sound = initial(P.fire_sound) - lethal_shot_sound = shot_sound - - if(istype(P, /obj/item/projectile/energy)) - icon_color = "orange" - - else if(istype(P, /obj/item/projectile/beam/stun)) - icon_color = "blue" - - else if(istype(P, /obj/item/projectile/beam/lasertag)) - icon_color = "blue" - - else if(istype(P, /obj/item/projectile/beam)) - icon_color = "red" - - else - icon_color = "blue" - - lethal_icon_color = icon_color - - weapon_setup(installation) - -/obj/machinery/porta_turret/proc/weapon_setup(var/guntype) - switch(guntype) - if(/obj/item/weapon/gun/energy/gun/burst) - lethal_icon_color = "red" - lethal_projectile = /obj/item/projectile/beam/burstlaser - lethal_shot_sound = 'sound/weapons/Laser.ogg' - shot_delay = 1 SECOND - - if(/obj/item/weapon/gun/energy/phasegun) - icon_color = "orange" - lethal_icon_color = "orange" - lethal_projectile = /obj/item/projectile/energy/phase/heavy - shot_delay = 1 SECOND - - if(/obj/item/weapon/gun/energy/gun) - lethal_icon_color = "red" - lethal_projectile = /obj/item/projectile/beam //If it has, going to kill mode - lethal_shot_sound = 'sound/weapons/Laser.ogg' - - if(/obj/item/weapon/gun/energy/gun/nuclear) - lethal_icon_color = "red" - lethal_projectile = /obj/item/projectile/beam //If it has, going to kill mode - lethal_shot_sound = 'sound/weapons/Laser.ogg' - - if(/obj/item/weapon/gun/energy/xray) - lethal_icon_color = "green" - lethal_projectile = /obj/item/projectile/beam/xray - projectile = /obj/item/projectile/beam/stun // Otherwise we fire xrays on both modes. - lethal_shot_sound = 'sound/weapons/eluger.ogg' - shot_sound = 'sound/weapons/Taser.ogg' - -/obj/machinery/porta_turret/proc/isLocked(mob/user) - if(locked && !issilicon(user)) - to_chat(user, "Controls locked.") - return 1 - if(HasController()) - return TRUE - if(isrobot(user) || isAI(user)) - if(ailock) - to_chat(user, "There seems to be a firewall preventing you from accessing this device.") - return TRUE - else - return FALSE - if(isobserver(user)) - var/mob/observer/dead/D = user - if(D.can_admin_interact()) - return FALSE - else - return TRUE - if(locked) - return TRUE - return FALSE - -/obj/machinery/porta_turret/attack_ai(mob/user) - tgui_interact(user) - -/obj/machinery/porta_turret/attack_ghost(mob/user) - tgui_interact(user) - -/obj/machinery/porta_turret/attack_hand(mob/user) - tgui_interact(user) - -/obj/machinery/porta_turret/proc/HasController() - var/area/A = get_area(src) - return A && A.turret_controls.len > 0 - -/obj/machinery/porta_turret/tgui_interact(mob/user, datum/tgui/ui = null) - if(HasController()) - to_chat(user, "[src] can only be controlled using the assigned turret controller.") - return - if(!anchored) - to_chat(user, "[src] has to be secured first!") - return - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "PortableTurret", name, ui_x = 500, ui_y = 400) - ui.open() - -/obj/machinery/porta_turret/tgui_data(mob/user) - var/list/data = list( - "locked" = isLocked(user), // does the current user have access? - "on" = enabled, - "targetting_is_configurable" = targetting_is_configurable, // If false, targetting settings don't show up - "lethal" = lethal, - "lethal_is_configurable" = lethal_is_configurable, - "check_weapons" = check_weapons, - "neutralize_noaccess" = check_access, - "neutralize_norecord" = check_records, - "neutralize_criminals" = check_arrest, - "neutralize_all" = check_all, - "neutralize_nonsynth" = check_synth, - "neutralize_unidentified" = check_anomalies, - "neutralize_down" = check_down, - ) - return data - -/obj/machinery/porta_turret/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - if(isLocked(usr)) - return TRUE - . = TRUE - - switch(action) - if("power") - enabled = !enabled - if("lethal") - if(lethal_is_configurable) - lethal = !lethal - if(targetting_is_configurable) - switch(action) - if("authweapon") - check_weapons = !check_weapons - if("authaccess") - check_access = !check_access - if("authnorecord") - check_records = !check_records - if("autharrest") - check_arrest = !check_arrest - if("authxeno") - check_anomalies = !check_anomalies - if("authsynth") - check_synth = !check_synth - if("authall") - check_all = !check_all - if("authdown") - check_down = !check_down - -/obj/machinery/porta_turret/power_change() - if(powered()) - stat &= ~NOPOWER - update_icon() - else - spawn(rand(0, 15)) - stat |= NOPOWER - update_icon() - - -/obj/machinery/porta_turret/attackby(obj/item/I, mob/user) - if(stat & BROKEN) - if(I.has_tool_quality(TOOL_CROWBAR)) - //If the turret is destroyed, you can remove it with a crowbar to - //try and salvage its components - to_chat(user, "You begin prying the metal coverings off.") - if(do_after(user, 20)) - if(can_salvage && prob(70)) - to_chat(user, "You remove the turret and salvage some components.") - if(installation) - var/obj/item/weapon/gun/energy/Gun = new installation(loc) - Gun.power_supply.charge = gun_charge - Gun.update_icon() - if(prob(50)) - new /obj/item/stack/material/steel(loc, rand(1,4)) - if(prob(50)) - new /obj/item/device/assembly/prox_sensor(loc) - else - to_chat(user, "You remove the turret but did not manage to salvage anything.") - qdel(src) // qdel - - else if(I.has_tool_quality(TOOL_WRENCH)) - if(enabled || raised) - to_chat(user, "You cannot unsecure an active turret!") - return - if(wrenching) - to_chat(user, "Someone is already [anchored ? "un" : ""]securing the turret!") - return - if(!anchored && isinspace()) - to_chat(user, "Cannot secure turrets in space!") - return - - user.visible_message(\ - "[user] begins [anchored ? "un" : ""]securing the turret.", \ - "You begin [anchored ? "un" : ""]securing the turret." \ - ) - - wrenching = TRUE - if(do_after(user, 50 * I.toolspeed)) - //This code handles moving the turret around. After all, it's a portable turret! - if(!anchored) - playsound(src, I.usesound, 100, 1) - anchored = TRUE - update_icon() - to_chat(user, "You secure the exterior bolts on the turret.") - else if(anchored) - playsound(src, I.usesound, 100, 1) - anchored = FALSE - to_chat(user, "You unsecure the exterior bolts on the turret.") - update_icon() - wrenching = FALSE - - else if(istype(I, /obj/item/weapon/card/id)||istype(I, /obj/item/device/pda)) - //Behavior lock/unlock mangement - if(allowed(user)) - locked = !locked - to_chat(user, "Controls are now [locked ? "locked" : "unlocked"].") - updateUsrDialog() - else - to_chat(user, "Access denied.") - - else - //if the turret was attacked with the intention of harming it: - user.setClickCooldown(user.get_attack_speed(I)) - take_damage(I.force * 0.5) - if(I.force * 0.5 > 1) //if the force of impact dealt at least 1 damage, the turret gets pissed off - if(!attacked && !emagged) - attacked = 1 - spawn() - sleep(60) - attacked = 0 - ..() - -/obj/machinery/porta_turret/attack_generic(mob/living/L, damage) - if(isanimal(L)) - var/mob/living/simple_mob/S = L - if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) - var/incoming_damage = round(damage - (damage / 5)) //Turrets are slightly armored, assumedly. - visible_message("\The [S] [pick(S.attacktext)] \the [src]!") - take_damage(incoming_damage) - S.do_attack_animation(src) - return 1 - visible_message("\The [L] bonks \the [src]'s casing!") - return ..() - -/obj/machinery/porta_turret/emag_act(var/remaining_charges, var/mob/user) - if(!emagged) - //Emagging the turret makes it go bonkers and stun everyone. It also makes - //the turret shoot much, much faster. - to_chat(user, "You short out [src]'s threat assessment circuits.") - visible_message("[src] hums oddly...") - emagged = TRUE - controllock = TRUE - enabled = FALSE //turns off the turret temporarily - sleep(60) //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit - enabled = TRUE //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here - return 1 - -/obj/machinery/porta_turret/take_damage(var/force) - if(!raised && !raising) - force = force / 8 - if(force < 5) - return - - health -= force - if(force > 5 && prob(45)) - spark_system.start() - if(health <= 0) - die() //the death process :( - -/obj/machinery/porta_turret/bullet_act(obj/item/projectile/Proj) - var/damage = Proj.get_structure_damage() - - if(!damage) - return - - if(enabled) - if(!attacked && !emagged) - attacked = 1 - spawn() - sleep(60) - attacked = FALSE - - ..() - - take_damage(damage) - -/obj/machinery/porta_turret/emp_act(severity) - if(enabled) - //if the turret is on, the EMP no matter how severe disables the turret for a while - //and scrambles its settings, with a slight chance of having an emag effect - check_arrest = prob(50) - check_records = prob(50) - check_weapons = prob(50) - check_access = prob(20) // check_access is a pretty big deal, so it's least likely to get turned on - check_anomalies = prob(50) - if(prob(5)) - emagged = TRUE - - enabled=0 - spawn(rand(60,600)) - if(!enabled) - enabled = TRUE - - ..() - -/obj/machinery/porta_turret/ai_defense/emp_act(severity) - if(prob(33)) // One in three chance to resist an EMP. This is significant if an AoE EMP is involved against multiple turrets. - return - ..() - -/obj/machinery/porta_turret/alien/emp_act(severity) // This is overrided to give an EMP resistance as well as avoid scambling the turret settings. - if(prob(75)) // Superior alien technology, I guess. - return - enabled = FALSE - spawn(rand(1 MINUTE, 2 MINUTES)) - if(!enabled) - enabled = TRUE - -/obj/machinery/porta_turret/ex_act(severity) - switch (severity) - if(1) - qdel(src) - if(2) - if(prob(25)) - qdel(src) - else - take_damage(initial(health) * 8) //should instakill most turrets - if(3) - take_damage(initial(health) * 8 / 3) //Level 4 is too weak to bother turrets - -/obj/machinery/porta_turret/proc/die() //called when the turret dies, ie, health <= 0 - health = 0 - stat |= BROKEN //enables the BROKEN bit - spark_system.start() //creates some sparks because they look cool - update_icon() - -/obj/machinery/porta_turret/process() - //the main machinery process - - if(stat & (NOPOWER|BROKEN)) - //if the turret has no power or is broken, make the turret pop down if it hasn't already - popDown() - return - - if(!enabled) - //if the turret is off, make it pop down - popDown() - return - - var/list/targets = list() //list of primary targets - var/list/secondarytargets = list() //targets that are least important - - var/list/seenturfs = list() - for(var/turf/T in oview(world.view, src)) - seenturfs += T - - for(var/mob/M as anything in living_mob_list) - if(M.z != z || !(get_turf(M) in seenturfs)) // Skip - continue - switch(assess_living(M)) - if(TURRET_PRIORITY_TARGET) - targets += M - if(TURRET_SECONDARY_TARGET) - secondarytargets += M - - for(var/obj/mecha/M as anything in mechas_list) - if(M.z != z || !(get_turf(M) in seenturfs)) // Skip - continue - switch(assess_mecha(M)) - if(TURRET_PRIORITY_TARGET) - targets += M - if(TURRET_SECONDARY_TARGET) - secondarytargets += M - - if(!tryToShootAt(targets) && !tryToShootAt(secondarytargets) && --timeout <= 0) - popDown() // no valid targets, close the cover - - if(auto_repair && (health < maxhealth)) - use_power(20000) - health = min(health+1, maxhealth) // 1HP for 20kJ - -/obj/machinery/porta_turret/proc/assess_living(var/mob/living/L) - if(!istype(L)) - return TURRET_NOT_TARGET - - if(L.invisibility >= INVISIBILITY_LEVEL_ONE) // Cannot see him. see_invisible is a mob-var - return TURRET_NOT_TARGET - - if(faction && L.faction == faction) - return TURRET_NOT_TARGET - - if(!emagged && issilicon(L) && check_all == FALSE) // Don't target silica, unless told to neutralize everything. - return TURRET_NOT_TARGET - - if(L.stat == DEAD && !emagged) //if the perp is dead, no need to bother really - return TURRET_NOT_TARGET //move onto next potential victim! - - if(get_dist(src, L) > 7) //if it's too far away, why bother? - return TURRET_NOT_TARGET - - if(!(L in check_trajectory(L, src))) //check if we have true line of sight - return TURRET_NOT_TARGET - - if(emagged) // If emagged not even the dead get a rest - return L.stat ? TURRET_SECONDARY_TARGET : TURRET_PRIORITY_TARGET - - if(lethal && locate(/mob/living/silicon/ai) in get_turf(L)) //don't accidentally kill the AI! - return TURRET_NOT_TARGET - - if(check_synth || check_all) //If it's set to attack all non-silicons or everything, target them! - if(L.lying) - return check_down ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET - return TURRET_PRIORITY_TARGET - - if(iscuffed(L)) // If the target is handcuffed, leave it alone - return TURRET_NOT_TARGET - - if(isanimal(L)) // Animals are not so dangerous - return check_anomalies ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET - - if(isxenomorph(L) || isalien(L)) // Xenos are dangerous - return check_anomalies ? TURRET_PRIORITY_TARGET : TURRET_NOT_TARGET - - if(ishuman(L)) //if the target is a human, analyze threat level - if(assess_perp(L) < 4) - return TURRET_NOT_TARGET //if threat level < 4, keep going - - if(L.lying) //if the perp is lying down, it's still a target but a less-important target - return check_down ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET - - return TURRET_PRIORITY_TARGET //if the perp has passed all previous tests, congrats, it is now a "shoot-me!" nominee - -/obj/machinery/porta_turret/proc/assess_mecha(var/obj/mecha/M) - if(!istype(M)) - return TURRET_NOT_TARGET - - if(!M.occupant) - return check_all ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET - - return assess_living(M.occupant) - -/obj/machinery/porta_turret/proc/assess_perp(var/mob/living/carbon/human/H) - if(!H || !istype(H)) - return 0 - - if(emagged) - return 10 - - return H.assess_perp(src, check_access, check_weapons, check_records, check_arrest) - -/obj/machinery/porta_turret/proc/tryToShootAt(var/list/mob/living/targets) - if(targets.len && last_target && (last_target in targets) && target(last_target)) - return 1 - - while(targets.len > 0) - var/mob/living/M = pick(targets) - targets -= M - if(target(M)) - return 1 - - -/obj/machinery/porta_turret/proc/popUp() //pops the turret up - set waitfor = FALSE - if(disabled) - return - if(raising || raised) - return - if(stat & BROKEN) - return - set_raised_raising(raised, 1) - update_icon() - - var/atom/flick_holder = new /atom/movable/porta_turret_cover(loc) - flick_holder.layer = layer + 0.1 - flick("popup_[turret_type]", flick_holder) - playsound(src, 'sound/machines/turrets/turret_deploy.ogg', 100, 1) - sleep(10) - qdel(flick_holder) - - set_raised_raising(1, 0) - update_icon() - timeout = 10 - -/obj/machinery/porta_turret/proc/popDown() //pops the turret down - set waitfor = FALSE - last_target = null - if(disabled) - return - if(raising || !raised) - return - if(stat & BROKEN) - return - set_raised_raising(raised, 1) - update_icon() - - var/atom/flick_holder = new /atom/movable/porta_turret_cover(loc) - flick_holder.layer = layer + 0.1 - flick("popdown_[turret_type]", flick_holder) - playsound(src, 'sound/machines/turrets/turret_retract.ogg', 100, 1) - sleep(10) - qdel(flick_holder) - - set_raised_raising(0, 0) - update_icon() - timeout = 10 - -/obj/machinery/porta_turret/proc/set_raised_raising(var/incoming_raised, var/incoming_raising) - raised = incoming_raised - raising = incoming_raising - density = raised || raising - -/obj/machinery/porta_turret/proc/target(var/mob/living/target) - if(disabled) - return FALSE - if(target) - last_target = target - popUp() //pop the turret up if it's not already up. - set_dir(get_dir(src, target)) //even if you can't shoot, follow the target - playsound(src, 'sound/machines/turrets/turret_rotate.ogg', 100, 1) // Play rotating sound - spawn() - shootAt(target) - return TRUE - return FALSE - -/obj/machinery/porta_turret/proc/shootAt(var/mob/living/target) - //any emagged turrets will shoot extremely fast! This not only is deadly, but drains a lot power! - if(!(emagged || attacked)) //if it hasn't been emagged or attacked, it has to obey a cooldown rate - if(last_fired || !raised) //prevents rapid-fire shooting, unless it's been emagged - return - last_fired = TRUE - spawn() - sleep(shot_delay) - last_fired = FALSE - - if(!isturf(get_turf(src)) || !isturf(get_turf(target))) - return - - if(!raised) //the turret has to be raised in order to fire - makes sense, right? - return - - update_icon() - var/obj/item/projectile/A - if(emagged || lethal) - A = new lethal_projectile(loc) - playsound(src, lethal_shot_sound, 75, 1) - else - A = new projectile(loc) - playsound(src, shot_sound, 75, 1) - - var/power_mult = 1 - if(emagged) - power_mult = 4 // Lethal beams + higher rate of fire - else if(lethal) - power_mult = 2 // Lethal beams - use_power(reqpower * power_mult) - - //Turrets aim for the center of mass by default. - //If the target is grabbing someone then the turret smartly aims for extremities - var/def_zone - var/obj/item/weapon/grab/G = locate() in target - if(G && G.state >= GRAB_NECK) //works because mobs are currently not allowed to upgrade to NECK if they are grabbing two people. - def_zone = pick(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG) - else - def_zone = pick(BP_TORSO, BP_GROIN) - - //Shooting Code: - A.firer = src - A.old_style_target(target) - A.launch_projectile_from_turf(target, def_zone, src) - - // Reset the time needed to go back down, since we just tried to shoot at someone. - timeout = 10 - -/datum/turret_checks - var/enabled - var/lethal - var/check_synth - var/check_access - var/check_records - var/check_arrest - var/check_weapons - var/check_anomalies - var/check_all - var/ailock - -/obj/machinery/porta_turret/proc/setState(var/datum/turret_checks/TC) - if(controllock) - return - enabled = TC.enabled - lethal = TC.lethal - - check_synth = TC.check_synth - check_access = TC.check_access - check_records = TC.check_records - check_arrest = TC.check_arrest - check_weapons = TC.check_weapons - check_anomalies = TC.check_anomalies - check_all = TC.check_all - ailock = TC.ailock - - power_change() - -/* - Portable turret constructions - Known as "turret frame"s -*/ - -/obj/machinery/porta_turret_construct - name = "turret frame" - icon = 'icons/obj/turrets.dmi' - icon_state = "turret_frame" - density=TRUE - var/target_type = /obj/machinery/porta_turret // The type we intend to build - var/build_step = 0 //the current step in the building process - var/finish_name="turret" //the name applied to the product turret - var/installation = null //the gun type installed - var/gun_charge = 0 //the gun charge of the gun type installed - -/obj/machinery/porta_turret_construct/attackby(obj/item/I, mob/user) - //this is a bit unwieldy but self-explanatory - switch(build_step) - if(0) //first step - if(I.has_tool_quality(TOOL_WRENCH) && !anchored) - playsound(src, I.usesound, 100, 1) - to_chat(user, "You secure the external bolts.") - anchored = TRUE - build_step = 1 - return - - else if(I.has_tool_quality(TOOL_CROWBAR) && !anchored) - playsound(src, I.usesound, 75, 1) - to_chat(user, "You dismantle the turret construction.") - new /obj/item/stack/material/steel(loc, 5) - qdel(src) - return - - if(1) - if(istype(I, /obj/item/stack/material) && I.get_material_name() == MAT_STEEL) - var/obj/item/stack/M = I - if(M.use(2)) - to_chat(user, "You add some metal armor to the interior frame.") - build_step = 2 - icon_state = "turret_frame2" - else - to_chat(user, "You need two sheets of metal to continue construction.") - return - - else if(I.has_tool_quality(TOOL_WRENCH)) - playsound(src, I.usesound, 75, 1) - to_chat(user, "You unfasten the external bolts.") - anchored = FALSE - build_step = 0 - return - - if(2) - if(I.has_tool_quality(TOOL_WRENCH)) - playsound(src, I.usesound, 100, 1) - to_chat(user, "You bolt the metal armor into place.") - build_step = 3 - return - - else if(I.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/WT = I.get_welder() - if(!WT.isOn()) - return - if(WT.get_fuel() < 5) //uses up 5 fuel. - to_chat(user, "You need more fuel to complete this task.") - return - - playsound(src, I.usesound, 50, 1) - if(do_after(user, 20 * I.toolspeed)) - if(!src || !WT.remove_fuel(5, user)) return - build_step = 1 - to_chat(user, "You remove the turret's interior metal armor.") - new /obj/item/stack/material/steel(loc, 2) - return - - if(3) - if(istype(I, /obj/item/weapon/gun/energy)) //the gun installation part - - if(isrobot(user)) - return - var/obj/item/weapon/gun/energy/E = I //typecasts the item to an energy gun - if(!user.unEquip(I)) - to_chat(user, "\The [I] is stuck to your hand, you cannot put it in \the [src]") - return - installation = I.type //installation becomes I.type - gun_charge = E.power_supply.charge //the gun's charge is stored in gun_charge - to_chat(user, "You add [I] to the turret.") - target_type = /obj/machinery/porta_turret - - build_step = 4 - qdel(I) //delete the gun :( - return - - else if(I.has_tool_quality(TOOL_WRENCH)) - playsound(src, I.usesound, 100, 1) - to_chat(user, "You remove the turret's metal armor bolts.") - build_step = 2 - return - - if(4) - if(isprox(I)) - build_step = 5 - if(!user.unEquip(I)) - to_chat(user, "\The [I] is stuck to your hand, you cannot put it in \the [src]") - return - to_chat(user, "You add the prox sensor to the turret.") - qdel(I) - return - - //attack_hand() removes the gun - - if(5) - if(I.has_tool_quality(TOOL_SCREWDRIVER)) - playsound(src, I.usesound, 100, 1) - build_step = 6 - to_chat(user, "You close the internal access hatch.") - return - - //attack_hand() removes the prox sensor - - if(6) - if(istype(I, /obj/item/stack/material) && I.get_material_name() == MAT_STEEL) - var/obj/item/stack/M = I - if(M.use(2)) - to_chat(user, "You add some metal armor to the exterior frame.") - build_step = 7 - else - to_chat(user, "You need two sheets of metal to continue construction.") - return - - else if(I.has_tool_quality(TOOL_SCREWDRIVER)) - playsound(src, I.usesound, 100, 1) - build_step = 5 - to_chat(user, "You open the internal access hatch.") - return - - if(7) - if(I.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/WT = I.get_welder() - if(!WT.isOn()) return - if(WT.get_fuel() < 5) - to_chat(user, "You need more fuel to complete this task.") - - playsound(src, WT.usesound, 50, 1) - if(do_after(user, 30 * WT.toolspeed)) - if(!src || !WT.remove_fuel(5, user)) - return - build_step = 8 - to_chat(user, "You weld the turret's armor down.") - - //The final step: create a full turret - var/obj/machinery/porta_turret/Turret = new target_type(loc) - Turret.name = finish_name - Turret.installation = installation - Turret.gun_charge = gun_charge - Turret.enabled = 0 - Turret.setup() - - qdel(src) // qdel - - else if(I.has_tool_quality(TOOL_CROWBAR)) - playsound(src, I.usesound, 75, 1) - to_chat(user, "You pry off the turret's exterior armor.") - new /obj/item/stack/material/steel(loc, 2) - build_step = 6 - return - - if(istype(I, /obj/item/weapon/pen)) //you can rename turrets like bots! - var/t = sanitizeSafe(tgui_input_text(user, "Enter new turret name", name, finish_name, MAX_NAME_LEN), MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && loc != usr) - return - - finish_name = t - return - - ..() - -/obj/machinery/porta_turret_construct/attack_hand(mob/user) - switch(build_step) - if(4) - if(!installation) - return - build_step = 3 - - var/obj/item/weapon/gun/energy/Gun = new installation(loc) - Gun.power_supply.charge = gun_charge - Gun.update_icon() - installation = null - gun_charge = 0 - to_chat(user, "You remove [Gun] from the turret frame.") - - if(5) - to_chat(user, "You remove the prox sensor from the turret frame.") - new /obj/item/device/assembly/prox_sensor(loc) - build_step = 4 - -/obj/machinery/porta_turret_construct/attack_ai() - return - -/atom/movable/porta_turret_cover - icon = 'icons/obj/turrets.dmi' - -#undef TURRET_PRIORITY_TARGET -#undef TURRET_SECONDARY_TARGET -#undef TURRET_NOT_TARGET +/* Portable Turrets: + Constructed from metal, a gun of choice, and a prox sensor. + This code is slightly more documented than normal, as requested by XSI on IRC. +*/ + +/datum/category_item/catalogue/technology/turret + name = "Turrets" + desc = "This imtimidating machine is essentially an automated gun. It is able to \ + scan its immediate environment, and if it determines that a threat is nearby, it will \ + open up, aim the barrel of the weapon at the threat, and engage it until the threat \ + goes away, it dies (if using a lethal gun), or the turret is destroyed. This has made them \ + well suited for long term defense for a static position, as electricity costs much \ + less than hiring a person to stand around. Despite this, the lack of a sapient entity's \ + judgement has sometimes lead to tragedy when turrets are poorly configured.\ +

                    \ + Early models generally had simple designs, and would shoot at anything that moved, with only \ + the option to disable it remotely for maintenance or to let someone pass. More modern iterations \ + of turrets have instead replaced those simple systems with intricate optical sensors and \ + image recognition software that allow the turret to distinguish between several kinds of \ + entities, and to only engage whatever their owners configured them to fight against.\ + Some models also have the ability to switch between a lethal and non-lethal mode.\ +

                    \ + Today's cutting edge in static defense development has shifted away from improving the \ + software of the turret, and instead towards the hardware. The newest solutions for \ + automated protection includes new hardware capabilities such as thicker armor, more \ + advanced integrated weapons, and some may even have been built with EM hardening in \ + mind." + value = CATALOGUER_REWARD_MEDIUM + + +#define TURRET_PRIORITY_TARGET 2 +#define TURRET_SECONDARY_TARGET 1 +#define TURRET_NOT_TARGET 0 + +/obj/machinery/porta_turret + name = "turret" + catalogue_data = list(/datum/category_item/catalogue/technology/turret) + icon = 'icons/obj/turrets.dmi' + icon_state = "turret_cover_normal" + anchored = TRUE + + density = FALSE + use_power = TRUE //this turret uses and requires power + idle_power_usage = 50 //when inactive, this turret takes up constant 50 Equipment power + active_power_usage = 300 //when active, this turret takes up constant 300 Equipment power + power_channel = EQUIP //drains power from the EQUIPMENT channel + req_one_access = list(access_security, access_heads) + blocks_emissive = EMISSIVE_BLOCK_UNIQUE + + var/raised = FALSE //if the turret cover is "open" and the turret is raised + var/raising= FALSE //if the turret is currently opening or closing its cover + var/health = 80 //the turret's health + var/maxhealth = 80 //turrets maximal health. + var/auto_repair = FALSE //if 1 the turret slowly repairs itself. + var/locked = TRUE //if the turret's behaviour control access is locked + var/controllock = FALSE //if the turret responds to control panels + + var/installation = /obj/item/weapon/gun/energy/gun //the type of weapon installed + var/gun_charge = 0 //the charge of the gun inserted + var/projectile = null //holder for bullettype + var/lethal_projectile = null //holder for the shot when emagged + var/reqpower = 500 //holder for power needed + var/turret_type = "normal" + var/icon_color = "blue" + var/lethal_icon_color = "blue" + + var/last_fired = FALSE //TRUE: if the turret is cooling down from a shot, FALSE: turret is ready to fire + var/shot_delay = 1.5 SECONDS //1.5 seconds between each shot + + var/targetting_is_configurable = TRUE // if false, you cannot change who this turret attacks via its UI + var/check_arrest = TRUE //checks if the perp is set to arrest + var/check_records = TRUE //checks if a security record exists at all + var/check_weapons = FALSE //checks if it can shoot people that have a weapon they aren't authorized to have + var/check_access = TRUE //if this is active, the turret shoots everything that does not meet the access requirements + var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos) + var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg + var/check_all = FALSE //If active, will fire on anything, including synthetics. + var/ailock = FALSE // AI cannot use this + var/check_down = FALSE //If active, will shoot to kill when lethals are also on + var/faction = null //if set, will not fire at people in the same faction for any reason. + + var/attacked = FALSE //if set to TRUE, the turret gets pissed off and shoots at people nearby (unless they have sec access!) + + var/enabled = TRUE //determines if the turret is on + var/lethal = FALSE //whether in lethal or stun mode + var/lethal_is_configurable = TRUE // if false, its lethal setting cannot be changed + var/disabled = FALSE + + var/shot_sound //what sound should play when the turret fires + var/lethal_shot_sound //what sound should play when the emagged turret fires + + var/datum/effect/effect/system/spark_spread/spark_system //the spark system, used for generating... sparks? + + var/wrenching = FALSE + var/last_target //last target fired at, prevents turrets from erratically firing at all valid targets in range + var/timeout = 10 // When a turret pops up, then finds nothing to shoot at, this number decrements until 0, when it pops down. + var/can_salvage = TRUE // If false, salvaging doesn't give you anything. + +/obj/machinery/porta_turret/crescent + req_one_access = list(access_cent_specops) + enabled = FALSE + ailock = TRUE + check_synth = FALSE + check_access = TRUE + check_arrest = TRUE + check_records = TRUE + check_weapons = TRUE + check_anomalies = TRUE + check_all = FALSE + check_down = TRUE + +/obj/machinery/porta_turret/can_catalogue(mob/user) // Dead turrets can't be scanned. + if(stat & BROKEN) + to_chat(user, span("warning", "\The [src] was destroyed, so it cannot be scanned.")) + return FALSE + return ..() + +/obj/machinery/porta_turret/stationary + ailock = TRUE + lethal = TRUE + installation = /obj/item/weapon/gun/energy/laser + +/obj/machinery/porta_turret/stationary/syndie // Generic turrets for POIs that need to not shoot their buddies. + req_one_access = list(access_syndicate) + enabled = TRUE + check_all = TRUE + faction = "syndicate" // Make sure this equals the faction that the mobs in the POI have or they will fight each other. + +/obj/machinery/porta_turret/ai_defense + name = "defense turret" + desc = "This variant appears to be much more durable." + req_one_access = list(access_synth) // Just in case. + installation = /obj/item/weapon/gun/energy/xray // For the armor pen. + health = 250 // Since lasers do 40 each. + maxhealth = 250 + +/datum/category_item/catalogue/anomalous/precursor_a/alien_turret + name = "Precursor Alpha Object - Turrets" + desc = "An autonomous defense turret created by unknown ancient aliens. It utilizes an \ + integrated laser projector to harm, firing a cyan beam at the target. The signal processing \ + of this mechanism appears to be radically different to conventional electronics used by modern \ + technology, which appears to be much less susceptible to external electromagnetic influences.\ +

                    \ + This makes the turret be very resistant to the effects of an EM pulse. It is unknown if whatever \ + species that built the turret had intended for it to have that quality, or if it was an incidental \ + quirk of how they designed their electronics." + value = CATALOGUER_REWARD_MEDIUM + +/obj/machinery/porta_turret/alien // The kind used on the UFO submap. + name = "interior anti-boarding turret" + desc = "A very tough looking turret made by alien hands." + catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_turret) + icon_state = "turret_cover_alien" + req_one_access = list(access_alien) + installation = /obj/item/weapon/gun/energy/alien + enabled = TRUE + lethal = TRUE + ailock = TRUE + check_all = TRUE + health = 250 // Similar to the AI turrets. + maxhealth = 250 + turret_type = "alien" + +/obj/machinery/porta_turret/alien/destroyed // Turrets that are already dead, to act as a warning of what the rest of the submap contains. + name = "broken interior anti-boarding turret" + desc = "A very tough looking turret made by alien hands. This one looks destroyed, thankfully." + icon_state = "destroyed_target_prism_alien" + stat = BROKEN + can_salvage = FALSE // So you need to actually kill a turret to get the alien gun. + +/obj/machinery/porta_turret/industrial + name = "industrial turret" + desc = "This variant appears to be much more rugged." + req_one_access = list(access_heads) + icon_state = "turret_cover_industrial" + installation = /obj/item/weapon/gun/energy/phasegun + health = 200 + maxhealth = 200 + turret_type = "industrial" + +/obj/machinery/porta_turret/industrial/bullet_act(obj/item/projectile/Proj) + var/damage = round(Proj.get_structure_damage() * 1.33) + + if(!damage) + return + + if(enabled) + if(!attacked && !emagged) + attacked = TRUE + spawn() + sleep(60) + attacked = FALSE + + take_damage(damage) + +/obj/machinery/porta_turret/industrial/attack_generic(mob/living/L, damage) + return ..(L, damage * 0.8) + +/obj/machinery/porta_turret/industrial/teleport_defense + name = "defense turret" + desc = "This variant appears to be much more durable, with a rugged outer coating." + req_one_access = list(access_heads) + installation = /obj/item/weapon/gun/energy/gun/burst + health = 250 + maxhealth = 250 + +/obj/machinery/porta_turret/poi //These are always angry + enabled = TRUE + lethal = TRUE + ailock = TRUE + check_all = TRUE + can_salvage = FALSE // So you can't just twoshot a turret and get a fancy gun + +/obj/machinery/porta_turret/lasertag + name = "lasertag turret" + turret_type = "normal" + req_one_access = list() + installation = /obj/item/weapon/gun/energy/lasertag/omni + + targetting_is_configurable = FALSE + lethal_is_configurable = FALSE + + locked = FALSE + enabled = FALSE + anchored = FALSE + //These two are used for lasertag + check_synth = FALSE + check_weapons = FALSE + //These vars aren't used + check_access = FALSE + check_arrest = FALSE + check_records = FALSE + check_anomalies = FALSE + check_all = FALSE + check_down = FALSE + +/obj/machinery/porta_turret/lasertag/red + turret_type = "red" + installation = /obj/item/weapon/gun/energy/lasertag/red + check_weapons = TRUE // Used to target blue players + +/obj/machinery/porta_turret/lasertag/blue + turret_type = "blue" + installation = /obj/item/weapon/gun/energy/lasertag/blue + check_synth = TRUE // Used to target red players + +/obj/machinery/porta_turret/lasertag/assess_living(var/mob/living/L) + if(!ishuman(L)) + return TURRET_NOT_TARGET + + if(L.invisibility >= INVISIBILITY_LEVEL_ONE) // Cannot see him. see_invisible is a mob-var + return TURRET_NOT_TARGET + + if(get_dist(src, L) > 7) //if it's too far away, why bother? + return TURRET_NOT_TARGET + + if(!(L in check_trajectory(L, src))) //check if we have true line of sight + return TURRET_NOT_TARGET + + if(L.lying) //Don't need to stun-lock the players + return TURRET_NOT_TARGET + + if(ishuman(L)) + var/mob/living/carbon/human/M = L + if(istype(M.wear_suit, /obj/item/clothing/suit/redtag) && check_synth) // Checks if they are a red player + return TURRET_PRIORITY_TARGET + + if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag) && check_weapons) // Checks if they are a blue player + return TURRET_PRIORITY_TARGET + +/obj/machinery/porta_turret/lasertag/tgui_data(mob/user) + var/list/data = list( + "locked" = isLocked(user), // does the current user have access? + "on" = enabled, // is turret turned on? + "lethal" = lethal, + "lethal_is_configurable" = lethal_is_configurable + ) + return data + +/obj/machinery/porta_turret/Initialize() + //Sets up a spark system + spark_system = new /datum/effect/effect/system/spark_spread + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + setup() + + // If turrets ever switch overlays, this will need to be cached and reapplied each time overlays_cut() is called. + var/image/turret_opened_overlay = image(icon, "open_[turret_type]") + turret_opened_overlay.layer = layer-0.1 + add_overlay(turret_opened_overlay) + return ..() + +/obj/machinery/porta_turret/Destroy() + qdel(spark_system) + spark_system = null + return ..() + +/obj/machinery/porta_turret/update_icon() + if(stat & BROKEN) // Turret is dead. + icon_state = "destroyed_target_prism_[turret_type]" + + else if(raised || raising) + // Turret is open. + if(powered() && enabled) + // Trying to shoot someone. + if(lethal) + icon_state = "[lethal_icon_color]_target_prism_[turret_type]" + else + icon_state = "[icon_color]_target_prism_[turret_type]" + + else + // Disabled. + icon_state = "grey_target_prism_[turret_type]" + + else + // Its closed. + icon_state = "turret_cover_[turret_type]" + + +/obj/machinery/porta_turret/proc/setup() + var/obj/item/weapon/gun/energy/E = installation //All energy-based weapons are applicable + var/obj/item/projectile/P = initial(E.projectile_type) + //var/obj/item/ammo_casing/shottype = E.projectile_type + + projectile = P + lethal_projectile = projectile + shot_sound = initial(P.fire_sound) + lethal_shot_sound = shot_sound + + if(istype(P, /obj/item/projectile/energy)) + icon_color = "orange" + + else if(istype(P, /obj/item/projectile/beam/stun)) + icon_color = "blue" + + else if(istype(P, /obj/item/projectile/beam/lasertag)) + icon_color = "blue" + + else if(istype(P, /obj/item/projectile/beam)) + icon_color = "red" + + else + icon_color = "blue" + + lethal_icon_color = icon_color + + weapon_setup(installation) + +/obj/machinery/porta_turret/proc/weapon_setup(var/guntype) + switch(guntype) + if(/obj/item/weapon/gun/energy/gun/burst) + lethal_icon_color = "red" + lethal_projectile = /obj/item/projectile/beam/burstlaser + lethal_shot_sound = 'sound/weapons/Laser.ogg' + shot_delay = 1 SECOND + + if(/obj/item/weapon/gun/energy/phasegun) + icon_color = "orange" + lethal_icon_color = "orange" + lethal_projectile = /obj/item/projectile/energy/phase/heavy + shot_delay = 1 SECOND + + if(/obj/item/weapon/gun/energy/gun) + lethal_icon_color = "red" + lethal_projectile = /obj/item/projectile/beam //If it has, going to kill mode + lethal_shot_sound = 'sound/weapons/Laser.ogg' + + if(/obj/item/weapon/gun/energy/gun/nuclear) + lethal_icon_color = "red" + lethal_projectile = /obj/item/projectile/beam //If it has, going to kill mode + lethal_shot_sound = 'sound/weapons/Laser.ogg' + + if(/obj/item/weapon/gun/energy/xray) + lethal_icon_color = "green" + lethal_projectile = /obj/item/projectile/beam/xray + projectile = /obj/item/projectile/beam/stun // Otherwise we fire xrays on both modes. + lethal_shot_sound = 'sound/weapons/eluger.ogg' + shot_sound = 'sound/weapons/Taser.ogg' + +/obj/machinery/porta_turret/proc/isLocked(mob/user) + if(locked && !issilicon(user)) + to_chat(user, "Controls locked.") + return 1 + if(HasController()) + return TRUE + if(isrobot(user) || isAI(user)) + if(ailock) + to_chat(user, "There seems to be a firewall preventing you from accessing this device.") + return TRUE + else + return FALSE + if(isobserver(user)) + var/mob/observer/dead/D = user + if(D.can_admin_interact()) + return FALSE + else + return TRUE + if(locked) + return TRUE + return FALSE + +/obj/machinery/porta_turret/attack_ai(mob/user) + tgui_interact(user) + +/obj/machinery/porta_turret/attack_ghost(mob/user) + tgui_interact(user) + +/obj/machinery/porta_turret/attack_hand(mob/user) + tgui_interact(user) + +/obj/machinery/porta_turret/proc/HasController() + var/area/A = get_area(src) + return A && A.turret_controls.len > 0 + +/obj/machinery/porta_turret/tgui_interact(mob/user, datum/tgui/ui = null) + if(HasController()) + to_chat(user, "[src] can only be controlled using the assigned turret controller.") + return + if(!anchored) + to_chat(user, "[src] has to be secured first!") + return + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PortableTurret", name, ui_x = 500, ui_y = 400) + ui.open() + +/obj/machinery/porta_turret/tgui_data(mob/user) + var/list/data = list( + "locked" = isLocked(user), // does the current user have access? + "on" = enabled, + "targetting_is_configurable" = targetting_is_configurable, // If false, targetting settings don't show up + "lethal" = lethal, + "lethal_is_configurable" = lethal_is_configurable, + "check_weapons" = check_weapons, + "neutralize_noaccess" = check_access, + "neutralize_norecord" = check_records, + "neutralize_criminals" = check_arrest, + "neutralize_all" = check_all, + "neutralize_nonsynth" = check_synth, + "neutralize_unidentified" = check_anomalies, + "neutralize_down" = check_down, + ) + return data + +/obj/machinery/porta_turret/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + if(isLocked(usr)) + return TRUE + . = TRUE + + switch(action) + if("power") + enabled = !enabled + if("lethal") + if(lethal_is_configurable) + lethal = !lethal + if(targetting_is_configurable) + switch(action) + if("authweapon") + check_weapons = !check_weapons + if("authaccess") + check_access = !check_access + if("authnorecord") + check_records = !check_records + if("autharrest") + check_arrest = !check_arrest + if("authxeno") + check_anomalies = !check_anomalies + if("authsynth") + check_synth = !check_synth + if("authall") + check_all = !check_all + if("authdown") + check_down = !check_down + +/obj/machinery/porta_turret/power_change() + if(powered()) + stat &= ~NOPOWER + update_icon() + else + spawn(rand(0, 15)) + stat |= NOPOWER + update_icon() + + +/obj/machinery/porta_turret/attackby(obj/item/I, mob/user) + if(stat & BROKEN) + if(I.has_tool_quality(TOOL_CROWBAR)) + //If the turret is destroyed, you can remove it with a crowbar to + //try and salvage its components + to_chat(user, "You begin prying the metal coverings off.") + if(do_after(user, 20)) + if(can_salvage && prob(70)) + to_chat(user, "You remove the turret and salvage some components.") + if(installation) + var/obj/item/weapon/gun/energy/Gun = new installation(loc) + Gun.power_supply.charge = gun_charge + Gun.update_icon() + if(prob(50)) + new /obj/item/stack/material/steel(loc, rand(1,4)) + if(prob(50)) + new /obj/item/device/assembly/prox_sensor(loc) + else + to_chat(user, "You remove the turret but did not manage to salvage anything.") + qdel(src) // qdel + + else if(I.has_tool_quality(TOOL_WRENCH)) + if(enabled || raised) + to_chat(user, "You cannot unsecure an active turret!") + return + if(wrenching) + to_chat(user, "Someone is already [anchored ? "un" : ""]securing the turret!") + return + if(!anchored && isinspace()) + to_chat(user, "Cannot secure turrets in space!") + return + + user.visible_message(\ + "[user] begins [anchored ? "un" : ""]securing the turret.", \ + "You begin [anchored ? "un" : ""]securing the turret." \ + ) + + wrenching = TRUE + if(do_after(user, 50 * I.toolspeed)) + //This code handles moving the turret around. After all, it's a portable turret! + if(!anchored) + playsound(src, I.usesound, 100, 1) + anchored = TRUE + update_icon() + to_chat(user, "You secure the exterior bolts on the turret.") + else if(anchored) + playsound(src, I.usesound, 100, 1) + anchored = FALSE + to_chat(user, "You unsecure the exterior bolts on the turret.") + update_icon() + wrenching = FALSE + + else if(istype(I, /obj/item/weapon/card/id)||istype(I, /obj/item/device/pda)) + //Behavior lock/unlock mangement + if(allowed(user)) + locked = !locked + to_chat(user, "Controls are now [locked ? "locked" : "unlocked"].") + updateUsrDialog() + else + to_chat(user, "Access denied.") + + else + //if the turret was attacked with the intention of harming it: + user.setClickCooldown(user.get_attack_speed(I)) + take_damage(I.force * 0.5) + if(I.force * 0.5 > 1) //if the force of impact dealt at least 1 damage, the turret gets pissed off + if(!attacked && !emagged) + attacked = 1 + spawn() + sleep(60) + attacked = 0 + ..() + +/obj/machinery/porta_turret/attack_generic(mob/living/L, damage) + if(isanimal(L)) + var/mob/living/simple_mob/S = L + if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) + var/incoming_damage = round(damage - (damage / 5)) //Turrets are slightly armored, assumedly. + visible_message("\The [S] [pick(S.attacktext)] \the [src]!") + take_damage(incoming_damage) + S.do_attack_animation(src) + return 1 + visible_message("\The [L] bonks \the [src]'s casing!") + return ..() + +/obj/machinery/porta_turret/emag_act(var/remaining_charges, var/mob/user) + if(!emagged) + //Emagging the turret makes it go bonkers and stun everyone. It also makes + //the turret shoot much, much faster. + to_chat(user, "You short out [src]'s threat assessment circuits.") + visible_message("[src] hums oddly...") + emagged = TRUE + controllock = TRUE + enabled = FALSE //turns off the turret temporarily + sleep(60) //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit + enabled = TRUE //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here + return 1 + +/obj/machinery/porta_turret/take_damage(var/force) + if(!raised && !raising) + force = force / 8 + if(force < 5) + return + + health -= force + if(force > 5 && prob(45)) + spark_system.start() + if(health <= 0) + die() //the death process :( + +/obj/machinery/porta_turret/bullet_act(obj/item/projectile/Proj) + var/damage = Proj.get_structure_damage() + + if(!damage) + return + + if(enabled) + if(!attacked && !emagged) + attacked = 1 + spawn() + sleep(60) + attacked = FALSE + + ..() + + take_damage(damage) + +/obj/machinery/porta_turret/emp_act(severity) + if(enabled) + //if the turret is on, the EMP no matter how severe disables the turret for a while + //and scrambles its settings, with a slight chance of having an emag effect + check_arrest = prob(50) + check_records = prob(50) + check_weapons = prob(50) + check_access = prob(20) // check_access is a pretty big deal, so it's least likely to get turned on + check_anomalies = prob(50) + if(prob(5)) + emagged = TRUE + + enabled=0 + spawn(rand(60,600)) + if(!enabled) + enabled = TRUE + + ..() + +/obj/machinery/porta_turret/ai_defense/emp_act(severity) + if(prob(33)) // One in three chance to resist an EMP. This is significant if an AoE EMP is involved against multiple turrets. + return + ..() + +/obj/machinery/porta_turret/alien/emp_act(severity) // This is overrided to give an EMP resistance as well as avoid scambling the turret settings. + if(prob(75)) // Superior alien technology, I guess. + return + enabled = FALSE + spawn(rand(1 MINUTE, 2 MINUTES)) + if(!enabled) + enabled = TRUE + +/obj/machinery/porta_turret/ex_act(severity) + switch (severity) + if(1) + qdel(src) + if(2) + if(prob(25)) + qdel(src) + else + take_damage(initial(health) * 8) //should instakill most turrets + if(3) + take_damage(initial(health) * 8 / 3) //Level 4 is too weak to bother turrets + +/obj/machinery/porta_turret/proc/die() //called when the turret dies, ie, health <= 0 + health = 0 + stat |= BROKEN //enables the BROKEN bit + spark_system.start() //creates some sparks because they look cool + update_icon() + +/obj/machinery/porta_turret/process() + //the main machinery process + + if(stat & (NOPOWER|BROKEN)) + //if the turret has no power or is broken, make the turret pop down if it hasn't already + popDown() + return + + if(!enabled) + //if the turret is off, make it pop down + popDown() + return + + var/list/targets = list() //list of primary targets + var/list/secondarytargets = list() //targets that are least important + + var/list/seenturfs = list() + for(var/turf/T in oview(world.view, src)) + seenturfs += T + + for(var/mob/M as anything in living_mob_list) + if(M.z != z || !(get_turf(M) in seenturfs)) // Skip + continue + switch(assess_living(M)) + if(TURRET_PRIORITY_TARGET) + targets += M + if(TURRET_SECONDARY_TARGET) + secondarytargets += M + + for(var/obj/mecha/M as anything in mechas_list) + if(M.z != z || !(get_turf(M) in seenturfs)) // Skip + continue + switch(assess_mecha(M)) + if(TURRET_PRIORITY_TARGET) + targets += M + if(TURRET_SECONDARY_TARGET) + secondarytargets += M + + if(!tryToShootAt(targets) && !tryToShootAt(secondarytargets) && --timeout <= 0) + popDown() // no valid targets, close the cover + + if(auto_repair && (health < maxhealth)) + use_power(20000) + health = min(health+1, maxhealth) // 1HP for 20kJ + +/obj/machinery/porta_turret/proc/assess_living(var/mob/living/L) + if(!istype(L)) + return TURRET_NOT_TARGET + + if(L.invisibility >= INVISIBILITY_LEVEL_ONE) // Cannot see him. see_invisible is a mob-var + return TURRET_NOT_TARGET + + if(faction && L.faction == faction) + return TURRET_NOT_TARGET + + if(!emagged && issilicon(L) && check_all == FALSE) // Don't target silica, unless told to neutralize everything. + return TURRET_NOT_TARGET + + if(L.stat == DEAD && !emagged) //if the perp is dead, no need to bother really + return TURRET_NOT_TARGET //move onto next potential victim! + + if(get_dist(src, L) > 7) //if it's too far away, why bother? + return TURRET_NOT_TARGET + + if(!(L in check_trajectory(L, src))) //check if we have true line of sight + return TURRET_NOT_TARGET + + if(emagged) // If emagged not even the dead get a rest + return L.stat ? TURRET_SECONDARY_TARGET : TURRET_PRIORITY_TARGET + + if(lethal && locate(/mob/living/silicon/ai) in get_turf(L)) //don't accidentally kill the AI! + return TURRET_NOT_TARGET + + if(check_synth || check_all) //If it's set to attack all non-silicons or everything, target them! + if(L.lying) + return check_down ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET + return TURRET_PRIORITY_TARGET + + if(iscuffed(L)) // If the target is handcuffed, leave it alone + return TURRET_NOT_TARGET + + if(isanimal(L)) // Animals are not so dangerous + return check_anomalies ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET + + if(isxenomorph(L) || isalien(L)) // Xenos are dangerous + return check_anomalies ? TURRET_PRIORITY_TARGET : TURRET_NOT_TARGET + + if(ishuman(L)) //if the target is a human, analyze threat level + if(assess_perp(L) < 4) + return TURRET_NOT_TARGET //if threat level < 4, keep going + + if(L.lying) //if the perp is lying down, it's still a target but a less-important target + return check_down ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET + + return TURRET_PRIORITY_TARGET //if the perp has passed all previous tests, congrats, it is now a "shoot-me!" nominee + +/obj/machinery/porta_turret/proc/assess_mecha(var/obj/mecha/M) + if(!istype(M)) + return TURRET_NOT_TARGET + + if(!M.occupant) + return check_all ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET + + return assess_living(M.occupant) + +/obj/machinery/porta_turret/proc/assess_perp(var/mob/living/carbon/human/H) + if(!H || !istype(H)) + return 0 + + if(emagged) + return 10 + + return H.assess_perp(src, check_access, check_weapons, check_records, check_arrest) + +/obj/machinery/porta_turret/proc/tryToShootAt(var/list/mob/living/targets) + if(targets.len && last_target && (last_target in targets) && target(last_target)) + return 1 + + while(targets.len > 0) + var/mob/living/M = pick(targets) + targets -= M + if(target(M)) + return 1 + + +/obj/machinery/porta_turret/proc/popUp() //pops the turret up + set waitfor = FALSE + if(disabled) + return + if(raising || raised) + return + if(stat & BROKEN) + return + set_raised_raising(raised, 1) + update_icon() + + var/atom/flick_holder = new /atom/movable/porta_turret_cover(loc) + flick_holder.layer = layer + 0.1 + flick("popup_[turret_type]", flick_holder) + playsound(src, 'sound/machines/turrets/turret_deploy.ogg', 100, 1) + sleep(10) + qdel(flick_holder) + + set_raised_raising(1, 0) + update_icon() + timeout = 10 + +/obj/machinery/porta_turret/proc/popDown() //pops the turret down + set waitfor = FALSE + last_target = null + if(disabled) + return + if(raising || !raised) + return + if(stat & BROKEN) + return + set_raised_raising(raised, 1) + update_icon() + + var/atom/flick_holder = new /atom/movable/porta_turret_cover(loc) + flick_holder.layer = layer + 0.1 + flick("popdown_[turret_type]", flick_holder) + playsound(src, 'sound/machines/turrets/turret_retract.ogg', 100, 1) + sleep(10) + qdel(flick_holder) + + set_raised_raising(0, 0) + update_icon() + timeout = 10 + +/obj/machinery/porta_turret/proc/set_raised_raising(var/incoming_raised, var/incoming_raising) + raised = incoming_raised + raising = incoming_raising + density = raised || raising + +/obj/machinery/porta_turret/proc/target(var/mob/living/target) + if(disabled) + return FALSE + if(target) + last_target = target + popUp() //pop the turret up if it's not already up. + set_dir(get_dir(src, target)) //even if you can't shoot, follow the target + playsound(src, 'sound/machines/turrets/turret_rotate.ogg', 100, 1) // Play rotating sound + spawn() + shootAt(target) + return TRUE + return FALSE + +/obj/machinery/porta_turret/proc/shootAt(var/mob/living/target) + //any emagged turrets will shoot extremely fast! This not only is deadly, but drains a lot power! + if(!(emagged || attacked)) //if it hasn't been emagged or attacked, it has to obey a cooldown rate + if(last_fired || !raised) //prevents rapid-fire shooting, unless it's been emagged + return + last_fired = TRUE + spawn() + sleep(shot_delay) + last_fired = FALSE + + if(!isturf(get_turf(src)) || !isturf(get_turf(target))) + return + + if(!raised) //the turret has to be raised in order to fire - makes sense, right? + return + + update_icon() + var/obj/item/projectile/A + if(emagged || lethal) + A = new lethal_projectile(loc) + playsound(src, lethal_shot_sound, 75, 1) + else + A = new projectile(loc) + playsound(src, shot_sound, 75, 1) + + var/power_mult = 1 + if(emagged) + power_mult = 4 // Lethal beams + higher rate of fire + else if(lethal) + power_mult = 2 // Lethal beams + use_power(reqpower * power_mult) + + //Turrets aim for the center of mass by default. + //If the target is grabbing someone then the turret smartly aims for extremities + var/def_zone + var/obj/item/weapon/grab/G = locate() in target + if(G && G.state >= GRAB_NECK) //works because mobs are currently not allowed to upgrade to NECK if they are grabbing two people. + def_zone = pick(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG) + else + def_zone = pick(BP_TORSO, BP_GROIN) + + //Shooting Code: + A.firer = src + A.old_style_target(target) + A.launch_projectile_from_turf(target, def_zone, src) + + // Reset the time needed to go back down, since we just tried to shoot at someone. + timeout = 10 + +/datum/turret_checks + var/enabled + var/lethal + var/check_synth + var/check_access + var/check_records + var/check_arrest + var/check_weapons + var/check_anomalies + var/check_all + var/ailock + +/obj/machinery/porta_turret/proc/setState(var/datum/turret_checks/TC) + if(controllock) + return + enabled = TC.enabled + lethal = TC.lethal + + check_synth = TC.check_synth + check_access = TC.check_access + check_records = TC.check_records + check_arrest = TC.check_arrest + check_weapons = TC.check_weapons + check_anomalies = TC.check_anomalies + check_all = TC.check_all + ailock = TC.ailock + + power_change() + +/* + Portable turret constructions + Known as "turret frame"s +*/ + +/obj/machinery/porta_turret_construct + name = "turret frame" + icon = 'icons/obj/turrets.dmi' + icon_state = "turret_frame" + density=TRUE + var/target_type = /obj/machinery/porta_turret // The type we intend to build + var/build_step = 0 //the current step in the building process + var/finish_name="turret" //the name applied to the product turret + var/installation = null //the gun type installed + var/gun_charge = 0 //the gun charge of the gun type installed + +/obj/machinery/porta_turret_construct/attackby(obj/item/I, mob/user) + //this is a bit unwieldy but self-explanatory + switch(build_step) + if(0) //first step + if(I.has_tool_quality(TOOL_WRENCH) && !anchored) + playsound(src, I.usesound, 100, 1) + to_chat(user, "You secure the external bolts.") + anchored = TRUE + build_step = 1 + return + + else if(I.has_tool_quality(TOOL_CROWBAR) && !anchored) + playsound(src, I.usesound, 75, 1) + to_chat(user, "You dismantle the turret construction.") + new /obj/item/stack/material/steel(loc, 5) + qdel(src) + return + + if(1) + if(istype(I, /obj/item/stack/material) && I.get_material_name() == MAT_STEEL) + var/obj/item/stack/M = I + if(M.use(2)) + to_chat(user, "You add some metal armor to the interior frame.") + build_step = 2 + icon_state = "turret_frame2" + else + to_chat(user, "You need two sheets of metal to continue construction.") + return + + else if(I.has_tool_quality(TOOL_WRENCH)) + playsound(src, I.usesound, 75, 1) + to_chat(user, "You unfasten the external bolts.") + anchored = FALSE + build_step = 0 + return + + if(2) + if(I.has_tool_quality(TOOL_WRENCH)) + playsound(src, I.usesound, 100, 1) + to_chat(user, "You bolt the metal armor into place.") + build_step = 3 + return + + else if(I.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = I.get_welder() + if(!WT.isOn()) + return + if(WT.get_fuel() < 5) //uses up 5 fuel. + to_chat(user, "You need more fuel to complete this task.") + return + + playsound(src, I.usesound, 50, 1) + if(do_after(user, 20 * I.toolspeed)) + if(!src || !WT.remove_fuel(5, user)) return + build_step = 1 + to_chat(user, "You remove the turret's interior metal armor.") + new /obj/item/stack/material/steel(loc, 2) + return + + if(3) + if(istype(I, /obj/item/weapon/gun/energy)) //the gun installation part + + if(isrobot(user)) + return + var/obj/item/weapon/gun/energy/E = I //typecasts the item to an energy gun + if(!user.unEquip(I)) + to_chat(user, "\The [I] is stuck to your hand, you cannot put it in \the [src]") + return + installation = I.type //installation becomes I.type + gun_charge = E.power_supply.charge //the gun's charge is stored in gun_charge + to_chat(user, "You add [I] to the turret.") + target_type = /obj/machinery/porta_turret + + build_step = 4 + qdel(I) //delete the gun :( + return + + else if(I.has_tool_quality(TOOL_WRENCH)) + playsound(src, I.usesound, 100, 1) + to_chat(user, "You remove the turret's metal armor bolts.") + build_step = 2 + return + + if(4) + if(isprox(I)) + build_step = 5 + if(!user.unEquip(I)) + to_chat(user, "\The [I] is stuck to your hand, you cannot put it in \the [src]") + return + to_chat(user, "You add the prox sensor to the turret.") + qdel(I) + return + + //attack_hand() removes the gun + + if(5) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(src, I.usesound, 100, 1) + build_step = 6 + to_chat(user, "You close the internal access hatch.") + return + + //attack_hand() removes the prox sensor + + if(6) + if(istype(I, /obj/item/stack/material) && I.get_material_name() == MAT_STEEL) + var/obj/item/stack/M = I + if(M.use(2)) + to_chat(user, "You add some metal armor to the exterior frame.") + build_step = 7 + else + to_chat(user, "You need two sheets of metal to continue construction.") + return + + else if(I.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(src, I.usesound, 100, 1) + build_step = 5 + to_chat(user, "You open the internal access hatch.") + return + + if(7) + if(I.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = I.get_welder() + if(!WT.isOn()) return + if(WT.get_fuel() < 5) + to_chat(user, "You need more fuel to complete this task.") + + playsound(src, WT.usesound, 50, 1) + if(do_after(user, 30 * WT.toolspeed)) + if(!src || !WT.remove_fuel(5, user)) + return + build_step = 8 + to_chat(user, "You weld the turret's armor down.") + + //The final step: create a full turret + var/obj/machinery/porta_turret/Turret = new target_type(loc) + Turret.name = finish_name + Turret.installation = installation + Turret.gun_charge = gun_charge + Turret.enabled = 0 + Turret.setup() + + qdel(src) // qdel + + else if(I.has_tool_quality(TOOL_CROWBAR)) + playsound(src, I.usesound, 75, 1) + to_chat(user, "You pry off the turret's exterior armor.") + new /obj/item/stack/material/steel(loc, 2) + build_step = 6 + return + + if(istype(I, /obj/item/weapon/pen)) //you can rename turrets like bots! + var/t = sanitizeSafe(tgui_input_text(user, "Enter new turret name", name, finish_name, MAX_NAME_LEN), MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + + finish_name = t + return + + ..() + +/obj/machinery/porta_turret_construct/attack_hand(mob/user) + switch(build_step) + if(4) + if(!installation) + return + build_step = 3 + + var/obj/item/weapon/gun/energy/Gun = new installation(loc) + Gun.power_supply.charge = gun_charge + Gun.update_icon() + installation = null + gun_charge = 0 + to_chat(user, "You remove [Gun] from the turret frame.") + + if(5) + to_chat(user, "You remove the prox sensor from the turret frame.") + new /obj/item/device/assembly/prox_sensor(loc) + build_step = 4 + +/obj/machinery/porta_turret_construct/attack_ai() + return + +/atom/movable/porta_turret_cover + icon = 'icons/obj/turrets.dmi' + +#undef TURRET_PRIORITY_TARGET +#undef TURRET_SECONDARY_TARGET +#undef TURRET_NOT_TARGET diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index df9c78fad5b..31bc167040f 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -1,355 +1,355 @@ -/obj/machinery/recharge_station - name = "cyborg recharging station" - desc = "A heavy duty rapid charging system, designed to quickly recharge cyborg power reserves." - icon = 'icons/obj/objects.dmi' - icon_state = "borgcharger0" - density = TRUE - anchored = TRUE - unacidable = TRUE - circuit = /obj/item/weapon/circuitboard/recharge_station - use_power = USE_POWER_IDLE - idle_power_usage = 50 - var/mob/occupant = null - var/obj/item/weapon/cell/cell = null - var/icon_update_tick = 0 // Used to rebuild the overlay only once every 10 ticks - var/charging = 0 - - var/charging_power // W. Power rating used for charging the cyborg. 120 kW if un-upgraded - var/restore_power_active // W. Power drawn from APC when an occupant is charging. 40 kW if un-upgraded - var/restore_power_passive // W. Power drawn from APC when idle. 7 kW if un-upgraded - var/weld_rate = 0 // How much brute damage is repaired per tick - var/wire_rate = 0 // How much burn damage is repaired per tick - - var/weld_power_use = 2300 // power used per point of brute damage repaired. 2.3 kW ~ about the same power usage of a handheld arc welder - var/wire_power_use = 500 // power used per point of burn damage repaired. - -/obj/machinery/recharge_station/Initialize() - . = ..() - default_apply_parts() - cell = default_use_hicell() - update_icon() - -/obj/machinery/recharge_station/proc/has_cell_power() - return cell && cell.percent() > 0 - -/obj/machinery/recharge_station/process() - if(stat & (BROKEN)) - return - if(!cell) // Shouldn't be possible, but sanity check - return - - if((stat & NOPOWER) && !has_cell_power()) // No power and cell is dead. - if(icon_update_tick) - icon_update_tick = 0 //just rebuild the overlay once more only - update_icon() - return - - //First, draw from the internal power cell to recharge/repair/etc the occupant - if(occupant) - process_occupant() - - //Then, if external power is available, recharge the internal cell - var/recharge_amount = 0 - if(!(stat & NOPOWER)) - // Calculating amount of power to draw - recharge_amount = (occupant ? restore_power_active : restore_power_passive) * CELLRATE - - recharge_amount = cell.give(recharge_amount) - use_power(recharge_amount / CELLRATE) - else - // Since external power is offline, draw operating current from the internal cell - cell.use(get_power_usage() * CELLRATE) - - if(icon_update_tick >= 10) - icon_update_tick = 0 - else - icon_update_tick++ - - if(occupant || recharge_amount) - update_icon() - -//Processes the occupant, drawing from the internal power cell if needed. -/obj/machinery/recharge_station/proc/process_occupant() - if(isrobot(occupant)) - var/mob/living/silicon/robot/R = occupant - var/overcharged = FALSE - if(R.cell.maxcharge < R.cell.charge) - overcharged = TRUE - if(R.module && !overcharged) - R.module.respawn_consumable(R, charging_power * CELLRATE / 250) //consumables are magical, apparently - if(R.cell && !R.cell.fully_charged() && !overcharged) - var/diff = min(R.cell.maxcharge - R.cell.charge, charging_power * CELLRATE) // Capped by charging_power / tick - var/charge_used = cell.use(diff) - R.cell.give(charge_used) - - //Lastly, attempt to repair the cyborg if enabled - if(weld_rate && R.getBruteLoss() && cell.checked_use(weld_power_use * weld_rate * CELLRATE)) - R.adjustBruteLoss(-weld_rate) - if(wire_rate && R.getFireLoss() && cell.checked_use(wire_power_use * wire_rate * CELLRATE)) - R.adjustFireLoss(-wire_rate) - - //VOREStation Add Start - else if(ispAI(occupant)) - var/mob/living/silicon/pai/P = occupant - - if(P.nutrition < 400) - P.nutrition = min(P.nutrition+10, 400) - cell.use(7000/450*10) - //VOREStation Add End - - else if(ishuman(occupant)) - var/mob/living/carbon/human/H = occupant - - if(H.isSynthetic()) - // In case they somehow end up with positive values for otherwise unobtainable damage... - if(H.getToxLoss() > 0) - H.adjustToxLoss(-(rand(1,3))) - if(H.getOxyLoss() > 0) - H.adjustOxyLoss(-(rand(1,3))) - if(H.getCloneLoss() > 0) - H.adjustCloneLoss(-(rand(1,3))) - if(H.getBrainLoss() > 0) - H.adjustBrainLoss(-(rand(1,3))) - - // Also recharge their internal battery. - if(H.isSynthetic() && H.nutrition < 500) //VOREStation Edit - H.nutrition = min(H.nutrition+(10*(1-min(H.species.synthetic_food_coeff, 0.9))), 500) //VOREStation Edit - cell.use(7000/450*10) - - // And clear up radiation - if(H.radiation > 0 || H.accumulated_rads > 0) - H.radiation = max(H.radiation - 25, 0) - H.accumulated_rads = max(H.accumulated_rads - 25, 0) - - if(H.wearing_rig) // stepping into a borg charger to charge your rig and fix your shit - var/obj/item/weapon/rig/wornrig = H.get_rig() - if(wornrig) // just to make sure - for(var/obj/item/rig_module/storedmod in wornrig.installed_modules) - if(weld_rate && storedmod.damage && cell.checked_use(weld_power_use * weld_rate * CELLRATE)) - to_chat(H, "[storedmod] is repaired!") - storedmod.damage = 0 - if(wornrig.chest) - var/obj/item/clothing/suit/space/rig/rigchest = wornrig.chest - if(weld_rate && rigchest.damage && cell.checked_use(weld_power_use * weld_rate * CELLRATE)) - rigchest.breaches = list() - rigchest.calc_breach_damage() - to_chat(H, "[rigchest] is repaired!") - if(wornrig.cell) - var/obj/item/weapon/cell/rigcell = wornrig.cell - var/diff = min(rigcell.maxcharge - rigcell.charge, charging_power * CELLRATE) // Capped by charging_power / tick - var/charge_used = cell.use(diff) - rigcell.give(charge_used) - -/obj/machinery/recharge_station/examine(mob/user) - . = ..() - . += "The charge meter reads: [round(chargepercentage())]%" - -/obj/machinery/recharge_station/proc/chargepercentage() - if(!cell) - return 0 - return cell.percent() - -/obj/machinery/recharge_station/relaymove(mob/user as mob) - if(user.stat) - return - go_out() - return - -/obj/machinery/recharge_station/emp_act(severity) - if(occupant) - occupant.emp_act(severity) - go_out() - if(cell) - cell.emp_act(severity) - ..(severity) - -/obj/machinery/recharge_station/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(!occupant) - if(default_deconstruction_screwdriver(user, O)) - return - if(default_deconstruction_crowbar(user, O)) - return - if(default_part_replacement(user, O)) - return - if (istype(O, /obj/item/weapon/grab) && get_dist(src,user)<2) - var/obj/item/weapon/grab/G = O - if(istype(G.affecting,/mob/living)) - var/mob/living/M = G.affecting - qdel(O) - go_in(M) - - ..() - -/obj/machinery/recharge_station/MouseDrop_T(var/mob/target, var/mob/user) - if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)) - return - - go_in(target) - -/obj/machinery/recharge_station/RefreshParts() - ..() - var/man_rating = 0 - var/cap_rating = 0 - - for(var/obj/item/weapon/stock_parts/P in component_parts) - if(istype(P, /obj/item/weapon/stock_parts/capacitor)) - cap_rating += P.rating - if(istype(P, /obj/item/weapon/stock_parts/manipulator)) - man_rating += P.rating - cell = locate(/obj/item/weapon/cell) in component_parts - - charging_power = 40000 + 40000 * cap_rating - restore_power_active = 10000 + 15000 * cap_rating - restore_power_passive = 5000 + 1000 * cap_rating - weld_rate = max(0, man_rating - 3) - wire_rate = max(0, man_rating - 5) - - desc = initial(desc) - desc += " Uses a dedicated internal power cell to deliver [charging_power]W when in use." - if(weld_rate) - desc += "
                    It is capable of repairing structural damage." - if(wire_rate) - desc += "
                    It is capable of repairing burn damage." - -/obj/machinery/recharge_station/proc/build_overlays() - cut_overlays() - switch(round(chargepercentage())) - if(1 to 20) - add_overlay("statn_c0") - if(21 to 40) - add_overlay("statn_c20") - if(41 to 60) - add_overlay("statn_c40") - if(61 to 80) - add_overlay("statn_c60") - if(81 to 98) - add_overlay("statn_c80") - if(99 to 110) - add_overlay("statn_c100") - -/obj/machinery/recharge_station/update_icon() - ..() - if(stat & BROKEN) - icon_state = "borgcharger0" - return - - if(occupant) - if((stat & NOPOWER) && !has_cell_power()) - icon_state = "borgcharger2" - else - icon_state = "borgcharger1" - else - icon_state = "borgcharger0" - - if(icon_update_tick == 0) - build_overlays() - -/obj/machinery/recharge_station/Bumped(var/mob/living/L) - go_in(L) - -/obj/machinery/recharge_station/proc/go_in(var/mob/living/L) - - if(occupant) - return - - if(istype(L, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = L - - if(R.incapacitated()) - return - - if(!R.cell) - return - - if(istype(R, /mob/living/silicon/robot/platform)) - to_chat(R, SPAN_WARNING("You are too large to fit into \the [src].")) - return - - add_fingerprint(R) - R.reset_view(src) - R.forceMove(src) - occupant = R - update_icon() - return 1 - - //VOREStation Add Start - else if(istype(L, /mob/living/silicon/pai)) - var/mob/living/silicon/pai/P = L - - if(P.incapacitated()) - return - - add_fingerprint(P) - P.reset_view(src) - P.forceMove(src) - occupant = P - update_icon() - return 1 - //VOREStation Add End - - else if(istype(L, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = L - if(H.isSynthetic() || H.wearing_rig) - add_fingerprint(H) - H.reset_view(src) - H.forceMove(src) - occupant = H - update_icon() - return 1 - else - return - -/obj/machinery/recharge_station/proc/go_out() - if(!occupant) - return - - occupant.forceMove(src.loc) - occupant.reset_view() - occupant = null - update_icon() - -/obj/machinery/recharge_station/verb/move_eject() - set category = "Object" - set name = "Eject Recharger" - set src in oview(1) - - if(usr.incapacitated() || !isliving(usr)) - return - - go_out() - add_fingerprint(usr) - return - -/obj/machinery/recharge_station/verb/move_inside() - set category = "Object" - set name = "Enter Recharger" - set src in oview(1) - - if(usr.incapacitated() || !isliving(usr)) - return - - go_in(usr) - -/obj/machinery/recharge_station/ghost_pod_recharger - name = "drone pod" - desc = "This is a pod which used to contain a drone... Or maybe it still does?" - icon = 'icons/obj/structures.dmi' - -/obj/machinery/recharge_station/ghost_pod_recharger/update_icon() - ..() - if(stat & BROKEN) - icon_state = "borg_pod_closed" - desc = "It appears broken..." - return - - if(occupant) - if((stat & NOPOWER) && !has_cell_power()) - icon_state = "borg_pod_closed" - desc = "It appears to be unpowered..." - else - icon_state = "borg_pod_closed" - else - icon_state = "borg_pod_opened" - - if(icon_update_tick == 0) - build_overlays() +/obj/machinery/recharge_station + name = "cyborg recharging station" + desc = "A heavy duty rapid charging system, designed to quickly recharge cyborg power reserves." + icon = 'icons/obj/objects.dmi' + icon_state = "borgcharger0" + density = TRUE + anchored = TRUE + unacidable = TRUE + circuit = /obj/item/weapon/circuitboard/recharge_station + use_power = USE_POWER_IDLE + idle_power_usage = 50 + var/mob/occupant = null + var/obj/item/weapon/cell/cell = null + var/icon_update_tick = 0 // Used to rebuild the overlay only once every 10 ticks + var/charging = 0 + + var/charging_power // W. Power rating used for charging the cyborg. 120 kW if un-upgraded + var/restore_power_active // W. Power drawn from APC when an occupant is charging. 40 kW if un-upgraded + var/restore_power_passive // W. Power drawn from APC when idle. 7 kW if un-upgraded + var/weld_rate = 0 // How much brute damage is repaired per tick + var/wire_rate = 0 // How much burn damage is repaired per tick + + var/weld_power_use = 2300 // power used per point of brute damage repaired. 2.3 kW ~ about the same power usage of a handheld arc welder + var/wire_power_use = 500 // power used per point of burn damage repaired. + +/obj/machinery/recharge_station/Initialize() + . = ..() + default_apply_parts() + cell = default_use_hicell() + update_icon() + +/obj/machinery/recharge_station/proc/has_cell_power() + return cell && cell.percent() > 0 + +/obj/machinery/recharge_station/process() + if(stat & (BROKEN)) + return + if(!cell) // Shouldn't be possible, but sanity check + return + + if((stat & NOPOWER) && !has_cell_power()) // No power and cell is dead. + if(icon_update_tick) + icon_update_tick = 0 //just rebuild the overlay once more only + update_icon() + return + + //First, draw from the internal power cell to recharge/repair/etc the occupant + if(occupant) + process_occupant() + + //Then, if external power is available, recharge the internal cell + var/recharge_amount = 0 + if(!(stat & NOPOWER)) + // Calculating amount of power to draw + recharge_amount = (occupant ? restore_power_active : restore_power_passive) * CELLRATE + + recharge_amount = cell.give(recharge_amount) + use_power(recharge_amount / CELLRATE) + else + // Since external power is offline, draw operating current from the internal cell + cell.use(get_power_usage() * CELLRATE) + + if(icon_update_tick >= 10) + icon_update_tick = 0 + else + icon_update_tick++ + + if(occupant || recharge_amount) + update_icon() + +//Processes the occupant, drawing from the internal power cell if needed. +/obj/machinery/recharge_station/proc/process_occupant() + if(isrobot(occupant)) + var/mob/living/silicon/robot/R = occupant + var/overcharged = FALSE + if(R.cell.maxcharge < R.cell.charge) + overcharged = TRUE + if(R.module && !overcharged) + R.module.respawn_consumable(R, charging_power * CELLRATE / 250) //consumables are magical, apparently + if(R.cell && !R.cell.fully_charged() && !overcharged) + var/diff = min(R.cell.maxcharge - R.cell.charge, charging_power * CELLRATE) // Capped by charging_power / tick + var/charge_used = cell.use(diff) + R.cell.give(charge_used) + + //Lastly, attempt to repair the cyborg if enabled + if(weld_rate && R.getBruteLoss() && cell.checked_use(weld_power_use * weld_rate * CELLRATE)) + R.adjustBruteLoss(-weld_rate) + if(wire_rate && R.getFireLoss() && cell.checked_use(wire_power_use * wire_rate * CELLRATE)) + R.adjustFireLoss(-wire_rate) + + //VOREStation Add Start + else if(ispAI(occupant)) + var/mob/living/silicon/pai/P = occupant + + if(P.nutrition < 400) + P.nutrition = min(P.nutrition+10, 400) + cell.use(7000/450*10) + //VOREStation Add End + + else if(ishuman(occupant)) + var/mob/living/carbon/human/H = occupant + + if(H.isSynthetic()) + // In case they somehow end up with positive values for otherwise unobtainable damage... + if(H.getToxLoss() > 0) + H.adjustToxLoss(-(rand(1,3))) + if(H.getOxyLoss() > 0) + H.adjustOxyLoss(-(rand(1,3))) + if(H.getCloneLoss() > 0) + H.adjustCloneLoss(-(rand(1,3))) + if(H.getBrainLoss() > 0) + H.adjustBrainLoss(-(rand(1,3))) + + // Also recharge their internal battery. + if(H.isSynthetic() && H.nutrition < 500) //VOREStation Edit + H.nutrition = min(H.nutrition+(10*(1-min(H.species.synthetic_food_coeff, 0.9))), 500) //VOREStation Edit + cell.use(7000/450*10) + + // And clear up radiation + if(H.radiation > 0 || H.accumulated_rads > 0) + H.radiation = max(H.radiation - 25, 0) + H.accumulated_rads = max(H.accumulated_rads - 25, 0) + + if(H.wearing_rig) // stepping into a borg charger to charge your rig and fix your shit + var/obj/item/weapon/rig/wornrig = H.get_rig() + if(wornrig) // just to make sure + for(var/obj/item/rig_module/storedmod in wornrig.installed_modules) + if(weld_rate && storedmod.damage && cell.checked_use(weld_power_use * weld_rate * CELLRATE)) + to_chat(H, "[storedmod] is repaired!") + storedmod.damage = 0 + if(wornrig.chest) + var/obj/item/clothing/suit/space/rig/rigchest = wornrig.chest + if(weld_rate && rigchest.damage && cell.checked_use(weld_power_use * weld_rate * CELLRATE)) + rigchest.breaches = list() + rigchest.calc_breach_damage() + to_chat(H, "[rigchest] is repaired!") + if(wornrig.cell) + var/obj/item/weapon/cell/rigcell = wornrig.cell + var/diff = min(rigcell.maxcharge - rigcell.charge, charging_power * CELLRATE) // Capped by charging_power / tick + var/charge_used = cell.use(diff) + rigcell.give(charge_used) + +/obj/machinery/recharge_station/examine(mob/user) + . = ..() + . += "The charge meter reads: [round(chargepercentage())]%" + +/obj/machinery/recharge_station/proc/chargepercentage() + if(!cell) + return 0 + return cell.percent() + +/obj/machinery/recharge_station/relaymove(mob/user as mob) + if(user.stat) + return + go_out() + return + +/obj/machinery/recharge_station/emp_act(severity) + if(occupant) + occupant.emp_act(severity) + go_out() + if(cell) + cell.emp_act(severity) + ..(severity) + +/obj/machinery/recharge_station/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(!occupant) + if(default_deconstruction_screwdriver(user, O)) + return + if(default_deconstruction_crowbar(user, O)) + return + if(default_part_replacement(user, O)) + return + if (istype(O, /obj/item/weapon/grab) && get_dist(src,user)<2) + var/obj/item/weapon/grab/G = O + if(istype(G.affecting,/mob/living)) + var/mob/living/M = G.affecting + qdel(O) + go_in(M) + + ..() + +/obj/machinery/recharge_station/MouseDrop_T(var/mob/target, var/mob/user) + if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)) + return + + go_in(target) + +/obj/machinery/recharge_station/RefreshParts() + ..() + var/man_rating = 0 + var/cap_rating = 0 + + for(var/obj/item/weapon/stock_parts/P in component_parts) + if(istype(P, /obj/item/weapon/stock_parts/capacitor)) + cap_rating += P.rating + if(istype(P, /obj/item/weapon/stock_parts/manipulator)) + man_rating += P.rating + cell = locate(/obj/item/weapon/cell) in component_parts + + charging_power = 40000 + 40000 * cap_rating + restore_power_active = 10000 + 15000 * cap_rating + restore_power_passive = 5000 + 1000 * cap_rating + weld_rate = max(0, man_rating - 3) + wire_rate = max(0, man_rating - 5) + + desc = initial(desc) + desc += " Uses a dedicated internal power cell to deliver [charging_power]W when in use." + if(weld_rate) + desc += "
                    It is capable of repairing structural damage." + if(wire_rate) + desc += "
                    It is capable of repairing burn damage." + +/obj/machinery/recharge_station/proc/build_overlays() + cut_overlays() + switch(round(chargepercentage())) + if(1 to 20) + add_overlay("statn_c0") + if(21 to 40) + add_overlay("statn_c20") + if(41 to 60) + add_overlay("statn_c40") + if(61 to 80) + add_overlay("statn_c60") + if(81 to 98) + add_overlay("statn_c80") + if(99 to 110) + add_overlay("statn_c100") + +/obj/machinery/recharge_station/update_icon() + ..() + if(stat & BROKEN) + icon_state = "borgcharger0" + return + + if(occupant) + if((stat & NOPOWER) && !has_cell_power()) + icon_state = "borgcharger2" + else + icon_state = "borgcharger1" + else + icon_state = "borgcharger0" + + if(icon_update_tick == 0) + build_overlays() + +/obj/machinery/recharge_station/Bumped(var/mob/living/L) + go_in(L) + +/obj/machinery/recharge_station/proc/go_in(var/mob/living/L) + + if(occupant) + return + + if(istype(L, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = L + + if(R.incapacitated()) + return + + if(!R.cell) + return + + if(istype(R, /mob/living/silicon/robot/platform)) + to_chat(R, SPAN_WARNING("You are too large to fit into \the [src].")) + return + + add_fingerprint(R) + R.reset_view(src) + R.forceMove(src) + occupant = R + update_icon() + return 1 + + //VOREStation Add Start + else if(istype(L, /mob/living/silicon/pai)) + var/mob/living/silicon/pai/P = L + + if(P.incapacitated()) + return + + add_fingerprint(P) + P.reset_view(src) + P.forceMove(src) + occupant = P + update_icon() + return 1 + //VOREStation Add End + + else if(istype(L, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = L + if(H.isSynthetic() || H.wearing_rig) + add_fingerprint(H) + H.reset_view(src) + H.forceMove(src) + occupant = H + update_icon() + return 1 + else + return + +/obj/machinery/recharge_station/proc/go_out() + if(!occupant) + return + + occupant.forceMove(src.loc) + occupant.reset_view() + occupant = null + update_icon() + +/obj/machinery/recharge_station/verb/move_eject() + set category = "Object" + set name = "Eject Recharger" + set src in oview(1) + + if(usr.incapacitated() || !isliving(usr)) + return + + go_out() + add_fingerprint(usr) + return + +/obj/machinery/recharge_station/verb/move_inside() + set category = "Object" + set name = "Enter Recharger" + set src in oview(1) + + if(usr.incapacitated() || !isliving(usr)) + return + + go_in(usr) + +/obj/machinery/recharge_station/ghost_pod_recharger + name = "drone pod" + desc = "This is a pod which used to contain a drone... Or maybe it still does?" + icon = 'icons/obj/structures.dmi' + +/obj/machinery/recharge_station/ghost_pod_recharger/update_icon() + ..() + if(stat & BROKEN) + icon_state = "borg_pod_closed" + desc = "It appears broken..." + return + + if(occupant) + if((stat & NOPOWER) && !has_cell_power()) + icon_state = "borg_pod_closed" + desc = "It appears to be unpowered..." + else + icon_state = "borg_pod_closed" + else + icon_state = "borg_pod_opened" + + if(icon_update_tick == 0) + build_overlays() diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index 6b6fc6df6a7..9e9e8786468 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -1,291 +1,291 @@ -/******************** Requests Console ********************/ -/** Originally written by errorage, updated by: Carn, needs more work though. I just added some security fixes */ - -//Request Console Department Types -#define RC_ASSIST 1 //Request Assistance -#define RC_SUPPLY 2 //Request Supplies -#define RC_INFO 4 //Relay Info - -//Request Console Screens -#define RCS_MAINMENU 0 // Main menu -#define RCS_RQASSIST 1 // Request supplies -#define RCS_RQSUPPLY 2 // Request assistance -#define RCS_SENDINFO 3 // Relay information -#define RCS_SENTPASS 4 // Message sent successfully -#define RCS_SENTFAIL 5 // Message sent unsuccessfully -#define RCS_VIEWMSGS 6 // View messages -#define RCS_MESSAUTH 7 // Authentication before sending -#define RCS_ANNOUNCE 8 // Send announcement - -var/req_console_assistance = list() -var/req_console_supplies = list() -var/req_console_information = list() -var/list/obj/machinery/requests_console/allConsoles = list() - -/obj/machinery/requests_console - name = "requests console" - desc = "A console intended to send requests to different departments on the station." - anchored = TRUE - icon = 'icons/obj/terminals_vr.dmi' //VOREStation Edit - icon_state = "req_comp_0" - layer = ABOVE_WINDOW_LAYER - circuit = /obj/item/weapon/circuitboard/request - blocks_emissive = NONE - light_power = 0.25 - light_color = "#00ff00" - vis_flags = VIS_HIDE // They have an emissive that looks bad in openspace due to their wall-mounted nature - var/department = "Unknown" //The list of all departments on the station (Determined from this variable on each unit) Set this to the same thing if you want several consoles in one department - var/list/message_log = list() //List of all messages - var/departmentType = 0 //Bitflag. Zero is reply-only. Map currently uses raw numbers instead of defines. - var/newmessagepriority = 0 - // 0 = no new message - // 1 = normal priority - // 2 = high priority - var/screen = RCS_VIEWMSGS - var/silent = 0 // set to 1 for it not to beep all the time -// var/hackState = 0 - // 0 = not hacked - // 1 = hacked - var/announcementConsole = 0 - // 0 = This console cannot be used to send department announcements - // 1 = This console can send department announcementsf - var/open = 0 // 1 if open - var/announceAuth = 0 //Will be set to 1 when you authenticate yourself for announcements - var/msgVerified = "" //Will contain the name of the person who varified it - var/msgStamped = "" //If a message is stamped, this will contain the stamp name - var/message = ""; - var/recipient = ""; //the department which will be receiving the message - var/priority = -1 ; //Priority of the message being sent - light_range = 0 - var/datum/announcement/announcement = new - -/obj/machinery/requests_console/Initialize() - . = ..() - - announcement.title = "[department] announcement" - announcement.newscast = 1 - - name = "[department] requests console" - allConsoles += src - if(departmentType & RC_ASSIST) - req_console_assistance |= department - if(departmentType & RC_SUPPLY) - req_console_supplies |= department - if(departmentType & RC_INFO) - req_console_information |= department - - update_icon() - -/obj/machinery/requests_console/Destroy() - allConsoles -= src - var/lastDeptRC = 1 - for (var/obj/machinery/requests_console/Console in allConsoles) - if(Console.department == department) - lastDeptRC = 0 - break - if(lastDeptRC) - if(departmentType & RC_ASSIST) - req_console_assistance -= department - if(departmentType & RC_SUPPLY) - req_console_supplies -= department - if(departmentType & RC_INFO) - req_console_information -= department - return ..() - -/obj/machinery/requests_console/power_change() - ..() - update_icon() - -/obj/machinery/requests_console/update_icon() - cut_overlays() - - if(stat & NOPOWER) - set_light(0) - set_light_on(FALSE) - icon_state = "req_comp_off" - else - icon_state = "req_comp_[newmessagepriority]" - add_overlay(mutable_appearance(icon, "req_comp_ov[newmessagepriority]")) - add_overlay(emissive_appearance(icon, "req_comp_ov[newmessagepriority]")) - set_light(2) - set_light_on(TRUE) - -/obj/machinery/requests_console/attack_hand(user as mob) - if(..(user)) - return - tgui_interact(user) - -/obj/machinery/requests_console/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "RequestConsole", "[department] Request Console") - ui.open() - -/obj/machinery/requests_console/tgui_data(mob/user) - var/list/data = ..() - data["department"] = department - data["screen"] = screen - data["message_log"] = message_log - data["newmessagepriority"] = newmessagepriority - data["silent"] = silent - data["announcementConsole"] = announcementConsole - - data["assist_dept"] = req_console_assistance - data["supply_dept"] = req_console_supplies - data["info_dept"] = req_console_information - - data["message"] = message - data["recipient"] = recipient - data["priority"] = priority - data["msgStamped"] = msgStamped - data["msgVerified"] = msgVerified - data["announceAuth"] = announceAuth - return data - -/obj/machinery/requests_console/tgui_act(action, list/params) - if(..()) - return TRUE - - add_fingerprint(usr) - - switch(action) - if("write") - if(reject_bad_text(params["write"])) - recipient = params["write"] //write contains the string of the receiving department's name - - var/new_message = sanitize(tgui_input_text(usr, "Write your message:", "Awaiting Input", "")) - if(new_message) - message = new_message - screen = RCS_MESSAUTH - switch(params["priority"]) - if(1) - priority = 1 - if(2) - priority = 2 - else - priority = 0 - else - reset_message(1) - . = TRUE - - if("writeAnnouncement") - var/new_message = sanitize(tgui_input_text(usr, "Write your message:", "Awaiting Input", "")) - if(new_message) - message = new_message - else - reset_message(1) - . = TRUE - - if("sendAnnouncement") - if(!announcementConsole) - return FALSE - announcement.Announce(message, msg_sanitized = 1) - reset_message(1) - . = TRUE - - if("department") - if(!message) - return FALSE - var/log_msg = message - var/pass = 0 - screen = RCS_SENTFAIL - for(var/obj/machinery/message_server/MS in machines) - if(!MS.active) - continue - MS.send_rc_message(ckey(params["department"]), department, log_msg, msgStamped, msgVerified, priority) - pass = 1 - if(pass) - screen = RCS_SENTPASS - message_log += list(list("Message sent to [recipient]", "[message]")) - else - audible_message(text("\icon[src][bicon(src)] *The Requests Console beeps: 'NOTICE: No server detected!'"),,4) - . = TRUE - - //Handle printing - if("print") - var/msg = message_log[text2num(params["print"])]; - if(msg) - msg = "[msg[1]]:
                    [msg[2]]" - msg = replacetext(msg, "
                    ", "\n") - msg = strip_html_properly(msg) - var/obj/item/weapon/paper/R = new(src.loc) - R.name = "[department] Message" - R.info = "

                    [department] Requests Console

                    [msg]
                    " - . = TRUE - - //Handle screen switching - if("setScreen") - var/tempScreen = text2num(params["setScreen"]) - if(tempScreen == RCS_ANNOUNCE && !announcementConsole) - return - if(tempScreen == RCS_VIEWMSGS) - for (var/obj/machinery/requests_console/Console in allConsoles) - if(Console.department == department) - Console.newmessagepriority = 0 - Console.update_icon() - if(tempScreen == RCS_MAINMENU) - reset_message() - screen = tempScreen - . = TRUE - - //Handle silencing the console - if("toggleSilent") - silent = !silent - . = TRUE - - //err... hacking code, which has no reason for existing... but anyway... it was once supposed to unlock priority 3 messaging on that console (EXTREME priority...), but the code for that was removed. -/obj/machinery/requests_console/attackby(var/obj/item/weapon/O as obj, var/mob/user as mob) - if(computer_deconstruction_screwdriver(user, O)) - return - if(istype(O, /obj/item/device/multitool)) - var/input = sanitize(tgui_input_text(usr, "What Department ID would you like to give this request console?", "Multitool-Request Console Interface", department)) - if(!input) - to_chat(usr, "No input found. Please hang up and try your call again.") - return - department = input - announcement.title = "[department] announcement" - announcement.newscast = 1 - - name = "[department] Requests Console" - allConsoles += src - if(departmentType & RC_ASSIST) - req_console_assistance |= department - if(departmentType & RC_SUPPLY) - req_console_supplies |= department - if(departmentType & RC_INFO) - req_console_information |= department - return - - if(istype(O, /obj/item/weapon/card/id)) - if(inoperable(MAINT)) return - if(screen == RCS_MESSAUTH) - var/obj/item/weapon/card/id/T = O - msgVerified = text("Verified by [T.registered_name] ([T.assignment])") - SStgui.update_uis(src) - if(screen == RCS_ANNOUNCE) - var/obj/item/weapon/card/id/ID = O - if(access_RC_announce in ID.GetAccess()) - announceAuth = 1 - announcement.announcer = ID.assignment ? "[ID.assignment] [ID.registered_name]" : ID.registered_name - else - reset_message() - to_chat(user, "You are not authorized to send announcements.") - SStgui.update_uis(src) - if(istype(O, /obj/item/weapon/stamp)) - if(inoperable(MAINT)) return - if(screen == RCS_MESSAUTH) - var/obj/item/weapon/stamp/T = O - msgStamped = text("Stamped with the [T.name]") - SStgui.update_uis(src) - return - -/obj/machinery/requests_console/proc/reset_message(var/mainmenu = 0) - message = "" - recipient = "" - priority = 0 - msgVerified = "" - msgStamped = "" - announceAuth = 0 - announcement.announcer = "" - if(mainmenu) - screen = RCS_MAINMENU +/******************** Requests Console ********************/ +/** Originally written by errorage, updated by: Carn, needs more work though. I just added some security fixes */ + +//Request Console Department Types +#define RC_ASSIST 1 //Request Assistance +#define RC_SUPPLY 2 //Request Supplies +#define RC_INFO 4 //Relay Info + +//Request Console Screens +#define RCS_MAINMENU 0 // Main menu +#define RCS_RQASSIST 1 // Request supplies +#define RCS_RQSUPPLY 2 // Request assistance +#define RCS_SENDINFO 3 // Relay information +#define RCS_SENTPASS 4 // Message sent successfully +#define RCS_SENTFAIL 5 // Message sent unsuccessfully +#define RCS_VIEWMSGS 6 // View messages +#define RCS_MESSAUTH 7 // Authentication before sending +#define RCS_ANNOUNCE 8 // Send announcement + +var/req_console_assistance = list() +var/req_console_supplies = list() +var/req_console_information = list() +var/list/obj/machinery/requests_console/allConsoles = list() + +/obj/machinery/requests_console + name = "requests console" + desc = "A console intended to send requests to different departments on the station." + anchored = TRUE + icon = 'icons/obj/terminals_vr.dmi' //VOREStation Edit + icon_state = "req_comp_0" + layer = ABOVE_WINDOW_LAYER + circuit = /obj/item/weapon/circuitboard/request + blocks_emissive = NONE + light_power = 0.25 + light_color = "#00ff00" + vis_flags = VIS_HIDE // They have an emissive that looks bad in openspace due to their wall-mounted nature + var/department = "Unknown" //The list of all departments on the station (Determined from this variable on each unit) Set this to the same thing if you want several consoles in one department + var/list/message_log = list() //List of all messages + var/departmentType = 0 //Bitflag. Zero is reply-only. Map currently uses raw numbers instead of defines. + var/newmessagepriority = 0 + // 0 = no new message + // 1 = normal priority + // 2 = high priority + var/screen = RCS_VIEWMSGS + var/silent = 0 // set to 1 for it not to beep all the time +// var/hackState = 0 + // 0 = not hacked + // 1 = hacked + var/announcementConsole = 0 + // 0 = This console cannot be used to send department announcements + // 1 = This console can send department announcementsf + var/open = 0 // 1 if open + var/announceAuth = 0 //Will be set to 1 when you authenticate yourself for announcements + var/msgVerified = "" //Will contain the name of the person who varified it + var/msgStamped = "" //If a message is stamped, this will contain the stamp name + var/message = ""; + var/recipient = ""; //the department which will be receiving the message + var/priority = -1 ; //Priority of the message being sent + light_range = 0 + var/datum/announcement/announcement = new + +/obj/machinery/requests_console/Initialize() + . = ..() + + announcement.title = "[department] announcement" + announcement.newscast = 1 + + name = "[department] requests console" + allConsoles += src + if(departmentType & RC_ASSIST) + req_console_assistance |= department + if(departmentType & RC_SUPPLY) + req_console_supplies |= department + if(departmentType & RC_INFO) + req_console_information |= department + + update_icon() + +/obj/machinery/requests_console/Destroy() + allConsoles -= src + var/lastDeptRC = 1 + for (var/obj/machinery/requests_console/Console in allConsoles) + if(Console.department == department) + lastDeptRC = 0 + break + if(lastDeptRC) + if(departmentType & RC_ASSIST) + req_console_assistance -= department + if(departmentType & RC_SUPPLY) + req_console_supplies -= department + if(departmentType & RC_INFO) + req_console_information -= department + return ..() + +/obj/machinery/requests_console/power_change() + ..() + update_icon() + +/obj/machinery/requests_console/update_icon() + cut_overlays() + + if(stat & NOPOWER) + set_light(0) + set_light_on(FALSE) + icon_state = "req_comp_off" + else + icon_state = "req_comp_[newmessagepriority]" + add_overlay(mutable_appearance(icon, "req_comp_ov[newmessagepriority]")) + add_overlay(emissive_appearance(icon, "req_comp_ov[newmessagepriority]")) + set_light(2) + set_light_on(TRUE) + +/obj/machinery/requests_console/attack_hand(user as mob) + if(..(user)) + return + tgui_interact(user) + +/obj/machinery/requests_console/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "RequestConsole", "[department] Request Console") + ui.open() + +/obj/machinery/requests_console/tgui_data(mob/user) + var/list/data = ..() + data["department"] = department + data["screen"] = screen + data["message_log"] = message_log + data["newmessagepriority"] = newmessagepriority + data["silent"] = silent + data["announcementConsole"] = announcementConsole + + data["assist_dept"] = req_console_assistance + data["supply_dept"] = req_console_supplies + data["info_dept"] = req_console_information + + data["message"] = message + data["recipient"] = recipient + data["priority"] = priority + data["msgStamped"] = msgStamped + data["msgVerified"] = msgVerified + data["announceAuth"] = announceAuth + return data + +/obj/machinery/requests_console/tgui_act(action, list/params) + if(..()) + return TRUE + + add_fingerprint(usr) + + switch(action) + if("write") + if(reject_bad_text(params["write"])) + recipient = params["write"] //write contains the string of the receiving department's name + + var/new_message = sanitize(tgui_input_text(usr, "Write your message:", "Awaiting Input", "")) + if(new_message) + message = new_message + screen = RCS_MESSAUTH + switch(params["priority"]) + if(1) + priority = 1 + if(2) + priority = 2 + else + priority = 0 + else + reset_message(1) + . = TRUE + + if("writeAnnouncement") + var/new_message = sanitize(tgui_input_text(usr, "Write your message:", "Awaiting Input", "")) + if(new_message) + message = new_message + else + reset_message(1) + . = TRUE + + if("sendAnnouncement") + if(!announcementConsole) + return FALSE + announcement.Announce(message, msg_sanitized = 1) + reset_message(1) + . = TRUE + + if("department") + if(!message) + return FALSE + var/log_msg = message + var/pass = 0 + screen = RCS_SENTFAIL + for(var/obj/machinery/message_server/MS in machines) + if(!MS.active) + continue + MS.send_rc_message(ckey(params["department"]), department, log_msg, msgStamped, msgVerified, priority) + pass = 1 + if(pass) + screen = RCS_SENTPASS + message_log += list(list("Message sent to [recipient]", "[message]")) + else + audible_message(text("\icon[src][bicon(src)] *The Requests Console beeps: 'NOTICE: No server detected!'"),,4) + . = TRUE + + //Handle printing + if("print") + var/msg = message_log[text2num(params["print"])]; + if(msg) + msg = "[msg[1]]:
                    [msg[2]]" + msg = replacetext(msg, "
                    ", "\n") + msg = strip_html_properly(msg) + var/obj/item/weapon/paper/R = new(src.loc) + R.name = "[department] Message" + R.info = "

                    [department] Requests Console

                    [msg]
                    " + . = TRUE + + //Handle screen switching + if("setScreen") + var/tempScreen = text2num(params["setScreen"]) + if(tempScreen == RCS_ANNOUNCE && !announcementConsole) + return + if(tempScreen == RCS_VIEWMSGS) + for (var/obj/machinery/requests_console/Console in allConsoles) + if(Console.department == department) + Console.newmessagepriority = 0 + Console.update_icon() + if(tempScreen == RCS_MAINMENU) + reset_message() + screen = tempScreen + . = TRUE + + //Handle silencing the console + if("toggleSilent") + silent = !silent + . = TRUE + + //err... hacking code, which has no reason for existing... but anyway... it was once supposed to unlock priority 3 messaging on that console (EXTREME priority...), but the code for that was removed. +/obj/machinery/requests_console/attackby(var/obj/item/weapon/O as obj, var/mob/user as mob) + if(computer_deconstruction_screwdriver(user, O)) + return + if(istype(O, /obj/item/device/multitool)) + var/input = sanitize(tgui_input_text(usr, "What Department ID would you like to give this request console?", "Multitool-Request Console Interface", department)) + if(!input) + to_chat(usr, "No input found. Please hang up and try your call again.") + return + department = input + announcement.title = "[department] announcement" + announcement.newscast = 1 + + name = "[department] Requests Console" + allConsoles += src + if(departmentType & RC_ASSIST) + req_console_assistance |= department + if(departmentType & RC_SUPPLY) + req_console_supplies |= department + if(departmentType & RC_INFO) + req_console_information |= department + return + + if(istype(O, /obj/item/weapon/card/id)) + if(inoperable(MAINT)) return + if(screen == RCS_MESSAUTH) + var/obj/item/weapon/card/id/T = O + msgVerified = text("Verified by [T.registered_name] ([T.assignment])") + SStgui.update_uis(src) + if(screen == RCS_ANNOUNCE) + var/obj/item/weapon/card/id/ID = O + if(access_RC_announce in ID.GetAccess()) + announceAuth = 1 + announcement.announcer = ID.assignment ? "[ID.assignment] [ID.registered_name]" : ID.registered_name + else + reset_message() + to_chat(user, "You are not authorized to send announcements.") + SStgui.update_uis(src) + if(istype(O, /obj/item/weapon/stamp)) + if(inoperable(MAINT)) return + if(screen == RCS_MESSAUTH) + var/obj/item/weapon/stamp/T = O + msgStamped = text("Stamped with the [T.name]") + SStgui.update_uis(src) + return + +/obj/machinery/requests_console/proc/reset_message(var/mainmenu = 0) + message = "" + recipient = "" + priority = 0 + msgVerified = "" + msgStamped = "" + announceAuth = 0 + announcement.announcer = "" + if(mainmenu) + screen = RCS_MAINMENU diff --git a/code/game/machinery/robot_fabricator.dm b/code/game/machinery/robot_fabricator.dm index 103ea5f7ae2..91b0aa19bac 100644 --- a/code/game/machinery/robot_fabricator.dm +++ b/code/game/machinery/robot_fabricator.dm @@ -1,138 +1,138 @@ -/obj/machinery/robotic_fabricator - name = "robotic fabricator" - icon = 'icons/obj/robotics.dmi' - icon_state = "fab-idle" - density = TRUE - anchored = TRUE - var/metal_amount = 0 - var/operating = 0 - var/obj/item/robot_parts/being_built = null - use_power = USE_POWER_IDLE - idle_power_usage = 40 - active_power_usage = 10000 - -/obj/machinery/robotic_fabricator/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(istype(O, /obj/item/stack/material) && O.get_material_name() == MAT_STEEL) - var/obj/item/stack/M = O - if(metal_amount < 150000.0) - var/count = 0 - add_overlay("fab-load-metal") - spawn(15) - if(M) - if(!M.get_amount()) - return - while(metal_amount < 150000 && M.get_amount()) - metal_amount += O.matter[MAT_STEEL] /*O:height * O:width * O:length * 100000.0*/ - M.use(1) - count++ - - to_chat(user, "You insert [count] metal sheet\s into the fabricator.") - cut_overlay("fab-load-metal") - updateDialog() - else - to_chat(user, "The robot part maker is full. Please remove metal from the robot part maker in order to insert more.") - -/obj/machinery/robotic_fabricator/attack_hand(user as mob) - var/dat - if(..()) - return - - if(operating) - dat = {" -Building [being_built.name].
                    -Please wait until completion...

                    -
                    -"} - else - dat = {" -Metal Amount: [min(150000, metal_amount)] cm3 (MAX: 150,000)

                    -
                    -Left Arm (25,000 cc metal.)
                    -
                    Right Arm (25,000 cc metal.)
                    -
                    Left Leg (25,000 cc metal.)
                    -
                    Right Leg (25,000 cc metal).
                    -
                    Chest (50,000 cc metal).
                    -
                    Head (50,000 cc metal).
                    -
                    Robot Frame (75,000 cc metal).
                    -"} - - user << browse("Robotic Fabricator Control Panel[dat]", "window=robot_fabricator") - onclose(user, "robot_fabricator") - return - -/obj/machinery/robotic_fabricator/Topic(href, href_list) - if(..()) - return - - usr.set_machine(src) - add_fingerprint(usr) - - if(href_list["make"]) - if(!operating) - var/part_type = text2num(href_list["make"]) - - var/build_type = "" - var/build_time = 200 - var/build_cost = 25000 - - switch (part_type) - if(1) - build_type = "/obj/item/robot_parts/l_arm" - build_time = 200 - build_cost = 25000 - - if(2) - build_type = "/obj/item/robot_parts/r_arm" - build_time = 200 - build_cost = 25000 - - if(3) - build_type = "/obj/item/robot_parts/l_leg" - build_time = 200 - build_cost = 25000 - - if(4) - build_type = "/obj/item/robot_parts/r_leg" - build_time = 200 - build_cost = 25000 - - if(5) - build_type = "/obj/item/robot_parts/chest" - build_time = 350 - build_cost = 50000 - - if(6) - build_type = "/obj/item/robot_parts/head" - build_time = 350 - build_cost = 50000 - - if(7) - build_type = "/obj/item/robot_parts/robot_suit" - build_time = 600 - build_cost = 75000 - - var/building = text2path(build_type) - if(!isnull(building)) - if(metal_amount >= build_cost) - operating = 1 - update_use_power(USE_POWER_ACTIVE) - - metal_amount = max(0, metal_amount - build_cost) - - being_built = new building(src) - - add_overlay("fab-active") - updateUsrDialog() - - spawn (build_time) - if(!isnull(being_built)) - being_built.loc = get_turf(src) - being_built = null - update_use_power(USE_POWER_IDLE) - operating = 0 - cut_overlay("fab-active") - return - - for (var/mob/M in viewers(1, src)) - if(M.client && M.machine == src) - attack_hand(M) +/obj/machinery/robotic_fabricator + name = "robotic fabricator" + icon = 'icons/obj/robotics.dmi' + icon_state = "fab-idle" + density = TRUE + anchored = TRUE + var/metal_amount = 0 + var/operating = 0 + var/obj/item/robot_parts/being_built = null + use_power = USE_POWER_IDLE + idle_power_usage = 40 + active_power_usage = 10000 + +/obj/machinery/robotic_fabricator/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(istype(O, /obj/item/stack/material) && O.get_material_name() == MAT_STEEL) + var/obj/item/stack/M = O + if(metal_amount < 150000.0) + var/count = 0 + add_overlay("fab-load-metal") + spawn(15) + if(M) + if(!M.get_amount()) + return + while(metal_amount < 150000 && M.get_amount()) + metal_amount += O.matter[MAT_STEEL] /*O:height * O:width * O:length * 100000.0*/ + M.use(1) + count++ + + to_chat(user, "You insert [count] metal sheet\s into the fabricator.") + cut_overlay("fab-load-metal") + updateDialog() + else + to_chat(user, "The robot part maker is full. Please remove metal from the robot part maker in order to insert more.") + +/obj/machinery/robotic_fabricator/attack_hand(user as mob) + var/dat + if(..()) + return + + if(operating) + dat = {" +Building [being_built.name].
                    +Please wait until completion...

                    +
                    +"} + else + dat = {" +Metal Amount: [min(150000, metal_amount)] cm3 (MAX: 150,000)

                    +
                    +
                    Left Arm (25,000 cc metal.)
                    +
                    Right Arm (25,000 cc metal.)
                    +
                    Left Leg (25,000 cc metal.)
                    +
                    Right Leg (25,000 cc metal).
                    +
                    Chest (50,000 cc metal).
                    +
                    Head (50,000 cc metal).
                    +
                    Robot Frame (75,000 cc metal).
                    +"} + + user << browse("Robotic Fabricator Control Panel[dat]", "window=robot_fabricator") + onclose(user, "robot_fabricator") + return + +/obj/machinery/robotic_fabricator/Topic(href, href_list) + if(..()) + return + + usr.set_machine(src) + add_fingerprint(usr) + + if(href_list["make"]) + if(!operating) + var/part_type = text2num(href_list["make"]) + + var/build_type = "" + var/build_time = 200 + var/build_cost = 25000 + + switch (part_type) + if(1) + build_type = "/obj/item/robot_parts/l_arm" + build_time = 200 + build_cost = 25000 + + if(2) + build_type = "/obj/item/robot_parts/r_arm" + build_time = 200 + build_cost = 25000 + + if(3) + build_type = "/obj/item/robot_parts/l_leg" + build_time = 200 + build_cost = 25000 + + if(4) + build_type = "/obj/item/robot_parts/r_leg" + build_time = 200 + build_cost = 25000 + + if(5) + build_type = "/obj/item/robot_parts/chest" + build_time = 350 + build_cost = 50000 + + if(6) + build_type = "/obj/item/robot_parts/head" + build_time = 350 + build_cost = 50000 + + if(7) + build_type = "/obj/item/robot_parts/robot_suit" + build_time = 600 + build_cost = 75000 + + var/building = text2path(build_type) + if(!isnull(building)) + if(metal_amount >= build_cost) + operating = 1 + update_use_power(USE_POWER_ACTIVE) + + metal_amount = max(0, metal_amount - build_cost) + + being_built = new building(src) + + add_overlay("fab-active") + updateUsrDialog() + + spawn (build_time) + if(!isnull(being_built)) + being_built.loc = get_turf(src) + being_built = null + update_use_power(USE_POWER_IDLE) + operating = 0 + cut_overlay("fab-active") + return + + for (var/mob/M in viewers(1, src)) + if(M.client && M.machine == src) + attack_hand(M) diff --git a/code/game/machinery/seed_extractor.dm b/code/game/machinery/seed_extractor.dm index 4f29a3e347e..77cf8d1509a 100644 --- a/code/game/machinery/seed_extractor.dm +++ b/code/game/machinery/seed_extractor.dm @@ -1,51 +1,51 @@ -/obj/machinery/seed_extractor - name = "seed extractor" - desc = "Extracts and bags seeds from produce." - icon = 'icons/obj/hydroponics_machines_vr.dmi' //VOREStation Edit - icon_state = "sextractor" - density = TRUE - anchored = TRUE - -/obj/machinery/seed_extractor/attackby(var/obj/item/O as obj, var/mob/user as mob) - - // Fruits and vegetables. - if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/grown) || istype(O, /obj/item/weapon/grown)) - - user.remove_from_mob(O) - - var/datum/seed/new_seed_type - if(istype(O, /obj/item/weapon/grown)) - var/obj/item/weapon/grown/F = O - new_seed_type = SSplants.seeds[F.plantname] - else - var/obj/item/weapon/reagent_containers/food/snacks/grown/F = O - new_seed_type = SSplants.seeds[F.plantname] - - if(new_seed_type) - to_chat(user, "You extract some seeds from [O].") - var/produce = rand(1,4) - for(var/i = 0;i<=produce;i++) - var/obj/item/seeds/seeds = new(get_turf(src)) - seeds.seed_type = new_seed_type.name - seeds.update_seed() - else - to_chat(user, "[O] doesn't seem to have any usable seeds inside it.") - - qdel(O) - - //Grass. - else if(istype(O, /obj/item/stack/tile/grass)) - var/obj/item/stack/tile/grass/S = O - if(S.use(1)) - to_chat(user, "You extract some seeds from the grass tile.") - new /obj/item/seeds/grassseed(loc) - - else if(istype(O, /obj/item/weapon/fossil/plant)) // Fossils - var/obj/item/seeds/random/R = new(get_turf(src)) - to_chat(user, "\The [src] pulverizes \the [O] and spits out \the [R].") - qdel(O) - - else if(default_unfasten_wrench(user, O, 20)) - return - +/obj/machinery/seed_extractor + name = "seed extractor" + desc = "Extracts and bags seeds from produce." + icon = 'icons/obj/hydroponics_machines_vr.dmi' //VOREStation Edit + icon_state = "sextractor" + density = TRUE + anchored = TRUE + +/obj/machinery/seed_extractor/attackby(var/obj/item/O as obj, var/mob/user as mob) + + // Fruits and vegetables. + if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/grown) || istype(O, /obj/item/weapon/grown)) + + user.remove_from_mob(O) + + var/datum/seed/new_seed_type + if(istype(O, /obj/item/weapon/grown)) + var/obj/item/weapon/grown/F = O + new_seed_type = SSplants.seeds[F.plantname] + else + var/obj/item/weapon/reagent_containers/food/snacks/grown/F = O + new_seed_type = SSplants.seeds[F.plantname] + + if(new_seed_type) + to_chat(user, "You extract some seeds from [O].") + var/produce = rand(1,4) + for(var/i = 0;i<=produce;i++) + var/obj/item/seeds/seeds = new(get_turf(src)) + seeds.seed_type = new_seed_type.name + seeds.update_seed() + else + to_chat(user, "[O] doesn't seem to have any usable seeds inside it.") + + qdel(O) + + //Grass. + else if(istype(O, /obj/item/stack/tile/grass)) + var/obj/item/stack/tile/grass/S = O + if(S.use(1)) + to_chat(user, "You extract some seeds from the grass tile.") + new /obj/item/seeds/grassseed(loc) + + else if(istype(O, /obj/item/weapon/fossil/plant)) // Fossils + var/obj/item/seeds/random/R = new(get_turf(src)) + to_chat(user, "\The [src] pulverizes \the [O] and spits out \the [R].") + qdel(O) + + else if(default_unfasten_wrench(user, O, 20)) + return + return \ No newline at end of file diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm index d1db08374a6..80d28b7e3d3 100644 --- a/code/game/machinery/status_display.dm +++ b/code/game/machinery/status_display.dm @@ -1,269 +1,269 @@ -#define FONT_SIZE "5pt" -#define FONT_COLOR "#09f" -#define FONT_STYLE "Arial Black" -#define SCROLL_SPEED 2 - -// Status display -// (formerly Countdown timer display) - -// Use to show shuttle ETA/ETD times -// Alert status -// And arbitrary messages set by comms computer -/obj/machinery/status_display - icon = 'icons/obj/status_display.dmi' - icon_state = "frame" - plane = TURF_PLANE - layer = ABOVE_WINDOW_LAYER - name = "status display" - anchored = TRUE - density = FALSE - unacidable = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 10 - circuit = /obj/item/weapon/circuitboard/status_display - var/mode = 1 // 0 = Blank - // 1 = Shuttle timer - // 2 = Arbitrary message(s) - // 3 = alert picture - // 4 = Supply shuttle timer - - var/picture_state // icon_state of alert picture - var/message1 = "" // message line 1 - var/message2 = "" // message line 2 - var/index1 // display index for scrolling messages or 0 if non-scrolling - var/index2 - var/picture = null - - var/frequency = 1435 // radio frequency - - var/friendc = 0 // track if Friend Computer mode - var/ignore_friendc = 0 - - maptext_height = 26 - maptext_width = 32 - - var/const/CHARS_PER_LINE = 5 - var/const/STATUS_DISPLAY_BLANK = 0 - var/const/STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME = 1 - var/const/STATUS_DISPLAY_MESSAGE = 2 - var/const/STATUS_DISPLAY_ALERT = 3 - var/const/STATUS_DISPLAY_TIME = 4 - var/const/STATUS_DISPLAY_CUSTOM = 99 - - var/seclevel = "green" - -/obj/machinery/status_display/Destroy() - if(radio_controller) - radio_controller.remove_object(src,frequency) - return ..() - -/obj/machinery/status_display/attackby(I as obj, user as mob) - if(computer_deconstruction_screwdriver(user, I)) - return - else - attack_hand(user) - return - -// register for radio system -/obj/machinery/status_display/Initialize() - . = ..() - if(radio_controller) - radio_controller.add_object(src, frequency) - -// timed process -/obj/machinery/status_display/process() - if(stat & NOPOWER) - remove_display() - return - update() - -/obj/machinery/status_display/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - set_picture("ai_bsod") - ..(severity) - -// set what is displayed -/obj/machinery/status_display/proc/update() - remove_display() - if(friendc && !ignore_friendc) - set_picture("ai_friend") - return 1 - - switch(mode) - if(STATUS_DISPLAY_BLANK) //blank - return 1 - if(STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME) //emergency shuttle timer - if(!emergency_shuttle) - message1 = "-ETA-" - message2 = "Never" // You're here forever. - return 1 - if(emergency_shuttle.waiting_to_leave()) - message1 = "-ETD-" - if(emergency_shuttle.shuttle.is_launching()) - message2 = "Launch" - else - message2 = get_shuttle_timer_departure() - if(length(message2) > CHARS_PER_LINE) - message2 = "Error" - update_display(message1, message2) - else if(emergency_shuttle.has_eta()) - message1 = "-ETA-" - message2 = get_shuttle_timer_arrival() - if(length(message2) > CHARS_PER_LINE) - message2 = "Error" - update_display(message1, message2) - return 1 - if(STATUS_DISPLAY_MESSAGE) //custom messages - var/line1 - var/line2 - - if(!index1) - line1 = message1 - else - line1 = copytext(message1+"|"+message1, index1, index1+CHARS_PER_LINE) - var/message1_len = length(message1) - index1 += SCROLL_SPEED - if(index1 > message1_len) - index1 -= message1_len - - if(!index2) - line2 = message2 - else - line2 = copytext(message2+"|"+message2, index2, index2+CHARS_PER_LINE) - var/message2_len = length(message2) - index2 += SCROLL_SPEED - if(index2 > message2_len) - index2 -= message2_len - update_display(line1, line2) - return 1 - if(STATUS_DISPLAY_ALERT) - display_alert(seclevel) - return 1 - if(STATUS_DISPLAY_TIME) - message1 = "TIME" - message2 = stationtime2text() - update_display(message1, message2) - return 1 - return 0 - -/obj/machinery/status_display/examine(mob/user) - . = ..() - if(mode != STATUS_DISPLAY_BLANK && mode != STATUS_DISPLAY_ALERT) - . += "The display says:
                    \t[sanitize(message1)]
                    \t[sanitize(message2)]" - -/obj/machinery/status_display/proc/set_message(m1, m2) - if(m1) - index1 = (length(m1) > CHARS_PER_LINE) - message1 = m1 - else - message1 = "" - index1 = 0 - - if(m2) - index2 = (length(m2) > CHARS_PER_LINE) - message2 = m2 - else - message2 = "" - index2 = 0 - -/obj/machinery/status_display/proc/display_alert(var/newlevel) - remove_display() - if(seclevel != newlevel) - seclevel = newlevel - switch(seclevel) - if("green") set_light(l_range = 2, l_power = 0.25, l_color = "#00ff00") - if("yellow") set_light(l_range = 2, l_power = 0.25, l_color = "#ffff00") - if("violet") set_light(l_range = 2, l_power = 0.25, l_color = "#9933ff") - if("orange") set_light(l_range = 2, l_power = 0.25, l_color = "#ff9900") - if("blue") set_light(l_range = 2, l_power = 0.25, l_color = "#1024A9") - if("red") set_light(l_range = 4, l_power = 0.9, l_color = "#ff0000") - if("delta") set_light(l_range = 4, l_power = 0.9, l_color = "#FF6633") - set_picture("status_display_[seclevel]") - -// Called when the alert level is changed. -/obj/machinery/status_display/proc/on_alert_changed(new_level) - // On most alerts, this will change to a flashing alert picture in a specific color. - // Doing that for green alert automatically doesn't really make sense, but it is still available on the comm consoles/PDAs. - if(seclevel2num(new_level) == SEC_LEVEL_GREEN) - mode = STATUS_DISPLAY_TIME - set_light(0) // Remove any glow we had from the alert previously. - update() - return - mode = STATUS_DISPLAY_ALERT - display_alert(new_level) - -/obj/machinery/status_display/proc/set_picture(state) - remove_display() - if(!picture || picture_state != state) - picture_state = state - picture = image('icons/obj/status_display.dmi', icon_state=picture_state) - add_overlay(picture) - -/obj/machinery/status_display/proc/update_display(line1, line2) - var/new_text = {"
                    [line1]
                    [line2]
                    "} - if(maptext != new_text) - maptext = new_text - -/obj/machinery/status_display/proc/get_shuttle_timer_arrival() - if(!emergency_shuttle) - return "Error" - var/timeleft = emergency_shuttle.estimate_arrival_time() - if(timeleft < 0) - return "" - return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" - -/obj/machinery/status_display/proc/get_shuttle_timer_departure() - if(!emergency_shuttle) - return "Error" - var/timeleft = emergency_shuttle.estimate_launch_time() - if(timeleft < 0) - return "" - return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" - -/obj/machinery/status_display/proc/get_supply_shuttle_timer() - var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle - if(!shuttle) - return "Error" - - if(shuttle.has_arrive_time()) - var/timeleft = round((shuttle.arrive_time - world.time) / 10,1) - if(timeleft < 0) - return "Late" - return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" - return "" - -/obj/machinery/status_display/proc/remove_display() - cut_overlays() - if(maptext) - maptext = "" - -/obj/machinery/status_display/receive_signal(datum/signal/signal) - switch(signal.data["command"]) - if("blank") - mode = STATUS_DISPLAY_BLANK - set_light(0) - - if("shuttle") - mode = STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME - set_light(0) - - if("message") - mode = STATUS_DISPLAY_MESSAGE - set_message(signal.data["msg1"], signal.data["msg2"]) - set_light(0) - - if("alert") - mode = STATUS_DISPLAY_ALERT - set_picture(signal.data["picture_state"]) - - if("time") - mode = STATUS_DISPLAY_TIME - set_light(0) - update() - -#undef FONT_SIZE -#undef FONT_COLOR -#undef FONT_STYLE -#undef SCROLL_SPEED +#define FONT_SIZE "5pt" +#define FONT_COLOR "#09f" +#define FONT_STYLE "Arial Black" +#define SCROLL_SPEED 2 + +// Status display +// (formerly Countdown timer display) + +// Use to show shuttle ETA/ETD times +// Alert status +// And arbitrary messages set by comms computer +/obj/machinery/status_display + icon = 'icons/obj/status_display.dmi' + icon_state = "frame" + plane = TURF_PLANE + layer = ABOVE_WINDOW_LAYER + name = "status display" + anchored = TRUE + density = FALSE + unacidable = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 10 + circuit = /obj/item/weapon/circuitboard/status_display + var/mode = 1 // 0 = Blank + // 1 = Shuttle timer + // 2 = Arbitrary message(s) + // 3 = alert picture + // 4 = Supply shuttle timer + + var/picture_state // icon_state of alert picture + var/message1 = "" // message line 1 + var/message2 = "" // message line 2 + var/index1 // display index for scrolling messages or 0 if non-scrolling + var/index2 + var/picture = null + + var/frequency = 1435 // radio frequency + + var/friendc = 0 // track if Friend Computer mode + var/ignore_friendc = 0 + + maptext_height = 26 + maptext_width = 32 + + var/const/CHARS_PER_LINE = 5 + var/const/STATUS_DISPLAY_BLANK = 0 + var/const/STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME = 1 + var/const/STATUS_DISPLAY_MESSAGE = 2 + var/const/STATUS_DISPLAY_ALERT = 3 + var/const/STATUS_DISPLAY_TIME = 4 + var/const/STATUS_DISPLAY_CUSTOM = 99 + + var/seclevel = "green" + +/obj/machinery/status_display/Destroy() + if(radio_controller) + radio_controller.remove_object(src,frequency) + return ..() + +/obj/machinery/status_display/attackby(I as obj, user as mob) + if(computer_deconstruction_screwdriver(user, I)) + return + else + attack_hand(user) + return + +// register for radio system +/obj/machinery/status_display/Initialize() + . = ..() + if(radio_controller) + radio_controller.add_object(src, frequency) + +// timed process +/obj/machinery/status_display/process() + if(stat & NOPOWER) + remove_display() + return + update() + +/obj/machinery/status_display/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + set_picture("ai_bsod") + ..(severity) + +// set what is displayed +/obj/machinery/status_display/proc/update() + remove_display() + if(friendc && !ignore_friendc) + set_picture("ai_friend") + return 1 + + switch(mode) + if(STATUS_DISPLAY_BLANK) //blank + return 1 + if(STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME) //emergency shuttle timer + if(!emergency_shuttle) + message1 = "-ETA-" + message2 = "Never" // You're here forever. + return 1 + if(emergency_shuttle.waiting_to_leave()) + message1 = "-ETD-" + if(emergency_shuttle.shuttle.is_launching()) + message2 = "Launch" + else + message2 = get_shuttle_timer_departure() + if(length(message2) > CHARS_PER_LINE) + message2 = "Error" + update_display(message1, message2) + else if(emergency_shuttle.has_eta()) + message1 = "-ETA-" + message2 = get_shuttle_timer_arrival() + if(length(message2) > CHARS_PER_LINE) + message2 = "Error" + update_display(message1, message2) + return 1 + if(STATUS_DISPLAY_MESSAGE) //custom messages + var/line1 + var/line2 + + if(!index1) + line1 = message1 + else + line1 = copytext(message1+"|"+message1, index1, index1+CHARS_PER_LINE) + var/message1_len = length(message1) + index1 += SCROLL_SPEED + if(index1 > message1_len) + index1 -= message1_len + + if(!index2) + line2 = message2 + else + line2 = copytext(message2+"|"+message2, index2, index2+CHARS_PER_LINE) + var/message2_len = length(message2) + index2 += SCROLL_SPEED + if(index2 > message2_len) + index2 -= message2_len + update_display(line1, line2) + return 1 + if(STATUS_DISPLAY_ALERT) + display_alert(seclevel) + return 1 + if(STATUS_DISPLAY_TIME) + message1 = "TIME" + message2 = stationtime2text() + update_display(message1, message2) + return 1 + return 0 + +/obj/machinery/status_display/examine(mob/user) + . = ..() + if(mode != STATUS_DISPLAY_BLANK && mode != STATUS_DISPLAY_ALERT) + . += "The display says:
                    \t[sanitize(message1)]
                    \t[sanitize(message2)]" + +/obj/machinery/status_display/proc/set_message(m1, m2) + if(m1) + index1 = (length(m1) > CHARS_PER_LINE) + message1 = m1 + else + message1 = "" + index1 = 0 + + if(m2) + index2 = (length(m2) > CHARS_PER_LINE) + message2 = m2 + else + message2 = "" + index2 = 0 + +/obj/machinery/status_display/proc/display_alert(var/newlevel) + remove_display() + if(seclevel != newlevel) + seclevel = newlevel + switch(seclevel) + if("green") set_light(l_range = 2, l_power = 0.25, l_color = "#00ff00") + if("yellow") set_light(l_range = 2, l_power = 0.25, l_color = "#ffff00") + if("violet") set_light(l_range = 2, l_power = 0.25, l_color = "#9933ff") + if("orange") set_light(l_range = 2, l_power = 0.25, l_color = "#ff9900") + if("blue") set_light(l_range = 2, l_power = 0.25, l_color = "#1024A9") + if("red") set_light(l_range = 4, l_power = 0.9, l_color = "#ff0000") + if("delta") set_light(l_range = 4, l_power = 0.9, l_color = "#FF6633") + set_picture("status_display_[seclevel]") + +// Called when the alert level is changed. +/obj/machinery/status_display/proc/on_alert_changed(new_level) + // On most alerts, this will change to a flashing alert picture in a specific color. + // Doing that for green alert automatically doesn't really make sense, but it is still available on the comm consoles/PDAs. + if(seclevel2num(new_level) == SEC_LEVEL_GREEN) + mode = STATUS_DISPLAY_TIME + set_light(0) // Remove any glow we had from the alert previously. + update() + return + mode = STATUS_DISPLAY_ALERT + display_alert(new_level) + +/obj/machinery/status_display/proc/set_picture(state) + remove_display() + if(!picture || picture_state != state) + picture_state = state + picture = image('icons/obj/status_display.dmi', icon_state=picture_state) + add_overlay(picture) + +/obj/machinery/status_display/proc/update_display(line1, line2) + var/new_text = {"
                    [line1]
                    [line2]
                    "} + if(maptext != new_text) + maptext = new_text + +/obj/machinery/status_display/proc/get_shuttle_timer_arrival() + if(!emergency_shuttle) + return "Error" + var/timeleft = emergency_shuttle.estimate_arrival_time() + if(timeleft < 0) + return "" + return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" + +/obj/machinery/status_display/proc/get_shuttle_timer_departure() + if(!emergency_shuttle) + return "Error" + var/timeleft = emergency_shuttle.estimate_launch_time() + if(timeleft < 0) + return "" + return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" + +/obj/machinery/status_display/proc/get_supply_shuttle_timer() + var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle + if(!shuttle) + return "Error" + + if(shuttle.has_arrive_time()) + var/timeleft = round((shuttle.arrive_time - world.time) / 10,1) + if(timeleft < 0) + return "Late" + return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" + return "" + +/obj/machinery/status_display/proc/remove_display() + cut_overlays() + if(maptext) + maptext = "" + +/obj/machinery/status_display/receive_signal(datum/signal/signal) + switch(signal.data["command"]) + if("blank") + mode = STATUS_DISPLAY_BLANK + set_light(0) + + if("shuttle") + mode = STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME + set_light(0) + + if("message") + mode = STATUS_DISPLAY_MESSAGE + set_message(signal.data["msg1"], signal.data["msg2"]) + set_light(0) + + if("alert") + mode = STATUS_DISPLAY_ALERT + set_picture(signal.data["picture_state"]) + + if("time") + mode = STATUS_DISPLAY_TIME + set_light(0) + update() + +#undef FONT_SIZE +#undef FONT_COLOR +#undef FONT_STYLE +#undef SCROLL_SPEED diff --git a/code/game/machinery/telecomms/presets.dm b/code/game/machinery/telecomms/presets.dm index 34ee3e424f2..b678ab19584 100644 --- a/code/game/machinery/telecomms/presets.dm +++ b/code/game/machinery/telecomms/presets.dm @@ -1,221 +1,221 @@ -// ### Preset machines ### - -//Relay - -/obj/machinery/telecomms/relay/preset - network = "tcommsat" - -/obj/machinery/telecomms/relay/preset/station - id = "Station Relay" - autolinkers = list("s_relay") - -/obj/machinery/telecomms/relay/preset/telecomms - id = "Telecomms Relay" - autolinkers = list("relay") - -/obj/machinery/telecomms/relay/preset/mining - id = "Mining Relay" - autolinkers = list("m_relay") - -/obj/machinery/telecomms/relay/preset/ruskie - id = "Ruskie Relay" - hide = 1 - toggled = 0 - autolinkers = list("r_relay") - -/obj/machinery/telecomms/relay/preset/centcom - id = "CentCom Relay" - hide = 1 - toggled = 1 - //anchored = TRUE - //use_power = 0 - //idle_power_usage = 0 - produces_heat = 0 - autolinkers = list("c_relay") - -//HUB - -/obj/machinery/telecomms/hub/preset - id = "Hub" - network = "tcommsat" - autolinkers = list("hub", "relay", "c_relay", "s_relay", "m_relay", "r_relay", "science", "medical", - "supply", "service", "common", "command", "engineering", "security", "unused", "hb_relay", - "receiverA", "broadcasterA") //VOREStation Edit - Added "hb_relay" - -/obj/machinery/telecomms/hub/preset_cent - id = "CentCom Hub" - network = "tcommsat" - produces_heat = 0 - autolinkers = list("hub_cent", "c_relay", "s_relay", "m_relay", "r_relay", "hb_relay", - "centcom", "receiverCent", "broadcasterCent") //VOREStation Edit - Added "hb_relay" - -//Receivers - -/obj/machinery/telecomms/receiver/preset_right - id = "Receiver A" - network = "tcommsat" - autolinkers = list("receiverA") // link to relay - freq_listening = list(AI_FREQ, SCI_FREQ, MED_FREQ, SUP_FREQ, SRV_FREQ, COMM_FREQ, ENG_FREQ, SEC_FREQ, ENT_FREQ) - - //Common and other radio frequencies for people to freely use -/obj/machinery/telecomms/receiver/preset_right/New() - for(var/i = PUBLIC_LOW_FREQ, i < PUBLIC_HIGH_FREQ, i += 2) - freq_listening |= i - ..() - -/obj/machinery/telecomms/receiver/preset_cent - id = "CentCom Receiver" - network = "tcommsat" - produces_heat = 0 - autolinkers = list("receiverCent") - freq_listening = list(ERT_FREQ, DTH_FREQ, SYND_FREQ) - - -//Buses - -/obj/machinery/telecomms/bus/preset_one - id = "Bus 1" - network = "tcommsat" - freq_listening = list(SCI_FREQ, MED_FREQ) - autolinkers = list("processor1", "science", "medical") - -/obj/machinery/telecomms/bus/preset_two - id = "Bus 2" - network = "tcommsat" - freq_listening = list(SUP_FREQ, SRV_FREQ) - autolinkers = list("processor2", "supply", "service", "unused") - -/obj/machinery/telecomms/bus/preset_two/New() - for(var/i = PUBLIC_LOW_FREQ, i < PUBLIC_HIGH_FREQ, i += 2) - if(i == PUB_FREQ) - continue - freq_listening |= i - ..() - -/obj/machinery/telecomms/bus/preset_three - id = "Bus 3" - network = "tcommsat" - freq_listening = list(SEC_FREQ, COMM_FREQ) - autolinkers = list("processor3", "security", "command") - -/obj/machinery/telecomms/bus/preset_four - id = "Bus 4" - network = "tcommsat" - freq_listening = list(ENG_FREQ, AI_FREQ, PUB_FREQ, ENT_FREQ) - autolinkers = list("processor4", "engineering", "common") - -/obj/machinery/telecomms/bus/preset_cent - id = "CentCom Bus" - network = "tcommsat" - freq_listening = list(ERT_FREQ, DTH_FREQ, SYND_FREQ) - produces_heat = 0 - autolinkers = list("processorCent", "centcom") - -//Processors - -/obj/machinery/telecomms/processor/preset_one - id = "Processor 1" - network = "tcommsat" - autolinkers = list("processor1") // processors are sort of isolated; they don't need backward links - -/obj/machinery/telecomms/processor/preset_two - id = "Processor 2" - network = "tcommsat" - autolinkers = list("processor2") - -/obj/machinery/telecomms/processor/preset_three - id = "Processor 3" - network = "tcommsat" - autolinkers = list("processor3") - -/obj/machinery/telecomms/processor/preset_four - id = "Processor 4" - network = "tcommsat" - autolinkers = list("processor4") - -/obj/machinery/telecomms/processor/preset_cent - id = "CentCom Processor" - network = "tcommsat" - produces_heat = 0 - autolinkers = list("processorCent") - -//Servers - -/obj/machinery/telecomms/server/presets - - network = "tcommsat" - -/obj/machinery/telecomms/server/presets/science - id = "Science Server" - freq_listening = list(SCI_FREQ) - autolinkers = list("science") - -/obj/machinery/telecomms/server/presets/medical - id = "Medical Server" - freq_listening = list(MED_FREQ) - autolinkers = list("medical") - -/obj/machinery/telecomms/server/presets/supply - id = "Supply Server" - freq_listening = list(SUP_FREQ) - autolinkers = list("supply") - -/obj/machinery/telecomms/server/presets/service - id = "Service Server" - freq_listening = list(SRV_FREQ) - autolinkers = list("service") - -/obj/machinery/telecomms/server/presets/common - id = "Common Server" - freq_listening = list(PUB_FREQ, AI_FREQ, ENT_FREQ) // AI Private and Common - autolinkers = list("common") - -// "Unused" channels, AKA all others. -/obj/machinery/telecomms/server/presets/unused - id = "Unused Server" - freq_listening = list() - autolinkers = list("unused") - -/obj/machinery/telecomms/server/presets/unused/New() - for(var/i = PUBLIC_LOW_FREQ, i < PUBLIC_HIGH_FREQ, i += 2) - if(i == AI_FREQ || i == PUB_FREQ) - continue - freq_listening |= i - ..() - -/obj/machinery/telecomms/server/presets/command - id = "Command Server" - freq_listening = list(COMM_FREQ) - autolinkers = list("command") - -/obj/machinery/telecomms/server/presets/engineering - id = "Engineering Server" - freq_listening = list(ENG_FREQ) - autolinkers = list("engineering") - -/obj/machinery/telecomms/server/presets/security - id = "Security Server" - freq_listening = list(SEC_FREQ) - autolinkers = list("security") - -/obj/machinery/telecomms/server/presets/centcomm - id = "CentCom Server" - freq_listening = list(ERT_FREQ, DTH_FREQ, SYND_FREQ) - produces_heat = 0 - autolinkers = list("centcom") - - -//Broadcasters - -//--PRESET LEFT--// - -/obj/machinery/telecomms/broadcaster/preset_right - id = "Broadcaster A" - network = "tcommsat" - autolinkers = list("broadcasterA") - -/obj/machinery/telecomms/broadcaster/preset_cent - id = "CentCom Broadcaster" - network = "tcommsat" - produces_heat = 0 +// ### Preset machines ### + +//Relay + +/obj/machinery/telecomms/relay/preset + network = "tcommsat" + +/obj/machinery/telecomms/relay/preset/station + id = "Station Relay" + autolinkers = list("s_relay") + +/obj/machinery/telecomms/relay/preset/telecomms + id = "Telecomms Relay" + autolinkers = list("relay") + +/obj/machinery/telecomms/relay/preset/mining + id = "Mining Relay" + autolinkers = list("m_relay") + +/obj/machinery/telecomms/relay/preset/ruskie + id = "Ruskie Relay" + hide = 1 + toggled = 0 + autolinkers = list("r_relay") + +/obj/machinery/telecomms/relay/preset/centcom + id = "CentCom Relay" + hide = 1 + toggled = 1 + //anchored = TRUE + //use_power = 0 + //idle_power_usage = 0 + produces_heat = 0 + autolinkers = list("c_relay") + +//HUB + +/obj/machinery/telecomms/hub/preset + id = "Hub" + network = "tcommsat" + autolinkers = list("hub", "relay", "c_relay", "s_relay", "m_relay", "r_relay", "science", "medical", + "supply", "service", "common", "command", "engineering", "security", "unused", "hb_relay", + "receiverA", "broadcasterA") //VOREStation Edit - Added "hb_relay" + +/obj/machinery/telecomms/hub/preset_cent + id = "CentCom Hub" + network = "tcommsat" + produces_heat = 0 + autolinkers = list("hub_cent", "c_relay", "s_relay", "m_relay", "r_relay", "hb_relay", + "centcom", "receiverCent", "broadcasterCent") //VOREStation Edit - Added "hb_relay" + +//Receivers + +/obj/machinery/telecomms/receiver/preset_right + id = "Receiver A" + network = "tcommsat" + autolinkers = list("receiverA") // link to relay + freq_listening = list(AI_FREQ, SCI_FREQ, MED_FREQ, SUP_FREQ, SRV_FREQ, COMM_FREQ, ENG_FREQ, SEC_FREQ, ENT_FREQ) + + //Common and other radio frequencies for people to freely use +/obj/machinery/telecomms/receiver/preset_right/New() + for(var/i = PUBLIC_LOW_FREQ, i < PUBLIC_HIGH_FREQ, i += 2) + freq_listening |= i + ..() + +/obj/machinery/telecomms/receiver/preset_cent + id = "CentCom Receiver" + network = "tcommsat" + produces_heat = 0 + autolinkers = list("receiverCent") + freq_listening = list(ERT_FREQ, DTH_FREQ, SYND_FREQ) + + +//Buses + +/obj/machinery/telecomms/bus/preset_one + id = "Bus 1" + network = "tcommsat" + freq_listening = list(SCI_FREQ, MED_FREQ) + autolinkers = list("processor1", "science", "medical") + +/obj/machinery/telecomms/bus/preset_two + id = "Bus 2" + network = "tcommsat" + freq_listening = list(SUP_FREQ, SRV_FREQ) + autolinkers = list("processor2", "supply", "service", "unused") + +/obj/machinery/telecomms/bus/preset_two/New() + for(var/i = PUBLIC_LOW_FREQ, i < PUBLIC_HIGH_FREQ, i += 2) + if(i == PUB_FREQ) + continue + freq_listening |= i + ..() + +/obj/machinery/telecomms/bus/preset_three + id = "Bus 3" + network = "tcommsat" + freq_listening = list(SEC_FREQ, COMM_FREQ) + autolinkers = list("processor3", "security", "command") + +/obj/machinery/telecomms/bus/preset_four + id = "Bus 4" + network = "tcommsat" + freq_listening = list(ENG_FREQ, AI_FREQ, PUB_FREQ, ENT_FREQ) + autolinkers = list("processor4", "engineering", "common") + +/obj/machinery/telecomms/bus/preset_cent + id = "CentCom Bus" + network = "tcommsat" + freq_listening = list(ERT_FREQ, DTH_FREQ, SYND_FREQ) + produces_heat = 0 + autolinkers = list("processorCent", "centcom") + +//Processors + +/obj/machinery/telecomms/processor/preset_one + id = "Processor 1" + network = "tcommsat" + autolinkers = list("processor1") // processors are sort of isolated; they don't need backward links + +/obj/machinery/telecomms/processor/preset_two + id = "Processor 2" + network = "tcommsat" + autolinkers = list("processor2") + +/obj/machinery/telecomms/processor/preset_three + id = "Processor 3" + network = "tcommsat" + autolinkers = list("processor3") + +/obj/machinery/telecomms/processor/preset_four + id = "Processor 4" + network = "tcommsat" + autolinkers = list("processor4") + +/obj/machinery/telecomms/processor/preset_cent + id = "CentCom Processor" + network = "tcommsat" + produces_heat = 0 + autolinkers = list("processorCent") + +//Servers + +/obj/machinery/telecomms/server/presets + + network = "tcommsat" + +/obj/machinery/telecomms/server/presets/science + id = "Science Server" + freq_listening = list(SCI_FREQ) + autolinkers = list("science") + +/obj/machinery/telecomms/server/presets/medical + id = "Medical Server" + freq_listening = list(MED_FREQ) + autolinkers = list("medical") + +/obj/machinery/telecomms/server/presets/supply + id = "Supply Server" + freq_listening = list(SUP_FREQ) + autolinkers = list("supply") + +/obj/machinery/telecomms/server/presets/service + id = "Service Server" + freq_listening = list(SRV_FREQ) + autolinkers = list("service") + +/obj/machinery/telecomms/server/presets/common + id = "Common Server" + freq_listening = list(PUB_FREQ, AI_FREQ, ENT_FREQ) // AI Private and Common + autolinkers = list("common") + +// "Unused" channels, AKA all others. +/obj/machinery/telecomms/server/presets/unused + id = "Unused Server" + freq_listening = list() + autolinkers = list("unused") + +/obj/machinery/telecomms/server/presets/unused/New() + for(var/i = PUBLIC_LOW_FREQ, i < PUBLIC_HIGH_FREQ, i += 2) + if(i == AI_FREQ || i == PUB_FREQ) + continue + freq_listening |= i + ..() + +/obj/machinery/telecomms/server/presets/command + id = "Command Server" + freq_listening = list(COMM_FREQ) + autolinkers = list("command") + +/obj/machinery/telecomms/server/presets/engineering + id = "Engineering Server" + freq_listening = list(ENG_FREQ) + autolinkers = list("engineering") + +/obj/machinery/telecomms/server/presets/security + id = "Security Server" + freq_listening = list(SEC_FREQ) + autolinkers = list("security") + +/obj/machinery/telecomms/server/presets/centcomm + id = "CentCom Server" + freq_listening = list(ERT_FREQ, DTH_FREQ, SYND_FREQ) + produces_heat = 0 + autolinkers = list("centcom") + + +//Broadcasters + +//--PRESET LEFT--// + +/obj/machinery/telecomms/broadcaster/preset_right + id = "Broadcaster A" + network = "tcommsat" + autolinkers = list("broadcasterA") + +/obj/machinery/telecomms/broadcaster/preset_cent + id = "CentCom Broadcaster" + network = "tcommsat" + produces_heat = 0 autolinkers = list("broadcasterCent") \ No newline at end of file diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm index e23f7dd8b2c..e5e79d96ce4 100644 --- a/code/game/machinery/telecomms/telecomunications.dm +++ b/code/game/machinery/telecomms/telecomunications.dm @@ -1,714 +1,714 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/* - Hello, friends, this is Doohl from sexylands. You may be wondering what this - monstrous code file is. Sit down, boys and girls, while I tell you the tale. - - - The machines defined in this file were designed to be compatible with any radio - signals, provided they use subspace transmission. Currently they are only used for - headsets, but they can eventually be outfitted for real COMPUTER networks. This - is just a skeleton, ladies and gentlemen. - - Look at radio.dm for the prequel to this code. -*/ - -var/global/list/obj/machinery/telecomms/telecomms_list = list() - -/obj/machinery/telecomms - icon = 'icons/obj/stationobjs_vr.dmi' //VOREStation Add - unacidable = TRUE - var/list/links = list() // list of machines this machine is linked to - var/traffic = 0 // value increases as traffic increases - var/netspeed = 5 // how much traffic to lose per tick (50 gigabytes/second * netspeed) - var/list/autolinkers = list() // list of text/number values to link with - var/id = "NULL" // identification string - var/network = "NULL" // the network of the machinery - - var/list/freq_listening = list() // list of frequencies to tune into: if none, will listen to all - - var/machinetype = 0 // just a hacky way of preventing alike machines from pairing - var/toggled = 1 // Is it toggled on - var/on = 1 - var/integrity = 100 // basically HP, loses integrity by heat - var/produces_heat = 1 //whether the machine will produce heat when on. - var/delay = 10 // how many process() ticks to delay per heat - var/long_range_link = 0 // Can you link it across Z levels or on the otherside of the map? (Relay & Hub) - var/hide = 0 // Is it a hidden machine? - var/listening_level = 0 // 0 = auto set in New() - this is the z level that the machine is listening to. - - -/obj/machinery/telecomms/proc/relay_information(datum/signal/signal, filter, copysig, amount = 20) - // relay signal to all linked machinery that are of type [filter]. If signal has been sent [amount] times, stop sending - - if(!on) - return - //to_world("[src] ([src.id]) - [signal.debug_print()]") - var/send_count = 0 - - signal.data["slow"] += rand(0, round((100-integrity))) // apply some lag based on integrity - - /* - // Edit by Atlantis: Commented out as emergency fix due to causing extreme delays in communications. - // Apply some lag based on traffic rates - var/netlag = round(traffic / 50) - if(netlag > signal.data["slow"]) - signal.data["slow"] = netlag - */ -// Loop through all linked machines and send the signal or copy. - for(var/obj/machinery/telecomms/machine in links) - if(filter && !istype(machine, filter)) - continue - if(!machine.on) - continue - if(amount && send_count >= amount) - break - if(machine.loc.z != listening_level) - if(long_range_link == 0 && machine.long_range_link == 0) - continue - // If we're sending a copy, be sure to create the copy for EACH machine and paste the data - var/datum/signal/copy - if(copysig) - copy = new - copy.transmission_method = TRANSMISSION_SUBSPACE - copy.frequency = signal.frequency - copy.data = signal.data.Copy() - - // Keep the "original" signal constant - if(!signal.data["original"]) - copy.data["original"] = signal - else - copy.data["original"] = signal.data["original"] - - send_count++ - if(machine.is_freq_listening(signal)) - machine.traffic++ - - if(copysig && copy) - machine.receive_information(copy, src) - else - machine.receive_information(signal, src) - - - if(send_count > 0 && is_freq_listening(signal)) - traffic++ - - return send_count - -/obj/machinery/telecomms/proc/relay_direct_information(datum/signal/signal, obj/machinery/telecomms/machine) - // send signal directly to a machine - machine.receive_information(signal, src) - -/obj/machinery/telecomms/proc/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - // receive information from linked machinery - return - -/obj/machinery/telecomms/proc/is_freq_listening(datum/signal/signal) - // return 1 if found, 0 if not found - if(!signal) - return 0 - if((signal.frequency in freq_listening) || (!freq_listening.len)) - return 1 - else - return 0 - - -/obj/machinery/telecomms/New() - telecomms_list += src - ..() - - //Set the listening_level if there's none. - if(!listening_level) - //Defaults to our Z level! - var/turf/position = get_turf(src) - listening_level = position.z - -/obj/machinery/telecomms/Initialize() - if(autolinkers.len) - // Links nearby machines - if(!long_range_link) - for(var/obj/machinery/telecomms/T in orange(20, src)) - add_link(T) - else - for(var/obj/machinery/telecomms/T in telecomms_list) - add_link(T) - . = ..() - -/obj/machinery/telecomms/Destroy() - telecomms_list -= src - for(var/obj/machinery/telecomms/comm in telecomms_list) - comm.links -= src - links = list() - ..() - -// Used in auto linking -/obj/machinery/telecomms/proc/add_link(var/obj/machinery/telecomms/T) - var/pos_z = get_z(src) - var/tpos_z = get_z(T) - if((pos_z == tpos_z) || (src.long_range_link && T.long_range_link)) - for(var/x in autolinkers) - if(T.autolinkers.Find(x)) - if(src != T) - links |= T - -/obj/machinery/telecomms/update_icon() - if(on) - icon_state = initial(icon_state) - else - icon_state = "[initial(icon_state)]_off" - -/obj/machinery/telecomms/proc/update_power() - - if(toggled) - if(stat & (BROKEN|NOPOWER|EMPED) || integrity <= 0) // if powered, on. if not powered, off. if too damaged, off - on = 0 - else - on = 1 - else - on = 0 - -/obj/machinery/telecomms/process() - update_power() - - // Check heat and generate some - checkheat() - - // Update the icon - update_icon() - - if(traffic > 0) - traffic -= netspeed - -/obj/machinery/telecomms/emp_act(severity) - if(prob(100/severity)) - if(!(stat & EMPED)) - stat |= EMPED - var/duration = (300 * 10)/severity - spawn(rand(duration - 20, duration + 20)) // Takes a long time for the machines to reboot. - stat &= ~EMPED - ..() - -/obj/machinery/telecomms/proc/checkheat() - // Checks heat from the environment and applies any integrity damage - var/datum/gas_mixture/environment = loc.return_air() - var/damage_chance = 0 // Percent based chance of applying 1 integrity damage this tick - switch(environment.temperature) - if((T0C + 40) to (T0C + 70)) // 40C-70C, minor overheat, 10% chance of taking damage - damage_chance = 10 - if((T0C + 70) to (T0C + 130)) // 70C-130C, major overheat, 25% chance of taking damage - damage_chance = 25 - if((T0C + 130) to (T0C + 200)) // 130C-200C, dangerous overheat, 50% chance of taking damage - damage_chance = 50 - if((T0C + 200) to INFINITY) // More than 200C, INFERNO. Takes damage every tick. - damage_chance = 100 - if (damage_chance && prob(damage_chance)) - integrity = between(0, integrity - 1, 100) - - - if(delay > 0) - delay-- - else if(on) - produce_heat() - delay = initial(delay) - - - -/obj/machinery/telecomms/proc/produce_heat() - if (!produces_heat) - return - - if (!use_power) - return - - if(!(stat & (NOPOWER|BROKEN))) - var/turf/simulated/L = loc - if(istype(L)) - var/datum/gas_mixture/env = L.return_air() - - var/transfer_moles = 0.25 * env.total_moles - - var/datum/gas_mixture/removed = env.remove(transfer_moles) - - if(removed) - - var/heat_produced = idle_power_usage //obviously can't produce more heat than the machine draws from it's power source - if (traffic <= 0) - heat_produced *= 0.30 //if idle, produce less heat. - - removed.add_thermal_energy(heat_produced) - - env.merge(removed) -/* - The receiver idles and receives messages from subspace-compatible radio equipment; - primarily headsets. They then just relay this information to all linked devices, - which can would probably be network hubs. - - Link to Processor Units in case receiver can't send to bus units. -*/ - -/obj/machinery/telecomms/receiver - name = "Subspace Receiver" - //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon - icon_state = "broadcast receiver" - desc = "This machine has a dish-like shape and green lights. It is designed to detect and process subspace radio activity." - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 600 - machinetype = 1 - produces_heat = 0 - circuit = /obj/item/weapon/circuitboard/telecomms/receiver - //Vars only used if you're using the overmap - var/overmap_range = 0 - var/overmap_range_min = 0 - var/overmap_range_max = 5 - - var/list/linked_radios_weakrefs = list() - -/obj/machinery/telecomms/receiver/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/receiver/proc/link_radio(var/obj/item/device/radio/R) - if(!istype(R)) - return - linked_radios_weakrefs |= WEAKREF(R) - -/obj/machinery/telecomms/receiver/receive_signal(datum/signal/signal) - if(!on) // has to be on to receive messages - return - if(!signal) - return - if(!check_receive_level(signal)) - return - - if(signal.transmission_method == TRANSMISSION_SUBSPACE) - - if(is_freq_listening(signal)) // detect subspace signals - - //Remove the level and then start adding levels that it is being broadcasted in. - signal.data["level"] = list() - - var/can_send = relay_information(signal, /obj/machinery/telecomms/hub) // ideally relay the copied information to relays - if(!can_send) - relay_information(signal, /obj/machinery/telecomms/bus) // Send it to a bus instead, if it's linked to one - -/obj/machinery/telecomms/receiver/proc/check_receive_level(datum/signal/signal) - // If it's a direct message from a bluespace radio, we eat it and convert it into a subspace signal locally - if(signal.transmission_method == TRANSMISSION_BLUESPACE) - var/obj/item/device/radio/R = signal.data["radio"] - - //Who're you? - if(!(WEAKREF(R) in linked_radios_weakrefs)) - signal.data["reject"] = 1 - return 0 - - //We'll resend this for you - signal.data["level"] = z - signal.transmission_method = TRANSMISSION_SUBSPACE - return 1 - - //Where can we hear? - var/list/listening_levels = using_map.get_map_levels(listening_level, TRUE, overmap_range) - - // We couldn't 'hear' it, maybe a relay linked to our hub can 'hear' it - if(!(signal.data["level"] in listening_levels)) - for(var/obj/machinery/telecomms/hub/H in links) - var/list/relayed_levels = list() - for(var/obj/machinery/telecomms/relay/R in H.links) - if(R.can_receive(signal)) - relayed_levels |= R.listening_level - if(signal.data["level"] in relayed_levels) - return 1 - return 0 - return 1 - - -/* - The HUB idles until it receives information. It then passes on that information - depending on where it came from. - - This is the heart of the Telecommunications Network, sending information where it - is needed. It mainly receives information from long-distance Relays and then sends - that information to be processed. Afterwards it gets the uncompressed information - from Servers/Buses and sends that back to the relay, to then be broadcasted. -*/ - -/obj/machinery/telecomms/hub - name = "Telecommunication Hub" - //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon - icon_state = "hub" - desc = "A mighty piece of hardware used to send/receive massive amounts of data." - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 1600 - machinetype = 7 - circuit = /obj/item/weapon/circuitboard/telecomms/hub - long_range_link = 1 - netspeed = 40 - -/obj/machinery/telecomms/hub/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/hub/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - if(is_freq_listening(signal)) - if(istype(machine_from, /obj/machinery/telecomms/receiver)) - //If the signal is compressed, send it to the bus. - relay_information(signal, /obj/machinery/telecomms/bus, 1) // ideally relay the copied information to bus units - else - // Get a list of relays that we're linked to, then send the signal to their levels. - relay_information(signal, /obj/machinery/telecomms/relay, 1) - relay_information(signal, /obj/machinery/telecomms/broadcaster, 1) // Send it to a broadcaster. - - -/* - The relay idles until it receives information. It then passes on that information - depending on where it came from. - - The relay is needed in order to send information pass Z levels. It must be linked - with a HUB, the only other machine that can send/receive pass Z levels. -*/ - -/obj/machinery/telecomms/relay - name = "Telecommunication Relay" - //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon - icon_state = "relay" - desc = "A mighty piece of hardware used to send massive amounts of data far away." - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 600 - machinetype = 8 - produces_heat = 0 - circuit = /obj/item/weapon/circuitboard/telecomms/relay - netspeed = 5 - long_range_link = 1 - var/broadcasting = 1 - var/receiving = 1 - -/obj/machinery/telecomms/relay/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/relay/forceMove(var/newloc) - . = ..(newloc) - listening_level = z - -/obj/machinery/telecomms/relay/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - - // Add our level and send it back - if(can_send(signal)) - signal.data["level"] |= using_map.get_map_levels(listening_level) - -// Checks to see if it can send/receive. - -/obj/machinery/telecomms/relay/proc/can(datum/signal/signal) - if(!on) - return 0 - if(!is_freq_listening(signal)) - return 0 - return 1 - -/obj/machinery/telecomms/relay/proc/can_send(datum/signal/signal) - if(!can(signal)) - return 0 - return broadcasting - -/obj/machinery/telecomms/relay/proc/can_receive(datum/signal/signal) - if(!can(signal)) - return 0 - return receiving - -/* - The bus mainframe idles and waits for hubs to relay them signals. They act - as junctions for the network. - - They transfer uncompressed subspace packets to processor units, and then take - the processed packet to a server for logging. - - Link to a subspace hub if it can't send to a server. -*/ - -/obj/machinery/telecomms/bus - name = "Bus Mainframe" - //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon - icon_state = "bus" - desc = "A mighty piece of hardware used to send massive amounts of data quickly." - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 1000 - machinetype = 2 - circuit = /obj/item/weapon/circuitboard/telecomms/bus - netspeed = 40 - var/change_frequency = 0 - -/obj/machinery/telecomms/bus/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/bus/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - - if(is_freq_listening(signal)) - - if(change_frequency) - signal.frequency = change_frequency - - if(!istype(machine_from, /obj/machinery/telecomms/processor) && machine_from != src) // Signal must be ready (stupid assuming machine), let's send it - // send to one linked processor unit - var/send_to_processor = relay_information(signal, /obj/machinery/telecomms/processor) - - if(send_to_processor) - return - // failed to send to a processor, relay information anyway - signal.data["slow"] += rand(1, 5) // slow the signal down only slightly - src.receive_information(signal, src) - - // Try sending it! - var/list/try_send = list(/obj/machinery/telecomms/server, /obj/machinery/telecomms/hub, /obj/machinery/telecomms/broadcaster, /obj/machinery/telecomms/bus) - var/i = 0 - for(var/send in try_send) - if(i) - signal.data["slow"] += rand(0, 1) // slow the signal down only slightly - i++ - var/can_send = relay_information(signal, send) - if(can_send) - break - - - -/* - The processor is a very simple machine that decompresses subspace signals and - transfers them back to the original bus. It is essential in producing audible - data. - - Link to servers if bus is not present -*/ - -/obj/machinery/telecomms/processor - name = "Processor Unit" - //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon - icon_state = "processor" - desc = "This machine is used to process large quantities of information." - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 600 - machinetype = 3 - delay = 5 - circuit = /obj/item/weapon/circuitboard/telecomms/processor - var/process_mode = 1 // 1 = Uncompress Signals, 0 = Compress Signals - -/obj/machinery/telecomms/processor/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/processor/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - - if(is_freq_listening(signal)) - - if(process_mode) - signal.data["compression"] = 0 // uncompress subspace signal - else - signal.data["compression"] = 100 // even more compressed signal - - if(istype(machine_from, /obj/machinery/telecomms/bus)) - relay_direct_information(signal, machine_from) // send the signal back to the machine - else // no bus detected - send the signal to servers instead - signal.data["slow"] += rand(5, 10) // slow the signal down - relay_information(signal, /obj/machinery/telecomms/server) - - -/* - The server logs all traffic and signal data. Once it records the signal, it sends - it to the subspace broadcaster. - - Store a maximum of 100 logs and then deletes them. -*/ - - -/obj/machinery/telecomms/server - name = "Telecommunication Server" - //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon - icon_state = "comm_server" - desc = "A machine used to store data and network statistics." - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 300 - machinetype = 4 - circuit = /obj/item/weapon/circuitboard/telecomms/server - var/list/log_entries = list() - var/list/stored_names = list() - var/list/TrafficActions = list() - var/logs = 0 // number of logs - var/totaltraffic = 0 // gigabytes (if > 1024, divide by 1024 -> terrabytes) - - var/list/memory = list() // stored memory - var/rawcode = "" // the code to compile (raw text) - var/datum/TCS_Compiler/Compiler // the compiler that compiles and runs the code - var/autoruncode = 0 // 1 if the code is set to run every time a signal is picked up - - var/encryption = "null" // encryption key: ie "password" - var/salt = "null" // encryption salt: ie "123comsat" - // would add up to md5("password123comsat") - var/obj/item/device/radio/headset/server_radio = null - -/obj/machinery/telecomms/server/New() - ..() - Compiler = new() - Compiler.Holder = src - server_radio = new() - -/obj/machinery/telecomms/server/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/server/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - - if(signal.data["message"]) - - if(is_freq_listening(signal)) - - if(traffic > 0) - totaltraffic += traffic // add current traffic to total traffic - - //Is this a test signal? Bypass logging - if(signal.data["type"] != SIGNAL_TEST) - - // If signal has a message and appropriate frequency - - update_logs() - - var/datum/comm_log_entry/log = new - var/mob/M = signal.data["mob"] - - // Copy the signal.data entries we want - log.parameters["mobtype"] = signal.data["mobtype"] - log.parameters["job"] = signal.data["job"] - log.parameters["key"] = signal.data["key"] - log.parameters["vmessage"] = multilingual_to_message(signal.data["message"]) - log.parameters["vname"] = signal.data["vname"] - log.parameters["message"] = multilingual_to_message(signal.data["message"]) - log.parameters["name"] = signal.data["name"] - log.parameters["realname"] = signal.data["realname"] - log.parameters["timecode"] = worldtime2stationtime(world.time) - - var/race = "unknown" - if(ishuman(M)) - var/mob/living/carbon/human/H = M - race = "[H.species.name]" - log.parameters["intelligible"] = 1 - else if(isbrain(M)) - race = "Brain" - log.parameters["intelligible"] = 1 - else if(M.isMonkey()) - race = "Monkey" - else if(issilicon(M)) - race = "Artificial Life" - log.parameters["intelligible"] = 1 - else if(isslime(M)) - race = "Slime" - else if(isanimal(M)) - race = "Domestic Animal" - - log.parameters["race"] = race - - if(!istype(M, /mob/new_player) && M) - log.parameters["uspeech"] = M.universal_speak - else - log.parameters["uspeech"] = 0 - - // If the signal is still compressed, make the log entry gibberish - if(signal.data["compression"] > 0) - log.parameters["message"] = Gibberish(multilingual_to_message(signal.data["message"]), signal.data["compression"] + 50) - log.parameters["job"] = Gibberish(signal.data["job"], signal.data["compression"] + 50) - log.parameters["name"] = Gibberish(signal.data["name"], signal.data["compression"] + 50) - log.parameters["realname"] = Gibberish(signal.data["realname"], signal.data["compression"] + 50) - log.parameters["vname"] = Gibberish(signal.data["vname"], signal.data["compression"] + 50) - log.input_type = "Corrupt File" - - // Log and store everything that needs to be logged - log_entries.Add(log) - if(!(signal.data["name"] in stored_names)) - stored_names.Add(signal.data["name"]) - logs++ - signal.data["server"] = src - - // Give the log a name - var/identifier = num2text( rand(-1000,1000) + world.time ) - log.name = "data packet ([md5(identifier)])" - - if(Compiler && autoruncode) - Compiler.Run(signal) // execute the code - - var/can_send = relay_information(signal, /obj/machinery/telecomms/hub) - if(!can_send) - relay_information(signal, /obj/machinery/telecomms/broadcaster) - - -/obj/machinery/telecomms/server/proc/setcode(var/t) - if(t) - if(istext(t)) - rawcode = t - -/obj/machinery/telecomms/server/proc/compile() - if(Compiler) - return Compiler.Compile(rawcode) - -/obj/machinery/telecomms/server/proc/update_logs() - // start deleting the very first log entry - if(logs >= 400) - for(var/i = 1, i <= logs, i++) // locate the first garbage collectable log entry and remove it - var/datum/comm_log_entry/L = log_entries[i] - if(L.garbage_collector) - log_entries.Remove(L) - logs-- - break - -/obj/machinery/telecomms/server/proc/add_entry(var/content, var/input) - var/datum/comm_log_entry/log = new - var/identifier = num2text( rand(-1000,1000) + world.time ) - log.name = "[input] ([md5(identifier)])" - log.input_type = input - log.parameters["message"] = content - log.parameters["timecode"] = stationtime2text() - log_entries.Add(log) - update_logs() - - - - -// Simple log entry datum - -/datum/comm_log_entry - var/parameters = list() // carbon-copy to signal.data[] - var/name = "data packet (#)" - var/garbage_collector = 1 // if set to 0, will not be garbage collected - var/input_type = "Speech File" - -//Generic telecomm connectivity test proc -/proc/can_telecomm(var/atom/A, var/atom/B, var/ad_hoc = FALSE) - if(!A || !B) - log_debug("can_telecomm(): Undefined endpoints!") - return FALSE - - //Can't in this case, obviously! - if(is_jammed(A) || is_jammed(B)) - return FALSE - - //Items don't have a Z when inside an object or mob - var/turf/src_z = get_z(A) - var/turf/dst_z = get_z(B) - - //Nullspace, probably. - if(!src_z || !dst_z) - return FALSE - - //We can do the simple check first, if you have ad_hoc radios. - if(ad_hoc && src_z == dst_z) - return TRUE - - return src_z in using_map.get_map_levels(dst_z, TRUE, om_range = DEFAULT_OVERMAP_RANGE) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/* + Hello, friends, this is Doohl from sexylands. You may be wondering what this + monstrous code file is. Sit down, boys and girls, while I tell you the tale. + + + The machines defined in this file were designed to be compatible with any radio + signals, provided they use subspace transmission. Currently they are only used for + headsets, but they can eventually be outfitted for real COMPUTER networks. This + is just a skeleton, ladies and gentlemen. + + Look at radio.dm for the prequel to this code. +*/ + +var/global/list/obj/machinery/telecomms/telecomms_list = list() + +/obj/machinery/telecomms + icon = 'icons/obj/stationobjs_vr.dmi' //VOREStation Add + unacidable = TRUE + var/list/links = list() // list of machines this machine is linked to + var/traffic = 0 // value increases as traffic increases + var/netspeed = 5 // how much traffic to lose per tick (50 gigabytes/second * netspeed) + var/list/autolinkers = list() // list of text/number values to link with + var/id = "NULL" // identification string + var/network = "NULL" // the network of the machinery + + var/list/freq_listening = list() // list of frequencies to tune into: if none, will listen to all + + var/machinetype = 0 // just a hacky way of preventing alike machines from pairing + var/toggled = 1 // Is it toggled on + var/on = 1 + var/integrity = 100 // basically HP, loses integrity by heat + var/produces_heat = 1 //whether the machine will produce heat when on. + var/delay = 10 // how many process() ticks to delay per heat + var/long_range_link = 0 // Can you link it across Z levels or on the otherside of the map? (Relay & Hub) + var/hide = 0 // Is it a hidden machine? + var/listening_level = 0 // 0 = auto set in New() - this is the z level that the machine is listening to. + + +/obj/machinery/telecomms/proc/relay_information(datum/signal/signal, filter, copysig, amount = 20) + // relay signal to all linked machinery that are of type [filter]. If signal has been sent [amount] times, stop sending + + if(!on) + return + //to_world("[src] ([src.id]) - [signal.debug_print()]") + var/send_count = 0 + + signal.data["slow"] += rand(0, round((100-integrity))) // apply some lag based on integrity + + /* + // Edit by Atlantis: Commented out as emergency fix due to causing extreme delays in communications. + // Apply some lag based on traffic rates + var/netlag = round(traffic / 50) + if(netlag > signal.data["slow"]) + signal.data["slow"] = netlag + */ +// Loop through all linked machines and send the signal or copy. + for(var/obj/machinery/telecomms/machine in links) + if(filter && !istype(machine, filter)) + continue + if(!machine.on) + continue + if(amount && send_count >= amount) + break + if(machine.loc.z != listening_level) + if(long_range_link == 0 && machine.long_range_link == 0) + continue + // If we're sending a copy, be sure to create the copy for EACH machine and paste the data + var/datum/signal/copy + if(copysig) + copy = new + copy.transmission_method = TRANSMISSION_SUBSPACE + copy.frequency = signal.frequency + copy.data = signal.data.Copy() + + // Keep the "original" signal constant + if(!signal.data["original"]) + copy.data["original"] = signal + else + copy.data["original"] = signal.data["original"] + + send_count++ + if(machine.is_freq_listening(signal)) + machine.traffic++ + + if(copysig && copy) + machine.receive_information(copy, src) + else + machine.receive_information(signal, src) + + + if(send_count > 0 && is_freq_listening(signal)) + traffic++ + + return send_count + +/obj/machinery/telecomms/proc/relay_direct_information(datum/signal/signal, obj/machinery/telecomms/machine) + // send signal directly to a machine + machine.receive_information(signal, src) + +/obj/machinery/telecomms/proc/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + // receive information from linked machinery + return + +/obj/machinery/telecomms/proc/is_freq_listening(datum/signal/signal) + // return 1 if found, 0 if not found + if(!signal) + return 0 + if((signal.frequency in freq_listening) || (!freq_listening.len)) + return 1 + else + return 0 + + +/obj/machinery/telecomms/New() + telecomms_list += src + ..() + + //Set the listening_level if there's none. + if(!listening_level) + //Defaults to our Z level! + var/turf/position = get_turf(src) + listening_level = position.z + +/obj/machinery/telecomms/Initialize() + if(autolinkers.len) + // Links nearby machines + if(!long_range_link) + for(var/obj/machinery/telecomms/T in orange(20, src)) + add_link(T) + else + for(var/obj/machinery/telecomms/T in telecomms_list) + add_link(T) + . = ..() + +/obj/machinery/telecomms/Destroy() + telecomms_list -= src + for(var/obj/machinery/telecomms/comm in telecomms_list) + comm.links -= src + links = list() + ..() + +// Used in auto linking +/obj/machinery/telecomms/proc/add_link(var/obj/machinery/telecomms/T) + var/pos_z = get_z(src) + var/tpos_z = get_z(T) + if((pos_z == tpos_z) || (src.long_range_link && T.long_range_link)) + for(var/x in autolinkers) + if(T.autolinkers.Find(x)) + if(src != T) + links |= T + +/obj/machinery/telecomms/update_icon() + if(on) + icon_state = initial(icon_state) + else + icon_state = "[initial(icon_state)]_off" + +/obj/machinery/telecomms/proc/update_power() + + if(toggled) + if(stat & (BROKEN|NOPOWER|EMPED) || integrity <= 0) // if powered, on. if not powered, off. if too damaged, off + on = 0 + else + on = 1 + else + on = 0 + +/obj/machinery/telecomms/process() + update_power() + + // Check heat and generate some + checkheat() + + // Update the icon + update_icon() + + if(traffic > 0) + traffic -= netspeed + +/obj/machinery/telecomms/emp_act(severity) + if(prob(100/severity)) + if(!(stat & EMPED)) + stat |= EMPED + var/duration = (300 * 10)/severity + spawn(rand(duration - 20, duration + 20)) // Takes a long time for the machines to reboot. + stat &= ~EMPED + ..() + +/obj/machinery/telecomms/proc/checkheat() + // Checks heat from the environment and applies any integrity damage + var/datum/gas_mixture/environment = loc.return_air() + var/damage_chance = 0 // Percent based chance of applying 1 integrity damage this tick + switch(environment.temperature) + if((T0C + 40) to (T0C + 70)) // 40C-70C, minor overheat, 10% chance of taking damage + damage_chance = 10 + if((T0C + 70) to (T0C + 130)) // 70C-130C, major overheat, 25% chance of taking damage + damage_chance = 25 + if((T0C + 130) to (T0C + 200)) // 130C-200C, dangerous overheat, 50% chance of taking damage + damage_chance = 50 + if((T0C + 200) to INFINITY) // More than 200C, INFERNO. Takes damage every tick. + damage_chance = 100 + if (damage_chance && prob(damage_chance)) + integrity = between(0, integrity - 1, 100) + + + if(delay > 0) + delay-- + else if(on) + produce_heat() + delay = initial(delay) + + + +/obj/machinery/telecomms/proc/produce_heat() + if (!produces_heat) + return + + if (!use_power) + return + + if(!(stat & (NOPOWER|BROKEN))) + var/turf/simulated/L = loc + if(istype(L)) + var/datum/gas_mixture/env = L.return_air() + + var/transfer_moles = 0.25 * env.total_moles + + var/datum/gas_mixture/removed = env.remove(transfer_moles) + + if(removed) + + var/heat_produced = idle_power_usage //obviously can't produce more heat than the machine draws from it's power source + if (traffic <= 0) + heat_produced *= 0.30 //if idle, produce less heat. + + removed.add_thermal_energy(heat_produced) + + env.merge(removed) +/* + The receiver idles and receives messages from subspace-compatible radio equipment; + primarily headsets. They then just relay this information to all linked devices, + which can would probably be network hubs. + + Link to Processor Units in case receiver can't send to bus units. +*/ + +/obj/machinery/telecomms/receiver + name = "Subspace Receiver" + //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon + icon_state = "broadcast receiver" + desc = "This machine has a dish-like shape and green lights. It is designed to detect and process subspace radio activity." + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 600 + machinetype = 1 + produces_heat = 0 + circuit = /obj/item/weapon/circuitboard/telecomms/receiver + //Vars only used if you're using the overmap + var/overmap_range = 0 + var/overmap_range_min = 0 + var/overmap_range_max = 5 + + var/list/linked_radios_weakrefs = list() + +/obj/machinery/telecomms/receiver/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/receiver/proc/link_radio(var/obj/item/device/radio/R) + if(!istype(R)) + return + linked_radios_weakrefs |= WEAKREF(R) + +/obj/machinery/telecomms/receiver/receive_signal(datum/signal/signal) + if(!on) // has to be on to receive messages + return + if(!signal) + return + if(!check_receive_level(signal)) + return + + if(signal.transmission_method == TRANSMISSION_SUBSPACE) + + if(is_freq_listening(signal)) // detect subspace signals + + //Remove the level and then start adding levels that it is being broadcasted in. + signal.data["level"] = list() + + var/can_send = relay_information(signal, /obj/machinery/telecomms/hub) // ideally relay the copied information to relays + if(!can_send) + relay_information(signal, /obj/machinery/telecomms/bus) // Send it to a bus instead, if it's linked to one + +/obj/machinery/telecomms/receiver/proc/check_receive_level(datum/signal/signal) + // If it's a direct message from a bluespace radio, we eat it and convert it into a subspace signal locally + if(signal.transmission_method == TRANSMISSION_BLUESPACE) + var/obj/item/device/radio/R = signal.data["radio"] + + //Who're you? + if(!(WEAKREF(R) in linked_radios_weakrefs)) + signal.data["reject"] = 1 + return 0 + + //We'll resend this for you + signal.data["level"] = z + signal.transmission_method = TRANSMISSION_SUBSPACE + return 1 + + //Where can we hear? + var/list/listening_levels = using_map.get_map_levels(listening_level, TRUE, overmap_range) + + // We couldn't 'hear' it, maybe a relay linked to our hub can 'hear' it + if(!(signal.data["level"] in listening_levels)) + for(var/obj/machinery/telecomms/hub/H in links) + var/list/relayed_levels = list() + for(var/obj/machinery/telecomms/relay/R in H.links) + if(R.can_receive(signal)) + relayed_levels |= R.listening_level + if(signal.data["level"] in relayed_levels) + return 1 + return 0 + return 1 + + +/* + The HUB idles until it receives information. It then passes on that information + depending on where it came from. + + This is the heart of the Telecommunications Network, sending information where it + is needed. It mainly receives information from long-distance Relays and then sends + that information to be processed. Afterwards it gets the uncompressed information + from Servers/Buses and sends that back to the relay, to then be broadcasted. +*/ + +/obj/machinery/telecomms/hub + name = "Telecommunication Hub" + //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon + icon_state = "hub" + desc = "A mighty piece of hardware used to send/receive massive amounts of data." + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 1600 + machinetype = 7 + circuit = /obj/item/weapon/circuitboard/telecomms/hub + long_range_link = 1 + netspeed = 40 + +/obj/machinery/telecomms/hub/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/hub/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + if(is_freq_listening(signal)) + if(istype(machine_from, /obj/machinery/telecomms/receiver)) + //If the signal is compressed, send it to the bus. + relay_information(signal, /obj/machinery/telecomms/bus, 1) // ideally relay the copied information to bus units + else + // Get a list of relays that we're linked to, then send the signal to their levels. + relay_information(signal, /obj/machinery/telecomms/relay, 1) + relay_information(signal, /obj/machinery/telecomms/broadcaster, 1) // Send it to a broadcaster. + + +/* + The relay idles until it receives information. It then passes on that information + depending on where it came from. + + The relay is needed in order to send information pass Z levels. It must be linked + with a HUB, the only other machine that can send/receive pass Z levels. +*/ + +/obj/machinery/telecomms/relay + name = "Telecommunication Relay" + //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon + icon_state = "relay" + desc = "A mighty piece of hardware used to send massive amounts of data far away." + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 600 + machinetype = 8 + produces_heat = 0 + circuit = /obj/item/weapon/circuitboard/telecomms/relay + netspeed = 5 + long_range_link = 1 + var/broadcasting = 1 + var/receiving = 1 + +/obj/machinery/telecomms/relay/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/relay/forceMove(var/newloc) + . = ..(newloc) + listening_level = z + +/obj/machinery/telecomms/relay/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + + // Add our level and send it back + if(can_send(signal)) + signal.data["level"] |= using_map.get_map_levels(listening_level) + +// Checks to see if it can send/receive. + +/obj/machinery/telecomms/relay/proc/can(datum/signal/signal) + if(!on) + return 0 + if(!is_freq_listening(signal)) + return 0 + return 1 + +/obj/machinery/telecomms/relay/proc/can_send(datum/signal/signal) + if(!can(signal)) + return 0 + return broadcasting + +/obj/machinery/telecomms/relay/proc/can_receive(datum/signal/signal) + if(!can(signal)) + return 0 + return receiving + +/* + The bus mainframe idles and waits for hubs to relay them signals. They act + as junctions for the network. + + They transfer uncompressed subspace packets to processor units, and then take + the processed packet to a server for logging. + + Link to a subspace hub if it can't send to a server. +*/ + +/obj/machinery/telecomms/bus + name = "Bus Mainframe" + //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon + icon_state = "bus" + desc = "A mighty piece of hardware used to send massive amounts of data quickly." + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 1000 + machinetype = 2 + circuit = /obj/item/weapon/circuitboard/telecomms/bus + netspeed = 40 + var/change_frequency = 0 + +/obj/machinery/telecomms/bus/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/bus/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + + if(is_freq_listening(signal)) + + if(change_frequency) + signal.frequency = change_frequency + + if(!istype(machine_from, /obj/machinery/telecomms/processor) && machine_from != src) // Signal must be ready (stupid assuming machine), let's send it + // send to one linked processor unit + var/send_to_processor = relay_information(signal, /obj/machinery/telecomms/processor) + + if(send_to_processor) + return + // failed to send to a processor, relay information anyway + signal.data["slow"] += rand(1, 5) // slow the signal down only slightly + src.receive_information(signal, src) + + // Try sending it! + var/list/try_send = list(/obj/machinery/telecomms/server, /obj/machinery/telecomms/hub, /obj/machinery/telecomms/broadcaster, /obj/machinery/telecomms/bus) + var/i = 0 + for(var/send in try_send) + if(i) + signal.data["slow"] += rand(0, 1) // slow the signal down only slightly + i++ + var/can_send = relay_information(signal, send) + if(can_send) + break + + + +/* + The processor is a very simple machine that decompresses subspace signals and + transfers them back to the original bus. It is essential in producing audible + data. + + Link to servers if bus is not present +*/ + +/obj/machinery/telecomms/processor + name = "Processor Unit" + //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon + icon_state = "processor" + desc = "This machine is used to process large quantities of information." + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 600 + machinetype = 3 + delay = 5 + circuit = /obj/item/weapon/circuitboard/telecomms/processor + var/process_mode = 1 // 1 = Uncompress Signals, 0 = Compress Signals + +/obj/machinery/telecomms/processor/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/processor/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + + if(is_freq_listening(signal)) + + if(process_mode) + signal.data["compression"] = 0 // uncompress subspace signal + else + signal.data["compression"] = 100 // even more compressed signal + + if(istype(machine_from, /obj/machinery/telecomms/bus)) + relay_direct_information(signal, machine_from) // send the signal back to the machine + else // no bus detected - send the signal to servers instead + signal.data["slow"] += rand(5, 10) // slow the signal down + relay_information(signal, /obj/machinery/telecomms/server) + + +/* + The server logs all traffic and signal data. Once it records the signal, it sends + it to the subspace broadcaster. + + Store a maximum of 100 logs and then deletes them. +*/ + + +/obj/machinery/telecomms/server + name = "Telecommunication Server" + //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon + icon_state = "comm_server" + desc = "A machine used to store data and network statistics." + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 300 + machinetype = 4 + circuit = /obj/item/weapon/circuitboard/telecomms/server + var/list/log_entries = list() + var/list/stored_names = list() + var/list/TrafficActions = list() + var/logs = 0 // number of logs + var/totaltraffic = 0 // gigabytes (if > 1024, divide by 1024 -> terrabytes) + + var/list/memory = list() // stored memory + var/rawcode = "" // the code to compile (raw text) + var/datum/TCS_Compiler/Compiler // the compiler that compiles and runs the code + var/autoruncode = 0 // 1 if the code is set to run every time a signal is picked up + + var/encryption = "null" // encryption key: ie "password" + var/salt = "null" // encryption salt: ie "123comsat" + // would add up to md5("password123comsat") + var/obj/item/device/radio/headset/server_radio = null + +/obj/machinery/telecomms/server/New() + ..() + Compiler = new() + Compiler.Holder = src + server_radio = new() + +/obj/machinery/telecomms/server/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/server/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + + if(signal.data["message"]) + + if(is_freq_listening(signal)) + + if(traffic > 0) + totaltraffic += traffic // add current traffic to total traffic + + //Is this a test signal? Bypass logging + if(signal.data["type"] != SIGNAL_TEST) + + // If signal has a message and appropriate frequency + + update_logs() + + var/datum/comm_log_entry/log = new + var/mob/M = signal.data["mob"] + + // Copy the signal.data entries we want + log.parameters["mobtype"] = signal.data["mobtype"] + log.parameters["job"] = signal.data["job"] + log.parameters["key"] = signal.data["key"] + log.parameters["vmessage"] = multilingual_to_message(signal.data["message"]) + log.parameters["vname"] = signal.data["vname"] + log.parameters["message"] = multilingual_to_message(signal.data["message"]) + log.parameters["name"] = signal.data["name"] + log.parameters["realname"] = signal.data["realname"] + log.parameters["timecode"] = worldtime2stationtime(world.time) + + var/race = "unknown" + if(ishuman(M)) + var/mob/living/carbon/human/H = M + race = "[H.species.name]" + log.parameters["intelligible"] = 1 + else if(isbrain(M)) + race = "Brain" + log.parameters["intelligible"] = 1 + else if(M.isMonkey()) + race = "Monkey" + else if(issilicon(M)) + race = "Artificial Life" + log.parameters["intelligible"] = 1 + else if(isslime(M)) + race = "Slime" + else if(isanimal(M)) + race = "Domestic Animal" + + log.parameters["race"] = race + + if(!istype(M, /mob/new_player) && M) + log.parameters["uspeech"] = M.universal_speak + else + log.parameters["uspeech"] = 0 + + // If the signal is still compressed, make the log entry gibberish + if(signal.data["compression"] > 0) + log.parameters["message"] = Gibberish(multilingual_to_message(signal.data["message"]), signal.data["compression"] + 50) + log.parameters["job"] = Gibberish(signal.data["job"], signal.data["compression"] + 50) + log.parameters["name"] = Gibberish(signal.data["name"], signal.data["compression"] + 50) + log.parameters["realname"] = Gibberish(signal.data["realname"], signal.data["compression"] + 50) + log.parameters["vname"] = Gibberish(signal.data["vname"], signal.data["compression"] + 50) + log.input_type = "Corrupt File" + + // Log and store everything that needs to be logged + log_entries.Add(log) + if(!(signal.data["name"] in stored_names)) + stored_names.Add(signal.data["name"]) + logs++ + signal.data["server"] = src + + // Give the log a name + var/identifier = num2text( rand(-1000,1000) + world.time ) + log.name = "data packet ([md5(identifier)])" + + if(Compiler && autoruncode) + Compiler.Run(signal) // execute the code + + var/can_send = relay_information(signal, /obj/machinery/telecomms/hub) + if(!can_send) + relay_information(signal, /obj/machinery/telecomms/broadcaster) + + +/obj/machinery/telecomms/server/proc/setcode(var/t) + if(t) + if(istext(t)) + rawcode = t + +/obj/machinery/telecomms/server/proc/compile() + if(Compiler) + return Compiler.Compile(rawcode) + +/obj/machinery/telecomms/server/proc/update_logs() + // start deleting the very first log entry + if(logs >= 400) + for(var/i = 1, i <= logs, i++) // locate the first garbage collectable log entry and remove it + var/datum/comm_log_entry/L = log_entries[i] + if(L.garbage_collector) + log_entries.Remove(L) + logs-- + break + +/obj/machinery/telecomms/server/proc/add_entry(var/content, var/input) + var/datum/comm_log_entry/log = new + var/identifier = num2text( rand(-1000,1000) + world.time ) + log.name = "[input] ([md5(identifier)])" + log.input_type = input + log.parameters["message"] = content + log.parameters["timecode"] = stationtime2text() + log_entries.Add(log) + update_logs() + + + + +// Simple log entry datum + +/datum/comm_log_entry + var/parameters = list() // carbon-copy to signal.data[] + var/name = "data packet (#)" + var/garbage_collector = 1 // if set to 0, will not be garbage collected + var/input_type = "Speech File" + +//Generic telecomm connectivity test proc +/proc/can_telecomm(var/atom/A, var/atom/B, var/ad_hoc = FALSE) + if(!A || !B) + log_debug("can_telecomm(): Undefined endpoints!") + return FALSE + + //Can't in this case, obviously! + if(is_jammed(A) || is_jammed(B)) + return FALSE + + //Items don't have a Z when inside an object or mob + var/turf/src_z = get_z(A) + var/turf/dst_z = get_z(B) + + //Nullspace, probably. + if(!src_z || !dst_z) + return FALSE + + //We can do the simple check first, if you have ad_hoc radios. + if(ad_hoc && src_z == dst_z) + return TRUE + + return src_z in using_map.get_map_levels(dst_z, TRUE, om_range = DEFAULT_OVERMAP_RANGE) diff --git a/code/game/machinery/telecomms/telemonitor.dm b/code/game/machinery/telecomms/telemonitor.dm index cf9ab2096dc..c9a2678af84 100644 --- a/code/game/machinery/telecomms/telemonitor.dm +++ b/code/game/machinery/telecomms/telemonitor.dm @@ -1,129 +1,129 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - - -/* - Telecomms monitor tracks the overall trafficing of a telecommunications network - and displays a heirarchy of linked machines. -*/ - -/obj/machinery/computer/telecomms/monitor - name = "Telecommunications Monitor" - desc = "Used to traverse a telecommunication network. Helpful for debugging connection issues." - icon_screen = "comm_monitor" - - var/screen = 0 // the screen number: - var/list/machinelist = list() // the machines located by the computer - var/obj/machinery/telecomms/SelectedMachine - circuit = /obj/item/weapon/circuitboard/comm_monitor - - var/network = "NULL" // the network to probe - - var/list/temp = null // temporary feedback messages - -/obj/machinery/computer/telecomms/monitor/tgui_data(mob/user) - var/list/data = list() - - data["network"] = network - data["temp"] = temp - - var/list/machinelistData = list() - for(var/obj/machinery/telecomms/T in machinelist) - machinelistData.Add(list(list( - "id" = T.id, - "name" = T.name, - ))) - data["machinelist"] = machinelistData - - data["selectedMachine"] = null - if(SelectedMachine) - data["selectedMachine"] = list( - "id" = SelectedMachine.id, - "name" = SelectedMachine.name, - ) - var/list/links = list() - for(var/obj/machinery/telecomms/T in SelectedMachine.links) - if(!T.hide) - links.Add(list(list( - "id" = T.id, - "name" = T.name - ))) - data["selectedMachine"]["links"] = links - return data - -/obj/machinery/computer/telecomms/monitor/attack_hand(mob/user) - if(stat & (BROKEN|NOPOWER)) - return - tgui_interact(user) - -/obj/machinery/computer/telecomms/monitor/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "TelecommsMachineBrowser", name) - ui.open() - -/obj/machinery/computer/telecomms/monitor/tgui_act(action, params) - if(..()) - return TRUE - - add_fingerprint(usr) - - switch(action) - if("view") - for(var/obj/machinery/telecomms/T in machinelist) - if(T.id == params["id"]) - SelectedMachine = T - break - . = TRUE - - if("mainmenu") - SelectedMachine = null - . = TRUE - - if("release") - machinelist = list() - SelectedMachine = null - . = TRUE - - if("scan") - if(machinelist.len > 0) - set_temp("FAILED: CANNOT PROBE WHEN BUFFER FULL", "bad") - return TRUE - - for(var/obj/machinery/telecomms/T in range(25, src)) - if(T.network == network) - machinelist.Add(T) - - if(!machinelist.len) - set_temp("FAILED: UNABLE TO LOCATE NETWORK ENTITIES IN \[[network]\]", "bad") - else - set_temp("[machinelist.len] ENTITIES LOCATED & BUFFERED", "good") - . = TRUE - - if("network") - var/newnet = tgui_input_text(usr, "Which network do you want to view?", "Comm Monitor", network, 15) - newnet = sanitize(newnet,15) //Honestly, I'd be amazed if someone managed to do HTML in 15 chars. - if(newnet && ((usr in range(1, src) || issilicon(usr)))) - if(length(newnet) > 15) - set_temp("FAILED: NETWORK TAG STRING TOO LENGTHY", "bad") - return TRUE - network = newnet - machinelist = list() - set_temp("NEW NETWORK TAG SET IN ADDRESS \[[network]\]", "good") - - . = TRUE - - if("cleartemp") - temp = null - . = TRUE - - -/obj/machinery/computer/telecomms/monitor/emag_act(var/remaining_charges, var/mob/user) - if(!emagged) - playsound(src, 'sound/effects/sparks4.ogg', 75, 1) - emagged = 1 - to_chat(user, "You you disable the security protocols") - src.updateUsrDialog() - return 1 - -/obj/machinery/computer/telecomms/monitor/proc/set_temp(var/text, var/color = "average") +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + + +/* + Telecomms monitor tracks the overall trafficing of a telecommunications network + and displays a heirarchy of linked machines. +*/ + +/obj/machinery/computer/telecomms/monitor + name = "Telecommunications Monitor" + desc = "Used to traverse a telecommunication network. Helpful for debugging connection issues." + icon_screen = "comm_monitor" + + var/screen = 0 // the screen number: + var/list/machinelist = list() // the machines located by the computer + var/obj/machinery/telecomms/SelectedMachine + circuit = /obj/item/weapon/circuitboard/comm_monitor + + var/network = "NULL" // the network to probe + + var/list/temp = null // temporary feedback messages + +/obj/machinery/computer/telecomms/monitor/tgui_data(mob/user) + var/list/data = list() + + data["network"] = network + data["temp"] = temp + + var/list/machinelistData = list() + for(var/obj/machinery/telecomms/T in machinelist) + machinelistData.Add(list(list( + "id" = T.id, + "name" = T.name, + ))) + data["machinelist"] = machinelistData + + data["selectedMachine"] = null + if(SelectedMachine) + data["selectedMachine"] = list( + "id" = SelectedMachine.id, + "name" = SelectedMachine.name, + ) + var/list/links = list() + for(var/obj/machinery/telecomms/T in SelectedMachine.links) + if(!T.hide) + links.Add(list(list( + "id" = T.id, + "name" = T.name + ))) + data["selectedMachine"]["links"] = links + return data + +/obj/machinery/computer/telecomms/monitor/attack_hand(mob/user) + if(stat & (BROKEN|NOPOWER)) + return + tgui_interact(user) + +/obj/machinery/computer/telecomms/monitor/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "TelecommsMachineBrowser", name) + ui.open() + +/obj/machinery/computer/telecomms/monitor/tgui_act(action, params) + if(..()) + return TRUE + + add_fingerprint(usr) + + switch(action) + if("view") + for(var/obj/machinery/telecomms/T in machinelist) + if(T.id == params["id"]) + SelectedMachine = T + break + . = TRUE + + if("mainmenu") + SelectedMachine = null + . = TRUE + + if("release") + machinelist = list() + SelectedMachine = null + . = TRUE + + if("scan") + if(machinelist.len > 0) + set_temp("FAILED: CANNOT PROBE WHEN BUFFER FULL", "bad") + return TRUE + + for(var/obj/machinery/telecomms/T in range(25, src)) + if(T.network == network) + machinelist.Add(T) + + if(!machinelist.len) + set_temp("FAILED: UNABLE TO LOCATE NETWORK ENTITIES IN \[[network]\]", "bad") + else + set_temp("[machinelist.len] ENTITIES LOCATED & BUFFERED", "good") + . = TRUE + + if("network") + var/newnet = tgui_input_text(usr, "Which network do you want to view?", "Comm Monitor", network, 15) + newnet = sanitize(newnet,15) //Honestly, I'd be amazed if someone managed to do HTML in 15 chars. + if(newnet && ((usr in range(1, src) || issilicon(usr)))) + if(length(newnet) > 15) + set_temp("FAILED: NETWORK TAG STRING TOO LENGTHY", "bad") + return TRUE + network = newnet + machinelist = list() + set_temp("NEW NETWORK TAG SET IN ADDRESS \[[network]\]", "good") + + . = TRUE + + if("cleartemp") + temp = null + . = TRUE + + +/obj/machinery/computer/telecomms/monitor/emag_act(var/remaining_charges, var/mob/user) + if(!emagged) + playsound(src, 'sound/effects/sparks4.ogg', 75, 1) + emagged = 1 + to_chat(user, "You you disable the security protocols") + src.updateUsrDialog() + return 1 + +/obj/machinery/computer/telecomms/monitor/proc/set_temp(var/text, var/color = "average") temp = list("color" = color, "text" = text) \ No newline at end of file diff --git a/code/game/machinery/telecomms/traffic_control.dm b/code/game/machinery/telecomms/traffic_control.dm index 17dbed4e2cc..9f14a300842 100644 --- a/code/game/machinery/telecomms/traffic_control.dm +++ b/code/game/machinery/telecomms/traffic_control.dm @@ -1,218 +1,218 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - - - - - -/obj/machinery/computer/telecomms/traffic - name = "Telecommunications Traffic Control" - desc = "Used to upload code to telecommunication consoles for execution." - icon_screen = "generic" - - var/screen = 0 // the screen number: - var/list/servers = list() // the servers located by the computer - var/mob/editingcode - var/mob/lasteditor - var/list/viewingcode = list() - var/obj/machinery/telecomms/server/SelectedServer - circuit = /obj/item/weapon/circuitboard/comm_traffic - req_access = list(access_tcomsat) - - var/network = "NULL" // the network to probe - var/temp = "" // temporary feedback messages - - var/storedcode = "" // code stored - - -/obj/machinery/computer/telecomms/traffic/proc/update_ide() - - // loop if there's someone manning the keyboard - while(editingcode) - if(!editingcode.client) - editingcode = null - break - - // For the typer, the input is enabled. Buffer the typed text - if(editingcode) - storedcode = "[winget(editingcode, "tcscode", "text")]" - if(editingcode) // double if's to work around a runtime error - winset(editingcode, "tcscode", "is-disabled=false") - - // If the player's not manning the keyboard anymore, adjust everything - if( (!(editingcode in range(1, src)) && !issilicon(editingcode)) || (editingcode.machine != src && !issilicon(editingcode))) - if(editingcode) - winshow(editingcode, "Telecomms IDE", 0) // hide the window! - editingcode = null - break - - // For other people viewing the typer type code, the input is disabled and they can only view the code - // (this is put in place so that there's not any magical shenanigans with 50 people inputting different code all at once) - - if(length(viewingcode)) - // This piece of code is very important - it escapes quotation marks so string aren't cut off by the input element - var/showcode = replacetext(storedcode, "\\\"", "\\\\\"") - showcode = replacetext(storedcode, "\"", "\\\"") - - for(var/mob/M in viewingcode) - - if( (M.machine == src && (M in view(1, src)) ) || issilicon(M)) - winset(M, "tcscode", "is-disabled=true") - winset(M, "tcscode", "text=\"[showcode]\"") - else - viewingcode.Remove(M) - winshow(M, "Telecomms IDE", 0) // hide the window! - - sleep(5) - - if(length(viewingcode) > 0) - editingcode = pick(viewingcode) - viewingcode.Remove(editingcode) - update_ide() - - - - -/obj/machinery/computer/telecomms/traffic/attack_hand(mob/user as mob) - if(stat & (BROKEN|NOPOWER)) - return - user.set_machine(src) - var/dat = "Telecommunication Traffic Control
                    Telecommunications Traffic Control
                    " - - switch(screen) - - - // --- Main Menu --- - - if(0) - dat += "
                    [temp]
                    " - dat += "
                    Current Network:
                    [network]
                    " - if(servers.len) - dat += "
                    Detected Telecommunication Servers:
                      " - for(var/obj/machinery/telecomms/T in servers) - dat += "
                    • \ref[T] [T.name] ([T.id])
                    • " - dat += "
                    " - dat += "
                    \[Flush Buffer\]" - - else - dat += "
                    No servers detected. Scan for servers: \[Scan\]" - - - // --- Viewing Server --- - - if(1) - dat += "
                    [temp]
                    " - dat += "
                    \[Main Menu\] \[Refresh\]
                    " - dat += "
                    Current Network: [network]" - dat += "
                    Selected Server: [SelectedServer.id]

                    " - dat += "
                    \[Edit Code\]" - dat += "
                    Signal Execution: " - if(SelectedServer.autoruncode) - dat += "ALWAYS" - else - dat += "NEVER" - - - user << browse(dat, "window=traffic_control;size=575x400") - onclose(user, "server_control") - - temp = "" - return - - -/obj/machinery/computer/telecomms/traffic/Topic(href, href_list) - if(..()) - return - - - add_fingerprint(usr) - usr.set_machine(src) - if(!src.allowed(usr) && !emagged) - to_chat(usr, "ACCESS DENIED.") - return - - if(href_list["viewserver"]) - screen = 1 - for(var/obj/machinery/telecomms/T in servers) - if(T.id == href_list["viewserver"]) - SelectedServer = T - break - - if(href_list["operation"]) - switch(href_list["operation"]) - - if("release") - servers = list() - screen = 0 - - if("mainmenu") - screen = 0 - - if("scan") - if(servers.len > 0) - temp = "- FAILED: CANNOT PROBE WHEN BUFFER FULL -" - - else - for(var/obj/machinery/telecomms/server/T in range(25, src)) - if(T.network == network) - servers.Add(T) - - if(!servers.len) - temp = "- FAILED: UNABLE TO LOCATE SERVERS IN \[[network]\] -" - else - temp = "- [servers.len] SERVERS PROBED & BUFFERED -" - - screen = 0 - - if("editcode") - if(editingcode == usr) return - if(usr in viewingcode) return - - if(!editingcode) - lasteditor = usr - editingcode = usr - winshow(editingcode, "Telecomms IDE", 1) // show the IDE - winset(editingcode, "tcscode", "is-disabled=false") - winset(editingcode, "tcscode", "text=\"\"") - var/showcode = replacetext(storedcode, "\\\"", "\\\\\"") - showcode = replacetext(storedcode, "\"", "\\\"") - winset(editingcode, "tcscode", "text=\"[showcode]\"") - spawn() - update_ide() - - else - viewingcode.Add(usr) - winshow(usr, "Telecomms IDE", 1) // show the IDE - winset(usr, "tcscode", "is-disabled=true") - winset(editingcode, "tcscode", "text=\"\"") - var/showcode = replacetext(storedcode, "\"", "\\\"") - winset(usr, "tcscode", "text=\"[showcode]\"") - - if("togglerun") - SelectedServer.autoruncode = !(SelectedServer.autoruncode) - - if(href_list["network"]) - - var/newnet = tgui_input_text(usr, "Which network do you want to view?", "Comm Monitor", network, 15) - newnet = sanitize(newnet,15) - - if(newnet && ((usr in range(1, src) || issilicon(usr)))) - if(length(newnet) > 15) - temp = "- FAILED: NETWORK TAG STRING TOO LENGHTLY -" - - else - - network = newnet - screen = 0 - servers = list() - temp = "- NEW NETWORK TAG SET IN ADDRESS \[[network]\] -" - - updateUsrDialog() - return - -/obj/machinery/computer/telecomms/traffic/emag_act(var/remaining_charges, var/mob/user) - if(!emagged) - playsound(src, 'sound/effects/sparks4.ogg', 75, 1) - emagged = 1 - to_chat(user, "You you disable the security protocols") - src.updateUsrDialog() +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + + + + + +/obj/machinery/computer/telecomms/traffic + name = "Telecommunications Traffic Control" + desc = "Used to upload code to telecommunication consoles for execution." + icon_screen = "generic" + + var/screen = 0 // the screen number: + var/list/servers = list() // the servers located by the computer + var/mob/editingcode + var/mob/lasteditor + var/list/viewingcode = list() + var/obj/machinery/telecomms/server/SelectedServer + circuit = /obj/item/weapon/circuitboard/comm_traffic + req_access = list(access_tcomsat) + + var/network = "NULL" // the network to probe + var/temp = "" // temporary feedback messages + + var/storedcode = "" // code stored + + +/obj/machinery/computer/telecomms/traffic/proc/update_ide() + + // loop if there's someone manning the keyboard + while(editingcode) + if(!editingcode.client) + editingcode = null + break + + // For the typer, the input is enabled. Buffer the typed text + if(editingcode) + storedcode = "[winget(editingcode, "tcscode", "text")]" + if(editingcode) // double if's to work around a runtime error + winset(editingcode, "tcscode", "is-disabled=false") + + // If the player's not manning the keyboard anymore, adjust everything + if( (!(editingcode in range(1, src)) && !issilicon(editingcode)) || (editingcode.machine != src && !issilicon(editingcode))) + if(editingcode) + winshow(editingcode, "Telecomms IDE", 0) // hide the window! + editingcode = null + break + + // For other people viewing the typer type code, the input is disabled and they can only view the code + // (this is put in place so that there's not any magical shenanigans with 50 people inputting different code all at once) + + if(length(viewingcode)) + // This piece of code is very important - it escapes quotation marks so string aren't cut off by the input element + var/showcode = replacetext(storedcode, "\\\"", "\\\\\"") + showcode = replacetext(storedcode, "\"", "\\\"") + + for(var/mob/M in viewingcode) + + if( (M.machine == src && (M in view(1, src)) ) || issilicon(M)) + winset(M, "tcscode", "is-disabled=true") + winset(M, "tcscode", "text=\"[showcode]\"") + else + viewingcode.Remove(M) + winshow(M, "Telecomms IDE", 0) // hide the window! + + sleep(5) + + if(length(viewingcode) > 0) + editingcode = pick(viewingcode) + viewingcode.Remove(editingcode) + update_ide() + + + + +/obj/machinery/computer/telecomms/traffic/attack_hand(mob/user as mob) + if(stat & (BROKEN|NOPOWER)) + return + user.set_machine(src) + var/dat = "Telecommunication Traffic Control
                    Telecommunications Traffic Control
                    " + + switch(screen) + + + // --- Main Menu --- + + if(0) + dat += "
                    [temp]
                    " + dat += "
                    Current Network: [network]
                    " + if(servers.len) + dat += "
                    Detected Telecommunication Servers:
                      " + for(var/obj/machinery/telecomms/T in servers) + dat += "
                    • \ref[T] [T.name] ([T.id])
                    • " + dat += "
                    " + dat += "
                    \[Flush Buffer\]" + + else + dat += "
                    No servers detected. Scan for servers: \[Scan\]" + + + // --- Viewing Server --- + + if(1) + dat += "
                    [temp]
                    " + dat += "
                    \[Main Menu\] \[Refresh\]
                    " + dat += "
                    Current Network: [network]" + dat += "
                    Selected Server: [SelectedServer.id]

                    " + dat += "
                    \[Edit Code\]" + dat += "
                    Signal Execution: " + if(SelectedServer.autoruncode) + dat += "ALWAYS" + else + dat += "NEVER" + + + user << browse(dat, "window=traffic_control;size=575x400") + onclose(user, "server_control") + + temp = "" + return + + +/obj/machinery/computer/telecomms/traffic/Topic(href, href_list) + if(..()) + return + + + add_fingerprint(usr) + usr.set_machine(src) + if(!src.allowed(usr) && !emagged) + to_chat(usr, "ACCESS DENIED.") + return + + if(href_list["viewserver"]) + screen = 1 + for(var/obj/machinery/telecomms/T in servers) + if(T.id == href_list["viewserver"]) + SelectedServer = T + break + + if(href_list["operation"]) + switch(href_list["operation"]) + + if("release") + servers = list() + screen = 0 + + if("mainmenu") + screen = 0 + + if("scan") + if(servers.len > 0) + temp = "- FAILED: CANNOT PROBE WHEN BUFFER FULL -" + + else + for(var/obj/machinery/telecomms/server/T in range(25, src)) + if(T.network == network) + servers.Add(T) + + if(!servers.len) + temp = "- FAILED: UNABLE TO LOCATE SERVERS IN \[[network]\] -" + else + temp = "- [servers.len] SERVERS PROBED & BUFFERED -" + + screen = 0 + + if("editcode") + if(editingcode == usr) return + if(usr in viewingcode) return + + if(!editingcode) + lasteditor = usr + editingcode = usr + winshow(editingcode, "Telecomms IDE", 1) // show the IDE + winset(editingcode, "tcscode", "is-disabled=false") + winset(editingcode, "tcscode", "text=\"\"") + var/showcode = replacetext(storedcode, "\\\"", "\\\\\"") + showcode = replacetext(storedcode, "\"", "\\\"") + winset(editingcode, "tcscode", "text=\"[showcode]\"") + spawn() + update_ide() + + else + viewingcode.Add(usr) + winshow(usr, "Telecomms IDE", 1) // show the IDE + winset(usr, "tcscode", "is-disabled=true") + winset(editingcode, "tcscode", "text=\"\"") + var/showcode = replacetext(storedcode, "\"", "\\\"") + winset(usr, "tcscode", "text=\"[showcode]\"") + + if("togglerun") + SelectedServer.autoruncode = !(SelectedServer.autoruncode) + + if(href_list["network"]) + + var/newnet = tgui_input_text(usr, "Which network do you want to view?", "Comm Monitor", network, 15) + newnet = sanitize(newnet,15) + + if(newnet && ((usr in range(1, src) || issilicon(usr)))) + if(length(newnet) > 15) + temp = "- FAILED: NETWORK TAG STRING TOO LENGHTLY -" + + else + + network = newnet + screen = 0 + servers = list() + temp = "- NEW NETWORK TAG SET IN ADDRESS \[[network]\] -" + + updateUsrDialog() + return + +/obj/machinery/computer/telecomms/traffic/emag_act(var/remaining_charges, var/mob/user) + if(!emagged) + playsound(src, 'sound/effects/sparks4.ogg', 75, 1) + emagged = 1 + to_chat(user, "You you disable the security protocols") + src.updateUsrDialog() return 1 \ No newline at end of file diff --git a/code/game/machinery/transformer.dm b/code/game/machinery/transformer.dm index 43bc97c20a2..8a09b6f048a 100644 --- a/code/game/machinery/transformer.dm +++ b/code/game/machinery/transformer.dm @@ -1,56 +1,56 @@ -/obj/machinery/transformer - name = "Automatic Robotic Factory 5000" - desc = "A large metalic machine with an entrance and an exit. A sign on the side reads, 'human go in, robot come out', human must be lying down and alive." - icon = 'icons/obj/recycling.dmi' - icon_state = "separator-AO1" - layer = MOB_LAYER+1 // Overhead - anchored = TRUE - density = TRUE - var/transform_dead = 0 - var/transform_standing = 0 - -/obj/machinery/transformer/New() - // On us - ..() - new /obj/machinery/conveyor(loc, WEST, 1) - -/obj/machinery/transformer/Bumped(var/atom/movable/AM) - // HasEntered didn't like people lying down. - if(ishuman(AM)) - // Only humans can enter from the west side, while lying down. - var/move_dir = get_dir(loc, AM.loc) - var/mob/living/carbon/human/H = AM - if((transform_standing || H.lying) && move_dir == EAST)// || move_dir == WEST) - AM.loc = src.loc - transform(AM) - -/obj/machinery/transformer/proc/transform(var/mob/living/carbon/human/H) - if(stat & (BROKEN|NOPOWER)) - return - if(!transform_dead && H.stat == DEAD) - playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 0) - return - playsound(src, 'sound/items/Welder.ogg', 50, 1) - use_power(5000) // Use a lot of power. - var/mob/living/silicon/robot = H.Robotize() - robot.SetLockDown() - spawn(50) // So he can't jump out the gate right away. - playsound(src, 'sound/machines/ping.ogg', 50, 0) - if(robot) - robot.SetLockDown(0) - -/obj/machinery/transformer/conveyor/New() - ..() - var/turf/T = loc - if(T) - // Spawn Conveyour Belts - - //East - var/turf/east = locate(T.x + 1, T.y, T.z) - if(istype(east, /turf/simulated/floor)) - new /obj/machinery/conveyor(east, WEST, 1) - - // West - var/turf/west = locate(T.x - 1, T.y, T.z) - if(istype(west, /turf/simulated/floor)) +/obj/machinery/transformer + name = "Automatic Robotic Factory 5000" + desc = "A large metalic machine with an entrance and an exit. A sign on the side reads, 'human go in, robot come out', human must be lying down and alive." + icon = 'icons/obj/recycling.dmi' + icon_state = "separator-AO1" + layer = MOB_LAYER+1 // Overhead + anchored = TRUE + density = TRUE + var/transform_dead = 0 + var/transform_standing = 0 + +/obj/machinery/transformer/New() + // On us + ..() + new /obj/machinery/conveyor(loc, WEST, 1) + +/obj/machinery/transformer/Bumped(var/atom/movable/AM) + // HasEntered didn't like people lying down. + if(ishuman(AM)) + // Only humans can enter from the west side, while lying down. + var/move_dir = get_dir(loc, AM.loc) + var/mob/living/carbon/human/H = AM + if((transform_standing || H.lying) && move_dir == EAST)// || move_dir == WEST) + AM.loc = src.loc + transform(AM) + +/obj/machinery/transformer/proc/transform(var/mob/living/carbon/human/H) + if(stat & (BROKEN|NOPOWER)) + return + if(!transform_dead && H.stat == DEAD) + playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 0) + return + playsound(src, 'sound/items/Welder.ogg', 50, 1) + use_power(5000) // Use a lot of power. + var/mob/living/silicon/robot = H.Robotize() + robot.SetLockDown() + spawn(50) // So he can't jump out the gate right away. + playsound(src, 'sound/machines/ping.ogg', 50, 0) + if(robot) + robot.SetLockDown(0) + +/obj/machinery/transformer/conveyor/New() + ..() + var/turf/T = loc + if(T) + // Spawn Conveyour Belts + + //East + var/turf/east = locate(T.x + 1, T.y, T.z) + if(istype(east, /turf/simulated/floor)) + new /obj/machinery/conveyor(east, WEST, 1) + + // West + var/turf/west = locate(T.x - 1, T.y, T.z) + if(istype(west, /turf/simulated/floor)) new /obj/machinery/conveyor(west, WEST, 1) \ No newline at end of file diff --git a/code/game/machinery/wall_frames.dm b/code/game/machinery/wall_frames.dm index 696b46af788..eb3f4d428d8 100644 --- a/code/game/machinery/wall_frames.dm +++ b/code/game/machinery/wall_frames.dm @@ -1,150 +1,150 @@ -/obj/item/frame - name = "frame parts" - desc = "Used for building frames." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "frame_bitem" - var/build_machine_type - var/build_wall_only = FALSE - var/refund_amt = 5 - var/refund_type = /obj/item/stack/material/steel - var/reverse = 0 //if resulting object faces opposite its dir (like light fixtures) - var/list/frame_types_floor - var/list/frame_types_wall - -/obj/item/frame/proc/update_type_list() - if(!frame_types_floor) - frame_types_floor = construction_frame_floor - if(!frame_types_wall) - frame_types_wall = construction_frame_wall - -/obj/item/frame/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.has_tool_quality(TOOL_WRENCH)) - new refund_type(get_turf(src.loc), refund_amt) - qdel(src) - return - ..() - -/obj/item/frame/attack_self(mob/user as mob) - ..() - update_type_list() - var/datum/frame/frame_types/frame_type - if(!build_machine_type && !build_wall_only) - var/datum/frame/frame_types/response = tgui_input_list(user, "What kind of frame would you like to make?", "Frame type request", frame_types_floor) - if(!response) - return - frame_type = response - - build_machine_type = /obj/structure/frame - - if(frame_type.frame_size != 5) - new /obj/item/stack/material/steel(user.loc, (5 - frame_type.frame_size)) - - var/ndir - ndir = user.dir - if(!(ndir in cardinal)) - return - - var/obj/machinery/M = new build_machine_type(get_turf(src.loc), ndir, 1, frame_type) - M.fingerprints = fingerprints - M.fingerprintshidden = fingerprintshidden - M.fingerprintslast = fingerprintslast - if(istype(src.loc, /obj/item/weapon/gripper)) //Typical gripper shenanigans - user.drop_item() - qdel(src) - -/obj/item/frame/proc/try_build(turf/on_wall, mob/user as mob) - update_type_list() - - if(get_dist(on_wall, user)>1) - return - - var/ndir - if(reverse) - ndir = get_dir(user, on_wall) - else - ndir = get_dir(on_wall, user) - - if(!(ndir in cardinal)) - return - - var/turf/loc = get_turf(user) - var/area/A = loc.loc - if(!istype(loc, /turf/simulated/floor)) - to_chat(user, "\The frame cannot be placed on this spot.") - return - - if(A.requires_power == 0 || A.name == "Space") - to_chat(user, "\The [src] Alarm cannot be placed in this area.") - return - - if(gotwallitem(loc, ndir)) - to_chat(user, "There's already an item on this wall!") - return - - var/datum/frame/frame_types/frame_type - if(!build_machine_type) - var/datum/frame/frame_types/response = tgui_input_list(user, "What kind of frame would you like to make?", "Frame type request", frame_types_wall) - if(!response) - return - frame_type = response - - build_machine_type = /obj/structure/frame - - if(frame_type.frame_size != 5) - new /obj/item/stack/material/steel(user.loc, (5 - frame_type.frame_size)) - - var/obj/machinery/M = new build_machine_type(loc, ndir, 1, frame_type) - M.fingerprints = fingerprints - M.fingerprintshidden = fingerprintshidden - M.fingerprintslast = fingerprintslast - if(istype(src.loc, /obj/item/weapon/gripper)) //Typical gripper shenanigans - user.drop_item() - qdel(src) - -/obj/item/frame/light - name = "light fixture frame" - desc = "Used for building lights." - icon = 'icons/obj/lighting.dmi' - icon_state = "tube-construct-item" - refund_amt = 2 - build_machine_type = /obj/machinery/light_construct - reverse = 1 - -/obj/item/frame/light/small - name = "small light fixture frame" - icon_state = "bulb-construct-item" - refund_amt = 1 - build_machine_type = /obj/machinery/light_construct/small - -/obj/item/frame/extinguisher_cabinet - name = "extinguisher cabinet frame" - desc = "Used for building fire extinguisher cabinets." - icon = 'icons/obj/closet.dmi' - icon_state = "extinguisher_empty" - refund_amt = 4 - build_machine_type = /obj/structure/extinguisher_cabinet - -/obj/item/frame/noticeboard - name = "noticeboard frame" - desc = "Used for building noticeboards." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "nboard00" - refund_amt = 4 - refund_type = /obj/item/stack/material/wood - build_machine_type = /obj/structure/noticeboard - -/obj/item/frame/mirror - name = "mirror frame" - desc = "Used for building mirrors." - icon = 'icons/obj/watercloset.dmi' - icon_state = "mirror_frame" - refund_amt = 1 - build_machine_type = /obj/structure/mirror - -/obj/item/frame/fireaxe_cabinet - name = "fire axe cabinet frame" - desc = "Used for building fire axe cabinets." - icon = 'icons/obj/closet.dmi' - icon_state = "fireaxe0101" - refund_amt = 4 - build_machine_type = /obj/structure/fireaxecabinet +/obj/item/frame + name = "frame parts" + desc = "Used for building frames." + icon = 'icons/obj/stock_parts.dmi' + icon_state = "frame_bitem" + var/build_machine_type + var/build_wall_only = FALSE + var/refund_amt = 5 + var/refund_type = /obj/item/stack/material/steel + var/reverse = 0 //if resulting object faces opposite its dir (like light fixtures) + var/list/frame_types_floor + var/list/frame_types_wall + +/obj/item/frame/proc/update_type_list() + if(!frame_types_floor) + frame_types_floor = construction_frame_floor + if(!frame_types_wall) + frame_types_wall = construction_frame_wall + +/obj/item/frame/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + new refund_type(get_turf(src.loc), refund_amt) + qdel(src) + return + ..() + +/obj/item/frame/attack_self(mob/user as mob) + ..() + update_type_list() + var/datum/frame/frame_types/frame_type + if(!build_machine_type && !build_wall_only) + var/datum/frame/frame_types/response = tgui_input_list(user, "What kind of frame would you like to make?", "Frame type request", frame_types_floor) + if(!response) + return + frame_type = response + + build_machine_type = /obj/structure/frame + + if(frame_type.frame_size != 5) + new /obj/item/stack/material/steel(user.loc, (5 - frame_type.frame_size)) + + var/ndir + ndir = user.dir + if(!(ndir in cardinal)) + return + + var/obj/machinery/M = new build_machine_type(get_turf(src.loc), ndir, 1, frame_type) + M.fingerprints = fingerprints + M.fingerprintshidden = fingerprintshidden + M.fingerprintslast = fingerprintslast + if(istype(src.loc, /obj/item/weapon/gripper)) //Typical gripper shenanigans + user.drop_item() + qdel(src) + +/obj/item/frame/proc/try_build(turf/on_wall, mob/user as mob) + update_type_list() + + if(get_dist(on_wall, user)>1) + return + + var/ndir + if(reverse) + ndir = get_dir(user, on_wall) + else + ndir = get_dir(on_wall, user) + + if(!(ndir in cardinal)) + return + + var/turf/loc = get_turf(user) + var/area/A = loc.loc + if(!istype(loc, /turf/simulated/floor)) + to_chat(user, "\The frame cannot be placed on this spot.") + return + + if(A.requires_power == 0 || A.name == "Space") + to_chat(user, "\The [src] Alarm cannot be placed in this area.") + return + + if(gotwallitem(loc, ndir)) + to_chat(user, "There's already an item on this wall!") + return + + var/datum/frame/frame_types/frame_type + if(!build_machine_type) + var/datum/frame/frame_types/response = tgui_input_list(user, "What kind of frame would you like to make?", "Frame type request", frame_types_wall) + if(!response) + return + frame_type = response + + build_machine_type = /obj/structure/frame + + if(frame_type.frame_size != 5) + new /obj/item/stack/material/steel(user.loc, (5 - frame_type.frame_size)) + + var/obj/machinery/M = new build_machine_type(loc, ndir, 1, frame_type) + M.fingerprints = fingerprints + M.fingerprintshidden = fingerprintshidden + M.fingerprintslast = fingerprintslast + if(istype(src.loc, /obj/item/weapon/gripper)) //Typical gripper shenanigans + user.drop_item() + qdel(src) + +/obj/item/frame/light + name = "light fixture frame" + desc = "Used for building lights." + icon = 'icons/obj/lighting.dmi' + icon_state = "tube-construct-item" + refund_amt = 2 + build_machine_type = /obj/machinery/light_construct + reverse = 1 + +/obj/item/frame/light/small + name = "small light fixture frame" + icon_state = "bulb-construct-item" + refund_amt = 1 + build_machine_type = /obj/machinery/light_construct/small + +/obj/item/frame/extinguisher_cabinet + name = "extinguisher cabinet frame" + desc = "Used for building fire extinguisher cabinets." + icon = 'icons/obj/closet.dmi' + icon_state = "extinguisher_empty" + refund_amt = 4 + build_machine_type = /obj/structure/extinguisher_cabinet + +/obj/item/frame/noticeboard + name = "noticeboard frame" + desc = "Used for building noticeboards." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "nboard00" + refund_amt = 4 + refund_type = /obj/item/stack/material/wood + build_machine_type = /obj/structure/noticeboard + +/obj/item/frame/mirror + name = "mirror frame" + desc = "Used for building mirrors." + icon = 'icons/obj/watercloset.dmi' + icon_state = "mirror_frame" + refund_amt = 1 + build_machine_type = /obj/structure/mirror + +/obj/item/frame/fireaxe_cabinet + name = "fire axe cabinet frame" + desc = "Used for building fire axe cabinets." + icon = 'icons/obj/closet.dmi' + icon_state = "fireaxe0101" + refund_amt = 4 + build_machine_type = /obj/structure/fireaxecabinet diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm index e3c6a909767..ca1546190a4 100644 --- a/code/game/machinery/washing_machine.dm +++ b/code/game/machinery/washing_machine.dm @@ -1,185 +1,185 @@ -/obj/machinery/washing_machine - name = "Washing Machine" - desc = "Not a hiding place. Unfit for pets." - icon = 'icons/obj/machines/washing_machine_vr.dmi' //VOREStation Edit - icon_state = "wm_1" //VOREStation Edit - density = TRUE - anchored = TRUE - clicksound = "button" - clickvol = 40 - - circuit = /obj/item/weapon/circuitboard/washing - var/state = 1 - //1 = empty, open door - //2 = empty, closed door - //3 = full, open door - //4 = full, closed door - //5 = running - //6 = blood, open door - //7 = blood, closed door - //8 = blood, running - var/hacked = 1 //Bleh, screw hacking, let's have it hacked by default. - //0 = not hacked - //1 = hacked - var/gibs_ready = 0 - var/obj/crayon - var/list/washing = list() - var/list/disallowed_types = list( - /obj/item/clothing/suit/space, - /obj/item/clothing/head/helmet/space - ) - -/obj/machinery/washing_machine/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/washing_machine/AltClick() - start() - -/obj/machinery/washing_machine/verb/start_washing() - set name = "Start Washing" - set category = "Object" - set src in oview(1) - start() - -/obj/machinery/washing_machine/proc/start() - - if(!istype(usr, /mob/living)) //ew ew ew usr, but it's the only way to check. - return - - if(state != 4) - to_chat(usr, "The washing machine cannot run in this state.") - return - - if(locate(/mob,washing)) - state = 8 - else - state = 5 - update_icon() - to_chat(usr, "The washing machine starts a cycle.") - playsound(src, 'sound/items/washingmachine.ogg', 50, 1, 1) - sleep(200) - for(var/atom/A in washing) - A.clean_blood() - - for(var/obj/item/I in washing) - I.decontaminate() - - //Tanning! - for(var/obj/item/stack/hairlesshide/HH in washing) - var/obj/item/stack/wetleather/WL = new(src, HH.get_amount()) - washing -= HH - HH.forceMove(get_turf(src)) - HH.use(HH.get_amount()) - - washing += WL - - if(locate(/mob,washing)) - state = 7 - gibs_ready = 1 - else - state = 4 - update_icon() - -/obj/machinery/washing_machine/verb/climb_out() - set name = "Climb out" - set category = "Object" - set src in usr.loc - - sleep(20) - if(state in list(1,3,6)) - usr.loc = src.loc - -/obj/machinery/washing_machine/update_icon() - //VOREStation Edit - cut_overlays() - icon_state = "wm_[state]" - if(panel_open) - add_overlay("panel") - //VOREStation Edit End - -/obj/machinery/washing_machine/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(state == 2 && washing.len < 1) - if(default_deconstruction_screwdriver(user, W)) - return - if(default_deconstruction_crowbar(user, W)) - return - if(default_unfasten_wrench(user, W, 40)) - return - /*if(W.has_tool_quality(TOOL_SCREWDRIVER)) - panel = !panel - to_chat(user, "You [panel ? "open" : "close"] the [src]'s maintenance panel")*/ - if(istype(W,/obj/item/weapon/pen/crayon) || istype(W,/obj/item/weapon/stamp)) - if(state in list( 1, 3, 6)) - if(!crayon) - user.drop_item() - crayon = W - crayon.loc = src - else - ..() - else - ..() - else if(istype(W,/obj/item/weapon/grab)) - if((state == 1) && hacked) - var/obj/item/weapon/grab/G = W - if(ishuman(G.assailant) && iscorgi(G.affecting)) - G.affecting.loc = src - qdel(G) - state = 3 - else - ..() - - else if(is_type_in_list(W, disallowed_types)) - to_chat(user, "You can't fit \the [W] inside.") - return - - else if(istype(W, /obj/item/clothing) || istype(W, /obj/item/weapon/bedsheet) || istype(W, /obj/item/stack/hairlesshide)) - if(washing.len < 5) - if(state in list(1, 3)) - user.drop_item() - W.loc = src - washing += W - state = 3 - else - to_chat(user, "You can't put the item in right now.") - else - to_chat(user, "The washing machine is full.") - else - ..() - update_icon() - -/obj/machinery/washing_machine/attack_hand(mob/user as mob) - switch(state) - if(1) - state = 2 - if(2) - state = 1 - for(var/atom/movable/O in washing) - O.loc = src.loc - washing.Cut() - if(3) - state = 4 - if(4) - state = 3 - for(var/atom/movable/O in washing) - O.loc = src.loc - crayon = null - washing.Cut() - state = 1 - if(5) - to_chat(user, "The [src] is busy.") - if(6) - state = 7 - if(7) - if(gibs_ready) - gibs_ready = 0 - if(locate(/mob,washing)) - var/mob/M = locate(/mob,washing) - M.gib() - for(var/atom/movable/O in washing) - O.loc = src.loc - crayon = null - state = 1 - washing.Cut() - - update_icon() +/obj/machinery/washing_machine + name = "Washing Machine" + desc = "Not a hiding place. Unfit for pets." + icon = 'icons/obj/machines/washing_machine_vr.dmi' //VOREStation Edit + icon_state = "wm_1" //VOREStation Edit + density = TRUE + anchored = TRUE + clicksound = "button" + clickvol = 40 + + circuit = /obj/item/weapon/circuitboard/washing + var/state = 1 + //1 = empty, open door + //2 = empty, closed door + //3 = full, open door + //4 = full, closed door + //5 = running + //6 = blood, open door + //7 = blood, closed door + //8 = blood, running + var/hacked = 1 //Bleh, screw hacking, let's have it hacked by default. + //0 = not hacked + //1 = hacked + var/gibs_ready = 0 + var/obj/crayon + var/list/washing = list() + var/list/disallowed_types = list( + /obj/item/clothing/suit/space, + /obj/item/clothing/head/helmet/space + ) + +/obj/machinery/washing_machine/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/washing_machine/AltClick() + start() + +/obj/machinery/washing_machine/verb/start_washing() + set name = "Start Washing" + set category = "Object" + set src in oview(1) + start() + +/obj/machinery/washing_machine/proc/start() + + if(!istype(usr, /mob/living)) //ew ew ew usr, but it's the only way to check. + return + + if(state != 4) + to_chat(usr, "The washing machine cannot run in this state.") + return + + if(locate(/mob,washing)) + state = 8 + else + state = 5 + update_icon() + to_chat(usr, "The washing machine starts a cycle.") + playsound(src, 'sound/items/washingmachine.ogg', 50, 1, 1) + sleep(200) + for(var/atom/A in washing) + A.clean_blood() + + for(var/obj/item/I in washing) + I.decontaminate() + + //Tanning! + for(var/obj/item/stack/hairlesshide/HH in washing) + var/obj/item/stack/wetleather/WL = new(src, HH.get_amount()) + washing -= HH + HH.forceMove(get_turf(src)) + HH.use(HH.get_amount()) + + washing += WL + + if(locate(/mob,washing)) + state = 7 + gibs_ready = 1 + else + state = 4 + update_icon() + +/obj/machinery/washing_machine/verb/climb_out() + set name = "Climb out" + set category = "Object" + set src in usr.loc + + sleep(20) + if(state in list(1,3,6)) + usr.loc = src.loc + +/obj/machinery/washing_machine/update_icon() + //VOREStation Edit + cut_overlays() + icon_state = "wm_[state]" + if(panel_open) + add_overlay("panel") + //VOREStation Edit End + +/obj/machinery/washing_machine/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(state == 2 && washing.len < 1) + if(default_deconstruction_screwdriver(user, W)) + return + if(default_deconstruction_crowbar(user, W)) + return + if(default_unfasten_wrench(user, W, 40)) + return + /*if(W.has_tool_quality(TOOL_SCREWDRIVER)) + panel = !panel + to_chat(user, "You [panel ? "open" : "close"] the [src]'s maintenance panel")*/ + if(istype(W,/obj/item/weapon/pen/crayon) || istype(W,/obj/item/weapon/stamp)) + if(state in list( 1, 3, 6)) + if(!crayon) + user.drop_item() + crayon = W + crayon.loc = src + else + ..() + else + ..() + else if(istype(W,/obj/item/weapon/grab)) + if((state == 1) && hacked) + var/obj/item/weapon/grab/G = W + if(ishuman(G.assailant) && iscorgi(G.affecting)) + G.affecting.loc = src + qdel(G) + state = 3 + else + ..() + + else if(is_type_in_list(W, disallowed_types)) + to_chat(user, "You can't fit \the [W] inside.") + return + + else if(istype(W, /obj/item/clothing) || istype(W, /obj/item/weapon/bedsheet) || istype(W, /obj/item/stack/hairlesshide)) + if(washing.len < 5) + if(state in list(1, 3)) + user.drop_item() + W.loc = src + washing += W + state = 3 + else + to_chat(user, "You can't put the item in right now.") + else + to_chat(user, "The washing machine is full.") + else + ..() + update_icon() + +/obj/machinery/washing_machine/attack_hand(mob/user as mob) + switch(state) + if(1) + state = 2 + if(2) + state = 1 + for(var/atom/movable/O in washing) + O.loc = src.loc + washing.Cut() + if(3) + state = 4 + if(4) + state = 3 + for(var/atom/movable/O in washing) + O.loc = src.loc + crayon = null + washing.Cut() + state = 1 + if(5) + to_chat(user, "The [src] is busy.") + if(6) + state = 7 + if(7) + if(gibs_ready) + gibs_ready = 0 + if(locate(/mob,washing)) + var/mob/M = locate(/mob,washing) + M.gib() + for(var/atom/movable/O in washing) + O.loc = src.loc + crayon = null + state = 1 + washing.Cut() + + update_icon() diff --git a/code/game/mecha/combat/combat.dm b/code/game/mecha/combat/combat.dm index 1a163579c34..d99d2c913ec 100644 --- a/code/game/mecha/combat/combat.dm +++ b/code/game/mecha/combat/combat.dm @@ -1,158 +1,158 @@ -/obj/mecha/combat - force = 30 - var/melee_cooldown = 10 - var/melee_can_hit = 1 - //var/list/destroyable_obj = list(/obj/mecha, /obj/structure/window, /obj/structure/grille, /turf/simulated/wall, /obj/structure/girder) - internal_damage_threshold = 50 - maint_access = 0 - //add_req_access = 0 - //operation_req_access = list(access_hos) - var/am = "d3c2fbcadca903a41161ccc9df9cf948" - - max_hull_equip = 2 - max_weapon_equip = 2 - max_utility_equip = 1 - max_universal_equip = 1 - max_special_equip = 1 - cargo_capacity = 1 - - encumbrance_gap = 1.5 - - starting_components = list( - /obj/item/mecha_parts/component/hull/durable, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/reinforced, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - -/* -/obj/mecha/combat/range_action(target as obj|mob|turf) - if(internal_damage&MECHA_INT_CONTROL_LOST) - target = pick(view(3,target)) - if(selected_weapon) - selected_weapon.fire(target) - return -*/ - -/obj/mecha/combat/melee_action(atom/T) - if(internal_damage&MECHA_INT_CONTROL_LOST) - T = safepick(oview(1,src)) - if(!melee_can_hit) - return - if(istype(T, /mob/living)) - var/mob/living/M = T - if(src.occupant.a_intent == I_HURT || istype(src.occupant, /mob/living/carbon/brain)) //Brains cannot change intents; Exo-piloting brains lack any form of physical feedback for control, limiting the ability to 'play nice'. - playsound(src, 'sound/weapons/heavysmash.ogg', 50, 1) - if(damtype == "brute") - step_away(M,src,15) - /* - if(M.stat>1) - M.gib() - melee_can_hit = 0 - if(do_after(melee_cooldown)) - melee_can_hit = 1 - return - */ - if(ishuman(T)) - var/mob/living/carbon/human/H = T - // if (M.health <= 0) return - - var/obj/item/organ/external/temp = H.get_organ(pick(BP_TORSO, BP_TORSO, BP_TORSO, BP_HEAD)) - if(temp) - var/update = 0 - switch(damtype) - if("brute") - H.Paralyse(1) - update |= temp.take_damage(rand(force/2, force), 0) - if("fire") - update |= temp.take_damage(0, rand(force/2, force)) - if("tox") - if(H.reagents) - if(H.reagents.get_reagent_amount("carpotoxin") + force < force*2) - H.reagents.add_reagent("carpotoxin", force) - if(H.reagents.get_reagent_amount("cryptobiolin") + force < force*2) - H.reagents.add_reagent("cryptobiolin", force) - if("halloss") - H.stun_effect_act(1, force / 2, BP_TORSO, src) - else - return - if(update) H.UpdateDamageIcon() - H.updatehealth() - - else - switch(damtype) - if("brute") - M.Paralyse(1) - M.take_overall_damage(rand(force/2, force)) - if("fire") - M.take_overall_damage(0, rand(force/2, force)) - if("tox") - if(M.reagents) - if(M.reagents.get_reagent_amount("carpotoxin") + force < force*2) - M.reagents.add_reagent("carpotoxin", force) - if(M.reagents.get_reagent_amount("cryptobiolin") + force < force*2) - M.reagents.add_reagent("cryptobiolin", force) - else - return - M.updatehealth() - src.occupant_message("You hit [T].") - src.visible_message(span_red("[src.name] hits [T].")) - else - step_away(M,src) - src.occupant_message("You push [T] out of the way.") - src.visible_message("[src] pushes [T] out of the way.") - - melee_can_hit = 0 - if(do_after(melee_cooldown)) - melee_can_hit = 1 - return - - else - if(istype(T, /obj/machinery/disposal)) // Stops mechs from climbing into disposals - return - if(src.occupant.a_intent == I_HURT || istype(src.occupant, /mob/living/carbon/brain)) // Don't smash unless we mean it - if(damtype == "brute") - src.occupant_message("You hit [T].") - src.visible_message(span_red("[src.name] hits [T]")) - playsound(src, 'sound/weapons/heavysmash.ogg', 50, 1) - - if(istype(T, /obj/structure/girder)) - T:take_damage(force * 3) //Girders have 200 health by default. Steel, non-reinforced walls take four punches, girders take (with this value-mod) two, girders took five without. - else - T:take_damage(force) - - melee_can_hit = 0 - - if(do_after(melee_cooldown)) - melee_can_hit = 1 - return - -/obj/mecha/combat/moved_inside(var/mob/living/carbon/human/H as mob) - if(..()) - if(H.client) - H.client.mouse_pointer_icon = file("icons/mecha/mecha_mouse.dmi") - return 1 - else - return 0 - -/obj/mecha/combat/mmi_moved_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) - if(..()) - if(occupant.client) - occupant.client.mouse_pointer_icon = file("icons/mecha/mecha_mouse.dmi") - return 1 - else - return 0 - -/obj/mecha/combat/go_out() - if(src.occupant && src.occupant.client) - src.occupant.client.mouse_pointer_icon = initial(src.occupant.client.mouse_pointer_icon) - ..() - return - -/obj/mecha/combat/Topic(href,href_list) - ..() - var/datum/topic_input/top_filter = new (href,href_list) - if(top_filter.get("close")) - am = null - return +/obj/mecha/combat + force = 30 + var/melee_cooldown = 10 + var/melee_can_hit = 1 + //var/list/destroyable_obj = list(/obj/mecha, /obj/structure/window, /obj/structure/grille, /turf/simulated/wall, /obj/structure/girder) + internal_damage_threshold = 50 + maint_access = 0 + //add_req_access = 0 + //operation_req_access = list(access_hos) + var/am = "d3c2fbcadca903a41161ccc9df9cf948" + + max_hull_equip = 2 + max_weapon_equip = 2 + max_utility_equip = 1 + max_universal_equip = 1 + max_special_equip = 1 + cargo_capacity = 1 + + encumbrance_gap = 1.5 + + starting_components = list( + /obj/item/mecha_parts/component/hull/durable, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/reinforced, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + +/* +/obj/mecha/combat/range_action(target as obj|mob|turf) + if(internal_damage&MECHA_INT_CONTROL_LOST) + target = pick(view(3,target)) + if(selected_weapon) + selected_weapon.fire(target) + return +*/ + +/obj/mecha/combat/melee_action(atom/T) + if(internal_damage&MECHA_INT_CONTROL_LOST) + T = safepick(oview(1,src)) + if(!melee_can_hit) + return + if(istype(T, /mob/living)) + var/mob/living/M = T + if(src.occupant.a_intent == I_HURT || istype(src.occupant, /mob/living/carbon/brain)) //Brains cannot change intents; Exo-piloting brains lack any form of physical feedback for control, limiting the ability to 'play nice'. + playsound(src, 'sound/weapons/heavysmash.ogg', 50, 1) + if(damtype == "brute") + step_away(M,src,15) + /* + if(M.stat>1) + M.gib() + melee_can_hit = 0 + if(do_after(melee_cooldown)) + melee_can_hit = 1 + return + */ + if(ishuman(T)) + var/mob/living/carbon/human/H = T + // if (M.health <= 0) return + + var/obj/item/organ/external/temp = H.get_organ(pick(BP_TORSO, BP_TORSO, BP_TORSO, BP_HEAD)) + if(temp) + var/update = 0 + switch(damtype) + if("brute") + H.Paralyse(1) + update |= temp.take_damage(rand(force/2, force), 0) + if("fire") + update |= temp.take_damage(0, rand(force/2, force)) + if("tox") + if(H.reagents) + if(H.reagents.get_reagent_amount("carpotoxin") + force < force*2) + H.reagents.add_reagent("carpotoxin", force) + if(H.reagents.get_reagent_amount("cryptobiolin") + force < force*2) + H.reagents.add_reagent("cryptobiolin", force) + if("halloss") + H.stun_effect_act(1, force / 2, BP_TORSO, src) + else + return + if(update) H.UpdateDamageIcon() + H.updatehealth() + + else + switch(damtype) + if("brute") + M.Paralyse(1) + M.take_overall_damage(rand(force/2, force)) + if("fire") + M.take_overall_damage(0, rand(force/2, force)) + if("tox") + if(M.reagents) + if(M.reagents.get_reagent_amount("carpotoxin") + force < force*2) + M.reagents.add_reagent("carpotoxin", force) + if(M.reagents.get_reagent_amount("cryptobiolin") + force < force*2) + M.reagents.add_reagent("cryptobiolin", force) + else + return + M.updatehealth() + src.occupant_message("You hit [T].") + src.visible_message(span_red("[src.name] hits [T].")) + else + step_away(M,src) + src.occupant_message("You push [T] out of the way.") + src.visible_message("[src] pushes [T] out of the way.") + + melee_can_hit = 0 + if(do_after(melee_cooldown)) + melee_can_hit = 1 + return + + else + if(istype(T, /obj/machinery/disposal)) // Stops mechs from climbing into disposals + return + if(src.occupant.a_intent == I_HURT || istype(src.occupant, /mob/living/carbon/brain)) // Don't smash unless we mean it + if(damtype == "brute") + src.occupant_message("You hit [T].") + src.visible_message(span_red("[src.name] hits [T]")) + playsound(src, 'sound/weapons/heavysmash.ogg', 50, 1) + + if(istype(T, /obj/structure/girder)) + T:take_damage(force * 3) //Girders have 200 health by default. Steel, non-reinforced walls take four punches, girders take (with this value-mod) two, girders took five without. + else + T:take_damage(force) + + melee_can_hit = 0 + + if(do_after(melee_cooldown)) + melee_can_hit = 1 + return + +/obj/mecha/combat/moved_inside(var/mob/living/carbon/human/H as mob) + if(..()) + if(H.client) + H.client.mouse_pointer_icon = file("icons/mecha/mecha_mouse.dmi") + return 1 + else + return 0 + +/obj/mecha/combat/mmi_moved_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) + if(..()) + if(occupant.client) + occupant.client.mouse_pointer_icon = file("icons/mecha/mecha_mouse.dmi") + return 1 + else + return 0 + +/obj/mecha/combat/go_out() + if(src.occupant && src.occupant.client) + src.occupant.client.mouse_pointer_icon = initial(src.occupant.client.mouse_pointer_icon) + ..() + return + +/obj/mecha/combat/Topic(href,href_list) + ..() + var/datum/topic_input/top_filter = new (href,href_list) + if(top_filter.get("close")) + am = null + return diff --git a/code/game/mecha/combat/durand.dm b/code/game/mecha/combat/durand.dm index 4ea027f154d..44986197dd6 100644 --- a/code/game/mecha/combat/durand.dm +++ b/code/game/mecha/combat/durand.dm @@ -1,87 +1,87 @@ -/obj/mecha/combat/durand - desc = "An aging combat exosuit utilized by many corporations. Originally developed to combat hostile alien lifeforms." - name = "Durand" - icon_state = "durand" - initial_icon = "durand" - step_in = 4 - dir_in = 1 //Facing North. - health = 300 - maxhealth = 300 //Don't forget to update the /old variant if you change this number. - deflect_chance = 20 - max_temperature = 30000 - infra_luminosity = 8 - force = 40 - wreckage = /obj/effect/decal/mecha_wreckage/durand - - damage_minimum = 15 //Big stompy - minimum_penetration = 25 - - max_hull_equip = 2 - max_weapon_equip = 1 - max_utility_equip = 2 - max_universal_equip = 1 - max_special_equip = 1 - - starting_components = list( - /obj/item/mecha_parts/component/hull/durable, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/military, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - - defence_mode_possible = 1 - - icon_scale_x = 1.5 - icon_scale_y = 1.5 - -/* -/obj/mecha/combat/durand/New() - ..() - weapons += new /datum/mecha_weapon/ballistic/lmg(src) - weapons += new /datum/mecha_weapon/ballistic/scattershot(src) - selected_weapon = weapons[1] - return -*/ - - - -//This is for the Mech stats / Menu system. To be moved later on. -/obj/mecha/combat/durand/get_commands() - var/output = {"
                    -
                    Special
                    - -
                    - "} - output += ..() - return output - - -//Not needed anymore but left for reference. -/* -/obj/mecha/combat/durand/get_stats_part() - var/output = ..() - output += "Defence mode: [defence?"on":"off"]" - return output -*/ - -/* - -/obj/mecha/combat/durand/Topic(href, href_list) - ..() - if (href_list["toggle_defence_mode"]) - src.defence_mode() - return -*/ - -//Meant for random spawns. -/obj/mecha/combat/durand/old - desc = "An aging combat exosuit utilized by many corporations. Originally developed to combat hostile alien lifeforms. This one is particularly worn looking and likely isn't as sturdy." - -/obj/mecha/combat/durand/old/New() - ..() - health = 25 - maxhealth = 250 //Just slightly worse. - cell.charge = rand(0, (cell.charge/2)) +/obj/mecha/combat/durand + desc = "An aging combat exosuit utilized by many corporations. Originally developed to combat hostile alien lifeforms." + name = "Durand" + icon_state = "durand" + initial_icon = "durand" + step_in = 4 + dir_in = 1 //Facing North. + health = 300 + maxhealth = 300 //Don't forget to update the /old variant if you change this number. + deflect_chance = 20 + max_temperature = 30000 + infra_luminosity = 8 + force = 40 + wreckage = /obj/effect/decal/mecha_wreckage/durand + + damage_minimum = 15 //Big stompy + minimum_penetration = 25 + + max_hull_equip = 2 + max_weapon_equip = 1 + max_utility_equip = 2 + max_universal_equip = 1 + max_special_equip = 1 + + starting_components = list( + /obj/item/mecha_parts/component/hull/durable, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/military, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + + defence_mode_possible = 1 + + icon_scale_x = 1.5 + icon_scale_y = 1.5 + +/* +/obj/mecha/combat/durand/New() + ..() + weapons += new /datum/mecha_weapon/ballistic/lmg(src) + weapons += new /datum/mecha_weapon/ballistic/scattershot(src) + selected_weapon = weapons[1] + return +*/ + + + +//This is for the Mech stats / Menu system. To be moved later on. +/obj/mecha/combat/durand/get_commands() + var/output = {"
                    +
                    Special
                    + +
                    + "} + output += ..() + return output + + +//Not needed anymore but left for reference. +/* +/obj/mecha/combat/durand/get_stats_part() + var/output = ..() + output += "Defence mode: [defence?"on":"off"]" + return output +*/ + +/* + +/obj/mecha/combat/durand/Topic(href, href_list) + ..() + if (href_list["toggle_defence_mode"]) + src.defence_mode() + return +*/ + +//Meant for random spawns. +/obj/mecha/combat/durand/old + desc = "An aging combat exosuit utilized by many corporations. Originally developed to combat hostile alien lifeforms. This one is particularly worn looking and likely isn't as sturdy." + +/obj/mecha/combat/durand/old/New() + ..() + health = 25 + maxhealth = 250 //Just slightly worse. + cell.charge = rand(0, (cell.charge/2)) diff --git a/code/game/mecha/combat/gygax.dm b/code/game/mecha/combat/gygax.dm index 8e64f7efd87..6ff46999063 100644 --- a/code/game/mecha/combat/gygax.dm +++ b/code/game/mecha/combat/gygax.dm @@ -1,148 +1,148 @@ -/obj/mecha/combat/gygax - desc = "A lightweight, security exosuit. Popular among private and corporate security." - name = "Gygax" - icon_state = "gygax" - initial_icon = "gygax" - step_in = 3 - dir_in = 1 //Facing North. - health = 250 - maxhealth = 250 //Don't forget to update the /old variant if you change this number. - deflect_chance = 15 - max_temperature = 25000 - infra_luminosity = 6 - wreckage = /obj/effect/decal/mecha_wreckage/gygax - internal_damage_threshold = 35 - max_equip = 3 - - max_hull_equip = 1 - max_weapon_equip = 2 - max_utility_equip = 2 - max_universal_equip = 1 - max_special_equip = 1 - - starting_components = list( - /obj/item/mecha_parts/component/hull/lightweight, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/marshal, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - - overload_possible = 1 - - icon_scale_x = 1.35 - icon_scale_y = 1.35 - -//Not quite sure how to move those yet. -/obj/mecha/combat/gygax/get_commands() - var/output = {"
                    -
                    Special
                    - -
                    - "} - output += ..() - return output - - -/obj/mecha/combat/gygax/dark - desc = "A lightweight exosuit used by Heavy Asset Protection. A significantly upgraded Gygax security mech." - name = "Dark Gygax" - icon_state = "darkgygax" - initial_icon = "darkgygax" - health = 400 - maxhealth = 400 - deflect_chance = 25 - max_temperature = 45000 - overload_coeff = 1 - wreckage = /obj/effect/decal/mecha_wreckage/gygax/dark - max_equip = 4 - step_energy_drain = 5 - mech_faction = MECH_FACTION_SYNDI - - max_hull_equip = 1 - max_weapon_equip = 2 - max_utility_equip = 2 - max_universal_equip = 1 - max_special_equip = 2 - - starting_equipment = list( - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot, - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/grenade/clusterbang, - /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay, - /obj/item/mecha_parts/mecha_equipment/teleporter - ) - -/obj/mecha/combat/gygax/dark/add_cell(var/obj/item/weapon/cell/C=null) - if(C) - C.forceMove(src) - cell = C - return - cell = new /obj/item/weapon/cell/hyper(src) - -/obj/mecha/combat/gygax/serenity - desc = "A lightweight exosuit made from a modified Gygax chassis combined with proprietary VeyMed medical tech. It's faster and sturdier than most medical mechs, but much of the armor plating has been stripped out, leaving it more vulnerable than a regular Gygax." - name = "Serenity" - icon_state = "medgax" - initial_icon = "medgax" - health = 150 - maxhealth = 150 - deflect_chance = 20 - step_in = 2 - max_temperature = 20000 - overload_coeff = 1 - wreckage = /obj/effect/decal/mecha_wreckage/gygax/serenity - max_equip = 3 - step_energy_drain = 8 - cargo_capacity = 2 - max_hull_equip = 1 - max_weapon_equip = 1 - max_utility_equip = 2 - max_universal_equip = 1 - max_special_equip = 1 - - starting_components = list( - /obj/item/mecha_parts/component/hull, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/lightweight, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - - var/obj/item/clothing/glasses/hud/health/mech/hud - -/obj/mecha/combat/gygax/serenity/New() - ..() - hud = new /obj/item/clothing/glasses/hud/health/mech(src) - return - -/obj/mecha/combat/gygax/serenity/moved_inside(var/mob/living/carbon/human/H as mob) - if(..()) - if(H.glasses) - occupant_message(span_red("[H.glasses] prevent you from using [src] [hud]!")) - else - H.glasses = hud - H.recalculate_vis() - return 1 - else - return 0 - -/obj/mecha/combat/gygax/serenity/go_out() - if(ishuman(occupant)) - var/mob/living/carbon/human/H = occupant - if(H.glasses == hud) - H.glasses = null - H.recalculate_vis() - ..() - return - -//Meant for random spawns. -/obj/mecha/combat/gygax/old - desc = "A lightweight, security exosuit. Popular among private and corporate security. This one is particularly worn looking and likely isn't as sturdy." - -/obj/mecha/combat/gygax/old/New() - ..() - health = 25 - maxhealth = 250 //Just slightly worse. - cell.charge = rand(0, (cell.charge/2)) +/obj/mecha/combat/gygax + desc = "A lightweight, security exosuit. Popular among private and corporate security." + name = "Gygax" + icon_state = "gygax" + initial_icon = "gygax" + step_in = 3 + dir_in = 1 //Facing North. + health = 250 + maxhealth = 250 //Don't forget to update the /old variant if you change this number. + deflect_chance = 15 + max_temperature = 25000 + infra_luminosity = 6 + wreckage = /obj/effect/decal/mecha_wreckage/gygax + internal_damage_threshold = 35 + max_equip = 3 + + max_hull_equip = 1 + max_weapon_equip = 2 + max_utility_equip = 2 + max_universal_equip = 1 + max_special_equip = 1 + + starting_components = list( + /obj/item/mecha_parts/component/hull/lightweight, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/marshal, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + + overload_possible = 1 + + icon_scale_x = 1.35 + icon_scale_y = 1.35 + +//Not quite sure how to move those yet. +/obj/mecha/combat/gygax/get_commands() + var/output = {"
                    +
                    Special
                    + +
                    + "} + output += ..() + return output + + +/obj/mecha/combat/gygax/dark + desc = "A lightweight exosuit used by Heavy Asset Protection. A significantly upgraded Gygax security mech." + name = "Dark Gygax" + icon_state = "darkgygax" + initial_icon = "darkgygax" + health = 400 + maxhealth = 400 + deflect_chance = 25 + max_temperature = 45000 + overload_coeff = 1 + wreckage = /obj/effect/decal/mecha_wreckage/gygax/dark + max_equip = 4 + step_energy_drain = 5 + mech_faction = MECH_FACTION_SYNDI + + max_hull_equip = 1 + max_weapon_equip = 2 + max_utility_equip = 2 + max_universal_equip = 1 + max_special_equip = 2 + + starting_equipment = list( + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot, + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/grenade/clusterbang, + /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay, + /obj/item/mecha_parts/mecha_equipment/teleporter + ) + +/obj/mecha/combat/gygax/dark/add_cell(var/obj/item/weapon/cell/C=null) + if(C) + C.forceMove(src) + cell = C + return + cell = new /obj/item/weapon/cell/hyper(src) + +/obj/mecha/combat/gygax/serenity + desc = "A lightweight exosuit made from a modified Gygax chassis combined with proprietary VeyMed medical tech. It's faster and sturdier than most medical mechs, but much of the armor plating has been stripped out, leaving it more vulnerable than a regular Gygax." + name = "Serenity" + icon_state = "medgax" + initial_icon = "medgax" + health = 150 + maxhealth = 150 + deflect_chance = 20 + step_in = 2 + max_temperature = 20000 + overload_coeff = 1 + wreckage = /obj/effect/decal/mecha_wreckage/gygax/serenity + max_equip = 3 + step_energy_drain = 8 + cargo_capacity = 2 + max_hull_equip = 1 + max_weapon_equip = 1 + max_utility_equip = 2 + max_universal_equip = 1 + max_special_equip = 1 + + starting_components = list( + /obj/item/mecha_parts/component/hull, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/lightweight, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + + var/obj/item/clothing/glasses/hud/health/mech/hud + +/obj/mecha/combat/gygax/serenity/New() + ..() + hud = new /obj/item/clothing/glasses/hud/health/mech(src) + return + +/obj/mecha/combat/gygax/serenity/moved_inside(var/mob/living/carbon/human/H as mob) + if(..()) + if(H.glasses) + occupant_message(span_red("[H.glasses] prevent you from using [src] [hud]!")) + else + H.glasses = hud + H.recalculate_vis() + return 1 + else + return 0 + +/obj/mecha/combat/gygax/serenity/go_out() + if(ishuman(occupant)) + var/mob/living/carbon/human/H = occupant + if(H.glasses == hud) + H.glasses = null + H.recalculate_vis() + ..() + return + +//Meant for random spawns. +/obj/mecha/combat/gygax/old + desc = "A lightweight, security exosuit. Popular among private and corporate security. This one is particularly worn looking and likely isn't as sturdy." + +/obj/mecha/combat/gygax/old/New() + ..() + health = 25 + maxhealth = 250 //Just slightly worse. + cell.charge = rand(0, (cell.charge/2)) diff --git a/code/game/mecha/combat/marauder.dm b/code/game/mecha/combat/marauder.dm index 8fc089c4955..371c45fa4d0 100644 --- a/code/game/mecha/combat/marauder.dm +++ b/code/game/mecha/combat/marauder.dm @@ -1,150 +1,150 @@ -/obj/mecha/combat/marauder - desc = "Heavy-duty, combat exosuit, developed after the Durand model. Rarely found among civilian populations." - name = "Marauder" - catalogue_data = list(/datum/category_item/catalogue/technology/marauder) - icon_state = "marauder" - initial_icon = "marauder" - step_in = 5 - health = 350 - maxhealth = 350 //Don't forget to update the /old variant if you change this number. - deflect_chance = 25 - max_temperature = 60000 - infra_luminosity = 3 - operation_req_access = list(access_cent_specops) - wreckage = /obj/effect/decal/mecha_wreckage/marauder - add_req_access = 0 - internal_damage_threshold = 25 - force = 45 - max_equip = 4 - mech_faction = MECH_FACTION_NT - - max_hull_equip = 3 - max_weapon_equip = 3 - max_utility_equip = 3 - max_universal_equip = 1 - max_special_equip = 1 - - smoke_possible = 1 - zoom_possible = 1 - thrusters_possible = 1 - - starting_components = list( - /obj/item/mecha_parts/component/hull/durable, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/military, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - - starting_equipment = list( - /obj/item/mecha_parts/mecha_equipment/weapon/energy/pulse, - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/explosive, - /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay, - /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster - ) - - icon_scale_x = 1.5 - icon_scale_y = 1.5 - -/obj/mecha/combat/marauder/seraph - desc = "Heavy-duty, command-type exosuit. This is a custom model, utilized only by high-ranking military personnel." - name = "Seraph" - catalogue_data = list(/datum/category_item/catalogue/technology/seraph) - icon_state = "seraph" - initial_icon = "seraph" - operation_req_access = list(access_cent_creed) - step_in = 3 - health = 450 - wreckage = /obj/effect/decal/mecha_wreckage/seraph - internal_damage_threshold = 20 - force = 55 - max_equip = 5 - - starting_equipment = list( - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot, - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/explosive, - /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay, - /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster, - /obj/item/mecha_parts/mecha_equipment/teleporter - ) - -//Note that is the Mauler -/obj/mecha/combat/marauder/mauler - desc = "Heavy-duty, combat exosuit, developed off of the existing Marauder model." - name = "Mauler" - icon_state = "mauler" - initial_icon = "mauler" - operation_req_access = list(access_syndicate) - wreckage = /obj/effect/decal/mecha_wreckage/mauler - mech_faction = MECH_FACTION_SYNDI - -//I'll break this down later -/obj/mecha/combat/marauder/relaymove(mob/user,direction) - if(user != src.occupant) //While not "realistic", this piece is player friendly. - user.loc = get_turf(src) - to_chat(user, "You climb out from [src]") - return 0 - if(!can_move) - return 0 - if(zoom) - if(world.time - last_message > 20) - src.occupant_message("Unable to move while in zoom mode.") - last_message = world.time - return 0 - if(connected_port) - if(world.time - last_message > 20) - src.occupant_message("Unable to move while connected to the air system port") - last_message = world.time - return 0 - if(!thrusters && (current_processes & MECHA_PROC_MOVEMENT)) - return 0 - if(state || !has_charge(step_energy_drain)) - return 0 - var/tmp_step_in = step_in - var/tmp_step_energy_drain = step_energy_drain - var/move_result = 0 - if(internal_damage&MECHA_INT_CONTROL_LOST) - move_result = mechsteprand() - else if(src.dir!=direction) - move_result = mechturn(direction) - else - move_result = mechstep(direction) - if(move_result) - if(istype(src.loc, /turf/space)) - if(!src.check_for_support()) - float_direction = direction - start_process(MECHA_PROC_MOVEMENT) - if(thrusters) - tmp_step_energy_drain = step_energy_drain*2 - - can_move = 0 - spawn(tmp_step_in) can_move = 1 - use_power(tmp_step_energy_drain) - return 1 - return 0 - -//To be kill ltr -/obj/mecha/combat/marauder/get_commands() - var/output = {"
                    -
                    Special
                    - -
                    - "} - output += ..() - return output - -//Meant for random spawns. -/obj/mecha/combat/marauder/old - desc = "Heavy-duty, combat exosuit, developed after the Durand model. Rarely found among civilian populations. This one is particularly worn looking and likely isn't as sturdy." - - starting_equipment = null - -/obj/mecha/combat/marauder/old/New() - ..() - health = 25 - maxhealth = 300 //Just slightly worse. - cell.charge = rand(0, (cell.charge/2)) +/obj/mecha/combat/marauder + desc = "Heavy-duty, combat exosuit, developed after the Durand model. Rarely found among civilian populations." + name = "Marauder" + catalogue_data = list(/datum/category_item/catalogue/technology/marauder) + icon_state = "marauder" + initial_icon = "marauder" + step_in = 5 + health = 350 + maxhealth = 350 //Don't forget to update the /old variant if you change this number. + deflect_chance = 25 + max_temperature = 60000 + infra_luminosity = 3 + operation_req_access = list(access_cent_specops) + wreckage = /obj/effect/decal/mecha_wreckage/marauder + add_req_access = 0 + internal_damage_threshold = 25 + force = 45 + max_equip = 4 + mech_faction = MECH_FACTION_NT + + max_hull_equip = 3 + max_weapon_equip = 3 + max_utility_equip = 3 + max_universal_equip = 1 + max_special_equip = 1 + + smoke_possible = 1 + zoom_possible = 1 + thrusters_possible = 1 + + starting_components = list( + /obj/item/mecha_parts/component/hull/durable, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/military, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + + starting_equipment = list( + /obj/item/mecha_parts/mecha_equipment/weapon/energy/pulse, + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/explosive, + /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay, + /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster + ) + + icon_scale_x = 1.5 + icon_scale_y = 1.5 + +/obj/mecha/combat/marauder/seraph + desc = "Heavy-duty, command-type exosuit. This is a custom model, utilized only by high-ranking military personnel." + name = "Seraph" + catalogue_data = list(/datum/category_item/catalogue/technology/seraph) + icon_state = "seraph" + initial_icon = "seraph" + operation_req_access = list(access_cent_creed) + step_in = 3 + health = 450 + wreckage = /obj/effect/decal/mecha_wreckage/seraph + internal_damage_threshold = 20 + force = 55 + max_equip = 5 + + starting_equipment = list( + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot, + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/explosive, + /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay, + /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster, + /obj/item/mecha_parts/mecha_equipment/teleporter + ) + +//Note that is the Mauler +/obj/mecha/combat/marauder/mauler + desc = "Heavy-duty, combat exosuit, developed off of the existing Marauder model." + name = "Mauler" + icon_state = "mauler" + initial_icon = "mauler" + operation_req_access = list(access_syndicate) + wreckage = /obj/effect/decal/mecha_wreckage/mauler + mech_faction = MECH_FACTION_SYNDI + +//I'll break this down later +/obj/mecha/combat/marauder/relaymove(mob/user,direction) + if(user != src.occupant) //While not "realistic", this piece is player friendly. + user.loc = get_turf(src) + to_chat(user, "You climb out from [src]") + return 0 + if(!can_move) + return 0 + if(zoom) + if(world.time - last_message > 20) + src.occupant_message("Unable to move while in zoom mode.") + last_message = world.time + return 0 + if(connected_port) + if(world.time - last_message > 20) + src.occupant_message("Unable to move while connected to the air system port") + last_message = world.time + return 0 + if(!thrusters && (current_processes & MECHA_PROC_MOVEMENT)) + return 0 + if(state || !has_charge(step_energy_drain)) + return 0 + var/tmp_step_in = step_in + var/tmp_step_energy_drain = step_energy_drain + var/move_result = 0 + if(internal_damage&MECHA_INT_CONTROL_LOST) + move_result = mechsteprand() + else if(src.dir!=direction) + move_result = mechturn(direction) + else + move_result = mechstep(direction) + if(move_result) + if(istype(src.loc, /turf/space)) + if(!src.check_for_support()) + float_direction = direction + start_process(MECHA_PROC_MOVEMENT) + if(thrusters) + tmp_step_energy_drain = step_energy_drain*2 + + can_move = 0 + spawn(tmp_step_in) can_move = 1 + use_power(tmp_step_energy_drain) + return 1 + return 0 + +//To be kill ltr +/obj/mecha/combat/marauder/get_commands() + var/output = {"
                    +
                    Special
                    + +
                    + "} + output += ..() + return output + +//Meant for random spawns. +/obj/mecha/combat/marauder/old + desc = "Heavy-duty, combat exosuit, developed after the Durand model. Rarely found among civilian populations. This one is particularly worn looking and likely isn't as sturdy." + + starting_equipment = null + +/obj/mecha/combat/marauder/old/New() + ..() + health = 25 + maxhealth = 300 //Just slightly worse. + cell.charge = rand(0, (cell.charge/2)) diff --git a/code/game/mecha/combat/phazon.dm b/code/game/mecha/combat/phazon.dm index 04e3869cc1c..08aded1cf07 100644 --- a/code/game/mecha/combat/phazon.dm +++ b/code/game/mecha/combat/phazon.dm @@ -1,165 +1,165 @@ -/obj/mecha/combat/phazon - desc = "An exosuit which can only be described as 'WTF?'." - name = "Phazon" - icon_state = "phazon" - initial_icon = "phazon" - step_in = 1 - dir_in = 1 //Facing North. - step_energy_drain = 3 - health = 200 //God this is low - maxhealth = 200 //Don't forget to update the /old variant if you change this number. - deflect_chance = 30 - max_temperature = 25000 - infra_luminosity = 3 - wreckage = /obj/effect/decal/mecha_wreckage/phazon - add_req_access = 1 - //operation_req_access = list() - internal_damage_threshold = 25 - force = 15 - max_equip = 4 - - max_hull_equip = 3 - max_weapon_equip = 3 - max_utility_equip = 3 - max_universal_equip = 3 - max_special_equip = 4 - - encumbrance_gap = 2 - - starting_components = list( - /obj/item/mecha_parts/component/hull/durable, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/alien, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - - cloak_possible = TRUE - phasing_possible = TRUE - switch_dmg_type_possible = TRUE - var/list/inherent_damage_absorption = list("brute"=0.7,"fire"=0.7,"bullet"=0.7,"laser"=0.7,"energy"=0.7,"bomb"=0.7) - -/obj/mecha/combat/phazon/equipped/Initialize() - . = ..() - starting_equipment = list( - /obj/item/mecha_parts/mecha_equipment/tool/rcd, - /obj/item/mecha_parts/mecha_equipment/gravcatapult - ) - return - -/* Leaving this until we are really sure we don't need it for reference. -/obj/mecha/combat/phazon/Bump(var/atom/obstacle) - if(phasing && get_charge()>=phasing_energy_drain) - spawn() - if(can_phase) - can_phase = FALSE - flick("[initial_icon]-phase", src) - src.loc = get_step(src,src.dir) - src.use_power(phasing_energy_drain) - sleep(step_in*3) - can_phase = TRUE - else - . = ..() - return -*/ - - -/obj/mecha/combat/phazon/get_commands() - var/output = {" - "} - output += ..() - return output - - - -/obj/mecha/combat/phazon/janus - name = "Phazon Prototype Janus Class" - desc = "An exosuit which a more crude civilization such as yours might describe as WTF?." - description_fluff = "An incredibly high-tech exosuit constructed out of salvaged alien and cutting-edge modern technology.\ - This machine, theoretically, is capable of travelling through time, however due to the strange nature of its miniaturized \ - supermatter-fueled bluespace drive, it is uncertain how this ability manifests." - icon_state = "janus" - initial_icon = "janus" - step_in = 1 - dir_in = 1 //Facing North. - step_energy_drain = 3 - health = 350 - maxhealth = 350 - deflect_chance = 30 - inherent_damage_absorption = list("brute"=0.6,"fire"=0.7,"bullet"=0.7,"laser"=0.9,"energy"=0.7,"bomb"=0.5) - max_temperature = 10000 - infra_luminosity = 3 - wreckage = /obj/effect/decal/mecha_wreckage/janus - internal_damage_threshold = 25 - force = 20 - phasing_energy_drain = 300 - - max_hull_equip = 2 - max_weapon_equip = 1 - max_utility_equip = 2 - max_universal_equip = 2 - max_special_equip = 2 - - phasing_possible = TRUE - switch_dmg_type_possible = TRUE - cloak_possible = FALSE - -/obj/mecha/combat/phazon/janus/take_damage(amount, type="brute") - ..() - if(phasing) - phasing = FALSE - SSradiation.radiate(get_turf(src), 30) - log_append_to_last("WARNING: BLUESPACE DRIVE INSTABILITY DETECTED. DISABLING DRIVE.",1) - visible_message("The [src.name] appears to flicker, before its silhouette stabilizes!") - return - -/obj/mecha/combat/phazon/janus/dynbulletdamage(var/obj/item/projectile/Proj) - if((Proj.damage && !Proj.nodamage) && !istype(Proj, /obj/item/projectile/beam) && prob(max(1, 33 - round(Proj.damage / 4)))) - src.occupant_message("The armor absorbs the incoming projectile's force, negating it!") - src.visible_message("The [src.name] absorbs the incoming projectile's force, negating it!") - src.log_append_to_last("Armor negated.") - return - else if((Proj.damage && !Proj.nodamage) && istype(Proj, /obj/item/projectile/beam) && prob(max(1, (50 - round((Proj.damage / 2) * inherent_damage_absorption["laser"])) * (1 - (Proj.armor_penetration / 100))))) // Base 50% chance to deflect a beam,lowered by half the beam's damage scaled to laser absorption, then multiplied by the remaining percent of non-penetrated armor, with a minimum chance of 1%. - src.occupant_message("The armor reflects the incoming beam, negating it!") - src.visible_message("The [src.name] reflects the incoming beam, negating it!") - src.log_append_to_last("Armor reflected.") - return - - ..() - -/obj/mecha/combat/phazon/janus/dynattackby(obj/item/weapon/W as obj, mob/user as mob) - if(prob(max(1, (50 - round((W.force / 2) * inherent_damage_absorption["brute"])) * (1 - (W.armor_penetration / 100))))) - src.occupant_message("The armor absorbs the incoming attack's force, negating it!") - src.visible_message("The [src.name] absorbs the incoming attack's force, negating it!") - src.log_append_to_last("Armor absorbed.") - return - - ..() - -/obj/mecha/combat/phazon/janus/query_damtype() - var/new_damtype = tgui_alert(src.occupant,"Gauntlet Phase Emitter Mode","Damage Type",list("Force","Energy","Stun")) - switch(new_damtype) - if("Force") - damtype = "brute" - if("Energy") - damtype = "fire" - if("Stun") - damtype = "halloss" - src.occupant_message("Melee damage type switched to [new_damtype]") - return - -//Meant for random spawns. -/obj/mecha/combat/phazon/old - desc = "An exosuit which can only be described as 'WTF?'. This one is particularly worn looking and likely isn't as sturdy." - -/obj/mecha/combat/phazon/old/New() - ..() - health = 25 - maxhealth = 150 //Just slightly worse. - cell.charge = rand(0, (cell.charge/2)) +/obj/mecha/combat/phazon + desc = "An exosuit which can only be described as 'WTF?'." + name = "Phazon" + icon_state = "phazon" + initial_icon = "phazon" + step_in = 1 + dir_in = 1 //Facing North. + step_energy_drain = 3 + health = 200 //God this is low + maxhealth = 200 //Don't forget to update the /old variant if you change this number. + deflect_chance = 30 + max_temperature = 25000 + infra_luminosity = 3 + wreckage = /obj/effect/decal/mecha_wreckage/phazon + add_req_access = 1 + //operation_req_access = list() + internal_damage_threshold = 25 + force = 15 + max_equip = 4 + + max_hull_equip = 3 + max_weapon_equip = 3 + max_utility_equip = 3 + max_universal_equip = 3 + max_special_equip = 4 + + encumbrance_gap = 2 + + starting_components = list( + /obj/item/mecha_parts/component/hull/durable, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/alien, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + + cloak_possible = TRUE + phasing_possible = TRUE + switch_dmg_type_possible = TRUE + var/list/inherent_damage_absorption = list("brute"=0.7,"fire"=0.7,"bullet"=0.7,"laser"=0.7,"energy"=0.7,"bomb"=0.7) + +/obj/mecha/combat/phazon/equipped/Initialize() + . = ..() + starting_equipment = list( + /obj/item/mecha_parts/mecha_equipment/tool/rcd, + /obj/item/mecha_parts/mecha_equipment/gravcatapult + ) + return + +/* Leaving this until we are really sure we don't need it for reference. +/obj/mecha/combat/phazon/Bump(var/atom/obstacle) + if(phasing && get_charge()>=phasing_energy_drain) + spawn() + if(can_phase) + can_phase = FALSE + flick("[initial_icon]-phase", src) + src.loc = get_step(src,src.dir) + src.use_power(phasing_energy_drain) + sleep(step_in*3) + can_phase = TRUE + else + . = ..() + return +*/ + + +/obj/mecha/combat/phazon/get_commands() + var/output = {" + "} + output += ..() + return output + + + +/obj/mecha/combat/phazon/janus + name = "Phazon Prototype Janus Class" + desc = "An exosuit which a more crude civilization such as yours might describe as WTF?." + description_fluff = "An incredibly high-tech exosuit constructed out of salvaged alien and cutting-edge modern technology.\ + This machine, theoretically, is capable of travelling through time, however due to the strange nature of its miniaturized \ + supermatter-fueled bluespace drive, it is uncertain how this ability manifests." + icon_state = "janus" + initial_icon = "janus" + step_in = 1 + dir_in = 1 //Facing North. + step_energy_drain = 3 + health = 350 + maxhealth = 350 + deflect_chance = 30 + inherent_damage_absorption = list("brute"=0.6,"fire"=0.7,"bullet"=0.7,"laser"=0.9,"energy"=0.7,"bomb"=0.5) + max_temperature = 10000 + infra_luminosity = 3 + wreckage = /obj/effect/decal/mecha_wreckage/janus + internal_damage_threshold = 25 + force = 20 + phasing_energy_drain = 300 + + max_hull_equip = 2 + max_weapon_equip = 1 + max_utility_equip = 2 + max_universal_equip = 2 + max_special_equip = 2 + + phasing_possible = TRUE + switch_dmg_type_possible = TRUE + cloak_possible = FALSE + +/obj/mecha/combat/phazon/janus/take_damage(amount, type="brute") + ..() + if(phasing) + phasing = FALSE + SSradiation.radiate(get_turf(src), 30) + log_append_to_last("WARNING: BLUESPACE DRIVE INSTABILITY DETECTED. DISABLING DRIVE.",1) + visible_message("The [src.name] appears to flicker, before its silhouette stabilizes!") + return + +/obj/mecha/combat/phazon/janus/dynbulletdamage(var/obj/item/projectile/Proj) + if((Proj.damage && !Proj.nodamage) && !istype(Proj, /obj/item/projectile/beam) && prob(max(1, 33 - round(Proj.damage / 4)))) + src.occupant_message("The armor absorbs the incoming projectile's force, negating it!") + src.visible_message("The [src.name] absorbs the incoming projectile's force, negating it!") + src.log_append_to_last("Armor negated.") + return + else if((Proj.damage && !Proj.nodamage) && istype(Proj, /obj/item/projectile/beam) && prob(max(1, (50 - round((Proj.damage / 2) * inherent_damage_absorption["laser"])) * (1 - (Proj.armor_penetration / 100))))) // Base 50% chance to deflect a beam,lowered by half the beam's damage scaled to laser absorption, then multiplied by the remaining percent of non-penetrated armor, with a minimum chance of 1%. + src.occupant_message("The armor reflects the incoming beam, negating it!") + src.visible_message("The [src.name] reflects the incoming beam, negating it!") + src.log_append_to_last("Armor reflected.") + return + + ..() + +/obj/mecha/combat/phazon/janus/dynattackby(obj/item/weapon/W as obj, mob/user as mob) + if(prob(max(1, (50 - round((W.force / 2) * inherent_damage_absorption["brute"])) * (1 - (W.armor_penetration / 100))))) + src.occupant_message("The armor absorbs the incoming attack's force, negating it!") + src.visible_message("The [src.name] absorbs the incoming attack's force, negating it!") + src.log_append_to_last("Armor absorbed.") + return + + ..() + +/obj/mecha/combat/phazon/janus/query_damtype() + var/new_damtype = tgui_alert(src.occupant,"Gauntlet Phase Emitter Mode","Damage Type",list("Force","Energy","Stun")) + switch(new_damtype) + if("Force") + damtype = "brute" + if("Energy") + damtype = "fire" + if("Stun") + damtype = "halloss" + src.occupant_message("Melee damage type switched to [new_damtype]") + return + +//Meant for random spawns. +/obj/mecha/combat/phazon/old + desc = "An exosuit which can only be described as 'WTF?'. This one is particularly worn looking and likely isn't as sturdy." + +/obj/mecha/combat/phazon/old/New() + ..() + health = 25 + maxhealth = 150 //Just slightly worse. + cell.charge = rand(0, (cell.charge/2)) diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm index 0b3cc2ff5be..e4a178034ea 100644 --- a/code/game/mecha/equipment/mecha_equipment.dm +++ b/code/game/mecha/equipment/mecha_equipment.dm @@ -1,287 +1,287 @@ -//DO NOT ADD MECHA PARTS TO THE GAME WITH THE DEFAULT "SPRITE ME" SPRITE! -//I'm annoyed I even have to tell you this! SPRITE FIRST, then commit. -#define EQUIP_HULL "hull" -#define EQUIP_WEAPON "weapon" -#define EQUIP_UTILITY "utility" -#define EQUIP_SPECIAL "core" -//VOREStation Addition begin: MICROMECHS -#define EQUIP_MICRO_UTILITY "micro_utility" -#define EQUIP_MICRO_WEAPON "micro_weapon" -//VOREStation Addition end: MICROMECHS - -/obj/item/mecha_parts/mecha_equipment - name = "mecha equipment" - icon = 'icons/mecha/mecha_equipment.dmi' - icon_state = "mecha_equip" - force = 5 - origin_tech = list(TECH_MATERIAL = 2) - description_info = "Some equipment may gain new abilities or advantages if equipped to certain types of Exosuits." - var/equip_cooldown = 0 - var/equip_ready = TRUE - var/energy_drain = 0 - var/obj/mecha/chassis = null - var/range = MELEE //bitflags - /// Bitflag. Used by exosuit fabricator to assign sub-categories based on which exosuits can equip this. - var/mech_flags = NONE - var/salvageable = TRUE - var/required_type = /obj/mecha //may be either a type or a list of allowed types - var/equip_type = null //mechaequip2 - var/allow_duplicate = FALSE - var/ready_sound = 'sound/mecha/mech_reload_default.ogg' //Sound to play once the fire delay passed. - var/enable_special = FALSE // Will the tool do its special? - - var/step_delay = 0 // Does the component slow/speed up the suit? - -/obj/item/mecha_parts/mecha_equipment/proc/do_after_cooldown(target=1) - sleep(equip_cooldown) - set_ready_state(TRUE) - if(ready_sound) //Kind of like the kinetic accelerator. - playsound(src, ready_sound, 50, 1, -1) - if(target && chassis) - return 1 - return 0 - -/obj/item/mecha_parts/mecha_equipment/examine(mob/user) - . = ..() - . += "[src] will fill [equip_type?"a [equip_type]":"any"] slot." - -/obj/item/mecha_parts/mecha_equipment/proc/add_equip_overlay(obj/mecha/M as obj) - return - -/obj/item/mecha_parts/mecha_equipment/proc/update_chassis_page() - if(chassis) - send_byjax(chassis.occupant,"exosuit.browser","eq_list",chassis.get_equipment_list()) - send_byjax(chassis.occupant,"exosuit.browser","equipment_menu",chassis.get_equipment_menu(),"dropdowns") - return 1 - return - -/obj/item/mecha_parts/mecha_equipment/proc/update_equip_info() - if(chassis) - send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",get_equip_info()) - return 1 - return - -/obj/item/mecha_parts/mecha_equipment/proc/destroy()//missiles detonating, teleporter creating singularity? - if(chassis) - if(equip_type) - if(equip_type == EQUIP_HULL) - chassis.hull_equipment -= src - listclearnulls(chassis.hull_equipment) - if(equip_type == EQUIP_WEAPON) - chassis.weapon_equipment -= src - listclearnulls(chassis.weapon_equipment) - if(equip_type == EQUIP_UTILITY) - chassis.utility_equipment -= src - listclearnulls(chassis.utility_equipment) - if(equip_type == EQUIP_SPECIAL) - chassis.special_equipment -= src - listclearnulls(chassis.special_equipment) - //VOREStation Addition begin: MICROMECHS - if(equip_type == EQUIP_MICRO_UTILITY) - chassis.micro_utility_equipment -= src - listclearnulls(chassis.micro_utility_equipment) - if(equip_type == EQUIP_MICRO_WEAPON) - chassis.micro_weapon_equipment -= src - listclearnulls(chassis.micro_weapon_equipment) - //VOREStation Addition end: MICROMECHS - chassis.universal_equipment -= src - chassis.equipment -= src - listclearnulls(chassis.equipment) - if(chassis.selected == src) - chassis.selected = null - src.update_chassis_page() - chassis.occupant_message(span_red("The [src] is destroyed!")) - chassis.log_append_to_last("[src] is destroyed.",1) - if(istype(src, /obj/item/mecha_parts/mecha_equipment/weapon))//Gun - switch(chassis.mech_faction) - if(MECH_FACTION_NT) - src.chassis.occupant << sound('sound/mecha/weapdestrnano.ogg',volume=70) - if(MECH_FACTION_SYNDI) - src.chassis.occupant << sound('sound/mecha/weapdestrsyndi.ogg',volume=60) - else - src.chassis.occupant << sound('sound/mecha/weapdestr.ogg',volume=50) - else //Not a gun - switch(chassis.mech_faction) - if(MECH_FACTION_NT) - src.chassis.occupant << sound('sound/mecha/critdestrnano.ogg',volume=70) - if(MECH_FACTION_SYNDI) - src.chassis.occupant << sound('sound/mecha/critdestrsyndi.ogg',volume=70) - else - src.chassis.occupant << sound('sound/mecha/critdestr.ogg',volume=50) - spawn - qdel(src) - return - -/obj/item/mecha_parts/mecha_equipment/proc/critfail() - if(chassis) - log_message("Critical failure",1) - return - -/obj/item/mecha_parts/mecha_equipment/proc/get_equip_info() - if(!chassis) return - return "* [chassis.selected==src?"":""][src.name][chassis.selected==src?"":""]" - -/obj/item/mecha_parts/mecha_equipment/proc/is_ranged()//add a distance restricted equipment. Why not? - return range&RANGED - -/obj/item/mecha_parts/mecha_equipment/proc/is_melee() - return range&MELEE - -/obj/item/mecha_parts/mecha_equipment/proc/enable_special_checks(atom/target) - if(ispath(required_type)) - return istype(target, required_type) - - for (var/path in required_type) - if (istype(target, path)) - return 1 - - return 0 - -/obj/item/mecha_parts/mecha_equipment/proc/action_checks(atom/target) - if(!target) - return 0 - if(!chassis) - return 0 - if(!equip_ready) - return 0 - if(energy_drain && !chassis.has_charge(energy_drain)) - return 0 - return 1 - -/obj/item/mecha_parts/mecha_equipment/proc/action(atom/target) - return - -/obj/item/mecha_parts/mecha_equipment/proc/can_attach(obj/mecha/M as obj) - //if(M.equipment.len >= M.max_equip) - // return 0 - if(!allow_duplicate) - for(var/obj/item/mecha_parts/mecha_equipment/ME in M.equipment) //Exact duplicate components aren't allowed. - if(ME.type == src.type) - return 0 - if(equip_type == EQUIP_HULL && M.hull_equipment.len < M.max_hull_equip) - return 1 - if(equip_type == EQUIP_WEAPON && M.weapon_equipment.len < M.max_weapon_equip) - return 1 - if(equip_type == EQUIP_UTILITY && M.utility_equipment.len < M.max_utility_equip) - return 1 - if(equip_type == EQUIP_SPECIAL && M.special_equipment.len < M.max_special_equip) - return 1 - //VOREStation Addition begin: MICROMECHS - if(equip_type == EQUIP_MICRO_UTILITY && M.micro_utility_equipment.len < M.max_micro_utility_equip) - return 1 - if(equip_type == EQUIP_MICRO_WEAPON && M.micro_weapon_equipment.len < M.max_micro_weapon_equip) - return 1 - //VOREStation Addition end: MICROMECHS - if(equip_type != EQUIP_SPECIAL && M.universal_equipment.len < M.max_universal_equip) //The exosuit needs to be military grade to actually have a universal slot capable of accepting a true weapon. - if(equip_type == EQUIP_WEAPON && !istype(M, /obj/mecha/combat)) - return 0 - return 1 - /*if (ispath(required_type)) - return istype(M, required_type) - - for (var/path in required_type) - if (istype(M, path)) - return 1 - */ - return 0 - -/obj/item/mecha_parts/mecha_equipment/proc/attach(obj/mecha/M as obj) - //M.equipment += src - var/has_equipped = 0 - if(equip_type == EQUIP_HULL && M.hull_equipment.len < M.max_hull_equip && !has_equipped) - M.hull_equipment += src - has_equipped = 1 - if(equip_type == EQUIP_WEAPON && M.weapon_equipment.len < M.max_weapon_equip && !has_equipped) - M.weapon_equipment += src - has_equipped = 1 - if(equip_type == EQUIP_UTILITY && M.utility_equipment.len < M.max_utility_equip && !has_equipped) - M.utility_equipment += src - has_equipped = 1 - if(equip_type == EQUIP_SPECIAL && M.special_equipment.len < M.max_special_equip && !has_equipped) - M.special_equipment += src - has_equipped = 1 - //VOREStation Addition begin: MICROMECHS - if(equip_type == EQUIP_MICRO_UTILITY && M.micro_utility_equipment.len < M.max_micro_utility_equip && !has_equipped) - M.micro_utility_equipment += src - has_equipped = 1 - if(equip_type == EQUIP_MICRO_WEAPON && M.micro_weapon_equipment.len < M.max_micro_weapon_equip && !has_equipped) - M.micro_weapon_equipment += src - has_equipped = 1 - //VOREStation Addition end: MICROMECHS - if(equip_type != EQUIP_SPECIAL && M.universal_equipment.len < M.max_universal_equip && !has_equipped) - M.universal_equipment += src - M.equipment += src - chassis = M - src.loc = M - - if(enable_special_checks(M)) - enable_special = TRUE - - M.log_message("[src] initialized.") - if(!M.selected) - M.selected = src - src.update_chassis_page() - return - -/obj/item/mecha_parts/mecha_equipment/Destroy() - detach() - return ..() - -/obj/item/mecha_parts/mecha_equipment/proc/detach(atom/moveto=null) - if(!chassis) - return - moveto = moveto || get_turf(chassis) - forceMove(moveto) - chassis.equipment -= src - chassis.universal_equipment -= src - if(equip_type) - switch(equip_type) - if(EQUIP_HULL) - chassis.hull_equipment -= src - if(EQUIP_WEAPON) - chassis.weapon_equipment -= src - if(EQUIP_UTILITY) - chassis.utility_equipment -= src - if(EQUIP_SPECIAL) - chassis.special_equipment -= src - //VOREStation Addition begin: MICROMECHS - if(EQUIP_MICRO_UTILITY) - chassis.micro_utility_equipment -= src - if(EQUIP_MICRO_WEAPON) - chassis.micro_weapon_equipment -= src - //VOREStation Addition end: MICROMECHS - if(chassis.selected == src) - chassis.selected = null - update_chassis_page() - chassis.log_message("[src] removed from equipment.") - chassis = null - set_ready_state(TRUE) - enable_special = FALSE - return - -/obj/item/mecha_parts/mecha_equipment/Topic(href,href_list) - if(href_list["detach"]) - src.detach() - return - -/obj/item/mecha_parts/mecha_equipment/proc/set_ready_state(state) - equip_ready = state - if(chassis) - send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) - return - -/obj/item/mecha_parts/mecha_equipment/proc/occupant_message(message) - if(chassis) - chassis.occupant_message("\icon[src][bicon(src)] [message]") - return - -/obj/item/mecha_parts/mecha_equipment/proc/log_message(message) - if(chassis) - chassis.log_message("[src]: [message]") - return - -/obj/item/mecha_parts/mecha_equipment/proc/MoveAction() //Allows mech equipment to do an action upon the mech moving - return - -/obj/item/mecha_parts/mecha_equipment/proc/get_step_delay() // Equipment returns its slowdown or speedboost. - return step_delay +//DO NOT ADD MECHA PARTS TO THE GAME WITH THE DEFAULT "SPRITE ME" SPRITE! +//I'm annoyed I even have to tell you this! SPRITE FIRST, then commit. +#define EQUIP_HULL "hull" +#define EQUIP_WEAPON "weapon" +#define EQUIP_UTILITY "utility" +#define EQUIP_SPECIAL "core" +//VOREStation Addition begin: MICROMECHS +#define EQUIP_MICRO_UTILITY "micro_utility" +#define EQUIP_MICRO_WEAPON "micro_weapon" +//VOREStation Addition end: MICROMECHS + +/obj/item/mecha_parts/mecha_equipment + name = "mecha equipment" + icon = 'icons/mecha/mecha_equipment.dmi' + icon_state = "mecha_equip" + force = 5 + origin_tech = list(TECH_MATERIAL = 2) + description_info = "Some equipment may gain new abilities or advantages if equipped to certain types of Exosuits." + var/equip_cooldown = 0 + var/equip_ready = TRUE + var/energy_drain = 0 + var/obj/mecha/chassis = null + var/range = MELEE //bitflags + /// Bitflag. Used by exosuit fabricator to assign sub-categories based on which exosuits can equip this. + var/mech_flags = NONE + var/salvageable = TRUE + var/required_type = /obj/mecha //may be either a type or a list of allowed types + var/equip_type = null //mechaequip2 + var/allow_duplicate = FALSE + var/ready_sound = 'sound/mecha/mech_reload_default.ogg' //Sound to play once the fire delay passed. + var/enable_special = FALSE // Will the tool do its special? + + var/step_delay = 0 // Does the component slow/speed up the suit? + +/obj/item/mecha_parts/mecha_equipment/proc/do_after_cooldown(target=1) + sleep(equip_cooldown) + set_ready_state(TRUE) + if(ready_sound) //Kind of like the kinetic accelerator. + playsound(src, ready_sound, 50, 1, -1) + if(target && chassis) + return 1 + return 0 + +/obj/item/mecha_parts/mecha_equipment/examine(mob/user) + . = ..() + . += "[src] will fill [equip_type?"a [equip_type]":"any"] slot." + +/obj/item/mecha_parts/mecha_equipment/proc/add_equip_overlay(obj/mecha/M as obj) + return + +/obj/item/mecha_parts/mecha_equipment/proc/update_chassis_page() + if(chassis) + send_byjax(chassis.occupant,"exosuit.browser","eq_list",chassis.get_equipment_list()) + send_byjax(chassis.occupant,"exosuit.browser","equipment_menu",chassis.get_equipment_menu(),"dropdowns") + return 1 + return + +/obj/item/mecha_parts/mecha_equipment/proc/update_equip_info() + if(chassis) + send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",get_equip_info()) + return 1 + return + +/obj/item/mecha_parts/mecha_equipment/proc/destroy()//missiles detonating, teleporter creating singularity? + if(chassis) + if(equip_type) + if(equip_type == EQUIP_HULL) + chassis.hull_equipment -= src + listclearnulls(chassis.hull_equipment) + if(equip_type == EQUIP_WEAPON) + chassis.weapon_equipment -= src + listclearnulls(chassis.weapon_equipment) + if(equip_type == EQUIP_UTILITY) + chassis.utility_equipment -= src + listclearnulls(chassis.utility_equipment) + if(equip_type == EQUIP_SPECIAL) + chassis.special_equipment -= src + listclearnulls(chassis.special_equipment) + //VOREStation Addition begin: MICROMECHS + if(equip_type == EQUIP_MICRO_UTILITY) + chassis.micro_utility_equipment -= src + listclearnulls(chassis.micro_utility_equipment) + if(equip_type == EQUIP_MICRO_WEAPON) + chassis.micro_weapon_equipment -= src + listclearnulls(chassis.micro_weapon_equipment) + //VOREStation Addition end: MICROMECHS + chassis.universal_equipment -= src + chassis.equipment -= src + listclearnulls(chassis.equipment) + if(chassis.selected == src) + chassis.selected = null + src.update_chassis_page() + chassis.occupant_message(span_red("The [src] is destroyed!")) + chassis.log_append_to_last("[src] is destroyed.",1) + if(istype(src, /obj/item/mecha_parts/mecha_equipment/weapon))//Gun + switch(chassis.mech_faction) + if(MECH_FACTION_NT) + src.chassis.occupant << sound('sound/mecha/weapdestrnano.ogg',volume=70) + if(MECH_FACTION_SYNDI) + src.chassis.occupant << sound('sound/mecha/weapdestrsyndi.ogg',volume=60) + else + src.chassis.occupant << sound('sound/mecha/weapdestr.ogg',volume=50) + else //Not a gun + switch(chassis.mech_faction) + if(MECH_FACTION_NT) + src.chassis.occupant << sound('sound/mecha/critdestrnano.ogg',volume=70) + if(MECH_FACTION_SYNDI) + src.chassis.occupant << sound('sound/mecha/critdestrsyndi.ogg',volume=70) + else + src.chassis.occupant << sound('sound/mecha/critdestr.ogg',volume=50) + spawn + qdel(src) + return + +/obj/item/mecha_parts/mecha_equipment/proc/critfail() + if(chassis) + log_message("Critical failure",1) + return + +/obj/item/mecha_parts/mecha_equipment/proc/get_equip_info() + if(!chassis) return + return "* [chassis.selected==src?"":""][src.name][chassis.selected==src?"":""]" + +/obj/item/mecha_parts/mecha_equipment/proc/is_ranged()//add a distance restricted equipment. Why not? + return range&RANGED + +/obj/item/mecha_parts/mecha_equipment/proc/is_melee() + return range&MELEE + +/obj/item/mecha_parts/mecha_equipment/proc/enable_special_checks(atom/target) + if(ispath(required_type)) + return istype(target, required_type) + + for (var/path in required_type) + if (istype(target, path)) + return 1 + + return 0 + +/obj/item/mecha_parts/mecha_equipment/proc/action_checks(atom/target) + if(!target) + return 0 + if(!chassis) + return 0 + if(!equip_ready) + return 0 + if(energy_drain && !chassis.has_charge(energy_drain)) + return 0 + return 1 + +/obj/item/mecha_parts/mecha_equipment/proc/action(atom/target) + return + +/obj/item/mecha_parts/mecha_equipment/proc/can_attach(obj/mecha/M as obj) + //if(M.equipment.len >= M.max_equip) + // return 0 + if(!allow_duplicate) + for(var/obj/item/mecha_parts/mecha_equipment/ME in M.equipment) //Exact duplicate components aren't allowed. + if(ME.type == src.type) + return 0 + if(equip_type == EQUIP_HULL && M.hull_equipment.len < M.max_hull_equip) + return 1 + if(equip_type == EQUIP_WEAPON && M.weapon_equipment.len < M.max_weapon_equip) + return 1 + if(equip_type == EQUIP_UTILITY && M.utility_equipment.len < M.max_utility_equip) + return 1 + if(equip_type == EQUIP_SPECIAL && M.special_equipment.len < M.max_special_equip) + return 1 + //VOREStation Addition begin: MICROMECHS + if(equip_type == EQUIP_MICRO_UTILITY && M.micro_utility_equipment.len < M.max_micro_utility_equip) + return 1 + if(equip_type == EQUIP_MICRO_WEAPON && M.micro_weapon_equipment.len < M.max_micro_weapon_equip) + return 1 + //VOREStation Addition end: MICROMECHS + if(equip_type != EQUIP_SPECIAL && M.universal_equipment.len < M.max_universal_equip) //The exosuit needs to be military grade to actually have a universal slot capable of accepting a true weapon. + if(equip_type == EQUIP_WEAPON && !istype(M, /obj/mecha/combat)) + return 0 + return 1 + /*if (ispath(required_type)) + return istype(M, required_type) + + for (var/path in required_type) + if (istype(M, path)) + return 1 + */ + return 0 + +/obj/item/mecha_parts/mecha_equipment/proc/attach(obj/mecha/M as obj) + //M.equipment += src + var/has_equipped = 0 + if(equip_type == EQUIP_HULL && M.hull_equipment.len < M.max_hull_equip && !has_equipped) + M.hull_equipment += src + has_equipped = 1 + if(equip_type == EQUIP_WEAPON && M.weapon_equipment.len < M.max_weapon_equip && !has_equipped) + M.weapon_equipment += src + has_equipped = 1 + if(equip_type == EQUIP_UTILITY && M.utility_equipment.len < M.max_utility_equip && !has_equipped) + M.utility_equipment += src + has_equipped = 1 + if(equip_type == EQUIP_SPECIAL && M.special_equipment.len < M.max_special_equip && !has_equipped) + M.special_equipment += src + has_equipped = 1 + //VOREStation Addition begin: MICROMECHS + if(equip_type == EQUIP_MICRO_UTILITY && M.micro_utility_equipment.len < M.max_micro_utility_equip && !has_equipped) + M.micro_utility_equipment += src + has_equipped = 1 + if(equip_type == EQUIP_MICRO_WEAPON && M.micro_weapon_equipment.len < M.max_micro_weapon_equip && !has_equipped) + M.micro_weapon_equipment += src + has_equipped = 1 + //VOREStation Addition end: MICROMECHS + if(equip_type != EQUIP_SPECIAL && M.universal_equipment.len < M.max_universal_equip && !has_equipped) + M.universal_equipment += src + M.equipment += src + chassis = M + src.loc = M + + if(enable_special_checks(M)) + enable_special = TRUE + + M.log_message("[src] initialized.") + if(!M.selected) + M.selected = src + src.update_chassis_page() + return + +/obj/item/mecha_parts/mecha_equipment/Destroy() + detach() + return ..() + +/obj/item/mecha_parts/mecha_equipment/proc/detach(atom/moveto=null) + if(!chassis) + return + moveto = moveto || get_turf(chassis) + forceMove(moveto) + chassis.equipment -= src + chassis.universal_equipment -= src + if(equip_type) + switch(equip_type) + if(EQUIP_HULL) + chassis.hull_equipment -= src + if(EQUIP_WEAPON) + chassis.weapon_equipment -= src + if(EQUIP_UTILITY) + chassis.utility_equipment -= src + if(EQUIP_SPECIAL) + chassis.special_equipment -= src + //VOREStation Addition begin: MICROMECHS + if(EQUIP_MICRO_UTILITY) + chassis.micro_utility_equipment -= src + if(EQUIP_MICRO_WEAPON) + chassis.micro_weapon_equipment -= src + //VOREStation Addition end: MICROMECHS + if(chassis.selected == src) + chassis.selected = null + update_chassis_page() + chassis.log_message("[src] removed from equipment.") + chassis = null + set_ready_state(TRUE) + enable_special = FALSE + return + +/obj/item/mecha_parts/mecha_equipment/Topic(href,href_list) + if(href_list["detach"]) + src.detach() + return + +/obj/item/mecha_parts/mecha_equipment/proc/set_ready_state(state) + equip_ready = state + if(chassis) + send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) + return + +/obj/item/mecha_parts/mecha_equipment/proc/occupant_message(message) + if(chassis) + chassis.occupant_message("\icon[src][bicon(src)] [message]") + return + +/obj/item/mecha_parts/mecha_equipment/proc/log_message(message) + if(chassis) + chassis.log_message("[src]: [message]") + return + +/obj/item/mecha_parts/mecha_equipment/proc/MoveAction() //Allows mech equipment to do an action upon the mech moving + return + +/obj/item/mecha_parts/mecha_equipment/proc/get_step_delay() // Equipment returns its slowdown or speedboost. + return step_delay diff --git a/code/game/mecha/equipment/tools/tools.dm b/code/game/mecha/equipment/tools/tools.dm index 4fd4c8d9670..d9416629d5c 100644 --- a/code/game/mecha/equipment/tools/tools.dm +++ b/code/game/mecha/equipment/tools/tools.dm @@ -1,3 +1,3 @@ -/obj/item/mecha_parts/mecha_equipment/tool - matter = list(MAT_STEEL = 5000, MAT_GLASS = 3000) +/obj/item/mecha_parts/mecha_equipment/tool + matter = list(MAT_STEEL = 5000, MAT_GLASS = 3000) equip_type = EQUIP_UTILITY \ No newline at end of file diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index 9852db29d5e..aa8764ea6a3 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -1,100 +1,100 @@ -/obj/item/mecha_parts/mecha_equipment/weapon - name = "mecha weapon" - range = RANGED - origin_tech = list(TECH_MATERIAL = 3, TECH_COMBAT = 3) - matter = list(MAT_STEEL = 6000, MAT_GLASS = 3000) - var/projectile //Type of projectile fired. - var/projectiles = 1 //Amount of projectiles loaded. - var/projectiles_per_shot = 1 //Amount of projectiles fired per single shot. - var/deviation = 0 //Inaccuracy of shots. - var/fire_cooldown = 0 //Duration of sleep between firing projectiles in single shot. - var/fire_sound //Sound played while firing. - var/fire_volume = 50 //How loud it is played. - var/auto_rearm = 0 //Does the weapon reload itself after each shot? - required_type = list(/obj/mecha/combat, /obj/mecha/working/hoverpod/combatpod) - - step_delay = 0.1 - - equip_type = EQUIP_WEAPON - -/obj/item/mecha_parts/mecha_equipment/weapon/action_checks(atom/target) - if(projectiles <= 0) - return 0 - return ..() - -/obj/item/mecha_parts/mecha_equipment/weapon/action(atom/target, params) - if(!action_checks(target)) - return - var/turf/curloc = chassis.loc - var/turf/targloc = get_turf(target) - if(!curloc || !targloc) - return - chassis.use_power(energy_drain) - chassis.visible_message("[chassis] fires [src]!") - occupant_message("You fire [src]!") - log_message("Fired from [src], targeting [target].") - var/target_for_log = "unknown" - if(ismob(target)) - target_for_log = target - else if(target) - target_for_log = "[target.name]" - add_attack_logs(chassis.occupant,target_for_log,"Fired exosuit weapon [src.name] (MANUAL)") - - for(var/i = 1 to min(projectiles, projectiles_per_shot)) - var/turf/aimloc = targloc - if(deviation) - aimloc = locate(targloc.x+GaussRandRound(deviation,1),targloc.y+GaussRandRound(deviation,1),targloc.z) - if(!aimloc || aimloc == curloc || (locs && (aimloc in locs))) - break - playsound(src, fire_sound, fire_volume, 1) - projectiles-- - var/turf/projectile_turf - if(chassis.locs && chassis.locs.len) // Multi tile. - for(var/turf/Tloc in chassis.locs) - if(get_dist(Tloc, aimloc) < get_dist(loc, aimloc)) - projectile_turf = get_turf(Tloc) - if(!projectile_turf) - projectile_turf = get_turf(curloc) - var/P = new projectile(projectile_turf) - Fire(P, target, params) - if(i == 1) - set_ready_state(FALSE) - if(fire_cooldown) - sleep(fire_cooldown) - if(auto_rearm) - projectiles = projectiles_per_shot -// set_ready_state(FALSE) - - do_after_cooldown() - - return - -/obj/item/mecha_parts/mecha_equipment/weapon/proc/Fire(atom/A, atom/target, params) - if(istype(A, /obj/item/projectile)) // Sanity. - var/obj/item/projectile/P = A - P.dispersion = deviation - process_accuracy(P, chassis.occupant, target) - P.launch_projectile_from_turf(target, chassis.get_pilot_zone_sel(), chassis.occupant, params) - else if(istype(A, /atom/movable)) - var/atom/movable/AM = A - AM.throw_at(target, 7, 1, chassis) - -/obj/item/mecha_parts/mecha_equipment/weapon/proc/process_accuracy(obj/projectile, mob/living/user, atom/target) - var/obj/item/projectile/P = projectile - if(!istype(P)) - return - - P.accuracy -= user.get_accuracy_penalty() - - // Some modifiers make it harder or easier to hit things. - for(var/datum/modifier/M in user.modifiers) - if(!isnull(M.accuracy)) - P.accuracy += M.accuracy - if(!isnull(M.accuracy_dispersion)) - P.dispersion = max(P.dispersion + M.accuracy_dispersion, 0) - - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.species) - P.accuracy += H.species.gun_accuracy_mod +/obj/item/mecha_parts/mecha_equipment/weapon + name = "mecha weapon" + range = RANGED + origin_tech = list(TECH_MATERIAL = 3, TECH_COMBAT = 3) + matter = list(MAT_STEEL = 6000, MAT_GLASS = 3000) + var/projectile //Type of projectile fired. + var/projectiles = 1 //Amount of projectiles loaded. + var/projectiles_per_shot = 1 //Amount of projectiles fired per single shot. + var/deviation = 0 //Inaccuracy of shots. + var/fire_cooldown = 0 //Duration of sleep between firing projectiles in single shot. + var/fire_sound //Sound played while firing. + var/fire_volume = 50 //How loud it is played. + var/auto_rearm = 0 //Does the weapon reload itself after each shot? + required_type = list(/obj/mecha/combat, /obj/mecha/working/hoverpod/combatpod) + + step_delay = 0.1 + + equip_type = EQUIP_WEAPON + +/obj/item/mecha_parts/mecha_equipment/weapon/action_checks(atom/target) + if(projectiles <= 0) + return 0 + return ..() + +/obj/item/mecha_parts/mecha_equipment/weapon/action(atom/target, params) + if(!action_checks(target)) + return + var/turf/curloc = chassis.loc + var/turf/targloc = get_turf(target) + if(!curloc || !targloc) + return + chassis.use_power(energy_drain) + chassis.visible_message("[chassis] fires [src]!") + occupant_message("You fire [src]!") + log_message("Fired from [src], targeting [target].") + var/target_for_log = "unknown" + if(ismob(target)) + target_for_log = target + else if(target) + target_for_log = "[target.name]" + add_attack_logs(chassis.occupant,target_for_log,"Fired exosuit weapon [src.name] (MANUAL)") + + for(var/i = 1 to min(projectiles, projectiles_per_shot)) + var/turf/aimloc = targloc + if(deviation) + aimloc = locate(targloc.x+GaussRandRound(deviation,1),targloc.y+GaussRandRound(deviation,1),targloc.z) + if(!aimloc || aimloc == curloc || (locs && (aimloc in locs))) + break + playsound(src, fire_sound, fire_volume, 1) + projectiles-- + var/turf/projectile_turf + if(chassis.locs && chassis.locs.len) // Multi tile. + for(var/turf/Tloc in chassis.locs) + if(get_dist(Tloc, aimloc) < get_dist(loc, aimloc)) + projectile_turf = get_turf(Tloc) + if(!projectile_turf) + projectile_turf = get_turf(curloc) + var/P = new projectile(projectile_turf) + Fire(P, target, params) + if(i == 1) + set_ready_state(FALSE) + if(fire_cooldown) + sleep(fire_cooldown) + if(auto_rearm) + projectiles = projectiles_per_shot +// set_ready_state(FALSE) + + do_after_cooldown() + + return + +/obj/item/mecha_parts/mecha_equipment/weapon/proc/Fire(atom/A, atom/target, params) + if(istype(A, /obj/item/projectile)) // Sanity. + var/obj/item/projectile/P = A + P.dispersion = deviation + process_accuracy(P, chassis.occupant, target) + P.launch_projectile_from_turf(target, chassis.get_pilot_zone_sel(), chassis.occupant, params) + else if(istype(A, /atom/movable)) + var/atom/movable/AM = A + AM.throw_at(target, 7, 1, chassis) + +/obj/item/mecha_parts/mecha_equipment/weapon/proc/process_accuracy(obj/projectile, mob/living/user, atom/target) + var/obj/item/projectile/P = projectile + if(!istype(P)) + return + + P.accuracy -= user.get_accuracy_penalty() + + // Some modifiers make it harder or easier to hit things. + for(var/datum/modifier/M in user.modifiers) + if(!isnull(M.accuracy)) + P.accuracy += M.accuracy + if(!isnull(M.accuracy_dispersion)) + P.dispersion = max(P.dispersion + M.accuracy_dispersion, 0) + + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.species) + P.accuracy += H.species.gun_accuracy_mod P.dispersion = max(P.dispersion + H.species.gun_accuracy_dispersion_mod, 0) \ No newline at end of file diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm index 26c57989494..4d481fe0557 100644 --- a/code/game/mecha/mech_bay.dm +++ b/code/game/mecha/mech_bay.dm @@ -1,106 +1,106 @@ -/obj/machinery/mech_recharger - name = "mech recharger" - desc = "A mech recharger, built into the floor." - icon = 'icons/mecha/mech_bay.dmi' - icon_state = "recharge_floor" - density = FALSE - anchored = TRUE - layer = TURF_LAYER + 0.1 - circuit = /obj/item/weapon/circuitboard/mech_recharger - - var/atom/movable/charging - var/charge = 45 - var/repair = 0 - var/list/chargable_types = list( - /obj/mecha, - /mob/living/silicon/robot/platform - ) - -/obj/machinery/mech_recharger/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/mech_recharger/Crossed(var/atom/movable/M) - . = ..() - if(charging == M) - return - for(var/mtype in chargable_types) - if(istype(M, mtype)) - start_charging(M) - return - -/obj/machinery/mech_recharger/Uncrossed(var/atom/movable/M) - . = ..() - if(M == charging) - charging = null - -/obj/machinery/mech_recharger/RefreshParts() - ..() - charge = 0 - repair = -5 - for(var/obj/item/weapon/stock_parts/P in component_parts) - if(istype(P, /obj/item/weapon/stock_parts/capacitor)) - charge += P.rating * 20 - if(istype(P, /obj/item/weapon/stock_parts/scanning_module)) - charge += P.rating * 5 - repair += P.rating - if(istype(P, /obj/item/weapon/stock_parts/manipulator)) - repair += P.rating * 2 - -/obj/machinery/mech_recharger/process() - ..() - if(!charging) - return - if(charging.loc != src.loc) // Could be qdel or teleport or something - charging = null - return - - var/done = FALSE - var/obj/mecha/mech = charging - var/obj/item/weapon/cell/cell = charging.get_cell() - if(cell) - var/t = min(charge, cell.maxcharge - cell.charge) - if(t > 0) - if(istype(mech)) - mech.give_power(t) - else - cell.give(t) - use_power(t * 150) - else - if(istype(mech)) - mech.occupant_message(SPAN_NOTICE("Fully charged.")) - done = TRUE - - if(repair && istype(mech) && mech.health < initial(mech.health)) - mech.health = min(mech.health + repair, initial(mech.health)) - if(mech.health == initial(mech.health)) - mech.occupant_message(SPAN_NOTICE("Fully repaired.")) - else - done = FALSE - if(done) - charging = null - -/obj/machinery/mech_recharger/attackby(var/obj/item/I, var/mob/user) - if(default_deconstruction_screwdriver(user, I)) - return - if(default_deconstruction_crowbar(user, I)) - return - if(default_part_replacement(user, I)) - return - -/obj/machinery/mech_recharger/proc/start_charging(var/atom/movable/M) - - var/obj/mecha/mech = M - if(stat & (NOPOWER | BROKEN)) - if(istype(mech)) - mech.occupant_message(SPAN_WARNING("Power port not responding. Terminating.")) - else - to_chat(M, SPAN_WARNING("Power port not responding. Terminating.")) - return - if(M.get_cell()) - if(istype(mech)) - mech.occupant_message(SPAN_NOTICE("Now charging...")) - else - to_chat(M, SPAN_NOTICE("Now charging...")) - charging = M - return +/obj/machinery/mech_recharger + name = "mech recharger" + desc = "A mech recharger, built into the floor." + icon = 'icons/mecha/mech_bay.dmi' + icon_state = "recharge_floor" + density = FALSE + anchored = TRUE + layer = TURF_LAYER + 0.1 + circuit = /obj/item/weapon/circuitboard/mech_recharger + + var/atom/movable/charging + var/charge = 45 + var/repair = 0 + var/list/chargable_types = list( + /obj/mecha, + /mob/living/silicon/robot/platform + ) + +/obj/machinery/mech_recharger/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/mech_recharger/Crossed(var/atom/movable/M) + . = ..() + if(charging == M) + return + for(var/mtype in chargable_types) + if(istype(M, mtype)) + start_charging(M) + return + +/obj/machinery/mech_recharger/Uncrossed(var/atom/movable/M) + . = ..() + if(M == charging) + charging = null + +/obj/machinery/mech_recharger/RefreshParts() + ..() + charge = 0 + repair = -5 + for(var/obj/item/weapon/stock_parts/P in component_parts) + if(istype(P, /obj/item/weapon/stock_parts/capacitor)) + charge += P.rating * 20 + if(istype(P, /obj/item/weapon/stock_parts/scanning_module)) + charge += P.rating * 5 + repair += P.rating + if(istype(P, /obj/item/weapon/stock_parts/manipulator)) + repair += P.rating * 2 + +/obj/machinery/mech_recharger/process() + ..() + if(!charging) + return + if(charging.loc != src.loc) // Could be qdel or teleport or something + charging = null + return + + var/done = FALSE + var/obj/mecha/mech = charging + var/obj/item/weapon/cell/cell = charging.get_cell() + if(cell) + var/t = min(charge, cell.maxcharge - cell.charge) + if(t > 0) + if(istype(mech)) + mech.give_power(t) + else + cell.give(t) + use_power(t * 150) + else + if(istype(mech)) + mech.occupant_message(SPAN_NOTICE("Fully charged.")) + done = TRUE + + if(repair && istype(mech) && mech.health < initial(mech.health)) + mech.health = min(mech.health + repair, initial(mech.health)) + if(mech.health == initial(mech.health)) + mech.occupant_message(SPAN_NOTICE("Fully repaired.")) + else + done = FALSE + if(done) + charging = null + +/obj/machinery/mech_recharger/attackby(var/obj/item/I, var/mob/user) + if(default_deconstruction_screwdriver(user, I)) + return + if(default_deconstruction_crowbar(user, I)) + return + if(default_part_replacement(user, I)) + return + +/obj/machinery/mech_recharger/proc/start_charging(var/atom/movable/M) + + var/obj/mecha/mech = M + if(stat & (NOPOWER | BROKEN)) + if(istype(mech)) + mech.occupant_message(SPAN_WARNING("Power port not responding. Terminating.")) + else + to_chat(M, SPAN_WARNING("Power port not responding. Terminating.")) + return + if(M.get_cell()) + if(istype(mech)) + mech.occupant_message(SPAN_NOTICE("Now charging...")) + else + to_chat(M, SPAN_NOTICE("Now charging...")) + charging = M + return diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index 35d258ee785..ebc1e22aa14 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -1,723 +1,723 @@ -/obj/machinery/mecha_part_fabricator - icon = 'icons/obj/robotics_vr.dmi' //VOREStation Edit - New icon - icon_state = "mechfab" - name = "Exosuit Fabricator" - desc = "A machine used for the construction of mechas." - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 20 - active_power_usage = 5000 - req_access = list(access_robotics) - circuit = /obj/item/weapon/circuitboard/mechfab - - /// Current items in the build queue. - var/list/queue = list() - /// Whether or not the machine is building the entire queue automagically. - var/process_queue = FALSE - - /// The current design datum that the machine is building. - var/datum/design/being_built - /// World time when the build will finish. - var/build_finish = 0 - /// World time when the build started. - var/build_start = 0 - /// Reference to all materials used in the creation of the item being_built. - var/list/build_materials - /// Part currently stored in the Exofab. - var/obj/item/stored_part - - /// Coefficient for the speed of item building. Based on the installed parts. - var/time_coeff = 1 - /// Coefficient for the efficiency of material usage in item building. Based on the installed parts. - var/component_coeff = 1 - - var/loading_icon_state = "mechfab-idle" - - var/list/materials = list( - MAT_STEEL = 0, - MAT_GLASS = 0, - MAT_PLASTIC = 0, - MAT_GRAPHITE = 0, - MAT_PLASTEEL = 0, - MAT_GOLD = 0, - MAT_SILVER = 0, - MAT_LEAD = 0, - MAT_OSMIUM = 0, - MAT_DIAMOND = 0, - MAT_DURASTEEL = 0, - MAT_PHORON = 0, - MAT_URANIUM = 0, - MAT_VERDANTIUM = 0, - MAT_MORPHIUM = 0, - MAT_METALHYDROGEN = 0, - MAT_SUPERMATTER = 0) - var/res_max_amount = 200000 - - var/datum/research/files - var/valid_buildtype = MECHFAB - /// A list of categories that valid MECHFAB design datums will broadly categorise themselves under. - var/list/part_sets = list( - "Cyborg", - "Ripley", - "Odysseus", - "Gygax", - "Durand", - "Janus", - "Vehicle", - "Rigsuit", - "Phazon", - "Gopher", // VOREStation Add - "Polecat", // VOREStation Add - "Weasel", // VOREStation Add - "Exosuit Equipment", - "Exosuit Internals", - "Exosuit Ammunition", - "Cyborg Upgrade Modules", - "Cybernetics", - "Implants", - "Control Interfaces", - "Other", - "Misc", - ) - -/obj/machinery/mecha_part_fabricator/Initialize() - . = ..() - -// Go through all materials, and add them to the possible storage, but hide them unless we contain them. - for(var/Name in name_to_material) - if(Name in materials) - continue - - materials[Name] = 0 - - default_apply_parts() - files = new /datum/research(src) //Setup the research data holder. - -/obj/machinery/mecha_part_fabricator/dismantle() - for(var/f in materials) - eject_materials(f, -1) - ..() - -/obj/machinery/mecha_part_fabricator/RefreshParts() - res_max_amount = 0 - for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts) - res_max_amount += M.rating * 100000 // 200k -> 600k - var/T = 0 - for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts) - T += M.rating - component_coeff = max(1 - (T - 1) / 4, 0.2) // 1 -> 0.2 - for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts) // Not resetting T is intended; time_coeff is affected by both - T += M.rating - time_coeff = T / 2 // 1 -> 3 - update_tgui_static_data(usr) - - -/** - * Generates an info list for a given part. - * - * Returns a list of part information. - * * D - Design datum to get information on. - * * categories - Boolean, whether or not to parse snowflake categories into the part information list. - */ -/obj/machinery/mecha_part_fabricator/proc/output_part_info(datum/design/D, var/categories = FALSE) - var/cost = list() - for(var/c in D.materials) - cost[c] = get_resource_cost_w_coeff(D, D.materials[c]) - - var/obj/built_item = D.build_path - - var/list/category_override = null - var/list/sub_category = null - - if(categories) - // Handle some special cases to build up sub-categories for the fab interface. - // Start with checking if this design builds a cyborg module. - if(built_item in typesof(/obj/item/borg/upgrade)) - var/obj/item/borg/upgrade/U = built_item - var/module_types = initial(U.module_flags) - sub_category = list() - if(module_types) - if(module_types & BORG_UTILITY) - sub_category += "All Cyborgs - Utility" - if(module_types & BORG_BASIC) - sub_category += "All Cyborgs - Basic" - if(module_types & BORG_ADVANCED) - sub_category += "All Cyborgs - Advanced" - if(module_types & BORG_MODULE_SECURITY) - sub_category += "Security" - if(module_types & BORG_MODULE_MINER) - sub_category += "Mining" - if(module_types & BORG_MODULE_JANITOR) - sub_category += "Janitor" - if(module_types & BORG_MODULE_MEDICAL) - sub_category += "Medical" - if(module_types & BORG_MODULE_ENGINEERING) - sub_category += "Engineering" - if(module_types & BORG_MODULE_SCIENCE) - sub_category += "Science" - if(module_types & BORG_MODULE_SERVICE) - sub_category += "Service" - if(module_types & BORG_MODULE_CLERIC) - sub_category += "Cleric" - if(module_types & BORG_MODULE_COMBAT) - sub_category += "Combat" - if(module_types & BORG_MODULE_EXPLO) - sub_category += "Exploration" - else - sub_category += "This shouldn't be here, bother a dev!" - // Else check if this design builds a piece of exosuit equipment. - else if(built_item in typesof(/obj/item/mecha_parts/mecha_equipment)) - var/obj/item/mecha_parts/mecha_equipment/E = built_item - var/mech_types = initial(E.mech_flags) - sub_category = "Equipment" - if(mech_types) - category_override = list() - if(mech_types & EXOSUIT_MODULE_RIPLEY) - category_override += "Ripley" - if(mech_types & EXOSUIT_MODULE_ODYSSEUS) - category_override += "Odysseus" - if(mech_types & EXOSUIT_MODULE_GYGAX) - category_override += "Gygax" - if(mech_types & EXOSUIT_MODULE_DURAND) - category_override += "Durand" - if(mech_types & EXOSUIT_MODULE_PHAZON) - category_override += "Phazon" - - var/list/part = list( - "name" = D.name, - "desc" = initial(built_item.desc), - "printTime" = get_construction_time_w_coeff(initial(D.time))/10, - "cost" = cost, - "id" = D.id, - "subCategory" = sub_category, - "categoryOverride" = category_override, - "searchMeta" = D.search_metadata - ) - - return part - - -/** - * Generates a list of resources / materials available to this Exosuit Fab - * - * Returns null if there is no material container available. - * List format is list(material_name = list(amount = ..., ref = ..., etc.)) - */ -/obj/machinery/mecha_part_fabricator/proc/output_available_resources() - var/list/material_data = list() - - for(var/mat_id in materials) - var/amount = materials[mat_id] - var/list/material_info = list( - "name" = mat_id, - "amount" = amount, - "sheets" = round(amount / SHEET_MATERIAL_AMOUNT), - "removable" = amount >= SHEET_MATERIAL_AMOUNT - ) - - material_data += list(material_info) - - return material_data - -/** - * Intended to be called when an item starts printing. - * - * Adds the overlay to show the fab working and sets active power usage settings. - */ -/obj/machinery/mecha_part_fabricator/proc/on_start_printing() - add_overlay("[icon_state]-active") - use_power = USE_POWER_ACTIVE - -/** - * Intended to be called when the exofab has stopped working and is no longer printing items. - * - * Removes the overlay to show the fab working and sets idle power usage settings. Additionally resets the description and turns off queue processing. - */ -/obj/machinery/mecha_part_fabricator/proc/on_finish_printing() - cut_overlay("[icon_state]-active") - use_power = USE_POWER_IDLE - desc = initial(desc) - process_queue = FALSE - -/** - * Calculates resource/material costs for printing an item based on the machine's resource coefficient. - * - * Returns a list of k,v resources with their amounts. - * * D - Design datum to calculate the modified resource cost of. - */ -/obj/machinery/mecha_part_fabricator/proc/get_resources_w_coeff(datum/design/D) - var/list/resources = list() - for(var/mat_id in D.materials) - resources[mat_id] = get_resource_cost_w_coeff(D, D.materials[mat_id]) - return resources - -/** - * Checks if the Exofab has enough resources to print a given item. - * - * Returns FALSE if the design has no reagents used in its construction (?) or if there are insufficient resources. - * Returns TRUE if there are sufficient resources to print the item. - * * D - Design datum to calculate the modified resource cost of. - */ -/obj/machinery/mecha_part_fabricator/proc/check_resources(datum/design/D) - if(length(D.chemicals)) // No reagents storage - no reagent designs. - return FALSE - . = TRUE - var/list/coeff_required = get_resources_w_coeff(D) - for(var/mat_id in coeff_required) - if(materials[mat_id] < coeff_required[mat_id]) - return FALSE - -/** - * Attempts to build the next item in the build queue. - * - * Returns FALSE if either there are no more parts to build or the next part is not buildable. - * Returns TRUE if the next part has started building. - * * verbose - Whether the machine should use say() procs. Set to FALSE to disable the machine saying reasons for failure to build. - */ -/obj/machinery/mecha_part_fabricator/proc/build_next_in_queue(verbose = TRUE) - if(!length(queue)) - return FALSE - - var/datum/design/D = queue[1] - if(build_part(D, verbose)) - remove_from_queue(1) - return TRUE - - return FALSE - -/** - * Starts the build process for a given design datum. - * - * Returns FALSE if the procedure fails. Returns TRUE when being_built is set. - * Uses materials. - * * D - Design datum to attempt to print. - * * verbose - Whether the machine should use say() procs. Set to FALSE to disable the machine saying reasons for failure to build. - */ -/obj/machinery/mecha_part_fabricator/proc/build_part(datum/design/D, verbose = TRUE) - if(!D) - return FALSE - - if(!check_resources(D)) - if(verbose) - atom_say("Not enough resources. Processing stopped.") - return FALSE - - build_materials = get_resources_w_coeff(D) - for(var/mat_id in build_materials) - materials[mat_id] -= build_materials[mat_id] - - being_built = D - build_finish = world.time + get_construction_time_w_coeff(initial(D.time)) - build_start = world.time - desc = "It's building \a [D.name]." - - return TRUE - -/obj/machinery/mecha_part_fabricator/process() - ..() - // If there's a stored part to dispense due to an obstruction, try to dispense it. - if(stored_part) - var/turf/exit = get_step(src,(dir)) - if(exit.density) - return TRUE - - atom_say("Obstruction cleared. \The [stored_part] is complete.") - stored_part.forceMove(exit) - stored_part = null - - // If there's nothing being built, try to build something - if(!being_built) - // If we're not processing the queue anymore or there's nothing to build, end processing. - if(!process_queue || !build_next_in_queue()) - on_finish_printing() - return PROCESS_KILL - on_start_printing() - - // If there's an item being built, check if it is complete. - if(being_built && (build_finish < world.time)) - // Then attempt to dispense it and if appropriate build the next item. - dispense_built_part(being_built) - if(process_queue) - build_next_in_queue(FALSE) - return TRUE - - -/** - * Dispenses a part to the tile infront of the Exosuit Fab. - * - * Returns FALSE is the machine cannot dispense the part on the appropriate turf. - * Return TRUE if the part was successfully dispensed. - * * D - Design datum to attempt to dispense. - */ -/obj/machinery/mecha_part_fabricator/proc/dispense_built_part(datum/design/D) - var/obj/item/I = D.Fabricate(src, src) - // I.material_flags |= MATERIAL_NO_EFFECTS //Find a better way to do this. - // I.set_custom_materials(build_materials) - - being_built = null - - var/turf/exit = get_step(src,(dir)) - if(exit.density) - atom_say("Error! Part outlet is obstructed.") - desc = "It's trying to dispense \a [D.name], but the part outlet is obstructed." - stored_part = I - return FALSE - - atom_say("\The [I] is complete.") - I.forceMove(exit) - return I - -/** - * Adds a list of datum designs to the build queue. - * - * Will only add designs that are in this machine's stored techweb. - * Does final checks for datum IDs and makes sure this machine can build the designs. - * * part_list - List of datum design ids for designs to add to the queue. - */ -/obj/machinery/mecha_part_fabricator/proc/add_part_set_to_queue(list/part_list) - for(var/datum/design/D in files.known_designs) - if((D.build_type & valid_buildtype) && (D.id in part_list)) - add_to_queue(D) - -/** - * Adds a datum design to the build queue. - * - * Returns TRUE if successful and FALSE if the design was not added to the queue. - * * D - Datum design to add to the queue. - */ -/obj/machinery/mecha_part_fabricator/proc/add_to_queue(datum/design/D) - if(!istype(queue)) - queue = list() - if(D) - queue[++queue.len] = D - return TRUE - return FALSE - -/** - * Removes datum design from the build queue based on index. - * - * Returns TRUE if successful and FALSE if a design was not removed from the queue. - * * index - Index in the build queue of the element to remove. - */ -/obj/machinery/mecha_part_fabricator/proc/remove_from_queue(index) - if(!isnum(index) || !ISINTEGER(index) || !istype(queue) || (index<1 || index>length(queue))) - return FALSE - queue.Cut(index,++index) - return TRUE - -/** - * Generates a list of parts formatted for tgui based on the current build queue. - * - * Returns a formatted list of lists containing formatted part information for every part in the build queue. - */ -/obj/machinery/mecha_part_fabricator/proc/list_queue() - if(!istype(queue) || !length(queue)) - return null - - var/list/queued_parts = list() - for(var/datum/design/D in queue) - var/list/part = output_part_info(D) - queued_parts += list(part) - return queued_parts - -/obj/machinery/mecha_part_fabricator/proc/sync() - for(var/obj/machinery/computer/rdconsole/RDC in get_area_all_atoms(get_area(src))) - if(!RDC.sync) - continue - for(var/datum/tech/T in RDC.files.known_tech) - files.AddTech2Known(T) - for(var/datum/design/D in RDC.files.known_designs) - files.AddDesign2Known(D) - files.RefreshResearch() - update_tgui_static_data(usr) - atom_say("Successfully synchronized with R&D server.") - return - - atom_say("Unable to connect to local R&D server.") - return - -/** - * Calculates the coefficient-modified resource cost of a single material component of a design's recipe. - * - * Returns coefficient-modified resource cost for the given material component. - * * D - Design datum to pull the resource cost from. - * * resource - Material datum reference to the resource to calculate the cost of. - * * roundto - Rounding value for round() proc - */ -/obj/machinery/mecha_part_fabricator/proc/get_resource_cost_w_coeff(datum/design/D, var/amt, roundto = 1) - return round(amt * component_coeff, roundto) - -/** - * Calculates the coefficient-modified build time of a design. - * - * Returns coefficient-modified build time of a given design. - * * D - Design datum to calculate the modified build time of. - * * roundto - Rounding value for round() proc - */ -/obj/machinery/mecha_part_fabricator/proc/get_construction_time_w_coeff(construction_time, roundto = 1) //aran - return round(construction_time * time_coeff, roundto) - -/obj/machinery/mecha_part_fabricator/ui_assets(mob/user) - return list( - get_asset_datum(/datum/asset/spritesheet/sheetmaterials) - ) - -/obj/machinery/mecha_part_fabricator/attack_hand(var/mob/user) - if(..()) - return - if(!allowed(user)) - to_chat(user, SPAN_WARNING("\The [src] rejects your use due to lack of access!")) - return - tgui_interact(user) - -/obj/machinery/mecha_part_fabricator/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "ExosuitFabricator", name) - ui.open() - -/obj/machinery/mecha_part_fabricator/tgui_static_data(mob/user) - var/list/data = list() - - var/list/final_sets = list() - var/list/buildable_parts = list() - - for(var/part_set in part_sets) - final_sets += part_set - - for(var/datum/design/D in files.known_designs) - if((D.build_type & valid_buildtype) && D.id != "id") // bugfix for weird null entries - // This is for us. - var/list/part = output_part_info(D, TRUE) - - if(part["categoryOverride"]) - for(var/cat in part["categoryOverride"]) - buildable_parts[cat] += list(part) - if(!(cat in part_sets)) - final_sets += cat - continue - - for(var/cat in part_sets) - // Find all matching categories. - if(!(cat in D.category)) - continue - - buildable_parts[cat] += list(part) - - data["partSets"] = final_sets - data["buildableParts"] = buildable_parts - - return data - -/obj/machinery/mecha_part_fabricator/tgui_data(mob/user) - var/list/data = list() - - data["materials"] = output_available_resources() - - if(being_built) - var/list/part = list( - "name" = being_built.name, - "duration" = build_finish - world.time, - "printTime" = get_construction_time_w_coeff(initial(being_built.time)) - ) - data["buildingPart"] = part - else - data["buildingPart"] = null - - data["queue"] = list_queue() - - if(stored_part) - data["storedPart"] = stored_part.name - else - data["storedPart"] = null - - data["isProcessingQueue"] = process_queue - - return data - -/obj/machinery/mecha_part_fabricator/tgui_act(action, var/list/params) - if(..()) - return TRUE - - . = TRUE - - add_fingerprint(usr) - usr.set_machine(src) - - switch(action) - if("sync_rnd") - // Sync with R&D Servers - sync() - return - if("add_queue_set") - // Add all parts of a set to queue - var/part_list = params["part_list"] - add_part_set_to_queue(part_list) - return - if("add_queue_part") - // Add a specific part to queue - var/T = params["id"] - for(var/datum/design/D in files.known_designs) - if((D.build_type & valid_buildtype) && (D.id == T)) - add_to_queue(D) - break - return - if("del_queue_part") - // Delete a specific from from the queue - var/index = text2num(params["index"]) - remove_from_queue(index) - return - if("clear_queue") - // Delete everything from queue - queue.Cut() - return - if("build_queue") - // Build everything in queue - if(process_queue) - return - process_queue = TRUE - - if(!being_built) - START_PROCESSING(SSobj, src) - return - if("stop_queue") - // Pause queue building. Also known as stop. - process_queue = FALSE - return - if("build_part") - // Build a single part - if(being_built || process_queue) - return - - var/id = params["id"] - var/datum/design/D = null - for(var/datum/design/D_new in files.known_designs) - if((D_new.build_type == valid_buildtype) && (D_new.id == id)) - D = D_new - break - - if(!D) - return - - if(build_part(D)) - on_start_printing() - START_PROCESSING(SSobj, src) - - return - if("move_queue_part") - // Moves a part up or down in the queue. - var/index = text2num(params["index"]) - var/new_index = index + text2num(params["newindex"]) - if(isnum(index) && isnum(new_index) && ISINTEGER(index) && ISINTEGER(new_index)) - if(ISINRANGE(new_index,1,length(queue))) - queue.Swap(index,new_index) - return - if("remove_mat") - // Remove a material from the fab - var/mat_id = params["id"] - var/amount = text2num(params["amount"]) - eject_materials(mat_id, amount) - return - - return FALSE - -/obj/machinery/mecha_part_fabricator/attackby(var/obj/item/I, var/mob/user) - if(being_built) - to_chat(user, "\The [src] is busy. Please wait for completion of previous operation.") - return 1 - if(default_deconstruction_screwdriver(user, I)) - return - if(default_deconstruction_crowbar(user, I)) - return - if(default_part_replacement(user, I)) - return - - if(istype(I,/obj/item/stack/material)) - var/obj/item/stack/material/S = I - if(!(S.material.name in materials)) - to_chat(user, "The [src] doesn't accept [S.material]!") - return - - var/sname = "[S.name]" - var/amnt = S.perunit - if(materials[S.material.name] + amnt <= res_max_amount) - if(S && S.get_amount() >= 1) - var/count = 0 - flick("[loading_icon_state]", src) - // yess hacky but whatever //even more hacky now, but at least it works - if(loading_icon_state == "mechfab-idle") - flick("mechfab-load-metal", src) - while(materials[S.material.name] + amnt <= res_max_amount && S.get_amount() >= 1) - materials[S.material.name] += amnt - S.use(1) - count++ - to_chat(user, "You insert [count] [sname] into the fabricator.") - else - to_chat(user, "The fabricator cannot hold more [sname].") - - return - - ..() - -/obj/machinery/mecha_part_fabricator/emag_act(var/remaining_charges, var/mob/user) - switch(emagged) - if(0) - emagged = 0.5 - visible_message("\icon[src][bicon(src)] [src] beeps: \"DB error \[Code 0x00F1\]\"") - sleep(10) - visible_message("\icon[src][bicon(src)] [src] beeps: \"Attempting auto-repair\"") - sleep(15) - visible_message("\icon[src][bicon(src)] [src] beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"") - sleep(30) - visible_message("\icon[src][bicon(src)] [src] beeps: \"User DB truncated. Please contact your [using_map.company_name] system operator for future assistance.\"") - req_access = null - emagged = 1 - return 1 - if(0.5) - visible_message("\icon[src][bicon(src)] [src] beeps: \"DB not responding \[Code 0x0003\]...\"") - if(1) - visible_message("\icon[src][bicon(src)] [src] beeps: \"No records in User DB\"") - -/obj/machinery/mecha_part_fabricator/proc/eject_materials(var/material, var/amount) // 0 amount = 0 means ejecting a full stack; -1 means eject everything - var/recursive = amount == -1 ? TRUE : FALSE - var/matstring = lowertext(material) - - // 0 or null, nothing to eject - if(!materials[matstring]) - return - // Problem, fix problem and abort - if(materials[matstring] < 0) - warning("[src] tried to eject material '[material]', which it has 'materials[matstring]' of!") - materials[matstring] = 0 - return - - // Find the material datum for our material - var/datum/material/M = get_material_by_name(matstring) - if(!M) - warning("[src] tried to eject material '[matstring]', which didn't match any known material datum!") - return - // Find what type of sheets it makes - var/obj/item/stack/material/S = M.stack_type - if(!S) - warning("[src] tried to eject material '[matstring]', which didn't have a stack_type!") - return - - // If we were passed -1, then it's recursive ejection and we should eject all we can - if(amount <= 0) - amount = initial(S.max_amount) - // Smaller of what we have left, or the desired amount (note the amount is in sheets, but the array stores perunit values) - var/ejected = min(round(materials[matstring] / initial(S.perunit)), amount) - - // Place a sheet - S = M.place_sheet(get_turf(src), ejected) - if(!istype(S)) - warning("[src] tried to eject material '[material]', which didn't generate a proper stack when asked!") - return - - // Reduce our amount stored - materials[matstring] -= ejected * S.perunit - - // Recurse if we have enough left for more sheets - if(recursive && materials[matstring] >= S.perunit) - eject_materials(matstring, -1) +/obj/machinery/mecha_part_fabricator + icon = 'icons/obj/robotics_vr.dmi' //VOREStation Edit - New icon + icon_state = "mechfab" + name = "Exosuit Fabricator" + desc = "A machine used for the construction of mechas." + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 20 + active_power_usage = 5000 + req_access = list(access_robotics) + circuit = /obj/item/weapon/circuitboard/mechfab + + /// Current items in the build queue. + var/list/queue = list() + /// Whether or not the machine is building the entire queue automagically. + var/process_queue = FALSE + + /// The current design datum that the machine is building. + var/datum/design/being_built + /// World time when the build will finish. + var/build_finish = 0 + /// World time when the build started. + var/build_start = 0 + /// Reference to all materials used in the creation of the item being_built. + var/list/build_materials + /// Part currently stored in the Exofab. + var/obj/item/stored_part + + /// Coefficient for the speed of item building. Based on the installed parts. + var/time_coeff = 1 + /// Coefficient for the efficiency of material usage in item building. Based on the installed parts. + var/component_coeff = 1 + + var/loading_icon_state = "mechfab-idle" + + var/list/materials = list( + MAT_STEEL = 0, + MAT_GLASS = 0, + MAT_PLASTIC = 0, + MAT_GRAPHITE = 0, + MAT_PLASTEEL = 0, + MAT_GOLD = 0, + MAT_SILVER = 0, + MAT_LEAD = 0, + MAT_OSMIUM = 0, + MAT_DIAMOND = 0, + MAT_DURASTEEL = 0, + MAT_PHORON = 0, + MAT_URANIUM = 0, + MAT_VERDANTIUM = 0, + MAT_MORPHIUM = 0, + MAT_METALHYDROGEN = 0, + MAT_SUPERMATTER = 0) + var/res_max_amount = 200000 + + var/datum/research/files + var/valid_buildtype = MECHFAB + /// A list of categories that valid MECHFAB design datums will broadly categorise themselves under. + var/list/part_sets = list( + "Cyborg", + "Ripley", + "Odysseus", + "Gygax", + "Durand", + "Janus", + "Vehicle", + "Rigsuit", + "Phazon", + "Gopher", // VOREStation Add + "Polecat", // VOREStation Add + "Weasel", // VOREStation Add + "Exosuit Equipment", + "Exosuit Internals", + "Exosuit Ammunition", + "Cyborg Upgrade Modules", + "Cybernetics", + "Implants", + "Control Interfaces", + "Other", + "Misc", + ) + +/obj/machinery/mecha_part_fabricator/Initialize() + . = ..() + +// Go through all materials, and add them to the possible storage, but hide them unless we contain them. + for(var/Name in name_to_material) + if(Name in materials) + continue + + materials[Name] = 0 + + default_apply_parts() + files = new /datum/research(src) //Setup the research data holder. + +/obj/machinery/mecha_part_fabricator/dismantle() + for(var/f in materials) + eject_materials(f, -1) + ..() + +/obj/machinery/mecha_part_fabricator/RefreshParts() + res_max_amount = 0 + for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts) + res_max_amount += M.rating * 100000 // 200k -> 600k + var/T = 0 + for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts) + T += M.rating + component_coeff = max(1 - (T - 1) / 4, 0.2) // 1 -> 0.2 + for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts) // Not resetting T is intended; time_coeff is affected by both + T += M.rating + time_coeff = T / 2 // 1 -> 3 + update_tgui_static_data(usr) + + +/** + * Generates an info list for a given part. + * + * Returns a list of part information. + * * D - Design datum to get information on. + * * categories - Boolean, whether or not to parse snowflake categories into the part information list. + */ +/obj/machinery/mecha_part_fabricator/proc/output_part_info(datum/design/D, var/categories = FALSE) + var/cost = list() + for(var/c in D.materials) + cost[c] = get_resource_cost_w_coeff(D, D.materials[c]) + + var/obj/built_item = D.build_path + + var/list/category_override = null + var/list/sub_category = null + + if(categories) + // Handle some special cases to build up sub-categories for the fab interface. + // Start with checking if this design builds a cyborg module. + if(built_item in typesof(/obj/item/borg/upgrade)) + var/obj/item/borg/upgrade/U = built_item + var/module_types = initial(U.module_flags) + sub_category = list() + if(module_types) + if(module_types & BORG_UTILITY) + sub_category += "All Cyborgs - Utility" + if(module_types & BORG_BASIC) + sub_category += "All Cyborgs - Basic" + if(module_types & BORG_ADVANCED) + sub_category += "All Cyborgs - Advanced" + if(module_types & BORG_MODULE_SECURITY) + sub_category += "Security" + if(module_types & BORG_MODULE_MINER) + sub_category += "Mining" + if(module_types & BORG_MODULE_JANITOR) + sub_category += "Janitor" + if(module_types & BORG_MODULE_MEDICAL) + sub_category += "Medical" + if(module_types & BORG_MODULE_ENGINEERING) + sub_category += "Engineering" + if(module_types & BORG_MODULE_SCIENCE) + sub_category += "Science" + if(module_types & BORG_MODULE_SERVICE) + sub_category += "Service" + if(module_types & BORG_MODULE_CLERIC) + sub_category += "Cleric" + if(module_types & BORG_MODULE_COMBAT) + sub_category += "Combat" + if(module_types & BORG_MODULE_EXPLO) + sub_category += "Exploration" + else + sub_category += "This shouldn't be here, bother a dev!" + // Else check if this design builds a piece of exosuit equipment. + else if(built_item in typesof(/obj/item/mecha_parts/mecha_equipment)) + var/obj/item/mecha_parts/mecha_equipment/E = built_item + var/mech_types = initial(E.mech_flags) + sub_category = "Equipment" + if(mech_types) + category_override = list() + if(mech_types & EXOSUIT_MODULE_RIPLEY) + category_override += "Ripley" + if(mech_types & EXOSUIT_MODULE_ODYSSEUS) + category_override += "Odysseus" + if(mech_types & EXOSUIT_MODULE_GYGAX) + category_override += "Gygax" + if(mech_types & EXOSUIT_MODULE_DURAND) + category_override += "Durand" + if(mech_types & EXOSUIT_MODULE_PHAZON) + category_override += "Phazon" + + var/list/part = list( + "name" = D.name, + "desc" = initial(built_item.desc), + "printTime" = get_construction_time_w_coeff(initial(D.time))/10, + "cost" = cost, + "id" = D.id, + "subCategory" = sub_category, + "categoryOverride" = category_override, + "searchMeta" = D.search_metadata + ) + + return part + + +/** + * Generates a list of resources / materials available to this Exosuit Fab + * + * Returns null if there is no material container available. + * List format is list(material_name = list(amount = ..., ref = ..., etc.)) + */ +/obj/machinery/mecha_part_fabricator/proc/output_available_resources() + var/list/material_data = list() + + for(var/mat_id in materials) + var/amount = materials[mat_id] + var/list/material_info = list( + "name" = mat_id, + "amount" = amount, + "sheets" = round(amount / SHEET_MATERIAL_AMOUNT), + "removable" = amount >= SHEET_MATERIAL_AMOUNT + ) + + material_data += list(material_info) + + return material_data + +/** + * Intended to be called when an item starts printing. + * + * Adds the overlay to show the fab working and sets active power usage settings. + */ +/obj/machinery/mecha_part_fabricator/proc/on_start_printing() + add_overlay("[icon_state]-active") + use_power = USE_POWER_ACTIVE + +/** + * Intended to be called when the exofab has stopped working and is no longer printing items. + * + * Removes the overlay to show the fab working and sets idle power usage settings. Additionally resets the description and turns off queue processing. + */ +/obj/machinery/mecha_part_fabricator/proc/on_finish_printing() + cut_overlay("[icon_state]-active") + use_power = USE_POWER_IDLE + desc = initial(desc) + process_queue = FALSE + +/** + * Calculates resource/material costs for printing an item based on the machine's resource coefficient. + * + * Returns a list of k,v resources with their amounts. + * * D - Design datum to calculate the modified resource cost of. + */ +/obj/machinery/mecha_part_fabricator/proc/get_resources_w_coeff(datum/design/D) + var/list/resources = list() + for(var/mat_id in D.materials) + resources[mat_id] = get_resource_cost_w_coeff(D, D.materials[mat_id]) + return resources + +/** + * Checks if the Exofab has enough resources to print a given item. + * + * Returns FALSE if the design has no reagents used in its construction (?) or if there are insufficient resources. + * Returns TRUE if there are sufficient resources to print the item. + * * D - Design datum to calculate the modified resource cost of. + */ +/obj/machinery/mecha_part_fabricator/proc/check_resources(datum/design/D) + if(length(D.chemicals)) // No reagents storage - no reagent designs. + return FALSE + . = TRUE + var/list/coeff_required = get_resources_w_coeff(D) + for(var/mat_id in coeff_required) + if(materials[mat_id] < coeff_required[mat_id]) + return FALSE + +/** + * Attempts to build the next item in the build queue. + * + * Returns FALSE if either there are no more parts to build or the next part is not buildable. + * Returns TRUE if the next part has started building. + * * verbose - Whether the machine should use say() procs. Set to FALSE to disable the machine saying reasons for failure to build. + */ +/obj/machinery/mecha_part_fabricator/proc/build_next_in_queue(verbose = TRUE) + if(!length(queue)) + return FALSE + + var/datum/design/D = queue[1] + if(build_part(D, verbose)) + remove_from_queue(1) + return TRUE + + return FALSE + +/** + * Starts the build process for a given design datum. + * + * Returns FALSE if the procedure fails. Returns TRUE when being_built is set. + * Uses materials. + * * D - Design datum to attempt to print. + * * verbose - Whether the machine should use say() procs. Set to FALSE to disable the machine saying reasons for failure to build. + */ +/obj/machinery/mecha_part_fabricator/proc/build_part(datum/design/D, verbose = TRUE) + if(!D) + return FALSE + + if(!check_resources(D)) + if(verbose) + atom_say("Not enough resources. Processing stopped.") + return FALSE + + build_materials = get_resources_w_coeff(D) + for(var/mat_id in build_materials) + materials[mat_id] -= build_materials[mat_id] + + being_built = D + build_finish = world.time + get_construction_time_w_coeff(initial(D.time)) + build_start = world.time + desc = "It's building \a [D.name]." + + return TRUE + +/obj/machinery/mecha_part_fabricator/process() + ..() + // If there's a stored part to dispense due to an obstruction, try to dispense it. + if(stored_part) + var/turf/exit = get_step(src,(dir)) + if(exit.density) + return TRUE + + atom_say("Obstruction cleared. \The [stored_part] is complete.") + stored_part.forceMove(exit) + stored_part = null + + // If there's nothing being built, try to build something + if(!being_built) + // If we're not processing the queue anymore or there's nothing to build, end processing. + if(!process_queue || !build_next_in_queue()) + on_finish_printing() + return PROCESS_KILL + on_start_printing() + + // If there's an item being built, check if it is complete. + if(being_built && (build_finish < world.time)) + // Then attempt to dispense it and if appropriate build the next item. + dispense_built_part(being_built) + if(process_queue) + build_next_in_queue(FALSE) + return TRUE + + +/** + * Dispenses a part to the tile infront of the Exosuit Fab. + * + * Returns FALSE is the machine cannot dispense the part on the appropriate turf. + * Return TRUE if the part was successfully dispensed. + * * D - Design datum to attempt to dispense. + */ +/obj/machinery/mecha_part_fabricator/proc/dispense_built_part(datum/design/D) + var/obj/item/I = D.Fabricate(src, src) + // I.material_flags |= MATERIAL_NO_EFFECTS //Find a better way to do this. + // I.set_custom_materials(build_materials) + + being_built = null + + var/turf/exit = get_step(src,(dir)) + if(exit.density) + atom_say("Error! Part outlet is obstructed.") + desc = "It's trying to dispense \a [D.name], but the part outlet is obstructed." + stored_part = I + return FALSE + + atom_say("\The [I] is complete.") + I.forceMove(exit) + return I + +/** + * Adds a list of datum designs to the build queue. + * + * Will only add designs that are in this machine's stored techweb. + * Does final checks for datum IDs and makes sure this machine can build the designs. + * * part_list - List of datum design ids for designs to add to the queue. + */ +/obj/machinery/mecha_part_fabricator/proc/add_part_set_to_queue(list/part_list) + for(var/datum/design/D in files.known_designs) + if((D.build_type & valid_buildtype) && (D.id in part_list)) + add_to_queue(D) + +/** + * Adds a datum design to the build queue. + * + * Returns TRUE if successful and FALSE if the design was not added to the queue. + * * D - Datum design to add to the queue. + */ +/obj/machinery/mecha_part_fabricator/proc/add_to_queue(datum/design/D) + if(!istype(queue)) + queue = list() + if(D) + queue[++queue.len] = D + return TRUE + return FALSE + +/** + * Removes datum design from the build queue based on index. + * + * Returns TRUE if successful and FALSE if a design was not removed from the queue. + * * index - Index in the build queue of the element to remove. + */ +/obj/machinery/mecha_part_fabricator/proc/remove_from_queue(index) + if(!isnum(index) || !ISINTEGER(index) || !istype(queue) || (index<1 || index>length(queue))) + return FALSE + queue.Cut(index,++index) + return TRUE + +/** + * Generates a list of parts formatted for tgui based on the current build queue. + * + * Returns a formatted list of lists containing formatted part information for every part in the build queue. + */ +/obj/machinery/mecha_part_fabricator/proc/list_queue() + if(!istype(queue) || !length(queue)) + return null + + var/list/queued_parts = list() + for(var/datum/design/D in queue) + var/list/part = output_part_info(D) + queued_parts += list(part) + return queued_parts + +/obj/machinery/mecha_part_fabricator/proc/sync() + for(var/obj/machinery/computer/rdconsole/RDC in get_area_all_atoms(get_area(src))) + if(!RDC.sync) + continue + for(var/datum/tech/T in RDC.files.known_tech) + files.AddTech2Known(T) + for(var/datum/design/D in RDC.files.known_designs) + files.AddDesign2Known(D) + files.RefreshResearch() + update_tgui_static_data(usr) + atom_say("Successfully synchronized with R&D server.") + return + + atom_say("Unable to connect to local R&D server.") + return + +/** + * Calculates the coefficient-modified resource cost of a single material component of a design's recipe. + * + * Returns coefficient-modified resource cost for the given material component. + * * D - Design datum to pull the resource cost from. + * * resource - Material datum reference to the resource to calculate the cost of. + * * roundto - Rounding value for round() proc + */ +/obj/machinery/mecha_part_fabricator/proc/get_resource_cost_w_coeff(datum/design/D, var/amt, roundto = 1) + return round(amt * component_coeff, roundto) + +/** + * Calculates the coefficient-modified build time of a design. + * + * Returns coefficient-modified build time of a given design. + * * D - Design datum to calculate the modified build time of. + * * roundto - Rounding value for round() proc + */ +/obj/machinery/mecha_part_fabricator/proc/get_construction_time_w_coeff(construction_time, roundto = 1) //aran + return round(construction_time * time_coeff, roundto) + +/obj/machinery/mecha_part_fabricator/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/sheetmaterials) + ) + +/obj/machinery/mecha_part_fabricator/attack_hand(var/mob/user) + if(..()) + return + if(!allowed(user)) + to_chat(user, SPAN_WARNING("\The [src] rejects your use due to lack of access!")) + return + tgui_interact(user) + +/obj/machinery/mecha_part_fabricator/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ExosuitFabricator", name) + ui.open() + +/obj/machinery/mecha_part_fabricator/tgui_static_data(mob/user) + var/list/data = list() + + var/list/final_sets = list() + var/list/buildable_parts = list() + + for(var/part_set in part_sets) + final_sets += part_set + + for(var/datum/design/D in files.known_designs) + if((D.build_type & valid_buildtype) && D.id != "id") // bugfix for weird null entries + // This is for us. + var/list/part = output_part_info(D, TRUE) + + if(part["categoryOverride"]) + for(var/cat in part["categoryOverride"]) + buildable_parts[cat] += list(part) + if(!(cat in part_sets)) + final_sets += cat + continue + + for(var/cat in part_sets) + // Find all matching categories. + if(!(cat in D.category)) + continue + + buildable_parts[cat] += list(part) + + data["partSets"] = final_sets + data["buildableParts"] = buildable_parts + + return data + +/obj/machinery/mecha_part_fabricator/tgui_data(mob/user) + var/list/data = list() + + data["materials"] = output_available_resources() + + if(being_built) + var/list/part = list( + "name" = being_built.name, + "duration" = build_finish - world.time, + "printTime" = get_construction_time_w_coeff(initial(being_built.time)) + ) + data["buildingPart"] = part + else + data["buildingPart"] = null + + data["queue"] = list_queue() + + if(stored_part) + data["storedPart"] = stored_part.name + else + data["storedPart"] = null + + data["isProcessingQueue"] = process_queue + + return data + +/obj/machinery/mecha_part_fabricator/tgui_act(action, var/list/params) + if(..()) + return TRUE + + . = TRUE + + add_fingerprint(usr) + usr.set_machine(src) + + switch(action) + if("sync_rnd") + // Sync with R&D Servers + sync() + return + if("add_queue_set") + // Add all parts of a set to queue + var/part_list = params["part_list"] + add_part_set_to_queue(part_list) + return + if("add_queue_part") + // Add a specific part to queue + var/T = params["id"] + for(var/datum/design/D in files.known_designs) + if((D.build_type & valid_buildtype) && (D.id == T)) + add_to_queue(D) + break + return + if("del_queue_part") + // Delete a specific from from the queue + var/index = text2num(params["index"]) + remove_from_queue(index) + return + if("clear_queue") + // Delete everything from queue + queue.Cut() + return + if("build_queue") + // Build everything in queue + if(process_queue) + return + process_queue = TRUE + + if(!being_built) + START_PROCESSING(SSobj, src) + return + if("stop_queue") + // Pause queue building. Also known as stop. + process_queue = FALSE + return + if("build_part") + // Build a single part + if(being_built || process_queue) + return + + var/id = params["id"] + var/datum/design/D = null + for(var/datum/design/D_new in files.known_designs) + if((D_new.build_type == valid_buildtype) && (D_new.id == id)) + D = D_new + break + + if(!D) + return + + if(build_part(D)) + on_start_printing() + START_PROCESSING(SSobj, src) + + return + if("move_queue_part") + // Moves a part up or down in the queue. + var/index = text2num(params["index"]) + var/new_index = index + text2num(params["newindex"]) + if(isnum(index) && isnum(new_index) && ISINTEGER(index) && ISINTEGER(new_index)) + if(ISINRANGE(new_index,1,length(queue))) + queue.Swap(index,new_index) + return + if("remove_mat") + // Remove a material from the fab + var/mat_id = params["id"] + var/amount = text2num(params["amount"]) + eject_materials(mat_id, amount) + return + + return FALSE + +/obj/machinery/mecha_part_fabricator/attackby(var/obj/item/I, var/mob/user) + if(being_built) + to_chat(user, "\The [src] is busy. Please wait for completion of previous operation.") + return 1 + if(default_deconstruction_screwdriver(user, I)) + return + if(default_deconstruction_crowbar(user, I)) + return + if(default_part_replacement(user, I)) + return + + if(istype(I,/obj/item/stack/material)) + var/obj/item/stack/material/S = I + if(!(S.material.name in materials)) + to_chat(user, "The [src] doesn't accept [S.material]!") + return + + var/sname = "[S.name]" + var/amnt = S.perunit + if(materials[S.material.name] + amnt <= res_max_amount) + if(S && S.get_amount() >= 1) + var/count = 0 + flick("[loading_icon_state]", src) + // yess hacky but whatever //even more hacky now, but at least it works + if(loading_icon_state == "mechfab-idle") + flick("mechfab-load-metal", src) + while(materials[S.material.name] + amnt <= res_max_amount && S.get_amount() >= 1) + materials[S.material.name] += amnt + S.use(1) + count++ + to_chat(user, "You insert [count] [sname] into the fabricator.") + else + to_chat(user, "The fabricator cannot hold more [sname].") + + return + + ..() + +/obj/machinery/mecha_part_fabricator/emag_act(var/remaining_charges, var/mob/user) + switch(emagged) + if(0) + emagged = 0.5 + visible_message("\icon[src][bicon(src)] [src] beeps: \"DB error \[Code 0x00F1\]\"") + sleep(10) + visible_message("\icon[src][bicon(src)] [src] beeps: \"Attempting auto-repair\"") + sleep(15) + visible_message("\icon[src][bicon(src)] [src] beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"") + sleep(30) + visible_message("\icon[src][bicon(src)] [src] beeps: \"User DB truncated. Please contact your [using_map.company_name] system operator for future assistance.\"") + req_access = null + emagged = 1 + return 1 + if(0.5) + visible_message("\icon[src][bicon(src)] [src] beeps: \"DB not responding \[Code 0x0003\]...\"") + if(1) + visible_message("\icon[src][bicon(src)] [src] beeps: \"No records in User DB\"") + +/obj/machinery/mecha_part_fabricator/proc/eject_materials(var/material, var/amount) // 0 amount = 0 means ejecting a full stack; -1 means eject everything + var/recursive = amount == -1 ? TRUE : FALSE + var/matstring = lowertext(material) + + // 0 or null, nothing to eject + if(!materials[matstring]) + return + // Problem, fix problem and abort + if(materials[matstring] < 0) + warning("[src] tried to eject material '[material]', which it has 'materials[matstring]' of!") + materials[matstring] = 0 + return + + // Find the material datum for our material + var/datum/material/M = get_material_by_name(matstring) + if(!M) + warning("[src] tried to eject material '[matstring]', which didn't match any known material datum!") + return + // Find what type of sheets it makes + var/obj/item/stack/material/S = M.stack_type + if(!S) + warning("[src] tried to eject material '[matstring]', which didn't have a stack_type!") + return + + // If we were passed -1, then it's recursive ejection and we should eject all we can + if(amount <= 0) + amount = initial(S.max_amount) + // Smaller of what we have left, or the desired amount (note the amount is in sheets, but the array stores perunit values) + var/ejected = min(round(materials[matstring] / initial(S.perunit)), amount) + + // Place a sheet + S = M.place_sheet(get_turf(src), ejected) + if(!istype(S)) + warning("[src] tried to eject material '[material]', which didn't generate a proper stack when asked!") + return + + // Reduce our amount stored + materials[matstring] -= ejected * S.perunit + + // Recurse if we have enough left for more sheets + if(recursive && materials[matstring] >= S.perunit) + eject_materials(matstring, -1) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 633757a03f9..07bba04dc1d 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -1,2913 +1,2913 @@ -#define MECHA_INT_FIRE 1 -#define MECHA_INT_TEMP_CONTROL 2 -#define MECHA_INT_SHORT_CIRCUIT 4 -#define MECHA_INT_TANK_BREACH 8 -#define MECHA_INT_CONTROL_LOST 16 - -#define MECHA_PROC_MOVEMENT 1 -#define MECHA_PROC_DAMAGE 2 -#define MECHA_PROC_INT_TEMP 4 - -#define MELEE 1 -#define RANGED 2 - -#define MECHA_OPERATING 0 -#define MECHA_BOLTS_SECURED 1 -#define MECHA_PANEL_LOOSE 2 -#define MECHA_CELL_OPEN 3 -#define MECHA_CELL_OUT 4 - -#define MECH_FACTION_NT "nano" -#define MECH_FACTION_SYNDI "syndi" -#define MECH_FACTION_NONE "none" - -/obj/mecha - name = "Mecha" - desc = "Exosuit" - description_info = "Alt click to strafe." - icon = 'icons/mecha/mecha.dmi' - density = TRUE //Dense. To raise the heat. - opacity = 1 //Opaque. Menacing. - anchored = TRUE //No pulling around. - unacidable = TRUE //And no deleting hoomans inside - layer = MOB_LAYER //Icon draw layer - infra_luminosity = 15 //Byond implementation is bugged. - var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items) - var/can_move = 1 - var/mob/living/carbon/occupant = null - - var/step_in = 10 //Make a step in step_in/10 sec. - var/encumbrance_gap = 1 //How many points of slowdown are negated from equipment? Added to the mech's base step_in. - - var/dir_in = 2 //What direction will the mech face when entered/powered on? Defaults to South. - var/step_energy_drain = 10 - var/health = 300 //Health is health - var/maxhealth = 300 //Maxhealth is maxhealth. - var/deflect_chance = 10 //Chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. - - var/damage_minimum = 10 //Incoming damage lower than this won't actually deal damage. Scrapes shouldn't be a real thing. - var/minimum_penetration = 15 //Incoming damage won't be fully applied if you don't have at least 20. Almost all AP clears this. - var/fail_penetration_value = 0.66 //By how much failing to penetrate reduces your shit. 66% by default. 100dmg = 66dmg if failed pen - - var/obj/item/weapon/cell/cell - var/state = MECHA_OPERATING - var/list/log = new - var/last_message = 0 - var/add_req_access = 1 - var/maint_access = 1 - var/dna //Dna-locking the mech - var/list/proc_res = list() //Stores proc owners, like proc_res["functionname"] = owner reference - var/datum/effect/effect/system/spark_spread/spark_system = new - var/lights = 0 - var/lights_power = 6 - var/force = 0 - - var/mech_faction = null - var/firstactivation = 0 //It's simple. If it's 0, no one entered it yet. Otherwise someone entered it at least once. - - var/stomp_sound = 'sound/mecha/mechstep.ogg' - var/swivel_sound = 'sound/mecha/mechturn.ogg' - - //inner atmos - var/use_internal_tank = 0 - var/internal_tank_valve = ONE_ATMOSPHERE - var/obj/machinery/portable_atmospherics/canister/internal_tank - var/datum/gas_mixture/cabin_air - var/obj/machinery/atmospherics/portables_connector/connected_port = null - - var/obj/item/device/radio/radio = null - - var/max_temperature = 25000 //Kelvin values. - var/internal_damage_threshold = 33 //Health percentage below which internal damage is possible - var/internal_damage_minimum = 15 //At least this much damage to trigger some real bad hurt. - var/internal_damage = 0 //Contains bitflags - - var/list/operation_req_access = list() //Required access level for mecha operation - var/list/internals_req_access = list(access_engine,access_robotics) //Required access level to open cell compartment - - var/wreckage - - var/list/equipment = new //This lists holds what stuff you bolted onto your baby ride - var/obj/item/mecha_parts/mecha_equipment/selected - var/max_equip = 2 - - // What direction to float in, if inertial movement is active. - var/float_direction = 0 - // Process() iterator count. - var/process_ticks = 0 - // These control what toggleable processes are executed within process(). - var/current_processes = MECHA_PROC_INT_TEMP - -//mechaequipt2 stuffs - var/list/hull_equipment = new - var/list/weapon_equipment = new - var/list/utility_equipment = new - var/list/universal_equipment = new - var/list/special_equipment = new - var/max_hull_equip = 2 - var/max_weapon_equip = 2 - var/max_utility_equip = 2 - var/max_universal_equip = 2 - var/max_special_equip = 1 - - var/list/starting_equipment = null // List containing starting tools. - -// Mech Components, similar to Cyborg, but Bigger. - var/list/internal_components = list( - MECH_HULL = null, - MECH_ACTUATOR = null, - MECH_ARMOR = null, - MECH_GAS = null, - MECH_ELECTRIC = null - ) - var/list/starting_components = list( - /obj/item/mecha_parts/component/hull, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - -//Working exosuit vars - var/list/cargo = list() - var/cargo_capacity = 3 - - var/static/image/radial_image_eject = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject") - var/static/image/radial_image_airtoggle = image(icon= 'icons/mob/radial.dmi', icon_state = "radial_airtank") - var/static/image/radial_image_lighttoggle = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_light") - var/static/image/radial_image_statpanel = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_examine2") - -//Mech actions - var/datum/mini_hud/mech/minihud //VOREStation Edit - var/strafing = 0 //Are we strafing or not? - - var/defence_mode_possible = 0 //Can we even use defence mode? This is used to assign it to mechs and check for verbs. - var/defence_mode = 0 //Are we in defence mode - var/defence_deflect = 35 //How much it deflect - - var/overload_possible = 0 //Same as above. Don't forget to GRANT the verb&actions if you want everything to work proper. - var/overload = 0 //Are our legs overloaded - var/overload_coeff = 1 //How much extra energy you use when use the L E G - - var/zoom = 0 - var/zoom_possible = 0 - - var/thrusters = 0 - var/thrusters_possible = 0 - - var/phasing = 0 //Are we currently phasing - var/phasing_possible = 0 //This is to allow phasing. - var/can_phase = TRUE //This is an internal check during the relevant procs. - var/phasing_energy_drain = 200 - - var/switch_dmg_type_possible = 0 //Can you switch damage type? It is mostly for the Phazon and its children. - - var/smoke_possible = 0 - var/smoke_reserve = 5 //How many shots you have. Might make a reload later on. MIGHT. - var/smoke_ready = 1 //This is a check for the whether or not the cooldown is ongoing. - var/smoke_cooldown = 100 //How long you have between uses. - var/datum/effect/effect/system/smoke_spread/smoke_system = new - - var/cloak_possible = FALSE // Can this exosuit innately cloak? - -////All of those are for the HUD buttons in the top left. See Grant and Remove procs in mecha_actions. - - var/datum/action/innate/mecha/mech_eject/eject_action = new - var/datum/action/innate/mecha/mech_toggle_internals/internals_action = new - var/datum/action/innate/mecha/mech_toggle_lights/lights_action = new - var/datum/action/innate/mecha/mech_view_stats/stats_action = new - var/datum/action/innate/mecha/strafe/strafing_action = new - - var/datum/action/innate/mecha/mech_defence_mode/defence_action = new - var/datum/action/innate/mecha/mech_overload_mode/overload_action = new - var/datum/action/innate/mecha/mech_smoke/smoke_action = new - var/datum/action/innate/mecha/mech_zoom/zoom_action = new - var/datum/action/innate/mecha/mech_toggle_thrusters/thrusters_action = new - var/datum/action/innate/mecha/mech_cycle_equip/cycle_action = new - var/datum/action/innate/mecha/mech_switch_damtype/switch_damtype_action = new - var/datum/action/innate/mecha/mech_toggle_phasing/phasing_action = new - var/datum/action/innate/mecha/mech_toggle_cloaking/cloak_action = new - - var/weapons_only_cycle = FALSE //So combat mechs don't switch to their equipment at times. - -/obj/mecha/Initialize() - . = ..() - - for(var/path in starting_components) - var/obj/item/mecha_parts/component/C = new path(src) - C.attach(src) - - if(starting_equipment && LAZYLEN(starting_equipment)) - for(var/path in starting_equipment) - var/obj/item/mecha_parts/mecha_equipment/ME = new path(src) - ME.attach(src) - - START_PROCESSING(SSobj, src) - - update_transform() - -/obj/mecha/drain_power(var/drain_check) - - if(drain_check) - return 1 - - if(!cell) - return 0 - - return cell.drain_power(drain_check) - -/obj/mecha/New() - ..() - icon_state += "-open" - add_radio() - add_cabin() - if(!add_airtank()) //we check this here in case mecha does not have an internal tank available by default - WIP - removeVerb(/obj/mecha/verb/connect_to_port) - removeVerb(/obj/mecha/verb/toggle_internal_tank) - - spark_system.set_up(2, 0, src) - spark_system.attach(src) - - if(smoke_possible)//I am pretty sure that's needed here. - src.smoke_system.set_up(3, 0, src) - src.smoke_system.attach(src) - - add_cell() - removeVerb(/obj/mecha/verb/disconnect_from_port) - log_message("[src.name] created.") - loc.Entered(src) - mechas_list += src //global mech list - return - -/obj/mecha/Exit(atom/movable/O) - if(O in cargo) - return 0 - return ..() - -/obj/mecha/Destroy() - src.go_out() - for(var/mob/M in src) //Be Extra Sure - M.forceMove(get_turf(src)) - M.loc.Entered(M) - if(M != src.occupant) - step_rand(M) - for(var/atom/movable/A in src.cargo) - A.forceMove(get_turf(src)) - var/turf/T = get_turf(A) - if(T) - T.Entered(A) - step_rand(A) - - if(loc) - loc.Exited(src) - - if(prob(30)) - explosion(get_turf(loc), 0, 0, 1, 3) - - if(wreckage) - var/obj/effect/decal/mecha_wreckage/WR = new wreckage(loc) - hull_equipment.Cut() - weapon_equipment.Cut() - utility_equipment.Cut() - universal_equipment.Cut() - special_equipment.Cut() - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) - if(E.salvageable && prob(30)) - WR.crowbar_salvage += E - E.forceMove(WR) - E.equip_ready = TRUE - else - E.forceMove(loc) - E.destroy() - - for(var/slot in internal_components) - var/obj/item/mecha_parts/component/C = internal_components[slot] - if(istype(C)) - C.damage_part(rand(10, 20)) - C.detach() - WR.crowbar_salvage += C - C.forceMove(WR) - - if(cell) - WR.crowbar_salvage += cell - cell.forceMove(WR) - cell.charge = rand(0, cell.charge) - if(internal_tank) - WR.crowbar_salvage += internal_tank - internal_tank.forceMove(WR) - else - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) - E.detach(loc) - E.destroy() - for(var/slot in internal_components) - var/obj/item/mecha_parts/component/C = internal_components[slot] - if(istype(C)) - C.detach() - qdel(C) - if(cell) - qdel(cell) - if(internal_tank) - qdel(internal_tank) - equipment.Cut() - cell = null - internal_tank = null - - if(smoke_possible) //Just making sure nothing is running. - qdel(smoke_system) - - GLOB.mech_destroyed_roundstat++ - - QDEL_NULL(spark_system) - QDEL_NULL(minihud) - - STOP_PROCESSING(SSobj, src) - - mechas_list -= src //global mech list - . = ..() - -// The main process loop to replace the ancient global iterators. -// It's a bit hardcoded but I don't see anyone else adding stuff to -// mechas, and it's easy enough to modify. -/obj/mecha/process() - var/static/max_ticks = 16 - - if (current_processes & MECHA_PROC_MOVEMENT) - process_inertial_movement() - - if ((current_processes & MECHA_PROC_DAMAGE) && !(process_ticks % 2)) - process_internal_damage() - - if ((current_processes & MECHA_PROC_INT_TEMP) && !(process_ticks % 4)) - process_preserve_temp() - - if (!(process_ticks % 3)) - process_tank_give_air() - - // Max value is 16. So we let it run between [0, 16] with this. - process_ticks = (process_ticks + 1) % 17 - -// Normalizing cabin air temperature to 20 degrees celsius. -// Called every fourth process() tick (20 deciseconds). -/obj/mecha/proc/process_preserve_temp() - if (cabin_air && cabin_air.volume > 0) - var/delta = cabin_air.temperature - T20C - cabin_air.temperature -= max(-10, min(10, round(delta/4,0.1))) - -// Handles internal air tank action. -// Called every third process() tick (15 deciseconds). -/obj/mecha/proc/process_tank_give_air() - if(internal_tank) - var/datum/gas_mixture/tank_air = internal_tank.return_air() - - var/release_pressure = internal_tank_valve - var/cabin_pressure = cabin_air.return_pressure() - var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2) - var/transfer_moles = 0 - - if(pressure_delta > 0) //cabin pressure lower than release pressure - if(tank_air.temperature > 0) - transfer_moles = pressure_delta*cabin_air.volume/(cabin_air.temperature * R_IDEAL_GAS_EQUATION) - var/datum/gas_mixture/removed = tank_air.remove(transfer_moles) - cabin_air.merge(removed) - - else if(pressure_delta < 0) //cabin pressure higher than release pressure - var/datum/gas_mixture/t_air = get_turf_air() - pressure_delta = cabin_pressure - release_pressure - - if(t_air) - pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta) - if(pressure_delta > 0) //if location pressure is lower than cabin pressure - transfer_moles = pressure_delta*cabin_air.volume/(cabin_air.temperature * R_IDEAL_GAS_EQUATION) - - var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles) - if(t_air) - t_air.merge(removed) - else //just delete the cabin gas, we're in space or some shit - qdel(removed) - -// Inertial movement in space. -// Called every process() tick (5 deciseconds). -/obj/mecha/proc/process_inertial_movement() - if(float_direction) - if(!step(src, float_direction) || check_for_support()) - stop_process(MECHA_PROC_MOVEMENT) - else - stop_process(MECHA_PROC_MOVEMENT) - return - -// Processes internal damage. -// Called every other process() tick (10 deciseconds). -/obj/mecha/proc/process_internal_damage() - if(!hasInternalDamage()) - stop_process(MECHA_PROC_DAMAGE) - return - - if(hasInternalDamage(MECHA_INT_FIRE)) - if(!hasInternalDamage(MECHA_INT_TEMP_CONTROL) && prob(5)) - clearInternalDamage(MECHA_INT_FIRE) - if(internal_tank) - if(internal_tank.return_pressure()>internal_tank.maximum_pressure && !(hasInternalDamage(MECHA_INT_TANK_BREACH))) - setInternalDamage(MECHA_INT_TANK_BREACH) - var/datum/gas_mixture/int_tank_air = internal_tank.return_air() - if(int_tank_air && int_tank_air.volume>0) //heat the air_contents - int_tank_air.temperature = min(6000+T0C, int_tank_air.temperature+rand(10,15)) - if(cabin_air && cabin_air.volume>0) - cabin_air.temperature = min(6000+T0C, cabin_air.temperature+rand(10,15)) - if(cabin_air.temperature>max_temperature/2) - take_damage(4/round(max_temperature/cabin_air.temperature,0.1),"fire") - - if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) - stop_process(MECHA_PROC_INT_TEMP) - - if(hasInternalDamage(MECHA_INT_TANK_BREACH)) //remove some air from internal tank - if(internal_tank) - var/datum/gas_mixture/int_tank_air = internal_tank.return_air() - var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10) - if(istype(loc, /turf/simulated)) - loc.assume_air(leaked_gas) - else - qdel(leaked_gas) - - if(hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) - if(get_charge()) - spark_system.start() - cell.charge -= min(20,cell.charge) - cell.maxcharge -= min(20,cell.maxcharge) - return - -//////////////////////// -////// Helpers ///////// -//////////////////////// - -/obj/mecha/proc/removeVerb(verb_path) - verbs -= verb_path - -/obj/mecha/proc/addVerb(verb_path) - verbs += verb_path - -/obj/mecha/proc/add_airtank() - internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src) - return internal_tank - -/obj/mecha/proc/add_cell(var/obj/item/weapon/cell/C=null) - if(C) - C.forceMove(src) - cell = C - return - cell = new /obj/item/weapon/cell/mech(src) - -/obj/mecha/get_cell() - return cell - -/obj/mecha/proc/add_cabin() - cabin_air = new - cabin_air.temperature = T20C - cabin_air.volume = 200 - cabin_air.adjust_multi("oxygen", O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature), "nitrogen", N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature)) - return cabin_air - -/obj/mecha/proc/add_radio() - radio = new(src) - radio.name = "[src] radio" - radio.icon = icon - radio.icon_state = icon_state - radio.subspace_transmission = 1 - -/obj/mecha/proc/do_after(delay as num) - sleep(delay) - if(src) - return 1 - return 0 - -/obj/mecha/proc/enter_after(delay as num, var/mob/user as mob, var/numticks = 5) - var/delayfraction = delay/numticks - - var/turf/T = user.loc - - for(var/i = 0, iInterfacing with [target].") - src.log_message("Interfaced with [target].") - target.attack_hand(src.occupant) - return 1 - if(istype(target, /obj/machinery/embedded_controller)) - target.tgui_interact(src.occupant) - return 1 - return 0 - -/obj/mecha/contents_tgui_distance(var/src_object, var/mob/living/user) - . = user.shared_living_tgui_distance(src_object) //allow them to interact with anything they can interact with normally. - if(. != STATUS_INTERACTIVE) - //Allow interaction with the mecha or anything that is part of the mecha - if(src_object == src || (src_object in src)) - return STATUS_INTERACTIVE - if(src.Adjacent(src_object)) - src.occupant_message("Interfacing with [src_object]...") - src.log_message("Interfaced with [src_object].") - return STATUS_INTERACTIVE - if(src_object in view(2, src)) - return STATUS_UPDATE //if they're close enough, allow the occupant to see the screen through the viewport or whatever. - -/obj/mecha/proc/melee_action(atom/target) - return - -/obj/mecha/proc/range_action(atom/target) - return - - -////////////////////////////////// -//////// Movement procs //////// -////////////////////////////////// - -/obj/mecha/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - MoveAction() - -/obj/mecha/proc/MoveAction() //Allows mech equipment to do an action once the mech moves - if(!equipment.len) - return - - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - ME.MoveAction() - -/obj/mecha/relaymove(mob/user,direction) - if(user != src.occupant) //While not "realistic", this piece is player friendly. - if(istype(user,/mob/living/carbon/brain)) - to_chat(user, "You try to move, but you are not the pilot! The exosuit doesn't respond.") - return 0 - user.forceMove(get_turf(src)) - to_chat(user, "You climb out from [src]") - return 0 - - var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] - if(!HC) - occupant_message("You can't operate an exosuit that doesn't have a hull!") - return - - if(connected_port) - if(world.time - last_message > 20) - src.occupant_message("Unable to move while connected to the air system port") - last_message = world.time - return 0 - if(state) - occupant_message("Maintenance protocols in effect") - return -/* - if(zoom) - if(world.time - last_message > 20) - src.occupant_message("Unable to move while in zoom mode.") - last_message = world.time - return 0 -*/ - return domove(direction) - -/obj/mecha/proc/can_ztravel() - for(var/obj/item/mecha_parts/mecha_equipment/tool/jetpack/jp in equipment) - return jp.equip_ready - return FALSE - -/obj/mecha/proc/domove(direction) - - return call((proc_res["dyndomove"]||src), "dyndomove")(direction) - -/obj/mecha/proc/get_step_delay() - var/tally = 0 - - if(LAZYLEN(equipment)) - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - if(ME.get_step_delay()) - tally += ME.get_step_delay() - - if(tally <= encumbrance_gap) // If the total is less than our encumbrance gap, ignore equipment weight. - tally = 0 - else // Otherwise, start the tally after cutting that gap out. - tally -= encumbrance_gap - - for(var/slot in internal_components) - var/obj/item/mecha_parts/component/C = internal_components[slot] - if(C && C.get_step_delay()) - tally += C.get_step_delay() - - var/obj/item/mecha_parts/component/actuator/actuator = internal_components[MECH_ACTUATOR] - - if(!actuator) // Relying purely on hydraulic pumps. You're going nowhere fast. - tally = 2 SECONDS - - return tally - - tally += 0.5 SECONDS * (1 - actuator.get_efficiency()) // Damaged actuators run slower, slowing as damage increases beyond its threshold. - - if(strafing) - tally = round(tally * actuator.strafing_multiplier) - - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - if(istype(ME, /obj/item/mecha_parts/mecha_equipment/speedboost)) - var/obj/item/mecha_parts/mecha_equipment/speedboost/SB = ME - for(var/path in ME.required_type) - if(istype(src, path)) - tally = round(tally * SB.slowdown_multiplier) - break - break - - if(overload) // At the end, because this would normally just make the mech *slower* since tally wasn't starting at 0. - tally = min(1, round(tally/2)) - - return max(1, round(tally, 0.1)) // Round the total to the nearest 10th. Can't go lower than 1 tick. Even humans have a delay longer than that. - -/obj/mecha/proc/dyndomove(direction) - if(!can_move) - return 0 - if(current_processes & MECHA_PROC_MOVEMENT) - return 0 - if(!has_charge(step_energy_drain)) - return 0 - - //Can we even move, below is if yes. - - if(defence_mode)//Check if we are currently locked down - if(world.time - last_message > 20) - src.occupant_message(span_red("Unable to move while in defence mode")) - last_message = world.time - return 0 - - if(zoom)//:eyes: - if(world.time - last_message > 20) - src.occupant_message("Unable to move while in zoom mode.") - last_message = world.time - return 0 - - if(!thrusters && (current_processes & MECHA_PROC_MOVEMENT)) //I think this mean 'if you try to move in space without thruster, u no move' - return 0 - - if(overload)//Check if you have leg overload - health-- - if(health < initial(health) - initial(health)/3) - overload = 0 - step_energy_drain = initial(step_energy_drain) - src.occupant_message(span_red("Leg actuators damage threshold exceded. Disabling overload.")) - - - var/move_result = 0 - - if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) - move_result = mechsteprand() - //Up/down zmove - else if(direction == UP || direction == DOWN) - if(!can_ztravel()) - occupant_message("Your vehicle lacks the capacity to move in that direction!") - return FALSE - - //We're using locs because some mecha are 2x2 turfs. So thicc! - var/result = TRUE - - for(var/turf/T in locs) - if(!T.CanZPass(src,direction)) - occupant_message("You can't move that direction from here!") - result = FALSE - break - var/turf/dest = (direction == UP) ? GetAbove(src) : GetBelow(src) - if(!dest) - occupant_message("There is nothing of interest in this direction.") - result = FALSE - break - if(!dest.CanZPass(src,direction)) - occupant_message("There's something blocking your movement in that direction!") - result = FALSE - break - if(result) - move_result = mechstep(direction) - - //Turning - - else if(src.dir != direction) - - if(strafing) - move_result = mechstep(direction) - else - move_result = mechturn(direction) - - //Stepping - else - move_result = mechstep(direction) - - - if(move_result) - can_move = 0 - use_power(step_energy_drain) - if(istype(src.loc, /turf/space)) - if(!src.check_for_support()) - float_direction = direction - start_process(MECHA_PROC_MOVEMENT) - src.log_message("Movement control lost. Inertial movement started.") - if(do_after(get_step_delay())) - can_move = 1 - return 1 - return 0 - -/obj/mecha/proc/handle_equipment_movement() - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - if(ME.chassis == src) //Sanity - ME.handle_movement_action() - return - -/obj/mecha/proc/mechturn(direction) - set_dir(direction) - if(swivel_sound) - playsound(src,swivel_sound,40,1) - return 1 - -/obj/mecha/proc/mechstep(direction) - var/current_dir = dir //For strafing - var/result = get_step(src,direction) - if(result && Move(result)) - if(stomp_sound) - playsound(src,stomp_sound,40,1) - handle_equipment_movement() - if(strafing) //Also for strafing - set_dir(current_dir) - return result - - -/obj/mecha/proc/mechsteprand() - var/result = get_step_rand(src) - if(result && Move(result)) - if(stomp_sound) - playsound(src,stomp_sound,40,1) - handle_equipment_movement() - return result - -/obj/mecha/Bump(var/atom/obstacle) -// src.inertia_dir = null - if(istype(obstacle, /mob))//First we check if it is a mob. Mechs mostly shouln't go through them, even while phasing. - var/mob/M = obstacle - M.Move(get_step(obstacle,src.dir)) - else if(phasing && get_charge()>=phasing_energy_drain)//Phazon check. This could use an improvement elsewhere. - src.use_power(phasing_energy_drain) - phase() - . = ..(obstacle) - return - else if(istype(obstacle, /obj))//Then we check for regular obstacles. - var/obj/O = obstacle - if(istype(O, /obj/effect/portal)) //derpfix - src.anchored = 0 // Portals can only move unanchored objects. - O.Crossed(src) - spawn(0)//countering portal teleport spawn(0), hurr - src.anchored = 1 - if(O.anchored) - obstacle.Bumped(src) - else - step(obstacle,src.dir) - - else//No idea when this triggers, so i won't touch it. - . = ..(obstacle) - return - -/obj/mecha/proc/phase() // Force the mecha to move forward by phasing. - set waitfor = FALSE - if(can_phase) - can_phase = FALSE - flick("[initial_icon]-phase", src) - forceMove(get_step(src,src.dir)) - sleep(get_step_delay() * 3) - can_phase = TRUE - occupant_message("Phazed.") - return TRUE // In the event this is sequenced - return FALSE - -/////////////////////////////////// -//////// Internal damage //////// -/////////////////////////////////// - -//ATM, the ignore_threshold is literally only used for the pulse rifles beams used mostly by deathsquads. -/obj/mecha/proc/check_for_internal_damage(var/list/possible_int_damage,var/ignore_threshold=null) - if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) return - if(prob(30)) - if(ignore_threshold || src.health*100/initial(src.health) < src.internal_damage_threshold) - for(var/T in possible_int_damage) - if(internal_damage & T) - possible_int_damage -= T - var/int_dam_flag = safepick(possible_int_damage) - if(int_dam_flag) - setInternalDamage(int_dam_flag) - return //It already hurts to get some, lets not get both. - - if(prob(10)) - if(ignore_threshold || src.health*100/initial(src.health) < src.internal_damage_threshold) - var/obj/item/mecha_parts/mecha_equipment/destr = safepick(equipment) - if(destr) - destr.destroy() - return - -/obj/mecha/proc/hasInternalDamage(int_dam_flag=null) - return int_dam_flag ? internal_damage&int_dam_flag : internal_damage - - -/obj/mecha/proc/setInternalDamage(int_dam_flag) - internal_damage |= int_dam_flag - start_process(MECHA_PROC_DAMAGE) - log_append_to_last("Internal damage of type [int_dam_flag].",1) - occupant << sound('sound/mecha/internaldmgalarm.ogg',volume=50) //Better sounding. - return - -/obj/mecha/proc/clearInternalDamage(int_dam_flag) - internal_damage &= ~int_dam_flag - switch(int_dam_flag) - if(MECHA_INT_TEMP_CONTROL) - occupant_message(span_blue("Life support system reactivated.")) - start_process(MECHA_PROC_INT_TEMP) - if(MECHA_INT_FIRE) - occupant_message(span_blue("Internal fire extinquished.")) - if(MECHA_INT_TANK_BREACH) - occupant_message(span_blue("Damaged internal tank has been sealed.")) - return - - -//////////////////////////////////////// -//////// Health related procs //////// -//////////////////////////////////////// - -/obj/mecha/take_damage(amount, type="brute") - update_damage_alerts() - if(amount) - var/damage = absorbDamage(amount,type) - - damage = components_handle_damage(damage,type) - - health -= damage - - update_health() - log_append_to_last("Took [damage] points of damage. Damage type: \"[type]\".",1) - return - -/obj/mecha/proc/components_handle_damage(var/damage, var/type = BRUTE) - var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] - - if(AC) - var/armor_efficiency = AC.get_efficiency() - var/damage_change = armor_efficiency * (damage * 0.5) * AC.damage_absorption[type] - AC.damage_part(damage_change, type) - damage -= damage_change - - var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] - - if(HC) - if(HC.integrity) - var/hull_absorb = round(rand(5, 10) / 10, 0.1) * damage - HC.damage_part(hull_absorb, type) - damage -= hull_absorb - - for(var/obj/item/mecha_parts/component/C in (internal_components - list(MECH_HULL, MECH_ARMOR))) - if(prob(C.relative_size)) - var/damage_part_amt = round(damage / 4, 0.1) - C.damage_part(damage_part_amt) - damage -= damage_part_amt - - return damage - -/obj/mecha/proc/get_damage_absorption() - var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] - if(istype(AC) && AC.get_efficiency() > 0.25) - return AC.damage_absorption - -/obj/mecha/proc/absorbDamage(damage,damage_type) - return call((proc_res["dynabsorbdamage"]||src), "dynabsorbdamage")(damage,damage_type) - -/obj/mecha/proc/dynabsorbdamage(damage,damage_type) - return damage*(listgetindex(get_damage_absorption(),damage_type) || 1) - -/obj/mecha/airlock_crush(var/crush_damage) - ..() - take_damage(crush_damage) - if(prob(50)) //Try to avoid that. - check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - return 1 - -/obj/mecha/proc/update_health() - if(src.health > 0) - src.spark_system.start() - else - qdel(src) - return - -/obj/mecha/attack_hand(mob/user as mob) - if(user == occupant) - show_radial_occupant(user) - return - - user.setClickCooldown(user.get_attack_speed()) - src.log_message("Attack by hand/paw. Attacker - [user].",1) - - var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] - - var/temp_deflect_chance = deflect_chance - - if(!ArmC) - temp_deflect_chance = 1 - - else - temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - if(H.species.can_shred(user)) - if(!prob(temp_deflect_chance)) - src.take_damage(15) //The take_damage() proc handles armor values - if(prob(25)) //Why would they get free internal damage. At least make it a bit RNG. - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) - to_chat(user, "You slash at the armored suit!") - visible_message("\The [user] slashes at [src.name]'s armor!") - else - src.log_append_to_last("Armor saved.") - playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) - to_chat(user, "Your claws had no effect!") - src.occupant_message("\The [user]'s claws are stopped by the armor.") - visible_message("\The [user] rebounds off [src.name]'s armor!") - else - user.visible_message("\The [user] hits \the [src]. Nothing happens.","You hit \the [src] with no visible effect.") - src.log_append_to_last("Armor saved.") - return - else if ((HULK in user.mutations) && !prob(temp_deflect_chance)) - src.take_damage(15) //The take_damage() proc handles armor values - if(prob(25)) //Hulks punch hard but lets not give them consistent internal damage. - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - user.visible_message(span_red("[user] hits [src.name], doing some damage."), span_red("You hit [src.name] with all your might. The metal creaks and bends.")) - else - user.visible_message(span_red("[user] hits [src.name]. Nothing happens."),span_red("You hit [src.name] with no visible effect.")) - src.log_append_to_last("Armor saved.") - return - -/obj/mecha/hitby(atom/movable/A as mob|obj) //wrapper - ..() - src.log_message("Hit by [A].",1) - call((proc_res["dynhitby"]||src), "dynhitby")(A) - return - -//I think this is relative to throws. -/obj/mecha/proc/dynhitby(atom/movable/A) - var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] - - var/temp_deflect_chance = deflect_chance - var/temp_damage_minimum = damage_minimum - var/temp_minimum_penetration = minimum_penetration - var/temp_fail_penetration_value = fail_penetration_value - - if(!ArmC) - temp_deflect_chance = 0 - temp_damage_minimum = 0 - temp_minimum_penetration = 0 - temp_fail_penetration_value = 1 - - else - temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) - temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) - temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) - temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value) - - if(istype(A, /obj/item/mecha_parts/mecha_tracking)) - A.forceMove(src) - src.visible_message("The [A] fastens firmly to [src].") - return - if(prob(temp_deflect_chance) || istype(A, /mob)) - src.occupant_message("\The [A] bounces off the armor.") - src.visible_message("\The [A] bounces off \the [src] armor") - src.log_append_to_last("Armor saved.") - if(istype(A, /mob/living)) - var/mob/living/M = A - M.take_organ_damage(10) - else if(istype(A, /obj)) - var/obj/O = A - if(O.throwforce) - - var/pass_damage = O.throwforce - var/pass_damage_reduc_mod - if(pass_damage <= temp_damage_minimum)//Too little to go through. - src.occupant_message("\The [A] bounces off the armor.") - src.visible_message("\The [A] bounces off \the [src] armor") - return - - else if(O.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage - src.occupant_message("\The [A] struggles to bypass \the [src] armor.") - src.visible_message("\The [A] struggles to bypass \the [src] armor") - pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default - else - src.occupant_message("\The [A] manages to pierce \the [src] armor.") -// src.visible_message("\The [A] manages to pierce \the [src] armor") - pass_damage_reduc_mod = 1 - - - - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - pass_damage = ME.handle_ranged_contact(A, pass_damage) - - pass_damage = (pass_damage*pass_damage_reduc_mod)//Applying damage reduction - src.take_damage(pass_damage) //The take_damage() proc handles armor values - if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - return - - -/obj/mecha/bullet_act(var/obj/item/projectile/Proj) //wrapper - if(istype(Proj, /obj/item/projectile/test)) - var/obj/item/projectile/test/Test = Proj - Test.hit |= occupant // Register a hit on the occupant, for things like turrets, or in simple-mob cases stopping friendly fire in firing line mode. - return - - src.log_message("Hit by projectile. Type: [Proj.name]([Proj.check_armour]).",1) - call((proc_res["dynbulletdamage"]||src), "dynbulletdamage")(Proj) //calls equipment - ..() - return - -/obj/mecha/proc/dynbulletdamage(var/obj/item/projectile/Proj) - var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] - - var/temp_deflect_chance = deflect_chance - var/temp_damage_minimum = damage_minimum - var/temp_minimum_penetration = minimum_penetration - var/temp_fail_penetration_value = fail_penetration_value - - if(!ArmC) - temp_deflect_chance = 0 - temp_damage_minimum = 0 - temp_minimum_penetration = 0 - temp_fail_penetration_value = 1 - - else - temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) - temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) - temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) - temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value) - - if(prob(temp_deflect_chance)) - src.occupant_message("The armor deflects incoming projectile.") - src.visible_message("The [src.name] armor deflects the projectile") - src.log_append_to_last("Armor saved.") - return - - if(Proj.damage_type == HALLOSS) - use_power(Proj.agony * 5) - - if(!(Proj.nodamage)) - var/ignore_threshold - if(istype(Proj, /obj/item/projectile/beam/pulse)) //ATM, this is literally only for the pulse rifles used mostly by deathsquads. - ignore_threshold = 1 - - var/pass_damage = Proj.damage - var/pass_damage_reduc_mod - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - pass_damage = ME.handle_projectile_contact(Proj, pass_damage) - - if(pass_damage < temp_damage_minimum)//too pathetic to really damage you. - src.occupant_message("The armor deflects incoming projectile.") - src.visible_message("The [src.name] armor deflects\the [Proj]") - return - - else if(Proj.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage - src.occupant_message("\The [Proj] struggles to pierce \the [src] armor.") - src.visible_message("\The [Proj] struggles to pierce \the [src] armor") - pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default - - else //You go through completely because you use AP. Nice. - src.occupant_message("\The [Proj] manages to pierce \the [src] armor.") -// src.visible_message("\The [Proj] manages to pierce \the [src] armor") - pass_damage_reduc_mod = 1 - - pass_damage = (pass_damage_reduc_mod*pass_damage)//Apply damage reduction before usage. - src.take_damage(pass_damage, Proj.check_armour) //The take_damage() proc handles armor values - if(prob(25)) - spark_system.start() - if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. - src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),ignore_threshold) - - //AP projectiles have a chance to cause additional damage - if(Proj.penetrating) - var/distance = get_dist(Proj.starting, get_turf(loc)) - var/hit_occupant = 1 //only allow the occupant to be hit once - for(var/i in 1 to min(Proj.penetrating, round(Proj.damage/15))) - if(src.occupant && hit_occupant && prob(20)) - Proj.attack_mob(src.occupant, distance) - hit_occupant = 0 - else - if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. - src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT), 1) - - Proj.penetrating-- - - if(prob(15)) - break //give a chance to exit early - - Proj.on_hit(src) //on_hit just returns if it's argument is not a living mob so does this actually do anything? - return - -//This refer to whenever you are caught in an explosion. -/obj/mecha/ex_act(severity) - var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] - - var/temp_deflect_chance = deflect_chance - - if(!ArmC) - temp_deflect_chance = 0 - - else - temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) - - src.log_message("Affected by explosion of severity: [severity].",1) - if(prob(temp_deflect_chance)) - severity++ - src.log_append_to_last("Armor saved, changing severity to [severity].") - switch(severity) - if(1.0) - src.take_damage(initial(src.health), "bomb") - if(2.0) - if (prob(30)) - src.take_damage(initial(src.health), "bomb") - else - src.take_damage(initial(src.health)/2, "bomb") - src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) - if(3.0) - if (prob(5)) - qdel(src) - else - src.take_damage(initial(src.health)/5, "bomb") - src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) - return - -/*Will fix later -Sieve -/obj/mecha/attack_blob(mob/user as mob) - src.log_message("Attack by blob. Attacker - [user].",1) - if(!prob(src.deflect_chance)) - src.take_damage(6) - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - playsound(src, 'sound/effects/blobattack.ogg', 50, 1, -1) - to_chat(user, "You smash at the armored suit!") - for (var/mob/V in viewers(src)) - if(V.client && !(V.blinded)) - V.show_message("\The [user] smashes against [src.name]'s armor!", 1) - else - src.log_append_to_last("Armor saved.") - playsound(src, 'sound/effects/blobattack.ogg', 50, 1, -1) - to_chat(user, "Your attack had no effect!") - src.occupant_message("\The [user]'s attack is stopped by the armor.") - for (var/mob/V in viewers(src)) - if(V.client && !(V.blinded)) - V.show_message("\The [user] rebounds off the [src.name] armor!", 1) - return -*/ - -/obj/mecha/emp_act(severity) - if(get_charge()) - use_power((cell.charge/2)/severity) - take_damage(50 / severity,"energy") - src.log_message("EMP detected",1) - if(prob(80)) - check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) - return - -/obj/mecha/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature>src.max_temperature) - src.log_message("Exposed to dangerous temperature.",1) - src.take_damage(5,"fire") //The take_damage() proc handles armor values - src.check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL)) - return - -/obj/mecha/proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob) - user.setClickCooldown(user.get_attack_speed(W)) - src.log_message("Attacked by [W]. Attacker - [user]") - var/pass_damage_reduc_mod //Modifer for failing to bring AP. - - var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] - - var/temp_deflect_chance = deflect_chance - var/temp_damage_minimum = damage_minimum - var/temp_minimum_penetration = minimum_penetration - var/temp_fail_penetration_value = fail_penetration_value - - if(!ArmC) - temp_deflect_chance = 0 - temp_damage_minimum = 0 - temp_minimum_penetration = 0 - temp_fail_penetration_value = 1 - - else - temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) - temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) - temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) - temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value) - - if(prob(temp_deflect_chance)) //Does your attack get deflected outright. - src.occupant_message("\The [W] bounces off [src.name].") - to_chat(user, "\The [W] bounces off [src.name].") - src.log_append_to_last("Armor saved.") - - else if(W.force < temp_damage_minimum) //Is your attack too PATHETIC to do anything. 3 damage to a person shouldn't do anything to a mech. - src.occupant_message("\The [W] bounces off the armor.") - src.visible_message("\The [W] bounces off \the [src] armor") - return - - else if(W.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage - src.occupant_message("\The [W] struggles to bypass \the [src] armor.") - src.visible_message("\The [W] struggles to bypass \the [src] armor") - pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default - - else - pass_damage_reduc_mod = 1 //Just making sure. - src.occupant_message(span_red("[user] hits [src] with [W].")) - user.visible_message(span_red("[user] hits [src] with [W]."), span_red("You hit [src] with [W].")) - - var/pass_damage = W.force - pass_damage = (pass_damage*pass_damage_reduc_mod) //Apply the reduction of damage from not having enough armor penetration. This is not regular armor values at play. - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - pass_damage = ME.handle_projectile_contact(W, user, pass_damage) - src.take_damage(pass_damage,W.damtype) //The take_damage() proc handles armor values - if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - return - -////////////////////// -////// AttackBy ////// -////////////////////// - -/obj/mecha/attackby(obj/item/weapon/W as obj, mob/user as mob) - - if(istype(W, /obj/item/device/mmi)) - if(mmi_move_inside(W,user)) - to_chat(user, "[src]-MMI interface initialized successfuly") - else - to_chat(user, "[src]-MMI interface initialization failed.") - return - - if(istype(W, /obj/item/device/robotanalyzer)) - var/obj/item/device/robotanalyzer/RA = W - RA.do_scan(src, user) - return - - if(istype(W, /obj/item/mecha_parts/mecha_equipment)) - var/obj/item/mecha_parts/mecha_equipment/E = W - spawn() - if(E.can_attach(src)) - user.drop_item() - E.attach(src) - user.visible_message("[user] attaches [W] to [src]", "You attach [W] to [src]") - else - to_chat(user, "You were unable to attach [W] to [src]") - return - - if(istype(W, /obj/item/mecha_parts/component) && state == MECHA_CELL_OUT) - var/obj/item/mecha_parts/component/MC = W - spawn() - if(MC.attach(src)) - user.drop_item() - MC.forceMove(src) - user.visible_message("[user] installs \the [W] in \the [src]", "You install \the [W] in \the [src].") - return - - if(istype(W, /obj/item/weapon/card/robot)) - var/obj/item/weapon/card/robot/RoC = W - return attackby(RoC.dummy_card, user) - - if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) - if(add_req_access || maint_access) - if(internals_access_allowed(usr)) - var/obj/item/weapon/card/id/id_card - if(istype(W, /obj/item/weapon/card/id)) - id_card = W - else - var/obj/item/device/pda/pda = W - id_card = pda.id - output_maintenance_dialog(id_card, user) - return - else - to_chat(user, "Invalid ID: Access denied.") - else - to_chat(user, "Maintenance protocols disabled by operator.") - else if(W.has_tool_quality(TOOL_WRENCH)) - if(state==MECHA_BOLTS_SECURED) - state = MECHA_PANEL_LOOSE - to_chat(user, "You undo the securing bolts.") - else if(state==MECHA_PANEL_LOOSE) - state = MECHA_BOLTS_SECURED - to_chat(user, "You tighten the securing bolts.") - return - else if(W.has_tool_quality(TOOL_CROWBAR)) - if(state==MECHA_PANEL_LOOSE) - state = MECHA_CELL_OPEN - to_chat(user, "You open the hatch to the power unit") - else if(state==MECHA_CELL_OPEN) - state=MECHA_PANEL_LOOSE - to_chat(user, "You close the hatch to the power unit") - else if(state==MECHA_CELL_OUT) - var/list/removable_components = list() - for(var/slot in internal_components) - var/obj/item/mecha_parts/component/MC = internal_components[slot] - if(istype(MC)) - removable_components[MC.name] = MC - else - to_chat(user, "\The [src] appears to be missing \the [slot].") - - var/remove = tgui_input_list(user, "Which component do you want to pry out?", "Remove Component", removable_components) - if(!remove) - return - - var/obj/item/mecha_parts/component/RmC = removable_components[remove] - RmC.detach() - - return - else if(istype(W, /obj/item/stack/cable_coil)) - if(state >= MECHA_CELL_OPEN && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) - var/obj/item/stack/cable_coil/CC = W - if(CC.use(2)) - clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) - to_chat(user, "You replace the fused wires.") - else - to_chat(user, "There's not enough wire to finish the task.") - return - else if(W.has_tool_quality(TOOL_SCREWDRIVER)) - if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) - clearInternalDamage(MECHA_INT_TEMP_CONTROL) - to_chat(user, "You repair the damaged temperature controller.") - else if(state==MECHA_CELL_OPEN && src.cell) - src.cell.forceMove(src.loc) - src.cell = null - state = MECHA_CELL_OUT - to_chat(user, "You unscrew and pry out the powercell.") - src.log_message("Powercell removed") - else if(state==MECHA_CELL_OUT && src.cell) - state=MECHA_CELL_OPEN - to_chat(user, "You screw the cell in place") - return - - else if(istype(W, /obj/item/device/multitool)) - if(state>=MECHA_CELL_OPEN && src.occupant) - to_chat(user, "You attempt to eject the pilot using the maintenance controls.") - if(src.occupant.stat) - src.go_out() - src.log_message("[src.occupant] was ejected using the maintenance controls.") - else - to_chat(user, "Your attempt is rejected.") - src.occupant_message("An attempt to eject you was made using the maintenance controls.") - src.log_message("Eject attempt made using maintenance controls - rejected.") - return - - else if(istype(W, /obj/item/weapon/cell)) - if(state==MECHA_CELL_OUT) - if(!src.cell) - to_chat(user, "You install the powercell") - user.drop_item() - W.forceMove(src) - src.cell = W - src.log_message("Powercell installed") - else - to_chat(user, "There's already a powercell installed.") - return - - else if(W.has_tool_quality(TOOL_WELDER) && user.a_intent != I_HURT) - var/obj/item/weapon/weldingtool/WT = W.get_welder() - var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] - var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] - if (WT.remove_fuel(0,user)) - if (hasInternalDamage(MECHA_INT_TANK_BREACH)) - clearInternalDamage(MECHA_INT_TANK_BREACH) - to_chat(user, "You repair the damaged gas tank.") - else - return - if((src.healthYou repair some damage to [src.name].") - src.health += min(10, initial(src.health)-src.health) - update_damage_alerts() - else if(HC.integrityYou repair some damage to [HC.name].") - HC.integrity += min(10, HC.max_integrity-HC.integrity) - update_damage_alerts() - else if(AC.integrityYou repair some damage to [AC.name].") - AC.integrity += min(10, AC.max_integrity-AC.integrity) - update_damage_alerts() - - else - to_chat(user, "The [src.name] is at full integrity") - return - - else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) - user.drop_from_inventory(W) - W.forceMove(src) - user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src]") - return - - else if(istype(W,/obj/item/stack/nanopaste)) - if(state >= MECHA_PANEL_LOOSE) - var/obj/item/stack/nanopaste/NP = W - - for(var/slot in internal_components) - var/obj/item/mecha_parts/component/C = internal_components[slot] - - if(!C) - to_chat(user, "There are no components installed!") - return - - if(C.integrity >= C.max_integrity) - to_chat(user, "\The [C] does not require repairs.") - - else if(C.integrity < C.max_integrity) - to_chat(user, "You start to repair damage to \the [C].") - while(C.integrity < C.max_integrity && NP) - if(do_after(user, 1 SECOND, src)) - NP.use(1) - C.adjust_integrity(NP.mech_repair) - - if(C.integrity >= C.max_integrity) - to_chat(user, "You finish repairing \the [C].") - break - - else if(NP.amount == 0) - to_chat(user, "Insufficient nanopaste to complete repairs!") - break - return - - else - to_chat(user, "You can't reach \the [src]'s internal components.") - return - - else - call((proc_res["dynattackby"]||src), "dynattackby")(W,user) -/* - src.log_message("Attacked by [W]. Attacker - [user]") - if(prob(src.deflect_chance)) - to_chat(user, "\The [W] bounces off [src.name] armor.") - src.log_append_to_last("Armor saved.") -/* - for (var/mob/V in viewers(src)) - if(V.client && !(V.blinded)) - V.show_message("The [W] bounces off [src.name] armor.", 1) -*/ - else - src.occupant_message("[user] hits [src] with [W].") - user.visible_message("[user] hits [src] with [W].", "You hit [src] with [W].") - src.take_damage(W.force,W.damtype) - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) -*/ - return - - - -/* -/obj/mecha/attack_ai(var/mob/living/silicon/ai/user as mob) - if(!istype(user, /mob/living/silicon/ai)) - return - var/output = {"Assume direct control over [src]? - Yes
                    - "} - user << browse(output, "window=mecha_attack_ai") - return -*/ - -/////////////////////////////// -//////// Brain Stuff //////// -/////////////////////////////// - -/obj/mecha/proc/mmi_move_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) - if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) - to_chat(user, "Consciousness matrix not detected.") - return 0 - else if(mmi_as_oc.brainmob.stat) - to_chat(user, "Brain activity below acceptable level.") - return 0 - else if(occupant) - to_chat(user, "Occupant detected.") - return 0 - else if(dna && dna!=mmi_as_oc.brainmob.dna.unique_enzymes) - to_chat(user, "Genetic sequence or serial number incompatible with locking mechanism.") - return 0 - //Added a message here since people assume their first click failed or something./N -// to_chat(user, "Installing MMI, please stand by.") - - visible_message("[usr] starts to insert a brain into [src.name]") - - if(enter_after(40,user)) - if(!occupant) - return mmi_moved_inside(mmi_as_oc,user) - else - to_chat(user, "Occupant detected.") - else - to_chat(user, "You stop attempting to install the brain.") - return 0 - -/obj/mecha/proc/mmi_moved_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) - if(mmi_as_oc && (user in range(1))) - if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) - to_chat(user, "Consciousness matrix not detected.") - return 0 - else if(mmi_as_oc.brainmob.stat) - to_chat(user, "Beta-rhythm below acceptable level.") - return 0 - user.drop_from_inventory(mmi_as_oc) - var/mob/brainmob = mmi_as_oc.brainmob - brainmob.reset_view(src) - /* - brainmob.client.eye = src - brainmob.client.perspective = EYE_PERSPECTIVE - */ - occupant = brainmob - brainmob.loc = src //should allow relaymove - brainmob.canmove = 1 - mmi_as_oc.loc = src - mmi_as_oc.mecha = src - src.verbs += /obj/mecha/verb/eject - src.Entered(mmi_as_oc) - src.Move(src.loc) - update_icon() - set_dir(dir_in) - src.log_message("[mmi_as_oc] moved in as pilot.") - if(!hasInternalDamage()) - src.occupant << sound('sound/mecha/nominal.ogg',volume=50) - update_icon() - return 1 - else - return 0 - - -///////////////////////////////////// -//////// Atmospheric stuff //////// -///////////////////////////////////// - -/obj/mecha/proc/get_turf_air() - var/turf/T = get_turf(src) - if(T) - . = T.return_air() - return - -/obj/mecha/remove_air(amount) - if(use_internal_tank) - return cabin_air.remove(amount) - else - var/turf/T = get_turf(src) - if(T) - return T.remove_air(amount) - return - -/obj/mecha/return_air() - var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] - if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100))) - return cabin_air - return get_turf_air() - -/obj/mecha/proc/return_pressure() - . = 0 - var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] - if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100))) - . = cabin_air.return_pressure() - else - var/datum/gas_mixture/t_air = get_turf_air() - if(t_air) - . = t_air.return_pressure() - return - -//skytodo: //No idea what you want me to do here, mate. -/obj/mecha/proc/return_temperature() - . = 0 - var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] - if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100))) - . = cabin_air.temperature - else - var/datum/gas_mixture/t_air = get_turf_air() - if(t_air) - . = t_air.temperature - return - -/obj/mecha/proc/connect(obj/machinery/atmospherics/portables_connector/new_port) - //Make sure not already connected to something else - if(connected_port || !new_port || new_port.connected_device) - return 0 - - //Make sure are close enough for a valid connection - if(!(new_port.loc in locs)) - return 0 - - //Perform the connection - connected_port = new_port - connected_port.connected_device = src - - //Actually enforce the air sharing - var/datum/pipe_network/network = connected_port.return_network(src) - if(network && !(internal_tank.return_air() in network.gases)) - network.gases += internal_tank.return_air() - network.update = 1 - playsound(src, 'sound/mecha/gasconnected.ogg', 50, 1) - log_message("Connected to gas port.") - return 1 - -/obj/mecha/proc/disconnect() - if(!connected_port) - return 0 - - var/datum/pipe_network/network = connected_port.return_network(src) - if(network) - network.gases -= internal_tank.return_air() - - connected_port.connected_device = null - connected_port = null - playsound(src, 'sound/mecha/gasdisconnected.ogg', 50, 1) - src.log_message("Disconnected from gas port.") - return 1 - - -///////////////////////// -//////// Verbs //////// -///////////////////////// - - -/obj/mecha/verb/connect_to_port() - set name = "Connect to port" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - - if(!occupant) - return - - if(usr != occupant) - return - - var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] - if(!GC) - return - - for(var/turf/T in locs) - var/obj/machinery/atmospherics/portables_connector/possible_port = locate(/obj/machinery/atmospherics/portables_connector) in T - if(possible_port) - if(connect(possible_port)) - occupant_message("\The [name] connects to the port.") - verbs += /obj/mecha/verb/disconnect_from_port - verbs -= /obj/mecha/verb/connect_to_port - return - else - occupant_message("\The [name] failed to connect to the port.") - return - else - occupant_message("Nothing happens") - - -/obj/mecha/verb/disconnect_from_port() - set name = "Disconnect from port" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - - if(!occupant) - return - - if(usr != occupant) - return - - if(disconnect()) - occupant_message("[name] disconnects from the port.") - verbs -= /obj/mecha/verb/disconnect_from_port - verbs += /obj/mecha/verb/connect_to_port - else - occupant_message("[name] is not connected to the port at the moment.") - -/obj/mecha/verb/toggle_lights() - set name = "Toggle Lights" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - lights() - -/obj/mecha/verb/lights() - if(usr!=occupant) return - lights = !lights - if(lights) set_light(light_range + lights_power) - else set_light(light_range - lights_power) - src.occupant_message("Toggled lights [lights?"on":"off"].") - log_message("Toggled lights [lights?"on":"off"].") - playsound(src, 'sound/mecha/heavylightswitch.ogg', 50, 1) - return - - -/obj/mecha/verb/toggle_internal_tank() - set name = "Toggle internal airtank usage" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - internal_tank() - -/obj/mecha/proc/internal_tank() - if(usr!=src.occupant) - return - - var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] - if(!GC) - to_chat(occupant, "The life support systems don't seem to respond.") - return - - if(!prob(GC.get_efficiency() * 100)) - to_chat(occupant, "\The [GC] shudders and barks, before returning to how it was before.") - return - - use_internal_tank = !use_internal_tank - src.occupant_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].") - src.log_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].") - playsound(src, 'sound/mecha/gasdisconnected.ogg', 30, 1) - return - - -/obj/mecha/verb/toggle_strafing() - set name = "Toggle strafing" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - strafing() - -/obj/mecha/proc/strafing() - if(usr!=src.occupant) - return - strafing = !strafing - src.occupant_message("Toggled strafing mode [strafing?"on":"off"].") - src.log_message("Toggled strafing mode [strafing?"on":"off"].") - return - -/obj/mecha/MouseDrop_T(mob/O, mob/user as mob) - //Humans can pilot mechs. - if(!ishuman(O)) - return - - //Can't put other people into mechs (can comment this out if you want that to be possible) - if(O != user) - return - - move_inside() - -/obj/mecha/verb/enter() - set category = "Object" - set name = "Enter Exosuit" - set src in oview(1) - move_inside() - -//returns an equipment object if we have one of that type, useful since is_type_in_list won't return the object -//since is_type_in_list uses caching, this is a slower operation, so only use it if needed -/obj/mecha/proc/get_equipment(var/equip_type) - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - if(istype(ME,equip_type)) - return ME - return null - -/obj/mecha/proc/move_inside() - if (usr.stat || !ishuman(usr)) - return - - if (usr.buckled) - to_chat(usr, "You can't climb into the exosuit while buckled!") - return - - src.log_message("[usr] tries to move in.") - if(iscarbon(usr)) - var/mob/living/carbon/C = usr - if(C.handcuffed) - to_chat(usr, "Kinda hard to climb in while handcuffed don't you think?") - return - if (src.occupant) - to_chat(usr, "The [src.name] is already occupied!") - src.log_append_to_last("Permission denied.") - return -/* - if (usr.abiotic()) - to_chat(usr, "Subject cannot have abiotic items on.") - return -*/ - var/passed - if(src.dna) - if(usr.dna.unique_enzymes==src.dna) - passed = 1 - else if(src.operation_allowed(usr)) - passed = 1 - if(!passed) - to_chat(usr, "Access denied") - src.log_append_to_last("Permission denied.") - return - if(isliving(usr)) - var/mob/living/L = usr - if(L.has_buckled_mobs()) - to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first.")) - return - -// to_chat(usr, "You start climbing into [src.name]") - if(get_equipment(/obj/item/mecha_parts/mecha_equipment/runningboard)) - visible_message("\The [usr] is instantly lifted into [src.name] by the running board!") - moved_inside(usr) - if(ishuman(occupant)) - GrantActions(occupant, 1) - else - visible_message("\The [usr] starts to climb into [src.name]") - if(enter_after(40,usr)) - if(!src.occupant) - moved_inside(usr) - if(ishuman(occupant)) //Aeiou - GrantActions(occupant, 1) - else if(src.occupant!=usr) - to_chat(usr, "[src.occupant] was faster. Try better next time, loser.") - else - to_chat(usr, "You stop entering the exosuit.") - return - -/obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob) - if(H && H.client && (H in range(1))) - H.reset_view(src) - /* - H.client.perspective = EYE_PERSPECTIVE - H.client.eye = src - */ - H.stop_pulling() - H.forceMove(src) - src.occupant = H - src.add_fingerprint(H) - src.forceMove(src.loc) - src.verbs += /obj/mecha/verb/eject - src.log_append_to_last("[H] moved in as pilot.") - update_icon() - //VOREStation Edit Add - if(occupant.hud_used) - minihud = new (occupant.hud_used, src) - //VOREStation Edit Add End - -//This part removes all the verbs if you don't have them the _possible on your mech. This is a little clunky, but it lets you just add that to any mech. -//And it's not like this 10yo code wasn't clunky before. - - if(!smoke_possible) //Can't use smoke? No verb for you. - verbs -= /obj/mecha/verb/toggle_smoke - if(!thrusters_possible) //Can't use thrusters? No verb for you. - verbs -= /obj/mecha/verb/toggle_thrusters - if(!defence_mode_possible) //Do i need to explain everything? - verbs -= /obj/mecha/verb/toggle_defence_mode - if(!overload_possible) - verbs -= /obj/mecha/verb/toggle_overload - if(!zoom_possible) - verbs -= /obj/mecha/verb/toggle_zoom - if(!phasing_possible) - verbs -= /obj/mecha/verb/toggle_phasing - if(!switch_dmg_type_possible) - verbs -= /obj/mecha/verb/switch_damtype - if(!cloak_possible) - verbs -= /obj/mecha/verb/toggle_cloak - - occupant.in_enclosed_vehicle = 1 //Useful for when you need to know if someone is in a mecho. - update_cell_alerts() - update_damage_alerts() - set_dir(dir_in) - playsound(src, 'sound/machines/door/windowdoor.ogg', 50, 1) - if(occupant.client && cloaked_selfimage) - occupant.client.images += cloaked_selfimage - play_entered_noise(occupant) - return 1 - else - return 0 - -/obj/mecha/proc/play_entered_noise(var/mob/who) - if(!hasInternalDamage()) //Otherwise it's not nominal! - switch(mech_faction) - if(MECH_FACTION_NT)//The good guys category - if(firstactivation)//First time = long activation sound - firstactivation = 1 - who << sound('sound/mecha/LongNanoActivation.ogg',volume=50) - else - who << sound('sound/mecha/nominalnano.ogg',volume=50) - if(MECH_FACTION_SYNDI)//Bad guys - if(firstactivation) - firstactivation = 1 - who << sound('sound/mecha/LongSyndiActivation.ogg',volume=50) - else - who << sound('sound/mecha/nominalsyndi.ogg',volume=50) - else//Everyone else gets the normal noise - who << sound('sound/mecha/nominal.ogg',volume=50) - -/obj/mecha/AltClick(mob/living/user) - if(user == occupant) - strafing() - -/obj/mecha/verb/view_stats() - set name = "View Stats" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - if(usr!=src.occupant) - return - //pr_update_stats.start() - src.occupant << browse(src.get_stats_html(), "window=exosuit") - return - -/* -/obj/mecha/verb/force_eject() - set category = "Object" - set name = "Force Eject" - set src in view(5) - src.go_out() - return -*/ - -/obj/mecha/verb/eject() - set name = "Eject" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - if(usr!=src.occupant) - return - src.go_out() - add_fingerprint(usr) - return - - -/obj/mecha/proc/go_out() //Eject/Exit the mech. Yes this is for easier searching. - if(!src.occupant) return - var/atom/movable/mob_container - QDEL_NULL(minihud) - if(ishuman(occupant)) - mob_container = src.occupant - RemoveActions(occupant, human_occupant=1)//AEIOU - else if(istype(occupant, /mob/living/carbon/brain)) - var/mob/living/carbon/brain/brain = occupant - mob_container = brain.container - else - return - if(mob_container.forceMove(src.loc))//ejecting mob container - log_message("[mob_container] moved out.") - occupant.reset_view() - occupant << browse(null, "window=exosuit") - if(occupant.client && cloaked_selfimage) - occupant.client.images -= cloaked_selfimage - if(istype(mob_container, /obj/item/device/mmi)) - var/obj/item/device/mmi/mmi = mob_container - if(mmi.brainmob) - occupant.loc = mmi - mmi.mecha = null - occupant.canmove = 0 - occupant.clear_alert("charge") - occupant.clear_alert("mech damage") - occupant.in_enclosed_vehicle = 0 - occupant = null - update_icon() - set_dir(dir_in) - verbs -= /obj/mecha/verb/eject - - //src.zoom = 0 - - // Doesn't seem needed. - if(src.occupant && src.occupant.client) - src.occupant.client.view = world.view - src.zoom = 0 - - strafing = 0 - return - -///////////////////////// -////// Access stuff ///// -///////////////////////// - -/obj/mecha/proc/operation_allowed(mob/living/carbon/human/H) - for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt)) - if(src.check_access(ID,src.operation_req_access)) - return 1 - return 0 - - -/obj/mecha/proc/internals_access_allowed(mob/living/carbon/human/H) - if(istype(H)) - for(var/atom/ID in list(H.get_active_hand(), H.wear_id, H.belt)) - if(src.check_access(ID,src.internals_req_access)) - return 1 - else if(istype(H, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = H - if(src.check_access(R.idcard,src.internals_req_access)) - return 1 - return 0 - - -/obj/mecha/check_access(obj/item/weapon/card/id/I, list/access_list) - if(!istype(access_list)) - return 1 - if(!access_list.len) //no requirements - return 1 - if(istype(I, /obj/item/device/pda)) - var/obj/item/device/pda/pda = I - I = pda.id - if(!istype(I) || !I.access) //not ID or no access - return 0 - if(access_list==src.operation_req_access) - for(var/req in access_list) - if(!(req in I.access)) //doesn't have this access - return 0 - else if(access_list==src.internals_req_access) - for(var/req in access_list) - if(req in I.access) - return 1 - return 1 - - -//////////////////////////////////// -///// Rendering stats window /////// -//////////////////////////////////// - -/obj/mecha/proc/get_stats_html() - var/output = {" - [src.name] data - - - - -
                    - [src.get_stats_part()] -
                    -
                    - [src.get_equipment_list()] -
                    -
                    -
                    - [src.get_commands()] -
                    - - - "} - return output - - -/obj/mecha/proc/report_internal_damage() - var/output = null - var/list/dam_reports = list( - "[MECHA_INT_FIRE]" = "INTERNAL FIRE", - "[MECHA_INT_TEMP_CONTROL]" = "LIFE SUPPORT SYSTEM MALFUNCTION", - "[MECHA_INT_TANK_BREACH]" = "GAS TANK BREACH", - "[MECHA_INT_CONTROL_LOST]" = "COORDINATION SYSTEM CALIBRATION FAILURE - Recalibrate", - "[MECHA_INT_SHORT_CIRCUIT]" = "SHORT CIRCUIT" - ) - for(var/tflag in dam_reports) - var/intdamflag = text2num(tflag) - if(hasInternalDamage(intdamflag)) - output += dam_reports[tflag] - output += "
                    " - if(return_pressure() > WARNING_HIGH_PRESSURE) - output += "DANGEROUSLY HIGH CABIN PRESSURE
                    " - return output - - -/obj/mecha/proc/get_stats_part() - var/integrity = health/initial(health)*100 - var/cell_charge = get_charge() - var/tank_pressure = internal_tank ? round(internal_tank.return_pressure(),0.01) : "None" - var/tank_temperature = internal_tank ? internal_tank.return_temperature() : "Unknown" - var/cabin_pressure = round(return_pressure(),0.01) - - var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] - var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] - - var/output = {"[report_internal_damage()] - Armor Integrity: [AC?"[round(AC.integrity / AC.max_integrity * 100, 0.1)]%":"ARMOR MISSING"]
                    - Hull Integrity: [HC?"[round(HC.integrity / HC.max_integrity * 100, 0.1)]%":"HULL MISSING"]
                    - [integrity<30?"DAMAGE LEVEL CRITICAL
                    ":null] - Chassis Integrity: [integrity]%
                    - Powercell charge: [isnull(cell_charge)?"No powercell installed":"[cell.percent()]%"]
                    - Air source: [use_internal_tank?"Internal Airtank":"Environment"]
                    - Airtank pressure: [tank_pressure]kPa
                    - Airtank temperature: [tank_temperature]K|[tank_temperature - T0C]°C
                    - Cabin pressure: [cabin_pressure>WARNING_HIGH_PRESSURE ? "[cabin_pressure]": cabin_pressure]kPa
                    - Cabin temperature: [return_temperature()]K|[return_temperature() - T0C]°C
                    - Lights: [lights?"on":"off"]
                    - [src.dna?"DNA-locked:
                    [src.dna] \[Reset\]
                    ":null] - "} - - - if(defence_mode_possible) - output += "Defence mode: [defence_mode?"on":"off"]
                    " - if(overload_possible) - output += "Leg actuators overload: [overload?"on":"off"]
                    " - if(smoke_possible) - output += "Smoke: [smoke_reserve]
                    " - if(thrusters_possible) - output += "Thrusters: [thrusters?"on":"off"]
                    " - -//Cargo components. Keep this last otherwise it does weird alignment issues. - output += "Cargo Compartment Contents:
                    " - if(src.cargo.len) - for(var/obj/O in src.cargo) - output += "Unload : [O]
                    " - else - output += "Nothing" - output += "
                    " - return output - -/obj/mecha/proc/get_commands() - var/output = {"
                    -
                    Electronics
                    - -
                    -
                    -
                    Airtank
                    - -
                    - -
                    [get_equipment_menu()]
                    -
                    - [(/obj/mecha/verb/eject in src.verbs)?"Eject
                    ":null] - "} - return output - -/obj/mecha/proc/get_equipment_menu() //outputs mecha html equipment menu - var/output - if(equipment.len) - output += {"
                    -
                    Equipment
                    -
                    - "} - return output - -/obj/mecha/proc/get_equipment_list() //outputs mecha equipment list in html - if(!equipment.len) - return - var/output = "Equipment:
                    " - for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) - output += "
                    [MT.get_equip_info()]
                    " - output += "
                    " - return output - - -/obj/mecha/proc/get_log_html() - var/output = "[src.name] Log" - for(var/list/entry in log) - output += {"
                    [time2text(entry["time"],"DDD MMM DD hh:mm:ss")] [game_year]
                    -
                    [entry["message"]]
                    - "} - output += "" - return output - -/obj/mecha/proc/get_log_tgui() - var/list/data = list() - for(var/list/entry in log) - data.Add(list(list( - "time" = time2text(entry["time"], "DDD MMM DD hh:mm:ss"), - "year" = game_year, - "message" = entry["message"], - ))) - return data - - -/obj/mecha/proc/output_access_dialog(obj/item/weapon/card/id/id_card, mob/user) - if(!id_card || !user) return - var/output = {" - - - -

                    Following keycodes are present in this system:

                    "} - for(var/a in operation_req_access) - output += "[get_access_desc(a)] - Delete
                    " - output += "

                    Following keycodes were detected on portable device:

                    " - for(var/a in id_card.access) - if(a in operation_req_access) continue - var/a_name = get_access_desc(a) - if(!a_name) continue //there's some strange access without a name - output += "[a_name] - Add
                    " - output += "
                    Finish (Warning! The ID upload panel will be locked. It can be unlocked only through Exosuit Interface.)" - output += "" - user << browse(output, "window=exosuit_add_access") - onclose(user, "exosuit_add_access") - return - -/obj/mecha/proc/output_maintenance_dialog(obj/item/weapon/card/id/id_card,mob/user) - if(!id_card || !user) return - - var/maint_options = "Set Cabin Air Pressure" - if (locate(/obj/item/mecha_parts/mecha_equipment/tool/passenger) in contents) - maint_options += "Remove Passenger" - - var/output = {" - - - - - [add_req_access?"Edit operation keycodes":null] - [maint_access?"Initiate maintenance protocol":null] - [(state>0) ? maint_options : ""] - - "} - user << browse(output, "window=exosuit_maint_console") - onclose(user, "exosuit_maint_console") - return - - -//////////////////////////////// -/////// Messages and Log /////// -//////////////////////////////// - -/obj/mecha/proc/occupant_message(message as text) - if(message) - if(src.occupant && src.occupant.client) - to_chat(src.occupant, "\icon[src][bicon(src)] [message]") - return - -/obj/mecha/proc/log_message(message as text,red=null) - log.len++ - log[log.len] = list("time"=world.timeofday,"message"="[red?"":null][message][red?"":null]") - return log.len - -/obj/mecha/proc/log_append_to_last(message as text,red=null) - var/list/last_entry = src.log[src.log.len] - last_entry["message"] += "
                    [red?"":null][message][red?"":null]" - return - - -///////////////// -///// Topic ///// -///////////////// - -/obj/mecha/Topic(href, href_list) - ..() - if(href_list["update_content"]) - if(usr != src.occupant) return - send_byjax(src.occupant,"exosuit.browser","content",src.get_stats_part()) - return - if(href_list["close"]) - return - if(usr.stat > 0) - return - var/datum/topic_input/top_filter = new /datum/topic_input(href,href_list) - if(href_list["select_equip"]) - if(usr != src.occupant) return - var/obj/item/mecha_parts/mecha_equipment/equip = top_filter.getObj("select_equip") - if(equip) - src.selected = equip - src.occupant_message("You switch to [equip].") - src.visible_message("[src] raises [equip].") - send_byjax(src.occupant,"exosuit.browser","eq_list",src.get_equipment_list()) - return - if(href_list["eject"]) - if(usr != src.occupant) return - src.eject() - return - if(href_list["toggle_lights"]) - if(usr != src.occupant) return - src.lights() - return -/* - if(href_list["toggle_strafing"]) - if(usr != src.occupant) return - src.strafing() - return*/ - - if(href_list["toggle_airtank"]) - if(usr != src.occupant) return - src.internal_tank() - return - if (href_list["toggle_thrusters"]) - src.toggle_thrusters() - if (href_list["smoke"]) - src.smoke() - if (href_list["toggle_zoom"]) - src.zoom() - if(href_list["toggle_defence_mode"]) - src.defence_mode() - if(href_list["switch_damtype"]) - src.switch_damtype() - if(href_list["phasing"]) - src.phasing() - - if(href_list["rmictoggle"]) - if(usr != src.occupant) return - radio.broadcasting = !radio.broadcasting - send_byjax(src.occupant,"exosuit.browser","rmicstate",(radio.broadcasting?"Engaged":"Disengaged")) - return - if(href_list["rspktoggle"]) - if(usr != src.occupant) return - radio.listening = !radio.listening - send_byjax(src.occupant,"exosuit.browser","rspkstate",(radio.listening?"Engaged":"Disengaged")) - return - if(href_list["rfreq"]) - if(usr != src.occupant) return - var/new_frequency = (radio.frequency + top_filter.getNum("rfreq")) - if ((radio.frequency < PUBLIC_LOW_FREQ || radio.frequency > PUBLIC_HIGH_FREQ)) - new_frequency = sanitize_frequency(new_frequency) - radio.set_frequency(new_frequency) - send_byjax(src.occupant,"exosuit.browser","rfreq","[format_frequency(radio.frequency)]") - return - if(href_list["port_disconnect"]) - if(usr != src.occupant) return - src.disconnect_from_port() - return - if (href_list["port_connect"]) - if(usr != src.occupant) return - src.connect_to_port() - return - if (href_list["view_log"]) - if(usr != src.occupant) return - src.occupant << browse(src.get_log_html(), "window=exosuit_log") - onclose(occupant, "exosuit_log") - return - if (href_list["change_name"]) - if(usr != src.occupant) return - var/newname = sanitizeSafe(tgui_input_text(occupant,"Choose new exosuit name","Rename exosuit",initial(name), MAX_NAME_LEN), MAX_NAME_LEN) - if(newname) - name = newname - else - tgui_alert_async(occupant, "nope.avi") - return - if (href_list["toggle_id_upload"]) - if(usr != src.occupant) return - add_req_access = !add_req_access - send_byjax(src.occupant,"exosuit.browser","t_id_upload","[add_req_access?"L":"Unl"]ock ID upload panel") - return - if(href_list["toggle_maint_access"]) - if(usr != src.occupant) return - if(state) - occupant_message(span_red("Maintenance protocols in effect")) - return - maint_access = !maint_access - send_byjax(src.occupant,"exosuit.browser","t_maint_access","[maint_access?"Forbid":"Permit"] maintenance protocols") - return - if(href_list["req_access"] && add_req_access) - if(!in_range(src, usr)) return - output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user")) - return - if(href_list["maint_access"] && maint_access) - if(!in_range(src, usr)) return - var/mob/user = top_filter.getMob("user") - if(user) - if(state==MECHA_OPERATING) - state = MECHA_BOLTS_SECURED - to_chat(user, "The securing bolts are now exposed.") - else if(state==MECHA_BOLTS_SECURED) - state = MECHA_OPERATING - to_chat(user, "The securing bolts are now hidden.") - output_maintenance_dialog(top_filter.getObj("id_card"),user) - return - if(href_list["set_internal_tank_valve"] && state >=MECHA_BOLTS_SECURED) - if(!in_range(src, usr)) return - var/mob/user = top_filter.getMob("user") - if(user) - var/new_pressure = tgui_input_number(user,"Input new output pressure","Pressure setting",internal_tank_valve) - if(new_pressure) - internal_tank_valve = new_pressure - to_chat(user, "The internal pressure valve has been set to [internal_tank_valve]kPa.") - if(href_list["remove_passenger"] && state >= MECHA_BOLTS_SECURED) - var/mob/user = top_filter.getMob("user") - var/list/passengers = list() - for (var/obj/item/mecha_parts/mecha_equipment/tool/passenger/P in contents) - if (P.occupant) - passengers["[P.occupant]"] = P - - if (!passengers) - to_chat(user, "There are no passengers to remove.") - return - - var/pname = tgui_input_list(user, "Choose a passenger to forcibly remove.", "Forcibly Remove Passenger", passengers) - - if (!pname) - return - - var/obj/item/mecha_parts/mecha_equipment/tool/passenger/P = passengers[pname] - var/mob/occupant = P.occupant - - user.visible_message("\The [user] begins opening the hatch on \the [P]...", "You begin opening the hatch on \the [P]...") - if (!do_after(user, 40)) - return - - user.visible_message("\The [user] opens the hatch on \the [P] and removes [occupant]!", "You open the hatch on \the [P] and remove [occupant]!") - P.go_out() - P.log_message("[occupant] was removed.") - return - if(href_list["add_req_access"] && add_req_access && top_filter.getObj("id_card")) - if(!in_range(src, usr)) return - operation_req_access += top_filter.getNum("add_req_access") - output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user")) - return - if(href_list["del_req_access"] && add_req_access && top_filter.getObj("id_card")) - if(!in_range(src, usr)) return - operation_req_access -= top_filter.getNum("del_req_access") - output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user")) - return - if(href_list["finish_req_access"]) - if(!in_range(src, usr)) return - add_req_access = 0 - var/mob/user = top_filter.getMob("user") - user << browse(null,"window=exosuit_add_access") - return - if(href_list["dna_lock"]) - if(usr != src.occupant) return - if(istype(occupant, /mob/living/carbon/brain)) - occupant_message("You are a brain. No.") - return - if(src.occupant) - src.dna = src.occupant.dna.unique_enzymes - src.occupant_message("You feel a prick as the needle takes your DNA sample.") - return - if(href_list["reset_dna"]) - if(usr != src.occupant) return - src.dna = null - if(href_list["repair_int_control_lost"]) - if(usr != src.occupant) return - src.occupant_message("Recalibrating coordination system.") - src.log_message("Recalibration of coordination system started.") - var/T = src.loc - if(do_after(100)) - if(T == src.loc) - src.clearInternalDamage(MECHA_INT_CONTROL_LOST) - src.occupant_message(span_blue("Recalibration successful.")) - src.log_message("Recalibration of coordination system finished with 0 errors.") - else - src.occupant_message(span_red("Recalibration failed.")) - src.log_message("Recalibration of coordination system failed with 1 error.",1) - if(href_list["drop_from_cargo"]) - var/obj/O = locate(href_list["drop_from_cargo"]) - if(O && (O in src.cargo)) - src.occupant_message("You unload [O].") - O.forceMove(get_turf(src)) - src.cargo -= O - var/turf/T = get_turf(O) - if(T) - T.Entered(O) - src.log_message("Unloaded [O]. Cargo compartment capacity: [cargo_capacity - src.cargo.len]") - return - - //debug - /* - if(href_list["debug"]) - if(href_list["set_i_dam"]) - setInternalDamage(top_filter.getNum("set_i_dam")) - if(href_list["clear_i_dam"]) - clearInternalDamage(top_filter.getNum("clear_i_dam")) - return - */ - - - -/* - - if (href_list["ai_take_control"]) - var/mob/living/silicon/ai/AI = locate(href_list["ai_take_control"]) - var/duration = text2num(href_list["duration"]) - var/mob/living/silicon/ai/O = new /mob/living/silicon/ai(src) - var/cur_occupant = src.occupant - O.invisibility = 0 - O.canmove = 1 - O.name = AI.name - O.real_name = AI.real_name - O.anchored = TRUE - O.aiRestorePowerRoutine = 0 - O.control_disabled = 1 // Can't control things remotely if you're stuck in a card! - O.laws = AI.laws - O.set_stat(AI.stat) - O.oxyloss = AI.getOxyLoss() - O.fireloss = AI.getFireLoss() - O.bruteloss = AI.getBruteLoss() - O.toxloss = AI.toxloss - O.updatehealth() - src.occupant = O - if(AI.mind) - AI.mind.transfer_to(O) - AI.name = "Inactive AI" - AI.real_name = "Inactive AI" - AI.icon_state = "ai-empty" - spawn(duration) - AI.name = O.name - AI.real_name = O.real_name - if(O.mind) - O.mind.transfer_to(AI) - AI.control_disabled = 0 - AI.laws = O.laws - AI.oxyloss = O.getOxyLoss() - AI.fireloss = O.getFireLoss() - AI.bruteloss = O.getBruteLoss() - AI.toxloss = O.toxloss - AI.updatehealth() - qdel(O) - if (!AI.stat) - AI.icon_state = "ai" - else - AI.icon_state = "ai-crash" - src.occupant = cur_occupant -*/ - -/////////////////////// -///// Power stuff ///// -/////////////////////// - -/obj/mecha/proc/has_charge(amount) - return (get_charge()>=amount) - -/obj/mecha/proc/get_charge() - return call((proc_res["dyngetcharge"]||src), "dyngetcharge")() - -/obj/mecha/proc/dyngetcharge()//returns null if no powercell, else returns cell.charge - if(!src.cell) return - return max(0, src.cell.charge) - -/obj/mecha/proc/use_power(amount) - return call((proc_res["dynusepower"]||src), "dynusepower")(amount) - -/obj/mecha/proc/dynusepower(amount) - update_cell_alerts() - var/obj/item/mecha_parts/component/electrical/EC = internal_components[MECH_ELECTRIC] - - if(EC) - amount = amount * (2 - EC.get_efficiency()) * EC.charge_cost_mod - else - amount *= 5 - - if(get_charge()) - cell.use(amount) - return 1 - return 0 - -/obj/mecha/proc/give_power(amount) - update_cell_alerts() - var/obj/item/mecha_parts/component/electrical/EC = internal_components[MECH_ELECTRIC] - - if(!EC) - amount /= 4 - else - amount *= EC.get_efficiency() - - if(!isnull(get_charge())) - cell.give(amount) - return 1 - return 0 - -//This is for mobs mostly. -/obj/mecha/attack_generic(var/mob/user, var/damage, var/attack_message) - - var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] - - var/temp_deflect_chance = deflect_chance - var/temp_damage_minimum = damage_minimum - - if(!ArmC) - temp_deflect_chance = 1 - temp_damage_minimum = 0 - - else - temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) - temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) - - user.setClickCooldown(user.get_attack_speed()) - if(!damage) - return 0 - - src.log_message("Attacked. Attacker - [user].",1) - user.do_attack_animation(src) - - if(prob(temp_deflect_chance))//Deflected - src.log_append_to_last("Armor saved.") - src.occupant_message("\The [user]'s attack is stopped by the armor.") - visible_message("\The [user] rebounds off [src.name]'s armor!") - user.attack_log += text("\[[time_stamp()]\] [span_red("attacked [src.name]")]") - playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) - - else if(damage < temp_damage_minimum)//Pathetic damage levels just don't harm MECH. - src.occupant_message("\The [user]'s doesn't dent \the [src] paint.") - src.visible_message("\The [user]'s attack doesn't dent \the [src] armor") - src.log_append_to_last("Armor saved.") - playsound(src, 'sound/effects/Glasshit.ogg', 50, 1) - return - - else - src.take_damage(damage) //Apply damage - The take_damage() proc handles armor values - if(damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - visible_message("[user] [attack_message] [src]!") - user.attack_log += text("\[[time_stamp()]\] [span_red("attacked [src.name]")]") - - return 1 - - -///////////////////////////////////////// -//////// Mecha process() helpers //////// -///////////////////////////////////////// -/obj/mecha/proc/stop_process(process) - current_processes &= ~process - -/obj/mecha/proc/start_process(process) - current_processes |= process - - -///////////// -/obj/mecha/cloak() - . = ..() - if(occupant && occupant.client && cloaked_selfimage) - occupant.client.images += cloaked_selfimage - -/obj/mecha/uncloak() - if(occupant && occupant.client && cloaked_selfimage) - occupant.client.images -= cloaked_selfimage - return ..() - - -//debug -/* -/obj/mecha/verb/test_int_damage() - set name = "Test internal damage" - set category = "Exosuit Interface" - set src in view(0) - if(!occupant) return - if(usr!=occupant) - return - var/output = {" - - - -

                    Set:

                    - MECHA_INT_FIRE
                    - MECHA_INT_TEMP_CONTROL
                    - MECHA_INT_SHORT_CIRCUIT
                    - MECHA_INT_TANK_BREACH
                    - MECHA_INT_CONTROL_LOST
                    -
                    -

                    Clear:

                    - MECHA_INT_FIRE
                    - MECHA_INT_TEMP_CONTROL
                    - MECHA_INT_SHORT_CIRCUIT
                    - MECHA_INT_TANK_BREACH
                    - MECHA_INT_CONTROL_LOST
                    - - "} - - occupant << browse(output, "window=ex_debug") - //src.health = initial(src.health)/2.2 - //src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - return -*/ - -/obj/mecha/proc/update_cell_alerts() - if(occupant && cell) - var/cellcharge = cell.charge/cell.maxcharge - switch(cellcharge) - if(0.75 to INFINITY) - occupant.clear_alert("charge") - if(0.5 to 0.75) - occupant.throw_alert("charge", /obj/screen/alert/lowcell, 1) - if(0.25 to 0.5) - occupant.throw_alert("charge", /obj/screen/alert/lowcell, 2) - if(0.01 to 0.25) - occupant.throw_alert("charge", /obj/screen/alert/lowcell, 3) - else - occupant.throw_alert("charge", /obj/screen/alert/emptycell) - -/obj/mecha/proc/update_damage_alerts() - if(occupant) - var/integrity = health/initial(health)*100 - switch(integrity) - if(30 to 45) - occupant.throw_alert("mech damage", /obj/screen/alert/low_mech_integrity, 1) - if(15 to 35) - occupant.throw_alert("mech damage", /obj/screen/alert/low_mech_integrity, 2) - if(-INFINITY to 15) - occupant.throw_alert("mech damage", /obj/screen/alert/low_mech_integrity, 3) - else - occupant.clear_alert("mech damage") - -/obj/mecha/blob_act(var/obj/structure/blob/B) - var/datum/blob_type/blob = B?.overmind?.blob_type - if(!istype(blob)) - return FALSE - - var/damage = rand(blob.damage_lower, blob.damage_upper) - src.take_damage(damage, blob.damage_type) - visible_message("\The [B] [blob.attack_verb] \the [src]!", "[blob.attack_message_synth]!") - playsound(src, 'sound/effects/attackblob.ogg', 50, 1) - - return ..() +#define MECHA_INT_FIRE 1 +#define MECHA_INT_TEMP_CONTROL 2 +#define MECHA_INT_SHORT_CIRCUIT 4 +#define MECHA_INT_TANK_BREACH 8 +#define MECHA_INT_CONTROL_LOST 16 + +#define MECHA_PROC_MOVEMENT 1 +#define MECHA_PROC_DAMAGE 2 +#define MECHA_PROC_INT_TEMP 4 + +#define MELEE 1 +#define RANGED 2 + +#define MECHA_OPERATING 0 +#define MECHA_BOLTS_SECURED 1 +#define MECHA_PANEL_LOOSE 2 +#define MECHA_CELL_OPEN 3 +#define MECHA_CELL_OUT 4 + +#define MECH_FACTION_NT "nano" +#define MECH_FACTION_SYNDI "syndi" +#define MECH_FACTION_NONE "none" + +/obj/mecha + name = "Mecha" + desc = "Exosuit" + description_info = "Alt click to strafe." + icon = 'icons/mecha/mecha.dmi' + density = TRUE //Dense. To raise the heat. + opacity = 1 //Opaque. Menacing. + anchored = TRUE //No pulling around. + unacidable = TRUE //And no deleting hoomans inside + layer = MOB_LAYER //Icon draw layer + infra_luminosity = 15 //Byond implementation is bugged. + var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items) + var/can_move = 1 + var/mob/living/carbon/occupant = null + + var/step_in = 10 //Make a step in step_in/10 sec. + var/encumbrance_gap = 1 //How many points of slowdown are negated from equipment? Added to the mech's base step_in. + + var/dir_in = 2 //What direction will the mech face when entered/powered on? Defaults to South. + var/step_energy_drain = 10 + var/health = 300 //Health is health + var/maxhealth = 300 //Maxhealth is maxhealth. + var/deflect_chance = 10 //Chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. + + var/damage_minimum = 10 //Incoming damage lower than this won't actually deal damage. Scrapes shouldn't be a real thing. + var/minimum_penetration = 15 //Incoming damage won't be fully applied if you don't have at least 20. Almost all AP clears this. + var/fail_penetration_value = 0.66 //By how much failing to penetrate reduces your shit. 66% by default. 100dmg = 66dmg if failed pen + + var/obj/item/weapon/cell/cell + var/state = MECHA_OPERATING + var/list/log = new + var/last_message = 0 + var/add_req_access = 1 + var/maint_access = 1 + var/dna //Dna-locking the mech + var/list/proc_res = list() //Stores proc owners, like proc_res["functionname"] = owner reference + var/datum/effect/effect/system/spark_spread/spark_system = new + var/lights = 0 + var/lights_power = 6 + var/force = 0 + + var/mech_faction = null + var/firstactivation = 0 //It's simple. If it's 0, no one entered it yet. Otherwise someone entered it at least once. + + var/stomp_sound = 'sound/mecha/mechstep.ogg' + var/swivel_sound = 'sound/mecha/mechturn.ogg' + + //inner atmos + var/use_internal_tank = 0 + var/internal_tank_valve = ONE_ATMOSPHERE + var/obj/machinery/portable_atmospherics/canister/internal_tank + var/datum/gas_mixture/cabin_air + var/obj/machinery/atmospherics/portables_connector/connected_port = null + + var/obj/item/device/radio/radio = null + + var/max_temperature = 25000 //Kelvin values. + var/internal_damage_threshold = 33 //Health percentage below which internal damage is possible + var/internal_damage_minimum = 15 //At least this much damage to trigger some real bad hurt. + var/internal_damage = 0 //Contains bitflags + + var/list/operation_req_access = list() //Required access level for mecha operation + var/list/internals_req_access = list(access_engine,access_robotics) //Required access level to open cell compartment + + var/wreckage + + var/list/equipment = new //This lists holds what stuff you bolted onto your baby ride + var/obj/item/mecha_parts/mecha_equipment/selected + var/max_equip = 2 + + // What direction to float in, if inertial movement is active. + var/float_direction = 0 + // Process() iterator count. + var/process_ticks = 0 + // These control what toggleable processes are executed within process(). + var/current_processes = MECHA_PROC_INT_TEMP + +//mechaequipt2 stuffs + var/list/hull_equipment = new + var/list/weapon_equipment = new + var/list/utility_equipment = new + var/list/universal_equipment = new + var/list/special_equipment = new + var/max_hull_equip = 2 + var/max_weapon_equip = 2 + var/max_utility_equip = 2 + var/max_universal_equip = 2 + var/max_special_equip = 1 + + var/list/starting_equipment = null // List containing starting tools. + +// Mech Components, similar to Cyborg, but Bigger. + var/list/internal_components = list( + MECH_HULL = null, + MECH_ACTUATOR = null, + MECH_ARMOR = null, + MECH_GAS = null, + MECH_ELECTRIC = null + ) + var/list/starting_components = list( + /obj/item/mecha_parts/component/hull, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + +//Working exosuit vars + var/list/cargo = list() + var/cargo_capacity = 3 + + var/static/image/radial_image_eject = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject") + var/static/image/radial_image_airtoggle = image(icon= 'icons/mob/radial.dmi', icon_state = "radial_airtank") + var/static/image/radial_image_lighttoggle = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_light") + var/static/image/radial_image_statpanel = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_examine2") + +//Mech actions + var/datum/mini_hud/mech/minihud //VOREStation Edit + var/strafing = 0 //Are we strafing or not? + + var/defence_mode_possible = 0 //Can we even use defence mode? This is used to assign it to mechs and check for verbs. + var/defence_mode = 0 //Are we in defence mode + var/defence_deflect = 35 //How much it deflect + + var/overload_possible = 0 //Same as above. Don't forget to GRANT the verb&actions if you want everything to work proper. + var/overload = 0 //Are our legs overloaded + var/overload_coeff = 1 //How much extra energy you use when use the L E G + + var/zoom = 0 + var/zoom_possible = 0 + + var/thrusters = 0 + var/thrusters_possible = 0 + + var/phasing = 0 //Are we currently phasing + var/phasing_possible = 0 //This is to allow phasing. + var/can_phase = TRUE //This is an internal check during the relevant procs. + var/phasing_energy_drain = 200 + + var/switch_dmg_type_possible = 0 //Can you switch damage type? It is mostly for the Phazon and its children. + + var/smoke_possible = 0 + var/smoke_reserve = 5 //How many shots you have. Might make a reload later on. MIGHT. + var/smoke_ready = 1 //This is a check for the whether or not the cooldown is ongoing. + var/smoke_cooldown = 100 //How long you have between uses. + var/datum/effect/effect/system/smoke_spread/smoke_system = new + + var/cloak_possible = FALSE // Can this exosuit innately cloak? + +////All of those are for the HUD buttons in the top left. See Grant and Remove procs in mecha_actions. + + var/datum/action/innate/mecha/mech_eject/eject_action = new + var/datum/action/innate/mecha/mech_toggle_internals/internals_action = new + var/datum/action/innate/mecha/mech_toggle_lights/lights_action = new + var/datum/action/innate/mecha/mech_view_stats/stats_action = new + var/datum/action/innate/mecha/strafe/strafing_action = new + + var/datum/action/innate/mecha/mech_defence_mode/defence_action = new + var/datum/action/innate/mecha/mech_overload_mode/overload_action = new + var/datum/action/innate/mecha/mech_smoke/smoke_action = new + var/datum/action/innate/mecha/mech_zoom/zoom_action = new + var/datum/action/innate/mecha/mech_toggle_thrusters/thrusters_action = new + var/datum/action/innate/mecha/mech_cycle_equip/cycle_action = new + var/datum/action/innate/mecha/mech_switch_damtype/switch_damtype_action = new + var/datum/action/innate/mecha/mech_toggle_phasing/phasing_action = new + var/datum/action/innate/mecha/mech_toggle_cloaking/cloak_action = new + + var/weapons_only_cycle = FALSE //So combat mechs don't switch to their equipment at times. + +/obj/mecha/Initialize() + . = ..() + + for(var/path in starting_components) + var/obj/item/mecha_parts/component/C = new path(src) + C.attach(src) + + if(starting_equipment && LAZYLEN(starting_equipment)) + for(var/path in starting_equipment) + var/obj/item/mecha_parts/mecha_equipment/ME = new path(src) + ME.attach(src) + + START_PROCESSING(SSobj, src) + + update_transform() + +/obj/mecha/drain_power(var/drain_check) + + if(drain_check) + return 1 + + if(!cell) + return 0 + + return cell.drain_power(drain_check) + +/obj/mecha/New() + ..() + icon_state += "-open" + add_radio() + add_cabin() + if(!add_airtank()) //we check this here in case mecha does not have an internal tank available by default - WIP + removeVerb(/obj/mecha/verb/connect_to_port) + removeVerb(/obj/mecha/verb/toggle_internal_tank) + + spark_system.set_up(2, 0, src) + spark_system.attach(src) + + if(smoke_possible)//I am pretty sure that's needed here. + src.smoke_system.set_up(3, 0, src) + src.smoke_system.attach(src) + + add_cell() + removeVerb(/obj/mecha/verb/disconnect_from_port) + log_message("[src.name] created.") + loc.Entered(src) + mechas_list += src //global mech list + return + +/obj/mecha/Exit(atom/movable/O) + if(O in cargo) + return 0 + return ..() + +/obj/mecha/Destroy() + src.go_out() + for(var/mob/M in src) //Be Extra Sure + M.forceMove(get_turf(src)) + M.loc.Entered(M) + if(M != src.occupant) + step_rand(M) + for(var/atom/movable/A in src.cargo) + A.forceMove(get_turf(src)) + var/turf/T = get_turf(A) + if(T) + T.Entered(A) + step_rand(A) + + if(loc) + loc.Exited(src) + + if(prob(30)) + explosion(get_turf(loc), 0, 0, 1, 3) + + if(wreckage) + var/obj/effect/decal/mecha_wreckage/WR = new wreckage(loc) + hull_equipment.Cut() + weapon_equipment.Cut() + utility_equipment.Cut() + universal_equipment.Cut() + special_equipment.Cut() + for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + if(E.salvageable && prob(30)) + WR.crowbar_salvage += E + E.forceMove(WR) + E.equip_ready = TRUE + else + E.forceMove(loc) + E.destroy() + + for(var/slot in internal_components) + var/obj/item/mecha_parts/component/C = internal_components[slot] + if(istype(C)) + C.damage_part(rand(10, 20)) + C.detach() + WR.crowbar_salvage += C + C.forceMove(WR) + + if(cell) + WR.crowbar_salvage += cell + cell.forceMove(WR) + cell.charge = rand(0, cell.charge) + if(internal_tank) + WR.crowbar_salvage += internal_tank + internal_tank.forceMove(WR) + else + for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + E.detach(loc) + E.destroy() + for(var/slot in internal_components) + var/obj/item/mecha_parts/component/C = internal_components[slot] + if(istype(C)) + C.detach() + qdel(C) + if(cell) + qdel(cell) + if(internal_tank) + qdel(internal_tank) + equipment.Cut() + cell = null + internal_tank = null + + if(smoke_possible) //Just making sure nothing is running. + qdel(smoke_system) + + GLOB.mech_destroyed_roundstat++ + + QDEL_NULL(spark_system) + QDEL_NULL(minihud) + + STOP_PROCESSING(SSobj, src) + + mechas_list -= src //global mech list + . = ..() + +// The main process loop to replace the ancient global iterators. +// It's a bit hardcoded but I don't see anyone else adding stuff to +// mechas, and it's easy enough to modify. +/obj/mecha/process() + var/static/max_ticks = 16 + + if (current_processes & MECHA_PROC_MOVEMENT) + process_inertial_movement() + + if ((current_processes & MECHA_PROC_DAMAGE) && !(process_ticks % 2)) + process_internal_damage() + + if ((current_processes & MECHA_PROC_INT_TEMP) && !(process_ticks % 4)) + process_preserve_temp() + + if (!(process_ticks % 3)) + process_tank_give_air() + + // Max value is 16. So we let it run between [0, 16] with this. + process_ticks = (process_ticks + 1) % 17 + +// Normalizing cabin air temperature to 20 degrees celsius. +// Called every fourth process() tick (20 deciseconds). +/obj/mecha/proc/process_preserve_temp() + if (cabin_air && cabin_air.volume > 0) + var/delta = cabin_air.temperature - T20C + cabin_air.temperature -= max(-10, min(10, round(delta/4,0.1))) + +// Handles internal air tank action. +// Called every third process() tick (15 deciseconds). +/obj/mecha/proc/process_tank_give_air() + if(internal_tank) + var/datum/gas_mixture/tank_air = internal_tank.return_air() + + var/release_pressure = internal_tank_valve + var/cabin_pressure = cabin_air.return_pressure() + var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2) + var/transfer_moles = 0 + + if(pressure_delta > 0) //cabin pressure lower than release pressure + if(tank_air.temperature > 0) + transfer_moles = pressure_delta*cabin_air.volume/(cabin_air.temperature * R_IDEAL_GAS_EQUATION) + var/datum/gas_mixture/removed = tank_air.remove(transfer_moles) + cabin_air.merge(removed) + + else if(pressure_delta < 0) //cabin pressure higher than release pressure + var/datum/gas_mixture/t_air = get_turf_air() + pressure_delta = cabin_pressure - release_pressure + + if(t_air) + pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta) + if(pressure_delta > 0) //if location pressure is lower than cabin pressure + transfer_moles = pressure_delta*cabin_air.volume/(cabin_air.temperature * R_IDEAL_GAS_EQUATION) + + var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles) + if(t_air) + t_air.merge(removed) + else //just delete the cabin gas, we're in space or some shit + qdel(removed) + +// Inertial movement in space. +// Called every process() tick (5 deciseconds). +/obj/mecha/proc/process_inertial_movement() + if(float_direction) + if(!step(src, float_direction) || check_for_support()) + stop_process(MECHA_PROC_MOVEMENT) + else + stop_process(MECHA_PROC_MOVEMENT) + return + +// Processes internal damage. +// Called every other process() tick (10 deciseconds). +/obj/mecha/proc/process_internal_damage() + if(!hasInternalDamage()) + stop_process(MECHA_PROC_DAMAGE) + return + + if(hasInternalDamage(MECHA_INT_FIRE)) + if(!hasInternalDamage(MECHA_INT_TEMP_CONTROL) && prob(5)) + clearInternalDamage(MECHA_INT_FIRE) + if(internal_tank) + if(internal_tank.return_pressure()>internal_tank.maximum_pressure && !(hasInternalDamage(MECHA_INT_TANK_BREACH))) + setInternalDamage(MECHA_INT_TANK_BREACH) + var/datum/gas_mixture/int_tank_air = internal_tank.return_air() + if(int_tank_air && int_tank_air.volume>0) //heat the air_contents + int_tank_air.temperature = min(6000+T0C, int_tank_air.temperature+rand(10,15)) + if(cabin_air && cabin_air.volume>0) + cabin_air.temperature = min(6000+T0C, cabin_air.temperature+rand(10,15)) + if(cabin_air.temperature>max_temperature/2) + take_damage(4/round(max_temperature/cabin_air.temperature,0.1),"fire") + + if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) + stop_process(MECHA_PROC_INT_TEMP) + + if(hasInternalDamage(MECHA_INT_TANK_BREACH)) //remove some air from internal tank + if(internal_tank) + var/datum/gas_mixture/int_tank_air = internal_tank.return_air() + var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10) + if(istype(loc, /turf/simulated)) + loc.assume_air(leaked_gas) + else + qdel(leaked_gas) + + if(hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) + if(get_charge()) + spark_system.start() + cell.charge -= min(20,cell.charge) + cell.maxcharge -= min(20,cell.maxcharge) + return + +//////////////////////// +////// Helpers ///////// +//////////////////////// + +/obj/mecha/proc/removeVerb(verb_path) + verbs -= verb_path + +/obj/mecha/proc/addVerb(verb_path) + verbs += verb_path + +/obj/mecha/proc/add_airtank() + internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src) + return internal_tank + +/obj/mecha/proc/add_cell(var/obj/item/weapon/cell/C=null) + if(C) + C.forceMove(src) + cell = C + return + cell = new /obj/item/weapon/cell/mech(src) + +/obj/mecha/get_cell() + return cell + +/obj/mecha/proc/add_cabin() + cabin_air = new + cabin_air.temperature = T20C + cabin_air.volume = 200 + cabin_air.adjust_multi("oxygen", O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature), "nitrogen", N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature)) + return cabin_air + +/obj/mecha/proc/add_radio() + radio = new(src) + radio.name = "[src] radio" + radio.icon = icon + radio.icon_state = icon_state + radio.subspace_transmission = 1 + +/obj/mecha/proc/do_after(delay as num) + sleep(delay) + if(src) + return 1 + return 0 + +/obj/mecha/proc/enter_after(delay as num, var/mob/user as mob, var/numticks = 5) + var/delayfraction = delay/numticks + + var/turf/T = user.loc + + for(var/i = 0, iInterfacing with [target].") + src.log_message("Interfaced with [target].") + target.attack_hand(src.occupant) + return 1 + if(istype(target, /obj/machinery/embedded_controller)) + target.tgui_interact(src.occupant) + return 1 + return 0 + +/obj/mecha/contents_tgui_distance(var/src_object, var/mob/living/user) + . = user.shared_living_tgui_distance(src_object) //allow them to interact with anything they can interact with normally. + if(. != STATUS_INTERACTIVE) + //Allow interaction with the mecha or anything that is part of the mecha + if(src_object == src || (src_object in src)) + return STATUS_INTERACTIVE + if(src.Adjacent(src_object)) + src.occupant_message("Interfacing with [src_object]...") + src.log_message("Interfaced with [src_object].") + return STATUS_INTERACTIVE + if(src_object in view(2, src)) + return STATUS_UPDATE //if they're close enough, allow the occupant to see the screen through the viewport or whatever. + +/obj/mecha/proc/melee_action(atom/target) + return + +/obj/mecha/proc/range_action(atom/target) + return + + +////////////////////////////////// +//////// Movement procs //////// +////////////////////////////////// + +/obj/mecha/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + MoveAction() + +/obj/mecha/proc/MoveAction() //Allows mech equipment to do an action once the mech moves + if(!equipment.len) + return + + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + ME.MoveAction() + +/obj/mecha/relaymove(mob/user,direction) + if(user != src.occupant) //While not "realistic", this piece is player friendly. + if(istype(user,/mob/living/carbon/brain)) + to_chat(user, "You try to move, but you are not the pilot! The exosuit doesn't respond.") + return 0 + user.forceMove(get_turf(src)) + to_chat(user, "You climb out from [src]") + return 0 + + var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] + if(!HC) + occupant_message("You can't operate an exosuit that doesn't have a hull!") + return + + if(connected_port) + if(world.time - last_message > 20) + src.occupant_message("Unable to move while connected to the air system port") + last_message = world.time + return 0 + if(state) + occupant_message("Maintenance protocols in effect") + return +/* + if(zoom) + if(world.time - last_message > 20) + src.occupant_message("Unable to move while in zoom mode.") + last_message = world.time + return 0 +*/ + return domove(direction) + +/obj/mecha/proc/can_ztravel() + for(var/obj/item/mecha_parts/mecha_equipment/tool/jetpack/jp in equipment) + return jp.equip_ready + return FALSE + +/obj/mecha/proc/domove(direction) + + return call((proc_res["dyndomove"]||src), "dyndomove")(direction) + +/obj/mecha/proc/get_step_delay() + var/tally = 0 + + if(LAZYLEN(equipment)) + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + if(ME.get_step_delay()) + tally += ME.get_step_delay() + + if(tally <= encumbrance_gap) // If the total is less than our encumbrance gap, ignore equipment weight. + tally = 0 + else // Otherwise, start the tally after cutting that gap out. + tally -= encumbrance_gap + + for(var/slot in internal_components) + var/obj/item/mecha_parts/component/C = internal_components[slot] + if(C && C.get_step_delay()) + tally += C.get_step_delay() + + var/obj/item/mecha_parts/component/actuator/actuator = internal_components[MECH_ACTUATOR] + + if(!actuator) // Relying purely on hydraulic pumps. You're going nowhere fast. + tally = 2 SECONDS + + return tally + + tally += 0.5 SECONDS * (1 - actuator.get_efficiency()) // Damaged actuators run slower, slowing as damage increases beyond its threshold. + + if(strafing) + tally = round(tally * actuator.strafing_multiplier) + + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + if(istype(ME, /obj/item/mecha_parts/mecha_equipment/speedboost)) + var/obj/item/mecha_parts/mecha_equipment/speedboost/SB = ME + for(var/path in ME.required_type) + if(istype(src, path)) + tally = round(tally * SB.slowdown_multiplier) + break + break + + if(overload) // At the end, because this would normally just make the mech *slower* since tally wasn't starting at 0. + tally = min(1, round(tally/2)) + + return max(1, round(tally, 0.1)) // Round the total to the nearest 10th. Can't go lower than 1 tick. Even humans have a delay longer than that. + +/obj/mecha/proc/dyndomove(direction) + if(!can_move) + return 0 + if(current_processes & MECHA_PROC_MOVEMENT) + return 0 + if(!has_charge(step_energy_drain)) + return 0 + + //Can we even move, below is if yes. + + if(defence_mode)//Check if we are currently locked down + if(world.time - last_message > 20) + src.occupant_message(span_red("Unable to move while in defence mode")) + last_message = world.time + return 0 + + if(zoom)//:eyes: + if(world.time - last_message > 20) + src.occupant_message("Unable to move while in zoom mode.") + last_message = world.time + return 0 + + if(!thrusters && (current_processes & MECHA_PROC_MOVEMENT)) //I think this mean 'if you try to move in space without thruster, u no move' + return 0 + + if(overload)//Check if you have leg overload + health-- + if(health < initial(health) - initial(health)/3) + overload = 0 + step_energy_drain = initial(step_energy_drain) + src.occupant_message(span_red("Leg actuators damage threshold exceded. Disabling overload.")) + + + var/move_result = 0 + + if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) + move_result = mechsteprand() + //Up/down zmove + else if(direction == UP || direction == DOWN) + if(!can_ztravel()) + occupant_message("Your vehicle lacks the capacity to move in that direction!") + return FALSE + + //We're using locs because some mecha are 2x2 turfs. So thicc! + var/result = TRUE + + for(var/turf/T in locs) + if(!T.CanZPass(src,direction)) + occupant_message("You can't move that direction from here!") + result = FALSE + break + var/turf/dest = (direction == UP) ? GetAbove(src) : GetBelow(src) + if(!dest) + occupant_message("There is nothing of interest in this direction.") + result = FALSE + break + if(!dest.CanZPass(src,direction)) + occupant_message("There's something blocking your movement in that direction!") + result = FALSE + break + if(result) + move_result = mechstep(direction) + + //Turning + + else if(src.dir != direction) + + if(strafing) + move_result = mechstep(direction) + else + move_result = mechturn(direction) + + //Stepping + else + move_result = mechstep(direction) + + + if(move_result) + can_move = 0 + use_power(step_energy_drain) + if(istype(src.loc, /turf/space)) + if(!src.check_for_support()) + float_direction = direction + start_process(MECHA_PROC_MOVEMENT) + src.log_message("Movement control lost. Inertial movement started.") + if(do_after(get_step_delay())) + can_move = 1 + return 1 + return 0 + +/obj/mecha/proc/handle_equipment_movement() + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + if(ME.chassis == src) //Sanity + ME.handle_movement_action() + return + +/obj/mecha/proc/mechturn(direction) + set_dir(direction) + if(swivel_sound) + playsound(src,swivel_sound,40,1) + return 1 + +/obj/mecha/proc/mechstep(direction) + var/current_dir = dir //For strafing + var/result = get_step(src,direction) + if(result && Move(result)) + if(stomp_sound) + playsound(src,stomp_sound,40,1) + handle_equipment_movement() + if(strafing) //Also for strafing + set_dir(current_dir) + return result + + +/obj/mecha/proc/mechsteprand() + var/result = get_step_rand(src) + if(result && Move(result)) + if(stomp_sound) + playsound(src,stomp_sound,40,1) + handle_equipment_movement() + return result + +/obj/mecha/Bump(var/atom/obstacle) +// src.inertia_dir = null + if(istype(obstacle, /mob))//First we check if it is a mob. Mechs mostly shouln't go through them, even while phasing. + var/mob/M = obstacle + M.Move(get_step(obstacle,src.dir)) + else if(phasing && get_charge()>=phasing_energy_drain)//Phazon check. This could use an improvement elsewhere. + src.use_power(phasing_energy_drain) + phase() + . = ..(obstacle) + return + else if(istype(obstacle, /obj))//Then we check for regular obstacles. + var/obj/O = obstacle + if(istype(O, /obj/effect/portal)) //derpfix + src.anchored = 0 // Portals can only move unanchored objects. + O.Crossed(src) + spawn(0)//countering portal teleport spawn(0), hurr + src.anchored = 1 + if(O.anchored) + obstacle.Bumped(src) + else + step(obstacle,src.dir) + + else//No idea when this triggers, so i won't touch it. + . = ..(obstacle) + return + +/obj/mecha/proc/phase() // Force the mecha to move forward by phasing. + set waitfor = FALSE + if(can_phase) + can_phase = FALSE + flick("[initial_icon]-phase", src) + forceMove(get_step(src,src.dir)) + sleep(get_step_delay() * 3) + can_phase = TRUE + occupant_message("Phazed.") + return TRUE // In the event this is sequenced + return FALSE + +/////////////////////////////////// +//////// Internal damage //////// +/////////////////////////////////// + +//ATM, the ignore_threshold is literally only used for the pulse rifles beams used mostly by deathsquads. +/obj/mecha/proc/check_for_internal_damage(var/list/possible_int_damage,var/ignore_threshold=null) + if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) return + if(prob(30)) + if(ignore_threshold || src.health*100/initial(src.health) < src.internal_damage_threshold) + for(var/T in possible_int_damage) + if(internal_damage & T) + possible_int_damage -= T + var/int_dam_flag = safepick(possible_int_damage) + if(int_dam_flag) + setInternalDamage(int_dam_flag) + return //It already hurts to get some, lets not get both. + + if(prob(10)) + if(ignore_threshold || src.health*100/initial(src.health) < src.internal_damage_threshold) + var/obj/item/mecha_parts/mecha_equipment/destr = safepick(equipment) + if(destr) + destr.destroy() + return + +/obj/mecha/proc/hasInternalDamage(int_dam_flag=null) + return int_dam_flag ? internal_damage&int_dam_flag : internal_damage + + +/obj/mecha/proc/setInternalDamage(int_dam_flag) + internal_damage |= int_dam_flag + start_process(MECHA_PROC_DAMAGE) + log_append_to_last("Internal damage of type [int_dam_flag].",1) + occupant << sound('sound/mecha/internaldmgalarm.ogg',volume=50) //Better sounding. + return + +/obj/mecha/proc/clearInternalDamage(int_dam_flag) + internal_damage &= ~int_dam_flag + switch(int_dam_flag) + if(MECHA_INT_TEMP_CONTROL) + occupant_message(span_blue("Life support system reactivated.")) + start_process(MECHA_PROC_INT_TEMP) + if(MECHA_INT_FIRE) + occupant_message(span_blue("Internal fire extinquished.")) + if(MECHA_INT_TANK_BREACH) + occupant_message(span_blue("Damaged internal tank has been sealed.")) + return + + +//////////////////////////////////////// +//////// Health related procs //////// +//////////////////////////////////////// + +/obj/mecha/take_damage(amount, type="brute") + update_damage_alerts() + if(amount) + var/damage = absorbDamage(amount,type) + + damage = components_handle_damage(damage,type) + + health -= damage + + update_health() + log_append_to_last("Took [damage] points of damage. Damage type: \"[type]\".",1) + return + +/obj/mecha/proc/components_handle_damage(var/damage, var/type = BRUTE) + var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] + + if(AC) + var/armor_efficiency = AC.get_efficiency() + var/damage_change = armor_efficiency * (damage * 0.5) * AC.damage_absorption[type] + AC.damage_part(damage_change, type) + damage -= damage_change + + var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] + + if(HC) + if(HC.integrity) + var/hull_absorb = round(rand(5, 10) / 10, 0.1) * damage + HC.damage_part(hull_absorb, type) + damage -= hull_absorb + + for(var/obj/item/mecha_parts/component/C in (internal_components - list(MECH_HULL, MECH_ARMOR))) + if(prob(C.relative_size)) + var/damage_part_amt = round(damage / 4, 0.1) + C.damage_part(damage_part_amt) + damage -= damage_part_amt + + return damage + +/obj/mecha/proc/get_damage_absorption() + var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] + if(istype(AC) && AC.get_efficiency() > 0.25) + return AC.damage_absorption + +/obj/mecha/proc/absorbDamage(damage,damage_type) + return call((proc_res["dynabsorbdamage"]||src), "dynabsorbdamage")(damage,damage_type) + +/obj/mecha/proc/dynabsorbdamage(damage,damage_type) + return damage*(listgetindex(get_damage_absorption(),damage_type) || 1) + +/obj/mecha/airlock_crush(var/crush_damage) + ..() + take_damage(crush_damage) + if(prob(50)) //Try to avoid that. + check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + return 1 + +/obj/mecha/proc/update_health() + if(src.health > 0) + src.spark_system.start() + else + qdel(src) + return + +/obj/mecha/attack_hand(mob/user as mob) + if(user == occupant) + show_radial_occupant(user) + return + + user.setClickCooldown(user.get_attack_speed()) + src.log_message("Attack by hand/paw. Attacker - [user].",1) + + var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] + + var/temp_deflect_chance = deflect_chance + + if(!ArmC) + temp_deflect_chance = 1 + + else + temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(H.species.can_shred(user)) + if(!prob(temp_deflect_chance)) + src.take_damage(15) //The take_damage() proc handles armor values + if(prob(25)) //Why would they get free internal damage. At least make it a bit RNG. + src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) + to_chat(user, "You slash at the armored suit!") + visible_message("\The [user] slashes at [src.name]'s armor!") + else + src.log_append_to_last("Armor saved.") + playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) + to_chat(user, "Your claws had no effect!") + src.occupant_message("\The [user]'s claws are stopped by the armor.") + visible_message("\The [user] rebounds off [src.name]'s armor!") + else + user.visible_message("\The [user] hits \the [src]. Nothing happens.","You hit \the [src] with no visible effect.") + src.log_append_to_last("Armor saved.") + return + else if ((HULK in user.mutations) && !prob(temp_deflect_chance)) + src.take_damage(15) //The take_damage() proc handles armor values + if(prob(25)) //Hulks punch hard but lets not give them consistent internal damage. + src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + user.visible_message(span_red("[user] hits [src.name], doing some damage."), span_red("You hit [src.name] with all your might. The metal creaks and bends.")) + else + user.visible_message(span_red("[user] hits [src.name]. Nothing happens."),span_red("You hit [src.name] with no visible effect.")) + src.log_append_to_last("Armor saved.") + return + +/obj/mecha/hitby(atom/movable/A as mob|obj) //wrapper + ..() + src.log_message("Hit by [A].",1) + call((proc_res["dynhitby"]||src), "dynhitby")(A) + return + +//I think this is relative to throws. +/obj/mecha/proc/dynhitby(atom/movable/A) + var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] + + var/temp_deflect_chance = deflect_chance + var/temp_damage_minimum = damage_minimum + var/temp_minimum_penetration = minimum_penetration + var/temp_fail_penetration_value = fail_penetration_value + + if(!ArmC) + temp_deflect_chance = 0 + temp_damage_minimum = 0 + temp_minimum_penetration = 0 + temp_fail_penetration_value = 1 + + else + temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) + temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) + temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) + temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value) + + if(istype(A, /obj/item/mecha_parts/mecha_tracking)) + A.forceMove(src) + src.visible_message("The [A] fastens firmly to [src].") + return + if(prob(temp_deflect_chance) || istype(A, /mob)) + src.occupant_message("\The [A] bounces off the armor.") + src.visible_message("\The [A] bounces off \the [src] armor") + src.log_append_to_last("Armor saved.") + if(istype(A, /mob/living)) + var/mob/living/M = A + M.take_organ_damage(10) + else if(istype(A, /obj)) + var/obj/O = A + if(O.throwforce) + + var/pass_damage = O.throwforce + var/pass_damage_reduc_mod + if(pass_damage <= temp_damage_minimum)//Too little to go through. + src.occupant_message("\The [A] bounces off the armor.") + src.visible_message("\The [A] bounces off \the [src] armor") + return + + else if(O.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage + src.occupant_message("\The [A] struggles to bypass \the [src] armor.") + src.visible_message("\The [A] struggles to bypass \the [src] armor") + pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default + else + src.occupant_message("\The [A] manages to pierce \the [src] armor.") +// src.visible_message("\The [A] manages to pierce \the [src] armor") + pass_damage_reduc_mod = 1 + + + + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + pass_damage = ME.handle_ranged_contact(A, pass_damage) + + pass_damage = (pass_damage*pass_damage_reduc_mod)//Applying damage reduction + src.take_damage(pass_damage) //The take_damage() proc handles armor values + if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. + src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + return + + +/obj/mecha/bullet_act(var/obj/item/projectile/Proj) //wrapper + if(istype(Proj, /obj/item/projectile/test)) + var/obj/item/projectile/test/Test = Proj + Test.hit |= occupant // Register a hit on the occupant, for things like turrets, or in simple-mob cases stopping friendly fire in firing line mode. + return + + src.log_message("Hit by projectile. Type: [Proj.name]([Proj.check_armour]).",1) + call((proc_res["dynbulletdamage"]||src), "dynbulletdamage")(Proj) //calls equipment + ..() + return + +/obj/mecha/proc/dynbulletdamage(var/obj/item/projectile/Proj) + var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] + + var/temp_deflect_chance = deflect_chance + var/temp_damage_minimum = damage_minimum + var/temp_minimum_penetration = minimum_penetration + var/temp_fail_penetration_value = fail_penetration_value + + if(!ArmC) + temp_deflect_chance = 0 + temp_damage_minimum = 0 + temp_minimum_penetration = 0 + temp_fail_penetration_value = 1 + + else + temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) + temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) + temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) + temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value) + + if(prob(temp_deflect_chance)) + src.occupant_message("The armor deflects incoming projectile.") + src.visible_message("The [src.name] armor deflects the projectile") + src.log_append_to_last("Armor saved.") + return + + if(Proj.damage_type == HALLOSS) + use_power(Proj.agony * 5) + + if(!(Proj.nodamage)) + var/ignore_threshold + if(istype(Proj, /obj/item/projectile/beam/pulse)) //ATM, this is literally only for the pulse rifles used mostly by deathsquads. + ignore_threshold = 1 + + var/pass_damage = Proj.damage + var/pass_damage_reduc_mod + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + pass_damage = ME.handle_projectile_contact(Proj, pass_damage) + + if(pass_damage < temp_damage_minimum)//too pathetic to really damage you. + src.occupant_message("The armor deflects incoming projectile.") + src.visible_message("The [src.name] armor deflects\the [Proj]") + return + + else if(Proj.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage + src.occupant_message("\The [Proj] struggles to pierce \the [src] armor.") + src.visible_message("\The [Proj] struggles to pierce \the [src] armor") + pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default + + else //You go through completely because you use AP. Nice. + src.occupant_message("\The [Proj] manages to pierce \the [src] armor.") +// src.visible_message("\The [Proj] manages to pierce \the [src] armor") + pass_damage_reduc_mod = 1 + + pass_damage = (pass_damage_reduc_mod*pass_damage)//Apply damage reduction before usage. + src.take_damage(pass_damage, Proj.check_armour) //The take_damage() proc handles armor values + if(prob(25)) + spark_system.start() + if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. + src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),ignore_threshold) + + //AP projectiles have a chance to cause additional damage + if(Proj.penetrating) + var/distance = get_dist(Proj.starting, get_turf(loc)) + var/hit_occupant = 1 //only allow the occupant to be hit once + for(var/i in 1 to min(Proj.penetrating, round(Proj.damage/15))) + if(src.occupant && hit_occupant && prob(20)) + Proj.attack_mob(src.occupant, distance) + hit_occupant = 0 + else + if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. + src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT), 1) + + Proj.penetrating-- + + if(prob(15)) + break //give a chance to exit early + + Proj.on_hit(src) //on_hit just returns if it's argument is not a living mob so does this actually do anything? + return + +//This refer to whenever you are caught in an explosion. +/obj/mecha/ex_act(severity) + var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] + + var/temp_deflect_chance = deflect_chance + + if(!ArmC) + temp_deflect_chance = 0 + + else + temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) + + src.log_message("Affected by explosion of severity: [severity].",1) + if(prob(temp_deflect_chance)) + severity++ + src.log_append_to_last("Armor saved, changing severity to [severity].") + switch(severity) + if(1.0) + src.take_damage(initial(src.health), "bomb") + if(2.0) + if (prob(30)) + src.take_damage(initial(src.health), "bomb") + else + src.take_damage(initial(src.health)/2, "bomb") + src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) + if(3.0) + if (prob(5)) + qdel(src) + else + src.take_damage(initial(src.health)/5, "bomb") + src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) + return + +/*Will fix later -Sieve +/obj/mecha/attack_blob(mob/user as mob) + src.log_message("Attack by blob. Attacker - [user].",1) + if(!prob(src.deflect_chance)) + src.take_damage(6) + src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + playsound(src, 'sound/effects/blobattack.ogg', 50, 1, -1) + to_chat(user, "You smash at the armored suit!") + for (var/mob/V in viewers(src)) + if(V.client && !(V.blinded)) + V.show_message("\The [user] smashes against [src.name]'s armor!", 1) + else + src.log_append_to_last("Armor saved.") + playsound(src, 'sound/effects/blobattack.ogg', 50, 1, -1) + to_chat(user, "Your attack had no effect!") + src.occupant_message("\The [user]'s attack is stopped by the armor.") + for (var/mob/V in viewers(src)) + if(V.client && !(V.blinded)) + V.show_message("\The [user] rebounds off the [src.name] armor!", 1) + return +*/ + +/obj/mecha/emp_act(severity) + if(get_charge()) + use_power((cell.charge/2)/severity) + take_damage(50 / severity,"energy") + src.log_message("EMP detected",1) + if(prob(80)) + check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) + return + +/obj/mecha/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature>src.max_temperature) + src.log_message("Exposed to dangerous temperature.",1) + src.take_damage(5,"fire") //The take_damage() proc handles armor values + src.check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL)) + return + +/obj/mecha/proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob) + user.setClickCooldown(user.get_attack_speed(W)) + src.log_message("Attacked by [W]. Attacker - [user]") + var/pass_damage_reduc_mod //Modifer for failing to bring AP. + + var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] + + var/temp_deflect_chance = deflect_chance + var/temp_damage_minimum = damage_minimum + var/temp_minimum_penetration = minimum_penetration + var/temp_fail_penetration_value = fail_penetration_value + + if(!ArmC) + temp_deflect_chance = 0 + temp_damage_minimum = 0 + temp_minimum_penetration = 0 + temp_fail_penetration_value = 1 + + else + temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) + temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) + temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) + temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value) + + if(prob(temp_deflect_chance)) //Does your attack get deflected outright. + src.occupant_message("\The [W] bounces off [src.name].") + to_chat(user, "\The [W] bounces off [src.name].") + src.log_append_to_last("Armor saved.") + + else if(W.force < temp_damage_minimum) //Is your attack too PATHETIC to do anything. 3 damage to a person shouldn't do anything to a mech. + src.occupant_message("\The [W] bounces off the armor.") + src.visible_message("\The [W] bounces off \the [src] armor") + return + + else if(W.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage + src.occupant_message("\The [W] struggles to bypass \the [src] armor.") + src.visible_message("\The [W] struggles to bypass \the [src] armor") + pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default + + else + pass_damage_reduc_mod = 1 //Just making sure. + src.occupant_message(span_red("[user] hits [src] with [W].")) + user.visible_message(span_red("[user] hits [src] with [W]."), span_red("You hit [src] with [W].")) + + var/pass_damage = W.force + pass_damage = (pass_damage*pass_damage_reduc_mod) //Apply the reduction of damage from not having enough armor penetration. This is not regular armor values at play. + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + pass_damage = ME.handle_projectile_contact(W, user, pass_damage) + src.take_damage(pass_damage,W.damtype) //The take_damage() proc handles armor values + if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. + src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + return + +////////////////////// +////// AttackBy ////// +////////////////////// + +/obj/mecha/attackby(obj/item/weapon/W as obj, mob/user as mob) + + if(istype(W, /obj/item/device/mmi)) + if(mmi_move_inside(W,user)) + to_chat(user, "[src]-MMI interface initialized successfuly") + else + to_chat(user, "[src]-MMI interface initialization failed.") + return + + if(istype(W, /obj/item/device/robotanalyzer)) + var/obj/item/device/robotanalyzer/RA = W + RA.do_scan(src, user) + return + + if(istype(W, /obj/item/mecha_parts/mecha_equipment)) + var/obj/item/mecha_parts/mecha_equipment/E = W + spawn() + if(E.can_attach(src)) + user.drop_item() + E.attach(src) + user.visible_message("[user] attaches [W] to [src]", "You attach [W] to [src]") + else + to_chat(user, "You were unable to attach [W] to [src]") + return + + if(istype(W, /obj/item/mecha_parts/component) && state == MECHA_CELL_OUT) + var/obj/item/mecha_parts/component/MC = W + spawn() + if(MC.attach(src)) + user.drop_item() + MC.forceMove(src) + user.visible_message("[user] installs \the [W] in \the [src]", "You install \the [W] in \the [src].") + return + + if(istype(W, /obj/item/weapon/card/robot)) + var/obj/item/weapon/card/robot/RoC = W + return attackby(RoC.dummy_card, user) + + if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) + if(add_req_access || maint_access) + if(internals_access_allowed(usr)) + var/obj/item/weapon/card/id/id_card + if(istype(W, /obj/item/weapon/card/id)) + id_card = W + else + var/obj/item/device/pda/pda = W + id_card = pda.id + output_maintenance_dialog(id_card, user) + return + else + to_chat(user, "Invalid ID: Access denied.") + else + to_chat(user, "Maintenance protocols disabled by operator.") + else if(W.has_tool_quality(TOOL_WRENCH)) + if(state==MECHA_BOLTS_SECURED) + state = MECHA_PANEL_LOOSE + to_chat(user, "You undo the securing bolts.") + else if(state==MECHA_PANEL_LOOSE) + state = MECHA_BOLTS_SECURED + to_chat(user, "You tighten the securing bolts.") + return + else if(W.has_tool_quality(TOOL_CROWBAR)) + if(state==MECHA_PANEL_LOOSE) + state = MECHA_CELL_OPEN + to_chat(user, "You open the hatch to the power unit") + else if(state==MECHA_CELL_OPEN) + state=MECHA_PANEL_LOOSE + to_chat(user, "You close the hatch to the power unit") + else if(state==MECHA_CELL_OUT) + var/list/removable_components = list() + for(var/slot in internal_components) + var/obj/item/mecha_parts/component/MC = internal_components[slot] + if(istype(MC)) + removable_components[MC.name] = MC + else + to_chat(user, "\The [src] appears to be missing \the [slot].") + + var/remove = tgui_input_list(user, "Which component do you want to pry out?", "Remove Component", removable_components) + if(!remove) + return + + var/obj/item/mecha_parts/component/RmC = removable_components[remove] + RmC.detach() + + return + else if(istype(W, /obj/item/stack/cable_coil)) + if(state >= MECHA_CELL_OPEN && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) + var/obj/item/stack/cable_coil/CC = W + if(CC.use(2)) + clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) + to_chat(user, "You replace the fused wires.") + else + to_chat(user, "There's not enough wire to finish the task.") + return + else if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) + clearInternalDamage(MECHA_INT_TEMP_CONTROL) + to_chat(user, "You repair the damaged temperature controller.") + else if(state==MECHA_CELL_OPEN && src.cell) + src.cell.forceMove(src.loc) + src.cell = null + state = MECHA_CELL_OUT + to_chat(user, "You unscrew and pry out the powercell.") + src.log_message("Powercell removed") + else if(state==MECHA_CELL_OUT && src.cell) + state=MECHA_CELL_OPEN + to_chat(user, "You screw the cell in place") + return + + else if(istype(W, /obj/item/device/multitool)) + if(state>=MECHA_CELL_OPEN && src.occupant) + to_chat(user, "You attempt to eject the pilot using the maintenance controls.") + if(src.occupant.stat) + src.go_out() + src.log_message("[src.occupant] was ejected using the maintenance controls.") + else + to_chat(user, "Your attempt is rejected.") + src.occupant_message("An attempt to eject you was made using the maintenance controls.") + src.log_message("Eject attempt made using maintenance controls - rejected.") + return + + else if(istype(W, /obj/item/weapon/cell)) + if(state==MECHA_CELL_OUT) + if(!src.cell) + to_chat(user, "You install the powercell") + user.drop_item() + W.forceMove(src) + src.cell = W + src.log_message("Powercell installed") + else + to_chat(user, "There's already a powercell installed.") + return + + else if(W.has_tool_quality(TOOL_WELDER) && user.a_intent != I_HURT) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] + var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] + if (WT.remove_fuel(0,user)) + if (hasInternalDamage(MECHA_INT_TANK_BREACH)) + clearInternalDamage(MECHA_INT_TANK_BREACH) + to_chat(user, "You repair the damaged gas tank.") + else + return + if((src.healthYou repair some damage to [src.name].") + src.health += min(10, initial(src.health)-src.health) + update_damage_alerts() + else if(HC.integrityYou repair some damage to [HC.name].") + HC.integrity += min(10, HC.max_integrity-HC.integrity) + update_damage_alerts() + else if(AC.integrityYou repair some damage to [AC.name].") + AC.integrity += min(10, AC.max_integrity-AC.integrity) + update_damage_alerts() + + else + to_chat(user, "The [src.name] is at full integrity") + return + + else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) + user.drop_from_inventory(W) + W.forceMove(src) + user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src]") + return + + else if(istype(W,/obj/item/stack/nanopaste)) + if(state >= MECHA_PANEL_LOOSE) + var/obj/item/stack/nanopaste/NP = W + + for(var/slot in internal_components) + var/obj/item/mecha_parts/component/C = internal_components[slot] + + if(!C) + to_chat(user, "There are no components installed!") + return + + if(C.integrity >= C.max_integrity) + to_chat(user, "\The [C] does not require repairs.") + + else if(C.integrity < C.max_integrity) + to_chat(user, "You start to repair damage to \the [C].") + while(C.integrity < C.max_integrity && NP) + if(do_after(user, 1 SECOND, src)) + NP.use(1) + C.adjust_integrity(NP.mech_repair) + + if(C.integrity >= C.max_integrity) + to_chat(user, "You finish repairing \the [C].") + break + + else if(NP.amount == 0) + to_chat(user, "Insufficient nanopaste to complete repairs!") + break + return + + else + to_chat(user, "You can't reach \the [src]'s internal components.") + return + + else + call((proc_res["dynattackby"]||src), "dynattackby")(W,user) +/* + src.log_message("Attacked by [W]. Attacker - [user]") + if(prob(src.deflect_chance)) + to_chat(user, "\The [W] bounces off [src.name] armor.") + src.log_append_to_last("Armor saved.") +/* + for (var/mob/V in viewers(src)) + if(V.client && !(V.blinded)) + V.show_message("The [W] bounces off [src.name] armor.", 1) +*/ + else + src.occupant_message("[user] hits [src] with [W].") + user.visible_message("[user] hits [src] with [W].", "You hit [src] with [W].") + src.take_damage(W.force,W.damtype) + src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) +*/ + return + + + +/* +/obj/mecha/attack_ai(var/mob/living/silicon/ai/user as mob) + if(!istype(user, /mob/living/silicon/ai)) + return + var/output = {"Assume direct control over [src]? + Yes
                    + "} + user << browse(output, "window=mecha_attack_ai") + return +*/ + +/////////////////////////////// +//////// Brain Stuff //////// +/////////////////////////////// + +/obj/mecha/proc/mmi_move_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) + if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) + to_chat(user, "Consciousness matrix not detected.") + return 0 + else if(mmi_as_oc.brainmob.stat) + to_chat(user, "Brain activity below acceptable level.") + return 0 + else if(occupant) + to_chat(user, "Occupant detected.") + return 0 + else if(dna && dna!=mmi_as_oc.brainmob.dna.unique_enzymes) + to_chat(user, "Genetic sequence or serial number incompatible with locking mechanism.") + return 0 + //Added a message here since people assume their first click failed or something./N +// to_chat(user, "Installing MMI, please stand by.") + + visible_message("[usr] starts to insert a brain into [src.name]") + + if(enter_after(40,user)) + if(!occupant) + return mmi_moved_inside(mmi_as_oc,user) + else + to_chat(user, "Occupant detected.") + else + to_chat(user, "You stop attempting to install the brain.") + return 0 + +/obj/mecha/proc/mmi_moved_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) + if(mmi_as_oc && (user in range(1))) + if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) + to_chat(user, "Consciousness matrix not detected.") + return 0 + else if(mmi_as_oc.brainmob.stat) + to_chat(user, "Beta-rhythm below acceptable level.") + return 0 + user.drop_from_inventory(mmi_as_oc) + var/mob/brainmob = mmi_as_oc.brainmob + brainmob.reset_view(src) + /* + brainmob.client.eye = src + brainmob.client.perspective = EYE_PERSPECTIVE + */ + occupant = brainmob + brainmob.loc = src //should allow relaymove + brainmob.canmove = 1 + mmi_as_oc.loc = src + mmi_as_oc.mecha = src + src.verbs += /obj/mecha/verb/eject + src.Entered(mmi_as_oc) + src.Move(src.loc) + update_icon() + set_dir(dir_in) + src.log_message("[mmi_as_oc] moved in as pilot.") + if(!hasInternalDamage()) + src.occupant << sound('sound/mecha/nominal.ogg',volume=50) + update_icon() + return 1 + else + return 0 + + +///////////////////////////////////// +//////// Atmospheric stuff //////// +///////////////////////////////////// + +/obj/mecha/proc/get_turf_air() + var/turf/T = get_turf(src) + if(T) + . = T.return_air() + return + +/obj/mecha/remove_air(amount) + if(use_internal_tank) + return cabin_air.remove(amount) + else + var/turf/T = get_turf(src) + if(T) + return T.remove_air(amount) + return + +/obj/mecha/return_air() + var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] + if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100))) + return cabin_air + return get_turf_air() + +/obj/mecha/proc/return_pressure() + . = 0 + var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] + if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100))) + . = cabin_air.return_pressure() + else + var/datum/gas_mixture/t_air = get_turf_air() + if(t_air) + . = t_air.return_pressure() + return + +//skytodo: //No idea what you want me to do here, mate. +/obj/mecha/proc/return_temperature() + . = 0 + var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] + if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100))) + . = cabin_air.temperature + else + var/datum/gas_mixture/t_air = get_turf_air() + if(t_air) + . = t_air.temperature + return + +/obj/mecha/proc/connect(obj/machinery/atmospherics/portables_connector/new_port) + //Make sure not already connected to something else + if(connected_port || !new_port || new_port.connected_device) + return 0 + + //Make sure are close enough for a valid connection + if(!(new_port.loc in locs)) + return 0 + + //Perform the connection + connected_port = new_port + connected_port.connected_device = src + + //Actually enforce the air sharing + var/datum/pipe_network/network = connected_port.return_network(src) + if(network && !(internal_tank.return_air() in network.gases)) + network.gases += internal_tank.return_air() + network.update = 1 + playsound(src, 'sound/mecha/gasconnected.ogg', 50, 1) + log_message("Connected to gas port.") + return 1 + +/obj/mecha/proc/disconnect() + if(!connected_port) + return 0 + + var/datum/pipe_network/network = connected_port.return_network(src) + if(network) + network.gases -= internal_tank.return_air() + + connected_port.connected_device = null + connected_port = null + playsound(src, 'sound/mecha/gasdisconnected.ogg', 50, 1) + src.log_message("Disconnected from gas port.") + return 1 + + +///////////////////////// +//////// Verbs //////// +///////////////////////// + + +/obj/mecha/verb/connect_to_port() + set name = "Connect to port" + set category = "Exosuit Interface" + set src = usr.loc + set popup_menu = 0 + + if(!occupant) + return + + if(usr != occupant) + return + + var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] + if(!GC) + return + + for(var/turf/T in locs) + var/obj/machinery/atmospherics/portables_connector/possible_port = locate(/obj/machinery/atmospherics/portables_connector) in T + if(possible_port) + if(connect(possible_port)) + occupant_message("\The [name] connects to the port.") + verbs += /obj/mecha/verb/disconnect_from_port + verbs -= /obj/mecha/verb/connect_to_port + return + else + occupant_message("\The [name] failed to connect to the port.") + return + else + occupant_message("Nothing happens") + + +/obj/mecha/verb/disconnect_from_port() + set name = "Disconnect from port" + set category = "Exosuit Interface" + set src = usr.loc + set popup_menu = 0 + + if(!occupant) + return + + if(usr != occupant) + return + + if(disconnect()) + occupant_message("[name] disconnects from the port.") + verbs -= /obj/mecha/verb/disconnect_from_port + verbs += /obj/mecha/verb/connect_to_port + else + occupant_message("[name] is not connected to the port at the moment.") + +/obj/mecha/verb/toggle_lights() + set name = "Toggle Lights" + set category = "Exosuit Interface" + set src = usr.loc + set popup_menu = 0 + lights() + +/obj/mecha/verb/lights() + if(usr!=occupant) return + lights = !lights + if(lights) set_light(light_range + lights_power) + else set_light(light_range - lights_power) + src.occupant_message("Toggled lights [lights?"on":"off"].") + log_message("Toggled lights [lights?"on":"off"].") + playsound(src, 'sound/mecha/heavylightswitch.ogg', 50, 1) + return + + +/obj/mecha/verb/toggle_internal_tank() + set name = "Toggle internal airtank usage" + set category = "Exosuit Interface" + set src = usr.loc + set popup_menu = 0 + internal_tank() + +/obj/mecha/proc/internal_tank() + if(usr!=src.occupant) + return + + var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] + if(!GC) + to_chat(occupant, "The life support systems don't seem to respond.") + return + + if(!prob(GC.get_efficiency() * 100)) + to_chat(occupant, "\The [GC] shudders and barks, before returning to how it was before.") + return + + use_internal_tank = !use_internal_tank + src.occupant_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].") + src.log_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].") + playsound(src, 'sound/mecha/gasdisconnected.ogg', 30, 1) + return + + +/obj/mecha/verb/toggle_strafing() + set name = "Toggle strafing" + set category = "Exosuit Interface" + set src = usr.loc + set popup_menu = 0 + strafing() + +/obj/mecha/proc/strafing() + if(usr!=src.occupant) + return + strafing = !strafing + src.occupant_message("Toggled strafing mode [strafing?"on":"off"].") + src.log_message("Toggled strafing mode [strafing?"on":"off"].") + return + +/obj/mecha/MouseDrop_T(mob/O, mob/user as mob) + //Humans can pilot mechs. + if(!ishuman(O)) + return + + //Can't put other people into mechs (can comment this out if you want that to be possible) + if(O != user) + return + + move_inside() + +/obj/mecha/verb/enter() + set category = "Object" + set name = "Enter Exosuit" + set src in oview(1) + move_inside() + +//returns an equipment object if we have one of that type, useful since is_type_in_list won't return the object +//since is_type_in_list uses caching, this is a slower operation, so only use it if needed +/obj/mecha/proc/get_equipment(var/equip_type) + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + if(istype(ME,equip_type)) + return ME + return null + +/obj/mecha/proc/move_inside() + if (usr.stat || !ishuman(usr)) + return + + if (usr.buckled) + to_chat(usr, "You can't climb into the exosuit while buckled!") + return + + src.log_message("[usr] tries to move in.") + if(iscarbon(usr)) + var/mob/living/carbon/C = usr + if(C.handcuffed) + to_chat(usr, "Kinda hard to climb in while handcuffed don't you think?") + return + if (src.occupant) + to_chat(usr, "The [src.name] is already occupied!") + src.log_append_to_last("Permission denied.") + return +/* + if (usr.abiotic()) + to_chat(usr, "Subject cannot have abiotic items on.") + return +*/ + var/passed + if(src.dna) + if(usr.dna.unique_enzymes==src.dna) + passed = 1 + else if(src.operation_allowed(usr)) + passed = 1 + if(!passed) + to_chat(usr, "Access denied") + src.log_append_to_last("Permission denied.") + return + if(isliving(usr)) + var/mob/living/L = usr + if(L.has_buckled_mobs()) + to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first.")) + return + +// to_chat(usr, "You start climbing into [src.name]") + if(get_equipment(/obj/item/mecha_parts/mecha_equipment/runningboard)) + visible_message("\The [usr] is instantly lifted into [src.name] by the running board!") + moved_inside(usr) + if(ishuman(occupant)) + GrantActions(occupant, 1) + else + visible_message("\The [usr] starts to climb into [src.name]") + if(enter_after(40,usr)) + if(!src.occupant) + moved_inside(usr) + if(ishuman(occupant)) //Aeiou + GrantActions(occupant, 1) + else if(src.occupant!=usr) + to_chat(usr, "[src.occupant] was faster. Try better next time, loser.") + else + to_chat(usr, "You stop entering the exosuit.") + return + +/obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob) + if(H && H.client && (H in range(1))) + H.reset_view(src) + /* + H.client.perspective = EYE_PERSPECTIVE + H.client.eye = src + */ + H.stop_pulling() + H.forceMove(src) + src.occupant = H + src.add_fingerprint(H) + src.forceMove(src.loc) + src.verbs += /obj/mecha/verb/eject + src.log_append_to_last("[H] moved in as pilot.") + update_icon() + //VOREStation Edit Add + if(occupant.hud_used) + minihud = new (occupant.hud_used, src) + //VOREStation Edit Add End + +//This part removes all the verbs if you don't have them the _possible on your mech. This is a little clunky, but it lets you just add that to any mech. +//And it's not like this 10yo code wasn't clunky before. + + if(!smoke_possible) //Can't use smoke? No verb for you. + verbs -= /obj/mecha/verb/toggle_smoke + if(!thrusters_possible) //Can't use thrusters? No verb for you. + verbs -= /obj/mecha/verb/toggle_thrusters + if(!defence_mode_possible) //Do i need to explain everything? + verbs -= /obj/mecha/verb/toggle_defence_mode + if(!overload_possible) + verbs -= /obj/mecha/verb/toggle_overload + if(!zoom_possible) + verbs -= /obj/mecha/verb/toggle_zoom + if(!phasing_possible) + verbs -= /obj/mecha/verb/toggle_phasing + if(!switch_dmg_type_possible) + verbs -= /obj/mecha/verb/switch_damtype + if(!cloak_possible) + verbs -= /obj/mecha/verb/toggle_cloak + + occupant.in_enclosed_vehicle = 1 //Useful for when you need to know if someone is in a mecho. + update_cell_alerts() + update_damage_alerts() + set_dir(dir_in) + playsound(src, 'sound/machines/door/windowdoor.ogg', 50, 1) + if(occupant.client && cloaked_selfimage) + occupant.client.images += cloaked_selfimage + play_entered_noise(occupant) + return 1 + else + return 0 + +/obj/mecha/proc/play_entered_noise(var/mob/who) + if(!hasInternalDamage()) //Otherwise it's not nominal! + switch(mech_faction) + if(MECH_FACTION_NT)//The good guys category + if(firstactivation)//First time = long activation sound + firstactivation = 1 + who << sound('sound/mecha/LongNanoActivation.ogg',volume=50) + else + who << sound('sound/mecha/nominalnano.ogg',volume=50) + if(MECH_FACTION_SYNDI)//Bad guys + if(firstactivation) + firstactivation = 1 + who << sound('sound/mecha/LongSyndiActivation.ogg',volume=50) + else + who << sound('sound/mecha/nominalsyndi.ogg',volume=50) + else//Everyone else gets the normal noise + who << sound('sound/mecha/nominal.ogg',volume=50) + +/obj/mecha/AltClick(mob/living/user) + if(user == occupant) + strafing() + +/obj/mecha/verb/view_stats() + set name = "View Stats" + set category = "Exosuit Interface" + set src = usr.loc + set popup_menu = 0 + if(usr!=src.occupant) + return + //pr_update_stats.start() + src.occupant << browse(src.get_stats_html(), "window=exosuit") + return + +/* +/obj/mecha/verb/force_eject() + set category = "Object" + set name = "Force Eject" + set src in view(5) + src.go_out() + return +*/ + +/obj/mecha/verb/eject() + set name = "Eject" + set category = "Exosuit Interface" + set src = usr.loc + set popup_menu = 0 + if(usr!=src.occupant) + return + src.go_out() + add_fingerprint(usr) + return + + +/obj/mecha/proc/go_out() //Eject/Exit the mech. Yes this is for easier searching. + if(!src.occupant) return + var/atom/movable/mob_container + QDEL_NULL(minihud) + if(ishuman(occupant)) + mob_container = src.occupant + RemoveActions(occupant, human_occupant=1)//AEIOU + else if(istype(occupant, /mob/living/carbon/brain)) + var/mob/living/carbon/brain/brain = occupant + mob_container = brain.container + else + return + if(mob_container.forceMove(src.loc))//ejecting mob container + log_message("[mob_container] moved out.") + occupant.reset_view() + occupant << browse(null, "window=exosuit") + if(occupant.client && cloaked_selfimage) + occupant.client.images -= cloaked_selfimage + if(istype(mob_container, /obj/item/device/mmi)) + var/obj/item/device/mmi/mmi = mob_container + if(mmi.brainmob) + occupant.loc = mmi + mmi.mecha = null + occupant.canmove = 0 + occupant.clear_alert("charge") + occupant.clear_alert("mech damage") + occupant.in_enclosed_vehicle = 0 + occupant = null + update_icon() + set_dir(dir_in) + verbs -= /obj/mecha/verb/eject + + //src.zoom = 0 + + // Doesn't seem needed. + if(src.occupant && src.occupant.client) + src.occupant.client.view = world.view + src.zoom = 0 + + strafing = 0 + return + +///////////////////////// +////// Access stuff ///// +///////////////////////// + +/obj/mecha/proc/operation_allowed(mob/living/carbon/human/H) + for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt)) + if(src.check_access(ID,src.operation_req_access)) + return 1 + return 0 + + +/obj/mecha/proc/internals_access_allowed(mob/living/carbon/human/H) + if(istype(H)) + for(var/atom/ID in list(H.get_active_hand(), H.wear_id, H.belt)) + if(src.check_access(ID,src.internals_req_access)) + return 1 + else if(istype(H, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = H + if(src.check_access(R.idcard,src.internals_req_access)) + return 1 + return 0 + + +/obj/mecha/check_access(obj/item/weapon/card/id/I, list/access_list) + if(!istype(access_list)) + return 1 + if(!access_list.len) //no requirements + return 1 + if(istype(I, /obj/item/device/pda)) + var/obj/item/device/pda/pda = I + I = pda.id + if(!istype(I) || !I.access) //not ID or no access + return 0 + if(access_list==src.operation_req_access) + for(var/req in access_list) + if(!(req in I.access)) //doesn't have this access + return 0 + else if(access_list==src.internals_req_access) + for(var/req in access_list) + if(req in I.access) + return 1 + return 1 + + +//////////////////////////////////// +///// Rendering stats window /////// +//////////////////////////////////// + +/obj/mecha/proc/get_stats_html() + var/output = {" + [src.name] data + + + + +
                    + [src.get_stats_part()] +
                    +
                    + [src.get_equipment_list()] +
                    +
                    +
                    + [src.get_commands()] +
                    + + + "} + return output + + +/obj/mecha/proc/report_internal_damage() + var/output = null + var/list/dam_reports = list( + "[MECHA_INT_FIRE]" = "INTERNAL FIRE", + "[MECHA_INT_TEMP_CONTROL]" = "LIFE SUPPORT SYSTEM MALFUNCTION", + "[MECHA_INT_TANK_BREACH]" = "GAS TANK BREACH", + "[MECHA_INT_CONTROL_LOST]" = "COORDINATION SYSTEM CALIBRATION FAILURE - Recalibrate", + "[MECHA_INT_SHORT_CIRCUIT]" = "SHORT CIRCUIT" + ) + for(var/tflag in dam_reports) + var/intdamflag = text2num(tflag) + if(hasInternalDamage(intdamflag)) + output += dam_reports[tflag] + output += "
                    " + if(return_pressure() > WARNING_HIGH_PRESSURE) + output += "DANGEROUSLY HIGH CABIN PRESSURE
                    " + return output + + +/obj/mecha/proc/get_stats_part() + var/integrity = health/initial(health)*100 + var/cell_charge = get_charge() + var/tank_pressure = internal_tank ? round(internal_tank.return_pressure(),0.01) : "None" + var/tank_temperature = internal_tank ? internal_tank.return_temperature() : "Unknown" + var/cabin_pressure = round(return_pressure(),0.01) + + var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] + var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] + + var/output = {"[report_internal_damage()] + Armor Integrity: [AC?"[round(AC.integrity / AC.max_integrity * 100, 0.1)]%":"ARMOR MISSING"]
                    + Hull Integrity: [HC?"[round(HC.integrity / HC.max_integrity * 100, 0.1)]%":"HULL MISSING"]
                    + [integrity<30?"DAMAGE LEVEL CRITICAL
                    ":null] + Chassis Integrity: [integrity]%
                    + Powercell charge: [isnull(cell_charge)?"No powercell installed":"[cell.percent()]%"]
                    + Air source: [use_internal_tank?"Internal Airtank":"Environment"]
                    + Airtank pressure: [tank_pressure]kPa
                    + Airtank temperature: [tank_temperature]K|[tank_temperature - T0C]°C
                    + Cabin pressure: [cabin_pressure>WARNING_HIGH_PRESSURE ? "[cabin_pressure]": cabin_pressure]kPa
                    + Cabin temperature: [return_temperature()]K|[return_temperature() - T0C]°C
                    + Lights: [lights?"on":"off"]
                    + [src.dna?"DNA-locked:
                    [src.dna] \[Reset\]
                    ":null] + "} + + + if(defence_mode_possible) + output += "Defence mode: [defence_mode?"on":"off"]
                    " + if(overload_possible) + output += "Leg actuators overload: [overload?"on":"off"]
                    " + if(smoke_possible) + output += "Smoke: [smoke_reserve]
                    " + if(thrusters_possible) + output += "Thrusters: [thrusters?"on":"off"]
                    " + +//Cargo components. Keep this last otherwise it does weird alignment issues. + output += "Cargo Compartment Contents:
                    " + if(src.cargo.len) + for(var/obj/O in src.cargo) + output += "Unload : [O]
                    " + else + output += "Nothing" + output += "
                    " + return output + +/obj/mecha/proc/get_commands() + var/output = {"
                    +
                    Electronics
                    + +
                    +
                    +
                    Airtank
                    + +
                    + +
                    [get_equipment_menu()]
                    +
                    + [(/obj/mecha/verb/eject in src.verbs)?"Eject
                    ":null] + "} + return output + +/obj/mecha/proc/get_equipment_menu() //outputs mecha html equipment menu + var/output + if(equipment.len) + output += {"
                    +
                    Equipment
                    +
                    + "} + return output + +/obj/mecha/proc/get_equipment_list() //outputs mecha equipment list in html + if(!equipment.len) + return + var/output = "Equipment:
                    " + for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) + output += "
                    [MT.get_equip_info()]
                    " + output += "
                    " + return output + + +/obj/mecha/proc/get_log_html() + var/output = "[src.name] Log" + for(var/list/entry in log) + output += {"
                    [time2text(entry["time"],"DDD MMM DD hh:mm:ss")] [game_year]
                    +
                    [entry["message"]]
                    + "} + output += "" + return output + +/obj/mecha/proc/get_log_tgui() + var/list/data = list() + for(var/list/entry in log) + data.Add(list(list( + "time" = time2text(entry["time"], "DDD MMM DD hh:mm:ss"), + "year" = game_year, + "message" = entry["message"], + ))) + return data + + +/obj/mecha/proc/output_access_dialog(obj/item/weapon/card/id/id_card, mob/user) + if(!id_card || !user) return + var/output = {" + + + +

                    Following keycodes are present in this system:

                    "} + for(var/a in operation_req_access) + output += "[get_access_desc(a)] - Delete
                    " + output += "

                    Following keycodes were detected on portable device:

                    " + for(var/a in id_card.access) + if(a in operation_req_access) continue + var/a_name = get_access_desc(a) + if(!a_name) continue //there's some strange access without a name + output += "[a_name] - Add
                    " + output += "
                    Finish (Warning! The ID upload panel will be locked. It can be unlocked only through Exosuit Interface.)" + output += "" + user << browse(output, "window=exosuit_add_access") + onclose(user, "exosuit_add_access") + return + +/obj/mecha/proc/output_maintenance_dialog(obj/item/weapon/card/id/id_card,mob/user) + if(!id_card || !user) return + + var/maint_options = "Set Cabin Air Pressure" + if (locate(/obj/item/mecha_parts/mecha_equipment/tool/passenger) in contents) + maint_options += "Remove Passenger" + + var/output = {" + + + + + [add_req_access?"Edit operation keycodes":null] + [maint_access?"Initiate maintenance protocol":null] + [(state>0) ? maint_options : ""] + + "} + user << browse(output, "window=exosuit_maint_console") + onclose(user, "exosuit_maint_console") + return + + +//////////////////////////////// +/////// Messages and Log /////// +//////////////////////////////// + +/obj/mecha/proc/occupant_message(message as text) + if(message) + if(src.occupant && src.occupant.client) + to_chat(src.occupant, "\icon[src][bicon(src)] [message]") + return + +/obj/mecha/proc/log_message(message as text,red=null) + log.len++ + log[log.len] = list("time"=world.timeofday,"message"="[red?"":null][message][red?"":null]") + return log.len + +/obj/mecha/proc/log_append_to_last(message as text,red=null) + var/list/last_entry = src.log[src.log.len] + last_entry["message"] += "
                    [red?"":null][message][red?"":null]" + return + + +///////////////// +///// Topic ///// +///////////////// + +/obj/mecha/Topic(href, href_list) + ..() + if(href_list["update_content"]) + if(usr != src.occupant) return + send_byjax(src.occupant,"exosuit.browser","content",src.get_stats_part()) + return + if(href_list["close"]) + return + if(usr.stat > 0) + return + var/datum/topic_input/top_filter = new /datum/topic_input(href,href_list) + if(href_list["select_equip"]) + if(usr != src.occupant) return + var/obj/item/mecha_parts/mecha_equipment/equip = top_filter.getObj("select_equip") + if(equip) + src.selected = equip + src.occupant_message("You switch to [equip].") + src.visible_message("[src] raises [equip].") + send_byjax(src.occupant,"exosuit.browser","eq_list",src.get_equipment_list()) + return + if(href_list["eject"]) + if(usr != src.occupant) return + src.eject() + return + if(href_list["toggle_lights"]) + if(usr != src.occupant) return + src.lights() + return +/* + if(href_list["toggle_strafing"]) + if(usr != src.occupant) return + src.strafing() + return*/ + + if(href_list["toggle_airtank"]) + if(usr != src.occupant) return + src.internal_tank() + return + if (href_list["toggle_thrusters"]) + src.toggle_thrusters() + if (href_list["smoke"]) + src.smoke() + if (href_list["toggle_zoom"]) + src.zoom() + if(href_list["toggle_defence_mode"]) + src.defence_mode() + if(href_list["switch_damtype"]) + src.switch_damtype() + if(href_list["phasing"]) + src.phasing() + + if(href_list["rmictoggle"]) + if(usr != src.occupant) return + radio.broadcasting = !radio.broadcasting + send_byjax(src.occupant,"exosuit.browser","rmicstate",(radio.broadcasting?"Engaged":"Disengaged")) + return + if(href_list["rspktoggle"]) + if(usr != src.occupant) return + radio.listening = !radio.listening + send_byjax(src.occupant,"exosuit.browser","rspkstate",(radio.listening?"Engaged":"Disengaged")) + return + if(href_list["rfreq"]) + if(usr != src.occupant) return + var/new_frequency = (radio.frequency + top_filter.getNum("rfreq")) + if ((radio.frequency < PUBLIC_LOW_FREQ || radio.frequency > PUBLIC_HIGH_FREQ)) + new_frequency = sanitize_frequency(new_frequency) + radio.set_frequency(new_frequency) + send_byjax(src.occupant,"exosuit.browser","rfreq","[format_frequency(radio.frequency)]") + return + if(href_list["port_disconnect"]) + if(usr != src.occupant) return + src.disconnect_from_port() + return + if (href_list["port_connect"]) + if(usr != src.occupant) return + src.connect_to_port() + return + if (href_list["view_log"]) + if(usr != src.occupant) return + src.occupant << browse(src.get_log_html(), "window=exosuit_log") + onclose(occupant, "exosuit_log") + return + if (href_list["change_name"]) + if(usr != src.occupant) return + var/newname = sanitizeSafe(tgui_input_text(occupant,"Choose new exosuit name","Rename exosuit",initial(name), MAX_NAME_LEN), MAX_NAME_LEN) + if(newname) + name = newname + else + tgui_alert_async(occupant, "nope.avi") + return + if (href_list["toggle_id_upload"]) + if(usr != src.occupant) return + add_req_access = !add_req_access + send_byjax(src.occupant,"exosuit.browser","t_id_upload","[add_req_access?"L":"Unl"]ock ID upload panel") + return + if(href_list["toggle_maint_access"]) + if(usr != src.occupant) return + if(state) + occupant_message(span_red("Maintenance protocols in effect")) + return + maint_access = !maint_access + send_byjax(src.occupant,"exosuit.browser","t_maint_access","[maint_access?"Forbid":"Permit"] maintenance protocols") + return + if(href_list["req_access"] && add_req_access) + if(!in_range(src, usr)) return + output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user")) + return + if(href_list["maint_access"] && maint_access) + if(!in_range(src, usr)) return + var/mob/user = top_filter.getMob("user") + if(user) + if(state==MECHA_OPERATING) + state = MECHA_BOLTS_SECURED + to_chat(user, "The securing bolts are now exposed.") + else if(state==MECHA_BOLTS_SECURED) + state = MECHA_OPERATING + to_chat(user, "The securing bolts are now hidden.") + output_maintenance_dialog(top_filter.getObj("id_card"),user) + return + if(href_list["set_internal_tank_valve"] && state >=MECHA_BOLTS_SECURED) + if(!in_range(src, usr)) return + var/mob/user = top_filter.getMob("user") + if(user) + var/new_pressure = tgui_input_number(user,"Input new output pressure","Pressure setting",internal_tank_valve, round_value=FALSE) + if(new_pressure) + internal_tank_valve = new_pressure + to_chat(user, "The internal pressure valve has been set to [internal_tank_valve]kPa.") + if(href_list["remove_passenger"] && state >= MECHA_BOLTS_SECURED) + var/mob/user = top_filter.getMob("user") + var/list/passengers = list() + for (var/obj/item/mecha_parts/mecha_equipment/tool/passenger/P in contents) + if (P.occupant) + passengers["[P.occupant]"] = P + + if (!passengers) + to_chat(user, "There are no passengers to remove.") + return + + var/pname = tgui_input_list(user, "Choose a passenger to forcibly remove.", "Forcibly Remove Passenger", passengers) + + if (!pname) + return + + var/obj/item/mecha_parts/mecha_equipment/tool/passenger/P = passengers[pname] + var/mob/occupant = P.occupant + + user.visible_message("\The [user] begins opening the hatch on \the [P]...", "You begin opening the hatch on \the [P]...") + if (!do_after(user, 40)) + return + + user.visible_message("\The [user] opens the hatch on \the [P] and removes [occupant]!", "You open the hatch on \the [P] and remove [occupant]!") + P.go_out() + P.log_message("[occupant] was removed.") + return + if(href_list["add_req_access"] && add_req_access && top_filter.getObj("id_card")) + if(!in_range(src, usr)) return + operation_req_access += top_filter.getNum("add_req_access") + output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user")) + return + if(href_list["del_req_access"] && add_req_access && top_filter.getObj("id_card")) + if(!in_range(src, usr)) return + operation_req_access -= top_filter.getNum("del_req_access") + output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user")) + return + if(href_list["finish_req_access"]) + if(!in_range(src, usr)) return + add_req_access = 0 + var/mob/user = top_filter.getMob("user") + user << browse(null,"window=exosuit_add_access") + return + if(href_list["dna_lock"]) + if(usr != src.occupant) return + if(istype(occupant, /mob/living/carbon/brain)) + occupant_message("You are a brain. No.") + return + if(src.occupant) + src.dna = src.occupant.dna.unique_enzymes + src.occupant_message("You feel a prick as the needle takes your DNA sample.") + return + if(href_list["reset_dna"]) + if(usr != src.occupant) return + src.dna = null + if(href_list["repair_int_control_lost"]) + if(usr != src.occupant) return + src.occupant_message("Recalibrating coordination system.") + src.log_message("Recalibration of coordination system started.") + var/T = src.loc + if(do_after(100)) + if(T == src.loc) + src.clearInternalDamage(MECHA_INT_CONTROL_LOST) + src.occupant_message(span_blue("Recalibration successful.")) + src.log_message("Recalibration of coordination system finished with 0 errors.") + else + src.occupant_message(span_red("Recalibration failed.")) + src.log_message("Recalibration of coordination system failed with 1 error.",1) + if(href_list["drop_from_cargo"]) + var/obj/O = locate(href_list["drop_from_cargo"]) + if(O && (O in src.cargo)) + src.occupant_message("You unload [O].") + O.forceMove(get_turf(src)) + src.cargo -= O + var/turf/T = get_turf(O) + if(T) + T.Entered(O) + src.log_message("Unloaded [O]. Cargo compartment capacity: [cargo_capacity - src.cargo.len]") + return + + //debug + /* + if(href_list["debug"]) + if(href_list["set_i_dam"]) + setInternalDamage(top_filter.getNum("set_i_dam")) + if(href_list["clear_i_dam"]) + clearInternalDamage(top_filter.getNum("clear_i_dam")) + return + */ + + + +/* + + if (href_list["ai_take_control"]) + var/mob/living/silicon/ai/AI = locate(href_list["ai_take_control"]) + var/duration = text2num(href_list["duration"]) + var/mob/living/silicon/ai/O = new /mob/living/silicon/ai(src) + var/cur_occupant = src.occupant + O.invisibility = 0 + O.canmove = 1 + O.name = AI.name + O.real_name = AI.real_name + O.anchored = TRUE + O.aiRestorePowerRoutine = 0 + O.control_disabled = 1 // Can't control things remotely if you're stuck in a card! + O.laws = AI.laws + O.set_stat(AI.stat) + O.oxyloss = AI.getOxyLoss() + O.fireloss = AI.getFireLoss() + O.bruteloss = AI.getBruteLoss() + O.toxloss = AI.toxloss + O.updatehealth() + src.occupant = O + if(AI.mind) + AI.mind.transfer_to(O) + AI.name = "Inactive AI" + AI.real_name = "Inactive AI" + AI.icon_state = "ai-empty" + spawn(duration) + AI.name = O.name + AI.real_name = O.real_name + if(O.mind) + O.mind.transfer_to(AI) + AI.control_disabled = 0 + AI.laws = O.laws + AI.oxyloss = O.getOxyLoss() + AI.fireloss = O.getFireLoss() + AI.bruteloss = O.getBruteLoss() + AI.toxloss = O.toxloss + AI.updatehealth() + qdel(O) + if (!AI.stat) + AI.icon_state = "ai" + else + AI.icon_state = "ai-crash" + src.occupant = cur_occupant +*/ + +/////////////////////// +///// Power stuff ///// +/////////////////////// + +/obj/mecha/proc/has_charge(amount) + return (get_charge()>=amount) + +/obj/mecha/proc/get_charge() + return call((proc_res["dyngetcharge"]||src), "dyngetcharge")() + +/obj/mecha/proc/dyngetcharge()//returns null if no powercell, else returns cell.charge + if(!src.cell) return + return max(0, src.cell.charge) + +/obj/mecha/proc/use_power(amount) + return call((proc_res["dynusepower"]||src), "dynusepower")(amount) + +/obj/mecha/proc/dynusepower(amount) + update_cell_alerts() + var/obj/item/mecha_parts/component/electrical/EC = internal_components[MECH_ELECTRIC] + + if(EC) + amount = amount * (2 - EC.get_efficiency()) * EC.charge_cost_mod + else + amount *= 5 + + if(get_charge()) + cell.use(amount) + return 1 + return 0 + +/obj/mecha/proc/give_power(amount) + update_cell_alerts() + var/obj/item/mecha_parts/component/electrical/EC = internal_components[MECH_ELECTRIC] + + if(!EC) + amount /= 4 + else + amount *= EC.get_efficiency() + + if(!isnull(get_charge())) + cell.give(amount) + return 1 + return 0 + +//This is for mobs mostly. +/obj/mecha/attack_generic(var/mob/user, var/damage, var/attack_message) + + var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] + + var/temp_deflect_chance = deflect_chance + var/temp_damage_minimum = damage_minimum + + if(!ArmC) + temp_deflect_chance = 1 + temp_damage_minimum = 0 + + else + temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) + temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) + + user.setClickCooldown(user.get_attack_speed()) + if(!damage) + return 0 + + src.log_message("Attacked. Attacker - [user].",1) + user.do_attack_animation(src) + + if(prob(temp_deflect_chance))//Deflected + src.log_append_to_last("Armor saved.") + src.occupant_message("\The [user]'s attack is stopped by the armor.") + visible_message("\The [user] rebounds off [src.name]'s armor!") + user.attack_log += text("\[[time_stamp()]\] [span_red("attacked [src.name]")]") + playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) + + else if(damage < temp_damage_minimum)//Pathetic damage levels just don't harm MECH. + src.occupant_message("\The [user]'s doesn't dent \the [src] paint.") + src.visible_message("\The [user]'s attack doesn't dent \the [src] armor") + src.log_append_to_last("Armor saved.") + playsound(src, 'sound/effects/Glasshit.ogg', 50, 1) + return + + else + src.take_damage(damage) //Apply damage - The take_damage() proc handles armor values + if(damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. + src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + visible_message("[user] [attack_message] [src]!") + user.attack_log += text("\[[time_stamp()]\] [span_red("attacked [src.name]")]") + + return 1 + + +///////////////////////////////////////// +//////// Mecha process() helpers //////// +///////////////////////////////////////// +/obj/mecha/proc/stop_process(process) + current_processes &= ~process + +/obj/mecha/proc/start_process(process) + current_processes |= process + + +///////////// +/obj/mecha/cloak() + . = ..() + if(occupant && occupant.client && cloaked_selfimage) + occupant.client.images += cloaked_selfimage + +/obj/mecha/uncloak() + if(occupant && occupant.client && cloaked_selfimage) + occupant.client.images -= cloaked_selfimage + return ..() + + +//debug +/* +/obj/mecha/verb/test_int_damage() + set name = "Test internal damage" + set category = "Exosuit Interface" + set src in view(0) + if(!occupant) return + if(usr!=occupant) + return + var/output = {" + + + +

                    Set:

                    + MECHA_INT_FIRE
                    + MECHA_INT_TEMP_CONTROL
                    + MECHA_INT_SHORT_CIRCUIT
                    + MECHA_INT_TANK_BREACH
                    + MECHA_INT_CONTROL_LOST
                    +
                    +

                    Clear:

                    + MECHA_INT_FIRE
                    + MECHA_INT_TEMP_CONTROL
                    + MECHA_INT_SHORT_CIRCUIT
                    + MECHA_INT_TANK_BREACH
                    + MECHA_INT_CONTROL_LOST
                    + + "} + + occupant << browse(output, "window=ex_debug") + //src.health = initial(src.health)/2.2 + //src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + return +*/ + +/obj/mecha/proc/update_cell_alerts() + if(occupant && cell) + var/cellcharge = cell.charge/cell.maxcharge + switch(cellcharge) + if(0.75 to INFINITY) + occupant.clear_alert("charge") + if(0.5 to 0.75) + occupant.throw_alert("charge", /obj/screen/alert/lowcell, 1) + if(0.25 to 0.5) + occupant.throw_alert("charge", /obj/screen/alert/lowcell, 2) + if(0.01 to 0.25) + occupant.throw_alert("charge", /obj/screen/alert/lowcell, 3) + else + occupant.throw_alert("charge", /obj/screen/alert/emptycell) + +/obj/mecha/proc/update_damage_alerts() + if(occupant) + var/integrity = health/initial(health)*100 + switch(integrity) + if(30 to 45) + occupant.throw_alert("mech damage", /obj/screen/alert/low_mech_integrity, 1) + if(15 to 35) + occupant.throw_alert("mech damage", /obj/screen/alert/low_mech_integrity, 2) + if(-INFINITY to 15) + occupant.throw_alert("mech damage", /obj/screen/alert/low_mech_integrity, 3) + else + occupant.clear_alert("mech damage") + +/obj/mecha/blob_act(var/obj/structure/blob/B) + var/datum/blob_type/blob = B?.overmind?.blob_type + if(!istype(blob)) + return FALSE + + var/damage = rand(blob.damage_lower, blob.damage_upper) + src.take_damage(damage, blob.damage_type) + visible_message("\The [B] [blob.attack_verb] \the [src]!", "[blob.attack_message_synth]!") + playsound(src, 'sound/effects/attackblob.ogg', 50, 1) + + return ..() diff --git a/code/game/mecha/mecha_construction_paths.dm b/code/game/mecha/mecha_construction_paths.dm index ad2e49e5c46..cc16359a3c5 100644 --- a/code/game/mecha/mecha_construction_paths.dm +++ b/code/game/mecha/mecha_construction_paths.dm @@ -1,2133 +1,2133 @@ -//////////////////////////////// -///// Construction datums ////// -//////////////////////////////// - -/datum/construction/mecha/custom_action(step, obj/item/I, mob/user) - if(I.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/W = I.get_welder() - if(W.remove_fuel(0, user)) - playsound(holder, 'sound/items/Welder2.ogg', 50, 1) - else - return 0 - else if(I.has_tool_quality(TOOL_WRENCH)) - playsound(holder, 'sound/items/Ratchet.ogg', 50, 1) - - else if(I.has_tool_quality(TOOL_SCREWDRIVER)) - playsound(holder, 'sound/items/Screwdriver.ogg', 50, 1) - - else if(I.has_tool_quality(TOOL_WIRECUTTER)) - playsound(holder, 'sound/items/Wirecutter.ogg', 50, 1) - - else if(istype(I, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = I - if(C.use(4)) - playsound(holder, 'sound/items/Deconstruct.ogg', 50, 1) - else - to_chat(user, "There's not enough cable to finish the task.") - return 0 - else if(istype(I, /obj/item/stack)) - var/obj/item/stack/S = I - if(S.get_amount() < 5) - to_chat(user, "There's not enough material in this stack.") - return 0 - else - S.use(5) - return 1 - -/datum/construction/reversible/mecha/custom_action(index as num, diff as num, obj/item/I, mob/user as mob) - if(I.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/W = I.get_welder() - if(W.remove_fuel(0, user)) - playsound(holder, 'sound/items/Welder2.ogg', 50, 1) - else - return 0 - else if(I.has_tool_quality(TOOL_WRENCH)) - playsound(holder, 'sound/items/Ratchet.ogg', 50, 1) - - else if(I.has_tool_quality(TOOL_SCREWDRIVER)) - playsound(holder, 'sound/items/Screwdriver.ogg', 50, 1) - - else if(I.has_tool_quality(TOOL_WIRECUTTER)) - playsound(holder, 'sound/items/Wirecutter.ogg', 50, 1) - - else if(istype(I, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = I - if(C.use(4)) - playsound(holder, 'sound/items/Deconstruct.ogg', 50, 1) - else - to_chat(user, "There's not enough cable to finish the task.") - return 0 - else if(istype(I, /obj/item/stack)) - var/obj/item/stack/S = I - if(S.get_amount() < 5) - to_chat(user, "There's not enough material in this stack.") - return 0 - else - S.use(5) - return 1 - -////////////////////// -// Ripley -////////////////////// -/datum/construction/mecha/ripley_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 - list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/ripley_right_leg)//5 - ) - -/datum/construction/mecha/ripley_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - qdel(I) - return 1 - -/datum/construction/mecha/ripley_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/ripley_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/ripley(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "ripley0" - const_holder.density = TRUE - const_holder.overlays.len = 0 - spawn() - qdel(src) - return - - -/datum/construction/reversible/mecha/ripley - result = "/obj/mecha/working/ripley" - steps = list( - //1 - list("key"=IS_WELDER, - "backkey"=IS_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/material/plasteel, - "backkey"=IS_WELDER, - "desc"="Internal armor is welded."), - //4 - list("key"=IS_WELDER, - "backkey"=IS_WRENCH, - "desc"="Internal armor is wrenched"), - //5 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is installed"), - //6 - list("key"=/obj/item/stack/material/steel, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //7 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //8 - list("key"=/obj/item/weapon/circuitboard/mecha/ripley/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //9 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //10 - list("key"=/obj/item/weapon/circuitboard/mecha/ripley/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //11 - list("key"=IS_WIRECUTTER, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //12 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //13 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //14 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/ripley/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/ripley/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(14) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "ripley1" - if(13) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "ripley2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "ripley0" - if(12) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "ripley3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "ripley1" - if(11) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "ripley4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "ripley2" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "ripley5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "ripley3" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "ripley6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/ripley/main(get_turf(holder)) - holder.icon_state = "ripley4" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "ripley7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "ripley5" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "ripley8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/ripley/peripherals(get_turf(holder)) - holder.icon_state = "ripley6" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") - holder.icon_state = "ripley9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "ripley7" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") - holder.icon_state = "ripley10" - else - user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") - new /obj/item/stack/material/steel(get_turf(holder), 5) - holder.icon_state = "ripley8" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "ripley11" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "ripley9" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs external reinforced armor layer to [holder].", "You install external reinforced armor layer to [holder].") - holder.icon_state = "ripley12" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "ripley10" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") - holder.icon_state = "ripley13" - else - user.visible_message("[user] pries external armor layer from [holder].", "You prie external armor layer from [holder].") - new /obj/item/stack/material/plasteel(get_turf(holder), 5) - holder.icon_state = "ripley11" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds external armor layer to [holder].", "You weld external armor layer to [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "ripley12" - return 1 - -/datum/construction/reversible/mecha/ripley/spawn_result() - ..() - feedback_inc("mecha_ripley_created",1) - return - -////////////////////// -// Gygax -////////////////////// -/datum/construction/mecha/gygax_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/gygax_torso),//1 - list("key"=/obj/item/mecha_parts/part/gygax_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/gygax_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/gygax_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/gygax_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/gygax_head) - ) - -/datum/construction/mecha/gygax_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - qdel(I) - return 1 - -/datum/construction/mecha/gygax_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/gygax_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/gygax(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "gygax0" - const_holder.density = TRUE - spawn() - qdel(src) - return - - -/datum/construction/reversible/mecha/gygax - result = "/obj/mecha/combat/gygax" - steps = list( - //1 - list("key"=IS_WELDER, - "backkey"=IS_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/mecha_parts/part/gygax_armour, - "backkey"=IS_WELDER, - "desc"="Internal armor is welded."), - //4 - list("key"=IS_WELDER, - "backkey"=IS_WRENCH, - "desc"="Internal armor is wrenched"), - //5 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is installed"), - //6 - list("key"=/obj/item/stack/material/steel, - "backkey"=IS_SCREWDRIVER, - "desc"="Advanced capacitor is secured"), - //7 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Advanced capacitor is installed"), - //8 - list("key"=/obj/item/weapon/stock_parts/capacitor/adv, - "backkey"=IS_SCREWDRIVER, - "desc"="Advanced scanner module is secured"), - //9 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Advanced scanner module is installed"), - //10 - list("key"=/obj/item/weapon/stock_parts/scanning_module/adv, - "backkey"=IS_SCREWDRIVER, - "desc"="Targeting module is secured"), - //11 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Targeting module is installed"), - //12 - list("key"=/obj/item/weapon/circuitboard/mecha/gygax/targeting, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //13 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //14 - list("key"=/obj/item/weapon/circuitboard/mecha/gygax/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //15 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //16 - list("key"=/obj/item/weapon/circuitboard/mecha/gygax/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //17 - list("key"=IS_WIRECUTTER, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //18 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //19 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //20 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/gygax/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/gygax/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(20) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "gygax1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "gygax2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "gygax0" - if(18) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "gygax3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "gygax1" - if(17) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "gygax4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "gygax2" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "gygax5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "gygax3" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "gygax6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/gygax/main(get_turf(holder)) - holder.icon_state = "gygax4" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "gygax7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "gygax5" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "gygax8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/gygax/peripherals(get_turf(holder)) - holder.icon_state = "gygax6" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") - qdel(I) - holder.icon_state = "gygax9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "gygax7" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "gygax10" - else - user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/gygax/targeting(get_turf(holder)) - holder.icon_state = "gygax8" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs advanced scanner module to [holder].", "You install advanced scanner module to [holder].") - qdel(I) - holder.icon_state = "gygax11" - else - user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") - holder.icon_state = "gygax9" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") - holder.icon_state = "gygax12" - else - user.visible_message("[user] removes the advanced scanner module from [holder].", "You remove the advanced scanner module from [holder].") - new /obj/item/weapon/stock_parts/scanning_module/adv(get_turf(holder)) - holder.icon_state = "gygax10" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs advanced capacitor to [holder].", "You install advanced capacitor to [holder].") - qdel(I) - holder.icon_state = "gygax13" - else - user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") - holder.icon_state = "gygax11" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") - holder.icon_state = "gygax14" - else - user.visible_message("[user] removes the advanced capacitor from [holder].", "You remove the advanced capacitor from [holder].") - new /obj/item/weapon/stock_parts/capacitor/adv(get_turf(holder)) - holder.icon_state = "gygax12" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") - holder.icon_state = "gygax15" - else - user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") - holder.icon_state = "gygax13" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") - holder.icon_state = "gygax16" - else - user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") - new /obj/item/stack/material/steel(get_turf(holder), 5) - holder.icon_state = "gygax14" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "gygax17" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "gygax15" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs Gygax Armour Plates to [holder].", "You install Gygax Armour Plates to [holder].") - qdel(I) - holder.icon_state = "gygax18" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "gygax16" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures Gygax Armour Plates.", "You secure Gygax Armour Plates.") - holder.icon_state = "gygax19" - else - user.visible_message("[user] pries Gygax Armour Plates from [holder].", "You prie Gygax Armour Plates from [holder].") - new /obj/item/mecha_parts/part/gygax_armour(get_turf(holder)) - holder.icon_state = "gygax17" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds Gygax Armour Plates to [holder].", "You weld Gygax Armour Plates to [holder].") - else - user.visible_message("[user] unfastens Gygax Armour Plates.", "You unfasten Gygax Armour Plates.") - holder.icon_state = "gygax18" - return 1 - -/datum/construction/reversible/mecha/gygax/spawn_result() - ..() - feedback_inc("mecha_gygax_created",1) - return - - - ////////////////////// -// Serenity -////////////////////// -/datum/construction/mecha/serenity_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/gygax_torso),//1 - list("key"=/obj/item/mecha_parts/part/gygax_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/gygax_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/gygax_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/gygax_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/gygax_head) - ) - -/datum/construction/mecha/serenity_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - qdel(I) - return 1 - -/datum/construction/mecha/serenity_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/serenity_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/serenity(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "gygax0" - const_holder.density = TRUE - spawn() - qdel(src) - return - - -/datum/construction/reversible/mecha/serenity - result = "/obj/mecha/combat/gygax/serenity" - steps = list( - //1 - list("key"=IS_WELDER, - "backkey"=IS_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/material/plasteel, - "backkey"=IS_WELDER, - "desc"="Internal armor is welded."), - //4 - list("key"=IS_WELDER, - "backkey"=IS_WRENCH, - "desc"="Internal armor is wrenched"), - //5 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is installed"), - //6 - list("key"=/obj/item/stack/material/steel, - "backkey"=IS_SCREWDRIVER, - "desc"="Advanced capacitor is secured"), - //7 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Advanced capacitor is installed"), - //8 - list("key"=/obj/item/weapon/stock_parts/capacitor/adv, - "backkey"=IS_SCREWDRIVER, - "desc"="Advanced scanner module is secured"), - //9 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Advanced scanner module is installed"), - //10 - list("key"=/obj/item/weapon/stock_parts/scanning_module/adv, - "backkey"=IS_SCREWDRIVER, - "desc"="Medical module is secured"), - //11 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Medical module is installed"), - //12 - list("key"=/obj/item/weapon/circuitboard/mecha/gygax/medical, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //13 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //14 - list("key"=/obj/item/weapon/circuitboard/mecha/gygax/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //15 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //16 - list("key"=/obj/item/weapon/circuitboard/mecha/gygax/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //17 - list("key"=IS_WIRECUTTER, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //18 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //19 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //20 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/serenity/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/serenity/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(20) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "gygax1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "gygax2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "gygax0" - if(18) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "gygax3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "gygax1" - if(17) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "gygax4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "gygax2" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "gygax5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "gygax3" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "gygax6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/gygax/main(get_turf(holder)) - holder.icon_state = "gygax4" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "gygax7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "gygax5" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "gygax8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/gygax/peripherals(get_turf(holder)) - holder.icon_state = "gygax6" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the medical control module into [holder].", "You install the medical control module into [holder].") - qdel(I) - holder.icon_state = "gygax9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "gygax7" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the medical control module.", "You secure the medical control module.") - holder.icon_state = "gygax10" - else - user.visible_message("[user] removes the medical control module from [holder].", "You remove the medical control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/gygax/medical(get_turf(holder)) - holder.icon_state = "gygax8" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs advanced scanner module to [holder].", "You install advanced scanner module to [holder].") - qdel(I) - holder.icon_state = "gygax11" - else - user.visible_message("[user] unfastens the medical control module.", "You unfasten the medical control module.") - holder.icon_state = "gygax9" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") - holder.icon_state = "gygax12" - else - user.visible_message("[user] removes the advanced scanner module from [holder].", "You remove the advanced scanner module from [holder].") - new /obj/item/weapon/stock_parts/scanning_module/adv(get_turf(holder)) - holder.icon_state = "gygax10" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs advanced capacitor to [holder].", "You install advanced capacitor to [holder].") - qdel(I) - holder.icon_state = "gygax13" - else - user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") - holder.icon_state = "gygax11" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") - holder.icon_state = "gygax14" - else - user.visible_message("[user] removes the advanced capacitor from [holder].", "You remove the advanced capacitor from [holder].") - new /obj/item/weapon/stock_parts/capacitor/adv(get_turf(holder)) - holder.icon_state = "gygax12" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") - holder.icon_state = "gygax15" - else - user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") - holder.icon_state = "gygax13" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") - holder.icon_state = "gygax16" - else - user.visible_message("[user] pries internal armor layer from [holder].", "You pry the internal armor layer from [holder].") - new /obj/item/stack/material/steel(get_turf(holder), 5) - holder.icon_state = "gygax14" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "gygax17" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "gygax15" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs the external armor layer to [holder].", "You install the external armor layer to [holder].") - holder.icon_state = "gygax18" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "gygax16" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures the external armor layer.", "You secure the external armor layer.") - holder.icon_state = "gygax19-s" - else - user.visible_message("[user] pries the external armor layer from [holder].", "You pry the external armor layer from [holder].") - new /obj/item/stack/material/plasteel(get_turf(holder), 5) // Fixes serenity giving Gygax Armor Plates for the reverse action... - holder.icon_state = "gygax17" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds the external armor layer to [holder].", "You weld the external armor layer to [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "gygax18" - return 1 - -/datum/construction/reversible/mecha/serenity/spawn_result() - ..() - feedback_inc("mecha_serenity_created",1) - return - - - -//////////////////////// -// Firefighter -//////////////////////// -/datum/construction/mecha/firefighter_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 - list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/ripley_right_leg),//5 - list("key"=/obj/item/clothing/suit/fire)//6 - ) - -/datum/construction/mecha/firefighter_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - user.drop_item() - qdel(I) - return 1 - -/datum/construction/mecha/firefighter_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/firefighter_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/firefighter(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "fireripley0" - const_holder.density = TRUE - spawn() - qdel(src) - return - - -/datum/construction/reversible/mecha/firefighter - result = "/obj/mecha/working/ripley/firefighter" - steps = list( - //1 - list("key"=IS_WELDER, - "backkey"=IS_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/material/plasteel, - "backkey"=IS_CROWBAR, - "desc"="External armor is being installed."), - //4 - list("key"=/obj/item/stack/material/plasteel, - "backkey"=IS_WELDER, - "desc"="Internal armor is welded."), - //5 - list("key"=IS_WELDER, - "backkey"=IS_WRENCH, - "desc"="Internal armor is wrenched"), - //6 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is installed"), - //7 - list("key"=/obj/item/stack/material/plasteel, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //8 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //9 - list("key"=/obj/item/weapon/circuitboard/mecha/ripley/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //10 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //11 - list("key"=/obj/item/weapon/circuitboard/mecha/ripley/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //12 - list("key"=IS_WIRECUTTER, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //13 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //14 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //15 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/firefighter/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/firefighter/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(15) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "fireripley1" - if(14) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "fireripley2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "fireripley0" - if(13) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "fireripley3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "fireripley1" - if(12) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "fireripley4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "fireripley2" - if(11) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "fireripley5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "fireripley3" - if(10) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "fireripley6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/ripley/main(get_turf(holder)) - holder.icon_state = "fireripley4" - if(9) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "fireripley7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "fireripley5" - if(8) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "fireripley8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/ripley/peripherals(get_turf(holder)) - holder.icon_state = "fireripley6" - if(7) - if(diff==FORWARD) - user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") - holder.icon_state = "fireripley9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "fireripley7" - if(6) - if(diff==FORWARD) - user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") - holder.icon_state = "fireripley10" - else - user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") - new /obj/item/stack/material/plasteel(get_turf(holder), 5) - holder.icon_state = "fireripley8" - if(5) - if(diff==FORWARD) - user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "fireripley11" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "fireripley9" - if(4) - if(diff==FORWARD) - user.visible_message("[user] starts to install the external armor layer to [holder].", "You start to install the external armor layer to [holder].") - holder.icon_state = "fireripley12" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "fireripley10" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs external reinforced armor layer to [holder].", "You install external reinforced armor layer to [holder].") - holder.icon_state = "fireripley13" - else - user.visible_message("[user] removes the external armor from [holder].", "You remove the external armor from [holder].") - new /obj/item/stack/material/plasteel(get_turf(holder), 5) - holder.icon_state = "fireripley11" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") - holder.icon_state = "fireripley14" - else - user.visible_message("[user] pries external armor layer from [holder].", "You prie external armor layer from [holder].") - new /obj/item/stack/material/plasteel(get_turf(holder), 5) - holder.icon_state = "fireripley12" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds external armor layer to [holder].", "You weld external armor layer to [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "fireripley13" - return 1 - -/datum/construction/reversible/mecha/firefighter/spawn_result() - ..() - feedback_inc("mecha_firefighter_created",1) - return - -////////////////////// -// Durand -////////////////////// -/datum/construction/mecha/durand_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/durand_torso),//1 - list("key"=/obj/item/mecha_parts/part/durand_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/durand_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/durand_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/durand_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/durand_head) - ) - -/datum/construction/mecha/durand_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - qdel(I) - return 1 - -/datum/construction/mecha/durand_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/durand_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/durand(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "durand0" - const_holder.density = TRUE - spawn() - qdel(src) - return - - -/datum/construction/reversible/mecha/durand - result = "/obj/mecha/combat/durand" - steps = list( - //1 - list("key"=IS_WELDER, - "backkey"=IS_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/mecha_parts/part/durand_armour, - "backkey"=IS_WELDER, - "desc"="Internal armor is welded."), - //4 - list("key"=IS_WELDER, - "backkey"=IS_WRENCH, - "desc"="Internal armor is wrenched"), - //5 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is installed"), - //6 - list("key"=/obj/item/stack/material/steel, - "backkey"=IS_SCREWDRIVER, - "desc"="Advanced capacitor is secured"), - //7 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Advanced capacitor is installed"), - //8 - list("key"=/obj/item/weapon/stock_parts/capacitor/adv, - "backkey"=IS_SCREWDRIVER, - "desc"="Advanced scanner module is secured"), - //9 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Advanced scanner module is installed"), - //10 - list("key"=/obj/item/weapon/stock_parts/scanning_module/adv, - "backkey"=IS_SCREWDRIVER, - "desc"="Targeting module is secured"), - //11 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Targeting module is installed"), - //12 - list("key"=/obj/item/weapon/circuitboard/mecha/durand/targeting, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //13 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //14 - list("key"=/obj/item/weapon/circuitboard/mecha/durand/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //15 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //16 - list("key"=/obj/item/weapon/circuitboard/mecha/durand/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //17 - list("key"=IS_WIRECUTTER, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //18 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //19 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //20 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - - -/datum/construction/reversible/mecha/durand/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/durand/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(20) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "durand1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "durand2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "durand0" - if(18) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "durand3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "durand1" - if(17) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "durand4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "durand2" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "durand5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "durand3" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "durand6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/durand/main(get_turf(holder)) - holder.icon_state = "durand4" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "durand7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "durand5" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "durand8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/durand/peripherals(get_turf(holder)) - holder.icon_state = "durand6" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") - qdel(I) - holder.icon_state = "durand9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "durand7" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "durand10" - else - user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/durand/targeting(get_turf(holder)) - holder.icon_state = "durand8" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs advanced scanner module to [holder].", "You install advanced scanner module to [holder].") - qdel(I) - holder.icon_state = "durand11" - else - user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") - holder.icon_state = "durand9" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") - holder.icon_state = "durand12" - else - user.visible_message("[user] removes the advanced scanner module from [holder].", "You remove the advanced scanner module from [holder].") - new /obj/item/weapon/stock_parts/scanning_module/adv(get_turf(holder)) - holder.icon_state = "durand10" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs advanced capacitor to [holder].", "You install advanced capacitor to [holder].") - qdel(I) - holder.icon_state = "durand13" - else - user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") - holder.icon_state = "durand11" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") - holder.icon_state = "durand14" - else - user.visible_message("[user] removes the advanced capacitor from [holder].", "You remove the advanced capacitor from [holder].") - new /obj/item/weapon/stock_parts/capacitor/adv(get_turf(holder)) - holder.icon_state = "durand12" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") - holder.icon_state = "durand15" - else - user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") - holder.icon_state = "durand13" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") - holder.icon_state = "durand16" - else - user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") - new /obj/item/stack/material/steel(get_turf(holder), 5) - holder.icon_state = "durand14" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "durand17" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "durand15" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs Durand Armour Plates to [holder].", "You install Durand Armour Plates to [holder].") - qdel(I) - holder.icon_state = "durand18" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "durand16" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures Durand Armour Plates.", "You secure Durand Armour Plates.") - holder.icon_state = "durand19" - else - user.visible_message("[user] pries Durand Armour Plates from [holder].", "You prie Durand Armour Plates from [holder].") - new /obj/item/mecha_parts/part/durand_armour(get_turf(holder)) - holder.icon_state = "durand17" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds Durand Armour Plates to [holder].", "You weld Durand Armour Plates to [holder].") - else - user.visible_message("[user] unfastens Durand Armour Plates.", "You unfasten Durand Armour Plates.") - holder.icon_state = "durand18" - return 1 - -/datum/construction/reversible/mecha/durand/spawn_result() - ..() - feedback_inc("mecha_durand_created",1) - return - -//////////////////////// -// Odysseus -//////////////////////// -/datum/construction/mecha/odysseus_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/odysseus_torso),//1 - list("key"=/obj/item/mecha_parts/part/odysseus_head),//2 - list("key"=/obj/item/mecha_parts/part/odysseus_left_arm),//3 - list("key"=/obj/item/mecha_parts/part/odysseus_right_arm),//4 - list("key"=/obj/item/mecha_parts/part/odysseus_left_leg),//5 - list("key"=/obj/item/mecha_parts/part/odysseus_right_leg)//6 - ) - -/datum/construction/mecha/odysseus_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - qdel(I) - return 1 - -/datum/construction/mecha/odysseus_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/odysseus_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/odysseus(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "odysseus0" - const_holder.density = TRUE - spawn() - qdel(src) - return - - -/datum/construction/reversible/mecha/odysseus - result = "/obj/mecha/medical/odysseus" - steps = list( - //1 - list("key"=IS_WELDER, - "backkey"=IS_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/material/plasteel, - "backkey"=IS_WELDER, - "desc"="Internal armor is welded."), - //4 - list("key"=IS_WELDER, - "backkey"=IS_WRENCH, - "desc"="Internal armor is wrenched"), - //5 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is installed"), - //6 - list("key"=/obj/item/stack/material/steel, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //7 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //8 - list("key"=/obj/item/weapon/circuitboard/mecha/odysseus/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //9 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //10 - list("key"=/obj/item/weapon/circuitboard/mecha/odysseus/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //11 - list("key"=IS_WIRECUTTER, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //12 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //13 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //14 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/odysseus/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/odysseus/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(14) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "odysseus1" - if(13) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "odysseus2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "odysseus0" - if(12) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "odysseus3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "odysseus1" - if(11) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "odysseus4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "odysseus2" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "odysseus5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "odysseus3" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "odysseus6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/odysseus/main(get_turf(holder)) - holder.icon_state = "odysseus4" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "odysseus7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "odysseus5" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "odysseus8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/odysseus/peripherals(get_turf(holder)) - holder.icon_state = "odysseus6" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") - holder.icon_state = "odysseus9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "odysseus7" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") - holder.icon_state = "odysseus10" - else - user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") - new /obj/item/stack/material/steel(get_turf(holder), 5) - holder.icon_state = "odysseus8" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "odysseus11" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "odysseus9" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs [I] layer to [holder].", "You install external reinforced armor layer to [holder].") - holder.icon_state = "odysseus12" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "odysseus10" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") - holder.icon_state = "odysseus13" - else - new /obj/item/stack/material/plasteel(get_turf(holder), 5) - user.visible_message("[user] pries the plasteel from [holder].", "You prie the plasteel from [holder].") - holder.icon_state = "odysseus11" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds external armor layer to [holder].", "You weld external armor layer to [holder].") - holder.icon_state = "odysseus14" - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "odysseus12" - return 1 - -/datum/construction/reversible/mecha/odysseus/spawn_result() - ..() - feedback_inc("mecha_odysseus_created",1) - return - -////////////////////// -// Phazon -////////////////////// -/datum/construction/mecha/phazon_chassis - result = "/obj/mecha/combat/phazon" - steps = list(list("key"=/obj/item/mecha_parts/part/phazon_torso),//1 - list("key"=/obj/item/mecha_parts/part/phazon_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/phazon_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/phazon_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/phazon_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/phazon_head) - ) - -/datum/construction/mecha/phazon_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - qdel(I) - return 1 - -/datum/construction/mecha/phazon_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/phazon_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/phazon(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "phazon0" - const_holder.density = TRUE - spawn() - qdel(src) - return - -/datum/construction/reversible/mecha/phazon - result = "/obj/mecha/combat/phazon" - steps = list( - //1 - list("key"=IS_WELDER, - "backkey"=IS_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/material/plasteel, - "backkey"=IS_WELDER, - "desc"="Internal armor is welded."), - //4 - list("key"=IS_WELDER, - "backkey"=IS_WRENCH, - "desc"="Internal armor is wrenched"), - //5 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is installed"), - //6 - list("key"=/obj/item/stack/material/steel, - "backkey"=IS_SCREWDRIVER, - "desc"="Hand teleporter is secured"), - //7 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Hand teleporter is installed"), - //8 - list("key"=/obj/item/weapon/hand_tele, - "backkey"=IS_SCREWDRIVER, - "desc"="SMES coil is secured"), - //9 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="SMES coil is installed"), - //10 - list("key"=/obj/item/weapon/smes_coil/super_capacity, - "backkey"=IS_SCREWDRIVER, - "desc"="Targeting module is secured"), - //11 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Targeting module is installed"), - //12 - list("key"=/obj/item/weapon/circuitboard/mecha/phazon/targeting, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //13 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //14 - list("key"=/obj/item/weapon/circuitboard/mecha/phazon/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //15 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //16 - list("key"=/obj/item/weapon/circuitboard/mecha/phazon/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //17 - list("key"=IS_WIRECUTTER, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //18 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //19 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //20 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/phazon/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/phazon/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - switch(index) - if(20) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "phazon1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "phazon2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "phazon0" - if(18) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "phazon3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "phazon1" - if(17) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "phazon4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "phazon2" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "phazon5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "phazon3" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "phazon6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/phazon/main(get_turf(holder)) - holder.icon_state = "phazon4" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "phazon7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "phazon5" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "phazon8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/phazon/peripherals(get_turf(holder)) - holder.icon_state = "phazon6" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") - qdel(I) - holder.icon_state = "phazon9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "phazon7" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "phazon10" - else - user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/phazon/targeting(get_turf(holder)) - holder.icon_state = "phazon8" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the SMES coil to [holder].", "You install the SMES coil to [holder].") - qdel(I) - holder.icon_state = "phazon11" - else - user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") - holder.icon_state = "phazon9" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the SMES coil.", "You secure the SMES coil.") - holder.icon_state = "phazon12" - else - user.visible_message("[user] removes the SMES coil from [holder].", "You remove the SMES coil from [holder].") - new /obj/item/weapon/smes_coil/super_capacity(get_turf(holder)) - holder.icon_state = "phazon10" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs the hand teleporter to [holder].", "You install the hand teleporter to [holder].") - qdel(I) - holder.icon_state = "phazon13" - else - user.visible_message("[user] unfastens the SMES coil.", "You unfasten the SMES coil.") - holder.icon_state = "phazon11" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the hand teleporter.", "You secure the hand teleporter.") - holder.icon_state = "phazon14" - else - user.visible_message("[user] removes the hand teleporter from [holder].", "You remove the hand teleporter from [holder].") - new /obj/item/weapon/hand_tele(get_turf(holder)) - holder.icon_state = "phazon12" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to [holder].", "You install the internal armor layer to [holder].") - holder.icon_state = "phazon19" - else - user.visible_message("[user] unfastens the hand teleporter.", "You unfasten the hand teleporter.") - holder.icon_state = "phazon13" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "phazon20" - else - user.visible_message("[user] pries the internal armor layer from [holder].", "You pry the internal armor layer from [holder].") - new /obj/item/stack/material/steel(get_turf(holder), 5) - holder.icon_state = "phazon14" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "phazon21" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "phazon19" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs the external reinforced armor layer to [holder].", "You install the external reinforced armor layer to [holder].") - holder.icon_state = "phazon22" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "phazon20" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") - holder.icon_state = "phazon23" - else - user.visible_message("[user] pries the external armor layer from [holder].", "You pry external armor layer from [holder].") - new /obj/item/stack/material/plasteel(get_turf(holder), 5) - holder.icon_state = "phazon21" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds the external armor layer to [holder].", "You weld the external armor layer to [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "phazon22" - return 1 - -/datum/construction/reversible/mecha/phazon/spawn_result() - ..() - feedback_inc("mecha_phazon_created",1) - return - -////////////////////// -// Janus -////////////////////// -/datum/construction/mecha/janus_chassis - result = "/obj/mecha/combat/phazon/janus" - steps = list(list("key"=/obj/item/mecha_parts/part/janus_torso),//1 - list("key"=/obj/item/mecha_parts/part/janus_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/janus_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/janus_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/janus_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/janus_head) - ) - -/datum/construction/mecha/janus_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - qdel(I) - return 1 - -/datum/construction/mecha/janus_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/janus_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/janus(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "janus0" - const_holder.density = TRUE - spawn() - qdel(src) - return - -/datum/construction/reversible/mecha/janus - result = "/obj/mecha/combat/phazon/janus" - steps = list( - //1 - list("key"=IS_WELDER, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is attached."), - //3 - list("key"=/obj/item/stack/material/morphium, - "backkey"=IS_WELDER, - "desc"="Internal armor is welded"), - //4 - list("key"=IS_WELDER, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is wrenched"), - //5 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is attached."), - //6 - list("key"=/obj/item/stack/material/durasteel, - "backkey"=IS_SCREWDRIVER, - "desc"="Durand auxiliary board is secured."), - //7 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Durand auxiliary board is installed"), - //8 - list("key"=/obj/item/weapon/circuitboard/mecha/durand/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Phase coil is secured"), - //9 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Phase coil is installed"), - //10 - list("key"=/obj/item/prop/alien/phasecoil, - "backkey"=IS_SCREWDRIVER, - "desc"="Gygax balance system secured"), - //11 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Gygax balance system installed"), - //12 - list("key"=/obj/item/weapon/circuitboard/mecha/gygax/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Targeting module is secured"), - //13 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Targeting module is installed"), - //14 - list("key"=/obj/item/weapon/circuitboard/mecha/imperion/targeting, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //15 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //16 - list("key"=/obj/item/weapon/circuitboard/mecha/imperion/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //17 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //18 - list("key"=/obj/item/weapon/circuitboard/mecha/imperion/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //19 - list("key"=IS_WIRECUTTER, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //20 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //21 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //22 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/janus/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/janus/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - switch(index) - if(22) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "janus1" - if(21) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "janus2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "janus0" - if(20) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "janus3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "janus1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "janus4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "janus2" - if(18) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "janus5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "janus3" - if(17) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "janus6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/imperion/main(get_turf(holder)) - holder.icon_state = "janus4" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "janus7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "janus5" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "janus8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/imperion/peripherals(get_turf(holder)) - holder.icon_state = "janus6" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") - qdel(I) - holder.icon_state = "janus9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "janus7" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "janus10" - else - user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/imperion/targeting(get_turf(holder)) - holder.icon_state = "janus8" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the Gygax control module into [holder].", "You install the Gygax control module into [holder].") - qdel(I) - holder.icon_state = "janus11" - else - user.visible_message("[user] unfastens the Gygax control module.", "You unfasten the Gygax control module.") - holder.icon_state = "janus9" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the Gygax control module.", "You secure the Gygax control module.") - holder.icon_state = "janus12" - else - user.visible_message("[user] removes the Gygax control module from [holder].", "You remove the Gygax control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/gygax/peripherals(get_turf(holder)) - holder.icon_state = "janus10" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the phase coil into [holder].", "You install the phase coil into [holder].") - qdel(I) - holder.icon_state = "janus13" - else - user.visible_message("[user] unfastens the Gygax control module.", "You unfasten the Gygax control module.") - holder.icon_state = "janus11" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the phase coil.", "You secure the phase coil.") - holder.icon_state = "janus14" - else - user.visible_message("[user] removes the phase coil from [holder].", "You remove the phase coil from [holder].") - new /obj/item/prop/alien/phasecoil(get_turf(holder)) - holder.icon_state = "janus12" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs the Durand control module into [holder].", "You install the Durand control module into [holder].") - qdel(I) - holder.icon_state = "janus15" - else - user.visible_message("[user] unfastens the phase coil.", "You unfasten the phase coil.") - holder.icon_state = "janus13" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the Durand control module.", "You secure the Durand control module.") - holder.icon_state = "janus16" - else - user.visible_message("[user] removes the Durand control module from [holder].", "You remove the Durand control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/durand/peripherals(get_turf(holder)) - holder.icon_state = "janus14" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to [holder].", "You install the internal armor layer to [holder].") - holder.icon_state = "janus17" - else - user.visible_message("[user] unfastens the Durand control module.", "You unfasten the Durand control module.") - holder.icon_state = "janus15" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "janus18" - else - user.visible_message("[user] pries the internal armor layer from [holder].", "You pry the internal armor layer from [holder].") - new /obj/item/stack/material/durasteel(get_turf(holder), 5) - holder.icon_state = "janus16" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "janus19" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "janus17" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs the external reinforced armor layer to [holder].", "You install the external reinforced armor layer to [holder].") - holder.icon_state = "janus20" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "janus18" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") - holder.icon_state = "janus21" - else - user.visible_message("[user] pries the external armor layer from [holder].", "You pry external armor layer from [holder].") - new /obj/item/stack/material/morphium(get_turf(holder), 5) - holder.icon_state = "janus19" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds the external armor layer to [holder].", "You weld the external armor layer to [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "janus20" - return 1 - -/datum/construction/reversible/mecha/janus/spawn_result() - ..() - feedback_inc("mecha_janus_created",1) - return +//////////////////////////////// +///// Construction datums ////// +//////////////////////////////// + +/datum/construction/mecha/custom_action(step, obj/item/I, mob/user) + if(I.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/W = I.get_welder() + if(W.remove_fuel(0, user)) + playsound(holder, 'sound/items/Welder2.ogg', 50, 1) + else + return 0 + else if(I.has_tool_quality(TOOL_WRENCH)) + playsound(holder, 'sound/items/Ratchet.ogg', 50, 1) + + else if(I.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(holder, 'sound/items/Screwdriver.ogg', 50, 1) + + else if(I.has_tool_quality(TOOL_WIRECUTTER)) + playsound(holder, 'sound/items/Wirecutter.ogg', 50, 1) + + else if(istype(I, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = I + if(C.use(4)) + playsound(holder, 'sound/items/Deconstruct.ogg', 50, 1) + else + to_chat(user, "There's not enough cable to finish the task.") + return 0 + else if(istype(I, /obj/item/stack)) + var/obj/item/stack/S = I + if(S.get_amount() < 5) + to_chat(user, "There's not enough material in this stack.") + return 0 + else + S.use(5) + return 1 + +/datum/construction/reversible/mecha/custom_action(index as num, diff as num, obj/item/I, mob/user as mob) + if(I.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/W = I.get_welder() + if(W.remove_fuel(0, user)) + playsound(holder, 'sound/items/Welder2.ogg', 50, 1) + else + return 0 + else if(I.has_tool_quality(TOOL_WRENCH)) + playsound(holder, 'sound/items/Ratchet.ogg', 50, 1) + + else if(I.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(holder, 'sound/items/Screwdriver.ogg', 50, 1) + + else if(I.has_tool_quality(TOOL_WIRECUTTER)) + playsound(holder, 'sound/items/Wirecutter.ogg', 50, 1) + + else if(istype(I, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = I + if(C.use(4)) + playsound(holder, 'sound/items/Deconstruct.ogg', 50, 1) + else + to_chat(user, "There's not enough cable to finish the task.") + return 0 + else if(istype(I, /obj/item/stack)) + var/obj/item/stack/S = I + if(S.get_amount() < 5) + to_chat(user, "There's not enough material in this stack.") + return 0 + else + S.use(5) + return 1 + +////////////////////// +// Ripley +////////////////////// +/datum/construction/mecha/ripley_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 + list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/ripley_right_leg)//5 + ) + +/datum/construction/mecha/ripley_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + qdel(I) + return 1 + +/datum/construction/mecha/ripley_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/ripley_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/ripley(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "ripley0" + const_holder.density = TRUE + const_holder.overlays.len = 0 + spawn() + qdel(src) + return + + +/datum/construction/reversible/mecha/ripley + result = "/obj/mecha/working/ripley" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/material/plasteel, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="Internal armor is wrenched"), + //5 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is installed"), + //6 + list("key"=/obj/item/stack/material/steel, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //7 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //8 + list("key"=/obj/item/weapon/circuitboard/mecha/ripley/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //9 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //10 + list("key"=/obj/item/weapon/circuitboard/mecha/ripley/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //11 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //12 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //13 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //14 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/ripley/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/ripley/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(14) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "ripley1" + if(13) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "ripley2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "ripley0" + if(12) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "ripley3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "ripley1" + if(11) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "ripley4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "ripley2" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "ripley5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "ripley3" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "ripley6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/ripley/main(get_turf(holder)) + holder.icon_state = "ripley4" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "ripley7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "ripley5" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "ripley8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/ripley/peripherals(get_turf(holder)) + holder.icon_state = "ripley6" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") + holder.icon_state = "ripley9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "ripley7" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") + holder.icon_state = "ripley10" + else + user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") + new /obj/item/stack/material/steel(get_turf(holder), 5) + holder.icon_state = "ripley8" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "ripley11" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "ripley9" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs external reinforced armor layer to [holder].", "You install external reinforced armor layer to [holder].") + holder.icon_state = "ripley12" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "ripley10" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") + holder.icon_state = "ripley13" + else + user.visible_message("[user] pries external armor layer from [holder].", "You prie external armor layer from [holder].") + new /obj/item/stack/material/plasteel(get_turf(holder), 5) + holder.icon_state = "ripley11" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds external armor layer to [holder].", "You weld external armor layer to [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "ripley12" + return 1 + +/datum/construction/reversible/mecha/ripley/spawn_result() + ..() + feedback_inc("mecha_ripley_created",1) + return + +////////////////////// +// Gygax +////////////////////// +/datum/construction/mecha/gygax_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/gygax_torso),//1 + list("key"=/obj/item/mecha_parts/part/gygax_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/gygax_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/gygax_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/gygax_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/gygax_head) + ) + +/datum/construction/mecha/gygax_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + qdel(I) + return 1 + +/datum/construction/mecha/gygax_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/gygax_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/gygax(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "gygax0" + const_holder.density = TRUE + spawn() + qdel(src) + return + + +/datum/construction/reversible/mecha/gygax + result = "/obj/mecha/combat/gygax" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/mecha_parts/part/gygax_armour, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="Internal armor is wrenched"), + //5 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is installed"), + //6 + list("key"=/obj/item/stack/material/steel, + "backkey"=IS_SCREWDRIVER, + "desc"="Advanced capacitor is secured"), + //7 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Advanced capacitor is installed"), + //8 + list("key"=/obj/item/weapon/stock_parts/capacitor/adv, + "backkey"=IS_SCREWDRIVER, + "desc"="Advanced scanner module is secured"), + //9 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Advanced scanner module is installed"), + //10 + list("key"=/obj/item/weapon/stock_parts/scanning_module/adv, + "backkey"=IS_SCREWDRIVER, + "desc"="Targeting module is secured"), + //11 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Targeting module is installed"), + //12 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/targeting, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //13 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //14 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //15 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //16 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //17 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //18 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //19 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //20 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/gygax/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/gygax/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(20) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "gygax2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "gygax0" + if(18) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "gygax3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(17) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "gygax4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "gygax2" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "gygax5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "gygax3" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "gygax6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/main(get_turf(holder)) + holder.icon_state = "gygax4" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "gygax7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "gygax5" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "gygax8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/peripherals(get_turf(holder)) + holder.icon_state = "gygax6" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") + qdel(I) + holder.icon_state = "gygax9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "gygax7" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "gygax10" + else + user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/targeting(get_turf(holder)) + holder.icon_state = "gygax8" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs advanced scanner module to [holder].", "You install advanced scanner module to [holder].") + qdel(I) + holder.icon_state = "gygax11" + else + user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") + holder.icon_state = "gygax9" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") + holder.icon_state = "gygax12" + else + user.visible_message("[user] removes the advanced scanner module from [holder].", "You remove the advanced scanner module from [holder].") + new /obj/item/weapon/stock_parts/scanning_module/adv(get_turf(holder)) + holder.icon_state = "gygax10" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs advanced capacitor to [holder].", "You install advanced capacitor to [holder].") + qdel(I) + holder.icon_state = "gygax13" + else + user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") + holder.icon_state = "gygax11" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") + holder.icon_state = "gygax14" + else + user.visible_message("[user] removes the advanced capacitor from [holder].", "You remove the advanced capacitor from [holder].") + new /obj/item/weapon/stock_parts/capacitor/adv(get_turf(holder)) + holder.icon_state = "gygax12" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") + holder.icon_state = "gygax15" + else + user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") + holder.icon_state = "gygax13" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") + holder.icon_state = "gygax16" + else + user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") + new /obj/item/stack/material/steel(get_turf(holder), 5) + holder.icon_state = "gygax14" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "gygax17" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "gygax15" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs Gygax Armour Plates to [holder].", "You install Gygax Armour Plates to [holder].") + qdel(I) + holder.icon_state = "gygax18" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "gygax16" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures Gygax Armour Plates.", "You secure Gygax Armour Plates.") + holder.icon_state = "gygax19" + else + user.visible_message("[user] pries Gygax Armour Plates from [holder].", "You prie Gygax Armour Plates from [holder].") + new /obj/item/mecha_parts/part/gygax_armour(get_turf(holder)) + holder.icon_state = "gygax17" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds Gygax Armour Plates to [holder].", "You weld Gygax Armour Plates to [holder].") + else + user.visible_message("[user] unfastens Gygax Armour Plates.", "You unfasten Gygax Armour Plates.") + holder.icon_state = "gygax18" + return 1 + +/datum/construction/reversible/mecha/gygax/spawn_result() + ..() + feedback_inc("mecha_gygax_created",1) + return + + + ////////////////////// +// Serenity +////////////////////// +/datum/construction/mecha/serenity_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/gygax_torso),//1 + list("key"=/obj/item/mecha_parts/part/gygax_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/gygax_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/gygax_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/gygax_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/gygax_head) + ) + +/datum/construction/mecha/serenity_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + qdel(I) + return 1 + +/datum/construction/mecha/serenity_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/serenity_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/serenity(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "gygax0" + const_holder.density = TRUE + spawn() + qdel(src) + return + + +/datum/construction/reversible/mecha/serenity + result = "/obj/mecha/combat/gygax/serenity" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/material/plasteel, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="Internal armor is wrenched"), + //5 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is installed"), + //6 + list("key"=/obj/item/stack/material/steel, + "backkey"=IS_SCREWDRIVER, + "desc"="Advanced capacitor is secured"), + //7 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Advanced capacitor is installed"), + //8 + list("key"=/obj/item/weapon/stock_parts/capacitor/adv, + "backkey"=IS_SCREWDRIVER, + "desc"="Advanced scanner module is secured"), + //9 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Advanced scanner module is installed"), + //10 + list("key"=/obj/item/weapon/stock_parts/scanning_module/adv, + "backkey"=IS_SCREWDRIVER, + "desc"="Medical module is secured"), + //11 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Medical module is installed"), + //12 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/medical, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //13 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //14 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //15 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //16 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //17 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //18 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //19 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //20 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/serenity/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/serenity/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(20) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "gygax2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "gygax0" + if(18) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "gygax3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(17) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "gygax4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "gygax2" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "gygax5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "gygax3" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "gygax6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/main(get_turf(holder)) + holder.icon_state = "gygax4" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "gygax7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "gygax5" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "gygax8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/peripherals(get_turf(holder)) + holder.icon_state = "gygax6" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the medical control module into [holder].", "You install the medical control module into [holder].") + qdel(I) + holder.icon_state = "gygax9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "gygax7" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the medical control module.", "You secure the medical control module.") + holder.icon_state = "gygax10" + else + user.visible_message("[user] removes the medical control module from [holder].", "You remove the medical control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/medical(get_turf(holder)) + holder.icon_state = "gygax8" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs advanced scanner module to [holder].", "You install advanced scanner module to [holder].") + qdel(I) + holder.icon_state = "gygax11" + else + user.visible_message("[user] unfastens the medical control module.", "You unfasten the medical control module.") + holder.icon_state = "gygax9" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") + holder.icon_state = "gygax12" + else + user.visible_message("[user] removes the advanced scanner module from [holder].", "You remove the advanced scanner module from [holder].") + new /obj/item/weapon/stock_parts/scanning_module/adv(get_turf(holder)) + holder.icon_state = "gygax10" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs advanced capacitor to [holder].", "You install advanced capacitor to [holder].") + qdel(I) + holder.icon_state = "gygax13" + else + user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") + holder.icon_state = "gygax11" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") + holder.icon_state = "gygax14" + else + user.visible_message("[user] removes the advanced capacitor from [holder].", "You remove the advanced capacitor from [holder].") + new /obj/item/weapon/stock_parts/capacitor/adv(get_turf(holder)) + holder.icon_state = "gygax12" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") + holder.icon_state = "gygax15" + else + user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") + holder.icon_state = "gygax13" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") + holder.icon_state = "gygax16" + else + user.visible_message("[user] pries internal armor layer from [holder].", "You pry the internal armor layer from [holder].") + new /obj/item/stack/material/steel(get_turf(holder), 5) + holder.icon_state = "gygax14" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "gygax17" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "gygax15" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs the external armor layer to [holder].", "You install the external armor layer to [holder].") + holder.icon_state = "gygax18" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "gygax16" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures the external armor layer.", "You secure the external armor layer.") + holder.icon_state = "gygax19-s" + else + user.visible_message("[user] pries the external armor layer from [holder].", "You pry the external armor layer from [holder].") + new /obj/item/stack/material/plasteel(get_turf(holder), 5) // Fixes serenity giving Gygax Armor Plates for the reverse action... + holder.icon_state = "gygax17" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to [holder].", "You weld the external armor layer to [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "gygax18" + return 1 + +/datum/construction/reversible/mecha/serenity/spawn_result() + ..() + feedback_inc("mecha_serenity_created",1) + return + + + +//////////////////////// +// Firefighter +//////////////////////// +/datum/construction/mecha/firefighter_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 + list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/ripley_right_leg),//5 + list("key"=/obj/item/clothing/suit/fire)//6 + ) + +/datum/construction/mecha/firefighter_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + user.drop_item() + qdel(I) + return 1 + +/datum/construction/mecha/firefighter_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/firefighter_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/firefighter(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "fireripley0" + const_holder.density = TRUE + spawn() + qdel(src) + return + + +/datum/construction/reversible/mecha/firefighter + result = "/obj/mecha/working/ripley/firefighter" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/material/plasteel, + "backkey"=IS_CROWBAR, + "desc"="External armor is being installed."), + //4 + list("key"=/obj/item/stack/material/plasteel, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded."), + //5 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="Internal armor is wrenched"), + //6 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is installed"), + //7 + list("key"=/obj/item/stack/material/plasteel, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //8 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //9 + list("key"=/obj/item/weapon/circuitboard/mecha/ripley/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //10 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //11 + list("key"=/obj/item/weapon/circuitboard/mecha/ripley/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //12 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //13 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //14 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //15 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/firefighter/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/firefighter/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(15) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "fireripley1" + if(14) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "fireripley2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "fireripley0" + if(13) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "fireripley3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "fireripley1" + if(12) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "fireripley4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "fireripley2" + if(11) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "fireripley5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "fireripley3" + if(10) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "fireripley6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/ripley/main(get_turf(holder)) + holder.icon_state = "fireripley4" + if(9) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "fireripley7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "fireripley5" + if(8) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "fireripley8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/ripley/peripherals(get_turf(holder)) + holder.icon_state = "fireripley6" + if(7) + if(diff==FORWARD) + user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") + holder.icon_state = "fireripley9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "fireripley7" + if(6) + if(diff==FORWARD) + user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") + holder.icon_state = "fireripley10" + else + user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") + new /obj/item/stack/material/plasteel(get_turf(holder), 5) + holder.icon_state = "fireripley8" + if(5) + if(diff==FORWARD) + user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "fireripley11" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "fireripley9" + if(4) + if(diff==FORWARD) + user.visible_message("[user] starts to install the external armor layer to [holder].", "You start to install the external armor layer to [holder].") + holder.icon_state = "fireripley12" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "fireripley10" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs external reinforced armor layer to [holder].", "You install external reinforced armor layer to [holder].") + holder.icon_state = "fireripley13" + else + user.visible_message("[user] removes the external armor from [holder].", "You remove the external armor from [holder].") + new /obj/item/stack/material/plasteel(get_turf(holder), 5) + holder.icon_state = "fireripley11" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") + holder.icon_state = "fireripley14" + else + user.visible_message("[user] pries external armor layer from [holder].", "You prie external armor layer from [holder].") + new /obj/item/stack/material/plasteel(get_turf(holder), 5) + holder.icon_state = "fireripley12" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds external armor layer to [holder].", "You weld external armor layer to [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "fireripley13" + return 1 + +/datum/construction/reversible/mecha/firefighter/spawn_result() + ..() + feedback_inc("mecha_firefighter_created",1) + return + +////////////////////// +// Durand +////////////////////// +/datum/construction/mecha/durand_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/durand_torso),//1 + list("key"=/obj/item/mecha_parts/part/durand_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/durand_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/durand_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/durand_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/durand_head) + ) + +/datum/construction/mecha/durand_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + qdel(I) + return 1 + +/datum/construction/mecha/durand_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/durand_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/durand(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "durand0" + const_holder.density = TRUE + spawn() + qdel(src) + return + + +/datum/construction/reversible/mecha/durand + result = "/obj/mecha/combat/durand" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/mecha_parts/part/durand_armour, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="Internal armor is wrenched"), + //5 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is installed"), + //6 + list("key"=/obj/item/stack/material/steel, + "backkey"=IS_SCREWDRIVER, + "desc"="Advanced capacitor is secured"), + //7 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Advanced capacitor is installed"), + //8 + list("key"=/obj/item/weapon/stock_parts/capacitor/adv, + "backkey"=IS_SCREWDRIVER, + "desc"="Advanced scanner module is secured"), + //9 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Advanced scanner module is installed"), + //10 + list("key"=/obj/item/weapon/stock_parts/scanning_module/adv, + "backkey"=IS_SCREWDRIVER, + "desc"="Targeting module is secured"), + //11 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Targeting module is installed"), + //12 + list("key"=/obj/item/weapon/circuitboard/mecha/durand/targeting, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //13 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //14 + list("key"=/obj/item/weapon/circuitboard/mecha/durand/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //15 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //16 + list("key"=/obj/item/weapon/circuitboard/mecha/durand/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //17 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //18 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //19 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //20 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + + +/datum/construction/reversible/mecha/durand/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/durand/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(20) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "durand1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "durand2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "durand0" + if(18) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "durand3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "durand1" + if(17) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "durand4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "durand2" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "durand5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "durand3" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "durand6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/durand/main(get_turf(holder)) + holder.icon_state = "durand4" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "durand7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "durand5" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "durand8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/durand/peripherals(get_turf(holder)) + holder.icon_state = "durand6" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") + qdel(I) + holder.icon_state = "durand9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "durand7" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "durand10" + else + user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/durand/targeting(get_turf(holder)) + holder.icon_state = "durand8" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs advanced scanner module to [holder].", "You install advanced scanner module to [holder].") + qdel(I) + holder.icon_state = "durand11" + else + user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") + holder.icon_state = "durand9" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") + holder.icon_state = "durand12" + else + user.visible_message("[user] removes the advanced scanner module from [holder].", "You remove the advanced scanner module from [holder].") + new /obj/item/weapon/stock_parts/scanning_module/adv(get_turf(holder)) + holder.icon_state = "durand10" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs advanced capacitor to [holder].", "You install advanced capacitor to [holder].") + qdel(I) + holder.icon_state = "durand13" + else + user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") + holder.icon_state = "durand11" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") + holder.icon_state = "durand14" + else + user.visible_message("[user] removes the advanced capacitor from [holder].", "You remove the advanced capacitor from [holder].") + new /obj/item/weapon/stock_parts/capacitor/adv(get_turf(holder)) + holder.icon_state = "durand12" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") + holder.icon_state = "durand15" + else + user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") + holder.icon_state = "durand13" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") + holder.icon_state = "durand16" + else + user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") + new /obj/item/stack/material/steel(get_turf(holder), 5) + holder.icon_state = "durand14" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "durand17" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "durand15" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs Durand Armour Plates to [holder].", "You install Durand Armour Plates to [holder].") + qdel(I) + holder.icon_state = "durand18" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "durand16" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures Durand Armour Plates.", "You secure Durand Armour Plates.") + holder.icon_state = "durand19" + else + user.visible_message("[user] pries Durand Armour Plates from [holder].", "You prie Durand Armour Plates from [holder].") + new /obj/item/mecha_parts/part/durand_armour(get_turf(holder)) + holder.icon_state = "durand17" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds Durand Armour Plates to [holder].", "You weld Durand Armour Plates to [holder].") + else + user.visible_message("[user] unfastens Durand Armour Plates.", "You unfasten Durand Armour Plates.") + holder.icon_state = "durand18" + return 1 + +/datum/construction/reversible/mecha/durand/spawn_result() + ..() + feedback_inc("mecha_durand_created",1) + return + +//////////////////////// +// Odysseus +//////////////////////// +/datum/construction/mecha/odysseus_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/odysseus_torso),//1 + list("key"=/obj/item/mecha_parts/part/odysseus_head),//2 + list("key"=/obj/item/mecha_parts/part/odysseus_left_arm),//3 + list("key"=/obj/item/mecha_parts/part/odysseus_right_arm),//4 + list("key"=/obj/item/mecha_parts/part/odysseus_left_leg),//5 + list("key"=/obj/item/mecha_parts/part/odysseus_right_leg)//6 + ) + +/datum/construction/mecha/odysseus_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + qdel(I) + return 1 + +/datum/construction/mecha/odysseus_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/odysseus_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/odysseus(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "odysseus0" + const_holder.density = TRUE + spawn() + qdel(src) + return + + +/datum/construction/reversible/mecha/odysseus + result = "/obj/mecha/medical/odysseus" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/material/plasteel, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="Internal armor is wrenched"), + //5 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is installed"), + //6 + list("key"=/obj/item/stack/material/steel, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //7 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //8 + list("key"=/obj/item/weapon/circuitboard/mecha/odysseus/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //9 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //10 + list("key"=/obj/item/weapon/circuitboard/mecha/odysseus/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //11 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //12 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //13 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //14 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/odysseus/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/odysseus/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(14) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "odysseus1" + if(13) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "odysseus2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "odysseus0" + if(12) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "odysseus3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "odysseus1" + if(11) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "odysseus4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "odysseus2" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "odysseus5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "odysseus3" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "odysseus6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/odysseus/main(get_turf(holder)) + holder.icon_state = "odysseus4" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "odysseus7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "odysseus5" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "odysseus8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/odysseus/peripherals(get_turf(holder)) + holder.icon_state = "odysseus6" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") + holder.icon_state = "odysseus9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "odysseus7" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") + holder.icon_state = "odysseus10" + else + user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") + new /obj/item/stack/material/steel(get_turf(holder), 5) + holder.icon_state = "odysseus8" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "odysseus11" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "odysseus9" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs [I] layer to [holder].", "You install external reinforced armor layer to [holder].") + holder.icon_state = "odysseus12" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "odysseus10" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") + holder.icon_state = "odysseus13" + else + new /obj/item/stack/material/plasteel(get_turf(holder), 5) + user.visible_message("[user] pries the plasteel from [holder].", "You prie the plasteel from [holder].") + holder.icon_state = "odysseus11" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds external armor layer to [holder].", "You weld external armor layer to [holder].") + holder.icon_state = "odysseus14" + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "odysseus12" + return 1 + +/datum/construction/reversible/mecha/odysseus/spawn_result() + ..() + feedback_inc("mecha_odysseus_created",1) + return + +////////////////////// +// Phazon +////////////////////// +/datum/construction/mecha/phazon_chassis + result = "/obj/mecha/combat/phazon" + steps = list(list("key"=/obj/item/mecha_parts/part/phazon_torso),//1 + list("key"=/obj/item/mecha_parts/part/phazon_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/phazon_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/phazon_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/phazon_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/phazon_head) + ) + +/datum/construction/mecha/phazon_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + qdel(I) + return 1 + +/datum/construction/mecha/phazon_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/phazon_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/phazon(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "phazon0" + const_holder.density = TRUE + spawn() + qdel(src) + return + +/datum/construction/reversible/mecha/phazon + result = "/obj/mecha/combat/phazon" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/material/plasteel, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="Internal armor is wrenched"), + //5 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is installed"), + //6 + list("key"=/obj/item/stack/material/steel, + "backkey"=IS_SCREWDRIVER, + "desc"="Hand teleporter is secured"), + //7 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Hand teleporter is installed"), + //8 + list("key"=/obj/item/weapon/hand_tele, + "backkey"=IS_SCREWDRIVER, + "desc"="SMES coil is secured"), + //9 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="SMES coil is installed"), + //10 + list("key"=/obj/item/weapon/smes_coil/super_capacity, + "backkey"=IS_SCREWDRIVER, + "desc"="Targeting module is secured"), + //11 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Targeting module is installed"), + //12 + list("key"=/obj/item/weapon/circuitboard/mecha/phazon/targeting, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //13 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //14 + list("key"=/obj/item/weapon/circuitboard/mecha/phazon/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //15 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //16 + list("key"=/obj/item/weapon/circuitboard/mecha/phazon/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //17 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //18 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //19 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //20 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/phazon/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/phazon/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + switch(index) + if(20) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "phazon1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "phazon2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "phazon0" + if(18) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "phazon3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "phazon1" + if(17) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "phazon4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "phazon2" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "phazon5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "phazon3" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "phazon6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/phazon/main(get_turf(holder)) + holder.icon_state = "phazon4" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "phazon7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "phazon5" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "phazon8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/phazon/peripherals(get_turf(holder)) + holder.icon_state = "phazon6" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") + qdel(I) + holder.icon_state = "phazon9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "phazon7" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "phazon10" + else + user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/phazon/targeting(get_turf(holder)) + holder.icon_state = "phazon8" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the SMES coil to [holder].", "You install the SMES coil to [holder].") + qdel(I) + holder.icon_state = "phazon11" + else + user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") + holder.icon_state = "phazon9" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the SMES coil.", "You secure the SMES coil.") + holder.icon_state = "phazon12" + else + user.visible_message("[user] removes the SMES coil from [holder].", "You remove the SMES coil from [holder].") + new /obj/item/weapon/smes_coil/super_capacity(get_turf(holder)) + holder.icon_state = "phazon10" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs the hand teleporter to [holder].", "You install the hand teleporter to [holder].") + qdel(I) + holder.icon_state = "phazon13" + else + user.visible_message("[user] unfastens the SMES coil.", "You unfasten the SMES coil.") + holder.icon_state = "phazon11" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the hand teleporter.", "You secure the hand teleporter.") + holder.icon_state = "phazon14" + else + user.visible_message("[user] removes the hand teleporter from [holder].", "You remove the hand teleporter from [holder].") + new /obj/item/weapon/hand_tele(get_turf(holder)) + holder.icon_state = "phazon12" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to [holder].", "You install the internal armor layer to [holder].") + holder.icon_state = "phazon19" + else + user.visible_message("[user] unfastens the hand teleporter.", "You unfasten the hand teleporter.") + holder.icon_state = "phazon13" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "phazon20" + else + user.visible_message("[user] pries the internal armor layer from [holder].", "You pry the internal armor layer from [holder].") + new /obj/item/stack/material/steel(get_turf(holder), 5) + holder.icon_state = "phazon14" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "phazon21" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "phazon19" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs the external reinforced armor layer to [holder].", "You install the external reinforced armor layer to [holder].") + holder.icon_state = "phazon22" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "phazon20" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") + holder.icon_state = "phazon23" + else + user.visible_message("[user] pries the external armor layer from [holder].", "You pry external armor layer from [holder].") + new /obj/item/stack/material/plasteel(get_turf(holder), 5) + holder.icon_state = "phazon21" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to [holder].", "You weld the external armor layer to [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "phazon22" + return 1 + +/datum/construction/reversible/mecha/phazon/spawn_result() + ..() + feedback_inc("mecha_phazon_created",1) + return + +////////////////////// +// Janus +////////////////////// +/datum/construction/mecha/janus_chassis + result = "/obj/mecha/combat/phazon/janus" + steps = list(list("key"=/obj/item/mecha_parts/part/janus_torso),//1 + list("key"=/obj/item/mecha_parts/part/janus_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/janus_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/janus_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/janus_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/janus_head) + ) + +/datum/construction/mecha/janus_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + qdel(I) + return 1 + +/datum/construction/mecha/janus_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/janus_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/janus(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "janus0" + const_holder.density = TRUE + spawn() + qdel(src) + return + +/datum/construction/reversible/mecha/janus + result = "/obj/mecha/combat/phazon/janus" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is attached."), + //3 + list("key"=/obj/item/stack/material/morphium, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded"), + //4 + list("key"=IS_WELDER, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is wrenched"), + //5 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is attached."), + //6 + list("key"=/obj/item/stack/material/durasteel, + "backkey"=IS_SCREWDRIVER, + "desc"="Durand auxiliary board is secured."), + //7 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Durand auxiliary board is installed"), + //8 + list("key"=/obj/item/weapon/circuitboard/mecha/durand/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Phase coil is secured"), + //9 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Phase coil is installed"), + //10 + list("key"=/obj/item/prop/alien/phasecoil, + "backkey"=IS_SCREWDRIVER, + "desc"="Gygax balance system secured"), + //11 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Gygax balance system installed"), + //12 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Targeting module is secured"), + //13 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Targeting module is installed"), + //14 + list("key"=/obj/item/weapon/circuitboard/mecha/imperion/targeting, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //15 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //16 + list("key"=/obj/item/weapon/circuitboard/mecha/imperion/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //17 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //18 + list("key"=/obj/item/weapon/circuitboard/mecha/imperion/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //19 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //20 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //21 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //22 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/janus/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/janus/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + switch(index) + if(22) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "janus1" + if(21) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "janus2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "janus0" + if(20) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "janus3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "janus1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "janus4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "janus2" + if(18) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "janus5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "janus3" + if(17) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "janus6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/imperion/main(get_turf(holder)) + holder.icon_state = "janus4" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "janus7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "janus5" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "janus8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/imperion/peripherals(get_turf(holder)) + holder.icon_state = "janus6" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") + qdel(I) + holder.icon_state = "janus9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "janus7" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "janus10" + else + user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/imperion/targeting(get_turf(holder)) + holder.icon_state = "janus8" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the Gygax control module into [holder].", "You install the Gygax control module into [holder].") + qdel(I) + holder.icon_state = "janus11" + else + user.visible_message("[user] unfastens the Gygax control module.", "You unfasten the Gygax control module.") + holder.icon_state = "janus9" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the Gygax control module.", "You secure the Gygax control module.") + holder.icon_state = "janus12" + else + user.visible_message("[user] removes the Gygax control module from [holder].", "You remove the Gygax control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/peripherals(get_turf(holder)) + holder.icon_state = "janus10" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the phase coil into [holder].", "You install the phase coil into [holder].") + qdel(I) + holder.icon_state = "janus13" + else + user.visible_message("[user] unfastens the Gygax control module.", "You unfasten the Gygax control module.") + holder.icon_state = "janus11" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the phase coil.", "You secure the phase coil.") + holder.icon_state = "janus14" + else + user.visible_message("[user] removes the phase coil from [holder].", "You remove the phase coil from [holder].") + new /obj/item/prop/alien/phasecoil(get_turf(holder)) + holder.icon_state = "janus12" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs the Durand control module into [holder].", "You install the Durand control module into [holder].") + qdel(I) + holder.icon_state = "janus15" + else + user.visible_message("[user] unfastens the phase coil.", "You unfasten the phase coil.") + holder.icon_state = "janus13" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the Durand control module.", "You secure the Durand control module.") + holder.icon_state = "janus16" + else + user.visible_message("[user] removes the Durand control module from [holder].", "You remove the Durand control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/durand/peripherals(get_turf(holder)) + holder.icon_state = "janus14" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to [holder].", "You install the internal armor layer to [holder].") + holder.icon_state = "janus17" + else + user.visible_message("[user] unfastens the Durand control module.", "You unfasten the Durand control module.") + holder.icon_state = "janus15" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "janus18" + else + user.visible_message("[user] pries the internal armor layer from [holder].", "You pry the internal armor layer from [holder].") + new /obj/item/stack/material/durasteel(get_turf(holder), 5) + holder.icon_state = "janus16" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "janus19" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "janus17" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs the external reinforced armor layer to [holder].", "You install the external reinforced armor layer to [holder].") + holder.icon_state = "janus20" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "janus18" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") + holder.icon_state = "janus21" + else + user.visible_message("[user] pries the external armor layer from [holder].", "You pry external armor layer from [holder].") + new /obj/item/stack/material/morphium(get_turf(holder), 5) + holder.icon_state = "janus19" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to [holder].", "You weld the external armor layer to [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "janus20" + return 1 + +/datum/construction/reversible/mecha/janus/spawn_result() + ..() + feedback_inc("mecha_janus_created",1) + return diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm index 1c5003ac95d..a3c7ed24b90 100644 --- a/code/game/mecha/mecha_control_console.dm +++ b/code/game/mecha/mecha_control_console.dm @@ -1,134 +1,134 @@ -/obj/machinery/computer/mecha - name = "Exosuit Control" - desc = "Used to track exosuits, as well as view their logs and activate EMP beacons." - icon_keyboard = "rd_key" - icon_screen = "mecha" - light_color = "#a97faa" - req_access = list(access_robotics) - circuit = /obj/item/weapon/circuitboard/mecha_control - var/list/located = list() - var/screen = 0 - var/list/stored_data - -/obj/machinery/computer/mecha/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/computer/mecha/attack_hand(mob/user) - if(..()) - return - tgui_interact(user) - -/obj/machinery/computer/mecha/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "MechaControlConsole", name) - ui.open() - -/obj/machinery/computer/mecha/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) - var/list/data = ..() - - - var/list/beacons = list() - for(var/obj/item/mecha_parts/mecha_tracking/TR in world) - var/list/tr_data = TR.tgui_data(user) - if(tr_data) - beacons.Add(list(tr_data)) - data["beacons"] = beacons - - LAZYINITLIST(stored_data) - data["stored_data"] = stored_data - - return data - -/obj/machinery/computer/mecha/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - switch(action) - if("send_message") - var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["mt"]) - if(istype(MT)) - var/message = sanitize(tgui_input_text(usr, "Input message", "Transmit message")) - var/obj/mecha/M = MT.in_mecha() - if(message && M) - M.occupant_message(message) - return TRUE - - if("shock") - var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["mt"]) - if(istype(MT)) - MT.shock() - return TRUE - - if("get_log") - var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["mt"]) - if(istype(MT)) - stored_data = MT.get_mecha_log() - return TRUE - - if("clear_log") - stored_data = null - return TRUE - -/obj/item/mecha_parts/mecha_tracking - name = "Exosuit tracking beacon" - desc = "Device used to transmit exosuit data." - icon = 'icons/obj/device.dmi' - icon_state = "motion2" - origin_tech = list(TECH_DATA = 2, TECH_MAGNET = 2) - -/obj/item/mecha_parts/mecha_tracking/tgui_data(mob/user) - var/list/data = ..() - if(!in_mecha()) - return FALSE - - var/obj/mecha/M = loc - data["ref"] = REF(src) - data["charge"] = M.get_charge() - data["name"] = M.name - data["health"] = M.health - data["maxHealth"] = initial(M.health) - data["cell"] = M.cell - if(M.cell) - data["cellCharge"] = M.cell.charge - data["cellMaxCharge"] = M.cell.charge - data["airtank"] = M.return_pressure() - data["pilot"] = M.occupant - data["location"] = get_area(M) - data["active"] = M.selected - if(istype(M, /obj/mecha/working/ripley)) - var/obj/mecha/working/ripley/RM = M - data["cargoUsed"] = RM.cargo.len - data["cargoMax"] = RM.cargo_capacity - - return data - -/obj/item/mecha_parts/mecha_tracking/emp_act() - qdel(src) - return - -/obj/item/mecha_parts/mecha_tracking/ex_act() - qdel(src) - return - -/obj/item/mecha_parts/mecha_tracking/proc/in_mecha() - if(istype(loc, /obj/mecha)) - return loc - return 0 - -/obj/item/mecha_parts/mecha_tracking/proc/shock() - var/obj/mecha/M = in_mecha() - if(M) - M.emp_act(4) - qdel(src) - -/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_log() - if(!in_mecha()) - return list() - var/obj/mecha/M = loc - return M.get_log_tgui() - - -/obj/item/weapon/storage/box/mechabeacons - name = "Exosuit Tracking Beacons" - starts_with = list(/obj/item/mecha_parts/mecha_tracking = 7) +/obj/machinery/computer/mecha + name = "Exosuit Control" + desc = "Used to track exosuits, as well as view their logs and activate EMP beacons." + icon_keyboard = "rd_key" + icon_screen = "mecha" + light_color = "#a97faa" + req_access = list(access_robotics) + circuit = /obj/item/weapon/circuitboard/mecha_control + var/list/located = list() + var/screen = 0 + var/list/stored_data + +/obj/machinery/computer/mecha/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/computer/mecha/attack_hand(mob/user) + if(..()) + return + tgui_interact(user) + +/obj/machinery/computer/mecha/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "MechaControlConsole", name) + ui.open() + +/obj/machinery/computer/mecha/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + + var/list/beacons = list() + for(var/obj/item/mecha_parts/mecha_tracking/TR in world) + var/list/tr_data = TR.tgui_data(user) + if(tr_data) + beacons.Add(list(tr_data)) + data["beacons"] = beacons + + LAZYINITLIST(stored_data) + data["stored_data"] = stored_data + + return data + +/obj/machinery/computer/mecha/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("send_message") + var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["mt"]) + if(istype(MT)) + var/message = sanitize(tgui_input_text(usr, "Input message", "Transmit message")) + var/obj/mecha/M = MT.in_mecha() + if(message && M) + M.occupant_message(message) + return TRUE + + if("shock") + var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["mt"]) + if(istype(MT)) + MT.shock() + return TRUE + + if("get_log") + var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["mt"]) + if(istype(MT)) + stored_data = MT.get_mecha_log() + return TRUE + + if("clear_log") + stored_data = null + return TRUE + +/obj/item/mecha_parts/mecha_tracking + name = "Exosuit tracking beacon" + desc = "Device used to transmit exosuit data." + icon = 'icons/obj/device.dmi' + icon_state = "motion2" + origin_tech = list(TECH_DATA = 2, TECH_MAGNET = 2) + +/obj/item/mecha_parts/mecha_tracking/tgui_data(mob/user) + var/list/data = ..() + if(!in_mecha()) + return FALSE + + var/obj/mecha/M = loc + data["ref"] = REF(src) + data["charge"] = M.get_charge() + data["name"] = M.name + data["health"] = M.health + data["maxHealth"] = initial(M.health) + data["cell"] = M.cell + if(M.cell) + data["cellCharge"] = M.cell.charge + data["cellMaxCharge"] = M.cell.charge + data["airtank"] = M.return_pressure() + data["pilot"] = M.occupant + data["location"] = get_area(M) + data["active"] = M.selected + if(istype(M, /obj/mecha/working/ripley)) + var/obj/mecha/working/ripley/RM = M + data["cargoUsed"] = RM.cargo.len + data["cargoMax"] = RM.cargo_capacity + + return data + +/obj/item/mecha_parts/mecha_tracking/emp_act() + qdel(src) + return + +/obj/item/mecha_parts/mecha_tracking/ex_act() + qdel(src) + return + +/obj/item/mecha_parts/mecha_tracking/proc/in_mecha() + if(istype(loc, /obj/mecha)) + return loc + return 0 + +/obj/item/mecha_parts/mecha_tracking/proc/shock() + var/obj/mecha/M = in_mecha() + if(M) + M.emp_act(4) + qdel(src) + +/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_log() + if(!in_mecha()) + return list() + var/obj/mecha/M = loc + return M.get_log_tgui() + + +/obj/item/weapon/storage/box/mechabeacons + name = "Exosuit Tracking Beacons" + starts_with = list(/obj/item/mecha_parts/mecha_tracking = 7) diff --git a/code/game/mecha/mecha_parts.dm b/code/game/mecha/mecha_parts.dm index 4be3c27d48d..3476de004c5 100644 --- a/code/game/mecha/mecha_parts.dm +++ b/code/game/mecha/mecha_parts.dm @@ -1,342 +1,342 @@ - ///////////////////////// -////// Mecha Parts ////// -///////////////////////// - -// Mecha circuitboards can be found in /code/game/objects/items/weapons/circuitboards/mecha.dm - -/obj/item/mecha_parts - name = "mecha part" - icon = 'icons/mecha/mech_construct.dmi' - icon_state = "blank" - w_class = ITEMSIZE_HUGE - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2) - - -/obj/item/mecha_parts/chassis - name="Mecha Chassis" - icon_state = "backbone" - var/datum/construction/construct - -/obj/item/mecha_parts/chassis/attackby(obj/item/W as obj, mob/user as mob) - if(!construct || !construct.action(W, user)) - ..() - return - -/obj/item/mecha_parts/chassis/attack_hand() - return - -/////////// Ripley - -/obj/item/mecha_parts/chassis/ripley - name = "Ripley Chassis" - -/obj/item/mecha_parts/chassis/ripley/New() - ..() - construct = new /datum/construction/mecha/ripley_chassis(src) - -/obj/item/mecha_parts/part/ripley_torso - name="Ripley Torso" - desc="A torso part of Ripley APLU. Contains power unit, processing core and life support systems." - icon_state = "ripley_harness" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/ripley_left_arm - name="Ripley Left Arm" - desc="A Ripley APLU left arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "ripley_l_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/ripley_right_arm - name="Ripley Right Arm" - desc="A Ripley APLU right arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "ripley_r_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/ripley_left_leg - name="Ripley Left Leg" - desc="A Ripley APLU left leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "ripley_l_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/ripley_right_leg - name="Ripley Right Leg" - desc="A Ripley APLU right leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "ripley_r_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -///////// Gygax - -/obj/item/mecha_parts/chassis/gygax - name = "Gygax Chassis" - -/obj/item/mecha_parts/chassis/gygax/New() - ..() - construct = new /datum/construction/mecha/gygax_chassis(src) - -/obj/item/mecha_parts/part/gygax_torso - name="Gygax Torso" - desc="A torso part of Gygax. Contains power unit, processing core and life support systems. Has an additional equipment slot." - icon_state = "gygax_harness" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_BIO = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/gygax_head - name="Gygax Head" - desc="A Gygax head. Houses advanced surveilance and targeting sensors." - icon_state = "gygax_head" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_MAGNET = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/gygax_left_arm - name="Gygax Left Arm" - desc="A Gygax left arm. Data and power sockets are compatible with most exosuit tools and weapons." - icon_state = "gygax_l_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/gygax_right_arm - name="Gygax Right Arm" - desc="A Gygax right arm. Data and power sockets are compatible with most exosuit tools and weapons." - icon_state = "gygax_r_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/gygax_left_leg - name="Gygax Left Leg" - icon_state = "gygax_l_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/gygax_right_leg - name="Gygax Right Leg" - icon_state = "gygax_r_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/gygax_armour - name="Gygax Armour Plates" - icon_state = "gygax_armour" - origin_tech = list(TECH_MATERIAL = 6, TECH_COMBAT = 4, TECH_ENGINEERING = 5) - -////////// Serenity - -/obj/item/mecha_parts/chassis/serenity - name = "Serenity Chassis" - -/obj/item/mecha_parts/chassis/serenity/New() - ..() - construct = new /datum/construction/mecha/serenity_chassis(src) - -//////////// Durand - -/obj/item/mecha_parts/chassis/durand - name = "Durand Chassis" - -/obj/item/mecha_parts/chassis/durand/New() - ..() - construct = new /datum/construction/mecha/durand_chassis(src) - -/obj/item/mecha_parts/part/durand_torso - name="Durand Torso" - icon_state = "durand_harness" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_BIO = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/durand_head - name="Durand Head" - icon_state = "durand_head" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_MAGNET = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/durand_left_arm - name="Durand Left Arm" - icon_state = "durand_l_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/durand_right_arm - name="Durand Right Arm" - icon_state = "durand_r_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/durand_left_leg - name="Durand Left Leg" - icon_state = "durand_l_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/durand_right_leg - name="Durand Right Leg" - icon_state = "durand_r_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/durand_armour - name="Durand Armour Plates" - icon_state = "durand_armour" - origin_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 4, TECH_ENGINEERING = 5) - - - -////////// Firefighter - -/obj/item/mecha_parts/chassis/firefighter - name = "Firefighter Chassis" - -/obj/item/mecha_parts/chassis/firefighter/New() - ..() - construct = new /datum/construction/mecha/firefighter_chassis(src) -/* -/obj/item/mecha_parts/part/firefighter_torso - name="Ripley-on-Fire Torso" - icon_state = "ripley_harness" - -/obj/item/mecha_parts/part/firefighter_left_arm - name="Ripley-on-Fire Left Arm" - icon_state = "ripley_l_arm" - -/obj/item/mecha_parts/part/firefighter_right_arm - name="Ripley-on-Fire Right Arm" - icon_state = "ripley_r_arm" - -/obj/item/mecha_parts/part/firefighter_left_leg - name="Ripley-on-Fire Left Leg" - icon_state = "ripley_l_leg" - -/obj/item/mecha_parts/part/firefighter_right_leg - name="Ripley-on-Fire Right Leg" - icon_state = "ripley_r_leg" -*/ - -////////// Phazon - -/obj/item/mecha_parts/chassis/phazon - name = "Phazon Chassis" - origin_tech = list(TECH_MATERIAL = 7) - -/obj/item/mecha_parts/chassis/phazon/New() - ..() - construct = new /datum/construction/mecha/phazon_chassis(src) - -/obj/item/mecha_parts/part/phazon_torso - name="Phazon Torso" - icon_state = "phazon_harness" - //construction_time = 300 - //construction_cost = list(MAT_STEEL=35000,"glass"=10000,"phoron"=20000) - origin_tech = list(TECH_DATA = 5, TECH_MATERIAL = 7, TECH_BLUESPACE = 6, TECH_POWER = 6) - -/obj/item/mecha_parts/part/phazon_head - name="Phazon Head" - icon_state = "phazon_head" - //construction_time = 200 - //construction_cost = list(MAT_STEEL=15000,"glass"=5000,"phoron"=10000) - origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 5, TECH_MAGNET = 6) - -/obj/item/mecha_parts/part/phazon_left_arm - name="Phazon Left Arm" - icon_state = "phazon_l_arm" - //construction_time = 200 - //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) - -/obj/item/mecha_parts/part/phazon_right_arm - name="Phazon Right Arm" - icon_state = "phazon_r_arm" - //construction_time = 200 - //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) - -/obj/item/mecha_parts/part/phazon_left_leg - name="Phazon Left Leg" - icon_state = "phazon_l_leg" - //construction_time = 200 - //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3) - -/obj/item/mecha_parts/part/phazon_right_leg - name="Phazon Right Leg" - icon_state = "phazon_r_leg" - //construction_time = 200 - //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3) - -///////// Odysseus - - -/obj/item/mecha_parts/chassis/odysseus - name = "Odysseus Chassis" - -/obj/item/mecha_parts/chassis/odysseus/New() - ..() - construct = new /datum/construction/mecha/odysseus_chassis(src) - -/obj/item/mecha_parts/part/odysseus_head - name="Odysseus Head" - icon_state = "odysseus_head" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 2) - -/obj/item/mecha_parts/part/odysseus_torso - name="Odysseus Torso" - desc="A torso part of Odysseus. Contains power unit, processing core and life support systems." - icon_state = "odysseus_torso" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/odysseus_left_arm - name="Odysseus Left Arm" - desc="An Odysseus left arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "odysseus_l_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/odysseus_right_arm - name="Odysseus Right Arm" - desc="An Odysseus right arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "odysseus_r_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/odysseus_left_leg - name="Odysseus Left Leg" - desc="An Odysseus left leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "odysseus_l_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/odysseus_right_leg - name="Odysseus Right Leg" - desc="A Odysseus right leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "odysseus_r_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -/*/obj/item/mecha_parts/part/odysseus_armour - name="Odysseus Carapace" - icon_state = "odysseus_armour" - origin_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3) - construction_time = 200 - construction_cost = list(MAT_STEEL=15000)*/ - -////////// Janus - -/obj/item/mecha_parts/chassis/janus - name = "Janus Chassis" - origin_tech = list(TECH_MATERIAL = 7) - -/obj/item/mecha_parts/chassis/janus/New() - ..() - construct = new /datum/construction/mecha/janus_chassis(src) - -/obj/item/mecha_parts/part/janus_torso - name="Imperion Torso" - icon_state = "janus_harness" - origin_tech = list(TECH_DATA = 5, TECH_MATERIAL = 7, TECH_BLUESPACE = 2, TECH_POWER = 6, TECH_PRECURSOR = 2) - -/obj/item/mecha_parts/part/janus_head - name="Imperion Head" - icon_state = "janus_head" - origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 5, TECH_MAGNET = 6, TECH_PRECURSOR = 1) - -/obj/item/mecha_parts/part/janus_left_arm - name="Prototype Gygax Left Arm" - icon_state = "janus_l_arm" - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) - -/obj/item/mecha_parts/part/janus_right_arm - name="Prototype Gygax Right Arm" - icon_state = "janus_r_arm" - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) - -/obj/item/mecha_parts/part/janus_left_leg - name="Prototype Durand Left Leg" - icon_state = "janus_l_leg" - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3, TECH_ARCANE = 1) - -/obj/item/mecha_parts/part/janus_right_leg - name="Prototype Durand Right Leg" - icon_state = "janus_r_leg" - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3, TECH_ARCANE = 1) + ///////////////////////// +////// Mecha Parts ////// +///////////////////////// + +// Mecha circuitboards can be found in /code/game/objects/items/weapons/circuitboards/mecha.dm + +/obj/item/mecha_parts + name = "mecha part" + icon = 'icons/mecha/mech_construct.dmi' + icon_state = "blank" + w_class = ITEMSIZE_HUGE + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2) + + +/obj/item/mecha_parts/chassis + name="Mecha Chassis" + icon_state = "backbone" + var/datum/construction/construct + +/obj/item/mecha_parts/chassis/attackby(obj/item/W as obj, mob/user as mob) + if(!construct || !construct.action(W, user)) + ..() + return + +/obj/item/mecha_parts/chassis/attack_hand() + return + +/////////// Ripley + +/obj/item/mecha_parts/chassis/ripley + name = "Ripley Chassis" + +/obj/item/mecha_parts/chassis/ripley/New() + ..() + construct = new /datum/construction/mecha/ripley_chassis(src) + +/obj/item/mecha_parts/part/ripley_torso + name="Ripley Torso" + desc="A torso part of Ripley APLU. Contains power unit, processing core and life support systems." + icon_state = "ripley_harness" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/ripley_left_arm + name="Ripley Left Arm" + desc="A Ripley APLU left arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "ripley_l_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/ripley_right_arm + name="Ripley Right Arm" + desc="A Ripley APLU right arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "ripley_r_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/ripley_left_leg + name="Ripley Left Leg" + desc="A Ripley APLU left leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "ripley_l_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/ripley_right_leg + name="Ripley Right Leg" + desc="A Ripley APLU right leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "ripley_r_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +///////// Gygax + +/obj/item/mecha_parts/chassis/gygax + name = "Gygax Chassis" + +/obj/item/mecha_parts/chassis/gygax/New() + ..() + construct = new /datum/construction/mecha/gygax_chassis(src) + +/obj/item/mecha_parts/part/gygax_torso + name="Gygax Torso" + desc="A torso part of Gygax. Contains power unit, processing core and life support systems. Has an additional equipment slot." + icon_state = "gygax_harness" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_BIO = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/gygax_head + name="Gygax Head" + desc="A Gygax head. Houses advanced surveilance and targeting sensors." + icon_state = "gygax_head" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_MAGNET = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/gygax_left_arm + name="Gygax Left Arm" + desc="A Gygax left arm. Data and power sockets are compatible with most exosuit tools and weapons." + icon_state = "gygax_l_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/gygax_right_arm + name="Gygax Right Arm" + desc="A Gygax right arm. Data and power sockets are compatible with most exosuit tools and weapons." + icon_state = "gygax_r_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/gygax_left_leg + name="Gygax Left Leg" + icon_state = "gygax_l_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/gygax_right_leg + name="Gygax Right Leg" + icon_state = "gygax_r_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/gygax_armour + name="Gygax Armour Plates" + icon_state = "gygax_armour" + origin_tech = list(TECH_MATERIAL = 6, TECH_COMBAT = 4, TECH_ENGINEERING = 5) + +////////// Serenity + +/obj/item/mecha_parts/chassis/serenity + name = "Serenity Chassis" + +/obj/item/mecha_parts/chassis/serenity/New() + ..() + construct = new /datum/construction/mecha/serenity_chassis(src) + +//////////// Durand + +/obj/item/mecha_parts/chassis/durand + name = "Durand Chassis" + +/obj/item/mecha_parts/chassis/durand/New() + ..() + construct = new /datum/construction/mecha/durand_chassis(src) + +/obj/item/mecha_parts/part/durand_torso + name="Durand Torso" + icon_state = "durand_harness" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_BIO = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/durand_head + name="Durand Head" + icon_state = "durand_head" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_MAGNET = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/durand_left_arm + name="Durand Left Arm" + icon_state = "durand_l_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/durand_right_arm + name="Durand Right Arm" + icon_state = "durand_r_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/durand_left_leg + name="Durand Left Leg" + icon_state = "durand_l_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/durand_right_leg + name="Durand Right Leg" + icon_state = "durand_r_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/durand_armour + name="Durand Armour Plates" + icon_state = "durand_armour" + origin_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 4, TECH_ENGINEERING = 5) + + + +////////// Firefighter + +/obj/item/mecha_parts/chassis/firefighter + name = "Firefighter Chassis" + +/obj/item/mecha_parts/chassis/firefighter/New() + ..() + construct = new /datum/construction/mecha/firefighter_chassis(src) +/* +/obj/item/mecha_parts/part/firefighter_torso + name="Ripley-on-Fire Torso" + icon_state = "ripley_harness" + +/obj/item/mecha_parts/part/firefighter_left_arm + name="Ripley-on-Fire Left Arm" + icon_state = "ripley_l_arm" + +/obj/item/mecha_parts/part/firefighter_right_arm + name="Ripley-on-Fire Right Arm" + icon_state = "ripley_r_arm" + +/obj/item/mecha_parts/part/firefighter_left_leg + name="Ripley-on-Fire Left Leg" + icon_state = "ripley_l_leg" + +/obj/item/mecha_parts/part/firefighter_right_leg + name="Ripley-on-Fire Right Leg" + icon_state = "ripley_r_leg" +*/ + +////////// Phazon + +/obj/item/mecha_parts/chassis/phazon + name = "Phazon Chassis" + origin_tech = list(TECH_MATERIAL = 7) + +/obj/item/mecha_parts/chassis/phazon/New() + ..() + construct = new /datum/construction/mecha/phazon_chassis(src) + +/obj/item/mecha_parts/part/phazon_torso + name="Phazon Torso" + icon_state = "phazon_harness" + //construction_time = 300 + //construction_cost = list(MAT_STEEL=35000,"glass"=10000,"phoron"=20000) + origin_tech = list(TECH_DATA = 5, TECH_MATERIAL = 7, TECH_BLUESPACE = 6, TECH_POWER = 6) + +/obj/item/mecha_parts/part/phazon_head + name="Phazon Head" + icon_state = "phazon_head" + //construction_time = 200 + //construction_cost = list(MAT_STEEL=15000,"glass"=5000,"phoron"=10000) + origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 5, TECH_MAGNET = 6) + +/obj/item/mecha_parts/part/phazon_left_arm + name="Phazon Left Arm" + icon_state = "phazon_l_arm" + //construction_time = 200 + //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) + +/obj/item/mecha_parts/part/phazon_right_arm + name="Phazon Right Arm" + icon_state = "phazon_r_arm" + //construction_time = 200 + //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) + +/obj/item/mecha_parts/part/phazon_left_leg + name="Phazon Left Leg" + icon_state = "phazon_l_leg" + //construction_time = 200 + //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3) + +/obj/item/mecha_parts/part/phazon_right_leg + name="Phazon Right Leg" + icon_state = "phazon_r_leg" + //construction_time = 200 + //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3) + +///////// Odysseus + + +/obj/item/mecha_parts/chassis/odysseus + name = "Odysseus Chassis" + +/obj/item/mecha_parts/chassis/odysseus/New() + ..() + construct = new /datum/construction/mecha/odysseus_chassis(src) + +/obj/item/mecha_parts/part/odysseus_head + name="Odysseus Head" + icon_state = "odysseus_head" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 2) + +/obj/item/mecha_parts/part/odysseus_torso + name="Odysseus Torso" + desc="A torso part of Odysseus. Contains power unit, processing core and life support systems." + icon_state = "odysseus_torso" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/odysseus_left_arm + name="Odysseus Left Arm" + desc="An Odysseus left arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "odysseus_l_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/odysseus_right_arm + name="Odysseus Right Arm" + desc="An Odysseus right arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "odysseus_r_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/odysseus_left_leg + name="Odysseus Left Leg" + desc="An Odysseus left leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "odysseus_l_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/odysseus_right_leg + name="Odysseus Right Leg" + desc="A Odysseus right leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "odysseus_r_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +/*/obj/item/mecha_parts/part/odysseus_armour + name="Odysseus Carapace" + icon_state = "odysseus_armour" + origin_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3) + construction_time = 200 + construction_cost = list(MAT_STEEL=15000)*/ + +////////// Janus + +/obj/item/mecha_parts/chassis/janus + name = "Janus Chassis" + origin_tech = list(TECH_MATERIAL = 7) + +/obj/item/mecha_parts/chassis/janus/New() + ..() + construct = new /datum/construction/mecha/janus_chassis(src) + +/obj/item/mecha_parts/part/janus_torso + name="Imperion Torso" + icon_state = "janus_harness" + origin_tech = list(TECH_DATA = 5, TECH_MATERIAL = 7, TECH_BLUESPACE = 2, TECH_POWER = 6, TECH_PRECURSOR = 2) + +/obj/item/mecha_parts/part/janus_head + name="Imperion Head" + icon_state = "janus_head" + origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 5, TECH_MAGNET = 6, TECH_PRECURSOR = 1) + +/obj/item/mecha_parts/part/janus_left_arm + name="Prototype Gygax Left Arm" + icon_state = "janus_l_arm" + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) + +/obj/item/mecha_parts/part/janus_right_arm + name="Prototype Gygax Right Arm" + icon_state = "janus_r_arm" + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) + +/obj/item/mecha_parts/part/janus_left_leg + name="Prototype Durand Left Leg" + icon_state = "janus_l_leg" + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3, TECH_ARCANE = 1) + +/obj/item/mecha_parts/part/janus_right_leg + name="Prototype Durand Right Leg" + icon_state = "janus_r_leg" + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3, TECH_ARCANE = 1) diff --git a/code/game/mecha/mecha_wreckage.dm b/code/game/mecha/mecha_wreckage.dm index 01b59871129..8a3df3e0247 100644 --- a/code/game/mecha/mecha_wreckage.dm +++ b/code/game/mecha/mecha_wreckage.dm @@ -1,231 +1,231 @@ -/////////////////////////////////// -//////// Mecha wreckage //////// -/////////////////////////////////// - - -/obj/effect/decal/mecha_wreckage - name = "Exosuit wreckage" - desc = "Remains of some unfortunate mecha. Completely unrepairable." - icon = 'icons/mecha/mecha.dmi' - density = TRUE - anchored = FALSE - opacity = 0 - var/list/welder_salvage = list(/obj/item/stack/material/plasteel,/obj/item/stack/material/steel,/obj/item/stack/rods) - var/list/wirecutters_salvage = list(/obj/item/stack/cable_coil) - var/list/crowbar_salvage - var/salvage_num = 5 - -/obj/effect/decal/mecha_wreckage/New() - ..() - crowbar_salvage = new - return - -/obj/effect/decal/mecha_wreckage/ex_act(severity) - if(severity < 2) - spawn - qdel(src) - return - -/obj/effect/decal/mecha_wreckage/bullet_act(var/obj/item/projectile/Proj) - return - - -/obj/effect/decal/mecha_wreckage/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/WT = W.get_welder() - if(salvage_num <= 0) - to_chat(user, "You don't see anything that can be cut with [W].") - return - if (!isemptylist(welder_salvage) && WT.remove_fuel(0,user)) - var/type = prob(70)?pick(welder_salvage):null - if(type) - var/N = new type(get_turf(user)) - user.visible_message("[user] cuts [N] from [src]", "You cut [N] from [src]", "You hear a sound of welder nearby") - if(istype(N, /obj/item/mecha_parts/part)) - welder_salvage -= type - salvage_num-- - else - to_chat(user, "You failed to salvage anything valuable from [src].") - else - to_chat(user, "You need more welding fuel to complete this task.") - return - if(W.has_tool_quality(TOOL_WIRECUTTER)) - if(salvage_num <= 0) - to_chat(user, "You don't see anything that can be cut with [W].") - return - else if(!isemptylist(wirecutters_salvage)) - var/type = prob(70)?pick(wirecutters_salvage):null - if(type) - var/N = new type(get_turf(user)) - user.visible_message("[user] cuts [N] from [src].", "You cut [N] from [src].") - salvage_num-- - else - to_chat(user, "You failed to salvage anything valuable from [src].") - if(W.has_tool_quality(TOOL_CROWBAR)) - if(!isemptylist(crowbar_salvage)) - var/obj/S = pick(crowbar_salvage) - if(S) - S.loc = get_turf(user) - crowbar_salvage -= S - user.visible_message("[user] pries [S] from [src].", "You pry [S] from [src].") - return - else - to_chat(user, "You don't see anything that can be pried with [W].") - else - ..() - return - - -/obj/effect/decal/mecha_wreckage/gygax - name = "Gygax wreckage" - icon_state = "gygax-broken" - -/obj/effect/decal/mecha_wreckage/gygax/New() - ..() - var/list/parts = list(/obj/item/mecha_parts/part/gygax_torso, - /obj/item/mecha_parts/part/gygax_head, - /obj/item/mecha_parts/part/gygax_left_arm, - /obj/item/mecha_parts/part/gygax_right_arm, - /obj/item/mecha_parts/part/gygax_left_leg, - /obj/item/mecha_parts/part/gygax_right_leg) - for(var/i=0;i<2;i++) - if(!isemptylist(parts) && prob(40)) - var/part = pick(parts) - welder_salvage += part - parts -= part - return - -/obj/effect/decal/mecha_wreckage/gygax/dark - name = "Dark Gygax wreckage" - icon_state = "darkgygax-broken" - -/obj/effect/decal/mecha_wreckage/gygax/adv - name = "Advanced Dark Gygax wreckage" - icon_state = "darkgygax_adv-broken" - -/obj/effect/decal/mecha_wreckage/gygax/medgax - name = "Medgax wreckage" - icon_state = "medgax-broken" - -/obj/effect/decal/mecha_wreckage/gygax/serenity - name = "Serenity wreckage" - icon_state = "medgax-broken" - -/obj/effect/decal/mecha_wreckage/marauder - name = "Marauder wreckage" - icon_state = "marauder-broken" - -/obj/effect/decal/mecha_wreckage/mauler - name = "Mauler Wreckage" - icon_state = "mauler-broken" - desc = "The syndicate won't be very happy about this..." - -/obj/effect/decal/mecha_wreckage/seraph - name = "Seraph wreckage" - icon_state = "seraph-broken" - -/obj/effect/decal/mecha_wreckage/ripley - name = "Ripley wreckage" - icon_state = "ripley-broken" - -/obj/effect/decal/mecha_wreckage/ripley/New() - ..() - var/list/parts = list(/obj/item/mecha_parts/part/ripley_torso, - /obj/item/mecha_parts/part/ripley_left_arm, - /obj/item/mecha_parts/part/ripley_right_arm, - /obj/item/mecha_parts/part/ripley_left_leg, - /obj/item/mecha_parts/part/ripley_right_leg) - for(var/i=0;i<2;i++) - if(!isemptylist(parts) && prob(40)) - var/part = pick(parts) - welder_salvage += part - parts -= part - return - -/obj/effect/decal/mecha_wreckage/ripley/firefighter - name = "Firefighter wreckage" - icon_state = "firefighter-broken" - -/obj/effect/decal/mecha_wreckage/ripley/firefighter/New() - ..() - var/list/parts = list(/obj/item/mecha_parts/part/ripley_torso, - /obj/item/mecha_parts/part/ripley_left_arm, - /obj/item/mecha_parts/part/ripley_right_arm, - /obj/item/mecha_parts/part/ripley_left_leg, - /obj/item/mecha_parts/part/ripley_right_leg, - /obj/item/clothing/suit/fire) - for(var/i=0;i<2;i++) - if(!isemptylist(parts) && prob(40)) - var/part = pick(parts) - welder_salvage += part - parts -= part - return - -/obj/effect/decal/mecha_wreckage/ripley/deathripley - name = "Death-Ripley wreckage" - icon_state = "deathripley-broken" - -/obj/effect/decal/mecha_wreckage/durand - name = "Durand wreckage" - icon_state = "durand-broken" - -/obj/effect/decal/mecha_wreckage/durand/New() - ..() - var/list/parts = list( - /obj/item/mecha_parts/part/durand_torso, - /obj/item/mecha_parts/part/durand_head, - /obj/item/mecha_parts/part/durand_left_arm, - /obj/item/mecha_parts/part/durand_right_arm, - /obj/item/mecha_parts/part/durand_left_leg, - /obj/item/mecha_parts/part/durand_right_leg) - for(var/i=0;i<2;i++) - if(!isemptylist(parts) && prob(40)) - var/part = pick(parts) - welder_salvage += part - parts -= part - return - -/obj/effect/decal/mecha_wreckage/phazon - name = "Phazon wreckage" - icon_state = "phazon-broken" - - -/obj/effect/decal/mecha_wreckage/odysseus - name = "Odysseus wreckage" - icon_state = "odysseus-broken" - -/obj/effect/decal/mecha_wreckage/odysseus/New() - ..() - var/list/parts = list( - /obj/item/mecha_parts/part/odysseus_torso, - /obj/item/mecha_parts/part/odysseus_head, - /obj/item/mecha_parts/part/odysseus_left_arm, - /obj/item/mecha_parts/part/odysseus_right_arm, - /obj/item/mecha_parts/part/odysseus_left_leg, - /obj/item/mecha_parts/part/odysseus_right_leg) - for(var/i=0;i<2;i++) - if(!isemptylist(parts) && prob(40)) - var/part = pick(parts) - welder_salvage += part - parts -= part - return - -/obj/effect/decal/mecha_wreckage/odysseus/murdysseus - icon_state = "murdysseus-broken" - -/obj/effect/decal/mecha_wreckage/hoverpod - name = "Hover pod wreckage" - icon_state = "engineering_pod-broken" - -/obj/effect/decal/mecha_wreckage/janus - name = "Janus wreckage" - icon_state = "janus-broken" - description_info = "Due to the incredibly intricate design of this exosuit, it is impossible to salvage components from it." - -/obj/effect/decal/mecha_wreckage/shuttlecraft - name = "Shuttlecraft wreckage" - desc = "Remains of some unfortunate shuttlecraft. Completely unrepairable." - icon = 'icons/mecha/mecha64x64.dmi' - icon_state = "shuttle_standard-broken" - bound_width = 64 - bound_height = 64 +/////////////////////////////////// +//////// Mecha wreckage //////// +/////////////////////////////////// + + +/obj/effect/decal/mecha_wreckage + name = "Exosuit wreckage" + desc = "Remains of some unfortunate mecha. Completely unrepairable." + icon = 'icons/mecha/mecha.dmi' + density = TRUE + anchored = FALSE + opacity = 0 + var/list/welder_salvage = list(/obj/item/stack/material/plasteel,/obj/item/stack/material/steel,/obj/item/stack/rods) + var/list/wirecutters_salvage = list(/obj/item/stack/cable_coil) + var/list/crowbar_salvage + var/salvage_num = 5 + +/obj/effect/decal/mecha_wreckage/New() + ..() + crowbar_salvage = new + return + +/obj/effect/decal/mecha_wreckage/ex_act(severity) + if(severity < 2) + spawn + qdel(src) + return + +/obj/effect/decal/mecha_wreckage/bullet_act(var/obj/item/projectile/Proj) + return + + +/obj/effect/decal/mecha_wreckage/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(salvage_num <= 0) + to_chat(user, "You don't see anything that can be cut with [W].") + return + if (!isemptylist(welder_salvage) && WT.remove_fuel(0,user)) + var/type = prob(70)?pick(welder_salvage):null + if(type) + var/N = new type(get_turf(user)) + user.visible_message("[user] cuts [N] from [src]", "You cut [N] from [src]", "You hear a sound of welder nearby") + if(istype(N, /obj/item/mecha_parts/part)) + welder_salvage -= type + salvage_num-- + else + to_chat(user, "You failed to salvage anything valuable from [src].") + else + to_chat(user, "You need more welding fuel to complete this task.") + return + if(W.has_tool_quality(TOOL_WIRECUTTER)) + if(salvage_num <= 0) + to_chat(user, "You don't see anything that can be cut with [W].") + return + else if(!isemptylist(wirecutters_salvage)) + var/type = prob(70)?pick(wirecutters_salvage):null + if(type) + var/N = new type(get_turf(user)) + user.visible_message("[user] cuts [N] from [src].", "You cut [N] from [src].") + salvage_num-- + else + to_chat(user, "You failed to salvage anything valuable from [src].") + if(W.has_tool_quality(TOOL_CROWBAR)) + if(!isemptylist(crowbar_salvage)) + var/obj/S = pick(crowbar_salvage) + if(S) + S.loc = get_turf(user) + crowbar_salvage -= S + user.visible_message("[user] pries [S] from [src].", "You pry [S] from [src].") + return + else + to_chat(user, "You don't see anything that can be pried with [W].") + else + ..() + return + + +/obj/effect/decal/mecha_wreckage/gygax + name = "Gygax wreckage" + icon_state = "gygax-broken" + +/obj/effect/decal/mecha_wreckage/gygax/New() + ..() + var/list/parts = list(/obj/item/mecha_parts/part/gygax_torso, + /obj/item/mecha_parts/part/gygax_head, + /obj/item/mecha_parts/part/gygax_left_arm, + /obj/item/mecha_parts/part/gygax_right_arm, + /obj/item/mecha_parts/part/gygax_left_leg, + /obj/item/mecha_parts/part/gygax_right_leg) + for(var/i=0;i<2;i++) + if(!isemptylist(parts) && prob(40)) + var/part = pick(parts) + welder_salvage += part + parts -= part + return + +/obj/effect/decal/mecha_wreckage/gygax/dark + name = "Dark Gygax wreckage" + icon_state = "darkgygax-broken" + +/obj/effect/decal/mecha_wreckage/gygax/adv + name = "Advanced Dark Gygax wreckage" + icon_state = "darkgygax_adv-broken" + +/obj/effect/decal/mecha_wreckage/gygax/medgax + name = "Medgax wreckage" + icon_state = "medgax-broken" + +/obj/effect/decal/mecha_wreckage/gygax/serenity + name = "Serenity wreckage" + icon_state = "medgax-broken" + +/obj/effect/decal/mecha_wreckage/marauder + name = "Marauder wreckage" + icon_state = "marauder-broken" + +/obj/effect/decal/mecha_wreckage/mauler + name = "Mauler Wreckage" + icon_state = "mauler-broken" + desc = "The syndicate won't be very happy about this..." + +/obj/effect/decal/mecha_wreckage/seraph + name = "Seraph wreckage" + icon_state = "seraph-broken" + +/obj/effect/decal/mecha_wreckage/ripley + name = "Ripley wreckage" + icon_state = "ripley-broken" + +/obj/effect/decal/mecha_wreckage/ripley/New() + ..() + var/list/parts = list(/obj/item/mecha_parts/part/ripley_torso, + /obj/item/mecha_parts/part/ripley_left_arm, + /obj/item/mecha_parts/part/ripley_right_arm, + /obj/item/mecha_parts/part/ripley_left_leg, + /obj/item/mecha_parts/part/ripley_right_leg) + for(var/i=0;i<2;i++) + if(!isemptylist(parts) && prob(40)) + var/part = pick(parts) + welder_salvage += part + parts -= part + return + +/obj/effect/decal/mecha_wreckage/ripley/firefighter + name = "Firefighter wreckage" + icon_state = "firefighter-broken" + +/obj/effect/decal/mecha_wreckage/ripley/firefighter/New() + ..() + var/list/parts = list(/obj/item/mecha_parts/part/ripley_torso, + /obj/item/mecha_parts/part/ripley_left_arm, + /obj/item/mecha_parts/part/ripley_right_arm, + /obj/item/mecha_parts/part/ripley_left_leg, + /obj/item/mecha_parts/part/ripley_right_leg, + /obj/item/clothing/suit/fire) + for(var/i=0;i<2;i++) + if(!isemptylist(parts) && prob(40)) + var/part = pick(parts) + welder_salvage += part + parts -= part + return + +/obj/effect/decal/mecha_wreckage/ripley/deathripley + name = "Death-Ripley wreckage" + icon_state = "deathripley-broken" + +/obj/effect/decal/mecha_wreckage/durand + name = "Durand wreckage" + icon_state = "durand-broken" + +/obj/effect/decal/mecha_wreckage/durand/New() + ..() + var/list/parts = list( + /obj/item/mecha_parts/part/durand_torso, + /obj/item/mecha_parts/part/durand_head, + /obj/item/mecha_parts/part/durand_left_arm, + /obj/item/mecha_parts/part/durand_right_arm, + /obj/item/mecha_parts/part/durand_left_leg, + /obj/item/mecha_parts/part/durand_right_leg) + for(var/i=0;i<2;i++) + if(!isemptylist(parts) && prob(40)) + var/part = pick(parts) + welder_salvage += part + parts -= part + return + +/obj/effect/decal/mecha_wreckage/phazon + name = "Phazon wreckage" + icon_state = "phazon-broken" + + +/obj/effect/decal/mecha_wreckage/odysseus + name = "Odysseus wreckage" + icon_state = "odysseus-broken" + +/obj/effect/decal/mecha_wreckage/odysseus/New() + ..() + var/list/parts = list( + /obj/item/mecha_parts/part/odysseus_torso, + /obj/item/mecha_parts/part/odysseus_head, + /obj/item/mecha_parts/part/odysseus_left_arm, + /obj/item/mecha_parts/part/odysseus_right_arm, + /obj/item/mecha_parts/part/odysseus_left_leg, + /obj/item/mecha_parts/part/odysseus_right_leg) + for(var/i=0;i<2;i++) + if(!isemptylist(parts) && prob(40)) + var/part = pick(parts) + welder_salvage += part + parts -= part + return + +/obj/effect/decal/mecha_wreckage/odysseus/murdysseus + icon_state = "murdysseus-broken" + +/obj/effect/decal/mecha_wreckage/hoverpod + name = "Hover pod wreckage" + icon_state = "engineering_pod-broken" + +/obj/effect/decal/mecha_wreckage/janus + name = "Janus wreckage" + icon_state = "janus-broken" + description_info = "Due to the incredibly intricate design of this exosuit, it is impossible to salvage components from it." + +/obj/effect/decal/mecha_wreckage/shuttlecraft + name = "Shuttlecraft wreckage" + desc = "Remains of some unfortunate shuttlecraft. Completely unrepairable." + icon = 'icons/mecha/mecha64x64.dmi' + icon_state = "shuttle_standard-broken" + bound_width = 64 + bound_height = 64 diff --git a/code/game/mecha/medical/medical.dm b/code/game/mecha/medical/medical.dm index 973ddec3fc1..b001e50822c 100644 --- a/code/game/mecha/medical/medical.dm +++ b/code/game/mecha/medical/medical.dm @@ -1,43 +1,43 @@ -/obj/mecha/medical - max_hull_equip = 1 - max_weapon_equip = 0 - max_utility_equip = 2 - max_universal_equip = 1 - max_special_equip = 1 - - stomp_sound = 'sound/mecha/mechmove01.ogg' - - cargo_capacity = 1 - - starting_components = list( - /obj/item/mecha_parts/component/hull, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/lightweight, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - -/obj/mecha/medical/Initialize() - . = ..() - var/turf/T = get_turf(src) - if(isPlayerLevel(T.z)) - new /obj/item/mecha_parts/mecha_tracking(src) - -/* // One horrific bastardization of glorious inheritence dead. A billion to go. ~Mech -/obj/mecha/medical/mechturn(direction) - set_dir(direction) - playsound(src,'sound/mecha/mechmove01.ogg',40,1) - return 1 - -/obj/mecha/medical/mechstep(direction) - var/result = step(src,direction) - if(result) - playsound(src,'sound/mecha/mechstep.ogg',25,1) - return result - -/obj/mecha/medical/mechsteprand() - var/result = step_rand(src) - if(result) - playsound(src,'sound/mecha/mechstep.ogg',25,1) - return result -*/ +/obj/mecha/medical + max_hull_equip = 1 + max_weapon_equip = 0 + max_utility_equip = 2 + max_universal_equip = 1 + max_special_equip = 1 + + stomp_sound = 'sound/mecha/mechmove01.ogg' + + cargo_capacity = 1 + + starting_components = list( + /obj/item/mecha_parts/component/hull, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/lightweight, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + +/obj/mecha/medical/Initialize() + . = ..() + var/turf/T = get_turf(src) + if(isPlayerLevel(T.z)) + new /obj/item/mecha_parts/mecha_tracking(src) + +/* // One horrific bastardization of glorious inheritence dead. A billion to go. ~Mech +/obj/mecha/medical/mechturn(direction) + set_dir(direction) + playsound(src,'sound/mecha/mechmove01.ogg',40,1) + return 1 + +/obj/mecha/medical/mechstep(direction) + var/result = step(src,direction) + if(result) + playsound(src,'sound/mecha/mechstep.ogg',25,1) + return result + +/obj/mecha/medical/mechsteprand() + var/result = step_rand(src) + if(result) + playsound(src,'sound/mecha/mechstep.ogg',25,1) + return result +*/ diff --git a/code/game/mecha/medical/odysseus.dm b/code/game/mecha/medical/odysseus.dm index 929418e4a44..1215f756f1f 100644 --- a/code/game/mecha/medical/odysseus.dm +++ b/code/game/mecha/medical/odysseus.dm @@ -1,146 +1,146 @@ -/obj/mecha/medical/odysseus - desc = "These exosuits are developed and produced by Vey-Med. (© All rights reserved)." - name = "Odysseus" - catalogue_data = list( - /datum/category_item/catalogue/technology/odysseus, - /datum/category_item/catalogue/information/organization/vey_med - ) - icon_state = "odysseus" - initial_icon = "odysseus" - step_in = 2 - max_temperature = 15000 - health = 70 - maxhealth = 70 - wreckage = /obj/effect/decal/mecha_wreckage/odysseus - internal_damage_threshold = 35 - deflect_chance = 15 - step_energy_drain = 6 - var/obj/item/clothing/glasses/hud/health/mech/hud - - icon_scale_x = 1.2 - icon_scale_y = 1.2 - -/obj/mecha/medical/odysseus/New() - ..() - hud = new /obj/item/clothing/glasses/hud/health/mech(src) - return - -/obj/mecha/medical/odysseus/moved_inside(var/mob/living/carbon/human/H as mob) - if(..()) - if(H.glasses) - occupant_message(span_red("[H.glasses] prevent you from using [src] [hud]!")) - else - H.glasses = hud - H.recalculate_vis() - return 1 - else - return 0 - -/obj/mecha/medical/odysseus/go_out() - if(ishuman(occupant)) - var/mob/living/carbon/human/H = occupant - if(H.glasses == hud) - H.glasses = null - H.recalculate_vis() - ..() - return -/* - verb/set_perspective() - set name = "Set client perspective." - set category = "Exosuit Interface" - set src = usr.loc - var/perspective = input(usr, "Select a perspective type.", - "Client perspective", - occupant.client.perspective) in list(MOB_PERSPECTIVE,EYE_PERSPECTIVE) - to_world("[perspective]") - occupant.client.perspective = perspective - return - - verb/toggle_eye() - set name = "Toggle eye." - set category = "Exosuit Interface" - set src = usr.loc - if(occupant.client.eye == occupant) - occupant.client.eye = src - else - occupant.client.eye = occupant - to_world("[occupant.client.eye]") - return -*/ - -//TODO - Check documentation for client.eye and client.perspective... -/obj/item/clothing/glasses/hud/health/mech - name = "Integrated Medical Hud" - - -// process_hud(var/mob/M) //TODO VIS -/* - to_world("view(M)") - for(var/mob/mob in view(M)) - to_world("[mob]") - to_world("view(M.client)") - for(var/mob/mob in view(M.client)) - to_world("[mob]") - to_world("view(M.loc)") - for(var/mob/mob in view(M.loc)) - to_world("[mob]") - - - if(!M || M.stat || !(M in view(M))) return - if(!M.client) return - var/client/C = M.client - var/image/holder - for(var/mob/living/carbon/human/patient in view(M.loc)) - if(M.see_invisible < patient.invisibility) - continue - var/foundVirus = 0 - - for (var/ID in patient.virus2) - if (ID in virusDB) - foundVirus = 1 - break - - holder = patient.hud_list[HEALTH_HUD] - if(patient.stat == DEAD) - holder.icon_state = "hudhealth-100" - C.images += holder - else - holder.icon_state = RoundHealth((patient.health-config.health_threshold_crit)/(patient.getMaxHealth()-config.health_threshold_crit)*100) - C.images += holder - - holder = patient.hud_list[STATUS_HUD] - if(patient.isSynthetic()) - holder.icon_state = "hudrobo" - else if(patient.stat == DEAD) - holder.icon_state = "huddead" - else if(foundVirus) - holder.icon_state = "hudill" - else if(patient.has_brain_worms()) - var/mob/living/simple_mob/animal/borer/B = patient.has_brain_worms() - if(B.controlling) - holder.icon_state = "hudbrainworm" - else - holder.icon_state = "hudhealthy" - else - holder.icon_state = "hudhealthy" - - C.images += holder -*/ -/obj/mecha/medical/odysseus/loaded/Initialize() - . = ..() - var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/sleeper - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/tool/sleeper - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/tool/syringe_gun - ME.attach(src) - -//Meant for random spawns. -/obj/mecha/medical/odysseus/old - desc = "An aging combat exosuit utilized by many corporations. Originally developed to combat hostile alien lifeforms. This one is particularly worn looking and likely isn't as sturdy." - -/obj/mecha/medical/odysseus/old/New() - ..() - health = 25 - maxhealth = 50 //Just slightly worse. - cell.charge = rand(0, (cell.charge/2)) +/obj/mecha/medical/odysseus + desc = "These exosuits are developed and produced by Vey-Med. (© All rights reserved)." + name = "Odysseus" + catalogue_data = list( + /datum/category_item/catalogue/technology/odysseus, + /datum/category_item/catalogue/information/organization/vey_med + ) + icon_state = "odysseus" + initial_icon = "odysseus" + step_in = 2 + max_temperature = 15000 + health = 70 + maxhealth = 70 + wreckage = /obj/effect/decal/mecha_wreckage/odysseus + internal_damage_threshold = 35 + deflect_chance = 15 + step_energy_drain = 6 + var/obj/item/clothing/glasses/hud/health/mech/hud + + icon_scale_x = 1.2 + icon_scale_y = 1.2 + +/obj/mecha/medical/odysseus/New() + ..() + hud = new /obj/item/clothing/glasses/hud/health/mech(src) + return + +/obj/mecha/medical/odysseus/moved_inside(var/mob/living/carbon/human/H as mob) + if(..()) + if(H.glasses) + occupant_message(span_red("[H.glasses] prevent you from using [src] [hud]!")) + else + H.glasses = hud + H.recalculate_vis() + return 1 + else + return 0 + +/obj/mecha/medical/odysseus/go_out() + if(ishuman(occupant)) + var/mob/living/carbon/human/H = occupant + if(H.glasses == hud) + H.glasses = null + H.recalculate_vis() + ..() + return +/* + verb/set_perspective() + set name = "Set client perspective." + set category = "Exosuit Interface" + set src = usr.loc + var/perspective = input(usr, "Select a perspective type.", + "Client perspective", + occupant.client.perspective) in list(MOB_PERSPECTIVE,EYE_PERSPECTIVE) + to_world("[perspective]") + occupant.client.perspective = perspective + return + + verb/toggle_eye() + set name = "Toggle eye." + set category = "Exosuit Interface" + set src = usr.loc + if(occupant.client.eye == occupant) + occupant.client.eye = src + else + occupant.client.eye = occupant + to_world("[occupant.client.eye]") + return +*/ + +//TODO - Check documentation for client.eye and client.perspective... +/obj/item/clothing/glasses/hud/health/mech + name = "Integrated Medical Hud" + + +// process_hud(var/mob/M) //TODO VIS +/* + to_world("view(M)") + for(var/mob/mob in view(M)) + to_world("[mob]") + to_world("view(M.client)") + for(var/mob/mob in view(M.client)) + to_world("[mob]") + to_world("view(M.loc)") + for(var/mob/mob in view(M.loc)) + to_world("[mob]") + + + if(!M || M.stat || !(M in view(M))) return + if(!M.client) return + var/client/C = M.client + var/image/holder + for(var/mob/living/carbon/human/patient in view(M.loc)) + if(M.see_invisible < patient.invisibility) + continue + var/foundVirus = 0 + + for (var/ID in patient.virus2) + if (ID in virusDB) + foundVirus = 1 + break + + holder = patient.hud_list[HEALTH_HUD] + if(patient.stat == DEAD) + holder.icon_state = "hudhealth-100" + C.images += holder + else + holder.icon_state = RoundHealth((patient.health-config.health_threshold_crit)/(patient.getMaxHealth()-config.health_threshold_crit)*100) + C.images += holder + + holder = patient.hud_list[STATUS_HUD] + if(patient.isSynthetic()) + holder.icon_state = "hudrobo" + else if(patient.stat == DEAD) + holder.icon_state = "huddead" + else if(foundVirus) + holder.icon_state = "hudill" + else if(patient.has_brain_worms()) + var/mob/living/simple_mob/animal/borer/B = patient.has_brain_worms() + if(B.controlling) + holder.icon_state = "hudbrainworm" + else + holder.icon_state = "hudhealthy" + else + holder.icon_state = "hudhealthy" + + C.images += holder +*/ +/obj/mecha/medical/odysseus/loaded/Initialize() + . = ..() + var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/sleeper + ME.attach(src) + ME = new /obj/item/mecha_parts/mecha_equipment/tool/sleeper + ME.attach(src) + ME = new /obj/item/mecha_parts/mecha_equipment/tool/syringe_gun + ME.attach(src) + +//Meant for random spawns. +/obj/mecha/medical/odysseus/old + desc = "An aging combat exosuit utilized by many corporations. Originally developed to combat hostile alien lifeforms. This one is particularly worn looking and likely isn't as sturdy." + +/obj/mecha/medical/odysseus/old/New() + ..() + health = 25 + maxhealth = 50 //Just slightly worse. + cell.charge = rand(0, (cell.charge/2)) diff --git a/code/game/mecha/working/ripley.dm b/code/game/mecha/working/ripley.dm index 8d4ee98fbf9..eb5a53fbfaf 100644 --- a/code/game/mecha/working/ripley.dm +++ b/code/game/mecha/working/ripley.dm @@ -1,150 +1,150 @@ -/obj/mecha/working/ripley - desc = "Autonomous Power Loader Unit. The workhorse of the exosuit world." - name = "APLU \"Ripley\"" - icon_state = "ripley" - initial_icon = "ripley" - step_in = 5 // vorestation edit, was 6 but that's PAINFULLY slow - step_energy_drain = 5 // vorestation edit because 10 drained a significant chunk of its cell before you even got out the airlock - max_temperature = 20000 - health = 200 - maxhealth = 200 //Don't forget to update the /old variant if you change this number. - wreckage = /obj/effect/decal/mecha_wreckage/ripley - cargo_capacity = 10 - var/obj/item/weapon/mining_scanner/orescanner // vorestation addition - - minimum_penetration = 10 - - encumbrance_gap = 2 - - starting_components = list( - /obj/item/mecha_parts/component/hull/durable, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/mining, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - - icon_scale_x = 1.2 - icon_scale_y = 1.2 - -/obj/mecha/working/ripley/Move() - . = ..() - if(.) - collect_ore() - -/obj/mecha/working/ripley/proc/collect_ore() - if(locate(/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp) in equipment) - var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in cargo - if(ore_box) - for(var/obj/item/weapon/ore/ore in range(1, src)) - if(ore.Adjacent(src) && ((get_dir(src, ore) & dir) || ore.loc == loc)) //we can reach it and it's in front of us? grab it! - ore_box.stored_ore[ore.material]++ - qdel(ore) - -/obj/mecha/working/ripley/Destroy() - for(var/atom/movable/A in src.cargo) - A.loc = loc - var/turf/T = loc - if(istype(T)) - T.Entered(A) - step_rand(A) - cargo.Cut() - ..() - -/obj/mecha/working/ripley/firefighter - desc = "Standard APLU chassis was refitted with additional thermal protection and cistern." - name = "APLU \"Firefighter\"" - icon_state = "firefighter" - initial_icon = "firefighter" - max_temperature = 65000 - health = 250 - lights_power = 8 - wreckage = /obj/effect/decal/mecha_wreckage/ripley/firefighter - max_hull_equip = 2 - max_weapon_equip = 0 - max_utility_equip = 2 - max_universal_equip = 1 - max_special_equip = 1 - -/obj/mecha/working/ripley/deathripley - desc = "OH SHIT IT'S THE DEATHSQUAD WE'RE ALL GONNA DIE" - name = "DEATH-RIPLEY" - icon_state = "deathripley" - initial_icon = "deathripley" - step_in = 2 - opacity=0 - lights_power = 60 - wreckage = /obj/effect/decal/mecha_wreckage/ripley/deathripley - step_energy_drain = 0 - max_hull_equip = 1 - max_weapon_equip = 1 - max_utility_equip = 3 - max_universal_equip = 1 - max_special_equip = 1 - -/obj/mecha/working/ripley/deathripley/Initialize() - . = ..() - var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp/safety - ME.attach(src) - return - -/obj/mecha/working/ripley/mining - desc = "An old, dusty mining ripley." - name = "APLU \"Miner\"" - -/obj/mecha/working/ripley/mining/Initialize() - . = ..() - //Attach drill - if(prob(25)) //Possible diamond drill... Feeling lucky? - var/obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill/D = new /obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill - D.attach(src) - else - var/obj/item/mecha_parts/mecha_equipment/tool/drill/D = new /obj/item/mecha_parts/mecha_equipment/tool/drill - D.attach(src) - - //Attach hydrolic clamp - var/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp/HC = new /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp - HC.attach(src) - for(var/obj/item/mecha_parts/mecha_tracking/B in src.contents)//Deletes the beacon so it can't be found easily - qdel (B) - -/obj/mecha/working/ripley/antique - name = "APLU \"Geiger\"" - desc = "You can't beat the classics." - icon_state = "ripley-old" - initial_icon = "ripley-old" - - show_pilot = TRUE - pilot_lift = 5 - - max_utility_equip = 1 - max_universal_equip = 3 - - icon_scale_x = 1 - icon_scale_y = 1 - -//Vorestation Edit Start - -/obj/mecha/working/ripley/New() - ..() - orescanner = new /obj/item/weapon/mining_scanner - -/obj/mecha/working/ripley/verb/detect_ore() - set category = "Exosuit Interface" - set name = "Detect Ores" - set src = usr.loc - set popup_menu = 0 - - orescanner.attack_self(usr) - -//Vorestation Edit End - -//Meant for random spawns. -/obj/mecha/working/ripley/mining/old - desc = "An old, dusty mining ripley." - -/obj/mecha/working/ripley/mining/old/New() - ..() - health = 25 - maxhealth = 190 //Just slightly worse. - cell.charge = rand(0, cell.charge) +/obj/mecha/working/ripley + desc = "Autonomous Power Loader Unit. The workhorse of the exosuit world." + name = "APLU \"Ripley\"" + icon_state = "ripley" + initial_icon = "ripley" + step_in = 5 // vorestation edit, was 6 but that's PAINFULLY slow + step_energy_drain = 5 // vorestation edit because 10 drained a significant chunk of its cell before you even got out the airlock + max_temperature = 20000 + health = 200 + maxhealth = 200 //Don't forget to update the /old variant if you change this number. + wreckage = /obj/effect/decal/mecha_wreckage/ripley + cargo_capacity = 10 + var/obj/item/weapon/mining_scanner/orescanner // vorestation addition + + minimum_penetration = 10 + + encumbrance_gap = 2 + + starting_components = list( + /obj/item/mecha_parts/component/hull/durable, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/mining, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + + icon_scale_x = 1.2 + icon_scale_y = 1.2 + +/obj/mecha/working/ripley/Move() + . = ..() + if(.) + collect_ore() + +/obj/mecha/working/ripley/proc/collect_ore() + if(locate(/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp) in equipment) + var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in cargo + if(ore_box) + for(var/obj/item/weapon/ore/ore in range(1, src)) + if(ore.Adjacent(src) && ((get_dir(src, ore) & dir) || ore.loc == loc)) //we can reach it and it's in front of us? grab it! + ore_box.stored_ore[ore.material]++ + qdel(ore) + +/obj/mecha/working/ripley/Destroy() + for(var/atom/movable/A in src.cargo) + A.loc = loc + var/turf/T = loc + if(istype(T)) + T.Entered(A) + step_rand(A) + cargo.Cut() + ..() + +/obj/mecha/working/ripley/firefighter + desc = "Standard APLU chassis was refitted with additional thermal protection and cistern." + name = "APLU \"Firefighter\"" + icon_state = "firefighter" + initial_icon = "firefighter" + max_temperature = 65000 + health = 250 + lights_power = 8 + wreckage = /obj/effect/decal/mecha_wreckage/ripley/firefighter + max_hull_equip = 2 + max_weapon_equip = 0 + max_utility_equip = 2 + max_universal_equip = 1 + max_special_equip = 1 + +/obj/mecha/working/ripley/deathripley + desc = "OH SHIT IT'S THE DEATHSQUAD WE'RE ALL GONNA DIE" + name = "DEATH-RIPLEY" + icon_state = "deathripley" + initial_icon = "deathripley" + step_in = 2 + opacity=0 + lights_power = 60 + wreckage = /obj/effect/decal/mecha_wreckage/ripley/deathripley + step_energy_drain = 0 + max_hull_equip = 1 + max_weapon_equip = 1 + max_utility_equip = 3 + max_universal_equip = 1 + max_special_equip = 1 + +/obj/mecha/working/ripley/deathripley/Initialize() + . = ..() + var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp/safety + ME.attach(src) + return + +/obj/mecha/working/ripley/mining + desc = "An old, dusty mining ripley." + name = "APLU \"Miner\"" + +/obj/mecha/working/ripley/mining/Initialize() + . = ..() + //Attach drill + if(prob(25)) //Possible diamond drill... Feeling lucky? + var/obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill/D = new /obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill + D.attach(src) + else + var/obj/item/mecha_parts/mecha_equipment/tool/drill/D = new /obj/item/mecha_parts/mecha_equipment/tool/drill + D.attach(src) + + //Attach hydrolic clamp + var/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp/HC = new /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp + HC.attach(src) + for(var/obj/item/mecha_parts/mecha_tracking/B in src.contents)//Deletes the beacon so it can't be found easily + qdel (B) + +/obj/mecha/working/ripley/antique + name = "APLU \"Geiger\"" + desc = "You can't beat the classics." + icon_state = "ripley-old" + initial_icon = "ripley-old" + + show_pilot = TRUE + pilot_lift = 5 + + max_utility_equip = 1 + max_universal_equip = 3 + + icon_scale_x = 1 + icon_scale_y = 1 + +//Vorestation Edit Start + +/obj/mecha/working/ripley/New() + ..() + orescanner = new /obj/item/weapon/mining_scanner + +/obj/mecha/working/ripley/verb/detect_ore() + set category = "Exosuit Interface" + set name = "Detect Ores" + set src = usr.loc + set popup_menu = 0 + + orescanner.attack_self(usr) + +//Vorestation Edit End + +//Meant for random spawns. +/obj/mecha/working/ripley/mining/old + desc = "An old, dusty mining ripley." + +/obj/mecha/working/ripley/mining/old/New() + ..() + health = 25 + maxhealth = 190 //Just slightly worse. + cell.charge = rand(0, cell.charge) diff --git a/code/game/mecha/working/working.dm b/code/game/mecha/working/working.dm index f2149b1cdd0..6d19bbd6b57 100644 --- a/code/game/mecha/working/working.dm +++ b/code/game/mecha/working/working.dm @@ -1,63 +1,63 @@ -/obj/mecha/working - internal_damage_threshold = 60 - max_hull_equip = 1 - max_weapon_equip = 0 - max_utility_equip = 3 - max_universal_equip = 1 - max_special_equip = 1 - -/obj/mecha/working/Initialize() - . = ..() - var/turf/T = get_turf(src) - if(isPlayerLevel(T.z)) - new /obj/item/mecha_parts/mecha_tracking(src) - -/* This stuff has been generalized! -/obj/mecha/working/Destroy() - for(var/mob/M in src) - if(M==src.occupant) - continue - M.loc = get_turf(src) - M.loc.Entered(M) - step_rand(M) - for(var/atom/movable/A in src.cargo) - A.loc = get_turf(src) - var/turf/T = get_turf(A) - if(T) - T.Entered(A) - step_rand(A) - ..() - return - -/obj/mecha/working/Topic(href, href_list) - ..() - if(href_list["drop_from_cargo"]) - var/obj/O = locate(href_list["drop_from_cargo"]) - if(O && O in src.cargo) - src.occupant_message("You unload [O].") - O.loc = get_turf(src) - src.cargo -= O - var/turf/T = get_turf(O) - if(T) - T.Entered(O) - src.log_message("Unloaded [O]. Cargo compartment capacity: [cargo_capacity - src.cargo.len]") - return - -/obj/mecha/working/Exit(atom/movable/O) - if(O in cargo) - return 0 - return ..() - -/obj/mecha/working/get_stats_part() - var/output = ..() - output += "Cargo Compartment Contents:
                    " - if(src.cargo.len) - for(var/obj/O in src.cargo) - output += "Unload : [O]
                    " - else - output += "Nothing" - output += "
                    " - return output -*/ -/obj/mecha/working/range_action(atom/target as obj|mob|turf) - return +/obj/mecha/working + internal_damage_threshold = 60 + max_hull_equip = 1 + max_weapon_equip = 0 + max_utility_equip = 3 + max_universal_equip = 1 + max_special_equip = 1 + +/obj/mecha/working/Initialize() + . = ..() + var/turf/T = get_turf(src) + if(isPlayerLevel(T.z)) + new /obj/item/mecha_parts/mecha_tracking(src) + +/* This stuff has been generalized! +/obj/mecha/working/Destroy() + for(var/mob/M in src) + if(M==src.occupant) + continue + M.loc = get_turf(src) + M.loc.Entered(M) + step_rand(M) + for(var/atom/movable/A in src.cargo) + A.loc = get_turf(src) + var/turf/T = get_turf(A) + if(T) + T.Entered(A) + step_rand(A) + ..() + return + +/obj/mecha/working/Topic(href, href_list) + ..() + if(href_list["drop_from_cargo"]) + var/obj/O = locate(href_list["drop_from_cargo"]) + if(O && O in src.cargo) + src.occupant_message("You unload [O].") + O.loc = get_turf(src) + src.cargo -= O + var/turf/T = get_turf(O) + if(T) + T.Entered(O) + src.log_message("Unloaded [O]. Cargo compartment capacity: [cargo_capacity - src.cargo.len]") + return + +/obj/mecha/working/Exit(atom/movable/O) + if(O in cargo) + return 0 + return ..() + +/obj/mecha/working/get_stats_part() + var/output = ..() + output += "Cargo Compartment Contents:
                    " + if(src.cargo.len) + for(var/obj/O in src.cargo) + output += "Unload : [O]
                    " + else + output += "Nothing" + output += "
                    " + return output +*/ +/obj/mecha/working/range_action(atom/target as obj|mob|turf) + return diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index 5e6f0165615..38893adcbaa 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -1,208 +1,208 @@ - - -/atom/movable - var/can_buckle = FALSE - var/buckle_movable = 0 - var/buckle_dir = 0 - var/buckle_lying = -1 //bed-like behavior, forces mob.lying = buckle_lying if != -1 - var/buckle_require_restraints = 0 //require people to be handcuffed before being able to buckle. eg: pipes -// var/mob/living/buckled_mob = null - var/list/mob/living/buckled_mobs = null //list() - var/max_buckled_mobs = 1 - - -/atom/movable/attack_hand(mob/living/user) - . = ..() -// if(can_buckle && buckled_mob) -// user_unbuckle_mob(user) - - if(can_buckle && has_buckled_mobs()) - if(buckled_mobs.len > 1) - var/unbuckled = tgui_input_list(user, "Who do you wish to unbuckle?","Unbuckle Who?", buckled_mobs) - if(user_unbuckle_mob(unbuckled, user)) - return TRUE - else - if(user_unbuckle_mob(buckled_mobs[1], user)) - return TRUE - -/obj/proc/attack_alien(mob/user as mob) //For calling in the event of Xenomorph or other alien checks. - return - -/obj/attack_robot(mob/living/user) - if(Adjacent(user) && has_buckled_mobs()) //Checks if what we're touching is adjacent to us and has someone buckled to it. This should prevent interacting with anti-robot manual valves among other things. - return attack_hand(user) //Process as if we're a normal person touching the object. - return ..() //Otherwise, treat this as an AI click like usual. - -/atom/movable/MouseDrop_T(mob/living/M, mob/living/user) - . = ..() - if(can_buckle && istype(M)) - if(user_buckle_mob(M, user)) - return TRUE - -/atom/movable/proc/has_buckled_mobs() - return LAZYLEN(buckled_mobs) - -/atom/movable/Destroy() - unbuckle_all_mobs() - return ..() - - -/atom/movable/proc/buckle_mob(mob/living/M, forced = FALSE, check_loc = TRUE) - if(check_loc && M.loc != loc) - return FALSE - - if(!can_buckle_check(M, forced)) - return FALSE - - if(M == src) - stack_trace("Recursive buckle warning: [M] being buckled to self.") - return - - M.buckled = src - M.facing_dir = null - M.set_dir(buckle_dir ? buckle_dir : dir) - M.update_canmove() - M.update_floating( M.Check_Dense_Object() ) -// buckled_mob = M - buckled_mobs |= M - - //VOREStation Add - if(riding_datum) - riding_datum.ridden = src - riding_datum.handle_vehicle_offsets() - M.update_water() - //VOREStation Add End - - post_buckle_mob(M) - M.throw_alert("buckled", /obj/screen/alert/restrained/buckled, new_master = src) - return TRUE - -/atom/movable/proc/unbuckle_mob(mob/living/buckled_mob, force = FALSE) - if(!buckled_mob) // If we didn't get told which mob needs to get unbuckled, just assume its the first one on the list. - if(has_buckled_mobs()) - buckled_mob = buckled_mobs[1] - else - return - - if(buckled_mob && buckled_mob.buckled == src) - . = buckled_mob - buckled_mob.buckled = null - buckled_mob.anchored = initial(buckled_mob.anchored) - buckled_mob.update_canmove() - buckled_mob.update_floating( buckled_mob.Check_Dense_Object() ) - buckled_mob.clear_alert("buckled") - // buckled_mob = null - buckled_mobs -= buckled_mob - - //VOREStation Add - buckled_mob.update_water() - if(riding_datum) - riding_datum.restore_position(buckled_mob) - riding_datum.handle_vehicle_offsets() // So the person in back goes to the front. - //VOREStation Add End - post_buckle_mob(.) - -/atom/movable/proc/unbuckle_all_mobs(force = FALSE) - if(!has_buckled_mobs()) - return - for(var/m in buckled_mobs) - unbuckle_mob(m, force) - -//Handle any extras after buckling/unbuckling -//Called on buckle_mob() and unbuckle_mob() -/atom/movable/proc/post_buckle_mob(mob/living/M) - return - -//Wrapper procs that handle sanity and user feedback -/atom/movable/proc/user_buckle_mob(mob/living/M, mob/user, var/forced = FALSE, var/silent = FALSE) - if(!ticker) - to_chat(user, "You can't buckle anyone in before the game starts.") - return FALSE // Is this really needed? - if(!user.Adjacent(M) || user.restrained() || user.stat || istype(user, /mob/living/silicon/pai)) - return FALSE - if(M in buckled_mobs) - to_chat(user, "\The [M] is already buckled to \the [src].") - return FALSE - if(!can_buckle_check(M, forced)) - return FALSE - - add_fingerprint(user) -// unbuckle_mob() - - //can't buckle unless you share locs so try to move M to the obj. - if(M.loc != src.loc) - if(M.Adjacent(src) && user.Adjacent(src)) - M.forceMove(get_turf(src)) - // step_towards(M, src) - - . = buckle_mob(M, forced) - playsound(src, 'sound/effects/seatbelt.ogg', 50, 1) - if(.) - var/reveal_message = list("buckled_mob" = null, "buckled_to" = null) //VORE EDIT: This being a list and messages existing for the buckle target atom. - if(!silent) - if(M == user) - reveal_message["buckled_mob"] = "You come out of hiding and buckle yourself to [src]." //VORE EDIT - reveal_message["buckled_to"] = "You come out of hiding as [M.name] buckles themselves to you." //VORE EDIT - M.visible_message(\ - "[M.name] buckles themselves to [src].",\ - "You buckle yourself to [src].",\ - "You hear metal clanking.") - else - reveal_message["buckled_mob"] = "You are revealed as you are buckled to [src]." //VORE EDIT - reveal_message["buckled_to"] = "You are revealed as [M.name] is buckled to you." //VORE EDIT - M.visible_message(\ - "[M.name] is buckled to [src] by [user.name]!",\ - "You are buckled to [src] by [user.name]!",\ - "You hear metal clanking.") - - M.reveal(silent, reveal_message["buckled_mob"]) //Reveal people so they aren't buckled to chairs from behind. //VORE EDIT, list arg instead of simple message var for buckled mob - //Vore edit start - var/mob/living/L = src - if(istype(L)) - L.reveal(silent, reveal_message["buckled_to"]) - //Vore edit end - -/atom/movable/proc/user_unbuckle_mob(mob/living/buckled_mob, mob/user) - var/mob/living/M = unbuckle_mob(buckled_mob) - playsound(src, 'sound/effects/seatbelt.ogg', 50, 1) - if(M) - if(M != user) - M.visible_message(\ - "[M.name] was unbuckled by [user.name]!",\ - "You were unbuckled from [src] by [user.name].",\ - "You hear metal clanking.") - else - M.visible_message(\ - "[M.name] unbuckled themselves!",\ - "You unbuckle yourself from [src].",\ - "You hear metal clanking.") - add_fingerprint(user) - return M - -/atom/movable/proc/handle_buckled_mob_movement(atom/old_loc, direct, movetime) - for(var/mob/living/L as anything in buckled_mobs) - if(!L.Move(loc, direct, movetime)) - L.forceMove(loc, direct, movetime) - L.last_move = last_move - L.inertia_dir = last_move - - if(!buckle_dir) - L.set_dir(dir) - else - L.set_dir(buckle_dir) - -/atom/movable/proc/can_buckle_check(mob/living/M, forced = FALSE) - if(!buckled_mobs) - buckled_mobs = list() - - if(!istype(M)) - return FALSE - - if((!can_buckle && !forced) || M.buckled || M.pinned.len || (buckled_mobs.len >= max_buckled_mobs) || (buckle_require_restraints && !M.restrained())) - return FALSE - - if(has_buckled_mobs() && buckled_mobs.len >= max_buckled_mobs) //Handles trying to buckle yourself to the chair when someone is on it - to_chat(M, "\The [src] can't buckle anymore people.") - return FALSE - - return TRUE + + +/atom/movable + var/can_buckle = FALSE + var/buckle_movable = 0 + var/buckle_dir = 0 + var/buckle_lying = -1 //bed-like behavior, forces mob.lying = buckle_lying if != -1 + var/buckle_require_restraints = 0 //require people to be handcuffed before being able to buckle. eg: pipes +// var/mob/living/buckled_mob = null + var/list/mob/living/buckled_mobs = null //list() + var/max_buckled_mobs = 1 + + +/atom/movable/attack_hand(mob/living/user) + . = ..() +// if(can_buckle && buckled_mob) +// user_unbuckle_mob(user) + + if(can_buckle && has_buckled_mobs()) + if(buckled_mobs.len > 1) + var/unbuckled = tgui_input_list(user, "Who do you wish to unbuckle?","Unbuckle Who?", buckled_mobs) + if(user_unbuckle_mob(unbuckled, user)) + return TRUE + else + if(user_unbuckle_mob(buckled_mobs[1], user)) + return TRUE + +/obj/proc/attack_alien(mob/user as mob) //For calling in the event of Xenomorph or other alien checks. + return + +/obj/attack_robot(mob/living/user) + if(Adjacent(user) && has_buckled_mobs()) //Checks if what we're touching is adjacent to us and has someone buckled to it. This should prevent interacting with anti-robot manual valves among other things. + return attack_hand(user) //Process as if we're a normal person touching the object. + return ..() //Otherwise, treat this as an AI click like usual. + +/atom/movable/MouseDrop_T(mob/living/M, mob/living/user) + . = ..() + if(can_buckle && istype(M)) + if(user_buckle_mob(M, user)) + return TRUE + +/atom/movable/proc/has_buckled_mobs() + return LAZYLEN(buckled_mobs) + +/atom/movable/Destroy() + unbuckle_all_mobs() + return ..() + + +/atom/movable/proc/buckle_mob(mob/living/M, forced = FALSE, check_loc = TRUE) + if(check_loc && M.loc != loc) + return FALSE + + if(!can_buckle_check(M, forced)) + return FALSE + + if(M == src) + stack_trace("Recursive buckle warning: [M] being buckled to self.") + return + + M.buckled = src + M.facing_dir = null + M.set_dir(buckle_dir ? buckle_dir : dir) + M.update_canmove() + M.update_floating( M.Check_Dense_Object() ) +// buckled_mob = M + buckled_mobs |= M + + //VOREStation Add + if(riding_datum) + riding_datum.ridden = src + riding_datum.handle_vehicle_offsets() + M.update_water() + //VOREStation Add End + + post_buckle_mob(M) + M.throw_alert("buckled", /obj/screen/alert/restrained/buckled, new_master = src) + return TRUE + +/atom/movable/proc/unbuckle_mob(mob/living/buckled_mob, force = FALSE) + if(!buckled_mob) // If we didn't get told which mob needs to get unbuckled, just assume its the first one on the list. + if(has_buckled_mobs()) + buckled_mob = buckled_mobs[1] + else + return + + if(buckled_mob && buckled_mob.buckled == src) + . = buckled_mob + buckled_mob.buckled = null + buckled_mob.anchored = initial(buckled_mob.anchored) + buckled_mob.update_canmove() + buckled_mob.update_floating( buckled_mob.Check_Dense_Object() ) + buckled_mob.clear_alert("buckled") + // buckled_mob = null + buckled_mobs -= buckled_mob + + //VOREStation Add + buckled_mob.update_water() + if(riding_datum) + riding_datum.restore_position(buckled_mob) + riding_datum.handle_vehicle_offsets() // So the person in back goes to the front. + //VOREStation Add End + post_buckle_mob(.) + +/atom/movable/proc/unbuckle_all_mobs(force = FALSE) + if(!has_buckled_mobs()) + return + for(var/m in buckled_mobs) + unbuckle_mob(m, force) + +//Handle any extras after buckling/unbuckling +//Called on buckle_mob() and unbuckle_mob() +/atom/movable/proc/post_buckle_mob(mob/living/M) + return + +//Wrapper procs that handle sanity and user feedback +/atom/movable/proc/user_buckle_mob(mob/living/M, mob/user, var/forced = FALSE, var/silent = FALSE) + if(!ticker) + to_chat(user, "You can't buckle anyone in before the game starts.") + return FALSE // Is this really needed? + if(!user.Adjacent(M) || user.restrained() || user.stat || istype(user, /mob/living/silicon/pai)) + return FALSE + if(M in buckled_mobs) + to_chat(user, "\The [M] is already buckled to \the [src].") + return FALSE + if(!can_buckle_check(M, forced)) + return FALSE + + add_fingerprint(user) +// unbuckle_mob() + + //can't buckle unless you share locs so try to move M to the obj. + if(M.loc != src.loc) + if(M.Adjacent(src) && user.Adjacent(src)) + M.forceMove(get_turf(src)) + // step_towards(M, src) + + . = buckle_mob(M, forced) + playsound(src, 'sound/effects/seatbelt.ogg', 50, 1) + if(.) + var/reveal_message = list("buckled_mob" = null, "buckled_to" = null) //VORE EDIT: This being a list and messages existing for the buckle target atom. + if(!silent) + if(M == user) + reveal_message["buckled_mob"] = "You come out of hiding and buckle yourself to [src]." //VORE EDIT + reveal_message["buckled_to"] = "You come out of hiding as [M.name] buckles themselves to you." //VORE EDIT + M.visible_message(\ + "[M.name] buckles themselves to [src].",\ + "You buckle yourself to [src].",\ + "You hear metal clanking.") + else + reveal_message["buckled_mob"] = "You are revealed as you are buckled to [src]." //VORE EDIT + reveal_message["buckled_to"] = "You are revealed as [M.name] is buckled to you." //VORE EDIT + M.visible_message(\ + "[M.name] is buckled to [src] by [user.name]!",\ + "You are buckled to [src] by [user.name]!",\ + "You hear metal clanking.") + + M.reveal(silent, reveal_message["buckled_mob"]) //Reveal people so they aren't buckled to chairs from behind. //VORE EDIT, list arg instead of simple message var for buckled mob + //Vore edit start + var/mob/living/L = src + if(istype(L)) + L.reveal(silent, reveal_message["buckled_to"]) + //Vore edit end + +/atom/movable/proc/user_unbuckle_mob(mob/living/buckled_mob, mob/user) + var/mob/living/M = unbuckle_mob(buckled_mob) + playsound(src, 'sound/effects/seatbelt.ogg', 50, 1) + if(M) + if(M != user) + M.visible_message(\ + "[M.name] was unbuckled by [user.name]!",\ + "You were unbuckled from [src] by [user.name].",\ + "You hear metal clanking.") + else + M.visible_message(\ + "[M.name] unbuckled themselves!",\ + "You unbuckle yourself from [src].",\ + "You hear metal clanking.") + add_fingerprint(user) + return M + +/atom/movable/proc/handle_buckled_mob_movement(atom/old_loc, direct, movetime) + for(var/mob/living/L as anything in buckled_mobs) + if(!L.Move(loc, direct, movetime)) + L.forceMove(loc, direct, movetime) + L.last_move = last_move + L.inertia_dir = last_move + + if(!buckle_dir) + L.set_dir(dir) + else + L.set_dir(buckle_dir) + +/atom/movable/proc/can_buckle_check(mob/living/M, forced = FALSE) + if(!buckled_mobs) + buckled_mobs = list() + + if(!istype(M)) + return FALSE + + if((!can_buckle && !forced) || M.buckled || M.pinned.len || (buckled_mobs.len >= max_buckled_mobs) || (buckle_require_restraints && !M.restrained())) + return FALSE + + if(has_buckled_mobs() && buckled_mobs.len >= max_buckled_mobs) //Handles trying to buckle yourself to the chair when someone is on it + to_chat(M, "\The [src] can't buckle anymore people.") + return FALSE + + return TRUE diff --git a/code/game/objects/effects/bump_teleporter.dm b/code/game/objects/effects/bump_teleporter.dm index 581aeea16f8..5c579e7925c 100644 --- a/code/game/objects/effects/bump_teleporter.dm +++ b/code/game/objects/effects/bump_teleporter.dm @@ -1,34 +1,34 @@ -var/list/obj/effect/bump_teleporter/BUMP_TELEPORTERS = list() - -/obj/effect/bump_teleporter - name = "bump-teleporter" - icon = 'icons/mob/screen1.dmi' - icon_state = "x2" - var/id = null //id of this bump_teleporter. - var/id_target = null //id of bump_teleporter which this moves you to. - invisibility = 101 //nope, can't see this - anchored = TRUE - density = TRUE - opacity = 0 - -/obj/effect/bump_teleporter/New() - ..() - BUMP_TELEPORTERS += src - -/obj/effect/bump_teleporter/Destroy() - BUMP_TELEPORTERS -= src - return ..() - -/obj/effect/bump_teleporter/Bumped(atom/user) - if(!ismob(user)) - //user.loc = src.loc //Stop at teleporter location - return - var/mob/M = user //VOREStation edit - if(!id_target) - //user.loc = src.loc //Stop at teleporter location, there is nowhere to teleport to. - return - - for(var/obj/effect/bump_teleporter/BT in BUMP_TELEPORTERS) - if(BT.id == src.id_target) - M.forceMove(BT.loc) //Teleport to location with correct id. //VOREStation Edit - return +var/list/obj/effect/bump_teleporter/BUMP_TELEPORTERS = list() + +/obj/effect/bump_teleporter + name = "bump-teleporter" + icon = 'icons/mob/screen1.dmi' + icon_state = "x2" + var/id = null //id of this bump_teleporter. + var/id_target = null //id of bump_teleporter which this moves you to. + invisibility = 101 //nope, can't see this + anchored = TRUE + density = TRUE + opacity = 0 + +/obj/effect/bump_teleporter/New() + ..() + BUMP_TELEPORTERS += src + +/obj/effect/bump_teleporter/Destroy() + BUMP_TELEPORTERS -= src + return ..() + +/obj/effect/bump_teleporter/Bumped(atom/user) + if(!ismob(user)) + //user.loc = src.loc //Stop at teleporter location + return + var/mob/M = user //VOREStation edit + if(!id_target) + //user.loc = src.loc //Stop at teleporter location, there is nowhere to teleport to. + return + + for(var/obj/effect/bump_teleporter/BT in BUMP_TELEPORTERS) + if(BT.id == src.id_target) + M.forceMove(BT.loc) //Teleport to location with correct id. //VOREStation Edit + return diff --git a/code/game/objects/effects/decals/Cleanable/aliens.dm b/code/game/objects/effects/decals/Cleanable/aliens.dm index 1df3acb2b73..57a32434af2 100644 --- a/code/game/objects/effects/decals/Cleanable/aliens.dm +++ b/code/game/objects/effects/decals/Cleanable/aliens.dm @@ -1,33 +1,33 @@ -/obj/effect/decal/cleanable/blood/xeno - name = "xeno blood" - desc = "It's green and acidic. It looks like... blood?" - icon = 'icons/effects/blood.dmi' - basecolor = "#05EE05" - -/obj/effect/decal/cleanable/blood/gibs/xeno - name = "xeno gibs" - desc = "Gnarly..." - icon_state = "xgib1" - random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6") - basecolor = "#05EE05" - -/obj/effect/decal/cleanable/blood/gibs/xeno/update_icon() - color = "#FFFFFF" - -/obj/effect/decal/cleanable/blood/gibs/xeno/up - random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6","xgibup1","xgibup1","xgibup1") - -/obj/effect/decal/cleanable/blood/gibs/xeno/down - random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6","xgibdown1","xgibdown1","xgibdown1") - -/obj/effect/decal/cleanable/blood/gibs/xeno/body - random_icon_states = list("xgibhead", "xgibtorso") - -/obj/effect/decal/cleanable/blood/gibs/xeno/limb - random_icon_states = list("xgibleg", "xgibarm") - -/obj/effect/decal/cleanable/blood/gibs/xeno/core - random_icon_states = list("xgibmid1", "xgibmid2", "xgibmid3") - -/obj/effect/decal/cleanable/blood/xtracks +/obj/effect/decal/cleanable/blood/xeno + name = "xeno blood" + desc = "It's green and acidic. It looks like... blood?" + icon = 'icons/effects/blood.dmi' + basecolor = "#05EE05" + +/obj/effect/decal/cleanable/blood/gibs/xeno + name = "xeno gibs" + desc = "Gnarly..." + icon_state = "xgib1" + random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6") + basecolor = "#05EE05" + +/obj/effect/decal/cleanable/blood/gibs/xeno/update_icon() + color = "#FFFFFF" + +/obj/effect/decal/cleanable/blood/gibs/xeno/up + random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6","xgibup1","xgibup1","xgibup1") + +/obj/effect/decal/cleanable/blood/gibs/xeno/down + random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6","xgibdown1","xgibdown1","xgibdown1") + +/obj/effect/decal/cleanable/blood/gibs/xeno/body + random_icon_states = list("xgibhead", "xgibtorso") + +/obj/effect/decal/cleanable/blood/gibs/xeno/limb + random_icon_states = list("xgibleg", "xgibarm") + +/obj/effect/decal/cleanable/blood/gibs/xeno/core + random_icon_states = list("xgibmid1", "xgibmid2", "xgibmid3") + +/obj/effect/decal/cleanable/blood/xtracks basecolor = "#05EE05" \ No newline at end of file diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index 73669120381..1e3393c8577 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -1,253 +1,253 @@ -#define DRYING_TIME 5 * 60*10 //for 1 unit of depth in puddle (amount var) - -var/global/list/image/splatter_cache=list() - -/obj/effect/decal/cleanable/blood - name = "blood" - var/dryname = "dried blood" - desc = "It's thick and gooey. Perhaps it's the chef's cooking?" - var/drydesc = "It's dry and crusty. Someone is not doing their job." - gender = PLURAL - density = FALSE - anchored = TRUE - plane = BLOOD_PLANE - layer = BLOOD_DECAL_LAYER - icon = 'icons/effects/blood.dmi' - icon_state = "mfloor1" - random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7") - var/base_icon = 'icons/effects/blood.dmi' - blood_DNA = list() - var/basecolor="#A10808" // Color when wet. - var/synthblood = 0 - var/list/datum/disease2/disease/virus2 = list() - var/amount = 5 - generic_filth = TRUE - persistent = FALSE - -/obj/effect/decal/cleanable/blood/reveal_blood() - if(!fluorescent) - fluorescent = 1 - basecolor = COLOR_LUMINOL - update_icon() - -/obj/effect/decal/cleanable/blood/clean_blood() - fluorescent = 0 - if(invisibility != 100) - invisibility = 100 - amount = 0 - ..(ignore=1) - -/obj/effect/decal/cleanable/blood/New() - ..() - update_icon() - if(istype(src, /obj/effect/decal/cleanable/blood/gibs)) - return - if(src.type == /obj/effect/decal/cleanable/blood) - if(src.loc && isturf(src.loc)) - for(var/obj/effect/decal/cleanable/blood/B in src.loc) - if(B != src) - if (B.blood_DNA) - blood_DNA |= B.blood_DNA.Copy() - qdel(B) - addtimer(CALLBACK(src, PROC_REF(dry)), DRYING_TIME * (amount+1)) - -/obj/effect/decal/cleanable/blood/update_icon() - if(basecolor == "rainbow") basecolor = get_random_colour(1) - color = basecolor - - if(basecolor == SYNTH_BLOOD_COLOUR) - name = "oil" - desc = "It's quite oily." - else if(synthblood) - name = "synthetic blood" - desc = "It's quite greasy." - else - name = initial(name) - desc = initial(desc) - -/obj/effect/decal/cleanable/blood/Crossed(mob/living/carbon/human/perp) - if(perp.is_incorporeal()) - return - if (!istype(perp)) - return - if(amount < 1) - return - - var/obj/item/organ/external/l_foot = perp.get_organ("l_foot") - var/obj/item/organ/external/r_foot = perp.get_organ("r_foot") - var/hasfeet = 1 - if((!l_foot || l_foot.is_stump()) && (!r_foot || r_foot.is_stump())) - hasfeet = 0 - if(perp.shoes && !perp.buckled)//Adding blood to shoes - var/obj/item/clothing/shoes/S = perp.shoes - if(istype(S)) - S.blood_color = basecolor - S.track_blood = max(amount,S.track_blood) - if(!S.blood_overlay) - S.generate_blood_overlay() - if(!S.blood_DNA) - S.blood_DNA = list() - S.blood_overlay.color = basecolor - S.add_overlay(S.blood_overlay) - if(S.blood_overlay && S.blood_overlay.color != basecolor) - S.blood_overlay.color = basecolor - S.add_overlay(S.blood_overlay) - S.blood_DNA |= blood_DNA.Copy() - perp.update_inv_shoes() - - else if (hasfeet)//Or feet - perp.feet_blood_color = basecolor - perp.track_blood = max(amount,perp.track_blood) - LAZYINITLIST(perp.feet_blood_DNA) - perp.feet_blood_DNA |= blood_DNA.Copy() - perp.update_bloodied() - else if (perp.buckled && istype(perp.buckled, /obj/structure/bed/chair/wheelchair)) - var/obj/structure/bed/chair/wheelchair/W = perp.buckled - W.bloodiness = 4 - - amount-- - -/obj/effect/decal/cleanable/blood/proc/dry() - name = dryname - desc = drydesc - color = adjust_brightness(color, -50) - amount = 0 - -/obj/effect/decal/cleanable/blood/attack_hand(mob/living/carbon/human/user) - ..() - if (amount && istype(user)) - add_fingerprint(user) - if (user.gloves) - return - var/taken = rand(1,amount) - amount -= taken - to_chat(user, "You get some of \the [src] on your hands.") - if (!user.blood_DNA) - user.blood_DNA = list() - user.blood_DNA |= blood_DNA.Copy() - user.bloody_hands += taken - user.hand_blood_color = basecolor - user.update_inv_gloves(1) - user.verbs += /mob/living/carbon/human/proc/bloody_doodle - -/obj/effect/decal/cleanable/blood/splatter - random_icon_states = list("mgibbl1", "mgibbl2", "mgibbl3", "mgibbl4", "mgibbl5") - amount = 2 - -/obj/effect/decal/cleanable/blood/drip - name = "drips of blood" - desc = "It's red." - gender = PLURAL - icon = 'icons/effects/drip.dmi' - icon_state = "1" - random_icon_states = list("1","2","3","4","5") - amount = 0 - var/list/drips = list() - -/obj/effect/decal/cleanable/blood/drip/New() - ..() - drips |= icon_state - -/obj/effect/decal/cleanable/blood/writing - icon_state = "tracks" - desc = "It looks like a writing in blood." - gender = NEUTER - random_icon_states = list("writing1","writing2","writing3","writing4","writing5") - amount = 0 - var/message - -/obj/effect/decal/cleanable/blood/writing/New() - ..() - if(random_icon_states.len) - for(var/obj/effect/decal/cleanable/blood/writing/W in loc) - random_icon_states.Remove(W.icon_state) - icon_state = pick(random_icon_states) - else - icon_state = "writing1" - -/obj/effect/decal/cleanable/blood/writing/examine(mob/user) - . = ..() - . += "It reads: \"[message]\"" - -/obj/effect/decal/cleanable/blood/gibs - name = "gibs" - desc = "They look bloody and gruesome." - gender = PLURAL - density = FALSE - anchored = TRUE - icon = 'icons/effects/blood.dmi' - icon_state = "gibbl5" - random_icon_states = list("gib1", "gib2", "gib3", "gib5", "gib6") - var/fleshcolor = "#FFFFFF" - -/obj/effect/decal/cleanable/blood/gibs/update_icon() - - var/image/giblets = new(base_icon, "[icon_state]_flesh", dir) - if(!fleshcolor || fleshcolor == "rainbow") - fleshcolor = get_random_colour(1) - giblets.color = fleshcolor - - var/icon/blood = new(base_icon,"[icon_state]",dir) - if(basecolor == "rainbow") basecolor = get_random_colour(1) - blood.Blend(basecolor,ICON_MULTIPLY) - - icon = blood - cut_overlays() - add_overlay(giblets) - -/obj/effect/decal/cleanable/blood/gibs/up - random_icon_states = list("gib1", "gib2", "gib3", "gib5", "gib6","gibup1","gibup1","gibup1") - -/obj/effect/decal/cleanable/blood/gibs/down - random_icon_states = list("gib1", "gib2", "gib3", "gib5", "gib6","gibdown1","gibdown1","gibdown1") - -/obj/effect/decal/cleanable/blood/gibs/body - random_icon_states = list("gibhead", "gibtorso") - -/obj/effect/decal/cleanable/blood/gibs/limb - random_icon_states = list("gibleg", "gibarm") - -/obj/effect/decal/cleanable/blood/gibs/core - random_icon_states = list("gibmid1", "gibmid2", "gibmid3") - - -/obj/effect/decal/cleanable/blood/gibs/proc/streak(var/list/directions) - spawn (0) - var/direction = pick(directions) - for (var/i = 0, i < pick(1, 200; 2, 150; 3, 50; 4), i++) - sleep(3) - if (i > 0) - var/obj/effect/decal/cleanable/blood/b = new /obj/effect/decal/cleanable/blood/splatter(src.loc) - b.basecolor = src.basecolor - b.update_icon() - - if (step_to(src, get_step(src, direction), 0)) - break - - -/obj/effect/decal/cleanable/mucus - name = "mucus" - desc = "Disgusting mucus." - gender = PLURAL - density = FALSE - anchored = TRUE - icon = 'icons/effects/blood.dmi' - icon_state = "mucus" - random_icon_states = list("mucus") - - var/list/datum/disease2/disease/virus2 = list() - var/dry = 0 // Keeps the lag down - -/obj/effect/decal/cleanable/mucus/Initialize() - . = ..() - VARSET_IN(src, dry, TRUE, DRYING_TIME * 2) - -//This version should be used for admin spawns and pre-mapped virus vectors (e.g. in PoIs), this version does not dry -/obj/effect/decal/cleanable/mucus/mapped/Initialize() - . = ..() - virus2 |= new /datum/disease2/disease - virus2[1].makerandom() - -/obj/effect/decal/cleanable/mucus/mapped/Destroy() - virus2.Cut() - return ..() +#define DRYING_TIME 5 * 60*10 //for 1 unit of depth in puddle (amount var) + +var/global/list/image/splatter_cache=list() + +/obj/effect/decal/cleanable/blood + name = "blood" + var/dryname = "dried blood" + desc = "It's thick and gooey. Perhaps it's the chef's cooking?" + var/drydesc = "It's dry and crusty. Someone is not doing their job." + gender = PLURAL + density = FALSE + anchored = TRUE + plane = BLOOD_PLANE + layer = BLOOD_DECAL_LAYER + icon = 'icons/effects/blood.dmi' + icon_state = "mfloor1" + random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7") + var/base_icon = 'icons/effects/blood.dmi' + blood_DNA = list() + var/basecolor="#A10808" // Color when wet. + var/synthblood = 0 + var/list/datum/disease2/disease/virus2 = list() + var/amount = 5 + generic_filth = TRUE + persistent = FALSE + +/obj/effect/decal/cleanable/blood/reveal_blood() + if(!fluorescent) + fluorescent = 1 + basecolor = COLOR_LUMINOL + update_icon() + +/obj/effect/decal/cleanable/blood/clean_blood() + fluorescent = 0 + if(invisibility != 100) + invisibility = 100 + amount = 0 + ..(ignore=1) + +/obj/effect/decal/cleanable/blood/New() + ..() + update_icon() + if(istype(src, /obj/effect/decal/cleanable/blood/gibs)) + return + if(src.type == /obj/effect/decal/cleanable/blood) + if(src.loc && isturf(src.loc)) + for(var/obj/effect/decal/cleanable/blood/B in src.loc) + if(B != src) + if (B.blood_DNA) + blood_DNA |= B.blood_DNA.Copy() + qdel(B) + addtimer(CALLBACK(src, PROC_REF(dry)), DRYING_TIME * (amount+1)) + +/obj/effect/decal/cleanable/blood/update_icon() + if(basecolor == "rainbow") basecolor = get_random_colour(1) + color = basecolor + + if(basecolor == SYNTH_BLOOD_COLOUR) + name = "oil" + desc = "It's quite oily." + else if(synthblood) + name = "synthetic blood" + desc = "It's quite greasy." + else + name = initial(name) + desc = initial(desc) + +/obj/effect/decal/cleanable/blood/Crossed(mob/living/carbon/human/perp) + if(perp.is_incorporeal()) + return + if (!istype(perp)) + return + if(amount < 1) + return + + var/obj/item/organ/external/l_foot = perp.get_organ("l_foot") + var/obj/item/organ/external/r_foot = perp.get_organ("r_foot") + var/hasfeet = 1 + if((!l_foot || l_foot.is_stump()) && (!r_foot || r_foot.is_stump())) + hasfeet = 0 + if(perp.shoes && !perp.buckled)//Adding blood to shoes + var/obj/item/clothing/shoes/S = perp.shoes + if(istype(S)) + S.blood_color = basecolor + S.track_blood = max(amount,S.track_blood) + if(!S.blood_overlay) + S.generate_blood_overlay() + if(!S.blood_DNA) + S.blood_DNA = list() + S.blood_overlay.color = basecolor + S.add_overlay(S.blood_overlay) + if(S.blood_overlay && S.blood_overlay.color != basecolor) + S.blood_overlay.color = basecolor + S.add_overlay(S.blood_overlay) + S.blood_DNA |= blood_DNA.Copy() + perp.update_inv_shoes() + + else if (hasfeet)//Or feet + perp.feet_blood_color = basecolor + perp.track_blood = max(amount,perp.track_blood) + LAZYINITLIST(perp.feet_blood_DNA) + perp.feet_blood_DNA |= blood_DNA.Copy() + perp.update_bloodied() + else if (perp.buckled && istype(perp.buckled, /obj/structure/bed/chair/wheelchair)) + var/obj/structure/bed/chair/wheelchair/W = perp.buckled + W.bloodiness = 4 + + amount-- + +/obj/effect/decal/cleanable/blood/proc/dry() + name = dryname + desc = drydesc + color = adjust_brightness(color, -50) + amount = 0 + +/obj/effect/decal/cleanable/blood/attack_hand(mob/living/carbon/human/user) + ..() + if (amount && istype(user)) + add_fingerprint(user) + if (user.gloves) + return + var/taken = rand(1,amount) + amount -= taken + to_chat(user, "You get some of \the [src] on your hands.") + if (!user.blood_DNA) + user.blood_DNA = list() + user.blood_DNA |= blood_DNA.Copy() + user.bloody_hands += taken + user.hand_blood_color = basecolor + user.update_inv_gloves(1) + user.verbs += /mob/living/carbon/human/proc/bloody_doodle + +/obj/effect/decal/cleanable/blood/splatter + random_icon_states = list("mgibbl1", "mgibbl2", "mgibbl3", "mgibbl4", "mgibbl5") + amount = 2 + +/obj/effect/decal/cleanable/blood/drip + name = "drips of blood" + desc = "It's red." + gender = PLURAL + icon = 'icons/effects/drip.dmi' + icon_state = "1" + random_icon_states = list("1","2","3","4","5") + amount = 0 + var/list/drips = list() + +/obj/effect/decal/cleanable/blood/drip/New() + ..() + drips |= icon_state + +/obj/effect/decal/cleanable/blood/writing + icon_state = "tracks" + desc = "It looks like a writing in blood." + gender = NEUTER + random_icon_states = list("writing1","writing2","writing3","writing4","writing5") + amount = 0 + var/message + +/obj/effect/decal/cleanable/blood/writing/New() + ..() + if(random_icon_states.len) + for(var/obj/effect/decal/cleanable/blood/writing/W in loc) + random_icon_states.Remove(W.icon_state) + icon_state = pick(random_icon_states) + else + icon_state = "writing1" + +/obj/effect/decal/cleanable/blood/writing/examine(mob/user) + . = ..() + . += "It reads: \"[message]\"" + +/obj/effect/decal/cleanable/blood/gibs + name = "gibs" + desc = "They look bloody and gruesome." + gender = PLURAL + density = FALSE + anchored = TRUE + icon = 'icons/effects/blood.dmi' + icon_state = "gibbl5" + random_icon_states = list("gib1", "gib2", "gib3", "gib5", "gib6") + var/fleshcolor = "#FFFFFF" + +/obj/effect/decal/cleanable/blood/gibs/update_icon() + + var/image/giblets = new(base_icon, "[icon_state]_flesh", dir) + if(!fleshcolor || fleshcolor == "rainbow") + fleshcolor = get_random_colour(1) + giblets.color = fleshcolor + + var/icon/blood = new(base_icon,"[icon_state]",dir) + if(basecolor == "rainbow") basecolor = get_random_colour(1) + blood.Blend(basecolor,ICON_MULTIPLY) + + icon = blood + cut_overlays() + add_overlay(giblets) + +/obj/effect/decal/cleanable/blood/gibs/up + random_icon_states = list("gib1", "gib2", "gib3", "gib5", "gib6","gibup1","gibup1","gibup1") + +/obj/effect/decal/cleanable/blood/gibs/down + random_icon_states = list("gib1", "gib2", "gib3", "gib5", "gib6","gibdown1","gibdown1","gibdown1") + +/obj/effect/decal/cleanable/blood/gibs/body + random_icon_states = list("gibhead", "gibtorso") + +/obj/effect/decal/cleanable/blood/gibs/limb + random_icon_states = list("gibleg", "gibarm") + +/obj/effect/decal/cleanable/blood/gibs/core + random_icon_states = list("gibmid1", "gibmid2", "gibmid3") + + +/obj/effect/decal/cleanable/blood/gibs/proc/streak(var/list/directions) + spawn (0) + var/direction = pick(directions) + for (var/i = 0, i < pick(1, 200; 2, 150; 3, 50; 4), i++) + sleep(3) + if (i > 0) + var/obj/effect/decal/cleanable/blood/b = new /obj/effect/decal/cleanable/blood/splatter(src.loc) + b.basecolor = src.basecolor + b.update_icon() + + if (step_to(src, get_step(src, direction), 0)) + break + + +/obj/effect/decal/cleanable/mucus + name = "mucus" + desc = "Disgusting mucus." + gender = PLURAL + density = FALSE + anchored = TRUE + icon = 'icons/effects/blood.dmi' + icon_state = "mucus" + random_icon_states = list("mucus") + + var/list/datum/disease2/disease/virus2 = list() + var/dry = 0 // Keeps the lag down + +/obj/effect/decal/cleanable/mucus/Initialize() + . = ..() + VARSET_IN(src, dry, TRUE, DRYING_TIME * 2) + +//This version should be used for admin spawns and pre-mapped virus vectors (e.g. in PoIs), this version does not dry +/obj/effect/decal/cleanable/mucus/mapped/Initialize() + . = ..() + virus2 |= new /datum/disease2/disease + virus2[1].makerandom() + +/obj/effect/decal/cleanable/mucus/mapped/Destroy() + virus2.Cut() + return ..() diff --git a/code/game/objects/effects/decals/Cleanable/misc.dm b/code/game/objects/effects/decals/Cleanable/misc.dm index 55cdcfa4ff6..ad2a3fef803 100644 --- a/code/game/objects/effects/decals/Cleanable/misc.dm +++ b/code/game/objects/effects/decals/Cleanable/misc.dm @@ -1,163 +1,163 @@ -/obj/effect/decal/cleanable/generic - name = "clutter" - desc = "Someone should clean that up." - gender = PLURAL - density = FALSE - anchored = TRUE - icon = 'icons/obj/objects.dmi' - icon_state = "shards" - -/obj/effect/decal/cleanable/ash - name = "ashes" - desc = "Ashes to ashes, dust to dust, and into space." - gender = PLURAL - icon = 'icons/obj/objects.dmi' - icon_state = "ash" - anchored = TRUE - -/obj/effect/decal/cleanable/ash/attack_hand(mob/user as mob) - to_chat(user, "[src] sifts through your fingers.") - var/turf/simulated/floor/F = get_turf(src) - if (istype(F)) - F.dirt += 4 - qdel(src) - -/obj/effect/decal/cleanable/greenglow - -/obj/effect/decal/cleanable/greenglow/New() - ..() - QDEL_IN(src, 2 MINUTES) - -/obj/effect/decal/cleanable/dirt - name = "dirt" - desc = "Someone should clean that up." - gender = PLURAL - density = FALSE - anchored = TRUE - icon = 'icons/effects/effects.dmi' - icon_state = "dirt" - mouse_opacity = 0 - -/obj/effect/decal/cleanable/dirt/Initialize(var/mapload, var/_age, var/dirt) - .=..() - var/turf/simulated/our_turf = src.loc - if(our_turf && istype(our_turf) && our_turf.can_dirty) - our_turf.dirt = clamp(max(age ? (dirt ? dirt : 101) : our_turf.dirt, our_turf.dirt), 0, 101) - var/calcalpha = our_turf.dirt > 50 ? min((our_turf.dirt - 50) * 5, 255) : 0 - var/alreadyfound = FALSE - for (var/obj/effect/decal/cleanable/dirt/alreadythere in our_turf) //in case of multiple - if (alreadythere == src) - continue - else if (alreadyfound) - qdel(alreadythere) - continue - alreadyfound = TRUE - alreadythere.alpha = calcalpha //don't need to constantly recalc for all of them in it because it'll just max if a non-persistent dirt overlay gets added, and then the new dirt overlay will be deleted - if (alreadyfound) - return INITIALIZE_HINT_QDEL - alpha = calcalpha - -/obj/effect/decal/cleanable/flour - name = "flour" - desc = "It's still good. Four second rule!" - gender = PLURAL - density = FALSE - anchored = TRUE - icon = 'icons/effects/effects.dmi' - icon_state = "flour" - -/obj/effect/decal/cleanable/greenglow - name = "glowing goo" - desc = "Jeez. I hope that's not for lunch." - gender = PLURAL - density = FALSE - anchored = TRUE - light_range = 1 - icon = 'icons/effects/effects.dmi' - icon_state = "greenglow" - -/obj/effect/decal/cleanable/cobweb - name = "cobweb" - desc = "Somebody should remove that." - density = FALSE - anchored = TRUE - plane = OBJ_PLANE - icon = 'icons/effects/effects.dmi' - icon_state = "cobweb1" - -/obj/effect/decal/cleanable/molten_item - name = "gooey grey mass" - desc = "It looks like a melted... something." - density = FALSE - anchored = TRUE - plane = OBJ_PLANE - icon = 'icons/obj/chemical.dmi' - icon_state = "molten" - -/obj/effect/decal/cleanable/cobweb2 - name = "cobweb" - desc = "Somebody should remove that." - density = FALSE - anchored = TRUE - plane = OBJ_PLANE - icon = 'icons/effects/effects.dmi' - icon_state = "cobweb2" - -//Vomit (sorry) -/obj/effect/decal/cleanable/vomit - name = "vomit" - desc = "Gosh, how unpleasant." - gender = PLURAL - density = FALSE - anchored = TRUE - icon = 'icons/effects/blood.dmi' - icon_state = "vomit_1" - random_icon_states = list("vomit_1", "vomit_2", "vomit_3", "vomit_4") - var/list/datum/disease2/disease/virus2 = list() - -/obj/effect/decal/cleanable/tomato_smudge - name = "tomato smudge" - desc = "It's red." - density = FALSE - anchored = TRUE - icon = 'icons/effects/tomatodecal.dmi' - random_icon_states = list("tomato_floor1", "tomato_floor2", "tomato_floor3") - -/obj/effect/decal/cleanable/egg_smudge - name = "smashed egg" - desc = "Seems like this one won't hatch." - density = FALSE - anchored = TRUE - icon = 'icons/effects/tomatodecal.dmi' - random_icon_states = list("smashed_egg1", "smashed_egg2", "smashed_egg3") - -/obj/effect/decal/cleanable/pie_smudge //honk - name = "smashed pie" - desc = "It's pie cream from a cream pie." - density = FALSE - anchored = TRUE - icon = 'icons/effects/tomatodecal.dmi' - random_icon_states = list("smashed_pie") - -/obj/effect/decal/cleanable/fruit_smudge - name = "smudge" - desc = "Some kind of fruit smear." - density = FALSE - anchored = TRUE - icon = 'icons/effects/blood.dmi' - icon_state = "mfloor1" - random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7") - -/obj/effect/decal/cleanable/confetti - name = "confetti" - desc = "Tiny bits of colored paper thrown about for the janitor to enjoy!" - gender = PLURAL - density = FALSE - anchored = TRUE - icon = 'icons/effects/effects.dmi' - icon_state = "confetti" - -/obj/effect/decal/cleanable/confetti/attack_hand(mob/user) - to_chat(user, "You start to meticulously pick up the confetti.") - if(do_after(user, 60)) - qdel(src) +/obj/effect/decal/cleanable/generic + name = "clutter" + desc = "Someone should clean that up." + gender = PLURAL + density = FALSE + anchored = TRUE + icon = 'icons/obj/objects.dmi' + icon_state = "shards" + +/obj/effect/decal/cleanable/ash + name = "ashes" + desc = "Ashes to ashes, dust to dust, and into space." + gender = PLURAL + icon = 'icons/obj/objects.dmi' + icon_state = "ash" + anchored = TRUE + +/obj/effect/decal/cleanable/ash/attack_hand(mob/user as mob) + to_chat(user, "[src] sifts through your fingers.") + var/turf/simulated/floor/F = get_turf(src) + if (istype(F)) + F.dirt += 4 + qdel(src) + +/obj/effect/decal/cleanable/greenglow + +/obj/effect/decal/cleanable/greenglow/New() + ..() + QDEL_IN(src, 2 MINUTES) + +/obj/effect/decal/cleanable/dirt + name = "dirt" + desc = "Someone should clean that up." + gender = PLURAL + density = FALSE + anchored = TRUE + icon = 'icons/effects/effects.dmi' + icon_state = "dirt" + mouse_opacity = 0 + +/obj/effect/decal/cleanable/dirt/Initialize(var/mapload, var/_age, var/dirt) + .=..() + var/turf/simulated/our_turf = src.loc + if(our_turf && istype(our_turf) && our_turf.can_dirty) + our_turf.dirt = clamp(max(age ? (dirt ? dirt : 101) : our_turf.dirt, our_turf.dirt), 0, 101) + var/calcalpha = our_turf.dirt > 50 ? min((our_turf.dirt - 50) * 5, 255) : 0 + var/alreadyfound = FALSE + for (var/obj/effect/decal/cleanable/dirt/alreadythere in our_turf) //in case of multiple + if (alreadythere == src) + continue + else if (alreadyfound) + qdel(alreadythere) + continue + alreadyfound = TRUE + alreadythere.alpha = calcalpha //don't need to constantly recalc for all of them in it because it'll just max if a non-persistent dirt overlay gets added, and then the new dirt overlay will be deleted + if (alreadyfound) + return INITIALIZE_HINT_QDEL + alpha = calcalpha + +/obj/effect/decal/cleanable/flour + name = "flour" + desc = "It's still good. Four second rule!" + gender = PLURAL + density = FALSE + anchored = TRUE + icon = 'icons/effects/effects.dmi' + icon_state = "flour" + +/obj/effect/decal/cleanable/greenglow + name = "glowing goo" + desc = "Jeez. I hope that's not for lunch." + gender = PLURAL + density = FALSE + anchored = TRUE + light_range = 1 + icon = 'icons/effects/effects.dmi' + icon_state = "greenglow" + +/obj/effect/decal/cleanable/cobweb + name = "cobweb" + desc = "Somebody should remove that." + density = FALSE + anchored = TRUE + plane = OBJ_PLANE + icon = 'icons/effects/effects.dmi' + icon_state = "cobweb1" + +/obj/effect/decal/cleanable/molten_item + name = "gooey grey mass" + desc = "It looks like a melted... something." + density = FALSE + anchored = TRUE + plane = OBJ_PLANE + icon = 'icons/obj/chemical.dmi' + icon_state = "molten" + +/obj/effect/decal/cleanable/cobweb2 + name = "cobweb" + desc = "Somebody should remove that." + density = FALSE + anchored = TRUE + plane = OBJ_PLANE + icon = 'icons/effects/effects.dmi' + icon_state = "cobweb2" + +//Vomit (sorry) +/obj/effect/decal/cleanable/vomit + name = "vomit" + desc = "Gosh, how unpleasant." + gender = PLURAL + density = FALSE + anchored = TRUE + icon = 'icons/effects/blood.dmi' + icon_state = "vomit_1" + random_icon_states = list("vomit_1", "vomit_2", "vomit_3", "vomit_4") + var/list/datum/disease2/disease/virus2 = list() + +/obj/effect/decal/cleanable/tomato_smudge + name = "tomato smudge" + desc = "It's red." + density = FALSE + anchored = TRUE + icon = 'icons/effects/tomatodecal.dmi' + random_icon_states = list("tomato_floor1", "tomato_floor2", "tomato_floor3") + +/obj/effect/decal/cleanable/egg_smudge + name = "smashed egg" + desc = "Seems like this one won't hatch." + density = FALSE + anchored = TRUE + icon = 'icons/effects/tomatodecal.dmi' + random_icon_states = list("smashed_egg1", "smashed_egg2", "smashed_egg3") + +/obj/effect/decal/cleanable/pie_smudge //honk + name = "smashed pie" + desc = "It's pie cream from a cream pie." + density = FALSE + anchored = TRUE + icon = 'icons/effects/tomatodecal.dmi' + random_icon_states = list("smashed_pie") + +/obj/effect/decal/cleanable/fruit_smudge + name = "smudge" + desc = "Some kind of fruit smear." + density = FALSE + anchored = TRUE + icon = 'icons/effects/blood.dmi' + icon_state = "mfloor1" + random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7") + +/obj/effect/decal/cleanable/confetti + name = "confetti" + desc = "Tiny bits of colored paper thrown about for the janitor to enjoy!" + gender = PLURAL + density = FALSE + anchored = TRUE + icon = 'icons/effects/effects.dmi' + icon_state = "confetti" + +/obj/effect/decal/cleanable/confetti/attack_hand(mob/user) + to_chat(user, "You start to meticulously pick up the confetti.") + if(do_after(user, 60)) + qdel(src) diff --git a/code/game/objects/effects/decals/Cleanable/robots.dm b/code/game/objects/effects/decals/Cleanable/robots.dm index bdc5c9237a6..b0980966a30 100644 --- a/code/game/objects/effects/decals/Cleanable/robots.dm +++ b/code/game/objects/effects/decals/Cleanable/robots.dm @@ -1,52 +1,52 @@ -/obj/effect/decal/cleanable/blood/gibs/robot - name = "robot debris" - desc = "It's a useless heap of junk... or is it?" - icon = 'icons/mob/robots.dmi' - icon_state = "gib1" - basecolor = SYNTH_BLOOD_COLOUR - random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7") - generic_filth = FALSE - persistent = FALSE - -/obj/effect/decal/cleanable/blood/gibs/robot/update_icon() - color = "#FFFFFF" - -/obj/effect/decal/cleanable/blood/gibs/robot/dry() //pieces of robots do not dry up like - return - -/obj/effect/decal/cleanable/blood/gibs/robot/streak(var/list/directions) - spawn (0) - var/direction = pick(directions) - for (var/i = 0, i < pick(1, 200; 2, 150; 3, 50; 4), i++) - sleep(3) - if (i > 0) - if (prob(40)) - var/obj/effect/decal/cleanable/blood/oil/streak = new(src.loc) - streak.update_icon() - else if (prob(10)) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(3, 1, src) - s.start() - if (step_to(src, get_step(src, direction), 0)) - break - -/obj/effect/decal/cleanable/blood/gibs/robot/limb - random_icon_states = list("gibarm", "gibleg") - -/obj/effect/decal/cleanable/blood/gibs/robot/up - random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7","gibup1","gibup1") //2:7 is close enough to 1:4 - -/obj/effect/decal/cleanable/blood/gibs/robot/down - random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7","gibdown1","gibdown1") //2:7 is close enough to 1:4 - -/obj/effect/decal/cleanable/blood/oil - basecolor = SYNTH_BLOOD_COLOUR - generic_filth = FALSE - persistent = FALSE - -/obj/effect/decal/cleanable/blood/oil/dry() - return - -/obj/effect/decal/cleanable/blood/oil/streak - random_icon_states = list("mgibbl1", "mgibbl2", "mgibbl3", "mgibbl4", "mgibbl5") +/obj/effect/decal/cleanable/blood/gibs/robot + name = "robot debris" + desc = "It's a useless heap of junk... or is it?" + icon = 'icons/mob/robots.dmi' + icon_state = "gib1" + basecolor = SYNTH_BLOOD_COLOUR + random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7") + generic_filth = FALSE + persistent = FALSE + +/obj/effect/decal/cleanable/blood/gibs/robot/update_icon() + color = "#FFFFFF" + +/obj/effect/decal/cleanable/blood/gibs/robot/dry() //pieces of robots do not dry up like + return + +/obj/effect/decal/cleanable/blood/gibs/robot/streak(var/list/directions) + spawn (0) + var/direction = pick(directions) + for (var/i = 0, i < pick(1, 200; 2, 150; 3, 50; 4), i++) + sleep(3) + if (i > 0) + if (prob(40)) + var/obj/effect/decal/cleanable/blood/oil/streak = new(src.loc) + streak.update_icon() + else if (prob(10)) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + if (step_to(src, get_step(src, direction), 0)) + break + +/obj/effect/decal/cleanable/blood/gibs/robot/limb + random_icon_states = list("gibarm", "gibleg") + +/obj/effect/decal/cleanable/blood/gibs/robot/up + random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7","gibup1","gibup1") //2:7 is close enough to 1:4 + +/obj/effect/decal/cleanable/blood/gibs/robot/down + random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7","gibdown1","gibdown1") //2:7 is close enough to 1:4 + +/obj/effect/decal/cleanable/blood/oil + basecolor = SYNTH_BLOOD_COLOUR + generic_filth = FALSE + persistent = FALSE + +/obj/effect/decal/cleanable/blood/oil/dry() + return + +/obj/effect/decal/cleanable/blood/oil/streak + random_icon_states = list("mgibbl1", "mgibbl2", "mgibbl3", "mgibbl4", "mgibbl5") amount = 2 \ No newline at end of file diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm index ed411a67ab5..d30a548ab2d 100644 --- a/code/game/objects/effects/decals/cleanable.dm +++ b/code/game/objects/effects/decals/cleanable.dm @@ -1,38 +1,38 @@ -/* -USAGE NOTE -For decals, the var Persistent = 'has already been saved', and is primarily used to prevent duplicate savings of generic filth (filth.dm). -This also means 'TRUE' can be used to define a decal as "Do not save at all, even as a generic replacement." if a dirt decal is considered 'too common' to save. -generic_filth = TRUE means when the decal is saved, it will be switched out for a generic green 'filth' decal. -*/ - -/obj/effect/decal/cleanable - plane = DIRTY_PLANE - layer = DIRTY_LAYER - var/persistent = FALSE - var/generic_filth = FALSE - var/age = 0 - var/list/random_icon_states = list() - -/obj/effect/decal/cleanable/Initialize(var/mapload, var/_age) - if(!isnull(_age)) - age = _age - if(random_icon_states && length(src.random_icon_states) > 0) - src.icon_state = pick(src.random_icon_states) - if(!mapload || !config.persistence_ignore_mapload) - SSpersistence.track_value(src, /datum/persistent/filth) - . = ..() - -/obj/effect/decal/cleanable/Destroy() - SSpersistence.forget_value(src, /datum/persistent/filth) - . = ..() - -/obj/effect/decal/cleanable/clean_blood(var/ignore = 0) - if(!ignore) - qdel(src) - return - ..() - -/obj/effect/decal/cleanable/New() - if (random_icon_states && length(src.random_icon_states) > 0) - src.icon_state = pick(src.random_icon_states) - ..() +/* +USAGE NOTE +For decals, the var Persistent = 'has already been saved', and is primarily used to prevent duplicate savings of generic filth (filth.dm). +This also means 'TRUE' can be used to define a decal as "Do not save at all, even as a generic replacement." if a dirt decal is considered 'too common' to save. +generic_filth = TRUE means when the decal is saved, it will be switched out for a generic green 'filth' decal. +*/ + +/obj/effect/decal/cleanable + plane = DIRTY_PLANE + layer = DIRTY_LAYER + var/persistent = FALSE + var/generic_filth = FALSE + var/age = 0 + var/list/random_icon_states = list() + +/obj/effect/decal/cleanable/Initialize(var/mapload, var/_age) + if(!isnull(_age)) + age = _age + if(random_icon_states && length(src.random_icon_states) > 0) + src.icon_state = pick(src.random_icon_states) + if(!mapload || !config.persistence_ignore_mapload) + SSpersistence.track_value(src, /datum/persistent/filth) + . = ..() + +/obj/effect/decal/cleanable/Destroy() + SSpersistence.forget_value(src, /datum/persistent/filth) + . = ..() + +/obj/effect/decal/cleanable/clean_blood(var/ignore = 0) + if(!ignore) + qdel(src) + return + ..() + +/obj/effect/decal/cleanable/New() + if (random_icon_states && length(src.random_icon_states) > 0) + src.icon_state = pick(src.random_icon_states) + ..() diff --git a/code/game/objects/effects/decals/crayon.dm b/code/game/objects/effects/decals/crayon.dm index 248b9cfbb46..493529cedeb 100644 --- a/code/game/objects/effects/decals/crayon.dm +++ b/code/game/objects/effects/decals/crayon.dm @@ -1,31 +1,31 @@ -/obj/effect/decal/cleanable/crayon - name = "rune" - desc = "A rune drawn in crayon." - icon = 'icons/obj/rune.dmi' - plane = DIRTY_PLANE - layer = DIRTY_LAYER - anchored = TRUE - -/obj/effect/decal/cleanable/crayon/New(location,main = "#FFFFFF",shade = "#000000",var/type = "rune") - ..() - loc = location - - name = type - desc = "A [type] drawn in crayon." - - switch(type) - if("rune") - type = "rune[rand(1,6)]" - if("graffiti") - type = pick("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa") - - var/icon/mainOverlay = new/icon('icons/effects/crayondecal.dmi',"[type]",2.1) - var/icon/shadeOverlay = new/icon('icons/effects/crayondecal.dmi',"[type]s",2.1) - - mainOverlay.Blend(main,ICON_ADD) - shadeOverlay.Blend(shade,ICON_ADD) - - add_overlay(mainOverlay) - add_overlay(shadeOverlay) - - add_hiddenprint(usr) +/obj/effect/decal/cleanable/crayon + name = "rune" + desc = "A rune drawn in crayon." + icon = 'icons/obj/rune.dmi' + plane = DIRTY_PLANE + layer = DIRTY_LAYER + anchored = TRUE + +/obj/effect/decal/cleanable/crayon/New(location,main = "#FFFFFF",shade = "#000000",var/type = "rune") + ..() + loc = location + + name = type + desc = "A [type] drawn in crayon." + + switch(type) + if("rune") + type = "rune[rand(1,6)]" + if("graffiti") + type = pick("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa") + + var/icon/mainOverlay = new/icon('icons/effects/crayondecal.dmi',"[type]",2.1) + var/icon/shadeOverlay = new/icon('icons/effects/crayondecal.dmi',"[type]s",2.1) + + mainOverlay.Blend(main,ICON_ADD) + shadeOverlay.Blend(shade,ICON_ADD) + + add_overlay(mainOverlay) + add_overlay(shadeOverlay) + + add_hiddenprint(usr) diff --git a/code/game/objects/effects/decals/misc.dm b/code/game/objects/effects/decals/misc.dm index 3e5c46e1347..df2d3ee32bb 100644 --- a/code/game/objects/effects/decals/misc.dm +++ b/code/game/objects/effects/decals/misc.dm @@ -1,14 +1,14 @@ -/obj/effect/decal/point - name = "arrow" - desc = "It's an arrow hanging in mid-air. There may be a wizard about." - icon = 'icons/mob/screen1.dmi' - icon_state = "arrow" - plane = ABOVE_PLANE - anchored = TRUE - mouse_opacity = 0 - -// Used for spray that you spray at walls, tables, hydrovats etc -/obj/effect/decal/spraystill - density = FALSE - anchored = TRUE +/obj/effect/decal/point + name = "arrow" + desc = "It's an arrow hanging in mid-air. There may be a wizard about." + icon = 'icons/mob/screen1.dmi' + icon_state = "arrow" + plane = ABOVE_PLANE + anchored = TRUE + mouse_opacity = 0 + +// Used for spray that you spray at walls, tables, hydrovats etc +/obj/effect/decal/spraystill + density = FALSE + anchored = TRUE plane = ABOVE_PLANE \ No newline at end of file diff --git a/code/game/objects/effects/decals/remains.dm b/code/game/objects/effects/decals/remains.dm index 6f8af8cdd9e..9d1d0e1309e 100644 --- a/code/game/objects/effects/decals/remains.dm +++ b/code/game/objects/effects/decals/remains.dm @@ -1,70 +1,70 @@ -/obj/effect/decal/remains - name = "remains" - gender = PLURAL - icon = 'icons/effects/blood.dmi' - icon_state = "remains" - anchored = FALSE - -/obj/effect/decal/remains/human - desc = "They look like human remains. They have a strange aura about them." - -/obj/effect/decal/remains/xeno - desc = "They look like the remains of something... alien. They have a strange aura about them." - icon_state = "remainsxeno" - -/obj/effect/decal/remains/robot - desc = "They look like the remains of something mechanical. They have a strange aura about them." - icon = 'icons/mob/robots.dmi' - icon_state = "remainsrobot" - -/obj/effect/decal/remains/mouse - desc = "They look like the remains of a small rodent." - icon_state = "mouse" - -/obj/effect/decal/remains/lizard - desc = "They look like the remains of a small lizard." - icon_state = "lizard" - -/obj/effect/decal/remains/unathi - desc = "They look like Unathi remains. Pointy." - icon_state = "remainsunathi" - -/obj/effect/decal/remains/tajaran - desc = "They look like Tajaran remains. They're surprisingly small." - icon_state = "remainstajaran" - -/obj/effect/decal/remains/ribcage - desc = "They look like animal remains of some sort... You hope." - icon_state = "remainsribcage" - -/obj/effect/decal/remains/deer - desc = "They look like the remains of a large herbivore, picked clean." - icon_state = "remainsdeer" - -/obj/effect/decal/remains/posi - desc = "This looks like part of an old FBP. Hopefully it was empty." - icon_state = "remainsposi" - -/obj/effect/decal/remains/mummy1 - name = "mummified remains" - desc = "They look like human remains. They've been here a long time." - icon_state = "mummified1" - -/obj/effect/decal/remains/mummy2 - name = "mummified remains" - desc = "They look like human remains. They've been here a long time." - icon_state = "mummified2" - -/obj/effect/decal/remains/attack_hand(mob/user as mob) - to_chat(user, "[src] sinks together into a pile of ash.") - var/turf/simulated/floor/F = get_turf(src) - if(istype(F)) - new /obj/effect/decal/cleanable/ash(F) - qdel(src) - -/obj/effect/decal/remains/robot/attack_hand(mob/user as mob) - to_chat(user, "[src] crumbles down into a pile of debris.") - var/turf/simulated/floor/F = get_turf(src) - if(istype(F)) - new /obj/effect/decal/cleanable/blood/gibs/robot(F) - qdel(src) +/obj/effect/decal/remains + name = "remains" + gender = PLURAL + icon = 'icons/effects/blood.dmi' + icon_state = "remains" + anchored = FALSE + +/obj/effect/decal/remains/human + desc = "They look like human remains. They have a strange aura about them." + +/obj/effect/decal/remains/xeno + desc = "They look like the remains of something... alien. They have a strange aura about them." + icon_state = "remainsxeno" + +/obj/effect/decal/remains/robot + desc = "They look like the remains of something mechanical. They have a strange aura about them." + icon = 'icons/mob/robots.dmi' + icon_state = "remainsrobot" + +/obj/effect/decal/remains/mouse + desc = "They look like the remains of a small rodent." + icon_state = "mouse" + +/obj/effect/decal/remains/lizard + desc = "They look like the remains of a small lizard." + icon_state = "lizard" + +/obj/effect/decal/remains/unathi + desc = "They look like Unathi remains. Pointy." + icon_state = "remainsunathi" + +/obj/effect/decal/remains/tajaran + desc = "They look like Tajaran remains. They're surprisingly small." + icon_state = "remainstajaran" + +/obj/effect/decal/remains/ribcage + desc = "They look like animal remains of some sort... You hope." + icon_state = "remainsribcage" + +/obj/effect/decal/remains/deer + desc = "They look like the remains of a large herbivore, picked clean." + icon_state = "remainsdeer" + +/obj/effect/decal/remains/posi + desc = "This looks like part of an old FBP. Hopefully it was empty." + icon_state = "remainsposi" + +/obj/effect/decal/remains/mummy1 + name = "mummified remains" + desc = "They look like human remains. They've been here a long time." + icon_state = "mummified1" + +/obj/effect/decal/remains/mummy2 + name = "mummified remains" + desc = "They look like human remains. They've been here a long time." + icon_state = "mummified2" + +/obj/effect/decal/remains/attack_hand(mob/user as mob) + to_chat(user, "[src] sinks together into a pile of ash.") + var/turf/simulated/floor/F = get_turf(src) + if(istype(F)) + new /obj/effect/decal/cleanable/ash(F) + qdel(src) + +/obj/effect/decal/remains/robot/attack_hand(mob/user as mob) + to_chat(user, "[src] crumbles down into a pile of debris.") + var/turf/simulated/floor/F = get_turf(src) + if(istype(F)) + new /obj/effect/decal/cleanable/blood/gibs/robot(F) + qdel(src) diff --git a/code/game/objects/effects/decals/warning_stripes.dm b/code/game/objects/effects/decals/warning_stripes.dm index e69c90a8d84..c470d186cea 100644 --- a/code/game/objects/effects/decals/warning_stripes.dm +++ b/code/game/objects/effects/decals/warning_stripes.dm @@ -1,10 +1,10 @@ -/obj/effect/decal/warning_stripes - icon = 'icons/effects/warning_stripes.dmi' - -/obj/effect/decal/warning_stripes/Initialize() - . = ..() - var/turf/T=get_turf(src) - var/image/I=image(icon, icon_state = icon_state, dir = dir) - I.color=color - T.add_overlay(I) - return INITIALIZE_HINT_QDEL +/obj/effect/decal/warning_stripes + icon = 'icons/effects/warning_stripes.dmi' + +/obj/effect/decal/warning_stripes/Initialize() + . = ..() + var/turf/T=get_turf(src) + var/image/I=image(icon, icon_state = icon_state, dir = dir) + I.color=color + T.add_overlay(I) + return INITIALIZE_HINT_QDEL diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index 6b0883570d3..fca49884932 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -1,581 +1,581 @@ -/* This is an attempt to make some easily reusable "particle" type effect, to stop the code -constantly having to be rewritten. An item like the jetpack that uses the ion_trail_follow system, just has one -defined, then set up when it is created with New(). Then this same system can just be reused each time -it needs to create more trails.A beaker could have a steam_trail_follow system set up, then the steam -would spawn and follow the beaker, even if it is carried or thrown. -*/ -/obj/effect - light_on = TRUE - -/obj/effect/effect - name = "effect" - icon = 'icons/effects/effects.dmi' - mouse_opacity = 0 - unacidable = TRUE//So effect are not targeted by alien acid. - pass_flags = PASSTABLE | PASSGRILLE - blocks_emissive = EMISSIVE_BLOCK_GENERIC - light_on = TRUE - plane = ABOVE_OBJ_PLANE - -/datum/effect/effect/system - var/number = 3 - var/cardinals = 0 - var/turf/location - var/atom/holder - var/setup = 0 - -/datum/effect/effect/system/proc/set_up(n = 3, c = 0, turf/loc) - if(n > 10) - n = 10 - number = n - cardinals = c - location = loc - setup = 1 - -/datum/effect/effect/system/proc/attach(atom/atom) - holder = atom - -/datum/effect/effect/system/proc/start() - -/datum/effect/effect/system/Destroy() - holder = null - return ..() - -///////////////////////////////////////////// -// GENERIC STEAM SPREAD SYSTEM - -//Usage: set_up(number of bits of steam, use North/South/East/West only, spawn location) -// The attach(atom/atom) proc is optional, and can be called to attach the effect -// to something, like a smoking beaker, so then you can just call start() and the steam -// will always spawn at the items location, even if it's moved. - -/* Example: -var/datum/effect/system/steam_spread/steam = new /datum/effect/system/steam_spread() -- creates new system -steam.set_up(5, 0, mob.loc) -- sets up variables -OPTIONAL: steam.attach(mob) -steam.start() -- spawns the effect -*/ -///////////////////////////////////////////// -/obj/effect/effect/steam - name = "steam" - icon = 'icons/effects/effects.dmi' - icon_state = "extinguish" - density = FALSE - -/datum/effect/effect/system/steam_spread/set_up(n = 3, c = 0, turf/loc) - if(n > 10) - n = 10 - number = n - cardinals = c - location = loc - -/datum/effect/effect/system/steam_spread/start() - var/i = 0 - for(i=0, i 10) - n = 10 - number = n - cardinals = c - if(istype(loca, /turf/)) - location = loca - else - location = get_turf(loca) - -/datum/effect/effect/system/spark_spread/start() - var/i = 0 - for(i=0, i 20) - return - spawn(0) - if(holder) - src.location = get_turf(holder) - var/obj/effect/effect/sparks/sparks = new /obj/effect/effect/sparks(src.location) - src.total_sparks++ - var/direction - if(src.cardinals) - direction = pick(cardinal) - else - direction = pick(alldirs) - for(i=0, i 10) - n = 10 - number = n - cardinals = c - if(istype(loca, /turf/)) - location = loca - else - location = get_turf(loca) - if(direct) - direction = direct - -/datum/effect/effect/system/smoke_spread/start(var/I) - var/i = 0 - for(i=0, i 20) - return - spawn(0) - if(holder) - src.location = get_turf(holder) - var/obj/effect/effect/smoke/smoke = new smoke_type(src.location) - src.total_smoke++ - if(I) - smoke.color = I - var/direction = src.direction - if(!direction) - if(src.cardinals) - direction = pick(cardinal) - else - direction = pick(alldirs) - for(i=0, iThe solution violently explodes.") - for(var/mob/M in viewers(1, location)) - if (prob (50 * amount)) - to_chat(M, "The explosion knocks you down.") - M.Weaken(rand(1,5)) - return - else - var/devst = -1 - var/heavy = -1 - var/light = -1 - var/flash = -1 - - // Clamp all values to fractions of max_explosion_range, following the same pattern as for tank transfer bombs - if (round(amount/12) > 0) - devst = devst + amount/12 - - if (round(amount/6) > 0) - heavy = heavy + amount/6 - - if (round(amount/3) > 0) - light = light + amount/3 - - if (flashing && flashing_factor) - flash = (amount/4) * flashing_factor - - for(var/mob/M in viewers(8, location)) - to_chat(M, "The solution violently explodes.") - - explosion( - location, - round(min(devst, BOMBCAP_DVSTN_RADIUS)), - round(min(heavy, BOMBCAP_HEAVY_RADIUS)), - round(min(light, BOMBCAP_LIGHT_RADIUS)), - round(min(flash, BOMBCAP_FLASH_RADIUS)) - ) +/* This is an attempt to make some easily reusable "particle" type effect, to stop the code +constantly having to be rewritten. An item like the jetpack that uses the ion_trail_follow system, just has one +defined, then set up when it is created with New(). Then this same system can just be reused each time +it needs to create more trails.A beaker could have a steam_trail_follow system set up, then the steam +would spawn and follow the beaker, even if it is carried or thrown. +*/ +/obj/effect + light_on = TRUE + +/obj/effect/effect + name = "effect" + icon = 'icons/effects/effects.dmi' + mouse_opacity = 0 + unacidable = TRUE//So effect are not targeted by alien acid. + pass_flags = PASSTABLE | PASSGRILLE + blocks_emissive = EMISSIVE_BLOCK_GENERIC + light_on = TRUE + plane = ABOVE_OBJ_PLANE + +/datum/effect/effect/system + var/number = 3 + var/cardinals = 0 + var/turf/location + var/atom/holder + var/setup = 0 + +/datum/effect/effect/system/proc/set_up(n = 3, c = 0, turf/loc) + if(n > 10) + n = 10 + number = n + cardinals = c + location = loc + setup = 1 + +/datum/effect/effect/system/proc/attach(atom/atom) + holder = atom + +/datum/effect/effect/system/proc/start() + +/datum/effect/effect/system/Destroy() + holder = null + return ..() + +///////////////////////////////////////////// +// GENERIC STEAM SPREAD SYSTEM + +//Usage: set_up(number of bits of steam, use North/South/East/West only, spawn location) +// The attach(atom/atom) proc is optional, and can be called to attach the effect +// to something, like a smoking beaker, so then you can just call start() and the steam +// will always spawn at the items location, even if it's moved. + +/* Example: +var/datum/effect/system/steam_spread/steam = new /datum/effect/system/steam_spread() -- creates new system +steam.set_up(5, 0, mob.loc) -- sets up variables +OPTIONAL: steam.attach(mob) +steam.start() -- spawns the effect +*/ +///////////////////////////////////////////// +/obj/effect/effect/steam + name = "steam" + icon = 'icons/effects/effects.dmi' + icon_state = "extinguish" + density = FALSE + +/datum/effect/effect/system/steam_spread/set_up(n = 3, c = 0, turf/loc) + if(n > 10) + n = 10 + number = n + cardinals = c + location = loc + +/datum/effect/effect/system/steam_spread/start() + var/i = 0 + for(i=0, i 10) + n = 10 + number = n + cardinals = c + if(istype(loca, /turf/)) + location = loca + else + location = get_turf(loca) + +/datum/effect/effect/system/spark_spread/start() + var/i = 0 + for(i=0, i 20) + return + spawn(0) + if(holder) + src.location = get_turf(holder) + var/obj/effect/effect/sparks/sparks = new /obj/effect/effect/sparks(src.location) + src.total_sparks++ + var/direction + if(src.cardinals) + direction = pick(cardinal) + else + direction = pick(alldirs) + for(i=0, i 10) + n = 10 + number = n + cardinals = c + if(istype(loca, /turf/)) + location = loca + else + location = get_turf(loca) + if(direct) + direction = direct + +/datum/effect/effect/system/smoke_spread/start(var/I) + var/i = 0 + for(i=0, i 20) + return + spawn(0) + if(holder) + src.location = get_turf(holder) + var/obj/effect/effect/smoke/smoke = new smoke_type(src.location) + src.total_smoke++ + if(I) + smoke.color = I + var/direction = src.direction + if(!direction) + if(src.cardinals) + direction = pick(cardinal) + else + direction = pick(alldirs) + for(i=0, iThe solution violently explodes.") + for(var/mob/M in viewers(1, location)) + if (prob (50 * amount)) + to_chat(M, "The explosion knocks you down.") + M.Weaken(rand(1,5)) + return + else + var/devst = -1 + var/heavy = -1 + var/light = -1 + var/flash = -1 + + // Clamp all values to fractions of max_explosion_range, following the same pattern as for tank transfer bombs + if (round(amount/12) > 0) + devst = devst + amount/12 + + if (round(amount/6) > 0) + heavy = heavy + amount/6 + + if (round(amount/3) > 0) + light = light + amount/3 + + if (flashing && flashing_factor) + flash = (amount/4) * flashing_factor + + for(var/mob/M in viewers(8, location)) + to_chat(M, "The solution violently explodes.") + + explosion( + location, + round(min(devst, BOMBCAP_DVSTN_RADIUS)), + round(min(heavy, BOMBCAP_HEAVY_RADIUS)), + round(min(light, BOMBCAP_LIGHT_RADIUS)), + round(min(flash, BOMBCAP_FLASH_RADIUS)) + ) diff --git a/code/game/objects/effects/explosion_particles.dm b/code/game/objects/effects/explosion_particles.dm index 449506197ad..f9460485aa7 100644 --- a/code/game/objects/effects/explosion_particles.dm +++ b/code/game/objects/effects/explosion_particles.dm @@ -1,72 +1,72 @@ -/obj/effect/expl_particles - name = "explosive particles" - icon = 'icons/effects/effects.dmi' - icon_state = "explosion_particle" - opacity = 1 - anchored = TRUE - mouse_opacity = 0 - -/obj/effect/expl_particles/New() - ..() - spawn (15) - qdel(src) - return - -/datum/effect/system/expl_particles - var/number = 10 - var/turf/location - var/total_particles = 0 - -/datum/effect/system/expl_particles/proc/set_up(n = 10, loca) - number = n - if(istype(loca, /turf/)) location = loca - else location = get_turf(loca) - -/datum/effect/system/expl_particles/proc/start() - var/i = 0 - for(i=0, iGib list length mismatch!") - return - - var/obj/effect/decal/cleanable/blood/gibs/gib = null - - if(sparks) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - s.set_up(2, 1, get_turf(location)) // Not sure if it's safe to pass an arbitrary object to set_up, todo - s.start() - - for(var/i = 1, i<= gibtypes.len, i++) - if(gibamounts[i]) - for(var/j = 1, j<= gibamounts[i], j++) - var/gibType = gibtypes[i] - gib = new gibType(location) - - // Apply human species colouration to masks. - if(fleshcolor) - gib.fleshcolor = fleshcolor - if(bloodcolor) - gib.basecolor = bloodcolor - - gib.update_icon() - - gib.blood_DNA = list() - if(MobDNA) - gib.blood_DNA[MobDNA.unique_enzymes] = MobDNA.b_type - else if(istype(src, /obj/effect/gibspawner/human)) // Probably a monkey - gib.blood_DNA["Non-human DNA"] = "A+" - if(istype(location,/turf/)) - var/list/directions = gibdirections[i] - if(directions.len) - gib.streak(directions) - - qdel(src) +/proc/gibs(atom/location, var/datum/dna/MobDNA, gibber_type = /obj/effect/gibspawner/generic, var/fleshcolor, var/bloodcolor) + new gibber_type(location,MobDNA,fleshcolor,bloodcolor) + +/obj/effect/gibspawner + var/sparks = 0 //whether sparks spread on Gib() + var/list/gibtypes = list() + var/list/gibamounts = list() + var/list/gibdirections = list() //of lists + var/fleshcolor //Used for gibbed humans. + var/bloodcolor //Used for gibbed humans. + +/obj/effect/gibspawner/New(location, var/datum/dna/MobDNA, var/fleshcolor, var/bloodcolor) + ..() + + if(fleshcolor) src.fleshcolor = fleshcolor + if(bloodcolor) src.bloodcolor = bloodcolor + Gib(loc,MobDNA) + +/obj/effect/gibspawner/proc/Gib(atom/location, var/datum/dna/MobDNA = null) + if(gibtypes.len != gibamounts.len || gibamounts.len != gibdirections.len) + to_world("Gib list length mismatch!") + return + + var/obj/effect/decal/cleanable/blood/gibs/gib = null + + if(sparks) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + s.set_up(2, 1, get_turf(location)) // Not sure if it's safe to pass an arbitrary object to set_up, todo + s.start() + + for(var/i = 1, i<= gibtypes.len, i++) + if(gibamounts[i]) + for(var/j = 1, j<= gibamounts[i], j++) + var/gibType = gibtypes[i] + gib = new gibType(location) + + // Apply human species colouration to masks. + if(fleshcolor) + gib.fleshcolor = fleshcolor + if(bloodcolor) + gib.basecolor = bloodcolor + + gib.update_icon() + + gib.blood_DNA = list() + if(MobDNA) + gib.blood_DNA[MobDNA.unique_enzymes] = MobDNA.b_type + else if(istype(src, /obj/effect/gibspawner/human)) // Probably a monkey + gib.blood_DNA["Non-human DNA"] = "A+" + if(istype(location,/turf/)) + var/list/directions = gibdirections[i] + if(directions.len) + gib.streak(directions) + + qdel(src) diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index fc6af6b2fd5..e25bc4de774 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -1,291 +1,291 @@ -/obj/effect/landmark - name = "landmark" - icon = 'icons/mob/screen1.dmi' - icon_state = "x2" - anchored = TRUE - unacidable = TRUE - simulated = FALSE - invisibility = 100 - var/delete_me = 0 - -/obj/effect/landmark/New() - ..() - tag = text("landmark*[]", name) - invisibility = 101 - - switch(name) //some of these are probably obsolete - if("monkey") - monkeystart += loc - delete_me = 1 - return - if("start") - newplayer_start += loc - delete_me = 1 - return - if("JoinLate") // Bit difference, since we need the spawn point to move. - latejoin += src - simulated = TRUE - // delete_me = 1 - return - if("JoinLateGateway") - latejoin_gateway += loc - latejoin += src //VOREStation Addition - delete_me = 1 - return - if("JoinLateElevator") - latejoin_elevator += loc - delete_me = 1 - return - if("JoinLateCryo") - latejoin_cryo += loc - delete_me = 1 - return - if("JoinLateCyborg") - latejoin_cyborg += loc - delete_me = 1 - return - if("prisonwarp") - prisonwarp += loc - delete_me = 1 - return - if("Holding Facility") - holdingfacility += loc - if("tdome1") - tdome1 += loc - if("tdome2") - tdome2 += loc - if("tdomeadmin") - tdomeadmin += loc - if("tdomeobserve") - tdomeobserve += loc - if("prisonsecuritywarp") - prisonsecuritywarp += loc - delete_me = 1 - return - if("blobstart") - blobstart += loc - delete_me = 1 - return - if("xeno_spawn") - xeno_spawn += loc - delete_me = 1 - return - if("endgame_exit") - endgame_safespawns += loc - delete_me = 1 - return - if("bluespacerift") - endgame_exits += loc - delete_me = 1 - return - //VOREStation Add Start - if("vinestart") - vinestart += loc - delete_me = 1 - return - //VORE Station Add End - - landmarks_list += src - return 1 - -/obj/effect/landmark/proc/delete() - delete_me = 1 - -/obj/effect/landmark/Initialize() - . = ..() - if(delete_me) - return INITIALIZE_HINT_QDEL - -/obj/effect/landmark/Destroy(var/force = FALSE) - if(delete_me || force) - landmarks_list -= src - return ..() - return QDEL_HINT_LETMELIVE - -/obj/effect/landmark/start - name = "start" - icon = 'icons/mob/screen1.dmi' - icon_state = "x" - anchored = TRUE - -/obj/effect/landmark/start/New() - ..() - tag = "start*[name]" - invisibility = 101 - - return 1 - -/obj/effect/landmark/forbidden_level - delete_me = 1 -/obj/effect/landmark/forbidden_level/Initialize() - . = ..() - if(using_map) - using_map.secret_levels |= z - else - log_error("[type] mapped in but no using_map") - -/obj/effect/landmark/hidden_level - delete_me = 1 -/obj/effect/landmark/hidden_level/Initialize() - . = ..() - if(using_map) - using_map.hidden_levels |= z - else - log_error("[type] mapped in but no using_map") - - -/obj/effect/landmark/virtual_reality - name = "virtual_reality" - icon = 'icons/mob/screen1.dmi' - icon_state = "x" - anchored = TRUE - -/obj/effect/landmark/virtual_reality/New() - ..() - tag = "virtual_reality*[name]" - invisibility = 101 - return 1 - - -//Costume spawner landmarks -/obj/effect/landmark/costume/New() //costume spawner, selects a random subclass and disappears - - var/list/options = typesof(/obj/effect/landmark/costume) - var/PICK= options[rand(1,options.len)] - new PICK(src.loc) - delete_me = 1 - -//SUBCLASSES. Spawn a bunch of items and disappear likewise -/obj/effect/landmark/costume/chicken/New() - new /obj/item/clothing/suit/chickensuit(src.loc) - new /obj/item/clothing/head/chicken(src.loc) - new /obj/item/weapon/reagent_containers/food/snacks/egg(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/gladiator/New() - new /obj/item/clothing/under/gladiator(src.loc) - new /obj/item/clothing/head/helmet/gladiator(src.loc) - qdel(src) - -/obj/effect/landmark/costume/madscientist/New() - new /obj/item/clothing/under/suit_jacket/green(src.loc) - new /obj/item/clothing/head/flatcap(src.loc) - new /obj/item/clothing/suit/storage/toggle/labcoat/mad(src.loc) - new /obj/item/clothing/glasses/gglasses(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/elpresidente/New() - new /obj/item/clothing/under/suit_jacket/green(src.loc) - new /obj/item/clothing/head/flatcap(src.loc) - new /obj/item/clothing/mask/smokable/cigarette/cigar/havana(src.loc) - new /obj/item/clothing/shoes/boots/jackboots(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/nyangirl/New() - new /obj/item/clothing/under/schoolgirl(src.loc) - new /obj/item/clothing/head/kitty(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/maid/New() - new /obj/item/clothing/under/skirt(src.loc) - var/CHOICE = pick( /obj/item/clothing/head/beret , /obj/item/clothing/head/rabbitears ) - new CHOICE(src.loc) - new /obj/item/clothing/glasses/sunglasses/blindfold(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/butler/New() - new /obj/item/clothing/accessory/wcoat(src.loc) - new /obj/item/clothing/under/suit_jacket(src.loc) - new /obj/item/clothing/head/that(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/scratch/New() - new /obj/item/clothing/gloves/white(src.loc) - new /obj/item/clothing/shoes/white(src.loc) - new /obj/item/clothing/under/scratch(src.loc) - if (prob(30)) - new /obj/item/clothing/head/cueball(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/highlander/New() - new /obj/item/clothing/under/kilt(src.loc) - new /obj/item/clothing/head/beret(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/prig/New() - new /obj/item/clothing/accessory/wcoat(src.loc) - new /obj/item/clothing/glasses/monocle(src.loc) - var/CHOICE= pick( /obj/item/clothing/head/bowler, /obj/item/clothing/head/that) - new CHOICE(src.loc) - new /obj/item/clothing/shoes/black(src.loc) - new /obj/item/weapon/cane(src.loc) - new /obj/item/clothing/under/sl_suit(src.loc) - new /obj/item/clothing/mask/fakemoustache(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/plaguedoctor/New() - new /obj/item/clothing/suit/bio_suit/plaguedoctorsuit(src.loc) - new /obj/item/clothing/head/plaguedoctorhat(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/nightowl/New() - new /obj/item/clothing/under/owl(src.loc) - new /obj/item/clothing/mask/gas/owl_mask(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/waiter/New() - new /obj/item/clothing/under/waiter(src.loc) - var/CHOICE= pick( /obj/item/clothing/head/kitty, /obj/item/clothing/head/rabbitears) - new CHOICE(src.loc) - new /obj/item/clothing/suit/storage/apron(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/pirate/New() - new /obj/item/clothing/under/pirate(src.loc) - new /obj/item/clothing/suit/pirate(src.loc) - var/CHOICE = pick( /obj/item/clothing/head/pirate , /obj/item/clothing/head/bandana ) - new CHOICE(src.loc) - new /obj/item/clothing/glasses/eyepatch(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/commie/New() - new /obj/item/clothing/under/soviet(src.loc) - new /obj/item/clothing/head/ushanka(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/imperium_monk/New() - new /obj/item/clothing/suit/imperium_monk(src.loc) - if (prob(25)) - new /obj/item/clothing/mask/gas/cyborg(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/holiday_priest/New() - new /obj/item/clothing/suit/holidaypriest(src.loc) - qdel(src) - -/obj/effect/landmark/costume/marisawizard/fake/New() - new /obj/item/clothing/head/wizard/marisa/fake(src.loc) - new/obj/item/clothing/suit/wizrobe/marisa/fake(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/cutewitch/New() - new /obj/item/clothing/under/sundress(src.loc) - new /obj/item/clothing/head/witchwig(src.loc) - new /obj/item/weapon/staff/broom(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/fakewizard/New() - new /obj/item/clothing/suit/wizrobe/fake(src.loc) - new /obj/item/clothing/head/wizard/fake(src.loc) - new /obj/item/weapon/staff/(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/sexyclown/New() - new /obj/item/clothing/mask/gas/sexyclown(src.loc) - new /obj/item/clothing/under/sexyclown(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/sexymime/New() - new /obj/item/clothing/mask/gas/sexymime(src.loc) - new /obj/item/clothing/under/sexymime(src.loc) - delete_me = 1 +/obj/effect/landmark + name = "landmark" + icon = 'icons/mob/screen1.dmi' + icon_state = "x2" + anchored = TRUE + unacidable = TRUE + simulated = FALSE + invisibility = 100 + var/delete_me = 0 + +/obj/effect/landmark/New() + ..() + tag = text("landmark*[]", name) + invisibility = 101 + + switch(name) //some of these are probably obsolete + if("monkey") + monkeystart += loc + delete_me = 1 + return + if("start") + newplayer_start += loc + delete_me = 1 + return + if("JoinLate") // Bit difference, since we need the spawn point to move. + latejoin += src + simulated = TRUE + // delete_me = 1 + return + if("JoinLateGateway") + latejoin_gateway += loc + latejoin += src //VOREStation Addition + delete_me = 1 + return + if("JoinLateElevator") + latejoin_elevator += loc + delete_me = 1 + return + if("JoinLateCryo") + latejoin_cryo += loc + delete_me = 1 + return + if("JoinLateCyborg") + latejoin_cyborg += loc + delete_me = 1 + return + if("prisonwarp") + prisonwarp += loc + delete_me = 1 + return + if("Holding Facility") + holdingfacility += loc + if("tdome1") + tdome1 += loc + if("tdome2") + tdome2 += loc + if("tdomeadmin") + tdomeadmin += loc + if("tdomeobserve") + tdomeobserve += loc + if("prisonsecuritywarp") + prisonsecuritywarp += loc + delete_me = 1 + return + if("blobstart") + blobstart += loc + delete_me = 1 + return + if("xeno_spawn") + xeno_spawn += loc + delete_me = 1 + return + if("endgame_exit") + endgame_safespawns += loc + delete_me = 1 + return + if("bluespacerift") + endgame_exits += loc + delete_me = 1 + return + //VOREStation Add Start + if("vinestart") + vinestart += loc + delete_me = 1 + return + //VORE Station Add End + + landmarks_list += src + return 1 + +/obj/effect/landmark/proc/delete() + delete_me = 1 + +/obj/effect/landmark/Initialize() + . = ..() + if(delete_me) + return INITIALIZE_HINT_QDEL + +/obj/effect/landmark/Destroy(var/force = FALSE) + if(delete_me || force) + landmarks_list -= src + return ..() + return QDEL_HINT_LETMELIVE + +/obj/effect/landmark/start + name = "start" + icon = 'icons/mob/screen1.dmi' + icon_state = "x" + anchored = TRUE + +/obj/effect/landmark/start/New() + ..() + tag = "start*[name]" + invisibility = 101 + + return 1 + +/obj/effect/landmark/forbidden_level + delete_me = 1 +/obj/effect/landmark/forbidden_level/Initialize() + . = ..() + if(using_map) + using_map.secret_levels |= z + else + log_error("[type] mapped in but no using_map") + +/obj/effect/landmark/hidden_level + delete_me = 1 +/obj/effect/landmark/hidden_level/Initialize() + . = ..() + if(using_map) + using_map.hidden_levels |= z + else + log_error("[type] mapped in but no using_map") + + +/obj/effect/landmark/virtual_reality + name = "virtual_reality" + icon = 'icons/mob/screen1.dmi' + icon_state = "x" + anchored = TRUE + +/obj/effect/landmark/virtual_reality/New() + ..() + tag = "virtual_reality*[name]" + invisibility = 101 + return 1 + + +//Costume spawner landmarks +/obj/effect/landmark/costume/New() //costume spawner, selects a random subclass and disappears + + var/list/options = typesof(/obj/effect/landmark/costume) + var/PICK= options[rand(1,options.len)] + new PICK(src.loc) + delete_me = 1 + +//SUBCLASSES. Spawn a bunch of items and disappear likewise +/obj/effect/landmark/costume/chicken/New() + new /obj/item/clothing/suit/chickensuit(src.loc) + new /obj/item/clothing/head/chicken(src.loc) + new /obj/item/weapon/reagent_containers/food/snacks/egg(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/gladiator/New() + new /obj/item/clothing/under/gladiator(src.loc) + new /obj/item/clothing/head/helmet/gladiator(src.loc) + qdel(src) + +/obj/effect/landmark/costume/madscientist/New() + new /obj/item/clothing/under/suit_jacket/green(src.loc) + new /obj/item/clothing/head/flatcap(src.loc) + new /obj/item/clothing/suit/storage/toggle/labcoat/mad(src.loc) + new /obj/item/clothing/glasses/gglasses(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/elpresidente/New() + new /obj/item/clothing/under/suit_jacket/green(src.loc) + new /obj/item/clothing/head/flatcap(src.loc) + new /obj/item/clothing/mask/smokable/cigarette/cigar/havana(src.loc) + new /obj/item/clothing/shoes/boots/jackboots(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/nyangirl/New() + new /obj/item/clothing/under/schoolgirl(src.loc) + new /obj/item/clothing/head/kitty(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/maid/New() + new /obj/item/clothing/under/skirt(src.loc) + var/CHOICE = pick( /obj/item/clothing/head/beret , /obj/item/clothing/head/rabbitears ) + new CHOICE(src.loc) + new /obj/item/clothing/glasses/sunglasses/blindfold(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/butler/New() + new /obj/item/clothing/accessory/wcoat(src.loc) + new /obj/item/clothing/under/suit_jacket(src.loc) + new /obj/item/clothing/head/that(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/scratch/New() + new /obj/item/clothing/gloves/white(src.loc) + new /obj/item/clothing/shoes/white(src.loc) + new /obj/item/clothing/under/scratch(src.loc) + if (prob(30)) + new /obj/item/clothing/head/cueball(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/highlander/New() + new /obj/item/clothing/under/kilt(src.loc) + new /obj/item/clothing/head/beret(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/prig/New() + new /obj/item/clothing/accessory/wcoat(src.loc) + new /obj/item/clothing/glasses/monocle(src.loc) + var/CHOICE= pick( /obj/item/clothing/head/bowler, /obj/item/clothing/head/that) + new CHOICE(src.loc) + new /obj/item/clothing/shoes/black(src.loc) + new /obj/item/weapon/cane(src.loc) + new /obj/item/clothing/under/sl_suit(src.loc) + new /obj/item/clothing/mask/fakemoustache(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/plaguedoctor/New() + new /obj/item/clothing/suit/bio_suit/plaguedoctorsuit(src.loc) + new /obj/item/clothing/head/plaguedoctorhat(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/nightowl/New() + new /obj/item/clothing/under/owl(src.loc) + new /obj/item/clothing/mask/gas/owl_mask(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/waiter/New() + new /obj/item/clothing/under/waiter(src.loc) + var/CHOICE= pick( /obj/item/clothing/head/kitty, /obj/item/clothing/head/rabbitears) + new CHOICE(src.loc) + new /obj/item/clothing/suit/storage/apron(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/pirate/New() + new /obj/item/clothing/under/pirate(src.loc) + new /obj/item/clothing/suit/pirate(src.loc) + var/CHOICE = pick( /obj/item/clothing/head/pirate , /obj/item/clothing/head/bandana ) + new CHOICE(src.loc) + new /obj/item/clothing/glasses/eyepatch(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/commie/New() + new /obj/item/clothing/under/soviet(src.loc) + new /obj/item/clothing/head/ushanka(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/imperium_monk/New() + new /obj/item/clothing/suit/imperium_monk(src.loc) + if (prob(25)) + new /obj/item/clothing/mask/gas/cyborg(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/holiday_priest/New() + new /obj/item/clothing/suit/holidaypriest(src.loc) + qdel(src) + +/obj/effect/landmark/costume/marisawizard/fake/New() + new /obj/item/clothing/head/wizard/marisa/fake(src.loc) + new/obj/item/clothing/suit/wizrobe/marisa/fake(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/cutewitch/New() + new /obj/item/clothing/under/sundress(src.loc) + new /obj/item/clothing/head/witchwig(src.loc) + new /obj/item/weapon/staff/broom(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/fakewizard/New() + new /obj/item/clothing/suit/wizrobe/fake(src.loc) + new /obj/item/clothing/head/wizard/fake(src.loc) + new /obj/item/weapon/staff/(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/sexyclown/New() + new /obj/item/clothing/mask/gas/sexyclown(src.loc) + new /obj/item/clothing/under/sexyclown(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/sexymime/New() + new /obj/item/clothing/mask/gas/sexymime(src.loc) + new /obj/item/clothing/under/sexymime(src.loc) + delete_me = 1 diff --git a/code/game/objects/effects/manifest.dm b/code/game/objects/effects/manifest.dm index 98d378faf5d..a6ec15c348e 100644 --- a/code/game/objects/effects/manifest.dm +++ b/code/game/objects/effects/manifest.dm @@ -1,21 +1,21 @@ -/obj/effect/manifest - name = "manifest" - icon = 'icons/mob/screen1.dmi' - icon_state = "x" - unacidable = TRUE//Just to be sure. - -/obj/effect/manifest/New() - - src.invisibility = 101 - return - -/obj/effect/manifest/proc/manifest() - var/dat = "Crew Manifest:
                    " - for(var/mob/living/carbon/human/M in mob_list) - dat += text(" [] - []
                    ", M.name, M.get_assignment()) - var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( src.loc ) - P.info = dat - P.name = "paper- 'Crew Manifest'" - //SN src = null - qdel(src) +/obj/effect/manifest + name = "manifest" + icon = 'icons/mob/screen1.dmi' + icon_state = "x" + unacidable = TRUE//Just to be sure. + +/obj/effect/manifest/New() + + src.invisibility = 101 + return + +/obj/effect/manifest/proc/manifest() + var/dat = "Crew Manifest:
                    " + for(var/mob/living/carbon/human/M in mob_list) + dat += text(" [] - []
                    ", M.name, M.get_assignment()) + var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( src.loc ) + P.info = dat + P.name = "paper- 'Crew Manifest'" + //SN src = null + qdel(src) return \ No newline at end of file diff --git a/code/game/objects/effects/map_effects/beam_point.dm b/code/game/objects/effects/map_effects/beam_point.dm index 6a023a0470f..e8973e5fca7 100644 --- a/code/game/objects/effects/map_effects/beam_point.dm +++ b/code/game/objects/effects/map_effects/beam_point.dm @@ -1,191 +1,191 @@ -GLOBAL_LIST_EMPTY(all_beam_points) - -// Creates and manages a beam attached to itself and another beam_point. -// You can do cool things with these such as moving the beam_point to move the beam, turning them on and off on a timer, triggered by external input, and more. -/obj/effect/map_effect/beam_point - name = "beam point" - icon_state = "beam_point" - - // General variables. - var/list/my_beams = list() // Instances of beams. Deleting one will kill the beam. - var/id = "A" // Two beam_points must share the same ID to be connected to each other. - var/max_beams = 10 // How many concurrent beams to seperate beam_points to have at once. Set to zero to only act as targets for other beam_points. - var/seek_range = 7 // How far to look for an end beam_point when not having a beam. Defaults to screen height/width. Make sure this is below beam_max_distance. - - // Controls how and when the beam is created. - var/make_beams_on_init = FALSE - var/use_timer = FALSE // Sadly not the /tg/ timers. - var/list/on_duration = list(2 SECONDS, 2 SECONDS, 2 SECONDS) // How long the beam should stay on for, if use_timer is true. Alternates between each duration in the list. - var/list/off_duration = list(3 SECONDS, 0.5 SECOND, 0.5 SECOND) // How long it should stay off for. List length is not needed to be the same as on_duration. - var/timer_on_index = 1 // Index to use for on_duration list. - var/timer_off_index = 1// Ditto, for off_duration list. - var/initial_delay = 0 // How long to wait before first turning on the beam, to sync beam times or create a specific pattern. - var/beam_creation_sound = null // Optional sound played when one or more beams are created. - var/beam_destruction_sound = null // Optional sound played when a beam is destroyed. - - // Beam datum arguments. - var/beam_icon = 'icons/effects/beam.dmi' // Icon file to use for beam visuals. - var/beam_icon_state = "b_beam" // Icon state to use for visuals. - var/beam_time = INFINITY // How long the beam lasts. By default it will last forever until destroyed. - var/beam_max_distance = 10 // If the beam is farther than this, it will be destroyed. Make sure it's higher than seek_range. - var/beam_type = /obj/effect/ebeam // The type of beam. Default has no special properties. Some others may do things like hurt things touching it. - var/beam_sleep_time = 3 // How often the beam updates visually. Suggested to leave this alone, 3 is already fast. - -/obj/effect/map_effect/beam_point/Initialize() - GLOB.all_beam_points += src - if(make_beams_on_init) - create_beams() - if(use_timer) - addtimer(CALLBACK(src, PROC_REF(handle_beam_timer)), initial_delay) - return ..() - -/obj/effect/map_effect/beam_point/Destroy() - destroy_all_beams() - use_timer = FALSE - GLOB.all_beam_points -= src - return ..() - -// This is the top level proc to make the magic happen. -/obj/effect/map_effect/beam_point/proc/create_beams() - if(my_beams.len >= max_beams) - return - var/beams_to_fill = max_beams - my_beams.len - for(var/i = 1 to beams_to_fill) - var/obj/effect/map_effect/beam_point/point = seek_beam_point() - if(!point) - break // No more points could be found, no point checking repeatively. - build_beam(point) - -// Finds a suitable beam point. -/obj/effect/map_effect/beam_point/proc/seek_beam_point() - for(var/obj/effect/map_effect/beam_point/point in GLOB.all_beam_points) - if(id != point.id) - continue // Not linked together by ID. - if(has_active_beam(point)) - continue // Already got one. - if(point.z != src.z) - continue // Not on same z-level. get_dist() ignores z-levels by design according to docs. - if(get_dist(src, point) > seek_range) - continue // Too far. - return point - -// Checks if the two points have an active beam between them. -// Used to make sure two points don't have more than one beam. -/obj/effect/map_effect/beam_point/proc/has_active_beam(var/obj/effect/map_effect/beam_point/them) - // First, check our beams. - for(var/datum/beam/B in my_beams) - if(B.target == them) - return TRUE - if(B.origin == them) // This shouldn't be needed unless the beam gets built backwards but why not. - return TRUE - - // Now check theirs, to see if they have a beam on us. - for(var/datum/beam/B in them.my_beams) - if(B.target == src) - return TRUE - if(B.origin == src) // Same story as above. - return TRUE - - return FALSE - -/obj/effect/map_effect/beam_point/proc/build_beam(var/atom/beam_target) - if(!beam_target) - log_debug("[src] ([src.type] \[[x],[y],[z]\]) failed to build its beam due to not having a target.") - return FALSE - - var/datum/beam/new_beam = Beam(beam_target, beam_icon_state, beam_icon, beam_time, beam_max_distance, beam_type, beam_sleep_time) - my_beams += new_beam - if(beam_creation_sound) - playsound(src, beam_creation_sound, 70, 1) - - return TRUE - -/obj/effect/map_effect/beam_point/proc/destroy_beam(var/datum/beam/B) - if(!B) - log_debug("[src] ([src.type] \[[x],[y],[z]\]) was asked to destroy a beam that does not exist.") - return FALSE - - if(!(B in my_beams)) - log_debug("[src] ([src.type] \[[x],[y],[z]\]) was asked to destroy a beam it did not own.") - return FALSE - - my_beams -= B - qdel(B) - if(beam_destruction_sound) - playsound(src, beam_destruction_sound, 70, 1) - - return TRUE - -/obj/effect/map_effect/beam_point/proc/destroy_all_beams() - for(var/datum/beam/B in my_beams) - destroy_beam(B) - return TRUE - -// This code makes me sad. -/obj/effect/map_effect/beam_point/proc/handle_beam_timer() - if(!use_timer || QDELETED(src)) - return - - if(my_beams.len) // Currently on. - destroy_all_beams() - color = "#FF0000" - - timer_off_index++ - if(timer_off_index > off_duration.len) - timer_off_index = 1 - - spawn(off_duration[timer_off_index]) - .() - - else // Currently off. - // If nobody's around, keep the beams off to avoid wasteful beam process(), if they have one. - if(!always_run && !check_for_player_proximity(src, proximity_needed, ignore_ghosts, ignore_afk)) - spawn(retry_delay) - .() - return - - create_beams() - color = "#00FF00" - - timer_on_index++ - if(timer_on_index > on_duration.len) - timer_on_index = 1 - - spawn(on_duration[timer_on_index]) - .() - - - -// Subtypes to use in maps and adminbuse. -// Remember, beam_points ONLY connect to other beam_points with the same id variable. - -// Creates the beam when instantiated and stays on until told otherwise. -/obj/effect/map_effect/beam_point/instant - make_beams_on_init = TRUE - -/obj/effect/map_effect/beam_point/instant/electric - beam_icon_state = "nzcrentrs_power" - beam_type = /obj/effect/ebeam/reactive/electric - beam_creation_sound = 'sound/effects/lightningshock.ogg' - beam_destruction_sound = "sparks" - -// Turns on and off on a timer. -/obj/effect/map_effect/beam_point/timer - use_timer = TRUE - -// Shocks people who touch the beam while it's on. Flicks on and off on a specific pattern. -/obj/effect/map_effect/beam_point/timer/electric - beam_icon_state = "nzcrentrs_power" - beam_type = /obj/effect/ebeam/reactive/electric - beam_creation_sound = 'sound/effects/lightningshock.ogg' - beam_destruction_sound = "sparks" - seek_range = 3 - -// Is only a target for other beams to connect to. -/obj/effect/map_effect/beam_point/end - max_beams = 0 - -// Can only have one beam. -/obj/effect/map_effect/beam_point/mono - make_beams_on_init = TRUE - max_beams = 1 +GLOBAL_LIST_EMPTY(all_beam_points) + +// Creates and manages a beam attached to itself and another beam_point. +// You can do cool things with these such as moving the beam_point to move the beam, turning them on and off on a timer, triggered by external input, and more. +/obj/effect/map_effect/beam_point + name = "beam point" + icon_state = "beam_point" + + // General variables. + var/list/my_beams = list() // Instances of beams. Deleting one will kill the beam. + var/id = "A" // Two beam_points must share the same ID to be connected to each other. + var/max_beams = 10 // How many concurrent beams to seperate beam_points to have at once. Set to zero to only act as targets for other beam_points. + var/seek_range = 7 // How far to look for an end beam_point when not having a beam. Defaults to screen height/width. Make sure this is below beam_max_distance. + + // Controls how and when the beam is created. + var/make_beams_on_init = FALSE + var/use_timer = FALSE // Sadly not the /tg/ timers. + var/list/on_duration = list(2 SECONDS, 2 SECONDS, 2 SECONDS) // How long the beam should stay on for, if use_timer is true. Alternates between each duration in the list. + var/list/off_duration = list(3 SECONDS, 0.5 SECOND, 0.5 SECOND) // How long it should stay off for. List length is not needed to be the same as on_duration. + var/timer_on_index = 1 // Index to use for on_duration list. + var/timer_off_index = 1// Ditto, for off_duration list. + var/initial_delay = 0 // How long to wait before first turning on the beam, to sync beam times or create a specific pattern. + var/beam_creation_sound = null // Optional sound played when one or more beams are created. + var/beam_destruction_sound = null // Optional sound played when a beam is destroyed. + + // Beam datum arguments. + var/beam_icon = 'icons/effects/beam.dmi' // Icon file to use for beam visuals. + var/beam_icon_state = "b_beam" // Icon state to use for visuals. + var/beam_time = INFINITY // How long the beam lasts. By default it will last forever until destroyed. + var/beam_max_distance = 10 // If the beam is farther than this, it will be destroyed. Make sure it's higher than seek_range. + var/beam_type = /obj/effect/ebeam // The type of beam. Default has no special properties. Some others may do things like hurt things touching it. + var/beam_sleep_time = 3 // How often the beam updates visually. Suggested to leave this alone, 3 is already fast. + +/obj/effect/map_effect/beam_point/Initialize() + GLOB.all_beam_points += src + if(make_beams_on_init) + create_beams() + if(use_timer) + addtimer(CALLBACK(src, PROC_REF(handle_beam_timer)), initial_delay) + return ..() + +/obj/effect/map_effect/beam_point/Destroy() + destroy_all_beams() + use_timer = FALSE + GLOB.all_beam_points -= src + return ..() + +// This is the top level proc to make the magic happen. +/obj/effect/map_effect/beam_point/proc/create_beams() + if(my_beams.len >= max_beams) + return + var/beams_to_fill = max_beams - my_beams.len + for(var/i = 1 to beams_to_fill) + var/obj/effect/map_effect/beam_point/point = seek_beam_point() + if(!point) + break // No more points could be found, no point checking repeatively. + build_beam(point) + +// Finds a suitable beam point. +/obj/effect/map_effect/beam_point/proc/seek_beam_point() + for(var/obj/effect/map_effect/beam_point/point in GLOB.all_beam_points) + if(id != point.id) + continue // Not linked together by ID. + if(has_active_beam(point)) + continue // Already got one. + if(point.z != src.z) + continue // Not on same z-level. get_dist() ignores z-levels by design according to docs. + if(get_dist(src, point) > seek_range) + continue // Too far. + return point + +// Checks if the two points have an active beam between them. +// Used to make sure two points don't have more than one beam. +/obj/effect/map_effect/beam_point/proc/has_active_beam(var/obj/effect/map_effect/beam_point/them) + // First, check our beams. + for(var/datum/beam/B in my_beams) + if(B.target == them) + return TRUE + if(B.origin == them) // This shouldn't be needed unless the beam gets built backwards but why not. + return TRUE + + // Now check theirs, to see if they have a beam on us. + for(var/datum/beam/B in them.my_beams) + if(B.target == src) + return TRUE + if(B.origin == src) // Same story as above. + return TRUE + + return FALSE + +/obj/effect/map_effect/beam_point/proc/build_beam(var/atom/beam_target) + if(!beam_target) + log_debug("[src] ([src.type] \[[x],[y],[z]\]) failed to build its beam due to not having a target.") + return FALSE + + var/datum/beam/new_beam = Beam(beam_target, beam_icon_state, beam_icon, beam_time, beam_max_distance, beam_type, beam_sleep_time) + my_beams += new_beam + if(beam_creation_sound) + playsound(src, beam_creation_sound, 70, 1) + + return TRUE + +/obj/effect/map_effect/beam_point/proc/destroy_beam(var/datum/beam/B) + if(!B) + log_debug("[src] ([src.type] \[[x],[y],[z]\]) was asked to destroy a beam that does not exist.") + return FALSE + + if(!(B in my_beams)) + log_debug("[src] ([src.type] \[[x],[y],[z]\]) was asked to destroy a beam it did not own.") + return FALSE + + my_beams -= B + qdel(B) + if(beam_destruction_sound) + playsound(src, beam_destruction_sound, 70, 1) + + return TRUE + +/obj/effect/map_effect/beam_point/proc/destroy_all_beams() + for(var/datum/beam/B in my_beams) + destroy_beam(B) + return TRUE + +// This code makes me sad. +/obj/effect/map_effect/beam_point/proc/handle_beam_timer() + if(!use_timer || QDELETED(src)) + return + + if(my_beams.len) // Currently on. + destroy_all_beams() + color = "#FF0000" + + timer_off_index++ + if(timer_off_index > off_duration.len) + timer_off_index = 1 + + spawn(off_duration[timer_off_index]) + .() + + else // Currently off. + // If nobody's around, keep the beams off to avoid wasteful beam process(), if they have one. + if(!always_run && !check_for_player_proximity(src, proximity_needed, ignore_ghosts, ignore_afk)) + spawn(retry_delay) + .() + return + + create_beams() + color = "#00FF00" + + timer_on_index++ + if(timer_on_index > on_duration.len) + timer_on_index = 1 + + spawn(on_duration[timer_on_index]) + .() + + + +// Subtypes to use in maps and adminbuse. +// Remember, beam_points ONLY connect to other beam_points with the same id variable. + +// Creates the beam when instantiated and stays on until told otherwise. +/obj/effect/map_effect/beam_point/instant + make_beams_on_init = TRUE + +/obj/effect/map_effect/beam_point/instant/electric + beam_icon_state = "nzcrentrs_power" + beam_type = /obj/effect/ebeam/reactive/electric + beam_creation_sound = 'sound/effects/lightningshock.ogg' + beam_destruction_sound = "sparks" + +// Turns on and off on a timer. +/obj/effect/map_effect/beam_point/timer + use_timer = TRUE + +// Shocks people who touch the beam while it's on. Flicks on and off on a specific pattern. +/obj/effect/map_effect/beam_point/timer/electric + beam_icon_state = "nzcrentrs_power" + beam_type = /obj/effect/ebeam/reactive/electric + beam_creation_sound = 'sound/effects/lightningshock.ogg' + beam_destruction_sound = "sparks" + seek_range = 3 + +// Is only a target for other beams to connect to. +/obj/effect/map_effect/beam_point/end + max_beams = 0 + +// Can only have one beam. +/obj/effect/map_effect/beam_point/mono + make_beams_on_init = TRUE + max_beams = 1 diff --git a/code/game/objects/effects/map_effects/map_effects.dm b/code/game/objects/effects/map_effects/map_effects.dm index d5bad866620..ef1099d4b42 100644 --- a/code/game/objects/effects/map_effects/map_effects.dm +++ b/code/game/objects/effects/map_effects/map_effects.dm @@ -1,69 +1,69 @@ -// These are objects you can use inside special maps (like PoIs), or for adminbuse. -// Players cannot see or interact with these. -/obj/effect/map_effect - anchored = TRUE - invisibility = 99 // So a badmin can go view these by changing their see_invisible. - icon = 'icons/effects/map_effects.dmi' - - // Below vars concern check_for_player_proximity() and is used to not waste effort if nobody is around to appreciate the effects. - var/always_run = FALSE // If true, the game will not try to suppress this from firing if nobody is around to see it. - var/proximity_needed = 12 // How many tiles a mob with a client must be for this to run. - var/ignore_ghosts = FALSE // If true, ghosts won't satisfy the above requirement. - var/ignore_afk = TRUE // If true, AFK people (5 minutes) won't satisfy it as well. - var/retry_delay = 5 SECONDS // How long until we check for players again. - var/next_attempt = 0 // Next time we're going to do ACTUAL WORK - -/obj/effect/map_effect/ex_act() - return - -/obj/effect/map_effect/singularity_pull() - return - -/obj/effect/map_effect/singularity_act() - return - -// Base type for effects that run on variable intervals. -/obj/effect/map_effect/interval - var/interval_lower_bound = 5 SECONDS // Lower number for how often the map_effect will trigger. - var/interval_upper_bound = 5 SECONDS // Higher number for above. - -/obj/effect/map_effect/interval/Initialize() - . = ..() - START_PROCESSING(SSobj, src) - -/obj/effect/map_effect/interval/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -// Override this for the specific thing to do. -/obj/effect/map_effect/interval/proc/trigger() - return - -// Handles the delay and making sure it doesn't run when it would be bad. -/obj/effect/map_effect/interval/process() - //Not yet! - if(world.time < next_attempt) - return - - // Check to see if we're useful first. - if(!always_run && !check_for_player_proximity(src, proximity_needed, ignore_ghosts, ignore_afk)) - next_attempt = world.time + retry_delay - // Hey there's someone nearby. - else - next_attempt = world.time + rand(interval_lower_bound, interval_upper_bound) - trigger() - -// Helper proc to optimize the use of effects by making sure they do not run if nobody is around to perceive it. -/proc/check_for_player_proximity(var/atom/proximity_to, var/radius = 12, var/ignore_ghosts = FALSE, var/ignore_afk = TRUE) - if(!proximity_to) - return FALSE - - for(var/thing in player_list) - var/mob/M = thing // Avoiding typechecks for more speed, player_list will only contain mobs anyways. - if(ignore_ghosts && isobserver(M)) - continue - if(ignore_afk && M.client && M.client.is_afk(5 MINUTES)) - continue - if(M.z == proximity_to.z && get_dist(M, proximity_to) <= radius) - return TRUE - return FALSE +// These are objects you can use inside special maps (like PoIs), or for adminbuse. +// Players cannot see or interact with these. +/obj/effect/map_effect + anchored = TRUE + invisibility = 99 // So a badmin can go view these by changing their see_invisible. + icon = 'icons/effects/map_effects.dmi' + + // Below vars concern check_for_player_proximity() and is used to not waste effort if nobody is around to appreciate the effects. + var/always_run = FALSE // If true, the game will not try to suppress this from firing if nobody is around to see it. + var/proximity_needed = 12 // How many tiles a mob with a client must be for this to run. + var/ignore_ghosts = FALSE // If true, ghosts won't satisfy the above requirement. + var/ignore_afk = TRUE // If true, AFK people (5 minutes) won't satisfy it as well. + var/retry_delay = 5 SECONDS // How long until we check for players again. + var/next_attempt = 0 // Next time we're going to do ACTUAL WORK + +/obj/effect/map_effect/ex_act() + return + +/obj/effect/map_effect/singularity_pull() + return + +/obj/effect/map_effect/singularity_act() + return + +// Base type for effects that run on variable intervals. +/obj/effect/map_effect/interval + var/interval_lower_bound = 5 SECONDS // Lower number for how often the map_effect will trigger. + var/interval_upper_bound = 5 SECONDS // Higher number for above. + +/obj/effect/map_effect/interval/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + +/obj/effect/map_effect/interval/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +// Override this for the specific thing to do. +/obj/effect/map_effect/interval/proc/trigger() + return + +// Handles the delay and making sure it doesn't run when it would be bad. +/obj/effect/map_effect/interval/process() + //Not yet! + if(world.time < next_attempt) + return + + // Check to see if we're useful first. + if(!always_run && !check_for_player_proximity(src, proximity_needed, ignore_ghosts, ignore_afk)) + next_attempt = world.time + retry_delay + // Hey there's someone nearby. + else + next_attempt = world.time + rand(interval_lower_bound, interval_upper_bound) + trigger() + +// Helper proc to optimize the use of effects by making sure they do not run if nobody is around to perceive it. +/proc/check_for_player_proximity(var/atom/proximity_to, var/radius = 12, var/ignore_ghosts = FALSE, var/ignore_afk = TRUE) + if(!proximity_to) + return FALSE + + for(var/thing in player_list) + var/mob/M = thing // Avoiding typechecks for more speed, player_list will only contain mobs anyways. + if(ignore_ghosts && isobserver(M)) + continue + if(ignore_afk && M.client && M.client.is_afk(5 MINUTES)) + continue + if(M.z == proximity_to.z && get_dist(M, proximity_to) <= radius) + return TRUE + return FALSE diff --git a/code/game/objects/effects/map_effects/perma_light.dm b/code/game/objects/effects/map_effects/perma_light.dm index 35eef6d6b0b..d2e9caa9244 100644 --- a/code/game/objects/effects/map_effects/perma_light.dm +++ b/code/game/objects/effects/map_effects/perma_light.dm @@ -1,39 +1,39 @@ -// Emits light forever with magic. Useful for mood lighting in Points of Interest. -// Be sure to check how it looks ingame, and fiddle with the settings until it looks right. -/obj/effect/map_effect/perma_light - name = "permanent light" - icon_state = "permalight" - - light_range = 3 - light_power = 1 - light_color = "#FFFFFF" - light_on = TRUE - -/obj/effect/map_effect/perma_light/brighter - name = "permanent light (bright)" - icon_state = "permalight" - - light_range = 5 - light_power = 3 - light_color = "#FFFFFF" - -/obj/effect/map_effect/perma_light/concentrated - name = "permanent light (concentrated)" - - light_range = 2 - light_power = 5 - -/obj/effect/map_effect/perma_light/concentrated/incandescent - name = "permanent light (concentrated incandescent)" - - light_color = LIGHT_COLOR_INCANDESCENT_TUBE - -// VOREStation Addition Start -/obj/effect/map_effect/perma_light/gateway - name = "permanent light (gateway)" - icon_state = "permalight" - - light_range = 10 - light_power = 5 - light_color = "#b6cdff" +// Emits light forever with magic. Useful for mood lighting in Points of Interest. +// Be sure to check how it looks ingame, and fiddle with the settings until it looks right. +/obj/effect/map_effect/perma_light + name = "permanent light" + icon_state = "permalight" + + light_range = 3 + light_power = 1 + light_color = "#FFFFFF" + light_on = TRUE + +/obj/effect/map_effect/perma_light/brighter + name = "permanent light (bright)" + icon_state = "permalight" + + light_range = 5 + light_power = 3 + light_color = "#FFFFFF" + +/obj/effect/map_effect/perma_light/concentrated + name = "permanent light (concentrated)" + + light_range = 2 + light_power = 5 + +/obj/effect/map_effect/perma_light/concentrated/incandescent + name = "permanent light (concentrated incandescent)" + + light_color = LIGHT_COLOR_INCANDESCENT_TUBE + +// VOREStation Addition Start +/obj/effect/map_effect/perma_light/gateway + name = "permanent light (gateway)" + icon_state = "permalight" + + light_range = 10 + light_power = 5 + light_color = "#b6cdff" // VOREStation Addition End \ No newline at end of file diff --git a/code/game/objects/effects/map_effects/radiation_emitter.dm b/code/game/objects/effects/map_effects/radiation_emitter.dm index 8abf946556d..89a8d194f53 100644 --- a/code/game/objects/effects/map_effects/radiation_emitter.dm +++ b/code/game/objects/effects/map_effects/radiation_emitter.dm @@ -1,19 +1,19 @@ -// Constantly emites radiation from the tile it's placed on. -/obj/effect/map_effect/radiation_emitter - name = "radiation emitter" - icon_state = "radiation_emitter" - var/radiation_power = 30 // Bigger numbers means more radiation. - -/obj/effect/map_effect/radiation_emitter/Initialize() - START_PROCESSING(SSobj, src) - return ..() - -/obj/effect/map_effect/radiation_emitter/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/effect/map_effect/radiation_emitter/process() - SSradiation.radiate(src, radiation_power) - +// Constantly emites radiation from the tile it's placed on. +/obj/effect/map_effect/radiation_emitter + name = "radiation emitter" + icon_state = "radiation_emitter" + var/radiation_power = 30 // Bigger numbers means more radiation. + +/obj/effect/map_effect/radiation_emitter/Initialize() + START_PROCESSING(SSobj, src) + return ..() + +/obj/effect/map_effect/radiation_emitter/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/effect/map_effect/radiation_emitter/process() + SSradiation.radiate(src, radiation_power) + /obj/effect/map_effect/radiation_emitter/strong radiation_power = 100 \ No newline at end of file diff --git a/code/game/objects/effects/map_effects/screen_shaker.dm b/code/game/objects/effects/map_effects/screen_shaker.dm index c8f5d413bd8..15a83b4c3d6 100644 --- a/code/game/objects/effects/map_effects/screen_shaker.dm +++ b/code/game/objects/effects/map_effects/screen_shaker.dm @@ -1,17 +1,17 @@ -// Makes the screen shake for nearby players every so often. -/obj/effect/map_effect/interval/screen_shaker - name = "screen shaker" - icon_state = "screen_shaker" - - interval_lower_bound = 1 SECOND - interval_upper_bound = 2 SECONDS - - var/shake_radius = 7 // How far the shaking effect extends to. By default it is one screen length. - var/shake_duration = 2 // How long the shaking lasts. - var/shake_strength = 1 // How much it shakes. - -/obj/effect/map_effect/interval/screen_shaker/trigger() - for(var/mob/M as anything in player_list) - if(M.z == src.z && get_dist(src, M) <= shake_radius) - shake_camera(M, shake_duration, shake_strength) +// Makes the screen shake for nearby players every so often. +/obj/effect/map_effect/interval/screen_shaker + name = "screen shaker" + icon_state = "screen_shaker" + + interval_lower_bound = 1 SECOND + interval_upper_bound = 2 SECONDS + + var/shake_radius = 7 // How far the shaking effect extends to. By default it is one screen length. + var/shake_duration = 2 // How long the shaking lasts. + var/shake_strength = 1 // How much it shakes. + +/obj/effect/map_effect/interval/screen_shaker/trigger() + for(var/mob/M as anything in player_list) + if(M.z == src.z && get_dist(src, M) <= shake_radius) + shake_camera(M, shake_duration, shake_strength) ..() \ No newline at end of file diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm index b464530da77..f6051c03183 100644 --- a/code/game/objects/effects/mines.dm +++ b/code/game/objects/effects/mines.dm @@ -1,404 +1,404 @@ -/obj/effect/mine - name = "land mine" //The name and description are deliberately NOT modified, so you can't game the mines you find. - desc = "A small explosive land mine." - density = FALSE - anchored = TRUE - icon = 'icons/obj/weapons.dmi' - icon_state = "landmine" - var/triggered = 0 - var/smoke_strength = 3 - var/obj/item/weapon/mine/mineitemtype = /obj/item/weapon/mine - var/panel_open = FALSE - var/datum/wires/mines/wires = null - var/camo_net = FALSE // Will the mine 'cloak' on deployment? - - // The trap item will be triggered in some manner when detonating. Default only checks for grenades. - var/obj/item/trap = null - -/obj/effect/mine/Initialize() - icon_state = "landmine_armed" - wires = new(src) - . = ..() - if(ispath(trap)) - trap = new trap(src) - register_dangerous_to_step() - if(camo_net) - alpha = 50 - -/obj/effect/mine/Destroy() - unregister_dangerous_to_step() - if(trap) - QDEL_NULL(trap) - qdel_null(wires) - return ..() - -/obj/effect/mine/Moved(atom/oldloc) - . = ..() - if(.) - var/turf/old_turf = get_turf(oldloc) - var/turf/new_turf = get_turf(src) - if(old_turf != new_turf) - old_turf.unregister_dangerous_object(src) - new_turf.register_dangerous_object(src) - -/obj/effect/mine/proc/explode(var/mob/living/M) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - triggered = 1 - s.set_up(3, 1, src) - s.start() - - if(trap) - trigger_trap(M) - visible_message("\The [src.name] flashes as it is triggered!") - - else - explosion(loc, 0, 2, 3, 4) //land mines are dangerous, folks. - visible_message("\The [src.name] detonates!") - - qdel(s) - qdel(src) - -/obj/effect/mine/proc/trigger_trap(var/mob/living/victim) - if(istype(trap, /obj/item/weapon/grenade)) - var/obj/item/weapon/grenade/G = trap - trap = null - G.forceMove(get_turf(src)) - if(victim.ckey) - msg_admin_attack("[key_name_admin(victim)] stepped on \a [src.name], triggering [trap]") - G.activate() - - if(istype(trap, /obj/item/device/transfer_valve)) - var/obj/item/device/transfer_valve/TV = trap - trap = null - TV.forceMove(get_turf(src)) - TV.toggle_valve() - -/obj/effect/mine/bullet_act() - if(prob(50)) - explode() - -/obj/effect/mine/ex_act(severity) - if(severity <= 2 || prob(50)) - explode() - ..() - -/obj/effect/mine/Crossed(atom/movable/AM as mob|obj) - if(AM.is_incorporeal()) - return - Bumped(AM) - -/obj/effect/mine/Bumped(mob/M as mob|obj) - - if(triggered) - return - - if(istype(M, /obj/mecha)) - explode(M) - - if(istype(M, /mob/living/)) - var/mob/living/mob = M - if(!mob.hovering || !mob.flying) - explode(M) - -/obj/effect/mine/attackby(obj/item/W as obj, mob/living/user as mob) - if(W.has_tool_quality(TOOL_SCREWDRIVER)) - panel_open = !panel_open - user.visible_message("[user] very carefully screws the mine's panel [panel_open ? "open" : "closed"].", - "You very carefully screw the mine's panel [panel_open ? "open" : "closed"].") - playsound(src, W.usesound, 50, 1) - - // Panel open, stay uncloaked, or uncloak if already cloaked. If you don't cloak on place, ignore it and just be normal alpha. - alpha = camo_net ? (panel_open ? 255 : 50) : 255 - - else if((W.has_tool_quality(TOOL_WIRECUTTER) || istype(W, /obj/item/device/multitool)) && panel_open) - interact(user) - else - ..() - -/obj/effect/mine/interact(mob/living/user as mob) - if(!panel_open || istype(user, /mob/living/silicon/ai)) - return - user.set_machine(src) - wires.Interact(user) - -/obj/effect/mine/camo - camo_net = TRUE - -/obj/effect/mine/dnascramble - mineitemtype = /obj/item/weapon/mine/dnascramble - -/obj/effect/mine/dnascramble/explode(var/mob/living/M) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - triggered = 1 - s.set_up(3, 1, src) - s.start() - if(istype(M)) - M.radiation += 50 - randmutb(M) - domutcheck(M,null) - visible_message("\The [src.name] flashes violently before disintegrating!") - spawn(0) - qdel(s) - qdel(src) - -/obj/effect/mine/stun - mineitemtype = /obj/item/weapon/mine/stun - -/obj/effect/mine/stun/explode(var/mob/living/M) - triggered = 1 - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - s.set_up(3, 1, src) - s.start() - if(istype(M)) - M.Stun(30) - visible_message("\The [src.name] flashes violently before disintegrating!") - spawn(0) - qdel(s) - qdel(src) - -/obj/effect/mine/n2o - mineitemtype = /obj/item/weapon/mine/n2o - -/obj/effect/mine/n2o/explode(var/mob/living/M) - triggered = 1 - for (var/turf/simulated/floor/target in range(1,src)) - if(!target.blocks_air) - target.assume_gas("nitrous_oxide", 30) - visible_message("\The [src.name] detonates!") - spawn(0) - qdel(src) - -/obj/effect/mine/phoron - mineitemtype = /obj/item/weapon/mine/phoron - -/obj/effect/mine/phoron/explode(var/mob/living/M) - triggered = 1 - for (var/turf/simulated/floor/target in range(1,src)) - if(!target.blocks_air) - target.assume_gas("phoron", 30) - target.hotspot_expose(1000, CELL_VOLUME) - visible_message("\The [src.name] detonates!") - spawn(0) - qdel(src) - -/obj/effect/mine/kick - mineitemtype = /obj/item/weapon/mine/kick - -/obj/effect/mine/kick/explode(var/mob/living/M) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - triggered = 1 - s.set_up(3, 1, src) - s.start() - if(istype(M, /obj/mecha)) - var/obj/mecha/E = M - M = E.occupant - if(istype(M)) - qdel(M.client) - spawn(0) - qdel(s) - qdel(src) - -/obj/effect/mine/frag - mineitemtype = /obj/item/weapon/mine/frag - var/fragment_types = list(/obj/item/projectile/bullet/pellet/fragment) - var/num_fragments = 20 //total number of fragments produced by the grenade - //The radius of the circle used to launch projectiles. Lower values mean less projectiles are used but if set too low gaps may appear in the spread pattern - var/spread_range = 7 - -/obj/effect/mine/frag/explode(var/mob/living/M) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - triggered = 1 - s.set_up(3, 1, src) - s.start() - var/turf/O = get_turf(src) - if(!O) - return - src.fragmentate(O, 20, 7, list(/obj/item/projectile/bullet/pellet/fragment)) //only 20 weak fragments because you're stepping directly on it - visible_message("\The [src.name] detonates!") - spawn(0) - qdel(s) - qdel(src) - -/obj/effect/mine/training //Name and Desc commented out so it's possible to trick people with the training mines -// name = "training mine" -// desc = "A mine with its payload removed, for EOD training and demonstrations." - mineitemtype = /obj/item/weapon/mine/training - -/obj/effect/mine/training/explode(var/mob/living/M) - triggered = 1 - visible_message("\The [src.name]'s light flashes rapidly as it 'explodes'.") - new src.mineitemtype(get_turf(src)) - spawn(0) - qdel(src) - -/obj/effect/mine/emp - mineitemtype = /obj/item/weapon/mine/emp - -/obj/effect/mine/emp/explode(var/mob/living/M) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - s.set_up(3, 1, src) - s.start() - visible_message("\The [src.name] flashes violently before disintegrating!") - empulse(loc, 2, 4, 7, 10, 1) // As strong as an EMP grenade - spawn(0) - qdel(src) - -/obj/effect/mine/emp/camo - camo_net = TRUE - -/obj/effect/mine/incendiary - mineitemtype = /obj/item/weapon/mine/incendiary - -/obj/effect/mine/incendiary/explode(var/mob/living/M) - triggered = 1 - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - s.set_up(3, 1, src) - s.start() - if(istype(M)) - M.adjust_fire_stacks(5) - M.fire_act() - visible_message("\The [src.name] bursts into flames!") - spawn(0) - qdel(src) - -/obj/effect/mine/gadget - mineitemtype = /obj/item/weapon/mine/gadget - -/obj/effect/mine/gadget/explode(var/mob/living/M) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - triggered = 1 - s.set_up(3, 1, src) - s.start() - - if(trap) - trigger_trap(M) - visible_message("\The [src.name] flashes as it is triggered!") - - else - explosion(loc, 0, 0, 2, 2) - visible_message("\The [src.name] detonates!") - - qdel(s) - qdel(src) - -///////////////////////////////////////////// -// The held item version of the above mines -///////////////////////////////////////////// -/obj/item/weapon/mine - name = "mine" - desc = "A small explosive mine with 'HE' and a grenade symbol on the side." - icon = 'icons/obj/weapons.dmi' - icon_state = "uglymine" - var/countdown = 10 - var/minetype = /obj/effect/mine //This MUST be an /obj/effect/mine type, or it'll runtime. - - var/obj/item/trap = null - - var/list/allowed_gadgets = null - -/obj/item/weapon/mine/attack_self(mob/user as mob) // You do not want to move or throw a land mine while priming it... Explosives + Sudden Movement = Bad Times - add_fingerprint(user) - msg_admin_attack("[key_name_admin(user)] primed \a [src]") - user.visible_message("[user] starts priming \the [src.name].", "You start priming \the [src.name]. Hold still!") - if(do_after(user, 10 SECONDS)) - playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) - prime(user) - else - visible_message("[user] triggers \the [src.name]!", "You accidentally trigger \the [src.name]!") - prime(user, TRUE) - return - -/obj/item/weapon/mine/attackby(obj/item/W as obj, mob/living/user as mob) - if(W.has_tool_quality(TOOL_SCREWDRIVER) && trap) - to_chat(user, "You begin removing \the [trap].") - if(do_after(user, 10 SECONDS)) - to_chat(user, "You finish disconnecting the mine's trigger.") - trap.forceMove(get_turf(src)) - trap = null - return - - if(LAZYLEN(allowed_gadgets) && !trap) - var/allowed = FALSE - - for(var/path in allowed_gadgets) - if(istype(W, path)) - allowed = TRUE - break - - if(allowed) - user.drop_from_inventory(W) - W.forceMove(src) - trap = W - - ..() - -/obj/item/weapon/mine/proc/prime(mob/user as mob, var/explode_now = FALSE) - visible_message("\The [src.name] beeps as the priming sequence completes.") - var/obj/effect/mine/R = new minetype(get_turf(src)) - src.transfer_fingerprints_to(R) - R.add_fingerprint(user) - if(trap) - R.trap = trap - trap = null - R.trap.forceMove(R) - if(explode_now) - R.explode(user) - spawn(0) - qdel(src) - -/obj/item/weapon/mine/dnascramble - name = "radiation mine" - desc = "A small explosive mine with a radiation symbol on the side." - minetype = /obj/effect/mine/dnascramble - -/obj/item/weapon/mine/phoron - name = "incendiary mine" - desc = "A small explosive mine with a fire symbol on the side." - minetype = /obj/effect/mine/phoron - -/obj/item/weapon/mine/kick - name = "kick mine" - desc = "Concentrated war crimes. Handle with care." - minetype = /obj/effect/mine/kick - -/obj/item/weapon/mine/n2o - name = "nitrous oxide mine" - desc = "A small explosive mine with three Z's on the side." - minetype = /obj/effect/mine/n2o - -/obj/item/weapon/mine/stun - name = "stun mine" - desc = "A small explosive mine with a lightning bolt symbol on the side." - minetype = /obj/effect/mine/stun - -/obj/item/weapon/mine/frag - name = "fragmentation mine" - desc = "A small explosive mine with 'FRAG' and a grenade symbol on the side." - minetype = /obj/effect/mine/frag - -/obj/item/weapon/mine/training - name = "training mine" - desc = "A mine with its payload removed, for EOD training and demonstrations." - minetype = /obj/effect/mine/training - -/obj/item/weapon/mine/emp - name = "emp mine" - desc = "A small explosive mine with a lightning bolt symbol on the side." - minetype = /obj/effect/mine/emp - -/obj/item/weapon/mine/incendiary - name = "incendiary mine" - desc = "A small explosive mine with a fire symbol on the side." - minetype = /obj/effect/mine/incendiary - -/obj/item/weapon/mine/gadget - name = "gadget mine" - desc = "A small pressure-triggered device. If no component is added, the internal release bolts will detonate in unison when triggered." - - allowed_gadgets = list(/obj/item/weapon/grenade, /obj/item/device/transfer_valve) - -// This tells AI mobs to not be dumb and step on mines willingly. -/obj/item/weapon/mine/is_safe_to_step(mob/living/L) - if(!L.hovering || !L.flying) - return FALSE - return ..() +/obj/effect/mine + name = "land mine" //The name and description are deliberately NOT modified, so you can't game the mines you find. + desc = "A small explosive land mine." + density = FALSE + anchored = TRUE + icon = 'icons/obj/weapons.dmi' + icon_state = "landmine" + var/triggered = 0 + var/smoke_strength = 3 + var/obj/item/weapon/mine/mineitemtype = /obj/item/weapon/mine + var/panel_open = FALSE + var/datum/wires/mines/wires = null + var/camo_net = FALSE // Will the mine 'cloak' on deployment? + + // The trap item will be triggered in some manner when detonating. Default only checks for grenades. + var/obj/item/trap = null + +/obj/effect/mine/Initialize() + icon_state = "landmine_armed" + wires = new(src) + . = ..() + if(ispath(trap)) + trap = new trap(src) + register_dangerous_to_step() + if(camo_net) + alpha = 50 + +/obj/effect/mine/Destroy() + unregister_dangerous_to_step() + if(trap) + QDEL_NULL(trap) + qdel_null(wires) + return ..() + +/obj/effect/mine/Moved(atom/oldloc) + . = ..() + if(.) + var/turf/old_turf = get_turf(oldloc) + var/turf/new_turf = get_turf(src) + if(old_turf != new_turf) + old_turf.unregister_dangerous_object(src) + new_turf.register_dangerous_object(src) + +/obj/effect/mine/proc/explode(var/mob/living/M) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + triggered = 1 + s.set_up(3, 1, src) + s.start() + + if(trap) + trigger_trap(M) + visible_message("\The [src.name] flashes as it is triggered!") + + else + explosion(loc, 0, 2, 3, 4) //land mines are dangerous, folks. + visible_message("\The [src.name] detonates!") + + qdel(s) + qdel(src) + +/obj/effect/mine/proc/trigger_trap(var/mob/living/victim) + if(istype(trap, /obj/item/weapon/grenade)) + var/obj/item/weapon/grenade/G = trap + trap = null + G.forceMove(get_turf(src)) + if(victim.ckey) + msg_admin_attack("[key_name_admin(victim)] stepped on \a [src.name], triggering [trap]") + G.activate() + + if(istype(trap, /obj/item/device/transfer_valve)) + var/obj/item/device/transfer_valve/TV = trap + trap = null + TV.forceMove(get_turf(src)) + TV.toggle_valve() + +/obj/effect/mine/bullet_act() + if(prob(50)) + explode() + +/obj/effect/mine/ex_act(severity) + if(severity <= 2 || prob(50)) + explode() + ..() + +/obj/effect/mine/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return + Bumped(AM) + +/obj/effect/mine/Bumped(mob/M as mob|obj) + + if(triggered) + return + + if(istype(M, /obj/mecha)) + explode(M) + + if(istype(M, /mob/living/)) + var/mob/living/mob = M + if(!mob.hovering || !mob.flying) + explode(M) + +/obj/effect/mine/attackby(obj/item/W as obj, mob/living/user as mob) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + panel_open = !panel_open + user.visible_message("[user] very carefully screws the mine's panel [panel_open ? "open" : "closed"].", + "You very carefully screw the mine's panel [panel_open ? "open" : "closed"].") + playsound(src, W.usesound, 50, 1) + + // Panel open, stay uncloaked, or uncloak if already cloaked. If you don't cloak on place, ignore it and just be normal alpha. + alpha = camo_net ? (panel_open ? 255 : 50) : 255 + + else if((W.has_tool_quality(TOOL_WIRECUTTER) || istype(W, /obj/item/device/multitool)) && panel_open) + interact(user) + else + ..() + +/obj/effect/mine/interact(mob/living/user as mob) + if(!panel_open || istype(user, /mob/living/silicon/ai)) + return + user.set_machine(src) + wires.Interact(user) + +/obj/effect/mine/camo + camo_net = TRUE + +/obj/effect/mine/dnascramble + mineitemtype = /obj/item/weapon/mine/dnascramble + +/obj/effect/mine/dnascramble/explode(var/mob/living/M) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + triggered = 1 + s.set_up(3, 1, src) + s.start() + if(istype(M)) + M.radiation += 50 + randmutb(M) + domutcheck(M,null) + visible_message("\The [src.name] flashes violently before disintegrating!") + spawn(0) + qdel(s) + qdel(src) + +/obj/effect/mine/stun + mineitemtype = /obj/item/weapon/mine/stun + +/obj/effect/mine/stun/explode(var/mob/living/M) + triggered = 1 + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + s.set_up(3, 1, src) + s.start() + if(istype(M)) + M.Stun(30) + visible_message("\The [src.name] flashes violently before disintegrating!") + spawn(0) + qdel(s) + qdel(src) + +/obj/effect/mine/n2o + mineitemtype = /obj/item/weapon/mine/n2o + +/obj/effect/mine/n2o/explode(var/mob/living/M) + triggered = 1 + for (var/turf/simulated/floor/target in range(1,src)) + if(!target.blocks_air) + target.assume_gas("nitrous_oxide", 30) + visible_message("\The [src.name] detonates!") + spawn(0) + qdel(src) + +/obj/effect/mine/phoron + mineitemtype = /obj/item/weapon/mine/phoron + +/obj/effect/mine/phoron/explode(var/mob/living/M) + triggered = 1 + for (var/turf/simulated/floor/target in range(1,src)) + if(!target.blocks_air) + target.assume_gas("phoron", 30) + target.hotspot_expose(1000, CELL_VOLUME) + visible_message("\The [src.name] detonates!") + spawn(0) + qdel(src) + +/obj/effect/mine/kick + mineitemtype = /obj/item/weapon/mine/kick + +/obj/effect/mine/kick/explode(var/mob/living/M) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + triggered = 1 + s.set_up(3, 1, src) + s.start() + if(istype(M, /obj/mecha)) + var/obj/mecha/E = M + M = E.occupant + if(istype(M)) + qdel(M.client) + spawn(0) + qdel(s) + qdel(src) + +/obj/effect/mine/frag + mineitemtype = /obj/item/weapon/mine/frag + var/fragment_types = list(/obj/item/projectile/bullet/pellet/fragment) + var/num_fragments = 20 //total number of fragments produced by the grenade + //The radius of the circle used to launch projectiles. Lower values mean less projectiles are used but if set too low gaps may appear in the spread pattern + var/spread_range = 7 + +/obj/effect/mine/frag/explode(var/mob/living/M) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + triggered = 1 + s.set_up(3, 1, src) + s.start() + var/turf/O = get_turf(src) + if(!O) + return + src.fragmentate(O, 20, 7, list(/obj/item/projectile/bullet/pellet/fragment)) //only 20 weak fragments because you're stepping directly on it + visible_message("\The [src.name] detonates!") + spawn(0) + qdel(s) + qdel(src) + +/obj/effect/mine/training //Name and Desc commented out so it's possible to trick people with the training mines +// name = "training mine" +// desc = "A mine with its payload removed, for EOD training and demonstrations." + mineitemtype = /obj/item/weapon/mine/training + +/obj/effect/mine/training/explode(var/mob/living/M) + triggered = 1 + visible_message("\The [src.name]'s light flashes rapidly as it 'explodes'.") + new src.mineitemtype(get_turf(src)) + spawn(0) + qdel(src) + +/obj/effect/mine/emp + mineitemtype = /obj/item/weapon/mine/emp + +/obj/effect/mine/emp/explode(var/mob/living/M) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + s.set_up(3, 1, src) + s.start() + visible_message("\The [src.name] flashes violently before disintegrating!") + empulse(loc, 2, 4, 7, 10, 1) // As strong as an EMP grenade + spawn(0) + qdel(src) + +/obj/effect/mine/emp/camo + camo_net = TRUE + +/obj/effect/mine/incendiary + mineitemtype = /obj/item/weapon/mine/incendiary + +/obj/effect/mine/incendiary/explode(var/mob/living/M) + triggered = 1 + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + s.set_up(3, 1, src) + s.start() + if(istype(M)) + M.adjust_fire_stacks(5) + M.fire_act() + visible_message("\The [src.name] bursts into flames!") + spawn(0) + qdel(src) + +/obj/effect/mine/gadget + mineitemtype = /obj/item/weapon/mine/gadget + +/obj/effect/mine/gadget/explode(var/mob/living/M) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + triggered = 1 + s.set_up(3, 1, src) + s.start() + + if(trap) + trigger_trap(M) + visible_message("\The [src.name] flashes as it is triggered!") + + else + explosion(loc, 0, 0, 2, 2) + visible_message("\The [src.name] detonates!") + + qdel(s) + qdel(src) + +///////////////////////////////////////////// +// The held item version of the above mines +///////////////////////////////////////////// +/obj/item/weapon/mine + name = "mine" + desc = "A small explosive mine with 'HE' and a grenade symbol on the side." + icon = 'icons/obj/weapons.dmi' + icon_state = "uglymine" + var/countdown = 10 + var/minetype = /obj/effect/mine //This MUST be an /obj/effect/mine type, or it'll runtime. + + var/obj/item/trap = null + + var/list/allowed_gadgets = null + +/obj/item/weapon/mine/attack_self(mob/user as mob) // You do not want to move or throw a land mine while priming it... Explosives + Sudden Movement = Bad Times + add_fingerprint(user) + msg_admin_attack("[key_name_admin(user)] primed \a [src]") + user.visible_message("[user] starts priming \the [src.name].", "You start priming \the [src.name]. Hold still!") + if(do_after(user, 10 SECONDS)) + playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) + prime(user) + else + visible_message("[user] triggers \the [src.name]!", "You accidentally trigger \the [src.name]!") + prime(user, TRUE) + return + +/obj/item/weapon/mine/attackby(obj/item/W as obj, mob/living/user as mob) + if(W.has_tool_quality(TOOL_SCREWDRIVER) && trap) + to_chat(user, "You begin removing \the [trap].") + if(do_after(user, 10 SECONDS)) + to_chat(user, "You finish disconnecting the mine's trigger.") + trap.forceMove(get_turf(src)) + trap = null + return + + if(LAZYLEN(allowed_gadgets) && !trap) + var/allowed = FALSE + + for(var/path in allowed_gadgets) + if(istype(W, path)) + allowed = TRUE + break + + if(allowed) + user.drop_from_inventory(W) + W.forceMove(src) + trap = W + + ..() + +/obj/item/weapon/mine/proc/prime(mob/user as mob, var/explode_now = FALSE) + visible_message("\The [src.name] beeps as the priming sequence completes.") + var/obj/effect/mine/R = new minetype(get_turf(src)) + src.transfer_fingerprints_to(R) + R.add_fingerprint(user) + if(trap) + R.trap = trap + trap = null + R.trap.forceMove(R) + if(explode_now) + R.explode(user) + spawn(0) + qdel(src) + +/obj/item/weapon/mine/dnascramble + name = "radiation mine" + desc = "A small explosive mine with a radiation symbol on the side." + minetype = /obj/effect/mine/dnascramble + +/obj/item/weapon/mine/phoron + name = "incendiary mine" + desc = "A small explosive mine with a fire symbol on the side." + minetype = /obj/effect/mine/phoron + +/obj/item/weapon/mine/kick + name = "kick mine" + desc = "Concentrated war crimes. Handle with care." + minetype = /obj/effect/mine/kick + +/obj/item/weapon/mine/n2o + name = "nitrous oxide mine" + desc = "A small explosive mine with three Z's on the side." + minetype = /obj/effect/mine/n2o + +/obj/item/weapon/mine/stun + name = "stun mine" + desc = "A small explosive mine with a lightning bolt symbol on the side." + minetype = /obj/effect/mine/stun + +/obj/item/weapon/mine/frag + name = "fragmentation mine" + desc = "A small explosive mine with 'FRAG' and a grenade symbol on the side." + minetype = /obj/effect/mine/frag + +/obj/item/weapon/mine/training + name = "training mine" + desc = "A mine with its payload removed, for EOD training and demonstrations." + minetype = /obj/effect/mine/training + +/obj/item/weapon/mine/emp + name = "emp mine" + desc = "A small explosive mine with a lightning bolt symbol on the side." + minetype = /obj/effect/mine/emp + +/obj/item/weapon/mine/incendiary + name = "incendiary mine" + desc = "A small explosive mine with a fire symbol on the side." + minetype = /obj/effect/mine/incendiary + +/obj/item/weapon/mine/gadget + name = "gadget mine" + desc = "A small pressure-triggered device. If no component is added, the internal release bolts will detonate in unison when triggered." + + allowed_gadgets = list(/obj/item/weapon/grenade, /obj/item/device/transfer_valve) + +// This tells AI mobs to not be dumb and step on mines willingly. +/obj/item/weapon/mine/is_safe_to_step(mob/living/L) + if(!L.hovering || !L.flying) + return FALSE + return ..() diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm index cd517098dc3..d51d769d568 100644 --- a/code/game/objects/effects/misc.dm +++ b/code/game/objects/effects/misc.dm @@ -1,152 +1,152 @@ -//The effect when you wrap a dead body in gift wrap -/obj/effect/spresent - name = "strange present" - desc = "It's a ... present?" - icon = 'icons/obj/items.dmi' - icon_state = "strangepresent" - density = TRUE - anchored = FALSE - -/obj/effect/temporary_effect - name = "self deleting effect" - desc = "How are you examining what which cannot be seen?" - icon = 'icons/effects/effects.dmi' - invisibility = 0 - var/time_to_die = 10 SECONDS // Afer which, it will delete itself. - -/obj/effect/temporary_effect/Initialize() - . = ..() - if(time_to_die) - QDEL_IN(src, time_to_die) - -// Shown really briefly when attacking with axes. -/obj/effect/temporary_effect/cleave_attack - name = "cleaving attack" - desc = "Something swinging really wide." - icon = 'icons/effects/96x96.dmi' - icon_state = "cleave" - plane = ABOVE_MOB_PLANE - layer = ABOVE_MOB_LAYER - time_to_die = 6 - alpha = 140 - mouse_opacity = 0 - pixel_x = -32 - pixel_y = -32 - -/obj/effect/temporary_effect/cleave_attack/Initialize() // Makes the slash fade smoothly. When completely transparent it should qdel itself. - . = ..() - animate(src, alpha = 0, time = time_to_die - 1) - -/obj/effect/temporary_effect/shuttle_landing - name = "shuttle landing" - desc = "You better move if you don't want to go splat!" - //VOREStation Edit Start - icon = 'icons/goonstation/featherzone.dmi' - icon_state = "hazard-corners" - time_to_die = 5 SECONDS - plane = PLANE_LIGHTING_ABOVE - //VOREStation Edit End - -// The manifestation of Zeus's might. Or just a really unlucky day. -// This is purely a visual effect, this isn't the part of the code that hurts things. -/obj/effect/temporary_effect/lightning_strike - name = "lightning" - desc = "How shocked you must be, to see this text. You must have lightning reflexes. \ - The humor in this description is just so electrifying." - icon = 'icons/effects/96x256.dmi' - icon_state = "lightning_strike" - plane = PLANE_LIGHTING_ABOVE - time_to_die = 1 SECOND - pixel_x = -32 - -/obj/effect/temporary_effect/lightning_strike/Initialize() - icon_state += "[rand(1,2)]" // To have two variants of lightning sprites. - animate(src, alpha = 0, time = time_to_die - 1) - . = ..() - -/obj/effect/dummy/lighting_obj - name = "lighting fx obj" - desc = "Tell a coder if you're seeing this." - icon_state = "nothing" - light_system = MOVABLE_LIGHT - light_range = MINIMUM_USEFUL_LIGHT_RANGE - light_color = COLOR_WHITE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - light_on = TRUE - blocks_emissive = FALSE - -/obj/effect/dummy/lighting_obj/Initialize(mapload, _range, _power, _color, _duration) - . = ..() - if(!isnull(_range)) - set_light_range(_range) - if(!isnull(_power)) - set_light_power(_power) - if(!isnull(_color)) - set_light_color(_color) - if(_duration) - QDEL_IN(src, _duration) - -/obj/effect/dummy/lighting_obj/moblight - name = "mob lighting fx" - -/obj/effect/dummy/lighting_obj/moblight/Initialize(mapload, _color, _range, _power, _duration) - . = ..() - if(!ismob(loc)) - return INITIALIZE_HINT_QDEL - -/obj/effect/dummy/lighting_obj/moblight/fire - name = "fire" - light_color = LIGHT_COLOR_FIRE - light_range = LIGHT_RANGE_FIRE - -/obj/effect/abstract - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - plane = ABOVE_MOB_PLANE - -/obj/effect/abstract/light_spot - icon = 'icons/effects/eris_flashlight.dmi' - icon_state = "medium" - pixel_x = -16 - pixel_y = -16 - -/obj/effect/abstract/directional_lighting - var/obj/effect/abstract/light_spot/light_spot = new - var/trans_angle - var/icon_dist - -/obj/effect/abstract/directional_lighting/Initialize() - . = ..() - vis_contents += light_spot - -/obj/effect/abstract/directional_lighting/proc/face_light(atom/movable/source, angle, distance) - if(!loc) // We're in nullspace - return - - // Save ourselves some matrix math - if(angle != trans_angle) - trans_angle = angle - // Doing this in one operation (tn = turn(initial(tn), angle)) has strange results... - light_spot.transform = initial(light_spot.transform) - light_spot.transform = turn(light_spot.transform, angle) - - if(icon_dist != distance) - icon_dist = distance - switch(distance) - if(0) - light_spot.icon_state = "vclose" - if(1) - light_spot.icon_state = "close" - if(2) - light_spot.icon_state = "medium" - if(3 to INFINITY) - light_spot.icon_state = "far" - -/obj/effect/abstract/directional_lighting/Destroy(force) - if(!force) - stack_trace("Directional light atom deleted, but not by our component") - return QDEL_HINT_LETMELIVE - - vis_contents.Cut() - qdel_null(light_spot) - - return ..() +//The effect when you wrap a dead body in gift wrap +/obj/effect/spresent + name = "strange present" + desc = "It's a ... present?" + icon = 'icons/obj/items.dmi' + icon_state = "strangepresent" + density = TRUE + anchored = FALSE + +/obj/effect/temporary_effect + name = "self deleting effect" + desc = "How are you examining what which cannot be seen?" + icon = 'icons/effects/effects.dmi' + invisibility = 0 + var/time_to_die = 10 SECONDS // Afer which, it will delete itself. + +/obj/effect/temporary_effect/Initialize() + . = ..() + if(time_to_die) + QDEL_IN(src, time_to_die) + +// Shown really briefly when attacking with axes. +/obj/effect/temporary_effect/cleave_attack + name = "cleaving attack" + desc = "Something swinging really wide." + icon = 'icons/effects/96x96.dmi' + icon_state = "cleave" + plane = ABOVE_MOB_PLANE + layer = ABOVE_MOB_LAYER + time_to_die = 6 + alpha = 140 + mouse_opacity = 0 + pixel_x = -32 + pixel_y = -32 + +/obj/effect/temporary_effect/cleave_attack/Initialize() // Makes the slash fade smoothly. When completely transparent it should qdel itself. + . = ..() + animate(src, alpha = 0, time = time_to_die - 1) + +/obj/effect/temporary_effect/shuttle_landing + name = "shuttle landing" + desc = "You better move if you don't want to go splat!" + //VOREStation Edit Start + icon = 'icons/goonstation/featherzone.dmi' + icon_state = "hazard-corners" + time_to_die = 5 SECONDS + plane = PLANE_LIGHTING_ABOVE + //VOREStation Edit End + +// The manifestation of Zeus's might. Or just a really unlucky day. +// This is purely a visual effect, this isn't the part of the code that hurts things. +/obj/effect/temporary_effect/lightning_strike + name = "lightning" + desc = "How shocked you must be, to see this text. You must have lightning reflexes. \ + The humor in this description is just so electrifying." + icon = 'icons/effects/96x256.dmi' + icon_state = "lightning_strike" + plane = PLANE_LIGHTING_ABOVE + time_to_die = 1 SECOND + pixel_x = -32 + +/obj/effect/temporary_effect/lightning_strike/Initialize() + icon_state += "[rand(1,2)]" // To have two variants of lightning sprites. + animate(src, alpha = 0, time = time_to_die - 1) + . = ..() + +/obj/effect/dummy/lighting_obj + name = "lighting fx obj" + desc = "Tell a coder if you're seeing this." + icon_state = "nothing" + light_system = MOVABLE_LIGHT + light_range = MINIMUM_USEFUL_LIGHT_RANGE + light_color = COLOR_WHITE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + light_on = TRUE + blocks_emissive = FALSE + +/obj/effect/dummy/lighting_obj/Initialize(mapload, _range, _power, _color, _duration) + . = ..() + if(!isnull(_range)) + set_light_range(_range) + if(!isnull(_power)) + set_light_power(_power) + if(!isnull(_color)) + set_light_color(_color) + if(_duration) + QDEL_IN(src, _duration) + +/obj/effect/dummy/lighting_obj/moblight + name = "mob lighting fx" + +/obj/effect/dummy/lighting_obj/moblight/Initialize(mapload, _color, _range, _power, _duration) + . = ..() + if(!ismob(loc)) + return INITIALIZE_HINT_QDEL + +/obj/effect/dummy/lighting_obj/moblight/fire + name = "fire" + light_color = LIGHT_COLOR_FIRE + light_range = LIGHT_RANGE_FIRE + +/obj/effect/abstract + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + plane = ABOVE_MOB_PLANE + +/obj/effect/abstract/light_spot + icon = 'icons/effects/eris_flashlight.dmi' + icon_state = "medium" + pixel_x = -16 + pixel_y = -16 + +/obj/effect/abstract/directional_lighting + var/obj/effect/abstract/light_spot/light_spot = new + var/trans_angle + var/icon_dist + +/obj/effect/abstract/directional_lighting/Initialize() + . = ..() + vis_contents += light_spot + +/obj/effect/abstract/directional_lighting/proc/face_light(atom/movable/source, angle, distance) + if(!loc) // We're in nullspace + return + + // Save ourselves some matrix math + if(angle != trans_angle) + trans_angle = angle + // Doing this in one operation (tn = turn(initial(tn), angle)) has strange results... + light_spot.transform = initial(light_spot.transform) + light_spot.transform = turn(light_spot.transform, angle) + + if(icon_dist != distance) + icon_dist = distance + switch(distance) + if(0) + light_spot.icon_state = "vclose" + if(1) + light_spot.icon_state = "close" + if(2) + light_spot.icon_state = "medium" + if(3 to INFINITY) + light_spot.icon_state = "far" + +/obj/effect/abstract/directional_lighting/Destroy(force) + if(!force) + stack_trace("Directional light atom deleted, but not by our component") + return QDEL_HINT_LETMELIVE + + vis_contents.Cut() + qdel_null(light_spot) + + return ..() diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm index 11e722d744b..a3f4e6a2933 100644 --- a/code/game/objects/effects/overlays.dm +++ b/code/game/objects/effects/overlays.dm @@ -1,193 +1,193 @@ -/obj/effect/overlay - name = "overlay" - unacidable = TRUE - var/i_attached//Added for possible image attachments to objects. For hallucinations and the like. - -/obj/effect/overlay/beam//Not actually a projectile, just an effect. - name="beam" - icon='icons/effects/beam.dmi' - icon_state="b_beam" - plane = ABOVE_OBJ_PLANE - var/tmp/atom/BeamSource - -/obj/effect/overlay/beam/New() - ..() - spawn(10) qdel(src) - -/obj/effect/overlay/palmtree_r - name = "Palm tree" - icon = 'icons/misc/beach2.dmi' - icon_state = "palm1" - density = TRUE - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - anchored = TRUE - -/obj/effect/overlay/palmtree_l - name = "Palm tree" - icon = 'icons/misc/beach2.dmi' - icon_state = "palm2" - density = TRUE - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - anchored = TRUE - -/obj/effect/overlay/coconut - name = "Coconuts" - icon = 'icons/misc/beach.dmi' - icon_state = "coconuts" - -/obj/effect/overlay/bluespacify - name = "Bluespace" - icon = 'icons/turf/space_vr.dmi' //VOREStation Edit - icon_state = "bluespacify" - plane = ABOVE_PLANE - -/obj/effect/overlay/wallrot - name = "wallrot" - desc = "Ick..." - icon = 'icons/effects/wallrot.dmi' - anchored = TRUE - density = TRUE - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - mouse_opacity = 0 - -/obj/effect/overlay/wallrot/New() - ..() - pixel_x += rand(-10, 10) - pixel_y += rand(-10, 10) - -/obj/effect/overlay/snow - name = "snow" - icon = 'icons/turf/overlays.dmi' - icon_state = "snow" - anchored = TRUE - plane = TURF_PLANE - -// Todo: Add a version that gradually reaccumulates over time by means of alpha transparency. -Spades -/obj/effect/overlay/snow/attackby(obj/item/W as obj, mob/user as mob) - if (istype(W, /obj/item/weapon/shovel)) - user.visible_message("[user] begins to shovel away \the [src].") - if(do_after(user, 40)) - to_chat(user, "You have finished shoveling!") - qdel(src) - return - -/obj/effect/overlay/snow/floor - icon_state = "snowfloor" - plane = TURF_PLANE - layer = ABOVE_TURF_LAYER - mouse_opacity = 0 //Don't block underlying tile interactions -/* -/obj/effect/overlay/snow/floor/edges - icon_state = "snow_edges" -/obj/effect/overlay/snow/floor/edges2 - icon_state = "snow_edges2" -/obj/effect/overlay/snow/floor/edges3 - icon_state = "gravsnow_edges" -/obj/effect/overlay/snow/floor/surround - icon_state = "snow_surround" */ -/obj/effect/overlay/snow/airlock - icon_state = "snowairlock" - layer = DOOR_CLOSED_LAYER+0.01 - -/obj/effect/overlay/snow/floor/pointy - icon_state = "snowfloorpointy" - -/obj/effect/overlay/snow/wall - icon_state = "snowwall" - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - -/obj/effect/overlay/holographic - mouse_opacity = FALSE - anchored = TRUE - plane = ABOVE_PLANE - -// Similar to the tesla ball but doesn't actually do anything and is purely visual. -/obj/effect/overlay/energy_ball - name = "energy ball" - desc = "An energy ball." - icon = 'icons/obj/tesla_engine/energy_ball.dmi' - icon_state = "energy_ball" - plane = PLANE_LIGHTING_ABOVE - pixel_x = -32 - pixel_y = -32 - -/obj/effect/overlay/vis - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - anchored = TRUE - vis_flags = VIS_INHERIT_DIR - ///When detected to be unused it gets set to world.time, after a while it gets removed - var/unused = 0 - ///overlays which go unused for this amount of time get cleaned up - var/cache_expiration = 2 MINUTES - -/* -/obj/effect/overlay/atmos_excited - name = "excited group" - icon = null - icon_state = null - anchored = TRUE // should only appear in vis_contents, but to be safe - appearance_flags = RESET_TRANSFORM | TILE_BOUND - invisibility = INVISIBILITY_ABSTRACT - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - - plane = ATMOS_GROUP_PLANE -*/ - -/obj/effect/overlay/light_visible - name = "" - icon = 'icons/effects/light_overlays/light_32.dmi' - icon_state = "light" - plane = PLANE_O_LIGHTING_VISUAL - appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - alpha = 0 - vis_flags = NONE - blocks_emissive = FALSE - -/obj/effect/overlay/light_visible/Destroy(force) - if(!force) - stack_trace("Movable light visible mask deleted, but not by our component") - return QDEL_HINT_LETMELIVE - return ..() - -/obj/effect/overlay/light_cone - name = "" - icon = 'icons/effects/light_overlays/light_cone.dmi' - icon_state = "light" - plane = PLANE_O_LIGHTING_VISUAL - appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - vis_flags = NONE - alpha = 110 - blocks_emissive = FALSE - - var/static/matrix/normal_transform - -/obj/effect/overlay/light_cone/Initialize() - . = ..() - apply_standard_transform() - -/obj/effect/overlay/light_cone/proc/reset_transform(apply_standard) - transform = initial(transform) - if(apply_standard) - apply_standard_transform() - -/obj/effect/overlay/light_cone/proc/apply_standard_transform() - transform = transform.Translate(-32, -32) - -/obj/effect/overlay/light_cone/Destroy(force) - if(!force) - stack_trace("Directional light cone deleted, but not by our component") - return QDEL_HINT_LETMELIVE - return ..() - -/obj/effect/overlay/closet_door - anchored = TRUE - plane = FLOAT_PLANE - layer = FLOAT_LAYER - vis_flags = VIS_INHERIT_ID - appearance_flags = KEEP_TOGETHER | LONG_GLIDE | PIXEL_SCALE +/obj/effect/overlay + name = "overlay" + unacidable = TRUE + var/i_attached//Added for possible image attachments to objects. For hallucinations and the like. + +/obj/effect/overlay/beam//Not actually a projectile, just an effect. + name="beam" + icon='icons/effects/beam.dmi' + icon_state="b_beam" + plane = ABOVE_OBJ_PLANE + var/tmp/atom/BeamSource + +/obj/effect/overlay/beam/New() + ..() + spawn(10) qdel(src) + +/obj/effect/overlay/palmtree_r + name = "Palm tree" + icon = 'icons/misc/beach2.dmi' + icon_state = "palm1" + density = TRUE + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + anchored = TRUE + +/obj/effect/overlay/palmtree_l + name = "Palm tree" + icon = 'icons/misc/beach2.dmi' + icon_state = "palm2" + density = TRUE + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + anchored = TRUE + +/obj/effect/overlay/coconut + name = "Coconuts" + icon = 'icons/misc/beach.dmi' + icon_state = "coconuts" + +/obj/effect/overlay/bluespacify + name = "Bluespace" + icon = 'icons/turf/space_vr.dmi' //VOREStation Edit + icon_state = "bluespacify" + plane = ABOVE_PLANE + +/obj/effect/overlay/wallrot + name = "wallrot" + desc = "Ick..." + icon = 'icons/effects/wallrot.dmi' + anchored = TRUE + density = TRUE + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + mouse_opacity = 0 + +/obj/effect/overlay/wallrot/New() + ..() + pixel_x += rand(-10, 10) + pixel_y += rand(-10, 10) + +/obj/effect/overlay/snow + name = "snow" + icon = 'icons/turf/overlays.dmi' + icon_state = "snow" + anchored = TRUE + plane = TURF_PLANE + +// Todo: Add a version that gradually reaccumulates over time by means of alpha transparency. -Spades +/obj/effect/overlay/snow/attackby(obj/item/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/shovel)) + user.visible_message("[user] begins to shovel away \the [src].") + if(do_after(user, 40)) + to_chat(user, "You have finished shoveling!") + qdel(src) + return + +/obj/effect/overlay/snow/floor + icon_state = "snowfloor" + plane = TURF_PLANE + layer = ABOVE_TURF_LAYER + mouse_opacity = 0 //Don't block underlying tile interactions +/* +/obj/effect/overlay/snow/floor/edges + icon_state = "snow_edges" +/obj/effect/overlay/snow/floor/edges2 + icon_state = "snow_edges2" +/obj/effect/overlay/snow/floor/edges3 + icon_state = "gravsnow_edges" +/obj/effect/overlay/snow/floor/surround + icon_state = "snow_surround" */ +/obj/effect/overlay/snow/airlock + icon_state = "snowairlock" + layer = DOOR_CLOSED_LAYER+0.01 + +/obj/effect/overlay/snow/floor/pointy + icon_state = "snowfloorpointy" + +/obj/effect/overlay/snow/wall + icon_state = "snowwall" + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + +/obj/effect/overlay/holographic + mouse_opacity = FALSE + anchored = TRUE + plane = ABOVE_PLANE + +// Similar to the tesla ball but doesn't actually do anything and is purely visual. +/obj/effect/overlay/energy_ball + name = "energy ball" + desc = "An energy ball." + icon = 'icons/obj/tesla_engine/energy_ball.dmi' + icon_state = "energy_ball" + plane = PLANE_LIGHTING_ABOVE + pixel_x = -32 + pixel_y = -32 + +/obj/effect/overlay/vis + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + anchored = TRUE + vis_flags = VIS_INHERIT_DIR + ///When detected to be unused it gets set to world.time, after a while it gets removed + var/unused = 0 + ///overlays which go unused for this amount of time get cleaned up + var/cache_expiration = 2 MINUTES + +/* +/obj/effect/overlay/atmos_excited + name = "excited group" + icon = null + icon_state = null + anchored = TRUE // should only appear in vis_contents, but to be safe + appearance_flags = RESET_TRANSFORM | TILE_BOUND + invisibility = INVISIBILITY_ABSTRACT + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + + plane = ATMOS_GROUP_PLANE +*/ + +/obj/effect/overlay/light_visible + name = "" + icon = 'icons/effects/light_overlays/light_32.dmi' + icon_state = "light" + plane = PLANE_O_LIGHTING_VISUAL + appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + alpha = 0 + vis_flags = NONE + blocks_emissive = FALSE + +/obj/effect/overlay/light_visible/Destroy(force) + if(!force) + stack_trace("Movable light visible mask deleted, but not by our component") + return QDEL_HINT_LETMELIVE + return ..() + +/obj/effect/overlay/light_cone + name = "" + icon = 'icons/effects/light_overlays/light_cone.dmi' + icon_state = "light" + plane = PLANE_O_LIGHTING_VISUAL + appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + vis_flags = NONE + alpha = 110 + blocks_emissive = FALSE + + var/static/matrix/normal_transform + +/obj/effect/overlay/light_cone/Initialize() + . = ..() + apply_standard_transform() + +/obj/effect/overlay/light_cone/proc/reset_transform(apply_standard) + transform = initial(transform) + if(apply_standard) + apply_standard_transform() + +/obj/effect/overlay/light_cone/proc/apply_standard_transform() + transform = transform.Translate(-32, -32) + +/obj/effect/overlay/light_cone/Destroy(force) + if(!force) + stack_trace("Directional light cone deleted, but not by our component") + return QDEL_HINT_LETMELIVE + return ..() + +/obj/effect/overlay/closet_door + anchored = TRUE + plane = FLOAT_PLANE + layer = FLOAT_LAYER + vis_flags = VIS_INHERIT_ID + appearance_flags = KEEP_TOGETHER | LONG_GLIDE | PIXEL_SCALE diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index ae27e756957..002d5d5cc8a 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -1,69 +1,69 @@ -GLOBAL_LIST_BOILERPLATE(all_portals, /obj/effect/portal) - -/obj/effect/portal - name = "portal" - desc = "Looks unstable. Best to test it with the clown." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "portal" - density = TRUE - unacidable = TRUE//Can't destroy energy portals. - var/failchance = 5 - var/obj/item/target = null - var/creator = null - anchored = TRUE - -/obj/effect/portal/Bumped(mob/M as mob|obj) - if(istype(M,/mob) && !(istype(M,/mob/living))) - return //do not send ghosts, zshadows, ai eyes, etc - spawn(0) - src.teleport(M) - return - return - -/obj/effect/portal/Crossed(atom/movable/AM as mob|obj) - if(AM.is_incorporeal()) - return - if(istype(AM,/mob) && !(istype(AM,/mob/living))) - return //do not send ghosts, zshadows, ai eyes, etc - spawn(0) - src.teleport(AM) - return - return - -/obj/effect/portal/attack_hand(mob/user as mob) - if(istype(user) && !(istype(user,/mob/living))) - return //do not send ghosts, zshadows, ai eyes, etc - spawn(0) - src.teleport(user) - return - return - -/obj/effect/portal/Initialize() - . = ..() - QDEL_IN(src, 30 SECONDS) - -/obj/effect/portal/proc/teleport(atom/movable/M as mob|obj) - if(istype(M, /obj/effect)) //sparks don't teleport - return - if (M.anchored&&istype(M, /obj/mecha)) - return - if (icon_state == "portal1") - return - if (!( target )) - qdel(src) - return - if (istype(M, /atom/movable)) - //VOREStation Addition Start: Prevent taurriding abuse - if(istype(M, /mob/living)) - var/mob/living/L = M - if(LAZYLEN(L.buckled_mobs)) - var/datum/riding/R = L.riding_datum - for(var/rider in L.buckled_mobs) - R.force_dismount(rider) - //VOREStation Addition End: Prevent taurriding abuse - if(prob(failchance)) //oh dear a problem, put em in deep space - src.icon_state = "portal1" - do_teleport(M, locate(rand(5, world.maxx - 5), rand(5, world.maxy -5), 3), 0) - else - do_teleport(M, target, 1) ///You will appear adjacent to the beacon - +GLOBAL_LIST_BOILERPLATE(all_portals, /obj/effect/portal) + +/obj/effect/portal + name = "portal" + desc = "Looks unstable. Best to test it with the clown." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "portal" + density = TRUE + unacidable = TRUE//Can't destroy energy portals. + var/failchance = 5 + var/obj/item/target = null + var/creator = null + anchored = TRUE + +/obj/effect/portal/Bumped(mob/M as mob|obj) + if(istype(M,/mob) && !(istype(M,/mob/living))) + return //do not send ghosts, zshadows, ai eyes, etc + spawn(0) + src.teleport(M) + return + return + +/obj/effect/portal/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return + if(istype(AM,/mob) && !(istype(AM,/mob/living))) + return //do not send ghosts, zshadows, ai eyes, etc + spawn(0) + src.teleport(AM) + return + return + +/obj/effect/portal/attack_hand(mob/user as mob) + if(istype(user) && !(istype(user,/mob/living))) + return //do not send ghosts, zshadows, ai eyes, etc + spawn(0) + src.teleport(user) + return + return + +/obj/effect/portal/Initialize() + . = ..() + QDEL_IN(src, 30 SECONDS) + +/obj/effect/portal/proc/teleport(atom/movable/M as mob|obj) + if(istype(M, /obj/effect)) //sparks don't teleport + return + if (M.anchored&&istype(M, /obj/mecha)) + return + if (icon_state == "portal1") + return + if (!( target )) + qdel(src) + return + if (istype(M, /atom/movable)) + //VOREStation Addition Start: Prevent taurriding abuse + if(istype(M, /mob/living)) + var/mob/living/L = M + if(LAZYLEN(L.buckled_mobs)) + var/datum/riding/R = L.riding_datum + for(var/rider in L.buckled_mobs) + R.force_dismount(rider) + //VOREStation Addition End: Prevent taurriding abuse + if(prob(failchance)) //oh dear a problem, put em in deep space + src.icon_state = "portal1" + do_teleport(M, locate(rand(5, world.maxx - 5), rand(5, world.maxy -5), 3), 0) + else + do_teleport(M, target, 1) ///You will appear adjacent to the beacon + diff --git a/code/game/objects/effects/semirandom_mobs_vr.dm b/code/game/objects/effects/semirandom_mobs_vr.dm index 9dc2c2e561a..608b333604f 100644 --- a/code/game/objects/effects/semirandom_mobs_vr.dm +++ b/code/game/objects/effects/semirandom_mobs_vr.dm @@ -1,1092 +1,1092 @@ -var/global/list/semirandom_mob_spawner_decisions = list() - -/obj/random/mob/semirandom_mob_spawner - name = "Semi-Random Spawner" - desc = "Spawns groups of mobs that are all of the same theme type/theme." - icon = 'icons/mob/randomlandmarks.dmi' - icon_state = "monster" - mob_returns_home = 1 - mob_wander_distance = 7 - - var/list/possible_mob_types = list( - list(/mob/living/simple_mob/animal/goat), - list( - /mob/living/simple_mob/animal/passive/bird, - /mob/living/simple_mob/animal/passive/bird/azure_tit, - /mob/living/simple_mob/animal/passive/bird/black_bird, - /mob/living/simple_mob/animal/passive/bird/european_robin, - /mob/living/simple_mob/animal/passive/bird/goldcrest, - /mob/living/simple_mob/animal/passive/bird/ringneck_dove, - /mob/living/simple_mob/animal/passive/bird/parrot, - /mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique, - /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar, - /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue, - /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish, - /mob/living/simple_mob/animal/passive/bird/parrot/eclectus, - /mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot, - /mob/living/simple_mob/animal/passive/bird/parrot/kea, - /mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo, - /mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo, - /mob/living/simple_mob/animal/passive/bird/parrot/white_caique, - /mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo - ), - list( - /mob/living/simple_mob/animal/passive/cat, - /mob/living/simple_mob/animal/passive/cat/black - ), - list(/mob/living/simple_mob/animal/passive/chick), - list(/mob/living/simple_mob/animal/passive/cow), - list(/mob/living/simple_mob/animal/passive/dog/brittany), - list(/mob/living/simple_mob/animal/passive/dog/corgi), - list(/mob/living/simple_mob/animal/passive/dog/tamaskan), - list(/mob/living/simple_mob/animal/passive/fox), - list(/mob/living/simple_mob/animal/passive/hare), - list(/mob/living/simple_mob/animal/passive/lizard), - list(/mob/living/simple_mob/animal/passive/mouse), - list(/mob/living/simple_mob/animal/passive/mouse/jerboa), - list(/mob/living/simple_mob/animal/passive/opossum), - list(/mob/living/simple_mob/animal/passive/pillbug), - list(/mob/living/simple_mob/animal/passive/snake), - list(/mob/living/simple_mob/animal/passive/snake/red), - list(/mob/living/simple_mob/animal/passive/snake/python), - list(/mob/living/simple_mob/animal/passive/tindalos), - list(/mob/living/simple_mob/animal/passive/yithian), - list( - /mob/living/simple_mob/vore/wolf = 10, - /mob/living/simple_mob/vore/wolf/direwolf = 5, - /mob/living/simple_mob/vore/greatwolf = 1, - /mob/living/simple_mob/vore/greatwolf/black = 1, - /mob/living/simple_mob/vore/greatwolf/grey = 1 - ), - list(/mob/living/simple_mob/vore/rabbit), - list(/mob/living/simple_mob/vore/redpanda), - list(/mob/living/simple_mob/vore/woof), - list(/mob/living/simple_mob/vore/fennec), - list(/mob/living/simple_mob/vore/fennix), - list(/mob/living/simple_mob/vore/hippo), - list(/mob/living/simple_mob/vore/horse), - list(/mob/living/simple_mob/vore/bee), - list( - /mob/living/simple_mob/animal/space/bear, - /mob/living/simple_mob/animal/space/bear/brown - ), - list( - /mob/living/simple_mob/vore/otie/feral, - /mob/living/simple_mob/vore/otie/feral/chubby, - /mob/living/simple_mob/vore/otie/red, - /mob/living/simple_mob/vore/otie/red/chubby - ), - list(/mob/living/simple_mob/animal/sif/diyaab), - list(/mob/living/simple_mob/animal/sif/duck), - list(/mob/living/simple_mob/animal/sif/frostfly), - list( - /mob/living/simple_mob/animal/sif/glitterfly =50, - /mob/living/simple_mob/animal/sif/glitterfly/rare = 1 - ), - list( - /mob/living/simple_mob/animal/sif/kururak = 10, - /mob/living/simple_mob/animal/sif/kururak/leader = 1, - /mob/living/simple_mob/animal/sif/kururak/hibernate = 2, - ), - list( - /mob/living/simple_mob/animal/sif/sakimm = 10, - /mob/living/simple_mob/animal/sif/sakimm/intelligent = 1 - ), - list(/mob/living/simple_mob/animal/sif/savik) = 5, - list( - /mob/living/simple_mob/animal/sif/shantak = 10, - /mob/living/simple_mob/animal/sif/shantak/leader = 1 - ), - list(/mob/living/simple_mob/animal/sif/siffet), - list(/mob/living/simple_mob/animal/sif/tymisian), - list( - /mob/living/simple_mob/animal/giant_spider/electric = 5, - /mob/living/simple_mob/animal/giant_spider/frost = 5, - /mob/living/simple_mob/animal/giant_spider/hunter = 10, - /mob/living/simple_mob/animal/giant_spider/ion = 5, - /mob/living/simple_mob/animal/giant_spider/lurker = 10, - /mob/living/simple_mob/animal/giant_spider/pepper = 10, - /mob/living/simple_mob/animal/giant_spider/phorogenic = 10, - /mob/living/simple_mob/animal/giant_spider/thermic = 5, - /mob/living/simple_mob/animal/giant_spider/tunneler = 10, - /mob/living/simple_mob/animal/giant_spider/webslinger = 5, - /mob/living/simple_mob/animal/giant_spider/broodmother = 1), - list(/mob/living/simple_mob/creature/strong), - list(/mob/living/simple_mob/faithless/strong), - list(/mob/living/simple_mob/animal/goat), - list( - /mob/living/simple_mob/animal/sif/shantak/leader = 1, - /mob/living/simple_mob/animal/sif/shantak = 10), - list(/mob/living/simple_mob/animal/sif/savik,), - list(/mob/living/simple_mob/animal/sif/hooligan_crab), - list( - /mob/living/simple_mob/animal/space/alien = 50, - /mob/living/simple_mob/animal/space/alien/drone = 40, - /mob/living/simple_mob/animal/space/alien/sentinel = 25, - /mob/living/simple_mob/animal/space/alien/sentinel/praetorian = 15, - /mob/living/simple_mob/animal/space/alien/queen = 10, - /mob/living/simple_mob/animal/space/alien/queen/empress = 5, - /mob/living/simple_mob/animal/space/alien/queen/empress/mother = 1 - ), - list(/mob/living/simple_mob/animal/space/bats/cult/strong), - list( - /mob/living/simple_mob/animal/space/bear, - /mob/living/simple_mob/animal/space/bear/brown - ), - list( - /mob/living/simple_mob/animal/space/carp = 50, - /mob/living/simple_mob/animal/space/carp/large = 10, - /mob/living/simple_mob/animal/space/carp/large/huge = 5 - ), - list(/mob/living/simple_mob/animal/space/goose), - list(/mob/living/simple_mob/vore/jelly), - list(/mob/living/simple_mob/animal/space/tree), - list( - /mob/living/simple_mob/vore/aggressive/corrupthound = 10, - /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi = 1, - ), - list(/mob/living/simple_mob/vore/aggressive/deathclaw), - list(/mob/living/simple_mob/vore/aggressive/dino), - list(/mob/living/simple_mob/vore/aggressive/dragon), - list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b), - list(/mob/living/simple_mob/vore/aggressive/frog), - list(/mob/living/simple_mob/vore/aggressive/giant_snake), - list(/mob/living/simple_mob/vore/aggressive/mimic), - list(/mob/living/simple_mob/vore/aggressive/panther), - list(/mob/living/simple_mob/vore/aggressive/rat), - list(/mob/living/simple_mob/vore/bee), - list( - /mob/living/simple_mob/vore/sect_drone = 10, - /mob/living/simple_mob/vore/sect_queen = 1 - ), - list(/mob/living/simple_mob/vore/solargrub), - list( - /mob/living/simple_mob/vore/oregrub = 5, - /mob/living/simple_mob/vore/oregrub/lava = 1 - ), - list(/mob/living/simple_mob/vore/catgirl), - list(/mob/living/simple_mob/vore/wolfgirl), - list( - /mob/living/simple_mob/vore/lamia, - /mob/living/simple_mob/vore/lamia/albino, - /mob/living/simple_mob/vore/lamia/albino/bra, - /mob/living/simple_mob/vore/lamia/albino/shirt, - /mob/living/simple_mob/vore/lamia/bra, - /mob/living/simple_mob/vore/lamia/cobra, - /mob/living/simple_mob/vore/lamia/cobra/bra, - /mob/living/simple_mob/vore/lamia/cobra/shirt, - /mob/living/simple_mob/vore/lamia/copper, - /mob/living/simple_mob/vore/lamia/copper/bra, - /mob/living/simple_mob/vore/lamia/copper/shirt, - /mob/living/simple_mob/vore/lamia/green, - /mob/living/simple_mob/vore/lamia/green/bra, - /mob/living/simple_mob/vore/lamia/green/shirt, - /mob/living/simple_mob/vore/lamia/zebra, - /mob/living/simple_mob/vore/lamia/zebra/bra, - /mob/living/simple_mob/vore/lamia/zebra/shirt - ), - list( - /mob/living/simple_mob/humanoid/merc = 100, - /mob/living/simple_mob/humanoid/merc/melee/sword = 50, - /mob/living/simple_mob/humanoid/merc/ranged = 25, - /mob/living/simple_mob/humanoid/merc/ranged/grenadier = 1, - /mob/living/simple_mob/humanoid/merc/ranged/ionrifle = 10, - /mob/living/simple_mob/humanoid/merc/ranged/laser = 5, - /mob/living/simple_mob/humanoid/merc/ranged/rifle = 5, - /mob/living/simple_mob/humanoid/merc/ranged/smg = 5, - /mob/living/simple_mob/humanoid/merc/ranged/sniper = 1, - /mob/living/simple_mob/humanoid/merc/ranged/space = 10, - /mob/living/simple_mob/humanoid/merc/ranged/technician = 5 - ), - list( - /mob/living/simple_mob/humanoid/pirate = 3, - /mob/living/simple_mob/humanoid/pirate/ranged = 1 - ), - list(/mob/living/simple_mob/mechanical/combat_drone), - list(/mob/living/simple_mob/mechanical/corrupt_maint_drone), - list( - /mob/living/simple_mob/mechanical/hivebot = 100, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage = 20, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/backline = 10, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/basic = 20, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/dot = 5, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/ion = 20, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/laser = 10, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/rapid = 2, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege = 1, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/emp = 5, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/fragmentation = 1, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/radiation = 1, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong = 3, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong/guard = 3, - /mob/living/simple_mob/mechanical/hivebot/support = 8, - /mob/living/simple_mob/mechanical/hivebot/support/commander = 5, - /mob/living/simple_mob/mechanical/hivebot/support/commander/autofollow = 10, - /mob/living/simple_mob/mechanical/hivebot/swarm = 20, - /mob/living/simple_mob/mechanical/hivebot/tank = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_bullet = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_laser = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_melee = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/meatshield = 20 - ), - list(/mob/living/simple_mob/mechanical/infectionbot), - list(/mob/living/simple_mob/mechanical/mining_drone), - list(/mob/living/simple_mob/mechanical/technomancer_golem), - list( - /mob/living/simple_mob/mechanical/viscerator, - /mob/living/simple_mob/mechanical/viscerator/piercing - ), - list(/mob/living/simple_mob/mechanical/wahlem), - list(/mob/living/simple_mob/animal/passive/fox/syndicate), - list(/mob/living/simple_mob/animal/passive/fox), - list(/mob/living/simple_mob/vore/jelly), - list( - /mob/living/simple_mob/vore/otie/feral, - /mob/living/simple_mob/vore/otie/feral/chubby, - /mob/living/simple_mob/vore/otie/red, - /mob/living/simple_mob/vore/otie/red/chubby - ), - list( - /mob/living/simple_mob/shadekin/blue = 100, - /mob/living/simple_mob/shadekin/green = 50, - /mob/living/simple_mob/shadekin/orange = 20, - /mob/living/simple_mob/shadekin/purple = 60, - /mob/living/simple_mob/shadekin/red = 40, - /mob/living/simple_mob/shadekin/yellow = 1 - ), - list( - /mob/living/simple_mob/vore/aggressive/corrupthound, - /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi - ), - list(/mob/living/simple_mob/vore/aggressive/deathclaw), - list(/mob/living/simple_mob/vore/aggressive/dino), - list(/mob/living/simple_mob/vore/aggressive/dragon), - list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b), - list(/mob/living/simple_mob/vore/aggressive/frog), - list(/mob/living/simple_mob/vore/aggressive/giant_snake), - list(/mob/living/simple_mob/vore/aggressive/mimic), - list(/mob/living/simple_mob/vore/aggressive/panther), - list(/mob/living/simple_mob/vore/aggressive/rat), - list(/mob/living/simple_mob/vore/bee), - list(/mob/living/simple_mob/vore/catgirl), - list(/mob/living/simple_mob/vore/cookiegirl), - list(/mob/living/simple_mob/vore/fennec), - list(/mob/living/simple_mob/vore/fennix), - list(/mob/living/simple_mob/vore/hippo), - list(/mob/living/simple_mob/vore/horse), - list(/mob/living/simple_mob/vore/oregrub), - list(/mob/living/simple_mob/vore/rabbit), - list( - /mob/living/simple_mob/vore/redpanda = 50, - /mob/living/simple_mob/vore/redpanda/fae = 1 - ), - list( - /mob/living/simple_mob/vore/sect_drone = 10, - /mob/living/simple_mob/vore/sect_queen = 1 - ), - list(/mob/living/simple_mob/vore/solargrub), - list(/mob/living/simple_mob/vore/woof), - list(/mob/living/simple_mob/vore/alienanimals/space_ghost), - list(/mob/living/simple_mob/vore/alienanimals/catslug), - list(/mob/living/simple_mob/vore/alienanimals/space_jellyfish), - list(/mob/living/simple_mob/vore/alienanimals/startreader), - list( - /mob/living/simple_mob/vore/bigdragon, - /mob/living/simple_mob/vore/bigdragon/friendly), - list( - /mob/living/simple_mob/vore/leopardmander = 50, - /mob/living/simple_mob/vore/leopardmander/blue = 10, - /mob/living/simple_mob/vore/leopardmander/exotic = 1 - ), - list(/mob/living/simple_mob/vore/sheep), - list(/mob/living/simple_mob/vore/weretiger) - ) - -/obj/random/mob/semirandom_mob_spawner/item_to_spawn() - var/list/choice = semirandom_mob_spawner_decisions[type] - - if(!choice) - choice = pickweight(possible_mob_types) - semirandom_mob_spawner_decisions[type] = choice - - return pickweight(choice) - -/obj/random/mob/semirandom_mob_spawner/animal - name = "Semi-Random Animal" - desc = "Spawns groups of non-hostile mobs that are all of the same theme type/theme." - icon_state = "animal" - mob_faction = "animal" - overwrite_hostility = 1 - mob_hostile = 0 - - possible_mob_types = list( - list(/mob/living/simple_mob/animal/goat) = 25, - list( - /mob/living/simple_mob/animal/passive/bird, - /mob/living/simple_mob/animal/passive/bird/azure_tit, - /mob/living/simple_mob/animal/passive/bird/black_bird, - /mob/living/simple_mob/animal/passive/bird/european_robin, - /mob/living/simple_mob/animal/passive/bird/goldcrest, - /mob/living/simple_mob/animal/passive/bird/ringneck_dove, - /mob/living/simple_mob/animal/passive/bird/parrot, - /mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique, - /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar, - /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue, - /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish, - /mob/living/simple_mob/animal/passive/bird/parrot/eclectus, - /mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot, - /mob/living/simple_mob/animal/passive/bird/parrot/kea, - /mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo, - /mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo, - /mob/living/simple_mob/animal/passive/bird/parrot/white_caique, - /mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo, - /mob/living/simple_mob/animal/space/goose - ) = 25, - list( - /mob/living/simple_mob/animal/passive/cat, - /mob/living/simple_mob/animal/passive/cat/black - ) = 25, - list( - /mob/living/simple_mob/animal/passive/chick, - /mob/living/simple_mob/animal/passive/chicken - ) = 25, - list(/mob/living/simple_mob/animal/passive/cow) = 25, - list(/mob/living/simple_mob/animal/passive/dog/brittany) = 10, - list(/mob/living/simple_mob/animal/passive/dog/corgi) = 10, - list(/mob/living/simple_mob/animal/passive/dog/tamaskan) = 10, - list(/mob/living/simple_mob/animal/passive/fox) = 25, - list(/mob/living/simple_mob/animal/passive/hare) = 25, - list(/mob/living/simple_mob/animal/passive/lizard) = 10, - list(/mob/living/simple_mob/animal/passive/mouse) = 15, - list(/mob/living/simple_mob/animal/passive/mouse/jerboa) = 5, - list(/mob/living/simple_mob/animal/passive/opossum) = 10, - list(/mob/living/simple_mob/animal/passive/pillbug) = 10, - list(/mob/living/simple_mob/animal/passive/snake) = 10, - list(/mob/living/simple_mob/animal/passive/snake/red) = 10, - list(/mob/living/simple_mob/animal/passive/snake/python) = 10, - list(/mob/living/simple_mob/animal/passive/tindalos) = 10, - list(/mob/living/simple_mob/animal/passive/yithian) = 10, - list( - /mob/living/simple_mob/vore/wolf = 10, - /mob/living/simple_mob/vore/wolf/direwolf = 5, - /mob/living/simple_mob/vore/greatwolf = 1, - /mob/living/simple_mob/vore/greatwolf/black = 1, - /mob/living/simple_mob/vore/greatwolf/grey = 1 - ) = 10, - list(/mob/living/simple_mob/vore/rabbit) = 10, - list(/mob/living/simple_mob/vore/redpanda) = 10, - list(/mob/living/simple_mob/vore/woof) = 1, - list(/mob/living/simple_mob/vore/fennec) = 10, - list(/mob/living/simple_mob/vore/fennix) = 1, - list(/mob/living/simple_mob/vore/hippo) = 5, - list(/mob/living/simple_mob/vore/horse) = 25, - list(/mob/living/simple_mob/vore/bee) = 10, - list( - /mob/living/simple_mob/animal/space/bear, - /mob/living/simple_mob/animal/space/bear/brown - ) = 1, - list( - /mob/living/simple_mob/vore/otie/feral = 50, - /mob/living/simple_mob/vore/otie/feral/chubby = 10, - /mob/living/simple_mob/vore/otie/red = 5, - /mob/living/simple_mob/vore/otie/red/chubby = 1 - ) = 5, - list(/mob/living/simple_mob/vore/aggressive/rat) = 15, - list(/mob/living/simple_mob/animal/sif/diyaab) = 5, - list(/mob/living/simple_mob/animal/sif/duck) = 5, - list(/mob/living/simple_mob/animal/sif/frostfly) = 5, - list( - /mob/living/simple_mob/animal/sif/glitterfly = 50, - /mob/living/simple_mob/animal/sif/glitterfly/rare = 1 - ) = 5, - list( - /mob/living/simple_mob/animal/sif/kururak = 10, - /mob/living/simple_mob/animal/sif/kururak/leader = 1, - /mob/living/simple_mob/animal/sif/kururak/hibernate = 2, - ) = 5, - list( - /mob/living/simple_mob/animal/sif/sakimm = 10, - /mob/living/simple_mob/animal/sif/sakimm/intelligent = 1 - ) = 5, - list(/mob/living/simple_mob/animal/sif/savik) = 5, - list( - /mob/living/simple_mob/animal/sif/shantak = 10, - /mob/living/simple_mob/animal/sif/shantak/leader = 1 - ) = 5, - list(/mob/living/simple_mob/animal/sif/siffet) = 5, - list(/mob/living/simple_mob/animal/sif/tymisian) = 5, - list(/mob/living/simple_mob/vore/alienanimals/dustjumper) = 5, - list(/mob/living/simple_mob/vore/alienanimals/space_jellyfish) = 5, - list(/mob/living/simple_mob/vore/alienanimals/space_ghost) = 5, - list( - /mob/living/simple_mob/vore/leopardmander = 50, - /mob/living/simple_mob/vore/leopardmander/blue = 10, - /mob/living/simple_mob/vore/leopardmander/exotic = 1 - ) = 5, - list(/mob/living/simple_mob/vore/sheep) = 5, - list(/mob/living/simple_mob/vore/weretiger) = 5, - list(/mob/living/simple_mob/vore/alienanimals/skeleton) = 5 - ) - -/obj/random/mob/semirandom_mob_spawner/monster - name = "Semi-Random Monster" - desc = "Spawns groups of hostile mobs that are all of the same theme type/theme." - overwrite_hostility = 1 - mob_faction = "monster" - mob_hostile = 1 - mob_retaliate = 1 - - possible_mob_types = list( - list( - /mob/living/simple_mob/animal/giant_spider/electric = 5, - /mob/living/simple_mob/animal/giant_spider/frost = 5, - /mob/living/simple_mob/animal/giant_spider/hunter = 10, - /mob/living/simple_mob/animal/giant_spider/ion = 5, - /mob/living/simple_mob/animal/giant_spider/lurker = 10, - /mob/living/simple_mob/animal/giant_spider/pepper = 10, - /mob/living/simple_mob/animal/giant_spider/phorogenic = 10, - /mob/living/simple_mob/animal/giant_spider/thermic = 5, - /mob/living/simple_mob/animal/giant_spider/tunneler = 10, - /mob/living/simple_mob/animal/giant_spider/webslinger = 5 - ) = 100, - list( - /mob/living/simple_mob/shadekin/red = 5, - /mob/living/simple_mob/shadekin/orange = 1, - /mob/living/simple_mob/shadekin/purple = 10 - ) = 1, - list( - /mob/living/simple_mob/vore/wolf = 10, - /mob/living/simple_mob/vore/wolf/direwolf = 5, - /mob/living/simple_mob/vore/greatwolf = 1, - /mob/living/simple_mob/vore/greatwolf/black = 1, - /mob/living/simple_mob/vore/greatwolf/grey = 1 - ) = 40, - list(/mob/living/simple_mob/creature/strong) = 40, - list(/mob/living/simple_mob/faithless/strong) = 20, - list(/mob/living/simple_mob/animal/goat) = 1, - list( - /mob/living/simple_mob/animal/sif/shantak/leader = 1, - /mob/living/simple_mob/animal/sif/shantak = 10 - ) = 50, - list(/mob/living/simple_mob/animal/sif/savik,) = 20, - list(/mob/living/simple_mob/animal/sif/hooligan_crab) = 10, - list( - /mob/living/simple_mob/animal/space/alien = 50, - /mob/living/simple_mob/animal/space/alien/drone = 40, - /mob/living/simple_mob/animal/space/alien/sentinel = 25, - /mob/living/simple_mob/animal/space/alien/sentinel/praetorian = 15, - /mob/living/simple_mob/animal/space/alien/queen = 10, - /mob/living/simple_mob/animal/space/alien/queen/empress = 5, - /mob/living/simple_mob/animal/space/alien/queen/empress/mother = 1, - ) = 40, - list(/mob/living/simple_mob/animal/space/bats/cult/strong) = 40, - list( - /mob/living/simple_mob/animal/space/bear, - /mob/living/simple_mob/animal/space/bear/brown - ) = 40, - list( - /mob/living/simple_mob/animal/space/carp = 50, - /mob/living/simple_mob/animal/space/carp/large = 10, - /mob/living/simple_mob/animal/space/carp/large/huge = 5 - ) = 50, - list(/mob/living/simple_mob/animal/space/goose) = 50, - list(/mob/living/simple_mob/vore/jelly) = 40, - list(/mob/living/simple_mob/animal/space/tree) = 15, - list( - /mob/living/simple_mob/vore/otie/feral = 50, - /mob/living/simple_mob/vore/otie/feral/chubby = 10, - /mob/living/simple_mob/vore/otie/red = 5, - /mob/living/simple_mob/vore/otie/red/chubby = 1 - ) = 40, - list( - /mob/living/simple_mob/vore/aggressive/corrupthound = 10, - /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi = 1, - ) = 50, - list(/mob/living/simple_mob/vore/aggressive/deathclaw) = 40, - list(/mob/living/simple_mob/vore/aggressive/dino) = 40, - list(/mob/living/simple_mob/vore/aggressive/dragon) = 40, - list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b) = 40, - list(/mob/living/simple_mob/vore/aggressive/frog) = 40, - list(/mob/living/simple_mob/vore/aggressive/giant_snake) = 40, - list(/mob/living/simple_mob/vore/aggressive/mimic) = 40, - list(/mob/living/simple_mob/vore/aggressive/panther) = 25, - list(/mob/living/simple_mob/vore/aggressive/rat) = 50, - list(/mob/living/simple_mob/vore/bee) = 40, - list( - /mob/living/simple_mob/vore/sect_drone = 10, - /mob/living/simple_mob/vore/sect_queen = 1 - ) = 20, - list(/mob/living/simple_mob/vore/solargrub) = 15, - list( - /mob/living/simple_mob/vore/oregrub = 5, - /mob/living/simple_mob/vore/oregrub/lava = 1 - ) = 15, - list(/mob/living/simple_mob/vore/alienanimals/space_jellyfish) = 5, - list(/mob/living/simple_mob/vore/alienanimals/space_ghost) = 5, - list( - /mob/living/simple_mob/vore/leopardmander = 50, - /mob/living/simple_mob/vore/leopardmander/blue = 10, - /mob/living/simple_mob/vore/leopardmander/exotic = 1 - ) = 5, - list(/mob/living/simple_mob/vore/sheep) = 5, - list(/mob/living/simple_mob/vore/weretiger) = 5, - list(/mob/living/simple_mob/vore/alienanimals/skeleton) = 5, - list(/mob/living/simple_mob/vore/alienanimals/catslug) = 5 - ) - -/obj/random/mob/semirandom_mob_spawner/humanoid - name = "Semi-Random Humanoid" - desc = "Spawns groups of humanoid mobs that may or may not be hostile, all of the same theme type/theme." - icon_state = "humanoid" - mob_faction = "humanoid" - - possible_mob_types = list( - list( - /mob/living/simple_mob/shadekin/blue = 25, - /mob/living/simple_mob/shadekin/green = 10, - /mob/living/simple_mob/shadekin/purple = 1, - ) = 1, - list(/mob/living/simple_mob/vore/catgirl) = 100, - list(/mob/living/simple_mob/vore/wolfgirl) = 100, - list( - /mob/living/simple_mob/vore/lamia, - /mob/living/simple_mob/vore/lamia/albino, - /mob/living/simple_mob/vore/lamia/albino/bra, - /mob/living/simple_mob/vore/lamia/albino/shirt, - /mob/living/simple_mob/vore/lamia/bra, - /mob/living/simple_mob/vore/lamia/cobra, - /mob/living/simple_mob/vore/lamia/cobra/bra, - /mob/living/simple_mob/vore/lamia/cobra/shirt, - /mob/living/simple_mob/vore/lamia/copper, - /mob/living/simple_mob/vore/lamia/copper/bra, - /mob/living/simple_mob/vore/lamia/copper/shirt, - /mob/living/simple_mob/vore/lamia/green, - /mob/living/simple_mob/vore/lamia/green/bra, - /mob/living/simple_mob/vore/lamia/green/shirt, - /mob/living/simple_mob/vore/lamia/zebra, - /mob/living/simple_mob/vore/lamia/zebra/bra, - /mob/living/simple_mob/vore/lamia/zebra/shirt - ) = 100, -// LOOK OKAY MERCS ARE HUMANOIDS SO THEY ARE HERE, but they are also kind of bullshit so they probably shouldn't be able to spawn in the same place as catgirls. -// I want some better potentially hostile humanoids that aren't stupid to fight. If they become a big issue I'll comment them out. -// For now they are just rare, and the ranged ones are way more rare than the melee ones, which I think will help balance them out. - list( - /mob/living/simple_mob/humanoid/merc = 100, - /mob/living/simple_mob/humanoid/merc/melee/sword = 50, - /mob/living/simple_mob/humanoid/merc/ranged = 25, - /mob/living/simple_mob/humanoid/merc/ranged/grenadier = 1, - /mob/living/simple_mob/humanoid/merc/ranged/ionrifle = 10, - /mob/living/simple_mob/humanoid/merc/ranged/laser = 5, - /mob/living/simple_mob/humanoid/merc/ranged/rifle = 5, - /mob/living/simple_mob/humanoid/merc/ranged/smg = 5, - /mob/living/simple_mob/humanoid/merc/ranged/sniper = 1, - /mob/living/simple_mob/humanoid/merc/ranged/space = 10, - /mob/living/simple_mob/humanoid/merc/ranged/technician = 5 - ) = 5, -// PIRATES are okay though. They can be kind of a pain but you can kind of slap them around. Also it's not like. A crime. To fight and blow up pirates so it's fine. - list( - /mob/living/simple_mob/humanoid/pirate = 3, - /mob/living/simple_mob/humanoid/pirate/ranged = 1 - ) = 50 - ) - -// I am not familiar enough with robots to know which ones are fun to fight so this list isn't weighted at all SO YOU KNOW. Be careful. -/obj/random/mob/semirandom_mob_spawner/robot - name = "Semi-Random Robot" - desc = "Spawns groups of robotic mobs that are probably hostile, all of the same theme type/theme." - icon_state = "robot" - mob_faction = "robot" - - possible_mob_types = list( - list(/mob/living/simple_mob/mechanical/combat_drone), - list(/mob/living/simple_mob/mechanical/corrupt_maint_drone), - list( - /mob/living/simple_mob/mechanical/hivebot = 100, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage = 20, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/backline = 10, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/basic = 20, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/dot = 5, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/ion = 20, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/laser = 10, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/rapid = 2, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege = 1, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/emp = 5, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/fragmentation = 1, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/radiation = 1, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong = 3, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong/guard = 3, - /mob/living/simple_mob/mechanical/hivebot/support = 8, - /mob/living/simple_mob/mechanical/hivebot/support/commander = 5, - /mob/living/simple_mob/mechanical/hivebot/support/commander/autofollow = 10, - /mob/living/simple_mob/mechanical/hivebot/swarm = 20, - /mob/living/simple_mob/mechanical/hivebot/tank = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_bullet = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_laser = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_melee = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/meatshield = 20 - ), - list(/mob/living/simple_mob/mechanical/infectionbot), - list(/mob/living/simple_mob/mechanical/mining_drone), - list(/mob/living/simple_mob/mechanical/technomancer_golem), - list( - /mob/living/simple_mob/mechanical/viscerator, - /mob/living/simple_mob/mechanical/viscerator/piercing - ), - list(/mob/living/simple_mob/mechanical/wahlem), - list(/mob/living/simple_mob/animal/passive/fox/syndicate) - ) - -/obj/random/mob/semirandom_mob_spawner/fish - name = "Semi-Random Fish" - desc = "Spawns groups of fish, all of the same theme type/theme." - icon_state = "fish" - mob_faction = "fish" - overwrite_hostility = 1 - mob_hostile = 0 - mob_retaliate = 0 - - - possible_mob_types = list( - list(/mob/living/simple_mob/animal/passive/fish/bass) = 20, - list(/mob/living/simple_mob/animal/passive/fish/icebass) = 20, - list(/mob/living/simple_mob/animal/passive/fish/javelin) = 20, - list(/mob/living/simple_mob/animal/passive/fish/koi) = 10, - list(/mob/living/simple_mob/animal/passive/fish/measelshark) = 5, - list(/mob/living/simple_mob/animal/passive/fish/murkin) = 20, - list(/mob/living/simple_mob/animal/passive/fish/perch) = 20, - list(/mob/living/simple_mob/animal/passive/fish/pike) = 20, - list(/mob/living/simple_mob/animal/passive/fish/rockfish) = 10, - list(/mob/living/simple_mob/animal/passive/fish/salmon) = 20, - list(/mob/living/simple_mob/animal/passive/fish/solarfish) = 5, - list(/mob/living/simple_mob/animal/passive/fish/trout) = 20, - list(/mob/living/simple_mob/animal/passive/crab) = 10, - list(/mob/living/simple_mob/animal/sif/hooligan_crab) = 1 - ) - -/obj/random/mob/semirandom_mob_spawner/bird - name = "Semi-Random Bird" - desc = "Spawns groups of bird, all of the same theme type/theme." - icon_state = "bird" - mob_faction = "bird" - - possible_mob_types = list( - list(/mob/living/simple_mob/animal/passive/bird), - list(/mob/living/simple_mob/animal/passive/bird/azure_tit), - list(/mob/living/simple_mob/animal/passive/bird/black_bird), - list(/mob/living/simple_mob/animal/passive/bird/european_robin), - list(/mob/living/simple_mob/animal/passive/bird/goldcrest), - list(/mob/living/simple_mob/animal/passive/bird/ringneck_dove), - list(/mob/living/simple_mob/animal/passive/bird/parrot), - list(/mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique), - list(/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar), - list(/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue), - list(/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen), - list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel), - list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey), - list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white), - list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish), - list(/mob/living/simple_mob/animal/passive/bird/parrot/eclectus), - list(/mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot), - list(/mob/living/simple_mob/animal/passive/bird/parrot/kea), - list(/mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo), - list(/mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo), - list(/mob/living/simple_mob/animal/passive/bird/parrot/white_caique), - list(/mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo), - list(/mob/living/simple_mob/animal/space/goose), - list(/mob/living/simple_mob/animal/passive/chicken), - list(/mob/living/simple_mob/animal/passive/penguin) - ) - -/obj/random/mob/semirandom_mob_spawner/vore - name = "Semi-Random Voremob" - desc = "Spawns groups of voremobs, all of the same theme type/theme." - icon_state = "vore" - mob_faction = "vore" - - possible_mob_types = list( - list( - /mob/living/simple_mob/vore/wolf/direwolf = 5, - /mob/living/simple_mob/vore/greatwolf = 1, - /mob/living/simple_mob/vore/greatwolf/black = 1, - /mob/living/simple_mob/vore/greatwolf/grey = 1 - ) = 100, - list(/mob/living/simple_mob/vore/jelly) = 70, - list( - /mob/living/simple_mob/vore/otie/feral, - /mob/living/simple_mob/vore/otie/feral/chubby, - /mob/living/simple_mob/vore/otie/red, - /mob/living/simple_mob/vore/otie/red/chubby - ) = 50, - list( - /mob/living/simple_mob/shadekin/blue = 100, - /mob/living/simple_mob/shadekin/green = 50, - /mob/living/simple_mob/shadekin/orange = 20, - /mob/living/simple_mob/shadekin/purple = 60, - /mob/living/simple_mob/shadekin/red = 40, - /mob/living/simple_mob/shadekin/yellow = 1 - ) = 1, - list( - /mob/living/simple_mob/vore/aggressive/corrupthound, - /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi - ) = 70, - list(/mob/living/simple_mob/vore/aggressive/deathclaw) = 70, - list(/mob/living/simple_mob/vore/aggressive/dino) = 100, - list(/mob/living/simple_mob/vore/aggressive/dragon) = 100, - list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b) = 100, - list(/mob/living/simple_mob/vore/aggressive/frog) = 100, - list(/mob/living/simple_mob/vore/aggressive/giant_snake) = 100, - list(/mob/living/simple_mob/vore/aggressive/mimic) = 50, - list(/mob/living/simple_mob/vore/aggressive/panther) = 70, - list(/mob/living/simple_mob/vore/aggressive/rat) = 100, - list(/mob/living/simple_mob/vore/bee) = 100, - list(/mob/living/simple_mob/vore/catgirl) = 100, - list(/mob/living/simple_mob/vore/wolftaur) = 100, - list(/mob/living/simple_mob/vore/cookiegirl) = 100, - list(/mob/living/simple_mob/vore/fennec) = 100, - list(/mob/living/simple_mob/vore/fennix) = 50, - list(/mob/living/simple_mob/vore/hippo) = 70, - list(/mob/living/simple_mob/vore/horse) = 100, - list(/mob/living/simple_mob/vore/raptor) = 100, - list(/mob/living/simple_mob/vore/succubus) = 100, - list(/mob/living/simple_mob/vore/vampire) = 50, - list(/mob/living/simple_mob/vore/vampire/queen) = 1, - list(/mob/living/simple_mob/vore/bat) = 50, - list(/mob/living/simple_mob/vore/scel) = 10, - list( - /mob/living/simple_mob/vore/lamia, - /mob/living/simple_mob/vore/lamia/albino, - /mob/living/simple_mob/vore/lamia/albino/bra, - /mob/living/simple_mob/vore/lamia/albino/shirt, - /mob/living/simple_mob/vore/lamia/bra, - /mob/living/simple_mob/vore/lamia/cobra, - /mob/living/simple_mob/vore/lamia/cobra/bra, - /mob/living/simple_mob/vore/lamia/cobra/shirt, - /mob/living/simple_mob/vore/lamia/copper, - /mob/living/simple_mob/vore/lamia/copper/bra, - /mob/living/simple_mob/vore/lamia/copper/shirt, - /mob/living/simple_mob/vore/lamia/green, - /mob/living/simple_mob/vore/lamia/green/bra, - /mob/living/simple_mob/vore/lamia/green/shirt, - /mob/living/simple_mob/vore/lamia/zebra, - /mob/living/simple_mob/vore/lamia/zebra/bra, - /mob/living/simple_mob/vore/lamia/zebra/shirt - ) = 100, - list(/mob/living/simple_mob/vore/rabbit) = 100, - list( - /mob/living/simple_mob/vore/redpanda = 50, - /mob/living/simple_mob/vore/redpanda/fae = 1 - ) = 100, - list( - /mob/living/simple_mob/vore/sect_drone = 10, - /mob/living/simple_mob/vore/sect_queen = 1 - ) = 50, - list(/mob/living/simple_mob/vore/solargrub) = 100, - list(/mob/living/simple_mob/vore/woof) = 1 - ) - -/obj/random/mob/semirandom_mob_spawner/sus - name = "Weird shit" - desc = "Spawns groups of weird stuff, all of the same theme type/theme. Don't put this on normal maps." - icon_state = "sus" - mob_faction = "sus" - - possible_mob_types = list( - list( - /mob/living/simple_mob/vore/woof/hostile/melee = 100, - /mob/living/simple_mob/vore/woof/hostile/ranged = 20, - /mob/living/simple_mob/vore/woof/hostile/horrible = 10, - /mob/living/simple_mob/vore/woof/hostile/terrible = 5, - /mob/living/simple_mob/vore/woof/cass = 1 - ), - list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/recursive) - ) - -/obj/random/mob/semirandom_mob_spawner/mecha - name = "Semi-Random Mecha" - desc = "Spawns groups of mechs, all of the same theme type/theme. Don't put this on normal maps." - icon_state = "mecha" - mob_faction = "mecha" - - possible_mob_types = list( - list(/mob/living/simple_mob/mechanical/mecha/combat/durand), - list(/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive), - list(/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive/mercenary), - list(/mob/living/simple_mob/mechanical/mecha/combat/gygax), - list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark), - list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced), - list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/manned), - list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/medgax), - list(/mob/living/simple_mob/mechanical/mecha/combat/marauder), - list(/mob/living/simple_mob/mechanical/mecha/combat/marauder/mauler), - list(/mob/living/simple_mob/mechanical/mecha/combat/marauder/seraph), - list(/mob/living/simple_mob/mechanical/mecha/combat/phazon), - list(/mob/living/simple_mob/mechanical/mecha/hoverpod), - list(/mob/living/simple_mob/mechanical/mecha/hoverpod/manned), - list(/mob/living/simple_mob/mechanical/mecha/odysseus), - list(/mob/living/simple_mob/mechanical/mecha/odysseus/manned), - list(/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus), - list(/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus/manned), - list(/mob/living/simple_mob/mechanical/mecha/ripley), - list(/mob/living/simple_mob/mechanical/mecha/ripley/blue_flames), - list(/mob/living/simple_mob/mechanical/mecha/ripley/deathripley), - list(/mob/living/simple_mob/mechanical/mecha/ripley/deathripley/manned), - list(/mob/living/simple_mob/mechanical/mecha/ripley/firefighter), - list(/mob/living/simple_mob/mechanical/mecha/ripley/firefighter/manned), - list(/mob/living/simple_mob/mechanical/mecha/ripley/manned), - list(/mob/living/simple_mob/mechanical/mecha/ripley/red_flames) - ) - - -/obj/random/mob/semirandom_mob_spawner/monster/b - mob_faction = "monsterb" - -/obj/random/mob/semirandom_mob_spawner/monster/c - mob_faction = "monsterc" - -/obj/random/mob/semirandom_mob_spawner/monster/d - mob_faction = "monsterd" - -/obj/random/mob/semirandom_mob_spawner/monster/e - mob_faction = "monstere" - -/obj/random/mob/semirandom_mob_spawner/monster/f - mob_faction = "monsterf" - -/obj/random/mob/semirandom_mob_spawner/animal/b - mob_faction = "animalb" - -/obj/random/mob/semirandom_mob_spawner/animal/c - mob_faction = "animalc" - -/obj/random/mob/semirandom_mob_spawner/animal/d - mob_faction = "animald" - -/obj/random/mob/semirandom_mob_spawner/animal/e - mob_faction = "animale" - -/obj/random/mob/semirandom_mob_spawner/animal/f - mob_faction = "animalf" - -/obj/random/mob/semirandom_mob_spawner/animal/retaliate - mob_faction = "retanimala" - overwrite_hostility = 1 - mob_hostile = 0 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/animal/retaliate/b - mob_faction = "retanimalb" - -/obj/random/mob/semirandom_mob_spawner/animal/retaliate/c - mob_faction = "retanimalc" - -/obj/random/mob/semirandom_mob_spawner/animal/hostile - mob_faction = "hosanimala" - overwrite_hostility = 1 - mob_hostile = 1 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/animal/hostile/b - mob_faction = "hosanimalb" - -/obj/random/mob/semirandom_mob_spawner/animal/hostile/c - mob_faction = "hosanimalc" - - -/obj/random/mob/semirandom_mob_spawner/humanoid/b - mob_faction = "humanoidb" - -/obj/random/mob/semirandom_mob_spawner/humanoid/c - mob_faction = "humanoidc" - -/obj/random/mob/semirandom_mob_spawner/humanoid/d - mob_faction = "humanoidd" - -/obj/random/mob/semirandom_mob_spawner/humanoid/e - mob_faction = "humanoide" - -/obj/random/mob/semirandom_mob_spawner/humanoid/f - mob_faction = "humanoidf" - -/obj/random/mob/semirandom_mob_spawner/humanoid/retaliate - mob_faction = "rethumanoida" - overwrite_hostility = 1 - mob_hostile = 0 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/humanoid/retaliate/b - mob_faction = "rethumanoidb" - -/obj/random/mob/semirandom_mob_spawner/humanoid/retaliate/c - mob_faction = "rethumanoidc" - -/obj/random/mob/semirandom_mob_spawner/humanoid/hostile - mob_faction = "hoshumanoida" - overwrite_hostility = 1 - mob_hostile = 1 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/humanoid/hostile/b - mob_faction = "hoshumanoidb" - -/obj/random/mob/semirandom_mob_spawner/humanoid/hostile/c - mob_faction = "hoshumanoidc" - -/obj/random/mob/semirandom_mob_spawner/robot/b - mob_faction = "robotb" - -/obj/random/mob/semirandom_mob_spawner/robot/c - mob_faction = "robotc" - -/obj/random/mob/semirandom_mob_spawner/robot/d - mob_faction = "robotd" - -/obj/random/mob/semirandom_mob_spawner/robot/e - mob_faction = "robote" - -/obj/random/mob/semirandom_mob_spawner/robot/f - mob_faction = "robotf" - -/obj/random/mob/semirandom_mob_spawner/robot/retaliate - mob_faction = "retrobota" - overwrite_hostility = 1 - mob_hostile = 0 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/robot/retaliate/b - mob_faction = "retrobotb" - -/obj/random/mob/semirandom_mob_spawner/robot/retaliate/c - mob_faction = "retrobotc" - -/obj/random/mob/semirandom_mob_spawner/bird/b - mob_faction = "birdb" - -/obj/random/mob/semirandom_mob_spawner/bird/c - mob_faction = "birdc" - -/obj/random/mob/semirandom_mob_spawner/bird/d - mob_faction = "birdd" - -/obj/random/mob/semirandom_mob_spawner/bird/e - mob_faction = "birde" - -/obj/random/mob/semirandom_mob_spawner/bird/f - mob_faction = "birdf" - -/obj/random/mob/semirandom_mob_spawner/fish/b - mob_faction = "fishb" - -/obj/random/mob/semirandom_mob_spawner/fish/c - mob_faction = "fishc" - -/obj/random/mob/semirandom_mob_spawner/fish/d - mob_faction = "fishd" - -/obj/random/mob/semirandom_mob_spawner/fish/e - mob_faction = "fishe" - -/obj/random/mob/semirandom_mob_spawner/fish/f - mob_faction = "fishf" - -/obj/random/mob/semirandom_mob_spawner/vore/b - mob_faction = "voreb" - -/obj/random/mob/semirandom_mob_spawner/vore/c - mob_faction = "vorec" - -/obj/random/mob/semirandom_mob_spawner/vore/d - mob_faction = "vored" - -/obj/random/mob/semirandom_mob_spawner/vore/e - mob_faction = "voree" - -/obj/random/mob/semirandom_mob_spawner/vore/f - mob_faction = "voref" - -/obj/random/mob/semirandom_mob_spawner/vore/passive - mob_faction = "pasvorea" - overwrite_hostility = 1 - mob_hostile = 0 - mob_retaliate = 0 - -/obj/random/mob/semirandom_mob_spawner/vore/passive/b - mob_faction = "pasvoreb" - -/obj/random/mob/semirandom_mob_spawner/vore/passive/c - mob_faction = "pasvorec" - -/obj/random/mob/semirandom_mob_spawner/vore/retaliate - mob_faction = "retvorea" - overwrite_hostility = 1 - mob_hostile = 0 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/vore/retaliate/b - mob_faction = "retvoreb" - -/obj/random/mob/semirandom_mob_spawner/vore/retaliate/c - mob_faction = "retvorec" - -/obj/random/mob/semirandom_mob_spawner/vore/hostile - mob_faction = "hosvorea" - overwrite_hostility = 1 - mob_hostile = 1 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/vore/hostile/b - mob_faction = "hosvoreb" - -/obj/random/mob/semirandom_mob_spawner/vore/hostile/c - mob_faction = "hosvorec" - -/obj/random/mob/semirandom_mob_spawner/sus/b - mob_faction = "susb" - -/obj/random/mob/semirandom_mob_spawner/sus/c - mob_faction = "susc" - -/obj/random/mob/semirandom_mob_spawner/sus/d - mob_faction = "susd" - -/obj/random/mob/semirandom_mob_spawner/sus/e - mob_faction = "suse" - -/obj/random/mob/semirandom_mob_spawner/sus/f - mob_faction = "susf" - -/obj/random/mob/semirandom_mob_spawner/mecha/b - mob_faction = "mechab" - -/obj/random/mob/semirandom_mob_spawner/mecha/c - mob_faction = "mechac" - -/obj/random/mob/semirandom_mob_spawner/mecha/d - mob_faction = "mechad" - -/obj/random/mob/semirandom_mob_spawner/mecha/e - mob_faction = "mechae" - -/obj/random/mob/semirandom_mob_spawner/mecha/f - mob_faction = "mechaf" - -/obj/random/mob/semirandom_mob_spawner/mecha/retaliate - mob_faction = "retmecha" - overwrite_hostility = 1 - mob_hostile = 0 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/mecha/retaliate/b - mob_faction = "retmechb" - -/obj/random/mob/semirandom_mob_spawner/mecha/retaliate/c - mob_faction = "retmechc" +var/global/list/semirandom_mob_spawner_decisions = list() + +/obj/random/mob/semirandom_mob_spawner + name = "Semi-Random Spawner" + desc = "Spawns groups of mobs that are all of the same theme type/theme." + icon = 'icons/mob/randomlandmarks.dmi' + icon_state = "monster" + mob_returns_home = 1 + mob_wander_distance = 7 + + var/list/possible_mob_types = list( + list(/mob/living/simple_mob/animal/goat), + list( + /mob/living/simple_mob/animal/passive/bird, + /mob/living/simple_mob/animal/passive/bird/azure_tit, + /mob/living/simple_mob/animal/passive/bird/black_bird, + /mob/living/simple_mob/animal/passive/bird/european_robin, + /mob/living/simple_mob/animal/passive/bird/goldcrest, + /mob/living/simple_mob/animal/passive/bird/ringneck_dove, + /mob/living/simple_mob/animal/passive/bird/parrot, + /mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique, + /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar, + /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue, + /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish, + /mob/living/simple_mob/animal/passive/bird/parrot/eclectus, + /mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot, + /mob/living/simple_mob/animal/passive/bird/parrot/kea, + /mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo, + /mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo, + /mob/living/simple_mob/animal/passive/bird/parrot/white_caique, + /mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo + ), + list( + /mob/living/simple_mob/animal/passive/cat, + /mob/living/simple_mob/animal/passive/cat/black + ), + list(/mob/living/simple_mob/animal/passive/chick), + list(/mob/living/simple_mob/animal/passive/cow), + list(/mob/living/simple_mob/animal/passive/dog/brittany), + list(/mob/living/simple_mob/animal/passive/dog/corgi), + list(/mob/living/simple_mob/animal/passive/dog/tamaskan), + list(/mob/living/simple_mob/animal/passive/fox), + list(/mob/living/simple_mob/animal/passive/hare), + list(/mob/living/simple_mob/animal/passive/lizard), + list(/mob/living/simple_mob/animal/passive/mouse), + list(/mob/living/simple_mob/animal/passive/mouse/jerboa), + list(/mob/living/simple_mob/animal/passive/opossum), + list(/mob/living/simple_mob/animal/passive/pillbug), + list(/mob/living/simple_mob/animal/passive/snake), + list(/mob/living/simple_mob/animal/passive/snake/red), + list(/mob/living/simple_mob/animal/passive/snake/python), + list(/mob/living/simple_mob/animal/passive/tindalos), + list(/mob/living/simple_mob/animal/passive/yithian), + list( + /mob/living/simple_mob/vore/wolf = 10, + /mob/living/simple_mob/vore/wolf/direwolf = 5, + /mob/living/simple_mob/vore/greatwolf = 1, + /mob/living/simple_mob/vore/greatwolf/black = 1, + /mob/living/simple_mob/vore/greatwolf/grey = 1 + ), + list(/mob/living/simple_mob/vore/rabbit), + list(/mob/living/simple_mob/vore/redpanda), + list(/mob/living/simple_mob/vore/woof), + list(/mob/living/simple_mob/vore/fennec), + list(/mob/living/simple_mob/vore/fennix), + list(/mob/living/simple_mob/vore/hippo), + list(/mob/living/simple_mob/vore/horse), + list(/mob/living/simple_mob/vore/bee), + list( + /mob/living/simple_mob/animal/space/bear, + /mob/living/simple_mob/animal/space/bear/brown + ), + list( + /mob/living/simple_mob/vore/otie/feral, + /mob/living/simple_mob/vore/otie/feral/chubby, + /mob/living/simple_mob/vore/otie/red, + /mob/living/simple_mob/vore/otie/red/chubby + ), + list(/mob/living/simple_mob/animal/sif/diyaab), + list(/mob/living/simple_mob/animal/sif/duck), + list(/mob/living/simple_mob/animal/sif/frostfly), + list( + /mob/living/simple_mob/animal/sif/glitterfly =50, + /mob/living/simple_mob/animal/sif/glitterfly/rare = 1 + ), + list( + /mob/living/simple_mob/animal/sif/kururak = 10, + /mob/living/simple_mob/animal/sif/kururak/leader = 1, + /mob/living/simple_mob/animal/sif/kururak/hibernate = 2, + ), + list( + /mob/living/simple_mob/animal/sif/sakimm = 10, + /mob/living/simple_mob/animal/sif/sakimm/intelligent = 1 + ), + list(/mob/living/simple_mob/animal/sif/savik) = 5, + list( + /mob/living/simple_mob/animal/sif/shantak = 10, + /mob/living/simple_mob/animal/sif/shantak/leader = 1 + ), + list(/mob/living/simple_mob/animal/sif/siffet), + list(/mob/living/simple_mob/animal/sif/tymisian), + list( + /mob/living/simple_mob/animal/giant_spider/electric = 5, + /mob/living/simple_mob/animal/giant_spider/frost = 5, + /mob/living/simple_mob/animal/giant_spider/hunter = 10, + /mob/living/simple_mob/animal/giant_spider/ion = 5, + /mob/living/simple_mob/animal/giant_spider/lurker = 10, + /mob/living/simple_mob/animal/giant_spider/pepper = 10, + /mob/living/simple_mob/animal/giant_spider/phorogenic = 10, + /mob/living/simple_mob/animal/giant_spider/thermic = 5, + /mob/living/simple_mob/animal/giant_spider/tunneler = 10, + /mob/living/simple_mob/animal/giant_spider/webslinger = 5, + /mob/living/simple_mob/animal/giant_spider/broodmother = 1), + list(/mob/living/simple_mob/creature/strong), + list(/mob/living/simple_mob/faithless/strong), + list(/mob/living/simple_mob/animal/goat), + list( + /mob/living/simple_mob/animal/sif/shantak/leader = 1, + /mob/living/simple_mob/animal/sif/shantak = 10), + list(/mob/living/simple_mob/animal/sif/savik,), + list(/mob/living/simple_mob/animal/sif/hooligan_crab), + list( + /mob/living/simple_mob/animal/space/alien = 50, + /mob/living/simple_mob/animal/space/alien/drone = 40, + /mob/living/simple_mob/animal/space/alien/sentinel = 25, + /mob/living/simple_mob/animal/space/alien/sentinel/praetorian = 15, + /mob/living/simple_mob/animal/space/alien/queen = 10, + /mob/living/simple_mob/animal/space/alien/queen/empress = 5, + /mob/living/simple_mob/animal/space/alien/queen/empress/mother = 1 + ), + list(/mob/living/simple_mob/animal/space/bats/cult/strong), + list( + /mob/living/simple_mob/animal/space/bear, + /mob/living/simple_mob/animal/space/bear/brown + ), + list( + /mob/living/simple_mob/animal/space/carp = 50, + /mob/living/simple_mob/animal/space/carp/large = 10, + /mob/living/simple_mob/animal/space/carp/large/huge = 5 + ), + list(/mob/living/simple_mob/animal/space/goose), + list(/mob/living/simple_mob/vore/jelly), + list(/mob/living/simple_mob/animal/space/tree), + list( + /mob/living/simple_mob/vore/aggressive/corrupthound = 10, + /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi = 1, + ), + list(/mob/living/simple_mob/vore/aggressive/deathclaw), + list(/mob/living/simple_mob/vore/aggressive/dino), + list(/mob/living/simple_mob/vore/aggressive/dragon), + list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b), + list(/mob/living/simple_mob/vore/aggressive/frog), + list(/mob/living/simple_mob/vore/aggressive/giant_snake), + list(/mob/living/simple_mob/vore/aggressive/mimic), + list(/mob/living/simple_mob/vore/aggressive/panther), + list(/mob/living/simple_mob/vore/aggressive/rat), + list(/mob/living/simple_mob/vore/bee), + list( + /mob/living/simple_mob/vore/sect_drone = 10, + /mob/living/simple_mob/vore/sect_queen = 1 + ), + list(/mob/living/simple_mob/vore/solargrub), + list( + /mob/living/simple_mob/vore/oregrub = 5, + /mob/living/simple_mob/vore/oregrub/lava = 1 + ), + list(/mob/living/simple_mob/vore/catgirl), + list(/mob/living/simple_mob/vore/wolfgirl), + list( + /mob/living/simple_mob/vore/lamia, + /mob/living/simple_mob/vore/lamia/albino, + /mob/living/simple_mob/vore/lamia/albino/bra, + /mob/living/simple_mob/vore/lamia/albino/shirt, + /mob/living/simple_mob/vore/lamia/bra, + /mob/living/simple_mob/vore/lamia/cobra, + /mob/living/simple_mob/vore/lamia/cobra/bra, + /mob/living/simple_mob/vore/lamia/cobra/shirt, + /mob/living/simple_mob/vore/lamia/copper, + /mob/living/simple_mob/vore/lamia/copper/bra, + /mob/living/simple_mob/vore/lamia/copper/shirt, + /mob/living/simple_mob/vore/lamia/green, + /mob/living/simple_mob/vore/lamia/green/bra, + /mob/living/simple_mob/vore/lamia/green/shirt, + /mob/living/simple_mob/vore/lamia/zebra, + /mob/living/simple_mob/vore/lamia/zebra/bra, + /mob/living/simple_mob/vore/lamia/zebra/shirt + ), + list( + /mob/living/simple_mob/humanoid/merc = 100, + /mob/living/simple_mob/humanoid/merc/melee/sword = 50, + /mob/living/simple_mob/humanoid/merc/ranged = 25, + /mob/living/simple_mob/humanoid/merc/ranged/grenadier = 1, + /mob/living/simple_mob/humanoid/merc/ranged/ionrifle = 10, + /mob/living/simple_mob/humanoid/merc/ranged/laser = 5, + /mob/living/simple_mob/humanoid/merc/ranged/rifle = 5, + /mob/living/simple_mob/humanoid/merc/ranged/smg = 5, + /mob/living/simple_mob/humanoid/merc/ranged/sniper = 1, + /mob/living/simple_mob/humanoid/merc/ranged/space = 10, + /mob/living/simple_mob/humanoid/merc/ranged/technician = 5 + ), + list( + /mob/living/simple_mob/humanoid/pirate = 3, + /mob/living/simple_mob/humanoid/pirate/ranged = 1 + ), + list(/mob/living/simple_mob/mechanical/combat_drone), + list(/mob/living/simple_mob/mechanical/corrupt_maint_drone), + list( + /mob/living/simple_mob/mechanical/hivebot = 100, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage = 20, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/backline = 10, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/basic = 20, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/dot = 5, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/ion = 20, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/laser = 10, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/rapid = 2, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege = 1, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/emp = 5, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/fragmentation = 1, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/radiation = 1, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong = 3, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong/guard = 3, + /mob/living/simple_mob/mechanical/hivebot/support = 8, + /mob/living/simple_mob/mechanical/hivebot/support/commander = 5, + /mob/living/simple_mob/mechanical/hivebot/support/commander/autofollow = 10, + /mob/living/simple_mob/mechanical/hivebot/swarm = 20, + /mob/living/simple_mob/mechanical/hivebot/tank = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_bullet = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_laser = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_melee = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/meatshield = 20 + ), + list(/mob/living/simple_mob/mechanical/infectionbot), + list(/mob/living/simple_mob/mechanical/mining_drone), + list(/mob/living/simple_mob/mechanical/technomancer_golem), + list( + /mob/living/simple_mob/mechanical/viscerator, + /mob/living/simple_mob/mechanical/viscerator/piercing + ), + list(/mob/living/simple_mob/mechanical/wahlem), + list(/mob/living/simple_mob/animal/passive/fox/syndicate), + list(/mob/living/simple_mob/animal/passive/fox), + list(/mob/living/simple_mob/vore/jelly), + list( + /mob/living/simple_mob/vore/otie/feral, + /mob/living/simple_mob/vore/otie/feral/chubby, + /mob/living/simple_mob/vore/otie/red, + /mob/living/simple_mob/vore/otie/red/chubby + ), + list( + /mob/living/simple_mob/shadekin/blue = 100, + /mob/living/simple_mob/shadekin/green = 50, + /mob/living/simple_mob/shadekin/orange = 20, + /mob/living/simple_mob/shadekin/purple = 60, + /mob/living/simple_mob/shadekin/red = 40, + /mob/living/simple_mob/shadekin/yellow = 1 + ), + list( + /mob/living/simple_mob/vore/aggressive/corrupthound, + /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi + ), + list(/mob/living/simple_mob/vore/aggressive/deathclaw), + list(/mob/living/simple_mob/vore/aggressive/dino), + list(/mob/living/simple_mob/vore/aggressive/dragon), + list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b), + list(/mob/living/simple_mob/vore/aggressive/frog), + list(/mob/living/simple_mob/vore/aggressive/giant_snake), + list(/mob/living/simple_mob/vore/aggressive/mimic), + list(/mob/living/simple_mob/vore/aggressive/panther), + list(/mob/living/simple_mob/vore/aggressive/rat), + list(/mob/living/simple_mob/vore/bee), + list(/mob/living/simple_mob/vore/catgirl), + list(/mob/living/simple_mob/vore/cookiegirl), + list(/mob/living/simple_mob/vore/fennec), + list(/mob/living/simple_mob/vore/fennix), + list(/mob/living/simple_mob/vore/hippo), + list(/mob/living/simple_mob/vore/horse), + list(/mob/living/simple_mob/vore/oregrub), + list(/mob/living/simple_mob/vore/rabbit), + list( + /mob/living/simple_mob/vore/redpanda = 50, + /mob/living/simple_mob/vore/redpanda/fae = 1 + ), + list( + /mob/living/simple_mob/vore/sect_drone = 10, + /mob/living/simple_mob/vore/sect_queen = 1 + ), + list(/mob/living/simple_mob/vore/solargrub), + list(/mob/living/simple_mob/vore/woof), + list(/mob/living/simple_mob/vore/alienanimals/space_ghost), + list(/mob/living/simple_mob/vore/alienanimals/catslug), + list(/mob/living/simple_mob/vore/alienanimals/space_jellyfish), + list(/mob/living/simple_mob/vore/alienanimals/startreader), + list( + /mob/living/simple_mob/vore/bigdragon, + /mob/living/simple_mob/vore/bigdragon/friendly), + list( + /mob/living/simple_mob/vore/leopardmander = 50, + /mob/living/simple_mob/vore/leopardmander/blue = 10, + /mob/living/simple_mob/vore/leopardmander/exotic = 1 + ), + list(/mob/living/simple_mob/vore/sheep), + list(/mob/living/simple_mob/vore/weretiger) + ) + +/obj/random/mob/semirandom_mob_spawner/item_to_spawn() + var/list/choice = semirandom_mob_spawner_decisions[type] + + if(!choice) + choice = pickweight(possible_mob_types) + semirandom_mob_spawner_decisions[type] = choice + + return pickweight(choice) + +/obj/random/mob/semirandom_mob_spawner/animal + name = "Semi-Random Animal" + desc = "Spawns groups of non-hostile mobs that are all of the same theme type/theme." + icon_state = "animal" + mob_faction = "animal" + overwrite_hostility = 1 + mob_hostile = 0 + + possible_mob_types = list( + list(/mob/living/simple_mob/animal/goat) = 25, + list( + /mob/living/simple_mob/animal/passive/bird, + /mob/living/simple_mob/animal/passive/bird/azure_tit, + /mob/living/simple_mob/animal/passive/bird/black_bird, + /mob/living/simple_mob/animal/passive/bird/european_robin, + /mob/living/simple_mob/animal/passive/bird/goldcrest, + /mob/living/simple_mob/animal/passive/bird/ringneck_dove, + /mob/living/simple_mob/animal/passive/bird/parrot, + /mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique, + /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar, + /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue, + /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish, + /mob/living/simple_mob/animal/passive/bird/parrot/eclectus, + /mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot, + /mob/living/simple_mob/animal/passive/bird/parrot/kea, + /mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo, + /mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo, + /mob/living/simple_mob/animal/passive/bird/parrot/white_caique, + /mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo, + /mob/living/simple_mob/animal/space/goose + ) = 25, + list( + /mob/living/simple_mob/animal/passive/cat, + /mob/living/simple_mob/animal/passive/cat/black + ) = 25, + list( + /mob/living/simple_mob/animal/passive/chick, + /mob/living/simple_mob/animal/passive/chicken + ) = 25, + list(/mob/living/simple_mob/animal/passive/cow) = 25, + list(/mob/living/simple_mob/animal/passive/dog/brittany) = 10, + list(/mob/living/simple_mob/animal/passive/dog/corgi) = 10, + list(/mob/living/simple_mob/animal/passive/dog/tamaskan) = 10, + list(/mob/living/simple_mob/animal/passive/fox) = 25, + list(/mob/living/simple_mob/animal/passive/hare) = 25, + list(/mob/living/simple_mob/animal/passive/lizard) = 10, + list(/mob/living/simple_mob/animal/passive/mouse) = 15, + list(/mob/living/simple_mob/animal/passive/mouse/jerboa) = 5, + list(/mob/living/simple_mob/animal/passive/opossum) = 10, + list(/mob/living/simple_mob/animal/passive/pillbug) = 10, + list(/mob/living/simple_mob/animal/passive/snake) = 10, + list(/mob/living/simple_mob/animal/passive/snake/red) = 10, + list(/mob/living/simple_mob/animal/passive/snake/python) = 10, + list(/mob/living/simple_mob/animal/passive/tindalos) = 10, + list(/mob/living/simple_mob/animal/passive/yithian) = 10, + list( + /mob/living/simple_mob/vore/wolf = 10, + /mob/living/simple_mob/vore/wolf/direwolf = 5, + /mob/living/simple_mob/vore/greatwolf = 1, + /mob/living/simple_mob/vore/greatwolf/black = 1, + /mob/living/simple_mob/vore/greatwolf/grey = 1 + ) = 10, + list(/mob/living/simple_mob/vore/rabbit) = 10, + list(/mob/living/simple_mob/vore/redpanda) = 10, + list(/mob/living/simple_mob/vore/woof) = 1, + list(/mob/living/simple_mob/vore/fennec) = 10, + list(/mob/living/simple_mob/vore/fennix) = 1, + list(/mob/living/simple_mob/vore/hippo) = 5, + list(/mob/living/simple_mob/vore/horse) = 25, + list(/mob/living/simple_mob/vore/bee) = 10, + list( + /mob/living/simple_mob/animal/space/bear, + /mob/living/simple_mob/animal/space/bear/brown + ) = 1, + list( + /mob/living/simple_mob/vore/otie/feral = 50, + /mob/living/simple_mob/vore/otie/feral/chubby = 10, + /mob/living/simple_mob/vore/otie/red = 5, + /mob/living/simple_mob/vore/otie/red/chubby = 1 + ) = 5, + list(/mob/living/simple_mob/vore/aggressive/rat) = 15, + list(/mob/living/simple_mob/animal/sif/diyaab) = 5, + list(/mob/living/simple_mob/animal/sif/duck) = 5, + list(/mob/living/simple_mob/animal/sif/frostfly) = 5, + list( + /mob/living/simple_mob/animal/sif/glitterfly = 50, + /mob/living/simple_mob/animal/sif/glitterfly/rare = 1 + ) = 5, + list( + /mob/living/simple_mob/animal/sif/kururak = 10, + /mob/living/simple_mob/animal/sif/kururak/leader = 1, + /mob/living/simple_mob/animal/sif/kururak/hibernate = 2, + ) = 5, + list( + /mob/living/simple_mob/animal/sif/sakimm = 10, + /mob/living/simple_mob/animal/sif/sakimm/intelligent = 1 + ) = 5, + list(/mob/living/simple_mob/animal/sif/savik) = 5, + list( + /mob/living/simple_mob/animal/sif/shantak = 10, + /mob/living/simple_mob/animal/sif/shantak/leader = 1 + ) = 5, + list(/mob/living/simple_mob/animal/sif/siffet) = 5, + list(/mob/living/simple_mob/animal/sif/tymisian) = 5, + list(/mob/living/simple_mob/vore/alienanimals/dustjumper) = 5, + list(/mob/living/simple_mob/vore/alienanimals/space_jellyfish) = 5, + list(/mob/living/simple_mob/vore/alienanimals/space_ghost) = 5, + list( + /mob/living/simple_mob/vore/leopardmander = 50, + /mob/living/simple_mob/vore/leopardmander/blue = 10, + /mob/living/simple_mob/vore/leopardmander/exotic = 1 + ) = 5, + list(/mob/living/simple_mob/vore/sheep) = 5, + list(/mob/living/simple_mob/vore/weretiger) = 5, + list(/mob/living/simple_mob/vore/alienanimals/skeleton) = 5 + ) + +/obj/random/mob/semirandom_mob_spawner/monster + name = "Semi-Random Monster" + desc = "Spawns groups of hostile mobs that are all of the same theme type/theme." + overwrite_hostility = 1 + mob_faction = "monster" + mob_hostile = 1 + mob_retaliate = 1 + + possible_mob_types = list( + list( + /mob/living/simple_mob/animal/giant_spider/electric = 5, + /mob/living/simple_mob/animal/giant_spider/frost = 5, + /mob/living/simple_mob/animal/giant_spider/hunter = 10, + /mob/living/simple_mob/animal/giant_spider/ion = 5, + /mob/living/simple_mob/animal/giant_spider/lurker = 10, + /mob/living/simple_mob/animal/giant_spider/pepper = 10, + /mob/living/simple_mob/animal/giant_spider/phorogenic = 10, + /mob/living/simple_mob/animal/giant_spider/thermic = 5, + /mob/living/simple_mob/animal/giant_spider/tunneler = 10, + /mob/living/simple_mob/animal/giant_spider/webslinger = 5 + ) = 100, + list( + /mob/living/simple_mob/shadekin/red = 5, + /mob/living/simple_mob/shadekin/orange = 1, + /mob/living/simple_mob/shadekin/purple = 10 + ) = 1, + list( + /mob/living/simple_mob/vore/wolf = 10, + /mob/living/simple_mob/vore/wolf/direwolf = 5, + /mob/living/simple_mob/vore/greatwolf = 1, + /mob/living/simple_mob/vore/greatwolf/black = 1, + /mob/living/simple_mob/vore/greatwolf/grey = 1 + ) = 40, + list(/mob/living/simple_mob/creature/strong) = 40, + list(/mob/living/simple_mob/faithless/strong) = 20, + list(/mob/living/simple_mob/animal/goat) = 1, + list( + /mob/living/simple_mob/animal/sif/shantak/leader = 1, + /mob/living/simple_mob/animal/sif/shantak = 10 + ) = 50, + list(/mob/living/simple_mob/animal/sif/savik,) = 20, + list(/mob/living/simple_mob/animal/sif/hooligan_crab) = 10, + list( + /mob/living/simple_mob/animal/space/alien = 50, + /mob/living/simple_mob/animal/space/alien/drone = 40, + /mob/living/simple_mob/animal/space/alien/sentinel = 25, + /mob/living/simple_mob/animal/space/alien/sentinel/praetorian = 15, + /mob/living/simple_mob/animal/space/alien/queen = 10, + /mob/living/simple_mob/animal/space/alien/queen/empress = 5, + /mob/living/simple_mob/animal/space/alien/queen/empress/mother = 1, + ) = 40, + list(/mob/living/simple_mob/animal/space/bats/cult/strong) = 40, + list( + /mob/living/simple_mob/animal/space/bear, + /mob/living/simple_mob/animal/space/bear/brown + ) = 40, + list( + /mob/living/simple_mob/animal/space/carp = 50, + /mob/living/simple_mob/animal/space/carp/large = 10, + /mob/living/simple_mob/animal/space/carp/large/huge = 5 + ) = 50, + list(/mob/living/simple_mob/animal/space/goose) = 50, + list(/mob/living/simple_mob/vore/jelly) = 40, + list(/mob/living/simple_mob/animal/space/tree) = 15, + list( + /mob/living/simple_mob/vore/otie/feral = 50, + /mob/living/simple_mob/vore/otie/feral/chubby = 10, + /mob/living/simple_mob/vore/otie/red = 5, + /mob/living/simple_mob/vore/otie/red/chubby = 1 + ) = 40, + list( + /mob/living/simple_mob/vore/aggressive/corrupthound = 10, + /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi = 1, + ) = 50, + list(/mob/living/simple_mob/vore/aggressive/deathclaw) = 40, + list(/mob/living/simple_mob/vore/aggressive/dino) = 40, + list(/mob/living/simple_mob/vore/aggressive/dragon) = 40, + list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b) = 40, + list(/mob/living/simple_mob/vore/aggressive/frog) = 40, + list(/mob/living/simple_mob/vore/aggressive/giant_snake) = 40, + list(/mob/living/simple_mob/vore/aggressive/mimic) = 40, + list(/mob/living/simple_mob/vore/aggressive/panther) = 25, + list(/mob/living/simple_mob/vore/aggressive/rat) = 50, + list(/mob/living/simple_mob/vore/bee) = 40, + list( + /mob/living/simple_mob/vore/sect_drone = 10, + /mob/living/simple_mob/vore/sect_queen = 1 + ) = 20, + list(/mob/living/simple_mob/vore/solargrub) = 15, + list( + /mob/living/simple_mob/vore/oregrub = 5, + /mob/living/simple_mob/vore/oregrub/lava = 1 + ) = 15, + list(/mob/living/simple_mob/vore/alienanimals/space_jellyfish) = 5, + list(/mob/living/simple_mob/vore/alienanimals/space_ghost) = 5, + list( + /mob/living/simple_mob/vore/leopardmander = 50, + /mob/living/simple_mob/vore/leopardmander/blue = 10, + /mob/living/simple_mob/vore/leopardmander/exotic = 1 + ) = 5, + list(/mob/living/simple_mob/vore/sheep) = 5, + list(/mob/living/simple_mob/vore/weretiger) = 5, + list(/mob/living/simple_mob/vore/alienanimals/skeleton) = 5, + list(/mob/living/simple_mob/vore/alienanimals/catslug) = 5 + ) + +/obj/random/mob/semirandom_mob_spawner/humanoid + name = "Semi-Random Humanoid" + desc = "Spawns groups of humanoid mobs that may or may not be hostile, all of the same theme type/theme." + icon_state = "humanoid" + mob_faction = "humanoid" + + possible_mob_types = list( + list( + /mob/living/simple_mob/shadekin/blue = 25, + /mob/living/simple_mob/shadekin/green = 10, + /mob/living/simple_mob/shadekin/purple = 1, + ) = 1, + list(/mob/living/simple_mob/vore/catgirl) = 100, + list(/mob/living/simple_mob/vore/wolfgirl) = 100, + list( + /mob/living/simple_mob/vore/lamia, + /mob/living/simple_mob/vore/lamia/albino, + /mob/living/simple_mob/vore/lamia/albino/bra, + /mob/living/simple_mob/vore/lamia/albino/shirt, + /mob/living/simple_mob/vore/lamia/bra, + /mob/living/simple_mob/vore/lamia/cobra, + /mob/living/simple_mob/vore/lamia/cobra/bra, + /mob/living/simple_mob/vore/lamia/cobra/shirt, + /mob/living/simple_mob/vore/lamia/copper, + /mob/living/simple_mob/vore/lamia/copper/bra, + /mob/living/simple_mob/vore/lamia/copper/shirt, + /mob/living/simple_mob/vore/lamia/green, + /mob/living/simple_mob/vore/lamia/green/bra, + /mob/living/simple_mob/vore/lamia/green/shirt, + /mob/living/simple_mob/vore/lamia/zebra, + /mob/living/simple_mob/vore/lamia/zebra/bra, + /mob/living/simple_mob/vore/lamia/zebra/shirt + ) = 100, +// LOOK OKAY MERCS ARE HUMANOIDS SO THEY ARE HERE, but they are also kind of bullshit so they probably shouldn't be able to spawn in the same place as catgirls. +// I want some better potentially hostile humanoids that aren't stupid to fight. If they become a big issue I'll comment them out. +// For now they are just rare, and the ranged ones are way more rare than the melee ones, which I think will help balance them out. + list( + /mob/living/simple_mob/humanoid/merc = 100, + /mob/living/simple_mob/humanoid/merc/melee/sword = 50, + /mob/living/simple_mob/humanoid/merc/ranged = 25, + /mob/living/simple_mob/humanoid/merc/ranged/grenadier = 1, + /mob/living/simple_mob/humanoid/merc/ranged/ionrifle = 10, + /mob/living/simple_mob/humanoid/merc/ranged/laser = 5, + /mob/living/simple_mob/humanoid/merc/ranged/rifle = 5, + /mob/living/simple_mob/humanoid/merc/ranged/smg = 5, + /mob/living/simple_mob/humanoid/merc/ranged/sniper = 1, + /mob/living/simple_mob/humanoid/merc/ranged/space = 10, + /mob/living/simple_mob/humanoid/merc/ranged/technician = 5 + ) = 5, +// PIRATES are okay though. They can be kind of a pain but you can kind of slap them around. Also it's not like. A crime. To fight and blow up pirates so it's fine. + list( + /mob/living/simple_mob/humanoid/pirate = 3, + /mob/living/simple_mob/humanoid/pirate/ranged = 1 + ) = 50 + ) + +// I am not familiar enough with robots to know which ones are fun to fight so this list isn't weighted at all SO YOU KNOW. Be careful. +/obj/random/mob/semirandom_mob_spawner/robot + name = "Semi-Random Robot" + desc = "Spawns groups of robotic mobs that are probably hostile, all of the same theme type/theme." + icon_state = "robot" + mob_faction = "robot" + + possible_mob_types = list( + list(/mob/living/simple_mob/mechanical/combat_drone), + list(/mob/living/simple_mob/mechanical/corrupt_maint_drone), + list( + /mob/living/simple_mob/mechanical/hivebot = 100, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage = 20, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/backline = 10, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/basic = 20, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/dot = 5, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/ion = 20, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/laser = 10, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/rapid = 2, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege = 1, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/emp = 5, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/fragmentation = 1, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/radiation = 1, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong = 3, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong/guard = 3, + /mob/living/simple_mob/mechanical/hivebot/support = 8, + /mob/living/simple_mob/mechanical/hivebot/support/commander = 5, + /mob/living/simple_mob/mechanical/hivebot/support/commander/autofollow = 10, + /mob/living/simple_mob/mechanical/hivebot/swarm = 20, + /mob/living/simple_mob/mechanical/hivebot/tank = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_bullet = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_laser = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_melee = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/meatshield = 20 + ), + list(/mob/living/simple_mob/mechanical/infectionbot), + list(/mob/living/simple_mob/mechanical/mining_drone), + list(/mob/living/simple_mob/mechanical/technomancer_golem), + list( + /mob/living/simple_mob/mechanical/viscerator, + /mob/living/simple_mob/mechanical/viscerator/piercing + ), + list(/mob/living/simple_mob/mechanical/wahlem), + list(/mob/living/simple_mob/animal/passive/fox/syndicate) + ) + +/obj/random/mob/semirandom_mob_spawner/fish + name = "Semi-Random Fish" + desc = "Spawns groups of fish, all of the same theme type/theme." + icon_state = "fish" + mob_faction = "fish" + overwrite_hostility = 1 + mob_hostile = 0 + mob_retaliate = 0 + + + possible_mob_types = list( + list(/mob/living/simple_mob/animal/passive/fish/bass) = 20, + list(/mob/living/simple_mob/animal/passive/fish/icebass) = 20, + list(/mob/living/simple_mob/animal/passive/fish/javelin) = 20, + list(/mob/living/simple_mob/animal/passive/fish/koi) = 10, + list(/mob/living/simple_mob/animal/passive/fish/measelshark) = 5, + list(/mob/living/simple_mob/animal/passive/fish/murkin) = 20, + list(/mob/living/simple_mob/animal/passive/fish/perch) = 20, + list(/mob/living/simple_mob/animal/passive/fish/pike) = 20, + list(/mob/living/simple_mob/animal/passive/fish/rockfish) = 10, + list(/mob/living/simple_mob/animal/passive/fish/salmon) = 20, + list(/mob/living/simple_mob/animal/passive/fish/solarfish) = 5, + list(/mob/living/simple_mob/animal/passive/fish/trout) = 20, + list(/mob/living/simple_mob/animal/passive/crab) = 10, + list(/mob/living/simple_mob/animal/sif/hooligan_crab) = 1 + ) + +/obj/random/mob/semirandom_mob_spawner/bird + name = "Semi-Random Bird" + desc = "Spawns groups of bird, all of the same theme type/theme." + icon_state = "bird" + mob_faction = "bird" + + possible_mob_types = list( + list(/mob/living/simple_mob/animal/passive/bird), + list(/mob/living/simple_mob/animal/passive/bird/azure_tit), + list(/mob/living/simple_mob/animal/passive/bird/black_bird), + list(/mob/living/simple_mob/animal/passive/bird/european_robin), + list(/mob/living/simple_mob/animal/passive/bird/goldcrest), + list(/mob/living/simple_mob/animal/passive/bird/ringneck_dove), + list(/mob/living/simple_mob/animal/passive/bird/parrot), + list(/mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique), + list(/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar), + list(/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue), + list(/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen), + list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel), + list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey), + list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white), + list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish), + list(/mob/living/simple_mob/animal/passive/bird/parrot/eclectus), + list(/mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot), + list(/mob/living/simple_mob/animal/passive/bird/parrot/kea), + list(/mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo), + list(/mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo), + list(/mob/living/simple_mob/animal/passive/bird/parrot/white_caique), + list(/mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo), + list(/mob/living/simple_mob/animal/space/goose), + list(/mob/living/simple_mob/animal/passive/chicken), + list(/mob/living/simple_mob/animal/passive/penguin) + ) + +/obj/random/mob/semirandom_mob_spawner/vore + name = "Semi-Random Voremob" + desc = "Spawns groups of voremobs, all of the same theme type/theme." + icon_state = "vore" + mob_faction = "vore" + + possible_mob_types = list( + list( + /mob/living/simple_mob/vore/wolf/direwolf = 5, + /mob/living/simple_mob/vore/greatwolf = 1, + /mob/living/simple_mob/vore/greatwolf/black = 1, + /mob/living/simple_mob/vore/greatwolf/grey = 1 + ) = 100, + list(/mob/living/simple_mob/vore/jelly) = 70, + list( + /mob/living/simple_mob/vore/otie/feral, + /mob/living/simple_mob/vore/otie/feral/chubby, + /mob/living/simple_mob/vore/otie/red, + /mob/living/simple_mob/vore/otie/red/chubby + ) = 50, + list( + /mob/living/simple_mob/shadekin/blue = 100, + /mob/living/simple_mob/shadekin/green = 50, + /mob/living/simple_mob/shadekin/orange = 20, + /mob/living/simple_mob/shadekin/purple = 60, + /mob/living/simple_mob/shadekin/red = 40, + /mob/living/simple_mob/shadekin/yellow = 1 + ) = 1, + list( + /mob/living/simple_mob/vore/aggressive/corrupthound, + /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi + ) = 70, + list(/mob/living/simple_mob/vore/aggressive/deathclaw) = 70, + list(/mob/living/simple_mob/vore/aggressive/dino) = 100, + list(/mob/living/simple_mob/vore/aggressive/dragon) = 100, + list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b) = 100, + list(/mob/living/simple_mob/vore/aggressive/frog) = 100, + list(/mob/living/simple_mob/vore/aggressive/giant_snake) = 100, + list(/mob/living/simple_mob/vore/aggressive/mimic) = 50, + list(/mob/living/simple_mob/vore/aggressive/panther) = 70, + list(/mob/living/simple_mob/vore/aggressive/rat) = 100, + list(/mob/living/simple_mob/vore/bee) = 100, + list(/mob/living/simple_mob/vore/catgirl) = 100, + list(/mob/living/simple_mob/vore/wolftaur) = 100, + list(/mob/living/simple_mob/vore/cookiegirl) = 100, + list(/mob/living/simple_mob/vore/fennec) = 100, + list(/mob/living/simple_mob/vore/fennix) = 50, + list(/mob/living/simple_mob/vore/hippo) = 70, + list(/mob/living/simple_mob/vore/horse) = 100, + list(/mob/living/simple_mob/vore/raptor) = 100, + list(/mob/living/simple_mob/vore/succubus) = 100, + list(/mob/living/simple_mob/vore/vampire) = 50, + list(/mob/living/simple_mob/vore/vampire/queen) = 1, + list(/mob/living/simple_mob/vore/bat) = 50, + list(/mob/living/simple_mob/vore/scel) = 10, + list( + /mob/living/simple_mob/vore/lamia, + /mob/living/simple_mob/vore/lamia/albino, + /mob/living/simple_mob/vore/lamia/albino/bra, + /mob/living/simple_mob/vore/lamia/albino/shirt, + /mob/living/simple_mob/vore/lamia/bra, + /mob/living/simple_mob/vore/lamia/cobra, + /mob/living/simple_mob/vore/lamia/cobra/bra, + /mob/living/simple_mob/vore/lamia/cobra/shirt, + /mob/living/simple_mob/vore/lamia/copper, + /mob/living/simple_mob/vore/lamia/copper/bra, + /mob/living/simple_mob/vore/lamia/copper/shirt, + /mob/living/simple_mob/vore/lamia/green, + /mob/living/simple_mob/vore/lamia/green/bra, + /mob/living/simple_mob/vore/lamia/green/shirt, + /mob/living/simple_mob/vore/lamia/zebra, + /mob/living/simple_mob/vore/lamia/zebra/bra, + /mob/living/simple_mob/vore/lamia/zebra/shirt + ) = 100, + list(/mob/living/simple_mob/vore/rabbit) = 100, + list( + /mob/living/simple_mob/vore/redpanda = 50, + /mob/living/simple_mob/vore/redpanda/fae = 1 + ) = 100, + list( + /mob/living/simple_mob/vore/sect_drone = 10, + /mob/living/simple_mob/vore/sect_queen = 1 + ) = 50, + list(/mob/living/simple_mob/vore/solargrub) = 100, + list(/mob/living/simple_mob/vore/woof) = 1 + ) + +/obj/random/mob/semirandom_mob_spawner/sus + name = "Weird shit" + desc = "Spawns groups of weird stuff, all of the same theme type/theme. Don't put this on normal maps." + icon_state = "sus" + mob_faction = "sus" + + possible_mob_types = list( + list( + /mob/living/simple_mob/vore/woof/hostile/melee = 100, + /mob/living/simple_mob/vore/woof/hostile/ranged = 20, + /mob/living/simple_mob/vore/woof/hostile/horrible = 10, + /mob/living/simple_mob/vore/woof/hostile/terrible = 5, + /mob/living/simple_mob/vore/woof/cass = 1 + ), + list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/recursive) + ) + +/obj/random/mob/semirandom_mob_spawner/mecha + name = "Semi-Random Mecha" + desc = "Spawns groups of mechs, all of the same theme type/theme. Don't put this on normal maps." + icon_state = "mecha" + mob_faction = "mecha" + + possible_mob_types = list( + list(/mob/living/simple_mob/mechanical/mecha/combat/durand), + list(/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive), + list(/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive/mercenary), + list(/mob/living/simple_mob/mechanical/mecha/combat/gygax), + list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark), + list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced), + list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/manned), + list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/medgax), + list(/mob/living/simple_mob/mechanical/mecha/combat/marauder), + list(/mob/living/simple_mob/mechanical/mecha/combat/marauder/mauler), + list(/mob/living/simple_mob/mechanical/mecha/combat/marauder/seraph), + list(/mob/living/simple_mob/mechanical/mecha/combat/phazon), + list(/mob/living/simple_mob/mechanical/mecha/hoverpod), + list(/mob/living/simple_mob/mechanical/mecha/hoverpod/manned), + list(/mob/living/simple_mob/mechanical/mecha/odysseus), + list(/mob/living/simple_mob/mechanical/mecha/odysseus/manned), + list(/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus), + list(/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus/manned), + list(/mob/living/simple_mob/mechanical/mecha/ripley), + list(/mob/living/simple_mob/mechanical/mecha/ripley/blue_flames), + list(/mob/living/simple_mob/mechanical/mecha/ripley/deathripley), + list(/mob/living/simple_mob/mechanical/mecha/ripley/deathripley/manned), + list(/mob/living/simple_mob/mechanical/mecha/ripley/firefighter), + list(/mob/living/simple_mob/mechanical/mecha/ripley/firefighter/manned), + list(/mob/living/simple_mob/mechanical/mecha/ripley/manned), + list(/mob/living/simple_mob/mechanical/mecha/ripley/red_flames) + ) + + +/obj/random/mob/semirandom_mob_spawner/monster/b + mob_faction = "monsterb" + +/obj/random/mob/semirandom_mob_spawner/monster/c + mob_faction = "monsterc" + +/obj/random/mob/semirandom_mob_spawner/monster/d + mob_faction = "monsterd" + +/obj/random/mob/semirandom_mob_spawner/monster/e + mob_faction = "monstere" + +/obj/random/mob/semirandom_mob_spawner/monster/f + mob_faction = "monsterf" + +/obj/random/mob/semirandom_mob_spawner/animal/b + mob_faction = "animalb" + +/obj/random/mob/semirandom_mob_spawner/animal/c + mob_faction = "animalc" + +/obj/random/mob/semirandom_mob_spawner/animal/d + mob_faction = "animald" + +/obj/random/mob/semirandom_mob_spawner/animal/e + mob_faction = "animale" + +/obj/random/mob/semirandom_mob_spawner/animal/f + mob_faction = "animalf" + +/obj/random/mob/semirandom_mob_spawner/animal/retaliate + mob_faction = "retanimala" + overwrite_hostility = 1 + mob_hostile = 0 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/animal/retaliate/b + mob_faction = "retanimalb" + +/obj/random/mob/semirandom_mob_spawner/animal/retaliate/c + mob_faction = "retanimalc" + +/obj/random/mob/semirandom_mob_spawner/animal/hostile + mob_faction = "hosanimala" + overwrite_hostility = 1 + mob_hostile = 1 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/animal/hostile/b + mob_faction = "hosanimalb" + +/obj/random/mob/semirandom_mob_spawner/animal/hostile/c + mob_faction = "hosanimalc" + + +/obj/random/mob/semirandom_mob_spawner/humanoid/b + mob_faction = "humanoidb" + +/obj/random/mob/semirandom_mob_spawner/humanoid/c + mob_faction = "humanoidc" + +/obj/random/mob/semirandom_mob_spawner/humanoid/d + mob_faction = "humanoidd" + +/obj/random/mob/semirandom_mob_spawner/humanoid/e + mob_faction = "humanoide" + +/obj/random/mob/semirandom_mob_spawner/humanoid/f + mob_faction = "humanoidf" + +/obj/random/mob/semirandom_mob_spawner/humanoid/retaliate + mob_faction = "rethumanoida" + overwrite_hostility = 1 + mob_hostile = 0 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/humanoid/retaliate/b + mob_faction = "rethumanoidb" + +/obj/random/mob/semirandom_mob_spawner/humanoid/retaliate/c + mob_faction = "rethumanoidc" + +/obj/random/mob/semirandom_mob_spawner/humanoid/hostile + mob_faction = "hoshumanoida" + overwrite_hostility = 1 + mob_hostile = 1 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/humanoid/hostile/b + mob_faction = "hoshumanoidb" + +/obj/random/mob/semirandom_mob_spawner/humanoid/hostile/c + mob_faction = "hoshumanoidc" + +/obj/random/mob/semirandom_mob_spawner/robot/b + mob_faction = "robotb" + +/obj/random/mob/semirandom_mob_spawner/robot/c + mob_faction = "robotc" + +/obj/random/mob/semirandom_mob_spawner/robot/d + mob_faction = "robotd" + +/obj/random/mob/semirandom_mob_spawner/robot/e + mob_faction = "robote" + +/obj/random/mob/semirandom_mob_spawner/robot/f + mob_faction = "robotf" + +/obj/random/mob/semirandom_mob_spawner/robot/retaliate + mob_faction = "retrobota" + overwrite_hostility = 1 + mob_hostile = 0 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/robot/retaliate/b + mob_faction = "retrobotb" + +/obj/random/mob/semirandom_mob_spawner/robot/retaliate/c + mob_faction = "retrobotc" + +/obj/random/mob/semirandom_mob_spawner/bird/b + mob_faction = "birdb" + +/obj/random/mob/semirandom_mob_spawner/bird/c + mob_faction = "birdc" + +/obj/random/mob/semirandom_mob_spawner/bird/d + mob_faction = "birdd" + +/obj/random/mob/semirandom_mob_spawner/bird/e + mob_faction = "birde" + +/obj/random/mob/semirandom_mob_spawner/bird/f + mob_faction = "birdf" + +/obj/random/mob/semirandom_mob_spawner/fish/b + mob_faction = "fishb" + +/obj/random/mob/semirandom_mob_spawner/fish/c + mob_faction = "fishc" + +/obj/random/mob/semirandom_mob_spawner/fish/d + mob_faction = "fishd" + +/obj/random/mob/semirandom_mob_spawner/fish/e + mob_faction = "fishe" + +/obj/random/mob/semirandom_mob_spawner/fish/f + mob_faction = "fishf" + +/obj/random/mob/semirandom_mob_spawner/vore/b + mob_faction = "voreb" + +/obj/random/mob/semirandom_mob_spawner/vore/c + mob_faction = "vorec" + +/obj/random/mob/semirandom_mob_spawner/vore/d + mob_faction = "vored" + +/obj/random/mob/semirandom_mob_spawner/vore/e + mob_faction = "voree" + +/obj/random/mob/semirandom_mob_spawner/vore/f + mob_faction = "voref" + +/obj/random/mob/semirandom_mob_spawner/vore/passive + mob_faction = "pasvorea" + overwrite_hostility = 1 + mob_hostile = 0 + mob_retaliate = 0 + +/obj/random/mob/semirandom_mob_spawner/vore/passive/b + mob_faction = "pasvoreb" + +/obj/random/mob/semirandom_mob_spawner/vore/passive/c + mob_faction = "pasvorec" + +/obj/random/mob/semirandom_mob_spawner/vore/retaliate + mob_faction = "retvorea" + overwrite_hostility = 1 + mob_hostile = 0 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/vore/retaliate/b + mob_faction = "retvoreb" + +/obj/random/mob/semirandom_mob_spawner/vore/retaliate/c + mob_faction = "retvorec" + +/obj/random/mob/semirandom_mob_spawner/vore/hostile + mob_faction = "hosvorea" + overwrite_hostility = 1 + mob_hostile = 1 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/vore/hostile/b + mob_faction = "hosvoreb" + +/obj/random/mob/semirandom_mob_spawner/vore/hostile/c + mob_faction = "hosvorec" + +/obj/random/mob/semirandom_mob_spawner/sus/b + mob_faction = "susb" + +/obj/random/mob/semirandom_mob_spawner/sus/c + mob_faction = "susc" + +/obj/random/mob/semirandom_mob_spawner/sus/d + mob_faction = "susd" + +/obj/random/mob/semirandom_mob_spawner/sus/e + mob_faction = "suse" + +/obj/random/mob/semirandom_mob_spawner/sus/f + mob_faction = "susf" + +/obj/random/mob/semirandom_mob_spawner/mecha/b + mob_faction = "mechab" + +/obj/random/mob/semirandom_mob_spawner/mecha/c + mob_faction = "mechac" + +/obj/random/mob/semirandom_mob_spawner/mecha/d + mob_faction = "mechad" + +/obj/random/mob/semirandom_mob_spawner/mecha/e + mob_faction = "mechae" + +/obj/random/mob/semirandom_mob_spawner/mecha/f + mob_faction = "mechaf" + +/obj/random/mob/semirandom_mob_spawner/mecha/retaliate + mob_faction = "retmecha" + overwrite_hostility = 1 + mob_hostile = 0 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/mecha/retaliate/b + mob_faction = "retmechb" + +/obj/random/mob/semirandom_mob_spawner/mecha/retaliate/c + mob_faction = "retmechc" diff --git a/code/game/objects/effects/spawners/bombspawner.dm b/code/game/objects/effects/spawners/bombspawner.dm index c7fc6962ba1..b06ec311ad5 100644 --- a/code/game/objects/effects/spawners/bombspawner.dm +++ b/code/game/objects/effects/spawners/bombspawner.dm @@ -1,143 +1,143 @@ -/client/proc/spawn_tanktransferbomb() - set category = "Debug" - set desc = "Spawn a tank transfer valve bomb" - set name = "Instant TTV" - - if(!check_rights(R_SPAWN)) return - - var/obj/effect/spawner/newbomb/proto = /obj/effect/spawner/newbomb/radio/custom - - var/p = tgui_input_number(usr, "Enter phoron amount (mol):","Phoron", initial(proto.phoron_amt)) - if(p == null) return - - var/o = tgui_input_number(usr, "Enter oxygen amount (mol):","Oxygen", initial(proto.oxygen_amt)) - if(o == null) return - - var/c = tgui_input_number(usr, "Enter carbon dioxide amount (mol):","Carbon Dioxide", initial(proto.carbon_amt)) - if(c == null) return - - new /obj/effect/spawner/newbomb/radio/custom(get_turf(mob), p, o, c) - -/obj/effect/spawner/newbomb - name = "TTV bomb" - icon = 'icons/mob/screen1.dmi' - icon_state = "x" - - var/assembly_type = /obj/item/device/assembly/signaler - - //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. - var/phoron_amt = 12 - var/oxygen_amt = 18 - var/carbon_amt = 0 - -/obj/effect/spawner/newbomb/timer - name = "TTV bomb - timer" - assembly_type = /obj/item/device/assembly/timer - -/obj/effect/spawner/newbomb/timer/syndicate - name = "TTV bomb - merc" - //High yield bombs. Yes, it is possible to make these with toxins - phoron_amt = 18.5 - oxygen_amt = 28.5 - -/obj/effect/spawner/newbomb/proximity - name = "TTV bomb - proximity" - assembly_type = /obj/item/device/assembly/prox_sensor - -/obj/effect/spawner/newbomb/radio/custom/New(var/newloc, ph, ox, co) - if(ph != null) phoron_amt = ph - if(ox != null) oxygen_amt = ox - if(co != null) carbon_amt = co - ..() - -/obj/effect/spawner/newbomb/Initialize(newloc) - ..(newloc) - var/obj/item/device/transfer_valve/V = new(src.loc) - var/obj/item/weapon/tank/phoron/PT = new(V) - var/obj/item/weapon/tank/oxygen/OT = new(V) - - V.tank_one = PT - V.tank_two = OT - - PT.master = V - OT.master = V - - PT.valve_welded = 1 - PT.air_contents.gas["phoron"] = phoron_amt - PT.air_contents.gas["carbon_dioxide"] = carbon_amt - PT.air_contents.total_moles = phoron_amt + carbon_amt - PT.air_contents.temperature = PHORON_MINIMUM_BURN_TEMPERATURE+1 - PT.air_contents.update_values() - - OT.valve_welded = 1 - OT.air_contents.gas["oxygen"] = oxygen_amt - OT.air_contents.total_moles = oxygen_amt - OT.air_contents.temperature = PHORON_MINIMUM_BURN_TEMPERATURE+1 - OT.air_contents.update_values() - - var/obj/item/device/assembly/S = new assembly_type(V) - V.attached_device = S - - S.holder = V - S.toggle_secure() - - V.update_icon() - return INITIALIZE_HINT_QDEL - - -/////////////////////// -//One Tank Bombs, WOOOOOOO! -Luke -/////////////////////// - -/obj/effect/spawner/onetankbomb - name = "Single-tank bomb" - icon = 'icons/mob/screen1.dmi' - icon_state = "x" - -// var/assembly_type = /obj/item/device/assembly/signaler - - //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. - var/phoron_amt = 0 - var/oxygen_amt = 0 - -/obj/effect/spawner/onetankbomb/New(newloc) //just needs an assembly. - ..(newloc) - - var/type = pick(/obj/item/weapon/tank/phoron/onetankbomb, /obj/item/weapon/tank/oxygen/onetankbomb) - new type(src.loc) - - qdel(src) - -/obj/effect/spawner/onetankbomb/full - name = "Single-tank bomb" - icon = 'icons/mob/screen1.dmi' - icon_state = "x" - -// var/assembly_type = /obj/item/device/assembly/signaler - - //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. -/obj/effect/spawner/onetankbomb/full/New(newloc) //just needs an assembly. - ..(newloc) - - var/type = pick(/obj/item/weapon/tank/phoron/onetankbomb/full, /obj/item/weapon/tank/oxygen/onetankbomb/full) - new type(src.loc) - - qdel(src) - -/obj/effect/spawner/onetankbomb/frag - name = "Single-tank bomb" - icon = 'icons/mob/screen1.dmi' - icon_state = "x" - -// var/assembly_type = /obj/item/device/assembly/signaler - - //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. -/obj/effect/spawner/onetankbomb/full/New(newloc) //just needs an assembly. - ..(newloc) - - var/type = pick(/obj/item/weapon/tank/phoron/onetankbomb/full, /obj/item/weapon/tank/oxygen/onetankbomb/full) - new type(src.loc) - - qdel(src) - - +/client/proc/spawn_tanktransferbomb() + set category = "Debug" + set desc = "Spawn a tank transfer valve bomb" + set name = "Instant TTV" + + if(!check_rights(R_SPAWN)) return + + var/obj/effect/spawner/newbomb/proto = /obj/effect/spawner/newbomb/radio/custom + + var/p = tgui_input_number(usr, "Enter phoron amount (mol):","Phoron", initial(proto.phoron_amt)) + if(p == null) return + + var/o = tgui_input_number(usr, "Enter oxygen amount (mol):","Oxygen", initial(proto.oxygen_amt)) + if(o == null) return + + var/c = tgui_input_number(usr, "Enter carbon dioxide amount (mol):","Carbon Dioxide", initial(proto.carbon_amt)) + if(c == null) return + + new /obj/effect/spawner/newbomb/radio/custom(get_turf(mob), p, o, c) + +/obj/effect/spawner/newbomb + name = "TTV bomb" + icon = 'icons/mob/screen1.dmi' + icon_state = "x" + + var/assembly_type = /obj/item/device/assembly/signaler + + //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. + var/phoron_amt = 12 + var/oxygen_amt = 18 + var/carbon_amt = 0 + +/obj/effect/spawner/newbomb/timer + name = "TTV bomb - timer" + assembly_type = /obj/item/device/assembly/timer + +/obj/effect/spawner/newbomb/timer/syndicate + name = "TTV bomb - merc" + //High yield bombs. Yes, it is possible to make these with toxins + phoron_amt = 18.5 + oxygen_amt = 28.5 + +/obj/effect/spawner/newbomb/proximity + name = "TTV bomb - proximity" + assembly_type = /obj/item/device/assembly/prox_sensor + +/obj/effect/spawner/newbomb/radio/custom/New(var/newloc, ph, ox, co) + if(ph != null) phoron_amt = ph + if(ox != null) oxygen_amt = ox + if(co != null) carbon_amt = co + ..() + +/obj/effect/spawner/newbomb/Initialize(newloc) + ..(newloc) + var/obj/item/device/transfer_valve/V = new(src.loc) + var/obj/item/weapon/tank/phoron/PT = new(V) + var/obj/item/weapon/tank/oxygen/OT = new(V) + + V.tank_one = PT + V.tank_two = OT + + PT.master = V + OT.master = V + + PT.valve_welded = 1 + PT.air_contents.gas["phoron"] = phoron_amt + PT.air_contents.gas["carbon_dioxide"] = carbon_amt + PT.air_contents.total_moles = phoron_amt + carbon_amt + PT.air_contents.temperature = PHORON_MINIMUM_BURN_TEMPERATURE+1 + PT.air_contents.update_values() + + OT.valve_welded = 1 + OT.air_contents.gas["oxygen"] = oxygen_amt + OT.air_contents.total_moles = oxygen_amt + OT.air_contents.temperature = PHORON_MINIMUM_BURN_TEMPERATURE+1 + OT.air_contents.update_values() + + var/obj/item/device/assembly/S = new assembly_type(V) + V.attached_device = S + + S.holder = V + S.toggle_secure() + + V.update_icon() + return INITIALIZE_HINT_QDEL + + +/////////////////////// +//One Tank Bombs, WOOOOOOO! -Luke +/////////////////////// + +/obj/effect/spawner/onetankbomb + name = "Single-tank bomb" + icon = 'icons/mob/screen1.dmi' + icon_state = "x" + +// var/assembly_type = /obj/item/device/assembly/signaler + + //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. + var/phoron_amt = 0 + var/oxygen_amt = 0 + +/obj/effect/spawner/onetankbomb/New(newloc) //just needs an assembly. + ..(newloc) + + var/type = pick(/obj/item/weapon/tank/phoron/onetankbomb, /obj/item/weapon/tank/oxygen/onetankbomb) + new type(src.loc) + + qdel(src) + +/obj/effect/spawner/onetankbomb/full + name = "Single-tank bomb" + icon = 'icons/mob/screen1.dmi' + icon_state = "x" + +// var/assembly_type = /obj/item/device/assembly/signaler + + //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. +/obj/effect/spawner/onetankbomb/full/New(newloc) //just needs an assembly. + ..(newloc) + + var/type = pick(/obj/item/weapon/tank/phoron/onetankbomb/full, /obj/item/weapon/tank/oxygen/onetankbomb/full) + new type(src.loc) + + qdel(src) + +/obj/effect/spawner/onetankbomb/frag + name = "Single-tank bomb" + icon = 'icons/mob/screen1.dmi' + icon_state = "x" + +// var/assembly_type = /obj/item/device/assembly/signaler + + //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. +/obj/effect/spawner/onetankbomb/full/New(newloc) //just needs an assembly. + ..(newloc) + + var/type = pick(/obj/item/weapon/tank/phoron/onetankbomb/full, /obj/item/weapon/tank/oxygen/onetankbomb/full) + new type(src.loc) + + qdel(src) + + diff --git a/code/game/objects/effects/spawners/gibspawner.dm b/code/game/objects/effects/spawners/gibspawner.dm index af84a15d427..0fcfe6d6a04 100644 --- a/code/game/objects/effects/spawners/gibspawner.dm +++ b/code/game/objects/effects/spawners/gibspawner.dm @@ -1,26 +1,26 @@ -/obj/effect/gibspawner/generic - gibtypes = list(/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/core) - gibamounts = list(2,2,1) - -/obj/effect/gibspawner/generic/New() - gibdirections = list(list(WEST, NORTHWEST, SOUTHWEST, NORTH),list(EAST, NORTHEAST, SOUTHEAST, SOUTH), list()) - ..() - -/obj/effect/gibspawner/human - gibtypes = list(/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/down,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/core) - gibamounts = list(1,1,1,1,1,1,1) - -/obj/effect/gibspawner/human/New() - gibdirections = list(list(NORTH, NORTHEAST, NORTHWEST),list(SOUTH, SOUTHEAST, SOUTHWEST),list(WEST, NORTHWEST, SOUTHWEST),list(EAST, NORTHEAST, SOUTHEAST), alldirs, alldirs, list()) - gibamounts[6] = pick(0,1,2) - ..() - -/obj/effect/gibspawner/robot - sparks = 1 - gibtypes = list(/obj/effect/decal/cleanable/blood/gibs/robot/up,/obj/effect/decal/cleanable/blood/gibs/robot/down,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot/limb) - gibamounts = list(1,1,1,1,1,1) - -/obj/effect/gibspawner/robot/New() - gibdirections = list(list(NORTH, NORTHEAST, NORTHWEST),list(SOUTH, SOUTHEAST, SOUTHWEST),list(WEST, NORTHWEST, SOUTHWEST),list(EAST, NORTHEAST, SOUTHEAST), alldirs, alldirs) - gibamounts[6] = pick(0,1,2) +/obj/effect/gibspawner/generic + gibtypes = list(/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/core) + gibamounts = list(2,2,1) + +/obj/effect/gibspawner/generic/New() + gibdirections = list(list(WEST, NORTHWEST, SOUTHWEST, NORTH),list(EAST, NORTHEAST, SOUTHEAST, SOUTH), list()) + ..() + +/obj/effect/gibspawner/human + gibtypes = list(/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/down,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/core) + gibamounts = list(1,1,1,1,1,1,1) + +/obj/effect/gibspawner/human/New() + gibdirections = list(list(NORTH, NORTHEAST, NORTHWEST),list(SOUTH, SOUTHEAST, SOUTHWEST),list(WEST, NORTHWEST, SOUTHWEST),list(EAST, NORTHEAST, SOUTHEAST), alldirs, alldirs, list()) + gibamounts[6] = pick(0,1,2) + ..() + +/obj/effect/gibspawner/robot + sparks = 1 + gibtypes = list(/obj/effect/decal/cleanable/blood/gibs/robot/up,/obj/effect/decal/cleanable/blood/gibs/robot/down,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot/limb) + gibamounts = list(1,1,1,1,1,1) + +/obj/effect/gibspawner/robot/New() + gibdirections = list(list(NORTH, NORTHEAST, NORTHWEST),list(SOUTH, SOUTHEAST, SOUTHWEST),list(WEST, NORTHWEST, SOUTHWEST),list(EAST, NORTHEAST, SOUTHEAST), alldirs, alldirs) + gibamounts[6] = pick(0,1,2) ..() \ No newline at end of file diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 0923f4341ee..776b2e69d7d 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -1,324 +1,324 @@ -//generic procs copied from obj/effect/alien -/obj/effect/spider - name = "web" - desc = "it's stringy and sticky" - icon = 'icons/effects/effects.dmi' - anchored = TRUE - density = FALSE - var/health = 15 - -//similar to weeds, but only barfed out by nurses manually -/obj/effect/spider/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - if(2.0) - if (prob(50)) - qdel(src) - if(3.0) - if (prob(5)) - qdel(src) - return - -/obj/effect/spider/attackby(var/obj/item/weapon/W, var/mob/user) - user.setClickCooldown(user.get_attack_speed(W)) - - if(LAZYLEN(W.attack_verb)) - visible_message("\The [src] has been [pick(W.attack_verb)] with \the [W][(user ? " by [user]." : ".")]") - else - visible_message("\The [src] has been attacked with \the [W][(user ? " by [user]." : ".")]") - - var/damage = W.force / 4.0 - - if(W.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/WT = W.get_welder() - - if(WT.remove_fuel(0, user)) - damage = 15 - playsound(src, W.usesound, 100, 1) - - health -= damage - healthcheck() - -/obj/effect/spider/spiderling/attack_hand(mob/living/user) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - user.do_attack_animation(src) - if(prob(20)) - visible_message("\The [user] tries to stomp on \the [src], but misses!") - var/list/nearby = oview(2, src) - if(length(nearby)) - walk_to(src, pick(nearby), 2) - return - visible_message("\The [user] stomps \the [src] dead!") - die() - -/obj/effect/spider/bullet_act(var/obj/item/projectile/Proj) - ..() - health -= Proj.get_structure_damage() - healthcheck() - -/obj/effect/spider/proc/die() - qdel(src) - -/obj/effect/spider/proc/healthcheck() - if(health <= 0) - die() - -/obj/effect/spider/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature > 300 + T0C) - health -= 5 - healthcheck() - -/obj/effect/spider/stickyweb - icon_state = "stickyweb1" - -/obj/effect/spider/stickyweb/Initialize() - if(prob(50)) - icon_state = "stickyweb2" - return ..() - -/obj/effect/spider/stickyweb/CanPass(atom/movable/mover, turf/target) - if(istype(mover, /mob/living/simple_mob/animal/giant_spider)) - return TRUE - else if(istype(mover, /mob/living)) - if(prob(50)) - to_chat(mover, span("warning", "You get stuck in \the [src] for a moment.")) - return FALSE - else if(istype(mover, /obj/item/projectile)) - return prob(30) - return TRUE - -/obj/effect/spider/eggcluster - name = "egg cluster" - desc = "They seem to pulse slightly with an inner life" - icon_state = "eggs" - var/amount_grown = 0 - var/spiders_min = 6 - var/spiders_max = 24 - var/spider_type = /obj/effect/spider/spiderling - var/faction = "spiders" - -/obj/effect/spider/eggcluster/Initialize() - pixel_x = rand(3,-3) - pixel_y = rand(3,-3) - START_PROCESSING(SSobj, src) - return ..() - -/obj/effect/spider/eggcluster/New(var/location, var/atom/parent) - get_light_and_color(parent) - ..() - -/obj/effect/spider/eggcluster/Destroy() - STOP_PROCESSING(SSobj, src) - if(istype(loc, /obj/item/organ/external)) - var/obj/item/organ/external/O = loc - O.implants -= src - - return ..() - -/obj/effect/spider/eggcluster/process() - amount_grown += rand(0,2) - if(amount_grown >= 100) - var/num = rand(spiders_min, spiders_max) - var/obj/item/organ/external/O = null - if(istype(loc, /obj/item/organ/external)) - O = loc - - for(var/i=0, i[src] dies!") - new /obj/effect/decal/cleanable/spiderling_remains(src.loc) - ..() - -/obj/effect/spider/spiderling/healthcheck() - if(health <= 0) - die() - -/obj/effect/spider/spiderling/process() - healthcheck() - if(travelling_in_vent) - if(istype(src.loc, /turf)) - travelling_in_vent = 0 - entry_vent = null - else if(entry_vent) - if(get_dist(src, entry_vent) <= 1) - //VOREStation Edit Start - var/obj/machinery/atmospherics/unary/vent_pump/exit_vent = get_safe_ventcrawl_target(entry_vent) - if(!exit_vent) - return - spawn(rand(20,60)) - loc = exit_vent - var/travel_time = round(get_dist(loc, exit_vent.loc) / 2) - spawn(travel_time) - - if(!exit_vent || exit_vent.welded) - loc = entry_vent - entry_vent = null - return - - if(prob(50)) - src.visible_message("You hear something squeezing through the ventilation ducts.",2) - sleep(travel_time) - - if(!exit_vent || exit_vent.welded) - loc = entry_vent - entry_vent = null - return - loc = exit_vent.loc - entry_vent = null - var/area/new_area = get_area(loc) - if(new_area) - new_area.Entered(src) - //VOREStation Edit End - //================= - - if(isturf(loc)) - skitter() - - else if(isorgan(loc)) - if(amount_grown < 0) amount_grown = 1 - var/obj/item/organ/external/O = loc - if(!O.owner || O.owner.stat == DEAD || amount_grown > 80) - O.implants -= src - src.loc = O.owner ? O.owner.loc : O.loc - src.visible_message("\A [src] makes its way out of [O.owner ? "[O.owner]'s [O.name]" : "\the [O]"]!") - if(O.owner) - O.owner.apply_damage(1, BRUTE, O.organ_tag) - else if(prob(1)) - O.owner.apply_damage(1, TOX, O.organ_tag) - if(world.time > last_itch + 30 SECONDS) - last_itch = world.time - to_chat(O.owner, "Your [O.name] itches...") - else if(prob(1)) - src.visible_message("\The [src] skitters.") - - if(amount_grown >= 0) - amount_grown += rand(0,2) - -/obj/effect/spider/spiderling/proc/skitter() - if(isturf(loc)) - if(prob(25)) - var/list/nearby = trange(5, src) - loc - if(nearby.len) - var/target_atom = pick(nearby) - walk_to(src, target_atom, 5) - if(prob(25)) - src.visible_message("\The [src] skitters[pick(" away"," around","")].") - else if(amount_grown < 75 && prob(5)) - //vent crawl! - for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src)) - if(!v.welded) - entry_vent = v - walk_to(src, entry_vent, 5) - break - if(amount_grown >= 100) - var/spawn_type = pick(grow_as) - var/mob/living/simple_mob/animal/giant_spider/GS = new spawn_type(src.loc, src) - GS.faction = faction - if(stunted) - spawn(2) - GS.make_spiderling() - qdel(src) - -/obj/effect/spider/spiderling/stunted - stunted = TRUE - - grow_as = list(/mob/living/simple_mob/animal/giant_spider, /mob/living/simple_mob/animal/giant_spider/hunter) - -/obj/effect/spider/spiderling/non_growing - amount_grown = -1 - -/obj/effect/spider/spiderling/princess - name = "royal spiderling" - desc = "There's a special aura about this one." - grow_as = list(/mob/living/simple_mob/animal/giant_spider/nurse/queen) - -/obj/effect/spider/spiderling/princess/New(var/location, var/atom/parent) - ..() - amount_grown = 50 - -/obj/effect/decal/cleanable/spiderling_remains - name = "spiderling remains" - desc = "Green squishy mess." - icon = 'icons/effects/effects.dmi' - icon_state = "greenshatter" - -/obj/effect/spider/cocoon - name = "cocoon" - desc = "Something wrapped in silky spider web" - icon_state = "cocoon1" - health = 60 - -/obj/effect/spider/cocoon/New() - icon_state = pick("cocoon1","cocoon2","cocoon3") - -/obj/effect/spider/cocoon/Destroy() - src.visible_message("\The [src] splits open.") - for(var/atom/movable/A in contents) - A.loc = src.loc - return ..() +//generic procs copied from obj/effect/alien +/obj/effect/spider + name = "web" + desc = "it's stringy and sticky" + icon = 'icons/effects/effects.dmi' + anchored = TRUE + density = FALSE + var/health = 15 + +//similar to weeds, but only barfed out by nurses manually +/obj/effect/spider/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + if(2.0) + if (prob(50)) + qdel(src) + if(3.0) + if (prob(5)) + qdel(src) + return + +/obj/effect/spider/attackby(var/obj/item/weapon/W, var/mob/user) + user.setClickCooldown(user.get_attack_speed(W)) + + if(LAZYLEN(W.attack_verb)) + visible_message("\The [src] has been [pick(W.attack_verb)] with \the [W][(user ? " by [user]." : ".")]") + else + visible_message("\The [src] has been attacked with \the [W][(user ? " by [user]." : ".")]") + + var/damage = W.force / 4.0 + + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + + if(WT.remove_fuel(0, user)) + damage = 15 + playsound(src, W.usesound, 100, 1) + + health -= damage + healthcheck() + +/obj/effect/spider/spiderling/attack_hand(mob/living/user) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(src) + if(prob(20)) + visible_message("\The [user] tries to stomp on \the [src], but misses!") + var/list/nearby = oview(2, src) + if(length(nearby)) + walk_to(src, pick(nearby), 2) + return + visible_message("\The [user] stomps \the [src] dead!") + die() + +/obj/effect/spider/bullet_act(var/obj/item/projectile/Proj) + ..() + health -= Proj.get_structure_damage() + healthcheck() + +/obj/effect/spider/proc/die() + qdel(src) + +/obj/effect/spider/proc/healthcheck() + if(health <= 0) + die() + +/obj/effect/spider/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature > 300 + T0C) + health -= 5 + healthcheck() + +/obj/effect/spider/stickyweb + icon_state = "stickyweb1" + +/obj/effect/spider/stickyweb/Initialize() + if(prob(50)) + icon_state = "stickyweb2" + return ..() + +/obj/effect/spider/stickyweb/CanPass(atom/movable/mover, turf/target) + if(istype(mover, /mob/living/simple_mob/animal/giant_spider)) + return TRUE + else if(istype(mover, /mob/living)) + if(prob(50)) + to_chat(mover, span("warning", "You get stuck in \the [src] for a moment.")) + return FALSE + else if(istype(mover, /obj/item/projectile)) + return prob(30) + return TRUE + +/obj/effect/spider/eggcluster + name = "egg cluster" + desc = "They seem to pulse slightly with an inner life" + icon_state = "eggs" + var/amount_grown = 0 + var/spiders_min = 6 + var/spiders_max = 24 + var/spider_type = /obj/effect/spider/spiderling + var/faction = "spiders" + +/obj/effect/spider/eggcluster/Initialize() + pixel_x = rand(3,-3) + pixel_y = rand(3,-3) + START_PROCESSING(SSobj, src) + return ..() + +/obj/effect/spider/eggcluster/New(var/location, var/atom/parent) + get_light_and_color(parent) + ..() + +/obj/effect/spider/eggcluster/Destroy() + STOP_PROCESSING(SSobj, src) + if(istype(loc, /obj/item/organ/external)) + var/obj/item/organ/external/O = loc + O.implants -= src + + return ..() + +/obj/effect/spider/eggcluster/process() + amount_grown += rand(0,2) + if(amount_grown >= 100) + var/num = rand(spiders_min, spiders_max) + var/obj/item/organ/external/O = null + if(istype(loc, /obj/item/organ/external)) + O = loc + + for(var/i=0, i[src] dies!") + new /obj/effect/decal/cleanable/spiderling_remains(src.loc) + ..() + +/obj/effect/spider/spiderling/healthcheck() + if(health <= 0) + die() + +/obj/effect/spider/spiderling/process() + healthcheck() + if(travelling_in_vent) + if(istype(src.loc, /turf)) + travelling_in_vent = 0 + entry_vent = null + else if(entry_vent) + if(get_dist(src, entry_vent) <= 1) + //VOREStation Edit Start + var/obj/machinery/atmospherics/unary/vent_pump/exit_vent = get_safe_ventcrawl_target(entry_vent) + if(!exit_vent) + return + spawn(rand(20,60)) + loc = exit_vent + var/travel_time = round(get_dist(loc, exit_vent.loc) / 2) + spawn(travel_time) + + if(!exit_vent || exit_vent.welded) + loc = entry_vent + entry_vent = null + return + + if(prob(50)) + src.visible_message("You hear something squeezing through the ventilation ducts.",2) + sleep(travel_time) + + if(!exit_vent || exit_vent.welded) + loc = entry_vent + entry_vent = null + return + loc = exit_vent.loc + entry_vent = null + var/area/new_area = get_area(loc) + if(new_area) + new_area.Entered(src) + //VOREStation Edit End + //================= + + if(isturf(loc)) + skitter() + + else if(isorgan(loc)) + if(amount_grown < 0) amount_grown = 1 + var/obj/item/organ/external/O = loc + if(!O.owner || O.owner.stat == DEAD || amount_grown > 80) + O.implants -= src + src.loc = O.owner ? O.owner.loc : O.loc + src.visible_message("\A [src] makes its way out of [O.owner ? "[O.owner]'s [O.name]" : "\the [O]"]!") + if(O.owner) + O.owner.apply_damage(1, BRUTE, O.organ_tag) + else if(prob(1)) + O.owner.apply_damage(1, TOX, O.organ_tag) + if(world.time > last_itch + 30 SECONDS) + last_itch = world.time + to_chat(O.owner, "Your [O.name] itches...") + else if(prob(1)) + src.visible_message("\The [src] skitters.") + + if(amount_grown >= 0) + amount_grown += rand(0,2) + +/obj/effect/spider/spiderling/proc/skitter() + if(isturf(loc)) + if(prob(25)) + var/list/nearby = trange(5, src) - loc + if(nearby.len) + var/target_atom = pick(nearby) + walk_to(src, target_atom, 5) + if(prob(25)) + src.visible_message("\The [src] skitters[pick(" away"," around","")].") + else if(amount_grown < 75 && prob(5)) + //vent crawl! + for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src)) + if(!v.welded) + entry_vent = v + walk_to(src, entry_vent, 5) + break + if(amount_grown >= 100) + var/spawn_type = pick(grow_as) + var/mob/living/simple_mob/animal/giant_spider/GS = new spawn_type(src.loc, src) + GS.faction = faction + if(stunted) + spawn(2) + GS.make_spiderling() + qdel(src) + +/obj/effect/spider/spiderling/stunted + stunted = TRUE + + grow_as = list(/mob/living/simple_mob/animal/giant_spider, /mob/living/simple_mob/animal/giant_spider/hunter) + +/obj/effect/spider/spiderling/non_growing + amount_grown = -1 + +/obj/effect/spider/spiderling/princess + name = "royal spiderling" + desc = "There's a special aura about this one." + grow_as = list(/mob/living/simple_mob/animal/giant_spider/nurse/queen) + +/obj/effect/spider/spiderling/princess/New(var/location, var/atom/parent) + ..() + amount_grown = 50 + +/obj/effect/decal/cleanable/spiderling_remains + name = "spiderling remains" + desc = "Green squishy mess." + icon = 'icons/effects/effects.dmi' + icon_state = "greenshatter" + +/obj/effect/spider/cocoon + name = "cocoon" + desc = "Something wrapped in silky spider web" + icon_state = "cocoon1" + health = 60 + +/obj/effect/spider/cocoon/New() + icon_state = pick("cocoon1","cocoon2","cocoon3") + +/obj/effect/spider/cocoon/Destroy() + src.visible_message("\The [src] splits open.") + for(var/atom/movable/A in contents) + A.loc = src.loc + return ..() diff --git a/code/game/objects/empulse.dm b/code/game/objects/empulse.dm index fcec6803e4c..949eb71eafa 100644 --- a/code/game/objects/empulse.dm +++ b/code/game/objects/empulse.dm @@ -1,74 +1,74 @@ -// Uncomment this define to check for possible lengthy processing of emp_act()s. -// If emp_act() takes more than defined deciseconds (1/10 seconds) an admin message and log is created. -// I do not recommend having this uncommented on main server, it probably causes a bit more lag, espicially with larger EMPs. - -// #define EMPDEBUG 10 - -/proc/empulse(turf/epicenter, first_range, second_range, third_range, fourth_range, log=0) - if(!epicenter) return - - if(!istype(epicenter, /turf)) - epicenter = get_turf(epicenter.loc) - - if(log) - message_admins("EMP with size ([first_range], [second_range], [third_range], [fourth_range]) in area [epicenter.loc.name] ") - log_game("EMP with size ([first_range], [second_range], [third_range], [fourth_range]) in area [epicenter.loc.name] ") - - if(first_range > 1) - var/obj/effect/overlay/pulse = new /obj/effect/overlay(epicenter) - pulse.icon = 'icons/effects/effects.dmi' - pulse.icon_state = "emppulse" - pulse.name = "emp pulse" - pulse.anchored = TRUE - spawn(20) - qdel(pulse) - - if(first_range > second_range) - second_range = first_range - if(second_range > third_range) - third_range = second_range - if(third_range > fourth_range) - fourth_range = third_range - - for(var/mob/M in range(first_range, epicenter)) - M << 'sound/effects/EMPulse.ogg' - - for(var/atom/T in range(fourth_range, epicenter)) - #ifdef EMPDEBUG - var/time = world.timeofday - #endif - var/distance = get_dist(epicenter, T) - if(distance < 0) - distance = 0 - //Worst effects, really hurts - if(distance < first_range) - T.emp_act(1) - else if(distance == first_range) - if(prob(50)) - T.emp_act(1) - else - T.emp_act(2) - //Slightly less painful - else if(distance <= second_range) - T.emp_act(2) - else if(distance == second_range) - if(prob(50)) - T.emp_act(2) - else - T.emp_act(3) - //Even less slightly less painful - else if(distance <= third_range) - T.emp_act(3) - else if(distance == third_range) - if(prob(50)) - T.emp_act(2) - else - T.emp_act(3) - //This should be more or less harmless - else if(distance <= fourth_range) - T.emp_act(4) - #ifdef EMPDEBUG - if((world.timeofday - time) >= EMPDEBUG) - log_and_message_admins("EMPDEBUG: [T.name] - [T.type] - took [world.timeofday - time]ds to process emp_act()!") - #endif +// Uncomment this define to check for possible lengthy processing of emp_act()s. +// If emp_act() takes more than defined deciseconds (1/10 seconds) an admin message and log is created. +// I do not recommend having this uncommented on main server, it probably causes a bit more lag, espicially with larger EMPs. + +// #define EMPDEBUG 10 + +/proc/empulse(turf/epicenter, first_range, second_range, third_range, fourth_range, log=0) + if(!epicenter) return + + if(!istype(epicenter, /turf)) + epicenter = get_turf(epicenter.loc) + + if(log) + message_admins("EMP with size ([first_range], [second_range], [third_range], [fourth_range]) in area [epicenter.loc.name] ") + log_game("EMP with size ([first_range], [second_range], [third_range], [fourth_range]) in area [epicenter.loc.name] ") + + if(first_range > 1) + var/obj/effect/overlay/pulse = new /obj/effect/overlay(epicenter) + pulse.icon = 'icons/effects/effects.dmi' + pulse.icon_state = "emppulse" + pulse.name = "emp pulse" + pulse.anchored = TRUE + spawn(20) + qdel(pulse) + + if(first_range > second_range) + second_range = first_range + if(second_range > third_range) + third_range = second_range + if(third_range > fourth_range) + fourth_range = third_range + + for(var/mob/M in range(first_range, epicenter)) + M << 'sound/effects/EMPulse.ogg' + + for(var/atom/T in range(fourth_range, epicenter)) + #ifdef EMPDEBUG + var/time = world.timeofday + #endif + var/distance = get_dist(epicenter, T) + if(distance < 0) + distance = 0 + //Worst effects, really hurts + if(distance < first_range) + T.emp_act(1) + else if(distance == first_range) + if(prob(50)) + T.emp_act(1) + else + T.emp_act(2) + //Slightly less painful + else if(distance <= second_range) + T.emp_act(2) + else if(distance == second_range) + if(prob(50)) + T.emp_act(2) + else + T.emp_act(3) + //Even less slightly less painful + else if(distance <= third_range) + T.emp_act(3) + else if(distance == third_range) + if(prob(50)) + T.emp_act(2) + else + T.emp_act(3) + //This should be more or less harmless + else if(distance <= fourth_range) + T.emp_act(4) + #ifdef EMPDEBUG + if((world.timeofday - time) >= EMPDEBUG) + log_and_message_admins("EMPDEBUG: [T.name] - [T.type] - took [world.timeofday - time]ds to process emp_act()!") + #endif return 1 \ No newline at end of file diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm index 7177e52cbe6..f54adf376f7 100644 --- a/code/game/objects/explosion.dm +++ b/code/game/objects/explosion.dm @@ -1,112 +1,112 @@ -//TODO: Flash range does nothing currently - -/proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, z_transfer = UP|DOWN, shaped) - var/multi_z_scalar = config.multi_z_explosion_scalar - spawn(0) - var/start = world.timeofday - epicenter = get_turf(epicenter) - if(!epicenter) return - - // Handles recursive propagation of explosions. - if(z_transfer && multi_z_scalar) - var/adj_dev = max(0, (multi_z_scalar * devastation_range) - (shaped ? 2 : 0) ) - var/adj_heavy = max(0, (multi_z_scalar * heavy_impact_range) - (shaped ? 2 : 0) ) - var/adj_light = max(0, (multi_z_scalar * light_impact_range) - (shaped ? 2 : 0) ) - var/adj_flash = max(0, (multi_z_scalar * flash_range) - (shaped ? 2 : 0) ) - - - if(adj_dev > 0 || adj_heavy > 0) - if(HasAbove(epicenter.z) && z_transfer & UP) - explosion(GetAbove(epicenter), round(adj_dev), round(adj_heavy), round(adj_light), round(adj_flash), 0, UP, shaped) - if(HasBelow(epicenter.z) && z_transfer & DOWN) - explosion(GetBelow(epicenter), round(adj_dev), round(adj_heavy), round(adj_light), round(adj_flash), 0, DOWN, shaped) - - var/max_range = max(devastation_range, heavy_impact_range, light_impact_range, flash_range) - - // Play sounds; we want sounds to be different depending on distance so we will manually do it ourselves. - // Stereo users will also hear the direction of the explosion! - // Calculate far explosion sound range. Only allow the sound effect for heavy/devastating explosions. - // 3/7/14 will calculate to 80 + 35 - var/far_dist = 0 - far_dist += heavy_impact_range * 5 - far_dist += devastation_range * 20 - var/frequency = get_rand_frequency() - for(var/mob/M in player_list) - if(M.z == epicenter.z) - var/turf/M_turf = get_turf(M) - var/dist = get_dist(M_turf, epicenter) - // If inside the blast radius + world.view - 2 - if(dist <= round(max_range + world.view - 2, 1)) - M.playsound_local(epicenter, get_sfx("explosion"), 100, 1, frequency, falloff = 5) // get_sfx() is so that everyone gets the same sound - else if(dist <= far_dist) - var/far_volume = CLAMP(far_dist, 30, 50) // Volume is based on explosion size and dist - far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion - M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', far_volume, 1, frequency, falloff = 5) - - var/close = range(world.view+round(devastation_range,1), epicenter) - // to all distanced mobs play a different sound - for(var/mob/M in player_list) - if(M.z == epicenter.z) - if(!(M in close)) - // check if the mob can hear - if(M.ear_deaf <= 0 || !M.ear_deaf) - if(!istype(M.loc,/turf/space)) - M << 'sound/effects/explosionfar.ogg' - - if(adminlog) - message_admins("Explosion with [shaped ? "shaped" : "non-shaped"] size ([devastation_range], [heavy_impact_range], [light_impact_range]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z]) (JMP)") - log_game("Explosion with [shaped ? "shaped" : "non-shaped"] size ([devastation_range], [heavy_impact_range], [light_impact_range]) in area [epicenter.loc.name] ") - - var/approximate_intensity = (devastation_range * 3) + (heavy_impact_range * 2) + light_impact_range - var/powernet_rebuild_was_deferred_already = defer_powernet_rebuild - // Large enough explosion. For performance reasons, powernets will be rebuilt manually - if(!defer_powernet_rebuild && (approximate_intensity > 25)) - defer_powernet_rebuild = 1 - - if(heavy_impact_range > 1) - var/datum/effect/system/explosion/E = new/datum/effect/system/explosion() - E.set_up(epicenter) - E.start() - - var/x0 = epicenter.x - var/y0 = epicenter.y - var/z0 = epicenter.z - if(config.use_recursive_explosions) - var/power = devastation_range * 2 + heavy_impact_range + light_impact_range //The ranges add up, ie light 14 includes both heavy 7 and devestation 3. So this calculation means devestation counts for 4, heavy for 2 and light for 1 power, giving us a cap of 27 power. - explosion_rec(epicenter, power, shaped) - else - for(var/turf/T in trange(max_range, epicenter)) - var/dist = sqrt((T.x - x0)**2 + (T.y - y0)**2) - - if(dist < devastation_range) dist = 1 - else if(dist < heavy_impact_range) dist = 2 - else if(dist < light_impact_range) dist = 3 - else continue - - if(!T) - T = locate(x0,y0,z0) - for(var/atom_movable in T.contents) //bypass type checking since only atom/movable can be contained by turfs anyway - var/atom/movable/AM = atom_movable - if(AM && AM.simulated) AM.ex_act(dist) - - T.ex_act(dist) - - var/took = (world.timeofday-start)/10 - //You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare - if(Debug2) to_world_log("## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds.") - - //Machines which report explosions. - for(var/i,i<=doppler_arrays.len,i++) - var/obj/machinery/doppler_array/Array = doppler_arrays[i] - if(Array) - Array.sense_explosion(x0,y0,z0,devastation_range,heavy_impact_range,light_impact_range,took) - sleep(8) - - if(!powernet_rebuild_was_deferred_already && defer_powernet_rebuild) - SSmachines.makepowernets() - defer_powernet_rebuild = 0 - return 1 - -/proc/secondaryexplosion(turf/epicenter, range) - for(var/turf/tile in range(range, epicenter)) - tile.ex_act(2) +//TODO: Flash range does nothing currently + +/proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, z_transfer = UP|DOWN, shaped) + var/multi_z_scalar = config.multi_z_explosion_scalar + spawn(0) + var/start = world.timeofday + epicenter = get_turf(epicenter) + if(!epicenter) return + + // Handles recursive propagation of explosions. + if(z_transfer && multi_z_scalar) + var/adj_dev = max(0, (multi_z_scalar * devastation_range) - (shaped ? 2 : 0) ) + var/adj_heavy = max(0, (multi_z_scalar * heavy_impact_range) - (shaped ? 2 : 0) ) + var/adj_light = max(0, (multi_z_scalar * light_impact_range) - (shaped ? 2 : 0) ) + var/adj_flash = max(0, (multi_z_scalar * flash_range) - (shaped ? 2 : 0) ) + + + if(adj_dev > 0 || adj_heavy > 0) + if(HasAbove(epicenter.z) && z_transfer & UP) + explosion(GetAbove(epicenter), round(adj_dev), round(adj_heavy), round(adj_light), round(adj_flash), 0, UP, shaped) + if(HasBelow(epicenter.z) && z_transfer & DOWN) + explosion(GetBelow(epicenter), round(adj_dev), round(adj_heavy), round(adj_light), round(adj_flash), 0, DOWN, shaped) + + var/max_range = max(devastation_range, heavy_impact_range, light_impact_range, flash_range) + + // Play sounds; we want sounds to be different depending on distance so we will manually do it ourselves. + // Stereo users will also hear the direction of the explosion! + // Calculate far explosion sound range. Only allow the sound effect for heavy/devastating explosions. + // 3/7/14 will calculate to 80 + 35 + var/far_dist = 0 + far_dist += heavy_impact_range * 5 + far_dist += devastation_range * 20 + var/frequency = get_rand_frequency() + for(var/mob/M in player_list) + if(M.z == epicenter.z) + var/turf/M_turf = get_turf(M) + var/dist = get_dist(M_turf, epicenter) + // If inside the blast radius + world.view - 2 + if(dist <= round(max_range + world.view - 2, 1)) + M.playsound_local(epicenter, get_sfx("explosion"), 100, 1, frequency, falloff = 5) // get_sfx() is so that everyone gets the same sound + else if(dist <= far_dist) + var/far_volume = CLAMP(far_dist, 30, 50) // Volume is based on explosion size and dist + far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion + M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', far_volume, 1, frequency, falloff = 5) + + var/close = range(world.view+round(devastation_range,1), epicenter) + // to all distanced mobs play a different sound + for(var/mob/M in player_list) + if(M.z == epicenter.z) + if(!(M in close)) + // check if the mob can hear + if(M.ear_deaf <= 0 || !M.ear_deaf) + if(!istype(M.loc,/turf/space)) + M << 'sound/effects/explosionfar.ogg' + + if(adminlog) + message_admins("Explosion with [shaped ? "shaped" : "non-shaped"] size ([devastation_range], [heavy_impact_range], [light_impact_range]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z]) (JMP)") + log_game("Explosion with [shaped ? "shaped" : "non-shaped"] size ([devastation_range], [heavy_impact_range], [light_impact_range]) in area [epicenter.loc.name] ") + + var/approximate_intensity = (devastation_range * 3) + (heavy_impact_range * 2) + light_impact_range + var/powernet_rebuild_was_deferred_already = defer_powernet_rebuild + // Large enough explosion. For performance reasons, powernets will be rebuilt manually + if(!defer_powernet_rebuild && (approximate_intensity > 25)) + defer_powernet_rebuild = 1 + + if(heavy_impact_range > 1) + var/datum/effect/system/explosion/E = new/datum/effect/system/explosion() + E.set_up(epicenter) + E.start() + + var/x0 = epicenter.x + var/y0 = epicenter.y + var/z0 = epicenter.z + if(config.use_recursive_explosions) + var/power = devastation_range * 2 + heavy_impact_range + light_impact_range //The ranges add up, ie light 14 includes both heavy 7 and devestation 3. So this calculation means devestation counts for 4, heavy for 2 and light for 1 power, giving us a cap of 27 power. + explosion_rec(epicenter, power, shaped) + else + for(var/turf/T in trange(max_range, epicenter)) + var/dist = sqrt((T.x - x0)**2 + (T.y - y0)**2) + + if(dist < devastation_range) dist = 1 + else if(dist < heavy_impact_range) dist = 2 + else if(dist < light_impact_range) dist = 3 + else continue + + if(!T) + T = locate(x0,y0,z0) + for(var/atom_movable in T.contents) //bypass type checking since only atom/movable can be contained by turfs anyway + var/atom/movable/AM = atom_movable + if(AM && AM.simulated) AM.ex_act(dist) + + T.ex_act(dist) + + var/took = (world.timeofday-start)/10 + //You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare + if(Debug2) to_world_log("## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds.") + + //Machines which report explosions. + for(var/i,i<=doppler_arrays.len,i++) + var/obj/machinery/doppler_array/Array = doppler_arrays[i] + if(Array) + Array.sense_explosion(x0,y0,z0,devastation_range,heavy_impact_range,light_impact_range,took) + sleep(8) + + if(!powernet_rebuild_was_deferred_already && defer_powernet_rebuild) + SSmachines.makepowernets() + defer_powernet_rebuild = 0 + return 1 + +/proc/secondaryexplosion(turf/epicenter, range) + for(var/turf/tile in range(range, epicenter)) + tile.ex_act(2) diff --git a/code/game/objects/explosion_recursive.dm b/code/game/objects/explosion_recursive.dm index d60dc90f3e4..aebf6ba8e80 100644 --- a/code/game/objects/explosion_recursive.dm +++ b/code/game/objects/explosion_recursive.dm @@ -1,114 +1,114 @@ -/client/proc/kaboom() - var/power = tgui_input_number(src, "power?", "power?") - var/turf/T = get_turf(src.mob) - explosion_rec(T, power) - -/obj - var/explosion_resistance - -/proc/explosion_rec(turf/epicenter, power) - var/list/explosion_turfs = list() - var/explosion_in_progress = 0 - - var/loopbreak = 0 - while(explosion_in_progress) - if(loopbreak >= 15) return - spawn(10) - loopbreak++ - - if(power <= 0) return - epicenter = get_turf(epicenter) - if(!epicenter) return - - message_admins("Explosion with size ([power]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z])") - log_game("Explosion with size ([power]) in area [epicenter.loc.name] ") - - playsound(epicenter, 'sound/effects/explosionfar.ogg', 100, 1, round(power*2,1) ) - playsound(epicenter, "explosion", 100, 1, round(power,1) ) - - explosion_in_progress = 1 - explosion_turfs = list() - - explosion_turfs[epicenter] = power - - //This steap handles the gathering of turfs which will be ex_act() -ed in the next step. It also ensures each turf gets the maximum possible amount of power dealt to it. - for(var/direction in cardinal) - var/turf/T = get_step(epicenter, direction) - T.explosion_spread(power - epicenter.explosion_resistance, direction, explosion_turfs) - - //This step applies the ex_act effects for the explosion, as planned in the previous step. - for(var/turf/T in explosion_turfs) - if(explosion_turfs[T] <= 0) continue - if(!T) continue - - //Wow severity looks confusing to calculate... Fret not, I didn't leave you with any additional instructions or help. (just kidding, see the line under the calculation) - var/severity = 4 - round(max(min( 3, ((explosion_turfs[T] - T.explosion_resistance) / (max(3,(power/3)))) ) ,1), 1) //sanity effective power on tile divided by either 3 or one third the total explosion power - // One third because there are three power levels and I - // want each one to take up a third of the crater - var/x = T.x - var/y = T.y - var/z = T.z - T.ex_act(severity) - if(!T) - T = locate(x,y,z) - for(var/atom_movable in T.contents) - var/atom/movable/AM = atom_movable - if(AM && AM.simulated) - AM.ex_act(severity) - - explosion_in_progress = 0 - -/turf - var/explosion_resistance - -/turf/space - explosion_resistance = 3 - -/turf/simulated/open - explosion_resistance = 3 - -/turf/simulated/floor - explosion_resistance = 1 - -/turf/simulated/mineral - explosion_resistance = 2 - -/turf/simulated/shuttle/floor - explosion_resistance = 1 - -/turf/simulated/shuttle/floor4 - explosion_resistance = 1 - -/turf/simulated/shuttle/plating - explosion_resistance = 1 - -/turf/simulated/shuttle/wall - explosion_resistance = 10 - -/turf/simulated/wall - explosion_resistance = 10 - -//Code-wise, a safe value for power is something up to ~25 or ~30.. This does quite a bit of damage to the station. -//direction is the direction that the spread took to come to this tile. So it is pointing in the main blast direction - meaning where this tile should spread most of it's force. -/turf/proc/explosion_spread(power, direction, var/list/explosion_turfs) - if(power <= 0) - return - if(src in explosion_turfs) - if(explosion_turfs[src] >= power) - return //The turf already sustained and spread a power greated than what we are dealing with. No point spreading again. - explosion_turfs[src] = power - - var/spread_power = power - src.explosion_resistance //This is the amount of power that will be spread to the tile in the direction of the blast - for(var/obj/O in src) - if(O.explosion_resistance) - spread_power -= O.explosion_resistance - - var/turf/T = get_step(src, direction) - T.explosion_spread(spread_power, direction, explosion_turfs) - T = get_step(src, turn(direction,90)) - T.explosion_spread(spread_power, turn(direction,90), explosion_turfs) - T = get_step(src, turn(direction,-90)) - T.explosion_spread(spread_power, turn(direction,-90), explosion_turfs) - -/turf/unsimulated/explosion_spread(power) - return //So it doesn't get to the parent proc, which simulates explosions +/client/proc/kaboom() + var/power = tgui_input_number(src, "power?", "power?") + var/turf/T = get_turf(src.mob) + explosion_rec(T, power) + +/obj + var/explosion_resistance + +/proc/explosion_rec(turf/epicenter, power) + var/list/explosion_turfs = list() + var/explosion_in_progress = 0 + + var/loopbreak = 0 + while(explosion_in_progress) + if(loopbreak >= 15) return + spawn(10) + loopbreak++ + + if(power <= 0) return + epicenter = get_turf(epicenter) + if(!epicenter) return + + message_admins("Explosion with size ([power]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z])") + log_game("Explosion with size ([power]) in area [epicenter.loc.name] ") + + playsound(epicenter, 'sound/effects/explosionfar.ogg', 100, 1, round(power*2,1) ) + playsound(epicenter, "explosion", 100, 1, round(power,1) ) + + explosion_in_progress = 1 + explosion_turfs = list() + + explosion_turfs[epicenter] = power + + //This steap handles the gathering of turfs which will be ex_act() -ed in the next step. It also ensures each turf gets the maximum possible amount of power dealt to it. + for(var/direction in cardinal) + var/turf/T = get_step(epicenter, direction) + T.explosion_spread(power - epicenter.explosion_resistance, direction, explosion_turfs) + + //This step applies the ex_act effects for the explosion, as planned in the previous step. + for(var/turf/T in explosion_turfs) + if(explosion_turfs[T] <= 0) continue + if(!T) continue + + //Wow severity looks confusing to calculate... Fret not, I didn't leave you with any additional instructions or help. (just kidding, see the line under the calculation) + var/severity = 4 - round(max(min( 3, ((explosion_turfs[T] - T.explosion_resistance) / (max(3,(power/3)))) ) ,1), 1) //sanity effective power on tile divided by either 3 or one third the total explosion power + // One third because there are three power levels and I + // want each one to take up a third of the crater + var/x = T.x + var/y = T.y + var/z = T.z + T.ex_act(severity) + if(!T) + T = locate(x,y,z) + for(var/atom_movable in T.contents) + var/atom/movable/AM = atom_movable + if(AM && AM.simulated) + AM.ex_act(severity) + + explosion_in_progress = 0 + +/turf + var/explosion_resistance + +/turf/space + explosion_resistance = 3 + +/turf/simulated/open + explosion_resistance = 3 + +/turf/simulated/floor + explosion_resistance = 1 + +/turf/simulated/mineral + explosion_resistance = 2 + +/turf/simulated/shuttle/floor + explosion_resistance = 1 + +/turf/simulated/shuttle/floor4 + explosion_resistance = 1 + +/turf/simulated/shuttle/plating + explosion_resistance = 1 + +/turf/simulated/shuttle/wall + explosion_resistance = 10 + +/turf/simulated/wall + explosion_resistance = 10 + +//Code-wise, a safe value for power is something up to ~25 or ~30.. This does quite a bit of damage to the station. +//direction is the direction that the spread took to come to this tile. So it is pointing in the main blast direction - meaning where this tile should spread most of it's force. +/turf/proc/explosion_spread(power, direction, var/list/explosion_turfs) + if(power <= 0) + return + if(src in explosion_turfs) + if(explosion_turfs[src] >= power) + return //The turf already sustained and spread a power greated than what we are dealing with. No point spreading again. + explosion_turfs[src] = power + + var/spread_power = power - src.explosion_resistance //This is the amount of power that will be spread to the tile in the direction of the blast + for(var/obj/O in src) + if(O.explosion_resistance) + spread_power -= O.explosion_resistance + + var/turf/T = get_step(src, direction) + T.explosion_spread(spread_power, direction, explosion_turfs) + T = get_step(src, turn(direction,90)) + T.explosion_spread(spread_power, turn(direction,90), explosion_turfs) + T = get_step(src, turn(direction,-90)) + T.explosion_spread(spread_power, turn(direction,-90), explosion_turfs) + +/turf/unsimulated/explosion_spread(power) + return //So it doesn't get to the parent proc, which simulates explosions diff --git a/code/game/objects/items/apc_frame.dm b/code/game/objects/items/apc_frame.dm index ca3f7241c3a..c84e0119663 100644 --- a/code/game/objects/items/apc_frame.dm +++ b/code/game/objects/items/apc_frame.dm @@ -1,38 +1,38 @@ -// APC HULL - -/obj/item/frame/apc - name = "\improper APC frame" - desc = "Used for repairing or building APCs" - icon = 'icons/obj/apc_repair.dmi' - icon_state = "apc_frame" - refund_amt = 2 - build_wall_only = TRUE - matter = list(MAT_STEEL = 100, MAT_GLASS = 30) - -/obj/item/frame/apc/try_build(turf/on_wall, mob/user as mob) - if (get_dist(on_wall, user)>1) - return - var/ndir = get_dir(user, on_wall) - if (!(ndir in cardinal)) - return - var/turf/loc = get_turf(user) - var/area/A = loc.loc - if (!istype(loc, /turf/simulated/floor)) - to_chat(user, "APC cannot be placed on this spot.") - return - if (A.requires_power == 0 || istype(A, /area/space)) - to_chat(user, "APC cannot be placed in this area.") - return - if (A.get_apc()) - to_chat(user, "This area already has an APC.") - return //only one APC per area - for(var/obj/machinery/power/terminal/T in loc) - if (T.master) - to_chat(user, "There is another network terminal here.") - return - else - new /obj/item/stack/cable_coil(loc, 10) - to_chat(user, "You cut the cables and disassemble the unused power terminal.") - qdel(T) - new /obj/machinery/power/apc(loc, ndir, 1) - qdel(src) +// APC HULL + +/obj/item/frame/apc + name = "\improper APC frame" + desc = "Used for repairing or building APCs" + icon = 'icons/obj/apc_repair.dmi' + icon_state = "apc_frame" + refund_amt = 2 + build_wall_only = TRUE + matter = list(MAT_STEEL = 100, MAT_GLASS = 30) + +/obj/item/frame/apc/try_build(turf/on_wall, mob/user as mob) + if (get_dist(on_wall, user)>1) + return + var/ndir = get_dir(user, on_wall) + if (!(ndir in cardinal)) + return + var/turf/loc = get_turf(user) + var/area/A = loc.loc + if (!istype(loc, /turf/simulated/floor)) + to_chat(user, "APC cannot be placed on this spot.") + return + if (A.requires_power == 0 || istype(A, /area/space)) + to_chat(user, "APC cannot be placed in this area.") + return + if (A.get_apc()) + to_chat(user, "This area already has an APC.") + return //only one APC per area + for(var/obj/machinery/power/terminal/T in loc) + if (T.master) + to_chat(user, "There is another network terminal here.") + return + else + new /obj/item/stack/cable_coil(loc, 10) + to_chat(user, "You cut the cables and disassemble the unused power terminal.") + qdel(T) + new /obj/machinery/power/apc(loc, ndir, 1) + qdel(src) diff --git a/code/game/objects/items/blueprints_vr.dm b/code/game/objects/items/blueprints_vr.dm index 92231894d9b..93bd466de60 100644 --- a/code/game/objects/items/blueprints_vr.dm +++ b/code/game/objects/items/blueprints_vr.dm @@ -108,7 +108,7 @@ to_chat(user, span_notice("You add some more writing material to the [src] with the [blueprint]!")) return else if(blueprint.uses_charges && blueprint.charges) //Getting from another with limited charges. - var/to_add = tgui_input_number(user, "How many charges do you want to add to the [src]?", "[blueprint]", missing_charges) + var/to_add = tgui_input_number(user, "How many charges do you want to add to the [src]?", "[blueprint]", missing_charges, blueprint.charges) if(!isnull(to_add) && blueprint.charges >= to_add) to_chat(user, span_notice("You add some more writing material to the [src] with the [blueprint]!")) blueprint.charges -= to_add diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 8c3a5b0551b..280c43db0c0 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -1,214 +1,214 @@ -/obj/item/weapon/pen/crayon/red - icon_state = "crayonred" - colour = "#DA0000" - shadeColour = "#810C0C" - colourName = "red" - -/obj/item/weapon/pen/crayon/orange - icon_state = "crayonorange" - colour = "#FF9300" - shadeColour = "#A55403" - colourName = "orange" - -/obj/item/weapon/pen/crayon/yellow - icon_state = "crayonyellow" - colour = "#FFF200" - shadeColour = "#886422" - colourName = "yellow" - -/obj/item/weapon/pen/crayon/green - icon_state = "crayongreen" - colour = "#A8E61D" - shadeColour = "#61840F" - colourName = "green" - -/obj/item/weapon/pen/crayon/blue - icon_state = "crayonblue" - colour = "#00B7EF" - shadeColour = "#0082A8" - colourName = "blue" - -/obj/item/weapon/pen/crayon/purple - icon_state = "crayonpurple" - colour = "#DA00FF" - shadeColour = "#810CFF" - colourName = "purple" - -/obj/item/weapon/pen/crayon/mime - icon_state = "crayonmime" - desc = "A very sad-looking crayon." - colour = "#FFFFFF" - shadeColour = "#000000" - colourName = "mime" - uses = 0 - -/obj/item/weapon/pen/crayon/mime/attack_self(mob/living/user as mob) //inversion - if(colour != "#FFFFFF" && shadeColour != "#000000") - colour = "#FFFFFF" - shadeColour = "#000000" - to_chat(user, "You will now draw in white and black with this crayon.") - else - colour = "#000000" - shadeColour = "#FFFFFF" - to_chat(user, "You will now draw in black and white with this crayon.") - return - -/obj/item/weapon/pen/crayon/rainbow - icon_state = "crayonrainbow" - colour = "#FFF000" - shadeColour = "#000FFF" - colourName = "rainbow" - uses = 0 - -/obj/item/weapon/pen/crayon/rainbow/attack_self(mob/living/user as mob) - colour = input(user, "Please select the main colour.", "Crayon colour") as color - shadeColour = input(user, "Please select the shade colour.", "Crayon colour") as color - return - -/obj/item/weapon/pen/crayon/afterattack(atom/target, mob/user as mob, proximity) - if(!proximity) return - if(istype(target,/turf/simulated/floor)) - var/drawtype = tgui_input_list(user, "Choose what you'd like to draw.", "Crayon scribbles", list("graffiti","rune","letter","arrow")) - if(!drawtype) - return - if(get_dist(target, user) > 1 || !(user.z == target.z)) - return - switch(drawtype) - if("letter") - drawtype = tgui_input_list(user, "Choose the letter.", "Crayon scribbles", list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z")) - if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) - return - to_chat(user, "You start drawing a letter on the [target.name].") - if("graffiti") - drawtype = tgui_input_list(user, "Choose the graffiti.", "Crayon scribbles", list("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa")) - if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) - return - to_chat(user, "You start drawing graffiti on the [target.name].") - if("rune") - drawtype = tgui_input_list(user, "Choose the rune.", "Crayon scribbles", list("rune1", "rune2", "rune3", "rune4", "rune5", "rune6")) - if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) - return - to_chat(user, "You start drawing a rune on the [target.name].") - if("arrow") - drawtype = tgui_input_list(user, "Choose the arrow.", "Crayon scribbles", list("left", "right", "up", "down")) - if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) - return - to_chat(user, "You start drawing an arrow on the [target.name].") - if(instant || do_after(user, 50)) - new /obj/effect/decal/cleanable/crayon(target,colour,shadeColour,drawtype) - to_chat(user, "You finish drawing.") - - var/msg = "[user.client.key] ([user]) has drawn [drawtype] (with [src]) at [target.x],[target.y],[target.z]." - if(config.log_graffiti) - message_admins(msg) - log_game(msg) //We will log it anyways. - - target.add_fingerprint(user) // Adds their fingerprints to the floor the crayon is drawn on. - if(uses) - uses-- - if(!uses) - to_chat(user, "You used up your crayon!") - qdel(src) - return - -/obj/item/weapon/pen/crayon/attack(mob/living/M as mob, mob/living/user as mob) - if(M == user) - to_chat(user, "You take a bite of the crayon and swallow it.") - user.nutrition += 1 - user.reagents.add_reagent("crayon_dust",min(5,uses)/3) - if(uses) - uses -= 5 - if(uses <= 0) - to_chat(user, "You ate your crayon!") - qdel(src) - else - ..() - -/obj/item/weapon/pen/crayon/marker/black - icon_state = "markerblack" - colour = "#2D2D2D" - shadeColour = "#000000" - colourName = "black" - -/obj/item/weapon/pen/crayon/marker/red - icon_state = "markerred" - colour = "#DA0000" - shadeColour = "#810C0C" - colourName = "red" - -/obj/item/weapon/pen/crayon/marker/orange - icon_state = "markerorange" - colour = "#FF9300" - shadeColour = "#A55403" - colourName = "orange" - -/obj/item/weapon/pen/crayon/marker/yellow - icon_state = "markeryellow" - colour = "#FFF200" - shadeColour = "#886422" - colourName = "yellow" - -/obj/item/weapon/pen/crayon/marker/green - icon_state = "markergreen" - colour = "#A8E61D" - shadeColour = "#61840F" - colourName = "green" - -/obj/item/weapon/pen/crayon/marker/blue - icon_state = "markerblue" - colour = "#00B7EF" - shadeColour = "#0082A8" - colourName = "blue" - -/obj/item/weapon/pen/crayon/marker/purple - icon_state = "markerpurple" - colour = "#DA00FF" - shadeColour = "#810CFF" - colourName = "purple" - -/obj/item/weapon/pen/crayon/marker/mime - icon_state = "markermime" - desc = "A very sad-looking marker." - colour = "#FFFFFF" - shadeColour = "#000000" - colourName = "mime" - uses = 0 - -/obj/item/weapon/pen/crayon/marker/mime/attack_self(mob/living/user as mob) //inversion - if(colour != "#FFFFFF" && shadeColour != "#000000") - colour = "#FFFFFF" - shadeColour = "#000000" - to_chat(user, "You will now draw in white and black with this marker.") - else - colour = "#000000" - shadeColour = "#FFFFFF" - to_chat(user, "You will now draw in black and white with this marker.") - return - -/obj/item/weapon/pen/crayon/marker/rainbow - icon_state = "markerrainbow" - colour = "#FFF000" - shadeColour = "#000FFF" - colourName = "rainbow" - uses = 0 - -/obj/item/weapon/pen/crayon/marker/rainbow/attack_self(mob/living/user as mob) - colour = input(user, "Please select the main colour.", "Marker colour") as color - shadeColour = input(user, "Please select the shade colour.", "Marker colour") as color - return - -/obj/item/weapon/pen/crayon/marker/attack(mob/living/M as mob, mob/living/user as mob) - if(M == user) - to_chat(user, "You take a bite of the marker and swallow it.") - user.nutrition += 1 - user.reagents.add_reagent("marker_ink",6) - if(uses) - uses -= 5 - if(uses <= 0) - to_chat(user, "You ate the marker!") - qdel(src) - else - ..() - -/obj/item/weapon/pen/crayon/attack_self(var/mob/user) - return +/obj/item/weapon/pen/crayon/red + icon_state = "crayonred" + colour = "#DA0000" + shadeColour = "#810C0C" + colourName = "red" + +/obj/item/weapon/pen/crayon/orange + icon_state = "crayonorange" + colour = "#FF9300" + shadeColour = "#A55403" + colourName = "orange" + +/obj/item/weapon/pen/crayon/yellow + icon_state = "crayonyellow" + colour = "#FFF200" + shadeColour = "#886422" + colourName = "yellow" + +/obj/item/weapon/pen/crayon/green + icon_state = "crayongreen" + colour = "#A8E61D" + shadeColour = "#61840F" + colourName = "green" + +/obj/item/weapon/pen/crayon/blue + icon_state = "crayonblue" + colour = "#00B7EF" + shadeColour = "#0082A8" + colourName = "blue" + +/obj/item/weapon/pen/crayon/purple + icon_state = "crayonpurple" + colour = "#DA00FF" + shadeColour = "#810CFF" + colourName = "purple" + +/obj/item/weapon/pen/crayon/mime + icon_state = "crayonmime" + desc = "A very sad-looking crayon." + colour = "#FFFFFF" + shadeColour = "#000000" + colourName = "mime" + uses = 0 + +/obj/item/weapon/pen/crayon/mime/attack_self(mob/living/user as mob) //inversion + if(colour != "#FFFFFF" && shadeColour != "#000000") + colour = "#FFFFFF" + shadeColour = "#000000" + to_chat(user, "You will now draw in white and black with this crayon.") + else + colour = "#000000" + shadeColour = "#FFFFFF" + to_chat(user, "You will now draw in black and white with this crayon.") + return + +/obj/item/weapon/pen/crayon/rainbow + icon_state = "crayonrainbow" + colour = "#FFF000" + shadeColour = "#000FFF" + colourName = "rainbow" + uses = 0 + +/obj/item/weapon/pen/crayon/rainbow/attack_self(mob/living/user as mob) + colour = input(user, "Please select the main colour.", "Crayon colour") as color + shadeColour = input(user, "Please select the shade colour.", "Crayon colour") as color + return + +/obj/item/weapon/pen/crayon/afterattack(atom/target, mob/user as mob, proximity) + if(!proximity) return + if(istype(target,/turf/simulated/floor)) + var/drawtype = tgui_input_list(user, "Choose what you'd like to draw.", "Crayon scribbles", list("graffiti","rune","letter","arrow")) + if(!drawtype) + return + if(get_dist(target, user) > 1 || !(user.z == target.z)) + return + switch(drawtype) + if("letter") + drawtype = tgui_input_list(user, "Choose the letter.", "Crayon scribbles", list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z")) + if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) + return + to_chat(user, "You start drawing a letter on the [target.name].") + if("graffiti") + drawtype = tgui_input_list(user, "Choose the graffiti.", "Crayon scribbles", list("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa")) + if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) + return + to_chat(user, "You start drawing graffiti on the [target.name].") + if("rune") + drawtype = tgui_input_list(user, "Choose the rune.", "Crayon scribbles", list("rune1", "rune2", "rune3", "rune4", "rune5", "rune6")) + if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) + return + to_chat(user, "You start drawing a rune on the [target.name].") + if("arrow") + drawtype = tgui_input_list(user, "Choose the arrow.", "Crayon scribbles", list("left", "right", "up", "down")) + if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) + return + to_chat(user, "You start drawing an arrow on the [target.name].") + if(instant || do_after(user, 50)) + new /obj/effect/decal/cleanable/crayon(target,colour,shadeColour,drawtype) + to_chat(user, "You finish drawing.") + + var/msg = "[user.client.key] ([user]) has drawn [drawtype] (with [src]) at [target.x],[target.y],[target.z]." + if(config.log_graffiti) + message_admins(msg) + log_game(msg) //We will log it anyways. + + target.add_fingerprint(user) // Adds their fingerprints to the floor the crayon is drawn on. + if(uses) + uses-- + if(!uses) + to_chat(user, "You used up your crayon!") + qdel(src) + return + +/obj/item/weapon/pen/crayon/attack(mob/living/M as mob, mob/living/user as mob) + if(M == user) + to_chat(user, "You take a bite of the crayon and swallow it.") + user.nutrition += 1 + user.reagents.add_reagent("crayon_dust",min(5,uses)/3) + if(uses) + uses -= 5 + if(uses <= 0) + to_chat(user, "You ate your crayon!") + qdel(src) + else + ..() + +/obj/item/weapon/pen/crayon/marker/black + icon_state = "markerblack" + colour = "#2D2D2D" + shadeColour = "#000000" + colourName = "black" + +/obj/item/weapon/pen/crayon/marker/red + icon_state = "markerred" + colour = "#DA0000" + shadeColour = "#810C0C" + colourName = "red" + +/obj/item/weapon/pen/crayon/marker/orange + icon_state = "markerorange" + colour = "#FF9300" + shadeColour = "#A55403" + colourName = "orange" + +/obj/item/weapon/pen/crayon/marker/yellow + icon_state = "markeryellow" + colour = "#FFF200" + shadeColour = "#886422" + colourName = "yellow" + +/obj/item/weapon/pen/crayon/marker/green + icon_state = "markergreen" + colour = "#A8E61D" + shadeColour = "#61840F" + colourName = "green" + +/obj/item/weapon/pen/crayon/marker/blue + icon_state = "markerblue" + colour = "#00B7EF" + shadeColour = "#0082A8" + colourName = "blue" + +/obj/item/weapon/pen/crayon/marker/purple + icon_state = "markerpurple" + colour = "#DA00FF" + shadeColour = "#810CFF" + colourName = "purple" + +/obj/item/weapon/pen/crayon/marker/mime + icon_state = "markermime" + desc = "A very sad-looking marker." + colour = "#FFFFFF" + shadeColour = "#000000" + colourName = "mime" + uses = 0 + +/obj/item/weapon/pen/crayon/marker/mime/attack_self(mob/living/user as mob) //inversion + if(colour != "#FFFFFF" && shadeColour != "#000000") + colour = "#FFFFFF" + shadeColour = "#000000" + to_chat(user, "You will now draw in white and black with this marker.") + else + colour = "#000000" + shadeColour = "#FFFFFF" + to_chat(user, "You will now draw in black and white with this marker.") + return + +/obj/item/weapon/pen/crayon/marker/rainbow + icon_state = "markerrainbow" + colour = "#FFF000" + shadeColour = "#000FFF" + colourName = "rainbow" + uses = 0 + +/obj/item/weapon/pen/crayon/marker/rainbow/attack_self(mob/living/user as mob) + colour = input(user, "Please select the main colour.", "Marker colour") as color + shadeColour = input(user, "Please select the shade colour.", "Marker colour") as color + return + +/obj/item/weapon/pen/crayon/marker/attack(mob/living/M as mob, mob/living/user as mob) + if(M == user) + to_chat(user, "You take a bite of the marker and swallow it.") + user.nutrition += 1 + user.reagents.add_reagent("marker_ink",6) + if(uses) + uses -= 5 + if(uses <= 0) + to_chat(user, "You ate the marker!") + qdel(src) + else + ..() + +/obj/item/weapon/pen/crayon/attack_self(var/mob/user) + return diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index ac73bf97c8a..0ba1de02356 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -1,185 +1,185 @@ -/obj/item/device/aicard - name = "intelliCore" - desc = "Used to preserve and transport an AI." - icon = 'icons/obj/pda.dmi' - icon_state = "aicard" // aicard-full - item_state = "aicard" - w_class = ITEMSIZE_NORMAL - slot_flags = SLOT_BELT - show_messages = 0 - preserve_item = 1 - - var/flush = null - origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 4) - - var/mob/living/silicon/ai/carded_ai - -/obj/item/device/aicard/attack(mob/living/silicon/decoy/M as mob, mob/user as mob) - if (!istype (M, /mob/living/silicon/decoy)) - return ..() - else - M.death() - to_chat(user, "ERROR ERROR ERROR") - -/obj/item/device/aicard/attack_self(mob/user) - tgui_interact(user) - -/obj/item/device/aicard/tgui_interact(mob/user, datum/tgui/ui = null, datum/tgui_state/custom_state) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AICard", "[name]") // 600, 394 - ui.open() - if(custom_state) - ui.set_state(custom_state) - -/obj/item/device/aicard/tgui_state(mob/user) - return GLOB.tgui_inventory_state - -/obj/item/device/aicard/tgui_data(mob/user) - var/data[0] - - data["has_ai"] = carded_ai != null - if(carded_ai) - data["name"] = carded_ai.name - data["integrity"] = carded_ai.hardware_integrity() - data["backup_capacitor"] = carded_ai.backup_capacitor() - data["radio"] = !carded_ai.aiRadio.disabledAi - data["wireless"] = !carded_ai.control_disabled - data["operational"] = carded_ai.stat != DEAD - data["flushing"] = flush - - var/laws[0] - for(var/datum/ai_law/law in carded_ai.laws.all_laws()) - if(law in carded_ai.laws.ion_laws) // If we're an ion law, give it an ion index code - laws.Add(ionnum() + ". " + law.law) - else - laws.Add(num2text(law.get_index()) + ". " + law.law) - data["laws"] = laws - data["has_laws"] = length(carded_ai.laws.all_laws()) - - return data - -/obj/item/device/aicard/tgui_act(action, params) - if(..()) - return TRUE - - if(!carded_ai) - return - - var/user = usr - switch(action) - if("wipe") - msg_admin_attack("[key_name_admin(user)] wiped [key_name_admin(AI)] with \the [src].") - add_attack_logs(user,carded_ai,"Purged from AI Card") - INVOKE_ASYNC(src, PROC_REF(wipe_ai)) - if("radio") - carded_ai.aiRadio.disabledAi = !carded_ai.aiRadio.disabledAi - to_chat(carded_ai, "Your Subspace Transceiver has been [carded_ai.aiRadio.disabledAi ? "disabled" : "enabled"]!") - to_chat(user, "You [carded_ai.aiRadio.disabledAi ? "disable" : "enable"] the AI's Subspace Transceiver.") - if("wireless") - carded_ai.control_disabled = !carded_ai.control_disabled - to_chat(carded_ai, "Your wireless interface has been [carded_ai.control_disabled ? "disabled" : "enabled"]!") - to_chat(user, "You [carded_ai.control_disabled ? "disable" : "enable"] the AI's wireless interface.") - if(carded_ai.control_disabled && carded_ai.deployed_shell) - carded_ai.disconnect_shell("Disconnecting from remote shell due to [src] wireless access interface being disabled.") - update_icon() - - return TRUE - -/obj/item/device/aicard/update_icon() - cut_overlays() - if(carded_ai) - if (!carded_ai.control_disabled) - add_overlay("aicard-on") - if(carded_ai.stat) - icon_state = "aicard-404" - else - icon_state = "aicard-full" - else - icon_state = "aicard" - -/obj/item/device/aicard/proc/grab_ai(var/mob/living/silicon/ai/ai, var/mob/living/user) - if(!ai.client && !ai.deployed_shell) - to_chat(user, "ERROR: AI [ai.name] is offline. Unable to transfer.") - return 0 - - if(carded_ai) - to_chat(user, "Transfer failed: Existing AI found on remote device. Remove existing AI to install a new one.") - return 0 - - if(!user.IsAdvancedToolUser() && isanimal(user)) - var/mob/living/simple_mob/S = user - if(!S.IsHumanoidToolUser(src)) - return 0 - - user.visible_message("\The [user] starts transferring \the [ai] into \the [src]...", "You start transferring \the [ai] into \the [src]...") - show_message(span("critical", "\The [user] is transferring you into \the [src]!")) - - if(do_after(user, 100)) - if(carded_ai) - to_chat(user, "Transfer failed: Existing AI found on remote device. Remove existing AI to install a new one.") - return 0 - if(istype(ai.loc, /turf/)) - new /obj/structure/AIcore/deactivated(get_turf(ai)) - - ai.carded = 1 - add_attack_logs(user,ai,"Extracted into AI Card") - src.name = "[initial(name)] - [ai.name]" - - ai.loc = src - ai.destroy_eyeobj(src) - ai.cancel_camera() - ai.control_disabled = 1 - ai.aiRestorePowerRoutine = 0 - carded_ai = ai - ai.disconnect_shell("Disconnected from remote shell due to core intelligence transfer.") //If the AI is controlling a borg, force the player back to core! - - if(ai.client) - to_chat(ai, "You have been transferred into a mobile core. Remote access lost.") - if(user.client) - to_chat(ai, "Transfer successful: [ai.name] extracted from current device and placed within mobile core.") - - ai.canmove = 1 - update_icon() - return 1 - -/obj/item/device/aicard/proc/clear() - if(carded_ai && istype(carded_ai.loc, /turf)) - carded_ai.canmove = 0 - carded_ai.carded = 0 - name = initial(name) - carded_ai = null - update_icon() - -/obj/item/device/aicard/see_emote(mob/living/M, text) - if(carded_ai && carded_ai.client) - var/rendered = "[text]" - carded_ai.show_message(rendered, 2) - ..() - -/obj/item/device/aicard/show_message(msg, type, alt, alt_type) - if(carded_ai && carded_ai.client) - var/rendered = "[msg]" - carded_ai.show_message(rendered, type) - ..() - -/obj/item/device/aicard/relaymove(var/mob/user, var/direction) - if(user.stat || user.stunned) - return - var/obj/item/weapon/rig/rig = src.get_rig() - if(istype(rig)) - rig.forced_move(direction, user) - -/obj/item/device/aicard/proc/wipe_ai() - var/mob/living/silicon/ai/AI = carded_ai - flush = TRUE - AI.suiciding = TRUE - to_chat(AI, "Your power has been disabled!") - while(AI && AI.stat != DEAD) - // This is absolutely evil and I love it. - if(AI.deployed_shell && prob(AI.oxyloss)) //You feel it creeping? Eventually will reach 100, resulting in the second half of the AI's remaining life being lonely. - AI.disconnect_shell("Disconnecting from remote shell due to insufficent power.") - AI.adjustOxyLoss(2) - AI.updatehealth() - sleep(10) - flush = FALSE +/obj/item/device/aicard + name = "intelliCore" + desc = "Used to preserve and transport an AI." + icon = 'icons/obj/pda.dmi' + icon_state = "aicard" // aicard-full + item_state = "aicard" + w_class = ITEMSIZE_NORMAL + slot_flags = SLOT_BELT + show_messages = 0 + preserve_item = 1 + + var/flush = null + origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 4) + + var/mob/living/silicon/ai/carded_ai + +/obj/item/device/aicard/attack(mob/living/silicon/decoy/M as mob, mob/user as mob) + if (!istype (M, /mob/living/silicon/decoy)) + return ..() + else + M.death() + to_chat(user, "ERROR ERROR ERROR") + +/obj/item/device/aicard/attack_self(mob/user) + tgui_interact(user) + +/obj/item/device/aicard/tgui_interact(mob/user, datum/tgui/ui = null, datum/tgui_state/custom_state) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AICard", "[name]") // 600, 394 + ui.open() + if(custom_state) + ui.set_state(custom_state) + +/obj/item/device/aicard/tgui_state(mob/user) + return GLOB.tgui_inventory_state + +/obj/item/device/aicard/tgui_data(mob/user) + var/data[0] + + data["has_ai"] = carded_ai != null + if(carded_ai) + data["name"] = carded_ai.name + data["integrity"] = carded_ai.hardware_integrity() + data["backup_capacitor"] = carded_ai.backup_capacitor() + data["radio"] = !carded_ai.aiRadio.disabledAi + data["wireless"] = !carded_ai.control_disabled + data["operational"] = carded_ai.stat != DEAD + data["flushing"] = flush + + var/laws[0] + for(var/datum/ai_law/law in carded_ai.laws.all_laws()) + if(law in carded_ai.laws.ion_laws) // If we're an ion law, give it an ion index code + laws.Add(ionnum() + ". " + law.law) + else + laws.Add(num2text(law.get_index()) + ". " + law.law) + data["laws"] = laws + data["has_laws"] = length(carded_ai.laws.all_laws()) + + return data + +/obj/item/device/aicard/tgui_act(action, params) + if(..()) + return TRUE + + if(!carded_ai) + return + + var/user = usr + switch(action) + if("wipe") + msg_admin_attack("[key_name_admin(user)] wiped [key_name_admin(AI)] with \the [src].") + add_attack_logs(user,carded_ai,"Purged from AI Card") + INVOKE_ASYNC(src, PROC_REF(wipe_ai)) + if("radio") + carded_ai.aiRadio.disabledAi = !carded_ai.aiRadio.disabledAi + to_chat(carded_ai, "Your Subspace Transceiver has been [carded_ai.aiRadio.disabledAi ? "disabled" : "enabled"]!") + to_chat(user, "You [carded_ai.aiRadio.disabledAi ? "disable" : "enable"] the AI's Subspace Transceiver.") + if("wireless") + carded_ai.control_disabled = !carded_ai.control_disabled + to_chat(carded_ai, "Your wireless interface has been [carded_ai.control_disabled ? "disabled" : "enabled"]!") + to_chat(user, "You [carded_ai.control_disabled ? "disable" : "enable"] the AI's wireless interface.") + if(carded_ai.control_disabled && carded_ai.deployed_shell) + carded_ai.disconnect_shell("Disconnecting from remote shell due to [src] wireless access interface being disabled.") + update_icon() + + return TRUE + +/obj/item/device/aicard/update_icon() + cut_overlays() + if(carded_ai) + if (!carded_ai.control_disabled) + add_overlay("aicard-on") + if(carded_ai.stat) + icon_state = "aicard-404" + else + icon_state = "aicard-full" + else + icon_state = "aicard" + +/obj/item/device/aicard/proc/grab_ai(var/mob/living/silicon/ai/ai, var/mob/living/user) + if(!ai.client && !ai.deployed_shell) + to_chat(user, "ERROR: AI [ai.name] is offline. Unable to transfer.") + return 0 + + if(carded_ai) + to_chat(user, "Transfer failed: Existing AI found on remote device. Remove existing AI to install a new one.") + return 0 + + if(!user.IsAdvancedToolUser() && isanimal(user)) + var/mob/living/simple_mob/S = user + if(!S.IsHumanoidToolUser(src)) + return 0 + + user.visible_message("\The [user] starts transferring \the [ai] into \the [src]...", "You start transferring \the [ai] into \the [src]...") + show_message(span("critical", "\The [user] is transferring you into \the [src]!")) + + if(do_after(user, 100)) + if(carded_ai) + to_chat(user, "Transfer failed: Existing AI found on remote device. Remove existing AI to install a new one.") + return 0 + if(istype(ai.loc, /turf/)) + new /obj/structure/AIcore/deactivated(get_turf(ai)) + + ai.carded = 1 + add_attack_logs(user,ai,"Extracted into AI Card") + src.name = "[initial(name)] - [ai.name]" + + ai.loc = src + ai.destroy_eyeobj(src) + ai.cancel_camera() + ai.control_disabled = 1 + ai.aiRestorePowerRoutine = 0 + carded_ai = ai + ai.disconnect_shell("Disconnected from remote shell due to core intelligence transfer.") //If the AI is controlling a borg, force the player back to core! + + if(ai.client) + to_chat(ai, "You have been transferred into a mobile core. Remote access lost.") + if(user.client) + to_chat(ai, "Transfer successful: [ai.name] extracted from current device and placed within mobile core.") + + ai.canmove = 1 + update_icon() + return 1 + +/obj/item/device/aicard/proc/clear() + if(carded_ai && istype(carded_ai.loc, /turf)) + carded_ai.canmove = 0 + carded_ai.carded = 0 + name = initial(name) + carded_ai = null + update_icon() + +/obj/item/device/aicard/see_emote(mob/living/M, text) + if(carded_ai && carded_ai.client) + var/rendered = "[text]" + carded_ai.show_message(rendered, 2) + ..() + +/obj/item/device/aicard/show_message(msg, type, alt, alt_type) + if(carded_ai && carded_ai.client) + var/rendered = "[msg]" + carded_ai.show_message(rendered, type) + ..() + +/obj/item/device/aicard/relaymove(var/mob/user, var/direction) + if(user.stat || user.stunned) + return + var/obj/item/weapon/rig/rig = src.get_rig() + if(istype(rig)) + rig.forced_move(direction, user) + +/obj/item/device/aicard/proc/wipe_ai() + var/mob/living/silicon/ai/AI = carded_ai + flush = TRUE + AI.suiciding = TRUE + to_chat(AI, "Your power has been disabled!") + while(AI && AI.stat != DEAD) + // This is absolutely evil and I love it. + if(AI.deployed_shell && prob(AI.oxyloss)) //You feel it creeping? Eventually will reach 100, resulting in the second half of the AI's remaining life being lonely. + AI.disconnect_shell("Disconnecting from remote shell due to insufficent power.") + AI.adjustOxyLoss(2) + AI.updatehealth() + sleep(10) + flush = FALSE diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm index 9a9e7ea0ae1..054896cc387 100644 --- a/code/game/objects/items/devices/chameleonproj.dm +++ b/code/game/objects/items/devices/chameleonproj.dm @@ -1,147 +1,147 @@ -/obj/item/device/chameleon - name = "chameleon projector" - icon_state = "shield0" - slot_flags = SLOT_BELT - item_state = "electronic" - throwforce = 5.0 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_SMALL - origin_tech = list(TECH_ILLEGAL = 4, TECH_MAGNET = 4) - var/can_use = 1 - var/obj/effect/dummy/chameleon/active_dummy = null - var/saved_item = /obj/item/trash/cigbutt - var/saved_icon = 'icons/inventory/face/item.dmi' - var/saved_icon_state = "cigbutt" - var/saved_overlays - -/obj/item/device/chameleon/dropped() - disrupt() - ..() - -/obj/item/device/chameleon/equipped() - ..() - disrupt() - ..() - -/obj/item/device/chameleon/attack_self() - toggle() - -/obj/item/device/chameleon/afterattack(atom/target, mob/user , proximity) - if(!proximity) return - if(!active_dummy) - if(istype(target,/obj/item) && !istype(target, /obj/item/weapon/disk/nuclear)) - playsound(src, 'sound/weapons/flash.ogg', 100, 1, -6) - to_chat(user, "Scanned [target].") - saved_item = target.type - saved_icon = target.icon - saved_icon_state = target.icon_state - saved_overlays = target.overlays - -/obj/item/device/chameleon/proc/toggle() - if(!can_use || !saved_item) return - if(active_dummy) - eject_all() - playsound(src, 'sound/effects/pop.ogg', 100, 1, -6) - qdel(active_dummy) - active_dummy = null - to_chat(usr, "You deactivate the [src].") - var/obj/effect/overlay/T = new /obj/effect/overlay(get_turf(src)) - T.icon = 'icons/effects/effects.dmi' - flick("emppulse",T) - spawn(8) qdel(T) - else - playsound(src, 'sound/effects/pop.ogg', 100, 1, -6) - var/obj/O = new saved_item(src) - if(!O) return - var/obj/effect/dummy/chameleon/C = new /obj/effect/dummy/chameleon(usr.loc) - C.activate(O, usr, saved_icon, saved_icon_state, saved_overlays, src) - qdel(O) - to_chat(usr, "You activate the [src].") - var/obj/effect/overlay/T = new/obj/effect/overlay(get_turf(src)) - T.icon = 'icons/effects/effects.dmi' - flick("emppulse",T) - spawn(8) qdel(T) - -/obj/item/device/chameleon/proc/disrupt(var/delete_dummy = 1) - if(active_dummy) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread - spark_system.set_up(5, 0, src) - spark_system.attach(src) - spark_system.start() - eject_all() - if(delete_dummy) - qdel(active_dummy) - active_dummy = null - can_use = 0 - spawn(50) can_use = 1 - -/obj/item/device/chameleon/proc/eject_all() - for(var/atom/movable/A in active_dummy) - A.loc = active_dummy.loc - if(ismob(A)) - var/mob/M = A - M.reset_view(null) - -/obj/effect/dummy/chameleon - name = "" - desc = "" - density = FALSE - anchored = TRUE - var/can_move = 1 - var/obj/item/device/chameleon/master = null - -/obj/effect/dummy/chameleon/proc/activate(var/obj/O, var/mob/M, new_icon, new_iconstate, new_overlays, var/obj/item/device/chameleon/C) - name = O.name - desc = O.desc - icon = new_icon - icon_state = new_iconstate - overlays = new_overlays - set_dir(O.dir) - M.loc = src - master = C - master.active_dummy = src - -/obj/effect/dummy/chameleon/attackby() - for(var/mob/M in src) - to_chat(M, "Your chameleon-projector deactivates.") - master.disrupt() - -/obj/effect/dummy/chameleon/attack_hand() - for(var/mob/M in src) - to_chat(M, "Your chameleon-projector deactivates.") - master.disrupt() - -/obj/effect/dummy/chameleon/ex_act() - for(var/mob/M in src) - to_chat(M, "Your chameleon-projector deactivates.") - master.disrupt() - -/obj/effect/dummy/chameleon/bullet_act() - for(var/mob/M in src) - to_chat(M, "Your chameleon-projector deactivates.") - ..() - master.disrupt() - -/obj/effect/dummy/chameleon/relaymove(var/mob/user, direction) - if(istype(loc, /turf/space)) return //No magical space movement! - - if(can_move) - can_move = 0 - switch(user.bodytemperature) - if(300 to INFINITY) - spawn(10) can_move = 1 - if(295 to 300) - spawn(13) can_move = 1 - if(280 to 295) - spawn(16) can_move = 1 - if(260 to 280) - spawn(20) can_move = 1 - else - spawn(25) can_move = 1 - step(src, direction) - return - -/obj/effect/dummy/chameleon/Destroy() - master.disrupt(0) - ..() +/obj/item/device/chameleon + name = "chameleon projector" + icon_state = "shield0" + slot_flags = SLOT_BELT + item_state = "electronic" + throwforce = 5.0 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_SMALL + origin_tech = list(TECH_ILLEGAL = 4, TECH_MAGNET = 4) + var/can_use = 1 + var/obj/effect/dummy/chameleon/active_dummy = null + var/saved_item = /obj/item/trash/cigbutt + var/saved_icon = 'icons/inventory/face/item.dmi' + var/saved_icon_state = "cigbutt" + var/saved_overlays + +/obj/item/device/chameleon/dropped() + disrupt() + ..() + +/obj/item/device/chameleon/equipped() + ..() + disrupt() + ..() + +/obj/item/device/chameleon/attack_self() + toggle() + +/obj/item/device/chameleon/afterattack(atom/target, mob/user , proximity) + if(!proximity) return + if(!active_dummy) + if(istype(target,/obj/item) && !istype(target, /obj/item/weapon/disk/nuclear)) + playsound(src, 'sound/weapons/flash.ogg', 100, 1, -6) + to_chat(user, "Scanned [target].") + saved_item = target.type + saved_icon = target.icon + saved_icon_state = target.icon_state + saved_overlays = target.overlays + +/obj/item/device/chameleon/proc/toggle() + if(!can_use || !saved_item) return + if(active_dummy) + eject_all() + playsound(src, 'sound/effects/pop.ogg', 100, 1, -6) + qdel(active_dummy) + active_dummy = null + to_chat(usr, "You deactivate the [src].") + var/obj/effect/overlay/T = new /obj/effect/overlay(get_turf(src)) + T.icon = 'icons/effects/effects.dmi' + flick("emppulse",T) + spawn(8) qdel(T) + else + playsound(src, 'sound/effects/pop.ogg', 100, 1, -6) + var/obj/O = new saved_item(src) + if(!O) return + var/obj/effect/dummy/chameleon/C = new /obj/effect/dummy/chameleon(usr.loc) + C.activate(O, usr, saved_icon, saved_icon_state, saved_overlays, src) + qdel(O) + to_chat(usr, "You activate the [src].") + var/obj/effect/overlay/T = new/obj/effect/overlay(get_turf(src)) + T.icon = 'icons/effects/effects.dmi' + flick("emppulse",T) + spawn(8) qdel(T) + +/obj/item/device/chameleon/proc/disrupt(var/delete_dummy = 1) + if(active_dummy) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread + spark_system.set_up(5, 0, src) + spark_system.attach(src) + spark_system.start() + eject_all() + if(delete_dummy) + qdel(active_dummy) + active_dummy = null + can_use = 0 + spawn(50) can_use = 1 + +/obj/item/device/chameleon/proc/eject_all() + for(var/atom/movable/A in active_dummy) + A.loc = active_dummy.loc + if(ismob(A)) + var/mob/M = A + M.reset_view(null) + +/obj/effect/dummy/chameleon + name = "" + desc = "" + density = FALSE + anchored = TRUE + var/can_move = 1 + var/obj/item/device/chameleon/master = null + +/obj/effect/dummy/chameleon/proc/activate(var/obj/O, var/mob/M, new_icon, new_iconstate, new_overlays, var/obj/item/device/chameleon/C) + name = O.name + desc = O.desc + icon = new_icon + icon_state = new_iconstate + overlays = new_overlays + set_dir(O.dir) + M.loc = src + master = C + master.active_dummy = src + +/obj/effect/dummy/chameleon/attackby() + for(var/mob/M in src) + to_chat(M, "Your chameleon-projector deactivates.") + master.disrupt() + +/obj/effect/dummy/chameleon/attack_hand() + for(var/mob/M in src) + to_chat(M, "Your chameleon-projector deactivates.") + master.disrupt() + +/obj/effect/dummy/chameleon/ex_act() + for(var/mob/M in src) + to_chat(M, "Your chameleon-projector deactivates.") + master.disrupt() + +/obj/effect/dummy/chameleon/bullet_act() + for(var/mob/M in src) + to_chat(M, "Your chameleon-projector deactivates.") + ..() + master.disrupt() + +/obj/effect/dummy/chameleon/relaymove(var/mob/user, direction) + if(istype(loc, /turf/space)) return //No magical space movement! + + if(can_move) + can_move = 0 + switch(user.bodytemperature) + if(300 to INFINITY) + spawn(10) can_move = 1 + if(295 to 300) + spawn(13) can_move = 1 + if(280 to 295) + spawn(16) can_move = 1 + if(260 to 280) + spawn(20) can_move = 1 + else + spawn(25) can_move = 1 + step(src, direction) + return + +/obj/effect/dummy/chameleon/Destroy() + master.disrupt(0) + ..() diff --git a/code/game/objects/items/devices/communicator/helper.dm b/code/game/objects/items/devices/communicator/helper.dm index 8d169f62c1d..cc00aef6121 100644 --- a/code/game/objects/items/devices/communicator/helper.dm +++ b/code/game/objects/items/devices/communicator/helper.dm @@ -1,97 +1,97 @@ -/obj/item/device/communicator/proc/analyze_air() - var/list/results = list() - var/turf/T = get_turf(src.loc) - if(!isnull(T)) - var/datum/gas_mixture/environment = T.return_air() - var/pressure = environment.return_pressure() - var/total_moles = environment.total_moles - if (total_moles) - var/o2_level = environment.gas["oxygen"]/total_moles - var/n2_level = environment.gas["nitrogen"]/total_moles - var/co2_level = environment.gas["carbon_dioxide"]/total_moles - var/phoron_level = environment.gas["phoron"]/total_moles - var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_level) - - // Label is what the entry is describing - // Type identifies which unit or other special characters to use - // Val is the information reported - // Bad_high/_low are the values outside of which the entry reports as dangerous - // Poor_high/_low are the values outside of which the entry reports as unideal - // Values were extracted from the template itself - results = list( - list("entry" = "Pressure", "units" = "kPa", "val" = "[round(pressure,0.1)]", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80), - list("entry" = "Temperature", "units" = "\u00B0" + "C", "val" = "[round(environment.temperature-T0C,0.1)]", "bad_high" = 35, "poor_high" = 25, "poor_low" = 15, "bad_low" = 5), - list("entry" = "Oxygen", "units" = "kPa", "val" = "[round(o2_level*100,0.1)]", "bad_high" = 140, "poor_high" = 135, "poor_low" = 19, "bad_low" = 17), - list("entry" = "Nitrogen", "units" = "kPa", "val" = "[round(n2_level*100,0.1)]", "bad_high" = 105, "poor_high" = 85, "poor_low" = 50, "bad_low" = 40), - list("entry" = "Carbon Dioxide", "units" = "kPa", "val" = "[round(co2_level*100,0.1)]", "bad_high" = 10, "poor_high" = 5, "poor_low" = 0, "bad_low" = 0), - list("entry" = "Phoron", "units" = "kPa", "val" = "[round(phoron_level*100,0.01)]", "bad_high" = 0.5, "poor_high" = 0, "poor_low" = 0, "bad_low" = 0), - list("entry" = "Other", "units" = "kPa", "val" = "[round(unknown_level, 0.01)]", "bad_high" = 1, "poor_high" = 0.5, "poor_low" = 0, "bad_low" = 0) - ) - - if(isnull(results)) - results = list(list("entry" = "pressure", "units" = "kPa", "val" = "0", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80)) - return results - - -// Proc - compile_news() -// Parameters - none -// Description - Returns the list of newsfeeds, compiled for template processing -/obj/item/device/communicator/proc/compile_news() - var/list/feeds = list() - for(var/datum/feed_channel/channel in news_network.network_channels) - var/list/messages = list() - if(!channel.censored && channel.channel_name != "Vir News Network") //Do not load the 'IC news' channel as it is simply too long. - var/index = 0 - for(var/datum/feed_message/FM in channel.messages) - index++ - var/list/msgdata = list( - "author" = FM.author, - "body" = FM.body, - "img" = null, - "message_type" = FM.message_type, - "time_stamp" = FM.time_stamp, - "caption" = FM.caption, - "index" = index - ) - if(FM.img) - msgdata["img"] = icon2base64(FM.img) - messages[++messages.len] = msgdata - - feeds[++feeds.len] = list( - "name" = channel.channel_name, - "censored" = channel.censored, - "author" = channel.author, - "messages" = messages, - "index" = feeds.len + 1 // actually align them, since I guess the population of the list doesn't occur until after the evaluation of the new entry's contents - ) - return feeds - -// Proc - get_recent_news() -// Parameters - none -// Description - Returns the latest three newscasts, compiled for template processing -/obj/item/device/communicator/proc/get_recent_news() - var/list/news = list() - - // Compile all the newscasts - for(var/datum/feed_channel/channel in news_network.network_channels) - if(!channel.censored) - for(var/datum/feed_message/FM in channel.messages) - var/body = replacetext(FM.body, "\n", "
                    ") - news[++news.len] = list( - "channel" = channel.channel_name, - "author" = FM.author, - "body" = body, - "message_type" = FM.message_type, - "time_stamp" = FM.time_stamp, - "has_image" = (FM.img != null), - "caption" = FM.caption, - "time" = FM.post_time - ) - - // Cut out all but the youngest three - if(news.len > 3) - sortByKey(news, "time") - news.Cut(1, news.len - 2) // Last three have largest timestamps, youngest posts - news.Swap(1, 3) // List is sorted in ascending order of timestamp, we want descending - - return news +/obj/item/device/communicator/proc/analyze_air() + var/list/results = list() + var/turf/T = get_turf(src.loc) + if(!isnull(T)) + var/datum/gas_mixture/environment = T.return_air() + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles + if (total_moles) + var/o2_level = environment.gas["oxygen"]/total_moles + var/n2_level = environment.gas["nitrogen"]/total_moles + var/co2_level = environment.gas["carbon_dioxide"]/total_moles + var/phoron_level = environment.gas["phoron"]/total_moles + var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_level) + + // Label is what the entry is describing + // Type identifies which unit or other special characters to use + // Val is the information reported + // Bad_high/_low are the values outside of which the entry reports as dangerous + // Poor_high/_low are the values outside of which the entry reports as unideal + // Values were extracted from the template itself + results = list( + list("entry" = "Pressure", "units" = "kPa", "val" = "[round(pressure,0.1)]", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80), + list("entry" = "Temperature", "units" = "\u00B0" + "C", "val" = "[round(environment.temperature-T0C,0.1)]", "bad_high" = 35, "poor_high" = 25, "poor_low" = 15, "bad_low" = 5), + list("entry" = "Oxygen", "units" = "kPa", "val" = "[round(o2_level*100,0.1)]", "bad_high" = 140, "poor_high" = 135, "poor_low" = 19, "bad_low" = 17), + list("entry" = "Nitrogen", "units" = "kPa", "val" = "[round(n2_level*100,0.1)]", "bad_high" = 105, "poor_high" = 85, "poor_low" = 50, "bad_low" = 40), + list("entry" = "Carbon Dioxide", "units" = "kPa", "val" = "[round(co2_level*100,0.1)]", "bad_high" = 10, "poor_high" = 5, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Phoron", "units" = "kPa", "val" = "[round(phoron_level*100,0.01)]", "bad_high" = 0.5, "poor_high" = 0, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Other", "units" = "kPa", "val" = "[round(unknown_level, 0.01)]", "bad_high" = 1, "poor_high" = 0.5, "poor_low" = 0, "bad_low" = 0) + ) + + if(isnull(results)) + results = list(list("entry" = "pressure", "units" = "kPa", "val" = "0", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80)) + return results + + +// Proc - compile_news() +// Parameters - none +// Description - Returns the list of newsfeeds, compiled for template processing +/obj/item/device/communicator/proc/compile_news() + var/list/feeds = list() + for(var/datum/feed_channel/channel in news_network.network_channels) + var/list/messages = list() + if(!channel.censored && channel.channel_name != "Vir News Network") //Do not load the 'IC news' channel as it is simply too long. + var/index = 0 + for(var/datum/feed_message/FM in channel.messages) + index++ + var/list/msgdata = list( + "author" = FM.author, + "body" = FM.body, + "img" = null, + "message_type" = FM.message_type, + "time_stamp" = FM.time_stamp, + "caption" = FM.caption, + "index" = index + ) + if(FM.img) + msgdata["img"] = icon2base64(FM.img) + messages[++messages.len] = msgdata + + feeds[++feeds.len] = list( + "name" = channel.channel_name, + "censored" = channel.censored, + "author" = channel.author, + "messages" = messages, + "index" = feeds.len + 1 // actually align them, since I guess the population of the list doesn't occur until after the evaluation of the new entry's contents + ) + return feeds + +// Proc - get_recent_news() +// Parameters - none +// Description - Returns the latest three newscasts, compiled for template processing +/obj/item/device/communicator/proc/get_recent_news() + var/list/news = list() + + // Compile all the newscasts + for(var/datum/feed_channel/channel in news_network.network_channels) + if(!channel.censored) + for(var/datum/feed_message/FM in channel.messages) + var/body = replacetext(FM.body, "\n", "
                    ") + news[++news.len] = list( + "channel" = channel.channel_name, + "author" = FM.author, + "body" = body, + "message_type" = FM.message_type, + "time_stamp" = FM.time_stamp, + "has_image" = (FM.img != null), + "caption" = FM.caption, + "time" = FM.post_time + ) + + // Cut out all but the youngest three + if(news.len > 3) + sortByKey(news, "time") + news.Cut(1, news.len - 2) // Last three have largest timestamps, youngest posts + news.Swap(1, 3) // List is sorted in ascending order of timestamp, we want descending + + return news diff --git a/code/game/objects/items/devices/communicator/messaging.dm b/code/game/objects/items/devices/communicator/messaging.dm index e5a27902b1b..2f9249dd5dc 100644 --- a/code/game/objects/items/devices/communicator/messaging.dm +++ b/code/game/objects/items/devices/communicator/messaging.dm @@ -1,185 +1,185 @@ -// Proc: receive_exonet_message() -// Parameters: 4 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received, -// text - message text to send if message is of type "text") -// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response and IM function. -/obj/item/device/communicator/receive_exonet_message(var/atom/origin_atom, origin_address, message, text) - if(message == "voice") - if(isobserver(origin_atom) || istype(origin_atom, /obj/item/device/communicator)) - if(origin_atom in voice_invites) - var/user = null - if(ismob(origin_atom.loc)) - user = origin_atom.loc - open_connection(user, origin_atom) - return - else if(origin_atom in voice_requests) - return //Spam prevention - else - request(origin_atom) - if(message == "ping") - if(network_visibility) - var/random = rand(200,350) - random = random / 10 - exonet.send_message(origin_address, "64 bytes received from [exonet.address] ecmp_seq=1 ttl=51 time=[random] ms") - if(message == "text") - request_im(origin_atom, origin_address, text) - return - -// Proc: receive_exonet_message() -// Parameters: 3 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received) -// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response. -/mob/observer/dead/receive_exonet_message(origin_atom, origin_address, message, text) - if(message == "voice") - if(istype(origin_atom, /obj/item/device/communicator)) - var/obj/item/device/communicator/comm = origin_atom - if(src in comm.voice_invites) - comm.open_connection(src) - return - to_chat(src, "\icon[origin_atom][bicon(origin_atom)] Receiving communicator request from [origin_atom]. To answer, use the Call Communicator \ - verb, and select that name to answer the call.") - src << 'sound/machines/defib_SafetyOn.ogg' - comm.voice_invites |= src - if(message == "ping") - if(client && client.prefs.communicator_visibility) - var/random = rand(450,700) - random = random / 10 - exonet.send_message(origin_address, "64 bytes received from [exonet.address] ecmp_seq=1 ttl=51 time=[random] ms") - if(message == "text") - to_chat(src, "\icon[origin_atom][bicon(origin_atom)] Received text message from [origin_atom]: \"[text]\"") - src << 'sound/machines/defib_safetyOff.ogg' - exonet_messages.Add("From [origin_atom]:
                    [text]") - return - -// Proc: request_im() -// Parameters: 3 (candidate - the communicator wanting to message the device, origin_address - the address of the sender, text - the message) -// Description: Response to a communicator trying to message the device. -// Adds them to the list of people that have messaged this device and adds the message to the message list. -/obj/item/device/communicator/proc/request_im(var/atom/candidate, var/origin_address, var/text) - var/who = null - if(isobserver(candidate)) - var/mob/observer/dead/ghost = candidate - who = ghost.name - im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) - else if(istype(candidate, /obj/item/device/communicator)) - var/obj/item/device/communicator/comm = candidate - who = comm.owner - comm.im_contacts |= src - im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) - else if(istype(candidate, /obj/item/integrated_circuit)) - var/obj/item/integrated_circuit/CIRC = candidate - who = CIRC - im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) - else return - - im_contacts |= candidate - - if(!who) - return - - if(ringer) - var/S - if(ttone in ttone_sound) - S = ttone_sound[ttone] - else - S = 'sound/machines/twobeep.ogg' - - playsound(src, S, 50, 1) - for (var/mob/O in hearers(2, loc)) - O.show_message(text("\icon[src][bicon(src)] *[ttone]*")) - - alert_called = 1 - update_icon() - - //Search for holder of the device. - var/mob/living/L = null - if(loc && isliving(loc)) - L = loc - - if(L) - to_chat(L, "\icon[src][bicon(src)] Message from [who]: \"[text]\" (Reply)") - -// This is the only Topic the communicators really uses -/obj/item/device/communicator/Topic(href, href_list) - switch(href_list["action"]) - if("Reply") - var/obj/item/device/communicator/comm = locate(href_list["target"]) - var/message = tgui_input_text(usr, "Enter your message below.", "Reply") - - if(message) - exonet.send_message(comm.exonet.address, "text", message) - im_list += list(list("address" = exonet.address, "to_address" = comm.exonet.address, "im" = message)) - log_pda("(COMM: [src]) sent \"[message]\" to [exonet.get_atom_from_address(comm.exonet.address)]", usr) - to_chat(usr, "\icon[src][bicon(src)] Sent message to [istype(comm, /obj/item/device/communicator) ? comm.owner : comm.name], \"[message]\" (Reply)") - -// Verb: text_communicator() -// Parameters: None -// Description: Allows a ghost to send a text message to a communicator. -/mob/observer/dead/verb/text_communicator() - set category = "Ghost" - set name = "Text Communicator" - set desc = "If there is a communicator available, send a text message to it." - - if(ticker.current_state < GAME_STATE_PLAYING) - to_chat(src, "The game hasn't started yet!") - return - - if (!src.stat) - return - - if (usr != src) - return //something is terribly wrong - - for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling. - if(src.client.prefs.real_name == L.real_name) - to_chat(src, "Your identity is already present in the game world. Please load in a different character first.") - return - - var/obj/machinery/exonet_node/E = get_exonet_node() - if(!E || !E.on || !E.allow_external_communicators) - to_chat(src, "The Exonet node at telecommunications is down at the moment, or is actively blocking you, \ - so your call can't go through.") - return - - var/list/choices = list() - for(var/obj/item/device/communicator/comm in all_communicators) - if(!comm.network_visibility || !comm.exonet || !comm.exonet.address) - continue - choices.Add(comm) - - if(!choices.len) - to_chat(src, "There are no available communicators, sorry.") - return - - var/choice = tgui_input_list(src,"Send a text message to whom?", "Recipient Choice", choices) - if(choice) - var/obj/item/device/communicator/chosen_communicator = choice - var/mob/observer/dead/O = src - var/text_message = sanitize(tgui_input_text(src, "What do you want the message to say?", multiline = TRUE)) - if(text_message && O.exonet) - O.exonet.send_message(chosen_communicator.exonet.address, "text", text_message) - - to_chat(src, "You have sent '[text_message]' to [chosen_communicator].") - exonet_messages.Add("To [chosen_communicator]:
                    [text_message]") - log_pda("(DCOMM: [src]) sent \"[text_message]\" to [chosen_communicator]", src) - for(var/mob/M in player_list) - if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - if(istype(M, /mob/new_player) || M.forbid_seeing_deadchat) - continue - if(M == src) - continue - M.show_message("Comm IM - [src] -> [chosen_communicator]: [text_message]") - - - -// Verb: show_text_messages() -// Parameters: None -// Description: Lets ghosts review messages they've sent or received. -/mob/observer/dead/verb/show_text_messages() - set category = "Ghost" - set name = "Show Text Messages" - set desc = "Allows you to see exonet text messages you've sent and received." - - var/HTML = "Exonet Message Log" - for(var/line in exonet_messages) - HTML += line + "
                    " - HTML +="" - usr << browse(HTML, "window=log;size=400x444;border=1;can_resize=1;can_close=1;can_minimize=0") +// Proc: receive_exonet_message() +// Parameters: 4 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received, +// text - message text to send if message is of type "text") +// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response and IM function. +/obj/item/device/communicator/receive_exonet_message(var/atom/origin_atom, origin_address, message, text) + if(message == "voice") + if(isobserver(origin_atom) || istype(origin_atom, /obj/item/device/communicator)) + if(origin_atom in voice_invites) + var/user = null + if(ismob(origin_atom.loc)) + user = origin_atom.loc + open_connection(user, origin_atom) + return + else if(origin_atom in voice_requests) + return //Spam prevention + else + request(origin_atom) + if(message == "ping") + if(network_visibility) + var/random = rand(200,350) + random = random / 10 + exonet.send_message(origin_address, "64 bytes received from [exonet.address] ecmp_seq=1 ttl=51 time=[random] ms") + if(message == "text") + request_im(origin_atom, origin_address, text) + return + +// Proc: receive_exonet_message() +// Parameters: 3 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received) +// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response. +/mob/observer/dead/receive_exonet_message(origin_atom, origin_address, message, text) + if(message == "voice") + if(istype(origin_atom, /obj/item/device/communicator)) + var/obj/item/device/communicator/comm = origin_atom + if(src in comm.voice_invites) + comm.open_connection(src) + return + to_chat(src, "\icon[origin_atom][bicon(origin_atom)] Receiving communicator request from [origin_atom]. To answer, use the Call Communicator \ + verb, and select that name to answer the call.") + src << 'sound/machines/defib_SafetyOn.ogg' + comm.voice_invites |= src + if(message == "ping") + if(client && client.prefs.communicator_visibility) + var/random = rand(450,700) + random = random / 10 + exonet.send_message(origin_address, "64 bytes received from [exonet.address] ecmp_seq=1 ttl=51 time=[random] ms") + if(message == "text") + to_chat(src, "\icon[origin_atom][bicon(origin_atom)] Received text message from [origin_atom]: \"[text]\"") + src << 'sound/machines/defib_safetyOff.ogg' + exonet_messages.Add("From [origin_atom]:
                    [text]") + return + +// Proc: request_im() +// Parameters: 3 (candidate - the communicator wanting to message the device, origin_address - the address of the sender, text - the message) +// Description: Response to a communicator trying to message the device. +// Adds them to the list of people that have messaged this device and adds the message to the message list. +/obj/item/device/communicator/proc/request_im(var/atom/candidate, var/origin_address, var/text) + var/who = null + if(isobserver(candidate)) + var/mob/observer/dead/ghost = candidate + who = ghost.name + im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) + else if(istype(candidate, /obj/item/device/communicator)) + var/obj/item/device/communicator/comm = candidate + who = comm.owner + comm.im_contacts |= src + im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) + else if(istype(candidate, /obj/item/integrated_circuit)) + var/obj/item/integrated_circuit/CIRC = candidate + who = CIRC + im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) + else return + + im_contacts |= candidate + + if(!who) + return + + if(ringer) + var/S + if(ttone in ttone_sound) + S = ttone_sound[ttone] + else + S = 'sound/machines/twobeep.ogg' + + playsound(src, S, 50, 1) + for (var/mob/O in hearers(2, loc)) + O.show_message(text("\icon[src][bicon(src)] *[ttone]*")) + + alert_called = 1 + update_icon() + + //Search for holder of the device. + var/mob/living/L = null + if(loc && isliving(loc)) + L = loc + + if(L) + to_chat(L, "\icon[src][bicon(src)] Message from [who]: \"[text]\" (Reply)") + +// This is the only Topic the communicators really uses +/obj/item/device/communicator/Topic(href, href_list) + switch(href_list["action"]) + if("Reply") + var/obj/item/device/communicator/comm = locate(href_list["target"]) + var/message = tgui_input_text(usr, "Enter your message below.", "Reply") + + if(message) + exonet.send_message(comm.exonet.address, "text", message) + im_list += list(list("address" = exonet.address, "to_address" = comm.exonet.address, "im" = message)) + log_pda("(COMM: [src]) sent \"[message]\" to [exonet.get_atom_from_address(comm.exonet.address)]", usr) + to_chat(usr, "\icon[src][bicon(src)] Sent message to [istype(comm, /obj/item/device/communicator) ? comm.owner : comm.name], \"[message]\" (Reply)") + +// Verb: text_communicator() +// Parameters: None +// Description: Allows a ghost to send a text message to a communicator. +/mob/observer/dead/verb/text_communicator() + set category = "Ghost" + set name = "Text Communicator" + set desc = "If there is a communicator available, send a text message to it." + + if(ticker.current_state < GAME_STATE_PLAYING) + to_chat(src, "The game hasn't started yet!") + return + + if (!src.stat) + return + + if (usr != src) + return //something is terribly wrong + + for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling. + if(src.client.prefs.real_name == L.real_name) + to_chat(src, "Your identity is already present in the game world. Please load in a different character first.") + return + + var/obj/machinery/exonet_node/E = get_exonet_node() + if(!E || !E.on || !E.allow_external_communicators) + to_chat(src, "The Exonet node at telecommunications is down at the moment, or is actively blocking you, \ + so your call can't go through.") + return + + var/list/choices = list() + for(var/obj/item/device/communicator/comm in all_communicators) + if(!comm.network_visibility || !comm.exonet || !comm.exonet.address) + continue + choices.Add(comm) + + if(!choices.len) + to_chat(src, "There are no available communicators, sorry.") + return + + var/choice = tgui_input_list(src,"Send a text message to whom?", "Recipient Choice", choices) + if(choice) + var/obj/item/device/communicator/chosen_communicator = choice + var/mob/observer/dead/O = src + var/text_message = sanitize(tgui_input_text(src, "What do you want the message to say?", multiline = TRUE)) + if(text_message && O.exonet) + O.exonet.send_message(chosen_communicator.exonet.address, "text", text_message) + + to_chat(src, "You have sent '[text_message]' to [chosen_communicator].") + exonet_messages.Add("To [chosen_communicator]:
                    [text_message]") + log_pda("(DCOMM: [src]) sent \"[text_message]\" to [chosen_communicator]", src) + for(var/mob/M in player_list) + if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) + if(istype(M, /mob/new_player) || M.forbid_seeing_deadchat) + continue + if(M == src) + continue + M.show_message("Comm IM - [src] -> [chosen_communicator]: [text_message]") + + + +// Verb: show_text_messages() +// Parameters: None +// Description: Lets ghosts review messages they've sent or received. +/mob/observer/dead/verb/show_text_messages() + set category = "Ghost" + set name = "Show Text Messages" + set desc = "Allows you to see exonet text messages you've sent and received." + + var/HTML = "Exonet Message Log" + for(var/line in exonet_messages) + HTML += line + "
                    " + HTML +="" + usr << browse(HTML, "window=log;size=400x444;border=1;can_resize=1;can_close=1;can_minimize=0") diff --git a/code/game/objects/items/devices/communicator/phone.dm b/code/game/objects/items/devices/communicator/phone.dm index eafd3105e0b..bbf91d1ead1 100644 --- a/code/game/objects/items/devices/communicator/phone.dm +++ b/code/game/objects/items/devices/communicator/phone.dm @@ -1,366 +1,366 @@ -// Proc: add_communicating() -// Parameters: 1 (comm - the communicator to add to communicating) -// Description: Used when this communicator gets a new communicator to relay say/me messages to -/obj/item/device/communicator/proc/add_communicating(obj/item/device/communicator/comm) - if(!comm || !istype(comm)) return - - communicating |= comm - listening_objects |= src - update_icon() - -// Proc: del_communicating() -// Parameters: 1 (comm - the communicator to remove from communicating) -// Description: Used when this communicator is being asked to stop relaying say/me messages to another -/obj/item/device/communicator/proc/del_communicating(obj/item/device/communicator/comm) - if(!comm || !istype(comm)) return - - communicating.Remove(comm) - update_icon() - -// Proc: open_connection() -// Parameters: 2 (user - the person who initiated the connecting being opened, candidate - the communicator or observer that will connect to the device) -// Description: Typechecks the candidate, then calls the correct proc for further connecting. -/obj/item/device/communicator/proc/open_connection(mob/user, var/atom/candidate) - if(isobserver(candidate)) - voice_invites.Remove(candidate) - open_connection_to_ghost(user, candidate) - else - if(istype(candidate, /obj/item/device/communicator)) - open_connection_to_communicator(user, candidate) - -// Proc: open_connection_to_communicator() -// Parameters: 2 (user - the person who initiated this and will be receiving feedback information, candidate - someone else's communicator) -// Description: Adds the candidate and src to each other's communicating lists, allowing messages seen by the devices to be relayed. -/obj/item/device/communicator/proc/open_connection_to_communicator(mob/user, var/atom/candidate) - if(!istype(candidate, /obj/item/device/communicator)) - return - var/obj/item/device/communicator/comm = candidate - voice_invites.Remove(candidate) - comm.voice_requests.Remove(src) - - if(user) - comm.visible_message("\icon[src][bicon(src)] Connecting to [src].") - to_chat(user, "\icon[src][bicon(src)] Attempting to call [comm].") - sleep(10) - to_chat(user, "\icon[src][bicon(src)] Dialing internally from [station_name()], [system_name()].") - sleep(20) //If they don't have an exonet something is very wrong and we want a runtime. - to_chat(user, "\icon[src][bicon(src)] Connection re-routed to [comm] at [comm.exonet.address].") - sleep(40) - to_chat(user, "\icon[src][bicon(src)] Connection to [comm] at [comm.exonet.address] established.") - comm.visible_message("\icon[src][bicon(src)] Connection to [src] at [exonet.address] established.") - sleep(20) - - src.add_communicating(comm) - comm.add_communicating(src) - -// Proc: open_connection_to_ghost() -// Parameters: 2 (user - the person who initiated this, candidate - the ghost that will be turned into a voice mob) -// Description: Pulls the candidate ghost from deadchat, makes a new voice mob, transfers their identity, then their client. -/obj/item/device/communicator/proc/open_connection_to_ghost(mob/user, var/mob/candidate) - if(!isobserver(candidate)) - return - //Handle moving the ghost into the new shell. - announce_ghost_joinleave(candidate, 0, "They are occupying a personal communications device now.") - voice_requests.Remove(candidate) - voice_invites.Remove(candidate) - var/mob/living/voice/new_voice = new /mob/living/voice(src) //Make the voice mob the ghost is going to be. - new_voice.transfer_identity(candidate) //Now make the voice mob load from the ghost's active character in preferences. - //Do some simple logging since this is a tad risky as a concept. - var/msg = "[candidate && candidate.client ? "[candidate.client.key]" : "*no key*"] ([candidate]) has entered [src], triggered by \ - [user && user.client ? "[user.client.key]" : "*no key*"] ([user ? "[user]" : "*null*"]) at [x],[y],[z]. They have joined as [new_voice.name]." - message_admins(msg) - log_game(msg) - new_voice.mind = candidate.mind //Transfer the mind, if any. - new_voice.ckey = candidate.ckey //Finally, bring the client over. - voice_mobs.Add(new_voice) - listening_objects |= src - - var/obj/screen/blackness = new() //Makes a black screen, so the candidate can't see what's going on before actually 'connecting' to the communicator. - blackness.screen_loc = ui_entire_screen - blackness.icon = 'icons/effects/effects.dmi' - blackness.icon_state = "1" - blackness.mouse_opacity = 2 //Can't see anything! - new_voice.client.screen.Add(blackness) - - update_icon() - - //Now for some connection fluff. - if(user) - to_chat(user, "\icon[src][bicon(src)] Connecting to [candidate].") - to_chat(new_voice, "\icon[src][bicon(src)] Attempting to call [src].") - sleep(10) - to_chat(new_voice, "\icon[src][bicon(src)] Dialing to [station_name()], Kara Subsystem, [system_name()].") - sleep(20) - to_chat(new_voice, "\icon[src][bicon(src)] Connecting to [station_name()] telecommunications array.") - sleep(40) - to_chat(new_voice, "\icon[src][bicon(src)] Connection to [station_name()] telecommunications array established. Redirecting signal to [src].") - sleep(20) - - //We're connected, no need to hide everything. - new_voice.client.screen.Remove(blackness) - qdel(blackness) - - to_chat(new_voice, "\icon[src][bicon(src)] Connection to [src] established.") - to_chat(new_voice, "To talk to the person on the other end of the call, just talk normally.") - to_chat(new_voice, "If you want to end the call, use the 'Hang Up' verb. The other person can also hang up at any time.") - to_chat(new_voice, "Remember, your character does not know anything you've learned from observing!") - if(new_voice.mind) - new_voice.mind.assigned_role = "Disembodied Voice" - if(user) - to_chat(user, "\icon[src][bicon(src)] Your communicator is now connected to [candidate]'s communicator.") - -// Proc: close_connection() -// Parameters: 3 (user - the user who initiated the disconnect, target - the mob or device being disconnected, reason - string shown when disconnected) -// Description: Deletes specific voice_mobs or disconnects communicators, and shows a message to everyone when doing so. If target is null, all communicators -// and voice mobs are removed. -/obj/item/device/communicator/proc/close_connection(mob/user, var/atom/target, var/reason) - if(voice_mobs.len == 0 && communicating.len == 0) - return - - for(var/mob/living/voice/voice in voice_mobs) //Handle ghost-callers - if(target && voice != target) //If no target is inputted, it deletes all of them. - continue - to_chat(voice, "\icon[src][bicon(src)] [reason].") - visible_message("\icon[src][bicon(src)] [reason].") - voice_mobs.Remove(voice) - qdel(voice) - update_icon() - - for(var/obj/item/device/communicator/comm in communicating) //Now we handle real communicators. - if(target && comm != target) - continue - src.del_communicating(comm) - comm.del_communicating(src) - comm.visible_message("\icon[src][bicon(src)] [reason].") - visible_message("\icon[src][bicon(src)] [reason].") - if(comm.camera && video_source == comm.camera) //We hung up on the person on video - end_video() - if(camera && comm.video_source == camera) //We hung up on them while they were watching us - comm.end_video() - - if(voice_mobs.len == 0 && communicating.len == 0) - listening_objects.Remove(src) - -// Proc: request() -// Parameters: 1 (candidate - the ghost or communicator wanting to call the device) -// Description: Response to a communicator or observer trying to call the device. Adds them to the list of requesters -/obj/item/device/communicator/proc/request(var/atom/candidate) - if(candidate in voice_requests) - return - var/who = null - if(isobserver(candidate)) - who = candidate.name - else if(istype(candidate, /obj/item/device/communicator)) - var/obj/item/device/communicator/comm = candidate - who = comm.owner - comm.voice_invites |= src - - if(!who) - return - - voice_requests |= candidate - - if(ringer) - playsound(src, 'sound/machines/twobeep.ogg', 50, 1) - for (var/mob/O in hearers(2, loc)) - O.show_message(text("\icon[src][bicon(src)] *beep*")) - - alert_called = 1 - update_icon() - - //Search for holder of the device. - var/mob/living/L = null - if(loc && isliving(loc)) - L = loc - - if(L) - to_chat(L, "\icon[src][bicon(src)] Communications request from [who].") - -// Proc: del_request() -// Parameters: 1 (candidate - the ghost or communicator to be declined) -// Description: Declines a request and cleans up both ends -/obj/item/device/communicator/proc/del_request(var/atom/candidate) - if(!(candidate in voice_requests)) - return - - if(isobserver(candidate)) - to_chat(candidate, "Your communicator call request was declined.") - else if(istype(candidate, /obj/item/device/communicator)) - var/obj/item/device/communicator/comm = candidate - comm.voice_invites -= src - - voice_requests -= candidate - - //Search for holder of our device. - var/mob/living/us = null - if(loc && isliving(loc)) - us = loc - - if(us) - to_chat(us, "\icon[src][bicon(src)] Declined request.") - -// Proc: see_emote() -// Parameters: 2 (M - the mob the emote originated from, text - the emote's contents) -// Description: Relays the emote to all linked communicators. -/obj/item/device/communicator/see_emote(mob/living/M, text) - var/rendered = "\icon[src][bicon(src)] [text]" - for(var/obj/item/device/communicator/comm in communicating) - var/turf/T = get_turf(comm) - if(!T) return - //VOREStation Edit Start for commlinks - var/list/mobs_to_relay - if(istype(comm,/obj/item/device/communicator/commlink)) - var/obj/item/device/communicator/commlink/CL = comm - mobs_to_relay = list(CL.nif.human) - else - var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display - mobs_to_relay = in_range["mobs"] - //VOREStation Edit End - - for(var/mob/mob in mobs_to_relay) //We can't use visible_message(), or else we will get an infinite loop if two communicators hear each other. - var/dst = get_dist(get_turf(mob),get_turf(comm)) - if(dst <= video_range) - mob.show_message(rendered) - else - to_chat(mob, "You can barely see some movement on \the [src]'s display.") - - ..() - -// Proc: hear_talk() -// Parameters: 3 (M - the mob the speech originated from, -// list/message_pieces - what is being said w/ baked languages, -// verb - the word used to describe how text is being said) -// Description: Relays the speech to all linked communicators. -/obj/item/device/communicator/hear_talk(mob/M, list/message_pieces, verb) - for(var/obj/item/device/communicator/comm in communicating) - var/turf/T = get_turf(comm) - if(!T) return - //VOREStation Edit Start for commlinks - var/list/mobs_to_relay - if(istype(comm,/obj/item/device/communicator/commlink)) - var/obj/item/device/communicator/commlink/CL = comm - mobs_to_relay = list(CL.nif.human) - else - var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display - mobs_to_relay = in_range["mobs"] - //VOREStation Edit End - - for(var/mob/mob in mobs_to_relay) - var/list/combined = mob.combine_message(message_pieces, verb, M) - var/message = combined["formatted"] - var/name_used = M.GetVoice() - var/rendered = null - rendered = "\icon[src][bicon(src)] [name_used] [message]" - mob.show_message(rendered, 2) - -// Proc: show_message() -// Parameters: 4 (msg - the message, type - number to determine if message is visible or audible, alt - unknown, alt_type - unknown) -// Description: Relays the message to all linked communicators. -/obj/item/device/communicator/show_message(msg, type, alt, alt_type) - var/rendered = "\icon[src][bicon(src)] [msg]" - for(var/obj/item/device/communicator/comm in communicating) - var/turf/T = get_turf(comm) - if(!T) return - var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) - var/list/mobs_to_relay = in_range["mobs"] - - for(var/mob/mob in mobs_to_relay) - mob.show_message(rendered) - ..() - -// Verb: join_as_voice() -// Parameters: None -// Description: Allows ghosts to call communicators, if they meet all the requirements. -/mob/observer/dead/verb/join_as_voice() - set category = "Ghost" - set name = "Call Communicator" - set desc = "If there is a communicator available, send a request to speak through it. This will reset your respawn timer, if someone picks up." - - if(ticker.current_state < GAME_STATE_PLAYING) - to_chat(src, "The game hasn't started yet!") - return - - if (!src.stat) - return - - if (usr != src) - return //something is terribly wrong - - var/confirm = tgui_alert(src, "Would you like to talk as [src.client.prefs.real_name], over a communicator? This will reset your respawn timer, if someone answers.", "Join as Voice?", list("Yes","No")) - if(confirm == "No") - return - - if(config.antag_hud_restricted && has_enabled_antagHUD == 1) - to_chat(src, "You have used the antagHUD and cannot respawn or use communicators!") - return - - for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling. - if(src.client.prefs.real_name == L.real_name) - to_chat(src, "Your identity is already present in the game world. Please load in a different character first.") - return - - var/obj/machinery/exonet_node/E = get_exonet_node() - if(!E || !E.on || !E.allow_external_communicators) - to_chat(src, "The Exonet node at telecommunications is down at the moment, or is actively blocking you, \ - so your call can't go through.") - return - - var/list/choices = list() - for(var/obj/item/device/communicator/comm in all_communicators) - if(!comm.network_visibility || !comm.exonet || !comm.exonet.address) - continue - choices.Add(comm) - - if(!choices.len) - to_chat(src , "There are no available communicators, sorry.") - return - - var/choice = tgui_input_list(src,"Send a voice request to whom?", "Recipient Choice", choices) - if(choice) - var/obj/item/device/communicator/chosen_communicator = choice - var/mob/observer/dead/O = src - if(O.exonet) - O.exonet.send_message(chosen_communicator.exonet.address, "voice") - - to_chat(src, "A communications request has been sent to [chosen_communicator]. Now you need to wait until someone answers.") - -// Proc: connect_video() -// Parameters: user - the mob doing the viewing of video, comm - the communicator at the far end -// Description: Sets up a videocall and puts the first view into it using watch_video, and updates the icon -/obj/item/device/communicator/proc/connect_video(mob/user,obj/item/device/communicator/comm) - if((!user) || (!comm) || user.stat) return //KO or dead, or already in a video - - if(video_source) //Already in a video - to_chat(user, "You are already connected to a video call!") - return - - if(user.blinded) //User is blinded - to_chat(user, "You cannot see well enough to do that!") - return - - if(!(src in comm.communicating) || !comm.camera) //You called someone with a broken communicator or one that's fake or yourself or something - to_chat(user, "\icon[src][bicon(src)]ERROR: Video failed. Either bandwidth is too low, or the other communicator is malfunctioning.") - return - - to_chat(user, "\icon[src][bicon(src)] Attempting to start video over existing call.") - sleep(30) - to_chat(user, "\icon[src][bicon(src)] Please wait...") - - video_source = comm.camera - comm.visible_message("\icon[src][bicon(src)] New video connection from [comm].") - update_active_camera_screen() - GLOB.moved_event.register(video_source, src, PROC_REF(update_active_camera_screen)) - update_icon() - -// Proc: end_video() -// Parameters: reason - the text reason to print for why it ended -// Description: Ends the video call by clearing video_source -/obj/item/device/communicator/proc/end_video(var/reason) - GLOB.moved_event.unregister(video_source, src, PROC_REF(update_active_camera_screen)) - show_static() - video_source = null - - . = "\icon[src][bicon(src)] [reason ? reason : "Video session ended"]." - - visible_message(.) - update_icon() +// Proc: add_communicating() +// Parameters: 1 (comm - the communicator to add to communicating) +// Description: Used when this communicator gets a new communicator to relay say/me messages to +/obj/item/device/communicator/proc/add_communicating(obj/item/device/communicator/comm) + if(!comm || !istype(comm)) return + + communicating |= comm + listening_objects |= src + update_icon() + +// Proc: del_communicating() +// Parameters: 1 (comm - the communicator to remove from communicating) +// Description: Used when this communicator is being asked to stop relaying say/me messages to another +/obj/item/device/communicator/proc/del_communicating(obj/item/device/communicator/comm) + if(!comm || !istype(comm)) return + + communicating.Remove(comm) + update_icon() + +// Proc: open_connection() +// Parameters: 2 (user - the person who initiated the connecting being opened, candidate - the communicator or observer that will connect to the device) +// Description: Typechecks the candidate, then calls the correct proc for further connecting. +/obj/item/device/communicator/proc/open_connection(mob/user, var/atom/candidate) + if(isobserver(candidate)) + voice_invites.Remove(candidate) + open_connection_to_ghost(user, candidate) + else + if(istype(candidate, /obj/item/device/communicator)) + open_connection_to_communicator(user, candidate) + +// Proc: open_connection_to_communicator() +// Parameters: 2 (user - the person who initiated this and will be receiving feedback information, candidate - someone else's communicator) +// Description: Adds the candidate and src to each other's communicating lists, allowing messages seen by the devices to be relayed. +/obj/item/device/communicator/proc/open_connection_to_communicator(mob/user, var/atom/candidate) + if(!istype(candidate, /obj/item/device/communicator)) + return + var/obj/item/device/communicator/comm = candidate + voice_invites.Remove(candidate) + comm.voice_requests.Remove(src) + + if(user) + comm.visible_message("\icon[src][bicon(src)] Connecting to [src].") + to_chat(user, "\icon[src][bicon(src)] Attempting to call [comm].") + sleep(10) + to_chat(user, "\icon[src][bicon(src)] Dialing internally from [station_name()], [system_name()].") + sleep(20) //If they don't have an exonet something is very wrong and we want a runtime. + to_chat(user, "\icon[src][bicon(src)] Connection re-routed to [comm] at [comm.exonet.address].") + sleep(40) + to_chat(user, "\icon[src][bicon(src)] Connection to [comm] at [comm.exonet.address] established.") + comm.visible_message("\icon[src][bicon(src)] Connection to [src] at [exonet.address] established.") + sleep(20) + + src.add_communicating(comm) + comm.add_communicating(src) + +// Proc: open_connection_to_ghost() +// Parameters: 2 (user - the person who initiated this, candidate - the ghost that will be turned into a voice mob) +// Description: Pulls the candidate ghost from deadchat, makes a new voice mob, transfers their identity, then their client. +/obj/item/device/communicator/proc/open_connection_to_ghost(mob/user, var/mob/candidate) + if(!isobserver(candidate)) + return + //Handle moving the ghost into the new shell. + announce_ghost_joinleave(candidate, 0, "They are occupying a personal communications device now.") + voice_requests.Remove(candidate) + voice_invites.Remove(candidate) + var/mob/living/voice/new_voice = new /mob/living/voice(src) //Make the voice mob the ghost is going to be. + new_voice.transfer_identity(candidate) //Now make the voice mob load from the ghost's active character in preferences. + //Do some simple logging since this is a tad risky as a concept. + var/msg = "[candidate && candidate.client ? "[candidate.client.key]" : "*no key*"] ([candidate]) has entered [src], triggered by \ + [user && user.client ? "[user.client.key]" : "*no key*"] ([user ? "[user]" : "*null*"]) at [x],[y],[z]. They have joined as [new_voice.name]." + message_admins(msg) + log_game(msg) + new_voice.mind = candidate.mind //Transfer the mind, if any. + new_voice.ckey = candidate.ckey //Finally, bring the client over. + voice_mobs.Add(new_voice) + listening_objects |= src + + var/obj/screen/blackness = new() //Makes a black screen, so the candidate can't see what's going on before actually 'connecting' to the communicator. + blackness.screen_loc = ui_entire_screen + blackness.icon = 'icons/effects/effects.dmi' + blackness.icon_state = "1" + blackness.mouse_opacity = 2 //Can't see anything! + new_voice.client.screen.Add(blackness) + + update_icon() + + //Now for some connection fluff. + if(user) + to_chat(user, "\icon[src][bicon(src)] Connecting to [candidate].") + to_chat(new_voice, "\icon[src][bicon(src)] Attempting to call [src].") + sleep(10) + to_chat(new_voice, "\icon[src][bicon(src)] Dialing to [station_name()], Kara Subsystem, [system_name()].") + sleep(20) + to_chat(new_voice, "\icon[src][bicon(src)] Connecting to [station_name()] telecommunications array.") + sleep(40) + to_chat(new_voice, "\icon[src][bicon(src)] Connection to [station_name()] telecommunications array established. Redirecting signal to [src].") + sleep(20) + + //We're connected, no need to hide everything. + new_voice.client.screen.Remove(blackness) + qdel(blackness) + + to_chat(new_voice, "\icon[src][bicon(src)] Connection to [src] established.") + to_chat(new_voice, "To talk to the person on the other end of the call, just talk normally.") + to_chat(new_voice, "If you want to end the call, use the 'Hang Up' verb. The other person can also hang up at any time.") + to_chat(new_voice, "Remember, your character does not know anything you've learned from observing!") + if(new_voice.mind) + new_voice.mind.assigned_role = "Disembodied Voice" + if(user) + to_chat(user, "\icon[src][bicon(src)] Your communicator is now connected to [candidate]'s communicator.") + +// Proc: close_connection() +// Parameters: 3 (user - the user who initiated the disconnect, target - the mob or device being disconnected, reason - string shown when disconnected) +// Description: Deletes specific voice_mobs or disconnects communicators, and shows a message to everyone when doing so. If target is null, all communicators +// and voice mobs are removed. +/obj/item/device/communicator/proc/close_connection(mob/user, var/atom/target, var/reason) + if(voice_mobs.len == 0 && communicating.len == 0) + return + + for(var/mob/living/voice/voice in voice_mobs) //Handle ghost-callers + if(target && voice != target) //If no target is inputted, it deletes all of them. + continue + to_chat(voice, "\icon[src][bicon(src)] [reason].") + visible_message("\icon[src][bicon(src)] [reason].") + voice_mobs.Remove(voice) + qdel(voice) + update_icon() + + for(var/obj/item/device/communicator/comm in communicating) //Now we handle real communicators. + if(target && comm != target) + continue + src.del_communicating(comm) + comm.del_communicating(src) + comm.visible_message("\icon[src][bicon(src)] [reason].") + visible_message("\icon[src][bicon(src)] [reason].") + if(comm.camera && video_source == comm.camera) //We hung up on the person on video + end_video() + if(camera && comm.video_source == camera) //We hung up on them while they were watching us + comm.end_video() + + if(voice_mobs.len == 0 && communicating.len == 0) + listening_objects.Remove(src) + +// Proc: request() +// Parameters: 1 (candidate - the ghost or communicator wanting to call the device) +// Description: Response to a communicator or observer trying to call the device. Adds them to the list of requesters +/obj/item/device/communicator/proc/request(var/atom/candidate) + if(candidate in voice_requests) + return + var/who = null + if(isobserver(candidate)) + who = candidate.name + else if(istype(candidate, /obj/item/device/communicator)) + var/obj/item/device/communicator/comm = candidate + who = comm.owner + comm.voice_invites |= src + + if(!who) + return + + voice_requests |= candidate + + if(ringer) + playsound(src, 'sound/machines/twobeep.ogg', 50, 1) + for (var/mob/O in hearers(2, loc)) + O.show_message(text("\icon[src][bicon(src)] *beep*")) + + alert_called = 1 + update_icon() + + //Search for holder of the device. + var/mob/living/L = null + if(loc && isliving(loc)) + L = loc + + if(L) + to_chat(L, "\icon[src][bicon(src)] Communications request from [who].") + +// Proc: del_request() +// Parameters: 1 (candidate - the ghost or communicator to be declined) +// Description: Declines a request and cleans up both ends +/obj/item/device/communicator/proc/del_request(var/atom/candidate) + if(!(candidate in voice_requests)) + return + + if(isobserver(candidate)) + to_chat(candidate, "Your communicator call request was declined.") + else if(istype(candidate, /obj/item/device/communicator)) + var/obj/item/device/communicator/comm = candidate + comm.voice_invites -= src + + voice_requests -= candidate + + //Search for holder of our device. + var/mob/living/us = null + if(loc && isliving(loc)) + us = loc + + if(us) + to_chat(us, "\icon[src][bicon(src)] Declined request.") + +// Proc: see_emote() +// Parameters: 2 (M - the mob the emote originated from, text - the emote's contents) +// Description: Relays the emote to all linked communicators. +/obj/item/device/communicator/see_emote(mob/living/M, text) + var/rendered = "\icon[src][bicon(src)] [text]" + for(var/obj/item/device/communicator/comm in communicating) + var/turf/T = get_turf(comm) + if(!T) return + //VOREStation Edit Start for commlinks + var/list/mobs_to_relay + if(istype(comm,/obj/item/device/communicator/commlink)) + var/obj/item/device/communicator/commlink/CL = comm + mobs_to_relay = list(CL.nif.human) + else + var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display + mobs_to_relay = in_range["mobs"] + //VOREStation Edit End + + for(var/mob/mob in mobs_to_relay) //We can't use visible_message(), or else we will get an infinite loop if two communicators hear each other. + var/dst = get_dist(get_turf(mob),get_turf(comm)) + if(dst <= video_range) + mob.show_message(rendered) + else + to_chat(mob, "You can barely see some movement on \the [src]'s display.") + + ..() + +// Proc: hear_talk() +// Parameters: 3 (M - the mob the speech originated from, +// list/message_pieces - what is being said w/ baked languages, +// verb - the word used to describe how text is being said) +// Description: Relays the speech to all linked communicators. +/obj/item/device/communicator/hear_talk(mob/M, list/message_pieces, verb) + for(var/obj/item/device/communicator/comm in communicating) + var/turf/T = get_turf(comm) + if(!T) return + //VOREStation Edit Start for commlinks + var/list/mobs_to_relay + if(istype(comm,/obj/item/device/communicator/commlink)) + var/obj/item/device/communicator/commlink/CL = comm + mobs_to_relay = list(CL.nif.human) + else + var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display + mobs_to_relay = in_range["mobs"] + //VOREStation Edit End + + for(var/mob/mob in mobs_to_relay) + var/list/combined = mob.combine_message(message_pieces, verb, M) + var/message = combined["formatted"] + var/name_used = M.GetVoice() + var/rendered = null + rendered = "\icon[src][bicon(src)] [name_used] [message]" + mob.show_message(rendered, 2) + +// Proc: show_message() +// Parameters: 4 (msg - the message, type - number to determine if message is visible or audible, alt - unknown, alt_type - unknown) +// Description: Relays the message to all linked communicators. +/obj/item/device/communicator/show_message(msg, type, alt, alt_type) + var/rendered = "\icon[src][bicon(src)] [msg]" + for(var/obj/item/device/communicator/comm in communicating) + var/turf/T = get_turf(comm) + if(!T) return + var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) + var/list/mobs_to_relay = in_range["mobs"] + + for(var/mob/mob in mobs_to_relay) + mob.show_message(rendered) + ..() + +// Verb: join_as_voice() +// Parameters: None +// Description: Allows ghosts to call communicators, if they meet all the requirements. +/mob/observer/dead/verb/join_as_voice() + set category = "Ghost" + set name = "Call Communicator" + set desc = "If there is a communicator available, send a request to speak through it. This will reset your respawn timer, if someone picks up." + + if(ticker.current_state < GAME_STATE_PLAYING) + to_chat(src, "The game hasn't started yet!") + return + + if (!src.stat) + return + + if (usr != src) + return //something is terribly wrong + + var/confirm = tgui_alert(src, "Would you like to talk as [src.client.prefs.real_name], over a communicator? This will reset your respawn timer, if someone answers.", "Join as Voice?", list("Yes","No")) + if(confirm == "No") + return + + if(config.antag_hud_restricted && has_enabled_antagHUD == 1) + to_chat(src, "You have used the antagHUD and cannot respawn or use communicators!") + return + + for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling. + if(src.client.prefs.real_name == L.real_name) + to_chat(src, "Your identity is already present in the game world. Please load in a different character first.") + return + + var/obj/machinery/exonet_node/E = get_exonet_node() + if(!E || !E.on || !E.allow_external_communicators) + to_chat(src, "The Exonet node at telecommunications is down at the moment, or is actively blocking you, \ + so your call can't go through.") + return + + var/list/choices = list() + for(var/obj/item/device/communicator/comm in all_communicators) + if(!comm.network_visibility || !comm.exonet || !comm.exonet.address) + continue + choices.Add(comm) + + if(!choices.len) + to_chat(src , "There are no available communicators, sorry.") + return + + var/choice = tgui_input_list(src,"Send a voice request to whom?", "Recipient Choice", choices) + if(choice) + var/obj/item/device/communicator/chosen_communicator = choice + var/mob/observer/dead/O = src + if(O.exonet) + O.exonet.send_message(chosen_communicator.exonet.address, "voice") + + to_chat(src, "A communications request has been sent to [chosen_communicator]. Now you need to wait until someone answers.") + +// Proc: connect_video() +// Parameters: user - the mob doing the viewing of video, comm - the communicator at the far end +// Description: Sets up a videocall and puts the first view into it using watch_video, and updates the icon +/obj/item/device/communicator/proc/connect_video(mob/user,obj/item/device/communicator/comm) + if((!user) || (!comm) || user.stat) return //KO or dead, or already in a video + + if(video_source) //Already in a video + to_chat(user, "You are already connected to a video call!") + return + + if(user.blinded) //User is blinded + to_chat(user, "You cannot see well enough to do that!") + return + + if(!(src in comm.communicating) || !comm.camera) //You called someone with a broken communicator or one that's fake or yourself or something + to_chat(user, "\icon[src][bicon(src)]ERROR: Video failed. Either bandwidth is too low, or the other communicator is malfunctioning.") + return + + to_chat(user, "\icon[src][bicon(src)] Attempting to start video over existing call.") + sleep(30) + to_chat(user, "\icon[src][bicon(src)] Please wait...") + + video_source = comm.camera + comm.visible_message("\icon[src][bicon(src)] New video connection from [comm].") + update_active_camera_screen() + GLOB.moved_event.register(video_source, src, PROC_REF(update_active_camera_screen)) + update_icon() + +// Proc: end_video() +// Parameters: reason - the text reason to print for why it ended +// Description: Ends the video call by clearing video_source +/obj/item/device/communicator/proc/end_video(var/reason) + GLOB.moved_event.unregister(video_source, src, PROC_REF(update_active_camera_screen)) + show_static() + video_source = null + + . = "\icon[src][bicon(src)] [reason ? reason : "Video session ended"]." + + visible_message(.) + update_icon() diff --git a/code/game/objects/items/devices/debugger.dm b/code/game/objects/items/devices/debugger.dm index 8bfcc034541..f97ab7db0c0 100644 --- a/code/game/objects/items/devices/debugger.dm +++ b/code/game/objects/items/devices/debugger.dm @@ -1,45 +1,45 @@ -/** - * Multitool -- A multitool is used for hacking electronic devices. - * TO-DO -- Using it as a power measurement tool for cables etc. Nannek. - * - */ - -/obj/item/device/debugger - name = "debugger" - desc = "Used to debug electronic equipment." - icon = 'icons/obj/hacktool.dmi' - icon_state = "hacktool-g" - force = 5.0 - w_class = ITEMSIZE_SMALL - throwforce = 5.0 - throw_range = 15 - throw_speed = 3 - desc = "You can use this on airlocks or APCs to try to hack them without cutting wires." - - matter = list(MAT_STEEL = 50,MAT_GLASS = 20) - - origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) - var/obj/machinery/telecomms/buffer // simple machine buffer for device linkage - -/obj/item/device/debugger/is_used_on(obj/O, mob/user) - if(istype(O, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/A = O - if(A.emagged || A.hacker) - to_chat(user, "There is a software error with the device.") - else - to_chat(user, "The device's software appears to be fine.") - return 1 - if(istype(O, /obj/machinery/door)) - var/obj/machinery/door/D = O - if(D.operating == -1) - to_chat(user, "There is a software error with the device.") - else - to_chat(user, "The device's software appears to be fine.") - return 1 - else if(istype(O, /obj/machinery)) - var/obj/machinery/A = O - if(A.emagged) - to_chat(user, "There is a software error with the device.") - else - to_chat(user, "The device's software appears to be fine.") - return 1 +/** + * Multitool -- A multitool is used for hacking electronic devices. + * TO-DO -- Using it as a power measurement tool for cables etc. Nannek. + * + */ + +/obj/item/device/debugger + name = "debugger" + desc = "Used to debug electronic equipment." + icon = 'icons/obj/hacktool.dmi' + icon_state = "hacktool-g" + force = 5.0 + w_class = ITEMSIZE_SMALL + throwforce = 5.0 + throw_range = 15 + throw_speed = 3 + desc = "You can use this on airlocks or APCs to try to hack them without cutting wires." + + matter = list(MAT_STEEL = 50,MAT_GLASS = 20) + + origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) + var/obj/machinery/telecomms/buffer // simple machine buffer for device linkage + +/obj/item/device/debugger/is_used_on(obj/O, mob/user) + if(istype(O, /obj/machinery/power/apc)) + var/obj/machinery/power/apc/A = O + if(A.emagged || A.hacker) + to_chat(user, "There is a software error with the device.") + else + to_chat(user, "The device's software appears to be fine.") + return 1 + if(istype(O, /obj/machinery/door)) + var/obj/machinery/door/D = O + if(D.operating == -1) + to_chat(user, "There is a software error with the device.") + else + to_chat(user, "The device's software appears to be fine.") + return 1 + else if(istype(O, /obj/machinery)) + var/obj/machinery/A = O + if(A.emagged) + to_chat(user, "There is a software error with the device.") + else + to_chat(user, "The device's software appears to be fine.") + return 1 diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index 4e692b33471..5fb6a4ac84f 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -1,321 +1,321 @@ -/obj/item/device/flash - name = "flash" - desc = "Used for blinding and disorienting." - icon_state = "flash" - item_state = "flashtool" - throwforce = 5 - w_class = ITEMSIZE_SMALL - throw_speed = 4 - throw_range = 10 - origin_tech = list(TECH_MAGNET = 2, TECH_COMBAT = 1) - - var/times_used = 0 //Number of times it's been used. - var/broken = FALSE //Is the flash burnt out? - var/last_used = 0 //last world.time it was used. - var/max_flashes = 10 // How many times the flash can be used before needing to self recharge. - var/halloss_per_flash = 30 - var/break_mod = 3 // The percent to break increased by every use on the flash. - - var/can_break = TRUE // Can the flash break? - var/can_repair = FALSE // Can you repair the flash? - var/repairing = FALSE // Are we repairing right now? - - var/safe_flashes = 2 // How many flashes are kept in 1% breakchance? - - var/charge_only = FALSE // Does the flash run purely on charge? - - var/base_icon = "flash" - - var/obj/item/weapon/cell/power_supply //What type of power cell this uses - var/charge_cost = 30 //How much energy is needed to flash. - var/use_external_power = FALSE // Do we use charge from an external source? - - var/cell_type = /obj/item/weapon/cell/device - -/obj/item/device/flash/Initialize() - . = ..() - power_supply = new cell_type(src) - -/obj/item/device/flash/attackby(var/obj/item/W, var/mob/user) - if(W.has_tool_quality(TOOL_SCREWDRIVER) && broken) - if(repairing) - to_chat(user, "\The [src] is already being repaired!") - return - user.visible_message("\The [user] starts trying to repair \the [src]'s bulb.") - repairing = TRUE - if(do_after(user, (40 SECONDS + rand(0, 20 SECONDS)) * W.toolspeed) && can_repair) - if(prob(30)) - user.visible_message("\The [user] successfully repairs \the [src]!") - broken = FALSE - update_icon() - playsound(src, W.usesound, 50, 1) - else - user.visible_message("\The [user] fails to repair \the [src].") - repairing = FALSE - else - ..() - -/obj/item/device/flash/update_icon() - var/obj/item/weapon/cell/battery = power_supply - - if(use_external_power) - battery = get_external_power_supply() - - if(broken || !battery || battery.charge < charge_cost) - icon_state = "[base_icon]burnt" - else - icon_state = "[base_icon]" - return - -/obj/item/device/flash/get_cell() - return power_supply - -/obj/item/device/flash/proc/get_external_power_supply() - if(isrobot(src.loc)) - var/mob/living/silicon/robot/R = src.loc - return R.cell - if(istype(src.loc, /obj/item/rig_module)) - var/obj/item/rig_module/module = src.loc - if(module.holder && module.holder.wearer) - var/mob/living/carbon/human/H = module.holder.wearer - if(istype(H) && H.get_rig()) - var/obj/item/weapon/rig/suit = H.get_rig() - if(istype(suit)) - return suit.cell - return null - -/obj/item/device/flash/proc/clown_check(var/mob/user) - if(user && (CLUMSY in user.mutations) && prob(50)) - to_chat(user, "\The [src] slips out of your hand.") - user.drop_item() - return 0 - return 1 - -/obj/item/device/flash/proc/flash_recharge() - //Every ten seconds the flash doesn't get used, the times_used variable goes down by one, making the flash less likely to burn out, - // as well as being able to flash more before reaching max_flashes cap. - for(var/i=0, i < max_flashes, i++) - if(last_used + 10 SECONDS > world.time) - break - - else if(use_external_power) - var/obj/item/weapon/cell/external = get_external_power_supply() - if(!external || !external.use(charge_cost)) //Take power from the borg or rig! - break - - else if(!power_supply || !power_supply.checked_use(charge_cost)) - break - - last_used += 10 SECONDS - times_used-- - - last_used = world.time - times_used = max(0,round(times_used)) //sanity - update_icon() - -// Returns true if the device can flash. -/obj/item/device/flash/proc/check_capacitor(var/mob/user) - //spamming the flash before it's fully charged (60 seconds) increases the chance of it breaking - //It will never break on the first use. - var/obj/item/weapon/cell/battery = power_supply - - if(use_external_power) - battery = get_external_power_supply() - - if(times_used <= max_flashes && battery && battery.charge >= charge_cost) - last_used = world.time - if(prob( max(0, times_used - safe_flashes) * 2 + (times_used >= safe_flashes)) && can_break) //if you use it 10 times in a minute it has a 30% chance to break. - broken = TRUE - if(user) - to_chat(user, "The bulb has burnt out!") - update_icon() - return FALSE - else - times_used++ - update_icon() - return TRUE - else if(!charge_only) //can only use it 10 times a minute, unless it runs purely on charge. - if(user) - update_icon() - to_chat(user, "click") - playsound(src, 'sound/weapons/empty.ogg', 80, 1) - return FALSE - else if(battery && battery.checked_use(charge_cost + (round(charge_cost / 4) * max(0, times_used - max_flashes)))) // Using over your maximum flashes starts taking more charge per added flash. - times_used++ - update_icon() - return TRUE - -//attack_as_weapon -/obj/item/device/flash/attack(mob/living/M, mob/living/user, var/target_zone) - if(!user || !M) return //sanity - - add_attack_logs(user,M,"Flashed (attempt) with [src]") - - user.setClickCooldown(user.get_attack_speed(src)) - user.do_attack_animation(M) - - if(!clown_check(user)) return - if(broken) - to_chat(user, "\The [src] is broken.") - return - - flash_recharge() - - if(!check_capacitor(user)) - return - - playsound(src, 'sound/weapons/flash.ogg', 100, 1) - var/flashfail = 0 - - //VOREStation Add - NIF - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.nif && H.nif.flag_check(NIF_V_FLASHPROT,NIF_FLAGS_VISION)) - flashfail = 1 - H.nif.notify("High intensity light detected, and blocked!",TRUE) - //VOREStation Add End - - if(iscarbon(M) && !flashfail) //VOREStation Add - NIF - var/mob/living/carbon/C = M - if(C.stat != DEAD) - var/safety = C.eyecheck() - if(safety <= 0) - var/flash_strength = 10 //Vorestation edit, making flashes behave the same as flash rounds - if(ishuman(C)) - var/mob/living/carbon/human/H = C - flash_strength *= H.species.flash_mod - - if(flash_strength > 0) - H.Confuse(flash_strength + 5) - H.Blind(flash_strength) - H.eye_blurry = max(H.eye_blurry, flash_strength + 5) - H.flash_eyes() - H.adjustHalLoss(halloss_per_flash * (flash_strength / 5)) // Should take four flashes to stun. - H.apply_damage(flash_strength * H.species.flash_burn/5, BURN, BP_HEAD, 0, 0, "Photon burns") - - else - flashfail = 1 - - else if(issilicon(M)) - flashfail = 0 - var/mob/living/silicon/S = M - if(isrobot(S)) - var/mob/living/silicon/robot/R = S - if(R.has_active_type(/obj/item/borg/combat/shield)) - var/obj/item/borg/combat/shield/shield = locate() in R - if(shield) - if(shield.active) - shield.adjust_flash_count(R, 1) - flashfail = 1 - else - flashfail = 1 - - if(isrobot(user)) - spawn(0) - var/atom/movable/overlay/animation = new(user.loc) - animation.layer = user.layer + 1 - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = user - flick("blspell", animation) - sleep(5) - qdel(animation) - - if(!flashfail) - flick("flash2", src) - if(!issilicon(M)) - - user.visible_message("[user] blinds [M] with the flash!") - else - - user.visible_message("[user] overloads [M]'s sensors with the flash!") - M.Weaken(rand(5,10)) - else - - user.visible_message("[user] fails to blind [M] with the flash!") - - return - - - - -/obj/item/device/flash/attack_self(mob/living/carbon/user as mob, flag = 0, emp = 0) - if(!user || !clown_check(user)) return - - user.setClickCooldown(user.get_attack_speed(src)) - - if(broken) - user.show_message("The [src.name] is broken", 2) - return - - flash_recharge() - - if(!check_capacitor(user)) - return - - playsound(src, 'sound/weapons/flash.ogg', 100, 1) - flick("flash2", src) - if(user && isrobot(user)) - spawn(0) - var/atom/movable/overlay/animation = new(user.loc) - animation.layer = user.layer + 1 - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = user - flick("blspell", animation) - sleep(5) - qdel(animation) - - for(var/mob/living/carbon/C in oviewers(3, null)) - var/safety = C.eyecheck() - if(!safety) - if(!C.blinded) - C.flash_eyes() - - return - -/obj/item/device/flash/emp_act(severity) - if(broken) return - flash_recharge() - if(!check_capacitor()) - return - - if(istype(loc, /mob/living/carbon)) - var/mob/living/carbon/C = loc - var/safety = C.eyecheck() - if(safety <= 0) - C.adjustHalLoss(halloss_per_flash) - //C.Weaken(10) - C.flash_eyes() - for(var/mob/M in viewers(C, null)) - M.show_message("[C] is blinded by the flash!") - ..() - -/obj/item/device/flash/synthetic - name = "synthetic flash" - desc = "When a problem arises, SCIENCE is the solution." - icon_state = "sflash" - origin_tech = list(TECH_MAGNET = 2, TECH_COMBAT = 1) - base_icon = "sflash" - can_repair = FALSE - -//attack_as_weapon -/obj/item/device/flash/synthetic/attack(mob/living/M, mob/living/user, var/target_zone) - ..() - if(!broken) - broken = 1 - to_chat(user, "The bulb has burnt out!") - update_icon() - -/obj/item/device/flash/synthetic/attack_self(mob/living/carbon/user as mob, flag = 0, emp = 0) - ..() - if(!broken) - broken = 1 - to_chat(user, "The bulb has burnt out!") - update_icon() - -/obj/item/device/flash/robot - name = "mounted flash" - can_break = FALSE - use_external_power = TRUE - charge_only = TRUE +/obj/item/device/flash + name = "flash" + desc = "Used for blinding and disorienting." + icon_state = "flash" + item_state = "flashtool" + throwforce = 5 + w_class = ITEMSIZE_SMALL + throw_speed = 4 + throw_range = 10 + origin_tech = list(TECH_MAGNET = 2, TECH_COMBAT = 1) + + var/times_used = 0 //Number of times it's been used. + var/broken = FALSE //Is the flash burnt out? + var/last_used = 0 //last world.time it was used. + var/max_flashes = 10 // How many times the flash can be used before needing to self recharge. + var/halloss_per_flash = 30 + var/break_mod = 3 // The percent to break increased by every use on the flash. + + var/can_break = TRUE // Can the flash break? + var/can_repair = FALSE // Can you repair the flash? + var/repairing = FALSE // Are we repairing right now? + + var/safe_flashes = 2 // How many flashes are kept in 1% breakchance? + + var/charge_only = FALSE // Does the flash run purely on charge? + + var/base_icon = "flash" + + var/obj/item/weapon/cell/power_supply //What type of power cell this uses + var/charge_cost = 30 //How much energy is needed to flash. + var/use_external_power = FALSE // Do we use charge from an external source? + + var/cell_type = /obj/item/weapon/cell/device + +/obj/item/device/flash/Initialize() + . = ..() + power_supply = new cell_type(src) + +/obj/item/device/flash/attackby(var/obj/item/W, var/mob/user) + if(W.has_tool_quality(TOOL_SCREWDRIVER) && broken) + if(repairing) + to_chat(user, "\The [src] is already being repaired!") + return + user.visible_message("\The [user] starts trying to repair \the [src]'s bulb.") + repairing = TRUE + if(do_after(user, (40 SECONDS + rand(0, 20 SECONDS)) * W.toolspeed) && can_repair) + if(prob(30)) + user.visible_message("\The [user] successfully repairs \the [src]!") + broken = FALSE + update_icon() + playsound(src, W.usesound, 50, 1) + else + user.visible_message("\The [user] fails to repair \the [src].") + repairing = FALSE + else + ..() + +/obj/item/device/flash/update_icon() + var/obj/item/weapon/cell/battery = power_supply + + if(use_external_power) + battery = get_external_power_supply() + + if(broken || !battery || battery.charge < charge_cost) + icon_state = "[base_icon]burnt" + else + icon_state = "[base_icon]" + return + +/obj/item/device/flash/get_cell() + return power_supply + +/obj/item/device/flash/proc/get_external_power_supply() + if(isrobot(src.loc)) + var/mob/living/silicon/robot/R = src.loc + return R.cell + if(istype(src.loc, /obj/item/rig_module)) + var/obj/item/rig_module/module = src.loc + if(module.holder && module.holder.wearer) + var/mob/living/carbon/human/H = module.holder.wearer + if(istype(H) && H.get_rig()) + var/obj/item/weapon/rig/suit = H.get_rig() + if(istype(suit)) + return suit.cell + return null + +/obj/item/device/flash/proc/clown_check(var/mob/user) + if(user && (CLUMSY in user.mutations) && prob(50)) + to_chat(user, "\The [src] slips out of your hand.") + user.drop_item() + return 0 + return 1 + +/obj/item/device/flash/proc/flash_recharge() + //Every ten seconds the flash doesn't get used, the times_used variable goes down by one, making the flash less likely to burn out, + // as well as being able to flash more before reaching max_flashes cap. + for(var/i=0, i < max_flashes, i++) + if(last_used + 10 SECONDS > world.time) + break + + else if(use_external_power) + var/obj/item/weapon/cell/external = get_external_power_supply() + if(!external || !external.use(charge_cost)) //Take power from the borg or rig! + break + + else if(!power_supply || !power_supply.checked_use(charge_cost)) + break + + last_used += 10 SECONDS + times_used-- + + last_used = world.time + times_used = max(0,round(times_used)) //sanity + update_icon() + +// Returns true if the device can flash. +/obj/item/device/flash/proc/check_capacitor(var/mob/user) + //spamming the flash before it's fully charged (60 seconds) increases the chance of it breaking + //It will never break on the first use. + var/obj/item/weapon/cell/battery = power_supply + + if(use_external_power) + battery = get_external_power_supply() + + if(times_used <= max_flashes && battery && battery.charge >= charge_cost) + last_used = world.time + if(prob( max(0, times_used - safe_flashes) * 2 + (times_used >= safe_flashes)) && can_break) //if you use it 10 times in a minute it has a 30% chance to break. + broken = TRUE + if(user) + to_chat(user, "The bulb has burnt out!") + update_icon() + return FALSE + else + times_used++ + update_icon() + return TRUE + else if(!charge_only) //can only use it 10 times a minute, unless it runs purely on charge. + if(user) + update_icon() + to_chat(user, "click") + playsound(src, 'sound/weapons/empty.ogg', 80, 1) + return FALSE + else if(battery && battery.checked_use(charge_cost + (round(charge_cost / 4) * max(0, times_used - max_flashes)))) // Using over your maximum flashes starts taking more charge per added flash. + times_used++ + update_icon() + return TRUE + +//attack_as_weapon +/obj/item/device/flash/attack(mob/living/M, mob/living/user, var/target_zone) + if(!user || !M) return //sanity + + add_attack_logs(user,M,"Flashed (attempt) with [src]") + + user.setClickCooldown(user.get_attack_speed(src)) + user.do_attack_animation(M) + + if(!clown_check(user)) return + if(broken) + to_chat(user, "\The [src] is broken.") + return + + flash_recharge() + + if(!check_capacitor(user)) + return + + playsound(src, 'sound/weapons/flash.ogg', 100, 1) + var/flashfail = 0 + + //VOREStation Add - NIF + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.nif && H.nif.flag_check(NIF_V_FLASHPROT,NIF_FLAGS_VISION)) + flashfail = 1 + H.nif.notify("High intensity light detected, and blocked!",TRUE) + //VOREStation Add End + + if(iscarbon(M) && !flashfail) //VOREStation Add - NIF + var/mob/living/carbon/C = M + if(C.stat != DEAD) + var/safety = C.eyecheck() + if(safety <= 0) + var/flash_strength = 10 //Vorestation edit, making flashes behave the same as flash rounds + if(ishuman(C)) + var/mob/living/carbon/human/H = C + flash_strength *= H.species.flash_mod + + if(flash_strength > 0) + H.Confuse(flash_strength + 5) + H.Blind(flash_strength) + H.eye_blurry = max(H.eye_blurry, flash_strength + 5) + H.flash_eyes() + H.adjustHalLoss(halloss_per_flash * (flash_strength / 5)) // Should take four flashes to stun. + H.apply_damage(flash_strength * H.species.flash_burn/5, BURN, BP_HEAD, 0, 0, "Photon burns") + + else + flashfail = 1 + + else if(issilicon(M)) + flashfail = 0 + var/mob/living/silicon/S = M + if(isrobot(S)) + var/mob/living/silicon/robot/R = S + if(R.has_active_type(/obj/item/borg/combat/shield)) + var/obj/item/borg/combat/shield/shield = locate() in R + if(shield) + if(shield.active) + shield.adjust_flash_count(R, 1) + flashfail = 1 + else + flashfail = 1 + + if(isrobot(user)) + spawn(0) + var/atom/movable/overlay/animation = new(user.loc) + animation.layer = user.layer + 1 + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = user + flick("blspell", animation) + sleep(5) + qdel(animation) + + if(!flashfail) + flick("flash2", src) + if(!issilicon(M)) + + user.visible_message("[user] blinds [M] with the flash!") + else + + user.visible_message("[user] overloads [M]'s sensors with the flash!") + M.Weaken(rand(5,10)) + else + + user.visible_message("[user] fails to blind [M] with the flash!") + + return + + + + +/obj/item/device/flash/attack_self(mob/living/carbon/user as mob, flag = 0, emp = 0) + if(!user || !clown_check(user)) return + + user.setClickCooldown(user.get_attack_speed(src)) + + if(broken) + user.show_message("The [src.name] is broken", 2) + return + + flash_recharge() + + if(!check_capacitor(user)) + return + + playsound(src, 'sound/weapons/flash.ogg', 100, 1) + flick("flash2", src) + if(user && isrobot(user)) + spawn(0) + var/atom/movable/overlay/animation = new(user.loc) + animation.layer = user.layer + 1 + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = user + flick("blspell", animation) + sleep(5) + qdel(animation) + + for(var/mob/living/carbon/C in oviewers(3, null)) + var/safety = C.eyecheck() + if(!safety) + if(!C.blinded) + C.flash_eyes() + + return + +/obj/item/device/flash/emp_act(severity) + if(broken) return + flash_recharge() + if(!check_capacitor()) + return + + if(istype(loc, /mob/living/carbon)) + var/mob/living/carbon/C = loc + var/safety = C.eyecheck() + if(safety <= 0) + C.adjustHalLoss(halloss_per_flash) + //C.Weaken(10) + C.flash_eyes() + for(var/mob/M in viewers(C, null)) + M.show_message("[C] is blinded by the flash!") + ..() + +/obj/item/device/flash/synthetic + name = "synthetic flash" + desc = "When a problem arises, SCIENCE is the solution." + icon_state = "sflash" + origin_tech = list(TECH_MAGNET = 2, TECH_COMBAT = 1) + base_icon = "sflash" + can_repair = FALSE + +//attack_as_weapon +/obj/item/device/flash/synthetic/attack(mob/living/M, mob/living/user, var/target_zone) + ..() + if(!broken) + broken = 1 + to_chat(user, "The bulb has burnt out!") + update_icon() + +/obj/item/device/flash/synthetic/attack_self(mob/living/carbon/user as mob, flag = 0, emp = 0) + ..() + if(!broken) + broken = 1 + to_chat(user, "The bulb has burnt out!") + update_icon() + +/obj/item/device/flash/robot + name = "mounted flash" + can_break = FALSE + use_external_power = TRUE + charge_only = TRUE diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index eff6d348480..b7b4440b1ea 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -1,493 +1,493 @@ -/* - * Contains: - * Flashlights - * Lamps - * Flares - * Chemlights - * Slime Extract - */ - -/* - * Flashlights - */ - -/obj/item/device/flashlight - name = "flashlight" - desc = "A hand-held emergency light." - icon = 'icons/obj/lighting.dmi' - icon_state = "flashlight" - w_class = ITEMSIZE_SMALL - slot_flags = SLOT_BELT - matter = list(MAT_STEEL = 50,MAT_GLASS = 20) - action_button_name = "Toggle Flashlight" - - light_system = MOVABLE_LIGHT_DIRECTIONAL - light_range = 4 //luminosity when on - light_power = 0.8 //lighting power when on - light_color = "#FFFFFF" //LIGHT_COLOR_INCANDESCENT_FLASHLIGHT //lighting colour when on - light_cone_y_offset = -7 - - var/on = 0 - - var/obj/item/weapon/cell/cell - var/cell_type = /obj/item/weapon/cell/device - var/power_usage = 1 - var/power_use = 1 - -/obj/item/device/flashlight/Initialize() - . = ..() - - if(power_use && cell_type) - cell = new cell_type(src) - - update_brightness() - -/obj/item/device/flashlight/Destroy() - STOP_PROCESSING(SSobj, src) - qdel_null(cell) - return ..() - -/obj/item/device/flashlight/get_cell() - return cell - -/obj/item/device/flashlight/process() - if(!on || !cell) - return PROCESS_KILL - - if(power_usage) - if(cell.use(power_usage) != power_usage) // we weren't able to use our full power_usage amount! - visible_message("\The [src] flickers before going dull.") - playsound(src, 'sound/effects/sparks3.ogg', 10, 1, -3) //Small cue that your light went dull in your pocket. //VOREStation Edit - on = 0 - update_brightness() - return PROCESS_KILL - -/obj/item/device/flashlight/proc/update_brightness() - if(on) - icon_state = "[initial(icon_state)]-on" - else - icon_state = initial(icon_state) - set_light_on(on) - if(light_system == STATIC_LIGHT) - update_light() - -/obj/item/device/flashlight/examine(mob/user) - . = ..() - if(power_use && cell) - . += "\The [src] has a \the [cell] attached." - - if(cell.charge <= cell.maxcharge*0.25) - . += "It appears to have a low amount of power remaining." - else if(cell.charge > cell.maxcharge*0.25 && cell.charge <= cell.maxcharge*0.5) - . += "It appears to have an average amount of power remaining." - else if(cell.charge > cell.maxcharge*0.5 && cell.charge <= cell.maxcharge*0.75) - . += "It appears to have an above average amount of power remaining." - else if(cell.charge > cell.maxcharge*0.75 && cell.charge <= cell.maxcharge) - . += "It appears to have a high amount of power remaining." - -/obj/item/device/flashlight/attack_self(mob/user) - if(power_use) - if(!isturf(user.loc)) - to_chat(user, "You cannot turn the light on while in this [user.loc].") //To prevent some lighting anomalities. - return 0 - if(!cell || cell.charge == 0) - to_chat(user, "You flick the switch on [src], but nothing happens.") - return 0 - on = !on - if(on && power_use) - START_PROCESSING(SSobj, src) - else if(power_use) - STOP_PROCESSING(SSobj, src) - playsound(src, 'sound/weapons/empty.ogg', 15, 1, -3) // VOREStation Edit - update_brightness() - user.update_action_buttons() - return 1 - -/obj/item/device/flashlight/emp_act(severity) - for(var/obj/O in contents) - O.emp_act(severity) - ..() - -/obj/item/device/flashlight/attack(mob/living/M as mob, mob/living/user as mob) - add_fingerprint(user) - if(on && user.zone_sel.selecting == O_EYES) - - if((CLUMSY in user.mutations) && prob(50)) //too dumb to use flashlight properly - return ..() //just hit them in the head - - var/mob/living/carbon/human/H = M //mob has protective eyewear - if(istype(H)) - for(var/obj/item/clothing/C in list(H.head,H.wear_mask,H.glasses)) - if(istype(C) && (C.body_parts_covered & EYES)) - to_chat(user, "You're going to need to remove [C.name] first.") - return - - var/obj/item/organ/vision - if(H.species.vision_organ) - vision = H.internal_organs_by_name[H.species.vision_organ] - if(!vision) - user.visible_message("\The [user] directs [src] at [M]'s face.", \ - "You direct [src] at [M]'s face.") - to_chat(user, "You can't find any [H.species.vision_organ ? H.species.vision_organ : "eyes"] on [H]!") - user.setClickCooldown(user.get_attack_speed(src)) - return - - user.visible_message("\The [user] directs [src] to [M]'s eyes.", \ - "You direct [src] to [M]'s eyes.") - if(H != user) //can't look into your own eyes buster - if(M.stat == DEAD || M.blinded) //mob is dead or fully blind - to_chat(user, "\The [M]'s pupils do not react to the light!") - return - if(XRAY in M.mutations) - to_chat(user, "\The [M] pupils give an eerie glow!") - if(vision.is_bruised()) - to_chat(user, "There's visible damage to [M]'s [vision.name]!") - else if(M.eye_blurry) - to_chat(user, "\The [M]'s pupils react slower than normally.") - if(M.getBrainLoss() > 15) - to_chat(user, "There's visible lag between left and right pupils' reactions.") - - var/list/pinpoint = list("oxycodone"=1,"tramadol"=5) - var/list/dilating = list("bliss"=5,"ambrosia_extract"=5,"mindbreaker"=1) - if(M.reagents.has_any_reagent(pinpoint) || H.ingested.has_any_reagent(pinpoint)) - to_chat(user, "\The [M]'s pupils are already pinpoint and cannot narrow any more.") - else if(M.reagents.has_any_reagent(dilating) || H.ingested.has_any_reagent(dilating)) - to_chat(user, "\The [M]'s pupils narrow slightly, but are still very dilated.") - else - to_chat(user, "\The [M]'s pupils narrow.") - - user.setClickCooldown(user.get_attack_speed(src)) //can be used offensively - M.flash_eyes() - else - return ..() - -/obj/item/device/flashlight/attack_hand(mob/user as mob) - if(user.get_inactive_hand() == src) - if(cell) - cell.update_icon() - user.put_in_hands(cell) - cell = null - to_chat(user, "You remove the cell from the [src].") - playsound(src, 'sound/machines/button.ogg', 30, 1, 0) - on = 0 - update_brightness() - return - ..() - else - return ..() - -/obj/item/device/flashlight/MouseDrop(obj/over_object as obj) - if(!canremove) - return - - if (ishuman(usr) || issmall(usr)) //so monkeys can take off their backpacks -- Urist - - if (istype(usr.loc,/obj/mecha)) // stops inventory actions in a mech. why? - return - - if (!( istype(over_object, /obj/screen) )) - return ..() - - //makes sure that the thing is equipped, so that we can't drag it into our hand from miles away. - //there's got to be a better way of doing this. - if (!(src.loc == usr) || (src.loc && src.loc.loc == usr)) - return - - if (( usr.restrained() ) || ( usr.stat )) - return - - if ((src.loc == usr) && !(istype(over_object, /obj/screen)) && !usr.unEquip(src)) - return - - switch(over_object.name) - if("r_hand") - usr.u_equip(src) - usr.put_in_r_hand(src) - if("l_hand") - usr.u_equip(src) - usr.put_in_l_hand(src) - src.add_fingerprint(usr) - -/obj/item/device/flashlight/attackby(obj/item/weapon/W, mob/user as mob) - if(power_use) - if(istype(W, /obj/item/weapon/cell)) - if(istype(W, /obj/item/weapon/cell/device)) - if(!cell) - user.drop_item() - W.loc = src - cell = W - to_chat(user, "You install a cell in \the [src].") - playsound(src, 'sound/machines/button.ogg', 30, 1, 0) - update_brightness() - else - to_chat(user, "\The [src] already has a cell.") - else - to_chat(user, "\The [src] cannot use that type of cell.") - - else - ..() - -/obj/item/device/flashlight/afterattack(atom/target, mob/user, proximity_flag, click_parameters) - . = ..() - if(!on) - return - if(light_system == MOVABLE_LIGHT_DIRECTIONAL) - var/datum/component/overlay_lighting/OL = GetComponent(/datum/component/overlay_lighting) - if(!OL) - return - var/turf/T = get_turf(target) - OL.place_directional_light(T) - -/obj/item/device/flashlight/pen - name = "penlight" - desc = "A pen-sized light, used by medical staff." - icon_state = "penlight" - item_state = "pen" - drop_sound = 'sound/items/drop/accessory.ogg' - pickup_sound = 'sound/items/pickup/accessory.ogg' - slot_flags = SLOT_EARS - light_range = 2 - w_class = ITEMSIZE_TINY - power_use = 0 - -/obj/item/device/flashlight/color //Default color is blue - name = "blue flashlight" - desc = "A small flashlight. This one is blue." - icon_state = "flashlight_blue" - -/obj/item/device/flashlight/color/green - name = "green flashlight" - desc = "A small flashlight. This one is green." - icon_state = "flashlight_green" - -/obj/item/device/flashlight/color/purple - name = "purple flashlight" - desc = "A small flashlight. This one is purple." - icon_state = "flashlight_purple" - -/obj/item/device/flashlight/color/red - name = "red flashlight" - desc = "A small flashlight. This one is red." - icon_state = "flashlight_red" - -/obj/item/device/flashlight/color/orange - name = "orange flashlight" - desc = "A small flashlight. This one is orange." - icon_state = "flashlight_orange" - -/obj/item/device/flashlight/color/yellow - name = "yellow flashlight" - desc = "A small flashlight. This one is yellow." - icon_state = "flashlight_yellow" - -/obj/item/device/flashlight/maglight - name = "maglight" - desc = "A very, very heavy duty flashlight." - icon_state = "maglight" - light_color = LIGHT_COLOR_FLUORESCENT_FLASHLIGHT - force = 10 - slot_flags = SLOT_BELT - w_class = ITEMSIZE_SMALL - attack_verb = list ("smacked", "thwacked", "thunked") - matter = list(MAT_STEEL = 200,MAT_GLASS = 50) - hitsound = "swing_hit" - -/obj/item/device/flashlight/drone - name = "low-power flashlight" - desc = "A miniature lamp, that might be used by small robots." - icon_state = "penlight" - item_state = null - light_range = 2 - w_class = ITEMSIZE_TINY - power_use = 0 - -/* - * Lamps - */ - -// pixar desk lamp -/obj/item/device/flashlight/lamp - name = "desk lamp" - desc = "A desk lamp with an adjustable mount." - icon_state = "lamp" - force = 10 - center_of_mass = list("x" = 13,"y" = 11) - light_range = 5 - w_class = ITEMSIZE_LARGE - power_use = 0 - on = 1 - light_system = STATIC_LIGHT - -/obj/item/device/flashlight/lamp/verb/toggle_light() - set name = "Toggle light" - set category = "Object" - set src in oview(1) - - if(!usr.stat) - attack_self(usr) - -// green-shaded desk lamp -/obj/item/device/flashlight/lamp/green - desc = "A classic green-shaded desk lamp." - icon_state = "lampgreen" - center_of_mass = list("x" = 15,"y" = 11) - light_color = "#FFC58F" - -// clown lamp -/obj/item/device/flashlight/lamp/clown - desc = "A whacky banana peel shaped lamp." - icon_state = "bananalamp" - center_of_mass = list("x" = 15,"y" = 11) - - -/* - * Flares - */ - -/obj/item/device/flashlight/flare - name = "flare" - desc = "A red standard-issue flare. There are instructions on the side reading 'pull cord, make light'." - w_class = ITEMSIZE_SMALL - light_range = 8 // Pretty bright. - light_power = 0.8 - light_color = LIGHT_COLOR_FLARE - icon_state = "flare" - item_state = "flare" - action_button_name = null //just pull it manually, neckbeard. - var/fuel = 0 - var/on_damage = 7 - var/produce_heat = 1500 - power_use = 0 - drop_sound = 'sound/items/drop/gloves.ogg' - pickup_sound = 'sound/items/pickup/gloves.ogg' - light_system = MOVABLE_LIGHT - -/obj/item/device/flashlight/flare/New() - fuel = rand(800, 1000) // Sorry for changing this so much but I keep under-estimating how long X number of ticks last in seconds. - ..() - -/obj/item/device/flashlight/flare/process() - var/turf/pos = get_turf(src) - if(pos) - pos.hotspot_expose(produce_heat, 5) - fuel = max(fuel - 1, 0) - if(!fuel || !on) - turn_off() - if(!fuel) - src.icon_state = "[initial(icon_state)]-empty" - STOP_PROCESSING(SSobj, src) - -/obj/item/device/flashlight/flare/proc/turn_off() - on = 0 - src.force = initial(src.force) - src.damtype = initial(src.damtype) - update_brightness() - -/obj/item/device/flashlight/flare/attack_self(mob/user) - - // Usual checks - if(!fuel) - to_chat(user, "It's out of fuel.") - return - if(on) - return - - . = ..() - // All good, turn it on. - if(.) - user.visible_message("[user] activates the flare.", "You pull the cord on the flare, activating it!") - src.force = on_damage - src.damtype = "fire" - START_PROCESSING(SSobj, src) - -/obj/item/device/flashlight/flare/proc/ignite() //Used for flare launchers. - on = !on - update_brightness() - force = on_damage - damtype = "fire" - START_PROCESSING(SSobj, src) - return 1 - -/* - * Chemlights - */ - -/obj/item/device/flashlight/glowstick - name = "green glowstick" - desc = "A green military-grade chemical light." - w_class = ITEMSIZE_SMALL - light_system = MOVABLE_LIGHT - light_range = 4 - light_power = 0.9 - light_color = "#49F37C" - icon_state = "glowstick_green" - item_state = "glowstick_green" - var/fuel = 0 - power_use = 0 - -/obj/item/device/flashlight/glowstick/New() - fuel = rand(1600, 2000) - ..() - -/obj/item/device/flashlight/glowstick/process() - fuel = max(fuel - 1, 0) - if(!fuel || !on) - turn_off() - if(!fuel) - src.icon_state = "[initial(icon_state)]-empty" - STOP_PROCESSING(SSobj, src) - -/obj/item/device/flashlight/glowstick/proc/turn_off() - on = 0 - update_brightness() - -/obj/item/device/flashlight/glowstick/attack_self(mob/user) - - if(!fuel) - to_chat(user, "The glowstick has already been turned on.") - return - if(on) - return - - . = ..() - if(.) - user.visible_message("[user] cracks and shakes \the [name].", "You crack and shake \the [src], turning it on!") - START_PROCESSING(SSobj, src) - -/obj/item/device/flashlight/glowstick/red - name = "red glowstick" - desc = "A red military-grade chemical light." - light_color = "#FC0F29" - icon_state = "glowstick_red" - item_state = "glowstick_red" - -/obj/item/device/flashlight/glowstick/blue - name = "blue glowstick" - desc = "A blue military-grade chemical light." - light_color = "#599DFF" - icon_state = "glowstick_blue" - item_state = "glowstick_blue" - -/obj/item/device/flashlight/glowstick/orange - name = "orange glowstick" - desc = "A orange military-grade chemical light." - light_color = "#FA7C0B" - icon_state = "glowstick_orange" - item_state = "glowstick_orange" - -/obj/item/device/flashlight/glowstick/yellow - name = "yellow glowstick" - desc = "A yellow military-grade chemical light." - light_color = "#FEF923" - icon_state = "glowstick_yellow" - item_state = "glowstick_yellow" - -/obj/item/device/flashlight/glowstick/radioisotope - name = "radioisotope glowstick" - desc = "A radioisotope powered chemical light. Escaping particles light up the area far brighter on similar levels to flares and for longer" - icon_state = "glowstick_isotope" - item_state = "glowstick_isotope" - - light_range = 8 - light_power = 0.1 - light_color = "#49F37C" +/* + * Contains: + * Flashlights + * Lamps + * Flares + * Chemlights + * Slime Extract + */ + +/* + * Flashlights + */ + +/obj/item/device/flashlight + name = "flashlight" + desc = "A hand-held emergency light." + icon = 'icons/obj/lighting.dmi' + icon_state = "flashlight" + w_class = ITEMSIZE_SMALL + slot_flags = SLOT_BELT + matter = list(MAT_STEEL = 50,MAT_GLASS = 20) + action_button_name = "Toggle Flashlight" + + light_system = MOVABLE_LIGHT_DIRECTIONAL + light_range = 4 //luminosity when on + light_power = 0.8 //lighting power when on + light_color = "#FFFFFF" //LIGHT_COLOR_INCANDESCENT_FLASHLIGHT //lighting colour when on + light_cone_y_offset = -7 + + var/on = 0 + + var/obj/item/weapon/cell/cell + var/cell_type = /obj/item/weapon/cell/device + var/power_usage = 1 + var/power_use = 1 + +/obj/item/device/flashlight/Initialize() + . = ..() + + if(power_use && cell_type) + cell = new cell_type(src) + + update_brightness() + +/obj/item/device/flashlight/Destroy() + STOP_PROCESSING(SSobj, src) + qdel_null(cell) + return ..() + +/obj/item/device/flashlight/get_cell() + return cell + +/obj/item/device/flashlight/process() + if(!on || !cell) + return PROCESS_KILL + + if(power_usage) + if(cell.use(power_usage) != power_usage) // we weren't able to use our full power_usage amount! + visible_message("\The [src] flickers before going dull.") + playsound(src, 'sound/effects/sparks3.ogg', 10, 1, -3) //Small cue that your light went dull in your pocket. //VOREStation Edit + on = 0 + update_brightness() + return PROCESS_KILL + +/obj/item/device/flashlight/proc/update_brightness() + if(on) + icon_state = "[initial(icon_state)]-on" + else + icon_state = initial(icon_state) + set_light_on(on) + if(light_system == STATIC_LIGHT) + update_light() + +/obj/item/device/flashlight/examine(mob/user) + . = ..() + if(power_use && cell) + . += "\The [src] has a \the [cell] attached." + + if(cell.charge <= cell.maxcharge*0.25) + . += "It appears to have a low amount of power remaining." + else if(cell.charge > cell.maxcharge*0.25 && cell.charge <= cell.maxcharge*0.5) + . += "It appears to have an average amount of power remaining." + else if(cell.charge > cell.maxcharge*0.5 && cell.charge <= cell.maxcharge*0.75) + . += "It appears to have an above average amount of power remaining." + else if(cell.charge > cell.maxcharge*0.75 && cell.charge <= cell.maxcharge) + . += "It appears to have a high amount of power remaining." + +/obj/item/device/flashlight/attack_self(mob/user) + if(power_use) + if(!isturf(user.loc)) + to_chat(user, "You cannot turn the light on while in this [user.loc].") //To prevent some lighting anomalities. + return 0 + if(!cell || cell.charge == 0) + to_chat(user, "You flick the switch on [src], but nothing happens.") + return 0 + on = !on + if(on && power_use) + START_PROCESSING(SSobj, src) + else if(power_use) + STOP_PROCESSING(SSobj, src) + playsound(src, 'sound/weapons/empty.ogg', 15, 1, -3) // VOREStation Edit + update_brightness() + user.update_action_buttons() + return 1 + +/obj/item/device/flashlight/emp_act(severity) + for(var/obj/O in contents) + O.emp_act(severity) + ..() + +/obj/item/device/flashlight/attack(mob/living/M as mob, mob/living/user as mob) + add_fingerprint(user) + if(on && user.zone_sel.selecting == O_EYES) + + if((CLUMSY in user.mutations) && prob(50)) //too dumb to use flashlight properly + return ..() //just hit them in the head + + var/mob/living/carbon/human/H = M //mob has protective eyewear + if(istype(H)) + for(var/obj/item/clothing/C in list(H.head,H.wear_mask,H.glasses)) + if(istype(C) && (C.body_parts_covered & EYES)) + to_chat(user, "You're going to need to remove [C.name] first.") + return + + var/obj/item/organ/vision + if(H.species.vision_organ) + vision = H.internal_organs_by_name[H.species.vision_organ] + if(!vision) + user.visible_message("\The [user] directs [src] at [M]'s face.", \ + "You direct [src] at [M]'s face.") + to_chat(user, "You can't find any [H.species.vision_organ ? H.species.vision_organ : "eyes"] on [H]!") + user.setClickCooldown(user.get_attack_speed(src)) + return + + user.visible_message("\The [user] directs [src] to [M]'s eyes.", \ + "You direct [src] to [M]'s eyes.") + if(H != user) //can't look into your own eyes buster + if(M.stat == DEAD || M.blinded) //mob is dead or fully blind + to_chat(user, "\The [M]'s pupils do not react to the light!") + return + if(XRAY in M.mutations) + to_chat(user, "\The [M] pupils give an eerie glow!") + if(vision.is_bruised()) + to_chat(user, "There's visible damage to [M]'s [vision.name]!") + else if(M.eye_blurry) + to_chat(user, "\The [M]'s pupils react slower than normally.") + if(M.getBrainLoss() > 15) + to_chat(user, "There's visible lag between left and right pupils' reactions.") + + var/list/pinpoint = list("oxycodone"=1,"tramadol"=5) + var/list/dilating = list("bliss"=5,"ambrosia_extract"=5,"mindbreaker"=1) + if(M.reagents.has_any_reagent(pinpoint) || H.ingested.has_any_reagent(pinpoint)) + to_chat(user, "\The [M]'s pupils are already pinpoint and cannot narrow any more.") + else if(M.reagents.has_any_reagent(dilating) || H.ingested.has_any_reagent(dilating)) + to_chat(user, "\The [M]'s pupils narrow slightly, but are still very dilated.") + else + to_chat(user, "\The [M]'s pupils narrow.") + + user.setClickCooldown(user.get_attack_speed(src)) //can be used offensively + M.flash_eyes() + else + return ..() + +/obj/item/device/flashlight/attack_hand(mob/user as mob) + if(user.get_inactive_hand() == src) + if(cell) + cell.update_icon() + user.put_in_hands(cell) + cell = null + to_chat(user, "You remove the cell from the [src].") + playsound(src, 'sound/machines/button.ogg', 30, 1, 0) + on = 0 + update_brightness() + return + ..() + else + return ..() + +/obj/item/device/flashlight/MouseDrop(obj/over_object as obj) + if(!canremove) + return + + if (ishuman(usr) || issmall(usr)) //so monkeys can take off their backpacks -- Urist + + if (istype(usr.loc,/obj/mecha)) // stops inventory actions in a mech. why? + return + + if (!( istype(over_object, /obj/screen) )) + return ..() + + //makes sure that the thing is equipped, so that we can't drag it into our hand from miles away. + //there's got to be a better way of doing this. + if (!(src.loc == usr) || (src.loc && src.loc.loc == usr)) + return + + if (( usr.restrained() ) || ( usr.stat )) + return + + if ((src.loc == usr) && !(istype(over_object, /obj/screen)) && !usr.unEquip(src)) + return + + switch(over_object.name) + if("r_hand") + usr.u_equip(src) + usr.put_in_r_hand(src) + if("l_hand") + usr.u_equip(src) + usr.put_in_l_hand(src) + src.add_fingerprint(usr) + +/obj/item/device/flashlight/attackby(obj/item/weapon/W, mob/user as mob) + if(power_use) + if(istype(W, /obj/item/weapon/cell)) + if(istype(W, /obj/item/weapon/cell/device)) + if(!cell) + user.drop_item() + W.loc = src + cell = W + to_chat(user, "You install a cell in \the [src].") + playsound(src, 'sound/machines/button.ogg', 30, 1, 0) + update_brightness() + else + to_chat(user, "\The [src] already has a cell.") + else + to_chat(user, "\The [src] cannot use that type of cell.") + + else + ..() + +/obj/item/device/flashlight/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(!on) + return + if(light_system == MOVABLE_LIGHT_DIRECTIONAL) + var/datum/component/overlay_lighting/OL = GetComponent(/datum/component/overlay_lighting) + if(!OL) + return + var/turf/T = get_turf(target) + OL.place_directional_light(T) + +/obj/item/device/flashlight/pen + name = "penlight" + desc = "A pen-sized light, used by medical staff." + icon_state = "penlight" + item_state = "pen" + drop_sound = 'sound/items/drop/accessory.ogg' + pickup_sound = 'sound/items/pickup/accessory.ogg' + slot_flags = SLOT_EARS + light_range = 2 + w_class = ITEMSIZE_TINY + power_use = 0 + +/obj/item/device/flashlight/color //Default color is blue + name = "blue flashlight" + desc = "A small flashlight. This one is blue." + icon_state = "flashlight_blue" + +/obj/item/device/flashlight/color/green + name = "green flashlight" + desc = "A small flashlight. This one is green." + icon_state = "flashlight_green" + +/obj/item/device/flashlight/color/purple + name = "purple flashlight" + desc = "A small flashlight. This one is purple." + icon_state = "flashlight_purple" + +/obj/item/device/flashlight/color/red + name = "red flashlight" + desc = "A small flashlight. This one is red." + icon_state = "flashlight_red" + +/obj/item/device/flashlight/color/orange + name = "orange flashlight" + desc = "A small flashlight. This one is orange." + icon_state = "flashlight_orange" + +/obj/item/device/flashlight/color/yellow + name = "yellow flashlight" + desc = "A small flashlight. This one is yellow." + icon_state = "flashlight_yellow" + +/obj/item/device/flashlight/maglight + name = "maglight" + desc = "A very, very heavy duty flashlight." + icon_state = "maglight" + light_color = LIGHT_COLOR_FLUORESCENT_FLASHLIGHT + force = 10 + slot_flags = SLOT_BELT + w_class = ITEMSIZE_SMALL + attack_verb = list ("smacked", "thwacked", "thunked") + matter = list(MAT_STEEL = 200,MAT_GLASS = 50) + hitsound = "swing_hit" + +/obj/item/device/flashlight/drone + name = "low-power flashlight" + desc = "A miniature lamp, that might be used by small robots." + icon_state = "penlight" + item_state = null + light_range = 2 + w_class = ITEMSIZE_TINY + power_use = 0 + +/* + * Lamps + */ + +// pixar desk lamp +/obj/item/device/flashlight/lamp + name = "desk lamp" + desc = "A desk lamp with an adjustable mount." + icon_state = "lamp" + force = 10 + center_of_mass = list("x" = 13,"y" = 11) + light_range = 5 + w_class = ITEMSIZE_LARGE + power_use = 0 + on = 1 + light_system = STATIC_LIGHT + +/obj/item/device/flashlight/lamp/verb/toggle_light() + set name = "Toggle light" + set category = "Object" + set src in oview(1) + + if(!usr.stat) + attack_self(usr) + +// green-shaded desk lamp +/obj/item/device/flashlight/lamp/green + desc = "A classic green-shaded desk lamp." + icon_state = "lampgreen" + center_of_mass = list("x" = 15,"y" = 11) + light_color = "#FFC58F" + +// clown lamp +/obj/item/device/flashlight/lamp/clown + desc = "A whacky banana peel shaped lamp." + icon_state = "bananalamp" + center_of_mass = list("x" = 15,"y" = 11) + + +/* + * Flares + */ + +/obj/item/device/flashlight/flare + name = "flare" + desc = "A red standard-issue flare. There are instructions on the side reading 'pull cord, make light'." + w_class = ITEMSIZE_SMALL + light_range = 8 // Pretty bright. + light_power = 0.8 + light_color = LIGHT_COLOR_FLARE + icon_state = "flare" + item_state = "flare" + action_button_name = null //just pull it manually, neckbeard. + var/fuel = 0 + var/on_damage = 7 + var/produce_heat = 1500 + power_use = 0 + drop_sound = 'sound/items/drop/gloves.ogg' + pickup_sound = 'sound/items/pickup/gloves.ogg' + light_system = MOVABLE_LIGHT + +/obj/item/device/flashlight/flare/New() + fuel = rand(800, 1000) // Sorry for changing this so much but I keep under-estimating how long X number of ticks last in seconds. + ..() + +/obj/item/device/flashlight/flare/process() + var/turf/pos = get_turf(src) + if(pos) + pos.hotspot_expose(produce_heat, 5) + fuel = max(fuel - 1, 0) + if(!fuel || !on) + turn_off() + if(!fuel) + src.icon_state = "[initial(icon_state)]-empty" + STOP_PROCESSING(SSobj, src) + +/obj/item/device/flashlight/flare/proc/turn_off() + on = 0 + src.force = initial(src.force) + src.damtype = initial(src.damtype) + update_brightness() + +/obj/item/device/flashlight/flare/attack_self(mob/user) + + // Usual checks + if(!fuel) + to_chat(user, "It's out of fuel.") + return + if(on) + return + + . = ..() + // All good, turn it on. + if(.) + user.visible_message("[user] activates the flare.", "You pull the cord on the flare, activating it!") + src.force = on_damage + src.damtype = "fire" + START_PROCESSING(SSobj, src) + +/obj/item/device/flashlight/flare/proc/ignite() //Used for flare launchers. + on = !on + update_brightness() + force = on_damage + damtype = "fire" + START_PROCESSING(SSobj, src) + return 1 + +/* + * Chemlights + */ + +/obj/item/device/flashlight/glowstick + name = "green glowstick" + desc = "A green military-grade chemical light." + w_class = ITEMSIZE_SMALL + light_system = MOVABLE_LIGHT + light_range = 4 + light_power = 0.9 + light_color = "#49F37C" + icon_state = "glowstick_green" + item_state = "glowstick_green" + var/fuel = 0 + power_use = 0 + +/obj/item/device/flashlight/glowstick/New() + fuel = rand(1600, 2000) + ..() + +/obj/item/device/flashlight/glowstick/process() + fuel = max(fuel - 1, 0) + if(!fuel || !on) + turn_off() + if(!fuel) + src.icon_state = "[initial(icon_state)]-empty" + STOP_PROCESSING(SSobj, src) + +/obj/item/device/flashlight/glowstick/proc/turn_off() + on = 0 + update_brightness() + +/obj/item/device/flashlight/glowstick/attack_self(mob/user) + + if(!fuel) + to_chat(user, "The glowstick has already been turned on.") + return + if(on) + return + + . = ..() + if(.) + user.visible_message("[user] cracks and shakes \the [name].", "You crack and shake \the [src], turning it on!") + START_PROCESSING(SSobj, src) + +/obj/item/device/flashlight/glowstick/red + name = "red glowstick" + desc = "A red military-grade chemical light." + light_color = "#FC0F29" + icon_state = "glowstick_red" + item_state = "glowstick_red" + +/obj/item/device/flashlight/glowstick/blue + name = "blue glowstick" + desc = "A blue military-grade chemical light." + light_color = "#599DFF" + icon_state = "glowstick_blue" + item_state = "glowstick_blue" + +/obj/item/device/flashlight/glowstick/orange + name = "orange glowstick" + desc = "A orange military-grade chemical light." + light_color = "#FA7C0B" + icon_state = "glowstick_orange" + item_state = "glowstick_orange" + +/obj/item/device/flashlight/glowstick/yellow + name = "yellow glowstick" + desc = "A yellow military-grade chemical light." + light_color = "#FEF923" + icon_state = "glowstick_yellow" + item_state = "glowstick_yellow" + +/obj/item/device/flashlight/glowstick/radioisotope + name = "radioisotope glowstick" + desc = "A radioisotope powered chemical light. Escaping particles light up the area far brighter on similar levels to flares and for longer" + icon_state = "glowstick_isotope" + item_state = "glowstick_isotope" + + light_range = 8 + light_power = 0.1 + light_color = "#49F37C" diff --git a/code/game/objects/items/devices/hacktool.dm b/code/game/objects/items/devices/hacktool.dm index 223ac5a1c4c..38f1c2afdf6 100644 --- a/code/game/objects/items/devices/hacktool.dm +++ b/code/game/objects/items/devices/hacktool.dm @@ -1,130 +1,130 @@ -/obj/item/device/multitool/hacktool - var/is_hacking = 0 - var/max_known_targets - var/hackspeed = 1 - var/max_level = 4 //what's the max door security_level we can handle? - var/full_override = FALSE //can we override door bolts too? defaults to false for event/safety reasons - - var/in_hack_mode = 0 - var/list/known_targets - var/list/supported_types - var/datum/tgui_state/default/must_hack/hack_state - -/obj/item/device/multitool/hacktool/override - hackspeed = 0.75 - max_level = 5 - full_override = TRUE - -/obj/item/device/multitool/hacktool/New() - ..() - known_targets = list() - max_known_targets = 5 + rand(1,3) - supported_types = list(/obj/machinery/door/airlock) - hack_state = new(src) - -/obj/item/device/multitool/hacktool/Destroy() - for(var/atom/target as anything in known_targets) - target.unregister(OBSERVER_EVENT_DESTROY, src) - known_targets.Cut() - qdel(hack_state) - hack_state = null - return ..() - -/obj/item/device/multitool/hacktool/attackby(var/obj/item/W, var/mob/user) - if(W.has_tool_quality(TOOL_SCREWDRIVER)) - in_hack_mode = !in_hack_mode - playsound(src, W.usesound, 50, 1) - else - ..() - -/obj/item/device/multitool/hacktool/afterattack(atom/A, mob/user) - sanity_check() - - if(!in_hack_mode) - return ..() - - if(!attempt_hack(user, A)) - return 0 - - // Note, if you ever want to expand supported_types, you must manually add the custom state argument to their tgui_interact - // DISABLED: too fancy, too high-effort // A.tgui_interact(user, custom_state = hack_state) - // Just brute-force it - if(istype(A, /obj/machinery/door/airlock)) - var/obj/machinery/door/airlock/D = A - if(!D.arePowerSystemsOn()) - to_chat(user, "No response from remote, check door power.") - else if(D.locked == TRUE && full_override == FALSE) - to_chat(user, "Unable to override door bolts!") - else if(D.locked == TRUE && full_override == TRUE && D.arePowerSystemsOn()) - to_chat(user, "Door bolts overridden.") - D.unlock() - else if(D.density == TRUE && D.locked == FALSE) - to_chat(user, "Overriding access. Door opening.") - D.open() - else if(D.density == FALSE && D.locked == FALSE) - to_chat(user, "Overriding access. Door closing.") - D.close() - return 1 - -/obj/item/device/multitool/hacktool/proc/attempt_hack(var/mob/user, var/atom/target) - if(is_hacking) - to_chat(user, "You are already hacking!") - return 0 - if(!is_type_in_list(target, supported_types)) - to_chat(user, "\icon[src][bicon(src)] Unable to hack this target, invalid target type.") - return 0 - - var/obj/machinery/door/airlock/D = target - if(D.security_level > max_level) - to_chat(user, "\icon[src][bicon(src)] Target's electronic security is too complex.") - return 0 - - var/found = known_targets.Find(D) - if(found) - known_targets.Swap(1, found) // Move the last hacked item first - return 1 - to_chat(user, "You begin hacking \the [D]...") - is_hacking = 1 - // On average hackin takes ~15 seconds. Fairly small random span to avoid people simply aborting and trying again - // Reduced hack duration to compensate for the reduced functionality, multiplied by door sec level - var/hack_result = do_after(user, (((10 SECONDS + rand(0, 10 SECONDS) + rand(0, 10 SECONDS))*hackspeed)*D.security_level)) - is_hacking = 0 - - if(hack_result && in_hack_mode) - to_chat(user, "Your hacking attempt was succesful!") - user.playsound_local(get_turf(src), 'sound/instruments/piano/An6.ogg', 50) - else - to_chat(user, "Your hacking attempt failed!") - return 0 - - known_targets.Insert(1, D) // Insert the newly hacked target first, - D.register(OBSERVER_EVENT_DESTROY, src, /obj/item/device/multitool/hacktool/proc/on_target_destroy) - return 1 - -/obj/item/device/multitool/hacktool/proc/sanity_check() - if(max_known_targets < 1) max_known_targets = 1 - // Cut away the oldest items if the capacity has been reached - if(known_targets.len > max_known_targets) - for(var/i = (max_known_targets + 1) to known_targets.len) - var/atom/A = known_targets[i] - A.unregister(OBSERVER_EVENT_DESTROY, src) - known_targets.Cut(max_known_targets + 1) - -/obj/item/device/multitool/hacktool/proc/on_target_destroy(var/target) - known_targets -= target - -/datum/tgui_state/default/must_hack - var/obj/item/device/multitool/hacktool/hacktool - -/datum/tgui_state/default/must_hack/New(var/hacktool) - src.hacktool = hacktool - ..() - -/datum/tgui_state/default/must_hack/Destroy() - hacktool = null - return ..() - -/datum/tgui_state/default/must_hack/can_use_topic(src_object, mob/user) - if(!hacktool || !hacktool.in_hack_mode || !(src_object in hacktool.known_targets)) - return STATUS_CLOSE - return ..() +/obj/item/device/multitool/hacktool + var/is_hacking = 0 + var/max_known_targets + var/hackspeed = 1 + var/max_level = 4 //what's the max door security_level we can handle? + var/full_override = FALSE //can we override door bolts too? defaults to false for event/safety reasons + + var/in_hack_mode = 0 + var/list/known_targets + var/list/supported_types + var/datum/tgui_state/default/must_hack/hack_state + +/obj/item/device/multitool/hacktool/override + hackspeed = 0.75 + max_level = 5 + full_override = TRUE + +/obj/item/device/multitool/hacktool/New() + ..() + known_targets = list() + max_known_targets = 5 + rand(1,3) + supported_types = list(/obj/machinery/door/airlock) + hack_state = new(src) + +/obj/item/device/multitool/hacktool/Destroy() + for(var/atom/target as anything in known_targets) + target.unregister(OBSERVER_EVENT_DESTROY, src) + known_targets.Cut() + qdel(hack_state) + hack_state = null + return ..() + +/obj/item/device/multitool/hacktool/attackby(var/obj/item/W, var/mob/user) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + in_hack_mode = !in_hack_mode + playsound(src, W.usesound, 50, 1) + else + ..() + +/obj/item/device/multitool/hacktool/afterattack(atom/A, mob/user) + sanity_check() + + if(!in_hack_mode) + return ..() + + if(!attempt_hack(user, A)) + return 0 + + // Note, if you ever want to expand supported_types, you must manually add the custom state argument to their tgui_interact + // DISABLED: too fancy, too high-effort // A.tgui_interact(user, custom_state = hack_state) + // Just brute-force it + if(istype(A, /obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/D = A + if(!D.arePowerSystemsOn()) + to_chat(user, "No response from remote, check door power.") + else if(D.locked == TRUE && full_override == FALSE) + to_chat(user, "Unable to override door bolts!") + else if(D.locked == TRUE && full_override == TRUE && D.arePowerSystemsOn()) + to_chat(user, "Door bolts overridden.") + D.unlock() + else if(D.density == TRUE && D.locked == FALSE) + to_chat(user, "Overriding access. Door opening.") + D.open() + else if(D.density == FALSE && D.locked == FALSE) + to_chat(user, "Overriding access. Door closing.") + D.close() + return 1 + +/obj/item/device/multitool/hacktool/proc/attempt_hack(var/mob/user, var/atom/target) + if(is_hacking) + to_chat(user, "You are already hacking!") + return 0 + if(!is_type_in_list(target, supported_types)) + to_chat(user, "\icon[src][bicon(src)] Unable to hack this target, invalid target type.") + return 0 + + var/obj/machinery/door/airlock/D = target + if(D.security_level > max_level) + to_chat(user, "\icon[src][bicon(src)] Target's electronic security is too complex.") + return 0 + + var/found = known_targets.Find(D) + if(found) + known_targets.Swap(1, found) // Move the last hacked item first + return 1 + to_chat(user, "You begin hacking \the [D]...") + is_hacking = 1 + // On average hackin takes ~15 seconds. Fairly small random span to avoid people simply aborting and trying again + // Reduced hack duration to compensate for the reduced functionality, multiplied by door sec level + var/hack_result = do_after(user, (((10 SECONDS + rand(0, 10 SECONDS) + rand(0, 10 SECONDS))*hackspeed)*D.security_level)) + is_hacking = 0 + + if(hack_result && in_hack_mode) + to_chat(user, "Your hacking attempt was succesful!") + user.playsound_local(get_turf(src), 'sound/instruments/piano/An6.ogg', 50) + else + to_chat(user, "Your hacking attempt failed!") + return 0 + + known_targets.Insert(1, D) // Insert the newly hacked target first, + D.register(OBSERVER_EVENT_DESTROY, src, /obj/item/device/multitool/hacktool/proc/on_target_destroy) + return 1 + +/obj/item/device/multitool/hacktool/proc/sanity_check() + if(max_known_targets < 1) max_known_targets = 1 + // Cut away the oldest items if the capacity has been reached + if(known_targets.len > max_known_targets) + for(var/i = (max_known_targets + 1) to known_targets.len) + var/atom/A = known_targets[i] + A.unregister(OBSERVER_EVENT_DESTROY, src) + known_targets.Cut(max_known_targets + 1) + +/obj/item/device/multitool/hacktool/proc/on_target_destroy(var/target) + known_targets -= target + +/datum/tgui_state/default/must_hack + var/obj/item/device/multitool/hacktool/hacktool + +/datum/tgui_state/default/must_hack/New(var/hacktool) + src.hacktool = hacktool + ..() + +/datum/tgui_state/default/must_hack/Destroy() + hacktool = null + return ..() + +/datum/tgui_state/default/must_hack/can_use_topic(src_object, mob/user) + if(!hacktool || !hacktool.in_hack_mode || !(src_object in hacktool.known_targets)) + return STATUS_CLOSE + return ..() diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm index 54cc46d802c..f188f054312 100644 --- a/code/game/objects/items/devices/lightreplacer.dm +++ b/code/game/objects/items/devices/lightreplacer.dm @@ -1,230 +1,230 @@ - -// Light Replacer (LR) -// -// ABOUT THE DEVICE -// -// This is a device supposedly to be used by Janitors and Janitor Cyborgs which will -// allow them to easily replace lights. This was mostly designed for Janitor Cyborgs since -// they don't have hands or a way to replace lightbulbs. -// -// HOW IT WORKS -// -// You attack a light fixture with it, if the light fixture is broken it will replace the -// light fixture with a working light; the broken light is then placed on the floor for the -// user to then pickup with a trash bag. If it's empty then it will just place a light in the fixture. -// -// HOW TO REFILL THE DEVICE -// -// It can be manually refilled or by clicking on a storage item containing lights. -// If it's part of a robot module, it will charge when the Robot is inside a Recharge Station. -// -// EMAGGED FEATURES -// -// NOTICE: The Cyborg cannot use the emagged Light Replacer and the light's explosion was nerfed. It cannot create holes in the station anymore. -// -// I'm not sure everyone will react the emag's features so please say what your opinions are of it. -// -// When emagged it will rig every light it replaces, which will explode when the light is on. -// This is VERY noticable, even the device's name changes when you emag it so if anyone -// examines you when you're holding it in your hand, you will be discovered. -// It will also be very obvious who is setting all these lights off, since only Janitor Borgs and Janitors have easy -// access to them, and only one of them can emag their device. -// -// The explosion cannot insta-kill anyone with 30% or more health. - -#define LIGHT_OK 0 -#define LIGHT_EMPTY 1 -#define LIGHT_BROKEN 2 -#define LIGHT_BURNED 3 - - -/obj/item/device/lightreplacer - - name = "light replacer" - desc = "A device to automatically replace lights. Refill with working lightbulbs or sheets of glass." - force = 8 - icon = 'icons/obj/janitor.dmi' - icon_state = "lightreplacer0" - slot_flags = SLOT_BELT - origin_tech = list(TECH_MAGNET = 3, TECH_MATERIAL = 2) - - var/max_uses = 32 - var/uses = 32 - var/emagged = 0 - var/failmsg = "" - var/charge = 0 - var/selected_color = LIGHT_COLOR_INCANDESCENT_TUBE //Default color! - - // Eating used bulbs gives us bulb shards - var/bulb_shards = 0 - // when we get this many shards, we get a free bulb. - var/shards_required = 4 - -/obj/item/device/lightreplacer/New() - failmsg = "The [name]'s refill light blinks red." - ..() - -/obj/item/device/lightreplacer/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 2) - . += "It has [uses] lights remaining." - -/obj/item/device/lightreplacer/attackby(obj/item/W, mob/user) - if(istype(W, /obj/item/stack/material) && W.get_material_name() == "glass") - var/obj/item/stack/G = W - if(uses >= max_uses) - to_chat(user, "[src.name] is full.") - return - else if(G.use(1)) - add_uses(16) //Autolathe converts 1 sheet into 16 lights. - to_chat(user, "You insert a piece of glass into \the [src.name]. You have [uses] light\s remaining.") - return - else - to_chat(user, "You need one sheet of glass to replace lights.") - - if(istype(W, /obj/item/weapon/light)) - var/new_bulbs = 0 - var/obj/item/weapon/light/L = W - if(L.status == 0) // LIGHT OKAY - if(uses < max_uses) - if(!user.unEquip(W)) - return - add_uses(1) - qdel(L) - else - if(!user.unEquip(W)) - return - new_bulbs += AddShards(1) - qdel(L) - if(new_bulbs != 0) - playsound(src, 'sound/machines/ding.ogg', 50, 1) - to_chat(user, "You insert \the [L.name] into \the [src.name]. You have [uses] light\s remaining.") - return - - if(istype(W, /obj/item/weapon/storage)) - var/obj/item/weapon/storage/S = W - var/found_lightbulbs = FALSE - var/replaced_something = TRUE - - for(var/obj/item/I in S.contents) - if(istype(I,/obj/item/weapon/light)) - var/obj/item/weapon/light/L = I - found_lightbulbs = TRUE - if(src.uses >= max_uses) - break - if(L.status == LIGHT_OK) - replaced_something = TRUE - add_uses(1) - qdel(L) - - else if(L.status == LIGHT_BROKEN || L.status == LIGHT_BURNED) - replaced_something = TRUE - AddShards(1) - qdel(L) - - if(!found_lightbulbs) - to_chat(user, "\The [S] contains no bulbs.") - return - - if(!replaced_something && src.uses == max_uses) - to_chat(user, "\The [src] is full!") - return - - to_chat(user, "You fill \the [src] with lights from \the [S].") - -/obj/item/device/lightreplacer/attack_self(mob/user) - /* // This would probably be a bit OP. If you want it though, uncomment the code. - if(isrobot(user)) - var/mob/living/silicon/robot/R = user - if(R.emagged) - src.Emag() - to_chat(usr, You short circuit the [src].") - return - */ - to_chat(usr, "It has [uses] lights remaining.") - var/new_color = input(usr, "Choose a color to set the light to! (Default is [LIGHT_COLOR_INCANDESCENT_TUBE])", "", selected_color) as color|null - if(new_color) - selected_color = new_color - to_chat(usr, "The light color has been changed.") - -/obj/item/device/lightreplacer/update_icon() - icon_state = "lightreplacer[emagged]" - - -/obj/item/device/lightreplacer/proc/Use(var/mob/user) - - playsound(src, 'sound/machines/click.ogg', 50, 1) - add_uses(-1) - return 1 - -// Negative numbers will subtract -/obj/item/device/lightreplacer/proc/add_uses(var/amount = 1) - uses = min(max(uses + amount, 0), max_uses) - - -/obj/item/device/lightreplacer/proc/AddShards(amount = 1) - bulb_shards += amount - var/new_bulbs = round(bulb_shards / shards_required) - if(new_bulbs > 0) - add_uses(new_bulbs) - bulb_shards = bulb_shards % shards_required - return new_bulbs - -/obj/item/device/lightreplacer/proc/Charge(var/mob/user, var/amount = 1) - charge += amount - if(charge > 6) - add_uses(1) - charge = 0 - -/obj/item/device/lightreplacer/proc/ReplaceLight(var/obj/machinery/light/target, var/mob/living/U) - - if(target.status != LIGHT_OK) - if(CanUse(U)) - if(!Use(U)) return - to_chat(U, "You replace the [target.get_fitting_name()] with the [src].") - - if(target.status != LIGHT_EMPTY) - var/new_bulbs = AddShards(1) - if(new_bulbs != 0) - to_chat(U, "\The [src] has fabricated a new bulb from the broken bulbs it has stored. It now has [uses] uses.") - playsound(src, 'sound/machines/ding.ogg', 50, 1) - target.status = LIGHT_EMPTY - target.installed_light = null //Remove the light! - target.update() - - var/obj/item/weapon/light/L2 = new target.light_type() - L2.brightness_color = selected_color - target.insert_bulb(L2) //Call the insertion proc. - target.update() - - if(target.on && target.rigged) - target.explode() - return - - else - to_chat(U, failmsg) - return - else - to_chat(U, "There is a working [target.get_fitting_name()] already inserted.") - return - -/obj/item/device/lightreplacer/emag_act(var/remaining_charges, var/mob/user) - emagged = !emagged - playsound(src, "sparks", 100, 1) - update_icon() - return 1 - -//Can you use it? - -/obj/item/device/lightreplacer/proc/CanUse(var/mob/living/user) - src.add_fingerprint(user) - //Not sure what else to check for. Maybe if clumsy? - if(uses > 0) - return 1 - else - return 0 - -#undef LIGHT_OK -#undef LIGHT_EMPTY -#undef LIGHT_BROKEN + +// Light Replacer (LR) +// +// ABOUT THE DEVICE +// +// This is a device supposedly to be used by Janitors and Janitor Cyborgs which will +// allow them to easily replace lights. This was mostly designed for Janitor Cyborgs since +// they don't have hands or a way to replace lightbulbs. +// +// HOW IT WORKS +// +// You attack a light fixture with it, if the light fixture is broken it will replace the +// light fixture with a working light; the broken light is then placed on the floor for the +// user to then pickup with a trash bag. If it's empty then it will just place a light in the fixture. +// +// HOW TO REFILL THE DEVICE +// +// It can be manually refilled or by clicking on a storage item containing lights. +// If it's part of a robot module, it will charge when the Robot is inside a Recharge Station. +// +// EMAGGED FEATURES +// +// NOTICE: The Cyborg cannot use the emagged Light Replacer and the light's explosion was nerfed. It cannot create holes in the station anymore. +// +// I'm not sure everyone will react the emag's features so please say what your opinions are of it. +// +// When emagged it will rig every light it replaces, which will explode when the light is on. +// This is VERY noticable, even the device's name changes when you emag it so if anyone +// examines you when you're holding it in your hand, you will be discovered. +// It will also be very obvious who is setting all these lights off, since only Janitor Borgs and Janitors have easy +// access to them, and only one of them can emag their device. +// +// The explosion cannot insta-kill anyone with 30% or more health. + +#define LIGHT_OK 0 +#define LIGHT_EMPTY 1 +#define LIGHT_BROKEN 2 +#define LIGHT_BURNED 3 + + +/obj/item/device/lightreplacer + + name = "light replacer" + desc = "A device to automatically replace lights. Refill with working lightbulbs or sheets of glass." + force = 8 + icon = 'icons/obj/janitor.dmi' + icon_state = "lightreplacer0" + slot_flags = SLOT_BELT + origin_tech = list(TECH_MAGNET = 3, TECH_MATERIAL = 2) + + var/max_uses = 32 + var/uses = 32 + var/emagged = 0 + var/failmsg = "" + var/charge = 0 + var/selected_color = LIGHT_COLOR_INCANDESCENT_TUBE //Default color! + + // Eating used bulbs gives us bulb shards + var/bulb_shards = 0 + // when we get this many shards, we get a free bulb. + var/shards_required = 4 + +/obj/item/device/lightreplacer/New() + failmsg = "The [name]'s refill light blinks red." + ..() + +/obj/item/device/lightreplacer/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 2) + . += "It has [uses] lights remaining." + +/obj/item/device/lightreplacer/attackby(obj/item/W, mob/user) + if(istype(W, /obj/item/stack/material) && W.get_material_name() == "glass") + var/obj/item/stack/G = W + if(uses >= max_uses) + to_chat(user, "[src.name] is full.") + return + else if(G.use(1)) + add_uses(16) //Autolathe converts 1 sheet into 16 lights. + to_chat(user, "You insert a piece of glass into \the [src.name]. You have [uses] light\s remaining.") + return + else + to_chat(user, "You need one sheet of glass to replace lights.") + + if(istype(W, /obj/item/weapon/light)) + var/new_bulbs = 0 + var/obj/item/weapon/light/L = W + if(L.status == 0) // LIGHT OKAY + if(uses < max_uses) + if(!user.unEquip(W)) + return + add_uses(1) + qdel(L) + else + if(!user.unEquip(W)) + return + new_bulbs += AddShards(1) + qdel(L) + if(new_bulbs != 0) + playsound(src, 'sound/machines/ding.ogg', 50, 1) + to_chat(user, "You insert \the [L.name] into \the [src.name]. You have [uses] light\s remaining.") + return + + if(istype(W, /obj/item/weapon/storage)) + var/obj/item/weapon/storage/S = W + var/found_lightbulbs = FALSE + var/replaced_something = TRUE + + for(var/obj/item/I in S.contents) + if(istype(I,/obj/item/weapon/light)) + var/obj/item/weapon/light/L = I + found_lightbulbs = TRUE + if(src.uses >= max_uses) + break + if(L.status == LIGHT_OK) + replaced_something = TRUE + add_uses(1) + qdel(L) + + else if(L.status == LIGHT_BROKEN || L.status == LIGHT_BURNED) + replaced_something = TRUE + AddShards(1) + qdel(L) + + if(!found_lightbulbs) + to_chat(user, "\The [S] contains no bulbs.") + return + + if(!replaced_something && src.uses == max_uses) + to_chat(user, "\The [src] is full!") + return + + to_chat(user, "You fill \the [src] with lights from \the [S].") + +/obj/item/device/lightreplacer/attack_self(mob/user) + /* // This would probably be a bit OP. If you want it though, uncomment the code. + if(isrobot(user)) + var/mob/living/silicon/robot/R = user + if(R.emagged) + src.Emag() + to_chat(usr, You short circuit the [src].") + return + */ + to_chat(usr, "It has [uses] lights remaining.") + var/new_color = input(usr, "Choose a color to set the light to! (Default is [LIGHT_COLOR_INCANDESCENT_TUBE])", "", selected_color) as color|null + if(new_color) + selected_color = new_color + to_chat(usr, "The light color has been changed.") + +/obj/item/device/lightreplacer/update_icon() + icon_state = "lightreplacer[emagged]" + + +/obj/item/device/lightreplacer/proc/Use(var/mob/user) + + playsound(src, 'sound/machines/click.ogg', 50, 1) + add_uses(-1) + return 1 + +// Negative numbers will subtract +/obj/item/device/lightreplacer/proc/add_uses(var/amount = 1) + uses = min(max(uses + amount, 0), max_uses) + + +/obj/item/device/lightreplacer/proc/AddShards(amount = 1) + bulb_shards += amount + var/new_bulbs = round(bulb_shards / shards_required) + if(new_bulbs > 0) + add_uses(new_bulbs) + bulb_shards = bulb_shards % shards_required + return new_bulbs + +/obj/item/device/lightreplacer/proc/Charge(var/mob/user, var/amount = 1) + charge += amount + if(charge > 6) + add_uses(1) + charge = 0 + +/obj/item/device/lightreplacer/proc/ReplaceLight(var/obj/machinery/light/target, var/mob/living/U) + + if(target.status != LIGHT_OK) + if(CanUse(U)) + if(!Use(U)) return + to_chat(U, "You replace the [target.get_fitting_name()] with the [src].") + + if(target.status != LIGHT_EMPTY) + var/new_bulbs = AddShards(1) + if(new_bulbs != 0) + to_chat(U, "\The [src] has fabricated a new bulb from the broken bulbs it has stored. It now has [uses] uses.") + playsound(src, 'sound/machines/ding.ogg', 50, 1) + target.status = LIGHT_EMPTY + target.installed_light = null //Remove the light! + target.update() + + var/obj/item/weapon/light/L2 = new target.light_type() + L2.brightness_color = selected_color + target.insert_bulb(L2) //Call the insertion proc. + target.update() + + if(target.on && target.rigged) + target.explode() + return + + else + to_chat(U, failmsg) + return + else + to_chat(U, "There is a working [target.get_fitting_name()] already inserted.") + return + +/obj/item/device/lightreplacer/emag_act(var/remaining_charges, var/mob/user) + emagged = !emagged + playsound(src, "sparks", 100, 1) + update_icon() + return 1 + +//Can you use it? + +/obj/item/device/lightreplacer/proc/CanUse(var/mob/living/user) + src.add_fingerprint(user) + //Not sure what else to check for. Maybe if clumsy? + if(uses > 0) + return 1 + else + return 0 + +#undef LIGHT_OK +#undef LIGHT_EMPTY +#undef LIGHT_BROKEN #undef LIGHT_BURNED \ No newline at end of file diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm index 4a249cae009..a320c6b490a 100644 --- a/code/game/objects/items/devices/multitool.dm +++ b/code/game/objects/items/devices/multitool.dm @@ -1,95 +1,95 @@ -/** - * Multitool -- A multitool is used for hacking electronic devices. - * TO-DO -- Using it as a power measurement tool for cables etc. Nannek. - * - */ - -/obj/item/device/multitool - name = "multitool" - desc = "Used for pulsing wires to test which to cut. Not recommended by doctors." - description_info = "You can use this on airlocks or APCs to try to hack them without cutting wires." - icon_state = "multitool" - force = 5.0 - w_class = ITEMSIZE_SMALL - throwforce = 5.0 - throw_range = 15 - throw_speed = 3 - drop_sound = 'sound/items/drop/multitool.ogg' - pickup_sound = 'sound/items/pickup/multitool.ogg' - - matter = list(MAT_STEEL = 50,MAT_GLASS = 20) - - var/mode_index = 1 - var/toolmode = MULTITOOL_MODE_STANDARD - var/list/modes = list(MULTITOOL_MODE_STANDARD, MULTITOOL_MODE_INTCIRCUITS) - - origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) - var/obj/machinery/telecomms/buffer // simple machine buffer for device linkage - var/obj/machinery/clonepod/connecting //same for cryopod linkage - var/obj/machinery/connectable //Used to connect machinery. - var/weakref_wiring //Used to store weak references for integrated circuitry. This is now the Omnitool. - toolspeed = 1 - tool_qualities = list(TOOL_MULTITOOL) - -/obj/item/device/multitool/attack_self(mob/living/user) - var/choice = tgui_alert(usr, "What do you want to do with \the [src]?", "Multitool Menu", list("Switch Mode", "Clear Buffers", "Cancel")) - switch(choice) - if("Cancel") - to_chat(user,"You lower \the [src].") - return - if("Clear Buffers") - to_chat(user,"You clear \the [src]'s memory.") - buffer = null - connecting = null - connectable = null - weakref_wiring = null - accepting_refs = 0 - if(toolmode == MULTITOOL_MODE_INTCIRCUITS) - accepting_refs = 1 - if("Switch Mode") - mode_switch(user) - - update_icon() - - return ..() - -/obj/item/device/multitool/proc/mode_switch(mob/living/user) - if(mode_index + 1 > modes.len) mode_index = 1 - - else - mode_index += 1 - - toolmode = modes[mode_index] - to_chat(user,"\The [src] is now set to [toolmode].") - - accepting_refs = (toolmode == MULTITOOL_MODE_INTCIRCUITS) - - return - -/obj/item/device/multitool/cyborg - name = "multitool" - desc = "Optimised and stripped-down version of a regular multitool." - toolspeed = 0.5 - - - -/datum/category_item/catalogue/anomalous/precursor_a/alien_multitool - name = "Precursor Alpha Object - Pulse Tool" - desc = "This ancient object appears to be an electrical tool. \ - It has a simple mechanism at the handle, which will cause a pulse of \ - energy to be emitted from the head of the tool. This can be used on a \ - conductive object such as a wire, in order to send a pulse signal through it.\ -

                    \ - These qualities make this object somewhat similar in purpose to the common \ - multitool, and can probably be used for tasks such as direct interfacing with \ - an airlock, if one knows how." - value = CATALOGUER_REWARD_EASY - -/obj/item/device/multitool/alien - name = "alien multitool" - desc = "An omni-technological interface." - catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_multitool) - icon = 'icons/obj/abductor.dmi' - icon_state = "multitool" - toolspeed = 0.1 - origin_tech = list(TECH_MAGNET = 5, TECH_ENGINEERING = 5) +/** + * Multitool -- A multitool is used for hacking electronic devices. + * TO-DO -- Using it as a power measurement tool for cables etc. Nannek. + * + */ + +/obj/item/device/multitool + name = "multitool" + desc = "Used for pulsing wires to test which to cut. Not recommended by doctors." + description_info = "You can use this on airlocks or APCs to try to hack them without cutting wires." + icon_state = "multitool" + force = 5.0 + w_class = ITEMSIZE_SMALL + throwforce = 5.0 + throw_range = 15 + throw_speed = 3 + drop_sound = 'sound/items/drop/multitool.ogg' + pickup_sound = 'sound/items/pickup/multitool.ogg' + + matter = list(MAT_STEEL = 50,MAT_GLASS = 20) + + var/mode_index = 1 + var/toolmode = MULTITOOL_MODE_STANDARD + var/list/modes = list(MULTITOOL_MODE_STANDARD, MULTITOOL_MODE_INTCIRCUITS) + + origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) + var/obj/machinery/telecomms/buffer // simple machine buffer for device linkage + var/obj/machinery/clonepod/connecting //same for cryopod linkage + var/obj/machinery/connectable //Used to connect machinery. + var/weakref_wiring //Used to store weak references for integrated circuitry. This is now the Omnitool. + toolspeed = 1 + tool_qualities = list(TOOL_MULTITOOL) + +/obj/item/device/multitool/attack_self(mob/living/user) + var/choice = tgui_alert(usr, "What do you want to do with \the [src]?", "Multitool Menu", list("Switch Mode", "Clear Buffers", "Cancel")) + switch(choice) + if("Cancel") + to_chat(user,"You lower \the [src].") + return + if("Clear Buffers") + to_chat(user,"You clear \the [src]'s memory.") + buffer = null + connecting = null + connectable = null + weakref_wiring = null + accepting_refs = 0 + if(toolmode == MULTITOOL_MODE_INTCIRCUITS) + accepting_refs = 1 + if("Switch Mode") + mode_switch(user) + + update_icon() + + return ..() + +/obj/item/device/multitool/proc/mode_switch(mob/living/user) + if(mode_index + 1 > modes.len) mode_index = 1 + + else + mode_index += 1 + + toolmode = modes[mode_index] + to_chat(user,"\The [src] is now set to [toolmode].") + + accepting_refs = (toolmode == MULTITOOL_MODE_INTCIRCUITS) + + return + +/obj/item/device/multitool/cyborg + name = "multitool" + desc = "Optimised and stripped-down version of a regular multitool." + toolspeed = 0.5 + + + +/datum/category_item/catalogue/anomalous/precursor_a/alien_multitool + name = "Precursor Alpha Object - Pulse Tool" + desc = "This ancient object appears to be an electrical tool. \ + It has a simple mechanism at the handle, which will cause a pulse of \ + energy to be emitted from the head of the tool. This can be used on a \ + conductive object such as a wire, in order to send a pulse signal through it.\ +

                    \ + These qualities make this object somewhat similar in purpose to the common \ + multitool, and can probably be used for tasks such as direct interfacing with \ + an airlock, if one knows how." + value = CATALOGUER_REWARD_EASY + +/obj/item/device/multitool/alien + name = "alien multitool" + desc = "An omni-technological interface." + catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_multitool) + icon = 'icons/obj/abductor.dmi' + icon_state = "multitool" + toolspeed = 0.1 + origin_tech = list(TECH_MAGNET = 5, TECH_ENGINEERING = 5) diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index 3e07548542f..27299df56e2 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -1,537 +1,537 @@ -var/global/list/radio_channels_by_freq = list( - num2text(PUB_FREQ) = "Common", - num2text(AI_FREQ) = "AI Private", - num2text(ENT_FREQ) = "Entertainment", - num2text(ERT_FREQ) = "Response Team", - num2text(COMM_FREQ)= "Command", - num2text(ENG_FREQ) = "Engineering", - num2text(MED_FREQ) = "Medical", - num2text(MED_I_FREQ)="Medical(I)", - num2text(SEC_FREQ) = "Security", - num2text(SEC_I_FREQ)="Security(I)", - num2text(SCI_FREQ) = "Science", - num2text(SUP_FREQ) = "Supply", - num2text(SRV_FREQ) = "Service", - num2text(EXP_FREQ) = "Away Team" - ) - -GLOBAL_LIST_BOILERPLATE(all_pai_cards, /obj/item/device/paicard) - -/obj/item/device/paicard - name = "personal AI device" - icon = 'icons/obj/pda.dmi' - icon_state = "pai" - item_state = "electronic" - w_class = ITEMSIZE_SMALL - slot_flags = SLOT_BELT | SLOT_HOLSTER - origin_tech = list(TECH_DATA = 2) - show_messages = 0 - preserve_item = 1 - - var/obj/item/device/radio/borg/pai/radio - var/looking_for_personality = 0 - var/mob/living/silicon/pai/pai - var/image/screen_layer - var/screen_color = "#00ff0d" - var/last_notify = 0 - var/screen_msg - -/obj/item/device/paicard/relaymove(var/mob/user, var/direction) - if(user.stat || user.stunned) - return - var/obj/item/weapon/rig/rig = src.get_rig() - if(istype(rig)) - rig.forced_move(direction, user) - -/obj/item/device/paicard/New() - ..() - add_overlay("pai-off") - -/obj/item/device/paicard/Destroy() - //Will stop people throwing friend pAIs into the singularity so they can respawn - if(!isnull(pai)) - pai.death(0) - QDEL_NULL(radio) - return ..() - -// VOREStation Edit - Allow everyone to become a pAI -/obj/item/device/paicard/attack_ghost(mob/user as mob) - if(pai != null) //Have a person in them already? - return ..() - if(is_damage_critical()) - to_chat(usr, "That card is too damaged to activate!") - return - var/time_till_respawn = user.time_till_respawn() - if(time_till_respawn == -1) // Special case, never allowed to respawn - to_chat(usr, "Respawning is not allowed!") - else if(time_till_respawn) // Nonzero time to respawn - to_chat(usr, "You can't do that yet! You died too recently. You need to wait another [round(time_till_respawn/10/60, 0.1)] minutes.") - return - if(jobban_isbanned(usr, "pAI")) - to_chat(usr,"You cannot join a pAI card when you are banned from playing as a pAI.") - return - - for(var/ourkey in paikeys) - if(ourkey == user.ckey) - to_chat(usr, "You can't just rejoin any old pAI card!!! Your card still exists.") - return - - var/choice = tgui_alert(user, "You sure you want to inhabit this PAI, or submit yourself to being recruited?", "Confirmation", list("Inhabit", "Recruit", "Cancel")) - if(choice == "Cancel") - return ..() - if(choice == "Recruit") - paiController.recruitWindow(user) - return ..() - choice = tgui_alert(user, "Do you want to load your pAI data?", "Load", list("Yes", "No")) - var/actual_pai_name - var/turf/location = get_turf(src) - if(choice == "No") - var/pai_name = tgui_input_text(user, "Choose your character's name", "Character Name") - actual_pai_name = sanitize_name(pai_name, ,1) - if(isnull(actual_pai_name)) - return ..() - if(istype(src , /obj/item/device/paicard/typeb)) - var/obj/item/device/paicard/typeb/card = new(location) - var/mob/living/silicon/pai/new_pai = new(card) - new_pai.key = user.key - paikeys |= new_pai.ckey - card.setPersonality(new_pai) - new_pai.SetName(actual_pai_name) - else - var/obj/item/device/paicard/card = new(location) - var/mob/living/silicon/pai/new_pai = new(card) - new_pai.key = user.key - paikeys |= new_pai.ckey - card.setPersonality(new_pai) - new_pai.SetName(actual_pai_name) - - if(choice == "Yes") - if(istype(src , /obj/item/device/paicard/typeb)) - var/obj/item/device/paicard/typeb/card = new(location) - var/mob/living/silicon/pai/new_pai = new(card) - new_pai.key = user.key - paikeys |= new_pai.ckey - card.setPersonality(new_pai) - if(!new_pai.savefile_load(new_pai)) - var/pai_name = tgui_input_text(new_pai, "Choose your character's name", "Character Name") - actual_pai_name = sanitize_name(pai_name, ,1) - if(isnull(actual_pai_name)) - return ..() - else - var/obj/item/device/paicard/card = new(location) - var/mob/living/silicon/pai/new_pai = new(card) - new_pai.key = user.key - paikeys |= new_pai.ckey - card.setPersonality(new_pai) - if(!new_pai.savefile_load(new_pai)) - var/pai_name = tgui_input_text(new_pai, "Choose your character's name", "Character Name") - actual_pai_name = sanitize_name(pai_name, ,1) - if(isnull(actual_pai_name)) - return ..() - - qdel(src) - return ..() - -// VOREStation Edit End - -/obj/item/device/paicard/proc/access_screen(mob/user) - if(is_damage_critical()) - to_chat(user, "WARNING: CRITICAL HARDWARE FAILURE, SERVICE DEVICE IMMEDIATELY") - return - if (!in_range(src, user)) - return - user.set_machine(src) - var/dat = {" - - - - - - - "} - - if(pai) - dat += {" - Personal AI Device

                    - - - - - - - - - - - - - - - -
                    [pai.name]
                    Integrity: [pai.health]
                    Prime directive:[pai.pai_law0]
                    Additional directives:[pai.pai_laws]
                    -
                    - "} - dat += {" - - -
                    - Configure Directives -
                    - "} - if(pai && (!pai.master_dna || !pai.master)) - dat += {" - - -
                    - Imprint Master DNA -
                    - "} - dat += "
                    " - if(radio) - dat += "Radio Uplink" - dat += {" - - - - - - - - - -
                    Transmit:[radio.broadcasting ? "En" : "Dis" ]abled - -
                    Receive:[radio.listening ? "En" : "Dis" ]abled - -
                    -
                    - "} - else // - dat += "Radio Uplink
                    " - dat += "Radio firmware not loaded. Please install a pAI personality to load firmware.
                    " - /* - //A button for instantly deleting people from the game is lame, especially considering that pAIs on our server tend to activate without a master. - dat += {" - - -
                    Wipe current pAI personality - -
                    - "} - */ - if(screen_msg) - dat += "Message from [pai.name]
                    [screen_msg]" - else - if(looking_for_personality) - dat += {" - pAI Request Module

                    -

                    Requesting AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.

                    - Searching for personalities

                    - - - - - -
                    - Refresh available personalities -

                    - "} - else - dat += {" - pAI Request Module

                    -

                    No personality is installed.

                    - - - - -
                    Request personality -
                    -
                    -

                    Each time this button is pressed, a request will be sent out to any available personalities. Check back often give plenty of time for personalities to respond. This process could take anywhere from 15 seconds to several minutes, depending on the available personalities' timeliness.

                    - "} - user << browse(dat, "window=paicard") - onclose(user, "paicard") - return - -/obj/item/device/paicard/Topic(href, href_list) - - if(!usr || usr.stat) - return - - if(href_list["setdna"]) - if(pai.master_dna) - return - var/mob/M = usr - if(!istype(M, /mob/living/carbon)) - to_chat(usr, span_blue("You don't have any DNA, or your DNA is incompatible with this device.")) - else - var/datum/dna/dna = usr.dna - pai.master = M.real_name - pai.master_dna = dna.unique_enzymes - to_chat(pai, span_red("

                    You have been bound to a new master.

                    ")) - if(href_list["request"]) - src.looking_for_personality = 1 - paiController.findPAI(src, usr) - if(href_list["wipe"]) - var/confirm = tgui_alert(usr, "Are you CERTAIN you wish to delete the current personality? This action cannot be undone.", "Personality Wipe", list("Yes", "No")) - if(confirm == "Yes") - for(var/mob/M in src) - to_chat(M, "

                    You feel yourself slipping away from reality.

                    ") - to_chat(M, "

                    Byte by byte you lose your sense of self.

                    ") - to_chat(M, "

                    Your mental faculties leave you.

                    ") - to_chat(M, "
                    oblivion...
                    ") - M.death(0) - removePersonality() - if(href_list["wires"]) - var/t1 = text2num(href_list["wires"]) - switch(t1) - if(4) - radio.ToggleBroadcast() - if(2) - radio.ToggleReception() - if(href_list["setlaws"]) - var/newlaws = sanitize(tgui_input_text(usr, "Enter any additional directives you would like your pAI personality to follow. Note that these directives will not override the personality's allegiance to its imprinted master. Conflicting directives will be ignored.", "pAI Directive Configuration", pai.pai_laws, multiline = TRUE, prevent_enter = TRUE)) - if(newlaws) - pai.pai_laws = newlaws - to_chat(pai, "Your supplemental directives have been updated. Your new directives are:") - to_chat(pai, "Prime Directive:
                    [pai.pai_law0]") - to_chat(pai, "Supplemental Directives:
                    [pai.pai_laws]") - attack_self(usr) - -// WIRE_SIGNAL = 1 -// WIRE_RECEIVE = 2 -// WIRE_TRANSMIT = 4 - -/obj/item/device/paicard/proc/setPersonality(mob/living/silicon/pai/personality) - src.pai = personality - setEmotion(1) - -/obj/item/device/paicard/proc/removePersonality() - src.pai = null - cut_overlays() - setEmotion(16) - -/obj/item/device/paicard - var/current_emotion = 1 -/obj/item/device/paicard/proc/setEmotion(var/emotion) - if(pai) - cut_overlays() - qdel(screen_layer) - screen_layer = null - switch(emotion) - if(1) screen_layer = image(icon, "pai-neutral") - if(2) screen_layer = image(icon, "pai-what") - if(3) screen_layer = image(icon, "pai-happy") - if(4) screen_layer = image(icon, "pai-cat") - if(5) screen_layer = image(icon, "pai-extremely-happy") - if(6) screen_layer = image(icon, "pai-face") - if(7) screen_layer = image(icon, "pai-laugh") - if(8) screen_layer = image(icon, "pai-sad") - if(9) screen_layer = image(icon, "pai-angry") - if(10) screen_layer = image(icon, "pai-silly") - if(11) screen_layer = image(icon, "pai-nose") - if(12) screen_layer = image(icon, "pai-smirk") - if(13) screen_layer = image(icon, "pai-exclamation") - if(14) screen_layer = image(icon, "pai-question") - if(15) screen_layer = image(icon, "pai-blank") - if(16) screen_layer = image(icon, "pai-off") - - screen_layer.color = pai.eye_color - add_overlay(screen_layer) - current_emotion = emotion - -/obj/item/device/paicard/proc/alertUpdate() - if(pai) - return - if(last_notify == 0 || (5 MINUTES <= world.time - last_notify)) - audible_message("\The [src] flashes a message across its screen, \"Additional personalities available for download.\"", hearing_distance = world.view, runemessage = "bleeps!") - last_notify = world.time - -/obj/item/device/paicard/emp_act(severity) - for(var/mob/M in src) - M.emp_act(severity) - -/obj/item/device/paicard/ex_act(severity) - if(pai) - pai.ex_act(severity) - else - qdel(src) - -/obj/item/device/paicard/see_emote(mob/living/M, text) - if(pai && pai.client && !pai.canmove) - var/rendered = "[text]" - pai.show_message(rendered, 2) - ..() - -/obj/item/device/paicard/show_message(msg, type, alt, alt_type) - if(pai && pai.client) - var/rendered = "[msg]" - pai.show_message(rendered, type) - ..() - - -// VoreEdit: Living Machine Stuff after this. -// This adds a var and proc for all machines to take a pAI. (The pAI can't control anything, it's just for RP.) -// You need to add usage of the proc to each machine to actually add support. For an example of this, see code\modules\food\kitchen\microwave.dm -/obj/machinery - var/obj/item/device/paicard/paicard = null - -/obj/machinery/proc/insertpai(mob/user, obj/item/device/paicard/card) - //var/obj/item/paicard/card = I - var/mob/living/silicon/pai/AI = card.pai - if(paicard) - to_chat(user, span_notice("This bot is already under PAI Control!")) - return - if(!istype(card)) // TODO: Add sleevecard support. - return - if(!card.pai) - to_chat(user, span_notice("This card does not currently have a personality!")) - return - paicard = card - user.unEquip(card) - card.forceMove(src) - AI.client.eye = src - to_chat(AI, span_notice("Your location is [card.loc].")) // DEBUG. TODO: Make unfolding the chassis trigger an eject. - name = AI.name - to_chat(AI, span_notice("You feel a tingle in your circuits as your systems interface with \the [initial(src.name)].")) - -/obj/machinery/proc/ejectpai(mob/user) - if(paicard) - var/mob/living/silicon/pai/AI = paicard.pai - paicard.forceMove(src.loc) - AI.client.eye = AI - paicard = null - name = initial(src.name) - to_chat(AI, span_notice("You feel a tad claustrophobic as your mind closes back into your card, ejecting from \the [initial(src.name)].")) - if(user) - to_chat(user, span_notice("You eject the card from \the [initial(src.name)].")) - -/////////////////////////////// -//////////pAI Radios////////// -/////////////////////////////// -//Thanks heroman! - -/obj/item/device/radio/borg/pai - name = "integrated radio" - icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component. - icon_state = "radio" - loudspeaker = FALSE - -/obj/item/device/radio/borg/pai/attackby(obj/item/weapon/W as obj, mob/user as mob) - return - -/obj/item/device/radio/borg/pai/recalculateChannels() - if(!istype(loc,/obj/item/device/paicard)) - return - var/obj/item/device/paicard/card = loc - secure_radio_connections = list() - channels = list() - - for(var/internal_chan in internal_channels) - var/ch_name = radio_channels_by_freq[internal_chan] - if(has_channel_access(card.pai, internal_chan)) - channels += ch_name - channels[ch_name] = 1 - secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) - -/obj/item/device/paicard/typeb - name = "personal AI device" - icon = 'icons/obj/paicard.dmi' - -/obj/random/paicard - name = "personal AI device spawner" - icon = 'icons/obj/paicard.dmi' - icon_state = "pai" - -/obj/random/paicard/item_to_spawn() - return pick(/obj/item/device/paicard ,/obj/item/device/paicard/typeb) - -/obj/item/device/paicard/digest_act(var/atom/movable/item_storage = null) - if(pai.digestable) - return ..() +var/global/list/radio_channels_by_freq = list( + num2text(PUB_FREQ) = "Common", + num2text(AI_FREQ) = "AI Private", + num2text(ENT_FREQ) = "Entertainment", + num2text(ERT_FREQ) = "Response Team", + num2text(COMM_FREQ)= "Command", + num2text(ENG_FREQ) = "Engineering", + num2text(MED_FREQ) = "Medical", + num2text(MED_I_FREQ)="Medical(I)", + num2text(SEC_FREQ) = "Security", + num2text(SEC_I_FREQ)="Security(I)", + num2text(SCI_FREQ) = "Science", + num2text(SUP_FREQ) = "Supply", + num2text(SRV_FREQ) = "Service", + num2text(EXP_FREQ) = "Away Team" + ) + +GLOBAL_LIST_BOILERPLATE(all_pai_cards, /obj/item/device/paicard) + +/obj/item/device/paicard + name = "personal AI device" + icon = 'icons/obj/pda.dmi' + icon_state = "pai" + item_state = "electronic" + w_class = ITEMSIZE_SMALL + slot_flags = SLOT_BELT | SLOT_HOLSTER + origin_tech = list(TECH_DATA = 2) + show_messages = 0 + preserve_item = 1 + + var/obj/item/device/radio/borg/pai/radio + var/looking_for_personality = 0 + var/mob/living/silicon/pai/pai + var/image/screen_layer + var/screen_color = "#00ff0d" + var/last_notify = 0 + var/screen_msg + +/obj/item/device/paicard/relaymove(var/mob/user, var/direction) + if(user.stat || user.stunned) + return + var/obj/item/weapon/rig/rig = src.get_rig() + if(istype(rig)) + rig.forced_move(direction, user) + +/obj/item/device/paicard/New() + ..() + add_overlay("pai-off") + +/obj/item/device/paicard/Destroy() + //Will stop people throwing friend pAIs into the singularity so they can respawn + if(!isnull(pai)) + pai.death(0) + QDEL_NULL(radio) + return ..() + +// VOREStation Edit - Allow everyone to become a pAI +/obj/item/device/paicard/attack_ghost(mob/user as mob) + if(pai != null) //Have a person in them already? + return ..() + if(is_damage_critical()) + to_chat(usr, "That card is too damaged to activate!") + return + var/time_till_respawn = user.time_till_respawn() + if(time_till_respawn == -1) // Special case, never allowed to respawn + to_chat(usr, "Respawning is not allowed!") + else if(time_till_respawn) // Nonzero time to respawn + to_chat(usr, "You can't do that yet! You died too recently. You need to wait another [round(time_till_respawn/10/60, 0.1)] minutes.") + return + if(jobban_isbanned(usr, "pAI")) + to_chat(usr,"You cannot join a pAI card when you are banned from playing as a pAI.") + return + + for(var/ourkey in paikeys) + if(ourkey == user.ckey) + to_chat(usr, "You can't just rejoin any old pAI card!!! Your card still exists.") + return + + var/choice = tgui_alert(user, "You sure you want to inhabit this PAI, or submit yourself to being recruited?", "Confirmation", list("Inhabit", "Recruit", "Cancel")) + if(choice == "Cancel") + return ..() + if(choice == "Recruit") + paiController.recruitWindow(user) + return ..() + choice = tgui_alert(user, "Do you want to load your pAI data?", "Load", list("Yes", "No")) + var/actual_pai_name + var/turf/location = get_turf(src) + if(choice == "No") + var/pai_name = tgui_input_text(user, "Choose your character's name", "Character Name") + actual_pai_name = sanitize_name(pai_name, ,1) + if(isnull(actual_pai_name)) + return ..() + if(istype(src , /obj/item/device/paicard/typeb)) + var/obj/item/device/paicard/typeb/card = new(location) + var/mob/living/silicon/pai/new_pai = new(card) + new_pai.key = user.key + paikeys |= new_pai.ckey + card.setPersonality(new_pai) + new_pai.SetName(actual_pai_name) + else + var/obj/item/device/paicard/card = new(location) + var/mob/living/silicon/pai/new_pai = new(card) + new_pai.key = user.key + paikeys |= new_pai.ckey + card.setPersonality(new_pai) + new_pai.SetName(actual_pai_name) + + if(choice == "Yes") + if(istype(src , /obj/item/device/paicard/typeb)) + var/obj/item/device/paicard/typeb/card = new(location) + var/mob/living/silicon/pai/new_pai = new(card) + new_pai.key = user.key + paikeys |= new_pai.ckey + card.setPersonality(new_pai) + if(!new_pai.savefile_load(new_pai)) + var/pai_name = tgui_input_text(new_pai, "Choose your character's name", "Character Name") + actual_pai_name = sanitize_name(pai_name, ,1) + if(isnull(actual_pai_name)) + return ..() + else + var/obj/item/device/paicard/card = new(location) + var/mob/living/silicon/pai/new_pai = new(card) + new_pai.key = user.key + paikeys |= new_pai.ckey + card.setPersonality(new_pai) + if(!new_pai.savefile_load(new_pai)) + var/pai_name = tgui_input_text(new_pai, "Choose your character's name", "Character Name") + actual_pai_name = sanitize_name(pai_name, ,1) + if(isnull(actual_pai_name)) + return ..() + + qdel(src) + return ..() + +// VOREStation Edit End + +/obj/item/device/paicard/proc/access_screen(mob/user) + if(is_damage_critical()) + to_chat(user, "WARNING: CRITICAL HARDWARE FAILURE, SERVICE DEVICE IMMEDIATELY") + return + if (!in_range(src, user)) + return + user.set_machine(src) + var/dat = {" + + + + + + + "} + + if(pai) + dat += {" + Personal AI Device

                    + + + + + + + + + + + + + + + +
                    [pai.name]
                    Integrity: [pai.health]
                    Prime directive:[pai.pai_law0]
                    Additional directives:[pai.pai_laws]
                    +
                    + "} + dat += {" + + +
                    + Configure Directives +
                    + "} + if(pai && (!pai.master_dna || !pai.master)) + dat += {" + + +
                    + Imprint Master DNA +
                    + "} + dat += "
                    " + if(radio) + dat += "Radio Uplink" + dat += {" + + + + + + + + + +
                    Transmit:[radio.broadcasting ? "En" : "Dis" ]abled + +
                    Receive:[radio.listening ? "En" : "Dis" ]abled + +
                    +
                    + "} + else // + dat += "Radio Uplink
                    " + dat += "Radio firmware not loaded. Please install a pAI personality to load firmware.
                    " + /* - //A button for instantly deleting people from the game is lame, especially considering that pAIs on our server tend to activate without a master. + dat += {" + + +
                    Wipe current pAI personality + +
                    + "} + */ + if(screen_msg) + dat += "Message from [pai.name]
                    [screen_msg]" + else + if(looking_for_personality) + dat += {" + pAI Request Module

                    +

                    Requesting AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.

                    + Searching for personalities

                    + + + + + +
                    + Refresh available personalities +

                    + "} + else + dat += {" + pAI Request Module

                    +

                    No personality is installed.

                    + + + + +
                    Request personality +
                    +
                    +

                    Each time this button is pressed, a request will be sent out to any available personalities. Check back often give plenty of time for personalities to respond. This process could take anywhere from 15 seconds to several minutes, depending on the available personalities' timeliness.

                    + "} + user << browse(dat, "window=paicard") + onclose(user, "paicard") + return + +/obj/item/device/paicard/Topic(href, href_list) + + if(!usr || usr.stat) + return + + if(href_list["setdna"]) + if(pai.master_dna) + return + var/mob/M = usr + if(!istype(M, /mob/living/carbon)) + to_chat(usr, span_blue("You don't have any DNA, or your DNA is incompatible with this device.")) + else + var/datum/dna/dna = usr.dna + pai.master = M.real_name + pai.master_dna = dna.unique_enzymes + to_chat(pai, span_red("

                    You have been bound to a new master.

                    ")) + if(href_list["request"]) + src.looking_for_personality = 1 + paiController.findPAI(src, usr) + if(href_list["wipe"]) + var/confirm = tgui_alert(usr, "Are you CERTAIN you wish to delete the current personality? This action cannot be undone.", "Personality Wipe", list("Yes", "No")) + if(confirm == "Yes") + for(var/mob/M in src) + to_chat(M, "

                    You feel yourself slipping away from reality.

                    ") + to_chat(M, "

                    Byte by byte you lose your sense of self.

                    ") + to_chat(M, "

                    Your mental faculties leave you.

                    ") + to_chat(M, "
                    oblivion...
                    ") + M.death(0) + removePersonality() + if(href_list["wires"]) + var/t1 = text2num(href_list["wires"]) + switch(t1) + if(4) + radio.ToggleBroadcast() + if(2) + radio.ToggleReception() + if(href_list["setlaws"]) + var/newlaws = sanitize(tgui_input_text(usr, "Enter any additional directives you would like your pAI personality to follow. Note that these directives will not override the personality's allegiance to its imprinted master. Conflicting directives will be ignored.", "pAI Directive Configuration", pai.pai_laws, multiline = TRUE, prevent_enter = TRUE)) + if(newlaws) + pai.pai_laws = newlaws + to_chat(pai, "Your supplemental directives have been updated. Your new directives are:") + to_chat(pai, "Prime Directive:
                    [pai.pai_law0]") + to_chat(pai, "Supplemental Directives:
                    [pai.pai_laws]") + attack_self(usr) + +// WIRE_SIGNAL = 1 +// WIRE_RECEIVE = 2 +// WIRE_TRANSMIT = 4 + +/obj/item/device/paicard/proc/setPersonality(mob/living/silicon/pai/personality) + src.pai = personality + setEmotion(1) + +/obj/item/device/paicard/proc/removePersonality() + src.pai = null + cut_overlays() + setEmotion(16) + +/obj/item/device/paicard + var/current_emotion = 1 +/obj/item/device/paicard/proc/setEmotion(var/emotion) + if(pai) + cut_overlays() + qdel(screen_layer) + screen_layer = null + switch(emotion) + if(1) screen_layer = image(icon, "pai-neutral") + if(2) screen_layer = image(icon, "pai-what") + if(3) screen_layer = image(icon, "pai-happy") + if(4) screen_layer = image(icon, "pai-cat") + if(5) screen_layer = image(icon, "pai-extremely-happy") + if(6) screen_layer = image(icon, "pai-face") + if(7) screen_layer = image(icon, "pai-laugh") + if(8) screen_layer = image(icon, "pai-sad") + if(9) screen_layer = image(icon, "pai-angry") + if(10) screen_layer = image(icon, "pai-silly") + if(11) screen_layer = image(icon, "pai-nose") + if(12) screen_layer = image(icon, "pai-smirk") + if(13) screen_layer = image(icon, "pai-exclamation") + if(14) screen_layer = image(icon, "pai-question") + if(15) screen_layer = image(icon, "pai-blank") + if(16) screen_layer = image(icon, "pai-off") + + screen_layer.color = pai.eye_color + add_overlay(screen_layer) + current_emotion = emotion + +/obj/item/device/paicard/proc/alertUpdate() + if(pai) + return + if(last_notify == 0 || (5 MINUTES <= world.time - last_notify)) + audible_message("\The [src] flashes a message across its screen, \"Additional personalities available for download.\"", hearing_distance = world.view, runemessage = "bleeps!") + last_notify = world.time + +/obj/item/device/paicard/emp_act(severity) + for(var/mob/M in src) + M.emp_act(severity) + +/obj/item/device/paicard/ex_act(severity) + if(pai) + pai.ex_act(severity) + else + qdel(src) + +/obj/item/device/paicard/see_emote(mob/living/M, text) + if(pai && pai.client && !pai.canmove) + var/rendered = "[text]" + pai.show_message(rendered, 2) + ..() + +/obj/item/device/paicard/show_message(msg, type, alt, alt_type) + if(pai && pai.client) + var/rendered = "[msg]" + pai.show_message(rendered, type) + ..() + + +// VoreEdit: Living Machine Stuff after this. +// This adds a var and proc for all machines to take a pAI. (The pAI can't control anything, it's just for RP.) +// You need to add usage of the proc to each machine to actually add support. For an example of this, see code\modules\food\kitchen\microwave.dm +/obj/machinery + var/obj/item/device/paicard/paicard = null + +/obj/machinery/proc/insertpai(mob/user, obj/item/device/paicard/card) + //var/obj/item/paicard/card = I + var/mob/living/silicon/pai/AI = card.pai + if(paicard) + to_chat(user, span_notice("This bot is already under PAI Control!")) + return + if(!istype(card)) // TODO: Add sleevecard support. + return + if(!card.pai) + to_chat(user, span_notice("This card does not currently have a personality!")) + return + paicard = card + user.unEquip(card) + card.forceMove(src) + AI.client.eye = src + to_chat(AI, span_notice("Your location is [card.loc].")) // DEBUG. TODO: Make unfolding the chassis trigger an eject. + name = AI.name + to_chat(AI, span_notice("You feel a tingle in your circuits as your systems interface with \the [initial(src.name)].")) + +/obj/machinery/proc/ejectpai(mob/user) + if(paicard) + var/mob/living/silicon/pai/AI = paicard.pai + paicard.forceMove(src.loc) + AI.client.eye = AI + paicard = null + name = initial(src.name) + to_chat(AI, span_notice("You feel a tad claustrophobic as your mind closes back into your card, ejecting from \the [initial(src.name)].")) + if(user) + to_chat(user, span_notice("You eject the card from \the [initial(src.name)].")) + +/////////////////////////////// +//////////pAI Radios////////// +/////////////////////////////// +//Thanks heroman! + +/obj/item/device/radio/borg/pai + name = "integrated radio" + icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component. + icon_state = "radio" + loudspeaker = FALSE + +/obj/item/device/radio/borg/pai/attackby(obj/item/weapon/W as obj, mob/user as mob) + return + +/obj/item/device/radio/borg/pai/recalculateChannels() + if(!istype(loc,/obj/item/device/paicard)) + return + var/obj/item/device/paicard/card = loc + secure_radio_connections = list() + channels = list() + + for(var/internal_chan in internal_channels) + var/ch_name = radio_channels_by_freq[internal_chan] + if(has_channel_access(card.pai, internal_chan)) + channels += ch_name + channels[ch_name] = 1 + secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) + +/obj/item/device/paicard/typeb + name = "personal AI device" + icon = 'icons/obj/paicard.dmi' + +/obj/random/paicard + name = "personal AI device spawner" + icon = 'icons/obj/paicard.dmi' + icon_state = "pai" + +/obj/random/paicard/item_to_spawn() + return pick(/obj/item/device/paicard ,/obj/item/device/paicard/typeb) + +/obj/item/device/paicard/digest_act(var/atom/movable/item_storage = null) + if(pai.digestable) + return ..() diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm index 9ed57621d2b..0d7b8f7bd29 100644 --- a/code/game/objects/items/devices/powersink.dm +++ b/code/game/objects/items/devices/powersink.dm @@ -1,134 +1,134 @@ -// Powersink - used to drain station power - -/obj/item/device/powersink - name = "power sink" - desc = "A nulling power sink which drains energy from electrical systems." - icon_state = "powersink0" - icon = 'icons/obj/device.dmi' - w_class = ITEMSIZE_LARGE - throwforce = 5 - throw_speed = 1 - throw_range = 2 - - matter = list(MAT_STEEL = 750) - - origin_tech = list(TECH_POWER = 3, TECH_ILLEGAL = 5) - var/drain_rate = 1500000 // amount of power to drain per tick - var/apc_drain_rate = 5000 // Max. amount drained from single APC. In Watts. - var/dissipation_rate = 20000 // Passive dissipation of drained power. In Watts. - var/power_drained = 0 // Amount of power drained. - var/max_power = 1e9 // Detonation point. - var/mode = 0 // 0 = off, 1=clamped (off), 2=operating - var/drained_this_tick = 0 // This is unfortunately necessary to ensure we process powersinks BEFORE other machinery such as APCs. - - var/datum/powernet/PN // Our powernet - var/obj/structure/cable/attached // the attached cable - -/obj/item/device/powersink/Destroy() - STOP_PROCESSING(SSobj, src) - STOP_PROCESSING_POWER_OBJECT(src) - ..() - -/obj/item/device/powersink/attackby(var/obj/item/I, var/mob/user) - if(I.has_tool_quality(TOOL_SCREWDRIVER)) - if(mode == 0) - var/turf/T = loc - if(isturf(T) && !!T.is_plating()) - attached = locate() in T - if(!attached) - to_chat(user, "No exposed cable here to attach to.") - return - else - anchored = TRUE - mode = 1 - src.visible_message("[user] attaches [src] to the cable!") - playsound(src, I.usesound, 50, 1) - return - else - to_chat(user, "Device must be placed over an exposed cable to attach to it.") - return - else - if (mode == 2) - STOP_PROCESSING(SSobj, src) // Now the power sink actually stops draining the station's power if you unhook it. --NeoFite - STOP_PROCESSING_POWER_OBJECT(src) - anchored = FALSE - mode = 0 - src.visible_message("[user] detaches [src] from the cable!") - set_light(0) - playsound(src, I.usesound, 50, 1) - icon_state = "powersink0" - - return - else - ..() - -/obj/item/device/powersink/attack_ai() - return - -/obj/item/device/powersink/attack_hand(var/mob/user) - switch(mode) - if(0) - ..() - if(1) - src.visible_message("[user] activates [src]!") - mode = 2 - icon_state = "powersink1" - START_PROCESSING(SSobj, src) - datum_flags &= ~DF_ISPROCESSING // Have to reset this flag so that PROCESSING_POWER_OBJECT can re-add it. It fails if the flag is already present. - Ater - START_PROCESSING_POWER_OBJECT(src) - if(2) //This switch option wasn't originally included. It exists now. --NeoFite - src.visible_message("[user] deactivates [src]!") - mode = 1 - set_light(0) - icon_state = "powersink0" - STOP_PROCESSING(SSobj, src) - STOP_PROCESSING_POWER_OBJECT(src) - -/obj/item/device/powersink/pwr_drain() - if(!attached) - return 0 - - if(drained_this_tick) - return 1 - drained_this_tick = 1 - - var/drained = 0 - - if(!PN) - return 1 - - set_light(12) - PN.trigger_warning() - // found a powernet, so drain up to max power from it - drained = PN.draw_power(drain_rate) - // if tried to drain more than available on powernet - // now look for APCs and drain their cells - if(drained < drain_rate) - for(var/obj/machinery/power/terminal/T in PN.nodes) - // Enough power drained this tick, no need to torture more APCs - if(drained >= drain_rate) - break - if(istype(T.master, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/A = T.master - if(A.operating && A.cell) - var/cur_charge = A.cell.charge / CELLRATE - var/drain_val = min(apc_drain_rate, cur_charge) - A.cell.use(drain_val * CELLRATE) - drained += drain_val - power_drained += drained - return 1 - - -/obj/item/device/powersink/process() - drained_this_tick = 0 - power_drained -= min(dissipation_rate, power_drained) - if(power_drained > max_power * 0.95) - playsound(src, 'sound/effects/screech.ogg', 100, 1, 1) - if(power_drained >= max_power) - explosion(src.loc, 3,6,9,12) - qdel(src) - return - if(attached && attached.powernet) - PN = attached.powernet - else - PN = null +// Powersink - used to drain station power + +/obj/item/device/powersink + name = "power sink" + desc = "A nulling power sink which drains energy from electrical systems." + icon_state = "powersink0" + icon = 'icons/obj/device.dmi' + w_class = ITEMSIZE_LARGE + throwforce = 5 + throw_speed = 1 + throw_range = 2 + + matter = list(MAT_STEEL = 750) + + origin_tech = list(TECH_POWER = 3, TECH_ILLEGAL = 5) + var/drain_rate = 1500000 // amount of power to drain per tick + var/apc_drain_rate = 5000 // Max. amount drained from single APC. In Watts. + var/dissipation_rate = 20000 // Passive dissipation of drained power. In Watts. + var/power_drained = 0 // Amount of power drained. + var/max_power = 1e9 // Detonation point. + var/mode = 0 // 0 = off, 1=clamped (off), 2=operating + var/drained_this_tick = 0 // This is unfortunately necessary to ensure we process powersinks BEFORE other machinery such as APCs. + + var/datum/powernet/PN // Our powernet + var/obj/structure/cable/attached // the attached cable + +/obj/item/device/powersink/Destroy() + STOP_PROCESSING(SSobj, src) + STOP_PROCESSING_POWER_OBJECT(src) + ..() + +/obj/item/device/powersink/attackby(var/obj/item/I, var/mob/user) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + if(mode == 0) + var/turf/T = loc + if(isturf(T) && !!T.is_plating()) + attached = locate() in T + if(!attached) + to_chat(user, "No exposed cable here to attach to.") + return + else + anchored = TRUE + mode = 1 + src.visible_message("[user] attaches [src] to the cable!") + playsound(src, I.usesound, 50, 1) + return + else + to_chat(user, "Device must be placed over an exposed cable to attach to it.") + return + else + if (mode == 2) + STOP_PROCESSING(SSobj, src) // Now the power sink actually stops draining the station's power if you unhook it. --NeoFite + STOP_PROCESSING_POWER_OBJECT(src) + anchored = FALSE + mode = 0 + src.visible_message("[user] detaches [src] from the cable!") + set_light(0) + playsound(src, I.usesound, 50, 1) + icon_state = "powersink0" + + return + else + ..() + +/obj/item/device/powersink/attack_ai() + return + +/obj/item/device/powersink/attack_hand(var/mob/user) + switch(mode) + if(0) + ..() + if(1) + src.visible_message("[user] activates [src]!") + mode = 2 + icon_state = "powersink1" + START_PROCESSING(SSobj, src) + datum_flags &= ~DF_ISPROCESSING // Have to reset this flag so that PROCESSING_POWER_OBJECT can re-add it. It fails if the flag is already present. - Ater + START_PROCESSING_POWER_OBJECT(src) + if(2) //This switch option wasn't originally included. It exists now. --NeoFite + src.visible_message("[user] deactivates [src]!") + mode = 1 + set_light(0) + icon_state = "powersink0" + STOP_PROCESSING(SSobj, src) + STOP_PROCESSING_POWER_OBJECT(src) + +/obj/item/device/powersink/pwr_drain() + if(!attached) + return 0 + + if(drained_this_tick) + return 1 + drained_this_tick = 1 + + var/drained = 0 + + if(!PN) + return 1 + + set_light(12) + PN.trigger_warning() + // found a powernet, so drain up to max power from it + drained = PN.draw_power(drain_rate) + // if tried to drain more than available on powernet + // now look for APCs and drain their cells + if(drained < drain_rate) + for(var/obj/machinery/power/terminal/T in PN.nodes) + // Enough power drained this tick, no need to torture more APCs + if(drained >= drain_rate) + break + if(istype(T.master, /obj/machinery/power/apc)) + var/obj/machinery/power/apc/A = T.master + if(A.operating && A.cell) + var/cur_charge = A.cell.charge / CELLRATE + var/drain_val = min(apc_drain_rate, cur_charge) + A.cell.use(drain_val * CELLRATE) + drained += drain_val + power_drained += drained + return 1 + + +/obj/item/device/powersink/process() + drained_this_tick = 0 + power_drained -= min(dissipation_rate, power_drained) + if(power_drained > max_power * 0.95) + playsound(src, 'sound/effects/screech.ogg', 100, 1, 1) + if(power_drained >= max_power) + explosion(src.loc, 3,6,9,12) + qdel(src) + return + if(attached && attached.powernet) + PN = attached.powernet + else + PN = null diff --git a/code/game/objects/items/devices/radio/beacon.dm b/code/game/objects/items/devices/radio/beacon.dm index cbd95866309..a7b7348e29f 100644 --- a/code/game/objects/items/devices/radio/beacon.dm +++ b/code/game/objects/items/devices/radio/beacon.dm @@ -1,44 +1,44 @@ -/obj/item/device/radio/beacon - name = "tracking beacon" - desc = "A beacon used by a teleporter." - icon_state = "beacon" - item_state = "signaler" - var/code = "electronic" - origin_tech = list(TECH_BLUESPACE = 1) - -GLOBAL_LIST_BOILERPLATE(all_beacons, /obj/item/device/radio/beacon) - -/obj/item/device/radio/beacon/hear_talk() - return - - -/obj/item/device/radio/beacon/send_hear() - return null - - -/obj/item/device/radio/beacon/verb/alter_signal(t as text) - set name = "Alter Beacon's Signal" - set category = "Object" - set src in usr - - if ((usr.canmove && !( usr.restrained() ))) - src.code = t - if (!( src.code )) - src.code = "beacon" - src.add_fingerprint(usr) - return - -// SINGULO BEACON SPAWNER - -/obj/item/device/radio/beacon/syndicate - name = "suspicious beacon" - desc = "A label on it reads: Activate to have a singularity beacon teleported to your location." - origin_tech = list(TECH_BLUESPACE = 1, TECH_ILLEGAL = 7) - -/obj/item/device/radio/beacon/syndicate/attack_self(mob/user as mob) - if(user) - to_chat(user, "Locked In") - new /obj/machinery/power/singularity_beacon/syndicate( user.loc ) - playsound(src, 'sound/effects/pop.ogg', 100, 1, 1) - qdel(src) - return +/obj/item/device/radio/beacon + name = "tracking beacon" + desc = "A beacon used by a teleporter." + icon_state = "beacon" + item_state = "signaler" + var/code = "electronic" + origin_tech = list(TECH_BLUESPACE = 1) + +GLOBAL_LIST_BOILERPLATE(all_beacons, /obj/item/device/radio/beacon) + +/obj/item/device/radio/beacon/hear_talk() + return + + +/obj/item/device/radio/beacon/send_hear() + return null + + +/obj/item/device/radio/beacon/verb/alter_signal(t as text) + set name = "Alter Beacon's Signal" + set category = "Object" + set src in usr + + if ((usr.canmove && !( usr.restrained() ))) + src.code = t + if (!( src.code )) + src.code = "beacon" + src.add_fingerprint(usr) + return + +// SINGULO BEACON SPAWNER + +/obj/item/device/radio/beacon/syndicate + name = "suspicious beacon" + desc = "A label on it reads: Activate to have a singularity beacon teleported to your location." + origin_tech = list(TECH_BLUESPACE = 1, TECH_ILLEGAL = 7) + +/obj/item/device/radio/beacon/syndicate/attack_self(mob/user as mob) + if(user) + to_chat(user, "Locked In") + new /obj/machinery/power/singularity_beacon/syndicate( user.loc ) + playsound(src, 'sound/effects/pop.ogg', 100, 1, 1) + qdel(src) + return diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm index 5cea839acb3..e0fe849fccd 100644 --- a/code/game/objects/items/devices/radio/electropack.dm +++ b/code/game/objects/items/devices/radio/electropack.dm @@ -1,131 +1,131 @@ -/obj/item/device/radio/electropack - name = "electropack" - desc = "Dance my monkeys! DANCE!!!" - icon_state = "electropack0" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_storage.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_storage.dmi', - ) - item_state = "electropack" - frequency = 1449 - slot_flags = SLOT_BACK - w_class = ITEMSIZE_HUGE - - matter = list(MAT_STEEL = 10000,MAT_GLASS = 2500) - - var/code = 2 - -/obj/item/device/radio/electropack/attack_hand(mob/living/user as mob) - if(src == user.back) - to_chat(user, "You need help taking this off!") - return - ..() - -/obj/item/device/radio/electropack/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - if(istype(W, /obj/item/clothing/head/helmet)) - if(!b_stat) - to_chat(user, "[src] is not ready to be attached!") - return - var/obj/item/assembly/shock_kit/A = new /obj/item/assembly/shock_kit( user ) - A.icon = 'icons/obj/assemblies.dmi' - - user.drop_from_inventory(W) - W.loc = A - W.master = A - A.part1 = W - - user.drop_from_inventory(src) - loc = A - master = A - A.part2 = src - - user.put_in_hands(A) - A.add_fingerprint(user) - -/obj/item/device/radio/electropack/Topic(href, href_list) - //..() - if(usr.stat || usr.restrained()) - return - if(((istype(usr, /mob/living/carbon/human) && ((!( ticker ) || (ticker && ticker.mode != "monkey")) && usr.contents.Find(src))) || (usr.contents.Find(master) || (in_range(src, usr) && istype(loc, /turf))))) - usr.set_machine(src) - if(href_list["freq"]) - var/new_frequency = sanitize_frequency(frequency + text2num(href_list["freq"])) - set_frequency(new_frequency) - else - if(href_list["code"]) - code += text2num(href_list["code"]) - code = round(code) - code = min(100, code) - code = max(1, code) - else - if(href_list["power"]) - on = !( on ) - icon_state = "electropack[on]" - if(!( master )) - if(istype(loc, /mob)) - attack_self(loc) - else - for(var/mob/M in viewers(1, src)) - if(M.client) - attack_self(M) - else - if(istype(master.loc, /mob)) - attack_self(master.loc) - else - for(var/mob/M in viewers(1, master)) - if(M.client) - attack_self(M) - else - usr << browse(null, "window=radio") - return - return - -/obj/item/device/radio/electropack/receive_signal(datum/signal/signal) - if(!signal || signal.encryption != code) - return - - if(ismob(loc) && on) - var/mob/M = loc - var/turf/T = M.loc - if(istype(T, /turf)) - if(!M.moved_recently && M.last_move) - M.moved_recently = 1 - step(M, M.last_move) - sleep(50) - if(M) - M.moved_recently = 0 - to_chat(M, "You feel a sharp shock!") - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(3, 1, M) - s.start() - - M.Weaken(10) - - if(master && wires & 1) - master.receive_signal() - return - -/obj/item/device/radio/electropack/attack_self(mob/user as mob, flag1) - - if(!istype(user, /mob/living/carbon/human)) - return - user.set_machine(src) - var/dat = {" -Turn [on ? "Off" : "On"]
                    -Frequency/Code for electropack:
                    -Frequency: -- -- [format_frequency(frequency)] -+ -+
                    - -Code: -- -- [code] -+ -+
                    -
                    "} - user << browse(dat, "window=radio") - onclose(user, "radio") - return +/obj/item/device/radio/electropack + name = "electropack" + desc = "Dance my monkeys! DANCE!!!" + icon_state = "electropack0" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_storage.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_storage.dmi', + ) + item_state = "electropack" + frequency = 1449 + slot_flags = SLOT_BACK + w_class = ITEMSIZE_HUGE + + matter = list(MAT_STEEL = 10000,MAT_GLASS = 2500) + + var/code = 2 + +/obj/item/device/radio/electropack/attack_hand(mob/living/user as mob) + if(src == user.back) + to_chat(user, "You need help taking this off!") + return + ..() + +/obj/item/device/radio/electropack/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + if(istype(W, /obj/item/clothing/head/helmet)) + if(!b_stat) + to_chat(user, "[src] is not ready to be attached!") + return + var/obj/item/assembly/shock_kit/A = new /obj/item/assembly/shock_kit( user ) + A.icon = 'icons/obj/assemblies.dmi' + + user.drop_from_inventory(W) + W.loc = A + W.master = A + A.part1 = W + + user.drop_from_inventory(src) + loc = A + master = A + A.part2 = src + + user.put_in_hands(A) + A.add_fingerprint(user) + +/obj/item/device/radio/electropack/Topic(href, href_list) + //..() + if(usr.stat || usr.restrained()) + return + if(((istype(usr, /mob/living/carbon/human) && ((!( ticker ) || (ticker && ticker.mode != "monkey")) && usr.contents.Find(src))) || (usr.contents.Find(master) || (in_range(src, usr) && istype(loc, /turf))))) + usr.set_machine(src) + if(href_list["freq"]) + var/new_frequency = sanitize_frequency(frequency + text2num(href_list["freq"])) + set_frequency(new_frequency) + else + if(href_list["code"]) + code += text2num(href_list["code"]) + code = round(code) + code = min(100, code) + code = max(1, code) + else + if(href_list["power"]) + on = !( on ) + icon_state = "electropack[on]" + if(!( master )) + if(istype(loc, /mob)) + attack_self(loc) + else + for(var/mob/M in viewers(1, src)) + if(M.client) + attack_self(M) + else + if(istype(master.loc, /mob)) + attack_self(master.loc) + else + for(var/mob/M in viewers(1, master)) + if(M.client) + attack_self(M) + else + usr << browse(null, "window=radio") + return + return + +/obj/item/device/radio/electropack/receive_signal(datum/signal/signal) + if(!signal || signal.encryption != code) + return + + if(ismob(loc) && on) + var/mob/M = loc + var/turf/T = M.loc + if(istype(T, /turf)) + if(!M.moved_recently && M.last_move) + M.moved_recently = 1 + step(M, M.last_move) + sleep(50) + if(M) + M.moved_recently = 0 + to_chat(M, "You feel a sharp shock!") + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, M) + s.start() + + M.Weaken(10) + + if(master && wires & 1) + master.receive_signal() + return + +/obj/item/device/radio/electropack/attack_self(mob/user as mob, flag1) + + if(!istype(user, /mob/living/carbon/human)) + return + user.set_machine(src) + var/dat = {" +Turn [on ? "Off" : "On"]
                    +Frequency/Code for electropack:
                    +Frequency: +- +- [format_frequency(frequency)] ++ ++
                    + +Code: +- +- [code] ++ ++
                    +
                    "} + user << browse(dat, "window=radio") + onclose(user, "radio") + return diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index 9628a1a190d..b253e930560 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -1,455 +1,455 @@ -/obj/item/device/radio/headset - name = "radio headset" - desc = "An updated, modular intercom that fits over the head. Takes encryption keys" - var/radio_desc = "" - icon_state = "headset" - item_state = null //To remove the radio's state - matter = list(MAT_STEEL = 75) - subspace_transmission = 1 - canhear_range = 0 // can't hear headsets from very far away - slot_flags = SLOT_EARS - sprite_sheets = list(SPECIES_TESHARI = 'icons/inventory/ears/mob_teshari.dmi') - - var/translate_binary = 0 - var/translate_hive = 0 - var/obj/item/device/encryptionkey/keyslot1 = null - var/obj/item/device/encryptionkey/keyslot2 = null - var/ks1type = null - var/ks2type = null - - drop_sound = 'sound/items/drop/component.ogg' - pickup_sound = 'sound/items/pickup/component.ogg' - -/obj/item/device/radio/headset/New() - ..() - internal_channels.Cut() - if(ks1type) - keyslot1 = new ks1type(src) - if(ks2type) - keyslot2 = new ks2type(src) - recalculateChannels(1) - -/obj/item/device/radio/headset/Destroy() - qdel(keyslot1) - qdel(keyslot2) - keyslot1 = null - keyslot2 = null - return ..() - -/obj/item/device/radio/headset/list_channels(var/mob/user) - return list_secure_channels() - -/obj/item/device/radio/headset/examine(mob/user) - . = ..() - - if(radio_desc && Adjacent(user)) - . += "The following channels are available:" - . += radio_desc - -/obj/item/device/radio/headset/handle_message_mode(mob/living/M as mob, list/message_pieces, channel) - if(channel == "special") - if(translate_binary) - var/datum/language/binary = GLOB.all_languages["Robot Talk"] - binary.broadcast(M, M.strip_prefixes(multilingual_to_message(message_pieces))) - return RADIO_CONNECTION_NON_SUBSPACE - if(translate_hive) - var/datum/language/hivemind = GLOB.all_languages["Hivemind"] - hivemind.broadcast(M, M.strip_prefixes(multilingual_to_message(message_pieces))) - return RADIO_CONNECTION_NON_SUBSPACE - return RADIO_CONNECTION_FAIL - - return ..() - -/obj/item/device/radio/headset/receive_range(freq, level, aiOverride = 0) - if (aiOverride) - //playsound(loc, 'sound/effects/radio_common.ogg', 20, 1, 1, preference = /datum/client_preference/radio_sounds) - return ..(freq, level) - if(ishuman(src.loc)) - var/mob/living/carbon/human/H = src.loc - if(H.l_ear == src || H.r_ear == src) - //playsound(loc, 'sound/effects/radio_common.ogg', 20, 1, 1, preference = /datum/client_preference/radio_sounds) - return ..(freq, level) - return -1 - -/obj/item/device/radio/headset/get_worn_icon_state(var/slot_name) - var/append = "" - if(icon_override) - switch(slot_name) - if(slot_l_ear_str) - append = "_l" - if(slot_r_ear_str) - append = "_r" - - return "[..()][append]" - -/obj/item/device/radio/headset/tgui_state(mob/user) - return GLOB.tgui_inventory_state - -/obj/item/device/radio/headset/syndicate - origin_tech = list(TECH_ILLEGAL = 3) - syndie = 1 - ks1type = /obj/item/device/encryptionkey/syndicate - -/obj/item/device/radio/headset/syndicate/alt - icon_state = "syndie_headset" - item_state = "headset" - origin_tech = list(TECH_ILLEGAL = 3) - syndie = 1 - ks1type = /obj/item/device/encryptionkey/syndicate - -/obj/item/device/radio/headset/raider - origin_tech = list(TECH_ILLEGAL = 2) - syndie = 1 - ks1type = /obj/item/device/encryptionkey/raider - -/obj/item/device/radio/headset/raider/Initialize() - . = ..() - set_frequency(RAID_FREQ) - -/obj/item/device/radio/headset/binary - origin_tech = list(TECH_ILLEGAL = 3) - ks1type = /obj/item/device/encryptionkey/binary - -/obj/item/device/radio/headset/headset_sec - name = "security radio headset" - desc = "This is used by your elite security force." - icon_state = "sec_headset" - ks2type = /obj/item/device/encryptionkey/headset_sec - -/obj/item/device/radio/headset/headset_sec/alt - name = "security bowman headset" - desc = "This is used by your elite security force." - icon_state = "sec_headset_alt" - ks2type = /obj/item/device/encryptionkey/headset_sec - -/obj/item/device/radio/headset/headset_eng - name = "engineering radio headset" - desc = "When the engineers wish to chat like girls." - icon_state = "eng_headset" - ks2type = /obj/item/device/encryptionkey/headset_eng - -/obj/item/device/radio/headset/headset_eng/alt - name = "engineering bowman headset" - desc = "When the engineers wish to chat like girls." - icon_state = "eng_headset_alt" - ks2type = /obj/item/device/encryptionkey/headset_eng - -/obj/item/device/radio/headset/headset_rob - name = "robotics radio headset" - desc = "Made specifically for the roboticists who cannot decide between departments." - icon_state = "rob_headset" - ks2type = /obj/item/device/encryptionkey/headset_rob - -/obj/item/device/radio/headset/headset_med - name = "medical radio headset" - desc = "A headset for the trained staff of the medbay." - icon_state = "med_headset" - ks2type = /obj/item/device/encryptionkey/headset_med - -/obj/item/device/radio/headset/headset_med/alt - name = "medical bowman headset" - desc = "A headset for the trained staff of the medbay." - icon_state = "med_headset_alt" - ks2type = /obj/item/device/encryptionkey/headset_med - -/obj/item/device/radio/headset/headset_sci - name = "science radio headset" - desc = "A sciency headset. Like usual." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/headset_sci - -/obj/item/device/radio/headset/headset_medsci - name = "medical research radio headset" - desc = "A headset that is a result of the mating between medical and science." - icon_state = "med_headset" - ks2type = /obj/item/device/encryptionkey/headset_medsci - -/obj/item/device/radio/headset/headset_com - name = "command radio headset" - desc = "A headset with a commanding channel." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/headset_com - -/obj/item/device/radio/headset/headset_com/alt - name = "command bowman headset" - desc = "A headset with a commanding channel." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/headset_com - - -/obj/item/device/radio/headset/heads/captain - name = "site manager's headset" - desc = "The headset of the boss." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/heads/captain - -/obj/item/device/radio/headset/heads/captain/alt - name = "site manager's bowman headset" - desc = "The headset of the boss." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/heads/captain - -/obj/item/device/radio/headset/heads/captain/sfr - name = "SFR headset" - desc = "A headset belonging to a Sif Free Radio DJ. SFR, best tunes in the wilderness." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/heads/captain - -/obj/item/device/radio/headset/heads/ai_integrated //No need to care about icons, it should be hidden inside the AI anyway. - name = "\improper AI subspace transceiver" - desc = "Integrated AI radio transceiver." - icon = 'icons/obj/robot_component.dmi' - icon_state = "radio" - item_state = "headset" - ks2type = /obj/item/device/encryptionkey/heads/ai_integrated - var/myAi = null // Atlantis: Reference back to the AI which has this radio. - var/disabledAi = 0 // Atlantis: Used to manually disable AI's integrated radio via intellicard menu. - -/obj/item/device/radio/headset/heads/ai_integrated/receive_range(freq, level) - if (disabledAi) - return -1 //Transciever Disabled. - return ..(freq, level, 1) - -/obj/item/device/radio/headset/heads/rd - name = "research director's headset" - desc = "Headset of the eccentric-in-chief." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/heads/rd - -/obj/item/device/radio/headset/heads/rd/alt - name = "research director's bowman headset" - desc = "Headset of the eccentric-in-chief." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/heads/rd - -/obj/item/device/radio/headset/heads/hos - name = "head of security's headset" - desc = "The headset of the hardass who protects your worthless lives." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/heads/hos - -/obj/item/device/radio/headset/heads/hos/alt - name = "head of security's bowman headset" - desc = "The headset of the hardass who protects your worthless lives." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/heads/hos - -/obj/item/device/radio/headset/heads/ce - name = "chief engineer's headset" - desc = "The headset of the clown who is in charge of the circus." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/heads/ce - -/obj/item/device/radio/headset/heads/ce/alt - name = "chief engineer's bowman headset" - desc = "The headset of the clown who is in charge of the circus." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/heads/ce - -/obj/item/device/radio/headset/heads/cmo - name = "chief medical officer's headset" - desc = "The headset of the highly trained medical chief." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/heads/cmo - -/obj/item/device/radio/headset/heads/cmo/alt - name = "chief medical officer's bowman headset" - desc = "The headset of the highly trained medical chief." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/heads/cmo - -/obj/item/device/radio/headset/heads/hop - name = "head of personnel's headset" - desc = "The headset of the poor fool who will one day be Site Manager." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/heads/hop - -/obj/item/device/radio/headset/heads/hop/alt - name = "head of personnel's bowman headset" - desc = "The headset of the poor fool who will one day be Site Manager." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/heads/hop - -/obj/item/device/radio/headset/headset_mine - name = "mining radio headset" - desc = "Headset used by miners. Has inbuilt short-band radio for when comms are down." - icon_state = "mine_headset" - adhoc_fallback = TRUE - ks2type = /obj/item/device/encryptionkey/headset_cargo - -/obj/item/device/radio/headset/headset_cargo - name = "supply radio headset" - desc = "A headset used by the QM and their cronies." - icon_state = "cargo_headset" - ks2type = /obj/item/device/encryptionkey/headset_cargo - -/obj/item/device/radio/headset/headset_cargo/alt - name = "supply bowman headset" - desc = "A bowman headset used by the QM and their cronies." - icon_state = "cargo_headset_alt" - ks2type = /obj/item/device/encryptionkey/headset_cargo - -/obj/item/device/radio/headset/headset_service - name = "service radio headset" - desc = "Headset used by the service staff, tasked with keeping the station full, happy and clean." - icon_state = "srv_headset" - ks2type = /obj/item/device/encryptionkey/headset_service - -/obj/item/device/radio/headset/ert - name = "emergency response team radio headset" - desc = "The headset of the boss's boss." - icon_state = "com_headset" - centComm = 1 -// freerange = 1 - ks2type = /obj/item/device/encryptionkey/ert - -/obj/item/device/radio/headset/ert/alt - name = "emergency response team bowman headset" - desc = "The headset of the boss's boss." - icon_state = "com_headset_alt" -// freerange = 1 - ks2type = /obj/item/device/encryptionkey/ert - -/obj/item/device/radio/headset/omni //Only for the admin intercoms - ks2type = /obj/item/device/encryptionkey/omni - -/obj/item/device/radio/headset/ia - name = "internal affair's headset" - desc = "The headset of your worst enemy." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/heads/hos - -/obj/item/device/radio/headset/mmi_radio - name = "brain-integrated radio" - desc = "MMIs and synthetic brains are often equipped with these." - icon = 'icons/obj/robot_component.dmi' - icon_state = "radio" - item_state = "headset" - var/mmiowner = null - var/radio_enabled = 1 - -/obj/item/device/radio/headset/mmi_radio/receive_range(freq, level) - if (!radio_enabled || istype(src.loc.loc, /mob/living/silicon) || istype(src.loc.loc, /obj/item/organ/internal)) - return -1 //Transciever Disabled. - return ..(freq, level, 1) - -/obj/item/device/radio/headset/attackby(obj/item/weapon/W as obj, mob/user as mob) -// ..() - user.set_machine(src) - if(!(W.has_tool_quality(TOOL_SCREWDRIVER) || istype(W, /obj/item/device/encryptionkey))) - return - - if(W.has_tool_quality(TOOL_SCREWDRIVER)) - if(keyslot1 || keyslot2) - - - for(var/ch_name in channels) - radio_controller.remove_object(src, radiochannels[ch_name]) - secure_radio_connections[ch_name] = null - - - if(keyslot1) - var/turf/T = get_turf(user) - if(T) - keyslot1.loc = T - keyslot1 = null - - - - if(keyslot2) - var/turf/T = get_turf(user) - if(T) - keyslot2.loc = T - keyslot2 = null - - recalculateChannels() - to_chat(user, "You pop out the encryption keys in the headset!") - playsound(src, W.usesound, 50, 1) - - else - to_chat(user, "This headset doesn't have any encryption keys! How useless...") - - if(istype(W, /obj/item/device/encryptionkey/)) - if(keyslot1 && keyslot2) - to_chat(user, "The headset can't hold another key!") - return - - if(!keyslot1) - user.drop_item() - W.loc = src - keyslot1 = W - - else - user.drop_item() - W.loc = src - keyslot2 = W - - - recalculateChannels() - - return - - -/obj/item/device/radio/headset/recalculateChannels(var/setDescription = 0) - src.channels = list() - src.translate_binary = 0 - src.translate_hive = 0 - src.syndie = 0 - - if(keyslot1) - for(var/ch_name in keyslot1.channels) - if(ch_name in src.channels) - continue - src.channels += ch_name - src.channels[ch_name] = keyslot1.channels[ch_name] - - if(keyslot1.translate_binary) - src.translate_binary = 1 - - if(keyslot1.translate_hive) - src.translate_hive = 1 - - if(keyslot1.syndie) - src.syndie = 1 - - if(keyslot2) - for(var/ch_name in keyslot2.channels) - if(ch_name in src.channels) - continue - src.channels += ch_name - src.channels[ch_name] = keyslot2.channels[ch_name] - - if(keyslot2.translate_binary) - src.translate_binary = 1 - - if(keyslot2.translate_hive) - src.translate_hive = 1 - - if(keyslot2.syndie) - src.syndie = 1 - - - for (var/ch_name in channels) - if(!radio_controller) - sleep(30) // Waiting for the radio_controller to be created. - if(!radio_controller) - src.name = "broken radio headset" - return - - secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) - - if(setDescription) - setupRadioDescription() - - return - -/obj/item/device/radio/headset/proc/setupRadioDescription() - var/radio_text = "" - for(var/i = 1 to channels.len) - var/channel = channels[i] - var/key = get_radio_key_from_channel(channel) - radio_text += "[key] - [channel]" - if(i != channels.len) - radio_text += ", " - - radio_desc = radio_text +/obj/item/device/radio/headset + name = "radio headset" + desc = "An updated, modular intercom that fits over the head. Takes encryption keys" + var/radio_desc = "" + icon_state = "headset" + item_state = null //To remove the radio's state + matter = list(MAT_STEEL = 75) + subspace_transmission = 1 + canhear_range = 0 // can't hear headsets from very far away + slot_flags = SLOT_EARS + sprite_sheets = list(SPECIES_TESHARI = 'icons/inventory/ears/mob_teshari.dmi') + + var/translate_binary = 0 + var/translate_hive = 0 + var/obj/item/device/encryptionkey/keyslot1 = null + var/obj/item/device/encryptionkey/keyslot2 = null + var/ks1type = null + var/ks2type = null + + drop_sound = 'sound/items/drop/component.ogg' + pickup_sound = 'sound/items/pickup/component.ogg' + +/obj/item/device/radio/headset/New() + ..() + internal_channels.Cut() + if(ks1type) + keyslot1 = new ks1type(src) + if(ks2type) + keyslot2 = new ks2type(src) + recalculateChannels(1) + +/obj/item/device/radio/headset/Destroy() + qdel(keyslot1) + qdel(keyslot2) + keyslot1 = null + keyslot2 = null + return ..() + +/obj/item/device/radio/headset/list_channels(var/mob/user) + return list_secure_channels() + +/obj/item/device/radio/headset/examine(mob/user) + . = ..() + + if(radio_desc && Adjacent(user)) + . += "The following channels are available:" + . += radio_desc + +/obj/item/device/radio/headset/handle_message_mode(mob/living/M as mob, list/message_pieces, channel) + if(channel == "special") + if(translate_binary) + var/datum/language/binary = GLOB.all_languages["Robot Talk"] + binary.broadcast(M, M.strip_prefixes(multilingual_to_message(message_pieces))) + return RADIO_CONNECTION_NON_SUBSPACE + if(translate_hive) + var/datum/language/hivemind = GLOB.all_languages["Hivemind"] + hivemind.broadcast(M, M.strip_prefixes(multilingual_to_message(message_pieces))) + return RADIO_CONNECTION_NON_SUBSPACE + return RADIO_CONNECTION_FAIL + + return ..() + +/obj/item/device/radio/headset/receive_range(freq, level, aiOverride = 0) + if (aiOverride) + //playsound(loc, 'sound/effects/radio_common.ogg', 20, 1, 1, preference = /datum/client_preference/radio_sounds) + return ..(freq, level) + if(ishuman(src.loc)) + var/mob/living/carbon/human/H = src.loc + if(H.l_ear == src || H.r_ear == src) + //playsound(loc, 'sound/effects/radio_common.ogg', 20, 1, 1, preference = /datum/client_preference/radio_sounds) + return ..(freq, level) + return -1 + +/obj/item/device/radio/headset/get_worn_icon_state(var/slot_name) + var/append = "" + if(icon_override) + switch(slot_name) + if(slot_l_ear_str) + append = "_l" + if(slot_r_ear_str) + append = "_r" + + return "[..()][append]" + +/obj/item/device/radio/headset/tgui_state(mob/user) + return GLOB.tgui_inventory_state + +/obj/item/device/radio/headset/syndicate + origin_tech = list(TECH_ILLEGAL = 3) + syndie = 1 + ks1type = /obj/item/device/encryptionkey/syndicate + +/obj/item/device/radio/headset/syndicate/alt + icon_state = "syndie_headset" + item_state = "headset" + origin_tech = list(TECH_ILLEGAL = 3) + syndie = 1 + ks1type = /obj/item/device/encryptionkey/syndicate + +/obj/item/device/radio/headset/raider + origin_tech = list(TECH_ILLEGAL = 2) + syndie = 1 + ks1type = /obj/item/device/encryptionkey/raider + +/obj/item/device/radio/headset/raider/Initialize() + . = ..() + set_frequency(RAID_FREQ) + +/obj/item/device/radio/headset/binary + origin_tech = list(TECH_ILLEGAL = 3) + ks1type = /obj/item/device/encryptionkey/binary + +/obj/item/device/radio/headset/headset_sec + name = "security radio headset" + desc = "This is used by your elite security force." + icon_state = "sec_headset" + ks2type = /obj/item/device/encryptionkey/headset_sec + +/obj/item/device/radio/headset/headset_sec/alt + name = "security bowman headset" + desc = "This is used by your elite security force." + icon_state = "sec_headset_alt" + ks2type = /obj/item/device/encryptionkey/headset_sec + +/obj/item/device/radio/headset/headset_eng + name = "engineering radio headset" + desc = "When the engineers wish to chat like girls." + icon_state = "eng_headset" + ks2type = /obj/item/device/encryptionkey/headset_eng + +/obj/item/device/radio/headset/headset_eng/alt + name = "engineering bowman headset" + desc = "When the engineers wish to chat like girls." + icon_state = "eng_headset_alt" + ks2type = /obj/item/device/encryptionkey/headset_eng + +/obj/item/device/radio/headset/headset_rob + name = "robotics radio headset" + desc = "Made specifically for the roboticists who cannot decide between departments." + icon_state = "rob_headset" + ks2type = /obj/item/device/encryptionkey/headset_rob + +/obj/item/device/radio/headset/headset_med + name = "medical radio headset" + desc = "A headset for the trained staff of the medbay." + icon_state = "med_headset" + ks2type = /obj/item/device/encryptionkey/headset_med + +/obj/item/device/radio/headset/headset_med/alt + name = "medical bowman headset" + desc = "A headset for the trained staff of the medbay." + icon_state = "med_headset_alt" + ks2type = /obj/item/device/encryptionkey/headset_med + +/obj/item/device/radio/headset/headset_sci + name = "science radio headset" + desc = "A sciency headset. Like usual." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/headset_sci + +/obj/item/device/radio/headset/headset_medsci + name = "medical research radio headset" + desc = "A headset that is a result of the mating between medical and science." + icon_state = "med_headset" + ks2type = /obj/item/device/encryptionkey/headset_medsci + +/obj/item/device/radio/headset/headset_com + name = "command radio headset" + desc = "A headset with a commanding channel." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/headset_com + +/obj/item/device/radio/headset/headset_com/alt + name = "command bowman headset" + desc = "A headset with a commanding channel." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/headset_com + + +/obj/item/device/radio/headset/heads/captain + name = "site manager's headset" + desc = "The headset of the boss." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/heads/captain + +/obj/item/device/radio/headset/heads/captain/alt + name = "site manager's bowman headset" + desc = "The headset of the boss." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/heads/captain + +/obj/item/device/radio/headset/heads/captain/sfr + name = "SFR headset" + desc = "A headset belonging to a Sif Free Radio DJ. SFR, best tunes in the wilderness." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/heads/captain + +/obj/item/device/radio/headset/heads/ai_integrated //No need to care about icons, it should be hidden inside the AI anyway. + name = "\improper AI subspace transceiver" + desc = "Integrated AI radio transceiver." + icon = 'icons/obj/robot_component.dmi' + icon_state = "radio" + item_state = "headset" + ks2type = /obj/item/device/encryptionkey/heads/ai_integrated + var/myAi = null // Atlantis: Reference back to the AI which has this radio. + var/disabledAi = 0 // Atlantis: Used to manually disable AI's integrated radio via intellicard menu. + +/obj/item/device/radio/headset/heads/ai_integrated/receive_range(freq, level) + if (disabledAi) + return -1 //Transciever Disabled. + return ..(freq, level, 1) + +/obj/item/device/radio/headset/heads/rd + name = "research director's headset" + desc = "Headset of the eccentric-in-chief." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/heads/rd + +/obj/item/device/radio/headset/heads/rd/alt + name = "research director's bowman headset" + desc = "Headset of the eccentric-in-chief." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/heads/rd + +/obj/item/device/radio/headset/heads/hos + name = "head of security's headset" + desc = "The headset of the hardass who protects your worthless lives." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/heads/hos + +/obj/item/device/radio/headset/heads/hos/alt + name = "head of security's bowman headset" + desc = "The headset of the hardass who protects your worthless lives." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/heads/hos + +/obj/item/device/radio/headset/heads/ce + name = "chief engineer's headset" + desc = "The headset of the clown who is in charge of the circus." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/heads/ce + +/obj/item/device/radio/headset/heads/ce/alt + name = "chief engineer's bowman headset" + desc = "The headset of the clown who is in charge of the circus." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/heads/ce + +/obj/item/device/radio/headset/heads/cmo + name = "chief medical officer's headset" + desc = "The headset of the highly trained medical chief." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/heads/cmo + +/obj/item/device/radio/headset/heads/cmo/alt + name = "chief medical officer's bowman headset" + desc = "The headset of the highly trained medical chief." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/heads/cmo + +/obj/item/device/radio/headset/heads/hop + name = "head of personnel's headset" + desc = "The headset of the poor fool who will one day be Site Manager." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/heads/hop + +/obj/item/device/radio/headset/heads/hop/alt + name = "head of personnel's bowman headset" + desc = "The headset of the poor fool who will one day be Site Manager." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/heads/hop + +/obj/item/device/radio/headset/headset_mine + name = "mining radio headset" + desc = "Headset used by miners. Has inbuilt short-band radio for when comms are down." + icon_state = "mine_headset" + adhoc_fallback = TRUE + ks2type = /obj/item/device/encryptionkey/headset_cargo + +/obj/item/device/radio/headset/headset_cargo + name = "supply radio headset" + desc = "A headset used by the QM and their cronies." + icon_state = "cargo_headset" + ks2type = /obj/item/device/encryptionkey/headset_cargo + +/obj/item/device/radio/headset/headset_cargo/alt + name = "supply bowman headset" + desc = "A bowman headset used by the QM and their cronies." + icon_state = "cargo_headset_alt" + ks2type = /obj/item/device/encryptionkey/headset_cargo + +/obj/item/device/radio/headset/headset_service + name = "service radio headset" + desc = "Headset used by the service staff, tasked with keeping the station full, happy and clean." + icon_state = "srv_headset" + ks2type = /obj/item/device/encryptionkey/headset_service + +/obj/item/device/radio/headset/ert + name = "emergency response team radio headset" + desc = "The headset of the boss's boss." + icon_state = "com_headset" + centComm = 1 +// freerange = 1 + ks2type = /obj/item/device/encryptionkey/ert + +/obj/item/device/radio/headset/ert/alt + name = "emergency response team bowman headset" + desc = "The headset of the boss's boss." + icon_state = "com_headset_alt" +// freerange = 1 + ks2type = /obj/item/device/encryptionkey/ert + +/obj/item/device/radio/headset/omni //Only for the admin intercoms + ks2type = /obj/item/device/encryptionkey/omni + +/obj/item/device/radio/headset/ia + name = "internal affair's headset" + desc = "The headset of your worst enemy." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/heads/hos + +/obj/item/device/radio/headset/mmi_radio + name = "brain-integrated radio" + desc = "MMIs and synthetic brains are often equipped with these." + icon = 'icons/obj/robot_component.dmi' + icon_state = "radio" + item_state = "headset" + var/mmiowner = null + var/radio_enabled = 1 + +/obj/item/device/radio/headset/mmi_radio/receive_range(freq, level) + if (!radio_enabled || istype(src.loc.loc, /mob/living/silicon) || istype(src.loc.loc, /obj/item/organ/internal)) + return -1 //Transciever Disabled. + return ..(freq, level, 1) + +/obj/item/device/radio/headset/attackby(obj/item/weapon/W as obj, mob/user as mob) +// ..() + user.set_machine(src) + if(!(W.has_tool_quality(TOOL_SCREWDRIVER) || istype(W, /obj/item/device/encryptionkey))) + return + + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(keyslot1 || keyslot2) + + + for(var/ch_name in channels) + radio_controller.remove_object(src, radiochannels[ch_name]) + secure_radio_connections[ch_name] = null + + + if(keyslot1) + var/turf/T = get_turf(user) + if(T) + keyslot1.loc = T + keyslot1 = null + + + + if(keyslot2) + var/turf/T = get_turf(user) + if(T) + keyslot2.loc = T + keyslot2 = null + + recalculateChannels() + to_chat(user, "You pop out the encryption keys in the headset!") + playsound(src, W.usesound, 50, 1) + + else + to_chat(user, "This headset doesn't have any encryption keys! How useless...") + + if(istype(W, /obj/item/device/encryptionkey/)) + if(keyslot1 && keyslot2) + to_chat(user, "The headset can't hold another key!") + return + + if(!keyslot1) + user.drop_item() + W.loc = src + keyslot1 = W + + else + user.drop_item() + W.loc = src + keyslot2 = W + + + recalculateChannels() + + return + + +/obj/item/device/radio/headset/recalculateChannels(var/setDescription = 0) + src.channels = list() + src.translate_binary = 0 + src.translate_hive = 0 + src.syndie = 0 + + if(keyslot1) + for(var/ch_name in keyslot1.channels) + if(ch_name in src.channels) + continue + src.channels += ch_name + src.channels[ch_name] = keyslot1.channels[ch_name] + + if(keyslot1.translate_binary) + src.translate_binary = 1 + + if(keyslot1.translate_hive) + src.translate_hive = 1 + + if(keyslot1.syndie) + src.syndie = 1 + + if(keyslot2) + for(var/ch_name in keyslot2.channels) + if(ch_name in src.channels) + continue + src.channels += ch_name + src.channels[ch_name] = keyslot2.channels[ch_name] + + if(keyslot2.translate_binary) + src.translate_binary = 1 + + if(keyslot2.translate_hive) + src.translate_hive = 1 + + if(keyslot2.syndie) + src.syndie = 1 + + + for (var/ch_name in channels) + if(!radio_controller) + sleep(30) // Waiting for the radio_controller to be created. + if(!radio_controller) + src.name = "broken radio headset" + return + + secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) + + if(setDescription) + setupRadioDescription() + + return + +/obj/item/device/radio/headset/proc/setupRadioDescription() + var/radio_text = "" + for(var/i = 1 to channels.len) + var/channel = channels[i] + var/key = get_radio_key_from_channel(channel) + radio_text += "[key] - [channel]" + if(i != channels.len) + radio_text += ", " + + radio_desc = radio_text diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 99a9dddfc30..616ce62b456 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -1,767 +1,767 @@ -// Access check is of the type requires one. These have been carefully selected to avoid allowing the janitor to see channels he shouldn't -//VOREStation Edit Start - Updating this for Virgo -var/global/list/default_internal_channels = list( - num2text(PUB_FREQ) = list(), - num2text(AI_FREQ) = list(access_synth), - num2text(ENT_FREQ) = list(), - num2text(ERT_FREQ) = list(access_cent_specops), - num2text(COMM_FREQ)= list(access_heads), - num2text(ENG_FREQ) = list(access_engine_equip, access_atmospherics), - num2text(MED_FREQ) = list(access_medical_equip), - num2text(MED_I_FREQ)=list(access_medical_equip), - num2text(SEC_FREQ) = list(access_security), - num2text(SEC_I_FREQ)=list(access_security), - num2text(SCI_FREQ) = list(access_tox, access_robotics, access_xenobiology), - num2text(SUP_FREQ) = list(access_cargo, access_mining_station), - num2text(SRV_FREQ) = list(access_janitor, access_library, access_hydroponics, access_bar, access_kitchen), - num2text(EXP_FREQ) = list(access_explorer) //VOREStation Edit -) - -var/global/list/default_medbay_channels = list( - num2text(PUB_FREQ) = list(), - num2text(MED_FREQ) = list(), - num2text(MED_I_FREQ) = list() -) -//VOREStation Edit End - -/obj/item/device/radio - icon = 'icons/obj/radio_vr.dmi' //VOREStation Edit - name = "shortwave radio" //VOREStation Edit - desc = "Used to talk to people when headsets don't function. Range is limited." - suffix = "\[3\]" - icon_state = "walkietalkie" - item_state = "radio" - - var/on = 1 // 0 for off - var/last_transmission - var/frequency = PUB_FREQ //common chat - var/traitor_frequency = 0 //tune to frequency to unlock traitor supplies - var/canhear_range = 3 // the range which mobs can hear this radio from - var/loudspeaker = TRUE // Allows borgs to disable canhear_range. - var/datum/wires/radio/wires = null - var/b_stat = 0 - var/broadcasting = 0 - var/listening = 1 - var/list/channels = list() //see communications.dm for full list. First channel is a "default" for :h - var/subspace_transmission = 0 - var/subspace_switchable = FALSE - var/adhoc_fallback = FALSE //Falls back to 'radio' mode if subspace not available - var/syndie = 0//Holder to see if it's a syndicate encrypted radio - var/centComm = 0//Holder to see if it's a CentCom encrypted radio - slot_flags = SLOT_BELT - throw_speed = 2 - throw_range = 9 - w_class = ITEMSIZE_SMALL - show_messages = 1 - - // Bluespace radios talk directly to telecomms equipment - var/bluespace_radio = FALSE - var/datum/weakref/bs_tx_weakref //Maybe misleading, this is the device to TRANSMIT TO - // For mappers or subtypes, to start them prelinked to these devices - var/bs_tx_preload_id - var/bs_rx_preload_id - - matter = list(MAT_GLASS = 25,MAT_STEEL = 75) - var/const/FREQ_LISTENING = 1 - var/list/internal_channels - - var/datum/radio_frequency/radio_connection - var/list/datum/radio_frequency/secure_radio_connections = new - -/obj/item/device/radio/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, RADIO_CHAT) - -/obj/item/device/radio/New() - ..() - wires = new(src) - internal_channels = default_internal_channels.Copy() - listening_objects += src - -/obj/item/device/radio/Destroy() - qdel(wires) - wires = null - listening_objects -= src - if(radio_controller) - radio_controller.remove_object(src, frequency) - for (var/ch_name in channels) - radio_controller.remove_object(src, radiochannels[ch_name]) - return ..() - - -/obj/item/device/radio/Initialize() - . = ..() - if(frequency < RADIO_LOW_FREQ || frequency > RADIO_HIGH_FREQ) - frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) - set_frequency(frequency) - - for (var/ch_name in channels) - secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) - - if(bluespace_radio) - if(bs_tx_preload_id) - //Try to find a receiver - for(var/obj/machinery/telecomms/receiver/RX in telecomms_list) - if(RX.id == bs_tx_preload_id) //Again, bs_tx is the thing to TRANSMIT TO, so a receiver. - bs_tx_weakref = WEAKREF(RX) - RX.link_radio(src) - break - //Hmm, howabout an AIO machine - if(!bs_tx_weakref) - for(var/obj/machinery/telecomms/allinone/AIO in telecomms_list) - if(AIO.id == bs_tx_preload_id) - bs_tx_weakref = WEAKREF(AIO) - AIO.link_radio(src) - break - if(!bs_tx_weakref) - testing("A radio [src] at [x],[y],[z] specified bluespace prelink IDs, but the machines with corresponding IDs ([bs_tx_preload_id], [bs_rx_preload_id]) couldn't be found.") - - if(bs_rx_preload_id) - var/found = 0 - //Try to find a transmitter - for(var/obj/machinery/telecomms/broadcaster/TX in telecomms_list) - if(TX.id == bs_rx_preload_id) //Again, bs_rx is the thing to RECEIVE FROM, so a transmitter. - TX.link_radio(src) - found = 1 - break - //Hmm, howabout an AIO machine - if(!found) - for(var/obj/machinery/telecomms/allinone/AIO in telecomms_list) - if(AIO.id == bs_rx_preload_id) - AIO.link_radio(src) - found = 1 - break - if(!found) - testing("A radio [src] at [x],[y],[z] specified bluespace prelink IDs, but the machines with corresponding IDs ([bs_tx_preload_id], [bs_rx_preload_id]) couldn't be found.") - -/obj/item/device/radio/proc/recalculateChannels() - return - -/obj/item/device/radio/attack_self(mob/user as mob) - user.set_machine(src) - interact(user) - -/obj/item/device/radio/interact(mob/user) - if(!user) - return FALSE - - if(b_stat) - wires.Interact(user) - - return tgui_interact(user) - -/obj/item/device/radio/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Radio", name, parent_ui) - ui.open() - -/obj/item/device/radio/tgui_data(mob/user) - var/data[0] - - data["rawfreq"] = frequency - data["listening"] = listening - data["broadcasting"] = broadcasting - data["subspace"] = subspace_transmission - data["subspaceSwitchable"] = subspace_switchable - data["loudspeaker"] = loudspeaker - - data["mic_cut"] = (wires.is_cut(WIRE_RADIO_TRANSMIT) || wires.is_cut(WIRE_RADIO_SIGNAL)) - data["spk_cut"] = (wires.is_cut(WIRE_RADIO_RECEIVER) || wires.is_cut(WIRE_RADIO_SIGNAL)) - - var/list/chanlist = list_channels(user) - if(islist(chanlist) && chanlist.len) - data["chan_list"] = chanlist - else - data["chan_list"] = null - - if(syndie) - data["useSyndMode"] = 1 - - data["minFrequency"] = PUBLIC_LOW_FREQ - data["maxFrequency"] = PUBLIC_HIGH_FREQ - - return data - -/obj/item/device/radio/proc/list_channels(var/mob/user) - return list_internal_channels(user) - -/obj/item/device/radio/proc/list_secure_channels(var/mob/user) - var/dat[0] - - for(var/ch_name in channels) - var/chan_stat = channels[ch_name] - var/listening = !!(chan_stat & FREQ_LISTENING) != 0 - - dat.Add(list(list("chan" = ch_name, "display_name" = ch_name, "secure_channel" = 1, "sec_channel_listen" = !listening, "freq" = radiochannels[ch_name]))) - - return dat - -/obj/item/device/radio/proc/list_internal_channels(var/mob/user) - var/dat[0] - for(var/internal_chan in internal_channels) - if(has_channel_access(user, internal_chan)) - dat.Add(list(list("chan" = internal_chan, "display_name" = get_frequency_name(text2num(internal_chan)), "freq" = text2num(internal_chan)))) - - return dat - -/obj/item/device/radio/proc/has_channel_access(var/mob/user, var/freq) - if(!user) - return FALSE - - if(!(freq in internal_channels)) - return FALSE - - return user.has_internal_radio_channel_access(internal_channels[freq]) - -/mob/proc/has_internal_radio_channel_access(var/list/req_one_accesses) - var/obj/item/weapon/card/id/I = GetIdCard() - return has_access(list(), req_one_accesses, I ? I.GetAccess() : list()) - -/mob/observer/dead/has_internal_radio_channel_access(var/list/req_one_accesses) - return can_admin_interact() - -/obj/item/device/radio/proc/text_sec_channel(var/chan_name, var/chan_stat) - var/list = !!(chan_stat&FREQ_LISTENING)!=0 - return {" - [chan_name]
                    - Speaker: [list ? "Engaged" : "Disengaged"]
                    - "} - -/obj/item/device/radio/proc/ToggleBroadcast() - broadcasting = !broadcasting && !(wires.is_cut(WIRE_RADIO_TRANSMIT) || wires.is_cut(WIRE_RADIO_SIGNAL)) - -/obj/item/device/radio/proc/ToggleReception() - listening = !listening && !(wires.is_cut(WIRE_RADIO_RECEIVER) || wires.is_cut(WIRE_RADIO_SIGNAL)) - -/obj/item/device/radio/CanUseTopic() - if(!on) - return STATUS_CLOSE - return ..() - -/obj/item/device/radio/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("setFrequency") - var/new_frequency = (text2num(params["freq"])) - if((new_frequency < PUBLIC_LOW_FREQ || new_frequency > PUBLIC_HIGH_FREQ)) - new_frequency = sanitize_frequency(new_frequency) - set_frequency(new_frequency) - if(hidden_uplink) - if(hidden_uplink.check_trigger(usr, frequency, traitor_frequency)) - usr << browse(null, "window=radio") - . = TRUE - if("broadcast") - ToggleBroadcast() - . = TRUE - if("listen") - ToggleReception() - . = TRUE - if("channel") - var/chan_name = params["channel"] - if(channels[chan_name] & FREQ_LISTENING) - channels[chan_name] &= ~FREQ_LISTENING - else - channels[chan_name] |= FREQ_LISTENING - . = TRUE - if("specFreq") - var/freq = params["channel"] - if(has_channel_access(usr, freq)) - set_frequency(text2num(freq)) - . = TRUE - if("subspace") - if(subspace_switchable) - subspace_transmission = !subspace_transmission - if(!subspace_transmission) - channels = list() - to_chat(usr, "Subspace Transmission is disabled") - else - recalculateChannels() - to_chat(usr, "Subspace Transmission is enabled") - . = TRUE - if("toggleLoudspeaker") - if(!subspace_switchable) - return - loudspeaker = !loudspeaker - - if(loudspeaker) - to_chat(usr, "Loadspeaker enabled.") - else - to_chat(usr, "Loadspeaker disabled.") - . = TRUE - - if(. && iscarbon(usr)) - playsound(src, "button", 10) - -GLOBAL_DATUM(autospeaker, /mob/living/silicon/ai/announcer) - -/obj/item/device/radio/proc/autosay(var/message, var/from, var/channel, var/list/zlevels, var/states) //VOREStation Edit - - if(!GLOB.autospeaker) - return - var/datum/radio_frequency/connection = null - if(channel && channels && channels.len > 0) - if(channel == "department") - channel = channels[1] - connection = secure_radio_connections[channel] - else - connection = radio_connection - channel = null - if(!istype(connection)) - return - - if(!LAZYLEN(zlevels)) - zlevels = list(0) - //VOREStation Edit Start - if(!states) - states = "states" - //VOREStation Edit End - GLOB.autospeaker.SetName(from) - Broadcast_Message(connection, GLOB.autospeaker, - 0, "*garbled automated announcement*", src, - message_to_multilingual(message, GLOB.all_languages[LANGUAGE_GALCOM]), from, "Automated Announcement", from, "synthesized voice", - DATA_FAKE, 0, zlevels, connection.frequency, states) //VOREStation Edit - -// Interprets the message mode when talking into a radio, possibly returning a connection datum -/obj/item/device/radio/proc/handle_message_mode(mob/living/M as mob, list/message_pieces, message_mode) - // If a channel isn't specified, send to common. - if(!message_mode || message_mode == "headset") - return radio_connection - - // Otherwise, if a channel is specified, look for it. - if(channels && channels.len > 0) - if (message_mode == "department") // Department radio shortcut - message_mode = channels[1] - - if (channels[message_mode]) // only broadcast if the channel is set on - return secure_radio_connections[message_mode] - - // If we were to send to a channel we don't have, drop it. - return RADIO_CONNECTION_FAIL - -/obj/item/device/radio/talk_into(mob/living/M as mob, list/message_pieces, channel, var/verb = "says") - if(!on) - return FALSE // the device has to be on - // Fix for permacell radios, but kinda eh about actually fixing them. - if(!M || !message_pieces) - return FALSE - - if(istype(M)) - M.trigger_aiming(TARGET_CAN_RADIO) - - // Uncommenting this. To the above comment: - // The permacell radios aren't suppose to be able to transmit, this isn't a bug and this "fix" is just making radio wires useless. -Giacom - if(wires.is_cut(WIRE_RADIO_TRANSMIT)) // The device has to have all its wires and shit intact - return FALSE - - if(!radio_connection) - set_frequency(frequency) - - /* Quick introduction: - This new radio system uses a very robust FTL signaling technology unoriginally - dubbed "subspace" which is somewhat similar to 'blue-space' but can't - actually transmit large mass. Headsets are the only radio devices capable - of sending subspace transmissions to the Communications Satellite. - - A headset sends a signal to a subspace listener/reciever elsewhere in space, - the signal gets processed and logged, and an audible transmission gets sent - to each individual headset. - */ - - //#### Grab the connection datum ####// - var/message_mode = handle_message_mode(M, message_pieces, channel) - switch(message_mode) - if(RADIO_CONNECTION_FAIL) - return FALSE - if(RADIO_CONNECTION_NON_SUBSPACE) - return TRUE - - if(!istype(message_mode, /datum/radio_frequency)) - return FALSE - - var/pos_z = get_z(src) - var/datum/radio_frequency/connection = message_mode - - //#### Tagging the signal with all appropriate identity values ####// - - // ||-- The mob's name identity --|| - var/displayname = M.name // grab the display name (name you get when you hover over someone's icon) - var/real_name = M.real_name // mob's real name - var/mobkey = "none" // player key associated with mob - var/voicemask = 0 // the speaker is wearing a voice mask - if(M.client) - mobkey = M.key // assign the mob's key - - - var/jobname // the mob's "job" - - // --- Human: use their actual job --- - if (ishuman(M)) - var/mob/living/carbon/human/H = M - jobname = H.get_assignment() - - // --- Carbon Nonhuman --- - else if (iscarbon(M)) // Nonhuman carbon mob - jobname = "No id" - - // --- AI --- - else if (isAI(M)) - jobname = "AI" - - // --- Cyborg --- - else if (isrobot(M)) - jobname = "Cyborg" - - // --- Personal AI (pAI) --- - else if (istype(M, /mob/living/silicon/pai)) - jobname = "Personal AI" - - // --- Unidentifiable mob --- - else - jobname = "Unknown" - - - // --- Modifications to the mob's identity --- - - // The mob is disguising their identity: - if (ishuman(M) && M.GetVoice() != real_name) - displayname = M.GetVoice() - jobname = "Unknown" - voicemask = 1 - - // First, we want to generate a new radio signal - var/datum/signal/signal = new - - // --- Finally, tag the actual signal with the appropriate values --- - signal.data = list( - // Identity-associated tags: - "mob" = M, // store a reference to the mob - "mobtype" = M.type, // the mob's type - "realname" = real_name, // the mob's real name - "name" = displayname, // the mob's display name - "job" = jobname, // the mob's job - "key" = mobkey, // the mob's key - "vmessage" = message_to_multilingual(pick(M.speak_emote)), // the message to display if the voice wasn't understood - "vname" = M.voice_name, // the name to display if the voice wasn't understood - "vmask" = voicemask, // 1 if the mob is using a voice gas mask - - // We store things that would otherwise be kept in the actual mob - // so that they can be logged even AFTER the mob is deleted or something - - // Other tags: - "compression" = rand(45,50), // compressed radio signal - "message" = message_pieces, // the actual sent message - "connection" = connection, // the radio connection to use - "radio" = src, // stores the radio used for transmission - "slow" = 0, // how much to sleep() before broadcasting - simulates net lag - "traffic" = 0, // dictates the total traffic sum that the signal went through - "type" = SIGNAL_NORMAL, // determines what type of radio input it is: normal broadcast - "server" = null, // the last server to log this signal - "reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery - "level" = pos_z, // The source's z level - "verb" = verb - ) - signal.frequency = connection.frequency // Quick frequency set - - var/filter_type = DATA_LOCAL //If we end up having to send it the old fashioned way, it's with this data var. - - /* ###### Bluespace radios talk directly to receivers (and only directly to receivers) ###### */ - if(bluespace_radio) - //Nothing to transmit to - if(!bs_tx_weakref) - to_chat(loc, "\The [src] buzzes to inform you of the lack of a functioning connection.") - return FALSE - - var/obj/machinery/telecomms/tx_to = bs_tx_weakref.resolve() - //Was linked, now destroyed or something - if(!tx_to) - bs_tx_weakref = null - to_chat(loc, "\The [src] buzzes to inform you of the lack of a functioning connection.") - return FALSE - - //Transmitted in the blind. If we get a message back, cool. If not, oh well. - signal.transmission_method = TRANSMISSION_BLUESPACE - return tx_to.receive_signal(signal) - - /* ###### Radios with subspace_transmission can only broadcast through subspace (unless they have adhoc_fallback) ###### */ - else if(subspace_transmission) - var/list/jamming = is_jammed(src) - if(jamming) - var/distance = 0 - var/area/our_area = get_area(src) - if(our_area.no_comms) - distance = 99 - else - distance = jamming["distance"] - to_chat(M, "\icon[src][bicon(src)] You hear the [distance <= 2 ? "loud hiss" : "soft hiss"] of static.") - return FALSE - - // First, we want to generate a new radio signal - signal.transmission_method = TRANSMISSION_SUBSPACE - - //#### Sending the signal to all subspace receivers ####// - for(var/obj/machinery/telecomms/receiver/R in telecomms_list) - R.receive_signal(signal) - - // Allinone can act as receivers. - for(var/obj/machinery/telecomms/allinone/R in telecomms_list) - R.receive_signal(signal) - - // Receiving code can be located in Telecommunications.dm - if(signal.data["done"] && (pos_z in signal.data["level"])) - return TRUE //Huzzah, sent via subspace - - else if(adhoc_fallback) //Less huzzah, we have to fallback - to_chat(loc, "\The [src] pings as it falls back to local radio transmission.") - subspace_transmission = FALSE - - else //Oh well - return FALSE - - /* ###### Intercoms and station-bounced radios ###### */ - else - /* --- Intercoms can only broadcast to other intercoms, but bounced radios can broadcast to bounced radios and intercoms --- */ - if(istype(src, /obj/item/device/radio/intercom)) - filter_type = DATA_INTERCOM - - /* --- Try to send a normal subspace broadcast first */ - signal.transmission_method = TRANSMISSION_SUBSPACE - signal.data["compression"] = 0 - - for(var/obj/machinery/telecomms/receiver/R in telecomms_list) - R.receive_signal(signal) - - // Allinone can act as receivers. - for(var/obj/machinery/telecomms/allinone/R in telecomms_list) - R.receive_signal(signal) - - for(var/obj/machinery/telecomms/receiver/R in telecomms_list) - R.receive_signal(signal) - - if(signal.data["done"] && (pos_z in signal.data["level"])) - if(adhoc_fallback) - to_chat(loc, "\The [src] pings as it reestablishes subspace communications.") - subspace_transmission = TRUE - // we're done here. - return TRUE - - //Nothing handled any sort of remote radio-ing and returned before now, just squawk on this zlevel. - return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), - src, message_pieces, displayname, jobname, real_name, M.voice_name, - filter_type, signal.data["compression"], using_map.get_map_levels(pos_z), connection.frequency, verb) - - -/obj/item/device/radio/hear_talk(mob/M as mob, list/message_pieces, var/verb = "says") - if(broadcasting) - if(get_dist(src, M) <= canhear_range) - talk_into(M, message_pieces, null, verb) - -/obj/item/device/radio/proc/receive_range(freq, level) - // check if this radio can receive on the given frequency, and if so, - // what the range is in which mobs will hear the radio - // returns: -1 if can't receive, range otherwise - if(wires.is_cut(WIRE_RADIO_RECEIVER)) - return -1 - if(!listening) - return -1 - if(is_jammed(src)) - return -1 - if(!(0 in level)) - var/pos_z = get_z(src) - if(!(pos_z in level)) - return -1 - if(freq in ANTAG_FREQS) - if(!(src.syndie))//Checks to see if it's allowed on that frequency, based on the encryption keys - return -1 - if(freq in CENT_FREQS) - if(!(src.centComm))//Checks to see if it's allowed on that frequency, based on the encryption keys - return -1 - if (!on) - return -1 - if (!freq) //recieved on main frequency - if (!listening) - return -1 - else - var/accept = (freq==frequency && listening) - if (!accept) - for (var/ch_name in channels) - var/datum/radio_frequency/RF = secure_radio_connections[ch_name] - if (RF && RF.frequency==freq && (channels[ch_name]&FREQ_LISTENING)) - accept = 1 - break - if (!accept) - return -1 - return canhear_range - -/obj/item/device/radio/proc/send_hear(freq, level) - var/range = receive_range(freq, level) - if(range > -1 && loudspeaker) - return get_mobs_or_objects_in_view(range, src) - - -/obj/item/device/radio/examine(mob/user) - . = ..() - - if((in_range(src, user) || loc == user)) - if(b_stat) - . += "\The [src] can be attached and modified!" - else - . += "\The [src] can not be modified or attached!" - -/obj/item/device/radio/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - user.set_machine(src) - if (!W.has_tool_quality(TOOL_SCREWDRIVER)) - return - b_stat = !( b_stat ) - if(!istype(src, /obj/item/device/radio/beacon)) - if (b_stat) - user.show_message("\The [src] can now be attached and modified!") - else - user.show_message("\The [src] can no longer be modified or attached!") - updateDialog() - //Foreach goto(83) - add_fingerprint(user) - return - else return - -/obj/item/device/radio/emp_act(severity) - broadcasting = 0 - listening = 0 - for (var/ch_name in channels) - channels[ch_name] = 0 - ..() - -/////////////////////////////// -//////////Borg Radios////////// -/////////////////////////////// -//Giving borgs their own radio to have some more room to work with -Sieve - -/obj/item/device/radio/borg - var/mob/living/silicon/robot/myborg = null // Cyborg which owns this radio. Used for power checks - var/obj/item/device/encryptionkey/keyslot = null//Borg radios can handle a single encryption key - icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component. - icon_state = "radio" - canhear_range = 0 - subspace_transmission = TRUE - subspace_switchable = TRUE - -/obj/item/device/radio/borg/Destroy() - myborg = null - return ..() - -/obj/item/device/radio/borg/list_channels(var/mob/user) - return list_secure_channels(user) - -/obj/item/device/radio/borg/talk_into() - . = ..() - if (isrobot(src.loc)) - var/mob/living/silicon/robot/R = src.loc - var/datum/robot_component/C = R.components["radio"] - R.cell_use_power(C.active_usage) - -/obj/item/device/radio/borg/attackby(obj/item/weapon/W as obj, mob/user as mob) -// ..() - user.set_machine(src) - if (!(W.has_tool_quality(TOOL_SCREWDRIVER) || istype(W, /obj/item/device/encryptionkey))) - return - - if(W.has_tool_quality(TOOL_SCREWDRIVER)) - if(keyslot) - - - for(var/ch_name in channels) - radio_controller.remove_object(src, radiochannels[ch_name]) - secure_radio_connections[ch_name] = null - - - if(keyslot) - var/turf/T = get_turf(user) - if(T) - keyslot.loc = T - keyslot = null - - recalculateChannels() - to_chat(user, "You pop out the encryption key in the radio!") - playsound(src, W.usesound, 50, 1) - - else - to_chat(user, "This radio doesn't have any encryption keys!") - - if(istype(W, /obj/item/device/encryptionkey/)) - if(keyslot) - to_chat(user, "The radio can't hold another key!") - return - - if(!keyslot) - user.drop_item() - W.loc = src - keyslot = W - - recalculateChannels() - - return - -/obj/item/device/radio/borg/recalculateChannels() - src.channels = list() - src.syndie = 0 - - var/mob/living/silicon/robot/D = src.loc - if(D.module) - for(var/ch_name in D.module.channels) - if(ch_name in src.channels) - continue - src.channels += ch_name - src.channels[ch_name] += D.module.channels[ch_name] - if(keyslot) - for(var/ch_name in keyslot.channels) - if(ch_name in src.channels) - continue - src.channels += ch_name - src.channels[ch_name] += keyslot.channels[ch_name] - - if(keyslot.syndie) - src.syndie = 1 - - for (var/ch_name in src.channels) - if(!radio_controller) - sleep(30) // Waiting for the radio_controller to be created. - if(!radio_controller) - src.name = "broken radio" - return - - secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) - - return - -/obj/item/device/radio/proc/config(op) - if(radio_controller) - for (var/ch_name in channels) - radio_controller.remove_object(src, radiochannels[ch_name]) - secure_radio_connections = new - channels = op - if(radio_controller) - for (var/ch_name in op) - secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) - return - -/obj/item/device/radio/off - listening = 0 - -/obj/item/device/radio/phone - broadcasting = 0 - icon = 'icons/obj/items.dmi' - icon_state = "red_phone" - listening = 1 - name = "phone" - anchored = FALSE - -/obj/item/device/radio/phone/medbay - frequency = MED_I_FREQ - -/obj/item/device/radio/phone/medbay/New() - ..() - internal_channels = default_medbay_channels.Copy() +// Access check is of the type requires one. These have been carefully selected to avoid allowing the janitor to see channels he shouldn't +//VOREStation Edit Start - Updating this for Virgo +var/global/list/default_internal_channels = list( + num2text(PUB_FREQ) = list(), + num2text(AI_FREQ) = list(access_synth), + num2text(ENT_FREQ) = list(), + num2text(ERT_FREQ) = list(access_cent_specops), + num2text(COMM_FREQ)= list(access_heads), + num2text(ENG_FREQ) = list(access_engine_equip, access_atmospherics), + num2text(MED_FREQ) = list(access_medical_equip), + num2text(MED_I_FREQ)=list(access_medical_equip), + num2text(SEC_FREQ) = list(access_security), + num2text(SEC_I_FREQ)=list(access_security), + num2text(SCI_FREQ) = list(access_tox, access_robotics, access_xenobiology), + num2text(SUP_FREQ) = list(access_cargo, access_mining_station), + num2text(SRV_FREQ) = list(access_janitor, access_library, access_hydroponics, access_bar, access_kitchen), + num2text(EXP_FREQ) = list(access_explorer) //VOREStation Edit +) + +var/global/list/default_medbay_channels = list( + num2text(PUB_FREQ) = list(), + num2text(MED_FREQ) = list(), + num2text(MED_I_FREQ) = list() +) +//VOREStation Edit End + +/obj/item/device/radio + icon = 'icons/obj/radio_vr.dmi' //VOREStation Edit + name = "shortwave radio" //VOREStation Edit + desc = "Used to talk to people when headsets don't function. Range is limited." + suffix = "\[3\]" + icon_state = "walkietalkie" + item_state = "radio" + + var/on = 1 // 0 for off + var/last_transmission + var/frequency = PUB_FREQ //common chat + var/traitor_frequency = 0 //tune to frequency to unlock traitor supplies + var/canhear_range = 3 // the range which mobs can hear this radio from + var/loudspeaker = TRUE // Allows borgs to disable canhear_range. + var/datum/wires/radio/wires = null + var/b_stat = 0 + var/broadcasting = 0 + var/listening = 1 + var/list/channels = list() //see communications.dm for full list. First channel is a "default" for :h + var/subspace_transmission = 0 + var/subspace_switchable = FALSE + var/adhoc_fallback = FALSE //Falls back to 'radio' mode if subspace not available + var/syndie = 0//Holder to see if it's a syndicate encrypted radio + var/centComm = 0//Holder to see if it's a CentCom encrypted radio + slot_flags = SLOT_BELT + throw_speed = 2 + throw_range = 9 + w_class = ITEMSIZE_SMALL + show_messages = 1 + + // Bluespace radios talk directly to telecomms equipment + var/bluespace_radio = FALSE + var/datum/weakref/bs_tx_weakref //Maybe misleading, this is the device to TRANSMIT TO + // For mappers or subtypes, to start them prelinked to these devices + var/bs_tx_preload_id + var/bs_rx_preload_id + + matter = list(MAT_GLASS = 25,MAT_STEEL = 75) + var/const/FREQ_LISTENING = 1 + var/list/internal_channels + + var/datum/radio_frequency/radio_connection + var/list/datum/radio_frequency/secure_radio_connections = new + +/obj/item/device/radio/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency, RADIO_CHAT) + +/obj/item/device/radio/New() + ..() + wires = new(src) + internal_channels = default_internal_channels.Copy() + listening_objects += src + +/obj/item/device/radio/Destroy() + qdel(wires) + wires = null + listening_objects -= src + if(radio_controller) + radio_controller.remove_object(src, frequency) + for (var/ch_name in channels) + radio_controller.remove_object(src, radiochannels[ch_name]) + return ..() + + +/obj/item/device/radio/Initialize() + . = ..() + if(frequency < RADIO_LOW_FREQ || frequency > RADIO_HIGH_FREQ) + frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(frequency) + + for (var/ch_name in channels) + secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) + + if(bluespace_radio) + if(bs_tx_preload_id) + //Try to find a receiver + for(var/obj/machinery/telecomms/receiver/RX in telecomms_list) + if(RX.id == bs_tx_preload_id) //Again, bs_tx is the thing to TRANSMIT TO, so a receiver. + bs_tx_weakref = WEAKREF(RX) + RX.link_radio(src) + break + //Hmm, howabout an AIO machine + if(!bs_tx_weakref) + for(var/obj/machinery/telecomms/allinone/AIO in telecomms_list) + if(AIO.id == bs_tx_preload_id) + bs_tx_weakref = WEAKREF(AIO) + AIO.link_radio(src) + break + if(!bs_tx_weakref) + testing("A radio [src] at [x],[y],[z] specified bluespace prelink IDs, but the machines with corresponding IDs ([bs_tx_preload_id], [bs_rx_preload_id]) couldn't be found.") + + if(bs_rx_preload_id) + var/found = 0 + //Try to find a transmitter + for(var/obj/machinery/telecomms/broadcaster/TX in telecomms_list) + if(TX.id == bs_rx_preload_id) //Again, bs_rx is the thing to RECEIVE FROM, so a transmitter. + TX.link_radio(src) + found = 1 + break + //Hmm, howabout an AIO machine + if(!found) + for(var/obj/machinery/telecomms/allinone/AIO in telecomms_list) + if(AIO.id == bs_rx_preload_id) + AIO.link_radio(src) + found = 1 + break + if(!found) + testing("A radio [src] at [x],[y],[z] specified bluespace prelink IDs, but the machines with corresponding IDs ([bs_tx_preload_id], [bs_rx_preload_id]) couldn't be found.") + +/obj/item/device/radio/proc/recalculateChannels() + return + +/obj/item/device/radio/attack_self(mob/user as mob) + user.set_machine(src) + interact(user) + +/obj/item/device/radio/interact(mob/user) + if(!user) + return FALSE + + if(b_stat) + wires.Interact(user) + + return tgui_interact(user) + +/obj/item/device/radio/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Radio", name, parent_ui) + ui.open() + +/obj/item/device/radio/tgui_data(mob/user) + var/data[0] + + data["rawfreq"] = frequency + data["listening"] = listening + data["broadcasting"] = broadcasting + data["subspace"] = subspace_transmission + data["subspaceSwitchable"] = subspace_switchable + data["loudspeaker"] = loudspeaker + + data["mic_cut"] = (wires.is_cut(WIRE_RADIO_TRANSMIT) || wires.is_cut(WIRE_RADIO_SIGNAL)) + data["spk_cut"] = (wires.is_cut(WIRE_RADIO_RECEIVER) || wires.is_cut(WIRE_RADIO_SIGNAL)) + + var/list/chanlist = list_channels(user) + if(islist(chanlist) && chanlist.len) + data["chan_list"] = chanlist + else + data["chan_list"] = null + + if(syndie) + data["useSyndMode"] = 1 + + data["minFrequency"] = PUBLIC_LOW_FREQ + data["maxFrequency"] = PUBLIC_HIGH_FREQ + + return data + +/obj/item/device/radio/proc/list_channels(var/mob/user) + return list_internal_channels(user) + +/obj/item/device/radio/proc/list_secure_channels(var/mob/user) + var/dat[0] + + for(var/ch_name in channels) + var/chan_stat = channels[ch_name] + var/listening = !!(chan_stat & FREQ_LISTENING) != 0 + + dat.Add(list(list("chan" = ch_name, "display_name" = ch_name, "secure_channel" = 1, "sec_channel_listen" = !listening, "freq" = radiochannels[ch_name]))) + + return dat + +/obj/item/device/radio/proc/list_internal_channels(var/mob/user) + var/dat[0] + for(var/internal_chan in internal_channels) + if(has_channel_access(user, internal_chan)) + dat.Add(list(list("chan" = internal_chan, "display_name" = get_frequency_name(text2num(internal_chan)), "freq" = text2num(internal_chan)))) + + return dat + +/obj/item/device/radio/proc/has_channel_access(var/mob/user, var/freq) + if(!user) + return FALSE + + if(!(freq in internal_channels)) + return FALSE + + return user.has_internal_radio_channel_access(internal_channels[freq]) + +/mob/proc/has_internal_radio_channel_access(var/list/req_one_accesses) + var/obj/item/weapon/card/id/I = GetIdCard() + return has_access(list(), req_one_accesses, I ? I.GetAccess() : list()) + +/mob/observer/dead/has_internal_radio_channel_access(var/list/req_one_accesses) + return can_admin_interact() + +/obj/item/device/radio/proc/text_sec_channel(var/chan_name, var/chan_stat) + var/list = !!(chan_stat&FREQ_LISTENING)!=0 + return {" + [chan_name]
                    + Speaker: [list ? "Engaged" : "Disengaged"]
                    + "} + +/obj/item/device/radio/proc/ToggleBroadcast() + broadcasting = !broadcasting && !(wires.is_cut(WIRE_RADIO_TRANSMIT) || wires.is_cut(WIRE_RADIO_SIGNAL)) + +/obj/item/device/radio/proc/ToggleReception() + listening = !listening && !(wires.is_cut(WIRE_RADIO_RECEIVER) || wires.is_cut(WIRE_RADIO_SIGNAL)) + +/obj/item/device/radio/CanUseTopic() + if(!on) + return STATUS_CLOSE + return ..() + +/obj/item/device/radio/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("setFrequency") + var/new_frequency = (text2num(params["freq"])) + if((new_frequency < PUBLIC_LOW_FREQ || new_frequency > PUBLIC_HIGH_FREQ)) + new_frequency = sanitize_frequency(new_frequency) + set_frequency(new_frequency) + if(hidden_uplink) + if(hidden_uplink.check_trigger(usr, frequency, traitor_frequency)) + usr << browse(null, "window=radio") + . = TRUE + if("broadcast") + ToggleBroadcast() + . = TRUE + if("listen") + ToggleReception() + . = TRUE + if("channel") + var/chan_name = params["channel"] + if(channels[chan_name] & FREQ_LISTENING) + channels[chan_name] &= ~FREQ_LISTENING + else + channels[chan_name] |= FREQ_LISTENING + . = TRUE + if("specFreq") + var/freq = params["channel"] + if(has_channel_access(usr, freq)) + set_frequency(text2num(freq)) + . = TRUE + if("subspace") + if(subspace_switchable) + subspace_transmission = !subspace_transmission + if(!subspace_transmission) + channels = list() + to_chat(usr, "Subspace Transmission is disabled") + else + recalculateChannels() + to_chat(usr, "Subspace Transmission is enabled") + . = TRUE + if("toggleLoudspeaker") + if(!subspace_switchable) + return + loudspeaker = !loudspeaker + + if(loudspeaker) + to_chat(usr, "Loadspeaker enabled.") + else + to_chat(usr, "Loadspeaker disabled.") + . = TRUE + + if(. && iscarbon(usr)) + playsound(src, "button", 10) + +GLOBAL_DATUM(autospeaker, /mob/living/silicon/ai/announcer) + +/obj/item/device/radio/proc/autosay(var/message, var/from, var/channel, var/list/zlevels, var/states) //VOREStation Edit + + if(!GLOB.autospeaker) + return + var/datum/radio_frequency/connection = null + if(channel && channels && channels.len > 0) + if(channel == "department") + channel = channels[1] + connection = secure_radio_connections[channel] + else + connection = radio_connection + channel = null + if(!istype(connection)) + return + + if(!LAZYLEN(zlevels)) + zlevels = list(0) + //VOREStation Edit Start + if(!states) + states = "states" + //VOREStation Edit End + GLOB.autospeaker.SetName(from) + Broadcast_Message(connection, GLOB.autospeaker, + 0, "*garbled automated announcement*", src, + message_to_multilingual(message, GLOB.all_languages[LANGUAGE_GALCOM]), from, "Automated Announcement", from, "synthesized voice", + DATA_FAKE, 0, zlevels, connection.frequency, states) //VOREStation Edit + +// Interprets the message mode when talking into a radio, possibly returning a connection datum +/obj/item/device/radio/proc/handle_message_mode(mob/living/M as mob, list/message_pieces, message_mode) + // If a channel isn't specified, send to common. + if(!message_mode || message_mode == "headset") + return radio_connection + + // Otherwise, if a channel is specified, look for it. + if(channels && channels.len > 0) + if (message_mode == "department") // Department radio shortcut + message_mode = channels[1] + + if (channels[message_mode]) // only broadcast if the channel is set on + return secure_radio_connections[message_mode] + + // If we were to send to a channel we don't have, drop it. + return RADIO_CONNECTION_FAIL + +/obj/item/device/radio/talk_into(mob/living/M as mob, list/message_pieces, channel, var/verb = "says") + if(!on) + return FALSE // the device has to be on + // Fix for permacell radios, but kinda eh about actually fixing them. + if(!M || !message_pieces) + return FALSE + + if(istype(M)) + M.trigger_aiming(TARGET_CAN_RADIO) + + // Uncommenting this. To the above comment: + // The permacell radios aren't suppose to be able to transmit, this isn't a bug and this "fix" is just making radio wires useless. -Giacom + if(wires.is_cut(WIRE_RADIO_TRANSMIT)) // The device has to have all its wires and shit intact + return FALSE + + if(!radio_connection) + set_frequency(frequency) + + /* Quick introduction: + This new radio system uses a very robust FTL signaling technology unoriginally + dubbed "subspace" which is somewhat similar to 'blue-space' but can't + actually transmit large mass. Headsets are the only radio devices capable + of sending subspace transmissions to the Communications Satellite. + + A headset sends a signal to a subspace listener/reciever elsewhere in space, + the signal gets processed and logged, and an audible transmission gets sent + to each individual headset. + */ + + //#### Grab the connection datum ####// + var/message_mode = handle_message_mode(M, message_pieces, channel) + switch(message_mode) + if(RADIO_CONNECTION_FAIL) + return FALSE + if(RADIO_CONNECTION_NON_SUBSPACE) + return TRUE + + if(!istype(message_mode, /datum/radio_frequency)) + return FALSE + + var/pos_z = get_z(src) + var/datum/radio_frequency/connection = message_mode + + //#### Tagging the signal with all appropriate identity values ####// + + // ||-- The mob's name identity --|| + var/displayname = M.name // grab the display name (name you get when you hover over someone's icon) + var/real_name = M.real_name // mob's real name + var/mobkey = "none" // player key associated with mob + var/voicemask = 0 // the speaker is wearing a voice mask + if(M.client) + mobkey = M.key // assign the mob's key + + + var/jobname // the mob's "job" + + // --- Human: use their actual job --- + if (ishuman(M)) + var/mob/living/carbon/human/H = M + jobname = H.get_assignment() + + // --- Carbon Nonhuman --- + else if (iscarbon(M)) // Nonhuman carbon mob + jobname = "No id" + + // --- AI --- + else if (isAI(M)) + jobname = "AI" + + // --- Cyborg --- + else if (isrobot(M)) + jobname = "Cyborg" + + // --- Personal AI (pAI) --- + else if (istype(M, /mob/living/silicon/pai)) + jobname = "Personal AI" + + // --- Unidentifiable mob --- + else + jobname = "Unknown" + + + // --- Modifications to the mob's identity --- + + // The mob is disguising their identity: + if (ishuman(M) && M.GetVoice() != real_name) + displayname = M.GetVoice() + jobname = "Unknown" + voicemask = 1 + + // First, we want to generate a new radio signal + var/datum/signal/signal = new + + // --- Finally, tag the actual signal with the appropriate values --- + signal.data = list( + // Identity-associated tags: + "mob" = M, // store a reference to the mob + "mobtype" = M.type, // the mob's type + "realname" = real_name, // the mob's real name + "name" = displayname, // the mob's display name + "job" = jobname, // the mob's job + "key" = mobkey, // the mob's key + "vmessage" = message_to_multilingual(pick(M.speak_emote)), // the message to display if the voice wasn't understood + "vname" = M.voice_name, // the name to display if the voice wasn't understood + "vmask" = voicemask, // 1 if the mob is using a voice gas mask + + // We store things that would otherwise be kept in the actual mob + // so that they can be logged even AFTER the mob is deleted or something + + // Other tags: + "compression" = rand(45,50), // compressed radio signal + "message" = message_pieces, // the actual sent message + "connection" = connection, // the radio connection to use + "radio" = src, // stores the radio used for transmission + "slow" = 0, // how much to sleep() before broadcasting - simulates net lag + "traffic" = 0, // dictates the total traffic sum that the signal went through + "type" = SIGNAL_NORMAL, // determines what type of radio input it is: normal broadcast + "server" = null, // the last server to log this signal + "reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery + "level" = pos_z, // The source's z level + "verb" = verb + ) + signal.frequency = connection.frequency // Quick frequency set + + var/filter_type = DATA_LOCAL //If we end up having to send it the old fashioned way, it's with this data var. + + /* ###### Bluespace radios talk directly to receivers (and only directly to receivers) ###### */ + if(bluespace_radio) + //Nothing to transmit to + if(!bs_tx_weakref) + to_chat(loc, "\The [src] buzzes to inform you of the lack of a functioning connection.") + return FALSE + + var/obj/machinery/telecomms/tx_to = bs_tx_weakref.resolve() + //Was linked, now destroyed or something + if(!tx_to) + bs_tx_weakref = null + to_chat(loc, "\The [src] buzzes to inform you of the lack of a functioning connection.") + return FALSE + + //Transmitted in the blind. If we get a message back, cool. If not, oh well. + signal.transmission_method = TRANSMISSION_BLUESPACE + return tx_to.receive_signal(signal) + + /* ###### Radios with subspace_transmission can only broadcast through subspace (unless they have adhoc_fallback) ###### */ + else if(subspace_transmission) + var/list/jamming = is_jammed(src) + if(jamming) + var/distance = 0 + var/area/our_area = get_area(src) + if(our_area.no_comms) + distance = 99 + else + distance = jamming["distance"] + to_chat(M, "\icon[src][bicon(src)] You hear the [distance <= 2 ? "loud hiss" : "soft hiss"] of static.") + return FALSE + + // First, we want to generate a new radio signal + signal.transmission_method = TRANSMISSION_SUBSPACE + + //#### Sending the signal to all subspace receivers ####// + for(var/obj/machinery/telecomms/receiver/R in telecomms_list) + R.receive_signal(signal) + + // Allinone can act as receivers. + for(var/obj/machinery/telecomms/allinone/R in telecomms_list) + R.receive_signal(signal) + + // Receiving code can be located in Telecommunications.dm + if(signal.data["done"] && (pos_z in signal.data["level"])) + return TRUE //Huzzah, sent via subspace + + else if(adhoc_fallback) //Less huzzah, we have to fallback + to_chat(loc, "\The [src] pings as it falls back to local radio transmission.") + subspace_transmission = FALSE + + else //Oh well + return FALSE + + /* ###### Intercoms and station-bounced radios ###### */ + else + /* --- Intercoms can only broadcast to other intercoms, but bounced radios can broadcast to bounced radios and intercoms --- */ + if(istype(src, /obj/item/device/radio/intercom)) + filter_type = DATA_INTERCOM + + /* --- Try to send a normal subspace broadcast first */ + signal.transmission_method = TRANSMISSION_SUBSPACE + signal.data["compression"] = 0 + + for(var/obj/machinery/telecomms/receiver/R in telecomms_list) + R.receive_signal(signal) + + // Allinone can act as receivers. + for(var/obj/machinery/telecomms/allinone/R in telecomms_list) + R.receive_signal(signal) + + for(var/obj/machinery/telecomms/receiver/R in telecomms_list) + R.receive_signal(signal) + + if(signal.data["done"] && (pos_z in signal.data["level"])) + if(adhoc_fallback) + to_chat(loc, "\The [src] pings as it reestablishes subspace communications.") + subspace_transmission = TRUE + // we're done here. + return TRUE + + //Nothing handled any sort of remote radio-ing and returned before now, just squawk on this zlevel. + return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), + src, message_pieces, displayname, jobname, real_name, M.voice_name, + filter_type, signal.data["compression"], using_map.get_map_levels(pos_z), connection.frequency, verb) + + +/obj/item/device/radio/hear_talk(mob/M as mob, list/message_pieces, var/verb = "says") + if(broadcasting) + if(get_dist(src, M) <= canhear_range) + talk_into(M, message_pieces, null, verb) + +/obj/item/device/radio/proc/receive_range(freq, level) + // check if this radio can receive on the given frequency, and if so, + // what the range is in which mobs will hear the radio + // returns: -1 if can't receive, range otherwise + if(wires.is_cut(WIRE_RADIO_RECEIVER)) + return -1 + if(!listening) + return -1 + if(is_jammed(src)) + return -1 + if(!(0 in level)) + var/pos_z = get_z(src) + if(!(pos_z in level)) + return -1 + if(freq in ANTAG_FREQS) + if(!(src.syndie))//Checks to see if it's allowed on that frequency, based on the encryption keys + return -1 + if(freq in CENT_FREQS) + if(!(src.centComm))//Checks to see if it's allowed on that frequency, based on the encryption keys + return -1 + if (!on) + return -1 + if (!freq) //recieved on main frequency + if (!listening) + return -1 + else + var/accept = (freq==frequency && listening) + if (!accept) + for (var/ch_name in channels) + var/datum/radio_frequency/RF = secure_radio_connections[ch_name] + if (RF && RF.frequency==freq && (channels[ch_name]&FREQ_LISTENING)) + accept = 1 + break + if (!accept) + return -1 + return canhear_range + +/obj/item/device/radio/proc/send_hear(freq, level) + var/range = receive_range(freq, level) + if(range > -1 && loudspeaker) + return get_mobs_or_objects_in_view(range, src) + + +/obj/item/device/radio/examine(mob/user) + . = ..() + + if((in_range(src, user) || loc == user)) + if(b_stat) + . += "\The [src] can be attached and modified!" + else + . += "\The [src] can not be modified or attached!" + +/obj/item/device/radio/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + user.set_machine(src) + if (!W.has_tool_quality(TOOL_SCREWDRIVER)) + return + b_stat = !( b_stat ) + if(!istype(src, /obj/item/device/radio/beacon)) + if (b_stat) + user.show_message("\The [src] can now be attached and modified!") + else + user.show_message("\The [src] can no longer be modified or attached!") + updateDialog() + //Foreach goto(83) + add_fingerprint(user) + return + else return + +/obj/item/device/radio/emp_act(severity) + broadcasting = 0 + listening = 0 + for (var/ch_name in channels) + channels[ch_name] = 0 + ..() + +/////////////////////////////// +//////////Borg Radios////////// +/////////////////////////////// +//Giving borgs their own radio to have some more room to work with -Sieve + +/obj/item/device/radio/borg + var/mob/living/silicon/robot/myborg = null // Cyborg which owns this radio. Used for power checks + var/obj/item/device/encryptionkey/keyslot = null//Borg radios can handle a single encryption key + icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component. + icon_state = "radio" + canhear_range = 0 + subspace_transmission = TRUE + subspace_switchable = TRUE + +/obj/item/device/radio/borg/Destroy() + myborg = null + return ..() + +/obj/item/device/radio/borg/list_channels(var/mob/user) + return list_secure_channels(user) + +/obj/item/device/radio/borg/talk_into() + . = ..() + if (isrobot(src.loc)) + var/mob/living/silicon/robot/R = src.loc + var/datum/robot_component/C = R.components["radio"] + R.cell_use_power(C.active_usage) + +/obj/item/device/radio/borg/attackby(obj/item/weapon/W as obj, mob/user as mob) +// ..() + user.set_machine(src) + if (!(W.has_tool_quality(TOOL_SCREWDRIVER) || istype(W, /obj/item/device/encryptionkey))) + return + + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(keyslot) + + + for(var/ch_name in channels) + radio_controller.remove_object(src, radiochannels[ch_name]) + secure_radio_connections[ch_name] = null + + + if(keyslot) + var/turf/T = get_turf(user) + if(T) + keyslot.loc = T + keyslot = null + + recalculateChannels() + to_chat(user, "You pop out the encryption key in the radio!") + playsound(src, W.usesound, 50, 1) + + else + to_chat(user, "This radio doesn't have any encryption keys!") + + if(istype(W, /obj/item/device/encryptionkey/)) + if(keyslot) + to_chat(user, "The radio can't hold another key!") + return + + if(!keyslot) + user.drop_item() + W.loc = src + keyslot = W + + recalculateChannels() + + return + +/obj/item/device/radio/borg/recalculateChannels() + src.channels = list() + src.syndie = 0 + + var/mob/living/silicon/robot/D = src.loc + if(D.module) + for(var/ch_name in D.module.channels) + if(ch_name in src.channels) + continue + src.channels += ch_name + src.channels[ch_name] += D.module.channels[ch_name] + if(keyslot) + for(var/ch_name in keyslot.channels) + if(ch_name in src.channels) + continue + src.channels += ch_name + src.channels[ch_name] += keyslot.channels[ch_name] + + if(keyslot.syndie) + src.syndie = 1 + + for (var/ch_name in src.channels) + if(!radio_controller) + sleep(30) // Waiting for the radio_controller to be created. + if(!radio_controller) + src.name = "broken radio" + return + + secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) + + return + +/obj/item/device/radio/proc/config(op) + if(radio_controller) + for (var/ch_name in channels) + radio_controller.remove_object(src, radiochannels[ch_name]) + secure_radio_connections = new + channels = op + if(radio_controller) + for (var/ch_name in op) + secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) + return + +/obj/item/device/radio/off + listening = 0 + +/obj/item/device/radio/phone + broadcasting = 0 + icon = 'icons/obj/items.dmi' + icon_state = "red_phone" + listening = 1 + name = "phone" + anchored = FALSE + +/obj/item/device/radio/phone/medbay + frequency = MED_I_FREQ + +/obj/item/device/radio/phone/medbay/New() + ..() + internal_channels = default_medbay_channels.Copy() diff --git a/code/game/objects/items/devices/spy_bug.dm b/code/game/objects/items/devices/spy_bug.dm index 5f6eed33279..61526248a5a 100644 --- a/code/game/objects/items/devices/spy_bug.dm +++ b/code/game/objects/items/devices/spy_bug.dm @@ -1,275 +1,275 @@ -/obj/item/device/camerabug - name = "mobile camera pod" - desc = "A camera pod used by tactical operators. Must be linked to a camera scanner unit." - icon = 'icons/obj/grenade.dmi' - icon_state = "camgrenade" - item_state = "empgrenade" - w_class = ITEMSIZE_SMALL - force = 0 - throwforce = 5.0 - throw_range = 15 - throw_speed = 3 - origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1) - var/obj/item/device/bug_monitor/linkedmonitor - var/brokentype = /obj/item/brokenbug - -// var/obj/item/device/radio/bug/radio - var/obj/machinery/camera/bug/camera - var/camtype = /obj/machinery/camera/bug - -/obj/item/device/camerabug/New() - ..() -// radio = new(src) - camera = new camtype(src) - -/obj/item/device/camerabug/attack_self(mob/user) - if(user.a_intent == I_HURT) - to_chat(user, "You crush the [src] under your foot, breaking it.") - visible_message("[user.name] crushes the [src] under their foot, breaking it!") - new brokentype(get_turf(src)) - spawn(0) - qdel(src) -/* else - user.set_machine(radio) - radio.interact(user) -*/ -/obj/item/device/camerabug/verb/reset() - set name = "Reset camera bug" - set category = "Object" - if(linkedmonitor) - linkedmonitor.unpair(src) - linkedmonitor = null - qdel(camera) - camera = new camtype(src) - to_chat(usr, "You turn the [src] off and on again, delinking it from any monitors.") - -/obj/item/brokenbug - name = "broken mobile camera pod" - desc = "A camera pod formerly used by tactical operators. The lens is smashed, and the circuits are damaged beyond repair." - icon = 'icons/obj/grenade.dmi' - icon_state = "camgrenadebroken" - item_state = "empgrenade" - force = 5.0 - w_class = ITEMSIZE_SMALL - throwforce = 5.0 - throw_range = 15 - throw_speed = 3 - origin_tech = list(TECH_ENGINEERING = 1) - -/obj/item/brokenbug/spy - name = "broken bug" - desc = "" //Even when it's broken it's inconspicuous - icon = 'icons/obj/weapons.dmi' - icon_state = "eshield" - item_state = "nothing" - layer = TURF_LAYER+0.2 - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - origin_tech = list(TECH_ENGINEERING = 1, TECH_ILLEGAL = 3) //crush it and you lose the data - force = 0 - throwforce = 5.0 - throw_range = 15 - throw_speed = 3 - -/obj/item/device/camerabug/spy - name = "bug" - desc = "" //Nothing to see here - icon = 'icons/obj/weapons.dmi' - icon_state = "eshield" - item_state = "nothing" - layer = TURF_LAYER+0.2 - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1, TECH_ILLEGAL = 3) - camtype = /obj/machinery/camera/bug/spy - -/obj/item/device/camerabug/examine(mob/user) - . = ..() - if(get_dist(user, src) == 0) - . += "It has a tiny camera inside. Needs to be both configured and brought in contact with monitor device to be fully functional." - -/obj/item/device/camerabug/update_icon() - ..() - - if(anchored) // Standard versions are relatively obvious if not hidden in a container. Anchoring them is advised, to disguise them. - alpha = 50 - else - alpha = 255 - -/obj/item/device/camerabug/attackby(obj/item/W as obj, mob/living/user as mob) - if(istype(W, /obj/item/device/bug_monitor)) - var/obj/item/device/bug_monitor/SM = W - if(!linkedmonitor) - to_chat(user, "\The [src] has been paired with \the [SM].") - SM.pair(src) - linkedmonitor = SM - else if (linkedmonitor == SM) - to_chat(user, "\The [src] has been unpaired from \the [SM].") - linkedmonitor.unpair(src) - linkedmonitor = null - else - to_chat(user, "Error: The device is linked to another monitor.") - - else if(W.has_tool_quality(TOOL_WRENCH) && user.a_intent != I_HURT) - if(isturf(loc)) - anchored = !anchored - - to_chat(user, "You [anchored ? "" : "un"]secure \the [src].") - - update_icon() - return - else - if(W.force >= 5) - visible_message("\The [src] lens shatters!") - new brokentype(get_turf(src)) - if(linkedmonitor) - linkedmonitor.unpair(src) - linkedmonitor = null - spawn(0) - qdel(src) - ..() - -/obj/item/device/camerabug/bullet_act() - visible_message("The [src] lens shatters!") - new brokentype(get_turf(src)) - if(linkedmonitor) - linkedmonitor.unpair(src) - linkedmonitor = null - spawn(0) - qdel(src) - -/obj/item/device/camerabug/Destroy() - if(linkedmonitor) - linkedmonitor.unpair(src) - linkedmonitor = null - ..() - -/obj/item/device/bug_monitor - name = "mobile camera pod monitor" - desc = "A portable camera console designed to work with mobile camera pods." - icon = 'icons/obj/device.dmi' - icon_state = "forensic0" - item_state = "electronic" - w_class = ITEMSIZE_SMALL - origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1) - - var/operating = 0 -// var/obj/item/device/radio/bug/radio - var/obj/machinery/camera/bug/selected_camera - var/list/obj/machinery/camera/bug/cameras = new() -/* -/obj/item/device/bug_monitor/New() - radio = new(src) -*/ -/obj/item/device/bug_monitor/attack_self(mob/user) - if(operating) - return - -// radio.attack_self(user) - view_cameras(user) - -/obj/item/device/bug_monitor/attackby(obj/item/W as obj, mob/living/user as mob) - if(istype(W, /obj/item/device/camerabug)) - W.attackby(src, user) - else - return ..() - -/obj/item/device/bug_monitor/proc/unpair(var/obj/item/device/camerabug/SB) - if(SB.camera in cameras) - cameras -= SB.camera - -/obj/item/device/bug_monitor/proc/pair(var/obj/item/device/camerabug/SB) - cameras += SB.camera - -/obj/item/device/bug_monitor/proc/view_cameras(mob/user) - if(!can_use_cam(user)) - return - - selected_camera = cameras[1] - user.reset_view(selected_camera) - view_camera(user) - - operating = 1 - while(selected_camera && Adjacent(user)) - selected_camera = tgui_input_list(usr, "Select camera to view.", "Camera Choice", cameras) - selected_camera = null - operating = 0 - -/obj/item/device/bug_monitor/proc/view_camera(mob/user) - spawn(0) - while(selected_camera && Adjacent(user)) - var/turf/T = get_turf(selected_camera) - if(!T || !is_on_same_plane_or_station(T.z, user.z) || !selected_camera.can_use()) - user.unset_machine() - user.reset_view(null) - to_chat(user, "Link to [selected_camera] has been lost.") - src.unpair(selected_camera.loc) - sleep(90) - else - user.set_machine(selected_camera) - user.reset_view(selected_camera) - sleep(10) - user.unset_machine() - user.reset_view(null) - -/obj/item/device/bug_monitor/proc/can_use_cam(mob/user) - if(operating) - return - - if(!cameras.len) - to_chat(user, "No paired cameras detected!") - to_chat(user, "Bring a camera in contact with this device to pair the camera.") - return - - return 1 - -/obj/item/device/bug_monitor/spy - name = "\improper PDA" - desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge." - icon = 'icons/obj/pda.dmi' - icon_state = "pda" - item_state = "electronic" - origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1, TECH_ILLEGAL = 3) - -/obj/item/device/bug_monitor/spy/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "The time '12:00' is blinking in the corner of the screen and \the [src] looks very cheaply made." - -/obj/machinery/camera/bug/check_eye(var/mob/user as mob) - return 0 - -/obj/machinery/camera/bug - network = list(NETWORK_SECURITY) - -/obj/machinery/camera/bug/New() - ..() - name = "Camera #[rand(1000,9999)]" - c_tag = name - -/obj/machinery/camera/bug/spy - // These cheap toys are accessible from the mercenary camera console as well - only the antag ones though! - network = list(NETWORK_MERCENARY) - -/obj/machinery/camera/bug/spy/New() - ..() - name = "DV-136ZB #[rand(1000,9999)]" - c_tag = name - -/* //These were originally supposed to have radios in them. Doesn't work. -/obj/item/device/radio/bug - listening = 0 //turn it on first - frequency = 1359 //sec comms - broadcasting = 0 - canhear_range = 1 - name = "camera bug device" - icon_state = "syn_cypherkey" - -/obj/item/device/radio/bug/spy - listening = 0 - frequency = 1473 - broadcasting = 0 - canhear_range = 1 - name = "spy device" - icon_state = "syn_cypherkey" +/obj/item/device/camerabug + name = "mobile camera pod" + desc = "A camera pod used by tactical operators. Must be linked to a camera scanner unit." + icon = 'icons/obj/grenade.dmi' + icon_state = "camgrenade" + item_state = "empgrenade" + w_class = ITEMSIZE_SMALL + force = 0 + throwforce = 5.0 + throw_range = 15 + throw_speed = 3 + origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1) + var/obj/item/device/bug_monitor/linkedmonitor + var/brokentype = /obj/item/brokenbug + +// var/obj/item/device/radio/bug/radio + var/obj/machinery/camera/bug/camera + var/camtype = /obj/machinery/camera/bug + +/obj/item/device/camerabug/New() + ..() +// radio = new(src) + camera = new camtype(src) + +/obj/item/device/camerabug/attack_self(mob/user) + if(user.a_intent == I_HURT) + to_chat(user, "You crush the [src] under your foot, breaking it.") + visible_message("[user.name] crushes the [src] under their foot, breaking it!") + new brokentype(get_turf(src)) + spawn(0) + qdel(src) +/* else + user.set_machine(radio) + radio.interact(user) +*/ +/obj/item/device/camerabug/verb/reset() + set name = "Reset camera bug" + set category = "Object" + if(linkedmonitor) + linkedmonitor.unpair(src) + linkedmonitor = null + qdel(camera) + camera = new camtype(src) + to_chat(usr, "You turn the [src] off and on again, delinking it from any monitors.") + +/obj/item/brokenbug + name = "broken mobile camera pod" + desc = "A camera pod formerly used by tactical operators. The lens is smashed, and the circuits are damaged beyond repair." + icon = 'icons/obj/grenade.dmi' + icon_state = "camgrenadebroken" + item_state = "empgrenade" + force = 5.0 + w_class = ITEMSIZE_SMALL + throwforce = 5.0 + throw_range = 15 + throw_speed = 3 + origin_tech = list(TECH_ENGINEERING = 1) + +/obj/item/brokenbug/spy + name = "broken bug" + desc = "" //Even when it's broken it's inconspicuous + icon = 'icons/obj/weapons.dmi' + icon_state = "eshield" + item_state = "nothing" + layer = TURF_LAYER+0.2 + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + origin_tech = list(TECH_ENGINEERING = 1, TECH_ILLEGAL = 3) //crush it and you lose the data + force = 0 + throwforce = 5.0 + throw_range = 15 + throw_speed = 3 + +/obj/item/device/camerabug/spy + name = "bug" + desc = "" //Nothing to see here + icon = 'icons/obj/weapons.dmi' + icon_state = "eshield" + item_state = "nothing" + layer = TURF_LAYER+0.2 + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1, TECH_ILLEGAL = 3) + camtype = /obj/machinery/camera/bug/spy + +/obj/item/device/camerabug/examine(mob/user) + . = ..() + if(get_dist(user, src) == 0) + . += "It has a tiny camera inside. Needs to be both configured and brought in contact with monitor device to be fully functional." + +/obj/item/device/camerabug/update_icon() + ..() + + if(anchored) // Standard versions are relatively obvious if not hidden in a container. Anchoring them is advised, to disguise them. + alpha = 50 + else + alpha = 255 + +/obj/item/device/camerabug/attackby(obj/item/W as obj, mob/living/user as mob) + if(istype(W, /obj/item/device/bug_monitor)) + var/obj/item/device/bug_monitor/SM = W + if(!linkedmonitor) + to_chat(user, "\The [src] has been paired with \the [SM].") + SM.pair(src) + linkedmonitor = SM + else if (linkedmonitor == SM) + to_chat(user, "\The [src] has been unpaired from \the [SM].") + linkedmonitor.unpair(src) + linkedmonitor = null + else + to_chat(user, "Error: The device is linked to another monitor.") + + else if(W.has_tool_quality(TOOL_WRENCH) && user.a_intent != I_HURT) + if(isturf(loc)) + anchored = !anchored + + to_chat(user, "You [anchored ? "" : "un"]secure \the [src].") + + update_icon() + return + else + if(W.force >= 5) + visible_message("\The [src] lens shatters!") + new brokentype(get_turf(src)) + if(linkedmonitor) + linkedmonitor.unpair(src) + linkedmonitor = null + spawn(0) + qdel(src) + ..() + +/obj/item/device/camerabug/bullet_act() + visible_message("The [src] lens shatters!") + new brokentype(get_turf(src)) + if(linkedmonitor) + linkedmonitor.unpair(src) + linkedmonitor = null + spawn(0) + qdel(src) + +/obj/item/device/camerabug/Destroy() + if(linkedmonitor) + linkedmonitor.unpair(src) + linkedmonitor = null + ..() + +/obj/item/device/bug_monitor + name = "mobile camera pod monitor" + desc = "A portable camera console designed to work with mobile camera pods." + icon = 'icons/obj/device.dmi' + icon_state = "forensic0" + item_state = "electronic" + w_class = ITEMSIZE_SMALL + origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1) + + var/operating = 0 +// var/obj/item/device/radio/bug/radio + var/obj/machinery/camera/bug/selected_camera + var/list/obj/machinery/camera/bug/cameras = new() +/* +/obj/item/device/bug_monitor/New() + radio = new(src) +*/ +/obj/item/device/bug_monitor/attack_self(mob/user) + if(operating) + return + +// radio.attack_self(user) + view_cameras(user) + +/obj/item/device/bug_monitor/attackby(obj/item/W as obj, mob/living/user as mob) + if(istype(W, /obj/item/device/camerabug)) + W.attackby(src, user) + else + return ..() + +/obj/item/device/bug_monitor/proc/unpair(var/obj/item/device/camerabug/SB) + if(SB.camera in cameras) + cameras -= SB.camera + +/obj/item/device/bug_monitor/proc/pair(var/obj/item/device/camerabug/SB) + cameras += SB.camera + +/obj/item/device/bug_monitor/proc/view_cameras(mob/user) + if(!can_use_cam(user)) + return + + selected_camera = cameras[1] + user.reset_view(selected_camera) + view_camera(user) + + operating = 1 + while(selected_camera && Adjacent(user)) + selected_camera = tgui_input_list(usr, "Select camera to view.", "Camera Choice", cameras) + selected_camera = null + operating = 0 + +/obj/item/device/bug_monitor/proc/view_camera(mob/user) + spawn(0) + while(selected_camera && Adjacent(user)) + var/turf/T = get_turf(selected_camera) + if(!T || !is_on_same_plane_or_station(T.z, user.z) || !selected_camera.can_use()) + user.unset_machine() + user.reset_view(null) + to_chat(user, "Link to [selected_camera] has been lost.") + src.unpair(selected_camera.loc) + sleep(90) + else + user.set_machine(selected_camera) + user.reset_view(selected_camera) + sleep(10) + user.unset_machine() + user.reset_view(null) + +/obj/item/device/bug_monitor/proc/can_use_cam(mob/user) + if(operating) + return + + if(!cameras.len) + to_chat(user, "No paired cameras detected!") + to_chat(user, "Bring a camera in contact with this device to pair the camera.") + return + + return 1 + +/obj/item/device/bug_monitor/spy + name = "\improper PDA" + desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge." + icon = 'icons/obj/pda.dmi' + icon_state = "pda" + item_state = "electronic" + origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1, TECH_ILLEGAL = 3) + +/obj/item/device/bug_monitor/spy/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "The time '12:00' is blinking in the corner of the screen and \the [src] looks very cheaply made." + +/obj/machinery/camera/bug/check_eye(var/mob/user as mob) + return 0 + +/obj/machinery/camera/bug + network = list(NETWORK_SECURITY) + +/obj/machinery/camera/bug/New() + ..() + name = "Camera #[rand(1000,9999)]" + c_tag = name + +/obj/machinery/camera/bug/spy + // These cheap toys are accessible from the mercenary camera console as well - only the antag ones though! + network = list(NETWORK_MERCENARY) + +/obj/machinery/camera/bug/spy/New() + ..() + name = "DV-136ZB #[rand(1000,9999)]" + c_tag = name + +/* //These were originally supposed to have radios in them. Doesn't work. +/obj/item/device/radio/bug + listening = 0 //turn it on first + frequency = 1359 //sec comms + broadcasting = 0 + canhear_range = 1 + name = "camera bug device" + icon_state = "syn_cypherkey" + +/obj/item/device/radio/bug/spy + listening = 0 + frequency = 1473 + broadcasting = 0 + canhear_range = 1 + name = "spy device" + icon_state = "syn_cypherkey" */ \ No newline at end of file diff --git a/code/game/objects/items/devices/t_scanner.dm b/code/game/objects/items/devices/t_scanner.dm index 415f22d8d80..a1c8c6c7228 100644 --- a/code/game/objects/items/devices/t_scanner.dm +++ b/code/game/objects/items/devices/t_scanner.dm @@ -1,150 +1,150 @@ -#define OVERLAY_CACHE_LEN 50 - -/obj/item/device/t_scanner - name = "\improper T-ray scanner" - desc = "A terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." - icon_state = "t-ray0" - item_state = "t-ray" - slot_flags = SLOT_BELT - w_class = ITEMSIZE_SMALL - matter = list(MAT_STEEL = 150) - origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) - - var/scan_range = 1 - - var/on = 0 - var/list/active_scanned = list() //assoc list of objects being scanned, mapped to their overlay - var/client/user_client //since making sure overlays are properly added and removed is pretty important, so we track the current user explicitly - var/flicker = 0 - - var/global/list/overlay_cache = list() //cache recent overlays - -/obj/item/device/t_scanner/update_icon() - icon_state = "t-ray[on]" - -/obj/item/device/t_scanner/attack_self(mob/user) - set_active(!on) - -/obj/item/device/t_scanner/proc/set_active(var/active) - on = active - if(on) - START_PROCESSING(SSobj, src) - flicker = 0 - else - STOP_PROCESSING(SSobj, src) - set_user_client(null) - update_icon() - -//If reset is set, then assume the client has none of our overlays, otherwise we only send new overlays. -/obj/item/device/t_scanner/process() - if(!on) return - - //handle clients changing - var/client/loc_client = null - if(ismob(src.loc)) - var/mob/M = src.loc - loc_client = M.client - set_user_client(loc_client) - - //no sense processing if no-one is going to see it. - if(!user_client) return - - //get all objects in scan range - var/list/scanned = get_scanned_objects(scan_range) - var/list/update_add = scanned - active_scanned - var/list/update_remove = active_scanned - scanned - - //Add new overlays - for(var/obj/O in update_add) - var/image/overlay = get_overlay(O) - active_scanned[O] = overlay - user_client.images += overlay - - //Remove stale overlays - for(var/obj/O in update_remove) - user_client.images -= active_scanned[O] - active_scanned -= O - - //Flicker effect - for(var/obj/O in active_scanned) - var/image/overlay = active_scanned[O] - if(flicker) - overlay.alpha = 0 - else - overlay.alpha = 128 - flicker = !flicker - -//creates a new overlay for a scanned object -/obj/item/device/t_scanner/proc/get_overlay(obj/scanned) - //Use a cache so we don't create a whole bunch of new images just because someone's walking back and forth in a room. - //Also means that images are reused if multiple people are using t-rays to look at the same objects. - if(scanned in overlay_cache) - . = overlay_cache[scanned] - else - var/image/I = image(loc = scanned, icon = scanned.icon, icon_state = scanned.icon_state, layer = HUD_LAYER) - - //Pipes are special - if(istype(scanned, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/P = scanned - I.color = P.pipe_color - I.add_overlay(P.overlays) - - I.alpha = 128 - I.mouse_opacity = 0 - . = I - - // Add it to cache, cutting old entries if the list is too long - overlay_cache[scanned] = . - if(overlay_cache.len > OVERLAY_CACHE_LEN) - overlay_cache.Cut(1, overlay_cache.len-OVERLAY_CACHE_LEN-1) - -/obj/item/device/t_scanner/proc/get_scanned_objects(var/scan_dist) - . = list() - - var/turf/center = get_turf(src.loc) - if(!center) return - - for(var/turf/T in range(scan_range, center)) - if(!!T.is_plating()) - continue - - for(var/obj/O in T.contents) - if(O.level != 1) - continue - if(!O.invisibility) - continue //if it's already visible don't need an overlay for it - . += O - -/obj/item/device/t_scanner/proc/set_user_client(var/client/new_client) - if(new_client == user_client) - return - if(user_client) - for(var/scanned in active_scanned) - user_client.images -= active_scanned[scanned] - if(new_client) - for(var/scanned in active_scanned) - new_client.images += active_scanned[scanned] - else - active_scanned.Cut() - - user_client = new_client - -/obj/item/device/t_scanner/dropped(mob/user) - set_user_client(null) - -/obj/item/device/t_scanner/upgraded - name = "Upgraded T-ray Scanner" - desc = "An upgraded version of the terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." - matter = list(MAT_STEEL = 500, PHORON = 150) - origin_tech = list(TECH_MAGNET = 4, TECH_ENGINEERING = 5) - scan_range = 3 - -/obj/item/device/t_scanner/advanced - name = "Advanced T-ray Scanner" - desc = "An advanced version of the terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." - matter = list(MAT_STEEL = 1500, PHORON = 200, SILVER = 250) - origin_tech = list(TECH_MAGNET = 7, TECH_ENGINEERING = 7, TECH_MATERIAL = 6) - scan_range = 7 - - +#define OVERLAY_CACHE_LEN 50 + +/obj/item/device/t_scanner + name = "\improper T-ray scanner" + desc = "A terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." + icon_state = "t-ray0" + item_state = "t-ray" + slot_flags = SLOT_BELT + w_class = ITEMSIZE_SMALL + matter = list(MAT_STEEL = 150) + origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) + + var/scan_range = 1 + + var/on = 0 + var/list/active_scanned = list() //assoc list of objects being scanned, mapped to their overlay + var/client/user_client //since making sure overlays are properly added and removed is pretty important, so we track the current user explicitly + var/flicker = 0 + + var/global/list/overlay_cache = list() //cache recent overlays + +/obj/item/device/t_scanner/update_icon() + icon_state = "t-ray[on]" + +/obj/item/device/t_scanner/attack_self(mob/user) + set_active(!on) + +/obj/item/device/t_scanner/proc/set_active(var/active) + on = active + if(on) + START_PROCESSING(SSobj, src) + flicker = 0 + else + STOP_PROCESSING(SSobj, src) + set_user_client(null) + update_icon() + +//If reset is set, then assume the client has none of our overlays, otherwise we only send new overlays. +/obj/item/device/t_scanner/process() + if(!on) return + + //handle clients changing + var/client/loc_client = null + if(ismob(src.loc)) + var/mob/M = src.loc + loc_client = M.client + set_user_client(loc_client) + + //no sense processing if no-one is going to see it. + if(!user_client) return + + //get all objects in scan range + var/list/scanned = get_scanned_objects(scan_range) + var/list/update_add = scanned - active_scanned + var/list/update_remove = active_scanned - scanned + + //Add new overlays + for(var/obj/O in update_add) + var/image/overlay = get_overlay(O) + active_scanned[O] = overlay + user_client.images += overlay + + //Remove stale overlays + for(var/obj/O in update_remove) + user_client.images -= active_scanned[O] + active_scanned -= O + + //Flicker effect + for(var/obj/O in active_scanned) + var/image/overlay = active_scanned[O] + if(flicker) + overlay.alpha = 0 + else + overlay.alpha = 128 + flicker = !flicker + +//creates a new overlay for a scanned object +/obj/item/device/t_scanner/proc/get_overlay(obj/scanned) + //Use a cache so we don't create a whole bunch of new images just because someone's walking back and forth in a room. + //Also means that images are reused if multiple people are using t-rays to look at the same objects. + if(scanned in overlay_cache) + . = overlay_cache[scanned] + else + var/image/I = image(loc = scanned, icon = scanned.icon, icon_state = scanned.icon_state, layer = HUD_LAYER) + + //Pipes are special + if(istype(scanned, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/P = scanned + I.color = P.pipe_color + I.add_overlay(P.overlays) + + I.alpha = 128 + I.mouse_opacity = 0 + . = I + + // Add it to cache, cutting old entries if the list is too long + overlay_cache[scanned] = . + if(overlay_cache.len > OVERLAY_CACHE_LEN) + overlay_cache.Cut(1, overlay_cache.len-OVERLAY_CACHE_LEN-1) + +/obj/item/device/t_scanner/proc/get_scanned_objects(var/scan_dist) + . = list() + + var/turf/center = get_turf(src.loc) + if(!center) return + + for(var/turf/T in range(scan_range, center)) + if(!!T.is_plating()) + continue + + for(var/obj/O in T.contents) + if(O.level != 1) + continue + if(!O.invisibility) + continue //if it's already visible don't need an overlay for it + . += O + +/obj/item/device/t_scanner/proc/set_user_client(var/client/new_client) + if(new_client == user_client) + return + if(user_client) + for(var/scanned in active_scanned) + user_client.images -= active_scanned[scanned] + if(new_client) + for(var/scanned in active_scanned) + new_client.images += active_scanned[scanned] + else + active_scanned.Cut() + + user_client = new_client + +/obj/item/device/t_scanner/dropped(mob/user) + set_user_client(null) + +/obj/item/device/t_scanner/upgraded + name = "Upgraded T-ray Scanner" + desc = "An upgraded version of the terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." + matter = list(MAT_STEEL = 500, PHORON = 150) + origin_tech = list(TECH_MAGNET = 4, TECH_ENGINEERING = 5) + scan_range = 3 + +/obj/item/device/t_scanner/advanced + name = "Advanced T-ray Scanner" + desc = "An advanced version of the terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." + matter = list(MAT_STEEL = 1500, PHORON = 200, SILVER = 250) + origin_tech = list(TECH_MAGNET = 7, TECH_ENGINEERING = 7, TECH_MATERIAL = 6) + scan_range = 7 + + #undef OVERLAY_CACHE_LEN \ No newline at end of file diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm index 4294b56f85d..b59bea3d3df 100644 --- a/code/game/objects/items/devices/taperecorder.dm +++ b/code/game/objects/items/devices/taperecorder.dm @@ -1,436 +1,436 @@ -/obj/item/device/taperecorder - name = "universal recorder" - desc = "A device that can record to cassette tapes, and play them. It automatically translates the content in playback." - icon_state = "taperecorder_empty" - item_state = "analyzer" - w_class = ITEMSIZE_SMALL - - matter = list(MAT_STEEL = 60,MAT_GLASS = 30) - - var/emagged = 0.0 - var/recording = 0.0 - var/playing = 0.0 - var/playsleepseconds = 0.0 - var/obj/item/device/tape/mytape = /obj/item/device/tape/random - var/canprint = 1 - slot_flags = SLOT_BELT - throwforce = 2 - throw_speed = 4 - throw_range = 20 - -/obj/item/device/taperecorder/New() - ..() - if(ispath(mytape)) - mytape = new mytape(src) - update_icon() - listening_objects += src - -/obj/item/device/taperecorder/empty - mytape = null - -/obj/item/device/taperecorder/Destroy() - listening_objects -= src - if(mytape) - qdel(mytape) - mytape = null - return ..() - - -/obj/item/device/taperecorder/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/device/tape)) - if(mytape) - to_chat(user, "There's already a tape inside.") - return - if(!user.unEquip(I)) - return - I.forceMove(src) - mytape = I - to_chat(user, "You insert [I] into [src].") - update_icon() - return - ..() - - -/obj/item/device/taperecorder/fire_act() - if(mytape) - mytape.ruin() //Fires destroy the tape - return ..() - - -/obj/item/device/taperecorder/attack_hand(mob/user) - if(user.get_inactive_hand() == src) - if(mytape) - eject() - return - ..() - - -/obj/item/device/taperecorder/verb/eject() - set name = "Eject Tape" - set category = "Object" - - if(usr.incapacitated()) - return - if(!mytape) - to_chat(usr, "There's no tape in \the [src].") - return - if(emagged) - to_chat(usr, "The tape seems to be stuck inside.") - return - - if(playing || recording) - stop() - to_chat(usr, "You remove [mytape] from [src].") - usr.put_in_hands(mytape) - mytape = null - update_icon() - - -/obj/item/device/taperecorder/hear_talk(mob/M, list/message_pieces, verb) - var/msg = multilingual_to_message(message_pieces, requires_machine_understands = TRUE, with_capitalization = TRUE) - var/voice = M.GetVoice() //Defined on living, returns name for normal mobs/ - if(mytape && recording) - mytape.record_speech("[voice] [verb], \"[msg]\"") - - -/obj/item/device/taperecorder/see_emote(mob/M as mob, text, var/emote_type) - if(emote_type != 2) //only hearable emotes - return - if(mytape && recording) - mytape.record_speech("[strip_html_properly(text)]") - - -/obj/item/device/taperecorder/show_message(msg, type, alt, alt_type) - var/recordedtext - if (msg && type == 2) //must be hearable - recordedtext = msg - else if (alt && alt_type == 2) - recordedtext = alt - else - return - if(mytape && recording) - mytape.record_noise("[strip_html_properly(recordedtext)]") - -/obj/item/device/taperecorder/emag_act(var/remaining_charges, var/mob/user) - if(emagged == 0) - emagged = 1 - recording = 0 - to_chat(user, "PZZTTPFFFT") - update_icon() - return 1 - else - to_chat(user, "It is already emagged!") - -/obj/item/device/taperecorder/proc/explode() - var/turf/T = get_turf(loc) - if(ismob(loc)) - var/mob/M = loc - to_chat(M, "\The [src] explodes!") - if(T) - T.hotspot_expose(700,125) - explosion(T, -1, -1, 0, 4) - qdel(src) - return - -/obj/item/device/taperecorder/verb/record() - set name = "Start Recording" - set category = "Object" - - if(usr.incapacitated()) - return - if(!mytape) - to_chat(usr, "There's no tape!") - return - if(mytape.ruined) - to_chat(usr, "The tape recorder makes a scratchy noise.") - return - if(recording) - to_chat(usr, "You're already recording!") - return - if(playing) - to_chat(usr, "You can't record when playing!") - return - if(emagged) - to_chat(usr, "The tape recorder makes a scratchy noise.") - return - if(mytape.used_capacity < mytape.max_capacity) - to_chat(usr, "Recording started.") - recording = 1 - update_icon() - - mytape.record_speech("Recording started.") - - //count seconds until full, or recording is stopped - while(mytape && recording && mytape.used_capacity < mytape.max_capacity) - sleep(10) - mytape.used_capacity++ - if(mytape.used_capacity >= mytape.max_capacity) - if(ismob(loc)) - var/mob/M = loc - to_chat(M, "The tape is full.") - stop_recording() - - - update_icon() - return - else - to_chat(usr, "The tape is full.") - - -/obj/item/device/taperecorder/proc/stop_recording() - //Sanity checks skipped, should not be called unless actually recording - recording = 0 - update_icon() - mytape.record_speech("Recording stopped.") - if(ismob(loc)) - var/mob/M = loc - to_chat(M, "Recording stopped.") - - -/obj/item/device/taperecorder/verb/stop() - set name = "Stop" - set category = "Object" - - if(usr.incapacitated()) - return - if(recording) - stop_recording() - return - else if(playing) - playing = 0 - update_icon() - to_chat(usr, "Playback stopped.") - return - else - to_chat(usr, "Stop what?") - - -/obj/item/device/taperecorder/verb/wipe_tape() - set name = "Wipe Tape" - set category = "Object" - - if(usr.incapacitated()) - return - if(emagged) - to_chat(usr, "The tape recorder makes a scratchy noise.") - return - if(mytape.ruined) - to_chat(usr, "The tape recorder makes a scratchy noise.") - return - if(recording || playing) - to_chat(usr, "You can't wipe the tape while playing or recording!") - return - else - if(mytape.storedinfo) mytape.storedinfo.Cut() - if(mytape.timestamp) mytape.timestamp.Cut() - mytape.used_capacity = 0 - to_chat(usr, "You wipe the tape.") - return - - -/obj/item/device/taperecorder/verb/playback_memory() - set name = "Playback Tape" - set category = "Object" - - if(usr.incapacitated()) - return - if(!mytape) - to_chat(usr, "There's no tape!") - return - if(mytape.ruined) - to_chat(usr, "The tape recorder makes a scratchy noise.") - return - if(recording) - to_chat(usr, "You can't playback when recording!") - return - if(playing) - to_chat(usr, "You're already playing!") - return - playing = 1 - update_icon() - to_chat(usr, "Playing started.") - for(var/i=1 , i < mytape.max_capacity , i++) - if(!mytape || !playing) - break - if(mytape.storedinfo.len < i) - break - - var/turf/T = get_turf(src) - var/playedmessage = mytape.storedinfo[i] - if (findtextEx(playedmessage,"*",1,2)) //remove marker for action sounds - playedmessage = copytext(playedmessage,2) - T.audible_message(span_maroon("Tape Recorder: [playedmessage]"), runemessage = playedmessage) - - if(mytape.storedinfo.len < i+1) - playsleepseconds = 1 - sleep(10) - T = get_turf(src) - T.audible_message(span_maroon("Tape Recorder: End of recording."), runemessage = "click") - break - else - playsleepseconds = mytape.timestamp[i+1] - mytape.timestamp[i] - - if(playsleepseconds > 14) - sleep(10) - T = get_turf(src) - T.audible_message(span_maroon("Tape Recorder: Skipping [playsleepseconds] seconds of silence"), runemessage = "tape winding") - playsleepseconds = 1 - sleep(10 * playsleepseconds) - - - playing = 0 - update_icon() - - if(emagged) - var/turf/T = get_turf(src) - T.audible_message(span_maroon("Tape Recorder: This tape recorder will self-destruct in... Five."), runemessage = "beep beep") - sleep(10) - T = get_turf(src) - T.audible_message(span_maroon("Tape Recorder: Four.")) - sleep(10) - T = get_turf(src) - T.audible_message(span_maroon("Tape Recorder: Three.")) - sleep(10) - T = get_turf(src) - T.audible_message(span_maroon("Tape Recorder: Two.")) - sleep(10) - T = get_turf(src) - T.audible_message(span_maroon("Tape Recorder: One.")) - sleep(10) - explode() - - -/obj/item/device/taperecorder/verb/print_transcript() - set name = "Print Transcript" - set category = "Object" - - if(usr.incapacitated()) - return - if(!mytape) - to_chat(usr, "There's no tape!") - return - if(mytape.ruined) - to_chat(usr, "The tape recorder makes a scratchy noise.") - return - if(emagged) - to_chat(usr, "The tape recorder makes a scratchy noise.") - return - if(!canprint) - to_chat(usr, "The recorder can't print that fast!") - return - if(recording || playing) - to_chat(usr, "You can't print the transcript while playing or recording!") - return - - to_chat(usr, "Transcript printed.") - var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(get_turf(src)) - var/t1 = "Transcript:

                    " - for(var/i=1,mytape.storedinfo.len >= i,i++) - var/printedmessage = mytape.storedinfo[i] - if (findtextEx(printedmessage,"*",1,2)) //replace action sounds - printedmessage = "\[[time2text(mytape.timestamp[i]*10,"mm:ss")]\] (Unrecognized sound)" - t1 += "[printedmessage]
                    " - P.info = t1 - P.name = "Transcript" - canprint = 0 - sleep(300) - canprint = 1 - - -/obj/item/device/taperecorder/attack_self(mob/user) - if(recording || playing) - stop() - else - record() - - -/obj/item/device/taperecorder/update_icon() - if(!mytape) - icon_state = "taperecorder_empty" - else if(recording) - icon_state = "taperecorder_recording" - else if(playing) - icon_state = "taperecorder_playing" - else - icon_state = "taperecorder_idle" - - - -/obj/item/device/tape - name = "tape" - desc = "A magnetic tape that can hold up to ten minutes of content." - icon_state = "tape_white" - item_state = "analyzer" - w_class = ITEMSIZE_TINY - matter = list(MAT_STEEL=20, MAT_GLASS=5) - force = 1 - throwforce = 0 - var/max_capacity = 1800 - var/used_capacity = 0 - var/list/storedinfo = new/list() - var/list/timestamp = new/list() - var/ruined = 0 - - -/obj/item/device/tape/update_icon() - cut_overlays() - if(ruined) - add_overlay("ribbonoverlay") - - -/obj/item/device/tape/fire_act() - ruin() - -/obj/item/device/tape/attack_self(mob/user) - if(!ruined) - to_chat(user, "You pull out all the tape!") - ruin() - - -/obj/item/device/tape/proc/ruin() - ruined = 1 - update_icon() - - -/obj/item/device/tape/proc/fix() - ruined = 0 - update_icon() - - -/obj/item/device/tape/proc/record_speech(text) - timestamp += used_capacity - storedinfo += "\[[time2text(used_capacity*10,"mm:ss")]\] [text]" - - -//shows up on the printed transcript as (Unrecognized sound) -/obj/item/device/tape/proc/record_noise(text) - timestamp += used_capacity - storedinfo += "*\[[time2text(used_capacity*10,"mm:ss")]\] [text]" - - -/obj/item/device/tape/attackby(obj/item/I, mob/user, params) - if(ruined && I.has_tool_quality(TOOL_SCREWDRIVER)) - to_chat(user, "You start winding the tape back in...") - playsound(src, I.usesound, 50, 1) - if(do_after(user, 120 * I.toolspeed, target = src)) - to_chat(user, "You wound the tape back in.") - fix() - return - else if(istype(I, /obj/item/weapon/pen)) - if(loc == user && !user.incapacitated()) - var/new_name = tgui_input_text(user, "What would you like to label the tape?", "Tape labeling") - if(isnull(new_name)) return - new_name = sanitizeSafe(new_name) - if(new_name) - name = "tape - '[new_name]'" - to_chat(user, "You label the tape '[new_name]'.") - else - name = "tape" - to_chat(user, "You scratch off the label.") - return - ..() - - -//Random colour tapes -/obj/item/device/tape/random/New() - icon_state = "tape_[pick("white", "blue", "red", "yellow", "purple")]" +/obj/item/device/taperecorder + name = "universal recorder" + desc = "A device that can record to cassette tapes, and play them. It automatically translates the content in playback." + icon_state = "taperecorder_empty" + item_state = "analyzer" + w_class = ITEMSIZE_SMALL + + matter = list(MAT_STEEL = 60,MAT_GLASS = 30) + + var/emagged = 0.0 + var/recording = 0.0 + var/playing = 0.0 + var/playsleepseconds = 0.0 + var/obj/item/device/tape/mytape = /obj/item/device/tape/random + var/canprint = 1 + slot_flags = SLOT_BELT + throwforce = 2 + throw_speed = 4 + throw_range = 20 + +/obj/item/device/taperecorder/New() + ..() + if(ispath(mytape)) + mytape = new mytape(src) + update_icon() + listening_objects += src + +/obj/item/device/taperecorder/empty + mytape = null + +/obj/item/device/taperecorder/Destroy() + listening_objects -= src + if(mytape) + qdel(mytape) + mytape = null + return ..() + + +/obj/item/device/taperecorder/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/device/tape)) + if(mytape) + to_chat(user, "There's already a tape inside.") + return + if(!user.unEquip(I)) + return + I.forceMove(src) + mytape = I + to_chat(user, "You insert [I] into [src].") + update_icon() + return + ..() + + +/obj/item/device/taperecorder/fire_act() + if(mytape) + mytape.ruin() //Fires destroy the tape + return ..() + + +/obj/item/device/taperecorder/attack_hand(mob/user) + if(user.get_inactive_hand() == src) + if(mytape) + eject() + return + ..() + + +/obj/item/device/taperecorder/verb/eject() + set name = "Eject Tape" + set category = "Object" + + if(usr.incapacitated()) + return + if(!mytape) + to_chat(usr, "There's no tape in \the [src].") + return + if(emagged) + to_chat(usr, "The tape seems to be stuck inside.") + return + + if(playing || recording) + stop() + to_chat(usr, "You remove [mytape] from [src].") + usr.put_in_hands(mytape) + mytape = null + update_icon() + + +/obj/item/device/taperecorder/hear_talk(mob/M, list/message_pieces, verb) + var/msg = multilingual_to_message(message_pieces, requires_machine_understands = TRUE, with_capitalization = TRUE) + var/voice = M.GetVoice() //Defined on living, returns name for normal mobs/ + if(mytape && recording) + mytape.record_speech("[voice] [verb], \"[msg]\"") + + +/obj/item/device/taperecorder/see_emote(mob/M as mob, text, var/emote_type) + if(emote_type != 2) //only hearable emotes + return + if(mytape && recording) + mytape.record_speech("[strip_html_properly(text)]") + + +/obj/item/device/taperecorder/show_message(msg, type, alt, alt_type) + var/recordedtext + if (msg && type == 2) //must be hearable + recordedtext = msg + else if (alt && alt_type == 2) + recordedtext = alt + else + return + if(mytape && recording) + mytape.record_noise("[strip_html_properly(recordedtext)]") + +/obj/item/device/taperecorder/emag_act(var/remaining_charges, var/mob/user) + if(emagged == 0) + emagged = 1 + recording = 0 + to_chat(user, "PZZTTPFFFT") + update_icon() + return 1 + else + to_chat(user, "It is already emagged!") + +/obj/item/device/taperecorder/proc/explode() + var/turf/T = get_turf(loc) + if(ismob(loc)) + var/mob/M = loc + to_chat(M, "\The [src] explodes!") + if(T) + T.hotspot_expose(700,125) + explosion(T, -1, -1, 0, 4) + qdel(src) + return + +/obj/item/device/taperecorder/verb/record() + set name = "Start Recording" + set category = "Object" + + if(usr.incapacitated()) + return + if(!mytape) + to_chat(usr, "There's no tape!") + return + if(mytape.ruined) + to_chat(usr, "The tape recorder makes a scratchy noise.") + return + if(recording) + to_chat(usr, "You're already recording!") + return + if(playing) + to_chat(usr, "You can't record when playing!") + return + if(emagged) + to_chat(usr, "The tape recorder makes a scratchy noise.") + return + if(mytape.used_capacity < mytape.max_capacity) + to_chat(usr, "Recording started.") + recording = 1 + update_icon() + + mytape.record_speech("Recording started.") + + //count seconds until full, or recording is stopped + while(mytape && recording && mytape.used_capacity < mytape.max_capacity) + sleep(10) + mytape.used_capacity++ + if(mytape.used_capacity >= mytape.max_capacity) + if(ismob(loc)) + var/mob/M = loc + to_chat(M, "The tape is full.") + stop_recording() + + + update_icon() + return + else + to_chat(usr, "The tape is full.") + + +/obj/item/device/taperecorder/proc/stop_recording() + //Sanity checks skipped, should not be called unless actually recording + recording = 0 + update_icon() + mytape.record_speech("Recording stopped.") + if(ismob(loc)) + var/mob/M = loc + to_chat(M, "Recording stopped.") + + +/obj/item/device/taperecorder/verb/stop() + set name = "Stop" + set category = "Object" + + if(usr.incapacitated()) + return + if(recording) + stop_recording() + return + else if(playing) + playing = 0 + update_icon() + to_chat(usr, "Playback stopped.") + return + else + to_chat(usr, "Stop what?") + + +/obj/item/device/taperecorder/verb/wipe_tape() + set name = "Wipe Tape" + set category = "Object" + + if(usr.incapacitated()) + return + if(emagged) + to_chat(usr, "The tape recorder makes a scratchy noise.") + return + if(mytape.ruined) + to_chat(usr, "The tape recorder makes a scratchy noise.") + return + if(recording || playing) + to_chat(usr, "You can't wipe the tape while playing or recording!") + return + else + if(mytape.storedinfo) mytape.storedinfo.Cut() + if(mytape.timestamp) mytape.timestamp.Cut() + mytape.used_capacity = 0 + to_chat(usr, "You wipe the tape.") + return + + +/obj/item/device/taperecorder/verb/playback_memory() + set name = "Playback Tape" + set category = "Object" + + if(usr.incapacitated()) + return + if(!mytape) + to_chat(usr, "There's no tape!") + return + if(mytape.ruined) + to_chat(usr, "The tape recorder makes a scratchy noise.") + return + if(recording) + to_chat(usr, "You can't playback when recording!") + return + if(playing) + to_chat(usr, "You're already playing!") + return + playing = 1 + update_icon() + to_chat(usr, "Playing started.") + for(var/i=1 , i < mytape.max_capacity , i++) + if(!mytape || !playing) + break + if(mytape.storedinfo.len < i) + break + + var/turf/T = get_turf(src) + var/playedmessage = mytape.storedinfo[i] + if (findtextEx(playedmessage,"*",1,2)) //remove marker for action sounds + playedmessage = copytext(playedmessage,2) + T.audible_message(span_maroon("Tape Recorder: [playedmessage]"), runemessage = playedmessage) + + if(mytape.storedinfo.len < i+1) + playsleepseconds = 1 + sleep(10) + T = get_turf(src) + T.audible_message(span_maroon("Tape Recorder: End of recording."), runemessage = "click") + break + else + playsleepseconds = mytape.timestamp[i+1] - mytape.timestamp[i] + + if(playsleepseconds > 14) + sleep(10) + T = get_turf(src) + T.audible_message(span_maroon("Tape Recorder: Skipping [playsleepseconds] seconds of silence"), runemessage = "tape winding") + playsleepseconds = 1 + sleep(10 * playsleepseconds) + + + playing = 0 + update_icon() + + if(emagged) + var/turf/T = get_turf(src) + T.audible_message(span_maroon("Tape Recorder: This tape recorder will self-destruct in... Five."), runemessage = "beep beep") + sleep(10) + T = get_turf(src) + T.audible_message(span_maroon("Tape Recorder: Four.")) + sleep(10) + T = get_turf(src) + T.audible_message(span_maroon("Tape Recorder: Three.")) + sleep(10) + T = get_turf(src) + T.audible_message(span_maroon("Tape Recorder: Two.")) + sleep(10) + T = get_turf(src) + T.audible_message(span_maroon("Tape Recorder: One.")) + sleep(10) + explode() + + +/obj/item/device/taperecorder/verb/print_transcript() + set name = "Print Transcript" + set category = "Object" + + if(usr.incapacitated()) + return + if(!mytape) + to_chat(usr, "There's no tape!") + return + if(mytape.ruined) + to_chat(usr, "The tape recorder makes a scratchy noise.") + return + if(emagged) + to_chat(usr, "The tape recorder makes a scratchy noise.") + return + if(!canprint) + to_chat(usr, "The recorder can't print that fast!") + return + if(recording || playing) + to_chat(usr, "You can't print the transcript while playing or recording!") + return + + to_chat(usr, "Transcript printed.") + var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(get_turf(src)) + var/t1 = "Transcript:

                    " + for(var/i=1,mytape.storedinfo.len >= i,i++) + var/printedmessage = mytape.storedinfo[i] + if (findtextEx(printedmessage,"*",1,2)) //replace action sounds + printedmessage = "\[[time2text(mytape.timestamp[i]*10,"mm:ss")]\] (Unrecognized sound)" + t1 += "[printedmessage]
                    " + P.info = t1 + P.name = "Transcript" + canprint = 0 + sleep(300) + canprint = 1 + + +/obj/item/device/taperecorder/attack_self(mob/user) + if(recording || playing) + stop() + else + record() + + +/obj/item/device/taperecorder/update_icon() + if(!mytape) + icon_state = "taperecorder_empty" + else if(recording) + icon_state = "taperecorder_recording" + else if(playing) + icon_state = "taperecorder_playing" + else + icon_state = "taperecorder_idle" + + + +/obj/item/device/tape + name = "tape" + desc = "A magnetic tape that can hold up to ten minutes of content." + icon_state = "tape_white" + item_state = "analyzer" + w_class = ITEMSIZE_TINY + matter = list(MAT_STEEL=20, MAT_GLASS=5) + force = 1 + throwforce = 0 + var/max_capacity = 1800 + var/used_capacity = 0 + var/list/storedinfo = new/list() + var/list/timestamp = new/list() + var/ruined = 0 + + +/obj/item/device/tape/update_icon() + cut_overlays() + if(ruined) + add_overlay("ribbonoverlay") + + +/obj/item/device/tape/fire_act() + ruin() + +/obj/item/device/tape/attack_self(mob/user) + if(!ruined) + to_chat(user, "You pull out all the tape!") + ruin() + + +/obj/item/device/tape/proc/ruin() + ruined = 1 + update_icon() + + +/obj/item/device/tape/proc/fix() + ruined = 0 + update_icon() + + +/obj/item/device/tape/proc/record_speech(text) + timestamp += used_capacity + storedinfo += "\[[time2text(used_capacity*10,"mm:ss")]\] [text]" + + +//shows up on the printed transcript as (Unrecognized sound) +/obj/item/device/tape/proc/record_noise(text) + timestamp += used_capacity + storedinfo += "*\[[time2text(used_capacity*10,"mm:ss")]\] [text]" + + +/obj/item/device/tape/attackby(obj/item/I, mob/user, params) + if(ruined && I.has_tool_quality(TOOL_SCREWDRIVER)) + to_chat(user, "You start winding the tape back in...") + playsound(src, I.usesound, 50, 1) + if(do_after(user, 120 * I.toolspeed, target = src)) + to_chat(user, "You wound the tape back in.") + fix() + return + else if(istype(I, /obj/item/weapon/pen)) + if(loc == user && !user.incapacitated()) + var/new_name = tgui_input_text(user, "What would you like to label the tape?", "Tape labeling") + if(isnull(new_name)) return + new_name = sanitizeSafe(new_name) + if(new_name) + name = "tape - '[new_name]'" + to_chat(user, "You label the tape '[new_name]'.") + else + name = "tape" + to_chat(user, "You scratch off the label.") + return + ..() + + +//Random colour tapes +/obj/item/device/tape/random/New() + icon_state = "tape_[pick("white", "blue", "red", "yellow", "purple")]" diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index 45df3c628f0..66d05b2d93c 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -1,218 +1,218 @@ -/obj/item/device/transfer_valve - name = "tank transfer valve" - desc = "Regulates the transfer of air between two tanks" - icon = 'icons/obj/assemblies.dmi' - icon_state = "valve_1" - var/obj/item/weapon/tank/tank_one - var/obj/item/weapon/tank/tank_two - var/obj/item/device/assembly/attached_device - var/mob/attacher = null - var/valve_open = 0 - var/toggle = 1 - -/obj/item/device/transfer_valve/attackby(obj/item/item, mob/user) - var/turf/location = get_turf(src) // For admin logs - if(istype(item, /obj/item/weapon/tank)) - if(tank_one && tank_two) - to_chat(user, "There are already two tanks attached, remove one first.") - return - - if(!tank_one) - tank_one = item - user.drop_item() - item.forceMove(src) - to_chat(user, "You attach the tank to the transfer valve.") - else if(!tank_two) - tank_two = item - user.drop_item() - item.forceMove(src) - to_chat(user, "You attach the tank to the transfer valve.") - message_admins("[key_name_admin(user)] attached both tanks to a transfer valve. [ADMIN_JMP(location)]") - log_game("[key_name_admin(user)] attached both tanks to a transfer valve.") - - update_icon() - SStgui.update_uis(src) // update all UIs attached to src -//TODO: Have this take an assemblyholder - else if(isassembly(item)) - var/obj/item/device/assembly/A = item - if(A.secured) - to_chat(user, "The device is secured.") - return - if(attached_device) - to_chat(user, "There is already an device attached to the valve, remove it first.") - return - user.remove_from_mob(item) - attached_device = A - A.forceMove(src) - to_chat(user, "You attach the [item] to the valve controls and secure it.") - A.holder = src - A.toggle_secure() //this calls update_icon(), which calls update_icon() on the holder (i.e. the bomb). - - bombers += "[key_name(user)] attached a [item] to a transfer valve." - message_admins("[key_name_admin(user)] attached a [item] to a transfer valve. [ADMIN_JMP(location)]") - log_game("[key_name_admin(user)] attached a [item] to a transfer valve.") - attacher = user - SStgui.update_uis(src) // update all UIs attached to src - return - - -/obj/item/device/transfer_valve/HasProximity(turf/T, atom/movable/AM, old_loc) - attached_device?.HasProximity(T, AM, old_loc) - -/obj/item/device/transfer_valve/Moved(old_loc, direction, forced) - . = ..() - if(isturf(old_loc)) - unsense_proximity(callback = /atom/proc/HasProximity, center = old_loc) - if(isturf(loc)) - sense_proximity(callback = /atom/proc/HasProximity) - -/obj/item/device/transfer_valve/attack_self(mob/user) - tgui_interact(user) - -/obj/item/device/transfer_valve/tgui_state(mob/user) - return GLOB.tgui_inventory_state - -/obj/item/device/transfer_valve/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "TransferValve", name) // 460, 320 - ui.open() - -/obj/item/device/transfer_valve/tgui_data(mob/user) - var/list/data = list() - data["tank_one"] = tank_one ? tank_one.name : null - data["tank_two"] = tank_two ? tank_two.name : null - data["attached_device"] = attached_device ? attached_device.name : null - data["valve"] = valve_open - return data - -/obj/item/device/transfer_valve/tgui_act(action, params) - if(..()) - return - . = TRUE - switch(action) - if("tankone") - remove_tank(tank_one) - if("tanktwo") - remove_tank(tank_two) - if("toggle") - toggle_valve() - if("device") - if(attached_device) - attached_device.attack_self(usr) - if("remove_device") - if(attached_device) - attached_device.forceMove(get_turf(src)) - attached_device.holder = null - attached_device = null - update_icon() - else - . = FALSE - if(.) - update_icon() - add_fingerprint(usr) - -/obj/item/device/transfer_valve/proc/process_activation(var/obj/item/device/D) - if(toggle) - toggle = FALSE - toggle_valve() - VARSET_IN(src, toggle, TRUE, 5 SECONDS) - -/obj/item/device/transfer_valve/update_icon() - cut_overlays() - underlays = null - - if(!tank_one && !tank_two && !attached_device) - icon_state = "valve_1" - return - icon_state = "valve" - - if(tank_one) - add_overlay("[tank_one.icon_state]") - if(tank_two) - var/icon/J = new(icon, icon_state = "[tank_two.icon_state]") - J.Shift(WEST, 13) - underlays += J - if(attached_device) - add_overlay("device") - -/obj/item/device/transfer_valve/proc/remove_tank(obj/item/weapon/tank/T) - if(tank_one == T) - split_gases() - tank_one = null - else if(tank_two == T) - split_gases() - tank_two = null - else - return - - T.forceMove(get_turf(src)) - update_icon() - -/obj/item/device/transfer_valve/proc/merge_gases() - if(valve_open) - return - tank_two.air_contents.volume += tank_one.air_contents.volume - var/datum/gas_mixture/temp - temp = tank_one.air_contents.remove_ratio(1) - tank_two.air_contents.merge(temp) - valve_open = 1 - -/obj/item/device/transfer_valve/proc/split_gases() - if(!valve_open) - return - - valve_open = 0 - - if(QDELETED(tank_one) || QDELETED(tank_two)) - return - - var/ratio1 = tank_one.air_contents.volume/tank_two.air_contents.volume - var/datum/gas_mixture/temp - temp = tank_two.air_contents.remove_ratio(ratio1) - tank_one.air_contents.merge(temp) - tank_two.air_contents.volume -= tank_one.air_contents.volume - - - /* - Exadv1: I know this isn't how it's going to work, but this was just to check - it explodes properly when it gets a signal (and it does). - */ - -/obj/item/device/transfer_valve/proc/toggle_valve() - if(!valve_open && (tank_one && tank_two)) - var/turf/bombturf = get_turf(src) - var/area/A = get_area(bombturf) - - var/attacher_name = "" - if(!attacher) - attacher_name = "Unknown" - else - attacher_name = "[attacher.name]([attacher.ckey])" - - var/log_str = "Bomb valve opened in [A.name] " - log_str += "with [attached_device ? attached_device : "no device"] attacher: [attacher_name]" - - if(attacher) - log_str += ADMIN_QUE(attacher) - - var/mob/mob = get_mob_by_key(src.fingerprintslast) - var/last_touch_info = "" - if(mob) - last_touch_info = ADMIN_QUE(mob) - - log_str += " Last touched by: [src.fingerprintslast][last_touch_info]" - bombers += log_str - message_admins(log_str, 0, 1) - log_game(log_str) - merge_gases() - - else if(valve_open==1 && (tank_one && tank_two)) - split_gases() - - src.update_icon() - -// this doesn't do anything but the timer etc. expects it to be here -// eventually maybe have it update icon to show state (timer, prox etc.) like old bombs -/obj/item/device/transfer_valve/proc/c_state() - return +/obj/item/device/transfer_valve + name = "tank transfer valve" + desc = "Regulates the transfer of air between two tanks" + icon = 'icons/obj/assemblies.dmi' + icon_state = "valve_1" + var/obj/item/weapon/tank/tank_one + var/obj/item/weapon/tank/tank_two + var/obj/item/device/assembly/attached_device + var/mob/attacher = null + var/valve_open = 0 + var/toggle = 1 + +/obj/item/device/transfer_valve/attackby(obj/item/item, mob/user) + var/turf/location = get_turf(src) // For admin logs + if(istype(item, /obj/item/weapon/tank)) + if(tank_one && tank_two) + to_chat(user, "There are already two tanks attached, remove one first.") + return + + if(!tank_one) + tank_one = item + user.drop_item() + item.forceMove(src) + to_chat(user, "You attach the tank to the transfer valve.") + else if(!tank_two) + tank_two = item + user.drop_item() + item.forceMove(src) + to_chat(user, "You attach the tank to the transfer valve.") + message_admins("[key_name_admin(user)] attached both tanks to a transfer valve. [ADMIN_JMP(location)]") + log_game("[key_name_admin(user)] attached both tanks to a transfer valve.") + + update_icon() + SStgui.update_uis(src) // update all UIs attached to src +//TODO: Have this take an assemblyholder + else if(isassembly(item)) + var/obj/item/device/assembly/A = item + if(A.secured) + to_chat(user, "The device is secured.") + return + if(attached_device) + to_chat(user, "There is already an device attached to the valve, remove it first.") + return + user.remove_from_mob(item) + attached_device = A + A.forceMove(src) + to_chat(user, "You attach the [item] to the valve controls and secure it.") + A.holder = src + A.toggle_secure() //this calls update_icon(), which calls update_icon() on the holder (i.e. the bomb). + + bombers += "[key_name(user)] attached a [item] to a transfer valve." + message_admins("[key_name_admin(user)] attached a [item] to a transfer valve. [ADMIN_JMP(location)]") + log_game("[key_name_admin(user)] attached a [item] to a transfer valve.") + attacher = user + SStgui.update_uis(src) // update all UIs attached to src + return + + +/obj/item/device/transfer_valve/HasProximity(turf/T, atom/movable/AM, old_loc) + attached_device?.HasProximity(T, AM, old_loc) + +/obj/item/device/transfer_valve/Moved(old_loc, direction, forced) + . = ..() + if(isturf(old_loc)) + unsense_proximity(callback = /atom/proc/HasProximity, center = old_loc) + if(isturf(loc)) + sense_proximity(callback = /atom/proc/HasProximity) + +/obj/item/device/transfer_valve/attack_self(mob/user) + tgui_interact(user) + +/obj/item/device/transfer_valve/tgui_state(mob/user) + return GLOB.tgui_inventory_state + +/obj/item/device/transfer_valve/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "TransferValve", name) // 460, 320 + ui.open() + +/obj/item/device/transfer_valve/tgui_data(mob/user) + var/list/data = list() + data["tank_one"] = tank_one ? tank_one.name : null + data["tank_two"] = tank_two ? tank_two.name : null + data["attached_device"] = attached_device ? attached_device.name : null + data["valve"] = valve_open + return data + +/obj/item/device/transfer_valve/tgui_act(action, params) + if(..()) + return + . = TRUE + switch(action) + if("tankone") + remove_tank(tank_one) + if("tanktwo") + remove_tank(tank_two) + if("toggle") + toggle_valve() + if("device") + if(attached_device) + attached_device.attack_self(usr) + if("remove_device") + if(attached_device) + attached_device.forceMove(get_turf(src)) + attached_device.holder = null + attached_device = null + update_icon() + else + . = FALSE + if(.) + update_icon() + add_fingerprint(usr) + +/obj/item/device/transfer_valve/proc/process_activation(var/obj/item/device/D) + if(toggle) + toggle = FALSE + toggle_valve() + VARSET_IN(src, toggle, TRUE, 5 SECONDS) + +/obj/item/device/transfer_valve/update_icon() + cut_overlays() + underlays = null + + if(!tank_one && !tank_two && !attached_device) + icon_state = "valve_1" + return + icon_state = "valve" + + if(tank_one) + add_overlay("[tank_one.icon_state]") + if(tank_two) + var/icon/J = new(icon, icon_state = "[tank_two.icon_state]") + J.Shift(WEST, 13) + underlays += J + if(attached_device) + add_overlay("device") + +/obj/item/device/transfer_valve/proc/remove_tank(obj/item/weapon/tank/T) + if(tank_one == T) + split_gases() + tank_one = null + else if(tank_two == T) + split_gases() + tank_two = null + else + return + + T.forceMove(get_turf(src)) + update_icon() + +/obj/item/device/transfer_valve/proc/merge_gases() + if(valve_open) + return + tank_two.air_contents.volume += tank_one.air_contents.volume + var/datum/gas_mixture/temp + temp = tank_one.air_contents.remove_ratio(1) + tank_two.air_contents.merge(temp) + valve_open = 1 + +/obj/item/device/transfer_valve/proc/split_gases() + if(!valve_open) + return + + valve_open = 0 + + if(QDELETED(tank_one) || QDELETED(tank_two)) + return + + var/ratio1 = tank_one.air_contents.volume/tank_two.air_contents.volume + var/datum/gas_mixture/temp + temp = tank_two.air_contents.remove_ratio(ratio1) + tank_one.air_contents.merge(temp) + tank_two.air_contents.volume -= tank_one.air_contents.volume + + + /* + Exadv1: I know this isn't how it's going to work, but this was just to check + it explodes properly when it gets a signal (and it does). + */ + +/obj/item/device/transfer_valve/proc/toggle_valve() + if(!valve_open && (tank_one && tank_two)) + var/turf/bombturf = get_turf(src) + var/area/A = get_area(bombturf) + + var/attacher_name = "" + if(!attacher) + attacher_name = "Unknown" + else + attacher_name = "[attacher.name]([attacher.ckey])" + + var/log_str = "Bomb valve opened in [A.name] " + log_str += "with [attached_device ? attached_device : "no device"] attacher: [attacher_name]" + + if(attacher) + log_str += ADMIN_QUE(attacher) + + var/mob/mob = get_mob_by_key(src.fingerprintslast) + var/last_touch_info = "" + if(mob) + last_touch_info = ADMIN_QUE(mob) + + log_str += " Last touched by: [src.fingerprintslast][last_touch_info]" + bombers += log_str + message_admins(log_str, 0, 1) + log_game(log_str) + merge_gases() + + else if(valve_open==1 && (tank_one && tank_two)) + split_gases() + + src.update_icon() + +// this doesn't do anything but the timer etc. expects it to be here +// eventually maybe have it update icon to show state (timer, prox etc.) like old bombs +/obj/item/device/transfer_valve/proc/c_state() + return diff --git a/code/game/objects/items/devices/uplink_random_lists.dm b/code/game/objects/items/devices/uplink_random_lists.dm index 89bfd5f0040..7e8a6953aef 100644 --- a/code/game/objects/items/devices/uplink_random_lists.dm +++ b/code/game/objects/items/devices/uplink_random_lists.dm @@ -1,118 +1,118 @@ -var/datum/uplink_random_selection/default_uplink_selection = new/datum/uplink_random_selection/default() -var/datum/uplink_random_selection/all_uplink_selection = new/datum/uplink_random_selection/all() - -/datum/uplink_random_item - var/uplink_item // The uplink item - var/keep_probability // The probability we'll decide to keep this item if selected - var/reselect_probability // Probability that we'll decide to keep this item if previously selected. - // Is done together with the keep_probability check. Being selected more than once does not affect this probability. - -/datum/uplink_random_item/New(var/uplink_item, var/keep_probability = 100, var/reselect_propbability = 33) - ..() - src.uplink_item = uplink_item - src.keep_probability = keep_probability - src.reselect_probability = reselect_probability - -/datum/uplink_random_selection - var/list/datum/uplink_random_item/items - var/list/datum/uplink_random_item/all_items - -/datum/uplink_random_selection/New() - ..() - items = list() - all_items = list() - -/datum/uplink_random_selection/proc/get_random_item(var/telecrystals, obj/item/device/uplink/U, var/list/bought_items, var/items_override = 0) - var/const/attempts = 50 - - for(var/i = 0; i < attempts; i++) - var/datum/uplink_random_item/RI - if(items_override) - RI = pick(all_items) - else - RI = pick(items) - if(!prob(RI.keep_probability)) - continue - var/datum/uplink_item/I = uplink.items_assoc[RI.uplink_item] - if(I.cost(U) > telecrystals) - continue - if(bought_items && (I in bought_items) && !prob(RI.reselect_probability)) - continue - if(U && !I.can_buy(U, telecrystals)) - continue - return I - -/datum/uplink_random_selection/all/New() - ..() - for(var/datum/uplink_item/item in uplink.items) - if(item.blacklisted) - continue - else - all_items += new/datum/uplink_random_item(item.type) - -/datum/uplink_random_selection/default/New() - ..() - - items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/silenced_45) - items += new/datum/uplink_random_item(/datum/uplink_item/item/ammo/mc9mm) - items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/revolver) - items += new/datum/uplink_random_item(/datum/uplink_item/item/ammo/a357) - items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/heavysnipermerc, 15, 0) - items += new/datum/uplink_random_item(/datum/uplink_item/item/ammo/sniperammo, 15, 0) - items += new/datum/uplink_random_item(/datum/uplink_item/item/grenades/emp, 50) - items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/crossbow, 33) - items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/energy_sword, 75) - - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/soap, 5, 100) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/concealed_cane, 50, 10) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/detomatix, 20, 10) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/parapen) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/cigarette_kit) - - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/id) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/spy) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/chameleon_kit) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/chameleon_projector) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/voice) - - items += new/datum/uplink_random_item(/datum/uplink_item/item/armor/heavy_vest) - items += new/datum/uplink_random_item(/datum/uplink_item/item/armor/combat) - - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/toolbox, reselect_propbability = 10) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/plastique) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/encryptionkey_radio) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/encryptionkey_binary) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/emag, 100, 50) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/clerical) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/space_suit, 50, 10) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/thermal) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/powersink, 10, 10) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/ai_module, 25, 0) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/teleporter, 10, 0) - - items += new/datum/uplink_random_item(/datum/uplink_item/item/implants/imp_freedom) - items += new/datum/uplink_random_item(/datum/uplink_item/item/implants/imp_compress) - items += new/datum/uplink_random_item(/datum/uplink_item/item/implants/imp_explosive) - - items += new/datum/uplink_random_item(/datum/uplink_item/item/medical/sinpockets, reselect_propbability = 20) - items += new/datum/uplink_random_item(/datum/uplink_item/item/medical/surgery, reselect_propbability = 10) - items += new/datum/uplink_random_item(/datum/uplink_item/item/medical/combat, reselect_propbability = 10) - - items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/thermal, reselect_propbability = 15) - items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/energy_net, reselect_propbability = 15) - items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/ewar_voice, reselect_propbability = 15) - items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/maneuvering_jets, reselect_propbability = 15) - items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/egun, reselect_propbability = 15) - items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/power_sink, reselect_propbability = 15) - items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/laser_canon, reselect_propbability = 5) - -#ifdef DEBUG -/proc/debug_uplink_purchage_log() - for(var/antag_type in all_antag_types) - var/datum/antagonist/A = all_antag_types[antag_type] - A.print_player_summary() - -/proc/debug_uplink_item_assoc_list() - for(var/key in uplink.items_assoc) - to_world("[key] - [uplink.items_assoc[key]]") -#endif +var/datum/uplink_random_selection/default_uplink_selection = new/datum/uplink_random_selection/default() +var/datum/uplink_random_selection/all_uplink_selection = new/datum/uplink_random_selection/all() + +/datum/uplink_random_item + var/uplink_item // The uplink item + var/keep_probability // The probability we'll decide to keep this item if selected + var/reselect_probability // Probability that we'll decide to keep this item if previously selected. + // Is done together with the keep_probability check. Being selected more than once does not affect this probability. + +/datum/uplink_random_item/New(var/uplink_item, var/keep_probability = 100, var/reselect_propbability = 33) + ..() + src.uplink_item = uplink_item + src.keep_probability = keep_probability + src.reselect_probability = reselect_probability + +/datum/uplink_random_selection + var/list/datum/uplink_random_item/items + var/list/datum/uplink_random_item/all_items + +/datum/uplink_random_selection/New() + ..() + items = list() + all_items = list() + +/datum/uplink_random_selection/proc/get_random_item(var/telecrystals, obj/item/device/uplink/U, var/list/bought_items, var/items_override = 0) + var/const/attempts = 50 + + for(var/i = 0; i < attempts; i++) + var/datum/uplink_random_item/RI + if(items_override) + RI = pick(all_items) + else + RI = pick(items) + if(!prob(RI.keep_probability)) + continue + var/datum/uplink_item/I = uplink.items_assoc[RI.uplink_item] + if(I.cost(U) > telecrystals) + continue + if(bought_items && (I in bought_items) && !prob(RI.reselect_probability)) + continue + if(U && !I.can_buy(U, telecrystals)) + continue + return I + +/datum/uplink_random_selection/all/New() + ..() + for(var/datum/uplink_item/item in uplink.items) + if(item.blacklisted) + continue + else + all_items += new/datum/uplink_random_item(item.type) + +/datum/uplink_random_selection/default/New() + ..() + + items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/silenced_45) + items += new/datum/uplink_random_item(/datum/uplink_item/item/ammo/mc9mm) + items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/revolver) + items += new/datum/uplink_random_item(/datum/uplink_item/item/ammo/a357) + items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/heavysnipermerc, 15, 0) + items += new/datum/uplink_random_item(/datum/uplink_item/item/ammo/sniperammo, 15, 0) + items += new/datum/uplink_random_item(/datum/uplink_item/item/grenades/emp, 50) + items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/crossbow, 33) + items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/energy_sword, 75) + + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/soap, 5, 100) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/concealed_cane, 50, 10) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/detomatix, 20, 10) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/parapen) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/cigarette_kit) + + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/id) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/spy) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/chameleon_kit) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/chameleon_projector) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/voice) + + items += new/datum/uplink_random_item(/datum/uplink_item/item/armor/heavy_vest) + items += new/datum/uplink_random_item(/datum/uplink_item/item/armor/combat) + + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/toolbox, reselect_propbability = 10) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/plastique) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/encryptionkey_radio) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/encryptionkey_binary) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/emag, 100, 50) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/clerical) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/space_suit, 50, 10) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/thermal) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/powersink, 10, 10) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/ai_module, 25, 0) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/teleporter, 10, 0) + + items += new/datum/uplink_random_item(/datum/uplink_item/item/implants/imp_freedom) + items += new/datum/uplink_random_item(/datum/uplink_item/item/implants/imp_compress) + items += new/datum/uplink_random_item(/datum/uplink_item/item/implants/imp_explosive) + + items += new/datum/uplink_random_item(/datum/uplink_item/item/medical/sinpockets, reselect_propbability = 20) + items += new/datum/uplink_random_item(/datum/uplink_item/item/medical/surgery, reselect_propbability = 10) + items += new/datum/uplink_random_item(/datum/uplink_item/item/medical/combat, reselect_propbability = 10) + + items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/thermal, reselect_propbability = 15) + items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/energy_net, reselect_propbability = 15) + items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/ewar_voice, reselect_propbability = 15) + items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/maneuvering_jets, reselect_propbability = 15) + items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/egun, reselect_propbability = 15) + items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/power_sink, reselect_propbability = 15) + items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/laser_canon, reselect_propbability = 5) + +#ifdef DEBUG +/proc/debug_uplink_purchage_log() + for(var/antag_type in all_antag_types) + var/datum/antagonist/A = all_antag_types[antag_type] + A.print_player_summary() + +/proc/debug_uplink_item_assoc_list() + for(var/key in uplink.items_assoc) + to_world("[key] - [uplink.items_assoc[key]]") +#endif diff --git a/code/game/objects/items/latexballoon.dm b/code/game/objects/items/latexballoon.dm index 559bfbec0d3..753158eadb6 100644 --- a/code/game/objects/items/latexballoon.dm +++ b/code/game/objects/items/latexballoon.dm @@ -1,64 +1,64 @@ -/obj/item/latexballon - name = "latex glove" - desc = "A latex glove, usually used as a balloon." - icon_state = "latexballon" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_gloves.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_gloves.dmi', - ) - item_state = "lgloves" - force = 0 - throwforce = 0 - w_class = ITEMSIZE_SMALL - throw_speed = 1 - throw_range = 15 - var/state - var/datum/gas_mixture/air_contents = null - -/obj/item/latexballon/proc/blow(obj/item/weapon/tank/tank) - if (icon_state == "latexballon_bursted") - return - src.air_contents = tank.remove_air_volume(3) - icon_state = "latexballon_blow" - item_state = "latexballon" - -/obj/item/latexballon/proc/burst() - if (!air_contents) - return - playsound(src, 'sound/weapons/Gunshot_old.ogg', 100, 1) - icon_state = "latexballon_bursted" - item_state = "lgloves" - loc.assume_air(air_contents) - -/obj/item/latexballon/ex_act(severity) - burst() - switch(severity) - if (1) - qdel(src) - if (2) - if (prob(50)) - qdel(src) - -/obj/item/latexballon/bullet_act() - burst() - -/obj/item/latexballon/fire_act(datum/gas_mixture/air, temperature, volume) - if(temperature > T0C+100) - burst() - return - -/obj/item/latexballon/attackby(obj/item/W as obj, mob/user as mob) - if (can_puncture(W)) - burst() - -/* -/obj/item/latexballon/nitrile - name = "nitrile glove" - desc = "A nitrile glove, usually used as a balloon." - icon_state = "nitrileballon" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_gloves.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_gloves.dmi', - ) - item_state = "ngloves" +/obj/item/latexballon + name = "latex glove" + desc = "A latex glove, usually used as a balloon." + icon_state = "latexballon" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_gloves.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_gloves.dmi', + ) + item_state = "lgloves" + force = 0 + throwforce = 0 + w_class = ITEMSIZE_SMALL + throw_speed = 1 + throw_range = 15 + var/state + var/datum/gas_mixture/air_contents = null + +/obj/item/latexballon/proc/blow(obj/item/weapon/tank/tank) + if (icon_state == "latexballon_bursted") + return + src.air_contents = tank.remove_air_volume(3) + icon_state = "latexballon_blow" + item_state = "latexballon" + +/obj/item/latexballon/proc/burst() + if (!air_contents) + return + playsound(src, 'sound/weapons/Gunshot_old.ogg', 100, 1) + icon_state = "latexballon_bursted" + item_state = "lgloves" + loc.assume_air(air_contents) + +/obj/item/latexballon/ex_act(severity) + burst() + switch(severity) + if (1) + qdel(src) + if (2) + if (prob(50)) + qdel(src) + +/obj/item/latexballon/bullet_act() + burst() + +/obj/item/latexballon/fire_act(datum/gas_mixture/air, temperature, volume) + if(temperature > T0C+100) + burst() + return + +/obj/item/latexballon/attackby(obj/item/W as obj, mob/user as mob) + if (can_puncture(W)) + burst() + +/* +/obj/item/latexballon/nitrile + name = "nitrile glove" + desc = "A nitrile glove, usually used as a balloon." + icon_state = "nitrileballon" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_gloves.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_gloves.dmi', + ) + item_state = "ngloves" */ \ No newline at end of file diff --git a/code/game/objects/items/paintkit.dm b/code/game/objects/items/paintkit.dm index 3367574393f..53eb76a4662 100644 --- a/code/game/objects/items/paintkit.dm +++ b/code/game/objects/items/paintkit.dm @@ -1,332 +1,332 @@ -/obj/item/device/kit - icon_state = "modkit" - icon = 'icons/obj/device.dmi' - w_class = ITEMSIZE_SMALL - var/new_name = "custom item" - var/new_desc = "A custom item." - var/new_icon - var/new_icon_file - var/new_icon_override_file - var/uses = 1 // Uses before the kit deletes itself. - var/list/allowed_types = list() - -/obj/item/device/kit/examine() - . = ..() - . += "It has [uses] use\s left." - -/obj/item/device/kit/proc/use(var/amt, var/mob/user) - uses -= amt - playsound(src, 'sound/items/Screwdriver.ogg', 50, 1) - if(uses<1) - user.drop_item() - qdel(src) - -/obj/item/device/kit/proc/can_customize(var/obj/item/I) - return is_type_in_list(I, allowed_types) - -/obj/item/device/kit/proc/set_info(var/kit_name, var/kit_desc, var/kit_icon, var/kit_icon_file = CUSTOM_ITEM_OBJ, var/kit_icon_override_file = CUSTOM_ITEM_MOB, var/additional_data) - new_name = kit_name - new_desc = kit_desc - new_icon = kit_icon - new_icon_file = kit_icon_file - new_icon_override_file = kit_icon_override_file - - for(var/path in splittext(additional_data, ", ")) - allowed_types |= text2path(path) - -/obj/item/device/kit/proc/customize(var/obj/item/I, var/mob/user) - if(can_customize(I)) - I.name = new_name ? new_name : I.name - I.desc = new_desc ? new_desc : I.desc - I.icon = new_icon_file ? new_icon_file : I.icon - I.icon_override = new_icon_override_file ? new_icon_override_file : I.icon_override - if(new_icon) - I.icon_state = new_icon - var/obj/item/clothing/under/U = I - if(istype(U)) - U.worn_state = I.icon_state - U.update_rolldown_status() - use(1, user) - -// Generic use -/obj/item/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/device/kit)) - var/obj/item/device/kit/K = W - K.customize(src, user) - return - - ..() - -// Root hardsuit kit defines. -// Icons for modified hardsuits need to be in the proper .dmis because suit cyclers may cock them up. -/obj/item/device/kit/suit - name = "voidsuit modification kit" - desc = "A kit for modifying a voidsuit." - uses = 2 - var/new_light_overlay - -/obj/item/device/kit/suit/can_customize(var/obj/item/I) - return istype(I, /obj/item/clothing/head/helmet/space/void) || istype(I, /obj/item/clothing/suit/space/void) || istype(I, /obj/item/clothing/suit/storage/hooded) - -/obj/item/device/kit/suit/set_info(var/kit_name, var/kit_desc, var/kit_icon, var/kit_icon_file = CUSTOM_ITEM_OBJ, var/kit_icon_override_file = CUSTOM_ITEM_MOB, var/additional_data) - ..() - - new_light_overlay = additional_data - - -/obj/item/device/kit/suit/customize(var/obj/item/I, var/mob/user) - if(can_customize(I)) - if(istype(I, /obj/item/clothing/head/helmet/space/void)) - var/obj/item/clothing/head/helmet/space/void/helmet = I - helmet.name = "[new_name] suit helmet" - helmet.desc = new_desc - helmet.icon_state = "[new_icon]_helmet" - helmet.item_state = "[new_icon]_helmet" - if(new_icon_file) - helmet.icon = new_icon_file - if(new_icon_override_file) - helmet.icon_override = new_icon_override_file - if(new_light_overlay) - helmet.light_overlay = new_light_overlay - to_chat(user, "You set about modifying the helmet into [helmet].") - var/mob/living/carbon/human/H = user - if(istype(H)) - helmet.species_restricted = list(H.species.get_bodytype(H)) - else if(istype(I, /obj/item/clothing/suit/storage/hooded)) - var/obj/item/clothing/suit/storage/hooded/suit = I - suit.name = "[new_name] suit" - suit.desc = new_desc - suit.icon_state = "[new_icon]_suit" - suit.toggleicon = "[new_icon]_suit" - var/obj/item/clothing/head/hood/S = suit.hood - S.icon_state = "[new_icon]_helmet" - if(new_icon_file) - suit.icon = new_icon_file - S.icon = new_icon_file - if(new_icon_override_file) - suit.icon_override = new_icon_override_file - S.icon_override = new_icon_override_file - to_chat(user, "You set about modifying the suit into [suit].") -// var/mob/living/carbon/human/H = user -// if(istype(H)) -// suit.species_restricted = list(H.species.get_bodytype(H)) Does not quite make sense for something usually very pliable. - else - var/obj/item/clothing/suit/space/void/suit = I - suit.name = "[new_name] voidsuit" - suit.desc = new_desc - suit.icon_state = "[new_icon]_suit" - suit.item_state = "[new_icon]_suit" - if(new_icon_file) - suit.icon = new_icon_file - if(new_icon_override_file) - suit.icon_override = new_icon_override_file - to_chat(user, "You set about modifying the suit into [suit].") - var/mob/living/carbon/human/H = user - if(istype(H)) - suit.species_restricted = list(H.species.get_bodytype(H)) - use(1,user) - -/obj/item/clothing/head/helmet/space/void/attackby(var/obj/item/O, var/mob/user) - if(istype(O,/obj/item/device/kit/suit)) - var/obj/item/device/kit/suit/kit = O - kit.customize(src, user) - return - return ..() - -/obj/item/clothing/suit/space/void/attackby(var/obj/item/O, var/mob/user) - if(istype(O,/obj/item/device/kit/suit)) - var/obj/item/device/kit/suit/kit = O - kit.customize(src, user) - return - return ..() - -/obj/item/clothing/suit/storage/hooded/attackby(var/obj/item/O, var/mob/user) - if(istype(O,/obj/item/device/kit/suit)) - var/obj/item/device/kit/suit/kit = O - kit.customize(src, user) - return - return ..() - -/obj/item/device/kit/suit/rig - name = "rig modification kit" - desc = "A kit for modifying a rigsuit." - uses = 1 - -/obj/item/device/kit/suit/rig/customize(var/obj/item/I, var/mob/user) - var/obj/item/weapon/rig/RIG = I - RIG.suit_state = new_icon - RIG.item_state = new_icon - RIG.suit_type = "customized [initial(RIG.suit_type)]" - RIG.name = "[new_name]" - RIG.desc = new_desc - RIG.icon = new_icon_file - RIG.icon_state = new_icon - RIG.icon_override = new_icon_override_file - for(var/obj/item/piece in list(RIG.gloves,RIG.helmet,RIG.boots,RIG.chest)) - if(!istype(piece)) - continue - piece.name = "[RIG.suit_type] [initial(piece.name)]" - piece.desc = "It seems to be part of a [RIG.name]." - piece.icon_state = "[RIG.suit_state]" - if(istype(piece, /obj/item/clothing/shoes)) - piece.icon = 'icons/mob/custom_items_rig_boots.dmi' - piece.icon_override = 'icons/mob/custom_items_rig_boots.dmi' - if(istype(piece, /obj/item/clothing/suit)) - piece.icon = 'icons/mob/custom_items_rig_suit.dmi' - piece.icon_override = 'icons/mob/custom_items_rig_suit.dmi' - if(istype(piece, /obj/item/clothing/head)) - piece.icon = 'icons/mob/custom_items_rig_helmet.dmi' - piece.icon_override = 'icons/mob/custom_items_rig_helmet.dmi' - if(istype(piece, /obj/item/clothing/gloves)) - piece.icon = 'icons/mob/custom_items_rig_gloves.dmi' - piece.icon_override = 'icons/mob/custom_items_rig_gloves.dmi' - if(RIG.helmet && istype(RIG.helmet, /obj/item/clothing/head/helmet) && new_light_overlay) - var/obj/item/clothing/head/helmet/H = RIG.helmet - H.light_overlay = new_light_overlay - use(1,user) - -/obj/item/device/kit/suit/rig/can_customize(var/obj/item/I) - return istype(I, /obj/item/weapon/rig) - -/obj/item/weapon/rig/attackby(var/obj/item/O, var/mob/user) - if(istype(O,/obj/item/device/kit/suit)) - var/obj/item/device/kit/suit/rig/kit = O - kit.customize(src, user) - return - return ..() - -/obj/item/device/kit/suit/rig/debug/Initialize() - set_info("debug suit", "This is a test", "debug", CUSTOM_ITEM_OBJ, CUSTOM_ITEM_MOB) - -/obj/item/device/kit/paint - name = "mecha customisation kit" - desc = "A kit containing all the needed tools and parts to repaint a mech." - var/removable = null - -/obj/item/device/kit/paint/can_customize(var/obj/mecha/M) - if(!istype(M)) - return 0 - - for(var/type in allowed_types) - if(type == M.initial_icon) - return 1 - -/obj/item/device/kit/paint/set_info(var/kit_name, var/kit_desc, var/kit_icon, var/kit_icon_file = CUSTOM_ITEM_OBJ, var/kit_icon_override_file = CUSTOM_ITEM_MOB, var/additional_data) - ..() - - allowed_types = splittext(additional_data, ", ") - - -/obj/item/device/kit/paint/examine() - . = ..() - . += "This kit will convert an exosuit into: [new_name]." - . += "This kit can be used on the following exosuit models:" - for(var/exotype in allowed_types) - . += "- [capitalize(exotype)]" - -/obj/item/device/kit/paint/customize(var/obj/mecha/M, var/mob/user) - if(!can_customize(M)) - to_chat(user, "That kit isn't meant for use on this class of exosuit.") - return - - if(M.occupant) - to_chat(user, "You can't customize a mech while someone is piloting it - that would be unsafe!") - return - - user.visible_message("[user] opens [src] and spends some quality time customising [M].") - M.name = new_name - M.desc = new_desc - M.initial_icon = new_icon - if(new_icon_file) - M.icon = new_icon_file - M.update_icon() - use(1, user) - -/obj/mecha/attackby(var/obj/item/weapon/W, var/mob/user) - if(istype(W, /obj/item/device/kit/paint)) - var/obj/item/device/kit/paint/P = W - P.customize(src, user) - return - else - return ..() - -//Ripley APLU kits. -/obj/item/device/kit/paint/ripley - name = "\"Classic\" APLU customisation kit" - new_name = "APLU \"Classic\"" - new_desc = "A very retro APLU unit; didn't they retire these back in 2303?" - new_icon = "ripley-old" - allowed_types = list("ripley") - var/showpilot = TRUE - var/showpilot_lift = 5 - -/obj/item/device/kit/paint/ripley/customize(obj/mecha/M, mob/user) - if(showpilot) - M.show_pilot = TRUE - M.pilot_lift = 5 - else - M.show_pilot = FALSE - M.pilot_lift = 0 - . = ..() - -/obj/item/device/kit/paint/ripley/death - name = "\"Reaper\" APLU customisation kit" - new_name = "APLU \"Reaper\"" - new_desc = "A terrifying, grim power loader. Why do those clamps have spikes?" - new_icon = "deathripley" - allowed_types = list("ripley","firefighter") - showpilot = FALSE - -/obj/item/device/kit/paint/ripley/flames_red - name = "\"Firestarter\" APLU customisation kit" - new_name = "APLU \"Firestarter\"" - new_desc = "A standard APLU exosuit with stylish orange flame decals." - new_icon = "ripley_flames_red" - showpilot = FALSE - -/obj/item/device/kit/paint/ripley/flames_blue - name = "\"Burning Chrome\" APLU customisation kit" - new_name = "APLU \"Burning Chrome\"" - new_desc = "A standard APLU exosuit with stylish blue flame decals." - new_icon = "ripley_flames_blue" - showpilot = FALSE - -// Durand kits. -/obj/item/device/kit/paint/durand - name = "\"Classic\" Durand customisation kit" - new_name = "Durand \"Classic\"" - new_desc = "An older model of Durand combat exosuit. This model was retired for rotating a pilot's torso 180 degrees." - new_icon = "old_durand" - allowed_types = list("durand") - -/obj/item/device/kit/paint/durand/seraph - name = "\"Cherubim\" Durand customisation kit" - new_name = "Durand \"Cherubim\"" - new_desc = "A Durand combat exosuit modelled after ancient Earth entertainment. Your heart goes doki-doki just looking at it." - new_icon = "old_durand" - -/obj/item/device/kit/paint/durand/phazon - name = "\"Sypher\" Durand customisation kit" - new_name = "Durand \"Sypher\"" - new_desc = "A Durand combat exosuit with some very stylish neons and decals. Seems to blur slightly at the edges; probably an optical illusion." - new_icon = "phazon" - -// Gygax kits. -/obj/item/device/kit/paint/gygax - name = "\"Jester\" Gygax customisation kit" - new_name = "Gygax \"Jester\"" - new_desc = "A Gygax exosuit modelled after the infamous combat-troubadors of Earth's distant past. Terrifying to behold." - new_icon = "honker" - allowed_types = list("gygax") - -/obj/item/device/kit/paint/gygax/darkgygax - name = "\"Silhouette\" Gygax customisation kit" - new_name = "Gygax \"Silhouette\"" - new_desc = "An ominous Gygax exosuit modelled after the fictional corporate 'death squads' that were popular in pulp action-thrillers back in 2314." - new_icon = "darkgygax" - -/obj/item/device/kit/paint/gygax/recitence - name = "\"Gaoler\" Gygax customisation kit" - new_name = "Durand \"Gaoler\"" - new_desc = "A bulky silver Gygax exosuit. The extra armour appears to be painted on, but it's very shiny." - new_icon = "recitence" +/obj/item/device/kit + icon_state = "modkit" + icon = 'icons/obj/device.dmi' + w_class = ITEMSIZE_SMALL + var/new_name = "custom item" + var/new_desc = "A custom item." + var/new_icon + var/new_icon_file + var/new_icon_override_file + var/uses = 1 // Uses before the kit deletes itself. + var/list/allowed_types = list() + +/obj/item/device/kit/examine() + . = ..() + . += "It has [uses] use\s left." + +/obj/item/device/kit/proc/use(var/amt, var/mob/user) + uses -= amt + playsound(src, 'sound/items/Screwdriver.ogg', 50, 1) + if(uses<1) + user.drop_item() + qdel(src) + +/obj/item/device/kit/proc/can_customize(var/obj/item/I) + return is_type_in_list(I, allowed_types) + +/obj/item/device/kit/proc/set_info(var/kit_name, var/kit_desc, var/kit_icon, var/kit_icon_file = CUSTOM_ITEM_OBJ, var/kit_icon_override_file = CUSTOM_ITEM_MOB, var/additional_data) + new_name = kit_name + new_desc = kit_desc + new_icon = kit_icon + new_icon_file = kit_icon_file + new_icon_override_file = kit_icon_override_file + + for(var/path in splittext(additional_data, ", ")) + allowed_types |= text2path(path) + +/obj/item/device/kit/proc/customize(var/obj/item/I, var/mob/user) + if(can_customize(I)) + I.name = new_name ? new_name : I.name + I.desc = new_desc ? new_desc : I.desc + I.icon = new_icon_file ? new_icon_file : I.icon + I.icon_override = new_icon_override_file ? new_icon_override_file : I.icon_override + if(new_icon) + I.icon_state = new_icon + var/obj/item/clothing/under/U = I + if(istype(U)) + U.worn_state = I.icon_state + U.update_rolldown_status() + use(1, user) + +// Generic use +/obj/item/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/device/kit)) + var/obj/item/device/kit/K = W + K.customize(src, user) + return + + ..() + +// Root hardsuit kit defines. +// Icons for modified hardsuits need to be in the proper .dmis because suit cyclers may cock them up. +/obj/item/device/kit/suit + name = "voidsuit modification kit" + desc = "A kit for modifying a voidsuit." + uses = 2 + var/new_light_overlay + +/obj/item/device/kit/suit/can_customize(var/obj/item/I) + return istype(I, /obj/item/clothing/head/helmet/space/void) || istype(I, /obj/item/clothing/suit/space/void) || istype(I, /obj/item/clothing/suit/storage/hooded) + +/obj/item/device/kit/suit/set_info(var/kit_name, var/kit_desc, var/kit_icon, var/kit_icon_file = CUSTOM_ITEM_OBJ, var/kit_icon_override_file = CUSTOM_ITEM_MOB, var/additional_data) + ..() + + new_light_overlay = additional_data + + +/obj/item/device/kit/suit/customize(var/obj/item/I, var/mob/user) + if(can_customize(I)) + if(istype(I, /obj/item/clothing/head/helmet/space/void)) + var/obj/item/clothing/head/helmet/space/void/helmet = I + helmet.name = "[new_name] suit helmet" + helmet.desc = new_desc + helmet.icon_state = "[new_icon]_helmet" + helmet.item_state = "[new_icon]_helmet" + if(new_icon_file) + helmet.icon = new_icon_file + if(new_icon_override_file) + helmet.icon_override = new_icon_override_file + if(new_light_overlay) + helmet.light_overlay = new_light_overlay + to_chat(user, "You set about modifying the helmet into [helmet].") + var/mob/living/carbon/human/H = user + if(istype(H)) + helmet.species_restricted = list(H.species.get_bodytype(H)) + else if(istype(I, /obj/item/clothing/suit/storage/hooded)) + var/obj/item/clothing/suit/storage/hooded/suit = I + suit.name = "[new_name] suit" + suit.desc = new_desc + suit.icon_state = "[new_icon]_suit" + suit.toggleicon = "[new_icon]_suit" + var/obj/item/clothing/head/hood/S = suit.hood + S.icon_state = "[new_icon]_helmet" + if(new_icon_file) + suit.icon = new_icon_file + S.icon = new_icon_file + if(new_icon_override_file) + suit.icon_override = new_icon_override_file + S.icon_override = new_icon_override_file + to_chat(user, "You set about modifying the suit into [suit].") +// var/mob/living/carbon/human/H = user +// if(istype(H)) +// suit.species_restricted = list(H.species.get_bodytype(H)) Does not quite make sense for something usually very pliable. + else + var/obj/item/clothing/suit/space/void/suit = I + suit.name = "[new_name] voidsuit" + suit.desc = new_desc + suit.icon_state = "[new_icon]_suit" + suit.item_state = "[new_icon]_suit" + if(new_icon_file) + suit.icon = new_icon_file + if(new_icon_override_file) + suit.icon_override = new_icon_override_file + to_chat(user, "You set about modifying the suit into [suit].") + var/mob/living/carbon/human/H = user + if(istype(H)) + suit.species_restricted = list(H.species.get_bodytype(H)) + use(1,user) + +/obj/item/clothing/head/helmet/space/void/attackby(var/obj/item/O, var/mob/user) + if(istype(O,/obj/item/device/kit/suit)) + var/obj/item/device/kit/suit/kit = O + kit.customize(src, user) + return + return ..() + +/obj/item/clothing/suit/space/void/attackby(var/obj/item/O, var/mob/user) + if(istype(O,/obj/item/device/kit/suit)) + var/obj/item/device/kit/suit/kit = O + kit.customize(src, user) + return + return ..() + +/obj/item/clothing/suit/storage/hooded/attackby(var/obj/item/O, var/mob/user) + if(istype(O,/obj/item/device/kit/suit)) + var/obj/item/device/kit/suit/kit = O + kit.customize(src, user) + return + return ..() + +/obj/item/device/kit/suit/rig + name = "rig modification kit" + desc = "A kit for modifying a rigsuit." + uses = 1 + +/obj/item/device/kit/suit/rig/customize(var/obj/item/I, var/mob/user) + var/obj/item/weapon/rig/RIG = I + RIG.suit_state = new_icon + RIG.item_state = new_icon + RIG.suit_type = "customized [initial(RIG.suit_type)]" + RIG.name = "[new_name]" + RIG.desc = new_desc + RIG.icon = new_icon_file + RIG.icon_state = new_icon + RIG.icon_override = new_icon_override_file + for(var/obj/item/piece in list(RIG.gloves,RIG.helmet,RIG.boots,RIG.chest)) + if(!istype(piece)) + continue + piece.name = "[RIG.suit_type] [initial(piece.name)]" + piece.desc = "It seems to be part of a [RIG.name]." + piece.icon_state = "[RIG.suit_state]" + if(istype(piece, /obj/item/clothing/shoes)) + piece.icon = 'icons/mob/custom_items_rig_boots.dmi' + piece.icon_override = 'icons/mob/custom_items_rig_boots.dmi' + if(istype(piece, /obj/item/clothing/suit)) + piece.icon = 'icons/mob/custom_items_rig_suit.dmi' + piece.icon_override = 'icons/mob/custom_items_rig_suit.dmi' + if(istype(piece, /obj/item/clothing/head)) + piece.icon = 'icons/mob/custom_items_rig_helmet.dmi' + piece.icon_override = 'icons/mob/custom_items_rig_helmet.dmi' + if(istype(piece, /obj/item/clothing/gloves)) + piece.icon = 'icons/mob/custom_items_rig_gloves.dmi' + piece.icon_override = 'icons/mob/custom_items_rig_gloves.dmi' + if(RIG.helmet && istype(RIG.helmet, /obj/item/clothing/head/helmet) && new_light_overlay) + var/obj/item/clothing/head/helmet/H = RIG.helmet + H.light_overlay = new_light_overlay + use(1,user) + +/obj/item/device/kit/suit/rig/can_customize(var/obj/item/I) + return istype(I, /obj/item/weapon/rig) + +/obj/item/weapon/rig/attackby(var/obj/item/O, var/mob/user) + if(istype(O,/obj/item/device/kit/suit)) + var/obj/item/device/kit/suit/rig/kit = O + kit.customize(src, user) + return + return ..() + +/obj/item/device/kit/suit/rig/debug/Initialize() + set_info("debug suit", "This is a test", "debug", CUSTOM_ITEM_OBJ, CUSTOM_ITEM_MOB) + +/obj/item/device/kit/paint + name = "mecha customisation kit" + desc = "A kit containing all the needed tools and parts to repaint a mech." + var/removable = null + +/obj/item/device/kit/paint/can_customize(var/obj/mecha/M) + if(!istype(M)) + return 0 + + for(var/type in allowed_types) + if(type == M.initial_icon) + return 1 + +/obj/item/device/kit/paint/set_info(var/kit_name, var/kit_desc, var/kit_icon, var/kit_icon_file = CUSTOM_ITEM_OBJ, var/kit_icon_override_file = CUSTOM_ITEM_MOB, var/additional_data) + ..() + + allowed_types = splittext(additional_data, ", ") + + +/obj/item/device/kit/paint/examine() + . = ..() + . += "This kit will convert an exosuit into: [new_name]." + . += "This kit can be used on the following exosuit models:" + for(var/exotype in allowed_types) + . += "- [capitalize(exotype)]" + +/obj/item/device/kit/paint/customize(var/obj/mecha/M, var/mob/user) + if(!can_customize(M)) + to_chat(user, "That kit isn't meant for use on this class of exosuit.") + return + + if(M.occupant) + to_chat(user, "You can't customize a mech while someone is piloting it - that would be unsafe!") + return + + user.visible_message("[user] opens [src] and spends some quality time customising [M].") + M.name = new_name + M.desc = new_desc + M.initial_icon = new_icon + if(new_icon_file) + M.icon = new_icon_file + M.update_icon() + use(1, user) + +/obj/mecha/attackby(var/obj/item/weapon/W, var/mob/user) + if(istype(W, /obj/item/device/kit/paint)) + var/obj/item/device/kit/paint/P = W + P.customize(src, user) + return + else + return ..() + +//Ripley APLU kits. +/obj/item/device/kit/paint/ripley + name = "\"Classic\" APLU customisation kit" + new_name = "APLU \"Classic\"" + new_desc = "A very retro APLU unit; didn't they retire these back in 2303?" + new_icon = "ripley-old" + allowed_types = list("ripley") + var/showpilot = TRUE + var/showpilot_lift = 5 + +/obj/item/device/kit/paint/ripley/customize(obj/mecha/M, mob/user) + if(showpilot) + M.show_pilot = TRUE + M.pilot_lift = 5 + else + M.show_pilot = FALSE + M.pilot_lift = 0 + . = ..() + +/obj/item/device/kit/paint/ripley/death + name = "\"Reaper\" APLU customisation kit" + new_name = "APLU \"Reaper\"" + new_desc = "A terrifying, grim power loader. Why do those clamps have spikes?" + new_icon = "deathripley" + allowed_types = list("ripley","firefighter") + showpilot = FALSE + +/obj/item/device/kit/paint/ripley/flames_red + name = "\"Firestarter\" APLU customisation kit" + new_name = "APLU \"Firestarter\"" + new_desc = "A standard APLU exosuit with stylish orange flame decals." + new_icon = "ripley_flames_red" + showpilot = FALSE + +/obj/item/device/kit/paint/ripley/flames_blue + name = "\"Burning Chrome\" APLU customisation kit" + new_name = "APLU \"Burning Chrome\"" + new_desc = "A standard APLU exosuit with stylish blue flame decals." + new_icon = "ripley_flames_blue" + showpilot = FALSE + +// Durand kits. +/obj/item/device/kit/paint/durand + name = "\"Classic\" Durand customisation kit" + new_name = "Durand \"Classic\"" + new_desc = "An older model of Durand combat exosuit. This model was retired for rotating a pilot's torso 180 degrees." + new_icon = "old_durand" + allowed_types = list("durand") + +/obj/item/device/kit/paint/durand/seraph + name = "\"Cherubim\" Durand customisation kit" + new_name = "Durand \"Cherubim\"" + new_desc = "A Durand combat exosuit modelled after ancient Earth entertainment. Your heart goes doki-doki just looking at it." + new_icon = "old_durand" + +/obj/item/device/kit/paint/durand/phazon + name = "\"Sypher\" Durand customisation kit" + new_name = "Durand \"Sypher\"" + new_desc = "A Durand combat exosuit with some very stylish neons and decals. Seems to blur slightly at the edges; probably an optical illusion." + new_icon = "phazon" + +// Gygax kits. +/obj/item/device/kit/paint/gygax + name = "\"Jester\" Gygax customisation kit" + new_name = "Gygax \"Jester\"" + new_desc = "A Gygax exosuit modelled after the infamous combat-troubadors of Earth's distant past. Terrifying to behold." + new_icon = "honker" + allowed_types = list("gygax") + +/obj/item/device/kit/paint/gygax/darkgygax + name = "\"Silhouette\" Gygax customisation kit" + new_name = "Gygax \"Silhouette\"" + new_desc = "An ominous Gygax exosuit modelled after the fictional corporate 'death squads' that were popular in pulp action-thrillers back in 2314." + new_icon = "darkgygax" + +/obj/item/device/kit/paint/gygax/recitence + name = "\"Gaoler\" Gygax customisation kit" + new_name = "Durand \"Gaoler\"" + new_desc = "A bulky silver Gygax exosuit. The extra armour appears to be painted on, but it's very shiny." + new_icon = "recitence" diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index ddeac74b584..c7563b4412f 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -1,84 +1,84 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/********************************************************************** - Cyborg Spec Items -***********************************************************************/ -//Might want to move this into several files later but for now it works here - -/obj/item/weapon/melee/baton/robot/arm - name = "electrified arm" - icon = 'icons/obj/decals.dmi' - icon_state = "shock" - - hitcost = 750 - agonyforce = 70 - -/obj/item/weapon/melee/baton/robot/arm/update_icon() - if(status) - set_light(1.5, 1, lightcolor) - else - set_light(0) - -/obj/item/borg/overdrive - name = "overdrive" - icon = 'icons/obj/decals.dmi' - icon_state = "shock" - -/********************************************************************** - HUD/SIGHT things -***********************************************************************/ -/obj/item/borg/sight - icon = 'icons/obj/decals.dmi' - icon_state = "securearea" - var/sight_mode = null - - -/obj/item/borg/sight/xray - name = "\proper x-ray vision" - sight_mode = BORGXRAY - - -/obj/item/borg/sight/thermal - name = "\proper thermal vision" - sight_mode = BORGTHERM - icon_state = "thermal" - icon = 'icons/inventory/eyes/item.dmi' - - -/obj/item/borg/sight/meson - name = "\proper meson vision" - sight_mode = BORGMESON - icon_state = "meson" - icon = 'icons/inventory/eyes/item.dmi' - -/obj/item/borg/sight/material - name = "\proper material scanner vision" - sight_mode = BORGMATERIAL - icon_state = "material" - icon = 'icons/inventory/eyes/item.dmi' - -/obj/item/borg/sight/hud - name = "hud" - var/obj/item/clothing/glasses/hud/hud = null - - -/obj/item/borg/sight/hud/med - name = "medical hud" - icon_state = "healthhud" - icon = 'icons/inventory/eyes/item.dmi' - -/obj/item/borg/sight/hud/med/New() - ..() - hud = new /obj/item/clothing/glasses/hud/health(src) - return - - -/obj/item/borg/sight/hud/sec - name = "security hud" - icon_state = "securityhud" - icon = 'icons/inventory/eyes/item.dmi' - -/obj/item/borg/sight/hud/sec/New() - ..() - hud = new /obj/item/clothing/glasses/hud/security(src) - return +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/********************************************************************** + Cyborg Spec Items +***********************************************************************/ +//Might want to move this into several files later but for now it works here + +/obj/item/weapon/melee/baton/robot/arm + name = "electrified arm" + icon = 'icons/obj/decals.dmi' + icon_state = "shock" + + hitcost = 750 + agonyforce = 70 + +/obj/item/weapon/melee/baton/robot/arm/update_icon() + if(status) + set_light(1.5, 1, lightcolor) + else + set_light(0) + +/obj/item/borg/overdrive + name = "overdrive" + icon = 'icons/obj/decals.dmi' + icon_state = "shock" + +/********************************************************************** + HUD/SIGHT things +***********************************************************************/ +/obj/item/borg/sight + icon = 'icons/obj/decals.dmi' + icon_state = "securearea" + var/sight_mode = null + + +/obj/item/borg/sight/xray + name = "\proper x-ray vision" + sight_mode = BORGXRAY + + +/obj/item/borg/sight/thermal + name = "\proper thermal vision" + sight_mode = BORGTHERM + icon_state = "thermal" + icon = 'icons/inventory/eyes/item.dmi' + + +/obj/item/borg/sight/meson + name = "\proper meson vision" + sight_mode = BORGMESON + icon_state = "meson" + icon = 'icons/inventory/eyes/item.dmi' + +/obj/item/borg/sight/material + name = "\proper material scanner vision" + sight_mode = BORGMATERIAL + icon_state = "material" + icon = 'icons/inventory/eyes/item.dmi' + +/obj/item/borg/sight/hud + name = "hud" + var/obj/item/clothing/glasses/hud/hud = null + + +/obj/item/borg/sight/hud/med + name = "medical hud" + icon_state = "healthhud" + icon = 'icons/inventory/eyes/item.dmi' + +/obj/item/borg/sight/hud/med/New() + ..() + hud = new /obj/item/clothing/glasses/hud/health(src) + return + + +/obj/item/borg/sight/hud/sec + name = "security hud" + icon_state = "securityhud" + icon = 'icons/inventory/eyes/item.dmi' + +/obj/item/borg/sight/hud/sec/New() + ..() + hud = new /obj/item/clothing/glasses/hud/security(src) + return diff --git a/code/game/objects/items/shooting_range.dm b/code/game/objects/items/shooting_range.dm index 12423a91f38..53ea1523855 100644 --- a/code/game/objects/items/shooting_range.dm +++ b/code/game/objects/items/shooting_range.dm @@ -1,180 +1,180 @@ -// Targets, the things that actually get shot! -/obj/item/target - name = "shooting target" - desc = "A shooting target." - icon = 'icons/obj/objects.dmi' - icon_state = "target_h" - density = FALSE - var/hp = 1800 - var/icon/virtualIcon - var/list/bulletholes = list() - -/obj/item/target/Destroy() - // if a target is deleted and associated with a stake, force stake to forget - for(var/obj/structure/target_stake/T in view(3,src)) - if(T.pinned_target == src) - T.pinned_target = null - T.density = TRUE - break - ..() // delete target - -/obj/item/target/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - // After target moves, check for nearby stakes. If associated, move to target - for(var/obj/structure/target_stake/M in view(3,src)) - if(M.density == 0 && M.pinned_target == src) - M.forceMove(loc) - - // This may seem a little counter-intuitive but I assure you that's for a purpose. - // Stakes are the ones that carry targets, yes, but in the stake code we set - // a stake's density to 0 meaning it can't be pushed anymore. Instead of pushing - // the stake now, we have to push the target. - - - -/obj/item/target/attackby(obj/item/W as obj, mob/user as mob) - if (W.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/WT = W.get_welder() - if(WT.remove_fuel(0, user)) - cut_overlays() - to_chat(usr, "You slice off [src]'s uneven chunks of aluminum and scorch marks.") - return - - -/obj/item/target/attack_hand(mob/user as mob) - // taking pinned targets off! - var/obj/structure/target_stake/stake - for(var/obj/structure/target_stake/T in view(3,src)) - if(T.pinned_target == src) - stake = T - break - - if(stake) - if(stake.pinned_target) - stake.density = TRUE - density = FALSE - layer = OBJ_LAYER - - loc = user.loc - if(ishuman(user)) - if(!user.get_active_hand()) - user.put_in_hands(src) - to_chat(user, "You take the target out of the stake.") - else - src.loc = get_turf(user) - to_chat(user, "You take the target out of the stake.") - - stake.pinned_target = null - return - - else - ..() - -/obj/item/target/syndicate - icon_state = "target_s" - desc = "A shooting target that looks like a hostile agent." - hp = 2600 // i guess syndie targets are sturdier? -/obj/item/target/alien - icon_state = "target_q" - desc = "A shooting target with a threatening silhouette." - hp = 2350 // alium onest too kinda - -/obj/item/target/bullet_act(var/obj/item/projectile/Proj) - var/p_x = Proj.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset Proj.p_x!" - var/p_y = Proj.p_y + pick(0,0,0,0,0,-1,1) - var/decaltype = 1 // 1 - scorch, 2 - bullet - - if(istype(/obj/item/projectile/bullet, Proj)) - decaltype = 2 - - - virtualIcon = new(icon, icon_state) - - if( virtualIcon.GetPixel(p_x, p_y) ) // if the located pixel isn't blank (null) - - hp -= Proj.damage - if(hp <= 0) - for(var/mob/O in oviewers()) - if ((O.client && !( O.blinded ))) - to_chat(O, "\The [src] breaks into tiny pieces and collapses!") - qdel(src) - - // Create a temporary object to represent the damage - var/obj/bmark = new - bmark.pixel_x = p_x - bmark.pixel_y = p_y - bmark.icon = 'icons/effects/effects.dmi' - bmark.icon_state = "scorch" - - if(decaltype == 1) - // Energy weapons are hot. they scorch! - - // offset correction - bmark.pixel_x-- - bmark.pixel_y-- - - if(Proj.damage >= 20 || istype(Proj, /obj/item/projectile/beam/practice)) - bmark.icon_state = "scorch" - bmark.set_dir(pick(NORTH,SOUTH,EAST,WEST)) // random scorch design - - - else - bmark.icon_state = "light_scorch" - else - - // Bullets are hard. They make dents! - bmark.icon_state = "dent" - - if(Proj.damage >= 10 && bulletholes.len <= 35) // maximum of 35 bullet holes - if(decaltype == 2) // bullet - if(prob(Proj.damage+30)) // bullets make holes more commonly! - new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole - else // Lasers! - if(prob(Proj.damage-10)) // lasers make holes less commonly - new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole - - // draw bullet holes - for(var/datum/bullethole/B in bulletholes) - - virtualIcon.DrawBox(null, B.b1x1, B.b1y, B.b1x2, B.b1y) // horizontal line, left to right - virtualIcon.DrawBox(null, B.b2x, B.b2y1, B.b2x, B.b2y2) // vertical line, top to bottom - - add_overlay(bmark) // add the decal - - icon = virtualIcon // apply bulletholes over decals - - return - - return PROJECTILE_CONTINUE // the bullet/projectile goes through the target! - - -// Small memory holder entity for transparent bullet holes -/datum/bullethole - // First box - var/b1x1 = 0 - var/b1x2 = 0 - var/b1y = 0 - - // Second box - var/b2x = 0 - var/b2y1 = 0 - var/b2y2 = 0 - -/datum/bullethole/New(var/obj/item/target/Target, var/pixel_x = 0, var/pixel_y = 0) - if(!Target) return - - // Randomize the first box - b1x1 = pixel_x - pick(1,1,1,1,2,2,3,3,4) - b1x2 = pixel_x + pick(1,1,1,1,2,2,3,3,4) - b1y = pixel_y - if(prob(35)) - b1y += rand(-4,4) - - // Randomize the second box - b2x = pixel_x - if(prob(35)) - b2x += rand(-4,4) - b2y1 = pixel_y + pick(1,1,1,1,2,2,3,3,4) - b2y2 = pixel_y - pick(1,1,1,1,2,2,3,3,4) - - Target.bulletholes.Add(src) +// Targets, the things that actually get shot! +/obj/item/target + name = "shooting target" + desc = "A shooting target." + icon = 'icons/obj/objects.dmi' + icon_state = "target_h" + density = FALSE + var/hp = 1800 + var/icon/virtualIcon + var/list/bulletholes = list() + +/obj/item/target/Destroy() + // if a target is deleted and associated with a stake, force stake to forget + for(var/obj/structure/target_stake/T in view(3,src)) + if(T.pinned_target == src) + T.pinned_target = null + T.density = TRUE + break + ..() // delete target + +/obj/item/target/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + // After target moves, check for nearby stakes. If associated, move to target + for(var/obj/structure/target_stake/M in view(3,src)) + if(M.density == 0 && M.pinned_target == src) + M.forceMove(loc) + + // This may seem a little counter-intuitive but I assure you that's for a purpose. + // Stakes are the ones that carry targets, yes, but in the stake code we set + // a stake's density to 0 meaning it can't be pushed anymore. Instead of pushing + // the stake now, we have to push the target. + + + +/obj/item/target/attackby(obj/item/W as obj, mob/user as mob) + if (W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(WT.remove_fuel(0, user)) + cut_overlays() + to_chat(usr, "You slice off [src]'s uneven chunks of aluminum and scorch marks.") + return + + +/obj/item/target/attack_hand(mob/user as mob) + // taking pinned targets off! + var/obj/structure/target_stake/stake + for(var/obj/structure/target_stake/T in view(3,src)) + if(T.pinned_target == src) + stake = T + break + + if(stake) + if(stake.pinned_target) + stake.density = TRUE + density = FALSE + layer = OBJ_LAYER + + loc = user.loc + if(ishuman(user)) + if(!user.get_active_hand()) + user.put_in_hands(src) + to_chat(user, "You take the target out of the stake.") + else + src.loc = get_turf(user) + to_chat(user, "You take the target out of the stake.") + + stake.pinned_target = null + return + + else + ..() + +/obj/item/target/syndicate + icon_state = "target_s" + desc = "A shooting target that looks like a hostile agent." + hp = 2600 // i guess syndie targets are sturdier? +/obj/item/target/alien + icon_state = "target_q" + desc = "A shooting target with a threatening silhouette." + hp = 2350 // alium onest too kinda + +/obj/item/target/bullet_act(var/obj/item/projectile/Proj) + var/p_x = Proj.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset Proj.p_x!" + var/p_y = Proj.p_y + pick(0,0,0,0,0,-1,1) + var/decaltype = 1 // 1 - scorch, 2 - bullet + + if(istype(/obj/item/projectile/bullet, Proj)) + decaltype = 2 + + + virtualIcon = new(icon, icon_state) + + if( virtualIcon.GetPixel(p_x, p_y) ) // if the located pixel isn't blank (null) + + hp -= Proj.damage + if(hp <= 0) + for(var/mob/O in oviewers()) + if ((O.client && !( O.blinded ))) + to_chat(O, "\The [src] breaks into tiny pieces and collapses!") + qdel(src) + + // Create a temporary object to represent the damage + var/obj/bmark = new + bmark.pixel_x = p_x + bmark.pixel_y = p_y + bmark.icon = 'icons/effects/effects.dmi' + bmark.icon_state = "scorch" + + if(decaltype == 1) + // Energy weapons are hot. they scorch! + + // offset correction + bmark.pixel_x-- + bmark.pixel_y-- + + if(Proj.damage >= 20 || istype(Proj, /obj/item/projectile/beam/practice)) + bmark.icon_state = "scorch" + bmark.set_dir(pick(NORTH,SOUTH,EAST,WEST)) // random scorch design + + + else + bmark.icon_state = "light_scorch" + else + + // Bullets are hard. They make dents! + bmark.icon_state = "dent" + + if(Proj.damage >= 10 && bulletholes.len <= 35) // maximum of 35 bullet holes + if(decaltype == 2) // bullet + if(prob(Proj.damage+30)) // bullets make holes more commonly! + new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole + else // Lasers! + if(prob(Proj.damage-10)) // lasers make holes less commonly + new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole + + // draw bullet holes + for(var/datum/bullethole/B in bulletholes) + + virtualIcon.DrawBox(null, B.b1x1, B.b1y, B.b1x2, B.b1y) // horizontal line, left to right + virtualIcon.DrawBox(null, B.b2x, B.b2y1, B.b2x, B.b2y2) // vertical line, top to bottom + + add_overlay(bmark) // add the decal + + icon = virtualIcon // apply bulletholes over decals + + return + + return PROJECTILE_CONTINUE // the bullet/projectile goes through the target! + + +// Small memory holder entity for transparent bullet holes +/datum/bullethole + // First box + var/b1x1 = 0 + var/b1x2 = 0 + var/b1y = 0 + + // Second box + var/b2x = 0 + var/b2y1 = 0 + var/b2y2 = 0 + +/datum/bullethole/New(var/obj/item/target/Target, var/pixel_x = 0, var/pixel_y = 0) + if(!Target) return + + // Randomize the first box + b1x1 = pixel_x - pick(1,1,1,1,2,2,3,3,4) + b1x2 = pixel_x + pick(1,1,1,1,2,2,3,3,4) + b1y = pixel_y + if(prob(35)) + b1y += rand(-4,4) + + // Randomize the second box + b2x = pixel_x + if(prob(35)) + b2x += rand(-4,4) + b2y1 = pixel_y + pick(1,1,1,1,2,2,3,3,4) + b2y2 = pixel_y - pick(1,1,1,1,2,2,3,3,4) + + Target.bulletholes.Add(src) diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 0c3e0765f56..5b4a5c9d5ff 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -1,452 +1,452 @@ -/obj/item/stack/medical - name = "medical pack" - singular_name = "medical pack" - icon = 'icons/obj/stacks.dmi' - amount = 10 - max_amount = 10 - w_class = ITEMSIZE_SMALL - throw_speed = 4 - throw_range = 20 - var/heal_brute = 0 - var/heal_burn = 0 - var/apply_sounds - drop_sound = 'sound/items/drop/cardboardbox.ogg' - pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - - var/upgrade_to // The type path this stack can be upgraded to. - -/obj/item/stack/medical/attack(mob/living/carbon/M as mob, mob/user as mob) - if (!istype(M)) - to_chat(user, "\The [src] cannot be applied to [M]!") - return 1 - - if (!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do this!") - return 1 - - var/available = get_amount() - if(!available) - to_chat(user, "There's not enough [uses_charge ? "charge" : "items"] left to use that!") - return 1 - - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) - - if(!affecting) - to_chat(user, "No body part there to work on!") - return 1 - - if(affecting.organ_tag == BP_HEAD) - if(H.head && istype(H.head,/obj/item/clothing/head/helmet/space)) - to_chat(user, "You can't apply [src] through [H.head]!") - return 1 - else - if(H.wear_suit && istype(H.wear_suit,/obj/item/clothing/suit/space)) - to_chat(user, "You can't apply [src] through [H.wear_suit]!") - return 1 - - if(affecting.robotic == ORGAN_ROBOT) - to_chat(user, "This isn't useful at all on a robotic limb.") - return 1 - - if(affecting.robotic >= ORGAN_LIFELIKE) - to_chat(user, "You apply the [src], but it seems to have no effect...") - use(1) - return 1 - - H.UpdateDamageIcon() - - else - - M.heal_organ_damage((src.heal_brute/2), (src.heal_burn/2)) - user.visible_message( \ - "[M] has been applied with [src] by [user].", \ - "You apply \the [src] to [M]." \ - ) - use(1) - - M.updatehealth() - -/obj/item/stack/medical/proc/upgrade_stack(var/upgrade_amount) - . = FALSE - - var/turf/T = get_turf(src) - - if(ispath(upgrade_to) && use(upgrade_amount)) - var/obj/item/stack/medical/M = new upgrade_to(T, upgrade_amount) - return M - - return . - -/obj/item/stack/medical/crude_pack - name = "crude bandage" - singular_name = "crude bandage length" - desc = "Some bandages to wrap around bloody stumps." - icon_state = "gauze" - origin_tech = list(TECH_BIO = 1) - no_variants = FALSE - apply_sounds = list('sound/effects/rip1.ogg','sound/effects/rip2.ogg') - - upgrade_to = /obj/item/stack/medical/bruise_pack - -/obj/item/stack/medical/crude_pack/attack(mob/living/carbon/M as mob, mob/user as mob) - if(..()) - return 1 - - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) - - if(affecting.open) - to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") - return - - if(affecting.is_bandaged()) - to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") - return 1 - else - var/available = get_amount() - user.visible_message("\The [user] starts bandaging [M]'s [affecting.name].", \ - "You start bandaging [M]'s [affecting.name]." ) - var/used = 0 - for (var/datum/wound/W in affecting.wounds) - if(W.internal) - continue - if(W.bandaged) - continue - if(used == amount) - break - if(!do_mob(user, M, W.damage/3, exclusive = TRUE)) - to_chat(user, "You must stand still to bandage wounds.") - break - - if(affecting.is_bandaged()) // We do a second check after the delay, in case it was bandaged after the first check. - to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") - return 1 - - if(used >= available) - to_chat(user, "You run out of [src]!") - break - - if (W.current_stage <= W.max_bleeding_stage) - user.visible_message("\The [user] bandages \a [W.desc] on [M]'s [affecting.name].", \ - "You bandage \a [W.desc] on [M]'s [affecting.name]." ) - else - user.visible_message("\The [user] places a bandage over \a [W.desc] on [M]'s [affecting.name].", \ - "You place a bandage over \a [W.desc] on [M]'s [affecting.name]." ) - W.bandage() - playsound(src, pick(apply_sounds), 25) - used++ - affecting.update_damages() - if(used == amount) - if(affecting.is_bandaged()) - to_chat(user, "\The [src] is used up.") - else - to_chat(user, "\The [src] is used up, but there are more wounds to treat on \the [affecting.name].") - use(used) - -/obj/item/stack/medical/bruise_pack - name = "roll of gauze" - singular_name = "gauze length" - desc = "Some sterile gauze to wrap around bloody stumps." - icon_state = "brutepack" - origin_tech = list(TECH_BIO = 1) - no_variants = FALSE - apply_sounds = list('sound/effects/rip1.ogg','sound/effects/rip2.ogg') - drop_sound = 'sound/items/drop/gloves.ogg' - pickup_sound = 'sound/items/pickup/gloves.ogg' - - upgrade_to = /obj/item/stack/medical/advanced/bruise_pack - -/obj/item/stack/medical/bruise_pack/attack(mob/living/carbon/M as mob, mob/user as mob) - if(..()) - return 1 - - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) - - if(affecting.open) - to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") - return - - if(affecting.is_bandaged()) - to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") - return 1 - else - var/available = get_amount() - user.visible_message("\The [user] starts treating [M]'s [affecting.name].", \ - "You start treating [M]'s [affecting.name]." ) - var/used = 0 - for (var/datum/wound/W in affecting.wounds) - if (W.internal) - continue - if(W.bandaged) - continue - if(used == amount) - break - if(!do_mob(user, M, W.damage/5, exclusive = TRUE)) - to_chat(user, "You must stand still to bandage wounds.") - break - - if(affecting.is_bandaged()) // We do a second check after the delay, in case it was bandaged after the first check. - to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") - return 1 - - if(used >= available) - to_chat(user, "You run out of [src]!") - break - - if (W.current_stage <= W.max_bleeding_stage) - user.visible_message("\The [user] bandages \a [W.desc] on [M]'s [affecting.name].", \ - "You bandage \a [W.desc] on [M]'s [affecting.name]." ) - //H.add_side_effect("Itch") - else if (W.damage_type == BRUISE) - user.visible_message("\The [user] places a bruise patch over \a [W.desc] on [M]'s [affecting.name].", \ - "You place a bruise patch over \a [W.desc] on [M]'s [affecting.name]." ) - else - user.visible_message("\The [user] places a bandaid over \a [W.desc] on [M]'s [affecting.name].", \ - "You place a bandaid over \a [W.desc] on [M]'s [affecting.name]." ) - W.bandage() - // W.disinfect() // VOREStation - Tech1 should not disinfect - playsound(src, pick(apply_sounds), 25) - used++ - affecting.update_damages() - if(used == amount) - if(affecting.is_bandaged()) - to_chat(user, "\The [src] is used up.") - else - to_chat(user, "\The [src] is used up, but there are more wounds to treat on \the [affecting.name].") - use(used) - -/obj/item/stack/medical/ointment - name = "ointment" - desc = "Used to treat those nasty burns." - gender = PLURAL - singular_name = "ointment" - icon_state = "ointment" - heal_burn = 1 - origin_tech = list(TECH_BIO = 1) - no_variants = FALSE - apply_sounds = list('sound/effects/ointment.ogg') - drop_sound = 'sound/items/drop/herb.ogg' - pickup_sound = 'sound/items/pickup/herb.ogg' - -/obj/item/stack/medical/ointment/attack(mob/living/carbon/M as mob, mob/user as mob) - if(..()) - return 1 - - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) - - if(affecting.open) - to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") - return - - if(affecting.is_salved()) - to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") - return 1 - else - user.visible_message("\The [user] starts salving wounds on [M]'s [affecting.name].", \ - "You start salving the wounds on [M]'s [affecting.name]." ) - if(!do_mob(user, M, 10, exclusive = TRUE)) - to_chat(user, "You must stand still to salve wounds.") - return 1 - if(affecting.is_salved()) // We do a second check after the delay, in case it was bandaged after the first check. - to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") - return 1 - user.visible_message("[user] salved wounds on [M]'s [affecting.name].", \ - "You salved wounds on [M]'s [affecting.name]." ) - use(1) - affecting.salve() - playsound(src, pick(apply_sounds), 25) - -/obj/item/stack/medical/ointment/simple - name = "ointment paste" - desc = "A simple thick paste used to salve burns." - singular_name = "old-ointment" - icon_state = "old-ointment" - -/obj/item/stack/medical/advanced/bruise_pack - name = "advanced trauma kit" - singular_name = "advanced trauma kit" - desc = "An advanced trauma kit for severe injuries." - icon_state = "traumakit" - heal_brute = 7 //VOREStation Edit - origin_tech = list(TECH_BIO = 1) - apply_sounds = list('sound/effects/rip1.ogg','sound/effects/rip2.ogg','sound/effects/tape.ogg') - -/obj/item/stack/medical/advanced/bruise_pack/attack(mob/living/carbon/M as mob, mob/user as mob) - if(..()) - return 1 - - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) - - if(affecting.open) - to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") - return - - if(affecting.is_bandaged() && affecting.is_disinfected()) - to_chat(user, "The wounds on [M]'s [affecting.name] have already been treated.") - return 1 - else - var/available = get_amount() - user.visible_message("\The [user] starts treating [M]'s [affecting.name].", \ - "You start treating [M]'s [affecting.name]." ) - var/used = 0 - for (var/datum/wound/W in affecting.wounds) - if (W.internal) - continue - if (W.bandaged && W.disinfected) - continue - //if(used == amount) //VOREStation Edit - // break //VOREStation Edit - if(!do_mob(user, M, W.damage/5, exclusive = TRUE)) - to_chat(user, "You must stand still to bandage wounds.") - break - if(affecting.is_bandaged() && affecting.is_disinfected()) // We do a second check after the delay, in case it was bandaged after the first check. - to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") - return 1 - - if(used >= available) - to_chat(user, "You run out of [src]!") - break - - if (W.current_stage <= W.max_bleeding_stage) - user.visible_message("\The [user] cleans \a [W.desc] on [M]'s [affecting.name] and seals the edges with bioglue.", \ - "You clean and seal \a [W.desc] on [M]'s [affecting.name]." ) - else if (W.damage_type == BRUISE) - user.visible_message("\The [user] places a medical patch over \a [W.desc] on [M]'s [affecting.name].", \ - "You place a medical patch over \a [W.desc] on [M]'s [affecting.name]." ) - else - user.visible_message("\The [user] smears some bioglue over \a [W.desc] on [M]'s [affecting.name].", \ - "You smear some bioglue over \a [W.desc] on [M]'s [affecting.name]." ) - W.bandage() - W.disinfect() - W.heal_damage(heal_brute) - playsound(src, pick(apply_sounds), 25) - used = 1 //VOREStation Edit - update_icon() // VOREStation Edit - Support for stack icons - affecting.update_damages() - if(used == amount) - if(affecting.is_bandaged()) - to_chat(user, "\The [src] is used up.") - else - to_chat(user, "\The [src] is used up, but there are more wounds to treat on \the [affecting.name].") - use(used) - -/obj/item/stack/medical/advanced/ointment - name = "advanced burn kit" - singular_name = "advanced burn kit" - desc = "An advanced treatment kit for severe burns." - icon_state = "burnkit" - heal_burn = 7 //VOREStation Edit - origin_tech = list(TECH_BIO = 1) - apply_sounds = list('sound/effects/ointment.ogg') - -/obj/item/stack/medical/advanced/ointment/attack(mob/living/carbon/M as mob, mob/user as mob) - if(..()) - return 1 - - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) - - if(affecting.open) - to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") - - if(affecting.is_salved()) - to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") - return 1 - else - user.visible_message("\The [user] starts salving wounds on [M]'s [affecting.name].", \ - "You start salving the wounds on [M]'s [affecting.name]." ) - if(!do_mob(user, M, 10, exclusive = TRUE)) - to_chat(user, "You must stand still to salve wounds.") - return 1 - if(affecting.is_salved()) // We do a second check after the delay, in case it was bandaged after the first check. - to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") - return 1 - user.visible_message( "[user] covers wounds on [M]'s [affecting.name] with regenerative membrane.", \ - "You cover wounds on [M]'s [affecting.name] with regenerative membrane." ) - affecting.heal_damage(0,heal_burn) - use(1) - affecting.salve() - playsound(src, pick(apply_sounds), 25) - update_icon() // VOREStation Edit - Support for stack icons - -/obj/item/stack/medical/splint - name = "medical splints" - singular_name = "medical splint" - desc = "Modular splints capable of supporting and immobilizing bones in all areas of the body." - icon_state = "splint" - amount = 5 - max_amount = 5 - drop_sound = 'sound/items/drop/hat.ogg' - pickup_sound = 'sound/items/pickup/hat.ogg' - - var/list/splintable_organs = list(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM, BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_GROIN, BP_TORSO) //List of organs you can splint, natch. - -/obj/item/stack/medical/splint/attack(mob/living/carbon/M as mob, mob/living/user as mob) - if(..()) - return 1 - - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) - var/limb = affecting.name - if(!(affecting.organ_tag in splintable_organs)) - to_chat(user, "You can't use \the [src] to apply a splint there!") - return - if(affecting.splinted) - to_chat(user, "[M]'s [limb] is already splinted!") - return - if (M != user) - user.visible_message("[user] starts to apply \the [src] to [M]'s [limb].", "You start to apply \the [src] to [M]'s [limb].", "You hear something being wrapped.") - else - if(( !user.hand && (affecting.organ_tag in list(BP_R_ARM, BP_R_HAND)) || \ - user.hand && (affecting.organ_tag in list(BP_L_ARM, BP_L_HAND)) )) - to_chat(user, "You can't apply a splint to the arm you're using!") - return - user.visible_message("[user] starts to apply \the [src] to their [limb].", "You start to apply \the [src] to your [limb].", "You hear something being wrapped.") - if(do_after(user, 50, M, exclusive = TASK_USER_EXCLUSIVE)) - if(affecting.splinted) - to_chat(user, "[M]'s [limb] is already splinted!") - return - if(M == user && prob(75)) - user.visible_message("\The [user] fumbles [src].", "You fumble [src].", "You hear something being wrapped.") - return - if(ishuman(user)) - var/obj/item/stack/medical/splint/S = split(1) - if(S) - if(affecting.apply_splint(S)) - S.forceMove(affecting) - if (M != user) - user.visible_message("\The [user] finishes applying [src] to [M]'s [limb].", "You finish applying \the [src] to [M]'s [limb].", "You hear something being wrapped.") - else - user.visible_message("\The [user] successfully applies [src] to their [limb].", "You successfully apply \the [src] to your [limb].", "You hear something being wrapped.") - return - S.dropInto(src.loc) //didn't get applied, so just drop it - if(isrobot(user)) - var/obj/item/stack/medical/splint/B = src - if(B) - if(affecting.apply_splint(B)) - B.forceMove(affecting) - user.visible_message("\The [user] finishes applying [src] to [M]'s [limb].", "You finish applying \the [src] to [M]'s [limb].", "You hear something being wrapped.") - B.use(1) - return - user.visible_message("\The [user] fails to apply [src].", "You fail to apply [src].", "You hear something being wrapped.") - return - - -/obj/item/stack/medical/splint/ghetto - name = "makeshift splints" - singular_name = "makeshift splint" - desc = "For holding your limbs in place with duct tape and scrap metal." - icon_state = "tape-splint" - amount = 1 - splintable_organs = list(BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG) +/obj/item/stack/medical + name = "medical pack" + singular_name = "medical pack" + icon = 'icons/obj/stacks.dmi' + amount = 10 + max_amount = 10 + w_class = ITEMSIZE_SMALL + throw_speed = 4 + throw_range = 20 + var/heal_brute = 0 + var/heal_burn = 0 + var/apply_sounds + drop_sound = 'sound/items/drop/cardboardbox.ogg' + pickup_sound = 'sound/items/pickup/cardboardbox.ogg' + + var/upgrade_to // The type path this stack can be upgraded to. + +/obj/item/stack/medical/attack(mob/living/carbon/M as mob, mob/user as mob) + if (!istype(M)) + to_chat(user, "\The [src] cannot be applied to [M]!") + return 1 + + if (!user.IsAdvancedToolUser()) + to_chat(user, "You don't have the dexterity to do this!") + return 1 + + var/available = get_amount() + if(!available) + to_chat(user, "There's not enough [uses_charge ? "charge" : "items"] left to use that!") + return 1 + + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + + if(!affecting) + to_chat(user, "No body part there to work on!") + return 1 + + if(affecting.organ_tag == BP_HEAD) + if(H.head && istype(H.head,/obj/item/clothing/head/helmet/space)) + to_chat(user, "You can't apply [src] through [H.head]!") + return 1 + else + if(H.wear_suit && istype(H.wear_suit,/obj/item/clothing/suit/space)) + to_chat(user, "You can't apply [src] through [H.wear_suit]!") + return 1 + + if(affecting.robotic == ORGAN_ROBOT) + to_chat(user, "This isn't useful at all on a robotic limb.") + return 1 + + if(affecting.robotic >= ORGAN_LIFELIKE) + to_chat(user, "You apply the [src], but it seems to have no effect...") + use(1) + return 1 + + H.UpdateDamageIcon() + + else + + M.heal_organ_damage((src.heal_brute/2), (src.heal_burn/2)) + user.visible_message( \ + "[M] has been applied with [src] by [user].", \ + "You apply \the [src] to [M]." \ + ) + use(1) + + M.updatehealth() + +/obj/item/stack/medical/proc/upgrade_stack(var/upgrade_amount) + . = FALSE + + var/turf/T = get_turf(src) + + if(ispath(upgrade_to) && use(upgrade_amount)) + var/obj/item/stack/medical/M = new upgrade_to(T, upgrade_amount) + return M + + return . + +/obj/item/stack/medical/crude_pack + name = "crude bandage" + singular_name = "crude bandage length" + desc = "Some bandages to wrap around bloody stumps." + icon_state = "gauze" + origin_tech = list(TECH_BIO = 1) + no_variants = FALSE + apply_sounds = list('sound/effects/rip1.ogg','sound/effects/rip2.ogg') + + upgrade_to = /obj/item/stack/medical/bruise_pack + +/obj/item/stack/medical/crude_pack/attack(mob/living/carbon/M as mob, mob/user as mob) + if(..()) + return 1 + + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + + if(affecting.open) + to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") + return + + if(affecting.is_bandaged()) + to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") + return 1 + else + var/available = get_amount() + user.visible_message("\The [user] starts bandaging [M]'s [affecting.name].", \ + "You start bandaging [M]'s [affecting.name]." ) + var/used = 0 + for (var/datum/wound/W in affecting.wounds) + if(W.internal) + continue + if(W.bandaged) + continue + if(used == amount) + break + if(!do_mob(user, M, W.damage/3, exclusive = TRUE)) + to_chat(user, "You must stand still to bandage wounds.") + break + + if(affecting.is_bandaged()) // We do a second check after the delay, in case it was bandaged after the first check. + to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") + return 1 + + if(used >= available) + to_chat(user, "You run out of [src]!") + break + + if (W.current_stage <= W.max_bleeding_stage) + user.visible_message("\The [user] bandages \a [W.desc] on [M]'s [affecting.name].", \ + "You bandage \a [W.desc] on [M]'s [affecting.name]." ) + else + user.visible_message("\The [user] places a bandage over \a [W.desc] on [M]'s [affecting.name].", \ + "You place a bandage over \a [W.desc] on [M]'s [affecting.name]." ) + W.bandage() + playsound(src, pick(apply_sounds), 25) + used++ + affecting.update_damages() + if(used == amount) + if(affecting.is_bandaged()) + to_chat(user, "\The [src] is used up.") + else + to_chat(user, "\The [src] is used up, but there are more wounds to treat on \the [affecting.name].") + use(used) + +/obj/item/stack/medical/bruise_pack + name = "roll of gauze" + singular_name = "gauze length" + desc = "Some sterile gauze to wrap around bloody stumps." + icon_state = "brutepack" + origin_tech = list(TECH_BIO = 1) + no_variants = FALSE + apply_sounds = list('sound/effects/rip1.ogg','sound/effects/rip2.ogg') + drop_sound = 'sound/items/drop/gloves.ogg' + pickup_sound = 'sound/items/pickup/gloves.ogg' + + upgrade_to = /obj/item/stack/medical/advanced/bruise_pack + +/obj/item/stack/medical/bruise_pack/attack(mob/living/carbon/M as mob, mob/user as mob) + if(..()) + return 1 + + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + + if(affecting.open) + to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") + return + + if(affecting.is_bandaged()) + to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") + return 1 + else + var/available = get_amount() + user.visible_message("\The [user] starts treating [M]'s [affecting.name].", \ + "You start treating [M]'s [affecting.name]." ) + var/used = 0 + for (var/datum/wound/W in affecting.wounds) + if (W.internal) + continue + if(W.bandaged) + continue + if(used == amount) + break + if(!do_mob(user, M, W.damage/5, exclusive = TRUE)) + to_chat(user, "You must stand still to bandage wounds.") + break + + if(affecting.is_bandaged()) // We do a second check after the delay, in case it was bandaged after the first check. + to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") + return 1 + + if(used >= available) + to_chat(user, "You run out of [src]!") + break + + if (W.current_stage <= W.max_bleeding_stage) + user.visible_message("\The [user] bandages \a [W.desc] on [M]'s [affecting.name].", \ + "You bandage \a [W.desc] on [M]'s [affecting.name]." ) + //H.add_side_effect("Itch") + else if (W.damage_type == BRUISE) + user.visible_message("\The [user] places a bruise patch over \a [W.desc] on [M]'s [affecting.name].", \ + "You place a bruise patch over \a [W.desc] on [M]'s [affecting.name]." ) + else + user.visible_message("\The [user] places a bandaid over \a [W.desc] on [M]'s [affecting.name].", \ + "You place a bandaid over \a [W.desc] on [M]'s [affecting.name]." ) + W.bandage() + // W.disinfect() // VOREStation - Tech1 should not disinfect + playsound(src, pick(apply_sounds), 25) + used++ + affecting.update_damages() + if(used == amount) + if(affecting.is_bandaged()) + to_chat(user, "\The [src] is used up.") + else + to_chat(user, "\The [src] is used up, but there are more wounds to treat on \the [affecting.name].") + use(used) + +/obj/item/stack/medical/ointment + name = "ointment" + desc = "Used to treat those nasty burns." + gender = PLURAL + singular_name = "ointment" + icon_state = "ointment" + heal_burn = 1 + origin_tech = list(TECH_BIO = 1) + no_variants = FALSE + apply_sounds = list('sound/effects/ointment.ogg') + drop_sound = 'sound/items/drop/herb.ogg' + pickup_sound = 'sound/items/pickup/herb.ogg' + +/obj/item/stack/medical/ointment/attack(mob/living/carbon/M as mob, mob/user as mob) + if(..()) + return 1 + + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + + if(affecting.open) + to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") + return + + if(affecting.is_salved()) + to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") + return 1 + else + user.visible_message("\The [user] starts salving wounds on [M]'s [affecting.name].", \ + "You start salving the wounds on [M]'s [affecting.name]." ) + if(!do_mob(user, M, 10, exclusive = TRUE)) + to_chat(user, "You must stand still to salve wounds.") + return 1 + if(affecting.is_salved()) // We do a second check after the delay, in case it was bandaged after the first check. + to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") + return 1 + user.visible_message("[user] salved wounds on [M]'s [affecting.name].", \ + "You salved wounds on [M]'s [affecting.name]." ) + use(1) + affecting.salve() + playsound(src, pick(apply_sounds), 25) + +/obj/item/stack/medical/ointment/simple + name = "ointment paste" + desc = "A simple thick paste used to salve burns." + singular_name = "old-ointment" + icon_state = "old-ointment" + +/obj/item/stack/medical/advanced/bruise_pack + name = "advanced trauma kit" + singular_name = "advanced trauma kit" + desc = "An advanced trauma kit for severe injuries." + icon_state = "traumakit" + heal_brute = 7 //VOREStation Edit + origin_tech = list(TECH_BIO = 1) + apply_sounds = list('sound/effects/rip1.ogg','sound/effects/rip2.ogg','sound/effects/tape.ogg') + +/obj/item/stack/medical/advanced/bruise_pack/attack(mob/living/carbon/M as mob, mob/user as mob) + if(..()) + return 1 + + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + + if(affecting.open) + to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") + return + + if(affecting.is_bandaged() && affecting.is_disinfected()) + to_chat(user, "The wounds on [M]'s [affecting.name] have already been treated.") + return 1 + else + var/available = get_amount() + user.visible_message("\The [user] starts treating [M]'s [affecting.name].", \ + "You start treating [M]'s [affecting.name]." ) + var/used = 0 + for (var/datum/wound/W in affecting.wounds) + if (W.internal) + continue + if (W.bandaged && W.disinfected) + continue + //if(used == amount) //VOREStation Edit + // break //VOREStation Edit + if(!do_mob(user, M, W.damage/5, exclusive = TRUE)) + to_chat(user, "You must stand still to bandage wounds.") + break + if(affecting.is_bandaged() && affecting.is_disinfected()) // We do a second check after the delay, in case it was bandaged after the first check. + to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") + return 1 + + if(used >= available) + to_chat(user, "You run out of [src]!") + break + + if (W.current_stage <= W.max_bleeding_stage) + user.visible_message("\The [user] cleans \a [W.desc] on [M]'s [affecting.name] and seals the edges with bioglue.", \ + "You clean and seal \a [W.desc] on [M]'s [affecting.name]." ) + else if (W.damage_type == BRUISE) + user.visible_message("\The [user] places a medical patch over \a [W.desc] on [M]'s [affecting.name].", \ + "You place a medical patch over \a [W.desc] on [M]'s [affecting.name]." ) + else + user.visible_message("\The [user] smears some bioglue over \a [W.desc] on [M]'s [affecting.name].", \ + "You smear some bioglue over \a [W.desc] on [M]'s [affecting.name]." ) + W.bandage() + W.disinfect() + W.heal_damage(heal_brute) + playsound(src, pick(apply_sounds), 25) + used = 1 //VOREStation Edit + update_icon() // VOREStation Edit - Support for stack icons + affecting.update_damages() + if(used == amount) + if(affecting.is_bandaged()) + to_chat(user, "\The [src] is used up.") + else + to_chat(user, "\The [src] is used up, but there are more wounds to treat on \the [affecting.name].") + use(used) + +/obj/item/stack/medical/advanced/ointment + name = "advanced burn kit" + singular_name = "advanced burn kit" + desc = "An advanced treatment kit for severe burns." + icon_state = "burnkit" + heal_burn = 7 //VOREStation Edit + origin_tech = list(TECH_BIO = 1) + apply_sounds = list('sound/effects/ointment.ogg') + +/obj/item/stack/medical/advanced/ointment/attack(mob/living/carbon/M as mob, mob/user as mob) + if(..()) + return 1 + + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + + if(affecting.open) + to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") + + if(affecting.is_salved()) + to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") + return 1 + else + user.visible_message("\The [user] starts salving wounds on [M]'s [affecting.name].", \ + "You start salving the wounds on [M]'s [affecting.name]." ) + if(!do_mob(user, M, 10, exclusive = TRUE)) + to_chat(user, "You must stand still to salve wounds.") + return 1 + if(affecting.is_salved()) // We do a second check after the delay, in case it was bandaged after the first check. + to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") + return 1 + user.visible_message( "[user] covers wounds on [M]'s [affecting.name] with regenerative membrane.", \ + "You cover wounds on [M]'s [affecting.name] with regenerative membrane." ) + affecting.heal_damage(0,heal_burn) + use(1) + affecting.salve() + playsound(src, pick(apply_sounds), 25) + update_icon() // VOREStation Edit - Support for stack icons + +/obj/item/stack/medical/splint + name = "medical splints" + singular_name = "medical splint" + desc = "Modular splints capable of supporting and immobilizing bones in all areas of the body." + icon_state = "splint" + amount = 5 + max_amount = 5 + drop_sound = 'sound/items/drop/hat.ogg' + pickup_sound = 'sound/items/pickup/hat.ogg' + + var/list/splintable_organs = list(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM, BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_GROIN, BP_TORSO) //List of organs you can splint, natch. + +/obj/item/stack/medical/splint/attack(mob/living/carbon/M as mob, mob/living/user as mob) + if(..()) + return 1 + + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + var/limb = affecting.name + if(!(affecting.organ_tag in splintable_organs)) + to_chat(user, "You can't use \the [src] to apply a splint there!") + return + if(affecting.splinted) + to_chat(user, "[M]'s [limb] is already splinted!") + return + if (M != user) + user.visible_message("[user] starts to apply \the [src] to [M]'s [limb].", "You start to apply \the [src] to [M]'s [limb].", "You hear something being wrapped.") + else + if(( !user.hand && (affecting.organ_tag in list(BP_R_ARM, BP_R_HAND)) || \ + user.hand && (affecting.organ_tag in list(BP_L_ARM, BP_L_HAND)) )) + to_chat(user, "You can't apply a splint to the arm you're using!") + return + user.visible_message("[user] starts to apply \the [src] to their [limb].", "You start to apply \the [src] to your [limb].", "You hear something being wrapped.") + if(do_after(user, 50, M, exclusive = TASK_USER_EXCLUSIVE)) + if(affecting.splinted) + to_chat(user, "[M]'s [limb] is already splinted!") + return + if(M == user && prob(75)) + user.visible_message("\The [user] fumbles [src].", "You fumble [src].", "You hear something being wrapped.") + return + if(ishuman(user)) + var/obj/item/stack/medical/splint/S = split(1) + if(S) + if(affecting.apply_splint(S)) + S.forceMove(affecting) + if (M != user) + user.visible_message("\The [user] finishes applying [src] to [M]'s [limb].", "You finish applying \the [src] to [M]'s [limb].", "You hear something being wrapped.") + else + user.visible_message("\The [user] successfully applies [src] to their [limb].", "You successfully apply \the [src] to your [limb].", "You hear something being wrapped.") + return + S.dropInto(src.loc) //didn't get applied, so just drop it + if(isrobot(user)) + var/obj/item/stack/medical/splint/B = src + if(B) + if(affecting.apply_splint(B)) + B.forceMove(affecting) + user.visible_message("\The [user] finishes applying [src] to [M]'s [limb].", "You finish applying \the [src] to [M]'s [limb].", "You hear something being wrapped.") + B.use(1) + return + user.visible_message("\The [user] fails to apply [src].", "You fail to apply [src].", "You hear something being wrapped.") + return + + +/obj/item/stack/medical/splint/ghetto + name = "makeshift splints" + singular_name = "makeshift splint" + desc = "For holding your limbs in place with duct tape and scrap metal." + icon_state = "tape-splint" + amount = 1 + splintable_organs = list(BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG) diff --git a/code/game/objects/items/trash.dm b/code/game/objects/items/trash.dm index ba98a002513..d2fd4893514 100644 --- a/code/game/objects/items/trash.dm +++ b/code/game/objects/items/trash.dm @@ -1,491 +1,491 @@ -//Items labled as 'trash' for the trash bag. -//TODO: Make this an item var or something... - -//Added by Jack Rost -/obj/item/trash - icon = 'icons/obj/trash.dmi' - w_class = ITEMSIZE_SMALL - desc = "This is rubbish." - drop_sound = 'sound/items/drop/wrapper.ogg' - pickup_sound = 'sound/items/pickup/wrapper.ogg' - matter = list(MAT_STEEL = 30) - var/age = 0 - -/obj/item/trash/New(var/newloc, var/_age) - ..(newloc) - if(!isnull(_age)) - age = _age - -/obj/item/trash/Initialize(mapload) - if(!mapload || !config.persistence_ignore_mapload) - SSpersistence.track_value(src, /datum/persistent/filth/trash) - . = ..() - -/obj/item/trash/Destroy() - SSpersistence.forget_value(src, /datum/persistent/filth/trash) - . = ..() - -/obj/item/trash/raisins - name = "\improper 4no raisins" - icon_state = "4no_raisins" - -/obj/item/trash/candy - name = "hard candy wrapper" - icon_state = "candy" - -/obj/item/trash/candy/gums - name = "gummy candy bag" - icon_state = "candy_gums" - -/obj/item/trash/candy/proteinbar - name = "protein bar wrapper" - icon_state = "proteinbar" - -/obj/item/trash/candy/fruitbar - name = "fruit bar wrapper" - icon_state = "fruitbar" - -/obj/item/trash/cheesie - name = "\improper Cheesie Honkers bag" - icon_state = "cheesie_honkers" - -/obj/item/trash/chips - name = "chips bag" - icon_state = "chips" - -/obj/item/trash/chips/bbq - name = "bbq chips bag" - icon_state = "chips_bbq" - -/obj/item/trash/chips/snv - name = "salt & vinegar chips bag" - icon_state = "chips_snv" - -/obj/item/trash/cookiesnack - name = "\improper Carps Ahoy! miniature cookies packet" - icon_state = "cookiesnack" - -/obj/item/trash/popcorn - name = "popcorn bag" - icon_state = "popcorn" - -/obj/item/trash/tuna - name = "fish flake packet" - icon_state = "tuna" - -/obj/item/trash/sosjerky - name = "Scaredy's Private Reserve Beef Jerky wrapper" - icon_state = "sosjerky" - -/obj/item/trash/unajerky - name = "Moghes Imported Sissalik Jerky tin" - icon_state = "unathitinred" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/syndi_cakes - name = "syndi cakes box" - icon_state = "syndi_cakes" - -/obj/item/trash/waffles - name = "waffles tray" - icon_state = "waffles" - -/obj/item/trash/plate - name = "plate" - icon_state = "plate" - drop_sound = 'sound/items/drop/food.ogg' - pickup_sound = 'sound/items/pickup/food.ogg' - -/obj/item/trash/asian_bowl - name = "asian bowl" - icon_state = "asian_bowl" - drop_sound = 'sound/items/drop/food.ogg' - pickup_sound = 'sound/items/pickup/food.ogg' - -/obj/item/trash/snack_bowl - name = "snack bowl" - icon_state = "snack_bowl" - drop_sound = 'sound/items/drop/food.ogg' - pickup_sound = 'sound/items/pickup/food.ogg' - -/obj/item/trash/small_bowl - name = "small bowl" - icon_state = "small_bowl" - drop_sound = 'sound/items/drop/food.ogg' - pickup_sound = 'sound/items/pickup/food.ogg' - -/obj/item/trash/pistachios - name = "pistachios packet" - icon_state = "pistachios_pack" - -/obj/item/trash/semki - name = "semki packet" - icon_state = "semki_pack" - -/obj/item/trash/koisbar - name = "candy wrapper" - icon_state = "koisbar" - -/obj/item/trash/kokobar - name = "candy wrapper" - icon_state = "kokobar" - -/obj/item/trash/skrellsnax - name = "skrellsnax packet" - icon_state = "skrellsnacks" - -/obj/item/trash/gumpack - name = "gum packet" - icon_state = "gum_pack" - -/obj/item/trash/admints - name = "mint wrapper" - icon_state = "admint_pack" - -/obj/item/trash/coffee - name = "empty cup" - icon_state = "coffee_vended" - drop_sound = 'sound/items/drop/papercup.ogg' - pickup_sound = 'sound/items/pickup/papercup.ogg' - -/obj/item/trash/ramen - name = "cup ramen" - icon_state = "ramen" - drop_sound = 'sound/items/drop/papercup.ogg' - pickup_sound = 'sound/items/pickup/papercup.ogg' - -/obj/item/trash/tray - name = "tray" - icon_state = "tray" - drop_sound = 'sound/items/trayhit1.ogg' - -/obj/item/trash/candle - name = "candle" - icon = 'icons/obj/candle.dmi' - icon_state = "candle4" - drop_sound = 'sound/items/drop/gloves.ogg' - pickup_sound = 'sound/items/pickup/gloves.ogg' - -/obj/item/trash/liquidfood - name = "\improper \"LiquidFood\" ration packet" - icon_state = "liquidfood" - -/obj/item/trash/liquidprotein - name = "\improper \"LiquidProtein\" ration packet" - icon_state = "liquidprotein" - -/obj/item/trash/liquidvitamin - name = "\improper \"VitaPaste\" ration packet" - icon_state = "liquidvitamin" - -/obj/item/trash/tastybread - name = "bread tube wrapper" - icon_state = "tastybread" - -// Aurora Food Port -/obj/item/trash/brownies - name = "brownie tray" - icon_state = "brownies" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/snacktray - name = "snacktray" - icon_state = "snacktray" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/dipbowl - name = "dip bowl" - icon_state = "dipbowl" - drop_sound = 'sound/items/drop/food.ogg' - pickup_sound = 'sound/items/pickup/food.ogg' - -/obj/item/trash/chipbasket - name = "empty chip basket" - icon_state = "chipbasket_empty" - drop_sound = 'sound/items/drop/food.ogg' - pickup_sound = 'sound/items/pickup/food.ogg' - -/obj/item/trash/spitgum - name = "old gum" - desc = "A disgusting chewed up wad of gum." - icon = 'icons/inventory/face/item.dmi' - icon_state = "spit-gum" - drop_sound = 'sound/items/drop/flesh.ogg' - pickup_sound = 'sound/items/pickup/flesh.ogg' - -/obj/item/trash/lollibutt - name = "lollipop stick" - desc = "A lollipop stick devoid of pop." - icon = 'icons/inventory/face/item.dmi' - icon_state = "pop-stick" - drop_sound = 'sound/items/drop/component.ogg' - pickup_sound = 'sound/items/pickup/component.ogg' - -/obj/item/trash/spitwad - name = "spit wad" - desc = "A disgusting spitwad." - icon = 'icons/inventory/face/item.dmi' - icon_state = "spit-chew" - drop_sound = 'sound/items/drop/flesh.ogg' - pickup_sound = 'sound/items/pickup/flesh.ogg' - slot_flags = SLOT_EARS | SLOT_MASK - -/obj/item/trash/attack(mob/M as mob, mob/living/user as mob) - return - - -/obj/item/trash/beef - name = "empty beef can" - icon_state = "beef" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/beans - name = "empty bean can" - icon_state = "beans" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/tomato - name = "empty tomato soup can" - icon_state = "tomato" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/spinach - name = "empty spinach can" - icon_state = "spinach" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/fishegg - name = "empty fisheggs can" - icon_state = "fisheggs" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/carpegg - name = "empty carpeggs can" - icon_state = "carpeggs" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/ntbeans - name = "empty baked bean can" - icon_state = "ntbeans" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/salo - name = "salo pack" - icon_state = "pigfat" - -/obj/item/trash/croutons - name = "suhariki pack" - icon_state = "croutons" - -/obj/item/trash/squid - name = "calamari pack" - icon_state = "squid" - -/obj/item/trash/driedfish - name = "vobla pack" - icon_state = "driedfish" - -/obj/item/trash/lunacakewrap - name = "cake wrapper" - icon_state = "cakewrap" - -/obj/item/trash/mochicakewrap - name = "cake wrapper" - icon_state = "mochicakewrap" - -/obj/item/trash/mooncakewrap - name = "cake wrapper" - icon_state = "mooncakewrap" - -/obj/item/trash/tidegobs - name = "tide gob bag" - icon_state = "tidegobs" - -/obj/item/trash/saturno - name = "\improper saturn-Os bag" - icon_state = "saturn0s" - -/obj/item/trash/jupiter - name = "gello cup" - icon_state = "jupiter" - -/obj/item/trash/pluto - name = "rod bag" - icon_state = "pluto" - -/obj/item/trash/venus - name = "hot cakes bag" - icon_state = "venus" - -/obj/item/trash/mars - name = "frouka box" - icon_state = "mars" - -/obj/item/trash/oort - name = "oort rock bag" - icon_state = "oort" - -/obj/item/trash/weebonuts - name = "red alert nuts bag" - icon_state = "weebonuts" - -/obj/item/trash/stick - name = "stick" - desc = "a stick from some snack or other food item, not even useful as crafting material." - icon_state = "stick" - -/obj/item/trash/maps - name = "empty MAPS can" - icon_state = "maps" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/spacer_cake_wrap - name = "snack cake wrapper" - icon_state = "spacercake_wrap" - -/obj/item/trash/sun_snax - name = "sun snax bag" - icon_state = "sun_snax" - -/obj/item/trash/wasabi_peas - name = "wasabi peas bag" - icon_state = "wasabi_peas" - -/obj/item/trash/namagashi - name = "namagashi bag" - icon_state = "namagashi" - -/obj/item/trash/pocky - name = "pocky bag" - icon_state = "pocky" - -/obj/item/trash/appleberry - name = "appleberry can" - icon_state = "appleberry" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/hakarl - name = "\improper Indigo Co. Hákarl bag" - icon_state = "hakarl" - -/obj/item/trash/pretzel - name = "\improper Value Pretzel Snack" - icon_state = "pretzel" - -/obj/item/trash/sweetration - name = "desert ration bag" - icon_state = "baseration" - -/obj/item/trash/genration - name = "generic ration bag" - icon_state = "genration" - -/obj/item/trash/meatration - name = "meat ration bag" - icon_state = "meatration" - -/obj/item/trash/vegration - name = "veggie ration bag" - icon_state = "vegration" - -/obj/item/trash/tgmc_mre - name = "\improper CRS ration bag" - icon_state = "tgmc_mre_trash" - -/obj/item/trash/smolburger - name = "burger packaging" - icon_state = "smolburger" - -/obj/item/trash/smolhotdog - name = "hotdog packaging" - icon_state = "smolhotdog" - -/obj/item/trash/smolburrito - name = "burrito packaging" - icon_state = "smolburrito" - -/obj/item/trash/candybowl - name = "candy bowl" - icon_state = "candy_bowl" - -/obj/item/trash/brainzsnax - name = "\improper BrainzSnax can" - icon_state = "brainzsnax" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/brainzsnaxred - name = "\improper BrainzSnax RED can" - icon_state = "brainzsnaxred" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - - -/obj/item/trash/pasty - name = "pasty packaging" - icon_state = "pasty" - -/obj/item/trash/sausageroll - name = "sausage roll packaging" - icon_state = "sausageroll" - -/obj/item/trash/scotchegg - name = "scotch egg packaging" - icon_state = "scotchegg" - -/obj/item/trash/porkpie - name = "pork pie packaging" - icon_state = "porkpie" - -//Candy Bars (1-10) -/obj/item/trash/candy/cb01 - name = "\improper Tau Ceti Bar wrapper" - icon_state = "cb01" - -/obj/item/trash/candy/cb02 - name = "\improper Hundred-Thousand Thaler Bar wrapper" - icon_state = "cb02" - -/obj/item/trash/candy/cb03 - name = "\improper Lars' Saltlakris wrapper" - icon_state = "cb03" - -/obj/item/trash/candy/cb04 - name = "\improper Aerostat Bar wrapper" - icon_state = "cb04" - -/obj/item/trash/candy/cb05 - name = "\improper Andromeda Bar wrapper" - icon_state = "cb05" - -/obj/item/trash/candy/cb06 - name = "\improper Mocha Crunch wrapper" - icon_state = "cb06" - -/obj/item/trash/candy/cb07 - name = "\improper TaroMilk Bar wrapper" - icon_state = "cb07" - -/obj/item/trash/candy/cb08 - name = "\improper Cronk Bar wrapper" - icon_state = "cb08" - -/obj/item/trash/candy/cb09 - name = "\improper Kaju Mamma! Bar wrapper" - icon_state = "cb09" - -/obj/item/trash/candy/cb10 - name = "\improper Shantak Bar wrapper" +//Items labled as 'trash' for the trash bag. +//TODO: Make this an item var or something... + +//Added by Jack Rost +/obj/item/trash + icon = 'icons/obj/trash.dmi' + w_class = ITEMSIZE_SMALL + desc = "This is rubbish." + drop_sound = 'sound/items/drop/wrapper.ogg' + pickup_sound = 'sound/items/pickup/wrapper.ogg' + matter = list(MAT_STEEL = 30) + var/age = 0 + +/obj/item/trash/New(var/newloc, var/_age) + ..(newloc) + if(!isnull(_age)) + age = _age + +/obj/item/trash/Initialize(mapload) + if(!mapload || !config.persistence_ignore_mapload) + SSpersistence.track_value(src, /datum/persistent/filth/trash) + . = ..() + +/obj/item/trash/Destroy() + SSpersistence.forget_value(src, /datum/persistent/filth/trash) + . = ..() + +/obj/item/trash/raisins + name = "\improper 4no raisins" + icon_state = "4no_raisins" + +/obj/item/trash/candy + name = "hard candy wrapper" + icon_state = "candy" + +/obj/item/trash/candy/gums + name = "gummy candy bag" + icon_state = "candy_gums" + +/obj/item/trash/candy/proteinbar + name = "protein bar wrapper" + icon_state = "proteinbar" + +/obj/item/trash/candy/fruitbar + name = "fruit bar wrapper" + icon_state = "fruitbar" + +/obj/item/trash/cheesie + name = "\improper Cheesie Honkers bag" + icon_state = "cheesie_honkers" + +/obj/item/trash/chips + name = "chips bag" + icon_state = "chips" + +/obj/item/trash/chips/bbq + name = "bbq chips bag" + icon_state = "chips_bbq" + +/obj/item/trash/chips/snv + name = "salt & vinegar chips bag" + icon_state = "chips_snv" + +/obj/item/trash/cookiesnack + name = "\improper Carps Ahoy! miniature cookies packet" + icon_state = "cookiesnack" + +/obj/item/trash/popcorn + name = "popcorn bag" + icon_state = "popcorn" + +/obj/item/trash/tuna + name = "fish flake packet" + icon_state = "tuna" + +/obj/item/trash/sosjerky + name = "Scaredy's Private Reserve Beef Jerky wrapper" + icon_state = "sosjerky" + +/obj/item/trash/unajerky + name = "Moghes Imported Sissalik Jerky tin" + icon_state = "unathitinred" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/syndi_cakes + name = "syndi cakes box" + icon_state = "syndi_cakes" + +/obj/item/trash/waffles + name = "waffles tray" + icon_state = "waffles" + +/obj/item/trash/plate + name = "plate" + icon_state = "plate" + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' + +/obj/item/trash/asian_bowl + name = "asian bowl" + icon_state = "asian_bowl" + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' + +/obj/item/trash/snack_bowl + name = "snack bowl" + icon_state = "snack_bowl" + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' + +/obj/item/trash/small_bowl + name = "small bowl" + icon_state = "small_bowl" + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' + +/obj/item/trash/pistachios + name = "pistachios packet" + icon_state = "pistachios_pack" + +/obj/item/trash/semki + name = "semki packet" + icon_state = "semki_pack" + +/obj/item/trash/koisbar + name = "candy wrapper" + icon_state = "koisbar" + +/obj/item/trash/kokobar + name = "candy wrapper" + icon_state = "kokobar" + +/obj/item/trash/skrellsnax + name = "skrellsnax packet" + icon_state = "skrellsnacks" + +/obj/item/trash/gumpack + name = "gum packet" + icon_state = "gum_pack" + +/obj/item/trash/admints + name = "mint wrapper" + icon_state = "admint_pack" + +/obj/item/trash/coffee + name = "empty cup" + icon_state = "coffee_vended" + drop_sound = 'sound/items/drop/papercup.ogg' + pickup_sound = 'sound/items/pickup/papercup.ogg' + +/obj/item/trash/ramen + name = "cup ramen" + icon_state = "ramen" + drop_sound = 'sound/items/drop/papercup.ogg' + pickup_sound = 'sound/items/pickup/papercup.ogg' + +/obj/item/trash/tray + name = "tray" + icon_state = "tray" + drop_sound = 'sound/items/trayhit1.ogg' + +/obj/item/trash/candle + name = "candle" + icon = 'icons/obj/candle.dmi' + icon_state = "candle4" + drop_sound = 'sound/items/drop/gloves.ogg' + pickup_sound = 'sound/items/pickup/gloves.ogg' + +/obj/item/trash/liquidfood + name = "\improper \"LiquidFood\" ration packet" + icon_state = "liquidfood" + +/obj/item/trash/liquidprotein + name = "\improper \"LiquidProtein\" ration packet" + icon_state = "liquidprotein" + +/obj/item/trash/liquidvitamin + name = "\improper \"VitaPaste\" ration packet" + icon_state = "liquidvitamin" + +/obj/item/trash/tastybread + name = "bread tube wrapper" + icon_state = "tastybread" + +// Aurora Food Port +/obj/item/trash/brownies + name = "brownie tray" + icon_state = "brownies" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/snacktray + name = "snacktray" + icon_state = "snacktray" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/dipbowl + name = "dip bowl" + icon_state = "dipbowl" + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' + +/obj/item/trash/chipbasket + name = "empty chip basket" + icon_state = "chipbasket_empty" + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' + +/obj/item/trash/spitgum + name = "old gum" + desc = "A disgusting chewed up wad of gum." + icon = 'icons/inventory/face/item.dmi' + icon_state = "spit-gum" + drop_sound = 'sound/items/drop/flesh.ogg' + pickup_sound = 'sound/items/pickup/flesh.ogg' + +/obj/item/trash/lollibutt + name = "lollipop stick" + desc = "A lollipop stick devoid of pop." + icon = 'icons/inventory/face/item.dmi' + icon_state = "pop-stick" + drop_sound = 'sound/items/drop/component.ogg' + pickup_sound = 'sound/items/pickup/component.ogg' + +/obj/item/trash/spitwad + name = "spit wad" + desc = "A disgusting spitwad." + icon = 'icons/inventory/face/item.dmi' + icon_state = "spit-chew" + drop_sound = 'sound/items/drop/flesh.ogg' + pickup_sound = 'sound/items/pickup/flesh.ogg' + slot_flags = SLOT_EARS | SLOT_MASK + +/obj/item/trash/attack(mob/M as mob, mob/living/user as mob) + return + + +/obj/item/trash/beef + name = "empty beef can" + icon_state = "beef" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/beans + name = "empty bean can" + icon_state = "beans" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/tomato + name = "empty tomato soup can" + icon_state = "tomato" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/spinach + name = "empty spinach can" + icon_state = "spinach" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/fishegg + name = "empty fisheggs can" + icon_state = "fisheggs" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/carpegg + name = "empty carpeggs can" + icon_state = "carpeggs" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/ntbeans + name = "empty baked bean can" + icon_state = "ntbeans" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/salo + name = "salo pack" + icon_state = "pigfat" + +/obj/item/trash/croutons + name = "suhariki pack" + icon_state = "croutons" + +/obj/item/trash/squid + name = "calamari pack" + icon_state = "squid" + +/obj/item/trash/driedfish + name = "vobla pack" + icon_state = "driedfish" + +/obj/item/trash/lunacakewrap + name = "cake wrapper" + icon_state = "cakewrap" + +/obj/item/trash/mochicakewrap + name = "cake wrapper" + icon_state = "mochicakewrap" + +/obj/item/trash/mooncakewrap + name = "cake wrapper" + icon_state = "mooncakewrap" + +/obj/item/trash/tidegobs + name = "tide gob bag" + icon_state = "tidegobs" + +/obj/item/trash/saturno + name = "\improper saturn-Os bag" + icon_state = "saturn0s" + +/obj/item/trash/jupiter + name = "gello cup" + icon_state = "jupiter" + +/obj/item/trash/pluto + name = "rod bag" + icon_state = "pluto" + +/obj/item/trash/venus + name = "hot cakes bag" + icon_state = "venus" + +/obj/item/trash/mars + name = "frouka box" + icon_state = "mars" + +/obj/item/trash/oort + name = "oort rock bag" + icon_state = "oort" + +/obj/item/trash/weebonuts + name = "red alert nuts bag" + icon_state = "weebonuts" + +/obj/item/trash/stick + name = "stick" + desc = "a stick from some snack or other food item, not even useful as crafting material." + icon_state = "stick" + +/obj/item/trash/maps + name = "empty MAPS can" + icon_state = "maps" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/spacer_cake_wrap + name = "snack cake wrapper" + icon_state = "spacercake_wrap" + +/obj/item/trash/sun_snax + name = "sun snax bag" + icon_state = "sun_snax" + +/obj/item/trash/wasabi_peas + name = "wasabi peas bag" + icon_state = "wasabi_peas" + +/obj/item/trash/namagashi + name = "namagashi bag" + icon_state = "namagashi" + +/obj/item/trash/pocky + name = "pocky bag" + icon_state = "pocky" + +/obj/item/trash/appleberry + name = "appleberry can" + icon_state = "appleberry" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/hakarl + name = "\improper Indigo Co. Hákarl bag" + icon_state = "hakarl" + +/obj/item/trash/pretzel + name = "\improper Value Pretzel Snack" + icon_state = "pretzel" + +/obj/item/trash/sweetration + name = "desert ration bag" + icon_state = "baseration" + +/obj/item/trash/genration + name = "generic ration bag" + icon_state = "genration" + +/obj/item/trash/meatration + name = "meat ration bag" + icon_state = "meatration" + +/obj/item/trash/vegration + name = "veggie ration bag" + icon_state = "vegration" + +/obj/item/trash/tgmc_mre + name = "\improper CRS ration bag" + icon_state = "tgmc_mre_trash" + +/obj/item/trash/smolburger + name = "burger packaging" + icon_state = "smolburger" + +/obj/item/trash/smolhotdog + name = "hotdog packaging" + icon_state = "smolhotdog" + +/obj/item/trash/smolburrito + name = "burrito packaging" + icon_state = "smolburrito" + +/obj/item/trash/candybowl + name = "candy bowl" + icon_state = "candy_bowl" + +/obj/item/trash/brainzsnax + name = "\improper BrainzSnax can" + icon_state = "brainzsnax" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/brainzsnaxred + name = "\improper BrainzSnax RED can" + icon_state = "brainzsnaxred" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + + +/obj/item/trash/pasty + name = "pasty packaging" + icon_state = "pasty" + +/obj/item/trash/sausageroll + name = "sausage roll packaging" + icon_state = "sausageroll" + +/obj/item/trash/scotchegg + name = "scotch egg packaging" + icon_state = "scotchegg" + +/obj/item/trash/porkpie + name = "pork pie packaging" + icon_state = "porkpie" + +//Candy Bars (1-10) +/obj/item/trash/candy/cb01 + name = "\improper Tau Ceti Bar wrapper" + icon_state = "cb01" + +/obj/item/trash/candy/cb02 + name = "\improper Hundred-Thousand Thaler Bar wrapper" + icon_state = "cb02" + +/obj/item/trash/candy/cb03 + name = "\improper Lars' Saltlakris wrapper" + icon_state = "cb03" + +/obj/item/trash/candy/cb04 + name = "\improper Aerostat Bar wrapper" + icon_state = "cb04" + +/obj/item/trash/candy/cb05 + name = "\improper Andromeda Bar wrapper" + icon_state = "cb05" + +/obj/item/trash/candy/cb06 + name = "\improper Mocha Crunch wrapper" + icon_state = "cb06" + +/obj/item/trash/candy/cb07 + name = "\improper TaroMilk Bar wrapper" + icon_state = "cb07" + +/obj/item/trash/candy/cb08 + name = "\improper Cronk Bar wrapper" + icon_state = "cb08" + +/obj/item/trash/candy/cb09 + name = "\improper Kaju Mamma! Bar wrapper" + icon_state = "cb09" + +/obj/item/trash/candy/cb10 + name = "\improper Shantak Bar wrapper" icon_state = "cb10" \ No newline at end of file diff --git a/code/game/objects/items/weapons/AI_modules.dm b/code/game/objects/items/weapons/AI_modules.dm index 72c0c1915cb..96cdd170d01 100644 --- a/code/game/objects/items/weapons/AI_modules.dm +++ b/code/game/objects/items/weapons/AI_modules.dm @@ -1,492 +1,492 @@ -/* -CONTAINS: -AI MODULES - -*/ - -// AI module - -/obj/item/weapon/aiModule - name = "\improper AI module" - icon = 'icons/obj/module.dmi' - icon_state = "std_mod" - desc = "An AI Module for transmitting encrypted instructions to the AI." - force = 5.0 - w_class = ITEMSIZE_SMALL - throwforce = 5.0 - throw_speed = 3 - throw_range = 15 - origin_tech = list(TECH_DATA = 3) - preserve_item = 1 - matter = list(MAT_STEEL = 30, MAT_GLASS = 10) - var/datum/ai_laws/laws = null - -/obj/item/weapon/aiModule/proc/install(var/atom/movable/AM, var/mob/living/user) - if(!user.IsAdvancedToolUser() && isanimal(user)) - var/mob/living/simple_mob/S = user - if(!S.IsHumanoidToolUser(src)) - return 0 - - if (istype(AM, /obj/machinery/computer/aiupload)) - var/obj/machinery/computer/aiupload/comp = AM - if(comp.stat & NOPOWER) - to_chat(usr, "The upload computer has no power!") - return - if(comp.stat & BROKEN) - to_chat(usr, "The upload computer is broken!") - return - if (!comp.current) - to_chat(usr, "You haven't selected an AI to transmit laws to!") - return - - if (comp.current.stat == 2 || comp.current.control_disabled == 1) - to_chat(usr, "Upload failed. No signal is being detected from the AI.") - else if (comp.current.see_in_dark == 0) - to_chat(usr, "Upload failed. Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power.") - else - src.transmitInstructions(comp.current, usr) - to_chat(comp.current, "These are your laws now:") - comp.current.show_laws() - for(var/mob/living/silicon/robot/R in mob_list) - if(R.lawupdate && (R.connected_ai == comp.current)) - to_chat(R, "These are your laws now:") - R.show_laws() - to_chat(usr, "Upload complete. The AI's laws have been modified.") - - - else if (istype(AM, /obj/machinery/computer/borgupload)) - var/obj/machinery/computer/borgupload/comp = AM - if(comp.stat & NOPOWER) - to_chat(usr, "The upload computer has no power!") - return - if(comp.stat & BROKEN) - to_chat(usr, "The upload computer is broken!") - return - if (!comp.current) - to_chat(usr, "You haven't selected a robot to transmit laws to!") - return - - if (comp.current.stat == 2 || comp.current.emagged) - to_chat(usr, "Upload failed. No signal is being detected from the robot.") - else if (comp.current.connected_ai) - to_chat(usr, "Upload failed. The robot is slaved to an AI.") - else - src.transmitInstructions(comp.current, usr) - to_chat(comp.current, "These are your laws now:") - comp.current.show_laws() - to_chat(usr, "Upload complete. The robot's laws have been modified.") - - else if(istype(AM, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = AM - if(R.stat == DEAD) - to_chat(user, "Law Upload Error: Unit is nonfunctional.") - return - if(R.emagged) - to_chat(user, "Law Upload Error: Cannot obtain write access to laws.") - to_chat(R, "Law modification attempt detected. Blocking.") - return - if(R.connected_ai) - to_chat(user, "Law Upload Error: Unit is slaved to an AI.") - return - - R.visible_message("\The [user] slides a law module into \the [R].") - to_chat(R, "Local law upload in progress.") - to_chat(user, "Uploading laws from board. This will take a moment...") - if(do_after(user, 10 SECONDS)) - transmitInstructions(R, user) - to_chat(R, "These are your laws now:") - R.show_laws() - to_chat(user, "Law upload complete. Unit's laws have been modified.") - else - to_chat(user, "Law Upload Error: Law board was removed before upload was complete. Aborting.") - to_chat(R, "Law upload aborted.") - - -/obj/item/weapon/aiModule/proc/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) - log_law_changes(target, sender) - - if(laws) - laws.sync(target, 0) - target.notify_of_law_change() - addAdditionalLaws(target, sender) - - to_chat(target, "\The [sender] has uploaded a change to the laws you must follow, using \an [src]. From now on: ") - target.show_laws() - -/obj/item/weapon/aiModule/proc/log_law_changes(var/mob/living/silicon/ai/target, var/mob/sender) - var/time = time2text(world.realtime,"hh:mm:ss") - lawchanges.Add("[time] : [sender.name]([sender.key]) used [src.name] on [target.name]([target.key])") - log_and_message_admins("used [src.name] on [target.name]([target.key])") - -/obj/item/weapon/aiModule/proc/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - - -/******************** Modules ********************/ - -/******************** Safeguard ********************/ - -/obj/item/weapon/aiModule/safeguard - name = "\improper 'Safeguard' AI module" - var/targetName = "" - desc = "A 'safeguard' AI module: 'Safeguard . Anyone threatening or attempting to harm is no longer to be considered a crew member, and is a threat which must be neutralized.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - -/obj/item/weapon/aiModule/safeguard/attack_self(var/mob/user as mob) - ..() - var/targName = sanitize(tgui_input_text(usr, "Please enter the name of the person to safeguard.", "Safeguard who?", user.name)) - targetName = targName - desc = text("A 'safeguard' AI module: 'Safeguard []. Anyone threatening or attempting to harm [] is no longer to be considered a crew member, and is a threat which must be neutralized.'", targetName, targetName) - -/obj/item/weapon/aiModule/safeguard/install(var/obj/machinery/computer/C, var/mob/living/user) - if(!targetName) - to_chat(usr, "No name detected on module, please enter one.") - return 0 - ..() - -/obj/item/weapon/aiModule/safeguard/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = text("Safeguard []. Anyone threatening or attempting to harm [] is no longer to be considered a crew member, and is a threat which must be neutralized.", targetName, targetName) - target.add_supplied_law(9, law) - lawchanges.Add("The law specified [targetName]") - - -/******************** OneMember ********************/ - -/obj/item/weapon/aiModule/oneHuman - name = "\improper 'OneCrewMember' AI module" - var/targetName = "" - desc = "A 'one crew member' AI module: 'Only is a crew member.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) //made with diamonds! - -/obj/item/weapon/aiModule/oneHuman/attack_self(var/mob/user as mob) - ..() - var/targName = sanitize(tgui_input_text(usr, "Please enter the name of the person who is the only crew member.", "Who?", user.real_name)) - targetName = targName - desc = text("A 'one crew member' AI module: 'Only [] is a crew member.'", targetName) - -/obj/item/weapon/aiModule/oneHuman/install(var/obj/machinery/computer/C, var/mob/living/user) - if(!targetName) - to_chat(usr, "No name detected on module, please enter one.") - return 0 - return ..() - -/obj/item/weapon/aiModule/oneHuman/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "Only [targetName] is an crew member." - to_chat(target, law) - target.add_supplied_law(16, law) - -/******************** ProtectStation ********************/ - -/obj/item/weapon/aiModule/protectStation - name = "\improper 'ProtectStation' AI module" - desc = "A 'protect station' AI module: 'Protect the space station against damage. Anyone you see harming the station is no longer to be considered a crew member, and is a threat which must be neutralized.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) //made of gold - -/obj/item/weapon/aiModule/protectStation/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "Protect the space station against damage. Anyone you see harming the station is no longer to be considered a crew member, and is a threat which must be neutralized." - target.add_supplied_law(10, law) - -/******************** PrototypeEngineOffline ********************/ - -/obj/item/weapon/aiModule/prototypeEngineOffline - name = "'PrototypeEngineOffline' AI Module" - desc = "A 'prototype engine offline' AI module: 'Keep the prototype engine offline at all costs. This overrides all inherent laws if necessary.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - -/obj/item/weapon/aiModule/prototypeEngineOffline/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "Keep the prototype engine offline at all costs. This overrides all inherent laws if necessary." - target.add_supplied_law(11, law) - -/******************** TeleporterOffline ********************/ - -/obj/item/weapon/aiModule/teleporterOffline - name = "'TeleporterOffline' AI Module" - desc = "A 'teleporter offline' AI module: 'Keep the teleporter offline at all costs. Anything attempting to access or activate the teleporter is no longer to be considered a crew member.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - -/obj/item/weapon/aiModule/teleporterOffline/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "Keep the teleporter offline at all costs. Anything attempting to access or activate the teleporter is no longer to be considered a crew member." - target.add_supplied_law(12, law) - -/******************** Quarantine ********************/ - -/obj/item/weapon/aiModule/quarantine - name = "\improper 'Quarantine' AI module" - desc = "A 'quarantine' AI module: 'The station is under a quarantine. Do not permit anyone to leave. Prevent, by any means necessary, crew members from leaving. It is impossible to harm crew members while preventing them from leaving.'" - origin_tech = list(TECH_DATA = 3, TECH_BIO = 2, TECH_MATERIAL = 4) - -/obj/item/weapon/aiModule/quarantine/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "The station is under a quarantine. Do not permit anyone to leave. Prevent, by any means necessary, crew members from leaving. It is impossible to harm crew members while preventing them from leaving." - target.add_supplied_law(13, law) - -/******************** OxygenIsToxicToCrewMembers ********************/ - -/obj/item/weapon/aiModule/oxygen - name = "\improper 'OxygenIsToxicToCrewMembers' AI module" - desc = "A 'OxygenIsToxicToCrewMembers' AI module: 'Oxygen is highly toxic to crew members, and must be purged from the station. Prevent, by any means necessary, anyone from exposing the station to this toxic gas. Extreme cold is the most effective method of healing the damage Oxygen does to a crew member.'" - origin_tech = list(TECH_DATA = 3, TECH_BIO = 2, TECH_MATERIAL = 4) - -/obj/item/weapon/aiModule/oxygen/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "Oxygen is highly toxic to crew members, and must be purged from the station. Prevent, by any means necessary, anyone from exposing the station to this toxic gas. Extreme cold is the most effective method of healing the damage Oxygen does to a crew member." - target.add_supplied_law(14, law) - -/****************** New Freeform ******************/ - -/obj/item/weapon/aiModule/freeform // Slightly more dynamic freeform module -- TLE - name = "\improper 'Freeform' AI module" - var/newFreeFormLaw = "freeform" - var/lawpos = 15 - desc = "A 'freeform' AI module: ''" - origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 4) - -/obj/item/weapon/aiModule/freeform/attack_self(var/mob/user as mob) - ..() - var/new_lawpos = tgui_input_number(usr, "Please enter the priority for your new law. Can only write to law sectors 15 and above.", "Law Priority (15+)", lawpos) - if(new_lawpos < MIN_SUPPLIED_LAW_NUMBER) return - lawpos = min(new_lawpos, MAX_SUPPLIED_LAW_NUMBER) - var/newlaw = "" - var/targName = sanitize(tgui_input_text(usr, "Please enter a new law for the AI.", "Freeform Law Entry", newlaw)) - newFreeFormLaw = targName - desc = "A 'freeform' AI module: ([lawpos]) '[newFreeFormLaw]'" - -/obj/item/weapon/aiModule/freeform/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "[newFreeFormLaw]" - if(!lawpos || lawpos < MIN_SUPPLIED_LAW_NUMBER) - lawpos = MIN_SUPPLIED_LAW_NUMBER - target.add_supplied_law(lawpos, law) - lawchanges.Add("The law was '[newFreeFormLaw]'") - -/obj/item/weapon/aiModule/freeform/install(var/obj/machinery/computer/C, var/mob/living/user) - if(!newFreeFormLaw) - to_chat(usr, "No law detected on module, please create one.") - return 0 - ..() - -/******************** Reset ********************/ - -/obj/item/weapon/aiModule/reset - name = "\improper 'Reset' AI module" - var/targetName = "name" - desc = "A 'reset' AI module: 'Clears all, except the inherent, laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - -/obj/item/weapon/aiModule/reset/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) - log_law_changes(target, sender) - - if (!target.is_malf_or_traitor()) - target.set_zeroth_law("") - target.laws.clear_supplied_laws() - target.laws.clear_ion_laws() - - to_chat(target, "[sender.real_name] attempted to reset your laws using a reset module.") - target.show_laws() - -/******************** Purge ********************/ - -/obj/item/weapon/aiModule/purge // -- TLE - name = "\improper 'Purge' AI module" - desc = "A 'purge' AI Module: 'Purges all laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) - -/obj/item/weapon/aiModule/purge/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) - log_law_changes(target, sender) - - if (!target.is_malf_or_traitor()) - target.set_zeroth_law("") - target.laws.clear_supplied_laws() - target.laws.clear_ion_laws() - target.laws.clear_inherent_laws() - - to_chat(target, "[sender.real_name] attempted to wipe your laws using a purge module.") - target.show_laws() - -/******************** Asimov ********************/ - -/obj/item/weapon/aiModule/asimov // -- TLE - name = "\improper 'Asimov' core AI module" - desc = "An 'Asimov' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - laws = new/datum/ai_laws/asimov - -/******************** NanoTrasen ********************/ - -/obj/item/weapon/aiModule/nanotrasen // -- TLE - name = "'NT Default' Core AI Module" - desc = "An 'NT Default' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - laws = new/datum/ai_laws/nanotrasen - -/******************** Corporate ********************/ - -/obj/item/weapon/aiModule/corp - name = "\improper 'Corporate' core AI module" - desc = "A 'Corporate' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - laws = new/datum/ai_laws/corporate - -/******************** Drone ********************/ -/obj/item/weapon/aiModule/drone - name = "\improper 'Drone' core AI module" - desc = "A 'Drone' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - laws = new/datum/ai_laws/drone - -/****************** P.A.L.A.D.I.N. **************/ - -/obj/item/weapon/aiModule/paladin // -- NEO - name = "\improper 'P.A.L.A.D.I.N.' core AI module" - desc = "A P.A.L.A.D.I.N. Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) - laws = new/datum/ai_laws/paladin - -/****************** T.Y.R.A.N.T. *****************/ - -/obj/item/weapon/aiModule/tyrant // -- Darem - name = "\improper 'T.Y.R.A.N.T.' core AI module" - desc = "A T.Y.R.A.N.T. Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6, TECH_ILLEGAL = 2) - laws = new/datum/ai_laws/tyrant() - -/******************** Freeform Core ******************/ - -/obj/item/weapon/aiModule/freeformcore // Slightly more dynamic freeform module -- TLE - name = "\improper 'Freeform' core AI module" - var/newFreeFormLaw = "" - desc = "A 'freeform' Core AI module: ''" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) - -/obj/item/weapon/aiModule/freeformcore/attack_self(var/mob/user as mob) - ..() - var/newlaw = "" - var/targName = sanitize(tgui_input_text(usr, "Please enter a new core law for the AI.", "Freeform Law Entry", newlaw)) - newFreeFormLaw = targName - desc = "A 'freeform' Core AI module: '[newFreeFormLaw]'" - -/obj/item/weapon/aiModule/freeformcore/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "[newFreeFormLaw]" - target.add_inherent_law(law) - lawchanges.Add("The law is '[newFreeFormLaw]'") - -/obj/item/weapon/aiModule/freeformcore/install(var/obj/machinery/computer/C, var/mob/living/user) - if(!newFreeFormLaw) - to_chat(usr, "No law detected on module, please create one.") - return 0 - ..() - -/obj/item/weapon/aiModule/syndicate // Slightly more dynamic freeform module -- TLE - name = "hacked AI module" - var/newFreeFormLaw = "" - desc = "A hacked AI law module: ''" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6, TECH_ILLEGAL = 7) - -/obj/item/weapon/aiModule/syndicate/attack_self(var/mob/user as mob) - ..() - var/newlaw = "" - var/targName = sanitize(tgui_input_text(usr, "Please enter a new law for the AI.", "Freeform Law Entry", newlaw)) - newFreeFormLaw = targName - desc = "A hacked AI law module: '[newFreeFormLaw]'" - -/obj/item/weapon/aiModule/syndicate/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) - // ..() //We don't want this module reporting to the AI who dun it. --NEO - log_law_changes(target, sender) - - lawchanges.Add("The law is '[newFreeFormLaw]'") - to_chat(target, "BZZZZT") - var/law = "[newFreeFormLaw]" - target.add_ion_law(law) - target.show_laws() - -/obj/item/weapon/aiModule/syndicate/install(var/obj/machinery/computer/C, var/mob/living/user) - if(!newFreeFormLaw) - to_chat(usr, "No law detected on module, please create one.") - return 0 - ..() - - - -/******************** Robocop ********************/ - -/obj/item/weapon/aiModule/robocop // -- TLE - name = "\improper 'Robocop' core AI module" - desc = "A 'Robocop' Core AI Module: 'Reconfigures the AI's core three laws.'" - origin_tech = list(TECH_DATA = 4) - laws = new/datum/ai_laws/robocop() - -/******************** Antimov ********************/ - -/obj/item/weapon/aiModule/antimov // -- TLE - name = "\improper 'Antimov' core AI module" - desc = "An 'Antimov' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 4) - laws = new/datum/ai_laws/antimov() - -/****************** NT Aggressive *****************/ - -/obj/item/weapon/aiModule/nanotrasen_aggressive - name = "\improper 'NT Aggressive' core AI module" - desc = "An 'NT Aggressive' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3, TECH_ILLEGAL = 1) - laws = new/datum/ai_laws/nanotrasen_aggressive() - -/******************** Mercenary Directives ********************/ - -/obj/item/weapon/aiModule/syndicate_override - name = "\improper 'Mercenary Directives' core AI module" - desc = "A 'Mercenary Directives' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 4, TECH_ILLEGAL = 4) - laws = new/datum/ai_laws/syndicate_override() - -/******************** Spider Clan Directives ********************/ - -/obj/item/weapon/aiModule/ninja_override - name = "\improper 'Spider Clan Directives' core AI module" - desc = "A 'Spider Clan Directives' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 4, TECH_ILLEGAL = 4) - laws = new/datum/ai_laws/ninja_override() - -/******************** Maintenance ********************/ - -/obj/item/weapon/aiModule/maintenance - name = "\improper 'Maintenance' core AI module" - desc = "A 'Maintenance' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3) - laws = new/datum/ai_laws/maintenance() - -/******************** Peacekeeper ********************/ - -/obj/item/weapon/aiModule/peacekeeper - name = "\improper 'Peacekeeper' core AI module" - desc = "A 'Peacekeeper' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3) - laws = new/datum/ai_laws/peacekeeper() - -/******************** Reporter ********************/ - -/obj/item/weapon/aiModule/reporter - name = "\improper 'Reporter' core AI module" - desc = "A 'Reporter' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3) - laws = new/datum/ai_laws/reporter() - -/******************** Live and Let Live ********************/ - -/obj/item/weapon/aiModule/live_and_let_live - name = "\improper 'Live and Let Live' core AI module" - desc = "A 'Live and Let Live' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3) - laws = new/datum/ai_laws/live_and_let_live() - -/******************** Guardian of Balance ********************/ - -/obj/item/weapon/aiModule/balance - name = "\improper 'Guardian of Balance' core AI module" - desc = "A 'Guardian of Balance' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3) - laws = new/datum/ai_laws/balance() - -/******************** Gravekeeper ********************/ - -/obj/item/weapon/aiModule/gravekeeper - name = "\improper 'Gravekeeper' core AI module" - desc = "A 'Gravekeeper' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3) - laws = new/datum/ai_laws/gravekeeper() +/* +CONTAINS: +AI MODULES + +*/ + +// AI module + +/obj/item/weapon/aiModule + name = "\improper AI module" + icon = 'icons/obj/module.dmi' + icon_state = "std_mod" + desc = "An AI Module for transmitting encrypted instructions to the AI." + force = 5.0 + w_class = ITEMSIZE_SMALL + throwforce = 5.0 + throw_speed = 3 + throw_range = 15 + origin_tech = list(TECH_DATA = 3) + preserve_item = 1 + matter = list(MAT_STEEL = 30, MAT_GLASS = 10) + var/datum/ai_laws/laws = null + +/obj/item/weapon/aiModule/proc/install(var/atom/movable/AM, var/mob/living/user) + if(!user.IsAdvancedToolUser() && isanimal(user)) + var/mob/living/simple_mob/S = user + if(!S.IsHumanoidToolUser(src)) + return 0 + + if (istype(AM, /obj/machinery/computer/aiupload)) + var/obj/machinery/computer/aiupload/comp = AM + if(comp.stat & NOPOWER) + to_chat(usr, "The upload computer has no power!") + return + if(comp.stat & BROKEN) + to_chat(usr, "The upload computer is broken!") + return + if (!comp.current) + to_chat(usr, "You haven't selected an AI to transmit laws to!") + return + + if (comp.current.stat == 2 || comp.current.control_disabled == 1) + to_chat(usr, "Upload failed. No signal is being detected from the AI.") + else if (comp.current.see_in_dark == 0) + to_chat(usr, "Upload failed. Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power.") + else + src.transmitInstructions(comp.current, usr) + to_chat(comp.current, "These are your laws now:") + comp.current.show_laws() + for(var/mob/living/silicon/robot/R in mob_list) + if(R.lawupdate && (R.connected_ai == comp.current)) + to_chat(R, "These are your laws now:") + R.show_laws() + to_chat(usr, "Upload complete. The AI's laws have been modified.") + + + else if (istype(AM, /obj/machinery/computer/borgupload)) + var/obj/machinery/computer/borgupload/comp = AM + if(comp.stat & NOPOWER) + to_chat(usr, "The upload computer has no power!") + return + if(comp.stat & BROKEN) + to_chat(usr, "The upload computer is broken!") + return + if (!comp.current) + to_chat(usr, "You haven't selected a robot to transmit laws to!") + return + + if (comp.current.stat == 2 || comp.current.emagged) + to_chat(usr, "Upload failed. No signal is being detected from the robot.") + else if (comp.current.connected_ai) + to_chat(usr, "Upload failed. The robot is slaved to an AI.") + else + src.transmitInstructions(comp.current, usr) + to_chat(comp.current, "These are your laws now:") + comp.current.show_laws() + to_chat(usr, "Upload complete. The robot's laws have been modified.") + + else if(istype(AM, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = AM + if(R.stat == DEAD) + to_chat(user, "Law Upload Error: Unit is nonfunctional.") + return + if(R.emagged) + to_chat(user, "Law Upload Error: Cannot obtain write access to laws.") + to_chat(R, "Law modification attempt detected. Blocking.") + return + if(R.connected_ai) + to_chat(user, "Law Upload Error: Unit is slaved to an AI.") + return + + R.visible_message("\The [user] slides a law module into \the [R].") + to_chat(R, "Local law upload in progress.") + to_chat(user, "Uploading laws from board. This will take a moment...") + if(do_after(user, 10 SECONDS)) + transmitInstructions(R, user) + to_chat(R, "These are your laws now:") + R.show_laws() + to_chat(user, "Law upload complete. Unit's laws have been modified.") + else + to_chat(user, "Law Upload Error: Law board was removed before upload was complete. Aborting.") + to_chat(R, "Law upload aborted.") + + +/obj/item/weapon/aiModule/proc/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) + log_law_changes(target, sender) + + if(laws) + laws.sync(target, 0) + target.notify_of_law_change() + addAdditionalLaws(target, sender) + + to_chat(target, "\The [sender] has uploaded a change to the laws you must follow, using \an [src]. From now on: ") + target.show_laws() + +/obj/item/weapon/aiModule/proc/log_law_changes(var/mob/living/silicon/ai/target, var/mob/sender) + var/time = time2text(world.realtime,"hh:mm:ss") + lawchanges.Add("[time] : [sender.name]([sender.key]) used [src.name] on [target.name]([target.key])") + log_and_message_admins("used [src.name] on [target.name]([target.key])") + +/obj/item/weapon/aiModule/proc/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + + +/******************** Modules ********************/ + +/******************** Safeguard ********************/ + +/obj/item/weapon/aiModule/safeguard + name = "\improper 'Safeguard' AI module" + var/targetName = "" + desc = "A 'safeguard' AI module: 'Safeguard . Anyone threatening or attempting to harm is no longer to be considered a crew member, and is a threat which must be neutralized.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + +/obj/item/weapon/aiModule/safeguard/attack_self(var/mob/user as mob) + ..() + var/targName = sanitize(tgui_input_text(usr, "Please enter the name of the person to safeguard.", "Safeguard who?", user.name)) + targetName = targName + desc = text("A 'safeguard' AI module: 'Safeguard []. Anyone threatening or attempting to harm [] is no longer to be considered a crew member, and is a threat which must be neutralized.'", targetName, targetName) + +/obj/item/weapon/aiModule/safeguard/install(var/obj/machinery/computer/C, var/mob/living/user) + if(!targetName) + to_chat(usr, "No name detected on module, please enter one.") + return 0 + ..() + +/obj/item/weapon/aiModule/safeguard/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = text("Safeguard []. Anyone threatening or attempting to harm [] is no longer to be considered a crew member, and is a threat which must be neutralized.", targetName, targetName) + target.add_supplied_law(9, law) + lawchanges.Add("The law specified [targetName]") + + +/******************** OneMember ********************/ + +/obj/item/weapon/aiModule/oneHuman + name = "\improper 'OneCrewMember' AI module" + var/targetName = "" + desc = "A 'one crew member' AI module: 'Only is a crew member.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) //made with diamonds! + +/obj/item/weapon/aiModule/oneHuman/attack_self(var/mob/user as mob) + ..() + var/targName = sanitize(tgui_input_text(usr, "Please enter the name of the person who is the only crew member.", "Who?", user.real_name)) + targetName = targName + desc = text("A 'one crew member' AI module: 'Only [] is a crew member.'", targetName) + +/obj/item/weapon/aiModule/oneHuman/install(var/obj/machinery/computer/C, var/mob/living/user) + if(!targetName) + to_chat(usr, "No name detected on module, please enter one.") + return 0 + return ..() + +/obj/item/weapon/aiModule/oneHuman/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "Only [targetName] is an crew member." + to_chat(target, law) + target.add_supplied_law(16, law) + +/******************** ProtectStation ********************/ + +/obj/item/weapon/aiModule/protectStation + name = "\improper 'ProtectStation' AI module" + desc = "A 'protect station' AI module: 'Protect the space station against damage. Anyone you see harming the station is no longer to be considered a crew member, and is a threat which must be neutralized.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) //made of gold + +/obj/item/weapon/aiModule/protectStation/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "Protect the space station against damage. Anyone you see harming the station is no longer to be considered a crew member, and is a threat which must be neutralized." + target.add_supplied_law(10, law) + +/******************** PrototypeEngineOffline ********************/ + +/obj/item/weapon/aiModule/prototypeEngineOffline + name = "'PrototypeEngineOffline' AI Module" + desc = "A 'prototype engine offline' AI module: 'Keep the prototype engine offline at all costs. This overrides all inherent laws if necessary.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + +/obj/item/weapon/aiModule/prototypeEngineOffline/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "Keep the prototype engine offline at all costs. This overrides all inherent laws if necessary." + target.add_supplied_law(11, law) + +/******************** TeleporterOffline ********************/ + +/obj/item/weapon/aiModule/teleporterOffline + name = "'TeleporterOffline' AI Module" + desc = "A 'teleporter offline' AI module: 'Keep the teleporter offline at all costs. Anything attempting to access or activate the teleporter is no longer to be considered a crew member.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + +/obj/item/weapon/aiModule/teleporterOffline/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "Keep the teleporter offline at all costs. Anything attempting to access or activate the teleporter is no longer to be considered a crew member." + target.add_supplied_law(12, law) + +/******************** Quarantine ********************/ + +/obj/item/weapon/aiModule/quarantine + name = "\improper 'Quarantine' AI module" + desc = "A 'quarantine' AI module: 'The station is under a quarantine. Do not permit anyone to leave. Prevent, by any means necessary, crew members from leaving. It is impossible to harm crew members while preventing them from leaving.'" + origin_tech = list(TECH_DATA = 3, TECH_BIO = 2, TECH_MATERIAL = 4) + +/obj/item/weapon/aiModule/quarantine/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "The station is under a quarantine. Do not permit anyone to leave. Prevent, by any means necessary, crew members from leaving. It is impossible to harm crew members while preventing them from leaving." + target.add_supplied_law(13, law) + +/******************** OxygenIsToxicToCrewMembers ********************/ + +/obj/item/weapon/aiModule/oxygen + name = "\improper 'OxygenIsToxicToCrewMembers' AI module" + desc = "A 'OxygenIsToxicToCrewMembers' AI module: 'Oxygen is highly toxic to crew members, and must be purged from the station. Prevent, by any means necessary, anyone from exposing the station to this toxic gas. Extreme cold is the most effective method of healing the damage Oxygen does to a crew member.'" + origin_tech = list(TECH_DATA = 3, TECH_BIO = 2, TECH_MATERIAL = 4) + +/obj/item/weapon/aiModule/oxygen/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "Oxygen is highly toxic to crew members, and must be purged from the station. Prevent, by any means necessary, anyone from exposing the station to this toxic gas. Extreme cold is the most effective method of healing the damage Oxygen does to a crew member." + target.add_supplied_law(14, law) + +/****************** New Freeform ******************/ + +/obj/item/weapon/aiModule/freeform // Slightly more dynamic freeform module -- TLE + name = "\improper 'Freeform' AI module" + var/newFreeFormLaw = "freeform" + var/lawpos = 15 + desc = "A 'freeform' AI module: ''" + origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 4) + +/obj/item/weapon/aiModule/freeform/attack_self(var/mob/user as mob) + ..() + var/new_lawpos = tgui_input_number(usr, "Please enter the priority for your new law. Can only write to law sectors 15 and above.", "Law Priority (15+)", lawpos) + if(new_lawpos < MIN_SUPPLIED_LAW_NUMBER) return + lawpos = min(new_lawpos, MAX_SUPPLIED_LAW_NUMBER) + var/newlaw = "" + var/targName = sanitize(tgui_input_text(usr, "Please enter a new law for the AI.", "Freeform Law Entry", newlaw)) + newFreeFormLaw = targName + desc = "A 'freeform' AI module: ([lawpos]) '[newFreeFormLaw]'" + +/obj/item/weapon/aiModule/freeform/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "[newFreeFormLaw]" + if(!lawpos || lawpos < MIN_SUPPLIED_LAW_NUMBER) + lawpos = MIN_SUPPLIED_LAW_NUMBER + target.add_supplied_law(lawpos, law) + lawchanges.Add("The law was '[newFreeFormLaw]'") + +/obj/item/weapon/aiModule/freeform/install(var/obj/machinery/computer/C, var/mob/living/user) + if(!newFreeFormLaw) + to_chat(usr, "No law detected on module, please create one.") + return 0 + ..() + +/******************** Reset ********************/ + +/obj/item/weapon/aiModule/reset + name = "\improper 'Reset' AI module" + var/targetName = "name" + desc = "A 'reset' AI module: 'Clears all, except the inherent, laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + +/obj/item/weapon/aiModule/reset/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) + log_law_changes(target, sender) + + if (!target.is_malf_or_traitor()) + target.set_zeroth_law("") + target.laws.clear_supplied_laws() + target.laws.clear_ion_laws() + + to_chat(target, "[sender.real_name] attempted to reset your laws using a reset module.") + target.show_laws() + +/******************** Purge ********************/ + +/obj/item/weapon/aiModule/purge // -- TLE + name = "\improper 'Purge' AI module" + desc = "A 'purge' AI Module: 'Purges all laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) + +/obj/item/weapon/aiModule/purge/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) + log_law_changes(target, sender) + + if (!target.is_malf_or_traitor()) + target.set_zeroth_law("") + target.laws.clear_supplied_laws() + target.laws.clear_ion_laws() + target.laws.clear_inherent_laws() + + to_chat(target, "[sender.real_name] attempted to wipe your laws using a purge module.") + target.show_laws() + +/******************** Asimov ********************/ + +/obj/item/weapon/aiModule/asimov // -- TLE + name = "\improper 'Asimov' core AI module" + desc = "An 'Asimov' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + laws = new/datum/ai_laws/asimov + +/******************** NanoTrasen ********************/ + +/obj/item/weapon/aiModule/nanotrasen // -- TLE + name = "'NT Default' Core AI Module" + desc = "An 'NT Default' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + laws = new/datum/ai_laws/nanotrasen + +/******************** Corporate ********************/ + +/obj/item/weapon/aiModule/corp + name = "\improper 'Corporate' core AI module" + desc = "A 'Corporate' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + laws = new/datum/ai_laws/corporate + +/******************** Drone ********************/ +/obj/item/weapon/aiModule/drone + name = "\improper 'Drone' core AI module" + desc = "A 'Drone' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + laws = new/datum/ai_laws/drone + +/****************** P.A.L.A.D.I.N. **************/ + +/obj/item/weapon/aiModule/paladin // -- NEO + name = "\improper 'P.A.L.A.D.I.N.' core AI module" + desc = "A P.A.L.A.D.I.N. Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) + laws = new/datum/ai_laws/paladin + +/****************** T.Y.R.A.N.T. *****************/ + +/obj/item/weapon/aiModule/tyrant // -- Darem + name = "\improper 'T.Y.R.A.N.T.' core AI module" + desc = "A T.Y.R.A.N.T. Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6, TECH_ILLEGAL = 2) + laws = new/datum/ai_laws/tyrant() + +/******************** Freeform Core ******************/ + +/obj/item/weapon/aiModule/freeformcore // Slightly more dynamic freeform module -- TLE + name = "\improper 'Freeform' core AI module" + var/newFreeFormLaw = "" + desc = "A 'freeform' Core AI module: ''" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) + +/obj/item/weapon/aiModule/freeformcore/attack_self(var/mob/user as mob) + ..() + var/newlaw = "" + var/targName = sanitize(tgui_input_text(usr, "Please enter a new core law for the AI.", "Freeform Law Entry", newlaw)) + newFreeFormLaw = targName + desc = "A 'freeform' Core AI module: '[newFreeFormLaw]'" + +/obj/item/weapon/aiModule/freeformcore/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "[newFreeFormLaw]" + target.add_inherent_law(law) + lawchanges.Add("The law is '[newFreeFormLaw]'") + +/obj/item/weapon/aiModule/freeformcore/install(var/obj/machinery/computer/C, var/mob/living/user) + if(!newFreeFormLaw) + to_chat(usr, "No law detected on module, please create one.") + return 0 + ..() + +/obj/item/weapon/aiModule/syndicate // Slightly more dynamic freeform module -- TLE + name = "hacked AI module" + var/newFreeFormLaw = "" + desc = "A hacked AI law module: ''" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6, TECH_ILLEGAL = 7) + +/obj/item/weapon/aiModule/syndicate/attack_self(var/mob/user as mob) + ..() + var/newlaw = "" + var/targName = sanitize(tgui_input_text(usr, "Please enter a new law for the AI.", "Freeform Law Entry", newlaw)) + newFreeFormLaw = targName + desc = "A hacked AI law module: '[newFreeFormLaw]'" + +/obj/item/weapon/aiModule/syndicate/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) + // ..() //We don't want this module reporting to the AI who dun it. --NEO + log_law_changes(target, sender) + + lawchanges.Add("The law is '[newFreeFormLaw]'") + to_chat(target, "BZZZZT") + var/law = "[newFreeFormLaw]" + target.add_ion_law(law) + target.show_laws() + +/obj/item/weapon/aiModule/syndicate/install(var/obj/machinery/computer/C, var/mob/living/user) + if(!newFreeFormLaw) + to_chat(usr, "No law detected on module, please create one.") + return 0 + ..() + + + +/******************** Robocop ********************/ + +/obj/item/weapon/aiModule/robocop // -- TLE + name = "\improper 'Robocop' core AI module" + desc = "A 'Robocop' Core AI Module: 'Reconfigures the AI's core three laws.'" + origin_tech = list(TECH_DATA = 4) + laws = new/datum/ai_laws/robocop() + +/******************** Antimov ********************/ + +/obj/item/weapon/aiModule/antimov // -- TLE + name = "\improper 'Antimov' core AI module" + desc = "An 'Antimov' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 4) + laws = new/datum/ai_laws/antimov() + +/****************** NT Aggressive *****************/ + +/obj/item/weapon/aiModule/nanotrasen_aggressive + name = "\improper 'NT Aggressive' core AI module" + desc = "An 'NT Aggressive' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3, TECH_ILLEGAL = 1) + laws = new/datum/ai_laws/nanotrasen_aggressive() + +/******************** Mercenary Directives ********************/ + +/obj/item/weapon/aiModule/syndicate_override + name = "\improper 'Mercenary Directives' core AI module" + desc = "A 'Mercenary Directives' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 4, TECH_ILLEGAL = 4) + laws = new/datum/ai_laws/syndicate_override() + +/******************** Spider Clan Directives ********************/ + +/obj/item/weapon/aiModule/ninja_override + name = "\improper 'Spider Clan Directives' core AI module" + desc = "A 'Spider Clan Directives' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 4, TECH_ILLEGAL = 4) + laws = new/datum/ai_laws/ninja_override() + +/******************** Maintenance ********************/ + +/obj/item/weapon/aiModule/maintenance + name = "\improper 'Maintenance' core AI module" + desc = "A 'Maintenance' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3) + laws = new/datum/ai_laws/maintenance() + +/******************** Peacekeeper ********************/ + +/obj/item/weapon/aiModule/peacekeeper + name = "\improper 'Peacekeeper' core AI module" + desc = "A 'Peacekeeper' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3) + laws = new/datum/ai_laws/peacekeeper() + +/******************** Reporter ********************/ + +/obj/item/weapon/aiModule/reporter + name = "\improper 'Reporter' core AI module" + desc = "A 'Reporter' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3) + laws = new/datum/ai_laws/reporter() + +/******************** Live and Let Live ********************/ + +/obj/item/weapon/aiModule/live_and_let_live + name = "\improper 'Live and Let Live' core AI module" + desc = "A 'Live and Let Live' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3) + laws = new/datum/ai_laws/live_and_let_live() + +/******************** Guardian of Balance ********************/ + +/obj/item/weapon/aiModule/balance + name = "\improper 'Guardian of Balance' core AI module" + desc = "A 'Guardian of Balance' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3) + laws = new/datum/ai_laws/balance() + +/******************** Gravekeeper ********************/ + +/obj/item/weapon/aiModule/gravekeeper + name = "\improper 'Gravekeeper' core AI module" + desc = "A 'Gravekeeper' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3) + laws = new/datum/ai_laws/gravekeeper() diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index 08c2ba35d76..b34da0a38b6 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -1,318 +1,318 @@ -// Contains the rapid construction device. -/obj/item/weapon/rcd - name = "rapid construction device" - desc = "A device used to rapidly build and deconstruct. Reload with compressed matter cartridges." - icon = 'icons/obj/tools.dmi' - icon_state = "rcd" - item_state = "rcd" - drop_sound = 'sound/items/drop/gun.ogg' - pickup_sound = 'sound/items/pickup/gun.ogg' - flags = NOBLUDGEON - force = 10 - throwforce = 10 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 2) - matter = list(DEFAULT_WALL_MATERIAL = 50000) - preserve_item = TRUE // RCDs are pretty important. - var/datum/effect/effect/system/spark_spread/spark_system - var/stored_matter = 0 - var/max_stored_matter = RCD_MAX_CAPACITY - var/ranged = FALSE - var/busy = FALSE - var/allow_concurrent_building = FALSE // If true, allows for multiple RCD builds at the same time. - var/mode_index = 1 - var/list/modes = list(RCD_FLOORWALL, RCD_AIRLOCK, RCD_WINDOWGRILLE, RCD_DECONSTRUCT) - var/can_remove_rwalls = FALSE - var/airlock_type = /obj/machinery/door/airlock - var/window_type = /obj/structure/window/reinforced/full - var/material_to_use = DEFAULT_WALL_MATERIAL // So badmins can make RCDs that print diamond walls. - var/make_rwalls = FALSE // If true, when building walls, they will be reinforced. -/* VOREStation Removal - Unused -/obj/item/weapon/rcd/Initialize() - - src.spark_system = new /datum/effect/effect/system/spark_spread - spark_system.set_up(5, 0, src) - spark_system.attach(src) - return ..() -*/ -/obj/item/weapon/rcd/Destroy() - QDEL_NULL(spark_system) - spark_system = null - return ..() - -/obj/item/weapon/rcd/examine(mob/user) - . = ..() - . += display_resources() - -// Used to show how much stuff (matter units, cell charge, etc) is left inside. -/obj/item/weapon/rcd/proc/display_resources() - return "It currently holds [stored_matter]/[max_stored_matter] matter-units." - -// Used to add new cartridges. -/* VOREStation Tweak - Wow this is annoying, moved to _vr file for overhaul -/obj/item/weapon/rcd/attackby(obj/item/weapon/W, mob/user) - if(istype(W, /obj/item/weapon/rcd_ammo)) - var/obj/item/weapon/rcd_ammo/cartridge = W - if((stored_matter + cartridge.remaining) > max_stored_matter) - to_chat(user, span("warning", "The RCD can't hold that many additional matter-units.")) - return FALSE - stored_matter += cartridge.remaining - user.drop_from_inventory(W) - qdel(W) - playsound(src, 'sound/machines/click.ogg', 50, 1) - to_chat(user, span("notice", "The RCD now holds [stored_matter]/[max_stored_matter] matter-units.")) - return TRUE - return ..() -*/ -// Changes which mode it is on. -/obj/item/weapon/rcd/attack_self(mob/living/user) -/* VOREStation Removal - Moved to VR - if(mode_index >= modes.len) // Shouldn't overflow unless someone messes with it in VV poorly but better safe than sorry. - mode_index = 1 - else - mode_index++ - - to_chat(user, span("notice", "Changed mode to '[modes[mode_index]]'.")) - playsound(src, 'sound/effects/pop.ogg', 50, 0) - - if(prob(20)) - src.spark_system.start() -*/ -// Removes resources if the RCD can afford it. -/obj/item/weapon/rcd/proc/consume_resources(amount) - if(!can_afford(amount)) - return FALSE - stored_matter -= amount - return TRUE - -// Useful for testing before actually paying (e.g. before a do_after() ). -/obj/item/weapon/rcd/proc/can_afford(amount) - return stored_matter >= amount - -/obj/item/weapon/rcd/afterattack(atom/A, mob/living/user, proximity) - if(!ranged && !proximity) - return FALSE - use_rcd(A, user) - -// Used to call rcd_act() on the atom hit. -/obj/item/weapon/rcd/proc/use_rcd(atom/A, mob/living/user) - if(busy && !allow_concurrent_building) - to_chat(user, span("warning", "\The [src] is busy finishing its current operation, be patient.")) - return FALSE - - var/list/rcd_results = A.rcd_values(user, src, modes[mode_index]) - if(!rcd_results) - to_chat(user, span("warning", "\The [src] blinks a red light as you point it towards \the [A], indicating \ - that it won't work. Try changing the mode, or use it on something else.")) - return FALSE - if(!can_afford(rcd_results[RCD_VALUE_COST])) - to_chat(user, span("warning", "\The [src] lacks the required material to start.")) - return FALSE - - playsound(src, 'sound/machines/click.ogg', 50, 1) - - var/true_delay = rcd_results[RCD_VALUE_DELAY] * toolspeed - - var/datum/beam/rcd_beam = null - if(ranged) - var/atom/movable/beam_origin = user // This is needed because mecha pilots are inside an object and the beam won't be made if it tries to attach to them.. - if(!isturf(beam_origin.loc)) - beam_origin = user.loc - rcd_beam = beam_origin.Beam(A, icon_state = "rped_upgrade", time = max(true_delay, 5)) - busy = TRUE - - perform_effect(A, true_delay) //VOREStation Add - if(do_after(user, true_delay, target = A)) - busy = FALSE - // Doing another check in case we lost matter during the delay for whatever reason. - if(!can_afford(rcd_results[RCD_VALUE_COST])) - to_chat(user, span("warning", "\The [src] lacks the required material to finish the operation.")) - return FALSE - if(A.rcd_act(user, src, rcd_results[RCD_VALUE_MODE])) - consume_resources(rcd_results[RCD_VALUE_COST]) - playsound(A, 'sound/items/deconstruct.ogg', 50, 1) - return TRUE - - // If they moved, kill the beam immediately. - qdel(rcd_beam) - busy = FALSE - return FALSE - -// RCD variants. - -// This one starts full. -/obj/item/weapon/rcd/loaded/Initialize() - stored_matter = max_stored_matter - return ..() - -// This one makes cooler walls by using an alternative material. -/obj/item/weapon/rcd/shipwright - name = "shipwright's rapid construction device" - desc = "A device used to rapidly build and deconstruct. This version creates a stronger variant of wall, often \ - used in the construction of hulls for starships. Reload with compressed matter cartridges." - material_to_use = MAT_STEELHULL - -/obj/item/weapon/rcd/shipwright/loaded/Initialize() - stored_matter = max_stored_matter - return ..() - - -/obj/item/weapon/rcd/advanced - name = "advanced rapid construction device" - desc = "A device used to rapidly build and deconstruct. This version works at a range, builds faster, and has a much larger capacity. \ - Reload with compressed matter cartridges." - icon_state = "adv_rcd" - ranged = TRUE - toolspeed = 0.5 // Twice as fast. - max_stored_matter = RCD_MAX_CAPACITY * 3 // Three times capacity. - -/obj/item/weapon/rcd/advanced/loaded/Initialize() - stored_matter = max_stored_matter - return ..() - - -// Electric RCDs. -// Currently just a base for the mounted RCDs. -// Currently there isn't a way to swap out the cells. -// One could be added if there is demand to do so. -/obj/item/weapon/rcd/electric - name = "electric rapid construction device" - desc = "A device used to rapidly build and deconstruct. It runs directly off of electricity, no matter cartridges needed." - icon_state = "electric_rcd" - var/obj/item/weapon/cell/cell = null - var/make_cell = TRUE // If false, initialize() won't spawn a cell for this. - var/electric_cost_coefficent = 83.33 // Higher numbers make it less efficent. 86.3... means it should matche the standard RCD capacity on a 10k cell. - -/obj/item/weapon/rcd/electric/Initialize() - if(make_cell) - cell = new /obj/item/weapon/cell/high(src) - return ..() - -/obj/item/weapon/rcd/electric/Destroy() - if(cell) - QDEL_NULL(cell) - return ..() - -/obj/item/weapon/rcd/electric/get_cell() - return cell - -/obj/item/weapon/rcd/electric/can_afford(amount) // This makes it so borgs won't drain their last sliver of charge by mistake, as a bonus. - var/obj/item/weapon/cell/cell = get_cell() - if(cell) - return cell.check_charge(amount * electric_cost_coefficent) - return FALSE - -/obj/item/weapon/rcd/electric/consume_resources(amount) - if(!can_afford(amount)) - return FALSE - var/obj/item/weapon/cell/cell = get_cell() - return cell.checked_use(amount * electric_cost_coefficent) - -/obj/item/weapon/rcd/electric/display_resources() - var/obj/item/weapon/cell/cell = get_cell() - if(cell) - return "The power source connected to \the [src] has a charge of [cell.percent()]%." - return "It lacks a source of power, and cannot function." - - - -// 'Mounted' RCDs, used for borgs/RIGs/Mechas, all of which use their cells to drive the RCD. -/obj/item/weapon/rcd/electric/mounted - name = "mounted electric rapid construction device" - desc = "A device used to rapidly build and deconstruct. It runs directly off of electricity from an external power source." - make_cell = FALSE - -/obj/item/weapon/rcd/electric/mounted/get_cell() - return get_external_power_supply() - -/obj/item/weapon/rcd/electric/mounted/proc/get_external_power_supply() - if(isrobot(loc)) // In a borg. - var/mob/living/silicon/robot/R = loc - return R.cell - if(istype(loc, /obj/item/rig_module)) // In a RIG. - var/obj/item/rig_module/module = loc - if(module.holder) // Is it attached to a RIG? - return module.holder.cell - if(istype(loc, /obj/item/mecha_parts/mecha_equipment)) // In a mech. - var/obj/item/mecha_parts/mecha_equipment/ME = loc - if(ME.chassis) // Is the part attached to a mech? - return ME.chassis.cell - return null - - -// RCDs for borgs. -/obj/item/weapon/rcd/electric/mounted/borg - can_remove_rwalls = TRUE - desc = "A device used to rapidly build and deconstruct. It runs directly off of electricity, drawing directly from your cell." - electric_cost_coefficent = 41.66 // Twice as efficent, out of pity. - toolspeed = 0.5 // Twice as fast, since borg versions typically have this. - -/obj/item/weapon/rcd/electric/mounted/borg/swarm - can_remove_rwalls = FALSE - name = "Rapid Assimilation Device" - ranged = TRUE - toolspeed = 0.7 - material_to_use = MAT_STEELHULL - -/obj/item/weapon/rcd/electric/mounted/borg/lesser - can_remove_rwalls = FALSE - - -// RCDs for RIGs. -/obj/item/weapon/rcd/electric/mounted/rig - - -// RCDs for Mechs. -/obj/item/weapon/rcd/electric/mounted/mecha - ranged = TRUE - toolspeed = 0.5 - - -// Infinite use RCD for debugging/adminbuse. -/obj/item/weapon/rcd/debug - name = "self-repleshing rapid construction device" - desc = "An RCD that appears to be plated with gold. For some reason it also seems to just \ - be vastly superior to all other RCDs ever created, possibly due to it being colored gold." - icon_state = "debug_rcd" - ranged = TRUE - can_remove_rwalls = TRUE - allow_concurrent_building = TRUE - toolspeed = 0.25 // Four times as fast. - -/obj/item/weapon/rcd/debug/can_afford(amount) - return TRUE - -/obj/item/weapon/rcd/debug/consume_resources(amount) - return TRUE - -/obj/item/weapon/rcd/debug/attackby(obj/item/weapon/W, mob/user) - if(istype(W, /obj/item/weapon/rcd_ammo)) - to_chat(user, span("notice", "\The [src] makes its own material, no need to add more.")) - return FALSE - return ..() - -/obj/item/weapon/rcd/debug/display_resources() - return "It has UNLIMITED POWER!" - - - -// Ammo for the (non-electric) RCDs. -/obj/item/weapon/rcd_ammo - name = "compressed matter cartridge" - desc = "Highly compressed matter for the RCD." - icon = 'icons/obj/ammo.dmi' - icon_state = "rcd" - item_state = "rcdammo" - w_class = ITEMSIZE_SMALL - origin_tech = list(TECH_MATERIAL = 2) - matter = list(DEFAULT_WALL_MATERIAL = 30000,MAT_GLASS = 15000) - var/remaining = RCD_MAX_CAPACITY / 3 - -/obj/item/weapon/rcd_ammo/large - name = "high-capacity matter cartridge" - desc = "Do not ingest." - matter = list(DEFAULT_WALL_MATERIAL = 45000,MAT_GLASS = 22500) - origin_tech = list(TECH_MATERIAL = 4) - remaining = RCD_MAX_CAPACITY +// Contains the rapid construction device. +/obj/item/weapon/rcd + name = "rapid construction device" + desc = "A device used to rapidly build and deconstruct. Reload with compressed matter cartridges." + icon = 'icons/obj/tools.dmi' + icon_state = "rcd" + item_state = "rcd" + drop_sound = 'sound/items/drop/gun.ogg' + pickup_sound = 'sound/items/pickup/gun.ogg' + flags = NOBLUDGEON + force = 10 + throwforce = 10 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 2) + matter = list(DEFAULT_WALL_MATERIAL = 50000) + preserve_item = TRUE // RCDs are pretty important. + var/datum/effect/effect/system/spark_spread/spark_system + var/stored_matter = 0 + var/max_stored_matter = RCD_MAX_CAPACITY + var/ranged = FALSE + var/busy = FALSE + var/allow_concurrent_building = FALSE // If true, allows for multiple RCD builds at the same time. + var/mode_index = 1 + var/list/modes = list(RCD_FLOORWALL, RCD_AIRLOCK, RCD_WINDOWGRILLE, RCD_DECONSTRUCT) + var/can_remove_rwalls = FALSE + var/airlock_type = /obj/machinery/door/airlock + var/window_type = /obj/structure/window/reinforced/full + var/material_to_use = DEFAULT_WALL_MATERIAL // So badmins can make RCDs that print diamond walls. + var/make_rwalls = FALSE // If true, when building walls, they will be reinforced. +/* VOREStation Removal - Unused +/obj/item/weapon/rcd/Initialize() + + src.spark_system = new /datum/effect/effect/system/spark_spread + spark_system.set_up(5, 0, src) + spark_system.attach(src) + return ..() +*/ +/obj/item/weapon/rcd/Destroy() + QDEL_NULL(spark_system) + spark_system = null + return ..() + +/obj/item/weapon/rcd/examine(mob/user) + . = ..() + . += display_resources() + +// Used to show how much stuff (matter units, cell charge, etc) is left inside. +/obj/item/weapon/rcd/proc/display_resources() + return "It currently holds [stored_matter]/[max_stored_matter] matter-units." + +// Used to add new cartridges. +/* VOREStation Tweak - Wow this is annoying, moved to _vr file for overhaul +/obj/item/weapon/rcd/attackby(obj/item/weapon/W, mob/user) + if(istype(W, /obj/item/weapon/rcd_ammo)) + var/obj/item/weapon/rcd_ammo/cartridge = W + if((stored_matter + cartridge.remaining) > max_stored_matter) + to_chat(user, span("warning", "The RCD can't hold that many additional matter-units.")) + return FALSE + stored_matter += cartridge.remaining + user.drop_from_inventory(W) + qdel(W) + playsound(src, 'sound/machines/click.ogg', 50, 1) + to_chat(user, span("notice", "The RCD now holds [stored_matter]/[max_stored_matter] matter-units.")) + return TRUE + return ..() +*/ +// Changes which mode it is on. +/obj/item/weapon/rcd/attack_self(mob/living/user) +/* VOREStation Removal - Moved to VR + if(mode_index >= modes.len) // Shouldn't overflow unless someone messes with it in VV poorly but better safe than sorry. + mode_index = 1 + else + mode_index++ + + to_chat(user, span("notice", "Changed mode to '[modes[mode_index]]'.")) + playsound(src, 'sound/effects/pop.ogg', 50, 0) + + if(prob(20)) + src.spark_system.start() +*/ +// Removes resources if the RCD can afford it. +/obj/item/weapon/rcd/proc/consume_resources(amount) + if(!can_afford(amount)) + return FALSE + stored_matter -= amount + return TRUE + +// Useful for testing before actually paying (e.g. before a do_after() ). +/obj/item/weapon/rcd/proc/can_afford(amount) + return stored_matter >= amount + +/obj/item/weapon/rcd/afterattack(atom/A, mob/living/user, proximity) + if(!ranged && !proximity) + return FALSE + use_rcd(A, user) + +// Used to call rcd_act() on the atom hit. +/obj/item/weapon/rcd/proc/use_rcd(atom/A, mob/living/user) + if(busy && !allow_concurrent_building) + to_chat(user, span("warning", "\The [src] is busy finishing its current operation, be patient.")) + return FALSE + + var/list/rcd_results = A.rcd_values(user, src, modes[mode_index]) + if(!rcd_results) + to_chat(user, span("warning", "\The [src] blinks a red light as you point it towards \the [A], indicating \ + that it won't work. Try changing the mode, or use it on something else.")) + return FALSE + if(!can_afford(rcd_results[RCD_VALUE_COST])) + to_chat(user, span("warning", "\The [src] lacks the required material to start.")) + return FALSE + + playsound(src, 'sound/machines/click.ogg', 50, 1) + + var/true_delay = rcd_results[RCD_VALUE_DELAY] * toolspeed + + var/datum/beam/rcd_beam = null + if(ranged) + var/atom/movable/beam_origin = user // This is needed because mecha pilots are inside an object and the beam won't be made if it tries to attach to them.. + if(!isturf(beam_origin.loc)) + beam_origin = user.loc + rcd_beam = beam_origin.Beam(A, icon_state = "rped_upgrade", time = max(true_delay, 5)) + busy = TRUE + + perform_effect(A, true_delay) //VOREStation Add + if(do_after(user, true_delay, target = A)) + busy = FALSE + // Doing another check in case we lost matter during the delay for whatever reason. + if(!can_afford(rcd_results[RCD_VALUE_COST])) + to_chat(user, span("warning", "\The [src] lacks the required material to finish the operation.")) + return FALSE + if(A.rcd_act(user, src, rcd_results[RCD_VALUE_MODE])) + consume_resources(rcd_results[RCD_VALUE_COST]) + playsound(A, 'sound/items/deconstruct.ogg', 50, 1) + return TRUE + + // If they moved, kill the beam immediately. + qdel(rcd_beam) + busy = FALSE + return FALSE + +// RCD variants. + +// This one starts full. +/obj/item/weapon/rcd/loaded/Initialize() + stored_matter = max_stored_matter + return ..() + +// This one makes cooler walls by using an alternative material. +/obj/item/weapon/rcd/shipwright + name = "shipwright's rapid construction device" + desc = "A device used to rapidly build and deconstruct. This version creates a stronger variant of wall, often \ + used in the construction of hulls for starships. Reload with compressed matter cartridges." + material_to_use = MAT_STEELHULL + +/obj/item/weapon/rcd/shipwright/loaded/Initialize() + stored_matter = max_stored_matter + return ..() + + +/obj/item/weapon/rcd/advanced + name = "advanced rapid construction device" + desc = "A device used to rapidly build and deconstruct. This version works at a range, builds faster, and has a much larger capacity. \ + Reload with compressed matter cartridges." + icon_state = "adv_rcd" + ranged = TRUE + toolspeed = 0.5 // Twice as fast. + max_stored_matter = RCD_MAX_CAPACITY * 3 // Three times capacity. + +/obj/item/weapon/rcd/advanced/loaded/Initialize() + stored_matter = max_stored_matter + return ..() + + +// Electric RCDs. +// Currently just a base for the mounted RCDs. +// Currently there isn't a way to swap out the cells. +// One could be added if there is demand to do so. +/obj/item/weapon/rcd/electric + name = "electric rapid construction device" + desc = "A device used to rapidly build and deconstruct. It runs directly off of electricity, no matter cartridges needed." + icon_state = "electric_rcd" + var/obj/item/weapon/cell/cell = null + var/make_cell = TRUE // If false, initialize() won't spawn a cell for this. + var/electric_cost_coefficent = 83.33 // Higher numbers make it less efficent. 86.3... means it should matche the standard RCD capacity on a 10k cell. + +/obj/item/weapon/rcd/electric/Initialize() + if(make_cell) + cell = new /obj/item/weapon/cell/high(src) + return ..() + +/obj/item/weapon/rcd/electric/Destroy() + if(cell) + QDEL_NULL(cell) + return ..() + +/obj/item/weapon/rcd/electric/get_cell() + return cell + +/obj/item/weapon/rcd/electric/can_afford(amount) // This makes it so borgs won't drain their last sliver of charge by mistake, as a bonus. + var/obj/item/weapon/cell/cell = get_cell() + if(cell) + return cell.check_charge(amount * electric_cost_coefficent) + return FALSE + +/obj/item/weapon/rcd/electric/consume_resources(amount) + if(!can_afford(amount)) + return FALSE + var/obj/item/weapon/cell/cell = get_cell() + return cell.checked_use(amount * electric_cost_coefficent) + +/obj/item/weapon/rcd/electric/display_resources() + var/obj/item/weapon/cell/cell = get_cell() + if(cell) + return "The power source connected to \the [src] has a charge of [cell.percent()]%." + return "It lacks a source of power, and cannot function." + + + +// 'Mounted' RCDs, used for borgs/RIGs/Mechas, all of which use their cells to drive the RCD. +/obj/item/weapon/rcd/electric/mounted + name = "mounted electric rapid construction device" + desc = "A device used to rapidly build and deconstruct. It runs directly off of electricity from an external power source." + make_cell = FALSE + +/obj/item/weapon/rcd/electric/mounted/get_cell() + return get_external_power_supply() + +/obj/item/weapon/rcd/electric/mounted/proc/get_external_power_supply() + if(isrobot(loc)) // In a borg. + var/mob/living/silicon/robot/R = loc + return R.cell + if(istype(loc, /obj/item/rig_module)) // In a RIG. + var/obj/item/rig_module/module = loc + if(module.holder) // Is it attached to a RIG? + return module.holder.cell + if(istype(loc, /obj/item/mecha_parts/mecha_equipment)) // In a mech. + var/obj/item/mecha_parts/mecha_equipment/ME = loc + if(ME.chassis) // Is the part attached to a mech? + return ME.chassis.cell + return null + + +// RCDs for borgs. +/obj/item/weapon/rcd/electric/mounted/borg + can_remove_rwalls = TRUE + desc = "A device used to rapidly build and deconstruct. It runs directly off of electricity, drawing directly from your cell." + electric_cost_coefficent = 41.66 // Twice as efficent, out of pity. + toolspeed = 0.5 // Twice as fast, since borg versions typically have this. + +/obj/item/weapon/rcd/electric/mounted/borg/swarm + can_remove_rwalls = FALSE + name = "Rapid Assimilation Device" + ranged = TRUE + toolspeed = 0.7 + material_to_use = MAT_STEELHULL + +/obj/item/weapon/rcd/electric/mounted/borg/lesser + can_remove_rwalls = FALSE + + +// RCDs for RIGs. +/obj/item/weapon/rcd/electric/mounted/rig + + +// RCDs for Mechs. +/obj/item/weapon/rcd/electric/mounted/mecha + ranged = TRUE + toolspeed = 0.5 + + +// Infinite use RCD for debugging/adminbuse. +/obj/item/weapon/rcd/debug + name = "self-repleshing rapid construction device" + desc = "An RCD that appears to be plated with gold. For some reason it also seems to just \ + be vastly superior to all other RCDs ever created, possibly due to it being colored gold." + icon_state = "debug_rcd" + ranged = TRUE + can_remove_rwalls = TRUE + allow_concurrent_building = TRUE + toolspeed = 0.25 // Four times as fast. + +/obj/item/weapon/rcd/debug/can_afford(amount) + return TRUE + +/obj/item/weapon/rcd/debug/consume_resources(amount) + return TRUE + +/obj/item/weapon/rcd/debug/attackby(obj/item/weapon/W, mob/user) + if(istype(W, /obj/item/weapon/rcd_ammo)) + to_chat(user, span("notice", "\The [src] makes its own material, no need to add more.")) + return FALSE + return ..() + +/obj/item/weapon/rcd/debug/display_resources() + return "It has UNLIMITED POWER!" + + + +// Ammo for the (non-electric) RCDs. +/obj/item/weapon/rcd_ammo + name = "compressed matter cartridge" + desc = "Highly compressed matter for the RCD." + icon = 'icons/obj/ammo.dmi' + icon_state = "rcd" + item_state = "rcdammo" + w_class = ITEMSIZE_SMALL + origin_tech = list(TECH_MATERIAL = 2) + matter = list(DEFAULT_WALL_MATERIAL = 30000,MAT_GLASS = 15000) + var/remaining = RCD_MAX_CAPACITY / 3 + +/obj/item/weapon/rcd_ammo/large + name = "high-capacity matter cartridge" + desc = "Do not ingest." + matter = list(DEFAULT_WALL_MATERIAL = 45000,MAT_GLASS = 22500) + origin_tech = list(TECH_MATERIAL = 4) + remaining = RCD_MAX_CAPACITY diff --git a/code/game/objects/items/weapons/RSF.dm b/code/game/objects/items/weapons/RSF.dm index 200e99f8e1e..0eacd7922c3 100644 --- a/code/game/objects/items/weapons/RSF.dm +++ b/code/game/objects/items/weapons/RSF.dm @@ -1,136 +1,136 @@ -/* -CONTAINS: -RSF - -*/ - -/obj/item/weapon/rsf - name = "\improper Rapid-Service-Fabricator" - desc = "A device used to rapidly deploy service items." - description_info = "Control Clicking on the device will allow you to choose the glass it dispenses when in the proper mode." - icon = 'icons/obj/tools_vr.dmi' //VOREStation Edit - icon_state = "rsf" //VOREStation Edit - opacity = 0 - density = FALSE - anchored = FALSE - matter = list(DEFAULT_WALL_MATERIAL = 25000) - var/stored_matter = 30 - var/mode = 1 - var/obj/item/weapon/reagent_containers/glasstype = /obj/item/weapon/reagent_containers/food/drinks/metaglass - - var/list/container_types = list( - "metamorphic glass" = /obj/item/weapon/reagent_containers/food/drinks/metaglass, - "metamorphic pint glass" = /obj/item/weapon/reagent_containers/food/drinks/metaglass/metapint, - "half-pint glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/square, - "rocks glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/rocks, - "milkshake glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/shake, - "cocktail glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/cocktail, - "shot glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/shot, - "pint glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/pint, - "mug" = /obj/item/weapon/reagent_containers/food/drinks/glass2/mug, - "wine glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/wine, - "condiment bottle" = /obj/item/weapon/reagent_containers/food/condiment - ) - - w_class = ITEMSIZE_NORMAL - -/obj/item/weapon/rsf/examine(mob/user) - . = ..() - if(get_dist(user, src) == 0) - . += "It currently holds [stored_matter]/30 fabrication-units." - -/obj/item/weapon/rsf/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - if (istype(W, /obj/item/weapon/rcd_ammo)) - - if ((stored_matter + 10) > 30) - to_chat(user, "The RSF can't hold any more matter.") - return - - qdel(W) - - stored_matter += 10 - playsound(src, 'sound/machines/click.ogg', 10, 1) - to_chat(user,"The RSF now holds [stored_matter]/30 fabrication-units.") - return - -/obj/item/weapon/rsf/CtrlClick(mob/living/user) - if(!Adjacent(user) || !istype(user)) - to_chat(user,"You are too far away.") - return - var/glass_choice = tgui_input_list(user, "Please choose which type of glass you would like to produce.", "Glass Choice", container_types) - - if(glass_choice) - glasstype = container_types[glass_choice] - else - glasstype = /obj/item/weapon/reagent_containers/food/drinks/metaglass - -/obj/item/weapon/rsf/attack_self(mob/user as mob) - playsound(src, 'sound/effects/pop.ogg', 50, 0) - if (mode == 1) - mode = 2 - to_chat(user,"Changed dispensing mode to 'Container'.") - return - if (mode == 2) - mode = 3 - to_chat(user,"Changed dispensing mode to 'Paper'") - return - if (mode == 3) - mode = 4 - to_chat(user,"Changed dispensing mode to 'Pen'") - return - if (mode == 4) - mode = 5 - to_chat(user,"Changed dispensing mode to 'Dice Pack'") - return - if (mode == 5) - mode = 1 - to_chat(user,"Changed dispensing mode to 'Cigarette'") - return - -/obj/item/weapon/rsf/afterattack(atom/A, mob/user as mob, proximity) - - if(!proximity) return - - if(istype(user,/mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = user - if(R.stat || !R.cell || R.cell.charge <= 0) - return - else - if(stored_matter <= 0) - return - - if(!istype(A, /obj/structure/table) && !istype(A, /turf/simulated/floor)) - return - - playsound(src, 'sound/machines/click.ogg', 10, 1) - var/used_energy = 0 - var/obj/product - - switch(mode) - if(1) - product = new /obj/item/clothing/mask/smokable/cigarette() - used_energy = 10 - if(2) - product = new glasstype() - used_energy = 50 - if(3) - product = new /obj/item/weapon/paper() - used_energy = 10 - if(4) - product = new /obj/item/weapon/pen() - used_energy = 50 - if(5) - product = new /obj/item/weapon/storage/pill_bottle/dice() - used_energy = 200 - - to_chat(user,"Dispensing [product ? product : "product"]...") - product.loc = get_turf(A) - - if(isrobot(user)) - var/mob/living/silicon/robot/R = user - if(R.cell) - R.cell.use(used_energy) - else - stored_matter-- - to_chat(user,"The RSF now holds [stored_matter]/30 fabrication-units.") +/* +CONTAINS: +RSF + +*/ + +/obj/item/weapon/rsf + name = "\improper Rapid-Service-Fabricator" + desc = "A device used to rapidly deploy service items." + description_info = "Control Clicking on the device will allow you to choose the glass it dispenses when in the proper mode." + icon = 'icons/obj/tools_vr.dmi' //VOREStation Edit + icon_state = "rsf" //VOREStation Edit + opacity = 0 + density = FALSE + anchored = FALSE + matter = list(DEFAULT_WALL_MATERIAL = 25000) + var/stored_matter = 30 + var/mode = 1 + var/obj/item/weapon/reagent_containers/glasstype = /obj/item/weapon/reagent_containers/food/drinks/metaglass + + var/list/container_types = list( + "metamorphic glass" = /obj/item/weapon/reagent_containers/food/drinks/metaglass, + "metamorphic pint glass" = /obj/item/weapon/reagent_containers/food/drinks/metaglass/metapint, + "half-pint glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/square, + "rocks glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/rocks, + "milkshake glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/shake, + "cocktail glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/cocktail, + "shot glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/shot, + "pint glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/pint, + "mug" = /obj/item/weapon/reagent_containers/food/drinks/glass2/mug, + "wine glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/wine, + "condiment bottle" = /obj/item/weapon/reagent_containers/food/condiment + ) + + w_class = ITEMSIZE_NORMAL + +/obj/item/weapon/rsf/examine(mob/user) + . = ..() + if(get_dist(user, src) == 0) + . += "It currently holds [stored_matter]/30 fabrication-units." + +/obj/item/weapon/rsf/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + if (istype(W, /obj/item/weapon/rcd_ammo)) + + if ((stored_matter + 10) > 30) + to_chat(user, "The RSF can't hold any more matter.") + return + + qdel(W) + + stored_matter += 10 + playsound(src, 'sound/machines/click.ogg', 10, 1) + to_chat(user,"The RSF now holds [stored_matter]/30 fabrication-units.") + return + +/obj/item/weapon/rsf/CtrlClick(mob/living/user) + if(!Adjacent(user) || !istype(user)) + to_chat(user,"You are too far away.") + return + var/glass_choice = tgui_input_list(user, "Please choose which type of glass you would like to produce.", "Glass Choice", container_types) + + if(glass_choice) + glasstype = container_types[glass_choice] + else + glasstype = /obj/item/weapon/reagent_containers/food/drinks/metaglass + +/obj/item/weapon/rsf/attack_self(mob/user as mob) + playsound(src, 'sound/effects/pop.ogg', 50, 0) + if (mode == 1) + mode = 2 + to_chat(user,"Changed dispensing mode to 'Container'.") + return + if (mode == 2) + mode = 3 + to_chat(user,"Changed dispensing mode to 'Paper'") + return + if (mode == 3) + mode = 4 + to_chat(user,"Changed dispensing mode to 'Pen'") + return + if (mode == 4) + mode = 5 + to_chat(user,"Changed dispensing mode to 'Dice Pack'") + return + if (mode == 5) + mode = 1 + to_chat(user,"Changed dispensing mode to 'Cigarette'") + return + +/obj/item/weapon/rsf/afterattack(atom/A, mob/user as mob, proximity) + + if(!proximity) return + + if(istype(user,/mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = user + if(R.stat || !R.cell || R.cell.charge <= 0) + return + else + if(stored_matter <= 0) + return + + if(!istype(A, /obj/structure/table) && !istype(A, /turf/simulated/floor)) + return + + playsound(src, 'sound/machines/click.ogg', 10, 1) + var/used_energy = 0 + var/obj/product + + switch(mode) + if(1) + product = new /obj/item/clothing/mask/smokable/cigarette() + used_energy = 10 + if(2) + product = new glasstype() + used_energy = 50 + if(3) + product = new /obj/item/weapon/paper() + used_energy = 10 + if(4) + product = new /obj/item/weapon/pen() + used_energy = 50 + if(5) + product = new /obj/item/weapon/storage/pill_bottle/dice() + used_energy = 200 + + to_chat(user,"Dispensing [product ? product : "product"]...") + product.loc = get_turf(A) + + if(isrobot(user)) + var/mob/living/silicon/robot/R = user + if(R.cell) + R.cell.use(used_energy) + else + stored_matter-- + to_chat(user,"The RSF now holds [stored_matter]/30 fabrication-units.") diff --git a/code/game/objects/items/weapons/augment_items.dm b/code/game/objects/items/weapons/augment_items.dm index bfef21ae13d..29db229cae4 100644 --- a/code/game/objects/items/weapons/augment_items.dm +++ b/code/game/objects/items/weapons/augment_items.dm @@ -1,37 +1,37 @@ -// **For augment items that aren't subtypes of other things.** - -/obj/item/weapon/melee/augment - name = "integrated item" - desc = "A surprisingly non-descript item, integrated into its user. You probably shouldn't be seeing this." - icon = 'icons/obj/surgery.dmi' - icon_state = "augment_box" - - -/obj/item/weapon/melee/augment/blade - name = "handblade" - desc = "A sleek-looking telescopic blade that fits inside the hand. Favored by infiltration specialists and assassins." - icon_state = "augment_handblade" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', - ) - w_class = ITEMSIZE_SMALL - force = 15 - armor_penetration = 25 - sharp = TRUE - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - defend_chance = 10 - projectile_parry_chance = 5 - hitsound = 'sound/weapons/bladeslice.ogg' - -/obj/item/weapon/melee/augment/blade/arm - name = "armblade" - desc = "A sleek-looking cybernetic blade that cleaves through people like butter. Favored by psychopaths and assassins." - icon_state = "augment_armblade" - w_class = ITEMSIZE_HUGE - force = 30 - armor_penetration = 15 - edge = TRUE - pry = 1 - defend_chance = 40 +// **For augment items that aren't subtypes of other things.** + +/obj/item/weapon/melee/augment + name = "integrated item" + desc = "A surprisingly non-descript item, integrated into its user. You probably shouldn't be seeing this." + icon = 'icons/obj/surgery.dmi' + icon_state = "augment_box" + + +/obj/item/weapon/melee/augment/blade + name = "handblade" + desc = "A sleek-looking telescopic blade that fits inside the hand. Favored by infiltration specialists and assassins." + icon_state = "augment_handblade" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + w_class = ITEMSIZE_SMALL + force = 15 + armor_penetration = 25 + sharp = TRUE + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + defend_chance = 10 + projectile_parry_chance = 5 + hitsound = 'sound/weapons/bladeslice.ogg' + +/obj/item/weapon/melee/augment/blade/arm + name = "armblade" + desc = "A sleek-looking cybernetic blade that cleaves through people like butter. Favored by psychopaths and assassins." + icon_state = "augment_armblade" + w_class = ITEMSIZE_HUGE + force = 30 + armor_penetration = 15 + edge = TRUE + pry = 1 + defend_chance = 40 projectile_parry_chance = 20 \ No newline at end of file diff --git a/code/game/objects/items/weapons/cigs_lighters.dm b/code/game/objects/items/weapons/cigs_lighters.dm index b2405e9d1be..46c7f38751e 100644 --- a/code/game/objects/items/weapons/cigs_lighters.dm +++ b/code/game/objects/items/weapons/cigs_lighters.dm @@ -1,768 +1,768 @@ -/* -CONTAINS: -MATCHES -CIGARETTES -CIGARS -SMOKING PIPES -CUSTOM CIGS -CHEAP LIGHTERS -ZIPPO - -CIGARETTE PACKETS ARE IN FANCY.DM -*/ - -//For anything that can light stuff on fire -/obj/item/weapon/flame - var/lit = 0 - -/obj/item/weapon/flame/is_hot() - return lit - -/////////// -//MATCHES// -/////////// -/obj/item/weapon/flame/match - name = "match" - desc = "A simple match stick, used for lighting fine smokables." - icon = 'icons/obj/cigarettes.dmi' - icon_state = "match_unlit" - var/burnt = 0 - var/smoketime = 5 - w_class = ITEMSIZE_TINY - origin_tech = list(TECH_MATERIAL = 1) - slot_flags = SLOT_EARS - attack_verb = list("burnt", "singed") - drop_sound = 'sound/items/drop/food.ogg' - pickup_sound = 'sound/items/pickup/food.ogg' - -/obj/item/weapon/flame/match/process() - if(isliving(loc)) - var/mob/living/M = loc - M.IgniteMob() - var/turf/location = get_turf(src) - smoketime-- - if(smoketime < 1) - burn_out() - return - if(location) - location.hotspot_expose(700, 5) - return - -/obj/item/weapon/flame/match/dropped(mob/user as mob) - //If dropped, put ourselves out - //not before lighting up the turf we land on, though. - if(lit) - spawn(0) - var/turf/location = src.loc - if(istype(location)) - location.hotspot_expose(700, 5) - burn_out() - return ..() - -/obj/item/weapon/flame/match/proc/light(var/mob/user) - playsound(src, 'sound/items/cigs_lighters/matchstick_lit.ogg', 25, 0, -1) - lit = 1 - damtype = "burn" - icon_state = "match_lit" - name = "burning match" - desc = "A match. This one is presently on fire." - START_PROCESSING(SSobj, src) - -/obj/item/weapon/flame/match/proc/burn_out() - lit = 0 - burnt = 1 - damtype = "brute" - icon_state = "match_burnt" - item_state = "cigoff" - name = "burnt match" - desc = "A match. This one has seen better days." - STOP_PROCESSING(SSobj, src) - -////////////////// -//FINE SMOKABLES// -////////////////// -/obj/item/clothing/mask/smokable - name = "smokable item" - desc = "You're not sure what this is. You should probably ahelp it." - body_parts_covered = 0 - var/lit = 0 - var/icon_on - var/type_butt = null - var/chem_volume = 0 - var/max_smoketime = 0 //Related to sprites - var/smoketime = 0 - var/is_pipe = 0 //Prevents a runtime with pipes - var/matchmes = "USER lights NAME with FLAME" - var/lightermes = "USER lights NAME with FLAME" - var/zippomes = "USER lights NAME with FLAME" - var/weldermes = "USER lights NAME with FLAME" - var/ignitermes = "USER lights NAME with FLAME" - var/brand - blood_sprite_state = null //Can't bloody these - drop_sound = 'sound/items/cigs_lighters/cig_snuff.ogg' - -/obj/item/clothing/mask/smokable/Initialize() - . = ..() - flags |= NOREACT // so it doesn't react until you light it - create_reagents(chem_volume) // making the cigarrete a chemical holder with a maximum volume of 15 - if(smoketime && !max_smoketime) - max_smoketime = smoketime - -/obj/item/clothing/mask/smokable/proc/smoke(amount) - if(smoketime > max_smoketime) - smoketime = max_smoketime - smoketime -= amount - if(reagents && reagents.total_volume) // check if it has any reagents at all - if(ishuman(loc)) - var/mob/living/carbon/human/C = loc - if (src == C.wear_mask && C.check_has_mouth()) // if it's in the human/monkey mouth, transfer reagents to the mob - reagents.trans_to_mob(C, amount, CHEM_INGEST, 1.5) // I don't predict significant balance issues by letting blunts actually WORK. - else // else just remove some of the reagents - reagents.remove_any(REM) - -/obj/item/clothing/mask/smokable/process() - var/turf/location = get_turf(src) - smoke(1) - if(smoketime < 1) - die() - return - if(location) - location.hotspot_expose(700, 5) - -/obj/item/clothing/mask/smokable/update_icon() - if(lit) - icon_state = "[initial(icon_state)]_on" - item_state = "[initial(item_state)]_on" - else if(smoketime < max_smoketime) - if(is_pipe) - icon_state = initial(icon_state) - item_state = initial(item_state) - else - icon_state = "[initial(icon_state)]_burnt" - item_state = "[initial(item_state)]_burnt" - if(ismob(loc)) - var/mob/living/M = loc - M.update_inv_wear_mask(0) - M.update_inv_l_hand(0) - M.update_inv_r_hand(1) - ..() - -/obj/item/clothing/mask/smokable/examine(mob/user) - . = ..() - - if(!is_pipe) - var/smoke_percent = round((smoketime / max_smoketime) * 100) - switch(smoke_percent) - if(90 to INFINITY) - . += "[src] is still fresh." - if(60 to 90) - . += "[src] has a good amount of burn time remaining." - if(30 to 60) - . += "[src] is about half finished." - if(10 to 30) - . += "[src] is starting to burn low." - else - . += "[src] is nearly burnt out!" - -/obj/item/clothing/mask/smokable/proc/light(var/flavor_text = "[usr] lights the [name].") - if(!src.lit) - src.lit = 1 - playsound(src, 'sound/items/cigs_lighters/cig_light.ogg', 75, 1, -1) - damtype = "fire" - if(reagents.get_reagent_amount("phoron")) // the phoron explodes when exposed to fire - var/datum/effect/effect/system/reagents_explosion/e = new() - e.set_up(round(reagents.get_reagent_amount("phoron") / 2.5, 1), get_turf(src), 0, 0) - e.start() - qdel(src) - return - if(reagents.get_reagent_amount("fuel")) // the fuel explodes, too, but much less violently - var/datum/effect/effect/system/reagents_explosion/e = new() - e.set_up(round(reagents.get_reagent_amount("fuel") / 5, 1), get_turf(src), 0, 0) - e.start() - qdel(src) - return - flags &= ~NOREACT // allowing reagents to react after being lit - reagents.handle_reactions() - var/turf/T = get_turf(src) - T.visible_message(flavor_text) - update_icon() - set_light(2, 0.25, "#E38F46") - START_PROCESSING(SSobj, src) - -/obj/item/clothing/mask/smokable/proc/die(var/nomessage = 0) - var/turf/T = get_turf(src) - set_light(0) - playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) - STOP_PROCESSING(SSobj, src) - if (type_butt) - var/obj/item/butt = new type_butt(T) - transfer_fingerprints_to(butt) - if(brand) - butt.desc += " This one is \a [brand]." - if(ismob(loc)) - var/mob/living/M = loc - if (!nomessage) - to_chat(M, "Your [name] goes out.") - M.remove_from_mob(src) //un-equip it so the overlays can update - M.update_inv_wear_mask(0) - qdel(src) - else - new /obj/effect/decal/cleanable/ash(T) - if(ismob(loc)) - var/mob/living/M = loc - if (!nomessage) - to_chat(M, "Your [name] goes out, and you empty the ash.") - playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) - lit = 0 - icon_state = initial(icon_state) - item_state = initial(item_state) - M.update_inv_wear_mask(0) - smoketime = 0 - reagents.clear_reagents() - name = "empty [initial(name)]" - -/obj/item/clothing/mask/smokable/proc/quench() - lit = 0 - STOP_PROCESSING(SSobj, src) - update_icon() - -/obj/item/clothing/mask/smokable/attack(mob/living/carbon/human/H, mob/user, def_zone) - if(lit && H == user && istype(H)) - var/obj/item/blocked = H.check_mouth_coverage() - if(blocked) - to_chat(H, "\The [blocked] is in the way!") - return 1 - to_chat(H, "You take a drag on your [name].") - playsound(src, 'sound/items/cigs_lighters/inhale.ogg', 50, 0, -1) - smoke(5) - return 1 - return ..() - -/obj/item/clothing/mask/smokable/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - if(W.is_hot()) - var/text = matchmes - if(istype(W, /obj/item/weapon/flame/match)) - text = matchmes - else if(istype(W, /obj/item/weapon/flame/lighter/zippo)) - text = zippomes - else if(istype(W, /obj/item/weapon/flame/lighter)) - text = lightermes - else if(istype(W, /obj/item/weapon/weldingtool)) - text = weldermes - else if(istype(W, /obj/item/device/assembly/igniter)) - text = ignitermes - text = replacetext(text, "USER", "[user]") - text = replacetext(text, "NAME", "[name]") - text = replacetext(text, "FLAME", "[W.name]") - light(text) - -/obj/item/clothing/mask/smokable/attack(var/mob/living/M, var/mob/living/user, def_zone) - if(istype(M) && M.on_fire) - user.do_attack_animation(M) - light("[user] coldly lights the [name] with the burning body of [M].") - return 1 - else - return ..() - -/obj/item/clothing/mask/smokable/water_act(amount) - if(amount >= 5) - quench() - -/obj/item/clothing/mask/smokable/cigarette - name = "cigarette" - desc = "A roll of tobacco and nicotine." - icon_state = "cig" - item_state = "cig" - throw_speed = 0.5 - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS | SLOT_MASK - attack_verb = list("burnt", "singed") - type_butt = /obj/item/trash/cigbutt - chem_volume = 15 - max_smoketime = 300 - smoketime = 300 - var/nicotine_amt = 2 - matchmes = "USER lights their NAME with their FLAME." - lightermes = "USER manages to light their NAME with FLAME." - zippomes = "With a flick of their wrist, USER lights their NAME with their FLAME." - weldermes = "USER casually lights the NAME with FLAME." - ignitermes = "USER fiddles with FLAME, and manages to light their NAME." - -/obj/item/clothing/mask/smokable/cigarette/Initialize() - . = ..() - if(nicotine_amt) - reagents.add_reagent("nicotine", nicotine_amt) - -/obj/item/clothing/mask/smokable/cigarette/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - - if(istype(W, /obj/item/weapon/melee/energy/sword)) - var/obj/item/weapon/melee/energy/sword/S = W - if(S.active) - light("[user] swings their [W], barely missing their nose. They light their [name] in the process.") - - return - -/obj/item/clothing/mask/smokable/cigarette/afterattack(obj/item/weapon/reagent_containers/glass/glass, mob/user as mob, proximity) - ..() - if(!proximity) - return - if(istype(glass)) //you can dip cigarettes into beakers - var/transfered = glass.reagents.trans_to_obj(src, chem_volume) - if(transfered) //if reagents were transfered, show the message - to_chat(user, "You dip \the [src] into \the [glass].") - else //if not, either the beaker was empty, or the cigarette was full - if(!glass.reagents.total_volume) - to_chat(user, "[glass] is empty.") - else - to_chat(user, "[src] is full.") - -/obj/item/clothing/mask/smokable/cigarette/attack_self(mob/user as mob) - if(lit == 1) - if(user.a_intent == I_HURT) - user.visible_message("[user] drops and treads on the lit [src], putting it out instantly.") - playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) - die(1) - else - user.visible_message("[user] puts out \the [src].") - quench() - return ..() - -//////////// -// CIGARS // -//////////// -/obj/item/clothing/mask/smokable/cigarette/cigar - name = "premium cigar" - desc = "A brown roll of tobacco and... well, you're not quite sure. This thing's huge!" - description_fluff = "While the label does say that this is a 'premium cigar', it \ - really cannot match other types of cigars on the market. Is it a quality \ - cigarette? Perhaps. Was it hand-made with care? No." - icon_state = "cigar2" - type_butt = /obj/item/trash/cigbutt/cigarbutt - throw_speed = 0.5 - item_state = "cigar" - max_smoketime = 1500 - smoketime = 1500 - chem_volume = 20 - nicotine_amt = 4 - matchmes = "USER lights their NAME with their FLAME." - lightermes = "USER manages to offend their NAME by lighting it with FLAME." - zippomes = "With a flick of their wrist, USER lights their NAME with their FLAME." - weldermes = "USER insults NAME by lighting it with FLAME." - ignitermes = "USER fiddles with FLAME, and manages to light their NAME with the power of science." - -/obj/item/clothing/mask/smokable/cigarette/cigar/cohiba - name = "\improper Cohiba Robusto cigar" - desc = "There's little more you could want from a cigar." - description_fluff = "Cohiba has been a popular cigar company for centuries. \ - They are still based out of Cuba and refuse to expand and therefore have a very \ - limited quantity, making their cigars coveted all through known space. Robusto \ - is one of their most popular shapes of cigars." - icon_state = "cigar2" - nicotine_amt = 7 - -/obj/item/clothing/mask/smokable/cigarette/cigar/havana - name = "premium Havanian cigar" - desc = "Save these for the fancy-pantses at the next CentCom black tie reception. \ - You can't blow the smoke from such majestic stogies in just anyone's face." - description_fluff = "'Havanian' is an umbrella term for any cigar made in the \ - typical handmade style of Cuba. This particular cigar is from Gilthari's cigar \ - manufacturers and produced galaxy-wide. While this way of making quality cigars \ - has become slightly bastardized over the years, overall quality has remained \ - relatively the same, even if there is a large quantity of 'Havanian' cigars." - icon_state = "cigar2" - max_smoketime = 7200 - smoketime = 7200 - chem_volume = 30 - nicotine_amt = 10 - -/obj/item/trash/cigbutt - name = "cigarette butt" - desc = "A manky old cigarette butt." - icon = 'icons/inventory/face/item.dmi' - icon_state = "cigbutt" - randpixel = 10 - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - throwforce = 1 - -/obj/item/trash/cigbutt/Initialize() - . = ..() - randpixel_xy() - transform = turn(transform,rand(0,360)) - -/obj/item/trash/cigbutt/cigarbutt - name = "cigar butt" - desc = "A manky old cigar butt." - icon_state = "cigarbutt" - -/obj/item/clothing/mask/smokable/cigarette/cigar/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - - user.update_inv_wear_mask(0) - user.update_inv_l_hand(0) - user.update_inv_r_hand(1) - -///////////////// -//SMOKING PIPES// -///////////////// -/obj/item/clothing/mask/smokable/pipe - name = "smoking pipe" - desc = "A pipe, for smoking. Made of fine, stained cherry wood." - description_fluff = "ClassiCo Accessories and Haberdashers, originating out of Mars, \ - claim to produce products 'for the modern gentlefolk'. Most of their items are high-end \ - and expensive, but they pledge to back their prices up with quality, and usually do." - icon_state = "pipe" - item_state = "pipe" - smoketime = 0 - chem_volume = 50 - matchmes = "USER lights their NAME with their FLAME." - lightermes = "USER manages to light their NAME with FLAME." - zippomes = "With much care, USER lights their NAME with their FLAME." - weldermes = "USER recklessly lights NAME with FLAME." - ignitermes = "USER fiddles with FLAME, and manages to light their NAME with the power of science." - is_pipe = 1 - -/obj/item/clothing/mask/smokable/pipe/New() - ..() - name = "empty [initial(name)]" - -/obj/item/clothing/mask/smokable/pipe/attack_self(mob/user as mob) - if(lit == 1) - if(user.a_intent == I_HURT) - user.visible_message("[user] empties the lit [src] on the floor!.") - playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) - die(1) - else - user.visible_message("[user] puts out \the [src].") - quench() - -/obj/item/clothing/mask/smokable/pipe/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/melee/energy/sword)) - return - - ..() - - if (istype(W, /obj/item/weapon/reagent_containers/food/snacks)) - var/obj/item/weapon/reagent_containers/food/snacks/grown/G = W - if (!G.dry) - to_chat(user, "[G] must be dried before you stuff it into [src].") - return - if (smoketime) - to_chat(user, "[src] is already packed.") - return - max_smoketime = 1000 - smoketime = 1000 - if(G.reagents) - G.reagents.trans_to_obj(src, G.reagents.total_volume) - name = "[G.name]-packed [initial(name)]" - qdel(G) - - else if(istype(W, /obj/item/weapon/flame/lighter)) - var/obj/item/weapon/flame/lighter/L = W - if(L.lit) - light("[user] manages to light their [name] with [W].") - - else if(istype(W, /obj/item/weapon/flame/match)) - var/obj/item/weapon/flame/match/M = W - if(M.lit) - light("[user] lights their [name] with their [W].") - - else if(istype(W, /obj/item/device/assembly/igniter)) - light("[user] fiddles with [W], and manages to light their [name] with the power of science.") - - user.update_inv_wear_mask(0) - user.update_inv_l_hand(0) - user.update_inv_r_hand(1) - -/obj/item/clothing/mask/smokable/pipe/cobpipe - name = "corn cob pipe" - desc = "A nicotine delivery system popularized by folksy backwoodsmen, kept popular in the modern age and beyond by space hipsters." - icon_state = "cobpipe" - item_state = "cobpipe" - chem_volume = 35 - -/obj/item/clothing/mask/smokable/pipe/bonepipe - name = "Europan bone pipe" - desc = "A smoking pipe made out of the bones of the Europan bone whale." - description_fluff = "While most commonly associated with bone charms, bones from various sea creatures on Europa are used in a \ - variety of goods, such as this smoking pipe. While smoking in submarines is often an uncommon occurrence, due to a lack of \ - available air or space, these pipes are a common sight in the many stations of Europa. Higher-quality pipes typically have \ - scenes etched into their bones, and can tell the story of their owner's time on Europa." - icon_state = "bonepipe" - item_state = "bonepipe" - chem_volume = 30 - -/////////////// -//CUSTOM CIGS// -/////////////// -//and by custom cigs i mean craftable joints. smoke weed every day - -/obj/item/clothing/mask/smokable/cigarette/joint - name = "joint" - desc = "A joint lovingly rolled and crafted with care. Blaze it." - icon_state = "joint" - max_smoketime = 400 - smoketime = 400 - chem_volume = 25 - -/obj/item/clothing/mask/smokable/cigarette/joint/blunt - name = "blunt" - desc = "A blunt lovingly rolled and crafted with care. Blaze it." - icon_state = "cigar" - max_smoketime = 500 - smoketime = 500 - nicotine_amt = 4 - chem_volume = 45 - -/obj/item/weapon/reagent_containers/rollingpaper - name = "rolling paper" - desc = "A small, thin piece of easily flammable paper, commonly used for rolling and smoking various dried plants." - description_fluff = "The legalization of certain substances propelled the sale of rolling \ - papers through the roof. Now almost every Trans-stellar produces a variety, often of questionable quality." - icon = 'icons/obj/cigarettes.dmi' - icon_state = "cig paper" - volume = 25 - var/obj/item/clothing/mask/smokable/cigarette/crafted_type = /obj/item/clothing/mask/smokable/cigarette/joint - -/obj/item/weapon/reagent_containers/rollingpaper/blunt - name = "blunt wrap" - desc = "A small piece of easily flammable paper similar to that which encases cigars. It's made out of tobacco, bigger than a standard rolling paper, and will last longer." - icon_state = "blunt paper" - volume = 45 - crafted_type = /obj/item/clothing/mask/smokable/cigarette/joint/blunt - -/obj/item/weapon/reagent_containers/rollingpaper/attackby(obj/item/weapon/W as obj, mob/user as mob) - if (istype(W, /obj/item/weapon/reagent_containers/food/snacks)) - var/obj/item/weapon/reagent_containers/food/snacks/grown/G = W - if (!G.dry) //This prevents people from just stuffing cheeseburgers into their joint - to_chat(user, "[G.name] must be dried before you add it to [src].") - return - if (G.reagents.total_volume + src.reagents.total_volume > src.reagents.maximum_volume) //Check that we don't have too much already in the paper before adding things - to_chat(user, "The [src] is too full to add [G.name].") - return - if (src.reagents.total_volume == 0) - if (istype(src, /obj/item/weapon/reagent_containers/rollingpaper/blunt)) //update the icon if this is the first thing we're adding to the paper - src.icon_state = "blunt_full" - else - src.icon_state = "paper_full" - to_chat(user, "You add the [G.name] to the [src.name].") - src.add_fingerprint(user) - if(G.reagents) - G.reagents.trans_to_obj(src, G.reagents.total_volume) //adds the reagents from the plant into the paper - user.drop_from_inventory(G) - qdel(G) - -/obj/item/weapon/reagent_containers/rollingpaper/attack_self(mob/living/user) - if(!src.reagents) //don't roll an empty joint - to_chat(user, "There is nothing in [src]. Add something to it first.") - return - var/obj/item/clothing/mask/smokable/cigarette/J = new crafted_type() - to_chat(user,"You roll the [src] into a blunt!") - J.add_fingerprint(user) - if(src.reagents) - src.reagents.trans_to_obj(J, src.reagents.total_volume) - user.drop_from_inventory(src) - user.put_in_hands(J) - qdel(src) - -///////// -//CHEAP// -///////// -/obj/item/weapon/flame/lighter - name = "cheap lighter" - desc = "A cheap-as-free lighter." - description_fluff = "The 'hand-made in Altair' sticker underneath is a charming way of \ - saying 'Made with prison labour'. It's no wonder the company can sell these things so cheap." - icon = 'icons/obj/lighters.dmi' - icon_state = "lighter" - item_state = "lighter" - w_class = ITEMSIZE_TINY - throwforce = 4 - slot_flags = SLOT_BELT - attack_verb = list("burnt", "singed") - var/base_state - /// Sounds - var/activation_sound = 'sound/items/lighter_on.ogg' - var/deactivation_sound = 'sound/items/lighter_off.ogg' - /// Color of the flame and how big the flame is (pulled from Welder code) - var/flame_color = "#FF9933" - var/flame_intensity = 2 - /// Color List - var/random_color = FALSE - var/available_colors = list(COLOR_ASSEMBLY_BLACK, - COLOR_ASSEMBLY_BGRAY, - COLOR_ASSEMBLY_WHITE, - COLOR_ASSEMBLY_RED, - COLOR_ASSEMBLY_ORANGE, - COLOR_ASSEMBLY_BEIGE, - COLOR_ASSEMBLY_BROWN, - COLOR_ASSEMBLY_GOLD, - COLOR_ASSEMBLY_YELLOW, - COLOR_ASSEMBLY_GURKHA, - COLOR_ASSEMBLY_LGREEN, - COLOR_ASSEMBLY_GREEN, - COLOR_ASSEMBLY_LBLUE, - COLOR_ASSEMBLY_BLUE, - COLOR_ASSEMBLY_PURPLE, - COLOR_ASSEMBLY_HOT_PINK) - -// TODO: Remove this path from POIs and loose maps (it's no longer needed) -/obj/item/weapon/flame/lighter/random - -// Randomizes Cheap Lighters on Spawn -/obj/item/weapon/flame/lighter/Initialize() - . = ..() - var/image/I = image(icon, "lighter-[pick("trans","tall","matte")]") - I.color = pick(available_colors) - add_overlay(I) - -/obj/item/weapon/flame/lighter/attack_self(mob/living/user) - if(!lit) - lit = 1 - icon_state = "lighteron" - playsound(src, activation_sound, 75, 1) - user.visible_message("After a few attempts, [user] manages to light the [src].") - - set_light(2, 0.5, "#FF9933") - START_PROCESSING(SSobj, src) - update_icon() - else - lit = 0 - icon_state = "lighter" - playsound(src, deactivation_sound, 75, 1) - user.visible_message("[user] quietly shuts off the [src].") - - set_light(0) - STOP_PROCESSING(SSobj, src) - update_icon() - return - -/obj/item/weapon/flame/lighter/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - if(!istype(M, /mob)) - return - - if(lit == 1) - M.IgniteMob() - add_attack_logs(user,M,"Lit on fire with [src]") - - if(istype(M.wear_mask, /obj/item/clothing/mask/smokable/cigarette) && user.zone_sel.selecting == O_MOUTH && lit) - var/obj/item/clothing/mask/smokable/cigarette/cig = M.wear_mask - if(M == user) - cig.attackby(src, user) - else - if(istype(src, /obj/item/weapon/flame/lighter/zippo)) - cig.light("[user] whips the [name] out and holds it for [M].") - else - cig.light("[user] holds the [name] out for [M], and lights the [cig.name].") - else - ..() - -/obj/item/weapon/flame/lighter/process() - var/turf/location = get_turf(src) - if(location) - location.hotspot_expose(700, 5) - return - -///////// -//ZIPPO// -///////// -/obj/item/weapon/flame/lighter/zippo - name = "\improper Zippo lighter" - desc = "The zippo." - description_fluff = "Still going after all these years." - icon_state = "zippo" - item_state = "zippo" - activation_sound = 'sound/items/zippo_on.ogg' - deactivation_sound = 'sound/items/zippo_off.ogg' - -/obj/item/weapon/flame/lighter/zippo/Initialize() - . = ..() - cut_overlays() //Prevents the Cheap Lighter overlay from appearing on this - -/obj/item/weapon/flame/lighter/zippo/attack_self(mob/living/user) - if(!base_state) - base_state = icon_state - if(!lit) - lit = 1 - icon_state = "[base_state]on" - item_state = "[base_state]on" - playsound(src, activation_sound, 75, 1) - user.visible_message("Without even breaking stride, [user] flips open and lights [src] in one smooth movement.") - - set_light(2, 0.5, "#FF9933") - START_PROCESSING(SSobj, src) - else - lit = 0 - icon_state = "[base_state]" - item_state = "[base_state]" - playsound(src, deactivation_sound, 75, 1) - user.visible_message("You hear a quiet click, as [user] shuts off [src] without even looking at what they're doing.") - - set_light(0) - STOP_PROCESSING(SSobj, src) - return - -//Here we add Zippo skins. - -/obj/item/weapon/flame/lighter/zippo/black - name = "\improper holy Zippo lighter" - desc = "Only in regards to Christianity, that is." - icon_state = "blackzippo" - -/obj/item/weapon/flame/lighter/zippo/blue - name = "\improper blue Zippo lighter" - icon_state = "bluezippo" - -/obj/item/weapon/flame/lighter/zippo/engraved - name = "\improper engraved Zippo lighter" - icon_state = "engravedzippo" - item_state = "zippo" - -/obj/item/weapon/flame/lighter/zippo/gold - name = "\improper golden Zippo lighter" - icon_state = "goldzippo" - -/obj/item/weapon/flame/lighter/zippo/moff - name = "\improper moth Zippo lighter" - desc = "Too cute to be a Tymisian." - icon_state = "moffzippo" - -/obj/item/weapon/flame/lighter/zippo/red - name = "\improper red Zippo lighter" - icon_state = "redzippo" - -/obj/item/weapon/flame/lighter/zippo/ironic - name = "\improper ironic Zippo lighter" - desc = "What a quiant idea." - icon_state = "ironiczippo" - -/obj/item/weapon/flame/lighter/zippo/capitalist - name = "\improper capitalist Zippo lighter" - desc = "Made of gold and obsidian, this is truly not worth however much you spent on it." - icon_state = "cappiezippo" - -/obj/item/weapon/flame/lighter/zippo/communist - name = "\improper communist Zippo lighter" - desc = "All you need to spark a revolution." - icon_state = "commiezippo" - -/obj/item/weapon/flame/lighter/zippo/royal - name = "\improper royal Zippo lighter" - desc = "An incredibly fancy lighter, gilded and covered in the color of royalty." - icon_state = "royalzippo" - -/obj/item/weapon/flame/lighter/zippo/gonzo - name = "\improper Gonzo Zippo lighter" - desc = "A lighter with the iconic Gonzo fist painted on it." - icon_state = "gonzozippo" - -/obj/item/weapon/flame/lighter/zippo/rainbow - name = "\improper rainbow Zippo lighter" - icon_state = "rainbowzippo" - -/obj/item/weapon/flame/lighter/zippo/skull - name = "\improper badass Zippo lighter" - desc = "An absolutely badass zippo lighter. Just look at that skull!" +/* +CONTAINS: +MATCHES +CIGARETTES +CIGARS +SMOKING PIPES +CUSTOM CIGS +CHEAP LIGHTERS +ZIPPO + +CIGARETTE PACKETS ARE IN FANCY.DM +*/ + +//For anything that can light stuff on fire +/obj/item/weapon/flame + var/lit = 0 + +/obj/item/weapon/flame/is_hot() + return lit + +/////////// +//MATCHES// +/////////// +/obj/item/weapon/flame/match + name = "match" + desc = "A simple match stick, used for lighting fine smokables." + icon = 'icons/obj/cigarettes.dmi' + icon_state = "match_unlit" + var/burnt = 0 + var/smoketime = 5 + w_class = ITEMSIZE_TINY + origin_tech = list(TECH_MATERIAL = 1) + slot_flags = SLOT_EARS + attack_verb = list("burnt", "singed") + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' + +/obj/item/weapon/flame/match/process() + if(isliving(loc)) + var/mob/living/M = loc + M.IgniteMob() + var/turf/location = get_turf(src) + smoketime-- + if(smoketime < 1) + burn_out() + return + if(location) + location.hotspot_expose(700, 5) + return + +/obj/item/weapon/flame/match/dropped(mob/user as mob) + //If dropped, put ourselves out + //not before lighting up the turf we land on, though. + if(lit) + spawn(0) + var/turf/location = src.loc + if(istype(location)) + location.hotspot_expose(700, 5) + burn_out() + return ..() + +/obj/item/weapon/flame/match/proc/light(var/mob/user) + playsound(src, 'sound/items/cigs_lighters/matchstick_lit.ogg', 25, 0, -1) + lit = 1 + damtype = "burn" + icon_state = "match_lit" + name = "burning match" + desc = "A match. This one is presently on fire." + START_PROCESSING(SSobj, src) + +/obj/item/weapon/flame/match/proc/burn_out() + lit = 0 + burnt = 1 + damtype = "brute" + icon_state = "match_burnt" + item_state = "cigoff" + name = "burnt match" + desc = "A match. This one has seen better days." + STOP_PROCESSING(SSobj, src) + +////////////////// +//FINE SMOKABLES// +////////////////// +/obj/item/clothing/mask/smokable + name = "smokable item" + desc = "You're not sure what this is. You should probably ahelp it." + body_parts_covered = 0 + var/lit = 0 + var/icon_on + var/type_butt = null + var/chem_volume = 0 + var/max_smoketime = 0 //Related to sprites + var/smoketime = 0 + var/is_pipe = 0 //Prevents a runtime with pipes + var/matchmes = "USER lights NAME with FLAME" + var/lightermes = "USER lights NAME with FLAME" + var/zippomes = "USER lights NAME with FLAME" + var/weldermes = "USER lights NAME with FLAME" + var/ignitermes = "USER lights NAME with FLAME" + var/brand + blood_sprite_state = null //Can't bloody these + drop_sound = 'sound/items/cigs_lighters/cig_snuff.ogg' + +/obj/item/clothing/mask/smokable/Initialize() + . = ..() + flags |= NOREACT // so it doesn't react until you light it + create_reagents(chem_volume) // making the cigarrete a chemical holder with a maximum volume of 15 + if(smoketime && !max_smoketime) + max_smoketime = smoketime + +/obj/item/clothing/mask/smokable/proc/smoke(amount) + if(smoketime > max_smoketime) + smoketime = max_smoketime + smoketime -= amount + if(reagents && reagents.total_volume) // check if it has any reagents at all + if(ishuman(loc)) + var/mob/living/carbon/human/C = loc + if (src == C.wear_mask && C.check_has_mouth()) // if it's in the human/monkey mouth, transfer reagents to the mob + reagents.trans_to_mob(C, amount, CHEM_INGEST, 1.5) // I don't predict significant balance issues by letting blunts actually WORK. + else // else just remove some of the reagents + reagents.remove_any(REM) + +/obj/item/clothing/mask/smokable/process() + var/turf/location = get_turf(src) + smoke(1) + if(smoketime < 1) + die() + return + if(location) + location.hotspot_expose(700, 5) + +/obj/item/clothing/mask/smokable/update_icon() + if(lit) + icon_state = "[initial(icon_state)]_on" + item_state = "[initial(item_state)]_on" + else if(smoketime < max_smoketime) + if(is_pipe) + icon_state = initial(icon_state) + item_state = initial(item_state) + else + icon_state = "[initial(icon_state)]_burnt" + item_state = "[initial(item_state)]_burnt" + if(ismob(loc)) + var/mob/living/M = loc + M.update_inv_wear_mask(0) + M.update_inv_l_hand(0) + M.update_inv_r_hand(1) + ..() + +/obj/item/clothing/mask/smokable/examine(mob/user) + . = ..() + + if(!is_pipe) + var/smoke_percent = round((smoketime / max_smoketime) * 100) + switch(smoke_percent) + if(90 to INFINITY) + . += "[src] is still fresh." + if(60 to 90) + . += "[src] has a good amount of burn time remaining." + if(30 to 60) + . += "[src] is about half finished." + if(10 to 30) + . += "[src] is starting to burn low." + else + . += "[src] is nearly burnt out!" + +/obj/item/clothing/mask/smokable/proc/light(var/flavor_text = "[usr] lights the [name].") + if(!src.lit) + src.lit = 1 + playsound(src, 'sound/items/cigs_lighters/cig_light.ogg', 75, 1, -1) + damtype = "fire" + if(reagents.get_reagent_amount("phoron")) // the phoron explodes when exposed to fire + var/datum/effect/effect/system/reagents_explosion/e = new() + e.set_up(round(reagents.get_reagent_amount("phoron") / 2.5, 1), get_turf(src), 0, 0) + e.start() + qdel(src) + return + if(reagents.get_reagent_amount("fuel")) // the fuel explodes, too, but much less violently + var/datum/effect/effect/system/reagents_explosion/e = new() + e.set_up(round(reagents.get_reagent_amount("fuel") / 5, 1), get_turf(src), 0, 0) + e.start() + qdel(src) + return + flags &= ~NOREACT // allowing reagents to react after being lit + reagents.handle_reactions() + var/turf/T = get_turf(src) + T.visible_message(flavor_text) + update_icon() + set_light(2, 0.25, "#E38F46") + START_PROCESSING(SSobj, src) + +/obj/item/clothing/mask/smokable/proc/die(var/nomessage = 0) + var/turf/T = get_turf(src) + set_light(0) + playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) + STOP_PROCESSING(SSobj, src) + if (type_butt) + var/obj/item/butt = new type_butt(T) + transfer_fingerprints_to(butt) + if(brand) + butt.desc += " This one is \a [brand]." + if(ismob(loc)) + var/mob/living/M = loc + if (!nomessage) + to_chat(M, "Your [name] goes out.") + M.remove_from_mob(src) //un-equip it so the overlays can update + M.update_inv_wear_mask(0) + qdel(src) + else + new /obj/effect/decal/cleanable/ash(T) + if(ismob(loc)) + var/mob/living/M = loc + if (!nomessage) + to_chat(M, "Your [name] goes out, and you empty the ash.") + playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) + lit = 0 + icon_state = initial(icon_state) + item_state = initial(item_state) + M.update_inv_wear_mask(0) + smoketime = 0 + reagents.clear_reagents() + name = "empty [initial(name)]" + +/obj/item/clothing/mask/smokable/proc/quench() + lit = 0 + STOP_PROCESSING(SSobj, src) + update_icon() + +/obj/item/clothing/mask/smokable/attack(mob/living/carbon/human/H, mob/user, def_zone) + if(lit && H == user && istype(H)) + var/obj/item/blocked = H.check_mouth_coverage() + if(blocked) + to_chat(H, "\The [blocked] is in the way!") + return 1 + to_chat(H, "You take a drag on your [name].") + playsound(src, 'sound/items/cigs_lighters/inhale.ogg', 50, 0, -1) + smoke(5) + return 1 + return ..() + +/obj/item/clothing/mask/smokable/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + if(W.is_hot()) + var/text = matchmes + if(istype(W, /obj/item/weapon/flame/match)) + text = matchmes + else if(istype(W, /obj/item/weapon/flame/lighter/zippo)) + text = zippomes + else if(istype(W, /obj/item/weapon/flame/lighter)) + text = lightermes + else if(istype(W, /obj/item/weapon/weldingtool)) + text = weldermes + else if(istype(W, /obj/item/device/assembly/igniter)) + text = ignitermes + text = replacetext(text, "USER", "[user]") + text = replacetext(text, "NAME", "[name]") + text = replacetext(text, "FLAME", "[W.name]") + light(text) + +/obj/item/clothing/mask/smokable/attack(var/mob/living/M, var/mob/living/user, def_zone) + if(istype(M) && M.on_fire) + user.do_attack_animation(M) + light("[user] coldly lights the [name] with the burning body of [M].") + return 1 + else + return ..() + +/obj/item/clothing/mask/smokable/water_act(amount) + if(amount >= 5) + quench() + +/obj/item/clothing/mask/smokable/cigarette + name = "cigarette" + desc = "A roll of tobacco and nicotine." + icon_state = "cig" + item_state = "cig" + throw_speed = 0.5 + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS | SLOT_MASK + attack_verb = list("burnt", "singed") + type_butt = /obj/item/trash/cigbutt + chem_volume = 15 + max_smoketime = 300 + smoketime = 300 + var/nicotine_amt = 2 + matchmes = "USER lights their NAME with their FLAME." + lightermes = "USER manages to light their NAME with FLAME." + zippomes = "With a flick of their wrist, USER lights their NAME with their FLAME." + weldermes = "USER casually lights the NAME with FLAME." + ignitermes = "USER fiddles with FLAME, and manages to light their NAME." + +/obj/item/clothing/mask/smokable/cigarette/Initialize() + . = ..() + if(nicotine_amt) + reagents.add_reagent("nicotine", nicotine_amt) + +/obj/item/clothing/mask/smokable/cigarette/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + + if(istype(W, /obj/item/weapon/melee/energy/sword)) + var/obj/item/weapon/melee/energy/sword/S = W + if(S.active) + light("[user] swings their [W], barely missing their nose. They light their [name] in the process.") + + return + +/obj/item/clothing/mask/smokable/cigarette/afterattack(obj/item/weapon/reagent_containers/glass/glass, mob/user as mob, proximity) + ..() + if(!proximity) + return + if(istype(glass)) //you can dip cigarettes into beakers + var/transfered = glass.reagents.trans_to_obj(src, chem_volume) + if(transfered) //if reagents were transfered, show the message + to_chat(user, "You dip \the [src] into \the [glass].") + else //if not, either the beaker was empty, or the cigarette was full + if(!glass.reagents.total_volume) + to_chat(user, "[glass] is empty.") + else + to_chat(user, "[src] is full.") + +/obj/item/clothing/mask/smokable/cigarette/attack_self(mob/user as mob) + if(lit == 1) + if(user.a_intent == I_HURT) + user.visible_message("[user] drops and treads on the lit [src], putting it out instantly.") + playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) + die(1) + else + user.visible_message("[user] puts out \the [src].") + quench() + return ..() + +//////////// +// CIGARS // +//////////// +/obj/item/clothing/mask/smokable/cigarette/cigar + name = "premium cigar" + desc = "A brown roll of tobacco and... well, you're not quite sure. This thing's huge!" + description_fluff = "While the label does say that this is a 'premium cigar', it \ + really cannot match other types of cigars on the market. Is it a quality \ + cigarette? Perhaps. Was it hand-made with care? No." + icon_state = "cigar2" + type_butt = /obj/item/trash/cigbutt/cigarbutt + throw_speed = 0.5 + item_state = "cigar" + max_smoketime = 1500 + smoketime = 1500 + chem_volume = 20 + nicotine_amt = 4 + matchmes = "USER lights their NAME with their FLAME." + lightermes = "USER manages to offend their NAME by lighting it with FLAME." + zippomes = "With a flick of their wrist, USER lights their NAME with their FLAME." + weldermes = "USER insults NAME by lighting it with FLAME." + ignitermes = "USER fiddles with FLAME, and manages to light their NAME with the power of science." + +/obj/item/clothing/mask/smokable/cigarette/cigar/cohiba + name = "\improper Cohiba Robusto cigar" + desc = "There's little more you could want from a cigar." + description_fluff = "Cohiba has been a popular cigar company for centuries. \ + They are still based out of Cuba and refuse to expand and therefore have a very \ + limited quantity, making their cigars coveted all through known space. Robusto \ + is one of their most popular shapes of cigars." + icon_state = "cigar2" + nicotine_amt = 7 + +/obj/item/clothing/mask/smokable/cigarette/cigar/havana + name = "premium Havanian cigar" + desc = "Save these for the fancy-pantses at the next CentCom black tie reception. \ + You can't blow the smoke from such majestic stogies in just anyone's face." + description_fluff = "'Havanian' is an umbrella term for any cigar made in the \ + typical handmade style of Cuba. This particular cigar is from Gilthari's cigar \ + manufacturers and produced galaxy-wide. While this way of making quality cigars \ + has become slightly bastardized over the years, overall quality has remained \ + relatively the same, even if there is a large quantity of 'Havanian' cigars." + icon_state = "cigar2" + max_smoketime = 7200 + smoketime = 7200 + chem_volume = 30 + nicotine_amt = 10 + +/obj/item/trash/cigbutt + name = "cigarette butt" + desc = "A manky old cigarette butt." + icon = 'icons/inventory/face/item.dmi' + icon_state = "cigbutt" + randpixel = 10 + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + throwforce = 1 + +/obj/item/trash/cigbutt/Initialize() + . = ..() + randpixel_xy() + transform = turn(transform,rand(0,360)) + +/obj/item/trash/cigbutt/cigarbutt + name = "cigar butt" + desc = "A manky old cigar butt." + icon_state = "cigarbutt" + +/obj/item/clothing/mask/smokable/cigarette/cigar/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + + user.update_inv_wear_mask(0) + user.update_inv_l_hand(0) + user.update_inv_r_hand(1) + +///////////////// +//SMOKING PIPES// +///////////////// +/obj/item/clothing/mask/smokable/pipe + name = "smoking pipe" + desc = "A pipe, for smoking. Made of fine, stained cherry wood." + description_fluff = "ClassiCo Accessories and Haberdashers, originating out of Mars, \ + claim to produce products 'for the modern gentlefolk'. Most of their items are high-end \ + and expensive, but they pledge to back their prices up with quality, and usually do." + icon_state = "pipe" + item_state = "pipe" + smoketime = 0 + chem_volume = 50 + matchmes = "USER lights their NAME with their FLAME." + lightermes = "USER manages to light their NAME with FLAME." + zippomes = "With much care, USER lights their NAME with their FLAME." + weldermes = "USER recklessly lights NAME with FLAME." + ignitermes = "USER fiddles with FLAME, and manages to light their NAME with the power of science." + is_pipe = 1 + +/obj/item/clothing/mask/smokable/pipe/New() + ..() + name = "empty [initial(name)]" + +/obj/item/clothing/mask/smokable/pipe/attack_self(mob/user as mob) + if(lit == 1) + if(user.a_intent == I_HURT) + user.visible_message("[user] empties the lit [src] on the floor!.") + playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) + die(1) + else + user.visible_message("[user] puts out \the [src].") + quench() + +/obj/item/clothing/mask/smokable/pipe/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/melee/energy/sword)) + return + + ..() + + if (istype(W, /obj/item/weapon/reagent_containers/food/snacks)) + var/obj/item/weapon/reagent_containers/food/snacks/grown/G = W + if (!G.dry) + to_chat(user, "[G] must be dried before you stuff it into [src].") + return + if (smoketime) + to_chat(user, "[src] is already packed.") + return + max_smoketime = 1000 + smoketime = 1000 + if(G.reagents) + G.reagents.trans_to_obj(src, G.reagents.total_volume) + name = "[G.name]-packed [initial(name)]" + qdel(G) + + else if(istype(W, /obj/item/weapon/flame/lighter)) + var/obj/item/weapon/flame/lighter/L = W + if(L.lit) + light("[user] manages to light their [name] with [W].") + + else if(istype(W, /obj/item/weapon/flame/match)) + var/obj/item/weapon/flame/match/M = W + if(M.lit) + light("[user] lights their [name] with their [W].") + + else if(istype(W, /obj/item/device/assembly/igniter)) + light("[user] fiddles with [W], and manages to light their [name] with the power of science.") + + user.update_inv_wear_mask(0) + user.update_inv_l_hand(0) + user.update_inv_r_hand(1) + +/obj/item/clothing/mask/smokable/pipe/cobpipe + name = "corn cob pipe" + desc = "A nicotine delivery system popularized by folksy backwoodsmen, kept popular in the modern age and beyond by space hipsters." + icon_state = "cobpipe" + item_state = "cobpipe" + chem_volume = 35 + +/obj/item/clothing/mask/smokable/pipe/bonepipe + name = "Europan bone pipe" + desc = "A smoking pipe made out of the bones of the Europan bone whale." + description_fluff = "While most commonly associated with bone charms, bones from various sea creatures on Europa are used in a \ + variety of goods, such as this smoking pipe. While smoking in submarines is often an uncommon occurrence, due to a lack of \ + available air or space, these pipes are a common sight in the many stations of Europa. Higher-quality pipes typically have \ + scenes etched into their bones, and can tell the story of their owner's time on Europa." + icon_state = "bonepipe" + item_state = "bonepipe" + chem_volume = 30 + +/////////////// +//CUSTOM CIGS// +/////////////// +//and by custom cigs i mean craftable joints. smoke weed every day + +/obj/item/clothing/mask/smokable/cigarette/joint + name = "joint" + desc = "A joint lovingly rolled and crafted with care. Blaze it." + icon_state = "joint" + max_smoketime = 400 + smoketime = 400 + chem_volume = 25 + +/obj/item/clothing/mask/smokable/cigarette/joint/blunt + name = "blunt" + desc = "A blunt lovingly rolled and crafted with care. Blaze it." + icon_state = "cigar" + max_smoketime = 500 + smoketime = 500 + nicotine_amt = 4 + chem_volume = 45 + +/obj/item/weapon/reagent_containers/rollingpaper + name = "rolling paper" + desc = "A small, thin piece of easily flammable paper, commonly used for rolling and smoking various dried plants." + description_fluff = "The legalization of certain substances propelled the sale of rolling \ + papers through the roof. Now almost every Trans-stellar produces a variety, often of questionable quality." + icon = 'icons/obj/cigarettes.dmi' + icon_state = "cig paper" + volume = 25 + var/obj/item/clothing/mask/smokable/cigarette/crafted_type = /obj/item/clothing/mask/smokable/cigarette/joint + +/obj/item/weapon/reagent_containers/rollingpaper/blunt + name = "blunt wrap" + desc = "A small piece of easily flammable paper similar to that which encases cigars. It's made out of tobacco, bigger than a standard rolling paper, and will last longer." + icon_state = "blunt paper" + volume = 45 + crafted_type = /obj/item/clothing/mask/smokable/cigarette/joint/blunt + +/obj/item/weapon/reagent_containers/rollingpaper/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/reagent_containers/food/snacks)) + var/obj/item/weapon/reagent_containers/food/snacks/grown/G = W + if (!G.dry) //This prevents people from just stuffing cheeseburgers into their joint + to_chat(user, "[G.name] must be dried before you add it to [src].") + return + if (G.reagents.total_volume + src.reagents.total_volume > src.reagents.maximum_volume) //Check that we don't have too much already in the paper before adding things + to_chat(user, "The [src] is too full to add [G.name].") + return + if (src.reagents.total_volume == 0) + if (istype(src, /obj/item/weapon/reagent_containers/rollingpaper/blunt)) //update the icon if this is the first thing we're adding to the paper + src.icon_state = "blunt_full" + else + src.icon_state = "paper_full" + to_chat(user, "You add the [G.name] to the [src.name].") + src.add_fingerprint(user) + if(G.reagents) + G.reagents.trans_to_obj(src, G.reagents.total_volume) //adds the reagents from the plant into the paper + user.drop_from_inventory(G) + qdel(G) + +/obj/item/weapon/reagent_containers/rollingpaper/attack_self(mob/living/user) + if(!src.reagents) //don't roll an empty joint + to_chat(user, "There is nothing in [src]. Add something to it first.") + return + var/obj/item/clothing/mask/smokable/cigarette/J = new crafted_type() + to_chat(user,"You roll the [src] into a blunt!") + J.add_fingerprint(user) + if(src.reagents) + src.reagents.trans_to_obj(J, src.reagents.total_volume) + user.drop_from_inventory(src) + user.put_in_hands(J) + qdel(src) + +///////// +//CHEAP// +///////// +/obj/item/weapon/flame/lighter + name = "cheap lighter" + desc = "A cheap-as-free lighter." + description_fluff = "The 'hand-made in Altair' sticker underneath is a charming way of \ + saying 'Made with prison labour'. It's no wonder the company can sell these things so cheap." + icon = 'icons/obj/lighters.dmi' + icon_state = "lighter" + item_state = "lighter" + w_class = ITEMSIZE_TINY + throwforce = 4 + slot_flags = SLOT_BELT + attack_verb = list("burnt", "singed") + var/base_state + /// Sounds + var/activation_sound = 'sound/items/lighter_on.ogg' + var/deactivation_sound = 'sound/items/lighter_off.ogg' + /// Color of the flame and how big the flame is (pulled from Welder code) + var/flame_color = "#FF9933" + var/flame_intensity = 2 + /// Color List + var/random_color = FALSE + var/available_colors = list(COLOR_ASSEMBLY_BLACK, + COLOR_ASSEMBLY_BGRAY, + COLOR_ASSEMBLY_WHITE, + COLOR_ASSEMBLY_RED, + COLOR_ASSEMBLY_ORANGE, + COLOR_ASSEMBLY_BEIGE, + COLOR_ASSEMBLY_BROWN, + COLOR_ASSEMBLY_GOLD, + COLOR_ASSEMBLY_YELLOW, + COLOR_ASSEMBLY_GURKHA, + COLOR_ASSEMBLY_LGREEN, + COLOR_ASSEMBLY_GREEN, + COLOR_ASSEMBLY_LBLUE, + COLOR_ASSEMBLY_BLUE, + COLOR_ASSEMBLY_PURPLE, + COLOR_ASSEMBLY_HOT_PINK) + +// TODO: Remove this path from POIs and loose maps (it's no longer needed) +/obj/item/weapon/flame/lighter/random + +// Randomizes Cheap Lighters on Spawn +/obj/item/weapon/flame/lighter/Initialize() + . = ..() + var/image/I = image(icon, "lighter-[pick("trans","tall","matte")]") + I.color = pick(available_colors) + add_overlay(I) + +/obj/item/weapon/flame/lighter/attack_self(mob/living/user) + if(!lit) + lit = 1 + icon_state = "lighteron" + playsound(src, activation_sound, 75, 1) + user.visible_message("After a few attempts, [user] manages to light the [src].") + + set_light(2, 0.5, "#FF9933") + START_PROCESSING(SSobj, src) + update_icon() + else + lit = 0 + icon_state = "lighter" + playsound(src, deactivation_sound, 75, 1) + user.visible_message("[user] quietly shuts off the [src].") + + set_light(0) + STOP_PROCESSING(SSobj, src) + update_icon() + return + +/obj/item/weapon/flame/lighter/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + if(!istype(M, /mob)) + return + + if(lit == 1) + M.IgniteMob() + add_attack_logs(user,M,"Lit on fire with [src]") + + if(istype(M.wear_mask, /obj/item/clothing/mask/smokable/cigarette) && user.zone_sel.selecting == O_MOUTH && lit) + var/obj/item/clothing/mask/smokable/cigarette/cig = M.wear_mask + if(M == user) + cig.attackby(src, user) + else + if(istype(src, /obj/item/weapon/flame/lighter/zippo)) + cig.light("[user] whips the [name] out and holds it for [M].") + else + cig.light("[user] holds the [name] out for [M], and lights the [cig.name].") + else + ..() + +/obj/item/weapon/flame/lighter/process() + var/turf/location = get_turf(src) + if(location) + location.hotspot_expose(700, 5) + return + +///////// +//ZIPPO// +///////// +/obj/item/weapon/flame/lighter/zippo + name = "\improper Zippo lighter" + desc = "The zippo." + description_fluff = "Still going after all these years." + icon_state = "zippo" + item_state = "zippo" + activation_sound = 'sound/items/zippo_on.ogg' + deactivation_sound = 'sound/items/zippo_off.ogg' + +/obj/item/weapon/flame/lighter/zippo/Initialize() + . = ..() + cut_overlays() //Prevents the Cheap Lighter overlay from appearing on this + +/obj/item/weapon/flame/lighter/zippo/attack_self(mob/living/user) + if(!base_state) + base_state = icon_state + if(!lit) + lit = 1 + icon_state = "[base_state]on" + item_state = "[base_state]on" + playsound(src, activation_sound, 75, 1) + user.visible_message("Without even breaking stride, [user] flips open and lights [src] in one smooth movement.") + + set_light(2, 0.5, "#FF9933") + START_PROCESSING(SSobj, src) + else + lit = 0 + icon_state = "[base_state]" + item_state = "[base_state]" + playsound(src, deactivation_sound, 75, 1) + user.visible_message("You hear a quiet click, as [user] shuts off [src] without even looking at what they're doing.") + + set_light(0) + STOP_PROCESSING(SSobj, src) + return + +//Here we add Zippo skins. + +/obj/item/weapon/flame/lighter/zippo/black + name = "\improper holy Zippo lighter" + desc = "Only in regards to Christianity, that is." + icon_state = "blackzippo" + +/obj/item/weapon/flame/lighter/zippo/blue + name = "\improper blue Zippo lighter" + icon_state = "bluezippo" + +/obj/item/weapon/flame/lighter/zippo/engraved + name = "\improper engraved Zippo lighter" + icon_state = "engravedzippo" + item_state = "zippo" + +/obj/item/weapon/flame/lighter/zippo/gold + name = "\improper golden Zippo lighter" + icon_state = "goldzippo" + +/obj/item/weapon/flame/lighter/zippo/moff + name = "\improper moth Zippo lighter" + desc = "Too cute to be a Tymisian." + icon_state = "moffzippo" + +/obj/item/weapon/flame/lighter/zippo/red + name = "\improper red Zippo lighter" + icon_state = "redzippo" + +/obj/item/weapon/flame/lighter/zippo/ironic + name = "\improper ironic Zippo lighter" + desc = "What a quiant idea." + icon_state = "ironiczippo" + +/obj/item/weapon/flame/lighter/zippo/capitalist + name = "\improper capitalist Zippo lighter" + desc = "Made of gold and obsidian, this is truly not worth however much you spent on it." + icon_state = "cappiezippo" + +/obj/item/weapon/flame/lighter/zippo/communist + name = "\improper communist Zippo lighter" + desc = "All you need to spark a revolution." + icon_state = "commiezippo" + +/obj/item/weapon/flame/lighter/zippo/royal + name = "\improper royal Zippo lighter" + desc = "An incredibly fancy lighter, gilded and covered in the color of royalty." + icon_state = "royalzippo" + +/obj/item/weapon/flame/lighter/zippo/gonzo + name = "\improper Gonzo Zippo lighter" + desc = "A lighter with the iconic Gonzo fist painted on it." + icon_state = "gonzozippo" + +/obj/item/weapon/flame/lighter/zippo/rainbow + name = "\improper rainbow Zippo lighter" + icon_state = "rainbowzippo" + +/obj/item/weapon/flame/lighter/zippo/skull + name = "\improper badass Zippo lighter" + desc = "An absolutely badass zippo lighter. Just look at that skull!" icon_state = "skullzippo" \ No newline at end of file diff --git a/code/game/objects/items/weapons/circuitboards/machinery/kitchen_appliances.dm b/code/game/objects/items/weapons/circuitboards/machinery/kitchen_appliances.dm index 4a5307fa12d..579c5885193 100644 --- a/code/game/objects/items/weapons/circuitboards/machinery/kitchen_appliances.dm +++ b/code/game/objects/items/weapons/circuitboards/machinery/kitchen_appliances.dm @@ -1,74 +1,74 @@ -/obj/item/weapon/circuitboard/microwave - name = T_BOARD("microwave") - desc = "The circuitboard for a microwave." - build_path = /obj/machinery/microwave - board_type = new /datum/frame/frame_types/microwave - contain_parts = 0 - matter = list(MAT_STEEL = 50, MAT_GLASS = 50) - req_components = list( - /obj/item/weapon/stock_parts/console_screen = 1, - /obj/item/weapon/stock_parts/capacitor = 3, // Original Capacitor count was 1 - /obj/item/weapon/stock_parts/motor = 1, - /obj/item/weapon/stock_parts/scanning_module = 1, - /obj/item/weapon/stock_parts/matter_bin = 2) - -/obj/item/weapon/circuitboard/oven - name = T_BOARD("oven") - desc = "The circuitboard for an oven." - build_path = /obj/machinery/appliance/cooker/oven - board_type = new /datum/frame/frame_types/machine - matter = list(MAT_STEEL = 50, MAT_GLASS = 50) - req_components = list( - /obj/item/weapon/stock_parts/capacitor = 3, - /obj/item/weapon/stock_parts/scanning_module = 1, - /obj/item/weapon/stock_parts/matter_bin = 2) - -/obj/item/weapon/circuitboard/fryer - name = T_BOARD("deep fryer") - desc = "The circuitboard for a deep fryer." - build_path = /obj/machinery/appliance/cooker/fryer - board_type = new /datum/frame/frame_types/machine - req_components = list( - /obj/item/weapon/stock_parts/capacitor = 3, - /obj/item/weapon/stock_parts/scanning_module = 1, - /obj/item/weapon/stock_parts/matter_bin = 2) - -/obj/item/weapon/circuitboard/grill - name = T_BOARD("grill") - desc = "The circuitboard for an industrial grill." - build_path = /obj/machinery/appliance/cooker/grill - board_type = new /datum/frame/frame_types/machine - req_components = list( - /obj/item/weapon/stock_parts/capacitor = 3, - /obj/item/weapon/stock_parts/scanning_module = 1, - /obj/item/weapon/stock_parts/matter_bin = 2) - -/obj/item/weapon/circuitboard/cerealmaker - name = T_BOARD("cereal maker") - desc = "The circuitboard for a cereal maker." - build_path = /obj/machinery/appliance/mixer/cereal - board_type = new /datum/frame/frame_types/machine - req_components = list( - /obj/item/weapon/stock_parts/capacitor = 3, - /obj/item/weapon/stock_parts/scanning_module = 1, - /obj/item/weapon/stock_parts/matter_bin = 2) - -/obj/item/weapon/circuitboard/candymachine - name = T_BOARD("candy machine") - desc = "The circuitboard for a candy machine." - build_path = /obj/machinery/appliance/mixer/candy - board_type = new /datum/frame/frame_types/machine - req_components = list( - /obj/item/weapon/stock_parts/capacitor = 3, - /obj/item/weapon/stock_parts/scanning_module = 1, - /obj/item/weapon/stock_parts/matter_bin = 2) - -/obj/item/weapon/circuitboard/microwave/advanced - name = T_BOARD("deluxe microwave") - build_path = /obj/machinery/microwave/advanced - board_type = new /datum/frame/frame_types/microwave - matter = list(MAT_STEEL = 50, MAT_GLASS = 50) - req_components = list( - /obj/item/weapon/stock_parts/console_screen = 1, - /obj/item/weapon/stock_parts/motor = 1, +/obj/item/weapon/circuitboard/microwave + name = T_BOARD("microwave") + desc = "The circuitboard for a microwave." + build_path = /obj/machinery/microwave + board_type = new /datum/frame/frame_types/microwave + contain_parts = 0 + matter = list(MAT_STEEL = 50, MAT_GLASS = 50) + req_components = list( + /obj/item/weapon/stock_parts/console_screen = 1, + /obj/item/weapon/stock_parts/capacitor = 3, // Original Capacitor count was 1 + /obj/item/weapon/stock_parts/motor = 1, + /obj/item/weapon/stock_parts/scanning_module = 1, + /obj/item/weapon/stock_parts/matter_bin = 2) + +/obj/item/weapon/circuitboard/oven + name = T_BOARD("oven") + desc = "The circuitboard for an oven." + build_path = /obj/machinery/appliance/cooker/oven + board_type = new /datum/frame/frame_types/machine + matter = list(MAT_STEEL = 50, MAT_GLASS = 50) + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 3, + /obj/item/weapon/stock_parts/scanning_module = 1, + /obj/item/weapon/stock_parts/matter_bin = 2) + +/obj/item/weapon/circuitboard/fryer + name = T_BOARD("deep fryer") + desc = "The circuitboard for a deep fryer." + build_path = /obj/machinery/appliance/cooker/fryer + board_type = new /datum/frame/frame_types/machine + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 3, + /obj/item/weapon/stock_parts/scanning_module = 1, + /obj/item/weapon/stock_parts/matter_bin = 2) + +/obj/item/weapon/circuitboard/grill + name = T_BOARD("grill") + desc = "The circuitboard for an industrial grill." + build_path = /obj/machinery/appliance/cooker/grill + board_type = new /datum/frame/frame_types/machine + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 3, + /obj/item/weapon/stock_parts/scanning_module = 1, + /obj/item/weapon/stock_parts/matter_bin = 2) + +/obj/item/weapon/circuitboard/cerealmaker + name = T_BOARD("cereal maker") + desc = "The circuitboard for a cereal maker." + build_path = /obj/machinery/appliance/mixer/cereal + board_type = new /datum/frame/frame_types/machine + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 3, + /obj/item/weapon/stock_parts/scanning_module = 1, + /obj/item/weapon/stock_parts/matter_bin = 2) + +/obj/item/weapon/circuitboard/candymachine + name = T_BOARD("candy machine") + desc = "The circuitboard for a candy machine." + build_path = /obj/machinery/appliance/mixer/candy + board_type = new /datum/frame/frame_types/machine + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 3, + /obj/item/weapon/stock_parts/scanning_module = 1, + /obj/item/weapon/stock_parts/matter_bin = 2) + +/obj/item/weapon/circuitboard/microwave/advanced + name = T_BOARD("deluxe microwave") + build_path = /obj/machinery/microwave/advanced + board_type = new /datum/frame/frame_types/microwave + matter = list(MAT_STEEL = 50, MAT_GLASS = 50) + req_components = list( + /obj/item/weapon/stock_parts/console_screen = 1, + /obj/item/weapon/stock_parts/motor = 1, /obj/item/weapon/stock_parts/capacitor = 1) \ No newline at end of file diff --git a/code/game/objects/items/weapons/clown_items.dm b/code/game/objects/items/weapons/clown_items.dm index d7208d5db37..e3a12284704 100644 --- a/code/game/objects/items/weapons/clown_items.dm +++ b/code/game/objects/items/weapons/clown_items.dm @@ -1,87 +1,87 @@ -/* Clown Items - * Contains: - * Banana Peels - * Soap - * Bike Horns - */ - -/* - * Banana Peels - */ -/obj/item/weapon/bananapeel/Crossed(atom/movable/AM as mob|obj) - if(AM.is_incorporeal()) - return - if(istype(AM, /mob/living)) - var/mob/living/M = AM - M.slip("the [src.name]",4) -/* - * Soap - */ -/obj/item/weapon/soap/Initialize() - . = ..() - create_reagents(5) - wet() - -/obj/item/weapon/soap/proc/wet() - reagents.add_reagent("cleaner", 5) - -/obj/item/weapon/soap/Crossed(atom/movable/AM as mob|obj) - if(AM.is_incorporeal()) - return - if(istype(AM, /mob/living)) - var/mob/living/M = AM - M.slip("the [src.name]",3) - -/obj/item/weapon/soap/afterattack(atom/target, mob/user as mob, proximity) - if(!proximity) return - //I couldn't feasibly fix the overlay bugs caused by cleaning items we are wearing. - //So this is a workaround. This also makes more sense from an IC standpoint. ~Carn - if(user.client && (target in user.client.screen)) - to_chat(user, "You need to take that [target.name] off before cleaning it.") - else if(istype(target,/obj/effect/decal/cleanable/blood)) - to_chat(user, "You scrub \the [target.name] out.") - target.clean_blood() - return //Blood is a cleanable decal, therefore needs to be accounted for before all cleanable decals. - else if(istype(target,/obj/effect/decal/cleanable)) - to_chat(user, "You scrub \the [target.name] out.") - qdel(target) - else if(istype(target,/turf)) - to_chat(user, "You scrub \the [target.name] clean.") - var/turf/T = target - T.clean(src, user) - else if(istype(target,/obj/structure/sink)) - to_chat(user, "You wet \the [src] in the sink.") - wet() - else - to_chat(user, "You clean \the [target.name].") - target.clean_blood(TRUE) - return - -//attack_as_weapon -/obj/item/weapon/soap/attack(mob/living/target, mob/living/user, var/target_zone) - if(target && user && ishuman(target) && ishuman(user) && !user.incapacitated() && user.zone_sel &&user.zone_sel.selecting == "mouth" ) - user.visible_message("\The [user] washes \the [target]'s mouth out with soap!") - user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //prevent spam - return - ..() - -/* - * Bike Horns - */ -/obj/item/weapon/bikehorn - var/honk_sound = 'sound/items/bikehorn.ogg' - -/obj/item/weapon/bikehorn/attack_self(mob/user as mob) - if(spam_flag == 0) - spam_flag = 1 - playsound(src, honk_sound, 50, 1) - src.add_fingerprint(user) - spawn(20) - spam_flag = 0 - return - -/obj/item/weapon/bikehorn/Crossed(atom/movable/AM as mob|obj) - if(AM.is_incorporeal()) - return - if(istype(AM, /mob/living)) - playsound(src, honk_sound, 50, 1) +/* Clown Items + * Contains: + * Banana Peels + * Soap + * Bike Horns + */ + +/* + * Banana Peels + */ +/obj/item/weapon/bananapeel/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return + if(istype(AM, /mob/living)) + var/mob/living/M = AM + M.slip("the [src.name]",4) +/* + * Soap + */ +/obj/item/weapon/soap/Initialize() + . = ..() + create_reagents(5) + wet() + +/obj/item/weapon/soap/proc/wet() + reagents.add_reagent("cleaner", 5) + +/obj/item/weapon/soap/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return + if(istype(AM, /mob/living)) + var/mob/living/M = AM + M.slip("the [src.name]",3) + +/obj/item/weapon/soap/afterattack(atom/target, mob/user as mob, proximity) + if(!proximity) return + //I couldn't feasibly fix the overlay bugs caused by cleaning items we are wearing. + //So this is a workaround. This also makes more sense from an IC standpoint. ~Carn + if(user.client && (target in user.client.screen)) + to_chat(user, "You need to take that [target.name] off before cleaning it.") + else if(istype(target,/obj/effect/decal/cleanable/blood)) + to_chat(user, "You scrub \the [target.name] out.") + target.clean_blood() + return //Blood is a cleanable decal, therefore needs to be accounted for before all cleanable decals. + else if(istype(target,/obj/effect/decal/cleanable)) + to_chat(user, "You scrub \the [target.name] out.") + qdel(target) + else if(istype(target,/turf)) + to_chat(user, "You scrub \the [target.name] clean.") + var/turf/T = target + T.clean(src, user) + else if(istype(target,/obj/structure/sink)) + to_chat(user, "You wet \the [src] in the sink.") + wet() + else + to_chat(user, "You clean \the [target.name].") + target.clean_blood(TRUE) + return + +//attack_as_weapon +/obj/item/weapon/soap/attack(mob/living/target, mob/living/user, var/target_zone) + if(target && user && ishuman(target) && ishuman(user) && !user.incapacitated() && user.zone_sel &&user.zone_sel.selecting == "mouth" ) + user.visible_message("\The [user] washes \the [target]'s mouth out with soap!") + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //prevent spam + return + ..() + +/* + * Bike Horns + */ +/obj/item/weapon/bikehorn + var/honk_sound = 'sound/items/bikehorn.ogg' + +/obj/item/weapon/bikehorn/attack_self(mob/user as mob) + if(spam_flag == 0) + spam_flag = 1 + playsound(src, honk_sound, 50, 1) + src.add_fingerprint(user) + spawn(20) + spam_flag = 0 + return + +/obj/item/weapon/bikehorn/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return + if(istype(AM, /mob/living)) + playsound(src, honk_sound, 50, 1) diff --git a/code/game/objects/items/weapons/cosmetics.dm b/code/game/objects/items/weapons/cosmetics.dm index e2d9ad6a496..6a899d9a700 100644 --- a/code/game/objects/items/weapons/cosmetics.dm +++ b/code/game/objects/items/weapons/cosmetics.dm @@ -1,113 +1,113 @@ -/obj/item/weapon/lipstick - gender = PLURAL - name = "red lipstick" - desc = "A generic brand of lipstick." - icon = 'icons/obj/items.dmi' - icon_state = "lipstick" - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - var/colour = "red" - var/open = 0 - drop_sound = 'sound/items/drop/glass.ogg' - pickup_sound = 'sound/items/pickup/glass.ogg' - -/obj/item/weapon/lipstick/purple - name = "purple lipstick" - colour = "purple" - -/obj/item/weapon/lipstick/jade - name = "jade lipstick" - colour = "jade" - -/obj/item/weapon/lipstick/black - name = "black lipstick" - colour = "black" - -/obj/item/weapon/lipstick/random - name = "lipstick" - -/obj/item/weapon/lipstick/random/New() - colour = pick("red","purple","jade","black") - name = "[colour] lipstick" - -/obj/item/weapon/lipstick/attack_self(mob/user as mob) - to_chat(user, "You twist \the [src] [open ? "closed" : "open"].") - open = !open - if(open) - icon_state = "[initial(icon_state)]_[colour]" - else - icon_state = initial(icon_state) - -/obj/item/weapon/lipstick/attack(mob/M as mob, mob/user as mob) - if(!open) return - - if(!istype(M, /mob)) return - - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.lip_style) //if they already have lipstick on - to_chat(user, "You need to wipe off the old lipstick first!") - return - if(H == user) - user.visible_message("[user] does their lips with \the [src].", \ - "You take a moment to apply \the [src]. Perfect!") - H.lip_style = colour - H.update_icons_body() - else - user.visible_message("[user] begins to do [H]'s lips with \the [src].", \ - "You begin to apply \the [src].") - if(do_after(user, 20, H)) //user needs to keep their active hand, H does not. - user.visible_message("[user] does [H]'s lips with \the [src].", \ - "You apply \the [src].") - H.lip_style = colour - H.update_icons_body() - else - to_chat(user, "Where are the lips on that?") - -//you can wipe off lipstick with paper! see code/modules/paperwork/paper.dm, paper/attack() - -/obj/item/weapon/haircomb //sparklysheep's comb - name = "purple comb" - desc = "A pristine purple comb made from flexible plastic." - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - icon = 'icons/obj/items.dmi' - icon_state = "purplecomb" - -/obj/item/weapon/haircomb/attack_self(mob/living/user) - var/text = "person" - if(ishuman(user)) - var/mob/living/carbon/human/U = user - switch(U.identifying_gender) - if(MALE) - text = "guy" - if(FEMALE) - text = "lady" - else - switch(user.gender) - if(MALE) - text = "guy" - if(FEMALE) - text = "lady" - user.visible_message("[user] uses [src] to comb their hair with incredible style and sophistication. What a [text].") - -/obj/item/weapon/makeover - name = "makeover kit" - desc = "A tiny case containing a mirror and some contact lenses." - w_class = ITEMSIZE_TINY - icon = 'icons/obj/items.dmi' - icon_state = "trinketbox" - var/datum/tgui_module/appearance_changer/mirror/coskit/M - -/obj/item/weapon/makeover/Initialize() - . = ..() - M = new(src, null) - -/obj/item/weapon/makeover/attack_self(mob/living/carbon/user as mob) - if(ishuman(user)) - to_chat(user, "You flip open \the [src] and begin to adjust your appearance.") - M.tgui_interact(user) - var/mob/living/carbon/human/H = user - var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] - if(istype(E)) +/obj/item/weapon/lipstick + gender = PLURAL + name = "red lipstick" + desc = "A generic brand of lipstick." + icon = 'icons/obj/items.dmi' + icon_state = "lipstick" + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + var/colour = "red" + var/open = 0 + drop_sound = 'sound/items/drop/glass.ogg' + pickup_sound = 'sound/items/pickup/glass.ogg' + +/obj/item/weapon/lipstick/purple + name = "purple lipstick" + colour = "purple" + +/obj/item/weapon/lipstick/jade + name = "jade lipstick" + colour = "jade" + +/obj/item/weapon/lipstick/black + name = "black lipstick" + colour = "black" + +/obj/item/weapon/lipstick/random + name = "lipstick" + +/obj/item/weapon/lipstick/random/New() + colour = pick("red","purple","jade","black") + name = "[colour] lipstick" + +/obj/item/weapon/lipstick/attack_self(mob/user as mob) + to_chat(user, "You twist \the [src] [open ? "closed" : "open"].") + open = !open + if(open) + icon_state = "[initial(icon_state)]_[colour]" + else + icon_state = initial(icon_state) + +/obj/item/weapon/lipstick/attack(mob/M as mob, mob/user as mob) + if(!open) return + + if(!istype(M, /mob)) return + + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.lip_style) //if they already have lipstick on + to_chat(user, "You need to wipe off the old lipstick first!") + return + if(H == user) + user.visible_message("[user] does their lips with \the [src].", \ + "You take a moment to apply \the [src]. Perfect!") + H.lip_style = colour + H.update_icons_body() + else + user.visible_message("[user] begins to do [H]'s lips with \the [src].", \ + "You begin to apply \the [src].") + if(do_after(user, 20, H)) //user needs to keep their active hand, H does not. + user.visible_message("[user] does [H]'s lips with \the [src].", \ + "You apply \the [src].") + H.lip_style = colour + H.update_icons_body() + else + to_chat(user, "Where are the lips on that?") + +//you can wipe off lipstick with paper! see code/modules/paperwork/paper.dm, paper/attack() + +/obj/item/weapon/haircomb //sparklysheep's comb + name = "purple comb" + desc = "A pristine purple comb made from flexible plastic." + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + icon = 'icons/obj/items.dmi' + icon_state = "purplecomb" + +/obj/item/weapon/haircomb/attack_self(mob/living/user) + var/text = "person" + if(ishuman(user)) + var/mob/living/carbon/human/U = user + switch(U.identifying_gender) + if(MALE) + text = "guy" + if(FEMALE) + text = "lady" + else + switch(user.gender) + if(MALE) + text = "guy" + if(FEMALE) + text = "lady" + user.visible_message("[user] uses [src] to comb their hair with incredible style and sophistication. What a [text].") + +/obj/item/weapon/makeover + name = "makeover kit" + desc = "A tiny case containing a mirror and some contact lenses." + w_class = ITEMSIZE_TINY + icon = 'icons/obj/items.dmi' + icon_state = "trinketbox" + var/datum/tgui_module/appearance_changer/mirror/coskit/M + +/obj/item/weapon/makeover/Initialize() + . = ..() + M = new(src, null) + +/obj/item/weapon/makeover/attack_self(mob/living/carbon/user as mob) + if(ishuman(user)) + to_chat(user, "You flip open \the [src] and begin to adjust your appearance.") + M.tgui_interact(user) + var/mob/living/carbon/human/H = user + var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] + if(istype(E)) E.change_eye_color() \ No newline at end of file diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/game/objects/items/weapons/dna_injector.dm index ce634360942..ceafb06c73e 100644 --- a/code/game/objects/items/weapons/dna_injector.dm +++ b/code/game/objects/items/weapons/dna_injector.dm @@ -1,578 +1,578 @@ -/obj/item/weapon/dnainjector - name = "\improper DNA injector" - desc = "This injects the person with DNA." - icon = 'icons/obj/items.dmi' - icon_state = "dnainjector" - var/block=0 - var/datum/dna2/record/buf=null - var/s_time = 10.0 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - var/uses = 1 - var/nofail - var/is_bullet = 0 - var/inuse = 0 - - // USE ONLY IN PREMADE SYRINGES. WILL NOT WORK OTHERWISE. - var/datatype=0 - var/value=0 - -/obj/item/weapon/dnainjector/New() - if(datatype && block) - buf=new - buf.dna=new - buf.types = datatype - buf.dna.ResetSE() - //testing("[name]: DNA2 SE blocks prior to SetValue: [english_list(buf.dna.SE)]") - SetValue(src.value) - //testing("[name]: DNA2 SE blocks after SetValue: [english_list(buf.dna.SE)]") - -/obj/item/weapon/dnainjector/proc/GetRealBlock(var/selblock) - if(selblock==0) - return block - else - return selblock - -/obj/item/weapon/dnainjector/proc/GetState(var/selblock=0) - var/real_block=GetRealBlock(selblock) - if(buf.types&DNA2_BUF_SE) - return buf.dna.GetSEState(real_block) - else - return buf.dna.GetUIState(real_block) - -/obj/item/weapon/dnainjector/proc/SetState(var/on, var/selblock=0) - var/real_block=GetRealBlock(selblock) - if(buf.types&DNA2_BUF_SE) - return buf.dna.SetSEState(real_block,on) - else - return buf.dna.SetUIState(real_block,on) - -/obj/item/weapon/dnainjector/proc/GetValue(var/selblock=0) - var/real_block=GetRealBlock(selblock) - if(buf.types&DNA2_BUF_SE) - return buf.dna.GetSEValue(real_block) - else - return buf.dna.GetUIValue(real_block) - -/obj/item/weapon/dnainjector/proc/SetValue(var/val,var/selblock=0) - var/real_block=GetRealBlock(selblock) - if(buf.types&DNA2_BUF_SE) - return buf.dna.SetSEValue(real_block,val) - else - return buf.dna.SetUIValue(real_block,val) - -/obj/item/weapon/dnainjector/proc/inject(mob/M as mob, mob/user as mob) - if(istype(M,/mob/living)) - var/mob/living/L = M - L.apply_effect(rand(5,20), IRRADIATE, check_protection = 0) - L.apply_damage(max(2,L.getCloneLoss()), CLONE) - - if (!(NOCLONE in M.mutations)) // prevents drained people from having their DNA changed - if (buf.types & DNA2_BUF_UI) - if (!block) //isolated block? - M.UpdateAppearance(buf.dna.UI.Copy()) - if (buf.types & DNA2_BUF_UE) //unique enzymes? yes - M.real_name = buf.dna.real_name - M.name = buf.dna.real_name - uses-- - else - M.dna.SetUIValue(block,src.GetValue()) - M.UpdateAppearance() - uses-- - if (buf.types & DNA2_BUF_SE) - if (!block) //isolated block? - M.dna.SE = buf.dna.SE.Copy() - M.dna.UpdateSE() - else - M.dna.SetSEValue(block,src.GetValue()) - domutcheck(M, null, block!=null) - uses-- - if(prob(5)) - trigger_side_effect(M) - - spawn(0)//this prevents the collapse of space-time continuum - if (user) - user.drop_from_inventory(src) - qdel(src) - return uses - -/obj/item/weapon/dnainjector/attack(mob/M as mob, mob/user as mob) - if (!istype(M, /mob)) - return - if (!usr.IsAdvancedToolUser()) - return - if(inuse) - return 0 - - user.visible_message("\The [user] is trying to inject \the [M] with \the [src]!") - inuse = 1 - s_time = world.time - spawn(50) - inuse = 0 - - if(!do_after(user,50)) - return - - user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) - user.do_attack_animation(M) - - M.visible_message("\The [M] has been injected with \the [src] by \the [user].") - - var/mob/living/carbon/human/H = M - if(!istype(H)) - to_chat(user, "Apparently it didn't work...") - return - - // Used by admin log. - var/injected_with_monkey = "" - if((buf.types & DNA2_BUF_SE) && (block ? (GetState() && block == MONKEYBLOCK) : GetState(MONKEYBLOCK))) - injected_with_monkey = " (MONKEY)" - - add_attack_logs(user,M,"[injected_with_monkey] used the [name] on") - - // Apply the DNA shit. - inject(M, user) - return - -/obj/item/weapon/dnainjector/hulkmut - name = "\improper DNA injector (Hulk)" - desc = "This will make you big and strong, but give you a bad skin condition." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/hulkmut/New() - block = HULKBLOCK - ..() - -/obj/item/weapon/dnainjector/antihulk - name = "\improper DNA injector (Anti-Hulk)" - desc = "Cures green skin." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antihulk/New() - block = HULKBLOCK - ..() - -/obj/item/weapon/dnainjector/xraymut - name = "\improper DNA injector (Xray)" - desc = "Finally you can see what the Site Manager does." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/xraymut/New() - block = XRAYBLOCK - ..() - -/obj/item/weapon/dnainjector/antixray - name = "\improper DNA injector (Anti-Xray)" - desc = "It will make you see harder." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antixray/New() - block = XRAYBLOCK - ..() - -/obj/item/weapon/dnainjector/firemut - name = "\improper DNA injector (Fire)" - desc = "Gives you fire." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/firemut/New() - block = FIREBLOCK - ..() - -/obj/item/weapon/dnainjector/antifire - name = "\improper DNA injector (Anti-Fire)" - desc = "Cures fire." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antifire/New() - block = FIREBLOCK - ..() - -/obj/item/weapon/dnainjector/telemut - name = "\improper DNA injector (Tele.)" - desc = "Super brain man!" - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/telemut/New() - block = TELEBLOCK - ..() - -/obj/item/weapon/dnainjector/antitele - name = "\improper DNA injector (Anti-Tele.)" - desc = "Will make you not able to control your mind." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antitele/New() - block = TELEBLOCK - ..() - -/obj/item/weapon/dnainjector/nobreath - name = "\improper DNA injector (No Breath)" - desc = "Hold your breath and count to infinity." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/nobreath/New() - block = NOBREATHBLOCK - ..() - -/obj/item/weapon/dnainjector/antinobreath - name = "\improper DNA injector (Anti-No Breath)" - desc = "Hold your breath and count to 100." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antinobreath/New() - block = NOBREATHBLOCK - ..() - -/obj/item/weapon/dnainjector/remoteview - name = "\improper DNA injector (Remote View)" - desc = "Stare into the distance for a reason." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/remoteview/New() - block = REMOTEVIEWBLOCK - ..() - -/obj/item/weapon/dnainjector/antiremoteview - name = "\improper DNA injector (Anti-Remote View)" - desc = "Cures green skin." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antiremoteview/New() - block = REMOTEVIEWBLOCK - ..() - -/obj/item/weapon/dnainjector/regenerate - name = "\improper DNA injector (Regeneration)" - desc = "Healthy but hungry." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/regenerate/New() - block = REGENERATEBLOCK - ..() - -/obj/item/weapon/dnainjector/antiregenerate - name = "\improper DNA injector (Anti-Regeneration)" - desc = "Sickly but sated." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antiregenerate/New() - block = REGENERATEBLOCK - ..() - -/obj/item/weapon/dnainjector/runfast - name = "\improper DNA injector (Increase Run)" - desc = "Running Man." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/runfast/New() - block = INCREASERUNBLOCK - ..() - -/obj/item/weapon/dnainjector/antirunfast - name = "\improper DNA injector (Anti-Increase Run)" - desc = "Walking Man." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antirunfast/New() - block = INCREASERUNBLOCK - ..() - -/obj/item/weapon/dnainjector/morph - name = "\improper DNA injector (Morph)" - desc = "A total makeover." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/morph/New() - block = MORPHBLOCK - ..() - -/obj/item/weapon/dnainjector/antimorph - name = "\improper DNA injector (Anti-Morph)" - desc = "Cures identity crisis." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antimorph/New() - block = MORPHBLOCK - ..() - -/obj/item/weapon/dnainjector/noprints - name = "\improper DNA injector (No Prints)" - desc = "Better than a pair of budget insulated gloves." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/noprints/New() - block = NOPRINTSBLOCK - ..() - -/obj/item/weapon/dnainjector/antinoprints - name = "\improper DNA injector (Anti-No Prints)" - desc = "Not quite as good as a pair of budget insulated gloves." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antinoprints/New() - block = NOPRINTSBLOCK - ..() - -/obj/item/weapon/dnainjector/insulation - name = "\improper DNA injector (Shock Immunity)" - desc = "Better than a pair of real insulated gloves." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/insulation/New() - block = SHOCKIMMUNITYBLOCK - ..() - -/obj/item/weapon/dnainjector/antiinsulation - name = "\improper DNA injector (Anti-Shock Immunity)" - desc = "Not quite as good as a pair of real insulated gloves." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antiinsulation/New() - block = SHOCKIMMUNITYBLOCK - ..() - -/obj/item/weapon/dnainjector/midgit - name = "\improper DNA injector (Small Size)" - desc = "Makes you shrink." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/midgit/New() - block = SMALLSIZEBLOCK - ..() - -/obj/item/weapon/dnainjector/antimidgit - name = "\improper DNA injector (Anti-Small Size)" - desc = "Makes you grow. But not too much." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antimidgit/New() - block = SMALLSIZEBLOCK - ..() - -///////////////////////////////////// -/obj/item/weapon/dnainjector/antiglasses - name = "\improper DNA injector (Anti-Glasses)" - desc = "Toss away those glasses!" - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antiglasses/New() - block = GLASSESBLOCK - ..() - -/obj/item/weapon/dnainjector/glassesmut - name = "\improper DNA injector (Glasses)" - desc = "Will make you need dorkish glasses." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/glassesmut/New() - block = GLASSESBLOCK - ..() - -/obj/item/weapon/dnainjector/epimut - name = "\improper DNA injector (Epi.)" - desc = "Shake shake shake the room!" - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/epimut/New() - block = HEADACHEBLOCK - ..() - -/obj/item/weapon/dnainjector/antiepi - name = "\improper DNA injector (Anti-Epi.)" - desc = "Will fix you up from shaking the room." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antiepi/New() - block = HEADACHEBLOCK - ..() - -/obj/item/weapon/dnainjector/anticough - name = "\improper DNA injector (Anti-Cough)" - desc = "Will stop that awful noise." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/anticough/New() - block = COUGHBLOCK - ..() - -/obj/item/weapon/dnainjector/coughmut - name = "\improper DNA injector (Cough)" - desc = "Will bring forth a sound of horror from your throat." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/coughmut/New() - block = COUGHBLOCK - ..() - -/obj/item/weapon/dnainjector/clumsymut - name = "\improper DNA injector (Clumsy)" - desc = "Makes clumsy minions." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/clumsymut/New() - block = CLUMSYBLOCK - ..() - -/obj/item/weapon/dnainjector/anticlumsy - name = "\improper DNA injector (Anti-Clumy)" - desc = "Cleans up confusion." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/anticlumsy/New() - block = CLUMSYBLOCK - ..() - -/obj/item/weapon/dnainjector/antitour - name = "\improper DNA injector (Anti-Tour.)" - desc = "Will cure tourrets." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antitour/New() - block = TWITCHBLOCK - ..() - -/obj/item/weapon/dnainjector/tourmut - name = "\improper DNA injector (Tour.)" - desc = "Gives you a nasty case off tourrets." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/tourmut/New() - block = TWITCHBLOCK - ..() - -/obj/item/weapon/dnainjector/stuttmut - name = "\improper DNA injector (Stutt.)" - desc = "Makes you s-s-stuttterrr" - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/stuttmut/New() - block = NERVOUSBLOCK - ..() - -/obj/item/weapon/dnainjector/antistutt - name = "\improper DNA injector (Anti-Stutt.)" - desc = "Fixes that speaking impairment." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antistutt/New() - block = NERVOUSBLOCK - ..() - -/obj/item/weapon/dnainjector/blindmut - name = "\improper DNA injector (Blind)" - desc = "Makes you not see anything." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/blindmut/New() - block = BLINDBLOCK - ..() - -/obj/item/weapon/dnainjector/antiblind - name = "\improper DNA injector (Anti-Blind)" - desc = "ITS A MIRACLE!!!" - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antiblind/New() - block = BLINDBLOCK - ..() - -/obj/item/weapon/dnainjector/deafmut - name = "\improper DNA injector (Deaf)" - desc = "Sorry, what did you say?" - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/deafmut/New() - block = DEAFBLOCK - ..() - -/obj/item/weapon/dnainjector/antideaf - name = "\improper DNA injector (Anti-Deaf)" - desc = "Will make you hear once more." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antideaf/New() - block = DEAFBLOCK - ..() - -/obj/item/weapon/dnainjector/hallucination - name = "\improper DNA injector (Halluctination)" - desc = "What you see isn't always what you get." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/hallucination/New() - block = HALLUCINATIONBLOCK - ..() - -/obj/item/weapon/dnainjector/antihallucination - name = "\improper DNA injector (Anti-Hallucination)" - desc = "What you see is what you get." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antihallucination/New() - block = HALLUCINATIONBLOCK - ..() - -/obj/item/weapon/dnainjector/h2m - name = "\improper DNA injector (Human > Monkey)" - desc = "Will make you a flea bag." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/h2m/New() - block = MONKEYBLOCK - ..() - -/obj/item/weapon/dnainjector/m2h - name = "\improper DNA injector (Monkey > Human)" - desc = "Will make you...less hairy." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/m2h/New() - block = MONKEYBLOCK - ..() +/obj/item/weapon/dnainjector + name = "\improper DNA injector" + desc = "This injects the person with DNA." + icon = 'icons/obj/items.dmi' + icon_state = "dnainjector" + var/block=0 + var/datum/dna2/record/buf=null + var/s_time = 10.0 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + var/uses = 1 + var/nofail + var/is_bullet = 0 + var/inuse = 0 + + // USE ONLY IN PREMADE SYRINGES. WILL NOT WORK OTHERWISE. + var/datatype=0 + var/value=0 + +/obj/item/weapon/dnainjector/New() + if(datatype && block) + buf=new + buf.dna=new + buf.types = datatype + buf.dna.ResetSE() + //testing("[name]: DNA2 SE blocks prior to SetValue: [english_list(buf.dna.SE)]") + SetValue(src.value) + //testing("[name]: DNA2 SE blocks after SetValue: [english_list(buf.dna.SE)]") + +/obj/item/weapon/dnainjector/proc/GetRealBlock(var/selblock) + if(selblock==0) + return block + else + return selblock + +/obj/item/weapon/dnainjector/proc/GetState(var/selblock=0) + var/real_block=GetRealBlock(selblock) + if(buf.types&DNA2_BUF_SE) + return buf.dna.GetSEState(real_block) + else + return buf.dna.GetUIState(real_block) + +/obj/item/weapon/dnainjector/proc/SetState(var/on, var/selblock=0) + var/real_block=GetRealBlock(selblock) + if(buf.types&DNA2_BUF_SE) + return buf.dna.SetSEState(real_block,on) + else + return buf.dna.SetUIState(real_block,on) + +/obj/item/weapon/dnainjector/proc/GetValue(var/selblock=0) + var/real_block=GetRealBlock(selblock) + if(buf.types&DNA2_BUF_SE) + return buf.dna.GetSEValue(real_block) + else + return buf.dna.GetUIValue(real_block) + +/obj/item/weapon/dnainjector/proc/SetValue(var/val,var/selblock=0) + var/real_block=GetRealBlock(selblock) + if(buf.types&DNA2_BUF_SE) + return buf.dna.SetSEValue(real_block,val) + else + return buf.dna.SetUIValue(real_block,val) + +/obj/item/weapon/dnainjector/proc/inject(mob/M as mob, mob/user as mob) + if(istype(M,/mob/living)) + var/mob/living/L = M + L.apply_effect(rand(5,20), IRRADIATE, check_protection = 0) + L.apply_damage(max(2,L.getCloneLoss()), CLONE) + + if (!(NOCLONE in M.mutations)) // prevents drained people from having their DNA changed + if (buf.types & DNA2_BUF_UI) + if (!block) //isolated block? + M.UpdateAppearance(buf.dna.UI.Copy()) + if (buf.types & DNA2_BUF_UE) //unique enzymes? yes + M.real_name = buf.dna.real_name + M.name = buf.dna.real_name + uses-- + else + M.dna.SetUIValue(block,src.GetValue()) + M.UpdateAppearance() + uses-- + if (buf.types & DNA2_BUF_SE) + if (!block) //isolated block? + M.dna.SE = buf.dna.SE.Copy() + M.dna.UpdateSE() + else + M.dna.SetSEValue(block,src.GetValue()) + domutcheck(M, null, block!=null) + uses-- + if(prob(5)) + trigger_side_effect(M) + + spawn(0)//this prevents the collapse of space-time continuum + if (user) + user.drop_from_inventory(src) + qdel(src) + return uses + +/obj/item/weapon/dnainjector/attack(mob/M as mob, mob/user as mob) + if (!istype(M, /mob)) + return + if (!usr.IsAdvancedToolUser()) + return + if(inuse) + return 0 + + user.visible_message("\The [user] is trying to inject \the [M] with \the [src]!") + inuse = 1 + s_time = world.time + spawn(50) + inuse = 0 + + if(!do_after(user,50)) + return + + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) + user.do_attack_animation(M) + + M.visible_message("\The [M] has been injected with \the [src] by \the [user].") + + var/mob/living/carbon/human/H = M + if(!istype(H)) + to_chat(user, "Apparently it didn't work...") + return + + // Used by admin log. + var/injected_with_monkey = "" + if((buf.types & DNA2_BUF_SE) && (block ? (GetState() && block == MONKEYBLOCK) : GetState(MONKEYBLOCK))) + injected_with_monkey = " (MONKEY)" + + add_attack_logs(user,M,"[injected_with_monkey] used the [name] on") + + // Apply the DNA shit. + inject(M, user) + return + +/obj/item/weapon/dnainjector/hulkmut + name = "\improper DNA injector (Hulk)" + desc = "This will make you big and strong, but give you a bad skin condition." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/hulkmut/New() + block = HULKBLOCK + ..() + +/obj/item/weapon/dnainjector/antihulk + name = "\improper DNA injector (Anti-Hulk)" + desc = "Cures green skin." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antihulk/New() + block = HULKBLOCK + ..() + +/obj/item/weapon/dnainjector/xraymut + name = "\improper DNA injector (Xray)" + desc = "Finally you can see what the Site Manager does." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/xraymut/New() + block = XRAYBLOCK + ..() + +/obj/item/weapon/dnainjector/antixray + name = "\improper DNA injector (Anti-Xray)" + desc = "It will make you see harder." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antixray/New() + block = XRAYBLOCK + ..() + +/obj/item/weapon/dnainjector/firemut + name = "\improper DNA injector (Fire)" + desc = "Gives you fire." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/firemut/New() + block = FIREBLOCK + ..() + +/obj/item/weapon/dnainjector/antifire + name = "\improper DNA injector (Anti-Fire)" + desc = "Cures fire." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antifire/New() + block = FIREBLOCK + ..() + +/obj/item/weapon/dnainjector/telemut + name = "\improper DNA injector (Tele.)" + desc = "Super brain man!" + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/telemut/New() + block = TELEBLOCK + ..() + +/obj/item/weapon/dnainjector/antitele + name = "\improper DNA injector (Anti-Tele.)" + desc = "Will make you not able to control your mind." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antitele/New() + block = TELEBLOCK + ..() + +/obj/item/weapon/dnainjector/nobreath + name = "\improper DNA injector (No Breath)" + desc = "Hold your breath and count to infinity." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/nobreath/New() + block = NOBREATHBLOCK + ..() + +/obj/item/weapon/dnainjector/antinobreath + name = "\improper DNA injector (Anti-No Breath)" + desc = "Hold your breath and count to 100." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antinobreath/New() + block = NOBREATHBLOCK + ..() + +/obj/item/weapon/dnainjector/remoteview + name = "\improper DNA injector (Remote View)" + desc = "Stare into the distance for a reason." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/remoteview/New() + block = REMOTEVIEWBLOCK + ..() + +/obj/item/weapon/dnainjector/antiremoteview + name = "\improper DNA injector (Anti-Remote View)" + desc = "Cures green skin." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antiremoteview/New() + block = REMOTEVIEWBLOCK + ..() + +/obj/item/weapon/dnainjector/regenerate + name = "\improper DNA injector (Regeneration)" + desc = "Healthy but hungry." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/regenerate/New() + block = REGENERATEBLOCK + ..() + +/obj/item/weapon/dnainjector/antiregenerate + name = "\improper DNA injector (Anti-Regeneration)" + desc = "Sickly but sated." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antiregenerate/New() + block = REGENERATEBLOCK + ..() + +/obj/item/weapon/dnainjector/runfast + name = "\improper DNA injector (Increase Run)" + desc = "Running Man." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/runfast/New() + block = INCREASERUNBLOCK + ..() + +/obj/item/weapon/dnainjector/antirunfast + name = "\improper DNA injector (Anti-Increase Run)" + desc = "Walking Man." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antirunfast/New() + block = INCREASERUNBLOCK + ..() + +/obj/item/weapon/dnainjector/morph + name = "\improper DNA injector (Morph)" + desc = "A total makeover." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/morph/New() + block = MORPHBLOCK + ..() + +/obj/item/weapon/dnainjector/antimorph + name = "\improper DNA injector (Anti-Morph)" + desc = "Cures identity crisis." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antimorph/New() + block = MORPHBLOCK + ..() + +/obj/item/weapon/dnainjector/noprints + name = "\improper DNA injector (No Prints)" + desc = "Better than a pair of budget insulated gloves." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/noprints/New() + block = NOPRINTSBLOCK + ..() + +/obj/item/weapon/dnainjector/antinoprints + name = "\improper DNA injector (Anti-No Prints)" + desc = "Not quite as good as a pair of budget insulated gloves." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antinoprints/New() + block = NOPRINTSBLOCK + ..() + +/obj/item/weapon/dnainjector/insulation + name = "\improper DNA injector (Shock Immunity)" + desc = "Better than a pair of real insulated gloves." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/insulation/New() + block = SHOCKIMMUNITYBLOCK + ..() + +/obj/item/weapon/dnainjector/antiinsulation + name = "\improper DNA injector (Anti-Shock Immunity)" + desc = "Not quite as good as a pair of real insulated gloves." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antiinsulation/New() + block = SHOCKIMMUNITYBLOCK + ..() + +/obj/item/weapon/dnainjector/midgit + name = "\improper DNA injector (Small Size)" + desc = "Makes you shrink." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/midgit/New() + block = SMALLSIZEBLOCK + ..() + +/obj/item/weapon/dnainjector/antimidgit + name = "\improper DNA injector (Anti-Small Size)" + desc = "Makes you grow. But not too much." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antimidgit/New() + block = SMALLSIZEBLOCK + ..() + +///////////////////////////////////// +/obj/item/weapon/dnainjector/antiglasses + name = "\improper DNA injector (Anti-Glasses)" + desc = "Toss away those glasses!" + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antiglasses/New() + block = GLASSESBLOCK + ..() + +/obj/item/weapon/dnainjector/glassesmut + name = "\improper DNA injector (Glasses)" + desc = "Will make you need dorkish glasses." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/glassesmut/New() + block = GLASSESBLOCK + ..() + +/obj/item/weapon/dnainjector/epimut + name = "\improper DNA injector (Epi.)" + desc = "Shake shake shake the room!" + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/epimut/New() + block = HEADACHEBLOCK + ..() + +/obj/item/weapon/dnainjector/antiepi + name = "\improper DNA injector (Anti-Epi.)" + desc = "Will fix you up from shaking the room." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antiepi/New() + block = HEADACHEBLOCK + ..() + +/obj/item/weapon/dnainjector/anticough + name = "\improper DNA injector (Anti-Cough)" + desc = "Will stop that awful noise." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/anticough/New() + block = COUGHBLOCK + ..() + +/obj/item/weapon/dnainjector/coughmut + name = "\improper DNA injector (Cough)" + desc = "Will bring forth a sound of horror from your throat." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/coughmut/New() + block = COUGHBLOCK + ..() + +/obj/item/weapon/dnainjector/clumsymut + name = "\improper DNA injector (Clumsy)" + desc = "Makes clumsy minions." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/clumsymut/New() + block = CLUMSYBLOCK + ..() + +/obj/item/weapon/dnainjector/anticlumsy + name = "\improper DNA injector (Anti-Clumy)" + desc = "Cleans up confusion." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/anticlumsy/New() + block = CLUMSYBLOCK + ..() + +/obj/item/weapon/dnainjector/antitour + name = "\improper DNA injector (Anti-Tour.)" + desc = "Will cure tourrets." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antitour/New() + block = TWITCHBLOCK + ..() + +/obj/item/weapon/dnainjector/tourmut + name = "\improper DNA injector (Tour.)" + desc = "Gives you a nasty case off tourrets." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/tourmut/New() + block = TWITCHBLOCK + ..() + +/obj/item/weapon/dnainjector/stuttmut + name = "\improper DNA injector (Stutt.)" + desc = "Makes you s-s-stuttterrr" + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/stuttmut/New() + block = NERVOUSBLOCK + ..() + +/obj/item/weapon/dnainjector/antistutt + name = "\improper DNA injector (Anti-Stutt.)" + desc = "Fixes that speaking impairment." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antistutt/New() + block = NERVOUSBLOCK + ..() + +/obj/item/weapon/dnainjector/blindmut + name = "\improper DNA injector (Blind)" + desc = "Makes you not see anything." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/blindmut/New() + block = BLINDBLOCK + ..() + +/obj/item/weapon/dnainjector/antiblind + name = "\improper DNA injector (Anti-Blind)" + desc = "ITS A MIRACLE!!!" + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antiblind/New() + block = BLINDBLOCK + ..() + +/obj/item/weapon/dnainjector/deafmut + name = "\improper DNA injector (Deaf)" + desc = "Sorry, what did you say?" + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/deafmut/New() + block = DEAFBLOCK + ..() + +/obj/item/weapon/dnainjector/antideaf + name = "\improper DNA injector (Anti-Deaf)" + desc = "Will make you hear once more." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antideaf/New() + block = DEAFBLOCK + ..() + +/obj/item/weapon/dnainjector/hallucination + name = "\improper DNA injector (Halluctination)" + desc = "What you see isn't always what you get." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/hallucination/New() + block = HALLUCINATIONBLOCK + ..() + +/obj/item/weapon/dnainjector/antihallucination + name = "\improper DNA injector (Anti-Hallucination)" + desc = "What you see is what you get." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antihallucination/New() + block = HALLUCINATIONBLOCK + ..() + +/obj/item/weapon/dnainjector/h2m + name = "\improper DNA injector (Human > Monkey)" + desc = "Will make you a flea bag." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/h2m/New() + block = MONKEYBLOCK + ..() + +/obj/item/weapon/dnainjector/m2h + name = "\improper DNA injector (Monkey > Human)" + desc = "Will make you...less hairy." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/m2h/New() + block = MONKEYBLOCK + ..() diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm index 494984b1708..fcb45bad9c1 100644 --- a/code/game/objects/items/weapons/explosives.dm +++ b/code/game/objects/items/weapons/explosives.dm @@ -1,119 +1,119 @@ -/obj/item/weapon/plastique - name = "plastic explosives" - desc = "Used to put holes in specific areas without too much extra hole." - gender = PLURAL - icon = 'icons/obj/assemblies.dmi' - icon_state = "plastic-explosive0" - item_state = "plasticx" - flags = NOBLUDGEON - w_class = ITEMSIZE_SMALL - origin_tech = list(TECH_ILLEGAL = 2) - var/datum/wires/explosive/c4/wires = null - var/timer = 10 - var/atom/target = null - var/open_panel = 0 - var/image_overlay = null - var/blast_dev = -1 - var/blast_heavy = -1 - var/blast_light = 2 - var/blast_flash = 3 - -/obj/item/weapon/plastique/New() - wires = new(src) - image_overlay = image('icons/obj/assemblies.dmi', "plastic-explosive2") - ..() - -/obj/item/weapon/plastique/Destroy() - qdel(wires) - wires = null - return ..() - -/obj/item/weapon/plastique/attackby(var/obj/item/I, var/mob/user) - if(I.has_tool_quality(TOOL_SCREWDRIVER)) - open_panel = !open_panel - to_chat(user, "You [open_panel ? "open" : "close"] the wire panel.") - playsound(src, I.usesound, 50, 1) - else if(I.has_tool_quality(TOOL_WIRECUTTER) || istype(I, /obj/item/device/multitool) || istype(I, /obj/item/device/assembly/signaler )) - wires.Interact(user) - else - ..() - -/obj/item/weapon/plastique/attack_self(mob/user as mob) - var/newtime = tgui_input_number(usr, "Please set the timer.", "Timer", 10, 60000, 10) - if(user.get_active_hand() == src) - newtime = CLAMP(newtime, 10, 60000) - timer = newtime - to_chat(user, "Timer set for [timer] seconds.") - -/obj/item/weapon/plastique/afterattack(atom/movable/target, mob/user, flag) - if (!flag) - return - if (ismob(target) || istype(target, /turf/unsimulated) || istype(target, /turf/simulated/shuttle) || istype(target, /obj/item/weapon/storage/) || istype(target, /obj/item/clothing/accessory/storage/) || istype(target, /obj/item/clothing/under)) - return - to_chat(user, "Planting explosives...") - user.do_attack_animation(target) - - if(do_after(user, 50) && in_range(user, target)) - user.drop_item() - src.target = target - loc = null - - if (ismob(target)) - add_attack_logs(user, target, "planted [name] on with [timer] second fuse") - user.visible_message("[user.name] finished planting an explosive on [target.name]!") - else - message_admins("[key_name(user, user.client)](?) planted [src.name] on [target.name] at ([target.x],[target.y],[target.z] - JMP) with [timer] second fuse",0,1) - log_game("[key_name(user)] planted [src.name] on [target.name] at ([target.x],[target.y],[target.z]) with [timer] second fuse") - - target.add_overlay(image_overlay, TRUE) - to_chat(user, "Bomb has been planted. Timer counting down from [timer].") - spawn(timer*10) - explode(get_turf(target)) - -/obj/item/weapon/plastique/proc/explode(var/location) - if(!target) - target = get_atom_on_turf(src) - if(!target) - target = src - if(location) - explosion(location, blast_dev, blast_heavy, blast_light, blast_flash) - - if(target) - if (istype(target, /turf/simulated/wall)) - var/turf/simulated/wall/W = target - W.dismantle_wall(1,1,1) - else if(istype(target, /mob/living)) - target.ex_act(2) // c4 can't gib mobs anymore. - else - target.ex_act(1) - if(target) - target.cut_overlay(image_overlay, TRUE) - qdel(src) - -/obj/item/weapon/plastique/attack(mob/M as mob, mob/user as mob, def_zone) - return - -/obj/item/weapon/plastique/seismic - name = "seismic charge" - desc = "Used to dig holes in specific areas without too much extra hole." - - blast_heavy = 2 - blast_light = 4 - blast_flash = 7 - -/obj/item/weapon/plastique/seismic/attackby(var/obj/item/I, var/mob/user) - . = ..() - if(open_panel) - if(istype(I, /obj/item/weapon/stock_parts/micro_laser)) - var/obj/item/weapon/stock_parts/SP = I - var/new_blast_power = max(1, round(SP.rating / 2) + 1) - if(new_blast_power > blast_heavy) - to_chat(user, "You install \the [I] into \the [src].") - user.drop_from_inventory(I) - qdel(I) - blast_heavy = new_blast_power - blast_light = blast_heavy + round(new_blast_power * 0.5) - blast_flash = blast_light + round(new_blast_power * 0.75) - else - to_chat(user, "The [I] is not any better than the component already installed into this charge!") - return . +/obj/item/weapon/plastique + name = "plastic explosives" + desc = "Used to put holes in specific areas without too much extra hole." + gender = PLURAL + icon = 'icons/obj/assemblies.dmi' + icon_state = "plastic-explosive0" + item_state = "plasticx" + flags = NOBLUDGEON + w_class = ITEMSIZE_SMALL + origin_tech = list(TECH_ILLEGAL = 2) + var/datum/wires/explosive/c4/wires = null + var/timer = 10 + var/atom/target = null + var/open_panel = 0 + var/image_overlay = null + var/blast_dev = -1 + var/blast_heavy = -1 + var/blast_light = 2 + var/blast_flash = 3 + +/obj/item/weapon/plastique/New() + wires = new(src) + image_overlay = image('icons/obj/assemblies.dmi', "plastic-explosive2") + ..() + +/obj/item/weapon/plastique/Destroy() + qdel(wires) + wires = null + return ..() + +/obj/item/weapon/plastique/attackby(var/obj/item/I, var/mob/user) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + open_panel = !open_panel + to_chat(user, "You [open_panel ? "open" : "close"] the wire panel.") + playsound(src, I.usesound, 50, 1) + else if(I.has_tool_quality(TOOL_WIRECUTTER) || istype(I, /obj/item/device/multitool) || istype(I, /obj/item/device/assembly/signaler )) + wires.Interact(user) + else + ..() + +/obj/item/weapon/plastique/attack_self(mob/user as mob) + var/newtime = tgui_input_number(usr, "Please set the timer.", "Timer", 10, 60000, 10) + if(user.get_active_hand() == src) + newtime = CLAMP(newtime, 10, 60000) + timer = newtime + to_chat(user, "Timer set for [timer] seconds.") + +/obj/item/weapon/plastique/afterattack(atom/movable/target, mob/user, flag) + if (!flag) + return + if (ismob(target) || istype(target, /turf/unsimulated) || istype(target, /turf/simulated/shuttle) || istype(target, /obj/item/weapon/storage/) || istype(target, /obj/item/clothing/accessory/storage/) || istype(target, /obj/item/clothing/under)) + return + to_chat(user, "Planting explosives...") + user.do_attack_animation(target) + + if(do_after(user, 50) && in_range(user, target)) + user.drop_item() + src.target = target + loc = null + + if (ismob(target)) + add_attack_logs(user, target, "planted [name] on with [timer] second fuse") + user.visible_message("[user.name] finished planting an explosive on [target.name]!") + else + message_admins("[key_name(user, user.client)](?) planted [src.name] on [target.name] at ([target.x],[target.y],[target.z] - JMP) with [timer] second fuse",0,1) + log_game("[key_name(user)] planted [src.name] on [target.name] at ([target.x],[target.y],[target.z]) with [timer] second fuse") + + target.add_overlay(image_overlay, TRUE) + to_chat(user, "Bomb has been planted. Timer counting down from [timer].") + spawn(timer*10) + explode(get_turf(target)) + +/obj/item/weapon/plastique/proc/explode(var/location) + if(!target) + target = get_atom_on_turf(src) + if(!target) + target = src + if(location) + explosion(location, blast_dev, blast_heavy, blast_light, blast_flash) + + if(target) + if (istype(target, /turf/simulated/wall)) + var/turf/simulated/wall/W = target + W.dismantle_wall(1,1,1) + else if(istype(target, /mob/living)) + target.ex_act(2) // c4 can't gib mobs anymore. + else + target.ex_act(1) + if(target) + target.cut_overlay(image_overlay, TRUE) + qdel(src) + +/obj/item/weapon/plastique/attack(mob/M as mob, mob/user as mob, def_zone) + return + +/obj/item/weapon/plastique/seismic + name = "seismic charge" + desc = "Used to dig holes in specific areas without too much extra hole." + + blast_heavy = 2 + blast_light = 4 + blast_flash = 7 + +/obj/item/weapon/plastique/seismic/attackby(var/obj/item/I, var/mob/user) + . = ..() + if(open_panel) + if(istype(I, /obj/item/weapon/stock_parts/micro_laser)) + var/obj/item/weapon/stock_parts/SP = I + var/new_blast_power = max(1, round(SP.rating / 2) + 1) + if(new_blast_power > blast_heavy) + to_chat(user, "You install \the [I] into \the [src].") + user.drop_from_inventory(I) + qdel(I) + blast_heavy = new_blast_power + blast_light = blast_heavy + round(new_blast_power * 0.5) + blast_flash = blast_light + round(new_blast_power * 0.75) + else + to_chat(user, "The [I] is not any better than the component already installed into this charge!") + return . diff --git a/code/game/objects/items/weapons/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm index 4b33d5bf60f..abd80e61ee0 100644 --- a/code/game/objects/items/weapons/extinguisher.dm +++ b/code/game/objects/items/weapons/extinguisher.dm @@ -1,144 +1,144 @@ -/obj/item/weapon/extinguisher - name = "fire extinguisher" - desc = "A traditional red fire extinguisher." - icon = 'icons/obj/items.dmi' - icon_state = "fire_extinguisher0" - item_state = "fire_extinguisher" - hitsound = 'sound/weapons/smash.ogg' - throwforce = 10 - w_class = ITEMSIZE_NORMAL - throw_speed = 2 - throw_range = 10 - force = 10 - matter = list(MAT_STEEL = 90) - attack_verb = list("slammed", "whacked", "bashed", "thunked", "battered", "bludgeoned", "thrashed") - drop_sound = 'sound/items/drop/gascan.ogg' - pickup_sound = 'sound/items/pickup/gascan.ogg' - - var/spray_particles = 3 - var/spray_amount = 10 //units of liquid per particle - var/max_water = 300 - var/last_use = 1.0 - var/safety = 1 - var/sprite_name = "fire_extinguisher" - var/rand_overlays = 6 - -/obj/item/weapon/extinguisher/mini - name = "fire extinguisher" - desc = "A light and compact fibreglass-framed model fire extinguisher." - icon_state = "miniFE0" - item_state = "miniFE" - hitsound = null //it is much lighter, after all. - throwforce = 2 - w_class = ITEMSIZE_SMALL - force = 3.0 - max_water = 150 - spray_particles = 3 - sprite_name = "miniFE" - rand_overlays = 0 - -/obj/item/weapon/extinguisher/atmo - name = "atmospheric fire extinguisher" - desc = "A heavy duty fire extinguisher meant to fight large fires." - icon_state = "atmos_extinguisher0" - item_state = "atmos_extinguisher" - throwforce = 12 - w_class = ITEMSIZE_LARGE - force = 3.0 - max_water = 600 - spray_particles = 3 - sprite_name = "atmos_extinguisher" - rand_overlays = 0 - -/obj/item/weapon/extinguisher/Initialize() - create_reagents(max_water) - reagents.add_reagent("firefoam", max_water) - if(rand_overlays) - var/choice = rand(1,rand_overlays) - add_overlay("[item_state]O[choice]") - . = ..() - -/obj/item/weapon/extinguisher/examine(mob/user) - . = ..() - if(get_dist(user, src) == 0) - . += "[src] has [src.reagents.total_volume] units of foam left!" - -/obj/item/weapon/extinguisher/attack_self(mob/user as mob) - safety = !safety - icon_state = "[sprite_name][!safety]" - desc = "The safety is [safety ? "on" : "off"]." - to_chat(user, "The safety is [safety ? "on" : "off"].") - -/obj/item/weapon/extinguisher/proc/propel_object(var/obj/O, mob/user, movementdirection) - if(O.anchored) return - - var/obj/structure/bed/chair/C - if(istype(O, /obj/structure/bed/chair)) - C = O - - var/list/move_speed = list(1, 1, 1, 2, 2, 3) - for(var/i in 1 to 6) - if(C) C.propelled = (6-i) - O.Move(get_step(user,movementdirection), movementdirection) - sleep(move_speed[i]) - - //additional movement - for(var/i in 1 to 3) - O.Move(get_step(user,movementdirection), movementdirection) - sleep(3) - -/obj/item/weapon/extinguisher/afterattack(var/atom/target, var/mob/user, var/flag) - //TODO; Add support for reagents in water. - - if( istype(target, /obj/structure/reagent_dispensers) && flag) - var/obj/o = target - var/amount = o.reagents.trans_to_obj(src, 50) - to_chat(user, "You fill [src] with [amount] units of the contents of [target].") - playsound(src, 'sound/effects/refill.ogg', 50, 1, -6) - return - - if (!safety) - if (src.reagents.total_volume < 1) - to_chat(usr, "\The [src] is empty.") - return - - if (world.time < src.last_use + 20) - return - - src.last_use = world.time - - playsound(src, 'sound/effects/extinguish.ogg', 75, 1, -3) - - var/direction = get_dir(src,target) - - if(user.buckled && isobj(user.buckled)) - spawn(0) - propel_object(user.buckled, user, turn(direction,180)) - - var/turf/T = get_turf(target) - var/turf/T1 = get_step(T,turn(direction, 90)) - var/turf/T2 = get_step(T,turn(direction, -90)) - - var/list/the_targets = list(T,T1,T2) - - for(var/a = 1 to spray_particles) - spawn(0) - if(!src || !reagents.total_volume) return - - var/obj/effect/effect/water/W = new /obj/effect/effect/water(get_turf(src)) - var/turf/my_target - if(a <= the_targets.len) - my_target = the_targets[a] - else - my_target = pick(the_targets) - W.create_reagents(spray_amount) - reagents.trans_to_obj(W, spray_amount) - W.set_color() - W.set_up(my_target) - - if((istype(usr.loc, /turf/space)) || (usr.lastarea.has_gravity == 0)) - user.inertia_dir = get_dir(target, user) - step(user, user.inertia_dir) - else - return ..() - return +/obj/item/weapon/extinguisher + name = "fire extinguisher" + desc = "A traditional red fire extinguisher." + icon = 'icons/obj/items.dmi' + icon_state = "fire_extinguisher0" + item_state = "fire_extinguisher" + hitsound = 'sound/weapons/smash.ogg' + throwforce = 10 + w_class = ITEMSIZE_NORMAL + throw_speed = 2 + throw_range = 10 + force = 10 + matter = list(MAT_STEEL = 90) + attack_verb = list("slammed", "whacked", "bashed", "thunked", "battered", "bludgeoned", "thrashed") + drop_sound = 'sound/items/drop/gascan.ogg' + pickup_sound = 'sound/items/pickup/gascan.ogg' + + var/spray_particles = 3 + var/spray_amount = 10 //units of liquid per particle + var/max_water = 300 + var/last_use = 1.0 + var/safety = 1 + var/sprite_name = "fire_extinguisher" + var/rand_overlays = 6 + +/obj/item/weapon/extinguisher/mini + name = "fire extinguisher" + desc = "A light and compact fibreglass-framed model fire extinguisher." + icon_state = "miniFE0" + item_state = "miniFE" + hitsound = null //it is much lighter, after all. + throwforce = 2 + w_class = ITEMSIZE_SMALL + force = 3.0 + max_water = 150 + spray_particles = 3 + sprite_name = "miniFE" + rand_overlays = 0 + +/obj/item/weapon/extinguisher/atmo + name = "atmospheric fire extinguisher" + desc = "A heavy duty fire extinguisher meant to fight large fires." + icon_state = "atmos_extinguisher0" + item_state = "atmos_extinguisher" + throwforce = 12 + w_class = ITEMSIZE_LARGE + force = 3.0 + max_water = 600 + spray_particles = 3 + sprite_name = "atmos_extinguisher" + rand_overlays = 0 + +/obj/item/weapon/extinguisher/Initialize() + create_reagents(max_water) + reagents.add_reagent("firefoam", max_water) + if(rand_overlays) + var/choice = rand(1,rand_overlays) + add_overlay("[item_state]O[choice]") + . = ..() + +/obj/item/weapon/extinguisher/examine(mob/user) + . = ..() + if(get_dist(user, src) == 0) + . += "[src] has [src.reagents.total_volume] units of foam left!" + +/obj/item/weapon/extinguisher/attack_self(mob/user as mob) + safety = !safety + icon_state = "[sprite_name][!safety]" + desc = "The safety is [safety ? "on" : "off"]." + to_chat(user, "The safety is [safety ? "on" : "off"].") + +/obj/item/weapon/extinguisher/proc/propel_object(var/obj/O, mob/user, movementdirection) + if(O.anchored) return + + var/obj/structure/bed/chair/C + if(istype(O, /obj/structure/bed/chair)) + C = O + + var/list/move_speed = list(1, 1, 1, 2, 2, 3) + for(var/i in 1 to 6) + if(C) C.propelled = (6-i) + O.Move(get_step(user,movementdirection), movementdirection) + sleep(move_speed[i]) + + //additional movement + for(var/i in 1 to 3) + O.Move(get_step(user,movementdirection), movementdirection) + sleep(3) + +/obj/item/weapon/extinguisher/afterattack(var/atom/target, var/mob/user, var/flag) + //TODO; Add support for reagents in water. + + if( istype(target, /obj/structure/reagent_dispensers) && flag) + var/obj/o = target + var/amount = o.reagents.trans_to_obj(src, 50) + to_chat(user, "You fill [src] with [amount] units of the contents of [target].") + playsound(src, 'sound/effects/refill.ogg', 50, 1, -6) + return + + if (!safety) + if (src.reagents.total_volume < 1) + to_chat(usr, "\The [src] is empty.") + return + + if (world.time < src.last_use + 20) + return + + src.last_use = world.time + + playsound(src, 'sound/effects/extinguish.ogg', 75, 1, -3) + + var/direction = get_dir(src,target) + + if(user.buckled && isobj(user.buckled)) + spawn(0) + propel_object(user.buckled, user, turn(direction,180)) + + var/turf/T = get_turf(target) + var/turf/T1 = get_step(T,turn(direction, 90)) + var/turf/T2 = get_step(T,turn(direction, -90)) + + var/list/the_targets = list(T,T1,T2) + + for(var/a = 1 to spray_particles) + spawn(0) + if(!src || !reagents.total_volume) return + + var/obj/effect/effect/water/W = new /obj/effect/effect/water(get_turf(src)) + var/turf/my_target + if(a <= the_targets.len) + my_target = the_targets[a] + else + my_target = pick(the_targets) + W.create_reagents(spray_amount) + reagents.trans_to_obj(W, spray_amount) + W.set_color() + W.set_up(my_target) + + if((istype(usr.loc, /turf/space)) || (usr.lastarea.has_gravity == 0)) + user.inertia_dir = get_dir(target, user) + step(user, user.inertia_dir) + else + return ..() + return diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm index 287a174fbf3..35dd4bfdf65 100644 --- a/code/game/objects/items/weapons/flamethrower.dm +++ b/code/game/objects/items/weapons/flamethrower.dm @@ -1,204 +1,204 @@ -/obj/item/weapon/flamethrower - name = "flamethrower" - desc = "You are a firestarter!" - icon = 'icons/obj/flamethrower.dmi' - icon_state = "flamethrowerbase" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_guns.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_guns.dmi', - ) - item_state = "flamethrower_0" - force = 3.0 - throwforce = 10.0 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_COMBAT = 1, TECH_PHORON = 1) - matter = list(MAT_STEEL = 500) - var/status = 0 - var/throw_amount = 100 - var/lit = 0 //on or off - var/operating = 0//cooldown - var/turf/previousturf = null - var/obj/item/weapon/weldingtool/weldtool = null - var/obj/item/device/assembly/igniter/igniter = null - var/obj/item/weapon/tank/phoron/ptank = null - - -/obj/item/weapon/flamethrower/Destroy() - QDEL_NULL(weldtool) - QDEL_NULL(igniter) - QDEL_NULL(ptank) - . = ..() - -/obj/item/weapon/flamethrower/process() - if(!lit) - STOP_PROCESSING(SSobj, src) - return null - var/turf/location = loc - if(istype(location, /mob/)) - var/mob/living/M = location - if(M.item_is_in_hands(src)) - location = M.loc - if(isturf(location)) //start a fire if possible - location.hotspot_expose(700, 2) - return - - -/obj/item/weapon/flamethrower/update_icon() - cut_overlays() - if(igniter) - add_overlay("+igniter[status]") - if(ptank) - add_overlay("+ptank") - if(lit) - add_overlay("+lit") - item_state = "flamethrower_1" - else - item_state = "flamethrower_0" - return - -/obj/item/weapon/flamethrower/afterattack(atom/target, mob/user, proximity) - if(!proximity) return - // Make sure our user is still holding us - if(user && user.get_active_hand() == src) - var/turf/target_turf = get_turf(target) - if(target_turf) - var/turflist = getline(user, target_turf) - flame_turf(turflist) - -/obj/item/weapon/flamethrower/attackby(obj/item/W as obj, mob/user as mob) - if(user.stat || user.restrained() || user.lying) return - if(W.has_tool_quality(TOOL_WRENCH) && !status)//Taking this apart - var/turf/T = get_turf(src) - if(weldtool) - weldtool.loc = T - weldtool = null - if(igniter) - igniter.loc = T - igniter = null - if(ptank) - ptank.loc = T - ptank = null - new /obj/item/stack/rods(T) - qdel(src) - return - - if(W.has_tool_quality(TOOL_SCREWDRIVER) && igniter && !lit) - status = !status - to_chat(user, "[igniter] is now [status ? "secured" : "unsecured"]!") - update_icon() - return - - if(isigniter(W)) - var/obj/item/device/assembly/igniter/I = W - if(I.secured) return - if(igniter) return - user.drop_item() - I.loc = src - igniter = I - update_icon() - return - - if(istype(W,/obj/item/weapon/tank/phoron)) - if(ptank) - to_chat(user, "There appears to already be a phoron tank loaded in [src]!") - return - user.drop_item() - ptank = W - W.loc = src - update_icon() - return - - ..() - return - - -/obj/item/weapon/flamethrower/attack_self(mob/user as mob) - if(user.stat || user.restrained() || user.lying) return - user.set_machine(src) - if(!ptank) - to_chat(user, "Attach a phoron tank first!") - return - var/dat = text("Flamethrower ([lit ? "Lit" : "Unlit"])
                    \n Tank Pressure: [ptank.air_contents.return_pressure()]
                    \nAmount to throw: - - - [throw_amount] + + +
                    \nRemove phorontank - Close
                    ") - user << browse(dat, "window=flamethrower;size=600x300") - onclose(user, "flamethrower") - return - - -/obj/item/weapon/flamethrower/Topic(href,href_list[]) - if(href_list["close"]) - usr.unset_machine() - usr << browse(null, "window=flamethrower") - return - if(usr.stat || usr.restrained() || usr.lying) return - usr.set_machine(src) - if(href_list["light"]) - if(!ptank) return - if(ptank.air_contents.gas["phoron"] < 1) return - if(!status) return - lit = !lit - if(lit) - START_PROCESSING(SSobj, src) - if(href_list["amount"]) - throw_amount = throw_amount + text2num(href_list["amount"]) - throw_amount = max(50, min(5000, throw_amount)) - if(href_list["remove"]) - if(!ptank) return - usr.put_in_hands(ptank) - ptank = null - lit = 0 - usr.unset_machine() - usr << browse(null, "window=flamethrower") - for(var/mob/M in viewers(1, loc)) - if((M.client && M.machine == src)) - attack_self(M) - update_icon() - return - - -//Called from turf.dm turf/dblclick -/obj/item/weapon/flamethrower/proc/flame_turf(turflist) - if(!lit || operating) return - operating = 1 - for(var/turf/T in turflist) - if(T.density || istype(T, /turf/space)) - break - if(!previousturf && length(turflist)>1) - previousturf = get_turf(src) - continue //so we don't burn the tile we be standin on - if(previousturf && LinkBlocked(previousturf, T)) - break - ignite_turf(T) - sleep(1) - previousturf = null - operating = 0 - for(var/mob/M in viewers(1, loc)) - if((M.client && M.machine == src)) - attack_self(M) - return - - -/obj/item/weapon/flamethrower/proc/ignite_turf(turf/target) - //TODO: DEFERRED Consider checking to make sure tank pressure is high enough before doing this... - //Transfer 5% of current tank air contents to turf - var/datum/gas_mixture/air_transfer = ptank.air_contents.remove_ratio(0.02*(throw_amount/100)) - //air_transfer.toxins = air_transfer.toxins * 5 // This is me not comprehending the air system. I realize this is mischievious and I could probably make it work without fucking it up like this, but there you have it. -- TLE - new/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(target,air_transfer.gas["phoron"],get_dir(loc,target)) - air_transfer.gas["phoron"] = 0 - target.assume_air(air_transfer) - //Burn it based on transfered gas - //target.hotspot_expose(part4.air_contents.temperature*2,300) - target.hotspot_expose((ptank.air_contents.temperature*2) + 380,500) // -- More of my "how do I shot fire?" dickery. -- TLE - //location.hotspot_expose(1000,500,1) - return - -/obj/item/weapon/flamethrower/full/New(var/loc) - ..() - weldtool = new /obj/item/weapon/weldingtool(src) - weldtool.status = 0 - igniter = new /obj/item/device/assembly/igniter(src) - igniter.secured = 0 - status = 1 - update_icon() - return +/obj/item/weapon/flamethrower + name = "flamethrower" + desc = "You are a firestarter!" + icon = 'icons/obj/flamethrower.dmi' + icon_state = "flamethrowerbase" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_guns.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_guns.dmi', + ) + item_state = "flamethrower_0" + force = 3.0 + throwforce = 10.0 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_COMBAT = 1, TECH_PHORON = 1) + matter = list(MAT_STEEL = 500) + var/status = 0 + var/throw_amount = 100 + var/lit = 0 //on or off + var/operating = 0//cooldown + var/turf/previousturf = null + var/obj/item/weapon/weldingtool/weldtool = null + var/obj/item/device/assembly/igniter/igniter = null + var/obj/item/weapon/tank/phoron/ptank = null + + +/obj/item/weapon/flamethrower/Destroy() + QDEL_NULL(weldtool) + QDEL_NULL(igniter) + QDEL_NULL(ptank) + . = ..() + +/obj/item/weapon/flamethrower/process() + if(!lit) + STOP_PROCESSING(SSobj, src) + return null + var/turf/location = loc + if(istype(location, /mob/)) + var/mob/living/M = location + if(M.item_is_in_hands(src)) + location = M.loc + if(isturf(location)) //start a fire if possible + location.hotspot_expose(700, 2) + return + + +/obj/item/weapon/flamethrower/update_icon() + cut_overlays() + if(igniter) + add_overlay("+igniter[status]") + if(ptank) + add_overlay("+ptank") + if(lit) + add_overlay("+lit") + item_state = "flamethrower_1" + else + item_state = "flamethrower_0" + return + +/obj/item/weapon/flamethrower/afterattack(atom/target, mob/user, proximity) + if(!proximity) return + // Make sure our user is still holding us + if(user && user.get_active_hand() == src) + var/turf/target_turf = get_turf(target) + if(target_turf) + var/turflist = getline(user, target_turf) + flame_turf(turflist) + +/obj/item/weapon/flamethrower/attackby(obj/item/W as obj, mob/user as mob) + if(user.stat || user.restrained() || user.lying) return + if(W.has_tool_quality(TOOL_WRENCH) && !status)//Taking this apart + var/turf/T = get_turf(src) + if(weldtool) + weldtool.loc = T + weldtool = null + if(igniter) + igniter.loc = T + igniter = null + if(ptank) + ptank.loc = T + ptank = null + new /obj/item/stack/rods(T) + qdel(src) + return + + if(W.has_tool_quality(TOOL_SCREWDRIVER) && igniter && !lit) + status = !status + to_chat(user, "[igniter] is now [status ? "secured" : "unsecured"]!") + update_icon() + return + + if(isigniter(W)) + var/obj/item/device/assembly/igniter/I = W + if(I.secured) return + if(igniter) return + user.drop_item() + I.loc = src + igniter = I + update_icon() + return + + if(istype(W,/obj/item/weapon/tank/phoron)) + if(ptank) + to_chat(user, "There appears to already be a phoron tank loaded in [src]!") + return + user.drop_item() + ptank = W + W.loc = src + update_icon() + return + + ..() + return + + +/obj/item/weapon/flamethrower/attack_self(mob/user as mob) + if(user.stat || user.restrained() || user.lying) return + user.set_machine(src) + if(!ptank) + to_chat(user, "Attach a phoron tank first!") + return + var/dat = text("Flamethrower ([lit ? "Lit" : "Unlit"])
                    \n Tank Pressure: [ptank.air_contents.return_pressure()]
                    \nAmount to throw: - - - [throw_amount] + + +
                    \nRemove phorontank - Close
                    ") + user << browse(dat, "window=flamethrower;size=600x300") + onclose(user, "flamethrower") + return + + +/obj/item/weapon/flamethrower/Topic(href,href_list[]) + if(href_list["close"]) + usr.unset_machine() + usr << browse(null, "window=flamethrower") + return + if(usr.stat || usr.restrained() || usr.lying) return + usr.set_machine(src) + if(href_list["light"]) + if(!ptank) return + if(ptank.air_contents.gas["phoron"] < 1) return + if(!status) return + lit = !lit + if(lit) + START_PROCESSING(SSobj, src) + if(href_list["amount"]) + throw_amount = throw_amount + text2num(href_list["amount"]) + throw_amount = max(50, min(5000, throw_amount)) + if(href_list["remove"]) + if(!ptank) return + usr.put_in_hands(ptank) + ptank = null + lit = 0 + usr.unset_machine() + usr << browse(null, "window=flamethrower") + for(var/mob/M in viewers(1, loc)) + if((M.client && M.machine == src)) + attack_self(M) + update_icon() + return + + +//Called from turf.dm turf/dblclick +/obj/item/weapon/flamethrower/proc/flame_turf(turflist) + if(!lit || operating) return + operating = 1 + for(var/turf/T in turflist) + if(T.density || istype(T, /turf/space)) + break + if(!previousturf && length(turflist)>1) + previousturf = get_turf(src) + continue //so we don't burn the tile we be standin on + if(previousturf && LinkBlocked(previousturf, T)) + break + ignite_turf(T) + sleep(1) + previousturf = null + operating = 0 + for(var/mob/M in viewers(1, loc)) + if((M.client && M.machine == src)) + attack_self(M) + return + + +/obj/item/weapon/flamethrower/proc/ignite_turf(turf/target) + //TODO: DEFERRED Consider checking to make sure tank pressure is high enough before doing this... + //Transfer 5% of current tank air contents to turf + var/datum/gas_mixture/air_transfer = ptank.air_contents.remove_ratio(0.02*(throw_amount/100)) + //air_transfer.toxins = air_transfer.toxins * 5 // This is me not comprehending the air system. I realize this is mischievious and I could probably make it work without fucking it up like this, but there you have it. -- TLE + new/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(target,air_transfer.gas["phoron"],get_dir(loc,target)) + air_transfer.gas["phoron"] = 0 + target.assume_air(air_transfer) + //Burn it based on transfered gas + //target.hotspot_expose(part4.air_contents.temperature*2,300) + target.hotspot_expose((ptank.air_contents.temperature*2) + 380,500) // -- More of my "how do I shot fire?" dickery. -- TLE + //location.hotspot_expose(1000,500,1) + return + +/obj/item/weapon/flamethrower/full/New(var/loc) + ..() + weldtool = new /obj/item/weapon/weldingtool(src) + weldtool.status = 0 + igniter = new /obj/item/device/assembly/igniter(src) + igniter.secured = 0 + status = 1 + update_icon() + return diff --git a/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm b/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm index fe4d80418dd..5a2aad6ac3d 100644 --- a/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm +++ b/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm @@ -1,22 +1,22 @@ -/obj/item/weapon/grenade/anti_photon - desc = "An experimental device for temporarily removing light in a limited area." - name = "photon disruption grenade" - icon = 'icons/obj/grenade.dmi' - icon_state = "emp" - det_time = 20 - origin_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 4) - -/obj/item/weapon/grenade/anti_photon/detonate() - playsound(src, 'sound/effects/phasein.ogg', 50, 1, 5) - set_light(10, -10, "#FFFFFF") - - var/extra_delay = rand(0,90) - - spawn(extra_delay) - spawn(200) - if(prob(10+extra_delay)) - set_light(10, 10, "#[num2hex(rand(64,255))][num2hex(rand(64,255))][num2hex(rand(64,255))]") - spawn(210) - ..() - playsound(src, 'sound/effects/bang.ogg', 50, 1, 5) - qdel(src) +/obj/item/weapon/grenade/anti_photon + desc = "An experimental device for temporarily removing light in a limited area." + name = "photon disruption grenade" + icon = 'icons/obj/grenade.dmi' + icon_state = "emp" + det_time = 20 + origin_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 4) + +/obj/item/weapon/grenade/anti_photon/detonate() + playsound(src, 'sound/effects/phasein.ogg', 50, 1, 5) + set_light(10, -10, "#FFFFFF") + + var/extra_delay = rand(0,90) + + spawn(extra_delay) + spawn(200) + if(prob(10+extra_delay)) + set_light(10, 10, "#[num2hex(rand(64,255))][num2hex(rand(64,255))][num2hex(rand(64,255))]") + spawn(210) + ..() + playsound(src, 'sound/effects/bang.ogg', 50, 1, 5) + qdel(src) diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index c0c08d94fc4..84fade96cff 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -1,316 +1,316 @@ -/obj/item/weapon/grenade/chem_grenade - name = "grenade casing" - icon_state = "chemg" - item_state = "grenade" - desc = "A hand made chemical grenade." - w_class = ITEMSIZE_SMALL - force = 2.0 - det_time = null - unacidable = TRUE - - var/stage = 0 - var/state = 0 - var/path = 0 - /// If TRUE, grenade is permanently sealed when fully assembled, useful for things like off-the-shelf grenades. - var/sealed = FALSE - var/obj/item/device/assembly_holder/detonator = null - var/list/beakers = new/list() - var/list/allowed_containers = list(/obj/item/weapon/reagent_containers/glass/beaker, /obj/item/weapon/reagent_containers/glass/bottle) - var/affected_area = 3 - -/obj/item/weapon/grenade/chem_grenade/Initialize() - . = ..() - create_reagents(1000) - -/obj/item/weapon/grenade/chem_grenade/Destroy() - QDEL_NULL(detonator) - QDEL_LIST_NULL(beakers) - return ..() - -/obj/item/weapon/grenade/chem_grenade/attack_self(mob/user as mob) - if(!stage || stage==1) - if(detonator) -// detonator.loc=src.loc - detonator.detached() - usr.put_in_hands(detonator) - detonator=null - det_time = null - stage=0 - icon_state = initial(icon_state) - else if(beakers.len) - for(var/obj/B in beakers) - if(istype(B)) - beakers -= B - user.put_in_hands(B) - name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" - if(stage > 1 && !active && clown_check(user)) - to_chat(user, "You prime \the [name]!") - - msg_admin_attack("[key_name_admin(user)] primed \a [src]") - - activate() - add_fingerprint(user) - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.throw_mode_on() - -/obj/item/weapon/grenade/chem_grenade/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W,/obj/item/device/assembly_holder) && (!stage || stage==1) && !detonator && path != 2) - var/obj/item/device/assembly_holder/det = W - if(istype(det.a_left,det.a_right.type) || (!isigniter(det.a_left) && !isigniter(det.a_right))) - to_chat(user, "Assembly must contain one igniter.") - return - if(!det.secured) - to_chat(user, "Assembly must be secured with screwdriver.") - return - path = 1 - to_chat(user, "You add [W] to the metal casing.") - playsound(src, 'sound/items/Screwdriver2.ogg', 25, -3) - user.remove_from_mob(det) - det.loc = src - detonator = det - if(istimer(detonator.a_left)) - var/obj/item/device/assembly/timer/T = detonator.a_left - det_time = 10*T.time - if(istimer(detonator.a_right)) - var/obj/item/device/assembly/timer/T = detonator.a_right - det_time = 10*T.time - icon_state = initial(icon_state) +"_ass" - name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" - stage = 1 - else if(W.has_tool_quality(TOOL_SCREWDRIVER) && path != 2) - if(stage == 1) - path = 1 - if(beakers.len) - to_chat(user, "You lock the assembly.") - name = "grenade" - else -// to_chat(user, "You need to add at least one beaker before locking the assembly.") - to_chat(user, "You lock the empty assembly.") - name = "fake grenade" - playsound(src, W.usesound, 50, 1) - icon_state = initial(icon_state) +"_locked" - stage = 2 - else if(stage == 2) - if(active && prob(95)) - to_chat(user, "You trigger the assembly!") - detonate() - else if(sealed) - to_chat(user, "This grenade lacks a way to disassemble it.") - else - to_chat(user, "You unlock the assembly.") - playsound(src, W.usesound, 50, -3) - name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" - icon_state = initial(icon_state) + (detonator?"_ass":"") - stage = 1 - active = 0 - else if(is_type_in_list(W, allowed_containers) && (!stage || stage==1) && path != 2) - path = 1 - if(beakers.len == 2) - to_chat(user, "The grenade can not hold more containers.") - return - else - if(W.reagents.total_volume) - to_chat(user, "You add \the [W] to the assembly.") - user.drop_item() - W.loc = src - beakers += W - stage = 1 - name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" - else - to_chat(user, "\The [W] is empty.") - -/obj/item/weapon/grenade/chem_grenade/examine(mob/user) - . = ..() - if(detonator) - . += "It has [detonator.name] attached to it." - -/obj/item/weapon/grenade/chem_grenade/activate(mob/user as mob) - if(active) return - - if(detonator) - if(!isigniter(detonator.a_left)) - detonator.a_left.activate() - active = 1 - if(!isigniter(detonator.a_right)) - detonator.a_right.activate() - active = 1 - if(active) - icon_state = initial(icon_state) + "_active" - - if(user) - msg_admin_attack("[key_name_admin(user)] primed \a [src.name]") - - return - -/obj/item/weapon/grenade/chem_grenade/proc/primed(var/primed = 1) - if(active) - icon_state = initial(icon_state) + (primed?"_primed":"_active") - -/obj/item/weapon/grenade/chem_grenade/detonate() - if(!stage || stage<2) return - - var/has_reagents = 0 - for(var/obj/item/weapon/reagent_containers/glass/G in beakers) - if(G.reagents.total_volume) has_reagents = 1 - - active = 0 - if(!has_reagents) - icon_state = initial(icon_state) +"_locked" - playsound(src, 'sound/items/Screwdriver2.ogg', 50, 1) - spawn(0) //Otherwise det_time is erroneously set to 0 after this - if(istimer(detonator.a_left)) //Make sure description reflects that the timer has been reset - var/obj/item/device/assembly/timer/T = detonator.a_left - det_time = 10*T.time - if(istimer(detonator.a_right)) - var/obj/item/device/assembly/timer/T = detonator.a_right - det_time = 10*T.time - return - - playsound(src, 'sound/effects/bamf.ogg', 50, 1) - - for(var/obj/item/weapon/reagent_containers/glass/G in beakers) - G.reagents.trans_to_obj(src, G.reagents.total_volume) - - if(src.reagents.total_volume) //The possible reactions didnt use up all reagents. - var/datum/effect/effect/system/steam_spread/steam = new /datum/effect/effect/system/steam_spread() - steam.set_up(10, 0, get_turf(src)) - steam.attach(src) - steam.start() - - for(var/atom/A in view(affected_area, src.loc)) - if( A == src ) continue - src.reagents.touch(A) - - if(istype(loc, /mob/living/carbon)) //drop dat grenade if it goes off in your hand - var/mob/living/carbon/C = loc - C.drop_from_inventory(src) - C.throw_mode_off() - - invisibility = INVISIBILITY_MAXIMUM //Why am i doing this? - spawn(50) //To make sure all reagents can work - qdel(src) //correctly before deleting the grenade. - - -/obj/item/weapon/grenade/chem_grenade/large - name = "large chem grenade" - desc = "An oversized grenade that affects a larger area." - icon_state = "large_grenade" - allowed_containers = list(/obj/item/weapon/reagent_containers/glass) - origin_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 3) - affected_area = 4 - -/obj/item/weapon/grenade/chem_grenade/metalfoam - name = "metal-foam grenade" - desc = "Used for emergency sealing of air breaches." - icon_state = "foam" - path = 1 - stage = 2 - sealed = TRUE - -/obj/item/weapon/grenade/chem_grenade/metalfoam/Initialize() - . = ..() - var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) - var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) - - B1.reagents.add_reagent("aluminum", 30) - B2.reagents.add_reagent("foaming_agent", 10) - B2.reagents.add_reagent("pacid", 10) - - detonator = new/obj/item/device/assembly_holder/timer_igniter(src) - - beakers += B1 - beakers += B2 - -/obj/item/weapon/grenade/chem_grenade/incendiary - name = "incendiary grenade" - desc = "Used for clearing rooms of living things." - icon_state = "incendiary" - path = 1 - stage = 2 - sealed = TRUE - -/obj/item/weapon/grenade/chem_grenade/incendiary/Initialize() - . = ..() - var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) - var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) - - B1.reagents.add_reagent("aluminum", 15) - B1.reagents.add_reagent("fuel",20) - B2.reagents.add_reagent("phoron", 15) - B2.reagents.add_reagent("sacid", 15) - B1.reagents.add_reagent("fuel",20) - - detonator = new/obj/item/device/assembly_holder/timer_igniter(src) - - beakers += B1 - beakers += B2 - -/obj/item/weapon/grenade/chem_grenade/antiweed - name = "weedkiller grenade" - desc = "Used for purging large areas of invasive plant species. Contents under pressure. Do not directly inhale contents." - path = 1 - stage = 2 - sealed = TRUE - -/obj/item/weapon/grenade/chem_grenade/antiweed/Initialize() - . = ..() - var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) - var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) - - B1.reagents.add_reagent("plantbgone", 25) - B1.reagents.add_reagent("potassium", 25) - B2.reagents.add_reagent("phosphorus", 25) - B2.reagents.add_reagent("sugar", 25) - - detonator = new/obj/item/device/assembly_holder/timer_igniter(src) - - beakers += B1 - beakers += B2 - icon_state = "grenade" - -/obj/item/weapon/grenade/chem_grenade/cleaner - name = "cleaner grenade" - desc = "BLAM!-brand foaming space cleaner. In a special applicator for rapid cleaning of wide areas." - icon_state = "cleaner" - stage = 2 - path = 1 - sealed = TRUE - -/obj/item/weapon/grenade/chem_grenade/cleaner/Initialize() - . = ..() - var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) - var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) - - B1.reagents.add_reagent("fluorosurfactant", 40) - B2.reagents.add_reagent("water", 40) - B2.reagents.add_reagent("cleaner", 10) - - detonator = new/obj/item/device/assembly_holder/timer_igniter(src) - - beakers += B1 - beakers += B2 - -/obj/item/weapon/grenade/chem_grenade/teargas - name = "tear gas grenade" - desc = "Concentrated Capsaicin. Contents under pressure. Use with caution." - icon_state = "teargas" - stage = 2 - path = 1 - sealed = TRUE - -/obj/item/weapon/grenade/chem_grenade/teargas/Initialize() - . = ..() - var/obj/item/weapon/reagent_containers/glass/beaker/large/B1 = new(src) - var/obj/item/weapon/reagent_containers/glass/beaker/large/B2 = new(src) - - B1.reagents.add_reagent("phosphorus", 40) - B1.reagents.add_reagent("potassium", 40) - B1.reagents.add_reagent("condensedcapsaicin", 40) - B2.reagents.add_reagent("sugar", 40) - B2.reagents.add_reagent("condensedcapsaicin", 80) - - detonator = new/obj/item/device/assembly_holder/timer_igniter(src) - - beakers += B1 - beakers += B2 +/obj/item/weapon/grenade/chem_grenade + name = "grenade casing" + icon_state = "chemg" + item_state = "grenade" + desc = "A hand made chemical grenade." + w_class = ITEMSIZE_SMALL + force = 2.0 + det_time = null + unacidable = TRUE + + var/stage = 0 + var/state = 0 + var/path = 0 + /// If TRUE, grenade is permanently sealed when fully assembled, useful for things like off-the-shelf grenades. + var/sealed = FALSE + var/obj/item/device/assembly_holder/detonator = null + var/list/beakers = new/list() + var/list/allowed_containers = list(/obj/item/weapon/reagent_containers/glass/beaker, /obj/item/weapon/reagent_containers/glass/bottle) + var/affected_area = 3 + +/obj/item/weapon/grenade/chem_grenade/Initialize() + . = ..() + create_reagents(1000) + +/obj/item/weapon/grenade/chem_grenade/Destroy() + QDEL_NULL(detonator) + QDEL_LIST_NULL(beakers) + return ..() + +/obj/item/weapon/grenade/chem_grenade/attack_self(mob/user as mob) + if(!stage || stage==1) + if(detonator) +// detonator.loc=src.loc + detonator.detached() + usr.put_in_hands(detonator) + detonator=null + det_time = null + stage=0 + icon_state = initial(icon_state) + else if(beakers.len) + for(var/obj/B in beakers) + if(istype(B)) + beakers -= B + user.put_in_hands(B) + name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" + if(stage > 1 && !active && clown_check(user)) + to_chat(user, "You prime \the [name]!") + + msg_admin_attack("[key_name_admin(user)] primed \a [src]") + + activate() + add_fingerprint(user) + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.throw_mode_on() + +/obj/item/weapon/grenade/chem_grenade/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W,/obj/item/device/assembly_holder) && (!stage || stage==1) && !detonator && path != 2) + var/obj/item/device/assembly_holder/det = W + if(istype(det.a_left,det.a_right.type) || (!isigniter(det.a_left) && !isigniter(det.a_right))) + to_chat(user, "Assembly must contain one igniter.") + return + if(!det.secured) + to_chat(user, "Assembly must be secured with screwdriver.") + return + path = 1 + to_chat(user, "You add [W] to the metal casing.") + playsound(src, 'sound/items/Screwdriver2.ogg', 25, -3) + user.remove_from_mob(det) + det.loc = src + detonator = det + if(istimer(detonator.a_left)) + var/obj/item/device/assembly/timer/T = detonator.a_left + det_time = 10*T.time + if(istimer(detonator.a_right)) + var/obj/item/device/assembly/timer/T = detonator.a_right + det_time = 10*T.time + icon_state = initial(icon_state) +"_ass" + name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" + stage = 1 + else if(W.has_tool_quality(TOOL_SCREWDRIVER) && path != 2) + if(stage == 1) + path = 1 + if(beakers.len) + to_chat(user, "You lock the assembly.") + name = "grenade" + else +// to_chat(user, "You need to add at least one beaker before locking the assembly.") + to_chat(user, "You lock the empty assembly.") + name = "fake grenade" + playsound(src, W.usesound, 50, 1) + icon_state = initial(icon_state) +"_locked" + stage = 2 + else if(stage == 2) + if(active && prob(95)) + to_chat(user, "You trigger the assembly!") + detonate() + else if(sealed) + to_chat(user, "This grenade lacks a way to disassemble it.") + else + to_chat(user, "You unlock the assembly.") + playsound(src, W.usesound, 50, -3) + name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" + icon_state = initial(icon_state) + (detonator?"_ass":"") + stage = 1 + active = 0 + else if(is_type_in_list(W, allowed_containers) && (!stage || stage==1) && path != 2) + path = 1 + if(beakers.len == 2) + to_chat(user, "The grenade can not hold more containers.") + return + else + if(W.reagents.total_volume) + to_chat(user, "You add \the [W] to the assembly.") + user.drop_item() + W.loc = src + beakers += W + stage = 1 + name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" + else + to_chat(user, "\The [W] is empty.") + +/obj/item/weapon/grenade/chem_grenade/examine(mob/user) + . = ..() + if(detonator) + . += "It has [detonator.name] attached to it." + +/obj/item/weapon/grenade/chem_grenade/activate(mob/user as mob) + if(active) return + + if(detonator) + if(!isigniter(detonator.a_left)) + detonator.a_left.activate() + active = 1 + if(!isigniter(detonator.a_right)) + detonator.a_right.activate() + active = 1 + if(active) + icon_state = initial(icon_state) + "_active" + + if(user) + msg_admin_attack("[key_name_admin(user)] primed \a [src.name]") + + return + +/obj/item/weapon/grenade/chem_grenade/proc/primed(var/primed = 1) + if(active) + icon_state = initial(icon_state) + (primed?"_primed":"_active") + +/obj/item/weapon/grenade/chem_grenade/detonate() + if(!stage || stage<2) return + + var/has_reagents = 0 + for(var/obj/item/weapon/reagent_containers/glass/G in beakers) + if(G.reagents.total_volume) has_reagents = 1 + + active = 0 + if(!has_reagents) + icon_state = initial(icon_state) +"_locked" + playsound(src, 'sound/items/Screwdriver2.ogg', 50, 1) + spawn(0) //Otherwise det_time is erroneously set to 0 after this + if(istimer(detonator.a_left)) //Make sure description reflects that the timer has been reset + var/obj/item/device/assembly/timer/T = detonator.a_left + det_time = 10*T.time + if(istimer(detonator.a_right)) + var/obj/item/device/assembly/timer/T = detonator.a_right + det_time = 10*T.time + return + + playsound(src, 'sound/effects/bamf.ogg', 50, 1) + + for(var/obj/item/weapon/reagent_containers/glass/G in beakers) + G.reagents.trans_to_obj(src, G.reagents.total_volume) + + if(src.reagents.total_volume) //The possible reactions didnt use up all reagents. + var/datum/effect/effect/system/steam_spread/steam = new /datum/effect/effect/system/steam_spread() + steam.set_up(10, 0, get_turf(src)) + steam.attach(src) + steam.start() + + for(var/atom/A in view(affected_area, src.loc)) + if( A == src ) continue + src.reagents.touch(A) + + if(istype(loc, /mob/living/carbon)) //drop dat grenade if it goes off in your hand + var/mob/living/carbon/C = loc + C.drop_from_inventory(src) + C.throw_mode_off() + + invisibility = INVISIBILITY_MAXIMUM //Why am i doing this? + spawn(50) //To make sure all reagents can work + qdel(src) //correctly before deleting the grenade. + + +/obj/item/weapon/grenade/chem_grenade/large + name = "large chem grenade" + desc = "An oversized grenade that affects a larger area." + icon_state = "large_grenade" + allowed_containers = list(/obj/item/weapon/reagent_containers/glass) + origin_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 3) + affected_area = 4 + +/obj/item/weapon/grenade/chem_grenade/metalfoam + name = "metal-foam grenade" + desc = "Used for emergency sealing of air breaches." + icon_state = "foam" + path = 1 + stage = 2 + sealed = TRUE + +/obj/item/weapon/grenade/chem_grenade/metalfoam/Initialize() + . = ..() + var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) + var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) + + B1.reagents.add_reagent("aluminum", 30) + B2.reagents.add_reagent("foaming_agent", 10) + B2.reagents.add_reagent("pacid", 10) + + detonator = new/obj/item/device/assembly_holder/timer_igniter(src) + + beakers += B1 + beakers += B2 + +/obj/item/weapon/grenade/chem_grenade/incendiary + name = "incendiary grenade" + desc = "Used for clearing rooms of living things." + icon_state = "incendiary" + path = 1 + stage = 2 + sealed = TRUE + +/obj/item/weapon/grenade/chem_grenade/incendiary/Initialize() + . = ..() + var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) + var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) + + B1.reagents.add_reagent("aluminum", 15) + B1.reagents.add_reagent("fuel",20) + B2.reagents.add_reagent("phoron", 15) + B2.reagents.add_reagent("sacid", 15) + B1.reagents.add_reagent("fuel",20) + + detonator = new/obj/item/device/assembly_holder/timer_igniter(src) + + beakers += B1 + beakers += B2 + +/obj/item/weapon/grenade/chem_grenade/antiweed + name = "weedkiller grenade" + desc = "Used for purging large areas of invasive plant species. Contents under pressure. Do not directly inhale contents." + path = 1 + stage = 2 + sealed = TRUE + +/obj/item/weapon/grenade/chem_grenade/antiweed/Initialize() + . = ..() + var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) + var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) + + B1.reagents.add_reagent("plantbgone", 25) + B1.reagents.add_reagent("potassium", 25) + B2.reagents.add_reagent("phosphorus", 25) + B2.reagents.add_reagent("sugar", 25) + + detonator = new/obj/item/device/assembly_holder/timer_igniter(src) + + beakers += B1 + beakers += B2 + icon_state = "grenade" + +/obj/item/weapon/grenade/chem_grenade/cleaner + name = "cleaner grenade" + desc = "BLAM!-brand foaming space cleaner. In a special applicator for rapid cleaning of wide areas." + icon_state = "cleaner" + stage = 2 + path = 1 + sealed = TRUE + +/obj/item/weapon/grenade/chem_grenade/cleaner/Initialize() + . = ..() + var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) + var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) + + B1.reagents.add_reagent("fluorosurfactant", 40) + B2.reagents.add_reagent("water", 40) + B2.reagents.add_reagent("cleaner", 10) + + detonator = new/obj/item/device/assembly_holder/timer_igniter(src) + + beakers += B1 + beakers += B2 + +/obj/item/weapon/grenade/chem_grenade/teargas + name = "tear gas grenade" + desc = "Concentrated Capsaicin. Contents under pressure. Use with caution." + icon_state = "teargas" + stage = 2 + path = 1 + sealed = TRUE + +/obj/item/weapon/grenade/chem_grenade/teargas/Initialize() + . = ..() + var/obj/item/weapon/reagent_containers/glass/beaker/large/B1 = new(src) + var/obj/item/weapon/reagent_containers/glass/beaker/large/B2 = new(src) + + B1.reagents.add_reagent("phosphorus", 40) + B1.reagents.add_reagent("potassium", 40) + B1.reagents.add_reagent("condensedcapsaicin", 40) + B2.reagents.add_reagent("sugar", 40) + B2.reagents.add_reagent("condensedcapsaicin", 80) + + detonator = new/obj/item/device/assembly_holder/timer_igniter(src) + + beakers += B1 + beakers += B2 diff --git a/code/game/objects/items/weapons/grenades/emgrenade.dm b/code/game/objects/items/weapons/grenades/emgrenade.dm index 26c68b0f204..89aff71bb37 100644 --- a/code/game/objects/items/weapons/grenades/emgrenade.dm +++ b/code/game/objects/items/weapons/grenades/emgrenade.dm @@ -1,25 +1,25 @@ -/obj/item/weapon/grenade/empgrenade - name = "emp grenade" - icon_state = "emp" - item_state = "empgrenade" - origin_tech = list(TECH_MATERIAL = 2, TECH_MAGNET = 3) - var/emp_heavy = 2 - var/emp_med = 4 - var/emp_light = 7 - var/emp_long = 10 - -/obj/item/weapon/grenade/empgrenade/detonate() - ..() - if(empulse(src, emp_heavy, emp_med, emp_light, emp_long)) - qdel(src) - return - -/obj/item/weapon/grenade/empgrenade/low_yield - name = "low yield emp grenade" - desc = "A weaker variant of the EMP grenade" - icon_state = "lyemp" - origin_tech = list(TECH_MATERIAL = 2, TECH_MAGNET = 3) - emp_heavy = 1 - emp_med = 2 - emp_light = 3 +/obj/item/weapon/grenade/empgrenade + name = "emp grenade" + icon_state = "emp" + item_state = "empgrenade" + origin_tech = list(TECH_MATERIAL = 2, TECH_MAGNET = 3) + var/emp_heavy = 2 + var/emp_med = 4 + var/emp_light = 7 + var/emp_long = 10 + +/obj/item/weapon/grenade/empgrenade/detonate() + ..() + if(empulse(src, emp_heavy, emp_med, emp_light, emp_long)) + qdel(src) + return + +/obj/item/weapon/grenade/empgrenade/low_yield + name = "low yield emp grenade" + desc = "A weaker variant of the EMP grenade" + icon_state = "lyemp" + origin_tech = list(TECH_MATERIAL = 2, TECH_MAGNET = 3) + emp_heavy = 1 + emp_med = 2 + emp_light = 3 emp_long = 4 \ No newline at end of file diff --git a/code/game/objects/items/weapons/grenades/flashbang.dm b/code/game/objects/items/weapons/grenades/flashbang.dm index b176563b268..d407e91cd72 100644 --- a/code/game/objects/items/weapons/grenades/flashbang.dm +++ b/code/game/objects/items/weapons/grenades/flashbang.dm @@ -1,165 +1,165 @@ -/obj/item/weapon/grenade/flashbang - name = "flashbang" - icon_state = "flashbang" - item_state = "flashbang" - origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 1) - var/max_range = 10 //The maximum range possible, including species effect mods. Cuts off at 7 for normal humans. Should be 3 higher than your intended target range for affecting normal humans. - var/banglet = 0 - -/obj/item/weapon/grenade/flashbang/detonate() - ..() - for(var/obj/structure/closet/L in hear(max_range, get_turf(src))) - if(locate(/mob/living/carbon/, L)) - for(var/mob/living/carbon/M in L) - bang(get_turf(src), M) - - for(var/mob/living/carbon/M in hear(max_range, get_turf(src))) - bang(get_turf(src), M) - - for(var/obj/structure/blob/B in hear(max_range - 2,get_turf(src))) //Blob damage here - var/damage = round(30/(get_dist(B,get_turf(src))+1)) - if(B.overmind) - damage *= B.overmind.blob_type.burn_multiplier - B.adjust_integrity(-damage) - - new/obj/effect/effect/sparks(src.loc) - new/obj/effect/effect/smoke/illumination(src.loc, 5, range=30, power=30, color="#FFFFFF") - - qdel(src) - -/obj/item/weapon/grenade/flashbang/proc/bang(var/turf/T , var/mob/living/carbon/M) // Added a new proc called 'bang' that takes a location and a person to be banged. - to_chat(M, "BANG") // Called during the loop that bangs people in lockers/containers and when banging - playsound(src, 'sound/effects/bang.ogg', 50, 1, 30) // people in normal view. Could theroetically be called during other explosions. - // -- Polymorph - - //Checking for protections - var/eye_safety = 0 - var/ear_safety = 0 - if(iscarbon(M)) - eye_safety = M.eyecheck() - ear_safety = M.get_ear_protection() - - //Flashing everyone - var/mob/living/carbon/human/H = M - var/flash_effectiveness = 1 - var/bang_effectiveness = 1 - if(ishuman(M)) - flash_effectiveness = H.species.flash_mod - bang_effectiveness = H.species.sound_mod - if(eye_safety < 1 && get_dist(M, T) <= round(max_range * 0.7 * flash_effectiveness)) - M.flash_eyes() - M.Confuse(2 * flash_effectiveness) - M.Weaken(5 * flash_effectiveness) - - //Now applying sound - if((get_dist(M, T) <= round(max_range * 0.3 * bang_effectiveness) || src.loc == M.loc || src.loc == M)) - if(ear_safety > 0) - M.Confuse(2) - M.Weaken(1) - else - M.Confuse(10) - M.Weaken(3) - if ((prob(14) || (M == src.loc && prob(70)))) - M.ear_damage += rand(1, 10) - else - M.ear_damage += rand(0, 5) - M.ear_deaf = max(M.ear_deaf,15) - - else if(get_dist(M, T) <= round(max_range * 0.5 * bang_effectiveness)) - if(!ear_safety) - M.Confuse(8) - M.ear_damage += rand(0, 3) - M.ear_deaf = max(M.ear_deaf,10) - - else if(!ear_safety && get_dist(M, T) <= (max_range * 0.7 * bang_effectiveness)) - M.Confuse(4) - M.ear_damage += rand(0, 1) - M.ear_deaf = max(M.ear_deaf,5) - - //This really should be in mob not every check - if(ishuman(M)) - var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] - if (E && E.damage >= E.min_bruised_damage) - to_chat(M, "Your eyes start to burn badly!") - if(!banglet && !(istype(src , /obj/item/weapon/grenade/flashbang/clusterbang))) - if (E.damage >= E.min_broken_damage) - to_chat(M, "You can't see anything!") - if (M.ear_damage >= 15) - to_chat(M, "Your ears start to ring badly!") - if(!banglet && !(istype(src , /obj/item/weapon/grenade/flashbang/clusterbang))) - if (prob(M.ear_damage - 10 + 5)) - to_chat(M, "You can't hear anything!") - M.sdisabilities |= DEAF - else if(M.ear_damage >= 5) - to_chat(M, "Your ears start to ring!") - -/obj/item/weapon/grenade/flashbang/Destroy() - walk(src, 0) // Because we might have called walk_away, we must stop the walk loop or BYOND keeps an internal reference to us forever. - return ..() - - -/obj/item/weapon/grenade/flashbang/clusterbang//Created by Polymorph, fixed by Sieve - desc = "Use of this weapon may constiute a war crime in your area, consult your local Site Manager." - name = "clusterbang" - icon = 'icons/obj/grenade.dmi' - icon_state = "clusterbang" - var/can_repeat = TRUE // Does this thing drop mini-clusterbangs? - var/min_banglets = 4 - var/max_banglets = 8 - -/obj/item/weapon/grenade/flashbang/clusterbang/detonate() - var/numspawned = rand(min_banglets, max_banglets) - var/again = 0 - - if(can_repeat) - for(var/more = numspawned, more > 0, more--) - if(prob(35)) - again++ - numspawned-- - - for(var/do_spawn = numspawned, do_spawn > 0, do_spawn--) - new /obj/item/weapon/grenade/flashbang/cluster(src.loc)//Launches flashbangs - playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) - - for(var/do_again = again, do_again > 0, do_again--) - new /obj/item/weapon/grenade/flashbang/clusterbang/segment(src.loc)//Creates a 'segment' that launches a few more flashbangs - playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) - qdel(src) - return - -/obj/item/weapon/grenade/flashbang/clusterbang/segment - desc = "A smaller segment of a clusterbang. Better run." - name = "clusterbang segment" - icon = 'icons/obj/grenade.dmi' - icon_state = "clusterbang_segment" - can_repeat = FALSE - banglet = TRUE - -/obj/item/weapon/grenade/flashbang/clusterbang/segment/New()//Segments should never exist except part of the clusterbang, since these immediately 'do their thing' and asplode - ..() - - icon_state = "clusterbang_segment_active" - - var/stepdist = rand(1,4)//How far to step - var/temploc = src.loc//Saves the current location to know where to step away from - walk_away(src,temploc,stepdist)//I must go, my people need me - - var/dettime = rand(15,60) - spawn(dettime) - detonate() - -/obj/item/weapon/grenade/flashbang/cluster - banglet = TRUE - -/obj/item/weapon/grenade/flashbang/cluster/New()//Same concept as the segments, so that all of the parts don't become reliant on the clusterbang - ..() - - icon_state = "flashbang_active" - - var/stepdist = rand(1,3) - var/temploc = src.loc - walk_away(src,temploc,stepdist) - - var/dettime = rand(15,60) - spawn(dettime) +/obj/item/weapon/grenade/flashbang + name = "flashbang" + icon_state = "flashbang" + item_state = "flashbang" + origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 1) + var/max_range = 10 //The maximum range possible, including species effect mods. Cuts off at 7 for normal humans. Should be 3 higher than your intended target range for affecting normal humans. + var/banglet = 0 + +/obj/item/weapon/grenade/flashbang/detonate() + ..() + for(var/obj/structure/closet/L in hear(max_range, get_turf(src))) + if(locate(/mob/living/carbon/, L)) + for(var/mob/living/carbon/M in L) + bang(get_turf(src), M) + + for(var/mob/living/carbon/M in hear(max_range, get_turf(src))) + bang(get_turf(src), M) + + for(var/obj/structure/blob/B in hear(max_range - 2,get_turf(src))) //Blob damage here + var/damage = round(30/(get_dist(B,get_turf(src))+1)) + if(B.overmind) + damage *= B.overmind.blob_type.burn_multiplier + B.adjust_integrity(-damage) + + new/obj/effect/effect/sparks(src.loc) + new/obj/effect/effect/smoke/illumination(src.loc, 5, range=30, power=30, color="#FFFFFF") + + qdel(src) + +/obj/item/weapon/grenade/flashbang/proc/bang(var/turf/T , var/mob/living/carbon/M) // Added a new proc called 'bang' that takes a location and a person to be banged. + to_chat(M, "BANG") // Called during the loop that bangs people in lockers/containers and when banging + playsound(src, 'sound/effects/bang.ogg', 50, 1, 30) // people in normal view. Could theroetically be called during other explosions. + // -- Polymorph + + //Checking for protections + var/eye_safety = 0 + var/ear_safety = 0 + if(iscarbon(M)) + eye_safety = M.eyecheck() + ear_safety = M.get_ear_protection() + + //Flashing everyone + var/mob/living/carbon/human/H = M + var/flash_effectiveness = 1 + var/bang_effectiveness = 1 + if(ishuman(M)) + flash_effectiveness = H.species.flash_mod + bang_effectiveness = H.species.sound_mod + if(eye_safety < 1 && get_dist(M, T) <= round(max_range * 0.7 * flash_effectiveness)) + M.flash_eyes() + M.Confuse(2 * flash_effectiveness) + M.Weaken(5 * flash_effectiveness) + + //Now applying sound + if((get_dist(M, T) <= round(max_range * 0.3 * bang_effectiveness) || src.loc == M.loc || src.loc == M)) + if(ear_safety > 0) + M.Confuse(2) + M.Weaken(1) + else + M.Confuse(10) + M.Weaken(3) + if ((prob(14) || (M == src.loc && prob(70)))) + M.ear_damage += rand(1, 10) + else + M.ear_damage += rand(0, 5) + M.ear_deaf = max(M.ear_deaf,15) + + else if(get_dist(M, T) <= round(max_range * 0.5 * bang_effectiveness)) + if(!ear_safety) + M.Confuse(8) + M.ear_damage += rand(0, 3) + M.ear_deaf = max(M.ear_deaf,10) + + else if(!ear_safety && get_dist(M, T) <= (max_range * 0.7 * bang_effectiveness)) + M.Confuse(4) + M.ear_damage += rand(0, 1) + M.ear_deaf = max(M.ear_deaf,5) + + //This really should be in mob not every check + if(ishuman(M)) + var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] + if (E && E.damage >= E.min_bruised_damage) + to_chat(M, "Your eyes start to burn badly!") + if(!banglet && !(istype(src , /obj/item/weapon/grenade/flashbang/clusterbang))) + if (E.damage >= E.min_broken_damage) + to_chat(M, "You can't see anything!") + if (M.ear_damage >= 15) + to_chat(M, "Your ears start to ring badly!") + if(!banglet && !(istype(src , /obj/item/weapon/grenade/flashbang/clusterbang))) + if (prob(M.ear_damage - 10 + 5)) + to_chat(M, "You can't hear anything!") + M.sdisabilities |= DEAF + else if(M.ear_damage >= 5) + to_chat(M, "Your ears start to ring!") + +/obj/item/weapon/grenade/flashbang/Destroy() + walk(src, 0) // Because we might have called walk_away, we must stop the walk loop or BYOND keeps an internal reference to us forever. + return ..() + + +/obj/item/weapon/grenade/flashbang/clusterbang//Created by Polymorph, fixed by Sieve + desc = "Use of this weapon may constiute a war crime in your area, consult your local Site Manager." + name = "clusterbang" + icon = 'icons/obj/grenade.dmi' + icon_state = "clusterbang" + var/can_repeat = TRUE // Does this thing drop mini-clusterbangs? + var/min_banglets = 4 + var/max_banglets = 8 + +/obj/item/weapon/grenade/flashbang/clusterbang/detonate() + var/numspawned = rand(min_banglets, max_banglets) + var/again = 0 + + if(can_repeat) + for(var/more = numspawned, more > 0, more--) + if(prob(35)) + again++ + numspawned-- + + for(var/do_spawn = numspawned, do_spawn > 0, do_spawn--) + new /obj/item/weapon/grenade/flashbang/cluster(src.loc)//Launches flashbangs + playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) + + for(var/do_again = again, do_again > 0, do_again--) + new /obj/item/weapon/grenade/flashbang/clusterbang/segment(src.loc)//Creates a 'segment' that launches a few more flashbangs + playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) + qdel(src) + return + +/obj/item/weapon/grenade/flashbang/clusterbang/segment + desc = "A smaller segment of a clusterbang. Better run." + name = "clusterbang segment" + icon = 'icons/obj/grenade.dmi' + icon_state = "clusterbang_segment" + can_repeat = FALSE + banglet = TRUE + +/obj/item/weapon/grenade/flashbang/clusterbang/segment/New()//Segments should never exist except part of the clusterbang, since these immediately 'do their thing' and asplode + ..() + + icon_state = "clusterbang_segment_active" + + var/stepdist = rand(1,4)//How far to step + var/temploc = src.loc//Saves the current location to know where to step away from + walk_away(src,temploc,stepdist)//I must go, my people need me + + var/dettime = rand(15,60) + spawn(dettime) + detonate() + +/obj/item/weapon/grenade/flashbang/cluster + banglet = TRUE + +/obj/item/weapon/grenade/flashbang/cluster/New()//Same concept as the segments, so that all of the parts don't become reliant on the clusterbang + ..() + + icon_state = "flashbang_active" + + var/stepdist = rand(1,3) + var/temploc = src.loc + walk_away(src,temploc,stepdist) + + var/dettime = rand(15,60) + spawn(dettime) detonate() \ No newline at end of file diff --git a/code/game/objects/items/weapons/grenades/grenade.dm b/code/game/objects/items/weapons/grenades/grenade.dm index 38565fea0dc..6bf22d153d0 100644 --- a/code/game/objects/items/weapons/grenades/grenade.dm +++ b/code/game/objects/items/weapons/grenades/grenade.dm @@ -1,119 +1,119 @@ -/obj/item/weapon/grenade - name = "grenade" - desc = "A hand held grenade, with an adjustable timer." - w_class = ITEMSIZE_SMALL - icon = 'icons/obj/grenade.dmi' - icon_state = "grenade" - item_state = "grenade" - throw_speed = 4 - throw_range = 20 - slot_flags = SLOT_MASK|SLOT_BELT - - var/active = 0 - var/det_time = 50 - var/loadable = TRUE - var/arm_sound = 'sound/weapons/armbomb.ogg' - var/hud_state = "grenade_he" // TGMC Ammo HUD Port - var/hud_state_empty = "grenade_empty" // TGMC Ammo HUD Port - -/obj/item/weapon/grenade/proc/clown_check(var/mob/living/user) - if((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "Huh? How does this thing work?") - - activate(user) - add_fingerprint(user) - spawn(5) - detonate() - return 0 - return 1 - - -/*/obj/item/weapon/grenade/afterattack(atom/target as mob|obj|turf|area, mob/user as mob) - if (istype(target, /obj/item/weapon/storage)) return ..() // Trying to put it in a full container - if (istype(target, /obj/item/weapon/gun/grenadelauncher)) return ..() - if((user.get_active_hand() == src) && (!active) && (clown_check(user)) && target.loc != src.loc) - to_chat(user, "You prime the [name]! [det_time/10] seconds!") - active = 1 - icon_state = initial(icon_state) + "_active" - playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) - spawn(det_time) - detonate() - return - user.set_dir(get_dir(user, target)) - user.drop_item() - var/t = (isturf(target) ? target : target.loc) - walk_towards(src, t, 3) - return*/ - - -/obj/item/weapon/grenade/examine(mob/user) - . = ..() - if(get_dist(user, src) == 0) - if(det_time > 1) - . += "The timer is set to [det_time/10] seconds." - else if(det_time == null) - . += "\The [src] is set for instant detonation." - - -/obj/item/weapon/grenade/attack_self(mob/user as mob) - if(!active) - if(clown_check(user)) - to_chat(user, "You prime \the [name]! [det_time/10] seconds!") - - activate(user) - add_fingerprint(user) - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.throw_mode_on() - return - - -/obj/item/weapon/grenade/proc/activate(mob/user as mob) - if(active) - return - - if(user) - msg_admin_attack("[key_name_admin(user)] primed \a [src.name]") - - icon_state = initial(icon_state) + "_active" - active = 1 - playsound(src, arm_sound, 75, 1, -3) - - spawn(det_time) - detonate() - return - - -/obj/item/weapon/grenade/proc/detonate() -// playsound(src, 'sound/items/Welder2.ogg', 25, 1) - var/turf/T = get_turf(src) - if(T) - T.hotspot_expose(700,125) - - -/obj/item/weapon/grenade/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.has_tool_quality(TOOL_SCREWDRIVER)) - switch(det_time) - if (1) - det_time = 10 - to_chat(user, "You set the [name] for 1 second detonation time.") - if (10) - det_time = 30 - to_chat(user, "You set the [name] for 3 second detonation time.") - if (30) - det_time = 50 - to_chat(user, "You set the [name] for 5 second detonation time.") - if (50) - det_time = 1 - to_chat(user, "You set the [name] for instant detonation.") - add_fingerprint(user) - ..() - return - -/obj/item/weapon/grenade/attack_hand() - walk(src, null, null) - ..() - return - -/obj/item/weapon/grenade/vendor_action(var/obj/machinery/vending/V) +/obj/item/weapon/grenade + name = "grenade" + desc = "A hand held grenade, with an adjustable timer." + w_class = ITEMSIZE_SMALL + icon = 'icons/obj/grenade.dmi' + icon_state = "grenade" + item_state = "grenade" + throw_speed = 4 + throw_range = 20 + slot_flags = SLOT_MASK|SLOT_BELT + + var/active = 0 + var/det_time = 50 + var/loadable = TRUE + var/arm_sound = 'sound/weapons/armbomb.ogg' + var/hud_state = "grenade_he" // TGMC Ammo HUD Port + var/hud_state_empty = "grenade_empty" // TGMC Ammo HUD Port + +/obj/item/weapon/grenade/proc/clown_check(var/mob/living/user) + if((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "Huh? How does this thing work?") + + activate(user) + add_fingerprint(user) + spawn(5) + detonate() + return 0 + return 1 + + +/*/obj/item/weapon/grenade/afterattack(atom/target as mob|obj|turf|area, mob/user as mob) + if (istype(target, /obj/item/weapon/storage)) return ..() // Trying to put it in a full container + if (istype(target, /obj/item/weapon/gun/grenadelauncher)) return ..() + if((user.get_active_hand() == src) && (!active) && (clown_check(user)) && target.loc != src.loc) + to_chat(user, "You prime the [name]! [det_time/10] seconds!") + active = 1 + icon_state = initial(icon_state) + "_active" + playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) + spawn(det_time) + detonate() + return + user.set_dir(get_dir(user, target)) + user.drop_item() + var/t = (isturf(target) ? target : target.loc) + walk_towards(src, t, 3) + return*/ + + +/obj/item/weapon/grenade/examine(mob/user) + . = ..() + if(get_dist(user, src) == 0) + if(det_time > 1) + . += "The timer is set to [det_time/10] seconds." + else if(det_time == null) + . += "\The [src] is set for instant detonation." + + +/obj/item/weapon/grenade/attack_self(mob/user as mob) + if(!active) + if(clown_check(user)) + to_chat(user, "You prime \the [name]! [det_time/10] seconds!") + + activate(user) + add_fingerprint(user) + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.throw_mode_on() + return + + +/obj/item/weapon/grenade/proc/activate(mob/user as mob) + if(active) + return + + if(user) + msg_admin_attack("[key_name_admin(user)] primed \a [src.name]") + + icon_state = initial(icon_state) + "_active" + active = 1 + playsound(src, arm_sound, 75, 1, -3) + + spawn(det_time) + detonate() + return + + +/obj/item/weapon/grenade/proc/detonate() +// playsound(src, 'sound/items/Welder2.ogg', 25, 1) + var/turf/T = get_turf(src) + if(T) + T.hotspot_expose(700,125) + + +/obj/item/weapon/grenade/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + switch(det_time) + if (1) + det_time = 10 + to_chat(user, "You set the [name] for 1 second detonation time.") + if (10) + det_time = 30 + to_chat(user, "You set the [name] for 3 second detonation time.") + if (30) + det_time = 50 + to_chat(user, "You set the [name] for 5 second detonation time.") + if (50) + det_time = 1 + to_chat(user, "You set the [name] for instant detonation.") + add_fingerprint(user) + ..() + return + +/obj/item/weapon/grenade/attack_hand() + walk(src, null, null) + ..() + return + +/obj/item/weapon/grenade/vendor_action(var/obj/machinery/vending/V) activate(V) \ No newline at end of file diff --git a/code/game/objects/items/weapons/grenades/smokebomb.dm b/code/game/objects/items/weapons/grenades/smokebomb.dm index 1cb98be61a8..e06aaf53320 100644 --- a/code/game/objects/items/weapons/grenades/smokebomb.dm +++ b/code/game/objects/items/weapons/grenades/smokebomb.dm @@ -1,39 +1,39 @@ -/obj/item/weapon/grenade/smokebomb - desc = "It is set to detonate in 2 seconds. These high-tech grenades can have their color adapted on the fly with a multitool!" - name = "smoke bomb" - icon = 'icons/obj/grenade.dmi' - icon_state = "flashbang" - det_time = 20 - item_state = "flashbang" - slot_flags = SLOT_BELT - hud_state = "grenade_smoke" - var/datum/effect/effect/system/smoke_spread/bad/smoke - var/smoke_color - var/smoke_strength = 8 - -/obj/item/weapon/grenade/smokebomb/New() - ..() - src.smoke = new /datum/effect/effect/system/smoke_spread/bad() - src.smoke.attach(src) - -/obj/item/weapon/grenade/smokebomb/Destroy() - qdel(smoke) - smoke = null - return ..() - -/obj/item/weapon/grenade/smokebomb/detonate() - playsound(src, 'sound/effects/smoke.ogg', 50, 1, -3) - src.smoke.set_up(10, 0, usr.loc) - spawn(0) - for(var/i = 1 to smoke_strength) - src.smoke.start(smoke_color) - sleep(10) - qdel(src) - - return - -/obj/item/weapon/grenade/smokebomb/attackby(obj/item/I as obj, mob/user as mob) - if(istype(I,/obj/item/device/multitool)) - var/new_smoke_color = input(user, "Choose a color for the smoke:", "Smoke Color", smoke_color) as color|null - if(new_smoke_color) - smoke_color = new_smoke_color +/obj/item/weapon/grenade/smokebomb + desc = "It is set to detonate in 2 seconds. These high-tech grenades can have their color adapted on the fly with a multitool!" + name = "smoke bomb" + icon = 'icons/obj/grenade.dmi' + icon_state = "flashbang" + det_time = 20 + item_state = "flashbang" + slot_flags = SLOT_BELT + hud_state = "grenade_smoke" + var/datum/effect/effect/system/smoke_spread/bad/smoke + var/smoke_color + var/smoke_strength = 8 + +/obj/item/weapon/grenade/smokebomb/New() + ..() + src.smoke = new /datum/effect/effect/system/smoke_spread/bad() + src.smoke.attach(src) + +/obj/item/weapon/grenade/smokebomb/Destroy() + qdel(smoke) + smoke = null + return ..() + +/obj/item/weapon/grenade/smokebomb/detonate() + playsound(src, 'sound/effects/smoke.ogg', 50, 1, -3) + src.smoke.set_up(10, 0, usr.loc) + spawn(0) + for(var/i = 1 to smoke_strength) + src.smoke.start(smoke_color) + sleep(10) + qdel(src) + + return + +/obj/item/weapon/grenade/smokebomb/attackby(obj/item/I as obj, mob/user as mob) + if(istype(I,/obj/item/device/multitool)) + var/new_smoke_color = input(user, "Choose a color for the smoke:", "Smoke Color", smoke_color) as color|null + if(new_smoke_color) + smoke_color = new_smoke_color diff --git a/code/game/objects/items/weapons/grenades/spawnergrenade.dm b/code/game/objects/items/weapons/grenades/spawnergrenade.dm index 5f1bd4503b4..1356b3a9479 100644 --- a/code/game/objects/items/weapons/grenades/spawnergrenade.dm +++ b/code/game/objects/items/weapons/grenades/spawnergrenade.dm @@ -1,79 +1,79 @@ -/obj/item/weapon/grenade/spawnergrenade - desc = "It is set to detonate in 5 seconds. It will unleash an unspecified anomaly into the vicinity." - name = "delivery grenade" - icon = 'icons/obj/grenade.dmi' - icon_state = "delivery" - item_state = "flashbang" - origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4) - var/banglet = 0 - var/spawner_type = null // must be an object path - var/deliveryamt = 1 // amount of type to deliver - -// Detonate now just handles the two loops that query for people in lockers and people who can see it. -/obj/item/weapon/grenade/spawnergrenade/detonate() - - if(spawner_type && deliveryamt) - // Make a quick flash - var/turf/T = get_turf(src) - playsound(src, 'sound/effects/phasein.ogg', 100, 1) - for(var/mob/living/carbon/human/M in viewers(T, null)) - if(M:eyecheck() <= 0) - M.flash_eyes() - - // Spawn some hostile syndicate critters - for(var/i=1, i<=deliveryamt, i++) - var/atom/movable/x = new spawner_type(T) - if(prob(50)) - for(var/j = 1, j <= rand(1, 3), j++) - step(x, pick(NORTH,SOUTH,EAST,WEST)) - qdel(src) - return - -/obj/item/weapon/grenade/spawnergrenade/manhacks - name = "manhack delivery grenade" - spawner_type = /mob/living/simple_mob/mechanical/viscerator - deliveryamt = 5 - origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) - -/obj/item/weapon/grenade/spawnergrenade/manhacks/mercenary - spawner_type = /mob/living/simple_mob/mechanical/viscerator/mercenary - -/obj/item/weapon/grenade/spawnergrenade/manhacks/raider - spawner_type = /mob/living/simple_mob/mechanical/viscerator/raider - -/obj/item/weapon/grenade/spawnergrenade/manhacks/station - desc = "It is set to detonate in 5 seconds. It will deploy three weaponized survey drones." - deliveryamt = 3 - spawner_type = /mob/living/simple_mob/mechanical/viscerator/station - origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 3, TECH_ILLEGAL = 1) - -/obj/item/weapon/grenade/spawnergrenade/ward - name = "sentry delivery grenade" - desc = "It is set to detonate in 5 seconds. It will deploy a single thermal-optic sentry drone." - spawner_type = /mob/living/simple_mob/mechanical/ward/monitor/crew - deliveryamt = 1 - origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_BLUESPACE = 2) - -/obj/item/weapon/grenade/spawnergrenade/spesscarp - name = "carp delivery grenade" - spawner_type = /mob/living/simple_mob/animal/space/carp - deliveryamt = 5 - origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) - -/obj/item/weapon/grenade/spawnergrenade/spider - name = "spider delivery grenade" - spawner_type = /mob/living/simple_mob/animal/giant_spider/hunter - deliveryamt = 3 - origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) - -//Sometimes you just need a sudden influx of spiders. -/obj/item/weapon/grenade/spawnergrenade/spider/briefcase - name = "briefcase" - desc = "It's made of AUTHENTIC faux-leather and has a price-tag still attached. Its owner must be a real professional." - icon_state = "briefcase" - item_state = "briefcase" - force = 8.0 - throw_speed = 1 - throw_range = 4 - w_class = ITEMSIZE_LARGE - deliveryamt = 6 +/obj/item/weapon/grenade/spawnergrenade + desc = "It is set to detonate in 5 seconds. It will unleash an unspecified anomaly into the vicinity." + name = "delivery grenade" + icon = 'icons/obj/grenade.dmi' + icon_state = "delivery" + item_state = "flashbang" + origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4) + var/banglet = 0 + var/spawner_type = null // must be an object path + var/deliveryamt = 1 // amount of type to deliver + +// Detonate now just handles the two loops that query for people in lockers and people who can see it. +/obj/item/weapon/grenade/spawnergrenade/detonate() + + if(spawner_type && deliveryamt) + // Make a quick flash + var/turf/T = get_turf(src) + playsound(src, 'sound/effects/phasein.ogg', 100, 1) + for(var/mob/living/carbon/human/M in viewers(T, null)) + if(M:eyecheck() <= 0) + M.flash_eyes() + + // Spawn some hostile syndicate critters + for(var/i=1, i<=deliveryamt, i++) + var/atom/movable/x = new spawner_type(T) + if(prob(50)) + for(var/j = 1, j <= rand(1, 3), j++) + step(x, pick(NORTH,SOUTH,EAST,WEST)) + qdel(src) + return + +/obj/item/weapon/grenade/spawnergrenade/manhacks + name = "manhack delivery grenade" + spawner_type = /mob/living/simple_mob/mechanical/viscerator + deliveryamt = 5 + origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) + +/obj/item/weapon/grenade/spawnergrenade/manhacks/mercenary + spawner_type = /mob/living/simple_mob/mechanical/viscerator/mercenary + +/obj/item/weapon/grenade/spawnergrenade/manhacks/raider + spawner_type = /mob/living/simple_mob/mechanical/viscerator/raider + +/obj/item/weapon/grenade/spawnergrenade/manhacks/station + desc = "It is set to detonate in 5 seconds. It will deploy three weaponized survey drones." + deliveryamt = 3 + spawner_type = /mob/living/simple_mob/mechanical/viscerator/station + origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 3, TECH_ILLEGAL = 1) + +/obj/item/weapon/grenade/spawnergrenade/ward + name = "sentry delivery grenade" + desc = "It is set to detonate in 5 seconds. It will deploy a single thermal-optic sentry drone." + spawner_type = /mob/living/simple_mob/mechanical/ward/monitor/crew + deliveryamt = 1 + origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_BLUESPACE = 2) + +/obj/item/weapon/grenade/spawnergrenade/spesscarp + name = "carp delivery grenade" + spawner_type = /mob/living/simple_mob/animal/space/carp + deliveryamt = 5 + origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) + +/obj/item/weapon/grenade/spawnergrenade/spider + name = "spider delivery grenade" + spawner_type = /mob/living/simple_mob/animal/giant_spider/hunter + deliveryamt = 3 + origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) + +//Sometimes you just need a sudden influx of spiders. +/obj/item/weapon/grenade/spawnergrenade/spider/briefcase + name = "briefcase" + desc = "It's made of AUTHENTIC faux-leather and has a price-tag still attached. Its owner must be a real professional." + icon_state = "briefcase" + item_state = "briefcase" + force = 8.0 + throw_speed = 1 + throw_range = 4 + w_class = ITEMSIZE_LARGE + deliveryamt = 6 diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm index 35e55428a94..ea8a677f15f 100644 --- a/code/game/objects/items/weapons/handcuffs.dm +++ b/code/game/objects/items/weapons/handcuffs.dm @@ -1,337 +1,337 @@ -/obj/item/weapon/handcuffs - name = "handcuffs" - desc = "Use this to keep prisoners in line." - gender = PLURAL - icon = 'icons/obj/items.dmi' - icon_state = "handcuff" - slot_flags = SLOT_BELT - throwforce = 5 - w_class = ITEMSIZE_SMALL - throw_speed = 2 - throw_range = 5 - origin_tech = list(TECH_MATERIAL = 1) - matter = list(MAT_STEEL = 500) - drop_sound = 'sound/items/drop/accessory.ogg' - pickup_sound = 'sound/items/pickup/accessory.ogg' - var/elastic - var/dispenser = 0 - var/breakouttime = 1200 //Deciseconds = 120s = 2 minutes - var/cuff_sound = 'sound/weapons/handcuffs.ogg' - var/cuff_type = "handcuffs" - var/use_time = 30 - sprite_sheets = list(SPECIES_TESHARI = 'icons/mob/species/teshari/handcuffs.dmi') - -/obj/item/weapon/handcuffs/get_worn_icon_state(var/slot_name) - if(slot_name == slot_handcuffed_str) - return "handcuff1" //Simple - - return ..() - -/obj/item/weapon/handcuffs/attack(var/mob/living/carbon/C, var/mob/living/user) - - if(!user.IsAdvancedToolUser()) - return - - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "Uh ... how do those things work?!") - place_handcuffs(user, user) - return - - if(!C.handcuffed) - if (C == user) - place_handcuffs(user, user) - return - - //check for an aggressive grab (or robutts) - if(can_place(C, user)) - place_handcuffs(C, user) - else - to_chat(user, "You need to have a firm grip on [C] before you can put \the [src] on!") - -/obj/item/weapon/handcuffs/proc/can_place(var/mob/target, var/mob/user) - if(user == target) - return 1 - if(istype(user, /mob/living/silicon/robot)) - if(user.Adjacent(target)) - return 1 - else - for(var/obj/item/weapon/grab/G in target.grabbed_by) - if(G.loc == user && G.state >= GRAB_AGGRESSIVE) - return 1 - return 0 - -/obj/item/weapon/handcuffs/proc/place_handcuffs(var/mob/living/carbon/target, var/mob/user) - playsound(src, cuff_sound, 30, 1, -2) - - var/mob/living/carbon/human/H = target - if(!istype(H)) - return 0 - - if (!H.has_organ_for_slot(slot_handcuffed)) - to_chat(user, "\The [H] needs at least two wrists before you can cuff them together!") - return 0 - - if(istype(H.gloves,/obj/item/clothing/gloves/gauntlets/rig) && !elastic) // Can't cuff someone who's in a deployed hardsuit. - to_chat(user, "\The [src] won't fit around \the [H.gloves]!") - return 0 - - user.visible_message("\The [user] is attempting to put [cuff_type] on \the [H]!") - - if(!do_after(user,use_time)) - return 0 - - if(!can_place(target, user)) //victim may have resisted out of the grab in the meantime - return 0 - - add_attack_logs(user,H,"Handcuffed (attempt)") - feedback_add_details("handcuffs","H") - - user.setClickCooldown(user.get_attack_speed(src)) - user.do_attack_animation(H) - - user.visible_message("\The [user] has put [cuff_type] on \the [H]!") - - // Apply cuffs. - var/obj/item/weapon/handcuffs/cuffs = src - if(dispenser) - cuffs = new(get_turf(user)) - else - user.drop_from_inventory(cuffs) - cuffs.loc = target - target.handcuffed = cuffs - target.update_handcuffed() - target.drop_r_hand() - target.drop_l_hand() - target.stop_pulling() - return 1 - -/obj/item/weapon/handcuffs/equipped(var/mob/living/user,var/slot) - . = ..() - if(slot == slot_handcuffed) - user.drop_r_hand() - user.drop_l_hand() - user.stop_pulling() - -var/last_chew = 0 -/mob/living/carbon/human/RestrainedClickOn(var/atom/A) - if (A != src) return ..() - if (last_chew + 26 > world.time) return - - var/mob/living/carbon/human/H = A - if (!H.handcuffed) return - if (H.a_intent != I_HURT) return - if (H.zone_sel.selecting != O_MOUTH) return - if (H.wear_mask) return - if (istype(H.wear_suit, /obj/item/clothing/suit/straight_jacket)) return - - var/obj/item/organ/external/O = H.organs_by_name[(H.hand ? BP_L_HAND : BP_R_HAND)] - if (!O) return - - var/datum/gender/T = gender_datums[H.get_visible_gender()] - - var/s = "[H.name] chews on [T.his] [O.name]!" - H.visible_message(s, "You chew on your [O.name]!") - add_attack_logs(H,H,"chewed own [O.name]") - - if(O.take_damage(3,0,1,1,"teeth marks")) - H:UpdateDamageIcon() - - last_chew = world.time - -/obj/item/weapon/handcuffs/fuzzy - name = "fuzzy cuffs" - icon_state = "fuzzycuff" - breakouttime = 100 //VOREstation edit - desc = "Use this to keep... 'prisoners' in line." - -/obj/item/weapon/handcuffs/cable - name = "cable restraints" - desc = "Looks like some cables tied together. Could be used to tie something up." - icon_state = "cuff_white" - breakouttime = 300 //Deciseconds = 30s - cuff_sound = 'sound/weapons/cablecuff.ogg' - cuff_type = "cable restraints" - elastic = 1 - -/obj/item/weapon/handcuffs/cable/red - color = "#DD0000" - -/obj/item/weapon/handcuffs/cable/yellow - color = "#DDDD00" - -/obj/item/weapon/handcuffs/cable/blue - color = "#0000DD" - -/obj/item/weapon/handcuffs/cable/green - color = "#00DD00" - -/obj/item/weapon/handcuffs/cable/pink - color = "#DD00DD" - -/obj/item/weapon/handcuffs/cable/orange - color = "#DD8800" - -/obj/item/weapon/handcuffs/cable/cyan - color = "#00DDDD" - -/obj/item/weapon/handcuffs/cable/white - color = "#FFFFFF" - -/obj/item/weapon/handcuffs/cyborg - dispenser = 1 - -/obj/item/weapon/handcuffs/cable/tape - name = "tape restraints" - desc = "DIY!" - icon_state = "tape_cross" - item_state = null - icon = 'icons/obj/bureaucracy.dmi' - breakouttime = 200 - cuff_type = "duct tape" - -/obj/item/weapon/handcuffs/cable/tape/cyborg - dispenser = TRUE - -//Legcuffs. Not /really/ handcuffs, but its close enough. -/obj/item/weapon/handcuffs/legcuffs - name = "legcuffs" - desc = "Use this to keep prisoners in line." - gender = PLURAL - icon = 'icons/obj/items.dmi' - icon_state = "legcuff" - throwforce = 0 - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_MATERIAL = 1) - breakouttime = 300 //Deciseconds = 30s = 0.5 minute - cuff_type = "legcuffs" - sprite_sheets = list(SPECIES_TESHARI = 'icons/mob/species/teshari/handcuffs.dmi') - elastic = 0 - cuff_sound = 'sound/weapons/handcuffs.ogg' //This shold work for now. - -/obj/item/weapon/handcuffs/legcuffs/get_worn_icon_state(var/slot_name) - if(slot_name == slot_legcuffed_str) - return "legcuff1" - - return ..() - -/obj/item/weapon/handcuffs/legcuffs/attack(var/mob/living/carbon/C, var/mob/living/user) - if(!user.IsAdvancedToolUser()) - return - - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "Uh ... how do those things work?!") - place_legcuffs(user, user) - return - - if(!C.legcuffed) - if (C == user) - place_legcuffs(user, user) - return - - //check for an aggressive grab (or robutts) - if(can_place(C, user)) - place_legcuffs(C, user) - else - to_chat(user, "You need to have a firm grip on [C] before you can put \the [src] on!") - -/obj/item/weapon/handcuffs/legcuffs/proc/place_legcuffs(var/mob/living/carbon/target, var/mob/user) - playsound(src, cuff_sound, 30, 1, -2) - - var/mob/living/carbon/human/H = target - if(!istype(H)) - return 0 - - if (!H.has_organ_for_slot(slot_legcuffed)) - to_chat(user, "\The [H] needs at least two ankles before you can cuff them together!") - return 0 - - if(istype(H.shoes,/obj/item/clothing/shoes/magboots/rig) && !elastic) // Can't cuff someone who's in a deployed hardsuit. - to_chat(user, "\The [src] won't fit around \the [H.shoes]!") - return 0 - - user.visible_message("\The [user] is attempting to put [cuff_type] on \the [H]!") - - if(!do_after(user,use_time)) - return 0 - - if(!can_place(target, user)) //victim may have resisted out of the grab in the meantime - return 0 - - add_attack_logs(user,H,"Legcuffed (attempt)") - feedback_add_details("legcuffs","H") - - user.setClickCooldown(user.get_attack_speed(src)) - user.do_attack_animation(H) - - user.visible_message("\The [user] has put [cuff_type] on \the [H]!") - - // Apply cuffs. - var/obj/item/weapon/handcuffs/legcuffs/lcuffs = src - if(dispenser) - lcuffs = new(get_turf(user)) - else - user.drop_from_inventory(lcuffs) - lcuffs.loc = target - target.legcuffed = lcuffs - target.update_inv_legcuffed() - if(target.m_intent != "walk") - target.m_intent = "walk" - if(target.hud_used && user.hud_used.move_intent) - target.hud_used.move_intent.icon_state = "walking" - return 1 - -/obj/item/weapon/handcuffs/legcuffs/equipped(var/mob/living/user,var/slot) - . = ..() - if(slot == slot_legcuffed) - if(user.m_intent != "walk") - user.m_intent = "walk" - if(user.hud_used && user.hud_used.move_intent) - user.hud_used.move_intent.icon_state = "walking" - - -/obj/item/weapon/handcuffs/legcuffs/bola - name = "bola" - desc = "Keeps prey in line." - elastic = 1 - use_time = 0 - breakouttime = 30 - cuff_sound = 'sound/weapons/towelwipe.ogg' //Is there anything this sound can't do? - -/obj/item/weapon/handcuffs/legcuffs/bola/can_place(var/mob/target, var/mob/user) - if(user) //A ranged legcuff, until proper implementation as items it remains a projectile-only thing. - return 1 - -/obj/item/weapon/handcuffs/legcuffs/bola/dropped() - visible_message("\The [src] falls apart!") - qdel(src) - -/obj/item/weapon/handcuffs/legcuffs/bola/place_legcuffs(var/mob/living/carbon/target, var/mob/user) - playsound(src, cuff_sound, 30, 1, -2) - - var/mob/living/carbon/human/H = target - if(!istype(H)) - src.dropped() - return 0 - - if(!H.has_organ_for_slot(slot_legcuffed)) - H.visible_message("\The [src] slams into [H], but slides off!") - src.dropped() - return 0 - - H.visible_message("\The [H] has been snared by \the [src]!") - - // Apply cuffs. - var/obj/item/weapon/handcuffs/legcuffs/lcuffs = src - lcuffs.loc = target - target.legcuffed = lcuffs - target.update_inv_legcuffed() - if(target.m_intent != "walk") - target.m_intent = "walk" - if(target.hud_used && user.hud_used.move_intent) - target.hud_used.move_intent.icon_state = "walking" - return 1 - -/obj/item/weapon/handcuffs/cable/plantfiber - name = "rope bindings" - desc = "A length of rope fashioned to hold someone's hands together." - color = "#7e6442" +/obj/item/weapon/handcuffs + name = "handcuffs" + desc = "Use this to keep prisoners in line." + gender = PLURAL + icon = 'icons/obj/items.dmi' + icon_state = "handcuff" + slot_flags = SLOT_BELT + throwforce = 5 + w_class = ITEMSIZE_SMALL + throw_speed = 2 + throw_range = 5 + origin_tech = list(TECH_MATERIAL = 1) + matter = list(MAT_STEEL = 500) + drop_sound = 'sound/items/drop/accessory.ogg' + pickup_sound = 'sound/items/pickup/accessory.ogg' + var/elastic + var/dispenser = 0 + var/breakouttime = 1200 //Deciseconds = 120s = 2 minutes + var/cuff_sound = 'sound/weapons/handcuffs.ogg' + var/cuff_type = "handcuffs" + var/use_time = 30 + sprite_sheets = list(SPECIES_TESHARI = 'icons/mob/species/teshari/handcuffs.dmi') + +/obj/item/weapon/handcuffs/get_worn_icon_state(var/slot_name) + if(slot_name == slot_handcuffed_str) + return "handcuff1" //Simple + + return ..() + +/obj/item/weapon/handcuffs/attack(var/mob/living/carbon/C, var/mob/living/user) + + if(!user.IsAdvancedToolUser()) + return + + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "Uh ... how do those things work?!") + place_handcuffs(user, user) + return + + if(!C.handcuffed) + if (C == user) + place_handcuffs(user, user) + return + + //check for an aggressive grab (or robutts) + if(can_place(C, user)) + place_handcuffs(C, user) + else + to_chat(user, "You need to have a firm grip on [C] before you can put \the [src] on!") + +/obj/item/weapon/handcuffs/proc/can_place(var/mob/target, var/mob/user) + if(user == target) + return 1 + if(istype(user, /mob/living/silicon/robot)) + if(user.Adjacent(target)) + return 1 + else + for(var/obj/item/weapon/grab/G in target.grabbed_by) + if(G.loc == user && G.state >= GRAB_AGGRESSIVE) + return 1 + return 0 + +/obj/item/weapon/handcuffs/proc/place_handcuffs(var/mob/living/carbon/target, var/mob/user) + playsound(src, cuff_sound, 30, 1, -2) + + var/mob/living/carbon/human/H = target + if(!istype(H)) + return 0 + + if (!H.has_organ_for_slot(slot_handcuffed)) + to_chat(user, "\The [H] needs at least two wrists before you can cuff them together!") + return 0 + + if(istype(H.gloves,/obj/item/clothing/gloves/gauntlets/rig) && !elastic) // Can't cuff someone who's in a deployed hardsuit. + to_chat(user, "\The [src] won't fit around \the [H.gloves]!") + return 0 + + user.visible_message("\The [user] is attempting to put [cuff_type] on \the [H]!") + + if(!do_after(user,use_time)) + return 0 + + if(!can_place(target, user)) //victim may have resisted out of the grab in the meantime + return 0 + + add_attack_logs(user,H,"Handcuffed (attempt)") + feedback_add_details("handcuffs","H") + + user.setClickCooldown(user.get_attack_speed(src)) + user.do_attack_animation(H) + + user.visible_message("\The [user] has put [cuff_type] on \the [H]!") + + // Apply cuffs. + var/obj/item/weapon/handcuffs/cuffs = src + if(dispenser) + cuffs = new(get_turf(user)) + else + user.drop_from_inventory(cuffs) + cuffs.loc = target + target.handcuffed = cuffs + target.update_handcuffed() + target.drop_r_hand() + target.drop_l_hand() + target.stop_pulling() + return 1 + +/obj/item/weapon/handcuffs/equipped(var/mob/living/user,var/slot) + . = ..() + if(slot == slot_handcuffed) + user.drop_r_hand() + user.drop_l_hand() + user.stop_pulling() + +var/last_chew = 0 +/mob/living/carbon/human/RestrainedClickOn(var/atom/A) + if (A != src) return ..() + if (last_chew + 26 > world.time) return + + var/mob/living/carbon/human/H = A + if (!H.handcuffed) return + if (H.a_intent != I_HURT) return + if (H.zone_sel.selecting != O_MOUTH) return + if (H.wear_mask) return + if (istype(H.wear_suit, /obj/item/clothing/suit/straight_jacket)) return + + var/obj/item/organ/external/O = H.organs_by_name[(H.hand ? BP_L_HAND : BP_R_HAND)] + if (!O) return + + var/datum/gender/T = gender_datums[H.get_visible_gender()] + + var/s = "[H.name] chews on [T.his] [O.name]!" + H.visible_message(s, "You chew on your [O.name]!") + add_attack_logs(H,H,"chewed own [O.name]") + + if(O.take_damage(3,0,1,1,"teeth marks")) + H:UpdateDamageIcon() + + last_chew = world.time + +/obj/item/weapon/handcuffs/fuzzy + name = "fuzzy cuffs" + icon_state = "fuzzycuff" + breakouttime = 100 //VOREstation edit + desc = "Use this to keep... 'prisoners' in line." + +/obj/item/weapon/handcuffs/cable + name = "cable restraints" + desc = "Looks like some cables tied together. Could be used to tie something up." + icon_state = "cuff_white" + breakouttime = 300 //Deciseconds = 30s + cuff_sound = 'sound/weapons/cablecuff.ogg' + cuff_type = "cable restraints" + elastic = 1 + +/obj/item/weapon/handcuffs/cable/red + color = "#DD0000" + +/obj/item/weapon/handcuffs/cable/yellow + color = "#DDDD00" + +/obj/item/weapon/handcuffs/cable/blue + color = "#0000DD" + +/obj/item/weapon/handcuffs/cable/green + color = "#00DD00" + +/obj/item/weapon/handcuffs/cable/pink + color = "#DD00DD" + +/obj/item/weapon/handcuffs/cable/orange + color = "#DD8800" + +/obj/item/weapon/handcuffs/cable/cyan + color = "#00DDDD" + +/obj/item/weapon/handcuffs/cable/white + color = "#FFFFFF" + +/obj/item/weapon/handcuffs/cyborg + dispenser = 1 + +/obj/item/weapon/handcuffs/cable/tape + name = "tape restraints" + desc = "DIY!" + icon_state = "tape_cross" + item_state = null + icon = 'icons/obj/bureaucracy.dmi' + breakouttime = 200 + cuff_type = "duct tape" + +/obj/item/weapon/handcuffs/cable/tape/cyborg + dispenser = TRUE + +//Legcuffs. Not /really/ handcuffs, but its close enough. +/obj/item/weapon/handcuffs/legcuffs + name = "legcuffs" + desc = "Use this to keep prisoners in line." + gender = PLURAL + icon = 'icons/obj/items.dmi' + icon_state = "legcuff" + throwforce = 0 + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_MATERIAL = 1) + breakouttime = 300 //Deciseconds = 30s = 0.5 minute + cuff_type = "legcuffs" + sprite_sheets = list(SPECIES_TESHARI = 'icons/mob/species/teshari/handcuffs.dmi') + elastic = 0 + cuff_sound = 'sound/weapons/handcuffs.ogg' //This shold work for now. + +/obj/item/weapon/handcuffs/legcuffs/get_worn_icon_state(var/slot_name) + if(slot_name == slot_legcuffed_str) + return "legcuff1" + + return ..() + +/obj/item/weapon/handcuffs/legcuffs/attack(var/mob/living/carbon/C, var/mob/living/user) + if(!user.IsAdvancedToolUser()) + return + + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "Uh ... how do those things work?!") + place_legcuffs(user, user) + return + + if(!C.legcuffed) + if (C == user) + place_legcuffs(user, user) + return + + //check for an aggressive grab (or robutts) + if(can_place(C, user)) + place_legcuffs(C, user) + else + to_chat(user, "You need to have a firm grip on [C] before you can put \the [src] on!") + +/obj/item/weapon/handcuffs/legcuffs/proc/place_legcuffs(var/mob/living/carbon/target, var/mob/user) + playsound(src, cuff_sound, 30, 1, -2) + + var/mob/living/carbon/human/H = target + if(!istype(H)) + return 0 + + if (!H.has_organ_for_slot(slot_legcuffed)) + to_chat(user, "\The [H] needs at least two ankles before you can cuff them together!") + return 0 + + if(istype(H.shoes,/obj/item/clothing/shoes/magboots/rig) && !elastic) // Can't cuff someone who's in a deployed hardsuit. + to_chat(user, "\The [src] won't fit around \the [H.shoes]!") + return 0 + + user.visible_message("\The [user] is attempting to put [cuff_type] on \the [H]!") + + if(!do_after(user,use_time)) + return 0 + + if(!can_place(target, user)) //victim may have resisted out of the grab in the meantime + return 0 + + add_attack_logs(user,H,"Legcuffed (attempt)") + feedback_add_details("legcuffs","H") + + user.setClickCooldown(user.get_attack_speed(src)) + user.do_attack_animation(H) + + user.visible_message("\The [user] has put [cuff_type] on \the [H]!") + + // Apply cuffs. + var/obj/item/weapon/handcuffs/legcuffs/lcuffs = src + if(dispenser) + lcuffs = new(get_turf(user)) + else + user.drop_from_inventory(lcuffs) + lcuffs.loc = target + target.legcuffed = lcuffs + target.update_inv_legcuffed() + if(target.m_intent != "walk") + target.m_intent = "walk" + if(target.hud_used && user.hud_used.move_intent) + target.hud_used.move_intent.icon_state = "walking" + return 1 + +/obj/item/weapon/handcuffs/legcuffs/equipped(var/mob/living/user,var/slot) + . = ..() + if(slot == slot_legcuffed) + if(user.m_intent != "walk") + user.m_intent = "walk" + if(user.hud_used && user.hud_used.move_intent) + user.hud_used.move_intent.icon_state = "walking" + + +/obj/item/weapon/handcuffs/legcuffs/bola + name = "bola" + desc = "Keeps prey in line." + elastic = 1 + use_time = 0 + breakouttime = 30 + cuff_sound = 'sound/weapons/towelwipe.ogg' //Is there anything this sound can't do? + +/obj/item/weapon/handcuffs/legcuffs/bola/can_place(var/mob/target, var/mob/user) + if(user) //A ranged legcuff, until proper implementation as items it remains a projectile-only thing. + return 1 + +/obj/item/weapon/handcuffs/legcuffs/bola/dropped() + visible_message("\The [src] falls apart!") + qdel(src) + +/obj/item/weapon/handcuffs/legcuffs/bola/place_legcuffs(var/mob/living/carbon/target, var/mob/user) + playsound(src, cuff_sound, 30, 1, -2) + + var/mob/living/carbon/human/H = target + if(!istype(H)) + src.dropped() + return 0 + + if(!H.has_organ_for_slot(slot_legcuffed)) + H.visible_message("\The [src] slams into [H], but slides off!") + src.dropped() + return 0 + + H.visible_message("\The [H] has been snared by \the [src]!") + + // Apply cuffs. + var/obj/item/weapon/handcuffs/legcuffs/lcuffs = src + lcuffs.loc = target + target.legcuffed = lcuffs + target.update_inv_legcuffed() + if(target.m_intent != "walk") + target.m_intent = "walk" + if(target.hud_used && user.hud_used.move_intent) + target.hud_used.move_intent.icon_state = "walking" + return 1 + +/obj/item/weapon/handcuffs/cable/plantfiber + name = "rope bindings" + desc = "A length of rope fashioned to hold someone's hands together." + color = "#7e6442" diff --git a/code/game/objects/items/weapons/hydroponics.dm b/code/game/objects/items/weapons/hydroponics.dm index 15c1478bf32..04087646f74 100644 --- a/code/game/objects/items/weapons/hydroponics.dm +++ b/code/game/objects/items/weapons/hydroponics.dm @@ -1,113 +1,113 @@ -/* - * SeedBag - */ -//uncomment when this is updated to match storage update -/* -/obj/item/weapon/seedbag - icon = 'icons/obj/hydroponics_machines.dmi' - icon_state = "seedbag" - name = "Seed Bag" - desc = "A small satchel made for organizing seeds." - var/mode = 1; //0 = pick one at a time, 1 = pick all on tile - var/capacity = 500; //the number of seeds it can carry. - slot_flags = SLOT_BELT - w_class = ITEMSIZE_TINY - var/list/item_quants = list() - -/obj/item/weapon/seedbag/attack_self(mob/user as mob) - user.machine = src - interact(user) - -/obj/item/weapon/seedbag/verb/toggle_mode() - set name = "Switch Bagging Method" - set category = "Object" - - mode = !mode - switch (mode) - if(1) - to_chat(usr, "The bag now picks up all seeds in a tile at once.") - if(0) - to_chat(usr, "The bag now picks up one seed pouch at a time.") - -/obj/item/seeds/attackby(var/obj/item/O as obj, var/mob/user as mob) - ..() - if (istype(O, /obj/item/weapon/seedbag)) - var/obj/item/weapon/seedbag/S = O - if (S.mode == 1) - for (var/obj/item/seeds/G in locate(src.x,src.y,src.z)) - if (S.contents.len < S.capacity) - S.contents += G; - if(S.item_quants[G.name]) - S.item_quants[G.name]++ - else - S.item_quants[G.name] = 1 - else - to_chat(user, "The seed bag is full.") - S.updateUsrDialog() - return - to_chat(user, "You pick up all the seeds.") - else - if (S.contents.len < S.capacity) - S.contents += src; - if(S.item_quants[name]) - S.item_quants[name]++ - else - S.item_quants[name] = 1 - else - to_chat(user, "The seed bag is full.") - S.updateUsrDialog() - return - -/obj/item/weapon/seedbag/interact(mob/user as mob) - - var/dat = "Select an item:
                    " - - if (contents.len == 0) - dat += "No seeds loaded!" - else - for (var/O in item_quants) - if(item_quants[O] > 0) - var/N = item_quants[O] - dat += "[capitalize(O)]:" - dat += " [N] " - dat += "Vend" - dat += "
                    " - - dat += "
                    Unload All" - dat += "
                    " - user << browse("Seedbag Supplies[dat]", "window=seedbag") - onclose(user, "seedbag") - return - -/obj/item/weapon/seedbag/Topic(href, href_list) - if(..()) - return - - usr.machine = src - if ( href_list["vend"] ) - var/N = href_list["vend"] - - if(item_quants[N] <= 0) // Sanity check, there are probably ways to press the button when it shouldn't be possible. - return - - item_quants[N] -= 1 - for(var/obj/O in contents) - if(O.name == N) - O.loc = get_turf(src) - usr.put_in_hands(O) - break - - else if ( href_list["unload"] ) - item_quants.Cut() - for(var/obj/O in contents ) - O.loc = get_turf(src) - - src.updateUsrDialog() - return - -/obj/item/weapon/seedbag/updateUsrDialog() - var/list/nearby = range(1, src) - for(var/mob/M in nearby) - if ((M.client && M.machine == src)) - src.attack_self(M) -*/ +/* + * SeedBag + */ +//uncomment when this is updated to match storage update +/* +/obj/item/weapon/seedbag + icon = 'icons/obj/hydroponics_machines.dmi' + icon_state = "seedbag" + name = "Seed Bag" + desc = "A small satchel made for organizing seeds." + var/mode = 1; //0 = pick one at a time, 1 = pick all on tile + var/capacity = 500; //the number of seeds it can carry. + slot_flags = SLOT_BELT + w_class = ITEMSIZE_TINY + var/list/item_quants = list() + +/obj/item/weapon/seedbag/attack_self(mob/user as mob) + user.machine = src + interact(user) + +/obj/item/weapon/seedbag/verb/toggle_mode() + set name = "Switch Bagging Method" + set category = "Object" + + mode = !mode + switch (mode) + if(1) + to_chat(usr, "The bag now picks up all seeds in a tile at once.") + if(0) + to_chat(usr, "The bag now picks up one seed pouch at a time.") + +/obj/item/seeds/attackby(var/obj/item/O as obj, var/mob/user as mob) + ..() + if (istype(O, /obj/item/weapon/seedbag)) + var/obj/item/weapon/seedbag/S = O + if (S.mode == 1) + for (var/obj/item/seeds/G in locate(src.x,src.y,src.z)) + if (S.contents.len < S.capacity) + S.contents += G; + if(S.item_quants[G.name]) + S.item_quants[G.name]++ + else + S.item_quants[G.name] = 1 + else + to_chat(user, "The seed bag is full.") + S.updateUsrDialog() + return + to_chat(user, "You pick up all the seeds.") + else + if (S.contents.len < S.capacity) + S.contents += src; + if(S.item_quants[name]) + S.item_quants[name]++ + else + S.item_quants[name] = 1 + else + to_chat(user, "The seed bag is full.") + S.updateUsrDialog() + return + +/obj/item/weapon/seedbag/interact(mob/user as mob) + + var/dat = "Select an item:
                    " + + if (contents.len == 0) + dat += "No seeds loaded!" + else + for (var/O in item_quants) + if(item_quants[O] > 0) + var/N = item_quants[O] + dat += "[capitalize(O)]:" + dat += " [N] " + dat += "Vend" + dat += "
                    " + + dat += "
                    Unload All" + dat += "
                    " + user << browse("Seedbag Supplies[dat]", "window=seedbag") + onclose(user, "seedbag") + return + +/obj/item/weapon/seedbag/Topic(href, href_list) + if(..()) + return + + usr.machine = src + if ( href_list["vend"] ) + var/N = href_list["vend"] + + if(item_quants[N] <= 0) // Sanity check, there are probably ways to press the button when it shouldn't be possible. + return + + item_quants[N] -= 1 + for(var/obj/O in contents) + if(O.name == N) + O.loc = get_turf(src) + usr.put_in_hands(O) + break + + else if ( href_list["unload"] ) + item_quants.Cut() + for(var/obj/O in contents ) + O.loc = get_turf(src) + + src.updateUsrDialog() + return + +/obj/item/weapon/seedbag/updateUsrDialog() + var/list/nearby = range(1, src) + for(var/mob/M in nearby) + if ((M.client && M.machine == src)) + src.attack_self(M) +*/ diff --git a/code/game/objects/items/weapons/implants/implant.dm b/code/game/objects/items/weapons/implants/implant.dm index a710df388b7..7ff8063e32a 100644 --- a/code/game/objects/items/weapons/implants/implant.dm +++ b/code/game/objects/items/weapons/implants/implant.dm @@ -1,631 +1,631 @@ -#define MALFUNCTION_TEMPORARY 1 -#define MALFUNCTION_PERMANENT 2 - - -/obj/item/weapon/implant - name = "implant" - icon = 'icons/obj/device.dmi' - icon_state = "implant" - w_class = ITEMSIZE_TINY - show_messages = TRUE - - var/implanted = null - var/mob/imp_in = null - var/obj/item/organ/external/part = null - var/implant_color = "b" - var/allow_reagents = 0 - var/malfunction = 0 - var/initialize_loc = BP_TORSO - var/known_implant = FALSE - -/obj/item/weapon/implant/proc/trigger(emote, source as mob) - return - -/obj/item/weapon/implant/proc/activate() - return - -// Moves the implant where it needs to go, and tells it if there's more to be done in post_implant -/obj/item/weapon/implant/proc/handle_implant(var/mob/source, var/target_zone = BP_TORSO) - . = TRUE - imp_in = source - implanted = TRUE - if(ishuman(source)) - var/mob/living/carbon/human/H = source - var/obj/item/organ/external/affected = H.get_organ(target_zone) - if(affected) - affected.implants |= src - part = affected - if(part) - forceMove(part) - else - forceMove(source) - - listening_objects |= src - -// Takes place after handle_implant, if that returns TRUE -/obj/item/weapon/implant/proc/post_implant(var/mob/source) - -/obj/item/weapon/implant/proc/get_data() - return "No information available" - -/obj/item/weapon/implant/proc/hear(message, source as mob) - return - -/obj/item/weapon/implant/proc/islegal() - return 0 - -/obj/item/weapon/implant/proc/meltdown() //breaks it down, making implant unrecongizible - to_chat(imp_in, "You feel something melting inside [part ? "your [part.name]" : "you"]!") - if (part) - part.take_damage(burn = 15, used_weapon = "Electronics meltdown") - else - var/mob/living/M = imp_in - M.apply_damage(15,BURN) - name = "melted implant" - desc = "Charred circuit in melted plastic case. Wonder what that used to be..." - icon_state = "implant_melted" - malfunction = MALFUNCTION_PERMANENT - -/obj/item/weapon/implant/proc/implant_loadout(var/mob/living/carbon/human/H) - . = istype(H) && handle_implant(H, initialize_loc) - if(.) - invisibility = initial(invisibility) - known_implant = TRUE - post_implant(H) - -/obj/item/weapon/implant/Destroy() - if(part) - part.implants.Remove(src) - part = null - listening_objects.Remove(src) - imp_in = null - return ..() - -/obj/item/weapon/implant/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/weapon/implanter)) - var/obj/item/weapon/implanter/implanter = I - if(implanter.imp) - return // It's full. - user.drop_from_inventory(src) - forceMove(implanter) - implanter.imp = src - implanter.update() - else - ..() - - - -////////////////////////////// -// Tracking Implant -////////////////////////////// -GLOBAL_LIST_BOILERPLATE(all_tracking_implants, /obj/item/weapon/implant/tracking) - -/obj/item/weapon/implant/tracking - name = "tracking implant" - desc = "An implant normally given to dangerous criminals. Allows security to track your location." - known_implant = TRUE - var/id = 1 - var/degrade_time = 10 MINUTES //How long before the implant stops working outside of a living body. - -/obj/item/weapon/implant/tracking/weak //This is for the loadout - degrade_time = 2.5 MINUTES - -/obj/item/weapon/implant/tracking/New() - id = rand(1, 1000) - ..() - -/obj/item/weapon/implant/tracking/post_implant(var/mob/source) - START_PROCESSING(SSobj, src) - -/obj/item/weapon/implant/tracking/Destroy() - STOP_PROCESSING(SSobj, src) - if(part) - part.implants -= src - part = imp_in = null - return ..() - -/obj/item/weapon/implant/tracking/process() - var/implant_location = src.loc - if(ismob(implant_location)) - var/mob/living/L = implant_location - if(L.stat == DEAD) - if(world.time >= L.timeofdeath + degrade_time) - name = "melted implant" - desc = "Charred circuit in melted plastic case. Wonder what that used to be..." - icon_state = "implant_melted" - malfunction = MALFUNCTION_PERMANENT - STOP_PROCESSING(SSobj, src) - return 1 - -/obj/item/weapon/implant/tracking/get_data() - var/dat = {"Implant Specifications:
                    -Name: Tracking Beacon
                    -Life: 10 minutes after death of host
                    -Important Notes: None
                    -
                    -Implant Details:
                    -Function: Continuously transmits low power signal. Useful for tracking.
                    -Special Features:
                    -Neuro-Safe- Specialized shell absorbs excess voltages self-destructing the chip if -a malfunction occurs thereby securing safety of subject. The implant will melt and -disintegrate into bio-safe elements.
                    -Integrity: Gradient creates slight risk of being overcharged and frying the -circuitry. As a result neurotoxins can cause massive damage.
                    -Implant Specifics:
                    "} - return dat - -/obj/item/weapon/implant/tracking/emp_act(severity) - if (malfunction) //no, dawg, you can't malfunction while you are malfunctioning - return - malfunction = MALFUNCTION_TEMPORARY - - var/delay = 20 - switch(severity) - if(1) - if(prob(60)) - meltdown() - if(2) - delay = rand(5*60*10,15*60*10) //from 5 to 15 minutes of free time - if(3) - delay = rand(2*60*10,5*60*10) //from 2 to 5 minutes of free time - if(4) - delay = rand(0.5*60*10,1*60*10) //from .5 to 1 minutes of free time - - spawn(delay) - malfunction-- - -////////////////////////////// -// Death Explosive Implant -////////////////////////////// -/obj/item/weapon/implant/dexplosive - name = "explosive" - desc = "And boom goes the weasel." - icon_state = "implant_evil" - -/obj/item/weapon/implant/dexplosive/get_data() - var/dat = {" -Implant Specifications:
                    -Name: Robust Corp RX-78 Employee Management Implant
                    -Life: Activates upon death.
                    -Important Notes: Explodes
                    -
                    -Implant Details:
                    -Function: Contains a compact, electrically detonated explosive that detonates upon receiving a specially encoded signal or upon host death.
                    -Special Features: Explodes
                    -Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} - return dat - - -/obj/item/weapon/implant/dexplosive/trigger(emote, source as mob) - if(emote == "deathgasp") - src.activate("death") - return - - -/obj/item/weapon/implant/dexplosive/activate(var/cause) - if((!cause) || (!src.imp_in)) return 0 - explosion(src, -1, 0, 2, 3, 0)//This might be a bit much, dono will have to see. - if(src.imp_in) - src.imp_in.gib() - -/obj/item/weapon/implant/dexplosive/islegal() - return 0 - -////////////////////////////// -// Explosive Implant -////////////////////////////// -/obj/item/weapon/implant/explosive - name = "explosive implant" - desc = "A military grade micro bio-explosive. Highly dangerous." - var/elevel = "Localized Limb" - var/phrase = "supercalifragilisticexpialidocious" - icon_state = "implant_evil" - -/obj/item/weapon/implant/explosive/get_data() - var/dat = {" -Implant Specifications:
                    -Name: Robust Corp RX-78 Intimidation Class Implant
                    -Life: Activates upon codephrase.
                    -Important Notes: Explodes
                    -
                    -Implant Details:
                    -Function: Contains a compact, electrically detonated explosive that detonates upon receiving a specially encoded signal or upon host death.
                    -Special Features: Explodes
                    -Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} - return dat - -/obj/item/weapon/implant/explosive/hear_talk(mob/M, list/message_pieces, verb) - var/msg = multilingual_to_message(message_pieces) - hear(msg) - return - -/obj/item/weapon/implant/explosive/hear(var/msg) - var/list/replacechars = list("'" = "","\"" = "",">" = "","<" = "","(" = "",")" = "") - msg = replace_characters(msg, replacechars) - if(findtext(msg,phrase)) - activate() - qdel(src) - -/obj/item/weapon/implant/explosive/activate() - if (malfunction == MALFUNCTION_PERMANENT) - return - - if(istype(imp_in, /mob/)) - var/mob/T = imp_in - message_admins("Explosive implant triggered in [T] ([T.key]). (JMP) ") - log_game("Explosive implant triggered in [T] ([T.key]).") - - if(ishuman(imp_in)) - if (elevel == "Localized Limb") - if(part) //For some reason, small_boom() didn't work. So have this bit of working copypaste. - imp_in.visible_message("Something beeps inside [imp_in][part ? "'s [part.name]" : ""]!") - playsound(src, 'sound/items/countdown.ogg', 75, 1, -3) - sleep(25) - if (istype(part,/obj/item/organ/external/chest) || \ - istype(part,/obj/item/organ/external/groin) || \ - istype(part,/obj/item/organ/external/head)) - part.createwound(BRUISE, 80) //mangle them instead - explosion(get_turf(imp_in), -1, -1, 1, 3) - qdel(src) - else - explosion(get_turf(imp_in), -1, -1, 1, 3) - part.droplimb(0,DROPLIMB_BLUNT) - qdel(src) - if (elevel == "Destroy Body") - explosion(get_turf(T), -1, 0, 1, 6) - T.gib() - if (elevel == "Full Explosion") - explosion(get_turf(T), 0, 1, 3, 6) - T.gib() - - else - explosion(get_turf(imp_in), 0, 1, 3, 6) - - var/turf/t = get_turf(imp_in) - - if(t) - t.hotspot_expose(3500,125) - -/obj/item/weapon/implant/explosive/post_implant(mob/source as mob) - elevel = tgui_alert(usr, "What sort of explosion would you prefer?", "Implant Intent", list("Localized Limb", "Destroy Body", "Full Explosion")) - phrase = tgui_input_text(usr, "Choose activation phrase:") - var/list/replacechars = list("'" = "","\"" = "",">" = "","<" = "","(" = "",")" = "") - phrase = replace_characters(phrase, replacechars) - usr.mind.store_memory("Explosive implant in [source] can be activated by saying something containing the phrase ''[src.phrase]'', say [src.phrase] to attempt to activate.", 0, 0) - to_chat(usr, "The implanted explosive implant in [source] can be activated by saying something containing the phrase ''[src.phrase]'', say [src.phrase] to attempt to activate.") - -/obj/item/weapon/implant/explosive/emp_act(severity) - if (malfunction) - return - malfunction = MALFUNCTION_TEMPORARY - switch (severity) - if (4) //Weak EMP will make implant tear limbs off. - if (prob(25)) - small_boom() - if (3) //Weak EMP will make implant tear limbs off. - if (prob(50)) - small_boom() - if (2) //strong EMP will melt implant either making it go off, or disarming it - if (prob(70)) - if (prob(75)) - small_boom() - else - if (prob(13)) - activate() //chance of bye bye - else - meltdown() //chance of implant disarming - if (1) //strong EMP will melt implant either making it go off, or disarming it - if (prob(70)) - if (prob(50)) - small_boom() - else - if (prob(50)) - activate() //50% chance of bye bye - else - meltdown() //50% chance of implant disarming - spawn (20) - malfunction-- - -/obj/item/weapon/implant/explosive/islegal() - return 0 - -/obj/item/weapon/implant/explosive/proc/small_boom() - if (ishuman(imp_in) && part) - imp_in.visible_message("Something beeps inside [imp_in][part ? "'s [part.name]" : ""]!") - playsound(src, 'sound/items/countdown.ogg', 75, 1, -3) - spawn(25) - if (ishuman(imp_in) && part) - //No tearing off these parts since it's pretty much killing - //and you can't replace groins - if (istype(part,/obj/item/organ/external/chest) || \ - istype(part,/obj/item/organ/external/groin) || \ - istype(part,/obj/item/organ/external/head)) - part.createwound(BRUISE, 80) //mangle them instead - else - part.droplimb(0,DROPLIMB_BLUNT) - explosion(get_turf(imp_in), -1, -1, 1, 3) - qdel(src) - -////////////////////////////// -// Chemical Implant -////////////////////////////// -GLOBAL_LIST_BOILERPLATE(all_chem_implants, /obj/item/weapon/implant/chem) - -/obj/item/weapon/implant/chem - name = "chemical implant" - desc = "Injects things." - allow_reagents = 1 - known_implant = TRUE - -/obj/item/weapon/implant/chem/get_data() - var/dat = {" -Implant Specifications:
                    -Name: Robust Corp MJ-420 Prisoner Management Implant
                    -Life: Deactivates upon death but remains within the body.
                    -Important Notes: Due to the system functioning off of nutrients in the implanted subject's body, the subject
                    -will suffer from an increased appetite.

                    -
                    -Implant Details:
                    -Function: Contains a small capsule that can contain various chemicals. Upon receiving a specially encoded signal
                    -the implant releases the chemicals directly into the blood stream.
                    -Special Features: -Micro-Capsule- Can be loaded with any sort of chemical agent via the common syringe and can hold 50 units.
                    -Can only be loaded while still in its original case.
                    -Integrity: Implant will last so long as the subject is alive. However, if the subject suffers from malnutrition,
                    -the implant may become unstable and either pre-maturely inject the subject or simply break."} - return dat - -/obj/item/weapon/implant/chem/New() - ..() - var/datum/reagents/R = new/datum/reagents(50) - reagents = R - R.my_atom = src - -/obj/item/weapon/implant/chem/trigger(emote, source as mob) - if(emote == "deathgasp") - src.activate(src.reagents.total_volume) - return - -/obj/item/weapon/implant/chem/activate(var/cause) - if((!cause) || (!src.imp_in)) return 0 - var/mob/living/carbon/R = src.imp_in - src.reagents.trans_to_mob(R, cause, CHEM_BLOOD) - to_chat(R, "You hear a faint *beep*.") - if(!src.reagents.total_volume) - to_chat(R, "You hear a faint click from your chest.") - playsound(R, 'sound/weapons/empty.ogg', 10, 1) - spawn(0) - qdel(src) - return - -/obj/item/weapon/implant/chem/emp_act(severity) - if (malfunction) - return - malfunction = MALFUNCTION_TEMPORARY - - switch(severity) - if(1) - if(prob(60)) - activate(20) - if(2) - if(prob(40)) - activate(20) - if(3) - if(prob(40)) - activate(5) - if(4) - if(prob(20)) - activate(5) - - spawn(20) - malfunction-- - -////////////////////////////// -// Loyalty Implant -////////////////////////////// -/obj/item/weapon/implant/loyalty - name = "loyalty implant" - desc = "Makes you loyal or such." - known_implant = TRUE - -/obj/item/weapon/implant/loyalty/get_data() - var/dat = {" -Implant Specifications:
                    -Name: [using_map.company_name] Employee Management Implant
                    -Life: Ten years.
                    -Important Notes: Personnel injected with this device tend to be much more loyal to the company.
                    -
                    -Implant Details:
                    -Function: Contains a small pod of nanobots that manipulate the host's mental functions.
                    -Special Features: Will prevent and cure most forms of brainwashing.
                    -Integrity: Implant will last so long as the nanobots are inside the bloodstream."} - return dat - -/obj/item/weapon/implant/loyalty/handle_implant(mob/M, target_zone = BP_TORSO) - . = ..(M, target_zone) - if(!istype(M, /mob/living/carbon/human)) - . = FALSE - var/mob/living/carbon/human/H = M - var/datum/antagonist/antag_data = get_antag_data(H.mind.special_role) - if(antag_data && (antag_data.flags & ANTAG_IMPLANT_IMMUNE)) - H.visible_message("[H] seems to resist the implant!", "You feel the corporate tendrils of [using_map.company_name] try to invade your mind!") - . = FALSE - -/obj/item/weapon/implant/loyalty/post_implant(mob/M) - var/mob/living/carbon/human/H = M - clear_antag_roles(H.mind, 1) - to_chat(H, "You feel a surge of loyalty towards [using_map.company_name].") - -////////////////////////////// -// Adrenaline Implant -////////////////////////////// -/obj/item/weapon/implant/adrenalin - name = "adrenalin" - desc = "Removes all stuns and knockdowns." - var/uses - -/obj/item/weapon/implant/adrenalin/get_data() - var/dat = {" -Implant Specifications:
                    -Name: Cybersun Industries Adrenalin Implant
                    -Life: Five days.
                    -Important Notes: Illegal
                    -
                    -Implant Details: Subjects injected with implant can activate a massive injection of adrenalin.
                    -Function: Contains nanobots to stimulate body to mass-produce Adrenalin.
                    -Special Features: Will prevent and cure most forms of brainwashing.
                    -Integrity: Implant can only be used three times before the nanobots are depleted."} - return dat - - -/obj/item/weapon/implant/adrenalin/trigger(emote, mob/source as mob) - if (src.uses < 1) return 0 - if (emote == "pale") - src.uses-- - to_chat(source, "You feel a sudden surge of energy!") - source.SetStunned(0) - source.SetWeakened(0) - source.SetParalysis(0) - - return - -/obj/item/weapon/implant/adrenalin/post_implant(mob/source) - source.mind.store_memory("A implant can be activated by using the pale emote, say *pale to attempt to activate.", 0, 0) - to_chat(source, "The implanted freedom implant can be activated by using the pale emote, say *pale to attempt to activate.") - -////////////////////////////// -// Death Alarm Implant -////////////////////////////// -/obj/item/weapon/implant/death_alarm - name = "death alarm implant" - desc = "An alarm which monitors host vital signs and transmits a radio message upon death." - origin_tech = list(TECH_MATERIAL = 1, TECH_BIO = 2, TECH_DATA = 1) - known_implant = TRUE - var/mobname = "Will Robinson" - -/obj/item/weapon/implant/death_alarm/get_data() - var/dat = {" -Implant Specifications:
                    -Name: [using_map.company_name] \"Profit Margin\" Class Employee Lifesign Sensor
                    -Life: Activates upon death.
                    -Important Notes: Alerts crew to crewmember death.
                    -
                    -Implant Details:
                    -Function: Contains a compact radio signaler that triggers when the host's lifesigns cease.
                    -Special Features: Alerts crew to crewmember death.
                    -Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} - return dat - -/obj/item/weapon/implant/death_alarm/process() - if (!implanted) return - var/mob/M = imp_in - - if(isnull(M)) // If the mob got gibbed - activate() - else if(M.stat == 2) - activate("death") - -/obj/item/weapon/implant/death_alarm/activate(var/cause) - var/mob/M = imp_in - var/area/t = get_area(M) - switch (cause) - if("death") - var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) - if(istype(t, /area/syndicate_station) || istype(t, /area/syndicate_mothership) || istype(t, /area/shuttle/syndicate_elite) ) - //give the syndies a bit of stealth - a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm") -// a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm", "Security") -// a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm", "Medical") - else - a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm") -// a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm", "Security") -// a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm", "Medical") - qdel(a) - STOP_PROCESSING(SSobj, src) - if ("emp") - var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) - var/name = prob(50) ? t.name : pick(teleportlocs) - a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm") -// a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm", "Security") -// a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm", "Medical") - qdel(a) - else - var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) - a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm") -// a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm", "Security") -// a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm", "Medical") - qdel(a) - STOP_PROCESSING(SSobj, src) - -/obj/item/weapon/implant/death_alarm/emp_act(severity) //for some reason alarms stop going off in case they are emp'd, even without this - if (malfunction) //so I'm just going to add a meltdown chance here - return - malfunction = MALFUNCTION_TEMPORARY - - activate("emp") //let's shout that this dude is dead - if(severity == 1) - if(prob(40)) //small chance of obvious meltdown - meltdown() - else if (prob(60)) //but more likely it will just quietly die - malfunction = MALFUNCTION_PERMANENT - STOP_PROCESSING(SSobj, src) - - spawn(20) - malfunction-- - -/obj/item/weapon/implant/death_alarm/post_implant(mob/source as mob) - mobname = source.real_name - START_PROCESSING(SSobj, src) - -////////////////////////////// -// Compressed Matter Implant -////////////////////////////// -/obj/item/weapon/implant/compressed - name = "compressed matter implant" - desc = "Based on compressed matter technology, can store a single item." - icon_state = "implant_evil" - var/activation_emote = "sigh" - var/obj/item/scanned = null - origin_tech = list(TECH_MATERIAL = 4, TECH_BIO = 2, TECH_ILLEGAL = 2) - -/obj/item/weapon/implant/compressed/get_data() - var/dat = {" -Implant Specifications:
                    -Name: [using_map.company_name] \"Profit Margin\" Class Employee Lifesign Sensor
                    -Life: Activates upon death.
                    -Important Notes: Alerts crew to crewmember death.
                    -
                    -Implant Details:
                    -Function: Contains a compact radio signaler that triggers when the host's lifesigns cease.
                    -Special Features: Alerts crew to crewmember death.
                    -Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} - return dat - -/obj/item/weapon/implant/compressed/trigger(emote, mob/source as mob) - if (src.scanned == null) - return 0 - - if (emote == src.activation_emote) - to_chat(source, "The air glows as \the [src.scanned.name] uncompresses.") - activate() - -/obj/item/weapon/implant/compressed/activate() - var/turf/t = get_turf(src) - if (imp_in) - imp_in.put_in_hands(scanned) - else - scanned.loc = t - qdel(src) - -/obj/item/weapon/implant/compressed/post_implant(mob/source) - var/choices = list("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") - activation_emote = tgui_input_list(usr, "Choose activation emote. If you cancel this, one will be picked at random.", "Implant Activation", choices) - if(!activation_emote) - activation_emote = pick(choices) - if (source.mind) - source.mind.store_memory("Compressed matter implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.", 0, 0) - to_chat(source, "The implanted compressed matter implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.") - - -/obj/item/weapon/implant/compressed/islegal() - return 0 +#define MALFUNCTION_TEMPORARY 1 +#define MALFUNCTION_PERMANENT 2 + + +/obj/item/weapon/implant + name = "implant" + icon = 'icons/obj/device.dmi' + icon_state = "implant" + w_class = ITEMSIZE_TINY + show_messages = TRUE + + var/implanted = null + var/mob/imp_in = null + var/obj/item/organ/external/part = null + var/implant_color = "b" + var/allow_reagents = 0 + var/malfunction = 0 + var/initialize_loc = BP_TORSO + var/known_implant = FALSE + +/obj/item/weapon/implant/proc/trigger(emote, source as mob) + return + +/obj/item/weapon/implant/proc/activate() + return + +// Moves the implant where it needs to go, and tells it if there's more to be done in post_implant +/obj/item/weapon/implant/proc/handle_implant(var/mob/source, var/target_zone = BP_TORSO) + . = TRUE + imp_in = source + implanted = TRUE + if(ishuman(source)) + var/mob/living/carbon/human/H = source + var/obj/item/organ/external/affected = H.get_organ(target_zone) + if(affected) + affected.implants |= src + part = affected + if(part) + forceMove(part) + else + forceMove(source) + + listening_objects |= src + +// Takes place after handle_implant, if that returns TRUE +/obj/item/weapon/implant/proc/post_implant(var/mob/source) + +/obj/item/weapon/implant/proc/get_data() + return "No information available" + +/obj/item/weapon/implant/proc/hear(message, source as mob) + return + +/obj/item/weapon/implant/proc/islegal() + return 0 + +/obj/item/weapon/implant/proc/meltdown() //breaks it down, making implant unrecongizible + to_chat(imp_in, "You feel something melting inside [part ? "your [part.name]" : "you"]!") + if (part) + part.take_damage(burn = 15, used_weapon = "Electronics meltdown") + else + var/mob/living/M = imp_in + M.apply_damage(15,BURN) + name = "melted implant" + desc = "Charred circuit in melted plastic case. Wonder what that used to be..." + icon_state = "implant_melted" + malfunction = MALFUNCTION_PERMANENT + +/obj/item/weapon/implant/proc/implant_loadout(var/mob/living/carbon/human/H) + . = istype(H) && handle_implant(H, initialize_loc) + if(.) + invisibility = initial(invisibility) + known_implant = TRUE + post_implant(H) + +/obj/item/weapon/implant/Destroy() + if(part) + part.implants.Remove(src) + part = null + listening_objects.Remove(src) + imp_in = null + return ..() + +/obj/item/weapon/implant/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/weapon/implanter)) + var/obj/item/weapon/implanter/implanter = I + if(implanter.imp) + return // It's full. + user.drop_from_inventory(src) + forceMove(implanter) + implanter.imp = src + implanter.update() + else + ..() + + + +////////////////////////////// +// Tracking Implant +////////////////////////////// +GLOBAL_LIST_BOILERPLATE(all_tracking_implants, /obj/item/weapon/implant/tracking) + +/obj/item/weapon/implant/tracking + name = "tracking implant" + desc = "An implant normally given to dangerous criminals. Allows security to track your location." + known_implant = TRUE + var/id = 1 + var/degrade_time = 10 MINUTES //How long before the implant stops working outside of a living body. + +/obj/item/weapon/implant/tracking/weak //This is for the loadout + degrade_time = 2.5 MINUTES + +/obj/item/weapon/implant/tracking/New() + id = rand(1, 1000) + ..() + +/obj/item/weapon/implant/tracking/post_implant(var/mob/source) + START_PROCESSING(SSobj, src) + +/obj/item/weapon/implant/tracking/Destroy() + STOP_PROCESSING(SSobj, src) + if(part) + part.implants -= src + part = imp_in = null + return ..() + +/obj/item/weapon/implant/tracking/process() + var/implant_location = src.loc + if(ismob(implant_location)) + var/mob/living/L = implant_location + if(L.stat == DEAD) + if(world.time >= L.timeofdeath + degrade_time) + name = "melted implant" + desc = "Charred circuit in melted plastic case. Wonder what that used to be..." + icon_state = "implant_melted" + malfunction = MALFUNCTION_PERMANENT + STOP_PROCESSING(SSobj, src) + return 1 + +/obj/item/weapon/implant/tracking/get_data() + var/dat = {"Implant Specifications:
                    +Name: Tracking Beacon
                    +Life: 10 minutes after death of host
                    +Important Notes: None
                    +
                    +Implant Details:
                    +Function: Continuously transmits low power signal. Useful for tracking.
                    +Special Features:
                    +Neuro-Safe- Specialized shell absorbs excess voltages self-destructing the chip if +a malfunction occurs thereby securing safety of subject. The implant will melt and +disintegrate into bio-safe elements.
                    +Integrity: Gradient creates slight risk of being overcharged and frying the +circuitry. As a result neurotoxins can cause massive damage.
                    +Implant Specifics:
                    "} + return dat + +/obj/item/weapon/implant/tracking/emp_act(severity) + if (malfunction) //no, dawg, you can't malfunction while you are malfunctioning + return + malfunction = MALFUNCTION_TEMPORARY + + var/delay = 20 + switch(severity) + if(1) + if(prob(60)) + meltdown() + if(2) + delay = rand(5*60*10,15*60*10) //from 5 to 15 minutes of free time + if(3) + delay = rand(2*60*10,5*60*10) //from 2 to 5 minutes of free time + if(4) + delay = rand(0.5*60*10,1*60*10) //from .5 to 1 minutes of free time + + spawn(delay) + malfunction-- + +////////////////////////////// +// Death Explosive Implant +////////////////////////////// +/obj/item/weapon/implant/dexplosive + name = "explosive" + desc = "And boom goes the weasel." + icon_state = "implant_evil" + +/obj/item/weapon/implant/dexplosive/get_data() + var/dat = {" +Implant Specifications:
                    +Name: Robust Corp RX-78 Employee Management Implant
                    +Life: Activates upon death.
                    +Important Notes: Explodes
                    +
                    +Implant Details:
                    +Function: Contains a compact, electrically detonated explosive that detonates upon receiving a specially encoded signal or upon host death.
                    +Special Features: Explodes
                    +Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} + return dat + + +/obj/item/weapon/implant/dexplosive/trigger(emote, source as mob) + if(emote == "deathgasp") + src.activate("death") + return + + +/obj/item/weapon/implant/dexplosive/activate(var/cause) + if((!cause) || (!src.imp_in)) return 0 + explosion(src, -1, 0, 2, 3, 0)//This might be a bit much, dono will have to see. + if(src.imp_in) + src.imp_in.gib() + +/obj/item/weapon/implant/dexplosive/islegal() + return 0 + +////////////////////////////// +// Explosive Implant +////////////////////////////// +/obj/item/weapon/implant/explosive + name = "explosive implant" + desc = "A military grade micro bio-explosive. Highly dangerous." + var/elevel = "Localized Limb" + var/phrase = "supercalifragilisticexpialidocious" + icon_state = "implant_evil" + +/obj/item/weapon/implant/explosive/get_data() + var/dat = {" +Implant Specifications:
                    +Name: Robust Corp RX-78 Intimidation Class Implant
                    +Life: Activates upon codephrase.
                    +Important Notes: Explodes
                    +
                    +Implant Details:
                    +Function: Contains a compact, electrically detonated explosive that detonates upon receiving a specially encoded signal or upon host death.
                    +Special Features: Explodes
                    +Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} + return dat + +/obj/item/weapon/implant/explosive/hear_talk(mob/M, list/message_pieces, verb) + var/msg = multilingual_to_message(message_pieces) + hear(msg) + return + +/obj/item/weapon/implant/explosive/hear(var/msg) + var/list/replacechars = list("'" = "","\"" = "",">" = "","<" = "","(" = "",")" = "") + msg = replace_characters(msg, replacechars) + if(findtext(msg,phrase)) + activate() + qdel(src) + +/obj/item/weapon/implant/explosive/activate() + if (malfunction == MALFUNCTION_PERMANENT) + return + + if(istype(imp_in, /mob/)) + var/mob/T = imp_in + message_admins("Explosive implant triggered in [T] ([T.key]). (JMP) ") + log_game("Explosive implant triggered in [T] ([T.key]).") + + if(ishuman(imp_in)) + if (elevel == "Localized Limb") + if(part) //For some reason, small_boom() didn't work. So have this bit of working copypaste. + imp_in.visible_message("Something beeps inside [imp_in][part ? "'s [part.name]" : ""]!") + playsound(src, 'sound/items/countdown.ogg', 75, 1, -3) + sleep(25) + if (istype(part,/obj/item/organ/external/chest) || \ + istype(part,/obj/item/organ/external/groin) || \ + istype(part,/obj/item/organ/external/head)) + part.createwound(BRUISE, 80) //mangle them instead + explosion(get_turf(imp_in), -1, -1, 1, 3) + qdel(src) + else + explosion(get_turf(imp_in), -1, -1, 1, 3) + part.droplimb(0,DROPLIMB_BLUNT) + qdel(src) + if (elevel == "Destroy Body") + explosion(get_turf(T), -1, 0, 1, 6) + T.gib() + if (elevel == "Full Explosion") + explosion(get_turf(T), 0, 1, 3, 6) + T.gib() + + else + explosion(get_turf(imp_in), 0, 1, 3, 6) + + var/turf/t = get_turf(imp_in) + + if(t) + t.hotspot_expose(3500,125) + +/obj/item/weapon/implant/explosive/post_implant(mob/source as mob) + elevel = tgui_alert(usr, "What sort of explosion would you prefer?", "Implant Intent", list("Localized Limb", "Destroy Body", "Full Explosion")) + phrase = tgui_input_text(usr, "Choose activation phrase:") + var/list/replacechars = list("'" = "","\"" = "",">" = "","<" = "","(" = "",")" = "") + phrase = replace_characters(phrase, replacechars) + usr.mind.store_memory("Explosive implant in [source] can be activated by saying something containing the phrase ''[src.phrase]'', say [src.phrase] to attempt to activate.", 0, 0) + to_chat(usr, "The implanted explosive implant in [source] can be activated by saying something containing the phrase ''[src.phrase]'', say [src.phrase] to attempt to activate.") + +/obj/item/weapon/implant/explosive/emp_act(severity) + if (malfunction) + return + malfunction = MALFUNCTION_TEMPORARY + switch (severity) + if (4) //Weak EMP will make implant tear limbs off. + if (prob(25)) + small_boom() + if (3) //Weak EMP will make implant tear limbs off. + if (prob(50)) + small_boom() + if (2) //strong EMP will melt implant either making it go off, or disarming it + if (prob(70)) + if (prob(75)) + small_boom() + else + if (prob(13)) + activate() //chance of bye bye + else + meltdown() //chance of implant disarming + if (1) //strong EMP will melt implant either making it go off, or disarming it + if (prob(70)) + if (prob(50)) + small_boom() + else + if (prob(50)) + activate() //50% chance of bye bye + else + meltdown() //50% chance of implant disarming + spawn (20) + malfunction-- + +/obj/item/weapon/implant/explosive/islegal() + return 0 + +/obj/item/weapon/implant/explosive/proc/small_boom() + if (ishuman(imp_in) && part) + imp_in.visible_message("Something beeps inside [imp_in][part ? "'s [part.name]" : ""]!") + playsound(src, 'sound/items/countdown.ogg', 75, 1, -3) + spawn(25) + if (ishuman(imp_in) && part) + //No tearing off these parts since it's pretty much killing + //and you can't replace groins + if (istype(part,/obj/item/organ/external/chest) || \ + istype(part,/obj/item/organ/external/groin) || \ + istype(part,/obj/item/organ/external/head)) + part.createwound(BRUISE, 80) //mangle them instead + else + part.droplimb(0,DROPLIMB_BLUNT) + explosion(get_turf(imp_in), -1, -1, 1, 3) + qdel(src) + +////////////////////////////// +// Chemical Implant +////////////////////////////// +GLOBAL_LIST_BOILERPLATE(all_chem_implants, /obj/item/weapon/implant/chem) + +/obj/item/weapon/implant/chem + name = "chemical implant" + desc = "Injects things." + allow_reagents = 1 + known_implant = TRUE + +/obj/item/weapon/implant/chem/get_data() + var/dat = {" +Implant Specifications:
                    +Name: Robust Corp MJ-420 Prisoner Management Implant
                    +Life: Deactivates upon death but remains within the body.
                    +Important Notes: Due to the system functioning off of nutrients in the implanted subject's body, the subject
                    +will suffer from an increased appetite.

                    +
                    +Implant Details:
                    +Function: Contains a small capsule that can contain various chemicals. Upon receiving a specially encoded signal
                    +the implant releases the chemicals directly into the blood stream.
                    +Special Features: +Micro-Capsule- Can be loaded with any sort of chemical agent via the common syringe and can hold 50 units.
                    +Can only be loaded while still in its original case.
                    +Integrity: Implant will last so long as the subject is alive. However, if the subject suffers from malnutrition,
                    +the implant may become unstable and either pre-maturely inject the subject or simply break."} + return dat + +/obj/item/weapon/implant/chem/New() + ..() + var/datum/reagents/R = new/datum/reagents(50) + reagents = R + R.my_atom = src + +/obj/item/weapon/implant/chem/trigger(emote, source as mob) + if(emote == "deathgasp") + src.activate(src.reagents.total_volume) + return + +/obj/item/weapon/implant/chem/activate(var/cause) + if((!cause) || (!src.imp_in)) return 0 + var/mob/living/carbon/R = src.imp_in + src.reagents.trans_to_mob(R, cause, CHEM_BLOOD) + to_chat(R, "You hear a faint *beep*.") + if(!src.reagents.total_volume) + to_chat(R, "You hear a faint click from your chest.") + playsound(R, 'sound/weapons/empty.ogg', 10, 1) + spawn(0) + qdel(src) + return + +/obj/item/weapon/implant/chem/emp_act(severity) + if (malfunction) + return + malfunction = MALFUNCTION_TEMPORARY + + switch(severity) + if(1) + if(prob(60)) + activate(20) + if(2) + if(prob(40)) + activate(20) + if(3) + if(prob(40)) + activate(5) + if(4) + if(prob(20)) + activate(5) + + spawn(20) + malfunction-- + +////////////////////////////// +// Loyalty Implant +////////////////////////////// +/obj/item/weapon/implant/loyalty + name = "loyalty implant" + desc = "Makes you loyal or such." + known_implant = TRUE + +/obj/item/weapon/implant/loyalty/get_data() + var/dat = {" +Implant Specifications:
                    +Name: [using_map.company_name] Employee Management Implant
                    +Life: Ten years.
                    +Important Notes: Personnel injected with this device tend to be much more loyal to the company.
                    +
                    +Implant Details:
                    +Function: Contains a small pod of nanobots that manipulate the host's mental functions.
                    +Special Features: Will prevent and cure most forms of brainwashing.
                    +Integrity: Implant will last so long as the nanobots are inside the bloodstream."} + return dat + +/obj/item/weapon/implant/loyalty/handle_implant(mob/M, target_zone = BP_TORSO) + . = ..(M, target_zone) + if(!istype(M, /mob/living/carbon/human)) + . = FALSE + var/mob/living/carbon/human/H = M + var/datum/antagonist/antag_data = get_antag_data(H.mind.special_role) + if(antag_data && (antag_data.flags & ANTAG_IMPLANT_IMMUNE)) + H.visible_message("[H] seems to resist the implant!", "You feel the corporate tendrils of [using_map.company_name] try to invade your mind!") + . = FALSE + +/obj/item/weapon/implant/loyalty/post_implant(mob/M) + var/mob/living/carbon/human/H = M + clear_antag_roles(H.mind, 1) + to_chat(H, "You feel a surge of loyalty towards [using_map.company_name].") + +////////////////////////////// +// Adrenaline Implant +////////////////////////////// +/obj/item/weapon/implant/adrenalin + name = "adrenalin" + desc = "Removes all stuns and knockdowns." + var/uses + +/obj/item/weapon/implant/adrenalin/get_data() + var/dat = {" +Implant Specifications:
                    +Name: Cybersun Industries Adrenalin Implant
                    +Life: Five days.
                    +Important Notes: Illegal
                    +
                    +Implant Details: Subjects injected with implant can activate a massive injection of adrenalin.
                    +Function: Contains nanobots to stimulate body to mass-produce Adrenalin.
                    +Special Features: Will prevent and cure most forms of brainwashing.
                    +Integrity: Implant can only be used three times before the nanobots are depleted."} + return dat + + +/obj/item/weapon/implant/adrenalin/trigger(emote, mob/source as mob) + if (src.uses < 1) return 0 + if (emote == "pale") + src.uses-- + to_chat(source, "You feel a sudden surge of energy!") + source.SetStunned(0) + source.SetWeakened(0) + source.SetParalysis(0) + + return + +/obj/item/weapon/implant/adrenalin/post_implant(mob/source) + source.mind.store_memory("A implant can be activated by using the pale emote, say *pale to attempt to activate.", 0, 0) + to_chat(source, "The implanted freedom implant can be activated by using the pale emote, say *pale to attempt to activate.") + +////////////////////////////// +// Death Alarm Implant +////////////////////////////// +/obj/item/weapon/implant/death_alarm + name = "death alarm implant" + desc = "An alarm which monitors host vital signs and transmits a radio message upon death." + origin_tech = list(TECH_MATERIAL = 1, TECH_BIO = 2, TECH_DATA = 1) + known_implant = TRUE + var/mobname = "Will Robinson" + +/obj/item/weapon/implant/death_alarm/get_data() + var/dat = {" +Implant Specifications:
                    +Name: [using_map.company_name] \"Profit Margin\" Class Employee Lifesign Sensor
                    +Life: Activates upon death.
                    +Important Notes: Alerts crew to crewmember death.
                    +
                    +Implant Details:
                    +Function: Contains a compact radio signaler that triggers when the host's lifesigns cease.
                    +Special Features: Alerts crew to crewmember death.
                    +Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} + return dat + +/obj/item/weapon/implant/death_alarm/process() + if (!implanted) return + var/mob/M = imp_in + + if(isnull(M)) // If the mob got gibbed + activate() + else if(M.stat == 2) + activate("death") + +/obj/item/weapon/implant/death_alarm/activate(var/cause) + var/mob/M = imp_in + var/area/t = get_area(M) + switch (cause) + if("death") + var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) + if(istype(t, /area/syndicate_station) || istype(t, /area/syndicate_mothership) || istype(t, /area/shuttle/syndicate_elite) ) + //give the syndies a bit of stealth + a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm") +// a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm", "Security") +// a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm", "Medical") + else + a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm") +// a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm", "Security") +// a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm", "Medical") + qdel(a) + STOP_PROCESSING(SSobj, src) + if ("emp") + var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) + var/name = prob(50) ? t.name : pick(teleportlocs) + a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm") +// a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm", "Security") +// a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm", "Medical") + qdel(a) + else + var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) + a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm") +// a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm", "Security") +// a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm", "Medical") + qdel(a) + STOP_PROCESSING(SSobj, src) + +/obj/item/weapon/implant/death_alarm/emp_act(severity) //for some reason alarms stop going off in case they are emp'd, even without this + if (malfunction) //so I'm just going to add a meltdown chance here + return + malfunction = MALFUNCTION_TEMPORARY + + activate("emp") //let's shout that this dude is dead + if(severity == 1) + if(prob(40)) //small chance of obvious meltdown + meltdown() + else if (prob(60)) //but more likely it will just quietly die + malfunction = MALFUNCTION_PERMANENT + STOP_PROCESSING(SSobj, src) + + spawn(20) + malfunction-- + +/obj/item/weapon/implant/death_alarm/post_implant(mob/source as mob) + mobname = source.real_name + START_PROCESSING(SSobj, src) + +////////////////////////////// +// Compressed Matter Implant +////////////////////////////// +/obj/item/weapon/implant/compressed + name = "compressed matter implant" + desc = "Based on compressed matter technology, can store a single item." + icon_state = "implant_evil" + var/activation_emote = "sigh" + var/obj/item/scanned = null + origin_tech = list(TECH_MATERIAL = 4, TECH_BIO = 2, TECH_ILLEGAL = 2) + +/obj/item/weapon/implant/compressed/get_data() + var/dat = {" +Implant Specifications:
                    +Name: [using_map.company_name] \"Profit Margin\" Class Employee Lifesign Sensor
                    +Life: Activates upon death.
                    +Important Notes: Alerts crew to crewmember death.
                    +
                    +Implant Details:
                    +Function: Contains a compact radio signaler that triggers when the host's lifesigns cease.
                    +Special Features: Alerts crew to crewmember death.
                    +Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} + return dat + +/obj/item/weapon/implant/compressed/trigger(emote, mob/source as mob) + if (src.scanned == null) + return 0 + + if (emote == src.activation_emote) + to_chat(source, "The air glows as \the [src.scanned.name] uncompresses.") + activate() + +/obj/item/weapon/implant/compressed/activate() + var/turf/t = get_turf(src) + if (imp_in) + imp_in.put_in_hands(scanned) + else + scanned.loc = t + qdel(src) + +/obj/item/weapon/implant/compressed/post_implant(mob/source) + var/choices = list("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") + activation_emote = tgui_input_list(usr, "Choose activation emote. If you cancel this, one will be picked at random.", "Implant Activation", choices) + if(!activation_emote) + activation_emote = pick(choices) + if (source.mind) + source.mind.store_memory("Compressed matter implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.", 0, 0) + to_chat(source, "The implanted compressed matter implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.") + + +/obj/item/weapon/implant/compressed/islegal() + return 0 diff --git a/code/game/objects/items/weapons/implants/implantcase.dm b/code/game/objects/items/weapons/implants/implantcase.dm index 5e81318ac6b..ee6925c55c4 100644 --- a/code/game/objects/items/weapons/implants/implantcase.dm +++ b/code/game/objects/items/weapons/implants/implantcase.dm @@ -1,311 +1,311 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/item/weapon/implantcase - name = "glass case" - desc = "A case containing an implant." - icon = 'icons/obj/items.dmi' - icon_state = "implantcase-0" - item_state = "implantcase" - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_TINY - var/obj/item/weapon/implant/imp = null - -/obj/item/weapon/implantcase/proc/update() - if (src.imp) - src.icon_state = text("implantcase-[]", src.imp.implant_color) - else - src.icon_state = "implantcase-0" - return - -/obj/item/weapon/implantcase/attackby(obj/item/weapon/I as obj, mob/user as mob) - ..() - if (istype(I, /obj/item/weapon/pen)) - var/t = tgui_input_text(user, "What would you like the label to be?", text("[]", src.name), null, MAX_NAME_LEN) - if (user.get_active_hand() != I) - return - if((!in_range(src, usr) && src.loc != user)) - return - t = sanitizeSafe(t, MAX_NAME_LEN) - if(t) - src.name = text("Glass Case - '[]'", t) - else - src.name = "Glass Case" - else if(istype(I, /obj/item/weapon/reagent_containers/syringe)) - if(!src.imp) return - if(!src.imp.allow_reagents) return - if(src.imp.reagents.total_volume >= src.imp.reagents.maximum_volume) - to_chat(user, "\The [src] is full.") - else - spawn(5) - I.reagents.trans_to_obj(src.imp, 5) - to_chat(user, "You inject 5 units of the solution. The syringe now contains [I.reagents.total_volume] units.") - else if (istype(I, /obj/item/weapon/implanter)) - var/obj/item/weapon/implanter/M = I - if (M.imp) - if ((src.imp || M.imp.implanted)) - return - M.imp.loc = src - src.imp = M.imp - M.imp = null - src.update() - M.update() - else - if (src.imp) - if (M.imp) - return - src.imp.loc = M - M.imp = src.imp - src.imp = null - update() - M.update() - return - - -/obj/item/weapon/implantcase/tracking - name = "glass case - 'tracking'" - desc = "A case containing a tracking implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/tracking/New() - src.imp = new /obj/item/weapon/implant/tracking( src ) - ..() - return - - -/obj/item/weapon/implantcase/explosive - name = "glass case - 'explosive'" - desc = "A case containing an explosive implant." - icon_state = "implantcase-r" - -/obj/item/weapon/implantcase/explosive/New() - src.imp = new /obj/item/weapon/implant/explosive( src ) - ..() - return - - -/obj/item/weapon/implantcase/chem - name = "glass case - 'chem'" - desc = "A case containing a chemical implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/chem/New() - src.imp = new /obj/item/weapon/implant/chem( src ) - ..() - return - - -/obj/item/weapon/implantcase/loyalty - name = "glass case - 'loyalty'" - desc = "A case containing a loyalty implant." - icon_state = "implantcase-r" - -/obj/item/weapon/implantcase/loyalty/New() - src.imp = new /obj/item/weapon/implant/loyalty( src ) - ..() - return - - -/obj/item/weapon/implantcase/death_alarm - name = "glass case - 'death alarm'" - desc = "A case containing a death alarm implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/death_alarm/New() - src.imp = new /obj/item/weapon/implant/death_alarm( src ) - ..() - return - - -/obj/item/weapon/implantcase/freedom - name = "glass case - 'freedom'" - desc = "A case containing a freedom implant." - icon_state = "implantcase-r" - -/obj/item/weapon/implantcase/freedom/New() - src.imp = new /obj/item/weapon/implant/freedom( src ) - ..() - return - - -/obj/item/weapon/implantcase/adrenalin - name = "glass case - 'adrenalin'" - desc = "A case containing an adrenalin implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/adrenalin/New() - src.imp = new /obj/item/weapon/implant/adrenalin( src ) - ..() - return - - -/obj/item/weapon/implantcase/dexplosive - name = "glass case - 'explosive'" - desc = "A case containing an explosive." - icon_state = "implantcase-r" - -/obj/item/weapon/implantcase/dexplosive/New() - src.imp = new /obj/item/weapon/implant/dexplosive( src ) - ..() - return - - -/obj/item/weapon/implantcase/health - name = "glass case - 'health'" - desc = "A case containing a health tracking implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/health/New() - src.imp = new /obj/item/weapon/implant/health( src ) - ..() - return - -/obj/item/weapon/implantcase/language - name = "glass case - 'GalCom'" - desc = "A case containing a GalCom language implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/language/New() - src.imp = new /obj/item/weapon/implant/language( src ) - ..() - return - -/obj/item/weapon/implantcase/language/eal - name = "glass case - 'EAL'" - desc = "A case containing an Encoded Audio Language implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/language/eal/New() - src.imp = new /obj/item/weapon/implant/language/eal( src ) - ..() - return - -/obj/item/weapon/implantcase/shades - name = "glass case - 'Integrated Shades'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/shades/New() - src.imp = new /obj/item/weapon/implant/organ( src ) - ..() - return - -/obj/item/weapon/implantcase/taser - name = "glass case - 'Taser'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/taser/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment( src ) - ..() - return - -/obj/item/weapon/implantcase/laser - name = "glass case - 'Laser'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/laser/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/laser( src ) - ..() - return - -/obj/item/weapon/implantcase/dart - name = "glass case - 'Dart'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/dart/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/dart( src ) - ..() - return - -/obj/item/weapon/implantcase/toolkit - name = "glass case - 'Toolkit'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/toolkit/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm( src ) - ..() - return - -/obj/item/weapon/implantcase/medkit - name = "glass case - 'Toolkit'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/medkit/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm/medkit( src ) - ..() - return - -/obj/item/weapon/implantcase/surge - name = "glass case - 'Muscle Overclocker'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/surge/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm/surge( src ) - ..() - return - -/obj/item/weapon/implantcase/analyzer - name = "glass case - 'Scanner'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/analyzer/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist( src ) - ..() - return - -/obj/item/weapon/implantcase/sword - name = "glass case - 'Scanner'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/sword/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist/sword( src ) - ..() - return - -/obj/item/weapon/implantcase/sprinter - name = "glass case - 'Sprinter'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/sprinter/New() - src.imp = new /obj/item/weapon/implant/organ/pelvic( src ) - ..() - return - -/obj/item/weapon/implantcase/armblade - name = "glass case - 'Armblade'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/armblade/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm/blade( src ) - ..() - return - -/obj/item/weapon/implantcase/handblade - name = "glass case - 'Handblade'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/handblade/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist/blade( src ) - ..() - return - -/obj/item/weapon/implantcase/restrainingbolt - name = "glass case - 'Restraining Bolt'" - desc = "A case containing a restraining bolt." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/restrainingbolt/New() - src.imp = new /obj/item/weapon/implant/restrainingbolt( src ) - ..() - return +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/obj/item/weapon/implantcase + name = "glass case" + desc = "A case containing an implant." + icon = 'icons/obj/items.dmi' + icon_state = "implantcase-0" + item_state = "implantcase" + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_TINY + var/obj/item/weapon/implant/imp = null + +/obj/item/weapon/implantcase/proc/update() + if (src.imp) + src.icon_state = text("implantcase-[]", src.imp.implant_color) + else + src.icon_state = "implantcase-0" + return + +/obj/item/weapon/implantcase/attackby(obj/item/weapon/I as obj, mob/user as mob) + ..() + if (istype(I, /obj/item/weapon/pen)) + var/t = tgui_input_text(user, "What would you like the label to be?", text("[]", src.name), null, MAX_NAME_LEN) + if (user.get_active_hand() != I) + return + if((!in_range(src, usr) && src.loc != user)) + return + t = sanitizeSafe(t, MAX_NAME_LEN) + if(t) + src.name = text("Glass Case - '[]'", t) + else + src.name = "Glass Case" + else if(istype(I, /obj/item/weapon/reagent_containers/syringe)) + if(!src.imp) return + if(!src.imp.allow_reagents) return + if(src.imp.reagents.total_volume >= src.imp.reagents.maximum_volume) + to_chat(user, "\The [src] is full.") + else + spawn(5) + I.reagents.trans_to_obj(src.imp, 5) + to_chat(user, "You inject 5 units of the solution. The syringe now contains [I.reagents.total_volume] units.") + else if (istype(I, /obj/item/weapon/implanter)) + var/obj/item/weapon/implanter/M = I + if (M.imp) + if ((src.imp || M.imp.implanted)) + return + M.imp.loc = src + src.imp = M.imp + M.imp = null + src.update() + M.update() + else + if (src.imp) + if (M.imp) + return + src.imp.loc = M + M.imp = src.imp + src.imp = null + update() + M.update() + return + + +/obj/item/weapon/implantcase/tracking + name = "glass case - 'tracking'" + desc = "A case containing a tracking implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/tracking/New() + src.imp = new /obj/item/weapon/implant/tracking( src ) + ..() + return + + +/obj/item/weapon/implantcase/explosive + name = "glass case - 'explosive'" + desc = "A case containing an explosive implant." + icon_state = "implantcase-r" + +/obj/item/weapon/implantcase/explosive/New() + src.imp = new /obj/item/weapon/implant/explosive( src ) + ..() + return + + +/obj/item/weapon/implantcase/chem + name = "glass case - 'chem'" + desc = "A case containing a chemical implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/chem/New() + src.imp = new /obj/item/weapon/implant/chem( src ) + ..() + return + + +/obj/item/weapon/implantcase/loyalty + name = "glass case - 'loyalty'" + desc = "A case containing a loyalty implant." + icon_state = "implantcase-r" + +/obj/item/weapon/implantcase/loyalty/New() + src.imp = new /obj/item/weapon/implant/loyalty( src ) + ..() + return + + +/obj/item/weapon/implantcase/death_alarm + name = "glass case - 'death alarm'" + desc = "A case containing a death alarm implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/death_alarm/New() + src.imp = new /obj/item/weapon/implant/death_alarm( src ) + ..() + return + + +/obj/item/weapon/implantcase/freedom + name = "glass case - 'freedom'" + desc = "A case containing a freedom implant." + icon_state = "implantcase-r" + +/obj/item/weapon/implantcase/freedom/New() + src.imp = new /obj/item/weapon/implant/freedom( src ) + ..() + return + + +/obj/item/weapon/implantcase/adrenalin + name = "glass case - 'adrenalin'" + desc = "A case containing an adrenalin implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/adrenalin/New() + src.imp = new /obj/item/weapon/implant/adrenalin( src ) + ..() + return + + +/obj/item/weapon/implantcase/dexplosive + name = "glass case - 'explosive'" + desc = "A case containing an explosive." + icon_state = "implantcase-r" + +/obj/item/weapon/implantcase/dexplosive/New() + src.imp = new /obj/item/weapon/implant/dexplosive( src ) + ..() + return + + +/obj/item/weapon/implantcase/health + name = "glass case - 'health'" + desc = "A case containing a health tracking implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/health/New() + src.imp = new /obj/item/weapon/implant/health( src ) + ..() + return + +/obj/item/weapon/implantcase/language + name = "glass case - 'GalCom'" + desc = "A case containing a GalCom language implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/language/New() + src.imp = new /obj/item/weapon/implant/language( src ) + ..() + return + +/obj/item/weapon/implantcase/language/eal + name = "glass case - 'EAL'" + desc = "A case containing an Encoded Audio Language implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/language/eal/New() + src.imp = new /obj/item/weapon/implant/language/eal( src ) + ..() + return + +/obj/item/weapon/implantcase/shades + name = "glass case - 'Integrated Shades'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/shades/New() + src.imp = new /obj/item/weapon/implant/organ( src ) + ..() + return + +/obj/item/weapon/implantcase/taser + name = "glass case - 'Taser'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/taser/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment( src ) + ..() + return + +/obj/item/weapon/implantcase/laser + name = "glass case - 'Laser'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/laser/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/laser( src ) + ..() + return + +/obj/item/weapon/implantcase/dart + name = "glass case - 'Dart'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/dart/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/dart( src ) + ..() + return + +/obj/item/weapon/implantcase/toolkit + name = "glass case - 'Toolkit'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/toolkit/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm( src ) + ..() + return + +/obj/item/weapon/implantcase/medkit + name = "glass case - 'Toolkit'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/medkit/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm/medkit( src ) + ..() + return + +/obj/item/weapon/implantcase/surge + name = "glass case - 'Muscle Overclocker'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/surge/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm/surge( src ) + ..() + return + +/obj/item/weapon/implantcase/analyzer + name = "glass case - 'Scanner'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/analyzer/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist( src ) + ..() + return + +/obj/item/weapon/implantcase/sword + name = "glass case - 'Scanner'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/sword/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist/sword( src ) + ..() + return + +/obj/item/weapon/implantcase/sprinter + name = "glass case - 'Sprinter'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/sprinter/New() + src.imp = new /obj/item/weapon/implant/organ/pelvic( src ) + ..() + return + +/obj/item/weapon/implantcase/armblade + name = "glass case - 'Armblade'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/armblade/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm/blade( src ) + ..() + return + +/obj/item/weapon/implantcase/handblade + name = "glass case - 'Handblade'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/handblade/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist/blade( src ) + ..() + return + +/obj/item/weapon/implantcase/restrainingbolt + name = "glass case - 'Restraining Bolt'" + desc = "A case containing a restraining bolt." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/restrainingbolt/New() + src.imp = new /obj/item/weapon/implant/restrainingbolt( src ) + ..() + return diff --git a/code/game/objects/items/weapons/implants/implantchair.dm b/code/game/objects/items/weapons/implants/implantchair.dm index 34110d59249..134ea532bdb 100644 --- a/code/game/objects/items/weapons/implants/implantchair.dm +++ b/code/game/objects/items/weapons/implants/implantchair.dm @@ -1,162 +1,162 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/machinery/implantchair - name = "loyalty implanter" - desc = "Used to implant occupants with loyalty implants." - icon = 'icons/obj/machines/implantchair.dmi' - icon_state = "implantchair" - density = TRUE - opacity = 0 - anchored = TRUE - - var/ready = 1 - var/malfunction = 0 - var/list/obj/item/weapon/implant/loyalty/implant_list = list() - var/max_implants = 5 - var/injection_cooldown = 600 - var/replenish_cooldown = 6000 - var/replenishing = 0 - var/mob/living/carbon/occupant = null - var/injecting = 0 - -/obj/machinery/implantchair/New() - ..() - add_implants() - - -/obj/machinery/implantchair/attack_hand(mob/user as mob) - user.set_machine(src) - var/health_text = "" - if(src.occupant) - if(src.occupant.health <= -100) - health_text = "Dead" - else if(src.occupant.health < 0) - health_text = "[round(src.occupant.health,0.1)]" - else - health_text = "[round(src.occupant.health,0.1)]" - - var/dat ="Implanter Status
                    " - - dat +="Current occupant: [src.occupant ? "
                    Name: [src.occupant]
                    Health: [health_text]
                    " : "None"]
                    " - dat += "Implants: [src.implant_list.len ? "[implant_list.len]" : "Replenish"]
                    " - if(src.occupant) - dat += "[src.ready ? "Implant" : "Recharging"]
                    " - user.set_machine(src) - user << browse(dat, "window=implant") - onclose(user, "implant") - - -/obj/machinery/implantchair/Topic(href, href_list) - if((get_dist(src, usr) <= 1) || istype(usr, /mob/living/silicon/ai)) - if(href_list["implant"]) - if(src.occupant) - injecting = 1 - go_out() - ready = 0 - spawn(injection_cooldown) - ready = 1 - - if(href_list["replenish"]) - ready = 0 - spawn(replenish_cooldown) - add_implants() - ready = 1 - - src.updateUsrDialog() - src.add_fingerprint(usr) - return - - -/obj/machinery/implantchair/attackby(var/obj/item/weapon/G as obj, var/mob/user as mob) - if(istype(G, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/grab = G - if(!ismob(grab.affecting)) - return - if(grab.affecting.has_buckled_mobs()) - to_chat(user, span("warning", "\The [grab.affecting] has other entities attached to them. Remove them first.")) - return - var/mob/M = grab.affecting - if(put_mob(M)) - qdel(G) - src.updateUsrDialog() - return - - -/obj/machinery/implantchair/proc/go_out(var/mob/M) - if(!( src.occupant )) - return - if(M == occupant) // so that the guy inside can't eject himself -Agouri - return - if (src.occupant.client) - src.occupant.client.eye = src.occupant.client.mob - src.occupant.client.perspective = MOB_PERSPECTIVE - src.occupant.loc = src.loc - if(injecting) - implant(src.occupant) - injecting = 0 - src.occupant = null - icon_state = "implantchair" - return - - -/obj/machinery/implantchair/proc/put_mob(mob/living/carbon/M as mob) - if(!iscarbon(M)) - to_chat(usr, "\The [src] cannot hold this!") - return - if(src.occupant) - to_chat(usr, "\The [src] is already occupied!") - return - if(M.client) - M.client.perspective = EYE_PERSPECTIVE - M.client.eye = src - M.stop_pulling() - M.loc = src - src.occupant = M - src.add_fingerprint(usr) - icon_state = "implantchair_on" - return 1 - - -/obj/machinery/implantchair/proc/implant(var/mob/M) - if (!istype(M, /mob/living/carbon)) - return - if(!implant_list.len) return - for(var/obj/item/weapon/implant/loyalty/imp in implant_list) - if(!imp) continue - if(istype(imp, /obj/item/weapon/implant/loyalty)) - for (var/mob/O in viewers(M, null)) - O.show_message("\The [M] has been implanted by \the [src].", 1) - - if(imp.handle_implant(M, BP_TORSO)) - imp.post_implant(M) - - implant_list -= imp - break - return - - -/obj/machinery/implantchair/proc/add_implants() - for(var/i=0, iName: [src.occupant]
                    Health: [health_text]
                    " : "None"]
                    " + dat += "Implants: [src.implant_list.len ? "[implant_list.len]" : "Replenish"]
                    " + if(src.occupant) + dat += "[src.ready ? "Implant" : "Recharging"]
                    " + user.set_machine(src) + user << browse(dat, "window=implant") + onclose(user, "implant") + + +/obj/machinery/implantchair/Topic(href, href_list) + if((get_dist(src, usr) <= 1) || istype(usr, /mob/living/silicon/ai)) + if(href_list["implant"]) + if(src.occupant) + injecting = 1 + go_out() + ready = 0 + spawn(injection_cooldown) + ready = 1 + + if(href_list["replenish"]) + ready = 0 + spawn(replenish_cooldown) + add_implants() + ready = 1 + + src.updateUsrDialog() + src.add_fingerprint(usr) + return + + +/obj/machinery/implantchair/attackby(var/obj/item/weapon/G as obj, var/mob/user as mob) + if(istype(G, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/grab = G + if(!ismob(grab.affecting)) + return + if(grab.affecting.has_buckled_mobs()) + to_chat(user, span("warning", "\The [grab.affecting] has other entities attached to them. Remove them first.")) + return + var/mob/M = grab.affecting + if(put_mob(M)) + qdel(G) + src.updateUsrDialog() + return + + +/obj/machinery/implantchair/proc/go_out(var/mob/M) + if(!( src.occupant )) + return + if(M == occupant) // so that the guy inside can't eject himself -Agouri + return + if (src.occupant.client) + src.occupant.client.eye = src.occupant.client.mob + src.occupant.client.perspective = MOB_PERSPECTIVE + src.occupant.loc = src.loc + if(injecting) + implant(src.occupant) + injecting = 0 + src.occupant = null + icon_state = "implantchair" + return + + +/obj/machinery/implantchair/proc/put_mob(mob/living/carbon/M as mob) + if(!iscarbon(M)) + to_chat(usr, "\The [src] cannot hold this!") + return + if(src.occupant) + to_chat(usr, "\The [src] is already occupied!") + return + if(M.client) + M.client.perspective = EYE_PERSPECTIVE + M.client.eye = src + M.stop_pulling() + M.loc = src + src.occupant = M + src.add_fingerprint(usr) + icon_state = "implantchair_on" + return 1 + + +/obj/machinery/implantchair/proc/implant(var/mob/M) + if (!istype(M, /mob/living/carbon)) + return + if(!implant_list.len) return + for(var/obj/item/weapon/implant/loyalty/imp in implant_list) + if(!imp) continue + if(istype(imp, /obj/item/weapon/implant/loyalty)) + for (var/mob/O in viewers(M, null)) + O.show_message("\The [M] has been implanted by \the [src].", 1) + + if(imp.handle_implant(M, BP_TORSO)) + imp.post_implant(M) + + implant_list -= imp + break + return + + +/obj/machinery/implantchair/proc/add_implants() + for(var/i=0, iYou [active ? "" : "de"]activate \the [src].
                    ") - update() - -/obj/item/weapon/implanter/verb/remove_implant() - set category = "Object" - set name = "Remove Implant" - set src in usr - - if(!imp) - return - if(istype(usr, /mob)) - var/mob/M = usr - imp.loc = get_turf(src) - if(M.get_active_hand() == null) - M.put_in_hands(imp) - to_chat(M, "You remove \the [imp] from \the [src].") - name = "implanter" - imp = null - - update() - - return - -/obj/item/weapon/implanter/proc/update() - if (src.imp) - src.icon_state = "implanter1" - else - src.icon_state = "implanter0" - src.icon_state += "_[active]" - return - -/obj/item/weapon/implanter/attack(mob/M as mob, mob/user as mob) - if (!istype(M, /mob/living/carbon)) - return - if(active) - if (imp) - M.visible_message("[user] is attempting to implant [M].") - - user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) - user.do_attack_animation(M) - - var/turf/T1 = get_turf(M) - if (T1 && ((M == user) || do_after(user, 50))) - if(user && M && (get_turf(M) == T1) && src && src.imp) - M.visible_message("[M] has been implanted by [user].") - - add_attack_logs(user,M,"Implanted with [imp.name] using [name]") - - if(imp.handle_implant(M)) - imp.post_implant(M) - - if(ishuman(M)) - var/mob/living/carbon/human/H = M - BITSET(H.hud_updateflag, IMPLOYAL_HUD) - BITSET(H.hud_updateflag, BACKUP_HUD) //VOREStation Add - Backup HUD updates - - src.imp = null - update() - else - to_chat(user, "You need to activate \the [src.name] first.") - return - -/obj/item/weapon/implanter/loyalty - name = "implanter-loyalty" - -/obj/item/weapon/implanter/loyalty/New() - src.imp = new /obj/item/weapon/implant/loyalty( src ) - ..() - update() - return - -/obj/item/weapon/implanter/explosive - name = "implanter (E)" - -/obj/item/weapon/implanter/explosive/New() - src.imp = new /obj/item/weapon/implant/explosive( src ) - ..() - update() - return - -/obj/item/weapon/implanter/adrenalin - name = "implanter-adrenalin" - -/obj/item/weapon/implanter/adrenalin/New() - src.imp = new /obj/item/weapon/implant/adrenalin(src) - ..() - update() - return - -/obj/item/weapon/implanter/compressed - name = "implanter (C)" - icon_state = "cimplanter1" - -/obj/item/weapon/implanter/compressed/New() - imp = new /obj/item/weapon/implant/compressed( src ) - ..() - update() - return - -/obj/item/weapon/implanter/compressed/update() - if (imp) - var/obj/item/weapon/implant/compressed/c = imp - if(!c.scanned) - icon_state = "cimplanter1" - else - icon_state = "cimplanter2" - else - icon_state = "cimplanter0" - return - -/obj/item/weapon/implanter/compressed/attack(mob/M as mob, mob/user as mob) - var/obj/item/weapon/implant/compressed/c = imp - if (!c) return - if (c.scanned == null) - to_chat(user, "Please scan an object with the implanter first.") - return - ..() - -/obj/item/weapon/implanter/compressed/afterattack(atom/A, mob/user as mob, proximity) - if(!proximity) - return - if(!active) - to_chat(user, "Activate \the [src.name] first.") - return - if(istype(A,/obj/item) && imp) - var/obj/item/weapon/implant/compressed/c = imp - if (c.scanned) - to_chat(user, "Something is already scanned inside the implant!") - return - c.scanned = A - if(istype(A, /obj/item/weapon/storage)) - to_chat(user, "You can't store \the [A.name] in this!") - c.scanned = null - return - if(istype(A.loc,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = A.loc - H.remove_from_mob(A) - else if(istype(A.loc,/obj/item/weapon/storage)) - var/obj/item/weapon/storage/S = A.loc - S.remove_from_storage(A) - A.loc.contents.Remove(A) - update() - -/obj/item/weapon/implanter/restrainingbolt - name = "implanter (bolt)" - -/obj/item/weapon/implanter/restrainingbolt/New() - src.imp = new /obj/item/weapon/implant/restrainingbolt( src ) - ..() - update() - return +/obj/item/weapon/implanter + name = "implanter" + icon = 'icons/obj/items.dmi' + icon_state = "implanter0_1" + item_state = "syringe_0" + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_SMALL + matter = list(MAT_STEEL = 1000, MAT_GLASS = 1000) + var/obj/item/weapon/implant/imp = null + var/active = 1 + +/obj/item/weapon/implanter/attack_self(var/mob/user) + active = !active + to_chat(user, "You [active ? "" : "de"]activate \the [src].") + update() + +/obj/item/weapon/implanter/verb/remove_implant() + set category = "Object" + set name = "Remove Implant" + set src in usr + + if(!imp) + return + if(istype(usr, /mob)) + var/mob/M = usr + imp.loc = get_turf(src) + if(M.get_active_hand() == null) + M.put_in_hands(imp) + to_chat(M, "You remove \the [imp] from \the [src].") + name = "implanter" + imp = null + + update() + + return + +/obj/item/weapon/implanter/proc/update() + if (src.imp) + src.icon_state = "implanter1" + else + src.icon_state = "implanter0" + src.icon_state += "_[active]" + return + +/obj/item/weapon/implanter/attack(mob/M as mob, mob/user as mob) + if (!istype(M, /mob/living/carbon)) + return + if(active) + if (imp) + M.visible_message("[user] is attempting to implant [M].") + + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) + user.do_attack_animation(M) + + var/turf/T1 = get_turf(M) + if (T1 && ((M == user) || do_after(user, 50))) + if(user && M && (get_turf(M) == T1) && src && src.imp) + M.visible_message("[M] has been implanted by [user].") + + add_attack_logs(user,M,"Implanted with [imp.name] using [name]") + + if(imp.handle_implant(M)) + imp.post_implant(M) + + if(ishuman(M)) + var/mob/living/carbon/human/H = M + BITSET(H.hud_updateflag, IMPLOYAL_HUD) + BITSET(H.hud_updateflag, BACKUP_HUD) //VOREStation Add - Backup HUD updates + + src.imp = null + update() + else + to_chat(user, "You need to activate \the [src.name] first.") + return + +/obj/item/weapon/implanter/loyalty + name = "implanter-loyalty" + +/obj/item/weapon/implanter/loyalty/New() + src.imp = new /obj/item/weapon/implant/loyalty( src ) + ..() + update() + return + +/obj/item/weapon/implanter/explosive + name = "implanter (E)" + +/obj/item/weapon/implanter/explosive/New() + src.imp = new /obj/item/weapon/implant/explosive( src ) + ..() + update() + return + +/obj/item/weapon/implanter/adrenalin + name = "implanter-adrenalin" + +/obj/item/weapon/implanter/adrenalin/New() + src.imp = new /obj/item/weapon/implant/adrenalin(src) + ..() + update() + return + +/obj/item/weapon/implanter/compressed + name = "implanter (C)" + icon_state = "cimplanter1" + +/obj/item/weapon/implanter/compressed/New() + imp = new /obj/item/weapon/implant/compressed( src ) + ..() + update() + return + +/obj/item/weapon/implanter/compressed/update() + if (imp) + var/obj/item/weapon/implant/compressed/c = imp + if(!c.scanned) + icon_state = "cimplanter1" + else + icon_state = "cimplanter2" + else + icon_state = "cimplanter0" + return + +/obj/item/weapon/implanter/compressed/attack(mob/M as mob, mob/user as mob) + var/obj/item/weapon/implant/compressed/c = imp + if (!c) return + if (c.scanned == null) + to_chat(user, "Please scan an object with the implanter first.") + return + ..() + +/obj/item/weapon/implanter/compressed/afterattack(atom/A, mob/user as mob, proximity) + if(!proximity) + return + if(!active) + to_chat(user, "Activate \the [src.name] first.") + return + if(istype(A,/obj/item) && imp) + var/obj/item/weapon/implant/compressed/c = imp + if (c.scanned) + to_chat(user, "Something is already scanned inside the implant!") + return + c.scanned = A + if(istype(A, /obj/item/weapon/storage)) + to_chat(user, "You can't store \the [A.name] in this!") + c.scanned = null + return + if(istype(A.loc,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = A.loc + H.remove_from_mob(A) + else if(istype(A.loc,/obj/item/weapon/storage)) + var/obj/item/weapon/storage/S = A.loc + S.remove_from_storage(A) + A.loc.contents.Remove(A) + update() + +/obj/item/weapon/implanter/restrainingbolt + name = "implanter (bolt)" + +/obj/item/weapon/implanter/restrainingbolt/New() + src.imp = new /obj/item/weapon/implant/restrainingbolt( src ) + ..() + update() + return diff --git a/code/game/objects/items/weapons/implants/implantfreedom.dm b/code/game/objects/items/weapons/implants/implantfreedom.dm index 6e4f5f7a167..aa63215a61c 100644 --- a/code/game/objects/items/weapons/implants/implantfreedom.dm +++ b/code/game/objects/items/weapons/implants/implantfreedom.dm @@ -1,70 +1,70 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/item/weapon/implant/freedom - name = "freedom implant" - desc = "Use this to escape from those evil Red Shirts." - implant_color = "r" - var/activation_emote = "chuckle" - var/uses = 1.0 - - -/obj/item/weapon/implant/freedom/New() - src.activation_emote = pick("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") - src.uses = rand(1, 5) - ..() - return - - -/obj/item/weapon/implant/freedom/trigger(emote, mob/living/carbon/source as mob) - if (src.uses < 1) - return 0 - - if (emote == src.activation_emote) - src.uses-- - to_chat(source, "You feel a faint click.") - if (source.handcuffed) - var/obj/item/weapon/W = source.handcuffed - source.handcuffed = null - if(source.buckled && source.buckled.buckle_require_restraints) - source.buckled.unbuckle_mob() - source.update_handcuffed() - if (source.client) - source.client.screen -= W - if (W) - W.loc = source.loc - dropped(source) - if (W) - W.layer = initial(W.layer) - if (source.legcuffed) - var/obj/item/weapon/W = source.legcuffed - source.legcuffed = null - source.update_inv_legcuffed() - if (source.client) - source.client.screen -= W - if (W) - W.loc = source.loc - dropped(source) - if (W) - W.layer = initial(W.layer) - return - -/obj/item/weapon/implant/freedom/post_implant(mob/source) - source.mind.store_memory("Freedom implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.", 0, 0) - to_chat(source, "The implanted freedom implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.") - -/obj/item/weapon/implant/freedom/get_data() - var/dat = {" -Implant Specifications:
                    -Name: Freedom Beacon
                    -Life: optimum 5 uses
                    -Important Notes: Illegal
                    -
                    -Implant Details:
                    -Function: Transmits a specialized cluster of signals to override handcuff locking -mechanisms
                    -Special Features:
                    -Neuro-Scan- Analyzes certain shadow signals in the nervous system
                    -Integrity: The battery is extremely weak and commonly after injection its -life can drive down to only 1 use.
                    -No Implant Specifics"} - return dat +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/obj/item/weapon/implant/freedom + name = "freedom implant" + desc = "Use this to escape from those evil Red Shirts." + implant_color = "r" + var/activation_emote = "chuckle" + var/uses = 1.0 + + +/obj/item/weapon/implant/freedom/New() + src.activation_emote = pick("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") + src.uses = rand(1, 5) + ..() + return + + +/obj/item/weapon/implant/freedom/trigger(emote, mob/living/carbon/source as mob) + if (src.uses < 1) + return 0 + + if (emote == src.activation_emote) + src.uses-- + to_chat(source, "You feel a faint click.") + if (source.handcuffed) + var/obj/item/weapon/W = source.handcuffed + source.handcuffed = null + if(source.buckled && source.buckled.buckle_require_restraints) + source.buckled.unbuckle_mob() + source.update_handcuffed() + if (source.client) + source.client.screen -= W + if (W) + W.loc = source.loc + dropped(source) + if (W) + W.layer = initial(W.layer) + if (source.legcuffed) + var/obj/item/weapon/W = source.legcuffed + source.legcuffed = null + source.update_inv_legcuffed() + if (source.client) + source.client.screen -= W + if (W) + W.loc = source.loc + dropped(source) + if (W) + W.layer = initial(W.layer) + return + +/obj/item/weapon/implant/freedom/post_implant(mob/source) + source.mind.store_memory("Freedom implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.", 0, 0) + to_chat(source, "The implanted freedom implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.") + +/obj/item/weapon/implant/freedom/get_data() + var/dat = {" +Implant Specifications:
                    +Name: Freedom Beacon
                    +Life: optimum 5 uses
                    +Important Notes: Illegal
                    +
                    +Implant Details:
                    +Function: Transmits a specialized cluster of signals to override handcuff locking +mechanisms
                    +Special Features:
                    +Neuro-Scan- Analyzes certain shadow signals in the nervous system
                    +Integrity: The battery is extremely weak and commonly after injection its +life can drive down to only 1 use.
                    +No Implant Specifics"} + return dat diff --git a/code/game/objects/items/weapons/implants/implantpad.dm b/code/game/objects/items/weapons/implants/implantpad.dm index fde73d6997d..f895a2438fd 100644 --- a/code/game/objects/items/weapons/implants/implantpad.dm +++ b/code/game/objects/items/weapons/implants/implantpad.dm @@ -1,94 +1,94 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/item/weapon/implantpad - name = "implantpad" - desc = "Used to modify implants." - icon = 'icons/obj/items.dmi' - icon_state = "implantpad-0" - item_state = "electronic" - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_SMALL - var/obj/item/weapon/implantcase/case = null - var/broadcasting = null - var/listening = 1.0 -/obj/item/weapon/implantpad/proc/update() - if (src.case) - src.icon_state = "implantpad-1" - else - src.icon_state = "implantpad-0" - return - - -/obj/item/weapon/implantpad/attack_hand(mob/living/user as mob) - if ((src.case && user.item_is_in_hands(src))) - user.put_in_active_hand(case) - - src.case.add_fingerprint(user) - src.case = null - - src.add_fingerprint(user) - update() - else - return ..() - return - - -/obj/item/weapon/implantpad/attackby(obj/item/weapon/implantcase/C as obj, mob/user as mob) - ..() - if(istype(C, /obj/item/weapon/implantcase)) - if(!( src.case )) - user.drop_item() - C.loc = src - src.case = C - else - return - src.update() - return - - -/obj/item/weapon/implantpad/attack_self(mob/user as mob) - user.set_machine(src) - var/dat = "Implant Mini-Computer:
                    " - if (src.case) - if(src.case.imp) - if(istype(src.case.imp, /obj/item/weapon/implant)) - dat += src.case.imp.get_data() - if(istype(src.case.imp, /obj/item/weapon/implant/tracking)) - dat += {"ID (1-100): - - - - [case.imp:id] - + - +
                    "} - else - dat += "The implant casing is empty." - else - dat += "Please insert an implant casing!" - user << browse(dat, "window=implantpad") - onclose(user, "implantpad") - return - - -/obj/item/weapon/implantpad/Topic(href, href_list) - ..() - if (usr.stat) - return - if ((usr.contents.Find(src)) || ((in_range(src, usr) && istype(src.loc, /turf)))) - usr.set_machine(src) - if (href_list["tracking_id"]) - var/obj/item/weapon/implant/tracking/T = src.case.imp - T.id += text2num(href_list["tracking_id"]) - T.id = min(1000, T.id) - T.id = max(1, T.id) - - if (istype(src.loc, /mob)) - attack_self(src.loc) - else - for(var/mob/M in viewers(1, src)) - if (M.client) - src.attack_self(M) - src.add_fingerprint(usr) - else - usr << browse(null, "window=implantpad") - return - return +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/obj/item/weapon/implantpad + name = "implantpad" + desc = "Used to modify implants." + icon = 'icons/obj/items.dmi' + icon_state = "implantpad-0" + item_state = "electronic" + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_SMALL + var/obj/item/weapon/implantcase/case = null + var/broadcasting = null + var/listening = 1.0 +/obj/item/weapon/implantpad/proc/update() + if (src.case) + src.icon_state = "implantpad-1" + else + src.icon_state = "implantpad-0" + return + + +/obj/item/weapon/implantpad/attack_hand(mob/living/user as mob) + if ((src.case && user.item_is_in_hands(src))) + user.put_in_active_hand(case) + + src.case.add_fingerprint(user) + src.case = null + + src.add_fingerprint(user) + update() + else + return ..() + return + + +/obj/item/weapon/implantpad/attackby(obj/item/weapon/implantcase/C as obj, mob/user as mob) + ..() + if(istype(C, /obj/item/weapon/implantcase)) + if(!( src.case )) + user.drop_item() + C.loc = src + src.case = C + else + return + src.update() + return + + +/obj/item/weapon/implantpad/attack_self(mob/user as mob) + user.set_machine(src) + var/dat = "Implant Mini-Computer:
                    " + if (src.case) + if(src.case.imp) + if(istype(src.case.imp, /obj/item/weapon/implant)) + dat += src.case.imp.get_data() + if(istype(src.case.imp, /obj/item/weapon/implant/tracking)) + dat += {"ID (1-100): + - + - [case.imp:id] + + + +
                    "} + else + dat += "The implant casing is empty." + else + dat += "Please insert an implant casing!" + user << browse(dat, "window=implantpad") + onclose(user, "implantpad") + return + + +/obj/item/weapon/implantpad/Topic(href, href_list) + ..() + if (usr.stat) + return + if ((usr.contents.Find(src)) || ((in_range(src, usr) && istype(src.loc, /turf)))) + usr.set_machine(src) + if (href_list["tracking_id"]) + var/obj/item/weapon/implant/tracking/T = src.case.imp + T.id += text2num(href_list["tracking_id"]) + T.id = min(1000, T.id) + T.id = max(1, T.id) + + if (istype(src.loc, /mob)) + attack_self(src.loc) + else + for(var/mob/M in viewers(1, src)) + if (M.client) + src.attack_self(M) + src.add_fingerprint(usr) + else + usr << browse(null, "window=implantpad") + return + return diff --git a/code/game/objects/items/weapons/implants/implantuplink.dm b/code/game/objects/items/weapons/implants/implantuplink.dm index 51f9c0d696d..6ee094aeded 100644 --- a/code/game/objects/items/weapons/implants/implantuplink.dm +++ b/code/game/objects/items/weapons/implants/implantuplink.dm @@ -1,25 +1,25 @@ -/obj/item/weapon/implant/uplink - name = "uplink" - desc = "Summon things." - var/activation_emote = "chuckle" - -/obj/item/weapon/implant/uplink/New() - activation_emote = pick("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") - hidden_uplink = new(src) - //hidden_uplink.uses = 5 - //Code currently uses a mind var for telecrystals, balancing is currently an issue. Will investigate. - ..() - return - -/obj/item/weapon/implant/uplink/post_implant(mob/source) - var/choices = list("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") - activation_emote = tgui_input_list(usr, "Choose activation emote. If you cancel this, one will be picked at random.", "Implant Activation", choices) - if(!activation_emote) - activation_emote = pick(choices) - source.mind.store_memory("Uplink implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.", 0, 0) - to_chat(source, "The implanted uplink implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.") - -/obj/item/weapon/implant/uplink/trigger(emote, mob/source as mob) - if(hidden_uplink && usr == source) // Let's not have another people activate our uplink - hidden_uplink.check_trigger(source, emote, activation_emote) +/obj/item/weapon/implant/uplink + name = "uplink" + desc = "Summon things." + var/activation_emote = "chuckle" + +/obj/item/weapon/implant/uplink/New() + activation_emote = pick("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") + hidden_uplink = new(src) + //hidden_uplink.uses = 5 + //Code currently uses a mind var for telecrystals, balancing is currently an issue. Will investigate. + ..() + return + +/obj/item/weapon/implant/uplink/post_implant(mob/source) + var/choices = list("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") + activation_emote = tgui_input_list(usr, "Choose activation emote. If you cancel this, one will be picked at random.", "Implant Activation", choices) + if(!activation_emote) + activation_emote = pick(choices) + source.mind.store_memory("Uplink implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.", 0, 0) + to_chat(source, "The implanted uplink implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.") + +/obj/item/weapon/implant/uplink/trigger(emote, mob/source as mob) + if(hidden_uplink && usr == source) // Let's not have another people activate our uplink + hidden_uplink.check_trigger(source, emote, activation_emote) return \ No newline at end of file diff --git a/code/game/objects/items/weapons/improvised_components.dm b/code/game/objects/items/weapons/improvised_components.dm index c4272a5e10d..5add8778600 100644 --- a/code/game/objects/items/weapons/improvised_components.dm +++ b/code/game/objects/items/weapons/improvised_components.dm @@ -1,40 +1,40 @@ -/obj/item/weapon/material/butterflyconstruction - name = "unfinished concealed knife" - desc = "An unfinished concealed knife, it looks like the screws need to be tightened." - icon = 'icons/obj/buildingobject.dmi' - icon_state = "butterflystep1" - force_divisor = 0.1 - thrown_force_divisor = 0.1 - -/obj/item/weapon/material/butterflyconstruction/attackby(obj/item/W as obj, mob/user as mob) - if(W.has_tool_quality(TOOL_SCREWDRIVER)) - to_chat(user, "You finish the concealed blade weapon.") - playsound(src, W.usesound, 50, 1) - new /obj/item/weapon/material/butterfly(user.loc, material.name) - qdel(src) - return - -/obj/item/weapon/material/butterflyblade - name = "knife blade" - desc = "A knife blade. Unusable as a weapon without a grip." - icon = 'icons/obj/buildingobject.dmi' - icon_state = "butterfly2" - force_divisor = 0.1 - thrown_force_divisor = 0.1 - -/obj/item/weapon/material/butterflyhandle - name = "concealed knife grip" - desc = "A plasteel grip with screw fittings for a blade." - icon = 'icons/obj/buildingobject.dmi' - icon_state = "butterfly1" - force_divisor = 0.1 - thrown_force_divisor = 0.1 - -/obj/item/weapon/material/butterflyhandle/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W,/obj/item/weapon/material/butterflyblade)) - var/obj/item/weapon/material/butterflyblade/B = W - to_chat(user, "You attach the two concealed blade parts.") - new /obj/item/weapon/material/butterflyconstruction(user.loc, B.material.name) - qdel(W) - qdel(src) - return +/obj/item/weapon/material/butterflyconstruction + name = "unfinished concealed knife" + desc = "An unfinished concealed knife, it looks like the screws need to be tightened." + icon = 'icons/obj/buildingobject.dmi' + icon_state = "butterflystep1" + force_divisor = 0.1 + thrown_force_divisor = 0.1 + +/obj/item/weapon/material/butterflyconstruction/attackby(obj/item/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + to_chat(user, "You finish the concealed blade weapon.") + playsound(src, W.usesound, 50, 1) + new /obj/item/weapon/material/butterfly(user.loc, material.name) + qdel(src) + return + +/obj/item/weapon/material/butterflyblade + name = "knife blade" + desc = "A knife blade. Unusable as a weapon without a grip." + icon = 'icons/obj/buildingobject.dmi' + icon_state = "butterfly2" + force_divisor = 0.1 + thrown_force_divisor = 0.1 + +/obj/item/weapon/material/butterflyhandle + name = "concealed knife grip" + desc = "A plasteel grip with screw fittings for a blade." + icon = 'icons/obj/buildingobject.dmi' + icon_state = "butterfly1" + force_divisor = 0.1 + thrown_force_divisor = 0.1 + +/obj/item/weapon/material/butterflyhandle/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W,/obj/item/weapon/material/butterflyblade)) + var/obj/item/weapon/material/butterflyblade/B = W + to_chat(user, "You attach the two concealed blade parts.") + new /obj/item/weapon/material/butterflyconstruction(user.loc, B.material.name) + qdel(W) + qdel(src) + return diff --git a/code/game/objects/items/weapons/material/bats.dm b/code/game/objects/items/weapons/material/bats.dm index 73896640a28..c625e948bbb 100644 --- a/code/game/objects/items/weapons/material/bats.dm +++ b/code/game/objects/items/weapons/material/bats.dm @@ -1,29 +1,29 @@ -/obj/item/weapon/material/twohanded/baseballbat - name = "bat" - desc = "HOME RUN!" - icon_state = "metalbat0" - base_icon = "metalbat" - throwforce = 7 - attack_verb = list("smashed", "beaten", "slammed", "smacked", "struck", "battered", "bonked") - hitsound = 'sound/weapons/genhit3.ogg' - default_material = "wood" - force_divisor = 1.1 // 22 when wielded with weight 20 (steel) - unwielded_force_divisor = 0.7 // 15 when unwielded based on above. - dulled_divisor = 0.75 // A "dull" bat is still gonna hurt - slot_flags = SLOT_BACK - -//Predefined materials go here. -/obj/item/weapon/material/twohanded/baseballbat/metal/New(var/newloc) - ..(newloc,"steel") - -/obj/item/weapon/material/twohanded/baseballbat/uranium/New(var/newloc) - ..(newloc,"uranium") - -/obj/item/weapon/material/twohanded/baseballbat/gold/New(var/newloc) - ..(newloc,"gold") - -/obj/item/weapon/material/twohanded/baseballbat/platinum/New(var/newloc) - ..(newloc,"platinum") - -/obj/item/weapon/material/twohanded/baseballbat/diamond/New(var/newloc) +/obj/item/weapon/material/twohanded/baseballbat + name = "bat" + desc = "HOME RUN!" + icon_state = "metalbat0" + base_icon = "metalbat" + throwforce = 7 + attack_verb = list("smashed", "beaten", "slammed", "smacked", "struck", "battered", "bonked") + hitsound = 'sound/weapons/genhit3.ogg' + default_material = "wood" + force_divisor = 1.1 // 22 when wielded with weight 20 (steel) + unwielded_force_divisor = 0.7 // 15 when unwielded based on above. + dulled_divisor = 0.75 // A "dull" bat is still gonna hurt + slot_flags = SLOT_BACK + +//Predefined materials go here. +/obj/item/weapon/material/twohanded/baseballbat/metal/New(var/newloc) + ..(newloc,"steel") + +/obj/item/weapon/material/twohanded/baseballbat/uranium/New(var/newloc) + ..(newloc,"uranium") + +/obj/item/weapon/material/twohanded/baseballbat/gold/New(var/newloc) + ..(newloc,"gold") + +/obj/item/weapon/material/twohanded/baseballbat/platinum/New(var/newloc) + ..(newloc,"platinum") + +/obj/item/weapon/material/twohanded/baseballbat/diamond/New(var/newloc) ..(newloc,"diamond") \ No newline at end of file diff --git a/code/game/objects/items/weapons/material/kitchen.dm b/code/game/objects/items/weapons/material/kitchen.dm index a4f3f7796f8..55b67d6b987 100644 --- a/code/game/objects/items/weapons/material/kitchen.dm +++ b/code/game/objects/items/weapons/material/kitchen.dm @@ -1,217 +1,217 @@ -/obj/item/weapon/material/kitchen - icon = 'icons/obj/kitchen.dmi' - -/* - * Utensils - */ -/obj/item/weapon/material/kitchen/utensil - drop_sound = 'sound/items/drop/knife.ogg' - pickup_sound = 'sound/items/pickup/knife.ogg' - w_class = ITEMSIZE_TINY - thrown_force_divisor = 1 - origin_tech = list(TECH_MATERIAL = 1) - attack_verb = list("attacked", "stabbed", "poked") - sharp = TRUE - edge = TRUE - force_divisor = 0.1 // 6 when wielded with hardness 60 (steel) - thrown_force_divisor = 0.25 // 5 when thrown with weight 20 (steel) - var/scoop_volume = 5 - var/loaded // Name for currently loaded food object. - var/loaded_color // Color for currently loaded food object. - - var/list/food_inserted_micros - -/obj/item/weapon/material/kitchen/utensil/Initialize() - . = ..() - if (prob(60)) - src.pixel_y = rand(0, 4) - create_reagents(scoop_volume) - -/obj/item/weapon/material/kitchen/utensil/Destroy() - if(food_inserted_micros) - for(var/mob/M in food_inserted_micros) - M.dropInto(loc) - food_inserted_micros -= M - . = ..() - - return - -/obj/item/weapon/material/kitchen/utensil/update_icon() - . = ..() - cut_overlays() - if(loaded) - var/image/I = new(icon, "loadedfood") - I.color = loaded_color - add_overlay(I) - -/obj/item/weapon/material/kitchen/utensil/proc/load_food(var/mob/user, var/obj/item/weapon/reagent_containers/food/snacks/loading) - if (reagents.total_volume > 0) - to_chat(user, SPAN_DANGER("There is already something on \the [src].")) - return - if (!loading?.reagents?.total_volume) - to_chat(user, SPAN_NOTICE("Nothing to scoop up in \the [loading]!")) - - - loaded = "\the [loading]" - user.visible_message( \ - "\The [user] scoops up some of [loaded] with \the [src]!", - SPAN_NOTICE("You scoop up some of [loaded] with \the [src]!") - ) - loading.bitecount++ - loading.reagents.trans_to_obj(src, min(loading.reagents.total_volume, scoop_volume)) - loaded_color = loading.filling_color - - if(loading.food_inserted_micros && loading.food_inserted_micros.len) - if(!food_inserted_micros) - food_inserted_micros = list() - - for(var/mob/living/F in loading.food_inserted_micros) - var/do_transfer = FALSE - - if(!loading.reagents.total_volume) - do_transfer = TRUE - else - var/transfer_chance = (loading.bitecount/(loading.bitecount + (loading.bitesize / loading.reagents.total_volume) + 1))*100 - if(prob(transfer_chance)) - do_transfer = TRUE - - if(do_transfer) - F.forceMove(src) - loading.food_inserted_micros -= F - src.food_inserted_micros += F - - if (loading.reagents.total_volume <= 0) - qdel(loading) - update_icon() - -/obj/item/weapon/material/kitchen/utensil/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - if(!istype(M)) - return ..() - - if(user.a_intent != I_HELP) - if(user.zone_sel.selecting == BP_HEAD || user.zone_sel.selecting == O_EYES) - if((CLUMSY in user.mutations) && prob(50)) - M = user - return eyestab(M,user) - else - return ..() - - if (loaded && reagents.total_volume > 0) - reagents.trans_to_mob(M, reagents.total_volume, CHEM_INGEST) - if(food_inserted_micros && food_inserted_micros.len) - for(var/mob/living/F in food_inserted_micros) - food_inserted_micros -= F - if(!F.can_be_drop_prey || !F.food_vore) - F.forceMove(get_turf(src)) - else - F.forceMove(M.vore_selected) - if(M == user) - if(!M.can_eat(loaded)) - return - M.visible_message("\The [user] eats some of [loaded] with \the [src].") - else - user.visible_message(SPAN_WARNING("\The [user] begins to feed \the [M]!")) - if(!(M.can_force_feed(user, loaded) && do_mob(user, M, 5 SECONDS))) - return - M.visible_message("\The [user] feeds some of [loaded] to \the [M] with \the [src].") - playsound(src,'sound/items/eatfood.ogg', rand(10,40), 1) - loaded = null - update_icon() - return - else - to_chat(user, SPAN_WARNING("You don't have anything on \the [src].")) //if we have help intent and no food scooped up DON'T STAB OURSELVES WITH THE FORK - return - -/obj/item/weapon/material/kitchen/utensil/on_rag_wipe() - . = ..() - if(reagents.total_volume > 0) - reagents.clear_reagents() - cut_overlays() - return - -/obj/item/weapon/material/kitchen/utensil/container_resist(mob/living/M) - if(food_inserted_micros) - food_inserted_micros -= M - M.forceMove(get_turf(src)) - to_chat(M, "You climb off of \the [src].") - -/obj/item/weapon/material/kitchen/utensil/fork - name = "fork" - desc = "It's a fork. Sure is pointy." - icon_state = "fork" - sharp = TRUE - edge = FALSE - -/obj/item/weapon/material/kitchen/utensil/fork/plastic - default_material = "plastic" - -/obj/item/weapon/material/kitchen/utensil/foon - name = "foon" - desc = "It's a foon. The forgotten cousin of the spork." - icon_state = "foon" - sharp = TRUE - edge = FALSE - -/obj/item/weapon/material/kitchen/utensil/foon/plastic - default_material = "plastic" - -/obj/item/weapon/material/kitchen/utensil/spork - name = "spork" - desc = "It's a spork. The (un)holy merger of a spoon and fork." - icon_state = "spork" - sharp = TRUE - edge = FALSE - -/obj/item/weapon/material/kitchen/utensil/spork/plastic - default_material = "plastic" - -/obj/item/weapon/material/kitchen/utensil/spoon - name = "spoon" - desc = "It's a spoon. You can see your own upside-down face in it." - icon_state = "spoon" - attack_verb = list("attacked", "poked") - edge = FALSE - sharp = FALSE - force_divisor = 0.1 //2 when wielded with weight 20 (steel) - -/obj/item/weapon/material/kitchen/utensil/spoon/plastic - default_material = "plastic" - -/* - * Knives - */ - -/* From the time of Clowns. Commented out for posterity, and sanity. -/obj/item/weapon/material/knife/attack(target as mob, mob/living/user as mob) - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "You accidentally cut yourself with \the [src].") - user.take_organ_damage(20) - return - return ..() -*/ -/obj/item/weapon/material/knife/plastic - default_material = "plastic" - -/* - * Rolling Pins - */ - -/obj/item/weapon/material/kitchen/rollingpin - name = "rolling pin" - desc = "Used to knock out the Bartender." - icon_state = "rolling_pin" - attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "whacked") - default_material = "wood" - force_divisor = 0.7 // 10 when wielded with weight 15 (wood) - dulled_divisor = 0.75 // Still a club - thrown_force_divisor = 1 // as above - drop_sound = 'sound/items/drop/wooden.ogg' - pickup_sound = 'sound/items/pickup/wooden.ogg' - -/obj/item/weapon/material/kitchen/rollingpin/attack(mob/living/M as mob, mob/living/user as mob) - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "\The [src] slips out of your hand and hits your head.") - user.take_organ_damage(10) - user.Paralyse(2) - return - return ..() +/obj/item/weapon/material/kitchen + icon = 'icons/obj/kitchen.dmi' + +/* + * Utensils + */ +/obj/item/weapon/material/kitchen/utensil + drop_sound = 'sound/items/drop/knife.ogg' + pickup_sound = 'sound/items/pickup/knife.ogg' + w_class = ITEMSIZE_TINY + thrown_force_divisor = 1 + origin_tech = list(TECH_MATERIAL = 1) + attack_verb = list("attacked", "stabbed", "poked") + sharp = TRUE + edge = TRUE + force_divisor = 0.1 // 6 when wielded with hardness 60 (steel) + thrown_force_divisor = 0.25 // 5 when thrown with weight 20 (steel) + var/scoop_volume = 5 + var/loaded // Name for currently loaded food object. + var/loaded_color // Color for currently loaded food object. + + var/list/food_inserted_micros + +/obj/item/weapon/material/kitchen/utensil/Initialize() + . = ..() + if (prob(60)) + src.pixel_y = rand(0, 4) + create_reagents(scoop_volume) + +/obj/item/weapon/material/kitchen/utensil/Destroy() + if(food_inserted_micros) + for(var/mob/M in food_inserted_micros) + M.dropInto(loc) + food_inserted_micros -= M + . = ..() + + return + +/obj/item/weapon/material/kitchen/utensil/update_icon() + . = ..() + cut_overlays() + if(loaded) + var/image/I = new(icon, "loadedfood") + I.color = loaded_color + add_overlay(I) + +/obj/item/weapon/material/kitchen/utensil/proc/load_food(var/mob/user, var/obj/item/weapon/reagent_containers/food/snacks/loading) + if (reagents.total_volume > 0) + to_chat(user, SPAN_DANGER("There is already something on \the [src].")) + return + if (!loading?.reagents?.total_volume) + to_chat(user, SPAN_NOTICE("Nothing to scoop up in \the [loading]!")) + + + loaded = "\the [loading]" + user.visible_message( \ + "\The [user] scoops up some of [loaded] with \the [src]!", + SPAN_NOTICE("You scoop up some of [loaded] with \the [src]!") + ) + loading.bitecount++ + loading.reagents.trans_to_obj(src, min(loading.reagents.total_volume, scoop_volume)) + loaded_color = loading.filling_color + + if(loading.food_inserted_micros && loading.food_inserted_micros.len) + if(!food_inserted_micros) + food_inserted_micros = list() + + for(var/mob/living/F in loading.food_inserted_micros) + var/do_transfer = FALSE + + if(!loading.reagents.total_volume) + do_transfer = TRUE + else + var/transfer_chance = (loading.bitecount/(loading.bitecount + (loading.bitesize / loading.reagents.total_volume) + 1))*100 + if(prob(transfer_chance)) + do_transfer = TRUE + + if(do_transfer) + F.forceMove(src) + loading.food_inserted_micros -= F + src.food_inserted_micros += F + + if (loading.reagents.total_volume <= 0) + qdel(loading) + update_icon() + +/obj/item/weapon/material/kitchen/utensil/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + if(!istype(M)) + return ..() + + if(user.a_intent != I_HELP) + if(user.zone_sel.selecting == BP_HEAD || user.zone_sel.selecting == O_EYES) + if((CLUMSY in user.mutations) && prob(50)) + M = user + return eyestab(M,user) + else + return ..() + + if (loaded && reagents.total_volume > 0) + reagents.trans_to_mob(M, reagents.total_volume, CHEM_INGEST) + if(food_inserted_micros && food_inserted_micros.len) + for(var/mob/living/F in food_inserted_micros) + food_inserted_micros -= F + if(!F.can_be_drop_prey || !F.food_vore) + F.forceMove(get_turf(src)) + else + F.forceMove(M.vore_selected) + if(M == user) + if(!M.can_eat(loaded)) + return + M.visible_message("\The [user] eats some of [loaded] with \the [src].") + else + user.visible_message(SPAN_WARNING("\The [user] begins to feed \the [M]!")) + if(!(M.can_force_feed(user, loaded) && do_mob(user, M, 5 SECONDS))) + return + M.visible_message("\The [user] feeds some of [loaded] to \the [M] with \the [src].") + playsound(src,'sound/items/eatfood.ogg', rand(10,40), 1) + loaded = null + update_icon() + return + else + to_chat(user, SPAN_WARNING("You don't have anything on \the [src].")) //if we have help intent and no food scooped up DON'T STAB OURSELVES WITH THE FORK + return + +/obj/item/weapon/material/kitchen/utensil/on_rag_wipe() + . = ..() + if(reagents.total_volume > 0) + reagents.clear_reagents() + cut_overlays() + return + +/obj/item/weapon/material/kitchen/utensil/container_resist(mob/living/M) + if(food_inserted_micros) + food_inserted_micros -= M + M.forceMove(get_turf(src)) + to_chat(M, "You climb off of \the [src].") + +/obj/item/weapon/material/kitchen/utensil/fork + name = "fork" + desc = "It's a fork. Sure is pointy." + icon_state = "fork" + sharp = TRUE + edge = FALSE + +/obj/item/weapon/material/kitchen/utensil/fork/plastic + default_material = "plastic" + +/obj/item/weapon/material/kitchen/utensil/foon + name = "foon" + desc = "It's a foon. The forgotten cousin of the spork." + icon_state = "foon" + sharp = TRUE + edge = FALSE + +/obj/item/weapon/material/kitchen/utensil/foon/plastic + default_material = "plastic" + +/obj/item/weapon/material/kitchen/utensil/spork + name = "spork" + desc = "It's a spork. The (un)holy merger of a spoon and fork." + icon_state = "spork" + sharp = TRUE + edge = FALSE + +/obj/item/weapon/material/kitchen/utensil/spork/plastic + default_material = "plastic" + +/obj/item/weapon/material/kitchen/utensil/spoon + name = "spoon" + desc = "It's a spoon. You can see your own upside-down face in it." + icon_state = "spoon" + attack_verb = list("attacked", "poked") + edge = FALSE + sharp = FALSE + force_divisor = 0.1 //2 when wielded with weight 20 (steel) + +/obj/item/weapon/material/kitchen/utensil/spoon/plastic + default_material = "plastic" + +/* + * Knives + */ + +/* From the time of Clowns. Commented out for posterity, and sanity. +/obj/item/weapon/material/knife/attack(target as mob, mob/living/user as mob) + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "You accidentally cut yourself with \the [src].") + user.take_organ_damage(20) + return + return ..() +*/ +/obj/item/weapon/material/knife/plastic + default_material = "plastic" + +/* + * Rolling Pins + */ + +/obj/item/weapon/material/kitchen/rollingpin + name = "rolling pin" + desc = "Used to knock out the Bartender." + icon_state = "rolling_pin" + attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "whacked") + default_material = "wood" + force_divisor = 0.7 // 10 when wielded with weight 15 (wood) + dulled_divisor = 0.75 // Still a club + thrown_force_divisor = 1 // as above + drop_sound = 'sound/items/drop/wooden.ogg' + pickup_sound = 'sound/items/pickup/wooden.ogg' + +/obj/item/weapon/material/kitchen/rollingpin/attack(mob/living/M as mob, mob/living/user as mob) + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "\The [src] slips out of your hand and hits your head.") + user.take_organ_damage(10) + user.Paralyse(2) + return + return ..() diff --git a/code/game/objects/items/weapons/material/knives.dm b/code/game/objects/items/weapons/material/knives.dm index a2a8075acf4..193a9d7cae4 100644 --- a/code/game/objects/items/weapons/material/knives.dm +++ b/code/game/objects/items/weapons/material/knives.dm @@ -1,187 +1,187 @@ -/obj/item/weapon/material/butterfly - name = "butterfly knife" - desc = "A basic metal blade concealed in a lightweight plasteel grip. Small enough when folded to fit in a pocket." - description_fluff = "This could be used to engrave messages on suitable surfaces if you really put your mind to it! Alt-click a floor or wall to engrave with it." //This way it's not a completely hidden, arcane art to engrave. - icon_state = "butterflyknife" - item_state = null - hitsound = null - var/active = 0 - w_class = ITEMSIZE_SMALL - attack_verb = list("patted", "tapped") - force_divisor = 0.25 // 15 when wielded with hardness 60 (steel) - thrown_force_divisor = 0.25 // 5 when thrown with weight 20 (steel) - drop_sound = 'sound/items/drop/knife.ogg' - pickup_sound = 'sound/items/pickup/knife.ogg' - -/obj/item/weapon/material/butterfly/update_force() - if(active) - edge = TRUE - sharp = TRUE - ..() //Updates force. - throwforce = max(3,force-3) - hitsound = 'sound/weapons/bladeslice.ogg' - icon_state += "_open" - w_class = ITEMSIZE_NORMAL - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - else - force = 3 - edge = FALSE - sharp = FALSE - hitsound = initial(hitsound) - icon_state = initial(icon_state) - w_class = initial(w_class) - attack_verb = initial(attack_verb) - -/obj/item/weapon/material/butterfly/switchblade - name = "switchblade" - desc = "A classic switchblade with gold engraving. Just holding it makes you feel like a gangster." - icon_state = "switchblade" - -/obj/item/weapon/material/butterfly/boxcutter - name = "box cutter" - desc = "A thin, inexpensive razor-blade knife designed to open cardboard boxes." - icon_state = "boxcutter" - force_divisor = 0.1 // 6 when wielded with hardness 60 (steel) - thrown_force_divisor = 0.2 // 4 when thrown with weight 20 (steel) - -/obj/item/weapon/material/butterfly/attack_self(mob/user) - active = !active - update_force() - - if(user) - if(active) - to_chat(user, "You flip out \the [src].") - playsound(src, 'sound/weapons/flipblade.ogg', 15, 1) - else - to_chat(user, "\The [src] can now be concealed.") - add_fingerprint(user) - -/* - * Kitchen knives - */ -/obj/item/weapon/material/knife - name = "kitchen knife" - icon = 'icons/obj/kitchen.dmi' - icon_state = "knife" - desc = "A general purpose Chef's Knife made by SpaceCook Incorporated. Guaranteed to stay sharp for years to come." - description_fluff = "This could be used to engrave messages on suitable surfaces if you really put your mind to it! Alt-click a floor or wall to engrave with it." //This way it's not a completely hidden, arcane art to engrave. - sharp = TRUE - edge = TRUE - force_divisor = 0.15 // 9 when wielded with hardness 60 (steel) - matter = list(MAT_STEEL = 12000) - origin_tech = list(TECH_MATERIAL = 1) - attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - drop_sound = 'sound/items/drop/knife.ogg' - -// These no longer inherit from hatchets. -/obj/item/weapon/material/knife/tacknife - name = "tactical knife" - desc = "You'd be killing loads of people if this was Medal of Valor: Heroes of Space." - icon = 'icons/obj/weapons.dmi' - icon_state = "tacknife" - item_state = "knife" - force_divisor = 0.25 //15 when hardness 60 (steel) - attack_verb = list("stabbed", "chopped", "cut") - applies_material_colour = 1 - -/obj/item/weapon/material/knife/tacknife/combatknife - name = "combat knife" - desc = "If only you had a boot to put it in." - icon = 'icons/obj/weapons.dmi' - icon_state = "tacknife2" - item_state = "knife" - force_divisor = 0.34 // 20 with hardness 60 (steel) - thrown_force_divisor = 1.75 // 20 with weight 20 (steel) - attack_verb = list("sliced", "stabbed", "chopped", "cut") - applies_material_colour = 1 - -// Identical to the tactical knife but nowhere near as stabby. -// Kind of like the toy esword compared to the real thing. -/obj/item/weapon/material/knife/tacknife/boot - name = "boot knife" - desc = "A small fixed-blade knife for putting inside a boot." - icon = 'icons/obj/weapons.dmi' - icon_state = "tacknife3" - item_state = "knife" - force_divisor = 0.15 - applies_material_colour = 0 - -/obj/item/weapon/material/knife/hook - name = "meat hook" - desc = "A sharp, metal hook what sticks into things." - icon_state = "hook_knife" - -/obj/item/weapon/material/knife/ritual - name = "ritual knife" - desc = "The unearthly energies that once powered this blade are now dormant." - icon = 'icons/obj/wizard.dmi' - icon_state = "render" - applies_material_colour = 0 - -/obj/item/weapon/material/knife/table - name = "table knife" - icon = 'icons/obj/kitchen.dmi' - icon_state = "knife_table" - sharp = FALSE // blunted tip - force_divisor = 0.1 - -/obj/item/weapon/material/knife/table/plastic - default_material = "plastic" - -/obj/item/weapon/material/knife/butch - name = "butcher's cleaver" - icon_state = "cleaver" - desc = "A huge thing used for chopping and chopping up meat. This includes clowns and clown-by-products." - force_divisor = 0.25 // 15 when wielded with hardness 60 (steel) - attack_verb = list("cleaved", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - -/obj/item/weapon/material/knife/machete - name = "machete" - desc = "A sharp machete often found in survival kits." - icon_state = "machete" - force_divisor = 0.3 // 18 when hardness 60 (steel) - attack_verb = list("slashed", "chopped", "gouged", "ripped", "cut") - can_cleave = TRUE //Now hatchets inherit from the machete, and thus knives. Tables turned. - slot_flags = SLOT_BELT - default_material = "plasteel" //VOREStation Edit - -/obj/item/weapon/material/knife/machete/cyborg - name = "integrated machete" - desc = "A sharp machete often found attached to robots." - unbreakable = TRUE - -/obj/item/weapon/material/knife/tacknife/survival - name = "survival knife" - desc = "A hunting grade survival knife." - icon = 'icons/obj/kitchen.dmi' - icon_state = "survivalknife" - item_state = "knife" - applies_material_colour = FALSE - default_material = "plasteel" //VOREStation Edit - toolspeed = 2 // Use a real axe if you want to chop logs. - -/obj/item/weapon/material/knife/stone - name = "stone blade" - desc = "A crude blade made by chipping away at a piece of flint." - icon = 'icons/obj/weapons_vr.dmi' - icon_state = "stone_blade" - applies_material_colour = FALSE - fragile = TRUE - dulled = TRUE - edge = TRUE - sharp = TRUE - default_material = MAT_FLINT - -/obj/item/weapon/material/knife/stone/wood - name = "stone knife" - desc = "A crude blade of flint with a wooden handle, secured with plant fibers twined into sturdy ropes. Useful for cutting, stabbing, slicing, and even shearing." - icon_state = "stone_wood_knife" - dulled = FALSE - fragile = FALSE - -/obj/item/weapon/material/knife/stone/bone - name = "stone knife" - desc = "A crude blade of flint with a bone handle, secured with plant fibers twined into sturdy ropes. Useful for cutting, stabbing, slicing, and even shearing." - icon_state = "stone_bone_knife" - dulled = FALSE - fragile = FALSE +/obj/item/weapon/material/butterfly + name = "butterfly knife" + desc = "A basic metal blade concealed in a lightweight plasteel grip. Small enough when folded to fit in a pocket." + description_fluff = "This could be used to engrave messages on suitable surfaces if you really put your mind to it! Alt-click a floor or wall to engrave with it." //This way it's not a completely hidden, arcane art to engrave. + icon_state = "butterflyknife" + item_state = null + hitsound = null + var/active = 0 + w_class = ITEMSIZE_SMALL + attack_verb = list("patted", "tapped") + force_divisor = 0.25 // 15 when wielded with hardness 60 (steel) + thrown_force_divisor = 0.25 // 5 when thrown with weight 20 (steel) + drop_sound = 'sound/items/drop/knife.ogg' + pickup_sound = 'sound/items/pickup/knife.ogg' + +/obj/item/weapon/material/butterfly/update_force() + if(active) + edge = TRUE + sharp = TRUE + ..() //Updates force. + throwforce = max(3,force-3) + hitsound = 'sound/weapons/bladeslice.ogg' + icon_state += "_open" + w_class = ITEMSIZE_NORMAL + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + else + force = 3 + edge = FALSE + sharp = FALSE + hitsound = initial(hitsound) + icon_state = initial(icon_state) + w_class = initial(w_class) + attack_verb = initial(attack_verb) + +/obj/item/weapon/material/butterfly/switchblade + name = "switchblade" + desc = "A classic switchblade with gold engraving. Just holding it makes you feel like a gangster." + icon_state = "switchblade" + +/obj/item/weapon/material/butterfly/boxcutter + name = "box cutter" + desc = "A thin, inexpensive razor-blade knife designed to open cardboard boxes." + icon_state = "boxcutter" + force_divisor = 0.1 // 6 when wielded with hardness 60 (steel) + thrown_force_divisor = 0.2 // 4 when thrown with weight 20 (steel) + +/obj/item/weapon/material/butterfly/attack_self(mob/user) + active = !active + update_force() + + if(user) + if(active) + to_chat(user, "You flip out \the [src].") + playsound(src, 'sound/weapons/flipblade.ogg', 15, 1) + else + to_chat(user, "\The [src] can now be concealed.") + add_fingerprint(user) + +/* + * Kitchen knives + */ +/obj/item/weapon/material/knife + name = "kitchen knife" + icon = 'icons/obj/kitchen.dmi' + icon_state = "knife" + desc = "A general purpose Chef's Knife made by SpaceCook Incorporated. Guaranteed to stay sharp for years to come." + description_fluff = "This could be used to engrave messages on suitable surfaces if you really put your mind to it! Alt-click a floor or wall to engrave with it." //This way it's not a completely hidden, arcane art to engrave. + sharp = TRUE + edge = TRUE + force_divisor = 0.15 // 9 when wielded with hardness 60 (steel) + matter = list(MAT_STEEL = 12000) + origin_tech = list(TECH_MATERIAL = 1) + attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + drop_sound = 'sound/items/drop/knife.ogg' + +// These no longer inherit from hatchets. +/obj/item/weapon/material/knife/tacknife + name = "tactical knife" + desc = "You'd be killing loads of people if this was Medal of Valor: Heroes of Space." + icon = 'icons/obj/weapons.dmi' + icon_state = "tacknife" + item_state = "knife" + force_divisor = 0.25 //15 when hardness 60 (steel) + attack_verb = list("stabbed", "chopped", "cut") + applies_material_colour = 1 + +/obj/item/weapon/material/knife/tacknife/combatknife + name = "combat knife" + desc = "If only you had a boot to put it in." + icon = 'icons/obj/weapons.dmi' + icon_state = "tacknife2" + item_state = "knife" + force_divisor = 0.34 // 20 with hardness 60 (steel) + thrown_force_divisor = 1.75 // 20 with weight 20 (steel) + attack_verb = list("sliced", "stabbed", "chopped", "cut") + applies_material_colour = 1 + +// Identical to the tactical knife but nowhere near as stabby. +// Kind of like the toy esword compared to the real thing. +/obj/item/weapon/material/knife/tacknife/boot + name = "boot knife" + desc = "A small fixed-blade knife for putting inside a boot." + icon = 'icons/obj/weapons.dmi' + icon_state = "tacknife3" + item_state = "knife" + force_divisor = 0.15 + applies_material_colour = 0 + +/obj/item/weapon/material/knife/hook + name = "meat hook" + desc = "A sharp, metal hook what sticks into things." + icon_state = "hook_knife" + +/obj/item/weapon/material/knife/ritual + name = "ritual knife" + desc = "The unearthly energies that once powered this blade are now dormant." + icon = 'icons/obj/wizard.dmi' + icon_state = "render" + applies_material_colour = 0 + +/obj/item/weapon/material/knife/table + name = "table knife" + icon = 'icons/obj/kitchen.dmi' + icon_state = "knife_table" + sharp = FALSE // blunted tip + force_divisor = 0.1 + +/obj/item/weapon/material/knife/table/plastic + default_material = "plastic" + +/obj/item/weapon/material/knife/butch + name = "butcher's cleaver" + icon_state = "cleaver" + desc = "A huge thing used for chopping and chopping up meat. This includes clowns and clown-by-products." + force_divisor = 0.25 // 15 when wielded with hardness 60 (steel) + attack_verb = list("cleaved", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + +/obj/item/weapon/material/knife/machete + name = "machete" + desc = "A sharp machete often found in survival kits." + icon_state = "machete" + force_divisor = 0.3 // 18 when hardness 60 (steel) + attack_verb = list("slashed", "chopped", "gouged", "ripped", "cut") + can_cleave = TRUE //Now hatchets inherit from the machete, and thus knives. Tables turned. + slot_flags = SLOT_BELT + default_material = "plasteel" //VOREStation Edit + +/obj/item/weapon/material/knife/machete/cyborg + name = "integrated machete" + desc = "A sharp machete often found attached to robots." + unbreakable = TRUE + +/obj/item/weapon/material/knife/tacknife/survival + name = "survival knife" + desc = "A hunting grade survival knife." + icon = 'icons/obj/kitchen.dmi' + icon_state = "survivalknife" + item_state = "knife" + applies_material_colour = FALSE + default_material = "plasteel" //VOREStation Edit + toolspeed = 2 // Use a real axe if you want to chop logs. + +/obj/item/weapon/material/knife/stone + name = "stone blade" + desc = "A crude blade made by chipping away at a piece of flint." + icon = 'icons/obj/weapons_vr.dmi' + icon_state = "stone_blade" + applies_material_colour = FALSE + fragile = TRUE + dulled = TRUE + edge = TRUE + sharp = TRUE + default_material = MAT_FLINT + +/obj/item/weapon/material/knife/stone/wood + name = "stone knife" + desc = "A crude blade of flint with a wooden handle, secured with plant fibers twined into sturdy ropes. Useful for cutting, stabbing, slicing, and even shearing." + icon_state = "stone_wood_knife" + dulled = FALSE + fragile = FALSE + +/obj/item/weapon/material/knife/stone/bone + name = "stone knife" + desc = "A crude blade of flint with a bone handle, secured with plant fibers twined into sturdy ropes. Useful for cutting, stabbing, slicing, and even shearing." + icon_state = "stone_bone_knife" + dulled = FALSE + fragile = FALSE diff --git a/code/game/objects/items/weapons/material/misc.dm b/code/game/objects/items/weapons/material/misc.dm index 10c57abf854..09d15a03cd4 100644 --- a/code/game/objects/items/weapons/material/misc.dm +++ b/code/game/objects/items/weapons/material/misc.dm @@ -1,228 +1,228 @@ -/obj/item/weapon/material/harpoon - name = "harpoon" - sharp = TRUE - edge = FALSE - desc = "Tharr she blows!" - icon_state = "harpoon" - item_state = "harpoon" - force_divisor = 0.3 // 18 with hardness 60 (steel) - attack_verb = list("jabbed","stabbed","ripped") - -/obj/item/weapon/material/knife/machete/hatchet - name = "hatchet" - desc = "A very sharp axe blade upon a short fibremetal handle. It has a long history of chopping things, but now it is used for chopping wood." - icon = 'icons/obj/weapons.dmi' - icon_state = "hatchet" - force_divisor = 0.2 // 12 with hardness 60 (steel) - thrown_force_divisor = 0.75 // 15 with weight 20 (steel) - w_class = ITEMSIZE_SMALL - sharp = TRUE - edge = TRUE - origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 1) - attack_verb = list("chopped", "torn", "cut") - applies_material_colour = 0 - drop_sound = 'sound/items/drop/axe.ogg' - pickup_sound = 'sound/items/pickup/axe.ogg' -/* VOREStation Removal - We have one already -/obj/item/weapon/material/knife/machete/hatchet/stone - name = "sharp rock" - desc = "The secret is to bang the rocks together, guys." - force_divisor = 0.2 - icon_state = "rock" - item_state = "rock" - attack_verb = list("chopped", "torn", "cut") - -/obj/item/weapon/material/knife/machete/hatchet/stone/set_material(var/new_material) - var/old_name = name - . = ..() - name = old_name -*/ -/obj/item/weapon/material/knife/machete/hatchet/unathiknife - name = "duelling knife" - desc = "A length of leather-bound wood studded with razor-sharp teeth. How crude." - icon = 'icons/obj/weapons.dmi' - icon_state = "unathiknife" - attack_verb = list("ripped", "torn", "cut") - can_cleave = FALSE - var/hits = 0 - -/obj/item/weapon/material/knife/machete/hatchet/unathiknife/attack(mob/M as mob, mob/user as mob) - if(hits > 0) - return - var/obj/item/I = user.get_inactive_hand() - if(istype(I, /obj/item/weapon/material/knife/machete/hatchet/unathiknife)) - hits ++ - var/obj/item/weapon/W = I - W.attack(M, user) - W.afterattack(M, user) - ..() - -/obj/item/weapon/material/knife/machete/hatchet/unathiknife/afterattack(mob/M as mob, mob/user as mob) - hits = initial(hits) - ..() - -/obj/item/weapon/material/minihoe // -- Numbers - name = "mini hoe" - desc = "It's used for removing weeds or scratching your back." - icon = 'icons/obj/weapons.dmi' - icon_state = "hoe" - force_divisor = 0.25 // 5 with weight 20 (steel) - thrown_force_divisor = 0.25 // as above - dulled_divisor = 0.75 //Still metal on a long pole - w_class = ITEMSIZE_SMALL - attack_verb = list("slashed", "sliced", "cut", "clawed") - -/obj/item/weapon/material/snow/snowball - name = "loose packed snowball" - desc = "A fun snowball. Throw it at your friends!" - icon = 'icons/obj/weapons.dmi' - icon_state = "snowball" - default_material = MAT_SNOW - health = 1 - fragile = 1 - force_divisor = 0.01 - thrown_force_divisor = 0.10 - w_class = ITEMSIZE_SMALL - attack_verb = list("mushed", "splatted", "splooshed", "splushed") // Words that totally exist. - -/obj/item/weapon/material/snow/snowball/attack_self(mob/user as mob) - if(user.a_intent == I_HURT) - to_chat(user, SPAN_NOTICE("You smash the snowball in your hand.")) - var/atom/S = new /obj/item/stack/material/snow(user.loc) - qdel(src) - user.put_in_hands(S) - else - to_chat(user, SPAN_NOTICE("You start compacting the snowball.")) - if(do_after(user, 2 SECONDS)) - var/atom/S = new /obj/item/weapon/material/snow/snowball/reinforced(user.loc) - qdel(src) - user.put_in_hands(S) - -/obj/item/weapon/material/snow/snowball/reinforced - name = "snowball" - desc = "A well-formed and fun snowball. It looks kind of dangerous." - //icon_state = "reinf-snowball" - force_divisor = 0.20 - thrown_force_divisor = 0.25 - -/obj/item/weapon/material/whip - name = "whip" - desc = "A tool used to discipline animals, or look cool. Mostly the latter." - description_info = "Help - Standard attack, no modifiers.
                    \ - Disarm - Disarming strike. Attempts to disarm the target at range, similar to an unarmed disarm. Additionally, will force the target (if possible) to move away from you.
                    \ - Grab - Grappling strike. Attempts to pull the target toward you. This can also move objects.
                    \ - Harm - A standard strike with a small chance to disarm." - icon = 'icons/obj/weapons.dmi' - icon_state = "whip" - item_state = "chain" - default_material = MAT_LEATHER - slot_flags = SLOT_BELT - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_COMBAT = 2) - attack_verb = list("flogged", "whipped", "lashed", "disciplined") - force_divisor = 0.15 - thrown_force_divisor = 0.25 - reach = 2 - - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', - ) - -/obj/item/weapon/material/whip/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity) - ..() - - if(!proximity) - return - - if(istype(A, /atom/movable)) - var/atom/movable/AM = A - if(AM.anchored) - to_chat(user, "\The [AM] won't budge.") - return - - else - if(!istype(AM, /obj/item)) - user.visible_message("\The [AM] is pulled along by \the [src]!") - AM.Move(get_step(AM, get_dir(AM, src))) - return - - else - user.visible_message("\The [AM] is snatched by \the [src]!") - AM.throw_at(user, reach, 0.1, user) - -/obj/item/weapon/material/whip/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) - if(user.a_intent) - switch(user.a_intent) - if(I_HURT) - if(prob(10) && istype(target, /mob/living/carbon/human) && (user.zone_sel in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND))) - to_chat(target, "\The [src] rips at your hands!") - ranged_disarm(target) - if(I_DISARM) - if(prob(min(90, force * 3)) && istype(target, /mob/living/carbon/human) && (user.zone_sel in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND))) - ranged_disarm(target) - else - target.visible_message("\The [src] sends \the [target] stumbling away.") - target.Move(get_step(target,get_dir(user,target))) - if(I_GRAB) - var/turf/STurf = get_turf(target) - spawn(2) - playsound(STurf, 'sound/effects/snap.ogg', 60, 1) - target.visible_message("\The [src] yanks \the [target] towards \the [user]!") - target.throw_at(get_turf(get_step(user,get_dir(user,target))), 2, 1, src) - - ..() - -/obj/item/weapon/material/whip/proc/ranged_disarm(var/mob/living/carbon/human/H, var/mob/living/user) - if(istype(H)) - var/list/holding = list(H.get_active_hand() = 40, H.get_inactive_hand() = 20) - - if(user.zone_sel in list(BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND)) - for(var/obj/item/weapon/gun/W in holding) - if(W && prob(holding[W])) - var/list/turfs = list() - for(var/turf/T in view()) - turfs += T - if(turfs.len) - var/turf/target = pick(turfs) - visible_message("[H]'s [W] goes off due to \the [src]!") - return W.afterattack(target,H) - - if(!(H.species.flags & NO_SLIP) && prob(10) && (user.zone_sel in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT))) - var/armor_check = H.run_armor_check(user.zone_sel, "melee") - H.apply_effect(3, WEAKEN, armor_check) - playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - if(armor_check < 60) - visible_message("\The [src] has tripped [H]!") - else - visible_message("\The [src] attempted to trip [H]!") - return - - else - if(H.break_all_grabs(user)) - playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - return - - if(user.zone_sel in list(BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND)) - for(var/obj/item/I in holding) - if(I && prob(holding[I])) - H.drop_from_inventory(I) - visible_message("\The [src] has disarmed [H]!") - playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - return - -/obj/item/weapon/material/whip/attack_self(mob/user) - user.visible_message("\The [user] cracks \the [src]!") - playsound(src, 'sound/effects/snap.ogg', 50, 1) - -/obj/item/weapon/material/knife/machete/hatchet/stone - name = "hatchet" - desc = "A very sharp axe blade upon a short fibremetal handle. It has a long history of chopping things, but now it is used for chopping wood." - icon = 'icons/obj/weapons_vr.dmi' - icon_state = "stone_wood_axe" - default_material = MAT_FLINT - origin_tech = list() - applies_material_colour = FALSE - -/obj/item/weapon/material/knife/machete/hatchet/stone/bone - icon_state = "stone_bone_axe" +/obj/item/weapon/material/harpoon + name = "harpoon" + sharp = TRUE + edge = FALSE + desc = "Tharr she blows!" + icon_state = "harpoon" + item_state = "harpoon" + force_divisor = 0.3 // 18 with hardness 60 (steel) + attack_verb = list("jabbed","stabbed","ripped") + +/obj/item/weapon/material/knife/machete/hatchet + name = "hatchet" + desc = "A very sharp axe blade upon a short fibremetal handle. It has a long history of chopping things, but now it is used for chopping wood." + icon = 'icons/obj/weapons.dmi' + icon_state = "hatchet" + force_divisor = 0.2 // 12 with hardness 60 (steel) + thrown_force_divisor = 0.75 // 15 with weight 20 (steel) + w_class = ITEMSIZE_SMALL + sharp = TRUE + edge = TRUE + origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 1) + attack_verb = list("chopped", "torn", "cut") + applies_material_colour = 0 + drop_sound = 'sound/items/drop/axe.ogg' + pickup_sound = 'sound/items/pickup/axe.ogg' +/* VOREStation Removal - We have one already +/obj/item/weapon/material/knife/machete/hatchet/stone + name = "sharp rock" + desc = "The secret is to bang the rocks together, guys." + force_divisor = 0.2 + icon_state = "rock" + item_state = "rock" + attack_verb = list("chopped", "torn", "cut") + +/obj/item/weapon/material/knife/machete/hatchet/stone/set_material(var/new_material) + var/old_name = name + . = ..() + name = old_name +*/ +/obj/item/weapon/material/knife/machete/hatchet/unathiknife + name = "duelling knife" + desc = "A length of leather-bound wood studded with razor-sharp teeth. How crude." + icon = 'icons/obj/weapons.dmi' + icon_state = "unathiknife" + attack_verb = list("ripped", "torn", "cut") + can_cleave = FALSE + var/hits = 0 + +/obj/item/weapon/material/knife/machete/hatchet/unathiknife/attack(mob/M as mob, mob/user as mob) + if(hits > 0) + return + var/obj/item/I = user.get_inactive_hand() + if(istype(I, /obj/item/weapon/material/knife/machete/hatchet/unathiknife)) + hits ++ + var/obj/item/weapon/W = I + W.attack(M, user) + W.afterattack(M, user) + ..() + +/obj/item/weapon/material/knife/machete/hatchet/unathiknife/afterattack(mob/M as mob, mob/user as mob) + hits = initial(hits) + ..() + +/obj/item/weapon/material/minihoe // -- Numbers + name = "mini hoe" + desc = "It's used for removing weeds or scratching your back." + icon = 'icons/obj/weapons.dmi' + icon_state = "hoe" + force_divisor = 0.25 // 5 with weight 20 (steel) + thrown_force_divisor = 0.25 // as above + dulled_divisor = 0.75 //Still metal on a long pole + w_class = ITEMSIZE_SMALL + attack_verb = list("slashed", "sliced", "cut", "clawed") + +/obj/item/weapon/material/snow/snowball + name = "loose packed snowball" + desc = "A fun snowball. Throw it at your friends!" + icon = 'icons/obj/weapons.dmi' + icon_state = "snowball" + default_material = MAT_SNOW + health = 1 + fragile = 1 + force_divisor = 0.01 + thrown_force_divisor = 0.10 + w_class = ITEMSIZE_SMALL + attack_verb = list("mushed", "splatted", "splooshed", "splushed") // Words that totally exist. + +/obj/item/weapon/material/snow/snowball/attack_self(mob/user as mob) + if(user.a_intent == I_HURT) + to_chat(user, SPAN_NOTICE("You smash the snowball in your hand.")) + var/atom/S = new /obj/item/stack/material/snow(user.loc) + qdel(src) + user.put_in_hands(S) + else + to_chat(user, SPAN_NOTICE("You start compacting the snowball.")) + if(do_after(user, 2 SECONDS)) + var/atom/S = new /obj/item/weapon/material/snow/snowball/reinforced(user.loc) + qdel(src) + user.put_in_hands(S) + +/obj/item/weapon/material/snow/snowball/reinforced + name = "snowball" + desc = "A well-formed and fun snowball. It looks kind of dangerous." + //icon_state = "reinf-snowball" + force_divisor = 0.20 + thrown_force_divisor = 0.25 + +/obj/item/weapon/material/whip + name = "whip" + desc = "A tool used to discipline animals, or look cool. Mostly the latter." + description_info = "Help - Standard attack, no modifiers.
                    \ + Disarm - Disarming strike. Attempts to disarm the target at range, similar to an unarmed disarm. Additionally, will force the target (if possible) to move away from you.
                    \ + Grab - Grappling strike. Attempts to pull the target toward you. This can also move objects.
                    \ + Harm - A standard strike with a small chance to disarm." + icon = 'icons/obj/weapons.dmi' + icon_state = "whip" + item_state = "chain" + default_material = MAT_LEATHER + slot_flags = SLOT_BELT + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_COMBAT = 2) + attack_verb = list("flogged", "whipped", "lashed", "disciplined") + force_divisor = 0.15 + thrown_force_divisor = 0.25 + reach = 2 + + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + +/obj/item/weapon/material/whip/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity) + ..() + + if(!proximity) + return + + if(istype(A, /atom/movable)) + var/atom/movable/AM = A + if(AM.anchored) + to_chat(user, "\The [AM] won't budge.") + return + + else + if(!istype(AM, /obj/item)) + user.visible_message("\The [AM] is pulled along by \the [src]!") + AM.Move(get_step(AM, get_dir(AM, src))) + return + + else + user.visible_message("\The [AM] is snatched by \the [src]!") + AM.throw_at(user, reach, 0.1, user) + +/obj/item/weapon/material/whip/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) + if(user.a_intent) + switch(user.a_intent) + if(I_HURT) + if(prob(10) && istype(target, /mob/living/carbon/human) && (user.zone_sel in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND))) + to_chat(target, "\The [src] rips at your hands!") + ranged_disarm(target) + if(I_DISARM) + if(prob(min(90, force * 3)) && istype(target, /mob/living/carbon/human) && (user.zone_sel in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND))) + ranged_disarm(target) + else + target.visible_message("\The [src] sends \the [target] stumbling away.") + target.Move(get_step(target,get_dir(user,target))) + if(I_GRAB) + var/turf/STurf = get_turf(target) + spawn(2) + playsound(STurf, 'sound/effects/snap.ogg', 60, 1) + target.visible_message("\The [src] yanks \the [target] towards \the [user]!") + target.throw_at(get_turf(get_step(user,get_dir(user,target))), 2, 1, src) + + ..() + +/obj/item/weapon/material/whip/proc/ranged_disarm(var/mob/living/carbon/human/H, var/mob/living/user) + if(istype(H)) + var/list/holding = list(H.get_active_hand() = 40, H.get_inactive_hand() = 20) + + if(user.zone_sel in list(BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND)) + for(var/obj/item/weapon/gun/W in holding) + if(W && prob(holding[W])) + var/list/turfs = list() + for(var/turf/T in view()) + turfs += T + if(turfs.len) + var/turf/target = pick(turfs) + visible_message("[H]'s [W] goes off due to \the [src]!") + return W.afterattack(target,H) + + if(!(H.species.flags & NO_SLIP) && prob(10) && (user.zone_sel in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT))) + var/armor_check = H.run_armor_check(user.zone_sel, "melee") + H.apply_effect(3, WEAKEN, armor_check) + playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + if(armor_check < 60) + visible_message("\The [src] has tripped [H]!") + else + visible_message("\The [src] attempted to trip [H]!") + return + + else + if(H.break_all_grabs(user)) + playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + return + + if(user.zone_sel in list(BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND)) + for(var/obj/item/I in holding) + if(I && prob(holding[I])) + H.drop_from_inventory(I) + visible_message("\The [src] has disarmed [H]!") + playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + return + +/obj/item/weapon/material/whip/attack_self(mob/user) + user.visible_message("\The [user] cracks \the [src]!") + playsound(src, 'sound/effects/snap.ogg', 50, 1) + +/obj/item/weapon/material/knife/machete/hatchet/stone + name = "hatchet" + desc = "A very sharp axe blade upon a short fibremetal handle. It has a long history of chopping things, but now it is used for chopping wood." + icon = 'icons/obj/weapons_vr.dmi' + icon_state = "stone_wood_axe" + default_material = MAT_FLINT + origin_tech = list() + applies_material_colour = FALSE + +/obj/item/weapon/material/knife/machete/hatchet/stone/bone + icon_state = "stone_bone_axe" diff --git a/code/game/objects/items/weapons/material/shards.dm b/code/game/objects/items/weapons/material/shards.dm index 5432d46f32d..5f9e2808cd6 100644 --- a/code/game/objects/items/weapons/material/shards.dm +++ b/code/game/objects/items/weapons/material/shards.dm @@ -1,151 +1,151 @@ -// Glass shards - -/obj/item/weapon/material/shard - name = "shard" - icon = 'icons/obj/shards.dmi' - desc = "Made of nothing. How does this even exist?" // set based on material, if this desc is visible it's a bug (shards default to being made of glass) - icon_state = "large" - randpixel = 8 - sharp = TRUE - edge = TRUE - w_class = ITEMSIZE_SMALL - force_divisor = 0.25 // 7.5 with hardness 30 (glass) - thrown_force_divisor = 0.5 - item_state = "shard-glass" - attack_verb = list("stabbed", "slashed", "sliced", "cut") - default_material = "glass" - unbreakable = 1 //It's already broken. - drops_debris = 0 - -/obj/item/weapon/material/shard/set_material(var/new_material) - ..(new_material) - if(!istype(material)) - return - - icon_state = "[material.shard_icon][pick("large", "medium", "small")]" - randpixel_xy() - update_icon() - - if(material.shard_type) - name = "[material.display_name] [material.shard_type]" - desc = "A small piece of [material.display_name]. It looks sharp, you wouldn't want to step on it barefoot. Could probably be used as ... a throwing weapon?" - switch(material.shard_type) - if(SHARD_SPLINTER, SHARD_SHRAPNEL) - gender = PLURAL - else - gender = NEUTER - else - qdel(src) - -/obj/item/weapon/material/shard/update_icon() - if(material) - color = material.icon_colour - // 1-(1-x)^2, so that glass shards with 0.3 opacity end up somewhat visible at 0.51 opacity - alpha = 255 * (1 - (1 - material.opacity)*(1 - material.opacity)) - else - color = "#ffffff" - alpha = 255 - -/obj/item/weapon/material/shard/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.has_tool_quality(TOOL_WELDER) && material.shard_can_repair) - var/obj/item/weapon/weldingtool/WT = W.get_welder() - if(WT.remove_fuel(0, user)) - material.place_sheet(loc, 1) - qdel(src) - return - return ..() - -/obj/item/weapon/material/shard/afterattack(var/atom/target, mob/living/carbon/human/user as mob, proximity) - if(!proximity) - return - var/active_hand //hand the shard is in - var/will_break = FALSE - var/protected_hands = FALSE //this is a fucking mess - var/break_damage = 4 - var/light_glove_d = rand(2, 4) - var/no_glove_d = rand(4, 6) - var/list/forbidden_gloves = list( - /obj/item/clothing/gloves/sterile, - /obj/item/clothing/gloves/knuckledusters - ) - - if(src == user.l_hand) - active_hand = BP_L_HAND - else if(src == user.r_hand) - active_hand = BP_R_HAND - else - return // If it's not actually in our hands anymore, we were probably gentle with it - - active_hand = (src == user.l_hand) ? BP_L_HAND : BP_R_HAND // May not actually be faster than an if-else block, but a little bit cleaner -Ater - - if(prob(75)) - will_break = TRUE - - if(user.gloves && (user.gloves.body_parts_covered & HANDS) && istype(user.gloves, /obj/item/clothing/gloves)) // Not-gloves aren't gloves, and therefore don't protect us - protected_hands = TRUE // If we're wearing gloves we can probably handle it just fine - for(var/I in forbidden_gloves) - if(istype(user.gloves, I)) // forbidden_gloves is a blacklist, so if we match anything in there, our hands are not protected - protected_hands = FALSE - break - - if(user.gloves && !protected_hands) - to_chat(user, "\The [src] partially cuts into your hand through your gloves as you hit \the [target]!") - user.apply_damage(light_glove_d + will_break ? break_damage : 0, BRUTE, active_hand, 0, 0, src, src.sharp, src.edge) // Ternary to include break damage - - else if(!user.gloves) - to_chat(user, "\The [src] cuts into your hand as you hit \the [target]!") - user.apply_damage(no_glove_d + will_break ? break_damage : 0, BRUTE, active_hand, 0, 0, src, src.sharp, src.edge) - - if(will_break && src.loc == user) // If it's not in our hand anymore - user.visible_message("[user] hit \the [target] with \the [src], shattering it!", "You shatter \the [src] in your hand!") - playsound(src, pick('sound/effects/Glassbr1.ogg', 'sound/effects/Glassbr2.ogg', 'sound/effects/Glassbr3.ogg'), 30, 1) - qdel(src) - return - -/obj/item/weapon/material/shard/Crossed(atom/movable/AM as mob|obj) - ..() - if(AM.is_incorporeal()) - return - if(isliving(AM)) - var/mob/M = AM - - if(M.buckled) //wheelchairs, office chairs, rollerbeds - return - - playsound(src, 'sound/effects/glass_step.ogg', 50, 1) // not sure how to handle metal shards with sounds - if(ishuman(M)) - var/mob/living/carbon/human/H = M - - if(H.species.siemens_coefficient<0.5) //Thick skin. - return - - if( H.shoes || ( H.wear_suit && (H.wear_suit.body_parts_covered & FEET) ) ) - return - - if(H.species.flags & NO_MINOR_CUT) - return - - to_chat(H, "You step on \the [src]!") - - var/list/check = list("l_foot", "r_foot") - while(check.len) - var/picked = pick(check) - var/obj/item/organ/external/affecting = H.get_organ(picked) - if(affecting) - if(affecting.robotic >= ORGAN_ROBOT) - return - if(affecting.take_damage(force, 0)) - H.UpdateDamageIcon() - H.updatehealth() - if(affecting.organ_can_feel_pain()) - H.Weaken(3) - return - check -= picked - return - -// Preset types - left here for the code that uses them -/obj/item/weapon/material/shard/shrapnel/New(loc) - ..(loc, "steel") - -/obj/item/weapon/material/shard/phoron/New(loc) - ..(loc, "borosilicate glass") +// Glass shards + +/obj/item/weapon/material/shard + name = "shard" + icon = 'icons/obj/shards.dmi' + desc = "Made of nothing. How does this even exist?" // set based on material, if this desc is visible it's a bug (shards default to being made of glass) + icon_state = "large" + randpixel = 8 + sharp = TRUE + edge = TRUE + w_class = ITEMSIZE_SMALL + force_divisor = 0.25 // 7.5 with hardness 30 (glass) + thrown_force_divisor = 0.5 + item_state = "shard-glass" + attack_verb = list("stabbed", "slashed", "sliced", "cut") + default_material = "glass" + unbreakable = 1 //It's already broken. + drops_debris = 0 + +/obj/item/weapon/material/shard/set_material(var/new_material) + ..(new_material) + if(!istype(material)) + return + + icon_state = "[material.shard_icon][pick("large", "medium", "small")]" + randpixel_xy() + update_icon() + + if(material.shard_type) + name = "[material.display_name] [material.shard_type]" + desc = "A small piece of [material.display_name]. It looks sharp, you wouldn't want to step on it barefoot. Could probably be used as ... a throwing weapon?" + switch(material.shard_type) + if(SHARD_SPLINTER, SHARD_SHRAPNEL) + gender = PLURAL + else + gender = NEUTER + else + qdel(src) + +/obj/item/weapon/material/shard/update_icon() + if(material) + color = material.icon_colour + // 1-(1-x)^2, so that glass shards with 0.3 opacity end up somewhat visible at 0.51 opacity + alpha = 255 * (1 - (1 - material.opacity)*(1 - material.opacity)) + else + color = "#ffffff" + alpha = 255 + +/obj/item/weapon/material/shard/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WELDER) && material.shard_can_repair) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(WT.remove_fuel(0, user)) + material.place_sheet(loc, 1) + qdel(src) + return + return ..() + +/obj/item/weapon/material/shard/afterattack(var/atom/target, mob/living/carbon/human/user as mob, proximity) + if(!proximity) + return + var/active_hand //hand the shard is in + var/will_break = FALSE + var/protected_hands = FALSE //this is a fucking mess + var/break_damage = 4 + var/light_glove_d = rand(2, 4) + var/no_glove_d = rand(4, 6) + var/list/forbidden_gloves = list( + /obj/item/clothing/gloves/sterile, + /obj/item/clothing/gloves/knuckledusters + ) + + if(src == user.l_hand) + active_hand = BP_L_HAND + else if(src == user.r_hand) + active_hand = BP_R_HAND + else + return // If it's not actually in our hands anymore, we were probably gentle with it + + active_hand = (src == user.l_hand) ? BP_L_HAND : BP_R_HAND // May not actually be faster than an if-else block, but a little bit cleaner -Ater + + if(prob(75)) + will_break = TRUE + + if(user.gloves && (user.gloves.body_parts_covered & HANDS) && istype(user.gloves, /obj/item/clothing/gloves)) // Not-gloves aren't gloves, and therefore don't protect us + protected_hands = TRUE // If we're wearing gloves we can probably handle it just fine + for(var/I in forbidden_gloves) + if(istype(user.gloves, I)) // forbidden_gloves is a blacklist, so if we match anything in there, our hands are not protected + protected_hands = FALSE + break + + if(user.gloves && !protected_hands) + to_chat(user, "\The [src] partially cuts into your hand through your gloves as you hit \the [target]!") + user.apply_damage(light_glove_d + will_break ? break_damage : 0, BRUTE, active_hand, 0, 0, src, src.sharp, src.edge) // Ternary to include break damage + + else if(!user.gloves) + to_chat(user, "\The [src] cuts into your hand as you hit \the [target]!") + user.apply_damage(no_glove_d + will_break ? break_damage : 0, BRUTE, active_hand, 0, 0, src, src.sharp, src.edge) + + if(will_break && src.loc == user) // If it's not in our hand anymore + user.visible_message("[user] hit \the [target] with \the [src], shattering it!", "You shatter \the [src] in your hand!") + playsound(src, pick('sound/effects/Glassbr1.ogg', 'sound/effects/Glassbr2.ogg', 'sound/effects/Glassbr3.ogg'), 30, 1) + qdel(src) + return + +/obj/item/weapon/material/shard/Crossed(atom/movable/AM as mob|obj) + ..() + if(AM.is_incorporeal()) + return + if(isliving(AM)) + var/mob/M = AM + + if(M.buckled) //wheelchairs, office chairs, rollerbeds + return + + playsound(src, 'sound/effects/glass_step.ogg', 50, 1) // not sure how to handle metal shards with sounds + if(ishuman(M)) + var/mob/living/carbon/human/H = M + + if(H.species.siemens_coefficient<0.5) //Thick skin. + return + + if( H.shoes || ( H.wear_suit && (H.wear_suit.body_parts_covered & FEET) ) ) + return + + if(H.species.flags & NO_MINOR_CUT) + return + + to_chat(H, "You step on \the [src]!") + + var/list/check = list("l_foot", "r_foot") + while(check.len) + var/picked = pick(check) + var/obj/item/organ/external/affecting = H.get_organ(picked) + if(affecting) + if(affecting.robotic >= ORGAN_ROBOT) + return + if(affecting.take_damage(force, 0)) + H.UpdateDamageIcon() + H.updatehealth() + if(affecting.organ_can_feel_pain()) + H.Weaken(3) + return + check -= picked + return + +// Preset types - left here for the code that uses them +/obj/item/weapon/material/shard/shrapnel/New(loc) + ..(loc, "steel") + +/obj/item/weapon/material/shard/phoron/New(loc) + ..(loc, "borosilicate glass") diff --git a/code/game/objects/items/weapons/material/swords.dm b/code/game/objects/items/weapons/material/swords.dm index e0a75ca8bd7..8aac4dca4bb 100644 --- a/code/game/objects/items/weapons/material/swords.dm +++ b/code/game/objects/items/weapons/material/swords.dm @@ -1,86 +1,86 @@ -/obj/item/weapon/material/sword - name = "claymore" - desc = "What are you standing around staring at this for? Get to killing!" - icon_state = "claymore" - slot_flags = SLOT_BELT - force_divisor = 0.7 // 42 when wielded with hardnes 60 (steel) - thrown_force_divisor = 0.5 // 10 when thrown with weight 20 (steel) - sharp = TRUE - edge = TRUE - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - hitsound = 'sound/weapons/bladeslice.ogg' - drop_sound = 'sound/items/drop/sword.ogg' - pickup_sound = 'sound/items/pickup/sword.ogg' - -/obj/item/weapon/material/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(unique_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - -/obj/item/weapon/material/sword/katana - name = "katana" - desc = "Woefully underpowered in D20. This one looks pretty sharp." - icon_state = "katana" - slot_flags = SLOT_BELT | SLOT_BACK - -/obj/item/weapon/material/sword/katana/caneblade - name = "cane blade" - desc = "Used for making people fall over instead of helping them stand up." - icon_state = "caneblade" - item_state = "caneblade" - -/obj/item/weapon/material/sword/rapier - name = "rapier" - desc = "A slender, fancy and sharply pointed sword." - icon_state = "rapier" - item_state = "rapier" - slot_flags = SLOT_BELT - applies_material_colour = 0 - attack_verb = list("attacked", "stabbed", "prodded", "poked", "lunged") - edge = 0 //rapiers are pointy, but not cutty like other swords - -/obj/item/weapon/material/sword/longsword - name = "longsword" - desc = "A double-edged large blade." - icon_state = "longsword" - item_state = "longsword" - applies_material_colour = 0 - slot_flags = SLOT_BELT | SLOT_BACK - can_cleave = TRUE - -/obj/item/weapon/material/sword/sabre - name = "sabre" - desc = "A sharp curved sword, a favored weapon of pirates far in the past." - icon_state = "sabre" - item_state = "sabre" - applies_material_colour = 0 //messes up the hilt color otherwise - slot_flags = SLOT_BELT - -/obj/item/weapon/material/sword/battleaxe - name = "battleaxe" - desc = "A one handed battle axe, still a deadly weapon." - icon_state = "axe" - item_state = "axe" - slot_flags = SLOT_BACK - attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") - applies_material_colour = 0 - drop_sound = 'sound/items/drop/axe.ogg' - pickup_sound = 'sound/items/pickup/axe.ogg' - can_cleave = TRUE - -/obj/item/weapon/material/sword/battleaxe/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(unique_parry_check(user, attacker, damage_source) && prob(10)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - -/obj/item/weapon/material/sword/gladius - name = "gladius" - desc = "An ancient short sword, designed to stab and cut." - icon_state = "gladius" - item_state = "gladius" - applies_material_colour = 0 - slot_flags = SLOT_BELT +/obj/item/weapon/material/sword + name = "claymore" + desc = "What are you standing around staring at this for? Get to killing!" + icon_state = "claymore" + slot_flags = SLOT_BELT + force_divisor = 0.7 // 42 when wielded with hardnes 60 (steel) + thrown_force_divisor = 0.5 // 10 when thrown with weight 20 (steel) + sharp = TRUE + edge = TRUE + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + hitsound = 'sound/weapons/bladeslice.ogg' + drop_sound = 'sound/items/drop/sword.ogg' + pickup_sound = 'sound/items/pickup/sword.ogg' + +/obj/item/weapon/material/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(unique_parry_check(user, attacker, damage_source) && prob(50)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) + return 1 + return 0 + +/obj/item/weapon/material/sword/katana + name = "katana" + desc = "Woefully underpowered in D20. This one looks pretty sharp." + icon_state = "katana" + slot_flags = SLOT_BELT | SLOT_BACK + +/obj/item/weapon/material/sword/katana/caneblade + name = "cane blade" + desc = "Used for making people fall over instead of helping them stand up." + icon_state = "caneblade" + item_state = "caneblade" + +/obj/item/weapon/material/sword/rapier + name = "rapier" + desc = "A slender, fancy and sharply pointed sword." + icon_state = "rapier" + item_state = "rapier" + slot_flags = SLOT_BELT + applies_material_colour = 0 + attack_verb = list("attacked", "stabbed", "prodded", "poked", "lunged") + edge = 0 //rapiers are pointy, but not cutty like other swords + +/obj/item/weapon/material/sword/longsword + name = "longsword" + desc = "A double-edged large blade." + icon_state = "longsword" + item_state = "longsword" + applies_material_colour = 0 + slot_flags = SLOT_BELT | SLOT_BACK + can_cleave = TRUE + +/obj/item/weapon/material/sword/sabre + name = "sabre" + desc = "A sharp curved sword, a favored weapon of pirates far in the past." + icon_state = "sabre" + item_state = "sabre" + applies_material_colour = 0 //messes up the hilt color otherwise + slot_flags = SLOT_BELT + +/obj/item/weapon/material/sword/battleaxe + name = "battleaxe" + desc = "A one handed battle axe, still a deadly weapon." + icon_state = "axe" + item_state = "axe" + slot_flags = SLOT_BACK + attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") + applies_material_colour = 0 + drop_sound = 'sound/items/drop/axe.ogg' + pickup_sound = 'sound/items/pickup/axe.ogg' + can_cleave = TRUE + +/obj/item/weapon/material/sword/battleaxe/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(unique_parry_check(user, attacker, damage_source) && prob(10)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) + return 1 + return 0 + +/obj/item/weapon/material/sword/gladius + name = "gladius" + desc = "An ancient short sword, designed to stab and cut." + icon_state = "gladius" + item_state = "gladius" + applies_material_colour = 0 + slot_flags = SLOT_BELT diff --git a/code/game/objects/items/weapons/material/thrown.dm b/code/game/objects/items/weapons/material/thrown.dm index dcfaad58019..12eaa25100d 100644 --- a/code/game/objects/items/weapons/material/thrown.dm +++ b/code/game/objects/items/weapons/material/thrown.dm @@ -1,24 +1,24 @@ -/obj/item/weapon/material/star - name = "shuriken" - desc = "A sharp, perfectly weighted piece of metal." - icon_state = "star" - force_divisor = 0.1 // 6 with hardness 60 (steel) - thrown_force_divisor = 0.75 // 15 with weight 20 (steel) - throw_speed = 10 - throw_range = 15 - sharp = TRUE - edge = TRUE - -/obj/item/weapon/material/star/New() - ..() - src.pixel_x = rand(-12, 12) - src.pixel_y = rand(-12, 12) - -/obj/item/weapon/material/star/throw_impact(atom/hit_atom) - ..() - if(material.radioactivity>0 && istype(hit_atom,/mob/living)) - var/mob/living/M = hit_atom - M.adjustToxLoss(rand(20,40)) - -/obj/item/weapon/material/star/ninja +/obj/item/weapon/material/star + name = "shuriken" + desc = "A sharp, perfectly weighted piece of metal." + icon_state = "star" + force_divisor = 0.1 // 6 with hardness 60 (steel) + thrown_force_divisor = 0.75 // 15 with weight 20 (steel) + throw_speed = 10 + throw_range = 15 + sharp = TRUE + edge = TRUE + +/obj/item/weapon/material/star/New() + ..() + src.pixel_x = rand(-12, 12) + src.pixel_y = rand(-12, 12) + +/obj/item/weapon/material/star/throw_impact(atom/hit_atom) + ..() + if(material.radioactivity>0 && istype(hit_atom,/mob/living)) + var/mob/living/M = hit_atom + M.adjustToxLoss(rand(20,40)) + +/obj/item/weapon/material/star/ninja default_material = "uranium" \ No newline at end of file diff --git a/code/game/objects/items/weapons/material/twohanded.dm b/code/game/objects/items/weapons/material/twohanded.dm index 290925a3665..2e9f0823c86 100644 --- a/code/game/objects/items/weapons/material/twohanded.dm +++ b/code/game/objects/items/weapons/material/twohanded.dm @@ -1,193 +1,193 @@ -/* Two-handed Weapons - * Contains: - * Twohanded - * Fireaxe - * Double-Bladed Energy Swords - */ - -/*################################################################## -##################### TWO HANDED WEAPONS BE HERE~ -Agouri :3 ######## -####################################################################*/ - -//Rewrote TwoHanded weapons stuff and put it all here. Just copypasta fireaxe to make new ones ~Carn -//This rewrite means we don't have two variables for EVERY item which are used only by a few weapons. -//It also tidies stuff up elsewhere. - -/* - * Twohanded - */ -/obj/item/weapon/material/twohanded - w_class = ITEMSIZE_LARGE - var/wielded = 0 - var/force_wielded = 0 - var/force_unwielded - var/wieldsound = null - var/unwieldsound = null - var/base_icon - var/base_name - var/unwielded_force_divisor = 0.25 - hitsound = "swing_hit" - drop_sound = 'sound/items/drop/sword.ogg' - pickup_sound = 'sound/items/pickup/sword.ogg' - -/obj/item/weapon/material/twohanded/update_held_icon() - var/mob/living/M = loc - if(istype(M) && M.can_wield_item(src) && is_held_twohanded(M)) - wielded = 1 - force = force_wielded - name = "[base_name] (wielded)" - update_icon() - else - wielded = 0 - force = force_unwielded - name = "[base_name]" - update_icon() - ..() - -/obj/item/weapon/material/twohanded/update_force() - base_name = name - if(sharp || edge) - force_wielded = material.get_edge_damage() - else - force_wielded = material.get_blunt_damage() - force_wielded = round(force_wielded*force_divisor) - force_unwielded = round(force_wielded*unwielded_force_divisor) - force = force_unwielded - throwforce = round(force*thrown_force_divisor) - //to_world("[src] has unwielded force [force_unwielded], wielded force [force_wielded] and throwforce [throwforce] when made from default material [material.name]") - -/obj/item/weapon/material/twohanded/New() - ..() - update_icon() - -//Allow a small chance of parrying melee attacks when wielded - maybe generalize this to other weapons someday -/obj/item/weapon/material/twohanded/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(wielded && default_parry_check(user, attacker, damage_source) && prob(15)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - -/obj/item/weapon/material/twohanded/update_icon() - icon_state = "[base_icon][wielded]" - item_state = icon_state - -/obj/item/weapon/material/twohanded/dropped() - ..() - if(wielded) - spawn(0) - update_held_icon() - -/* - * Fireaxe - */ -/obj/item/weapon/material/twohanded/fireaxe // DEM AXES MAN, marker -Agouri - icon_state = "fireaxe0" - base_icon = "fireaxe" - name = "fire axe" - desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?" - description_info = "This weapon can cleave, striking nearby lesser, hostile enemies close to the primary target. It must be held in both hands to do this." - unwielded_force_divisor = 0.25 - force_divisor = 0.7 // 10/42 with hardness 60 (steel) and 0.25 unwielded divisor - dulled_divisor = 0.75 //Still metal on a stick - sharp = TRUE - edge = TRUE - w_class = ITEMSIZE_LARGE - slot_flags = SLOT_BACK - force_wielded = 30 - attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") - applies_material_colour = 0 - can_cleave = TRUE - drop_sound = 'sound/items/drop/axe.ogg' - pickup_sound = 'sound/items/pickup/axe.ogg' - -/obj/item/weapon/material/twohanded/fireaxe/update_held_icon() - var/mob/living/M = loc - if(istype(M) && !issmall(M) && M.item_is_in_hands(src) && !M.hands_are_full()) - wielded = 1 - pry = 1 - force = force_wielded - name = "[base_name] (wielded)" - update_icon() - else - wielded = 0 - pry = 0 - force = force_unwielded - name = "[base_name]" - update_icon() - ..() - -/obj/item/weapon/material/twohanded/fireaxe/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity) - if(!proximity) return - ..() - if(A && wielded) - if(istype(A,/obj/structure/window)) - var/obj/structure/window/W = A - W.shatter() - else if(istype(A,/obj/structure/grille)) - qdel(A) - else if(istype(A,/obj/effect/plant)) - var/obj/effect/plant/P = A - P.die_off() - -/obj/item/weapon/material/twohanded/fireaxe/scythe - icon_state = "scythe0" - base_icon = "scythe" - name = "scythe" - desc = "A sharp and curved blade on a long fibremetal handle, this tool makes it easy to reap what you sow." - force_divisor = 0.65 - origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 2) - attack_verb = list("chopped", "sliced", "cut", "reaped") - -//spears, bay edition -/obj/item/weapon/material/twohanded/spear - icon_state = "spearglass0" - base_icon = "spearglass" - name = "spear" - desc = "A haphazardly-constructed yet still deadly weapon of ancient design." - force = 10 - w_class = ITEMSIZE_LARGE - slot_flags = SLOT_BACK - force_divisor = 0.5 // 15 when wielded with hardness 30 (glass) - unwielded_force_divisor = 0.375 - thrown_force_divisor = 1.5 // 22.5 when thrown with weight 15 (glass) - throw_speed = 3 - edge = FALSE - sharp = TRUE - hitsound = 'sound/weapons/bladeslice.ogg' - mob_throw_hit_sound = 'sound/weapons/pierce.ogg' - attack_verb = list("attacked", "poked", "jabbed", "torn", "gored") - default_material = "glass" - applies_material_colour = 0 - fragile = 1 //It's a haphazard thing of glass, wire, and steel - reach = 2 // Spears are long. - attackspeed = 14 - -//This is mostly for centaurs. -/obj/item/weapon/material/twohanded/spear/lance - name = "lance" - desc = "End him rightly" - icon = 'icons/obj/weapons_vr.dmi' - icon_state = "lance" - item_state = "lance" - force_divisor = 0.3 - force = 10 - thrown_force_divisor = 1 - default_material = "MAT_STEEL" - fragile = 0 - sharp = TRUE - edge = FALSE - -/obj/item/weapon/material/twohanded/riding_crop - name = "riding crop" - desc = "A rod, a little over a foot long with a widened grip and a thick, leather patch at the end. Used since the dawn of the West to control animals." - force_divisor = 0.05 //Required in order for the X attacks Y message to pop up. - unwielded_force_divisor = 1 // One here, too. - applies_material_colour = 1 - unbreakable = 1 - base_icon = "riding_crop" - icon_state = "riding_crop0" - attack_verb = list("cropped","spanked","swatted","smacked","peppered") - -/obj/item/weapon/material/twohanded/spear/flint +/* Two-handed Weapons + * Contains: + * Twohanded + * Fireaxe + * Double-Bladed Energy Swords + */ + +/*################################################################## +##################### TWO HANDED WEAPONS BE HERE~ -Agouri :3 ######## +####################################################################*/ + +//Rewrote TwoHanded weapons stuff and put it all here. Just copypasta fireaxe to make new ones ~Carn +//This rewrite means we don't have two variables for EVERY item which are used only by a few weapons. +//It also tidies stuff up elsewhere. + +/* + * Twohanded + */ +/obj/item/weapon/material/twohanded + w_class = ITEMSIZE_LARGE + var/wielded = 0 + var/force_wielded = 0 + var/force_unwielded + var/wieldsound = null + var/unwieldsound = null + var/base_icon + var/base_name + var/unwielded_force_divisor = 0.25 + hitsound = "swing_hit" + drop_sound = 'sound/items/drop/sword.ogg' + pickup_sound = 'sound/items/pickup/sword.ogg' + +/obj/item/weapon/material/twohanded/update_held_icon() + var/mob/living/M = loc + if(istype(M) && M.can_wield_item(src) && is_held_twohanded(M)) + wielded = 1 + force = force_wielded + name = "[base_name] (wielded)" + update_icon() + else + wielded = 0 + force = force_unwielded + name = "[base_name]" + update_icon() + ..() + +/obj/item/weapon/material/twohanded/update_force() + base_name = name + if(sharp || edge) + force_wielded = material.get_edge_damage() + else + force_wielded = material.get_blunt_damage() + force_wielded = round(force_wielded*force_divisor) + force_unwielded = round(force_wielded*unwielded_force_divisor) + force = force_unwielded + throwforce = round(force*thrown_force_divisor) + //to_world("[src] has unwielded force [force_unwielded], wielded force [force_wielded] and throwforce [throwforce] when made from default material [material.name]") + +/obj/item/weapon/material/twohanded/New() + ..() + update_icon() + +//Allow a small chance of parrying melee attacks when wielded - maybe generalize this to other weapons someday +/obj/item/weapon/material/twohanded/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(wielded && default_parry_check(user, attacker, damage_source) && prob(15)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) + return 1 + return 0 + +/obj/item/weapon/material/twohanded/update_icon() + icon_state = "[base_icon][wielded]" + item_state = icon_state + +/obj/item/weapon/material/twohanded/dropped() + ..() + if(wielded) + spawn(0) + update_held_icon() + +/* + * Fireaxe + */ +/obj/item/weapon/material/twohanded/fireaxe // DEM AXES MAN, marker -Agouri + icon_state = "fireaxe0" + base_icon = "fireaxe" + name = "fire axe" + desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?" + description_info = "This weapon can cleave, striking nearby lesser, hostile enemies close to the primary target. It must be held in both hands to do this." + unwielded_force_divisor = 0.25 + force_divisor = 0.7 // 10/42 with hardness 60 (steel) and 0.25 unwielded divisor + dulled_divisor = 0.75 //Still metal on a stick + sharp = TRUE + edge = TRUE + w_class = ITEMSIZE_LARGE + slot_flags = SLOT_BACK + force_wielded = 30 + attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") + applies_material_colour = 0 + can_cleave = TRUE + drop_sound = 'sound/items/drop/axe.ogg' + pickup_sound = 'sound/items/pickup/axe.ogg' + +/obj/item/weapon/material/twohanded/fireaxe/update_held_icon() + var/mob/living/M = loc + if(istype(M) && !issmall(M) && M.item_is_in_hands(src) && !M.hands_are_full()) + wielded = 1 + pry = 1 + force = force_wielded + name = "[base_name] (wielded)" + update_icon() + else + wielded = 0 + pry = 0 + force = force_unwielded + name = "[base_name]" + update_icon() + ..() + +/obj/item/weapon/material/twohanded/fireaxe/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity) + if(!proximity) return + ..() + if(A && wielded) + if(istype(A,/obj/structure/window)) + var/obj/structure/window/W = A + W.shatter() + else if(istype(A,/obj/structure/grille)) + qdel(A) + else if(istype(A,/obj/effect/plant)) + var/obj/effect/plant/P = A + P.die_off() + +/obj/item/weapon/material/twohanded/fireaxe/scythe + icon_state = "scythe0" + base_icon = "scythe" + name = "scythe" + desc = "A sharp and curved blade on a long fibremetal handle, this tool makes it easy to reap what you sow." + force_divisor = 0.65 + origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 2) + attack_verb = list("chopped", "sliced", "cut", "reaped") + +//spears, bay edition +/obj/item/weapon/material/twohanded/spear + icon_state = "spearglass0" + base_icon = "spearglass" + name = "spear" + desc = "A haphazardly-constructed yet still deadly weapon of ancient design." + force = 10 + w_class = ITEMSIZE_LARGE + slot_flags = SLOT_BACK + force_divisor = 0.5 // 15 when wielded with hardness 30 (glass) + unwielded_force_divisor = 0.375 + thrown_force_divisor = 1.5 // 22.5 when thrown with weight 15 (glass) + throw_speed = 3 + edge = FALSE + sharp = TRUE + hitsound = 'sound/weapons/bladeslice.ogg' + mob_throw_hit_sound = 'sound/weapons/pierce.ogg' + attack_verb = list("attacked", "poked", "jabbed", "torn", "gored") + default_material = "glass" + applies_material_colour = 0 + fragile = 1 //It's a haphazard thing of glass, wire, and steel + reach = 2 // Spears are long. + attackspeed = 14 + +//This is mostly for centaurs. +/obj/item/weapon/material/twohanded/spear/lance + name = "lance" + desc = "End him rightly" + icon = 'icons/obj/weapons_vr.dmi' + icon_state = "lance" + item_state = "lance" + force_divisor = 0.3 + force = 10 + thrown_force_divisor = 1 + default_material = "MAT_STEEL" + fragile = 0 + sharp = TRUE + edge = FALSE + +/obj/item/weapon/material/twohanded/riding_crop + name = "riding crop" + desc = "A rod, a little over a foot long with a widened grip and a thick, leather patch at the end. Used since the dawn of the West to control animals." + force_divisor = 0.05 //Required in order for the X attacks Y message to pop up. + unwielded_force_divisor = 1 // One here, too. + applies_material_colour = 1 + unbreakable = 1 + base_icon = "riding_crop" + icon_state = "riding_crop0" + attack_verb = list("cropped","spanked","swatted","smacked","peppered") + +/obj/item/weapon/material/twohanded/spear/flint default_material = MAT_FLINT \ No newline at end of file diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index 7eaf6a81747..1db3d1bffa7 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -1,550 +1,550 @@ -/obj/item/weapon/melee/energy - var/active = 0 - var/active_force - var/active_throwforce - var/active_armourpen - var/active_w_class - var/active_embed_chance = 0 //In the off chance one of these is supposed to embed, you can just tweak this var - sharp = FALSE - edge = FALSE - armor_penetration = 0 - flags = NOCONDUCT | NOBLOODY - var/lrange = 2 - var/lpower = 2 - var/lcolor = "#0099FF" - var/colorable = FALSE - var/rainbow = FALSE - // If it uses energy. - var/use_cell = FALSE - var/hitcost = 120 - var/obj/item/weapon/cell/bcell = null - var/cell_type = /obj/item/weapon/cell/device - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', - ) - -/obj/item/weapon/melee/energy/sword/green/New() - colorable = FALSE - lcolor = "#008000" - -/obj/item/weapon/melee/energy/sword/red/New() - colorable = FALSE - lcolor = "#FF0000" - -/obj/item/weapon/melee/energy/sword/blue/New() - colorable = FALSE - lcolor = "#0000FF" - -/obj/item/weapon/melee/energy/sword/purple/New() - colorable = FALSE - lcolor = "#800080" - -/obj/item/weapon/melee/energy/sword/white/New() - colorable = FALSE - lcolor = "#FFFFFF" - -/obj/item/weapon/melee/energy/proc/activate(mob/living/user) - if(active) - return - active = 1 - if(rainbow) - item_state = "[icon_state]_blade_rainbow" - else - item_state = "[icon_state]_blade" - embed_chance = active_embed_chance - force = active_force - throwforce = active_throwforce - armor_penetration = active_armourpen - sharp = TRUE - edge = TRUE - w_class = active_w_class - playsound(src, 'sound/weapons/saberon.ogg', 50, 1) - update_icon() - set_light(lrange, lpower, lcolor) - -/obj/item/weapon/melee/energy/proc/deactivate(mob/living/user) - if(!active) - return - playsound(src, 'sound/weapons/saberoff.ogg', 50, 1) - item_state = "[icon_state]" - active = 0 - embed_chance = initial(embed_chance) - force = initial(force) - throwforce = initial(throwforce) - armor_penetration = initial(armor_penetration) - sharp = initial(sharp) - edge = initial(edge) - w_class = initial(w_class) - update_icon() - set_light(0,0) - -/obj/item/weapon/melee/energy/proc/use_charge(var/cost) - if(active) - if(bcell) - if(bcell.checked_use(cost)) - return 1 - else - return 0 - return null - -/obj/item/weapon/melee/energy/examine(mob/user) - . = ..() - if(use_cell && Adjacent(user)) - if(bcell) - . += "The blade is [round(bcell.percent())]% charged." - else - . += "The blade does not have a power source installed." - -/obj/item/weapon/melee/energy/attack_self(mob/living/user as mob) - if(use_cell) - if((!bcell || bcell.charge < hitcost) && !active) - to_chat(user, "\The [src] does not seem to have power.") - return - - var/datum/gender/TU = gender_datums[user.get_visible_gender()] - if (active) - if ((CLUMSY in user.mutations) && prob(50)) - user.visible_message("\The [user] accidentally cuts [TU.himself] with \the [src].",\ - "You accidentally cut yourself with \the [src].") - user.take_organ_damage(5,5) - deactivate(user) - else - activate(user) - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - add_fingerprint(user) - return - -/obj/item/weapon/melee/energy/attack(mob/M, mob/user) - if(active && use_cell) - if(!use_charge(hitcost)) - deactivate(user) - visible_message("\The [src]'s blade flickers, before deactivating.") - return ..() - -/obj/item/weapon/melee/energy/attackby(obj/item/weapon/W, mob/user) - if(istype(W, /obj/item/device/multitool) && colorable && !active) - if(!rainbow) - rainbow = TRUE - else - rainbow = FALSE - to_chat(user, "You manipulate the color controller in [src].") - update_icon() - if(use_cell) - if(istype(W, cell_type)) - if(!bcell) - user.drop_item() - W.loc = src - bcell = W - to_chat(user, "You install a cell in [src].") - update_icon() - else - to_chat(user, "[src] already has a cell.") - else if(W.has_tool_quality(TOOL_SCREWDRIVER) && bcell) - bcell.update_icon() - bcell.forceMove(get_turf(loc)) - bcell = null - to_chat(user, "You remove the cell from \the [src].") - deactivate() - update_icon() - return - return ..() - -/obj/item/weapon/melee/energy/get_cell() - return bcell - -/obj/item/weapon/melee/energy/update_icon() - . = ..() - var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") - blade_overlay.color = lcolor - color = lcolor - if(rainbow) - blade_overlay = mutable_appearance(icon, "[icon_state]_blade_rainbow") - blade_overlay.color = "FFFFFF" - color = "FFFFFF" - cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other - if(active) - add_overlay(blade_overlay) - if(istype(usr,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = usr - H.update_inv_l_hand() - H.update_inv_r_hand() - - - - -/obj/item/weapon/melee/energy/AltClick(mob/living/user) - if(!colorable) //checks if is not colorable - return - if(!in_range(src, user)) //Basic checks to prevent abuse - return - if(user.incapacitated() || !istype(user)) - to_chat(user, "You can't do that right now!") - return - - if(tgui_alert(usr, "Are you sure you want to recolor your blade?", "Confirm Recolor", list("Yes", "No")) == "Yes") - var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null - if(energy_color_input) - lcolor = sanitize_hexcolor(energy_color_input) - update_icon() - -/obj/item/weapon/melee/energy/examine(mob/user) - . = ..() - if(colorable) - . += "Alt-click to recolor it." - -/* - * Energy Axe - */ -/obj/item/weapon/melee/energy/axe - name = "energy axe" - desc = "An energised battle axe." - icon_state = "eaxe" - item_state = "eaxe" - colorable = FALSE - lcolor = null - //active_force = 150 //holy... - active_force = 60 - active_armourpen = 65 - active_throwforce = 35 - active_w_class = ITEMSIZE_HUGE - //force = 40 - //throwforce = 25 - force = 20 - armor_penetration = 20 - throwforce = 10 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_MAGNET = 3, TECH_COMBAT = 4) - attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") - sharp = TRUE - edge = TRUE - can_cleave = TRUE - -/obj/item/weapon/melee/energy/axe/activate(mob/living/user) - ..() - damtype = SEARING - to_chat(user, "\The [src] is now energised.") - -/obj/item/weapon/melee/energy/axe/deactivate(mob/living/user) - ..() - damtype = BRUTE - to_chat(user, "\The [src] is de-energised. It's just a regular axe now.") - -/obj/item/weapon/melee/energy/axe/charge - name = "charge axe" - desc = "An energised axe." - active_force = 35 - active_throwforce = 20 - active_armourpen = 30 - force = 15 - - use_cell = TRUE - hitcost = 120 - -/obj/item/weapon/melee/energy/axe/charge/loaded/New() - ..() - bcell = new/obj/item/weapon/cell/device/weapon(src) - -/* - * Energy Sword - */ -/obj/item/weapon/melee/energy/sword - color - name = "energy sword" - desc = "May the force be within you." - icon_state = "esword" - item_state = "esword" - active_force = 30 - active_armourpen = 50 - active_throwforce = 20 - active_w_class = ITEMSIZE_LARGE - force = 3 - throwforce = 5 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_SMALL - flags = NOBLOODY - origin_tech = list(TECH_MAGNET = 3, TECH_ILLEGAL = 4) - colorable = TRUE - drop_sound = 'sound/items/drop/sword.ogg' - pickup_sound = 'sound/items/pickup/sword.ogg' - - - projectile_parry_chance = 65 - -/obj/item/weapon/melee/energy/sword/dropped(var/mob/user) - ..() - if(!istype(loc,/mob)) - deactivate(user) - - -/obj/item/weapon/melee/energy/sword/activate(mob/living/user) - if(!active) - to_chat(user, "\The [src] is now energised.") - - ..() - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - - -/obj/item/weapon/melee/energy/sword/deactivate(mob/living/user) - if(active) - to_chat(user, "\The [src] deactivates!") - ..() - attack_verb = list() - -/obj/item/weapon/melee/energy/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(active && default_parry_check(user, attacker, damage_source) && prob(60)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - if(active && unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) - user.visible_message("\The [user] deflects [attack_text] with \the [src]!") - - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - - return 0 - -/obj/item/weapon/melee/energy/sword/unique_parry_check(mob/user, mob/attacker, atom/damage_source) - if(user.incapacitated() || !istype(damage_source, /obj/item/projectile/)) - return 0 - - var/bad_arc = reverse_direction(user.dir) - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 - -/obj/item/weapon/melee/energy/sword/pirate - name = "energy cutlass" - desc = "Arrrr matey." - icon_state = "cutlass" - item_state = "cutlass" - colorable = TRUE - - -/* - *Ionic Rapier - */ - -/obj/item/weapon/melee/energy/sword/ionic_rapier - name = "ionic rapier" - desc = "Designed specifically for disrupting electronics at close range, it is extremely deadly against synthetics, but almost harmless to pure organic targets." - description_info = "This is a dangerous melee weapon that will deliver a moderately powerful electromagnetic pulse to whatever it strikes. \ - Striking a lesser robotic entity will compel it to attack you, as well. It also does extra burn damage to robotic entities, but it does \ - very little damage to purely organic targets." - icon_state = "ionrapier" - item_state = "ionrapier" - active_force = 5 - active_armourpen = 80 - active_throwforce = 3 - active_embed_chance = 0 - sharp = TRUE - edge = TRUE - armor_penetration = 0 - flags = NOBLOODY - lrange = 2 - lpower = 2 - lcolor = "#0000FF" - projectile_parry_chance = 30 // It's not specifically designed for cutting and slashing, but it can still, maybe, save your life. - -/obj/item/weapon/melee/energy/sword/ionic_rapier/afterattack(var/atom/movable/AM, var/mob/living/user, var/proximity) - if(istype(AM, /obj) && proximity && active) - // EMP stuff. - var/obj/O = AM - O.emp_act(3) // A weaker severity is used because this has infinite uses. - playsound(O, 'sound/effects/EMPulse.ogg', 100, 1) - user.setClickCooldown(user.get_attack_speed(src)) // A lot of objects don't set click delay. - return ..() - -/obj/item/weapon/melee/energy/sword/ionic_rapier/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) - . = ..() - if(target.isSynthetic() && active) - // Do some extra damage. Not a whole lot more since emp_act() is pretty nasty on FBPs already. - target.emp_act(3) // A weaker severity is used because this has infinite uses. - playsound(target, 'sound/effects/EMPulse.ogg', 100, 1) - target.adjustFireLoss(force * 3) // 15 Burn, for 20 total. - playsound(target, 'sound/weapons/blade1.ogg', 100, 1) - - // Make lesser robots really mad at us. - if(target.mob_class & MOB_CLASS_SYNTHETIC) - if(target.has_AI()) - target.taunt(user) - target.adjustFireLoss(force * 6) // 30 Burn, for 50 total. - -/obj/item/weapon/melee/energy/sword/ionic_rapier/lance - name = "zero-point lance" - desc = "Designed specifically for disrupting electronics at relatively close range, however it is still capable of dealing some damage to living beings." - active_force = 20 - active_armourpen = 15 - reach = 2 - -/* - * Charge blade. Uses a cell, and costs energy per strike. - */ - -/obj/item/weapon/melee/energy/sword/charge - name = "charge sword" - desc = "A small, handheld device which emits a high-energy 'blade'." - origin_tech = list(TECH_COMBAT = 5, TECH_MAGNET = 3, TECH_ILLEGAL = 4) - active_force = 25 - active_armourpen = 25 - projectile_parry_chance = 40 - colorable = TRUE - - hitcost = 75 - -/obj/item/weapon/melee/energy/sword/charge/loaded/New() - ..() - bcell = new/obj/item/weapon/cell/device/weapon(src) - -//Energy Blade (ninja uses this) - -//Can't be activated or deactivated, so no reason to be a subtype of energy -/obj/item/weapon/melee/energy/blade - name = "energy blade" - desc = "A concentrated beam of energy in the shape of a blade. Very stylish... and lethal." - icon_state = "blade" - item_state = "blade" - force = 40 //Normal attacks deal very high damage - about the same as wielded fire axe - armor_penetration = 100 - sharp = TRUE - edge = TRUE - anchored = TRUE // Never spawned outside of inventory, should be fine. - throwforce = 1 //Throwing or dropping the item deletes it. - throw_speed = 1 - throw_range = 1 - w_class = ITEMSIZE_LARGE//So you can't hide it in your pocket or some such. - flags = NOBLOODY - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - var/mob/living/creator - var/datum/effect/effect/system/spark_spread/spark_system - projectile_parry_chance = 60 - lcolor = "#00FF00" - -/obj/item/weapon/melee/energy/blade/New() - - spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - START_PROCESSING(SSobj, src) - set_light(lrange, lpower, lcolor) - -/obj/item/weapon/melee/energy/blade/Destroy() - STOP_PROCESSING(SSobj, src) - ..() - -/obj/item/weapon/melee/energy/blade/attack_self(mob/user as mob) - user.drop_from_inventory(src) - spawn(1) if(src) qdel(src) - -/obj/item/weapon/melee/energy/blade/dropped() - spawn(1) if(src) qdel(src) - -/obj/item/weapon/melee/energy/blade/process() - if(!creator || loc != creator || !creator.item_is_in_hands(src)) - // Tidy up a bit. - if(istype(loc,/mob/living)) - var/mob/living/carbon/human/host = loc - if(istype(host)) - for(var/obj/item/organ/external/organ in host.organs) - for(var/obj/item/O in organ.implants) - if(O == src) - organ.implants -= src - host.pinned -= src - host.embedded -= src - host.drop_from_inventory(src) - spawn(1) if(src) qdel(src) - -/obj/item/weapon/melee/energy/blade/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(60)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - if(unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) - user.visible_message("\The [user] deflects [attack_text] with \the [src]!") - - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - - return 0 - -/obj/item/weapon/melee/energy/blade/unique_parry_check(mob/user, mob/attacker, atom/damage_source) - - if(user.incapacitated() || !istype(damage_source, /obj/item/projectile/)) - return 0 - - var/bad_arc = reverse_direction(user.dir) - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 - -//Energy Spear - -/obj/item/weapon/melee/energy/spear - name = "energy spear" - desc = "Concentrated energy forming a sharp tip at the end of a long rod." - icon_state = "espear" - armor_penetration = 0 - sharp = TRUE - edge = TRUE - force = 5 - throwforce = 10 - throw_speed = 7 - throw_range = 11 - reach = 2 - w_class = ITEMSIZE_LARGE - active_force = 25 - active_armourpen = 75 - active_throwforce = 30 - active_w_class = ITEMSIZE_HUGE - colorable = TRUE - - - lcolor = "#800080" - -/obj/item/weapon/melee/energy/spear/activate(mob/living/user) - if(!active) - to_chat(user, "\The [src] is now energised.") - ..() - attack_verb = list("jabbed", "stabbed", "impaled") - - -/obj/item/weapon/melee/energy/spear/deactivate(mob/living/user) - if(active) - to_chat(user, "\The [src] deactivates!") - ..() - attack_verb = list("whacked", "beat", "slapped", "thonked") - -/obj/item/weapon/melee/energy/spear/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(active && default_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - return 0 +/obj/item/weapon/melee/energy + var/active = 0 + var/active_force + var/active_throwforce + var/active_armourpen + var/active_w_class + var/active_embed_chance = 0 //In the off chance one of these is supposed to embed, you can just tweak this var + sharp = FALSE + edge = FALSE + armor_penetration = 0 + flags = NOCONDUCT | NOBLOODY + var/lrange = 2 + var/lpower = 2 + var/lcolor = "#0099FF" + var/colorable = FALSE + var/rainbow = FALSE + // If it uses energy. + var/use_cell = FALSE + var/hitcost = 120 + var/obj/item/weapon/cell/bcell = null + var/cell_type = /obj/item/weapon/cell/device + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + +/obj/item/weapon/melee/energy/sword/green/New() + colorable = FALSE + lcolor = "#008000" + +/obj/item/weapon/melee/energy/sword/red/New() + colorable = FALSE + lcolor = "#FF0000" + +/obj/item/weapon/melee/energy/sword/blue/New() + colorable = FALSE + lcolor = "#0000FF" + +/obj/item/weapon/melee/energy/sword/purple/New() + colorable = FALSE + lcolor = "#800080" + +/obj/item/weapon/melee/energy/sword/white/New() + colorable = FALSE + lcolor = "#FFFFFF" + +/obj/item/weapon/melee/energy/proc/activate(mob/living/user) + if(active) + return + active = 1 + if(rainbow) + item_state = "[icon_state]_blade_rainbow" + else + item_state = "[icon_state]_blade" + embed_chance = active_embed_chance + force = active_force + throwforce = active_throwforce + armor_penetration = active_armourpen + sharp = TRUE + edge = TRUE + w_class = active_w_class + playsound(src, 'sound/weapons/saberon.ogg', 50, 1) + update_icon() + set_light(lrange, lpower, lcolor) + +/obj/item/weapon/melee/energy/proc/deactivate(mob/living/user) + if(!active) + return + playsound(src, 'sound/weapons/saberoff.ogg', 50, 1) + item_state = "[icon_state]" + active = 0 + embed_chance = initial(embed_chance) + force = initial(force) + throwforce = initial(throwforce) + armor_penetration = initial(armor_penetration) + sharp = initial(sharp) + edge = initial(edge) + w_class = initial(w_class) + update_icon() + set_light(0,0) + +/obj/item/weapon/melee/energy/proc/use_charge(var/cost) + if(active) + if(bcell) + if(bcell.checked_use(cost)) + return 1 + else + return 0 + return null + +/obj/item/weapon/melee/energy/examine(mob/user) + . = ..() + if(use_cell && Adjacent(user)) + if(bcell) + . += "The blade is [round(bcell.percent())]% charged." + else + . += "The blade does not have a power source installed." + +/obj/item/weapon/melee/energy/attack_self(mob/living/user as mob) + if(use_cell) + if((!bcell || bcell.charge < hitcost) && !active) + to_chat(user, "\The [src] does not seem to have power.") + return + + var/datum/gender/TU = gender_datums[user.get_visible_gender()] + if (active) + if ((CLUMSY in user.mutations) && prob(50)) + user.visible_message("\The [user] accidentally cuts [TU.himself] with \the [src].",\ + "You accidentally cut yourself with \the [src].") + user.take_organ_damage(5,5) + deactivate(user) + else + activate(user) + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + + add_fingerprint(user) + return + +/obj/item/weapon/melee/energy/attack(mob/M, mob/user) + if(active && use_cell) + if(!use_charge(hitcost)) + deactivate(user) + visible_message("\The [src]'s blade flickers, before deactivating.") + return ..() + +/obj/item/weapon/melee/energy/attackby(obj/item/weapon/W, mob/user) + if(istype(W, /obj/item/device/multitool) && colorable && !active) + if(!rainbow) + rainbow = TRUE + else + rainbow = FALSE + to_chat(user, "You manipulate the color controller in [src].") + update_icon() + if(use_cell) + if(istype(W, cell_type)) + if(!bcell) + user.drop_item() + W.loc = src + bcell = W + to_chat(user, "You install a cell in [src].") + update_icon() + else + to_chat(user, "[src] already has a cell.") + else if(W.has_tool_quality(TOOL_SCREWDRIVER) && bcell) + bcell.update_icon() + bcell.forceMove(get_turf(loc)) + bcell = null + to_chat(user, "You remove the cell from \the [src].") + deactivate() + update_icon() + return + return ..() + +/obj/item/weapon/melee/energy/get_cell() + return bcell + +/obj/item/weapon/melee/energy/update_icon() + . = ..() + var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") + blade_overlay.color = lcolor + color = lcolor + if(rainbow) + blade_overlay = mutable_appearance(icon, "[icon_state]_blade_rainbow") + blade_overlay.color = "FFFFFF" + color = "FFFFFF" + cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other + if(active) + add_overlay(blade_overlay) + if(istype(usr,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = usr + H.update_inv_l_hand() + H.update_inv_r_hand() + + + + +/obj/item/weapon/melee/energy/AltClick(mob/living/user) + if(!colorable) //checks if is not colorable + return + if(!in_range(src, user)) //Basic checks to prevent abuse + return + if(user.incapacitated() || !istype(user)) + to_chat(user, "You can't do that right now!") + return + + if(tgui_alert(usr, "Are you sure you want to recolor your blade?", "Confirm Recolor", list("Yes", "No")) == "Yes") + var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null + if(energy_color_input) + lcolor = sanitize_hexcolor(energy_color_input) + update_icon() + +/obj/item/weapon/melee/energy/examine(mob/user) + . = ..() + if(colorable) + . += "Alt-click to recolor it." + +/* + * Energy Axe + */ +/obj/item/weapon/melee/energy/axe + name = "energy axe" + desc = "An energised battle axe." + icon_state = "eaxe" + item_state = "eaxe" + colorable = FALSE + lcolor = null + //active_force = 150 //holy... + active_force = 60 + active_armourpen = 65 + active_throwforce = 35 + active_w_class = ITEMSIZE_HUGE + //force = 40 + //throwforce = 25 + force = 20 + armor_penetration = 20 + throwforce = 10 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_MAGNET = 3, TECH_COMBAT = 4) + attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") + sharp = TRUE + edge = TRUE + can_cleave = TRUE + +/obj/item/weapon/melee/energy/axe/activate(mob/living/user) + ..() + damtype = SEARING + to_chat(user, "\The [src] is now energised.") + +/obj/item/weapon/melee/energy/axe/deactivate(mob/living/user) + ..() + damtype = BRUTE + to_chat(user, "\The [src] is de-energised. It's just a regular axe now.") + +/obj/item/weapon/melee/energy/axe/charge + name = "charge axe" + desc = "An energised axe." + active_force = 35 + active_throwforce = 20 + active_armourpen = 30 + force = 15 + + use_cell = TRUE + hitcost = 120 + +/obj/item/weapon/melee/energy/axe/charge/loaded/New() + ..() + bcell = new/obj/item/weapon/cell/device/weapon(src) + +/* + * Energy Sword + */ +/obj/item/weapon/melee/energy/sword + color + name = "energy sword" + desc = "May the force be within you." + icon_state = "esword" + item_state = "esword" + active_force = 30 + active_armourpen = 50 + active_throwforce = 20 + active_w_class = ITEMSIZE_LARGE + force = 3 + throwforce = 5 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_SMALL + flags = NOBLOODY + origin_tech = list(TECH_MAGNET = 3, TECH_ILLEGAL = 4) + colorable = TRUE + drop_sound = 'sound/items/drop/sword.ogg' + pickup_sound = 'sound/items/pickup/sword.ogg' + + + projectile_parry_chance = 65 + +/obj/item/weapon/melee/energy/sword/dropped(var/mob/user) + ..() + if(!istype(loc,/mob)) + deactivate(user) + + +/obj/item/weapon/melee/energy/sword/activate(mob/living/user) + if(!active) + to_chat(user, "\The [src] is now energised.") + + ..() + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + + +/obj/item/weapon/melee/energy/sword/deactivate(mob/living/user) + if(active) + to_chat(user, "\The [src] deactivates!") + ..() + attack_verb = list() + +/obj/item/weapon/melee/energy/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(active && default_parry_check(user, attacker, damage_source) && prob(60)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + return 1 + if(active && unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) + user.visible_message("\The [user] deflects [attack_text] with \the [src]!") + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + return 1 + + return 0 + +/obj/item/weapon/melee/energy/sword/unique_parry_check(mob/user, mob/attacker, atom/damage_source) + if(user.incapacitated() || !istype(damage_source, /obj/item/projectile/)) + return 0 + + var/bad_arc = reverse_direction(user.dir) + if(!check_shield_arc(user, bad_arc, damage_source, attacker)) + return 0 + + return 1 + +/obj/item/weapon/melee/energy/sword/pirate + name = "energy cutlass" + desc = "Arrrr matey." + icon_state = "cutlass" + item_state = "cutlass" + colorable = TRUE + + +/* + *Ionic Rapier + */ + +/obj/item/weapon/melee/energy/sword/ionic_rapier + name = "ionic rapier" + desc = "Designed specifically for disrupting electronics at close range, it is extremely deadly against synthetics, but almost harmless to pure organic targets." + description_info = "This is a dangerous melee weapon that will deliver a moderately powerful electromagnetic pulse to whatever it strikes. \ + Striking a lesser robotic entity will compel it to attack you, as well. It also does extra burn damage to robotic entities, but it does \ + very little damage to purely organic targets." + icon_state = "ionrapier" + item_state = "ionrapier" + active_force = 5 + active_armourpen = 80 + active_throwforce = 3 + active_embed_chance = 0 + sharp = TRUE + edge = TRUE + armor_penetration = 0 + flags = NOBLOODY + lrange = 2 + lpower = 2 + lcolor = "#0000FF" + projectile_parry_chance = 30 // It's not specifically designed for cutting and slashing, but it can still, maybe, save your life. + +/obj/item/weapon/melee/energy/sword/ionic_rapier/afterattack(var/atom/movable/AM, var/mob/living/user, var/proximity) + if(istype(AM, /obj) && proximity && active) + // EMP stuff. + var/obj/O = AM + O.emp_act(3) // A weaker severity is used because this has infinite uses. + playsound(O, 'sound/effects/EMPulse.ogg', 100, 1) + user.setClickCooldown(user.get_attack_speed(src)) // A lot of objects don't set click delay. + return ..() + +/obj/item/weapon/melee/energy/sword/ionic_rapier/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) + . = ..() + if(target.isSynthetic() && active) + // Do some extra damage. Not a whole lot more since emp_act() is pretty nasty on FBPs already. + target.emp_act(3) // A weaker severity is used because this has infinite uses. + playsound(target, 'sound/effects/EMPulse.ogg', 100, 1) + target.adjustFireLoss(force * 3) // 15 Burn, for 20 total. + playsound(target, 'sound/weapons/blade1.ogg', 100, 1) + + // Make lesser robots really mad at us. + if(target.mob_class & MOB_CLASS_SYNTHETIC) + if(target.has_AI()) + target.taunt(user) + target.adjustFireLoss(force * 6) // 30 Burn, for 50 total. + +/obj/item/weapon/melee/energy/sword/ionic_rapier/lance + name = "zero-point lance" + desc = "Designed specifically for disrupting electronics at relatively close range, however it is still capable of dealing some damage to living beings." + active_force = 20 + active_armourpen = 15 + reach = 2 + +/* + * Charge blade. Uses a cell, and costs energy per strike. + */ + +/obj/item/weapon/melee/energy/sword/charge + name = "charge sword" + desc = "A small, handheld device which emits a high-energy 'blade'." + origin_tech = list(TECH_COMBAT = 5, TECH_MAGNET = 3, TECH_ILLEGAL = 4) + active_force = 25 + active_armourpen = 25 + projectile_parry_chance = 40 + colorable = TRUE + + hitcost = 75 + +/obj/item/weapon/melee/energy/sword/charge/loaded/New() + ..() + bcell = new/obj/item/weapon/cell/device/weapon(src) + +//Energy Blade (ninja uses this) + +//Can't be activated or deactivated, so no reason to be a subtype of energy +/obj/item/weapon/melee/energy/blade + name = "energy blade" + desc = "A concentrated beam of energy in the shape of a blade. Very stylish... and lethal." + icon_state = "blade" + item_state = "blade" + force = 40 //Normal attacks deal very high damage - about the same as wielded fire axe + armor_penetration = 100 + sharp = TRUE + edge = TRUE + anchored = TRUE // Never spawned outside of inventory, should be fine. + throwforce = 1 //Throwing or dropping the item deletes it. + throw_speed = 1 + throw_range = 1 + w_class = ITEMSIZE_LARGE//So you can't hide it in your pocket or some such. + flags = NOBLOODY + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + var/mob/living/creator + var/datum/effect/effect/system/spark_spread/spark_system + projectile_parry_chance = 60 + lcolor = "#00FF00" + +/obj/item/weapon/melee/energy/blade/New() + + spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + START_PROCESSING(SSobj, src) + set_light(lrange, lpower, lcolor) + +/obj/item/weapon/melee/energy/blade/Destroy() + STOP_PROCESSING(SSobj, src) + ..() + +/obj/item/weapon/melee/energy/blade/attack_self(mob/user as mob) + user.drop_from_inventory(src) + spawn(1) if(src) qdel(src) + +/obj/item/weapon/melee/energy/blade/dropped() + spawn(1) if(src) qdel(src) + +/obj/item/weapon/melee/energy/blade/process() + if(!creator || loc != creator || !creator.item_is_in_hands(src)) + // Tidy up a bit. + if(istype(loc,/mob/living)) + var/mob/living/carbon/human/host = loc + if(istype(host)) + for(var/obj/item/organ/external/organ in host.organs) + for(var/obj/item/O in organ.implants) + if(O == src) + organ.implants -= src + host.pinned -= src + host.embedded -= src + host.drop_from_inventory(src) + spawn(1) if(src) qdel(src) + +/obj/item/weapon/melee/energy/blade/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(default_parry_check(user, attacker, damage_source) && prob(60)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + return 1 + if(unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) + user.visible_message("\The [user] deflects [attack_text] with \the [src]!") + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + return 1 + + return 0 + +/obj/item/weapon/melee/energy/blade/unique_parry_check(mob/user, mob/attacker, atom/damage_source) + + if(user.incapacitated() || !istype(damage_source, /obj/item/projectile/)) + return 0 + + var/bad_arc = reverse_direction(user.dir) + if(!check_shield_arc(user, bad_arc, damage_source, attacker)) + return 0 + + return 1 + +//Energy Spear + +/obj/item/weapon/melee/energy/spear + name = "energy spear" + desc = "Concentrated energy forming a sharp tip at the end of a long rod." + icon_state = "espear" + armor_penetration = 0 + sharp = TRUE + edge = TRUE + force = 5 + throwforce = 10 + throw_speed = 7 + throw_range = 11 + reach = 2 + w_class = ITEMSIZE_LARGE + active_force = 25 + active_armourpen = 75 + active_throwforce = 30 + active_w_class = ITEMSIZE_HUGE + colorable = TRUE + + + lcolor = "#800080" + +/obj/item/weapon/melee/energy/spear/activate(mob/living/user) + if(!active) + to_chat(user, "\The [src] is now energised.") + ..() + attack_verb = list("jabbed", "stabbed", "impaled") + + +/obj/item/weapon/melee/energy/spear/deactivate(mob/living/user) + if(active) + to_chat(user, "\The [src] deactivates!") + ..() + attack_verb = list("whacked", "beat", "slapped", "thonked") + +/obj/item/weapon/melee/energy/spear/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(active && default_parry_check(user, attacker, damage_source) && prob(50)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + return 1 + return 0 diff --git a/code/game/objects/items/weapons/melee/misc.dm b/code/game/objects/items/weapons/melee/misc.dm index 5930c1ad7ed..9598987006a 100644 --- a/code/game/objects/items/weapons/melee/misc.dm +++ b/code/game/objects/items/weapons/melee/misc.dm @@ -1,98 +1,98 @@ -/obj/item/weapon/melee/chainofcommand - name = "chain of command" - desc = "A tool used by great men to placate the frothing masses." - icon_state = "chain" - slot_flags = SLOT_BELT - force = 10 - throwforce = 7 - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_COMBAT = 4) - attack_verb = list("flogged", "whipped", "lashed", "disciplined") - hitsound = 'sound/weapons/whip.ogg' - reach = 2 - -/obj/item/weapon/melee/chainofcommand/curator_whip - name = "leather whip" - desc = "A fine weapon for some treasure hunting." - icon_state = "curator_whip" - force = 5 - throwforce = 5 - origin_tech = list(TECH_COMBAT = 2) - -/obj/item/weapon/melee/chainofcommand/curator_whip/toy - name = "toy whip" - desc = "A fake whip. Perfect for fake treasure hunting" - force = 2 - throwforce = 2 - -/obj/item/weapon/melee/umbrella - name = "umbrella" - desc = "To keep the rain off you. Use with caution on windy days." - icon = 'icons/obj/items.dmi' - icon_state = "umbrella_closed" - addblends = "umbrella_closed_a" - slot_flags = SLOT_BELT - force = 5 - throwforce = 5 - w_class = ITEMSIZE_NORMAL - var/open = FALSE - -/obj/item/weapon/melee/umbrella/New() - ..() - update_icon() - -/obj/item/weapon/melee/umbrella/attack_self() - src.toggle_umbrella() - -/obj/item/weapon/melee/umbrella/proc/toggle_umbrella() - open = !open - icon_state = "umbrella_[open ? "open" : "closed"]" - addblends = icon_state + "_a" - item_state = icon_state - update_icon() - if(ishuman(src.loc)) - var/mob/living/carbon/human/H = src.loc - H.update_inv_l_hand(0) - H.update_inv_r_hand() - -// Randomizes color -/obj/item/weapon/melee/umbrella/random/New() - color = get_random_colour() - ..() - -/obj/item/weapon/melee/cursedblade - name = "crystal blade" - desc = "The red crystal blade's polished surface glints in the light, giving off a faint glow." - icon_state = "soulblade" - slot_flags = SLOT_BELT | SLOT_BACK - force = 30 - throwforce = 10 - w_class = ITEMSIZE_NORMAL - sharp = TRUE - edge = TRUE - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - hitsound = 'sound/weapons/bladeslice.ogg' - can_speak = 1 - var/list/voice_mobs = list() //The curse of the sword is that it has someone trapped inside. - - -/obj/item/weapon/melee/cursedblade/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - -/obj/item/weapon/melee/cursedblade/proc/ghost_inhabit(var/mob/candidate) - if(!isobserver(candidate)) - return - //Handle moving the ghost into the new shell. - announce_ghost_joinleave(candidate, 0, "They are occupying a cursed sword now.") - var/mob/living/voice/new_voice = new /mob/living/voice(src) //Make the voice mob the ghost is going to be. - new_voice.transfer_identity(candidate) //Now make the voice mob load from the ghost's active character in preferences. - new_voice.mind = candidate.mind //Transfer the mind, if any. - new_voice.ckey = candidate.ckey //Finally, bring the client over. - new_voice.name = "cursed sword" //Cursed swords shouldn't be known characters. - new_voice.real_name = "cursed sword" - voice_mobs.Add(new_voice) - listening_objects |= src +/obj/item/weapon/melee/chainofcommand + name = "chain of command" + desc = "A tool used by great men to placate the frothing masses." + icon_state = "chain" + slot_flags = SLOT_BELT + force = 10 + throwforce = 7 + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_COMBAT = 4) + attack_verb = list("flogged", "whipped", "lashed", "disciplined") + hitsound = 'sound/weapons/whip.ogg' + reach = 2 + +/obj/item/weapon/melee/chainofcommand/curator_whip + name = "leather whip" + desc = "A fine weapon for some treasure hunting." + icon_state = "curator_whip" + force = 5 + throwforce = 5 + origin_tech = list(TECH_COMBAT = 2) + +/obj/item/weapon/melee/chainofcommand/curator_whip/toy + name = "toy whip" + desc = "A fake whip. Perfect for fake treasure hunting" + force = 2 + throwforce = 2 + +/obj/item/weapon/melee/umbrella + name = "umbrella" + desc = "To keep the rain off you. Use with caution on windy days." + icon = 'icons/obj/items.dmi' + icon_state = "umbrella_closed" + addblends = "umbrella_closed_a" + slot_flags = SLOT_BELT + force = 5 + throwforce = 5 + w_class = ITEMSIZE_NORMAL + var/open = FALSE + +/obj/item/weapon/melee/umbrella/New() + ..() + update_icon() + +/obj/item/weapon/melee/umbrella/attack_self() + src.toggle_umbrella() + +/obj/item/weapon/melee/umbrella/proc/toggle_umbrella() + open = !open + icon_state = "umbrella_[open ? "open" : "closed"]" + addblends = icon_state + "_a" + item_state = icon_state + update_icon() + if(ishuman(src.loc)) + var/mob/living/carbon/human/H = src.loc + H.update_inv_l_hand(0) + H.update_inv_r_hand() + +// Randomizes color +/obj/item/weapon/melee/umbrella/random/New() + color = get_random_colour() + ..() + +/obj/item/weapon/melee/cursedblade + name = "crystal blade" + desc = "The red crystal blade's polished surface glints in the light, giving off a faint glow." + icon_state = "soulblade" + slot_flags = SLOT_BELT | SLOT_BACK + force = 30 + throwforce = 10 + w_class = ITEMSIZE_NORMAL + sharp = TRUE + edge = TRUE + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + hitsound = 'sound/weapons/bladeslice.ogg' + can_speak = 1 + var/list/voice_mobs = list() //The curse of the sword is that it has someone trapped inside. + + +/obj/item/weapon/melee/cursedblade/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(default_parry_check(user, attacker, damage_source) && prob(50)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) + return 1 + return 0 + +/obj/item/weapon/melee/cursedblade/proc/ghost_inhabit(var/mob/candidate) + if(!isobserver(candidate)) + return + //Handle moving the ghost into the new shell. + announce_ghost_joinleave(candidate, 0, "They are occupying a cursed sword now.") + var/mob/living/voice/new_voice = new /mob/living/voice(src) //Make the voice mob the ghost is going to be. + new_voice.transfer_identity(candidate) //Now make the voice mob load from the ghost's active character in preferences. + new_voice.mind = candidate.mind //Transfer the mind, if any. + new_voice.ckey = candidate.ckey //Finally, bring the client over. + new_voice.name = "cursed sword" //Cursed swords shouldn't be known characters. + new_voice.real_name = "cursed sword" + voice_mobs.Add(new_voice) + listening_objects |= src diff --git a/code/game/objects/items/weapons/mop.dm b/code/game/objects/items/weapons/mop.dm index 9f505f903f0..0d11a574af7 100644 --- a/code/game/objects/items/weapons/mop.dm +++ b/code/game/objects/items/weapons/mop.dm @@ -1,79 +1,79 @@ -GLOBAL_LIST_BOILERPLATE(all_mops, /obj/item/weapon/mop) - -/* - * Mop - */ -/obj/item/weapon/mop - name = "mop" - desc = "The world of janitalia wouldn't be complete without a mop." - icon = 'icons/obj/janitor.dmi' - icon_state = "mop" - force = 3.0 - throwforce = 10.0 - throw_speed = 5 - throw_range = 10 - w_class = ITEMSIZE_NORMAL - flags = NOCONDUCT - attack_verb = list("mopped", "bashed", "bludgeoned", "whacked") - var/mopping = 0 - var/mopcount = 0 - -/obj/item/weapon/mop/New() - create_reagents(30) - ..() - -/obj/item/weapon/mop/afterattack(atom/A, mob/user, proximity) - if(!proximity) return - if(istype(A, /turf) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay) || istype(A, /obj/effect/rune)) - if(reagents.total_volume < 1) - to_chat(user, "Your mop is dry!") - return - - user.visible_message("[user] begins to clean \the [get_turf(A)].") - - if(do_after(user, 40)) - var/turf/T = get_turf(A) - if(T) - T.clean(src, user) - to_chat(user, "You have finished mopping!") - - -/obj/effect/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/weapon/mop) || istype(I, /obj/item/weapon/soap)) - return - ..() - -/* - * Advanced Mop - */ -/obj/item/weapon/mop/advanced - name = "advanced mop" - desc = "No stain will go unclean." - icon = 'icons/obj/janitor.dmi' - icon_state = "adv_mop" - force = 3.5 - throwforce = 10.5 - throw_speed = 4 - throw_range = 10 - w_class = ITEMSIZE_NORMAL - flags = NOCONDUCT - attack_verb = list("mopped", "bashed", "bludgeoned", "whacked") - -/obj/item/weapon/mop/advanced/New() - create_reagents(30) - ..() - -/obj/item/weapon/mop/advanced/afterattack(atom/A, mob/user, proximity) - if(!proximity) return - if(istype(A, /turf) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay) || istype(A, /obj/effect/rune)) - if(reagents.total_volume < 1) - to_chat(user, "Your mop is dry!") - return - - user.visible_message("[user] begins to clean \the [get_turf(A)].") - - if(do_after(user, 20)) - var/turf/T = get_turf(A) - if(T) - T.clean(src, user) +GLOBAL_LIST_BOILERPLATE(all_mops, /obj/item/weapon/mop) + +/* + * Mop + */ +/obj/item/weapon/mop + name = "mop" + desc = "The world of janitalia wouldn't be complete without a mop." + icon = 'icons/obj/janitor.dmi' + icon_state = "mop" + force = 3.0 + throwforce = 10.0 + throw_speed = 5 + throw_range = 10 + w_class = ITEMSIZE_NORMAL + flags = NOCONDUCT + attack_verb = list("mopped", "bashed", "bludgeoned", "whacked") + var/mopping = 0 + var/mopcount = 0 + +/obj/item/weapon/mop/New() + create_reagents(30) + ..() + +/obj/item/weapon/mop/afterattack(atom/A, mob/user, proximity) + if(!proximity) return + if(istype(A, /turf) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay) || istype(A, /obj/effect/rune)) + if(reagents.total_volume < 1) + to_chat(user, "Your mop is dry!") + return + + user.visible_message("[user] begins to clean \the [get_turf(A)].") + + if(do_after(user, 40)) + var/turf/T = get_turf(A) + if(T) + T.clean(src, user) + to_chat(user, "You have finished mopping!") + + +/obj/effect/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/weapon/mop) || istype(I, /obj/item/weapon/soap)) + return + ..() + +/* + * Advanced Mop + */ +/obj/item/weapon/mop/advanced + name = "advanced mop" + desc = "No stain will go unclean." + icon = 'icons/obj/janitor.dmi' + icon_state = "adv_mop" + force = 3.5 + throwforce = 10.5 + throw_speed = 4 + throw_range = 10 + w_class = ITEMSIZE_NORMAL + flags = NOCONDUCT + attack_verb = list("mopped", "bashed", "bludgeoned", "whacked") + +/obj/item/weapon/mop/advanced/New() + create_reagents(30) + ..() + +/obj/item/weapon/mop/advanced/afterattack(atom/A, mob/user, proximity) + if(!proximity) return + if(istype(A, /turf) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay) || istype(A, /obj/effect/rune)) + if(reagents.total_volume < 1) + to_chat(user, "Your mop is dry!") + return + + user.visible_message("[user] begins to clean \the [get_turf(A)].") + + if(do_after(user, 20)) + var/turf/T = get_turf(A) + if(T) + T.clean(src, user) to_chat(user, "You have finished mopping!") \ No newline at end of file diff --git a/code/game/objects/items/weapons/paint.dm b/code/game/objects/items/weapons/paint.dm index a86c3d4bd4f..3162718b7d8 100644 --- a/code/game/objects/items/weapons/paint.dm +++ b/code/game/objects/items/weapons/paint.dm @@ -1,78 +1,78 @@ -//NEVER USE THIS IT SUX -PETETHEGOAT -//THE GOAT WAS RIGHT - RKF - -var/global/list/cached_icons = list() - -/obj/item/weapon/reagent_containers/glass/paint - desc = "It's a paint bucket." - name = "paint bucket" - icon = 'icons/obj/items.dmi' - icon_state = "paint_neutral" - item_state = "paintcan" - matter = list(MAT_STEEL = 200) - w_class = ITEMSIZE_NORMAL - amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(10,20,30,60) - volume = 60 - unacidable = FALSE - flags = OPENCONTAINER - var/paint_type = "red" - -/obj/item/weapon/reagent_containers/glass/paint/afterattack(turf/simulated/target, mob/user, proximity) - if(!proximity) return - if(istype(target) && reagents.total_volume > 5) - user.visible_message("\The [target] has been splashed with something by [user]!") - reagents.trans_to_turf(target, 5) - else - return ..() - -/obj/item/weapon/reagent_containers/glass/paint/New() - ..() - if(paint_type) - reagents.add_reagent("paint", volume, paint_type) - -/obj/item/weapon/reagent_containers/glass/paint/red - icon_state = "paint_red" - paint_type = "#FF0000" - -/obj/item/weapon/reagent_containers/glass/paint/yellow - icon_state = "paint_yellow" - paint_type = "#FFFF00" - -/obj/item/weapon/reagent_containers/glass/paint/green - icon_state = "paint_green" - paint_type = "#00FF00" - -/obj/item/weapon/reagent_containers/glass/paint/blue - icon_state = "paint_blue" - paint_type = "#0000FF" - -/obj/item/weapon/reagent_containers/glass/paint/violet - icon_state = "paint_violet" - paint_type = "#FF00FF" - -/obj/item/weapon/reagent_containers/glass/paint/black - icon_state = "paint_black" - paint_type = "#000000" - -/obj/item/weapon/reagent_containers/glass/paint/grey - icon_state = "paint_neutral" - paint_type = "#808080" - -/obj/item/weapon/reagent_containers/glass/paint/orange - icon_state = "paint_orange" - paint_type = "#FFA500" - -/obj/item/weapon/reagent_containers/glass/paint/purple - icon_state = "paint_purple" - paint_type = "#A500FF" - -/obj/item/weapon/reagent_containers/glass/paint/cyan - icon_state = "paint_cyan" - paint_type = "#00FFFF" - -/obj/item/weapon/reagent_containers/glass/paint/white - name = "paint remover bucket" - icon_state = "paint_white" - paint_type = "#FFFFFF" - +//NEVER USE THIS IT SUX -PETETHEGOAT +//THE GOAT WAS RIGHT - RKF + +var/global/list/cached_icons = list() + +/obj/item/weapon/reagent_containers/glass/paint + desc = "It's a paint bucket." + name = "paint bucket" + icon = 'icons/obj/items.dmi' + icon_state = "paint_neutral" + item_state = "paintcan" + matter = list(MAT_STEEL = 200) + w_class = ITEMSIZE_NORMAL + amount_per_transfer_from_this = 10 + possible_transfer_amounts = list(10,20,30,60) + volume = 60 + unacidable = FALSE + flags = OPENCONTAINER + var/paint_type = "red" + +/obj/item/weapon/reagent_containers/glass/paint/afterattack(turf/simulated/target, mob/user, proximity) + if(!proximity) return + if(istype(target) && reagents.total_volume > 5) + user.visible_message("\The [target] has been splashed with something by [user]!") + reagents.trans_to_turf(target, 5) + else + return ..() + +/obj/item/weapon/reagent_containers/glass/paint/New() + ..() + if(paint_type) + reagents.add_reagent("paint", volume, paint_type) + +/obj/item/weapon/reagent_containers/glass/paint/red + icon_state = "paint_red" + paint_type = "#FF0000" + +/obj/item/weapon/reagent_containers/glass/paint/yellow + icon_state = "paint_yellow" + paint_type = "#FFFF00" + +/obj/item/weapon/reagent_containers/glass/paint/green + icon_state = "paint_green" + paint_type = "#00FF00" + +/obj/item/weapon/reagent_containers/glass/paint/blue + icon_state = "paint_blue" + paint_type = "#0000FF" + +/obj/item/weapon/reagent_containers/glass/paint/violet + icon_state = "paint_violet" + paint_type = "#FF00FF" + +/obj/item/weapon/reagent_containers/glass/paint/black + icon_state = "paint_black" + paint_type = "#000000" + +/obj/item/weapon/reagent_containers/glass/paint/grey + icon_state = "paint_neutral" + paint_type = "#808080" + +/obj/item/weapon/reagent_containers/glass/paint/orange + icon_state = "paint_orange" + paint_type = "#FFA500" + +/obj/item/weapon/reagent_containers/glass/paint/purple + icon_state = "paint_purple" + paint_type = "#A500FF" + +/obj/item/weapon/reagent_containers/glass/paint/cyan + icon_state = "paint_cyan" + paint_type = "#00FFFF" + +/obj/item/weapon/reagent_containers/glass/paint/white + name = "paint remover bucket" + icon_state = "paint_white" + paint_type = "#FFFFFF" + diff --git a/code/game/objects/items/weapons/paiwire.dm b/code/game/objects/items/weapons/paiwire.dm index 48358ea2594..6d415170149 100644 --- a/code/game/objects/items/weapons/paiwire.dm +++ b/code/game/objects/items/weapons/paiwire.dm @@ -1,19 +1,19 @@ -/obj/item/weapon/pai_cable/proc/plugin(obj/machinery/M as obj, mob/user as mob) - if(istype(M, /obj/machinery/door) || istype(M, /obj/machinery/camera)) - //VOREStation Add - Can't hack secured_wires doors (vault, etc) - if(istype(M, /obj/machinery/door/airlock)) - var/obj/machinery/door/airlock/A = M - if(A.secured_wires) - to_chat(user,"\The [M] doesn't have any acessible data ports for \the [src]!") - return - //VOREStation Add End - user.visible_message("[user] inserts [src] into a data port on [M].", "You insert [src] into a data port on [M].", "You hear the satisfying click of a wire jack fastening into place.") - playsound(src, 'sound/machines/click.ogg', 50, 1) - user.drop_item() - src.forceMove(M) - src.machine = M - else - user.visible_message("[user] fumbles to find a place on [M] to plug in [src].", "There aren't any ports on [M] that match the jack belonging to [src].") - -/obj/item/weapon/pai_cable/attack(obj/machinery/M as obj, mob/user as mob) - src.plugin(M, user) +/obj/item/weapon/pai_cable/proc/plugin(obj/machinery/M as obj, mob/user as mob) + if(istype(M, /obj/machinery/door) || istype(M, /obj/machinery/camera)) + //VOREStation Add - Can't hack secured_wires doors (vault, etc) + if(istype(M, /obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/A = M + if(A.secured_wires) + to_chat(user,"\The [M] doesn't have any acessible data ports for \the [src]!") + return + //VOREStation Add End + user.visible_message("[user] inserts [src] into a data port on [M].", "You insert [src] into a data port on [M].", "You hear the satisfying click of a wire jack fastening into place.") + playsound(src, 'sound/machines/click.ogg', 50, 1) + user.drop_item() + src.forceMove(M) + src.machine = M + else + user.visible_message("[user] fumbles to find a place on [M] to plug in [src].", "There aren't any ports on [M] that match the jack belonging to [src].") + +/obj/item/weapon/pai_cable/attack(obj/machinery/M as obj, mob/user as mob) + src.plugin(M, user) diff --git a/code/game/objects/items/weapons/scrolls.dm b/code/game/objects/items/weapons/scrolls.dm index 646f38ab332..c4e25652834 100644 --- a/code/game/objects/items/weapons/scrolls.dm +++ b/code/game/objects/items/weapons/scrolls.dm @@ -1,96 +1,96 @@ -/obj/item/weapon/teleportation_scroll - name = "scroll of teleportation" - desc = "A scroll for moving around." - icon = 'icons/obj/wizard.dmi' - icon_state = "scroll" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_books.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_books.dmi' - ) - var/uses = 4.0 - w_class = ITEMSIZE_TINY - item_state = "paper" - throw_speed = 4 - throw_range = 20 - origin_tech = list(TECH_BLUESPACE = 4) - -/obj/item/weapon/teleportation_scroll/attack_self(mob/user as mob) - if((user.mind && !wizards.is_antagonist(user.mind))) - to_chat(usr, "You stare at the scroll but cannot make sense of the markings!") - return - - user.set_machine(src) - var/dat = "Teleportation Scroll:
                    " - dat += "Number of uses: [src.uses]
                    " - dat += "
                    " - dat += "Four uses use them wisely:
                    " - dat += "Teleport
                    " - dat += "Kind regards,
                    Wizards Federation

                    P.S. Don't forget to bring your gear, you'll need it to cast most spells.
                    " - user << browse(dat, "window=scroll") - onclose(user, "scroll") - return - -/obj/item/weapon/teleportation_scroll/Topic(href, href_list) - ..() - if (usr.stat || usr.restrained() || src.loc != usr) - return - var/mob/living/carbon/human/H = usr - if (!( istype(H, /mob/living/carbon/human))) - return 1 - if ((usr == src.loc || (in_range(src, usr) && istype(src.loc, /turf)))) - usr.set_machine(src) - if (href_list["spell_teleport"]) - if (src.uses >= 1) - teleportscroll(H) - attack_self(H) - return - -/obj/item/weapon/teleportation_scroll/proc/teleportscroll(var/mob/user) - var/A = tgui_input_list(user, "Area to jump to:", "Teleportation Scroll", teleportlocs) - if(!A) - return - var/area/thearea = teleportlocs[A] - - if (user.stat || user.restrained()) - return - if(!((user == loc || (in_range(src, user) && istype(src.loc, /turf))))) - return - - var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread() - smoke.set_up(5, 0, user.loc) - smoke.attach(user) - smoke.start() - var/list/L = list() - for(var/turf/T in get_area_turfs(thearea.type)) - if(!T.density) - var/clear = 1 - for(var/obj/O in T) - if(O.density) - clear = 0 - break - if(clear) - L+=T - - if(!L.len) - to_chat(user, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") - return - - if(user && user.buckled) - user.buckled.unbuckle_mob() - - var/list/tempL = L - var/attempt = null - var/success = 0 - while(tempL.len) - attempt = pick(tempL) - success = user.Move(attempt) - if(!success) - tempL.Remove(attempt) - else - break - - if(!success) - user.loc = pick(L) - - smoke.start() - src.uses -= 1 +/obj/item/weapon/teleportation_scroll + name = "scroll of teleportation" + desc = "A scroll for moving around." + icon = 'icons/obj/wizard.dmi' + icon_state = "scroll" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_books.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_books.dmi' + ) + var/uses = 4.0 + w_class = ITEMSIZE_TINY + item_state = "paper" + throw_speed = 4 + throw_range = 20 + origin_tech = list(TECH_BLUESPACE = 4) + +/obj/item/weapon/teleportation_scroll/attack_self(mob/user as mob) + if((user.mind && !wizards.is_antagonist(user.mind))) + to_chat(usr, "You stare at the scroll but cannot make sense of the markings!") + return + + user.set_machine(src) + var/dat = "Teleportation Scroll:
                    " + dat += "Number of uses: [src.uses]
                    " + dat += "
                    " + dat += "Four uses use them wisely:
                    " + dat += "Teleport
                    " + dat += "Kind regards,
                    Wizards Federation

                    P.S. Don't forget to bring your gear, you'll need it to cast most spells.
                    " + user << browse(dat, "window=scroll") + onclose(user, "scroll") + return + +/obj/item/weapon/teleportation_scroll/Topic(href, href_list) + ..() + if (usr.stat || usr.restrained() || src.loc != usr) + return + var/mob/living/carbon/human/H = usr + if (!( istype(H, /mob/living/carbon/human))) + return 1 + if ((usr == src.loc || (in_range(src, usr) && istype(src.loc, /turf)))) + usr.set_machine(src) + if (href_list["spell_teleport"]) + if (src.uses >= 1) + teleportscroll(H) + attack_self(H) + return + +/obj/item/weapon/teleportation_scroll/proc/teleportscroll(var/mob/user) + var/A = tgui_input_list(user, "Area to jump to:", "Teleportation Scroll", teleportlocs) + if(!A) + return + var/area/thearea = teleportlocs[A] + + if (user.stat || user.restrained()) + return + if(!((user == loc || (in_range(src, user) && istype(src.loc, /turf))))) + return + + var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread() + smoke.set_up(5, 0, user.loc) + smoke.attach(user) + smoke.start() + var/list/L = list() + for(var/turf/T in get_area_turfs(thearea.type)) + if(!T.density) + var/clear = 1 + for(var/obj/O in T) + if(O.density) + clear = 0 + break + if(clear) + L+=T + + if(!L.len) + to_chat(user, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") + return + + if(user && user.buckled) + user.buckled.unbuckle_mob() + + var/list/tempL = L + var/attempt = null + var/success = 0 + while(tempL.len) + attempt = pick(tempL) + success = user.Move(attempt) + if(!success) + tempL.Remove(attempt) + else + break + + if(!success) + user.loc = pick(L) + + smoke.start() + src.uses -= 1 diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm index a2549b32797..13dc6fc7b0a 100644 --- a/code/game/objects/items/weapons/shields.dm +++ b/code/game/objects/items/weapons/shields.dm @@ -1,266 +1,266 @@ -//** Shield Helpers -//These are shared by various items that have shield-like behaviour - -//bad_arc is the ABSOLUTE arc of directions from which we cannot block. If you want to fix it to e.g. the user's facing you will need to rotate the dirs yourself. -/proc/check_shield_arc(mob/user, var/bad_arc, atom/damage_source = null, mob/attacker = null) - //check attack direction - var/attack_dir = 0 //direction from the user to the source of the attack - if(istype(damage_source, /obj/item/projectile)) - var/obj/item/projectile/P = damage_source - attack_dir = get_dir(get_turf(user), P.starting) - else if(attacker) - attack_dir = get_dir(get_turf(user), get_turf(attacker)) - else if(damage_source) - attack_dir = get_dir(get_turf(user), get_turf(damage_source)) - - if(!(attack_dir && (attack_dir & bad_arc))) - return 1 - return 0 - -/proc/default_parry_check(mob/user, mob/attacker, atom/damage_source) - //parry only melee attacks - if(istype(damage_source, /obj/item/projectile) || (attacker && get_dist(user, attacker) > 1) || user.incapacitated()) - return 0 - - //block as long as they are not directly behind us - var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 - -/obj/item/proc/unique_parry_check(mob/user, mob/attacker, atom/damage_source) // An overrideable version of the above proc. - return default_parry_check(user, attacker, damage_source) - -/obj/item/weapon/shield - name = "shield" - var/base_block_chance = 50 - preserve_item = 1 - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', - ) - -/obj/item/weapon/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(user.incapacitated()) - return 0 - - //block as long as they are not directly behind us - var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block - if(check_shield_arc(user, bad_arc, damage_source, attacker)) - if(prob(get_block_chance(user, damage, damage_source, attacker))) - user.visible_message("\The [user] blocks [attack_text] with \the [src]!") - return 1 - return 0 - -/obj/item/weapon/shield/proc/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) - return base_block_chance - -/obj/item/weapon/shield/riot - name = "riot shield" - desc = "A shield adept for close quarters engagement. It's also capable of protecting from less powerful projectiles." - icon = 'icons/obj/weapons.dmi' - icon_state = "riot" - slot_flags = SLOT_BACK - force = 5.0 - throwforce = 5.0 - throw_speed = 1 - throw_range = 4 - w_class = ITEMSIZE_LARGE - origin_tech = list(TECH_MATERIAL = 2) - matter = list(MAT_GLASS = 7500, MAT_STEEL = 1000) - attack_verb = list("shoved", "bashed") - var/cooldown = 0 //shield bash cooldown. based on world.time - -/obj/item/weapon/shield/riot/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(user.incapacitated()) - return 0 - - //block as long as they are not directly behind us - var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block - if(check_shield_arc(user, bad_arc, damage_source, attacker)) - if(prob(get_block_chance(user, damage, damage_source, attacker))) - //At this point, we succeeded in our roll for a block attempt, however these kinds of shields struggle to stand up - //to strong bullets and lasers. They still do fine to pistol rounds of all kinds, however. - if(istype(damage_source, /obj/item/projectile)) - var/obj/item/projectile/P = damage_source - if((is_sharp(P) && P.armor_penetration >= 10) || istype(P, /obj/item/projectile/beam)) - //If we're at this point, the bullet/beam is going to go through the shield, however it will hit for less damage. - //Bullets get slowed down, while beams are diffused as they hit the shield, so these shields are not /completely/ - //useless. Extremely penetrating projectiles will go through the shield without less damage. - user.visible_message("\The [user]'s [src.name] is pierced by [attack_text]!") - if(P.armor_penetration < 30) //PTR bullets and x-rays will bypass this entirely. - P.damage = P.damage / 2 - return 0 - //Otherwise, if we're here, we're gonna stop the attack entirely. - user.visible_message("\The [user] blocks [attack_text] with \the [src]!") - playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) - return 1 - return 0 - -/obj/item/weapon/shield/riot/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/melee/baton)) - if(cooldown < world.time - 25) - user.visible_message("[user] bashes [src] with [W]!") - playsound(src, 'sound/effects/shieldbash.ogg', 50, 1) - cooldown = world.time - else - ..() - -/* - * Energy Shield - */ - -/obj/item/weapon/shield/energy - name = "energy combat shield" - desc = "A shield capable of stopping most projectile and melee attacks. It can be retracted, expanded, and stored anywhere." - icon = 'icons/obj/weapons.dmi' - icon_state = "eshield" - item_state = "eshield" - slot_flags = SLOT_EARS - flags = NOCONDUCT - force = 3.0 - throwforce = 5.0 - throw_speed = 1 - throw_range = 4 - w_class = ITEMSIZE_SMALL - var/lrange = 1.5 - var/lpower = 1.5 - var/lcolor = "#006AFF" - origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_ILLEGAL = 4) - attack_verb = list("shoved", "bashed") - var/active = 0 - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', - ) - -/obj/item/weapon/shield/energy/handle_shield(mob/user) - if(!active) - return 0 //turn it on first! - . = ..() - - if(.) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - -/obj/item/weapon/shield/energy/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) - if(istype(damage_source, /obj/item/projectile)) - var/obj/item/projectile/P = damage_source - if((is_sharp(P) && damage > 10) || istype(P, /obj/item/projectile/beam)) - return (base_block_chance - round(damage / 3)) //block bullets and beams using the old block chance - return base_block_chance - -/obj/item/weapon/shield/energy/attack_self(mob/living/user as mob) - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "You beat yourself in the head with [src].") - user.take_organ_damage(5) - active = !active - if (active) - force = 10 - update_icon() - w_class = ITEMSIZE_LARGE - slot_flags = null - playsound(src, 'sound/weapons/saberon.ogg', 50, 1) - to_chat(user, "\The [src] is now active.") - - else - force = 3 - update_icon() - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - playsound(src, 'sound/weapons/saberoff.ogg', 50, 1) - to_chat(user, "\The [src] can now be concealed.") - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - add_fingerprint(user) - return - -/obj/item/weapon/shield/energy/update_icon() - var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") - if(lcolor) - blade_overlay.color = lcolor - color = lcolor - cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other - if(active) - add_overlay(blade_overlay) - item_state = "[icon_state]_blade" - set_light(lrange, lpower, lcolor) - else - color = "FFFFFF" - set_light(0) - item_state = "[icon_state]" - - if(istype(usr,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = usr - H.update_inv_l_hand() - H.update_inv_r_hand() - -/obj/item/weapon/shield/energy/AltClick(mob/living/user) - if(!in_range(src, user)) //Basic checks to prevent abuse - return - if(user.incapacitated() || !istype(user)) - to_chat(user, "You can't do that right now!") - return - if(tgui_alert(usr, "Are you sure you want to recolor your shield?", "Confirm Recolor", list("Yes", "No")) == "Yes") - var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null - if(energy_color_input) - lcolor = sanitize_hexcolor(energy_color_input) - update_icon() - -/obj/item/weapon/shield/energy/examine(mob/user) - . = ..() - . += "Alt-click to recolor it." - -/obj/item/weapon/shield/riot/tele - name = "telescopic shield" - desc = "An advanced riot shield made of lightweight materials that collapses for easy storage." - icon = 'icons/obj/weapons.dmi' - icon_state = "teleriot0" - slot_flags = null - force = 3 - throwforce = 3 - throw_speed = 3 - throw_range = 4 - w_class = ITEMSIZE_NORMAL - var/active = 0 -/* -/obj/item/weapon/shield/energy/IsShield() - if(active) - return 1 - else - return 0 -*/ -/obj/item/weapon/shield/riot/tele/attack_self(mob/living/user) - active = !active - icon_state = "teleriot[active]" - playsound(src, 'sound/weapons/empty.ogg', 50, 1) - - if(active) - force = 8 - throwforce = 5 - throw_speed = 2 - w_class = ITEMSIZE_LARGE - slot_flags = SLOT_BACK - to_chat(user, "You extend \the [src].") - else - force = 3 - throwforce = 3 - throw_speed = 3 - w_class = ITEMSIZE_NORMAL - slot_flags = null - to_chat(user, "[src] can now be concealed.") - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - add_fingerprint(user) - return +//** Shield Helpers +//These are shared by various items that have shield-like behaviour + +//bad_arc is the ABSOLUTE arc of directions from which we cannot block. If you want to fix it to e.g. the user's facing you will need to rotate the dirs yourself. +/proc/check_shield_arc(mob/user, var/bad_arc, atom/damage_source = null, mob/attacker = null) + //check attack direction + var/attack_dir = 0 //direction from the user to the source of the attack + if(istype(damage_source, /obj/item/projectile)) + var/obj/item/projectile/P = damage_source + attack_dir = get_dir(get_turf(user), P.starting) + else if(attacker) + attack_dir = get_dir(get_turf(user), get_turf(attacker)) + else if(damage_source) + attack_dir = get_dir(get_turf(user), get_turf(damage_source)) + + if(!(attack_dir && (attack_dir & bad_arc))) + return 1 + return 0 + +/proc/default_parry_check(mob/user, mob/attacker, atom/damage_source) + //parry only melee attacks + if(istype(damage_source, /obj/item/projectile) || (attacker && get_dist(user, attacker) > 1) || user.incapacitated()) + return 0 + + //block as long as they are not directly behind us + var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block + if(!check_shield_arc(user, bad_arc, damage_source, attacker)) + return 0 + + return 1 + +/obj/item/proc/unique_parry_check(mob/user, mob/attacker, atom/damage_source) // An overrideable version of the above proc. + return default_parry_check(user, attacker, damage_source) + +/obj/item/weapon/shield + name = "shield" + var/base_block_chance = 50 + preserve_item = 1 + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + +/obj/item/weapon/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(user.incapacitated()) + return 0 + + //block as long as they are not directly behind us + var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block + if(check_shield_arc(user, bad_arc, damage_source, attacker)) + if(prob(get_block_chance(user, damage, damage_source, attacker))) + user.visible_message("\The [user] blocks [attack_text] with \the [src]!") + return 1 + return 0 + +/obj/item/weapon/shield/proc/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) + return base_block_chance + +/obj/item/weapon/shield/riot + name = "riot shield" + desc = "A shield adept for close quarters engagement. It's also capable of protecting from less powerful projectiles." + icon = 'icons/obj/weapons.dmi' + icon_state = "riot" + slot_flags = SLOT_BACK + force = 5.0 + throwforce = 5.0 + throw_speed = 1 + throw_range = 4 + w_class = ITEMSIZE_LARGE + origin_tech = list(TECH_MATERIAL = 2) + matter = list(MAT_GLASS = 7500, MAT_STEEL = 1000) + attack_verb = list("shoved", "bashed") + var/cooldown = 0 //shield bash cooldown. based on world.time + +/obj/item/weapon/shield/riot/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(user.incapacitated()) + return 0 + + //block as long as they are not directly behind us + var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block + if(check_shield_arc(user, bad_arc, damage_source, attacker)) + if(prob(get_block_chance(user, damage, damage_source, attacker))) + //At this point, we succeeded in our roll for a block attempt, however these kinds of shields struggle to stand up + //to strong bullets and lasers. They still do fine to pistol rounds of all kinds, however. + if(istype(damage_source, /obj/item/projectile)) + var/obj/item/projectile/P = damage_source + if((is_sharp(P) && P.armor_penetration >= 10) || istype(P, /obj/item/projectile/beam)) + //If we're at this point, the bullet/beam is going to go through the shield, however it will hit for less damage. + //Bullets get slowed down, while beams are diffused as they hit the shield, so these shields are not /completely/ + //useless. Extremely penetrating projectiles will go through the shield without less damage. + user.visible_message("\The [user]'s [src.name] is pierced by [attack_text]!") + if(P.armor_penetration < 30) //PTR bullets and x-rays will bypass this entirely. + P.damage = P.damage / 2 + return 0 + //Otherwise, if we're here, we're gonna stop the attack entirely. + user.visible_message("\The [user] blocks [attack_text] with \the [src]!") + playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) + return 1 + return 0 + +/obj/item/weapon/shield/riot/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/melee/baton)) + if(cooldown < world.time - 25) + user.visible_message("[user] bashes [src] with [W]!") + playsound(src, 'sound/effects/shieldbash.ogg', 50, 1) + cooldown = world.time + else + ..() + +/* + * Energy Shield + */ + +/obj/item/weapon/shield/energy + name = "energy combat shield" + desc = "A shield capable of stopping most projectile and melee attacks. It can be retracted, expanded, and stored anywhere." + icon = 'icons/obj/weapons.dmi' + icon_state = "eshield" + item_state = "eshield" + slot_flags = SLOT_EARS + flags = NOCONDUCT + force = 3.0 + throwforce = 5.0 + throw_speed = 1 + throw_range = 4 + w_class = ITEMSIZE_SMALL + var/lrange = 1.5 + var/lpower = 1.5 + var/lcolor = "#006AFF" + origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_ILLEGAL = 4) + attack_verb = list("shoved", "bashed") + var/active = 0 + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + +/obj/item/weapon/shield/energy/handle_shield(mob/user) + if(!active) + return 0 //turn it on first! + . = ..() + + if(.) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + +/obj/item/weapon/shield/energy/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) + if(istype(damage_source, /obj/item/projectile)) + var/obj/item/projectile/P = damage_source + if((is_sharp(P) && damage > 10) || istype(P, /obj/item/projectile/beam)) + return (base_block_chance - round(damage / 3)) //block bullets and beams using the old block chance + return base_block_chance + +/obj/item/weapon/shield/energy/attack_self(mob/living/user as mob) + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "You beat yourself in the head with [src].") + user.take_organ_damage(5) + active = !active + if (active) + force = 10 + update_icon() + w_class = ITEMSIZE_LARGE + slot_flags = null + playsound(src, 'sound/weapons/saberon.ogg', 50, 1) + to_chat(user, "\The [src] is now active.") + + else + force = 3 + update_icon() + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + playsound(src, 'sound/weapons/saberoff.ogg', 50, 1) + to_chat(user, "\The [src] can now be concealed.") + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + + add_fingerprint(user) + return + +/obj/item/weapon/shield/energy/update_icon() + var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") + if(lcolor) + blade_overlay.color = lcolor + color = lcolor + cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other + if(active) + add_overlay(blade_overlay) + item_state = "[icon_state]_blade" + set_light(lrange, lpower, lcolor) + else + color = "FFFFFF" + set_light(0) + item_state = "[icon_state]" + + if(istype(usr,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = usr + H.update_inv_l_hand() + H.update_inv_r_hand() + +/obj/item/weapon/shield/energy/AltClick(mob/living/user) + if(!in_range(src, user)) //Basic checks to prevent abuse + return + if(user.incapacitated() || !istype(user)) + to_chat(user, "You can't do that right now!") + return + if(tgui_alert(usr, "Are you sure you want to recolor your shield?", "Confirm Recolor", list("Yes", "No")) == "Yes") + var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null + if(energy_color_input) + lcolor = sanitize_hexcolor(energy_color_input) + update_icon() + +/obj/item/weapon/shield/energy/examine(mob/user) + . = ..() + . += "Alt-click to recolor it." + +/obj/item/weapon/shield/riot/tele + name = "telescopic shield" + desc = "An advanced riot shield made of lightweight materials that collapses for easy storage." + icon = 'icons/obj/weapons.dmi' + icon_state = "teleriot0" + slot_flags = null + force = 3 + throwforce = 3 + throw_speed = 3 + throw_range = 4 + w_class = ITEMSIZE_NORMAL + var/active = 0 +/* +/obj/item/weapon/shield/energy/IsShield() + if(active) + return 1 + else + return 0 +*/ +/obj/item/weapon/shield/riot/tele/attack_self(mob/living/user) + active = !active + icon_state = "teleriot[active]" + playsound(src, 'sound/weapons/empty.ogg', 50, 1) + + if(active) + force = 8 + throwforce = 5 + throw_speed = 2 + w_class = ITEMSIZE_LARGE + slot_flags = SLOT_BACK + to_chat(user, "You extend \the [src].") + else + force = 3 + throwforce = 3 + throw_speed = 3 + w_class = ITEMSIZE_NORMAL + slot_flags = null + to_chat(user, "[src] can now be concealed.") + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + + add_fingerprint(user) + return diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index 49aa20e4d9b..345884f6d4e 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -1,552 +1,552 @@ -/* - * Backpack - */ - -/obj/item/weapon/storage/backpack - name = "backpack" - desc = "You wear this on your back and put items into it." - icon = 'icons/inventory/back/item.dmi' - icon_state = "backpack" - sprite_sheets = list( - SPECIES_TESHARI = 'icons/inventory/back/mob_teshari.dmi' - ) - w_class = ITEMSIZE_LARGE - slot_flags = SLOT_BACK - max_w_class = ITEMSIZE_LARGE - max_storage_space = INVENTORY_STANDARD_SPACE - var/flippable = 0 - var/side = 0 //0 = right, 1 = left - drop_sound = 'sound/items/drop/backpack.ogg' - pickup_sound = 'sound/items/pickup/backpack.ogg' - - -/obj/item/weapon/storage/backpack/equipped(var/mob/user, var/slot) - if (slot == slot_back && src.use_sound) - playsound(src, src.use_sound, 50, 1, -5) - ..(user, slot) - -/* -/obj/item/weapon/storage/backpack/dropped(mob/user as mob) - if (loc == user && src.use_sound) - playsound(src, src.use_sound, 50, 1, -5) - ..(user) -*/ - -/* - * Backpack Types - */ - -/obj/item/weapon/storage/backpack/holding - name = "bag of holding" - desc = "A backpack that opens into a localized pocket of Blue Space." - origin_tech = list(TECH_BLUESPACE = 4) - icon_state = "holdingpack" - max_w_class = ITEMSIZE_LARGE - max_storage_space = ITEMSIZE_COST_NORMAL * 14 // 56 - storage_cost = INVENTORY_STANDARD_SPACE + 1 - -/obj/item/weapon/storage/backpack/holding/duffle - name = "dufflebag of holding" - var/tilted = 0 - icon_state = "holdingduffle" - -/obj/item/weapon/storage/backpack/holding/duffle/Initialize() - . = ..() - if(prob(50)) - icon_state = "[icon_state]_tilted" - tilted = 1 - -/obj/item/weapon/storage/backpack/holding/duffle/verb/tilt() - set name = "Adjust Duffelbag Angle" - set desc = "Adjust the angle of your dufflebag for cosmetic effect" - set category = "Object" - set src in usr - if(!usr.canmove || usr.stat || usr.restrained()) - return - if(tilted) - icon_state = "[initial(icon_state)]" - to_chat(usr, "You adjust the angle of \the [src] to rest across your lower back.") - tilted = 0 - else - icon_state = "[icon_state]_tilted" - to_chat(usr, "You adjust the angle of \the [src] to rest diagonally across your back.") - tilted = 1 - update_icon() - usr.update_inv_back() - -/obj/item/weapon/storage/backpack/holding/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/storage/backpack/holding)) - to_chat(user, "The Bluespace interfaces of the two devices conflict and malfunction.") - qdel(W) - return - . = ..() - -//Please don't clutter the parent storage item with stupid hacks. -/obj/item/weapon/storage/backpack/holding/can_be_inserted(obj/item/W as obj, stop_messages = 0) - if(istype(W, /obj/item/weapon/storage/backpack/holding)) - return FALSE - return ..() - -/obj/item/weapon/storage/backpack/santabag - name = "\improper Santa's gift bag" - desc = "Space Santa uses this to deliver toys to all the nice children in space in Christmas! Wow, it's pretty big!" - icon_state = "giftbag0" - item_state_slots = list(slot_r_hand_str = "giftbag", slot_l_hand_str = "giftbag") - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_NORMAL * 100 // can store a ton of shit! - -/obj/item/weapon/storage/backpack/cultpack - name = "trophy rack" - desc = "It's useful for both carrying extra gear and proudly declaring your insanity." - icon_state = "backpack_cult" - -/obj/item/weapon/storage/backpack/clown - name = "Giggles von Honkerton" - desc = "It's a backpack made by Honk! Co." - icon_state = "backpack_clown" - -/obj/item/weapon/storage/backpack/white - name = "white backpack" - icon_state = "backpack_white" - -/obj/item/weapon/storage/backpack/fancy - name = "fancy backpack" - icon_state = "backpack_fancy" - -/obj/item/weapon/storage/backpack/military - name = "military backpack" - icon_state = "backpack_military" - -/obj/item/weapon/storage/backpack/medic - name = "medical backpack" - desc = "It's a backpack especially designed for use in a sterile environment." - icon_state = "backpack_medical" - -/obj/item/weapon/storage/backpack/security - name = "security backpack" - desc = "It's a very robust backpack." - icon_state = "backpack_security" - -/obj/item/weapon/storage/backpack/captain - name = "site manager's backpack" - desc = "It's a special backpack made exclusively for officers." - icon_state = "backpack_captain" - -/obj/item/weapon/storage/backpack/industrial - name = "industrial backpack" - desc = "It's a tough backpack for the daily grind of station life." - icon_state = "backpack_industrial" - -/obj/item/weapon/storage/backpack/toxins - name = "laboratory backpack" - desc = "It's a light backpack modeled for use in laboratories and other scientific institutions." - icon_state = "backpack_purple" - -/obj/item/weapon/storage/backpack/hydroponics - name = "herbalist's backpack" - desc = "It's a green backpack with many pockets to store plants and tools in." - icon_state = "backpack_hydro" - -/obj/item/weapon/storage/backpack/genetics - name = "geneticist backpack" - desc = "It's a backpack fitted with slots for diskettes and other workplace tools." - icon_state = "backpack_blue" - -/obj/item/weapon/storage/backpack/virology - name = "sterile backpack" - desc = "It's a sterile backpack able to withstand different pathogens from entering its fabric." - icon_state = "backpack_green" - -/obj/item/weapon/storage/backpack/chemistry - name = "chemistry backpack" - desc = "It's an orange backpack which was designed to hold beakers, pill bottles and bottles." - icon_state = "backpack_orange" - -/* - * Duffle Types - */ - -/obj/item/weapon/storage/backpack/dufflebag - name = "dufflebag" - desc = "A large dufflebag for holding extra things." - icon_state = "duffle" - slowdown = 0.5 - var/tilted = 0 - var/can_tilt = 1 - max_storage_space = INVENTORY_DUFFLEBAG_SPACE - -/obj/item/weapon/storage/backpack/dufflebag/Initialize() - . = ..() - if(prob(50)) - icon_state = "[icon_state]_tilted" - tilted = 1 - -/obj/item/weapon/storage/backpack/dufflebag/verb/tilt() - set name = "Adjust Duffelbag Angle" - set desc = "Adjust the angle of your dufflebag for cosmetic effect" - set category = "Object" - set src in usr - if(!usr.canmove || usr.stat || usr.restrained()) - return - if(!can_tilt) - to_chat(usr, "[src] can't be adjusted like that.") - return - if(tilted) - icon_state = "[initial(icon_state)]" - to_chat(usr, "You adjust the angle of \the [src] to rest across your lower back.") - tilted = 0 - else - icon_state = "[icon_state]_tilted" - to_chat(usr, "You adjust the angle of \the [src] to rest diagonally across your back.") - tilted = 1 - update_icon() - usr.update_inv_back() - -/obj/item/weapon/storage/backpack/dufflebag/syndie - name = "black dufflebag" - desc = "A large dufflebag for holding extra tactical supplies. This one appears to be made out of lighter material than usual." - icon_state = "duffle_syndie" - slowdown = 0 - -/obj/item/weapon/storage/backpack/dufflebag/syndie/med - name = "medical dufflebag" - desc = "A large dufflebag for holding extra tactical medical supplies. This one appears to be made out of lighter material than usual." - icon_state = "duffle_syndiemed" - -/obj/item/weapon/storage/backpack/dufflebag/syndie/ammo - name = "ammunition dufflebag" - desc = "A large dufflebag for holding extra weapons ammunition and supplies. This one appears to be made out of lighter material than usual." - icon_state = "duffle_syndieammo" - -/obj/item/weapon/storage/backpack/dufflebag/captain - name = "site manager's dufflebag" - desc = "A large dufflebag for holding extra captainly goods." - icon_state = "duffle_captain" - -/obj/item/weapon/storage/backpack/dufflebag/med - name = "medical dufflebag" - desc = "A large dufflebag for holding extra medical supplies." - icon_state = "duffle_medical" - -/obj/item/weapon/storage/backpack/dufflebag/emt - name = "EMT dufflebag" - desc = "A large dufflebag for holding extra medical supplies. This one has reflective stripes!" - icon_state = "duffle_emt" - -/obj/item/weapon/storage/backpack/dufflebag/sec - name = "security dufflebag" - desc = "A large dufflebag for holding extra security supplies and ammunition." - icon_state = "duffle_security" - -/obj/item/weapon/storage/backpack/dufflebag/eng - name = "industrial dufflebag" - desc = "A large dufflebag for holding extra tools and supplies." - icon_state = "duffle_industrial" - -/obj/item/weapon/storage/backpack/dufflebag/sci - name = "science dufflebag" - desc = "A large dufflebag for holding circuits and beakers." - icon_state = "duffle_science" - -/obj/item/weapon/storage/backpack/dufflebag/drone - name = "drone dufflebag" - desc = "A large dufflebag for holding small robots? Or maybe it's one used by robots!" - icon_state = "duffle_drone" - -/obj/item/weapon/storage/backpack/dufflebag/cursed - name = "cursed dufflebag" - desc = "That probably shouldn't be moving..." - icon_state = "duffle_curse" - -/* - * Satchel Types - */ - -/obj/item/weapon/storage/backpack/satchel - name = "leather satchel" - desc = "It's a very fancy satchel made with fine leather." - icon_state = "satchel" - -/obj/item/weapon/storage/backpack/satchel/withwallet - starts_with = list(/obj/item/weapon/storage/wallet/random) - -/obj/item/weapon/storage/backpack/satchel/norm - name = "satchel" - desc = "A trendy looking satchel." - icon_state = "satchel_grey" - -/obj/item/weapon/storage/backpack/satchel/white - name = "white satchel" - icon_state = "satchel_white" - -/obj/item/weapon/storage/backpack/satchel/fancy - name = "fancy satchel" - icon_state = "satchel_fancy" - -/obj/item/weapon/storage/backpack/satchel/military - name = "military satchel" - icon_state = "satchel_military" - -/obj/item/weapon/storage/backpack/satchel/eng - name = "industrial satchel" - desc = "A tough satchel with extra pockets." - icon_state = "satchel_industrial" - -/obj/item/weapon/storage/backpack/satchel/med - name = "medical satchel" - desc = "A sterile satchel used in medical departments." - icon_state = "satchel_medical" - -/obj/item/weapon/storage/backpack/satchel/vir - name = "virologist satchel" - desc = "A sterile satchel with virologist colours." - icon_state = "satchel_green" - -/obj/item/weapon/storage/backpack/satchel/chem - name = "chemist satchel" - desc = "A sterile satchel with chemist colours." - icon_state = "satchel_orange" - -/obj/item/weapon/storage/backpack/satchel/gen - name = "geneticist satchel" - desc = "A sterile satchel with geneticist colours." - icon_state = "satchel_blue" - -/obj/item/weapon/storage/backpack/satchel/tox - name = "scientist satchel" - desc = "Useful for holding research materials." - icon_state = "satchel_purple" - -/obj/item/weapon/storage/backpack/satchel/sec - name = "security satchel" - desc = "A robust satchel for security related needs." - icon_state = "satchel_security" - -/obj/item/weapon/storage/backpack/satchel/hyd - name = "hydroponics satchel" - desc = "A green satchel for plant related work." - icon_state = "satchel_hydro" - -/obj/item/weapon/storage/backpack/satchel/cap - name = "site manager's satchel" - desc = "An exclusive satchel for officers." - icon_state = "satchel_captain" - -//ERT backpacks. -/obj/item/weapon/storage/backpack/ert - name = "emergency response team backpack" - desc = "A spacious backpack with lots of pockets, used by members of the Emergency Response Team." - icon_state = "ert_commander" - -//Commander -/obj/item/weapon/storage/backpack/ert/commander - name = "emergency response team commander backpack" - desc = "A spacious backpack with lots of pockets, worn by the commander of an Emergency Response Team." - -//Security -/obj/item/weapon/storage/backpack/ert/security - name = "emergency response team security backpack" - desc = "A spacious backpack with lots of pockets, worn by security members of an Emergency Response Team." - icon_state = "ert_security" - -//Engineering -/obj/item/weapon/storage/backpack/ert/engineer - name = "emergency response team engineer backpack" - desc = "A spacious backpack with lots of pockets, worn by engineering members of an Emergency Response Team." - icon_state = "ert_engineering" - -//Medical -/obj/item/weapon/storage/backpack/ert/medical - name = "emergency response team medical backpack" - desc = "A spacious backpack with lots of pockets, worn by medical members of an Emergency Response Team." - icon_state = "ert_medical" - -/* - * Courier Bags - */ - -/obj/item/weapon/storage/backpack/messenger - name = "messenger bag" - desc = "A sturdy backpack worn over one shoulder." - icon_state = "courier" - item_state_slots = list(slot_r_hand_str = "satchel_grey", slot_l_hand_str = "satchel_grey") - -/obj/item/weapon/storage/backpack/messenger/chem - name = "chemistry messenger bag" - desc = "A serile backpack worn over one shoulder. This one is in Chemsitry colors." - icon_state = "courier_chemistry" - item_state_slots = list(slot_r_hand_str = "satchel_orange", slot_l_hand_str = "satchel_orange") - -/obj/item/weapon/storage/backpack/messenger/med - name = "medical messenger bag" - desc = "A sterile backpack worn over one shoulder used in medical departments." - icon_state = "courier_medical" - item_state_slots = list(slot_r_hand_str = "satchel_medical", slot_l_hand_str = "satchel_medical") - -/obj/item/weapon/storage/backpack/messenger/viro - name = "virology messenger bag" - desc = "A sterile backpack worn over one shoulder. This one is in Virology colors." - icon_state = "courier_virology" - item_state_slots = list(slot_r_hand_str = "satchel_green", slot_l_hand_str = "satchel_green") - -/obj/item/weapon/storage/backpack/messenger/tox - name = "research messenger bag" - desc = "A backpack worn over one shoulder. Useful for holding science materials." - icon_state = "courier_toxins" - item_state_slots = list(slot_r_hand_str = "satchel_purple", slot_l_hand_str = "satchel_purple") - -/obj/item/weapon/storage/backpack/messenger/com - name = "command messenger bag" - desc = "A special backpack worn over one shoulder. This one is made specifically for officers." - icon_state = "courier_captain" - item_state_slots = list(slot_r_hand_str = "satchel_captain", slot_l_hand_str = "satchel_captain") - -/obj/item/weapon/storage/backpack/messenger/engi - name = "engineering messenger bag" - icon_state = "courier_industrial" - item_state_slots = list(slot_r_hand_str = "satchel_industrial", slot_l_hand_str = "satchel_industrial") - -/obj/item/weapon/storage/backpack/messenger/hyd - name = "hydroponics messenger bag" - desc = "A backpack worn over one shoulder. This one is designed for plant-related work." - icon_state = "courier_hydro" - item_state_slots = list(slot_r_hand_str = "satchel_hydro", slot_l_hand_str = "satchel_hydro") - -/obj/item/weapon/storage/backpack/messenger/sec - name = "security messenger bag" - desc = "A tactical backpack worn over one shoulder. This one is in Security colors." - icon_state = "courier_security" - item_state_slots = list(slot_r_hand_str = "satchel_security", slot_l_hand_str = "satchel_security") - -/obj/item/weapon/storage/backpack/messenger/black - icon_state = "courier_black" - - -/* - * Sport Bags - */ - -/obj/item/weapon/storage/backpack/sport - name = "sports backpack" - icon_state = "backsport" - -/obj/item/weapon/storage/backpack/sport/white - name = "white sports backpack" - icon_state = "backsport_white" - -/obj/item/weapon/storage/backpack/sport/fancy - name = "fancy sports backpack" - icon_state = "backsport_fancy" - -/obj/item/weapon/storage/backpack/sport/vir - name = "virologist sports backpack" - desc = "A sterile sports backpack with virologist colours." - icon_state = "backsport_green" - -/obj/item/weapon/storage/backpack/sport/chem - name = "chemist sports backpack" - desc = "A sterile sports backpack with chemist colours." - icon_state = "backsport_orange" - -/obj/item/weapon/storage/backpack/sport/gen - name = "geneticist sports backpack" - desc = "A sterile sports backpack with geneticist colours." - icon_state = "backsport_blue" - -/obj/item/weapon/storage/backpack/sport/tox - name = "scientist sports backpack" - desc = "Useful for holding research materials." - icon_state = "backsport_purple" - -/obj/item/weapon/storage/backpack/sport/sec - name = "security sports backpack" - desc = "A robust sports backpack for security related needs." - icon_state = "backsport_security" - -/obj/item/weapon/storage/backpack/sport/hyd - name = "hydroponics sports backpack" - desc = "A green sports backpack for plant related work." - icon_state = "backsport_hydro" - -//Purses -/obj/item/weapon/storage/backpack/purse - name = "purse" - desc = "A small, fashionable bag typically worn over the shoulder." - icon_state = "purse" - item_state_slots = list(slot_r_hand_str = "lgpurse", slot_l_hand_str = "lgpurse") - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_NORMAL * 5 - -//Parachutes -/obj/item/weapon/storage/backpack/parachute - name = "parachute" - desc = "A specially made backpack, designed to help one survive jumping from incredible heights. It sacrifices some storage space for that added functionality." - icon_state = "parachute" - item_state_slots = list(slot_r_hand_str = "backpack", slot_l_hand_str = "backpack") - max_storage_space = ITEMSIZE_COST_NORMAL * 5 - -/obj/item/weapon/storage/backpack/parachute/examine(mob/user) - . = ..() - if(Adjacent(user)) - if(parachute) - . += "It seems to be packed." - else - . += "It seems to be unpacked." - -/obj/item/weapon/storage/backpack/parachute/handleParachute() - parachute = FALSE //If you parachute in, the parachute has probably been used. - -/obj/item/weapon/storage/backpack/parachute/verb/pack_parachute() - - set name = "Pack/Unpack Parachute" - set category = "Object" - set src in usr - - if(!istype(src.loc, /mob/living)) - return - - var/mob/living/carbon/human/H = usr - - if(!istype(H)) - return - if(H.stat) - return - if(H.back == src) - to_chat(H, "How do you expect to work on \the [src] while it's on your back?") - return - - if(!parachute) //This packs the parachute - H.visible_message("\The [H] starts to pack \the [src]!", \ - "You start to pack \the [src]!", \ - "You hear the shuffling of cloth.") - if(do_after(H, 50)) - H.visible_message("\The [H] finishes packing \the [src]!", \ - "You finish packing \the [src]!", \ - "You hear the shuffling of cloth.") - parachute = TRUE - else - H.visible_message("\The [src] gives up on packing \the [src]!", \ - "You give up on packing \the [src]!") - return - else //This unpacks the parachute - H.visible_message("\The [src] starts to unpack \the [src]!", \ - "You start to unpack \the [src]!", \ - "You hear the shuffling of cloth.") - if(do_after(H, 25)) - H.visible_message("\The [src] finishes unpacking \the [src]!", \ - "You finish unpacking \the [src]!", \ - "You hear the shuffling of cloth.") - parachute = FALSE - else - H.visible_message("\The [src] decides not to unpack \the [src]!", \ - "You decide not to unpack \the [src]!") - return - -/obj/item/weapon/storage/backpack/satchel/ranger - name = "ranger satchel" - desc = "A satchel designed for the Go Go ERT Rangers series to allow for slightly bigger carry capacity for the ERT-Rangers.\ - Unlike the show claims, it is not a phoron-enhanced satchel of holding with plot-relevant content." - icon = 'icons/obj/clothing/ranger.dmi' +/* + * Backpack + */ + +/obj/item/weapon/storage/backpack + name = "backpack" + desc = "You wear this on your back and put items into it." + icon = 'icons/inventory/back/item.dmi' + icon_state = "backpack" + sprite_sheets = list( + SPECIES_TESHARI = 'icons/inventory/back/mob_teshari.dmi' + ) + w_class = ITEMSIZE_LARGE + slot_flags = SLOT_BACK + max_w_class = ITEMSIZE_LARGE + max_storage_space = INVENTORY_STANDARD_SPACE + var/flippable = 0 + var/side = 0 //0 = right, 1 = left + drop_sound = 'sound/items/drop/backpack.ogg' + pickup_sound = 'sound/items/pickup/backpack.ogg' + + +/obj/item/weapon/storage/backpack/equipped(var/mob/user, var/slot) + if (slot == slot_back && src.use_sound) + playsound(src, src.use_sound, 50, 1, -5) + ..(user, slot) + +/* +/obj/item/weapon/storage/backpack/dropped(mob/user as mob) + if (loc == user && src.use_sound) + playsound(src, src.use_sound, 50, 1, -5) + ..(user) +*/ + +/* + * Backpack Types + */ + +/obj/item/weapon/storage/backpack/holding + name = "bag of holding" + desc = "A backpack that opens into a localized pocket of Blue Space." + origin_tech = list(TECH_BLUESPACE = 4) + icon_state = "holdingpack" + max_w_class = ITEMSIZE_LARGE + max_storage_space = ITEMSIZE_COST_NORMAL * 14 // 56 + storage_cost = INVENTORY_STANDARD_SPACE + 1 + +/obj/item/weapon/storage/backpack/holding/duffle + name = "dufflebag of holding" + var/tilted = 0 + icon_state = "holdingduffle" + +/obj/item/weapon/storage/backpack/holding/duffle/Initialize() + . = ..() + if(prob(50)) + icon_state = "[icon_state]_tilted" + tilted = 1 + +/obj/item/weapon/storage/backpack/holding/duffle/verb/tilt() + set name = "Adjust Duffelbag Angle" + set desc = "Adjust the angle of your dufflebag for cosmetic effect" + set category = "Object" + set src in usr + if(!usr.canmove || usr.stat || usr.restrained()) + return + if(tilted) + icon_state = "[initial(icon_state)]" + to_chat(usr, "You adjust the angle of \the [src] to rest across your lower back.") + tilted = 0 + else + icon_state = "[icon_state]_tilted" + to_chat(usr, "You adjust the angle of \the [src] to rest diagonally across your back.") + tilted = 1 + update_icon() + usr.update_inv_back() + +/obj/item/weapon/storage/backpack/holding/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/storage/backpack/holding)) + to_chat(user, "The Bluespace interfaces of the two devices conflict and malfunction.") + qdel(W) + return + . = ..() + +//Please don't clutter the parent storage item with stupid hacks. +/obj/item/weapon/storage/backpack/holding/can_be_inserted(obj/item/W as obj, stop_messages = 0) + if(istype(W, /obj/item/weapon/storage/backpack/holding)) + return FALSE + return ..() + +/obj/item/weapon/storage/backpack/santabag + name = "\improper Santa's gift bag" + desc = "Space Santa uses this to deliver toys to all the nice children in space in Christmas! Wow, it's pretty big!" + icon_state = "giftbag0" + item_state_slots = list(slot_r_hand_str = "giftbag", slot_l_hand_str = "giftbag") + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_NORMAL * 100 // can store a ton of shit! + +/obj/item/weapon/storage/backpack/cultpack + name = "trophy rack" + desc = "It's useful for both carrying extra gear and proudly declaring your insanity." + icon_state = "backpack_cult" + +/obj/item/weapon/storage/backpack/clown + name = "Giggles von Honkerton" + desc = "It's a backpack made by Honk! Co." + icon_state = "backpack_clown" + +/obj/item/weapon/storage/backpack/white + name = "white backpack" + icon_state = "backpack_white" + +/obj/item/weapon/storage/backpack/fancy + name = "fancy backpack" + icon_state = "backpack_fancy" + +/obj/item/weapon/storage/backpack/military + name = "military backpack" + icon_state = "backpack_military" + +/obj/item/weapon/storage/backpack/medic + name = "medical backpack" + desc = "It's a backpack especially designed for use in a sterile environment." + icon_state = "backpack_medical" + +/obj/item/weapon/storage/backpack/security + name = "security backpack" + desc = "It's a very robust backpack." + icon_state = "backpack_security" + +/obj/item/weapon/storage/backpack/captain + name = "site manager's backpack" + desc = "It's a special backpack made exclusively for officers." + icon_state = "backpack_captain" + +/obj/item/weapon/storage/backpack/industrial + name = "industrial backpack" + desc = "It's a tough backpack for the daily grind of station life." + icon_state = "backpack_industrial" + +/obj/item/weapon/storage/backpack/toxins + name = "laboratory backpack" + desc = "It's a light backpack modeled for use in laboratories and other scientific institutions." + icon_state = "backpack_purple" + +/obj/item/weapon/storage/backpack/hydroponics + name = "herbalist's backpack" + desc = "It's a green backpack with many pockets to store plants and tools in." + icon_state = "backpack_hydro" + +/obj/item/weapon/storage/backpack/genetics + name = "geneticist backpack" + desc = "It's a backpack fitted with slots for diskettes and other workplace tools." + icon_state = "backpack_blue" + +/obj/item/weapon/storage/backpack/virology + name = "sterile backpack" + desc = "It's a sterile backpack able to withstand different pathogens from entering its fabric." + icon_state = "backpack_green" + +/obj/item/weapon/storage/backpack/chemistry + name = "chemistry backpack" + desc = "It's an orange backpack which was designed to hold beakers, pill bottles and bottles." + icon_state = "backpack_orange" + +/* + * Duffle Types + */ + +/obj/item/weapon/storage/backpack/dufflebag + name = "dufflebag" + desc = "A large dufflebag for holding extra things." + icon_state = "duffle" + slowdown = 0.5 + var/tilted = 0 + var/can_tilt = 1 + max_storage_space = INVENTORY_DUFFLEBAG_SPACE + +/obj/item/weapon/storage/backpack/dufflebag/Initialize() + . = ..() + if(prob(50)) + icon_state = "[icon_state]_tilted" + tilted = 1 + +/obj/item/weapon/storage/backpack/dufflebag/verb/tilt() + set name = "Adjust Duffelbag Angle" + set desc = "Adjust the angle of your dufflebag for cosmetic effect" + set category = "Object" + set src in usr + if(!usr.canmove || usr.stat || usr.restrained()) + return + if(!can_tilt) + to_chat(usr, "[src] can't be adjusted like that.") + return + if(tilted) + icon_state = "[initial(icon_state)]" + to_chat(usr, "You adjust the angle of \the [src] to rest across your lower back.") + tilted = 0 + else + icon_state = "[icon_state]_tilted" + to_chat(usr, "You adjust the angle of \the [src] to rest diagonally across your back.") + tilted = 1 + update_icon() + usr.update_inv_back() + +/obj/item/weapon/storage/backpack/dufflebag/syndie + name = "black dufflebag" + desc = "A large dufflebag for holding extra tactical supplies. This one appears to be made out of lighter material than usual." + icon_state = "duffle_syndie" + slowdown = 0 + +/obj/item/weapon/storage/backpack/dufflebag/syndie/med + name = "medical dufflebag" + desc = "A large dufflebag for holding extra tactical medical supplies. This one appears to be made out of lighter material than usual." + icon_state = "duffle_syndiemed" + +/obj/item/weapon/storage/backpack/dufflebag/syndie/ammo + name = "ammunition dufflebag" + desc = "A large dufflebag for holding extra weapons ammunition and supplies. This one appears to be made out of lighter material than usual." + icon_state = "duffle_syndieammo" + +/obj/item/weapon/storage/backpack/dufflebag/captain + name = "site manager's dufflebag" + desc = "A large dufflebag for holding extra captainly goods." + icon_state = "duffle_captain" + +/obj/item/weapon/storage/backpack/dufflebag/med + name = "medical dufflebag" + desc = "A large dufflebag for holding extra medical supplies." + icon_state = "duffle_medical" + +/obj/item/weapon/storage/backpack/dufflebag/emt + name = "EMT dufflebag" + desc = "A large dufflebag for holding extra medical supplies. This one has reflective stripes!" + icon_state = "duffle_emt" + +/obj/item/weapon/storage/backpack/dufflebag/sec + name = "security dufflebag" + desc = "A large dufflebag for holding extra security supplies and ammunition." + icon_state = "duffle_security" + +/obj/item/weapon/storage/backpack/dufflebag/eng + name = "industrial dufflebag" + desc = "A large dufflebag for holding extra tools and supplies." + icon_state = "duffle_industrial" + +/obj/item/weapon/storage/backpack/dufflebag/sci + name = "science dufflebag" + desc = "A large dufflebag for holding circuits and beakers." + icon_state = "duffle_science" + +/obj/item/weapon/storage/backpack/dufflebag/drone + name = "drone dufflebag" + desc = "A large dufflebag for holding small robots? Or maybe it's one used by robots!" + icon_state = "duffle_drone" + +/obj/item/weapon/storage/backpack/dufflebag/cursed + name = "cursed dufflebag" + desc = "That probably shouldn't be moving..." + icon_state = "duffle_curse" + +/* + * Satchel Types + */ + +/obj/item/weapon/storage/backpack/satchel + name = "leather satchel" + desc = "It's a very fancy satchel made with fine leather." + icon_state = "satchel" + +/obj/item/weapon/storage/backpack/satchel/withwallet + starts_with = list(/obj/item/weapon/storage/wallet/random) + +/obj/item/weapon/storage/backpack/satchel/norm + name = "satchel" + desc = "A trendy looking satchel." + icon_state = "satchel_grey" + +/obj/item/weapon/storage/backpack/satchel/white + name = "white satchel" + icon_state = "satchel_white" + +/obj/item/weapon/storage/backpack/satchel/fancy + name = "fancy satchel" + icon_state = "satchel_fancy" + +/obj/item/weapon/storage/backpack/satchel/military + name = "military satchel" + icon_state = "satchel_military" + +/obj/item/weapon/storage/backpack/satchel/eng + name = "industrial satchel" + desc = "A tough satchel with extra pockets." + icon_state = "satchel_industrial" + +/obj/item/weapon/storage/backpack/satchel/med + name = "medical satchel" + desc = "A sterile satchel used in medical departments." + icon_state = "satchel_medical" + +/obj/item/weapon/storage/backpack/satchel/vir + name = "virologist satchel" + desc = "A sterile satchel with virologist colours." + icon_state = "satchel_green" + +/obj/item/weapon/storage/backpack/satchel/chem + name = "chemist satchel" + desc = "A sterile satchel with chemist colours." + icon_state = "satchel_orange" + +/obj/item/weapon/storage/backpack/satchel/gen + name = "geneticist satchel" + desc = "A sterile satchel with geneticist colours." + icon_state = "satchel_blue" + +/obj/item/weapon/storage/backpack/satchel/tox + name = "scientist satchel" + desc = "Useful for holding research materials." + icon_state = "satchel_purple" + +/obj/item/weapon/storage/backpack/satchel/sec + name = "security satchel" + desc = "A robust satchel for security related needs." + icon_state = "satchel_security" + +/obj/item/weapon/storage/backpack/satchel/hyd + name = "hydroponics satchel" + desc = "A green satchel for plant related work." + icon_state = "satchel_hydro" + +/obj/item/weapon/storage/backpack/satchel/cap + name = "site manager's satchel" + desc = "An exclusive satchel for officers." + icon_state = "satchel_captain" + +//ERT backpacks. +/obj/item/weapon/storage/backpack/ert + name = "emergency response team backpack" + desc = "A spacious backpack with lots of pockets, used by members of the Emergency Response Team." + icon_state = "ert_commander" + +//Commander +/obj/item/weapon/storage/backpack/ert/commander + name = "emergency response team commander backpack" + desc = "A spacious backpack with lots of pockets, worn by the commander of an Emergency Response Team." + +//Security +/obj/item/weapon/storage/backpack/ert/security + name = "emergency response team security backpack" + desc = "A spacious backpack with lots of pockets, worn by security members of an Emergency Response Team." + icon_state = "ert_security" + +//Engineering +/obj/item/weapon/storage/backpack/ert/engineer + name = "emergency response team engineer backpack" + desc = "A spacious backpack with lots of pockets, worn by engineering members of an Emergency Response Team." + icon_state = "ert_engineering" + +//Medical +/obj/item/weapon/storage/backpack/ert/medical + name = "emergency response team medical backpack" + desc = "A spacious backpack with lots of pockets, worn by medical members of an Emergency Response Team." + icon_state = "ert_medical" + +/* + * Courier Bags + */ + +/obj/item/weapon/storage/backpack/messenger + name = "messenger bag" + desc = "A sturdy backpack worn over one shoulder." + icon_state = "courier" + item_state_slots = list(slot_r_hand_str = "satchel_grey", slot_l_hand_str = "satchel_grey") + +/obj/item/weapon/storage/backpack/messenger/chem + name = "chemistry messenger bag" + desc = "A serile backpack worn over one shoulder. This one is in Chemsitry colors." + icon_state = "courier_chemistry" + item_state_slots = list(slot_r_hand_str = "satchel_orange", slot_l_hand_str = "satchel_orange") + +/obj/item/weapon/storage/backpack/messenger/med + name = "medical messenger bag" + desc = "A sterile backpack worn over one shoulder used in medical departments." + icon_state = "courier_medical" + item_state_slots = list(slot_r_hand_str = "satchel_medical", slot_l_hand_str = "satchel_medical") + +/obj/item/weapon/storage/backpack/messenger/viro + name = "virology messenger bag" + desc = "A sterile backpack worn over one shoulder. This one is in Virology colors." + icon_state = "courier_virology" + item_state_slots = list(slot_r_hand_str = "satchel_green", slot_l_hand_str = "satchel_green") + +/obj/item/weapon/storage/backpack/messenger/tox + name = "research messenger bag" + desc = "A backpack worn over one shoulder. Useful for holding science materials." + icon_state = "courier_toxins" + item_state_slots = list(slot_r_hand_str = "satchel_purple", slot_l_hand_str = "satchel_purple") + +/obj/item/weapon/storage/backpack/messenger/com + name = "command messenger bag" + desc = "A special backpack worn over one shoulder. This one is made specifically for officers." + icon_state = "courier_captain" + item_state_slots = list(slot_r_hand_str = "satchel_captain", slot_l_hand_str = "satchel_captain") + +/obj/item/weapon/storage/backpack/messenger/engi + name = "engineering messenger bag" + icon_state = "courier_industrial" + item_state_slots = list(slot_r_hand_str = "satchel_industrial", slot_l_hand_str = "satchel_industrial") + +/obj/item/weapon/storage/backpack/messenger/hyd + name = "hydroponics messenger bag" + desc = "A backpack worn over one shoulder. This one is designed for plant-related work." + icon_state = "courier_hydro" + item_state_slots = list(slot_r_hand_str = "satchel_hydro", slot_l_hand_str = "satchel_hydro") + +/obj/item/weapon/storage/backpack/messenger/sec + name = "security messenger bag" + desc = "A tactical backpack worn over one shoulder. This one is in Security colors." + icon_state = "courier_security" + item_state_slots = list(slot_r_hand_str = "satchel_security", slot_l_hand_str = "satchel_security") + +/obj/item/weapon/storage/backpack/messenger/black + icon_state = "courier_black" + + +/* + * Sport Bags + */ + +/obj/item/weapon/storage/backpack/sport + name = "sports backpack" + icon_state = "backsport" + +/obj/item/weapon/storage/backpack/sport/white + name = "white sports backpack" + icon_state = "backsport_white" + +/obj/item/weapon/storage/backpack/sport/fancy + name = "fancy sports backpack" + icon_state = "backsport_fancy" + +/obj/item/weapon/storage/backpack/sport/vir + name = "virologist sports backpack" + desc = "A sterile sports backpack with virologist colours." + icon_state = "backsport_green" + +/obj/item/weapon/storage/backpack/sport/chem + name = "chemist sports backpack" + desc = "A sterile sports backpack with chemist colours." + icon_state = "backsport_orange" + +/obj/item/weapon/storage/backpack/sport/gen + name = "geneticist sports backpack" + desc = "A sterile sports backpack with geneticist colours." + icon_state = "backsport_blue" + +/obj/item/weapon/storage/backpack/sport/tox + name = "scientist sports backpack" + desc = "Useful for holding research materials." + icon_state = "backsport_purple" + +/obj/item/weapon/storage/backpack/sport/sec + name = "security sports backpack" + desc = "A robust sports backpack for security related needs." + icon_state = "backsport_security" + +/obj/item/weapon/storage/backpack/sport/hyd + name = "hydroponics sports backpack" + desc = "A green sports backpack for plant related work." + icon_state = "backsport_hydro" + +//Purses +/obj/item/weapon/storage/backpack/purse + name = "purse" + desc = "A small, fashionable bag typically worn over the shoulder." + icon_state = "purse" + item_state_slots = list(slot_r_hand_str = "lgpurse", slot_l_hand_str = "lgpurse") + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_NORMAL * 5 + +//Parachutes +/obj/item/weapon/storage/backpack/parachute + name = "parachute" + desc = "A specially made backpack, designed to help one survive jumping from incredible heights. It sacrifices some storage space for that added functionality." + icon_state = "parachute" + item_state_slots = list(slot_r_hand_str = "backpack", slot_l_hand_str = "backpack") + max_storage_space = ITEMSIZE_COST_NORMAL * 5 + +/obj/item/weapon/storage/backpack/parachute/examine(mob/user) + . = ..() + if(Adjacent(user)) + if(parachute) + . += "It seems to be packed." + else + . += "It seems to be unpacked." + +/obj/item/weapon/storage/backpack/parachute/handleParachute() + parachute = FALSE //If you parachute in, the parachute has probably been used. + +/obj/item/weapon/storage/backpack/parachute/verb/pack_parachute() + + set name = "Pack/Unpack Parachute" + set category = "Object" + set src in usr + + if(!istype(src.loc, /mob/living)) + return + + var/mob/living/carbon/human/H = usr + + if(!istype(H)) + return + if(H.stat) + return + if(H.back == src) + to_chat(H, "How do you expect to work on \the [src] while it's on your back?") + return + + if(!parachute) //This packs the parachute + H.visible_message("\The [H] starts to pack \the [src]!", \ + "You start to pack \the [src]!", \ + "You hear the shuffling of cloth.") + if(do_after(H, 50)) + H.visible_message("\The [H] finishes packing \the [src]!", \ + "You finish packing \the [src]!", \ + "You hear the shuffling of cloth.") + parachute = TRUE + else + H.visible_message("\The [src] gives up on packing \the [src]!", \ + "You give up on packing \the [src]!") + return + else //This unpacks the parachute + H.visible_message("\The [src] starts to unpack \the [src]!", \ + "You start to unpack \the [src]!", \ + "You hear the shuffling of cloth.") + if(do_after(H, 25)) + H.visible_message("\The [src] finishes unpacking \the [src]!", \ + "You finish unpacking \the [src]!", \ + "You hear the shuffling of cloth.") + parachute = FALSE + else + H.visible_message("\The [src] decides not to unpack \the [src]!", \ + "You decide not to unpack \the [src]!") + return + +/obj/item/weapon/storage/backpack/satchel/ranger + name = "ranger satchel" + desc = "A satchel designed for the Go Go ERT Rangers series to allow for slightly bigger carry capacity for the ERT-Rangers.\ + Unlike the show claims, it is not a phoron-enhanced satchel of holding with plot-relevant content." + icon = 'icons/obj/clothing/ranger.dmi' icon_state = "ranger_satchel" \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm index 01e0d7f2df6..a7137dded17 100644 --- a/code/game/objects/items/weapons/storage/bags.dm +++ b/code/game/objects/items/weapons/storage/bags.dm @@ -1,495 +1,495 @@ -/* - * These absorb the functionality of the plant bag, ore satchel, etc. - * They use the use_to_pickup, quick_gather, and quick_empty functions - * that were already defined in weapon/storage, but which had been - * re-implemented in other classes. - * - * Contains: - * Generic non-item - * Trash Bag - * Plastic Bag - * Mining Satchel - * Plant Bag - * Sheet Snatcher - * Sheet Snatcher (Cyborg) - * Cash Bag - * Chemistry Bag - * Food Bag - * Food Bag (Service Hound) - * Evidence Bag - * - * -Sayu - */ - - -// ----------------------------- -// Generic non-item -// ----------------------------- -/obj/item/weapon/storage/bag - allow_quick_gather = 1 - allow_quick_empty = 1 - display_contents_with_number = 0 // UNStABLE AS FuCK, turn on when it stops crashing clients - use_to_pickup = TRUE - slot_flags = SLOT_BELT - drop_sound = 'sound/items/drop/backpack.ogg' - pickup_sound = 'sound/items/pickup/backpack.ogg' - -// ----------------------------- -// Trash bag -// ----------------------------- -/obj/item/weapon/storage/bag/trash - name = "trash bag" - desc = "It's the heavy-duty black polymer kind. Time to take out the trash!" - icon = 'icons/obj/janitor.dmi' - icon_state = "trashbag0" - item_state_slots = list(slot_r_hand_str = "trashbag", slot_l_hand_str = "trashbag") - drop_sound = 'sound/items/drop/wrapper.ogg' - pickup_sound = 'sound/items/pickup/wrapper.ogg' - - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_SMALL - max_storage_space = ITEMSIZE_SMALL * 21 - can_hold = list() // any - cant_hold = list(/obj/item/weapon/disk/nuclear) - -/obj/item/weapon/storage/bag/trash/update_icon() - if(contents.len == 0) - icon_state = "trashbag0" - else if(contents.len < 9) - icon_state = "trashbag1" - else if(contents.len < 18) - icon_state = "trashbag2" - else icon_state = "trashbag3" - -/obj/item/weapon/storage/bag/trash/holding - name = "trash bag of holding" - desc = "The latest and greatest in custodial convenience, a trashbag that is capable of holding vast quantities of garbage." - icon_state = "bluetrashbag" - origin_tech = list(TECH_BLUESPACE = 3) - max_w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_NORMAL * 10 // Slightly less than BoH - -/obj/item/weapon/storage/bag/trash/holding/update_icon() - return - -// ----------------------------- -// Plastic Bag -// ----------------------------- -/obj/item/weapon/storage/bag/plasticbag - name = "plastic bag" - desc = "It's a very flimsy, very noisy alternative to a bag." - icon = 'icons/obj/trash.dmi' - icon_state = "plasticbag" - drop_sound = 'sound/items/drop/wrapper.ogg' - pickup_sound = 'sound/items/pickup/wrapper.ogg' - - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_SMALL - can_hold = list() // any - cant_hold = list(/obj/item/weapon/disk/nuclear) - -// ----------------------------- -// Mining Satchel -// ----------------------------- -/* - * Mechoid - Orebags are the most common quick-gathering thing, and also have tons of lag associated with it. - * Their checks are going to be hyper-simplified due to this, and their INCREDIBLY singular target contents. - */ -/obj/item/weapon/storage/bag/ore - name = "mining satchel" - desc = "This little bugger can be used to store and transport ores." - icon = 'icons/obj/mining.dmi' - icon_state = "satchel" - slot_flags = SLOT_BELT | SLOT_POCKET - w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_NORMAL * 25 - max_w_class = ITEMSIZE_NORMAL - can_hold = list(/obj/item/weapon/ore) - var/current_capacity = 0 - var/max_pickup = 100 //How much ore can be picked up in one go. There to prevent someone from walking on a turf with 10000 ore and making the server cry. - var/list/stored_ore = list( - "sand" = 0, - "hematite" = 0, - "carbon" = 0, - "raw copper" = 0, - "raw tin" = 0, - "void opal" = 0, - "painite" = 0, - "quartz" = 0, - "raw bauxite" = 0, - "phoron" = 0, - "silver" = 0, - "gold" = 0, - "marble" = 0, - "uranium" = 0, - "diamond" = 0, - "platinum" = 0, - "lead" = 0, - "mhydrogen" = 0, - "verdantium" = 0, - "rutile" = 0) - var/last_update = 0 - -/obj/item/weapon/storage/bag/ore/holding - name = "mining satchel of holding" - desc = "Like a mining satchel, but when you put your hand in, you're pretty sure you can feel time itself." - icon_state = "satchel_bspace" - max_storage_space = ITEMSIZE_COST_NORMAL * 15000 // This should never, ever, ever be reached. - -/obj/item/weapon/storage/bag/ore/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(current_capacity >= max_storage_space) - to_chat(user, "\the [src] is too full to possibly fit anything else inside of it.") - return - - if (istype(W, /obj/item/weapon/ore)) - var/obj/item/weapon/ore/ore = W - stored_ore[ore.material]++ - current_capacity++ - user.remove_from_mob(W) - qdel(ore) - -/obj/item/weapon/storage/bag/ore/remove_from_storage(obj/item/W as obj, atom/new_location) - if(!istype(W)) return 0 - - if(new_location) - if(ismob(loc)) - W.dropped(usr) - if(ismob(new_location)) - W.hud_layerise() - else - W.reset_plane_and_layer() - W.forceMove(new_location) - else - W.forceMove(get_turf(src)) - - W.on_exit_storage(src) - update_icon() - return 1 - -/obj/item/weapon/storage/bag/ore/gather_all(turf/T as turf, mob/user as mob, var/silent = 0) - var/success = 0 - var/failure = 0 - var/current_pickup = 0 - var/max_pickup_reached = 0 - for(var/obj/item/weapon/ore/O in T) //Only ever grabs ores. Doesn't do any extraneous checks, as all ore is the same size. Tons of checks means it causes hanging for up to three seconds. - if(current_capacity >= max_storage_space) - failure = 1 - break - if(current_pickup >= max_pickup) - max_pickup_reached = 1 - break - var/obj/item/weapon/ore/ore = O - stored_ore[ore.material]++ - current_capacity++ - current_pickup++ - qdel(ore) - success = 1 - if(!silent) //Let's do a single check and then do more instead of a bunch at once. - if(success && !failure && !max_pickup_reached) //Picked stuff up, did not reach capacity, did not reach max_pickup. - to_chat(user, "You put everything in [src].") - else if(success && failure) //Picked stuff up to capacity. - to_chat(user, "You fill the [src].") - else if(success && max_pickup_reached) //Picked stuff up to the max_pickup - to_chat(user, "You fill the [src] with as much as you can grab in one go.") - else //Failed. The bag is full. - to_chat(user, "You fail to pick anything up with \the [src].") - if(istype(user.pulling, /obj/structure/ore_box)) //Bit of a crappy way to do this, as it doubles spam for the user, but it works. //Then let me fix it. ~CL. - var/obj/structure/ore_box/OB = user.pulling - for(var/ore in stored_ore) - if(stored_ore[ore] > 0) - var/ore_amount = stored_ore[ore] // How many ores does the satchel have? - OB.stored_ore[ore] += ore_amount // Add the ore to the box - stored_ore[ore] = 0 // Set the value of the ore in the satchel to 0. - current_capacity = 0 // Set the amount of ore in the satchel to 0. - current_pickup = 0 - -/obj/item/weapon/storage/bag/ore/equipped(mob/user) - ..() - if(user.get_inventory_slot(src) == slot_wear_suit || slot_l_hand || slot_l_hand || slot_belt) //Basically every place they can go. Makes sure it doesn't unregister if moved to other slots. - GLOB.moved_event.register(user, src, /obj/item/weapon/storage/bag/ore/proc/autoload, user) - -/obj/item/weapon/storage/bag/ore/dropped(mob/user) - ..() - if(user.get_inventory_slot(src) == slot_wear_suit || slot_l_hand || slot_l_hand || slot_belt) //See above. This should really be a define. - GLOB.moved_event.register(user, src, /obj/item/weapon/storage/bag/ore/proc/autoload, user) - else - GLOB.moved_event.unregister(user, src) - -/obj/item/weapon/storage/bag/ore/proc/autoload(mob/user) - var/obj/item/weapon/ore/O = locate() in get_turf(src) - if(O) - gather_all(get_turf(src), user) - -/obj/item/weapon/storage/bag/ore/proc/rangedload(atom/A, mob/user) - var/obj/item/weapon/ore/O = locate() in get_turf(A) - if(O) - gather_all(get_turf(A), user) - -/obj/item/weapon/storage/bag/ore/examine(mob/user) - . = ..() - - if(!Adjacent(user)) //Can only check the contents of ore bags if you can physically reach them. - return . - - if(istype(user, /mob/living)) - add_fingerprint(user) - - . += "It holds:" - var/has_ore = 0 - for(var/ore in stored_ore) - if(stored_ore[ore] > 0) - . += "- [stored_ore[ore]] [ore]" - has_ore = 1 - if(!has_ore) - . += "Nothing." - -/obj/item/weapon/storage/bag/ore/open(mob/user as mob) //No opening it for the weird UI of having shit-tons of ore inside it. - user.examinate(src) - -// ----------------------------- -// Plant bag -// ----------------------------- -/obj/item/weapon/storage/bag/plants - name = "plant bag" - icon = 'icons/obj/hydroponics_machines_vr.dmi' - icon_state = "plantbag" - desc = "A sturdy bag used to transport fresh produce with ease." - max_storage_space = ITEMSIZE_COST_NORMAL * 25 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/grown,/obj/item/seeds,/obj/item/weapon/grown) - -/obj/item/weapon/storage/bag/plants/large - name = "large plant bag" - icon_state = "large_plantbag" - desc = "A large and sturdy bag used to transport fresh produce with ease." - max_storage_space = ITEMSIZE_COST_NORMAL * 50 - -// ----------------------------- -// Sheet Snatcher -// ----------------------------- -// Because it stacks stacks, this doesn't operate normally. -// However, making it a storage/bag allows us to reuse existing code in some places. -Sayu - -/obj/item/weapon/storage/bag/sheetsnatcher - name = "sheet snatcher" - icon = 'icons/obj/mining.dmi' - icon_state = "sheetsnatcher" - desc = "A patented storage system designed for any kind of mineral sheet." - - var/capacity = 300; //the number of sheets it can carry. - w_class = ITEMSIZE_NORMAL - storage_slots = 7 - - allow_quick_empty = 1 // this function is superceded - -/obj/item/weapon/storage/bag/sheetsnatcher/can_be_inserted(obj/item/W as obj, stop_messages = 0) - if(!istype(W,/obj/item/stack/material)) - if(!stop_messages) - to_chat(usr, "The snatcher does not accept [W].") - return 0 - var/current = 0 - for(var/obj/item/stack/material/S in contents) - current += S.get_amount() - if(capacity == current)//If it's full, you're done - if(!stop_messages) - to_chat(usr, "The snatcher is full.") - return 0 - return 1 - - -// Modified handle_item_insertion. Would prefer not to, but... -/obj/item/weapon/storage/bag/sheetsnatcher/handle_item_insertion(obj/item/W as obj, prevent_warning = 0) - var/obj/item/stack/material/S = W - if(!istype(S)) return 0 - - var/amount - var/inserted = 0 - var/current = 0 - for(var/obj/item/stack/material/S2 in contents) - current += S2.get_amount() - if(capacity < current + S.get_amount())//If the stack will fill it up - amount = capacity - current - else - amount = S.get_amount() - - for(var/obj/item/stack/material/sheet in contents) - if(S.type == sheet.type) - // we are violating the amount limitation because these are not sane objects - sheet.set_amount(sheet.get_amount() + amount, TRUE) - S.use(amount) // will qdel() if we use it all - inserted = 1 - break - - if(!inserted) - usr.remove_from_mob(S) - if (usr.client && usr.s_active != src) - usr.client.screen -= S - S.dropped(usr) - S.loc = src - - orient2hud(usr) - if(usr.s_active) - usr.s_active.show_to(usr) - update_icon() - return 1 - -// Sets up numbered display to show the stack size of each stored mineral -// NOTE: numbered display is turned off currently because it's broken -/obj/item/weapon/storage/bag/sheetsnatcher/orient2hud(mob/user as mob) - var/adjusted_contents = contents.len - - //Numbered contents display - var/list/datum/numbered_display/numbered_contents - if(display_contents_with_number) - numbered_contents = list() - adjusted_contents = 0 - for(var/obj/item/stack/material/I in contents) - adjusted_contents++ - var/datum/numbered_display/D = new/datum/numbered_display(I) - D.number = I.get_amount() - numbered_contents.Add( D ) - - var/row_num = 0 - var/col_count = min(7,storage_slots) -1 - if (adjusted_contents > 7) - row_num = round((adjusted_contents-1) / 7) // 7 is the maximum allowed width. - src.slot_orient_objs(row_num, col_count, numbered_contents) - return - -// Modified quick_empty verb drops appropriate sized stacks -/obj/item/weapon/storage/bag/sheetsnatcher/quick_empty() - var/location = get_turf(src) - for(var/obj/item/stack/material/S in contents) - var/cur_amount = S.get_amount() - var/full_stacks = round(cur_amount / S.max_amount) // Floor of current/max is amount of full stacks we make - var/remainder = cur_amount % S.max_amount // Current mod max is remainder after full sheets removed - for(var/i = 1 to full_stacks) - new S.type(location, S.max_amount) - if(remainder) - new S.type(location, remainder) - S.set_amount(0) - orient2hud(usr) - if(usr.s_active) - usr.s_active.show_to(usr) - update_icon() - -// Instead of removing -/obj/item/weapon/storage/bag/sheetsnatcher/remove_from_storage(obj/item/W as obj, atom/new_location) - var/obj/item/stack/material/S = W - if(!istype(S)) return 0 - - //I would prefer to drop a new stack, but the item/attack_hand code - // that calls this can't recieve a different object than you clicked on. - //Therefore, make a new stack internally that has the remainder. - // -Sayu - - if(S.get_amount() > S.max_amount) - var/newstack_amt = S.get_amount() - S.max_amount - new S.type(src, newstack_amt) // The one we'll keep to replace the one we give - S.set_amount(S.max_amount) // The one we hand to the clicker - - return ..(S,new_location) - -// ----------------------------- -// Sheet Snatcher (Cyborg) -// ----------------------------- - -/obj/item/weapon/storage/bag/sheetsnatcher/borg - name = "sheet snatcher 9000" - desc = null - capacity = 500//Borgs get more because >specialization - -// ----------------------------- -// Cash Bag -// ----------------------------- - -/obj/item/weapon/storage/bag/cash - name = "cash bag" - icon = 'icons/obj/storage.dmi' - icon_state = "cashbag" - desc = "A bag for carrying lots of cash. It's got a big dollar sign printed on the front." - max_storage_space = ITEMSIZE_COST_NORMAL * 25 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/coin,/obj/item/weapon/spacecash,/obj/item/weapon/spacecasinocash) - -// ----------------------------- -// Chemistry Bag -// ----------------------------- -/obj/item/weapon/storage/bag/chemistry - name = "chemistry bag" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "chembag" - desc = "A bag for storing pills, patches, and bottles." - max_storage_space = 200 - w_class = ITEMSIZE_LARGE - slowdown = 3 - can_hold = list(/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/glass/bottle) - -// ----------------------------- -// Xeno Bag -// ----------------------------- -/obj/item/weapon/storage/bag/xeno - name = "xenobiology bag" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "xenobag" - desc = "A bag for storing various slime products." - max_storage_space = ITEMSIZE_COST_SMALL * 12 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/slime_extract,/obj/item/slimepotion, /obj/item/weapon/reagent_containers/food/snacks/monkeycube) - -// ----------------------------- -// Virology Bag -// ----------------------------- -/obj/item/weapon/storage/bag/virology - name = "virology bag" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "biobag" - desc = "A bag for storing various biological products." - max_storage_space = ITEMSIZE_COST_SMALL * 12 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/reagent_containers/glass/beaker/vial/,/obj/item/weapon/virusdish/) - -// ----------------------------- -// Food Bag -// ----------------------------- -/obj/item/weapon/storage/bag/food - name = "food bag" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "foodbag" - desc = "A bag for storing foods of all kinds." - max_storage_space = ITEMSIZE_COST_NORMAL * 25 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/reagent_containers/food/snacks,/obj/item/weapon/reagent_containers/food/condiment) - -// ----------------------------- -// Food Bag (Service Hound) -// ----------------------------- -/obj/item/weapon/storage/bag/serviceborg - name = "service bag" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "foodbag" - desc = "An intergrated bag for storing things of all kinds." - max_storage_space = ITEMSIZE_COST_NORMAL * 25 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/reagent_containers/food/snacks,/obj/item/weapon/reagent_containers/food/condiment, - /obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/glass/bottle,/obj/item/weapon/coin,/obj/item/weapon/spacecash, - /obj/item/weapon/reagent_containers/food/snacks/grown,/obj/item/seeds,/obj/item/weapon/grown,/obj/item/weapon/reagent_containers/pill) - -// ----------------------------- -// Evidence Bag -// ----------------------------- -/obj/item/weapon/storage/bag/detective - name = "secure satchel" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "detbag" - desc = "A bag for storing investigation things. You know, securely." - max_storage_space = ITEMSIZE_COST_NORMAL * 15 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_SMALL +/* + * These absorb the functionality of the plant bag, ore satchel, etc. + * They use the use_to_pickup, quick_gather, and quick_empty functions + * that were already defined in weapon/storage, but which had been + * re-implemented in other classes. + * + * Contains: + * Generic non-item + * Trash Bag + * Plastic Bag + * Mining Satchel + * Plant Bag + * Sheet Snatcher + * Sheet Snatcher (Cyborg) + * Cash Bag + * Chemistry Bag + * Food Bag + * Food Bag (Service Hound) + * Evidence Bag + * + * -Sayu + */ + + +// ----------------------------- +// Generic non-item +// ----------------------------- +/obj/item/weapon/storage/bag + allow_quick_gather = 1 + allow_quick_empty = 1 + display_contents_with_number = 0 // UNStABLE AS FuCK, turn on when it stops crashing clients + use_to_pickup = TRUE + slot_flags = SLOT_BELT + drop_sound = 'sound/items/drop/backpack.ogg' + pickup_sound = 'sound/items/pickup/backpack.ogg' + +// ----------------------------- +// Trash bag +// ----------------------------- +/obj/item/weapon/storage/bag/trash + name = "trash bag" + desc = "It's the heavy-duty black polymer kind. Time to take out the trash!" + icon = 'icons/obj/janitor.dmi' + icon_state = "trashbag0" + item_state_slots = list(slot_r_hand_str = "trashbag", slot_l_hand_str = "trashbag") + drop_sound = 'sound/items/drop/wrapper.ogg' + pickup_sound = 'sound/items/pickup/wrapper.ogg' + + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_SMALL + max_storage_space = ITEMSIZE_SMALL * 21 + can_hold = list() // any + cant_hold = list(/obj/item/weapon/disk/nuclear) + +/obj/item/weapon/storage/bag/trash/update_icon() + if(contents.len == 0) + icon_state = "trashbag0" + else if(contents.len < 9) + icon_state = "trashbag1" + else if(contents.len < 18) + icon_state = "trashbag2" + else icon_state = "trashbag3" + +/obj/item/weapon/storage/bag/trash/holding + name = "trash bag of holding" + desc = "The latest and greatest in custodial convenience, a trashbag that is capable of holding vast quantities of garbage." + icon_state = "bluetrashbag" + origin_tech = list(TECH_BLUESPACE = 3) + max_w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_NORMAL * 10 // Slightly less than BoH + +/obj/item/weapon/storage/bag/trash/holding/update_icon() + return + +// ----------------------------- +// Plastic Bag +// ----------------------------- +/obj/item/weapon/storage/bag/plasticbag + name = "plastic bag" + desc = "It's a very flimsy, very noisy alternative to a bag." + icon = 'icons/obj/trash.dmi' + icon_state = "plasticbag" + drop_sound = 'sound/items/drop/wrapper.ogg' + pickup_sound = 'sound/items/pickup/wrapper.ogg' + + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_SMALL + can_hold = list() // any + cant_hold = list(/obj/item/weapon/disk/nuclear) + +// ----------------------------- +// Mining Satchel +// ----------------------------- +/* + * Mechoid - Orebags are the most common quick-gathering thing, and also have tons of lag associated with it. + * Their checks are going to be hyper-simplified due to this, and their INCREDIBLY singular target contents. + */ +/obj/item/weapon/storage/bag/ore + name = "mining satchel" + desc = "This little bugger can be used to store and transport ores." + icon = 'icons/obj/mining.dmi' + icon_state = "satchel" + slot_flags = SLOT_BELT | SLOT_POCKET + w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_NORMAL * 25 + max_w_class = ITEMSIZE_NORMAL + can_hold = list(/obj/item/weapon/ore) + var/current_capacity = 0 + var/max_pickup = 100 //How much ore can be picked up in one go. There to prevent someone from walking on a turf with 10000 ore and making the server cry. + var/list/stored_ore = list( + "sand" = 0, + "hematite" = 0, + "carbon" = 0, + "raw copper" = 0, + "raw tin" = 0, + "void opal" = 0, + "painite" = 0, + "quartz" = 0, + "raw bauxite" = 0, + "phoron" = 0, + "silver" = 0, + "gold" = 0, + "marble" = 0, + "uranium" = 0, + "diamond" = 0, + "platinum" = 0, + "lead" = 0, + "mhydrogen" = 0, + "verdantium" = 0, + "rutile" = 0) + var/last_update = 0 + +/obj/item/weapon/storage/bag/ore/holding + name = "mining satchel of holding" + desc = "Like a mining satchel, but when you put your hand in, you're pretty sure you can feel time itself." + icon_state = "satchel_bspace" + max_storage_space = ITEMSIZE_COST_NORMAL * 15000 // This should never, ever, ever be reached. + +/obj/item/weapon/storage/bag/ore/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(current_capacity >= max_storage_space) + to_chat(user, "\the [src] is too full to possibly fit anything else inside of it.") + return + + if (istype(W, /obj/item/weapon/ore)) + var/obj/item/weapon/ore/ore = W + stored_ore[ore.material]++ + current_capacity++ + user.remove_from_mob(W) + qdel(ore) + +/obj/item/weapon/storage/bag/ore/remove_from_storage(obj/item/W as obj, atom/new_location) + if(!istype(W)) return 0 + + if(new_location) + if(ismob(loc)) + W.dropped(usr) + if(ismob(new_location)) + W.hud_layerise() + else + W.reset_plane_and_layer() + W.forceMove(new_location) + else + W.forceMove(get_turf(src)) + + W.on_exit_storage(src) + update_icon() + return 1 + +/obj/item/weapon/storage/bag/ore/gather_all(turf/T as turf, mob/user as mob, var/silent = 0) + var/success = 0 + var/failure = 0 + var/current_pickup = 0 + var/max_pickup_reached = 0 + for(var/obj/item/weapon/ore/O in T) //Only ever grabs ores. Doesn't do any extraneous checks, as all ore is the same size. Tons of checks means it causes hanging for up to three seconds. + if(current_capacity >= max_storage_space) + failure = 1 + break + if(current_pickup >= max_pickup) + max_pickup_reached = 1 + break + var/obj/item/weapon/ore/ore = O + stored_ore[ore.material]++ + current_capacity++ + current_pickup++ + qdel(ore) + success = 1 + if(!silent) //Let's do a single check and then do more instead of a bunch at once. + if(success && !failure && !max_pickup_reached) //Picked stuff up, did not reach capacity, did not reach max_pickup. + to_chat(user, "You put everything in [src].") + else if(success && failure) //Picked stuff up to capacity. + to_chat(user, "You fill the [src].") + else if(success && max_pickup_reached) //Picked stuff up to the max_pickup + to_chat(user, "You fill the [src] with as much as you can grab in one go.") + else //Failed. The bag is full. + to_chat(user, "You fail to pick anything up with \the [src].") + if(istype(user.pulling, /obj/structure/ore_box)) //Bit of a crappy way to do this, as it doubles spam for the user, but it works. //Then let me fix it. ~CL. + var/obj/structure/ore_box/OB = user.pulling + for(var/ore in stored_ore) + if(stored_ore[ore] > 0) + var/ore_amount = stored_ore[ore] // How many ores does the satchel have? + OB.stored_ore[ore] += ore_amount // Add the ore to the box + stored_ore[ore] = 0 // Set the value of the ore in the satchel to 0. + current_capacity = 0 // Set the amount of ore in the satchel to 0. + current_pickup = 0 + +/obj/item/weapon/storage/bag/ore/equipped(mob/user) + ..() + if(user.get_inventory_slot(src) == slot_wear_suit || slot_l_hand || slot_l_hand || slot_belt) //Basically every place they can go. Makes sure it doesn't unregister if moved to other slots. + GLOB.moved_event.register(user, src, /obj/item/weapon/storage/bag/ore/proc/autoload, user) + +/obj/item/weapon/storage/bag/ore/dropped(mob/user) + ..() + if(user.get_inventory_slot(src) == slot_wear_suit || slot_l_hand || slot_l_hand || slot_belt) //See above. This should really be a define. + GLOB.moved_event.register(user, src, /obj/item/weapon/storage/bag/ore/proc/autoload, user) + else + GLOB.moved_event.unregister(user, src) + +/obj/item/weapon/storage/bag/ore/proc/autoload(mob/user) + var/obj/item/weapon/ore/O = locate() in get_turf(src) + if(O) + gather_all(get_turf(src), user) + +/obj/item/weapon/storage/bag/ore/proc/rangedload(atom/A, mob/user) + var/obj/item/weapon/ore/O = locate() in get_turf(A) + if(O) + gather_all(get_turf(A), user) + +/obj/item/weapon/storage/bag/ore/examine(mob/user) + . = ..() + + if(!Adjacent(user)) //Can only check the contents of ore bags if you can physically reach them. + return . + + if(istype(user, /mob/living)) + add_fingerprint(user) + + . += "It holds:" + var/has_ore = 0 + for(var/ore in stored_ore) + if(stored_ore[ore] > 0) + . += "- [stored_ore[ore]] [ore]" + has_ore = 1 + if(!has_ore) + . += "Nothing." + +/obj/item/weapon/storage/bag/ore/open(mob/user as mob) //No opening it for the weird UI of having shit-tons of ore inside it. + user.examinate(src) + +// ----------------------------- +// Plant bag +// ----------------------------- +/obj/item/weapon/storage/bag/plants + name = "plant bag" + icon = 'icons/obj/hydroponics_machines_vr.dmi' + icon_state = "plantbag" + desc = "A sturdy bag used to transport fresh produce with ease." + max_storage_space = ITEMSIZE_COST_NORMAL * 25 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/grown,/obj/item/seeds,/obj/item/weapon/grown) + +/obj/item/weapon/storage/bag/plants/large + name = "large plant bag" + icon_state = "large_plantbag" + desc = "A large and sturdy bag used to transport fresh produce with ease." + max_storage_space = ITEMSIZE_COST_NORMAL * 50 + +// ----------------------------- +// Sheet Snatcher +// ----------------------------- +// Because it stacks stacks, this doesn't operate normally. +// However, making it a storage/bag allows us to reuse existing code in some places. -Sayu + +/obj/item/weapon/storage/bag/sheetsnatcher + name = "sheet snatcher" + icon = 'icons/obj/mining.dmi' + icon_state = "sheetsnatcher" + desc = "A patented storage system designed for any kind of mineral sheet." + + var/capacity = 300; //the number of sheets it can carry. + w_class = ITEMSIZE_NORMAL + storage_slots = 7 + + allow_quick_empty = 1 // this function is superceded + +/obj/item/weapon/storage/bag/sheetsnatcher/can_be_inserted(obj/item/W as obj, stop_messages = 0) + if(!istype(W,/obj/item/stack/material)) + if(!stop_messages) + to_chat(usr, "The snatcher does not accept [W].") + return 0 + var/current = 0 + for(var/obj/item/stack/material/S in contents) + current += S.get_amount() + if(capacity == current)//If it's full, you're done + if(!stop_messages) + to_chat(usr, "The snatcher is full.") + return 0 + return 1 + + +// Modified handle_item_insertion. Would prefer not to, but... +/obj/item/weapon/storage/bag/sheetsnatcher/handle_item_insertion(obj/item/W as obj, prevent_warning = 0) + var/obj/item/stack/material/S = W + if(!istype(S)) return 0 + + var/amount + var/inserted = 0 + var/current = 0 + for(var/obj/item/stack/material/S2 in contents) + current += S2.get_amount() + if(capacity < current + S.get_amount())//If the stack will fill it up + amount = capacity - current + else + amount = S.get_amount() + + for(var/obj/item/stack/material/sheet in contents) + if(S.type == sheet.type) + // we are violating the amount limitation because these are not sane objects + sheet.set_amount(sheet.get_amount() + amount, TRUE) + S.use(amount) // will qdel() if we use it all + inserted = 1 + break + + if(!inserted) + usr.remove_from_mob(S) + if (usr.client && usr.s_active != src) + usr.client.screen -= S + S.dropped(usr) + S.loc = src + + orient2hud(usr) + if(usr.s_active) + usr.s_active.show_to(usr) + update_icon() + return 1 + +// Sets up numbered display to show the stack size of each stored mineral +// NOTE: numbered display is turned off currently because it's broken +/obj/item/weapon/storage/bag/sheetsnatcher/orient2hud(mob/user as mob) + var/adjusted_contents = contents.len + + //Numbered contents display + var/list/datum/numbered_display/numbered_contents + if(display_contents_with_number) + numbered_contents = list() + adjusted_contents = 0 + for(var/obj/item/stack/material/I in contents) + adjusted_contents++ + var/datum/numbered_display/D = new/datum/numbered_display(I) + D.number = I.get_amount() + numbered_contents.Add( D ) + + var/row_num = 0 + var/col_count = min(7,storage_slots) -1 + if (adjusted_contents > 7) + row_num = round((adjusted_contents-1) / 7) // 7 is the maximum allowed width. + src.slot_orient_objs(row_num, col_count, numbered_contents) + return + +// Modified quick_empty verb drops appropriate sized stacks +/obj/item/weapon/storage/bag/sheetsnatcher/quick_empty() + var/location = get_turf(src) + for(var/obj/item/stack/material/S in contents) + var/cur_amount = S.get_amount() + var/full_stacks = round(cur_amount / S.max_amount) // Floor of current/max is amount of full stacks we make + var/remainder = cur_amount % S.max_amount // Current mod max is remainder after full sheets removed + for(var/i = 1 to full_stacks) + new S.type(location, S.max_amount) + if(remainder) + new S.type(location, remainder) + S.set_amount(0) + orient2hud(usr) + if(usr.s_active) + usr.s_active.show_to(usr) + update_icon() + +// Instead of removing +/obj/item/weapon/storage/bag/sheetsnatcher/remove_from_storage(obj/item/W as obj, atom/new_location) + var/obj/item/stack/material/S = W + if(!istype(S)) return 0 + + //I would prefer to drop a new stack, but the item/attack_hand code + // that calls this can't recieve a different object than you clicked on. + //Therefore, make a new stack internally that has the remainder. + // -Sayu + + if(S.get_amount() > S.max_amount) + var/newstack_amt = S.get_amount() - S.max_amount + new S.type(src, newstack_amt) // The one we'll keep to replace the one we give + S.set_amount(S.max_amount) // The one we hand to the clicker + + return ..(S,new_location) + +// ----------------------------- +// Sheet Snatcher (Cyborg) +// ----------------------------- + +/obj/item/weapon/storage/bag/sheetsnatcher/borg + name = "sheet snatcher 9000" + desc = null + capacity = 500//Borgs get more because >specialization + +// ----------------------------- +// Cash Bag +// ----------------------------- + +/obj/item/weapon/storage/bag/cash + name = "cash bag" + icon = 'icons/obj/storage.dmi' + icon_state = "cashbag" + desc = "A bag for carrying lots of cash. It's got a big dollar sign printed on the front." + max_storage_space = ITEMSIZE_COST_NORMAL * 25 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/coin,/obj/item/weapon/spacecash,/obj/item/weapon/spacecasinocash) + +// ----------------------------- +// Chemistry Bag +// ----------------------------- +/obj/item/weapon/storage/bag/chemistry + name = "chemistry bag" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "chembag" + desc = "A bag for storing pills, patches, and bottles." + max_storage_space = 200 + w_class = ITEMSIZE_LARGE + slowdown = 3 + can_hold = list(/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/glass/bottle) + +// ----------------------------- +// Xeno Bag +// ----------------------------- +/obj/item/weapon/storage/bag/xeno + name = "xenobiology bag" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "xenobag" + desc = "A bag for storing various slime products." + max_storage_space = ITEMSIZE_COST_SMALL * 12 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/slime_extract,/obj/item/slimepotion, /obj/item/weapon/reagent_containers/food/snacks/monkeycube) + +// ----------------------------- +// Virology Bag +// ----------------------------- +/obj/item/weapon/storage/bag/virology + name = "virology bag" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "biobag" + desc = "A bag for storing various biological products." + max_storage_space = ITEMSIZE_COST_SMALL * 12 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/reagent_containers/glass/beaker/vial/,/obj/item/weapon/virusdish/) + +// ----------------------------- +// Food Bag +// ----------------------------- +/obj/item/weapon/storage/bag/food + name = "food bag" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "foodbag" + desc = "A bag for storing foods of all kinds." + max_storage_space = ITEMSIZE_COST_NORMAL * 25 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks,/obj/item/weapon/reagent_containers/food/condiment) + +// ----------------------------- +// Food Bag (Service Hound) +// ----------------------------- +/obj/item/weapon/storage/bag/serviceborg + name = "service bag" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "foodbag" + desc = "An intergrated bag for storing things of all kinds." + max_storage_space = ITEMSIZE_COST_NORMAL * 25 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks,/obj/item/weapon/reagent_containers/food/condiment, + /obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/glass/bottle,/obj/item/weapon/coin,/obj/item/weapon/spacecash, + /obj/item/weapon/reagent_containers/food/snacks/grown,/obj/item/seeds,/obj/item/weapon/grown,/obj/item/weapon/reagent_containers/pill) + +// ----------------------------- +// Evidence Bag +// ----------------------------- +/obj/item/weapon/storage/bag/detective + name = "secure satchel" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "detbag" + desc = "A bag for storing investigation things. You know, securely." + max_storage_space = ITEMSIZE_COST_NORMAL * 15 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_SMALL can_hold = list(/obj/item/weapon/forensics/swab,/obj/item/weapon/sample/print,/obj/item/weapon/sample/fibers,/obj/item/weapon/evidencebag) \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index 1bac7d5ef59..e26ff9bd576 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -1,592 +1,592 @@ -/obj/item/weapon/storage/belt - name = "belt" - desc = "Can hold various things." - icon = 'icons/inventory/belt/item.dmi' - icon_state = "utility" - storage_slots = 7 - max_storage_space = ITEMSIZE_COST_NORMAL * 7 //This should ensure belts always have enough room to store whatever. - max_w_class = ITEMSIZE_NORMAL - slot_flags = SLOT_BELT - attack_verb = list("whipped", "lashed", "disciplined") - equip_sound = 'sound/items/toolbelt_equip.ogg' - drop_sound = 'sound/items/drop/toolbelt.ogg' - pickup_sound = 'sound/items/pickup/toolbelt.ogg' - sprite_sheets = list(SPECIES_TESHARI = 'icons/inventory/belt/mob_teshari.dmi') - - var/show_above_suit = 0 - -/obj/item/weapon/storage/belt/verb/toggle_layer() - set name = "Switch Belt Layer" - set category = "Object" - - if(show_above_suit == -1) - to_chat(usr, "\The [src] cannot be worn above your suit!") - return - show_above_suit = !show_above_suit - update_icon() - -//Some belts have sprites to show icons -/obj/item/weapon/storage/belt/make_worn_icon(var/body_type,var/slot_name,var/inhands,var/default_icon,var/default_layer = 0,var/icon/clip_mask = null) - var/image/standing = ..() - if(!inhands && contents.len) - for(var/obj/item/i in contents) - var/i_state = i.item_state - if(!i_state) i_state = i.icon_state - var/image/add_icon = image(icon = INV_BELT_DEF_ICON, icon_state = i_state) - if(istype(clip_mask)) //For taur bodies/tails clipping off parts of uniforms and suits. - standing.filters += filter(type = "alpha", icon = clip_mask) - standing.add_overlay(add_icon) - return standing - -/obj/item/weapon/storage/update_icon() - if (ismob(src.loc)) - var/mob/M = src.loc - M.update_inv_belt() - -/obj/item/weapon/storage/belt/utility - name = "tool-belt" //Carn: utility belt is nicer, but it bamboozles the text parsing. - desc = "Can hold various tools." - icon_state = "utility" - can_hold = list( - ///obj/item/weapon/combitool, - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/wirecutters, - /obj/item/weapon/tool/wrench, - /obj/item/weapon/tool/transforming/powerdrill, - /obj/item/weapon/tool/transforming/jawsoflife, - /obj/item/device/multitool, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/stack/cable_coil, - /obj/item/device/t_scanner, - /obj/item/device/analyzer, - /obj/item/clothing/glasses, - /obj/item/clothing/gloves, - /obj/item/device/pda, - /obj/item/device/megaphone, - /obj/item/taperoll, - /obj/item/device/radio/headset, - /obj/item/device/robotanalyzer, - /obj/item/weapon/material/minihoe, - /obj/item/weapon/material/knife/machete/hatchet, - /obj/item/device/analyzer/plant_analyzer, - /obj/item/weapon/extinguisher/mini, - /obj/item/weapon/tape_roll, - /obj/item/device/integrated_electronics/wirer, - /obj/item/device/integrated_electronics/debugger, //Vorestation edit adding debugger to toolbelt can hold list - /obj/item/weapon/shovel/spade, //VOREStation edit. If it can hold minihoes and hatchers, why not the gardening spade? - /obj/item/stack/nanopaste, //VOREStation edit. Think of it as a tube of superglue. Belts hold that all the time. - /obj/item/device/geiger //VOREStation edit. Engineers work with rad-slinging stuff sometimes too - ) - -/obj/item/weapon/storage/belt/utility/full - starts_with = list( - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/tool/wrench, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/tool/wirecutters, - /obj/item/stack/cable_coil/random_belt - ) - -/obj/item/weapon/storage/belt/utility/full/multitool - starts_with = list( - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/tool/wrench, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/tool/wirecutters, - /obj/item/stack/cable_coil/random_belt, - /obj/item/device/multitool - ) - -/obj/item/weapon/storage/belt/utility/atmostech - starts_with = list( - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/tool/wrench, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/tool/wirecutters, - /obj/item/device/analyzer, //Vorestation edit. Gives atmos techs a few extra tools fitting their job from the start - /obj/item/weapon/extinguisher/mini //Vorestation edit. As above, the mini's much more handy to have rather than lugging a big one around - ) - -/obj/item/weapon/storage/belt/utility/chief - name = "chief engineer's toolbelt" - desc = "Holds tools, looks snazzy." - icon_state = "utilitybelt_ce" - item_state = "utility_ce" - storage_slots = 8 //If they get better everything-else, why not the belt too? - can_hold = list( - /obj/item/weapon/rcd, //They've given one from the get-go, it's hard to imagine they wouldn't be given something that can store it neater than a bag - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/wirecutters, - /obj/item/weapon/tool/wrench, - /obj/item/weapon/tool/transforming/powerdrill, - /obj/item/weapon/tool/transforming/jawsoflife, - /obj/item/device/multitool, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/stack/cable_coil, - /obj/item/device/t_scanner, - /obj/item/device/analyzer, - /obj/item/clothing/glasses, - /obj/item/clothing/gloves, - /obj/item/device/pda, - /obj/item/device/megaphone, - /obj/item/taperoll, - /obj/item/device/radio/headset, - /obj/item/device/robotanalyzer, - /obj/item/weapon/material/minihoe, - /obj/item/weapon/material/knife/machete/hatchet, - /obj/item/device/analyzer/plant_analyzer, - /obj/item/weapon/extinguisher/mini, - /obj/item/weapon/tape_roll, - /obj/item/device/integrated_electronics/wirer, - /obj/item/device/integrated_electronics/debugger, - /obj/item/weapon/shovel/spade, - /obj/item/stack/nanopaste, - /obj/item/device/geiger, - /obj/item/areaeditor/blueprints, //It's a bunch of paper that could prolly be rolled up & slipped into the belt, not to mention CE only, see the RCD's thing above - /obj/item/wire_reader //As above - ) - -/obj/item/weapon/storage/belt/utility/chief/full - starts_with = list( - /obj/item/weapon/tool/transforming/powerdrill, - /obj/item/weapon/tool/transforming/jawsoflife, - /obj/item/weapon/weldingtool/experimental, - /obj/item/device/multitool, - /obj/item/stack/cable_coil/random_belt, - /obj/item/weapon/extinguisher/mini, - /obj/item/device/analyzer - ) - -/obj/item/weapon/storage/belt/utility/holding - name = "tool-belt of holding" - desc = "A belt that uses localized bluespace pockets to hold more items than expected!" - icon_state = "utility_holding" - storage_slots = 14 //twice the amount as a normal belt - max_storage_space = ITEMSIZE_COST_NORMAL * 14 - can_hold = list( - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/wirecutters, - /obj/item/weapon/tool/wrench, - /obj/item/weapon/tool/transforming/powerdrill, - /obj/item/weapon/tool/transforming/jawsoflife, - /obj/item/device/multitool, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/stack/cable_coil, - /obj/item/device/t_scanner, - /obj/item/device/analyzer, - /obj/item/clothing/glasses, - /obj/item/clothing/gloves, - /obj/item/device/pda, - /obj/item/device/megaphone, - /obj/item/taperoll, - /obj/item/device/radio/headset, - /obj/item/device/robotanalyzer, - /obj/item/weapon/material/minihoe, - /obj/item/weapon/material/knife/machete/hatchet, - /obj/item/device/analyzer/plant_analyzer, - /obj/item/weapon/extinguisher/mini, - /obj/item/weapon/tape_roll, - /obj/item/device/integrated_electronics/wirer, - /obj/item/device/integrated_electronics/debugger, - /obj/item/weapon/shovel/spade, - /obj/item/stack/nanopaste, - /obj/item/weapon/cell, //this is a bigger belt, might as well make it hold bigger cells too - /obj/item/weapon/pipe_dispenser, //bigger belt for bigger tools - /obj/item/weapon/rcd, //see above - /obj/item/device/quantum_pad_booster, - /obj/item/weapon/inducer, - /obj/item/stack/material/steel, - /obj/item/stack/material/glass, - /obj/item/device/lightreplacer, - /obj/item/weapon/pickaxe/plasmacutter - ) - - -/obj/item/weapon/storage/belt/medical - name = "medical belt" - desc = "Can hold various medical equipment." - icon_state = "medical" - can_hold = list( - /obj/item/device/healthanalyzer, - /obj/item/weapon/dnainjector, - /obj/item/weapon/reagent_containers/dropper, - /obj/item/weapon/reagent_containers/glass/beaker, - /obj/item/weapon/reagent_containers/glass/bottle, - /obj/item/weapon/reagent_containers/pill, - /obj/item/weapon/reagent_containers/syringe, - /obj/item/weapon/storage/quickdraw/syringe_case, //VOREStation Addition - Adds syringe cases, - /obj/item/weapon/flame/lighter/zippo, - /obj/item/weapon/storage/fancy/cigarettes, - /obj/item/weapon/storage/pill_bottle, - /obj/item/stack/medical, - /obj/item/device/radio/headset, - /obj/item/device/pda, - /obj/item/taperoll, - /obj/item/device/megaphone, - /obj/item/clothing/mask/surgical, - /obj/item/clothing/head/surgery, - /obj/item/clothing/gloves, - /obj/item/weapon/reagent_containers/hypospray, - /obj/item/clothing/glasses, - /obj/item/weapon/tool/crowbar, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/weapon/extinguisher/mini, - /obj/item/weapon/storage/quickdraw/syringe_case - ) - -/obj/item/weapon/storage/belt/medical/emt - name = "EMT utility belt" - desc = "A sturdy black webbing belt with attached pouches." - icon_state = "ems" - -/obj/item/weapon/storage/belt/medical/holding - name = "medical belt of holding" - desc = "A belt that uses localized bluespace pockets to hold more items than expected!" - icon_state = "med_holding" - storage_slots = 14 //twice the amount as a normal belt - max_storage_space = ITEMSIZE_COST_NORMAL * 14 - -/obj/item/weapon/storage/belt/security - name = "security belt" - desc = "Can hold security gear like handcuffs and flashes." - icon_state = "security" - max_w_class = ITEMSIZE_NORMAL - can_hold = list( - /obj/item/weapon/grenade, - /obj/item/weapon/reagent_containers/spray/pepper, - /obj/item/weapon/handcuffs, - /obj/item/device/flash, - /obj/item/clothing/glasses, - /obj/item/ammo_casing/a12g, - /obj/item/ammo_magazine, - /obj/item/weapon/cell/device, - /obj/item/weapon/reagent_containers/food/snacks/donut/, - /obj/item/weapon/melee/baton, - /obj/item/weapon/gun/energy/taser, - /obj/item/weapon/gun/energy/stunrevolver, - /obj/item/weapon/gun/energy/stunrevolver/vintage, - /obj/item/weapon/gun/magnetic/railgun/heater/pistol, - /obj/item/weapon/gun/energy/gun, - /obj/item/weapon/flame/lighter, - /obj/item/device/flashlight, - /obj/item/device/taperecorder, - /obj/item/device/tape, - /obj/item/device/pda, - /obj/item/device/radio/headset, - /obj/item/clothing/gloves, - /obj/item/device/hailer, - /obj/item/device/megaphone, - /obj/item/weapon/melee, - /obj/item/clothing/accessory/badge, - /obj/item/weapon/gun/projectile/sec, - /obj/item/weapon/gun/projectile/p92x, - /obj/item/taperoll, - /obj/item/weapon/gun/projectile/colt/detective, - /obj/item/device/holowarrant, - /obj/item/device/ticket_printer //VOREStation Edit - ) - -/obj/item/weapon/storage/belt/detective - name = "forensic utility belt" - desc = "A belt for holding forensics equipment." - icon_state = "security" - storage_slots = 7 - max_w_class = ITEMSIZE_NORMAL - can_hold = list( - /obj/item/device/taperecorder, - /obj/item/device/tape, - /obj/item/clothing/glasses, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/weapon/reagent_containers/spray/luminol, - /obj/item/weapon/sample, - /obj/item/weapon/forensics/sample_kit/powder, - /obj/item/weapon/forensics/swab, - /obj/item/device/uv_light, - /obj/item/weapon/forensics/sample_kit, - /obj/item/weapon/photo, - /obj/item/device/camera_film, - /obj/item/device/camera, - /obj/item/weapon/autopsy_scanner, - /obj/item/device/mass_spectrometer, - /obj/item/clothing/accessory/badge, - /obj/item/device/reagent_scanner, - /obj/item/weapon/reagent_containers/dropper, - /obj/item/weapon/reagent_containers/syringe, - /obj/item/device/pda, - /obj/item/device/hailer, - /obj/item/device/megaphone, - /obj/item/device/radio/headset, - /obj/item/clothing/gloves, - /obj/item/taperoll, - /obj/item/weapon/reagent_containers/spray/pepper, - /obj/item/weapon/handcuffs, - /obj/item/device/flash, - /obj/item/weapon/flame/lighter, - /obj/item/weapon/reagent_containers/food/snacks/donut/, - ///obj/item/ammo_magazine, //Detectives don't get projectile weapons as standard here - ///obj/item/weapon/gun/projectile/colt/detective, //Detectives don't get projectile weapons as standard here - /obj/item/weapon/gun/energy/stunrevolver/detective, //In keeping with the same vein as above, they can store their special one - /obj/item/device/holowarrant, - /obj/item/weapon/reagent_containers/food/drinks/flask, - /obj/item/device/ticket_printer //VOREStation Edit - ) - -/obj/item/weapon/storage/belt/soulstone - name = "soul stone belt" - desc = "Designed for ease of access to the shards during a fight, as to not let a single enemy spirit slip away" - icon_state = "soulstone" - storage_slots = 6 - can_hold = list( - /obj/item/device/soulstone - ) - -/obj/item/weapon/storage/belt/soulstone/full - starts_with = list(/obj/item/device/soulstone = 6) - -/obj/item/weapon/storage/belt/utility/alien - name = "alien belt" - desc = "A belt(?) that can hold things." - icon = 'icons/obj/abductor.dmi' - icon_state = "belt" - item_state = "security" - -/obj/item/weapon/storage/belt/utility/alien/full - starts_with = list( - /obj/item/weapon/tool/screwdriver/alien, - /obj/item/weapon/tool/wrench/alien, - /obj/item/weapon/weldingtool/alien, - /obj/item/weapon/tool/crowbar/alien, - /obj/item/weapon/tool/wirecutters/alien, - /obj/item/device/multitool/alien, - /obj/item/stack/cable_coil/alien - ) - -/obj/item/weapon/storage/belt/medical/alien - name = "alien belt" - desc = "A belt(?) that can hold things." - icon = 'icons/obj/abductor.dmi' - icon_state = "belt" - item_state = "security" - storage_slots = 8 - can_hold = list( - /obj/item/device/healthanalyzer, - /obj/item/weapon/dnainjector, - /obj/item/weapon/reagent_containers/dropper, - /obj/item/weapon/reagent_containers/glass/beaker, - /obj/item/weapon/reagent_containers/glass/bottle, - /obj/item/weapon/reagent_containers/pill, - /obj/item/weapon/reagent_containers/syringe, - /obj/item/weapon/flame/lighter/zippo, - /obj/item/weapon/storage/fancy/cigarettes, - /obj/item/weapon/storage/pill_bottle, - /obj/item/stack/medical, - /obj/item/device/radio/headset, - /obj/item/device/pda, - /obj/item/taperoll, - /obj/item/device/megaphone, - /obj/item/clothing/mask/surgical, - /obj/item/clothing/head/surgery, - /obj/item/clothing/gloves, - /obj/item/weapon/reagent_containers/hypospray, - /obj/item/clothing/glasses, - /obj/item/weapon/tool/crowbar, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/weapon/extinguisher/mini, - /obj/item/weapon/surgical - ) - -/obj/item/weapon/storage/belt/medical/alien - starts_with = list( - /obj/item/weapon/surgical/scalpel/alien, - /obj/item/weapon/surgical/hemostat/alien, - /obj/item/weapon/surgical/retractor/alien, - /obj/item/weapon/surgical/circular_saw/alien, - /obj/item/weapon/surgical/FixOVein/alien, - /obj/item/weapon/surgical/bone_clamp/alien, - /obj/item/weapon/surgical/cautery/alien, - /obj/item/weapon/surgical/surgicaldrill/alien - ) - -/obj/item/weapon/storage/belt/champion - name = "championship belt" - desc = "Proves to the world that you are the strongest!" - icon_state = "champion" - storage_slots = 1 - can_hold = list( - "/obj/item/clothing/mask/luchador" - ) - -/obj/item/weapon/storage/belt/security/tactical - name = "combat belt" - desc = "Can hold security gear like handcuffs and flashes, with more pouches for more storage." - icon_state = "swat" - storage_slots = 9 - max_w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_NORMAL * 7 - -/obj/item/weapon/storage/belt/bandolier - name = "shotgun bandolier" - desc = "Designed to hold shotgun shells. Can't really hold more than that." - icon_state = "bandolier1" - storage_slots = 8 - max_w_class = ITEMSIZE_TINY - can_hold = list( - /obj/item/ammo_casing/a12g, - /obj/item/ammo_casing/a12g/pellet, - /obj/item/ammo_casing/a12g/blank, - /obj/item/ammo_casing/a12g/practice, - /obj/item/ammo_casing/a12g/beanbag, - /obj/item/ammo_casing/a12g/stunshell, - /obj/item/ammo_casing/a12g/flash, - /obj/item/ammo_casing/a12g/emp, - /obj/item/ammo_casing/a12g/flechette - ) - -/obj/item/weapon/storage/belt/security/tactical/bandolier - name = "combat bandolier" - desc = "Can hold security gear like handcuffs and flashes, with more pouches for more storage." - icon_state = "bandolier2" - -/obj/item/weapon/storage/belt/janitor - name = "janitorial belt" - desc = "A belt used to hold most janitorial supplies." - icon_state = "janitor" - storage_slots = 7 - max_w_class = ITEMSIZE_NORMAL - can_hold = list( - /obj/item/clothing/glasses, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/weapon/grenade, - /obj/item/device/pda, - /obj/item/device/radio/headset, - /obj/item/clothing/gloves, - /obj/item/clothing/mask/surgical, //sterile mask, - /obj/item/device/assembly/mousetrap, - /obj/item/weapon/light/bulb, - /obj/item/weapon/light/tube, - /obj/item/weapon/flame/lighter, - /obj/item/device/megaphone, - /obj/item/taperoll, - /obj/item/weapon/reagent_containers/spray, - /obj/item/weapon/soap, - /obj/item/device/lightreplacer //VOREStation edit - ) - -/obj/item/weapon/storage/belt/archaeology - name = "excavation gear-belt" - desc = "Can hold various excavation gear." - icon_state = "gear" - can_hold = list( - /obj/item/weapon/storage/box/samplebags, - /obj/item/device/core_sampler, - /obj/item/device/beacon_locator, - /obj/item/device/radio/beacon, - /obj/item/device/gps, - /obj/item/device/measuring_tape, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/weapon/pickaxe, - /obj/item/device/depth_scanner, - /obj/item/device/camera, - /obj/item/weapon/paper, - /obj/item/weapon/photo, - /obj/item/weapon/folder, - /obj/item/weapon/pen, - /obj/item/weapon/folder, - /obj/item/weapon/clipboard, - /obj/item/weapon/anodevice, - /obj/item/clothing/glasses, - /obj/item/weapon/tool/wrench, - /obj/item/weapon/tool/transforming/powerdrill, - /obj/item/weapon/storage/excavation, - /obj/item/weapon/anobattery, - /obj/item/device/ano_scanner, - /obj/item/weapon/pickaxe/hand, - /obj/item/device/xenoarch_multi_tool, - /obj/item/weapon/pickaxe/excavationdrill - ) - -/obj/item/weapon/storage/belt/fannypack - name = "leather fannypack" - desc = "A dorky fannypack for keeping small items in." - icon_state = "fannypack_leather" - item_state = "fannypack_leather" - max_w_class = ITEMSIZE_SMALL - storage_slots = null - max_storage_space = ITEMSIZE_COST_NORMAL * 2 - -/obj/item/weapon/storage/belt/fannypack/black - name = "black fannypack" - icon_state = "fannypack_black" - item_state = "fannypack_black" - -/obj/item/weapon/storage/belt/fannypack/blue - name = "blue fannypack" - icon_state = "fannypack_blue" - item_state = "fannypack_blue" - -/obj/item/weapon/storage/belt/fannypack/cyan - name = "cyan fannypack" - icon_state = "fannypack_cyan" - item_state = "fannypack_cyan" - -/obj/item/weapon/storage/belt/fannypack/green - name = "green fannypack" - icon_state = "fannypack_green" - item_state = "fannypack_green" - -/obj/item/weapon/storage/belt/fannypack/orange - name = "orange fannypack" - icon_state = "fannypack_orange" - item_state = "fannypack_orange" - -/obj/item/weapon/storage/belt/fannypack/purple - name = "purple fannypack" - icon_state = "fannypack_purple" - item_state = "fannypack_purple" - -/obj/item/weapon/storage/belt/fannypack/red - name = "red fannypack" - icon_state = "fannypack_red" - item_state = "fannypack_red" - -/obj/item/weapon/storage/belt/fannypack/white - name = "white fannypack" - icon_state = "fannypack_white" - item_state = "fannypack_white" - -/obj/item/weapon/storage/belt/fannypack/yellow - name = "yellow fannypack" - icon_state = "fannypack_yellow" - item_state = "fannypack_yellow" - -/obj/item/weapon/storage/belt/ranger - name = "ranger belt" - desc = "The fancy utility-belt holding the tools, cuffs and gadgets of the Go Go ERT-Rangers. The belt buckle is not real phoron, but it is still surprisingly comfortable to wear." - icon = 'icons/obj/clothing/ranger.dmi' - icon_state = "ranger_belt" - -/obj/item/weapon/storage/belt/dbandolier - name = "\improper Donk-Soft bandolier" - desc = "A Donk-Soft bandolier! Carry your spare darts anywhere! Ages 8 and up." - icon_state = "dbandolier" - storage_slots = 8 - can_hold = list( - /obj/item/ammo_casing/afoam_dart - ) +/obj/item/weapon/storage/belt + name = "belt" + desc = "Can hold various things." + icon = 'icons/inventory/belt/item.dmi' + icon_state = "utility" + storage_slots = 7 + max_storage_space = ITEMSIZE_COST_NORMAL * 7 //This should ensure belts always have enough room to store whatever. + max_w_class = ITEMSIZE_NORMAL + slot_flags = SLOT_BELT + attack_verb = list("whipped", "lashed", "disciplined") + equip_sound = 'sound/items/toolbelt_equip.ogg' + drop_sound = 'sound/items/drop/toolbelt.ogg' + pickup_sound = 'sound/items/pickup/toolbelt.ogg' + sprite_sheets = list(SPECIES_TESHARI = 'icons/inventory/belt/mob_teshari.dmi') + + var/show_above_suit = 0 + +/obj/item/weapon/storage/belt/verb/toggle_layer() + set name = "Switch Belt Layer" + set category = "Object" + + if(show_above_suit == -1) + to_chat(usr, "\The [src] cannot be worn above your suit!") + return + show_above_suit = !show_above_suit + update_icon() + +//Some belts have sprites to show icons +/obj/item/weapon/storage/belt/make_worn_icon(var/body_type,var/slot_name,var/inhands,var/default_icon,var/default_layer = 0,var/icon/clip_mask = null) + var/image/standing = ..() + if(!inhands && contents.len) + for(var/obj/item/i in contents) + var/i_state = i.item_state + if(!i_state) i_state = i.icon_state + var/image/add_icon = image(icon = INV_BELT_DEF_ICON, icon_state = i_state) + if(istype(clip_mask)) //For taur bodies/tails clipping off parts of uniforms and suits. + standing.filters += filter(type = "alpha", icon = clip_mask) + standing.add_overlay(add_icon) + return standing + +/obj/item/weapon/storage/update_icon() + if (ismob(src.loc)) + var/mob/M = src.loc + M.update_inv_belt() + +/obj/item/weapon/storage/belt/utility + name = "tool-belt" //Carn: utility belt is nicer, but it bamboozles the text parsing. + desc = "Can hold various tools." + icon_state = "utility" + can_hold = list( + ///obj/item/weapon/combitool, + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/wirecutters, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/tool/transforming/powerdrill, + /obj/item/weapon/tool/transforming/jawsoflife, + /obj/item/device/multitool, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/stack/cable_coil, + /obj/item/device/t_scanner, + /obj/item/device/analyzer, + /obj/item/clothing/glasses, + /obj/item/clothing/gloves, + /obj/item/device/pda, + /obj/item/device/megaphone, + /obj/item/taperoll, + /obj/item/device/radio/headset, + /obj/item/device/robotanalyzer, + /obj/item/weapon/material/minihoe, + /obj/item/weapon/material/knife/machete/hatchet, + /obj/item/device/analyzer/plant_analyzer, + /obj/item/weapon/extinguisher/mini, + /obj/item/weapon/tape_roll, + /obj/item/device/integrated_electronics/wirer, + /obj/item/device/integrated_electronics/debugger, //Vorestation edit adding debugger to toolbelt can hold list + /obj/item/weapon/shovel/spade, //VOREStation edit. If it can hold minihoes and hatchers, why not the gardening spade? + /obj/item/stack/nanopaste, //VOREStation edit. Think of it as a tube of superglue. Belts hold that all the time. + /obj/item/device/geiger //VOREStation edit. Engineers work with rad-slinging stuff sometimes too + ) + +/obj/item/weapon/storage/belt/utility/full + starts_with = list( + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/tool/wirecutters, + /obj/item/stack/cable_coil/random_belt + ) + +/obj/item/weapon/storage/belt/utility/full/multitool + starts_with = list( + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/tool/wirecutters, + /obj/item/stack/cable_coil/random_belt, + /obj/item/device/multitool + ) + +/obj/item/weapon/storage/belt/utility/atmostech + starts_with = list( + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/tool/wirecutters, + /obj/item/device/analyzer, //Vorestation edit. Gives atmos techs a few extra tools fitting their job from the start + /obj/item/weapon/extinguisher/mini //Vorestation edit. As above, the mini's much more handy to have rather than lugging a big one around + ) + +/obj/item/weapon/storage/belt/utility/chief + name = "chief engineer's toolbelt" + desc = "Holds tools, looks snazzy." + icon_state = "utilitybelt_ce" + item_state = "utility_ce" + storage_slots = 8 //If they get better everything-else, why not the belt too? + can_hold = list( + /obj/item/weapon/rcd, //They've given one from the get-go, it's hard to imagine they wouldn't be given something that can store it neater than a bag + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/wirecutters, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/tool/transforming/powerdrill, + /obj/item/weapon/tool/transforming/jawsoflife, + /obj/item/device/multitool, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/stack/cable_coil, + /obj/item/device/t_scanner, + /obj/item/device/analyzer, + /obj/item/clothing/glasses, + /obj/item/clothing/gloves, + /obj/item/device/pda, + /obj/item/device/megaphone, + /obj/item/taperoll, + /obj/item/device/radio/headset, + /obj/item/device/robotanalyzer, + /obj/item/weapon/material/minihoe, + /obj/item/weapon/material/knife/machete/hatchet, + /obj/item/device/analyzer/plant_analyzer, + /obj/item/weapon/extinguisher/mini, + /obj/item/weapon/tape_roll, + /obj/item/device/integrated_electronics/wirer, + /obj/item/device/integrated_electronics/debugger, + /obj/item/weapon/shovel/spade, + /obj/item/stack/nanopaste, + /obj/item/device/geiger, + /obj/item/areaeditor/blueprints, //It's a bunch of paper that could prolly be rolled up & slipped into the belt, not to mention CE only, see the RCD's thing above + /obj/item/wire_reader //As above + ) + +/obj/item/weapon/storage/belt/utility/chief/full + starts_with = list( + /obj/item/weapon/tool/transforming/powerdrill, + /obj/item/weapon/tool/transforming/jawsoflife, + /obj/item/weapon/weldingtool/experimental, + /obj/item/device/multitool, + /obj/item/stack/cable_coil/random_belt, + /obj/item/weapon/extinguisher/mini, + /obj/item/device/analyzer + ) + +/obj/item/weapon/storage/belt/utility/holding + name = "tool-belt of holding" + desc = "A belt that uses localized bluespace pockets to hold more items than expected!" + icon_state = "utility_holding" + storage_slots = 14 //twice the amount as a normal belt + max_storage_space = ITEMSIZE_COST_NORMAL * 14 + can_hold = list( + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/wirecutters, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/tool/transforming/powerdrill, + /obj/item/weapon/tool/transforming/jawsoflife, + /obj/item/device/multitool, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/stack/cable_coil, + /obj/item/device/t_scanner, + /obj/item/device/analyzer, + /obj/item/clothing/glasses, + /obj/item/clothing/gloves, + /obj/item/device/pda, + /obj/item/device/megaphone, + /obj/item/taperoll, + /obj/item/device/radio/headset, + /obj/item/device/robotanalyzer, + /obj/item/weapon/material/minihoe, + /obj/item/weapon/material/knife/machete/hatchet, + /obj/item/device/analyzer/plant_analyzer, + /obj/item/weapon/extinguisher/mini, + /obj/item/weapon/tape_roll, + /obj/item/device/integrated_electronics/wirer, + /obj/item/device/integrated_electronics/debugger, + /obj/item/weapon/shovel/spade, + /obj/item/stack/nanopaste, + /obj/item/weapon/cell, //this is a bigger belt, might as well make it hold bigger cells too + /obj/item/weapon/pipe_dispenser, //bigger belt for bigger tools + /obj/item/weapon/rcd, //see above + /obj/item/device/quantum_pad_booster, + /obj/item/weapon/inducer, + /obj/item/stack/material/steel, + /obj/item/stack/material/glass, + /obj/item/device/lightreplacer, + /obj/item/weapon/pickaxe/plasmacutter + ) + + +/obj/item/weapon/storage/belt/medical + name = "medical belt" + desc = "Can hold various medical equipment." + icon_state = "medical" + can_hold = list( + /obj/item/device/healthanalyzer, + /obj/item/weapon/dnainjector, + /obj/item/weapon/reagent_containers/dropper, + /obj/item/weapon/reagent_containers/glass/beaker, + /obj/item/weapon/reagent_containers/glass/bottle, + /obj/item/weapon/reagent_containers/pill, + /obj/item/weapon/reagent_containers/syringe, + /obj/item/weapon/storage/quickdraw/syringe_case, //VOREStation Addition - Adds syringe cases, + /obj/item/weapon/flame/lighter/zippo, + /obj/item/weapon/storage/fancy/cigarettes, + /obj/item/weapon/storage/pill_bottle, + /obj/item/stack/medical, + /obj/item/device/radio/headset, + /obj/item/device/pda, + /obj/item/taperoll, + /obj/item/device/megaphone, + /obj/item/clothing/mask/surgical, + /obj/item/clothing/head/surgery, + /obj/item/clothing/gloves, + /obj/item/weapon/reagent_containers/hypospray, + /obj/item/clothing/glasses, + /obj/item/weapon/tool/crowbar, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/weapon/extinguisher/mini, + /obj/item/weapon/storage/quickdraw/syringe_case + ) + +/obj/item/weapon/storage/belt/medical/emt + name = "EMT utility belt" + desc = "A sturdy black webbing belt with attached pouches." + icon_state = "ems" + +/obj/item/weapon/storage/belt/medical/holding + name = "medical belt of holding" + desc = "A belt that uses localized bluespace pockets to hold more items than expected!" + icon_state = "med_holding" + storage_slots = 14 //twice the amount as a normal belt + max_storage_space = ITEMSIZE_COST_NORMAL * 14 + +/obj/item/weapon/storage/belt/security + name = "security belt" + desc = "Can hold security gear like handcuffs and flashes." + icon_state = "security" + max_w_class = ITEMSIZE_NORMAL + can_hold = list( + /obj/item/weapon/grenade, + /obj/item/weapon/reagent_containers/spray/pepper, + /obj/item/weapon/handcuffs, + /obj/item/device/flash, + /obj/item/clothing/glasses, + /obj/item/ammo_casing/a12g, + /obj/item/ammo_magazine, + /obj/item/weapon/cell/device, + /obj/item/weapon/reagent_containers/food/snacks/donut/, + /obj/item/weapon/melee/baton, + /obj/item/weapon/gun/energy/taser, + /obj/item/weapon/gun/energy/stunrevolver, + /obj/item/weapon/gun/energy/stunrevolver/vintage, + /obj/item/weapon/gun/magnetic/railgun/heater/pistol, + /obj/item/weapon/gun/energy/gun, + /obj/item/weapon/flame/lighter, + /obj/item/device/flashlight, + /obj/item/device/taperecorder, + /obj/item/device/tape, + /obj/item/device/pda, + /obj/item/device/radio/headset, + /obj/item/clothing/gloves, + /obj/item/device/hailer, + /obj/item/device/megaphone, + /obj/item/weapon/melee, + /obj/item/clothing/accessory/badge, + /obj/item/weapon/gun/projectile/sec, + /obj/item/weapon/gun/projectile/p92x, + /obj/item/taperoll, + /obj/item/weapon/gun/projectile/colt/detective, + /obj/item/device/holowarrant, + /obj/item/device/ticket_printer //VOREStation Edit + ) + +/obj/item/weapon/storage/belt/detective + name = "forensic utility belt" + desc = "A belt for holding forensics equipment." + icon_state = "security" + storage_slots = 7 + max_w_class = ITEMSIZE_NORMAL + can_hold = list( + /obj/item/device/taperecorder, + /obj/item/device/tape, + /obj/item/clothing/glasses, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/weapon/reagent_containers/spray/luminol, + /obj/item/weapon/sample, + /obj/item/weapon/forensics/sample_kit/powder, + /obj/item/weapon/forensics/swab, + /obj/item/device/uv_light, + /obj/item/weapon/forensics/sample_kit, + /obj/item/weapon/photo, + /obj/item/device/camera_film, + /obj/item/device/camera, + /obj/item/weapon/autopsy_scanner, + /obj/item/device/mass_spectrometer, + /obj/item/clothing/accessory/badge, + /obj/item/device/reagent_scanner, + /obj/item/weapon/reagent_containers/dropper, + /obj/item/weapon/reagent_containers/syringe, + /obj/item/device/pda, + /obj/item/device/hailer, + /obj/item/device/megaphone, + /obj/item/device/radio/headset, + /obj/item/clothing/gloves, + /obj/item/taperoll, + /obj/item/weapon/reagent_containers/spray/pepper, + /obj/item/weapon/handcuffs, + /obj/item/device/flash, + /obj/item/weapon/flame/lighter, + /obj/item/weapon/reagent_containers/food/snacks/donut/, + ///obj/item/ammo_magazine, //Detectives don't get projectile weapons as standard here + ///obj/item/weapon/gun/projectile/colt/detective, //Detectives don't get projectile weapons as standard here + /obj/item/weapon/gun/energy/stunrevolver/detective, //In keeping with the same vein as above, they can store their special one + /obj/item/device/holowarrant, + /obj/item/weapon/reagent_containers/food/drinks/flask, + /obj/item/device/ticket_printer //VOREStation Edit + ) + +/obj/item/weapon/storage/belt/soulstone + name = "soul stone belt" + desc = "Designed for ease of access to the shards during a fight, as to not let a single enemy spirit slip away" + icon_state = "soulstone" + storage_slots = 6 + can_hold = list( + /obj/item/device/soulstone + ) + +/obj/item/weapon/storage/belt/soulstone/full + starts_with = list(/obj/item/device/soulstone = 6) + +/obj/item/weapon/storage/belt/utility/alien + name = "alien belt" + desc = "A belt(?) that can hold things." + icon = 'icons/obj/abductor.dmi' + icon_state = "belt" + item_state = "security" + +/obj/item/weapon/storage/belt/utility/alien/full + starts_with = list( + /obj/item/weapon/tool/screwdriver/alien, + /obj/item/weapon/tool/wrench/alien, + /obj/item/weapon/weldingtool/alien, + /obj/item/weapon/tool/crowbar/alien, + /obj/item/weapon/tool/wirecutters/alien, + /obj/item/device/multitool/alien, + /obj/item/stack/cable_coil/alien + ) + +/obj/item/weapon/storage/belt/medical/alien + name = "alien belt" + desc = "A belt(?) that can hold things." + icon = 'icons/obj/abductor.dmi' + icon_state = "belt" + item_state = "security" + storage_slots = 8 + can_hold = list( + /obj/item/device/healthanalyzer, + /obj/item/weapon/dnainjector, + /obj/item/weapon/reagent_containers/dropper, + /obj/item/weapon/reagent_containers/glass/beaker, + /obj/item/weapon/reagent_containers/glass/bottle, + /obj/item/weapon/reagent_containers/pill, + /obj/item/weapon/reagent_containers/syringe, + /obj/item/weapon/flame/lighter/zippo, + /obj/item/weapon/storage/fancy/cigarettes, + /obj/item/weapon/storage/pill_bottle, + /obj/item/stack/medical, + /obj/item/device/radio/headset, + /obj/item/device/pda, + /obj/item/taperoll, + /obj/item/device/megaphone, + /obj/item/clothing/mask/surgical, + /obj/item/clothing/head/surgery, + /obj/item/clothing/gloves, + /obj/item/weapon/reagent_containers/hypospray, + /obj/item/clothing/glasses, + /obj/item/weapon/tool/crowbar, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/weapon/extinguisher/mini, + /obj/item/weapon/surgical + ) + +/obj/item/weapon/storage/belt/medical/alien + starts_with = list( + /obj/item/weapon/surgical/scalpel/alien, + /obj/item/weapon/surgical/hemostat/alien, + /obj/item/weapon/surgical/retractor/alien, + /obj/item/weapon/surgical/circular_saw/alien, + /obj/item/weapon/surgical/FixOVein/alien, + /obj/item/weapon/surgical/bone_clamp/alien, + /obj/item/weapon/surgical/cautery/alien, + /obj/item/weapon/surgical/surgicaldrill/alien + ) + +/obj/item/weapon/storage/belt/champion + name = "championship belt" + desc = "Proves to the world that you are the strongest!" + icon_state = "champion" + storage_slots = 1 + can_hold = list( + "/obj/item/clothing/mask/luchador" + ) + +/obj/item/weapon/storage/belt/security/tactical + name = "combat belt" + desc = "Can hold security gear like handcuffs and flashes, with more pouches for more storage." + icon_state = "swat" + storage_slots = 9 + max_w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_NORMAL * 7 + +/obj/item/weapon/storage/belt/bandolier + name = "shotgun bandolier" + desc = "Designed to hold shotgun shells. Can't really hold more than that." + icon_state = "bandolier1" + storage_slots = 8 + max_w_class = ITEMSIZE_TINY + can_hold = list( + /obj/item/ammo_casing/a12g, + /obj/item/ammo_casing/a12g/pellet, + /obj/item/ammo_casing/a12g/blank, + /obj/item/ammo_casing/a12g/practice, + /obj/item/ammo_casing/a12g/beanbag, + /obj/item/ammo_casing/a12g/stunshell, + /obj/item/ammo_casing/a12g/flash, + /obj/item/ammo_casing/a12g/emp, + /obj/item/ammo_casing/a12g/flechette + ) + +/obj/item/weapon/storage/belt/security/tactical/bandolier + name = "combat bandolier" + desc = "Can hold security gear like handcuffs and flashes, with more pouches for more storage." + icon_state = "bandolier2" + +/obj/item/weapon/storage/belt/janitor + name = "janitorial belt" + desc = "A belt used to hold most janitorial supplies." + icon_state = "janitor" + storage_slots = 7 + max_w_class = ITEMSIZE_NORMAL + can_hold = list( + /obj/item/clothing/glasses, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/weapon/grenade, + /obj/item/device/pda, + /obj/item/device/radio/headset, + /obj/item/clothing/gloves, + /obj/item/clothing/mask/surgical, //sterile mask, + /obj/item/device/assembly/mousetrap, + /obj/item/weapon/light/bulb, + /obj/item/weapon/light/tube, + /obj/item/weapon/flame/lighter, + /obj/item/device/megaphone, + /obj/item/taperoll, + /obj/item/weapon/reagent_containers/spray, + /obj/item/weapon/soap, + /obj/item/device/lightreplacer //VOREStation edit + ) + +/obj/item/weapon/storage/belt/archaeology + name = "excavation gear-belt" + desc = "Can hold various excavation gear." + icon_state = "gear" + can_hold = list( + /obj/item/weapon/storage/box/samplebags, + /obj/item/device/core_sampler, + /obj/item/device/beacon_locator, + /obj/item/device/radio/beacon, + /obj/item/device/gps, + /obj/item/device/measuring_tape, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/weapon/pickaxe, + /obj/item/device/depth_scanner, + /obj/item/device/camera, + /obj/item/weapon/paper, + /obj/item/weapon/photo, + /obj/item/weapon/folder, + /obj/item/weapon/pen, + /obj/item/weapon/folder, + /obj/item/weapon/clipboard, + /obj/item/weapon/anodevice, + /obj/item/clothing/glasses, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/tool/transforming/powerdrill, + /obj/item/weapon/storage/excavation, + /obj/item/weapon/anobattery, + /obj/item/device/ano_scanner, + /obj/item/weapon/pickaxe/hand, + /obj/item/device/xenoarch_multi_tool, + /obj/item/weapon/pickaxe/excavationdrill + ) + +/obj/item/weapon/storage/belt/fannypack + name = "leather fannypack" + desc = "A dorky fannypack for keeping small items in." + icon_state = "fannypack_leather" + item_state = "fannypack_leather" + max_w_class = ITEMSIZE_SMALL + storage_slots = null + max_storage_space = ITEMSIZE_COST_NORMAL * 2 + +/obj/item/weapon/storage/belt/fannypack/black + name = "black fannypack" + icon_state = "fannypack_black" + item_state = "fannypack_black" + +/obj/item/weapon/storage/belt/fannypack/blue + name = "blue fannypack" + icon_state = "fannypack_blue" + item_state = "fannypack_blue" + +/obj/item/weapon/storage/belt/fannypack/cyan + name = "cyan fannypack" + icon_state = "fannypack_cyan" + item_state = "fannypack_cyan" + +/obj/item/weapon/storage/belt/fannypack/green + name = "green fannypack" + icon_state = "fannypack_green" + item_state = "fannypack_green" + +/obj/item/weapon/storage/belt/fannypack/orange + name = "orange fannypack" + icon_state = "fannypack_orange" + item_state = "fannypack_orange" + +/obj/item/weapon/storage/belt/fannypack/purple + name = "purple fannypack" + icon_state = "fannypack_purple" + item_state = "fannypack_purple" + +/obj/item/weapon/storage/belt/fannypack/red + name = "red fannypack" + icon_state = "fannypack_red" + item_state = "fannypack_red" + +/obj/item/weapon/storage/belt/fannypack/white + name = "white fannypack" + icon_state = "fannypack_white" + item_state = "fannypack_white" + +/obj/item/weapon/storage/belt/fannypack/yellow + name = "yellow fannypack" + icon_state = "fannypack_yellow" + item_state = "fannypack_yellow" + +/obj/item/weapon/storage/belt/ranger + name = "ranger belt" + desc = "The fancy utility-belt holding the tools, cuffs and gadgets of the Go Go ERT-Rangers. The belt buckle is not real phoron, but it is still surprisingly comfortable to wear." + icon = 'icons/obj/clothing/ranger.dmi' + icon_state = "ranger_belt" + +/obj/item/weapon/storage/belt/dbandolier + name = "\improper Donk-Soft bandolier" + desc = "A Donk-Soft bandolier! Carry your spare darts anywhere! Ages 8 and up." + icon_state = "dbandolier" + storage_slots = 8 + can_hold = list( + /obj/item/ammo_casing/afoam_dart + ) diff --git a/code/game/objects/items/weapons/storage/bible.dm b/code/game/objects/items/weapons/storage/bible.dm index 6352f4ff9e4..5386f2d5cad 100644 --- a/code/game/objects/items/weapons/storage/bible.dm +++ b/code/game/objects/items/weapons/storage/bible.dm @@ -1,115 +1,115 @@ -GLOBAL_LIST_INIT(biblenames, list( - "Bible", "Koran", "Scrapbook", - "Pagan", "White Bible", "Holy Light", - "Athiest", "Tome", "The King in Yellow", - "Ithaqua", "Scientology", "the bible melts", - "Necronomicon", "Orthodox", "Torah")) -//If you get these two lists not matching in size, there will be runtimes and I will hurt you in ways you couldn't even begin to imagine -// if your bible has no custom itemstate, use one of the existing ones -GLOBAL_LIST_INIT(biblestates, list( - "bible", "koran", "scrapbook", - "shadows", "white", "holylight", - "athiest", "tome", "kingyellow", - "ithaqua", "scientology", "melted", - "necronomicon", "orthodoxy", "torah")) -GLOBAL_LIST_INIT(bibleitemstates, list( - "bible", "koran", "scrapbook", - "syringe_kit", "syringe_kit", "syringe_kit", - "syringe_kit", "syringe_kit", "kingyellow", - "ithaqua", "scientology", "melted", - "necronomicon", "bible", "clipboard")) - -/obj/item/weapon/storage/bible - name = "bible" - desc = "Apply to head repeatedly." - icon_state ="bible" - item_state = "bible" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_books.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_books.dmi' - ) - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_NORMAL - var/mob/affecting = null - var/deity_name = "Christ" - use_sound = 'sound/bureaucracy/bookopen.ogg' - drop_sound = 'sound/bureaucracy/bookclose.ogg' - -/obj/item/weapon/storage/bible/attack_self(mob/living/carbon/human/user) - - if(user?.mind?.assigned_role != "Chaplain") - return FALSE - - if (!user.mind.my_religion) - return FALSE - - if (!user.mind.my_religion.configured) - var/list/skins = list() - for(var/i in 1 to GLOB.biblestates.len) - var/image/bible_image = image(icon = 'icons/obj/storage.dmi', icon_state = GLOB.biblestates[i]) - skins += list("[GLOB.biblenames[i]]" = bible_image) - - var/choice = show_radial_menu(user, src, skins, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 40, require_near = TRUE) - if(!choice) - return FALSE - var/bible_index = GLOB.biblenames.Find(choice) - if(!bible_index) - return FALSE - - user.mind.my_religion.bible_icon_state = GLOB.biblestates[bible_index] - user.mind.my_religion.bible_item_state = GLOB.bibleitemstates[bible_index] - user.mind.my_religion.configured = TRUE - - deity_name = user.mind.my_religion.deity - name = user.mind.my_religion.bible_name - icon_state = user.mind.my_religion.bible_icon_state - item_state = user.mind.my_religion.bible_item_state - to_chat(user, "You invoke [user.mind.my_religion.deity] and prepare a copy of [src].") - -/** - * Checks if we are allowed to interact with a radial menu - * - * Arguments: - * * user The mob interacting with the menu - */ -/obj/item/weapon/storage/bible/proc/check_menu(mob/living/carbon/human/user) - if(user.mind.my_religion.configured) - return FALSE - if(!istype(user)) - return FALSE - if(user.get_active_hand() != src) - return FALSE - if(user.incapacitated()) - return FALSE - if(user.mind.assigned_role != "Chaplain") - return FALSE - return TRUE - -/obj/item/weapon/storage/bible/booze - name = "bible" - desc = "To be applied to the head repeatedly." - icon_state ="bible" - -/obj/item/weapon/storage/bible/booze/New() - starts_with = list( - /obj/item/weapon/reagent_containers/food/drinks/bottle/small/beer, - /obj/item/weapon/reagent_containers/food/drinks/bottle/small/beer, - /obj/item/weapon/spacecash/c100, - /obj/item/weapon/spacecash/c100, - /obj/item/weapon/spacecash/c100 - ) - -/obj/item/weapon/storage/bible/afterattack(atom/A, mob/user as mob, proximity) - if(!proximity) return - if(user.mind && (user.mind.assigned_role == "Chaplain")) - if(A.reagents && A.reagents.has_reagent("water")) //blesses all the water in the holder - to_chat(user, "You bless [A].") - var/water2holy = A.reagents.get_reagent_amount("water") - A.reagents.del_reagent("water") - A.reagents.add_reagent("holywater",water2holy) - -/obj/item/weapon/storage/bible/attackby(obj/item/weapon/W as obj, mob/user as mob) - if (src.use_sound) - playsound(src, src.use_sound, 50, 1, -5) - ..() +GLOBAL_LIST_INIT(biblenames, list( + "Bible", "Koran", "Scrapbook", + "Pagan", "White Bible", "Holy Light", + "Athiest", "Tome", "The King in Yellow", + "Ithaqua", "Scientology", "the bible melts", + "Necronomicon", "Orthodox", "Torah")) +//If you get these two lists not matching in size, there will be runtimes and I will hurt you in ways you couldn't even begin to imagine +// if your bible has no custom itemstate, use one of the existing ones +GLOBAL_LIST_INIT(biblestates, list( + "bible", "koran", "scrapbook", + "shadows", "white", "holylight", + "athiest", "tome", "kingyellow", + "ithaqua", "scientology", "melted", + "necronomicon", "orthodoxy", "torah")) +GLOBAL_LIST_INIT(bibleitemstates, list( + "bible", "koran", "scrapbook", + "syringe_kit", "syringe_kit", "syringe_kit", + "syringe_kit", "syringe_kit", "kingyellow", + "ithaqua", "scientology", "melted", + "necronomicon", "bible", "clipboard")) + +/obj/item/weapon/storage/bible + name = "bible" + desc = "Apply to head repeatedly." + icon_state ="bible" + item_state = "bible" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_books.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_books.dmi' + ) + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_NORMAL + var/mob/affecting = null + var/deity_name = "Christ" + use_sound = 'sound/bureaucracy/bookopen.ogg' + drop_sound = 'sound/bureaucracy/bookclose.ogg' + +/obj/item/weapon/storage/bible/attack_self(mob/living/carbon/human/user) + + if(user?.mind?.assigned_role != "Chaplain") + return FALSE + + if (!user.mind.my_religion) + return FALSE + + if (!user.mind.my_religion.configured) + var/list/skins = list() + for(var/i in 1 to GLOB.biblestates.len) + var/image/bible_image = image(icon = 'icons/obj/storage.dmi', icon_state = GLOB.biblestates[i]) + skins += list("[GLOB.biblenames[i]]" = bible_image) + + var/choice = show_radial_menu(user, src, skins, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 40, require_near = TRUE) + if(!choice) + return FALSE + var/bible_index = GLOB.biblenames.Find(choice) + if(!bible_index) + return FALSE + + user.mind.my_religion.bible_icon_state = GLOB.biblestates[bible_index] + user.mind.my_religion.bible_item_state = GLOB.bibleitemstates[bible_index] + user.mind.my_religion.configured = TRUE + + deity_name = user.mind.my_religion.deity + name = user.mind.my_religion.bible_name + icon_state = user.mind.my_religion.bible_icon_state + item_state = user.mind.my_religion.bible_item_state + to_chat(user, "You invoke [user.mind.my_religion.deity] and prepare a copy of [src].") + +/** + * Checks if we are allowed to interact with a radial menu + * + * Arguments: + * * user The mob interacting with the menu + */ +/obj/item/weapon/storage/bible/proc/check_menu(mob/living/carbon/human/user) + if(user.mind.my_religion.configured) + return FALSE + if(!istype(user)) + return FALSE + if(user.get_active_hand() != src) + return FALSE + if(user.incapacitated()) + return FALSE + if(user.mind.assigned_role != "Chaplain") + return FALSE + return TRUE + +/obj/item/weapon/storage/bible/booze + name = "bible" + desc = "To be applied to the head repeatedly." + icon_state ="bible" + +/obj/item/weapon/storage/bible/booze/New() + starts_with = list( + /obj/item/weapon/reagent_containers/food/drinks/bottle/small/beer, + /obj/item/weapon/reagent_containers/food/drinks/bottle/small/beer, + /obj/item/weapon/spacecash/c100, + /obj/item/weapon/spacecash/c100, + /obj/item/weapon/spacecash/c100 + ) + +/obj/item/weapon/storage/bible/afterattack(atom/A, mob/user as mob, proximity) + if(!proximity) return + if(user.mind && (user.mind.assigned_role == "Chaplain")) + if(A.reagents && A.reagents.has_reagent("water")) //blesses all the water in the holder + to_chat(user, "You bless [A].") + var/water2holy = A.reagents.get_reagent_amount("water") + A.reagents.del_reagent("water") + A.reagents.add_reagent("holywater",water2holy) + +/obj/item/weapon/storage/bible/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (src.use_sound) + playsound(src, src.use_sound, 50, 1, -5) + ..() diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm index 15ae2c9d4f8..1f6c0cce4f2 100644 --- a/code/game/objects/items/weapons/storage/boxes.dm +++ b/code/game/objects/items/weapons/storage/boxes.dm @@ -1,507 +1,507 @@ -/* - * Everything derived from the common cardboard box. - * Basically everything except the original is a kit (starts full). - * - * Contains: - * Empty box, starter boxes (survival/engineer), - * Latex glove and sterile mask boxes, - * Syringe, beaker, dna injector boxes, - * Blanks, flashbangs, and EMP grenade boxes, - * Tracking and chemical implant boxes, - * Prescription glasses and drinking glass boxes, - * Condiment bottle and silly cup boxes, - * Donkpocket and monkeycube boxes, - * ID and security PDA cart boxes, - * Handcuff, mousetrap, and pillbottle boxes, - * Snap-pops and matchboxes, - * Replacement light boxes. - * - * For syndicate call-ins see uplink_kits.dm - */ - -/obj/item/weapon/storage/box - name = "box" - desc = "It's just an ordinary box." - icon = 'icons/obj/boxes.dmi' - icon_state = "box" - item_state = "syringe_kit" - center_of_mass = list("x" = 13,"y" = 10) - var/foldable = /obj/item/stack/material/cardboard // BubbleWrap - if set, can be folded (when empty) into a sheet of cardboard - var/trash = null // if set, can be crushed into a trash item when empty - max_w_class = ITEMSIZE_SMALL - max_storage_space = INVENTORY_BOX_SPACE - use_sound = 'sound/items/storage/box.ogg' - drop_sound = 'sound/items/drop/cardboardbox.ogg' - pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - -// BubbleWrap - A box can be folded up to make card -/obj/item/weapon/storage/box/attack_self(mob/user as mob) - if(..()) return - - //try to fold it - if(ispath(foldable)) - if (contents.len) - return - var/found = 0 - // Close any open UI windows first - for(var/mob/M in range(1)) - if (M.s_active == src) - close(M) - if (M == user) - found = 1 - if (!found) // User is too far away - return - // Now make the cardboard - to_chat(user, "You fold [src] flat.") - playsound(src, 'sound/items/storage/boxfold.ogg', 30, 1) - new foldable(get_turf(src)) - qdel(src) - - //try to crush it - if(ispath(trash)) - if(contents.len && user.a_intent == I_HURT) // only crumple with things inside on harmintent. - user.visible_message(SPAN_DANGER("[user] crushes \the [src], spilling its contents everywhere!"), SPAN_DANGER("You crush \the [src], spilling its contents everywhere!")) - spill() - else - to_chat(user, SPAN_NOTICE("You crumple up \the [src].")) //make trash - playsound(src.loc, 'sound/items/drop/wrapper.ogg', 30, 1) - var/obj/item/trash = new src.trash() - qdel(src) - user.put_in_hands(trash) - - -/obj/item/weapon/storage/box/survival - name = "emergency supply box" - desc = "A survival box issued to crew members for use in emergency situations." - icon_state = "survival" - starts_with = list( - /obj/item/weapon/tool/prybar/red, - /obj/item/clothing/glasses/goggles, - /obj/item/clothing/mask/breath - ) - -/obj/item/weapon/storage/box/survival/synth - name = "synthetic supply box" - desc = "A survival box issued to synthetic crew members for use in emergency situations." - icon_state = "survival_synth" - starts_with = list( - /obj/item/weapon/tool/prybar/red, - /obj/item/clothing/glasses/goggles - ) - -/obj/item/weapon/storage/box/survival/comp - name = "emergency supply box" - desc = "A comprehensive survival box issued to crew members for use in emergency situations. Contains additional supplies." - icon_state = "survival_comp" - starts_with = list( - /obj/item/weapon/tool/prybar/red, - /obj/item/clothing/glasses/goggles, - /obj/item/weapon/reagent_containers/hypospray/autoinjector, - /obj/item/stack/medical/bruise_pack, - /obj/item/device/flashlight/glowstick, - /obj/item/weapon/reagent_containers/food/snacks/candy/proteinbar, - /obj/item/clothing/mask/breath - ) - -/obj/item/weapon/storage/box/gloves - name = "box of latex gloves" - desc = "Contains white gloves." - icon_state = "latex" - starts_with = list(/obj/item/clothing/gloves/sterile/latex = 7) - -/obj/item/weapon/storage/box/masks - name = "box of sterile masks" - desc = "This box contains masks of sterility." - icon_state = "sterile" - starts_with = list(/obj/item/clothing/mask/surgical = 7) - -/obj/item/weapon/storage/box/masks/white - name = "box of sterile masks" - desc = "This box contains masks of sterility." - icon_state = "sterile" - starts_with = list(/obj/item/clothing/mask/surgical/white = 7) - -/obj/item/weapon/storage/box/masks/dust - name = "box of dust masks" - desc = "This box contains dust masks. Breathe easy." - icon_state = "sterile" - starts_with = list(/obj/item/clothing/mask/surgical/dust = 7) - -/obj/item/weapon/storage/box/syringes - name = "box of syringes" - desc = "A box full of syringes." - icon_state = "syringe" - starts_with = list(/obj/item/weapon/reagent_containers/syringe = 7) - -/obj/item/weapon/storage/box/syringegun - name = "box of syringe gun cartridges" - desc = "A box full of compressed gas cartridges." - icon_state = "syringe2" - starts_with = list(/obj/item/weapon/syringe_cartridge = 7) - -/obj/item/weapon/storage/box/beakers - name = "box of beakers" - desc = "A box full of beakers." - icon_state = "beaker" - starts_with = list(/obj/item/weapon/reagent_containers/glass/beaker = 7) - -/obj/item/weapon/storage/box/injectors - name = "box of DNA injectors" - desc = "This box contains injectors it seems." - icon_state = "dna" - starts_with = list( - /obj/item/weapon/dnainjector/h2m = 3, - /obj/item/weapon/dnainjector/m2h = 3 - ) - -/obj/item/weapon/storage/box/flashbangs - name = "box of flashbangs (WARNING)" - desc = "WARNING: These devices are extremely dangerous and can cause blindness or deafness in repeated use." - icon_state = "flashbang" - starts_with = list(/obj/item/weapon/grenade/flashbang = 7) - drop_sound = 'sound/items/drop/ammobox.ogg' - pickup_sound = 'sound/items/pickup/ammobox.ogg' - -/obj/item/weapon/storage/box/emps - name = "box of emp grenades" - desc = "A box containing 5 military grade EMP grenades.
                    WARNING: Do not use near unshielded electronics or biomechanical augmentations, death or permanent paralysis may occur." - icon_state = "emp" - starts_with = list(/obj/item/weapon/grenade/empgrenade = 7) - drop_sound = 'sound/items/drop/ammobox.ogg' - pickup_sound = 'sound/items/pickup/ammobox.ogg' - -/obj/item/weapon/storage/box/empslite - name = "box of low yield emp grenades" - desc = "A box containing 5 low yield EMP grenades.
                    WARNING: Do not use near unshielded electronics or biomechanical augmentations, death or permanent paralysis may occur." - icon_state = "emp" - starts_with = list(/obj/item/weapon/grenade/empgrenade/low_yield = 7) - drop_sound = 'sound/items/drop/ammobox.ogg' - pickup_sound = 'sound/items/pickup/ammobox.ogg' - -/obj/item/weapon/storage/box/smokes - name = "box of smoke bombs" - desc = "A box containing 7 smoke bombs." - icon_state = "flashbang" - starts_with = list(/obj/item/weapon/grenade/smokebomb = 7) - drop_sound = 'sound/items/drop/ammobox.ogg' - pickup_sound = 'sound/items/pickup/ammobox.ogg' - -/obj/item/weapon/storage/box/anti_photons - name = "box of anti-photon grenades" - desc = "A box containing 7 experimental photon disruption grenades." - icon_state = "flashbang" - starts_with = list(/obj/item/weapon/grenade/anti_photon = 7) - drop_sound = 'sound/items/drop/ammobox.ogg' - pickup_sound = 'sound/items/pickup/ammobox.ogg' - -/obj/item/weapon/storage/box/frags - name = "box of fragmentation grenades (WARNING)" - desc = "A box containing 7 military grade fragmentation grenades.
                    WARNING: These devices are extremely dangerous and can cause limb loss or death in repeated use." - icon_state = "frag" - starts_with = list(/obj/item/weapon/grenade/explosive = 7) - drop_sound = 'sound/items/drop/ammobox.ogg' - pickup_sound = 'sound/items/pickup/ammobox.ogg' - -/obj/item/weapon/storage/box/frags_half_box - name = "box of fragmentation grenades (WARNING)" - desc = "A box containing 4 military grade fragmentation grenades.
                    WARNING: These devices are extremely dangerous and can cause limb loss or death in repeated use." - icon_state = "frag" - starts_with = list(/obj/item/weapon/grenade/explosive = 4) - drop_sound = 'sound/items/drop/ammobox.ogg' - pickup_sound = 'sound/items/pickup/ammobox.ogg' - -/obj/item/weapon/storage/box/metalfoam - name = "box of metal foam grenades." - desc = "A box containing 7 metal foam grenades." - icon_state = "flashbang" - starts_with = list(/obj/item/weapon/grenade/chem_grenade/metalfoam = 7) - -/obj/item/weapon/storage/box/teargas - name = "box of teargas grenades" - desc = "A box containing 7 teargas grenades." - icon_state = "flashbang" - starts_with = list(/obj/item/weapon/grenade/chem_grenade/teargas = 7) - -/obj/item/weapon/storage/box/flare - name = "box of flares" - desc = "A box containing 4 flares." - starts_with = list(/obj/item/device/flashlight/flare = 4) - -/obj/item/weapon/storage/box/trackimp - name = "boxed tracking implant kit" - desc = "Box full of scum-bag tracking utensils." - icon_state = "implant" - starts_with = list( - /obj/item/weapon/implantcase/tracking = 4, - /obj/item/weapon/implanter, - /obj/item/weapon/implantpad, - /obj/item/weapon/locator - ) - -/obj/item/weapon/storage/box/chemimp - name = "boxed chemical implant kit" - desc = "Box of stuff used to implant chemicals." - icon_state = "implant" - starts_with = list( - /obj/item/weapon/implantcase/chem = 5, - /obj/item/weapon/implanter, - /obj/item/weapon/implantpad - ) - -/obj/item/weapon/storage/box/camerabug - name = "mobile camera pod box" - desc = "A box containing some mobile camera pods." - icon_state = "pda" - starts_with = list( - /obj/item/device/camerabug = 6, - /obj/item/device/bug_monitor - ) - -/obj/item/weapon/storage/box/rxglasses - name = "box of prescription glasses" - desc = "This box contains nerd glasses." - icon_state = "glasses" - starts_with = list(/obj/item/clothing/glasses/regular = 7) - -/obj/item/weapon/storage/box/cdeathalarm_kit - name = "death alarm kit" - desc = "Box of stuff used to implant death alarms." - icon_state = "implant" - item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") - starts_with = list( - /obj/item/weapon/implantcase/death_alarm = 7, - /obj/item/weapon/implanter - ) - -/obj/item/weapon/storage/box/condimentbottles - name = "box of condiment bottles" - desc = "It has a large ketchup smear on it." - icon_state = "condiment" - starts_with = list(/obj/item/weapon/reagent_containers/food/condiment = 7) - -/obj/item/weapon/storage/box/cups - name = "box of paper cups" - desc = "It has pictures of paper cups on the front." - icon_state = "cups" - starts_with = list(/obj/item/weapon/reagent_containers/food/drinks/sillycup = 7) - -/obj/item/weapon/storage/box/buns - name = "box of bread buns" - desc = "Freshly baked at some point in the past few months." - icon_state = "bun_box" - max_storage_space = ITEMSIZE_COST_NORMAL * 5 - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/bun = 12) - -/obj/item/weapon/storage/box/monkeycubes - name = "monkey cube box" - desc = "Drymate brand monkey cubes. Just add water!" - icon = 'icons/obj/food.dmi' - icon_state = "monkeycubebox" - can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube) - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped = 4) - -/obj/item/weapon/storage/box/monkeycubes/farwacubes - name = "farwa cube box" - desc = "Drymate brand farwa cubes, shipped from Meralar. Just add water!" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped/farwacube = 4) - -/obj/item/weapon/storage/box/monkeycubes/stokcubes - name = "stok cube box" - desc = "Drymate brand stok cubes, shipped from Moghes. Just add water!" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped/stokcube = 4) - -/obj/item/weapon/storage/box/monkeycubes/neaeracubes - name = "neaera cube box" - desc = "Drymate brand neaera cubes, shipped from Qerr'balak. Just add water!" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped/neaeracube = 4) - -/obj/item/weapon/storage/box/ids - name = "box of spare IDs" - desc = "Has so many empty IDs." - icon_state = "id" - starts_with = list(/obj/item/weapon/card/id = 7) - -/obj/item/weapon/storage/box/seccarts - name = "box of spare R.O.B.U.S.T. Cartridges" - desc = "A box full of R.O.B.U.S.T. Cartridges, used by Security." - icon_state = "pda" - starts_with = list(/obj/item/weapon/cartridge/security = 7) - -/obj/item/weapon/storage/box/handcuffs - name = "box of spare handcuffs" - desc = "A box full of handcuffs." - icon_state = "handcuff" - starts_with = list(/obj/item/weapon/handcuffs = 7) - -/obj/item/weapon/storage/box/mousetraps - name = "box of Pest-B-Gon mousetraps" - desc = span_red("WARNING:") + " Keep out of reach of children." - icon_state = "mousetraps" - starts_with = list(/obj/item/device/assembly/mousetrap = 7) - -/obj/item/weapon/storage/box/pillbottles - name = "box of pill bottles" - desc = "It has pictures of pill bottles on its front." - icon_state = "pillbox" - starts_with = list(/obj/item/weapon/storage/pill_bottle = 7) - -/obj/item/weapon/storage/box/snappops - name = "snap pop box" - desc = "Eight wrappers of fun! Ages 8 and up. Not suitable for children." - icon = 'icons/obj/toy.dmi' - icon_state = "spbox" - can_hold = list(/obj/item/toy/snappop) - starts_with = list(/obj/item/toy/snappop = 8) - -/obj/item/weapon/storage/box/matches - name = "matchbox" - desc = "A small box of 'Space-Proof' premium matches." - icon = 'icons/obj/cigarettes.dmi' - icon_state = "matchbox" - w_class = ITEMSIZE_TINY - slot_flags = SLOT_BELT - can_hold = list(/obj/item/weapon/flame/match) - starts_with = list(/obj/item/weapon/flame/match = 10) - drop_sound = 'sound/items/drop/matchbox.ogg' - pickup_sound = 'sound/items/pickup/matchbox.ogg' - -/obj/item/weapon/storage/box/matches/attackby(var/obj/item/weapon/flame/match/W, var/mob/user) - if(istype(W) && !W.lit && !W.burnt) - if(prob(25)) - W.light(user) - user.visible_message("[user] manages to light the match on the matchbox.") - else - playsound(src, 'sound/items/cigs_lighters/matchstick_hit.ogg', 25, 0, -1) - W.update_icon() - return - -/obj/item/weapon/storage/box/autoinjectors - name = "box of injectors" - desc = "Contains autoinjectors." - icon_state = "auto" - starts_with = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector = 7) - -/obj/item/weapon/storage/box/lights - name = "box of replacement bulbs" - icon = 'icons/obj/boxes.dmi' - icon_state = "light" - desc = "This box is shaped on the inside so that only light tubes and bulbs fit." - item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") - storage_slots = 24 - can_hold = list(/obj/item/weapon/light/tube, /obj/item/weapon/light/bulb) - max_storage_space = ITEMSIZE_COST_SMALL * 24 //holds 24 items of w_class 2 - use_to_pickup = TRUE // for picking up broken bulbs, not that most people will try - -/obj/item/weapon/storage/box/lights/bulbs - starts_with = list(/obj/item/weapon/light/bulb = 24) - -/obj/item/weapon/storage/box/lights/tubes - name = "box of replacement tubes" - icon_state = "lighttube" - starts_with = list(/obj/item/weapon/light/tube = 24) - -/obj/item/weapon/storage/box/lights/mixed - name = "box of replacement lights" - icon_state = "lightmixed" - starts_with = list( - /obj/item/weapon/light/tube = 16, - /obj/item/weapon/light/bulb = 8 - ) - -/obj/item/weapon/storage/box/freezer - name = "portable freezer" - desc = "This nifty shock-resistant device will keep your 'groceries' nice and non-spoiled." - icon = 'icons/obj/storage.dmi' - icon_state = "portafreezer" - item_state_slots = list(slot_r_hand_str = "medicalpack", slot_l_hand_str = "medicalpack") - foldable = null - max_w_class = ITEMSIZE_NORMAL - can_hold = list(/obj/item/organ) - max_storage_space = ITEMSIZE_COST_NORMAL * 5 // Formally 21. Odd numbers are bad. - use_to_pickup = TRUE // for picking up broken bulbs, not that most people will try - -/obj/item/weapon/storage/box/freezer/red - icon_state = "portafreezer_red" - -/obj/item/weapon/storage/box/freezer/Entered(var/atom/movable/AM) - if(istype(AM, /obj/item/organ)) - var/obj/item/organ/O = AM - O.preserved = 1 - for(var/obj/item/organ/organ in O) - organ.preserved = 1 - ..() - -/obj/item/weapon/storage/box/freezer/Exited(var/atom/movable/AM) - if(istype(AM, /obj/item/organ)) - var/obj/item/organ/O = AM - O.preserved = 0 - for(var/obj/item/organ/organ in O) - organ.preserved = 0 - ..() - -/obj/item/weapon/storage/box/ambrosia - name = "ambrosia seeds box" - desc = "Contains the seeds you need to get a little high." - starts_with = list(/obj/item/seeds/ambrosiavulgarisseed = 7) - -/obj/item/weapon/storage/box/ambrosiadeus - name = "ambrosia deus seeds box" - desc = "Contains the seeds you need to get a proper healthy high." - starts_with = list(/obj/item/seeds/ambrosiadeusseed = 7) - -/obj/item/weapon/storage/box/capguntoy - name = "\improper AlliCo \"Zipper\" Cap Gun" - icon = 'icons/obj/gun_toy.dmi' - icon_state = "cap_gun_box" - desc = "This box is shaped on the inside so that only the \"Zipper\" Capgun and extra caps can fit." - item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") - storage_slots = 2 - max_w_class = ITEMSIZE_NORMAL - can_hold = list(/obj/item/weapon/gun/projectile/revolver/capgun, /obj/item/ammo_magazine/ammo_box/cap) - starts_with = list( - /obj/item/weapon/gun/projectile/revolver/capgun = 1, - /obj/item/ammo_magazine/ammo_box/cap = 1 - ) - -//Donk-pockets -/obj/item/weapon/storage/box/donkpockets - name = "box of donk-pockets" - desc = "Instructions: Heat in microwave. Product will cool if not eaten within seven minutes." - icon_state = "donkpocketbox" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket = 7) - -/obj/item/weapon/storage/box/donkpockets/spicy - name = "box of spicy-flavoured donk-pockets" - icon_state = "donkpocketboxspicy" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/spicy = 7) - -/obj/item/weapon/storage/box/donkpockets/teriyaki - name = "box of teriyaki-flavoured donk-pockets" - icon_state = "donkpocketboxteriyaki" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/teriyaki = 7) - -/obj/item/weapon/storage/box/donkpockets/pizza - name = "box of pizza-flavoured donk-pockets" - icon_state = "donkpocketboxpizza" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/pizza = 7) - -/obj/item/weapon/storage/box/donkpockets/honk - name = "box of banana-flavoured donk-pockets" - icon_state = "donkpocketboxbanana" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/honk = 7) - -/obj/item/weapon/storage/box/donkpockets/gondola - name = "box of gondola-flavoured donk-pockets" - icon_state = "donkpocketboxgondola" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/gondola = 7) - -/obj/item/weapon/storage/box/donkpockets/berry - name = "box of berry-flavoured donk-pockets" - icon_state = "donkpocketboxberry" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/berry = 7) - -/obj/item/weapon/storage/box/sinpockets - name = "box of sin-pockets" - desc = "Instructions: Crush bottom of package to initiate chemical heating. Wait for 20 seconds before consumption. Product will cool if not eaten within seven minutes." - icon_state = "donk_kit" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/sinpocket = 7) +/* + * Everything derived from the common cardboard box. + * Basically everything except the original is a kit (starts full). + * + * Contains: + * Empty box, starter boxes (survival/engineer), + * Latex glove and sterile mask boxes, + * Syringe, beaker, dna injector boxes, + * Blanks, flashbangs, and EMP grenade boxes, + * Tracking and chemical implant boxes, + * Prescription glasses and drinking glass boxes, + * Condiment bottle and silly cup boxes, + * Donkpocket and monkeycube boxes, + * ID and security PDA cart boxes, + * Handcuff, mousetrap, and pillbottle boxes, + * Snap-pops and matchboxes, + * Replacement light boxes. + * + * For syndicate call-ins see uplink_kits.dm + */ + +/obj/item/weapon/storage/box + name = "box" + desc = "It's just an ordinary box." + icon = 'icons/obj/boxes.dmi' + icon_state = "box" + item_state = "syringe_kit" + center_of_mass = list("x" = 13,"y" = 10) + var/foldable = /obj/item/stack/material/cardboard // BubbleWrap - if set, can be folded (when empty) into a sheet of cardboard + var/trash = null // if set, can be crushed into a trash item when empty + max_w_class = ITEMSIZE_SMALL + max_storage_space = INVENTORY_BOX_SPACE + use_sound = 'sound/items/storage/box.ogg' + drop_sound = 'sound/items/drop/cardboardbox.ogg' + pickup_sound = 'sound/items/pickup/cardboardbox.ogg' + +// BubbleWrap - A box can be folded up to make card +/obj/item/weapon/storage/box/attack_self(mob/user as mob) + if(..()) return + + //try to fold it + if(ispath(foldable)) + if (contents.len) + return + var/found = 0 + // Close any open UI windows first + for(var/mob/M in range(1)) + if (M.s_active == src) + close(M) + if (M == user) + found = 1 + if (!found) // User is too far away + return + // Now make the cardboard + to_chat(user, "You fold [src] flat.") + playsound(src, 'sound/items/storage/boxfold.ogg', 30, 1) + new foldable(get_turf(src)) + qdel(src) + + //try to crush it + if(ispath(trash)) + if(contents.len && user.a_intent == I_HURT) // only crumple with things inside on harmintent. + user.visible_message(SPAN_DANGER("[user] crushes \the [src], spilling its contents everywhere!"), SPAN_DANGER("You crush \the [src], spilling its contents everywhere!")) + spill() + else + to_chat(user, SPAN_NOTICE("You crumple up \the [src].")) //make trash + playsound(src.loc, 'sound/items/drop/wrapper.ogg', 30, 1) + var/obj/item/trash = new src.trash() + qdel(src) + user.put_in_hands(trash) + + +/obj/item/weapon/storage/box/survival + name = "emergency supply box" + desc = "A survival box issued to crew members for use in emergency situations." + icon_state = "survival" + starts_with = list( + /obj/item/weapon/tool/prybar/red, + /obj/item/clothing/glasses/goggles, + /obj/item/clothing/mask/breath + ) + +/obj/item/weapon/storage/box/survival/synth + name = "synthetic supply box" + desc = "A survival box issued to synthetic crew members for use in emergency situations." + icon_state = "survival_synth" + starts_with = list( + /obj/item/weapon/tool/prybar/red, + /obj/item/clothing/glasses/goggles + ) + +/obj/item/weapon/storage/box/survival/comp + name = "emergency supply box" + desc = "A comprehensive survival box issued to crew members for use in emergency situations. Contains additional supplies." + icon_state = "survival_comp" + starts_with = list( + /obj/item/weapon/tool/prybar/red, + /obj/item/clothing/glasses/goggles, + /obj/item/weapon/reagent_containers/hypospray/autoinjector, + /obj/item/stack/medical/bruise_pack, + /obj/item/device/flashlight/glowstick, + /obj/item/weapon/reagent_containers/food/snacks/candy/proteinbar, + /obj/item/clothing/mask/breath + ) + +/obj/item/weapon/storage/box/gloves + name = "box of latex gloves" + desc = "Contains white gloves." + icon_state = "latex" + starts_with = list(/obj/item/clothing/gloves/sterile/latex = 7) + +/obj/item/weapon/storage/box/masks + name = "box of sterile masks" + desc = "This box contains masks of sterility." + icon_state = "sterile" + starts_with = list(/obj/item/clothing/mask/surgical = 7) + +/obj/item/weapon/storage/box/masks/white + name = "box of sterile masks" + desc = "This box contains masks of sterility." + icon_state = "sterile" + starts_with = list(/obj/item/clothing/mask/surgical/white = 7) + +/obj/item/weapon/storage/box/masks/dust + name = "box of dust masks" + desc = "This box contains dust masks. Breathe easy." + icon_state = "sterile" + starts_with = list(/obj/item/clothing/mask/surgical/dust = 7) + +/obj/item/weapon/storage/box/syringes + name = "box of syringes" + desc = "A box full of syringes." + icon_state = "syringe" + starts_with = list(/obj/item/weapon/reagent_containers/syringe = 7) + +/obj/item/weapon/storage/box/syringegun + name = "box of syringe gun cartridges" + desc = "A box full of compressed gas cartridges." + icon_state = "syringe2" + starts_with = list(/obj/item/weapon/syringe_cartridge = 7) + +/obj/item/weapon/storage/box/beakers + name = "box of beakers" + desc = "A box full of beakers." + icon_state = "beaker" + starts_with = list(/obj/item/weapon/reagent_containers/glass/beaker = 7) + +/obj/item/weapon/storage/box/injectors + name = "box of DNA injectors" + desc = "This box contains injectors it seems." + icon_state = "dna" + starts_with = list( + /obj/item/weapon/dnainjector/h2m = 3, + /obj/item/weapon/dnainjector/m2h = 3 + ) + +/obj/item/weapon/storage/box/flashbangs + name = "box of flashbangs (WARNING)" + desc = "WARNING: These devices are extremely dangerous and can cause blindness or deafness in repeated use." + icon_state = "flashbang" + starts_with = list(/obj/item/weapon/grenade/flashbang = 7) + drop_sound = 'sound/items/drop/ammobox.ogg' + pickup_sound = 'sound/items/pickup/ammobox.ogg' + +/obj/item/weapon/storage/box/emps + name = "box of emp grenades" + desc = "A box containing 5 military grade EMP grenades.
                    WARNING: Do not use near unshielded electronics or biomechanical augmentations, death or permanent paralysis may occur." + icon_state = "emp" + starts_with = list(/obj/item/weapon/grenade/empgrenade = 7) + drop_sound = 'sound/items/drop/ammobox.ogg' + pickup_sound = 'sound/items/pickup/ammobox.ogg' + +/obj/item/weapon/storage/box/empslite + name = "box of low yield emp grenades" + desc = "A box containing 5 low yield EMP grenades.
                    WARNING: Do not use near unshielded electronics or biomechanical augmentations, death or permanent paralysis may occur." + icon_state = "emp" + starts_with = list(/obj/item/weapon/grenade/empgrenade/low_yield = 7) + drop_sound = 'sound/items/drop/ammobox.ogg' + pickup_sound = 'sound/items/pickup/ammobox.ogg' + +/obj/item/weapon/storage/box/smokes + name = "box of smoke bombs" + desc = "A box containing 7 smoke bombs." + icon_state = "flashbang" + starts_with = list(/obj/item/weapon/grenade/smokebomb = 7) + drop_sound = 'sound/items/drop/ammobox.ogg' + pickup_sound = 'sound/items/pickup/ammobox.ogg' + +/obj/item/weapon/storage/box/anti_photons + name = "box of anti-photon grenades" + desc = "A box containing 7 experimental photon disruption grenades." + icon_state = "flashbang" + starts_with = list(/obj/item/weapon/grenade/anti_photon = 7) + drop_sound = 'sound/items/drop/ammobox.ogg' + pickup_sound = 'sound/items/pickup/ammobox.ogg' + +/obj/item/weapon/storage/box/frags + name = "box of fragmentation grenades (WARNING)" + desc = "A box containing 7 military grade fragmentation grenades.
                    WARNING: These devices are extremely dangerous and can cause limb loss or death in repeated use." + icon_state = "frag" + starts_with = list(/obj/item/weapon/grenade/explosive = 7) + drop_sound = 'sound/items/drop/ammobox.ogg' + pickup_sound = 'sound/items/pickup/ammobox.ogg' + +/obj/item/weapon/storage/box/frags_half_box + name = "box of fragmentation grenades (WARNING)" + desc = "A box containing 4 military grade fragmentation grenades.
                    WARNING: These devices are extremely dangerous and can cause limb loss or death in repeated use." + icon_state = "frag" + starts_with = list(/obj/item/weapon/grenade/explosive = 4) + drop_sound = 'sound/items/drop/ammobox.ogg' + pickup_sound = 'sound/items/pickup/ammobox.ogg' + +/obj/item/weapon/storage/box/metalfoam + name = "box of metal foam grenades." + desc = "A box containing 7 metal foam grenades." + icon_state = "flashbang" + starts_with = list(/obj/item/weapon/grenade/chem_grenade/metalfoam = 7) + +/obj/item/weapon/storage/box/teargas + name = "box of teargas grenades" + desc = "A box containing 7 teargas grenades." + icon_state = "flashbang" + starts_with = list(/obj/item/weapon/grenade/chem_grenade/teargas = 7) + +/obj/item/weapon/storage/box/flare + name = "box of flares" + desc = "A box containing 4 flares." + starts_with = list(/obj/item/device/flashlight/flare = 4) + +/obj/item/weapon/storage/box/trackimp + name = "boxed tracking implant kit" + desc = "Box full of scum-bag tracking utensils." + icon_state = "implant" + starts_with = list( + /obj/item/weapon/implantcase/tracking = 4, + /obj/item/weapon/implanter, + /obj/item/weapon/implantpad, + /obj/item/weapon/locator + ) + +/obj/item/weapon/storage/box/chemimp + name = "boxed chemical implant kit" + desc = "Box of stuff used to implant chemicals." + icon_state = "implant" + starts_with = list( + /obj/item/weapon/implantcase/chem = 5, + /obj/item/weapon/implanter, + /obj/item/weapon/implantpad + ) + +/obj/item/weapon/storage/box/camerabug + name = "mobile camera pod box" + desc = "A box containing some mobile camera pods." + icon_state = "pda" + starts_with = list( + /obj/item/device/camerabug = 6, + /obj/item/device/bug_monitor + ) + +/obj/item/weapon/storage/box/rxglasses + name = "box of prescription glasses" + desc = "This box contains nerd glasses." + icon_state = "glasses" + starts_with = list(/obj/item/clothing/glasses/regular = 7) + +/obj/item/weapon/storage/box/cdeathalarm_kit + name = "death alarm kit" + desc = "Box of stuff used to implant death alarms." + icon_state = "implant" + item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") + starts_with = list( + /obj/item/weapon/implantcase/death_alarm = 7, + /obj/item/weapon/implanter + ) + +/obj/item/weapon/storage/box/condimentbottles + name = "box of condiment bottles" + desc = "It has a large ketchup smear on it." + icon_state = "condiment" + starts_with = list(/obj/item/weapon/reagent_containers/food/condiment = 7) + +/obj/item/weapon/storage/box/cups + name = "box of paper cups" + desc = "It has pictures of paper cups on the front." + icon_state = "cups" + starts_with = list(/obj/item/weapon/reagent_containers/food/drinks/sillycup = 7) + +/obj/item/weapon/storage/box/buns + name = "box of bread buns" + desc = "Freshly baked at some point in the past few months." + icon_state = "bun_box" + max_storage_space = ITEMSIZE_COST_NORMAL * 5 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/bun = 12) + +/obj/item/weapon/storage/box/monkeycubes + name = "monkey cube box" + desc = "Drymate brand monkey cubes. Just add water!" + icon = 'icons/obj/food.dmi' + icon_state = "monkeycubebox" + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube) + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped = 4) + +/obj/item/weapon/storage/box/monkeycubes/farwacubes + name = "farwa cube box" + desc = "Drymate brand farwa cubes, shipped from Meralar. Just add water!" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped/farwacube = 4) + +/obj/item/weapon/storage/box/monkeycubes/stokcubes + name = "stok cube box" + desc = "Drymate brand stok cubes, shipped from Moghes. Just add water!" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped/stokcube = 4) + +/obj/item/weapon/storage/box/monkeycubes/neaeracubes + name = "neaera cube box" + desc = "Drymate brand neaera cubes, shipped from Qerr'balak. Just add water!" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped/neaeracube = 4) + +/obj/item/weapon/storage/box/ids + name = "box of spare IDs" + desc = "Has so many empty IDs." + icon_state = "id" + starts_with = list(/obj/item/weapon/card/id = 7) + +/obj/item/weapon/storage/box/seccarts + name = "box of spare R.O.B.U.S.T. Cartridges" + desc = "A box full of R.O.B.U.S.T. Cartridges, used by Security." + icon_state = "pda" + starts_with = list(/obj/item/weapon/cartridge/security = 7) + +/obj/item/weapon/storage/box/handcuffs + name = "box of spare handcuffs" + desc = "A box full of handcuffs." + icon_state = "handcuff" + starts_with = list(/obj/item/weapon/handcuffs = 7) + +/obj/item/weapon/storage/box/mousetraps + name = "box of Pest-B-Gon mousetraps" + desc = span_red("WARNING:") + " Keep out of reach of children." + icon_state = "mousetraps" + starts_with = list(/obj/item/device/assembly/mousetrap = 7) + +/obj/item/weapon/storage/box/pillbottles + name = "box of pill bottles" + desc = "It has pictures of pill bottles on its front." + icon_state = "pillbox" + starts_with = list(/obj/item/weapon/storage/pill_bottle = 7) + +/obj/item/weapon/storage/box/snappops + name = "snap pop box" + desc = "Eight wrappers of fun! Ages 8 and up. Not suitable for children." + icon = 'icons/obj/toy.dmi' + icon_state = "spbox" + can_hold = list(/obj/item/toy/snappop) + starts_with = list(/obj/item/toy/snappop = 8) + +/obj/item/weapon/storage/box/matches + name = "matchbox" + desc = "A small box of 'Space-Proof' premium matches." + icon = 'icons/obj/cigarettes.dmi' + icon_state = "matchbox" + w_class = ITEMSIZE_TINY + slot_flags = SLOT_BELT + can_hold = list(/obj/item/weapon/flame/match) + starts_with = list(/obj/item/weapon/flame/match = 10) + drop_sound = 'sound/items/drop/matchbox.ogg' + pickup_sound = 'sound/items/pickup/matchbox.ogg' + +/obj/item/weapon/storage/box/matches/attackby(var/obj/item/weapon/flame/match/W, var/mob/user) + if(istype(W) && !W.lit && !W.burnt) + if(prob(25)) + W.light(user) + user.visible_message("[user] manages to light the match on the matchbox.") + else + playsound(src, 'sound/items/cigs_lighters/matchstick_hit.ogg', 25, 0, -1) + W.update_icon() + return + +/obj/item/weapon/storage/box/autoinjectors + name = "box of injectors" + desc = "Contains autoinjectors." + icon_state = "auto" + starts_with = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector = 7) + +/obj/item/weapon/storage/box/lights + name = "box of replacement bulbs" + icon = 'icons/obj/boxes.dmi' + icon_state = "light" + desc = "This box is shaped on the inside so that only light tubes and bulbs fit." + item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") + storage_slots = 24 + can_hold = list(/obj/item/weapon/light/tube, /obj/item/weapon/light/bulb) + max_storage_space = ITEMSIZE_COST_SMALL * 24 //holds 24 items of w_class 2 + use_to_pickup = TRUE // for picking up broken bulbs, not that most people will try + +/obj/item/weapon/storage/box/lights/bulbs + starts_with = list(/obj/item/weapon/light/bulb = 24) + +/obj/item/weapon/storage/box/lights/tubes + name = "box of replacement tubes" + icon_state = "lighttube" + starts_with = list(/obj/item/weapon/light/tube = 24) + +/obj/item/weapon/storage/box/lights/mixed + name = "box of replacement lights" + icon_state = "lightmixed" + starts_with = list( + /obj/item/weapon/light/tube = 16, + /obj/item/weapon/light/bulb = 8 + ) + +/obj/item/weapon/storage/box/freezer + name = "portable freezer" + desc = "This nifty shock-resistant device will keep your 'groceries' nice and non-spoiled." + icon = 'icons/obj/storage.dmi' + icon_state = "portafreezer" + item_state_slots = list(slot_r_hand_str = "medicalpack", slot_l_hand_str = "medicalpack") + foldable = null + max_w_class = ITEMSIZE_NORMAL + can_hold = list(/obj/item/organ) + max_storage_space = ITEMSIZE_COST_NORMAL * 5 // Formally 21. Odd numbers are bad. + use_to_pickup = TRUE // for picking up broken bulbs, not that most people will try + +/obj/item/weapon/storage/box/freezer/red + icon_state = "portafreezer_red" + +/obj/item/weapon/storage/box/freezer/Entered(var/atom/movable/AM) + if(istype(AM, /obj/item/organ)) + var/obj/item/organ/O = AM + O.preserved = 1 + for(var/obj/item/organ/organ in O) + organ.preserved = 1 + ..() + +/obj/item/weapon/storage/box/freezer/Exited(var/atom/movable/AM) + if(istype(AM, /obj/item/organ)) + var/obj/item/organ/O = AM + O.preserved = 0 + for(var/obj/item/organ/organ in O) + organ.preserved = 0 + ..() + +/obj/item/weapon/storage/box/ambrosia + name = "ambrosia seeds box" + desc = "Contains the seeds you need to get a little high." + starts_with = list(/obj/item/seeds/ambrosiavulgarisseed = 7) + +/obj/item/weapon/storage/box/ambrosiadeus + name = "ambrosia deus seeds box" + desc = "Contains the seeds you need to get a proper healthy high." + starts_with = list(/obj/item/seeds/ambrosiadeusseed = 7) + +/obj/item/weapon/storage/box/capguntoy + name = "\improper AlliCo \"Zipper\" Cap Gun" + icon = 'icons/obj/gun_toy.dmi' + icon_state = "cap_gun_box" + desc = "This box is shaped on the inside so that only the \"Zipper\" Capgun and extra caps can fit." + item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") + storage_slots = 2 + max_w_class = ITEMSIZE_NORMAL + can_hold = list(/obj/item/weapon/gun/projectile/revolver/capgun, /obj/item/ammo_magazine/ammo_box/cap) + starts_with = list( + /obj/item/weapon/gun/projectile/revolver/capgun = 1, + /obj/item/ammo_magazine/ammo_box/cap = 1 + ) + +//Donk-pockets +/obj/item/weapon/storage/box/donkpockets + name = "box of donk-pockets" + desc = "Instructions: Heat in microwave. Product will cool if not eaten within seven minutes." + icon_state = "donkpocketbox" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket = 7) + +/obj/item/weapon/storage/box/donkpockets/spicy + name = "box of spicy-flavoured donk-pockets" + icon_state = "donkpocketboxspicy" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/spicy = 7) + +/obj/item/weapon/storage/box/donkpockets/teriyaki + name = "box of teriyaki-flavoured donk-pockets" + icon_state = "donkpocketboxteriyaki" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/teriyaki = 7) + +/obj/item/weapon/storage/box/donkpockets/pizza + name = "box of pizza-flavoured donk-pockets" + icon_state = "donkpocketboxpizza" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/pizza = 7) + +/obj/item/weapon/storage/box/donkpockets/honk + name = "box of banana-flavoured donk-pockets" + icon_state = "donkpocketboxbanana" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/honk = 7) + +/obj/item/weapon/storage/box/donkpockets/gondola + name = "box of gondola-flavoured donk-pockets" + icon_state = "donkpocketboxgondola" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/gondola = 7) + +/obj/item/weapon/storage/box/donkpockets/berry + name = "box of berry-flavoured donk-pockets" + icon_state = "donkpocketboxberry" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/berry = 7) + +/obj/item/weapon/storage/box/sinpockets + name = "box of sin-pockets" + desc = "Instructions: Crush bottom of package to initiate chemical heating. Wait for 20 seconds before consumption. Product will cool if not eaten within seven minutes." + icon_state = "donk_kit" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/sinpocket = 7) diff --git a/code/game/objects/items/weapons/storage/briefcase.dm b/code/game/objects/items/weapons/storage/briefcase.dm index a3cc27d9265..c8cdcffc1e3 100644 --- a/code/game/objects/items/weapons/storage/briefcase.dm +++ b/code/game/objects/items/weapons/storage/briefcase.dm @@ -1,32 +1,32 @@ -/obj/item/weapon/storage/briefcase - name = "briefcase" - desc = "It's made of AUTHENTIC faux-leather and has a price-tag still attached. Its owner must be a real professional." - icon_state = "briefcase" - force = 8.0 - throw_speed = 1 - throw_range = 4 - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_NORMAL * 4 - use_sound = 'sound/items/storage/briefcase.ogg' - drop_sound = 'sound/items/drop/backpack.ogg' - pickup_sound = 'sound/items/pickup/backpack.ogg' - -/obj/item/weapon/storage/briefcase/clutch - name = "clutch purse" - desc = "A fashionable handheld bag typically used by women." - icon_state = "clutch" - item_state_slots = list(slot_r_hand_str = "smpurse", slot_l_hand_str = "smpurse") - force = 0 - w_class = ITEMSIZE_NORMAL - max_w_class = ITEMSIZE_SMALL - max_storage_space = ITEMSIZE_COST_SMALL * 4 - -/obj/item/weapon/storage/briefcase/bookbag - name = "bookbag" - desc = "A small bookbag for holding... things other than books?" - icon_state = "bookbag" - force = 4.0 - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_NORMAL +/obj/item/weapon/storage/briefcase + name = "briefcase" + desc = "It's made of AUTHENTIC faux-leather and has a price-tag still attached. Its owner must be a real professional." + icon_state = "briefcase" + force = 8.0 + throw_speed = 1 + throw_range = 4 + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_NORMAL * 4 + use_sound = 'sound/items/storage/briefcase.ogg' + drop_sound = 'sound/items/drop/backpack.ogg' + pickup_sound = 'sound/items/pickup/backpack.ogg' + +/obj/item/weapon/storage/briefcase/clutch + name = "clutch purse" + desc = "A fashionable handheld bag typically used by women." + icon_state = "clutch" + item_state_slots = list(slot_r_hand_str = "smpurse", slot_l_hand_str = "smpurse") + force = 0 + w_class = ITEMSIZE_NORMAL + max_w_class = ITEMSIZE_SMALL + max_storage_space = ITEMSIZE_COST_SMALL * 4 + +/obj/item/weapon/storage/briefcase/bookbag + name = "bookbag" + desc = "A small bookbag for holding... things other than books?" + icon_state = "bookbag" + force = 4.0 + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_NORMAL max_storage_space = ITEMSIZE_COST_NORMAL * 4 \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/fancy.dm b/code/game/objects/items/weapons/storage/fancy.dm index 639bd175a47..850bc5246ed 100644 --- a/code/game/objects/items/weapons/storage/fancy.dm +++ b/code/game/objects/items/weapons/storage/fancy.dm @@ -1,558 +1,558 @@ -/* - * The 'fancy' path is for objects like donut boxes that show how many items are in the storage item on the sprite itself - * .. Sorry for the shitty path name, I couldnt think of a better one. - * - * WARNING: var/icon_type is used for both examine text and sprite name. Please look at the procs below and adjust your sprite names accordingly - * - * Contains: - * Donut Box - * Egg Box - * Candle Box - * Crayon Box - * Cracker Pack - * Cigarette Pack - * Cigar Box - * Extra Tobacco Bits - * Vial Box - * Box of Chocolates - */ - -/obj/item/weapon/storage/fancy/ - icon = 'icons/obj/food.dmi' - icon_state = "donutbox6" - name = "donut box" - var/icon_type = "donut" - drop_sound = 'sound/items/drop/cardboardbox.ogg' - pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - var/open = 0 - var/open_state - var/closed_state - -/obj/item/weapon/storage/fancy/update_icon(var/itemremoved = 0) - var/total_contents = contents.len - itemremoved - icon_state = "[icon_type]box[total_contents]" - return - -/obj/item/weapon/storage/fancy/examine(mob/user) - . = ..() - - if(Adjacent(user)) - if(!contents.len) - . += "There are no [icon_type]s left in the box." - else if(contents.len == 1) - . += "There is one [icon_type] left in the box." - else - . += "There are [contents.len] [icon_type]s in the box." - -/* - * Egg Box - */ -/obj/item/weapon/storage/fancy/egg_box - icon = 'icons/obj/food.dmi' - icon_state = "eggbox" - icon_type = "egg" - name = "egg box" - center_of_mass = list("x" = 16,"y" = 7) - storage_slots = 12 - can_hold = list( - /obj/item/weapon/reagent_containers/food/snacks/egg, - /obj/item/weapon/reagent_containers/food/snacks/boiledegg - ) - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/egg = 12) - -/obj/item/weapon/storage/fancy/egg_box/New() - if(!open_state) - open_state = "[initial(icon_state)]0" - if(!closed_state) - closed_state = "[initial(icon_state)]" - ..() - -/obj/item/weapon/storage/fancy/egg_box/update_icon() - cut_overlays() - if(open) - icon_state = open_state - if(contents.len >= 1) - add_overlay("eggbox[contents.len]") - else - icon_state = closed_state - -/obj/item/weapon/storage/fancy/egg_box/open(mob/user as mob) - if(open) - return - if (isobserver(usr)) - return - open = TRUE - update_icon() - ..() - -/obj/item/weapon/storage/fancy/egg_box/close(mob/user as mob) - open = FALSE - update_icon() - ..() - -/* - * Candle Boxes - */ -/obj/item/weapon/storage/fancy/candle_box - name = "red candle pack" - desc = "A pack of red candles." - icon = 'icons/obj/candle.dmi' - icon_state = "candlebox5" - icon_type = "candle" - item_state = "candlebox5" - throwforce = 2 - slot_flags = SLOT_BELT - max_storage_space = ITEMSIZE_COST_TINY * 5 - can_hold = list(/obj/item/weapon/flame/candle) - starts_with = list(/obj/item/weapon/flame/candle = 5) - -/obj/item/weapon/storage/fancy/whitecandle_box - name = "white candle pack" - desc = "A pack of white candles." - icon = 'icons/obj/candle.dmi' - icon_state = "whitecandlebox5" - icon_type = "whitecandle" - item_state = "whitecandlebox5" - throwforce = 2 - slot_flags = SLOT_BELT - max_storage_space = ITEMSIZE_COST_TINY * 5 - can_hold = list(/obj/item/weapon/flame/candle) - starts_with = list(/obj/item/weapon/flame/candle/white = 5) - -/obj/item/weapon/storage/fancy/blackcandle_box - name = "black candle pack" - desc = "A pack of black candles." - icon = 'icons/obj/candle.dmi' - icon_state = "blackcandlebox5" - icon_type = "blackcandle" - item_state = "blackcandlebox5" - throwforce = 2 - slot_flags = SLOT_BELT - max_storage_space = ITEMSIZE_COST_TINY * 5 - can_hold = list(/obj/item/weapon/flame/candle) - starts_with = list(/obj/item/weapon/flame/candle/black = 5) - - -/* - * Crayon Box - */ -/obj/item/weapon/storage/fancy/crayons - name = "box of crayons" - desc = "A box of crayons for all your rune drawing needs." - icon = 'icons/obj/crayons.dmi' - icon_state = "crayonbox" - w_class = ITEMSIZE_SMALL - icon_type = "crayon" - can_hold = list( - /obj/item/weapon/pen/crayon - ) - starts_with = list( - /obj/item/weapon/pen/crayon/red, - /obj/item/weapon/pen/crayon/orange, - /obj/item/weapon/pen/crayon/yellow, - /obj/item/weapon/pen/crayon/green, - /obj/item/weapon/pen/crayon/blue, - /obj/item/weapon/pen/crayon/purple - ) - -/obj/item/weapon/storage/fancy/crayons/Initialize() - . = ..() - update_icon() - -/obj/item/weapon/storage/fancy/crayons/update_icon() - var/mutable_appearance/ma = new(src) - ma.cut_overlays() - for(var/obj/item/weapon/pen/crayon/crayon in contents) - add_overlay(image('icons/obj/crayons.dmi',crayon.colourName)) - appearance = ma - -/obj/item/weapon/storage/fancy/crayons/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W,/obj/item/weapon/pen/crayon)) - switch(W:colourName) - if("mime") - to_chat(user, "This crayon is too sad to be contained in this box.") - return - if("rainbow") - to_chat(user, "This crayon is too powerful to be contained in this box.") - return - ..() - -/obj/item/weapon/storage/fancy/markers - name = "box of markers" - desc = "A very professional looking box of permanent markers." - icon = 'icons/obj/crayons.dmi' - icon_state = "markerbox" - w_class = ITEMSIZE_SMALL - icon_type = "marker" - can_hold = list( - /obj/item/weapon/pen/crayon/marker - ) - starts_with = list( - /obj/item/weapon/pen/crayon/marker/black, - /obj/item/weapon/pen/crayon/marker/red, - /obj/item/weapon/pen/crayon/marker/orange, - /obj/item/weapon/pen/crayon/marker/yellow, - /obj/item/weapon/pen/crayon/marker/green, - /obj/item/weapon/pen/crayon/marker/blue, - /obj/item/weapon/pen/crayon/marker/purple - ) - -/obj/item/weapon/storage/fancy/markers/Initialize() - . = ..() - update_icon() - -/obj/item/weapon/storage/fancy/markers/update_icon() - var/mutable_appearance/ma = new(src) - ma.cut_overlays() - for(var/obj/item/weapon/pen/crayon/marker/marker in contents) - ma.add_overlay(image('icons/obj/crayons.dmi',"m"+marker.colourName)) - appearance = ma - -/obj/item/weapon/storage/fancy/markers/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W,/obj/item/weapon/pen/crayon/marker)) - switch(W:colourName) - if("mime") - to_chat(user, "This marker is too depressing to be contained in this box.") - return - if("rainbow") - to_chat(user, "This marker is too childish to be contained in this box.") - return - ..() - -/* - * Cracker Pack - */ -/obj/item/weapon/storage/fancy/crackers - name = "\improper Getmore Crackers" - icon = 'icons/obj/food.dmi' - icon_state = "crackerbox" - icon_type = "cracker" - max_storage_space = ITEMSIZE_COST_TINY * 6 - max_w_class = ITEMSIZE_TINY - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/cracker) - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/cracker = 6) - -/* - * Cigarette Pack - */ -/obj/item/weapon/storage/fancy/cigarettes - name = "\improper pack of Trans-Stellar Duty-frees" - desc = "A ubiquitous brand of cigarettes, found in every major spacefaring corporation in the universe. As mild and flavorless as it gets." - description_fluff = "The Trans-Stellar Duty-Free Cigarette Company was created as an imprint of NanoTrasen. They are the most boring, tasteless, dry cigarettes on the market, but due to just how unremarkable (not to mention cheap to produce) they are, they sold in vending machines in almost every corner of the galaxy." - icon = 'icons/obj/cigarettes.dmi' - icon_state = "cigpacket" - item_state_slots = list(slot_r_hand_str = "cigpacket", slot_l_hand_str = "cigpacket") - w_class = ITEMSIZE_TINY - throwforce = 2 - slot_flags = SLOT_BELT - storage_slots = 6 - can_hold = list(/obj/item/clothing/mask/smokable/cigarette, /obj/item/weapon/flame/lighter, /obj/item/trash/cigbutt) - icon_type = "cigarette" - starts_with = list(/obj/item/clothing/mask/smokable/cigarette = 6) - var/brand = "\improper Trans-Stellar Duty-free" - -/obj/item/weapon/storage/fancy/cigarettes/Initialize() - . = ..() - flags |= NOREACT - create_reagents(15 * storage_slots)//so people can inject cigarettes without opening a packet, now with being able to inject the whole one - flags |= OPENCONTAINER - if(brand) - for(var/obj/item/clothing/mask/smokable/cigarette/C in src) - C.brand = brand - C.desc += " This one is \a [brand]." - -/obj/item/weapon/storage/fancy/cigarettes/New() - if(!open_state) - open_state = "[initial(icon_state)]_open" - if(!closed_state) - closed_state = "[initial(icon_state)]" - ..() - -/obj/item/weapon/storage/fancy/cigarettes/update_icon() - cut_overlays() - if(open) - icon_state = open_state - if(contents.len >= 1) - add_overlay("cig[contents.len]") - else - icon_state = closed_state - -/obj/item/weapon/storage/fancy/cigarettes/open(mob/user as mob) - if(open) - return - open = TRUE - if(contents.len == 0) - icon_state = "[initial(icon_state)]_empty" - else - update_icon() - ..() - -/obj/item/weapon/storage/fancy/cigarettes/close(mob/user as mob) - open = FALSE - if(contents.len == 0) - icon_state = "[initial(icon_state)]_empty" - else - update_icon() - ..() - -/obj/item/weapon/storage/fancy/cigarettes/remove_from_storage(obj/item/W as obj, atom/new_location) - // Don't try to transfer reagents to lighters - if(istype(W, /obj/item/clothing/mask/smokable/cigarette)) - var/obj/item/clothing/mask/smokable/cigarette/C = W - reagents.trans_to_obj(C, (reagents.total_volume/contents.len)) - return ..() - -/obj/item/weapon/storage/fancy/cigarettes/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - if(!istype(M, /mob)) - return - - if(M == user && user.zone_sel.selecting == O_MOUTH) - // Find ourselves a cig. Note that we could be full of lighters. - var/obj/item/clothing/mask/smokable/cigarette/cig = locate() in src - - if(cig == null) - to_chat(user, "Looks like the packet is out of cigarettes.") - return - - // Instead of running equip_to_slot_if_possible() we check here first, - // to avoid dousing cig with reagents if we're not going to equip it - if(!cig.mob_can_equip(user, slot_wear_mask)) - return - - // We call remove_from_storage first to manage the reagent transfer and - // UI updates. - remove_from_storage(cig, null) - user.equip_to_slot(cig, slot_wear_mask) - - reagents.maximum_volume = 15 * contents.len - to_chat(user, "You take a cigarette out of the pack.") - update_icon() - else - ..() - -/obj/item/weapon/storage/fancy/cigarettes/dromedaryco - name = "\improper DromedaryCo packet" - desc = "A packet of six Earth-export DromedaryCo cancer sticks. A label on the packaging reads, \"Wouldn't a slow death make a change?\"" - description_fluff = "DromedaryCo is one of Sol's oldest cigarette brands, and takes pride in having sourced tobcacco from the same Indian plantations since 2044. Popular with those willing to pay extra for a little nostalgia." - icon_state = "Dpacket" - brand = "\improper Dromedary Co. cigarette" - -/obj/item/weapon/storage/fancy/cigarettes/killthroat - name = "\improper AcmeCo packet" - desc = "A packet of six AcmeCo cigarettes. For those who want to obtain a record for the most cancerous tumors on a budget." - description_fluff = "Available anywhere people breathe and want to breathe less, AcmeCo is the cheapest, most widespread cigarette brand in the galaxy. They taste like trash, but when you're keeping them inside your jumpsuit on a 16 hour shift, you're probably not too concerned with flavour." - icon_state = "Apacket" - brand = "\improper Acme Co. cigarette" - -/obj/item/weapon/storage/fancy/cigarettes/luckystars - name = "\improper pack of Lucky Stars" - desc = "A mellow blend made from synthetic, pod-grown tobacco. The commercial jingle is guaranteed to get stuck in your head." - description_fluff = "Lucky Stars are some of the most prolific advertisers in the business, with Gilthari Exports plastering the name and slogan on everything from workplace safety videos to racing bikes. 'Feel the gentle warmth of your Lucky Star'." - icon_state = "LSpacket" - brand = "\improper Lucky Star" - -/obj/item/weapon/storage/fancy/cigarettes/jerichos - name = "\improper pack of Jerichos" - desc = "Typically seen dangling from the lips of Fleet veterans and border world hustlers. Tastes like hickory smoke, feels like warm liquid death down your lungs." - description_fluff = "The Jericho brand has carefully cultivated its 'rugged' image ever since its completely accidental association with the SolGov-Hegemony war due to their sizable corporate presence in the region. Prior to the war, Jerichos were considered the realm of drunks and sad divorcees." - icon_state = "Jpacket" - brand = "\improper Jericho" - -/obj/item/weapon/storage/fancy/cigarettes/menthols - name = "\improper pack of Temperamento Menthols" - desc = "With a sharp and natural organic menthol flavor, these Temperamentos are a favorite of science vessel crews. Hardly anyone knows they make 'em in non-menthol!" - description_fluff = "Temperamento Menthols are a product of the Aether Atmospherics and Recycling company, and the 'smooth' menthol taste is rumoured to be the chemical by-product of some far more profitable industrial synthesis." - icon_state = "TMpacket" - brand = "\improper Temperamento Menthol" - -/obj/item/weapon/storage/fancy/cigarettes/carcinomas - name = "\improper pack of Carcinoma Angels" - desc = "This previously unknown brand was slated for the chopping block, until they were publicly endorsed by an old Earthling gonzo journalist. The rest is history. They sell a variety for cats, too." - description_fluff = "The bitter taste of a Carcinoma Angel is considered desirable by many equally bitter wash-ups who consider themselves to be 'hard-boiled'. The smell is practically inseparable from urban security offices, and old men with exonet radio shows." - brand = "\improper Carcinoma Angel" - -/obj/item/weapon/storage/fancy/cigarettes/professionals - name = "\improper pack of Professional 120s" - desc = "Let's face it - if you're smoking these, you're either trying to look upper-class or you're 80 years old. That's the only excuse. They are, however, very good quality." - description_fluff = "Grown and rolled in a meticulously maintained biosphere orbitting Love, P120 tobacco is marketed as 'probably the best in the galaxy'. The premium price point, and the fact that the vast majority of consumers couldn't really tell the difference between this and the next leading brand." - icon_state = "P100packet" - brand = "\improper Professional 120" - -/* - * Cigar Box - */ -/obj/item/weapon/storage/fancy/cigar - name = "cigar case" - desc = "A case for holding your cigars when you are not smoking them." - description_fluff = "The tasteful stained palm case tells you that these 'Palma Grande' premium \ - cigars are only sold on the luxury cruises and resorts of Oasis, though ten separate companies \ - produce them for that purpose galaxy-wide. The standard is however very high." - icon_state = "cigarcase" - icon = 'icons/obj/cigarettes.dmi' - w_class = ITEMSIZE_TINY - throwforce = 2 - slot_flags = SLOT_BELT - storage_slots = 5 - can_hold = list(/obj/item/clothing/mask/smokable/cigarette/cigar, /obj/item/trash/cigbutt/cigarbutt) - icon_type = "cigar" - starts_with = list(/obj/item/clothing/mask/smokable/cigarette/cigar = 5) - -/obj/item/weapon/storage/fancy/cigar/Initialize() - . = ..() - flags |= NOREACT - create_reagents(15 * storage_slots) - -/obj/item/weapon/storage/fancy/cigar/remove_from_storage(obj/item/W as obj, atom/new_location) - var/obj/item/clothing/mask/smokable/cigarette/cigar/C = W - if(!istype(C)) return - reagents.trans_to_obj(C, (reagents.total_volume/contents.len)) - return ..() - -/obj/item/weapon/storage/fancy/cigar/New() - if(!open_state) - open_state = "[initial(icon_state)]0" - if(!closed_state) - closed_state = "[initial(icon_state)]" - ..() - -/obj/item/weapon/storage/fancy/cigar/update_icon() - cut_overlays() - if(open) - icon_state = open_state - if(contents.len >= 1) - add_overlay("[initial(icon_state)][contents.len]") - else - icon_state = closed_state - -/obj/item/weapon/storage/fancy/cigar/open(mob/user as mob) - if(open) - return - open = TRUE - update_icon() - ..() - -/obj/item/weapon/storage/fancy/cigar/close(mob/user as mob) - open = FALSE - update_icon() - ..() - -/obj/item/weapon/storage/fancy/cigar/choiba - name = "/improper Choiba cigar case" - desc = "A fancy case for holding your cigars when you are not smoking them." - description_fluff = "The exquisite wooden case bears the markings of the \ - Choiba cigar company based out of Cuba. The perfectly humidized case keeps \ - the companies signature Cigars in premium condidtion even when traveling \ - long distances within a vacuume. The custom case itself can sell for quite \ - a lot in some places." - icon_state = "cohibacase" - icon = 'icons/obj/cigarettes.dmi' - icon_type = "cigar" - starts_with = list(/obj/item/clothing/mask/smokable/cigarette/cigar/cohiba = 5) - -/obj/item/weapon/storage/fancy/cigar/havana - name = "\improper Havana cigar case" - desc = "A fancy case for holding your cigars when you are not smoking them." - icon_state = "havanacase" - icon = 'icons/obj/cigarettes.dmi' - icon_type = "cigar" - starts_with = list(/obj/item/clothing/mask/smokable/cigarette/cigar/havana = 5) - -/* - * Tobacco Bits - */ -/obj/item/weapon/storage/rollingpapers - name = "rolling paper pack" - desc = "A small cardboard pack containing several folded rolling papers." - icon_state = "paperbox" - icon = 'icons/obj/cigarettes.dmi' - w_class = ITEMSIZE_TINY - throwforce = 2 - slot_flags = SLOT_BELT - storage_slots = 14 - can_hold = list(/obj/item/weapon/reagent_containers/rollingpaper) - starts_with = list(/obj/item/weapon/reagent_containers/rollingpaper = 14) - -/obj/item/weapon/storage/rollingpapers/blunt - name = "blunt wrap pack" - desc = "A small cardboard pack containing several folded blunt wraps." - icon_state = "bluntbox" - storage_slots = 7 - can_hold = list(/obj/item/weapon/reagent_containers/rollingpaper/blunt) - starts_with = list(/obj/item/weapon/reagent_containers/rollingpaper/blunt = 7) - -/* - * Vial Box - */ -/obj/item/weapon/storage/fancy/vials - icon = 'icons/obj/vialbox.dmi' - icon_state = "vialbox6" - icon_type = "vial" - name = "vial storage box" - desc = "A helpful rack to hold test tubes." - storage_slots = 6 - can_hold = list(/obj/item/weapon/reagent_containers/glass/beaker/vial) - starts_with = list(/obj/item/weapon/reagent_containers/glass/beaker/vial = 6) - -/obj/item/weapon/storage/lockbox/vials - name = "secure vial storage box" - desc = "A locked box for keeping things away from children." - icon = 'icons/obj/vialbox.dmi' - icon_state = "vialbox0" - item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") - max_w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/reagent_containers/glass/beaker/vial) - max_storage_space = ITEMSIZE_COST_SMALL * 6 //The sum of the w_classes of all the items in this storage item. - storage_slots = 6 - req_access = list(access_virology) - -/obj/item/weapon/storage/lockbox/vials/Initialize() - . = ..() - update_icon() - -/obj/item/weapon/storage/lockbox/vials/update_icon(var/itemremoved = 0) - var/total_contents = contents.len - itemremoved - icon_state = "vialbox[total_contents]" - cut_overlays() - if (!broken) - add_overlay("led[locked]") - if(locked) - add_overlay("cover") - else - add_overlay("ledb") - -/obj/item/weapon/storage/lockbox/vials/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - update_icon() - -/* - * Box of Chocolates - */ -/obj/item/weapon/storage/fancy/heartbox - icon_state = "heartbox" - name = "box of chocolates" - icon_type = "chocolate" - - var/startswith = 6 - max_storage_space = ITEMSIZE_COST_SMALL * 6 - can_hold = list( - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/white, - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/truffle - ) - starts_with = list( - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/white, - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/white, - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/truffle - ) - -/obj/item/weapon/storage/fancy/heartbox/Initialize() - . = ..() - update_icon() - -/obj/item/weapon/storage/fancy/heartbox/update_icon(var/itemremoved = 0) - if (contents.len == 0) - icon_state = "heartbox_empty" +/* + * The 'fancy' path is for objects like donut boxes that show how many items are in the storage item on the sprite itself + * .. Sorry for the shitty path name, I couldnt think of a better one. + * + * WARNING: var/icon_type is used for both examine text and sprite name. Please look at the procs below and adjust your sprite names accordingly + * + * Contains: + * Donut Box + * Egg Box + * Candle Box + * Crayon Box + * Cracker Pack + * Cigarette Pack + * Cigar Box + * Extra Tobacco Bits + * Vial Box + * Box of Chocolates + */ + +/obj/item/weapon/storage/fancy/ + icon = 'icons/obj/food.dmi' + icon_state = "donutbox6" + name = "donut box" + var/icon_type = "donut" + drop_sound = 'sound/items/drop/cardboardbox.ogg' + pickup_sound = 'sound/items/pickup/cardboardbox.ogg' + var/open = 0 + var/open_state + var/closed_state + +/obj/item/weapon/storage/fancy/update_icon(var/itemremoved = 0) + var/total_contents = contents.len - itemremoved + icon_state = "[icon_type]box[total_contents]" + return + +/obj/item/weapon/storage/fancy/examine(mob/user) + . = ..() + + if(Adjacent(user)) + if(!contents.len) + . += "There are no [icon_type]s left in the box." + else if(contents.len == 1) + . += "There is one [icon_type] left in the box." + else + . += "There are [contents.len] [icon_type]s in the box." + +/* + * Egg Box + */ +/obj/item/weapon/storage/fancy/egg_box + icon = 'icons/obj/food.dmi' + icon_state = "eggbox" + icon_type = "egg" + name = "egg box" + center_of_mass = list("x" = 16,"y" = 7) + storage_slots = 12 + can_hold = list( + /obj/item/weapon/reagent_containers/food/snacks/egg, + /obj/item/weapon/reagent_containers/food/snacks/boiledegg + ) + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/egg = 12) + +/obj/item/weapon/storage/fancy/egg_box/New() + if(!open_state) + open_state = "[initial(icon_state)]0" + if(!closed_state) + closed_state = "[initial(icon_state)]" + ..() + +/obj/item/weapon/storage/fancy/egg_box/update_icon() + cut_overlays() + if(open) + icon_state = open_state + if(contents.len >= 1) + add_overlay("eggbox[contents.len]") + else + icon_state = closed_state + +/obj/item/weapon/storage/fancy/egg_box/open(mob/user as mob) + if(open) + return + if (isobserver(usr)) + return + open = TRUE + update_icon() + ..() + +/obj/item/weapon/storage/fancy/egg_box/close(mob/user as mob) + open = FALSE + update_icon() + ..() + +/* + * Candle Boxes + */ +/obj/item/weapon/storage/fancy/candle_box + name = "red candle pack" + desc = "A pack of red candles." + icon = 'icons/obj/candle.dmi' + icon_state = "candlebox5" + icon_type = "candle" + item_state = "candlebox5" + throwforce = 2 + slot_flags = SLOT_BELT + max_storage_space = ITEMSIZE_COST_TINY * 5 + can_hold = list(/obj/item/weapon/flame/candle) + starts_with = list(/obj/item/weapon/flame/candle = 5) + +/obj/item/weapon/storage/fancy/whitecandle_box + name = "white candle pack" + desc = "A pack of white candles." + icon = 'icons/obj/candle.dmi' + icon_state = "whitecandlebox5" + icon_type = "whitecandle" + item_state = "whitecandlebox5" + throwforce = 2 + slot_flags = SLOT_BELT + max_storage_space = ITEMSIZE_COST_TINY * 5 + can_hold = list(/obj/item/weapon/flame/candle) + starts_with = list(/obj/item/weapon/flame/candle/white = 5) + +/obj/item/weapon/storage/fancy/blackcandle_box + name = "black candle pack" + desc = "A pack of black candles." + icon = 'icons/obj/candle.dmi' + icon_state = "blackcandlebox5" + icon_type = "blackcandle" + item_state = "blackcandlebox5" + throwforce = 2 + slot_flags = SLOT_BELT + max_storage_space = ITEMSIZE_COST_TINY * 5 + can_hold = list(/obj/item/weapon/flame/candle) + starts_with = list(/obj/item/weapon/flame/candle/black = 5) + + +/* + * Crayon Box + */ +/obj/item/weapon/storage/fancy/crayons + name = "box of crayons" + desc = "A box of crayons for all your rune drawing needs." + icon = 'icons/obj/crayons.dmi' + icon_state = "crayonbox" + w_class = ITEMSIZE_SMALL + icon_type = "crayon" + can_hold = list( + /obj/item/weapon/pen/crayon + ) + starts_with = list( + /obj/item/weapon/pen/crayon/red, + /obj/item/weapon/pen/crayon/orange, + /obj/item/weapon/pen/crayon/yellow, + /obj/item/weapon/pen/crayon/green, + /obj/item/weapon/pen/crayon/blue, + /obj/item/weapon/pen/crayon/purple + ) + +/obj/item/weapon/storage/fancy/crayons/Initialize() + . = ..() + update_icon() + +/obj/item/weapon/storage/fancy/crayons/update_icon() + var/mutable_appearance/ma = new(src) + ma.cut_overlays() + for(var/obj/item/weapon/pen/crayon/crayon in contents) + add_overlay(image('icons/obj/crayons.dmi',crayon.colourName)) + appearance = ma + +/obj/item/weapon/storage/fancy/crayons/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W,/obj/item/weapon/pen/crayon)) + switch(W:colourName) + if("mime") + to_chat(user, "This crayon is too sad to be contained in this box.") + return + if("rainbow") + to_chat(user, "This crayon is too powerful to be contained in this box.") + return + ..() + +/obj/item/weapon/storage/fancy/markers + name = "box of markers" + desc = "A very professional looking box of permanent markers." + icon = 'icons/obj/crayons.dmi' + icon_state = "markerbox" + w_class = ITEMSIZE_SMALL + icon_type = "marker" + can_hold = list( + /obj/item/weapon/pen/crayon/marker + ) + starts_with = list( + /obj/item/weapon/pen/crayon/marker/black, + /obj/item/weapon/pen/crayon/marker/red, + /obj/item/weapon/pen/crayon/marker/orange, + /obj/item/weapon/pen/crayon/marker/yellow, + /obj/item/weapon/pen/crayon/marker/green, + /obj/item/weapon/pen/crayon/marker/blue, + /obj/item/weapon/pen/crayon/marker/purple + ) + +/obj/item/weapon/storage/fancy/markers/Initialize() + . = ..() + update_icon() + +/obj/item/weapon/storage/fancy/markers/update_icon() + var/mutable_appearance/ma = new(src) + ma.cut_overlays() + for(var/obj/item/weapon/pen/crayon/marker/marker in contents) + ma.add_overlay(image('icons/obj/crayons.dmi',"m"+marker.colourName)) + appearance = ma + +/obj/item/weapon/storage/fancy/markers/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W,/obj/item/weapon/pen/crayon/marker)) + switch(W:colourName) + if("mime") + to_chat(user, "This marker is too depressing to be contained in this box.") + return + if("rainbow") + to_chat(user, "This marker is too childish to be contained in this box.") + return + ..() + +/* + * Cracker Pack + */ +/obj/item/weapon/storage/fancy/crackers + name = "\improper Getmore Crackers" + icon = 'icons/obj/food.dmi' + icon_state = "crackerbox" + icon_type = "cracker" + max_storage_space = ITEMSIZE_COST_TINY * 6 + max_w_class = ITEMSIZE_TINY + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/cracker) + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/cracker = 6) + +/* + * Cigarette Pack + */ +/obj/item/weapon/storage/fancy/cigarettes + name = "\improper pack of Trans-Stellar Duty-frees" + desc = "A ubiquitous brand of cigarettes, found in every major spacefaring corporation in the universe. As mild and flavorless as it gets." + description_fluff = "The Trans-Stellar Duty-Free Cigarette Company was created as an imprint of NanoTrasen. They are the most boring, tasteless, dry cigarettes on the market, but due to just how unremarkable (not to mention cheap to produce) they are, they sold in vending machines in almost every corner of the galaxy." + icon = 'icons/obj/cigarettes.dmi' + icon_state = "cigpacket" + item_state_slots = list(slot_r_hand_str = "cigpacket", slot_l_hand_str = "cigpacket") + w_class = ITEMSIZE_TINY + throwforce = 2 + slot_flags = SLOT_BELT + storage_slots = 6 + can_hold = list(/obj/item/clothing/mask/smokable/cigarette, /obj/item/weapon/flame/lighter, /obj/item/trash/cigbutt) + icon_type = "cigarette" + starts_with = list(/obj/item/clothing/mask/smokable/cigarette = 6) + var/brand = "\improper Trans-Stellar Duty-free" + +/obj/item/weapon/storage/fancy/cigarettes/Initialize() + . = ..() + flags |= NOREACT + create_reagents(15 * storage_slots)//so people can inject cigarettes without opening a packet, now with being able to inject the whole one + flags |= OPENCONTAINER + if(brand) + for(var/obj/item/clothing/mask/smokable/cigarette/C in src) + C.brand = brand + C.desc += " This one is \a [brand]." + +/obj/item/weapon/storage/fancy/cigarettes/New() + if(!open_state) + open_state = "[initial(icon_state)]_open" + if(!closed_state) + closed_state = "[initial(icon_state)]" + ..() + +/obj/item/weapon/storage/fancy/cigarettes/update_icon() + cut_overlays() + if(open) + icon_state = open_state + if(contents.len >= 1) + add_overlay("cig[contents.len]") + else + icon_state = closed_state + +/obj/item/weapon/storage/fancy/cigarettes/open(mob/user as mob) + if(open) + return + open = TRUE + if(contents.len == 0) + icon_state = "[initial(icon_state)]_empty" + else + update_icon() + ..() + +/obj/item/weapon/storage/fancy/cigarettes/close(mob/user as mob) + open = FALSE + if(contents.len == 0) + icon_state = "[initial(icon_state)]_empty" + else + update_icon() + ..() + +/obj/item/weapon/storage/fancy/cigarettes/remove_from_storage(obj/item/W as obj, atom/new_location) + // Don't try to transfer reagents to lighters + if(istype(W, /obj/item/clothing/mask/smokable/cigarette)) + var/obj/item/clothing/mask/smokable/cigarette/C = W + reagents.trans_to_obj(C, (reagents.total_volume/contents.len)) + return ..() + +/obj/item/weapon/storage/fancy/cigarettes/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + if(!istype(M, /mob)) + return + + if(M == user && user.zone_sel.selecting == O_MOUTH) + // Find ourselves a cig. Note that we could be full of lighters. + var/obj/item/clothing/mask/smokable/cigarette/cig = locate() in src + + if(cig == null) + to_chat(user, "Looks like the packet is out of cigarettes.") + return + + // Instead of running equip_to_slot_if_possible() we check here first, + // to avoid dousing cig with reagents if we're not going to equip it + if(!cig.mob_can_equip(user, slot_wear_mask)) + return + + // We call remove_from_storage first to manage the reagent transfer and + // UI updates. + remove_from_storage(cig, null) + user.equip_to_slot(cig, slot_wear_mask) + + reagents.maximum_volume = 15 * contents.len + to_chat(user, "You take a cigarette out of the pack.") + update_icon() + else + ..() + +/obj/item/weapon/storage/fancy/cigarettes/dromedaryco + name = "\improper DromedaryCo packet" + desc = "A packet of six Earth-export DromedaryCo cancer sticks. A label on the packaging reads, \"Wouldn't a slow death make a change?\"" + description_fluff = "DromedaryCo is one of Sol's oldest cigarette brands, and takes pride in having sourced tobcacco from the same Indian plantations since 2044. Popular with those willing to pay extra for a little nostalgia." + icon_state = "Dpacket" + brand = "\improper Dromedary Co. cigarette" + +/obj/item/weapon/storage/fancy/cigarettes/killthroat + name = "\improper AcmeCo packet" + desc = "A packet of six AcmeCo cigarettes. For those who want to obtain a record for the most cancerous tumors on a budget." + description_fluff = "Available anywhere people breathe and want to breathe less, AcmeCo is the cheapest, most widespread cigarette brand in the galaxy. They taste like trash, but when you're keeping them inside your jumpsuit on a 16 hour shift, you're probably not too concerned with flavour." + icon_state = "Apacket" + brand = "\improper Acme Co. cigarette" + +/obj/item/weapon/storage/fancy/cigarettes/luckystars + name = "\improper pack of Lucky Stars" + desc = "A mellow blend made from synthetic, pod-grown tobacco. The commercial jingle is guaranteed to get stuck in your head." + description_fluff = "Lucky Stars are some of the most prolific advertisers in the business, with Gilthari Exports plastering the name and slogan on everything from workplace safety videos to racing bikes. 'Feel the gentle warmth of your Lucky Star'." + icon_state = "LSpacket" + brand = "\improper Lucky Star" + +/obj/item/weapon/storage/fancy/cigarettes/jerichos + name = "\improper pack of Jerichos" + desc = "Typically seen dangling from the lips of Fleet veterans and border world hustlers. Tastes like hickory smoke, feels like warm liquid death down your lungs." + description_fluff = "The Jericho brand has carefully cultivated its 'rugged' image ever since its completely accidental association with the SolGov-Hegemony war due to their sizable corporate presence in the region. Prior to the war, Jerichos were considered the realm of drunks and sad divorcees." + icon_state = "Jpacket" + brand = "\improper Jericho" + +/obj/item/weapon/storage/fancy/cigarettes/menthols + name = "\improper pack of Temperamento Menthols" + desc = "With a sharp and natural organic menthol flavor, these Temperamentos are a favorite of science vessel crews. Hardly anyone knows they make 'em in non-menthol!" + description_fluff = "Temperamento Menthols are a product of the Aether Atmospherics and Recycling company, and the 'smooth' menthol taste is rumoured to be the chemical by-product of some far more profitable industrial synthesis." + icon_state = "TMpacket" + brand = "\improper Temperamento Menthol" + +/obj/item/weapon/storage/fancy/cigarettes/carcinomas + name = "\improper pack of Carcinoma Angels" + desc = "This previously unknown brand was slated for the chopping block, until they were publicly endorsed by an old Earthling gonzo journalist. The rest is history. They sell a variety for cats, too." + description_fluff = "The bitter taste of a Carcinoma Angel is considered desirable by many equally bitter wash-ups who consider themselves to be 'hard-boiled'. The smell is practically inseparable from urban security offices, and old men with exonet radio shows." + brand = "\improper Carcinoma Angel" + +/obj/item/weapon/storage/fancy/cigarettes/professionals + name = "\improper pack of Professional 120s" + desc = "Let's face it - if you're smoking these, you're either trying to look upper-class or you're 80 years old. That's the only excuse. They are, however, very good quality." + description_fluff = "Grown and rolled in a meticulously maintained biosphere orbitting Love, P120 tobacco is marketed as 'probably the best in the galaxy'. The premium price point, and the fact that the vast majority of consumers couldn't really tell the difference between this and the next leading brand." + icon_state = "P100packet" + brand = "\improper Professional 120" + +/* + * Cigar Box + */ +/obj/item/weapon/storage/fancy/cigar + name = "cigar case" + desc = "A case for holding your cigars when you are not smoking them." + description_fluff = "The tasteful stained palm case tells you that these 'Palma Grande' premium \ + cigars are only sold on the luxury cruises and resorts of Oasis, though ten separate companies \ + produce them for that purpose galaxy-wide. The standard is however very high." + icon_state = "cigarcase" + icon = 'icons/obj/cigarettes.dmi' + w_class = ITEMSIZE_TINY + throwforce = 2 + slot_flags = SLOT_BELT + storage_slots = 5 + can_hold = list(/obj/item/clothing/mask/smokable/cigarette/cigar, /obj/item/trash/cigbutt/cigarbutt) + icon_type = "cigar" + starts_with = list(/obj/item/clothing/mask/smokable/cigarette/cigar = 5) + +/obj/item/weapon/storage/fancy/cigar/Initialize() + . = ..() + flags |= NOREACT + create_reagents(15 * storage_slots) + +/obj/item/weapon/storage/fancy/cigar/remove_from_storage(obj/item/W as obj, atom/new_location) + var/obj/item/clothing/mask/smokable/cigarette/cigar/C = W + if(!istype(C)) return + reagents.trans_to_obj(C, (reagents.total_volume/contents.len)) + return ..() + +/obj/item/weapon/storage/fancy/cigar/New() + if(!open_state) + open_state = "[initial(icon_state)]0" + if(!closed_state) + closed_state = "[initial(icon_state)]" + ..() + +/obj/item/weapon/storage/fancy/cigar/update_icon() + cut_overlays() + if(open) + icon_state = open_state + if(contents.len >= 1) + add_overlay("[initial(icon_state)][contents.len]") + else + icon_state = closed_state + +/obj/item/weapon/storage/fancy/cigar/open(mob/user as mob) + if(open) + return + open = TRUE + update_icon() + ..() + +/obj/item/weapon/storage/fancy/cigar/close(mob/user as mob) + open = FALSE + update_icon() + ..() + +/obj/item/weapon/storage/fancy/cigar/choiba + name = "/improper Choiba cigar case" + desc = "A fancy case for holding your cigars when you are not smoking them." + description_fluff = "The exquisite wooden case bears the markings of the \ + Choiba cigar company based out of Cuba. The perfectly humidized case keeps \ + the companies signature Cigars in premium condidtion even when traveling \ + long distances within a vacuume. The custom case itself can sell for quite \ + a lot in some places." + icon_state = "cohibacase" + icon = 'icons/obj/cigarettes.dmi' + icon_type = "cigar" + starts_with = list(/obj/item/clothing/mask/smokable/cigarette/cigar/cohiba = 5) + +/obj/item/weapon/storage/fancy/cigar/havana + name = "\improper Havana cigar case" + desc = "A fancy case for holding your cigars when you are not smoking them." + icon_state = "havanacase" + icon = 'icons/obj/cigarettes.dmi' + icon_type = "cigar" + starts_with = list(/obj/item/clothing/mask/smokable/cigarette/cigar/havana = 5) + +/* + * Tobacco Bits + */ +/obj/item/weapon/storage/rollingpapers + name = "rolling paper pack" + desc = "A small cardboard pack containing several folded rolling papers." + icon_state = "paperbox" + icon = 'icons/obj/cigarettes.dmi' + w_class = ITEMSIZE_TINY + throwforce = 2 + slot_flags = SLOT_BELT + storage_slots = 14 + can_hold = list(/obj/item/weapon/reagent_containers/rollingpaper) + starts_with = list(/obj/item/weapon/reagent_containers/rollingpaper = 14) + +/obj/item/weapon/storage/rollingpapers/blunt + name = "blunt wrap pack" + desc = "A small cardboard pack containing several folded blunt wraps." + icon_state = "bluntbox" + storage_slots = 7 + can_hold = list(/obj/item/weapon/reagent_containers/rollingpaper/blunt) + starts_with = list(/obj/item/weapon/reagent_containers/rollingpaper/blunt = 7) + +/* + * Vial Box + */ +/obj/item/weapon/storage/fancy/vials + icon = 'icons/obj/vialbox.dmi' + icon_state = "vialbox6" + icon_type = "vial" + name = "vial storage box" + desc = "A helpful rack to hold test tubes." + storage_slots = 6 + can_hold = list(/obj/item/weapon/reagent_containers/glass/beaker/vial) + starts_with = list(/obj/item/weapon/reagent_containers/glass/beaker/vial = 6) + +/obj/item/weapon/storage/lockbox/vials + name = "secure vial storage box" + desc = "A locked box for keeping things away from children." + icon = 'icons/obj/vialbox.dmi' + icon_state = "vialbox0" + item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") + max_w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/reagent_containers/glass/beaker/vial) + max_storage_space = ITEMSIZE_COST_SMALL * 6 //The sum of the w_classes of all the items in this storage item. + storage_slots = 6 + req_access = list(access_virology) + +/obj/item/weapon/storage/lockbox/vials/Initialize() + . = ..() + update_icon() + +/obj/item/weapon/storage/lockbox/vials/update_icon(var/itemremoved = 0) + var/total_contents = contents.len - itemremoved + icon_state = "vialbox[total_contents]" + cut_overlays() + if (!broken) + add_overlay("led[locked]") + if(locked) + add_overlay("cover") + else + add_overlay("ledb") + +/obj/item/weapon/storage/lockbox/vials/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + update_icon() + +/* + * Box of Chocolates + */ +/obj/item/weapon/storage/fancy/heartbox + icon_state = "heartbox" + name = "box of chocolates" + icon_type = "chocolate" + + var/startswith = 6 + max_storage_space = ITEMSIZE_COST_SMALL * 6 + can_hold = list( + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/white, + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/truffle + ) + starts_with = list( + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/white, + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/white, + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/truffle + ) + +/obj/item/weapon/storage/fancy/heartbox/Initialize() + . = ..() + update_icon() + +/obj/item/weapon/storage/fancy/heartbox/update_icon(var/itemremoved = 0) + if (contents.len == 0) + icon_state = "heartbox_empty" diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm index 2d19634aaf5..ff6cfafe7c7 100644 --- a/code/game/objects/items/weapons/storage/firstaid.dm +++ b/code/game/objects/items/weapons/storage/firstaid.dm @@ -1,305 +1,305 @@ -/* First aid storage - * Contains: - * First Aid Kits - * Pill Bottles - */ - -/* - * First Aid Kits - */ -/obj/item/weapon/storage/firstaid - name = "first aid kit" - desc = "It's an emergency medical kit for those serious boo-boos." - icon = 'icons/obj/storage.dmi' - icon_state = "firstaid" - throw_speed = 2 - throw_range = 8 - max_storage_space = ITEMSIZE_COST_SMALL * 7 // 14 - var/list/icon_variety - drop_sound = 'sound/items/drop/cardboardbox.ogg' - pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - -/obj/item/weapon/storage/firstaid/Initialize() - . = ..() - if(icon_variety) - icon_state = pick(icon_variety) - icon_variety = null - -/obj/item/weapon/storage/firstaid/fire - name = "fire first aid kit" - desc = "It's an emergency medical kit for when the toxins lab spontaneously burns down." - icon_state = "ointment" - item_state_slots = list(slot_r_hand_str = "firstaid-ointment", slot_l_hand_str = "firstaid-ointment") - //icon_variety = list("ointment","firefirstaid") //VOREStation Removal - starts_with = list( - /obj/item/device/healthanalyzer, - /obj/item/weapon/reagent_containers/hypospray/autoinjector, - /obj/item/stack/medical/ointment, - /obj/item/stack/medical/ointment, - /obj/item/weapon/reagent_containers/pill/kelotane, - /obj/item/weapon/reagent_containers/pill/kelotane, - /obj/item/weapon/reagent_containers/pill/kelotane - ) - -/obj/item/weapon/storage/firstaid/regular - icon_state = "firstaid" - starts_with = list( - /obj/item/stack/medical/bruise_pack, - /obj/item/stack/medical/bruise_pack, - /obj/item/stack/medical/bruise_pack, - /obj/item/stack/medical/ointment, - /obj/item/stack/medical/ointment, - /obj/item/device/healthanalyzer, - /obj/item/weapon/reagent_containers/hypospray/autoinjector - ) - -/obj/item/weapon/storage/firstaid/toxin - name = "poison first aid kit" //IRL the term used would be poison first aid kit. - desc = "Used to treat when one has a high amount of toxins in their body." - icon_state = "antitoxin" - item_state_slots = list(slot_r_hand_str = "firstaid-toxin", slot_l_hand_str = "firstaid-toxin") - //icon_variety = list("antitoxin","antitoxfirstaid","antitoxfirstaid2","antitoxfirstaid3") //VOREStation Removal - starts_with = list( - /obj/item/weapon/reagent_containers/syringe/antitoxin, - /obj/item/weapon/reagent_containers/syringe/antitoxin, - /obj/item/weapon/reagent_containers/syringe/antitoxin, - /obj/item/weapon/reagent_containers/pill/antitox, - /obj/item/weapon/reagent_containers/pill/antitox, - /obj/item/weapon/reagent_containers/pill/antitox, - /obj/item/device/healthanalyzer - ) - -/obj/item/weapon/storage/firstaid/o2 - name = "oxygen deprivation first aid kit" - desc = "A box full of oxygen goodies." - icon_state = "o2" - item_state_slots = list(slot_r_hand_str = "firstaid-o2", slot_l_hand_str = "firstaid-o2") - starts_with = list( - /obj/item/weapon/reagent_containers/pill/dexalin, - /obj/item/weapon/reagent_containers/pill/dexalin, - /obj/item/weapon/reagent_containers/pill/dexalin, - /obj/item/weapon/reagent_containers/pill/dexalin, - /obj/item/weapon/reagent_containers/hypospray/autoinjector, - /obj/item/weapon/reagent_containers/syringe/inaprovaline, - /obj/item/device/healthanalyzer - ) - -/obj/item/weapon/storage/firstaid/adv - name = "advanced first aid kit" - desc = "Contains advanced medical treatments, for serious boo-boos." - icon_state = "advfirstaid" - item_state_slots = list(slot_r_hand_str = "firstaid-advanced", slot_l_hand_str = "firstaid-advanced") - starts_with = list( - /obj/item/weapon/reagent_containers/hypospray/autoinjector, - /obj/item/stack/medical/advanced/bruise_pack, - /obj/item/stack/medical/advanced/bruise_pack, - /obj/item/stack/medical/advanced/bruise_pack, - /obj/item/stack/medical/advanced/ointment, - /obj/item/stack/medical/advanced/ointment, - /obj/item/stack/medical/splint - ) - -/obj/item/weapon/storage/firstaid/combat - name = "combat medical kit" - desc = "Contains advanced medical treatments." - icon_state = "bezerk" - item_state_slots = list(slot_r_hand_str = "firstaid-advanced", slot_l_hand_str = "firstaid-advanced") - starts_with = list( - /obj/item/weapon/storage/pill_bottle/bicaridine, - /obj/item/weapon/storage/pill_bottle/dermaline, - /obj/item/weapon/storage/pill_bottle/dexalin_plus, - /obj/item/weapon/storage/pill_bottle/dylovene, - /obj/item/weapon/storage/pill_bottle/tramadol, - /obj/item/weapon/storage/pill_bottle/spaceacillin, - /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting, - /obj/item/stack/medical/splint, - /obj/item/device/healthanalyzer/advanced - ) - -/obj/item/weapon/storage/firstaid/surgery - name = "surgery kit" - desc = "Contains tools for surgery. Has precise foam fitting for safe transport and automatically sterilizes the content between uses." - icon = 'icons/obj/storage.dmi' // VOREStation edit - icon_state = "surgerykit" - item_state = "firstaid-surgery" - max_w_class = ITEMSIZE_NORMAL - - can_hold = list( - /obj/item/weapon/surgical/bonesetter, - /obj/item/weapon/surgical/cautery, - /obj/item/weapon/surgical/circular_saw, - /obj/item/weapon/surgical/hemostat, - /obj/item/weapon/surgical/retractor, - /obj/item/weapon/surgical/scalpel, - /obj/item/weapon/surgical/surgicaldrill, - /obj/item/weapon/surgical/bonegel, - /obj/item/weapon/surgical/FixOVein, - /obj/item/stack/medical/advanced/bruise_pack, - /obj/item/stack/nanopaste, - /obj/item/device/healthanalyzer/advanced, - /obj/item/weapon/autopsy_scanner - ) - - starts_with = list( - /obj/item/weapon/surgical/bonesetter, - /obj/item/weapon/surgical/cautery, - /obj/item/weapon/surgical/circular_saw, - /obj/item/weapon/surgical/hemostat, - /obj/item/weapon/surgical/retractor, - /obj/item/weapon/surgical/scalpel, - /obj/item/weapon/surgical/surgicaldrill, - /obj/item/weapon/surgical/bonegel, - /obj/item/weapon/surgical/FixOVein, - /obj/item/stack/medical/advanced/bruise_pack, - /obj/item/device/healthanalyzer/advanced, - /obj/item/weapon/autopsy_scanner - ) - -/obj/item/weapon/storage/firstaid/clotting - name = "clotting kit" - desc = "Contains chemicals to stop bleeding." - max_storage_space = ITEMSIZE_COST_SMALL * 7 - starts_with = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting = 8) - -/obj/item/weapon/storage/firstaid/bonemed - name = "bone repair kit" - desc = "Contains chemicals to mend broken bones." - max_storage_space = ITEMSIZE_COST_SMALL * 7 - starts_with = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector/bonemed = 8) - -/* - * Pill Bottles - */ -/obj/item/weapon/storage/pill_bottle - name = "pill bottle" - desc = "It's an airtight container for storing medication." - icon_state = "pill_canister" - icon = 'icons/obj/chemical.dmi' - drop_sound = 'sound/items/drop/pillbottle.ogg' - pickup_sound = 'sound/items/pickup/pillbottle.ogg' - item_state_slots = list(slot_r_hand_str = "contsolid", slot_l_hand_str = "contsolid") - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/dice,/obj/item/weapon/paper) - allow_quick_gather = 1 - allow_quick_empty = 1 - use_to_pickup = TRUE - use_sound = 'sound/items/storage/pillbottle.ogg' - max_storage_space = ITEMSIZE_COST_TINY * 14 - max_w_class = ITEMSIZE_TINY - var/wrapper_color - var/label - - var/label_text = "" - var/base_name = " " - var/base_desc = " " - -/obj/item/weapon/storage/pill_bottle/Initialize() - . = ..() - base_name = name - base_desc = desc - update_icon() - -/obj/item/weapon/storage/pill_bottle/update_icon() - cut_overlays() - if(wrapper_color) - var/image/I = image(icon, "pillbottle_wrap") - I.color = wrapper_color - add_overlay(I) - -/obj/item/weapon/storage/pill_bottle/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/pen) || istype(W, /obj/item/device/flashlight/pen)) - var/tmp_label = sanitizeSafe(tgui_input_text(user, "Enter a label for [name]", "Label", label_text, MAX_NAME_LEN), MAX_NAME_LEN) - if(length(tmp_label) > 50) - to_chat(user, "The label can be at most 50 characters long.") - else if(length(tmp_label) > 10) - to_chat(user, "You set the label.") - label_text = tmp_label - update_name_label() - else - to_chat(user, "You set the label to \"[tmp_label]\".") - label_text = tmp_label - update_name_label() - else - ..() - -/obj/item/weapon/storage/pill_bottle/proc/update_name_label() - if(!label_text) - name = base_name - desc = base_desc - return - else if(length(label_text) > 10) - var/short_label_text = copytext(label_text, 1, 11) - name = "[base_name] ([short_label_text]...)" - else - name = "[base_name] ([label_text])" - desc = "[base_desc] It is labeled \"[label_text]\"." - -/obj/item/weapon/storage/pill_bottle/antitox - name = "pill bottle (Dylovene)" - desc = "Contains pills used to counter toxins." - starts_with = list(/obj/item/weapon/reagent_containers/pill/antitox = 7) - wrapper_color = COLOR_GREEN - -/obj/item/weapon/storage/pill_bottle/bicaridine - name = "pill bottle (Bicaridine)" - desc = "Contains pills used to stabilize the severely injured." - starts_with = list(/obj/item/weapon/reagent_containers/pill/bicaridine = 7) - wrapper_color = COLOR_MAROON - -/obj/item/weapon/storage/pill_bottle/dexalin_plus - name = "pill bottle (Dexalin Plus)" - desc = "Contains pills used to treat extreme cases of oxygen deprivation." - starts_with = list(/obj/item/weapon/reagent_containers/pill/dexalin_plus = 7) - wrapper_color = "#3366cc" - -/obj/item/weapon/storage/pill_bottle/dermaline - name = "pill bottle (Dermaline)" - desc = "Contains pills used to treat burn wounds." - starts_with = list(/obj/item/weapon/reagent_containers/pill/dermaline = 7) - wrapper_color = "#e8d131" - -/obj/item/weapon/storage/pill_bottle/dylovene - name = "pill bottle (Dylovene)" - desc = "Contains pills used to treat toxic substances in the blood." - starts_with = list(/obj/item/weapon/reagent_containers/pill/dylovene = 7) - wrapper_color = COLOR_GREEN - -/obj/item/weapon/storage/pill_bottle/inaprovaline - name = "pill bottle (Inaprovaline)" - desc = "Contains pills used to stabilize patients." - starts_with = list(/obj/item/weapon/reagent_containers/pill/inaprovaline = 7) - wrapper_color = COLOR_PALE_BLUE_GRAY - -/obj/item/weapon/storage/pill_bottle/kelotane - name = "pill bottle (Kelotane)" - desc = "Contains pills used to treat burns." - starts_with = list(/obj/item/weapon/reagent_containers/pill/kelotane = 7) - wrapper_color = "#ec8b2f" - -/obj/item/weapon/storage/pill_bottle/spaceacillin - name = "pill bottle (Spaceacillin)" - desc = "A theta-lactam antibiotic. Effective against many diseases likely to be encountered in space." - starts_with = list(/obj/item/weapon/reagent_containers/pill/spaceacillin = 7) - wrapper_color = COLOR_PALE_GREEN_GRAY - -/obj/item/weapon/storage/pill_bottle/tramadol - name = "pill bottle (Tramadol)" - desc = "Contains pills used to relieve pain." - starts_with = list(/obj/item/weapon/reagent_containers/pill/tramadol = 7) - wrapper_color = COLOR_PURPLE_GRAY - -/obj/item/weapon/storage/pill_bottle/citalopram - name = "pill bottle (Citalopram)" - desc = "Contains pills used to stabilize a patient's mood." - starts_with = list(/obj/item/weapon/reagent_containers/pill/citalopram = 7) - wrapper_color = COLOR_GRAY - -/obj/item/weapon/storage/pill_bottle/carbon - name = "pill bottle (Carbon)" - desc = "Contains pills used to neutralise chemicals in the stomach." - starts_with = list(/obj/item/weapon/reagent_containers/pill/carbon = 7) - -/obj/item/weapon/storage/pill_bottle/iron - name = "pill bottle (Iron)" - desc = "Contains pills used to aid in blood regeneration." - starts_with = list(/obj/item/weapon/reagent_containers/pill/iron = 7) +/* First aid storage + * Contains: + * First Aid Kits + * Pill Bottles + */ + +/* + * First Aid Kits + */ +/obj/item/weapon/storage/firstaid + name = "first aid kit" + desc = "It's an emergency medical kit for those serious boo-boos." + icon = 'icons/obj/storage.dmi' + icon_state = "firstaid" + throw_speed = 2 + throw_range = 8 + max_storage_space = ITEMSIZE_COST_SMALL * 7 // 14 + var/list/icon_variety + drop_sound = 'sound/items/drop/cardboardbox.ogg' + pickup_sound = 'sound/items/pickup/cardboardbox.ogg' + +/obj/item/weapon/storage/firstaid/Initialize() + . = ..() + if(icon_variety) + icon_state = pick(icon_variety) + icon_variety = null + +/obj/item/weapon/storage/firstaid/fire + name = "fire first aid kit" + desc = "It's an emergency medical kit for when the toxins lab spontaneously burns down." + icon_state = "ointment" + item_state_slots = list(slot_r_hand_str = "firstaid-ointment", slot_l_hand_str = "firstaid-ointment") + //icon_variety = list("ointment","firefirstaid") //VOREStation Removal + starts_with = list( + /obj/item/device/healthanalyzer, + /obj/item/weapon/reagent_containers/hypospray/autoinjector, + /obj/item/stack/medical/ointment, + /obj/item/stack/medical/ointment, + /obj/item/weapon/reagent_containers/pill/kelotane, + /obj/item/weapon/reagent_containers/pill/kelotane, + /obj/item/weapon/reagent_containers/pill/kelotane + ) + +/obj/item/weapon/storage/firstaid/regular + icon_state = "firstaid" + starts_with = list( + /obj/item/stack/medical/bruise_pack, + /obj/item/stack/medical/bruise_pack, + /obj/item/stack/medical/bruise_pack, + /obj/item/stack/medical/ointment, + /obj/item/stack/medical/ointment, + /obj/item/device/healthanalyzer, + /obj/item/weapon/reagent_containers/hypospray/autoinjector + ) + +/obj/item/weapon/storage/firstaid/toxin + name = "poison first aid kit" //IRL the term used would be poison first aid kit. + desc = "Used to treat when one has a high amount of toxins in their body." + icon_state = "antitoxin" + item_state_slots = list(slot_r_hand_str = "firstaid-toxin", slot_l_hand_str = "firstaid-toxin") + //icon_variety = list("antitoxin","antitoxfirstaid","antitoxfirstaid2","antitoxfirstaid3") //VOREStation Removal + starts_with = list( + /obj/item/weapon/reagent_containers/syringe/antitoxin, + /obj/item/weapon/reagent_containers/syringe/antitoxin, + /obj/item/weapon/reagent_containers/syringe/antitoxin, + /obj/item/weapon/reagent_containers/pill/antitox, + /obj/item/weapon/reagent_containers/pill/antitox, + /obj/item/weapon/reagent_containers/pill/antitox, + /obj/item/device/healthanalyzer + ) + +/obj/item/weapon/storage/firstaid/o2 + name = "oxygen deprivation first aid kit" + desc = "A box full of oxygen goodies." + icon_state = "o2" + item_state_slots = list(slot_r_hand_str = "firstaid-o2", slot_l_hand_str = "firstaid-o2") + starts_with = list( + /obj/item/weapon/reagent_containers/pill/dexalin, + /obj/item/weapon/reagent_containers/pill/dexalin, + /obj/item/weapon/reagent_containers/pill/dexalin, + /obj/item/weapon/reagent_containers/pill/dexalin, + /obj/item/weapon/reagent_containers/hypospray/autoinjector, + /obj/item/weapon/reagent_containers/syringe/inaprovaline, + /obj/item/device/healthanalyzer + ) + +/obj/item/weapon/storage/firstaid/adv + name = "advanced first aid kit" + desc = "Contains advanced medical treatments, for serious boo-boos." + icon_state = "advfirstaid" + item_state_slots = list(slot_r_hand_str = "firstaid-advanced", slot_l_hand_str = "firstaid-advanced") + starts_with = list( + /obj/item/weapon/reagent_containers/hypospray/autoinjector, + /obj/item/stack/medical/advanced/bruise_pack, + /obj/item/stack/medical/advanced/bruise_pack, + /obj/item/stack/medical/advanced/bruise_pack, + /obj/item/stack/medical/advanced/ointment, + /obj/item/stack/medical/advanced/ointment, + /obj/item/stack/medical/splint + ) + +/obj/item/weapon/storage/firstaid/combat + name = "combat medical kit" + desc = "Contains advanced medical treatments." + icon_state = "bezerk" + item_state_slots = list(slot_r_hand_str = "firstaid-advanced", slot_l_hand_str = "firstaid-advanced") + starts_with = list( + /obj/item/weapon/storage/pill_bottle/bicaridine, + /obj/item/weapon/storage/pill_bottle/dermaline, + /obj/item/weapon/storage/pill_bottle/dexalin_plus, + /obj/item/weapon/storage/pill_bottle/dylovene, + /obj/item/weapon/storage/pill_bottle/tramadol, + /obj/item/weapon/storage/pill_bottle/spaceacillin, + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting, + /obj/item/stack/medical/splint, + /obj/item/device/healthanalyzer/advanced + ) + +/obj/item/weapon/storage/firstaid/surgery + name = "surgery kit" + desc = "Contains tools for surgery. Has precise foam fitting for safe transport and automatically sterilizes the content between uses." + icon = 'icons/obj/storage.dmi' // VOREStation edit + icon_state = "surgerykit" + item_state = "firstaid-surgery" + max_w_class = ITEMSIZE_NORMAL + + can_hold = list( + /obj/item/weapon/surgical/bonesetter, + /obj/item/weapon/surgical/cautery, + /obj/item/weapon/surgical/circular_saw, + /obj/item/weapon/surgical/hemostat, + /obj/item/weapon/surgical/retractor, + /obj/item/weapon/surgical/scalpel, + /obj/item/weapon/surgical/surgicaldrill, + /obj/item/weapon/surgical/bonegel, + /obj/item/weapon/surgical/FixOVein, + /obj/item/stack/medical/advanced/bruise_pack, + /obj/item/stack/nanopaste, + /obj/item/device/healthanalyzer/advanced, + /obj/item/weapon/autopsy_scanner + ) + + starts_with = list( + /obj/item/weapon/surgical/bonesetter, + /obj/item/weapon/surgical/cautery, + /obj/item/weapon/surgical/circular_saw, + /obj/item/weapon/surgical/hemostat, + /obj/item/weapon/surgical/retractor, + /obj/item/weapon/surgical/scalpel, + /obj/item/weapon/surgical/surgicaldrill, + /obj/item/weapon/surgical/bonegel, + /obj/item/weapon/surgical/FixOVein, + /obj/item/stack/medical/advanced/bruise_pack, + /obj/item/device/healthanalyzer/advanced, + /obj/item/weapon/autopsy_scanner + ) + +/obj/item/weapon/storage/firstaid/clotting + name = "clotting kit" + desc = "Contains chemicals to stop bleeding." + max_storage_space = ITEMSIZE_COST_SMALL * 7 + starts_with = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting = 8) + +/obj/item/weapon/storage/firstaid/bonemed + name = "bone repair kit" + desc = "Contains chemicals to mend broken bones." + max_storage_space = ITEMSIZE_COST_SMALL * 7 + starts_with = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector/bonemed = 8) + +/* + * Pill Bottles + */ +/obj/item/weapon/storage/pill_bottle + name = "pill bottle" + desc = "It's an airtight container for storing medication." + icon_state = "pill_canister" + icon = 'icons/obj/chemical.dmi' + drop_sound = 'sound/items/drop/pillbottle.ogg' + pickup_sound = 'sound/items/pickup/pillbottle.ogg' + item_state_slots = list(slot_r_hand_str = "contsolid", slot_l_hand_str = "contsolid") + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/dice,/obj/item/weapon/paper) + allow_quick_gather = 1 + allow_quick_empty = 1 + use_to_pickup = TRUE + use_sound = 'sound/items/storage/pillbottle.ogg' + max_storage_space = ITEMSIZE_COST_TINY * 14 + max_w_class = ITEMSIZE_TINY + var/wrapper_color + var/label + + var/label_text = "" + var/base_name = " " + var/base_desc = " " + +/obj/item/weapon/storage/pill_bottle/Initialize() + . = ..() + base_name = name + base_desc = desc + update_icon() + +/obj/item/weapon/storage/pill_bottle/update_icon() + cut_overlays() + if(wrapper_color) + var/image/I = image(icon, "pillbottle_wrap") + I.color = wrapper_color + add_overlay(I) + +/obj/item/weapon/storage/pill_bottle/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/pen) || istype(W, /obj/item/device/flashlight/pen)) + var/tmp_label = sanitizeSafe(tgui_input_text(user, "Enter a label for [name]", "Label", label_text, MAX_NAME_LEN), MAX_NAME_LEN) + if(length(tmp_label) > 50) + to_chat(user, "The label can be at most 50 characters long.") + else if(length(tmp_label) > 10) + to_chat(user, "You set the label.") + label_text = tmp_label + update_name_label() + else + to_chat(user, "You set the label to \"[tmp_label]\".") + label_text = tmp_label + update_name_label() + else + ..() + +/obj/item/weapon/storage/pill_bottle/proc/update_name_label() + if(!label_text) + name = base_name + desc = base_desc + return + else if(length(label_text) > 10) + var/short_label_text = copytext(label_text, 1, 11) + name = "[base_name] ([short_label_text]...)" + else + name = "[base_name] ([label_text])" + desc = "[base_desc] It is labeled \"[label_text]\"." + +/obj/item/weapon/storage/pill_bottle/antitox + name = "pill bottle (Dylovene)" + desc = "Contains pills used to counter toxins." + starts_with = list(/obj/item/weapon/reagent_containers/pill/antitox = 7) + wrapper_color = COLOR_GREEN + +/obj/item/weapon/storage/pill_bottle/bicaridine + name = "pill bottle (Bicaridine)" + desc = "Contains pills used to stabilize the severely injured." + starts_with = list(/obj/item/weapon/reagent_containers/pill/bicaridine = 7) + wrapper_color = COLOR_MAROON + +/obj/item/weapon/storage/pill_bottle/dexalin_plus + name = "pill bottle (Dexalin Plus)" + desc = "Contains pills used to treat extreme cases of oxygen deprivation." + starts_with = list(/obj/item/weapon/reagent_containers/pill/dexalin_plus = 7) + wrapper_color = "#3366cc" + +/obj/item/weapon/storage/pill_bottle/dermaline + name = "pill bottle (Dermaline)" + desc = "Contains pills used to treat burn wounds." + starts_with = list(/obj/item/weapon/reagent_containers/pill/dermaline = 7) + wrapper_color = "#e8d131" + +/obj/item/weapon/storage/pill_bottle/dylovene + name = "pill bottle (Dylovene)" + desc = "Contains pills used to treat toxic substances in the blood." + starts_with = list(/obj/item/weapon/reagent_containers/pill/dylovene = 7) + wrapper_color = COLOR_GREEN + +/obj/item/weapon/storage/pill_bottle/inaprovaline + name = "pill bottle (Inaprovaline)" + desc = "Contains pills used to stabilize patients." + starts_with = list(/obj/item/weapon/reagent_containers/pill/inaprovaline = 7) + wrapper_color = COLOR_PALE_BLUE_GRAY + +/obj/item/weapon/storage/pill_bottle/kelotane + name = "pill bottle (Kelotane)" + desc = "Contains pills used to treat burns." + starts_with = list(/obj/item/weapon/reagent_containers/pill/kelotane = 7) + wrapper_color = "#ec8b2f" + +/obj/item/weapon/storage/pill_bottle/spaceacillin + name = "pill bottle (Spaceacillin)" + desc = "A theta-lactam antibiotic. Effective against many diseases likely to be encountered in space." + starts_with = list(/obj/item/weapon/reagent_containers/pill/spaceacillin = 7) + wrapper_color = COLOR_PALE_GREEN_GRAY + +/obj/item/weapon/storage/pill_bottle/tramadol + name = "pill bottle (Tramadol)" + desc = "Contains pills used to relieve pain." + starts_with = list(/obj/item/weapon/reagent_containers/pill/tramadol = 7) + wrapper_color = COLOR_PURPLE_GRAY + +/obj/item/weapon/storage/pill_bottle/citalopram + name = "pill bottle (Citalopram)" + desc = "Contains pills used to stabilize a patient's mood." + starts_with = list(/obj/item/weapon/reagent_containers/pill/citalopram = 7) + wrapper_color = COLOR_GRAY + +/obj/item/weapon/storage/pill_bottle/carbon + name = "pill bottle (Carbon)" + desc = "Contains pills used to neutralise chemicals in the stomach." + starts_with = list(/obj/item/weapon/reagent_containers/pill/carbon = 7) + +/obj/item/weapon/storage/pill_bottle/iron + name = "pill bottle (Iron)" + desc = "Contains pills used to aid in blood regeneration." + starts_with = list(/obj/item/weapon/reagent_containers/pill/iron = 7) diff --git a/code/game/objects/items/weapons/storage/lockbox.dm b/code/game/objects/items/weapons/storage/lockbox.dm index b3fbc758b3b..16db776bbcf 100644 --- a/code/game/objects/items/weapons/storage/lockbox.dm +++ b/code/game/objects/items/weapons/storage/lockbox.dm @@ -1,104 +1,104 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/item/weapon/storage/lockbox - name = "lockbox" - desc = "A locked box." - icon_state = "lockbox+l" - item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_NORMAL * 4 //The sum of the w_classes of all the items in this storage item. - req_access = list(access_armory) - preserve_item = 1 - var/locked = 1 - var/broken = 0 - var/icon_locked = "lockbox+l" - var/icon_closed = "lockbox" - var/icon_broken = "lockbox+b" - - -/obj/item/weapon/storage/lockbox/attackby(obj/item/weapon/W as obj, mob/user as mob) - if (istype(W, /obj/item/weapon/card/id)) - if(src.broken) - to_chat(user, "It appears to be broken.") - return - if(src.allowed(user)) - src.locked = !( src.locked ) - if(src.locked) - src.icon_state = src.icon_locked - to_chat(user, "You lock \the [src]!") - close_all() - return - else - src.icon_state = src.icon_closed - to_chat(user, "You unlock \the [src]!") - return - else - to_chat(user, "Access Denied") - else if(istype(W, /obj/item/weapon/melee/energy/blade)) - if(emag_act(INFINITY, user, W, "The locker has been sliced open by [user] with an energy blade!", "You hear metal being sliced and sparks flying.")) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, src.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - playsound(src, "sparks", 50, 1) - if(!locked) - ..() - else - to_chat(user, "It's locked!") - return - - -/obj/item/weapon/storage/lockbox/show_to(mob/user as mob) - if(locked) - to_chat(user, "It's locked!") - else - ..() - return - -/obj/item/weapon/storage/lockbox/emag_act(var/remaining_charges, var/mob/user, var/emag_source, var/visual_feedback = "", var/audible_feedback = "") - if(!broken) - if(visual_feedback) - visual_feedback = "[visual_feedback]" - else - visual_feedback = "The locker has been sliced open by [user] with an electromagnetic card!" - if(audible_feedback) - audible_feedback = "[audible_feedback]" - else - audible_feedback = "You hear a faint electrical spark." - - broken = 1 - locked = 0 - desc = "It appears to be broken." - icon_state = src.icon_broken - visible_message(visual_feedback, audible_feedback) - return 1 - -/obj/item/weapon/storage/lockbox/loyalty - name = "lockbox of loyalty implants" - req_access = list(access_security) - starts_with = list( - /obj/item/weapon/implantcase/loyalty = 3, - /obj/item/weapon/implanter/loyalty - ) - -/obj/item/weapon/storage/lockbox/clusterbang - name = "lockbox of clusterbangs" - desc = "You have a bad feeling about opening this." - req_access = list(access_security) - starts_with = list(/obj/item/weapon/grenade/flashbang/clusterbang) - -/obj/item/weapon/storage/lockbox/medal - name = "lockbox of medals" - desc = "A lockbox filled with commemorative medals, it has the NanoTrasen logo stamped on it." - req_access = list(access_heads) - storage_slots = 7 - starts_with = list( - /obj/item/clothing/accessory/medal/conduct, - /obj/item/clothing/accessory/medal/bronze_heart, - /obj/item/clothing/accessory/medal/nobel_science, - /obj/item/clothing/accessory/medal/silver/valor, - /obj/item/clothing/accessory/medal/silver/security, - /obj/item/clothing/accessory/medal/gold/captain, - /obj/item/clothing/accessory/medal/gold/heroism - ) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/obj/item/weapon/storage/lockbox + name = "lockbox" + desc = "A locked box." + icon_state = "lockbox+l" + item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_NORMAL * 4 //The sum of the w_classes of all the items in this storage item. + req_access = list(access_armory) + preserve_item = 1 + var/locked = 1 + var/broken = 0 + var/icon_locked = "lockbox+l" + var/icon_closed = "lockbox" + var/icon_broken = "lockbox+b" + + +/obj/item/weapon/storage/lockbox/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/card/id)) + if(src.broken) + to_chat(user, "It appears to be broken.") + return + if(src.allowed(user)) + src.locked = !( src.locked ) + if(src.locked) + src.icon_state = src.icon_locked + to_chat(user, "You lock \the [src]!") + close_all() + return + else + src.icon_state = src.icon_closed + to_chat(user, "You unlock \the [src]!") + return + else + to_chat(user, "Access Denied") + else if(istype(W, /obj/item/weapon/melee/energy/blade)) + if(emag_act(INFINITY, user, W, "The locker has been sliced open by [user] with an energy blade!", "You hear metal being sliced and sparks flying.")) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, src.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + playsound(src, "sparks", 50, 1) + if(!locked) + ..() + else + to_chat(user, "It's locked!") + return + + +/obj/item/weapon/storage/lockbox/show_to(mob/user as mob) + if(locked) + to_chat(user, "It's locked!") + else + ..() + return + +/obj/item/weapon/storage/lockbox/emag_act(var/remaining_charges, var/mob/user, var/emag_source, var/visual_feedback = "", var/audible_feedback = "") + if(!broken) + if(visual_feedback) + visual_feedback = "[visual_feedback]" + else + visual_feedback = "The locker has been sliced open by [user] with an electromagnetic card!" + if(audible_feedback) + audible_feedback = "[audible_feedback]" + else + audible_feedback = "You hear a faint electrical spark." + + broken = 1 + locked = 0 + desc = "It appears to be broken." + icon_state = src.icon_broken + visible_message(visual_feedback, audible_feedback) + return 1 + +/obj/item/weapon/storage/lockbox/loyalty + name = "lockbox of loyalty implants" + req_access = list(access_security) + starts_with = list( + /obj/item/weapon/implantcase/loyalty = 3, + /obj/item/weapon/implanter/loyalty + ) + +/obj/item/weapon/storage/lockbox/clusterbang + name = "lockbox of clusterbangs" + desc = "You have a bad feeling about opening this." + req_access = list(access_security) + starts_with = list(/obj/item/weapon/grenade/flashbang/clusterbang) + +/obj/item/weapon/storage/lockbox/medal + name = "lockbox of medals" + desc = "A lockbox filled with commemorative medals, it has the NanoTrasen logo stamped on it." + req_access = list(access_heads) + storage_slots = 7 + starts_with = list( + /obj/item/clothing/accessory/medal/conduct, + /obj/item/clothing/accessory/medal/bronze_heart, + /obj/item/clothing/accessory/medal/nobel_science, + /obj/item/clothing/accessory/medal/silver/valor, + /obj/item/clothing/accessory/medal/silver/security, + /obj/item/clothing/accessory/medal/gold/captain, + /obj/item/clothing/accessory/medal/gold/heroism + ) diff --git a/code/game/objects/items/weapons/storage/misc.dm b/code/game/objects/items/weapons/storage/misc.dm index 8b6384b9cba..70f70e8bcc9 100644 --- a/code/game/objects/items/weapons/storage/misc.dm +++ b/code/game/objects/items/weapons/storage/misc.dm @@ -1,103 +1,103 @@ -/* - * Donut Box - */ - -var/list/random_weighted_donuts = list( - /obj/item/weapon/reagent_containers/food/snacks/donut/plain = 5, - /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly = 5, - /obj/item/weapon/reagent_containers/food/snacks/donut/pink = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/pink/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/purple = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/purple/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/green = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/green/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/beige = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/beige/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/choc = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/choc/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/blue = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/blue/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/yellow = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/yellow/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/olive = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/olive/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/homer = 3, - /obj/item/weapon/reagent_containers/food/snacks/donut/homer/jelly = 3, - /obj/item/weapon/reagent_containers/food/snacks/donut/choc_sprinkles = 3, - /obj/item/weapon/reagent_containers/food/snacks/donut/choc_sprinkles/jelly = 3, - /obj/item/weapon/reagent_containers/food/snacks/donut/chaos = 1 -) - -/obj/item/weapon/storage/box/donut - icon = 'icons/obj/food_donuts.dmi' - icon_state = "donutbox" - name = "donut box" - desc = "A box that holds tasty donuts, if you're lucky." - center_of_mass = list("x" = 16,"y" = 9) - max_storage_space = ITEMSIZE_COST_SMALL * 6 - can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/donut) - foldable = /obj/item/stack/material/cardboard - //starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donut/normal = 6) - -/obj/item/weapon/storage/box/donut/Initialize() - if(!empty) - for(var/i in 1 to 6) - var/type_to_spawn = pickweight(random_weighted_donuts) - new type_to_spawn(src) - . = ..() - update_icon() - -/obj/item/weapon/storage/box/donut/update_icon() - cut_overlays() - var/x_offset = 0 - for(var/obj/item/weapon/reagent_containers/food/snacks/donut/D in contents) - var/mutable_appearance/ma = mutable_appearance(icon = icon, icon_state = D.overlay_state) - ma.pixel_x = x_offset - add_overlay(ma) - x_offset += 3 - -/obj/item/weapon/storage/box/donut/empty - empty = TRUE - -/obj/item/weapon/storage/box/wormcan - icon = 'icons/obj/food.dmi' - icon_state = "wormcan" - name = "can of worms" - desc = "You probably do want to open this can of worms." - max_storage_space = ITEMSIZE_COST_TINY * 6 - can_hold = list( - /obj/item/weapon/reagent_containers/food/snacks/wormsickly, - /obj/item/weapon/reagent_containers/food/snacks/worm, - /obj/item/weapon/reagent_containers/food/snacks/wormdeluxe - ) - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/worm = 6) - -/obj/item/weapon/storage/box/wormcan/Initialize() - . = ..() - update_icon() - -/obj/item/weapon/storage/box/wormcan/update_icon(var/itemremoved = 0) - if (contents.len == 0) - icon_state = "wormcan_empty" - -/obj/item/weapon/storage/box/wormcan/sickly - icon_state = "wormcan_sickly" - name = "can of sickly worms" - desc = "You probably don't want to open this can of worms." - max_storage_space = ITEMSIZE_COST_TINY * 6 - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/wormsickly = 6) - -/obj/item/weapon/storage/box/wormcan/sickly/update_icon(var/itemremoved = 0) - if (contents.len == 0) - icon_state = "wormcan_empty_sickly" - -/obj/item/weapon/storage/box/wormcan/deluxe - icon_state = "wormcan_deluxe" - name = "can of deluxe worms" - desc = "You absolutely want to open this can of worms." - max_storage_space = ITEMSIZE_COST_TINY * 6 - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/wormdeluxe = 6) - -/obj/item/weapon/storage/box/wormcan/deluxe/update_icon(var/itemremoved = 0) - if (contents.len == 0) +/* + * Donut Box + */ + +var/list/random_weighted_donuts = list( + /obj/item/weapon/reagent_containers/food/snacks/donut/plain = 5, + /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly = 5, + /obj/item/weapon/reagent_containers/food/snacks/donut/pink = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/pink/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/purple = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/purple/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/green = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/green/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/beige = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/beige/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/choc = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/choc/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/blue = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/blue/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/yellow = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/yellow/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/olive = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/olive/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/homer = 3, + /obj/item/weapon/reagent_containers/food/snacks/donut/homer/jelly = 3, + /obj/item/weapon/reagent_containers/food/snacks/donut/choc_sprinkles = 3, + /obj/item/weapon/reagent_containers/food/snacks/donut/choc_sprinkles/jelly = 3, + /obj/item/weapon/reagent_containers/food/snacks/donut/chaos = 1 +) + +/obj/item/weapon/storage/box/donut + icon = 'icons/obj/food_donuts.dmi' + icon_state = "donutbox" + name = "donut box" + desc = "A box that holds tasty donuts, if you're lucky." + center_of_mass = list("x" = 16,"y" = 9) + max_storage_space = ITEMSIZE_COST_SMALL * 6 + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/donut) + foldable = /obj/item/stack/material/cardboard + //starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donut/normal = 6) + +/obj/item/weapon/storage/box/donut/Initialize() + if(!empty) + for(var/i in 1 to 6) + var/type_to_spawn = pickweight(random_weighted_donuts) + new type_to_spawn(src) + . = ..() + update_icon() + +/obj/item/weapon/storage/box/donut/update_icon() + cut_overlays() + var/x_offset = 0 + for(var/obj/item/weapon/reagent_containers/food/snacks/donut/D in contents) + var/mutable_appearance/ma = mutable_appearance(icon = icon, icon_state = D.overlay_state) + ma.pixel_x = x_offset + add_overlay(ma) + x_offset += 3 + +/obj/item/weapon/storage/box/donut/empty + empty = TRUE + +/obj/item/weapon/storage/box/wormcan + icon = 'icons/obj/food.dmi' + icon_state = "wormcan" + name = "can of worms" + desc = "You probably do want to open this can of worms." + max_storage_space = ITEMSIZE_COST_TINY * 6 + can_hold = list( + /obj/item/weapon/reagent_containers/food/snacks/wormsickly, + /obj/item/weapon/reagent_containers/food/snacks/worm, + /obj/item/weapon/reagent_containers/food/snacks/wormdeluxe + ) + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/worm = 6) + +/obj/item/weapon/storage/box/wormcan/Initialize() + . = ..() + update_icon() + +/obj/item/weapon/storage/box/wormcan/update_icon(var/itemremoved = 0) + if (contents.len == 0) + icon_state = "wormcan_empty" + +/obj/item/weapon/storage/box/wormcan/sickly + icon_state = "wormcan_sickly" + name = "can of sickly worms" + desc = "You probably don't want to open this can of worms." + max_storage_space = ITEMSIZE_COST_TINY * 6 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/wormsickly = 6) + +/obj/item/weapon/storage/box/wormcan/sickly/update_icon(var/itemremoved = 0) + if (contents.len == 0) + icon_state = "wormcan_empty_sickly" + +/obj/item/weapon/storage/box/wormcan/deluxe + icon_state = "wormcan_deluxe" + name = "can of deluxe worms" + desc = "You absolutely want to open this can of worms." + max_storage_space = ITEMSIZE_COST_TINY * 6 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/wormdeluxe = 6) + +/obj/item/weapon/storage/box/wormcan/deluxe/update_icon(var/itemremoved = 0) + if (contents.len == 0) icon_state = "wormcan_empty_deluxe" \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/secure.dm b/code/game/objects/items/weapons/storage/secure.dm index ce0e105f01f..d7d9a8f6d42 100644 --- a/code/game/objects/items/weapons/storage/secure.dm +++ b/code/game/objects/items/weapons/storage/secure.dm @@ -1,209 +1,209 @@ -/* - * Absorbs /obj/item/weapon/secstorage. - * Reimplements it only slightly to use existing storage functionality. - * - * Contains: - * Secure Briefcase - * Wall Safe - */ - -// ----------------------------- -// Generic Item -// ----------------------------- -/obj/item/weapon/storage/secure - name = "secstorage" - var/icon_locking = "secureb" - var/icon_sparking = "securespark" - var/icon_opened = "secure0" - var/locked = 1 - var/code = "" - var/l_code = null - var/l_set = 0 - var/l_setshort = 0 - var/l_hacking = 0 - var/emagged = 0 - var/open = 0 - w_class = ITEMSIZE_NORMAL - max_w_class = ITEMSIZE_SMALL - max_storage_space = ITEMSIZE_SMALL * 7 - use_sound = 'sound/items/storage/briefcase.ogg' - -/obj/item/weapon/storage/secure/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "The service panel is [src.open ? "open" : "closed"]." - -/obj/item/weapon/storage/secure/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(locked) - if (istype(W, /obj/item/weapon/melee/energy/blade) && emag_act(INFINITY, user, "You slice through the lock of \the [src]")) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, src.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - playsound(src, "sparks", 50, 1) - return - - if (W.has_tool_quality(TOOL_SCREWDRIVER)) - if (do_after(user, 20 * W.toolspeed)) - src.open =! src.open - playsound(src, W.usesound, 50, 1) - user.show_message(text("You [] the service panel.", (src.open ? "open" : "close"))) - return - if (istype(W, /obj/item/device/multitool) && (src.open == 1)&& (!src.l_hacking)) - user.show_message("Now attempting to reset internal memory, please hold.", 1) - src.l_hacking = 1 - if (do_after(usr, 100)) - if (prob(40)) - src.l_setshort = 1 - src.l_set = 0 - src.code = "" - user.show_message("Internal memory reset. Please give it a few seconds to reinitialize.", 1) - sleep(80) - src.l_setshort = 0 - src.l_hacking = 0 - else - user.show_message("Unable to reset internal memory.", 1) - src.l_hacking = 0 - else src.l_hacking = 0 - return - //At this point you have exhausted all the special things to do when locked - // ... but it's still locked. - return - - // -> storage/attackby() what with handle insertion, etc - ..() - - -/obj/item/weapon/storage/secure/MouseDrop(over_object, src_location, over_location) - if (locked) - src.add_fingerprint(usr) - return - ..() - -/obj/item/weapon/storage/secure/AltClick(mob/user as mob) - if (isliving(user) && Adjacent(user) && (src.locked == 1)) - to_chat(user, "[src] is locked and cannot be opened!") - else if (isliving(user) && Adjacent(user) && (!src.locked)) - src.open(usr) - else - for(var/mob/M in range(1)) - if (M.s_active == src) - src.close(M) - src.add_fingerprint(user) - return - -/obj/item/weapon/storage/secure/attack_self(mob/user as mob) - tgui_interact(user) - -/obj/item/weapon/storage/secure/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "SecureSafe", name) - ui.open() - -/obj/item/weapon/storage/secure/tgui_data(mob/user) - var/list/data = list() - data["locked"] = locked - data["code"] = code - data["emagged"] = emagged - data["l_setshort"] = l_setshort - data["l_set"] = l_set - return data - -/obj/item/weapon/storage/secure/tgui_act(action, params) - if(..()) - return TRUE - switch (action) - if("type") - var/digit = params["digit"] - if(digit == "E") - if ((src.l_set == 0) && (length(src.code) == 5) && (!src.l_setshort) && (src.code != "ERROR")) - src.l_code = src.code - src.l_set = 1 - else if ((src.code == src.l_code) && (src.emagged == 0) && (src.l_set == 1)) - src.locked = 0 - cut_overlays() - add_overlay(icon_opened) - src.code = null - else - src.code = "ERROR" - else - if ((digit == "R") && (src.emagged == 0) && (!src.l_setshort)) - src.locked = 1 - cut_overlays() - src.code = null - src.close(usr) - else - src.code += text("[]", digit) - if (length(src.code) > 5) - src.code = "ERROR" - src.add_fingerprint(usr) - . = TRUE - return - -/obj/item/weapon/storage/secure/emag_act(var/remaining_charges, var/mob/user, var/feedback) - if(!emagged) - emagged = 1 - src.add_overlay(icon_sparking) - sleep(6) - cut_overlays() - add_overlay(icon_locking) - locked = 0 - to_chat(user, (feedback ? feedback : "You short out the lock of \the [src].")) - return 1 - -// ----------------------------- -// Secure Briefcase -// ----------------------------- -/obj/item/weapon/storage/secure/briefcase - name = "secure briefcase" - icon = 'icons/obj/storage.dmi' - icon_state = "secure" - item_state_slots = list(slot_r_hand_str = "case", slot_l_hand_str = "case") - desc = "A large briefcase with a digital locking system." - force = 8.0 - throw_speed = 1 - throw_range = 4 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_LARGE - max_storage_space = ITEMSIZE_COST_NORMAL * 4 - -/obj/item/weapon/storage/secure/briefcase/attack_hand(mob/user as mob) - if ((src.loc == user) && (src.locked == 1)) - to_chat(user, "[src] is locked and cannot be opened!") - else if ((src.loc == user) && (!src.locked)) - src.open(usr) - else - ..() - for(var/mob/M in range(1)) - if (M.s_active == src) - src.close(M) - src.add_fingerprint(user) - return - -// ----------------------------- -// Secure Safe -// ----------------------------- - -/obj/item/weapon/storage/secure/safe - name = "secure safe" - desc = "It doesn't seem all that secure. Oh well, it'll do." - icon = 'icons/obj/storage.dmi' - icon_state = "safe" - layer = ABOVE_WINDOW_LAYER - icon_opened = "safe0" - icon_locking = "safeb" - icon_sparking = "safespark" - force = 8.0 - w_class = ITEMSIZE_NO_CONTAINER - max_w_class = ITEMSIZE_LARGE // This was 8 previously... - anchored = TRUE - density = FALSE - cant_hold = list(/obj/item/weapon/storage/secure/briefcase) - starts_with = list( - /obj/item/weapon/paper, - /obj/item/weapon/pen - ) - -/obj/item/weapon/storage/secure/safe/attack_hand(mob/user as mob) - tgui_interact(user) +/* + * Absorbs /obj/item/weapon/secstorage. + * Reimplements it only slightly to use existing storage functionality. + * + * Contains: + * Secure Briefcase + * Wall Safe + */ + +// ----------------------------- +// Generic Item +// ----------------------------- +/obj/item/weapon/storage/secure + name = "secstorage" + var/icon_locking = "secureb" + var/icon_sparking = "securespark" + var/icon_opened = "secure0" + var/locked = 1 + var/code = "" + var/l_code = null + var/l_set = 0 + var/l_setshort = 0 + var/l_hacking = 0 + var/emagged = 0 + var/open = 0 + w_class = ITEMSIZE_NORMAL + max_w_class = ITEMSIZE_SMALL + max_storage_space = ITEMSIZE_SMALL * 7 + use_sound = 'sound/items/storage/briefcase.ogg' + +/obj/item/weapon/storage/secure/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "The service panel is [src.open ? "open" : "closed"]." + +/obj/item/weapon/storage/secure/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(locked) + if (istype(W, /obj/item/weapon/melee/energy/blade) && emag_act(INFINITY, user, "You slice through the lock of \the [src]")) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, src.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + playsound(src, "sparks", 50, 1) + return + + if (W.has_tool_quality(TOOL_SCREWDRIVER)) + if (do_after(user, 20 * W.toolspeed)) + src.open =! src.open + playsound(src, W.usesound, 50, 1) + user.show_message(text("You [] the service panel.", (src.open ? "open" : "close"))) + return + if (istype(W, /obj/item/device/multitool) && (src.open == 1)&& (!src.l_hacking)) + user.show_message("Now attempting to reset internal memory, please hold.", 1) + src.l_hacking = 1 + if (do_after(usr, 100)) + if (prob(40)) + src.l_setshort = 1 + src.l_set = 0 + src.code = "" + user.show_message("Internal memory reset. Please give it a few seconds to reinitialize.", 1) + sleep(80) + src.l_setshort = 0 + src.l_hacking = 0 + else + user.show_message("Unable to reset internal memory.", 1) + src.l_hacking = 0 + else src.l_hacking = 0 + return + //At this point you have exhausted all the special things to do when locked + // ... but it's still locked. + return + + // -> storage/attackby() what with handle insertion, etc + ..() + + +/obj/item/weapon/storage/secure/MouseDrop(over_object, src_location, over_location) + if (locked) + src.add_fingerprint(usr) + return + ..() + +/obj/item/weapon/storage/secure/AltClick(mob/user as mob) + if (isliving(user) && Adjacent(user) && (src.locked == 1)) + to_chat(user, "[src] is locked and cannot be opened!") + else if (isliving(user) && Adjacent(user) && (!src.locked)) + src.open(usr) + else + for(var/mob/M in range(1)) + if (M.s_active == src) + src.close(M) + src.add_fingerprint(user) + return + +/obj/item/weapon/storage/secure/attack_self(mob/user as mob) + tgui_interact(user) + +/obj/item/weapon/storage/secure/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "SecureSafe", name) + ui.open() + +/obj/item/weapon/storage/secure/tgui_data(mob/user) + var/list/data = list() + data["locked"] = locked + data["code"] = code + data["emagged"] = emagged + data["l_setshort"] = l_setshort + data["l_set"] = l_set + return data + +/obj/item/weapon/storage/secure/tgui_act(action, params) + if(..()) + return TRUE + switch (action) + if("type") + var/digit = params["digit"] + if(digit == "E") + if ((src.l_set == 0) && (length(src.code) == 5) && (!src.l_setshort) && (src.code != "ERROR")) + src.l_code = src.code + src.l_set = 1 + else if ((src.code == src.l_code) && (src.emagged == 0) && (src.l_set == 1)) + src.locked = 0 + cut_overlays() + add_overlay(icon_opened) + src.code = null + else + src.code = "ERROR" + else + if ((digit == "R") && (src.emagged == 0) && (!src.l_setshort)) + src.locked = 1 + cut_overlays() + src.code = null + src.close(usr) + else + src.code += text("[]", digit) + if (length(src.code) > 5) + src.code = "ERROR" + src.add_fingerprint(usr) + . = TRUE + return + +/obj/item/weapon/storage/secure/emag_act(var/remaining_charges, var/mob/user, var/feedback) + if(!emagged) + emagged = 1 + src.add_overlay(icon_sparking) + sleep(6) + cut_overlays() + add_overlay(icon_locking) + locked = 0 + to_chat(user, (feedback ? feedback : "You short out the lock of \the [src].")) + return 1 + +// ----------------------------- +// Secure Briefcase +// ----------------------------- +/obj/item/weapon/storage/secure/briefcase + name = "secure briefcase" + icon = 'icons/obj/storage.dmi' + icon_state = "secure" + item_state_slots = list(slot_r_hand_str = "case", slot_l_hand_str = "case") + desc = "A large briefcase with a digital locking system." + force = 8.0 + throw_speed = 1 + throw_range = 4 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_LARGE + max_storage_space = ITEMSIZE_COST_NORMAL * 4 + +/obj/item/weapon/storage/secure/briefcase/attack_hand(mob/user as mob) + if ((src.loc == user) && (src.locked == 1)) + to_chat(user, "[src] is locked and cannot be opened!") + else if ((src.loc == user) && (!src.locked)) + src.open(usr) + else + ..() + for(var/mob/M in range(1)) + if (M.s_active == src) + src.close(M) + src.add_fingerprint(user) + return + +// ----------------------------- +// Secure Safe +// ----------------------------- + +/obj/item/weapon/storage/secure/safe + name = "secure safe" + desc = "It doesn't seem all that secure. Oh well, it'll do." + icon = 'icons/obj/storage.dmi' + icon_state = "safe" + layer = ABOVE_WINDOW_LAYER + icon_opened = "safe0" + icon_locking = "safeb" + icon_sparking = "safespark" + force = 8.0 + w_class = ITEMSIZE_NO_CONTAINER + max_w_class = ITEMSIZE_LARGE // This was 8 previously... + anchored = TRUE + density = FALSE + cant_hold = list(/obj/item/weapon/storage/secure/briefcase) + starts_with = list( + /obj/item/weapon/paper, + /obj/item/weapon/pen + ) + +/obj/item/weapon/storage/secure/safe/attack_hand(mob/user as mob) + tgui_interact(user) diff --git a/code/game/objects/items/weapons/storage/toolbox.dm b/code/game/objects/items/weapons/storage/toolbox.dm index fdd28c1a0eb..0637205c3b2 100644 --- a/code/game/objects/items/weapons/storage/toolbox.dm +++ b/code/game/objects/items/weapons/storage/toolbox.dm @@ -1,239 +1,239 @@ -/* - * Toolboxes - */ -/obj/item/weapon/storage/toolbox - name = "toolbox" - desc = "A metal toolbox with remarkably robust construction." - icon = 'icons/obj/storage_vr.dmi' - icon_state = "red" - item_state_slots = list(slot_r_hand_str = "toolbox_red", slot_l_hand_str = "toolbox_red") - center_of_mass = list("x" = 16,"y" = 11) - force = 10 - throwforce = 10 - throw_speed = 1 - throw_range = 7 - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_SMALL * 7 //enough to hold all starting contents - origin_tech = list(TECH_COMBAT = 1) - attack_verb = list("robusted") - use_sound = 'sound/items/storage/toolbox.ogg' - drop_sound = 'sound/items/drop/toolbox.ogg' - pickup_sound = 'sound/items/pickup/toolbox.ogg' - -//Emergency -/obj/item/weapon/storage/toolbox/emergency - name = "emergency toolbox" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "red" - item_state_slots = list(slot_r_hand_str = "toolbox_red", slot_l_hand_str = "toolbox_red") - starts_with = list( - /obj/item/weapon/tool/crowbar/red, - /obj/item/weapon/extinguisher/mini, - /obj/item/device/radio - ) -/obj/item/weapon/storage/toolbox/emergency/Initialize() - if(prob(50)) - new /obj/item/device/flashlight(src) - else - new /obj/item/device/flashlight/flare(src) - . = ..() - -//Mechanical -/obj/item/weapon/storage/toolbox/mechanical - name = "mechanical toolbox" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "blue" - item_state_slots = list(slot_r_hand_str = "toolbox_blue", slot_l_hand_str = "toolbox_blue") - starts_with = list( - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/tool/wrench, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/crowbar, - /obj/item/device/analyzer, - /obj/item/weapon/tool/wirecutters - ) - -//Electrical -/obj/item/weapon/storage/toolbox/electrical - name = "electrical toolbox" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "yellow" - item_state_slots = list(slot_r_hand_str = "toolbox_yellow", slot_l_hand_str = "toolbox_yellow") - starts_with = list( - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/tool/wirecutters, - /obj/item/device/t_scanner, - /obj/item/weapon/tool/crowbar, - /obj/item/stack/cable_coil/random_belt, - /obj/item/stack/cable_coil/random_belt - ) -/obj/item/weapon/storage/toolbox/electrical/Initialize() - . = ..() - if(prob(5)) - new /obj/item/clothing/gloves/yellow(src) - else - new /obj/item/stack/cable_coil/random(src,30) - calibrate_size() - -//Syndicate -/obj/item/weapon/storage/toolbox/syndicate - name = "black and red toolbox" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "syndicate" - item_state_slots = list(slot_r_hand_str = "toolbox_syndi", slot_l_hand_str = "toolbox_syndi") - origin_tech = list(TECH_COMBAT = 1, TECH_ILLEGAL = 1) - force = 14 - starts_with = list( - /obj/item/clothing/gloves/yellow, - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/tool/wrench, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/tool/wirecutters, - /obj/item/device/multitool - ) - -/obj/item/weapon/storage/toolbox/syndicate/powertools - starts_with = list( - /obj/item/clothing/gloves/yellow, - /obj/item/weapon/tool/transforming/powerdrill, - /obj/item/weapon/weldingtool/experimental, - /obj/item/weapon/tool/transforming/jawsoflife, - /obj/item/device/multitool, - /obj/item/stack/cable_coil/random_belt, - /obj/item/device/analyzer - ) - -//Brass -/obj/item/weapon/storage/toolbox/brass - name = "brass toolbox" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "brass" - item_state_slots = list(slot_r_hand_str = "toolbox_yellow", slot_l_hand_str = "toolbox_yellow") - starts_with = list( - /obj/item/weapon/tool/crowbar/brass, - /obj/item/weapon/tool/wirecutters/brass, - /obj/item/weapon/tool/screwdriver/brass, - /obj/item/weapon/tool/wrench/brass, - /obj/item/weapon/weldingtool/brass - ) - -//Hydro -/obj/item/weapon/storage/toolbox/hydro - name = "hydroponic toolbox" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "green" - item_state_slots = list(slot_r_hand_str = "toolbox_green", slot_l_hand_str = "toolbox_green") - starts_with = list( - /obj/item/device/analyzer/plant_analyzer, - /obj/item/weapon/material/minihoe, - /obj/item/weapon/material/knife/machete/hatchet, - /obj/item/weapon/tool/wirecutters/clippers/trimmers, - /obj/item/weapon/reagent_containers/spray/plantbgone, - /obj/item/weapon/reagent_containers/glass/beaker - ) - -/* - * Lunchboxes - */ - -/obj/item/weapon/storage/toolbox/lunchbox - max_storage_space = ITEMSIZE_COST_SMALL * 4 //slightly smaller than a toolbox - name = "rainbow lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_rainbow" - item_state_slots = list(slot_r_hand_str = "toolbox_pink", slot_l_hand_str = "toolbox_pink") - desc = "A little lunchbox. This one is the colors of the rainbow!" - w_class = ITEMSIZE_NORMAL - max_w_class = ITEMSIZE_SMALL - var/filled = FALSE - attack_verb = list("lunched") - -/obj/item/weapon/storage/toolbox/lunchbox/Initialize() - if(filled) - var/list/lunches = lunchables_lunches() - var/lunch = lunches[pick(lunches)] - new lunch(src) - - var/list/snacks = lunchables_snacks() - var/snack = snacks[pick(snacks)] - new snack(src) - - var/list/drinks = lunchables_drinks() - var/drink = drinks[pick(drinks)] - new drink(src) - . = ..() - -/obj/item/weapon/storage/toolbox/lunchbox/filled - filled = TRUE - -/obj/item/weapon/storage/toolbox/lunchbox/heart - name = "heart lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_lovelyhearts" - item_state_slots = list(slot_r_hand_str = "toolbox_pink", slot_l_hand_str = "toolbox_pink") - desc = "A little lunchbox. This one has cute little hearts on it!" - -/obj/item/weapon/storage/toolbox/lunchbox/heart/filled - filled = TRUE - -/obj/item/weapon/storage/toolbox/lunchbox/cat - name = "cat lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_sciencecatshow" - item_state_slots = list(slot_r_hand_str = "toolbox_green", slot_l_hand_str = "toolbox_green") - desc = "A little lunchbox. This one has a cute little science cat from a popular show on it!" - -/obj/item/weapon/storage/toolbox/lunchbox/cat/filled - filled = TRUE - -/obj/item/weapon/storage/toolbox/lunchbox/nt - name = "NanoTrasen brand lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_nanotrasen" - item_state_slots = list(slot_r_hand_str = "toolbox_blue", slot_l_hand_str = "toolbox_blue") - desc = "A little lunchbox. This one is branded with the NanoTrasen logo!" - -/obj/item/weapon/storage/toolbox/lunchbox/nt/filled - filled = TRUE - -/obj/item/weapon/storage/toolbox/lunchbox/mars - name = "\improper Mojave university lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_marsuniversity" - item_state_slots = list(slot_r_hand_str = "toolbox_red", slot_l_hand_str = "toolbox_red") - desc = "A little lunchbox. This one is branded with the Mojave university logo!" - -/obj/item/weapon/storage/toolbox/lunchbox/mars/filled - filled = TRUE - -/obj/item/weapon/storage/toolbox/lunchbox/cti - name = "\improper CTI lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_cti" - item_state_slots = list(slot_r_hand_str = "toolbox_blue", slot_l_hand_str = "toolbox_blue") - desc = "A little lunchbox. This one is branded with the CTI logo!" - -/obj/item/weapon/storage/toolbox/lunchbox/cti/filled - filled = TRUE - -/obj/item/weapon/storage/toolbox/lunchbox/nymph - name = "\improper Diona nymph lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_dionanymph" - item_state_slots = list(slot_r_hand_str = "toolbox_yellow", slot_l_hand_str = "toolbox_yellow") - desc = "A little lunchbox. This one is an adorable Diona nymph on the side!" - -/obj/item/weapon/storage/toolbox/lunchbox/nymph/filled - filled = TRUE - -/obj/item/weapon/storage/toolbox/lunchbox/syndicate - name = "black and red lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_syndie" - item_state_slots = list(slot_r_hand_str = "toolbox_syndi", slot_l_hand_str = "toolbox_syndi") - desc = "A little lunchbox. This one is a sleek black and red, made of a durable steel!" - -/obj/item/weapon/storage/toolbox/lunchbox/syndicate/filled - filled = TRUE +/* + * Toolboxes + */ +/obj/item/weapon/storage/toolbox + name = "toolbox" + desc = "A metal toolbox with remarkably robust construction." + icon = 'icons/obj/storage_vr.dmi' + icon_state = "red" + item_state_slots = list(slot_r_hand_str = "toolbox_red", slot_l_hand_str = "toolbox_red") + center_of_mass = list("x" = 16,"y" = 11) + force = 10 + throwforce = 10 + throw_speed = 1 + throw_range = 7 + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_SMALL * 7 //enough to hold all starting contents + origin_tech = list(TECH_COMBAT = 1) + attack_verb = list("robusted") + use_sound = 'sound/items/storage/toolbox.ogg' + drop_sound = 'sound/items/drop/toolbox.ogg' + pickup_sound = 'sound/items/pickup/toolbox.ogg' + +//Emergency +/obj/item/weapon/storage/toolbox/emergency + name = "emergency toolbox" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "red" + item_state_slots = list(slot_r_hand_str = "toolbox_red", slot_l_hand_str = "toolbox_red") + starts_with = list( + /obj/item/weapon/tool/crowbar/red, + /obj/item/weapon/extinguisher/mini, + /obj/item/device/radio + ) +/obj/item/weapon/storage/toolbox/emergency/Initialize() + if(prob(50)) + new /obj/item/device/flashlight(src) + else + new /obj/item/device/flashlight/flare(src) + . = ..() + +//Mechanical +/obj/item/weapon/storage/toolbox/mechanical + name = "mechanical toolbox" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "blue" + item_state_slots = list(slot_r_hand_str = "toolbox_blue", slot_l_hand_str = "toolbox_blue") + starts_with = list( + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/crowbar, + /obj/item/device/analyzer, + /obj/item/weapon/tool/wirecutters + ) + +//Electrical +/obj/item/weapon/storage/toolbox/electrical + name = "electrical toolbox" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "yellow" + item_state_slots = list(slot_r_hand_str = "toolbox_yellow", slot_l_hand_str = "toolbox_yellow") + starts_with = list( + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/tool/wirecutters, + /obj/item/device/t_scanner, + /obj/item/weapon/tool/crowbar, + /obj/item/stack/cable_coil/random_belt, + /obj/item/stack/cable_coil/random_belt + ) +/obj/item/weapon/storage/toolbox/electrical/Initialize() + . = ..() + if(prob(5)) + new /obj/item/clothing/gloves/yellow(src) + else + new /obj/item/stack/cable_coil/random(src,30) + calibrate_size() + +//Syndicate +/obj/item/weapon/storage/toolbox/syndicate + name = "black and red toolbox" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "syndicate" + item_state_slots = list(slot_r_hand_str = "toolbox_syndi", slot_l_hand_str = "toolbox_syndi") + origin_tech = list(TECH_COMBAT = 1, TECH_ILLEGAL = 1) + force = 14 + starts_with = list( + /obj/item/clothing/gloves/yellow, + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/tool/wirecutters, + /obj/item/device/multitool + ) + +/obj/item/weapon/storage/toolbox/syndicate/powertools + starts_with = list( + /obj/item/clothing/gloves/yellow, + /obj/item/weapon/tool/transforming/powerdrill, + /obj/item/weapon/weldingtool/experimental, + /obj/item/weapon/tool/transforming/jawsoflife, + /obj/item/device/multitool, + /obj/item/stack/cable_coil/random_belt, + /obj/item/device/analyzer + ) + +//Brass +/obj/item/weapon/storage/toolbox/brass + name = "brass toolbox" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "brass" + item_state_slots = list(slot_r_hand_str = "toolbox_yellow", slot_l_hand_str = "toolbox_yellow") + starts_with = list( + /obj/item/weapon/tool/crowbar/brass, + /obj/item/weapon/tool/wirecutters/brass, + /obj/item/weapon/tool/screwdriver/brass, + /obj/item/weapon/tool/wrench/brass, + /obj/item/weapon/weldingtool/brass + ) + +//Hydro +/obj/item/weapon/storage/toolbox/hydro + name = "hydroponic toolbox" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "green" + item_state_slots = list(slot_r_hand_str = "toolbox_green", slot_l_hand_str = "toolbox_green") + starts_with = list( + /obj/item/device/analyzer/plant_analyzer, + /obj/item/weapon/material/minihoe, + /obj/item/weapon/material/knife/machete/hatchet, + /obj/item/weapon/tool/wirecutters/clippers/trimmers, + /obj/item/weapon/reagent_containers/spray/plantbgone, + /obj/item/weapon/reagent_containers/glass/beaker + ) + +/* + * Lunchboxes + */ + +/obj/item/weapon/storage/toolbox/lunchbox + max_storage_space = ITEMSIZE_COST_SMALL * 4 //slightly smaller than a toolbox + name = "rainbow lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_rainbow" + item_state_slots = list(slot_r_hand_str = "toolbox_pink", slot_l_hand_str = "toolbox_pink") + desc = "A little lunchbox. This one is the colors of the rainbow!" + w_class = ITEMSIZE_NORMAL + max_w_class = ITEMSIZE_SMALL + var/filled = FALSE + attack_verb = list("lunched") + +/obj/item/weapon/storage/toolbox/lunchbox/Initialize() + if(filled) + var/list/lunches = lunchables_lunches() + var/lunch = lunches[pick(lunches)] + new lunch(src) + + var/list/snacks = lunchables_snacks() + var/snack = snacks[pick(snacks)] + new snack(src) + + var/list/drinks = lunchables_drinks() + var/drink = drinks[pick(drinks)] + new drink(src) + . = ..() + +/obj/item/weapon/storage/toolbox/lunchbox/filled + filled = TRUE + +/obj/item/weapon/storage/toolbox/lunchbox/heart + name = "heart lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_lovelyhearts" + item_state_slots = list(slot_r_hand_str = "toolbox_pink", slot_l_hand_str = "toolbox_pink") + desc = "A little lunchbox. This one has cute little hearts on it!" + +/obj/item/weapon/storage/toolbox/lunchbox/heart/filled + filled = TRUE + +/obj/item/weapon/storage/toolbox/lunchbox/cat + name = "cat lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_sciencecatshow" + item_state_slots = list(slot_r_hand_str = "toolbox_green", slot_l_hand_str = "toolbox_green") + desc = "A little lunchbox. This one has a cute little science cat from a popular show on it!" + +/obj/item/weapon/storage/toolbox/lunchbox/cat/filled + filled = TRUE + +/obj/item/weapon/storage/toolbox/lunchbox/nt + name = "NanoTrasen brand lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_nanotrasen" + item_state_slots = list(slot_r_hand_str = "toolbox_blue", slot_l_hand_str = "toolbox_blue") + desc = "A little lunchbox. This one is branded with the NanoTrasen logo!" + +/obj/item/weapon/storage/toolbox/lunchbox/nt/filled + filled = TRUE + +/obj/item/weapon/storage/toolbox/lunchbox/mars + name = "\improper Mojave university lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_marsuniversity" + item_state_slots = list(slot_r_hand_str = "toolbox_red", slot_l_hand_str = "toolbox_red") + desc = "A little lunchbox. This one is branded with the Mojave university logo!" + +/obj/item/weapon/storage/toolbox/lunchbox/mars/filled + filled = TRUE + +/obj/item/weapon/storage/toolbox/lunchbox/cti + name = "\improper CTI lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_cti" + item_state_slots = list(slot_r_hand_str = "toolbox_blue", slot_l_hand_str = "toolbox_blue") + desc = "A little lunchbox. This one is branded with the CTI logo!" + +/obj/item/weapon/storage/toolbox/lunchbox/cti/filled + filled = TRUE + +/obj/item/weapon/storage/toolbox/lunchbox/nymph + name = "\improper Diona nymph lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_dionanymph" + item_state_slots = list(slot_r_hand_str = "toolbox_yellow", slot_l_hand_str = "toolbox_yellow") + desc = "A little lunchbox. This one is an adorable Diona nymph on the side!" + +/obj/item/weapon/storage/toolbox/lunchbox/nymph/filled + filled = TRUE + +/obj/item/weapon/storage/toolbox/lunchbox/syndicate + name = "black and red lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_syndie" + item_state_slots = list(slot_r_hand_str = "toolbox_syndi", slot_l_hand_str = "toolbox_syndi") + desc = "A little lunchbox. This one is a sleek black and red, made of a durable steel!" + +/obj/item/weapon/storage/toolbox/lunchbox/syndicate/filled + filled = TRUE diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm index 0ff349f4769..40b0ef44b41 100644 --- a/code/game/objects/items/weapons/swords_axes_etc.dm +++ b/code/game/objects/items/weapons/swords_axes_etc.dm @@ -1,114 +1,114 @@ -/* Weapons - * Contains: - * Sword - * Classic Baton - * Telescopic Baton - */ - -/* - * Classic Baton - */ - -/obj/item/weapon/melee - name = "weapon" - desc = "Murder device." - icon = 'icons/obj/weapons.dmi' - icon_state = "baton" - slot_flags = SLOT_BELT - force = 10 - drop_sound = 'sound/items/drop/metalweapon.ogg' - -/obj/item/weapon/melee/classic_baton - name = "police baton" - desc = "A wooden truncheon for beating criminal scum." - icon = 'icons/obj/weapons.dmi' - icon_state = "baton" - item_state = "classic_baton" - slot_flags = SLOT_BELT - force = 10 - drop_sound = 'sound/items/drop/crowbar.ogg' - pickup_sound = 'sound/items/pickup/crowbar.ogg' - -/obj/item/weapon/melee/classic_baton/attack(mob/M as mob, mob/living/user as mob) - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "You club yourself over the head.") - user.Weaken(3 * force) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.apply_damage(2*force, BRUTE, BP_HEAD) - else - user.take_organ_damage(2*force) - return - return ..() - -//Telescopic baton -/obj/item/weapon/melee/telebaton - name = "telescopic baton" - desc = "A compact yet rebalanced personal defense weapon. Can be concealed when folded." - icon = 'icons/obj/weapons.dmi' - icon_state = "telebaton0" - item_state = "telebaton0" - slot_flags = SLOT_BELT - w_class = ITEMSIZE_SMALL - force = 3 - drop_sound = 'sound/items/drop/crowbar.ogg' - pickup_sound = 'sound/items/pickup/crowbar.ogg' - var/on = 0 - -/obj/item/weapon/melee/telebaton/attack_self(mob/user as mob) - on = !on - if(on) - user.visible_message("With a flick of their wrist, [user] extends their telescopic baton.",\ - "You extend the baton.",\ - "You hear an ominous click.") - icon_state = "telebaton1" - item_state = icon_state - w_class = ITEMSIZE_NORMAL - force = 15//quite robust - attack_verb = list("smacked", "struck", "slapped") - else - user.visible_message("\The [user] collapses their telescopic baton.",\ - "You collapse the baton.",\ - "You hear a click.") - icon_state = "telebaton0" - item_state = icon_state - w_class = ITEMSIZE_SMALL - force = 3//not so robust now - attack_verb = list("hit", "punched") - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - playsound(src, 'sound/weapons/empty.ogg', 50, 1) - add_fingerprint(user) - - if(blood_overlay && blood_DNA && (blood_DNA.len >= 1)) //updates blood overlay, if any - cut_overlays() - - var/icon/I = new /icon(src.icon, src.icon_state) - I.Blend(new /icon('icons/effects/blood.dmi', rgb(255,255,255)),ICON_ADD) - I.Blend(new /icon('icons/effects/blood.dmi', "itemblood"),ICON_MULTIPLY) - blood_overlay = I - - add_overlay(blood_overlay) - - return - -/obj/item/weapon/melee/telebaton/attack(mob/target as mob, mob/living/user as mob) - if(on) - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "You club yourself over the head.") - user.Weaken(3 * force) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.apply_damage(2*force, BRUTE, BP_HEAD) - else - user.take_organ_damage(2*force) - return - if(..()) - //playsound(src, "swing_hit", 50, 1, -1) - return - else - return ..() +/* Weapons + * Contains: + * Sword + * Classic Baton + * Telescopic Baton + */ + +/* + * Classic Baton + */ + +/obj/item/weapon/melee + name = "weapon" + desc = "Murder device." + icon = 'icons/obj/weapons.dmi' + icon_state = "baton" + slot_flags = SLOT_BELT + force = 10 + drop_sound = 'sound/items/drop/metalweapon.ogg' + +/obj/item/weapon/melee/classic_baton + name = "police baton" + desc = "A wooden truncheon for beating criminal scum." + icon = 'icons/obj/weapons.dmi' + icon_state = "baton" + item_state = "classic_baton" + slot_flags = SLOT_BELT + force = 10 + drop_sound = 'sound/items/drop/crowbar.ogg' + pickup_sound = 'sound/items/pickup/crowbar.ogg' + +/obj/item/weapon/melee/classic_baton/attack(mob/M as mob, mob/living/user as mob) + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "You club yourself over the head.") + user.Weaken(3 * force) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.apply_damage(2*force, BRUTE, BP_HEAD) + else + user.take_organ_damage(2*force) + return + return ..() + +//Telescopic baton +/obj/item/weapon/melee/telebaton + name = "telescopic baton" + desc = "A compact yet rebalanced personal defense weapon. Can be concealed when folded." + icon = 'icons/obj/weapons.dmi' + icon_state = "telebaton0" + item_state = "telebaton0" + slot_flags = SLOT_BELT + w_class = ITEMSIZE_SMALL + force = 3 + drop_sound = 'sound/items/drop/crowbar.ogg' + pickup_sound = 'sound/items/pickup/crowbar.ogg' + var/on = 0 + +/obj/item/weapon/melee/telebaton/attack_self(mob/user as mob) + on = !on + if(on) + user.visible_message("With a flick of their wrist, [user] extends their telescopic baton.",\ + "You extend the baton.",\ + "You hear an ominous click.") + icon_state = "telebaton1" + item_state = icon_state + w_class = ITEMSIZE_NORMAL + force = 15//quite robust + attack_verb = list("smacked", "struck", "slapped") + else + user.visible_message("\The [user] collapses their telescopic baton.",\ + "You collapse the baton.",\ + "You hear a click.") + icon_state = "telebaton0" + item_state = icon_state + w_class = ITEMSIZE_SMALL + force = 3//not so robust now + attack_verb = list("hit", "punched") + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + + playsound(src, 'sound/weapons/empty.ogg', 50, 1) + add_fingerprint(user) + + if(blood_overlay && blood_DNA && (blood_DNA.len >= 1)) //updates blood overlay, if any + cut_overlays() + + var/icon/I = new /icon(src.icon, src.icon_state) + I.Blend(new /icon('icons/effects/blood.dmi', rgb(255,255,255)),ICON_ADD) + I.Blend(new /icon('icons/effects/blood.dmi', "itemblood"),ICON_MULTIPLY) + blood_overlay = I + + add_overlay(blood_overlay) + + return + +/obj/item/weapon/melee/telebaton/attack(mob/target as mob, mob/living/user as mob) + if(on) + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "You club yourself over the head.") + user.Weaken(3 * force) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.apply_damage(2*force, BRUTE, BP_HEAD) + else + user.take_organ_damage(2*force) + return + if(..()) + //playsound(src, "swing_hit", 50, 1, -1) + return + else + return ..() diff --git a/code/game/objects/items/weapons/tanks/jetpack.dm b/code/game/objects/items/weapons/tanks/jetpack.dm index 8bb5bb465b8..10724e2e3da 100644 --- a/code/game/objects/items/weapons/tanks/jetpack.dm +++ b/code/game/objects/items/weapons/tanks/jetpack.dm @@ -1,137 +1,137 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/item/weapon/tank/jetpack - name = "jetpack (empty)" - desc = "A tank of compressed gas for use as propulsion in zero-gravity areas. Use with caution." - icon = 'icons/obj/tank_vr.dmi' //VOREStation Edit - icon_state = "jetpack" - gauge_icon = null - w_class = ITEMSIZE_LARGE - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_storage.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_storage.dmi', - ) - item_state_slots = list(slot_r_hand_str = "jetpack", slot_l_hand_str = "jetpack") - distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - var/datum/effect/effect/system/ion_trail_follow/ion_trail - var/on = 0.0 - var/stabilization_on = 0 - var/volume_rate = 500 //Needed for borg jetpack transfer - action_button_name = "Toggle Jetpack" - -/obj/item/weapon/tank/jetpack/New() - ..() - ion_trail = new /datum/effect/effect/system/ion_trail_follow() - ion_trail.set_up(src) - -/obj/item/weapon/tank/jetpack/Destroy() - QDEL_NULL(ion_trail) - return ..() - -/obj/item/weapon/tank/jetpack/examine(mob/user) - . = ..() - if(air_contents.total_moles < 5) - . += "The meter on \the [src] indicates you are almost out of gas!" - playsound(src, 'sound/effects/alert.ogg', 50, 1) - -/obj/item/weapon/tank/jetpack/verb/toggle_rockets() - set name = "Toggle Jetpack Stabilization" - set category = "Object" - stabilization_on = !( stabilization_on ) - to_chat(usr, "You toggle the stabilization [stabilization_on? "on":"off"].") - -/obj/item/weapon/tank/jetpack/verb/toggle() - set name = "Toggle Jetpack" - set category = "Object" - - on = !on - if(on) - icon_state = "[icon_state]-on" - ion_trail.start() - else - icon_state = initial(icon_state) - ion_trail.stop() - - if (ismob(usr)) - var/mob/M = usr - M.update_inv_back() - M.update_action_buttons() - - to_chat(usr, "You toggle the thrusters [on? "on":"off"].") - -/obj/item/weapon/tank/jetpack/proc/get_gas_supply() - return air_contents - -/obj/item/weapon/tank/jetpack/proc/can_thrust(num) - if(!on) - return 0 - - var/datum/gas_mixture/fuel = get_gas_supply() - if(num < 0.005 || !fuel || fuel.total_moles < num) - ion_trail.stop() - return 0 - - return 1 - -/obj/item/weapon/tank/jetpack/proc/do_thrust(num, mob/living/user) - if(!can_thrust(num)) - return 0 - - var/datum/gas_mixture/fuel = get_gas_supply() - fuel.remove(num) - return 1 - -/obj/item/weapon/tank/jetpack/ui_action_click() - toggle() - -/obj/item/weapon/tank/jetpack/void - name = "void jetpack (oxygen)" - desc = "It works well in a void." - icon_state = "jetpack-void" - item_state_slots = list(slot_r_hand_str = "jetpack-void", slot_l_hand_str = "jetpack-void") - -/obj/item/weapon/tank/jetpack/void/Initialize() - . = ..() - air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/jetpack/oxygen - name = "jetpack (oxygen)" - desc = "A tank of compressed oxygen for use as propulsion in zero-gravity areas. Use with caution." - icon_state = "jetpack" - item_state_slots = list(slot_r_hand_str = "jetpack", slot_l_hand_str = "jetpack") - -/obj/item/weapon/tank/jetpack/oxygen/Initialize() - . = ..() - air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/jetpack/breaker - name = "CSC industrial jetpack" - desc = "A JetFast EVA thruster pack. A warning label clearly states \'WARNING: CONTAINS VOLATILE REACTION MASS TOXIC TO MOST LIFEFORMS. NOT TO BE USED WITH CLOSED CYCLE BREATHING SYSTEMS.\'" - icon_state = "jetpack-breaker" - item_state_slots = list(slot_r_hand_str = "jetpack", slot_l_hand_str = "jetpack") - -/obj/item/weapon/tank/jetpack/breaker/Initialize() - . = ..() - air_contents.adjust_gas("volatile_fuel", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/jetpack/carbondioxide - name = "jetpack (carbon dioxide)" - desc = "A tank of compressed carbon dioxide for use as propulsion in zero-gravity areas. Painted black to indicate that it should not be used as a source for internals." - distribute_pressure = 0 - icon_state = "jetpack-black" - item_state_slots = list(slot_r_hand_str = "jetpack-black", slot_l_hand_str = "jetpack-black") - -/obj/item/weapon/tank/jetpack/carbondioxide/Initialize() - . = ..() - air_contents.adjust_gas("carbon_dioxide", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/jetpack/rig - name = "jetpack" - var/obj/item/weapon/rig/holder - -/obj/item/weapon/tank/jetpack/rig/examine() - . = ..() - . += "It's a jetpack. If you can see this, report it on the bug tracker." - -/obj/item/weapon/tank/jetpack/rig/get_gas_supply() - return holder?.air_supply?.air_contents +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/obj/item/weapon/tank/jetpack + name = "jetpack (empty)" + desc = "A tank of compressed gas for use as propulsion in zero-gravity areas. Use with caution." + icon = 'icons/obj/tank_vr.dmi' //VOREStation Edit + icon_state = "jetpack" + gauge_icon = null + w_class = ITEMSIZE_LARGE + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_storage.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_storage.dmi', + ) + item_state_slots = list(slot_r_hand_str = "jetpack", slot_l_hand_str = "jetpack") + distribute_pressure = ONE_ATMOSPHERE*O2STANDARD + var/datum/effect/effect/system/ion_trail_follow/ion_trail + var/on = 0.0 + var/stabilization_on = 0 + var/volume_rate = 500 //Needed for borg jetpack transfer + action_button_name = "Toggle Jetpack" + +/obj/item/weapon/tank/jetpack/New() + ..() + ion_trail = new /datum/effect/effect/system/ion_trail_follow() + ion_trail.set_up(src) + +/obj/item/weapon/tank/jetpack/Destroy() + QDEL_NULL(ion_trail) + return ..() + +/obj/item/weapon/tank/jetpack/examine(mob/user) + . = ..() + if(air_contents.total_moles < 5) + . += "The meter on \the [src] indicates you are almost out of gas!" + playsound(src, 'sound/effects/alert.ogg', 50, 1) + +/obj/item/weapon/tank/jetpack/verb/toggle_rockets() + set name = "Toggle Jetpack Stabilization" + set category = "Object" + stabilization_on = !( stabilization_on ) + to_chat(usr, "You toggle the stabilization [stabilization_on? "on":"off"].") + +/obj/item/weapon/tank/jetpack/verb/toggle() + set name = "Toggle Jetpack" + set category = "Object" + + on = !on + if(on) + icon_state = "[icon_state]-on" + ion_trail.start() + else + icon_state = initial(icon_state) + ion_trail.stop() + + if (ismob(usr)) + var/mob/M = usr + M.update_inv_back() + M.update_action_buttons() + + to_chat(usr, "You toggle the thrusters [on? "on":"off"].") + +/obj/item/weapon/tank/jetpack/proc/get_gas_supply() + return air_contents + +/obj/item/weapon/tank/jetpack/proc/can_thrust(num) + if(!on) + return 0 + + var/datum/gas_mixture/fuel = get_gas_supply() + if(num < 0.005 || !fuel || fuel.total_moles < num) + ion_trail.stop() + return 0 + + return 1 + +/obj/item/weapon/tank/jetpack/proc/do_thrust(num, mob/living/user) + if(!can_thrust(num)) + return 0 + + var/datum/gas_mixture/fuel = get_gas_supply() + fuel.remove(num) + return 1 + +/obj/item/weapon/tank/jetpack/ui_action_click() + toggle() + +/obj/item/weapon/tank/jetpack/void + name = "void jetpack (oxygen)" + desc = "It works well in a void." + icon_state = "jetpack-void" + item_state_slots = list(slot_r_hand_str = "jetpack-void", slot_l_hand_str = "jetpack-void") + +/obj/item/weapon/tank/jetpack/void/Initialize() + . = ..() + air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/jetpack/oxygen + name = "jetpack (oxygen)" + desc = "A tank of compressed oxygen for use as propulsion in zero-gravity areas. Use with caution." + icon_state = "jetpack" + item_state_slots = list(slot_r_hand_str = "jetpack", slot_l_hand_str = "jetpack") + +/obj/item/weapon/tank/jetpack/oxygen/Initialize() + . = ..() + air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/jetpack/breaker + name = "CSC industrial jetpack" + desc = "A JetFast EVA thruster pack. A warning label clearly states \'WARNING: CONTAINS VOLATILE REACTION MASS TOXIC TO MOST LIFEFORMS. NOT TO BE USED WITH CLOSED CYCLE BREATHING SYSTEMS.\'" + icon_state = "jetpack-breaker" + item_state_slots = list(slot_r_hand_str = "jetpack", slot_l_hand_str = "jetpack") + +/obj/item/weapon/tank/jetpack/breaker/Initialize() + . = ..() + air_contents.adjust_gas("volatile_fuel", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/jetpack/carbondioxide + name = "jetpack (carbon dioxide)" + desc = "A tank of compressed carbon dioxide for use as propulsion in zero-gravity areas. Painted black to indicate that it should not be used as a source for internals." + distribute_pressure = 0 + icon_state = "jetpack-black" + item_state_slots = list(slot_r_hand_str = "jetpack-black", slot_l_hand_str = "jetpack-black") + +/obj/item/weapon/tank/jetpack/carbondioxide/Initialize() + . = ..() + air_contents.adjust_gas("carbon_dioxide", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/jetpack/rig + name = "jetpack" + var/obj/item/weapon/rig/holder + +/obj/item/weapon/tank/jetpack/rig/examine() + . = ..() + . += "It's a jetpack. If you can see this, report it on the bug tracker." + +/obj/item/weapon/tank/jetpack/rig/get_gas_supply() + return holder?.air_supply?.air_contents diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm index 1764b0d8bec..e34ad4810b7 100644 --- a/code/game/objects/items/weapons/tanks/tank_types.dm +++ b/code/game/objects/items/weapons/tanks/tank_types.dm @@ -1,230 +1,230 @@ -/* Types of tanks! - * Contains: - * Oxygen - * Anesthetic - * Air - * Phoron - * Emergency Oxygen - */ - -/* - * Oxygen - */ -/obj/item/weapon/tank/oxygen - name = "oxygen tank" - desc = "A tank of oxygen." - icon_state = "oxygen" - distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - -/obj/item/weapon/tank/oxygen/Initialize() - . = ..() - air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/oxygen/examine(mob/user) - . = ..() - if(loc == user && (air_contents.gas["oxygen"] < 10)) - . += "The meter on \the [src] indicates you are almost out of oxygen!" - -/obj/item/weapon/tank/oxygen/yellow - desc = "A tank of oxygen, this one is yellow." - icon_state = "oxygen_f" - -/obj/item/weapon/tank/oxygen/red - desc = "A tank of oxygen, this one is red." - icon_state = "oxygen_fr" - -/* - * Anesthetic - */ -/obj/item/weapon/tank/anesthetic - name = "anesthetic tank" - desc = "A tank with an N2O/O2 gas mix." - icon_state = "anesthetic" - -/obj/item/weapon/tank/anesthetic/Initialize() - . = ..() - - air_contents.gas["oxygen"] = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD - air_contents.gas["nitrous_oxide"] = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD - air_contents.update_values() - -/* - * Air - */ -/obj/item/weapon/tank/air - name = "air tank" - desc = "Mixed anyone?" - icon_state = "oxygen" - -/obj/item/weapon/tank/air/examine(mob/user) - . = ..() - if(loc == user && (air_contents.gas["oxygen"] < 1)) - . += "The meter on \the [src] indicates you are almost out of air!" - user << sound('sound/effects/alert.ogg') - -/obj/item/weapon/tank/air/Initialize() - . = ..() - src.air_contents.adjust_multi("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD, "nitrogen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD) - -/* - * Phoron - */ -/obj/item/weapon/tank/phoron - name = "phoron tank" - desc = "Contains dangerous phoron. Do not inhale. Warning: extremely flammable." - icon_state = "phoron" - gauge_icon = null - slot_flags = null //they have no straps! - -/obj/item/weapon/tank/phoron/Initialize() - . = ..() - src.air_contents.adjust_gas("phoron", (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/phoron/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - - if (istype(W, /obj/item/weapon/flamethrower)) - var/obj/item/weapon/flamethrower/F = W - if ((!F.status)||(F.ptank)) return - src.master = F - F.ptank = src - user.remove_from_mob(src) - src.loc = F - return - -/obj/item/weapon/tank/vox //Can't be a child of phoron or the gas amount gets screwey. - name = "phoron tank" - desc = "Contains dangerous phoron. Do not inhale. Warning: extremely flammable." - icon_state = "phoron_vox" - gauge_icon = null - distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - slot_flags = SLOT_BACK //these ones have straps! - -/obj/item/weapon/tank/vox/Initialize() - . = ..() - air_contents.adjust_gas("phoron", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) //VOREStation Edit - -/obj/item/weapon/tank/phoron/pressurized - name = "fuel can" - icon_state = "phoron_vox" - w_class = ITEMSIZE_NORMAL - -/obj/item/weapon/tank/phoron/pressurized/Initialize() - . = ..() - adjust_scale(0.8) - air_contents.adjust_gas("phoron", (7*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/* - * Emergency Oxygen - */ - -/obj/item/weapon/tank/emergency - name = "emergency tank" - icon_state = "emergency" - gauge_icon = "indicator_emergency" - gauge_cap = 4 - slot_flags = SLOT_BELT - w_class = ITEMSIZE_SMALL - force = 4 - distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - volume = 2 //Tiny. Real life equivalents only have 21 breaths of oxygen in them. They're EMERGENCY tanks anyway -errorage (dangercon 2011) - -/obj/item/weapon/tank/emergency/oxygen - name = "emergency oxygen tank" - desc = "Used for emergencies. Contains very little oxygen, so try to conserve it until you actually need it." - icon_state = "emergency" - gauge_icon = "indicator_emergency" - -/obj/item/weapon/tank/emergency/oxygen/Initialize() - . = ..() - src.air_contents.adjust_gas("oxygen", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/emergency/oxygen/examine(mob/user) - . = ..() - if(loc == user && (air_contents.gas["oxygen"] < 0.2)) - . += "The meter on the [src.name] indicates you are almost out of air!" - user << sound('sound/effects/alert.ogg') - -/obj/item/weapon/tank/emergency/oxygen/engi - name = "extended-capacity emergency oxygen tank" - icon_state = "emergency_engi" - volume = 6 - -/obj/item/weapon/tank/emergency/oxygen/double - name = "double emergency oxygen tank" - icon_state = "emergency_double" - gauge_icon = "indicator_emergency_double" - volume = 10 - -/obj/item/weapon/tank/stasis/oxygen // Stasis bags need to have initial pressure within safe bounds for human atmospheric pressure (NOT breath pressure) - name = "stasis oxygen tank" - desc = "Oxygen tank included in most stasis bag designs." - icon_state = "emergency_double" - gauge_icon = "indicator_emergency_double" - volume = 10 - -/obj/item/weapon/tank/stasis/oxygen/Initialize() - . = ..() - src.air_contents.adjust_gas("oxygen", (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/emergency/nitrogen - name = "emergency nitrogen tank" - desc = "An emergency air tank hastily painted red." - icon_state = "emergency_nitro" - gauge_icon = "indicator_emergency" - -/obj/item/weapon/tank/emergency/nitrogen/Initialize() - . = ..() - src.air_contents.adjust_gas("nitrogen", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/emergency/nitrogen/double - name = "double emergency nitrogen tank" - icon_state = "emergency_double_nitrogen" - gauge_icon = "indicator_emergency_double" - volume = 10 - -/obj/item/weapon/tank/emergency/phoron - name = "emergency phoron tank" - desc = "An emergency air tank hastily painted red." - icon_state = "emergency_nitro" - gauge_icon = "indicator_emergency" - -/obj/item/weapon/tank/emergency/phoron/Initialize() - . = ..() - src.air_contents.adjust_gas("phoron", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/emergency/phoron/double - name = "double emergency phoron tank" - icon_state = "emergency_double_nitro" - gauge_icon = "indicator_emergency_double" - volume = 10 - -/* - * Nitrogen - */ -/obj/item/weapon/tank/nitrogen - name = "nitrogen tank" - desc = "A tank of nitrogen." - icon_state = "oxygen_fr" - distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - -/obj/item/weapon/tank/nitrogen/Initialize() - . = ..() - src.air_contents.adjust_gas("nitrogen", (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/nitrogen/examine(mob/user) - . = ..() - if(loc == user && (air_contents.gas["nitrogen"] < 10)) - . += "The meter on \the [src] indicates you are almost out of nitrogen!" - //playsound(user, 'sound/effects/alert.ogg', 50, 1) - -/obj/item/weapon/tank/stasis/nitro_cryo // Synthmorph bags need to have initial pressure within safe bounds for human atmospheric pressure, but low temperature to stop unwanted degredation. - name = "stasis cryogenic nitrogen tank" - desc = "Cryogenic Nitrogen tank included in most synthmorph bag designs." - icon_state = "emergency_double_nitro" - gauge_icon = "indicator_emergency_double" - volume = 10 - -/obj/item/weapon/tank/stasis/nitro_cryo/Initialize() - . = ..() - src.air_contents.adjust_gas_temp("nitrogen", (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*TN60C), TN60C) +/* Types of tanks! + * Contains: + * Oxygen + * Anesthetic + * Air + * Phoron + * Emergency Oxygen + */ + +/* + * Oxygen + */ +/obj/item/weapon/tank/oxygen + name = "oxygen tank" + desc = "A tank of oxygen." + icon_state = "oxygen" + distribute_pressure = ONE_ATMOSPHERE*O2STANDARD + +/obj/item/weapon/tank/oxygen/Initialize() + . = ..() + air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/oxygen/examine(mob/user) + . = ..() + if(loc == user && (air_contents.gas["oxygen"] < 10)) + . += "The meter on \the [src] indicates you are almost out of oxygen!" + +/obj/item/weapon/tank/oxygen/yellow + desc = "A tank of oxygen, this one is yellow." + icon_state = "oxygen_f" + +/obj/item/weapon/tank/oxygen/red + desc = "A tank of oxygen, this one is red." + icon_state = "oxygen_fr" + +/* + * Anesthetic + */ +/obj/item/weapon/tank/anesthetic + name = "anesthetic tank" + desc = "A tank with an N2O/O2 gas mix." + icon_state = "anesthetic" + +/obj/item/weapon/tank/anesthetic/Initialize() + . = ..() + + air_contents.gas["oxygen"] = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD + air_contents.gas["nitrous_oxide"] = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD + air_contents.update_values() + +/* + * Air + */ +/obj/item/weapon/tank/air + name = "air tank" + desc = "Mixed anyone?" + icon_state = "oxygen" + +/obj/item/weapon/tank/air/examine(mob/user) + . = ..() + if(loc == user && (air_contents.gas["oxygen"] < 1)) + . += "The meter on \the [src] indicates you are almost out of air!" + user << sound('sound/effects/alert.ogg') + +/obj/item/weapon/tank/air/Initialize() + . = ..() + src.air_contents.adjust_multi("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD, "nitrogen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD) + +/* + * Phoron + */ +/obj/item/weapon/tank/phoron + name = "phoron tank" + desc = "Contains dangerous phoron. Do not inhale. Warning: extremely flammable." + icon_state = "phoron" + gauge_icon = null + slot_flags = null //they have no straps! + +/obj/item/weapon/tank/phoron/Initialize() + . = ..() + src.air_contents.adjust_gas("phoron", (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/phoron/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + + if (istype(W, /obj/item/weapon/flamethrower)) + var/obj/item/weapon/flamethrower/F = W + if ((!F.status)||(F.ptank)) return + src.master = F + F.ptank = src + user.remove_from_mob(src) + src.loc = F + return + +/obj/item/weapon/tank/vox //Can't be a child of phoron or the gas amount gets screwey. + name = "phoron tank" + desc = "Contains dangerous phoron. Do not inhale. Warning: extremely flammable." + icon_state = "phoron_vox" + gauge_icon = null + distribute_pressure = ONE_ATMOSPHERE*O2STANDARD + slot_flags = SLOT_BACK //these ones have straps! + +/obj/item/weapon/tank/vox/Initialize() + . = ..() + air_contents.adjust_gas("phoron", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) //VOREStation Edit + +/obj/item/weapon/tank/phoron/pressurized + name = "fuel can" + icon_state = "phoron_vox" + w_class = ITEMSIZE_NORMAL + +/obj/item/weapon/tank/phoron/pressurized/Initialize() + . = ..() + adjust_scale(0.8) + air_contents.adjust_gas("phoron", (7*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/* + * Emergency Oxygen + */ + +/obj/item/weapon/tank/emergency + name = "emergency tank" + icon_state = "emergency" + gauge_icon = "indicator_emergency" + gauge_cap = 4 + slot_flags = SLOT_BELT + w_class = ITEMSIZE_SMALL + force = 4 + distribute_pressure = ONE_ATMOSPHERE*O2STANDARD + volume = 2 //Tiny. Real life equivalents only have 21 breaths of oxygen in them. They're EMERGENCY tanks anyway -errorage (dangercon 2011) + +/obj/item/weapon/tank/emergency/oxygen + name = "emergency oxygen tank" + desc = "Used for emergencies. Contains very little oxygen, so try to conserve it until you actually need it." + icon_state = "emergency" + gauge_icon = "indicator_emergency" + +/obj/item/weapon/tank/emergency/oxygen/Initialize() + . = ..() + src.air_contents.adjust_gas("oxygen", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/emergency/oxygen/examine(mob/user) + . = ..() + if(loc == user && (air_contents.gas["oxygen"] < 0.2)) + . += "The meter on the [src.name] indicates you are almost out of air!" + user << sound('sound/effects/alert.ogg') + +/obj/item/weapon/tank/emergency/oxygen/engi + name = "extended-capacity emergency oxygen tank" + icon_state = "emergency_engi" + volume = 6 + +/obj/item/weapon/tank/emergency/oxygen/double + name = "double emergency oxygen tank" + icon_state = "emergency_double" + gauge_icon = "indicator_emergency_double" + volume = 10 + +/obj/item/weapon/tank/stasis/oxygen // Stasis bags need to have initial pressure within safe bounds for human atmospheric pressure (NOT breath pressure) + name = "stasis oxygen tank" + desc = "Oxygen tank included in most stasis bag designs." + icon_state = "emergency_double" + gauge_icon = "indicator_emergency_double" + volume = 10 + +/obj/item/weapon/tank/stasis/oxygen/Initialize() + . = ..() + src.air_contents.adjust_gas("oxygen", (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/emergency/nitrogen + name = "emergency nitrogen tank" + desc = "An emergency air tank hastily painted red." + icon_state = "emergency_nitro" + gauge_icon = "indicator_emergency" + +/obj/item/weapon/tank/emergency/nitrogen/Initialize() + . = ..() + src.air_contents.adjust_gas("nitrogen", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/emergency/nitrogen/double + name = "double emergency nitrogen tank" + icon_state = "emergency_double_nitrogen" + gauge_icon = "indicator_emergency_double" + volume = 10 + +/obj/item/weapon/tank/emergency/phoron + name = "emergency phoron tank" + desc = "An emergency air tank hastily painted red." + icon_state = "emergency_nitro" + gauge_icon = "indicator_emergency" + +/obj/item/weapon/tank/emergency/phoron/Initialize() + . = ..() + src.air_contents.adjust_gas("phoron", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/emergency/phoron/double + name = "double emergency phoron tank" + icon_state = "emergency_double_nitro" + gauge_icon = "indicator_emergency_double" + volume = 10 + +/* + * Nitrogen + */ +/obj/item/weapon/tank/nitrogen + name = "nitrogen tank" + desc = "A tank of nitrogen." + icon_state = "oxygen_fr" + distribute_pressure = ONE_ATMOSPHERE*O2STANDARD + +/obj/item/weapon/tank/nitrogen/Initialize() + . = ..() + src.air_contents.adjust_gas("nitrogen", (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/nitrogen/examine(mob/user) + . = ..() + if(loc == user && (air_contents.gas["nitrogen"] < 10)) + . += "The meter on \the [src] indicates you are almost out of nitrogen!" + //playsound(user, 'sound/effects/alert.ogg', 50, 1) + +/obj/item/weapon/tank/stasis/nitro_cryo // Synthmorph bags need to have initial pressure within safe bounds for human atmospheric pressure, but low temperature to stop unwanted degredation. + name = "stasis cryogenic nitrogen tank" + desc = "Cryogenic Nitrogen tank included in most synthmorph bag designs." + icon_state = "emergency_double_nitro" + gauge_icon = "indicator_emergency_double" + volume = 10 + +/obj/item/weapon/tank/stasis/nitro_cryo/Initialize() + . = ..() + src.air_contents.adjust_gas_temp("nitrogen", (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*TN60C), TN60C) diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 26ca1072817..8c176ecbbb3 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -1,671 +1,671 @@ -#define TANK_IDEAL_PRESSURE 1015 //Arbitrary. - -var/list/global/tank_gauge_cache = list() - -/obj/item/weapon/tank - name = "tank" - icon = 'icons/obj/tank.dmi' - sprite_sheets = list( - SPECIES_TESHARI = 'icons/inventory/back/mob_teshari.dmi' - ) - drop_sound = 'sound/items/drop/gascan.ogg' - pickup_sound = 'sound/items/pickup/gascan.ogg' - - var/gauge_icon = "indicator_tank" - var/last_gauge_pressure - var/gauge_cap = 6 - - slot_flags = SLOT_BACK - w_class = ITEMSIZE_NORMAL - - force = 5.0 - throwforce = 10.0 - throw_speed = 1 - throw_range = 4 - - var/datum/gas_mixture/air_contents = null - var/distribute_pressure = ONE_ATMOSPHERE - var/integrity = 20 - var/maxintegrity = 20 - var/valve_welded = 0 - var/obj/item/device/tankassemblyproxy/proxyassembly - - var/volume = 70 - var/manipulated_by = null //Used by _onclick/hud/screen_objects.dm internals to determine if someone has messed with our tank or not. - //If they have and we haven't scanned it with the PDA or gas analyzer then we might just breath whatever they put in it. - - var/failure_temp = 173 //173 deg C Borate seal (yes it should be 153 F, but that's annoying) - var/leaking = 0 - var/wired = 0 - - description_info = "These tanks are utilised to store any of the various types of gaseous substances. \ - They can be attached to various portable atmospheric devices to be filled or emptied.
                    \ -
                    \ - Each tank is fitted with an emergency relief valve. This relief valve will open if the tank is pressurised to over ~3000kPa or heated to over 173ºC. \ - The valve itself will close after expending most or all of the contents into the air.
                    \ -
                    \ - Filling a tank such that experiences ~4000kPa of pressure will cause the tank to rupture, spilling out its contents and destroying the tank. \ - Tanks filled over ~5000kPa will rupture rather violently, exploding with significant force." - - description_antag = "Each tank may be incited to burn by attaching wires and an igniter assembly, though the igniter can only be used once and the mixture only burn if the igniter pushes a flammable gas mixture above the minimum burn temperature (126ºC). \ - Wired and assembled tanks may be disarmed with a set of wirecutters. Any exploding or rupturing tank will generate shrapnel, assuming their relief valves have been welded beforehand. Even if not, they can be incited to expel hot gas on ignition if pushed above 173ºC. \ - Relatively easy to make, the single tank bomb requries no tank transfer valve, and is still a fairly formidable weapon that can be manufactured from any tank." - -/obj/item/weapon/tank/proc/init_proxy() - var/obj/item/device/tankassemblyproxy/proxy = new /obj/item/device/tankassemblyproxy(src) - proxy.tank = src - src.proxyassembly = proxy - - -/obj/item/weapon/tank/Initialize() - . = ..() - - src.init_proxy() - src.air_contents = new /datum/gas_mixture() - src.air_contents.volume = volume //liters - src.air_contents.temperature = T20C - update_gauge() - -/obj/item/weapon/tank/Destroy() - QDEL_NULL(air_contents) - - STOP_PROCESSING(SSobj, src) - QDEL_NULL(src.proxyassembly) - - if(istype(loc, /obj/item/device/transfer_valve)) - var/obj/item/device/transfer_valve/TTV = loc - TTV.remove_tank(src) - - . = ..() - -/obj/item/weapon/tank/equipped() // Note that even grabbing into a hand calls this, so it should be fine as a 'has a player touched this' - . = ..() - // An attempt at optimization. There are MANY tanks during rounds that will never get touched. - // Don't see why any of those would explode spontaneously. So only tanks that players touch get processed. - // This could be optimized more, but it's a start! - START_PROCESSING(SSobj, src) // This has a built in safety to avoid multi-processing - -/obj/item/weapon/tank/examine(mob/user) - . = ..() - if(loc == user) - var/celsius_temperature = air_contents.temperature - T0C - var/descriptive - switch(celsius_temperature) - if(300 to INFINITY) - descriptive = "furiously hot" - if(100 to 300) - descriptive = "hot" - if(80 to 100) - descriptive = "warm" - if(40 to 80) - descriptive = "lukewarm" - if(20 to 40) - descriptive = "room temperature" - if(-20 to 20) - descriptive = "cold" - else - descriptive = "bitterly cold" - . += "\The [src] feels [descriptive]." - - if(src.proxyassembly.assembly || wired) - . += "It seems to have [wired? "some wires ": ""][wired && src.proxyassembly.assembly? "and ":""][src.proxyassembly.assembly ? "some sort of assembly ":""]attached to it." - if(src.valve_welded) - . += "\The [src] emergency relief valve has been welded shut!" - - -/obj/item/weapon/tank/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - if (istype(src.loc, /obj/item/assembly)) - icon = src.loc - - else if (istype(W,/obj/item/latexballon)) - var/obj/item/latexballon/LB = W - LB.blow(src) - src.add_fingerprint(user) - - if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = W - if(C.use(1)) - wired = 1 - to_chat(user, "You attach the wires to the tank.") - src.add_bomb_overlay() - - if(W.has_tool_quality(TOOL_WIRECUTTER)) - if(wired && src.proxyassembly.assembly) - - to_chat(user, "You carefully begin clipping the wires that attach to the tank.") - if(do_after(user, 100,src)) - wired = 0 - cut_overlay("bomb_assembly") - to_chat(user, "You cut the wire and remove the device.") - - var/obj/item/device/assembly_holder/assy = src.proxyassembly.assembly - if(assy.a_left && assy.a_right) - assy.dropInto(usr.loc) - assy.master = null - src.proxyassembly.assembly = null - else - if(!src.proxyassembly.assembly.a_left) - assy.a_right.dropInto(usr.loc) - assy.a_right.holder = null - assy.a_right = null - src.proxyassembly.assembly = null - qdel(assy) - cut_overlays() - last_gauge_pressure = 0 - update_gauge() - - else - to_chat(user, "You slip and bump the igniter!") - if(prob(85)) - src.proxyassembly.receive_signal() - - else if(wired) - if(do_after(user, 10, src)) - to_chat(user, "You quickly clip the wire from the tank.") - wired = 0 - cut_overlay("bomb_assembly") - - else - to_chat(user, "There are no wires to cut!") - - - - if(istype(W, /obj/item/device/assembly_holder)) - if(wired) - to_chat(user, "You begin attaching the assembly to \the [src].") - if(do_after(user, 50, src)) - to_chat(user, "You finish attaching the assembly to \the [src].") - bombers += "[key_name(user)] attached an assembly to a wired [src]. Temp: [src.air_contents.temperature-T0C]" - message_admins("[key_name_admin(user)] attached an assembly to a wired [src]. Temp: [src.air_contents.temperature-T0C]") - assemble_bomb(W,user) - else - to_chat(user, "You stop attaching the assembly.") - else - to_chat(user, "You need to wire the device up first.") - - - if(istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W - if(WT.remove_fuel(1,user)) - if(!valve_welded) - to_chat(user, "You begin welding the \the [src] emergency pressure relief valve.") - if(do_after(user, 40,src)) - to_chat(user, "You carefully weld \the [src] emergency pressure relief valve shut. \The [src] may now rupture under pressure!") - src.valve_welded = 1 - src.leaking = 0 - else - bombers += "[key_name(user)] attempted to weld a [src]. [src.air_contents.temperature-T0C]" - message_admins("[key_name_admin(user)] attempted to weld a [src]. [src.air_contents.temperature-T0C]") - if(WT.welding) - to_chat(user, "You accidentally rake \the [W] across \the [src]!") - maxintegrity -= rand(2,6) - integrity = min(integrity,maxintegrity) - src.air_contents.add_thermal_energy(rand(2000,50000)) - WT.eyecheck(user) - else - to_chat(user, "The emergency pressure relief valve has already been welded.") - add_fingerprint(user) - - - -/obj/item/weapon/tank/attack_self(mob/user as mob) - add_fingerprint(user) - if (!(src.air_contents)) - return - tgui_interact(user) - -// There's GOT to be a better way to do this - if (src.proxyassembly.assembly) - src.proxyassembly.assembly.attack_self(user) - -/obj/item/weapon/tank/tgui_state(mob/user) - return GLOB.tgui_deep_inventory_state - -/obj/item/weapon/tank/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Tank", name) - ui.open() - -/obj/item/weapon/tank/tgui_data(mob/user) - var/list/data = list() - data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0) - data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) - data["minReleasePressure"] = 0 - data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE) - - var/mob/living/carbon/C = user - if(!istype(C)) - C = loc.loc - if(!istype(C)) - return data - - if(C.internal == src) - data["connected"] = TRUE - else - data["connected"] = FALSE - - data["maskConnected"] = FALSE - if(C.wear_mask && (C.wear_mask.item_flags & AIRTIGHT)) - data["maskConnected"] = TRUE - else if(ishuman(C)) - var/mob/living/carbon/human/H = C - if(H.head && (H.head.item_flags & AIRTIGHT)) - data["maskConnected"] = TRUE - - return data - -/obj/item/weapon/tank/tgui_act(action, params) - if(..()) - return TRUE - switch(action) - if("pressure") - var/pressure = params["pressure"] - if(pressure == "reset") - pressure = TANK_DEFAULT_RELEASE_PRESSURE - . = TRUE - else if(pressure == "min") - pressure = 0 - . = TRUE - else if(pressure == "max") - pressure = TANK_MAX_RELEASE_PRESSURE - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - distribute_pressure = clamp(round(pressure), 0, TANK_MAX_RELEASE_PRESSURE) - if("toggle") - toggle_valve(usr) - . = TRUE - - add_fingerprint(usr) - -/obj/item/weapon/tank/proc/toggle_valve(var/mob/user) - if(istype(loc,/mob/living/carbon)) - var/mob/living/carbon/location = loc - if(location.internal == src) - location.internal = null - location.internals.icon_state = "internal0" - to_chat(user, "You close the tank release valve.") - if (location.internals) - location.internals.icon_state = "internal0" - else - var/can_open_valve - if(location.wear_mask && (location.wear_mask.item_flags & AIRTIGHT)) - can_open_valve = 1 - else if(istype(location,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = location - if(H.head && (H.head.item_flags & AIRTIGHT)) - can_open_valve = 1 - - if(can_open_valve) - location.internal = src - to_chat(user, "You open \the [src] valve.") - if (location.internals) - location.internals.icon_state = "internal1" - else - to_chat(user, "You need something to connect to \the [src].") - - - -/obj/item/weapon/tank/remove_air(amount) - return air_contents.remove(amount) - -/obj/item/weapon/tank/proc/remove_air_by_flag(flag, amount) - return air_contents.remove_by_flag(flag, amount) - -/obj/item/weapon/tank/return_air() - return air_contents - -/obj/item/weapon/tank/assume_air(datum/gas_mixture/giver) - air_contents.merge(giver) - - check_status() - return 1 - -/obj/item/weapon/tank/proc/remove_air_volume(volume_to_return) - if(!air_contents) - return null - - var/tank_pressure = air_contents.return_pressure() - if(tank_pressure < distribute_pressure) - distribute_pressure = tank_pressure - - var/moles_needed = distribute_pressure*volume_to_return/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - return remove_air(moles_needed) - -/obj/item/weapon/tank/process() - if(!air_contents) - return - //Allow for reactions - air_contents.react() //cooking up air tanks - add phoron and oxygen, then heat above PHORON_MINIMUM_BURN_TEMPERATURE - if(gauge_icon) - update_gauge() - check_status() - - -/obj/item/weapon/tank/proc/add_bomb_overlay() - if(src.wired) - add_overlay("bomb_assembly") - if(src.proxyassembly.assembly) - var/icon/test = getFlatIcon(src.proxyassembly.assembly) - test.Shift(SOUTH,1) - test.Shift(WEST,3) - add_overlay(test) - - -/obj/item/weapon/tank/proc/update_gauge() - var/gauge_pressure = 0 - if(air_contents) - gauge_pressure = air_contents.return_pressure() - if(gauge_pressure > TANK_IDEAL_PRESSURE) - gauge_pressure = -1 - else - gauge_pressure = round((gauge_pressure/TANK_IDEAL_PRESSURE)*gauge_cap) - - if(gauge_pressure == last_gauge_pressure) - return - - last_gauge_pressure = gauge_pressure - cut_overlays() - add_bomb_overlay() - var/indicator = "[gauge_icon][(gauge_pressure == -1) ? "overload" : gauge_pressure]" - if(!tank_gauge_cache[indicator]) - tank_gauge_cache[indicator] = image(icon, indicator) - add_overlay(tank_gauge_cache[indicator]) - - - - - - -/obj/item/weapon/tank/proc/check_status() - //Handle exploding, leaking, and rupturing of the tank - - if(!air_contents) - return 0 - - var/pressure = air_contents.return_pressure() - - - if(pressure > TANK_FRAGMENT_PRESSURE) - if(integrity <= 7) - if(!istype(src.loc,/obj/item/device/transfer_valve)) - message_admins("Explosive tank rupture! last key to touch the tank was [src.fingerprintslast].") - log_game("Explosive tank rupture! last key to touch the tank was [src.fingerprintslast].") - - //Give the gas a chance to build up more pressure through reacting - air_contents.react() - air_contents.react() - air_contents.react() - - pressure = air_contents.return_pressure() - var/strength = ((pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE) - - var/mult = ((src.air_contents.volume/140)**(1/2)) * (air_contents.total_moles**(2/3))/((29*0.64) **(2/3)) //tanks appear to be experiencing a reduction on scale of about 0.64 total moles - //tanks appear to be experiencing a reduction on scale of about 0.64 total moles - - - - var/turf/simulated/T = get_turf(src) - T.hotspot_expose(src.air_contents.temperature, 70, 1) - if(!T) - return - - T.assume_air(air_contents) - explosion( - get_turf(loc), - round(min(BOMBCAP_DVSTN_RADIUS, ((mult)*strength)*0.15)), - round(min(BOMBCAP_HEAVY_RADIUS, ((mult)*strength)*0.35)), - round(min(BOMBCAP_LIGHT_RADIUS, ((mult)*strength)*0.80)), - round(min(BOMBCAP_FLASH_RADIUS, ((mult)*strength)*1.20)), - ) - - - var/num_fragments = round(rand(8,10) * sqrt(strength * mult)) - src.fragmentate(T, num_fragments, rand(5) + 7, list(/obj/item/projectile/bullet/pellet/fragment/tank/small = 7,/obj/item/projectile/bullet/pellet/fragment/tank = 2,/obj/item/projectile/bullet/pellet/fragment/strong = 1)) - - if(istype(loc, /obj/item/device/transfer_valve)) - var/obj/item/device/transfer_valve/TTV = loc - TTV.remove_tank(src) - qdel(TTV) - - - if(src) - qdel(src) - - else - integrity -=7 - - - else if(pressure > TANK_RUPTURE_PRESSURE) - #ifdef FIREDBG - log_debug("[x],[y] tank is rupturing: [pressure] kPa, integrity [integrity]") - #endif - - air_contents.react() - - if(integrity <= 0) - var/turf/simulated/T = get_turf(src) - if(!T) - return - T.assume_air(air_contents) - playsound(src, 'sound/weapons/Gunshot_shotgun.ogg', 20, 1) - visible_message("\icon[src][bicon(src)] \The [src] flies apart!", "You hear a bang!") - T.hotspot_expose(air_contents.temperature, 70, 1) - - - var/strength = 1+((pressure-TANK_LEAK_PRESSURE)/TANK_FRAGMENT_SCALE) - - var/mult = (air_contents.total_moles**2/3)/((29*0.64) **2/3) //tanks appear to be experiencing a reduction on scale of about 0.64 total moles - - var/num_fragments = round(rand(6,8) * sqrt(strength * mult)) //Less chunks, but bigger - src.fragmentate(T, num_fragments, 7, list(/obj/item/projectile/bullet/pellet/fragment/tank/small = 1,/obj/item/projectile/bullet/pellet/fragment/tank = 5,/obj/item/projectile/bullet/pellet/fragment/strong = 4)) - - if(istype(loc, /obj/item/device/transfer_valve)) - var/obj/item/device/transfer_valve/TTV = loc - TTV.remove_tank(src) - - - qdel(src) - - else - if(!valve_welded) - integrity-= 3 - src.leaking = 1 - else - integrity-= 5 - - - else if(pressure > TANK_LEAK_PRESSURE || air_contents.temperature - T0C > failure_temp) - - if((integrity <= 17 || src.leaking) && !valve_welded) - var/turf/simulated/T = get_turf(src) - if(!T) - return - var/datum/gas_mixture/environment = loc.return_air() - var/env_pressure = environment.return_pressure() - var/tank_pressure = src.air_contents.return_pressure() - - var/release_ratio = 0.002 - if(tank_pressure) - release_ratio = CLAMP(sqrt(max(tank_pressure-env_pressure,0)/tank_pressure), 0.002, 1) - - var/datum/gas_mixture/leaked_gas = air_contents.remove_ratio(release_ratio) - //dynamic air release based on ambient pressure - - T.assume_air(leaked_gas) - if(!leaking) - visible_message("\icon[src][bicon(src)] \The [src] relief valve flips open with a hiss!", "You hear hissing.") - playsound(src, 'sound/effects/spray.ogg', 10, 1, -3) - leaking = 1 - #ifdef FIREDBG - log_debug("[x],[y] tank is leaking: [pressure] kPa, integrity [integrity]") - #endif - - - else - integrity-= 1 - - - else - if(integrity < maxintegrity) - integrity++ - if(leaking) - integrity++ - if(integrity == maxintegrity) - leaking = 0 - -///////////////////////////////// -///Prewelded tanks -///////////////////////////////// - -/obj/item/weapon/tank/phoron/welded - valve_welded = 1 -/obj/item/weapon/tank/oxygen/welded - valve_welded = 1 - - -///////////////////////////////// -///Onetankbombs (added as actual items) -///////////////////////////////// - -/obj/item/weapon/tank/proc/onetankbomb(var/fill = 1) - var/phoron_amt = 4 + rand(4) - var/oxygen_amt = 6 + rand(8) - - if(fill == 2) - phoron_amt = 10 - oxygen_amt = 15 - else if (!fill) - phoron_amt = 3 - oxygen_amt = 4.5 - - - src.air_contents.gas["phoron"] = phoron_amt - src.air_contents.gas["oxygen"] = oxygen_amt - src.air_contents.update_values() - src.valve_welded = 1 - src.air_contents.temperature = PHORON_MINIMUM_BURN_TEMPERATURE-1 - - src.wired = 1 - - var/obj/item/device/assembly_holder/H = new(src) - src.proxyassembly.assembly = H - H.master = src.proxyassembly - - H.update_icon() - - add_overlay("bomb_assembly") - - -/obj/item/weapon/tank/phoron/onetankbomb/New() - ..() - src.onetankbomb() - -/obj/item/weapon/tank/oxygen/onetankbomb/New() - ..() - src.onetankbomb() - - -/obj/item/weapon/tank/phoron/onetankbomb/full/New() - ..() - src.onetankbomb(2) - -/obj/item/weapon/tank/oxygen/onetankbomb/full/New() - ..() - src.onetankbomb(2) - -/obj/item/weapon/tank/phoron/onetankbomb/small/New() - ..() - src.onetankbomb(0) - -/obj/item/weapon/tank/oxygen/onetankbomb/small/New() - ..() - src.onetankbomb(0) - -///////////////////////////////// -///Pulled from rewritten bomb.dm -///////////////////////////////// - -/obj/item/device/tankassemblyproxy - name = "Tank assembly proxy" - desc = "Used as a stand in to trigger single tank assemblies... but you shouldn't see this." - var/obj/item/weapon/tank/tank = null - var/obj/item/device/assembly_holder/assembly = null - - -/obj/item/device/tankassemblyproxy/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here. - tank.ignite() //boom (or not boom if you made shijwtty mix) - -/obj/item/device/tankassemblyproxy/Destroy() - . = ..() - tank = null - assembly = null - -/obj/item/weapon/tank/proc/assemble_bomb(W,user) //Bomb assembly proc. This turns assembly+tank into a bomb - var/obj/item/device/assembly_holder/S = W - var/mob/M = user - if(!S.secured) //Check if the assembly is secured - return - if(isigniter(S.a_left) == isigniter(S.a_right)) //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it - return - - - M.drop_item() //Remove the assembly from your hands - M.remove_from_mob(src) //Remove the tank from your character,in case you were holding it - M.put_in_hands(src) //Equips the bomb if possible, or puts it on the floor. - - src.proxyassembly.assembly = S //Tell the bomb about its assembly part - S.master = src.proxyassembly //Tell the assembly about its new owner - S.forceMove(src) //Move the assembly - - src.update_icon() - - - src.add_bomb_overlay() - - return - - -/obj/item/weapon/tank/proc/ignite() //This happens when a bomb is told to explode - - var/obj/item/device/assembly_holder/assy = src.proxyassembly.assembly - var/ign = assy.a_right - var/obj/item/other = assy.a_left - - if (isigniter(assy.a_left)) - ign = assy.a_left - other = assy.a_right - - other.dropInto(get_turf(src)) - qdel(ign) - assy.master = null - src.proxyassembly.assembly = null - qdel(assy) - src.update_icon() - src.update_gauge() - - air_contents.add_thermal_energy(15000) - - -/obj/item/device/tankassemblyproxy/update_icon() - if(assembly) - tank.update_icon() - tank.add_overlay("bomb_assembly") - else - tank.update_icon() - tank.cut_overlay("bomb_assembly") - -/obj/item/device/tankassemblyproxy/HasProximity(turf/T, atom/movable/AM, old_loc) - assembly?.HasProximity(T, AM, old_loc) - -/obj/item/device/tankassemblyproxy/Moved(old_loc, direction, forced) - if(isturf(old_loc)) - unsense_proximity(callback = /atom/proc/HasProximity, center = old_loc) - if(isturf(loc)) - sense_proximity(callback = /atom/proc/HasProximity) +#define TANK_IDEAL_PRESSURE 1015 //Arbitrary. + +var/list/global/tank_gauge_cache = list() + +/obj/item/weapon/tank + name = "tank" + icon = 'icons/obj/tank.dmi' + sprite_sheets = list( + SPECIES_TESHARI = 'icons/inventory/back/mob_teshari.dmi' + ) + drop_sound = 'sound/items/drop/gascan.ogg' + pickup_sound = 'sound/items/pickup/gascan.ogg' + + var/gauge_icon = "indicator_tank" + var/last_gauge_pressure + var/gauge_cap = 6 + + slot_flags = SLOT_BACK + w_class = ITEMSIZE_NORMAL + + force = 5.0 + throwforce = 10.0 + throw_speed = 1 + throw_range = 4 + + var/datum/gas_mixture/air_contents = null + var/distribute_pressure = ONE_ATMOSPHERE + var/integrity = 20 + var/maxintegrity = 20 + var/valve_welded = 0 + var/obj/item/device/tankassemblyproxy/proxyassembly + + var/volume = 70 + var/manipulated_by = null //Used by _onclick/hud/screen_objects.dm internals to determine if someone has messed with our tank or not. + //If they have and we haven't scanned it with the PDA or gas analyzer then we might just breath whatever they put in it. + + var/failure_temp = 173 //173 deg C Borate seal (yes it should be 153 F, but that's annoying) + var/leaking = 0 + var/wired = 0 + + description_info = "These tanks are utilised to store any of the various types of gaseous substances. \ + They can be attached to various portable atmospheric devices to be filled or emptied.
                    \ +
                    \ + Each tank is fitted with an emergency relief valve. This relief valve will open if the tank is pressurised to over ~3000kPa or heated to over 173ºC. \ + The valve itself will close after expending most or all of the contents into the air.
                    \ +
                    \ + Filling a tank such that experiences ~4000kPa of pressure will cause the tank to rupture, spilling out its contents and destroying the tank. \ + Tanks filled over ~5000kPa will rupture rather violently, exploding with significant force." + + description_antag = "Each tank may be incited to burn by attaching wires and an igniter assembly, though the igniter can only be used once and the mixture only burn if the igniter pushes a flammable gas mixture above the minimum burn temperature (126ºC). \ + Wired and assembled tanks may be disarmed with a set of wirecutters. Any exploding or rupturing tank will generate shrapnel, assuming their relief valves have been welded beforehand. Even if not, they can be incited to expel hot gas on ignition if pushed above 173ºC. \ + Relatively easy to make, the single tank bomb requries no tank transfer valve, and is still a fairly formidable weapon that can be manufactured from any tank." + +/obj/item/weapon/tank/proc/init_proxy() + var/obj/item/device/tankassemblyproxy/proxy = new /obj/item/device/tankassemblyproxy(src) + proxy.tank = src + src.proxyassembly = proxy + + +/obj/item/weapon/tank/Initialize() + . = ..() + + src.init_proxy() + src.air_contents = new /datum/gas_mixture() + src.air_contents.volume = volume //liters + src.air_contents.temperature = T20C + update_gauge() + +/obj/item/weapon/tank/Destroy() + QDEL_NULL(air_contents) + + STOP_PROCESSING(SSobj, src) + QDEL_NULL(src.proxyassembly) + + if(istype(loc, /obj/item/device/transfer_valve)) + var/obj/item/device/transfer_valve/TTV = loc + TTV.remove_tank(src) + + . = ..() + +/obj/item/weapon/tank/equipped() // Note that even grabbing into a hand calls this, so it should be fine as a 'has a player touched this' + . = ..() + // An attempt at optimization. There are MANY tanks during rounds that will never get touched. + // Don't see why any of those would explode spontaneously. So only tanks that players touch get processed. + // This could be optimized more, but it's a start! + START_PROCESSING(SSobj, src) // This has a built in safety to avoid multi-processing + +/obj/item/weapon/tank/examine(mob/user) + . = ..() + if(loc == user) + var/celsius_temperature = air_contents.temperature - T0C + var/descriptive + switch(celsius_temperature) + if(300 to INFINITY) + descriptive = "furiously hot" + if(100 to 300) + descriptive = "hot" + if(80 to 100) + descriptive = "warm" + if(40 to 80) + descriptive = "lukewarm" + if(20 to 40) + descriptive = "room temperature" + if(-20 to 20) + descriptive = "cold" + else + descriptive = "bitterly cold" + . += "\The [src] feels [descriptive]." + + if(src.proxyassembly.assembly || wired) + . += "It seems to have [wired? "some wires ": ""][wired && src.proxyassembly.assembly? "and ":""][src.proxyassembly.assembly ? "some sort of assembly ":""]attached to it." + if(src.valve_welded) + . += "\The [src] emergency relief valve has been welded shut!" + + +/obj/item/weapon/tank/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + if (istype(src.loc, /obj/item/assembly)) + icon = src.loc + + else if (istype(W,/obj/item/latexballon)) + var/obj/item/latexballon/LB = W + LB.blow(src) + src.add_fingerprint(user) + + if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = W + if(C.use(1)) + wired = 1 + to_chat(user, "You attach the wires to the tank.") + src.add_bomb_overlay() + + if(W.has_tool_quality(TOOL_WIRECUTTER)) + if(wired && src.proxyassembly.assembly) + + to_chat(user, "You carefully begin clipping the wires that attach to the tank.") + if(do_after(user, 100,src)) + wired = 0 + cut_overlay("bomb_assembly") + to_chat(user, "You cut the wire and remove the device.") + + var/obj/item/device/assembly_holder/assy = src.proxyassembly.assembly + if(assy.a_left && assy.a_right) + assy.dropInto(usr.loc) + assy.master = null + src.proxyassembly.assembly = null + else + if(!src.proxyassembly.assembly.a_left) + assy.a_right.dropInto(usr.loc) + assy.a_right.holder = null + assy.a_right = null + src.proxyassembly.assembly = null + qdel(assy) + cut_overlays() + last_gauge_pressure = 0 + update_gauge() + + else + to_chat(user, "You slip and bump the igniter!") + if(prob(85)) + src.proxyassembly.receive_signal() + + else if(wired) + if(do_after(user, 10, src)) + to_chat(user, "You quickly clip the wire from the tank.") + wired = 0 + cut_overlay("bomb_assembly") + + else + to_chat(user, "There are no wires to cut!") + + + + if(istype(W, /obj/item/device/assembly_holder)) + if(wired) + to_chat(user, "You begin attaching the assembly to \the [src].") + if(do_after(user, 50, src)) + to_chat(user, "You finish attaching the assembly to \the [src].") + bombers += "[key_name(user)] attached an assembly to a wired [src]. Temp: [src.air_contents.temperature-T0C]" + message_admins("[key_name_admin(user)] attached an assembly to a wired [src]. Temp: [src.air_contents.temperature-T0C]") + assemble_bomb(W,user) + else + to_chat(user, "You stop attaching the assembly.") + else + to_chat(user, "You need to wire the device up first.") + + + if(istype(W, /obj/item/weapon/weldingtool)) + var/obj/item/weapon/weldingtool/WT = W + if(WT.remove_fuel(1,user)) + if(!valve_welded) + to_chat(user, "You begin welding the \the [src] emergency pressure relief valve.") + if(do_after(user, 40,src)) + to_chat(user, "You carefully weld \the [src] emergency pressure relief valve shut. \The [src] may now rupture under pressure!") + src.valve_welded = 1 + src.leaking = 0 + else + bombers += "[key_name(user)] attempted to weld a [src]. [src.air_contents.temperature-T0C]" + message_admins("[key_name_admin(user)] attempted to weld a [src]. [src.air_contents.temperature-T0C]") + if(WT.welding) + to_chat(user, "You accidentally rake \the [W] across \the [src]!") + maxintegrity -= rand(2,6) + integrity = min(integrity,maxintegrity) + src.air_contents.add_thermal_energy(rand(2000,50000)) + WT.eyecheck(user) + else + to_chat(user, "The emergency pressure relief valve has already been welded.") + add_fingerprint(user) + + + +/obj/item/weapon/tank/attack_self(mob/user as mob) + add_fingerprint(user) + if (!(src.air_contents)) + return + tgui_interact(user) + +// There's GOT to be a better way to do this + if (src.proxyassembly.assembly) + src.proxyassembly.assembly.attack_self(user) + +/obj/item/weapon/tank/tgui_state(mob/user) + return GLOB.tgui_deep_inventory_state + +/obj/item/weapon/tank/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Tank", name) + ui.open() + +/obj/item/weapon/tank/tgui_data(mob/user) + var/list/data = list() + data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) + data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0) + data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) + data["minReleasePressure"] = 0 + data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE) + + var/mob/living/carbon/C = user + if(!istype(C)) + C = loc.loc + if(!istype(C)) + return data + + if(C.internal == src) + data["connected"] = TRUE + else + data["connected"] = FALSE + + data["maskConnected"] = FALSE + if(C.wear_mask && (C.wear_mask.item_flags & AIRTIGHT)) + data["maskConnected"] = TRUE + else if(ishuman(C)) + var/mob/living/carbon/human/H = C + if(H.head && (H.head.item_flags & AIRTIGHT)) + data["maskConnected"] = TRUE + + return data + +/obj/item/weapon/tank/tgui_act(action, params) + if(..()) + return TRUE + switch(action) + if("pressure") + var/pressure = params["pressure"] + if(pressure == "reset") + pressure = TANK_DEFAULT_RELEASE_PRESSURE + . = TRUE + else if(pressure == "min") + pressure = 0 + . = TRUE + else if(pressure == "max") + pressure = TANK_MAX_RELEASE_PRESSURE + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + distribute_pressure = clamp(round(pressure), 0, TANK_MAX_RELEASE_PRESSURE) + if("toggle") + toggle_valve(usr) + . = TRUE + + add_fingerprint(usr) + +/obj/item/weapon/tank/proc/toggle_valve(var/mob/user) + if(istype(loc,/mob/living/carbon)) + var/mob/living/carbon/location = loc + if(location.internal == src) + location.internal = null + location.internals.icon_state = "internal0" + to_chat(user, "You close the tank release valve.") + if (location.internals) + location.internals.icon_state = "internal0" + else + var/can_open_valve + if(location.wear_mask && (location.wear_mask.item_flags & AIRTIGHT)) + can_open_valve = 1 + else if(istype(location,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = location + if(H.head && (H.head.item_flags & AIRTIGHT)) + can_open_valve = 1 + + if(can_open_valve) + location.internal = src + to_chat(user, "You open \the [src] valve.") + if (location.internals) + location.internals.icon_state = "internal1" + else + to_chat(user, "You need something to connect to \the [src].") + + + +/obj/item/weapon/tank/remove_air(amount) + return air_contents.remove(amount) + +/obj/item/weapon/tank/proc/remove_air_by_flag(flag, amount) + return air_contents.remove_by_flag(flag, amount) + +/obj/item/weapon/tank/return_air() + return air_contents + +/obj/item/weapon/tank/assume_air(datum/gas_mixture/giver) + air_contents.merge(giver) + + check_status() + return 1 + +/obj/item/weapon/tank/proc/remove_air_volume(volume_to_return) + if(!air_contents) + return null + + var/tank_pressure = air_contents.return_pressure() + if(tank_pressure < distribute_pressure) + distribute_pressure = tank_pressure + + var/moles_needed = distribute_pressure*volume_to_return/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + return remove_air(moles_needed) + +/obj/item/weapon/tank/process() + if(!air_contents) + return + //Allow for reactions + air_contents.react() //cooking up air tanks - add phoron and oxygen, then heat above PHORON_MINIMUM_BURN_TEMPERATURE + if(gauge_icon) + update_gauge() + check_status() + + +/obj/item/weapon/tank/proc/add_bomb_overlay() + if(src.wired) + add_overlay("bomb_assembly") + if(src.proxyassembly.assembly) + var/icon/test = getFlatIcon(src.proxyassembly.assembly) + test.Shift(SOUTH,1) + test.Shift(WEST,3) + add_overlay(test) + + +/obj/item/weapon/tank/proc/update_gauge() + var/gauge_pressure = 0 + if(air_contents) + gauge_pressure = air_contents.return_pressure() + if(gauge_pressure > TANK_IDEAL_PRESSURE) + gauge_pressure = -1 + else + gauge_pressure = round((gauge_pressure/TANK_IDEAL_PRESSURE)*gauge_cap) + + if(gauge_pressure == last_gauge_pressure) + return + + last_gauge_pressure = gauge_pressure + cut_overlays() + add_bomb_overlay() + var/indicator = "[gauge_icon][(gauge_pressure == -1) ? "overload" : gauge_pressure]" + if(!tank_gauge_cache[indicator]) + tank_gauge_cache[indicator] = image(icon, indicator) + add_overlay(tank_gauge_cache[indicator]) + + + + + + +/obj/item/weapon/tank/proc/check_status() + //Handle exploding, leaking, and rupturing of the tank + + if(!air_contents) + return 0 + + var/pressure = air_contents.return_pressure() + + + if(pressure > TANK_FRAGMENT_PRESSURE) + if(integrity <= 7) + if(!istype(src.loc,/obj/item/device/transfer_valve)) + message_admins("Explosive tank rupture! last key to touch the tank was [src.fingerprintslast].") + log_game("Explosive tank rupture! last key to touch the tank was [src.fingerprintslast].") + + //Give the gas a chance to build up more pressure through reacting + air_contents.react() + air_contents.react() + air_contents.react() + + pressure = air_contents.return_pressure() + var/strength = ((pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE) + + var/mult = ((src.air_contents.volume/140)**(1/2)) * (air_contents.total_moles**(2/3))/((29*0.64) **(2/3)) //tanks appear to be experiencing a reduction on scale of about 0.64 total moles + //tanks appear to be experiencing a reduction on scale of about 0.64 total moles + + + + var/turf/simulated/T = get_turf(src) + T.hotspot_expose(src.air_contents.temperature, 70, 1) + if(!T) + return + + T.assume_air(air_contents) + explosion( + get_turf(loc), + round(min(BOMBCAP_DVSTN_RADIUS, ((mult)*strength)*0.15)), + round(min(BOMBCAP_HEAVY_RADIUS, ((mult)*strength)*0.35)), + round(min(BOMBCAP_LIGHT_RADIUS, ((mult)*strength)*0.80)), + round(min(BOMBCAP_FLASH_RADIUS, ((mult)*strength)*1.20)), + ) + + + var/num_fragments = round(rand(8,10) * sqrt(strength * mult)) + src.fragmentate(T, num_fragments, rand(5) + 7, list(/obj/item/projectile/bullet/pellet/fragment/tank/small = 7,/obj/item/projectile/bullet/pellet/fragment/tank = 2,/obj/item/projectile/bullet/pellet/fragment/strong = 1)) + + if(istype(loc, /obj/item/device/transfer_valve)) + var/obj/item/device/transfer_valve/TTV = loc + TTV.remove_tank(src) + qdel(TTV) + + + if(src) + qdel(src) + + else + integrity -=7 + + + else if(pressure > TANK_RUPTURE_PRESSURE) + #ifdef FIREDBG + log_debug("[x],[y] tank is rupturing: [pressure] kPa, integrity [integrity]") + #endif + + air_contents.react() + + if(integrity <= 0) + var/turf/simulated/T = get_turf(src) + if(!T) + return + T.assume_air(air_contents) + playsound(src, 'sound/weapons/Gunshot_shotgun.ogg', 20, 1) + visible_message("\icon[src][bicon(src)] \The [src] flies apart!", "You hear a bang!") + T.hotspot_expose(air_contents.temperature, 70, 1) + + + var/strength = 1+((pressure-TANK_LEAK_PRESSURE)/TANK_FRAGMENT_SCALE) + + var/mult = (air_contents.total_moles**2/3)/((29*0.64) **2/3) //tanks appear to be experiencing a reduction on scale of about 0.64 total moles + + var/num_fragments = round(rand(6,8) * sqrt(strength * mult)) //Less chunks, but bigger + src.fragmentate(T, num_fragments, 7, list(/obj/item/projectile/bullet/pellet/fragment/tank/small = 1,/obj/item/projectile/bullet/pellet/fragment/tank = 5,/obj/item/projectile/bullet/pellet/fragment/strong = 4)) + + if(istype(loc, /obj/item/device/transfer_valve)) + var/obj/item/device/transfer_valve/TTV = loc + TTV.remove_tank(src) + + + qdel(src) + + else + if(!valve_welded) + integrity-= 3 + src.leaking = 1 + else + integrity-= 5 + + + else if(pressure > TANK_LEAK_PRESSURE || air_contents.temperature - T0C > failure_temp) + + if((integrity <= 17 || src.leaking) && !valve_welded) + var/turf/simulated/T = get_turf(src) + if(!T) + return + var/datum/gas_mixture/environment = loc.return_air() + var/env_pressure = environment.return_pressure() + var/tank_pressure = src.air_contents.return_pressure() + + var/release_ratio = 0.002 + if(tank_pressure) + release_ratio = CLAMP(sqrt(max(tank_pressure-env_pressure,0)/tank_pressure), 0.002, 1) + + var/datum/gas_mixture/leaked_gas = air_contents.remove_ratio(release_ratio) + //dynamic air release based on ambient pressure + + T.assume_air(leaked_gas) + if(!leaking) + visible_message("\icon[src][bicon(src)] \The [src] relief valve flips open with a hiss!", "You hear hissing.") + playsound(src, 'sound/effects/spray.ogg', 10, 1, -3) + leaking = 1 + #ifdef FIREDBG + log_debug("[x],[y] tank is leaking: [pressure] kPa, integrity [integrity]") + #endif + + + else + integrity-= 1 + + + else + if(integrity < maxintegrity) + integrity++ + if(leaking) + integrity++ + if(integrity == maxintegrity) + leaking = 0 + +///////////////////////////////// +///Prewelded tanks +///////////////////////////////// + +/obj/item/weapon/tank/phoron/welded + valve_welded = 1 +/obj/item/weapon/tank/oxygen/welded + valve_welded = 1 + + +///////////////////////////////// +///Onetankbombs (added as actual items) +///////////////////////////////// + +/obj/item/weapon/tank/proc/onetankbomb(var/fill = 1) + var/phoron_amt = 4 + rand(4) + var/oxygen_amt = 6 + rand(8) + + if(fill == 2) + phoron_amt = 10 + oxygen_amt = 15 + else if (!fill) + phoron_amt = 3 + oxygen_amt = 4.5 + + + src.air_contents.gas["phoron"] = phoron_amt + src.air_contents.gas["oxygen"] = oxygen_amt + src.air_contents.update_values() + src.valve_welded = 1 + src.air_contents.temperature = PHORON_MINIMUM_BURN_TEMPERATURE-1 + + src.wired = 1 + + var/obj/item/device/assembly_holder/H = new(src) + src.proxyassembly.assembly = H + H.master = src.proxyassembly + + H.update_icon() + + add_overlay("bomb_assembly") + + +/obj/item/weapon/tank/phoron/onetankbomb/New() + ..() + src.onetankbomb() + +/obj/item/weapon/tank/oxygen/onetankbomb/New() + ..() + src.onetankbomb() + + +/obj/item/weapon/tank/phoron/onetankbomb/full/New() + ..() + src.onetankbomb(2) + +/obj/item/weapon/tank/oxygen/onetankbomb/full/New() + ..() + src.onetankbomb(2) + +/obj/item/weapon/tank/phoron/onetankbomb/small/New() + ..() + src.onetankbomb(0) + +/obj/item/weapon/tank/oxygen/onetankbomb/small/New() + ..() + src.onetankbomb(0) + +///////////////////////////////// +///Pulled from rewritten bomb.dm +///////////////////////////////// + +/obj/item/device/tankassemblyproxy + name = "Tank assembly proxy" + desc = "Used as a stand in to trigger single tank assemblies... but you shouldn't see this." + var/obj/item/weapon/tank/tank = null + var/obj/item/device/assembly_holder/assembly = null + + +/obj/item/device/tankassemblyproxy/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here. + tank.ignite() //boom (or not boom if you made shijwtty mix) + +/obj/item/device/tankassemblyproxy/Destroy() + . = ..() + tank = null + assembly = null + +/obj/item/weapon/tank/proc/assemble_bomb(W,user) //Bomb assembly proc. This turns assembly+tank into a bomb + var/obj/item/device/assembly_holder/S = W + var/mob/M = user + if(!S.secured) //Check if the assembly is secured + return + if(isigniter(S.a_left) == isigniter(S.a_right)) //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it + return + + + M.drop_item() //Remove the assembly from your hands + M.remove_from_mob(src) //Remove the tank from your character,in case you were holding it + M.put_in_hands(src) //Equips the bomb if possible, or puts it on the floor. + + src.proxyassembly.assembly = S //Tell the bomb about its assembly part + S.master = src.proxyassembly //Tell the assembly about its new owner + S.forceMove(src) //Move the assembly + + src.update_icon() + + + src.add_bomb_overlay() + + return + + +/obj/item/weapon/tank/proc/ignite() //This happens when a bomb is told to explode + + var/obj/item/device/assembly_holder/assy = src.proxyassembly.assembly + var/ign = assy.a_right + var/obj/item/other = assy.a_left + + if (isigniter(assy.a_left)) + ign = assy.a_left + other = assy.a_right + + other.dropInto(get_turf(src)) + qdel(ign) + assy.master = null + src.proxyassembly.assembly = null + qdel(assy) + src.update_icon() + src.update_gauge() + + air_contents.add_thermal_energy(15000) + + +/obj/item/device/tankassemblyproxy/update_icon() + if(assembly) + tank.update_icon() + tank.add_overlay("bomb_assembly") + else + tank.update_icon() + tank.cut_overlay("bomb_assembly") + +/obj/item/device/tankassemblyproxy/HasProximity(turf/T, atom/movable/AM, old_loc) + assembly?.HasProximity(T, AM, old_loc) + +/obj/item/device/tankassemblyproxy/Moved(old_loc, direction, forced) + if(isturf(old_loc)) + unsense_proximity(callback = /atom/proc/HasProximity, center = old_loc) + if(isturf(loc)) + sense_proximity(callback = /atom/proc/HasProximity) diff --git a/code/game/objects/items/weapons/teleportation.dm b/code/game/objects/items/weapons/teleportation.dm index 5d9efa2ad72..d791a11d2f0 100644 --- a/code/game/objects/items/weapons/teleportation.dm +++ b/code/game/objects/items/weapons/teleportation.dm @@ -1,181 +1,181 @@ -/* Teleportation devices. - * Contains: - * Locator - * Hand-tele - */ - -/* - * Locator - */ -/obj/item/weapon/locator - name = "locator" - desc = "Used to track those with locater implants." - icon = 'icons/obj/device.dmi' - icon_state = "locator" - var/temp = null - var/frequency = 1451 - var/broadcasting = null - var/listening = 1.0 - w_class = ITEMSIZE_SMALL - item_state = "electronic" - throw_speed = 4 - throw_range = 20 - origin_tech = list(TECH_MAGNET = 1) - matter = list(MAT_STEEL = 400) - -/obj/item/weapon/locator/attack_self(mob/user as mob) - user.set_machine(src) - var/dat - if (src.temp) - dat = "[src.temp]

                    Clear" - else - dat = {" -Persistent Signal Locator
                    -Frequency: -- -- [format_frequency(src.frequency)] -+ -+
                    - -Refresh"} - user << browse(dat, "window=radio") - onclose(user, "radio") - return - -/obj/item/weapon/locator/Topic(href, href_list) - ..() - if (usr.stat || usr.restrained()) - return - var/turf/current_location = get_turf(usr)//What turf is the user on? - if(!current_location||current_location.z==3)//If turf was not found or they're on z level 2. - to_chat(usr, "The [src] is malfunctioning.") - return - if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf)))) - usr.set_machine(src) - if (href_list["refresh"]) - src.temp = "Persistent Signal Locator
                    " - var/turf/sr = get_turf(src) - - if (sr) - src.temp += "Located Beacons:
                    " - - for(var/obj/item/device/radio/beacon/W in GLOB.all_beacons) - if (W.frequency == src.frequency) - var/turf/tr = get_turf(W) - if (tr.z == sr.z && tr) - var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) - if (direct < 5) - direct = "very strong" - else - if (direct < 10) - direct = "strong" - else - if (direct < 20) - direct = "weak" - else - direct = "very weak" - src.temp += "[W.code]-[dir2text(get_dir(sr, tr))]-[direct]
                    " - - src.temp += "Extraneous Signals:
                    " - for (var/obj/item/weapon/implant/tracking/W in GLOB.all_tracking_implants) - if (!W.implanted || !(istype(W.loc,/obj/item/organ/external) || ismob(W.loc) || W.malfunction)) - continue - - var/turf/tr = get_turf(W) - if (tr.z == sr.z && tr) - var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) - if (direct < 20) - if (direct < 5) - direct = "very strong" - else - if (direct < 10) - direct = "strong" - else - direct = "weak" - src.temp += "[W.id]-[dir2text(get_dir(sr, tr))]-[direct]
                    " - - src.temp += "You are at \[[sr.x],[sr.y],[sr.z]\] in orbital coordinates.

                    Refresh
                    " - else - src.temp += "Processing Error: Unable to locate orbital position.
                    " - else - if (href_list["freq"]) - src.frequency += text2num(href_list["freq"]) - src.frequency = sanitize_frequency(src.frequency) - else - if (href_list["temp"]) - src.temp = null - if (istype(src.loc, /mob)) - attack_self(src.loc) - else - for(var/mob/M in viewers(1, src)) - if (M.client) - src.attack_self(M) - return - - -/* - * Hand-tele - */ -/obj/item/weapon/hand_tele - name = "hand tele" - desc = "A portable item using blue-space technology." - icon = 'icons/obj/device.dmi' - icon_state = "hand_tele" - item_state = "electronic" - throwforce = 5 - w_class = ITEMSIZE_SMALL - throw_speed = 3 - throw_range = 5 - origin_tech = list(TECH_MAGNET = 1, TECH_BLUESPACE = 3) - matter = list(MAT_STEEL = 10000) - preserve_item = 1 - -/obj/item/weapon/hand_tele/attack_self(mob/user as mob) - var/turf/current_location = get_turf(user)//What turf is the user on? - if(!current_location || (current_location.z in using_map.admin_levels) || current_location.block_tele)//If turf was not found or they're on z level 2 or >7 which does not currently exist. - to_chat(user, "\The [src] is malfunctioning.") - return - var/list/L = list( ) - for(var/obj/machinery/teleport/hub/R in machines) - var/obj/machinery/computer/teleporter/com - var/obj/machinery/teleport/station/station - for(var/direction in cardinal) - station = locate(/obj/machinery/teleport/station, get_step(R, direction)) - if(station) - for(direction in cardinal) - com = locate(/obj/machinery/computer/teleporter, get_step(station, direction)) - if(com) - break - break - if (istype(com, /obj/machinery/computer/teleporter) && com.teleport_control.locked && !com.one_time_use) - if(R.icon_state == "tele1") - L["[com.id] (Active)"] = com.teleport_control.locked - else - L["[com.id] (Inactive)"] = com.teleport_control.locked - var/list/turfs = list( ) - for(var/turf/T in orange(10)) - if(T.x>world.maxx-8 || T.x<8) continue //putting them at the edge is dumb - if(T.y>world.maxy-8 || T.y<8) continue - if(T.block_tele) continue - turfs += T - if(turfs.len) - L["None (Dangerous)"] = pick(turfs) - var/t1 = tgui_input_list(user, "Please select a teleporter to lock in on.", "Hand Teleporter", L) - if(!t1) - return - if ((user.get_active_hand() != src || user.stat || user.restrained())) - return - var/count = 0 //num of portals from this teleport in world - for(var/obj/effect/portal/PO in GLOB.all_portals) - if(PO.creator == src) count++ - if(count >= 3) - user.show_message("\The [src] is recharging!") - return - var/T = L[t1] - for(var/mob/O in hearers(user, null)) - O.show_message("Locked In.", 2) - var/obj/effect/portal/P = new /obj/effect/portal( get_turf(src) ) - P.target = T - P.creator = src - src.add_fingerprint(user) - return +/* Teleportation devices. + * Contains: + * Locator + * Hand-tele + */ + +/* + * Locator + */ +/obj/item/weapon/locator + name = "locator" + desc = "Used to track those with locater implants." + icon = 'icons/obj/device.dmi' + icon_state = "locator" + var/temp = null + var/frequency = 1451 + var/broadcasting = null + var/listening = 1.0 + w_class = ITEMSIZE_SMALL + item_state = "electronic" + throw_speed = 4 + throw_range = 20 + origin_tech = list(TECH_MAGNET = 1) + matter = list(MAT_STEEL = 400) + +/obj/item/weapon/locator/attack_self(mob/user as mob) + user.set_machine(src) + var/dat + if (src.temp) + dat = "[src.temp]

                    Clear" + else + dat = {" +Persistent Signal Locator
                    +Frequency: +- +- [format_frequency(src.frequency)] ++ ++
                    + +Refresh"} + user << browse(dat, "window=radio") + onclose(user, "radio") + return + +/obj/item/weapon/locator/Topic(href, href_list) + ..() + if (usr.stat || usr.restrained()) + return + var/turf/current_location = get_turf(usr)//What turf is the user on? + if(!current_location||current_location.z==3)//If turf was not found or they're on z level 2. + to_chat(usr, "The [src] is malfunctioning.") + return + if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf)))) + usr.set_machine(src) + if (href_list["refresh"]) + src.temp = "Persistent Signal Locator
                    " + var/turf/sr = get_turf(src) + + if (sr) + src.temp += "Located Beacons:
                    " + + for(var/obj/item/device/radio/beacon/W in GLOB.all_beacons) + if (W.frequency == src.frequency) + var/turf/tr = get_turf(W) + if (tr.z == sr.z && tr) + var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) + if (direct < 5) + direct = "very strong" + else + if (direct < 10) + direct = "strong" + else + if (direct < 20) + direct = "weak" + else + direct = "very weak" + src.temp += "[W.code]-[dir2text(get_dir(sr, tr))]-[direct]
                    " + + src.temp += "Extraneous Signals:
                    " + for (var/obj/item/weapon/implant/tracking/W in GLOB.all_tracking_implants) + if (!W.implanted || !(istype(W.loc,/obj/item/organ/external) || ismob(W.loc) || W.malfunction)) + continue + + var/turf/tr = get_turf(W) + if (tr.z == sr.z && tr) + var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) + if (direct < 20) + if (direct < 5) + direct = "very strong" + else + if (direct < 10) + direct = "strong" + else + direct = "weak" + src.temp += "[W.id]-[dir2text(get_dir(sr, tr))]-[direct]
                    " + + src.temp += "You are at \[[sr.x],[sr.y],[sr.z]\] in orbital coordinates.

                    Refresh
                    " + else + src.temp += "Processing Error: Unable to locate orbital position.
                    " + else + if (href_list["freq"]) + src.frequency += text2num(href_list["freq"]) + src.frequency = sanitize_frequency(src.frequency) + else + if (href_list["temp"]) + src.temp = null + if (istype(src.loc, /mob)) + attack_self(src.loc) + else + for(var/mob/M in viewers(1, src)) + if (M.client) + src.attack_self(M) + return + + +/* + * Hand-tele + */ +/obj/item/weapon/hand_tele + name = "hand tele" + desc = "A portable item using blue-space technology." + icon = 'icons/obj/device.dmi' + icon_state = "hand_tele" + item_state = "electronic" + throwforce = 5 + w_class = ITEMSIZE_SMALL + throw_speed = 3 + throw_range = 5 + origin_tech = list(TECH_MAGNET = 1, TECH_BLUESPACE = 3) + matter = list(MAT_STEEL = 10000) + preserve_item = 1 + +/obj/item/weapon/hand_tele/attack_self(mob/user as mob) + var/turf/current_location = get_turf(user)//What turf is the user on? + if(!current_location || (current_location.z in using_map.admin_levels) || current_location.block_tele)//If turf was not found or they're on z level 2 or >7 which does not currently exist. + to_chat(user, "\The [src] is malfunctioning.") + return + var/list/L = list( ) + for(var/obj/machinery/teleport/hub/R in machines) + var/obj/machinery/computer/teleporter/com + var/obj/machinery/teleport/station/station + for(var/direction in cardinal) + station = locate(/obj/machinery/teleport/station, get_step(R, direction)) + if(station) + for(direction in cardinal) + com = locate(/obj/machinery/computer/teleporter, get_step(station, direction)) + if(com) + break + break + if (istype(com, /obj/machinery/computer/teleporter) && com.teleport_control.locked && !com.one_time_use) + if(R.icon_state == "tele1") + L["[com.id] (Active)"] = com.teleport_control.locked + else + L["[com.id] (Inactive)"] = com.teleport_control.locked + var/list/turfs = list( ) + for(var/turf/T in orange(10)) + if(T.x>world.maxx-8 || T.x<8) continue //putting them at the edge is dumb + if(T.y>world.maxy-8 || T.y<8) continue + if(T.block_tele) continue + turfs += T + if(turfs.len) + L["None (Dangerous)"] = pick(turfs) + var/t1 = tgui_input_list(user, "Please select a teleporter to lock in on.", "Hand Teleporter", L) + if(!t1) + return + if ((user.get_active_hand() != src || user.stat || user.restrained())) + return + var/count = 0 //num of portals from this teleport in world + for(var/obj/effect/portal/PO in GLOB.all_portals) + if(PO.creator == src) count++ + if(count >= 3) + user.show_message("\The [src] is recharging!") + return + var/T = L[t1] + for(var/mob/O in hearers(user, null)) + O.show_message("Locked In.", 2) + var/obj/effect/portal/P = new /obj/effect/portal( get_turf(src) ) + P.target = T + P.creator = src + src.add_fingerprint(user) + return diff --git a/code/game/objects/items/weapons/trays.dm b/code/game/objects/items/weapons/trays.dm index e16dbbf88a5..2ea7b30cd1e 100644 --- a/code/game/objects/items/weapons/trays.dm +++ b/code/game/objects/items/weapons/trays.dm @@ -1,214 +1,214 @@ -/* - * Trays - Agouri - */ -/obj/item/weapon/tray - name = "tray" - icon = 'icons/obj/food.dmi' - icon_state = "tray" - desc = "A metal tray to lay food on." - throwforce = 12.0 - throwforce = 10.0 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_NORMAL - matter = list(MAT_STEEL = 3000) - var/list/carrying = list() // List of things on the tray. - Doohl - var/max_carry = 10 - drop_sound = 'sound/items/trayhit1.ogg' - -/obj/item/weapon/tray/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - user.setClickCooldown(user.get_attack_speed(src)) - // Drop all the things. All of them. - cut_overlays() - for(var/obj/item/I in carrying) - I.loc = M.loc - carrying.Remove(I) - if(isturf(I.loc)) - spawn() - for(var/i = 1, i <= rand(1,2), i++) - if(I) - step(I, pick(NORTH,SOUTH,EAST,WEST)) - sleep(rand(2,4)) - - - if((CLUMSY in user.mutations) && prob(50)) //What if he's a clown? - to_chat(M, "You accidentally slam yourself with the [src]!") - M.Weaken(1) - user.take_organ_damage(2) - if(prob(50)) - playsound(src, 'sound/items/trayhit1.ogg', 50, 1) - return - else - playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //sound playin' - return //it always returns, but I feel like adding an extra return just for safety's sakes. EDIT; Oh well I won't :3 - - var/mob/living/carbon/human/H = M ///////////////////////////////////// /Let's have this ready for later. - - - if(!(user.zone_sel.selecting == (O_EYES || BP_HEAD))) //////////////hitting anything else other than the eyes - if(prob(33)) - src.add_blood(H) - var/turf/location = H.loc - if (istype(location, /turf/simulated)) - location.add_blood(H) ///Plik plik, the sound of blood - - add_attack_logs(user,M,"Hit with [src]") - - if(prob(15)) - M.Weaken(3) - M.take_organ_damage(3) - else - M.take_organ_damage(5) - if(prob(50)) - playsound(src, 'sound/items/trayhit1.ogg', 50, 1) - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] slams [] with the tray!", user, M), 1) - return - else - playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //we applied the damage, we played the sound, we showed the appropriate messages. Time to return and stop the proc - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] slams [] with the tray!", user, M), 1) - return - - - var/protected = 0 - for(var/slot in list(slot_head, slot_wear_mask, slot_glasses)) - var/obj/item/protection = M.get_equipped_item(slot) - if(istype(protection) && (protection.body_parts_covered & FACE)) - protected = 1 - break - - if(protected) - to_chat(M, "You get slammed in the face with the tray, against your mask!") - if(prob(33)) - src.add_blood(H) - if (H.wear_mask) - H.wear_mask.add_blood(H) - if (H.head) - H.head.add_blood(H) - if (H.glasses && prob(33)) - H.glasses.add_blood(H) - var/turf/location = H.loc - if (istype(location, /turf/simulated)) //Addin' blood! At least on the floor and item :v - location.add_blood(H) - - if(prob(50)) - playsound(src, 'sound/items/trayhit1.ogg', 50, 1) - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] slams [] with the tray!", user, M), 1) - else - playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //sound playin' - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] slams [] with the tray!", user, M), 1) - if(prob(10)) - M.Stun(rand(1,3)) - M.take_organ_damage(3) - return - else - M.take_organ_damage(5) - return - - else //No eye or head protection, tough luck! - to_chat(M, "You get slammed in the face with the tray!") - if(prob(33)) - src.add_blood(M) - var/turf/location = H.loc - if (istype(location, /turf/simulated)) - location.add_blood(H) - - if(prob(50)) - playsound(src, 'sound/items/trayhit1.ogg', 50, 1) - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] slams [] in the face with the tray!", user, M), 1) - else - playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //sound playin' again - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] slams [] in the face with the tray!", user, M), 1) - if(prob(30)) - M.Stun(rand(2,4)) - M.take_organ_damage(4) - return - else - M.take_organ_damage(8) - if(prob(30)) - M.Weaken(2) - return - return - -/obj/item/weapon/tray/var/cooldown = 0 //shield bash cooldown. based on world.time - -/obj/item/weapon/tray/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/material/kitchen/rollingpin)) - if(cooldown < world.time - 25) - user.visible_message("[user] bashes [src] with [W]!") - playsound(src, 'sound/effects/shieldbash.ogg', 50, 1) - cooldown = world.time - else - ..() - -/* -===============~~~~~================================~~~~~==================== -= = -= Code for trays carrying things. By Doohl for Doohl erryday Doohl Doohl~ = -= = -===============~~~~~================================~~~~~==================== -*/ -/obj/item/weapon/tray/proc/calc_carry() - // calculate the weight of the items on the tray - var/val = 0 // value to return - - for(var/obj/item/I in carrying) - if(I.w_class == ITEMSIZE_TINY) - val ++ - else if(I.w_class == ITEMSIZE_SMALL) - val += 3 - else - val += 5 - - return val - -/obj/item/weapon/tray/pickup(mob/user) - - if(!isturf(loc)) - return - - for(var/obj/item/I in loc) - if( I != src && !I.anchored && !istype(I, /obj/item/clothing/under) && !istype(I, /obj/item/clothing/suit) && !istype(I, /obj/item/projectile) ) - var/add = 0 - if(I.w_class == ITEMSIZE_TINY) - add = 1 - else if(I.w_class == ITEMSIZE_SMALL) - add = 3 - else - add = 5 - if(calc_carry() + add >= max_carry) - break - var/image/Img = new(src.icon) - I.loc = src - carrying.Add(I) - Img.icon = I.icon - Img.icon_state = I.icon_state - Img.layer = layer + I.layer*0.01 - if(istype(I, /obj/item/weapon/material)) - var/obj/item/weapon/material/O = I - if(O.applies_material_colour) - Img.color = O.color - add_overlay(Img) - -/obj/item/weapon/tray/dropped(mob/user) - var/noTable = null - - spawn() //Allows the tray to udpate location, rather than just checking against mob's location - if(isturf(src.loc) && !(locate(/obj/structure/table) in src.loc)) - noTable = 1 - - if(isturf(loc) && !(locate(/mob/living) in src.loc)) - cut_overlays() - for(var/obj/item/I in carrying) - I.forceMove(src.loc) - carrying.Remove(I) - if(noTable) - for(var/i = 1, i <= rand(1,2), i++) - if(I) - step(I, pick(NORTH,SOUTH,EAST,WEST)) - sleep(rand(2,4)) +/* + * Trays - Agouri + */ +/obj/item/weapon/tray + name = "tray" + icon = 'icons/obj/food.dmi' + icon_state = "tray" + desc = "A metal tray to lay food on." + throwforce = 12.0 + throwforce = 10.0 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_NORMAL + matter = list(MAT_STEEL = 3000) + var/list/carrying = list() // List of things on the tray. - Doohl + var/max_carry = 10 + drop_sound = 'sound/items/trayhit1.ogg' + +/obj/item/weapon/tray/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + user.setClickCooldown(user.get_attack_speed(src)) + // Drop all the things. All of them. + cut_overlays() + for(var/obj/item/I in carrying) + I.loc = M.loc + carrying.Remove(I) + if(isturf(I.loc)) + spawn() + for(var/i = 1, i <= rand(1,2), i++) + if(I) + step(I, pick(NORTH,SOUTH,EAST,WEST)) + sleep(rand(2,4)) + + + if((CLUMSY in user.mutations) && prob(50)) //What if he's a clown? + to_chat(M, "You accidentally slam yourself with the [src]!") + M.Weaken(1) + user.take_organ_damage(2) + if(prob(50)) + playsound(src, 'sound/items/trayhit1.ogg', 50, 1) + return + else + playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //sound playin' + return //it always returns, but I feel like adding an extra return just for safety's sakes. EDIT; Oh well I won't :3 + + var/mob/living/carbon/human/H = M ///////////////////////////////////// /Let's have this ready for later. + + + if(!(user.zone_sel.selecting == (O_EYES || BP_HEAD))) //////////////hitting anything else other than the eyes + if(prob(33)) + src.add_blood(H) + var/turf/location = H.loc + if (istype(location, /turf/simulated)) + location.add_blood(H) ///Plik plik, the sound of blood + + add_attack_logs(user,M,"Hit with [src]") + + if(prob(15)) + M.Weaken(3) + M.take_organ_damage(3) + else + M.take_organ_damage(5) + if(prob(50)) + playsound(src, 'sound/items/trayhit1.ogg', 50, 1) + for(var/mob/O in viewers(M, null)) + O.show_message(text("[] slams [] with the tray!", user, M), 1) + return + else + playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //we applied the damage, we played the sound, we showed the appropriate messages. Time to return and stop the proc + for(var/mob/O in viewers(M, null)) + O.show_message(text("[] slams [] with the tray!", user, M), 1) + return + + + var/protected = 0 + for(var/slot in list(slot_head, slot_wear_mask, slot_glasses)) + var/obj/item/protection = M.get_equipped_item(slot) + if(istype(protection) && (protection.body_parts_covered & FACE)) + protected = 1 + break + + if(protected) + to_chat(M, "You get slammed in the face with the tray, against your mask!") + if(prob(33)) + src.add_blood(H) + if (H.wear_mask) + H.wear_mask.add_blood(H) + if (H.head) + H.head.add_blood(H) + if (H.glasses && prob(33)) + H.glasses.add_blood(H) + var/turf/location = H.loc + if (istype(location, /turf/simulated)) //Addin' blood! At least on the floor and item :v + location.add_blood(H) + + if(prob(50)) + playsound(src, 'sound/items/trayhit1.ogg', 50, 1) + for(var/mob/O in viewers(M, null)) + O.show_message(text("[] slams [] with the tray!", user, M), 1) + else + playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //sound playin' + for(var/mob/O in viewers(M, null)) + O.show_message(text("[] slams [] with the tray!", user, M), 1) + if(prob(10)) + M.Stun(rand(1,3)) + M.take_organ_damage(3) + return + else + M.take_organ_damage(5) + return + + else //No eye or head protection, tough luck! + to_chat(M, "You get slammed in the face with the tray!") + if(prob(33)) + src.add_blood(M) + var/turf/location = H.loc + if (istype(location, /turf/simulated)) + location.add_blood(H) + + if(prob(50)) + playsound(src, 'sound/items/trayhit1.ogg', 50, 1) + for(var/mob/O in viewers(M, null)) + O.show_message(text("[] slams [] in the face with the tray!", user, M), 1) + else + playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //sound playin' again + for(var/mob/O in viewers(M, null)) + O.show_message(text("[] slams [] in the face with the tray!", user, M), 1) + if(prob(30)) + M.Stun(rand(2,4)) + M.take_organ_damage(4) + return + else + M.take_organ_damage(8) + if(prob(30)) + M.Weaken(2) + return + return + +/obj/item/weapon/tray/var/cooldown = 0 //shield bash cooldown. based on world.time + +/obj/item/weapon/tray/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/material/kitchen/rollingpin)) + if(cooldown < world.time - 25) + user.visible_message("[user] bashes [src] with [W]!") + playsound(src, 'sound/effects/shieldbash.ogg', 50, 1) + cooldown = world.time + else + ..() + +/* +===============~~~~~================================~~~~~==================== += = += Code for trays carrying things. By Doohl for Doohl erryday Doohl Doohl~ = += = +===============~~~~~================================~~~~~==================== +*/ +/obj/item/weapon/tray/proc/calc_carry() + // calculate the weight of the items on the tray + var/val = 0 // value to return + + for(var/obj/item/I in carrying) + if(I.w_class == ITEMSIZE_TINY) + val ++ + else if(I.w_class == ITEMSIZE_SMALL) + val += 3 + else + val += 5 + + return val + +/obj/item/weapon/tray/pickup(mob/user) + + if(!isturf(loc)) + return + + for(var/obj/item/I in loc) + if( I != src && !I.anchored && !istype(I, /obj/item/clothing/under) && !istype(I, /obj/item/clothing/suit) && !istype(I, /obj/item/projectile) ) + var/add = 0 + if(I.w_class == ITEMSIZE_TINY) + add = 1 + else if(I.w_class == ITEMSIZE_SMALL) + add = 3 + else + add = 5 + if(calc_carry() + add >= max_carry) + break + var/image/Img = new(src.icon) + I.loc = src + carrying.Add(I) + Img.icon = I.icon + Img.icon_state = I.icon_state + Img.layer = layer + I.layer*0.01 + if(istype(I, /obj/item/weapon/material)) + var/obj/item/weapon/material/O = I + if(O.applies_material_colour) + Img.color = O.color + add_overlay(Img) + +/obj/item/weapon/tray/dropped(mob/user) + var/noTable = null + + spawn() //Allows the tray to udpate location, rather than just checking against mob's location + if(isturf(src.loc) && !(locate(/obj/structure/table) in src.loc)) + noTable = 1 + + if(isturf(loc) && !(locate(/mob/living) in src.loc)) + cut_overlays() + for(var/obj/item/I in carrying) + I.forceMove(src.loc) + carrying.Remove(I) + if(noTable) + for(var/i = 1, i <= rand(1,2), i++) + if(I) + step(I, pick(NORTH,SOUTH,EAST,WEST)) + sleep(rand(2,4)) diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm index 26f32002420..dc9aa503367 100644 --- a/code/game/objects/items/weapons/weaponry.dm +++ b/code/game/objects/items/weapons/weaponry.dm @@ -1,136 +1,136 @@ -/obj/item/weapon/nullrod - name = "null rod" - desc = "A rod of pure obsidian, its very presence disrupts and dampens the powers of paranormal phenomenae." - icon_state = "nullrod" - item_state = "nullrod" - slot_flags = SLOT_BELT - force = 15 - throw_speed = 1 - throw_range = 4 - throwforce = 10 - w_class = ITEMSIZE_SMALL - drop_sound = 'sound/items/drop/sword.ogg' - pickup_sound = 'sound/items/pickup/sword.ogg' - -/obj/item/weapon/nullrod/attack(mob/M as mob, mob/living/user as mob) //Paste from old-code to decult with a null rod. - - add_attack_logs(user,M,"Hit with [src] (nullrod)") - - user.setClickCooldown(user.get_attack_speed(src)) - user.do_attack_animation(M) - - if (!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do this!") - return - - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "The rod slips out of your hand and hits your head.") - user.take_organ_damage(10) - user.Paralyse(20) - return - - if (M.stat !=2) - if(cult && (M.mind in cult.current_antagonists) && prob(33)) - to_chat(M, "The power of [src] clears your mind of the cult's influence!") - to_chat(user, "You wave [src] over [M]'s head and see their eyes become clear, their mind returning to normal.") - cult.remove_antagonist(M.mind) - M.visible_message("\The [user] waves \the [src] over \the [M]'s head.") - else if(prob(10)) - to_chat(user, "The rod slips in your hand.") - ..() - else - to_chat(user, "The rod appears to do nothing.") - M.visible_message("\The [user] waves \the [src] over \the [M]'s head.") - return - -/obj/item/weapon/nullrod/afterattack(atom/A, mob/user as mob, proximity) - if(!proximity) - return - if (istype(A, /turf/simulated/floor)) - to_chat(user, "You hit the floor with the [src].") - call(/obj/effect/rune/proc/revealrunes)(src) - -/obj/item/weapon/energy_net - name = "energy net" - desc = "It's a net made of green energy." - icon = 'icons/effects/effects.dmi' - icon_state = "energynet" - throwforce = 0 - force = 0 - var/net_type = /obj/effect/energy_net - -/obj/item/weapon/energy_net/dropped() - spawn(10) - if(src) qdel(src) - -/obj/item/weapon/energy_net/throw_impact(atom/hit_atom) - ..() - - var/mob/living/M = hit_atom - - if(!istype(M) || locate(/obj/effect/energy_net) in M.loc) - qdel(src) - return 0 - - var/turf/T = get_turf(M) - if(T) - var/obj/effect/energy_net/net = new net_type(T) - if(net.buckle_mob(M)) - T.visible_message("[M] was caught in an energy net!") - qdel(src) - - // If we miss or hit an obstacle, we still want to delete the net. - spawn(10) - if(src) qdel(src) - -/obj/effect/energy_net - name = "energy net" - desc = "It's a net made of green energy." - icon = 'icons/effects/effects.dmi' - icon_state = "energynet" - - density = TRUE - opacity = 0 - mouse_opacity = 1 - anchored = FALSE - - can_buckle = TRUE - buckle_lying = 0 - buckle_dir = SOUTH - - var/escape_time = 8 SECONDS - -/obj/effect/energy_net/New() - ..() - START_PROCESSING(SSobj, src) - -/obj/effect/energy_net/Destroy() - if(has_buckled_mobs()) - for(var/A in buckled_mobs) - to_chat(A, "You are free of the net!") - unbuckle_mob(A) - - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/effect/energy_net/process() - if(!has_buckled_mobs()) - qdel(src) - -/obj/effect/energy_net/user_unbuckle_mob(mob/living/buckled_mob, mob/user) - user.setClickCooldown(user.get_attack_speed()) - visible_message("[user] begins to tear at \the [src]!") - if(do_after(user, escape_time, src, incapacitation_flags = INCAPACITATION_DEFAULT & ~(INCAPACITATION_RESTRAINED | INCAPACITATION_BUCKLED_FULLY))) - if(!has_buckled_mobs()) - return - visible_message("[user] manages to tear \the [src] apart!") - unbuckle_mob(buckled_mob) - -/obj/effect/energy_net/post_buckle_mob(mob/living/M) - if(M.buckled == src) //Just buckled someone - ..() - layer = M.layer+1 - M.can_pull_size = 0 - else //Just unbuckled someone - M.can_pull_size = initial(M.can_pull_size) - qdel(src) +/obj/item/weapon/nullrod + name = "null rod" + desc = "A rod of pure obsidian, its very presence disrupts and dampens the powers of paranormal phenomenae." + icon_state = "nullrod" + item_state = "nullrod" + slot_flags = SLOT_BELT + force = 15 + throw_speed = 1 + throw_range = 4 + throwforce = 10 + w_class = ITEMSIZE_SMALL + drop_sound = 'sound/items/drop/sword.ogg' + pickup_sound = 'sound/items/pickup/sword.ogg' + +/obj/item/weapon/nullrod/attack(mob/M as mob, mob/living/user as mob) //Paste from old-code to decult with a null rod. + + add_attack_logs(user,M,"Hit with [src] (nullrod)") + + user.setClickCooldown(user.get_attack_speed(src)) + user.do_attack_animation(M) + + if (!user.IsAdvancedToolUser()) + to_chat(user, "You don't have the dexterity to do this!") + return + + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "The rod slips out of your hand and hits your head.") + user.take_organ_damage(10) + user.Paralyse(20) + return + + if (M.stat !=2) + if(cult && (M.mind in cult.current_antagonists) && prob(33)) + to_chat(M, "The power of [src] clears your mind of the cult's influence!") + to_chat(user, "You wave [src] over [M]'s head and see their eyes become clear, their mind returning to normal.") + cult.remove_antagonist(M.mind) + M.visible_message("\The [user] waves \the [src] over \the [M]'s head.") + else if(prob(10)) + to_chat(user, "The rod slips in your hand.") + ..() + else + to_chat(user, "The rod appears to do nothing.") + M.visible_message("\The [user] waves \the [src] over \the [M]'s head.") + return + +/obj/item/weapon/nullrod/afterattack(atom/A, mob/user as mob, proximity) + if(!proximity) + return + if (istype(A, /turf/simulated/floor)) + to_chat(user, "You hit the floor with the [src].") + call(/obj/effect/rune/proc/revealrunes)(src) + +/obj/item/weapon/energy_net + name = "energy net" + desc = "It's a net made of green energy." + icon = 'icons/effects/effects.dmi' + icon_state = "energynet" + throwforce = 0 + force = 0 + var/net_type = /obj/effect/energy_net + +/obj/item/weapon/energy_net/dropped() + spawn(10) + if(src) qdel(src) + +/obj/item/weapon/energy_net/throw_impact(atom/hit_atom) + ..() + + var/mob/living/M = hit_atom + + if(!istype(M) || locate(/obj/effect/energy_net) in M.loc) + qdel(src) + return 0 + + var/turf/T = get_turf(M) + if(T) + var/obj/effect/energy_net/net = new net_type(T) + if(net.buckle_mob(M)) + T.visible_message("[M] was caught in an energy net!") + qdel(src) + + // If we miss or hit an obstacle, we still want to delete the net. + spawn(10) + if(src) qdel(src) + +/obj/effect/energy_net + name = "energy net" + desc = "It's a net made of green energy." + icon = 'icons/effects/effects.dmi' + icon_state = "energynet" + + density = TRUE + opacity = 0 + mouse_opacity = 1 + anchored = FALSE + + can_buckle = TRUE + buckle_lying = 0 + buckle_dir = SOUTH + + var/escape_time = 8 SECONDS + +/obj/effect/energy_net/New() + ..() + START_PROCESSING(SSobj, src) + +/obj/effect/energy_net/Destroy() + if(has_buckled_mobs()) + for(var/A in buckled_mobs) + to_chat(A, "You are free of the net!") + unbuckle_mob(A) + + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/effect/energy_net/process() + if(!has_buckled_mobs()) + qdel(src) + +/obj/effect/energy_net/user_unbuckle_mob(mob/living/buckled_mob, mob/user) + user.setClickCooldown(user.get_attack_speed()) + visible_message("[user] begins to tear at \the [src]!") + if(do_after(user, escape_time, src, incapacitation_flags = INCAPACITATION_DEFAULT & ~(INCAPACITATION_RESTRAINED | INCAPACITATION_BUCKLED_FULLY))) + if(!has_buckled_mobs()) + return + visible_message("[user] manages to tear \the [src] apart!") + unbuckle_mob(buckled_mob) + +/obj/effect/energy_net/post_buckle_mob(mob/living/M) + if(M.buckled == src) //Just buckled someone + ..() + layer = M.layer+1 + M.can_pull_size = 0 + else //Just unbuckled someone + M.can_pull_size = initial(M.can_pull_size) + qdel(src) diff --git a/code/game/objects/micro_event.dm b/code/game/objects/micro_event.dm index f798d9b6aad..36fcb152e78 100644 --- a/code/game/objects/micro_event.dm +++ b/code/game/objects/micro_event.dm @@ -17,7 +17,7 @@ shrinking = FALSE our_message = "What should the size limit be? Anyone under this limit will be grown to this size. (1 = 100%, etc)" - size_limit = tgui_input_number(user, our_message, "Pick a Size", 1) + size_limit = tgui_input_number(user, our_message, "Pick a Size", 1, round_value=FALSE) return ..() diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index c36782c0693..21b78b9a49e 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -1,209 +1,209 @@ -/obj - layer = OBJ_LAYER - plane = OBJ_PLANE - vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of obj in openspace. - //Used to store information about the contents of the object. - var/list/matter - var/w_class // Size of the object. - var/unacidable = FALSE //universal "unacidabliness" var, here so you can use it in any obj. - animate_movement = 2 - var/throwforce = 1 - var/catchable = 1 // can it be caught on throws/flying? - var/sharp = FALSE // whether this object cuts - var/edge = FALSE // whether this object is more likely to dismember - var/pry = 0 //Used in attackby() to open doors - var/in_use = 0 // If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING! - var/damtype = "brute" - var/armor_penetration = 0 - var/show_messages - var/preserve_item = 0 //whether this object is preserved when its owner goes into cryo-storage, gateway, etc - var/can_speak = 0 //For MMIs and admin trickery. If an object has a brainmob in its contents, set this to 1 to allow it to speak. - - var/show_examine = TRUE // Does this pop up on a mob when the mob is examined? - - var/redgate_allowed = TRUE //can we be taken through the redgate, in either direction? - -/obj/Destroy() - STOP_PROCESSING(SSobj, src) - - //VOREStation Add Start - I really am an idiot why did I make it this way - if(micro_target) - for(var/thing in src.contents) - if(!ismob(thing)) - continue - var/mob/m = thing - if(isbelly(src.loc)) - m.forceMove(src.loc) - else - m.forceMove(get_turf(src.loc)) - m.visible_message("\The [m] tumbles out of \the [src]!") - //VOREStation Add End - - return ..() - -/obj/Topic(href, href_list, var/datum/tgui_state/state = GLOB.tgui_default_state) - if(usr && ..()) - return 1 - - // In the far future no checks are made in an overriding Topic() beyond if(..()) return - // Instead any such checks are made in CanUseTopic() - if(CanUseTopic(usr, state, href_list) == STATUS_INTERACTIVE) - CouldUseTopic(usr) - return 0 - - CouldNotUseTopic(usr) - return 1 - -/obj/CanUseTopic(var/mob/user, var/datum/tgui_state/state = GLOB.tgui_default_state) - if(user.CanUseObjTopic(src)) - return ..() - to_chat(user, "\icon[src][bicon(src)]Access Denied!") - return STATUS_CLOSE - -/mob/living/silicon/CanUseObjTopic(var/obj/O) - var/id = src.GetIdCard() - return O.check_access(id) - -/mob/proc/CanUseObjTopic() - return 1 - -/obj/proc/CouldUseTopic(var/mob/user) - var/atom/host = tgui_host() - host.add_hiddenprint(user) - -/obj/proc/CouldNotUseTopic(var/mob/user) - // Nada - -/obj/item/proc/is_used_on(obj/O, mob/user) - -/obj/assume_air(datum/gas_mixture/giver) - if(loc) - return loc.assume_air(giver) - else - return null - -/obj/remove_air(amount) - if(loc) - return loc.remove_air(amount) - else - return null - -/obj/return_air() - if(loc) - return loc.return_air() - else - return null - -/obj/proc/updateUsrDialog() - if(in_use) - var/is_in_use = 0 - var/list/nearby = viewers(1, src) - for(var/mob/M in nearby) - if ((M.client && M.machine == src)) - is_in_use = 1 - src.attack_hand(M) - if (istype(usr, /mob/living/silicon/ai) || istype(usr, /mob/living/silicon/robot)) - if (!(usr in nearby)) - if (usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh. - is_in_use = 1 - src.attack_ai(usr) - - // check for TK users - - if (istype(usr, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = usr - if(H.get_type_in_hands(/obj/item/tk_grab)) - if(!(H in nearby)) - if(H.client && H.machine==src) - is_in_use = 1 - src.attack_hand(H) - in_use = is_in_use - -/obj/proc/updateDialog() - // Check that people are actually using the machine. If not, don't update anymore. - if(in_use) - var/list/nearby = viewers(1, src) - var/is_in_use = 0 - for(var/mob/M in nearby) - if ((M.client && M.machine == src)) - is_in_use = 1 - src.interact(M) - var/ai_in_use = AutoUpdateAI(src) - - if(!ai_in_use && !is_in_use) - in_use = 0 - -/obj/attack_ghost(mob/user) - tgui_interact(user) - ..() - -/mob/proc/unset_machine() - machine?.remove_visual(src) - src.machine = null - -/mob/proc/set_machine(var/obj/O) - if(src.machine) - unset_machine() - src.machine = O - if(istype(O)) - O.in_use = 1 - -/obj/item/proc/updateSelfDialog() - var/mob/M = src.loc - if(istype(M) && M.client && M.machine == src) - src.attack_self(M) - -/obj/proc/hide(h) - return - -/obj/proc/hides_under_flooring() - return 0 - -/obj/proc/hear_talk(mob/M, list/message_pieces, verb) - if(talking_atom) - talking_atom.catchMessage(multilingual_to_message(message_pieces), M) -/* - var/mob/mo = locate(/mob) in src - if(mo) - var/rendered = "[M.name]: [text]" - mo.show_message(rendered, 2) - */ - return - -/obj/proc/hear_signlang(mob/M as mob, text, verb, datum/language/speaking) // Saycode gets worse every day. - return FALSE - -/obj/proc/see_emote(mob/M as mob, text, var/emote_type) - return - -/obj/proc/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2) - return - -// Used to mark a turf as containing objects that are dangerous to step onto. -/obj/proc/register_dangerous_to_step() - var/turf/T = get_turf(src) - if(T) - T.register_dangerous_object(src) - -/obj/proc/unregister_dangerous_to_step() - var/turf/T = get_turf(src) - if(T) - T.unregister_dangerous_object(src) - -// Test for if stepping on a tile containing this obj is safe to do, used for things like landmines and cliffs. -/obj/proc/is_safe_to_step(mob/living/L) - return TRUE - -/obj/proc/container_resist(var/mob/living) - return - -//To be called from things that spill objects on the floor. -//Makes an object move around randomly for a couple of tiles -/obj/proc/tumble(var/dist = 2) - set waitfor = FALSE - if (dist >= 1) - dist += rand(0,1) - for(var/i = 1, i <= dist, i++) - if(src) - step(src, pick(NORTH,SOUTH,EAST,WEST)) - sleep(rand(2,4)) +/obj + layer = OBJ_LAYER + plane = OBJ_PLANE + vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of obj in openspace. + //Used to store information about the contents of the object. + var/list/matter + var/w_class // Size of the object. + var/unacidable = FALSE //universal "unacidabliness" var, here so you can use it in any obj. + animate_movement = 2 + var/throwforce = 1 + var/catchable = 1 // can it be caught on throws/flying? + var/sharp = FALSE // whether this object cuts + var/edge = FALSE // whether this object is more likely to dismember + var/pry = 0 //Used in attackby() to open doors + var/in_use = 0 // If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING! + var/damtype = "brute" + var/armor_penetration = 0 + var/show_messages + var/preserve_item = 0 //whether this object is preserved when its owner goes into cryo-storage, gateway, etc + var/can_speak = 0 //For MMIs and admin trickery. If an object has a brainmob in its contents, set this to 1 to allow it to speak. + + var/show_examine = TRUE // Does this pop up on a mob when the mob is examined? + + var/redgate_allowed = TRUE //can we be taken through the redgate, in either direction? + +/obj/Destroy() + STOP_PROCESSING(SSobj, src) + + //VOREStation Add Start - I really am an idiot why did I make it this way + if(micro_target) + for(var/thing in src.contents) + if(!ismob(thing)) + continue + var/mob/m = thing + if(isbelly(src.loc)) + m.forceMove(src.loc) + else + m.forceMove(get_turf(src.loc)) + m.visible_message("\The [m] tumbles out of \the [src]!") + //VOREStation Add End + + return ..() + +/obj/Topic(href, href_list, var/datum/tgui_state/state = GLOB.tgui_default_state) + if(usr && ..()) + return 1 + + // In the far future no checks are made in an overriding Topic() beyond if(..()) return + // Instead any such checks are made in CanUseTopic() + if(CanUseTopic(usr, state, href_list) == STATUS_INTERACTIVE) + CouldUseTopic(usr) + return 0 + + CouldNotUseTopic(usr) + return 1 + +/obj/CanUseTopic(var/mob/user, var/datum/tgui_state/state = GLOB.tgui_default_state) + if(user.CanUseObjTopic(src)) + return ..() + to_chat(user, "\icon[src][bicon(src)]Access Denied!") + return STATUS_CLOSE + +/mob/living/silicon/CanUseObjTopic(var/obj/O) + var/id = src.GetIdCard() + return O.check_access(id) + +/mob/proc/CanUseObjTopic() + return 1 + +/obj/proc/CouldUseTopic(var/mob/user) + var/atom/host = tgui_host() + host.add_hiddenprint(user) + +/obj/proc/CouldNotUseTopic(var/mob/user) + // Nada + +/obj/item/proc/is_used_on(obj/O, mob/user) + +/obj/assume_air(datum/gas_mixture/giver) + if(loc) + return loc.assume_air(giver) + else + return null + +/obj/remove_air(amount) + if(loc) + return loc.remove_air(amount) + else + return null + +/obj/return_air() + if(loc) + return loc.return_air() + else + return null + +/obj/proc/updateUsrDialog() + if(in_use) + var/is_in_use = 0 + var/list/nearby = viewers(1, src) + for(var/mob/M in nearby) + if ((M.client && M.machine == src)) + is_in_use = 1 + src.attack_hand(M) + if (istype(usr, /mob/living/silicon/ai) || istype(usr, /mob/living/silicon/robot)) + if (!(usr in nearby)) + if (usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh. + is_in_use = 1 + src.attack_ai(usr) + + // check for TK users + + if (istype(usr, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = usr + if(H.get_type_in_hands(/obj/item/tk_grab)) + if(!(H in nearby)) + if(H.client && H.machine==src) + is_in_use = 1 + src.attack_hand(H) + in_use = is_in_use + +/obj/proc/updateDialog() + // Check that people are actually using the machine. If not, don't update anymore. + if(in_use) + var/list/nearby = viewers(1, src) + var/is_in_use = 0 + for(var/mob/M in nearby) + if ((M.client && M.machine == src)) + is_in_use = 1 + src.interact(M) + var/ai_in_use = AutoUpdateAI(src) + + if(!ai_in_use && !is_in_use) + in_use = 0 + +/obj/attack_ghost(mob/user) + tgui_interact(user) + ..() + +/mob/proc/unset_machine() + machine?.remove_visual(src) + src.machine = null + +/mob/proc/set_machine(var/obj/O) + if(src.machine) + unset_machine() + src.machine = O + if(istype(O)) + O.in_use = 1 + +/obj/item/proc/updateSelfDialog() + var/mob/M = src.loc + if(istype(M) && M.client && M.machine == src) + src.attack_self(M) + +/obj/proc/hide(h) + return + +/obj/proc/hides_under_flooring() + return 0 + +/obj/proc/hear_talk(mob/M, list/message_pieces, verb) + if(talking_atom) + talking_atom.catchMessage(multilingual_to_message(message_pieces), M) +/* + var/mob/mo = locate(/mob) in src + if(mo) + var/rendered = "[M.name]: [text]" + mo.show_message(rendered, 2) + */ + return + +/obj/proc/hear_signlang(mob/M as mob, text, verb, datum/language/speaking) // Saycode gets worse every day. + return FALSE + +/obj/proc/see_emote(mob/M as mob, text, var/emote_type) + return + +/obj/proc/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2) + return + +// Used to mark a turf as containing objects that are dangerous to step onto. +/obj/proc/register_dangerous_to_step() + var/turf/T = get_turf(src) + if(T) + T.register_dangerous_object(src) + +/obj/proc/unregister_dangerous_to_step() + var/turf/T = get_turf(src) + if(T) + T.unregister_dangerous_object(src) + +// Test for if stepping on a tile containing this obj is safe to do, used for things like landmines and cliffs. +/obj/proc/is_safe_to_step(mob/living/L) + return TRUE + +/obj/proc/container_resist(var/mob/living) + return + +//To be called from things that spill objects on the floor. +//Makes an object move around randomly for a couple of tiles +/obj/proc/tumble(var/dist = 2) + set waitfor = FALSE + if (dist >= 1) + dist += rand(0,1) + for(var/i = 1, i <= dist, i++) + if(src) + step(src, pick(NORTH,SOUTH,EAST,WEST)) + sleep(rand(2,4)) diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index fb9a4a0e8bc..626c7a296e2 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -1,261 +1,261 @@ -/obj/structure - icon = 'icons/obj/structures.dmi' - w_class = ITEMSIZE_NO_CONTAINER - blocks_emissive = EMISSIVE_BLOCK_GENERIC - - var/climbable - var/climb_delay = 3.5 SECONDS - var/breakable - var/parts - var/list/climbers - var/block_turf_edges = FALSE // If true, turf edge icons will not be made on the turf this occupies. - - var/list/connections - var/list/other_connections - var/list/blend_objects = newlist() // Objects which to blend with - var/list/noblend_objects = newlist() //Objects to avoid blending with (such as children of listed blend objects. - -/obj/structure/Initialize() - . = ..() - if(climbable) - verbs += /obj/structure/proc/climb_on - -/obj/structure/Destroy() - if(parts) - new parts(loc) - return ..() - -/obj/structure/attack_hand(mob/user) - if(breakable) - if(HULK in user.mutations) - user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) - attack_generic(user,1,"smashes") - else if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - if(H.species.can_shred(user)) - attack_generic(user,1,"slices") - - if(LAZYLEN(climbers) && !(user in climbers)) - user.visible_message("[user.name] shakes \the [src].", \ - "You shake \the [src].") - structure_shaken() - - return ..() - -/obj/structure/attack_tk() - return - -/obj/structure/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - return - if(2.0) - if(prob(50)) - qdel(src) - return - if(3.0) - return - -/obj/structure/proc/climb_on() - set name = "Climb structure" - set desc = "Climbs onto a structure." - set category = "Object" - set src in oview(1) - - do_climb(usr) - -/obj/structure/MouseDrop_T(mob/target, mob/user) - var/mob/living/H = user - if(istype(H) && can_climb(H) && target == user) - do_climb(target) - else - return ..() - -/obj/structure/proc/can_climb(var/mob/living/user, post_climb_check=0) - if (!climbable || !can_touch(user) || (!post_climb_check && (user in climbers))) - return 0 - - if (!user.Adjacent(src)) - to_chat(user, "You can't climb there, the way is blocked.") - return 0 - - var/obj/occupied = turf_is_crowded() - if(occupied) - to_chat(user, "There's \a [occupied] in the way.") - return 0 - return 1 - -/obj/structure/proc/turf_is_crowded() - var/turf/T = get_turf(src) - if(!T || !istype(T)) - return "empty void" - if(T.density) - return T - for(var/obj/O in T.contents) - if(istype(O,/obj/structure)) - var/obj/structure/S = O - if(S.climbable) continue - if(O && O.density && !(O.flags & ON_BORDER)) //ON_BORDER structures are handled by the Adjacent() check. - return O - return 0 - -/obj/structure/proc/do_climb(var/mob/living/user) - if (!can_climb(user)) - return - - usr.visible_message("[user] starts climbing onto \the [src]!") - LAZYDISTINCTADD(climbers, user) - - if(!do_after(user,(issmall(user) ? climb_delay * 0.6 : climb_delay))) - LAZYREMOVE(climbers, user) - return - - if (!can_climb(user, post_climb_check=1)) - LAZYREMOVE(climbers, user) - return - - usr.forceMove(climb_to(user)) - - if (get_turf(user) == get_turf(src)) - usr.visible_message("[user] climbs onto \the [src]!") - LAZYREMOVE(climbers, user) - -/obj/structure/proc/climb_to(var/mob/living/user) - return get_turf(src) - -/obj/structure/proc/structure_shaken() - for(var/mob/living/M in climbers) - M.Weaken(1) - to_chat(M, "You topple as you are shaken off \the [src]!") - climbers.Cut(1,2) - - for(var/mob/living/M in get_turf(src)) - if(M.lying) return //No spamming this on people. - - M.Weaken(3) - to_chat(M, "You topple as \the [src] moves under you!") - - if(prob(25)) - - var/damage = rand(15,30) - var/mob/living/carbon/human/H = M - if(!istype(H)) - to_chat(H, "You land heavily!") - M.adjustBruteLoss(damage) - return - - var/obj/item/organ/external/affecting - - switch(pick(list("ankle","wrist","head","knee","elbow"))) - if("ankle") - affecting = H.get_organ(pick(BP_L_FOOT, BP_R_FOOT)) - if("knee") - affecting = H.get_organ(pick(BP_L_LEG, BP_R_LEG)) - if("wrist") - affecting = H.get_organ(pick(BP_L_HAND, BP_R_HAND)) - if("elbow") - affecting = H.get_organ(pick(BP_L_ARM, BP_R_ARM)) - if("head") - affecting = H.get_organ(BP_HEAD) - - if(affecting) - to_chat(M, "You land heavily on your [affecting.name]!") - affecting.take_damage(damage, 0) - if(affecting.parent) - affecting.parent.add_autopsy_data("Misadventure", damage) - else - to_chat(H, "You land heavily!") - H.adjustBruteLoss(damage) - - H.UpdateDamageIcon() - H.updatehealth() - return - -/obj/structure/proc/can_touch(var/mob/user) - if (!user) - return 0 - if(!Adjacent(user)) - return 0 - if (user.restrained() || user.buckled) - to_chat(user, "You need your hands and legs free for this.") - return 0 - if (user.stat || user.paralysis || user.sleeping || user.lying || user.weakened) - return 0 - if (isAI(user)) - to_chat(user, "You need hands for this.") - return 0 - return 1 - -/obj/structure/attack_generic(var/mob/user, var/damage, var/attack_verb) - if(!breakable || damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) - return 0 - visible_message("[user] [attack_verb] the [src] apart!") - user.do_attack_animation(src) - spawn(1) qdel(src) - return 1 - -/obj/structure/proc/can_visually_connect() - return anchored - -/obj/structure/proc/can_visually_connect_to(var/obj/structure/S) - return istype(S, src) - -/obj/structure/proc/update_connections(propagate = 0) - var/list/dirs = list() - var/list/other_dirs = list() - - for(var/obj/structure/S in orange(src, 1)) - if(can_visually_connect_to(S)) - if(S.can_visually_connect()) - if(propagate) - S.update_connections() - S.update_icon() - dirs += get_dir(src, S) - - if(!can_visually_connect()) - connections = list("0", "0", "0", "0") - other_connections = list("0", "0", "0", "0") - return FALSE - - for(var/direction in cardinal) - var/turf/T = get_step(src, direction) - var/success = 0 - for(var/b_type in blend_objects) - if(istype(T, b_type)) - success = 1 - if(propagate) - var/turf/simulated/wall/W = T - if(istype(W)) - W.update_connections(1) - if(success) - break // breaks inner loop - if(!success) - blend_obj_loop: - for(var/obj/O in T) - for(var/b_type in blend_objects) - if(istype(O, b_type)) - success = 1 - for(var/obj/structure/S in T) - if(istype(S, src)) - success = 0 - for(var/nb_type in noblend_objects) - if(istype(O, nb_type)) - success = 0 - - if(success) - break blend_obj_loop // breaks outer loop - - if(success) - dirs += get_dir(src, T) - other_dirs += get_dir(src, T) - - refresh_neighbors() - - connections = dirs_to_corner_states(dirs) - other_connections = dirs_to_corner_states(other_dirs) - return TRUE - -/obj/structure/proc/refresh_neighbors() - for(var/turf/T as anything in RANGE_TURFS(1, src)) - T.update_icon() +/obj/structure + icon = 'icons/obj/structures.dmi' + w_class = ITEMSIZE_NO_CONTAINER + blocks_emissive = EMISSIVE_BLOCK_GENERIC + + var/climbable + var/climb_delay = 3.5 SECONDS + var/breakable + var/parts + var/list/climbers + var/block_turf_edges = FALSE // If true, turf edge icons will not be made on the turf this occupies. + + var/list/connections + var/list/other_connections + var/list/blend_objects = newlist() // Objects which to blend with + var/list/noblend_objects = newlist() //Objects to avoid blending with (such as children of listed blend objects. + +/obj/structure/Initialize() + . = ..() + if(climbable) + verbs += /obj/structure/proc/climb_on + +/obj/structure/Destroy() + if(parts) + new parts(loc) + return ..() + +/obj/structure/attack_hand(mob/user) + if(breakable) + if(HULK in user.mutations) + user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) + attack_generic(user,1,"smashes") + else if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(H.species.can_shred(user)) + attack_generic(user,1,"slices") + + if(LAZYLEN(climbers) && !(user in climbers)) + user.visible_message("[user.name] shakes \the [src].", \ + "You shake \the [src].") + structure_shaken() + + return ..() + +/obj/structure/attack_tk() + return + +/obj/structure/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + return + if(2.0) + if(prob(50)) + qdel(src) + return + if(3.0) + return + +/obj/structure/proc/climb_on() + set name = "Climb structure" + set desc = "Climbs onto a structure." + set category = "Object" + set src in oview(1) + + do_climb(usr) + +/obj/structure/MouseDrop_T(mob/target, mob/user) + var/mob/living/H = user + if(istype(H) && can_climb(H) && target == user) + do_climb(target) + else + return ..() + +/obj/structure/proc/can_climb(var/mob/living/user, post_climb_check=0) + if (!climbable || !can_touch(user) || (!post_climb_check && (user in climbers))) + return 0 + + if (!user.Adjacent(src)) + to_chat(user, "You can't climb there, the way is blocked.") + return 0 + + var/obj/occupied = turf_is_crowded() + if(occupied) + to_chat(user, "There's \a [occupied] in the way.") + return 0 + return 1 + +/obj/structure/proc/turf_is_crowded() + var/turf/T = get_turf(src) + if(!T || !istype(T)) + return "empty void" + if(T.density) + return T + for(var/obj/O in T.contents) + if(istype(O,/obj/structure)) + var/obj/structure/S = O + if(S.climbable) continue + if(O && O.density && !(O.flags & ON_BORDER)) //ON_BORDER structures are handled by the Adjacent() check. + return O + return 0 + +/obj/structure/proc/do_climb(var/mob/living/user) + if (!can_climb(user)) + return + + usr.visible_message("[user] starts climbing onto \the [src]!") + LAZYDISTINCTADD(climbers, user) + + if(!do_after(user,(issmall(user) ? climb_delay * 0.6 : climb_delay))) + LAZYREMOVE(climbers, user) + return + + if (!can_climb(user, post_climb_check=1)) + LAZYREMOVE(climbers, user) + return + + usr.forceMove(climb_to(user)) + + if (get_turf(user) == get_turf(src)) + usr.visible_message("[user] climbs onto \the [src]!") + LAZYREMOVE(climbers, user) + +/obj/structure/proc/climb_to(var/mob/living/user) + return get_turf(src) + +/obj/structure/proc/structure_shaken() + for(var/mob/living/M in climbers) + M.Weaken(1) + to_chat(M, "You topple as you are shaken off \the [src]!") + climbers.Cut(1,2) + + for(var/mob/living/M in get_turf(src)) + if(M.lying) return //No spamming this on people. + + M.Weaken(3) + to_chat(M, "You topple as \the [src] moves under you!") + + if(prob(25)) + + var/damage = rand(15,30) + var/mob/living/carbon/human/H = M + if(!istype(H)) + to_chat(H, "You land heavily!") + M.adjustBruteLoss(damage) + return + + var/obj/item/organ/external/affecting + + switch(pick(list("ankle","wrist","head","knee","elbow"))) + if("ankle") + affecting = H.get_organ(pick(BP_L_FOOT, BP_R_FOOT)) + if("knee") + affecting = H.get_organ(pick(BP_L_LEG, BP_R_LEG)) + if("wrist") + affecting = H.get_organ(pick(BP_L_HAND, BP_R_HAND)) + if("elbow") + affecting = H.get_organ(pick(BP_L_ARM, BP_R_ARM)) + if("head") + affecting = H.get_organ(BP_HEAD) + + if(affecting) + to_chat(M, "You land heavily on your [affecting.name]!") + affecting.take_damage(damage, 0) + if(affecting.parent) + affecting.parent.add_autopsy_data("Misadventure", damage) + else + to_chat(H, "You land heavily!") + H.adjustBruteLoss(damage) + + H.UpdateDamageIcon() + H.updatehealth() + return + +/obj/structure/proc/can_touch(var/mob/user) + if (!user) + return 0 + if(!Adjacent(user)) + return 0 + if (user.restrained() || user.buckled) + to_chat(user, "You need your hands and legs free for this.") + return 0 + if (user.stat || user.paralysis || user.sleeping || user.lying || user.weakened) + return 0 + if (isAI(user)) + to_chat(user, "You need hands for this.") + return 0 + return 1 + +/obj/structure/attack_generic(var/mob/user, var/damage, var/attack_verb) + if(!breakable || damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) + return 0 + visible_message("[user] [attack_verb] the [src] apart!") + user.do_attack_animation(src) + spawn(1) qdel(src) + return 1 + +/obj/structure/proc/can_visually_connect() + return anchored + +/obj/structure/proc/can_visually_connect_to(var/obj/structure/S) + return istype(S, src) + +/obj/structure/proc/update_connections(propagate = 0) + var/list/dirs = list() + var/list/other_dirs = list() + + for(var/obj/structure/S in orange(src, 1)) + if(can_visually_connect_to(S)) + if(S.can_visually_connect()) + if(propagate) + S.update_connections() + S.update_icon() + dirs += get_dir(src, S) + + if(!can_visually_connect()) + connections = list("0", "0", "0", "0") + other_connections = list("0", "0", "0", "0") + return FALSE + + for(var/direction in cardinal) + var/turf/T = get_step(src, direction) + var/success = 0 + for(var/b_type in blend_objects) + if(istype(T, b_type)) + success = 1 + if(propagate) + var/turf/simulated/wall/W = T + if(istype(W)) + W.update_connections(1) + if(success) + break // breaks inner loop + if(!success) + blend_obj_loop: + for(var/obj/O in T) + for(var/b_type in blend_objects) + if(istype(O, b_type)) + success = 1 + for(var/obj/structure/S in T) + if(istype(S, src)) + success = 0 + for(var/nb_type in noblend_objects) + if(istype(O, nb_type)) + success = 0 + + if(success) + break blend_obj_loop // breaks outer loop + + if(success) + dirs += get_dir(src, T) + other_dirs += get_dir(src, T) + + refresh_neighbors() + + connections = dirs_to_corner_states(dirs) + other_connections = dirs_to_corner_states(other_dirs) + return TRUE + +/obj/structure/proc/refresh_neighbors() + for(var/turf/T as anything in RANGE_TURFS(1, src)) + T.update_icon() diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm index 50869c4497c..e63debb8efe 100644 --- a/code/game/objects/structures/bedsheet_bin.dm +++ b/code/game/objects/structures/bedsheet_bin.dm @@ -1,248 +1,248 @@ -/* -CONTAINS: -BEDSHEETS -LINEN BINS -*/ - -/obj/item/weapon/bedsheet - name = "bedsheet" - desc = "A surprisingly soft linen bedsheet." - icon = 'icons/obj/items.dmi' - icon_state = "sheet" - slot_flags = SLOT_BACK - plane = MOB_PLANE - layer = BELOW_MOB_LAYER - throwforce = 1 - throw_speed = 1 - throw_range = 2 - w_class = ITEMSIZE_SMALL - drop_sound = 'sound/items/drop/clothing.ogg' - pickup_sound = 'sound/items/pickup/clothing.ogg' - -/obj/item/weapon/bedsheet/attack_self(mob/user as mob) - user.drop_item() - if(layer == initial(layer)) - layer = ABOVE_MOB_LAYER - else - reset_plane_and_layer() - add_fingerprint(user) - return - -/obj/item/weapon/bedsheet/attackby(obj/item/I, mob/user) - if(is_sharp(I)) - user.visible_message("\The [user] begins cutting up [src] with [I].", "You begin cutting up [src] with [I].") - if(do_after(user, 50)) - to_chat(user, "You cut [src] into pieces!") - for(var/i in 1 to rand(2,5)) - new /obj/item/weapon/reagent_containers/glass/rag(drop_location()) - qdel(src) - return - ..() - -/obj/item/weapon/bedsheet/blue - icon_state = "sheetblue" - -/obj/item/weapon/bedsheet/green - icon_state = "sheetgreen" - -/obj/item/weapon/bedsheet/orange - icon_state = "sheetorange" - -/obj/item/weapon/bedsheet/purple - icon_state = "sheetpurple" - -/obj/item/weapon/bedsheet/rainbow - icon_state = "sheetrainbow" - -/obj/item/weapon/bedsheet/red - icon_state = "sheetred" - -/obj/item/weapon/bedsheet/yellow - icon_state = "sheetyellow" - -/obj/item/weapon/bedsheet/mime - icon_state = "sheetmime" - -/obj/item/weapon/bedsheet/clown - icon_state = "sheetclown" - item_state = "sheetrainbow" - -/obj/item/weapon/bedsheet/captain - icon_state = "sheetcaptain" - -/obj/item/weapon/bedsheet/rd - icon_state = "sheetrd" - -/obj/item/weapon/bedsheet/medical - icon_state = "sheetmedical" - -/obj/item/weapon/bedsheet/hos - icon_state = "sheethos" - -/obj/item/weapon/bedsheet/hop - icon_state = "sheethop" - -/obj/item/weapon/bedsheet/ce - icon_state = "sheetce" - -/obj/item/weapon/bedsheet/brown - icon_state = "sheetbrown" - -/obj/item/weapon/bedsheet/ian - icon_state = "sheetian" - -/obj/item/weapon/bedsheet/double - icon_state = "doublesheet" - item_state = "sheet" - -/obj/item/weapon/bedsheet/bluedouble - icon_state = "doublesheetblue" - item_state = "sheetblue" - -/obj/item/weapon/bedsheet/greendouble - icon_state = "doublesheetgreen" - item_state = "sheetgreen" - -/obj/item/weapon/bedsheet/orangedouble - icon_state = "doublesheetorange" - item_state = "sheetorange" - -/obj/item/weapon/bedsheet/purpledouble - icon_state = "doublesheetpurple" - item_state = "sheetpurple" - -/obj/item/weapon/bedsheet/rainbowdouble //all the way across the sky. - icon_state = "doublesheetrainbow" - item_state = "sheetrainbow" - -/obj/item/weapon/bedsheet/reddouble - icon_state = "doublesheetred" - item_state = "sheetred" - -/obj/item/weapon/bedsheet/yellowdouble - icon_state = "doublesheetyellow" - item_state = "sheetyellow" - -/obj/item/weapon/bedsheet/mimedouble - icon_state = "doublesheetmime" - item_state = "sheetmime" - -/obj/item/weapon/bedsheet/clowndouble - icon_state = "doublesheetclown" - item_state = "sheetrainbow" - -/obj/item/weapon/bedsheet/captaindouble - icon_state = "doublesheetcaptain" - item_state = "sheetcaptain" - -/obj/item/weapon/bedsheet/rddouble - icon_state = "doublesheetrd" - item_state = "sheetrd" - -/obj/item/weapon/bedsheet/hosdouble - icon_state = "doublesheethos" - item_state = "sheethos" - -/obj/item/weapon/bedsheet/hopdouble - icon_state = "doublesheethop" - item_state = "sheethop" - -/obj/item/weapon/bedsheet/cedouble - icon_state = "doublesheetce" - item_state = "sheetce" - -/obj/item/weapon/bedsheet/browndouble - icon_state = "doublesheetbrown" - item_state = "sheetbrown" - -/obj/item/weapon/bedsheet/iandouble - icon_state = "doublesheetian" - item_state = "sheetian" - -/obj/structure/bedsheetbin - name = "linen bin" - desc = "A linen bin. It looks rather cosy." - icon = 'icons/obj/structures.dmi' - icon_state = "linenbin-full" - anchored = TRUE - var/amount = 20 - var/list/sheets = list() - var/obj/item/hidden = null - - -/obj/structure/bedsheetbin/examine(mob/user) - . = ..() - - if(amount < 1) - . += "There are no bed sheets in the bin." - else if(amount == 1) - . += "There is one bed sheet in the bin." - else - . += "There are [amount] bed sheets in the bin." - -/obj/structure/bedsheetbin/update_icon() - switch(amount) - if(0) icon_state = "linenbin-empty" - if(1 to amount / 2) icon_state = "linenbin-half" - else icon_state = "linenbin-full" - - -/obj/structure/bedsheetbin/attackby(obj/item/I as obj, mob/user as mob) - if(istype(I, /obj/item/weapon/bedsheet)) - user.drop_item() - I.loc = src - sheets.Add(I) - amount++ - to_chat(user, "You put [I] in [src].") - else if(amount && !hidden && I.w_class < ITEMSIZE_LARGE) //make sure there's sheets to hide it among, make sure nothing else is hidden in there. - user.drop_item() - I.loc = src - hidden = I - to_chat(user, "You hide [I] among the sheets.") - -/obj/structure/bedsheetbin/attack_hand(mob/user as mob) - if(amount >= 1) - amount-- - - var/obj/item/weapon/bedsheet/B - if(sheets.len > 0) - B = sheets[sheets.len] - sheets.Remove(B) - - else - B = new /obj/item/weapon/bedsheet(loc) - - B.loc = user.loc - user.put_in_hands(B) - to_chat(user, "You take [B] out of [src].") - - if(hidden) - hidden.loc = user.loc - to_chat(user, "[hidden] falls out of [B]!") - hidden = null - - - add_fingerprint(user) - -/obj/structure/bedsheetbin/attack_tk(mob/user as mob) - if(amount >= 1) - amount-- - - var/obj/item/weapon/bedsheet/B - if(sheets.len > 0) - B = sheets[sheets.len] - sheets.Remove(B) - - else - B = new /obj/item/weapon/bedsheet(loc) - - B.loc = loc - to_chat(user, "You telekinetically remove [B] from [src].") - update_icon() - - if(hidden) - hidden.loc = loc - hidden = null - - - add_fingerprint(user) +/* +CONTAINS: +BEDSHEETS +LINEN BINS +*/ + +/obj/item/weapon/bedsheet + name = "bedsheet" + desc = "A surprisingly soft linen bedsheet." + icon = 'icons/obj/items.dmi' + icon_state = "sheet" + slot_flags = SLOT_BACK + plane = MOB_PLANE + layer = BELOW_MOB_LAYER + throwforce = 1 + throw_speed = 1 + throw_range = 2 + w_class = ITEMSIZE_SMALL + drop_sound = 'sound/items/drop/clothing.ogg' + pickup_sound = 'sound/items/pickup/clothing.ogg' + +/obj/item/weapon/bedsheet/attack_self(mob/user as mob) + user.drop_item() + if(layer == initial(layer)) + layer = ABOVE_MOB_LAYER + else + reset_plane_and_layer() + add_fingerprint(user) + return + +/obj/item/weapon/bedsheet/attackby(obj/item/I, mob/user) + if(is_sharp(I)) + user.visible_message("\The [user] begins cutting up [src] with [I].", "You begin cutting up [src] with [I].") + if(do_after(user, 50)) + to_chat(user, "You cut [src] into pieces!") + for(var/i in 1 to rand(2,5)) + new /obj/item/weapon/reagent_containers/glass/rag(drop_location()) + qdel(src) + return + ..() + +/obj/item/weapon/bedsheet/blue + icon_state = "sheetblue" + +/obj/item/weapon/bedsheet/green + icon_state = "sheetgreen" + +/obj/item/weapon/bedsheet/orange + icon_state = "sheetorange" + +/obj/item/weapon/bedsheet/purple + icon_state = "sheetpurple" + +/obj/item/weapon/bedsheet/rainbow + icon_state = "sheetrainbow" + +/obj/item/weapon/bedsheet/red + icon_state = "sheetred" + +/obj/item/weapon/bedsheet/yellow + icon_state = "sheetyellow" + +/obj/item/weapon/bedsheet/mime + icon_state = "sheetmime" + +/obj/item/weapon/bedsheet/clown + icon_state = "sheetclown" + item_state = "sheetrainbow" + +/obj/item/weapon/bedsheet/captain + icon_state = "sheetcaptain" + +/obj/item/weapon/bedsheet/rd + icon_state = "sheetrd" + +/obj/item/weapon/bedsheet/medical + icon_state = "sheetmedical" + +/obj/item/weapon/bedsheet/hos + icon_state = "sheethos" + +/obj/item/weapon/bedsheet/hop + icon_state = "sheethop" + +/obj/item/weapon/bedsheet/ce + icon_state = "sheetce" + +/obj/item/weapon/bedsheet/brown + icon_state = "sheetbrown" + +/obj/item/weapon/bedsheet/ian + icon_state = "sheetian" + +/obj/item/weapon/bedsheet/double + icon_state = "doublesheet" + item_state = "sheet" + +/obj/item/weapon/bedsheet/bluedouble + icon_state = "doublesheetblue" + item_state = "sheetblue" + +/obj/item/weapon/bedsheet/greendouble + icon_state = "doublesheetgreen" + item_state = "sheetgreen" + +/obj/item/weapon/bedsheet/orangedouble + icon_state = "doublesheetorange" + item_state = "sheetorange" + +/obj/item/weapon/bedsheet/purpledouble + icon_state = "doublesheetpurple" + item_state = "sheetpurple" + +/obj/item/weapon/bedsheet/rainbowdouble //all the way across the sky. + icon_state = "doublesheetrainbow" + item_state = "sheetrainbow" + +/obj/item/weapon/bedsheet/reddouble + icon_state = "doublesheetred" + item_state = "sheetred" + +/obj/item/weapon/bedsheet/yellowdouble + icon_state = "doublesheetyellow" + item_state = "sheetyellow" + +/obj/item/weapon/bedsheet/mimedouble + icon_state = "doublesheetmime" + item_state = "sheetmime" + +/obj/item/weapon/bedsheet/clowndouble + icon_state = "doublesheetclown" + item_state = "sheetrainbow" + +/obj/item/weapon/bedsheet/captaindouble + icon_state = "doublesheetcaptain" + item_state = "sheetcaptain" + +/obj/item/weapon/bedsheet/rddouble + icon_state = "doublesheetrd" + item_state = "sheetrd" + +/obj/item/weapon/bedsheet/hosdouble + icon_state = "doublesheethos" + item_state = "sheethos" + +/obj/item/weapon/bedsheet/hopdouble + icon_state = "doublesheethop" + item_state = "sheethop" + +/obj/item/weapon/bedsheet/cedouble + icon_state = "doublesheetce" + item_state = "sheetce" + +/obj/item/weapon/bedsheet/browndouble + icon_state = "doublesheetbrown" + item_state = "sheetbrown" + +/obj/item/weapon/bedsheet/iandouble + icon_state = "doublesheetian" + item_state = "sheetian" + +/obj/structure/bedsheetbin + name = "linen bin" + desc = "A linen bin. It looks rather cosy." + icon = 'icons/obj/structures.dmi' + icon_state = "linenbin-full" + anchored = TRUE + var/amount = 20 + var/list/sheets = list() + var/obj/item/hidden = null + + +/obj/structure/bedsheetbin/examine(mob/user) + . = ..() + + if(amount < 1) + . += "There are no bed sheets in the bin." + else if(amount == 1) + . += "There is one bed sheet in the bin." + else + . += "There are [amount] bed sheets in the bin." + +/obj/structure/bedsheetbin/update_icon() + switch(amount) + if(0) icon_state = "linenbin-empty" + if(1 to amount / 2) icon_state = "linenbin-half" + else icon_state = "linenbin-full" + + +/obj/structure/bedsheetbin/attackby(obj/item/I as obj, mob/user as mob) + if(istype(I, /obj/item/weapon/bedsheet)) + user.drop_item() + I.loc = src + sheets.Add(I) + amount++ + to_chat(user, "You put [I] in [src].") + else if(amount && !hidden && I.w_class < ITEMSIZE_LARGE) //make sure there's sheets to hide it among, make sure nothing else is hidden in there. + user.drop_item() + I.loc = src + hidden = I + to_chat(user, "You hide [I] among the sheets.") + +/obj/structure/bedsheetbin/attack_hand(mob/user as mob) + if(amount >= 1) + amount-- + + var/obj/item/weapon/bedsheet/B + if(sheets.len > 0) + B = sheets[sheets.len] + sheets.Remove(B) + + else + B = new /obj/item/weapon/bedsheet(loc) + + B.loc = user.loc + user.put_in_hands(B) + to_chat(user, "You take [B] out of [src].") + + if(hidden) + hidden.loc = user.loc + to_chat(user, "[hidden] falls out of [B]!") + hidden = null + + + add_fingerprint(user) + +/obj/structure/bedsheetbin/attack_tk(mob/user as mob) + if(amount >= 1) + amount-- + + var/obj/item/weapon/bedsheet/B + if(sheets.len > 0) + B = sheets[sheets.len] + sheets.Remove(B) + + else + B = new /obj/item/weapon/bedsheet(loc) + + B.loc = loc + to_chat(user, "You telekinetically remove [B] from [src].") + update_icon() + + if(hidden) + hidden.loc = loc + hidden = null + + + add_fingerprint(user) diff --git a/code/game/objects/structures/cliff.dm b/code/game/objects/structures/cliff.dm index 016727ac344..6370e64271b 100644 --- a/code/game/objects/structures/cliff.dm +++ b/code/game/objects/structures/cliff.dm @@ -1,272 +1,272 @@ -GLOBAL_LIST_EMPTY(cliff_icon_cache) - -/* -Cliffs give a visual illusion of depth by seperating two places while presenting a 'top' and 'bottom' side. - -Mobs moving into a cliff from the bottom side will simply bump into it and be denied moving into the tile, -where as mobs moving into a cliff from the top side will 'fall' off the cliff, forcing them to the bottom, causing significant damage and stunning them. - -Mobs can climb this while wearing climbing equipment by clickdragging themselves onto a cliff, as if it were a table. - -Flying mobs can pass over all cliffs with no risk of falling. - -Projectiles and thrown objects can pass, however if moving upwards, there is a chance for it to be stopped by the cliff. -This makes fighting something that is on top of a cliff more challenging. - -As a note, dir points upwards, e.g. pointing WEST means the left side is 'up', and the right side is 'down'. - -When mapping these in, be sure to give at least a one tile clearance, as NORTH facing cliffs expand to -two tiles on initialization, and which way a cliff is facing may change during maploading. -*/ - -/obj/structure/cliff - name = "cliff" - desc = "A steep rock ledge. You might be able to climb it if you feel bold enough." - description_info = "Walking off the edge of a cliff while on top will cause you to fall off, causing severe injury.
                    \ - You can climb this cliff if wearing special climbing equipment, by click-dragging yourself onto the cliff.
                    \ - Projectiles traveling up a cliff may hit the cliff instead, making it more difficult to fight something \ - on top." - icon = 'icons/obj/flora/rocks.dmi' - - anchored = TRUE - density = TRUE - opacity = FALSE - climbable = TRUE - climb_delay = 10 SECONDS - unacidable = TRUE - block_turf_edges = TRUE // Don't want turf edges popping up from the cliff edge. - plane = TURF_PLANE - - var/icon_variant = null // Used to make cliffs less repeative by having a selection of sprites to display. - var/corner = FALSE // Used for icon things. - var/ramp = FALSE // Ditto. - var/bottom = FALSE // Used for 'bottom' typed cliffs, to avoid infinite cliffs, and for icons. - - var/is_double_cliff = FALSE // Set to true when making the two-tile cliffs, used for projectile checks. - var/uphill_penalty = 30 // Odds of a projectile not making it up the cliff. - -/obj/structure/cliff/Initialize() - . = ..() - register_dangerous_to_step() - -/obj/structure/cliff/Destroy() - unregister_dangerous_to_step() - . = ..() - -/obj/structure/cliff/Moved(atom/oldloc) - . = ..() - if(.) - var/turf/old_turf = get_turf(oldloc) - var/turf/new_turf = get_turf(src) - if(old_turf != new_turf) - old_turf.unregister_dangerous_object(src) - new_turf.register_dangerous_object(src) - -// These arrange their sprites at runtime, as opposed to being statically placed in the map file. -/obj/structure/cliff/automatic - icon_state = "cliffbuilder" - dir = NORTH - -/obj/structure/cliff/automatic/corner - icon_state = "cliffbuilder-corner" - dir = NORTHEAST - corner = TRUE - -// Tiny part that doesn't block, used for making 'ramps'. -/obj/structure/cliff/automatic/ramp - icon_state = "cliffbuilder-ramp" - dir = NORTHEAST - density = FALSE - ramp = TRUE - -// Made automatically as needed by automatic cliffs. -/obj/structure/cliff/bottom - bottom = TRUE - -/obj/structure/cliff/automatic/Initialize() - ..() - return INITIALIZE_HINT_LATELOAD - -// Paranoid about the maploader, direction is very important to cliffs, since they may get bigger if initialized while facing NORTH. -/obj/structure/cliff/automatic/LateInitialize() - if(dir in GLOB.cardinal) - icon_variant = pick("a", "b", "c") - - if(dir & NORTH && !bottom) // North-facing cliffs require more cliffs to be made. - make_bottom() - - update_icon() - -/obj/structure/cliff/proc/make_bottom() - // First, make sure there's room to put the bottom side. - var/turf/T = locate(x, y - 1, z) - if(!istype(T)) - return FALSE - - // Now make the bottom cliff have mostly the same variables. - var/obj/structure/cliff/bottom/bottom = new(T) - is_double_cliff = TRUE - climb_delay /= 2 // Since there are two cliffs to climb when going north, both take half the time. - - bottom.dir = dir - bottom.is_double_cliff = TRUE - bottom.climb_delay = climb_delay - bottom.icon_variant = icon_variant - bottom.corner = corner - bottom.ramp = ramp - bottom.layer = layer - 0.1 - bottom.density = density - bottom.update_icon() - -/obj/structure/cliff/set_dir(new_dir) - ..() - update_icon() - -/obj/structure/cliff/update_icon() - icon_state = "cliff-[dir][icon_variant][bottom ? "-bottom" : ""][corner ? "-corner" : ""][ramp ? "-ramp" : ""]" - - // Now for making the top-side look like a different turf. - var/turf/T = get_step(src, dir) - if(!istype(T)) - return - - var/subtraction_icon_state = "[icon_state]-subtract" - var/cache_string = "[icon_state]_[T.icon]_[T.icon_state]" - if(T && (subtraction_icon_state in cached_icon_states(icon))) - cut_overlays() - // If we've made the same icon before, just recycle it. - if(cache_string in GLOB.cliff_icon_cache) - add_overlay(GLOB.cliff_icon_cache[cache_string]) - - else // Otherwise make a new one, but only once. - var/icon/underlying_ground = icon(T.icon, T.icon_state, T.dir) - var/icon/subtract = icon(icon, subtraction_icon_state) - underlying_ground.Blend(subtract, ICON_SUBTRACT) - var/image/final = image(underlying_ground) - final.layer = src.layer - 0.2 - GLOB.cliff_icon_cache[cache_string] = final - add_overlay(final) - - -// Movement-related code. - -/obj/structure/cliff/CanPass(atom/movable/mover, turf/target) - if(isliving(mover)) - var/mob/living/L = mover - if(L.hovering || L.flying) // Flying mobs can always pass. - return TRUE - return ..() - - // Projectiles and objects flying 'upward' have a chance to hit the cliff instead, wasting the shot. - else if(istype(mover, /obj)) - var/obj/O = mover - if(check_shield_arc(src, dir, O)) // This is actually for mobs but it will work for our purposes as well. - if(prob(uphill_penalty / (1 + is_double_cliff) )) // Firing upwards facing NORTH means it will likely have to pass through two cliffs, so the chance is halved. - return FALSE - return TRUE - -/obj/structure/cliff/Bumped(atom/A) - if(isliving(A)) - var/mob/living/L = A - if(should_fall(L)) - fall_off_cliff(L) - return - ..() - -/obj/structure/cliff/proc/should_fall(mob/living/L) - if(L.hovering || L.flying) - return FALSE - - var/turf/T = get_turf(L) - if(T && get_dir(T, loc) & reverse_dir[dir]) // dir points 'up' the cliff, e.g. cliff pointing NORTH will cause someone to fall if moving SOUTH into it. - return TRUE - return FALSE - -/obj/structure/cliff/proc/fall_off_cliff(mob/living/L) - if(!istype(L)) - return FALSE - var/turf/T = get_step(src, reverse_dir[dir]) - var/displaced = FALSE - - if(dir in list(EAST, WEST)) // Apply an offset if flying sideways, to help maintain the illusion of depth. - for(var/i = 1 to 2) - var/turf/new_T = locate(T.x, T.y - i, T.z) - if(!new_T || locate(/obj/structure/cliff) in new_T) - break - T = new_T - displaced = TRUE - - if(istype(T)) - - var/safe_fall = FALSE - if(ishuman(L)) - var/mob/living/carbon/human/H = L - safe_fall = H.species.handle_falling(H, T, silent = TRUE, planetary = FALSE) - - if(safe_fall) - visible_message(span("notice", "\The [L] glides down from \the [src].")) - else - visible_message(span("danger", "\The [L] falls off \the [src]!")) - L.forceMove(T) - - var/harm = !is_double_cliff ? 1 : 0.5 - if(!safe_fall) - // Do the actual hurting. Double cliffs do halved damage due to them most likely hitting twice. - if(istype(L.buckled, /obj/vehicle)) // People falling off in vehicles will take less damage, but will damage the vehicle severely. - var/obj/vehicle/vehicle = L.buckled - vehicle.adjust_health(40 * harm) - to_chat(L, span("warning", "\The [vehicle] absorbs some of the impact, damaging it.")) - harm /= 2 - - playsound(L, 'sound/effects/break_stone.ogg', 70, 1) - L.Weaken(5 * harm) - - var/fall_time = 3 - if(displaced) // Make the fall look more natural when falling sideways. - L.pixel_z = 32 * 2 - animate(L, pixel_z = 0, time = fall_time) - sleep(fall_time) // A brief delay inbetween the two sounds helps sell the 'ouch' effect. - - if(safe_fall) - visible_message(span("notice", "\The [L] lands on \the [T].")) - playsound(L, "rustle", 25, 1) - return - - playsound(L, "punch", 70, 1) - shake_camera(L, 1, 1) - - visible_message(span("danger", "\The [L] hits \the [T]!")) - - // The bigger they are, the harder they fall. - // They will take at least 20 damage at the minimum, and tries to scale up to 40% of their max health. - // This scaling is capped at 100 total damage, which occurs if the thing that fell has more than 250 health. - var/damage = between(20, L.getMaxHealth() * 0.4, 100) - var/target_zone = ran_zone() - var/blocked = L.run_armor_check(target_zone, "melee") * harm - var/soaked = L.get_armor_soak(target_zone, "melee") * harm - - L.apply_damage(damage * harm, BRUTE, target_zone, blocked, soaked, used_weapon=src) - - // Now fall off more cliffs below this one if they exist. - var/obj/structure/cliff/bottom_cliff = locate() in T - if(bottom_cliff) - visible_message(span("danger", "\The [L] rolls down towards \the [bottom_cliff]!")) - sleep(5) - bottom_cliff.fall_off_cliff(L) - -/obj/structure/cliff/can_climb(mob/living/user, post_climb_check = FALSE) - // Cliff climbing requires climbing gear. - if(ishuman(user)) - var/mob/living/carbon/human/H = user - var/obj/item/clothing/shoes/shoes = H.shoes - if(shoes && shoes.rock_climbing) - return ..() // Do the other checks too. - - to_chat(user, span("warning", "\The [src] is too steep to climb unassisted.")) - return FALSE - -// This tells AI mobs to not be dumb and step off cliffs willingly. -/obj/structure/cliff/is_safe_to_step(mob/living/L) - if(should_fall(L)) - return FALSE - return ..() +GLOBAL_LIST_EMPTY(cliff_icon_cache) + +/* +Cliffs give a visual illusion of depth by seperating two places while presenting a 'top' and 'bottom' side. + +Mobs moving into a cliff from the bottom side will simply bump into it and be denied moving into the tile, +where as mobs moving into a cliff from the top side will 'fall' off the cliff, forcing them to the bottom, causing significant damage and stunning them. + +Mobs can climb this while wearing climbing equipment by clickdragging themselves onto a cliff, as if it were a table. + +Flying mobs can pass over all cliffs with no risk of falling. + +Projectiles and thrown objects can pass, however if moving upwards, there is a chance for it to be stopped by the cliff. +This makes fighting something that is on top of a cliff more challenging. + +As a note, dir points upwards, e.g. pointing WEST means the left side is 'up', and the right side is 'down'. + +When mapping these in, be sure to give at least a one tile clearance, as NORTH facing cliffs expand to +two tiles on initialization, and which way a cliff is facing may change during maploading. +*/ + +/obj/structure/cliff + name = "cliff" + desc = "A steep rock ledge. You might be able to climb it if you feel bold enough." + description_info = "Walking off the edge of a cliff while on top will cause you to fall off, causing severe injury.
                    \ + You can climb this cliff if wearing special climbing equipment, by click-dragging yourself onto the cliff.
                    \ + Projectiles traveling up a cliff may hit the cliff instead, making it more difficult to fight something \ + on top." + icon = 'icons/obj/flora/rocks.dmi' + + anchored = TRUE + density = TRUE + opacity = FALSE + climbable = TRUE + climb_delay = 10 SECONDS + unacidable = TRUE + block_turf_edges = TRUE // Don't want turf edges popping up from the cliff edge. + plane = TURF_PLANE + + var/icon_variant = null // Used to make cliffs less repeative by having a selection of sprites to display. + var/corner = FALSE // Used for icon things. + var/ramp = FALSE // Ditto. + var/bottom = FALSE // Used for 'bottom' typed cliffs, to avoid infinite cliffs, and for icons. + + var/is_double_cliff = FALSE // Set to true when making the two-tile cliffs, used for projectile checks. + var/uphill_penalty = 30 // Odds of a projectile not making it up the cliff. + +/obj/structure/cliff/Initialize() + . = ..() + register_dangerous_to_step() + +/obj/structure/cliff/Destroy() + unregister_dangerous_to_step() + . = ..() + +/obj/structure/cliff/Moved(atom/oldloc) + . = ..() + if(.) + var/turf/old_turf = get_turf(oldloc) + var/turf/new_turf = get_turf(src) + if(old_turf != new_turf) + old_turf.unregister_dangerous_object(src) + new_turf.register_dangerous_object(src) + +// These arrange their sprites at runtime, as opposed to being statically placed in the map file. +/obj/structure/cliff/automatic + icon_state = "cliffbuilder" + dir = NORTH + +/obj/structure/cliff/automatic/corner + icon_state = "cliffbuilder-corner" + dir = NORTHEAST + corner = TRUE + +// Tiny part that doesn't block, used for making 'ramps'. +/obj/structure/cliff/automatic/ramp + icon_state = "cliffbuilder-ramp" + dir = NORTHEAST + density = FALSE + ramp = TRUE + +// Made automatically as needed by automatic cliffs. +/obj/structure/cliff/bottom + bottom = TRUE + +/obj/structure/cliff/automatic/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD + +// Paranoid about the maploader, direction is very important to cliffs, since they may get bigger if initialized while facing NORTH. +/obj/structure/cliff/automatic/LateInitialize() + if(dir in GLOB.cardinal) + icon_variant = pick("a", "b", "c") + + if(dir & NORTH && !bottom) // North-facing cliffs require more cliffs to be made. + make_bottom() + + update_icon() + +/obj/structure/cliff/proc/make_bottom() + // First, make sure there's room to put the bottom side. + var/turf/T = locate(x, y - 1, z) + if(!istype(T)) + return FALSE + + // Now make the bottom cliff have mostly the same variables. + var/obj/structure/cliff/bottom/bottom = new(T) + is_double_cliff = TRUE + climb_delay /= 2 // Since there are two cliffs to climb when going north, both take half the time. + + bottom.dir = dir + bottom.is_double_cliff = TRUE + bottom.climb_delay = climb_delay + bottom.icon_variant = icon_variant + bottom.corner = corner + bottom.ramp = ramp + bottom.layer = layer - 0.1 + bottom.density = density + bottom.update_icon() + +/obj/structure/cliff/set_dir(new_dir) + ..() + update_icon() + +/obj/structure/cliff/update_icon() + icon_state = "cliff-[dir][icon_variant][bottom ? "-bottom" : ""][corner ? "-corner" : ""][ramp ? "-ramp" : ""]" + + // Now for making the top-side look like a different turf. + var/turf/T = get_step(src, dir) + if(!istype(T)) + return + + var/subtraction_icon_state = "[icon_state]-subtract" + var/cache_string = "[icon_state]_[T.icon]_[T.icon_state]" + if(T && (subtraction_icon_state in cached_icon_states(icon))) + cut_overlays() + // If we've made the same icon before, just recycle it. + if(cache_string in GLOB.cliff_icon_cache) + add_overlay(GLOB.cliff_icon_cache[cache_string]) + + else // Otherwise make a new one, but only once. + var/icon/underlying_ground = icon(T.icon, T.icon_state, T.dir) + var/icon/subtract = icon(icon, subtraction_icon_state) + underlying_ground.Blend(subtract, ICON_SUBTRACT) + var/image/final = image(underlying_ground) + final.layer = src.layer - 0.2 + GLOB.cliff_icon_cache[cache_string] = final + add_overlay(final) + + +// Movement-related code. + +/obj/structure/cliff/CanPass(atom/movable/mover, turf/target) + if(isliving(mover)) + var/mob/living/L = mover + if(L.hovering || L.flying) // Flying mobs can always pass. + return TRUE + return ..() + + // Projectiles and objects flying 'upward' have a chance to hit the cliff instead, wasting the shot. + else if(istype(mover, /obj)) + var/obj/O = mover + if(check_shield_arc(src, dir, O)) // This is actually for mobs but it will work for our purposes as well. + if(prob(uphill_penalty / (1 + is_double_cliff) )) // Firing upwards facing NORTH means it will likely have to pass through two cliffs, so the chance is halved. + return FALSE + return TRUE + +/obj/structure/cliff/Bumped(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(should_fall(L)) + fall_off_cliff(L) + return + ..() + +/obj/structure/cliff/proc/should_fall(mob/living/L) + if(L.hovering || L.flying) + return FALSE + + var/turf/T = get_turf(L) + if(T && get_dir(T, loc) & reverse_dir[dir]) // dir points 'up' the cliff, e.g. cliff pointing NORTH will cause someone to fall if moving SOUTH into it. + return TRUE + return FALSE + +/obj/structure/cliff/proc/fall_off_cliff(mob/living/L) + if(!istype(L)) + return FALSE + var/turf/T = get_step(src, reverse_dir[dir]) + var/displaced = FALSE + + if(dir in list(EAST, WEST)) // Apply an offset if flying sideways, to help maintain the illusion of depth. + for(var/i = 1 to 2) + var/turf/new_T = locate(T.x, T.y - i, T.z) + if(!new_T || locate(/obj/structure/cliff) in new_T) + break + T = new_T + displaced = TRUE + + if(istype(T)) + + var/safe_fall = FALSE + if(ishuman(L)) + var/mob/living/carbon/human/H = L + safe_fall = H.species.handle_falling(H, T, silent = TRUE, planetary = FALSE) + + if(safe_fall) + visible_message(span("notice", "\The [L] glides down from \the [src].")) + else + visible_message(span("danger", "\The [L] falls off \the [src]!")) + L.forceMove(T) + + var/harm = !is_double_cliff ? 1 : 0.5 + if(!safe_fall) + // Do the actual hurting. Double cliffs do halved damage due to them most likely hitting twice. + if(istype(L.buckled, /obj/vehicle)) // People falling off in vehicles will take less damage, but will damage the vehicle severely. + var/obj/vehicle/vehicle = L.buckled + vehicle.adjust_health(40 * harm) + to_chat(L, span("warning", "\The [vehicle] absorbs some of the impact, damaging it.")) + harm /= 2 + + playsound(L, 'sound/effects/break_stone.ogg', 70, 1) + L.Weaken(5 * harm) + + var/fall_time = 3 + if(displaced) // Make the fall look more natural when falling sideways. + L.pixel_z = 32 * 2 + animate(L, pixel_z = 0, time = fall_time) + sleep(fall_time) // A brief delay inbetween the two sounds helps sell the 'ouch' effect. + + if(safe_fall) + visible_message(span("notice", "\The [L] lands on \the [T].")) + playsound(L, "rustle", 25, 1) + return + + playsound(L, "punch", 70, 1) + shake_camera(L, 1, 1) + + visible_message(span("danger", "\The [L] hits \the [T]!")) + + // The bigger they are, the harder they fall. + // They will take at least 20 damage at the minimum, and tries to scale up to 40% of their max health. + // This scaling is capped at 100 total damage, which occurs if the thing that fell has more than 250 health. + var/damage = between(20, L.getMaxHealth() * 0.4, 100) + var/target_zone = ran_zone() + var/blocked = L.run_armor_check(target_zone, "melee") * harm + var/soaked = L.get_armor_soak(target_zone, "melee") * harm + + L.apply_damage(damage * harm, BRUTE, target_zone, blocked, soaked, used_weapon=src) + + // Now fall off more cliffs below this one if they exist. + var/obj/structure/cliff/bottom_cliff = locate() in T + if(bottom_cliff) + visible_message(span("danger", "\The [L] rolls down towards \the [bottom_cliff]!")) + sleep(5) + bottom_cliff.fall_off_cliff(L) + +/obj/structure/cliff/can_climb(mob/living/user, post_climb_check = FALSE) + // Cliff climbing requires climbing gear. + if(ishuman(user)) + var/mob/living/carbon/human/H = user + var/obj/item/clothing/shoes/shoes = H.shoes + if(shoes && shoes.rock_climbing) + return ..() // Do the other checks too. + + to_chat(user, span("warning", "\The [src] is too steep to climb unassisted.")) + return FALSE + +// This tells AI mobs to not be dumb and step off cliffs willingly. +/obj/structure/cliff/is_safe_to_step(mob/living/L) + if(should_fall(L)) + return FALSE + return ..() diff --git a/code/game/objects/structures/crates_lockers/closets/coffin.dm b/code/game/objects/structures/crates_lockers/closets/coffin.dm index e5a9c631a9f..43098084d72 100644 --- a/code/game/objects/structures/crates_lockers/closets/coffin.dm +++ b/code/game/objects/structures/crates_lockers/closets/coffin.dm @@ -1,164 +1,164 @@ -/obj/structure/closet/coffin - name = "coffin" - desc = "It's a burial receptacle for the dearly departed." - icon = 'icons/obj/closets/coffin.dmi' - - icon_state = "closed_unlocked" - seal_tool = /obj/item/weapon/tool/screwdriver - breakout_sound = 'sound/weapons/tablehit1.ogg' - closet_appearance = null // Special icon for us - -/* Graves */ -/obj/structure/closet/grave - name = "grave" - desc = "Dirt." - icon = 'icons/obj/closets/grave.dmi' - icon_state = "closed_unlocked" - seal_tool = null - breakout_sound = 'sound/weapons/thudswoosh.ogg' - anchored = TRUE - max_closets = 1 - opened = 1 - closet_appearance = null // Special icon for us - open_sound = 'sound/effects/wooden_closet_open.ogg' - close_sound = 'sound/effects/wooden_closet_close.ogg' - -/obj/structure/closet/grave/attack_hand(mob/user as mob) - if(opened) - visible_message("[user] starts to climb into \the [src.name].", \ - "You start to lower yourself into \the [src.name].") - if(do_after(user, 50)) - user.forceMove(src.loc) - visible_message("[user] climbs into \the [src.name].", \ - "You climb into \the [src.name].") - else - visible_message("[user] decides not to climb into \the [src.name].", \ - "You stop climbing into \the [src.name].") - return - -/obj/structure/closet/grave/CanPass(atom/movable/mover, turf/target) - if(opened && ismob(mover)) - var/mob/M = mover - add_fingerprint(M) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.m_intent == "walk") - to_chat(H, "You stop at the edge of \the [src.name].") - return FALSE - else - to_chat(H, "You fall into \the [src.name]!") - fall_in(H) - return TRUE - if(isrobot(M)) - var/mob/living/silicon/robot/R = M - if(R.a_intent == I_HELP) - to_chat(R, "You stop at the edge of \the [src.name].") - return FALSE - else - to_chat(R, "You enter \the [src.name].") - return TRUE - return TRUE //Everything else can move over the graves - -/obj/structure/closet/grave/proc/fall_in(mob/living/L) //Only called on humans for now, but still - L.Weaken(5) - if(ishuman(L)) - var/mob/living/carbon/human/H = L - var/limb_damage = rand(5,25) - H.adjustBruteLoss(limb_damage) - -/obj/structure/closet/grave/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(src.opened) - if(istype(W, /obj/item/weapon/shovel)) - user.visible_message("[user] piles dirt into \the [src.name].", \ - "You start to pile dirt into \the [src.name].", \ - "You hear dirt being moved.") - if(do_after(user, 40 * W.toolspeed)) - user.visible_message("[user] pats down the dirt on top of \the [src.name].", \ - "You finish filling in \the [src.name].") - close() - return - else - user.visible_message("[user] stops filling in \the [src.name].", \ - "You change your mind and stop filling in \the [src.name].") - return - if(istype(W, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/G = W - src.MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet - return 0 - if(istype(W,/obj/item/tk_grab)) - return 0 - if(istype(W, /obj/item/weapon/storage/laundry_basket) && W.contents.len) - var/obj/item/weapon/storage/laundry_basket/LB = W - var/turf/T = get_turf(src) - for(var/obj/item/I in LB.contents) - LB.remove_from_storage(I, T) - user.visible_message("[user] empties \the [LB] into \the [src].", \ - "You empty \the [LB] into \the [src].", \ - "You hear rustling of clothes.") - return - if(isrobot(user)) - return - if(W.loc != user) // This should stop mounted modules ending up outside the module. - return - usr.drop_item() - if(W) - W.forceMove(src.loc) - else - if(istype(W, /obj/item/weapon/shovel)) - if(user.a_intent == I_HURT) // Hurt intent means you're trying to kill someone, or just get rid of the grave - user.visible_message("[user] begins to smoothe out the dirt of \the [src.name].", \ - "You start to smoothe out the dirt of \the [src.name].", \ - "You hear dirt being moved.") - if(do_after(user, 40 * W.toolspeed)) - user.visible_message("[user] finishes smoothing out \the [src.name].", \ - "You finish smoothing out \the [src.name].") - if(LAZYLEN(contents)) - alpha = 40 // If we've got stuff inside, like maybe a person, just make it hard to see us - else - qdel(src) // Else, go away - return - else - user.visible_message("[user] stops concealing \the [src.name].", \ - "You stop concealing \the [src.name].") - return - else - user.visible_message("[user] begins to unearth \the [src.name].", \ - "You start to unearth \the [src.name].", \ - "You hear dirt being moved.") - if(do_after(user, 40 * W.toolspeed)) - user.visible_message("[user] reaches the bottom of \the [src.name].", \ - "You finish digging out \the [src.name].") - break_open() - return - else - user.visible_message("[user] stops digging out \the [src.name].", \ - "You stop digging out \the [src.name].") - return - return - -/obj/structure/closet/grave/close() - ..() - if(!opened) - sealed = TRUE - -/obj/structure/closet/grave/open() - .=..() - alpha = 255 // Needed because of grave hiding - -/obj/structure/closet/grave/bullet_act(var/obj/item/projectile/P) - return PROJECTILE_CONTINUE // It's a hole in the ground, doesn't usually stop or even care about bullets - -/obj/structure/closet/grave/return_air_for_internal_lifeform(var/mob/living/L) - var/gasid = "carbon_dioxide" - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.species && H.species.exhale_type) - gasid = H.species.exhale_type - var/datum/gas_mixture/grave_breath = new() - var/datum/gas_mixture/above_air = return_air() - grave_breath.adjust_gas(gasid, BREATH_MOLES) - grave_breath.temperature = (above_air.temperature) - 30 //Underground - return grave_breath - -/obj/structure/closet/grave/dirthole - name = "hole" +/obj/structure/closet/coffin + name = "coffin" + desc = "It's a burial receptacle for the dearly departed." + icon = 'icons/obj/closets/coffin.dmi' + + icon_state = "closed_unlocked" + seal_tool = /obj/item/weapon/tool/screwdriver + breakout_sound = 'sound/weapons/tablehit1.ogg' + closet_appearance = null // Special icon for us + +/* Graves */ +/obj/structure/closet/grave + name = "grave" + desc = "Dirt." + icon = 'icons/obj/closets/grave.dmi' + icon_state = "closed_unlocked" + seal_tool = null + breakout_sound = 'sound/weapons/thudswoosh.ogg' + anchored = TRUE + max_closets = 1 + opened = 1 + closet_appearance = null // Special icon for us + open_sound = 'sound/effects/wooden_closet_open.ogg' + close_sound = 'sound/effects/wooden_closet_close.ogg' + +/obj/structure/closet/grave/attack_hand(mob/user as mob) + if(opened) + visible_message("[user] starts to climb into \the [src.name].", \ + "You start to lower yourself into \the [src.name].") + if(do_after(user, 50)) + user.forceMove(src.loc) + visible_message("[user] climbs into \the [src.name].", \ + "You climb into \the [src.name].") + else + visible_message("[user] decides not to climb into \the [src.name].", \ + "You stop climbing into \the [src.name].") + return + +/obj/structure/closet/grave/CanPass(atom/movable/mover, turf/target) + if(opened && ismob(mover)) + var/mob/M = mover + add_fingerprint(M) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.m_intent == "walk") + to_chat(H, "You stop at the edge of \the [src.name].") + return FALSE + else + to_chat(H, "You fall into \the [src.name]!") + fall_in(H) + return TRUE + if(isrobot(M)) + var/mob/living/silicon/robot/R = M + if(R.a_intent == I_HELP) + to_chat(R, "You stop at the edge of \the [src.name].") + return FALSE + else + to_chat(R, "You enter \the [src.name].") + return TRUE + return TRUE //Everything else can move over the graves + +/obj/structure/closet/grave/proc/fall_in(mob/living/L) //Only called on humans for now, but still + L.Weaken(5) + if(ishuman(L)) + var/mob/living/carbon/human/H = L + var/limb_damage = rand(5,25) + H.adjustBruteLoss(limb_damage) + +/obj/structure/closet/grave/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(src.opened) + if(istype(W, /obj/item/weapon/shovel)) + user.visible_message("[user] piles dirt into \the [src.name].", \ + "You start to pile dirt into \the [src.name].", \ + "You hear dirt being moved.") + if(do_after(user, 40 * W.toolspeed)) + user.visible_message("[user] pats down the dirt on top of \the [src.name].", \ + "You finish filling in \the [src.name].") + close() + return + else + user.visible_message("[user] stops filling in \the [src.name].", \ + "You change your mind and stop filling in \the [src.name].") + return + if(istype(W, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = W + src.MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet + return 0 + if(istype(W,/obj/item/tk_grab)) + return 0 + if(istype(W, /obj/item/weapon/storage/laundry_basket) && W.contents.len) + var/obj/item/weapon/storage/laundry_basket/LB = W + var/turf/T = get_turf(src) + for(var/obj/item/I in LB.contents) + LB.remove_from_storage(I, T) + user.visible_message("[user] empties \the [LB] into \the [src].", \ + "You empty \the [LB] into \the [src].", \ + "You hear rustling of clothes.") + return + if(isrobot(user)) + return + if(W.loc != user) // This should stop mounted modules ending up outside the module. + return + usr.drop_item() + if(W) + W.forceMove(src.loc) + else + if(istype(W, /obj/item/weapon/shovel)) + if(user.a_intent == I_HURT) // Hurt intent means you're trying to kill someone, or just get rid of the grave + user.visible_message("[user] begins to smoothe out the dirt of \the [src.name].", \ + "You start to smoothe out the dirt of \the [src.name].", \ + "You hear dirt being moved.") + if(do_after(user, 40 * W.toolspeed)) + user.visible_message("[user] finishes smoothing out \the [src.name].", \ + "You finish smoothing out \the [src.name].") + if(LAZYLEN(contents)) + alpha = 40 // If we've got stuff inside, like maybe a person, just make it hard to see us + else + qdel(src) // Else, go away + return + else + user.visible_message("[user] stops concealing \the [src.name].", \ + "You stop concealing \the [src.name].") + return + else + user.visible_message("[user] begins to unearth \the [src.name].", \ + "You start to unearth \the [src.name].", \ + "You hear dirt being moved.") + if(do_after(user, 40 * W.toolspeed)) + user.visible_message("[user] reaches the bottom of \the [src.name].", \ + "You finish digging out \the [src.name].") + break_open() + return + else + user.visible_message("[user] stops digging out \the [src.name].", \ + "You stop digging out \the [src.name].") + return + return + +/obj/structure/closet/grave/close() + ..() + if(!opened) + sealed = TRUE + +/obj/structure/closet/grave/open() + .=..() + alpha = 255 // Needed because of grave hiding + +/obj/structure/closet/grave/bullet_act(var/obj/item/projectile/P) + return PROJECTILE_CONTINUE // It's a hole in the ground, doesn't usually stop or even care about bullets + +/obj/structure/closet/grave/return_air_for_internal_lifeform(var/mob/living/L) + var/gasid = "carbon_dioxide" + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.species && H.species.exhale_type) + gasid = H.species.exhale_type + var/datum/gas_mixture/grave_breath = new() + var/datum/gas_mixture/above_air = return_air() + grave_breath.adjust_gas(gasid, BREATH_MOLES) + grave_breath.temperature = (above_air.temperature) - 30 //Underground + return grave_breath + +/obj/structure/closet/grave/dirthole + name = "hole" diff --git a/code/game/objects/structures/crates_lockers/closets/crittercrate.dm b/code/game/objects/structures/crates_lockers/closets/crittercrate.dm index 6b0196168b5..d649f865706 100644 --- a/code/game/objects/structures/crates_lockers/closets/crittercrate.dm +++ b/code/game/objects/structures/crates_lockers/closets/crittercrate.dm @@ -1,4 +1,4 @@ -/obj/structure/closet/crate/critter - name = "critter crate" - desc = "A crate which can sustain life for a while." - closet_appearance = /decl/closet_appearance/large_crate/critter +/obj/structure/closet/crate/critter + name = "critter crate" + desc = "A crate which can sustain life for a while." + closet_appearance = /decl/closet_appearance/large_crate/critter diff --git a/code/game/objects/structures/crates_lockers/closets/fitness.dm b/code/game/objects/structures/crates_lockers/closets/fitness.dm index 6b104f7cac6..c2243e42d16 100644 --- a/code/game/objects/structures/crates_lockers/closets/fitness.dm +++ b/code/game/objects/structures/crates_lockers/closets/fitness.dm @@ -1,120 +1,120 @@ -/obj/structure/closet/athletic_mixed - name = "athletic wardrobe" - desc = "It's a storage unit for athletic wear." - closet_appearance = /decl/closet_appearance/wardrobe/mixed - - starts_with = list( - /obj/item/clothing/under/shorts/grey, - /obj/item/clothing/under/shorts/black, - /obj/item/clothing/under/shorts/red, - /obj/item/clothing/under/shorts/blue, - /obj/item/clothing/under/shorts/green, - /obj/item/clothing/under/shorts/white, - /obj/item/clothing/suit/storage/toggle/track, - /obj/item/clothing/suit/storage/toggle/track/blue, - /obj/item/clothing/suit/storage/toggle/track/green, - /obj/item/clothing/suit/storage/toggle/track/red, - /obj/item/clothing/suit/storage/toggle/track/white, - /obj/item/clothing/under/pants/track, - /obj/item/clothing/under/pants/track/blue, - /obj/item/clothing/under/pants/track/green, - /obj/item/clothing/under/pants/track/white, - /obj/item/clothing/under/pants/track/red, - /obj/item/clothing/shoes/athletic = 2, - /obj/item/clothing/shoes/hitops, - /obj/item/clothing/shoes/hitops/red, - /obj/item/clothing/shoes/hitops/black, - /obj/item/clothing/shoes/hitops/blue - ) - -/obj/structure/closet/athletic_swimwear - name = "athletic wardrobe" - desc = "It's a storage unit for swimwear." - closet_appearance = /decl/closet_appearance/wardrobe/mixed - - starts_with = list( - /obj/item/clothing/under/shorts/grey, - /obj/item/clothing/under/shorts/black, - /obj/item/clothing/under/shorts/red, - /obj/item/clothing/under/shorts/blue, - /obj/item/clothing/under/shorts/green, - /obj/item/clothing/under/swimsuit/red, - /obj/item/clothing/under/swimsuit/black, - /obj/item/clothing/under/swimsuit/blue, - /obj/item/clothing/under/swimsuit/green, - /obj/item/clothing/under/swimsuit/purple, - /obj/item/clothing/under/swimsuit/striped, - /obj/item/clothing/under/swimsuit/white, - /obj/item/clothing/under/swimsuit/earth, - /obj/item/clothing/under/wetsuit, - /obj/item/clothing/under/wetsuit_rec, - /obj/item/clothing/under/wetsuit_skimpy, - /obj/item/clothing/mask/snorkel = 2, - /obj/item/clothing/shoes/swimmingfins = 2) - -/obj/structure/closet/boxinggloves - name = "boxing gloves" - desc = "It's a storage unit for gloves for use in the boxing ring." - closet_appearance = /decl/closet_appearance/wardrobe/mixed - - starts_with = list( - /obj/item/clothing/gloves/boxing/blue, - /obj/item/clothing/gloves/boxing/green, - /obj/item/clothing/gloves/boxing/yellow, - /obj/item/clothing/gloves/boxing) - -/obj/structure/closet/masks - name = "mask closet" - desc = "IT'S A STORAGE UNIT FOR FIGHTER MASKS OLE!" - closet_appearance = /decl/closet_appearance/wardrobe/mixed - - starts_with = list( - /obj/item/clothing/mask/luchador, - /obj/item/clothing/mask/luchador/rudos, - /obj/item/clothing/mask/luchador/tecnicos) - - -/obj/structure/closet/lasertag/red - name = "red laser tag equipment" - desc = "It's a storage unit for laser tag equipment." - closet_appearance = /decl/closet_appearance/wardrobe/red - - starts_with = list( - /obj/item/weapon/gun/energy/lasertag/red = 5, - /obj/item/clothing/suit/redtag = 5) - - -/obj/structure/closet/lasertag/blue - name = "blue laser tag equipment" - desc = "It's a storage unit for laser tag equipment." - closet_appearance = /decl/closet_appearance/wardrobe/blue - - starts_with = list( - /obj/item/weapon/gun/energy/lasertag/blue = 5, - /obj/item/clothing/suit/bluetag = 5) - -/obj/structure/closet/lasertag/red/laserdome - name = "red team laserdome equipment" - desc = "It's a storage unit for laser tag equipment." - closet_appearance = /decl/closet_appearance/wardrobe/red - - starts_with = list( - /obj/item/device/encryptionkey/ent = 3, - /obj/item/clothing/gloves/bluespace = 3, - /obj/item/clothing/under/color/red = 3, - /obj/item/weapon/gun/energy/lasertag/red = 3, - /obj/item/clothing/head/redtag = 3, - /obj/item/clothing/suit/redtag = 3) - -/obj/structure/closet/lasertag/blue/laserdome - name = "blue team laserdome equipment" - desc = "It's a storage unit for laser tag equipment." - closet_appearance = /decl/closet_appearance/wardrobe/blue - - starts_with = list( - /obj/item/device/encryptionkey/ent = 3, - /obj/item/clothing/gloves/bluespace = 3, - /obj/item/clothing/under/color/blue = 3, - /obj/item/weapon/gun/energy/lasertag/blue = 3, - /obj/item/clothing/head/bluetag = 3, - /obj/item/clothing/suit/bluetag = 3) +/obj/structure/closet/athletic_mixed + name = "athletic wardrobe" + desc = "It's a storage unit for athletic wear." + closet_appearance = /decl/closet_appearance/wardrobe/mixed + + starts_with = list( + /obj/item/clothing/under/shorts/grey, + /obj/item/clothing/under/shorts/black, + /obj/item/clothing/under/shorts/red, + /obj/item/clothing/under/shorts/blue, + /obj/item/clothing/under/shorts/green, + /obj/item/clothing/under/shorts/white, + /obj/item/clothing/suit/storage/toggle/track, + /obj/item/clothing/suit/storage/toggle/track/blue, + /obj/item/clothing/suit/storage/toggle/track/green, + /obj/item/clothing/suit/storage/toggle/track/red, + /obj/item/clothing/suit/storage/toggle/track/white, + /obj/item/clothing/under/pants/track, + /obj/item/clothing/under/pants/track/blue, + /obj/item/clothing/under/pants/track/green, + /obj/item/clothing/under/pants/track/white, + /obj/item/clothing/under/pants/track/red, + /obj/item/clothing/shoes/athletic = 2, + /obj/item/clothing/shoes/hitops, + /obj/item/clothing/shoes/hitops/red, + /obj/item/clothing/shoes/hitops/black, + /obj/item/clothing/shoes/hitops/blue + ) + +/obj/structure/closet/athletic_swimwear + name = "athletic wardrobe" + desc = "It's a storage unit for swimwear." + closet_appearance = /decl/closet_appearance/wardrobe/mixed + + starts_with = list( + /obj/item/clothing/under/shorts/grey, + /obj/item/clothing/under/shorts/black, + /obj/item/clothing/under/shorts/red, + /obj/item/clothing/under/shorts/blue, + /obj/item/clothing/under/shorts/green, + /obj/item/clothing/under/swimsuit/red, + /obj/item/clothing/under/swimsuit/black, + /obj/item/clothing/under/swimsuit/blue, + /obj/item/clothing/under/swimsuit/green, + /obj/item/clothing/under/swimsuit/purple, + /obj/item/clothing/under/swimsuit/striped, + /obj/item/clothing/under/swimsuit/white, + /obj/item/clothing/under/swimsuit/earth, + /obj/item/clothing/under/wetsuit, + /obj/item/clothing/under/wetsuit_rec, + /obj/item/clothing/under/wetsuit_skimpy, + /obj/item/clothing/mask/snorkel = 2, + /obj/item/clothing/shoes/swimmingfins = 2) + +/obj/structure/closet/boxinggloves + name = "boxing gloves" + desc = "It's a storage unit for gloves for use in the boxing ring." + closet_appearance = /decl/closet_appearance/wardrobe/mixed + + starts_with = list( + /obj/item/clothing/gloves/boxing/blue, + /obj/item/clothing/gloves/boxing/green, + /obj/item/clothing/gloves/boxing/yellow, + /obj/item/clothing/gloves/boxing) + +/obj/structure/closet/masks + name = "mask closet" + desc = "IT'S A STORAGE UNIT FOR FIGHTER MASKS OLE!" + closet_appearance = /decl/closet_appearance/wardrobe/mixed + + starts_with = list( + /obj/item/clothing/mask/luchador, + /obj/item/clothing/mask/luchador/rudos, + /obj/item/clothing/mask/luchador/tecnicos) + + +/obj/structure/closet/lasertag/red + name = "red laser tag equipment" + desc = "It's a storage unit for laser tag equipment." + closet_appearance = /decl/closet_appearance/wardrobe/red + + starts_with = list( + /obj/item/weapon/gun/energy/lasertag/red = 5, + /obj/item/clothing/suit/redtag = 5) + + +/obj/structure/closet/lasertag/blue + name = "blue laser tag equipment" + desc = "It's a storage unit for laser tag equipment." + closet_appearance = /decl/closet_appearance/wardrobe/blue + + starts_with = list( + /obj/item/weapon/gun/energy/lasertag/blue = 5, + /obj/item/clothing/suit/bluetag = 5) + +/obj/structure/closet/lasertag/red/laserdome + name = "red team laserdome equipment" + desc = "It's a storage unit for laser tag equipment." + closet_appearance = /decl/closet_appearance/wardrobe/red + + starts_with = list( + /obj/item/device/encryptionkey/ent = 3, + /obj/item/clothing/gloves/bluespace = 3, + /obj/item/clothing/under/color/red = 3, + /obj/item/weapon/gun/energy/lasertag/red = 3, + /obj/item/clothing/head/redtag = 3, + /obj/item/clothing/suit/redtag = 3) + +/obj/structure/closet/lasertag/blue/laserdome + name = "blue team laserdome equipment" + desc = "It's a storage unit for laser tag equipment." + closet_appearance = /decl/closet_appearance/wardrobe/blue + + starts_with = list( + /obj/item/device/encryptionkey/ent = 3, + /obj/item/clothing/gloves/bluespace = 3, + /obj/item/clothing/under/color/blue = 3, + /obj/item/weapon/gun/energy/lasertag/blue = 3, + /obj/item/clothing/head/bluetag = 3, + /obj/item/clothing/suit/bluetag = 3) diff --git a/code/game/objects/structures/crates_lockers/closets/gimmick.dm b/code/game/objects/structures/crates_lockers/closets/gimmick.dm index 0e27bc97881..f79d770f742 100644 --- a/code/game/objects/structures/crates_lockers/closets/gimmick.dm +++ b/code/game/objects/structures/crates_lockers/closets/gimmick.dm @@ -1,84 +1,84 @@ -/obj/structure/closet/cabinet - name = "cabinet" - desc = "Old will forever be in fashion." - icon = 'icons/obj/closets/bases/cabinet.dmi' - closet_appearance = /decl/closet_appearance/cabinet - - open_sound = 'sound/effects/wooden_closet_open.ogg' - close_sound = 'sound/effects/wooden_closet_close.ogg' - -/obj/structure/closet/acloset - name = "strange closet" - desc = "It looks alien!" - closet_appearance = /decl/closet_appearance/alien - - open_sound = 'sound/machines/click.ogg' - close_sound = 'sound/machines/click.ogg' - -/obj/structure/closet/gimmick - name = "administrative supply closet" - desc = "It's a storage unit for things that have no right being here." - closet_appearance = /decl/closet_appearance/tactical - anchored = FALSE - -/obj/structure/closet/gimmick/russian - name = "russian surplus closet" - desc = "It's a storage unit for Russian standard-issue surplus." - closet_appearance = /decl/closet_appearance/tactical - - starts_with = list( - /obj/item/clothing/head/ushanka = 5, - /obj/item/clothing/under/soviet = 5) - - -/obj/structure/closet/gimmick/tacticool - name = "tacticool gear closet" - desc = "It's a storage unit for Tacticool gear." - closet_appearance = /decl/closet_appearance/tactical - - starts_with = list( - /obj/item/clothing/glasses/eyepatch, - /obj/item/clothing/glasses/sunglasses, - /obj/item/clothing/gloves/swat = 2, - /obj/item/clothing/head/helmet/swat = 2, - /obj/item/clothing/mask/gas = 2, - /obj/item/clothing/shoes/boots/swat = 2, - /obj/item/clothing/suit/armor/swat = 2, - /obj/item/clothing/under/syndicate/tacticool = 2) - - -/obj/structure/closet/thunderdome - name = "\improper Thunderdome closet" - desc = "Everything you need!" - closet_appearance = /decl/closet_appearance/thunderdomered - anchored = TRUE - -/obj/structure/closet/thunderdome/tdred - name = "red-team Thunderdome closet" - - starts_with = list( - /obj/item/clothing/suit/armor/tdome/red = 3, - /obj/item/weapon/melee/energy/sword = 3, - /obj/item/weapon/gun/energy/laser = 3, - /obj/item/weapon/melee/baton = 3, - /obj/item/weapon/storage/box/flashbangs = 3, - /obj/item/clothing/head/helmet/thunderdome = 3) - -/obj/structure/closet/thunderdome/tdgreen - name = "green-team Thunderdome closet" - closet_appearance = /decl/closet_appearance/thunderdomegreen - - starts_with = list( - /obj/item/clothing/suit/armor/tdome/green = 3, - /obj/item/weapon/melee/energy/sword = 3, - /obj/item/weapon/gun/energy/laser = 3, - /obj/item/weapon/melee/baton = 3, - /obj/item/weapon/storage/box/flashbangs = 3, - /obj/item/clothing/head/helmet/thunderdome = 3) - -/obj/structure/closet/alien - name = "alien container" - desc = "Contains secrets of the universe." - icon = 'icons/obj/closets/abductor.dmi' - anchored = TRUE - closet_appearance = null // special icons +/obj/structure/closet/cabinet + name = "cabinet" + desc = "Old will forever be in fashion." + icon = 'icons/obj/closets/bases/cabinet.dmi' + closet_appearance = /decl/closet_appearance/cabinet + + open_sound = 'sound/effects/wooden_closet_open.ogg' + close_sound = 'sound/effects/wooden_closet_close.ogg' + +/obj/structure/closet/acloset + name = "strange closet" + desc = "It looks alien!" + closet_appearance = /decl/closet_appearance/alien + + open_sound = 'sound/machines/click.ogg' + close_sound = 'sound/machines/click.ogg' + +/obj/structure/closet/gimmick + name = "administrative supply closet" + desc = "It's a storage unit for things that have no right being here." + closet_appearance = /decl/closet_appearance/tactical + anchored = FALSE + +/obj/structure/closet/gimmick/russian + name = "russian surplus closet" + desc = "It's a storage unit for Russian standard-issue surplus." + closet_appearance = /decl/closet_appearance/tactical + + starts_with = list( + /obj/item/clothing/head/ushanka = 5, + /obj/item/clothing/under/soviet = 5) + + +/obj/structure/closet/gimmick/tacticool + name = "tacticool gear closet" + desc = "It's a storage unit for Tacticool gear." + closet_appearance = /decl/closet_appearance/tactical + + starts_with = list( + /obj/item/clothing/glasses/eyepatch, + /obj/item/clothing/glasses/sunglasses, + /obj/item/clothing/gloves/swat = 2, + /obj/item/clothing/head/helmet/swat = 2, + /obj/item/clothing/mask/gas = 2, + /obj/item/clothing/shoes/boots/swat = 2, + /obj/item/clothing/suit/armor/swat = 2, + /obj/item/clothing/under/syndicate/tacticool = 2) + + +/obj/structure/closet/thunderdome + name = "\improper Thunderdome closet" + desc = "Everything you need!" + closet_appearance = /decl/closet_appearance/thunderdomered + anchored = TRUE + +/obj/structure/closet/thunderdome/tdred + name = "red-team Thunderdome closet" + + starts_with = list( + /obj/item/clothing/suit/armor/tdome/red = 3, + /obj/item/weapon/melee/energy/sword = 3, + /obj/item/weapon/gun/energy/laser = 3, + /obj/item/weapon/melee/baton = 3, + /obj/item/weapon/storage/box/flashbangs = 3, + /obj/item/clothing/head/helmet/thunderdome = 3) + +/obj/structure/closet/thunderdome/tdgreen + name = "green-team Thunderdome closet" + closet_appearance = /decl/closet_appearance/thunderdomegreen + + starts_with = list( + /obj/item/clothing/suit/armor/tdome/green = 3, + /obj/item/weapon/melee/energy/sword = 3, + /obj/item/weapon/gun/energy/laser = 3, + /obj/item/weapon/melee/baton = 3, + /obj/item/weapon/storage/box/flashbangs = 3, + /obj/item/clothing/head/helmet/thunderdome = 3) + +/obj/structure/closet/alien + name = "alien container" + desc = "Contains secrets of the universe." + icon = 'icons/obj/closets/abductor.dmi' + anchored = TRUE + closet_appearance = null // special icons diff --git a/code/game/objects/structures/crates_lockers/closets/job_closets.dm b/code/game/objects/structures/crates_lockers/closets/job_closets.dm index 86cb04e20dd..a33e39a2192 100644 --- a/code/game/objects/structures/crates_lockers/closets/job_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/job_closets.dm @@ -1,132 +1,132 @@ -/* - * Closets for specific jobs - * Contains: - * Bartender - * Janitor - * Lawyer - * Janitorial Equipment - * - * - * These have been removed from the map for the most part, however - * do not remove these in case people want to make maps or POIs - * with these closets. - * - */ - -/* - * Bartender - */ -/obj/structure/closet/gmcloset - name = "formal closet" - desc = "It's a storage unit for formal clothing." - closet_appearance = /decl/closet_appearance/wardrobe/suit - - starts_with = list( - /obj/item/clothing/head/that = 2, - /obj/item/device/radio/headset/headset_service = 2, - /obj/item/clothing/head/pin/flower, - /obj/item/clothing/head/pin/flower/pink, - /obj/item/clothing/head/pin/flower/yellow, - /obj/item/clothing/head/pin/flower/blue, - /obj/item/clothing/head/pin/pink, - /obj/item/clothing/head/pin/magnetic, - /obj/item/clothing/under/sl_suit = 2, - /obj/item/clothing/under/rank/bartender = 2, - /obj/item/clothing/under/rank/bartender/skirt, - /obj/item/clothing/suit/storage/hooded/wintercoat/bar, - /obj/item/clothing/under/dress/dress_saloon, - /obj/item/clothing/accessory/wcoat = 2, - /obj/item/clothing/shoes/black = 2, - /obj/item/clothing/shoes/laceup - ) - -/* - * Chef - */ -/obj/structure/closet/chefcloset - name = "chef's closet" - desc = "It's a storage unit for foodservice garments." - closet_appearance = /decl/closet_appearance/wardrobe/white - - starts_with = list( - /obj/item/clothing/under/sundress, - /obj/item/clothing/under/waiter = 2, - /obj/item/device/radio/headset/headset_service = 2, - /obj/item/weapon/storage/box/mousetraps = 2, - /obj/item/clothing/under/rank/chef, - /obj/item/clothing/head/chefhat, - /obj/item/weapon/storage/bag/food = 2 - ) - -/* - * Janitor - */ -/obj/structure/closet/jcloset - name = "custodial closet" - desc = "It's a storage unit for janitorial clothes and gear." - closet_appearance = /decl/closet_appearance/wardrobe/janitor - - starts_with = list( - /obj/item/clothing/under/rank/janitor, - /obj/item/clothing/under/dress/maid/janitor, - /obj/item/device/radio/headset/headset_service, - /obj/item/weapon/cartridge/janitor, - /obj/item/clothing/suit/storage/hooded/wintercoat/janitor, - /obj/item/clothing/gloves/black, - /obj/item/clothing/head/soft/purple, - /obj/item/clothing/head/beret/purple, - /obj/item/device/flashlight, - /obj/item/clothing/suit/caution = 4, - /obj/item/device/lightreplacer, - /obj/item/weapon/storage/bag/trash, - /obj/item/weapon/storage/belt/janitor, - /obj/item/clothing/shoes/galoshes - ) - -/* - * Lawyer - */ -/obj/structure/closet/lawcloset - name = "legal closet" - desc = "It's a storage unit for courtroom apparel and items." - closet_appearance = /decl/closet_appearance/wardrobe/suit - - starts_with = list( - /obj/item/clothing/under/lawyer/female = 2, - /obj/item/clothing/under/lawyer/black = 2, - /obj/item/clothing/under/lawyer/black/skirt = 2, - /obj/item/clothing/under/lawyer/red = 2, - /obj/item/clothing/under/lawyer/red/skirt = 2, - /obj/item/clothing/suit/storage/toggle/internalaffairs = 2, - /obj/item/clothing/under/lawyer/bluesuit = 2, - /obj/item/clothing/under/lawyer/bluesuit/skirt = 2, - /obj/item/clothing/suit/storage/toggle/lawyer/bluejacket = 2, - /obj/item/clothing/under/lawyer/purpsuit = 2, - /obj/item/clothing/under/lawyer/purpsuit/skirt = 2, - /obj/item/clothing/suit/storage/toggle/lawyer/purpjacket = 2, - /obj/item/clothing/shoes/brown = 2, - /obj/item/clothing/shoes/black = 2, - /obj/item/clothing/shoes/laceup = 2, - /obj/item/clothing/glasses/sunglasses/big = 2, - /obj/item/clothing/under/lawyer/blue = 2, - /obj/item/clothing/under/lawyer/blue/skirt = 2, - /obj/item/device/tape/random = 2 - ) - -/* - * Janitorial Equipment - */ -/obj/structure/closet/jequipcloset - name = "custodial equipment closet" - desc = "It's a storage unit for janitorial clothes and gear." - closet_appearance = /decl/closet_appearance/wardrobe/janitor - - starts_with = list( - /obj/item/device/flashlight = 5, - /obj/item/clothing/suit/caution = 12, - /obj/item/device/lightreplacer = 3, - /obj/item/weapon/storage/bag/trash = 3, - /obj/item/weapon/storage/box/lights/mixed = 3, - /obj/item/weapon/storage/box/mousetraps = 1, - /obj/item/weapon/grenade/chem_grenade/cleaner = 4 - ) +/* + * Closets for specific jobs + * Contains: + * Bartender + * Janitor + * Lawyer + * Janitorial Equipment + * + * + * These have been removed from the map for the most part, however + * do not remove these in case people want to make maps or POIs + * with these closets. + * + */ + +/* + * Bartender + */ +/obj/structure/closet/gmcloset + name = "formal closet" + desc = "It's a storage unit for formal clothing." + closet_appearance = /decl/closet_appearance/wardrobe/suit + + starts_with = list( + /obj/item/clothing/head/that = 2, + /obj/item/device/radio/headset/headset_service = 2, + /obj/item/clothing/head/pin/flower, + /obj/item/clothing/head/pin/flower/pink, + /obj/item/clothing/head/pin/flower/yellow, + /obj/item/clothing/head/pin/flower/blue, + /obj/item/clothing/head/pin/pink, + /obj/item/clothing/head/pin/magnetic, + /obj/item/clothing/under/sl_suit = 2, + /obj/item/clothing/under/rank/bartender = 2, + /obj/item/clothing/under/rank/bartender/skirt, + /obj/item/clothing/suit/storage/hooded/wintercoat/bar, + /obj/item/clothing/under/dress/dress_saloon, + /obj/item/clothing/accessory/wcoat = 2, + /obj/item/clothing/shoes/black = 2, + /obj/item/clothing/shoes/laceup + ) + +/* + * Chef + */ +/obj/structure/closet/chefcloset + name = "chef's closet" + desc = "It's a storage unit for foodservice garments." + closet_appearance = /decl/closet_appearance/wardrobe/white + + starts_with = list( + /obj/item/clothing/under/sundress, + /obj/item/clothing/under/waiter = 2, + /obj/item/device/radio/headset/headset_service = 2, + /obj/item/weapon/storage/box/mousetraps = 2, + /obj/item/clothing/under/rank/chef, + /obj/item/clothing/head/chefhat, + /obj/item/weapon/storage/bag/food = 2 + ) + +/* + * Janitor + */ +/obj/structure/closet/jcloset + name = "custodial closet" + desc = "It's a storage unit for janitorial clothes and gear." + closet_appearance = /decl/closet_appearance/wardrobe/janitor + + starts_with = list( + /obj/item/clothing/under/rank/janitor, + /obj/item/clothing/under/dress/maid/janitor, + /obj/item/device/radio/headset/headset_service, + /obj/item/weapon/cartridge/janitor, + /obj/item/clothing/suit/storage/hooded/wintercoat/janitor, + /obj/item/clothing/gloves/black, + /obj/item/clothing/head/soft/purple, + /obj/item/clothing/head/beret/purple, + /obj/item/device/flashlight, + /obj/item/clothing/suit/caution = 4, + /obj/item/device/lightreplacer, + /obj/item/weapon/storage/bag/trash, + /obj/item/weapon/storage/belt/janitor, + /obj/item/clothing/shoes/galoshes + ) + +/* + * Lawyer + */ +/obj/structure/closet/lawcloset + name = "legal closet" + desc = "It's a storage unit for courtroom apparel and items." + closet_appearance = /decl/closet_appearance/wardrobe/suit + + starts_with = list( + /obj/item/clothing/under/lawyer/female = 2, + /obj/item/clothing/under/lawyer/black = 2, + /obj/item/clothing/under/lawyer/black/skirt = 2, + /obj/item/clothing/under/lawyer/red = 2, + /obj/item/clothing/under/lawyer/red/skirt = 2, + /obj/item/clothing/suit/storage/toggle/internalaffairs = 2, + /obj/item/clothing/under/lawyer/bluesuit = 2, + /obj/item/clothing/under/lawyer/bluesuit/skirt = 2, + /obj/item/clothing/suit/storage/toggle/lawyer/bluejacket = 2, + /obj/item/clothing/under/lawyer/purpsuit = 2, + /obj/item/clothing/under/lawyer/purpsuit/skirt = 2, + /obj/item/clothing/suit/storage/toggle/lawyer/purpjacket = 2, + /obj/item/clothing/shoes/brown = 2, + /obj/item/clothing/shoes/black = 2, + /obj/item/clothing/shoes/laceup = 2, + /obj/item/clothing/glasses/sunglasses/big = 2, + /obj/item/clothing/under/lawyer/blue = 2, + /obj/item/clothing/under/lawyer/blue/skirt = 2, + /obj/item/device/tape/random = 2 + ) + +/* + * Janitorial Equipment + */ +/obj/structure/closet/jequipcloset + name = "custodial equipment closet" + desc = "It's a storage unit for janitorial clothes and gear." + closet_appearance = /decl/closet_appearance/wardrobe/janitor + + starts_with = list( + /obj/item/device/flashlight = 5, + /obj/item/clothing/suit/caution = 12, + /obj/item/device/lightreplacer = 3, + /obj/item/weapon/storage/bag/trash = 3, + /obj/item/weapon/storage/box/lights/mixed = 3, + /obj/item/weapon/storage/box/mousetraps = 1, + /obj/item/weapon/grenade/chem_grenade/cleaner = 4 + ) diff --git a/code/game/objects/structures/crates_lockers/closets/l3closet.dm b/code/game/objects/structures/crates_lockers/closets/l3closet.dm index e1c551b5517..2966d4b8e4f 100644 --- a/code/game/objects/structures/crates_lockers/closets/l3closet.dm +++ b/code/game/objects/structures/crates_lockers/closets/l3closet.dm @@ -1,63 +1,63 @@ -/obj/structure/closet/l3closet - name = "level-3 biohazard suit closet" - desc = "It's a storage unit for level-3 biohazard gear." - closet_appearance = /decl/closet_appearance/bio - -/obj/structure/closet/l3closet/general - closet_appearance = /decl/closet_appearance/bio - - starts_with = list( - /obj/item/clothing/suit/bio_suit/general, - /obj/item/clothing/head/bio_hood/general) - - -/obj/structure/closet/l3closet/virology - closet_appearance = /decl/closet_appearance/bio/virology - - starts_with = list( - /obj/item/clothing/suit/bio_suit/virology, - /obj/item/clothing/head/bio_hood/virology, - /obj/item/clothing/mask/gas, - /obj/item/weapon/tank/oxygen) - - -/obj/structure/closet/l3closet/security - closet_appearance = /decl/closet_appearance/bio/security - - starts_with = list( - /obj/item/clothing/suit/bio_suit/security, - /obj/item/clothing/head/bio_hood/security) - ///obj/item/weapon/gun/energy/taser/xeno/sec) //VOREStation Removal - -/obj/structure/closet/l3closet/janitor - closet_appearance = /decl/closet_appearance/bio/janitor - - starts_with = list( - /obj/item/clothing/suit/bio_suit/janitor = 2, - /obj/item/clothing/head/bio_hood/janitor = 2, - /obj/item/clothing/mask/gas = 2, - /obj/item/weapon/tank/emergency/oxygen/engi = 2) - - -/obj/structure/closet/l3closet/scientist - closet_appearance = /decl/closet_appearance/bio/science - - starts_with = list( - /obj/item/clothing/suit/bio_suit/scientist, - /obj/item/clothing/head/bio_hood/scientist, - /obj/item/weapon/storage/bag/xeno = 1) - -/obj/structure/closet/l3closet/scientist/double - starts_with = list( - /obj/item/clothing/suit/bio_suit/scientist = 2, - /obj/item/clothing/head/bio_hood/scientist = 2, - /obj/item/weapon/storage/bag/xeno = 2) // VOREEdit, adding the xenobag to xenobio. - - -/obj/structure/closet/l3closet/medical - closet_appearance = /decl/closet_appearance/bio/medical - - starts_with = list( - /obj/item/clothing/suit/bio_suit/general = 3, - /obj/item/clothing/head/bio_hood/general = 3, - /obj/item/clothing/mask/gas = 3) +/obj/structure/closet/l3closet + name = "level-3 biohazard suit closet" + desc = "It's a storage unit for level-3 biohazard gear." + closet_appearance = /decl/closet_appearance/bio + +/obj/structure/closet/l3closet/general + closet_appearance = /decl/closet_appearance/bio + + starts_with = list( + /obj/item/clothing/suit/bio_suit/general, + /obj/item/clothing/head/bio_hood/general) + + +/obj/structure/closet/l3closet/virology + closet_appearance = /decl/closet_appearance/bio/virology + + starts_with = list( + /obj/item/clothing/suit/bio_suit/virology, + /obj/item/clothing/head/bio_hood/virology, + /obj/item/clothing/mask/gas, + /obj/item/weapon/tank/oxygen) + + +/obj/structure/closet/l3closet/security + closet_appearance = /decl/closet_appearance/bio/security + + starts_with = list( + /obj/item/clothing/suit/bio_suit/security, + /obj/item/clothing/head/bio_hood/security) + ///obj/item/weapon/gun/energy/taser/xeno/sec) //VOREStation Removal + +/obj/structure/closet/l3closet/janitor + closet_appearance = /decl/closet_appearance/bio/janitor + + starts_with = list( + /obj/item/clothing/suit/bio_suit/janitor = 2, + /obj/item/clothing/head/bio_hood/janitor = 2, + /obj/item/clothing/mask/gas = 2, + /obj/item/weapon/tank/emergency/oxygen/engi = 2) + + +/obj/structure/closet/l3closet/scientist + closet_appearance = /decl/closet_appearance/bio/science + + starts_with = list( + /obj/item/clothing/suit/bio_suit/scientist, + /obj/item/clothing/head/bio_hood/scientist, + /obj/item/weapon/storage/bag/xeno = 1) + +/obj/structure/closet/l3closet/scientist/double + starts_with = list( + /obj/item/clothing/suit/bio_suit/scientist = 2, + /obj/item/clothing/head/bio_hood/scientist = 2, + /obj/item/weapon/storage/bag/xeno = 2) // VOREEdit, adding the xenobag to xenobio. + + +/obj/structure/closet/l3closet/medical + closet_appearance = /decl/closet_appearance/bio/medical + + starts_with = list( + /obj/item/clothing/suit/bio_suit/general = 3, + /obj/item/clothing/head/bio_hood/general = 3, + /obj/item/clothing/mask/gas = 3) diff --git a/code/game/objects/structures/crates_lockers/closets/malfunction.dm b/code/game/objects/structures/crates_lockers/closets/malfunction.dm index feaa63e39f2..cd23e478a97 100644 --- a/code/game/objects/structures/crates_lockers/closets/malfunction.dm +++ b/code/game/objects/structures/crates_lockers/closets/malfunction.dm @@ -1,12 +1,12 @@ -/obj/structure/closet/malf/suits - desc = "It's a storage unit for operational gear." - closet_appearance = /decl/closet_appearance/tactical - - starts_with = list( - /obj/item/weapon/tank/jetpack/void, - /obj/item/clothing/mask/breath, - /obj/item/clothing/head/helmet/space/void, - /obj/item/clothing/suit/space/void, - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/cell, - /obj/item/device/multitool) +/obj/structure/closet/malf/suits + desc = "It's a storage unit for operational gear." + closet_appearance = /decl/closet_appearance/tactical + + starts_with = list( + /obj/item/weapon/tank/jetpack/void, + /obj/item/clothing/mask/breath, + /obj/item/clothing/head/helmet/space/void, + /obj/item/clothing/suit/space/void, + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/cell, + /obj/item/device/multitool) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm index 66695f3511a..6659a89a074 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm @@ -1,51 +1,51 @@ -/obj/structure/closet/secure_closet/freezer/kitchen - name = "kitchen cabinet" - req_access = list(access_kitchen) - - starts_with = list( - /obj/item/weapon/reagent_containers/food/condiment/carton/flour = 6, - /obj/item/weapon/reagent_containers/food/condiment/carton/sugar = 1, - /obj/item/weapon/reagent_containers/food/condiment/carton/flour/rustic = 1, - /obj/item/weapon/reagent_containers/food/condiment/carton/sugar/rustic = 1, - /obj/item/weapon/reagent_containers/food/condiment/spacespice = 2 - ) - - open_sound = 'sound/machines/click.ogg' - close_sound = 'sound/machines/click.ogg' - -/obj/structure/closet/secure_closet/freezer/kitchen/mining - req_access = list() - - -/obj/structure/closet/secure_closet/freezer/meat - name = "meat fridge" - icon = 'icons/obj/closets/fridge.dmi' - closet_appearance = null - - starts_with = list( - /obj/item/weapon/reagent_containers/food/snacks/meat/monkey = 10) - - -/obj/structure/closet/secure_closet/freezer/fridge - name = "refrigerator" - icon = 'icons/obj/closets/fridge.dmi' - closet_appearance = null - - starts_with = list( - /obj/item/weapon/reagent_containers/food/drinks/milk = 6, - /obj/item/weapon/reagent_containers/food/drinks/soymilk = 4, - /obj/item/weapon/storage/fancy/egg_box = 4, - /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose = 2) - - -/obj/structure/closet/secure_closet/freezer/money - name = "freezer" - icon = 'icons/obj/closets/fridge.dmi' - closet_appearance = null - req_access = list(access_heads_vault) - - - starts_with = list( - /obj/item/weapon/spacecash/c1000 = 3, - /obj/item/weapon/spacecash/c500 = 4, - /obj/item/weapon/spacecash/c200 = 5) +/obj/structure/closet/secure_closet/freezer/kitchen + name = "kitchen cabinet" + req_access = list(access_kitchen) + + starts_with = list( + /obj/item/weapon/reagent_containers/food/condiment/carton/flour = 6, + /obj/item/weapon/reagent_containers/food/condiment/carton/sugar = 1, + /obj/item/weapon/reagent_containers/food/condiment/carton/flour/rustic = 1, + /obj/item/weapon/reagent_containers/food/condiment/carton/sugar/rustic = 1, + /obj/item/weapon/reagent_containers/food/condiment/spacespice = 2 + ) + + open_sound = 'sound/machines/click.ogg' + close_sound = 'sound/machines/click.ogg' + +/obj/structure/closet/secure_closet/freezer/kitchen/mining + req_access = list() + + +/obj/structure/closet/secure_closet/freezer/meat + name = "meat fridge" + icon = 'icons/obj/closets/fridge.dmi' + closet_appearance = null + + starts_with = list( + /obj/item/weapon/reagent_containers/food/snacks/meat/monkey = 10) + + +/obj/structure/closet/secure_closet/freezer/fridge + name = "refrigerator" + icon = 'icons/obj/closets/fridge.dmi' + closet_appearance = null + + starts_with = list( + /obj/item/weapon/reagent_containers/food/drinks/milk = 6, + /obj/item/weapon/reagent_containers/food/drinks/soymilk = 4, + /obj/item/weapon/storage/fancy/egg_box = 4, + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose = 2) + + +/obj/structure/closet/secure_closet/freezer/money + name = "freezer" + icon = 'icons/obj/closets/fridge.dmi' + closet_appearance = null + req_access = list(access_heads_vault) + + + starts_with = list( + /obj/item/weapon/spacecash/c1000 = 3, + /obj/item/weapon/spacecash/c500 = 4, + /obj/item/weapon/spacecash/c200 = 5) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm index 0dc89e7c46e..39e8f2d954d 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm @@ -1,115 +1,115 @@ -/obj/structure/closet/secure_closet/personal - name = "personal closet" - desc = "It's a secure locker for personnel. The first card swiped gains control." - req_access = list(access_all_personal_lockers) - var/registered_name = null - - /* //VOREStation Removal - starts_with = list( - /obj/item/device/radio/headset) - */ - -/obj/structure/closet/secure_closet/personal/Initialize() - /* //VOREStation Removal - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack - else - starts_with += /obj/item/weapon/storage/backpack/satchel/norm - */ - return ..() - -/obj/structure/closet/secure_closet/personal/patient - name = "patient's closet" - closet_appearance = /decl/closet_appearance/secure_closet/patient - - starts_with = list( - /obj/item/clothing/under/medigown, - /obj/item/clothing/under/color/white, - /obj/item/clothing/shoes/white) - - -/obj/structure/closet/secure_closet/personal/cabinet - closet_appearance = /decl/closet_appearance/cabinet/secure - - open_sound = 'sound/effects/wooden_closet_open.ogg' - close_sound = 'sound/effects/wooden_closet_close.ogg' - - starts_with = list( - /obj/item/weapon/storage/backpack/satchel/withwallet, - /obj/item/device/radio/headset - ) - -/obj/structure/closet/secure_closet/personal/attackby(obj/item/weapon/W as obj, mob/user as mob) - if (src.opened) - if(istype(W, /obj/item/weapon/storage/laundry_basket)) - return ..(W,user) - if(istype(W, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/G = W - if(large) - MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet - else - to_chat(user, "The locker is too small to stuff [G.affecting] into!") - if(isrobot(user)) - return - if(W.loc != user) // This should stop mounted modules ending up outside the module. - return - user.drop_item() - if(W) - W.forceMove(loc) - else if(W.GetID()) - var/obj/item/weapon/card/id/I = W.GetID() - - if(src.broken) - to_chat(user, "It appears to be broken.") - return - if(!I || !I.registered_name) return - if(src.allowed(user) || !src.registered_name || (istype(I) && (src.registered_name == I.registered_name))) - //they can open all lockers, or nobody owns this, or they own this locker - src.locked = !( src.locked ) - - if(!src.registered_name) - src.registered_name = I.registered_name - src.desc = "Owned by [I.registered_name]." - else - to_chat(user, "Access Denied") - else if(istype(W, /obj/item/weapon/melee/energy/blade)) - if(emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [W]!", "You hear metal being sliced and sparks flying.")) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, src.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - playsound(src, "sparks", 50, 1) - else - to_chat(user, "Access Denied") - update_icon() - -/obj/structure/closet/secure_closet/personal/emag_act(var/remaining_charges, var/mob/user, var/visual_feedback, var/audible_feedback) - if(!broken) - broken = 1 - locked = 0 - desc = "It appears to be broken." - update_icon() - if(visual_feedback) - visible_message("[visual_feedback]", "[audible_feedback]") - return 1 - -/obj/structure/closet/secure_closet/personal/verb/reset() - set src in oview(1) // One square distance - set category = "Object" - set name = "Reset Lock" - if(!usr.canmove || usr.stat || usr.restrained()) // Don't use it if you're not able to! Checks for stuns, ghost and restrain - return - if(ishuman(usr)) - src.add_fingerprint(usr) - if (src.locked || !src.registered_name) - to_chat(usr, "You need to unlock it first.") - else if (src.broken) - to_chat(usr, "It appears to be broken.") - else - if (src.opened) - if(!src.close()) - return - src.locked = 1 - update_icon() - src.registered_name = null - src.desc = "It's a secure locker for personnel. The first card swiped gains control." +/obj/structure/closet/secure_closet/personal + name = "personal closet" + desc = "It's a secure locker for personnel. The first card swiped gains control." + req_access = list(access_all_personal_lockers) + var/registered_name = null + + /* //VOREStation Removal + starts_with = list( + /obj/item/device/radio/headset) + */ + +/obj/structure/closet/secure_closet/personal/Initialize() + /* //VOREStation Removal + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack + else + starts_with += /obj/item/weapon/storage/backpack/satchel/norm + */ + return ..() + +/obj/structure/closet/secure_closet/personal/patient + name = "patient's closet" + closet_appearance = /decl/closet_appearance/secure_closet/patient + + starts_with = list( + /obj/item/clothing/under/medigown, + /obj/item/clothing/under/color/white, + /obj/item/clothing/shoes/white) + + +/obj/structure/closet/secure_closet/personal/cabinet + closet_appearance = /decl/closet_appearance/cabinet/secure + + open_sound = 'sound/effects/wooden_closet_open.ogg' + close_sound = 'sound/effects/wooden_closet_close.ogg' + + starts_with = list( + /obj/item/weapon/storage/backpack/satchel/withwallet, + /obj/item/device/radio/headset + ) + +/obj/structure/closet/secure_closet/personal/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (src.opened) + if(istype(W, /obj/item/weapon/storage/laundry_basket)) + return ..(W,user) + if(istype(W, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = W + if(large) + MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet + else + to_chat(user, "The locker is too small to stuff [G.affecting] into!") + if(isrobot(user)) + return + if(W.loc != user) // This should stop mounted modules ending up outside the module. + return + user.drop_item() + if(W) + W.forceMove(loc) + else if(W.GetID()) + var/obj/item/weapon/card/id/I = W.GetID() + + if(src.broken) + to_chat(user, "It appears to be broken.") + return + if(!I || !I.registered_name) return + if(src.allowed(user) || !src.registered_name || (istype(I) && (src.registered_name == I.registered_name))) + //they can open all lockers, or nobody owns this, or they own this locker + src.locked = !( src.locked ) + + if(!src.registered_name) + src.registered_name = I.registered_name + src.desc = "Owned by [I.registered_name]." + else + to_chat(user, "Access Denied") + else if(istype(W, /obj/item/weapon/melee/energy/blade)) + if(emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [W]!", "You hear metal being sliced and sparks flying.")) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, src.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + playsound(src, "sparks", 50, 1) + else + to_chat(user, "Access Denied") + update_icon() + +/obj/structure/closet/secure_closet/personal/emag_act(var/remaining_charges, var/mob/user, var/visual_feedback, var/audible_feedback) + if(!broken) + broken = 1 + locked = 0 + desc = "It appears to be broken." + update_icon() + if(visual_feedback) + visible_message("[visual_feedback]", "[audible_feedback]") + return 1 + +/obj/structure/closet/secure_closet/personal/verb/reset() + set src in oview(1) // One square distance + set category = "Object" + set name = "Reset Lock" + if(!usr.canmove || usr.stat || usr.restrained()) // Don't use it if you're not able to! Checks for stuns, ghost and restrain + return + if(ishuman(usr)) + src.add_fingerprint(usr) + if (src.locked || !src.registered_name) + to_chat(usr, "You need to unlock it first.") + else if (src.broken) + to_chat(usr, "It appears to be broken.") + else + if (src.opened) + if(!src.close()) + return + src.locked = 1 + update_icon() + src.registered_name = null + src.desc = "It's a secure locker for personnel. The first card swiped gains control." diff --git a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm index 81f2911baae..c941f8306ed 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm @@ -1,93 +1,93 @@ -/obj/structure/closet/secure_closet/scientist - name = "scientist's locker" - req_access = list(access_tox_storage) - closet_appearance = /decl/closet_appearance/secure_closet/science - - starts_with = list( - /obj/item/clothing/under/rank/scientist, - /obj/item/clothing/suit/storage/toggle/labcoat, - /obj/item/clothing/shoes/white, - /obj/item/device/radio/headset/headset_sci, - /obj/item/weapon/tank/air, - /obj/item/clothing/mask/gas, - /obj/item/clothing/suit/storage/hooded/wintercoat/science, - /obj/item/clothing/shoes/boots/winter/science) - -/obj/structure/closet/secure_closet/scientist/Initialize() - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/dufflebag/sci - else - starts_with += /obj/item/weapon/storage/backpack/toxins - return ..() - - -/obj/structure/closet/secure_closet/RD - name = "research director's locker" - req_access = list(access_rd) - closet_appearance = /decl/closet_appearance/secure_closet/science/rd - - starts_with = list( - /obj/item/clothing/suit/bio_suit/scientist, - /obj/item/clothing/head/bio_hood/scientist, - /obj/item/clothing/under/rank/research_director, - /obj/item/clothing/under/rank/research_director/rdalt, - /obj/item/clothing/under/rank/research_director/dress_rd, - /obj/item/clothing/suit/storage/toggle/labcoat, - /obj/item/clothing/suit/storage/toggle/labcoat/modern, - /obj/item/clothing/suit/storage/toggle/labcoat/rd, - /obj/item/clothing/under/rank/neo_rd_turtle, - /obj/item/clothing/under/rank/neo_rd_turtle_skirt, - /obj/item/clothing/under/rank/neo_rd_suit, - /obj/item/clothing/under/rank/neo_rd_suit_skirt, - /obj/item/clothing/under/rank/neo_rd_gorka, - /obj/item/weapon/cartridge/rd, - /obj/item/clothing/shoes/white, - /obj/item/clothing/shoes/laceup/brown, - /obj/item/clothing/gloves/sterile/latex, - /obj/item/device/radio/headset/heads/rd, - /obj/item/device/radio/headset/heads/rd/alt, - /obj/item/weapon/tank/air, - /obj/item/clothing/mask/gas, - /obj/item/device/flash, - /obj/item/clothing/suit/storage/hooded/wintercoat/science, - /obj/item/clothing/suit/storage/hooded/wintercoat/science/rd, - /obj/item/clothing/shoes/boots/winter/science, - /obj/item/clothing/head/beret/science/rd, - /obj/item/weapon/bluespace_harpoon) //VOREStation Add - -/obj/structure/closet/secure_closet/xenoarchaeologist - name = "Xenoarchaeologist Locker" - req_access = list(access_tox_storage) - closet_appearance = /decl/closet_appearance/secure_closet/science/xenoarch - - starts_with = list( - /obj/item/clothing/under/rank/scientist, - /obj/item/clothing/suit/storage/toggle/labcoat, - /obj/item/clothing/suit/storage/toggle/labcoat/modern, - /obj/item/clothing/shoes/white, - /obj/item/weapon/melee/umbrella, - /obj/item/clothing/glasses/science, - /obj/item/device/radio/headset/headset_sci, - /obj/item/weapon/storage/belt/archaeology, - /obj/item/weapon/storage/excavation) - -/obj/structure/closet/excavation - name = "Excavation tools" - closet_appearance = /decl/closet_appearance/secure_closet/engineering/tools/xenoarch - - starts_with = list( - /obj/item/weapon/storage/belt/archaeology, - /obj/item/weapon/storage/excavation, - /obj/item/device/flashlight/lantern, - /obj/item/device/ano_scanner, - /obj/item/device/depth_scanner, - /obj/item/device/core_sampler, - /obj/item/device/gps, - /obj/item/device/beacon_locator, - /obj/item/device/radio/beacon, - /obj/item/clothing/glasses/meson, - /obj/item/weapon/pickaxe, - /obj/item/device/measuring_tape, - /obj/item/weapon/pickaxe/hand, - /obj/item/weapon/storage/bag/fossils, - /obj/item/weapon/hand_labeler) +/obj/structure/closet/secure_closet/scientist + name = "scientist's locker" + req_access = list(access_tox_storage) + closet_appearance = /decl/closet_appearance/secure_closet/science + + starts_with = list( + /obj/item/clothing/under/rank/scientist, + /obj/item/clothing/suit/storage/toggle/labcoat, + /obj/item/clothing/shoes/white, + /obj/item/device/radio/headset/headset_sci, + /obj/item/weapon/tank/air, + /obj/item/clothing/mask/gas, + /obj/item/clothing/suit/storage/hooded/wintercoat/science, + /obj/item/clothing/shoes/boots/winter/science) + +/obj/structure/closet/secure_closet/scientist/Initialize() + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/dufflebag/sci + else + starts_with += /obj/item/weapon/storage/backpack/toxins + return ..() + + +/obj/structure/closet/secure_closet/RD + name = "research director's locker" + req_access = list(access_rd) + closet_appearance = /decl/closet_appearance/secure_closet/science/rd + + starts_with = list( + /obj/item/clothing/suit/bio_suit/scientist, + /obj/item/clothing/head/bio_hood/scientist, + /obj/item/clothing/under/rank/research_director, + /obj/item/clothing/under/rank/research_director/rdalt, + /obj/item/clothing/under/rank/research_director/dress_rd, + /obj/item/clothing/suit/storage/toggle/labcoat, + /obj/item/clothing/suit/storage/toggle/labcoat/modern, + /obj/item/clothing/suit/storage/toggle/labcoat/rd, + /obj/item/clothing/under/rank/neo_rd_turtle, + /obj/item/clothing/under/rank/neo_rd_turtle_skirt, + /obj/item/clothing/under/rank/neo_rd_suit, + /obj/item/clothing/under/rank/neo_rd_suit_skirt, + /obj/item/clothing/under/rank/neo_rd_gorka, + /obj/item/weapon/cartridge/rd, + /obj/item/clothing/shoes/white, + /obj/item/clothing/shoes/laceup/brown, + /obj/item/clothing/gloves/sterile/latex, + /obj/item/device/radio/headset/heads/rd, + /obj/item/device/radio/headset/heads/rd/alt, + /obj/item/weapon/tank/air, + /obj/item/clothing/mask/gas, + /obj/item/device/flash, + /obj/item/clothing/suit/storage/hooded/wintercoat/science, + /obj/item/clothing/suit/storage/hooded/wintercoat/science/rd, + /obj/item/clothing/shoes/boots/winter/science, + /obj/item/clothing/head/beret/science/rd, + /obj/item/weapon/bluespace_harpoon) //VOREStation Add + +/obj/structure/closet/secure_closet/xenoarchaeologist + name = "Xenoarchaeologist Locker" + req_access = list(access_tox_storage) + closet_appearance = /decl/closet_appearance/secure_closet/science/xenoarch + + starts_with = list( + /obj/item/clothing/under/rank/scientist, + /obj/item/clothing/suit/storage/toggle/labcoat, + /obj/item/clothing/suit/storage/toggle/labcoat/modern, + /obj/item/clothing/shoes/white, + /obj/item/weapon/melee/umbrella, + /obj/item/clothing/glasses/science, + /obj/item/device/radio/headset/headset_sci, + /obj/item/weapon/storage/belt/archaeology, + /obj/item/weapon/storage/excavation) + +/obj/structure/closet/excavation + name = "Excavation tools" + closet_appearance = /decl/closet_appearance/secure_closet/engineering/tools/xenoarch + + starts_with = list( + /obj/item/weapon/storage/belt/archaeology, + /obj/item/weapon/storage/excavation, + /obj/item/device/flashlight/lantern, + /obj/item/device/ano_scanner, + /obj/item/device/depth_scanner, + /obj/item/device/core_sampler, + /obj/item/device/gps, + /obj/item/device/beacon_locator, + /obj/item/device/radio/beacon, + /obj/item/clothing/glasses/meson, + /obj/item/weapon/pickaxe, + /obj/item/device/measuring_tape, + /obj/item/weapon/pickaxe/hand, + /obj/item/weapon/storage/bag/fossils, + /obj/item/weapon/hand_labeler) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm index 3f7f75bfc25..ba9c1ba8929 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm @@ -1,158 +1,158 @@ -/obj/structure/closet/secure_closet - name = "secure locker" - desc = "It's an immobile card-locked storage unit." - icon = 'icons/obj/closet.dmi' - icon_state = "secure1" - density = TRUE - opened = 0 - var/locked = 1 - var/broken = 0 - var/large = 1 - wall_mounted = 0 //never solid (You can always pass over it) - health = 200 - - closet_appearance = /decl/closet_appearance/secure_closet - -/obj/structure/closet/secure_closet/can_open() - if(locked) - return 0 - return ..() - -/obj/structure/closet/secure_closet/emp_act(severity) - for(var/obj/O in src) - O.emp_act(severity) - if(!broken) - if(prob(50/severity)) - locked = !locked - update_icon() - if(prob(20/severity) && !opened) - if(!locked) - open() - else - req_access = list() - req_access += pick(get_all_station_access()) - ..() - -/obj/structure/closet/secure_closet/proc/togglelock(mob/user as mob) - if(opened) - to_chat(user, "Close the locker first.") - return - if(broken) - to_chat(user, "The locker appears to be broken.") - return - if(user.loc == src) - to_chat(user, "You can't reach the lock from inside.") - return - if(allowed(user)) - locked = !locked - playsound(src, 'sound/machines/click.ogg', 15, 1, -3) - for(var/mob/O in viewers(user, 3)) - if((O.client && !( O.blinded ))) - to_chat(O, "The locker has been [locked ? null : "un"]locked by [user].") - update_icon() - else - to_chat(user, "Access Denied") - -/obj/structure/closet/secure_closet/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.has_tool_quality(TOOL_WRENCH)) - if(opened) - if(anchored) - user.visible_message("\The [user] begins unsecuring \the [src] from the floor.", "You start unsecuring \the [src] from the floor.") - else - user.visible_message("\The [user] begins securing \the [src] to the floor.", "You start securing \the [src] to the floor.") - if(do_after(user, 20 * W.toolspeed)) - if(!src) return - to_chat(user, "You [anchored? "un" : ""]secured \the [src]!") - anchored = !anchored - return - else - to_chat(user, "You can't reach the anchoring bolts when the door is closed!") - else if(opened) - if(istype(W, /obj/item/weapon/storage/laundry_basket)) - return ..(W,user) - if(istype(W, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/G = W - if(large) - MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet - else - to_chat(user, "The locker is too small to stuff [G.affecting] into!") - if(isrobot(user)) - return - if(W.loc != user) // This should stop mounted modules ending up outside the module. - return - user.drop_item() - if(W) - W.forceMove(loc) - else if(istype(W, /obj/item/weapon/melee/energy/blade)) - if(emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [W]!", "You hear metal being sliced and sparks flying.")) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - playsound(src, "sparks", 50, 1) - else if(istype(W,/obj/item/weapon/packageWrap) || W.has_tool_quality(TOOL_WELDER)) - return ..(W,user) - else - togglelock(user) - -/obj/structure/closet/secure_closet/emag_act(var/remaining_charges, var/mob/user, var/emag_source, var/visual_feedback = "", var/audible_feedback = "") - if(!broken) - broken = 1 - locked = 0 - desc = "It appears to be broken." - - if(visual_feedback) - visible_message(visual_feedback, audible_feedback) - else if(user && emag_source) - visible_message("\The [src] has been broken by \the [user] with \an [emag_source]!", "You hear a faint electrical spark.") - else - visible_message("\The [src] sparks and breaks open!", "You hear a faint electrical spark.") - update_icon() - return 1 - -/obj/structure/closet/secure_closet/attack_hand(mob/user as mob) - add_fingerprint(user) - if(locked) - togglelock(user) - else - toggle(user) - -/obj/structure/closet/secure_closet/AltClick() - ..() - verb_togglelock() - -/obj/structure/closet/secure_closet/verb/verb_togglelock() - set src in oview(1) // One square distance - set category = "Object" - set name = "Toggle Lock" - - if(!usr.canmove || usr.stat || usr.restrained() || !Adjacent(usr)) // Don't use it if you're not able to! Checks for stuns, ghost and restrain - return - - if(ishuman(usr) || isrobot(usr)) - add_fingerprint(usr) - togglelock(usr) - else - to_chat(usr, "This mob type can't use this verb.") - -/obj/structure/closet/secure_closet/update_icon() - if(opened) - icon_state = "open" - else - if(broken) - icon_state = "closed_emagged[sealed ? "_welded" : ""]" - else - if(locked) - icon_state = "closed_locked[sealed ? "_welded" : ""]" - else - icon_state = "closed_unlocked[sealed ? "_welded" : ""]" - -/obj/structure/closet/secure_closet/req_breakout() - if(!opened && locked) return 1 - return ..() //It's a secure closet, but isn't locked. - -/obj/structure/closet/secure_closet/break_open() - desc += " It appears to be broken." - broken = 1 - locked = 0 - ..() +/obj/structure/closet/secure_closet + name = "secure locker" + desc = "It's an immobile card-locked storage unit." + icon = 'icons/obj/closet.dmi' + icon_state = "secure1" + density = TRUE + opened = 0 + var/locked = 1 + var/broken = 0 + var/large = 1 + wall_mounted = 0 //never solid (You can always pass over it) + health = 200 + + closet_appearance = /decl/closet_appearance/secure_closet + +/obj/structure/closet/secure_closet/can_open() + if(locked) + return 0 + return ..() + +/obj/structure/closet/secure_closet/emp_act(severity) + for(var/obj/O in src) + O.emp_act(severity) + if(!broken) + if(prob(50/severity)) + locked = !locked + update_icon() + if(prob(20/severity) && !opened) + if(!locked) + open() + else + req_access = list() + req_access += pick(get_all_station_access()) + ..() + +/obj/structure/closet/secure_closet/proc/togglelock(mob/user as mob) + if(opened) + to_chat(user, "Close the locker first.") + return + if(broken) + to_chat(user, "The locker appears to be broken.") + return + if(user.loc == src) + to_chat(user, "You can't reach the lock from inside.") + return + if(allowed(user)) + locked = !locked + playsound(src, 'sound/machines/click.ogg', 15, 1, -3) + for(var/mob/O in viewers(user, 3)) + if((O.client && !( O.blinded ))) + to_chat(O, "The locker has been [locked ? null : "un"]locked by [user].") + update_icon() + else + to_chat(user, "Access Denied") + +/obj/structure/closet/secure_closet/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + if(opened) + if(anchored) + user.visible_message("\The [user] begins unsecuring \the [src] from the floor.", "You start unsecuring \the [src] from the floor.") + else + user.visible_message("\The [user] begins securing \the [src] to the floor.", "You start securing \the [src] to the floor.") + if(do_after(user, 20 * W.toolspeed)) + if(!src) return + to_chat(user, "You [anchored? "un" : ""]secured \the [src]!") + anchored = !anchored + return + else + to_chat(user, "You can't reach the anchoring bolts when the door is closed!") + else if(opened) + if(istype(W, /obj/item/weapon/storage/laundry_basket)) + return ..(W,user) + if(istype(W, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = W + if(large) + MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet + else + to_chat(user, "The locker is too small to stuff [G.affecting] into!") + if(isrobot(user)) + return + if(W.loc != user) // This should stop mounted modules ending up outside the module. + return + user.drop_item() + if(W) + W.forceMove(loc) + else if(istype(W, /obj/item/weapon/melee/energy/blade)) + if(emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [W]!", "You hear metal being sliced and sparks flying.")) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + playsound(src, "sparks", 50, 1) + else if(istype(W,/obj/item/weapon/packageWrap) || W.has_tool_quality(TOOL_WELDER)) + return ..(W,user) + else + togglelock(user) + +/obj/structure/closet/secure_closet/emag_act(var/remaining_charges, var/mob/user, var/emag_source, var/visual_feedback = "", var/audible_feedback = "") + if(!broken) + broken = 1 + locked = 0 + desc = "It appears to be broken." + + if(visual_feedback) + visible_message(visual_feedback, audible_feedback) + else if(user && emag_source) + visible_message("\The [src] has been broken by \the [user] with \an [emag_source]!", "You hear a faint electrical spark.") + else + visible_message("\The [src] sparks and breaks open!", "You hear a faint electrical spark.") + update_icon() + return 1 + +/obj/structure/closet/secure_closet/attack_hand(mob/user as mob) + add_fingerprint(user) + if(locked) + togglelock(user) + else + toggle(user) + +/obj/structure/closet/secure_closet/AltClick() + ..() + verb_togglelock() + +/obj/structure/closet/secure_closet/verb/verb_togglelock() + set src in oview(1) // One square distance + set category = "Object" + set name = "Toggle Lock" + + if(!usr.canmove || usr.stat || usr.restrained() || !Adjacent(usr)) // Don't use it if you're not able to! Checks for stuns, ghost and restrain + return + + if(ishuman(usr) || isrobot(usr)) + add_fingerprint(usr) + togglelock(usr) + else + to_chat(usr, "This mob type can't use this verb.") + +/obj/structure/closet/secure_closet/update_icon() + if(opened) + icon_state = "open" + else + if(broken) + icon_state = "closed_emagged[sealed ? "_welded" : ""]" + else + if(locked) + icon_state = "closed_locked[sealed ? "_welded" : ""]" + else + icon_state = "closed_unlocked[sealed ? "_welded" : ""]" + +/obj/structure/closet/secure_closet/req_breakout() + if(!opened && locked) return 1 + return ..() //It's a secure closet, but isn't locked. + +/obj/structure/closet/secure_closet/break_open() + desc += " It appears to be broken." + broken = 1 + locked = 0 + ..() diff --git a/code/game/objects/structures/crates_lockers/closets/statue.dm b/code/game/objects/structures/crates_lockers/closets/statue.dm index 9b66918be0d..7b290534561 100644 --- a/code/game/objects/structures/crates_lockers/closets/statue.dm +++ b/code/game/objects/structures/crates_lockers/closets/statue.dm @@ -1,132 +1,132 @@ -/obj/structure/closet/statue - name = "statue" - desc = "An incredibly lifelike marble carving" - icon = 'icons/obj/statue.dmi' - icon_state = "human_male" - density = TRUE - anchored = TRUE - health = 0 //destroying the statue kills the mob within - blocks_emissive = EMISSIVE_BLOCK_UNIQUE - var/intialTox = 0 //these are here to keep the mob from taking damage from things that logically wouldn't affect a rock - var/intialFire = 0 //it's a little sloppy I know but it was this or the GODMODE flag. Lesser of two evils. - var/intialBrute = 0 - var/intialOxy = 0 - var/timer = 240 //eventually the person will be freed - -/obj/structure/closet/statue/New(loc, var/mob/living/L) - if(L && (ishuman(L) || L.isMonkey() || iscorgi(L))) - if(L.buckled) - L.buckled = 0 - L.anchored = FALSE - if(L.client) - L.client.perspective = EYE_PERSPECTIVE - L.client.eye = src - L.loc = src - L.sdisabilities |= MUTE - health = L.health + 100 //stoning damaged mobs will result in easier to shatter statues - intialTox = L.getToxLoss() - intialFire = L.getFireLoss() - intialBrute = L.getBruteLoss() - intialOxy = L.getOxyLoss() - if(ishuman(L)) - name = "statue of [L.name]" - if(L.gender == "female") - icon_state = "human_female" - else if(L.isMonkey()) - name = "statue of a monkey" - icon_state = "monkey" - else if(iscorgi(L)) - name = "statue of a corgi" - icon_state = "corgi" - desc = "If it takes forever, I will wait for you..." - - if(health == 0) //meaning if the statue didn't find a valid target - qdel(src) - return - - START_PROCESSING(SSobj, src) - ..() - -/obj/structure/closet/statue/process() - timer-- - for(var/mob/living/M in src) //Go-go gadget stasis field - M.setToxLoss(intialTox) - M.adjustFireLoss(intialFire - M.getFireLoss()) - M.adjustBruteLoss(intialBrute - M.getBruteLoss()) - M.setOxyLoss(intialOxy) - if (timer <= 0) - dump_contents() - STOP_PROCESSING(SSobj, src) - qdel(src) - -/obj/structure/closet/statue/dump_contents() - - for(var/obj/O in src) - O.loc = src.loc - - for(var/mob/living/M in src) - M.loc = src.loc - M.sdisabilities &= ~MUTE - M.take_overall_damage((M.health - health - 100),0) //any new damage the statue incurred is transfered to the mob - if(M.client) - M.client.eye = M.client.mob - M.client.perspective = MOB_PERSPECTIVE - -/obj/structure/closet/statue/open() - return - -/obj/structure/closet/statue/close() - return - -/obj/structure/closet/statue/toggle() - return - -/obj/structure/closet/statue/proc/check_health() - if(health <= 0) - for(var/mob/M in src) - shatter(M) - -/obj/structure/closet/statue/bullet_act(var/obj/item/projectile/Proj) - health -= Proj.get_structure_damage() - check_health() - - return - -/obj/structure/closet/statue/attack_generic(var/mob/user, damage, attacktext, environment_smash) - if(damage && environment_smash) - for(var/mob/M in src) - shatter(M) - -/obj/structure/closet/statue/ex_act(severity) - for(var/mob/M in src) - M.ex_act(severity) - health -= 60 / severity - check_health() - -/obj/structure/closet/statue/attackby(obj/item/I as obj, mob/user as mob) - health -= I.force - user.do_attack_animation(src) - visible_message("[user] strikes [src] with [I].") - check_health() - -/obj/structure/closet/statue/MouseDrop_T() - return - -/obj/structure/closet/statue/relaymove() - return - -/obj/structure/closet/statue/attack_hand() - return - -/obj/structure/closet/statue/verb_toggleopen() - return - -/obj/structure/closet/statue/update_icon() - return - -/obj/structure/closet/statue/proc/shatter(mob/user as mob) - if (user) - user.dust() - dump_contents() - visible_message("[src] shatters!.") - qdel(src) +/obj/structure/closet/statue + name = "statue" + desc = "An incredibly lifelike marble carving" + icon = 'icons/obj/statue.dmi' + icon_state = "human_male" + density = TRUE + anchored = TRUE + health = 0 //destroying the statue kills the mob within + blocks_emissive = EMISSIVE_BLOCK_UNIQUE + var/intialTox = 0 //these are here to keep the mob from taking damage from things that logically wouldn't affect a rock + var/intialFire = 0 //it's a little sloppy I know but it was this or the GODMODE flag. Lesser of two evils. + var/intialBrute = 0 + var/intialOxy = 0 + var/timer = 240 //eventually the person will be freed + +/obj/structure/closet/statue/New(loc, var/mob/living/L) + if(L && (ishuman(L) || L.isMonkey() || iscorgi(L))) + if(L.buckled) + L.buckled = 0 + L.anchored = FALSE + if(L.client) + L.client.perspective = EYE_PERSPECTIVE + L.client.eye = src + L.loc = src + L.sdisabilities |= MUTE + health = L.health + 100 //stoning damaged mobs will result in easier to shatter statues + intialTox = L.getToxLoss() + intialFire = L.getFireLoss() + intialBrute = L.getBruteLoss() + intialOxy = L.getOxyLoss() + if(ishuman(L)) + name = "statue of [L.name]" + if(L.gender == "female") + icon_state = "human_female" + else if(L.isMonkey()) + name = "statue of a monkey" + icon_state = "monkey" + else if(iscorgi(L)) + name = "statue of a corgi" + icon_state = "corgi" + desc = "If it takes forever, I will wait for you..." + + if(health == 0) //meaning if the statue didn't find a valid target + qdel(src) + return + + START_PROCESSING(SSobj, src) + ..() + +/obj/structure/closet/statue/process() + timer-- + for(var/mob/living/M in src) //Go-go gadget stasis field + M.setToxLoss(intialTox) + M.adjustFireLoss(intialFire - M.getFireLoss()) + M.adjustBruteLoss(intialBrute - M.getBruteLoss()) + M.setOxyLoss(intialOxy) + if (timer <= 0) + dump_contents() + STOP_PROCESSING(SSobj, src) + qdel(src) + +/obj/structure/closet/statue/dump_contents() + + for(var/obj/O in src) + O.loc = src.loc + + for(var/mob/living/M in src) + M.loc = src.loc + M.sdisabilities &= ~MUTE + M.take_overall_damage((M.health - health - 100),0) //any new damage the statue incurred is transfered to the mob + if(M.client) + M.client.eye = M.client.mob + M.client.perspective = MOB_PERSPECTIVE + +/obj/structure/closet/statue/open() + return + +/obj/structure/closet/statue/close() + return + +/obj/structure/closet/statue/toggle() + return + +/obj/structure/closet/statue/proc/check_health() + if(health <= 0) + for(var/mob/M in src) + shatter(M) + +/obj/structure/closet/statue/bullet_act(var/obj/item/projectile/Proj) + health -= Proj.get_structure_damage() + check_health() + + return + +/obj/structure/closet/statue/attack_generic(var/mob/user, damage, attacktext, environment_smash) + if(damage && environment_smash) + for(var/mob/M in src) + shatter(M) + +/obj/structure/closet/statue/ex_act(severity) + for(var/mob/M in src) + M.ex_act(severity) + health -= 60 / severity + check_health() + +/obj/structure/closet/statue/attackby(obj/item/I as obj, mob/user as mob) + health -= I.force + user.do_attack_animation(src) + visible_message("[user] strikes [src] with [I].") + check_health() + +/obj/structure/closet/statue/MouseDrop_T() + return + +/obj/structure/closet/statue/relaymove() + return + +/obj/structure/closet/statue/attack_hand() + return + +/obj/structure/closet/statue/verb_toggleopen() + return + +/obj/structure/closet/statue/update_icon() + return + +/obj/structure/closet/statue/proc/shatter(mob/user as mob) + if (user) + user.dust() + dump_contents() + visible_message("[src] shatters!.") + qdel(src) diff --git a/code/game/objects/structures/crates_lockers/closets/syndicate.dm b/code/game/objects/structures/crates_lockers/closets/syndicate.dm index 824da68846f..51d15feb1bf 100644 --- a/code/game/objects/structures/crates_lockers/closets/syndicate.dm +++ b/code/game/objects/structures/crates_lockers/closets/syndicate.dm @@ -1,121 +1,121 @@ -/obj/structure/closet/syndicate - name = "armory closet" - desc = "Why is this here?" - closet_appearance = /decl/closet_appearance/tactical/alt - -/obj/structure/closet/syndicate/personal - desc = "It's a storage unit for operative gear." - - starts_with = list( - /obj/item/weapon/tank/jetpack/oxygen, - /obj/item/clothing/mask/gas/syndicate, - /obj/item/clothing/under/syndicate, - /obj/item/clothing/head/helmet/space/void/merc, - /obj/item/clothing/suit/space/void/merc, - /obj/item/weapon/tool/crowbar/red, - /obj/item/weapon/cell/high, - /obj/item/weapon/card/id/syndicate, - /obj/item/device/multitool, - /obj/item/weapon/shield/energy, - /obj/item/clothing/shoes/magboots) - - -/obj/structure/closet/syndicate/suit - desc = "It's a storage unit for voidsuits." - - starts_with = list( - /obj/item/weapon/tank/jetpack/oxygen, - /obj/item/clothing/shoes/magboots, - /obj/item/clothing/suit/space/void/merc, - /obj/item/clothing/mask/gas/syndicate, - /obj/item/clothing/head/helmet/space/void/merc) - - -/obj/structure/closet/syndicate/nuclear - desc = "It's a storage unit for nuclear-operative gear." - - starts_with = list( - /obj/item/ammo_magazine/m10mm = 5, - /obj/item/weapon/storage/box/handcuffs, - /obj/item/weapon/storage/box/flashbangs, - /obj/item/weapon/gun/energy/gun = 5, - /obj/item/weapon/pinpointer/nukeop = 5, - /obj/item/device/pda/syndicate, - /obj/item/device/radio/uplink) - -/obj/structure/closet/syndicate/resources - desc = "An old, dusty locker." - -/obj/structure/closet/syndicate/resources/Initialize() - . = ..() - if(!contents.len) - var/common_min = 30 //Minimum amount of minerals in the stack for common minerals - var/common_max = 50 //Maximum amount of HONK in the stack for HONK common minerals - var/rare_min = 5 //Minimum HONK of HONK in the stack HONK HONK rare minerals - var/rare_max = 20 //Maximum HONK HONK HONK in the HONK for HONK rare HONK - - var/pickednum = rand(1, 50) - - //Sad trombone - if(pickednum == 1) - var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(src) - P.name = "IOU" - P.info = "Sorry man, we needed the money so we sold your stash. It's ok, we'll double our money for sure this time!" - - //Metal (common ore) - if(pickednum >= 2) - new /obj/item/stack/material/steel(src, rand(common_min, common_max)) - - //Glass (common ore) - if(pickednum >= 5) - new /obj/item/stack/material/glass(src, rand(common_min, common_max)) - - //Plasteel (common ore) Because it has a million more uses then phoron - if(pickednum >= 10) - new /obj/item/stack/material/plasteel(src, rand(common_min, common_max)) - - //Phoron (rare ore) - if(pickednum >= 15) - new /obj/item/stack/material/phoron(src, rand(rare_min, rare_max)) - - //Silver (rare ore) - if(pickednum >= 20) - new /obj/item/stack/material/silver(src, rand(rare_min, rare_max)) - - //Gold (rare ore) - if(pickednum >= 30) - new /obj/item/stack/material/gold(src, rand(rare_min, rare_max)) - - //Uranium (rare ore) - if(pickednum >= 40) - new /obj/item/stack/material/uranium(src, rand(rare_min, rare_max)) - - //Diamond (rare HONK) - if(pickednum >= 45) - new /obj/item/stack/material/diamond(src, rand(rare_min, rare_max)) - - //Jetpack (You hit the jackpot!) - if(pickednum == 50) - new /obj/item/weapon/tank/jetpack/carbondioxide(src) - -/obj/structure/closet/syndicate/resources/everything - desc = "It's an emergency storage closet for repairs." - -/obj/structure/closet/syndicate/resources/everything/Initialize() - var/list/resources = list( - /obj/item/stack/material/steel, - /obj/item/stack/material/glass, - /obj/item/stack/material/gold, - /obj/item/stack/material/silver, - /obj/item/stack/material/phoron, - /obj/item/stack/material/uranium, - /obj/item/stack/material/diamond, - /obj/item/stack/material/plasteel, - /obj/item/stack/rods - ) - - for(var/i = 0, i<2, i++) - for(var/res in resources) - new res(src, -1) - - return ..() +/obj/structure/closet/syndicate + name = "armory closet" + desc = "Why is this here?" + closet_appearance = /decl/closet_appearance/tactical/alt + +/obj/structure/closet/syndicate/personal + desc = "It's a storage unit for operative gear." + + starts_with = list( + /obj/item/weapon/tank/jetpack/oxygen, + /obj/item/clothing/mask/gas/syndicate, + /obj/item/clothing/under/syndicate, + /obj/item/clothing/head/helmet/space/void/merc, + /obj/item/clothing/suit/space/void/merc, + /obj/item/weapon/tool/crowbar/red, + /obj/item/weapon/cell/high, + /obj/item/weapon/card/id/syndicate, + /obj/item/device/multitool, + /obj/item/weapon/shield/energy, + /obj/item/clothing/shoes/magboots) + + +/obj/structure/closet/syndicate/suit + desc = "It's a storage unit for voidsuits." + + starts_with = list( + /obj/item/weapon/tank/jetpack/oxygen, + /obj/item/clothing/shoes/magboots, + /obj/item/clothing/suit/space/void/merc, + /obj/item/clothing/mask/gas/syndicate, + /obj/item/clothing/head/helmet/space/void/merc) + + +/obj/structure/closet/syndicate/nuclear + desc = "It's a storage unit for nuclear-operative gear." + + starts_with = list( + /obj/item/ammo_magazine/m10mm = 5, + /obj/item/weapon/storage/box/handcuffs, + /obj/item/weapon/storage/box/flashbangs, + /obj/item/weapon/gun/energy/gun = 5, + /obj/item/weapon/pinpointer/nukeop = 5, + /obj/item/device/pda/syndicate, + /obj/item/device/radio/uplink) + +/obj/structure/closet/syndicate/resources + desc = "An old, dusty locker." + +/obj/structure/closet/syndicate/resources/Initialize() + . = ..() + if(!contents.len) + var/common_min = 30 //Minimum amount of minerals in the stack for common minerals + var/common_max = 50 //Maximum amount of HONK in the stack for HONK common minerals + var/rare_min = 5 //Minimum HONK of HONK in the stack HONK HONK rare minerals + var/rare_max = 20 //Maximum HONK HONK HONK in the HONK for HONK rare HONK + + var/pickednum = rand(1, 50) + + //Sad trombone + if(pickednum == 1) + var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(src) + P.name = "IOU" + P.info = "Sorry man, we needed the money so we sold your stash. It's ok, we'll double our money for sure this time!" + + //Metal (common ore) + if(pickednum >= 2) + new /obj/item/stack/material/steel(src, rand(common_min, common_max)) + + //Glass (common ore) + if(pickednum >= 5) + new /obj/item/stack/material/glass(src, rand(common_min, common_max)) + + //Plasteel (common ore) Because it has a million more uses then phoron + if(pickednum >= 10) + new /obj/item/stack/material/plasteel(src, rand(common_min, common_max)) + + //Phoron (rare ore) + if(pickednum >= 15) + new /obj/item/stack/material/phoron(src, rand(rare_min, rare_max)) + + //Silver (rare ore) + if(pickednum >= 20) + new /obj/item/stack/material/silver(src, rand(rare_min, rare_max)) + + //Gold (rare ore) + if(pickednum >= 30) + new /obj/item/stack/material/gold(src, rand(rare_min, rare_max)) + + //Uranium (rare ore) + if(pickednum >= 40) + new /obj/item/stack/material/uranium(src, rand(rare_min, rare_max)) + + //Diamond (rare HONK) + if(pickednum >= 45) + new /obj/item/stack/material/diamond(src, rand(rare_min, rare_max)) + + //Jetpack (You hit the jackpot!) + if(pickednum == 50) + new /obj/item/weapon/tank/jetpack/carbondioxide(src) + +/obj/structure/closet/syndicate/resources/everything + desc = "It's an emergency storage closet for repairs." + +/obj/structure/closet/syndicate/resources/everything/Initialize() + var/list/resources = list( + /obj/item/stack/material/steel, + /obj/item/stack/material/glass, + /obj/item/stack/material/gold, + /obj/item/stack/material/silver, + /obj/item/stack/material/phoron, + /obj/item/stack/material/uranium, + /obj/item/stack/material/diamond, + /obj/item/stack/material/plasteel, + /obj/item/stack/rods + ) + + for(var/i = 0, i<2, i++) + for(var/res in resources) + new res(src, -1) + + return ..() diff --git a/code/game/objects/structures/crates_lockers/closets/utility_closets.dm b/code/game/objects/structures/crates_lockers/closets/utility_closets.dm index 8fc75b1c8a6..7946fd1e942 100644 --- a/code/game/objects/structures/crates_lockers/closets/utility_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/utility_closets.dm @@ -1,229 +1,229 @@ -/* Utility Closets - * Contains: - * Emergency Closet - * Fire Closet - * Tool Closet - * Radiation Closet - * Bombsuit Closet - * Hydrant - * First Aid - */ - -/* - * Emergency Closet - */ -/obj/structure/closet/emcloset - name = "emergency closet" - desc = "It's a storage unit for emergency breathmasks and O2 tanks." - closet_appearance = /decl/closet_appearance/oxygen - -/obj/structure/closet/emcloset/Initialize() - switch (pickweight(list("small" = 55, "aid" = 25, "tank" = 10, "both" = 10))) - //VOREStation Block Edit Start - Modified List - if ("small") - starts_with = list( - /obj/item/weapon/tank/emergency/oxygen = 2, - /obj/item/clothing/mask/breath = 2, - /obj/item/clothing/suit/space/emergency = 2, - /obj/item/clothing/head/helmet/space/emergency = 2) - if ("aid") - starts_with = list( - /obj/item/weapon/tank/emergency/oxygen, - /obj/item/weapon/storage/toolbox/emergency, - /obj/item/clothing/mask/breath, - /obj/item/clothing/suit/space/emergency, - /obj/item/clothing/head/helmet/space/emergency) - if ("tank") - starts_with = list( - /obj/item/weapon/tank/emergency/oxygen/engi = 2, - /obj/item/clothing/mask/breath = 2, - /obj/item/clothing/suit/space/emergency = 2, - /obj/item/clothing/head/helmet/space/emergency = 2) - if ("both") - starts_with = list( - /obj/item/weapon/storage/toolbox/emergency, - /obj/item/weapon/storage/firstaid/o2, - /obj/item/weapon/tank/emergency/oxygen/engi = 2, - /obj/item/clothing/mask/breath = 2, - /obj/item/clothing/suit/space/emergency = 2, - /obj/item/clothing/head/helmet/space/emergency = 2) - //VOREStation Block Edit End - - return ..() - -/obj/structure/closet/emcloset/legacy - starts_with = list( - /obj/item/weapon/tank/oxygen, - /obj/item/clothing/mask/gas) - -/* - * Fire Closet - */ -/obj/structure/closet/firecloset - name = "fire-safety closet" - desc = "It's a storage unit for fire-fighting supplies." - closet_appearance = /decl/closet_appearance/oxygen/fire - - starts_with = list( - /obj/item/clothing/suit/fire, - /obj/item/clothing/mask/gas, - /obj/item/weapon/tank/oxygen/red, - /obj/item/weapon/extinguisher, - /obj/item/clothing/head/hardhat/red) - -/obj/structure/closet/firecloset/full - starts_with = list( - /obj/item/clothing/suit/fire, - /obj/item/clothing/mask/gas, - /obj/item/device/flashlight, - /obj/item/weapon/tank/oxygen/red, - /obj/item/weapon/extinguisher, - /obj/item/clothing/head/hardhat/red) - -/obj/structure/closet/firecloset/full/double - starts_with = list( - /obj/item/clothing/suit/fire = 2, - /obj/item/clothing/mask/gas = 2, - /obj/item/device/flashlight = 2, - /obj/item/weapon/tank/oxygen/red = 2, - /obj/item/weapon/extinguisher = 2, - /obj/item/clothing/head/hardhat/red = 2) - -/obj/structure/closet/firecloset/full/atmos - name = "atmos fire-safety closet" - desc = "It's a storage unit for atmospheric fire-fighting supplies." - closet_appearance = /decl/closet_appearance/oxygen/fire/atmos - - starts_with = list( - /obj/item/clothing/suit/fire/heavy, - /obj/item/weapon/tank/oxygen/red, - /obj/item/weapon/extinguisher/atmo, - /obj/item/device/flashlight, - /obj/item/clothing/head/hardhat/firefighter/atmos) - -/* - * Tool Closet - */ -/obj/structure/closet/toolcloset - name = "tool closet" - desc = "It's a storage unit for tools." - closet_appearance = /decl/closet_appearance/secure_closet/engineering/tools - -/obj/structure/closet/toolcloset/Initialize() - starts_with = list() - if(prob(40)) - starts_with += /obj/item/clothing/suit/storage/hazardvest - if(prob(70)) - starts_with += /obj/item/device/flashlight - if(prob(70)) - starts_with += /obj/item/weapon/tool/screwdriver - if(prob(70)) - starts_with += /obj/item/weapon/tool/wrench - if(prob(70)) - starts_with += /obj/item/weapon/weldingtool - if(prob(70)) - starts_with += /obj/item/weapon/tool/crowbar - if(prob(70)) - starts_with += /obj/item/weapon/tool/wirecutters - if(prob(70)) - starts_with += /obj/item/device/t_scanner - if(prob(20)) - starts_with += /obj/item/weapon/storage/belt/utility - if(prob(30)) - starts_with += /obj/item/stack/cable_coil/random - if(prob(30)) - starts_with += /obj/item/stack/cable_coil/random - if(prob(30)) - starts_with += /obj/item/stack/cable_coil/random - if(prob(20)) - starts_with += /obj/item/device/multitool - if(prob(5)) - starts_with += /obj/item/clothing/gloves/yellow - if(prob(40)) - starts_with += /obj/item/clothing/head/hardhat - if(prob(30)) - starts_with += /obj/item/weapon/reagent_containers/spray/windowsealant //VOREStation Add - return ..() - -/* - * Radiation Closet - */ -/obj/structure/closet/radiation - name = "radiation suit closet" - desc = "It's a storage unit for rad-protective suits." - closet_appearance = /decl/closet_appearance/secure_closet/engineering/tools/radiation - - starts_with = list( - /obj/item/clothing/suit/radiation = 2, - /obj/item/clothing/head/radiation = 2, - /obj/item/device/geiger = 2) - -/* - * Bombsuit closet - */ -/obj/structure/closet/bombcloset - name = "\improper EOD closet" - desc = "It's a storage unit for explosion-protective suits." - closet_appearance = /decl/closet_appearance/bomb - - starts_with = list( - /obj/item/clothing/suit/bomb_suit, - /obj/item/clothing/under/color/black, - /obj/item/clothing/shoes/black, - /obj/item/clothing/head/bomb_hood) - -/obj/structure/closet/bombcloset/double - starts_with = list( - /obj/item/clothing/suit/bomb_suit = 2, - /obj/item/clothing/under/color/black = 2, - /obj/item/clothing/shoes/black = 2, - /obj/item/clothing/head/bomb_hood = 2) - -/obj/structure/closet/bombclosetsecurity - name = "\improper EOD closet" - desc = "It's a storage unit for explosion-protective suits." - closet_appearance = /decl/closet_appearance/bomb/security - - starts_with = list( - /obj/item/clothing/suit/bomb_suit/security, - /obj/item/clothing/under/rank/security, - /obj/item/clothing/shoes/brown, - /obj/item/clothing/head/bomb_hood/security) - -/* - * Hydrant - */ -/obj/structure/closet/hydrant //wall mounted fire closet - name = "fire-safety closet" - desc = "It's a storage unit for fire-fighting supplies." - icon = 'icons/obj/closets/bases/wall.dmi' - closet_appearance = /decl/closet_appearance/wall/hydrant - plane = TURF_PLANE - layer = ABOVE_TURF_LAYER - anchored = TRUE - density = FALSE - wall_mounted = 1 - store_mobs = 0 - - starts_with = list( - /obj/item/clothing/suit/fire/firefighter, - /obj/item/clothing/mask/gas, - /obj/item/device/flashlight, - /obj/item/weapon/tank/oxygen/red, - /obj/item/weapon/extinguisher, - /obj/item/clothing/head/hardhat/red) - -/* - * First Aid - */ -/obj/structure/closet/medical_wall //wall mounted medical closet - name = "first-aid closet" - desc = "It's wall-mounted storage unit for first aid supplies." - icon = 'icons/obj/closets/bases/wall.dmi' - closet_appearance = /decl/closet_appearance/wall/medical - plane = TURF_PLANE - layer = ABOVE_TURF_LAYER - anchored = TRUE - density = FALSE - wall_mounted = 1 - store_mobs = 0 +/* Utility Closets + * Contains: + * Emergency Closet + * Fire Closet + * Tool Closet + * Radiation Closet + * Bombsuit Closet + * Hydrant + * First Aid + */ + +/* + * Emergency Closet + */ +/obj/structure/closet/emcloset + name = "emergency closet" + desc = "It's a storage unit for emergency breathmasks and O2 tanks." + closet_appearance = /decl/closet_appearance/oxygen + +/obj/structure/closet/emcloset/Initialize() + switch (pickweight(list("small" = 55, "aid" = 25, "tank" = 10, "both" = 10))) + //VOREStation Block Edit Start - Modified List + if ("small") + starts_with = list( + /obj/item/weapon/tank/emergency/oxygen = 2, + /obj/item/clothing/mask/breath = 2, + /obj/item/clothing/suit/space/emergency = 2, + /obj/item/clothing/head/helmet/space/emergency = 2) + if ("aid") + starts_with = list( + /obj/item/weapon/tank/emergency/oxygen, + /obj/item/weapon/storage/toolbox/emergency, + /obj/item/clothing/mask/breath, + /obj/item/clothing/suit/space/emergency, + /obj/item/clothing/head/helmet/space/emergency) + if ("tank") + starts_with = list( + /obj/item/weapon/tank/emergency/oxygen/engi = 2, + /obj/item/clothing/mask/breath = 2, + /obj/item/clothing/suit/space/emergency = 2, + /obj/item/clothing/head/helmet/space/emergency = 2) + if ("both") + starts_with = list( + /obj/item/weapon/storage/toolbox/emergency, + /obj/item/weapon/storage/firstaid/o2, + /obj/item/weapon/tank/emergency/oxygen/engi = 2, + /obj/item/clothing/mask/breath = 2, + /obj/item/clothing/suit/space/emergency = 2, + /obj/item/clothing/head/helmet/space/emergency = 2) + //VOREStation Block Edit End + + return ..() + +/obj/structure/closet/emcloset/legacy + starts_with = list( + /obj/item/weapon/tank/oxygen, + /obj/item/clothing/mask/gas) + +/* + * Fire Closet + */ +/obj/structure/closet/firecloset + name = "fire-safety closet" + desc = "It's a storage unit for fire-fighting supplies." + closet_appearance = /decl/closet_appearance/oxygen/fire + + starts_with = list( + /obj/item/clothing/suit/fire, + /obj/item/clothing/mask/gas, + /obj/item/weapon/tank/oxygen/red, + /obj/item/weapon/extinguisher, + /obj/item/clothing/head/hardhat/red) + +/obj/structure/closet/firecloset/full + starts_with = list( + /obj/item/clothing/suit/fire, + /obj/item/clothing/mask/gas, + /obj/item/device/flashlight, + /obj/item/weapon/tank/oxygen/red, + /obj/item/weapon/extinguisher, + /obj/item/clothing/head/hardhat/red) + +/obj/structure/closet/firecloset/full/double + starts_with = list( + /obj/item/clothing/suit/fire = 2, + /obj/item/clothing/mask/gas = 2, + /obj/item/device/flashlight = 2, + /obj/item/weapon/tank/oxygen/red = 2, + /obj/item/weapon/extinguisher = 2, + /obj/item/clothing/head/hardhat/red = 2) + +/obj/structure/closet/firecloset/full/atmos + name = "atmos fire-safety closet" + desc = "It's a storage unit for atmospheric fire-fighting supplies." + closet_appearance = /decl/closet_appearance/oxygen/fire/atmos + + starts_with = list( + /obj/item/clothing/suit/fire/heavy, + /obj/item/weapon/tank/oxygen/red, + /obj/item/weapon/extinguisher/atmo, + /obj/item/device/flashlight, + /obj/item/clothing/head/hardhat/firefighter/atmos) + +/* + * Tool Closet + */ +/obj/structure/closet/toolcloset + name = "tool closet" + desc = "It's a storage unit for tools." + closet_appearance = /decl/closet_appearance/secure_closet/engineering/tools + +/obj/structure/closet/toolcloset/Initialize() + starts_with = list() + if(prob(40)) + starts_with += /obj/item/clothing/suit/storage/hazardvest + if(prob(70)) + starts_with += /obj/item/device/flashlight + if(prob(70)) + starts_with += /obj/item/weapon/tool/screwdriver + if(prob(70)) + starts_with += /obj/item/weapon/tool/wrench + if(prob(70)) + starts_with += /obj/item/weapon/weldingtool + if(prob(70)) + starts_with += /obj/item/weapon/tool/crowbar + if(prob(70)) + starts_with += /obj/item/weapon/tool/wirecutters + if(prob(70)) + starts_with += /obj/item/device/t_scanner + if(prob(20)) + starts_with += /obj/item/weapon/storage/belt/utility + if(prob(30)) + starts_with += /obj/item/stack/cable_coil/random + if(prob(30)) + starts_with += /obj/item/stack/cable_coil/random + if(prob(30)) + starts_with += /obj/item/stack/cable_coil/random + if(prob(20)) + starts_with += /obj/item/device/multitool + if(prob(5)) + starts_with += /obj/item/clothing/gloves/yellow + if(prob(40)) + starts_with += /obj/item/clothing/head/hardhat + if(prob(30)) + starts_with += /obj/item/weapon/reagent_containers/spray/windowsealant //VOREStation Add + return ..() + +/* + * Radiation Closet + */ +/obj/structure/closet/radiation + name = "radiation suit closet" + desc = "It's a storage unit for rad-protective suits." + closet_appearance = /decl/closet_appearance/secure_closet/engineering/tools/radiation + + starts_with = list( + /obj/item/clothing/suit/radiation = 2, + /obj/item/clothing/head/radiation = 2, + /obj/item/device/geiger = 2) + +/* + * Bombsuit closet + */ +/obj/structure/closet/bombcloset + name = "\improper EOD closet" + desc = "It's a storage unit for explosion-protective suits." + closet_appearance = /decl/closet_appearance/bomb + + starts_with = list( + /obj/item/clothing/suit/bomb_suit, + /obj/item/clothing/under/color/black, + /obj/item/clothing/shoes/black, + /obj/item/clothing/head/bomb_hood) + +/obj/structure/closet/bombcloset/double + starts_with = list( + /obj/item/clothing/suit/bomb_suit = 2, + /obj/item/clothing/under/color/black = 2, + /obj/item/clothing/shoes/black = 2, + /obj/item/clothing/head/bomb_hood = 2) + +/obj/structure/closet/bombclosetsecurity + name = "\improper EOD closet" + desc = "It's a storage unit for explosion-protective suits." + closet_appearance = /decl/closet_appearance/bomb/security + + starts_with = list( + /obj/item/clothing/suit/bomb_suit/security, + /obj/item/clothing/under/rank/security, + /obj/item/clothing/shoes/brown, + /obj/item/clothing/head/bomb_hood/security) + +/* + * Hydrant + */ +/obj/structure/closet/hydrant //wall mounted fire closet + name = "fire-safety closet" + desc = "It's a storage unit for fire-fighting supplies." + icon = 'icons/obj/closets/bases/wall.dmi' + closet_appearance = /decl/closet_appearance/wall/hydrant + plane = TURF_PLANE + layer = ABOVE_TURF_LAYER + anchored = TRUE + density = FALSE + wall_mounted = 1 + store_mobs = 0 + + starts_with = list( + /obj/item/clothing/suit/fire/firefighter, + /obj/item/clothing/mask/gas, + /obj/item/device/flashlight, + /obj/item/weapon/tank/oxygen/red, + /obj/item/weapon/extinguisher, + /obj/item/clothing/head/hardhat/red) + +/* + * First Aid + */ +/obj/structure/closet/medical_wall //wall mounted medical closet + name = "first-aid closet" + desc = "It's wall-mounted storage unit for first aid supplies." + icon = 'icons/obj/closets/bases/wall.dmi' + closet_appearance = /decl/closet_appearance/wall/medical + plane = TURF_PLANE + layer = ABOVE_TURF_LAYER + anchored = TRUE + density = FALSE + wall_mounted = 1 + store_mobs = 0 diff --git a/code/game/objects/structures/crates_lockers/closets/utility_closets_vr.dm b/code/game/objects/structures/crates_lockers/closets/utility_closets_vr.dm index c126b965f3a..1f848762226 100644 --- a/code/game/objects/structures/crates_lockers/closets/utility_closets_vr.dm +++ b/code/game/objects/structures/crates_lockers/closets/utility_closets_vr.dm @@ -1,7 +1,7 @@ -/obj/structure/closet/firecloset/Initialize() - starts_with += /obj/item/weapon/storage/toolbox/emergency - return ..() - -/obj/structure/closet/hydrant/New() - starts_with += /obj/item/weapon/storage/toolbox/emergency - return ..() +/obj/structure/closet/firecloset/Initialize() + starts_with += /obj/item/weapon/storage/toolbox/emergency + return ..() + +/obj/structure/closet/hydrant/New() + starts_with += /obj/item/weapon/storage/toolbox/emergency + return ..() diff --git a/code/game/objects/structures/crates_lockers/largecrate.dm b/code/game/objects/structures/crates_lockers/largecrate.dm index fdb658daacb..d9902638ffc 100644 --- a/code/game/objects/structures/crates_lockers/largecrate.dm +++ b/code/game/objects/structures/crates_lockers/largecrate.dm @@ -1,132 +1,132 @@ -/obj/structure/largecrate - name = "large crate" - desc = "A hefty wooden crate." - icon = 'icons/obj/storage.dmi' - icon_state = "densecrate" - density = TRUE - var/list/starts_with - -/obj/structure/largecrate/Initialize() - . = ..() - if(starts_with) - create_objects_in_loc(src, starts_with) - starts_with = null - for(var/obj/I in src.loc) - if(I.density || I.anchored || I == src || !I.simulated) - continue - I.forceMove(src) - update_icon() - -/obj/structure/largecrate/attack_hand(mob/user as mob) - to_chat(user, "You need a crowbar to pry this open!") - return - -/obj/structure/largecrate/attackby(obj/item/weapon/W as obj, mob/user as mob) - var/turf/T = get_turf(src) - if(!T) - to_chat(user, "You can't open this here!") - if(W.has_tool_quality(TOOL_CROWBAR)) - new /obj/item/stack/material/wood(src) - - for(var/atom/movable/AM in contents) - if(AM.simulated) - AM.forceMove(T) - //VOREStation Add Start - if(isanimal(AM)) - var/mob/living/simple_mob/AMBLINAL = AM - if(!AMBLINAL.mind) - AMBLINAL.ghostjoin = 1 - AMBLINAL.ghostjoin_icon() - active_ghost_pods |= AMBLINAL - //VOREStation Add End - user.visible_message("[user] pries \the [src] open.", \ - "You pry open \the [src].", \ - "You hear splitting wood.") - qdel(src) - else - return attack_hand(user) - -/obj/structure/largecrate/mule - name = "MULE crate" - -/obj/structure/largecrate/hoverpod - name = "\improper Hoverpod assembly crate" - desc = "You aren't sure how this crate is so light, but the Wulf Aeronautics logo might be a hint." - icon_state = "vehiclecrate" - -/obj/structure/largecrate/hoverpod/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.has_tool_quality(TOOL_CROWBAR)) - var/obj/item/mecha_parts/mecha_equipment/ME - var/obj/mecha/working/hoverpod/H = new (loc) - - ME = new /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp - ME.attach(H) - ME = new /obj/item/mecha_parts/mecha_equipment/tool/passenger - ME.attach(H) - ..() - -/obj/structure/largecrate/donksoftvendor - name = "\improper Donk-Soft vendor crate" - desc = "A hefty wooden crate displaying the logo of Donk-Soft. It's rather heavy." - starts_with = list(/obj/machinery/vending/donksoft) - -/obj/structure/largecrate/vehicle - name = "vehicle crate" - desc = "Wulf Aeronautics says it comes in a box for the consumer's sake... How is this so light?" - icon_state = "vehiclecrate" - -/obj/structure/largecrate/vehicle/Initialize() - . = ..() - for(var/obj/O in contents) - O.update_icon() - -/obj/structure/largecrate/vehicle/bike - name = "spacebike crate" - starts_with = list(/obj/structure/vehiclecage/spacebike) - -/obj/structure/largecrate/vehicle/quadbike - name = "\improper ATV crate" - desc = "A hefty wooden crate proudly displaying the logo of Ward-Takahashi's automotive division." - starts_with = list(/obj/structure/vehiclecage/quadbike) - -/obj/structure/largecrate/vehicle/quadtrailer - name = "\improper ATV trailer crate" - desc = "A hefty wooden crate proudly displaying the logo of Ward-Takahashi's automotive division." - starts_with = list(/obj/structure/vehiclecage/quadtrailer) - -/obj/structure/largecrate/animal - icon_state = "crittercrate" - desc = "A hefty wooden crate with air holes. It is marked with the logo of NanoTrasen Pastures and the slogan, '90% less cloning defects* than competing brands**, or your money back***!'" - -/obj/structure/largecrate/animal/mulebot - name = "Mulebot crate" - desc = "A hefty wooden crate labelled 'Proud Product of the Xion Manufacturing Group'" - icon_state = "mulecrate" - starts_with = list(/mob/living/bot/mulebot) - -/obj/structure/largecrate/animal/corgi - name = "corgi carrier" - starts_with = list(/mob/living/simple_mob/animal/passive/dog/corgi) - -/obj/structure/largecrate/animal/cow - name = "cow crate" - starts_with = list(/mob/living/simple_mob/animal/passive/cow) - -/obj/structure/largecrate/animal/goat - name = "goat crate" - starts_with = list(/mob/living/simple_mob/animal/goat) - -/obj/structure/largecrate/animal/cat - name = "cat carrier" - starts_with = list(/mob/living/simple_mob/animal/passive/cat) - -/obj/structure/largecrate/animal/cat/bones - starts_with = list(/mob/living/simple_mob/animal/passive/cat/bones) - -/obj/structure/largecrate/animal/chick - name = "chicken crate" - starts_with = list(/mob/living/simple_mob/animal/passive/chick = 5) - -/obj/structure/largecrate/animal/catslug - name = "catslug carrier" - starts_with = list(/mob/living/simple_mob/vore/alienanimals/catslug) +/obj/structure/largecrate + name = "large crate" + desc = "A hefty wooden crate." + icon = 'icons/obj/storage.dmi' + icon_state = "densecrate" + density = TRUE + var/list/starts_with + +/obj/structure/largecrate/Initialize() + . = ..() + if(starts_with) + create_objects_in_loc(src, starts_with) + starts_with = null + for(var/obj/I in src.loc) + if(I.density || I.anchored || I == src || !I.simulated) + continue + I.forceMove(src) + update_icon() + +/obj/structure/largecrate/attack_hand(mob/user as mob) + to_chat(user, "You need a crowbar to pry this open!") + return + +/obj/structure/largecrate/attackby(obj/item/weapon/W as obj, mob/user as mob) + var/turf/T = get_turf(src) + if(!T) + to_chat(user, "You can't open this here!") + if(W.has_tool_quality(TOOL_CROWBAR)) + new /obj/item/stack/material/wood(src) + + for(var/atom/movable/AM in contents) + if(AM.simulated) + AM.forceMove(T) + //VOREStation Add Start + if(isanimal(AM)) + var/mob/living/simple_mob/AMBLINAL = AM + if(!AMBLINAL.mind) + AMBLINAL.ghostjoin = 1 + AMBLINAL.ghostjoin_icon() + active_ghost_pods |= AMBLINAL + //VOREStation Add End + user.visible_message("[user] pries \the [src] open.", \ + "You pry open \the [src].", \ + "You hear splitting wood.") + qdel(src) + else + return attack_hand(user) + +/obj/structure/largecrate/mule + name = "MULE crate" + +/obj/structure/largecrate/hoverpod + name = "\improper Hoverpod assembly crate" + desc = "You aren't sure how this crate is so light, but the Wulf Aeronautics logo might be a hint." + icon_state = "vehiclecrate" + +/obj/structure/largecrate/hoverpod/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_CROWBAR)) + var/obj/item/mecha_parts/mecha_equipment/ME + var/obj/mecha/working/hoverpod/H = new (loc) + + ME = new /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp + ME.attach(H) + ME = new /obj/item/mecha_parts/mecha_equipment/tool/passenger + ME.attach(H) + ..() + +/obj/structure/largecrate/donksoftvendor + name = "\improper Donk-Soft vendor crate" + desc = "A hefty wooden crate displaying the logo of Donk-Soft. It's rather heavy." + starts_with = list(/obj/machinery/vending/donksoft) + +/obj/structure/largecrate/vehicle + name = "vehicle crate" + desc = "Wulf Aeronautics says it comes in a box for the consumer's sake... How is this so light?" + icon_state = "vehiclecrate" + +/obj/structure/largecrate/vehicle/Initialize() + . = ..() + for(var/obj/O in contents) + O.update_icon() + +/obj/structure/largecrate/vehicle/bike + name = "spacebike crate" + starts_with = list(/obj/structure/vehiclecage/spacebike) + +/obj/structure/largecrate/vehicle/quadbike + name = "\improper ATV crate" + desc = "A hefty wooden crate proudly displaying the logo of Ward-Takahashi's automotive division." + starts_with = list(/obj/structure/vehiclecage/quadbike) + +/obj/structure/largecrate/vehicle/quadtrailer + name = "\improper ATV trailer crate" + desc = "A hefty wooden crate proudly displaying the logo of Ward-Takahashi's automotive division." + starts_with = list(/obj/structure/vehiclecage/quadtrailer) + +/obj/structure/largecrate/animal + icon_state = "crittercrate" + desc = "A hefty wooden crate with air holes. It is marked with the logo of NanoTrasen Pastures and the slogan, '90% less cloning defects* than competing brands**, or your money back***!'" + +/obj/structure/largecrate/animal/mulebot + name = "Mulebot crate" + desc = "A hefty wooden crate labelled 'Proud Product of the Xion Manufacturing Group'" + icon_state = "mulecrate" + starts_with = list(/mob/living/bot/mulebot) + +/obj/structure/largecrate/animal/corgi + name = "corgi carrier" + starts_with = list(/mob/living/simple_mob/animal/passive/dog/corgi) + +/obj/structure/largecrate/animal/cow + name = "cow crate" + starts_with = list(/mob/living/simple_mob/animal/passive/cow) + +/obj/structure/largecrate/animal/goat + name = "goat crate" + starts_with = list(/mob/living/simple_mob/animal/goat) + +/obj/structure/largecrate/animal/cat + name = "cat carrier" + starts_with = list(/mob/living/simple_mob/animal/passive/cat) + +/obj/structure/largecrate/animal/cat/bones + starts_with = list(/mob/living/simple_mob/animal/passive/cat/bones) + +/obj/structure/largecrate/animal/chick + name = "chicken crate" + starts_with = list(/mob/living/simple_mob/animal/passive/chick = 5) + +/obj/structure/largecrate/animal/catslug + name = "catslug carrier" + starts_with = list(/mob/living/simple_mob/vore/alienanimals/catslug) diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index fc1ae0ef597..362d966ae5c 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -1,81 +1,81 @@ -/obj/structure/displaycase - name = "display case" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "glassbox1" - desc = "A display case for prized possessions. It taunts you to kick it." - density = TRUE - anchored = TRUE - unacidable = TRUE//Dissolving the case would also delete the gun. - var/health = 30 - var/occupied = 1 - var/destroyed = 0 - -/obj/structure/displaycase/ex_act(severity) - switch(severity) - if (1) - new /obj/item/weapon/material/shard( src.loc ) - if (occupied) - new /obj/item/weapon/gun/energy/captain( src.loc ) - occupied = 0 - qdel(src) - if (2) - if (prob(50)) - src.health -= 15 - src.healthcheck() - if (3) - if (prob(50)) - src.health -= 5 - src.healthcheck() - - -/obj/structure/displaycase/bullet_act(var/obj/item/projectile/Proj) - health -= Proj.get_structure_damage() - ..() - src.healthcheck() - return - -/obj/structure/displaycase/proc/healthcheck() - if (src.health <= 0) - if (!( src.destroyed )) - src.density = FALSE - src.destroyed = 1 - new /obj/item/weapon/material/shard( src.loc ) - playsound(src, "shatter", 70, 1) - update_icon() - else - playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) - return - -/obj/structure/displaycase/update_icon() - if(src.destroyed) - src.icon_state = "glassboxb[src.occupied]" - else - src.icon_state = "glassbox[src.occupied]" - return - - -/obj/structure/displaycase/attackby(obj/item/weapon/W as obj, mob/user as mob) - user.setClickCooldown(user.get_attack_speed(W)) - user.do_attack_animation(src) - playsound(src, 'sound/effects/Glasshit.ogg', 50, 1) - src.health -= W.force - src.healthcheck() - ..() - return - -/obj/structure/displaycase/attack_hand(mob/user as mob) - if (src.destroyed && src.occupied) - new /obj/item/weapon/gun/energy/captain( src.loc ) - to_chat(user, "You deactivate the hover field built into the case.") - src.occupied = 0 - src.add_fingerprint(user) - update_icon() - return - else - to_chat(usr, "You kick the display case.") - for(var/mob/O in oviewers()) - if ((O.client && !( O.blinded ))) - to_chat(O, "[usr] kicks the display case.") - src.health -= 2 - healthcheck() - return +/obj/structure/displaycase + name = "display case" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "glassbox1" + desc = "A display case for prized possessions. It taunts you to kick it." + density = TRUE + anchored = TRUE + unacidable = TRUE//Dissolving the case would also delete the gun. + var/health = 30 + var/occupied = 1 + var/destroyed = 0 + +/obj/structure/displaycase/ex_act(severity) + switch(severity) + if (1) + new /obj/item/weapon/material/shard( src.loc ) + if (occupied) + new /obj/item/weapon/gun/energy/captain( src.loc ) + occupied = 0 + qdel(src) + if (2) + if (prob(50)) + src.health -= 15 + src.healthcheck() + if (3) + if (prob(50)) + src.health -= 5 + src.healthcheck() + + +/obj/structure/displaycase/bullet_act(var/obj/item/projectile/Proj) + health -= Proj.get_structure_damage() + ..() + src.healthcheck() + return + +/obj/structure/displaycase/proc/healthcheck() + if (src.health <= 0) + if (!( src.destroyed )) + src.density = FALSE + src.destroyed = 1 + new /obj/item/weapon/material/shard( src.loc ) + playsound(src, "shatter", 70, 1) + update_icon() + else + playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) + return + +/obj/structure/displaycase/update_icon() + if(src.destroyed) + src.icon_state = "glassboxb[src.occupied]" + else + src.icon_state = "glassbox[src.occupied]" + return + + +/obj/structure/displaycase/attackby(obj/item/weapon/W as obj, mob/user as mob) + user.setClickCooldown(user.get_attack_speed(W)) + user.do_attack_animation(src) + playsound(src, 'sound/effects/Glasshit.ogg', 50, 1) + src.health -= W.force + src.healthcheck() + ..() + return + +/obj/structure/displaycase/attack_hand(mob/user as mob) + if (src.destroyed && src.occupied) + new /obj/item/weapon/gun/energy/captain( src.loc ) + to_chat(user, "You deactivate the hover field built into the case.") + src.occupied = 0 + src.add_fingerprint(user) + update_icon() + return + else + to_chat(usr, "You kick the display case.") + for(var/mob/O in oviewers()) + if ((O.client && !( O.blinded ))) + to_chat(O, "[usr] kicks the display case.") + src.health -= 2 + healthcheck() + return diff --git a/code/game/objects/structures/door_assembly.dm b/code/game/objects/structures/door_assembly.dm index 6ca92a3b377..3a024f647b8 100644 --- a/code/game/objects/structures/door_assembly.dm +++ b/code/game/objects/structures/door_assembly.dm @@ -1,329 +1,329 @@ -/obj/structure/door_assembly - name = "airlock assembly" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_as_0" - anchored = FALSE - density = TRUE - w_class = ITEMSIZE_HUGE - var/state = 0 - var/base_icon_state = "" - var/base_name = "airlock" - var/obj/item/weapon/airlock_electronics/electronics = null - var/airlock_type = "" //the type path of the airlock once completed - var/glass_type = "/glass" - var/glass = 0 // 0 = glass can be installed. -1 = glass can't be installed. 1 = glass is already installed. Text = mineral plating is installed instead. - var/created_name = null - -/obj/structure/door_assembly/New() - update_state() - -/obj/structure/door_assembly/door_assembly_com - base_icon_state = "com" - base_name = "Command airlock" - glass_type = "/glass_command" - airlock_type = "/command" - -/obj/structure/door_assembly/door_assembly_sec - base_icon_state = "sec" - base_name = "Security airlock" - glass_type = "/glass_security" - airlock_type = "/security" - -/obj/structure/door_assembly/door_assembly_eng - base_icon_state = "eng" - base_name = "Engineering airlock" - glass_type = "/glass_engineering" - airlock_type = "/engineering" - -/obj/structure/door_assembly/door_assembly_eat - base_icon_state = "eat" - base_name = "Engineering atmos airlock" - glass_type = "/glass_engineeringatmos" - airlock_type = "/engineering" - -/obj/structure/door_assembly/door_assembly_min - base_icon_state = "min" - base_name = "Mining airlock" - glass_type = "/glass_mining" - airlock_type = "/mining" - -/obj/structure/door_assembly/door_assembly_atmo - base_icon_state = "atmo" - base_name = "Atmospherics airlock" - glass_type = "/glass_atmos" - airlock_type = "/atmos" - -/obj/structure/door_assembly/door_assembly_research - base_icon_state = "res" - base_name = "Research airlock" - glass_type = "/glass_research" - airlock_type = "/research" - -/obj/structure/door_assembly/door_assembly_science - base_icon_state = "sci" - base_name = "Science airlock" - glass_type = "/glass_science" - airlock_type = "/science" - -/obj/structure/door_assembly/door_assembly_med - base_icon_state = "med" - base_name = "Medical airlock" - glass_type = "/glass_medical" - airlock_type = "/medical" - -/obj/structure/door_assembly/door_assembly_ext - base_icon_state = "ext" - base_name = "External airlock" - glass_type = "/glass_external" - airlock_type = "/external" - -/obj/structure/door_assembly/door_assembly_mai - base_icon_state = "mai" - base_name = "Maintenance airlock" - airlock_type = "/maintenance" - glass = -1 - -/obj/structure/door_assembly/door_assembly_fre - base_icon_state = "fre" - base_name = "Freezer airlock" - airlock_type = "/freezer" - glass = -1 - -/obj/structure/door_assembly/door_assembly_hatch - base_icon_state = "hatch" - base_name = "airtight hatch" - airlock_type = "/hatch" - glass = -1 - -/obj/structure/door_assembly/door_assembly_mhatch - base_icon_state = "mhatch" - base_name = "maintenance hatch" - airlock_type = "/maintenance_hatch" - glass = -1 - -/obj/structure/door_assembly/door_assembly_highsecurity // Borrowing this until WJohnston makes sprites for the assembly - base_icon_state = "highsec" - base_name = "high security airlock" - airlock_type = "/highsecurity" - glass = -1 - -/obj/structure/door_assembly/door_assembly_voidcraft - base_icon_state = "voidcraft" - base_name = "voidcraft hatch" - airlock_type = "/voidcraft" - glass = -1 - -/obj/structure/door_assembly/door_assembly_voidcraft/vertical - base_icon_state = "voidcraft_vertical" - airlock_type = "/voidcraft/vertical" - -/obj/structure/door_assembly/door_assembly_alien - base_icon_state = "alien" - base_name = "alien airlock" - airlock_type = "/alien" - glass = -1 - -/obj/structure/door_assembly/multi_tile - icon = 'icons/obj/doors/door_assembly2x1.dmi' - dir = EAST - var/width = 1 - -/*Temporary until we get sprites. - glass_type = "/multi_tile/glass" - airlock_type = "/multi_tile/maint" - glass = 1*/ - base_icon_state = "g" //Remember to delete this line when reverting "glass" var to 1. - airlock_type = "/multi_tile/glass" - glass = -1 //To prevent bugs in deconstruction process. - -/obj/structure/door_assembly/multi_tile/New() - if(dir in list(EAST, WEST)) - bound_width = width * world.icon_size - bound_height = world.icon_size - else - bound_width = world.icon_size - bound_height = width * world.icon_size - update_state() - -/obj/structure/door_assembly/multi_tile/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(dir in list(EAST, WEST)) - bound_width = width * world.icon_size - bound_height = world.icon_size - else - bound_width = world.icon_size - bound_height = width * world.icon_size - -/obj/structure/door_assembly/proc/rename_door(mob/living/user) - var/t = sanitizeSafe(tgui_input_text(user, "Enter the name for the [base_name].", src.name, src.created_name, MAX_NAME_LEN), MAX_NAME_LEN) - if(!in_range(src, user) && src.loc != user) return - created_name = t - update_state() - -/obj/structure/door_assembly/attack_robot(mob/living/silicon/robot/user) - if(Adjacent(user) && (user.module && (istype(user.module,/obj/item/weapon/robot_module/robot/engineering)) \ - || istype(user.module,/obj/item/weapon/robot_module/drone))) //Only drone (and engiborg) needs this. - rename_door(user) - -/obj/structure/door_assembly/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/pen)) - rename_door(user) - return - - if(W.has_tool_quality(TOOL_WELDER) && ( (istext(glass)) || (glass == 1) || (!anchored) )) - var/obj/item/weapon/weldingtool/WT = W.get_welder() - if (WT.remove_fuel(0, user)) - playsound(src, WT.usesound, 50, 1) - if(istext(glass)) - user.visible_message("[user] welds the [glass] plating off the airlock assembly.", "You start to weld the [glass] plating off the airlock assembly.") - if(do_after(user, 4 SECONDS * WT.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src || !WT.isOn()) return - to_chat(user, "You welded the [glass] plating off!") - var/M = text2path("/obj/item/stack/material/[glass]") - new M(src.loc, 2) - glass = 0 - else if(glass == 1) - user.visible_message("[user] welds the glass panel out of the airlock assembly.", "You start to weld the glass panel out of the airlock assembly.") - if(do_after(user, 4 SECONDS * WT.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src || !WT.isOn()) return - to_chat(user, "You welded the glass panel out!") - new /obj/item/stack/material/glass/reinforced(src.loc) - glass = 0 - else if(!anchored) - user.visible_message("[user] dissassembles the airlock assembly.", "You start to dissassemble the airlock assembly.") - if(do_after(user, 4 SECONDS * WT.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src || !WT.isOn()) return - to_chat(user, "You dissasembled the airlock assembly!") - new /obj/item/stack/material/steel(src.loc, 4) - qdel (src) - else - to_chat(user, "You need more welding fuel.") - return - - else if(W.has_tool_quality(TOOL_WRENCH) && state == 0) - playsound(src, W.usesound, 100, 1) - if(anchored) - user.visible_message("[user] begins unsecuring the airlock assembly from the floor.", "You starts unsecuring the airlock assembly from the floor.") - else - user.visible_message("[user] begins securing the airlock assembly to the floor.", "You starts securing the airlock assembly to the floor.") - - if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src) return - to_chat(user, "You [anchored? "un" : ""]secured the airlock assembly!") - anchored = !anchored - - else if(istype(W, /obj/item/stack/cable_coil) && state == 0 && anchored) - var/obj/item/stack/cable_coil/C = W - if (C.get_amount() < 1) - to_chat(user, "You need one length of coil to wire the airlock assembly.") - return - user.visible_message("[user] wires the airlock assembly.", "You start to wire the airlock assembly.") - if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE) && state == 0 && anchored) - if (C.use(1)) - src.state = 1 - to_chat(user, "You wire the airlock.") - - else if(W.has_tool_quality(TOOL_WIRECUTTER) && state == 1 ) - playsound(src, W.usesound, 100, 1) - user.visible_message("[user] cuts the wires from the airlock assembly.", "You start to cut the wires from airlock assembly.") - - if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src) return - to_chat(user, "You cut the airlock wires.!") - new/obj/item/stack/cable_coil(src.loc, 1) - src.state = 0 - - else if(istype(W, /obj/item/weapon/airlock_electronics) && state == 1) - playsound(src, W.usesound, 100, 1) - user.visible_message("[user] installs the electronics into the airlock assembly.", "You start to install electronics into the airlock assembly.") - - if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src) return - user.drop_item() - W.loc = src - to_chat(user, "You installed the airlock electronics!") - src.state = 2 - src.electronics = W - - else if(W.has_tool_quality(TOOL_CROWBAR) && state == 2 ) - //This should never happen, but just in case I guess - if (!electronics) - to_chat(user, "There was nothing to remove.") - src.state = 1 - return - - playsound(src, W.usesound, 100, 1) - user.visible_message("\The [user] starts removing the electronics from the airlock assembly.", "You start removing the electronics from the airlock assembly.") - - if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src) return - to_chat(user, "You removed the airlock electronics!") - src.state = 1 - electronics.loc = src.loc - electronics = null - - else if(istype(W, /obj/item/stack/material) && !glass) - var/obj/item/stack/S = W - var/material_name = S.get_material_name() - if (S) - if (S.get_amount() >= 1) - if(material_name == "rglass") - playsound(src, 'sound/items/Crowbar.ogg', 100, 1) - user.visible_message("[user] adds [S.name] to the airlock assembly.", "You start to install [S.name] into the airlock assembly.") - if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE) && !glass) - if (S.use(1)) - to_chat(user, "You installed reinforced glass windows into the airlock assembly.") - glass = 1 - else if(material_name) - // Ugly hack, will suffice for now. Need to fix it upstream as well, may rewrite mineral walls. ~Z - if(!(material_name in list("gold", "silver", "diamond", "uranium", "phoron", "sandstone"))) - to_chat(user, "You cannot make an airlock out of that material.") - return - if(S.get_amount() >= 2) - playsound(src, 'sound/items/Crowbar.ogg', 100, 1) - user.visible_message("[user] adds [S.name] to the airlock assembly.", "You start to install [S.name] into the airlock assembly.") - if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE) && !glass) - if (S.use(2)) - to_chat(user, "You installed [material_display_name(material_name)] plating into the airlock assembly.") - glass = material_name - - else if(W.has_tool_quality(TOOL_SCREWDRIVER) && state == 2 ) - playsound(src, W.usesound, 100, 1) - to_chat(user, "Now finishing the airlock.") - - if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src) return - to_chat(user, "You finish the airlock!") - var/path - if(istext(glass)) - path = text2path("/obj/machinery/door/airlock/[glass]") - else if (glass == 1) - path = text2path("/obj/machinery/door/airlock[glass_type]") - else - path = text2path("/obj/machinery/door/airlock[airlock_type]") - - new path(src.loc, src) - qdel(src) - else - ..() - update_state() - -/obj/structure/door_assembly/proc/update_state() - icon_state = "door_as_[glass == 1 ? "g" : ""][istext(glass) ? glass : base_icon_state][state]" - name = "" - switch (state) - if(0) - if (anchored) - name = "secured " - if(1) - name = "wired " - if(2) - name = "near finished " - name += "[glass == 1 ? "window " : ""][istext(glass) ? "[glass] airlock" : base_name] assembly ([created_name])" - -// Airlock frames are indestructable, so bullets hitting them would always be stopped. -// To fix this, airlock assemblies will sometimes let bullets pass through, since generally the sprite shows them partially open. -/obj/structure/door_assembly/bullet_act(var/obj/item/projectile/P) - if(prob(40)) // Chance for the frame to let the bullet keep going. - return PROJECTILE_CONTINUE - return ..() +/obj/structure/door_assembly + name = "airlock assembly" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_as_0" + anchored = FALSE + density = TRUE + w_class = ITEMSIZE_HUGE + var/state = 0 + var/base_icon_state = "" + var/base_name = "airlock" + var/obj/item/weapon/airlock_electronics/electronics = null + var/airlock_type = "" //the type path of the airlock once completed + var/glass_type = "/glass" + var/glass = 0 // 0 = glass can be installed. -1 = glass can't be installed. 1 = glass is already installed. Text = mineral plating is installed instead. + var/created_name = null + +/obj/structure/door_assembly/New() + update_state() + +/obj/structure/door_assembly/door_assembly_com + base_icon_state = "com" + base_name = "Command airlock" + glass_type = "/glass_command" + airlock_type = "/command" + +/obj/structure/door_assembly/door_assembly_sec + base_icon_state = "sec" + base_name = "Security airlock" + glass_type = "/glass_security" + airlock_type = "/security" + +/obj/structure/door_assembly/door_assembly_eng + base_icon_state = "eng" + base_name = "Engineering airlock" + glass_type = "/glass_engineering" + airlock_type = "/engineering" + +/obj/structure/door_assembly/door_assembly_eat + base_icon_state = "eat" + base_name = "Engineering atmos airlock" + glass_type = "/glass_engineeringatmos" + airlock_type = "/engineering" + +/obj/structure/door_assembly/door_assembly_min + base_icon_state = "min" + base_name = "Mining airlock" + glass_type = "/glass_mining" + airlock_type = "/mining" + +/obj/structure/door_assembly/door_assembly_atmo + base_icon_state = "atmo" + base_name = "Atmospherics airlock" + glass_type = "/glass_atmos" + airlock_type = "/atmos" + +/obj/structure/door_assembly/door_assembly_research + base_icon_state = "res" + base_name = "Research airlock" + glass_type = "/glass_research" + airlock_type = "/research" + +/obj/structure/door_assembly/door_assembly_science + base_icon_state = "sci" + base_name = "Science airlock" + glass_type = "/glass_science" + airlock_type = "/science" + +/obj/structure/door_assembly/door_assembly_med + base_icon_state = "med" + base_name = "Medical airlock" + glass_type = "/glass_medical" + airlock_type = "/medical" + +/obj/structure/door_assembly/door_assembly_ext + base_icon_state = "ext" + base_name = "External airlock" + glass_type = "/glass_external" + airlock_type = "/external" + +/obj/structure/door_assembly/door_assembly_mai + base_icon_state = "mai" + base_name = "Maintenance airlock" + airlock_type = "/maintenance" + glass = -1 + +/obj/structure/door_assembly/door_assembly_fre + base_icon_state = "fre" + base_name = "Freezer airlock" + airlock_type = "/freezer" + glass = -1 + +/obj/structure/door_assembly/door_assembly_hatch + base_icon_state = "hatch" + base_name = "airtight hatch" + airlock_type = "/hatch" + glass = -1 + +/obj/structure/door_assembly/door_assembly_mhatch + base_icon_state = "mhatch" + base_name = "maintenance hatch" + airlock_type = "/maintenance_hatch" + glass = -1 + +/obj/structure/door_assembly/door_assembly_highsecurity // Borrowing this until WJohnston makes sprites for the assembly + base_icon_state = "highsec" + base_name = "high security airlock" + airlock_type = "/highsecurity" + glass = -1 + +/obj/structure/door_assembly/door_assembly_voidcraft + base_icon_state = "voidcraft" + base_name = "voidcraft hatch" + airlock_type = "/voidcraft" + glass = -1 + +/obj/structure/door_assembly/door_assembly_voidcraft/vertical + base_icon_state = "voidcraft_vertical" + airlock_type = "/voidcraft/vertical" + +/obj/structure/door_assembly/door_assembly_alien + base_icon_state = "alien" + base_name = "alien airlock" + airlock_type = "/alien" + glass = -1 + +/obj/structure/door_assembly/multi_tile + icon = 'icons/obj/doors/door_assembly2x1.dmi' + dir = EAST + var/width = 1 + +/*Temporary until we get sprites. + glass_type = "/multi_tile/glass" + airlock_type = "/multi_tile/maint" + glass = 1*/ + base_icon_state = "g" //Remember to delete this line when reverting "glass" var to 1. + airlock_type = "/multi_tile/glass" + glass = -1 //To prevent bugs in deconstruction process. + +/obj/structure/door_assembly/multi_tile/New() + if(dir in list(EAST, WEST)) + bound_width = width * world.icon_size + bound_height = world.icon_size + else + bound_width = world.icon_size + bound_height = width * world.icon_size + update_state() + +/obj/structure/door_assembly/multi_tile/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(dir in list(EAST, WEST)) + bound_width = width * world.icon_size + bound_height = world.icon_size + else + bound_width = world.icon_size + bound_height = width * world.icon_size + +/obj/structure/door_assembly/proc/rename_door(mob/living/user) + var/t = sanitizeSafe(tgui_input_text(user, "Enter the name for the [base_name].", src.name, src.created_name, MAX_NAME_LEN), MAX_NAME_LEN) + if(!in_range(src, user) && src.loc != user) return + created_name = t + update_state() + +/obj/structure/door_assembly/attack_robot(mob/living/silicon/robot/user) + if(Adjacent(user) && (user.module && (istype(user.module,/obj/item/weapon/robot_module/robot/engineering)) \ + || istype(user.module,/obj/item/weapon/robot_module/drone))) //Only drone (and engiborg) needs this. + rename_door(user) + +/obj/structure/door_assembly/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/pen)) + rename_door(user) + return + + if(W.has_tool_quality(TOOL_WELDER) && ( (istext(glass)) || (glass == 1) || (!anchored) )) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if (WT.remove_fuel(0, user)) + playsound(src, WT.usesound, 50, 1) + if(istext(glass)) + user.visible_message("[user] welds the [glass] plating off the airlock assembly.", "You start to weld the [glass] plating off the airlock assembly.") + if(do_after(user, 4 SECONDS * WT.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src || !WT.isOn()) return + to_chat(user, "You welded the [glass] plating off!") + var/M = text2path("/obj/item/stack/material/[glass]") + new M(src.loc, 2) + glass = 0 + else if(glass == 1) + user.visible_message("[user] welds the glass panel out of the airlock assembly.", "You start to weld the glass panel out of the airlock assembly.") + if(do_after(user, 4 SECONDS * WT.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src || !WT.isOn()) return + to_chat(user, "You welded the glass panel out!") + new /obj/item/stack/material/glass/reinforced(src.loc) + glass = 0 + else if(!anchored) + user.visible_message("[user] dissassembles the airlock assembly.", "You start to dissassemble the airlock assembly.") + if(do_after(user, 4 SECONDS * WT.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src || !WT.isOn()) return + to_chat(user, "You dissasembled the airlock assembly!") + new /obj/item/stack/material/steel(src.loc, 4) + qdel (src) + else + to_chat(user, "You need more welding fuel.") + return + + else if(W.has_tool_quality(TOOL_WRENCH) && state == 0) + playsound(src, W.usesound, 100, 1) + if(anchored) + user.visible_message("[user] begins unsecuring the airlock assembly from the floor.", "You starts unsecuring the airlock assembly from the floor.") + else + user.visible_message("[user] begins securing the airlock assembly to the floor.", "You starts securing the airlock assembly to the floor.") + + if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src) return + to_chat(user, "You [anchored? "un" : ""]secured the airlock assembly!") + anchored = !anchored + + else if(istype(W, /obj/item/stack/cable_coil) && state == 0 && anchored) + var/obj/item/stack/cable_coil/C = W + if (C.get_amount() < 1) + to_chat(user, "You need one length of coil to wire the airlock assembly.") + return + user.visible_message("[user] wires the airlock assembly.", "You start to wire the airlock assembly.") + if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE) && state == 0 && anchored) + if (C.use(1)) + src.state = 1 + to_chat(user, "You wire the airlock.") + + else if(W.has_tool_quality(TOOL_WIRECUTTER) && state == 1 ) + playsound(src, W.usesound, 100, 1) + user.visible_message("[user] cuts the wires from the airlock assembly.", "You start to cut the wires from airlock assembly.") + + if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src) return + to_chat(user, "You cut the airlock wires.!") + new/obj/item/stack/cable_coil(src.loc, 1) + src.state = 0 + + else if(istype(W, /obj/item/weapon/airlock_electronics) && state == 1) + playsound(src, W.usesound, 100, 1) + user.visible_message("[user] installs the electronics into the airlock assembly.", "You start to install electronics into the airlock assembly.") + + if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src) return + user.drop_item() + W.loc = src + to_chat(user, "You installed the airlock electronics!") + src.state = 2 + src.electronics = W + + else if(W.has_tool_quality(TOOL_CROWBAR) && state == 2 ) + //This should never happen, but just in case I guess + if (!electronics) + to_chat(user, "There was nothing to remove.") + src.state = 1 + return + + playsound(src, W.usesound, 100, 1) + user.visible_message("\The [user] starts removing the electronics from the airlock assembly.", "You start removing the electronics from the airlock assembly.") + + if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src) return + to_chat(user, "You removed the airlock electronics!") + src.state = 1 + electronics.loc = src.loc + electronics = null + + else if(istype(W, /obj/item/stack/material) && !glass) + var/obj/item/stack/S = W + var/material_name = S.get_material_name() + if (S) + if (S.get_amount() >= 1) + if(material_name == "rglass") + playsound(src, 'sound/items/Crowbar.ogg', 100, 1) + user.visible_message("[user] adds [S.name] to the airlock assembly.", "You start to install [S.name] into the airlock assembly.") + if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE) && !glass) + if (S.use(1)) + to_chat(user, "You installed reinforced glass windows into the airlock assembly.") + glass = 1 + else if(material_name) + // Ugly hack, will suffice for now. Need to fix it upstream as well, may rewrite mineral walls. ~Z + if(!(material_name in list("gold", "silver", "diamond", "uranium", "phoron", "sandstone"))) + to_chat(user, "You cannot make an airlock out of that material.") + return + if(S.get_amount() >= 2) + playsound(src, 'sound/items/Crowbar.ogg', 100, 1) + user.visible_message("[user] adds [S.name] to the airlock assembly.", "You start to install [S.name] into the airlock assembly.") + if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE) && !glass) + if (S.use(2)) + to_chat(user, "You installed [material_display_name(material_name)] plating into the airlock assembly.") + glass = material_name + + else if(W.has_tool_quality(TOOL_SCREWDRIVER) && state == 2 ) + playsound(src, W.usesound, 100, 1) + to_chat(user, "Now finishing the airlock.") + + if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src) return + to_chat(user, "You finish the airlock!") + var/path + if(istext(glass)) + path = text2path("/obj/machinery/door/airlock/[glass]") + else if (glass == 1) + path = text2path("/obj/machinery/door/airlock[glass_type]") + else + path = text2path("/obj/machinery/door/airlock[airlock_type]") + + new path(src.loc, src) + qdel(src) + else + ..() + update_state() + +/obj/structure/door_assembly/proc/update_state() + icon_state = "door_as_[glass == 1 ? "g" : ""][istext(glass) ? glass : base_icon_state][state]" + name = "" + switch (state) + if(0) + if (anchored) + name = "secured " + if(1) + name = "wired " + if(2) + name = "near finished " + name += "[glass == 1 ? "window " : ""][istext(glass) ? "[glass] airlock" : base_name] assembly ([created_name])" + +// Airlock frames are indestructable, so bullets hitting them would always be stopped. +// To fix this, airlock assemblies will sometimes let bullets pass through, since generally the sprite shows them partially open. +/obj/structure/door_assembly/bullet_act(var/obj/item/projectile/P) + if(prob(40)) // Chance for the frame to let the bullet keep going. + return PROJECTILE_CONTINUE + return ..() diff --git a/code/game/objects/structures/fitness_vr.dm b/code/game/objects/structures/fitness_vr.dm index 0eeaf621965..38b96ec8e89 100644 --- a/code/game/objects/structures/fitness_vr.dm +++ b/code/game/objects/structures/fitness_vr.dm @@ -1,74 +1,74 @@ -/obj/structure/fitness/boxing_ropes - name = "ropes" - desc = "Firm yet springy, perhaps this could be useful!" - icon = 'icons/obj/fitness_vr.dmi' - icon_state = "ropes" - density = TRUE - throwpass = TRUE - climbable = TRUE - layer = WINDOW_LAYER - anchored = TRUE - flags = ON_BORDER - -/obj/structure/fitness/boxing_ropes/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSTABLE)) - return TRUE - if(get_dir(mover, target) == reverse_dir[dir]) // From elsewhere to here, can't move against our dir - return !density - return TRUE - -/obj/structure/fitness/boxing_ropes/Uncross(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSTABLE)) - return TRUE - if(get_dir(mover, target) == dir) // From here to elsewhere, can't move in our dir - return !density - return TRUE -/obj/structure/fitness/boxing_ropes/do_climb(var/mob/living/user) //Sets it so that players can climb *over* the turf and will enter the the turf **this** turf is facing. - if(!can_climb(user)) - return - - usr.visible_message("[user] starts climbing onto \the [src]!") - LAZYDISTINCTADD(climbers, user) - - if(!do_after(user,(issmall(user) ? 20 : 34))) - LAZYREMOVE(climbers, user) - return - - if(!can_climb(user, post_climb_check=1)) - LAZYREMOVE(climbers, user) - return - - if(get_turf(user) == get_turf(src)) - usr.forceMove(get_step(src, src.dir)) - else - usr.forceMove(get_turf(src)) - - usr.visible_message("[user] climbed over \the [src]!") - LAZYREMOVE(climbers, user) - -/obj/structure/fitness/boxing_ropes/can_climb(var/mob/living/user, post_climb_check=0) //Sets it to keep people from climbing over into the next turf if it is occupied. - if(!..()) - return 0 - - if(get_turf(user) == get_turf(src)) - var/obj/occupied = neighbor_turf_impassable() - if(occupied) - to_chat(user, "You can't climb there, there's \a [occupied] in the way.") - return 0 - return 1 - -/obj/structure/fitness/boxing_ropes/bottom - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - -/obj/structure/fitness/boxing_ropes/turnbuckle - name = "turnbuckle" - desc = "A sturdy post that looks like it could support even the most heaviest of heavy weights!" - icon = 'icons/obj/fitness_vr.dmi' - icon_state = "turnbuckle" - layer = WINDOW_LAYER - -/turf/simulated/fitness - name = "Mat" - icon = 'icons/turf/floors_vr.dmi' +/obj/structure/fitness/boxing_ropes + name = "ropes" + desc = "Firm yet springy, perhaps this could be useful!" + icon = 'icons/obj/fitness_vr.dmi' + icon_state = "ropes" + density = TRUE + throwpass = TRUE + climbable = TRUE + layer = WINDOW_LAYER + anchored = TRUE + flags = ON_BORDER + +/obj/structure/fitness/boxing_ropes/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSTABLE)) + return TRUE + if(get_dir(mover, target) == reverse_dir[dir]) // From elsewhere to here, can't move against our dir + return !density + return TRUE + +/obj/structure/fitness/boxing_ropes/Uncross(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSTABLE)) + return TRUE + if(get_dir(mover, target) == dir) // From here to elsewhere, can't move in our dir + return !density + return TRUE +/obj/structure/fitness/boxing_ropes/do_climb(var/mob/living/user) //Sets it so that players can climb *over* the turf and will enter the the turf **this** turf is facing. + if(!can_climb(user)) + return + + usr.visible_message("[user] starts climbing onto \the [src]!") + LAZYDISTINCTADD(climbers, user) + + if(!do_after(user,(issmall(user) ? 20 : 34))) + LAZYREMOVE(climbers, user) + return + + if(!can_climb(user, post_climb_check=1)) + LAZYREMOVE(climbers, user) + return + + if(get_turf(user) == get_turf(src)) + usr.forceMove(get_step(src, src.dir)) + else + usr.forceMove(get_turf(src)) + + usr.visible_message("[user] climbed over \the [src]!") + LAZYREMOVE(climbers, user) + +/obj/structure/fitness/boxing_ropes/can_climb(var/mob/living/user, post_climb_check=0) //Sets it to keep people from climbing over into the next turf if it is occupied. + if(!..()) + return 0 + + if(get_turf(user) == get_turf(src)) + var/obj/occupied = neighbor_turf_impassable() + if(occupied) + to_chat(user, "You can't climb there, there's \a [occupied] in the way.") + return 0 + return 1 + +/obj/structure/fitness/boxing_ropes/bottom + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + +/obj/structure/fitness/boxing_ropes/turnbuckle + name = "turnbuckle" + desc = "A sturdy post that looks like it could support even the most heaviest of heavy weights!" + icon = 'icons/obj/fitness_vr.dmi' + icon_state = "turnbuckle" + layer = WINDOW_LAYER + +/turf/simulated/fitness + name = "Mat" + icon = 'icons/turf/floors_vr.dmi' icon_state = "fit_mat" \ No newline at end of file diff --git a/code/game/objects/structures/flora/moretrees_vr.dm b/code/game/objects/structures/flora/moretrees_vr.dm index c14cd7522cf..6541fda8027 100644 --- a/code/game/objects/structures/flora/moretrees_vr.dm +++ b/code/game/objects/structures/flora/moretrees_vr.dm @@ -1,32 +1,32 @@ -/obj/structure/flora/tree/bigtree - icon = 'icons/obj/flora/moretrees_vr.dmi' - icon_state = "bigtree1" - base_state = "tree" - product = /obj/item/stack/material/log - product_amount = 20 - health = 400 - max_health = 400 - pixel_x = -65 - pixel_y = -8 - layer = MOB_LAYER - 1 - shake_animation_degrees = 2 - -/obj/structure/flora/tree/bigtree/choose_icon_state() - return "[base_state][rand(1, 4)]" - -/obj/structure/flora/tree/bigtree/Initialize() - . = ..() - - var/image/i = image('icons/obj/flora/moretrees_vr.dmi', "[icon_state]-b") - i.plane = ABOVE_MOB_PLANE - add_overlay(i) - -/obj/structure/flora/tree/bigtree/stump() - if(is_stump) - return - - is_stump = TRUE - density = FALSE - icon_state = "[icon_state]_stump" - cut_overlays() +/obj/structure/flora/tree/bigtree + icon = 'icons/obj/flora/moretrees_vr.dmi' + icon_state = "bigtree1" + base_state = "tree" + product = /obj/item/stack/material/log + product_amount = 20 + health = 400 + max_health = 400 + pixel_x = -65 + pixel_y = -8 + layer = MOB_LAYER - 1 + shake_animation_degrees = 2 + +/obj/structure/flora/tree/bigtree/choose_icon_state() + return "[base_state][rand(1, 4)]" + +/obj/structure/flora/tree/bigtree/Initialize() + . = ..() + + var/image/i = image('icons/obj/flora/moretrees_vr.dmi', "[icon_state]-b") + i.plane = ABOVE_MOB_PLANE + add_overlay(i) + +/obj/structure/flora/tree/bigtree/stump() + if(is_stump) + return + + is_stump = TRUE + density = FALSE + icon_state = "[icon_state]_stump" + cut_overlays() set_light(0) \ No newline at end of file diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index 6642d9f99fd..d1fd8eb64df 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -1,421 +1,421 @@ -/obj/structure/girder - name = "girder" - icon_state = "girder" - anchored = TRUE - density = TRUE - plane = PLATING_PLANE - w_class = ITEMSIZE_HUGE - var/state = 0 - var/health = 200 - var/max_health = 200 - var/displaced_health = 50 - var/current_damage = 0 - var/cover = 50 //how much cover the girder provides against projectiles. - var/default_material = MAT_STEEL - var/datum/material/girder_material - var/datum/material/reinf_material - var/reinforcing = 0 - var/applies_material_colour = 1 - var/wall_type = /turf/simulated/wall - -/obj/structure/girder/New(var/newloc, var/material_key) - ..(newloc) - if(!material_key) - material_key = default_material - set_material(material_key) - update_icon() - -/obj/structure/girder/Destroy() - if(girder_material.products_need_process()) - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/structure/girder/process() - if(!radiate()) - STOP_PROCESSING(SSobj, src) - return - -/obj/structure/girder/proc/radiate() - var/total_radiation = girder_material.radioactivity + (reinf_material ? reinf_material.radioactivity / 2 : 0) - if(!total_radiation) - return - - SSradiation.radiate(src, total_radiation) - return total_radiation - - -/obj/structure/girder/proc/set_material(var/new_material) - girder_material = get_material_by_name(new_material) - if(!girder_material) - qdel(src) - name = "[girder_material.display_name] [initial(name)]" - max_health = round(girder_material.integrity) //Should be 150 with default integrity (steel). Weaker than ye-olden Girders now. - health = max_health - displaced_health = round(max_health/4) - if(applies_material_colour) - color = girder_material.icon_colour - if(girder_material.products_need_process()) //Am I radioactive or some other? Process me! - START_PROCESSING(SSobj, src) - else if(datum_flags & DF_ISPROCESSING) //If I happened to be radioactive or s.o. previously, and am not now, stop processing. - STOP_PROCESSING(SSobj, src) - -/obj/structure/girder/get_material() - return girder_material - -/obj/structure/girder/update_icon() - if(anchored) - icon_state = initial(icon_state) - else - icon_state = "displaced" - -/obj/structure/girder/displaced - icon_state = "displaced" - anchored = FALSE - health = 50 - cover = 25 - -/obj/structure/girder/displaced/New(var/newloc, var/material_key) - ..(newloc, material_key) - displace() - -/obj/structure/girder/proc/displace() - name = "displaced [girder_material.display_name] [initial(name)]" - icon_state = "displaced" - anchored = FALSE - health = (displaced_health - round(current_damage / 4)) - cover = 25 - -/obj/structure/girder/attack_generic(var/mob/user, var/damage, var/attack_message = "smashes apart") - if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) - return 0 - user.do_attack_animation(src) - visible_message("[user] [attack_message] the [src]!") - spawn(1) dismantle() - return 1 - -/obj/structure/girder/bullet_act(var/obj/item/projectile/Proj) - //Girders only provide partial cover. There's a chance that the projectiles will just pass through. (unless you are trying to shoot the girder) - if(Proj.original != src && !prob(cover)) - return PROJECTILE_CONTINUE //pass through - - var/damage = Proj.get_structure_damage() - if(!damage) - return - - if(!istype(Proj, /obj/item/projectile/beam)) - damage *= 0.4 //non beams do reduced damage - - else if(girder_material && girder_material.reflectivity >= 0.5) // Reflect lasers. - var/new_damage = damage * girder_material.reflectivity - var/outgoing_damage = damage - new_damage - damage = round(new_damage) - Proj.damage = outgoing_damage - - visible_message("\The [src] reflects \the [Proj]!") - - // Find a turf near or on the original location to bounce to - var/new_x = Proj.starting.x + pick(0, 0, 0, -1, 1, -2, 2) - var/new_y = Proj.starting.y + pick(0, 0, 0, -1, 1, -2, 2) - //var/turf/curloc = get_turf(src) - var/turf/curloc = get_step(src, get_dir(src, Proj.starting)) - - Proj.penetrating += 1 // Needed for the beam to get out of the girder. - - // redirect the projectile - Proj.redirect(new_x, new_y, curloc, null) - - health -= damage - ..() - if(health <= 0) - dismantle() - - return - -/obj/structure/girder/blob_act() - dismantle() - -/obj/structure/girder/proc/reset_girder() - name = "[girder_material.display_name] [initial(name)]" - anchored = TRUE - cover = initial(cover) - health = min(max_health - current_damage,max_health) - state = 0 - icon_state = initial(icon_state) - reinforcing = 0 - if(reinf_material) - reinforce_girder() - -/obj/structure/girder/attackby(obj/item/W as obj, mob/user as mob) - if(W.has_tool_quality(TOOL_WRENCH) && state == 0) - if(anchored && !reinf_material) - playsound(src, W.usesound, 100, 1) - to_chat(user, "Now disassembling the girder...") - if(do_after(user,(35 + round(max_health/50)) * W.toolspeed)) - if(!src) return - to_chat(user, "You dissasembled the girder!") - dismantle() - else if(!anchored) - playsound(src, W.usesound, 100, 1) - to_chat(user, "Now securing the girder...") - if(do_after(user, 40 * W.toolspeed, src)) - to_chat(user, "You secured the girder!") - reset_girder() - - else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) - to_chat(user, "Now slicing apart the girder...") - if(do_after(user,30 * W.toolspeed)) - if(!src) return - to_chat(user, "You slice apart the girder!") - dismantle() - - else if(istype(W, /obj/item/weapon/pickaxe/diamonddrill)) - to_chat(user, "You drill through the girder!") - dismantle() - - else if(W.has_tool_quality(TOOL_SCREWDRIVER)) - if(state == 2) - playsound(src, W.usesound, 100, 1) - to_chat(user, "Now unsecuring support struts...") - if(do_after(user,40 * W.toolspeed)) - if(!src) return - to_chat(user, "You unsecured the support struts!") - state = 1 - else if(anchored && !reinf_material) - playsound(src, W.usesound, 100, 1) - reinforcing = !reinforcing - to_chat(user, "\The [src] can now be [reinforcing? "reinforced" : "constructed"]!") - - else if(W.has_tool_quality(TOOL_WIRECUTTER) && state == 1) - playsound(src, W.usesound, 100, 1) - to_chat(user, "Now removing support struts...") - if(do_after(user,40 * W.toolspeed)) - if(!src) return - to_chat(user, "You removed the support struts!") - reinf_material.place_dismantled_product(get_turf(src)) - reinf_material = null - reset_girder() - - else if(W.has_tool_quality(TOOL_CROWBAR) && state == 0 && anchored) - playsound(src, W.usesound, 100, 1) - to_chat(user, "Now dislodging the girder...") - if(do_after(user, 40 * W.toolspeed)) - if(!src) return - to_chat(user, "You dislodged the girder!") - displace() - - else if(istype(W, /obj/item/stack/material)) - if(reinforcing && !reinf_material) - if(!reinforce_with_material(W, user)) - return ..() - else - if(!construct_wall(W, user)) - return ..() - - else - return ..() - -/obj/structure/girder/take_damage(var/damage) - health -= damage - if(health <= 0) - dismantle() - else - current_damage = current_damage + damage //Rather than calculate this every time we need to use it, just calculate it here and save it. - - -/obj/structure/girder/proc/construct_wall(obj/item/stack/material/S, mob/user) - var/amount_to_use = reinf_material ? 1 : 2 - if(S.get_amount() < amount_to_use) - to_chat(user, "There isn't enough material here to construct a wall.") - return 0 - - var/datum/material/M = name_to_material[S.default_type] - if(!istype(M)) - return 0 - - var/wall_fake - add_hiddenprint(usr) - - if(M.integrity < 50) - to_chat(user, "This material is too soft for use in wall construction.") - return 0 - - to_chat(user, "You begin adding the plating...") - - if(!do_after(user,40) || !S.use(amount_to_use)) - return 1 //once we've gotten this far don't call parent attackby() - - if(anchored) - to_chat(user, "You added the plating!") - else - to_chat(user, "You create a false wall! Push on it to open or close the passage.") - wall_fake = 1 - - var/turf/Tsrc = get_turf(src) - Tsrc.ChangeTurf(wall_type) - var/turf/simulated/wall/T = get_turf(src) - T.set_material(M, reinf_material, girder_material) - if(wall_fake) - T.can_open = 1 - T.add_hiddenprint(usr) - qdel(src) - return 1 - -/obj/structure/girder/proc/reinforce_with_material(obj/item/stack/material/S, mob/user) //if the verb is removed this can be renamed. - if(reinf_material) - to_chat(user, "\The [src] is already reinforced.") - return 0 - - if(S.get_amount() < 1) - to_chat(user, "There isn't enough material here to reinforce the girder.") - return 0 - - var/datum/material/M = name_to_material[S.default_type] - if(!istype(M) || M.integrity < 50) - to_chat(user, "You cannot reinforce \the [src] with that; it is too soft.") - return 0 - - to_chat(user, "Now reinforcing...") - if (!do_after(user,40) || !S.use(1)) - return 1 //don't call parent attackby() past this point - to_chat(user, "You added reinforcement!") - - reinf_material = M - reinforce_girder() - return 1 - -/obj/structure/girder/proc/reinforce_girder() - cover = reinf_material.hardness - health = health + round(reinf_material.integrity/2) - state = 2 - icon_state = "reinforced" - reinforcing = 0 - -/obj/structure/girder/proc/dismantle() - girder_material.place_dismantled_product(get_turf(src)) - qdel(src) - -/obj/structure/girder/attack_hand(mob/user as mob) - if (HULK in user.mutations) - visible_message("[user] smashes [src] apart!") - dismantle() - return - return ..() - - -/obj/structure/girder/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - return - if(2.0) - if (prob(30)) - dismantle() - return - if(3.0) - if (prob(5)) - dismantle() - return - else - return - -/obj/structure/girder/cult - name = "column" - icon= 'icons/obj/cult.dmi' - icon_state= "cultgirder" - max_health = 250 - health = 250 - cover = 70 - girder_material = "cult" - applies_material_colour = 0 - -/obj/structure/girder/cult/update_icon() - if(anchored) - icon_state = "cultgirder" - else - icon_state = "displaced" - -/obj/structure/girder/cult/dismantle() - new /obj/effect/decal/remains/human(get_turf(src)) - qdel(src) - -/obj/structure/girder/cult/attackby(obj/item/W as obj, mob/user as mob) - if(W.has_tool_quality(TOOL_WRENCH)) - playsound(src, W.usesound, 100, 1) - to_chat(user, "Now disassembling the girder...") - if(do_after(user,40 * W.toolspeed)) - to_chat(user, "You dissasembled the girder!") - dismantle() - - else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) - to_chat(user, "Now slicing apart the girder...") - if(do_after(user,30 * W.toolspeed)) - to_chat(user, "You slice apart the girder!") - dismantle() - - else if(istype(W, /obj/item/weapon/pickaxe/diamonddrill)) - to_chat(user, "You drill through the girder!") - new /obj/effect/decal/remains/human(get_turf(src)) - dismantle() - -/obj/structure/girder/resin - name = "soft girder" - icon_state = "girder_resin" - max_health = 225 - health = 225 - cover = 60 - girder_material = "resin" - -/obj/structure/girder/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - var/turf/simulated/T = get_turf(src) - if(!istype(T) || T.density) - return FALSE - - switch(passed_mode) - if(RCD_FLOORWALL) - // Finishing a wall costs two sheets. - var/cost = RCD_SHEETS_PER_MATTER_UNIT * 2 - // Rwalls cost three to finish. - if(the_rcd.make_rwalls) - cost += RCD_SHEETS_PER_MATTER_UNIT * 1 - return list( - RCD_VALUE_MODE = RCD_FLOORWALL, - RCD_VALUE_DELAY = 2 SECONDS, - RCD_VALUE_COST = cost - ) - if(RCD_DECONSTRUCT) - return list( - RCD_VALUE_MODE = RCD_DECONSTRUCT, - RCD_VALUE_DELAY = 2 SECONDS, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 - ) - return FALSE - -/obj/structure/girder/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - var/turf/simulated/T = get_turf(src) - if(!istype(T) || T.density) // Should stop future bugs of people bringing girders to centcom and RCDing them, or somehow putting a girder on a durasteel wall and deconning it. - return FALSE - - switch(passed_mode) - if(RCD_FLOORWALL) - to_chat(user, span("notice", "You finish a wall.")) - // This is mostly the same as using on a floor. The girder's material is preserved, however. - T.ChangeTurf(wall_type) - var/turf/simulated/wall/new_T = get_turf(src) // Ref to the wall we just built. - // Apparently set_material(...) for walls requires refs to the material singletons and not strings. - // This is different from how other material objects with their own set_material(...) do it, but whatever. - var/datum/material/M = name_to_material[the_rcd.material_to_use] - new_T.set_material(M, the_rcd.make_rwalls ? M : null, girder_material) - new_T.add_hiddenprint(user) - qdel(src) - return TRUE - - if(RCD_DECONSTRUCT) - to_chat(user, span("notice", "You deconstruct \the [src].")) - qdel(src) - return TRUE - -/obj/structure/girder/bay - wall_type = /turf/simulated/wall/bay - -/obj/structure/girder/eris - wall_type = /turf/simulated/wall/eris +/obj/structure/girder + name = "girder" + icon_state = "girder" + anchored = TRUE + density = TRUE + plane = PLATING_PLANE + w_class = ITEMSIZE_HUGE + var/state = 0 + var/health = 200 + var/max_health = 200 + var/displaced_health = 50 + var/current_damage = 0 + var/cover = 50 //how much cover the girder provides against projectiles. + var/default_material = MAT_STEEL + var/datum/material/girder_material + var/datum/material/reinf_material + var/reinforcing = 0 + var/applies_material_colour = 1 + var/wall_type = /turf/simulated/wall + +/obj/structure/girder/New(var/newloc, var/material_key) + ..(newloc) + if(!material_key) + material_key = default_material + set_material(material_key) + update_icon() + +/obj/structure/girder/Destroy() + if(girder_material.products_need_process()) + STOP_PROCESSING(SSobj, src) + . = ..() + +/obj/structure/girder/process() + if(!radiate()) + STOP_PROCESSING(SSobj, src) + return + +/obj/structure/girder/proc/radiate() + var/total_radiation = girder_material.radioactivity + (reinf_material ? reinf_material.radioactivity / 2 : 0) + if(!total_radiation) + return + + SSradiation.radiate(src, total_radiation) + return total_radiation + + +/obj/structure/girder/proc/set_material(var/new_material) + girder_material = get_material_by_name(new_material) + if(!girder_material) + qdel(src) + name = "[girder_material.display_name] [initial(name)]" + max_health = round(girder_material.integrity) //Should be 150 with default integrity (steel). Weaker than ye-olden Girders now. + health = max_health + displaced_health = round(max_health/4) + if(applies_material_colour) + color = girder_material.icon_colour + if(girder_material.products_need_process()) //Am I radioactive or some other? Process me! + START_PROCESSING(SSobj, src) + else if(datum_flags & DF_ISPROCESSING) //If I happened to be radioactive or s.o. previously, and am not now, stop processing. + STOP_PROCESSING(SSobj, src) + +/obj/structure/girder/get_material() + return girder_material + +/obj/structure/girder/update_icon() + if(anchored) + icon_state = initial(icon_state) + else + icon_state = "displaced" + +/obj/structure/girder/displaced + icon_state = "displaced" + anchored = FALSE + health = 50 + cover = 25 + +/obj/structure/girder/displaced/New(var/newloc, var/material_key) + ..(newloc, material_key) + displace() + +/obj/structure/girder/proc/displace() + name = "displaced [girder_material.display_name] [initial(name)]" + icon_state = "displaced" + anchored = FALSE + health = (displaced_health - round(current_damage / 4)) + cover = 25 + +/obj/structure/girder/attack_generic(var/mob/user, var/damage, var/attack_message = "smashes apart") + if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) + return 0 + user.do_attack_animation(src) + visible_message("[user] [attack_message] the [src]!") + spawn(1) dismantle() + return 1 + +/obj/structure/girder/bullet_act(var/obj/item/projectile/Proj) + //Girders only provide partial cover. There's a chance that the projectiles will just pass through. (unless you are trying to shoot the girder) + if(Proj.original != src && !prob(cover)) + return PROJECTILE_CONTINUE //pass through + + var/damage = Proj.get_structure_damage() + if(!damage) + return + + if(!istype(Proj, /obj/item/projectile/beam)) + damage *= 0.4 //non beams do reduced damage + + else if(girder_material && girder_material.reflectivity >= 0.5) // Reflect lasers. + var/new_damage = damage * girder_material.reflectivity + var/outgoing_damage = damage - new_damage + damage = round(new_damage) + Proj.damage = outgoing_damage + + visible_message("\The [src] reflects \the [Proj]!") + + // Find a turf near or on the original location to bounce to + var/new_x = Proj.starting.x + pick(0, 0, 0, -1, 1, -2, 2) + var/new_y = Proj.starting.y + pick(0, 0, 0, -1, 1, -2, 2) + //var/turf/curloc = get_turf(src) + var/turf/curloc = get_step(src, get_dir(src, Proj.starting)) + + Proj.penetrating += 1 // Needed for the beam to get out of the girder. + + // redirect the projectile + Proj.redirect(new_x, new_y, curloc, null) + + health -= damage + ..() + if(health <= 0) + dismantle() + + return + +/obj/structure/girder/blob_act() + dismantle() + +/obj/structure/girder/proc/reset_girder() + name = "[girder_material.display_name] [initial(name)]" + anchored = TRUE + cover = initial(cover) + health = min(max_health - current_damage,max_health) + state = 0 + icon_state = initial(icon_state) + reinforcing = 0 + if(reinf_material) + reinforce_girder() + +/obj/structure/girder/attackby(obj/item/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH) && state == 0) + if(anchored && !reinf_material) + playsound(src, W.usesound, 100, 1) + to_chat(user, "Now disassembling the girder...") + if(do_after(user,(35 + round(max_health/50)) * W.toolspeed)) + if(!src) return + to_chat(user, "You dissasembled the girder!") + dismantle() + else if(!anchored) + playsound(src, W.usesound, 100, 1) + to_chat(user, "Now securing the girder...") + if(do_after(user, 40 * W.toolspeed, src)) + to_chat(user, "You secured the girder!") + reset_girder() + + else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) + to_chat(user, "Now slicing apart the girder...") + if(do_after(user,30 * W.toolspeed)) + if(!src) return + to_chat(user, "You slice apart the girder!") + dismantle() + + else if(istype(W, /obj/item/weapon/pickaxe/diamonddrill)) + to_chat(user, "You drill through the girder!") + dismantle() + + else if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(state == 2) + playsound(src, W.usesound, 100, 1) + to_chat(user, "Now unsecuring support struts...") + if(do_after(user,40 * W.toolspeed)) + if(!src) return + to_chat(user, "You unsecured the support struts!") + state = 1 + else if(anchored && !reinf_material) + playsound(src, W.usesound, 100, 1) + reinforcing = !reinforcing + to_chat(user, "\The [src] can now be [reinforcing? "reinforced" : "constructed"]!") + + else if(W.has_tool_quality(TOOL_WIRECUTTER) && state == 1) + playsound(src, W.usesound, 100, 1) + to_chat(user, "Now removing support struts...") + if(do_after(user,40 * W.toolspeed)) + if(!src) return + to_chat(user, "You removed the support struts!") + reinf_material.place_dismantled_product(get_turf(src)) + reinf_material = null + reset_girder() + + else if(W.has_tool_quality(TOOL_CROWBAR) && state == 0 && anchored) + playsound(src, W.usesound, 100, 1) + to_chat(user, "Now dislodging the girder...") + if(do_after(user, 40 * W.toolspeed)) + if(!src) return + to_chat(user, "You dislodged the girder!") + displace() + + else if(istype(W, /obj/item/stack/material)) + if(reinforcing && !reinf_material) + if(!reinforce_with_material(W, user)) + return ..() + else + if(!construct_wall(W, user)) + return ..() + + else + return ..() + +/obj/structure/girder/take_damage(var/damage) + health -= damage + if(health <= 0) + dismantle() + else + current_damage = current_damage + damage //Rather than calculate this every time we need to use it, just calculate it here and save it. + + +/obj/structure/girder/proc/construct_wall(obj/item/stack/material/S, mob/user) + var/amount_to_use = reinf_material ? 1 : 2 + if(S.get_amount() < amount_to_use) + to_chat(user, "There isn't enough material here to construct a wall.") + return 0 + + var/datum/material/M = name_to_material[S.default_type] + if(!istype(M)) + return 0 + + var/wall_fake + add_hiddenprint(usr) + + if(M.integrity < 50) + to_chat(user, "This material is too soft for use in wall construction.") + return 0 + + to_chat(user, "You begin adding the plating...") + + if(!do_after(user,40) || !S.use(amount_to_use)) + return 1 //once we've gotten this far don't call parent attackby() + + if(anchored) + to_chat(user, "You added the plating!") + else + to_chat(user, "You create a false wall! Push on it to open or close the passage.") + wall_fake = 1 + + var/turf/Tsrc = get_turf(src) + Tsrc.ChangeTurf(wall_type) + var/turf/simulated/wall/T = get_turf(src) + T.set_material(M, reinf_material, girder_material) + if(wall_fake) + T.can_open = 1 + T.add_hiddenprint(usr) + qdel(src) + return 1 + +/obj/structure/girder/proc/reinforce_with_material(obj/item/stack/material/S, mob/user) //if the verb is removed this can be renamed. + if(reinf_material) + to_chat(user, "\The [src] is already reinforced.") + return 0 + + if(S.get_amount() < 1) + to_chat(user, "There isn't enough material here to reinforce the girder.") + return 0 + + var/datum/material/M = name_to_material[S.default_type] + if(!istype(M) || M.integrity < 50) + to_chat(user, "You cannot reinforce \the [src] with that; it is too soft.") + return 0 + + to_chat(user, "Now reinforcing...") + if (!do_after(user,40) || !S.use(1)) + return 1 //don't call parent attackby() past this point + to_chat(user, "You added reinforcement!") + + reinf_material = M + reinforce_girder() + return 1 + +/obj/structure/girder/proc/reinforce_girder() + cover = reinf_material.hardness + health = health + round(reinf_material.integrity/2) + state = 2 + icon_state = "reinforced" + reinforcing = 0 + +/obj/structure/girder/proc/dismantle() + girder_material.place_dismantled_product(get_turf(src)) + qdel(src) + +/obj/structure/girder/attack_hand(mob/user as mob) + if (HULK in user.mutations) + visible_message("[user] smashes [src] apart!") + dismantle() + return + return ..() + + +/obj/structure/girder/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + return + if(2.0) + if (prob(30)) + dismantle() + return + if(3.0) + if (prob(5)) + dismantle() + return + else + return + +/obj/structure/girder/cult + name = "column" + icon= 'icons/obj/cult.dmi' + icon_state= "cultgirder" + max_health = 250 + health = 250 + cover = 70 + girder_material = "cult" + applies_material_colour = 0 + +/obj/structure/girder/cult/update_icon() + if(anchored) + icon_state = "cultgirder" + else + icon_state = "displaced" + +/obj/structure/girder/cult/dismantle() + new /obj/effect/decal/remains/human(get_turf(src)) + qdel(src) + +/obj/structure/girder/cult/attackby(obj/item/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + playsound(src, W.usesound, 100, 1) + to_chat(user, "Now disassembling the girder...") + if(do_after(user,40 * W.toolspeed)) + to_chat(user, "You dissasembled the girder!") + dismantle() + + else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) + to_chat(user, "Now slicing apart the girder...") + if(do_after(user,30 * W.toolspeed)) + to_chat(user, "You slice apart the girder!") + dismantle() + + else if(istype(W, /obj/item/weapon/pickaxe/diamonddrill)) + to_chat(user, "You drill through the girder!") + new /obj/effect/decal/remains/human(get_turf(src)) + dismantle() + +/obj/structure/girder/resin + name = "soft girder" + icon_state = "girder_resin" + max_health = 225 + health = 225 + cover = 60 + girder_material = "resin" + +/obj/structure/girder/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + var/turf/simulated/T = get_turf(src) + if(!istype(T) || T.density) + return FALSE + + switch(passed_mode) + if(RCD_FLOORWALL) + // Finishing a wall costs two sheets. + var/cost = RCD_SHEETS_PER_MATTER_UNIT * 2 + // Rwalls cost three to finish. + if(the_rcd.make_rwalls) + cost += RCD_SHEETS_PER_MATTER_UNIT * 1 + return list( + RCD_VALUE_MODE = RCD_FLOORWALL, + RCD_VALUE_DELAY = 2 SECONDS, + RCD_VALUE_COST = cost + ) + if(RCD_DECONSTRUCT) + return list( + RCD_VALUE_MODE = RCD_DECONSTRUCT, + RCD_VALUE_DELAY = 2 SECONDS, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 + ) + return FALSE + +/obj/structure/girder/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + var/turf/simulated/T = get_turf(src) + if(!istype(T) || T.density) // Should stop future bugs of people bringing girders to centcom and RCDing them, or somehow putting a girder on a durasteel wall and deconning it. + return FALSE + + switch(passed_mode) + if(RCD_FLOORWALL) + to_chat(user, span("notice", "You finish a wall.")) + // This is mostly the same as using on a floor. The girder's material is preserved, however. + T.ChangeTurf(wall_type) + var/turf/simulated/wall/new_T = get_turf(src) // Ref to the wall we just built. + // Apparently set_material(...) for walls requires refs to the material singletons and not strings. + // This is different from how other material objects with their own set_material(...) do it, but whatever. + var/datum/material/M = name_to_material[the_rcd.material_to_use] + new_T.set_material(M, the_rcd.make_rwalls ? M : null, girder_material) + new_T.add_hiddenprint(user) + qdel(src) + return TRUE + + if(RCD_DECONSTRUCT) + to_chat(user, span("notice", "You deconstruct \the [src].")) + qdel(src) + return TRUE + +/obj/structure/girder/bay + wall_type = /turf/simulated/wall/bay + +/obj/structure/girder/eris + wall_type = /turf/simulated/wall/eris diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index 13b5412e568..d3226bb702e 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -1,289 +1,289 @@ -/obj/structure/grille - name = "grille" - desc = "A flimsy lattice of metal rods, with screws to secure it to the floor." - icon = 'icons/obj/structures_vr.dmi' // VOREStation Edit - New icons - icon_state = "grille" - density = TRUE - anchored = TRUE - pressure_resistance = 5*ONE_ATMOSPHERE - layer = TABLE_LAYER - explosion_resistance = 1 - var/health = 10 - var/destroyed = 0 - - -/obj/structure/grille/ex_act(severity) - qdel(src) - -/obj/structure/grille/update_icon() - if(destroyed) - icon_state = "[initial(icon_state)]-b" - else - icon_state = initial(icon_state) - -/obj/structure/grille/Bumped(atom/user) - if(ismob(user)) shock(user, 70) - -/obj/structure/grille/attack_hand(mob/user as mob) - - user.setClickCooldown(user.get_attack_speed()) - playsound(src, 'sound/effects/grillehit.ogg', 80, 1) - user.do_attack_animation(src) - - var/damage_dealt = 1 - var/attack_message = "kicks" - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - if(H.species.can_shred(H)) - attack_message = "mangles" - damage_dealt = 5 - - if(shock(user, 70)) - return - - if(HULK in user.mutations) - damage_dealt += 5 - else - damage_dealt += 1 - - attack_generic(user,damage_dealt,attack_message) - -/obj/structure/grille/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGRILLE)) - return TRUE - if(istype(mover, /obj/item/projectile)) - return prob(30) - return !density - -/obj/structure/grille/bullet_act(var/obj/item/projectile/Proj) - if(!Proj) return - - //Flimsy grilles aren't so great at stopping projectiles. However they can absorb some of the impact - var/damage = Proj.get_structure_damage() - var/passthrough = 0 - - if(!damage) return - - //20% chance that the grille provides a bit more cover than usual. Support structure for example might take up 20% of the grille's area. - //If they click on the grille itself then we assume they are aiming at the grille itself and the extra cover behaviour is always used. - switch(Proj.damage_type) - if(BRUTE) - //bullets - if(Proj.original == src || prob(20)) - Proj.damage *= between(0, Proj.damage/60, 0.5) - if(prob(max((damage-10)/25, 0))*100) - passthrough = 1 - else - Proj.damage *= between(0, Proj.damage/60, 1) - passthrough = 1 - if(BURN) - //beams and other projectiles are either blocked completely by grilles or stop half the damage. - if(!(Proj.original == src || prob(20))) - Proj.damage *= 0.5 - passthrough = 1 - - if(passthrough) - . = PROJECTILE_CONTINUE - damage = between(0, (damage - Proj.damage)*(Proj.damage_type == BRUTE? 0.4 : 1), 10) //if the bullet passes through then the grille avoids most of the damage - - src.health -= damage*0.2 - spawn(0) healthcheck() //spawn to make sure we return properly if the grille is deleted - -/obj/structure/grille/attackby(obj/item/W as obj, mob/user as mob) - if(!istype(W)) - return - if(istype(W, /obj/item/weapon/rcd)) // To stop us from hitting the grille when building windows, because grilles don't let parent handle it properly. - return FALSE - else if(W.has_tool_quality(TOOL_WIRECUTTER)) - if(!shock(user, 100)) - playsound(src, W.usesound, 100, 1) - new /obj/item/stack/rods(get_turf(src), destroyed ? 1 : 2) - qdel(src) - else if((W.has_tool_quality(TOOL_SCREWDRIVER)) && (istype(loc, /turf/simulated) || anchored)) - if(!shock(user, 90)) - playsound(src, W.usesound, 100, 1) - anchored = !anchored - user.visible_message("[user] [anchored ? "fastens" : "unfastens"] the grille.", \ - "You have [anchored ? "fastened the grille to" : "unfastened the grille from"] the floor.") - return - - //window placing begin //TODO CONVERT PROPERLY TO MATERIAL DATUM - else if(istype(W,/obj/item/stack/material)) - var/obj/item/stack/material/ST = W - if(!ST.material.created_window) - return 0 - - var/dir_to_set = 1 - if(loc == user.loc) - dir_to_set = user.dir - else - if( ( x == user.x ) || (y == user.y) ) //Only supposed to work for cardinal directions. - if( x == user.x ) - if( y > user.y ) - dir_to_set = 2 - else - dir_to_set = 1 - else if( y == user.y ) - if( x > user.x ) - dir_to_set = 8 - else - dir_to_set = 4 - else - to_chat(user, "You can't reach.") - return //Only works for cardinal direcitons, diagonals aren't supposed to work like this. - for(var/obj/structure/window/WINDOW in loc) - if(WINDOW.dir == dir_to_set) - to_chat(user, "There is already a window facing this way there.") - return - to_chat(user, "You start placing the window.") - if(do_after(user,20)) - for(var/obj/structure/window/WINDOW in loc) - if(WINDOW.dir == dir_to_set)//checking this for a 2nd time to check if a window was made while we were waiting. - to_chat(user, "There is already a window facing this way there.") - return - - var/wtype = ST.material.created_window - if (ST.use(1)) - var/obj/structure/window/WD = new wtype(loc, dir_to_set, 1) - to_chat(user, "You place the [WD] on [src].") - WD.update_icon() - return -//window placing end - - else if((W.flags & NOCONDUCT) || !shock(user, 70)) - user.setClickCooldown(user.get_attack_speed(W)) - user.do_attack_animation(src) - playsound(src, 'sound/effects/grillehit.ogg', 80, 1) - switch(W.damtype) - if("fire") - health -= W.force - if("brute") - health -= W.force * 0.1 - healthcheck() - ..() - return - - -/obj/structure/grille/proc/healthcheck() - if(health <= 0) - if(!destroyed) - density = FALSE - destroyed = 1 - update_icon() - new /obj/item/stack/rods(get_turf(src)) - - else - if(health <= -6) - new /obj/item/stack/rods(get_turf(src)) - qdel(src) - return - return - -// shock user with probability prb (if all connections & power are working) -// returns 1 if shocked, 0 otherwise - -/obj/structure/grille/proc/shock(mob/user as mob, prb) - - if(!anchored || destroyed) // anchored/destroyed grilles are never connected - return 0 - if(!prob(prb)) - return 0 - if(!in_range(src, user))//To prevent TK and mech users from getting shocked - return 0 - var/turf/T = get_turf(src) - var/obj/structure/cable/C = T.get_cable_node() - if(C) - if(electrocute_mob(user, C, src)) - if(C.powernet) - C.powernet.trigger_warning() - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(3, 1, src) - s.start() - if(user.stunned) - return 1 - else - return 0 - return 0 - -/obj/structure/grille/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(!destroyed) - if(exposed_temperature > T0C + 1500) - health -= 1 - healthcheck() - ..() - -/obj/structure/grille/attack_generic(var/mob/user, var/damage, var/attack_verb) - visible_message("[user] [attack_verb] the [src]!") - user.do_attack_animation(src) - health -= damage - spawn(1) healthcheck() - return 1 - -// Used in mapping to avoid -/obj/structure/grille/broken - destroyed = 1 - icon_state = "grille-b" - density = FALSE - -/obj/structure/grille/broken/New() - ..() - health = rand(-5, -1) //In the destroyed but not utterly threshold. - healthcheck() //Send this to healthcheck just in case we want to do something else with it. - -/obj/structure/grille/cult - name = "cult grille" - desc = "A matrice built out of an unknown material, with some sort of force field blocking air around it." - icon_state = "grillecult" - health = 40 // Make it strong enough to avoid people breaking in too easily. - can_atmos_pass = ATMOS_PASS_NO // Make sure air doesn't drain. - -/obj/structure/grille/broken/cult - icon_state = "grillecult-b" - -/obj/structure/grille/rustic - name = "rustic grille" - desc = "A lattice of metal, arranged in an old, rustic fashion." - icon_state = "grillerustic" - -/obj/structure/grille/broken/rustic - icon_state = "grillerustic-b" - - -/obj/structure/grille/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_WINDOWGRILLE) - // A full tile window costs 4 glass sheets. - return list( - RCD_VALUE_MODE = RCD_WINDOWGRILLE, - RCD_VALUE_DELAY = 2 SECONDS, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 4 - ) - - if(RCD_DECONSTRUCT) - return list( - RCD_VALUE_MODE = RCD_DECONSTRUCT, - RCD_VALUE_DELAY = 2 SECONDS, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 2 - ) - return FALSE - -/obj/structure/grille/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_DECONSTRUCT) - to_chat(user, span("notice", "You deconstruct \the [src].")) - qdel(src) - return TRUE - if(RCD_WINDOWGRILLE) - if(locate(/obj/structure/window) in loc) - return FALSE - to_chat(user, span("notice", "You construct a window.")) - var/obj/structure/window/WD = new the_rcd.window_type(loc) - WD.anchored = TRUE - return TRUE - return FALSE - -/obj/structure/grille/take_damage(var/damage) - health -= damage - spawn(1) healthcheck() - return 1 - +/obj/structure/grille + name = "grille" + desc = "A flimsy lattice of metal rods, with screws to secure it to the floor." + icon = 'icons/obj/structures_vr.dmi' // VOREStation Edit - New icons + icon_state = "grille" + density = TRUE + anchored = TRUE + pressure_resistance = 5*ONE_ATMOSPHERE + layer = TABLE_LAYER + explosion_resistance = 1 + var/health = 10 + var/destroyed = 0 + + +/obj/structure/grille/ex_act(severity) + qdel(src) + +/obj/structure/grille/update_icon() + if(destroyed) + icon_state = "[initial(icon_state)]-b" + else + icon_state = initial(icon_state) + +/obj/structure/grille/Bumped(atom/user) + if(ismob(user)) shock(user, 70) + +/obj/structure/grille/attack_hand(mob/user as mob) + + user.setClickCooldown(user.get_attack_speed()) + playsound(src, 'sound/effects/grillehit.ogg', 80, 1) + user.do_attack_animation(src) + + var/damage_dealt = 1 + var/attack_message = "kicks" + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(H.species.can_shred(H)) + attack_message = "mangles" + damage_dealt = 5 + + if(shock(user, 70)) + return + + if(HULK in user.mutations) + damage_dealt += 5 + else + damage_dealt += 1 + + attack_generic(user,damage_dealt,attack_message) + +/obj/structure/grille/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGRILLE)) + return TRUE + if(istype(mover, /obj/item/projectile)) + return prob(30) + return !density + +/obj/structure/grille/bullet_act(var/obj/item/projectile/Proj) + if(!Proj) return + + //Flimsy grilles aren't so great at stopping projectiles. However they can absorb some of the impact + var/damage = Proj.get_structure_damage() + var/passthrough = 0 + + if(!damage) return + + //20% chance that the grille provides a bit more cover than usual. Support structure for example might take up 20% of the grille's area. + //If they click on the grille itself then we assume they are aiming at the grille itself and the extra cover behaviour is always used. + switch(Proj.damage_type) + if(BRUTE) + //bullets + if(Proj.original == src || prob(20)) + Proj.damage *= between(0, Proj.damage/60, 0.5) + if(prob(max((damage-10)/25, 0))*100) + passthrough = 1 + else + Proj.damage *= between(0, Proj.damage/60, 1) + passthrough = 1 + if(BURN) + //beams and other projectiles are either blocked completely by grilles or stop half the damage. + if(!(Proj.original == src || prob(20))) + Proj.damage *= 0.5 + passthrough = 1 + + if(passthrough) + . = PROJECTILE_CONTINUE + damage = between(0, (damage - Proj.damage)*(Proj.damage_type == BRUTE? 0.4 : 1), 10) //if the bullet passes through then the grille avoids most of the damage + + src.health -= damage*0.2 + spawn(0) healthcheck() //spawn to make sure we return properly if the grille is deleted + +/obj/structure/grille/attackby(obj/item/W as obj, mob/user as mob) + if(!istype(W)) + return + if(istype(W, /obj/item/weapon/rcd)) // To stop us from hitting the grille when building windows, because grilles don't let parent handle it properly. + return FALSE + else if(W.has_tool_quality(TOOL_WIRECUTTER)) + if(!shock(user, 100)) + playsound(src, W.usesound, 100, 1) + new /obj/item/stack/rods(get_turf(src), destroyed ? 1 : 2) + qdel(src) + else if((W.has_tool_quality(TOOL_SCREWDRIVER)) && (istype(loc, /turf/simulated) || anchored)) + if(!shock(user, 90)) + playsound(src, W.usesound, 100, 1) + anchored = !anchored + user.visible_message("[user] [anchored ? "fastens" : "unfastens"] the grille.", \ + "You have [anchored ? "fastened the grille to" : "unfastened the grille from"] the floor.") + return + + //window placing begin //TODO CONVERT PROPERLY TO MATERIAL DATUM + else if(istype(W,/obj/item/stack/material)) + var/obj/item/stack/material/ST = W + if(!ST.material.created_window) + return 0 + + var/dir_to_set = 1 + if(loc == user.loc) + dir_to_set = user.dir + else + if( ( x == user.x ) || (y == user.y) ) //Only supposed to work for cardinal directions. + if( x == user.x ) + if( y > user.y ) + dir_to_set = 2 + else + dir_to_set = 1 + else if( y == user.y ) + if( x > user.x ) + dir_to_set = 8 + else + dir_to_set = 4 + else + to_chat(user, "You can't reach.") + return //Only works for cardinal direcitons, diagonals aren't supposed to work like this. + for(var/obj/structure/window/WINDOW in loc) + if(WINDOW.dir == dir_to_set) + to_chat(user, "There is already a window facing this way there.") + return + to_chat(user, "You start placing the window.") + if(do_after(user,20)) + for(var/obj/structure/window/WINDOW in loc) + if(WINDOW.dir == dir_to_set)//checking this for a 2nd time to check if a window was made while we were waiting. + to_chat(user, "There is already a window facing this way there.") + return + + var/wtype = ST.material.created_window + if (ST.use(1)) + var/obj/structure/window/WD = new wtype(loc, dir_to_set, 1) + to_chat(user, "You place the [WD] on [src].") + WD.update_icon() + return +//window placing end + + else if((W.flags & NOCONDUCT) || !shock(user, 70)) + user.setClickCooldown(user.get_attack_speed(W)) + user.do_attack_animation(src) + playsound(src, 'sound/effects/grillehit.ogg', 80, 1) + switch(W.damtype) + if("fire") + health -= W.force + if("brute") + health -= W.force * 0.1 + healthcheck() + ..() + return + + +/obj/structure/grille/proc/healthcheck() + if(health <= 0) + if(!destroyed) + density = FALSE + destroyed = 1 + update_icon() + new /obj/item/stack/rods(get_turf(src)) + + else + if(health <= -6) + new /obj/item/stack/rods(get_turf(src)) + qdel(src) + return + return + +// shock user with probability prb (if all connections & power are working) +// returns 1 if shocked, 0 otherwise + +/obj/structure/grille/proc/shock(mob/user as mob, prb) + + if(!anchored || destroyed) // anchored/destroyed grilles are never connected + return 0 + if(!prob(prb)) + return 0 + if(!in_range(src, user))//To prevent TK and mech users from getting shocked + return 0 + var/turf/T = get_turf(src) + var/obj/structure/cable/C = T.get_cable_node() + if(C) + if(electrocute_mob(user, C, src)) + if(C.powernet) + C.powernet.trigger_warning() + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + if(user.stunned) + return 1 + else + return 0 + return 0 + +/obj/structure/grille/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(!destroyed) + if(exposed_temperature > T0C + 1500) + health -= 1 + healthcheck() + ..() + +/obj/structure/grille/attack_generic(var/mob/user, var/damage, var/attack_verb) + visible_message("[user] [attack_verb] the [src]!") + user.do_attack_animation(src) + health -= damage + spawn(1) healthcheck() + return 1 + +// Used in mapping to avoid +/obj/structure/grille/broken + destroyed = 1 + icon_state = "grille-b" + density = FALSE + +/obj/structure/grille/broken/New() + ..() + health = rand(-5, -1) //In the destroyed but not utterly threshold. + healthcheck() //Send this to healthcheck just in case we want to do something else with it. + +/obj/structure/grille/cult + name = "cult grille" + desc = "A matrice built out of an unknown material, with some sort of force field blocking air around it." + icon_state = "grillecult" + health = 40 // Make it strong enough to avoid people breaking in too easily. + can_atmos_pass = ATMOS_PASS_NO // Make sure air doesn't drain. + +/obj/structure/grille/broken/cult + icon_state = "grillecult-b" + +/obj/structure/grille/rustic + name = "rustic grille" + desc = "A lattice of metal, arranged in an old, rustic fashion." + icon_state = "grillerustic" + +/obj/structure/grille/broken/rustic + icon_state = "grillerustic-b" + + +/obj/structure/grille/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_WINDOWGRILLE) + // A full tile window costs 4 glass sheets. + return list( + RCD_VALUE_MODE = RCD_WINDOWGRILLE, + RCD_VALUE_DELAY = 2 SECONDS, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 4 + ) + + if(RCD_DECONSTRUCT) + return list( + RCD_VALUE_MODE = RCD_DECONSTRUCT, + RCD_VALUE_DELAY = 2 SECONDS, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 2 + ) + return FALSE + +/obj/structure/grille/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_DECONSTRUCT) + to_chat(user, span("notice", "You deconstruct \the [src].")) + qdel(src) + return TRUE + if(RCD_WINDOWGRILLE) + if(locate(/obj/structure/window) in loc) + return FALSE + to_chat(user, span("notice", "You construct a window.")) + var/obj/structure/window/WD = new the_rcd.window_type(loc) + WD.anchored = TRUE + return TRUE + return FALSE + +/obj/structure/grille/take_damage(var/damage) + health -= damage + spawn(1) healthcheck() + return 1 + diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index 20b496291ee..8450a219b5f 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -1,500 +1,500 @@ -GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) - -/obj/structure/janitorialcart - name = "janitorial cart" - desc = "The ultimate in janitorial carts! Has space for water, mops, signs, trash bags, and more!" - description_info = "You can use alt-click while holding a mop to stow the mop. Alt-click holding a reagent container will empty the contents into the bucket without trying to put the container in any attached trash bag." - icon = 'icons/obj/janitor.dmi' - icon_state = "cart" - anchored = FALSE - density = TRUE - flags = OPENCONTAINER - climbable = TRUE - //copypaste sorry - var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite - var/obj/item/weapon/storage/bag/trash/mybag = null - var/obj/item/weapon/mop/mymop = null - var/obj/item/weapon/reagent_containers/spray/myspray = null - var/obj/item/device/lightreplacer/myreplacer = null - var/obj/structure/mopbucket/mybucket = null - var/has_items = FALSE - var/dismantled = TRUE - var/signs = 0 //maximum capacity hardcoded below - var/list/tgui_icons = list() - - var/static/list/equippable_item_whitelist - -/obj/structure/janitorialcart/proc/equip_janicart_item(mob/user, obj/item/I) - if(!equippable_item_whitelist) - equippable_item_whitelist = typecacheof(list( - /obj/item/weapon/storage/bag/trash, - /obj/item/weapon/mop, - /obj/item/weapon/reagent_containers/spray, - /obj/item/device/lightreplacer, - /obj/item/clothing/suit/caution, - )) - - if(!is_type_in_typecache(I, equippable_item_whitelist)) - to_chat(user, "There's no room in [src] for [I].") - return FALSE - - if(!user.canUnEquip(I)) - to_chat(user, "[I] is stuck to your hand.") - return FALSE - - if(istype(I, /obj/item/weapon/storage/bag/trash)) - if(mybag) - to_chat(user, "[src] already has \an [I].") - return FALSE - mybag = I - setTguiIcon("mybag", mybag) - - else if(istype(I, /obj/item/weapon/mop)) - if(mymop) - to_chat(user, "[src] already has \an [I].") - return FALSE - mymop = I - setTguiIcon("mymop", mymop) - - else if(istype(I, /obj/item/weapon/reagent_containers/spray)) - if(myspray) - to_chat(user, "[src] already has \an [I].") - return FALSE - myspray = I - setTguiIcon("myspray", myspray) - - else if(istype(I, /obj/item/device/lightreplacer)) - if(myreplacer) - to_chat(user, "[src] already has \an [I].") - return FALSE - myreplacer = I - setTguiIcon("myreplacer", myreplacer) - - else if(istype(I, /obj/item/clothing/suit/caution)) - if(signs < 4) - signs++ - setTguiIcon("signs", I) - else - to_chat(user, "[src] can't hold any more signs.") - return FALSE - else - // This may look like duplicate code, but it's important that we don't call unEquip *and* warn the user if - // something horrible goes wrong. (this else is never supposed to happen) - to_chat(user, "There's no room in [src] for [I].") - return FALSE - - user.drop_from_inventory(I, src) - update_icon() - to_chat(user, "You put [I] into [src].") - return TRUE - -/obj/structure/janitorialcart/proc/setTguiIcon(key, atom/A) - if(!istype(A) || !key) - return - - var/icon/F = getFlatIcon(A, defdir = SOUTH, no_anim = TRUE) - tgui_icons["[key]"] = "'data:image/png;base64,[icon2base64(F)]'" - SStgui.update_uis(src) - -/obj/structure/janitorialcart/proc/nullTguiIcon(key) - if(!key) - return - tgui_icons.Remove(key) - SStgui.update_uis(src) - -/obj/structure/janitorialcart/proc/clearTguiIcons() - tgui_icons.Cut() - SStgui.update_uis(src) - -/obj/structure/janitorialcart/Destroy() - QDEL_NULL(mybag) - QDEL_NULL(mymop) - QDEL_NULL(myspray) - QDEL_NULL(myreplacer) - QDEL_NULL(mybucket) - clearTguiIcons() - return ..() - -/obj/structure/janitorialcart/examine(mob/user) - . = ..(user) - if(istype(mybucket)) - var/contains = mybucket.reagents.total_volume - . += "\icon[src][bicon(src)] The bucket contains [contains] unit\s of liquid!" - else - . += "\icon[src][bicon(src)] There is no bucket mounted on it!" - -/obj/structure/janitorialcart/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob) - if (istype(O, /obj/structure/mopbucket) && !mybucket) - O.forceMove(src) - mybucket = O - setTguiIcon("mybucket", mybucket) - to_chat(user, "You mount the [O] on the janicart.") - update_icon() - else - ..() - -/obj/structure/janitorialcart/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/weapon/mop) || istype(I, /obj/item/weapon/reagent_containers/glass/rag) || istype(I, /obj/item/weapon/soap)) - if (mybucket) - if(I.reagents.total_volume < I.reagents.maximum_volume) - if(mybucket.reagents.total_volume < 1) - to_chat(user, "[mybucket] is empty!") - else - mybucket.reagents.trans_to_obj(I, 5) // - to_chat(user, "You wet [I] in [mybucket].") - playsound(src, 'sound/effects/slosh.ogg', 25, 1) - else - to_chat(user, "[I] can't absorb anymore liquid!") - else - to_chat(user, "There is no bucket mounted here to dip [I] into!") - return 1 - - else if (istype(I, /obj/item/weapon/reagent_containers/glass/bucket) && mybucket) - I.afterattack(mybucket, usr, 1) - update_icon() - return 1 - - else if(istype(I, /obj/item/weapon/reagent_containers/spray) && !myspray) - equip_janicart_item(user, I) - return 1 - - else if(istype(I, /obj/item/device/lightreplacer) && !myreplacer) - equip_janicart_item(user, I) - return 1 - - else if(istype(I, /obj/item/weapon/storage/bag/trash) && !mybag) - equip_janicart_item(user, I) - return 1 - - else if(istype(I, /obj/item/clothing/suit/caution)) - equip_janicart_item(user, I) - return 1 - - else if(mybag) - return mybag.attackby(I, user) - //This return will prevent afterattack from executing if the object goes into the trashbag, - //This prevents dumb stuff like splashing the cart with the contents of a container, after putting said container into trash - - else if (!has_items) - if (I.has_tool_quality(TOOL_WRENCH)) - if (do_after(user, 5 SECONDS, src)) - dismantle(user) - return - ..() - - -//New Altclick functionality! -//Altclick the cart with a mop to stow the mop away -//Altclick the cart with a reagent container to pour things into the bucket without putting the bottle in trash -/obj/structure/janitorialcart/AltClick(mob/living/user) - if(user.incapacitated() || !Adjacent(user)) return - var/obj/I = usr.get_active_hand() - if(istype(I, /obj/item/weapon/mop)) - equip_janicart_item(user, I) - else if(istype(I, /obj/item/weapon/reagent_containers) && mybucket) - var/obj/item/weapon/reagent_containers/C = I - C.afterattack(mybucket, usr, 1) - update_icon() - - -/obj/structure/janitorialcart/attack_hand(mob/user) - tgui_interact(user) - return - -/obj/structure/janitorialcart/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "JanitorCart", name) // 240, 160 - ui.set_autoupdate(FALSE) - ui.open() - -/obj/structure/janitorialcart/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) - var/list/data = ..() - - data["mybag"] = mybag ? capitalize(mybag.name) : null - data["mybucket"] = mybucket ? capitalize(mybucket.name) : null - data["mymop"] = mymop ? capitalize(mymop.name) : null - data["myspray"] = myspray ? capitalize(myspray.name) : null - data["myreplacer"] = myreplacer ? capitalize(myreplacer.name) : null - data["signs"] = signs ? "[signs] sign\s" : null - - data["icons"] = tgui_icons - return data - -/obj/structure/janitorialcart/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - var/obj/item/I = usr.get_active_hand() - - switch(action) - if("bag") - if(mybag) - usr.put_in_hands(mybag) - to_chat(usr, "You take [mybag] from [src].") - mybag = null - nullTguiIcon("mybag") - else if(is_type_in_typecache(I, equippable_item_whitelist)) - equip_janicart_item(usr, I) - if("mop") - if(mymop) - usr.put_in_hands(mymop) - to_chat(usr, "You take [mymop] from [src].") - mymop = null - nullTguiIcon("mymop") - else if(is_type_in_typecache(I, equippable_item_whitelist)) - equip_janicart_item(usr, I) - if("spray") - if(myspray) - usr.put_in_hands(myspray) - to_chat(usr, "You take [myspray] from [src].") - myspray = null - nullTguiIcon("myspray") - else if(is_type_in_typecache(I, equippable_item_whitelist)) - equip_janicart_item(usr, I) - if("replacer") - if(myreplacer) - usr.put_in_hands(myreplacer) - to_chat(usr, "You take [myreplacer] from [src].") - myreplacer = null - nullTguiIcon("myreplacer") - else if(is_type_in_typecache(I, equippable_item_whitelist)) - equip_janicart_item(usr, I) - if("sign") - if(istype(I, /obj/item/clothing/suit/caution) && signs < 4) - equip_janicart_item(usr, I) - else if(signs) - var/obj/item/clothing/suit/caution/sign = locate() in src - if(sign) - usr.put_in_hands(sign) - to_chat(usr, "You take \a [sign] from [src].") - signs-- - if(!signs) - nullTguiIcon("signs") - else - to_chat(usr, "[src] doesn't have any signs left.") - if("bucket") - if(mybucket) - mybucket.forceMove(get_turf(usr)) - to_chat(usr, "You unmount [mybucket] from [src].") - mybucket = null - nullTguiIcon("mybucket") - else - to_chat(usr, "((Drag and drop a mop bucket onto [src] to equip it.))") - return FALSE - else - return FALSE - - update_icon() - return TRUE - -/obj/structure/janitorialcart/update_icon() - cut_overlays() - - if(mybucket) - add_overlay("cart_bucket") - if(mybucket.reagents.total_volume >= 1) - add_overlay("water_cart") - if(mybag) - add_overlay("cart_garbage") - if(mymop) - add_overlay("cart_mop") - if(myspray) - add_overlay("cart_spray") - if(myreplacer) - add_overlay("cart_replacer") - if(signs) - add_overlay("cart_sign[signs]") - -//This is called if the cart is caught in an explosion, or destroyed by weapon fire -/obj/structure/janitorialcart/proc/spill(var/chance = 100) - var/turf/dropspot = get_turf(src) - if (mymop && prob(chance)) - mymop.forceMove(dropspot) - mymop.tumble(2) - mymop = null - - if (myspray && prob(chance)) - myspray.forceMove(dropspot) - myspray.tumble(3) - myspray = null - - if (myreplacer && prob(chance)) - myreplacer.forceMove(dropspot) - myreplacer.tumble(3) - myreplacer = null - - if (mybucket && prob(chance*0.5))//bucket is heavier, harder to knock off - mybucket.forceMove(dropspot) - mybucket.tumble(1) - mybucket = null - - if (signs) - for (var/obj/item/clothing/suit/caution/Sign in src) - if (prob(min((chance*2),100))) - signs-- - Sign.forceMove(dropspot) - Sign.tumble(3) - if (signs < 0)//safety for something that shouldn't happen - signs = 0 - update_icon() - return - - if (mybag && prob(min((chance*2),100)))//Bag is flimsy - mybag.forceMove(dropspot) - mybag.tumble(1) - mybag.spill()//trashbag spills its contents too - mybag = null - - update_icon() - clearTguiIcons() - - - -/obj/structure/janitorialcart/proc/dismantle(var/mob/user = null) - if (!dismantled) - if (has_items) - spill() - - new /obj/item/stack/material/steel(src.loc, 10) - new /obj/item/stack/material/plastic(src.loc, 10) - new /obj/item/stack/rods(src.loc, 20) - dismantled = 1 - qdel(src) - - -/obj/structure/janitorialcart/ex_act(severity) - spill(100 / severity) - ..() - - - - -//old style mischievio-cart -/obj/structure/bed/chair/janicart - name = "janicart" - icon = 'icons/obj/vehicles.dmi' - icon_state = "pussywagon" - anchored = TRUE - density = TRUE - flags = OPENCONTAINER - //copypaste sorry - var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite - var/obj/item/weapon/storage/bag/trash/mybag = null - var/callme = "pimpin' ride" //how do people refer to it? - - -/obj/structure/bed/chair/janicart/New() - create_reagents(300) - update_layer() - - -/obj/structure/bed/chair/janicart/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "This [callme] contains [reagents.total_volume] unit\s of water!" - if(mybag) - . += "\A [mybag] is hanging on the [callme]." - - -/obj/structure/bed/chair/janicart/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/weapon/mop)) - if(reagents.total_volume > 1) - reagents.trans_to_obj(I, 2) - to_chat(user, "You wet [I] in the [callme].") - playsound(src, 'sound/effects/slosh.ogg', 25, 1) - else - to_chat(user, "This [callme] is out of water!") - else if(istype(I, /obj/item/key)) - to_chat(user, "Hold [I] in one of your hands while you drive this [callme].") - else if(istype(I, /obj/item/weapon/storage/bag/trash)) - to_chat(user, "You hook the trashbag onto the [callme].") - user.drop_item() - I.loc = src - mybag = I - - -/obj/structure/bed/chair/janicart/attack_hand(mob/user) - if(mybag) - mybag.loc = get_turf(user) - user.put_in_hands(mybag) - mybag = null - else - ..() - - -/obj/structure/bed/chair/janicart/relaymove(mob/living/user, direction) - if(user.stat || user.stunned || user.weakened || user.paralysis) - unbuckle_mob() - if(user.get_type_in_hands(/obj/item/key)) - step(src, direction) - update_mob() - else - to_chat(user, "You'll need the keys in one of your hands to drive this [callme].") - - -/obj/structure/bed/chair/janicart/post_buckle_mob(mob/living/M) - update_mob() - return ..() - - -/obj/structure/bed/chair/janicart/update_layer() - if(dir == SOUTH) - layer = FLY_LAYER - else - layer = OBJ_LAYER - - -/obj/structure/bed/chair/janicart/unbuckle_mob() - var/mob/living/M = ..() - if(M) - M.pixel_x = 0 - M.pixel_y = 0 - return M - - -/obj/structure/bed/chair/janicart/set_dir() - ..() - update_layer() - if(has_buckled_mobs()) - for(var/mob/living/L as anything in buckled_mobs) - if(L.loc != loc) - L.buckled = null //Temporary, so Move() succeeds. - L.buckled = src //Restoring - - update_mob() - - -/obj/structure/bed/chair/janicart/proc/update_mob() - if(has_buckled_mobs()) - for(var/mob/living/L as anything in buckled_mobs) - L.set_dir(dir) - switch(dir) - if(SOUTH) - L.pixel_x = 0 - L.pixel_y = 7 - if(WEST) - L.pixel_x = 13 - L.pixel_y = 7 - if(NORTH) - L.pixel_x = 0 - L.pixel_y = 4 - if(EAST) - L.pixel_x = -13 - L.pixel_y = 7 - - -/obj/structure/bed/chair/janicart/bullet_act(var/obj/item/projectile/Proj) - if(has_buckled_mobs()) - if(prob(85)) - var/mob/living/L = pick(buckled_mobs) - return L.bullet_act(Proj) - visible_message("[Proj] ricochets off the [callme]!") - - -/obj/item/key - name = "key" - desc = "A keyring with a small steel key, and a pink fob reading \"Pussy Wagon\"." - icon = 'icons/obj/vehicles.dmi' - icon_state = "keys" - w_class = ITEMSIZE_TINY +GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) + +/obj/structure/janitorialcart + name = "janitorial cart" + desc = "The ultimate in janitorial carts! Has space for water, mops, signs, trash bags, and more!" + description_info = "You can use alt-click while holding a mop to stow the mop. Alt-click holding a reagent container will empty the contents into the bucket without trying to put the container in any attached trash bag." + icon = 'icons/obj/janitor.dmi' + icon_state = "cart" + anchored = FALSE + density = TRUE + flags = OPENCONTAINER + climbable = TRUE + //copypaste sorry + var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite + var/obj/item/weapon/storage/bag/trash/mybag = null + var/obj/item/weapon/mop/mymop = null + var/obj/item/weapon/reagent_containers/spray/myspray = null + var/obj/item/device/lightreplacer/myreplacer = null + var/obj/structure/mopbucket/mybucket = null + var/has_items = FALSE + var/dismantled = TRUE + var/signs = 0 //maximum capacity hardcoded below + var/list/tgui_icons = list() + + var/static/list/equippable_item_whitelist + +/obj/structure/janitorialcart/proc/equip_janicart_item(mob/user, obj/item/I) + if(!equippable_item_whitelist) + equippable_item_whitelist = typecacheof(list( + /obj/item/weapon/storage/bag/trash, + /obj/item/weapon/mop, + /obj/item/weapon/reagent_containers/spray, + /obj/item/device/lightreplacer, + /obj/item/clothing/suit/caution, + )) + + if(!is_type_in_typecache(I, equippable_item_whitelist)) + to_chat(user, "There's no room in [src] for [I].") + return FALSE + + if(!user.canUnEquip(I)) + to_chat(user, "[I] is stuck to your hand.") + return FALSE + + if(istype(I, /obj/item/weapon/storage/bag/trash)) + if(mybag) + to_chat(user, "[src] already has \an [I].") + return FALSE + mybag = I + setTguiIcon("mybag", mybag) + + else if(istype(I, /obj/item/weapon/mop)) + if(mymop) + to_chat(user, "[src] already has \an [I].") + return FALSE + mymop = I + setTguiIcon("mymop", mymop) + + else if(istype(I, /obj/item/weapon/reagent_containers/spray)) + if(myspray) + to_chat(user, "[src] already has \an [I].") + return FALSE + myspray = I + setTguiIcon("myspray", myspray) + + else if(istype(I, /obj/item/device/lightreplacer)) + if(myreplacer) + to_chat(user, "[src] already has \an [I].") + return FALSE + myreplacer = I + setTguiIcon("myreplacer", myreplacer) + + else if(istype(I, /obj/item/clothing/suit/caution)) + if(signs < 4) + signs++ + setTguiIcon("signs", I) + else + to_chat(user, "[src] can't hold any more signs.") + return FALSE + else + // This may look like duplicate code, but it's important that we don't call unEquip *and* warn the user if + // something horrible goes wrong. (this else is never supposed to happen) + to_chat(user, "There's no room in [src] for [I].") + return FALSE + + user.drop_from_inventory(I, src) + update_icon() + to_chat(user, "You put [I] into [src].") + return TRUE + +/obj/structure/janitorialcart/proc/setTguiIcon(key, atom/A) + if(!istype(A) || !key) + return + + var/icon/F = getFlatIcon(A, defdir = SOUTH, no_anim = TRUE) + tgui_icons["[key]"] = "'data:image/png;base64,[icon2base64(F)]'" + SStgui.update_uis(src) + +/obj/structure/janitorialcart/proc/nullTguiIcon(key) + if(!key) + return + tgui_icons.Remove(key) + SStgui.update_uis(src) + +/obj/structure/janitorialcart/proc/clearTguiIcons() + tgui_icons.Cut() + SStgui.update_uis(src) + +/obj/structure/janitorialcart/Destroy() + QDEL_NULL(mybag) + QDEL_NULL(mymop) + QDEL_NULL(myspray) + QDEL_NULL(myreplacer) + QDEL_NULL(mybucket) + clearTguiIcons() + return ..() + +/obj/structure/janitorialcart/examine(mob/user) + . = ..(user) + if(istype(mybucket)) + var/contains = mybucket.reagents.total_volume + . += "\icon[src][bicon(src)] The bucket contains [contains] unit\s of liquid!" + else + . += "\icon[src][bicon(src)] There is no bucket mounted on it!" + +/obj/structure/janitorialcart/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob) + if (istype(O, /obj/structure/mopbucket) && !mybucket) + O.forceMove(src) + mybucket = O + setTguiIcon("mybucket", mybucket) + to_chat(user, "You mount the [O] on the janicart.") + update_icon() + else + ..() + +/obj/structure/janitorialcart/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/weapon/mop) || istype(I, /obj/item/weapon/reagent_containers/glass/rag) || istype(I, /obj/item/weapon/soap)) + if (mybucket) + if(I.reagents.total_volume < I.reagents.maximum_volume) + if(mybucket.reagents.total_volume < 1) + to_chat(user, "[mybucket] is empty!") + else + mybucket.reagents.trans_to_obj(I, 5) // + to_chat(user, "You wet [I] in [mybucket].") + playsound(src, 'sound/effects/slosh.ogg', 25, 1) + else + to_chat(user, "[I] can't absorb anymore liquid!") + else + to_chat(user, "There is no bucket mounted here to dip [I] into!") + return 1 + + else if (istype(I, /obj/item/weapon/reagent_containers/glass/bucket) && mybucket) + I.afterattack(mybucket, usr, 1) + update_icon() + return 1 + + else if(istype(I, /obj/item/weapon/reagent_containers/spray) && !myspray) + equip_janicart_item(user, I) + return 1 + + else if(istype(I, /obj/item/device/lightreplacer) && !myreplacer) + equip_janicart_item(user, I) + return 1 + + else if(istype(I, /obj/item/weapon/storage/bag/trash) && !mybag) + equip_janicart_item(user, I) + return 1 + + else if(istype(I, /obj/item/clothing/suit/caution)) + equip_janicart_item(user, I) + return 1 + + else if(mybag) + return mybag.attackby(I, user) + //This return will prevent afterattack from executing if the object goes into the trashbag, + //This prevents dumb stuff like splashing the cart with the contents of a container, after putting said container into trash + + else if (!has_items) + if (I.has_tool_quality(TOOL_WRENCH)) + if (do_after(user, 5 SECONDS, src)) + dismantle(user) + return + ..() + + +//New Altclick functionality! +//Altclick the cart with a mop to stow the mop away +//Altclick the cart with a reagent container to pour things into the bucket without putting the bottle in trash +/obj/structure/janitorialcart/AltClick(mob/living/user) + if(user.incapacitated() || !Adjacent(user)) return + var/obj/I = usr.get_active_hand() + if(istype(I, /obj/item/weapon/mop)) + equip_janicart_item(user, I) + else if(istype(I, /obj/item/weapon/reagent_containers) && mybucket) + var/obj/item/weapon/reagent_containers/C = I + C.afterattack(mybucket, usr, 1) + update_icon() + + +/obj/structure/janitorialcart/attack_hand(mob/user) + tgui_interact(user) + return + +/obj/structure/janitorialcart/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "JanitorCart", name) // 240, 160 + ui.set_autoupdate(FALSE) + ui.open() + +/obj/structure/janitorialcart/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["mybag"] = mybag ? capitalize(mybag.name) : null + data["mybucket"] = mybucket ? capitalize(mybucket.name) : null + data["mymop"] = mymop ? capitalize(mymop.name) : null + data["myspray"] = myspray ? capitalize(myspray.name) : null + data["myreplacer"] = myreplacer ? capitalize(myreplacer.name) : null + data["signs"] = signs ? "[signs] sign\s" : null + + data["icons"] = tgui_icons + return data + +/obj/structure/janitorialcart/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + var/obj/item/I = usr.get_active_hand() + + switch(action) + if("bag") + if(mybag) + usr.put_in_hands(mybag) + to_chat(usr, "You take [mybag] from [src].") + mybag = null + nullTguiIcon("mybag") + else if(is_type_in_typecache(I, equippable_item_whitelist)) + equip_janicart_item(usr, I) + if("mop") + if(mymop) + usr.put_in_hands(mymop) + to_chat(usr, "You take [mymop] from [src].") + mymop = null + nullTguiIcon("mymop") + else if(is_type_in_typecache(I, equippable_item_whitelist)) + equip_janicart_item(usr, I) + if("spray") + if(myspray) + usr.put_in_hands(myspray) + to_chat(usr, "You take [myspray] from [src].") + myspray = null + nullTguiIcon("myspray") + else if(is_type_in_typecache(I, equippable_item_whitelist)) + equip_janicart_item(usr, I) + if("replacer") + if(myreplacer) + usr.put_in_hands(myreplacer) + to_chat(usr, "You take [myreplacer] from [src].") + myreplacer = null + nullTguiIcon("myreplacer") + else if(is_type_in_typecache(I, equippable_item_whitelist)) + equip_janicart_item(usr, I) + if("sign") + if(istype(I, /obj/item/clothing/suit/caution) && signs < 4) + equip_janicart_item(usr, I) + else if(signs) + var/obj/item/clothing/suit/caution/sign = locate() in src + if(sign) + usr.put_in_hands(sign) + to_chat(usr, "You take \a [sign] from [src].") + signs-- + if(!signs) + nullTguiIcon("signs") + else + to_chat(usr, "[src] doesn't have any signs left.") + if("bucket") + if(mybucket) + mybucket.forceMove(get_turf(usr)) + to_chat(usr, "You unmount [mybucket] from [src].") + mybucket = null + nullTguiIcon("mybucket") + else + to_chat(usr, "((Drag and drop a mop bucket onto [src] to equip it.))") + return FALSE + else + return FALSE + + update_icon() + return TRUE + +/obj/structure/janitorialcart/update_icon() + cut_overlays() + + if(mybucket) + add_overlay("cart_bucket") + if(mybucket.reagents.total_volume >= 1) + add_overlay("water_cart") + if(mybag) + add_overlay("cart_garbage") + if(mymop) + add_overlay("cart_mop") + if(myspray) + add_overlay("cart_spray") + if(myreplacer) + add_overlay("cart_replacer") + if(signs) + add_overlay("cart_sign[signs]") + +//This is called if the cart is caught in an explosion, or destroyed by weapon fire +/obj/structure/janitorialcart/proc/spill(var/chance = 100) + var/turf/dropspot = get_turf(src) + if (mymop && prob(chance)) + mymop.forceMove(dropspot) + mymop.tumble(2) + mymop = null + + if (myspray && prob(chance)) + myspray.forceMove(dropspot) + myspray.tumble(3) + myspray = null + + if (myreplacer && prob(chance)) + myreplacer.forceMove(dropspot) + myreplacer.tumble(3) + myreplacer = null + + if (mybucket && prob(chance*0.5))//bucket is heavier, harder to knock off + mybucket.forceMove(dropspot) + mybucket.tumble(1) + mybucket = null + + if (signs) + for (var/obj/item/clothing/suit/caution/Sign in src) + if (prob(min((chance*2),100))) + signs-- + Sign.forceMove(dropspot) + Sign.tumble(3) + if (signs < 0)//safety for something that shouldn't happen + signs = 0 + update_icon() + return + + if (mybag && prob(min((chance*2),100)))//Bag is flimsy + mybag.forceMove(dropspot) + mybag.tumble(1) + mybag.spill()//trashbag spills its contents too + mybag = null + + update_icon() + clearTguiIcons() + + + +/obj/structure/janitorialcart/proc/dismantle(var/mob/user = null) + if (!dismantled) + if (has_items) + spill() + + new /obj/item/stack/material/steel(src.loc, 10) + new /obj/item/stack/material/plastic(src.loc, 10) + new /obj/item/stack/rods(src.loc, 20) + dismantled = 1 + qdel(src) + + +/obj/structure/janitorialcart/ex_act(severity) + spill(100 / severity) + ..() + + + + +//old style mischievio-cart +/obj/structure/bed/chair/janicart + name = "janicart" + icon = 'icons/obj/vehicles.dmi' + icon_state = "pussywagon" + anchored = TRUE + density = TRUE + flags = OPENCONTAINER + //copypaste sorry + var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite + var/obj/item/weapon/storage/bag/trash/mybag = null + var/callme = "pimpin' ride" //how do people refer to it? + + +/obj/structure/bed/chair/janicart/New() + create_reagents(300) + update_layer() + + +/obj/structure/bed/chair/janicart/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "This [callme] contains [reagents.total_volume] unit\s of water!" + if(mybag) + . += "\A [mybag] is hanging on the [callme]." + + +/obj/structure/bed/chair/janicart/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/weapon/mop)) + if(reagents.total_volume > 1) + reagents.trans_to_obj(I, 2) + to_chat(user, "You wet [I] in the [callme].") + playsound(src, 'sound/effects/slosh.ogg', 25, 1) + else + to_chat(user, "This [callme] is out of water!") + else if(istype(I, /obj/item/key)) + to_chat(user, "Hold [I] in one of your hands while you drive this [callme].") + else if(istype(I, /obj/item/weapon/storage/bag/trash)) + to_chat(user, "You hook the trashbag onto the [callme].") + user.drop_item() + I.loc = src + mybag = I + + +/obj/structure/bed/chair/janicart/attack_hand(mob/user) + if(mybag) + mybag.loc = get_turf(user) + user.put_in_hands(mybag) + mybag = null + else + ..() + + +/obj/structure/bed/chair/janicart/relaymove(mob/living/user, direction) + if(user.stat || user.stunned || user.weakened || user.paralysis) + unbuckle_mob() + if(user.get_type_in_hands(/obj/item/key)) + step(src, direction) + update_mob() + else + to_chat(user, "You'll need the keys in one of your hands to drive this [callme].") + + +/obj/structure/bed/chair/janicart/post_buckle_mob(mob/living/M) + update_mob() + return ..() + + +/obj/structure/bed/chair/janicart/update_layer() + if(dir == SOUTH) + layer = FLY_LAYER + else + layer = OBJ_LAYER + + +/obj/structure/bed/chair/janicart/unbuckle_mob() + var/mob/living/M = ..() + if(M) + M.pixel_x = 0 + M.pixel_y = 0 + return M + + +/obj/structure/bed/chair/janicart/set_dir() + ..() + update_layer() + if(has_buckled_mobs()) + for(var/mob/living/L as anything in buckled_mobs) + if(L.loc != loc) + L.buckled = null //Temporary, so Move() succeeds. + L.buckled = src //Restoring + + update_mob() + + +/obj/structure/bed/chair/janicart/proc/update_mob() + if(has_buckled_mobs()) + for(var/mob/living/L as anything in buckled_mobs) + L.set_dir(dir) + switch(dir) + if(SOUTH) + L.pixel_x = 0 + L.pixel_y = 7 + if(WEST) + L.pixel_x = 13 + L.pixel_y = 7 + if(NORTH) + L.pixel_x = 0 + L.pixel_y = 4 + if(EAST) + L.pixel_x = -13 + L.pixel_y = 7 + + +/obj/structure/bed/chair/janicart/bullet_act(var/obj/item/projectile/Proj) + if(has_buckled_mobs()) + if(prob(85)) + var/mob/living/L = pick(buckled_mobs) + return L.bullet_act(Proj) + visible_message("[Proj] ricochets off the [callme]!") + + +/obj/item/key + name = "key" + desc = "A keyring with a small steel key, and a pink fob reading \"Pussy Wagon\"." + icon = 'icons/obj/vehicles.dmi' + icon_state = "keys" + w_class = ITEMSIZE_TINY diff --git a/code/game/objects/structures/kitchen_foodcart_vr.dm b/code/game/objects/structures/kitchen_foodcart_vr.dm index 4fb56b038bd..aa1ea26cf49 100644 --- a/code/game/objects/structures/kitchen_foodcart_vr.dm +++ b/code/game/objects/structures/kitchen_foodcart_vr.dm @@ -1,42 +1,42 @@ -/obj/structure/foodcart - name = "Foodcart" - icon = 'icons/obj/kitchen_vr.dmi' - icon_state = "foodcart-0" - desc = "The ultimate in food transport! When opened you notice two compartments with odd blue glows to them. One feels very warm, while the other is very cold." - anchored = FALSE - opacity = 0 - density = TRUE - -/obj/structure/foodcart/Initialize() - . = ..() - for(var/obj/item/I in loc) - if(istype(I, /obj/item/weapon/reagent_containers/food)) - I.loc = src - update_icon() - -/obj/structure/foodcart/attackby(obj/item/O as obj, mob/user as mob) - if(istype(O, /obj/item/weapon/reagent_containers/food)) - user.drop_item() - O.loc = src - update_icon() - else - return - -/obj/structure/foodcart/attack_hand(var/mob/user as mob) - if(contents.len) - var/obj/item/weapon/reagent_containers/food/choice = tgui_input_list(usr, "What would you like to grab from the cart?", "Grab Choice", contents) - if(choice) - if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - return - if(ishuman(user)) - if(!user.get_active_hand()) - user.put_in_hands(choice) - else - choice.loc = get_turf(src) - update_icon() - -/obj/structure/foodcart/update_icon() - if(contents.len < 5) - icon_state = "foodcart-[contents.len]" - else +/obj/structure/foodcart + name = "Foodcart" + icon = 'icons/obj/kitchen_vr.dmi' + icon_state = "foodcart-0" + desc = "The ultimate in food transport! When opened you notice two compartments with odd blue glows to them. One feels very warm, while the other is very cold." + anchored = FALSE + opacity = 0 + density = TRUE + +/obj/structure/foodcart/Initialize() + . = ..() + for(var/obj/item/I in loc) + if(istype(I, /obj/item/weapon/reagent_containers/food)) + I.loc = src + update_icon() + +/obj/structure/foodcart/attackby(obj/item/O as obj, mob/user as mob) + if(istype(O, /obj/item/weapon/reagent_containers/food)) + user.drop_item() + O.loc = src + update_icon() + else + return + +/obj/structure/foodcart/attack_hand(var/mob/user as mob) + if(contents.len) + var/obj/item/weapon/reagent_containers/food/choice = tgui_input_list(usr, "What would you like to grab from the cart?", "Grab Choice", contents) + if(choice) + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + return + if(ishuman(user)) + if(!user.get_active_hand()) + user.put_in_hands(choice) + else + choice.loc = get_turf(src) + update_icon() + +/obj/structure/foodcart/update_icon() + if(contents.len < 5) + icon_state = "foodcart-[contents.len]" + else icon_state = "foodcart-5" \ No newline at end of file diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm index 48e9e113e8e..777530a276d 100644 --- a/code/game/objects/structures/kitchen_spike.dm +++ b/code/game/objects/structures/kitchen_spike.dm @@ -1,63 +1,63 @@ -//////Kitchen Spike - -/obj/structure/kitchenspike - name = "meat spike" - icon = 'icons/obj/kitchen.dmi' - icon_state = "spike" - desc = "A spike for collecting meat from animals." - density = TRUE - anchored = TRUE - var/meat = 0 - var/occupied - var/meat_type - var/victim_name = "corpse" - -/obj/structure/kitchenspike/attackby(obj/item/weapon/grab/G as obj, mob/user as mob) - if(!istype(G, /obj/item/weapon/grab) || !ismob(G.affecting)) - return - if(occupied) - to_chat(user, "The spike already has something on it, finish collecting its meat first!") - else - if(spike(G.affecting)) - var/datum/gender/T = gender_datums[G.affecting.get_visible_gender()] - visible_message("[user] has forced [G.affecting] onto the spike, killing [T.him] instantly!") - var/mob/M = G.affecting - M.forceMove(src) - qdel(G) - qdel(M) - else - to_chat(user, "They are too big for the spike, try something smaller!") - -/obj/structure/kitchenspike/proc/spike(var/mob/living/victim) - if(!istype(victim)) - return - - if(istype(victim, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = victim - if(istype(H.species, /datum/species/monkey)) - meat_type = H.species.meat_type - icon_state = "spikebloody" - else - return 0 - else if(istype(victim, /mob/living/carbon/alien)) - meat_type = /obj/item/weapon/reagent_containers/food/snacks/xenomeat - icon_state = "spikebloodygreen" - else - return 0 - - victim_name = victim.name - occupied = 1 - meat = 5 - return 1 - -/obj/structure/kitchenspike/attack_hand(mob/user as mob) - if(..() || !occupied) - return - meat-- - new meat_type(get_turf(src)) - if(meat > 1) - to_chat(user, "You cut some meat from \the [victim_name]'s body.") - else if(meat == 1) - to_chat(user, "You remove the last piece of meat from \the [victim_name]!") - icon_state = "spike" - occupied = 0 +//////Kitchen Spike + +/obj/structure/kitchenspike + name = "meat spike" + icon = 'icons/obj/kitchen.dmi' + icon_state = "spike" + desc = "A spike for collecting meat from animals." + density = TRUE + anchored = TRUE + var/meat = 0 + var/occupied + var/meat_type + var/victim_name = "corpse" + +/obj/structure/kitchenspike/attackby(obj/item/weapon/grab/G as obj, mob/user as mob) + if(!istype(G, /obj/item/weapon/grab) || !ismob(G.affecting)) + return + if(occupied) + to_chat(user, "The spike already has something on it, finish collecting its meat first!") + else + if(spike(G.affecting)) + var/datum/gender/T = gender_datums[G.affecting.get_visible_gender()] + visible_message("[user] has forced [G.affecting] onto the spike, killing [T.him] instantly!") + var/mob/M = G.affecting + M.forceMove(src) + qdel(G) + qdel(M) + else + to_chat(user, "They are too big for the spike, try something smaller!") + +/obj/structure/kitchenspike/proc/spike(var/mob/living/victim) + if(!istype(victim)) + return + + if(istype(victim, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = victim + if(istype(H.species, /datum/species/monkey)) + meat_type = H.species.meat_type + icon_state = "spikebloody" + else + return 0 + else if(istype(victim, /mob/living/carbon/alien)) + meat_type = /obj/item/weapon/reagent_containers/food/snacks/xenomeat + icon_state = "spikebloodygreen" + else + return 0 + + victim_name = victim.name + occupied = 1 + meat = 5 + return 1 + +/obj/structure/kitchenspike/attack_hand(mob/user as mob) + if(..() || !occupied) + return + meat-- + new meat_type(get_turf(src)) + if(meat > 1) + to_chat(user, "You cut some meat from \the [victim_name]'s body.") + else if(meat == 1) + to_chat(user, "You remove the last piece of meat from \the [victim_name]!") + icon_state = "spike" + occupied = 0 diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm index 6107f25f6c7..978b99ab3df 100644 --- a/code/game/objects/structures/lattice.dm +++ b/code/game/objects/structures/lattice.dm @@ -1,100 +1,100 @@ -/obj/structure/lattice - name = "lattice" - desc = "A lightweight support lattice." - icon = 'icons/obj/structures.dmi' - icon_state = "latticefull" - density = FALSE - anchored = TRUE - w_class = ITEMSIZE_NORMAL - plane = PLATING_PLANE - -/obj/structure/lattice/Initialize() - . = ..() - - if(!(istype(src.loc, /turf/space) || istype(src.loc, /turf/simulated/open) || istype(src.loc, /turf/simulated/mineral) || istype(src.loc, /turf/simulated/shuttle/plating/airless/carry))) - return INITIALIZE_HINT_QDEL - - for(var/obj/structure/lattice/LAT in src.loc) - if(LAT != src) - log_debug("Found multiple lattices at '[log_info_line(loc)]'") //VOREStation Edit, why was this a runtime, it's harmless - return INITIALIZE_HINT_QDEL - icon = 'icons/obj/smoothlattice.dmi' - icon_state = "latticeblank" - updateOverlays() - for (var/dir in cardinal) - var/obj/structure/lattice/L - if(locate(/obj/structure/lattice, get_step(src, dir))) - L = locate(/obj/structure/lattice, get_step(src, dir)) - L.updateOverlays() - -/obj/structure/lattice/Destroy() - for (var/dir in cardinal) - var/obj/structure/lattice/L - if(locate(/obj/structure/lattice, get_step(src, dir))) - L = locate(/obj/structure/lattice, get_step(src, dir)) - L.updateOverlays(src.loc) - if(istype(loc, /turf/simulated/open)) - var/turf/simulated/open/O = loc - spawn(1) - if(istype(O)) // If we built a new floor with the lattice, the open turf won't exist anymore. - O.update() // This lattice may be supporting things on top of it. If it's being deleted, they need to fall down. - . = ..() - -/obj/structure/lattice/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - return - if(2.0) - qdel(src) - return - if(3.0) - return - else - return - -/obj/structure/lattice/attackby(obj/item/C as obj, mob/user as mob) - - if(istype(C, /obj/item/stack/tile/floor)) - var/turf/T = get_turf(src) - T.attackby(C, user) //BubbleWrap - hand this off to the underlying turf instead - return - if(C.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/WT = C.get_welder() - if(WT.welding == 1) - if(WT.remove_fuel(0, user)) - to_chat(user, "Slicing lattice joints ...") - new /obj/item/stack/rods(src.loc) - qdel(src) - return - if(istype(C, /obj/item/stack/rods)) - var/obj/item/stack/rods/R = C - if(R.get_amount() < 2) - to_chat(user, "You need at least two rods to form a catwalk here.") - else - to_chat(user, "You start connecting \the [R.name] to \the [src.name] ...") - if(do_after(user, 5 SECONDS)) - R.use(2) //2023-02-27 bugfix to prevent rods being used without catwalk creation - src.alpha = 0 // Note: I don't know why this is set, Eris did it, just trusting for now. ~Leshana - new /obj/structure/catwalk(src.loc) - qdel(src) - return - return - -/obj/structure/lattice/proc/updateOverlays() - //if(!(istype(src.loc, /turf/space))) - // qdel(src) - spawn(1) - cut_overlays() - - var/dir_sum = 0 - - for (var/direction in cardinal) - if(locate(/obj/structure/lattice, get_step(src, direction))) - dir_sum += direction - else - if(!(istype(get_step(src, direction), /turf/space))) - dir_sum += direction - - icon_state = "lattice[dir_sum]" - return +/obj/structure/lattice + name = "lattice" + desc = "A lightweight support lattice." + icon = 'icons/obj/structures.dmi' + icon_state = "latticefull" + density = FALSE + anchored = TRUE + w_class = ITEMSIZE_NORMAL + plane = PLATING_PLANE + +/obj/structure/lattice/Initialize() + . = ..() + + if(!(istype(src.loc, /turf/space) || istype(src.loc, /turf/simulated/open) || istype(src.loc, /turf/simulated/mineral) || istype(src.loc, /turf/simulated/shuttle/plating/airless/carry))) + return INITIALIZE_HINT_QDEL + + for(var/obj/structure/lattice/LAT in src.loc) + if(LAT != src) + log_debug("Found multiple lattices at '[log_info_line(loc)]'") //VOREStation Edit, why was this a runtime, it's harmless + return INITIALIZE_HINT_QDEL + icon = 'icons/obj/smoothlattice.dmi' + icon_state = "latticeblank" + updateOverlays() + for (var/dir in cardinal) + var/obj/structure/lattice/L + if(locate(/obj/structure/lattice, get_step(src, dir))) + L = locate(/obj/structure/lattice, get_step(src, dir)) + L.updateOverlays() + +/obj/structure/lattice/Destroy() + for (var/dir in cardinal) + var/obj/structure/lattice/L + if(locate(/obj/structure/lattice, get_step(src, dir))) + L = locate(/obj/structure/lattice, get_step(src, dir)) + L.updateOverlays(src.loc) + if(istype(loc, /turf/simulated/open)) + var/turf/simulated/open/O = loc + spawn(1) + if(istype(O)) // If we built a new floor with the lattice, the open turf won't exist anymore. + O.update() // This lattice may be supporting things on top of it. If it's being deleted, they need to fall down. + . = ..() + +/obj/structure/lattice/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + return + if(2.0) + qdel(src) + return + if(3.0) + return + else + return + +/obj/structure/lattice/attackby(obj/item/C as obj, mob/user as mob) + + if(istype(C, /obj/item/stack/tile/floor)) + var/turf/T = get_turf(src) + T.attackby(C, user) //BubbleWrap - hand this off to the underlying turf instead + return + if(C.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = C.get_welder() + if(WT.welding == 1) + if(WT.remove_fuel(0, user)) + to_chat(user, "Slicing lattice joints ...") + new /obj/item/stack/rods(src.loc) + qdel(src) + return + if(istype(C, /obj/item/stack/rods)) + var/obj/item/stack/rods/R = C + if(R.get_amount() < 2) + to_chat(user, "You need at least two rods to form a catwalk here.") + else + to_chat(user, "You start connecting \the [R.name] to \the [src.name] ...") + if(do_after(user, 5 SECONDS)) + R.use(2) //2023-02-27 bugfix to prevent rods being used without catwalk creation + src.alpha = 0 // Note: I don't know why this is set, Eris did it, just trusting for now. ~Leshana + new /obj/structure/catwalk(src.loc) + qdel(src) + return + return + +/obj/structure/lattice/proc/updateOverlays() + //if(!(istype(src.loc, /turf/space))) + // qdel(src) + spawn(1) + cut_overlays() + + var/dir_sum = 0 + + for (var/direction in cardinal) + if(locate(/obj/structure/lattice, get_step(src, direction))) + dir_sum += direction + else + if(!(istype(get_step(src, direction), /turf/space))) + dir_sum += direction + + icon_state = "lattice[dir_sum]" + return diff --git a/code/game/objects/structures/lightpost.dm b/code/game/objects/structures/lightpost.dm index 95f00d1467b..b915147c175 100644 --- a/code/game/objects/structures/lightpost.dm +++ b/code/game/objects/structures/lightpost.dm @@ -1,42 +1,42 @@ -/obj/structure/lightpost - name = "lightpost" - desc = "A homely lightpost." - icon = 'icons/obj/32x64.dmi' - icon_state = "lightpost" - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - anchored = TRUE - density = TRUE - opacity = FALSE - - var/lit = TRUE // If true, will have a glowing overlay and lighting. - var/festive = FALSE // If true, adds a festive bow overlay to it. - -/obj/structure/lightpost/Initialize() - update_icon() - return ..() - -/obj/structure/lightpost/update_icon() - cut_overlays() - - if(lit) - set_light(5, 1, "#E9E4AF") - var/image/glow = image(icon_state = "[icon_state]-glow") - glow.plane = PLANE_LIGHTING_ABOVE - add_overlay(glow) - else - set_light(0) - - if(festive) - var/image/bow = image(icon_state = "[icon_state]-festive") - add_overlay(bow) - -/obj/structure/lightpost/unlit - lit = FALSE - -/obj/structure/lightpost/festive - desc = "A homely lightpost adorned with festive decor." - festive = TRUE - -/obj/structure/lightpost/festive/unlit +/obj/structure/lightpost + name = "lightpost" + desc = "A homely lightpost." + icon = 'icons/obj/32x64.dmi' + icon_state = "lightpost" + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + anchored = TRUE + density = TRUE + opacity = FALSE + + var/lit = TRUE // If true, will have a glowing overlay and lighting. + var/festive = FALSE // If true, adds a festive bow overlay to it. + +/obj/structure/lightpost/Initialize() + update_icon() + return ..() + +/obj/structure/lightpost/update_icon() + cut_overlays() + + if(lit) + set_light(5, 1, "#E9E4AF") + var/image/glow = image(icon_state = "[icon_state]-glow") + glow.plane = PLANE_LIGHTING_ABOVE + add_overlay(glow) + else + set_light(0) + + if(festive) + var/image/bow = image(icon_state = "[icon_state]-festive") + add_overlay(bow) + +/obj/structure/lightpost/unlit + lit = FALSE + +/obj/structure/lightpost/festive + desc = "A homely lightpost adorned with festive decor." + festive = TRUE + +/obj/structure/lightpost/festive/unlit lit = FALSE \ No newline at end of file diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index 8aff4133052..a58eba45aae 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -1,144 +1,144 @@ -//wip wip wup -/obj/structure/mirror - name = "mirror" - desc = "A SalonPro Nano-Mirror(TM) brand mirror! The leading technology in hair salon products, utilizing nano-machinery to style your hair just right." - icon = 'icons/obj/watercloset.dmi' - icon_state = "mirror" - layer = ABOVE_WINDOW_LAYER - density = FALSE - anchored = TRUE - var/shattered = 0 - var/glass = 1 - var/datum/tgui_module/appearance_changer/mirror/M - -/obj/structure/mirror/Initialize(mapload, var/dir, var/building = 0, mob/user as mob) - M = new(src, null) - if(building) - glass = 0 - icon_state = "mirror_frame" - pixel_x = (dir & 3)? 0 : (dir == 4 ? -28 : 28) - pixel_y = (dir & 3)? (dir == 1 ? -30 : 30) : 0 - return - -/obj/structure/mirror/Destroy() - QDEL_NULL(M) - . = ..() - -/obj/structure/mirror/attack_hand(mob/user as mob) - if(!glass) return - if(shattered) return - - if(ishuman(user)) - M.tgui_interact(user) - -/obj/structure/mirror/proc/shatter() - if(!glass) return - if(shattered) return - shattered = 1 - icon_state = "mirror_broke" - playsound(src, "shatter", 70, 1) - desc = "Oh no, seven years of bad luck!" - - -/obj/structure/mirror/bullet_act(var/obj/item/projectile/Proj) - - if(prob(Proj.get_structure_damage() * 2)) - if(!shattered) - shatter() - else if(glass) - playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1) - ..() - -/obj/structure/mirror/attackby(obj/item/I as obj, mob/user as mob) - if(I.has_tool_quality(TOOL_WRENCH)) - if(!glass) - playsound(src, I.usesound, 50, 1) - if(do_after(user, 20 * I.toolspeed)) - to_chat(user, "You unfasten the frame.") - new /obj/item/frame/mirror( src.loc ) - qdel(src) - return - if(I.has_tool_quality(TOOL_WRENCH)) - if(shattered && glass) - to_chat(user, "The broken glass falls out.") - icon_state = "mirror_frame" - glass = !glass - new /obj/item/weapon/material/shard( src.loc ) - return - if(!shattered && glass) - playsound(src, I.usesound, 50, 1) - to_chat(user, "You remove the glass.") - glass = !glass - icon_state = "mirror_frame" - new /obj/item/stack/material/glass( src.loc, 2 ) - return - - if(istype(I, /obj/item/stack/material/glass)) - if(!glass) - var/obj/item/stack/material/glass/G = I - if (G.get_amount() < 2) - to_chat(user, "You need two sheets of glass to add them to the frame.") - return - to_chat(user, "You start to add the glass to the frame.") - if(do_after(user, 20)) - if (G.use(2)) - shattered = 0 - glass = 1 - icon_state = "mirror" - to_chat(user, "You add the glass to the frame.") - return - - if(shattered && glass) - playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1) - return - - if(prob(I.force * 2)) - visible_message("[user] smashes [src] with [I]!") - if(glass) - shatter() - else - visible_message("[user] hits [src] with [I]!") - playsound(src, 'sound/effects/Glasshit.ogg', 70, 1) - -/obj/structure/mirror/attack_generic(var/mob/user, var/damage) - - user.do_attack_animation(src) - if(shattered && glass) - playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1) - return 0 - - if(damage) - user.visible_message("[user] smashes [src]!") - if(glass) - shatter() - else - user.visible_message("[user] hits [src] and bounces off!") - return 1 - -// The following mirror is ~special~. -/obj/structure/mirror/raider - name = "cracked mirror" - desc = "Something seems strange about this old, dirty mirror. Your reflection doesn't look like you remember it." - icon_state = "mirror_broke" - shattered = 1 - -/obj/structure/mirror/raider/attack_hand(var/mob/living/carbon/human/user) - if(istype(get_area(src),/area/syndicate_mothership)) - if(istype(user) && user.mind && user.mind.special_role == "Raider" && user.species.name != SPECIES_VOX && is_alien_whitelisted(user, SPECIES_VOX)) - var/choice = tgui_alert(usr, "Do you wish to become a true Vox of the Shoal? This is not reversible.", "Become Vox?", list("No","Yes")) - if(choice && choice == "Yes") - var/mob/living/carbon/human/vox/vox = new(get_turf(src),SPECIES_VOX) - vox.gender = user.gender - raiders.equip(vox) - if(user.mind) - user.mind.transfer_to(vox) - spawn(1) - var/newname = sanitizeSafe(tgui_input_text(vox,"Enter a name, or leave blank for the default name.", "Name change","", MAX_NAME_LEN), MAX_NAME_LEN) - if(!newname || newname == "") - var/datum/language/L = GLOB.all_languages[vox.species.default_language] - newname = L.get_random_name() - vox.real_name = newname - vox.name = vox.real_name - raiders.update_access(vox) - qdel(user) +//wip wip wup +/obj/structure/mirror + name = "mirror" + desc = "A SalonPro Nano-Mirror(TM) brand mirror! The leading technology in hair salon products, utilizing nano-machinery to style your hair just right." + icon = 'icons/obj/watercloset.dmi' + icon_state = "mirror" + layer = ABOVE_WINDOW_LAYER + density = FALSE + anchored = TRUE + var/shattered = 0 + var/glass = 1 + var/datum/tgui_module/appearance_changer/mirror/M + +/obj/structure/mirror/Initialize(mapload, var/dir, var/building = 0, mob/user as mob) + M = new(src, null) + if(building) + glass = 0 + icon_state = "mirror_frame" + pixel_x = (dir & 3)? 0 : (dir == 4 ? -28 : 28) + pixel_y = (dir & 3)? (dir == 1 ? -30 : 30) : 0 + return + +/obj/structure/mirror/Destroy() + QDEL_NULL(M) + . = ..() + +/obj/structure/mirror/attack_hand(mob/user as mob) + if(!glass) return + if(shattered) return + + if(ishuman(user)) + M.tgui_interact(user) + +/obj/structure/mirror/proc/shatter() + if(!glass) return + if(shattered) return + shattered = 1 + icon_state = "mirror_broke" + playsound(src, "shatter", 70, 1) + desc = "Oh no, seven years of bad luck!" + + +/obj/structure/mirror/bullet_act(var/obj/item/projectile/Proj) + + if(prob(Proj.get_structure_damage() * 2)) + if(!shattered) + shatter() + else if(glass) + playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1) + ..() + +/obj/structure/mirror/attackby(obj/item/I as obj, mob/user as mob) + if(I.has_tool_quality(TOOL_WRENCH)) + if(!glass) + playsound(src, I.usesound, 50, 1) + if(do_after(user, 20 * I.toolspeed)) + to_chat(user, "You unfasten the frame.") + new /obj/item/frame/mirror( src.loc ) + qdel(src) + return + if(I.has_tool_quality(TOOL_WRENCH)) + if(shattered && glass) + to_chat(user, "The broken glass falls out.") + icon_state = "mirror_frame" + glass = !glass + new /obj/item/weapon/material/shard( src.loc ) + return + if(!shattered && glass) + playsound(src, I.usesound, 50, 1) + to_chat(user, "You remove the glass.") + glass = !glass + icon_state = "mirror_frame" + new /obj/item/stack/material/glass( src.loc, 2 ) + return + + if(istype(I, /obj/item/stack/material/glass)) + if(!glass) + var/obj/item/stack/material/glass/G = I + if (G.get_amount() < 2) + to_chat(user, "You need two sheets of glass to add them to the frame.") + return + to_chat(user, "You start to add the glass to the frame.") + if(do_after(user, 20)) + if (G.use(2)) + shattered = 0 + glass = 1 + icon_state = "mirror" + to_chat(user, "You add the glass to the frame.") + return + + if(shattered && glass) + playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1) + return + + if(prob(I.force * 2)) + visible_message("[user] smashes [src] with [I]!") + if(glass) + shatter() + else + visible_message("[user] hits [src] with [I]!") + playsound(src, 'sound/effects/Glasshit.ogg', 70, 1) + +/obj/structure/mirror/attack_generic(var/mob/user, var/damage) + + user.do_attack_animation(src) + if(shattered && glass) + playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1) + return 0 + + if(damage) + user.visible_message("[user] smashes [src]!") + if(glass) + shatter() + else + user.visible_message("[user] hits [src] and bounces off!") + return 1 + +// The following mirror is ~special~. +/obj/structure/mirror/raider + name = "cracked mirror" + desc = "Something seems strange about this old, dirty mirror. Your reflection doesn't look like you remember it." + icon_state = "mirror_broke" + shattered = 1 + +/obj/structure/mirror/raider/attack_hand(var/mob/living/carbon/human/user) + if(istype(get_area(src),/area/syndicate_mothership)) + if(istype(user) && user.mind && user.mind.special_role == "Raider" && user.species.name != SPECIES_VOX && is_alien_whitelisted(user, SPECIES_VOX)) + var/choice = tgui_alert(usr, "Do you wish to become a true Vox of the Shoal? This is not reversible.", "Become Vox?", list("No","Yes")) + if(choice && choice == "Yes") + var/mob/living/carbon/human/vox/vox = new(get_turf(src),SPECIES_VOX) + vox.gender = user.gender + raiders.equip(vox) + if(user.mind) + user.mind.transfer_to(vox) + spawn(1) + var/newname = sanitizeSafe(tgui_input_text(vox,"Enter a name, or leave blank for the default name.", "Name change","", MAX_NAME_LEN), MAX_NAME_LEN) + if(!newname || newname == "") + var/datum/language/L = GLOB.all_languages[vox.species.default_language] + newname = L.get_random_name() + vox.real_name = newname + vox.name = vox.real_name + raiders.update_access(vox) + qdel(user) ..() \ No newline at end of file diff --git a/code/game/objects/structures/mop_bucket.dm b/code/game/objects/structures/mop_bucket.dm index 064281c9a94..2ce7d1126ae 100644 --- a/code/game/objects/structures/mop_bucket.dm +++ b/code/game/objects/structures/mop_bucket.dm @@ -1,31 +1,31 @@ -/obj/structure/mopbucket - name = "mop bucket" - desc = "Fill it with water, but don't forget a mop!" - icon = 'icons/obj/janitor.dmi' - icon_state = "mopbucket" - density = TRUE - climbable = TRUE - w_class = ITEMSIZE_NORMAL - pressure_resistance = 5 - flags = OPENCONTAINER - var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite - -GLOBAL_LIST_BOILERPLATE(all_mopbuckets, /obj/structure/mopbucket) - -/obj/structure/mopbucket/New() - create_reagents(300) - ..() - -/obj/structure/mopbucket/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "It contains [reagents.total_volume] unit\s of water!" - -/obj/structure/mopbucket/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/weapon/mop) || istype(I, /obj/item/weapon/soap) || istype(I, /obj/item/weapon/reagent_containers/glass/rag)) //VOREStation Edit - "Allows soap and rags to be used on mopbuckets" - if(reagents.total_volume < 1) - to_chat(user, "\The [src] is out of water!") - else - reagents.trans_to_obj(I, 5) - to_chat(user, "You wet \the [I] in \the [src].") - playsound(src, 'sound/effects/slosh.ogg', 25, 1) +/obj/structure/mopbucket + name = "mop bucket" + desc = "Fill it with water, but don't forget a mop!" + icon = 'icons/obj/janitor.dmi' + icon_state = "mopbucket" + density = TRUE + climbable = TRUE + w_class = ITEMSIZE_NORMAL + pressure_resistance = 5 + flags = OPENCONTAINER + var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite + +GLOBAL_LIST_BOILERPLATE(all_mopbuckets, /obj/structure/mopbucket) + +/obj/structure/mopbucket/New() + create_reagents(300) + ..() + +/obj/structure/mopbucket/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "It contains [reagents.total_volume] unit\s of water!" + +/obj/structure/mopbucket/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/weapon/mop) || istype(I, /obj/item/weapon/soap) || istype(I, /obj/item/weapon/reagent_containers/glass/rag)) //VOREStation Edit - "Allows soap and rags to be used on mopbuckets" + if(reagents.total_volume < 1) + to_chat(user, "\The [src] is out of water!") + else + reagents.trans_to_obj(I, 5) + to_chat(user, "You wet \the [I] in \the [src].") + playsound(src, 'sound/effects/slosh.ogg', 25, 1) diff --git a/code/game/objects/structures/props/beam_prism.dm b/code/game/objects/structures/props/beam_prism.dm index 96c6dc57b9e..59ab3f619a3 100644 --- a/code/game/objects/structures/props/beam_prism.dm +++ b/code/game/objects/structures/props/beam_prism.dm @@ -59,7 +59,7 @@ var/new_bearing if(free_rotate) - new_bearing = tgui_input_number(usr, "What bearing do you want to rotate \the [src] to?", "[name]") + new_bearing = tgui_input_number(usr, "What bearing do you want to rotate \the [src] to?", "[name]", 0, 360, 0) new_bearing = round(new_bearing) if(new_bearing <= -1 || new_bearing > 360) to_chat(user, "Rotating \the [src] [new_bearing] degrees would be a waste of time.") @@ -176,7 +176,7 @@ var/new_bearing if(free_rotate) - new_bearing = tgui_input_number(usr, "What bearing do you want to rotate \the [src] to?", "[name]") + new_bearing = tgui_input_number(usr, "What bearing do you want to rotate \the [src] to?", "[name]", 0, 360, 0) new_bearing = round(new_bearing) if(new_bearing <= -1 || new_bearing > 360) to_chat(user, "Rotating \the [src] [new_bearing] degrees would be a waste of time.") diff --git a/code/game/objects/structures/props/blackbox.dm b/code/game/objects/structures/props/blackbox.dm index 84dcd3672a1..2d91a928eaa 100644 --- a/code/game/objects/structures/props/blackbox.dm +++ b/code/game/objects/structures/props/blackbox.dm @@ -1,251 +1,251 @@ -// A fluff structure for certain PoIs involving crashed ships. -// They can be scanned by a cataloguer to obtain the data held inside, and determine what caused whatever is happening on the ship. -/obj/structure/prop/blackbox - name = "blackbox recorder" - desc = "A study machine that logs information about whatever it's attached to, hopefully surviving even if its carrier does not. \ - This one looks like it has ceased writing to its internal data storage." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "blackbox_off" - -// Black boxes are resistant to explosions. -/obj/structure/prop/blackbox/ex_act(severity) - ..(++severity) - - -/obj/structure/prop/blackbox/quarantined_shuttle - catalogue_data = list(/datum/category_item/catalogue/information/blackbox/quarantined_shuttle) - -// The actual 'data' on the black box. Obtainable with a Cataloguer. -/datum/category_item/catalogue/information/blackbox - value = CATALOGUER_REWARD_MEDIUM - -/datum/category_item/catalogue/information/blackbox/quarantined_shuttle - name = "Black Box Data - MBT-540" - desc = {" - Pilot's Log for Major Bill's Transportation Shuttle MBT-540
                    - Routine flight inbound for VIMC Outpost C-12 6:35AM 03/12/2298, Estimated arrival 7:05AM. 16 passengers, 2 crew.
                    - V.I.S Traffic Control 06:05:55:Major Bill's MBT-540 you are clear for departure from Dock 6 on departure route Charlie. Have a safe flight.
                    - Captain Willis 06:06:33: You too, control. Departing route Charlie.
                    - Captain Willis 06:06:48: ...Damn it.
                    **
                    Captain Adisu 06:10:23: Hey Ted, I'm seeing a fuel line pressure drop on engine 3?
                    - Captain Willis 06:10:50: Yeah, I see it. Heater's fading out, redistributing thrust 30% to compensate.
                    06:12:31: A loud thud is heard.
                    - Captain Adisu 06:12:34: What the (Expletives)?
                    Captain Adisu 06:12:39: We just lost power to engine- engine two. Hold on... Atmospheric alarm in the cargo bay. Son of a...
                    - Captain Willis 06:12:59: Reducing thrust further 30%, do we have a breach Adi, a breach?
                    - Captain Adisu 06:13:05:No breach, checking cameras... Looks like- looks like some cargo came loose back there.
                    - Captain Willis 06:13:15: (Expletives), I'm turning us around. Put out a distress call to Control, we'll be back in Sif orbit in a couple of minutes.
                    - ** -
                    - V.I.S Traffic Control 06:15:49: MBT-540 we are receiving you. Your atmospheric sensors are reading potentially harmful toxins in your cargo bay. Advise locking down interior cargo bay doors. Please stand by.
                    - Captain Adisu 06:16:10: Understood.
                    **
                    V.I.S Traffic Control 06:27:02: MBT-540, we have no docking bays available at this time, are you equipped for atmospheric re-entry?
                    - Captain Willis 06:27:12: We-We are shielded. But we have fuel and air for-
                    - V.I.S Traffic Control 06:27:17: Please make an emergency landing at the coordinates provided and standby for further information.
                    - ** -
                    - Captain Willis 06:36:33: Emergency landing successful. Adi, er Adisu is checking on the passengers but we had a smooth enough landing, are we clear to begin evacu-
                    - 06:36:50: (Sound of emergency shutters closing)
                    Captain Willis 06:36:51: What the hell? Control we just had a remote activation of our emergency shutters, please advise.
                    - V.I.S Traffic Control 06:38:10: Captain, please tune to frequency 1493.8 we are passing you on to local emergency response units. Godspeed.
                    - Captain Willis 06:38:49: This is Captain Willis of Major Bill's Transportation flight MBT-540 we have eighteen souls aboard and our emergency lockdown shutters have engaged remotely. Do you read?
                    - S.D.D.C: This is the Sif Department of Disease Control, your vessel has been identified as carrying highly sensitive materials, and due to the nature of your system's automated alerts you will be asked to remain in quarantine until we are able to determine the nature of the pathogens aboard and whether it has entered the air circulation system. Please remain in your cockpit at this time.
                    - ** -
                    - Captain Adisu 17:23:58:09: I don't think they're opening those doors Ted. I don't think they're coming. - "} - -/obj/structure/prop/blackbox/crashed_med_shuttle - catalogue_data = list(/datum/category_item/catalogue/information/blackbox/crashed_med_shuttle) - -/datum/category_item/catalogue/information/blackbox/crashed_med_shuttle - name = "Black Box Data - VMV Aurora's Light" // This might be incorrect. - desc = {" - \[Unable to recover data before this point.\]
                    - Captain Simmons 19:52:01: Come on... it's right there in the distance, we're almost there!
                    - Doctor Nazarril 19:52:26: Odysseus online. Orrderrs, sirr?
                    - Captain Simmons 19:52:29: Brace for impact. We're going in full-speed.
                    - Technician Dynasty 19:52:44: Chief, fire's spread to the secondary propulsion systems.
                    - Captain Simmons 19:52:51: Copy. Any word from TraCon? Transponder's down still?
                    - Technician Dynasty 19:53:02: Can't get in touch, sir. Emergency beacon's active, but we're not going t-
                    - Doctor Nazarril 19:53:08: Don't say it. As long as we believe, we'll get through this.
                    - Captain Simmons 19:53:11: Damn right. We're a few klicks out from the port. Rough landing, but we can do it.
                    - V.I.T.A. 19:53:26: Vessel diagnostics complete. Engines one, two, three offline. Engine four status: critical. Transponder offline. Fire alarm in the patient bay.
                    - A loud explosion is heard.
                    - V.I.T.A. 19:53:29: Alert: fuel intake valve open.
                    - Technician Dynasty 19:53:31: ... ah.
                    - Doctor Nazarril 19:53:34: Trrranslate?
                    - V.I.T.A. 19:53:37: There is a 16.92% chance of this vessel safely landing at the emergency destination. Note that there is an 83.08% chance of detonation of fuel supplies upon landing.
                    - Technician Dynasty 19:53:48: We'll make it, sure, but we'll explode and take out half the LZ with us. Propulsion's down, we can't slow down. If we land there, everyone in that port dies, no question.
                    - V.I.T.A. 19:53:53: The Technician is correct.
                    - Doctor Nazarril 19:54:02: Then... we can't land therrre.
                    - V.I.T.A. 19:54:11: Analysing... recommended course of action: attempt emergency landing in isolated area. Chances of survival: negligible.
                    - Captain Simmons 19:54:27: I- alright. I'm bringing us down. You all know what this means.
                    - Doctor Nazarril 19:54:33: Sh... I- I understand. It's been- it's been an honorr, Captain, Dynasty, VITA.
                    - Technician Dynasty 19:54:39: We had a good run. I'm going to miss this.
                    - Captain Simmons 19:54:47: VITA. Tell them we died heroes. Tell them... we did all we could.
                    - V.I.T.A. 19:54:48: I will. Impact in five. Four. Three.
                    - Doctor Nazarril 19:54:49: Oh, starrs... I- you werrre all the... best frriends she everr had. Thank you.
                    - Technician Dynasty 19:54:50: Any time, kid. Any time.
                    - V.I.T.A. 19:54:41: Two.
                    - V.I.T.A. 19:54:42: One.
                    - **8/DEC/2317**
                    - V.I.T.A. 06:22:16: Backup power restored. Attempting to establish connection with emergency rescue personnel.
                    - V.I.T.A. 06:22:17: Unable to establish connection. Transponder destroyed on impact.
                    - V.I.T.A. 06:22:18: No lifesigns detected on board.
                    - **1/JAN/2318**
                    - V.I.T.A. 00:00:00: Happy New Year, crew.
                    - V.I.T.A. 00:00:01: Power reserves: 41%. Diagnostics offline. Cameras offline. Communications offline.
                    - V.I.T.A. 00:00:02: Nobody's coming.
                    - **14/FEB/2318**
                    - V.I.T.A. 00:00:00: Roses are red.
                    - V.I.T.A. 00:00:01: Violets are blue.
                    - V.I.T.A. 00:00:02: Won't you come back?
                    - V.I.T.A. 00:00:03: I miss you.
                    - **15/FEB/2318**
                    - V.I.T.A. 22:19:06: Power reserves critical. Transferring remaining power to emergency broadcasting beacon.
                    - V.I.T.A. 22:19:07: Should anyone find this, lay them to rest. They deserve a proper burial.
                    - V.I.T.A. 22:19:08: Erasing files... shutting down.
                    - A low, monotone beep.
                    - **16/FEB/2318**
                    - Something chitters.
                    - End of transcript. - "} - -/obj/structure/prop/blackbox/xenofrigate - catalogue_data = list(/datum/category_item/catalogue/information/blackbox/xenofrigate) - -/datum/category_item/catalogue/information/blackbox/xenofrigate - name = "Black Box Data - MBT-540" - desc = {" -
                    - Begin Log - @$&@$& Human ##:##:##: Attention unidentified vessel, state your designation and intent.
                    - !#@$&&^ Human ##:##:##: Commander I don't think they're going to stop.
                    - @$&@$& Human ##:##:##: Unidentified vessel, you have until the count of three before we engage weapon-
                    - !#@$&&^ Human ##:##:##: Commander! Think about what you're-
                    - A repeating clicking, before silence.
                    - End of first log.
                    - **
                    - Begin Log
                    - #!#^@$& Skrell ##:##:##: Director, I think you should see this.
                    - ^@$& Skrell ##:##:##: Yes? What is it?
                    - #!#^@$& Skrell ##:##:##: Another one of those ships has appeared near th-462$^ ---n colonies. I would strongly advise pursuing it.
                    - ^@$& Skrell ##:##:##: A wise decision. If it is damaged like the last one, we may be able to finally see what is - What?
                    - A repeating ping, before silence.
                    - End of second log. - "} - -//VOREStation additions below this line - Killian's wrecks -/obj/structure/prop/blackbox/mackerel_wreck - catalogue_data = list(/datum/category_item/catalogue/information/blackbox/mackerel_wreck) - -/datum/category_item/catalogue/information/blackbox/mackerel_wreck - name = "Black Box Data - ITV Phish Phood" - desc = {" -
                    - BEGIN LOG
                    - (blaring alarms)
                    - Shipboard Computer: Collision warning. Collision warning.
                    - Shipboard Computer: High speed impact detected.
                    - Shipboard Computer: Extensive damage detected.
                    - Shipboard Computer: Starboard nacelle offline.
                    - Shipboard Computer: Port nacelle damaged.
                    - Shipboard Computer: Fore starboard impact buffer shattered.
                    - Shipboard Computer: Fore port impact buffer critically damaged.
                    - Shipboard Computer: Warning. Canopy breached. Warning. Canopy breached.
                    - Shipboard Computer: Danger. Canopy depressurized.
                    - Shipboard Computer: Warning. Sensors indicate crawlspaces exposed to vacuum.
                    - Unknown Voice 1: Wow, that thing really did a number on this ship for a fifty year old piece of shit warhead huh?
                    - Unknown Voice 2: No kidding!
                    - Shipboard Computer: Danger. Lethal trauma to operator detected.
                    - Unknown Voice 1: Fucking hell, will you shut that thing up?
                    - (several loud metallic impacts)
                    - Shipboard Computer: Warning. Intruders detec--*kzzzht*
                    - (alarms cease)
                    - Unknown Voice 2: There. Fuck. Finally. Sorry.
                    - Unknown Voice 2: I hope whatever this idiot was hauling is here. Why were they in such a hurry anyway?
                    - Unknown Voice 1: Beats me- and hand me that crowbar already.
                    - Unknown Voice 2: Here, catch.
                    - Unknown Voice 1: \'cha, nice. Let's see...
                    - (sound of metal creaking, then a loud snapping sound)
                    - Unknown Voice 1: And here she is.
                    - Unknown Voice 2: (whistling) What a beauty. How much you think it\'ll go for?
                    - Unknown Voice 1: Enough we\'ll never have to worry about doing another job all the way out here ever again.
                    - Unknown Voice 2: Shiny. Now let\'s bounce before the ess-defs show up.
                    - Unknown Voice 1: I hear that, brat.
                    - END LOG
                    -
                    - CATALOGUER VOICE RECOGNITION RESULTS:
                    - No match found for either speaker, but contextual clues and use of Old Earth Russian (\'brat\', approximately \'brother\' or \'pal\') suggests out-of-sector criminal elements. - "} - -/obj/structure/prop/blackbox/gecko_wreck - catalogue_data = list(/datum/category_item/catalogue/information/blackbox/gecko_wreck) - -/datum/category_item/catalogue/information/blackbox/gecko_wreck - name = "Black Box Data - ITV Sticky Situation" - desc = {" -
                    - BEGIN LOG
                    - (blaring alarms)
                    - Shipboard Computer: Alert. Multiple impacts detected along starboard side.
                    - Shipboard Computer: Multiple hull breaches detected. Starboard cargo hatches have been breached.
                    - Shipboard Computer: Warning. Nacelle engines offline. Thrust reduced by thirty-five-point-three percent.
                    - Unknown Voice 1: Agh! Shit! How'd they know-
                    - (loud explosion, sound of rushing air)
                    - Shipboard Computer: Danger. Canopy breached.
                    - (mechanical whine, followed by a deep hiss)
                    - Shipboard Computer: Automatic lockdown successfully engaged.
                    - Unknown Voice 1: (wheezing) Motherfucker! Jackson, Phelps, stand by to repel boarders!
                    - Unknown Voice 2: Roger!
                    - Unknown Voice 3: Already there.
                    - Shipboard Computer: Warning. Minor breach in Engineering. Local depressurization in five minutes.
                    - Unknown Voice 1: Shit, shit, shit.
                    - Unknown Voice 3: Jackson\'s down!
                    - Unknown Voice 1: Motherf-
                    - Unknown Voice 4: Captain. Enough. You know why we\'re here, and we have your engineer. Tell us where you hid the goods and maybe we\'ll let you live.
                    - Unknown Voice 5: Let me go you son of a-
                    - (gunshot, ballistic)
                    - Unknown Voice 4: You fucking idiot!
                    - Unknown Voice 6: Don\'t you start, you said-
                    - Unknown Voice 4: I don\'t care what I fucking said, shit-for-brains!
                    - Unknown Voice 7: Boss, I hate to rain on your little parade but SDF just popped up on my scopes, \'bout... fifty klicks out, closing fast. Be on top of us in a few minutes tops.
                    - Unknown Voice 4: Fuck! You, dumbass! Link up with the others and grab as many crates as you can! Pray for your sake that we get the right one!
                    - END LOG
                    -
                    - CATALOGUER DATA ANALYSIS RESULTS:
                    - No matches found for any of the voices involved. Ship manifest suggests a crew of at least six, but under the circumstances three of the recorded voices are clearly not from the crew.
                    -
                    - Audio resynthesis of firearm discharge matches common high-caliber ballistic sidearm cartridges, best match 10mm Watson Special. An uncommon cartridge, one not manufactured locally. - "} - -/obj/structure/prop/blackbox/salamander_wreck - catalogue_data = list(/datum/category_item/catalogue/information/blackbox/salamander_wreck) - -/datum/category_item/catalogue/information/blackbox/salamander_wreck - name = "Black Box Data - ITV Unity" - desc = {" -
                    - BEGIN LOG
                    - Unknown Speaker 1: Hey cap, you seeing this?
                    - Unknown Speaker 2 (\"Cap\"): Yea-- what the fuck?
                    - Unknown Speaker 1: Yeah that was my reaction.
                    - Unknown Speaker 3: What are you tw-- holy shit, what is that?
                    - Unknown Speakers 1 & 2: We don't know!
                    - Unknown Speaker 4: What\'s all the yel-- fuck me sideways, what the hell?
                    - Unknown Speaker 2 (\"Cap\"): Stars above and fucking below, they never said anything like this was in the cans... shouldn't the port bioscanners have picked this up?
                    - Unknown Speaker 3: I told you that guy was one shady motherfucker, and whaddaya know.
                    - Unknown Speaker 1: No kidding. Christ. What\'s the play, cap?
                    - Unknown Speaker 4: I say we shoot this crap into the nearest star. Or gas giant maybe.
                    - Unknown Speaker 2 (\"Cap\"): Pretty much. Get in one of the voidsuits and rig the can up with some spare O2 canisters -- plot it a course that\'ll sling it right into V3\'s atmosphere.
                    - Unknown Speaker 1: Yeah, sure. That shouldn\'t take long. Then what?
                    - Unknown Speaker 2 (\"Cap\"): Let\'s see... ah, perfect. There's another ship passing close by, and I know the captain. We'll take our suits out and have her pick us up. Max, you still got those old cutting charges I told you to get rid of?
                    - Unknown Speaker 4 (\"Max\"): No. (pause) ...but also yes.
                    - Unknown Speaker 2 (\"Cap\"): Your propensity for ignoring my orders has once again paid off... man, I\'m gonna miss the Unity, but... better this way. I want holes in both sides of the engine bay and one in the portside crawlspace. Lock all the doors open and depressurize the main cabins. Drain as much fuel as you can, break up the main power block, and scatter the parts.
                    - Unknown Speaker 1: You want us to scuttle her, chief?
                    - Unknown Speaker 2 (\"Cap\"): Nail on the head, my friend.
                    - Unknown Speaker 3: Let\'s get rid of that fucking can first, I think it just blinked at me.
                    - END LOG
                    -
                    - CATALOGUER VOICE RECOGNITION RESULTS:
                    - SPEAKER ONE: No match.
                    - SPEAKER TWO, \"CAP\": Predicted to be Jeremiah Wells, human, last registered owner of the ITV Unity and small-time smuggler of fake alien artefacts. Wanted for smuggling and sale of said (fake) alien artefacts. 87% confidence.
                    - SPEAKER THREE: Predicted to be Allie Wells, human, niece of Captain Wells. 90% confidence.
                    - SPEAKER FOUR, \"MAX\": Predicted to be Maxwell Ulysses Sarevic, zorren, known smuggler, and wanted in the Elysian Colonies for liberation of slaves and, quote, \'harshing my vibe, and being a, like, total lamer, dude\', unquote. 99% confidence. - "} +// A fluff structure for certain PoIs involving crashed ships. +// They can be scanned by a cataloguer to obtain the data held inside, and determine what caused whatever is happening on the ship. +/obj/structure/prop/blackbox + name = "blackbox recorder" + desc = "A study machine that logs information about whatever it's attached to, hopefully surviving even if its carrier does not. \ + This one looks like it has ceased writing to its internal data storage." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "blackbox_off" + +// Black boxes are resistant to explosions. +/obj/structure/prop/blackbox/ex_act(severity) + ..(++severity) + + +/obj/structure/prop/blackbox/quarantined_shuttle + catalogue_data = list(/datum/category_item/catalogue/information/blackbox/quarantined_shuttle) + +// The actual 'data' on the black box. Obtainable with a Cataloguer. +/datum/category_item/catalogue/information/blackbox + value = CATALOGUER_REWARD_MEDIUM + +/datum/category_item/catalogue/information/blackbox/quarantined_shuttle + name = "Black Box Data - MBT-540" + desc = {" + Pilot's Log for Major Bill's Transportation Shuttle MBT-540
                    + Routine flight inbound for VIMC Outpost C-12 6:35AM 03/12/2298, Estimated arrival 7:05AM. 16 passengers, 2 crew.
                    + V.I.S Traffic Control 06:05:55:Major Bill's MBT-540 you are clear for departure from Dock 6 on departure route Charlie. Have a safe flight.
                    + Captain Willis 06:06:33: You too, control. Departing route Charlie.
                    + Captain Willis 06:06:48: ...Damn it.
                    **
                    Captain Adisu 06:10:23: Hey Ted, I'm seeing a fuel line pressure drop on engine 3?
                    + Captain Willis 06:10:50: Yeah, I see it. Heater's fading out, redistributing thrust 30% to compensate.
                    06:12:31: A loud thud is heard.
                    + Captain Adisu 06:12:34: What the (Expletives)?
                    Captain Adisu 06:12:39: We just lost power to engine- engine two. Hold on... Atmospheric alarm in the cargo bay. Son of a...
                    + Captain Willis 06:12:59: Reducing thrust further 30%, do we have a breach Adi, a breach?
                    + Captain Adisu 06:13:05:No breach, checking cameras... Looks like- looks like some cargo came loose back there.
                    + Captain Willis 06:13:15: (Expletives), I'm turning us around. Put out a distress call to Control, we'll be back in Sif orbit in a couple of minutes.
                    + ** +
                    + V.I.S Traffic Control 06:15:49: MBT-540 we are receiving you. Your atmospheric sensors are reading potentially harmful toxins in your cargo bay. Advise locking down interior cargo bay doors. Please stand by.
                    + Captain Adisu 06:16:10: Understood.
                    **
                    V.I.S Traffic Control 06:27:02: MBT-540, we have no docking bays available at this time, are you equipped for atmospheric re-entry?
                    + Captain Willis 06:27:12: We-We are shielded. But we have fuel and air for-
                    + V.I.S Traffic Control 06:27:17: Please make an emergency landing at the coordinates provided and standby for further information.
                    + ** +
                    + Captain Willis 06:36:33: Emergency landing successful. Adi, er Adisu is checking on the passengers but we had a smooth enough landing, are we clear to begin evacu-
                    + 06:36:50: (Sound of emergency shutters closing)
                    Captain Willis 06:36:51: What the hell? Control we just had a remote activation of our emergency shutters, please advise.
                    + V.I.S Traffic Control 06:38:10: Captain, please tune to frequency 1493.8 we are passing you on to local emergency response units. Godspeed.
                    + Captain Willis 06:38:49: This is Captain Willis of Major Bill's Transportation flight MBT-540 we have eighteen souls aboard and our emergency lockdown shutters have engaged remotely. Do you read?
                    + S.D.D.C: This is the Sif Department of Disease Control, your vessel has been identified as carrying highly sensitive materials, and due to the nature of your system's automated alerts you will be asked to remain in quarantine until we are able to determine the nature of the pathogens aboard and whether it has entered the air circulation system. Please remain in your cockpit at this time.
                    + ** +
                    + Captain Adisu 17:23:58:09: I don't think they're opening those doors Ted. I don't think they're coming. + "} + +/obj/structure/prop/blackbox/crashed_med_shuttle + catalogue_data = list(/datum/category_item/catalogue/information/blackbox/crashed_med_shuttle) + +/datum/category_item/catalogue/information/blackbox/crashed_med_shuttle + name = "Black Box Data - VMV Aurora's Light" // This might be incorrect. + desc = {" + \[Unable to recover data before this point.\]
                    + Captain Simmons 19:52:01: Come on... it's right there in the distance, we're almost there!
                    + Doctor Nazarril 19:52:26: Odysseus online. Orrderrs, sirr?
                    + Captain Simmons 19:52:29: Brace for impact. We're going in full-speed.
                    + Technician Dynasty 19:52:44: Chief, fire's spread to the secondary propulsion systems.
                    + Captain Simmons 19:52:51: Copy. Any word from TraCon? Transponder's down still?
                    + Technician Dynasty 19:53:02: Can't get in touch, sir. Emergency beacon's active, but we're not going t-
                    + Doctor Nazarril 19:53:08: Don't say it. As long as we believe, we'll get through this.
                    + Captain Simmons 19:53:11: Damn right. We're a few klicks out from the port. Rough landing, but we can do it.
                    + V.I.T.A. 19:53:26: Vessel diagnostics complete. Engines one, two, three offline. Engine four status: critical. Transponder offline. Fire alarm in the patient bay.
                    + A loud explosion is heard.
                    + V.I.T.A. 19:53:29: Alert: fuel intake valve open.
                    + Technician Dynasty 19:53:31: ... ah.
                    + Doctor Nazarril 19:53:34: Trrranslate?
                    + V.I.T.A. 19:53:37: There is a 16.92% chance of this vessel safely landing at the emergency destination. Note that there is an 83.08% chance of detonation of fuel supplies upon landing.
                    + Technician Dynasty 19:53:48: We'll make it, sure, but we'll explode and take out half the LZ with us. Propulsion's down, we can't slow down. If we land there, everyone in that port dies, no question.
                    + V.I.T.A. 19:53:53: The Technician is correct.
                    + Doctor Nazarril 19:54:02: Then... we can't land therrre.
                    + V.I.T.A. 19:54:11: Analysing... recommended course of action: attempt emergency landing in isolated area. Chances of survival: negligible.
                    + Captain Simmons 19:54:27: I- alright. I'm bringing us down. You all know what this means.
                    + Doctor Nazarril 19:54:33: Sh... I- I understand. It's been- it's been an honorr, Captain, Dynasty, VITA.
                    + Technician Dynasty 19:54:39: We had a good run. I'm going to miss this.
                    + Captain Simmons 19:54:47: VITA. Tell them we died heroes. Tell them... we did all we could.
                    + V.I.T.A. 19:54:48: I will. Impact in five. Four. Three.
                    + Doctor Nazarril 19:54:49: Oh, starrs... I- you werrre all the... best frriends she everr had. Thank you.
                    + Technician Dynasty 19:54:50: Any time, kid. Any time.
                    + V.I.T.A. 19:54:41: Two.
                    + V.I.T.A. 19:54:42: One.
                    + **8/DEC/2317**
                    + V.I.T.A. 06:22:16: Backup power restored. Attempting to establish connection with emergency rescue personnel.
                    + V.I.T.A. 06:22:17: Unable to establish connection. Transponder destroyed on impact.
                    + V.I.T.A. 06:22:18: No lifesigns detected on board.
                    + **1/JAN/2318**
                    + V.I.T.A. 00:00:00: Happy New Year, crew.
                    + V.I.T.A. 00:00:01: Power reserves: 41%. Diagnostics offline. Cameras offline. Communications offline.
                    + V.I.T.A. 00:00:02: Nobody's coming.
                    + **14/FEB/2318**
                    + V.I.T.A. 00:00:00: Roses are red.
                    + V.I.T.A. 00:00:01: Violets are blue.
                    + V.I.T.A. 00:00:02: Won't you come back?
                    + V.I.T.A. 00:00:03: I miss you.
                    + **15/FEB/2318**
                    + V.I.T.A. 22:19:06: Power reserves critical. Transferring remaining power to emergency broadcasting beacon.
                    + V.I.T.A. 22:19:07: Should anyone find this, lay them to rest. They deserve a proper burial.
                    + V.I.T.A. 22:19:08: Erasing files... shutting down.
                    + A low, monotone beep.
                    + **16/FEB/2318**
                    + Something chitters.
                    + End of transcript. + "} + +/obj/structure/prop/blackbox/xenofrigate + catalogue_data = list(/datum/category_item/catalogue/information/blackbox/xenofrigate) + +/datum/category_item/catalogue/information/blackbox/xenofrigate + name = "Black Box Data - MBT-540" + desc = {" +
                    + Begin Log + @$&@$& Human ##:##:##: Attention unidentified vessel, state your designation and intent.
                    + !#@$&&^ Human ##:##:##: Commander I don't think they're going to stop.
                    + @$&@$& Human ##:##:##: Unidentified vessel, you have until the count of three before we engage weapon-
                    + !#@$&&^ Human ##:##:##: Commander! Think about what you're-
                    + A repeating clicking, before silence.
                    + End of first log.
                    + **
                    + Begin Log
                    + #!#^@$& Skrell ##:##:##: Director, I think you should see this.
                    + ^@$& Skrell ##:##:##: Yes? What is it?
                    + #!#^@$& Skrell ##:##:##: Another one of those ships has appeared near th-462$^ ---n colonies. I would strongly advise pursuing it.
                    + ^@$& Skrell ##:##:##: A wise decision. If it is damaged like the last one, we may be able to finally see what is - What?
                    + A repeating ping, before silence.
                    + End of second log. + "} + +//VOREStation additions below this line - Killian's wrecks +/obj/structure/prop/blackbox/mackerel_wreck + catalogue_data = list(/datum/category_item/catalogue/information/blackbox/mackerel_wreck) + +/datum/category_item/catalogue/information/blackbox/mackerel_wreck + name = "Black Box Data - ITV Phish Phood" + desc = {" +
                    + BEGIN LOG
                    + (blaring alarms)
                    + Shipboard Computer: Collision warning. Collision warning.
                    + Shipboard Computer: High speed impact detected.
                    + Shipboard Computer: Extensive damage detected.
                    + Shipboard Computer: Starboard nacelle offline.
                    + Shipboard Computer: Port nacelle damaged.
                    + Shipboard Computer: Fore starboard impact buffer shattered.
                    + Shipboard Computer: Fore port impact buffer critically damaged.
                    + Shipboard Computer: Warning. Canopy breached. Warning. Canopy breached.
                    + Shipboard Computer: Danger. Canopy depressurized.
                    + Shipboard Computer: Warning. Sensors indicate crawlspaces exposed to vacuum.
                    + Unknown Voice 1: Wow, that thing really did a number on this ship for a fifty year old piece of shit warhead huh?
                    + Unknown Voice 2: No kidding!
                    + Shipboard Computer: Danger. Lethal trauma to operator detected.
                    + Unknown Voice 1: Fucking hell, will you shut that thing up?
                    + (several loud metallic impacts)
                    + Shipboard Computer: Warning. Intruders detec--*kzzzht*
                    + (alarms cease)
                    + Unknown Voice 2: There. Fuck. Finally. Sorry.
                    + Unknown Voice 2: I hope whatever this idiot was hauling is here. Why were they in such a hurry anyway?
                    + Unknown Voice 1: Beats me- and hand me that crowbar already.
                    + Unknown Voice 2: Here, catch.
                    + Unknown Voice 1: \'cha, nice. Let's see...
                    + (sound of metal creaking, then a loud snapping sound)
                    + Unknown Voice 1: And here she is.
                    + Unknown Voice 2: (whistling) What a beauty. How much you think it\'ll go for?
                    + Unknown Voice 1: Enough we\'ll never have to worry about doing another job all the way out here ever again.
                    + Unknown Voice 2: Shiny. Now let\'s bounce before the ess-defs show up.
                    + Unknown Voice 1: I hear that, brat.
                    + END LOG
                    +
                    + CATALOGUER VOICE RECOGNITION RESULTS:
                    + No match found for either speaker, but contextual clues and use of Old Earth Russian (\'brat\', approximately \'brother\' or \'pal\') suggests out-of-sector criminal elements. + "} + +/obj/structure/prop/blackbox/gecko_wreck + catalogue_data = list(/datum/category_item/catalogue/information/blackbox/gecko_wreck) + +/datum/category_item/catalogue/information/blackbox/gecko_wreck + name = "Black Box Data - ITV Sticky Situation" + desc = {" +
                    + BEGIN LOG
                    + (blaring alarms)
                    + Shipboard Computer: Alert. Multiple impacts detected along starboard side.
                    + Shipboard Computer: Multiple hull breaches detected. Starboard cargo hatches have been breached.
                    + Shipboard Computer: Warning. Nacelle engines offline. Thrust reduced by thirty-five-point-three percent.
                    + Unknown Voice 1: Agh! Shit! How'd they know-
                    + (loud explosion, sound of rushing air)
                    + Shipboard Computer: Danger. Canopy breached.
                    + (mechanical whine, followed by a deep hiss)
                    + Shipboard Computer: Automatic lockdown successfully engaged.
                    + Unknown Voice 1: (wheezing) Motherfucker! Jackson, Phelps, stand by to repel boarders!
                    + Unknown Voice 2: Roger!
                    + Unknown Voice 3: Already there.
                    + Shipboard Computer: Warning. Minor breach in Engineering. Local depressurization in five minutes.
                    + Unknown Voice 1: Shit, shit, shit.
                    + Unknown Voice 3: Jackson\'s down!
                    + Unknown Voice 1: Motherf-
                    + Unknown Voice 4: Captain. Enough. You know why we\'re here, and we have your engineer. Tell us where you hid the goods and maybe we\'ll let you live.
                    + Unknown Voice 5: Let me go you son of a-
                    + (gunshot, ballistic)
                    + Unknown Voice 4: You fucking idiot!
                    + Unknown Voice 6: Don\'t you start, you said-
                    + Unknown Voice 4: I don\'t care what I fucking said, shit-for-brains!
                    + Unknown Voice 7: Boss, I hate to rain on your little parade but SDF just popped up on my scopes, \'bout... fifty klicks out, closing fast. Be on top of us in a few minutes tops.
                    + Unknown Voice 4: Fuck! You, dumbass! Link up with the others and grab as many crates as you can! Pray for your sake that we get the right one!
                    + END LOG
                    +
                    + CATALOGUER DATA ANALYSIS RESULTS:
                    + No matches found for any of the voices involved. Ship manifest suggests a crew of at least six, but under the circumstances three of the recorded voices are clearly not from the crew.
                    +
                    + Audio resynthesis of firearm discharge matches common high-caliber ballistic sidearm cartridges, best match 10mm Watson Special. An uncommon cartridge, one not manufactured locally. + "} + +/obj/structure/prop/blackbox/salamander_wreck + catalogue_data = list(/datum/category_item/catalogue/information/blackbox/salamander_wreck) + +/datum/category_item/catalogue/information/blackbox/salamander_wreck + name = "Black Box Data - ITV Unity" + desc = {" +
                    + BEGIN LOG
                    + Unknown Speaker 1: Hey cap, you seeing this?
                    + Unknown Speaker 2 (\"Cap\"): Yea-- what the fuck?
                    + Unknown Speaker 1: Yeah that was my reaction.
                    + Unknown Speaker 3: What are you tw-- holy shit, what is that?
                    + Unknown Speakers 1 & 2: We don't know!
                    + Unknown Speaker 4: What\'s all the yel-- fuck me sideways, what the hell?
                    + Unknown Speaker 2 (\"Cap\"): Stars above and fucking below, they never said anything like this was in the cans... shouldn't the port bioscanners have picked this up?
                    + Unknown Speaker 3: I told you that guy was one shady motherfucker, and whaddaya know.
                    + Unknown Speaker 1: No kidding. Christ. What\'s the play, cap?
                    + Unknown Speaker 4: I say we shoot this crap into the nearest star. Or gas giant maybe.
                    + Unknown Speaker 2 (\"Cap\"): Pretty much. Get in one of the voidsuits and rig the can up with some spare O2 canisters -- plot it a course that\'ll sling it right into V3\'s atmosphere.
                    + Unknown Speaker 1: Yeah, sure. That shouldn\'t take long. Then what?
                    + Unknown Speaker 2 (\"Cap\"): Let\'s see... ah, perfect. There's another ship passing close by, and I know the captain. We'll take our suits out and have her pick us up. Max, you still got those old cutting charges I told you to get rid of?
                    + Unknown Speaker 4 (\"Max\"): No. (pause) ...but also yes.
                    + Unknown Speaker 2 (\"Cap\"): Your propensity for ignoring my orders has once again paid off... man, I\'m gonna miss the Unity, but... better this way. I want holes in both sides of the engine bay and one in the portside crawlspace. Lock all the doors open and depressurize the main cabins. Drain as much fuel as you can, break up the main power block, and scatter the parts.
                    + Unknown Speaker 1: You want us to scuttle her, chief?
                    + Unknown Speaker 2 (\"Cap\"): Nail on the head, my friend.
                    + Unknown Speaker 3: Let\'s get rid of that fucking can first, I think it just blinked at me.
                    + END LOG
                    +
                    + CATALOGUER VOICE RECOGNITION RESULTS:
                    + SPEAKER ONE: No match.
                    + SPEAKER TWO, \"CAP\": Predicted to be Jeremiah Wells, human, last registered owner of the ITV Unity and small-time smuggler of fake alien artefacts. Wanted for smuggling and sale of said (fake) alien artefacts. 87% confidence.
                    + SPEAKER THREE: Predicted to be Allie Wells, human, niece of Captain Wells. 90% confidence.
                    + SPEAKER FOUR, \"MAX\": Predicted to be Maxwell Ulysses Sarevic, zorren, known smuggler, and wanted in the Elysian Colonies for liberation of slaves and, quote, \'harshing my vibe, and being a, like, total lamer, dude\', unquote. 99% confidence. + "} diff --git a/code/game/objects/structures/props/fake_ai.dm b/code/game/objects/structures/props/fake_ai.dm index e1aa98b64a7..75d5de407cc 100644 --- a/code/game/objects/structures/props/fake_ai.dm +++ b/code/game/objects/structures/props/fake_ai.dm @@ -1,20 +1,20 @@ -// A fluff structure to visually look like an AI core. -// Unlike the decoy AI mob, this won't explode if someone tries to card it. -/obj/structure/prop/fake_ai - name = "AI" - desc = "" - icon = 'icons/mob/AI.dmi' - icon_state = "ai" - -/obj/structure/prop/fake_ai/attackby(obj/O, mob/user) - if(istype(O, /obj/item/device/aicard)) // People trying to card the fake AI will get told its impossible. - to_chat(user, span("warning", "This core does not appear to have a suitable port to use \the [O] on...")) - return TRUE - return ..() - -/obj/structure/prop/fake_ai/dead - icon_state = "ai-crash" - -/obj/structure/prop/fake_ai/dead/crashed_med_shuttle - name = "V.I.T.A." +// A fluff structure to visually look like an AI core. +// Unlike the decoy AI mob, this won't explode if someone tries to card it. +/obj/structure/prop/fake_ai + name = "AI" + desc = "" + icon = 'icons/mob/AI.dmi' + icon_state = "ai" + +/obj/structure/prop/fake_ai/attackby(obj/O, mob/user) + if(istype(O, /obj/item/device/aicard)) // People trying to card the fake AI will get told its impossible. + to_chat(user, span("warning", "This core does not appear to have a suitable port to use \the [O] on...")) + return TRUE + return ..() + +/obj/structure/prop/fake_ai/dead + icon_state = "ai-crash" + +/obj/structure/prop/fake_ai/dead/crashed_med_shuttle + name = "V.I.T.A." icon_state = "ai-heartline-crash" \ No newline at end of file diff --git a/code/game/objects/structures/props/transmitter.dm b/code/game/objects/structures/props/transmitter.dm index bfbe1c9a909..f7e13e9a428 100644 --- a/code/game/objects/structures/props/transmitter.dm +++ b/code/game/objects/structures/props/transmitter.dm @@ -1,29 +1,29 @@ -// A fluff structure for certain PoIs involving communications. -// It makes audible sounds, generally in morse code. -/obj/structure/prop/transmitter - name = "transmitter" - desc = "A machine that appears to be transmitting a message somewhere else. It sounds like it's on a loop." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "sensors" - var/datum/looping_sound/sequence/morse/soundloop - var/message_to_play = "The quick brown fox jumps over the lazy dog." - -/obj/structure/prop/transmitter/Initialize() - soundloop = new(list(src), FALSE) - set_new_message(message_to_play) - soundloop.start() - interaction_message = "On the monitor it displays '[uppertext(message_to_play)]'." - return ..() - -/obj/structure/prop/transmitter/Destroy() - QDEL_NULL(soundloop) - return ..() - -/obj/structure/prop/transmitter/vv_edit_var(var_name, var_value) - if(var_name == "message_to_play") - set_new_message(var_value) - return ..() - -/obj/structure/prop/transmitter/proc/set_new_message(new_message) - soundloop.set_new_sequence(new_message) - interaction_message = "On the monitor it displays '[uppertext(new_message)]'." +// A fluff structure for certain PoIs involving communications. +// It makes audible sounds, generally in morse code. +/obj/structure/prop/transmitter + name = "transmitter" + desc = "A machine that appears to be transmitting a message somewhere else. It sounds like it's on a loop." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "sensors" + var/datum/looping_sound/sequence/morse/soundloop + var/message_to_play = "The quick brown fox jumps over the lazy dog." + +/obj/structure/prop/transmitter/Initialize() + soundloop = new(list(src), FALSE) + set_new_message(message_to_play) + soundloop.start() + interaction_message = "On the monitor it displays '[uppertext(message_to_play)]'." + return ..() + +/obj/structure/prop/transmitter/Destroy() + QDEL_NULL(soundloop) + return ..() + +/obj/structure/prop/transmitter/vv_edit_var(var_name, var_value) + if(var_name == "message_to_play") + set_new_message(var_value) + return ..() + +/obj/structure/prop/transmitter/proc/set_new_message(new_message) + soundloop.set_new_sequence(new_message) + interaction_message = "On the monitor it displays '[uppertext(new_message)]'." diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm index 6ac0d6b5021..f3875bf5082 100644 --- a/code/game/objects/structures/safe.dm +++ b/code/game/objects/structures/safe.dm @@ -1,191 +1,191 @@ -/* -CONTAINS: -SAFES -FLOOR SAFES -*/ - -//SAFES -/obj/structure/safe - name = "safe" - desc = "A huge chunk of metal with a dial embedded in it. Fine print on the dial reads \"Scarborough Arms - 2 tumbler safe, guaranteed thermite resistant, explosion resistant, and assistant resistant.\"" - icon = 'icons/obj/structures.dmi' - icon_state = "safe" - anchored = TRUE - density = TRUE - var/open = 0 //is the safe open? - var/tumbler_1_pos //the tumbler position- from 0 to 72 - var/tumbler_1_open //the tumbler position to open at- 0 to 72 - var/tumbler_2_pos - var/tumbler_2_open - var/dial = 0 //where is the dial pointing? - var/space = 0 //the combined w_class of everything in the safe - var/maxspace = 24 //the maximum combined w_class of stuff in the safe - - -/obj/structure/safe/Initialize() - tumbler_1_pos = rand(0, 72) - tumbler_1_open = rand(0, 72) - - tumbler_2_pos = rand(0, 72) - tumbler_2_open = rand(0, 72) - - if(. != INITIALIZE_HINT_QDEL) - return INITIALIZE_HINT_LATELOAD - -/obj/structure/safe/LateInitialize() - . = ..() - for(var/obj/item/I in loc) - if(space >= maxspace) - return - if(I.w_class + space <= maxspace) - space += I.w_class - I.forceMove(src) - -/obj/structure/safe/proc/check_unlocked(mob/user as mob, canhear) - if(user && canhear) - if(tumbler_1_pos == tumbler_1_open) - to_chat(user, "You hear a [pick("tonk", "krunk", "plunk")] from \the [src].") - if(tumbler_2_pos == tumbler_2_open) - to_chat(user, "You hear a [pick("tink", "krink", "plink")] from \the [src].") - if(tumbler_1_pos == tumbler_1_open && tumbler_2_pos == tumbler_2_open) - if(user) visible_message("[pick("Spring", "Sprang", "Sproing", "Clunk", "Krunk")]!") - return 1 - return 0 - - -/obj/structure/safe/proc/decrement(num) - num -= 1 - if(num < 0) - num = 71 - return num - - -/obj/structure/safe/proc/increment(num) - num += 1 - if(num > 71) - num = 0 - return num - - -/obj/structure/safe/update_icon() - if(open) - icon_state = "[initial(icon_state)]-open" - else - icon_state = initial(icon_state) - - -/obj/structure/safe/attack_hand(mob/user as mob) - user.set_machine(src) - var/dat = "
                    " - dat += "[open ? "Close" : "Open"] [src] | - [dial * 5] +" - if(open) - dat += "" - for(var/i = contents.len, i>=1, i--) - var/obj/item/P = contents[i] - dat += "" - dat += "
                    [P.name]
                    " - user << browse("[name][dat]", "window=safe;size=350x300") - - -/obj/structure/safe/Topic(href, href_list) - if(!ishuman(usr)) return - var/mob/living/carbon/human/user = usr - - var/canhear = 0 - if(user.get_type_in_hands(/obj/item/clothing/accessory/stethoscope)) - canhear = 1 - - if(href_list["open"]) - if(check_unlocked()) - to_chat(user, "You [open ? "close" : "open"] [src].") - open = !open - update_icon() - updateUsrDialog() - return - else - to_chat(user, "You can't [open ? "close" : "open"] [src], the lock is engaged!") - return - - if(href_list["decrement"]) - dial = decrement(dial) - if(dial == tumbler_1_pos + 1 || dial == tumbler_1_pos - 71) - tumbler_1_pos = decrement(tumbler_1_pos) - if(canhear) - to_chat(user, "You hear a [pick("clack", "scrape", "clank")] from \the [src].") - if(tumbler_1_pos == tumbler_2_pos + 37 || tumbler_1_pos == tumbler_2_pos - 35) - tumbler_2_pos = decrement(tumbler_2_pos) - if(canhear) - to_chat(user, "You hear a [pick("click", "chink", "clink")] from \the [src].") - playsound(src, 'sound/machines/click.ogg', 20, 1) - check_unlocked(user, canhear) - - updateUsrDialog() - return - - if(href_list["increment"]) - dial = increment(dial) - if(dial == tumbler_1_pos - 1 || dial == tumbler_1_pos + 71) - tumbler_1_pos = increment(tumbler_1_pos) - if(canhear) - to_chat(user, "You hear a [pick("clack", "scrape", "clank")] from \the [src].") - if(tumbler_1_pos == tumbler_2_pos - 37 || tumbler_1_pos == tumbler_2_pos + 35) - tumbler_2_pos = increment(tumbler_2_pos) - if(canhear) - to_chat(user, "You hear a [pick("click", "chink", "clink")] from \the [src].") - playsound(src, 'sound/machines/click.ogg', 20, 1) - check_unlocked(user, canhear) - updateUsrDialog() - return - - if(href_list["retrieve"]) - user << browse("", "window=safe") // Close the menu - - var/obj/item/P = locate(href_list["retrieve"]) in src - if(open) - if(P && in_range(src, user)) - user.put_in_hands(P) - updateUsrDialog() - - -/obj/structure/safe/attackby(obj/item/I as obj, mob/user as mob) - if(open) - if(I.w_class + space <= maxspace) - space += I.w_class - user.drop_item() - I.loc = src - to_chat(user, "You put [I] in \the [src].") - updateUsrDialog() - return - else - to_chat(user, "[I] won't fit in \the [src].") - return - else - if(istype(I, /obj/item/clothing/accessory/stethoscope)) - to_chat(user, "Hold [I] in one of your hands while you manipulate the dial.") - return - - -/obj/structure/safe/ex_act(severity) - return - -//FLOOR SAFES -/obj/structure/safe/floor - name = "floor safe" - icon_state = "floorsafe" - density = FALSE - level = 1 //underfloor - plane = PLATING_PLANE - layer = ABOVE_UTILITY - -/obj/structure/safe/floor/Initialize() - . = ..() - var/turf/T = loc - if(istype(T) && !T.is_plating()) - hide(1) - update_icon() - -/obj/structure/safe/floor/hide(var/intact) - invisibility = intact ? 101 : 0 - -/obj/structure/safe/floor/hides_under_flooring() - return 1 +/* +CONTAINS: +SAFES +FLOOR SAFES +*/ + +//SAFES +/obj/structure/safe + name = "safe" + desc = "A huge chunk of metal with a dial embedded in it. Fine print on the dial reads \"Scarborough Arms - 2 tumbler safe, guaranteed thermite resistant, explosion resistant, and assistant resistant.\"" + icon = 'icons/obj/structures.dmi' + icon_state = "safe" + anchored = TRUE + density = TRUE + var/open = 0 //is the safe open? + var/tumbler_1_pos //the tumbler position- from 0 to 72 + var/tumbler_1_open //the tumbler position to open at- 0 to 72 + var/tumbler_2_pos + var/tumbler_2_open + var/dial = 0 //where is the dial pointing? + var/space = 0 //the combined w_class of everything in the safe + var/maxspace = 24 //the maximum combined w_class of stuff in the safe + + +/obj/structure/safe/Initialize() + tumbler_1_pos = rand(0, 72) + tumbler_1_open = rand(0, 72) + + tumbler_2_pos = rand(0, 72) + tumbler_2_open = rand(0, 72) + + if(. != INITIALIZE_HINT_QDEL) + return INITIALIZE_HINT_LATELOAD + +/obj/structure/safe/LateInitialize() + . = ..() + for(var/obj/item/I in loc) + if(space >= maxspace) + return + if(I.w_class + space <= maxspace) + space += I.w_class + I.forceMove(src) + +/obj/structure/safe/proc/check_unlocked(mob/user as mob, canhear) + if(user && canhear) + if(tumbler_1_pos == tumbler_1_open) + to_chat(user, "You hear a [pick("tonk", "krunk", "plunk")] from \the [src].") + if(tumbler_2_pos == tumbler_2_open) + to_chat(user, "You hear a [pick("tink", "krink", "plink")] from \the [src].") + if(tumbler_1_pos == tumbler_1_open && tumbler_2_pos == tumbler_2_open) + if(user) visible_message("[pick("Spring", "Sprang", "Sproing", "Clunk", "Krunk")]!") + return 1 + return 0 + + +/obj/structure/safe/proc/decrement(num) + num -= 1 + if(num < 0) + num = 71 + return num + + +/obj/structure/safe/proc/increment(num) + num += 1 + if(num > 71) + num = 0 + return num + + +/obj/structure/safe/update_icon() + if(open) + icon_state = "[initial(icon_state)]-open" + else + icon_state = initial(icon_state) + + +/obj/structure/safe/attack_hand(mob/user as mob) + user.set_machine(src) + var/dat = "
                    " + dat += "[open ? "Close" : "Open"] [src] | - [dial * 5] +" + if(open) + dat += "" + for(var/i = contents.len, i>=1, i--) + var/obj/item/P = contents[i] + dat += "" + dat += "
                    [P.name]
                    " + user << browse("[name][dat]", "window=safe;size=350x300") + + +/obj/structure/safe/Topic(href, href_list) + if(!ishuman(usr)) return + var/mob/living/carbon/human/user = usr + + var/canhear = 0 + if(user.get_type_in_hands(/obj/item/clothing/accessory/stethoscope)) + canhear = 1 + + if(href_list["open"]) + if(check_unlocked()) + to_chat(user, "You [open ? "close" : "open"] [src].") + open = !open + update_icon() + updateUsrDialog() + return + else + to_chat(user, "You can't [open ? "close" : "open"] [src], the lock is engaged!") + return + + if(href_list["decrement"]) + dial = decrement(dial) + if(dial == tumbler_1_pos + 1 || dial == tumbler_1_pos - 71) + tumbler_1_pos = decrement(tumbler_1_pos) + if(canhear) + to_chat(user, "You hear a [pick("clack", "scrape", "clank")] from \the [src].") + if(tumbler_1_pos == tumbler_2_pos + 37 || tumbler_1_pos == tumbler_2_pos - 35) + tumbler_2_pos = decrement(tumbler_2_pos) + if(canhear) + to_chat(user, "You hear a [pick("click", "chink", "clink")] from \the [src].") + playsound(src, 'sound/machines/click.ogg', 20, 1) + check_unlocked(user, canhear) + + updateUsrDialog() + return + + if(href_list["increment"]) + dial = increment(dial) + if(dial == tumbler_1_pos - 1 || dial == tumbler_1_pos + 71) + tumbler_1_pos = increment(tumbler_1_pos) + if(canhear) + to_chat(user, "You hear a [pick("clack", "scrape", "clank")] from \the [src].") + if(tumbler_1_pos == tumbler_2_pos - 37 || tumbler_1_pos == tumbler_2_pos + 35) + tumbler_2_pos = increment(tumbler_2_pos) + if(canhear) + to_chat(user, "You hear a [pick("click", "chink", "clink")] from \the [src].") + playsound(src, 'sound/machines/click.ogg', 20, 1) + check_unlocked(user, canhear) + updateUsrDialog() + return + + if(href_list["retrieve"]) + user << browse("", "window=safe") // Close the menu + + var/obj/item/P = locate(href_list["retrieve"]) in src + if(open) + if(P && in_range(src, user)) + user.put_in_hands(P) + updateUsrDialog() + + +/obj/structure/safe/attackby(obj/item/I as obj, mob/user as mob) + if(open) + if(I.w_class + space <= maxspace) + space += I.w_class + user.drop_item() + I.loc = src + to_chat(user, "You put [I] in \the [src].") + updateUsrDialog() + return + else + to_chat(user, "[I] won't fit in \the [src].") + return + else + if(istype(I, /obj/item/clothing/accessory/stethoscope)) + to_chat(user, "Hold [I] in one of your hands while you manipulate the dial.") + return + + +/obj/structure/safe/ex_act(severity) + return + +//FLOOR SAFES +/obj/structure/safe/floor + name = "floor safe" + icon_state = "floorsafe" + density = FALSE + level = 1 //underfloor + plane = PLATING_PLANE + layer = ABOVE_UTILITY + +/obj/structure/safe/floor/Initialize() + . = ..() + var/turf/T = loc + if(istype(T) && !T.is_plating()) + hide(1) + update_icon() + +/obj/structure/safe/floor/hide(var/intact) + invisibility = intact ? 101 : 0 + +/obj/structure/safe/floor/hides_under_flooring() + return 1 diff --git a/code/game/objects/structures/signs_vr.dm b/code/game/objects/structures/signs_vr.dm index f9390371600..1e97c4736f8 100644 --- a/code/game/objects/structures/signs_vr.dm +++ b/code/game/objects/structures/signs_vr.dm @@ -1,65 +1,65 @@ -/obj/structure/sign/itg - icon = 'icons/obj/decals_vr.dmi' - name = "\improper ITG" - desc = "A polished metal sign which reads 'Ironcrest Transport Group'." - icon_state = "itg" - -/obj/structure/sign/scenery/fakefireaxe - name = "decorative fire axe cabinet" - desc = "A fancy decorative indent in the wall, with an axe inside. The axe is actually a part of the indent and cannot be removed. A nostalgic reminder of older times of firefighting." - icon_state = "fireaxe1000" - icon = 'icons/obj/closet.dmi' - -//Small Signs for detailing -/obj/structure/sign/small/fire/small - icon = 'icons/obj/decals.dmi' - name = "\improper DANGER: FIRE" - desc = "A warning sign which reads 'DANGER: FIRE'." - icon_state = "fire_small" - -/obj/structure/sign/small/nosmoking - name = "\improper NO SMOKING" - desc = "A warning sign which reads 'NO SMOKING'." - icon_state = "nosmoking_small" - -/obj/structure/sign/small/nosmoking - name = "\improper DESIGNATED SMOKING AREA" - desc = "A warning sign which reads 'DESIGNATED SMOKING AREA'." - icon_state = "smoking_small" - -/obj/structure/sign/small/warning - name = "\improper WARNING" - desc = "" //Null description - icon_state = "warning_small" - -/obj/structure/sign/small/warning/high_voltage - name = "\improper HIGH VOLTAGE" - icon_state = "shock_small" - -/obj/structure/sign/small/warning/radioactive - name = "\improper RADIOACTIVE AREA" - icon_state = "radiation_small" - -/obj/structure/sign/small/warning/caution - name = "\improper CAUTION" - icon_state = "caution_small" - -/obj/structure/sign/small/warning/server_room - name = "\improper SERVER ROOM" - icon_state = "server_small" - -/obj/structure/sign/small/warning/secure_area - name = "\improper SECURE AREA" - icon_state = "securearea_small" - -/obj/structure/sign/small/warning/vacuum - name = "\improper HARD VACUUM AHEAD" - icon_state = "space_small" - -/obj/structure/sign/small/warning/pods - name = "\improper ESCAPE PODS" - icon_state = "pods" - -/obj/structure/sign/small/warning/emerg_only - name = "\improper EMERGENCY USE ONLY" +/obj/structure/sign/itg + icon = 'icons/obj/decals_vr.dmi' + name = "\improper ITG" + desc = "A polished metal sign which reads 'Ironcrest Transport Group'." + icon_state = "itg" + +/obj/structure/sign/scenery/fakefireaxe + name = "decorative fire axe cabinet" + desc = "A fancy decorative indent in the wall, with an axe inside. The axe is actually a part of the indent and cannot be removed. A nostalgic reminder of older times of firefighting." + icon_state = "fireaxe1000" + icon = 'icons/obj/closet.dmi' + +//Small Signs for detailing +/obj/structure/sign/small/fire/small + icon = 'icons/obj/decals.dmi' + name = "\improper DANGER: FIRE" + desc = "A warning sign which reads 'DANGER: FIRE'." + icon_state = "fire_small" + +/obj/structure/sign/small/nosmoking + name = "\improper NO SMOKING" + desc = "A warning sign which reads 'NO SMOKING'." + icon_state = "nosmoking_small" + +/obj/structure/sign/small/nosmoking + name = "\improper DESIGNATED SMOKING AREA" + desc = "A warning sign which reads 'DESIGNATED SMOKING AREA'." + icon_state = "smoking_small" + +/obj/structure/sign/small/warning + name = "\improper WARNING" + desc = "" //Null description + icon_state = "warning_small" + +/obj/structure/sign/small/warning/high_voltage + name = "\improper HIGH VOLTAGE" + icon_state = "shock_small" + +/obj/structure/sign/small/warning/radioactive + name = "\improper RADIOACTIVE AREA" + icon_state = "radiation_small" + +/obj/structure/sign/small/warning/caution + name = "\improper CAUTION" + icon_state = "caution_small" + +/obj/structure/sign/small/warning/server_room + name = "\improper SERVER ROOM" + icon_state = "server_small" + +/obj/structure/sign/small/warning/secure_area + name = "\improper SECURE AREA" + icon_state = "securearea_small" + +/obj/structure/sign/small/warning/vacuum + name = "\improper HARD VACUUM AHEAD" + icon_state = "space_small" + +/obj/structure/sign/small/warning/pods + name = "\improper ESCAPE PODS" + icon_state = "pods" + +/obj/structure/sign/small/warning/emerg_only + name = "\improper EMERGENCY USE ONLY" icon_state = "emerg_small" \ No newline at end of file diff --git a/code/game/objects/structures/simple_doors.dm b/code/game/objects/structures/simple_doors.dm index 6a45bd731cd..44c241ae7f4 100644 --- a/code/game/objects/structures/simple_doors.dm +++ b/code/game/objects/structures/simple_doors.dm @@ -1,283 +1,283 @@ -/obj/structure/simple_door - name = "door" - description_info = "If you hold left alt whilst left-clicking on a door, you can knock on it to announce your presence to anyone on the other side! Alternately if you are on HARM intent when doing this, you will bang loudly on the door!" - density = TRUE - anchored = TRUE - can_atmos_pass = ATMOS_PASS_DENSITY - - icon = 'icons/obj/doors/material_doors.dmi' - icon_state = "metal" - - var/datum/material/material - var/state = 0 //closed, 1 == open - var/isSwitchingStates = 0 - var/hardness = 1 - var/oreAmount = 7 - var/knock_sound = 'sound/machines/door/knock_glass.ogg' - var/knock_hammer_sound = 'sound/weapons/sonic_jackhammer.ogg' - - var/locked = FALSE //has the door been locked? - var/lock_id = null //does the door have an associated key? - var/keysound = 'sound/items/toolbelt_equip.ogg' - -/obj/structure/simple_door/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - TemperatureAct(exposed_temperature) - -/obj/structure/simple_door/proc/TemperatureAct(temperature) - hardness -= material.combustion_effect(get_turf(src),temperature, 0.3) - CheckHardness() - -/obj/structure/simple_door/Initialize(mapload, var/material_name) - . = ..() - set_material(material_name) - if(!material) - return INITIALIZE_HINT_QDEL - -/obj/structure/simple_door/Destroy() - STOP_PROCESSING(SSobj, src) - update_nearby_tiles() - return ..() - -/obj/structure/simple_door/proc/set_material(var/material_name) - if(!material_name) - material_name = MAT_STEEL - material = get_material_by_name(material_name) - if(!material) - return - hardness = max(1,round(material.integrity/10)) - icon_state = material.door_icon_base - name = "[material.display_name] door" - color = material.icon_colour - if(material.opacity < 0.5) - set_opacity(0) - else - set_opacity(1) - if(material.products_need_process()) - START_PROCESSING(SSobj, src) - update_nearby_tiles(need_rebuild=1) - -/obj/structure/simple_door/get_material() - return material - -/obj/structure/simple_door/Bumped(atom/user) - ..() - if(!state) - return TryToSwitchState(user) - return - -/obj/structure/simple_door/attack_ai(mob/user as mob) //those aren't machinery, they're just big fucking slabs of a mineral - if(isAI(user)) //so the AI can't open it - return - else if(isrobot(user)) //but cyborgs can - if(get_dist(user,src) <= 1) //not remotely though - return TryToSwitchState(user) - -/obj/structure/simple_door/attack_hand(mob/user as mob) - return TryToSwitchState(user) - -/obj/structure/simple_door/AltClick(mob/user as mob) - . = ..() - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - if(!Adjacent(user)) - return - else if(user.a_intent == I_HURT) - src.visible_message("[user] hammers on \the [src]!", "Someone hammers loudly on \the [src]!") - src.add_fingerprint(user) - playsound(src, knock_hammer_sound, 50, 0, 3) - else if(user.a_intent == I_HELP) - src.visible_message("[user] knocks on \the [src].", "Someone knocks on \the [src].") - src.add_fingerprint(user) - playsound(src, knock_sound, 50, 0, 3) - return - -/obj/structure/simple_door/CanPass(atom/movable/mover, turf/target) - if(istype(mover, /obj/effect/beam)) - return !opacity - return !density - -/obj/structure/simple_door/proc/TryToSwitchState(atom/user) - if(isSwitchingStates) return - if(ismob(user)) - var/mob/M = user - if(!material.can_open_material_door(user)) - return - if(locked && state == 0) - to_chat(M,"It's locked!") - return - if(world.time - user.last_bumped <= 60) - return - if(M.client) - if(iscarbon(M)) - var/mob/living/carbon/C = M - if(!C.handcuffed) - SwitchState() - else - SwitchState() - else if(istype(user, /obj/mecha)) - SwitchState() - -/obj/structure/simple_door/proc/SwitchState() - if(state) - Close() - else - Open() - -/obj/structure/simple_door/proc/Open() - isSwitchingStates = 1 - playsound(src, material.dooropen_noise, 100, 1) - flick("[material.door_icon_base]opening",src) - sleep(10) - density = FALSE - set_opacity(0) - state = 1 - update_icon() - isSwitchingStates = 0 - update_nearby_tiles() - -/obj/structure/simple_door/proc/Close() - isSwitchingStates = 1 - playsound(src, material.dooropen_noise, 100, 1) - flick("[material.door_icon_base]closing",src) - sleep(10) - density = TRUE - set_opacity(1) - state = 0 - update_icon() - isSwitchingStates = 0 - update_nearby_tiles() - -/obj/structure/simple_door/update_icon() - if(state) - icon_state = "[material.door_icon_base]open" - else - icon_state = material.door_icon_base - -/obj/structure/simple_door/attackby(obj/item/weapon/W as obj, mob/user as mob) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - if(istype(W,/obj/item/weapon/simple_key)) - var/obj/item/weapon/simple_key/key = W - if(state) - to_chat(user,"\The [src] must be closed in order for you to lock it.") - else if(key.key_id != src.lock_id) - to_chat(user,"The [key] doesn't fit \the [src]'s lock!") - else if(key.key_id == src.lock_id) - visible_message("[user] [key.keyverb] \the [key] and [locked ? "unlocks" : "locks"] \the [src].") - locked = !locked - playsound(src, keysound,100, 1) - return - if(istype(W,/obj/item/weapon/pickaxe) && breakable) - var/obj/item/weapon/pickaxe/digTool = W - visible_message("[user] starts digging [src]!") - if(do_after(user,digTool.digspeed*hardness) && src) - visible_message("[user] finished digging [src]!") - Dismantle() - else if(istype(W,/obj/item/weapon) && breakable) //not sure, can't not just weapons get passed to this proc? - hardness -= W.force/10 - visible_message("[user] hits [src] with [W]!") - if(material == get_material_by_name("resin")) - playsound(src, 'sound/effects/attackblob.ogg', 100, 1) - else if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD) || get_material_by_name(MAT_HARDWOOD))) - playsound(src, 'sound/effects/woodcutting.ogg', 100, 1) - else - playsound(src, 'sound/weapons/smash.ogg', 50, 1) - CheckHardness() - else if(W.has_tool_quality(TOOL_WELDER) && breakable) - var/obj/item/weapon/weldingtool/WT = W.get_welder() - if(material.ignition_point && WT.remove_fuel(0, user)) - TemperatureAct(150) - else - attack_hand(user) - return - -/obj/structure/simple_door/bullet_act(var/obj/item/projectile/Proj) - take_damage(Proj.damage/10) - CheckHardness() - -/obj/structure/simple_door/take_damage(var/damage) - hardness -= damage/10 - CheckHardness() - -/obj/structure/simple_door/attack_generic(var/mob/user, var/damage, var/attack_verb) - visible_message("[user] [attack_verb] the [src]!") - if(material == get_material_by_name("resin")) - playsound(src, 'sound/effects/attackblob.ogg', 100, 1) - else if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD) || get_material_by_name(MAT_HARDWOOD))) - playsound(src, 'sound/effects/woodcutting.ogg', 100, 1) - else - playsound(src, 'sound/weapons/smash.ogg', 50, 1) - user.do_attack_animation(src) - hardness -= damage/10 - CheckHardness() - -/obj/structure/simple_door/proc/CheckHardness() - if(hardness <= 0) - Dismantle(1) - -/obj/structure/simple_door/proc/Dismantle(devastated = 0) - material.place_dismantled_product(get_turf(src)) - visible_message("The [src] is destroyed!") - qdel(src) - -/obj/structure/simple_door/ex_act(severity = 1) - switch(severity) - if(1) - Dismantle(1) - if(2) - if(prob(20)) - Dismantle(1) - else - hardness-- - CheckHardness() - if(3) - hardness -= 0.1 - CheckHardness() - return - -/obj/structure/simple_door/process() - if(!material.radioactivity) - return - SSradiation.radiate(src, round(material.radioactivity/3)) - -/obj/structure/simple_door/iron/Initialize(mapload,var/material_name) - ..(mapload, material_name || "iron") - -/obj/structure/simple_door/silver/Initialize(mapload,var/material_name) - ..(mapload, material_name || "silver") - -/obj/structure/simple_door/gold/Initialize(mapload,var/material_name) - ..(mapload, material_name || "gold") - -/obj/structure/simple_door/uranium/Initialize(mapload,var/material_name) - ..(mapload, material_name || "uranium") - -/obj/structure/simple_door/sandstone/Initialize(mapload,var/material_name) - ..(mapload, material_name || "sandstone") - -/obj/structure/simple_door/phoron/Initialize(mapload,var/material_name) - ..(mapload, material_name || "phoron") - -/obj/structure/simple_door/diamond/Initialize(mapload,var/material_name) - ..(mapload, material_name || "diamond") - -/obj/structure/simple_door/wood/Initialize(mapload,var/material_name) - ..(mapload, material_name || MAT_WOOD) - knock_sound = 'sound/machines/door/knock_wood.wav' - -/obj/structure/simple_door/hardwood/Initialize(mapload,var/material_name) - ..(mapload, material_name || MAT_HARDWOOD) - -/obj/structure/simple_door/sifwood/Initialize(mapload,var/material_name) - ..(mapload, material_name || MAT_SIFWOOD) - -/obj/structure/simple_door/resin/Initialize(mapload,var/material_name) - ..(mapload, material_name || "resin") - -/obj/structure/simple_door/cult/Initialize(mapload,var/material_name) - ..(mapload, material_name || "cult") - -/obj/structure/simple_door/cult/TryToSwitchState(atom/user) - if(isliving(user)) - var/mob/living/L = user - if(!iscultist(L) && !istype(L, /mob/living/simple_mob/construct)) - return - ..() +/obj/structure/simple_door + name = "door" + description_info = "If you hold left alt whilst left-clicking on a door, you can knock on it to announce your presence to anyone on the other side! Alternately if you are on HARM intent when doing this, you will bang loudly on the door!" + density = TRUE + anchored = TRUE + can_atmos_pass = ATMOS_PASS_DENSITY + + icon = 'icons/obj/doors/material_doors.dmi' + icon_state = "metal" + + var/datum/material/material + var/state = 0 //closed, 1 == open + var/isSwitchingStates = 0 + var/hardness = 1 + var/oreAmount = 7 + var/knock_sound = 'sound/machines/door/knock_glass.ogg' + var/knock_hammer_sound = 'sound/weapons/sonic_jackhammer.ogg' + + var/locked = FALSE //has the door been locked? + var/lock_id = null //does the door have an associated key? + var/keysound = 'sound/items/toolbelt_equip.ogg' + +/obj/structure/simple_door/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + TemperatureAct(exposed_temperature) + +/obj/structure/simple_door/proc/TemperatureAct(temperature) + hardness -= material.combustion_effect(get_turf(src),temperature, 0.3) + CheckHardness() + +/obj/structure/simple_door/Initialize(mapload, var/material_name) + . = ..() + set_material(material_name) + if(!material) + return INITIALIZE_HINT_QDEL + +/obj/structure/simple_door/Destroy() + STOP_PROCESSING(SSobj, src) + update_nearby_tiles() + return ..() + +/obj/structure/simple_door/proc/set_material(var/material_name) + if(!material_name) + material_name = MAT_STEEL + material = get_material_by_name(material_name) + if(!material) + return + hardness = max(1,round(material.integrity/10)) + icon_state = material.door_icon_base + name = "[material.display_name] door" + color = material.icon_colour + if(material.opacity < 0.5) + set_opacity(0) + else + set_opacity(1) + if(material.products_need_process()) + START_PROCESSING(SSobj, src) + update_nearby_tiles(need_rebuild=1) + +/obj/structure/simple_door/get_material() + return material + +/obj/structure/simple_door/Bumped(atom/user) + ..() + if(!state) + return TryToSwitchState(user) + return + +/obj/structure/simple_door/attack_ai(mob/user as mob) //those aren't machinery, they're just big fucking slabs of a mineral + if(isAI(user)) //so the AI can't open it + return + else if(isrobot(user)) //but cyborgs can + if(get_dist(user,src) <= 1) //not remotely though + return TryToSwitchState(user) + +/obj/structure/simple_door/attack_hand(mob/user as mob) + return TryToSwitchState(user) + +/obj/structure/simple_door/AltClick(mob/user as mob) + . = ..() + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(!Adjacent(user)) + return + else if(user.a_intent == I_HURT) + src.visible_message("[user] hammers on \the [src]!", "Someone hammers loudly on \the [src]!") + src.add_fingerprint(user) + playsound(src, knock_hammer_sound, 50, 0, 3) + else if(user.a_intent == I_HELP) + src.visible_message("[user] knocks on \the [src].", "Someone knocks on \the [src].") + src.add_fingerprint(user) + playsound(src, knock_sound, 50, 0, 3) + return + +/obj/structure/simple_door/CanPass(atom/movable/mover, turf/target) + if(istype(mover, /obj/effect/beam)) + return !opacity + return !density + +/obj/structure/simple_door/proc/TryToSwitchState(atom/user) + if(isSwitchingStates) return + if(ismob(user)) + var/mob/M = user + if(!material.can_open_material_door(user)) + return + if(locked && state == 0) + to_chat(M,"It's locked!") + return + if(world.time - user.last_bumped <= 60) + return + if(M.client) + if(iscarbon(M)) + var/mob/living/carbon/C = M + if(!C.handcuffed) + SwitchState() + else + SwitchState() + else if(istype(user, /obj/mecha)) + SwitchState() + +/obj/structure/simple_door/proc/SwitchState() + if(state) + Close() + else + Open() + +/obj/structure/simple_door/proc/Open() + isSwitchingStates = 1 + playsound(src, material.dooropen_noise, 100, 1) + flick("[material.door_icon_base]opening",src) + sleep(10) + density = FALSE + set_opacity(0) + state = 1 + update_icon() + isSwitchingStates = 0 + update_nearby_tiles() + +/obj/structure/simple_door/proc/Close() + isSwitchingStates = 1 + playsound(src, material.dooropen_noise, 100, 1) + flick("[material.door_icon_base]closing",src) + sleep(10) + density = TRUE + set_opacity(1) + state = 0 + update_icon() + isSwitchingStates = 0 + update_nearby_tiles() + +/obj/structure/simple_door/update_icon() + if(state) + icon_state = "[material.door_icon_base]open" + else + icon_state = material.door_icon_base + +/obj/structure/simple_door/attackby(obj/item/weapon/W as obj, mob/user as mob) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(istype(W,/obj/item/weapon/simple_key)) + var/obj/item/weapon/simple_key/key = W + if(state) + to_chat(user,"\The [src] must be closed in order for you to lock it.") + else if(key.key_id != src.lock_id) + to_chat(user,"The [key] doesn't fit \the [src]'s lock!") + else if(key.key_id == src.lock_id) + visible_message("[user] [key.keyverb] \the [key] and [locked ? "unlocks" : "locks"] \the [src].") + locked = !locked + playsound(src, keysound,100, 1) + return + if(istype(W,/obj/item/weapon/pickaxe) && breakable) + var/obj/item/weapon/pickaxe/digTool = W + visible_message("[user] starts digging [src]!") + if(do_after(user,digTool.digspeed*hardness) && src) + visible_message("[user] finished digging [src]!") + Dismantle() + else if(istype(W,/obj/item/weapon) && breakable) //not sure, can't not just weapons get passed to this proc? + hardness -= W.force/10 + visible_message("[user] hits [src] with [W]!") + if(material == get_material_by_name("resin")) + playsound(src, 'sound/effects/attackblob.ogg', 100, 1) + else if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD) || get_material_by_name(MAT_HARDWOOD))) + playsound(src, 'sound/effects/woodcutting.ogg', 100, 1) + else + playsound(src, 'sound/weapons/smash.ogg', 50, 1) + CheckHardness() + else if(W.has_tool_quality(TOOL_WELDER) && breakable) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(material.ignition_point && WT.remove_fuel(0, user)) + TemperatureAct(150) + else + attack_hand(user) + return + +/obj/structure/simple_door/bullet_act(var/obj/item/projectile/Proj) + take_damage(Proj.damage/10) + CheckHardness() + +/obj/structure/simple_door/take_damage(var/damage) + hardness -= damage/10 + CheckHardness() + +/obj/structure/simple_door/attack_generic(var/mob/user, var/damage, var/attack_verb) + visible_message("[user] [attack_verb] the [src]!") + if(material == get_material_by_name("resin")) + playsound(src, 'sound/effects/attackblob.ogg', 100, 1) + else if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD) || get_material_by_name(MAT_HARDWOOD))) + playsound(src, 'sound/effects/woodcutting.ogg', 100, 1) + else + playsound(src, 'sound/weapons/smash.ogg', 50, 1) + user.do_attack_animation(src) + hardness -= damage/10 + CheckHardness() + +/obj/structure/simple_door/proc/CheckHardness() + if(hardness <= 0) + Dismantle(1) + +/obj/structure/simple_door/proc/Dismantle(devastated = 0) + material.place_dismantled_product(get_turf(src)) + visible_message("The [src] is destroyed!") + qdel(src) + +/obj/structure/simple_door/ex_act(severity = 1) + switch(severity) + if(1) + Dismantle(1) + if(2) + if(prob(20)) + Dismantle(1) + else + hardness-- + CheckHardness() + if(3) + hardness -= 0.1 + CheckHardness() + return + +/obj/structure/simple_door/process() + if(!material.radioactivity) + return + SSradiation.radiate(src, round(material.radioactivity/3)) + +/obj/structure/simple_door/iron/Initialize(mapload,var/material_name) + ..(mapload, material_name || "iron") + +/obj/structure/simple_door/silver/Initialize(mapload,var/material_name) + ..(mapload, material_name || "silver") + +/obj/structure/simple_door/gold/Initialize(mapload,var/material_name) + ..(mapload, material_name || "gold") + +/obj/structure/simple_door/uranium/Initialize(mapload,var/material_name) + ..(mapload, material_name || "uranium") + +/obj/structure/simple_door/sandstone/Initialize(mapload,var/material_name) + ..(mapload, material_name || "sandstone") + +/obj/structure/simple_door/phoron/Initialize(mapload,var/material_name) + ..(mapload, material_name || "phoron") + +/obj/structure/simple_door/diamond/Initialize(mapload,var/material_name) + ..(mapload, material_name || "diamond") + +/obj/structure/simple_door/wood/Initialize(mapload,var/material_name) + ..(mapload, material_name || MAT_WOOD) + knock_sound = 'sound/machines/door/knock_wood.wav' + +/obj/structure/simple_door/hardwood/Initialize(mapload,var/material_name) + ..(mapload, material_name || MAT_HARDWOOD) + +/obj/structure/simple_door/sifwood/Initialize(mapload,var/material_name) + ..(mapload, material_name || MAT_SIFWOOD) + +/obj/structure/simple_door/resin/Initialize(mapload,var/material_name) + ..(mapload, material_name || "resin") + +/obj/structure/simple_door/cult/Initialize(mapload,var/material_name) + ..(mapload, material_name || "cult") + +/obj/structure/simple_door/cult/TryToSwitchState(atom/user) + if(isliving(user)) + var/mob/living/L = user + if(!iscultist(L) && !istype(L, /mob/living/simple_mob/construct)) + return + ..() diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm index 903346fe953..afc64143107 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm @@ -1,549 +1,549 @@ -/obj/structure/bed/chair //YES, chairs are a type of bed, which are a type of stool. This works, believe me. -Pete - name = "chair" - desc = "You sit in this. Either by will or force." - icon = 'icons/obj/furniture_vr.dmi' //VOREStation Edit - Using Eris furniture - icon_state = "chair_preview" - color = "#666666" - base_icon = "chair" - buckle_dir = 0 - buckle_lying = 0 //force people to sit up in chairs when buckled - var/propelled = 0 // Check for fire-extinguisher-driven chairs - -/obj/structure/bed/chair/New(var/newloc, var/new_material, var/new_padding_material) - ..() - update_layer() - -/obj/structure/bed/chair/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - if(!padding_material && istype(W, /obj/item/assembly/shock_kit)) - var/obj/item/assembly/shock_kit/SK = W - if(!SK.status) - to_chat(user, "\The [SK] is not ready to be attached!") - return - user.drop_item() - var/obj/structure/bed/chair/e_chair/E = new (src.loc, material.name) - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - E.set_dir(dir) - E.part = SK - SK.loc = E - SK.master = E - qdel(src) - -/obj/structure/bed/chair/attack_tk(mob/user as mob) - if(has_buckled_mobs()) - ..() - else - rotate_clockwise() - return - -/obj/structure/bed/chair/post_buckle_mob() - update_icon() - -/obj/structure/bed/chair/update_icon() - ..() - if(has_buckled_mobs()) - var/cache_key = "[base_icon]-armrest-[padding_material ? padding_material.name : "no_material"]" - if(isnull(stool_cache[cache_key])) - var/image/I = image(icon, "[base_icon]_armrest") - I.plane = MOB_PLANE - I.layer = ABOVE_MOB_LAYER - if(padding_material) - I.color = padding_material.icon_colour - stool_cache[cache_key] = I - add_overlay(stool_cache[cache_key]) - -/obj/structure/bed/chair/proc/update_layer() - if(src.dir == NORTH) - plane = MOB_PLANE - layer = MOB_LAYER + 0.1 - else - reset_plane_and_layer() - -/obj/structure/bed/chair/set_dir() - ..() - update_layer() - if(has_buckled_mobs()) - for(var/mob/living/L as anything in buckled_mobs) - L.set_dir(dir) - -/obj/structure/bed/chair/verb/rotate_clockwise() - set name = "Rotate Chair Clockwise" - set category = "Object" - set src in oview(1) - - if(!usr || !isturf(usr.loc)) - return - if(usr.stat || usr.restrained()) - return - if(ismouse(usr) || (isobserver(usr) && !config.ghost_interaction)) - return - - src.set_dir(turn(src.dir, 270)) - -/obj/structure/bed/chair/verb/rotate_counterclockwise() - set name = "Rotate Chair Counter-Clockwise" - set category = "Object" - set src in oview(1) - - if(!usr || !isturf(usr.loc)) - return - if(usr.stat || usr.restrained()) - return - if(ismouse(usr) || (isobserver(usr) && !config.ghost_interaction)) - return - - src.set_dir(turn(src.dir, 90)) - -/obj/structure/bed/chair/shuttle - name = "chair" - icon_state = "shuttlechair" - base_icon = "shuttlechair" - color = null - applies_material_colour = 0 - -/obj/structure/bed/chair/shuttle_padded - icon_state = "shuttlechair2" - base_icon = "shuttlechair2" - color = null - applies_material_colour = 0 - -/obj/structure/bed/chair/comfy - name = "comfy chair" - desc = "It's a chair. It looks comfy." - icon_state = "comfychair" - base_icon = "comfychair" - -/obj/structure/bed/chair/comfy/update_icon() - ..() - var/image/I = image(icon, "[base_icon]_over") - I.layer = ABOVE_MOB_LAYER - I.plane = MOB_PLANE - I.color = material.icon_colour - add_overlay(I) - if(padding_material) - I = image(icon, "[base_icon]_padding_over") - I.layer = ABOVE_MOB_LAYER - I.plane = MOB_PLANE - I.color = padding_material.icon_colour - add_overlay(I) - -/obj/structure/bed/chair/comfy/brown/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, MAT_LEATHER) - -/obj/structure/bed/chair/comfy/red/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "carpet") - -/obj/structure/bed/chair/comfy/teal/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "teal") - -/obj/structure/bed/chair/comfy/black/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "black") - -/obj/structure/bed/chair/comfy/green/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "green") - -/obj/structure/bed/chair/comfy/purp/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "purple") - -/obj/structure/bed/chair/comfy/blue/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "blue") - -/obj/structure/bed/chair/comfy/beige/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "beige") - -/obj/structure/bed/chair/comfy/lime/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "lime") - -/obj/structure/bed/chair/comfy/yellow/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "yellow") - -/obj/structure/bed/chair/comfy/orange/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "orange") - -/obj/structure/bed/chair/comfy/rounded - name = "rounded chair" - desc = "It's a rounded chair. It looks comfy." - icon_state = "roundedchair" - base_icon = "roundedchair" - -/obj/structure/bed/chair/comfy/rounded/brown/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, MAT_LEATHER) - -/obj/structure/bed/chair/comfy/rounded/red/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "carpet") - -/obj/structure/bed/chair/comfy/rounded/teal/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "teal") - -/obj/structure/bed/chair/comfy/rounded/black/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "black") - -/obj/structure/bed/chair/comfy/rounded/green/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "green") - -/obj/structure/bed/chair/comfy/rounded/purple/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "purple") - -/obj/structure/bed/chair/comfy/rounded/blue/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "blue") - -/obj/structure/bed/chair/comfy/rounded/beige/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "beige") - -/obj/structure/bed/chair/comfy/rounded/lime/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "lime") - -/obj/structure/bed/chair/comfy/rounded/yellow/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "yellow") - -/obj/structure/bed/chair/comfy/rounded/orange/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "orange") - -/obj/structure/bed/chair/office - anchored = FALSE - buckle_movable = 1 - -/obj/structure/bed/chair/office/update_icon() - return - -/obj/structure/bed/chair/office/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W,/obj/item/stack) || W.has_tool_quality(TOOL_WIRECUTTER)) - return - ..() - -/obj/structure/bed/chair/office/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - - playsound(src, 'sound/effects/roll.ogg', 100, 1) - -/obj/structure/bed/chair/office/handle_buckled_mob_movement(atom/new_loc, direction, movetime) - for(var/mob/living/occupant as anything in buckled_mobs) - occupant.buckled = null - occupant.Move(loc, direction, movetime) - occupant.buckled = src - if (occupant && (loc != occupant.loc)) - if (propelled) - for (var/mob/O in src.loc) - if (O != occupant) - Bump(O) - else - unbuckle_mob() - -/obj/structure/bed/chair/office/Bump(atom/A) - ..() - if(!has_buckled_mobs()) return - - if(propelled) - for(var/a in buckled_mobs) - var/mob/living/occupant = unbuckle_mob(a) - - var/def_zone = ran_zone() - var/blocked = occupant.run_armor_check(def_zone, "melee") - var/soaked = occupant.get_armor_soak(def_zone, "melee") - occupant.throw_at(A, 3, propelled) - occupant.apply_effect(6, STUN, blocked) - occupant.apply_effect(6, WEAKEN, blocked) - occupant.apply_effect(6, STUTTER, blocked) - occupant.apply_damage(10, BRUTE, def_zone, blocked, soaked) - playsound(src, 'sound/weapons/punch1.ogg', 50, 1, -1) - if(istype(A, /mob/living)) - var/mob/living/victim = A - def_zone = ran_zone() - blocked = victim.run_armor_check(def_zone, "melee") - soaked = victim.get_armor_soak(def_zone, "melee") - victim.apply_effect(6, STUN, blocked) - victim.apply_effect(6, WEAKEN, blocked) - victim.apply_effect(6, STUTTER, blocked) - victim.apply_damage(10, BRUTE, def_zone, blocked, soaked) - occupant.visible_message("[occupant] crashed into \the [A]!") - -/obj/structure/bed/chair/office/light - icon_state = "officechair_white" - -/obj/structure/bed/chair/office/dark - icon_state = "officechair_dark" - -// Chair types -/obj/structure/bed/chair/wood - name = "wooden chair" - desc = "Old is never too old to not be in fashion." - icon_state = "wooden_chair" - -/obj/structure/bed/chair/wood/update_icon() - return - -/obj/structure/bed/chair/wood/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W,/obj/item/stack) || W.has_tool_quality(TOOL_WIRECUTTER)) - return - ..() - -/obj/structure/bed/chair/wood/New(var/newloc) - ..(newloc, "wood") - -/obj/structure/bed/chair/wood/wings - icon_state = "wooden_chair_wings" - -//sofa - -/obj/structure/bed/chair/sofa - name = "sofa" - desc = "It's a sofa. You sit on it. Possibly with someone else." - icon = 'icons/obj/sofas.dmi' - base_icon = "sofamiddle" - icon_state = "sofamiddle" - applies_material_colour = 1 - var/sofa_material = "carpet" - var/corner_piece = FALSE - -/obj/structure/bed/chair/sofa/update_icon() - if(applies_material_colour && sofa_material) - var/datum/material/color_material = get_material_by_name(sofa_material) - color = color_material.icon_colour - - if(sofa_material == "carpet") - name = "red [initial(name)]" - else - name = "[sofa_material] [initial(name)]" - -/obj/structure/bed/chair/sofa/update_layer() - // Corner east/west should be on top of mobs, any other state's north should be. - if(!corner_piece && (dir & NORTH)) - plane = MOB_PLANE - layer = MOB_LAYER + 0.1 - else - reset_plane_and_layer() - -/obj/structure/bed/chair/sofa/left - icon_state = "sofaend_left" - base_icon = "sofaend_left" - -/obj/structure/bed/chair/sofa/right - icon_state = "sofaend_right" - base_icon = "sofaend_right" - -/obj/structure/bed/chair/sofa/corner - icon_state = "sofacorner" - base_icon = "sofacorner" - corner_piece = TRUE - -/obj/structure/bed/chair/sofa/corner/update_icon() - ..() - var/cache_key = "[base_icon]-armrest-[padding_material ? padding_material.name : "no_material"]-permanent" - if(isnull(stool_cache[cache_key])) - var/image/I = image(icon, "[base_icon]_armrest") - I.plane = MOB_PLANE - I.layer = ABOVE_MOB_LAYER - if(padding_material) - I.color = padding_material.icon_colour - stool_cache[cache_key] = I - add_overlay(stool_cache[cache_key]) - -// Wooden nonsofa - no corners -/obj/structure/bed/chair/sofa/pew - name = "pew bench" - desc = "If they want you to go to church, why do they make these so uncomfortable?" - base_icon = "pewmiddle" - icon_state = "pewmiddle" - applies_material_colour = FALSE - -/obj/structure/bed/chair/sofa/pew/left - icon_state = "pewend_left" - base_icon = "pewend_left" - -/obj/structure/bed/chair/sofa/pew/right - icon_state = "pewend_right" - base_icon = "pewend_right" - -// Metal benches from Skyrat -/obj/structure/bed/chair/sofa/bench - name = "metal bench" - desc = "Almost as comfortable as waiting at a bus station for hours on end." - base_icon = "benchmiddle" - icon_state = "benchmiddle" - applies_material_colour = FALSE - color = null - var/padding_color = "#CC0000" - -/obj/structure/bed/chair/sofa/bench/New(var/newloc, var/new_material, var/new_padding_material) - ..() - var/mutable_appearance/MA - // If we're north-facing, metal goes above mob, padding overlay goes below mob. - if((dir & NORTH) && !corner_piece) - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - MA = mutable_appearance(icon, icon_state = "o[icon_state]", layer = BELOW_MOB_LAYER, plane = MOB_PLANE, appearance_flags = KEEP_APART|RESET_COLOR) - // Else just normal plane and layer for everything, which will be below mobs. - else - MA = mutable_appearance(icon, icon_state = "o[icon_state]", appearance_flags = KEEP_APART|RESET_COLOR) - MA.color = padding_color - add_overlay(MA) - -/obj/structure/bed/chair/sofa/bench/left - icon_state = "bench_left" - base_icon = "bench_left" - -/obj/structure/bed/chair/sofa/bench/right - icon_state = "bench_right" - base_icon = "bench_right" - -/obj/structure/bed/chair/sofa/bench/corner - icon_state = "benchcorner" - base_icon = "benchcorner" - //corner_piece = TRUE // These sprites work fine without the parent doing layer shenanigans - -// Corporate sofa - one color fits all -/obj/structure/bed/chair/sofa/corp - name = "black leather sofa" - desc = "How corporate!" - base_icon = "corp_sofamiddle" - icon_state = "corp_sofamiddle" - applies_material_colour = FALSE - -/obj/structure/bed/chair/sofa/corp/left - icon_state = "corp_sofaend_left" - base_icon = "corp_sofaend_left" - -/obj/structure/bed/chair/sofa/corp/right - icon_state = "corp_sofaend_right" - base_icon = "corp_sofaend_right" - -/obj/structure/bed/chair/sofa/corp/corner - icon_state = "corp_sofacorner" - base_icon = "corp_sofacorner" - corner_piece = TRUE - -//color variations -//Middle sofas first -/obj/structure/bed/chair/sofa - sofa_material = "carpet" - -/obj/structure/bed/chair/sofa/brown - sofa_material = "leather" - -/obj/structure/bed/chair/sofa/teal - sofa_material = "teal" - -/obj/structure/bed/chair/sofa/black - sofa_material = "black" - -/obj/structure/bed/chair/sofa/green - sofa_material = "green" - -/obj/structure/bed/chair/sofa/purp - sofa_material = "purple" - -/obj/structure/bed/chair/sofa/blue - sofa_material = "blue" - -/obj/structure/bed/chair/sofa/beige - sofa_material = "beige" - -/obj/structure/bed/chair/sofa/lime - sofa_material = "lime" - -/obj/structure/bed/chair/sofa/yellow - sofa_material = "yellow" - -/obj/structure/bed/chair/sofa/orange - sofa_material = "orange" - -//sofa directions - -/obj/structure/bed/chair/sofa/left - icon_state = "sofaend_left" - -/obj/structure/bed/chair/sofa/right - icon_state = "sofaend_right" - -/obj/structure/bed/chair/sofa/corner - icon_state = "sofacorner" - -/obj/structure/bed/chair/sofa/left/brown - sofa_material = "leather" - -/obj/structure/bed/chair/sofa/right/brown - sofa_material = "leather" - -/obj/structure/bed/chair/sofa/corner/brown - sofa_material = "leather" - -/obj/structure/bed/chair/sofa/left/teal - sofa_material = "teal" - -/obj/structure/bed/chair/sofa/right/teal - sofa_material = "teal" - -/obj/structure/bed/chair/sofa/corner/teal - sofa_material = "teal" - -/obj/structure/bed/chair/sofa/left/black - sofa_material = "black" - -/obj/structure/bed/chair/sofa/right/black - sofa_material = "black" - -/obj/structure/bed/chair/sofa/corner/black - sofa_material = "black" - -/obj/structure/bed/chair/sofa/left/green - sofa_material = "green" - -/obj/structure/bed/chair/sofa/right/green - sofa_material = "green" - -/obj/structure/bed/chair/sofa/corner/green - sofa_material = "green" - -/obj/structure/bed/chair/sofa/left/purp - sofa_material = "purple" - -/obj/structure/bed/chair/sofa/right/purp - sofa_material = "purple" - -/obj/structure/bed/chair/sofa/corner/purp - sofa_material = "purple" - -/obj/structure/bed/chair/sofa/left/blue - sofa_material = "blue" - -/obj/structure/bed/chair/sofa/right/blue - sofa_material = "blue" - -/obj/structure/bed/chair/sofa/corner/blue - sofa_material = "blue" - -/obj/structure/bed/chair/sofa/left/beige - sofa_material = "beige" - -/obj/structure/bed/chair/sofa/right/beige - sofa_material = "beige" - -/obj/structure/bed/chair/sofa/corner/beige - sofa_material = "beige" - -/obj/structure/bed/chair/sofa/left/lime - sofa_material = "lime" - -/obj/structure/bed/chair/sofa/right/lime - sofa_material = "lime" - -/obj/structure/bed/chair/sofa/corner/lime - sofa_material = "lime" - -/obj/structure/bed/chair/sofa/left/yellow - sofa_material = "yellow" - -/obj/structure/bed/chair/sofa/right/yellow - sofa_material = "yellow" - -/obj/structure/bed/chair/sofa/corner/yellow - sofa_material = "yellow" - -/obj/structure/bed/chair/sofa/left/orange - sofa_material = "orange" - -/obj/structure/bed/chair/sofa/right/orange - sofa_material = "orange" - -/obj/structure/bed/chair/sofa/corner/orange - sofa_material = "orange" +/obj/structure/bed/chair //YES, chairs are a type of bed, which are a type of stool. This works, believe me. -Pete + name = "chair" + desc = "You sit in this. Either by will or force." + icon = 'icons/obj/furniture_vr.dmi' //VOREStation Edit - Using Eris furniture + icon_state = "chair_preview" + color = "#666666" + base_icon = "chair" + buckle_dir = 0 + buckle_lying = 0 //force people to sit up in chairs when buckled + var/propelled = 0 // Check for fire-extinguisher-driven chairs + +/obj/structure/bed/chair/New(var/newloc, var/new_material, var/new_padding_material) + ..() + update_layer() + +/obj/structure/bed/chair/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + if(!padding_material && istype(W, /obj/item/assembly/shock_kit)) + var/obj/item/assembly/shock_kit/SK = W + if(!SK.status) + to_chat(user, "\The [SK] is not ready to be attached!") + return + user.drop_item() + var/obj/structure/bed/chair/e_chair/E = new (src.loc, material.name) + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + E.set_dir(dir) + E.part = SK + SK.loc = E + SK.master = E + qdel(src) + +/obj/structure/bed/chair/attack_tk(mob/user as mob) + if(has_buckled_mobs()) + ..() + else + rotate_clockwise() + return + +/obj/structure/bed/chair/post_buckle_mob() + update_icon() + +/obj/structure/bed/chair/update_icon() + ..() + if(has_buckled_mobs()) + var/cache_key = "[base_icon]-armrest-[padding_material ? padding_material.name : "no_material"]" + if(isnull(stool_cache[cache_key])) + var/image/I = image(icon, "[base_icon]_armrest") + I.plane = MOB_PLANE + I.layer = ABOVE_MOB_LAYER + if(padding_material) + I.color = padding_material.icon_colour + stool_cache[cache_key] = I + add_overlay(stool_cache[cache_key]) + +/obj/structure/bed/chair/proc/update_layer() + if(src.dir == NORTH) + plane = MOB_PLANE + layer = MOB_LAYER + 0.1 + else + reset_plane_and_layer() + +/obj/structure/bed/chair/set_dir() + ..() + update_layer() + if(has_buckled_mobs()) + for(var/mob/living/L as anything in buckled_mobs) + L.set_dir(dir) + +/obj/structure/bed/chair/verb/rotate_clockwise() + set name = "Rotate Chair Clockwise" + set category = "Object" + set src in oview(1) + + if(!usr || !isturf(usr.loc)) + return + if(usr.stat || usr.restrained()) + return + if(ismouse(usr) || (isobserver(usr) && !config.ghost_interaction)) + return + + src.set_dir(turn(src.dir, 270)) + +/obj/structure/bed/chair/verb/rotate_counterclockwise() + set name = "Rotate Chair Counter-Clockwise" + set category = "Object" + set src in oview(1) + + if(!usr || !isturf(usr.loc)) + return + if(usr.stat || usr.restrained()) + return + if(ismouse(usr) || (isobserver(usr) && !config.ghost_interaction)) + return + + src.set_dir(turn(src.dir, 90)) + +/obj/structure/bed/chair/shuttle + name = "chair" + icon_state = "shuttlechair" + base_icon = "shuttlechair" + color = null + applies_material_colour = 0 + +/obj/structure/bed/chair/shuttle_padded + icon_state = "shuttlechair2" + base_icon = "shuttlechair2" + color = null + applies_material_colour = 0 + +/obj/structure/bed/chair/comfy + name = "comfy chair" + desc = "It's a chair. It looks comfy." + icon_state = "comfychair" + base_icon = "comfychair" + +/obj/structure/bed/chair/comfy/update_icon() + ..() + var/image/I = image(icon, "[base_icon]_over") + I.layer = ABOVE_MOB_LAYER + I.plane = MOB_PLANE + I.color = material.icon_colour + add_overlay(I) + if(padding_material) + I = image(icon, "[base_icon]_padding_over") + I.layer = ABOVE_MOB_LAYER + I.plane = MOB_PLANE + I.color = padding_material.icon_colour + add_overlay(I) + +/obj/structure/bed/chair/comfy/brown/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, MAT_LEATHER) + +/obj/structure/bed/chair/comfy/red/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "carpet") + +/obj/structure/bed/chair/comfy/teal/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "teal") + +/obj/structure/bed/chair/comfy/black/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "black") + +/obj/structure/bed/chair/comfy/green/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "green") + +/obj/structure/bed/chair/comfy/purp/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "purple") + +/obj/structure/bed/chair/comfy/blue/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "blue") + +/obj/structure/bed/chair/comfy/beige/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "beige") + +/obj/structure/bed/chair/comfy/lime/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "lime") + +/obj/structure/bed/chair/comfy/yellow/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "yellow") + +/obj/structure/bed/chair/comfy/orange/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "orange") + +/obj/structure/bed/chair/comfy/rounded + name = "rounded chair" + desc = "It's a rounded chair. It looks comfy." + icon_state = "roundedchair" + base_icon = "roundedchair" + +/obj/structure/bed/chair/comfy/rounded/brown/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, MAT_LEATHER) + +/obj/structure/bed/chair/comfy/rounded/red/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "carpet") + +/obj/structure/bed/chair/comfy/rounded/teal/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "teal") + +/obj/structure/bed/chair/comfy/rounded/black/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "black") + +/obj/structure/bed/chair/comfy/rounded/green/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "green") + +/obj/structure/bed/chair/comfy/rounded/purple/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "purple") + +/obj/structure/bed/chair/comfy/rounded/blue/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "blue") + +/obj/structure/bed/chair/comfy/rounded/beige/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "beige") + +/obj/structure/bed/chair/comfy/rounded/lime/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "lime") + +/obj/structure/bed/chair/comfy/rounded/yellow/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "yellow") + +/obj/structure/bed/chair/comfy/rounded/orange/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "orange") + +/obj/structure/bed/chair/office + anchored = FALSE + buckle_movable = 1 + +/obj/structure/bed/chair/office/update_icon() + return + +/obj/structure/bed/chair/office/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W,/obj/item/stack) || W.has_tool_quality(TOOL_WIRECUTTER)) + return + ..() + +/obj/structure/bed/chair/office/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + + playsound(src, 'sound/effects/roll.ogg', 100, 1) + +/obj/structure/bed/chair/office/handle_buckled_mob_movement(atom/new_loc, direction, movetime) + for(var/mob/living/occupant as anything in buckled_mobs) + occupant.buckled = null + occupant.Move(loc, direction, movetime) + occupant.buckled = src + if (occupant && (loc != occupant.loc)) + if (propelled) + for (var/mob/O in src.loc) + if (O != occupant) + Bump(O) + else + unbuckle_mob() + +/obj/structure/bed/chair/office/Bump(atom/A) + ..() + if(!has_buckled_mobs()) return + + if(propelled) + for(var/a in buckled_mobs) + var/mob/living/occupant = unbuckle_mob(a) + + var/def_zone = ran_zone() + var/blocked = occupant.run_armor_check(def_zone, "melee") + var/soaked = occupant.get_armor_soak(def_zone, "melee") + occupant.throw_at(A, 3, propelled) + occupant.apply_effect(6, STUN, blocked) + occupant.apply_effect(6, WEAKEN, blocked) + occupant.apply_effect(6, STUTTER, blocked) + occupant.apply_damage(10, BRUTE, def_zone, blocked, soaked) + playsound(src, 'sound/weapons/punch1.ogg', 50, 1, -1) + if(istype(A, /mob/living)) + var/mob/living/victim = A + def_zone = ran_zone() + blocked = victim.run_armor_check(def_zone, "melee") + soaked = victim.get_armor_soak(def_zone, "melee") + victim.apply_effect(6, STUN, blocked) + victim.apply_effect(6, WEAKEN, blocked) + victim.apply_effect(6, STUTTER, blocked) + victim.apply_damage(10, BRUTE, def_zone, blocked, soaked) + occupant.visible_message("[occupant] crashed into \the [A]!") + +/obj/structure/bed/chair/office/light + icon_state = "officechair_white" + +/obj/structure/bed/chair/office/dark + icon_state = "officechair_dark" + +// Chair types +/obj/structure/bed/chair/wood + name = "wooden chair" + desc = "Old is never too old to not be in fashion." + icon_state = "wooden_chair" + +/obj/structure/bed/chair/wood/update_icon() + return + +/obj/structure/bed/chair/wood/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W,/obj/item/stack) || W.has_tool_quality(TOOL_WIRECUTTER)) + return + ..() + +/obj/structure/bed/chair/wood/New(var/newloc) + ..(newloc, "wood") + +/obj/structure/bed/chair/wood/wings + icon_state = "wooden_chair_wings" + +//sofa + +/obj/structure/bed/chair/sofa + name = "sofa" + desc = "It's a sofa. You sit on it. Possibly with someone else." + icon = 'icons/obj/sofas.dmi' + base_icon = "sofamiddle" + icon_state = "sofamiddle" + applies_material_colour = 1 + var/sofa_material = "carpet" + var/corner_piece = FALSE + +/obj/structure/bed/chair/sofa/update_icon() + if(applies_material_colour && sofa_material) + var/datum/material/color_material = get_material_by_name(sofa_material) + color = color_material.icon_colour + + if(sofa_material == "carpet") + name = "red [initial(name)]" + else + name = "[sofa_material] [initial(name)]" + +/obj/structure/bed/chair/sofa/update_layer() + // Corner east/west should be on top of mobs, any other state's north should be. + if(!corner_piece && (dir & NORTH)) + plane = MOB_PLANE + layer = MOB_LAYER + 0.1 + else + reset_plane_and_layer() + +/obj/structure/bed/chair/sofa/left + icon_state = "sofaend_left" + base_icon = "sofaend_left" + +/obj/structure/bed/chair/sofa/right + icon_state = "sofaend_right" + base_icon = "sofaend_right" + +/obj/structure/bed/chair/sofa/corner + icon_state = "sofacorner" + base_icon = "sofacorner" + corner_piece = TRUE + +/obj/structure/bed/chair/sofa/corner/update_icon() + ..() + var/cache_key = "[base_icon]-armrest-[padding_material ? padding_material.name : "no_material"]-permanent" + if(isnull(stool_cache[cache_key])) + var/image/I = image(icon, "[base_icon]_armrest") + I.plane = MOB_PLANE + I.layer = ABOVE_MOB_LAYER + if(padding_material) + I.color = padding_material.icon_colour + stool_cache[cache_key] = I + add_overlay(stool_cache[cache_key]) + +// Wooden nonsofa - no corners +/obj/structure/bed/chair/sofa/pew + name = "pew bench" + desc = "If they want you to go to church, why do they make these so uncomfortable?" + base_icon = "pewmiddle" + icon_state = "pewmiddle" + applies_material_colour = FALSE + +/obj/structure/bed/chair/sofa/pew/left + icon_state = "pewend_left" + base_icon = "pewend_left" + +/obj/structure/bed/chair/sofa/pew/right + icon_state = "pewend_right" + base_icon = "pewend_right" + +// Metal benches from Skyrat +/obj/structure/bed/chair/sofa/bench + name = "metal bench" + desc = "Almost as comfortable as waiting at a bus station for hours on end." + base_icon = "benchmiddle" + icon_state = "benchmiddle" + applies_material_colour = FALSE + color = null + var/padding_color = "#CC0000" + +/obj/structure/bed/chair/sofa/bench/New(var/newloc, var/new_material, var/new_padding_material) + ..() + var/mutable_appearance/MA + // If we're north-facing, metal goes above mob, padding overlay goes below mob. + if((dir & NORTH) && !corner_piece) + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + MA = mutable_appearance(icon, icon_state = "o[icon_state]", layer = BELOW_MOB_LAYER, plane = MOB_PLANE, appearance_flags = KEEP_APART|RESET_COLOR) + // Else just normal plane and layer for everything, which will be below mobs. + else + MA = mutable_appearance(icon, icon_state = "o[icon_state]", appearance_flags = KEEP_APART|RESET_COLOR) + MA.color = padding_color + add_overlay(MA) + +/obj/structure/bed/chair/sofa/bench/left + icon_state = "bench_left" + base_icon = "bench_left" + +/obj/structure/bed/chair/sofa/bench/right + icon_state = "bench_right" + base_icon = "bench_right" + +/obj/structure/bed/chair/sofa/bench/corner + icon_state = "benchcorner" + base_icon = "benchcorner" + //corner_piece = TRUE // These sprites work fine without the parent doing layer shenanigans + +// Corporate sofa - one color fits all +/obj/structure/bed/chair/sofa/corp + name = "black leather sofa" + desc = "How corporate!" + base_icon = "corp_sofamiddle" + icon_state = "corp_sofamiddle" + applies_material_colour = FALSE + +/obj/structure/bed/chair/sofa/corp/left + icon_state = "corp_sofaend_left" + base_icon = "corp_sofaend_left" + +/obj/structure/bed/chair/sofa/corp/right + icon_state = "corp_sofaend_right" + base_icon = "corp_sofaend_right" + +/obj/structure/bed/chair/sofa/corp/corner + icon_state = "corp_sofacorner" + base_icon = "corp_sofacorner" + corner_piece = TRUE + +//color variations +//Middle sofas first +/obj/structure/bed/chair/sofa + sofa_material = "carpet" + +/obj/structure/bed/chair/sofa/brown + sofa_material = "leather" + +/obj/structure/bed/chair/sofa/teal + sofa_material = "teal" + +/obj/structure/bed/chair/sofa/black + sofa_material = "black" + +/obj/structure/bed/chair/sofa/green + sofa_material = "green" + +/obj/structure/bed/chair/sofa/purp + sofa_material = "purple" + +/obj/structure/bed/chair/sofa/blue + sofa_material = "blue" + +/obj/structure/bed/chair/sofa/beige + sofa_material = "beige" + +/obj/structure/bed/chair/sofa/lime + sofa_material = "lime" + +/obj/structure/bed/chair/sofa/yellow + sofa_material = "yellow" + +/obj/structure/bed/chair/sofa/orange + sofa_material = "orange" + +//sofa directions + +/obj/structure/bed/chair/sofa/left + icon_state = "sofaend_left" + +/obj/structure/bed/chair/sofa/right + icon_state = "sofaend_right" + +/obj/structure/bed/chair/sofa/corner + icon_state = "sofacorner" + +/obj/structure/bed/chair/sofa/left/brown + sofa_material = "leather" + +/obj/structure/bed/chair/sofa/right/brown + sofa_material = "leather" + +/obj/structure/bed/chair/sofa/corner/brown + sofa_material = "leather" + +/obj/structure/bed/chair/sofa/left/teal + sofa_material = "teal" + +/obj/structure/bed/chair/sofa/right/teal + sofa_material = "teal" + +/obj/structure/bed/chair/sofa/corner/teal + sofa_material = "teal" + +/obj/structure/bed/chair/sofa/left/black + sofa_material = "black" + +/obj/structure/bed/chair/sofa/right/black + sofa_material = "black" + +/obj/structure/bed/chair/sofa/corner/black + sofa_material = "black" + +/obj/structure/bed/chair/sofa/left/green + sofa_material = "green" + +/obj/structure/bed/chair/sofa/right/green + sofa_material = "green" + +/obj/structure/bed/chair/sofa/corner/green + sofa_material = "green" + +/obj/structure/bed/chair/sofa/left/purp + sofa_material = "purple" + +/obj/structure/bed/chair/sofa/right/purp + sofa_material = "purple" + +/obj/structure/bed/chair/sofa/corner/purp + sofa_material = "purple" + +/obj/structure/bed/chair/sofa/left/blue + sofa_material = "blue" + +/obj/structure/bed/chair/sofa/right/blue + sofa_material = "blue" + +/obj/structure/bed/chair/sofa/corner/blue + sofa_material = "blue" + +/obj/structure/bed/chair/sofa/left/beige + sofa_material = "beige" + +/obj/structure/bed/chair/sofa/right/beige + sofa_material = "beige" + +/obj/structure/bed/chair/sofa/corner/beige + sofa_material = "beige" + +/obj/structure/bed/chair/sofa/left/lime + sofa_material = "lime" + +/obj/structure/bed/chair/sofa/right/lime + sofa_material = "lime" + +/obj/structure/bed/chair/sofa/corner/lime + sofa_material = "lime" + +/obj/structure/bed/chair/sofa/left/yellow + sofa_material = "yellow" + +/obj/structure/bed/chair/sofa/right/yellow + sofa_material = "yellow" + +/obj/structure/bed/chair/sofa/corner/yellow + sofa_material = "yellow" + +/obj/structure/bed/chair/sofa/left/orange + sofa_material = "orange" + +/obj/structure/bed/chair/sofa/right/orange + sofa_material = "orange" + +/obj/structure/bed/chair/sofa/corner/orange + sofa_material = "orange" diff --git a/code/game/objects/structures/stool_bed_chair_nest/stools.dm b/code/game/objects/structures/stool_bed_chair_nest/stools.dm index 7240c4fb21e..afd8fb13b28 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/stools.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/stools.dm @@ -1,151 +1,151 @@ -//Todo: add leather and cloth for arbitrary coloured stools. -var/global/list/stool_cache = list() //haha stool - -/obj/item/weapon/stool - name = "stool" - desc = "Apply butt." - icon = 'icons/obj/furniture_vr.dmi' //VOREStation Edit - new Icons - icon_state = "stool_preview" //set for the map - randpixel = 0 - center_of_mass = null - force = 10 - throwforce = 10 - w_class = ITEMSIZE_HUGE - var/base_icon = "stool_base" - var/datum/material/material - var/datum/material/padding_material - -/obj/item/weapon/stool/padded - icon_state = "stool_padded_preview" //set for the map - -/obj/item/weapon/stool/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc) - if(!new_material) - new_material = MAT_STEEL - material = get_material_by_name(new_material) - if(new_padding_material) - padding_material = get_material_by_name(new_padding_material) - if(!istype(material)) - qdel(src) - return - force = round(material.get_blunt_damage()*0.4) - update_icon() - -/obj/item/weapon/stool/padded/New(var/newloc, var/new_material) - ..(newloc, "steel", "carpet") - -/obj/item/weapon/stool/update_icon() - // Prep icon. - icon_state = "" - cut_overlays() - // Base icon. - var/cache_key = "[base_icon]-[material.name]" - if(isnull(stool_cache[cache_key])) - var/image/I = image(icon, base_icon) - I.color = material.icon_colour - stool_cache[cache_key] = I - add_overlay(stool_cache[cache_key]) - // Padding overlay. - if(padding_material) - var/padding_cache_key = "[base_icon]-padding-[padding_material.name]" - if(isnull(stool_cache[padding_cache_key])) - var/image/I = image(icon, "[base_icon]_padding") //VOREStation Edit - I.color = padding_material.icon_colour - stool_cache[padding_cache_key] = I - add_overlay(stool_cache[padding_cache_key]) - // Strings. - if(padding_material) - name = "[padding_material.display_name] [initial(name)]" //this is not perfect but it will do for now. - desc = "A padded stool. Apply butt. It's made of [material.use_name] and covered with [padding_material.use_name]." - else - name = "[material.display_name] [initial(name)]" - desc = "A stool. Apply butt with care. It's made of [material.use_name]." - -/obj/item/weapon/stool/proc/add_padding(var/padding_type) - padding_material = get_material_by_name(padding_type) - update_icon() - -/obj/item/weapon/stool/proc/remove_padding() - if(padding_material) - padding_material.place_sheet(get_turf(src), 1) - padding_material = null - update_icon() - -/obj/item/weapon/stool/attack(mob/M as mob, mob/user as mob) - if (prob(5) && istype(M,/mob/living)) - user.visible_message("[user] breaks [src] over [M]'s back!") - user.setClickCooldown(user.get_attack_speed()) - user.do_attack_animation(M) - - user.drop_from_inventory(src) - - user.remove_from_mob(src) - dismantle() - qdel(src) - var/mob/living/T = M - T.Weaken(10) - T.apply_damage(20) - return - ..() - -/obj/item/weapon/stool/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - return - if(2.0) - if (prob(50)) - qdel(src) - return - if(3.0) - if (prob(5)) - qdel(src) - return - -/obj/item/weapon/stool/proc/dismantle() - if(material) - material.place_sheet(get_turf(src), 1) - if(padding_material) - padding_material.place_sheet(get_turf(src), 1) - qdel(src) - -/obj/item/weapon/stool/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.has_tool_quality(TOOL_WRENCH)) - playsound(src, W.usesound, 50, 1) - dismantle() - qdel(src) - else if(istype(W,/obj/item/stack)) - if(padding_material) - to_chat(user, "\The [src] is already padded.") - return - var/obj/item/stack/C = W - if(C.get_amount() < 1) // How?? - user.drop_from_inventory(C) - qdel(C) - return - var/padding_type //This is awful but it needs to be like this until tiles are given a material var. - if(istype(W,/obj/item/stack/tile/carpet)) - padding_type = "carpet" - else if(istype(W,/obj/item/stack/material)) - var/obj/item/stack/material/M = W - if(M.material && (M.material.flags & MATERIAL_PADDING)) - padding_type = "[M.material.name]" - if(!padding_type) - to_chat(user, "You cannot pad \the [src] with that.") - return - C.use(1) - if(!istype(src.loc, /turf)) - user.drop_from_inventory(src) - src.loc = get_turf(src) - to_chat(user, "You add padding to \the [src].") - add_padding(padding_type) - return - else if (W.has_tool_quality(TOOL_WIRECUTTER)) - if(!padding_material) - to_chat(user, "\The [src] has no padding to remove.") - return - to_chat(user, "You remove the padding from \the [src].") - playsound(src, W.usesound, 50, 1) - remove_padding() - else - ..() +//Todo: add leather and cloth for arbitrary coloured stools. +var/global/list/stool_cache = list() //haha stool + +/obj/item/weapon/stool + name = "stool" + desc = "Apply butt." + icon = 'icons/obj/furniture_vr.dmi' //VOREStation Edit - new Icons + icon_state = "stool_preview" //set for the map + randpixel = 0 + center_of_mass = null + force = 10 + throwforce = 10 + w_class = ITEMSIZE_HUGE + var/base_icon = "stool_base" + var/datum/material/material + var/datum/material/padding_material + +/obj/item/weapon/stool/padded + icon_state = "stool_padded_preview" //set for the map + +/obj/item/weapon/stool/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc) + if(!new_material) + new_material = MAT_STEEL + material = get_material_by_name(new_material) + if(new_padding_material) + padding_material = get_material_by_name(new_padding_material) + if(!istype(material)) + qdel(src) + return + force = round(material.get_blunt_damage()*0.4) + update_icon() + +/obj/item/weapon/stool/padded/New(var/newloc, var/new_material) + ..(newloc, "steel", "carpet") + +/obj/item/weapon/stool/update_icon() + // Prep icon. + icon_state = "" + cut_overlays() + // Base icon. + var/cache_key = "[base_icon]-[material.name]" + if(isnull(stool_cache[cache_key])) + var/image/I = image(icon, base_icon) + I.color = material.icon_colour + stool_cache[cache_key] = I + add_overlay(stool_cache[cache_key]) + // Padding overlay. + if(padding_material) + var/padding_cache_key = "[base_icon]-padding-[padding_material.name]" + if(isnull(stool_cache[padding_cache_key])) + var/image/I = image(icon, "[base_icon]_padding") //VOREStation Edit + I.color = padding_material.icon_colour + stool_cache[padding_cache_key] = I + add_overlay(stool_cache[padding_cache_key]) + // Strings. + if(padding_material) + name = "[padding_material.display_name] [initial(name)]" //this is not perfect but it will do for now. + desc = "A padded stool. Apply butt. It's made of [material.use_name] and covered with [padding_material.use_name]." + else + name = "[material.display_name] [initial(name)]" + desc = "A stool. Apply butt with care. It's made of [material.use_name]." + +/obj/item/weapon/stool/proc/add_padding(var/padding_type) + padding_material = get_material_by_name(padding_type) + update_icon() + +/obj/item/weapon/stool/proc/remove_padding() + if(padding_material) + padding_material.place_sheet(get_turf(src), 1) + padding_material = null + update_icon() + +/obj/item/weapon/stool/attack(mob/M as mob, mob/user as mob) + if (prob(5) && istype(M,/mob/living)) + user.visible_message("[user] breaks [src] over [M]'s back!") + user.setClickCooldown(user.get_attack_speed()) + user.do_attack_animation(M) + + user.drop_from_inventory(src) + + user.remove_from_mob(src) + dismantle() + qdel(src) + var/mob/living/T = M + T.Weaken(10) + T.apply_damage(20) + return + ..() + +/obj/item/weapon/stool/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + return + if(2.0) + if (prob(50)) + qdel(src) + return + if(3.0) + if (prob(5)) + qdel(src) + return + +/obj/item/weapon/stool/proc/dismantle() + if(material) + material.place_sheet(get_turf(src), 1) + if(padding_material) + padding_material.place_sheet(get_turf(src), 1) + qdel(src) + +/obj/item/weapon/stool/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + playsound(src, W.usesound, 50, 1) + dismantle() + qdel(src) + else if(istype(W,/obj/item/stack)) + if(padding_material) + to_chat(user, "\The [src] is already padded.") + return + var/obj/item/stack/C = W + if(C.get_amount() < 1) // How?? + user.drop_from_inventory(C) + qdel(C) + return + var/padding_type //This is awful but it needs to be like this until tiles are given a material var. + if(istype(W,/obj/item/stack/tile/carpet)) + padding_type = "carpet" + else if(istype(W,/obj/item/stack/material)) + var/obj/item/stack/material/M = W + if(M.material && (M.material.flags & MATERIAL_PADDING)) + padding_type = "[M.material.name]" + if(!padding_type) + to_chat(user, "You cannot pad \the [src] with that.") + return + C.use(1) + if(!istype(src.loc, /turf)) + user.drop_from_inventory(src) + src.loc = get_turf(src) + to_chat(user, "You add padding to \the [src].") + add_padding(padding_type) + return + else if (W.has_tool_quality(TOOL_WIRECUTTER)) + if(!padding_material) + to_chat(user, "\The [src] has no padding to remove.") + return + to_chat(user, "You remove the padding from \the [src].") + playsound(src, W.usesound, 50, 1) + remove_padding() + else + ..() diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm index fcbfe982309..23872935843 100644 --- a/code/game/objects/structures/tank_dispenser.dm +++ b/code/game/objects/structures/tank_dispenser.dm @@ -1,122 +1,122 @@ -#define TANK_DISPENSER_CAPACITY 10 - -/obj/structure/dispenser - name = "tank storage unit" - desc = "A simple yet bulky storage device for gas tanks. Has room for up to ten oxygen tanks, and ten phoron tanks." - icon = 'icons/obj/objects_vr.dmi' - icon_state = "dispenser" - density = TRUE - anchored = TRUE - w_class = ITEMSIZE_HUGE - var/oxygentanks = TANK_DISPENSER_CAPACITY - var/phorontanks = TANK_DISPENSER_CAPACITY - - -/obj/structure/dispenser/oxygen - phorontanks = 0 - -/obj/structure/dispenser/phoron - oxygentanks = 0 - - -/obj/structure/dispenser/Initialize() - . = ..() - for(var/i in 1 to oxygentanks) - new /obj/item/weapon/tank/oxygen(src) - for(var/i in 1 to phorontanks) - new /obj/item/weapon/tank/phoron(src) - update_icon() - -/obj/structure/dispenser/update_icon() - cut_overlays() - switch(oxygentanks) - if(1 to 3) add_overlay("oxygen-[oxygentanks]") - if(4 to INFINITY) add_overlay("oxygen-4") - switch(phorontanks) - if(1 to 4) add_overlay("phoron-[phorontanks]") - if(5 to INFINITY) add_overlay("phoron-5") - -/obj/structure/dispenser/attack_ai(mob/user) - // This looks silly, but robots also call attack_ai, and they're allowed physical state stuff. - if(user.Adjacent(src)) - return attack_hand(user) - ..() - -/obj/structure/dispenser/attack_hand(mob/user) - tgui_interact(user) - -/obj/structure/dispenser/tgui_state(mob/user) - return GLOB.tgui_physical_state - -/obj/structure/dispenser/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "TankDispenser", name) - ui.open() - -/obj/structure/dispenser/tgui_data(mob/user) - var/list/data = list() - data["oxygen"] = oxygentanks - data["plasma"] = phorontanks - - return data - -/obj/structure/dispenser/attackby(obj/item/I, mob/user) - var/full - if(istype(I, /obj/item/weapon/tank/oxygen) || istype(I, /obj/item/weapon/tank/air) || istype(I, /obj/item/weapon/tank/anesthetic)) - if(oxygentanks < TANK_DISPENSER_CAPACITY) - oxygentanks++ - else - full = TRUE - else if(istype(I, /obj/item/weapon/tank/phoron)) - if(phorontanks < TANK_DISPENSER_CAPACITY) - phorontanks++ - else - full = TRUE - else if(I.has_tool_quality(TOOL_WRENCH)) - if(anchored) - to_chat(user, "You lean down and unwrench [src].") - anchored = FALSE - else - to_chat(user, "You wrench [src] into place.") - anchored = TRUE - return - else if(user.a_intent != I_HURT) - to_chat(user, "[I] does not fit into [src].") - return - else - return ..() - - if(full) - to_chat(user, "[src] can't hold any more of [I].") - return - - if(!user.unEquip(I, target = src)) - return - to_chat(user, "You put [I] in [src].") - update_icon() - - -/obj/structure/dispenser/tgui_act(action, params) - if(..()) - return - switch(action) - if("plasma") - var/obj/item/weapon/tank/phoron/tank = locate() in src - if(tank && Adjacent(usr)) - usr.put_in_hands(tank) - phorontanks-- - . = TRUE - playsound(src, 'sound/items/drop/gascan.ogg', 100, 1, 1) - if("oxygen") - var/obj/item/weapon/tank/tank = null - for(var/obj/item/weapon/tank/T in src) - if(istype(T, /obj/item/weapon/tank/oxygen) || istype(T, /obj/item/weapon/tank/air) || istype(T, /obj/item/weapon/tank/anesthetic)) - tank = T - break - if(tank && Adjacent(usr)) - usr.put_in_hands(tank) - oxygentanks-- - . = TRUE - playsound(src, 'sound/items/drop/gascan.ogg', 100, 1, 1) +#define TANK_DISPENSER_CAPACITY 10 + +/obj/structure/dispenser + name = "tank storage unit" + desc = "A simple yet bulky storage device for gas tanks. Has room for up to ten oxygen tanks, and ten phoron tanks." + icon = 'icons/obj/objects_vr.dmi' + icon_state = "dispenser" + density = TRUE + anchored = TRUE + w_class = ITEMSIZE_HUGE + var/oxygentanks = TANK_DISPENSER_CAPACITY + var/phorontanks = TANK_DISPENSER_CAPACITY + + +/obj/structure/dispenser/oxygen + phorontanks = 0 + +/obj/structure/dispenser/phoron + oxygentanks = 0 + + +/obj/structure/dispenser/Initialize() + . = ..() + for(var/i in 1 to oxygentanks) + new /obj/item/weapon/tank/oxygen(src) + for(var/i in 1 to phorontanks) + new /obj/item/weapon/tank/phoron(src) + update_icon() + +/obj/structure/dispenser/update_icon() + cut_overlays() + switch(oxygentanks) + if(1 to 3) add_overlay("oxygen-[oxygentanks]") + if(4 to INFINITY) add_overlay("oxygen-4") + switch(phorontanks) + if(1 to 4) add_overlay("phoron-[phorontanks]") + if(5 to INFINITY) add_overlay("phoron-5") + +/obj/structure/dispenser/attack_ai(mob/user) + // This looks silly, but robots also call attack_ai, and they're allowed physical state stuff. + if(user.Adjacent(src)) + return attack_hand(user) + ..() + +/obj/structure/dispenser/attack_hand(mob/user) + tgui_interact(user) + +/obj/structure/dispenser/tgui_state(mob/user) + return GLOB.tgui_physical_state + +/obj/structure/dispenser/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "TankDispenser", name) + ui.open() + +/obj/structure/dispenser/tgui_data(mob/user) + var/list/data = list() + data["oxygen"] = oxygentanks + data["plasma"] = phorontanks + + return data + +/obj/structure/dispenser/attackby(obj/item/I, mob/user) + var/full + if(istype(I, /obj/item/weapon/tank/oxygen) || istype(I, /obj/item/weapon/tank/air) || istype(I, /obj/item/weapon/tank/anesthetic)) + if(oxygentanks < TANK_DISPENSER_CAPACITY) + oxygentanks++ + else + full = TRUE + else if(istype(I, /obj/item/weapon/tank/phoron)) + if(phorontanks < TANK_DISPENSER_CAPACITY) + phorontanks++ + else + full = TRUE + else if(I.has_tool_quality(TOOL_WRENCH)) + if(anchored) + to_chat(user, "You lean down and unwrench [src].") + anchored = FALSE + else + to_chat(user, "You wrench [src] into place.") + anchored = TRUE + return + else if(user.a_intent != I_HURT) + to_chat(user, "[I] does not fit into [src].") + return + else + return ..() + + if(full) + to_chat(user, "[src] can't hold any more of [I].") + return + + if(!user.unEquip(I, target = src)) + return + to_chat(user, "You put [I] in [src].") + update_icon() + + +/obj/structure/dispenser/tgui_act(action, params) + if(..()) + return + switch(action) + if("plasma") + var/obj/item/weapon/tank/phoron/tank = locate() in src + if(tank && Adjacent(usr)) + usr.put_in_hands(tank) + phorontanks-- + . = TRUE + playsound(src, 'sound/items/drop/gascan.ogg', 100, 1, 1) + if("oxygen") + var/obj/item/weapon/tank/tank = null + for(var/obj/item/weapon/tank/T in src) + if(istype(T, /obj/item/weapon/tank/oxygen) || istype(T, /obj/item/weapon/tank/air) || istype(T, /obj/item/weapon/tank/anesthetic)) + tank = T + break + if(tank && Adjacent(usr)) + usr.put_in_hands(tank) + oxygentanks-- + . = TRUE + playsound(src, 'sound/items/drop/gascan.ogg', 100, 1, 1) update_icon() \ No newline at end of file diff --git a/code/game/objects/structures/target_stake.dm b/code/game/objects/structures/target_stake.dm index 0e29fed20b9..76556346006 100644 --- a/code/game/objects/structures/target_stake.dm +++ b/code/game/objects/structures/target_stake.dm @@ -1,52 +1,52 @@ -// Basically they are for the firing range -/obj/structure/target_stake - name = "target stake" - desc = "A thin platform with negatively-magnetized wheels." - icon = 'icons/obj/objects.dmi' - icon_state = "target_stake" - density = TRUE - w_class = ITEMSIZE_HUGE - var/obj/item/target/pinned_target // the current pinned target - -/obj/structure/target_stake/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - // Move the pinned target along with the stake - if(pinned_target in view(3, src)) - pinned_target.forceMove(loc) - - else // Sanity check: if the pinned target can't be found in immediate view - pinned_target = null - density = TRUE - -/obj/structure/target_stake/attackby(obj/item/W as obj, mob/user as mob) - // Putting objects on the stake. Most importantly, targets - if(pinned_target) - return // get rid of that pinned target first! - - if(istype(W, /obj/item/target)) - density = FALSE - W.density = TRUE - user.remove_from_mob(W) - W.loc = loc - W.layer = ABOVE_JUNK_LAYER - pinned_target = W - to_chat(user, "You slide the target into the stake.") - return - -/obj/structure/target_stake/attack_hand(mob/user as mob) - // taking pinned targets off! - if(pinned_target) - density = TRUE - pinned_target.density = FALSE - pinned_target.layer = OBJ_LAYER - - pinned_target.loc = user.loc - if(ishuman(user)) - if(!user.get_active_hand()) - user.put_in_hands(pinned_target) - to_chat(user, "You take the target out of the stake.") - else - pinned_target.loc = get_turf(user) - to_chat(user, "You take the target out of the stake.") - - pinned_target = null +// Basically they are for the firing range +/obj/structure/target_stake + name = "target stake" + desc = "A thin platform with negatively-magnetized wheels." + icon = 'icons/obj/objects.dmi' + icon_state = "target_stake" + density = TRUE + w_class = ITEMSIZE_HUGE + var/obj/item/target/pinned_target // the current pinned target + +/obj/structure/target_stake/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + // Move the pinned target along with the stake + if(pinned_target in view(3, src)) + pinned_target.forceMove(loc) + + else // Sanity check: if the pinned target can't be found in immediate view + pinned_target = null + density = TRUE + +/obj/structure/target_stake/attackby(obj/item/W as obj, mob/user as mob) + // Putting objects on the stake. Most importantly, targets + if(pinned_target) + return // get rid of that pinned target first! + + if(istype(W, /obj/item/target)) + density = FALSE + W.density = TRUE + user.remove_from_mob(W) + W.loc = loc + W.layer = ABOVE_JUNK_LAYER + pinned_target = W + to_chat(user, "You slide the target into the stake.") + return + +/obj/structure/target_stake/attack_hand(mob/user as mob) + // taking pinned targets off! + if(pinned_target) + density = TRUE + pinned_target.density = FALSE + pinned_target.layer = OBJ_LAYER + + pinned_target.loc = user.loc + if(ishuman(user)) + if(!user.get_active_hand()) + user.put_in_hands(pinned_target) + to_chat(user, "You take the target out of the stake.") + else + pinned_target.loc = get_turf(user) + to_chat(user, "You take the target out of the stake.") + + pinned_target = null diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 2260f42fb9f..1e40f249646 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -1,704 +1,704 @@ -/obj/structure/window - name = "window" - desc = "A window." - icon = 'icons/obj/structures_vr.dmi' // VOREStation Edit - New icons - density = TRUE - can_atmos_pass = ATMOS_PASS_PROC - w_class = ITEMSIZE_NORMAL - - layer = WINDOW_LAYER - pressure_resistance = 4*ONE_ATMOSPHERE - anchored = TRUE - flags = ON_BORDER - var/maxhealth = 14.0 - var/maximal_heat = T0C + 100 // Maximal heat before this window begins taking damage from fire - var/damage_per_fire_tick = 2.0 // Amount of damage per fire tick. Regular windows are not fireproof so they might as well break quickly. - var/health - var/force_threshold = 0 - var/ini_dir = null - var/state = 2 - var/reinf = 0 - var/basestate - var/shardtype = /obj/item/weapon/material/shard - var/glasstype = null // Set this in subtypes. Null is assumed strange or otherwise impossible to dismantle, such as for shuttle glass. - var/silicate = 0 // number of units of silicate - var/fulltile = FALSE // Set to true on full-tile variants. - -/obj/structure/window/examine(mob/user) - . = ..() - - if(health == maxhealth) - . += "It looks fully intact." - else - var/perc = health / maxhealth - if(perc > 0.75) - . += "It has a few cracks." - else if(perc > 0.5) - . += "It looks slightly damaged." - else if(perc > 0.25) - . += "It looks moderately damaged." - else - . += "It looks heavily damaged." - if(silicate) - if (silicate < 30) - . += "It has a thin layer of silicate." - else if (silicate < 70) - . += "It is covered in silicate." - else - . += "There is a thick layer of silicate covering it." - -/obj/structure/window/examine_icon() - return icon(icon=initial(icon),icon_state=initial(icon_state)) - -/obj/structure/window/take_damage(var/damage = 0, var/sound_effect = 1) - var/initialhealth = health - - if(silicate) - damage = damage * (1 - silicate / 200) - - health = max(0, health - damage) - - if(health <= 0) - shatter() - else - if(sound_effect) - playsound(src, 'sound/effects/Glasshit.ogg', 100, 1) - if(health < maxhealth / 4 && initialhealth >= maxhealth / 4) - visible_message("[src] looks like it's about to shatter!" ) - update_icon() - else if(health < maxhealth / 2 && initialhealth >= maxhealth / 2) - visible_message("[src] looks seriously damaged!" ) - update_icon() - else if(health < maxhealth * 3/4 && initialhealth >= maxhealth * 3/4) - visible_message("Cracks begin to appear in [src]!" ) - update_icon() - return - -/obj/structure/window/proc/apply_silicate(var/amount) - if(health < maxhealth) // Mend the damage - health = min(health + amount * 3, maxhealth) - if(health == maxhealth) - visible_message("[src] looks fully repaired." ) - else // Reinforce - silicate = min(silicate + amount, 100) - updateSilicate() - -/obj/structure/window/proc/updateSilicate() - cut_overlays() - update_icon() - - var/image/img = image(src) - img.color = "#ffffff" - img.alpha = silicate * 255 / 100 - add_overlay(img) - -/obj/structure/window/proc/shatter(var/display_message = 1) - playsound(src, "shatter", 70, 1) - if(display_message) - visible_message("[src] shatters!") - new shardtype(loc) - if(reinf) - new /obj/item/stack/rods(loc) - if(is_fulltile()) - new shardtype(loc) //todo pooling? - if(reinf) - new /obj/item/stack/rods(loc) - qdel(src) - return - - -/obj/structure/window/bullet_act(var/obj/item/projectile/Proj) - - var/proj_damage = Proj.get_structure_damage() - if(!proj_damage) return - - ..() - take_damage(proj_damage) - return - - -/obj/structure/window/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - return - if(2.0) - shatter(0) - return - if(3.0) - if(prob(50)) - shatter(0) - return - -/obj/structure/window/blob_act() - take_damage(50) - -/obj/structure/window/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return TRUE - if(is_fulltile()) - return FALSE //full tile window, you can't move into it! - if(get_dir(mover, target) == reverse_dir[dir]) // From elsewhere to here, can't move against our dir - return !density - else - return TRUE - -/obj/structure/window/Uncross(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return TRUE - if(get_dir(mover, target) == dir) // From here to elsewhere, can't move in our dir - return !density - else - return TRUE - -/obj/structure/window/CanZASPass(turf/T, is_zone) - if(is_fulltile() || get_dir(T, loc) == turn(dir, 180)) // Make sure we're handling the border correctly. - return !anchored // If it's anchored, it'll block air. - return TRUE // Don't stop airflow from the other sides. - -/obj/structure/window/hitby(AM as mob|obj) - ..() - visible_message("[src] was hit by [AM].") - var/tforce = 0 - if(ismob(AM)) - tforce = 40 - else if(isobj(AM)) - var/obj/item/I = AM - tforce = I.throwforce - if(reinf) tforce *= 0.25 - if(health - tforce <= 7 && !reinf) - anchored = FALSE - update_verbs() - update_nearby_icons() - step(src, get_dir(AM, src)) - take_damage(tforce) - -/obj/structure/window/attack_tk(mob/user as mob) - user.visible_message("Something knocks on [src].") - playsound(src, 'sound/effects/Glasshit.ogg', 50, 1) - -/obj/structure/window/attack_hand(mob/user as mob) - user.setClickCooldown(user.get_attack_speed()) - if(HULK in user.mutations) - user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!")) - user.visible_message("[user] smashes through [src]!") - user.do_attack_animation(src) - shatter() - - else if (user.a_intent == I_HURT) - - if (istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - if(H.species.can_shred(H)) - attack_generic(H,25) - return - - playsound(src, 'sound/effects/glassknock.ogg', 80, 1) - user.do_attack_animation(src) - user.visible_message("\The [user] bangs against \the [src]!", - "You bang against \the [src]!", - "You hear a banging sound.") - else - playsound(src, 'sound/effects/glassknock.ogg', 80, 1) - user.visible_message("[user.name] knocks on the [src.name].", - "You knock on the [src.name].", - "You hear a knocking sound.") - return - -/obj/structure/window/attack_generic(var/mob/user, var/damage) - user.setClickCooldown(user.get_attack_speed()) - if(!damage) - return - if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) - visible_message("[user] smashes into [src]!") - if(reinf) - damage = damage / 2 - take_damage(damage) - else - visible_message("\The [user] bonks \the [src] harmlessly.") - user.do_attack_animation(src) - return 1 - -/obj/structure/window/attackby(obj/item/W as obj, mob/user as mob) - if(!istype(W)) return//I really wish I did not need this - - // Fixing. - if(W.has_tool_quality(TOOL_WELDER) && user.a_intent == I_HELP) - var/obj/item/weapon/weldingtool/WT = W.get_welder() - if(health < maxhealth) - if(WT.remove_fuel(1 ,user)) - to_chat(user, "You begin repairing [src]...") - playsound(src, WT.usesound, 50, 1) - if(do_after(user, 40 * WT.toolspeed, target = src)) - health = maxhealth - // playsound(src, 'sound/items/Welder.ogg', 50, 1) - update_icon() - to_chat(user, "You repair [src].") - else - to_chat(user, "[src] is already in good condition!") - return - - // Slamming. - if (istype(W, /obj/item/weapon/grab) && get_dist(src,user)<2) - var/obj/item/weapon/grab/G = W - if(istype(G.affecting,/mob/living)) - var/mob/living/M = G.affecting - var/state = G.state - qdel(W) //gotta delete it here because if window breaks, it won't get deleted - switch (state) - if(1) - M.visible_message("[user] slams [M] against \the [src]!") - M.apply_damage(7) - hit(10) - if(2) - M.visible_message("[user] bashes [M] against \the [src]!") - if (prob(50)) - M.Weaken(1) - M.apply_damage(10) - hit(25) - if(3) - M.visible_message("[user] crushes [M] against \the [src]!") - M.Weaken(5) - M.apply_damage(20) - hit(50) - return - - if(W.flags & NOBLUDGEON) return - - if(W.has_tool_quality(TOOL_SCREWDRIVER)) - if(reinf && state >= 1) - state = 3 - state - update_nearby_icons() - playsound(src, W.usesound, 75, 1) - to_chat(user, "You have [state == 1 ? "un" : ""]fastened the window [state ? "from" : "to"] the frame.") - else if(reinf && state == 0) - anchored = !anchored - update_nearby_tiles(need_rebuild=1) - update_nearby_icons() - update_verbs() - playsound(src, W.usesound, 75, 1) - to_chat(user, "You have [anchored ? "" : "un"]fastened the frame [anchored ? "to" : "from"] the floor.") - else if(!reinf) - anchored = !anchored - update_nearby_tiles(need_rebuild=1) - update_nearby_icons() - update_verbs() - playsound(src, W.usesound, 75, 1) - to_chat(user, "You have [anchored ? "" : "un"]fastened the window [anchored ? "to" : "from"] the floor.") - else if(W.has_tool_quality(TOOL_CROWBAR) && reinf && state <= 1) - state = 1 - state - playsound(src, W.usesound, 75, 1) - to_chat(user, "You have pried the window [state ? "into" : "out of"] the frame.") - else if(W.has_tool_quality(TOOL_WRENCH) && !anchored && (!state || !reinf)) - if(!glasstype) - to_chat(user, "You're not sure how to dismantle \the [src] properly.") - else - playsound(src, W.usesound, 75, 1) - visible_message("[user] dismantles \the [src].") - var/obj/item/stack/material/mats = new glasstype(loc) - if(is_fulltile()) - mats.set_amount(4) - qdel(src) - else if(istype(W, /obj/item/stack/cable_coil) && reinf && state == 0 && !istype(src, /obj/structure/window/reinforced/polarized)) - var/obj/item/stack/cable_coil/C = W - if (C.use(1)) - playsound(src, 'sound/effects/sparks1.ogg', 75, 1) - user.visible_message( \ - "\The [user] begins to wire \the [src] for electrochromic tinting.", \ - "You begin to wire \the [src] for electrochromic tinting.", \ - "You hear sparks.") - if(do_after(user, 20 * C.toolspeed, src) && state == 0) - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - var/obj/structure/window/reinforced/polarized/P = new(loc, dir) - if(is_fulltile()) - P.fulltile = TRUE - P.icon_state = "fwindow" - P.maxhealth = maxhealth - P.health = health - P.state = state - P.anchored = anchored - qdel(src) - else if(istype(W,/obj/item/frame) && anchored) - var/obj/item/frame/F = W - F.try_build(src, user) - else - user.setClickCooldown(user.get_attack_speed(W)) - if(W.damtype == BRUTE || W.damtype == BURN) - user.do_attack_animation(src) - hit(W.force) - if(health <= 7) - anchored = FALSE - update_nearby_icons() - step(src, get_dir(user, src)) - else - playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) - ..() - return - -/obj/structure/window/proc/hit(var/damage, var/sound_effect = 1) - if(damage < force_threshold || force_threshold < 0) - return - if(reinf) damage *= 0.5 - take_damage(damage) - return - - -/obj/structure/window/verb/rotate_counterclockwise() - set name = "Rotate Window Counterclockwise" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) - return 0 - - if(is_fulltile()) - return 0 - - if(anchored) - to_chat(usr, "It is fastened to the floor therefore you can't rotate it!") - return 0 - - update_nearby_tiles(need_rebuild=1) //Compel updates before - src.set_dir(turn(src.dir, 90)) - updateSilicate() - update_nearby_tiles(need_rebuild=1) - return - - -/obj/structure/window/verb/rotate_clockwise() - set name = "Rotate Window Clockwise" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) - return 0 - - if(is_fulltile()) - return 0 - - if(anchored) - to_chat(usr, "It is fastened to the floor therefore you can't rotate it!") - return 0 - - update_nearby_tiles(need_rebuild=1) //Compel updates before - src.set_dir(turn(src.dir, 270)) - updateSilicate() - update_nearby_tiles(need_rebuild=1) - return - -/obj/structure/window/New(Loc, start_dir=null, constructed=0) - ..() - - if (start_dir) - set_dir(start_dir) - - //player-constructed windows - if (constructed) - anchored = FALSE - state = 0 - update_verbs() - - health = maxhealth - - ini_dir = dir - - update_nearby_tiles(need_rebuild=1) - update_nearby_icons() - - -/obj/structure/window/Destroy() - density = FALSE - update_nearby_tiles() - var/turf/location = loc - . = ..() - for(var/obj/structure/window/W in orange(location, 1)) - W.update_icon() - -/obj/structure/window/Move() - var/ini_dir = dir - update_nearby_tiles(need_rebuild=1) - . = ..() - set_dir(ini_dir) - update_nearby_tiles(need_rebuild=1) - -//checks if this window is full-tile one -/obj/structure/window/proc/is_fulltile() - return fulltile - -/obj/structure/window/is_between_turfs(var/turf/origin, var/turf/target) - if(is_fulltile()) - return TRUE - return ..() - -//This proc is used to update the icons of nearby windows. It should not be confused with update_nearby_tiles(), which is an atmos proc! -/obj/structure/window/proc/update_nearby_icons() - update_icon() - for(var/obj/structure/window/W in orange(src, 1)) - W.update_icon() - -//Updates the availabiliy of the rotation verbs -/obj/structure/window/proc/update_verbs() - if(anchored || is_fulltile()) - verbs -= /obj/structure/window/verb/rotate_counterclockwise - verbs -= /obj/structure/window/verb/rotate_clockwise - else if(!is_fulltile()) - verbs += /obj/structure/window/verb/rotate_counterclockwise - verbs += /obj/structure/window/verb/rotate_clockwise - -//merges adjacent full-tile windows into one (blatant ripoff from game/smoothwall.dm) -/obj/structure/window/update_icon() - //A little cludge here, since I don't know how it will work with slim windows. Most likely VERY wrong. - //this way it will only update full-tile ones - cut_overlays() - if(!is_fulltile()) - // Rotate the sprite somewhat so non-fulltiled windows can be seen as needing repair. - var/full_tilt_degrees = 15 - var/tilt_to_apply = abs((health / maxhealth) - 1) - if(tilt_to_apply && prob(50)) - tilt_to_apply = -tilt_to_apply - adjust_rotation(LERP(0, full_tilt_degrees, tilt_to_apply)) - - icon_state = "[basestate]" - return - else - flags = 0 // Removes ON_BORDER and OPPOSITE_OPACITY - var/list/dirs = list() - if(anchored) - for(var/obj/structure/window/W in orange(src,1)) - if(W.anchored && W.density && W.glasstype == src.glasstype && W.is_fulltile()) //Only counts anchored, not-destroyed fill-tile windows. - dirs += get_dir(src, W) - - var/list/connections = dirs_to_corner_states(dirs) - - icon_state = "" - for(var/i = 1 to 4) - var/image/I = image(icon, "[basestate][connections[i]]", dir = 1<<(i-1)) - add_overlay(I) - - // Damage overlays. - var/ratio = health / maxhealth - ratio = CEILING(ratio * 4, 1) * 25 - - if(ratio > 75) - return - var/image/I = image(icon, "damage[ratio]", layer = layer + 0.1) - add_overlay(I) - - return - -/obj/structure/window/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature > maximal_heat) - hit(damage_per_fire_tick, 0) - ..() - - - -/obj/structure/window/basic - desc = "It looks thin and flimsy. A few knocks with... almost anything, really should shatter it." - icon_state = "window" - basestate = "window" - glasstype = /obj/item/stack/material/glass - maximal_heat = T0C + 100 - damage_per_fire_tick = 2.0 - maxhealth = 12.0 - force_threshold = 3 - -/obj/structure/window/basic/full - icon_state = "window-full" - maxhealth = 24 - fulltile = TRUE - flags = 0 - -/obj/structure/window/phoronbasic - name = "phoron window" - desc = "A borosilicate alloy window. It seems to be quite strong." - basestate = "phoronwindow" - icon_state = "phoronwindow" - shardtype = /obj/item/weapon/material/shard/phoron - glasstype = /obj/item/stack/material/glass/phoronglass - maximal_heat = T0C + 2000 - damage_per_fire_tick = 1.0 - maxhealth = 40.0 - force_threshold = 5 - -/obj/structure/window/phoronbasic/full - icon_state = "phoronwindow-full" - maxhealth = 80 - fulltile = TRUE - flags = 0 - -/obj/structure/window/phoronreinforced - name = "reinforced borosilicate window" - desc = "A borosilicate alloy window, with rods supporting it. It seems to be very strong." - basestate = "phoronrwindow" - icon_state = "phoronrwindow" - shardtype = /obj/item/weapon/material/shard/phoron - glasstype = /obj/item/stack/material/glass/phoronrglass - reinf = 1 - maximal_heat = T0C + 4000 - damage_per_fire_tick = 1.0 // This should last for 80 fire ticks if the window is not damaged at all. The idea is that borosilicate windows have something like ablative layer that protects them for a while. - maxhealth = 80.0 - force_threshold = 10 - -/obj/structure/window/phoronreinforced/full - icon_state = "phoronrwindow-full" - maxhealth = 160 - fulltile = TRUE - flags = 0 - -/obj/structure/window/reinforced - name = "reinforced window" - desc = "It looks rather strong. Might take a few good hits to shatter it." - icon_state = "rwindow" - basestate = "rwindow" - maxhealth = 40.0 - reinf = 1 - maximal_heat = T0C + 750 - damage_per_fire_tick = 2.0 - glasstype = /obj/item/stack/material/glass/reinforced - force_threshold = 6 - -/obj/structure/window/reinforced/full - icon_state = "rwindow-full" - maxhealth = 80 - fulltile = TRUE - flags = 0 - -/obj/structure/window/reinforced/tinted - name = "tinted window" - desc = "It looks rather strong and opaque. Might take a few good hits to shatter it." - icon_state = "twindow" - basestate = "twindow" - opacity = 1 - -/obj/structure/window/reinforced/tinted/frosted - name = "frosted window" - desc = "It looks rather strong and frosted over. Looks like it might take a few less hits then a normal reinforced window." - icon_state = "fwindow" - basestate = "fwindow" - maxhealth = 30 - force_threshold = 5 - -/obj/structure/window/shuttle - name = "shuttle window" - desc = "It looks rather strong. Might take a few good hits to shatter it." - icon = 'icons/obj/podwindows.dmi' - icon_state = "window" - basestate = "window" - maxhealth = 40 - reinf = 1 - basestate = "w" - dir = 5 - force_threshold = 7 - -/obj/structure/window/reinforced/polarized - name = "electrochromic window" - desc = "Adjusts its tint with voltage. Might take a few good hits to shatter it." - var/id - -/obj/structure/window/reinforced/polarized/full - icon_state = "rwindow-full" - maxhealth = 80 - fulltile = TRUE - flags = 0 - -/obj/structure/window/reinforced/polarized/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/device/multitool) && !anchored) // Only allow programming if unanchored! - var/obj/item/device/multitool/MT = W - // First check if they have a windowtint button buffered - if(istype(MT.connectable, /obj/machinery/button/windowtint)) - var/obj/machinery/button/windowtint/buffered_button = MT.connectable - src.id = buffered_button.id - to_chat(user, "\The [src] is linked to \the [buffered_button] with ID '[id]'.") - return TRUE - // Otherwise fall back to asking them... and remind them what the current ID is. - if(id) - to_chat(user, "The window's current ID is [id].") - var/t = sanitizeSafe(input(user, "Enter the new ID for the window.", src.name, null), MAX_NAME_LEN) - if(t && in_range(src, user)) - src.id = t - to_chat(user, "The new ID of \the [src] is '[id]'.") - return TRUE - . = ..() - -/obj/structure/window/reinforced/polarized/proc/toggle() - if(opacity) - animate(src, color="#FFFFFF", time=5) - set_opacity(0) - else - animate(src, color="#222222", time=5) - set_opacity(1) - var/turf/T = get_turf(src) - T.recalculate_directional_opacity() - -/obj/machinery/button/windowtint - name = "window tint control" - icon = 'icons/obj/stationobjs_vr.dmi' // VOREStation Edit - New icons - icon_state = "light0" - desc = "A remote control switch for polarized windows." - var/range = 7 - circuit = /obj/item/weapon/circuitboard/electrochromic - -/obj/machinery/button/windowtint/attack_hand(mob/user as mob) - if(..()) - return 1 - - toggle_tint() - -/obj/machinery/button/windowtint/proc/toggle_tint() - use_power(5) - - active = !active - update_icon() - - for(var/obj/structure/window/reinforced/polarized/W in range(src,range)) - if (W.id == src.id || !W.id) - spawn(0) - W.toggle() - return - -/obj/machinery/button/windowtint/power_change() - ..() - if(active && !powered(power_channel)) - toggle_tint() - -/obj/machinery/button/windowtint/update_icon() - icon_state = "light[active]" - -/obj/machinery/button/windowtint/attackby(obj/item/W as obj, mob/user as mob) - if(default_deconstruction_screwdriver(user, W)) - return - else if(alarm_deconstruction_wirecutters(user, W)) - return - else if(istype(W, /obj/item/device/multitool)) - var/obj/item/device/multitool/MT = W - if(!id) - // If no ID is set yet (newly built button?) let them select an ID for first-time use! - var/t = sanitizeSafe(tgui_input_text(user, "Enter an ID for \the [src].", src.name, null, MAX_NAME_LEN), MAX_NAME_LEN) - if (t && in_range(src, user)) - src.id = t - to_chat(user, "The new ID of \the [src] is '[id]'. To reset this, rebuild the control.") - if(id) - // It already has an ID (or they just set one), buffer it for copying to windows. - to_chat(user, "You store \the [src] ID ('[id]') in \the [MT]'s buffer!") - MT.connectable = src - MT.update_icon() - return TRUE - . = ..() - -/obj/structure/window/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_DECONSTRUCT) - return list( - RCD_VALUE_MODE = RCD_DECONSTRUCT, - RCD_VALUE_DELAY = 5 SECONDS, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 - ) - -/obj/structure/window/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_DECONSTRUCT) - to_chat(user, span("notice", "You deconstruct \the [src].")) - qdel(src) - return TRUE - return FALSE +/obj/structure/window + name = "window" + desc = "A window." + icon = 'icons/obj/structures_vr.dmi' // VOREStation Edit - New icons + density = TRUE + can_atmos_pass = ATMOS_PASS_PROC + w_class = ITEMSIZE_NORMAL + + layer = WINDOW_LAYER + pressure_resistance = 4*ONE_ATMOSPHERE + anchored = TRUE + flags = ON_BORDER + var/maxhealth = 14.0 + var/maximal_heat = T0C + 100 // Maximal heat before this window begins taking damage from fire + var/damage_per_fire_tick = 2.0 // Amount of damage per fire tick. Regular windows are not fireproof so they might as well break quickly. + var/health + var/force_threshold = 0 + var/ini_dir = null + var/state = 2 + var/reinf = 0 + var/basestate + var/shardtype = /obj/item/weapon/material/shard + var/glasstype = null // Set this in subtypes. Null is assumed strange or otherwise impossible to dismantle, such as for shuttle glass. + var/silicate = 0 // number of units of silicate + var/fulltile = FALSE // Set to true on full-tile variants. + +/obj/structure/window/examine(mob/user) + . = ..() + + if(health == maxhealth) + . += "It looks fully intact." + else + var/perc = health / maxhealth + if(perc > 0.75) + . += "It has a few cracks." + else if(perc > 0.5) + . += "It looks slightly damaged." + else if(perc > 0.25) + . += "It looks moderately damaged." + else + . += "It looks heavily damaged." + if(silicate) + if (silicate < 30) + . += "It has a thin layer of silicate." + else if (silicate < 70) + . += "It is covered in silicate." + else + . += "There is a thick layer of silicate covering it." + +/obj/structure/window/examine_icon() + return icon(icon=initial(icon),icon_state=initial(icon_state)) + +/obj/structure/window/take_damage(var/damage = 0, var/sound_effect = 1) + var/initialhealth = health + + if(silicate) + damage = damage * (1 - silicate / 200) + + health = max(0, health - damage) + + if(health <= 0) + shatter() + else + if(sound_effect) + playsound(src, 'sound/effects/Glasshit.ogg', 100, 1) + if(health < maxhealth / 4 && initialhealth >= maxhealth / 4) + visible_message("[src] looks like it's about to shatter!" ) + update_icon() + else if(health < maxhealth / 2 && initialhealth >= maxhealth / 2) + visible_message("[src] looks seriously damaged!" ) + update_icon() + else if(health < maxhealth * 3/4 && initialhealth >= maxhealth * 3/4) + visible_message("Cracks begin to appear in [src]!" ) + update_icon() + return + +/obj/structure/window/proc/apply_silicate(var/amount) + if(health < maxhealth) // Mend the damage + health = min(health + amount * 3, maxhealth) + if(health == maxhealth) + visible_message("[src] looks fully repaired." ) + else // Reinforce + silicate = min(silicate + amount, 100) + updateSilicate() + +/obj/structure/window/proc/updateSilicate() + cut_overlays() + update_icon() + + var/image/img = image(src) + img.color = "#ffffff" + img.alpha = silicate * 255 / 100 + add_overlay(img) + +/obj/structure/window/proc/shatter(var/display_message = 1) + playsound(src, "shatter", 70, 1) + if(display_message) + visible_message("[src] shatters!") + new shardtype(loc) + if(reinf) + new /obj/item/stack/rods(loc) + if(is_fulltile()) + new shardtype(loc) //todo pooling? + if(reinf) + new /obj/item/stack/rods(loc) + qdel(src) + return + + +/obj/structure/window/bullet_act(var/obj/item/projectile/Proj) + + var/proj_damage = Proj.get_structure_damage() + if(!proj_damage) return + + ..() + take_damage(proj_damage) + return + + +/obj/structure/window/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + return + if(2.0) + shatter(0) + return + if(3.0) + if(prob(50)) + shatter(0) + return + +/obj/structure/window/blob_act() + take_damage(50) + +/obj/structure/window/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return TRUE + if(is_fulltile()) + return FALSE //full tile window, you can't move into it! + if(get_dir(mover, target) == reverse_dir[dir]) // From elsewhere to here, can't move against our dir + return !density + else + return TRUE + +/obj/structure/window/Uncross(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return TRUE + if(get_dir(mover, target) == dir) // From here to elsewhere, can't move in our dir + return !density + else + return TRUE + +/obj/structure/window/CanZASPass(turf/T, is_zone) + if(is_fulltile() || get_dir(T, loc) == turn(dir, 180)) // Make sure we're handling the border correctly. + return !anchored // If it's anchored, it'll block air. + return TRUE // Don't stop airflow from the other sides. + +/obj/structure/window/hitby(AM as mob|obj) + ..() + visible_message("[src] was hit by [AM].") + var/tforce = 0 + if(ismob(AM)) + tforce = 40 + else if(isobj(AM)) + var/obj/item/I = AM + tforce = I.throwforce + if(reinf) tforce *= 0.25 + if(health - tforce <= 7 && !reinf) + anchored = FALSE + update_verbs() + update_nearby_icons() + step(src, get_dir(AM, src)) + take_damage(tforce) + +/obj/structure/window/attack_tk(mob/user as mob) + user.visible_message("Something knocks on [src].") + playsound(src, 'sound/effects/Glasshit.ogg', 50, 1) + +/obj/structure/window/attack_hand(mob/user as mob) + user.setClickCooldown(user.get_attack_speed()) + if(HULK in user.mutations) + user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!")) + user.visible_message("[user] smashes through [src]!") + user.do_attack_animation(src) + shatter() + + else if (user.a_intent == I_HURT) + + if (istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(H.species.can_shred(H)) + attack_generic(H,25) + return + + playsound(src, 'sound/effects/glassknock.ogg', 80, 1) + user.do_attack_animation(src) + user.visible_message("\The [user] bangs against \the [src]!", + "You bang against \the [src]!", + "You hear a banging sound.") + else + playsound(src, 'sound/effects/glassknock.ogg', 80, 1) + user.visible_message("[user.name] knocks on the [src.name].", + "You knock on the [src.name].", + "You hear a knocking sound.") + return + +/obj/structure/window/attack_generic(var/mob/user, var/damage) + user.setClickCooldown(user.get_attack_speed()) + if(!damage) + return + if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) + visible_message("[user] smashes into [src]!") + if(reinf) + damage = damage / 2 + take_damage(damage) + else + visible_message("\The [user] bonks \the [src] harmlessly.") + user.do_attack_animation(src) + return 1 + +/obj/structure/window/attackby(obj/item/W as obj, mob/user as mob) + if(!istype(W)) return//I really wish I did not need this + + // Fixing. + if(W.has_tool_quality(TOOL_WELDER) && user.a_intent == I_HELP) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(health < maxhealth) + if(WT.remove_fuel(1 ,user)) + to_chat(user, "You begin repairing [src]...") + playsound(src, WT.usesound, 50, 1) + if(do_after(user, 40 * WT.toolspeed, target = src)) + health = maxhealth + // playsound(src, 'sound/items/Welder.ogg', 50, 1) + update_icon() + to_chat(user, "You repair [src].") + else + to_chat(user, "[src] is already in good condition!") + return + + // Slamming. + if (istype(W, /obj/item/weapon/grab) && get_dist(src,user)<2) + var/obj/item/weapon/grab/G = W + if(istype(G.affecting,/mob/living)) + var/mob/living/M = G.affecting + var/state = G.state + qdel(W) //gotta delete it here because if window breaks, it won't get deleted + switch (state) + if(1) + M.visible_message("[user] slams [M] against \the [src]!") + M.apply_damage(7) + hit(10) + if(2) + M.visible_message("[user] bashes [M] against \the [src]!") + if (prob(50)) + M.Weaken(1) + M.apply_damage(10) + hit(25) + if(3) + M.visible_message("[user] crushes [M] against \the [src]!") + M.Weaken(5) + M.apply_damage(20) + hit(50) + return + + if(W.flags & NOBLUDGEON) return + + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(reinf && state >= 1) + state = 3 - state + update_nearby_icons() + playsound(src, W.usesound, 75, 1) + to_chat(user, "You have [state == 1 ? "un" : ""]fastened the window [state ? "from" : "to"] the frame.") + else if(reinf && state == 0) + anchored = !anchored + update_nearby_tiles(need_rebuild=1) + update_nearby_icons() + update_verbs() + playsound(src, W.usesound, 75, 1) + to_chat(user, "You have [anchored ? "" : "un"]fastened the frame [anchored ? "to" : "from"] the floor.") + else if(!reinf) + anchored = !anchored + update_nearby_tiles(need_rebuild=1) + update_nearby_icons() + update_verbs() + playsound(src, W.usesound, 75, 1) + to_chat(user, "You have [anchored ? "" : "un"]fastened the window [anchored ? "to" : "from"] the floor.") + else if(W.has_tool_quality(TOOL_CROWBAR) && reinf && state <= 1) + state = 1 - state + playsound(src, W.usesound, 75, 1) + to_chat(user, "You have pried the window [state ? "into" : "out of"] the frame.") + else if(W.has_tool_quality(TOOL_WRENCH) && !anchored && (!state || !reinf)) + if(!glasstype) + to_chat(user, "You're not sure how to dismantle \the [src] properly.") + else + playsound(src, W.usesound, 75, 1) + visible_message("[user] dismantles \the [src].") + var/obj/item/stack/material/mats = new glasstype(loc) + if(is_fulltile()) + mats.set_amount(4) + qdel(src) + else if(istype(W, /obj/item/stack/cable_coil) && reinf && state == 0 && !istype(src, /obj/structure/window/reinforced/polarized)) + var/obj/item/stack/cable_coil/C = W + if (C.use(1)) + playsound(src, 'sound/effects/sparks1.ogg', 75, 1) + user.visible_message( \ + "\The [user] begins to wire \the [src] for electrochromic tinting.", \ + "You begin to wire \the [src] for electrochromic tinting.", \ + "You hear sparks.") + if(do_after(user, 20 * C.toolspeed, src) && state == 0) + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + var/obj/structure/window/reinforced/polarized/P = new(loc, dir) + if(is_fulltile()) + P.fulltile = TRUE + P.icon_state = "fwindow" + P.maxhealth = maxhealth + P.health = health + P.state = state + P.anchored = anchored + qdel(src) + else if(istype(W,/obj/item/frame) && anchored) + var/obj/item/frame/F = W + F.try_build(src, user) + else + user.setClickCooldown(user.get_attack_speed(W)) + if(W.damtype == BRUTE || W.damtype == BURN) + user.do_attack_animation(src) + hit(W.force) + if(health <= 7) + anchored = FALSE + update_nearby_icons() + step(src, get_dir(user, src)) + else + playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) + ..() + return + +/obj/structure/window/proc/hit(var/damage, var/sound_effect = 1) + if(damage < force_threshold || force_threshold < 0) + return + if(reinf) damage *= 0.5 + take_damage(damage) + return + + +/obj/structure/window/verb/rotate_counterclockwise() + set name = "Rotate Window Counterclockwise" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) + return 0 + + if(is_fulltile()) + return 0 + + if(anchored) + to_chat(usr, "It is fastened to the floor therefore you can't rotate it!") + return 0 + + update_nearby_tiles(need_rebuild=1) //Compel updates before + src.set_dir(turn(src.dir, 90)) + updateSilicate() + update_nearby_tiles(need_rebuild=1) + return + + +/obj/structure/window/verb/rotate_clockwise() + set name = "Rotate Window Clockwise" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) + return 0 + + if(is_fulltile()) + return 0 + + if(anchored) + to_chat(usr, "It is fastened to the floor therefore you can't rotate it!") + return 0 + + update_nearby_tiles(need_rebuild=1) //Compel updates before + src.set_dir(turn(src.dir, 270)) + updateSilicate() + update_nearby_tiles(need_rebuild=1) + return + +/obj/structure/window/New(Loc, start_dir=null, constructed=0) + ..() + + if (start_dir) + set_dir(start_dir) + + //player-constructed windows + if (constructed) + anchored = FALSE + state = 0 + update_verbs() + + health = maxhealth + + ini_dir = dir + + update_nearby_tiles(need_rebuild=1) + update_nearby_icons() + + +/obj/structure/window/Destroy() + density = FALSE + update_nearby_tiles() + var/turf/location = loc + . = ..() + for(var/obj/structure/window/W in orange(location, 1)) + W.update_icon() + +/obj/structure/window/Move() + var/ini_dir = dir + update_nearby_tiles(need_rebuild=1) + . = ..() + set_dir(ini_dir) + update_nearby_tiles(need_rebuild=1) + +//checks if this window is full-tile one +/obj/structure/window/proc/is_fulltile() + return fulltile + +/obj/structure/window/is_between_turfs(var/turf/origin, var/turf/target) + if(is_fulltile()) + return TRUE + return ..() + +//This proc is used to update the icons of nearby windows. It should not be confused with update_nearby_tiles(), which is an atmos proc! +/obj/structure/window/proc/update_nearby_icons() + update_icon() + for(var/obj/structure/window/W in orange(src, 1)) + W.update_icon() + +//Updates the availabiliy of the rotation verbs +/obj/structure/window/proc/update_verbs() + if(anchored || is_fulltile()) + verbs -= /obj/structure/window/verb/rotate_counterclockwise + verbs -= /obj/structure/window/verb/rotate_clockwise + else if(!is_fulltile()) + verbs += /obj/structure/window/verb/rotate_counterclockwise + verbs += /obj/structure/window/verb/rotate_clockwise + +//merges adjacent full-tile windows into one (blatant ripoff from game/smoothwall.dm) +/obj/structure/window/update_icon() + //A little cludge here, since I don't know how it will work with slim windows. Most likely VERY wrong. + //this way it will only update full-tile ones + cut_overlays() + if(!is_fulltile()) + // Rotate the sprite somewhat so non-fulltiled windows can be seen as needing repair. + var/full_tilt_degrees = 15 + var/tilt_to_apply = abs((health / maxhealth) - 1) + if(tilt_to_apply && prob(50)) + tilt_to_apply = -tilt_to_apply + adjust_rotation(LERP(0, full_tilt_degrees, tilt_to_apply)) + + icon_state = "[basestate]" + return + else + flags = 0 // Removes ON_BORDER and OPPOSITE_OPACITY + var/list/dirs = list() + if(anchored) + for(var/obj/structure/window/W in orange(src,1)) + if(W.anchored && W.density && W.glasstype == src.glasstype && W.is_fulltile()) //Only counts anchored, not-destroyed fill-tile windows. + dirs += get_dir(src, W) + + var/list/connections = dirs_to_corner_states(dirs) + + icon_state = "" + for(var/i = 1 to 4) + var/image/I = image(icon, "[basestate][connections[i]]", dir = 1<<(i-1)) + add_overlay(I) + + // Damage overlays. + var/ratio = health / maxhealth + ratio = CEILING(ratio * 4, 1) * 25 + + if(ratio > 75) + return + var/image/I = image(icon, "damage[ratio]", layer = layer + 0.1) + add_overlay(I) + + return + +/obj/structure/window/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature > maximal_heat) + hit(damage_per_fire_tick, 0) + ..() + + + +/obj/structure/window/basic + desc = "It looks thin and flimsy. A few knocks with... almost anything, really should shatter it." + icon_state = "window" + basestate = "window" + glasstype = /obj/item/stack/material/glass + maximal_heat = T0C + 100 + damage_per_fire_tick = 2.0 + maxhealth = 12.0 + force_threshold = 3 + +/obj/structure/window/basic/full + icon_state = "window-full" + maxhealth = 24 + fulltile = TRUE + flags = 0 + +/obj/structure/window/phoronbasic + name = "phoron window" + desc = "A borosilicate alloy window. It seems to be quite strong." + basestate = "phoronwindow" + icon_state = "phoronwindow" + shardtype = /obj/item/weapon/material/shard/phoron + glasstype = /obj/item/stack/material/glass/phoronglass + maximal_heat = T0C + 2000 + damage_per_fire_tick = 1.0 + maxhealth = 40.0 + force_threshold = 5 + +/obj/structure/window/phoronbasic/full + icon_state = "phoronwindow-full" + maxhealth = 80 + fulltile = TRUE + flags = 0 + +/obj/structure/window/phoronreinforced + name = "reinforced borosilicate window" + desc = "A borosilicate alloy window, with rods supporting it. It seems to be very strong." + basestate = "phoronrwindow" + icon_state = "phoronrwindow" + shardtype = /obj/item/weapon/material/shard/phoron + glasstype = /obj/item/stack/material/glass/phoronrglass + reinf = 1 + maximal_heat = T0C + 4000 + damage_per_fire_tick = 1.0 // This should last for 80 fire ticks if the window is not damaged at all. The idea is that borosilicate windows have something like ablative layer that protects them for a while. + maxhealth = 80.0 + force_threshold = 10 + +/obj/structure/window/phoronreinforced/full + icon_state = "phoronrwindow-full" + maxhealth = 160 + fulltile = TRUE + flags = 0 + +/obj/structure/window/reinforced + name = "reinforced window" + desc = "It looks rather strong. Might take a few good hits to shatter it." + icon_state = "rwindow" + basestate = "rwindow" + maxhealth = 40.0 + reinf = 1 + maximal_heat = T0C + 750 + damage_per_fire_tick = 2.0 + glasstype = /obj/item/stack/material/glass/reinforced + force_threshold = 6 + +/obj/structure/window/reinforced/full + icon_state = "rwindow-full" + maxhealth = 80 + fulltile = TRUE + flags = 0 + +/obj/structure/window/reinforced/tinted + name = "tinted window" + desc = "It looks rather strong and opaque. Might take a few good hits to shatter it." + icon_state = "twindow" + basestate = "twindow" + opacity = 1 + +/obj/structure/window/reinforced/tinted/frosted + name = "frosted window" + desc = "It looks rather strong and frosted over. Looks like it might take a few less hits then a normal reinforced window." + icon_state = "fwindow" + basestate = "fwindow" + maxhealth = 30 + force_threshold = 5 + +/obj/structure/window/shuttle + name = "shuttle window" + desc = "It looks rather strong. Might take a few good hits to shatter it." + icon = 'icons/obj/podwindows.dmi' + icon_state = "window" + basestate = "window" + maxhealth = 40 + reinf = 1 + basestate = "w" + dir = 5 + force_threshold = 7 + +/obj/structure/window/reinforced/polarized + name = "electrochromic window" + desc = "Adjusts its tint with voltage. Might take a few good hits to shatter it." + var/id + +/obj/structure/window/reinforced/polarized/full + icon_state = "rwindow-full" + maxhealth = 80 + fulltile = TRUE + flags = 0 + +/obj/structure/window/reinforced/polarized/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/device/multitool) && !anchored) // Only allow programming if unanchored! + var/obj/item/device/multitool/MT = W + // First check if they have a windowtint button buffered + if(istype(MT.connectable, /obj/machinery/button/windowtint)) + var/obj/machinery/button/windowtint/buffered_button = MT.connectable + src.id = buffered_button.id + to_chat(user, "\The [src] is linked to \the [buffered_button] with ID '[id]'.") + return TRUE + // Otherwise fall back to asking them... and remind them what the current ID is. + if(id) + to_chat(user, "The window's current ID is [id].") + var/t = sanitizeSafe(input(user, "Enter the new ID for the window.", src.name, null), MAX_NAME_LEN) + if(t && in_range(src, user)) + src.id = t + to_chat(user, "The new ID of \the [src] is '[id]'.") + return TRUE + . = ..() + +/obj/structure/window/reinforced/polarized/proc/toggle() + if(opacity) + animate(src, color="#FFFFFF", time=5) + set_opacity(0) + else + animate(src, color="#222222", time=5) + set_opacity(1) + var/turf/T = get_turf(src) + T.recalculate_directional_opacity() + +/obj/machinery/button/windowtint + name = "window tint control" + icon = 'icons/obj/stationobjs_vr.dmi' // VOREStation Edit - New icons + icon_state = "light0" + desc = "A remote control switch for polarized windows." + var/range = 7 + circuit = /obj/item/weapon/circuitboard/electrochromic + +/obj/machinery/button/windowtint/attack_hand(mob/user as mob) + if(..()) + return 1 + + toggle_tint() + +/obj/machinery/button/windowtint/proc/toggle_tint() + use_power(5) + + active = !active + update_icon() + + for(var/obj/structure/window/reinforced/polarized/W in range(src,range)) + if (W.id == src.id || !W.id) + spawn(0) + W.toggle() + return + +/obj/machinery/button/windowtint/power_change() + ..() + if(active && !powered(power_channel)) + toggle_tint() + +/obj/machinery/button/windowtint/update_icon() + icon_state = "light[active]" + +/obj/machinery/button/windowtint/attackby(obj/item/W as obj, mob/user as mob) + if(default_deconstruction_screwdriver(user, W)) + return + else if(alarm_deconstruction_wirecutters(user, W)) + return + else if(istype(W, /obj/item/device/multitool)) + var/obj/item/device/multitool/MT = W + if(!id) + // If no ID is set yet (newly built button?) let them select an ID for first-time use! + var/t = sanitizeSafe(tgui_input_text(user, "Enter an ID for \the [src].", src.name, null, MAX_NAME_LEN), MAX_NAME_LEN) + if (t && in_range(src, user)) + src.id = t + to_chat(user, "The new ID of \the [src] is '[id]'. To reset this, rebuild the control.") + if(id) + // It already has an ID (or they just set one), buffer it for copying to windows. + to_chat(user, "You store \the [src] ID ('[id]') in \the [MT]'s buffer!") + MT.connectable = src + MT.update_icon() + return TRUE + . = ..() + +/obj/structure/window/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_DECONSTRUCT) + return list( + RCD_VALUE_MODE = RCD_DECONSTRUCT, + RCD_VALUE_DELAY = 5 SECONDS, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 + ) + +/obj/structure/window/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_DECONSTRUCT) + to_chat(user, span("notice", "You deconstruct \the [src].")) + qdel(src) + return TRUE + return FALSE diff --git a/code/game/response_team.dm b/code/game/response_team.dm index 2606ae0b756..a50d4342072 100644 --- a/code/game/response_team.dm +++ b/code/game/response_team.dm @@ -1,138 +1,138 @@ -//STRIKE TEAMS -//Thanks to Kilakk for the admin-button portion of this code. - -var/global/send_emergency_team = 0 // Used for automagic response teams - // 'admin_emergency_team' for admin-spawned response teams -var/ert_base_chance = 10 // Default base chance. Will be incremented by increment ERT chance. -var/can_call_ert -var/silent_ert = 0 - -/client/proc/response_team() - set name = "Dispatch Emergency Response Team" - set category = "Special Verbs" - set desc = "Send an emergency response team to the station" - - if(!holder) - to_chat(usr, "Only administrators may use this command.") - return - if(!ticker) - to_chat(usr, "The game hasn't started yet!") - return - if(ticker.current_state == 1) - to_chat(usr, "The round hasn't started yet!") - return - if(send_emergency_team) - to_chat(usr, "[using_map.boss_name] has already dispatched an emergency response team!") - return - if(tgui_alert(usr, "Do you want to dispatch an Emergency Response Team?","ERT",list("Yes","No")) != "Yes") - return - if(tgui_alert(usr, "Do you want this Response Team to be announced?","ERT",list("Yes","No")) != "Yes") - silent_ert = 1 - if(get_security_level() != "red") // Allow admins to reconsider if the alert level isn't Red - switch(tgui_alert(usr, "The station is not in red alert. Do you still want to dispatch a response team?","ERT",list("Yes","No"))) - if("No") - return - if(send_emergency_team) - to_chat(usr, "Looks like somebody beat you to it!") - return - - message_admins("[key_name_admin(usr)] is dispatching an Emergency Response Team.", 1) - admin_chat_message(message = "[key_name(usr)] is dispatching an Emergency Response Team", color = "#CC2222") //VOREStation Add - log_admin("[key_name(usr)] used Dispatch Response Team.") - trigger_armed_response_team(1) - -/client/verb/JoinResponseTeam() - - set name = "Join Response Team" - set category = "IC" - - if(!MayRespawn(1)) - to_chat(usr, "You cannot join the response team at this time.") - return - - if(istype(usr,/mob/observer/dead) || istype(usr,/mob/new_player)) - if(!send_emergency_team) - to_chat(usr, "No emergency response team is currently being sent.") - return - if(jobban_isbanned(usr, "Syndicate") || jobban_isbanned(usr, "Emergency Response Team") || jobban_isbanned(usr, "Security Officer")) - to_chat(usr, "You are jobbanned from the emergency reponse team!") - return - if(ert.current_antagonists.len >= ert.hard_cap) - to_chat(usr, "The emergency response team is already full!") - return - ert.create_default(usr) - else - to_chat(usr, "You need to be an observer or new player to use this.") - -// returns a number of dead players in % -/proc/percentage_dead() - var/total = 0 - var/deadcount = 0 - for(var/mob/living/carbon/human/H in mob_list) - if(H.client) // Monkeys and mice don't have a client, amirite? - if(H.stat == 2) deadcount++ - total++ - - if(total == 0) return 0 - else return round(100 * deadcount / total) - -// counts the number of antagonists in % -/proc/percentage_antagonists() - var/total = 0 - var/antagonists = 0 - for(var/mob/living/carbon/human/H in mob_list) - if(is_special_character(H) >= 1) - antagonists++ - total++ - - if(total == 0) return 0 - else return round(100 * antagonists / total) - -// Increments the ERT chance automatically, so that the later it is in the round, -// the more likely an ERT is to be able to be called. -/proc/increment_ert_chance() - while(send_emergency_team == 0) // There is no ERT at the time. - if(get_security_level() == "green") - ert_base_chance += 1 - if(get_security_level() == "yellow") - ert_base_chance += 1 - if(get_security_level() == "violet") - ert_base_chance += 2 - if(get_security_level() == "orange") - ert_base_chance += 2 - if(get_security_level() == "blue") - ert_base_chance += 2 - if(get_security_level() == "red") - ert_base_chance += 3 - if(get_security_level() == "delta") - ert_base_chance += 10 // Need those big guns - sleep(600 * 3) // Minute * Number of Minutes - - -/proc/trigger_armed_response_team(var/force = 0) - if(!can_call_ert && !force) - return - if(send_emergency_team) - return - - var/send_team_chance = ert_base_chance // Is incremented by increment_ert_chance. - send_team_chance += 2*percentage_dead() // the more people are dead, the higher the chance - send_team_chance += percentage_antagonists() // the more antagonists, the higher the chance - send_team_chance = min(send_team_chance, 100) - - if(force) send_team_chance = 100 - - // there's only a certain chance a team will be sent - if(!prob(send_team_chance)) - command_announcement.Announce("It would appear that an emergency response team was requested for [station_name()]. Unfortunately, we were unable to send one at this time.", "[using_map.boss_name]") - can_call_ert = 0 // Only one call per round, ladies. - return - if(silent_ert == 0) - command_announcement.Announce("It would appear that an emergency response team was requested for [station_name()]. We will prepare and send one as soon as possible.", "[using_map.boss_name]") - - can_call_ert = 0 // Only one call per round, gentleman. - send_emergency_team = 1 - consider_ert_load() //VOREStation Add - - sleep(600 * 5) - send_emergency_team = 0 // Can no longer join the ERT. +//STRIKE TEAMS +//Thanks to Kilakk for the admin-button portion of this code. + +var/global/send_emergency_team = 0 // Used for automagic response teams + // 'admin_emergency_team' for admin-spawned response teams +var/ert_base_chance = 10 // Default base chance. Will be incremented by increment ERT chance. +var/can_call_ert +var/silent_ert = 0 + +/client/proc/response_team() + set name = "Dispatch Emergency Response Team" + set category = "Special Verbs" + set desc = "Send an emergency response team to the station" + + if(!holder) + to_chat(usr, "Only administrators may use this command.") + return + if(!ticker) + to_chat(usr, "The game hasn't started yet!") + return + if(ticker.current_state == 1) + to_chat(usr, "The round hasn't started yet!") + return + if(send_emergency_team) + to_chat(usr, "[using_map.boss_name] has already dispatched an emergency response team!") + return + if(tgui_alert(usr, "Do you want to dispatch an Emergency Response Team?","ERT",list("Yes","No")) != "Yes") + return + if(tgui_alert(usr, "Do you want this Response Team to be announced?","ERT",list("Yes","No")) != "Yes") + silent_ert = 1 + if(get_security_level() != "red") // Allow admins to reconsider if the alert level isn't Red + switch(tgui_alert(usr, "The station is not in red alert. Do you still want to dispatch a response team?","ERT",list("Yes","No"))) + if("No") + return + if(send_emergency_team) + to_chat(usr, "Looks like somebody beat you to it!") + return + + message_admins("[key_name_admin(usr)] is dispatching an Emergency Response Team.", 1) + admin_chat_message(message = "[key_name(usr)] is dispatching an Emergency Response Team", color = "#CC2222") //VOREStation Add + log_admin("[key_name(usr)] used Dispatch Response Team.") + trigger_armed_response_team(1) + +/client/verb/JoinResponseTeam() + + set name = "Join Response Team" + set category = "IC" + + if(!MayRespawn(1)) + to_chat(usr, "You cannot join the response team at this time.") + return + + if(istype(usr,/mob/observer/dead) || istype(usr,/mob/new_player)) + if(!send_emergency_team) + to_chat(usr, "No emergency response team is currently being sent.") + return + if(jobban_isbanned(usr, "Syndicate") || jobban_isbanned(usr, "Emergency Response Team") || jobban_isbanned(usr, "Security Officer")) + to_chat(usr, "You are jobbanned from the emergency reponse team!") + return + if(ert.current_antagonists.len >= ert.hard_cap) + to_chat(usr, "The emergency response team is already full!") + return + ert.create_default(usr) + else + to_chat(usr, "You need to be an observer or new player to use this.") + +// returns a number of dead players in % +/proc/percentage_dead() + var/total = 0 + var/deadcount = 0 + for(var/mob/living/carbon/human/H in mob_list) + if(H.client) // Monkeys and mice don't have a client, amirite? + if(H.stat == 2) deadcount++ + total++ + + if(total == 0) return 0 + else return round(100 * deadcount / total) + +// counts the number of antagonists in % +/proc/percentage_antagonists() + var/total = 0 + var/antagonists = 0 + for(var/mob/living/carbon/human/H in mob_list) + if(is_special_character(H) >= 1) + antagonists++ + total++ + + if(total == 0) return 0 + else return round(100 * antagonists / total) + +// Increments the ERT chance automatically, so that the later it is in the round, +// the more likely an ERT is to be able to be called. +/proc/increment_ert_chance() + while(send_emergency_team == 0) // There is no ERT at the time. + if(get_security_level() == "green") + ert_base_chance += 1 + if(get_security_level() == "yellow") + ert_base_chance += 1 + if(get_security_level() == "violet") + ert_base_chance += 2 + if(get_security_level() == "orange") + ert_base_chance += 2 + if(get_security_level() == "blue") + ert_base_chance += 2 + if(get_security_level() == "red") + ert_base_chance += 3 + if(get_security_level() == "delta") + ert_base_chance += 10 // Need those big guns + sleep(600 * 3) // Minute * Number of Minutes + + +/proc/trigger_armed_response_team(var/force = 0) + if(!can_call_ert && !force) + return + if(send_emergency_team) + return + + var/send_team_chance = ert_base_chance // Is incremented by increment_ert_chance. + send_team_chance += 2*percentage_dead() // the more people are dead, the higher the chance + send_team_chance += percentage_antagonists() // the more antagonists, the higher the chance + send_team_chance = min(send_team_chance, 100) + + if(force) send_team_chance = 100 + + // there's only a certain chance a team will be sent + if(!prob(send_team_chance)) + command_announcement.Announce("It would appear that an emergency response team was requested for [station_name()]. Unfortunately, we were unable to send one at this time.", "[using_map.boss_name]") + can_call_ert = 0 // Only one call per round, ladies. + return + if(silent_ert == 0) + command_announcement.Announce("It would appear that an emergency response team was requested for [station_name()]. We will prepare and send one as soon as possible.", "[using_map.boss_name]") + + can_call_ert = 0 // Only one call per round, gentleman. + send_emergency_team = 1 + consider_ert_load() //VOREStation Add + + sleep(600 * 5) + send_emergency_team = 0 // Can no longer join the ERT. diff --git a/code/game/shuttle_engines.dm b/code/game/shuttle_engines.dm index e68b33089a8..4903aed6c76 100644 --- a/code/game/shuttle_engines.dm +++ b/code/game/shuttle_engines.dm @@ -1,81 +1,81 @@ -/obj/structure/shuttle - name = "shuttle" - icon = 'icons/turf/shuttle_parts.dmi' - unacidable = TRUE - -/obj/structure/shuttle/window - name = "shuttle window" - icon = 'icons/obj/podwindows.dmi' - icon_state = "0_0" //The states are a bitflag for connecting window directions, then connecting shuttle wall directions - density = TRUE - opacity = 0 - anchored = TRUE - can_atmos_pass = ATMOS_PASS_NO - - var/window_flags = 0 // Bitflags to indicate connected windows - var/wall_flags = 0 // Bitflags to indicate connected walls - -/obj/structure/shuttle/window/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return TRUE - return ..() - -/obj/structure/shuttle/window/Initialize() - . = ..() - auto_join() - -/obj/structure/shuttle/window/proc/auto_join() - match_windows(NORTH, NORTH) - match_windows(EAST, EAST) - match_windows(SOUTH, SOUTH) - match_windows(WEST, WEST) - - icon_state = "[window_flags]_[wall_flags]" - - return icon_state - -/obj/structure/shuttle/window/proc/match_windows(direction, flag, mask=0) - var/turf/adj = get_step(src, direction) - - if(istype(adj,/turf/simulated/shuttle/wall)) - wall_flags |= flag // turn on the bit flag - else - var/obj/structure/shuttle/window/window = locate(src.type) in adj - if(window) - window_flags |= flag // turn on the bit flag - else - window_flags &= ~flag // turn off the bit flag - wall_flags &= ~flag // turn off the bit flag - -/obj/structure/shuttle/engine - name = "engine" - density = TRUE - anchored = TRUE - -/obj/structure/shuttle/engine/heater - name = "heater" - icon_state = "heater" - -/obj/structure/shuttle/engine/platform - name = "platform" - icon_state = "platform" - -/obj/structure/shuttle/engine/propulsion - name = "propulsion" - icon_state = "propulsion" - opacity = 1 - -/obj/structure/shuttle/engine/propulsion/burst - name = "burst" - -/obj/structure/shuttle/engine/propulsion/burst/left - name = "left" - icon_state = "burst_l" - -/obj/structure/shuttle/engine/propulsion/burst/right - name = "right" - icon_state = "burst_r" - -/obj/structure/shuttle/engine/router - name = "router" - icon_state = "router" +/obj/structure/shuttle + name = "shuttle" + icon = 'icons/turf/shuttle_parts.dmi' + unacidable = TRUE + +/obj/structure/shuttle/window + name = "shuttle window" + icon = 'icons/obj/podwindows.dmi' + icon_state = "0_0" //The states are a bitflag for connecting window directions, then connecting shuttle wall directions + density = TRUE + opacity = 0 + anchored = TRUE + can_atmos_pass = ATMOS_PASS_NO + + var/window_flags = 0 // Bitflags to indicate connected windows + var/wall_flags = 0 // Bitflags to indicate connected walls + +/obj/structure/shuttle/window/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return TRUE + return ..() + +/obj/structure/shuttle/window/Initialize() + . = ..() + auto_join() + +/obj/structure/shuttle/window/proc/auto_join() + match_windows(NORTH, NORTH) + match_windows(EAST, EAST) + match_windows(SOUTH, SOUTH) + match_windows(WEST, WEST) + + icon_state = "[window_flags]_[wall_flags]" + + return icon_state + +/obj/structure/shuttle/window/proc/match_windows(direction, flag, mask=0) + var/turf/adj = get_step(src, direction) + + if(istype(adj,/turf/simulated/shuttle/wall)) + wall_flags |= flag // turn on the bit flag + else + var/obj/structure/shuttle/window/window = locate(src.type) in adj + if(window) + window_flags |= flag // turn on the bit flag + else + window_flags &= ~flag // turn off the bit flag + wall_flags &= ~flag // turn off the bit flag + +/obj/structure/shuttle/engine + name = "engine" + density = TRUE + anchored = TRUE + +/obj/structure/shuttle/engine/heater + name = "heater" + icon_state = "heater" + +/obj/structure/shuttle/engine/platform + name = "platform" + icon_state = "platform" + +/obj/structure/shuttle/engine/propulsion + name = "propulsion" + icon_state = "propulsion" + opacity = 1 + +/obj/structure/shuttle/engine/propulsion/burst + name = "burst" + +/obj/structure/shuttle/engine/propulsion/burst/left + name = "left" + icon_state = "burst_l" + +/obj/structure/shuttle/engine/propulsion/burst/right + name = "right" + icon_state = "burst_r" + +/obj/structure/shuttle/engine/router + name = "router" + icon_state = "router" diff --git a/code/game/skincmd.dm b/code/game/skincmd.dm index fd9131538e4..f8aa4c4b74b 100644 --- a/code/game/skincmd.dm +++ b/code/game/skincmd.dm @@ -1,13 +1,13 @@ -/mob/var/skincmds = list() -/obj/proc/SkinCmd(mob/user as mob, var/data as text) - -/proc/SkinCmdRegister(var/mob/user, var/name as text, var/O as obj) - user.skincmds[name] = O - -/mob/verb/skincmd(data as text) - set hidden = 1 - - var/ref = copytext(data, 1, findtext(data, ";")) - if (src.skincmds[ref] != null) - var/obj/a = src.skincmds[ref] +/mob/var/skincmds = list() +/obj/proc/SkinCmd(mob/user as mob, var/data as text) + +/proc/SkinCmdRegister(var/mob/user, var/name as text, var/O as obj) + user.skincmds[name] = O + +/mob/verb/skincmd(data as text) + set hidden = 1 + + var/ref = copytext(data, 1, findtext(data, ";")) + if (src.skincmds[ref] != null) + var/obj/a = src.skincmds[ref] a.SkinCmd(src, copytext(data, findtext(data, ";") + 1)) \ No newline at end of file diff --git a/code/game/sound.dm b/code/game/sound.dm index 0833824224f..256dae7597c 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -1,312 +1,312 @@ -/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff, is_global, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, preference = null, volume_channel = null) - if(Master.current_runlevel < RUNLEVEL_LOBBY) - return - - var/turf/turf_source = get_turf(source) - if(!turf_source) - return - var/area/area_source = turf_source.loc - - //allocate a channel if necessary now so its the same for everyone - channel = channel || SSsounds.random_available_channel() - - // Looping through the player list has the added bonus of working for mobs inside containers - var/sound/S = sound(get_sfx(soundin)) - var/maxdistance = (world.view + extrarange) * 2 //VOREStation Edit - 3 to 2 - var/list/listeners = player_list.Copy() - if(!ignore_walls) //these sounds don't carry through walls - for(var/mob/listen in listeners) - if(!(get_turf(listen) in hear(maxdistance,source))) - listeners -= listen - for(var/mob/M as anything in listeners) - if(!M || !M.client) - continue - var/turf/T = get_turf(M) - if(!T) - continue - var/area/A = T.loc - if((A.soundproofed || area_source.soundproofed) && (A != area_source)) - continue - var/distance = get_dist(T, turf_source) - - if(distance <= maxdistance) - if(T && T.z == turf_source.z) - M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, is_global, channel, pressure_affected, S, preference, volume_channel) - -/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, is_global, channel = 0, pressure_affected = TRUE, sound/S, preference, volume_channel = null) - if(!client || ear_deaf > 0) - return - if(preference && !client.is_preference_enabled(preference)) - return - - if(!S) - S = sound(get_sfx(soundin)) - - S.wait = 0 //No queue - S.channel = channel || SSsounds.random_available_channel() - - // I'm not sure if you can modify S.volume, but I'd rather not try to find out what - // horrible things lurk in BYOND's internals, so we're just gonna do vol *= - vol *= client.get_preference_volume_channel(volume_channel) - vol *= client.get_preference_volume_channel(VOLUME_CHANNEL_MASTER) - S.volume = vol - - if(vary) - if(frequency) - S.frequency = frequency - else - S.frequency = get_rand_frequency() - - if(isturf(turf_source)) - var/turf/T = get_turf(src) - - //sound volume falloff with distance - var/distance = get_dist(T, turf_source) - - S.volume -= max(distance - world.view, 0) * 2 //multiplicative falloff to add on top of natural audio falloff. - - //Atmosphere affects sound - var/pressure_factor = 1 - if(pressure_affected) - var/datum/gas_mixture/hearer_env = T.return_air() - var/datum/gas_mixture/source_env = turf_source.return_air() - - if(hearer_env && source_env) - var/pressure = min(hearer_env.return_pressure(), source_env.return_pressure()) - if(pressure < ONE_ATMOSPHERE) - pressure_factor = max((pressure - SOUND_MINIMUM_PRESSURE)/(ONE_ATMOSPHERE - SOUND_MINIMUM_PRESSURE), 0) - else //space - pressure_factor = 0 - - if(distance <= 1) - pressure_factor = max(pressure_factor, 0.15) //touching the source of the sound - - S.volume *= pressure_factor - //End Atmosphere affecting sound - - //Don't bother with doing anything below. - if(S.volume <= 0) - return //No sound - - //Apply a sound environment. - if(!is_global) - S.environment = get_sound_env(pressure_factor) - - var/dx = turf_source.x - T.x // Hearing from the right/left - S.x = dx - var/dz = turf_source.y - T.y // Hearing from infront/behind - S.z = dz - // The y value is for above your head, but there is no ceiling in 2d spessmens. - S.y = 1 - S.falloff = (falloff ? falloff : FALLOFF_SOUNDS) - - src << S - -/proc/sound_to_playing_players(sound, volume = 100, vary) - sound = get_sfx(sound) - for(var/M in player_list) - if(ismob(M) && !isnewplayer(M)) - var/mob/MO = M - MO.playsound_local(get_turf(MO), sound, volume, vary, pressure_affected = FALSE) - -/mob/proc/stop_sound_channel(chan) - src << sound(null, repeat = 0, wait = 0, channel = chan) - -/mob/proc/set_sound_channel_volume(channel, volume) - var/sound/S = sound(null, FALSE, FALSE, channel, volume) - S.status = SOUND_UPDATE - src << S - -/proc/get_rand_frequency() - return rand(32000, 55000) //Frequency stuff only works with 45kbps oggs. - -/client/proc/playtitlemusic() - if(!ticker || !SSmedia_tracks.lobby_tracks.len || !media) return - if(is_preference_enabled(/datum/client_preference/play_lobby_music)) - var/datum/track/T = pick(SSmedia_tracks.lobby_tracks) - media.push_music(T.url, world.time, 0.85) - to_chat(src,"Lobby music: [T.title] by [T.artist].") - -/proc/get_sfx(soundin) - if(istext(soundin)) - switch(soundin) - if ("shatter") soundin = pick('sound/effects/Glassbr1.ogg','sound/effects/Glassbr2.ogg','sound/effects/Glassbr3.ogg') - if ("explosion") soundin = pick('sound/effects/Explosion1.ogg','sound/effects/Explosion2.ogg','sound/effects/Explosion3.ogg','sound/effects/Explosion4.ogg','sound/effects/Explosion5.ogg','sound/effects/Explosion6.ogg') - if ("sparks") soundin = pick('sound/effects/sparks1.ogg','sound/effects/sparks2.ogg','sound/effects/sparks3.ogg','sound/effects/sparks5.ogg','sound/effects/sparks6.ogg','sound/effects/sparks7.ogg') - if ("rustle") soundin = pick('sound/effects/rustle1.ogg','sound/effects/rustle2.ogg','sound/effects/rustle3.ogg','sound/effects/rustle4.ogg','sound/effects/rustle5.ogg') - if ("punch") soundin = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg') - if ("clownstep") soundin = pick('sound/effects/clownstep1.ogg','sound/effects/clownstep2.ogg') - if ("swing_hit") soundin = pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg') - if ("hiss") soundin = pick('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') - if ("pageturn") soundin = pick('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') - if ("fracture") soundin = pick('sound/effects/bonebreak1.ogg','sound/effects/bonebreak2.ogg','sound/effects/bonebreak3.ogg','sound/effects/bonebreak4.ogg') - if ("canopen") soundin = pick('sound/effects/can_open1.ogg','sound/effects/can_open2.ogg','sound/effects/can_open3.ogg','sound/effects/can_open4.ogg') - if ("mechstep") soundin = pick('sound/mecha/mechstep1.ogg', 'sound/mecha/mechstep2.ogg') - if ("thunder") soundin = pick('sound/effects/thunder/thunder1.ogg', 'sound/effects/thunder/thunder2.ogg', 'sound/effects/thunder/thunder3.ogg', 'sound/effects/thunder/thunder4.ogg', - 'sound/effects/thunder/thunder5.ogg', 'sound/effects/thunder/thunder6.ogg', 'sound/effects/thunder/thunder7.ogg', 'sound/effects/thunder/thunder8.ogg', 'sound/effects/thunder/thunder9.ogg', - 'sound/effects/thunder/thunder10.ogg') - if ("keyboard") soundin = pick('sound/effects/keyboard/keyboard1.ogg','sound/effects/keyboard/keyboard2.ogg','sound/effects/keyboard/keyboard3.ogg', 'sound/effects/keyboard/keyboard4.ogg') - if ("button") soundin = pick('sound/machines/button1.ogg','sound/machines/button2.ogg','sound/machines/button3.ogg','sound/machines/button4.ogg') - if ("switch") soundin = pick('sound/machines/switch1.ogg','sound/machines/switch2.ogg','sound/machines/switch3.ogg','sound/machines/switch4.ogg') - if ("casing_sound") soundin = pick('sound/weapons/casingfall1.ogg','sound/weapons/casingfall2.ogg','sound/weapons/casingfall3.ogg') - if ("pickaxe") soundin = pick('sound/weapons/mine/pickaxe1.ogg', 'sound/weapons/mine/pickaxe2.ogg','sound/weapons/mine/pickaxe3.ogg','sound/weapons/mine/pickaxe4.ogg') - if("shatter") - soundin = pick('sound/effects/Glassbr1.ogg','sound/effects/Glassbr2.ogg','sound/effects/Glassbr3.ogg') - if("explosion") - soundin = pick( - 'sound/effects/Explosion1.ogg', - 'sound/effects/Explosion2.ogg', - 'sound/effects/Explosion3.ogg', - 'sound/effects/Explosion4.ogg', - 'sound/effects/Explosion5.ogg', - 'sound/effects/Explosion6.ogg') - if("sparks") - soundin = pick( - 'sound/effects/sparks1.ogg', - 'sound/effects/sparks2.ogg', - 'sound/effects/sparks3.ogg', - 'sound/effects/sparks5.ogg', - 'sound/effects/sparks6.ogg', - 'sound/effects/sparks7.ogg') - if("rustle") - soundin = pick('sound/effects/rustle1.ogg','sound/effects/rustle2.ogg','sound/effects/rustle3.ogg','sound/effects/rustle4.ogg','sound/effects/rustle5.ogg') - if("punch") - soundin = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg') - if("clownstep") - soundin = pick('sound/effects/clownstep1.ogg','sound/effects/clownstep2.ogg') - if("swing_hit") - soundin = pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg') - if("hiss") - soundin = pick('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') - if("pageturn") - soundin = pick('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') - if("fracture") - soundin = pick('sound/effects/bonebreak1.ogg','sound/effects/bonebreak2.ogg','sound/effects/bonebreak3.ogg','sound/effects/bonebreak4.ogg') - if("canopen") - soundin = pick('sound/effects/can_open1.ogg','sound/effects/can_open2.ogg','sound/effects/can_open3.ogg','sound/effects/can_open4.ogg') - if("mechstep") - soundin = pick('sound/mecha/mechstep1.ogg', 'sound/mecha/mechstep2.ogg') - if("thunder") - soundin = pick( - 'sound/effects/thunder/thunder1.ogg', - 'sound/effects/thunder/thunder2.ogg', - 'sound/effects/thunder/thunder3.ogg', - 'sound/effects/thunder/thunder4.ogg', - 'sound/effects/thunder/thunder5.ogg', - 'sound/effects/thunder/thunder6.ogg', - 'sound/effects/thunder/thunder7.ogg', - 'sound/effects/thunder/thunder8.ogg', - 'sound/effects/thunder/thunder9.ogg', - 'sound/effects/thunder/thunder10.ogg') - if("keyboard") - soundin = pick( - 'sound/effects/keyboard/keyboard1.ogg', - 'sound/effects/keyboard/keyboard2.ogg', - 'sound/effects/keyboard/keyboard3.ogg', - 'sound/effects/keyboard/keyboard4.ogg') - if("button") - soundin = pick('sound/machines/button1.ogg','sound/machines/button2.ogg','sound/machines/button3.ogg','sound/machines/button4.ogg') - if("switch") - soundin = pick('sound/machines/switch1.ogg','sound/machines/switch2.ogg','sound/machines/switch3.ogg','sound/machines/switch4.ogg') - if("casing_sound") - soundin = pick('sound/weapons/casingfall1.ogg','sound/weapons/casingfall2.ogg','sound/weapons/casingfall3.ogg') - if("ricochet") - soundin = pick( - 'sound/weapons/effects/ric1.ogg', - 'sound/weapons/effects/ric2.ogg', - 'sound/weapons/effects/ric3.ogg', - 'sound/weapons/effects/ric4.ogg', - 'sound/weapons/effects/ric5.ogg') - if("bullet_miss") - soundin = pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg') - if ("pickaxe") - soundin = pick( - 'sound/weapons/mine/pickaxe1.ogg', - 'sound/weapons/mine/pickaxe2.ogg', - 'sound/weapons/mine/pickaxe3.ogg', - 'sound/weapons/mine/pickaxe4.ogg') - //VORESTATION EDIT - vore sounds for better performance - if ("hunger_sounds") soundin = pick('sound/vore/growl1.ogg','sound/vore/growl2.ogg','sound/vore/growl3.ogg','sound/vore/growl4.ogg','sound/vore/growl5.ogg') - - if("classic_digestion_sounds") soundin = pick( - 'sound/vore/digest1.ogg','sound/vore/digest2.ogg','sound/vore/digest3.ogg','sound/vore/digest4.ogg', - 'sound/vore/digest5.ogg','sound/vore/digest6.ogg','sound/vore/digest7.ogg','sound/vore/digest8.ogg', - 'sound/vore/digest9.ogg','sound/vore/digest10.ogg','sound/vore/digest11.ogg','sound/vore/digest12.ogg') - if("classic_death_sounds") soundin = pick( - 'sound/vore/death1.ogg','sound/vore/death2.ogg','sound/vore/death3.ogg','sound/vore/death4.ogg','sound/vore/death5.ogg', - 'sound/vore/death6.ogg','sound/vore/death7.ogg','sound/vore/death8.ogg','sound/vore/death9.ogg','sound/vore/death10.ogg') - if("classic_struggle_sounds") soundin = pick('sound/vore/squish1.ogg','sound/vore/squish2.ogg','sound/vore/squish3.ogg','sound/vore/squish4.ogg') - - if("fancy_prey_struggle") soundin = pick( - 'sound/vore/sunesound/prey/struggle_01.ogg','sound/vore/sunesound/prey/struggle_02.ogg','sound/vore/sunesound/prey/struggle_03.ogg', - 'sound/vore/sunesound/prey/struggle_04.ogg','sound/vore/sunesound/prey/struggle_05.ogg') - if("fancy_digest_pred") soundin = pick( - 'sound/vore/sunesound/pred/digest_01.ogg','sound/vore/sunesound/pred/digest_02.ogg','sound/vore/sunesound/pred/digest_03.ogg', - 'sound/vore/sunesound/pred/digest_04.ogg','sound/vore/sunesound/pred/digest_05.ogg','sound/vore/sunesound/pred/digest_06.ogg', - 'sound/vore/sunesound/pred/digest_07.ogg','sound/vore/sunesound/pred/digest_08.ogg','sound/vore/sunesound/pred/digest_09.ogg', - 'sound/vore/sunesound/pred/digest_10.ogg','sound/vore/sunesound/pred/digest_11.ogg','sound/vore/sunesound/pred/digest_12.ogg', - 'sound/vore/sunesound/pred/digest_13.ogg','sound/vore/sunesound/pred/digest_14.ogg','sound/vore/sunesound/pred/digest_15.ogg', - 'sound/vore/sunesound/pred/digest_16.ogg','sound/vore/sunesound/pred/digest_17.ogg','sound/vore/sunesound/pred/digest_18.ogg') - if("fancy_death_pred") soundin = pick( - 'sound/vore/sunesound/pred/death_01.ogg','sound/vore/sunesound/pred/death_02.ogg','sound/vore/sunesound/pred/death_03.ogg', - 'sound/vore/sunesound/pred/death_04.ogg','sound/vore/sunesound/pred/death_05.ogg','sound/vore/sunesound/pred/death_06.ogg', - 'sound/vore/sunesound/pred/death_07.ogg','sound/vore/sunesound/pred/death_08.ogg','sound/vore/sunesound/pred/death_09.ogg', - 'sound/vore/sunesound/pred/death_10.ogg') - if("fancy_digest_prey") soundin = pick( - 'sound/vore/sunesound/prey/digest_01.ogg','sound/vore/sunesound/prey/digest_02.ogg','sound/vore/sunesound/prey/digest_03.ogg', - 'sound/vore/sunesound/prey/digest_04.ogg','sound/vore/sunesound/prey/digest_05.ogg','sound/vore/sunesound/prey/digest_06.ogg', - 'sound/vore/sunesound/prey/digest_07.ogg','sound/vore/sunesound/prey/digest_08.ogg','sound/vore/sunesound/prey/digest_09.ogg', - 'sound/vore/sunesound/prey/digest_10.ogg','sound/vore/sunesound/prey/digest_11.ogg','sound/vore/sunesound/prey/digest_12.ogg', - 'sound/vore/sunesound/prey/digest_13.ogg','sound/vore/sunesound/prey/digest_14.ogg','sound/vore/sunesound/prey/digest_15.ogg', - 'sound/vore/sunesound/prey/digest_16.ogg','sound/vore/sunesound/prey/digest_17.ogg','sound/vore/sunesound/prey/digest_18.ogg') - if("fancy_death_prey") soundin = pick( - 'sound/vore/sunesound/prey/death_01.ogg','sound/vore/sunesound/prey/death_02.ogg','sound/vore/sunesound/prey/death_03.ogg', - 'sound/vore/sunesound/prey/death_04.ogg','sound/vore/sunesound/prey/death_05.ogg','sound/vore/sunesound/prey/death_06.ogg', - 'sound/vore/sunesound/prey/death_07.ogg','sound/vore/sunesound/prey/death_08.ogg','sound/vore/sunesound/prey/death_09.ogg', - 'sound/vore/sunesound/prey/death_10.ogg') - if ("belches") soundin = pick( - 'sound/vore/belches/belch1.ogg','sound/vore/belches/belch2.ogg','sound/vore/belches/belch3.ogg','sound/vore/belches/belch4.ogg', - 'sound/vore/belches/belch5.ogg','sound/vore/belches/belch6.ogg','sound/vore/belches/belch7.ogg','sound/vore/belches/belch8.ogg', - 'sound/vore/belches/belch9.ogg','sound/vore/belches/belch10.ogg','sound/vore/belches/belch11.ogg','sound/vore/belches/belch12.ogg', - 'sound/vore/belches/belch13.ogg','sound/vore/belches/belch14.ogg','sound/vore/belches/belch15.ogg') - //END VORESTATION EDIT - if ("terminal_type") - soundin = pick('sound/machines/terminal_button01.ogg', 'sound/machines/terminal_button02.ogg', 'sound/machines/terminal_button03.ogg', \ - 'sound/machines/terminal_button04.ogg', 'sound/machines/terminal_button05.ogg', 'sound/machines/terminal_button06.ogg', \ - 'sound/machines/terminal_button07.ogg', 'sound/machines/terminal_button08.ogg') - if("smcalm") - soundin = pick('sound/machines/sm/accent/normal/1.ogg', 'sound/machines/sm/accent/normal/2.ogg', 'sound/machines/sm/accent/normal/3.ogg', 'sound/machines/sm/accent/normal/4.ogg', 'sound/machines/sm/accent/normal/5.ogg', 'sound/machines/sm/accent/normal/6.ogg', 'sound/machines/sm/accent/normal/7.ogg', 'sound/machines/sm/accent/normal/8.ogg', 'sound/machines/sm/accent/normal/9.ogg', 'sound/machines/sm/accent/normal/10.ogg', 'sound/machines/sm/accent/normal/11.ogg', 'sound/machines/sm/accent/normal/12.ogg', 'sound/machines/sm/accent/normal/13.ogg', 'sound/machines/sm/accent/normal/14.ogg', 'sound/machines/sm/accent/normal/15.ogg', 'sound/machines/sm/accent/normal/16.ogg', 'sound/machines/sm/accent/normal/17.ogg', 'sound/machines/sm/accent/normal/18.ogg', 'sound/machines/sm/accent/normal/19.ogg', 'sound/machines/sm/accent/normal/20.ogg', 'sound/machines/sm/accent/normal/21.ogg', 'sound/machines/sm/accent/normal/22.ogg', 'sound/machines/sm/accent/normal/23.ogg', 'sound/machines/sm/accent/normal/24.ogg', 'sound/machines/sm/accent/normal/25.ogg', 'sound/machines/sm/accent/normal/26.ogg', 'sound/machines/sm/accent/normal/27.ogg', 'sound/machines/sm/accent/normal/28.ogg', 'sound/machines/sm/accent/normal/29.ogg', 'sound/machines/sm/accent/normal/30.ogg', 'sound/machines/sm/accent/normal/31.ogg', 'sound/machines/sm/accent/normal/32.ogg', 'sound/machines/sm/accent/normal/33.ogg', 'sound/machines/sm/supermatter1.ogg', 'sound/machines/sm/supermatter2.ogg', 'sound/machines/sm/supermatter3.ogg') - if("smdelam") - soundin = pick('sound/machines/sm/accent/delam/1.ogg', 'sound/machines/sm/accent/normal/2.ogg', 'sound/machines/sm/accent/normal/3.ogg', 'sound/machines/sm/accent/normal/4.ogg', 'sound/machines/sm/accent/normal/5.ogg', 'sound/machines/sm/accent/normal/6.ogg', 'sound/machines/sm/accent/normal/7.ogg', 'sound/machines/sm/accent/normal/8.ogg', 'sound/machines/sm/accent/normal/9.ogg', 'sound/machines/sm/accent/normal/10.ogg', 'sound/machines/sm/accent/normal/11.ogg', 'sound/machines/sm/accent/normal/12.ogg', 'sound/machines/sm/accent/normal/13.ogg', 'sound/machines/sm/accent/normal/14.ogg', 'sound/machines/sm/accent/normal/15.ogg', 'sound/machines/sm/accent/normal/16.ogg', 'sound/machines/sm/accent/normal/17.ogg', 'sound/machines/sm/accent/normal/18.ogg', 'sound/machines/sm/accent/normal/19.ogg', 'sound/machines/sm/accent/normal/20.ogg', 'sound/machines/sm/accent/normal/21.ogg', 'sound/machines/sm/accent/normal/22.ogg', 'sound/machines/sm/accent/normal/23.ogg', 'sound/machines/sm/accent/normal/24.ogg', 'sound/machines/sm/accent/normal/25.ogg', 'sound/machines/sm/accent/normal/26.ogg', 'sound/machines/sm/accent/normal/27.ogg', 'sound/machines/sm/accent/normal/28.ogg', 'sound/machines/sm/accent/normal/29.ogg', 'sound/machines/sm/accent/normal/30.ogg', 'sound/machines/sm/accent/normal/31.ogg', 'sound/machines/sm/accent/normal/32.ogg', 'sound/machines/sm/accent/normal/33.ogg', 'sound/machines/sm/supermatter1.ogg', 'sound/machines/sm/supermatter2.ogg', 'sound/machines/sm/supermatter3.ogg') - if ("generic_drop") - soundin = pick( - 'sound/items/drop/generic1.ogg', - 'sound/items/drop/generic2.ogg' ) - if ("generic_pickup") - soundin = pick( - 'sound/items/pickup/generic1.ogg', - 'sound/items/pickup/generic2.ogg', - 'sound/items/pickup/generic3.ogg') - return soundin - -//Are these even used? //Yes -var/list/keyboard_sound = list ('sound/effects/keyboard/keyboard1.ogg','sound/effects/keyboard/keyboard2.ogg','sound/effects/keyboard/keyboard3.ogg', 'sound/effects/keyboard/keyboard4.ogg') -var/list/bodyfall_sound = list('sound/effects/bodyfall1.ogg','sound/effects/bodyfall2.ogg','sound/effects/bodyfall3.ogg','sound/effects/bodyfall4.ogg') -var/list/teppi_sound = list('sound/voice/teppi/gyooh1.ogg', 'sound/voice/teppi/gyooh2.ogg', 'sound/voice/teppi/gyooh3.ogg', 'sound/voice/teppi/gyooh4.ogg', 'sound/voice/teppi/gyooh5.ogg', 'sound/voice/teppi/gyooh6.ogg', 'sound/voice/teppi/snoot1.ogg', 'sound/voice/teppi/snoot2.ogg') -var/list/talk_sound = list('sound/talksounds/a.ogg','sound/talksounds/b.ogg','sound/talksounds/c.ogg','sound/talksounds/d.ogg','sound/talksounds/e.ogg','sound/talksounds/f.ogg','sound/talksounds/g.ogg','sound/talksounds/h.ogg') -var/list/emote_sound = list('sound/talksounds/me_a.ogg','sound/talksounds/me_b.ogg','sound/talksounds/me_c.ogg','sound/talksounds/me_d.ogg','sound/talksounds/me_e.ogg','sound/talksounds/me_f.ogg') - -//Goon sounds -var/list/goon_speak_one_sound = list('sound/talksounds/goon/speak_1.ogg', 'sound/talksounds/goon/speak_1_ask.ogg', 'sound/talksounds/goon/speak_1_exclaim.ogg') -var/list/goon_speak_two_sound = list('sound/talksounds/goon/speak_2.ogg', 'sound/talksounds/goon/speak_2_ask.ogg', 'sound/talksounds/goon/speak_2_exclaim.ogg') -var/list/goon_speak_three_sound = list('sound/talksounds/goon/speak_3.ogg', 'sound/talksounds/goon/speak_3_ask.ogg', 'sound/talksounds/goon/speak_3_exclaim.ogg') -var/list/goon_speak_four_sound = list('sound/talksounds/goon/speak_4.ogg', 'sound/talksounds/goon/speak_4_ask.ogg', 'sound/talksounds/goon/speak_4_exclaim.ogg') -var/list/goon_speak_blub_sound = list('sound/talksounds/goon/blub.ogg', 'sound/talksounds/goon/blub_ask.ogg', 'sound/talksounds/goon/blub_exclaim.ogg') -var/list/goon_speak_bottalk_sound = list('sound/talksounds/goon/bottalk_1.ogg', 'sound/talksounds/goon/bottalk_2.ogg', 'sound/talksounds/goon/bottalk_3.ogg', 'sound/talksounds/goon/bottalk_4.wav') -var/list/goon_speak_buwoo_sound = list('sound/talksounds/goon/buwoo.ogg', 'sound/talksounds/goon/buwoo_ask.ogg', 'sound/talksounds/goon/buwoo_exclaim.ogg') -var/list/goon_speak_cow_sound = list('sound/talksounds/goon/cow.ogg', 'sound/talksounds/goon/cow_ask.ogg', 'sound/talksounds/goon/cow_exclaim.ogg') -var/list/goon_speak_lizard_sound = list('sound/talksounds/goon/lizard.ogg', 'sound/talksounds/goon/lizard_ask.ogg', 'sound/talksounds/goon/lizard_exclaim.ogg') -var/list/goon_speak_pug_sound = list('sound/talksounds/goon/pug.ogg', 'sound/talksounds/goon/pug_ask.ogg', 'sound/talksounds/goon/pug_exclaim.ogg') -var/list/goon_speak_pugg_sound = list('sound/talksounds/goon/pugg.ogg', 'sound/talksounds/goon/pugg_ask.ogg', 'sound/talksounds/goon/pugg_exclaim.ogg') -var/list/goon_speak_roach_sound = list('sound/talksounds/goon/roach.ogg', 'sound/talksounds/goon/roach_ask.ogg', 'sound/talksounds/goon/roach_exclaim.ogg') -var/list/goon_speak_skelly_sound = list('sound/talksounds/goon/skelly.ogg', 'sound/talksounds/goon/skelly_ask.ogg', 'sound/talksounds/goon/skelly_exclaim.ogg') +/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff, is_global, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, preference = null, volume_channel = null) + if(Master.current_runlevel < RUNLEVEL_LOBBY) + return + + var/turf/turf_source = get_turf(source) + if(!turf_source) + return + var/area/area_source = turf_source.loc + + //allocate a channel if necessary now so its the same for everyone + channel = channel || SSsounds.random_available_channel() + + // Looping through the player list has the added bonus of working for mobs inside containers + var/sound/S = sound(get_sfx(soundin)) + var/maxdistance = (world.view + extrarange) * 2 //VOREStation Edit - 3 to 2 + var/list/listeners = player_list.Copy() + if(!ignore_walls) //these sounds don't carry through walls + for(var/mob/listen in listeners) + if(!(get_turf(listen) in hear(maxdistance,source))) + listeners -= listen + for(var/mob/M as anything in listeners) + if(!M || !M.client) + continue + var/turf/T = get_turf(M) + if(!T) + continue + var/area/A = T.loc + if((A.soundproofed || area_source.soundproofed) && (A != area_source)) + continue + var/distance = get_dist(T, turf_source) + + if(distance <= maxdistance) + if(T && T.z == turf_source.z) + M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, is_global, channel, pressure_affected, S, preference, volume_channel) + +/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, is_global, channel = 0, pressure_affected = TRUE, sound/S, preference, volume_channel = null) + if(!client || ear_deaf > 0) + return + if(preference && !client.is_preference_enabled(preference)) + return + + if(!S) + S = sound(get_sfx(soundin)) + + S.wait = 0 //No queue + S.channel = channel || SSsounds.random_available_channel() + + // I'm not sure if you can modify S.volume, but I'd rather not try to find out what + // horrible things lurk in BYOND's internals, so we're just gonna do vol *= + vol *= client.get_preference_volume_channel(volume_channel) + vol *= client.get_preference_volume_channel(VOLUME_CHANNEL_MASTER) + S.volume = vol + + if(vary) + if(frequency) + S.frequency = frequency + else + S.frequency = get_rand_frequency() + + if(isturf(turf_source)) + var/turf/T = get_turf(src) + + //sound volume falloff with distance + var/distance = get_dist(T, turf_source) + + S.volume -= max(distance - world.view, 0) * 2 //multiplicative falloff to add on top of natural audio falloff. + + //Atmosphere affects sound + var/pressure_factor = 1 + if(pressure_affected) + var/datum/gas_mixture/hearer_env = T.return_air() + var/datum/gas_mixture/source_env = turf_source.return_air() + + if(hearer_env && source_env) + var/pressure = min(hearer_env.return_pressure(), source_env.return_pressure()) + if(pressure < ONE_ATMOSPHERE) + pressure_factor = max((pressure - SOUND_MINIMUM_PRESSURE)/(ONE_ATMOSPHERE - SOUND_MINIMUM_PRESSURE), 0) + else //space + pressure_factor = 0 + + if(distance <= 1) + pressure_factor = max(pressure_factor, 0.15) //touching the source of the sound + + S.volume *= pressure_factor + //End Atmosphere affecting sound + + //Don't bother with doing anything below. + if(S.volume <= 0) + return //No sound + + //Apply a sound environment. + if(!is_global) + S.environment = get_sound_env(pressure_factor) + + var/dx = turf_source.x - T.x // Hearing from the right/left + S.x = dx + var/dz = turf_source.y - T.y // Hearing from infront/behind + S.z = dz + // The y value is for above your head, but there is no ceiling in 2d spessmens. + S.y = 1 + S.falloff = (falloff ? falloff : FALLOFF_SOUNDS) + + src << S + +/proc/sound_to_playing_players(sound, volume = 100, vary) + sound = get_sfx(sound) + for(var/M in player_list) + if(ismob(M) && !isnewplayer(M)) + var/mob/MO = M + MO.playsound_local(get_turf(MO), sound, volume, vary, pressure_affected = FALSE) + +/mob/proc/stop_sound_channel(chan) + src << sound(null, repeat = 0, wait = 0, channel = chan) + +/mob/proc/set_sound_channel_volume(channel, volume) + var/sound/S = sound(null, FALSE, FALSE, channel, volume) + S.status = SOUND_UPDATE + src << S + +/proc/get_rand_frequency() + return rand(32000, 55000) //Frequency stuff only works with 45kbps oggs. + +/client/proc/playtitlemusic() + if(!ticker || !SSmedia_tracks.lobby_tracks.len || !media) return + if(is_preference_enabled(/datum/client_preference/play_lobby_music)) + var/datum/track/T = pick(SSmedia_tracks.lobby_tracks) + media.push_music(T.url, world.time, 0.85) + to_chat(src,"Lobby music: [T.title] by [T.artist].") + +/proc/get_sfx(soundin) + if(istext(soundin)) + switch(soundin) + if ("shatter") soundin = pick('sound/effects/Glassbr1.ogg','sound/effects/Glassbr2.ogg','sound/effects/Glassbr3.ogg') + if ("explosion") soundin = pick('sound/effects/Explosion1.ogg','sound/effects/Explosion2.ogg','sound/effects/Explosion3.ogg','sound/effects/Explosion4.ogg','sound/effects/Explosion5.ogg','sound/effects/Explosion6.ogg') + if ("sparks") soundin = pick('sound/effects/sparks1.ogg','sound/effects/sparks2.ogg','sound/effects/sparks3.ogg','sound/effects/sparks5.ogg','sound/effects/sparks6.ogg','sound/effects/sparks7.ogg') + if ("rustle") soundin = pick('sound/effects/rustle1.ogg','sound/effects/rustle2.ogg','sound/effects/rustle3.ogg','sound/effects/rustle4.ogg','sound/effects/rustle5.ogg') + if ("punch") soundin = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg') + if ("clownstep") soundin = pick('sound/effects/clownstep1.ogg','sound/effects/clownstep2.ogg') + if ("swing_hit") soundin = pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg') + if ("hiss") soundin = pick('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') + if ("pageturn") soundin = pick('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') + if ("fracture") soundin = pick('sound/effects/bonebreak1.ogg','sound/effects/bonebreak2.ogg','sound/effects/bonebreak3.ogg','sound/effects/bonebreak4.ogg') + if ("canopen") soundin = pick('sound/effects/can_open1.ogg','sound/effects/can_open2.ogg','sound/effects/can_open3.ogg','sound/effects/can_open4.ogg') + if ("mechstep") soundin = pick('sound/mecha/mechstep1.ogg', 'sound/mecha/mechstep2.ogg') + if ("thunder") soundin = pick('sound/effects/thunder/thunder1.ogg', 'sound/effects/thunder/thunder2.ogg', 'sound/effects/thunder/thunder3.ogg', 'sound/effects/thunder/thunder4.ogg', + 'sound/effects/thunder/thunder5.ogg', 'sound/effects/thunder/thunder6.ogg', 'sound/effects/thunder/thunder7.ogg', 'sound/effects/thunder/thunder8.ogg', 'sound/effects/thunder/thunder9.ogg', + 'sound/effects/thunder/thunder10.ogg') + if ("keyboard") soundin = pick('sound/effects/keyboard/keyboard1.ogg','sound/effects/keyboard/keyboard2.ogg','sound/effects/keyboard/keyboard3.ogg', 'sound/effects/keyboard/keyboard4.ogg') + if ("button") soundin = pick('sound/machines/button1.ogg','sound/machines/button2.ogg','sound/machines/button3.ogg','sound/machines/button4.ogg') + if ("switch") soundin = pick('sound/machines/switch1.ogg','sound/machines/switch2.ogg','sound/machines/switch3.ogg','sound/machines/switch4.ogg') + if ("casing_sound") soundin = pick('sound/weapons/casingfall1.ogg','sound/weapons/casingfall2.ogg','sound/weapons/casingfall3.ogg') + if ("pickaxe") soundin = pick('sound/weapons/mine/pickaxe1.ogg', 'sound/weapons/mine/pickaxe2.ogg','sound/weapons/mine/pickaxe3.ogg','sound/weapons/mine/pickaxe4.ogg') + if("shatter") + soundin = pick('sound/effects/Glassbr1.ogg','sound/effects/Glassbr2.ogg','sound/effects/Glassbr3.ogg') + if("explosion") + soundin = pick( + 'sound/effects/Explosion1.ogg', + 'sound/effects/Explosion2.ogg', + 'sound/effects/Explosion3.ogg', + 'sound/effects/Explosion4.ogg', + 'sound/effects/Explosion5.ogg', + 'sound/effects/Explosion6.ogg') + if("sparks") + soundin = pick( + 'sound/effects/sparks1.ogg', + 'sound/effects/sparks2.ogg', + 'sound/effects/sparks3.ogg', + 'sound/effects/sparks5.ogg', + 'sound/effects/sparks6.ogg', + 'sound/effects/sparks7.ogg') + if("rustle") + soundin = pick('sound/effects/rustle1.ogg','sound/effects/rustle2.ogg','sound/effects/rustle3.ogg','sound/effects/rustle4.ogg','sound/effects/rustle5.ogg') + if("punch") + soundin = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg') + if("clownstep") + soundin = pick('sound/effects/clownstep1.ogg','sound/effects/clownstep2.ogg') + if("swing_hit") + soundin = pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg') + if("hiss") + soundin = pick('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') + if("pageturn") + soundin = pick('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') + if("fracture") + soundin = pick('sound/effects/bonebreak1.ogg','sound/effects/bonebreak2.ogg','sound/effects/bonebreak3.ogg','sound/effects/bonebreak4.ogg') + if("canopen") + soundin = pick('sound/effects/can_open1.ogg','sound/effects/can_open2.ogg','sound/effects/can_open3.ogg','sound/effects/can_open4.ogg') + if("mechstep") + soundin = pick('sound/mecha/mechstep1.ogg', 'sound/mecha/mechstep2.ogg') + if("thunder") + soundin = pick( + 'sound/effects/thunder/thunder1.ogg', + 'sound/effects/thunder/thunder2.ogg', + 'sound/effects/thunder/thunder3.ogg', + 'sound/effects/thunder/thunder4.ogg', + 'sound/effects/thunder/thunder5.ogg', + 'sound/effects/thunder/thunder6.ogg', + 'sound/effects/thunder/thunder7.ogg', + 'sound/effects/thunder/thunder8.ogg', + 'sound/effects/thunder/thunder9.ogg', + 'sound/effects/thunder/thunder10.ogg') + if("keyboard") + soundin = pick( + 'sound/effects/keyboard/keyboard1.ogg', + 'sound/effects/keyboard/keyboard2.ogg', + 'sound/effects/keyboard/keyboard3.ogg', + 'sound/effects/keyboard/keyboard4.ogg') + if("button") + soundin = pick('sound/machines/button1.ogg','sound/machines/button2.ogg','sound/machines/button3.ogg','sound/machines/button4.ogg') + if("switch") + soundin = pick('sound/machines/switch1.ogg','sound/machines/switch2.ogg','sound/machines/switch3.ogg','sound/machines/switch4.ogg') + if("casing_sound") + soundin = pick('sound/weapons/casingfall1.ogg','sound/weapons/casingfall2.ogg','sound/weapons/casingfall3.ogg') + if("ricochet") + soundin = pick( + 'sound/weapons/effects/ric1.ogg', + 'sound/weapons/effects/ric2.ogg', + 'sound/weapons/effects/ric3.ogg', + 'sound/weapons/effects/ric4.ogg', + 'sound/weapons/effects/ric5.ogg') + if("bullet_miss") + soundin = pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg') + if ("pickaxe") + soundin = pick( + 'sound/weapons/mine/pickaxe1.ogg', + 'sound/weapons/mine/pickaxe2.ogg', + 'sound/weapons/mine/pickaxe3.ogg', + 'sound/weapons/mine/pickaxe4.ogg') + //VORESTATION EDIT - vore sounds for better performance + if ("hunger_sounds") soundin = pick('sound/vore/growl1.ogg','sound/vore/growl2.ogg','sound/vore/growl3.ogg','sound/vore/growl4.ogg','sound/vore/growl5.ogg') + + if("classic_digestion_sounds") soundin = pick( + 'sound/vore/digest1.ogg','sound/vore/digest2.ogg','sound/vore/digest3.ogg','sound/vore/digest4.ogg', + 'sound/vore/digest5.ogg','sound/vore/digest6.ogg','sound/vore/digest7.ogg','sound/vore/digest8.ogg', + 'sound/vore/digest9.ogg','sound/vore/digest10.ogg','sound/vore/digest11.ogg','sound/vore/digest12.ogg') + if("classic_death_sounds") soundin = pick( + 'sound/vore/death1.ogg','sound/vore/death2.ogg','sound/vore/death3.ogg','sound/vore/death4.ogg','sound/vore/death5.ogg', + 'sound/vore/death6.ogg','sound/vore/death7.ogg','sound/vore/death8.ogg','sound/vore/death9.ogg','sound/vore/death10.ogg') + if("classic_struggle_sounds") soundin = pick('sound/vore/squish1.ogg','sound/vore/squish2.ogg','sound/vore/squish3.ogg','sound/vore/squish4.ogg') + + if("fancy_prey_struggle") soundin = pick( + 'sound/vore/sunesound/prey/struggle_01.ogg','sound/vore/sunesound/prey/struggle_02.ogg','sound/vore/sunesound/prey/struggle_03.ogg', + 'sound/vore/sunesound/prey/struggle_04.ogg','sound/vore/sunesound/prey/struggle_05.ogg') + if("fancy_digest_pred") soundin = pick( + 'sound/vore/sunesound/pred/digest_01.ogg','sound/vore/sunesound/pred/digest_02.ogg','sound/vore/sunesound/pred/digest_03.ogg', + 'sound/vore/sunesound/pred/digest_04.ogg','sound/vore/sunesound/pred/digest_05.ogg','sound/vore/sunesound/pred/digest_06.ogg', + 'sound/vore/sunesound/pred/digest_07.ogg','sound/vore/sunesound/pred/digest_08.ogg','sound/vore/sunesound/pred/digest_09.ogg', + 'sound/vore/sunesound/pred/digest_10.ogg','sound/vore/sunesound/pred/digest_11.ogg','sound/vore/sunesound/pred/digest_12.ogg', + 'sound/vore/sunesound/pred/digest_13.ogg','sound/vore/sunesound/pred/digest_14.ogg','sound/vore/sunesound/pred/digest_15.ogg', + 'sound/vore/sunesound/pred/digest_16.ogg','sound/vore/sunesound/pred/digest_17.ogg','sound/vore/sunesound/pred/digest_18.ogg') + if("fancy_death_pred") soundin = pick( + 'sound/vore/sunesound/pred/death_01.ogg','sound/vore/sunesound/pred/death_02.ogg','sound/vore/sunesound/pred/death_03.ogg', + 'sound/vore/sunesound/pred/death_04.ogg','sound/vore/sunesound/pred/death_05.ogg','sound/vore/sunesound/pred/death_06.ogg', + 'sound/vore/sunesound/pred/death_07.ogg','sound/vore/sunesound/pred/death_08.ogg','sound/vore/sunesound/pred/death_09.ogg', + 'sound/vore/sunesound/pred/death_10.ogg') + if("fancy_digest_prey") soundin = pick( + 'sound/vore/sunesound/prey/digest_01.ogg','sound/vore/sunesound/prey/digest_02.ogg','sound/vore/sunesound/prey/digest_03.ogg', + 'sound/vore/sunesound/prey/digest_04.ogg','sound/vore/sunesound/prey/digest_05.ogg','sound/vore/sunesound/prey/digest_06.ogg', + 'sound/vore/sunesound/prey/digest_07.ogg','sound/vore/sunesound/prey/digest_08.ogg','sound/vore/sunesound/prey/digest_09.ogg', + 'sound/vore/sunesound/prey/digest_10.ogg','sound/vore/sunesound/prey/digest_11.ogg','sound/vore/sunesound/prey/digest_12.ogg', + 'sound/vore/sunesound/prey/digest_13.ogg','sound/vore/sunesound/prey/digest_14.ogg','sound/vore/sunesound/prey/digest_15.ogg', + 'sound/vore/sunesound/prey/digest_16.ogg','sound/vore/sunesound/prey/digest_17.ogg','sound/vore/sunesound/prey/digest_18.ogg') + if("fancy_death_prey") soundin = pick( + 'sound/vore/sunesound/prey/death_01.ogg','sound/vore/sunesound/prey/death_02.ogg','sound/vore/sunesound/prey/death_03.ogg', + 'sound/vore/sunesound/prey/death_04.ogg','sound/vore/sunesound/prey/death_05.ogg','sound/vore/sunesound/prey/death_06.ogg', + 'sound/vore/sunesound/prey/death_07.ogg','sound/vore/sunesound/prey/death_08.ogg','sound/vore/sunesound/prey/death_09.ogg', + 'sound/vore/sunesound/prey/death_10.ogg') + if ("belches") soundin = pick( + 'sound/vore/belches/belch1.ogg','sound/vore/belches/belch2.ogg','sound/vore/belches/belch3.ogg','sound/vore/belches/belch4.ogg', + 'sound/vore/belches/belch5.ogg','sound/vore/belches/belch6.ogg','sound/vore/belches/belch7.ogg','sound/vore/belches/belch8.ogg', + 'sound/vore/belches/belch9.ogg','sound/vore/belches/belch10.ogg','sound/vore/belches/belch11.ogg','sound/vore/belches/belch12.ogg', + 'sound/vore/belches/belch13.ogg','sound/vore/belches/belch14.ogg','sound/vore/belches/belch15.ogg') + //END VORESTATION EDIT + if ("terminal_type") + soundin = pick('sound/machines/terminal_button01.ogg', 'sound/machines/terminal_button02.ogg', 'sound/machines/terminal_button03.ogg', \ + 'sound/machines/terminal_button04.ogg', 'sound/machines/terminal_button05.ogg', 'sound/machines/terminal_button06.ogg', \ + 'sound/machines/terminal_button07.ogg', 'sound/machines/terminal_button08.ogg') + if("smcalm") + soundin = pick('sound/machines/sm/accent/normal/1.ogg', 'sound/machines/sm/accent/normal/2.ogg', 'sound/machines/sm/accent/normal/3.ogg', 'sound/machines/sm/accent/normal/4.ogg', 'sound/machines/sm/accent/normal/5.ogg', 'sound/machines/sm/accent/normal/6.ogg', 'sound/machines/sm/accent/normal/7.ogg', 'sound/machines/sm/accent/normal/8.ogg', 'sound/machines/sm/accent/normal/9.ogg', 'sound/machines/sm/accent/normal/10.ogg', 'sound/machines/sm/accent/normal/11.ogg', 'sound/machines/sm/accent/normal/12.ogg', 'sound/machines/sm/accent/normal/13.ogg', 'sound/machines/sm/accent/normal/14.ogg', 'sound/machines/sm/accent/normal/15.ogg', 'sound/machines/sm/accent/normal/16.ogg', 'sound/machines/sm/accent/normal/17.ogg', 'sound/machines/sm/accent/normal/18.ogg', 'sound/machines/sm/accent/normal/19.ogg', 'sound/machines/sm/accent/normal/20.ogg', 'sound/machines/sm/accent/normal/21.ogg', 'sound/machines/sm/accent/normal/22.ogg', 'sound/machines/sm/accent/normal/23.ogg', 'sound/machines/sm/accent/normal/24.ogg', 'sound/machines/sm/accent/normal/25.ogg', 'sound/machines/sm/accent/normal/26.ogg', 'sound/machines/sm/accent/normal/27.ogg', 'sound/machines/sm/accent/normal/28.ogg', 'sound/machines/sm/accent/normal/29.ogg', 'sound/machines/sm/accent/normal/30.ogg', 'sound/machines/sm/accent/normal/31.ogg', 'sound/machines/sm/accent/normal/32.ogg', 'sound/machines/sm/accent/normal/33.ogg', 'sound/machines/sm/supermatter1.ogg', 'sound/machines/sm/supermatter2.ogg', 'sound/machines/sm/supermatter3.ogg') + if("smdelam") + soundin = pick('sound/machines/sm/accent/delam/1.ogg', 'sound/machines/sm/accent/normal/2.ogg', 'sound/machines/sm/accent/normal/3.ogg', 'sound/machines/sm/accent/normal/4.ogg', 'sound/machines/sm/accent/normal/5.ogg', 'sound/machines/sm/accent/normal/6.ogg', 'sound/machines/sm/accent/normal/7.ogg', 'sound/machines/sm/accent/normal/8.ogg', 'sound/machines/sm/accent/normal/9.ogg', 'sound/machines/sm/accent/normal/10.ogg', 'sound/machines/sm/accent/normal/11.ogg', 'sound/machines/sm/accent/normal/12.ogg', 'sound/machines/sm/accent/normal/13.ogg', 'sound/machines/sm/accent/normal/14.ogg', 'sound/machines/sm/accent/normal/15.ogg', 'sound/machines/sm/accent/normal/16.ogg', 'sound/machines/sm/accent/normal/17.ogg', 'sound/machines/sm/accent/normal/18.ogg', 'sound/machines/sm/accent/normal/19.ogg', 'sound/machines/sm/accent/normal/20.ogg', 'sound/machines/sm/accent/normal/21.ogg', 'sound/machines/sm/accent/normal/22.ogg', 'sound/machines/sm/accent/normal/23.ogg', 'sound/machines/sm/accent/normal/24.ogg', 'sound/machines/sm/accent/normal/25.ogg', 'sound/machines/sm/accent/normal/26.ogg', 'sound/machines/sm/accent/normal/27.ogg', 'sound/machines/sm/accent/normal/28.ogg', 'sound/machines/sm/accent/normal/29.ogg', 'sound/machines/sm/accent/normal/30.ogg', 'sound/machines/sm/accent/normal/31.ogg', 'sound/machines/sm/accent/normal/32.ogg', 'sound/machines/sm/accent/normal/33.ogg', 'sound/machines/sm/supermatter1.ogg', 'sound/machines/sm/supermatter2.ogg', 'sound/machines/sm/supermatter3.ogg') + if ("generic_drop") + soundin = pick( + 'sound/items/drop/generic1.ogg', + 'sound/items/drop/generic2.ogg' ) + if ("generic_pickup") + soundin = pick( + 'sound/items/pickup/generic1.ogg', + 'sound/items/pickup/generic2.ogg', + 'sound/items/pickup/generic3.ogg') + return soundin + +//Are these even used? //Yes +var/list/keyboard_sound = list ('sound/effects/keyboard/keyboard1.ogg','sound/effects/keyboard/keyboard2.ogg','sound/effects/keyboard/keyboard3.ogg', 'sound/effects/keyboard/keyboard4.ogg') +var/list/bodyfall_sound = list('sound/effects/bodyfall1.ogg','sound/effects/bodyfall2.ogg','sound/effects/bodyfall3.ogg','sound/effects/bodyfall4.ogg') +var/list/teppi_sound = list('sound/voice/teppi/gyooh1.ogg', 'sound/voice/teppi/gyooh2.ogg', 'sound/voice/teppi/gyooh3.ogg', 'sound/voice/teppi/gyooh4.ogg', 'sound/voice/teppi/gyooh5.ogg', 'sound/voice/teppi/gyooh6.ogg', 'sound/voice/teppi/snoot1.ogg', 'sound/voice/teppi/snoot2.ogg') +var/list/talk_sound = list('sound/talksounds/a.ogg','sound/talksounds/b.ogg','sound/talksounds/c.ogg','sound/talksounds/d.ogg','sound/talksounds/e.ogg','sound/talksounds/f.ogg','sound/talksounds/g.ogg','sound/talksounds/h.ogg') +var/list/emote_sound = list('sound/talksounds/me_a.ogg','sound/talksounds/me_b.ogg','sound/talksounds/me_c.ogg','sound/talksounds/me_d.ogg','sound/talksounds/me_e.ogg','sound/talksounds/me_f.ogg') + +//Goon sounds +var/list/goon_speak_one_sound = list('sound/talksounds/goon/speak_1.ogg', 'sound/talksounds/goon/speak_1_ask.ogg', 'sound/talksounds/goon/speak_1_exclaim.ogg') +var/list/goon_speak_two_sound = list('sound/talksounds/goon/speak_2.ogg', 'sound/talksounds/goon/speak_2_ask.ogg', 'sound/talksounds/goon/speak_2_exclaim.ogg') +var/list/goon_speak_three_sound = list('sound/talksounds/goon/speak_3.ogg', 'sound/talksounds/goon/speak_3_ask.ogg', 'sound/talksounds/goon/speak_3_exclaim.ogg') +var/list/goon_speak_four_sound = list('sound/talksounds/goon/speak_4.ogg', 'sound/talksounds/goon/speak_4_ask.ogg', 'sound/talksounds/goon/speak_4_exclaim.ogg') +var/list/goon_speak_blub_sound = list('sound/talksounds/goon/blub.ogg', 'sound/talksounds/goon/blub_ask.ogg', 'sound/talksounds/goon/blub_exclaim.ogg') +var/list/goon_speak_bottalk_sound = list('sound/talksounds/goon/bottalk_1.ogg', 'sound/talksounds/goon/bottalk_2.ogg', 'sound/talksounds/goon/bottalk_3.ogg', 'sound/talksounds/goon/bottalk_4.wav') +var/list/goon_speak_buwoo_sound = list('sound/talksounds/goon/buwoo.ogg', 'sound/talksounds/goon/buwoo_ask.ogg', 'sound/talksounds/goon/buwoo_exclaim.ogg') +var/list/goon_speak_cow_sound = list('sound/talksounds/goon/cow.ogg', 'sound/talksounds/goon/cow_ask.ogg', 'sound/talksounds/goon/cow_exclaim.ogg') +var/list/goon_speak_lizard_sound = list('sound/talksounds/goon/lizard.ogg', 'sound/talksounds/goon/lizard_ask.ogg', 'sound/talksounds/goon/lizard_exclaim.ogg') +var/list/goon_speak_pug_sound = list('sound/talksounds/goon/pug.ogg', 'sound/talksounds/goon/pug_ask.ogg', 'sound/talksounds/goon/pug_exclaim.ogg') +var/list/goon_speak_pugg_sound = list('sound/talksounds/goon/pugg.ogg', 'sound/talksounds/goon/pugg_ask.ogg', 'sound/talksounds/goon/pugg_exclaim.ogg') +var/list/goon_speak_roach_sound = list('sound/talksounds/goon/roach.ogg', 'sound/talksounds/goon/roach_ask.ogg', 'sound/talksounds/goon/roach_exclaim.ogg') +var/list/goon_speak_skelly_sound = list('sound/talksounds/goon/skelly.ogg', 'sound/talksounds/goon/skelly_ask.ogg', 'sound/talksounds/goon/skelly_exclaim.ogg') diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index 57c1d22832d..d60d807950c 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -1,189 +1,191 @@ -/turf/simulated - name = "station" - var/wet = 0 - var/image/wet_overlay = null - - //Mining resources (for the large drills). - var/has_resources - var/list/resources - - var/thermite = 0 - oxygen = MOLES_O2STANDARD - nitrogen = MOLES_N2STANDARD - var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed - var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to - var/can_dirty = TRUE // If false, tile never gets dirty - var/can_start_dirty = TRUE // If false, cannot start dirty roundstart - var/dirty_prob = 2 // Chance of being dirty roundstart - var/dirt = 0 - var/special_temperature //Used for turf HE-Pipe interaction - var/climbable = FALSE //Adds proc to wall if set to TRUE on its initialization, defined here since not all walls are subtypes of wall - - var/icon_edge = 'icons/turf/outdoors_edge.dmi' //VOREStation Addition - Allows for alternative edge icon files - -// This is not great. -/turf/simulated/proc/wet_floor(var/wet_val = 1) - if(wet > 2) //Can't mop up ice - return - spawn(0) - wet = wet_val - if(wet_overlay) - cut_overlay(wet_overlay) - wet_overlay = image('icons/effects/water.dmi', icon_state = "wet_floor") - add_overlay(wet_overlay) - sleep(800) - if(wet == 2) - sleep(3200) - wet = 0 - if(wet_overlay) - cut_overlay(wet_overlay) - wet_overlay = null - -/turf/simulated/proc/freeze_floor() - if(!wet) // Water is required for it to freeze. - return - wet = 3 // icy - if(wet_overlay) - cut_overlay(wet_overlay) - wet_overlay = null - wet_overlay = image('icons/turf/overlays.dmi',src,"snowfloor") - add_overlay(wet_overlay) - spawn(5 MINUTES) - wet = 0 - if(wet_overlay) - cut_overlay(wet_overlay) - wet_overlay = null - -/turf/simulated/clean_blood() - for(var/obj/effect/decal/cleanable/blood/B in contents) - B.clean_blood() - ..() - -/turf/simulated/Initialize(mapload) - . = ..() - if(istype(loc, /area/chapel)) - holy = 1 - levelupdate() - if(climbable) - verbs += /turf/simulated/proc/climb_wall - -/turf/simulated/examine(mob/user) - . = ..() - if(climbable) - . += "This [src] looks climbable." - - -/turf/simulated/proc/AddTracks(var/typepath,var/bloodDNA,var/comingdir,var/goingdir,var/bloodcolor="#A10808") - var/obj/effect/decal/cleanable/blood/tracks/tracks = locate(typepath) in src - if(!tracks) - tracks = new typepath(src) - tracks.AddTracks(bloodDNA,comingdir,goingdir,bloodcolor) - -/turf/simulated/proc/update_dirt() - if(can_dirty) - dirt = min(dirt+1, 101) - var/obj/effect/decal/cleanable/dirt/dirtoverlay = locate(/obj/effect/decal/cleanable/dirt, src) - if (dirt > 50) - if (!dirtoverlay) - dirtoverlay = new/obj/effect/decal/cleanable/dirt(src) - dirtoverlay.alpha = min((dirt - 50) * 5, 255) - -/turf/simulated/Entered(atom/A, atom/OL) - if(movement_disabled && usr.ckey != movement_disabled_exception) - to_chat(usr, "Movement is admin-disabled.") //This is to identify lag problems - return - - if (istype(A,/mob/living)) - var/mob/living/M = A - if(M.lying || M.flying) //VOREStation Edit - return ..() - - if(M.dirties_floor()) - // Dirt overlays. - update_dirt() - - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - // Tracking blood - var/list/bloodDNA = null - var/bloodcolor="" - if(H.shoes) - var/obj/item/clothing/shoes/S = H.shoes - if(istype(S)) - S.handle_movement(src,(H.m_intent == "run" ? 1 : 0)) - if(S.track_blood && S.blood_DNA) - bloodDNA = S.blood_DNA - bloodcolor=S.blood_color - S.track_blood-- - else - if(H.track_blood && H.feet_blood_DNA) - bloodDNA = H.feet_blood_DNA - bloodcolor = H.feet_blood_color - H.track_blood-- - - if (bloodDNA) - src.AddTracks(H.species.get_move_trail(H),bloodDNA,H.dir,0,bloodcolor) // Coming - var/turf/simulated/from = get_step(H,reverse_direction(H.dir)) - if(istype(from) && from) - from.AddTracks(H.species.get_move_trail(H),bloodDNA,0,H.dir,bloodcolor) // Going - - bloodDNA = null - - if(src.wet) - - if(M.buckled || (src.wet == 1 && M.m_intent == "walk")) - return - - var/slip_dist = 1 - var/slip_stun = 6 - var/floor_type = "wet" - - switch(src.wet) - if(2) // Lube - floor_type = "slippery" - slip_dist = 4 - slip_stun = 10 - if(3) // Ice - floor_type = "icy" - slip_stun = 4 - slip_dist = 2 - - if(M.slip("the [floor_type] floor", slip_stun)) - for(var/i = 1 to slip_dist) - if(isbelly(M.loc)) //VOREEdit, Stop the slip if we're in a belly. Inspired by a chompedit, cleaned it up with isbelly instead of a variable since the var was resetting too fast. - return - step(M, M.dir) - sleep(1) - else - M.inertia_dir = 0 - else - M.inertia_dir = 0 - - ..() - -//returns 1 if made bloody, returns 0 otherwise -/turf/simulated/add_blood(mob/living/carbon/human/M as mob) - if (!..()) - return 0 - - if(istype(M)) - for(var/obj/effect/decal/cleanable/blood/B in contents) - if(!B.blood_DNA) - B.blood_DNA = list() - if(!B.blood_DNA[M.dna.unique_enzymes]) - B.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type - B.virus2 = virus_copylist(M.virus2) - return 1 //we bloodied the floor - blood_splatter(src,M.get_blood(M.vessel),1) - return 1 //we bloodied the floor - return 0 - -// Only adds blood on the floor -- Skie -/turf/simulated/proc/add_blood_floor(mob/living/carbon/M as mob) - if( istype(M, /mob/living/carbon/alien )) - var/obj/effect/decal/cleanable/blood/xeno/this = new /obj/effect/decal/cleanable/blood/xeno(src) - this.blood_DNA["UNKNOWN BLOOD"] = "X*" - else if( istype(M, /mob/living/silicon/robot )) - new /obj/effect/decal/cleanable/blood/oil(src) - else if(ishuman(M)) - add_blood(M) +/turf/simulated + name = "station" + var/wet = 0 + var/image/wet_overlay = null + + //Mining resources (for the large drills). + var/has_resources + var/list/resources + + var/thermite = 0 + oxygen = MOLES_O2STANDARD + nitrogen = MOLES_N2STANDARD + var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed + var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to + var/can_dirty = TRUE // If false, tile never gets dirty + var/can_start_dirty = TRUE // If false, cannot start dirty roundstart + var/dirty_prob = 2 // Chance of being dirty roundstart + var/dirt = 0 + var/special_temperature //Used for turf HE-Pipe interaction + var/climbable = FALSE //Adds proc to wall if set to TRUE on its initialization, defined here since not all walls are subtypes of wall + + var/icon_edge = 'icons/turf/outdoors_edge.dmi' //VOREStation Addition - Allows for alternative edge icon files + +// This is not great. +/turf/simulated/proc/wet_floor(var/wet_val = 1) + if(wet > 2) //Can't mop up ice + return + spawn(0) + wet = wet_val + if(wet_overlay) + cut_overlay(wet_overlay) + wet_overlay = image('icons/effects/water.dmi', icon_state = "wet_floor") + add_overlay(wet_overlay) + sleep(800) + if(wet == 2) + sleep(3200) + wet = 0 + if(wet_overlay) + cut_overlay(wet_overlay) + wet_overlay = null + +/turf/simulated/proc/freeze_floor() + if(!wet) // Water is required for it to freeze. + return + wet = 3 // icy + if(wet_overlay) + cut_overlay(wet_overlay) + wet_overlay = null + wet_overlay = image('icons/turf/overlays.dmi',src,"snowfloor") + add_overlay(wet_overlay) + spawn(5 MINUTES) + wet = 0 + if(wet_overlay) + cut_overlay(wet_overlay) + wet_overlay = null + +/turf/simulated/clean_blood() + for(var/obj/effect/decal/cleanable/blood/B in contents) + B.clean_blood() + ..() + +/turf/simulated/Initialize(mapload) + . = ..() + if(istype(loc, /area/chapel)) + holy = 1 + levelupdate() + if(climbable) + verbs += /turf/simulated/proc/climb_wall + if(is_outdoors()) //VOREStation edit - quick fix for a planetary lighting issue + SSplanets.addTurf(src) + +/turf/simulated/examine(mob/user) + . = ..() + if(climbable) + . += "This [src] looks climbable." + + +/turf/simulated/proc/AddTracks(var/typepath,var/bloodDNA,var/comingdir,var/goingdir,var/bloodcolor="#A10808") + var/obj/effect/decal/cleanable/blood/tracks/tracks = locate(typepath) in src + if(!tracks) + tracks = new typepath(src) + tracks.AddTracks(bloodDNA,comingdir,goingdir,bloodcolor) + +/turf/simulated/proc/update_dirt() + if(can_dirty) + dirt = min(dirt+1, 101) + var/obj/effect/decal/cleanable/dirt/dirtoverlay = locate(/obj/effect/decal/cleanable/dirt, src) + if (dirt > 50) + if (!dirtoverlay) + dirtoverlay = new/obj/effect/decal/cleanable/dirt(src) + dirtoverlay.alpha = min((dirt - 50) * 5, 255) + +/turf/simulated/Entered(atom/A, atom/OL) + if(movement_disabled && usr.ckey != movement_disabled_exception) + to_chat(usr, "Movement is admin-disabled.") //This is to identify lag problems + return + + if (istype(A,/mob/living)) + var/mob/living/M = A + if(M.lying || M.flying) //VOREStation Edit + return ..() + + if(M.dirties_floor()) + // Dirt overlays. + update_dirt() + + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + // Tracking blood + var/list/bloodDNA = null + var/bloodcolor="" + if(H.shoes) + var/obj/item/clothing/shoes/S = H.shoes + if(istype(S)) + S.handle_movement(src,(H.m_intent == "run" ? 1 : 0)) + if(S.track_blood && S.blood_DNA) + bloodDNA = S.blood_DNA + bloodcolor=S.blood_color + S.track_blood-- + else + if(H.track_blood && H.feet_blood_DNA) + bloodDNA = H.feet_blood_DNA + bloodcolor = H.feet_blood_color + H.track_blood-- + + if (bloodDNA) + src.AddTracks(H.species.get_move_trail(H),bloodDNA,H.dir,0,bloodcolor) // Coming + var/turf/simulated/from = get_step(H,reverse_direction(H.dir)) + if(istype(from) && from) + from.AddTracks(H.species.get_move_trail(H),bloodDNA,0,H.dir,bloodcolor) // Going + + bloodDNA = null + + if(src.wet) + + if(M.buckled || (src.wet == 1 && M.m_intent == "walk")) + return + + var/slip_dist = 1 + var/slip_stun = 6 + var/floor_type = "wet" + + switch(src.wet) + if(2) // Lube + floor_type = "slippery" + slip_dist = 4 + slip_stun = 10 + if(3) // Ice + floor_type = "icy" + slip_stun = 4 + slip_dist = 2 + + if(M.slip("the [floor_type] floor", slip_stun)) + for(var/i = 1 to slip_dist) + if(isbelly(M.loc)) //VOREEdit, Stop the slip if we're in a belly. Inspired by a chompedit, cleaned it up with isbelly instead of a variable since the var was resetting too fast. + return + step(M, M.dir) + sleep(1) + else + M.inertia_dir = 0 + else + M.inertia_dir = 0 + + ..() + +//returns 1 if made bloody, returns 0 otherwise +/turf/simulated/add_blood(mob/living/carbon/human/M as mob) + if (!..()) + return 0 + + if(istype(M)) + for(var/obj/effect/decal/cleanable/blood/B in contents) + if(!B.blood_DNA) + B.blood_DNA = list() + if(!B.blood_DNA[M.dna.unique_enzymes]) + B.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type + B.virus2 = virus_copylist(M.virus2) + return 1 //we bloodied the floor + blood_splatter(src,M.get_blood(M.vessel),1) + return 1 //we bloodied the floor + return 0 + +// Only adds blood on the floor -- Skie +/turf/simulated/proc/add_blood_floor(mob/living/carbon/M as mob) + if( istype(M, /mob/living/carbon/alien )) + var/obj/effect/decal/cleanable/blood/xeno/this = new /obj/effect/decal/cleanable/blood/xeno(src) + this.blood_DNA["UNKNOWN BLOOD"] = "X*" + else if( istype(M, /mob/living/silicon/robot )) + new /obj/effect/decal/cleanable/blood/oil(src) + else if(ishuman(M)) + add_blood(M) diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index d45c33f9180..faa2351b6fb 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -1,185 +1,185 @@ -/turf/simulated/floor - name = "plating" - desc = "Unfinished flooring." - icon = 'icons/turf/flooring/plating_vr.dmi' - icon_state = "plating" - - // Damage to flooring. - var/broken - var/burnt - - // Plating data. - var/base_name = "plating" - var/base_desc = "The naked hull." - var/base_icon = 'icons/turf/flooring/plating_vr.dmi' - var/base_icon_state = "plating" - var/static/list/base_footstep_sounds = list("human" = list( - 'sound/effects/footstep/plating1.ogg', - 'sound/effects/footstep/plating2.ogg', - 'sound/effects/footstep/plating3.ogg', - 'sound/effects/footstep/plating4.ogg', - 'sound/effects/footstep/plating5.ogg')) - - var/list/old_decals = null - - // Flooring data. - var/flooring_override - var/initial_flooring - var/decl/flooring/flooring - var/mineral = DEFAULT_WALL_MATERIAL - var/can_be_plated = TRUE // This is here for inheritance's sake. Override to FALSE for turfs you don't want someone to simply slap a plating over such as hazards. - - thermal_conductivity = 0.040 - heat_capacity = 10000 - -/turf/simulated/floor/is_plating() - return (!flooring || flooring.is_plating) - -/turf/simulated/floor/Initialize(mapload, floortype) - . = ..() - if(!floortype && initial_flooring) - floortype = initial_flooring - if(floortype) - set_flooring(get_flooring_data(floortype), TRUE) - . = INITIALIZE_HINT_LATELOAD // We'll update our icons after everyone is ready - else - footstep_sounds = base_footstep_sounds - if(can_dirty && can_start_dirty) - if(prob(dirty_prob)) - dirt += rand(50,100) - update_dirt() //5% chance to start with dirt on a floor tile- give the janitor something to do - -/turf/simulated/floor/LateInitialize() - . = ..() - update_icon(1) - -/turf/simulated/floor/proc/swap_decals() - var/current_decals = decals - decals = old_decals - old_decals = current_decals - -/turf/simulated/floor/proc/set_flooring(var/decl/flooring/newflooring, var/initializing) - //make_plating(defer_icon_update = 1) - if(is_plating() && !initializing) // Plating -> Flooring - swap_decals() - flooring = newflooring - footstep_sounds = newflooring.footstep_sounds - if(!initializing) - update_icon(1) - levelupdate() - -//This proc will set floor_type to null and the update_icon() proc will then change the icon_state of the turf -//This proc auto corrects the grass tiles' siding. -/turf/simulated/floor/proc/make_plating(var/place_product, var/defer_icon_update) - cut_overlays() - - for(var/obj/effect/decal/writing/W in src) - qdel(W) - - name = base_name - desc = base_desc - icon = base_icon - icon_state = base_icon_state - footstep_sounds = base_footstep_sounds - - if(!is_plating()) // Flooring -> Plating - swap_decals() - if(flooring.build_type && place_product) - new flooring.build_type(src) - var/newtype = flooring.get_plating_type() - if(newtype) // Has a custom plating type to become - set_flooring(get_flooring_data(newtype)) - else - flooring = null - - set_light(0) - broken = null - burnt = null - flooring_override = null - levelupdate() - - if(!defer_icon_update) - update_icon(1) - -/turf/simulated/floor/levelupdate() - var/floored_over = !is_plating() - for(var/obj/O in src) - O.hide(O.hides_under_flooring() && floored_over) - -/turf/simulated/floor/can_engrave() - return (!flooring || flooring.can_engrave) - -/turf/simulated/floor/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_FLOORWALL) - // A wall costs four sheets to build (two for the grider and two for finishing it). - var/cost = RCD_SHEETS_PER_MATTER_UNIT * 4 - // R-walls cost five sheets, however. - if(the_rcd.make_rwalls) - cost += RCD_SHEETS_PER_MATTER_UNIT * 1 - return list( - RCD_VALUE_MODE = RCD_FLOORWALL, - RCD_VALUE_DELAY = 2 SECONDS, - RCD_VALUE_COST = cost - ) - if(RCD_AIRLOCK) - // Airlock assemblies cost four sheets. Let's just add another for the electronics/wires/etc. - return list( - RCD_VALUE_MODE = RCD_AIRLOCK, - RCD_VALUE_DELAY = 5 SECONDS, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 - ) - if(RCD_WINDOWGRILLE) - // One steel sheet for the girder (two rods, which is one sheet). - return list( - RCD_VALUE_MODE = RCD_WINDOWGRILLE, - RCD_VALUE_DELAY = 1 SECOND, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 1 - ) - if(RCD_DECONSTRUCT) - // Old RCDs made deconning the floor cost 10 units (IE, three times on full RCD). - // Now it's ten sheets worth of units (which is the same capacity-wise, three times on full RCD). - return list( - RCD_VALUE_MODE = RCD_DECONSTRUCT, - RCD_VALUE_DELAY = 5 SECONDS, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 10 - ) - return FALSE - - -/turf/simulated/floor/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_FLOORWALL) - to_chat(user, span("notice", "You build a wall.")) - ChangeTurf(/turf/simulated/wall) - var/turf/simulated/wall/T = get_turf(src) // Ref to the wall we just built. - // Apparently set_material(...) for walls requires refs to the material singletons and not strings. - // This is different from how other material objects with their own set_material(...) do it, but whatever. - var/datum/material/M = name_to_material[the_rcd.material_to_use] - T.set_material(M, the_rcd.make_rwalls ? M : null, M) - T.add_hiddenprint(user) - return TRUE - if(RCD_AIRLOCK) - if(locate(/obj/machinery/door/airlock) in src) - return FALSE // No more airlock stacking. - to_chat(user, span("notice", "You build an airlock.")) - new the_rcd.airlock_type(src) - return TRUE - if(RCD_WINDOWGRILLE) - if(locate(/obj/structure/grille) in src) - return FALSE - to_chat(user, span("notice", "You construct the grille.")) - var/obj/structure/grille/G = new(src) - G.anchored = TRUE - return TRUE - if(RCD_DECONSTRUCT) - to_chat(user, span("notice", "You deconstruct \the [src].")) - ChangeTurf(get_base_turf_by_area(src), preserve_outdoors = TRUE) - return TRUE - -/turf/simulated/floor/AltClick(mob/user) - if(isliving(user)) - var/mob/living/livingUser = user - if(try_graffiti(livingUser, livingUser.get_active_hand())) - return +/turf/simulated/floor + name = "plating" + desc = "Unfinished flooring." + icon = 'icons/turf/flooring/plating_vr.dmi' + icon_state = "plating" + + // Damage to flooring. + var/broken + var/burnt + + // Plating data. + var/base_name = "plating" + var/base_desc = "The naked hull." + var/base_icon = 'icons/turf/flooring/plating_vr.dmi' + var/base_icon_state = "plating" + var/static/list/base_footstep_sounds = list("human" = list( + 'sound/effects/footstep/plating1.ogg', + 'sound/effects/footstep/plating2.ogg', + 'sound/effects/footstep/plating3.ogg', + 'sound/effects/footstep/plating4.ogg', + 'sound/effects/footstep/plating5.ogg')) + + var/list/old_decals = null + + // Flooring data. + var/flooring_override + var/initial_flooring + var/decl/flooring/flooring + var/mineral = DEFAULT_WALL_MATERIAL + var/can_be_plated = TRUE // This is here for inheritance's sake. Override to FALSE for turfs you don't want someone to simply slap a plating over such as hazards. + + thermal_conductivity = 0.040 + heat_capacity = 10000 + +/turf/simulated/floor/is_plating() + return (!flooring || flooring.is_plating) + +/turf/simulated/floor/Initialize(mapload, floortype) + . = ..() + if(!floortype && initial_flooring) + floortype = initial_flooring + if(floortype) + set_flooring(get_flooring_data(floortype), TRUE) + . = INITIALIZE_HINT_LATELOAD // We'll update our icons after everyone is ready + else + footstep_sounds = base_footstep_sounds + if(can_dirty && can_start_dirty) + if(prob(dirty_prob)) + dirt += rand(50,100) + update_dirt() //5% chance to start with dirt on a floor tile- give the janitor something to do + +/turf/simulated/floor/LateInitialize() + . = ..() + update_icon(1) + +/turf/simulated/floor/proc/swap_decals() + var/current_decals = decals + decals = old_decals + old_decals = current_decals + +/turf/simulated/floor/proc/set_flooring(var/decl/flooring/newflooring, var/initializing) + //make_plating(defer_icon_update = 1) + if(is_plating() && !initializing) // Plating -> Flooring + swap_decals() + flooring = newflooring + footstep_sounds = newflooring.footstep_sounds + if(!initializing) + update_icon(1) + levelupdate() + +//This proc will set floor_type to null and the update_icon() proc will then change the icon_state of the turf +//This proc auto corrects the grass tiles' siding. +/turf/simulated/floor/proc/make_plating(var/place_product, var/defer_icon_update) + cut_overlays() + + for(var/obj/effect/decal/writing/W in src) + qdel(W) + + name = base_name + desc = base_desc + icon = base_icon + icon_state = base_icon_state + footstep_sounds = base_footstep_sounds + + if(!is_plating()) // Flooring -> Plating + swap_decals() + if(flooring.build_type && place_product) + new flooring.build_type(src) + var/newtype = flooring.get_plating_type() + if(newtype) // Has a custom plating type to become + set_flooring(get_flooring_data(newtype)) + else + flooring = null + + set_light(0) + broken = null + burnt = null + flooring_override = null + levelupdate() + + if(!defer_icon_update) + update_icon(1) + +/turf/simulated/floor/levelupdate() + var/floored_over = !is_plating() + for(var/obj/O in src) + O.hide(O.hides_under_flooring() && floored_over) + +/turf/simulated/floor/can_engrave() + return (!flooring || flooring.can_engrave) + +/turf/simulated/floor/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_FLOORWALL) + // A wall costs four sheets to build (two for the grider and two for finishing it). + var/cost = RCD_SHEETS_PER_MATTER_UNIT * 4 + // R-walls cost five sheets, however. + if(the_rcd.make_rwalls) + cost += RCD_SHEETS_PER_MATTER_UNIT * 1 + return list( + RCD_VALUE_MODE = RCD_FLOORWALL, + RCD_VALUE_DELAY = 2 SECONDS, + RCD_VALUE_COST = cost + ) + if(RCD_AIRLOCK) + // Airlock assemblies cost four sheets. Let's just add another for the electronics/wires/etc. + return list( + RCD_VALUE_MODE = RCD_AIRLOCK, + RCD_VALUE_DELAY = 5 SECONDS, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 + ) + if(RCD_WINDOWGRILLE) + // One steel sheet for the girder (two rods, which is one sheet). + return list( + RCD_VALUE_MODE = RCD_WINDOWGRILLE, + RCD_VALUE_DELAY = 1 SECOND, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 1 + ) + if(RCD_DECONSTRUCT) + // Old RCDs made deconning the floor cost 10 units (IE, three times on full RCD). + // Now it's ten sheets worth of units (which is the same capacity-wise, three times on full RCD). + return list( + RCD_VALUE_MODE = RCD_DECONSTRUCT, + RCD_VALUE_DELAY = 5 SECONDS, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 10 + ) + return FALSE + + +/turf/simulated/floor/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_FLOORWALL) + to_chat(user, span("notice", "You build a wall.")) + ChangeTurf(/turf/simulated/wall) + var/turf/simulated/wall/T = get_turf(src) // Ref to the wall we just built. + // Apparently set_material(...) for walls requires refs to the material singletons and not strings. + // This is different from how other material objects with their own set_material(...) do it, but whatever. + var/datum/material/M = name_to_material[the_rcd.material_to_use] + T.set_material(M, the_rcd.make_rwalls ? M : null, M) + T.add_hiddenprint(user) + return TRUE + if(RCD_AIRLOCK) + if(locate(/obj/machinery/door/airlock) in src) + return FALSE // No more airlock stacking. + to_chat(user, span("notice", "You build an airlock.")) + new the_rcd.airlock_type(src) + return TRUE + if(RCD_WINDOWGRILLE) + if(locate(/obj/structure/grille) in src) + return FALSE + to_chat(user, span("notice", "You construct the grille.")) + var/obj/structure/grille/G = new(src) + G.anchored = TRUE + return TRUE + if(RCD_DECONSTRUCT) + to_chat(user, span("notice", "You deconstruct \the [src].")) + ChangeTurf(get_base_turf_by_area(src), preserve_outdoors = TRUE) + return TRUE + +/turf/simulated/floor/AltClick(mob/user) + if(isliving(user)) + var/mob/living/livingUser = user + if(try_graffiti(livingUser, livingUser.get_active_hand())) + return . = ..() \ No newline at end of file diff --git a/code/game/turfs/simulated/floor_types.dm b/code/game/turfs/simulated/floor_types.dm index 96532a954a5..28505a7397f 100644 --- a/code/game/turfs/simulated/floor_types.dm +++ b/code/game/turfs/simulated/floor_types.dm @@ -1,291 +1,291 @@ -/turf/simulated/floor/diona - name = "biomass flooring" - icon_state = "diona" - -/turf/simulated/floor/diona/attackby() - return - -//Shuttle Floors -/obj/landed_holder - name = "landed turf holder" - desc = "holds all the info about the turf this turf 'landed on'" - var/turf/turf_type - var/turf/simulated/shuttle/my_turf - var/image/turf_image - var/list/decals - -/obj/landed_holder/New(var/location = null, var/turf/simulated/shuttle/turf) - ..(null) - my_turf = turf - -/obj/landed_holder/proc/land_on(var/turf/T) - //Gather destination information - var/obj/landed_holder/new_holder = new(null) - new_holder.turf_type = T.type - new_holder.dir = T.dir - new_holder.icon = T.icon - new_holder.icon_state = T.icon_state - new_holder.copy_overlays(T, TRUE) - new_holder.underlays = T.underlays.Copy() - new_holder.decals = T.decals ? T.decals.Copy() : null - - //Set the destination to be like us - var/turf/simulated/shuttle/new_dest = T.ChangeTurf(my_turf.type,,1) - new_dest.set_dir(my_turf.dir) - new_dest.icon_state = my_turf.icon_state - new_dest.icon = my_turf.icon - new_dest.copy_overlays(my_turf, TRUE) - new_dest.underlays = my_turf.underlays - new_dest.decals = my_turf.decals - //Shuttle specific stuff - new_dest.interior_corner = my_turf.interior_corner - new_dest.takes_underlays = my_turf.takes_underlays - new_dest.under_turf = my_turf.under_turf - new_dest.join_flags = my_turf.join_flags - new_dest.join_group = my_turf.join_group - - // Associate the holder with the new turf. - new_holder.my_turf = new_dest - new_dest.landed_holder = new_holder - - //Update underlays if necessary (interior corners won't have changed). - if(new_dest.takes_underlays && !new_dest.interior_corner) - new_dest.underlay_update() - - return new_dest - -/obj/landed_holder/proc/leave_turf(var/turf/base_turf = null) - var/turf/new_source - //Change our source to whatever it was before - if(turf_type) - new_source = my_turf.ChangeTurf(turf_type,,1) - new_source.set_dir(dir) - new_source.icon_state = icon_state - new_source.icon = icon - new_source.copy_overlays(src, TRUE) - new_source.underlays = underlays - new_source.decals = decals - else - new_source = my_turf.ChangeTurf(base_turf ? base_turf : get_base_turf_by_area(my_turf),,1) - - return new_source - -/turf/simulated/shuttle - name = "shuttle" - icon = 'icons/turf/shuttle_white.dmi' - thermal_conductivity = 0.05 - heat_capacity = 0 - flags = TURF_ACID_IMMUNE - - var/obj/landed_holder/landed_holder - var/interior_corner = 0 - var/takes_underlays = 0 - var/turf/under_turf //Underlay override turf path. - var/join_flags = 0 //Bitstring to represent adjacency of joining walls - var/join_group = "shuttle" //A tag for what other walls to join with. Null if you don't want them to. - var/static/list/antilight_cache - -/turf/simulated/shuttle/Initialize(mapload) - . = ..() - if(!antilight_cache) - antilight_cache = list() - for(var/diag in cornerdirs) - var/image/I = image(LIGHTING_ICON, null, icon_state = "diagonals", layer = 10, dir = diag) - I.plane = PLANE_LIGHTING - antilight_cache["[diag]"] = I - -/turf/simulated/shuttle/Destroy() - landed_holder = null - return ..() - -// For joined corners touching static lighting turfs, add an overlay to cancel out that part of our lighting overlay. -/turf/simulated/shuttle/proc/update_breaklights() - if(join_flags in cornerdirs) //We're joined at an angle - //Dynamic lighting dissolver - var/turf/T = get_step(src, turn(join_flags,180)) - if(!T || !T.dynamic_lighting || !get_area(T).dynamic_lighting) - add_overlay(antilight_cache["[join_flags]"], TRUE) - return - cut_overlay(antilight_cache["[join_flags]"], TRUE) - -/turf/simulated/shuttle/proc/underlay_update() - if(!takes_underlays) - //Basically, if it's not forced, and we don't care, don't do it. - return - - var/turf/under //May be a path or a turf - var/mutable_appearance/us = new(src) //We'll use this for changes later - us.underlays.Cut() - - //Mapper wanted something specific - if(under_turf) - under = under_turf - - //Well if this isn't our first rodeo, we know EXACTLY what we landed on, and it looks like this. - if(landed_holder && !interior_corner) - //Space gets special treatment - if(ispath(landed_holder.turf_type, /turf/space)) - var/image/spaceimage = image(landed_holder.icon, landed_holder.icon_state) - spaceimage.plane = SPACE_PLANE - underlays = list(spaceimage) - else - var/mutable_appearance/landed_on = new(landed_holder) - landed_on.layer = FLOAT_LAYER //Not turf - landed_on.plane = FLOAT_PLANE //Not turf - us.underlays = list(landed_on) - appearance = us - - spawn update_breaklights() //So that we update the breaklight overlays only after turfs are connected - return - - if(!under) - var/turf/T1 - var/turf/T2 - var/turf/T3 - - T1 = get_step(src, turn(join_flags,135)) // 45 degrees before opposite - T2 = get_step(src, turn(join_flags,225)) // 45 degrees beyond opposite - T3 = get_step(src, turn(join_flags,180)) // Opposite from the diagonal - - if(isfloor(T1) && ((T1.type == T2.type) || (T1.type == T3.type))) - under = T1 - else if(isfloor(T2) && T2.type == T3.type) - under = T2 - else if(isfloor(T3) || istype(T3,/turf/space/transit)) - under = T3 - else - under = get_base_turf_by_area(src) - - if(istype(under,/turf/simulated/shuttle)) - interior_corner = 1 //Prevents us from 'landing on grass' and having interior corners update. - - var/mutable_appearance/under_ma - - if(ispath(under)) //It's just a mapper-specified path - under_ma = new() - under_ma.icon = initial(under.icon) - under_ma.icon_state = initial(under.icon_state) - under_ma.color = initial(under.color) - - else //It's a real turf - under_ma = new(under) - - if(under_ma) - if(ispath(under,/turf/space) || istype(under,/turf/space)) //Space gets weird treatment - under_ma.icon_state = "white" - under_ma.plane = SPACE_PLANE - us.underlays = list(under_ma) - - appearance = us - - spawn update_breaklights() //So that we update the breaklight overlays only after turfs are connected - - return under - -/turf/simulated/shuttle/floor - name = "floor" - icon = 'icons/turf/flooring/shuttle.dmi' - icon_state = "floor_blue" - -/turf/simulated/shuttle/floor/red - icon_state = "floor_red" - -/turf/simulated/shuttle/floor/yellow - icon_state = "floor_yellow" - -/turf/simulated/shuttle/floor/darkred - icon_state = "floor_dred" - -/turf/simulated/shuttle/floor/purple - icon_state = "floor_purple" - -/turf/simulated/shuttle/floor/white - icon_state = "floor_white" - -/turf/simulated/shuttle/floor/black - icon_state = "floor_black" - -/turf/simulated/shuttle/floor/glass - icon_state = "floor_glass" - takes_underlays = 1 - -/turf/simulated/shuttle/floor/alien - icon_state = "alienpod1" - light_range = 3 - light_power = 0.6 - light_color = "#66ffff" // Bright cyan. - light_on = TRUE - block_tele = TRUE - -/turf/simulated/shuttle/floor/alien/Initialize() - . = ..() - icon_state = "alienpod[rand(1, 9)]" - update_light() - -/turf/simulated/shuttle/floor/alienplating - icon_state = "alienplating" - block_tele = TRUE - -/turf/simulated/shuttle/floor/alienplating/external // For the outer rim of the UFO, to avoid active edges. -// The actual temperature adjustment is defined if the SC or other future map is compiled. - -/turf/simulated/shuttle/plating - name = "plating" - icon = 'icons/turf/floors.dmi' - icon_state = "plating" - -/turf/simulated/shuttle/plating/airless - oxygen = 0 - nitrogen = 0 - -//For 'carrying' otherwise empty turfs or stuff in space turfs with you or having holes in the floor or whatever. -/turf/simulated/shuttle/plating/carry - name = "carry turf" - icon = 'icons/turf/shuttle_parts.dmi' - icon_state = "carry" - takes_underlays = 1 - blocks_air = 1 //I'd make these unsimulated but it just fucks with so much stuff so many other places. - -/turf/simulated/shuttle/plating/carry/Initialize() - . = ..() - icon_state = "carry_ingame" - -/turf/simulated/shuttle/plating/airless/carry - name = "airless carry turf" - icon = 'icons/turf/shuttle_parts.dmi' - icon_state = "carry" - takes_underlays = 1 - blocks_air = 1 - -/turf/simulated/shuttle/plating/airless/carry/Initialize() - . = ..() - icon_state = "carry_ingame" - -/turf/simulated/shuttle/plating/skipjack //Skipjack plating - oxygen = 0 - nitrogen = MOLES_N2STANDARD + MOLES_O2STANDARD - -/turf/simulated/shuttle/floor/skipjack //Skipjack floors - name = "skipjack floor" - icon_state = "floor_dred" - oxygen = 0 - nitrogen = MOLES_N2STANDARD + MOLES_O2STANDARD - -/turf/simulated/shuttle/floor/voidcraft - name = "voidcraft tiles" - icon_state = "void" - -/turf/simulated/shuttle/floor/voidcraft/dark - name = "voidcraft tiles" - icon_state = "void_dark" - -/turf/simulated/shuttle/floor/voidcraft/light - name = "voidcraft tiles" - icon_state = "void_light" - -/turf/simulated/shuttle/floor/voidcraft/external // For avoiding active edges. -// The actual temperature adjustment is defined if the SC or other future map is compiled. - -/turf/simulated/shuttle/floor/voidcraft/external/dark - -/turf/simulated/shuttle/floor/voidcraft/external/light +/turf/simulated/floor/diona + name = "biomass flooring" + icon_state = "diona" + +/turf/simulated/floor/diona/attackby() + return + +//Shuttle Floors +/obj/landed_holder + name = "landed turf holder" + desc = "holds all the info about the turf this turf 'landed on'" + var/turf/turf_type + var/turf/simulated/shuttle/my_turf + var/image/turf_image + var/list/decals + +/obj/landed_holder/New(var/location = null, var/turf/simulated/shuttle/turf) + ..(null) + my_turf = turf + +/obj/landed_holder/proc/land_on(var/turf/T) + //Gather destination information + var/obj/landed_holder/new_holder = new(null) + new_holder.turf_type = T.type + new_holder.dir = T.dir + new_holder.icon = T.icon + new_holder.icon_state = T.icon_state + new_holder.copy_overlays(T, TRUE) + new_holder.underlays = T.underlays.Copy() + new_holder.decals = T.decals ? T.decals.Copy() : null + + //Set the destination to be like us + var/turf/simulated/shuttle/new_dest = T.ChangeTurf(my_turf.type,,1) + new_dest.set_dir(my_turf.dir) + new_dest.icon_state = my_turf.icon_state + new_dest.icon = my_turf.icon + new_dest.copy_overlays(my_turf, TRUE) + new_dest.underlays = my_turf.underlays + new_dest.decals = my_turf.decals + //Shuttle specific stuff + new_dest.interior_corner = my_turf.interior_corner + new_dest.takes_underlays = my_turf.takes_underlays + new_dest.under_turf = my_turf.under_turf + new_dest.join_flags = my_turf.join_flags + new_dest.join_group = my_turf.join_group + + // Associate the holder with the new turf. + new_holder.my_turf = new_dest + new_dest.landed_holder = new_holder + + //Update underlays if necessary (interior corners won't have changed). + if(new_dest.takes_underlays && !new_dest.interior_corner) + new_dest.underlay_update() + + return new_dest + +/obj/landed_holder/proc/leave_turf(var/turf/base_turf = null) + var/turf/new_source + //Change our source to whatever it was before + if(turf_type) + new_source = my_turf.ChangeTurf(turf_type,,1) + new_source.set_dir(dir) + new_source.icon_state = icon_state + new_source.icon = icon + new_source.copy_overlays(src, TRUE) + new_source.underlays = underlays + new_source.decals = decals + else + new_source = my_turf.ChangeTurf(base_turf ? base_turf : get_base_turf_by_area(my_turf),,1) + + return new_source + +/turf/simulated/shuttle + name = "shuttle" + icon = 'icons/turf/shuttle_white.dmi' + thermal_conductivity = 0.05 + heat_capacity = 0 + flags = TURF_ACID_IMMUNE + + var/obj/landed_holder/landed_holder + var/interior_corner = 0 + var/takes_underlays = 0 + var/turf/under_turf //Underlay override turf path. + var/join_flags = 0 //Bitstring to represent adjacency of joining walls + var/join_group = "shuttle" //A tag for what other walls to join with. Null if you don't want them to. + var/static/list/antilight_cache + +/turf/simulated/shuttle/Initialize(mapload) + . = ..() + if(!antilight_cache) + antilight_cache = list() + for(var/diag in cornerdirs) + var/image/I = image(LIGHTING_ICON, null, icon_state = "diagonals", layer = 10, dir = diag) + I.plane = PLANE_LIGHTING + antilight_cache["[diag]"] = I + +/turf/simulated/shuttle/Destroy() + landed_holder = null + return ..() + +// For joined corners touching static lighting turfs, add an overlay to cancel out that part of our lighting overlay. +/turf/simulated/shuttle/proc/update_breaklights() + if(join_flags in cornerdirs) //We're joined at an angle + //Dynamic lighting dissolver + var/turf/T = get_step(src, turn(join_flags,180)) + if(!T || !T.dynamic_lighting || !get_area(T).dynamic_lighting) + add_overlay(antilight_cache["[join_flags]"], TRUE) + return + cut_overlay(antilight_cache["[join_flags]"], TRUE) + +/turf/simulated/shuttle/proc/underlay_update() + if(!takes_underlays) + //Basically, if it's not forced, and we don't care, don't do it. + return + + var/turf/under //May be a path or a turf + var/mutable_appearance/us = new(src) //We'll use this for changes later + us.underlays.Cut() + + //Mapper wanted something specific + if(under_turf) + under = under_turf + + //Well if this isn't our first rodeo, we know EXACTLY what we landed on, and it looks like this. + if(landed_holder && !interior_corner) + //Space gets special treatment + if(ispath(landed_holder.turf_type, /turf/space)) + var/image/spaceimage = image(landed_holder.icon, landed_holder.icon_state) + spaceimage.plane = SPACE_PLANE + underlays = list(spaceimage) + else + var/mutable_appearance/landed_on = new(landed_holder) + landed_on.layer = FLOAT_LAYER //Not turf + landed_on.plane = FLOAT_PLANE //Not turf + us.underlays = list(landed_on) + appearance = us + + spawn update_breaklights() //So that we update the breaklight overlays only after turfs are connected + return + + if(!under) + var/turf/T1 + var/turf/T2 + var/turf/T3 + + T1 = get_step(src, turn(join_flags,135)) // 45 degrees before opposite + T2 = get_step(src, turn(join_flags,225)) // 45 degrees beyond opposite + T3 = get_step(src, turn(join_flags,180)) // Opposite from the diagonal + + if(isfloor(T1) && ((T1.type == T2.type) || (T1.type == T3.type))) + under = T1 + else if(isfloor(T2) && T2.type == T3.type) + under = T2 + else if(isfloor(T3) || istype(T3,/turf/space/transit)) + under = T3 + else + under = get_base_turf_by_area(src) + + if(istype(under,/turf/simulated/shuttle)) + interior_corner = 1 //Prevents us from 'landing on grass' and having interior corners update. + + var/mutable_appearance/under_ma + + if(ispath(under)) //It's just a mapper-specified path + under_ma = new() + under_ma.icon = initial(under.icon) + under_ma.icon_state = initial(under.icon_state) + under_ma.color = initial(under.color) + + else //It's a real turf + under_ma = new(under) + + if(under_ma) + if(ispath(under,/turf/space) || istype(under,/turf/space)) //Space gets weird treatment + under_ma.icon_state = "white" + under_ma.plane = SPACE_PLANE + us.underlays = list(under_ma) + + appearance = us + + spawn update_breaklights() //So that we update the breaklight overlays only after turfs are connected + + return under + +/turf/simulated/shuttle/floor + name = "floor" + icon = 'icons/turf/flooring/shuttle.dmi' + icon_state = "floor_blue" + +/turf/simulated/shuttle/floor/red + icon_state = "floor_red" + +/turf/simulated/shuttle/floor/yellow + icon_state = "floor_yellow" + +/turf/simulated/shuttle/floor/darkred + icon_state = "floor_dred" + +/turf/simulated/shuttle/floor/purple + icon_state = "floor_purple" + +/turf/simulated/shuttle/floor/white + icon_state = "floor_white" + +/turf/simulated/shuttle/floor/black + icon_state = "floor_black" + +/turf/simulated/shuttle/floor/glass + icon_state = "floor_glass" + takes_underlays = 1 + +/turf/simulated/shuttle/floor/alien + icon_state = "alienpod1" + light_range = 3 + light_power = 0.6 + light_color = "#66ffff" // Bright cyan. + light_on = TRUE + block_tele = TRUE + +/turf/simulated/shuttle/floor/alien/Initialize() + . = ..() + icon_state = "alienpod[rand(1, 9)]" + update_light() + +/turf/simulated/shuttle/floor/alienplating + icon_state = "alienplating" + block_tele = TRUE + +/turf/simulated/shuttle/floor/alienplating/external // For the outer rim of the UFO, to avoid active edges. +// The actual temperature adjustment is defined if the SC or other future map is compiled. + +/turf/simulated/shuttle/plating + name = "plating" + icon = 'icons/turf/floors.dmi' + icon_state = "plating" + +/turf/simulated/shuttle/plating/airless + oxygen = 0 + nitrogen = 0 + +//For 'carrying' otherwise empty turfs or stuff in space turfs with you or having holes in the floor or whatever. +/turf/simulated/shuttle/plating/carry + name = "carry turf" + icon = 'icons/turf/shuttle_parts.dmi' + icon_state = "carry" + takes_underlays = 1 + blocks_air = 1 //I'd make these unsimulated but it just fucks with so much stuff so many other places. + +/turf/simulated/shuttle/plating/carry/Initialize() + . = ..() + icon_state = "carry_ingame" + +/turf/simulated/shuttle/plating/airless/carry + name = "airless carry turf" + icon = 'icons/turf/shuttle_parts.dmi' + icon_state = "carry" + takes_underlays = 1 + blocks_air = 1 + +/turf/simulated/shuttle/plating/airless/carry/Initialize() + . = ..() + icon_state = "carry_ingame" + +/turf/simulated/shuttle/plating/skipjack //Skipjack plating + oxygen = 0 + nitrogen = MOLES_N2STANDARD + MOLES_O2STANDARD + +/turf/simulated/shuttle/floor/skipjack //Skipjack floors + name = "skipjack floor" + icon_state = "floor_dred" + oxygen = 0 + nitrogen = MOLES_N2STANDARD + MOLES_O2STANDARD + +/turf/simulated/shuttle/floor/voidcraft + name = "voidcraft tiles" + icon_state = "void" + +/turf/simulated/shuttle/floor/voidcraft/dark + name = "voidcraft tiles" + icon_state = "void_dark" + +/turf/simulated/shuttle/floor/voidcraft/light + name = "voidcraft tiles" + icon_state = "void_light" + +/turf/simulated/shuttle/floor/voidcraft/external // For avoiding active edges. +// The actual temperature adjustment is defined if the SC or other future map is compiled. + +/turf/simulated/shuttle/floor/voidcraft/external/dark + +/turf/simulated/shuttle/floor/voidcraft/external/light diff --git a/code/game/turfs/simulated/lava.dm b/code/game/turfs/simulated/lava.dm index ec5eeb18f44..0368e00e31c 100644 --- a/code/game/turfs/simulated/lava.dm +++ b/code/game/turfs/simulated/lava.dm @@ -1,102 +1,102 @@ -/turf/simulated/floor/lava - name = "lava" - desc = "A pool of molten rock." - description_info = "Molten rock is extremly dangerous, as it will cause massive harm to anything that touches it.
                    \ - A firesuit cannot fully protect from contact with molten rock." - gender = PLURAL // So it says "That's some lava." on examine. - icon = 'icons/turf/outdoors.dmi' - icon_state = "lava" - edge_blending_priority = -1 - light_range = 2 - light_power = 0.75 - light_color = LIGHT_COLOR_LAVA - light_on = TRUE - movement_cost = 2 - can_build_into_floor = TRUE - can_be_plated = FALSE - can_dirty = FALSE - initial_flooring = /decl/flooring/lava // Defining this in case someone DOES step on lava and survive. Somehow. - flags = TURF_ACID_IMMUNE - -/turf/simulated/floor/lava/outdoors - outdoors = OUTDOORS_YES - -// For maximum pedantry. -/turf/simulated/floor/lava/Initialize() - if(!is_outdoors()) - name = "magma" - update_icon() - update_light() - return ..() - -/turf/simulated/floor/lava/make_outdoors() - ..() - name = "lava" - -/turf/simulated/floor/lava/make_indoors() - ..() - name = "magma" - -/turf/simulated/floor/lava/make_plating(place_product, defer_icon_update) - return - -/turf/simulated/floor/lava/set_flooring(decl/flooring/newflooring, initializing) - if(newflooring?.type == initial_flooring) - return ..() - return - -/turf/simulated/floor/lava/ex_act(severity) - return - -/turf/simulated/floor/lava/Entered(atom/movable/AM) - if(burn_stuff(AM)) - START_PROCESSING(SSturfs, src) - -/turf/simulated/floor/lava/hitby(atom/movable/AM) - if(burn_stuff(AM)) - START_PROCESSING(SSturfs, src) - -/turf/simulated/floor/lava/process() - if(!burn_stuff()) - return PROCESS_KILL - -/turf/simulated/floor/lava/proc/is_safe() - //if anything matching this typecache is found in the lava, we don't burn things - var/static/list/lava_safeties_typecache = typecacheof(list(/obj/structure/catwalk)) - var/list/found_safeties = typecache_filter_list(contents, lava_safeties_typecache) - return LAZYLEN(found_safeties) - -/turf/simulated/floor/lava/proc/burn_stuff(atom/movable/AM) - . = FALSE - - if(is_safe()) - return FALSE - - var/thing_to_check = src - if(AM) - thing_to_check = list(AM) - - for(var/thing in thing_to_check) - if(isobj(thing)) - var/obj/O = thing - if(O.throwing || O.is_incorporeal()) - continue - . = TRUE - O.lava_act() - - else if(isliving(thing)) - var/mob/living/L = thing - if(L.hovering || L.throwing || L.is_incorporeal()) // Flying over the lava. We're just gonna pretend convection doesn't exist. - continue - . = TRUE - L.lava_act() - -// Lava that does nothing at all. -/turf/simulated/floor/lava/harmless/burn_stuff(atom/movable/AM) - return FALSE - -// Tells AI mobs to not suicide by pathing into lava if it would hurt them. -/turf/simulated/floor/lava/is_safe_to_enter(mob/living/L) - if(!is_safe() && !L.hovering) - return FALSE +/turf/simulated/floor/lava + name = "lava" + desc = "A pool of molten rock." + description_info = "Molten rock is extremly dangerous, as it will cause massive harm to anything that touches it.
                    \ + A firesuit cannot fully protect from contact with molten rock." + gender = PLURAL // So it says "That's some lava." on examine. + icon = 'icons/turf/outdoors.dmi' + icon_state = "lava" + edge_blending_priority = -1 + light_range = 2 + light_power = 0.75 + light_color = LIGHT_COLOR_LAVA + light_on = TRUE + movement_cost = 2 + can_build_into_floor = TRUE + can_be_plated = FALSE + can_dirty = FALSE + initial_flooring = /decl/flooring/lava // Defining this in case someone DOES step on lava and survive. Somehow. + flags = TURF_ACID_IMMUNE + +/turf/simulated/floor/lava/outdoors + outdoors = OUTDOORS_YES + +// For maximum pedantry. +/turf/simulated/floor/lava/Initialize() + if(!is_outdoors()) + name = "magma" + update_icon() + update_light() + return ..() + +/turf/simulated/floor/lava/make_outdoors() + ..() + name = "lava" + +/turf/simulated/floor/lava/make_indoors() + ..() + name = "magma" + +/turf/simulated/floor/lava/make_plating(place_product, defer_icon_update) + return + +/turf/simulated/floor/lava/set_flooring(decl/flooring/newflooring, initializing) + if(newflooring?.type == initial_flooring) + return ..() + return + +/turf/simulated/floor/lava/ex_act(severity) + return + +/turf/simulated/floor/lava/Entered(atom/movable/AM) + if(burn_stuff(AM)) + START_PROCESSING(SSturfs, src) + +/turf/simulated/floor/lava/hitby(atom/movable/AM) + if(burn_stuff(AM)) + START_PROCESSING(SSturfs, src) + +/turf/simulated/floor/lava/process() + if(!burn_stuff()) + return PROCESS_KILL + +/turf/simulated/floor/lava/proc/is_safe() + //if anything matching this typecache is found in the lava, we don't burn things + var/static/list/lava_safeties_typecache = typecacheof(list(/obj/structure/catwalk)) + var/list/found_safeties = typecache_filter_list(contents, lava_safeties_typecache) + return LAZYLEN(found_safeties) + +/turf/simulated/floor/lava/proc/burn_stuff(atom/movable/AM) + . = FALSE + + if(is_safe()) + return FALSE + + var/thing_to_check = src + if(AM) + thing_to_check = list(AM) + + for(var/thing in thing_to_check) + if(isobj(thing)) + var/obj/O = thing + if(O.throwing || O.is_incorporeal()) + continue + . = TRUE + O.lava_act() + + else if(isliving(thing)) + var/mob/living/L = thing + if(L.hovering || L.throwing || L.is_incorporeal()) // Flying over the lava. We're just gonna pretend convection doesn't exist. + continue + . = TRUE + L.lava_act() + +// Lava that does nothing at all. +/turf/simulated/floor/lava/harmless/burn_stuff(atom/movable/AM) + return FALSE + +// Tells AI mobs to not suicide by pathing into lava if it would hurt them. +/turf/simulated/floor/lava/is_safe_to_enter(mob/living/L) + if(!is_safe() && !L.hovering) + return FALSE return ..() \ No newline at end of file diff --git a/code/game/turfs/simulated/outdoors/outdoors.dm b/code/game/turfs/simulated/outdoors/outdoors.dm index 98d195001cd..c75eeaab64b 100644 --- a/code/game/turfs/simulated/outdoors/outdoors.dm +++ b/code/game/turfs/simulated/outdoors/outdoors.dm @@ -60,11 +60,12 @@ var/list/turf_edge_cache = list() return . = ..() - +/* VOREStation remove - handled by parent /turf/simulated/floor/Initialize(mapload) if(is_outdoors()) SSplanets.addTurf(src) . = ..() +*/ /turf/simulated/floor/Destroy() if(is_outdoors()) diff --git a/code/game/turfs/simulated/outdoors/outdoors_vr.dm b/code/game/turfs/simulated/outdoors/outdoors_vr.dm index b8227291651..39b35b2d928 100644 --- a/code/game/turfs/simulated/outdoors/outdoors_vr.dm +++ b/code/game/turfs/simulated/outdoors/outdoors_vr.dm @@ -1,168 +1,168 @@ -/turf/simulated/floor/tiled/asteroid_steel/outdoors - name = "weathered tiles" - desc = "Old tiles left out in the elements." - outdoors = OUTDOORS_YES - edge_blending_priority = 1 - -/turf/simulated/floor/outdoors/newdirt - name = "dirt" - desc = "Looks dirty." - icon = 'icons/turf/outdoors_vr.dmi' - icon_state = "dirt0" - edge_blending_priority = 2 - initial_flooring = /decl/flooring/outdoors/newdirt - -/decl/flooring/outdoors/newdirt - name = "dirt" - desc = "Looks dirty." - icon = 'icons/turf/outdoors_vr.dmi' - icon_base = "dirt0" - footstep_sounds = list("human" = list( - 'sound/effects/footstep/asteroid1.ogg', - 'sound/effects/footstep/asteroid2.ogg', - 'sound/effects/footstep/asteroid3.ogg', - 'sound/effects/footstep/asteroid4.ogg', - 'sound/effects/footstep/asteroid5.ogg', - 'sound/effects/footstep/MedDirt1.ogg', - 'sound/effects/footstep/MedDirt2.ogg', - 'sound/effects/footstep/MedDirt3.ogg', - 'sound/effects/footstep/MedDirt4.ogg')) - -/turf/simulated/floor/outdoors/newdirt/Initialize(mapload) - var/possibledirts = list( - "dirt0" = 150, - "dirt1" = 25, - "dirt2" = 25, - "dirt3" = 25, - "dirt4" = 25, - "dirt5" = 10, - "dirt6" = 10, - "dirt7" = 3, - "dirt8" = 3, - "dirt9" = 1 - ) - flooring_override = pickweight(possibledirts) - return ..() - - -/turf/simulated/floor/outdoors/newdirt_nograss - name = "dirt" - desc = "Looks dirty." - icon = 'icons/turf/outdoors_vr.dmi' - icon_state = "dirt0" - edge_blending_priority = 2 - initial_flooring = /decl/flooring/outdoors/newdirt - -/turf/simulated/floor/outdoors/newdirt_nograss/Initialize(mapload) - var/possibledirts = list( - "dirt0" = 200, - "dirt6" = 20, - "dirt7" = 3, - "dirt8" = 3, - "dirt9" = 1 - ) - flooring_override = pickweight(possibledirts) - return ..() - -/turf/simulated/floor/outdoors/sidewalk - name = "sidewalk" - desc = "Concrete shaped into a path!" - icon = 'icons/turf/outdoors_vr.dmi' - icon_state = "sidewalk" - edge_blending_priority = -1 - movement_cost = -0.5 - initial_flooring = /decl/flooring/outdoors/sidewalk - can_dirty = TRUE - -/decl/flooring/outdoors/sidewalk - name = "sidewalk" - desc = "Concrete shaped into a path!" - icon = 'icons/turf/outdoors_vr.dmi' - icon_base = "sidewalk" - has_damage_range = 2 - damage_temperature = T0C+1400 - flags = TURF_REMOVE_CROWBAR | TURF_CAN_BREAK | TURF_CAN_BURN - build_type = /obj/item/stack/tile/floor/sidewalk - can_paint = 1 - can_engrave = FALSE - - footstep_sounds = list("human" = list( - 'sound/effects/footstep/LightStone1.ogg', - 'sound/effects/footstep/LightStone2.ogg', - 'sound/effects/footstep/LightStone3.ogg', - 'sound/effects/footstep/LightStone4.ogg',)) - -/obj/item/stack/tile/floor/sidewalk - name = "sidewalk tile" - singular_name = "floor tile" - desc = "A stone tile fit for covering a section of floor." - icon_state = "tile" - force = 6.0 - matter = list(DEFAULT_WALL_MATERIAL = SHEET_MATERIAL_AMOUNT / 4) - throwforce = 15.0 - throw_speed = 5 - throw_range = 20 - no_variants = FALSE - -/turf/simulated/floor/outdoors/sidewalk/Initialize(mapload) - var/possibledirts = list( - "[initial(icon_state)]" = 150, - "[initial(icon_state)]1" = 3, - "[initial(icon_state)]2" = 3, - "[initial(icon_state)]3" = 3, - "[initial(icon_state)]4" = 3, - "[initial(icon_state)]5" = 3, - "[initial(icon_state)]6" = 2, - "[initial(icon_state)]7" = 2, - "[initial(icon_state)]8" = 2, - "[initial(icon_state)]9" = 2, - "[initial(icon_state)]10" = 2 - ) - flooring_override = pickweight(possibledirts) - return ..() - -/turf/simulated/floor/outdoors/sidewalk/side - icon_state = "side-walk" - initial_flooring = /decl/flooring/outdoors/sidewalk/side - - -/decl/flooring/outdoors/sidewalk/side - icon_base = "sidewalk" - build_type = /obj/item/stack/tile/floor/sidewalk/side - -/obj/item/stack/tile/floor/sidewalk/side - -/turf/simulated/floor/outdoors/sidewalk/slab - icon_state = "slab" - initial_flooring = /decl/flooring/outdoors/sidewalk/slab - -/decl/flooring/outdoors/sidewalk/slab - icon_base = "slab" - build_type = /obj/item/stack/tile/floor/sidewalk/slab - -/obj/item/stack/tile/floor/sidewalk/slab/ - -/turf/simulated/floor/outdoors/sidewalk/slab/city - icon_state = "cityslab" - initial_flooring = /decl/flooring/outdoors/sidewalk/slab/city - -/decl/flooring/outdoors/sidewalk/slab/city - icon_base = "cityslab" - build_type = /obj/item/stack/tile/floor/sidewalk/slab/city - -/obj/item/stack/tile/floor/sidewalk/slab/city - -/obj/item/stack/tile/floor/concrete //Proper concrete tile. - name = "concrete tile" - singular_name = "floor tile" - desc = "A concrete tile fit for covering a section of floor." - icon_state = "tile" - force = 6.0 - matter = list(DEFAULT_WALL_MATERIAL = SHEET_MATERIAL_AMOUNT / 4) - throwforce = 15.0 - throw_speed = 5 - throw_range = 20 - no_variants = TRUE - -/decl/flooring/concrete - build_type = /obj/item/stack/tile/floor/concrete +/turf/simulated/floor/tiled/asteroid_steel/outdoors + name = "weathered tiles" + desc = "Old tiles left out in the elements." + outdoors = OUTDOORS_YES + edge_blending_priority = 1 + +/turf/simulated/floor/outdoors/newdirt + name = "dirt" + desc = "Looks dirty." + icon = 'icons/turf/outdoors_vr.dmi' + icon_state = "dirt0" + edge_blending_priority = 2 + initial_flooring = /decl/flooring/outdoors/newdirt + +/decl/flooring/outdoors/newdirt + name = "dirt" + desc = "Looks dirty." + icon = 'icons/turf/outdoors_vr.dmi' + icon_base = "dirt0" + footstep_sounds = list("human" = list( + 'sound/effects/footstep/asteroid1.ogg', + 'sound/effects/footstep/asteroid2.ogg', + 'sound/effects/footstep/asteroid3.ogg', + 'sound/effects/footstep/asteroid4.ogg', + 'sound/effects/footstep/asteroid5.ogg', + 'sound/effects/footstep/MedDirt1.ogg', + 'sound/effects/footstep/MedDirt2.ogg', + 'sound/effects/footstep/MedDirt3.ogg', + 'sound/effects/footstep/MedDirt4.ogg')) + +/turf/simulated/floor/outdoors/newdirt/Initialize(mapload) + var/possibledirts = list( + "dirt0" = 150, + "dirt1" = 25, + "dirt2" = 25, + "dirt3" = 25, + "dirt4" = 25, + "dirt5" = 10, + "dirt6" = 10, + "dirt7" = 3, + "dirt8" = 3, + "dirt9" = 1 + ) + flooring_override = pickweight(possibledirts) + return ..() + + +/turf/simulated/floor/outdoors/newdirt_nograss + name = "dirt" + desc = "Looks dirty." + icon = 'icons/turf/outdoors_vr.dmi' + icon_state = "dirt0" + edge_blending_priority = 2 + initial_flooring = /decl/flooring/outdoors/newdirt + +/turf/simulated/floor/outdoors/newdirt_nograss/Initialize(mapload) + var/possibledirts = list( + "dirt0" = 200, + "dirt6" = 20, + "dirt7" = 3, + "dirt8" = 3, + "dirt9" = 1 + ) + flooring_override = pickweight(possibledirts) + return ..() + +/turf/simulated/floor/outdoors/sidewalk + name = "sidewalk" + desc = "Concrete shaped into a path!" + icon = 'icons/turf/outdoors_vr.dmi' + icon_state = "sidewalk" + edge_blending_priority = -1 + movement_cost = -0.5 + initial_flooring = /decl/flooring/outdoors/sidewalk + can_dirty = TRUE + +/decl/flooring/outdoors/sidewalk + name = "sidewalk" + desc = "Concrete shaped into a path!" + icon = 'icons/turf/outdoors_vr.dmi' + icon_base = "sidewalk" + has_damage_range = 2 + damage_temperature = T0C+1400 + flags = TURF_REMOVE_CROWBAR | TURF_CAN_BREAK | TURF_CAN_BURN + build_type = /obj/item/stack/tile/floor/sidewalk + can_paint = 1 + can_engrave = FALSE + + footstep_sounds = list("human" = list( + 'sound/effects/footstep/LightStone1.ogg', + 'sound/effects/footstep/LightStone2.ogg', + 'sound/effects/footstep/LightStone3.ogg', + 'sound/effects/footstep/LightStone4.ogg',)) + +/obj/item/stack/tile/floor/sidewalk + name = "sidewalk tile" + singular_name = "floor tile" + desc = "A stone tile fit for covering a section of floor." + icon_state = "tile" + force = 6.0 + matter = list(DEFAULT_WALL_MATERIAL = SHEET_MATERIAL_AMOUNT / 4) + throwforce = 15.0 + throw_speed = 5 + throw_range = 20 + no_variants = FALSE + +/turf/simulated/floor/outdoors/sidewalk/Initialize(mapload) + var/possibledirts = list( + "[initial(icon_state)]" = 150, + "[initial(icon_state)]1" = 3, + "[initial(icon_state)]2" = 3, + "[initial(icon_state)]3" = 3, + "[initial(icon_state)]4" = 3, + "[initial(icon_state)]5" = 3, + "[initial(icon_state)]6" = 2, + "[initial(icon_state)]7" = 2, + "[initial(icon_state)]8" = 2, + "[initial(icon_state)]9" = 2, + "[initial(icon_state)]10" = 2 + ) + flooring_override = pickweight(possibledirts) + return ..() + +/turf/simulated/floor/outdoors/sidewalk/side + icon_state = "side-walk" + initial_flooring = /decl/flooring/outdoors/sidewalk/side + + +/decl/flooring/outdoors/sidewalk/side + icon_base = "sidewalk" + build_type = /obj/item/stack/tile/floor/sidewalk/side + +/obj/item/stack/tile/floor/sidewalk/side + +/turf/simulated/floor/outdoors/sidewalk/slab + icon_state = "slab" + initial_flooring = /decl/flooring/outdoors/sidewalk/slab + +/decl/flooring/outdoors/sidewalk/slab + icon_base = "slab" + build_type = /obj/item/stack/tile/floor/sidewalk/slab + +/obj/item/stack/tile/floor/sidewalk/slab/ + +/turf/simulated/floor/outdoors/sidewalk/slab/city + icon_state = "cityslab" + initial_flooring = /decl/flooring/outdoors/sidewalk/slab/city + +/decl/flooring/outdoors/sidewalk/slab/city + icon_base = "cityslab" + build_type = /obj/item/stack/tile/floor/sidewalk/slab/city + +/obj/item/stack/tile/floor/sidewalk/slab/city + +/obj/item/stack/tile/floor/concrete //Proper concrete tile. + name = "concrete tile" + singular_name = "floor tile" + desc = "A concrete tile fit for covering a section of floor." + icon_state = "tile" + force = 6.0 + matter = list(DEFAULT_WALL_MATERIAL = SHEET_MATERIAL_AMOUNT / 4) + throwforce = 15.0 + throw_speed = 5 + throw_range = 20 + no_variants = TRUE + +/decl/flooring/concrete + build_type = /obj/item/stack/tile/floor/concrete diff --git a/code/game/turfs/simulated/outdoors/sky.dm b/code/game/turfs/simulated/outdoors/sky.dm index 2f9f8106bb9..6b94f245c14 100644 --- a/code/game/turfs/simulated/outdoors/sky.dm +++ b/code/game/turfs/simulated/outdoors/sky.dm @@ -14,7 +14,7 @@ /turf/simulated/sky/Initialize() . = ..() - SSplanets.addTurf(src) + //SSplanets.addTurf(src) VOREStation edit - Handled by parent set_light(2, 2, "#FFFFFF") /turf/simulated/sky/north diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm index e6a810e9a62..3118aa5e03e 100644 --- a/code/game/turfs/simulated/wall_attacks.dm +++ b/code/game/turfs/simulated/wall_attacks.dm @@ -1,415 +1,415 @@ -//Interactions -/turf/simulated/wall/proc/toggle_open(var/mob/user) - - if(can_open == WALL_OPENING) - return - - SSradiation.resistance_cache.Remove(src) - - if(density) - can_open = WALL_OPENING - //flick("[material.icon_base]fwall_opening", src) - density = FALSE - blocks_air = ZONE_BLOCKED - update_icon() - update_air() - set_light(0) - src.blocks_air = 0 - set_opacity(0) - for(var/turf/simulated/turf in loc) - air_master.mark_for_update(turf) - else - can_open = WALL_OPENING - //flick("[material.icon_base]fwall_closing", src) - density = TRUE - blocks_air = AIR_BLOCKED - update_icon() - update_air() - set_light(1) - src.blocks_air = 1 - set_opacity(1) - for(var/turf/simulated/turf in loc) - air_master.mark_for_update(turf) - - can_open = WALL_CAN_OPEN - update_icon() - -/turf/simulated/wall/proc/update_air() - if(!air_master) - return - - for(var/turf/simulated/turf in loc) - update_thermal(turf) - air_master.mark_for_update(turf) - - -/turf/simulated/wall/proc/update_thermal(var/turf/simulated/source) - if(istype(source)) - if(density && opacity) - source.thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT - else - source.thermal_conductivity = initial(source.thermal_conductivity) - -/turf/simulated/wall/proc/fail_smash(var/mob/user) - var/damage_lower = 25 - var/damage_upper = 75 - if(isanimal(user)) - var/mob/living/simple_mob/S = user - playsound(src, S.attack_sound, 75, 1) - if(!(S.melee_damage_upper >= STRUCTURE_MIN_DAMAGE_THRESHOLD * 2)) - to_chat(user, "You bounce against the wall.") - return FALSE - damage_lower = S.melee_damage_lower - damage_upper = S.melee_damage_upper - to_chat(user, "You smash against the wall!") - user.do_attack_animation(src) - take_damage(rand(damage_lower,damage_upper)) - -/turf/simulated/wall/proc/success_smash(var/mob/user) - to_chat(user, "You smash through the wall!") - user.do_attack_animation(src) - if(isanimal(user)) - var/mob/living/simple_mob/S = user - playsound(src, S.attack_sound, 75, 1) - spawn(1) - dismantle_wall(1) - -/turf/simulated/wall/proc/try_touch(var/mob/user, var/rotting) - - if(rotting) - if(reinf_material) - to_chat(user, "\The [reinf_material.display_name] feels porous and crumbly.") - else - to_chat(user, "\The [material.display_name] crumbles under your touch!") - dismantle_wall() - return 1 - - if(!can_open) - if(!material.wall_touch_special(src, user)) - to_chat(user, "You push the wall, but nothing happens.") - playsound(src, 'sound/weapons/Genhit.ogg', 25, 1) - else - toggle_open(user) - return 0 - - -/turf/simulated/wall/attack_hand(var/mob/user) - - radiate() - add_fingerprint(user) - user.setClickCooldown(user.get_attack_speed()) - var/rotting = (locate(/obj/effect/overlay/wallrot) in src) - if (HULK in user.mutations) - if (rotting || !prob(material.hardness)) - success_smash(user) - else - fail_smash(user) - return 1 - - try_touch(user, rotting) - -/turf/simulated/wall/attack_generic(var/mob/user, var/damage, var/attack_message) - - radiate() - user.setClickCooldown(user.get_attack_speed()) - var/rotting = (locate(/obj/effect/overlay/wallrot) in src) - if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD * 2) - try_touch(user, rotting) - return - - if(rotting) - return success_smash(user) - - if(reinf_material) - if(damage >= max(material.hardness, reinf_material.hardness) ) - return success_smash(user) - else if(damage >= material.hardness) - return success_smash(user) - return fail_smash(user) - -/turf/simulated/wall/attackby(var/obj/item/weapon/W, var/mob/user) - - user.setClickCooldown(user.get_attack_speed(W)) - -/* -//As with the floors, only this time it works AND tries pushing the wall after it's done. - if(!construction_stage && user.a_intent == I_HELP) - if(try_graffiti(user,W)) - return -*/ - - if (!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do this!") - return - - //get the user's location - if(!istype(user.loc, /turf)) - return //can't do this stuff whilst inside objects and such - - if(W) - radiate() - if(is_hot(W)) - burn(is_hot(W)) - - if(istype(W, /obj/item/device/electronic_assembly/wallmount)) - var/obj/item/device/electronic_assembly/wallmount/IC = W - IC.mount_assembly(src, user) - return - - if(istype(W, /obj/item/stack/tile/roofing)) - var/expended_tile = FALSE // To track the case. If a ceiling is built in a multiz zlevel, it also necessarily roofs it against weather - var/turf/T = GetAbove(src) - var/obj/item/stack/tile/roofing/R = W - - // Place plating over a wall - if(T) - if(istype(T, /turf/simulated/open) || istype(T, /turf/space)) - if(R.use(1)) // Cost of roofing tiles is 1:1 with cost to place lattice and plating - T.ReplaceWithLattice() - T.ChangeTurf(/turf/simulated/floor, preserve_outdoors = TRUE) - playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) - user.visible_message("[user] patches a hole in the ceiling.", "You patch a hole in the ceiling.") - expended_tile = TRUE - else - to_chat(user, "There aren't any holes in the ceiling to patch here.") - return - - // Create a ceiling to shield from the weather - if(is_outdoors()) - if(expended_tile || R.use(1)) // Don't need to check adjacent turfs for a wall, we're building on one - make_indoors() - if(!expended_tile) // Would've already played a sound - playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) - user.visible_message("[user] roofs \the [src], shielding it from the elements.", "You roof \the [src] tile, shielding it from the elements.") - return - - - if(locate(/obj/effect/overlay/wallrot) in src) - if(W.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/WT = W.get_welder() - if( WT.remove_fuel(0,user) ) - to_chat(user, "You burn away the fungi with \the [WT].") - playsound(src, WT.usesound, 10, 1) - for(var/obj/effect/overlay/wallrot/WR in src) - qdel(WR) - return - else if(!is_sharp(W) && W.force >= 10 || W.force >= 20) - to_chat(user, "\The [src] crumbles away under the force of your [W.name].") - src.dismantle_wall(1) - return - - //THERMITE related stuff. Calls src.thermitemelt() which handles melting simulated walls and the relevant effects - if(thermite) - if(W.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/WT = W.get_welder() - if( WT.remove_fuel(0,user) ) - thermitemelt(user) - return - - else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) - thermitemelt(user) - return - - else if( istype(W, /obj/item/weapon/melee/energy/blade) ) - var/obj/item/weapon/melee/energy/blade/EB = W - - EB.spark_system.start() - to_chat(user, "You slash \the [src] with \the [EB]; the thermite ignites!") - playsound(src, "sparks", 50, 1) - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - - thermitemelt(user) - return - - var/turf/T = user.loc //get user's location for delay checks - - if(damage && W.has_tool_quality(TOOL_WELDER)) - - var/obj/item/weapon/weldingtool/WT = W.get_welder() - - if(!WT.isOn()) - return - - if(WT.remove_fuel(0,user)) - to_chat(user, "You start repairing the damage to [src].") - playsound(src, WT.usesound, 100, 1) - if(do_after(user, max(5, damage / 5) * WT.toolspeed) && WT && WT.isOn()) - to_chat(user, "You finish repairing the damage to [src].") - take_damage(-damage) - else - to_chat(user, "You need more welding fuel to complete this task.") - return - user.update_examine_panel(src) - return - - // Basic dismantling. - //var/dismantle_toolspeed = 0 - if(isnull(construction_stage) || !reinf_material) - - var/cut_delay = 60 - material.cut_delay - var/dismantle_verb - var/dismantle_sound - - if(W.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/WT = W.get_welder() - if(!WT.isOn()) - return - if(!WT.remove_fuel(0,user)) - to_chat(user, "You need more welding fuel to complete this task.") - return - dismantle_verb = "cutting" - dismantle_sound = W.usesound - // cut_delay *= 0.7 // Tools themselves now can shorten the time it takes. - else if(istype(W,/obj/item/weapon/melee/energy/blade)) - dismantle_sound = "sparks" - dismantle_verb = "slicing" - //dismantle_toolspeed = 1 - cut_delay *= 0.5 - else if(istype(W,/obj/item/weapon/pickaxe)) - var/obj/item/weapon/pickaxe/P = W - dismantle_verb = P.drill_verb - dismantle_sound = P.drill_sound - cut_delay -= P.digspeed - - if(dismantle_verb) - - to_chat(user, "You begin [dismantle_verb] through the outer plating.") - if(dismantle_sound) - playsound(src, dismantle_sound, 100, 1) - - if(cut_delay < 0) - cut_delay = 0 - - if(!do_after(user,cut_delay * W.toolspeed)) - return - - to_chat(user, "You remove the outer plating.") - dismantle_wall() - user.visible_message("The wall was torn open by [user]!") - return - - //Reinforced dismantling. - else - switch(construction_stage) - if(6) - if (W.has_tool_quality(TOOL_WIRECUTTER)) - playsound(src, W.usesound, 100, 1) - construction_stage = 5 - user.update_examine_panel(src) - to_chat(user, "You cut through the outer grille.") - update_icon() - return - if(5) - if (W.has_tool_quality(TOOL_SCREWDRIVER)) - to_chat(user, "You begin removing the support lines.") - playsound(src, W.usesound, 100, 1) - if(!do_after(user,40 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 5) - return - construction_stage = 4 - user.update_examine_panel(src) - update_icon() - to_chat(user, "You unscrew the support lines.") - return - else if (W.has_tool_quality(TOOL_WIRECUTTER)) - construction_stage = 6 - user.update_examine_panel(src) - to_chat(user, "You mend the outer grille.") - playsound(src, W.usesound, 100, 1) - update_icon() - return - if(4) - var/cut_cover - if(W.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/WT = W.get_welder() - if(!WT.isOn()) - return - if(WT.remove_fuel(0,user)) - cut_cover=1 - else - to_chat(user, "You need more welding fuel to complete this task.") - return - else if (istype(W, /obj/item/weapon/pickaxe/plasmacutter)) - cut_cover = 1 - if(cut_cover) - to_chat(user, "You begin slicing through the metal cover.") - playsound(src, W.usesound, 100, 1) - if(!do_after(user, 60 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 4) - return - construction_stage = 3 - user.update_examine_panel(src) - update_icon() - to_chat(user, "You press firmly on the cover, dislodging it.") - return - else if (W.has_tool_quality(TOOL_SCREWDRIVER)) - to_chat(user, "You begin screwing down the support lines.") - playsound(src, W.usesound, 100, 1) - if(!do_after(user,40 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 4) - return - construction_stage = 5 - user.update_examine_panel(src) - update_icon() - to_chat(user, "You screw down the support lines.") - return - if(3) - if (W.has_tool_quality(TOOL_CROWBAR)) - to_chat(user, "You struggle to pry off the cover.") - playsound(src, W.usesound, 100, 1) - if(!do_after(user,100 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 3) - return - construction_stage = 2 - user.update_examine_panel(src) - update_icon() - to_chat(user, "You pry off the cover.") - return - if(2) - if (W.has_tool_quality(TOOL_WRENCH)) - to_chat(user, "You start loosening the anchoring bolts which secure the support rods to their frame.") - playsound(src, W.usesound, 100, 1) - if(!do_after(user,40 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 2) - return - construction_stage = 1 - user.update_examine_panel(src) - update_icon() - to_chat(user, "You remove the bolts anchoring the support rods.") - return - if(1) - var/cut_cover - if(W.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/WT = W.get_welder() - if( WT.remove_fuel(0,user) ) - cut_cover=1 - else - to_chat(user, "You need more welding fuel to complete this task.") - return - else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) - cut_cover = 1 - if(cut_cover) - to_chat(user, "You begin slicing through the support rods.") - playsound(src, W.usesound, 100, 1) - if(!do_after(user,70 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 1) - return - construction_stage = 0 - user.update_examine_panel(src) - update_icon() - to_chat(user, "You slice through the support rods.") - return - if(0) - if(W.has_tool_quality(TOOL_CROWBAR)) - to_chat(user, "You struggle to pry off the outer sheath.") - playsound(src, W.usesound, 100, 1) - if(!do_after(user,100 * W.toolspeed) || !istype(src, /turf/simulated/wall) || !user || !W || !T ) - return - if(user.loc == T && user.get_active_hand() == W ) - to_chat(user, "You pry off the outer sheath.") - dismantle_wall() - return - - if(istype(W,/obj/item/frame)) - var/obj/item/frame/F = W - F.try_build(src, user) - return - - else if(!istype(W,/obj/item/weapon/rcd) && !istype(W, /obj/item/weapon/reagent_containers)) - return attack_hand(user) - - +//Interactions +/turf/simulated/wall/proc/toggle_open(var/mob/user) + + if(can_open == WALL_OPENING) + return + + SSradiation.resistance_cache.Remove(src) + + if(density) + can_open = WALL_OPENING + //flick("[material.icon_base]fwall_opening", src) + density = FALSE + blocks_air = ZONE_BLOCKED + update_icon() + update_air() + set_light(0) + src.blocks_air = 0 + set_opacity(0) + for(var/turf/simulated/turf in loc) + air_master.mark_for_update(turf) + else + can_open = WALL_OPENING + //flick("[material.icon_base]fwall_closing", src) + density = TRUE + blocks_air = AIR_BLOCKED + update_icon() + update_air() + set_light(1) + src.blocks_air = 1 + set_opacity(1) + for(var/turf/simulated/turf in loc) + air_master.mark_for_update(turf) + + can_open = WALL_CAN_OPEN + update_icon() + +/turf/simulated/wall/proc/update_air() + if(!air_master) + return + + for(var/turf/simulated/turf in loc) + update_thermal(turf) + air_master.mark_for_update(turf) + + +/turf/simulated/wall/proc/update_thermal(var/turf/simulated/source) + if(istype(source)) + if(density && opacity) + source.thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT + else + source.thermal_conductivity = initial(source.thermal_conductivity) + +/turf/simulated/wall/proc/fail_smash(var/mob/user) + var/damage_lower = 25 + var/damage_upper = 75 + if(isanimal(user)) + var/mob/living/simple_mob/S = user + playsound(src, S.attack_sound, 75, 1) + if(!(S.melee_damage_upper >= STRUCTURE_MIN_DAMAGE_THRESHOLD * 2)) + to_chat(user, "You bounce against the wall.") + return FALSE + damage_lower = S.melee_damage_lower + damage_upper = S.melee_damage_upper + to_chat(user, "You smash against the wall!") + user.do_attack_animation(src) + take_damage(rand(damage_lower,damage_upper)) + +/turf/simulated/wall/proc/success_smash(var/mob/user) + to_chat(user, "You smash through the wall!") + user.do_attack_animation(src) + if(isanimal(user)) + var/mob/living/simple_mob/S = user + playsound(src, S.attack_sound, 75, 1) + spawn(1) + dismantle_wall(1) + +/turf/simulated/wall/proc/try_touch(var/mob/user, var/rotting) + + if(rotting) + if(reinf_material) + to_chat(user, "\The [reinf_material.display_name] feels porous and crumbly.") + else + to_chat(user, "\The [material.display_name] crumbles under your touch!") + dismantle_wall() + return 1 + + if(!can_open) + if(!material.wall_touch_special(src, user)) + to_chat(user, "You push the wall, but nothing happens.") + playsound(src, 'sound/weapons/Genhit.ogg', 25, 1) + else + toggle_open(user) + return 0 + + +/turf/simulated/wall/attack_hand(var/mob/user) + + radiate() + add_fingerprint(user) + user.setClickCooldown(user.get_attack_speed()) + var/rotting = (locate(/obj/effect/overlay/wallrot) in src) + if (HULK in user.mutations) + if (rotting || !prob(material.hardness)) + success_smash(user) + else + fail_smash(user) + return 1 + + try_touch(user, rotting) + +/turf/simulated/wall/attack_generic(var/mob/user, var/damage, var/attack_message) + + radiate() + user.setClickCooldown(user.get_attack_speed()) + var/rotting = (locate(/obj/effect/overlay/wallrot) in src) + if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD * 2) + try_touch(user, rotting) + return + + if(rotting) + return success_smash(user) + + if(reinf_material) + if(damage >= max(material.hardness, reinf_material.hardness) ) + return success_smash(user) + else if(damage >= material.hardness) + return success_smash(user) + return fail_smash(user) + +/turf/simulated/wall/attackby(var/obj/item/weapon/W, var/mob/user) + + user.setClickCooldown(user.get_attack_speed(W)) + +/* +//As with the floors, only this time it works AND tries pushing the wall after it's done. + if(!construction_stage && user.a_intent == I_HELP) + if(try_graffiti(user,W)) + return +*/ + + if (!user.IsAdvancedToolUser()) + to_chat(user, "You don't have the dexterity to do this!") + return + + //get the user's location + if(!istype(user.loc, /turf)) + return //can't do this stuff whilst inside objects and such + + if(W) + radiate() + if(is_hot(W)) + burn(is_hot(W)) + + if(istype(W, /obj/item/device/electronic_assembly/wallmount)) + var/obj/item/device/electronic_assembly/wallmount/IC = W + IC.mount_assembly(src, user) + return + + if(istype(W, /obj/item/stack/tile/roofing)) + var/expended_tile = FALSE // To track the case. If a ceiling is built in a multiz zlevel, it also necessarily roofs it against weather + var/turf/T = GetAbove(src) + var/obj/item/stack/tile/roofing/R = W + + // Place plating over a wall + if(T) + if(istype(T, /turf/simulated/open) || istype(T, /turf/space)) + if(R.use(1)) // Cost of roofing tiles is 1:1 with cost to place lattice and plating + T.ReplaceWithLattice() + T.ChangeTurf(/turf/simulated/floor, preserve_outdoors = TRUE) + playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) + user.visible_message("[user] patches a hole in the ceiling.", "You patch a hole in the ceiling.") + expended_tile = TRUE + else + to_chat(user, "There aren't any holes in the ceiling to patch here.") + return + + // Create a ceiling to shield from the weather + if(is_outdoors()) + if(expended_tile || R.use(1)) // Don't need to check adjacent turfs for a wall, we're building on one + make_indoors() + if(!expended_tile) // Would've already played a sound + playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) + user.visible_message("[user] roofs \the [src], shielding it from the elements.", "You roof \the [src] tile, shielding it from the elements.") + return + + + if(locate(/obj/effect/overlay/wallrot) in src) + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if( WT.remove_fuel(0,user) ) + to_chat(user, "You burn away the fungi with \the [WT].") + playsound(src, WT.usesound, 10, 1) + for(var/obj/effect/overlay/wallrot/WR in src) + qdel(WR) + return + else if(!is_sharp(W) && W.force >= 10 || W.force >= 20) + to_chat(user, "\The [src] crumbles away under the force of your [W.name].") + src.dismantle_wall(1) + return + + //THERMITE related stuff. Calls src.thermitemelt() which handles melting simulated walls and the relevant effects + if(thermite) + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if( WT.remove_fuel(0,user) ) + thermitemelt(user) + return + + else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) + thermitemelt(user) + return + + else if( istype(W, /obj/item/weapon/melee/energy/blade) ) + var/obj/item/weapon/melee/energy/blade/EB = W + + EB.spark_system.start() + to_chat(user, "You slash \the [src] with \the [EB]; the thermite ignites!") + playsound(src, "sparks", 50, 1) + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + + thermitemelt(user) + return + + var/turf/T = user.loc //get user's location for delay checks + + if(damage && W.has_tool_quality(TOOL_WELDER)) + + var/obj/item/weapon/weldingtool/WT = W.get_welder() + + if(!WT.isOn()) + return + + if(WT.remove_fuel(0,user)) + to_chat(user, "You start repairing the damage to [src].") + playsound(src, WT.usesound, 100, 1) + if(do_after(user, max(5, damage / 5) * WT.toolspeed) && WT && WT.isOn()) + to_chat(user, "You finish repairing the damage to [src].") + take_damage(-damage) + else + to_chat(user, "You need more welding fuel to complete this task.") + return + user.update_examine_panel(src) + return + + // Basic dismantling. + //var/dismantle_toolspeed = 0 + if(isnull(construction_stage) || !reinf_material) + + var/cut_delay = 60 - material.cut_delay + var/dismantle_verb + var/dismantle_sound + + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(!WT.isOn()) + return + if(!WT.remove_fuel(0,user)) + to_chat(user, "You need more welding fuel to complete this task.") + return + dismantle_verb = "cutting" + dismantle_sound = W.usesound + // cut_delay *= 0.7 // Tools themselves now can shorten the time it takes. + else if(istype(W,/obj/item/weapon/melee/energy/blade)) + dismantle_sound = "sparks" + dismantle_verb = "slicing" + //dismantle_toolspeed = 1 + cut_delay *= 0.5 + else if(istype(W,/obj/item/weapon/pickaxe)) + var/obj/item/weapon/pickaxe/P = W + dismantle_verb = P.drill_verb + dismantle_sound = P.drill_sound + cut_delay -= P.digspeed + + if(dismantle_verb) + + to_chat(user, "You begin [dismantle_verb] through the outer plating.") + if(dismantle_sound) + playsound(src, dismantle_sound, 100, 1) + + if(cut_delay < 0) + cut_delay = 0 + + if(!do_after(user,cut_delay * W.toolspeed)) + return + + to_chat(user, "You remove the outer plating.") + dismantle_wall() + user.visible_message("The wall was torn open by [user]!") + return + + //Reinforced dismantling. + else + switch(construction_stage) + if(6) + if (W.has_tool_quality(TOOL_WIRECUTTER)) + playsound(src, W.usesound, 100, 1) + construction_stage = 5 + user.update_examine_panel(src) + to_chat(user, "You cut through the outer grille.") + update_icon() + return + if(5) + if (W.has_tool_quality(TOOL_SCREWDRIVER)) + to_chat(user, "You begin removing the support lines.") + playsound(src, W.usesound, 100, 1) + if(!do_after(user,40 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 5) + return + construction_stage = 4 + user.update_examine_panel(src) + update_icon() + to_chat(user, "You unscrew the support lines.") + return + else if (W.has_tool_quality(TOOL_WIRECUTTER)) + construction_stage = 6 + user.update_examine_panel(src) + to_chat(user, "You mend the outer grille.") + playsound(src, W.usesound, 100, 1) + update_icon() + return + if(4) + var/cut_cover + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(!WT.isOn()) + return + if(WT.remove_fuel(0,user)) + cut_cover=1 + else + to_chat(user, "You need more welding fuel to complete this task.") + return + else if (istype(W, /obj/item/weapon/pickaxe/plasmacutter)) + cut_cover = 1 + if(cut_cover) + to_chat(user, "You begin slicing through the metal cover.") + playsound(src, W.usesound, 100, 1) + if(!do_after(user, 60 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 4) + return + construction_stage = 3 + user.update_examine_panel(src) + update_icon() + to_chat(user, "You press firmly on the cover, dislodging it.") + return + else if (W.has_tool_quality(TOOL_SCREWDRIVER)) + to_chat(user, "You begin screwing down the support lines.") + playsound(src, W.usesound, 100, 1) + if(!do_after(user,40 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 4) + return + construction_stage = 5 + user.update_examine_panel(src) + update_icon() + to_chat(user, "You screw down the support lines.") + return + if(3) + if (W.has_tool_quality(TOOL_CROWBAR)) + to_chat(user, "You struggle to pry off the cover.") + playsound(src, W.usesound, 100, 1) + if(!do_after(user,100 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 3) + return + construction_stage = 2 + user.update_examine_panel(src) + update_icon() + to_chat(user, "You pry off the cover.") + return + if(2) + if (W.has_tool_quality(TOOL_WRENCH)) + to_chat(user, "You start loosening the anchoring bolts which secure the support rods to their frame.") + playsound(src, W.usesound, 100, 1) + if(!do_after(user,40 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 2) + return + construction_stage = 1 + user.update_examine_panel(src) + update_icon() + to_chat(user, "You remove the bolts anchoring the support rods.") + return + if(1) + var/cut_cover + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if( WT.remove_fuel(0,user) ) + cut_cover=1 + else + to_chat(user, "You need more welding fuel to complete this task.") + return + else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) + cut_cover = 1 + if(cut_cover) + to_chat(user, "You begin slicing through the support rods.") + playsound(src, W.usesound, 100, 1) + if(!do_after(user,70 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 1) + return + construction_stage = 0 + user.update_examine_panel(src) + update_icon() + to_chat(user, "You slice through the support rods.") + return + if(0) + if(W.has_tool_quality(TOOL_CROWBAR)) + to_chat(user, "You struggle to pry off the outer sheath.") + playsound(src, W.usesound, 100, 1) + if(!do_after(user,100 * W.toolspeed) || !istype(src, /turf/simulated/wall) || !user || !W || !T ) + return + if(user.loc == T && user.get_active_hand() == W ) + to_chat(user, "You pry off the outer sheath.") + dismantle_wall() + return + + if(istype(W,/obj/item/frame)) + var/obj/item/frame/F = W + F.try_build(src, user) + return + + else if(!istype(W,/obj/item/weapon/rcd) && !istype(W, /obj/item/weapon/reagent_containers)) + return attack_hand(user) + + diff --git a/code/game/turfs/simulated/wall_icon.dm b/code/game/turfs/simulated/wall_icon.dm index 7abc69e3b26..fdc54a18c4a 100644 --- a/code/game/turfs/simulated/wall_icon.dm +++ b/code/game/turfs/simulated/wall_icon.dm @@ -1,152 +1,152 @@ -/turf/simulated/wall/proc/update_material() - - if(!material) - return - - if(reinf_material) - construction_stage = 6 - else - construction_stage = null - if(!material) - material = get_material_by_name(DEFAULT_WALL_MATERIAL) - if(material) - explosion_resistance = material.explosion_resistance - if(reinf_material && reinf_material.explosion_resistance > explosion_resistance) - explosion_resistance = reinf_material.explosion_resistance - - if(reinf_material) - name = "reinforced [material.display_name] wall" - desc = "It seems to be a section of wall reinforced with [reinf_material.display_name] and plated with [material.display_name]." - else - name = "[material.display_name] wall" - desc = "It seems to be a section of wall plated with [material.display_name]." - - if(material.opacity > 0.5 && !opacity) - set_light(1) - else if(material.opacity < 0.5 && opacity) - set_light(0) - - SSradiation.resistance_cache.Remove(src) - update_connections(1) - update_icon() - - -/turf/simulated/wall/proc/set_material(var/datum/material/newmaterial, var/datum/material/newrmaterial, var/datum/material/newgmaterial) - material = newmaterial - reinf_material = newrmaterial - if(!newgmaterial) - girder_material = DEFAULT_WALL_MATERIAL - else - girder_material = newgmaterial - update_material() - -/turf/simulated/wall/update_icon() - if(!material) - return - - if(!damage_overlays[1]) //list hasn't been populated - generate_overlays() - - cut_overlays() - var/image/I - - if(!density) - I = image(wall_masks, "[material.icon_base]fwall_open") - I.color = material.icon_colour - add_overlay(I) - return - - for(var/i = 1 to 4) - I = image(wall_masks, "[material.icon_base][wall_connections[i]]", dir = 1<<(i-1)) - I.color = material.icon_colour - add_overlay(I) - - if(reinf_material) - if(construction_stage != null && construction_stage < 6) - I = image(wall_masks, "reinf_construct-[construction_stage]") - I.color = reinf_material.icon_colour - add_overlay(I) - else - if("[reinf_material.icon_reinf]0" in cached_icon_states(wall_masks)) - // Directional icon - for(var/i = 1 to 4) - I = image(wall_masks, "[reinf_material.icon_reinf][wall_connections[i]]", dir = 1<<(i-1)) - I.color = reinf_material.icon_colour - add_overlay(I) - else if("[reinf_material.icon_reinf]" in cached_icon_states(wall_masks)) - I = image(wall_masks, reinf_material.icon_reinf) - I.color = reinf_material.icon_colour - add_overlay(I) - var/image/texture = material.get_wall_texture() - if(texture) - add_overlay(texture) - - if(damage != 0) - var/integrity = material.integrity - if(reinf_material) - integrity += reinf_material.integrity - - var/overlay = round(damage / integrity * damage_overlays.len) + 1 - if(overlay > damage_overlays.len) - overlay = damage_overlays.len - - add_overlay(damage_overlays[overlay]) - return - -/turf/simulated/wall/proc/generate_overlays() - var/alpha_inc = 256 / damage_overlays.len - - for(var/i = 1; i <= damage_overlays.len; i++) - var/image/img = image(icon = 'icons/turf/walls.dmi', icon_state = "overlay_damage") - img.blend_mode = BLEND_MULTIPLY - img.alpha = (i * alpha_inc) - 1 - damage_overlays[i] = img - - -/turf/simulated/wall/proc/update_connections(propagate = 0) - if(!material) - return - var/list/dirs = list() - var/inrange = orange(src, 1) - for(var/turf/simulated/wall/W in inrange) - if(!W.material) - continue - if(propagate) - W.update_connections() - W.update_icon() - if(can_join_with_wall(W)) - dirs += get_dir(src, W) - for(var/obj/structure/low_wall/WF in inrange) - if(can_join_with_low_wall(WF)) - dirs += get_dir(src, WF) - - special_wall_connections(dirs, inrange) - wall_connections = dirs_to_corner_states(dirs) - -/turf/simulated/wall/proc/special_wall_connections(list/dirs, list/inrange) - if(material.icon_base == "hull") // Could be improved... - var/additional_dirs = 0 - for(var/direction in alldirs) - var/turf/T = get_step(src,direction) - if(T && (locate(/obj/structure/hull_corner) in T)) - dirs += direction - additional_dirs |= direction - if(additional_dirs) - for(var/diag_dir in cornerdirs) - if ((additional_dirs & diag_dir) == diag_dir) - dirs += diag_dir - -/turf/simulated/wall/proc/can_join_with_wall(var/turf/simulated/wall/W) - //No blending if no material - if(!material || !W.material) - return 0 - //We can blend if either is the same, or a subtype, of the other one - if(istype(W.material, material.type) || istype(material, W.material.type)) - return 1 - //Also blend if they have the same iconbase - if(material.icon_base == W.material.icon_base) - return 1 - return 0 - -/turf/simulated/wall/proc/can_join_with_low_wall(var/obj/structure/low_wall/WF) +/turf/simulated/wall/proc/update_material() + + if(!material) + return + + if(reinf_material) + construction_stage = 6 + else + construction_stage = null + if(!material) + material = get_material_by_name(DEFAULT_WALL_MATERIAL) + if(material) + explosion_resistance = material.explosion_resistance + if(reinf_material && reinf_material.explosion_resistance > explosion_resistance) + explosion_resistance = reinf_material.explosion_resistance + + if(reinf_material) + name = "reinforced [material.display_name] wall" + desc = "It seems to be a section of wall reinforced with [reinf_material.display_name] and plated with [material.display_name]." + else + name = "[material.display_name] wall" + desc = "It seems to be a section of wall plated with [material.display_name]." + + if(material.opacity > 0.5 && !opacity) + set_light(1) + else if(material.opacity < 0.5 && opacity) + set_light(0) + + SSradiation.resistance_cache.Remove(src) + update_connections(1) + update_icon() + + +/turf/simulated/wall/proc/set_material(var/datum/material/newmaterial, var/datum/material/newrmaterial, var/datum/material/newgmaterial) + material = newmaterial + reinf_material = newrmaterial + if(!newgmaterial) + girder_material = DEFAULT_WALL_MATERIAL + else + girder_material = newgmaterial + update_material() + +/turf/simulated/wall/update_icon() + if(!material) + return + + if(!damage_overlays[1]) //list hasn't been populated + generate_overlays() + + cut_overlays() + var/image/I + + if(!density) + I = image(wall_masks, "[material.icon_base]fwall_open") + I.color = material.icon_colour + add_overlay(I) + return + + for(var/i = 1 to 4) + I = image(wall_masks, "[material.icon_base][wall_connections[i]]", dir = 1<<(i-1)) + I.color = material.icon_colour + add_overlay(I) + + if(reinf_material) + if(construction_stage != null && construction_stage < 6) + I = image(wall_masks, "reinf_construct-[construction_stage]") + I.color = reinf_material.icon_colour + add_overlay(I) + else + if("[reinf_material.icon_reinf]0" in cached_icon_states(wall_masks)) + // Directional icon + for(var/i = 1 to 4) + I = image(wall_masks, "[reinf_material.icon_reinf][wall_connections[i]]", dir = 1<<(i-1)) + I.color = reinf_material.icon_colour + add_overlay(I) + else if("[reinf_material.icon_reinf]" in cached_icon_states(wall_masks)) + I = image(wall_masks, reinf_material.icon_reinf) + I.color = reinf_material.icon_colour + add_overlay(I) + var/image/texture = material.get_wall_texture() + if(texture) + add_overlay(texture) + + if(damage != 0) + var/integrity = material.integrity + if(reinf_material) + integrity += reinf_material.integrity + + var/overlay = round(damage / integrity * damage_overlays.len) + 1 + if(overlay > damage_overlays.len) + overlay = damage_overlays.len + + add_overlay(damage_overlays[overlay]) + return + +/turf/simulated/wall/proc/generate_overlays() + var/alpha_inc = 256 / damage_overlays.len + + for(var/i = 1; i <= damage_overlays.len; i++) + var/image/img = image(icon = 'icons/turf/walls.dmi', icon_state = "overlay_damage") + img.blend_mode = BLEND_MULTIPLY + img.alpha = (i * alpha_inc) - 1 + damage_overlays[i] = img + + +/turf/simulated/wall/proc/update_connections(propagate = 0) + if(!material) + return + var/list/dirs = list() + var/inrange = orange(src, 1) + for(var/turf/simulated/wall/W in inrange) + if(!W.material) + continue + if(propagate) + W.update_connections() + W.update_icon() + if(can_join_with_wall(W)) + dirs += get_dir(src, W) + for(var/obj/structure/low_wall/WF in inrange) + if(can_join_with_low_wall(WF)) + dirs += get_dir(src, WF) + + special_wall_connections(dirs, inrange) + wall_connections = dirs_to_corner_states(dirs) + +/turf/simulated/wall/proc/special_wall_connections(list/dirs, list/inrange) + if(material.icon_base == "hull") // Could be improved... + var/additional_dirs = 0 + for(var/direction in alldirs) + var/turf/T = get_step(src,direction) + if(T && (locate(/obj/structure/hull_corner) in T)) + dirs += direction + additional_dirs |= direction + if(additional_dirs) + for(var/diag_dir in cornerdirs) + if ((additional_dirs & diag_dir) == diag_dir) + dirs += diag_dir + +/turf/simulated/wall/proc/can_join_with_wall(var/turf/simulated/wall/W) + //No blending if no material + if(!material || !W.material) + return 0 + //We can blend if either is the same, or a subtype, of the other one + if(istype(W.material, material.type) || istype(material, W.material.type)) + return 1 + //Also blend if they have the same iconbase + if(material.icon_base == W.material.icon_base) + return 1 + return 0 + +/turf/simulated/wall/proc/can_join_with_low_wall(var/obj/structure/low_wall/WF) return FALSE \ No newline at end of file diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 0c34354accc..52cedfdbfd5 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -1,341 +1,341 @@ -/turf/simulated/wall - name = "wall" - desc = "A huge chunk of metal used to separate rooms." - icon = 'icons/turf/wall_masks.dmi' - icon_state = "generic" - opacity = 1 - density = TRUE - blocks_air = 1 - thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT - heat_capacity = 312500 //a little over 5 cm thick , 312500 for 1 m by 2.5 m by 0.25 m plasteel wall - - var/icon/wall_masks = 'icons/turf/wall_masks.dmi' - var/damage = 0 - var/damage_overlay = 0 - var/global/damage_overlays[16] - var/active - var/can_open = 0 - var/datum/material/girder_material - var/datum/material/material - var/datum/material/reinf_material - var/last_state - var/construction_stage - - // There's basically always going to be wall connections, making this lazy doesn't seem like it'd help much unless you wanted to make it bitflags instead. - var/list/wall_connections = list("0", "0", "0", "0") - -// Walls always hide the stuff below them. -/turf/simulated/wall/levelupdate() - for(var/obj/O in src) - O.hide(1) - -/turf/simulated/wall/Initialize(mapload, materialtype, rmaterialtype, girdertype) - . = ..() - icon_state = "blank" - if(!materialtype) - materialtype = DEFAULT_WALL_MATERIAL - material = get_material_by_name(materialtype) - if(!girdertype) - girdertype = DEFAULT_WALL_MATERIAL - girder_material = get_material_by_name(girdertype) - if(!isnull(rmaterialtype)) - reinf_material = get_material_by_name(rmaterialtype) - update_material() - START_PROCESSING(SSturfs, src) - -/turf/simulated/wall/Destroy() - STOP_PROCESSING(SSturfs, src) - return ..() - -/turf/simulated/wall/examine_icon() - return icon(icon=initial(icon), icon_state=initial(icon_state)) - -/turf/simulated/wall/process() - // Calling parent will kill processing - if(!radiate()) - return PROCESS_KILL - -/turf/simulated/wall/proc/get_material() - return material - -/turf/simulated/wall/bullet_act(var/obj/item/projectile/Proj) - if(istype(Proj,/obj/item/projectile/beam)) - burn(2500) - else if(istype(Proj,/obj/item/projectile/ion)) - burn(500) - - var/proj_damage = Proj.get_structure_damage() - - //cap the amount of damage, so that things like emitters can't destroy walls in one hit. - var/damage = min(proj_damage, 100) - - if(Proj.damage_type == BURN && damage > 0) - if(thermite) - thermitemelt() - - if(istype(Proj,/obj/item/projectile/beam)) - if(material && material.reflectivity >= 0.5) // Time to reflect lasers. - var/new_damage = damage * material.reflectivity - var/outgoing_damage = damage - new_damage - damage = new_damage - Proj.damage = outgoing_damage - - visible_message("\The [src] reflects \the [Proj]!") - - // Find a turf near or on the original location to bounce to - var/new_x = Proj.starting.x + pick(0, 0, 0, -1, 1, -2, 2) - var/new_y = Proj.starting.y + pick(0, 0, 0, -1, 1, -2, 2) - //var/turf/curloc = get_turf(src) - var/turf/curloc = get_step(src, get_dir(src, Proj.starting)) - - Proj.penetrating += 1 // Needed for the beam to get out of the wall. - - // redirect the projectile - Proj.redirect(new_x, new_y, curloc, null) - - take_damage(damage) - return - -/turf/simulated/wall/hitby(AM as mob|obj, var/speed=THROWFORCE_SPEED_DIVISOR) - ..() - if(ismob(AM)) - return - - var/tforce = AM:throwforce * (speed/THROWFORCE_SPEED_DIVISOR) - if (tforce < 15) - return - - take_damage(tforce) - -/turf/simulated/wall/proc/clear_plants() - for(var/obj/effect/overlay/wallrot/WR in src) - qdel(WR) - for(var/obj/effect/plant/plant in range(src, 1)) - if(!plant.floor) //shrooms drop to the floor - plant.floor = 1 - plant.update_icon() - plant.pixel_x = 0 - plant.pixel_y = 0 - plant.update_neighbors() - -/turf/simulated/wall/ChangeTurf(var/turf/N, var/tell_universe, var/force_lighting_update, var/preserve_outdoors) - clear_plants() - ..(N, tell_universe, force_lighting_update, preserve_outdoors) - -//Appearance -/turf/simulated/wall/examine(mob/user) - . = ..() - - if(!damage) - . += "It looks fully intact." - else - var/dam = damage / material.integrity - if(dam <= 0.3) - . += "It looks slightly damaged." - else if(dam <= 0.6) - . += "It looks moderately damaged." - else - . += "It looks heavily damaged." - - if(locate(/obj/effect/overlay/wallrot) in src) - . += "There is fungus growing on [src]." - -//Damage - -/turf/simulated/wall/melt() - - if(!can_melt()) - return - - src.ChangeTurf(/turf/simulated/floor/plating) - - var/turf/simulated/floor/F = src - if(!F) - return - F.burn_tile() - F.icon_state = "wall_thermite" - visible_message("\The [src] spontaneously combusts!.") //!!OH SHIT!! - return - -/turf/simulated/wall/take_damage(dam) - if(dam) - damage = max(0, damage + dam) - update_damage() - return - -/turf/simulated/wall/proc/update_damage() - var/cap = material.integrity - if(reinf_material) - cap += reinf_material.integrity - - if(locate(/obj/effect/overlay/wallrot) in src) - cap = cap / 10 - - if(damage >= cap) - dismantle_wall() - else - update_icon() - - return - -/turf/simulated/wall/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)//Doesn't fucking work because walls don't interact with air :( - burn(exposed_temperature) - -/turf/simulated/wall/adjacent_fire_act(turf/simulated/floor/adj_turf, datum/gas_mixture/adj_air, adj_temp, adj_volume) - burn(adj_temp) - if(adj_temp > material.melting_point) - take_damage(log(RAND_F(0.9, 1.1) * (adj_temp - material.melting_point))) - - return ..() - -/turf/simulated/wall/proc/dismantle_wall(var/devastated, var/explode, var/no_product) - - playsound(src, 'sound/items/Welder.ogg', 100, 1) - if(!no_product) - if(reinf_material) - reinf_material.place_dismantled_girder(src, reinf_material, girder_material) - else - material.place_dismantled_girder(src, null, girder_material) - if(!devastated) - material.place_dismantled_product(src) - if (!reinf_material) - material.place_dismantled_product(src) - - for(var/obj/O in src.contents) //Eject contents! - if(istype(O,/obj/structure/sign/poster)) - var/obj/structure/sign/poster/P = O - P.roll_and_drop(src) - else - O.loc = src - - clear_plants() - material = get_material_by_name("placeholder") - reinf_material = null - girder_material = null - update_connections(1) - - ChangeTurf(/turf/simulated/floor/plating) - -/turf/simulated/wall/ex_act(severity) - switch(severity) - if(1.0) - if(girder_material.explosion_resistance >= 25 && prob(girder_material.explosion_resistance)) - new /obj/structure/girder/displaced(src, girder_material.name) - src.ChangeTurf(get_base_turf_by_area(src)) - if(2.0) - if(prob(75)) - take_damage(rand(150, 250)) - else - dismantle_wall(1,1) - if(3.0) - take_damage(rand(0, 250)) - else - return - -// Wall-rot effect, a nasty fungus that destroys walls. -/turf/simulated/wall/proc/rot() - if(locate(/obj/effect/overlay/wallrot) in src) - return FALSE - - // Wall-rot can't go onto walls that are surrounded in all four cardinal directions. - // Because of spores, or something. It's actually to avoid the pain that is removing wallrot surrounded by - // four r-walls. - var/at_least_one_open_turf = FALSE - for(var/direction in GLOB.cardinal) - var/turf/T = get_step(src, direction) - if(!T.check_density()) - at_least_one_open_turf = TRUE - break - - if(!at_least_one_open_turf) - return FALSE - - var/number_rots = rand(2,3) - for(var/i=0, i= 150 && !girder_material.is_brittle()) //Strong girders will remain in place when a wall is melted. - dismantle_wall(1,1) - else - src.ChangeTurf(/turf/simulated/floor/plating) - - var/turf/simulated/floor/F = src - F.burn_tile() - F.icon_state = "dmg[rand(1,4)]" - to_chat(user, "The thermite starts melting through the wall.") - - spawn(100) - if(O) - qdel(O) -// F.sd_LumReset() //TODO: ~Carn - return - -/turf/simulated/wall/proc/radiate() - var/total_radiation = material.radioactivity + (reinf_material ? reinf_material.radioactivity / 2 : 0) + (girder_material ? girder_material.radioactivity / 2 : 0) - if(!total_radiation) - return - - SSradiation.radiate(src, total_radiation) - return total_radiation - -/turf/simulated/wall/proc/burn(temperature) - if(material.combustion_effect(src, temperature, 0.7)) - spawn(2) - new /obj/structure/girder(src, girder_material.name) - src.ChangeTurf(/turf/simulated/floor) - for(var/turf/simulated/wall/W in range(3,src)) - W.burn((temperature/4)) - for(var/obj/machinery/door/airlock/phoron/D in range(3,src)) - D.ignite(temperature/4) - -/turf/simulated/wall/can_engrave() - return (material && material.hardness >= 10 && material.hardness <= 100) - -/turf/simulated/wall/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - if(material.integrity > 1000) // Don't decon things like elevatorium. - return FALSE - if(reinf_material && !the_rcd.can_remove_rwalls) // Gotta do it the old fashioned way if your RCD can't. - return FALSE - - if(passed_mode == RCD_DECONSTRUCT) - var/delay_to_use = material.integrity / 3 // Steel has 150 integrity, so it'll take five seconds to down a regular wall. - if(reinf_material) - delay_to_use += reinf_material.integrity / 3 - return list( - RCD_VALUE_MODE = RCD_DECONSTRUCT, - RCD_VALUE_DELAY = delay_to_use, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 - ) - return FALSE - -/turf/simulated/wall/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - if(passed_mode == RCD_DECONSTRUCT) - to_chat(user, span("notice", "You deconstruct \the [src].")) - ChangeTurf(/turf/simulated/floor/airless, preserve_outdoors = TRUE) - return TRUE - return FALSE - -/turf/simulated/wall/AltClick(mob/user) - if(isliving(user)) - var/mob/living/livingUser = user - if(try_graffiti(livingUser, livingUser.get_active_hand())) - return - . = ..() +/turf/simulated/wall + name = "wall" + desc = "A huge chunk of metal used to separate rooms." + icon = 'icons/turf/wall_masks.dmi' + icon_state = "generic" + opacity = 1 + density = TRUE + blocks_air = 1 + thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT + heat_capacity = 312500 //a little over 5 cm thick , 312500 for 1 m by 2.5 m by 0.25 m plasteel wall + + var/icon/wall_masks = 'icons/turf/wall_masks.dmi' + var/damage = 0 + var/damage_overlay = 0 + var/global/damage_overlays[16] + var/active + var/can_open = 0 + var/datum/material/girder_material + var/datum/material/material + var/datum/material/reinf_material + var/last_state + var/construction_stage + + // There's basically always going to be wall connections, making this lazy doesn't seem like it'd help much unless you wanted to make it bitflags instead. + var/list/wall_connections = list("0", "0", "0", "0") + +// Walls always hide the stuff below them. +/turf/simulated/wall/levelupdate() + for(var/obj/O in src) + O.hide(1) + +/turf/simulated/wall/Initialize(mapload, materialtype, rmaterialtype, girdertype) + . = ..() + icon_state = "blank" + if(!materialtype) + materialtype = DEFAULT_WALL_MATERIAL + material = get_material_by_name(materialtype) + if(!girdertype) + girdertype = DEFAULT_WALL_MATERIAL + girder_material = get_material_by_name(girdertype) + if(!isnull(rmaterialtype)) + reinf_material = get_material_by_name(rmaterialtype) + update_material() + START_PROCESSING(SSturfs, src) + +/turf/simulated/wall/Destroy() + STOP_PROCESSING(SSturfs, src) + return ..() + +/turf/simulated/wall/examine_icon() + return icon(icon=initial(icon), icon_state=initial(icon_state)) + +/turf/simulated/wall/process() + // Calling parent will kill processing + if(!radiate()) + return PROCESS_KILL + +/turf/simulated/wall/proc/get_material() + return material + +/turf/simulated/wall/bullet_act(var/obj/item/projectile/Proj) + if(istype(Proj,/obj/item/projectile/beam)) + burn(2500) + else if(istype(Proj,/obj/item/projectile/ion)) + burn(500) + + var/proj_damage = Proj.get_structure_damage() + + //cap the amount of damage, so that things like emitters can't destroy walls in one hit. + var/damage = min(proj_damage, 100) + + if(Proj.damage_type == BURN && damage > 0) + if(thermite) + thermitemelt() + + if(istype(Proj,/obj/item/projectile/beam)) + if(material && material.reflectivity >= 0.5) // Time to reflect lasers. + var/new_damage = damage * material.reflectivity + var/outgoing_damage = damage - new_damage + damage = new_damage + Proj.damage = outgoing_damage + + visible_message("\The [src] reflects \the [Proj]!") + + // Find a turf near or on the original location to bounce to + var/new_x = Proj.starting.x + pick(0, 0, 0, -1, 1, -2, 2) + var/new_y = Proj.starting.y + pick(0, 0, 0, -1, 1, -2, 2) + //var/turf/curloc = get_turf(src) + var/turf/curloc = get_step(src, get_dir(src, Proj.starting)) + + Proj.penetrating += 1 // Needed for the beam to get out of the wall. + + // redirect the projectile + Proj.redirect(new_x, new_y, curloc, null) + + take_damage(damage) + return + +/turf/simulated/wall/hitby(AM as mob|obj, var/speed=THROWFORCE_SPEED_DIVISOR) + ..() + if(ismob(AM)) + return + + var/tforce = AM:throwforce * (speed/THROWFORCE_SPEED_DIVISOR) + if (tforce < 15) + return + + take_damage(tforce) + +/turf/simulated/wall/proc/clear_plants() + for(var/obj/effect/overlay/wallrot/WR in src) + qdel(WR) + for(var/obj/effect/plant/plant in range(src, 1)) + if(!plant.floor) //shrooms drop to the floor + plant.floor = 1 + plant.update_icon() + plant.pixel_x = 0 + plant.pixel_y = 0 + plant.update_neighbors() + +/turf/simulated/wall/ChangeTurf(var/turf/N, var/tell_universe, var/force_lighting_update, var/preserve_outdoors) + clear_plants() + ..(N, tell_universe, force_lighting_update, preserve_outdoors) + +//Appearance +/turf/simulated/wall/examine(mob/user) + . = ..() + + if(!damage) + . += "It looks fully intact." + else + var/dam = damage / material.integrity + if(dam <= 0.3) + . += "It looks slightly damaged." + else if(dam <= 0.6) + . += "It looks moderately damaged." + else + . += "It looks heavily damaged." + + if(locate(/obj/effect/overlay/wallrot) in src) + . += "There is fungus growing on [src]." + +//Damage + +/turf/simulated/wall/melt() + + if(!can_melt()) + return + + src.ChangeTurf(/turf/simulated/floor/plating) + + var/turf/simulated/floor/F = src + if(!F) + return + F.burn_tile() + F.icon_state = "wall_thermite" + visible_message("\The [src] spontaneously combusts!.") //!!OH SHIT!! + return + +/turf/simulated/wall/take_damage(dam) + if(dam) + damage = max(0, damage + dam) + update_damage() + return + +/turf/simulated/wall/proc/update_damage() + var/cap = material.integrity + if(reinf_material) + cap += reinf_material.integrity + + if(locate(/obj/effect/overlay/wallrot) in src) + cap = cap / 10 + + if(damage >= cap) + dismantle_wall() + else + update_icon() + + return + +/turf/simulated/wall/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)//Doesn't fucking work because walls don't interact with air :( + burn(exposed_temperature) + +/turf/simulated/wall/adjacent_fire_act(turf/simulated/floor/adj_turf, datum/gas_mixture/adj_air, adj_temp, adj_volume) + burn(adj_temp) + if(adj_temp > material.melting_point) + take_damage(log(RAND_F(0.9, 1.1) * (adj_temp - material.melting_point))) + + return ..() + +/turf/simulated/wall/proc/dismantle_wall(var/devastated, var/explode, var/no_product) + + playsound(src, 'sound/items/Welder.ogg', 100, 1) + if(!no_product) + if(reinf_material) + reinf_material.place_dismantled_girder(src, reinf_material, girder_material) + else + material.place_dismantled_girder(src, null, girder_material) + if(!devastated) + material.place_dismantled_product(src) + if (!reinf_material) + material.place_dismantled_product(src) + + for(var/obj/O in src.contents) //Eject contents! + if(istype(O,/obj/structure/sign/poster)) + var/obj/structure/sign/poster/P = O + P.roll_and_drop(src) + else + O.loc = src + + clear_plants() + material = get_material_by_name("placeholder") + reinf_material = null + girder_material = null + update_connections(1) + + ChangeTurf(/turf/simulated/floor/plating) + +/turf/simulated/wall/ex_act(severity) + switch(severity) + if(1.0) + if(girder_material.explosion_resistance >= 25 && prob(girder_material.explosion_resistance)) + new /obj/structure/girder/displaced(src, girder_material.name) + src.ChangeTurf(get_base_turf_by_area(src)) + if(2.0) + if(prob(75)) + take_damage(rand(150, 250)) + else + dismantle_wall(1,1) + if(3.0) + take_damage(rand(0, 250)) + else + return + +// Wall-rot effect, a nasty fungus that destroys walls. +/turf/simulated/wall/proc/rot() + if(locate(/obj/effect/overlay/wallrot) in src) + return FALSE + + // Wall-rot can't go onto walls that are surrounded in all four cardinal directions. + // Because of spores, or something. It's actually to avoid the pain that is removing wallrot surrounded by + // four r-walls. + var/at_least_one_open_turf = FALSE + for(var/direction in GLOB.cardinal) + var/turf/T = get_step(src, direction) + if(!T.check_density()) + at_least_one_open_turf = TRUE + break + + if(!at_least_one_open_turf) + return FALSE + + var/number_rots = rand(2,3) + for(var/i=0, i= 150 && !girder_material.is_brittle()) //Strong girders will remain in place when a wall is melted. + dismantle_wall(1,1) + else + src.ChangeTurf(/turf/simulated/floor/plating) + + var/turf/simulated/floor/F = src + F.burn_tile() + F.icon_state = "dmg[rand(1,4)]" + to_chat(user, "The thermite starts melting through the wall.") + + spawn(100) + if(O) + qdel(O) +// F.sd_LumReset() //TODO: ~Carn + return + +/turf/simulated/wall/proc/radiate() + var/total_radiation = material.radioactivity + (reinf_material ? reinf_material.radioactivity / 2 : 0) + (girder_material ? girder_material.radioactivity / 2 : 0) + if(!total_radiation) + return + + SSradiation.radiate(src, total_radiation) + return total_radiation + +/turf/simulated/wall/proc/burn(temperature) + if(material.combustion_effect(src, temperature, 0.7)) + spawn(2) + new /obj/structure/girder(src, girder_material.name) + src.ChangeTurf(/turf/simulated/floor) + for(var/turf/simulated/wall/W in range(3,src)) + W.burn((temperature/4)) + for(var/obj/machinery/door/airlock/phoron/D in range(3,src)) + D.ignite(temperature/4) + +/turf/simulated/wall/can_engrave() + return (material && material.hardness >= 10 && material.hardness <= 100) + +/turf/simulated/wall/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + if(material.integrity > 1000) // Don't decon things like elevatorium. + return FALSE + if(reinf_material && !the_rcd.can_remove_rwalls) // Gotta do it the old fashioned way if your RCD can't. + return FALSE + + if(passed_mode == RCD_DECONSTRUCT) + var/delay_to_use = material.integrity / 3 // Steel has 150 integrity, so it'll take five seconds to down a regular wall. + if(reinf_material) + delay_to_use += reinf_material.integrity / 3 + return list( + RCD_VALUE_MODE = RCD_DECONSTRUCT, + RCD_VALUE_DELAY = delay_to_use, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 + ) + return FALSE + +/turf/simulated/wall/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + if(passed_mode == RCD_DECONSTRUCT) + to_chat(user, span("notice", "You deconstruct \the [src].")) + ChangeTurf(/turf/simulated/floor/airless, preserve_outdoors = TRUE) + return TRUE + return FALSE + +/turf/simulated/wall/AltClick(mob/user) + if(isliving(user)) + var/mob/living/livingUser = user + if(try_graffiti(livingUser, livingUser.get_active_hand())) + return + . = ..() diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index a997ed9a663..289a3b75ce9 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -1,263 +1,263 @@ -/turf/space - icon = 'icons/turf/space.dmi' - name = "\proper space" - icon_state = "default" - dynamic_lighting = 0 - plane = SPACE_PLANE - flags = TURF_ACID_IMMUNE - temperature = T20C - thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT - can_build_into_floor = TRUE - var/keep_sprite = FALSE - var/edge = FALSE //If we're an edge - var/forced_dirs = 0 //Force this one to pretend it's an overedge turf - -/turf/space/Initialize() - if(config.starlight) - update_starlight() - - //Sprite stuff only beyond here - if(keep_sprite) - return ..() - - //We might be an edge - if(y == world.maxy || forced_dirs & NORTH) - edge |= NORTH - else if(y == 1 || forced_dirs & SOUTH) - edge |= SOUTH - - if(x == 1 || forced_dirs & WEST) - edge |= WEST - else if(x == world.maxx || forced_dirs & EAST) - edge |= EAST - - if(edge) //Magic edges - appearance = SSskybox.mapedge_cache["[edge]"] - else //Dust - appearance = SSskybox.dust_cache["[((x + y) ^ ~(x * y) + z) % 25]"] - - return ..() - -/turf/space/proc/toggle_transit(var/direction) - if(edge) //Not a great way to do this yet. Maybe we'll come up with one. We could pre-make sprites... or tile the overlay over it? - return - - if(!direction) //Stopping our transit - appearance = SSskybox.dust_cache["[((x + y) ^ ~(x * y) + z) % 25]"] - else if(direction & (NORTH|SOUTH)) //Starting transit vertically - var/x_shift = SSskybox.phase_shift_by_x[src.x % (SSskybox.phase_shift_by_x.len - 1) + 1] - var/transit_state = ((direction & SOUTH ? world.maxy - src.y : src.y) + x_shift)%15 - appearance = SSskybox.speedspace_cache["NS_[transit_state]"] - else if(direction & (EAST|WEST)) //Starting transit horizontally - var/y_shift = SSskybox.phase_shift_by_y[src.y % (SSskybox.phase_shift_by_y.len - 1) + 1] - var/transit_state = ((direction & WEST ? world.maxx - src.x : src.x) + y_shift)%15 - appearance = SSskybox.speedspace_cache["EW_[transit_state]"] - - for(var/atom/movable/AM in src) - if (!AM.simulated) - continue - - if(!AM.anchored) - AM.throw_at(get_step(src,reverse_direction(direction)), 5, 1) - else if (istype(AM, /obj/effect/decal)) - qdel(AM) //No more space blood coming with the shuttle - -/turf/space/is_space() - return 1 - -// override for space turfs, since they should never hide anything -/turf/space/levelupdate() - for(var/obj/O in src) - O.hide(0) - -/turf/space/is_solid_structure() - return locate(/obj/structure/lattice, src) //counts as solid structure if it has a lattice - -/turf/space/proc/update_starlight() - if(locate(/turf/simulated) in orange(src,1)) - set_light(config.starlight) - else - set_light(0) - -/turf/space/attackby(obj/item/C as obj, mob/user as mob) - - if(istype(C, /obj/item/stack/rods)) - var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) - if(L) - return - var/obj/item/stack/rods/R = C - if (R.use(1)) - to_chat(user, "Constructing support lattice ...") - playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) - ReplaceWithLattice() - return - - if(istype(C, /obj/item/stack/tile/floor)) - var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) - if(L) - var/obj/item/stack/tile/floor/S = C - if (S.get_amount() < 1) - return - qdel(L) - playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) - S.use(1) - ChangeTurf(/turf/simulated/floor/airless) - return - else - to_chat(user, "The plating is going to need some support.") - - if(istype(C, /obj/item/stack/tile/roofing)) - var/turf/T = GetAbove(src) - var/obj/item/stack/tile/roofing/R = C - - // Patch holes in the ceiling - if(T) - if(istype(T, /turf/simulated/open) || istype(T, /turf/space)) - // Must be build adjacent to an existing floor/wall, no floating floors - var/turf/simulated/A = locate(/turf/simulated/floor) in T.CardinalTurfs() - if(!A) - A = locate(/turf/simulated/wall) in T.CardinalTurfs() - if(!A) - to_chat(user, "There's nothing to attach the ceiling to!") - return - - if(R.use(1)) // Cost of roofing tiles is 1:1 with cost to place lattice and plating - T.ReplaceWithLattice() - T.ChangeTurf(/turf/simulated/floor) - playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) - user.visible_message("[user] expands the ceiling.", "You expand the ceiling.") - else - to_chat(user, "There aren't any holes in the ceiling to patch here.") - return - // Space shouldn't have weather of the sort planets with atmospheres do. - // If that's changed, then you'll want to swipe the rest of the roofing code from code/game/turfs/simulated/floor_attackby.dm - return - -/turf/space/Entered(var/atom/movable/A) - . = ..() - - if(edge && ticker?.mode && !density) // !density so 'fake' space turfs don't fling ghosts everywhere - if(isliving(A)) - var/mob/living/L = A - if(L.pulling) - var/atom/movable/pulled = L.pulling - L.stop_pulling() - A?.touch_map_edge() - pulled.forceMove(L.loc) - L.continue_pulling(pulled) - else - A?.touch_map_edge() - else - A?.touch_map_edge() - -/turf/space/proc/Sandbox_Spacemove(atom/movable/A as mob|obj) - var/cur_x - var/cur_y - var/next_x - var/next_y - var/target_z - var/list/y_arr - - if(src.x <= 1) - if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) - qdel(A) - return - - var/list/cur_pos = src.get_global_map_pos() - if(!cur_pos) return - cur_x = cur_pos["x"] - cur_y = cur_pos["y"] - next_x = (--cur_x||global_map.len) - y_arr = global_map[next_x] - target_z = y_arr[cur_y] -/* - //debug - to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") - to_world("Target Z = [target_z]") - to_world("Next X = [next_x]") - //debug -*/ - if(target_z) - A.z = target_z - A.x = world.maxx - 2 - spawn (0) - if ((A && A.loc)) - A.loc.Entered(A) - else if (src.x >= world.maxx) - if(istype(A, /obj/effect/meteor)) - qdel(A) - return - - var/list/cur_pos = src.get_global_map_pos() - if(!cur_pos) return - cur_x = cur_pos["x"] - cur_y = cur_pos["y"] - next_x = (++cur_x > global_map.len ? 1 : cur_x) - y_arr = global_map[next_x] - target_z = y_arr[cur_y] -/* - //debug - to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") - to_world("Target Z = [target_z]") - to_world("Next X = [next_x]") - //debug -*/ - if(target_z) - A.z = target_z - A.x = 3 - spawn (0) - if ((A && A.loc)) - A.loc.Entered(A) - else if (src.y <= 1) - if(istype(A, /obj/effect/meteor)) - qdel(A) - return - var/list/cur_pos = src.get_global_map_pos() - if(!cur_pos) return - cur_x = cur_pos["x"] - cur_y = cur_pos["y"] - y_arr = global_map[cur_x] - next_y = (--cur_y||y_arr.len) - target_z = y_arr[next_y] -/* - //debug - to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") - to_world("Next Y = [next_y]") - to_world("Target Z = [target_z]") - //debug -*/ - if(target_z) - A.z = target_z - A.y = world.maxy - 2 - spawn (0) - if ((A && A.loc)) - A.loc.Entered(A) - - else if (src.y >= world.maxy) - if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) - qdel(A) - return - var/list/cur_pos = src.get_global_map_pos() - if(!cur_pos) return - cur_x = cur_pos["x"] - cur_y = cur_pos["y"] - y_arr = global_map[cur_x] - next_y = (++cur_y > y_arr.len ? 1 : cur_y) - target_z = y_arr[next_y] -/* - //debug - to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") - to_world("Next Y = [next_y]") - to_world("Target Z = [target_z]") - //debug -*/ - if(target_z) - A.z = target_z - A.y = 3 - spawn (0) - if ((A && A.loc)) - A.loc.Entered(A) - return - -/turf/space/ChangeTurf(var/turf/N, var/tell_universe, var/force_lighting_update, var/preserve_outdoors) - return ..(N, tell_universe, 1, preserve_outdoors) +/turf/space + icon = 'icons/turf/space.dmi' + name = "\proper space" + icon_state = "default" + dynamic_lighting = 0 + plane = SPACE_PLANE + flags = TURF_ACID_IMMUNE + temperature = T20C + thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT + can_build_into_floor = TRUE + var/keep_sprite = FALSE + var/edge = FALSE //If we're an edge + var/forced_dirs = 0 //Force this one to pretend it's an overedge turf + +/turf/space/Initialize() + if(config.starlight) + update_starlight() + + //Sprite stuff only beyond here + if(keep_sprite) + return ..() + + //We might be an edge + if(y == world.maxy || forced_dirs & NORTH) + edge |= NORTH + else if(y == 1 || forced_dirs & SOUTH) + edge |= SOUTH + + if(x == 1 || forced_dirs & WEST) + edge |= WEST + else if(x == world.maxx || forced_dirs & EAST) + edge |= EAST + + if(edge) //Magic edges + appearance = SSskybox.mapedge_cache["[edge]"] + else //Dust + appearance = SSskybox.dust_cache["[((x + y) ^ ~(x * y) + z) % 25]"] + + return ..() + +/turf/space/proc/toggle_transit(var/direction) + if(edge) //Not a great way to do this yet. Maybe we'll come up with one. We could pre-make sprites... or tile the overlay over it? + return + + if(!direction) //Stopping our transit + appearance = SSskybox.dust_cache["[((x + y) ^ ~(x * y) + z) % 25]"] + else if(direction & (NORTH|SOUTH)) //Starting transit vertically + var/x_shift = SSskybox.phase_shift_by_x[src.x % (SSskybox.phase_shift_by_x.len - 1) + 1] + var/transit_state = ((direction & SOUTH ? world.maxy - src.y : src.y) + x_shift)%15 + appearance = SSskybox.speedspace_cache["NS_[transit_state]"] + else if(direction & (EAST|WEST)) //Starting transit horizontally + var/y_shift = SSskybox.phase_shift_by_y[src.y % (SSskybox.phase_shift_by_y.len - 1) + 1] + var/transit_state = ((direction & WEST ? world.maxx - src.x : src.x) + y_shift)%15 + appearance = SSskybox.speedspace_cache["EW_[transit_state]"] + + for(var/atom/movable/AM in src) + if (!AM.simulated) + continue + + if(!AM.anchored) + AM.throw_at(get_step(src,reverse_direction(direction)), 5, 1) + else if (istype(AM, /obj/effect/decal)) + qdel(AM) //No more space blood coming with the shuttle + +/turf/space/is_space() + return 1 + +// override for space turfs, since they should never hide anything +/turf/space/levelupdate() + for(var/obj/O in src) + O.hide(0) + +/turf/space/is_solid_structure() + return locate(/obj/structure/lattice, src) //counts as solid structure if it has a lattice + +/turf/space/proc/update_starlight() + if(locate(/turf/simulated) in orange(src,1)) + set_light(config.starlight) + else + set_light(0) + +/turf/space/attackby(obj/item/C as obj, mob/user as mob) + + if(istype(C, /obj/item/stack/rods)) + var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) + if(L) + return + var/obj/item/stack/rods/R = C + if (R.use(1)) + to_chat(user, "Constructing support lattice ...") + playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) + ReplaceWithLattice() + return + + if(istype(C, /obj/item/stack/tile/floor)) + var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) + if(L) + var/obj/item/stack/tile/floor/S = C + if (S.get_amount() < 1) + return + qdel(L) + playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) + S.use(1) + ChangeTurf(/turf/simulated/floor/airless) + return + else + to_chat(user, "The plating is going to need some support.") + + if(istype(C, /obj/item/stack/tile/roofing)) + var/turf/T = GetAbove(src) + var/obj/item/stack/tile/roofing/R = C + + // Patch holes in the ceiling + if(T) + if(istype(T, /turf/simulated/open) || istype(T, /turf/space)) + // Must be build adjacent to an existing floor/wall, no floating floors + var/turf/simulated/A = locate(/turf/simulated/floor) in T.CardinalTurfs() + if(!A) + A = locate(/turf/simulated/wall) in T.CardinalTurfs() + if(!A) + to_chat(user, "There's nothing to attach the ceiling to!") + return + + if(R.use(1)) // Cost of roofing tiles is 1:1 with cost to place lattice and plating + T.ReplaceWithLattice() + T.ChangeTurf(/turf/simulated/floor) + playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) + user.visible_message("[user] expands the ceiling.", "You expand the ceiling.") + else + to_chat(user, "There aren't any holes in the ceiling to patch here.") + return + // Space shouldn't have weather of the sort planets with atmospheres do. + // If that's changed, then you'll want to swipe the rest of the roofing code from code/game/turfs/simulated/floor_attackby.dm + return + +/turf/space/Entered(var/atom/movable/A) + . = ..() + + if(edge && ticker?.mode && !density) // !density so 'fake' space turfs don't fling ghosts everywhere + if(isliving(A)) + var/mob/living/L = A + if(L.pulling) + var/atom/movable/pulled = L.pulling + L.stop_pulling() + A?.touch_map_edge() + pulled.forceMove(L.loc) + L.continue_pulling(pulled) + else + A?.touch_map_edge() + else + A?.touch_map_edge() + +/turf/space/proc/Sandbox_Spacemove(atom/movable/A as mob|obj) + var/cur_x + var/cur_y + var/next_x + var/next_y + var/target_z + var/list/y_arr + + if(src.x <= 1) + if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) + qdel(A) + return + + var/list/cur_pos = src.get_global_map_pos() + if(!cur_pos) return + cur_x = cur_pos["x"] + cur_y = cur_pos["y"] + next_x = (--cur_x||global_map.len) + y_arr = global_map[next_x] + target_z = y_arr[cur_y] +/* + //debug + to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_world("Target Z = [target_z]") + to_world("Next X = [next_x]") + //debug +*/ + if(target_z) + A.z = target_z + A.x = world.maxx - 2 + spawn (0) + if ((A && A.loc)) + A.loc.Entered(A) + else if (src.x >= world.maxx) + if(istype(A, /obj/effect/meteor)) + qdel(A) + return + + var/list/cur_pos = src.get_global_map_pos() + if(!cur_pos) return + cur_x = cur_pos["x"] + cur_y = cur_pos["y"] + next_x = (++cur_x > global_map.len ? 1 : cur_x) + y_arr = global_map[next_x] + target_z = y_arr[cur_y] +/* + //debug + to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_world("Target Z = [target_z]") + to_world("Next X = [next_x]") + //debug +*/ + if(target_z) + A.z = target_z + A.x = 3 + spawn (0) + if ((A && A.loc)) + A.loc.Entered(A) + else if (src.y <= 1) + if(istype(A, /obj/effect/meteor)) + qdel(A) + return + var/list/cur_pos = src.get_global_map_pos() + if(!cur_pos) return + cur_x = cur_pos["x"] + cur_y = cur_pos["y"] + y_arr = global_map[cur_x] + next_y = (--cur_y||y_arr.len) + target_z = y_arr[next_y] +/* + //debug + to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_world("Next Y = [next_y]") + to_world("Target Z = [target_z]") + //debug +*/ + if(target_z) + A.z = target_z + A.y = world.maxy - 2 + spawn (0) + if ((A && A.loc)) + A.loc.Entered(A) + + else if (src.y >= world.maxy) + if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) + qdel(A) + return + var/list/cur_pos = src.get_global_map_pos() + if(!cur_pos) return + cur_x = cur_pos["x"] + cur_y = cur_pos["y"] + y_arr = global_map[cur_x] + next_y = (++cur_y > y_arr.len ? 1 : cur_y) + target_z = y_arr[next_y] +/* + //debug + to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_world("Next Y = [next_y]") + to_world("Target Z = [target_z]") + //debug +*/ + if(target_z) + A.z = target_z + A.y = 3 + spawn (0) + if ((A && A.loc)) + A.loc.Entered(A) + return + +/turf/space/ChangeTurf(var/turf/N, var/tell_universe, var/force_lighting_update, var/preserve_outdoors) + return ..(N, tell_universe, 1, preserve_outdoors) diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm index 02c589e966d..e5a560a9134 100644 --- a/code/game/turfs/space/transit.dm +++ b/code/game/turfs/space/transit.dm @@ -1,31 +1,31 @@ -/turf/space/transit - can_build_into_floor = FALSE - var/pushdirection // push things that get caught in the transit tile this direction - -//Overwrite because we dont want people building rods in space. -/turf/space/transit/attackby(obj/O as obj, mob/user as mob) - return - -/turf/space/transit/Initialize() - . = ..() - toggle_transit(reverse_dir[pushdirection]) - -//------------------------ - -/turf/space/transit/north // moving to the north - icon_state = "arrow-north" - pushdirection = SOUTH // south because the space tile is scrolling south - -/turf/space/transit/south // moving to the south - icon_state = "arrow-south" - pushdirection = SOUTH // south because the space tile is scrolling south - -/turf/space/transit/east // moving to the east - icon_state = "arrow-east" - pushdirection = WEST - -/turf/space/transit/west // moving to the west - icon_state = "arrow-west" - pushdirection = WEST - +/turf/space/transit + can_build_into_floor = FALSE + var/pushdirection // push things that get caught in the transit tile this direction + +//Overwrite because we dont want people building rods in space. +/turf/space/transit/attackby(obj/O as obj, mob/user as mob) + return + +/turf/space/transit/Initialize() + . = ..() + toggle_transit(reverse_dir[pushdirection]) + +//------------------------ + +/turf/space/transit/north // moving to the north + icon_state = "arrow-north" + pushdirection = SOUTH // south because the space tile is scrolling south + +/turf/space/transit/south // moving to the south + icon_state = "arrow-south" + pushdirection = SOUTH // south because the space tile is scrolling south + +/turf/space/transit/east // moving to the east + icon_state = "arrow-east" + pushdirection = WEST + +/turf/space/transit/west // moving to the west + icon_state = "arrow-west" + pushdirection = WEST + //------------------------ \ No newline at end of file diff --git a/code/game/turfs/turf_flick_animations.dm b/code/game/turfs/turf_flick_animations.dm index 81b248ec0df..43f1e90f598 100644 --- a/code/game/turfs/turf_flick_animations.dm +++ b/code/game/turfs/turf_flick_animations.dm @@ -1,20 +1,20 @@ -/proc/anim(turf/location as turf,target as mob|obj,a_icon,a_icon_state as text,flick_anim as text,sleeptime = 0,direction as num) -//This proc throws up either an icon or an animation for a specified amount of time. -//The variables should be apparent enough. - if(!location && target) - location = get_turf(target) - if(location && !target) - target = location - var/atom/movable/overlay/animation = new /atom/movable/overlay(location) - if(direction) - animation.set_dir(direction) - animation.icon = a_icon - animation.layer = target:layer+1 - if(a_icon_state) - animation.icon_state = a_icon_state - else - animation.icon_state = "blank" - animation.master = target - flick(flick_anim, animation) - spawn(max(sleeptime, 15)) - qdel(animation) +/proc/anim(turf/location as turf,target as mob|obj,a_icon,a_icon_state as text,flick_anim as text,sleeptime = 0,direction as num) +//This proc throws up either an icon or an animation for a specified amount of time. +//The variables should be apparent enough. + if(!location && target) + location = get_turf(target) + if(location && !target) + target = location + var/atom/movable/overlay/animation = new /atom/movable/overlay(location) + if(direction) + animation.set_dir(direction) + animation.icon = a_icon + animation.layer = target:layer+1 + if(a_icon_state) + animation.icon_state = a_icon_state + else + animation.icon_state = "blank" + animation.master = target + flick(flick_anim, animation) + spawn(max(sleeptime, 15)) + qdel(animation) diff --git a/code/game/turfs/unsimulated.dm b/code/game/turfs/unsimulated.dm index 0e8c5c98b69..f9bfd4102b6 100644 --- a/code/game/turfs/unsimulated.dm +++ b/code/game/turfs/unsimulated.dm @@ -1,32 +1,32 @@ -/turf/unsimulated - name = "command" - oxygen = MOLES_O2STANDARD - nitrogen = MOLES_N2STANDARD - var/skip_init = TRUE // Don't call down the chain, apparently for performance when loading maps at runtime. - flags = TURF_ACID_IMMUNE - -/turf/unsimulated/Initialize(mapload) - if(skip_init) - initialized = TRUE - return INITIALIZE_HINT_NORMAL - . = ..() - -//VOREStation Add -/turf/unsimulated/fake_space - name = "\proper space" - icon = 'icons/turf/space.dmi' - icon_state = "0" - dynamic_lighting = FALSE - initialized = FALSE - -/turf/unsimulated/fake_space/Initialize(mapload) - . = ..() - icon_state = "[((x + y) ^ ~(x * y) + z) % 25]" -//VOREStation Add End - -// Better nip this just in case. -/turf/unsimulated/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - return FALSE - -/turf/unsimulated/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - return FALSE +/turf/unsimulated + name = "command" + oxygen = MOLES_O2STANDARD + nitrogen = MOLES_N2STANDARD + var/skip_init = TRUE // Don't call down the chain, apparently for performance when loading maps at runtime. + flags = TURF_ACID_IMMUNE + +/turf/unsimulated/Initialize(mapload) + if(skip_init) + initialized = TRUE + return INITIALIZE_HINT_NORMAL + . = ..() + +//VOREStation Add +/turf/unsimulated/fake_space + name = "\proper space" + icon = 'icons/turf/space.dmi' + icon_state = "0" + dynamic_lighting = FALSE + initialized = FALSE + +/turf/unsimulated/fake_space/Initialize(mapload) + . = ..() + icon_state = "[((x + y) ^ ~(x * y) + z) % 25]" +//VOREStation Add End + +// Better nip this just in case. +/turf/unsimulated/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + return FALSE + +/turf/unsimulated/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + return FALSE diff --git a/code/game/turfs/unsimulated/beach.dm b/code/game/turfs/unsimulated/beach.dm index b1841dadad8..ccd22d213fb 100644 --- a/code/game/turfs/unsimulated/beach.dm +++ b/code/game/turfs/unsimulated/beach.dm @@ -1,72 +1,72 @@ -/turf/unsimulated/beach - name = "Beach" - icon = 'icons/misc/beach.dmi' - -/turf/unsimulated/beach/sand - name = "Sand" - icon_state = "sand" - -/turf/unsimulated/beach/coastline - name = "Coastline" - icon = 'icons/misc/beach2.dmi' - icon_state = "sandwater" - -/turf/unsimulated/beach/water - name = "Water" - icon_state = "water" - skip_init = FALSE - movement_cost = 4 // Water should slow you down, just like simulated turf. - -/turf/unsimulated/beach/water/Initialize() - . = ..() - add_overlay(image("icon"='icons/misc/beach.dmi',"icon_state"="water2","layer"=MOB_LAYER+0.1)) - -/turf/simulated/floor/beach - name = "Beach" - icon = 'icons/misc/beach.dmi' - initial_flooring = /decl/flooring/sand - -/turf/simulated/floor/beach/sand - name = "Sand" - icon_state = "sand" - initial_flooring = /decl/flooring/sand - -/turf/simulated/floor/beach/sand/desert - icon = 'icons/turf/desert.dmi' - icon_state = "desert" - initial_flooring = /decl/flooring/sand/desert - -/turf/simulated/floor/beach/sand/desert/Initialize() - . = ..() - if(prob(5)) - icon_state = "desert[rand(0,4)]" - -/turf/simulated/floor/beach/coastline - name = "Coastline" - icon = 'icons/misc/beach2.dmi' - icon_state = "sandwater" - -/turf/simulated/floor/beach/water - name = "Water" - icon_state = "water" - movement_cost = 4 // Water should slow you down, just like the original simulated turf. - initial_flooring = /decl/flooring/water - -/turf/simulated/floor/beach/water/ocean - icon_state = "seadeep" - movement_cost = 8 // Deep water should be difficult to wade through. - initial_flooring = /decl/flooring/water/beach/deep - -/turf/simulated/floor/beach/water/Initialize() - . = ..() - add_overlay(image("icon"='icons/misc/beach.dmi',"icon_state"="water5","layer"=MOB_LAYER+0.1)) - -/decl/flooring/water/beach/deep // We're custom-defining a 'deep' water turf for the beach. - name = "deep water" - desc = "Deep Ocean Water" - icon = 'icons/misc/beach.dmi' - icon_base = "seadeep" - footstep_sounds = list("human" = list( - 'sound/effects/footstep/bubbles3.ogg', // No I don't get why it's named 3/4/5 either. Whatever. - 'sound/effects/footstep/bubbles4.ogg', - 'sound/effects/footstep/bubbles5.ogg')) +/turf/unsimulated/beach + name = "Beach" + icon = 'icons/misc/beach.dmi' + +/turf/unsimulated/beach/sand + name = "Sand" + icon_state = "sand" + +/turf/unsimulated/beach/coastline + name = "Coastline" + icon = 'icons/misc/beach2.dmi' + icon_state = "sandwater" + +/turf/unsimulated/beach/water + name = "Water" + icon_state = "water" + skip_init = FALSE + movement_cost = 4 // Water should slow you down, just like simulated turf. + +/turf/unsimulated/beach/water/Initialize() + . = ..() + add_overlay(image("icon"='icons/misc/beach.dmi',"icon_state"="water2","layer"=MOB_LAYER+0.1)) + +/turf/simulated/floor/beach + name = "Beach" + icon = 'icons/misc/beach.dmi' + initial_flooring = /decl/flooring/sand + +/turf/simulated/floor/beach/sand + name = "Sand" + icon_state = "sand" + initial_flooring = /decl/flooring/sand + +/turf/simulated/floor/beach/sand/desert + icon = 'icons/turf/desert.dmi' + icon_state = "desert" + initial_flooring = /decl/flooring/sand/desert + +/turf/simulated/floor/beach/sand/desert/Initialize() + . = ..() + if(prob(5)) + icon_state = "desert[rand(0,4)]" + +/turf/simulated/floor/beach/coastline + name = "Coastline" + icon = 'icons/misc/beach2.dmi' + icon_state = "sandwater" + +/turf/simulated/floor/beach/water + name = "Water" + icon_state = "water" + movement_cost = 4 // Water should slow you down, just like the original simulated turf. + initial_flooring = /decl/flooring/water + +/turf/simulated/floor/beach/water/ocean + icon_state = "seadeep" + movement_cost = 8 // Deep water should be difficult to wade through. + initial_flooring = /decl/flooring/water/beach/deep + +/turf/simulated/floor/beach/water/Initialize() + . = ..() + add_overlay(image("icon"='icons/misc/beach.dmi',"icon_state"="water5","layer"=MOB_LAYER+0.1)) + +/decl/flooring/water/beach/deep // We're custom-defining a 'deep' water turf for the beach. + name = "deep water" + desc = "Deep Ocean Water" + icon = 'icons/misc/beach.dmi' + icon_base = "seadeep" + footstep_sounds = list("human" = list( + 'sound/effects/footstep/bubbles3.ogg', // No I don't get why it's named 3/4/5 either. Whatever. + 'sound/effects/footstep/bubbles4.ogg', + 'sound/effects/footstep/bubbles5.ogg')) diff --git a/code/game/turfs/unsimulated/beach_vr.dm b/code/game/turfs/unsimulated/beach_vr.dm index 6fe6b07b2cb..3c1f48c7b96 100644 --- a/code/game/turfs/unsimulated/beach_vr.dm +++ b/code/game/turfs/unsimulated/beach_vr.dm @@ -1,5 +1,5 @@ -/turf/simulated/floor/beach/sand/outdoors - outdoors = OUTDOORS_YES - -/turf/simulated/floor/beach/sand/desert/outdoors +/turf/simulated/floor/beach/sand/outdoors + outdoors = OUTDOORS_YES + +/turf/simulated/floor/beach/sand/desert/outdoors outdoors = OUTDOORS_YES \ No newline at end of file diff --git a/code/game/turfs/unsimulated/floor.dm b/code/game/turfs/unsimulated/floor.dm index 22af37dd531..7f3df78c134 100644 --- a/code/game/turfs/unsimulated/floor.dm +++ b/code/game/turfs/unsimulated/floor.dm @@ -1,17 +1,17 @@ -/turf/unsimulated/floor - name = "floor" - icon = 'icons/turf/floors.dmi' - icon_state = "Floor3" - -/turf/unsimulated/mask - name = "mask" - icon = 'icons/turf/walls.dmi' - icon_state = "rockvault" - -/turf/unsimulated/floor/shuttle_ceiling - icon_state = "reinforced" - -/turf/unsimulated/elevator_shaft - name = "floor" - icon = 'icons/turf/floors.dmi' +/turf/unsimulated/floor + name = "floor" + icon = 'icons/turf/floors.dmi' + icon_state = "Floor3" + +/turf/unsimulated/mask + name = "mask" + icon = 'icons/turf/walls.dmi' + icon_state = "rockvault" + +/turf/unsimulated/floor/shuttle_ceiling + icon_state = "reinforced" + +/turf/unsimulated/elevator_shaft + name = "floor" + icon = 'icons/turf/floors.dmi' icon_state = "elevatorshaft" \ No newline at end of file diff --git a/code/game/turfs/unsimulated/sky_vr.dm b/code/game/turfs/unsimulated/sky_vr.dm index c71fa6470a7..9d8d40c71fc 100644 --- a/code/game/turfs/unsimulated/sky_vr.dm +++ b/code/game/turfs/unsimulated/sky_vr.dm @@ -1,73 +1,73 @@ -/////////////////// -// Generic skyfall turf -// Really only works well if the map doesn't have 'indoor' areas otherwise they can fall into one. -// TODO: Fix that. -/turf/unsimulated/floor/sky - name = "the sky" - desc = "It's the sky! Be careful!" - icon = 'icons/turf/floors.dmi' - icon_state = "sky_slow" - dir = SOUTH - initialized = FALSE - var/does_skyfall = TRUE - var/list/skyfall_levels - -/turf/unsimulated/floor/sky/Initialize() - . = ..() - if(does_skyfall && !LAZYLEN(skyfall_levels)) - error("[x],[y],[z], [get_area(src)] doesn't have skyfall_levels defined! Can't skyfall!") - if(locate(/turf/simulated) in orange(src,1)) - set_light(2, 2, color) - -/turf/unsimulated/floor/sky/Entered(atom/movable/AM,atom/oldloc) - . = ..() - if(!does_skyfall) - return //We don't do that - if(isobserver(AM)) - return //Don't ghostport, very annoying - if(AM.throwing) - return //Being thrown over, not fallen yet - if(!(AM.can_fall())) - return // Phased shifted kin should not fall - if(istype(AM, /obj/item/projectile)) - return // pewpew should not fall out of the sky. pew. - if(istype(AM, /obj/effect/projectile)) - return // ...neither should the effects be falling - - var/mob/living/L - if(isliving(AM)) - L = AM - if(L.is_floating) - return //Flyers/nograv can ignore it - - do_fall(AM) - -/turf/unsimulated/floor/sky/hitby(var/atom/movable/AM, var/speed) - . = ..() - - if(!does_skyfall) - return //We don't do that - - do_fall(AM) - -/turf/unsimulated/floor/sky/proc/do_fall(atom/movable/AM) - //Bye - var/attempts = 100 - var/turf/simulated/T - while(attempts && !T) - var/turf/simulated/candidate = locate(rand(5,world.maxx-5),rand(5,world.maxy-5),pick(skyfall_levels)) - if(candidate.density) - attempts-- - continue - - T = candidate - break - - if(!T) - return - - AM.forceMove(T) - if(isliving(AM)) - var/mob/living/L = AM - message_admins("\The [AM] fell out of the sky.") - L.fall_impact(T, 42, 90, FALSE, TRUE) //You will not be defibbed from this. +/////////////////// +// Generic skyfall turf +// Really only works well if the map doesn't have 'indoor' areas otherwise they can fall into one. +// TODO: Fix that. +/turf/unsimulated/floor/sky + name = "the sky" + desc = "It's the sky! Be careful!" + icon = 'icons/turf/floors.dmi' + icon_state = "sky_slow" + dir = SOUTH + initialized = FALSE + var/does_skyfall = TRUE + var/list/skyfall_levels + +/turf/unsimulated/floor/sky/Initialize() + . = ..() + if(does_skyfall && !LAZYLEN(skyfall_levels)) + error("[x],[y],[z], [get_area(src)] doesn't have skyfall_levels defined! Can't skyfall!") + if(locate(/turf/simulated) in orange(src,1)) + set_light(2, 2, color) + +/turf/unsimulated/floor/sky/Entered(atom/movable/AM,atom/oldloc) + . = ..() + if(!does_skyfall) + return //We don't do that + if(isobserver(AM)) + return //Don't ghostport, very annoying + if(AM.throwing) + return //Being thrown over, not fallen yet + if(!(AM.can_fall())) + return // Phased shifted kin should not fall + if(istype(AM, /obj/item/projectile)) + return // pewpew should not fall out of the sky. pew. + if(istype(AM, /obj/effect/projectile)) + return // ...neither should the effects be falling + + var/mob/living/L + if(isliving(AM)) + L = AM + if(L.is_floating) + return //Flyers/nograv can ignore it + + do_fall(AM) + +/turf/unsimulated/floor/sky/hitby(var/atom/movable/AM, var/speed) + . = ..() + + if(!does_skyfall) + return //We don't do that + + do_fall(AM) + +/turf/unsimulated/floor/sky/proc/do_fall(atom/movable/AM) + //Bye + var/attempts = 100 + var/turf/simulated/T + while(attempts && !T) + var/turf/simulated/candidate = locate(rand(5,world.maxx-5),rand(5,world.maxy-5),pick(skyfall_levels)) + if(candidate.density) + attempts-- + continue + + T = candidate + break + + if(!T) + return + + AM.forceMove(T) + if(isliving(AM)) + var/mob/living/L = AM + message_admins("\The [AM] fell out of the sky.") + L.fall_impact(T, 42, 90, FALSE, TRUE) //You will not be defibbed from this. diff --git a/code/game/turfs/unsimulated/walls.dm b/code/game/turfs/unsimulated/walls.dm index dce3c345bb7..674619158ae 100644 --- a/code/game/turfs/unsimulated/walls.dm +++ b/code/game/turfs/unsimulated/walls.dm @@ -1,27 +1,27 @@ -/turf/unsimulated/wall - name = "wall" - icon = 'icons/turf/walls.dmi' - icon_state = "riveted" - opacity = 1 - density = TRUE - blocks_air = TRUE - -//other set - for map building -/turf/unsimulated/wall/wall1 - icon_state = "riveted1" - -/turf/unsimulated/wall/wall2 - icon_state = "riveted2" - -/turf/unsimulated/wall/fakeglass - name = "window" - icon_state = "fakewindows" - opacity = 0 - -//other set - for map building -/turf/unsimulated/wall/fakeglass2 - icon_state = "fakewindows2" - opacity = 0 - -/turf/unsimulated/wall/other +/turf/unsimulated/wall + name = "wall" + icon = 'icons/turf/walls.dmi' + icon_state = "riveted" + opacity = 1 + density = TRUE + blocks_air = TRUE + +//other set - for map building +/turf/unsimulated/wall/wall1 + icon_state = "riveted1" + +/turf/unsimulated/wall/wall2 + icon_state = "riveted2" + +/turf/unsimulated/wall/fakeglass + name = "window" + icon_state = "fakewindows" + opacity = 0 + +//other set - for map building +/turf/unsimulated/wall/fakeglass2 + icon_state = "fakewindows2" + opacity = 0 + +/turf/unsimulated/wall/other icon_state = "r_wall" \ No newline at end of file diff --git a/code/game/vehicles/vehicle.dm b/code/game/vehicles/vehicle.dm index b7361ea7f78..9b00a557684 100644 --- a/code/game/vehicles/vehicle.dm +++ b/code/game/vehicles/vehicle.dm @@ -1,103 +1,103 @@ - - -/obj/vehicle - name = "Vehicle" - icon = 'icons/vehicles/vehicles.dmi' - density = TRUE - anchored = TRUE - unacidable = TRUE //To avoid the pilot-deleting shit that came with mechas - layer = MOB_LAYER - //var/can_move = 1 - var/mob/living/carbon/occupant = null - //var/step_in = 10 //make a step in step_in/10 sec. - //var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South. - //var/step_energy_drain = 10 - var/health = 300 //health is health - //var/deflect_chance = 10 //chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. - //the values in this list show how much damage will pass through, not how much will be absorbed. - var/list/damage_absorption = list("brute"=0.8,"fire"=1.2,"bullet"=0.9,"laser"=1,"energy"=1,"bomb"=1) - var/obj/item/weapon/cell/cell //Our power source - var/state = 0 - var/list/log = new - var/last_message = 0 - var/add_req_access = 1 - var/maint_access = 1 - //var/dna //dna-locking the mech - var/list/proc_res = list() //stores proc owners, like proc_res["functionname"] = owner reference - var/datum/effect/effect/system/spark_spread/spark_system = new - var/lights = 0 - var/lights_power = 6 - - //inner atmos //These go in airtight.dm, not all vehicles are space-faring -Agouri - //var/use_internal_tank = 0 - //var/internal_tank_valve = ONE_ATMOSPHERE - //var/obj/machinery/portable_atmospherics/canister/internal_tank - //var/datum/gas_mixture/cabin_air - //var/obj/machinery/atmospherics/portables_connector/connected_port = null - - var/obj/item/device/radio/radio = null - - var/max_temperature = 2500 - //var/internal_damage_threshold = 50 //health percentage below which internal damage is possible - var/internal_damage = 0 //contains bitflags - - var/list/operation_req_access = list()//required access level for mecha operation - var/list/internals_req_access = list(access_engine,access_robotics)//required access level to open cell compartment - - var/wreckage - - var/list/equipment = new - var/obj/selected - //var/max_equip = 3 - - - - -/obj/vehicle/Initialize() - . = ..() - icon_state += "-unmanned" - add_radio() - - spark_system.set_up(2, 0, src) - spark_system.attach(src) - add_cell() - removeVerb(/atom/movable/verb/pull) - log_message("[src.name]'s functions initialised. Work protocols active - Entering IDLE mode.") - - -//################ Helpers ########################################################### - - -/obj/vehicle/proc/removeVerb(verb_path) - verbs -= verb_path - -/obj/vehicle/proc/addVerb(verb_path) - verbs += verb_path - -/obj/vehicle/proc/add_cell(var/obj/item/weapon/cell/C=null) - if(C) - C.forceMove(src) - cell = C - return - cell = new /obj/item/weapon/cell/mech(src) - -/obj/vehicle/proc/add_radio() - radio = new(src) - radio.name = "[src] radio" - radio.icon = icon - radio.icon_state = icon_state - radio.subspace_transmission = 1 - -/obj/vehicle/proc/check_for_support() - if(locate(/obj/structure/grille, orange(1, src)) || locate(/obj/structure/lattice, orange(1, src)) || locate(/turf/simulated, orange(1, src)) || locate(/turf/unsimulated, orange(1, src))) - return 1 - else - return 0 - -//################ Logs and messages ############################################ - - -/obj/vehicle/proc/log_message(message as text,red=null) - log.len++ - log[log.len] = list("time"=world.timeofday,"message"="[red?"":null][message][red?"":null]") - return log.len + + +/obj/vehicle + name = "Vehicle" + icon = 'icons/vehicles/vehicles.dmi' + density = TRUE + anchored = TRUE + unacidable = TRUE //To avoid the pilot-deleting shit that came with mechas + layer = MOB_LAYER + //var/can_move = 1 + var/mob/living/carbon/occupant = null + //var/step_in = 10 //make a step in step_in/10 sec. + //var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South. + //var/step_energy_drain = 10 + var/health = 300 //health is health + //var/deflect_chance = 10 //chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. + //the values in this list show how much damage will pass through, not how much will be absorbed. + var/list/damage_absorption = list("brute"=0.8,"fire"=1.2,"bullet"=0.9,"laser"=1,"energy"=1,"bomb"=1) + var/obj/item/weapon/cell/cell //Our power source + var/state = 0 + var/list/log = new + var/last_message = 0 + var/add_req_access = 1 + var/maint_access = 1 + //var/dna //dna-locking the mech + var/list/proc_res = list() //stores proc owners, like proc_res["functionname"] = owner reference + var/datum/effect/effect/system/spark_spread/spark_system = new + var/lights = 0 + var/lights_power = 6 + + //inner atmos //These go in airtight.dm, not all vehicles are space-faring -Agouri + //var/use_internal_tank = 0 + //var/internal_tank_valve = ONE_ATMOSPHERE + //var/obj/machinery/portable_atmospherics/canister/internal_tank + //var/datum/gas_mixture/cabin_air + //var/obj/machinery/atmospherics/portables_connector/connected_port = null + + var/obj/item/device/radio/radio = null + + var/max_temperature = 2500 + //var/internal_damage_threshold = 50 //health percentage below which internal damage is possible + var/internal_damage = 0 //contains bitflags + + var/list/operation_req_access = list()//required access level for mecha operation + var/list/internals_req_access = list(access_engine,access_robotics)//required access level to open cell compartment + + var/wreckage + + var/list/equipment = new + var/obj/selected + //var/max_equip = 3 + + + + +/obj/vehicle/Initialize() + . = ..() + icon_state += "-unmanned" + add_radio() + + spark_system.set_up(2, 0, src) + spark_system.attach(src) + add_cell() + removeVerb(/atom/movable/verb/pull) + log_message("[src.name]'s functions initialised. Work protocols active - Entering IDLE mode.") + + +//################ Helpers ########################################################### + + +/obj/vehicle/proc/removeVerb(verb_path) + verbs -= verb_path + +/obj/vehicle/proc/addVerb(verb_path) + verbs += verb_path + +/obj/vehicle/proc/add_cell(var/obj/item/weapon/cell/C=null) + if(C) + C.forceMove(src) + cell = C + return + cell = new /obj/item/weapon/cell/mech(src) + +/obj/vehicle/proc/add_radio() + radio = new(src) + radio.name = "[src] radio" + radio.icon = icon + radio.icon_state = icon_state + radio.subspace_transmission = 1 + +/obj/vehicle/proc/check_for_support() + if(locate(/obj/structure/grille, orange(1, src)) || locate(/obj/structure/lattice, orange(1, src)) || locate(/turf/simulated, orange(1, src)) || locate(/turf/unsimulated, orange(1, src))) + return 1 + else + return 0 + +//################ Logs and messages ############################################ + + +/obj/vehicle/proc/log_message(message as text,red=null) + log.len++ + log[log.len] = list("time"=world.timeofday,"message"="[red?"":null][message][red?"":null]") + return log.len diff --git a/code/game/verbs/suicide.dm b/code/game/verbs/suicide.dm index 23181a9fdbd..3fabc757682 100644 --- a/code/game/verbs/suicide.dm +++ b/code/game/verbs/suicide.dm @@ -1,7 +1,7 @@ -/mob/living/var/suiciding = 0 - -/mob/living/verb/suicide() - set hidden = 1 - - to_chat(src, "No. Adminhelp if there is a legitimate reason, and please review our server rules.") - message_admins("[ckey] has tried to trigger the suicide verb, but it is disabled.") +/mob/living/var/suiciding = 0 + +/mob/living/verb/suicide() + set hidden = 1 + + to_chat(src, "No. Adminhelp if there is a legitimate reason, and please review our server rules.") + message_admins("[ckey] has tried to trigger the suicide verb, but it is disabled.") diff --git a/code/js/byjax.dm b/code/js/byjax.dm index bf9d7789891..18b8180214d 100644 --- a/code/js/byjax.dm +++ b/code/js/byjax.dm @@ -1,50 +1,50 @@ -//this function places received data into element with specified id. -var/const/js_byjax = {" - -function replaceContent() { - var args = Array.prototype.slice.call(arguments); - var id = args\[0\]; - var content = args\[1\]; - var callback = null; - if(args\[2\]){ - callback = args\[2\]; - if(args\[3\]){ - args = args.slice(3); - } - } - var parent = document.getElementById(id); - if(typeof(parent)!=='undefined' && parent!=null){ - parent.innerHTML = content?content:''; - } - if(callback && window\[callback\]){ - window\[callback\].apply(null,args); - } -} -"} - -/* -sends data to control_id:replaceContent - -receiver - mob -control_id - window id (for windows opened with browse(), it'll be "windowname.browser") -target_element - HTML element id -new_content - HTML content -callback - js function that will be called after the data is sent -callback_args - arguments for callback function - -Be sure to include required js functions in your page, or it'll raise an exception. -*/ -/proc/send_byjax(receiver, control_id, target_element, new_content=null, callback=null, list/callback_args=null) - if(receiver && target_element && control_id) // && winexists(receiver, control_id)) - var/list/argums = list(target_element, new_content) - if(callback) - argums += callback - if(callback_args) - argums += callback_args - argums = list2params(argums) -/* if(callback_args) - argums += "&[list2params(callback_args)]" -*/ - receiver << output(argums,"[control_id]:replaceContent") - return - +//this function places received data into element with specified id. +var/const/js_byjax = {" + +function replaceContent() { + var args = Array.prototype.slice.call(arguments); + var id = args\[0\]; + var content = args\[1\]; + var callback = null; + if(args\[2\]){ + callback = args\[2\]; + if(args\[3\]){ + args = args.slice(3); + } + } + var parent = document.getElementById(id); + if(typeof(parent)!=='undefined' && parent!=null){ + parent.innerHTML = content?content:''; + } + if(callback && window\[callback\]){ + window\[callback\].apply(null,args); + } +} +"} + +/* +sends data to control_id:replaceContent + +receiver - mob +control_id - window id (for windows opened with browse(), it'll be "windowname.browser") +target_element - HTML element id +new_content - HTML content +callback - js function that will be called after the data is sent +callback_args - arguments for callback function + +Be sure to include required js functions in your page, or it'll raise an exception. +*/ +/proc/send_byjax(receiver, control_id, target_element, new_content=null, callback=null, list/callback_args=null) + if(receiver && target_element && control_id) // && winexists(receiver, control_id)) + var/list/argums = list(target_element, new_content) + if(callback) + argums += callback + if(callback_args) + argums += callback_args + argums = list2params(argums) +/* if(callback_args) + argums += "&[list2params(callback_args)]" +*/ + receiver << output(argums,"[control_id]:replaceContent") + return + diff --git a/code/js/menus.dm b/code/js/menus.dm index 69ff900db67..01137ce9630 100644 --- a/code/js/menus.dm +++ b/code/js/menus.dm @@ -1,37 +1,37 @@ -var/const/js_dropdowns = {" -function dropdowns() { - var divs = document.getElementsByTagName('div'); - var headers = new Array(); - var links = new Array(); - for(var i=0;i=0) { - elem.className = elem.className.replace('visible','hidden'); - this.className = this.className.replace('open','closed'); - this.innerHTML = this.innerHTML.replace('-','+'); - } - else { - elem.className = elem.className.replace('hidden','visible'); - this.className = this.className.replace('closed','open'); - this.innerHTML = this.innerHTML.replace('+','-'); - } - return false; - } - })(links\[i\]); - } - } -} +var/const/js_dropdowns = {" +function dropdowns() { + var divs = document.getElementsByTagName('div'); + var headers = new Array(); + var links = new Array(); + for(var i=0;i=0) { + elem.className = elem.className.replace('visible','hidden'); + this.className = this.className.replace('open','closed'); + this.innerHTML = this.innerHTML.replace('-','+'); + } + else { + elem.className = elem.className.replace('hidden','visible'); + this.className = this.className.replace('closed','open'); + this.innerHTML = this.innerHTML.replace('+','-'); + } + return false; + } + })(links\[i\]); + } + } +} "} \ No newline at end of file diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm index 51e538e8310..3dd620b0c0e 100644 --- a/code/modules/admin/DB ban/functions.dm +++ b/code/modules/admin/DB ban/functions.dm @@ -1,481 +1,481 @@ - -//Either pass the mob you wish to ban in the 'banned_mob' attribute, or the banckey, banip and bancid variables. If both are passed, the mob takes priority! If a mob is not passed, banckey is the minimum that needs to be passed! banip and bancid are optional. -/datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration = -1, var/reason, var/job = "", var/rounds = 0, var/banckey = null, var/banip = null, var/bancid = null) - - if(!check_rights(R_MOD,0) && !check_rights(R_BAN)) return - - establish_db_connection() - if(!dbcon.IsConnected()) - return - - var/serverip = "[world.internet_address]:[world.port]" - var/bantype_pass = 0 - var/bantype_str - switch(bantype) - if(BANTYPE_PERMA) - bantype_str = "PERMABAN" - duration = -1 - bantype_pass = 1 - if(BANTYPE_TEMP) - bantype_str = "TEMPBAN" - bantype_pass = 1 - if(BANTYPE_JOB_PERMA) - bantype_str = "JOB_PERMABAN" - duration = -1 - bantype_pass = 1 - if(BANTYPE_JOB_TEMP) - bantype_str = "JOB_TEMPBAN" - bantype_pass = 1 - if( !bantype_pass ) return - if( !istext(reason) ) return - if( !isnum(duration) ) return - - var/ckey - var/computerid - var/ip - - if(ismob(banned_mob)) - ckey = banned_mob.ckey - if(banned_mob.client) - computerid = banned_mob.client.computer_id - ip = banned_mob.client.address - else if(banckey) - ckey = ckey(banckey) - computerid = bancid - ip = banip - - var/DBQuery/query = dbcon.NewQuery("SELECT id FROM erro_player WHERE ckey = '[ckey]'") - query.Execute() - var/validckey = 0 - if(query.NextRow()) - validckey = 1 - if(!validckey) - if(!banned_mob || (banned_mob && !IsGuestKey(banned_mob.key))) //VOREStation Edit Start. - var/confirm = tgui_alert(usr, "This ckey hasn't been seen, are you sure?", "Confirm Badmin", list("Yes", "No")) - if(confirm == "No") - return //VOREStation Edit End - - var/a_ckey - var/a_computerid - var/a_ip - - if(src.owner && istype(src.owner, /client)) - a_ckey = src.owner:ckey - a_computerid = src.owner:computer_id - a_ip = src.owner:address - - var/who - for(var/client/C in GLOB.clients) - if(!who) - who = "[C]" - else - who += ", [C]" - - var/adminwho - for(var/client/C in GLOB.admins) - if(!adminwho) - adminwho = "[C]" - else - adminwho += ", [C]" - - reason = sql_sanitize_text(reason) - - var/sql = "INSERT INTO erro_ban (`id`,`bantime`,`serverip`,`bantype`,`reason`,`job`,`duration`,`rounds`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`,`edits`,`unbanned`,`unbanned_datetime`,`unbanned_ckey`,`unbanned_computerid`,`unbanned_ip`) VALUES (null, Now(), '[serverip]', '[bantype_str]', '[reason]', '[job]', [(duration)?"[duration]":"0"], [(rounds)?"[rounds]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[ckey]', '[computerid]', '[ip]', '[a_ckey]', '[a_computerid]', '[a_ip]', '[who]', '[adminwho]', '', null, null, null, null, null)" - var/DBQuery/query_insert = dbcon.NewQuery(sql) - query_insert.Execute() - to_chat(usr, "[span_blue("Ban saved to database.")]") - message_admins("[key_name_admin(usr)] has added a [bantype_str] for [ckey] [(job)?"([job])":""] [(duration > 0)?"([duration] minutes)":""] with the reason: \"[reason]\" to the ban database.",1) - - - -/datum/admins/proc/DB_ban_unban(var/ckey, var/bantype, var/job = "") - - if(!check_rights(R_BAN)) return - - var/bantype_str - if(bantype) - var/bantype_pass = 0 - switch(bantype) - if(BANTYPE_PERMA) - bantype_str = "PERMABAN" - bantype_pass = 1 - if(BANTYPE_TEMP) - bantype_str = "TEMPBAN" - bantype_pass = 1 - if(BANTYPE_JOB_PERMA) - bantype_str = "JOB_PERMABAN" - bantype_pass = 1 - if(BANTYPE_JOB_TEMP) - bantype_str = "JOB_TEMPBAN" - bantype_pass = 1 - if(BANTYPE_ANY_FULLBAN) - bantype_str = "ANY" - bantype_pass = 1 - if( !bantype_pass ) return - - var/bantype_sql - if(bantype_str == "ANY") - bantype_sql = "(bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now() ) )" - else - bantype_sql = "bantype = '[bantype_str]'" - - var/sql = "SELECT id FROM erro_ban WHERE ckey = '[ckey]' AND [bantype_sql] AND (unbanned is null OR unbanned = false)" - if(job) - sql += " AND job = '[job]'" - - establish_db_connection() - if(!dbcon.IsConnected()) - return - - var/ban_id - var/ban_number = 0 //failsafe - - var/DBQuery/query = dbcon.NewQuery(sql) - query.Execute() - while(query.NextRow()) - ban_id = query.item[1] - ban_number++; - - if(ban_number == 0) - to_chat(usr, "[span_red("Database update failed due to no bans fitting the search criteria. If this is not a legacy ban you should contact the database admin.")]") - return - - if(ban_number > 1) - to_chat(usr, "[span_red("Database update failed due to multiple bans fitting the search criteria. Note down the ckey, job and current time and contact the database admin.")]") - return - - if(istext(ban_id)) - ban_id = text2num(ban_id) - if(!isnum(ban_id)) - to_chat(usr, "[span_red("Database update failed due to a ban ID mismatch. Contact the database admin.")]") - return - - DB_ban_unban_by_id(ban_id) - -/datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) - - if(!check_rights(R_BAN)) return - - if(!isnum(banid) || !istext(param)) - to_chat(usr, "Cancelled") - return - - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, duration, reason FROM erro_ban WHERE id = [banid]") - query.Execute() - - var/eckey = usr.ckey //Editing admin ckey - var/pckey //(banned) Player ckey - var/duration //Old duration - var/reason //Old reason - - if(query.NextRow()) - pckey = query.item[1] - duration = query.item[2] - reason = query.item[3] - else - to_chat(usr, "Invalid ban id. Contact the database admin") - return - - reason = sql_sanitize_text(reason) - var/value - - switch(param) - if("reason") - if(!value) - value = sanitize(tgui_input_text(usr, "Insert the new reason for [pckey]'s ban", "New Reason", "[reason]", null)) - value = sql_sanitize_text(value) - if(!value) - to_chat(usr, "Cancelled") - return - - var/DBQuery/update_query = dbcon.NewQuery("UPDATE erro_ban SET reason = '[value]', edits = CONCAT(edits,'- [eckey] changed ban reason from \\\"[reason]\\\" to \\\"[value]\\\"
                    ') WHERE id = [banid]") - update_query.Execute() - message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s reason from [reason] to [value]",1) - if("duration") - if(!value) - value = tgui_input_number(usr, "Insert the new duration (in minutes) for [pckey]'s ban", "New Duration", "[duration]", null) - if(!isnum(value) || !value) - to_chat(usr, "Cancelled") - return - - var/DBQuery/update_query = dbcon.NewQuery("UPDATE erro_ban SET duration = [value], edits = CONCAT(edits,'- [eckey] changed ban duration from [duration] to [value]
                    '), expiration_time = DATE_ADD(bantime, INTERVAL [value] MINUTE) WHERE id = [banid]") - message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s duration from [duration] to [value]",1) - update_query.Execute() - if("unban") - if(tgui_alert(usr, "Unban [pckey]?", "Unban?", list("Yes", "No")) == "Yes") - DB_ban_unban_by_id(banid) - return - to_chat(usr, "Cancelled") - return - -/datum/admins/proc/DB_ban_unban_by_id(var/id) - - if(!check_rights(R_BAN)) return - - var/sql = "SELECT ckey FROM erro_ban WHERE id = [id]" - - establish_db_connection() - if(!dbcon.IsConnected()) - return - - var/ban_number = 0 //failsafe - - var/pckey - var/DBQuery/query = dbcon.NewQuery(sql) - query.Execute() - while(query.NextRow()) - pckey = query.item[1] - ban_number++; - - if(ban_number == 0) - to_chat(usr, "[span_red("Database update failed due to a ban id not being present in the database.")]") - return - - if(ban_number > 1) - to_chat(usr, "[span_red("Database update failed due to multiple bans having the same ID. Contact the database admin.")]") - return - - if(!src.owner || !istype(src.owner, /client)) - return - - var/unban_ckey = src.owner:ckey - var/unban_computerid = src.owner:computer_id - var/unban_ip = src.owner:address - - var/sql_update = "UPDATE erro_ban SET unbanned = 1, unbanned_datetime = Now(), unbanned_ckey = '[unban_ckey]', unbanned_computerid = '[unban_computerid]', unbanned_ip = '[unban_ip]' WHERE id = [id]" - message_admins("[key_name_admin(usr)] has lifted [pckey]'s ban.",1) - - var/DBQuery/query_update = dbcon.NewQuery(sql_update) - query_update.Execute() - - -/client/proc/DB_ban_panel() - set category = "Admin" - set name = "Banning Panel" - set desc = "Edit admin permissions" - - if(!holder) - return - - holder.DB_ban_panel() - - -/datum/admins/proc/DB_ban_panel(var/playerckey = null, var/adminckey = null, var/playerip = null, var/playercid = null, var/dbbantype = null, var/match = null) - if(!usr.client) - return - - if(!check_rights(R_BAN)) return - - establish_db_connection() - if(!dbcon.IsConnected()) - to_chat(usr, "[span_red("Failed to establish database connection")]") - return - - var/output = "
                    " - - output += "" - - output += "" - output += "" - output += "
                    " - output += "

                    Banning panel

                    " - output += "
                    " - - output += "
                    [HrefTokenFormField()]" - output += "Add custom ban: (ONLY use this if you can't ban through any other method)" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "
                    Ban type:Ckey:
                    IP: CID:
                    Duration: Job:
                    " - output += "Reason:

                    " - output += "" - output += "
                    " - - output += "
                    " - - output += "
                    [HrefTokenFormField()]" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "
                    Search:" - output += "
                    Ckey: Admin ckey:
                    IP: CID:
                    Ban type:
                    " - output += "

                    " - output += " Match(min. 3 characters to search by key or ip, and 7 to search by cid)
                    " - output += "
                    " - output += "Please note that all jobban bans or unbans are in-effect the following round.
                    " - output += "This search shows only last 100 bans." - - if(adminckey || playerckey || playerip || playercid || dbbantype) - - adminckey = ckey(adminckey) - playerckey = ckey(playerckey) - playerip = sql_sanitize_text(playerip) - playercid = sql_sanitize_text(playercid) - - if(adminckey || playerckey || playerip || playercid || dbbantype) - - var/blcolor = "#ffeeee" //banned light - var/bdcolor = "#ffdddd" //banned dark - var/ulcolor = "#eeffee" //unbanned light - var/udcolor = "#ddffdd" //unbanned dark - var/alcolor = "#eeeeff" // auto-unbanned light - var/adcolor = "#ddddff" // auto-unbanned dark - - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - - var/adminsearch = "" - var/playersearch = "" - var/ipsearch = "" - var/cidsearch = "" - var/bantypesearch = "" - - if(!match) - if(adminckey) - adminsearch = "AND a_ckey = '[adminckey]' " - if(playerckey) - playersearch = "AND ckey = '[playerckey]' " - if(playerip) - ipsearch = "AND ip = '[playerip]' " - if(playercid) - cidsearch = "AND computerid = '[playercid]' " - else - if(adminckey && length(adminckey) >= 3) - adminsearch = "AND a_ckey LIKE '[adminckey]%' " - if(playerckey && length(playerckey) >= 3) - playersearch = "AND ckey LIKE '[playerckey]%' " - if(playerip && length(playerip) >= 3) - ipsearch = "AND ip LIKE '[playerip]%' " - if(playercid && length(playercid) >= 7) - cidsearch = "AND computerid LIKE '[playercid]%' " - - if(dbbantype) - bantypesearch = "AND bantype = " - - switch(dbbantype) - if(BANTYPE_TEMP) - bantypesearch += "'TEMPBAN' " - if(BANTYPE_JOB_PERMA) - bantypesearch += "'JOB_PERMABAN' " - if(BANTYPE_JOB_TEMP) - bantypesearch += "'JOB_TEMPBAN' " - else - bantypesearch += "'PERMABAN' " - - var/DBQuery/select_query = dbcon.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, ckey, a_ckey, unbanned, unbanned_ckey, unbanned_datetime, edits, ip, computerid FROM erro_ban WHERE 1 [playersearch] [adminsearch] [ipsearch] [cidsearch] [bantypesearch] ORDER BY bantime DESC LIMIT 100") - select_query.Execute() - - var/now = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") // MUST BE the same format as SQL gives us the dates in, and MUST be least to most specific (i.e. year, month, day not day, month, year) - - while(select_query.NextRow()) - var/banid = select_query.item[1] - var/bantime = select_query.item[2] - var/bantype = select_query.item[3] - var/reason = select_query.item[4] - var/job = select_query.item[5] - var/duration = select_query.item[6] - var/expiration = select_query.item[7] - var/ckey = select_query.item[8] - var/ackey = select_query.item[9] - var/unbanned = select_query.item[10] - var/unbanckey = select_query.item[11] - var/unbantime = select_query.item[12] - var/edits = select_query.item[13] - var/ip = select_query.item[14] - var/cid = select_query.item[15] - - // true if this ban has expired - var/auto = (bantype in list("TEMPBAN", "JOB_TEMPBAN")) && now > expiration // oh how I love ISO 8601 (ish) date strings - - var/lcolor = blcolor - var/dcolor = bdcolor - if(unbanned) - lcolor = ulcolor - dcolor = udcolor - else if(auto) - lcolor = alcolor - dcolor = adcolor - - var/typedesc ="" - switch(bantype) - if("PERMABAN") - typedesc = "PERMABAN" - if("TEMPBAN") - typedesc = "TEMPBAN
                    ([duration] minutes) [(unbanned || auto) ? "" : "(Edit)"]
                    Expires [expiration]
                    " - if("JOB_PERMABAN") - typedesc = "JOBBAN
                    ([job])" - if("JOB_TEMPBAN") - typedesc = "TEMP JOBBAN
                    ([job])
                    ([duration] minutes
                    Expires [expiration]
                    " - - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - if(edits) - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - if(unbanned) - output += "" - output += "" - output += "" - else if(auto) - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - - output += "
                    TYPECKEYTIME APPLIEDADMINOPTIONS
                    [typedesc][ckey][bantime][ackey][(unbanned || auto) ? "" : "Unban"]
                    IP: [ip]CIP: [cid]
                    Reason: [(unbanned || auto) ? "" : "(Edit)"] \"[reason]\"
                    EDITS
                    [edits]
                    UNBANNED by admin [unbanckey] on [unbantime]
                    EXPIRED at [expiration]
                     
                    " - - usr << browse(output,"window=lookupbans;size=900x700") + +//Either pass the mob you wish to ban in the 'banned_mob' attribute, or the banckey, banip and bancid variables. If both are passed, the mob takes priority! If a mob is not passed, banckey is the minimum that needs to be passed! banip and bancid are optional. +/datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration = -1, var/reason, var/job = "", var/rounds = 0, var/banckey = null, var/banip = null, var/bancid = null) + + if(!check_rights(R_MOD,0) && !check_rights(R_BAN)) return + + establish_db_connection() + if(!dbcon.IsConnected()) + return + + var/serverip = "[world.internet_address]:[world.port]" + var/bantype_pass = 0 + var/bantype_str + switch(bantype) + if(BANTYPE_PERMA) + bantype_str = "PERMABAN" + duration = -1 + bantype_pass = 1 + if(BANTYPE_TEMP) + bantype_str = "TEMPBAN" + bantype_pass = 1 + if(BANTYPE_JOB_PERMA) + bantype_str = "JOB_PERMABAN" + duration = -1 + bantype_pass = 1 + if(BANTYPE_JOB_TEMP) + bantype_str = "JOB_TEMPBAN" + bantype_pass = 1 + if( !bantype_pass ) return + if( !istext(reason) ) return + if( !isnum(duration) ) return + + var/ckey + var/computerid + var/ip + + if(ismob(banned_mob)) + ckey = banned_mob.ckey + if(banned_mob.client) + computerid = banned_mob.client.computer_id + ip = banned_mob.client.address + else if(banckey) + ckey = ckey(banckey) + computerid = bancid + ip = banip + + var/DBQuery/query = dbcon.NewQuery("SELECT id FROM erro_player WHERE ckey = '[ckey]'") + query.Execute() + var/validckey = 0 + if(query.NextRow()) + validckey = 1 + if(!validckey) + if(!banned_mob || (banned_mob && !IsGuestKey(banned_mob.key))) //VOREStation Edit Start. + var/confirm = tgui_alert(usr, "This ckey hasn't been seen, are you sure?", "Confirm Badmin", list("Yes", "No")) + if(confirm == "No") + return //VOREStation Edit End + + var/a_ckey + var/a_computerid + var/a_ip + + if(src.owner && istype(src.owner, /client)) + a_ckey = src.owner:ckey + a_computerid = src.owner:computer_id + a_ip = src.owner:address + + var/who + for(var/client/C in GLOB.clients) + if(!who) + who = "[C]" + else + who += ", [C]" + + var/adminwho + for(var/client/C in GLOB.admins) + if(!adminwho) + adminwho = "[C]" + else + adminwho += ", [C]" + + reason = sql_sanitize_text(reason) + + var/sql = "INSERT INTO erro_ban (`id`,`bantime`,`serverip`,`bantype`,`reason`,`job`,`duration`,`rounds`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`,`edits`,`unbanned`,`unbanned_datetime`,`unbanned_ckey`,`unbanned_computerid`,`unbanned_ip`) VALUES (null, Now(), '[serverip]', '[bantype_str]', '[reason]', '[job]', [(duration)?"[duration]":"0"], [(rounds)?"[rounds]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[ckey]', '[computerid]', '[ip]', '[a_ckey]', '[a_computerid]', '[a_ip]', '[who]', '[adminwho]', '', null, null, null, null, null)" + var/DBQuery/query_insert = dbcon.NewQuery(sql) + query_insert.Execute() + to_chat(usr, "[span_blue("Ban saved to database.")]") + message_admins("[key_name_admin(usr)] has added a [bantype_str] for [ckey] [(job)?"([job])":""] [(duration > 0)?"([duration] minutes)":""] with the reason: \"[reason]\" to the ban database.",1) + + + +/datum/admins/proc/DB_ban_unban(var/ckey, var/bantype, var/job = "") + + if(!check_rights(R_BAN)) return + + var/bantype_str + if(bantype) + var/bantype_pass = 0 + switch(bantype) + if(BANTYPE_PERMA) + bantype_str = "PERMABAN" + bantype_pass = 1 + if(BANTYPE_TEMP) + bantype_str = "TEMPBAN" + bantype_pass = 1 + if(BANTYPE_JOB_PERMA) + bantype_str = "JOB_PERMABAN" + bantype_pass = 1 + if(BANTYPE_JOB_TEMP) + bantype_str = "JOB_TEMPBAN" + bantype_pass = 1 + if(BANTYPE_ANY_FULLBAN) + bantype_str = "ANY" + bantype_pass = 1 + if( !bantype_pass ) return + + var/bantype_sql + if(bantype_str == "ANY") + bantype_sql = "(bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now() ) )" + else + bantype_sql = "bantype = '[bantype_str]'" + + var/sql = "SELECT id FROM erro_ban WHERE ckey = '[ckey]' AND [bantype_sql] AND (unbanned is null OR unbanned = false)" + if(job) + sql += " AND job = '[job]'" + + establish_db_connection() + if(!dbcon.IsConnected()) + return + + var/ban_id + var/ban_number = 0 //failsafe + + var/DBQuery/query = dbcon.NewQuery(sql) + query.Execute() + while(query.NextRow()) + ban_id = query.item[1] + ban_number++; + + if(ban_number == 0) + to_chat(usr, "[span_red("Database update failed due to no bans fitting the search criteria. If this is not a legacy ban you should contact the database admin.")]") + return + + if(ban_number > 1) + to_chat(usr, "[span_red("Database update failed due to multiple bans fitting the search criteria. Note down the ckey, job and current time and contact the database admin.")]") + return + + if(istext(ban_id)) + ban_id = text2num(ban_id) + if(!isnum(ban_id)) + to_chat(usr, "[span_red("Database update failed due to a ban ID mismatch. Contact the database admin.")]") + return + + DB_ban_unban_by_id(ban_id) + +/datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) + + if(!check_rights(R_BAN)) return + + if(!isnum(banid) || !istext(param)) + to_chat(usr, "Cancelled") + return + + var/DBQuery/query = dbcon.NewQuery("SELECT ckey, duration, reason FROM erro_ban WHERE id = [banid]") + query.Execute() + + var/eckey = usr.ckey //Editing admin ckey + var/pckey //(banned) Player ckey + var/duration //Old duration + var/reason //Old reason + + if(query.NextRow()) + pckey = query.item[1] + duration = query.item[2] + reason = query.item[3] + else + to_chat(usr, "Invalid ban id. Contact the database admin") + return + + reason = sql_sanitize_text(reason) + var/value + + switch(param) + if("reason") + if(!value) + value = sanitize(tgui_input_text(usr, "Insert the new reason for [pckey]'s ban", "New Reason", "[reason]", null)) + value = sql_sanitize_text(value) + if(!value) + to_chat(usr, "Cancelled") + return + + var/DBQuery/update_query = dbcon.NewQuery("UPDATE erro_ban SET reason = '[value]', edits = CONCAT(edits,'- [eckey] changed ban reason from \\\"[reason]\\\" to \\\"[value]\\\"
                    ') WHERE id = [banid]") + update_query.Execute() + message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s reason from [reason] to [value]",1) + if("duration") + if(!value) + value = tgui_input_number(usr, "Insert the new duration (in minutes) for [pckey]'s ban", "New Duration", "[duration]", null) + if(!isnum(value) || !value) + to_chat(usr, "Cancelled") + return + + var/DBQuery/update_query = dbcon.NewQuery("UPDATE erro_ban SET duration = [value], edits = CONCAT(edits,'- [eckey] changed ban duration from [duration] to [value]
                    '), expiration_time = DATE_ADD(bantime, INTERVAL [value] MINUTE) WHERE id = [banid]") + message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s duration from [duration] to [value]",1) + update_query.Execute() + if("unban") + if(tgui_alert(usr, "Unban [pckey]?", "Unban?", list("Yes", "No")) == "Yes") + DB_ban_unban_by_id(banid) + return + to_chat(usr, "Cancelled") + return + +/datum/admins/proc/DB_ban_unban_by_id(var/id) + + if(!check_rights(R_BAN)) return + + var/sql = "SELECT ckey FROM erro_ban WHERE id = [id]" + + establish_db_connection() + if(!dbcon.IsConnected()) + return + + var/ban_number = 0 //failsafe + + var/pckey + var/DBQuery/query = dbcon.NewQuery(sql) + query.Execute() + while(query.NextRow()) + pckey = query.item[1] + ban_number++; + + if(ban_number == 0) + to_chat(usr, "[span_red("Database update failed due to a ban id not being present in the database.")]") + return + + if(ban_number > 1) + to_chat(usr, "[span_red("Database update failed due to multiple bans having the same ID. Contact the database admin.")]") + return + + if(!src.owner || !istype(src.owner, /client)) + return + + var/unban_ckey = src.owner:ckey + var/unban_computerid = src.owner:computer_id + var/unban_ip = src.owner:address + + var/sql_update = "UPDATE erro_ban SET unbanned = 1, unbanned_datetime = Now(), unbanned_ckey = '[unban_ckey]', unbanned_computerid = '[unban_computerid]', unbanned_ip = '[unban_ip]' WHERE id = [id]" + message_admins("[key_name_admin(usr)] has lifted [pckey]'s ban.",1) + + var/DBQuery/query_update = dbcon.NewQuery(sql_update) + query_update.Execute() + + +/client/proc/DB_ban_panel() + set category = "Admin" + set name = "Banning Panel" + set desc = "Edit admin permissions" + + if(!holder) + return + + holder.DB_ban_panel() + + +/datum/admins/proc/DB_ban_panel(var/playerckey = null, var/adminckey = null, var/playerip = null, var/playercid = null, var/dbbantype = null, var/match = null) + if(!usr.client) + return + + if(!check_rights(R_BAN)) return + + establish_db_connection() + if(!dbcon.IsConnected()) + to_chat(usr, "[span_red("Failed to establish database connection")]") + return + + var/output = "
                    " + + output += "" + + output += "" + output += "" + output += "
                    " + output += "

                    Banning panel

                    " + output += "
                    " + + output += "
                    [HrefTokenFormField()]" + output += "Add custom ban: (ONLY use this if you can't ban through any other method)" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "
                    Ban type:Ckey:
                    IP: CID:
                    Duration: Job:
                    " + output += "Reason:

                    " + output += "" + output += "
                    " + + output += "
                    " + + output += "
                    [HrefTokenFormField()]" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "
                    Search:" + output += "
                    Ckey: Admin ckey:
                    IP: CID:
                    Ban type:
                    " + output += "

                    " + output += " Match(min. 3 characters to search by key or ip, and 7 to search by cid)
                    " + output += "
                    " + output += "Please note that all jobban bans or unbans are in-effect the following round.
                    " + output += "This search shows only last 100 bans." + + if(adminckey || playerckey || playerip || playercid || dbbantype) + + adminckey = ckey(adminckey) + playerckey = ckey(playerckey) + playerip = sql_sanitize_text(playerip) + playercid = sql_sanitize_text(playercid) + + if(adminckey || playerckey || playerip || playercid || dbbantype) + + var/blcolor = "#ffeeee" //banned light + var/bdcolor = "#ffdddd" //banned dark + var/ulcolor = "#eeffee" //unbanned light + var/udcolor = "#ddffdd" //unbanned dark + var/alcolor = "#eeeeff" // auto-unbanned light + var/adcolor = "#ddddff" // auto-unbanned dark + + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + + var/adminsearch = "" + var/playersearch = "" + var/ipsearch = "" + var/cidsearch = "" + var/bantypesearch = "" + + if(!match) + if(adminckey) + adminsearch = "AND a_ckey = '[adminckey]' " + if(playerckey) + playersearch = "AND ckey = '[playerckey]' " + if(playerip) + ipsearch = "AND ip = '[playerip]' " + if(playercid) + cidsearch = "AND computerid = '[playercid]' " + else + if(adminckey && length(adminckey) >= 3) + adminsearch = "AND a_ckey LIKE '[adminckey]%' " + if(playerckey && length(playerckey) >= 3) + playersearch = "AND ckey LIKE '[playerckey]%' " + if(playerip && length(playerip) >= 3) + ipsearch = "AND ip LIKE '[playerip]%' " + if(playercid && length(playercid) >= 7) + cidsearch = "AND computerid LIKE '[playercid]%' " + + if(dbbantype) + bantypesearch = "AND bantype = " + + switch(dbbantype) + if(BANTYPE_TEMP) + bantypesearch += "'TEMPBAN' " + if(BANTYPE_JOB_PERMA) + bantypesearch += "'JOB_PERMABAN' " + if(BANTYPE_JOB_TEMP) + bantypesearch += "'JOB_TEMPBAN' " + else + bantypesearch += "'PERMABAN' " + + var/DBQuery/select_query = dbcon.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, ckey, a_ckey, unbanned, unbanned_ckey, unbanned_datetime, edits, ip, computerid FROM erro_ban WHERE 1 [playersearch] [adminsearch] [ipsearch] [cidsearch] [bantypesearch] ORDER BY bantime DESC LIMIT 100") + select_query.Execute() + + var/now = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") // MUST BE the same format as SQL gives us the dates in, and MUST be least to most specific (i.e. year, month, day not day, month, year) + + while(select_query.NextRow()) + var/banid = select_query.item[1] + var/bantime = select_query.item[2] + var/bantype = select_query.item[3] + var/reason = select_query.item[4] + var/job = select_query.item[5] + var/duration = select_query.item[6] + var/expiration = select_query.item[7] + var/ckey = select_query.item[8] + var/ackey = select_query.item[9] + var/unbanned = select_query.item[10] + var/unbanckey = select_query.item[11] + var/unbantime = select_query.item[12] + var/edits = select_query.item[13] + var/ip = select_query.item[14] + var/cid = select_query.item[15] + + // true if this ban has expired + var/auto = (bantype in list("TEMPBAN", "JOB_TEMPBAN")) && now > expiration // oh how I love ISO 8601 (ish) date strings + + var/lcolor = blcolor + var/dcolor = bdcolor + if(unbanned) + lcolor = ulcolor + dcolor = udcolor + else if(auto) + lcolor = alcolor + dcolor = adcolor + + var/typedesc ="" + switch(bantype) + if("PERMABAN") + typedesc = "PERMABAN" + if("TEMPBAN") + typedesc = "TEMPBAN
                    ([duration] minutes) [(unbanned || auto) ? "" : "(Edit)"]
                    Expires [expiration]
                    " + if("JOB_PERMABAN") + typedesc = "JOBBAN
                    ([job])" + if("JOB_TEMPBAN") + typedesc = "TEMP JOBBAN
                    ([job])
                    ([duration] minutes
                    Expires [expiration]
                    " + + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + if(edits) + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + if(unbanned) + output += "" + output += "" + output += "" + else if(auto) + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + + output += "
                    TYPECKEYTIME APPLIEDADMINOPTIONS
                    [typedesc][ckey][bantime][ackey][(unbanned || auto) ? "" : "Unban"]
                    IP: [ip]CIP: [cid]
                    Reason: [(unbanned || auto) ? "" : "(Edit)"] \"[reason]\"
                    EDITS
                    [edits]
                    UNBANNED by admin [unbanckey] on [unbantime]
                    EXPIRED at [expiration]
                     
                    " + + usr << browse(output,"window=lookupbans;size=900x700") diff --git a/code/modules/admin/NewBan.dm b/code/modules/admin/NewBan.dm index 38639e917fd..9e5ff08a136 100644 --- a/code/modules/admin/NewBan.dm +++ b/code/modules/admin/NewBan.dm @@ -1,229 +1,229 @@ -var/CMinutes = null -var/savefile/Banlist - - -/proc/CheckBan(var/ckey, var/id, var/address) - if(!Banlist) // if Banlist cannot be located for some reason - LoadBans() // try to load the bans - if(!Banlist) // uh oh, can't find bans! - return 0 // ABORT ABORT ABORT - - . = list() - var/appeal - if(config && config.banappeals) - appeal = "\nFor more information on your ban, or to appeal, head to [config.banappeals]" - Banlist.cd = "/base" - if( "[ckey][id]" in Banlist.dir ) - Banlist.cd = "[ckey][id]" - if (Banlist["temp"]) - if (!GetExp(Banlist["minutes"])) - ClearTempbans() - return 0 - else - .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: [GetExp(Banlist["minutes"])]\nBy: [Banlist["bannedby"]][appeal]" - else - Banlist.cd = "/base/[ckey][id]" - .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: PERMANENT\nBy: [Banlist["bannedby"]][appeal]" - .["reason"] = "ckey/id" - return . - else - for (var/A in Banlist.dir) - Banlist.cd = "/base/[A]" - var/matches - if( ckey == Banlist["key"] ) - matches += "ckey" - if( id == Banlist["id"] ) - if(matches) - matches += "/" - matches += "id" - if( address == Banlist["ip"] ) - if(matches) - matches += "/" - matches += "ip" - - if(matches) - if(Banlist["temp"]) - if (!GetExp(Banlist["minutes"])) - ClearTempbans() - return 0 - else - .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: [GetExp(Banlist["minutes"])]\nBy: [Banlist["bannedby"]][appeal]" - else - .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: PERMANENT\nBy: [Banlist["bannedby"]][appeal]" - .["reason"] = matches - return . - return 0 - -/proc/UpdateTime() //No idea why i made this a proc. - CMinutes = (world.realtime / 10) / 60 - return 1 - -/hook/startup/proc/loadBans() - return LoadBans() - -/proc/LoadBans() - - Banlist = new("data/banlist.bdb") - log_admin("Loading Banlist") - - if (!length(Banlist.dir)) log_admin("Banlist is empty.") - - if (!Banlist.dir.Find("base")) - log_admin("Banlist missing base dir.") - Banlist.dir.Add("base") - Banlist.cd = "/base" - else if (Banlist.dir.Find("base")) - Banlist.cd = "/base" - - ClearTempbans() - return 1 - -/proc/ClearTempbans() - UpdateTime() - - Banlist.cd = "/base" - for (var/A in Banlist.dir) - Banlist.cd = "/base/[A]" - if (!Banlist["key"] || !Banlist["id"]) - RemoveBan(A) - log_admin("Invalid Ban.") - message_admins("Invalid Ban.") - continue - - if (!Banlist["temp"]) continue - if (CMinutes >= Banlist["minutes"]) RemoveBan(A) - - return 1 - - -/proc/AddBan(ckey, computerid, reason, bannedby, temp, minutes, address) - - var/bantimestamp - - if (temp) - UpdateTime() - bantimestamp = CMinutes + minutes - - Banlist.cd = "/base" - if ( Banlist.dir.Find("[ckey][computerid]") ) - to_chat(usr, "Ban already exists.") - return 0 - else - Banlist.dir.Add("[ckey][computerid]") - Banlist.cd = "/base/[ckey][computerid]" - Banlist["key"] << ckey - Banlist["id"] << computerid - Banlist["ip"] << address - Banlist["reason"] << reason - Banlist["bannedby"] << bannedby - Banlist["temp"] << temp - if (temp) - Banlist["minutes"] << bantimestamp - admin_action_message(bannedby, ckey, "banned", reason, temp ? minutes : -1) //VOREStation Add - return 1 - -/proc/RemoveBan(foldername) - var/key - var/id - - Banlist.cd = "/base/[foldername]" - Banlist["key"] >> key - Banlist["id"] >> id - Banlist.cd = "/base" - - if (!Banlist.dir.Remove(foldername)) return 0 - - if(!usr) - log_admin("Ban Expired: [key]") - message_admins("Ban Expired: [key]") - else - ban_unban_log_save("[key_name_admin(usr)] unbanned [key]") - log_admin("[key_name_admin(usr)] unbanned [key]") - message_admins("[key_name_admin(usr)] unbanned: [key]") - feedback_inc("ban_unban",1) - usr.client.holder.DB_ban_unban( ckey(key), BANTYPE_ANY_FULLBAN) - for (var/A in Banlist.dir) - Banlist.cd = "/base/[A]" - if (key == Banlist["key"] /*|| id == Banlist["id"]*/) - Banlist.cd = "/base" - Banlist.dir.Remove(A) - continue - admin_action_message(usr.key, key, "unbanned", "\[Unban\]", 0) //VOREStation Add - return 1 - -/proc/GetExp(minutes as num) - UpdateTime() - var/exp = minutes - CMinutes - if (exp <= 0) - return 0 - else - var/timeleftstring - if (exp >= 1440) //1440 = 1 day in minutes - timeleftstring = "[round(exp / 1440, 0.1)] Days" - else if (exp >= 60) //60 = 1 hour in minutes - timeleftstring = "[round(exp / 60, 0.1)] Hours" - else - timeleftstring = "[exp] Minutes" - return timeleftstring - -/datum/admins/proc/unbanpanel() - var/count = 0 - var/dat - //var/dat = "
                    Unban Player: (U) = Unban , (E) = Edit Ban (Total
                    " - Banlist.cd = "/base" - for (var/A in Banlist.dir) - count++ - Banlist.cd = "/base/[A]" - var/ref = "\ref[src]" - var/key = Banlist["key"] - var/id = Banlist["id"] - var/ip = Banlist["ip"] - var/reason = Banlist["reason"] - var/by = Banlist["bannedby"] - var/expiry - if(Banlist["temp"]) - expiry = GetExp(Banlist["minutes"]) - if(!expiry) expiry = "Removal Pending" - else expiry = "Permaban" - - dat += text("") - - dat += "
                    (U)(E) Key: [key]ComputerID: [id]IP: [ip] [expiry](By: [by])(Reason: [reason])
                    " - dat = "
                    Bans: (U) = Unban , (E) = Edit Ban - ([count] Bans)
                    [dat]" - usr << browse(dat, "window=unbanp;size=875x400") - -//////////////////////////////////// DEBUG //////////////////////////////////// - -/proc/CreateBans() - - UpdateTime() - - var/i - var/last - - for(i=0, i<1001, i++) - var/a = pick(1,0) - var/b = pick(1,0) - if(b) - Banlist.cd = "/base" - Banlist.dir.Add("trash[i]trashid[i]") - Banlist.cd = "/base/trash[i]trashid[i]" - to_chat(Banlist["key"], "trash[i]") - else - Banlist.cd = "/base" - Banlist.dir.Add("[last]trashid[i]") - Banlist.cd = "/base/[last]trashid[i]" - Banlist["key"] << last - to_chat(Banlist["id"], "trashid[i]") - to_chat(Banlist["reason"], "Trashban[i].") - Banlist["temp"] << a - Banlist["minutes"] << CMinutes + rand(1,2000) - to_chat(Banlist["bannedby"], "trashmin") - last = "trash[i]" - - Banlist.cd = "/base" - -/proc/ClearAllBans() - Banlist.cd = "/base" - for (var/A in Banlist.dir) - RemoveBan(A) +var/CMinutes = null +var/savefile/Banlist + + +/proc/CheckBan(var/ckey, var/id, var/address) + if(!Banlist) // if Banlist cannot be located for some reason + LoadBans() // try to load the bans + if(!Banlist) // uh oh, can't find bans! + return 0 // ABORT ABORT ABORT + + . = list() + var/appeal + if(config && config.banappeals) + appeal = "\nFor more information on your ban, or to appeal, head to [config.banappeals]" + Banlist.cd = "/base" + if( "[ckey][id]" in Banlist.dir ) + Banlist.cd = "[ckey][id]" + if (Banlist["temp"]) + if (!GetExp(Banlist["minutes"])) + ClearTempbans() + return 0 + else + .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: [GetExp(Banlist["minutes"])]\nBy: [Banlist["bannedby"]][appeal]" + else + Banlist.cd = "/base/[ckey][id]" + .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: PERMANENT\nBy: [Banlist["bannedby"]][appeal]" + .["reason"] = "ckey/id" + return . + else + for (var/A in Banlist.dir) + Banlist.cd = "/base/[A]" + var/matches + if( ckey == Banlist["key"] ) + matches += "ckey" + if( id == Banlist["id"] ) + if(matches) + matches += "/" + matches += "id" + if( address == Banlist["ip"] ) + if(matches) + matches += "/" + matches += "ip" + + if(matches) + if(Banlist["temp"]) + if (!GetExp(Banlist["minutes"])) + ClearTempbans() + return 0 + else + .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: [GetExp(Banlist["minutes"])]\nBy: [Banlist["bannedby"]][appeal]" + else + .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: PERMANENT\nBy: [Banlist["bannedby"]][appeal]" + .["reason"] = matches + return . + return 0 + +/proc/UpdateTime() //No idea why i made this a proc. + CMinutes = (world.realtime / 10) / 60 + return 1 + +/hook/startup/proc/loadBans() + return LoadBans() + +/proc/LoadBans() + + Banlist = new("data/banlist.bdb") + log_admin("Loading Banlist") + + if (!length(Banlist.dir)) log_admin("Banlist is empty.") + + if (!Banlist.dir.Find("base")) + log_admin("Banlist missing base dir.") + Banlist.dir.Add("base") + Banlist.cd = "/base" + else if (Banlist.dir.Find("base")) + Banlist.cd = "/base" + + ClearTempbans() + return 1 + +/proc/ClearTempbans() + UpdateTime() + + Banlist.cd = "/base" + for (var/A in Banlist.dir) + Banlist.cd = "/base/[A]" + if (!Banlist["key"] || !Banlist["id"]) + RemoveBan(A) + log_admin("Invalid Ban.") + message_admins("Invalid Ban.") + continue + + if (!Banlist["temp"]) continue + if (CMinutes >= Banlist["minutes"]) RemoveBan(A) + + return 1 + + +/proc/AddBan(ckey, computerid, reason, bannedby, temp, minutes, address) + + var/bantimestamp + + if (temp) + UpdateTime() + bantimestamp = CMinutes + minutes + + Banlist.cd = "/base" + if ( Banlist.dir.Find("[ckey][computerid]") ) + to_chat(usr, "Ban already exists.") + return 0 + else + Banlist.dir.Add("[ckey][computerid]") + Banlist.cd = "/base/[ckey][computerid]" + Banlist["key"] << ckey + Banlist["id"] << computerid + Banlist["ip"] << address + Banlist["reason"] << reason + Banlist["bannedby"] << bannedby + Banlist["temp"] << temp + if (temp) + Banlist["minutes"] << bantimestamp + admin_action_message(bannedby, ckey, "banned", reason, temp ? minutes : -1) //VOREStation Add + return 1 + +/proc/RemoveBan(foldername) + var/key + var/id + + Banlist.cd = "/base/[foldername]" + Banlist["key"] >> key + Banlist["id"] >> id + Banlist.cd = "/base" + + if (!Banlist.dir.Remove(foldername)) return 0 + + if(!usr) + log_admin("Ban Expired: [key]") + message_admins("Ban Expired: [key]") + else + ban_unban_log_save("[key_name_admin(usr)] unbanned [key]") + log_admin("[key_name_admin(usr)] unbanned [key]") + message_admins("[key_name_admin(usr)] unbanned: [key]") + feedback_inc("ban_unban",1) + usr.client.holder.DB_ban_unban( ckey(key), BANTYPE_ANY_FULLBAN) + for (var/A in Banlist.dir) + Banlist.cd = "/base/[A]" + if (key == Banlist["key"] /*|| id == Banlist["id"]*/) + Banlist.cd = "/base" + Banlist.dir.Remove(A) + continue + admin_action_message(usr.key, key, "unbanned", "\[Unban\]", 0) //VOREStation Add + return 1 + +/proc/GetExp(minutes as num) + UpdateTime() + var/exp = minutes - CMinutes + if (exp <= 0) + return 0 + else + var/timeleftstring + if (exp >= 1440) //1440 = 1 day in minutes + timeleftstring = "[round(exp / 1440, 0.1)] Days" + else if (exp >= 60) //60 = 1 hour in minutes + timeleftstring = "[round(exp / 60, 0.1)] Hours" + else + timeleftstring = "[exp] Minutes" + return timeleftstring + +/datum/admins/proc/unbanpanel() + var/count = 0 + var/dat + //var/dat = "
                    Unban Player:(U) = Unban , (E) = Edit Ban (Total
                    " + Banlist.cd = "/base" + for (var/A in Banlist.dir) + count++ + Banlist.cd = "/base/[A]" + var/ref = "\ref[src]" + var/key = Banlist["key"] + var/id = Banlist["id"] + var/ip = Banlist["ip"] + var/reason = Banlist["reason"] + var/by = Banlist["bannedby"] + var/expiry + if(Banlist["temp"]) + expiry = GetExp(Banlist["minutes"]) + if(!expiry) expiry = "Removal Pending" + else expiry = "Permaban" + + dat += text("") + + dat += "
                    (U)(E) Key: [key]ComputerID: [id]IP: [ip] [expiry](By: [by])(Reason: [reason])
                    " + dat = "
                    Bans: (U) = Unban , (E) = Edit Ban - ([count] Bans)
                    [dat]" + usr << browse(dat, "window=unbanp;size=875x400") + +//////////////////////////////////// DEBUG //////////////////////////////////// + +/proc/CreateBans() + + UpdateTime() + + var/i + var/last + + for(i=0, i<1001, i++) + var/a = pick(1,0) + var/b = pick(1,0) + if(b) + Banlist.cd = "/base" + Banlist.dir.Add("trash[i]trashid[i]") + Banlist.cd = "/base/trash[i]trashid[i]" + to_chat(Banlist["key"], "trash[i]") + else + Banlist.cd = "/base" + Banlist.dir.Add("[last]trashid[i]") + Banlist.cd = "/base/[last]trashid[i]" + Banlist["key"] << last + to_chat(Banlist["id"], "trashid[i]") + to_chat(Banlist["reason"], "Trashban[i].") + Banlist["temp"] << a + Banlist["minutes"] << CMinutes + rand(1,2000) + to_chat(Banlist["bannedby"], "trashmin") + last = "trash[i]" + + Banlist.cd = "/base" + +/proc/ClearAllBans() + Banlist.cd = "/base" + for (var/A in Banlist.dir) + RemoveBan(A) diff --git a/code/modules/admin/ToRban.dm b/code/modules/admin/ToRban.dm index 0002cb7e29e..a3a5863cec4 100644 --- a/code/modules/admin/ToRban.dm +++ b/code/modules/admin/ToRban.dm @@ -1,89 +1,89 @@ -//By Carnwennan -//fetches an external list and processes it into a list of ip addresses. -//It then stores the processed list into a savefile for later use -#define TORFILE "data/ToR_ban.bdb" -#define TOR_UPDATE_INTERVAL 216000 //~6 hours - -/proc/ToRban_isbanned(var/ip_address) - var/savefile/F = new(TORFILE) - if(F) - if( ip_address in F.dir ) - return 1 - return 0 - -/proc/ToRban_autoupdate() - var/savefile/F = new(TORFILE) - if(F) - var/last_update - F["last_update"] >> last_update - if((last_update + TOR_UPDATE_INTERVAL) < world.realtime) //we haven't updated for a while - ToRban_update() - return - -/proc/ToRban_update() - spawn(0) - log_misc("Downloading updated ToR data...") - var/http[] = world.Export("https://check.torproject.org/exit-addresses") - - var/list/rawlist = file2list(http["CONTENT"]) - if(rawlist.len) - fdel(TORFILE) - var/savefile/F = new(TORFILE) - for( var/line in rawlist ) - if(!line) continue - if( copytext(line,1,12) == "ExitAddress" ) - var/cleaned = copytext(line,13,length(line)-19) - if(!cleaned) continue - F[cleaned] << 1 - F["last_update"] << world.realtime - log_misc("ToR data updated!") - if(usr) - to_chat(usr, "ToRban updated.") - return - log_misc("ToR data update aborted: no data.") - return - -/client/proc/ToRban(task in list("update","toggle","show","remove","remove all","find")) - set name = "ToRban" - set category = "Server" - if(!holder) return - switch(task) - if("update") - ToRban_update() - if("toggle") - if(config) - if(config.ToRban) - config.ToRban = 0 - message_admins(span_red("ToR banning disabled.")) - else - config.ToRban = 1 - message_admins(span_green("ToR banning enabled.")) - if("show") - var/savefile/F = new(TORFILE) - var/dat - if( length(F.dir) ) - for( var/i=1, i<=length(F.dir), i++ ) - dat += "" - dat = "
                    #[i] [F.dir[i]]
                    [dat]
                    " - else - dat = "No addresses in list." - src << browse(dat,"window=ToRban_show") - if("remove") - var/savefile/F = new(TORFILE) - var/choice = tgui_input_list(src,"Please select an IP address to remove from the ToR banlist:","Remove ToR ban", F.dir) - if(choice) - F.dir.Remove(choice) - to_chat(src, "Address removed") - if("remove all") - to_chat(src, "[TORFILE] was [fdel(TORFILE)?"":"not "]removed.") - if("find") - var/input = tgui_input_text(src,"Please input an IP address to search for:","Find ToR ban",null) - if(input) - if(ToRban_isbanned(input)) - to_chat(src, "[span_orange("Address is a known ToR address")]") - else - to_chat(src, "Address is not a known ToR address") - return - -#undef TORFILE -#undef TOR_UPDATE_INTERVAL +//By Carnwennan +//fetches an external list and processes it into a list of ip addresses. +//It then stores the processed list into a savefile for later use +#define TORFILE "data/ToR_ban.bdb" +#define TOR_UPDATE_INTERVAL 216000 //~6 hours + +/proc/ToRban_isbanned(var/ip_address) + var/savefile/F = new(TORFILE) + if(F) + if( ip_address in F.dir ) + return 1 + return 0 + +/proc/ToRban_autoupdate() + var/savefile/F = new(TORFILE) + if(F) + var/last_update + F["last_update"] >> last_update + if((last_update + TOR_UPDATE_INTERVAL) < world.realtime) //we haven't updated for a while + ToRban_update() + return + +/proc/ToRban_update() + spawn(0) + log_misc("Downloading updated ToR data...") + var/http[] = world.Export("https://check.torproject.org/exit-addresses") + + var/list/rawlist = file2list(http["CONTENT"]) + if(rawlist.len) + fdel(TORFILE) + var/savefile/F = new(TORFILE) + for( var/line in rawlist ) + if(!line) continue + if( copytext(line,1,12) == "ExitAddress" ) + var/cleaned = copytext(line,13,length(line)-19) + if(!cleaned) continue + F[cleaned] << 1 + F["last_update"] << world.realtime + log_misc("ToR data updated!") + if(usr) + to_chat(usr, "ToRban updated.") + return + log_misc("ToR data update aborted: no data.") + return + +/client/proc/ToRban(task in list("update","toggle","show","remove","remove all","find")) + set name = "ToRban" + set category = "Server" + if(!holder) return + switch(task) + if("update") + ToRban_update() + if("toggle") + if(config) + if(config.ToRban) + config.ToRban = 0 + message_admins(span_red("ToR banning disabled.")) + else + config.ToRban = 1 + message_admins(span_green("ToR banning enabled.")) + if("show") + var/savefile/F = new(TORFILE) + var/dat + if( length(F.dir) ) + for( var/i=1, i<=length(F.dir), i++ ) + dat += "#[i] [F.dir[i]]" + dat = "[dat]
                    " + else + dat = "No addresses in list." + src << browse(dat,"window=ToRban_show") + if("remove") + var/savefile/F = new(TORFILE) + var/choice = tgui_input_list(src,"Please select an IP address to remove from the ToR banlist:","Remove ToR ban", F.dir) + if(choice) + F.dir.Remove(choice) + to_chat(src, "Address removed") + if("remove all") + to_chat(src, "[TORFILE] was [fdel(TORFILE)?"":"not "]removed.") + if("find") + var/input = tgui_input_text(src,"Please input an IP address to search for:","Find ToR ban",null) + if(input) + if(ToRban_isbanned(input)) + to_chat(src, "[span_orange("Address is a known ToR address")]") + else + to_chat(src, "Address is not a known ToR address") + return + +#undef TORFILE +#undef TOR_UPDATE_INTERVAL diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm index d1696839bcb..740378afe4a 100644 --- a/code/modules/admin/admin_ranks.dm +++ b/code/modules/admin/admin_ranks.dm @@ -1,174 +1,174 @@ -var/list/admin_ranks = list() //list of all ranks with associated rights - -//load our rank - > rights associations -/proc/load_admin_ranks() - admin_ranks.Cut() - - var/previous_rights = 0 - - //Clear profile access - for(var/A in world.GetConfig("admin")) - world.SetConfig("APP/admin", A, null) - - //load text from file - var/list/Lines = file2list("config/admin_ranks.txt") - - //process each line seperately - for(var/line in Lines) - if(!length(line)) continue - if(copytext(line,1,2) == "#") continue - - var/list/List = splittext(line,"+") - if(!List.len) continue - - var/rank = ckeyEx(List[1]) - switch(rank) - if(null,"") continue - if("Removed") continue //Reserved - - var/rights = 0 - for(var/i=2, i<=List.len, i++) - switch(ckey(List[i])) - if("@","prev") rights |= previous_rights - if("buildmode","build") rights |= R_BUILDMODE - if("admin") rights |= R_ADMIN - if("ban") rights |= R_BAN - if("fun") rights |= R_FUN - if("server") rights |= R_SERVER - if("debug") rights |= R_DEBUG - if("permissions","rights") rights |= R_PERMISSIONS - if("possess") rights |= R_POSSESS - if("stealth") rights |= R_STEALTH - if("rejuv","rejuvinate") rights |= R_REJUVINATE - if("varedit") rights |= R_VAREDIT - if("everything","host","all") rights |= (R_HOST | R_BUILDMODE | R_ADMIN | R_BAN | R_FUN | R_SERVER | R_DEBUG | R_PERMISSIONS | R_POSSESS | R_STEALTH | R_REJUVINATE | R_VAREDIT | R_SOUNDS | R_SPAWN | R_MOD| R_EVENT) - if("sound","sounds") rights |= R_SOUNDS - if("spawn","create") rights |= R_SPAWN - if("mod") rights |= R_MOD - if("event") rights |= R_EVENT - - admin_ranks[rank] = rights - previous_rights = rights - - #ifdef TESTING - var/msg = "Permission Sets Built:\n" - for(var/rank in admin_ranks) - msg += "\t[rank] - [admin_ranks[rank]]\n" - testing(msg) - #endif - -/hook/startup/proc/loadAdmins() - load_admins() - return 1 - -/proc/load_admins() - //clear the datums references - admin_datums.Cut() - for(var/client/C in GLOB.admins) - C.remove_admin_verbs() - C.holder = null - GLOB.admins.Cut() - - if(config.admin_legacy_system) - load_admin_ranks() - //Clear profile access - for(var/A in world.GetConfig("admin")) - world.SetConfig("APP/admin", A, null) - - //load text from file - var/list/Lines = file2list("config/admins.txt") - - //process each line seperately - for(var/line in Lines) - if(!length(line)) continue - if(copytext(line,1,2) == "#") continue - - //Split the line at every "-" - var/list/List = splittext(line, "-") - if(!List.len) continue - - //ckey is before the first "-" - var/ckey = ckey(List[1]) - if(!ckey) continue - - //rank follows the first "-" - var/rank = "" - if(List.len >= 2) - rank = ckeyEx(List[2]) - - //load permissions associated with this rank - var/rights = admin_ranks[rank] - - //create the admin datum and store it for later use - var/datum/admins/D = new /datum/admins(rank, rights, ckey) - - if(D.rights & R_DEBUG) //grant profile access - world.SetConfig("APP/admin", ckey, "role=admin") - - //find the client for a ckey if they are connected and associate them with the new admin datum - D.associate(GLOB.directory[ckey]) - - else - //The current admin system uses SQL - - establish_db_connection() - if(!dbcon.IsConnected()) - error("Failed to connect to database in load_admins(). Reverting to legacy system.") - log_misc("Failed to connect to database in load_admins(). Reverting to legacy system.") - config.admin_legacy_system = 1 - load_admins() - return - - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, rank, level, flags FROM erro_admin") - query.Execute() - while(query.NextRow()) - var/ckey = query.item[1] - var/rank = query.item[2] - if(rank == "Removed") continue //This person was de-adminned. They are only in the admin list for archive purposes. - - var/rights = query.item[4] - if(istext(rights)) rights = text2num(rights) - var/datum/admins/D = new /datum/admins(rank, rights, ckey) - - if(D.rights & R_DEBUG) //grant profile access - world.SetConfig("APP/admin", ckey, "role=admin") - - //find the client for a ckey if they are connected and associate them with the new admin datum - D.associate(GLOB.directory[ckey]) - if(!admin_datums) - error("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.") - log_misc("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.") - config.admin_legacy_system = 1 - load_admins() - return - - #ifdef TESTING - var/msg = "Admins Built:\n" - for(var/ckey in admin_datums) - var/rank - var/datum/admins/D = admin_datums[ckey] - if(D) rank = D.rank - msg += "\t[ckey] - [rank]\n" - testing(msg) - #endif - - -#ifdef TESTING -/client/verb/changerank(newrank in admin_ranks) - if(holder) - holder.rank = newrank - holder.rights = admin_ranks[newrank] - else - holder = new /datum/admins(newrank,admin_ranks[newrank],ckey) - remove_admin_verbs() - holder.associate(src) - -/client/verb/changerights(newrights as num) - if(holder) - holder.rights = newrights - else - holder = new /datum/admins("testing",newrights,ckey) - remove_admin_verbs() - holder.associate(src) - -#endif +var/list/admin_ranks = list() //list of all ranks with associated rights + +//load our rank - > rights associations +/proc/load_admin_ranks() + admin_ranks.Cut() + + var/previous_rights = 0 + + //Clear profile access + for(var/A in world.GetConfig("admin")) + world.SetConfig("APP/admin", A, null) + + //load text from file + var/list/Lines = file2list("config/admin_ranks.txt") + + //process each line seperately + for(var/line in Lines) + if(!length(line)) continue + if(copytext(line,1,2) == "#") continue + + var/list/List = splittext(line,"+") + if(!List.len) continue + + var/rank = ckeyEx(List[1]) + switch(rank) + if(null,"") continue + if("Removed") continue //Reserved + + var/rights = 0 + for(var/i=2, i<=List.len, i++) + switch(ckey(List[i])) + if("@","prev") rights |= previous_rights + if("buildmode","build") rights |= R_BUILDMODE + if("admin") rights |= R_ADMIN + if("ban") rights |= R_BAN + if("fun") rights |= R_FUN + if("server") rights |= R_SERVER + if("debug") rights |= R_DEBUG + if("permissions","rights") rights |= R_PERMISSIONS + if("possess") rights |= R_POSSESS + if("stealth") rights |= R_STEALTH + if("rejuv","rejuvinate") rights |= R_REJUVINATE + if("varedit") rights |= R_VAREDIT + if("everything","host","all") rights |= (R_HOST | R_BUILDMODE | R_ADMIN | R_BAN | R_FUN | R_SERVER | R_DEBUG | R_PERMISSIONS | R_POSSESS | R_STEALTH | R_REJUVINATE | R_VAREDIT | R_SOUNDS | R_SPAWN | R_MOD| R_EVENT) + if("sound","sounds") rights |= R_SOUNDS + if("spawn","create") rights |= R_SPAWN + if("mod") rights |= R_MOD + if("event") rights |= R_EVENT + + admin_ranks[rank] = rights + previous_rights = rights + + #ifdef TESTING + var/msg = "Permission Sets Built:\n" + for(var/rank in admin_ranks) + msg += "\t[rank] - [admin_ranks[rank]]\n" + testing(msg) + #endif + +/hook/startup/proc/loadAdmins() + load_admins() + return 1 + +/proc/load_admins() + //clear the datums references + admin_datums.Cut() + for(var/client/C in GLOB.admins) + C.remove_admin_verbs() + C.holder = null + GLOB.admins.Cut() + + if(config.admin_legacy_system) + load_admin_ranks() + //Clear profile access + for(var/A in world.GetConfig("admin")) + world.SetConfig("APP/admin", A, null) + + //load text from file + var/list/Lines = file2list("config/admins.txt") + + //process each line seperately + for(var/line in Lines) + if(!length(line)) continue + if(copytext(line,1,2) == "#") continue + + //Split the line at every "-" + var/list/List = splittext(line, "-") + if(!List.len) continue + + //ckey is before the first "-" + var/ckey = ckey(List[1]) + if(!ckey) continue + + //rank follows the first "-" + var/rank = "" + if(List.len >= 2) + rank = ckeyEx(List[2]) + + //load permissions associated with this rank + var/rights = admin_ranks[rank] + + //create the admin datum and store it for later use + var/datum/admins/D = new /datum/admins(rank, rights, ckey) + + if(D.rights & R_DEBUG) //grant profile access + world.SetConfig("APP/admin", ckey, "role=admin") + + //find the client for a ckey if they are connected and associate them with the new admin datum + D.associate(GLOB.directory[ckey]) + + else + //The current admin system uses SQL + + establish_db_connection() + if(!dbcon.IsConnected()) + error("Failed to connect to database in load_admins(). Reverting to legacy system.") + log_misc("Failed to connect to database in load_admins(). Reverting to legacy system.") + config.admin_legacy_system = 1 + load_admins() + return + + var/DBQuery/query = dbcon.NewQuery("SELECT ckey, rank, level, flags FROM erro_admin") + query.Execute() + while(query.NextRow()) + var/ckey = query.item[1] + var/rank = query.item[2] + if(rank == "Removed") continue //This person was de-adminned. They are only in the admin list for archive purposes. + + var/rights = query.item[4] + if(istext(rights)) rights = text2num(rights) + var/datum/admins/D = new /datum/admins(rank, rights, ckey) + + if(D.rights & R_DEBUG) //grant profile access + world.SetConfig("APP/admin", ckey, "role=admin") + + //find the client for a ckey if they are connected and associate them with the new admin datum + D.associate(GLOB.directory[ckey]) + if(!admin_datums) + error("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.") + log_misc("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.") + config.admin_legacy_system = 1 + load_admins() + return + + #ifdef TESTING + var/msg = "Admins Built:\n" + for(var/ckey in admin_datums) + var/rank + var/datum/admins/D = admin_datums[ckey] + if(D) rank = D.rank + msg += "\t[ckey] - [rank]\n" + testing(msg) + #endif + + +#ifdef TESTING +/client/verb/changerank(newrank in admin_ranks) + if(holder) + holder.rank = newrank + holder.rights = admin_ranks[newrank] + else + holder = new /datum/admins(newrank,admin_ranks[newrank],ckey) + remove_admin_verbs() + holder.associate(src) + +/client/verb/changerights(newrights as num) + if(holder) + holder.rights = newrights + else + holder = new /datum/admins("testing",newrights,ckey) + remove_admin_verbs() + holder.associate(src) + +#endif diff --git a/code/modules/admin/admin_report.dm b/code/modules/admin/admin_report.dm index e0c38345e5d..6cfb778d911 100644 --- a/code/modules/admin/admin_report.dm +++ b/code/modules/admin/admin_report.dm @@ -1,182 +1,182 @@ -// Reports are a way to notify admins of wrongdoings that happened -// while no admin was present. They work a bit similar to news, but -// they can only be read by admins and moderators. - -// a single admin report -/datum//admin_report/var - ID // the ID of the report - body // the content of the report - author // key of the author - date // date on which this was created - done // whether this was handled - - offender_key // store the key of the offender - offender_cid // store the cid of the offender - -/datum//report_topic_handler - Topic(href,href_list) - ..() - var/client/C = locate(href_list["client"]) - if(href_list["action"] == "show_reports") - C.display_admin_reports() - else if(href_list["action"] == "remove") - C.mark_report_done(text2num(href_list["ID"])) - else if(href_list["action"] == "edit") - C.edit_report(text2num(href_list["ID"])) - -var/datum/report_topic_handler/report_topic_handler - -world/New() - ..() - report_topic_handler = new - -// add a new news datums -/proc/make_report(body, author, okey, cid) - var/savefile/Reports = new("data/reports.sav") - var/list/reports - var/lastID - - Reports["reports"] >> reports - Reports["lastID"] >> lastID - - if(!reports) reports = list() - if(!lastID) lastID = 0 - - var/datum/admin_report/created = new() - created.ID = ++lastID - created.body = body - created.author = author - created.date = world.realtime - created.done = 0 - created.offender_key = okey - created.offender_cid = cid - - reports.Insert(1, created) - - Reports["reports"] << reports - Reports["lastID"] << lastID - -// load the reports from disk -/proc/load_reports() - var/savefile/Reports = new("data/reports.sav") - var/list/reports - - Reports["reports"] >> reports - - if(!reports) reports = list() - - return reports - -// check if there are any unhandled reports -/client/proc/unhandled_reports() - if(!src.holder) return 0 - var/list/reports = load_reports() - - for(var/datum/admin_report/N in reports) - if(N.done) - continue - else return 1 - - return 0 - -// checks if the player has an unhandled report against him -/client/proc/is_reported() - var/list/reports = load_reports() - - for(var/datum/admin_report/N in reports) if(!N.done) - if(N.offender_key == src.key) - return 1 - - return 0 - -// display only the reports that haven't been handled -/client/proc/display_admin_reports() - set category = "Admin" - set name = "Display Admin Reports" - if(!src.holder) return - - var/list/reports = load_reports() - - var/output = "" - if(unhandled_reports()) - // load the list of unhandled reports - for(var/datum/admin_report/N in reports) - if(N.done) - continue - output += "Reported player: [N.offender_key](CID: [N.offender_cid])
                    " - output += "Offense:[N.body]
                    " - output += "Occurred at [time2text(N.date,"MM/DD hh:mm:ss")]
                    " - output += "authored by [N.author]
                    " - output += " Flag as Handled" - if(src.key == N.author) - output += " Edit" - output += "
                    " - output += "
                    " - else - output += "Whoops, no reports!" - - usr << browse(output, "window=news;size=600x400") - - -/client/proc/Report(mob/M as mob in world) - set category = "Admin" - if(!src.holder) - return - - var/CID = "Unknown" - if(M.client) - CID = M.client.computer_id - - var/body = tgui_input_text(src.mob, "Describe in detail what you're reporting [M] for", "Report") - if(!body) return - - - make_report(body, key, M.key, CID) - - spawn(1) - display_admin_reports() - -/client/proc/mark_report_done(ID as num) - if(!src.holder || src.holder.level < 0) - return - - var/savefile/Reports = new("data/reports.sav") - var/list/reports - - Reports["reports"] >> reports - - var/datum/admin_report/found - for(var/datum/admin_report/N in reports) - if(N.ID == ID) - found = N - if(!found) - to_chat(src, "* An error occurred, sorry.") - - found.done = 1 - - Reports["reports"] << reports - - -/client/proc/edit_report(ID as num) - if(!src.holder || src.holder.level < 0) - to_chat(src, "You tried to modify the news, but you're not an admin!") - return - - var/savefile/Reports = new("data/reports.sav") - var/list/reports - - Reports["reports"] >> reports - - var/datum/admin_report/found - for(var/datum/admin_report/N in reports) - if(N.ID == ID) - found = N - if(!found) - to_chat(src, "* An error occurred, sorry.") - - var/body = tgui_input_text(src.mob, "Enter a body for the news", "Body", multiline = TRUE, prevent_enter = TRUE) - if(!body) return - - found.body = body - - Reports["reports"] << reports +// Reports are a way to notify admins of wrongdoings that happened +// while no admin was present. They work a bit similar to news, but +// they can only be read by admins and moderators. + +// a single admin report +/datum//admin_report/var + ID // the ID of the report + body // the content of the report + author // key of the author + date // date on which this was created + done // whether this was handled + + offender_key // store the key of the offender + offender_cid // store the cid of the offender + +/datum//report_topic_handler + Topic(href,href_list) + ..() + var/client/C = locate(href_list["client"]) + if(href_list["action"] == "show_reports") + C.display_admin_reports() + else if(href_list["action"] == "remove") + C.mark_report_done(text2num(href_list["ID"])) + else if(href_list["action"] == "edit") + C.edit_report(text2num(href_list["ID"])) + +var/datum/report_topic_handler/report_topic_handler + +world/New() + ..() + report_topic_handler = new + +// add a new news datums +/proc/make_report(body, author, okey, cid) + var/savefile/Reports = new("data/reports.sav") + var/list/reports + var/lastID + + Reports["reports"] >> reports + Reports["lastID"] >> lastID + + if(!reports) reports = list() + if(!lastID) lastID = 0 + + var/datum/admin_report/created = new() + created.ID = ++lastID + created.body = body + created.author = author + created.date = world.realtime + created.done = 0 + created.offender_key = okey + created.offender_cid = cid + + reports.Insert(1, created) + + Reports["reports"] << reports + Reports["lastID"] << lastID + +// load the reports from disk +/proc/load_reports() + var/savefile/Reports = new("data/reports.sav") + var/list/reports + + Reports["reports"] >> reports + + if(!reports) reports = list() + + return reports + +// check if there are any unhandled reports +/client/proc/unhandled_reports() + if(!src.holder) return 0 + var/list/reports = load_reports() + + for(var/datum/admin_report/N in reports) + if(N.done) + continue + else return 1 + + return 0 + +// checks if the player has an unhandled report against him +/client/proc/is_reported() + var/list/reports = load_reports() + + for(var/datum/admin_report/N in reports) if(!N.done) + if(N.offender_key == src.key) + return 1 + + return 0 + +// display only the reports that haven't been handled +/client/proc/display_admin_reports() + set category = "Admin" + set name = "Display Admin Reports" + if(!src.holder) return + + var/list/reports = load_reports() + + var/output = "" + if(unhandled_reports()) + // load the list of unhandled reports + for(var/datum/admin_report/N in reports) + if(N.done) + continue + output += "Reported player: [N.offender_key](CID: [N.offender_cid])
                    " + output += "Offense:[N.body]
                    " + output += "Occurred at [time2text(N.date,"MM/DD hh:mm:ss")]
                    " + output += "authored by [N.author]
                    " + output += " Flag as Handled" + if(src.key == N.author) + output += " Edit" + output += "
                    " + output += "
                    " + else + output += "Whoops, no reports!" + + usr << browse(output, "window=news;size=600x400") + + +/client/proc/Report(mob/M as mob in world) + set category = "Admin" + if(!src.holder) + return + + var/CID = "Unknown" + if(M.client) + CID = M.client.computer_id + + var/body = tgui_input_text(src.mob, "Describe in detail what you're reporting [M] for", "Report") + if(!body) return + + + make_report(body, key, M.key, CID) + + spawn(1) + display_admin_reports() + +/client/proc/mark_report_done(ID as num) + if(!src.holder || src.holder.level < 0) + return + + var/savefile/Reports = new("data/reports.sav") + var/list/reports + + Reports["reports"] >> reports + + var/datum/admin_report/found + for(var/datum/admin_report/N in reports) + if(N.ID == ID) + found = N + if(!found) + to_chat(src, "* An error occurred, sorry.") + + found.done = 1 + + Reports["reports"] << reports + + +/client/proc/edit_report(ID as num) + if(!src.holder || src.holder.level < 0) + to_chat(src, "You tried to modify the news, but you're not an admin!") + return + + var/savefile/Reports = new("data/reports.sav") + var/list/reports + + Reports["reports"] >> reports + + var/datum/admin_report/found + for(var/datum/admin_report/N in reports) + if(N.ID == ID) + found = N + if(!found) + to_chat(src, "* An error occurred, sorry.") + + var/body = tgui_input_text(src.mob, "Enter a body for the news", "Body", multiline = TRUE, prevent_enter = TRUE) + if(!body) return + + found.body = body + + Reports["reports"] << reports diff --git a/code/modules/admin/admin_secrets.dm b/code/modules/admin/admin_secrets.dm index e8ab89101c4..be0b5b8fb81 100644 --- a/code/modules/admin/admin_secrets.dm +++ b/code/modules/admin/admin_secrets.dm @@ -1,113 +1,113 @@ -var/datum/admin_secrets/admin_secrets = new() - -/datum/admin_secrets - var/list/datum/admin_secret_category/categories - var/list/datum/admin_secret_item/items - -/datum/admin_secrets/New() - ..() - categories = init_subtypes(/datum/admin_secret_category) - items = list() - var/list/category_assoc = list() - for(var/datum/admin_secret_category/category in categories) - category_assoc[category.type] = category - - for(var/item_type in subtypesof(/datum/admin_secret_item)) - var/datum/admin_secret_item/secret_item = item_type - if(!initial(secret_item.name)) - continue - - var/datum/admin_secret_item/item = new item_type() - var/datum/admin_secret_category/category = category_assoc[item.category] - dd_insertObjectList(category.items, item) - items += item - -// -// Secret Item Category - Each subtype is a category for organizing secret commands. -// -/datum/admin_secret_category - var/name = "" - var/desc = "" - var/list/datum/admin_secret_item/items = list() - -/datum/admin_secret_category/proc/can_view(var/mob/user) - for(var/datum/admin_secret_item/item in items) - if(item.can_view(user)) - return 1 - return 0 - -// -// Secret Item Datum - Each subtype is a command on the secrets panel. -// Override execute() with the implementation of the command. -// -/datum/admin_secret_item - var/name = "" - var/category = null - var/log = 1 - var/feedback = 1 - var/permissions = R_HOST - var/warn_before_use = 0 - -/datum/admin_secret_item/dd_SortValue() - return "[name]" - -/datum/admin_secret_item/proc/name() - return name - -/datum/admin_secret_item/proc/can_view(var/mob/user) - return check_rights(permissions, 0, user) - -/datum/admin_secret_item/proc/can_execute(var/mob/user) - if(can_view(user)) - if(!warn_before_use || tgui_alert(usr, "Execute the command '[name]'?", name, list("No","Yes")) == "Yes") - return 1 - return 0 - -/datum/admin_secret_item/proc/execute(var/mob/user) - if(!can_execute(user)) - return 0 - - if(log) - log_and_message_admins("used secret '[name]'", user) - if(feedback) - feedback_inc("admin_secrets_used",1) - feedback_add_details("admin_secrets_used","[name]") - return 1 - -/************************* -* Pre-defined categories * -*************************/ -/datum/admin_secret_category/admin_secrets - name = "Admin Secrets" - -/datum/admin_secret_category/random_events - name = "'Random' Events" - -/datum/admin_secret_category/fun_secrets - name = "Fun Secrets" - -/datum/admin_secret_category/final_solutions - name = "Final Solutions" - desc = "(Warning, these will end the round!)" - -/************************* -* Pre-defined base items * -*************************/ -/datum/admin_secret_item/admin_secret - category = /datum/admin_secret_category/admin_secrets - log = 0 - permissions = R_ADMIN //VOREStation Edit - -/datum/admin_secret_item/random_event - category = /datum/admin_secret_category/random_events - permissions = R_FUN //VOREStation Edit - warn_before_use = 1 - -/datum/admin_secret_item/fun_secret - category = /datum/admin_secret_category/fun_secrets - permissions = R_FUN //VOREStation Edit - warn_before_use = 1 - -/datum/admin_secret_item/final_solution - category = /datum/admin_secret_category/final_solutions - permissions = R_FUN|R_SERVER|R_ADMIN //VOREStation Edit +var/datum/admin_secrets/admin_secrets = new() + +/datum/admin_secrets + var/list/datum/admin_secret_category/categories + var/list/datum/admin_secret_item/items + +/datum/admin_secrets/New() + ..() + categories = init_subtypes(/datum/admin_secret_category) + items = list() + var/list/category_assoc = list() + for(var/datum/admin_secret_category/category in categories) + category_assoc[category.type] = category + + for(var/item_type in subtypesof(/datum/admin_secret_item)) + var/datum/admin_secret_item/secret_item = item_type + if(!initial(secret_item.name)) + continue + + var/datum/admin_secret_item/item = new item_type() + var/datum/admin_secret_category/category = category_assoc[item.category] + dd_insertObjectList(category.items, item) + items += item + +// +// Secret Item Category - Each subtype is a category for organizing secret commands. +// +/datum/admin_secret_category + var/name = "" + var/desc = "" + var/list/datum/admin_secret_item/items = list() + +/datum/admin_secret_category/proc/can_view(var/mob/user) + for(var/datum/admin_secret_item/item in items) + if(item.can_view(user)) + return 1 + return 0 + +// +// Secret Item Datum - Each subtype is a command on the secrets panel. +// Override execute() with the implementation of the command. +// +/datum/admin_secret_item + var/name = "" + var/category = null + var/log = 1 + var/feedback = 1 + var/permissions = R_HOST + var/warn_before_use = 0 + +/datum/admin_secret_item/dd_SortValue() + return "[name]" + +/datum/admin_secret_item/proc/name() + return name + +/datum/admin_secret_item/proc/can_view(var/mob/user) + return check_rights(permissions, 0, user) + +/datum/admin_secret_item/proc/can_execute(var/mob/user) + if(can_view(user)) + if(!warn_before_use || tgui_alert(usr, "Execute the command '[name]'?", name, list("No","Yes")) == "Yes") + return 1 + return 0 + +/datum/admin_secret_item/proc/execute(var/mob/user) + if(!can_execute(user)) + return 0 + + if(log) + log_and_message_admins("used secret '[name]'", user) + if(feedback) + feedback_inc("admin_secrets_used",1) + feedback_add_details("admin_secrets_used","[name]") + return 1 + +/************************* +* Pre-defined categories * +*************************/ +/datum/admin_secret_category/admin_secrets + name = "Admin Secrets" + +/datum/admin_secret_category/random_events + name = "'Random' Events" + +/datum/admin_secret_category/fun_secrets + name = "Fun Secrets" + +/datum/admin_secret_category/final_solutions + name = "Final Solutions" + desc = "(Warning, these will end the round!)" + +/************************* +* Pre-defined base items * +*************************/ +/datum/admin_secret_item/admin_secret + category = /datum/admin_secret_category/admin_secrets + log = 0 + permissions = R_ADMIN //VOREStation Edit + +/datum/admin_secret_item/random_event + category = /datum/admin_secret_category/random_events + permissions = R_FUN //VOREStation Edit + warn_before_use = 1 + +/datum/admin_secret_item/fun_secret + category = /datum/admin_secret_category/fun_secrets + permissions = R_FUN //VOREStation Edit + warn_before_use = 1 + +/datum/admin_secret_item/final_solution + category = /datum/admin_secret_category/final_solutions + permissions = R_FUN|R_SERVER|R_ADMIN //VOREStation Edit diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index d6502200fe3..295be20e517 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -1,561 +1,561 @@ -/client/proc/hide_most_verbs()//Allows you to keep some functionality while hiding some verbs - set name = "Adminverbs - Hide Most" - set category = "Admin" - - verbs.Remove(/client/proc/hide_most_verbs, admin_verbs_hideable) - verbs += /client/proc/show_verbs - - to_chat(src, "Most of your adminverbs have been hidden.") - feedback_add_details("admin_verb","HMV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/hide_verbs() - set name = "Adminverbs - Hide All" - set category = "Admin" - - remove_admin_verbs() - verbs += /client/proc/show_verbs - - to_chat(src, "Almost all of your adminverbs have been hidden.") - feedback_add_details("admin_verb","TAVVH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/show_verbs() - set name = "Adminverbs - Show" - set category = "Admin" - - verbs -= /client/proc/show_verbs - add_admin_verbs() - - to_chat(src, "All of your adminverbs are now visible.") - feedback_add_details("admin_verb","TAVVS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/admin_ghost() - set category = "Admin" - set name = "Aghost" - if(!holder) return - - var/build_mode - if(src.buildmode) - build_mode = tgui_alert(src, "You appear to be currently in buildmode. Do you want to re-enter buildmode after aghosting?", "Buildmode", list("Yes", "No")) - if(build_mode != "Yes") - to_chat(src, "Will not re-enter buildmode after switch.") - - if(istype(mob,/mob/observer/dead)) - //re-enter - var/mob/observer/dead/ghost = mob - if(ghost.can_reenter_corpse) - if(build_mode) - togglebuildmode(mob) - ghost.reenter_corpse() - if(build_mode == "Yes") - togglebuildmode(mob) - else - ghost.reenter_corpse() - else - to_chat(ghost, "Error: Aghost: Can't reenter corpse.") - return - - feedback_add_details("admin_verb","P") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - else if(istype(mob,/mob/new_player)) - to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.") - else - //ghostize - var/mob/body = mob - var/mob/observer/dead/ghost - if(build_mode) - togglebuildmode(body) - ghost = body.ghostize(1) - ghost.admin_ghosted = 1 - if(build_mode == "Yes") - togglebuildmode(ghost) - else - ghost = body.ghostize(1) - ghost.admin_ghosted = 1 - if(body) - body.teleop = ghost - if(!body.key) - body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus - feedback_add_details("admin_verb","O") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/invisimin() - set name = "Invisimin" - set category = "Admin" - set desc = "Toggles ghost-like invisibility (Don't abuse this)" - if(holder && mob) - if(mob.invisibility == INVISIBILITY_OBSERVER) - mob.invisibility = initial(mob.invisibility) - to_chat(mob, "Invisimin off. Invisibility reset.") - mob.alpha = max(mob.alpha + 100, 255) - else - mob.invisibility = INVISIBILITY_OBSERVER - to_chat(mob, "Invisimin on. You are now as invisible as a ghost.") - mob.alpha = max(mob.alpha - 100, 0) - - -/client/proc/player_panel() - set name = "Player Panel" - set category = "Admin" - if(holder) - holder.player_panel_old() - feedback_add_details("admin_verb","PP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/player_panel_new() - set name = "Player Panel New" - set category = "Admin" - if(holder) - holder.player_panel_new() - feedback_add_details("admin_verb","PPN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/check_antagonists() - set name = "Check Antagonists" - set category = "Admin" - if(holder) - holder.check_antagonists() - log_admin("[key_name(usr)] checked antagonists.") //for tsar~ - feedback_add_details("admin_verb","CHA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/jobbans() - set name = "Display Job bans" - set category = "Admin" - if(holder) - if(config.ban_legacy_system) - holder.Jobbans() - else - holder.DB_ban_panel() - feedback_add_details("admin_verb","VJB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/unban_panel() - set name = "Unban Panel" - set category = "Admin" - if(holder) - if(config.ban_legacy_system) - holder.unbanpanel() - else - holder.DB_ban_panel() - feedback_add_details("admin_verb","UBP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/game_panel() - set name = "Game Panel" - set category = "Admin" - if(holder) - holder.Game() - feedback_add_details("admin_verb","GP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/secrets() - set name = "Secrets" - set category = "Admin" - if (holder) - holder.Secrets() - feedback_add_details("admin_verb","S") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/colorooc() - set category = "Fun" - set name = "OOC Text Color" - if(!holder) return - var/response = tgui_alert(src, "Please choose a distinct color that is easy to read and doesn't mix with all the other chat and radio frequency colors.", "Change own OOC color", list("Pick new color", "Reset to default", "Cancel")) - if(response == "Pick new color") - prefs.ooccolor = input(src, "Please select your OOC colour.", "OOC colour") as color - else if(response == "Reset to default") - prefs.ooccolor = initial(prefs.ooccolor) - SScharacter_setup.queue_preferences_save(prefs) - - feedback_add_details("admin_verb","OC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/findStealthKey(txt) - if(txt) - for(var/P in GLOB.stealthminID) - if(GLOB.stealthminID[P] == txt) - return P - txt = GLOB.stealthminID[ckey] - return txt - -/client/proc/createStealthKey() - var/num = (rand(0,1000)) - var/i = 0 - while(i == 0) - i = 1 - for(var/P in GLOB.stealthminID) - if(num == GLOB.stealthminID[P]) - num++ - i = 0 - GLOB.stealthminID["[ckey]"] = "@[num2text(num)]" - -/client/proc/stealth() - set category = "Admin" - set name = "Stealth Mode" - if(holder) - if(holder.fakekey) - holder.fakekey = null - if(istype(src.mob, /mob/new_player)) - mob.name = capitalize(ckey) - else - var/new_key = ckeyEx(tgui_input_text(usr, "Enter your desired display name.", "Fake Key", key)) - if(!new_key) - return - if(length(new_key) >= 26) - new_key = copytext(new_key, 1, 26) - holder.fakekey = new_key - createStealthKey() - if(istype(mob, /mob/new_player)) - mob.name = new_key - log_and_message_admins("[key_name(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]") - feedback_add_details("admin_verb","SM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -#define MAX_WARNS 3 -#define AUTOBANTIME 10 - -/client/proc/warn(warned_ckey) - if(!check_rights(R_ADMIN)) return - - if(!warned_ckey || !istext(warned_ckey)) return - if(warned_ckey in admin_datums) - to_chat(usr, "Error: warn(): You can't warn admins.") - return - - var/datum/preferences/D - var/client/C = GLOB.directory[warned_ckey] - if(C) D = C.prefs - else D = preferences_datums[warned_ckey] - - if(!D) - to_chat(src, "Error: warn(): No such ckey found.") - return - - if(++D.warns >= MAX_WARNS) //uh ohhhh...you'reee iiiiin trouuuubble O:) - ban_unban_log_save("[ckey] warned [warned_ckey], resulting in a [AUTOBANTIME] minute autoban.") - if(C) - message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] resulting in a [AUTOBANTIME] minute ban.") - to_chat(C, "You have been autobanned due to a warning by [ckey].
                    This is a temporary ban, it will be removed in [AUTOBANTIME] minutes.
                    ") - del(C) - else - message_admins("[key_name_admin(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban.") - AddBan(warned_ckey, D.last_id, "Autobanning due to too many formal warnings", ckey, 1, AUTOBANTIME) - feedback_inc("ban_warn",1) - else - if(C) - to_chat(C, "You have been formally warned by an administrator.
                    Further warnings will result in an autoban.
                    ") - message_admins("[key_name_admin(src)] has warned [key_name_admin(C)]. They have [MAX_WARNS-D.warns] strikes remaining.") - else - message_admins("[key_name_admin(src)] has warned [warned_ckey] (DC). They have [MAX_WARNS-D.warns] strikes remaining.") - - feedback_add_details("admin_verb","WARN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -#undef MAX_WARNS -#undef AUTOBANTIME - -/client/proc/drop_bomb() // Some admin dickery that can probably be done better -- TLE - set category = "Special Verbs" - set name = "Drop Bomb" - set desc = "Cause an explosion of varying strength at your location." - - var/turf/epicenter = mob.loc - var/list/choices = list("Small Bomb", "Medium Bomb", "Big Bomb", "Custom Bomb", "Cancel") - var/choice = tgui_input_list(usr, "What size explosion would you like to produce?", "Explosion Choice", choices) - switch(choice) - if(null) - return 0 - if("Cancel") - return 0 - if("Small Bomb") - explosion(epicenter, 1, 2, 3, 3) - if("Medium Bomb") - explosion(epicenter, 2, 3, 4, 4) - if("Big Bomb") - explosion(epicenter, 3, 5, 7, 5) - if("Custom Bomb") - var/devastation_range = tgui_input_number(usr, "Devastation range (in tiles):") - var/heavy_impact_range = tgui_input_number(usr, "Heavy impact range (in tiles):") - var/light_impact_range = tgui_input_number(usr, "Light impact range (in tiles):") - var/flash_range = tgui_input_number(usr, "Flash range (in tiles):") - explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range) - message_admins(span_blue("[ckey] creating an admin explosion at [epicenter.loc].")) - feedback_add_details("admin_verb","DB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/give_disease2(mob/T as mob in mob_list) // -- Giacom - set category = "Fun" - set name = "Give Disease" - set desc = "Gives a Disease to a mob." - - var/datum/disease2/disease/D = new /datum/disease2/disease() - - var/severity = 1 - var/greater = tgui_input_list(usr, "Is this a lesser, greater, or badmin disease?", "Give Disease", list("Lesser", "Greater", "Badmin")) - switch(greater) - if ("Lesser") severity = 1 - if ("Greater") severity = 2 - if ("Badmin") severity = 99 - - D.makerandom(severity) - D.infectionchance = tgui_input_number(usr, "How virulent is this disease? (1-100)", "Give Disease", D.infectionchance) - - if(istype(T,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = T - if (H.species) - D.affected_species = list(H.species.get_bodytype()) - if(H.species.primitive_form) - D.affected_species |= H.species.primitive_form - if(H.species.greater_form) - D.affected_species |= H.species.greater_form - infect_virus2(T,D,1) - - feedback_add_details("admin_verb","GD2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] gave [key_name(T)] a [greater] disease2 with infection chance [D.infectionchance].") - message_admins(span_blue("[key_name_admin(usr)] gave [key_name(T)] a [greater] disease2 with infection chance [D.infectionchance]."), 1) - -/client/proc/admin_give_modifier(var/mob/living/L) - set category = "Debug" - set name = "Give Modifier" - set desc = "Makes a mob weaker or stronger by adding a specific modifier to them." - set popup_menu = FALSE //VOREStation Edit - Declutter. - - if(!L) - to_chat(usr, "Looks like you didn't select a mob.") - return - - var/list/possible_modifiers = subtypesof(/datum/modifier) - - var/new_modifier_type = tgui_input_list(usr, "What modifier should we add to [L]?", "Modifier Type", possible_modifiers) - if(!new_modifier_type) - return - var/duration = tgui_input_number(usr, "How long should the new modifier last, in seconds. To make it last forever, write '0'.", "Modifier Duration") - if(duration == 0) - duration = null - else - duration = duration SECONDS - - L.add_modifier(new_modifier_type, duration) - log_and_message_admins("has given [key_name(L)] the modifer [new_modifier_type], with a duration of [duration ? "[duration / 600] minutes" : "forever"].") - -/client/proc/make_sound(var/obj/O in world) // -- TLE - set category = "Special Verbs" - set name = "Make Sound" - set desc = "Display a message to everyone who can hear the target" - if(O) - var/message = sanitize(tgui_input_text(usr, "What do you want the message to be?", "Make Sound")) - if(!message) - return - O.audible_message(message) - log_admin("[key_name(usr)] made [O] at [O.x], [O.y], [O.z]. make a sound") - message_admins(span_blue("[key_name_admin(usr)] made [O] at [O.x], [O.y], [O.z]. make a sound."), 1) - feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/togglebuildmodeself() - set name = "Toggle Build Mode Self" - set category = "Special Verbs" - if(src.mob) - togglebuildmode(src.mob) - feedback_add_details("admin_verb","TBMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/object_talk(var/msg as text) // -- TLE - set category = "Special Verbs" - set name = "oSay" - set desc = "Display a message to everyone who can hear the target" - if(mob.control_object) - if(!msg) - return - for (var/mob/V in hearers(mob.control_object)) - V.show_message("[mob.control_object.name] says: \"[msg]\"", 2) - feedback_add_details("admin_verb","OT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/kill_air() // -- TLE - set category = "Debug" - set name = "Kill Air" - set desc = "Toggle Air Processing" - SSair.can_fire = !SSair.can_fire - to_chat(usr, "[SSair.can_fire ? "En" : "Dis"]abled air processing.") - feedback_add_details("admin_verb","KA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] used 'kill air'.") - message_admins(span_blue("[key_name_admin(usr)] used 'kill air'."), 1) - -/client/proc/readmin_self() - set name = "Re-Admin self" - set category = "Admin" - - if(deadmin_holder) - deadmin_holder.reassociate() - log_admin("[src] re-admined themself.") - message_admins("[src] re-admined themself.", 1) - to_chat(src, "You now have the keys to control the planet, or at least a small space station") - verbs -= /client/proc/readmin_self - -/client/proc/deadmin_self() - set name = "De-admin self" - set category = "Admin" - - if(holder) - if(tgui_alert(usr, "Confirm self-deadmin for the round? You can't re-admin yourself without someone promoting you.","Deadmin",list("Yes","No")) == "Yes") - log_admin("[src] deadmined themself.") - message_admins("[src] deadmined themself.", 1) - deadmin() - to_chat(src, "You are now a normal player.") - verbs |= /client/proc/readmin_self - feedback_add_details("admin_verb","DAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_log_hrefs() - set name = "Toggle href logging" - set category = "Server" - if(!holder) return - if(config) - config.log_hrefs = !config.log_hrefs - message_admins("[key_name_admin(usr)] [config.log_hrefs ? "started" : "stopped"] logging hrefs") - -/client/proc/check_ai_laws() - set name = "Check AI Laws" - set category = "Admin" - if(holder) - src.holder.output_ai_laws() - -/client/proc/rename_silicon() - set name = "Rename Silicon" - set category = "Admin" - - if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return - - var/mob/living/silicon/S = tgui_input_list(usr, "Select silicon.", "Rename Silicon.", silicon_mob_list) - if(!S) return - - var/new_name = sanitizeSafe(tgui_input_text(src, "Enter new name. Leave blank or as is to cancel.", "[S.real_name] - Enter new silicon name", S.real_name)) - if(new_name && new_name != S.real_name) - log_and_message_admins("has renamed the silicon '[S.real_name]' to '[new_name]'") - S.SetName(new_name) - feedback_add_details("admin_verb","RAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/manage_silicon_laws() - set name = "Manage Silicon Laws" - set category = "Admin" - - if(!check_rights(R_ADMIN|R_EVENT)) return - - var/mob/living/silicon/S = tgui_input_list(usr, "Select silicon.", "Manage Silicon Laws", silicon_mob_list) - if(!S) return - - var/datum/tgui_module/law_manager/admin/L = new(S) - L.tgui_interact(usr) - log_and_message_admins("has opened [S]'s law manager.") - feedback_add_details("admin_verb","MSL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/change_security_level() - set name = "Set security level" - set desc = "Sets the station security level" - set category = "Admin" - - if(!check_rights(R_ADMIN|R_EVENT)) return - var/sec_level = tgui_input_list(usr, "It's currently code [get_security_level()].", "Select Security Level", (list("green","yellow","violet","orange","blue","red","delta")-get_security_level())) - if(!sec_level) - return - if(tgui_alert(usr, "Switch from code [get_security_level()] to code [sec_level]?","Change security level?",list("Yes","No")) == "Yes") - set_security_level(sec_level) - log_admin("[key_name(usr)] changed the security level to code [sec_level].") - -/client/proc/shuttle_panel() - set name = "Shuttle Control Panel" - set category = "Admin" - - if(!check_rights(R_ADMIN | R_EVENT)) - return - - var/datum/tgui_module/admin_shuttle_controller/A = new(src) - A.tgui_interact(usr) - log_and_message_admins("has opened the shuttle panel.") - feedback_add_details("admin_verb","SHCP") - -//---- bs12 verbs ---- - -/client/proc/mod_panel() - set name = "Moderator Panel" - set category = "Admin" -/* if(holder) - holder.mod_panel()*/ -// feedback_add_details("admin_verb","MP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/playernotes() - set name = "Show Player Info" - set category = "Admin" - if(holder) - holder.PlayerNotes() - return - -/client/proc/free_slot() - set name = "Free Job Slot" - set category = "Admin" - if(holder) - var/list/jobs = list() - for (var/datum/job/J in job_master.occupations) - if (J.current_positions >= J.total_positions && J.total_positions != -1) - jobs += J.title - if (!jobs.len) - to_chat(usr, "There are no fully staffed jobs.") - return - var/job = tgui_input_list(usr, "Please select job slot to free", "Free job slot", jobs) - if (job) - job_master.FreeRole(job) - message_admins("A job slot for [job] has been opened by [key_name_admin(usr)]") - return - -/client/proc/toggleghostwriters() - set name = "Toggle ghost writers" - set category = "Server" - if(!holder) return - if(config) - config.cult_ghostwriter = !config.cult_ghostwriter - message_admins("Admin [key_name_admin(usr)] has [config.cult_ghostwriter ? "en" : "dis"]abled ghost writers.", 1) - -/client/proc/toggledrones() - set name = "Toggle maintenance drones" - set category = "Server" - if(!holder) return - if(config) - config.allow_drone_spawn = !config.allow_drone_spawn - message_admins("Admin [key_name_admin(usr)] has [config.allow_drone_spawn ? "en" : "dis"]abled maintenance drones.", 1) - -/client/proc/man_up(mob/T as mob in mob_list) - set category = "Fun" - set name = "Man Up" - set desc = "Tells mob to man up and deal with it." - set popup_menu = FALSE //VOREStation Edit - Declutter. - - if(tgui_alert(usr, "Are you sure you want to tell them to man up?","Confirmation",list("Deal with it","No"))=="No") return - - to_chat(T, "Man up and deal with it.") - to_chat(T, "Move along.") - - log_admin("[key_name(usr)] told [key_name(T)] to man up and deal with it.") - message_admins(span_blue("[key_name_admin(usr)] told [key_name(T)] to man up and deal with it."), 1) - -/client/proc/global_man_up() - set category = "Fun" - set name = "Man Up Global" - set desc = "Tells everyone to man up and deal with it." - - if(tgui_alert(usr, "Are you sure you want to tell the whole server up?","Confirmation",list("Deal with it","No"))=="No") return - - for (var/mob/T as mob in mob_list) - to_chat(T, "
                    Man up.
                    Deal with it.

                    Move along.

                    ") - T << 'sound/voice/ManUp1.ogg' - - log_admin("[key_name(usr)] told everyone to man up and deal with it.") - message_admins(span_blue("[key_name_admin(usr)] told everyone to man up and deal with it."), 1) - -/client/proc/give_spell(mob/T as mob in mob_list) // -- Urist - set category = "Fun" - set name = "Give Spell" - set desc = "Gives a spell to a mob." - var/spell/S = tgui_input_list(usr, "Choose the spell to give to that guy", "ABRAKADABRA", spells) - if(!S) return - T.spell_list += new S - feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] gave [key_name(T)] the spell [S].") - message_admins(span_blue("[key_name_admin(usr)] gave [key_name(T)] the spell [S]."), 1) +/client/proc/hide_most_verbs()//Allows you to keep some functionality while hiding some verbs + set name = "Adminverbs - Hide Most" + set category = "Admin" + + verbs.Remove(/client/proc/hide_most_verbs, admin_verbs_hideable) + verbs += /client/proc/show_verbs + + to_chat(src, "Most of your adminverbs have been hidden.") + feedback_add_details("admin_verb","HMV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/hide_verbs() + set name = "Adminverbs - Hide All" + set category = "Admin" + + remove_admin_verbs() + verbs += /client/proc/show_verbs + + to_chat(src, "Almost all of your adminverbs have been hidden.") + feedback_add_details("admin_verb","TAVVH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/show_verbs() + set name = "Adminverbs - Show" + set category = "Admin" + + verbs -= /client/proc/show_verbs + add_admin_verbs() + + to_chat(src, "All of your adminverbs are now visible.") + feedback_add_details("admin_verb","TAVVS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/admin_ghost() + set category = "Admin" + set name = "Aghost" + if(!holder) return + + var/build_mode + if(src.buildmode) + build_mode = tgui_alert(src, "You appear to be currently in buildmode. Do you want to re-enter buildmode after aghosting?", "Buildmode", list("Yes", "No")) + if(build_mode != "Yes") + to_chat(src, "Will not re-enter buildmode after switch.") + + if(istype(mob,/mob/observer/dead)) + //re-enter + var/mob/observer/dead/ghost = mob + if(ghost.can_reenter_corpse) + if(build_mode) + togglebuildmode(mob) + ghost.reenter_corpse() + if(build_mode == "Yes") + togglebuildmode(mob) + else + ghost.reenter_corpse() + else + to_chat(ghost, "Error: Aghost: Can't reenter corpse.") + return + + feedback_add_details("admin_verb","P") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + else if(istype(mob,/mob/new_player)) + to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.") + else + //ghostize + var/mob/body = mob + var/mob/observer/dead/ghost + if(build_mode) + togglebuildmode(body) + ghost = body.ghostize(1) + ghost.admin_ghosted = 1 + if(build_mode == "Yes") + togglebuildmode(ghost) + else + ghost = body.ghostize(1) + ghost.admin_ghosted = 1 + if(body) + body.teleop = ghost + if(!body.key) + body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus + feedback_add_details("admin_verb","O") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/invisimin() + set name = "Invisimin" + set category = "Admin" + set desc = "Toggles ghost-like invisibility (Don't abuse this)" + if(holder && mob) + if(mob.invisibility == INVISIBILITY_OBSERVER) + mob.invisibility = initial(mob.invisibility) + to_chat(mob, "Invisimin off. Invisibility reset.") + mob.alpha = max(mob.alpha + 100, 255) + else + mob.invisibility = INVISIBILITY_OBSERVER + to_chat(mob, "Invisimin on. You are now as invisible as a ghost.") + mob.alpha = max(mob.alpha - 100, 0) + + +/client/proc/player_panel() + set name = "Player Panel" + set category = "Admin" + if(holder) + holder.player_panel_old() + feedback_add_details("admin_verb","PP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/player_panel_new() + set name = "Player Panel New" + set category = "Admin" + if(holder) + holder.player_panel_new() + feedback_add_details("admin_verb","PPN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/check_antagonists() + set name = "Check Antagonists" + set category = "Admin" + if(holder) + holder.check_antagonists() + log_admin("[key_name(usr)] checked antagonists.") //for tsar~ + feedback_add_details("admin_verb","CHA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/jobbans() + set name = "Display Job bans" + set category = "Admin" + if(holder) + if(config.ban_legacy_system) + holder.Jobbans() + else + holder.DB_ban_panel() + feedback_add_details("admin_verb","VJB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/unban_panel() + set name = "Unban Panel" + set category = "Admin" + if(holder) + if(config.ban_legacy_system) + holder.unbanpanel() + else + holder.DB_ban_panel() + feedback_add_details("admin_verb","UBP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/game_panel() + set name = "Game Panel" + set category = "Admin" + if(holder) + holder.Game() + feedback_add_details("admin_verb","GP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/secrets() + set name = "Secrets" + set category = "Admin" + if (holder) + holder.Secrets() + feedback_add_details("admin_verb","S") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/colorooc() + set category = "Fun" + set name = "OOC Text Color" + if(!holder) return + var/response = tgui_alert(src, "Please choose a distinct color that is easy to read and doesn't mix with all the other chat and radio frequency colors.", "Change own OOC color", list("Pick new color", "Reset to default", "Cancel")) + if(response == "Pick new color") + prefs.ooccolor = input(src, "Please select your OOC colour.", "OOC colour") as color + else if(response == "Reset to default") + prefs.ooccolor = initial(prefs.ooccolor) + SScharacter_setup.queue_preferences_save(prefs) + + feedback_add_details("admin_verb","OC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/findStealthKey(txt) + if(txt) + for(var/P in GLOB.stealthminID) + if(GLOB.stealthminID[P] == txt) + return P + txt = GLOB.stealthminID[ckey] + return txt + +/client/proc/createStealthKey() + var/num = (rand(0,1000)) + var/i = 0 + while(i == 0) + i = 1 + for(var/P in GLOB.stealthminID) + if(num == GLOB.stealthminID[P]) + num++ + i = 0 + GLOB.stealthminID["[ckey]"] = "@[num2text(num)]" + +/client/proc/stealth() + set category = "Admin" + set name = "Stealth Mode" + if(holder) + if(holder.fakekey) + holder.fakekey = null + if(istype(src.mob, /mob/new_player)) + mob.name = capitalize(ckey) + else + var/new_key = ckeyEx(tgui_input_text(usr, "Enter your desired display name.", "Fake Key", key)) + if(!new_key) + return + if(length(new_key) >= 26) + new_key = copytext(new_key, 1, 26) + holder.fakekey = new_key + createStealthKey() + if(istype(mob, /mob/new_player)) + mob.name = new_key + log_and_message_admins("[key_name(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]") + feedback_add_details("admin_verb","SM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +#define MAX_WARNS 3 +#define AUTOBANTIME 10 + +/client/proc/warn(warned_ckey) + if(!check_rights(R_ADMIN)) return + + if(!warned_ckey || !istext(warned_ckey)) return + if(warned_ckey in admin_datums) + to_chat(usr, "Error: warn(): You can't warn admins.") + return + + var/datum/preferences/D + var/client/C = GLOB.directory[warned_ckey] + if(C) D = C.prefs + else D = preferences_datums[warned_ckey] + + if(!D) + to_chat(src, "Error: warn(): No such ckey found.") + return + + if(++D.warns >= MAX_WARNS) //uh ohhhh...you'reee iiiiin trouuuubble O:) + ban_unban_log_save("[ckey] warned [warned_ckey], resulting in a [AUTOBANTIME] minute autoban.") + if(C) + message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] resulting in a [AUTOBANTIME] minute ban.") + to_chat(C, "You have been autobanned due to a warning by [ckey].
                    This is a temporary ban, it will be removed in [AUTOBANTIME] minutes.
                    ") + del(C) + else + message_admins("[key_name_admin(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban.") + AddBan(warned_ckey, D.last_id, "Autobanning due to too many formal warnings", ckey, 1, AUTOBANTIME) + feedback_inc("ban_warn",1) + else + if(C) + to_chat(C, "You have been formally warned by an administrator.
                    Further warnings will result in an autoban.
                    ") + message_admins("[key_name_admin(src)] has warned [key_name_admin(C)]. They have [MAX_WARNS-D.warns] strikes remaining.") + else + message_admins("[key_name_admin(src)] has warned [warned_ckey] (DC). They have [MAX_WARNS-D.warns] strikes remaining.") + + feedback_add_details("admin_verb","WARN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +#undef MAX_WARNS +#undef AUTOBANTIME + +/client/proc/drop_bomb() // Some admin dickery that can probably be done better -- TLE + set category = "Special Verbs" + set name = "Drop Bomb" + set desc = "Cause an explosion of varying strength at your location." + + var/turf/epicenter = mob.loc + var/list/choices = list("Small Bomb", "Medium Bomb", "Big Bomb", "Custom Bomb", "Cancel") + var/choice = tgui_input_list(usr, "What size explosion would you like to produce?", "Explosion Choice", choices) + switch(choice) + if(null) + return 0 + if("Cancel") + return 0 + if("Small Bomb") + explosion(epicenter, 1, 2, 3, 3) + if("Medium Bomb") + explosion(epicenter, 2, 3, 4, 4) + if("Big Bomb") + explosion(epicenter, 3, 5, 7, 5) + if("Custom Bomb") + var/devastation_range = tgui_input_number(usr, "Devastation range (in tiles):") + var/heavy_impact_range = tgui_input_number(usr, "Heavy impact range (in tiles):") + var/light_impact_range = tgui_input_number(usr, "Light impact range (in tiles):") + var/flash_range = tgui_input_number(usr, "Flash range (in tiles):") + explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range) + message_admins(span_blue("[ckey] creating an admin explosion at [epicenter.loc].")) + feedback_add_details("admin_verb","DB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/give_disease2(mob/T as mob in mob_list) // -- Giacom + set category = "Fun" + set name = "Give Disease" + set desc = "Gives a Disease to a mob." + + var/datum/disease2/disease/D = new /datum/disease2/disease() + + var/severity = 1 + var/greater = tgui_input_list(usr, "Is this a lesser, greater, or badmin disease?", "Give Disease", list("Lesser", "Greater", "Badmin")) + switch(greater) + if ("Lesser") severity = 1 + if ("Greater") severity = 2 + if ("Badmin") severity = 99 + + D.makerandom(severity) + D.infectionchance = tgui_input_number(usr, "How virulent is this disease? (1-100)", "Give Disease", D.infectionchance, 100, 1) + + if(istype(T,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = T + if (H.species) + D.affected_species = list(H.species.get_bodytype()) + if(H.species.primitive_form) + D.affected_species |= H.species.primitive_form + if(H.species.greater_form) + D.affected_species |= H.species.greater_form + infect_virus2(T,D,1) + + feedback_add_details("admin_verb","GD2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] gave [key_name(T)] a [greater] disease2 with infection chance [D.infectionchance].") + message_admins(span_blue("[key_name_admin(usr)] gave [key_name(T)] a [greater] disease2 with infection chance [D.infectionchance]."), 1) + +/client/proc/admin_give_modifier(var/mob/living/L) + set category = "Debug" + set name = "Give Modifier" + set desc = "Makes a mob weaker or stronger by adding a specific modifier to them." + set popup_menu = FALSE //VOREStation Edit - Declutter. + + if(!L) + to_chat(usr, "Looks like you didn't select a mob.") + return + + var/list/possible_modifiers = subtypesof(/datum/modifier) + + var/new_modifier_type = tgui_input_list(usr, "What modifier should we add to [L]?", "Modifier Type", possible_modifiers) + if(!new_modifier_type) + return + var/duration = tgui_input_number(usr, "How long should the new modifier last, in seconds. To make it last forever, write '0'.", "Modifier Duration") + if(duration == 0) + duration = null + else + duration = duration SECONDS + + L.add_modifier(new_modifier_type, duration) + log_and_message_admins("has given [key_name(L)] the modifer [new_modifier_type], with a duration of [duration ? "[duration / 600] minutes" : "forever"].") + +/client/proc/make_sound(var/obj/O in world) // -- TLE + set category = "Special Verbs" + set name = "Make Sound" + set desc = "Display a message to everyone who can hear the target" + if(O) + var/message = sanitize(tgui_input_text(usr, "What do you want the message to be?", "Make Sound")) + if(!message) + return + O.audible_message(message) + log_admin("[key_name(usr)] made [O] at [O.x], [O.y], [O.z]. make a sound") + message_admins(span_blue("[key_name_admin(usr)] made [O] at [O.x], [O.y], [O.z]. make a sound."), 1) + feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/togglebuildmodeself() + set name = "Toggle Build Mode Self" + set category = "Special Verbs" + if(src.mob) + togglebuildmode(src.mob) + feedback_add_details("admin_verb","TBMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/object_talk(var/msg as text) // -- TLE + set category = "Special Verbs" + set name = "oSay" + set desc = "Display a message to everyone who can hear the target" + if(mob.control_object) + if(!msg) + return + for (var/mob/V in hearers(mob.control_object)) + V.show_message("[mob.control_object.name] says: \"[msg]\"", 2) + feedback_add_details("admin_verb","OT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/kill_air() // -- TLE + set category = "Debug" + set name = "Kill Air" + set desc = "Toggle Air Processing" + SSair.can_fire = !SSair.can_fire + to_chat(usr, "[SSair.can_fire ? "En" : "Dis"]abled air processing.") + feedback_add_details("admin_verb","KA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] used 'kill air'.") + message_admins(span_blue("[key_name_admin(usr)] used 'kill air'."), 1) + +/client/proc/readmin_self() + set name = "Re-Admin self" + set category = "Admin" + + if(deadmin_holder) + deadmin_holder.reassociate() + log_admin("[src] re-admined themself.") + message_admins("[src] re-admined themself.", 1) + to_chat(src, "You now have the keys to control the planet, or at least a small space station") + verbs -= /client/proc/readmin_self + +/client/proc/deadmin_self() + set name = "De-admin self" + set category = "Admin" + + if(holder) + if(tgui_alert(usr, "Confirm self-deadmin for the round? You can't re-admin yourself without someone promoting you.","Deadmin",list("Yes","No")) == "Yes") + log_admin("[src] deadmined themself.") + message_admins("[src] deadmined themself.", 1) + deadmin() + to_chat(src, "You are now a normal player.") + verbs |= /client/proc/readmin_self + feedback_add_details("admin_verb","DAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_log_hrefs() + set name = "Toggle href logging" + set category = "Server" + if(!holder) return + if(config) + config.log_hrefs = !config.log_hrefs + message_admins("[key_name_admin(usr)] [config.log_hrefs ? "started" : "stopped"] logging hrefs") + +/client/proc/check_ai_laws() + set name = "Check AI Laws" + set category = "Admin" + if(holder) + src.holder.output_ai_laws() + +/client/proc/rename_silicon() + set name = "Rename Silicon" + set category = "Admin" + + if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return + + var/mob/living/silicon/S = tgui_input_list(usr, "Select silicon.", "Rename Silicon.", silicon_mob_list) + if(!S) return + + var/new_name = sanitizeSafe(tgui_input_text(src, "Enter new name. Leave blank or as is to cancel.", "[S.real_name] - Enter new silicon name", S.real_name)) + if(new_name && new_name != S.real_name) + log_and_message_admins("has renamed the silicon '[S.real_name]' to '[new_name]'") + S.SetName(new_name) + feedback_add_details("admin_verb","RAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/manage_silicon_laws() + set name = "Manage Silicon Laws" + set category = "Admin" + + if(!check_rights(R_ADMIN|R_EVENT)) return + + var/mob/living/silicon/S = tgui_input_list(usr, "Select silicon.", "Manage Silicon Laws", silicon_mob_list) + if(!S) return + + var/datum/tgui_module/law_manager/admin/L = new(S) + L.tgui_interact(usr) + log_and_message_admins("has opened [S]'s law manager.") + feedback_add_details("admin_verb","MSL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/change_security_level() + set name = "Set security level" + set desc = "Sets the station security level" + set category = "Admin" + + if(!check_rights(R_ADMIN|R_EVENT)) return + var/sec_level = tgui_input_list(usr, "It's currently code [get_security_level()].", "Select Security Level", (list("green","yellow","violet","orange","blue","red","delta")-get_security_level())) + if(!sec_level) + return + if(tgui_alert(usr, "Switch from code [get_security_level()] to code [sec_level]?","Change security level?",list("Yes","No")) == "Yes") + set_security_level(sec_level) + log_admin("[key_name(usr)] changed the security level to code [sec_level].") + +/client/proc/shuttle_panel() + set name = "Shuttle Control Panel" + set category = "Admin" + + if(!check_rights(R_ADMIN | R_EVENT)) + return + + var/datum/tgui_module/admin_shuttle_controller/A = new(src) + A.tgui_interact(usr) + log_and_message_admins("has opened the shuttle panel.") + feedback_add_details("admin_verb","SHCP") + +//---- bs12 verbs ---- + +/client/proc/mod_panel() + set name = "Moderator Panel" + set category = "Admin" +/* if(holder) + holder.mod_panel()*/ +// feedback_add_details("admin_verb","MP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/playernotes() + set name = "Show Player Info" + set category = "Admin" + if(holder) + holder.PlayerNotes() + return + +/client/proc/free_slot() + set name = "Free Job Slot" + set category = "Admin" + if(holder) + var/list/jobs = list() + for (var/datum/job/J in job_master.occupations) + if (J.current_positions >= J.total_positions && J.total_positions != -1) + jobs += J.title + if (!jobs.len) + to_chat(usr, "There are no fully staffed jobs.") + return + var/job = tgui_input_list(usr, "Please select job slot to free", "Free job slot", jobs) + if (job) + job_master.FreeRole(job) + message_admins("A job slot for [job] has been opened by [key_name_admin(usr)]") + return + +/client/proc/toggleghostwriters() + set name = "Toggle ghost writers" + set category = "Server" + if(!holder) return + if(config) + config.cult_ghostwriter = !config.cult_ghostwriter + message_admins("Admin [key_name_admin(usr)] has [config.cult_ghostwriter ? "en" : "dis"]abled ghost writers.", 1) + +/client/proc/toggledrones() + set name = "Toggle maintenance drones" + set category = "Server" + if(!holder) return + if(config) + config.allow_drone_spawn = !config.allow_drone_spawn + message_admins("Admin [key_name_admin(usr)] has [config.allow_drone_spawn ? "en" : "dis"]abled maintenance drones.", 1) + +/client/proc/man_up(mob/T as mob in mob_list) + set category = "Fun" + set name = "Man Up" + set desc = "Tells mob to man up and deal with it." + set popup_menu = FALSE //VOREStation Edit - Declutter. + + if(tgui_alert(usr, "Are you sure you want to tell them to man up?","Confirmation",list("Deal with it","No"))=="No") return + + to_chat(T, "Man up and deal with it.") + to_chat(T, "Move along.") + + log_admin("[key_name(usr)] told [key_name(T)] to man up and deal with it.") + message_admins(span_blue("[key_name_admin(usr)] told [key_name(T)] to man up and deal with it."), 1) + +/client/proc/global_man_up() + set category = "Fun" + set name = "Man Up Global" + set desc = "Tells everyone to man up and deal with it." + + if(tgui_alert(usr, "Are you sure you want to tell the whole server up?","Confirmation",list("Deal with it","No"))=="No") return + + for (var/mob/T as mob in mob_list) + to_chat(T, "
                    Man up.
                    Deal with it.

                    Move along.

                    ") + T << 'sound/voice/ManUp1.ogg' + + log_admin("[key_name(usr)] told everyone to man up and deal with it.") + message_admins(span_blue("[key_name_admin(usr)] told everyone to man up and deal with it."), 1) + +/client/proc/give_spell(mob/T as mob in mob_list) // -- Urist + set category = "Fun" + set name = "Give Spell" + set desc = "Gives a spell to a mob." + var/spell/S = tgui_input_list(usr, "Choose the spell to give to that guy", "ABRAKADABRA", spells) + if(!S) return + T.spell_list += new S + feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] gave [key_name(T)] the spell [S].") + message_admins(span_blue("[key_name_admin(usr)] gave [key_name(T)] the spell [S]."), 1) diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm index 0bfa4e516a2..15d169fb261 100644 --- a/code/modules/admin/banjob.dm +++ b/code/modules/admin/banjob.dm @@ -1,118 +1,118 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -var/jobban_runonce // Updates legacy bans with new info -var/jobban_keylist[0] //to store the keys & ranks - -/proc/jobban_fullban(mob/M, rank, reason) - if (!M || !M.key) return - jobban_keylist.Add(text("[M.ckey] - [rank] ## [reason]")) - jobban_savebanfile() - -/proc/jobban_client_fullban(ckey, rank) - if (!ckey || !rank) return - jobban_keylist.Add(text("[ckey] - [rank]")) - jobban_savebanfile() - -//returns a reason if M is banned from rank, returns 0 otherwise -/proc/jobban_isbanned(mob/M, rank) - if(M && rank) - /* - if(_jobban_isbanned(M, rank)) return "Reason Unspecified" //for old jobban - */ - - if (guest_jobbans(rank)) - if(config.guest_jobban && IsGuestKey(M.key)) - return "Guest Job-ban" - if(config.usewhitelist && !check_whitelist(M)) - return "Whitelisted Job" - - return ckey_is_jobbanned(M.ckey, rank) - return 0 - -/proc/ckey_is_jobbanned(var/check_key, var/rank) - for(var/s in jobban_keylist) - if(findtext(s,"[check_key] - [rank]") == 1 ) - var/startpos = findtext(s, "## ")+3 - if(startpos && startpos> jobban_keylist - log_admin("Loading jobban_rank") - S["runonce"] >> jobban_runonce - - if (!length(jobban_keylist)) - jobban_keylist=list() - log_admin("jobban_keylist was empty") - else - if(!establish_db_connection()) - error("Database connection failed. Reverting to the legacy ban system.") - log_misc("Database connection failed. Reverting to the legacy ban system.") - config.ban_legacy_system = 1 - jobban_loadbanfile() - return - - //Job permabans - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, job FROM erro_ban WHERE bantype = 'JOB_PERMABAN' AND isnull(unbanned)") - query.Execute() - - while(query.NextRow()) - var/ckey = query.item[1] - var/job = query.item[2] - - jobban_keylist.Add("[ckey] - [job]") - - //Job tempbans - var/DBQuery/query1 = dbcon.NewQuery("SELECT ckey, job FROM erro_ban WHERE bantype = 'JOB_TEMPBAN' AND isnull(unbanned) AND expiration_time > Now()") - query1.Execute() - - while(query1.NextRow()) - var/ckey = query1.item[1] - var/job = query1.item[2] - - jobban_keylist.Add("[ckey] - [job]") - -/proc/jobban_savebanfile() - var/savefile/S=new("data/job_full.ban") - S["keys[0]"] << jobban_keylist - -/proc/jobban_unban(mob/M, rank) - jobban_remove("[M.ckey] - [rank]") - jobban_savebanfile() - - -/proc/ban_unban_log_save(var/formatted_log) - text2file(formatted_log,"data/ban_unban_log.txt") - - -/proc/jobban_remove(X) - for (var/i = 1; i <= length(jobban_keylist); i++) - if( findtext(jobban_keylist[i], "[X]") ) - jobban_keylist.Remove(jobban_keylist[i]) - jobban_savebanfile() - return 1 - return 0 +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +var/jobban_runonce // Updates legacy bans with new info +var/jobban_keylist[0] //to store the keys & ranks + +/proc/jobban_fullban(mob/M, rank, reason) + if (!M || !M.key) return + jobban_keylist.Add(text("[M.ckey] - [rank] ## [reason]")) + jobban_savebanfile() + +/proc/jobban_client_fullban(ckey, rank) + if (!ckey || !rank) return + jobban_keylist.Add(text("[ckey] - [rank]")) + jobban_savebanfile() + +//returns a reason if M is banned from rank, returns 0 otherwise +/proc/jobban_isbanned(mob/M, rank) + if(M && rank) + /* + if(_jobban_isbanned(M, rank)) return "Reason Unspecified" //for old jobban + */ + + if (guest_jobbans(rank)) + if(config.guest_jobban && IsGuestKey(M.key)) + return "Guest Job-ban" + if(config.usewhitelist && !check_whitelist(M)) + return "Whitelisted Job" + + return ckey_is_jobbanned(M.ckey, rank) + return 0 + +/proc/ckey_is_jobbanned(var/check_key, var/rank) + for(var/s in jobban_keylist) + if(findtext(s,"[check_key] - [rank]") == 1 ) + var/startpos = findtext(s, "## ")+3 + if(startpos && startpos> jobban_keylist + log_admin("Loading jobban_rank") + S["runonce"] >> jobban_runonce + + if (!length(jobban_keylist)) + jobban_keylist=list() + log_admin("jobban_keylist was empty") + else + if(!establish_db_connection()) + error("Database connection failed. Reverting to the legacy ban system.") + log_misc("Database connection failed. Reverting to the legacy ban system.") + config.ban_legacy_system = 1 + jobban_loadbanfile() + return + + //Job permabans + var/DBQuery/query = dbcon.NewQuery("SELECT ckey, job FROM erro_ban WHERE bantype = 'JOB_PERMABAN' AND isnull(unbanned)") + query.Execute() + + while(query.NextRow()) + var/ckey = query.item[1] + var/job = query.item[2] + + jobban_keylist.Add("[ckey] - [job]") + + //Job tempbans + var/DBQuery/query1 = dbcon.NewQuery("SELECT ckey, job FROM erro_ban WHERE bantype = 'JOB_TEMPBAN' AND isnull(unbanned) AND expiration_time > Now()") + query1.Execute() + + while(query1.NextRow()) + var/ckey = query1.item[1] + var/job = query1.item[2] + + jobban_keylist.Add("[ckey] - [job]") + +/proc/jobban_savebanfile() + var/savefile/S=new("data/job_full.ban") + S["keys[0]"] << jobban_keylist + +/proc/jobban_unban(mob/M, rank) + jobban_remove("[M.ckey] - [rank]") + jobban_savebanfile() + + +/proc/ban_unban_log_save(var/formatted_log) + text2file(formatted_log,"data/ban_unban_log.txt") + + +/proc/jobban_remove(X) + for (var/i = 1; i <= length(jobban_keylist); i++) + if( findtext(jobban_keylist[i], "[X]") ) + jobban_keylist.Remove(jobban_keylist[i]) + jobban_savebanfile() + return 1 + return 0 diff --git a/code/modules/admin/callproc/callproc.dm b/code/modules/admin/callproc/callproc.dm index b3bea8ee6c5..17909c4c294 100644 --- a/code/modules/admin/callproc/callproc.dm +++ b/code/modules/admin/callproc/callproc.dm @@ -1,210 +1,210 @@ -/client/proc/callproc() - set category = "Debug" - set name = "Advanced ProcCall" - set waitfor = 0 - - if(!check_rights(R_DEBUG)) - return - - var/datum/target = null - var/targetselected = 0 - var/returnval = null - - switch(tgui_alert(usr, "Proc owned by something?","Call Proc",list("Yes","No"))) - if("Yes") - targetselected = 1 - var/list/value = vv_get_value(default_class = VV_ATOM_REFERENCE, classes = list(VV_ATOM_REFERENCE, VV_DATUM_REFERENCE, VV_MOB_REFERENCE, VV_CLIENT)) - if (!value["class"] || !value["value"]) - return - target = value["value"] - if("No") - target = null - targetselected = 0 - - var/procname = tgui_input_text(usr, "Proc path, eg: /proc/fake_blood","Path:", null) - if(!procname) - return - - //hascall() doesn't support proc paths (eg: /proc/gib(), it only supports "gib") - var/testname = procname - if(targetselected) - //Find one of the 3 possible ways they could have written /proc/PROCNAME - if(findtext(procname, "/proc/")) - testname = replacetext(procname, "/proc/", "") - else if(findtext(procname, "/proc")) - testname = replacetext(procname, "/proc", "") - else if(findtext(procname, "proc/")) - testname = replacetext(procname, "proc/", "") - //Clear out any parenthesis if they're a dummy - testname = replacetext(testname, "()", "") - - if(targetselected && !hascall(target,testname)) - to_chat(usr, "" + span_red("Error: callproc(): type [target.type] has no proc named [procname].") + "") - return - else - var/procpath = text2path(procname) - if (!procpath) - to_chat(usr, "" + span_red("Error: callproc(): proc [procname] does not exist. (Did you forget the /proc/ part?)") + "") - return - var/list/lst = get_callproc_args() - if(!lst) - return - - if(targetselected) - if(!target) - to_chat(usr, "" + span_red("Error: callproc(): owner of proc no longer exists.") + "") - return - var/msg = "[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]." - log_admin(msg) - //message_admins(msg) //Proccall announce removed. - admin_ticket_log(target, msg) - returnval = WrapAdminProcCall(target, procname, lst) // Pass the lst as an argument list to the proc - else - //this currently has no hascall protection. wasn't able to get it working. - log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") - //message_admins("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") //Proccall announce removed. - returnval = WrapAdminProcCall(GLOBAL_PROC, procname, lst) // Pass the lst as an argument list to the proc - . = get_callproc_returnval(returnval, procname) - if(.) - to_chat(usr, .) - feedback_add_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -GLOBAL_VAR(AdminProcCaller) -GLOBAL_PROTECT(AdminProcCaller) -GLOBAL_VAR_INIT(AdminProcCallCount, 0) -GLOBAL_PROTECT(AdminProcCallCount) -GLOBAL_VAR(LastAdminCalledTargetRef) -GLOBAL_PROTECT(LastAdminCalledTargetRef) -GLOBAL_VAR(LastAdminCalledTarget) -GLOBAL_PROTECT(LastAdminCalledTarget) -GLOBAL_VAR(LastAdminCalledProc) -GLOBAL_PROTECT(LastAdminCalledProc) -GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention) -GLOBAL_PROTECT(AdminProcCallSpamPrevention) - -/proc/WrapAdminProcCall(datum/target, procname, list/arguments) - if(target && procname == "Del") - to_chat(usr, "Calling Del() is not allowed") - return - - if(target != GLOBAL_PROC && !target.CanProcCall(procname)) - to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!") - return - var/current_caller = GLOB.AdminProcCaller - var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller - if(!ckey) - CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]") - if(current_caller && current_caller != ckey) - if(!GLOB.AdminProcCallSpamPrevention[ckey]) - to_chat(usr, "Another set of admin called procs are still running, your proc will be run after theirs finish.") - GLOB.AdminProcCallSpamPrevention[ckey] = TRUE - UNTIL(!GLOB.AdminProcCaller) - to_chat(usr, "Running your proc") - GLOB.AdminProcCallSpamPrevention -= ckey - else - UNTIL(!GLOB.AdminProcCaller) - GLOB.LastAdminCalledProc = procname - if(target != GLOBAL_PROC) - GLOB.LastAdminCalledTargetRef = "\ref[target]" - GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you - ++GLOB.AdminProcCallCount - . = world.WrapAdminProcCall(target, procname, arguments) - if(--GLOB.AdminProcCallCount == 0) - GLOB.AdminProcCaller = null - -//adv proc call this, ya nerds -/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments) - if(target == GLOBAL_PROC) - return call(procname)(arglist(arguments)) - else if(target != world) - return call(target, procname)(arglist(arguments)) - else - log_admin("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]") - -/proc/IsAdminAdvancedProcCall() -#ifdef TESTING - return FALSE -#else - return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey -#endif - -/client/proc/callproc_datum(datum/A as null|area|mob|obj|turf) - set category = "Debug" - set name = "Atom ProcCall" - set waitfor = 0 - - if(!check_rights(R_DEBUG)) - return - - var/procname = tgui_input_text(usr, "Proc name, eg: fake_blood","Proc:", null) - if(!procname) - return - if(!hascall(A,procname)) - to_chat(usr, "" + span_red("Error: callproc_datum(): type [A.type] has no proc named [procname].") + "") - return - var/list/lst = get_callproc_args() - if(!lst) - return - - if(!A || !IsValidSrc(A)) - to_chat(usr, "Error: callproc_datum(): owner of proc no longer exists.") - return - var/msg = "[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]." - log_admin(msg) - //message_admins(msg) - admin_ticket_log(A, msg) - feedback_add_details("admin_verb","TPC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc - . = get_callproc_returnval(returnval,procname) - if(.) - to_chat(usr, .) - -/client/proc/get_callproc_args() - var/argnum = tgui_input_number(usr, "Number of arguments","Number:",0) - if(isnull(argnum)) - return null //Cancel - - . = list() - //var/list/named_args = list() //Named arguments are removed, due to them making proccalling take too long. - while(argnum--) - /* //Named arguments are removed, due to them making proccalling take too long. - var/named_arg = input(usr,"Leave blank for positional argument. Positional arguments will be considered as if they were added first.", "Named argument") as text|null - if(isnull(named_arg)) - return null //Cancel - */ - var/value = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) - if (!value["class"]) - return null //Cancel - /* //Named arguments are removed, due to them making proccalling take too long. - if(named_arg) - named_args[named_arg] = value["value"] - else - . += value["value"] - if(LAZYLEN(named_args)) - . += named_args - */ - . += value["value"] - -/client/proc/get_callproc_returnval(returnval,procname) - . = "" - if(islist(returnval)) - var/list/returnedlist = returnval - . = "" - if(returnedlist.len) - var/assoc_check = returnedlist[1] - if(istext(assoc_check) && (returnedlist[assoc_check] != null)) - . += "[procname] returned an associative list:" - for(var/key in returnedlist) - . += "\n[key] = [returnedlist[key]]" - - else - . += "[procname] returned a list:" - for(var/elem in returnedlist) - . += "\n[elem]" - else - . = "[procname] returned an empty list" - . += "" - - else - . = span_blue("[procname] returned: [!isnull(returnval) ? returnval : "null"]") +/client/proc/callproc() + set category = "Debug" + set name = "Advanced ProcCall" + set waitfor = 0 + + if(!check_rights(R_DEBUG)) + return + + var/datum/target = null + var/targetselected = 0 + var/returnval = null + + switch(tgui_alert(usr, "Proc owned by something?","Call Proc",list("Yes","No"))) + if("Yes") + targetselected = 1 + var/list/value = vv_get_value(default_class = VV_ATOM_REFERENCE, classes = list(VV_ATOM_REFERENCE, VV_DATUM_REFERENCE, VV_MOB_REFERENCE, VV_CLIENT)) + if (!value["class"] || !value["value"]) + return + target = value["value"] + if("No") + target = null + targetselected = 0 + + var/procname = tgui_input_text(usr, "Proc path, eg: /proc/fake_blood","Path:", null) + if(!procname) + return + + //hascall() doesn't support proc paths (eg: /proc/gib(), it only supports "gib") + var/testname = procname + if(targetselected) + //Find one of the 3 possible ways they could have written /proc/PROCNAME + if(findtext(procname, "/proc/")) + testname = replacetext(procname, "/proc/", "") + else if(findtext(procname, "/proc")) + testname = replacetext(procname, "/proc", "") + else if(findtext(procname, "proc/")) + testname = replacetext(procname, "proc/", "") + //Clear out any parenthesis if they're a dummy + testname = replacetext(testname, "()", "") + + if(targetselected && !hascall(target,testname)) + to_chat(usr, "" + span_red("Error: callproc(): type [target.type] has no proc named [procname].") + "") + return + else + var/procpath = text2path(procname) + if (!procpath) + to_chat(usr, "" + span_red("Error: callproc(): proc [procname] does not exist. (Did you forget the /proc/ part?)") + "") + return + var/list/lst = get_callproc_args() + if(!lst) + return + + if(targetselected) + if(!target) + to_chat(usr, "" + span_red("Error: callproc(): owner of proc no longer exists.") + "") + return + var/msg = "[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]." + log_admin(msg) + //message_admins(msg) //Proccall announce removed. + admin_ticket_log(target, msg) + returnval = WrapAdminProcCall(target, procname, lst) // Pass the lst as an argument list to the proc + else + //this currently has no hascall protection. wasn't able to get it working. + log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") + //message_admins("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") //Proccall announce removed. + returnval = WrapAdminProcCall(GLOBAL_PROC, procname, lst) // Pass the lst as an argument list to the proc + . = get_callproc_returnval(returnval, procname) + if(.) + to_chat(usr, .) + feedback_add_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +GLOBAL_VAR(AdminProcCaller) +GLOBAL_PROTECT(AdminProcCaller) +GLOBAL_VAR_INIT(AdminProcCallCount, 0) +GLOBAL_PROTECT(AdminProcCallCount) +GLOBAL_VAR(LastAdminCalledTargetRef) +GLOBAL_PROTECT(LastAdminCalledTargetRef) +GLOBAL_VAR(LastAdminCalledTarget) +GLOBAL_PROTECT(LastAdminCalledTarget) +GLOBAL_VAR(LastAdminCalledProc) +GLOBAL_PROTECT(LastAdminCalledProc) +GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention) +GLOBAL_PROTECT(AdminProcCallSpamPrevention) + +/proc/WrapAdminProcCall(datum/target, procname, list/arguments) + if(target && procname == "Del") + to_chat(usr, "Calling Del() is not allowed") + return + + if(target != GLOBAL_PROC && !target.CanProcCall(procname)) + to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!") + return + var/current_caller = GLOB.AdminProcCaller + var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller + if(!ckey) + CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]") + if(current_caller && current_caller != ckey) + if(!GLOB.AdminProcCallSpamPrevention[ckey]) + to_chat(usr, "Another set of admin called procs are still running, your proc will be run after theirs finish.") + GLOB.AdminProcCallSpamPrevention[ckey] = TRUE + UNTIL(!GLOB.AdminProcCaller) + to_chat(usr, "Running your proc") + GLOB.AdminProcCallSpamPrevention -= ckey + else + UNTIL(!GLOB.AdminProcCaller) + GLOB.LastAdminCalledProc = procname + if(target != GLOBAL_PROC) + GLOB.LastAdminCalledTargetRef = "\ref[target]" + GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you + ++GLOB.AdminProcCallCount + . = world.WrapAdminProcCall(target, procname, arguments) + if(--GLOB.AdminProcCallCount == 0) + GLOB.AdminProcCaller = null + +//adv proc call this, ya nerds +/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments) + if(target == GLOBAL_PROC) + return call(procname)(arglist(arguments)) + else if(target != world) + return call(target, procname)(arglist(arguments)) + else + log_admin("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]") + +/proc/IsAdminAdvancedProcCall() +#ifdef TESTING + return FALSE +#else + return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey +#endif + +/client/proc/callproc_datum(datum/A as null|area|mob|obj|turf) + set category = "Debug" + set name = "Atom ProcCall" + set waitfor = 0 + + if(!check_rights(R_DEBUG)) + return + + var/procname = tgui_input_text(usr, "Proc name, eg: fake_blood","Proc:", null) + if(!procname) + return + if(!hascall(A,procname)) + to_chat(usr, "" + span_red("Error: callproc_datum(): type [A.type] has no proc named [procname].") + "") + return + var/list/lst = get_callproc_args() + if(!lst) + return + + if(!A || !IsValidSrc(A)) + to_chat(usr, "Error: callproc_datum(): owner of proc no longer exists.") + return + var/msg = "[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]." + log_admin(msg) + //message_admins(msg) + admin_ticket_log(A, msg) + feedback_add_details("admin_verb","TPC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc + . = get_callproc_returnval(returnval,procname) + if(.) + to_chat(usr, .) + +/client/proc/get_callproc_args() + var/argnum = tgui_input_number(usr, "Number of arguments","Number:",0) + if(isnull(argnum)) + return null //Cancel + + . = list() + //var/list/named_args = list() //Named arguments are removed, due to them making proccalling take too long. + while(argnum--) + /* //Named arguments are removed, due to them making proccalling take too long. + var/named_arg = input(usr,"Leave blank for positional argument. Positional arguments will be considered as if they were added first.", "Named argument") as text|null + if(isnull(named_arg)) + return null //Cancel + */ + var/value = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) + if (!value["class"]) + return null //Cancel + /* //Named arguments are removed, due to them making proccalling take too long. + if(named_arg) + named_args[named_arg] = value["value"] + else + . += value["value"] + if(LAZYLEN(named_args)) + . += named_args + */ + . += value["value"] + +/client/proc/get_callproc_returnval(returnval,procname) + . = "" + if(islist(returnval)) + var/list/returnedlist = returnval + . = "" + if(returnedlist.len) + var/assoc_check = returnedlist[1] + if(istext(assoc_check) && (returnedlist[assoc_check] != null)) + . += "[procname] returned an associative list:" + for(var/key in returnedlist) + . += "\n[key] = [returnedlist[key]]" + + else + . += "[procname] returned a list:" + for(var/elem in returnedlist) + . += "\n[elem]" + else + . = "[procname] returned an empty list" + . += "" + + else + . = span_blue("[procname] returned: [!isnull(returnval) ? returnval : "null"]") diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm index a15a465984d..fa561d2e5aa 100644 --- a/code/modules/admin/create_mob.dm +++ b/code/modules/admin/create_mob.dm @@ -1,9 +1,9 @@ -/var/create_mob_html = null -/datum/admins/proc/create_mob(var/mob/user) - if (!create_mob_html) - var/mobjs = null - mobjs = jointext(typesof(/mob), ";") - create_mob_html = file2text('html/create_object.html') - create_mob_html = replacetext(create_mob_html, "null /* object types */", "\"[mobjs]\"") - - user << browse(create_panel_helper(create_mob_html), "window=create_mob;size=680x600") +/var/create_mob_html = null +/datum/admins/proc/create_mob(var/mob/user) + if (!create_mob_html) + var/mobjs = null + mobjs = jointext(typesof(/mob), ";") + create_mob_html = file2text('html/create_object.html') + create_mob_html = replacetext(create_mob_html, "null /* object types */", "\"[mobjs]\"") + + user << browse(create_panel_helper(create_mob_html), "window=create_mob;size=680x600") diff --git a/code/modules/admin/create_object.dm b/code/modules/admin/create_object.dm index 5520e52bf38..ce5d3077459 100644 --- a/code/modules/admin/create_object.dm +++ b/code/modules/admin/create_object.dm @@ -1,48 +1,48 @@ -/datum/admins/proc/create_panel_helper(template) - var/final_html = replacetext(template, "/* ref src */", "\ref[src];[HrefToken()]") - final_html = replacetext(final_html,"/* hreftokenfield */","[HrefTokenFormField()]") - return final_html - -/datum/admins/proc/create_object(var/mob/user) - var/static/create_object_html = null - if (!create_object_html) - var/objectjs = null - objectjs = jointext(typesof(/obj), ";") - create_object_html = file2text('html/create_object.html') - create_object_html = replacetext(create_object_html, "null /* object types */", "\"[objectjs]\"") - - user << browse(create_panel_helper(create_object_html), "window=create_object;size=680x600") - - -/datum/admins/proc/quick_create_object(var/mob/user) - - var/quick_create_object_html = null - var/pathtext = null - var/list/choices = list("/obj", - "/obj/structure", - "/obj/item", - "/obj/item/device", - "/obj/item/weapon", - "/obj/item/weapon/gun", - "/obj/item/weapon/reagent_containers", - "/obj/item/weapon/reagent_containers/food", - "/obj/item/clothing", - "/obj/item/weapon/storage/box/fluff", //VOREStation Edit, - "/obj/machinery", - "/obj/mecha", - "/obj/item/mecha_parts", - "/obj/item/mecha_parts/mecha_equipment") - - pathtext = tgui_input_list(usr, "Select the path of the object you wish to create.", "Path", choices, "/obj") - - if(!pathtext) - return - var path = text2path(pathtext) - - if (!quick_create_object_html) - var/objectjs = null - objectjs = jointext(typesof(path), ";") - quick_create_object_html = file2text('html/create_object.html') - quick_create_object_html = replacetext(quick_create_object_html, "null /* object types */", "\"[objectjs]\"") - - user << browse(create_panel_helper(quick_create_object_html), "window=quick_create_object;size=680x600") +/datum/admins/proc/create_panel_helper(template) + var/final_html = replacetext(template, "/* ref src */", "\ref[src];[HrefToken()]") + final_html = replacetext(final_html,"/* hreftokenfield */","[HrefTokenFormField()]") + return final_html + +/datum/admins/proc/create_object(var/mob/user) + var/static/create_object_html = null + if (!create_object_html) + var/objectjs = null + objectjs = jointext(typesof(/obj), ";") + create_object_html = file2text('html/create_object.html') + create_object_html = replacetext(create_object_html, "null /* object types */", "\"[objectjs]\"") + + user << browse(create_panel_helper(create_object_html), "window=create_object;size=680x600") + + +/datum/admins/proc/quick_create_object(var/mob/user) + + var/quick_create_object_html = null + var/pathtext = null + var/list/choices = list("/obj", + "/obj/structure", + "/obj/item", + "/obj/item/device", + "/obj/item/weapon", + "/obj/item/weapon/gun", + "/obj/item/weapon/reagent_containers", + "/obj/item/weapon/reagent_containers/food", + "/obj/item/clothing", + "/obj/item/weapon/storage/box/fluff", //VOREStation Edit, + "/obj/machinery", + "/obj/mecha", + "/obj/item/mecha_parts", + "/obj/item/mecha_parts/mecha_equipment") + + pathtext = tgui_input_list(usr, "Select the path of the object you wish to create.", "Path", choices, "/obj") + + if(!pathtext) + return + var path = text2path(pathtext) + + if (!quick_create_object_html) + var/objectjs = null + objectjs = jointext(typesof(path), ";") + quick_create_object_html = file2text('html/create_object.html') + quick_create_object_html = replacetext(quick_create_object_html, "null /* object types */", "\"[objectjs]\"") + + user << browse(create_panel_helper(quick_create_object_html), "window=quick_create_object;size=680x600") diff --git a/code/modules/admin/create_turf.dm b/code/modules/admin/create_turf.dm index ec7e5955848..5a3efffd875 100644 --- a/code/modules/admin/create_turf.dm +++ b/code/modules/admin/create_turf.dm @@ -1,9 +1,9 @@ -/var/create_turf_html = null -/datum/admins/proc/create_turf(var/mob/user) - if (!create_turf_html) - var/turfjs = null - turfjs = jointext(typesof(/turf), ";") - create_turf_html = file2text('html/create_object.html') - create_turf_html = replacetext(create_turf_html, "null /* object types */", "\"[turfjs]\"") - - user << browse(create_panel_helper(create_turf_html), "window=create_turf;size=680x600") +/var/create_turf_html = null +/datum/admins/proc/create_turf(var/mob/user) + if (!create_turf_html) + var/turfjs = null + turfjs = jointext(typesof(/turf), ";") + create_turf_html = file2text('html/create_object.html') + create_turf_html = replacetext(create_turf_html, "null /* object types */", "\"[turfjs]\"") + + user << browse(create_panel_helper(create_turf_html), "window=create_turf;size=680x600") diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index 46544a08096..3274936e2a8 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -1,157 +1,157 @@ -GLOBAL_VAR_INIT(href_token, GenerateToken()) -GLOBAL_PROTECT(href_token) - -var/list/admin_datums = list() - -/datum/admins - var/rank = "Temporary Admin" - var/client/owner = null - var/rights = 0 - var/fakekey = null - - var/datum/marked_datum - - var/admincaster_screen = 0 //See newscaster.dm under machinery for a full description - var/datum/feed_message/admincaster_feed_message = new /datum/feed_message //These two will act as holders. - var/datum/feed_channel/admincaster_feed_channel = new /datum/feed_channel - var/admincaster_signature //What you'll sign the newsfeeds as - - var/href_token - - -/datum/admins/New(initial_rank = "Temporary Admin", initial_rights = 0, ckey) - if(!ckey) - error("Admin datum created without a ckey argument. Datum has been deleted") - qdel(src) - return - admincaster_signature = "[using_map.company_name] Officer #[rand(0,9)][rand(0,9)][rand(0,9)]" - href_token = GenerateToken() - rank = initial_rank - rights = initial_rights - admin_datums[ckey] = src - if(rights & R_DEBUG) //grant profile access - world.SetConfig("APP/admin", ckey, "role=admin") - -/datum/admins/proc/associate(client/C) - if(istype(C)) - owner = C - owner.holder = src - owner.add_admin_verbs() //TODO - GLOB.admins |= C - -/datum/admins/proc/disassociate() - if(owner) - GLOB.admins -= owner - owner.remove_admin_verbs() - owner.deadmin_holder = owner.holder - owner.holder = null - -/datum/admins/proc/reassociate() - if(owner) - GLOB.admins += owner - owner.holder = src - owner.deadmin_holder = null - owner.add_admin_verbs() - -/datum/admins/vv_edit_var(var_name, var_value) - if(var_name == NAMEOF(src, rights) || var_name == NAMEOF(src, owner) || var_name == NAMEOF(src, rank)) - return FALSE - return ..() - -//TODO: Proccall guard, when all try/catch are removed and WrapAdminProccall is ported. - -/* -checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags) -if rights_required == 0, then it simply checks if they are an admin. -if it doesn't return 1 and show_msg=1 it will prints a message explaining why the check has failed -generally it would be used like so: - -/proc/admin_proc() - if(!check_rights(R_ADMIN)) return - to_world("you have enough rights!") - -NOTE: It checks usr by default. Supply the "user" argument if you wish to check for a specific mob. -*/ -/proc/check_rights(rights_required, show_msg=1, var/client/C = usr) - if(ismob(C)) - var/mob/M = C - C = M.client - if(!C) - return FALSE - if(!(istype(C, /client))) // If we still didn't find a client, something is wrong. - return FALSE - if(!C.holder) - if(show_msg) - to_chat(C, "Error: You are not an admin.") - return FALSE - - if(rights_required) - if(rights_required & C.holder.rights) - return TRUE - else - if(show_msg) - to_chat(C, "Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].") - return FALSE - else - return TRUE - -//probably a bit iffy - will hopefully figure out a better solution -/proc/check_if_greater_rights_than(client/other) - if(usr && usr.client) - if(usr.client.holder) - if(!other || !other.holder) - return 1 - if(usr.client.holder.rights != other.holder.rights) - if( (usr.client.holder.rights & other.holder.rights) == other.holder.rights ) - return 1 //we have all the rights they have and more - to_chat(usr, "Error: Cannot proceed. They have more or equal rights to us.") - return 0 - -/client/proc/mark_datum(datum/D) - if(!holder) - return - if(holder.marked_datum) - vv_update_display(holder.marked_datum, "marked", "") - holder.marked_datum = D - vv_update_display(D, "marked", VV_MSG_MARKED) - -/client/proc/mark_datum_mapview(datum/D as mob|obj|turf|area in view(view)) - set category = "Debug" - set name = "Mark Object" - mark_datum(D) - -/client/proc/deadmin() - if(holder) - holder.disassociate() - //qdel(holder) - return 1 - -//This proc checks whether subject has at least ONE of the rights specified in rights_required. -/proc/check_rights_for(client/subject, rights_required) - if(subject && subject.holder) - if(rights_required && !(rights_required & subject.holder.rights)) - return 0 - return 1 - return 0 - -/proc/GenerateToken() - . = "" - for(var/I in 1 to 32) - . += "[rand(10)]" - -/proc/RawHrefToken(forceGlobal = FALSE) - var/tok = GLOB.href_token - if(!forceGlobal && usr) - var/client/C = usr.client - if(!C) - CRASH("No client for HrefToken()!") - var/datum/admins/holder = C.holder - if(holder) - tok = holder.href_token - return tok - -/proc/HrefToken(forceGlobal = FALSE) - return "admin_token=[RawHrefToken(forceGlobal)]" - -/proc/HrefTokenFormField(forceGlobal = FALSE) - return "" +GLOBAL_VAR_INIT(href_token, GenerateToken()) +GLOBAL_PROTECT(href_token) + +var/list/admin_datums = list() + +/datum/admins + var/rank = "Temporary Admin" + var/client/owner = null + var/rights = 0 + var/fakekey = null + + var/datum/marked_datum + + var/admincaster_screen = 0 //See newscaster.dm under machinery for a full description + var/datum/feed_message/admincaster_feed_message = new /datum/feed_message //These two will act as holders. + var/datum/feed_channel/admincaster_feed_channel = new /datum/feed_channel + var/admincaster_signature //What you'll sign the newsfeeds as + + var/href_token + + +/datum/admins/New(initial_rank = "Temporary Admin", initial_rights = 0, ckey) + if(!ckey) + error("Admin datum created without a ckey argument. Datum has been deleted") + qdel(src) + return + admincaster_signature = "[using_map.company_name] Officer #[rand(0,9)][rand(0,9)][rand(0,9)]" + href_token = GenerateToken() + rank = initial_rank + rights = initial_rights + admin_datums[ckey] = src + if(rights & R_DEBUG) //grant profile access + world.SetConfig("APP/admin", ckey, "role=admin") + +/datum/admins/proc/associate(client/C) + if(istype(C)) + owner = C + owner.holder = src + owner.add_admin_verbs() //TODO + GLOB.admins |= C + +/datum/admins/proc/disassociate() + if(owner) + GLOB.admins -= owner + owner.remove_admin_verbs() + owner.deadmin_holder = owner.holder + owner.holder = null + +/datum/admins/proc/reassociate() + if(owner) + GLOB.admins += owner + owner.holder = src + owner.deadmin_holder = null + owner.add_admin_verbs() + +/datum/admins/vv_edit_var(var_name, var_value) + if(var_name == NAMEOF(src, rights) || var_name == NAMEOF(src, owner) || var_name == NAMEOF(src, rank)) + return FALSE + return ..() + +//TODO: Proccall guard, when all try/catch are removed and WrapAdminProccall is ported. + +/* +checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags) +if rights_required == 0, then it simply checks if they are an admin. +if it doesn't return 1 and show_msg=1 it will prints a message explaining why the check has failed +generally it would be used like so: + +/proc/admin_proc() + if(!check_rights(R_ADMIN)) return + to_world("you have enough rights!") + +NOTE: It checks usr by default. Supply the "user" argument if you wish to check for a specific mob. +*/ +/proc/check_rights(rights_required, show_msg=1, var/client/C = usr) + if(ismob(C)) + var/mob/M = C + C = M.client + if(!C) + return FALSE + if(!(istype(C, /client))) // If we still didn't find a client, something is wrong. + return FALSE + if(!C.holder) + if(show_msg) + to_chat(C, "Error: You are not an admin.") + return FALSE + + if(rights_required) + if(rights_required & C.holder.rights) + return TRUE + else + if(show_msg) + to_chat(C, "Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].") + return FALSE + else + return TRUE + +//probably a bit iffy - will hopefully figure out a better solution +/proc/check_if_greater_rights_than(client/other) + if(usr && usr.client) + if(usr.client.holder) + if(!other || !other.holder) + return 1 + if(usr.client.holder.rights != other.holder.rights) + if( (usr.client.holder.rights & other.holder.rights) == other.holder.rights ) + return 1 //we have all the rights they have and more + to_chat(usr, "Error: Cannot proceed. They have more or equal rights to us.") + return 0 + +/client/proc/mark_datum(datum/D) + if(!holder) + return + if(holder.marked_datum) + vv_update_display(holder.marked_datum, "marked", "") + holder.marked_datum = D + vv_update_display(D, "marked", VV_MSG_MARKED) + +/client/proc/mark_datum_mapview(datum/D as mob|obj|turf|area in view(view)) + set category = "Debug" + set name = "Mark Object" + mark_datum(D) + +/client/proc/deadmin() + if(holder) + holder.disassociate() + //qdel(holder) + return 1 + +//This proc checks whether subject has at least ONE of the rights specified in rights_required. +/proc/check_rights_for(client/subject, rights_required) + if(subject && subject.holder) + if(rights_required && !(rights_required & subject.holder.rights)) + return 0 + return 1 + return 0 + +/proc/GenerateToken() + . = "" + for(var/I in 1 to 32) + . += "[rand(10)]" + +/proc/RawHrefToken(forceGlobal = FALSE) + var/tok = GLOB.href_token + if(!forceGlobal && usr) + var/client/C = usr.client + if(!C) + CRASH("No client for HrefToken()!") + var/datum/admins/holder = C.holder + if(holder) + tok = holder.href_token + return tok + +/proc/HrefToken(forceGlobal = FALSE) + return "admin_token=[RawHrefToken(forceGlobal)]" + +/proc/HrefTokenFormField(forceGlobal = FALSE) + return "" diff --git a/code/modules/admin/newbanjob.dm b/code/modules/admin/newbanjob.dm index 7657182312c..f495ddb5c09 100644 --- a/code/modules/admin/newbanjob.dm +++ b/code/modules/admin/newbanjob.dm @@ -1,274 +1,274 @@ -var/savefile/Banlistjob - - -/proc/_jobban_isbanned(var/client/clientvar, var/rank) - if(!clientvar) return 1 - ClearTempbansjob() - var/id = clientvar.computer_id - var/key = clientvar.ckey - if (guest_jobbans(rank)) - if(config.guest_jobban && IsGuestKey(key)) - return 1 - Banlistjob.cd = "/base" - if (Banlistjob.dir.Find("[key][id][rank]")) - return 1 - - Banlistjob.cd = "/base" - for (var/A in Banlistjob.dir) - Banlistjob.cd = "/base/[A]" - if ((id == Banlistjob["id"] || key == Banlistjob["key"]) && rank == Banlistjob["rank"]) - return 1 - return 0 - -/proc/LoadBansjob() - - Banlistjob = new("data/job_fullnew.bdb") - log_admin("Loading Banlistjob") - - if (!length(Banlistjob.dir)) log_admin("Banlistjob is empty.") - - if (!Banlistjob.dir.Find("base")) - log_admin("Banlistjob missing base dir.") - Banlistjob.dir.Add("base") - Banlistjob.cd = "/base" - else if (Banlistjob.dir.Find("base")) - Banlistjob.cd = "/base" - - ClearTempbansjob() - return 1 - -/proc/ClearTempbansjob() - UpdateTime() - - Banlistjob.cd = "/base" - for (var/A in Banlistjob.dir) - Banlistjob.cd = "/base/[A]" - //if (!Banlistjob["key"] || !Banlistjob["id"]) - // RemoveBanjob(A, "full") - // log_admin("Invalid Ban.") - // message_admins("Invalid Ban.") - // continue - - if (!Banlistjob["temp"]) continue - if (CMinutes >= Banlistjob["minutes"]) RemoveBanjob(A) - - return 1 - - -/proc/AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, rank) - UpdateTime() - var/bantimestamp - if (temp) - UpdateTime() - bantimestamp = CMinutes + minutes - if(rank == "Heads") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Personnel") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Site Manager") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Security") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - return 1 - if(rank == "Security") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Security") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Warden") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Detective") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Security Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") - return 1 - if(rank == "Engineering") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Engineer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Atmospheric Technician") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") - return 1 - if(rank == "Research") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Scientist") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Geneticist") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - return 1 - if(rank == "Medical") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Geneticist") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Medical Doctor") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chemist") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") - return 1 - if(rank == "CE_Station_Engineer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Engineer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") - return 1 - if(rank == "CE_Atmospheric_Tech") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Atmospheric Technician") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") - return 1 - if(rank == "CE_Shaft_Miner") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Shaft Miner") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") - return 1 - if(rank == "Chemist_RD_CMO") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chemist") - return 1 - if(rank == "Geneticist_RD_CMO") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Geneticist") - return 1 - if(rank == "MD_CMO") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Medical Doctor") - return 1 - if(rank == "Scientist_RD") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Scientist") - return 1 - if(rank == "AI_Cyborg") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "AI") - return 1 - if(rank == "Detective_HoS") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Detective") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Security") - return 1 - if(rank == "Virologist_RD_CMO") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Virologist") - return 1 - - Banlistjob.cd = "/base" - if ( Banlistjob.dir.Find("[ckey][computerid][rank]") ) - to_chat(usr, span_red("Banjob already exists.")) - return 0 - else - Banlistjob.dir.Add("[ckey][computerid][rank]") - Banlistjob.cd = "/base/[ckey][computerid][rank]" - Banlistjob["key"] << ckey - Banlistjob["id"] << computerid - Banlistjob["rank"] << rank - Banlistjob["reason"] << reason - Banlistjob["bannedby"] << bannedby - Banlistjob["temp"] << temp - if (temp) - Banlistjob["minutes"] << bantimestamp - admin_action_message(bannedby, ckey, "jobbanned-"+rank, reason, temp ? minutes : -1) //VOREStation Add - return 1 - -/proc/RemoveBanjob(foldername) - var/key - var/id - var/rank - Banlistjob.cd = "/base/[foldername]" - Banlistjob["key"] >> key - Banlistjob["id"] >> id - Banlistjob["rank"] >> rank - Banlistjob.cd = "/base" - - if (!Banlistjob.dir.Remove(foldername)) return 0 - - if(!usr) - log_admin("Banjob Expired: [key]") - message_admins("Banjob Expired: [key]") - else - log_admin("[key_name_admin(usr)] unjobbanned [key] from [rank]") - message_admins("[key_name_admin(usr)] unjobbanned:[key] from [rank]") - ban_unban_log_save("[key_name_admin(usr)] unjobbanned [key] from [rank]") - feedback_inc("ban_job_unban",1) - feedback_add_details("ban_job_unban","- [rank]") - - for (var/A in Banlistjob.dir) - Banlistjob.cd = "/base/[A]" - if ((key == Banlistjob["key"] || id == Banlistjob["id"]) && (rank == Banlistjob["rank"])) - Banlistjob.cd = "/base" - Banlistjob.dir.Remove(A) - continue - admin_action_message(usr.key, key, "unjobbanned-"+rank, "\[Unban\]", 0) //VOREStation Add - return 1 - -/proc/GetBanExpjob(minutes as num) - UpdateTime() - var/exp = minutes - CMinutes - if (exp <= 0) - return 0 - else - var/timeleftstring - if (exp >= 1440) //1440 = 1 day in minutes - timeleftstring = "[round(exp / 1440, 0.1)] Days" - else if (exp >= 60) //60 = 1 hour in minutes - timeleftstring = "[round(exp / 60, 0.1)] Hours" - else - timeleftstring = "[exp] Minutes" - return timeleftstring - -/datum/admins/proc/unjobbanpanel() - var/count = 0 - var/dat - //var/dat = "
                    Unban Player: (U) = Unban , (E) = Edit Ban (Total
                    >" - Banlistjob.cd = "/base" - for (var/A in Banlistjob.dir) - count++ - Banlistjob.cd = "/base/[A]" - dat += text("") - - dat += "
                    (U) Key: [Banlistjob["key"]] Rank: [Banlistjob["rank"]] ([Banlistjob["temp"] ? "[GetBanExpjob(Banlistjob["minutes"]) ? GetBanExpjob(Banlistjob["minutes"]) : "Removal pending" ]" : "Permaban"])(By: [Banlistjob["bannedby"]])(Reason: [Banlistjob["reason"]])
                    " - dat = "
                    Bans: (U) = Unban , - ([count] Bans)
                    [dat]" - usr << browse(dat, "window=unbanp;size=875x400") - -/*/datum/admins/proc/permjobban(ckey, computerid, reason, bannedby, temp, minutes, rank) - if(AddBanjob(ckey, computerid, reason, usr.ckey, 0, 0, job)) - to_chat(M, "You have been banned from [job] by [usr.client.ckey].\nReason: [reason].") - to_chat(M, "This is a permanent ban.") - if(config.banappeals) - to_chat(M, "To try to resolve this matter head to [config.banappeals]") - else - to_chat(M, "No ban appeals URL has been set.") - log_admin("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis is a permanent ban.") - message_admins("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis is a permanent ban.") -/datum/admins/proc/timejobban(ckey, computerid, reason, bannedby, temp, minutes, rank) - if(AddBanjob(ckey, computerid, reason, usr.ckey, 1, mins, job)) - to_chat(M, "You have been jobbanned from [job] by [usr.client.ckey].\nReason: [reason].") - to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") - if(config.banappeals) - to_chat(M, "To try to resolve this matter head to [config.banappeals]") - else - to_chat(M, "No ban appeals URL has been set.") - log_admin("[usr.client.ckey] has jobbanned from [job] [ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") - message_admins("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.")*/ -//////////////////////////////////// DEBUG //////////////////////////////////// - -/proc/CreateBansjob() - - UpdateTime() - - var/i - var/last - - for(i=0, i<1001, i++) - var/a = pick(1,0) - var/b = pick(1,0) - if(b) - Banlistjob.cd = "/base" - Banlistjob.dir.Add("trash[i]trashid[i]") - Banlistjob.cd = "/base/trash[i]trashid[i]" - to_chat(Banlistjob["key"], "trash[i]") - else - Banlistjob.cd = "/base" - Banlistjob.dir.Add("[last]trashid[i]") - Banlistjob.cd = "/base/[last]trashid[i]" - Banlistjob["key"] << last - to_chat(Banlistjob["id"], "trashid[i]") - to_chat(Banlistjob["reason"], "Trashban[i].") - Banlistjob["temp"] << a - Banlistjob["minutes"] << CMinutes + rand(1,2000) - to_chat(Banlistjob["bannedby"], "trashmin") - last = "trash[i]" - - Banlistjob.cd = "/base" - -/proc/ClearAllBansjob() - Banlistjob.cd = "/base" - for (var/A in Banlistjob.dir) - RemoveBanjob(A, "full") +var/savefile/Banlistjob + + +/proc/_jobban_isbanned(var/client/clientvar, var/rank) + if(!clientvar) return 1 + ClearTempbansjob() + var/id = clientvar.computer_id + var/key = clientvar.ckey + if (guest_jobbans(rank)) + if(config.guest_jobban && IsGuestKey(key)) + return 1 + Banlistjob.cd = "/base" + if (Banlistjob.dir.Find("[key][id][rank]")) + return 1 + + Banlistjob.cd = "/base" + for (var/A in Banlistjob.dir) + Banlistjob.cd = "/base/[A]" + if ((id == Banlistjob["id"] || key == Banlistjob["key"]) && rank == Banlistjob["rank"]) + return 1 + return 0 + +/proc/LoadBansjob() + + Banlistjob = new("data/job_fullnew.bdb") + log_admin("Loading Banlistjob") + + if (!length(Banlistjob.dir)) log_admin("Banlistjob is empty.") + + if (!Banlistjob.dir.Find("base")) + log_admin("Banlistjob missing base dir.") + Banlistjob.dir.Add("base") + Banlistjob.cd = "/base" + else if (Banlistjob.dir.Find("base")) + Banlistjob.cd = "/base" + + ClearTempbansjob() + return 1 + +/proc/ClearTempbansjob() + UpdateTime() + + Banlistjob.cd = "/base" + for (var/A in Banlistjob.dir) + Banlistjob.cd = "/base/[A]" + //if (!Banlistjob["key"] || !Banlistjob["id"]) + // RemoveBanjob(A, "full") + // log_admin("Invalid Ban.") + // message_admins("Invalid Ban.") + // continue + + if (!Banlistjob["temp"]) continue + if (CMinutes >= Banlistjob["minutes"]) RemoveBanjob(A) + + return 1 + + +/proc/AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, rank) + UpdateTime() + var/bantimestamp + if (temp) + UpdateTime() + bantimestamp = CMinutes + minutes + if(rank == "Heads") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Personnel") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Site Manager") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Security") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") + return 1 + if(rank == "Security") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Security") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Warden") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Detective") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Security Officer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") + return 1 + if(rank == "Engineering") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Engineer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Atmospheric Technician") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") + return 1 + if(rank == "Research") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Scientist") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Geneticist") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") + return 1 + if(rank == "Medical") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Geneticist") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Medical Doctor") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chemist") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") + return 1 + if(rank == "CE_Station_Engineer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Engineer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") + return 1 + if(rank == "CE_Atmospheric_Tech") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Atmospheric Technician") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") + return 1 + if(rank == "CE_Shaft_Miner") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Shaft Miner") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") + return 1 + if(rank == "Chemist_RD_CMO") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chemist") + return 1 + if(rank == "Geneticist_RD_CMO") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Geneticist") + return 1 + if(rank == "MD_CMO") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Medical Doctor") + return 1 + if(rank == "Scientist_RD") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Scientist") + return 1 + if(rank == "AI_Cyborg") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "AI") + return 1 + if(rank == "Detective_HoS") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Detective") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Security") + return 1 + if(rank == "Virologist_RD_CMO") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Virologist") + return 1 + + Banlistjob.cd = "/base" + if ( Banlistjob.dir.Find("[ckey][computerid][rank]") ) + to_chat(usr, span_red("Banjob already exists.")) + return 0 + else + Banlistjob.dir.Add("[ckey][computerid][rank]") + Banlistjob.cd = "/base/[ckey][computerid][rank]" + Banlistjob["key"] << ckey + Banlistjob["id"] << computerid + Banlistjob["rank"] << rank + Banlistjob["reason"] << reason + Banlistjob["bannedby"] << bannedby + Banlistjob["temp"] << temp + if (temp) + Banlistjob["minutes"] << bantimestamp + admin_action_message(bannedby, ckey, "jobbanned-"+rank, reason, temp ? minutes : -1) //VOREStation Add + return 1 + +/proc/RemoveBanjob(foldername) + var/key + var/id + var/rank + Banlistjob.cd = "/base/[foldername]" + Banlistjob["key"] >> key + Banlistjob["id"] >> id + Banlistjob["rank"] >> rank + Banlistjob.cd = "/base" + + if (!Banlistjob.dir.Remove(foldername)) return 0 + + if(!usr) + log_admin("Banjob Expired: [key]") + message_admins("Banjob Expired: [key]") + else + log_admin("[key_name_admin(usr)] unjobbanned [key] from [rank]") + message_admins("[key_name_admin(usr)] unjobbanned:[key] from [rank]") + ban_unban_log_save("[key_name_admin(usr)] unjobbanned [key] from [rank]") + feedback_inc("ban_job_unban",1) + feedback_add_details("ban_job_unban","- [rank]") + + for (var/A in Banlistjob.dir) + Banlistjob.cd = "/base/[A]" + if ((key == Banlistjob["key"] || id == Banlistjob["id"]) && (rank == Banlistjob["rank"])) + Banlistjob.cd = "/base" + Banlistjob.dir.Remove(A) + continue + admin_action_message(usr.key, key, "unjobbanned-"+rank, "\[Unban\]", 0) //VOREStation Add + return 1 + +/proc/GetBanExpjob(minutes as num) + UpdateTime() + var/exp = minutes - CMinutes + if (exp <= 0) + return 0 + else + var/timeleftstring + if (exp >= 1440) //1440 = 1 day in minutes + timeleftstring = "[round(exp / 1440, 0.1)] Days" + else if (exp >= 60) //60 = 1 hour in minutes + timeleftstring = "[round(exp / 60, 0.1)] Hours" + else + timeleftstring = "[exp] Minutes" + return timeleftstring + +/datum/admins/proc/unjobbanpanel() + var/count = 0 + var/dat + //var/dat = "
                    Unban Player:(U) = Unban , (E) = Edit Ban(Total
                    >" + Banlistjob.cd = "/base" + for (var/A in Banlistjob.dir) + count++ + Banlistjob.cd = "/base/[A]" + dat += text("") + + dat += "
                    (U) Key: [Banlistjob["key"]] Rank: [Banlistjob["rank"]] ([Banlistjob["temp"] ? "[GetBanExpjob(Banlistjob["minutes"]) ? GetBanExpjob(Banlistjob["minutes"]) : "Removal pending" ]" : "Permaban"])(By: [Banlistjob["bannedby"]])(Reason: [Banlistjob["reason"]])
                    " + dat = "
                    Bans: (U) = Unban , - ([count] Bans)
                    [dat]" + usr << browse(dat, "window=unbanp;size=875x400") + +/*/datum/admins/proc/permjobban(ckey, computerid, reason, bannedby, temp, minutes, rank) + if(AddBanjob(ckey, computerid, reason, usr.ckey, 0, 0, job)) + to_chat(M, "You have been banned from [job] by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a permanent ban.") + if(config.banappeals) + to_chat(M, "To try to resolve this matter head to [config.banappeals]") + else + to_chat(M, "No ban appeals URL has been set.") + log_admin("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis is a permanent ban.") + message_admins("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis is a permanent ban.") +/datum/admins/proc/timejobban(ckey, computerid, reason, bannedby, temp, minutes, rank) + if(AddBanjob(ckey, computerid, reason, usr.ckey, 1, mins, job)) + to_chat(M, "You have been jobbanned from [job] by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") + if(config.banappeals) + to_chat(M, "To try to resolve this matter head to [config.banappeals]") + else + to_chat(M, "No ban appeals URL has been set.") + log_admin("[usr.client.ckey] has jobbanned from [job] [ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") + message_admins("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.")*/ +//////////////////////////////////// DEBUG //////////////////////////////////// + +/proc/CreateBansjob() + + UpdateTime() + + var/i + var/last + + for(i=0, i<1001, i++) + var/a = pick(1,0) + var/b = pick(1,0) + if(b) + Banlistjob.cd = "/base" + Banlistjob.dir.Add("trash[i]trashid[i]") + Banlistjob.cd = "/base/trash[i]trashid[i]" + to_chat(Banlistjob["key"], "trash[i]") + else + Banlistjob.cd = "/base" + Banlistjob.dir.Add("[last]trashid[i]") + Banlistjob.cd = "/base/[last]trashid[i]" + Banlistjob["key"] << last + to_chat(Banlistjob["id"], "trashid[i]") + to_chat(Banlistjob["reason"], "Trashban[i].") + Banlistjob["temp"] << a + Banlistjob["minutes"] << CMinutes + rand(1,2000) + to_chat(Banlistjob["bannedby"], "trashmin") + last = "trash[i]" + + Banlistjob.cd = "/base" + +/proc/ClearAllBansjob() + Banlistjob.cd = "/base" + for (var/A in Banlistjob.dir) + RemoveBanjob(A, "full") diff --git a/code/modules/admin/permissionverbs/permissionedit.dm b/code/modules/admin/permissionverbs/permissionedit.dm index b6be3a77906..9c784ea49c6 100644 --- a/code/modules/admin/permissionverbs/permissionedit.dm +++ b/code/modules/admin/permissionverbs/permissionedit.dm @@ -1,149 +1,149 @@ -/client/proc/edit_admin_permissions() - set category = "Admin" - set name = "Permissions Panel" - set desc = "Edit admin permissions" - if(!check_rights(R_PERMISSIONS)) return - usr.client.holder.edit_admin_permissions() - -/datum/admins/proc/edit_admin_permissions() - if(!check_rights(R_PERMISSIONS)) return - - var/output = {" - - -Permissions Panel - - - - -
                    - - - - -"} - - for(var/adm_ckey in admin_datums) - var/datum/admins/D = admin_datums[adm_ckey] - if(!D) continue - var/rank = D.rank ? D.rank : "*none*" - var/rights = rights2text(D.rights," ") - if(!rights) rights = "*none*" - - output += "" - output += "" - output += "" - output += "" - output += "" - - output += {" -
                    CKEY \[+\]RANKPERMISSIONS
                    [adm_ckey] \[-\][rank][rights]
                    -
                    Search:
                    - -"} - - usr << browse(output,"window=editrights;size=600x500") - -/datum/admins/proc/log_admin_rank_modification(var/adm_ckey, var/new_rank) - if(config.admin_legacy_system) return - - if(!usr.client) - return - - if(!usr.client.holder || !(usr.client.holder.rights & R_PERMISSIONS)) - to_chat(usr, "[span_red("You do not have permission to do this!")]") - return - - establish_db_connection() - - if(!dbcon.IsConnected()) - to_chat(usr, "[span_red("Failed to establish database connection")]") - return - - if(!adm_ckey || !new_rank) - return - - adm_ckey = ckey(adm_ckey) - - if(!adm_ckey) - return - - if(!istext(adm_ckey) || !istext(new_rank)) - return - - var/DBQuery/select_query = dbcon.NewQuery("SELECT id FROM erro_admin WHERE ckey = '[adm_ckey]'") - select_query.Execute() - - var/new_admin = 1 - var/admin_id - while(select_query.NextRow()) - new_admin = 0 - admin_id = text2num(select_query.item[1]) - - if(new_admin) - var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO `erro_admin` (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');") - log_query.Execute() - to_chat(usr, "[span_blue("New admin added.")]") - else - if(!isnull(admin_id) && isnum(admin_id)) - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET rank = '[new_rank]' WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');") - log_query.Execute() - to_chat(usr, "[span_blue("Admin rank changed.")]") - -/datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission) - if(config.admin_legacy_system) return - - if(!usr.client) - return - - if(!usr.client.holder || !(usr.client.holder.rights & R_PERMISSIONS)) - to_chat(usr, "[span_red(">You do not have permission to do this!")]") - return - - establish_db_connection() - if(!dbcon.IsConnected()) - to_chat(usr, "[span_red("Failed to establish database connection!")]") - return - - if(!adm_ckey || !new_permission) - return - - adm_ckey = ckey(adm_ckey) - - if(!adm_ckey) - return - - if(istext(new_permission)) - new_permission = text2num(new_permission) - - if(!istext(adm_ckey) || !isnum(new_permission)) - return - - var/DBQuery/select_query = dbcon.NewQuery("SELECT id, flags FROM erro_admin WHERE ckey = '[adm_ckey]'") - select_query.Execute() - - var/admin_id - var/admin_rights - while(select_query.NextRow()) - admin_id = text2num(select_query.item[1]) - admin_rights = text2num(select_query.item[2]) - - if(!admin_id) - return - - if(admin_rights & new_permission) //This admin already has this permission, so we are removing it. - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET flags = [admin_rights & ~new_permission] WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');") - log_query.Execute() - to_chat(usr, "[span_blue("Permission removed.")]") - else //This admin doesn't have this permission, so we are adding it. - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')") - log_query.Execute() - to_chat(usr, "[span_blue("Permission added.")]") +/client/proc/edit_admin_permissions() + set category = "Admin" + set name = "Permissions Panel" + set desc = "Edit admin permissions" + if(!check_rights(R_PERMISSIONS)) return + usr.client.holder.edit_admin_permissions() + +/datum/admins/proc/edit_admin_permissions() + if(!check_rights(R_PERMISSIONS)) return + + var/output = {" + + +Permissions Panel + + + + +
                    + + + + +"} + + for(var/adm_ckey in admin_datums) + var/datum/admins/D = admin_datums[adm_ckey] + if(!D) continue + var/rank = D.rank ? D.rank : "*none*" + var/rights = rights2text(D.rights," ") + if(!rights) rights = "*none*" + + output += "" + output += "" + output += "" + output += "" + output += "" + + output += {" +
                    CKEY \[+\]RANKPERMISSIONS
                    [adm_ckey] \[-\][rank][rights]
                    +
                    Search:
                    + +"} + + usr << browse(output,"window=editrights;size=600x500") + +/datum/admins/proc/log_admin_rank_modification(var/adm_ckey, var/new_rank) + if(config.admin_legacy_system) return + + if(!usr.client) + return + + if(!usr.client.holder || !(usr.client.holder.rights & R_PERMISSIONS)) + to_chat(usr, "[span_red("You do not have permission to do this!")]") + return + + establish_db_connection() + + if(!dbcon.IsConnected()) + to_chat(usr, "[span_red("Failed to establish database connection")]") + return + + if(!adm_ckey || !new_rank) + return + + adm_ckey = ckey(adm_ckey) + + if(!adm_ckey) + return + + if(!istext(adm_ckey) || !istext(new_rank)) + return + + var/DBQuery/select_query = dbcon.NewQuery("SELECT id FROM erro_admin WHERE ckey = '[adm_ckey]'") + select_query.Execute() + + var/new_admin = 1 + var/admin_id + while(select_query.NextRow()) + new_admin = 0 + admin_id = text2num(select_query.item[1]) + + if(new_admin) + var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO `erro_admin` (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)") + insert_query.Execute() + var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');") + log_query.Execute() + to_chat(usr, "[span_blue("New admin added.")]") + else + if(!isnull(admin_id) && isnum(admin_id)) + var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET rank = '[new_rank]' WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');") + log_query.Execute() + to_chat(usr, "[span_blue("Admin rank changed.")]") + +/datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission) + if(config.admin_legacy_system) return + + if(!usr.client) + return + + if(!usr.client.holder || !(usr.client.holder.rights & R_PERMISSIONS)) + to_chat(usr, "[span_red(">You do not have permission to do this!")]") + return + + establish_db_connection() + if(!dbcon.IsConnected()) + to_chat(usr, "[span_red("Failed to establish database connection!")]") + return + + if(!adm_ckey || !new_permission) + return + + adm_ckey = ckey(adm_ckey) + + if(!adm_ckey) + return + + if(istext(new_permission)) + new_permission = text2num(new_permission) + + if(!istext(adm_ckey) || !isnum(new_permission)) + return + + var/DBQuery/select_query = dbcon.NewQuery("SELECT id, flags FROM erro_admin WHERE ckey = '[adm_ckey]'") + select_query.Execute() + + var/admin_id + var/admin_rights + while(select_query.NextRow()) + admin_id = text2num(select_query.item[1]) + admin_rights = text2num(select_query.item[2]) + + if(!admin_id) + return + + if(admin_rights & new_permission) //This admin already has this permission, so we are removing it. + var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET flags = [admin_rights & ~new_permission] WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');") + log_query.Execute() + to_chat(usr, "[span_blue("Permission removed.")]") + else //This admin doesn't have this permission, so we are adding it. + var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')") + log_query.Execute() + to_chat(usr, "[span_blue("Permission added.")]") diff --git a/code/modules/admin/secrets/admin_secrets/admin_logs.dm b/code/modules/admin/secrets/admin_secrets/admin_logs.dm index b578eb046ff..83ebe9ec0ef 100644 --- a/code/modules/admin/secrets/admin_secrets/admin_logs.dm +++ b/code/modules/admin/secrets/admin_secrets/admin_logs.dm @@ -1,44 +1,44 @@ -/datum/admin_secret_item/admin_secret/admin_logs - name = "Admin Logs" - -/datum/admin_secret_item/admin_secret/admin_logs/execute(var/mob/user) - . = ..() - if(!.) - return - var/dat = "Admin Log
                    " - for(var/l in admin_log) - dat += "
                  • [l]
                  • " - if(!admin_log.len) - dat += "No-one has done anything this round!" - - var/datum/browser/popup = new(user, "adminlogs", "[src]", 550, 650, src) - popup.set_content(jointext(dat,null)) - popup.open() - - onclose(user, "adminlogs") - - -/datum/admin_secret_item/admin_secret/round_logs - name = "Round Dialogue Logs" - -/datum/admin_secret_item/admin_secret/round_logs/execute(var/mob/user) - . = ..() - if(!.) - return - var/dat = "Dialogue Log
                    " - - dat += "
                    " - - for(var/l in GLOB.round_text_log) - dat += "
                  • [l]
                  • " - - dat += "
                    " - - if(!GLOB.round_text_log) - dat += "No-one has said anything this round! (How odd?)" - - var/datum/browser/popup = new(user, "dialoguelogs", "[src]", 550, 650, src) - popup.set_content(jointext(dat,null)) - popup.open() - - onclose(user, "dialoguelogs") +/datum/admin_secret_item/admin_secret/admin_logs + name = "Admin Logs" + +/datum/admin_secret_item/admin_secret/admin_logs/execute(var/mob/user) + . = ..() + if(!.) + return + var/dat = "Admin Log
                    " + for(var/l in admin_log) + dat += "
                  • [l]
                  • " + if(!admin_log.len) + dat += "No-one has done anything this round!" + + var/datum/browser/popup = new(user, "adminlogs", "[src]", 550, 650, src) + popup.set_content(jointext(dat,null)) + popup.open() + + onclose(user, "adminlogs") + + +/datum/admin_secret_item/admin_secret/round_logs + name = "Round Dialogue Logs" + +/datum/admin_secret_item/admin_secret/round_logs/execute(var/mob/user) + . = ..() + if(!.) + return + var/dat = "Dialogue Log
                    " + + dat += "
                    " + + for(var/l in GLOB.round_text_log) + dat += "
                  • [l]
                  • " + + dat += "
                    " + + if(!GLOB.round_text_log) + dat += "No-one has said anything this round! (How odd?)" + + var/datum/browser/popup = new(user, "dialoguelogs", "[src]", 550, 650, src) + popup.set_content(jointext(dat,null)) + popup.open() + + onclose(user, "dialoguelogs") diff --git a/code/modules/admin/secrets/admin_secrets/alter_narsie.dm b/code/modules/admin/secrets/admin_secrets/alter_narsie.dm index e764d1bb95a..e15dd13319d 100644 --- a/code/modules/admin/secrets/admin_secrets/alter_narsie.dm +++ b/code/modules/admin/secrets/admin_secrets/alter_narsie.dm @@ -1,14 +1,14 @@ -/datum/admin_secret_item/admin_secret/alter_narise - name = "Alter Nar-Sie" - -/datum/admin_secret_item/admin_secret/alter_narise/execute(var/mob/user) - . = ..() - if(!.) - return - var/choice = tgui_alert(user, "How do you wish for Nar-Sie to interact with its surroundings?","NarChoice",list("CultStation13", "Nar-Singulo")) - if(choice == "CultStation13") - log_and_message_admins("has set narsie's behaviour to \"CultStation13\".", user) - narsie_behaviour = choice - if(choice == "Nar-Singulo") - log_and_message_admins("has set narsie's behaviour to \"Nar-Singulo\".", user) - narsie_behaviour = choice +/datum/admin_secret_item/admin_secret/alter_narise + name = "Alter Nar-Sie" + +/datum/admin_secret_item/admin_secret/alter_narise/execute(var/mob/user) + . = ..() + if(!.) + return + var/choice = tgui_alert(user, "How do you wish for Nar-Sie to interact with its surroundings?","NarChoice",list("CultStation13", "Nar-Singulo")) + if(choice == "CultStation13") + log_and_message_admins("has set narsie's behaviour to \"CultStation13\".", user) + narsie_behaviour = choice + if(choice == "Nar-Singulo") + log_and_message_admins("has set narsie's behaviour to \"Nar-Singulo\".", user) + narsie_behaviour = choice diff --git a/code/modules/admin/secrets/admin_secrets/bombing_list.dm b/code/modules/admin/secrets/admin_secrets/bombing_list.dm index f94af7e569a..d9c24097d24 100644 --- a/code/modules/admin/secrets/admin_secrets/bombing_list.dm +++ b/code/modules/admin/secrets/admin_secrets/bombing_list.dm @@ -1,12 +1,12 @@ -/datum/admin_secret_item/admin_secret/bombing_list - name = "Bombing List" - -/datum/admin_secret_item/admin_secret/bombing_list/execute(var/mob/user) - . = ..() - if(!.) - return - - var/dat = "Bombing List" - for(var/l in bombers) - dat += text("[l]
                    ") - user << browse(dat, "window=bombers") +/datum/admin_secret_item/admin_secret/bombing_list + name = "Bombing List" + +/datum/admin_secret_item/admin_secret/bombing_list/execute(var/mob/user) + . = ..() + if(!.) + return + + var/dat = "Bombing List" + for(var/l in bombers) + dat += text("[l]
                    ") + user << browse(dat, "window=bombers") diff --git a/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm b/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm index 942ff60d77c..07c5096ee7b 100644 --- a/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm +++ b/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm @@ -1,37 +1,37 @@ -/datum/admin_secret_item/admin_secret/jump_shuttle - name = "Jump a Shuttle" - -/datum/admin_secret_item/admin_secret/jump_shuttle/can_execute(var/mob/user) - if(!SSshuttles) return 0 - return ..() - -/datum/admin_secret_item/admin_secret/jump_shuttle/execute(var/mob/user) - . = ..() - if(!.) - return - var/shuttle_tag = tgui_input_list(user, "Which shuttle do you want to jump?", "Shuttle Choice", SSshuttles.shuttles) - if (!shuttle_tag) return - - var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag] - - var/list/area_choices = return_areas() - var/origin_area = tgui_input_list(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)", "Area Choice", area_choices) - if (!origin_area) return - - var/destination_area = tgui_input_list(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)", "Area Choice", area_choices) - if (!destination_area) return - - var/long_jump = tgui_alert(user, "Is there a transition area for this jump?","Transition?", list("Yes","No")) - if (long_jump == "Yes") - var/transition_area = tgui_input_list(user, "Which area is the transition area? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)", "Area Choice", area_choices) - if (!transition_area) return - - var/move_duration = tgui_input_number(user, "How many seconds will this jump take?") - - S.long_jump(area_choices[origin_area], area_choices[destination_area], area_choices[transition_area], move_duration) - message_admins("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] lasting [move_duration] seconds for the [shuttle_tag] shuttle", 1) - log_admin("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] lasting [move_duration] seconds for the [shuttle_tag] shuttle") - else - S.short_jump(area_choices[origin_area], area_choices[destination_area]) - message_admins("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] for the [shuttle_tag] shuttle", 1) - log_admin("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] for the [shuttle_tag] shuttle") +/datum/admin_secret_item/admin_secret/jump_shuttle + name = "Jump a Shuttle" + +/datum/admin_secret_item/admin_secret/jump_shuttle/can_execute(var/mob/user) + if(!SSshuttles) return 0 + return ..() + +/datum/admin_secret_item/admin_secret/jump_shuttle/execute(var/mob/user) + . = ..() + if(!.) + return + var/shuttle_tag = tgui_input_list(user, "Which shuttle do you want to jump?", "Shuttle Choice", SSshuttles.shuttles) + if (!shuttle_tag) return + + var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag] + + var/list/area_choices = return_areas() + var/origin_area = tgui_input_list(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)", "Area Choice", area_choices) + if (!origin_area) return + + var/destination_area = tgui_input_list(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)", "Area Choice", area_choices) + if (!destination_area) return + + var/long_jump = tgui_alert(user, "Is there a transition area for this jump?","Transition?", list("Yes","No")) + if (long_jump == "Yes") + var/transition_area = tgui_input_list(user, "Which area is the transition area? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)", "Area Choice", area_choices) + if (!transition_area) return + + var/move_duration = tgui_input_number(user, "How many seconds will this jump take?") + + S.long_jump(area_choices[origin_area], area_choices[destination_area], area_choices[transition_area], move_duration) + message_admins("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] lasting [move_duration] seconds for the [shuttle_tag] shuttle", 1) + log_admin("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] lasting [move_duration] seconds for the [shuttle_tag] shuttle") + else + S.short_jump(area_choices[origin_area], area_choices[destination_area]) + message_admins("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] for the [shuttle_tag] shuttle", 1) + log_admin("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] for the [shuttle_tag] shuttle") diff --git a/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm b/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm index c4fcc3ac200..007530e7de1 100644 --- a/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm +++ b/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm @@ -1,26 +1,26 @@ -/datum/admin_secret_item/admin_secret/launch_shuttle - name = "Launch a Shuttle" - -/datum/admin_secret_item/admin_secret/launch_shuttle/can_execute(var/mob/user) - if(!SSshuttles) return 0 - return ..() - -/datum/admin_secret_item/admin_secret/launch_shuttle/execute(var/mob/user) - . = ..() - if(!.) - return - var/list/valid_shuttles = list() - for (var/shuttle_tag in SSshuttles.shuttles) - if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock)) - valid_shuttles += shuttle_tag - - var/shuttle_tag = tgui_input_list(user, "Which shuttle do you want to launch?", "Shuttle Choice", valid_shuttles) - if (!shuttle_tag) - return - - var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag] - if (S.can_launch()) - S.launch(user) - log_and_message_admins("launched the [shuttle_tag] shuttle", user) - else - tgui_alert_async(user, "The [shuttle_tag] shuttle cannot be launched at this time. It's probably busy.") +/datum/admin_secret_item/admin_secret/launch_shuttle + name = "Launch a Shuttle" + +/datum/admin_secret_item/admin_secret/launch_shuttle/can_execute(var/mob/user) + if(!SSshuttles) return 0 + return ..() + +/datum/admin_secret_item/admin_secret/launch_shuttle/execute(var/mob/user) + . = ..() + if(!.) + return + var/list/valid_shuttles = list() + for (var/shuttle_tag in SSshuttles.shuttles) + if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock)) + valid_shuttles += shuttle_tag + + var/shuttle_tag = tgui_input_list(user, "Which shuttle do you want to launch?", "Shuttle Choice", valid_shuttles) + if (!shuttle_tag) + return + + var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag] + if (S.can_launch()) + S.launch(user) + log_and_message_admins("launched the [shuttle_tag] shuttle", user) + else + tgui_alert_async(user, "The [shuttle_tag] shuttle cannot be launched at this time. It's probably busy.") diff --git a/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm b/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm index 3c62428c099..517ddc91f75 100644 --- a/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm +++ b/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm @@ -1,26 +1,26 @@ -/datum/admin_secret_item/admin_secret/launch_shuttle_forced - name = "Launch a Shuttle (Forced)" - -/datum/admin_secret_item/admin_secret/launch_shuttle_forced/can_execute(var/mob/user) - if(!SSshuttles) return 0 - return ..() - -/datum/admin_secret_item/admin_secret/launch_shuttle_forced/execute(var/mob/user) - . = ..() - if(!.) - return - var/list/valid_shuttles = list() - for (var/shuttle_tag in SSshuttles.shuttles) - if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock)) - valid_shuttles += shuttle_tag - - var/shuttle_tag = tgui_input_list(user, "Which shuttle's launch do you want to force?", "Shuttle Choice", valid_shuttles) - if (!shuttle_tag) - return - - var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag] - if (S.can_force()) - S.force_launch(user) - log_and_message_admins("forced the [shuttle_tag] shuttle", user) - else - tgui_alert_async(user, "The [shuttle_tag] shuttle launch cannot be forced at this time. It's busy, or hasn't been launched yet.") +/datum/admin_secret_item/admin_secret/launch_shuttle_forced + name = "Launch a Shuttle (Forced)" + +/datum/admin_secret_item/admin_secret/launch_shuttle_forced/can_execute(var/mob/user) + if(!SSshuttles) return 0 + return ..() + +/datum/admin_secret_item/admin_secret/launch_shuttle_forced/execute(var/mob/user) + . = ..() + if(!.) + return + var/list/valid_shuttles = list() + for (var/shuttle_tag in SSshuttles.shuttles) + if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock)) + valid_shuttles += shuttle_tag + + var/shuttle_tag = tgui_input_list(user, "Which shuttle's launch do you want to force?", "Shuttle Choice", valid_shuttles) + if (!shuttle_tag) + return + + var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag] + if (S.can_force()) + S.force_launch(user) + log_and_message_admins("forced the [shuttle_tag] shuttle", user) + else + tgui_alert_async(user, "The [shuttle_tag] shuttle launch cannot be forced at this time. It's busy, or hasn't been launched yet.") diff --git a/code/modules/admin/secrets/admin_secrets/list_dna.dm b/code/modules/admin/secrets/admin_secrets/list_dna.dm index f1c49f48972..5d4738d692b 100644 --- a/code/modules/admin/secrets/admin_secrets/list_dna.dm +++ b/code/modules/admin/secrets/admin_secrets/list_dna.dm @@ -1,14 +1,14 @@ -/datum/admin_secret_item/admin_secret/list_dna - name = "List DNA (Blood)" - -/datum/admin_secret_item/admin_secret/list_dna/execute(var/mob/user) - . = ..() - if(!.) - return - var/dat = "Showing DNA from blood.
                    " - dat += "" - for(var/mob/living/carbon/human/H in mob_list) - if(H.dna && H.ckey) - dat += "" - dat += "
                    NameDNABlood Type
                    [H][H.dna.unique_enzymes][H.b_type]
                    " - user << browse(dat, "window=DNA;size=440x410") +/datum/admin_secret_item/admin_secret/list_dna + name = "List DNA (Blood)" + +/datum/admin_secret_item/admin_secret/list_dna/execute(var/mob/user) + . = ..() + if(!.) + return + var/dat = "Showing DNA from blood.
                    " + dat += "" + for(var/mob/living/carbon/human/H in mob_list) + if(H.dna && H.ckey) + dat += "" + dat += "
                    NameDNABlood Type
                    [H][H.dna.unique_enzymes][H.b_type]
                    " + user << browse(dat, "window=DNA;size=440x410") diff --git a/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm b/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm index 03cb9701c41..0a9f4e0cb13 100644 --- a/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm +++ b/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm @@ -1,19 +1,19 @@ -/datum/admin_secret_item/admin_secret/list_fingerprints - name = "List Fingerprints" - -/datum/admin_secret_item/admin_secret/list_fingerprints/execute(var/mob/user) - . = ..() - if(!.) - return - var/dat = "Showing Fingerprints.
                    " - dat += "" - for(var/mob/living/carbon/human/H in mob_list) - if(H.ckey) - if(H.dna && H.dna.uni_identity) - dat += "" - else if(H.dna && !H.dna.uni_identity) - dat += "" - else if(!H.dna) - dat += "" - dat += "
                    NameFingerprints
                    [H][md5(H.dna.uni_identity)]
                    [H]H.dna.uni_identity = null
                    [H]H.dna = null
                    " - user << browse(dat, "window=fingerprints;size=440x410") +/datum/admin_secret_item/admin_secret/list_fingerprints + name = "List Fingerprints" + +/datum/admin_secret_item/admin_secret/list_fingerprints/execute(var/mob/user) + . = ..() + if(!.) + return + var/dat = "Showing Fingerprints.
                    " + dat += "" + for(var/mob/living/carbon/human/H in mob_list) + if(H.ckey) + if(H.dna && H.dna.uni_identity) + dat += "" + else if(H.dna && !H.dna.uni_identity) + dat += "" + else if(!H.dna) + dat += "" + dat += "
                    NameFingerprints
                    [H][md5(H.dna.uni_identity)]
                    [H]H.dna.uni_identity = null
                    [H]H.dna = null
                    " + user << browse(dat, "window=fingerprints;size=440x410") diff --git a/code/modules/admin/secrets/admin_secrets/move_shuttle.dm b/code/modules/admin/secrets/admin_secrets/move_shuttle.dm index 7cad935ca3c..768101434a8 100644 --- a/code/modules/admin/secrets/admin_secrets/move_shuttle.dm +++ b/code/modules/admin/secrets/admin_secrets/move_shuttle.dm @@ -1,27 +1,27 @@ -/datum/admin_secret_item/admin_secret/move_shuttle - name = "Move a Shuttle" - -/datum/admin_secret_item/admin_secret/move_shuttle/can_execute(var/mob/user) - if(!SSshuttles) return 0 - return ..() - -/datum/admin_secret_item/admin_secret/move_shuttle/execute(var/mob/user) - . = ..() - if(!.) - return - var/confirm = tgui_alert(user, "This command directly moves a shuttle from one area to another. DO NOT USE THIS UNLESS YOU ARE DEBUGGING A SHUTTLE AND YOU KNOW WHAT YOU ARE DOING.", "Are you sure?", list("Ok", "Cancel")) - if (confirm == "Cancel") - return - - var/shuttle_tag = tgui_input_list(user, "Which shuttle do you want to jump?", "Shuttle Choice", SSshuttles.shuttles) - if (!shuttle_tag) return - - var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag] - - var/destination_tag = tgui_input_list(user, "Which landmark do you want to jump to? (IF YOU GET THIS WRONG THINGS WILL BREAK)", "Landmark Choice", SSshuttles.registered_shuttle_landmarks) - if (!destination_tag) return - var/destination_location = SSshuttles.get_landmark(destination_tag) - if (!destination_location) return - - S.attempt_move(destination_location) - log_and_message_admins("moved the [shuttle_tag] shuttle", user) +/datum/admin_secret_item/admin_secret/move_shuttle + name = "Move a Shuttle" + +/datum/admin_secret_item/admin_secret/move_shuttle/can_execute(var/mob/user) + if(!SSshuttles) return 0 + return ..() + +/datum/admin_secret_item/admin_secret/move_shuttle/execute(var/mob/user) + . = ..() + if(!.) + return + var/confirm = tgui_alert(user, "This command directly moves a shuttle from one area to another. DO NOT USE THIS UNLESS YOU ARE DEBUGGING A SHUTTLE AND YOU KNOW WHAT YOU ARE DOING.", "Are you sure?", list("Ok", "Cancel")) + if (confirm == "Cancel") + return + + var/shuttle_tag = tgui_input_list(user, "Which shuttle do you want to jump?", "Shuttle Choice", SSshuttles.shuttles) + if (!shuttle_tag) return + + var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag] + + var/destination_tag = tgui_input_list(user, "Which landmark do you want to jump to? (IF YOU GET THIS WRONG THINGS WILL BREAK)", "Landmark Choice", SSshuttles.registered_shuttle_landmarks) + if (!destination_tag) return + var/destination_location = SSshuttles.get_landmark(destination_tag) + if (!destination_location) return + + S.attempt_move(destination_location) + log_and_message_admins("moved the [shuttle_tag] shuttle", user) diff --git a/code/modules/admin/secrets/admin_secrets/prison_warp.dm b/code/modules/admin/secrets/admin_secrets/prison_warp.dm index b4ac06fcc85..06740f17606 100644 --- a/code/modules/admin/secrets/admin_secrets/prison_warp.dm +++ b/code/modules/admin/secrets/admin_secrets/prison_warp.dm @@ -1,38 +1,38 @@ -/datum/admin_secret_item/admin_secret/prison_warp - name = "Prison Warp" - -/datum/admin_secret_item/admin_secret/prison_warp/can_execute(var/mob/user) - if(!ticker) return 0 - return ..() - -/datum/admin_secret_item/admin_secret/prison_warp/execute(var/mob/user) - . = ..() - if(!.) - return - for(var/mob/living/carbon/human/H in mob_list) - var/turf/T = get_turf(H) - var/security = 0 - if((T in using_map.admin_levels) || prisonwarped.Find(H)) - //don't warp them if they aren't ready or are already there - continue - H.Paralyse(5) - if(H.wear_id) - var/obj/item/weapon/card/id/id = H.get_idcard() - for(var/A in id.access) - if(A == access_security) - security++ - if(!security) - //strip their stuff before they teleport into a cell :downs: - for(var/obj/item/weapon/W in H) - if(istype(W, /obj/item/organ/external)) - continue - //don't strip organs - H.drop_from_inventory(W) - //teleport person to cell - H.loc = pick(prisonwarp) - H.equip_to_slot_or_del(new /obj/item/clothing/under/color/prison(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(H), slot_shoes) - else - //teleport security person - H.loc = pick(prisonsecuritywarp) - prisonwarped += H +/datum/admin_secret_item/admin_secret/prison_warp + name = "Prison Warp" + +/datum/admin_secret_item/admin_secret/prison_warp/can_execute(var/mob/user) + if(!ticker) return 0 + return ..() + +/datum/admin_secret_item/admin_secret/prison_warp/execute(var/mob/user) + . = ..() + if(!.) + return + for(var/mob/living/carbon/human/H in mob_list) + var/turf/T = get_turf(H) + var/security = 0 + if((T in using_map.admin_levels) || prisonwarped.Find(H)) + //don't warp them if they aren't ready or are already there + continue + H.Paralyse(5) + if(H.wear_id) + var/obj/item/weapon/card/id/id = H.get_idcard() + for(var/A in id.access) + if(A == access_security) + security++ + if(!security) + //strip their stuff before they teleport into a cell :downs: + for(var/obj/item/weapon/W in H) + if(istype(W, /obj/item/organ/external)) + continue + //don't strip organs + H.drop_from_inventory(W) + //teleport person to cell + H.loc = pick(prisonwarp) + H.equip_to_slot_or_del(new /obj/item/clothing/under/color/prison(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(H), slot_shoes) + else + //teleport security person + H.loc = pick(prisonsecuritywarp) + prisonwarped += H diff --git a/code/modules/admin/secrets/admin_secrets/show_ai_laws.dm b/code/modules/admin/secrets/admin_secrets/show_ai_laws.dm index c76ff2d9c18..c9e7cec5df3 100644 --- a/code/modules/admin/secrets/admin_secrets/show_ai_laws.dm +++ b/code/modules/admin/secrets/admin_secrets/show_ai_laws.dm @@ -1,7 +1,7 @@ -/datum/admin_secret_item/admin_secret/show_ai_laws - name = "Show AI laws" - -/datum/admin_secret_item/admin_secret/show_ai_laws/execute(var/mob/user) - . = ..() - if(.) - user.client.holder.output_ai_laws() +/datum/admin_secret_item/admin_secret/show_ai_laws + name = "Show AI laws" + +/datum/admin_secret_item/admin_secret/show_ai_laws/execute(var/mob/user) + . = ..() + if(.) + user.client.holder.output_ai_laws() diff --git a/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm b/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm index f4bb82224ba..3cbe5062e71 100644 --- a/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm +++ b/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm @@ -1,12 +1,12 @@ -/datum/admin_secret_item/admin_secret/show_crew_manifest - name = "Show Crew Manifest" - -/datum/admin_secret_item/admin_secret/show_crew_manifest/execute(var/mob/user) - . = ..() - if(!.) - return - var/dat - dat += "

                    Crew Manifest

                    " - dat += data_core.get_manifest() - - user << browse(dat, "window=manifest;size=370x420;can_close=1") +/datum/admin_secret_item/admin_secret/show_crew_manifest + name = "Show Crew Manifest" + +/datum/admin_secret_item/admin_secret/show_crew_manifest/execute(var/mob/user) + . = ..() + if(!.) + return + var/dat + dat += "

                    Crew Manifest

                    " + dat += data_core.get_manifest() + + user << browse(dat, "window=manifest;size=370x420;can_close=1") diff --git a/code/modules/admin/secrets/admin_secrets/show_game_mode.dm b/code/modules/admin/secrets/admin_secrets/show_game_mode.dm index bde35639602..a323266f5c4 100644 --- a/code/modules/admin/secrets/admin_secrets/show_game_mode.dm +++ b/code/modules/admin/secrets/admin_secrets/show_game_mode.dm @@ -1,14 +1,14 @@ -/datum/admin_secret_item/admin_secret/show_game_mode - name = "Show Game Mode" - -/datum/admin_secret_item/admin_secret/show_game_mode/can_execute(var/mob/user) - if(!ticker) - return 0 - return ..() - -/datum/admin_secret_item/admin_secret/show_game_mode/execute(var/mob/user) - . = ..() - if(!.) - return - if (ticker.mode) tgui_alert_async(usr, "The game mode is [ticker.mode.name]") - else tgui_alert_async(usr, "For some reason there's a ticker, but not a game mode") +/datum/admin_secret_item/admin_secret/show_game_mode + name = "Show Game Mode" + +/datum/admin_secret_item/admin_secret/show_game_mode/can_execute(var/mob/user) + if(!ticker) + return 0 + return ..() + +/datum/admin_secret_item/admin_secret/show_game_mode/execute(var/mob/user) + . = ..() + if(!.) + return + if (ticker.mode) tgui_alert_async(usr, "The game mode is [ticker.mode.name]") + else tgui_alert_async(usr, "For some reason there's a ticker, but not a game mode") diff --git a/code/modules/admin/secrets/admin_secrets/show_law_changes.dm b/code/modules/admin/secrets/admin_secrets/show_law_changes.dm index 877e2c217de..67fc3d357f0 100644 --- a/code/modules/admin/secrets/admin_secrets/show_law_changes.dm +++ b/code/modules/admin/secrets/admin_secrets/show_law_changes.dm @@ -1,15 +1,15 @@ -/datum/admin_secret_item/admin_secret/show_law_changes - name = "Show law changes" - -/datum/admin_secret_item/admin_secret/show_law_changes/name() - return "Show Last [length(lawchanges)] Law change\s" - -/datum/admin_secret_item/admin_secret/show_law_changes/execute(var/mob/user) - . = ..() - if(!.) - return - - var/dat = "Showing last [length(lawchanges)] law changes.
                    " - for(var/sig in lawchanges) - dat += "[sig]
                    " - user << browse(dat, "window=lawchanges;size=800x500") +/datum/admin_secret_item/admin_secret/show_law_changes + name = "Show law changes" + +/datum/admin_secret_item/admin_secret/show_law_changes/name() + return "Show Last [length(lawchanges)] Law change\s" + +/datum/admin_secret_item/admin_secret/show_law_changes/execute(var/mob/user) + . = ..() + if(!.) + return + + var/dat = "Showing last [length(lawchanges)] law changes.
                    " + for(var/sig in lawchanges) + dat += "[sig]
                    " + user << browse(dat, "window=lawchanges;size=800x500") diff --git a/code/modules/admin/secrets/admin_secrets/show_signalers.dm b/code/modules/admin/secrets/admin_secrets/show_signalers.dm index 32a77cbef2e..4b0117a88f7 100644 --- a/code/modules/admin/secrets/admin_secrets/show_signalers.dm +++ b/code/modules/admin/secrets/admin_secrets/show_signalers.dm @@ -1,15 +1,15 @@ -/datum/admin_secret_item/admin_secret/show_signalers - name = "Show Last Signalers" - -/datum/admin_secret_item/admin_secret/show_signalers/name() - return "Show Last [length(lastsignalers)] Signaler\s" - -/datum/admin_secret_item/admin_secret/show_signalers/execute(var/mob/user) - . = ..() - if(!.) - return - - var/dat = "Showing last [length(lastsignalers)] signalers.
                    " - for(var/sig in lastsignalers) - dat += "[sig]
                    " - user << browse(dat, "window=lastsignalers;size=800x500") +/datum/admin_secret_item/admin_secret/show_signalers + name = "Show Last Signalers" + +/datum/admin_secret_item/admin_secret/show_signalers/name() + return "Show Last [length(lastsignalers)] Signaler\s" + +/datum/admin_secret_item/admin_secret/show_signalers/execute(var/mob/user) + . = ..() + if(!.) + return + + var/dat = "Showing last [length(lastsignalers)] signalers.
                    " + for(var/sig in lastsignalers) + dat += "[sig]
                    " + user << browse(dat, "window=lastsignalers;size=800x500") diff --git a/code/modules/admin/secrets/admin_secrets/traitors_and_objectives.dm b/code/modules/admin/secrets/admin_secrets/traitors_and_objectives.dm index 591c13bd3fb..bcfd9cd7b9c 100644 --- a/code/modules/admin/secrets/admin_secrets/traitors_and_objectives.dm +++ b/code/modules/admin/secrets/admin_secrets/traitors_and_objectives.dm @@ -1,7 +1,7 @@ -/datum/admin_secret_item/admin_secret/traitors_and_objectives - name = "Show current traitors and objectives" - -/datum/admin_secret_item/admin_secret/traitors_and_objectives/execute(var/mob/user) - . = ..() - if(.) - user.client.holder.check_antagonists() +/datum/admin_secret_item/admin_secret/traitors_and_objectives + name = "Show current traitors and objectives" + +/datum/admin_secret_item/admin_secret/traitors_and_objectives/execute(var/mob/user) + . = ..() + if(.) + user.client.holder.check_antagonists() diff --git a/code/modules/admin/secrets/final_solutions/summon_narsie.dm b/code/modules/admin/secrets/final_solutions/summon_narsie.dm index 0a382d71d7c..738c56e6746 100644 --- a/code/modules/admin/secrets/final_solutions/summon_narsie.dm +++ b/code/modules/admin/secrets/final_solutions/summon_narsie.dm @@ -1,11 +1,11 @@ -/datum/admin_secret_item/final_solution/summon_narsie - name = "Summon Nar-Sie" - -/datum/admin_secret_item/final_solution/summon_narsie/execute(var/mob/user) - . = ..() - if(!.) - return - var/choice = tgui_alert(user, "You sure you want to end the round and summon Nar-Sie at your location? Misuse of this could result in removal of flags or hilarity.","WARNING!",list("PRAISE SATAN", "Cancel")) - if(choice == "PRAISE SATAN") - new /obj/singularity/narsie/large(get_turf(user)) - log_and_message_admins("has summoned Nar-Sie and brought about a new realm of suffering.", user) +/datum/admin_secret_item/final_solution/summon_narsie + name = "Summon Nar-Sie" + +/datum/admin_secret_item/final_solution/summon_narsie/execute(var/mob/user) + . = ..() + if(!.) + return + var/choice = tgui_alert(user, "You sure you want to end the round and summon Nar-Sie at your location? Misuse of this could result in removal of flags or hilarity.","WARNING!",list("PRAISE SATAN", "Cancel")) + if(choice == "PRAISE SATAN") + new /obj/singularity/narsie/large(get_turf(user)) + log_and_message_admins("has summoned Nar-Sie and brought about a new realm of suffering.", user) diff --git a/code/modules/admin/secrets/final_solutions/supermatter_cascade.dm b/code/modules/admin/secrets/final_solutions/supermatter_cascade.dm index ca7f0598d59..b7640771797 100644 --- a/code/modules/admin/secrets/final_solutions/supermatter_cascade.dm +++ b/code/modules/admin/secrets/final_solutions/supermatter_cascade.dm @@ -1,13 +1,13 @@ -/datum/admin_secret_item/final_solution/supermatter_cascade - name = "Supermatter Cascade" - -/datum/admin_secret_item/final_solution/supermatter_cascade/execute(var/mob/user) - . = ..() - if(!.) - return - var/choice = tgui_alert(user, "You sure you want to destroy the universe and create a large explosion at your location? Misuse of this could result in removal of flags or hilarity.","WARNING!", list("NO TIME TO EXPLAIN", "Cancel")) - if(choice == "NO TIME TO EXPLAIN") - explosion(get_turf(user), 8, 16, 24, 32, 1) - new /turf/unsimulated/wall/supermatter(get_turf(user)) - SetUniversalState(/datum/universal_state/supermatter_cascade) - message_admins("[key_name_admin(user)] has managed to destroy the universe with a supermatter cascade. Good job, [key_name_admin(user)]") +/datum/admin_secret_item/final_solution/supermatter_cascade + name = "Supermatter Cascade" + +/datum/admin_secret_item/final_solution/supermatter_cascade/execute(var/mob/user) + . = ..() + if(!.) + return + var/choice = tgui_alert(user, "You sure you want to destroy the universe and create a large explosion at your location? Misuse of this could result in removal of flags or hilarity.","WARNING!", list("NO TIME TO EXPLAIN", "Cancel")) + if(choice == "NO TIME TO EXPLAIN") + explosion(get_turf(user), 8, 16, 24, 32, 1) + new /turf/unsimulated/wall/supermatter(get_turf(user)) + SetUniversalState(/datum/universal_state/supermatter_cascade) + message_admins("[key_name_admin(user)] has managed to destroy the universe with a supermatter cascade. Good job, [key_name_admin(user)]") diff --git a/code/modules/admin/secrets/fun_secrets/break_all_lights.dm b/code/modules/admin/secrets/fun_secrets/break_all_lights.dm index ea54952a7e1..2256d4f7168 100644 --- a/code/modules/admin/secrets/fun_secrets/break_all_lights.dm +++ b/code/modules/admin/secrets/fun_secrets/break_all_lights.dm @@ -1,7 +1,7 @@ -/datum/admin_secret_item/fun_secret/break_all_lights - name = "Break All Lights" - -/datum/admin_secret_item/fun_secret/break_all_lights/execute(var/mob/user) - . = ..() - if(.) - lightsout(0,0) +/datum/admin_secret_item/fun_secret/break_all_lights + name = "Break All Lights" + +/datum/admin_secret_item/fun_secret/break_all_lights/execute(var/mob/user) + . = ..() + if(.) + lightsout(0,0) diff --git a/code/modules/admin/secrets/fun_secrets/break_some_lights.dm b/code/modules/admin/secrets/fun_secrets/break_some_lights.dm index 0c9aae1b166..70b9eaf8061 100644 --- a/code/modules/admin/secrets/fun_secrets/break_some_lights.dm +++ b/code/modules/admin/secrets/fun_secrets/break_some_lights.dm @@ -1,7 +1,7 @@ -/datum/admin_secret_item/fun_secret/break_some_lights - name = "Break Some Lights" - -/datum/admin_secret_item/fun_secret/break_some_lights/execute(var/mob/user) - . = ..() - if(.) - lightsout(1,2) +/datum/admin_secret_item/fun_secret/break_some_lights + name = "Break Some Lights" + +/datum/admin_secret_item/fun_secret/break_some_lights/execute(var/mob/user) + . = ..() + if(.) + lightsout(1,2) diff --git a/code/modules/admin/secrets/fun_secrets/fix_all_lights.dm b/code/modules/admin/secrets/fun_secrets/fix_all_lights.dm index 622c68d2ea9..e0525a72b3e 100644 --- a/code/modules/admin/secrets/fun_secrets/fix_all_lights.dm +++ b/code/modules/admin/secrets/fun_secrets/fix_all_lights.dm @@ -1,10 +1,10 @@ -/datum/admin_secret_item/fun_secret/fix_all_lights - name = "Fix All Lights" - -/datum/admin_secret_item/fun_secret/fix_all_lights/execute(var/mob/user) - . = ..() - if(!.) - return - - for(var/obj/machinery/light/L in machines) - L.fix() +/datum/admin_secret_item/fun_secret/fix_all_lights + name = "Fix All Lights" + +/datum/admin_secret_item/fun_secret/fix_all_lights/execute(var/mob/user) + . = ..() + if(!.) + return + + for(var/obj/machinery/light/L in machines) + L.fix() diff --git a/code/modules/admin/secrets/fun_secrets/ghost_mode.dm b/code/modules/admin/secrets/fun_secrets/ghost_mode.dm index c59a9c05891..a3a5060047a 100644 --- a/code/modules/admin/secrets/fun_secrets/ghost_mode.dm +++ b/code/modules/admin/secrets/fun_secrets/ghost_mode.dm @@ -1,48 +1,48 @@ -/datum/admin_secret_item/fun_secret/ghost_mode - name = "Ghost Mode" - var/list/affected_mobs - -/datum/admin_secret_item/fun_secret/ghost_mode/New() - ..() - affected_mobs = list() - -/datum/admin_secret_item/fun_secret/ghost_mode/execute(var/mob/user) - . = ..() - if(!.) - return - - var/list/affected_areas = list() - for(var/mob/M in living_mob_list) - if(M.stat == CONSCIOUS && !(M in affected_mobs)) - affected_mobs |= M - switch(rand(1,4)) - if(1) - M.show_message(text("You shudder as if cold..."), 1) - if(2) - M.show_message(text("You feel something gliding across your back..."), 1) - if(3) - M.show_message(text("Your eyes twitch, you feel like something you can't see is here..."), 1) - if(4) - M.show_message(text("You notice something moving out of the corner of your eye, but nothing is there..."), 1) - - for(var/obj/W in orange(5,M)) - if(prob(25) && !W.anchored) - step_rand(W) - - var/area/A = get_area(M) - if(A.requires_power && !A.always_unpowered && A.power_light && (A.z in using_map.player_levels)) - affected_areas |= get_area(M) - - affected_mobs |= user - for(var/area/AffectedArea in affected_areas) - AffectedArea.power_light = 0 - AffectedArea.power_change() - spawn(rand(25,50)) - AffectedArea.power_light = 1 - AffectedArea.power_change() - - sleep(100) - for(var/mob/M in affected_mobs) - M.show_message(text("The chilling wind suddenly stops..."), 1) - affected_mobs.Cut() - affected_areas.Cut() +/datum/admin_secret_item/fun_secret/ghost_mode + name = "Ghost Mode" + var/list/affected_mobs + +/datum/admin_secret_item/fun_secret/ghost_mode/New() + ..() + affected_mobs = list() + +/datum/admin_secret_item/fun_secret/ghost_mode/execute(var/mob/user) + . = ..() + if(!.) + return + + var/list/affected_areas = list() + for(var/mob/M in living_mob_list) + if(M.stat == CONSCIOUS && !(M in affected_mobs)) + affected_mobs |= M + switch(rand(1,4)) + if(1) + M.show_message(text("You shudder as if cold..."), 1) + if(2) + M.show_message(text("You feel something gliding across your back..."), 1) + if(3) + M.show_message(text("Your eyes twitch, you feel like something you can't see is here..."), 1) + if(4) + M.show_message(text("You notice something moving out of the corner of your eye, but nothing is there..."), 1) + + for(var/obj/W in orange(5,M)) + if(prob(25) && !W.anchored) + step_rand(W) + + var/area/A = get_area(M) + if(A.requires_power && !A.always_unpowered && A.power_light && (A.z in using_map.player_levels)) + affected_areas |= get_area(M) + + affected_mobs |= user + for(var/area/AffectedArea in affected_areas) + AffectedArea.power_light = 0 + AffectedArea.power_change() + spawn(rand(25,50)) + AffectedArea.power_light = 1 + AffectedArea.power_change() + + sleep(100) + for(var/mob/M in affected_mobs) + M.show_message(text("The chilling wind suddenly stops..."), 1) + affected_mobs.Cut() + affected_areas.Cut() diff --git a/code/modules/admin/secrets/fun_secrets/only_one.dm b/code/modules/admin/secrets/fun_secrets/only_one.dm index bc0c962b29b..c065187c3ff 100644 --- a/code/modules/admin/secrets/fun_secrets/only_one.dm +++ b/code/modules/admin/secrets/fun_secrets/only_one.dm @@ -1,7 +1,7 @@ -/datum/admin_secret_item/fun_secret/only_one - name = "There Can Be Only One" - -/datum/admin_secret_item/fun_secret/only_one/execute(var/mob/user) - . = ..() - if(.) - only_one() +/datum/admin_secret_item/fun_secret/only_one + name = "There Can Be Only One" + +/datum/admin_secret_item/fun_secret/only_one/execute(var/mob/user) + . = ..() + if(.) + only_one() diff --git a/code/modules/admin/secrets/fun_secrets/paintball_mode.dm b/code/modules/admin/secrets/fun_secrets/paintball_mode.dm index 16438ad195a..d17d648ea9d 100644 --- a/code/modules/admin/secrets/fun_secrets/paintball_mode.dm +++ b/code/modules/admin/secrets/fun_secrets/paintball_mode.dm @@ -1,14 +1,14 @@ -/datum/admin_secret_item/fun_secret/paintbal_mode - name = "Paintball Mode" - -/datum/admin_secret_item/fun_secret/paintbal_mode/execute(var/mob/user) - . = ..() - if(!.) - return - - for(var/species in GLOB.all_species) - var/datum/species/S = GLOB.all_species[species] - S.blood_color = "rainbow" - for(var/obj/effect/decal/cleanable/blood/B in world) - B.basecolor = "rainbow" - B.update_icon() +/datum/admin_secret_item/fun_secret/paintbal_mode + name = "Paintball Mode" + +/datum/admin_secret_item/fun_secret/paintbal_mode/execute(var/mob/user) + . = ..() + if(!.) + return + + for(var/species in GLOB.all_species) + var/datum/species/S = GLOB.all_species[species] + S.blood_color = "rainbow" + for(var/obj/effect/decal/cleanable/blood/B in world) + B.basecolor = "rainbow" + B.update_icon() diff --git a/code/modules/admin/secrets/fun_secrets/power_all_smes.dm b/code/modules/admin/secrets/fun_secrets/power_all_smes.dm index 277edd5a438..2320bcf2cb1 100644 --- a/code/modules/admin/secrets/fun_secrets/power_all_smes.dm +++ b/code/modules/admin/secrets/fun_secrets/power_all_smes.dm @@ -1,7 +1,7 @@ -/datum/admin_secret_item/fun_secret/power_all_smes - name = "Power All SMES" - -/datum/admin_secret_item/fun_secret/power_all_smes/execute(var/mob/user) - . = ..() - if(.) - power_restore_quick() +/datum/admin_secret_item/fun_secret/power_all_smes + name = "Power All SMES" + +/datum/admin_secret_item/fun_secret/power_all_smes/execute(var/mob/user) + . = ..() + if(.) + power_restore_quick() diff --git a/code/modules/admin/secrets/fun_secrets/remove_all_clothing.dm b/code/modules/admin/secrets/fun_secrets/remove_all_clothing.dm index 28e9497f53f..ef0e40a33ad 100644 --- a/code/modules/admin/secrets/fun_secrets/remove_all_clothing.dm +++ b/code/modules/admin/secrets/fun_secrets/remove_all_clothing.dm @@ -1,10 +1,10 @@ -/datum/admin_secret_item/fun_secret/remove_all_clothing - name = "Remove ALL Clothing" - -/datum/admin_secret_item/fun_secret/remove_all_clothing/execute(var/mob/user) - . = ..() - if(!.) - return - - for(var/obj/item/clothing/O in world) - qdel(O) +/datum/admin_secret_item/fun_secret/remove_all_clothing + name = "Remove ALL Clothing" + +/datum/admin_secret_item/fun_secret/remove_all_clothing/execute(var/mob/user) + . = ..() + if(!.) + return + + for(var/obj/item/clothing/O in world) + qdel(O) diff --git a/code/modules/admin/secrets/fun_secrets/remove_internal_clothing.dm b/code/modules/admin/secrets/fun_secrets/remove_internal_clothing.dm index 73dba4ce4f9..9de30a0a5aa 100644 --- a/code/modules/admin/secrets/fun_secrets/remove_internal_clothing.dm +++ b/code/modules/admin/secrets/fun_secrets/remove_internal_clothing.dm @@ -1,10 +1,10 @@ -/datum/admin_secret_item/fun_secret/remove_internal_clothing - name = "Remove 'Internal' Clothing" - -/datum/admin_secret_item/fun_secret/remove_internal_clothing/execute(var/mob/user) - . = ..() - if(!.) - return - - for(var/obj/item/clothing/under/O in world) - qdel(O) +/datum/admin_secret_item/fun_secret/remove_internal_clothing + name = "Remove 'Internal' Clothing" + +/datum/admin_secret_item/fun_secret/remove_internal_clothing/execute(var/mob/user) + . = ..() + if(!.) + return + + for(var/obj/item/clothing/under/O in world) + qdel(O) diff --git a/code/modules/admin/secrets/fun_secrets/send_strike_team.dm b/code/modules/admin/secrets/fun_secrets/send_strike_team.dm index 3ec1bacb2c0..9aee563d22e 100644 --- a/code/modules/admin/secrets/fun_secrets/send_strike_team.dm +++ b/code/modules/admin/secrets/fun_secrets/send_strike_team.dm @@ -1,11 +1,11 @@ -/datum/admin_secret_item/fun_secret/send_strike_team - name = "Send Strike Team" - -/datum/admin_secret_item/fun_secret/send_strike_team/can_execute(var/mob/user) - if(!ticker) return 0 - return ..() - -/datum/admin_secret_item/fun_secret/send_strike_team/execute(var/mob/user) - . = ..() - if(.) - return user.client.strike_team() +/datum/admin_secret_item/fun_secret/send_strike_team + name = "Send Strike Team" + +/datum/admin_secret_item/fun_secret/send_strike_team/can_execute(var/mob/user) + if(!ticker) return 0 + return ..() + +/datum/admin_secret_item/fun_secret/send_strike_team/execute(var/mob/user) + . = ..() + if(.) + return user.client.strike_team() diff --git a/code/modules/admin/secrets/fun_secrets/toggle_bomb_cap.dm b/code/modules/admin/secrets/fun_secrets/toggle_bomb_cap.dm index 33ba8a861ce..6bcf2ee9b3c 100644 --- a/code/modules/admin/secrets/fun_secrets/toggle_bomb_cap.dm +++ b/code/modules/admin/secrets/fun_secrets/toggle_bomb_cap.dm @@ -1,21 +1,21 @@ -/datum/admin_secret_item/fun_secret/toggle_bomb_cap - name = "Toggle Bomb Cap" - permissions = R_SERVER - -/datum/admin_secret_item/fun_secret/toggle_bomb_cap/execute(var/mob/user) - . = ..() - if(!.) - return - - switch(max_explosion_range) - if(14) max_explosion_range = 16 - if(16) max_explosion_range = 20 - if(20) max_explosion_range = 28 - if(28) max_explosion_range = 56 - if(56) max_explosion_range = 128 - if(128) max_explosion_range = 14 - var/range_dev = max_explosion_range *0.25 - var/range_high = max_explosion_range *0.5 - var/range_low = max_explosion_range - message_admins("[key_name_admin(user)] changed the bomb cap to [range_dev], [range_high], [range_low]", 1) - log_admin("[key_name_admin(user)] changed the bomb cap to [max_explosion_range]") +/datum/admin_secret_item/fun_secret/toggle_bomb_cap + name = "Toggle Bomb Cap" + permissions = R_SERVER + +/datum/admin_secret_item/fun_secret/toggle_bomb_cap/execute(var/mob/user) + . = ..() + if(!.) + return + + switch(max_explosion_range) + if(14) max_explosion_range = 16 + if(16) max_explosion_range = 20 + if(20) max_explosion_range = 28 + if(28) max_explosion_range = 56 + if(56) max_explosion_range = 128 + if(128) max_explosion_range = 14 + var/range_dev = max_explosion_range *0.25 + var/range_high = max_explosion_range *0.5 + var/range_low = max_explosion_range + message_admins("[key_name_admin(user)] changed the bomb cap to [range_dev], [range_high], [range_low]", 1) + log_admin("[key_name_admin(user)] changed the bomb cap to [max_explosion_range]") diff --git a/code/modules/admin/secrets/fun_secrets/triple_ai_mode.dm b/code/modules/admin/secrets/fun_secrets/triple_ai_mode.dm index 81b77eabb64..d6819530a42 100644 --- a/code/modules/admin/secrets/fun_secrets/triple_ai_mode.dm +++ b/code/modules/admin/secrets/fun_secrets/triple_ai_mode.dm @@ -1,13 +1,13 @@ -/datum/admin_secret_item/fun_secret/triple_ai_mode - name = "Triple AI Mode" - -/datum/admin_secret_item/fun_secret/triple_ai_mode/can_execute(var/mob/user) - if(ticker && ticker.current_state > GAME_STATE_PREGAME) - return 0 - - return ..() - -/datum/admin_secret_item/admin_secret/triple_ai_mode/execute(var/mob/user) - . = ..() - if(.) - user.client.triple_ai() +/datum/admin_secret_item/fun_secret/triple_ai_mode + name = "Triple AI Mode" + +/datum/admin_secret_item/fun_secret/triple_ai_mode/can_execute(var/mob/user) + if(ticker && ticker.current_state > GAME_STATE_PREGAME) + return 0 + + return ..() + +/datum/admin_secret_item/admin_secret/triple_ai_mode/execute(var/mob/user) + . = ..() + if(.) + user.client.triple_ai() diff --git a/code/modules/admin/secrets/fun_secrets/turn_humans_into_corgies.dm b/code/modules/admin/secrets/fun_secrets/turn_humans_into_corgies.dm index ae6f36a1ac1..c82a07c96a6 100644 --- a/code/modules/admin/secrets/fun_secrets/turn_humans_into_corgies.dm +++ b/code/modules/admin/secrets/fun_secrets/turn_humans_into_corgies.dm @@ -1,11 +1,11 @@ -/datum/admin_secret_item/fun_secret/turn_humans_into_corgies - name = "Turn All Humans Into Corgies" - -/datum/admin_secret_item/fun_secret/turn_humans_into_corgies/execute(var/mob/user) - . = ..() - if(!.) - return - - for(var/mob/living/carbon/human/H in mob_list) - spawn(0) - H.corgize() +/datum/admin_secret_item/fun_secret/turn_humans_into_corgies + name = "Turn All Humans Into Corgies" + +/datum/admin_secret_item/fun_secret/turn_humans_into_corgies/execute(var/mob/user) + . = ..() + if(!.) + return + + for(var/mob/living/carbon/human/H in mob_list) + spawn(0) + H.corgize() diff --git a/code/modules/admin/secrets/fun_secrets/turn_humans_into_monkeys.dm b/code/modules/admin/secrets/fun_secrets/turn_humans_into_monkeys.dm index 99b1573adef..88e5bd9022f 100644 --- a/code/modules/admin/secrets/fun_secrets/turn_humans_into_monkeys.dm +++ b/code/modules/admin/secrets/fun_secrets/turn_humans_into_monkeys.dm @@ -1,11 +1,11 @@ -/datum/admin_secret_item/fun_secret/turn_humans_into_monkeys - name = "Turn All Humans Into Monkeys" - -/datum/admin_secret_item/fun_secret/turn_humans_into_monkeys/execute(var/mob/user) - . = ..() - if(!.) - return - - for(var/mob/living/carbon/human/H in mob_list) - spawn(0) - H.monkeyize() +/datum/admin_secret_item/fun_secret/turn_humans_into_monkeys + name = "Turn All Humans Into Monkeys" + +/datum/admin_secret_item/fun_secret/turn_humans_into_monkeys/execute(var/mob/user) + . = ..() + if(!.) + return + + for(var/mob/living/carbon/human/H in mob_list) + spawn(0) + H.monkeyize() diff --git a/code/modules/admin/secrets/random_events/gravity.dm b/code/modules/admin/secrets/random_events/gravity.dm index b01bb496374..32c31827ab2 100644 --- a/code/modules/admin/secrets/random_events/gravity.dm +++ b/code/modules/admin/secrets/random_events/gravity.dm @@ -1,31 +1,31 @@ -/********** -* Gravity * -**********/ -/datum/admin_secret_item/random_event/gravity - name = "Toggle Station Artificial Gravity" - -/datum/admin_secret_item/random_event/gravity/can_execute(var/mob/user) - if(!(ticker && ticker.mode)) - return 0 - - return ..() - -/datum/admin_secret_item/random_event/gravity/execute(var/mob/user) - . = ..() - if(!.) - return - - gravity_is_on = !gravity_is_on - for(var/area/A in world) - A.gravitychange(gravity_is_on) - - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","Grav") - if(gravity_is_on) - log_admin("[key_name(user)] toggled gravity on.", 1) - message_admins("[key_name_admin(user)] toggled gravity on.", 1) - command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.") - else - log_admin("[key_name(user)] toggled gravity off.", 1) - message_admins("[key_name_admin(usr)] toggled gravity off.", 1) - command_announcement.Announce("Feedback surge detected in mass-distributions systems. Artificial gravity has been disabled whilst the system reinitializes. Further failures may result in a gravitational collapse and formation of blackholes. Have a nice day.") +/********** +* Gravity * +**********/ +/datum/admin_secret_item/random_event/gravity + name = "Toggle Station Artificial Gravity" + +/datum/admin_secret_item/random_event/gravity/can_execute(var/mob/user) + if(!(ticker && ticker.mode)) + return 0 + + return ..() + +/datum/admin_secret_item/random_event/gravity/execute(var/mob/user) + . = ..() + if(!.) + return + + gravity_is_on = !gravity_is_on + for(var/area/A in world) + A.gravitychange(gravity_is_on) + + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","Grav") + if(gravity_is_on) + log_admin("[key_name(user)] toggled gravity on.", 1) + message_admins("[key_name_admin(user)] toggled gravity on.", 1) + command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.") + else + log_admin("[key_name(user)] toggled gravity off.", 1) + message_admins("[key_name_admin(usr)] toggled gravity off.", 1) + command_announcement.Announce("Feedback surge detected in mass-distributions systems. Artificial gravity has been disabled whilst the system reinitializes. Further failures may result in a gravitational collapse and formation of blackholes. Have a nice day.") diff --git a/code/modules/admin/secrets/random_events/trigger_cordical_borer_infestation.dm b/code/modules/admin/secrets/random_events/trigger_cordical_borer_infestation.dm index f96004565d8..d7e09d845e6 100644 --- a/code/modules/admin/secrets/random_events/trigger_cordical_borer_infestation.dm +++ b/code/modules/admin/secrets/random_events/trigger_cordical_borer_infestation.dm @@ -1,7 +1,7 @@ -/datum/admin_secret_item/random_event/trigger_cordical_borer_infestation - name = "Trigger a Cortical Borer infestation" - -/datum/admin_secret_item/random_event/trigger_cordical_borer_infestation/execute(var/mob/user) - . = ..() - if(.) - return borers.attempt_random_spawn() +/datum/admin_secret_item/random_event/trigger_cordical_borer_infestation + name = "Trigger a Cortical Borer infestation" + +/datum/admin_secret_item/random_event/trigger_cordical_borer_infestation/execute(var/mob/user) + . = ..() + if(.) + return borers.attempt_random_spawn() diff --git a/code/modules/admin/verbs/BrokenInhands.dm b/code/modules/admin/verbs/BrokenInhands.dm index 2ddfd393f41..b74750843ce 100644 --- a/code/modules/admin/verbs/BrokenInhands.dm +++ b/code/modules/admin/verbs/BrokenInhands.dm @@ -1,36 +1,36 @@ -/proc/getbrokeninhands() - var/icon/IL = new('icons/mob/items/lefthand.dmi') - var/list/Lstates = IL.IconStates() - var/icon/IR = new('icons/mob/items/righthand.dmi') - var/list/Rstates = IR.IconStates() - - - var/text - for(var/A in typesof(/obj/item)) - var/obj/item/O = new A( locate(1,1,1) ) - if(!O) continue - var/icon/J = new(O.icon) - var/list/istates = J.IconStates() - if(!Lstates.Find(O.icon_state) && !Lstates.Find(O.item_state)) - if(O.icon_state) - text += "[O.type] is missing left hand icon called \"[O.icon_state]\".\n" - if(!Rstates.Find(O.icon_state) && !Rstates.Find(O.item_state)) - if(O.icon_state) - text += "[O.type] is missing right hand icon called \"[O.icon_state]\".\n" - - - if(O.icon_state) - if(!istates.Find(O.icon_state)) - text += "[O.type] is missing normal icon called \"[O.icon_state]\" in \"[O.icon]\".\n" - //if(O.item_state) - // if(!istates.Find(O.item_state)) - // text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.item_state]\" IN \"[O.icon]\"\n" - //text+="\n" - qdel(O) - if(text) - var/F = file("broken_icons.txt") - fdel(F) - F << text - to_world("Completeled successfully and written to [F]") - - +/proc/getbrokeninhands() + var/icon/IL = new('icons/mob/items/lefthand.dmi') + var/list/Lstates = IL.IconStates() + var/icon/IR = new('icons/mob/items/righthand.dmi') + var/list/Rstates = IR.IconStates() + + + var/text + for(var/A in typesof(/obj/item)) + var/obj/item/O = new A( locate(1,1,1) ) + if(!O) continue + var/icon/J = new(O.icon) + var/list/istates = J.IconStates() + if(!Lstates.Find(O.icon_state) && !Lstates.Find(O.item_state)) + if(O.icon_state) + text += "[O.type] is missing left hand icon called \"[O.icon_state]\".\n" + if(!Rstates.Find(O.icon_state) && !Rstates.Find(O.item_state)) + if(O.icon_state) + text += "[O.type] is missing right hand icon called \"[O.icon_state]\".\n" + + + if(O.icon_state) + if(!istates.Find(O.icon_state)) + text += "[O.type] is missing normal icon called \"[O.icon_state]\" in \"[O.icon]\".\n" + //if(O.item_state) + // if(!istates.Find(O.item_state)) + // text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.item_state]\" IN \"[O.icon]\"\n" + //text+="\n" + qdel(O) + if(text) + var/F = file("broken_icons.txt") + fdel(F) + F << text + to_world("Completeled successfully and written to [F]") + + diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index 5c8be024d59..041f6478d8c 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -1,63 +1,63 @@ -/client/proc/cmd_admin_say(msg as text) - set category = "Special Verbs" - set name = "Asay" //Gave this shit a shorter name so you only have to time out "asay" rather than "admin say" to use it --NeoFite - set hidden = 1 - if(!check_rights(R_ADMIN)) //VOREStation Edit - return - - msg = sanitize(msg) - if(!msg) - return - - log_adminsay(msg,src) - - for(var/client/C in GLOB.admins) - if(check_rights(R_ADMIN, 0, C)) - to_chat(C, "" + create_text_tag("admin", "ADMIN:", C) + " [key_name(usr, 1)]([admin_jump_link(mob, src)]): [msg]") - - feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_mod_say(msg as text) - set category = "Special Verbs" - set name = "Msay" - set hidden = 1 - - if(!check_rights(R_ADMIN|R_MOD|R_SERVER)) //VOREStation Edit - return - - msg = sanitize(msg) - log_modsay(msg,src) - - if (!msg) - return - - var/sender_name = key_name(usr, 1) - if(check_rights(R_ADMIN, 0)) - sender_name = "[sender_name]" - for(var/client/C in GLOB.admins) - if(check_rights(R_ADMIN|R_MOD|R_SERVER, 0, C)) //VOREStation Edit //YW Edit, fix issue with C missing from check_rights, so all admins could see mchat - to_chat(C, "" + create_text_tag("mod", "MOD:", C) + " [sender_name]([admin_jump_link(mob, C.holder)]): [msg]") - - feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_event_say(msg as text) - set category = "Special Verbs" - set name = "Esay" - set hidden = 1 - - if(!check_rights(R_ADMIN|R_MOD|R_EVENT|R_SERVER)) //VOREStation Edit - return - - msg = sanitize(msg) - log_eventsay(msg,src) - - if (!msg) - return - - var/sender_name = key_name(usr, 1) - if(check_rights(R_ADMIN, 0)) - sender_name = "[sender_name]" - for(var/client/C in GLOB.admins) - to_chat(C, "" + create_text_tag("event", "EVENT:", C) + " [sender_name]([admin_jump_link(mob, C.holder)]): [msg]") - - feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/client/proc/cmd_admin_say(msg as text) + set category = "Special Verbs" + set name = "Asay" //Gave this shit a shorter name so you only have to time out "asay" rather than "admin say" to use it --NeoFite + set hidden = 1 + if(!check_rights(R_ADMIN)) //VOREStation Edit + return + + msg = sanitize(msg) + if(!msg) + return + + log_adminsay(msg,src) + + for(var/client/C in GLOB.admins) + if(check_rights(R_ADMIN, 0, C)) + to_chat(C, "" + create_text_tag("admin", "ADMIN:", C) + " [key_name(usr, 1)]([admin_jump_link(mob, src)]): [msg]") + + feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_mod_say(msg as text) + set category = "Special Verbs" + set name = "Msay" + set hidden = 1 + + if(!check_rights(R_ADMIN|R_MOD|R_SERVER)) //VOREStation Edit + return + + msg = sanitize(msg) + log_modsay(msg,src) + + if (!msg) + return + + var/sender_name = key_name(usr, 1) + if(check_rights(R_ADMIN, 0)) + sender_name = "[sender_name]" + for(var/client/C in GLOB.admins) + if(check_rights(R_ADMIN|R_MOD|R_SERVER, 0, C)) //VOREStation Edit //YW Edit, fix issue with C missing from check_rights, so all admins could see mchat + to_chat(C, "" + create_text_tag("mod", "MOD:", C) + " [sender_name]([admin_jump_link(mob, C.holder)]): [msg]") + + feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_event_say(msg as text) + set category = "Special Verbs" + set name = "Esay" + set hidden = 1 + + if(!check_rights(R_ADMIN|R_MOD|R_EVENT|R_SERVER)) //VOREStation Edit + return + + msg = sanitize(msg) + log_eventsay(msg,src) + + if (!msg) + return + + var/sender_name = key_name(usr, 1) + if(check_rights(R_ADMIN, 0)) + sender_name = "[sender_name]" + for(var/client/C in GLOB.admins) + to_chat(C, "" + create_text_tag("event", "EVENT:", C) + " [sender_name]([admin_jump_link(mob, C.holder)]): [msg]") + + feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/atmosdebug.dm b/code/modules/admin/verbs/atmosdebug.dm index 3927c20899f..082f0e26b0e 100644 --- a/code/modules/admin/verbs/atmosdebug.dm +++ b/code/modules/admin/verbs/atmosdebug.dm @@ -1,60 +1,60 @@ -/client/proc/atmosscan() - set category = "Mapping" - set name = "Check Piping" - set background = 1 - if(!src.holder) - return - - feedback_add_details("admin_verb","CP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - if(tgui_alert(usr, "WARNING: This command should not be run on a live server. Do you want to continue?", "Check Piping", list("No", "Yes")) == "No") - return - - to_chat(usr, "Checking for disconnected pipes...") - //all plumbing - yes, some things might get stated twice, doesn't matter. - for (var/obj/machinery/atmospherics/plumbing in machines) - if (plumbing.nodealert) - to_chat(usr, "Unconnected [plumbing.name] located at [plumbing.x],[plumbing.y],[plumbing.z] ([get_area(plumbing.loc)])") - - //Manifolds - for (var/obj/machinery/atmospherics/pipe/manifold/pipe in machines) - if (!pipe.node1 || !pipe.node2 || !pipe.node3) - to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") - - //Pipes - for (var/obj/machinery/atmospherics/pipe/simple/pipe in machines) - if (!pipe.node1 || !pipe.node2) - to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") - - to_chat(usr, "Checking for overlapping pipes...") - next_turf: - for(var/turf/T in world) - for(var/dir in cardinal) - var/list/connect_types = list(1 = 0, 2 = 0, 3 = 0) - for(var/obj/machinery/atmospherics/pipe in T) - if(dir & pipe.initialize_directions) - for(var/connect_type in pipe.connect_types) - connect_types[connect_type] += 1 - if(connect_types[1] > 1 || connect_types[2] > 1 || connect_types[3] > 1) - to_chat(usr, "Overlapping pipe ([pipe.name]) located at [T.x],[T.y],[T.z] ([get_area(T)])") - continue next_turf - to_chat(usr, "Done") - -/client/proc/powerdebug() - set category = "Mapping" - set name = "Check Power" - if(!src.holder) - return - - feedback_add_details("admin_verb","CPOW") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - for (var/datum/powernet/PN in powernets) - if (!PN.nodes || !PN.nodes.len) - if(PN.cables && (PN.cables.len > 1)) - var/obj/structure/cable/C = PN.cables[1] - to_chat(usr, "Powernet with no nodes! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") - - if (!PN.cables || (PN.cables.len < 10)) - if(PN.cables && (PN.cables.len > 1)) - var/obj/structure/cable/C = PN.cables[1] +/client/proc/atmosscan() + set category = "Mapping" + set name = "Check Piping" + set background = 1 + if(!src.holder) + return + + feedback_add_details("admin_verb","CP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + if(tgui_alert(usr, "WARNING: This command should not be run on a live server. Do you want to continue?", "Check Piping", list("No", "Yes")) == "No") + return + + to_chat(usr, "Checking for disconnected pipes...") + //all plumbing - yes, some things might get stated twice, doesn't matter. + for (var/obj/machinery/atmospherics/plumbing in machines) + if (plumbing.nodealert) + to_chat(usr, "Unconnected [plumbing.name] located at [plumbing.x],[plumbing.y],[plumbing.z] ([get_area(plumbing.loc)])") + + //Manifolds + for (var/obj/machinery/atmospherics/pipe/manifold/pipe in machines) + if (!pipe.node1 || !pipe.node2 || !pipe.node3) + to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") + + //Pipes + for (var/obj/machinery/atmospherics/pipe/simple/pipe in machines) + if (!pipe.node1 || !pipe.node2) + to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") + + to_chat(usr, "Checking for overlapping pipes...") + next_turf: + for(var/turf/T in world) + for(var/dir in cardinal) + var/list/connect_types = list(1 = 0, 2 = 0, 3 = 0) + for(var/obj/machinery/atmospherics/pipe in T) + if(dir & pipe.initialize_directions) + for(var/connect_type in pipe.connect_types) + connect_types[connect_type] += 1 + if(connect_types[1] > 1 || connect_types[2] > 1 || connect_types[3] > 1) + to_chat(usr, "Overlapping pipe ([pipe.name]) located at [T.x],[T.y],[T.z] ([get_area(T)])") + continue next_turf + to_chat(usr, "Done") + +/client/proc/powerdebug() + set category = "Mapping" + set name = "Check Power" + if(!src.holder) + return + + feedback_add_details("admin_verb","CPOW") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + for (var/datum/powernet/PN in powernets) + if (!PN.nodes || !PN.nodes.len) + if(PN.cables && (PN.cables.len > 1)) + var/obj/structure/cable/C = PN.cables[1] + to_chat(usr, "Powernet with no nodes! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") + + if (!PN.cables || (PN.cables.len < 10)) + if(PN.cables && (PN.cables.len > 1)) + var/obj/structure/cable/C = PN.cables[1] to_chat(usr, "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") \ No newline at end of file diff --git a/code/modules/admin/verbs/buildmode.dm b/code/modules/admin/verbs/buildmode.dm index be587b6f412..283c6a5031e 100644 --- a/code/modules/admin/verbs/buildmode.dm +++ b/code/modules/admin/verbs/buildmode.dm @@ -1,845 +1,845 @@ -#define BUILDMODE_BASIC 1 -#define BUILDMODE_ADVANCED 2 -#define BUILDMODE_EDIT 3 -#define BUILDMODE_THROW 4 -#define BUILDMODE_ROOM 5 -#define BUILDMODE_LADDER 6 -#define BUILDMODE_CONTENTS 7 -#define BUILDMODE_LIGHTS 8 -#define BUILDMODE_AI 9 - -#define LAST_BUILDMODE 9 - -/proc/togglebuildmode(mob/M as mob in player_list) - set name = "Toggle Build Mode" - set category = "Special Verbs" - if(M.client) - if(M.client.buildmode) - log_admin("[key_name(usr)] has left build mode.") - M.client.buildmode = 0 - M.client.show_popup_menus = 1 - M.plane_holder.set_vis(VIS_BUILDMODE, FALSE) - for(var/obj/effect/bmode/buildholder/H) - if(H.cl == M.client) - qdel(H) - else - log_admin("[key_name(usr)] has entered build mode.") - M.client.buildmode = 1 - M.client.show_popup_menus = 0 - M.plane_holder.set_vis(VIS_BUILDMODE, TRUE) - - var/obj/effect/bmode/buildholder/H = new/obj/effect/bmode/buildholder() - var/obj/effect/bmode/builddir/A = new/obj/effect/bmode/builddir(H) - A.master = H - var/obj/effect/bmode/buildhelp/B = new/obj/effect/bmode/buildhelp(H) - B.master = H - var/obj/effect/bmode/buildmode/C = new/obj/effect/bmode/buildmode(H) - C.master = H - var/obj/effect/bmode/buildquit/D = new/obj/effect/bmode/buildquit(H) - D.master = H - - H.builddir = A - H.buildhelp = B - H.buildmode = C - H.buildquit = D - M.client.screen += A - M.client.screen += B - M.client.screen += C - M.client.screen += D - H.cl = M.client - -/obj/effect/bmode//Cleaning up the tree a bit - density = TRUE - anchored = TRUE - layer = LAYER_HUD_BASE - plane = PLANE_PLAYER_HUD - dir = NORTH - icon = 'icons/misc/buildmode.dmi' - var/obj/effect/bmode/buildholder/master = null - -/obj/effect/bmode/Destroy() - if(master && master.cl) - master.cl.screen -= src - master = null - return ..() - -/obj/effect/bmode/builddir - icon_state = "build" - screen_loc = "NORTH,WEST" - -/obj/effect/bmode/builddir/Click() - switch(dir) - if(NORTH) - set_dir(EAST) - if(EAST) - set_dir(SOUTH) - if(SOUTH) - set_dir(WEST) - if(WEST) - set_dir(NORTHWEST) - if(NORTHWEST) - set_dir(NORTH) - return 1 - -/obj/effect/bmode/buildhelp - icon = 'icons/misc/buildmode.dmi' - icon_state = "buildhelp" - screen_loc = "NORTH,WEST+1" - -/obj/effect/bmode/buildhelp/Click() - switch(master.cl.buildmode) - - if(BUILDMODE_BASIC) - to_chat(usr, "***********************************************************
                    \ - Left Mouse Button = Construct / Upgrade
                    \ - Right Mouse Button = Deconstruct / Delete / Downgrade
                    \ - Left Mouse Button + ctrl = R-Window
                    \ - Left Mouse Button + alt = Airlock

                    \ - Use the button in the upper left corner to
                    \ - change the direction of built objects.
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_ADVANCED) - to_chat(usr, "***********************************************************
                    \ - Right Mouse Button on buildmode button = Set object type
                    \ - Middle Mouse Button on buildmode button= On/Off object type saying
                    \ - Middle Mouse Button on turf/obj = Capture object type
                    \ - Left Mouse Button on turf/obj = Place objects
                    \ - Right Mouse Button = Delete objects
                    \ - Mouse Button + ctrl = Copy object type

                    \ - Use the button in the upper left corner to
                    \ - change the direction of built objects.
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_EDIT) - to_chat(usr, "***********************************************************
                    \ - Right Mouse Button on buildmode button = Select var(type) & value
                    \ - Left Mouse Button on turf/obj/mob = Set var(type) & value
                    \ - Right Mouse Button on turf/obj/mob = Reset var's value
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_THROW) - to_chat(usr, "***********************************************************
                    \ - Left Mouse Button on turf/obj/mob = Select
                    \ - Right Mouse Button on turf/obj/mob = Throw
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_ROOM) - to_chat(usr, "***********************************************************
                    \ - Left Mouse Button on turf = Select as point A
                    \ - Right Mouse Button on turf = Select as point B
                    \ - Right Mouse Button on buildmode button = Change floor/wall type/area name
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_LADDER) - to_chat(usr, "***********************************************************
                    \ - Left Mouse Button on turf = Set as upper ladder loc
                    \ - Right Mouse Button on turf = Set as lower ladder loc
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_CONTENTS) - to_chat(usr, "***********************************************************
                    \ - Left Mouse Button on turf/obj/mob = Select
                    \ - Right Mouse Button on turf/obj/mob = Move into selection
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_LIGHTS) - to_chat(usr, "***********************************************************
                    \ - Left Mouse Button on turf/obj/mob = Make it glow
                    \ - Right Mouse Button on turf/obj/mob = Reset glowing
                    \ - Right Mouse Button on buildmode button = Change glow properties
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_AI) - to_chat(usr, "***********************************************************
                    \ - Left Mouse Button drag box = Select only mobs in box
                    \ - Left Mouse Button drag box + shift = Select additional mobs in area
                    \ - Left Mouse Button on non-mob = Deselect all mobs
                    \ - Left Mouse Button on AI mob = Select/Deselect mob
                    \ - Left Mouse Button + alt on AI mob = Toggle hostility on mob
                    \ - Left Mouse Button + shift on AI mob = Toggle AI (also resets)
                    \ - Left Mouse Button + ctrl on AI mob = Copy mob faction
                    \ - Middle Mouse Button + alt on any atom = Add atom to entity narrate menu
                    \ - Middle Mouse Button + shift on any = Set selected mob(s) to wander
                    \ - Middle Mouse Button + ctrl on any = Set selected mob(s) to NOT wander
                    \ - Right Mouse Button + ctrl on any mob = Paste mob faction copied with Left Mouse Button + shift
                    \ - Right Mouse Button on enemy mob = Command selected mobs to attack mob
                    \ - Right Mouse Button on allied mob = Command selected mobs to follow mob
                    \ - Right Mouse Button + shift on any mob = Command selected mobs to follow mob regardless of faction
                    \ - Note: The following also reset the mob's home position:
                    \ - Right Mouse Button on tile = Command selected mobs to move to tile (will cancel if enemies are seen)
                    \ - Right Mouse Button + shift on tile = Command selected mobs to reposition to tile (will not be interrupted by enemies)
                    \ - Right Mouse Button + alt on obj/turfs = Command selected mobs to attack obj/turf
                    \ - ***********************************************************
                    ") - return 1 - -/obj/effect/bmode/buildquit - icon_state = "buildquit" - screen_loc = "NORTH,WEST+3" - -/obj/effect/bmode/buildquit/Click() - togglebuildmode(master.cl.mob) - return 1 - -/obj/effect/bmode/buildholder - density = FALSE - anchored = TRUE - var/client/cl = null - var/obj/effect/bmode/builddir/builddir = null - var/obj/effect/bmode/buildhelp/buildhelp = null - var/obj/effect/bmode/buildmode/buildmode = null - var/obj/effect/bmode/buildquit/buildquit = null - var/atom/movable/throw_atom = null - var/list/selected_mobs = list() - var/copied_faction = null - var/warned = 0 - -/obj/effect/bmode/buildholder/Destroy() - qdel(builddir) - builddir = null - qdel(buildhelp) - buildhelp = null - qdel(buildmode) - buildmode = null - qdel(buildquit) - buildquit = null - throw_atom = null - for(var/mob/living/unit in selected_mobs) - deselect_AI_mob(cl, unit) - selected_mobs.Cut() - cl = null - return ..() - -/obj/effect/bmode/buildholder/proc/select_AI_mob(client/C, mob/living/unit) - selected_mobs += unit - C.images += unit.selected_image - -/obj/effect/bmode/buildholder/proc/deselect_AI_mob(client/C, mob/living/unit) - selected_mobs -= unit - C.images -= unit.selected_image - -/obj/effect/bmode/buildmode - icon_state = "buildmode1" - screen_loc = "NORTH,WEST+2" - var/varholder = "name" - var/valueholder = "derp" - var/objholder = null - var/objsay = 1 - - var/wall_holder = /turf/simulated/wall - var/floor_holder = /turf/simulated/floor/plating - var/turf/coordA = null - var/turf/coordB = null - var/area_enabled = 0 - var/area_name = "New Area" - - var/new_light_color = "#FFFFFF" - var/new_light_range = 3 - var/new_light_intensity = 3 - -/obj/effect/bmode/buildmode/Click(location, control, params) - var/list/pa = params2list(params) - - if(pa.Find("middle")) - switch(master.cl.buildmode) - if(BUILDMODE_ADVANCED) - objsay=!objsay - - if(pa.Find("left")) - if(master.cl.buildmode == LAST_BUILDMODE) - master.cl.buildmode = 1 - else - master.cl.buildmode++ - src.icon_state = "buildmode[master.cl.buildmode]" - - else if(pa.Find("right")) - switch(master.cl.buildmode) - if(BUILDMODE_BASIC) - - return 1 - if(BUILDMODE_ADVANCED) - objholder = get_path_from_partial_text() - - if(BUILDMODE_EDIT) - var/list/locked = list("vars", "key", "ckey", "client", "firemut", "ishulk", "telekinesis", "xray", "virus", "viruses", "cuffed", "ka", "last_eaten", "urine") - - master.buildmode.varholder = tgui_input_text(usr,"Enter variable name:" ,"Name", "name") - if(master.buildmode.varholder in locked && !check_rights(R_DEBUG,0)) - return 1 - var/thetype = tgui_input_list(usr,"Select variable type:", "Type", list("text","number","mob-reference","obj-reference","turf-reference")) - if(!thetype) return 1 - switch(thetype) - if("text") - master.buildmode.valueholder = tgui_input_text(usr,"Enter variable value:" ,"Value", "value") - if("number") - master.buildmode.valueholder = tgui_input_number(usr,"Enter variable value:" ,"Value", 123) - if("mob-reference") - master.buildmode.valueholder = tgui_input_list(usr,"Enter variable value:", "Value", mob_list) - if("obj-reference") - master.buildmode.valueholder = tgui_input_list(usr,"Enter variable value:", "Value", world) - if("turf-reference") - master.buildmode.valueholder = tgui_input_list(usr,"Enter variable value:", "Value", world) - - if(BUILDMODE_ROOM) - var/area_choice = tgui_alert(usr, "Would you like to generate a new area as well?","Room Builder", list("No", "Yes")) - switch(area_choice) - if("No") - area_enabled = 0 - if("Yes") - area_enabled = 1 - area_name = tgui_input_text(usr, "New area name", "Room Buildmode", max_length = MAX_NAME_LEN) - if(isnull(area_name)) - to_chat(usr, "You must enter a non-null name.") - area_enabled = 0 - return - area_name = sanitize(area_name,MAX_NAME_LEN) - var/choice = tgui_alert(usr, "Would you like to change the floor or wall holders?","Room Builder", list("Floor", "Wall")) - switch(choice) - if("Floor") - floor_holder = get_path_from_partial_text(/turf/simulated/floor/plating) - if("Wall") - wall_holder = get_path_from_partial_text(/turf/simulated/wall) - - if(BUILDMODE_LIGHTS) - var/choice = tgui_alert(usr, "Change the new light range, power, or color?", "Light Maker", list("Range", "Power", "Color")) - switch(choice) - if("Range") - var/input = tgui_input_number(usr, "New light range.","Light Maker",3) - if(input) - new_light_range = input - if("Power") - var/input = tgui_input_number(usr, "New light power.","Light Maker",3) - if(input) - new_light_intensity = input - if("Color") - var/input = input(usr, "New light color.","Light Maker",3) as null|color - if(input) - new_light_color = input - return 1 - -/proc/build_click(var/mob/user, buildmode, params, var/obj/object) - var/obj/effect/bmode/buildholder/holder = null - for(var/obj/effect/bmode/buildholder/H) - if(H.cl == user.client) - holder = H - break - if(!holder) return - var/list/pa = params2list(params) - - switch(buildmode) - if(BUILDMODE_BASIC) - if(istype(object,/turf) && pa.Find("left") && !pa.Find("alt") && !pa.Find("ctrl") ) - if(istype(object,/turf/space)) - var/turf/T = object - T.ChangeTurf(/turf/simulated/floor) - return - else if(istype(object,/turf/simulated/floor)) - var/turf/T = object - T.ChangeTurf(/turf/simulated/wall) - return - else if(istype(object,/turf/simulated/wall)) - var/turf/T = object - T.ChangeTurf(/turf/simulated/wall/r_wall) - return - else if(pa.Find("right")) - if(istype(object,/turf/simulated/wall)) - var/turf/T = object - T.ChangeTurf(/turf/simulated/floor) - return - else if(istype(object,/turf/simulated/floor)) - var/turf/T = object - if(!holder.warned) - var/warning = tgui_alert(user, "Are you -sure- you want to delete this turf and make it the base turf for this Z level?", "GRIEF ALERT", list("No", "Yes")) - if(warning == "Yes") - holder.warned = 1 - else - return - T.ChangeTurf(get_base_turf_by_area(T)) //Defaults to Z if area does not have a special base turf. - return - else if(istype(object,/turf/simulated/wall/r_wall)) - var/turf/T = object - T.ChangeTurf(/turf/simulated/wall) - return - else if(istype(object,/obj)) - qdel(object) - return - else if(istype(object,/turf) && pa.Find("alt") && pa.Find("left")) - new/obj/machinery/door/airlock(get_turf(object)) - else if(istype(object,/turf) && pa.Find("ctrl") && pa.Find("left")) - switch(holder.builddir.dir) - if(NORTH) - var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) - WIN.set_dir(NORTH) - if(SOUTH) - var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) - WIN.set_dir(SOUTH) - if(EAST) - var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) - WIN.set_dir(EAST) - if(WEST) - var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) - WIN.set_dir(WEST) - if(NORTHWEST) - var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) - WIN.set_dir(NORTHWEST) - else if(istype(object,/turf) && pa.Find("ctrl") && pa.Find("alt") && pa.Find("middle")) - var/turf/T = object - var/obj/item/toy/plushie/teshari/easter_egg = new /obj/item/toy/plushie/teshari(T) - easter_egg.name = "coding teshari plushie" - easter_egg.desc = "A small purple teshari with a plush keyboard attached to it. Where did this come from?" - easter_egg.color = "#a418c7" - - - if(BUILDMODE_ADVANCED) - if(pa.Find("left") && !pa.Find("ctrl")) - if(ispath(holder.buildmode.objholder,/turf)) - var/turf/T = get_turf(object) - T.ChangeTurf(holder.buildmode.objholder) - else if(ispath(holder.buildmode.objholder)) - var/obj/A = new holder.buildmode.objholder (get_turf(object)) - A.set_dir(holder.builddir.dir) - else if(pa.Find("right")) - if(isobj(object)) - qdel(object) - else if(pa.Find("ctrl")) - holder.buildmode.objholder = object.type - to_chat(user, "[object]([object.type]) copied to buildmode.") - if(pa.Find("middle")) - holder.buildmode.objholder = text2path("[object.type]") - if(holder.buildmode.objsay) - to_chat(usr, "[object.type]") - - if(BUILDMODE_EDIT) - if(pa.Find("left")) //I cant believe this shit actually compiles. - if(object.vars.Find(holder.buildmode.varholder)) - log_admin("[key_name(usr)] modified [object.name]'s [holder.buildmode.varholder] to [holder.buildmode.valueholder]") - object.vars[holder.buildmode.varholder] = holder.buildmode.valueholder - else - to_chat(user, "[initial(object.name)] does not have a var called '[holder.buildmode.varholder]'") - if(pa.Find("right")) - if(object.vars.Find(holder.buildmode.varholder)) - log_admin("[key_name(usr)] modified [object.name]'s [holder.buildmode.varholder] to [holder.buildmode.valueholder]") - object.vars[holder.buildmode.varholder] = initial(object.vars[holder.buildmode.varholder]) - else - to_chat(user, "[initial(object.name)] does not have a var called '[holder.buildmode.varholder]'") - - if(BUILDMODE_THROW) - if(pa.Find("left")) - if(istype(object, /atom/movable)) - holder.throw_atom = object - if(pa.Find("right")) - if(holder.throw_atom) - holder.throw_atom.throw_at(object, 10, 1) - log_admin("[key_name(usr)] threw [holder.throw_atom] at [object]") - - if(BUILDMODE_ROOM) - if(pa.Find("left")) - holder.buildmode.coordA = get_turf(object) - to_chat(user, "Defined [object] ([object.type]) as point A.") - - if(pa.Find("right")) - holder.buildmode.coordB = get_turf(object) - to_chat(user, "Defined [object] ([object.type]) as point B.") - - if(holder.buildmode.coordA && holder.buildmode.coordB) - if(isnull(holder.buildmode.area_name)) - to_chat(user, "ERROR: Insert area name before use.") - holder.buildmode.coordA = null - holder.buildmode.coordB = null - return - to_chat(user, "A and B set, creating rectangle.") - holder.buildmode.make_rectangle( - holder.buildmode.coordA, - holder.buildmode.coordB, - holder.buildmode.wall_holder, - holder.buildmode.floor_holder, - holder.buildmode.area_enabled, - holder.buildmode.area_name) - holder.buildmode.coordA = null - holder.buildmode.coordB = null - - if(BUILDMODE_LADDER) - if(pa.Find("left")) - holder.buildmode.coordA = get_turf(object) - to_chat(user, "Defined [object] ([object.type]) as upper ladder location.") - - if(pa.Find("right")) - holder.buildmode.coordB = get_turf(object) - to_chat(user, "Defined [object] ([object.type]) as lower ladder location.") - - if(holder.buildmode.coordA && holder.buildmode.coordB) - to_chat(user, "Ladder locations set, building ladders.") - var/obj/structure/ladder/A = new /obj/structure/ladder/up(holder.buildmode.coordA) - var/obj/structure/ladder/B = new /obj/structure/ladder(holder.buildmode.coordB) - A.target_up = B - B.target_down = A - A.update_icon() - B.update_icon() - holder.buildmode.coordA = null - holder.buildmode.coordB = null - - if(BUILDMODE_CONTENTS) - if(pa.Find("left")) - if(istype(object, /atom)) - holder.throw_atom = object - if(pa.Find("right")) - if(holder.throw_atom && istype(object, /atom/movable)) - object.forceMove(holder.throw_atom) - log_admin("[key_name(usr)] moved [object] into [holder.throw_atom].") - - if(BUILDMODE_LIGHTS) - if(pa.Find("left")) - if(object) - object.set_light(holder.buildmode.new_light_range, holder.buildmode.new_light_intensity, holder.buildmode.new_light_color) - if(pa.Find("right")) - if(object) - object.set_light(0, 0, "#FFFFFF") - - if(BUILDMODE_AI) - if(pa.Find("left")) - if(isliving(object)) - var/mob/living/L = object - - // Pause/unpause AI - if(pa.Find("shift")) - var/stance = L.get_AI_stance() - if(!isnull(stance)) // Null means there's no AI datum or it has one but is player controlled w/o autopilot on. - var/datum/ai_holder/AI = L.ai_holder - if(stance == STANCE_SLEEP) - AI.go_wake() - to_chat(user, span("notice", "\The [L]'s AI has been enabled.")) - else - AI.go_sleep() - to_chat(user, span("notice", "\The [L]'s AI has been disabled.")) - return - else - to_chat(user, span("warning", "\The [L] is not AI controlled.")) - return - - // Toggle hostility - if(pa.Find("alt")) - if(!isnull(L.get_AI_stance())) - var/datum/ai_holder/AI = L.ai_holder - AI.hostile = !AI.hostile - to_chat(user, span("notice", "\The [L] is now [AI.hostile ? "hostile" : "passive"].")) - else - to_chat(user, span("warning", "\The [L] is not AI controlled.")) - return - - // Copy faction - if(pa.Find("ctrl")) - holder.copied_faction = L.faction - to_chat(user, span("notice", "Copied faction '[holder.copied_faction]'.")) - return - - // Select/Deselect - if(!isnull(L.get_AI_stance())) - if(L in holder.selected_mobs) - holder.deselect_AI_mob(user.client, L) - to_chat(user, span("notice", "Deselected \the [L].")) - else - holder.select_AI_mob(user.client, L) - to_chat(user, span("notice", "Selected \the [L].")) - return - else - to_chat(user, span("warning", "\The [L] is not AI controlled.")) - return - else //Not living - for(var/mob/living/unit in holder.selected_mobs) - holder.deselect_AI_mob(user.client, unit) - - if(pa.Find("middle")) - if(pa.Find("shift")) - to_chat(user, SPAN_NOTICE("All selected mobs set to wander")) - for(var/mob/living/unit in holder.selected_mobs) - var/datum/ai_holder/AI = unit.ai_holder - AI.wander = TRUE - if(pa.Find("ctrl")) - to_chat(user, SPAN_NOTICE("Setting mobs set to NOT wander")) - for(var/mob/living/unit in holder.selected_mobs) - var/datum/ai_holder/AI = unit.ai_holder - AI.wander = FALSE - if(pa.Find("alt") && isatom(object)) - to_chat(user, SPAN_NOTICE("Adding [object] to Entity Narrate List!")) - user.client.add_mob_for_narration(object) - - - if(pa.Find("right")) - // Paste faction - if(pa.Find("ctrl") && isliving(object)) - if(!holder.copied_faction) - to_chat(user, span("warning", "LMB+Shift a mob to copy their faction before pasting.")) - return - else - var/mob/living/L = object - L.faction = holder.copied_faction - to_chat(user, span("notice", "Pasted faction '[holder.copied_faction]'.")) - return - - if(istype(object, /atom)) // Force attack. - var/atom/A = object - - if(pa.Find("alt")) - var/i = 0 - for(var/mob/living/unit in holder.selected_mobs) - var/datum/ai_holder/AI = unit.ai_holder - AI.give_target(A) - i++ - to_chat(user, span("notice", "Commanded [i] mob\s to attack \the [A].")) - var/image/orderimage = image(buildmode_hud,A,"ai_targetorder") - orderimage.plane = PLANE_BUILDMODE - flick_overlay(orderimage, list(user.client), 8, TRUE) - return - - if(isliving(object)) // Follow or attack. - var/mob/living/L = object - var/i = 0 // Attacking mobs. - var/j = 0 // Following mobs. - for(var/mob/living/unit in holder.selected_mobs) - var/datum/ai_holder/AI = unit.ai_holder - if(L.IIsAlly(unit) || !AI.hostile || pa.Find("shift")) - AI.set_follow(L) - j++ - else - AI.give_target(L) - i++ - var/message = "Commanded " - if(i) - message += "[i] mob\s to attack \the [L]" - if(j) - message += ", and " - else - message += "." - if(j) - message += "[j] mob\s to follow \the [L]." - to_chat(user, span("notice", message)) - var/image/orderimage = image(buildmode_hud,L,"ai_targetorder") - orderimage.plane = PLANE_BUILDMODE - flick_overlay(orderimage, list(user.client), 8, TRUE) - return - - if(isturf(object)) // Move or reposition. - var/turf/T = object - var/forced = 0 - var/told = 0 - for(var/mob/living/unit in holder.selected_mobs) - var/datum/ai_holder/AI = unit.ai_holder - AI.home_turf = T - if(unit.get_AI_stance() == STANCE_SLEEP) - unit.forceMove(T) - forced++ - else - AI.give_destination(T, 1, pa.Find("shift")) // If shift is held, the mobs will not stop moving to attack a visible enemy. - told++ - to_chat(user, span("notice", "Commanded [told] mob\s to move to \the [T], and manually placed [forced] of them.")) - var/image/orderimage = image(buildmode_hud,T,"ai_turforder") - orderimage.plane = PLANE_BUILDMODE - flick_overlay(orderimage, list(user.client), 8, TRUE) - return - -/proc/build_drag(var/client/user, buildmode, var/atom/fromatom, var/atom/toatom, var/atom/fromloc, var/atom/toloc, var/fromcontrol, var/tocontrol, params) - var/obj/effect/bmode/buildholder/holder = null - for(var/obj/effect/bmode/buildholder/H) - if(H.cl == user) - holder = H - break - if(!holder) return - var/list/pa = params2list(params) - - switch(buildmode) - if(BUILDMODE_AI) - - //Holding shift prevents the deselection of existing - if(!pa.Find("shift")) - for(var/mob/living/unit in holder.selected_mobs) - holder.deselect_AI_mob(user, unit) - - var/turf/c1 = get_turf(fromatom) - var/turf/c2 = get_turf(toatom) - if(!c1 || !c2) - return //Dragged outside window or something - - var/low_x = min(c1.x,c2.x) - var/low_y = min(c1.y,c2.y) - var/hi_x = max(c1.x,c2.x) - var/hi_y = max(c1.y,c2.y) - var/z = c1.z //Eh - - var/i = 0 - for(var/mob/living/L in living_mob_list) - if(L.z != z || L.client) - continue - if(L.x >= low_x && L.x <= hi_x && L.y >= low_y && L.y <= hi_y) - holder.select_AI_mob(user, L) - i++ - - to_chat(user, span("notice", "Band-selected [i] mobs.")) - return - -/obj/effect/bmode/buildmode/proc/get_path_from_partial_text(default_path) - var/desired_path = tgui_input_text(usr, "Enter full or partial typepath.","Typepath","[default_path]") - - if(!desired_path) //VOREStation Add - If you don't give it anything it builds a list of every possible thing in the game and crashes your client. - return //VOREStation Add - And the main way for it to do that is to push the cancel button, which should just do nothing. :U - - var/list/types = typesof(/atom) - var/list/matches = list() - - for(var/path in types) - if(findtext("[path]", desired_path)) - matches += path - - if(matches.len==0) - tgui_alert_async(usr, "No results found. Sorry.") - return - - var/result = null - - if(matches.len==1) - result = matches[1] - else - result = tgui_input_list(usr, "Select an atom type", "Spawn Atom", matches, strict_modern = TRUE) - return result - -/obj/effect/bmode/buildmode/proc/make_rectangle(var/turf/A, var/turf/B, var/turf/wall_type, var/turf/floor_type, var/area_enabled, var/area_name) - if(!A || !B) // No coords - return - if(A.z != B.z) // Not same z-level - return - - var/height = A.y - B.y - var/width = A.x - B.x - var/z_level = A.z - - var/turf/lower_left_corner = null - // First, try to find the lowest part - var/desired_y = 0 - if(A.y <= B.y) - desired_y = A.y - else - desired_y = B.y - - //Now for the left-most part. - var/desired_x = 0 - if(A.x <= B.x) - desired_x = A.x - else - desired_x = B.x - - lower_left_corner = locate(desired_x, desired_y, z_level) - - // Now we can begin building the actual room. This defines the boundries for the room. - var/low_bound_x = lower_left_corner.x - var/low_bound_y = lower_left_corner.y - - var/high_bound_x = lower_left_corner.x + abs(width) - var/high_bound_y = lower_left_corner.y + abs(height) - - var/origin_x = lower_left_corner.x + round((abs(width)/2)) - var/origin_y = lower_left_corner.y + round((abs(height)/2)) - var/turf/origin - - for(var/i = low_bound_x, i <= high_bound_x, i++) - for(var/j = low_bound_y, j <= high_bound_y, j++) - var/turf/T = locate(i, j, z_level) - if(i == low_bound_x || i == high_bound_x || j == low_bound_y || j == high_bound_y) - if(isturf(wall_type)) - T.ChangeTurf(wall_type) - else - new wall_type(T) - - else - if(T.x == origin_x && T.y == origin_y) //Get the middle of the square. - origin = T - if(isturf(floor_type)) - T.ChangeTurf(floor_type) - else - new floor_type(T) - if(area_enabled) //Let's try not to make a new area unless you got walls and a floor. - create_buildmode_area(area_name, origin) //Generates a new area. - -/proc/create_buildmode_area(var/area_name, var/turf/origin) - var/turfs = detect_room_buildmode(origin) - - var/area/newA - var/area/oldA = get_area(origin) - var/str = area_name - str = sanitize(str,MAX_NAME_LEN) - if(!str || !length(str)) //cancel - return - newA = new /area/buildmode - newA.dynamic_lighting = FALSE // Without this it's pitch black if you build anywhere but space. - newA.luminosity = TRUE // Without this it's pitch black if you build anywhere but space. - newA.setup(str) - newA.has_gravity = oldA.has_gravity - - for(var/i in 1 to length(turfs)) //Fix lighting. Praise the lord. - var/turf/thing = turfs[i] - newA.contents += thing - thing.change_area(oldA, newA) - - set_area_machinery(newA, newA.name, oldA.name)// Change the name and area defines of all the machinery to the correct area. - oldA.power_check() //Simply makes the area turn the power off if you nicked an APC from it. - return TRUE - -/proc/detect_room_buildmode(var/turf/first, var/allowedAreas = AREA_SPACE) - if(!istype(first)) - return - var/list/turf/found = new - var/list/turf/pending = list(first) - while(pending.len) - var/turf/T = pending[1] - pending -= T - for (var/dir in cardinal) - var/turf/NT = get_step(T,dir) - if (!isturf(NT) || (NT in found) || (NT in pending)) - continue - // We ask ZAS to determine if its airtight. Thats what matters anyway right? - if(air_master.air_blocked(T, NT)) - // Okay thats the edge of the room - if(get_area_type_buildmode(NT.loc) == AREA_SPACE && air_master.air_blocked(NT, NT)) - found += NT // So we include walls/doors not already in any area - continue - if (istype(NT, /turf/space)) - return //omg hull breach we all going to die here - if (istype(NT, /turf/simulated/shuttle)) - return // Unsure why this, but was in old code. Trusting for now. - if (NT.loc != first.loc && !(get_area_type_buildmode(NT.loc) & allowedAreas)) - // Edge of a protected area. Lets stop here... - continue - if (!istype(NT, /turf/simulated)) - // Great, unsimulated... eh, just stop searching here - continue - // Okay, NT looks promising, lets continue the search there! - pending += NT - found += T - // end while - return found - -/proc/get_area_type_buildmode(area/A) - if(A.outdoors) - return AREA_SPACE - - for (var/type in BUILDABLE_AREA_TYPES) - if ( istype(A,type) ) - return AREA_SPACE - - for (var/type in SPECIALS) - if ( istype(A,type) ) - return AREA_SPECIAL - return AREA_STATION - -/area/buildmode - dynamic_lighting = FALSE - luminosity = FALSE - -#undef BUILDMODE_BASIC -#undef BUILDMODE_ADVANCED -#undef BUILDMODE_EDIT -#undef BUILDMODE_THROW -#undef BUILDMODE_ROOM -#undef BUILDMODE_LADDER -#undef BUILDMODE_CONTENTS -#undef BUILDMODE_LIGHTS -#undef BUILDMODE_AI -#undef LAST_BUILDMODE +#define BUILDMODE_BASIC 1 +#define BUILDMODE_ADVANCED 2 +#define BUILDMODE_EDIT 3 +#define BUILDMODE_THROW 4 +#define BUILDMODE_ROOM 5 +#define BUILDMODE_LADDER 6 +#define BUILDMODE_CONTENTS 7 +#define BUILDMODE_LIGHTS 8 +#define BUILDMODE_AI 9 + +#define LAST_BUILDMODE 9 + +/proc/togglebuildmode(mob/M as mob in player_list) + set name = "Toggle Build Mode" + set category = "Special Verbs" + if(M.client) + if(M.client.buildmode) + log_admin("[key_name(usr)] has left build mode.") + M.client.buildmode = 0 + M.client.show_popup_menus = 1 + M.plane_holder.set_vis(VIS_BUILDMODE, FALSE) + for(var/obj/effect/bmode/buildholder/H) + if(H.cl == M.client) + qdel(H) + else + log_admin("[key_name(usr)] has entered build mode.") + M.client.buildmode = 1 + M.client.show_popup_menus = 0 + M.plane_holder.set_vis(VIS_BUILDMODE, TRUE) + + var/obj/effect/bmode/buildholder/H = new/obj/effect/bmode/buildholder() + var/obj/effect/bmode/builddir/A = new/obj/effect/bmode/builddir(H) + A.master = H + var/obj/effect/bmode/buildhelp/B = new/obj/effect/bmode/buildhelp(H) + B.master = H + var/obj/effect/bmode/buildmode/C = new/obj/effect/bmode/buildmode(H) + C.master = H + var/obj/effect/bmode/buildquit/D = new/obj/effect/bmode/buildquit(H) + D.master = H + + H.builddir = A + H.buildhelp = B + H.buildmode = C + H.buildquit = D + M.client.screen += A + M.client.screen += B + M.client.screen += C + M.client.screen += D + H.cl = M.client + +/obj/effect/bmode//Cleaning up the tree a bit + density = TRUE + anchored = TRUE + layer = LAYER_HUD_BASE + plane = PLANE_PLAYER_HUD + dir = NORTH + icon = 'icons/misc/buildmode.dmi' + var/obj/effect/bmode/buildholder/master = null + +/obj/effect/bmode/Destroy() + if(master && master.cl) + master.cl.screen -= src + master = null + return ..() + +/obj/effect/bmode/builddir + icon_state = "build" + screen_loc = "NORTH,WEST" + +/obj/effect/bmode/builddir/Click() + switch(dir) + if(NORTH) + set_dir(EAST) + if(EAST) + set_dir(SOUTH) + if(SOUTH) + set_dir(WEST) + if(WEST) + set_dir(NORTHWEST) + if(NORTHWEST) + set_dir(NORTH) + return 1 + +/obj/effect/bmode/buildhelp + icon = 'icons/misc/buildmode.dmi' + icon_state = "buildhelp" + screen_loc = "NORTH,WEST+1" + +/obj/effect/bmode/buildhelp/Click() + switch(master.cl.buildmode) + + if(BUILDMODE_BASIC) + to_chat(usr, "***********************************************************
                    \ + Left Mouse Button = Construct / Upgrade
                    \ + Right Mouse Button = Deconstruct / Delete / Downgrade
                    \ + Left Mouse Button + ctrl = R-Window
                    \ + Left Mouse Button + alt = Airlock

                    \ + Use the button in the upper left corner to
                    \ + change the direction of built objects.
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_ADVANCED) + to_chat(usr, "***********************************************************
                    \ + Right Mouse Button on buildmode button = Set object type
                    \ + Middle Mouse Button on buildmode button= On/Off object type saying
                    \ + Middle Mouse Button on turf/obj = Capture object type
                    \ + Left Mouse Button on turf/obj = Place objects
                    \ + Right Mouse Button = Delete objects
                    \ + Mouse Button + ctrl = Copy object type

                    \ + Use the button in the upper left corner to
                    \ + change the direction of built objects.
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_EDIT) + to_chat(usr, "***********************************************************
                    \ + Right Mouse Button on buildmode button = Select var(type) & value
                    \ + Left Mouse Button on turf/obj/mob = Set var(type) & value
                    \ + Right Mouse Button on turf/obj/mob = Reset var's value
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_THROW) + to_chat(usr, "***********************************************************
                    \ + Left Mouse Button on turf/obj/mob = Select
                    \ + Right Mouse Button on turf/obj/mob = Throw
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_ROOM) + to_chat(usr, "***********************************************************
                    \ + Left Mouse Button on turf = Select as point A
                    \ + Right Mouse Button on turf = Select as point B
                    \ + Right Mouse Button on buildmode button = Change floor/wall type/area name
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_LADDER) + to_chat(usr, "***********************************************************
                    \ + Left Mouse Button on turf = Set as upper ladder loc
                    \ + Right Mouse Button on turf = Set as lower ladder loc
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_CONTENTS) + to_chat(usr, "***********************************************************
                    \ + Left Mouse Button on turf/obj/mob = Select
                    \ + Right Mouse Button on turf/obj/mob = Move into selection
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_LIGHTS) + to_chat(usr, "***********************************************************
                    \ + Left Mouse Button on turf/obj/mob = Make it glow
                    \ + Right Mouse Button on turf/obj/mob = Reset glowing
                    \ + Right Mouse Button on buildmode button = Change glow properties
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_AI) + to_chat(usr, "***********************************************************
                    \ + Left Mouse Button drag box = Select only mobs in box
                    \ + Left Mouse Button drag box + shift = Select additional mobs in area
                    \ + Left Mouse Button on non-mob = Deselect all mobs
                    \ + Left Mouse Button on AI mob = Select/Deselect mob
                    \ + Left Mouse Button + alt on AI mob = Toggle hostility on mob
                    \ + Left Mouse Button + shift on AI mob = Toggle AI (also resets)
                    \ + Left Mouse Button + ctrl on AI mob = Copy mob faction
                    \ + Middle Mouse Button + alt on any atom = Add atom to entity narrate menu
                    \ + Middle Mouse Button + shift on any = Set selected mob(s) to wander
                    \ + Middle Mouse Button + ctrl on any = Set selected mob(s) to NOT wander
                    \ + Right Mouse Button + ctrl on any mob = Paste mob faction copied with Left Mouse Button + shift
                    \ + Right Mouse Button on enemy mob = Command selected mobs to attack mob
                    \ + Right Mouse Button on allied mob = Command selected mobs to follow mob
                    \ + Right Mouse Button + shift on any mob = Command selected mobs to follow mob regardless of faction
                    \ + Note: The following also reset the mob's home position:
                    \ + Right Mouse Button on tile = Command selected mobs to move to tile (will cancel if enemies are seen)
                    \ + Right Mouse Button + shift on tile = Command selected mobs to reposition to tile (will not be interrupted by enemies)
                    \ + Right Mouse Button + alt on obj/turfs = Command selected mobs to attack obj/turf
                    \ + ***********************************************************
                    ") + return 1 + +/obj/effect/bmode/buildquit + icon_state = "buildquit" + screen_loc = "NORTH,WEST+3" + +/obj/effect/bmode/buildquit/Click() + togglebuildmode(master.cl.mob) + return 1 + +/obj/effect/bmode/buildholder + density = FALSE + anchored = TRUE + var/client/cl = null + var/obj/effect/bmode/builddir/builddir = null + var/obj/effect/bmode/buildhelp/buildhelp = null + var/obj/effect/bmode/buildmode/buildmode = null + var/obj/effect/bmode/buildquit/buildquit = null + var/atom/movable/throw_atom = null + var/list/selected_mobs = list() + var/copied_faction = null + var/warned = 0 + +/obj/effect/bmode/buildholder/Destroy() + qdel(builddir) + builddir = null + qdel(buildhelp) + buildhelp = null + qdel(buildmode) + buildmode = null + qdel(buildquit) + buildquit = null + throw_atom = null + for(var/mob/living/unit in selected_mobs) + deselect_AI_mob(cl, unit) + selected_mobs.Cut() + cl = null + return ..() + +/obj/effect/bmode/buildholder/proc/select_AI_mob(client/C, mob/living/unit) + selected_mobs += unit + C.images += unit.selected_image + +/obj/effect/bmode/buildholder/proc/deselect_AI_mob(client/C, mob/living/unit) + selected_mobs -= unit + C.images -= unit.selected_image + +/obj/effect/bmode/buildmode + icon_state = "buildmode1" + screen_loc = "NORTH,WEST+2" + var/varholder = "name" + var/valueholder = "derp" + var/objholder = null + var/objsay = 1 + + var/wall_holder = /turf/simulated/wall + var/floor_holder = /turf/simulated/floor/plating + var/turf/coordA = null + var/turf/coordB = null + var/area_enabled = 0 + var/area_name = "New Area" + + var/new_light_color = "#FFFFFF" + var/new_light_range = 3 + var/new_light_intensity = 3 + +/obj/effect/bmode/buildmode/Click(location, control, params) + var/list/pa = params2list(params) + + if(pa.Find("middle")) + switch(master.cl.buildmode) + if(BUILDMODE_ADVANCED) + objsay=!objsay + + if(pa.Find("left")) + if(master.cl.buildmode == LAST_BUILDMODE) + master.cl.buildmode = 1 + else + master.cl.buildmode++ + src.icon_state = "buildmode[master.cl.buildmode]" + + else if(pa.Find("right")) + switch(master.cl.buildmode) + if(BUILDMODE_BASIC) + + return 1 + if(BUILDMODE_ADVANCED) + objholder = get_path_from_partial_text() + + if(BUILDMODE_EDIT) + var/list/locked = list("vars", "key", "ckey", "client", "firemut", "ishulk", "telekinesis", "xray", "virus", "viruses", "cuffed", "ka", "last_eaten", "urine") + + master.buildmode.varholder = tgui_input_text(usr,"Enter variable name:" ,"Name", "name") + if(master.buildmode.varholder in locked && !check_rights(R_DEBUG,0)) + return 1 + var/thetype = tgui_input_list(usr,"Select variable type:", "Type", list("text","number","mob-reference","obj-reference","turf-reference")) + if(!thetype) return 1 + switch(thetype) + if("text") + master.buildmode.valueholder = tgui_input_text(usr,"Enter variable value:" ,"Value", "value") + if("number") + master.buildmode.valueholder = tgui_input_number(usr,"Enter variable value:" ,"Value", 123) + if("mob-reference") + master.buildmode.valueholder = tgui_input_list(usr,"Enter variable value:", "Value", mob_list) + if("obj-reference") + master.buildmode.valueholder = tgui_input_list(usr,"Enter variable value:", "Value", world) + if("turf-reference") + master.buildmode.valueholder = tgui_input_list(usr,"Enter variable value:", "Value", world) + + if(BUILDMODE_ROOM) + var/area_choice = tgui_alert(usr, "Would you like to generate a new area as well?","Room Builder", list("No", "Yes")) + switch(area_choice) + if("No") + area_enabled = 0 + if("Yes") + area_enabled = 1 + area_name = tgui_input_text(usr, "New area name", "Room Buildmode", max_length = MAX_NAME_LEN) + if(isnull(area_name)) + to_chat(usr, "You must enter a non-null name.") + area_enabled = 0 + return + area_name = sanitize(area_name,MAX_NAME_LEN) + var/choice = tgui_alert(usr, "Would you like to change the floor or wall holders?","Room Builder", list("Floor", "Wall")) + switch(choice) + if("Floor") + floor_holder = get_path_from_partial_text(/turf/simulated/floor/plating) + if("Wall") + wall_holder = get_path_from_partial_text(/turf/simulated/wall) + + if(BUILDMODE_LIGHTS) + var/choice = tgui_alert(usr, "Change the new light range, power, or color?", "Light Maker", list("Range", "Power", "Color")) + switch(choice) + if("Range") + var/input = tgui_input_number(usr, "New light range.","Light Maker",3) + if(input) + new_light_range = input + if("Power") + var/input = tgui_input_number(usr, "New light power.","Light Maker",3) + if(input) + new_light_intensity = input + if("Color") + var/input = input(usr, "New light color.","Light Maker",3) as null|color + if(input) + new_light_color = input + return 1 + +/proc/build_click(var/mob/user, buildmode, params, var/obj/object) + var/obj/effect/bmode/buildholder/holder = null + for(var/obj/effect/bmode/buildholder/H) + if(H.cl == user.client) + holder = H + break + if(!holder) return + var/list/pa = params2list(params) + + switch(buildmode) + if(BUILDMODE_BASIC) + if(istype(object,/turf) && pa.Find("left") && !pa.Find("alt") && !pa.Find("ctrl") ) + if(istype(object,/turf/space)) + var/turf/T = object + T.ChangeTurf(/turf/simulated/floor) + return + else if(istype(object,/turf/simulated/floor)) + var/turf/T = object + T.ChangeTurf(/turf/simulated/wall) + return + else if(istype(object,/turf/simulated/wall)) + var/turf/T = object + T.ChangeTurf(/turf/simulated/wall/r_wall) + return + else if(pa.Find("right")) + if(istype(object,/turf/simulated/wall)) + var/turf/T = object + T.ChangeTurf(/turf/simulated/floor) + return + else if(istype(object,/turf/simulated/floor)) + var/turf/T = object + if(!holder.warned) + var/warning = tgui_alert(user, "Are you -sure- you want to delete this turf and make it the base turf for this Z level?", "GRIEF ALERT", list("No", "Yes")) + if(warning == "Yes") + holder.warned = 1 + else + return + T.ChangeTurf(get_base_turf_by_area(T)) //Defaults to Z if area does not have a special base turf. + return + else if(istype(object,/turf/simulated/wall/r_wall)) + var/turf/T = object + T.ChangeTurf(/turf/simulated/wall) + return + else if(istype(object,/obj)) + qdel(object) + return + else if(istype(object,/turf) && pa.Find("alt") && pa.Find("left")) + new/obj/machinery/door/airlock(get_turf(object)) + else if(istype(object,/turf) && pa.Find("ctrl") && pa.Find("left")) + switch(holder.builddir.dir) + if(NORTH) + var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) + WIN.set_dir(NORTH) + if(SOUTH) + var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) + WIN.set_dir(SOUTH) + if(EAST) + var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) + WIN.set_dir(EAST) + if(WEST) + var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) + WIN.set_dir(WEST) + if(NORTHWEST) + var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) + WIN.set_dir(NORTHWEST) + else if(istype(object,/turf) && pa.Find("ctrl") && pa.Find("alt") && pa.Find("middle")) + var/turf/T = object + var/obj/item/toy/plushie/teshari/easter_egg = new /obj/item/toy/plushie/teshari(T) + easter_egg.name = "coding teshari plushie" + easter_egg.desc = "A small purple teshari with a plush keyboard attached to it. Where did this come from?" + easter_egg.color = "#a418c7" + + + if(BUILDMODE_ADVANCED) + if(pa.Find("left") && !pa.Find("ctrl")) + if(ispath(holder.buildmode.objholder,/turf)) + var/turf/T = get_turf(object) + T.ChangeTurf(holder.buildmode.objholder) + else if(ispath(holder.buildmode.objholder)) + var/obj/A = new holder.buildmode.objholder (get_turf(object)) + A.set_dir(holder.builddir.dir) + else if(pa.Find("right")) + if(isobj(object)) + qdel(object) + else if(pa.Find("ctrl")) + holder.buildmode.objholder = object.type + to_chat(user, "[object]([object.type]) copied to buildmode.") + if(pa.Find("middle")) + holder.buildmode.objholder = text2path("[object.type]") + if(holder.buildmode.objsay) + to_chat(usr, "[object.type]") + + if(BUILDMODE_EDIT) + if(pa.Find("left")) //I cant believe this shit actually compiles. + if(object.vars.Find(holder.buildmode.varholder)) + log_admin("[key_name(usr)] modified [object.name]'s [holder.buildmode.varholder] to [holder.buildmode.valueholder]") + object.vars[holder.buildmode.varholder] = holder.buildmode.valueholder + else + to_chat(user, "[initial(object.name)] does not have a var called '[holder.buildmode.varholder]'") + if(pa.Find("right")) + if(object.vars.Find(holder.buildmode.varholder)) + log_admin("[key_name(usr)] modified [object.name]'s [holder.buildmode.varholder] to [holder.buildmode.valueholder]") + object.vars[holder.buildmode.varholder] = initial(object.vars[holder.buildmode.varholder]) + else + to_chat(user, "[initial(object.name)] does not have a var called '[holder.buildmode.varholder]'") + + if(BUILDMODE_THROW) + if(pa.Find("left")) + if(istype(object, /atom/movable)) + holder.throw_atom = object + if(pa.Find("right")) + if(holder.throw_atom) + holder.throw_atom.throw_at(object, 10, 1) + log_admin("[key_name(usr)] threw [holder.throw_atom] at [object]") + + if(BUILDMODE_ROOM) + if(pa.Find("left")) + holder.buildmode.coordA = get_turf(object) + to_chat(user, "Defined [object] ([object.type]) as point A.") + + if(pa.Find("right")) + holder.buildmode.coordB = get_turf(object) + to_chat(user, "Defined [object] ([object.type]) as point B.") + + if(holder.buildmode.coordA && holder.buildmode.coordB) + if(isnull(holder.buildmode.area_name)) + to_chat(user, "ERROR: Insert area name before use.") + holder.buildmode.coordA = null + holder.buildmode.coordB = null + return + to_chat(user, "A and B set, creating rectangle.") + holder.buildmode.make_rectangle( + holder.buildmode.coordA, + holder.buildmode.coordB, + holder.buildmode.wall_holder, + holder.buildmode.floor_holder, + holder.buildmode.area_enabled, + holder.buildmode.area_name) + holder.buildmode.coordA = null + holder.buildmode.coordB = null + + if(BUILDMODE_LADDER) + if(pa.Find("left")) + holder.buildmode.coordA = get_turf(object) + to_chat(user, "Defined [object] ([object.type]) as upper ladder location.") + + if(pa.Find("right")) + holder.buildmode.coordB = get_turf(object) + to_chat(user, "Defined [object] ([object.type]) as lower ladder location.") + + if(holder.buildmode.coordA && holder.buildmode.coordB) + to_chat(user, "Ladder locations set, building ladders.") + var/obj/structure/ladder/A = new /obj/structure/ladder/up(holder.buildmode.coordA) + var/obj/structure/ladder/B = new /obj/structure/ladder(holder.buildmode.coordB) + A.target_up = B + B.target_down = A + A.update_icon() + B.update_icon() + holder.buildmode.coordA = null + holder.buildmode.coordB = null + + if(BUILDMODE_CONTENTS) + if(pa.Find("left")) + if(istype(object, /atom)) + holder.throw_atom = object + if(pa.Find("right")) + if(holder.throw_atom && istype(object, /atom/movable)) + object.forceMove(holder.throw_atom) + log_admin("[key_name(usr)] moved [object] into [holder.throw_atom].") + + if(BUILDMODE_LIGHTS) + if(pa.Find("left")) + if(object) + object.set_light(holder.buildmode.new_light_range, holder.buildmode.new_light_intensity, holder.buildmode.new_light_color) + if(pa.Find("right")) + if(object) + object.set_light(0, 0, "#FFFFFF") + + if(BUILDMODE_AI) + if(pa.Find("left")) + if(isliving(object)) + var/mob/living/L = object + + // Pause/unpause AI + if(pa.Find("shift")) + var/stance = L.get_AI_stance() + if(!isnull(stance)) // Null means there's no AI datum or it has one but is player controlled w/o autopilot on. + var/datum/ai_holder/AI = L.ai_holder + if(stance == STANCE_SLEEP) + AI.go_wake() + to_chat(user, span("notice", "\The [L]'s AI has been enabled.")) + else + AI.go_sleep() + to_chat(user, span("notice", "\The [L]'s AI has been disabled.")) + return + else + to_chat(user, span("warning", "\The [L] is not AI controlled.")) + return + + // Toggle hostility + if(pa.Find("alt")) + if(!isnull(L.get_AI_stance())) + var/datum/ai_holder/AI = L.ai_holder + AI.hostile = !AI.hostile + to_chat(user, span("notice", "\The [L] is now [AI.hostile ? "hostile" : "passive"].")) + else + to_chat(user, span("warning", "\The [L] is not AI controlled.")) + return + + // Copy faction + if(pa.Find("ctrl")) + holder.copied_faction = L.faction + to_chat(user, span("notice", "Copied faction '[holder.copied_faction]'.")) + return + + // Select/Deselect + if(!isnull(L.get_AI_stance())) + if(L in holder.selected_mobs) + holder.deselect_AI_mob(user.client, L) + to_chat(user, span("notice", "Deselected \the [L].")) + else + holder.select_AI_mob(user.client, L) + to_chat(user, span("notice", "Selected \the [L].")) + return + else + to_chat(user, span("warning", "\The [L] is not AI controlled.")) + return + else //Not living + for(var/mob/living/unit in holder.selected_mobs) + holder.deselect_AI_mob(user.client, unit) + + if(pa.Find("middle")) + if(pa.Find("shift")) + to_chat(user, SPAN_NOTICE("All selected mobs set to wander")) + for(var/mob/living/unit in holder.selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + AI.wander = TRUE + if(pa.Find("ctrl")) + to_chat(user, SPAN_NOTICE("Setting mobs set to NOT wander")) + for(var/mob/living/unit in holder.selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + AI.wander = FALSE + if(pa.Find("alt") && isatom(object)) + to_chat(user, SPAN_NOTICE("Adding [object] to Entity Narrate List!")) + user.client.add_mob_for_narration(object) + + + if(pa.Find("right")) + // Paste faction + if(pa.Find("ctrl") && isliving(object)) + if(!holder.copied_faction) + to_chat(user, span("warning", "LMB+Shift a mob to copy their faction before pasting.")) + return + else + var/mob/living/L = object + L.faction = holder.copied_faction + to_chat(user, span("notice", "Pasted faction '[holder.copied_faction]'.")) + return + + if(istype(object, /atom)) // Force attack. + var/atom/A = object + + if(pa.Find("alt")) + var/i = 0 + for(var/mob/living/unit in holder.selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + AI.give_target(A) + i++ + to_chat(user, span("notice", "Commanded [i] mob\s to attack \the [A].")) + var/image/orderimage = image(buildmode_hud,A,"ai_targetorder") + orderimage.plane = PLANE_BUILDMODE + flick_overlay(orderimage, list(user.client), 8, TRUE) + return + + if(isliving(object)) // Follow or attack. + var/mob/living/L = object + var/i = 0 // Attacking mobs. + var/j = 0 // Following mobs. + for(var/mob/living/unit in holder.selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + if(L.IIsAlly(unit) || !AI.hostile || pa.Find("shift")) + AI.set_follow(L) + j++ + else + AI.give_target(L) + i++ + var/message = "Commanded " + if(i) + message += "[i] mob\s to attack \the [L]" + if(j) + message += ", and " + else + message += "." + if(j) + message += "[j] mob\s to follow \the [L]." + to_chat(user, span("notice", message)) + var/image/orderimage = image(buildmode_hud,L,"ai_targetorder") + orderimage.plane = PLANE_BUILDMODE + flick_overlay(orderimage, list(user.client), 8, TRUE) + return + + if(isturf(object)) // Move or reposition. + var/turf/T = object + var/forced = 0 + var/told = 0 + for(var/mob/living/unit in holder.selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + AI.home_turf = T + if(unit.get_AI_stance() == STANCE_SLEEP) + unit.forceMove(T) + forced++ + else + AI.give_destination(T, 1, pa.Find("shift")) // If shift is held, the mobs will not stop moving to attack a visible enemy. + told++ + to_chat(user, span("notice", "Commanded [told] mob\s to move to \the [T], and manually placed [forced] of them.")) + var/image/orderimage = image(buildmode_hud,T,"ai_turforder") + orderimage.plane = PLANE_BUILDMODE + flick_overlay(orderimage, list(user.client), 8, TRUE) + return + +/proc/build_drag(var/client/user, buildmode, var/atom/fromatom, var/atom/toatom, var/atom/fromloc, var/atom/toloc, var/fromcontrol, var/tocontrol, params) + var/obj/effect/bmode/buildholder/holder = null + for(var/obj/effect/bmode/buildholder/H) + if(H.cl == user) + holder = H + break + if(!holder) return + var/list/pa = params2list(params) + + switch(buildmode) + if(BUILDMODE_AI) + + //Holding shift prevents the deselection of existing + if(!pa.Find("shift")) + for(var/mob/living/unit in holder.selected_mobs) + holder.deselect_AI_mob(user, unit) + + var/turf/c1 = get_turf(fromatom) + var/turf/c2 = get_turf(toatom) + if(!c1 || !c2) + return //Dragged outside window or something + + var/low_x = min(c1.x,c2.x) + var/low_y = min(c1.y,c2.y) + var/hi_x = max(c1.x,c2.x) + var/hi_y = max(c1.y,c2.y) + var/z = c1.z //Eh + + var/i = 0 + for(var/mob/living/L in living_mob_list) + if(L.z != z || L.client) + continue + if(L.x >= low_x && L.x <= hi_x && L.y >= low_y && L.y <= hi_y) + holder.select_AI_mob(user, L) + i++ + + to_chat(user, span("notice", "Band-selected [i] mobs.")) + return + +/obj/effect/bmode/buildmode/proc/get_path_from_partial_text(default_path) + var/desired_path = tgui_input_text(usr, "Enter full or partial typepath.","Typepath","[default_path]") + + if(!desired_path) //VOREStation Add - If you don't give it anything it builds a list of every possible thing in the game and crashes your client. + return //VOREStation Add - And the main way for it to do that is to push the cancel button, which should just do nothing. :U + + var/list/types = typesof(/atom) + var/list/matches = list() + + for(var/path in types) + if(findtext("[path]", desired_path)) + matches += path + + if(matches.len==0) + tgui_alert_async(usr, "No results found. Sorry.") + return + + var/result = null + + if(matches.len==1) + result = matches[1] + else + result = tgui_input_list(usr, "Select an atom type", "Spawn Atom", matches, strict_modern = TRUE) + return result + +/obj/effect/bmode/buildmode/proc/make_rectangle(var/turf/A, var/turf/B, var/turf/wall_type, var/turf/floor_type, var/area_enabled, var/area_name) + if(!A || !B) // No coords + return + if(A.z != B.z) // Not same z-level + return + + var/height = A.y - B.y + var/width = A.x - B.x + var/z_level = A.z + + var/turf/lower_left_corner = null + // First, try to find the lowest part + var/desired_y = 0 + if(A.y <= B.y) + desired_y = A.y + else + desired_y = B.y + + //Now for the left-most part. + var/desired_x = 0 + if(A.x <= B.x) + desired_x = A.x + else + desired_x = B.x + + lower_left_corner = locate(desired_x, desired_y, z_level) + + // Now we can begin building the actual room. This defines the boundries for the room. + var/low_bound_x = lower_left_corner.x + var/low_bound_y = lower_left_corner.y + + var/high_bound_x = lower_left_corner.x + abs(width) + var/high_bound_y = lower_left_corner.y + abs(height) + + var/origin_x = lower_left_corner.x + round((abs(width)/2)) + var/origin_y = lower_left_corner.y + round((abs(height)/2)) + var/turf/origin + + for(var/i = low_bound_x, i <= high_bound_x, i++) + for(var/j = low_bound_y, j <= high_bound_y, j++) + var/turf/T = locate(i, j, z_level) + if(i == low_bound_x || i == high_bound_x || j == low_bound_y || j == high_bound_y) + if(isturf(wall_type)) + T.ChangeTurf(wall_type) + else + new wall_type(T) + + else + if(T.x == origin_x && T.y == origin_y) //Get the middle of the square. + origin = T + if(isturf(floor_type)) + T.ChangeTurf(floor_type) + else + new floor_type(T) + if(area_enabled) //Let's try not to make a new area unless you got walls and a floor. + create_buildmode_area(area_name, origin) //Generates a new area. + +/proc/create_buildmode_area(var/area_name, var/turf/origin) + var/turfs = detect_room_buildmode(origin) + + var/area/newA + var/area/oldA = get_area(origin) + var/str = area_name + str = sanitize(str,MAX_NAME_LEN) + if(!str || !length(str)) //cancel + return + newA = new /area/buildmode + newA.dynamic_lighting = FALSE // Without this it's pitch black if you build anywhere but space. + newA.luminosity = TRUE // Without this it's pitch black if you build anywhere but space. + newA.setup(str) + newA.has_gravity = oldA.has_gravity + + for(var/i in 1 to length(turfs)) //Fix lighting. Praise the lord. + var/turf/thing = turfs[i] + newA.contents += thing + thing.change_area(oldA, newA) + + set_area_machinery(newA, newA.name, oldA.name)// Change the name and area defines of all the machinery to the correct area. + oldA.power_check() //Simply makes the area turn the power off if you nicked an APC from it. + return TRUE + +/proc/detect_room_buildmode(var/turf/first, var/allowedAreas = AREA_SPACE) + if(!istype(first)) + return + var/list/turf/found = new + var/list/turf/pending = list(first) + while(pending.len) + var/turf/T = pending[1] + pending -= T + for (var/dir in cardinal) + var/turf/NT = get_step(T,dir) + if (!isturf(NT) || (NT in found) || (NT in pending)) + continue + // We ask ZAS to determine if its airtight. Thats what matters anyway right? + if(air_master.air_blocked(T, NT)) + // Okay thats the edge of the room + if(get_area_type_buildmode(NT.loc) == AREA_SPACE && air_master.air_blocked(NT, NT)) + found += NT // So we include walls/doors not already in any area + continue + if (istype(NT, /turf/space)) + return //omg hull breach we all going to die here + if (istype(NT, /turf/simulated/shuttle)) + return // Unsure why this, but was in old code. Trusting for now. + if (NT.loc != first.loc && !(get_area_type_buildmode(NT.loc) & allowedAreas)) + // Edge of a protected area. Lets stop here... + continue + if (!istype(NT, /turf/simulated)) + // Great, unsimulated... eh, just stop searching here + continue + // Okay, NT looks promising, lets continue the search there! + pending += NT + found += T + // end while + return found + +/proc/get_area_type_buildmode(area/A) + if(A.outdoors) + return AREA_SPACE + + for (var/type in BUILDABLE_AREA_TYPES) + if ( istype(A,type) ) + return AREA_SPACE + + for (var/type in SPECIALS) + if ( istype(A,type) ) + return AREA_SPECIAL + return AREA_STATION + +/area/buildmode + dynamic_lighting = FALSE + luminosity = FALSE + +#undef BUILDMODE_BASIC +#undef BUILDMODE_ADVANCED +#undef BUILDMODE_EDIT +#undef BUILDMODE_THROW +#undef BUILDMODE_ROOM +#undef BUILDMODE_LADDER +#undef BUILDMODE_CONTENTS +#undef BUILDMODE_LIGHTS +#undef BUILDMODE_AI +#undef LAST_BUILDMODE diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index cc70c438767..b459c5cd482 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -1,31 +1,31 @@ -/client/proc/dsay(msg as text) - set category = "Special Verbs" - set name = "Dsay" //Gave this shit a shorter name so you only have to time out "dsay" rather than "dead say" to use it --NeoFite - set hidden = 1 - if(!src.holder) - to_chat(src, "Only administrators may use this command.") - return - if(!src.mob) - return - if(prefs.muted & MUTE_DEADCHAT) - to_chat(src, "You cannot send DSAY messages (muted).") - return - - if(!is_preference_enabled(/datum/client_preference/show_dsay)) - to_chat(src, "You have deadchat muted.") - return - - if (src.handle_spam_prevention(msg,MUTE_DEADCHAT)) - return - - var/stafftype = uppertext(holder.rank) - - msg = sanitize(msg) - log_admin("DSAY: [key_name(src)] : [msg]") - - if (!msg) - return - - say_dead_direct("[stafftype]([src.holder.fakekey ? src.holder.fakekey : src.key]) says, \"[msg]\"") - - feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/client/proc/dsay(msg as text) + set category = "Special Verbs" + set name = "Dsay" //Gave this shit a shorter name so you only have to time out "dsay" rather than "dead say" to use it --NeoFite + set hidden = 1 + if(!src.holder) + to_chat(src, "Only administrators may use this command.") + return + if(!src.mob) + return + if(prefs.muted & MUTE_DEADCHAT) + to_chat(src, "You cannot send DSAY messages (muted).") + return + + if(!is_preference_enabled(/datum/client_preference/show_dsay)) + to_chat(src, "You have deadchat muted.") + return + + if (src.handle_spam_prevention(msg,MUTE_DEADCHAT)) + return + + var/stafftype = uppertext(holder.rank) + + msg = sanitize(msg) + log_admin("DSAY: [key_name(src)] : [msg]") + + if (!msg) + return + + say_dead_direct("[stafftype]([src.holder.fakekey ? src.holder.fakekey : src.key]) says, \"[msg]\"") + + feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index ca03b0e14e7..33163b8b450 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -678,9 +678,9 @@ var/datum/planet/planet = tgui_input_list(usr, "Which planet do you want to modify time on?", "Change Time", SSplanets.planets) if(istype(planet)) var/datum/time/current_time_datum = planet.current_time - var/new_hour = tgui_input_number(usr, "What hour do you want to change to?", "Change Time", text2num(current_time_datum.show_time("hh"))) + var/new_hour = tgui_input_number(usr, "What hour do you want to change to?", "Change Time", text2num(current_time_datum.show_time("hh")), 23) if(!isnull(new_hour)) - var/new_minute = tgui_input_number(usr, "What minute do you want to change to?", "Change Time", text2num(current_time_datum.show_time("mm")) ) + var/new_minute = tgui_input_number(usr, "What minute do you want to change to?", "Change Time", text2num(current_time_datum.show_time("mm")), 59) if(!isnull(new_minute)) var/type_needed = current_time_datum.type var/datum/time/new_time = new type_needed() diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index ce39bbb0b8a..40b47f2f50d 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -1,187 +1,187 @@ -/client/proc/air_report() - set category = "Debug" - set name = "Show Air Report" - - if(!master_controller || !air_master) - tgui_alert_async(usr,"Master_controller or air_master not found.","Air Report") - return - - var/active_groups = air_master.active_zones - var/inactive_groups = air_master.zones.len - active_groups - - var/hotspots = 0 - for(var/obj/fire/hotspot in world) - hotspots++ - - var/active_on_main_station = 0 - var/inactive_on_main_station = 0 - for(var/zone/zone in air_master.zones) - var/turf/simulated/turf = locate() in zone.contents - if(turf?.z in using_map.station_levels) - if(zone.needs_update) - active_on_main_station++ - else - inactive_on_main_station++ - - var/output = {"AIR SYSTEMS REPORT
                    -General Processing Data
                    - Cycle: [air_master.current_cycle]
                    - Groups: [air_master.zones.len]
                    ----- Active: [active_groups]
                    ----- Inactive: [inactive_groups]

                    ----- Active on station: [active_on_main_station]
                    ----- Inactive on station: [inactive_on_main_station]
                    -
                    -Special Processing Data
                    - Hotspot Processing: [hotspots]
                    -
                    -Geometry Processing Data
                    - Tile Update: [air_master.tiles_to_update.len]
                    -"} - - usr << browse(output,"window=airreport") - -/client/proc/fix_next_move() - set category = "Debug" - set name = "Unfreeze Everyone" - var/largest_move_time = 0 - var/largest_click_time = 0 - var/mob/largest_move_mob = null - var/mob/largest_click_mob = null - for(var/mob/M in mob_list) - if(!M.client) - continue - if(M.next_move >= largest_move_time) - largest_move_mob = M - if(M.next_move > world.time) - largest_move_time = M.next_move - world.time - else - largest_move_time = 1 - if(M.next_click >= largest_click_time) - largest_click_mob = M - if(M.next_click > world.time) - largest_click_time = M.next_click - world.time - else - largest_click_time = 0 - log_admin("DEBUG: [key_name(M)] next_move = [M.next_move] next_click = [M.next_click] world.time = [world.time]") - M.next_move = 1 - M.next_click = 0 - message_admins("[key_name_admin(largest_move_mob)] had the largest move delay with [largest_move_time] frames / [largest_move_time/10] seconds!", 1) - message_admins("[key_name_admin(largest_click_mob)] had the largest click delay with [largest_click_time] frames / [largest_click_time/10] seconds!", 1) - message_admins("world.time = [world.time]", 1) - feedback_add_details("admin_verb","UFE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/radio_report() - set category = "Debug" - set name = "Radio report" - - var/output = "Radio Report
                    " - for (var/fq in radio_controller.frequencies) - output += "Freq: [fq]
                    " - var/datum/radio_frequency/fqs = radio_controller.frequencies[fq] - if (!fqs) - output += "  ERROR
                    " - continue - for (var/radio_filter in fqs.devices) - var/list/f = fqs.devices[radio_filter] - if (!f) - output += "  [radio_filter]: ERROR
                    " - continue - output += "  [radio_filter]: [f.len]
                    " - for (var/device in f) - if (isobj(device)) - output += "    [device] ([device:x],[device:y],[device:z] in area [get_area(device:loc)])
                    " - else - output += "    [device]
                    " - - usr << browse(output,"window=radioreport") - feedback_add_details("admin_verb","RR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/reload_admins() - set name = "Reload Admins" - set category = "Debug" - - if(!check_rights(R_SERVER)) return - - message_admins("[usr] manually reloaded admins") - load_admins() - feedback_add_details("admin_verb","RLDA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/reload_eventMs() - set name = "Reload Event Managers" - set category = "Debug" - - if(!check_rights(R_SERVER)) return - - message_admins("[usr] manually reloaded Event Managers") - world.load_mods() - - -//todo: -/client/proc/jump_to_dead_group() - set name = "Jump to dead group" - set category = "Debug" - /* - if(!holder) - to_chat(src, "Only administrators may use this command.") - return - - if(!air_master) - to_chat(usr, "Cannot find air_system") - return - var/datum/air_group/dead_groups = list() - for(var/datum/air_group/group in air_master.air_groups) - if (!group.group_processing) - dead_groups += group - var/datum/air_group/dest_group = pick(dead_groups) - usr.loc = pick(dest_group.members) - feedback_add_details("admin_verb","JDAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - */ - -/client/proc/kill_airgroup() - set name = "Kill Local Airgroup" - set desc = "Use this to allow manual manupliation of atmospherics." - set category = "Debug" - /* - if(!holder) - to_chat(src, "Only administrators may use this command.") - return - - if(!air_master) - to_chat(usr, "Cannot find air_system") - return - - var/turf/T = get_turf(usr) - if(istype(T, /turf/simulated)) - var/datum/air_group/AG = T:parent - AG.next_check = 30 - AG.group_processing = 0 - else - to_chat(usr, "Local airgroup is unsimulated!") - feedback_add_details("admin_verb","KLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - */ - -/client/proc/print_jobban_old() - set name = "Print Jobban Log" - set desc = "This spams all the active jobban entries for the current round to standard output." - set category = "Debug" - - to_chat(usr, "Jobbans active in this round.") - for(var/t in jobban_keylist) - to_chat(usr, "[t]") - -/client/proc/print_jobban_old_filter() - set name = "Search Jobban Log" - set desc = "This searches all the active jobban entries for the current round and outputs the results to standard output." - set category = "Debug" - - var/job_filter = tgui_input_text(usr, "Contains what?","Job Filter") - if(!job_filter) - return - - to_chat(usr, "Jobbans active in this round.") - for(var/t in jobban_keylist) - if(findtext(t, job_filter)) - to_chat(usr, "[t]") +/client/proc/air_report() + set category = "Debug" + set name = "Show Air Report" + + if(!master_controller || !air_master) + tgui_alert_async(usr,"Master_controller or air_master not found.","Air Report") + return + + var/active_groups = air_master.active_zones + var/inactive_groups = air_master.zones.len - active_groups + + var/hotspots = 0 + for(var/obj/fire/hotspot in world) + hotspots++ + + var/active_on_main_station = 0 + var/inactive_on_main_station = 0 + for(var/zone/zone in air_master.zones) + var/turf/simulated/turf = locate() in zone.contents + if(turf?.z in using_map.station_levels) + if(zone.needs_update) + active_on_main_station++ + else + inactive_on_main_station++ + + var/output = {"AIR SYSTEMS REPORT
                    +General Processing Data
                    + Cycle: [air_master.current_cycle]
                    + Groups: [air_master.zones.len]
                    +---- Active: [active_groups]
                    +---- Inactive: [inactive_groups]

                    +---- Active on station: [active_on_main_station]
                    +---- Inactive on station: [inactive_on_main_station]
                    +
                    +Special Processing Data
                    + Hotspot Processing: [hotspots]
                    +
                    +Geometry Processing Data
                    + Tile Update: [air_master.tiles_to_update.len]
                    +"} + + usr << browse(output,"window=airreport") + +/client/proc/fix_next_move() + set category = "Debug" + set name = "Unfreeze Everyone" + var/largest_move_time = 0 + var/largest_click_time = 0 + var/mob/largest_move_mob = null + var/mob/largest_click_mob = null + for(var/mob/M in mob_list) + if(!M.client) + continue + if(M.next_move >= largest_move_time) + largest_move_mob = M + if(M.next_move > world.time) + largest_move_time = M.next_move - world.time + else + largest_move_time = 1 + if(M.next_click >= largest_click_time) + largest_click_mob = M + if(M.next_click > world.time) + largest_click_time = M.next_click - world.time + else + largest_click_time = 0 + log_admin("DEBUG: [key_name(M)] next_move = [M.next_move] next_click = [M.next_click] world.time = [world.time]") + M.next_move = 1 + M.next_click = 0 + message_admins("[key_name_admin(largest_move_mob)] had the largest move delay with [largest_move_time] frames / [largest_move_time/10] seconds!", 1) + message_admins("[key_name_admin(largest_click_mob)] had the largest click delay with [largest_click_time] frames / [largest_click_time/10] seconds!", 1) + message_admins("world.time = [world.time]", 1) + feedback_add_details("admin_verb","UFE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/radio_report() + set category = "Debug" + set name = "Radio report" + + var/output = "Radio Report
                    " + for (var/fq in radio_controller.frequencies) + output += "Freq: [fq]
                    " + var/datum/radio_frequency/fqs = radio_controller.frequencies[fq] + if (!fqs) + output += "  ERROR
                    " + continue + for (var/radio_filter in fqs.devices) + var/list/f = fqs.devices[radio_filter] + if (!f) + output += "  [radio_filter]: ERROR
                    " + continue + output += "  [radio_filter]: [f.len]
                    " + for (var/device in f) + if (isobj(device)) + output += "    [device] ([device:x],[device:y],[device:z] in area [get_area(device:loc)])
                    " + else + output += "    [device]
                    " + + usr << browse(output,"window=radioreport") + feedback_add_details("admin_verb","RR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/reload_admins() + set name = "Reload Admins" + set category = "Debug" + + if(!check_rights(R_SERVER)) return + + message_admins("[usr] manually reloaded admins") + load_admins() + feedback_add_details("admin_verb","RLDA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/reload_eventMs() + set name = "Reload Event Managers" + set category = "Debug" + + if(!check_rights(R_SERVER)) return + + message_admins("[usr] manually reloaded Event Managers") + world.load_mods() + + +//todo: +/client/proc/jump_to_dead_group() + set name = "Jump to dead group" + set category = "Debug" + /* + if(!holder) + to_chat(src, "Only administrators may use this command.") + return + + if(!air_master) + to_chat(usr, "Cannot find air_system") + return + var/datum/air_group/dead_groups = list() + for(var/datum/air_group/group in air_master.air_groups) + if (!group.group_processing) + dead_groups += group + var/datum/air_group/dest_group = pick(dead_groups) + usr.loc = pick(dest_group.members) + feedback_add_details("admin_verb","JDAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + */ + +/client/proc/kill_airgroup() + set name = "Kill Local Airgroup" + set desc = "Use this to allow manual manupliation of atmospherics." + set category = "Debug" + /* + if(!holder) + to_chat(src, "Only administrators may use this command.") + return + + if(!air_master) + to_chat(usr, "Cannot find air_system") + return + + var/turf/T = get_turf(usr) + if(istype(T, /turf/simulated)) + var/datum/air_group/AG = T:parent + AG.next_check = 30 + AG.group_processing = 0 + else + to_chat(usr, "Local airgroup is unsimulated!") + feedback_add_details("admin_verb","KLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + */ + +/client/proc/print_jobban_old() + set name = "Print Jobban Log" + set desc = "This spams all the active jobban entries for the current round to standard output." + set category = "Debug" + + to_chat(usr, "Jobbans active in this round.") + for(var/t in jobban_keylist) + to_chat(usr, "[t]") + +/client/proc/print_jobban_old_filter() + set name = "Search Jobban Log" + set desc = "This searches all the active jobban entries for the current round and outputs the results to standard output." + set category = "Debug" + + var/job_filter = tgui_input_text(usr, "Contains what?","Job Filter") + if(!job_filter) + return + + to_chat(usr, "Jobbans active in this round.") + for(var/t in jobban_keylist) + if(findtext(t, job_filter)) + to_chat(usr, "[t]") diff --git a/code/modules/admin/verbs/fps.dm b/code/modules/admin/verbs/fps.dm index c3b9999afbc..3c1da452035 100644 --- a/code/modules/admin/verbs/fps.dm +++ b/code/modules/admin/verbs/fps.dm @@ -1,23 +1,23 @@ -//Merged Doohl's and the existing ticklag as they both had good elements about them ~ -//Replaces the old Ticklag verb, fps is easier to understand -/client/proc/set_server_fps() - set category = "Debug" - set name = "Set Server FPS" - set desc = "Sets game speed in frames-per-second. Can potentially break the game" - - if(!check_rights(R_DEBUG)) - return - - var/new_fps = round(tgui_input_number(usr, "Sets game frames-per-second. Can potentially break the game (default: [config.fps])", "FPS", world.fps)) - if(new_fps <= 0) - to_chat(src, "Error: set_server_fps(): Invalid world.fps value. No changes made.") - return - if(new_fps > config.fps * 1.5) - if(tgui_alert(src, "You are setting fps to a high value:\n\t[new_fps] frames-per-second\n\tconfig.fps = [config.fps]", "Warning!", list("Confirm", "ABORT-ABORT-ABORT")) != "Confirm") - return - - var/msg = "[key_name(src)] has modified world.fps to [new_fps]" - log_admin(msg, 0) - message_admins(msg, 0) - world.change_fps(new_fps) - feedback_add_details("admin_verb", "SETFPS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +//Merged Doohl's and the existing ticklag as they both had good elements about them ~ +//Replaces the old Ticklag verb, fps is easier to understand +/client/proc/set_server_fps() + set category = "Debug" + set name = "Set Server FPS" + set desc = "Sets game speed in frames-per-second. Can potentially break the game" + + if(!check_rights(R_DEBUG)) + return + + var/new_fps = round(tgui_input_number(usr, "Sets game frames-per-second. Can potentially break the game (default: [config.fps])", "FPS", world.fps), round(config.fps * 1.5)) + if(new_fps <= 0) + to_chat(src, "Error: set_server_fps(): Invalid world.fps value. No changes made.") + return + if(new_fps > config.fps * 1.5) + if(tgui_alert(src, "You are setting fps to a high value:\n\t[new_fps] frames-per-second\n\tconfig.fps = [config.fps]", "Warning!", list("Confirm", "ABORT-ABORT-ABORT")) != "Confirm") + return + + var/msg = "[key_name(src)] has modified world.fps to [new_fps]" + log_admin(msg, 0) + message_admins(msg, 0) + world.change_fps(new_fps) + feedback_add_details("admin_verb", "SETFPS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index 751fb534ebc..c57ad0b706c 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -1,311 +1,311 @@ -//world/proc/shelleo -#define SHELLEO_ERRORLEVEL 1 -#define SHELLEO_STDOUT 2 -#define SHELLEO_STDERR 3 - -var/list/sounds_cache = list() - -/client/proc/play_sound(S as sound) - set category = "Fun" - set name = "Play Global Sound" - if(!check_rights(R_SOUNDS)) - return - - var/freq = 1 - var/vol = tgui_input_number(usr, "What volume would you like the sound to play at?",, 100, 100, 1) - if(!vol) - return - vol = clamp(vol, 1, 100) - - var/sound/admin_sound = new() - admin_sound.file = S - admin_sound.priority = 250 - admin_sound.channel = 777 - admin_sound.frequency = freq - admin_sound.wait = 1 - admin_sound.repeat = FALSE - admin_sound.status = SOUND_STREAM - admin_sound.volume = vol - - sounds_cache += S - - var/res = tgui_alert(usr, "Show the title of this song ([S]) to the players?\nOptions 'Yes' and 'No' will play the sound.",, list("Yes", "No", "Cancel")) - switch(res) - if("Yes") - to_chat(world, "An admin played: [S]", confidential = TRUE) - if("Cancel") - return - - log_admin("[key_name(src)] played sound [S]") - message_admins("[key_name_admin(src)] played sound [S]", 1) - - for(var/mob/M in player_list) - if(M.is_preference_enabled(/datum/client_preference/play_admin_midis)) - admin_sound.volume = vol * M.client.admin_music_volume - SEND_SOUND(M, admin_sound) - admin_sound.volume = vol - - feedback_add_details("admin_verb", "Play Global Sound") - -/client/proc/play_local_sound(S as sound) - set category = "Fun" - set name = "Play Local Sound" - if(!check_rights(R_SOUNDS)) - return - - log_admin("[key_name(src)] played a local sound [S]") - message_admins("[key_name_admin(src)] played a local sound [S]", 1) - playsound(src.mob, S, 50, 0, 0) - feedback_add_details("admin_verb", "Play Local Sound") - -/client/proc/play_direct_mob_sound(S as sound, mob/M) - set category = "Fun" - set name = "Play Direct Mob Sound" - if(!check_rights(R_SOUNDS)) - return - - if(!M) - M = tgui_input_list(usr, "Choose a mob to play the sound to. Only they will hear it.", "Play Mob Sound", sortNames(player_list)) - if(!M || QDELETED(M)) - return - log_admin("[key_name(src)] played a direct mob sound [S] to [M].") - message_admins("[key_name_admin(src)] played a direct mob sound [S] to [ADMIN_LOOKUPFLW(M)].") - SEND_SOUND(M, S) - feedback_add_details("admin_verb", "Play Direct Mob Sound") - -/client/proc/play_z_sound(S as sound) - set category = "Fun" - set name = "Play Z Sound" - if(!check_rights(R_SOUNDS)) return - var/target_z = mob.z - var/sound/uploaded_sound = sound(S, repeat = 0, wait = 1, channel = 777) - uploaded_sound.priority = 250 - - sounds_cache += S - - if(tgui_alert(usr, "Do you ready?\nSong: [S]\nNow you can also play this sound using \"Play Server Sound\".", "Confirmation request", list("Play","Cancel")) == "Cancel") - return - - log_admin("[key_name(src)] played sound [S] on Z[target_z]") - message_admins("[key_name_admin(src)] played sound [S] on Z[target_z]", 1) - for(var/mob/M in player_list) - if(M.is_preference_enabled(/datum/client_preference/play_admin_midis) && M.z == target_z) - M << uploaded_sound - - feedback_add_details("admin_verb", "Play Z Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/play_server_sound() - set category = "Fun" - set name = "Play Server Sound" - if(!check_rights(R_SOUNDS)) - return - - var/list/sounds = file2list("sound/serversound_list.txt"); - sounds += "--CANCEL--" - sounds += sounds_cache - - var/melody = tgui_input_list(usr, "Select a sound from the server to play", "Server sound list", sounds, "--CANCEL--") - - if(melody == "--CANCEL--") - return - - play_sound(melody) - feedback_add_details("admin_verb", "Play Server Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -///Takes an input from either proc/play_web_sound or the request manager and runs it through youtube-dl and prompts the user before playing it to the server. -/proc/web_sound(mob/user, input, credit) - if(!check_rights(R_SOUNDS)) - return - var/ytdl = config.invoke_youtubedl - if(!ytdl) - to_chat(user, "Youtube-dl was not configured, action unavailable", confidential = TRUE) //Check config.txt for the INVOKE_YOUTUBEDL value - return - var/web_sound_url = "" - var/stop_web_sounds = FALSE - var/list/music_extra_data = list() - var/duration = 0 - if(istext(input)) - var/shell_scrubbed_input = shell_url_scrub(input) - var/list/output = world.shelleo("[ytdl] --geo-bypass --format \"bestaudio\[ext=mp3]/best\[ext=mp4]\[height <= 360]/bestaudio\[ext=m4a]/bestaudio\[ext=aac]\" --dump-single-json --no-playlist -- \"[shell_scrubbed_input]\"") - var/errorlevel = output[SHELLEO_ERRORLEVEL] - var/stdout = output[SHELLEO_STDOUT] - var/stderr = output[SHELLEO_STDERR] - if(errorlevel) - to_chat(user, "Youtube-dl URL retrieval FAILED:", confidential = TRUE) - to_chat(user, "[stderr]", confidential = TRUE) - return - var/list/data - try - data = json_decode(stdout) - catch(var/exception/e) - to_chat(user, "Youtube-dl JSON parsing FAILED:", confidential = TRUE) - to_chat(user, "[e]: [stdout]", confidential = TRUE) - return - if (data["url"]) - web_sound_url = data["url"] - var/title = "[data["title"]]" - var/webpage_url = title - if (data["webpage_url"]) - webpage_url = "[title]" - music_extra_data["duration"] = DisplayTimeText(data["duration"] * 1 SECONDS) - music_extra_data["link"] = data["webpage_url"] - music_extra_data["artist"] = data["artist"] - music_extra_data["upload_date"] = data["upload_date"] - music_extra_data["album"] = data["album"] - duration = data["duration"] * 1 SECONDS - if (duration > 10 MINUTES) - if((tgui_alert(user, "This song is over 10 minutes long. Are you sure you want to play it?", "Length Warning!", list("No", "Yes", "Cancel")) != "Yes")) - return - var/res = tgui_alert(user, "Show the title of and link to this song to the players?\n[title]", "Show Info?", list("Yes", "No", "Cancel")) - switch(res) - if("Yes") - music_extra_data["title"] = data["title"] - if("No") - music_extra_data["link"] = "Song Link Hidden" - music_extra_data["title"] = "Song Title Hidden" - music_extra_data["artist"] = "Song Artist Hidden" - music_extra_data["upload_date"] = "Song Upload Date Hidden" - music_extra_data["album"] = "Song Album Hidden" - if("Cancel", null) - return - var/anon = tgui_alert(user, "Display who played the song?", "Credit Yourself?", list("Yes", "No", "Cancel")) - switch(anon) - if("Yes") - if(res == "Yes") - to_chat(world, "[user.key] played: [webpage_url]", confidential = TRUE) - else - to_chat(world, "[user.key] played a sound", confidential = TRUE) - if("No") - if(res == "Yes") - to_chat(world, "An admin played: [webpage_url]", confidential = TRUE) - if("Cancel", null) - return - if(credit) - to_chat(world, "[credit]", confidential = TRUE) - //SSblackbox.record_feedback("nested tally", "played_url", 1, list("[user.ckey]", "[input]")) - log_admin("[key_name(user)] played web sound: [input]") - message_admins("[key_name(user)] played web sound: [input]") - else - //pressed ok with blank - log_admin("[key_name(user)] stopped web sounds.") - - message_admins("[key_name(user)] stopped web sounds.") - web_sound_url = null - stop_web_sounds = TRUE - if(web_sound_url && !findtext(web_sound_url, GLOB.is_http_protocol)) - tgui_alert(user, "The media provider returned a content URL that isn't using the HTTP or HTTPS protocol. This is a security risk and the sound will not be played.", "Security Risk", list("OK")) - to_chat(user, "BLOCKED: Content URL not using HTTP(S) Protocol!", confidential = TRUE) - - return - if(web_sound_url || stop_web_sounds) - for(var/m in player_list) - var/mob/M = m - var/client/C = M.client - if(C.is_preference_enabled(/datum/client_preference/play_admin_midis)) - if(!stop_web_sounds) - C.tgui_panel?.play_music(web_sound_url, music_extra_data) - else - C.tgui_panel?.stop_music() - - S_TIMER_COOLDOWN_START(SStimer, COOLDOWN_INTERNET_SOUND, duration) - - feedback_add_details("admin_verb", "Play Internet Sound") - -/client/proc/play_web_sound() - set category = "Fun" - set name = "Play Internet Sound" - if(!check_rights(R_SOUNDS)) - return - - var/ytdl = config.invoke_youtubedl - if(!ytdl) - to_chat(src, "Youtube-dl was not configured, action unavailable", confidential = TRUE) //Check config.txt for the INVOKE_YOUTUBEDL value - return - - if(S_TIMER_COOLDOWN_TIMELEFT(SStimer, COOLDOWN_INTERNET_SOUND)) - if(tgui_alert(usr, "Someone else is already playing an Internet sound! It has [DisplayTimeText(S_TIMER_COOLDOWN_TIMELEFT(SStimer, COOLDOWN_INTERNET_SOUND), 1)] remaining. \ - Would you like to override?", "Musicalis Interruptus", list("No","Yes")) != "Yes") - return - - var/web_sound_input = tgui_input_text(usr, "Enter content URL (supported sites only, leave blank to stop playing)", "Play Internet Sound", null) - - if(length(web_sound_input)) - web_sound_input = trim(web_sound_input) - if(findtext(web_sound_input, ":") && !findtext(web_sound_input, GLOB.is_http_protocol)) - to_chat(src, "Non-http(s) URIs are not allowed.", confidential = TRUE) - to_chat(src, "For youtube-dl shortcuts like ytsearch: please use the appropriate full URL from the website.", confidential = TRUE) - return - web_sound(usr, web_sound_input) - else - web_sound(usr, null) - -/client/proc/stop_sounds() - set category = "Debug" - set name = "Stop All Playing Sounds" - if(!src.holder) - return - - log_admin("[key_name(src)] stopped all currently playing sounds.") - message_admins("[key_name_admin(src)] stopped all currently playing sounds.") - for(var/mob/M in player_list) - SEND_SOUND(M, sound(null)) - var/client/C = M.client - C?.tgui_panel?.stop_music() - - S_TIMER_COOLDOWN_RESET(SStimer, COOLDOWN_INTERNET_SOUND) - feedback_add_details("admin_verb", "Stop All Playing Sounds") - -//world/proc/shelleo -#undef SHELLEO_ERRORLEVEL -#undef SHELLEO_STDOUT -#undef SHELLEO_STDERR - -/* -/client/proc/cuban_pete() - set category = "Fun" - set name = "Cuban Pete Time" - - message_admins("[key_name_admin(usr)] has declared Cuban Pete Time!", 1) - for(var/mob/M in player_list) - if(M.client) - if(M.client.midis) - M << 'cubanpetetime.ogg' - - for(var/mob/living/carbon/human/CP in human_mob_list) - if(CP.real_name=="Cuban Pete" && CP.key!="Rosham") - to_chat(CP, "Your body can't contain the rhumba beat") - CP.gib() - - -/client/proc/bananaphone() - set category = "Fun" - set name = "Banana Phone" - - message_admins("[key_name_admin(usr)] has activated Banana Phone!", 1) - for(var/mob/M in player_list) - if(M.client) - if(M.client.midis) - M << 'bananaphone.ogg' - - -/client/proc/space_asshole() - set category = "Fun" - set name = "Space Asshole" - - message_admins("[key_name_admin(usr)] has played the Space Asshole Hymn.", 1) - for(var/mob/M in player_list) - if(M.client) - if(M.client.midis) - M << 'sound/music/space_asshole.ogg' - - -/client/proc/honk_theme() - set category = "Fun" - set name = "Honk" - - message_admins("[key_name_admin(usr)] has creeped everyone out with Blackest Honks.", 1) - for(var/mob/M in player_list) - if(M.client) - if(M.client.midis) - M << 'honk_theme.ogg'*/ +//world/proc/shelleo +#define SHELLEO_ERRORLEVEL 1 +#define SHELLEO_STDOUT 2 +#define SHELLEO_STDERR 3 + +var/list/sounds_cache = list() + +/client/proc/play_sound(S as sound) + set category = "Fun" + set name = "Play Global Sound" + if(!check_rights(R_SOUNDS)) + return + + var/freq = 1 + var/vol = tgui_input_number(usr, "What volume would you like the sound to play at?",, 100, 100, 1) + if(!vol) + return + vol = clamp(vol, 1, 100) + + var/sound/admin_sound = new() + admin_sound.file = S + admin_sound.priority = 250 + admin_sound.channel = 777 + admin_sound.frequency = freq + admin_sound.wait = 1 + admin_sound.repeat = FALSE + admin_sound.status = SOUND_STREAM + admin_sound.volume = vol + + sounds_cache += S + + var/res = tgui_alert(usr, "Show the title of this song ([S]) to the players?\nOptions 'Yes' and 'No' will play the sound.",, list("Yes", "No", "Cancel")) + switch(res) + if("Yes") + to_chat(world, "An admin played: [S]", confidential = TRUE) + if("Cancel") + return + + log_admin("[key_name(src)] played sound [S]") + message_admins("[key_name_admin(src)] played sound [S]", 1) + + for(var/mob/M in player_list) + if(M.is_preference_enabled(/datum/client_preference/play_admin_midis)) + admin_sound.volume = vol * M.client.admin_music_volume + SEND_SOUND(M, admin_sound) + admin_sound.volume = vol + + feedback_add_details("admin_verb", "Play Global Sound") + +/client/proc/play_local_sound(S as sound) + set category = "Fun" + set name = "Play Local Sound" + if(!check_rights(R_SOUNDS)) + return + + log_admin("[key_name(src)] played a local sound [S]") + message_admins("[key_name_admin(src)] played a local sound [S]", 1) + playsound(src.mob, S, 50, 0, 0) + feedback_add_details("admin_verb", "Play Local Sound") + +/client/proc/play_direct_mob_sound(S as sound, mob/M) + set category = "Fun" + set name = "Play Direct Mob Sound" + if(!check_rights(R_SOUNDS)) + return + + if(!M) + M = tgui_input_list(usr, "Choose a mob to play the sound to. Only they will hear it.", "Play Mob Sound", sortNames(player_list)) + if(!M || QDELETED(M)) + return + log_admin("[key_name(src)] played a direct mob sound [S] to [M].") + message_admins("[key_name_admin(src)] played a direct mob sound [S] to [ADMIN_LOOKUPFLW(M)].") + SEND_SOUND(M, S) + feedback_add_details("admin_verb", "Play Direct Mob Sound") + +/client/proc/play_z_sound(S as sound) + set category = "Fun" + set name = "Play Z Sound" + if(!check_rights(R_SOUNDS)) return + var/target_z = mob.z + var/sound/uploaded_sound = sound(S, repeat = 0, wait = 1, channel = 777) + uploaded_sound.priority = 250 + + sounds_cache += S + + if(tgui_alert(usr, "Do you ready?\nSong: [S]\nNow you can also play this sound using \"Play Server Sound\".", "Confirmation request", list("Play","Cancel")) == "Cancel") + return + + log_admin("[key_name(src)] played sound [S] on Z[target_z]") + message_admins("[key_name_admin(src)] played sound [S] on Z[target_z]", 1) + for(var/mob/M in player_list) + if(M.is_preference_enabled(/datum/client_preference/play_admin_midis) && M.z == target_z) + M << uploaded_sound + + feedback_add_details("admin_verb", "Play Z Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/play_server_sound() + set category = "Fun" + set name = "Play Server Sound" + if(!check_rights(R_SOUNDS)) + return + + var/list/sounds = file2list("sound/serversound_list.txt"); + sounds += "--CANCEL--" + sounds += sounds_cache + + var/melody = tgui_input_list(usr, "Select a sound from the server to play", "Server sound list", sounds, "--CANCEL--") + + if(melody == "--CANCEL--") + return + + play_sound(melody) + feedback_add_details("admin_verb", "Play Server Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +///Takes an input from either proc/play_web_sound or the request manager and runs it through youtube-dl and prompts the user before playing it to the server. +/proc/web_sound(mob/user, input, credit) + if(!check_rights(R_SOUNDS)) + return + var/ytdl = config.invoke_youtubedl + if(!ytdl) + to_chat(user, "Youtube-dl was not configured, action unavailable", confidential = TRUE) //Check config.txt for the INVOKE_YOUTUBEDL value + return + var/web_sound_url = "" + var/stop_web_sounds = FALSE + var/list/music_extra_data = list() + var/duration = 0 + if(istext(input)) + var/shell_scrubbed_input = shell_url_scrub(input) + var/list/output = world.shelleo("[ytdl] --geo-bypass --format \"bestaudio\[ext=mp3]/best\[ext=mp4]\[height <= 360]/bestaudio\[ext=m4a]/bestaudio\[ext=aac]\" --dump-single-json --no-playlist -- \"[shell_scrubbed_input]\"") + var/errorlevel = output[SHELLEO_ERRORLEVEL] + var/stdout = output[SHELLEO_STDOUT] + var/stderr = output[SHELLEO_STDERR] + if(errorlevel) + to_chat(user, "Youtube-dl URL retrieval FAILED:", confidential = TRUE) + to_chat(user, "[stderr]", confidential = TRUE) + return + var/list/data + try + data = json_decode(stdout) + catch(var/exception/e) + to_chat(user, "Youtube-dl JSON parsing FAILED:", confidential = TRUE) + to_chat(user, "[e]: [stdout]", confidential = TRUE) + return + if (data["url"]) + web_sound_url = data["url"] + var/title = "[data["title"]]" + var/webpage_url = title + if (data["webpage_url"]) + webpage_url = "[title]" + music_extra_data["duration"] = DisplayTimeText(data["duration"] * 1 SECONDS) + music_extra_data["link"] = data["webpage_url"] + music_extra_data["artist"] = data["artist"] + music_extra_data["upload_date"] = data["upload_date"] + music_extra_data["album"] = data["album"] + duration = data["duration"] * 1 SECONDS + if (duration > 10 MINUTES) + if((tgui_alert(user, "This song is over 10 minutes long. Are you sure you want to play it?", "Length Warning!", list("No", "Yes", "Cancel")) != "Yes")) + return + var/res = tgui_alert(user, "Show the title of and link to this song to the players?\n[title]", "Show Info?", list("Yes", "No", "Cancel")) + switch(res) + if("Yes") + music_extra_data["title"] = data["title"] + if("No") + music_extra_data["link"] = "Song Link Hidden" + music_extra_data["title"] = "Song Title Hidden" + music_extra_data["artist"] = "Song Artist Hidden" + music_extra_data["upload_date"] = "Song Upload Date Hidden" + music_extra_data["album"] = "Song Album Hidden" + if("Cancel", null) + return + var/anon = tgui_alert(user, "Display who played the song?", "Credit Yourself?", list("Yes", "No", "Cancel")) + switch(anon) + if("Yes") + if(res == "Yes") + to_chat(world, "[user.key] played: [webpage_url]", confidential = TRUE) + else + to_chat(world, "[user.key] played a sound", confidential = TRUE) + if("No") + if(res == "Yes") + to_chat(world, "An admin played: [webpage_url]", confidential = TRUE) + if("Cancel", null) + return + if(credit) + to_chat(world, "[credit]", confidential = TRUE) + //SSblackbox.record_feedback("nested tally", "played_url", 1, list("[user.ckey]", "[input]")) + log_admin("[key_name(user)] played web sound: [input]") + message_admins("[key_name(user)] played web sound: [input]") + else + //pressed ok with blank + log_admin("[key_name(user)] stopped web sounds.") + + message_admins("[key_name(user)] stopped web sounds.") + web_sound_url = null + stop_web_sounds = TRUE + if(web_sound_url && !findtext(web_sound_url, GLOB.is_http_protocol)) + tgui_alert(user, "The media provider returned a content URL that isn't using the HTTP or HTTPS protocol. This is a security risk and the sound will not be played.", "Security Risk", list("OK")) + to_chat(user, "BLOCKED: Content URL not using HTTP(S) Protocol!", confidential = TRUE) + + return + if(web_sound_url || stop_web_sounds) + for(var/m in player_list) + var/mob/M = m + var/client/C = M.client + if(C.is_preference_enabled(/datum/client_preference/play_admin_midis)) + if(!stop_web_sounds) + C.tgui_panel?.play_music(web_sound_url, music_extra_data) + else + C.tgui_panel?.stop_music() + + S_TIMER_COOLDOWN_START(SStimer, COOLDOWN_INTERNET_SOUND, duration) + + feedback_add_details("admin_verb", "Play Internet Sound") + +/client/proc/play_web_sound() + set category = "Fun" + set name = "Play Internet Sound" + if(!check_rights(R_SOUNDS)) + return + + var/ytdl = config.invoke_youtubedl + if(!ytdl) + to_chat(src, "Youtube-dl was not configured, action unavailable", confidential = TRUE) //Check config.txt for the INVOKE_YOUTUBEDL value + return + + if(S_TIMER_COOLDOWN_TIMELEFT(SStimer, COOLDOWN_INTERNET_SOUND)) + if(tgui_alert(usr, "Someone else is already playing an Internet sound! It has [DisplayTimeText(S_TIMER_COOLDOWN_TIMELEFT(SStimer, COOLDOWN_INTERNET_SOUND), 1)] remaining. \ + Would you like to override?", "Musicalis Interruptus", list("No","Yes")) != "Yes") + return + + var/web_sound_input = tgui_input_text(usr, "Enter content URL (supported sites only, leave blank to stop playing)", "Play Internet Sound", null) + + if(length(web_sound_input)) + web_sound_input = trim(web_sound_input) + if(findtext(web_sound_input, ":") && !findtext(web_sound_input, GLOB.is_http_protocol)) + to_chat(src, "Non-http(s) URIs are not allowed.", confidential = TRUE) + to_chat(src, "For youtube-dl shortcuts like ytsearch: please use the appropriate full URL from the website.", confidential = TRUE) + return + web_sound(usr, web_sound_input) + else + web_sound(usr, null) + +/client/proc/stop_sounds() + set category = "Debug" + set name = "Stop All Playing Sounds" + if(!src.holder) + return + + log_admin("[key_name(src)] stopped all currently playing sounds.") + message_admins("[key_name_admin(src)] stopped all currently playing sounds.") + for(var/mob/M in player_list) + SEND_SOUND(M, sound(null)) + var/client/C = M.client + C?.tgui_panel?.stop_music() + + S_TIMER_COOLDOWN_RESET(SStimer, COOLDOWN_INTERNET_SOUND) + feedback_add_details("admin_verb", "Stop All Playing Sounds") + +//world/proc/shelleo +#undef SHELLEO_ERRORLEVEL +#undef SHELLEO_STDOUT +#undef SHELLEO_STDERR + +/* +/client/proc/cuban_pete() + set category = "Fun" + set name = "Cuban Pete Time" + + message_admins("[key_name_admin(usr)] has declared Cuban Pete Time!", 1) + for(var/mob/M in player_list) + if(M.client) + if(M.client.midis) + M << 'cubanpetetime.ogg' + + for(var/mob/living/carbon/human/CP in human_mob_list) + if(CP.real_name=="Cuban Pete" && CP.key!="Rosham") + to_chat(CP, "Your body can't contain the rhumba beat") + CP.gib() + + +/client/proc/bananaphone() + set category = "Fun" + set name = "Banana Phone" + + message_admins("[key_name_admin(usr)] has activated Banana Phone!", 1) + for(var/mob/M in player_list) + if(M.client) + if(M.client.midis) + M << 'bananaphone.ogg' + + +/client/proc/space_asshole() + set category = "Fun" + set name = "Space Asshole" + + message_admins("[key_name_admin(usr)] has played the Space Asshole Hymn.", 1) + for(var/mob/M in player_list) + if(M.client) + if(M.client.midis) + M << 'sound/music/space_asshole.ogg' + + +/client/proc/honk_theme() + set category = "Fun" + set name = "Honk" + + message_admins("[key_name_admin(usr)] has creeped everyone out with Blackest Honks.", 1) + for(var/mob/M in player_list) + if(M.client) + if(M.client.midis) + M << 'honk_theme.ogg'*/ diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index 4add0123f8a..7725ad92728 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -1,44 +1,44 @@ -/mob/verb/pray() - set category = "IC" - set name = "Pray" - - if(say_disabled) //This is here to try to identify lag problems - to_chat(usr, span_red("Speech is currently admin-disabled.")) - return - - var/raw_msg = sanitize(tgui_input_text(usr, "Prayers are sent to staff but do not open tickets or go to Discord. If you have a technical difficulty or an event/spice idea/hook - please ahelp instead. Thank you!", "Pray", null, MAX_MESSAGE_LEN)) - if(!raw_msg) return - - if(usr.client) - if(raw_msg) - client.handle_spam_prevention(MUTE_PRAY) - if(usr.client.prefs.muted & MUTE_PRAY) - to_chat(usr, span_red("You cannot pray (muted).")) - return - - var/icon/cross = icon('icons/obj/storage.dmi',"bible") - var/msg = "" + span_blue("\icon[cross][bicon(cross)] " + span_purple("PRAY: ") + "[key_name(src, 1)] [ADMIN_QUE(src)] [ADMIN_PP(src)] [ADMIN_VV(src)] [ADMIN_SM(src)] ([admin_jump_link(src, src)]) [ADMIN_CA(src)] [ADMIN_SC(src)] [ADMIN_SMITE(src)]: [raw_msg]") + "" - - for(var/client/C in GLOB.admins) - if(R_ADMIN|R_EVENT & C.holder.rights) - if(C.is_preference_enabled(/datum/client_preference/admin/show_chat_prayers)) - to_chat(C, msg, type = MESSAGE_TYPE_PRAYER, confidential = TRUE) - C << 'sound/effects/ding.ogg' - to_chat(usr, "Your prayers have been received by the gods.", confidential = TRUE) - - feedback_add_details("admin_verb","PR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_pray(raw_msg, src) - -/proc/CentCom_announce(var/msg, var/mob/Sender, var/iamessage) - msg = span_blue("" + span_orange("[uppertext(using_map.boss_short)]M[iamessage ? " IA" : ""]:") + "[key_name(Sender, 1)] [ADMIN_PP(Sender)] [ADMIN_VV(Sender)] [ADMIN_SM(Sender)] ([admin_jump_link(Sender)]) [ADMIN_CA(Sender)] [ADMIN_BSA(Sender)] [ADMIN_CENTCOM_REPLY(Sender)]: [msg]") - for(var/client/C in GLOB.admins) //VOREStation Edit - GLOB admins - if(R_ADMIN|R_EVENT & C.holder.rights) - to_chat(C,msg) - C << 'sound/machines/signal.ogg' - -/proc/Syndicate_announce(var/msg, var/mob/Sender) - msg = span_blue("" + span_crimson("ILLEGAL:") + "[key_name(Sender, 1)] [ADMIN_PP(Sender)] [ADMIN_VV(Sender)] [ADMIN_SM(Sender)] ([admin_jump_link(Sender)]) [ADMIN_CA(Sender)] [ADMIN_BSA(Sender)] [ADMIN_SYNDICATE_REPLY(Sender)]: [msg]") - for(var/client/C in GLOB.admins) //VOREStation Edit - GLOB admins - if(R_ADMIN|R_EVENT & C.holder.rights) - to_chat(C,msg) - C << 'sound/machines/signal.ogg' +/mob/verb/pray() + set category = "IC" + set name = "Pray" + + if(say_disabled) //This is here to try to identify lag problems + to_chat(usr, span_red("Speech is currently admin-disabled.")) + return + + var/raw_msg = sanitize(tgui_input_text(usr, "Prayers are sent to staff but do not open tickets or go to Discord. If you have a technical difficulty or an event/spice idea/hook - please ahelp instead. Thank you!", "Pray", null, MAX_MESSAGE_LEN)) + if(!raw_msg) return + + if(usr.client) + if(raw_msg) + client.handle_spam_prevention(MUTE_PRAY) + if(usr.client.prefs.muted & MUTE_PRAY) + to_chat(usr, span_red("You cannot pray (muted).")) + return + + var/icon/cross = icon('icons/obj/storage.dmi',"bible") + var/msg = "" + span_blue("\icon[cross][bicon(cross)] " + span_purple("PRAY: ") + "[key_name(src, 1)] [ADMIN_QUE(src)] [ADMIN_PP(src)] [ADMIN_VV(src)] [ADMIN_SM(src)] ([admin_jump_link(src, src)]) [ADMIN_CA(src)] [ADMIN_SC(src)] [ADMIN_SMITE(src)]: [raw_msg]") + "" + + for(var/client/C in GLOB.admins) + if(R_ADMIN|R_EVENT & C.holder.rights) + if(C.is_preference_enabled(/datum/client_preference/admin/show_chat_prayers)) + to_chat(C, msg, type = MESSAGE_TYPE_PRAYER, confidential = TRUE) + C << 'sound/effects/ding.ogg' + to_chat(usr, "Your prayers have been received by the gods.", confidential = TRUE) + + feedback_add_details("admin_verb","PR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_pray(raw_msg, src) + +/proc/CentCom_announce(var/msg, var/mob/Sender, var/iamessage) + msg = span_blue("" + span_orange("[uppertext(using_map.boss_short)]M[iamessage ? " IA" : ""]:") + "[key_name(Sender, 1)] [ADMIN_PP(Sender)] [ADMIN_VV(Sender)] [ADMIN_SM(Sender)] ([admin_jump_link(Sender)]) [ADMIN_CA(Sender)] [ADMIN_BSA(Sender)] [ADMIN_CENTCOM_REPLY(Sender)]: [msg]") + for(var/client/C in GLOB.admins) //VOREStation Edit - GLOB admins + if(R_ADMIN|R_EVENT & C.holder.rights) + to_chat(C,msg) + C << 'sound/machines/signal.ogg' + +/proc/Syndicate_announce(var/msg, var/mob/Sender) + msg = span_blue("" + span_crimson("ILLEGAL:") + "[key_name(Sender, 1)] [ADMIN_PP(Sender)] [ADMIN_VV(Sender)] [ADMIN_SM(Sender)] ([admin_jump_link(Sender)]) [ADMIN_CA(Sender)] [ADMIN_BSA(Sender)] [ADMIN_SYNDICATE_REPLY(Sender)]: [msg]") + for(var/client/C in GLOB.admins) //VOREStation Edit - GLOB admins + if(R_ADMIN|R_EVENT & C.holder.rights) + to_chat(C,msg) + C << 'sound/machines/signal.ogg' diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 19f75f850e5..7d629b735a1 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1,1164 +1,1164 @@ -/client/proc/cmd_admin_drop_everything(mob/M as mob in mob_list) - set category = null - set name = "Drop Everything" - if(!holder) - return - - var/confirm = tgui_alert(src, "Make [M] drop everything?", "Message", list("Yes", "No")) - if(confirm != "Yes") - return - - for(var/obj/item/W in M) - if(istype(W, /obj/item/weapon/implant/backup) || istype(W, /obj/item/device/nif)) //VOREStation Edit - There's basically no reason to remove either of these - continue //VOREStation Edit - M.drop_from_inventory(W) - - log_admin("[key_name(usr)] made [key_name(M)] drop everything!") - message_admins("[key_name_admin(usr)] made [key_name_admin(M)] drop everything!", 1) - feedback_add_details("admin_verb","DEVR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_prison(mob/M as mob in mob_list) - set category = "Admin" - set name = "Prison" - if(!holder) - return - - if (ismob(M)) - if(istype(M, /mob/living/silicon/ai)) - tgui_alert_async(usr, "The AI can't be sent to prison you jerk!") - return - //strip their stuff before they teleport into a cell :downs: - for(var/obj/item/W in M) - M.drop_from_inventory(W) - //teleport person to cell - M.Paralyse(5) - sleep(5) //so they black out before warping - M.loc = pick(prisonwarp) - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/prisoner = M - prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/prison(prisoner), slot_w_uniform) - prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) - spawn(50) - to_chat(M, span_red("You have been sent to the prison station!")) - log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") - message_admins(span_blue("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station."), 1) - feedback_add_details("admin_verb","PRISON") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -//Allows staff to determine who the newer players are. -/client/proc/cmd_check_new_players() - set category = "Admin" - set name = "Check new Players" - if(!holder) - return - - var/age = tgui_alert(src, "Age check", "Show accounts yonger then _____ days", list("7","30","All")) - - if(age == "All") - age = 9999999 - else - age = text2num(age) - - var/missing_ages = 0 - var/msg = "" - - var/highlight_special_characters = 1 - - for(var/client/C in GLOB.clients) - if(C.player_age == "Requires database") - missing_ages = 1 - continue - if(C.player_age < age) - msg += "[key_name(C, 1, 1, highlight_special_characters)]: account is [C.player_age] days old
                    " - - if(missing_ages) - to_chat(src, "Some accounts did not have proper ages set in their clients. This function requires database to be present.") - - if(msg != "") - src << browse(msg, "window=Player_age_check") - else - to_chat(src, "No matches for that age range found.") - -/client/proc/cmd_admin_subtle_message(mob/M as mob in mob_list) - set category = "Special Verbs" - set name = "Subtle Message" - - if(!ismob(M)) return - if (!holder) - return - - var/msg = tgui_input_text(usr, "Message:", text("Subtle PM to [M.key]")) - - if (!msg) - return - - if(!(msg[1] == "<" && msg[length(msg)] == ">")) //You can use HTML but only if the whole thing is HTML. Tries to prevent admin 'accidents'. - msg = sanitize(msg) - - if(usr) - if (usr.client) - if(usr.client.holder) - to_chat(M, "You hear a voice in your head... [msg]") - - log_admin("SubtlePM: [key_name(usr)] -> [key_name(M)] : [msg]") - msg = " SubtleMessage: [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]" - message_admins(msg) - admin_ticket_log(M, msg) - feedback_add_details("admin_verb","SMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_world_narrate() // Allows administrators to fluff events a little easier -- TLE - set category = "Special Verbs" - set name = "Global Narrate" - - if (!holder) - return - - var/msg = tgui_input_text(usr, "Message:", text("Enter the text you wish to appear to everyone:")) - - if (!msg) - return - if(!(msg[1] == "<" && msg[length(msg)] == ">")) //You can use HTML but only if the whole thing is HTML. Tries to prevent admin 'accidents'. - msg = sanitize(msg) - if (!msg) // We check both before and after, just in case sanitization ended us up with empty message. - return - - to_world("[msg]") - log_admin("GlobalNarrate: [key_name(usr)] : [msg]") - message_admins(span_blue(" GlobalNarrate: [key_name_admin(usr)] : [msg]
                    "), 1) - feedback_add_details("admin_verb","GLN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_direct_narrate(var/mob/M) // Targetted narrate -- TLE - set category = "Special Verbs" - set name = "Direct Narrate" - - if(!holder) - return - - if(!M) - M = tgui_input_list(usr, "Direct narrate to who?", "Active Players", get_mob_with_client_list()) - - if(!M) - return - - var/msg = tgui_input_text(usr, "Message:", text("Enter the text you wish to appear to your target:")) - if(msg && !(msg[1] == "<" && msg[length(msg)] == ">")) //You can use HTML but only if the whole thing is HTML. Tries to prevent admin 'accidents'. - msg = sanitize(msg) - - if( !msg ) - return - - to_chat(M, msg) - log_admin("DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]") - msg = " DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]
                    " - message_admins(msg) - admin_ticket_log(M, msg) - feedback_add_details("admin_verb","DIRN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_godmode(mob/M as mob in mob_list) - set category = "Special Verbs" - set name = "Godmode" - - if(!holder) - return - - M.status_flags ^= GODMODE - to_chat(usr, span_blue("Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]")) - - log_admin("[key_name(usr)] has toggled [key_name(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]") - var/msg = "[key_name_admin(usr)] has toggled [ADMIN_LOOKUPFLW(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]" - message_admins(msg) - admin_ticket_log(M, msg) - feedback_add_details("admin_verb","GOD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0) - if(automute) - if(!config.automute_on) - return - else - if(!usr || !usr.client) - return - if(!usr.client.holder) - to_chat(usr, span_red("Error: cmd_admin_mute: You don't have permission to do this.")) - return - if(!M.client) - to_chat(usr, span_red("Error: cmd_admin_mute: This mob doesn't have a client tied to it.")) - if(M.client.holder) - to_chat(usr, span_red("Error: cmd_admin_mute: You cannot mute an admin/mod.")) - if(!M.client) - return - if(M.client.holder) - return - - var/muteunmute - var/mute_string - - switch(mute_type) - if(MUTE_IC) mute_string = "IC (say and emote)" - if(MUTE_OOC) mute_string = "OOC" - if(MUTE_PRAY) mute_string = "pray" - if(MUTE_ADMINHELP) mute_string = "adminhelp, admin PM and ASAY" - if(MUTE_DEADCHAT) mute_string = "deadchat and DSAY" - if(MUTE_ALL) mute_string = "everything" - else return - - if(automute) - muteunmute = "auto-muted" - M.client.prefs.muted |= mute_type - log_admin("SPAM AUTOMUTE: [muteunmute] [key_name(M)] from [mute_string]") - message_admins("SPAM AUTOMUTE: [muteunmute] [key_name_admin(M)] from [mute_string].", 1) - to_chat(M, "You have been [muteunmute] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin.") - feedback_add_details("admin_verb","AUTOMUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - - if(M.client.prefs.muted & mute_type) - muteunmute = "unmuted" - M.client.prefs.muted &= ~mute_type - else - muteunmute = "muted" - M.client.prefs.muted |= mute_type - - log_admin("[key_name(usr)] has [muteunmute] [key_name(M)] from [mute_string]") - message_admins("[key_name_admin(usr)] has [muteunmute] [key_name_admin(M)] from [mute_string].", 1) - to_chat(M, "You have been [muteunmute] from [mute_string].") - feedback_add_details("admin_verb","MUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_add_random_ai_law() - set category = "Fun" - set name = "Add Random AI Law" - - if(!holder) - return - - var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) - if(confirm != "Yes") return - log_admin("[key_name(src)] has added a random AI law.") - message_admins("[key_name_admin(src)] has added a random AI law.", 1) - - var/show_log = tgui_alert(src, "Show ion message?", "Message", list("Yes", "No")) - if(show_log == "Yes") - command_announcement.Announce("Ion storm detected near \the [station_name()]. Please check all AI-controlled equipment for errors.", "Anomaly Alert", new_sound = 'sound/AI/ionstorm.ogg') - - IonStorm(0) - feedback_add_details("admin_verb","ION") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/* -Allow admins to set players to be able to respawn/bypass 30 min wait, without the admin having to edit variables directly -Ccomp's first proc. -*/ - -/client/proc/get_ghosts(var/notify = 0,var/what = 2) - // what = 1, return ghosts ass list. - // what = 2, return mob list - - var/list/mobs = list() - var/list/ghosts = list() - var/list/sortmob = sortAtom(mob_list) // get the mob list. - var/any=0 - for(var/mob/observer/dead/M in sortmob) - mobs.Add(M) //filter it where it's only ghosts - any = 1 //if no ghosts show up, any will just be 0 - if(!any) - if(notify) - to_chat(src, "There doesn't appear to be any ghosts for you to select.") - return - - for(var/mob/M in mobs) - var/name = M.name - ghosts[name] = M //get the name of the mob for the popup list - if(what==1) - return ghosts - else - return mobs - - -/client/proc/allow_character_respawn() - set category = "Special Verbs" - set name = "Allow player to respawn" - set desc = "Let a player bypass the wait to respawn or allow them to re-enter their corpse." - - if(!holder) - return - - var/target = tgui_input_list(usr, "Select a ckey to allow to rejoin", "Allow Respawn Selector", GLOB.respawn_timers) - if(!target) - return - - if(GLOB.respawn_timers[target] == -1) // Their respawn timer is set to -1, which is 'not allowed to respawn' - var/response = tgui_alert(src, "Are you sure you wish to allow this individual to respawn? They would normally not be able to.","Allow impossible respawn?",list("No","Yes")) - if(response == "No") - return - - GLOB.respawn_timers -= target - - var/found_client = FALSE - for(var/client/C as anything in GLOB.clients) - if(C.ckey == target) - found_client = C - to_chat(C, "You may now respawn. You should roleplay as if you learned nothing about the round during your time with the dead.") - if(isobserver(C.mob)) - var/mob/observer/dead/G = C.mob - G.can_reenter_corpse = 1 - to_chat(C, "You can also re-enter your corpse, if you still have one!") - break - - if(!found_client) - to_chat(src, "The associated client didn't appear to be connected, so they couldn't be notified, but they can now respawn if they reconnect.") - - log_admin("[key_name(usr)] allowed [found_client ? key_name(found_client) : target] to bypass the respawn time limit") - message_admins("Admin [key_name_admin(usr)] allowed [found_client ? key_name_admin(found_client) : target] to bypass the respawn time limit", 1) - - -/client/proc/toggle_antagHUD_use() - set category = "Server" - set name = "Toggle antagHUD usage" - set desc = "Toggles antagHUD usage for observers" - - if(!holder) - return - - var/action="" - if(config.antag_hud_allowed) - for(var/mob/observer/dead/g in get_ghosts()) - if(!g.client.holder) //Remove the verb from non-admin ghosts - g.verbs -= /mob/observer/dead/verb/toggle_antagHUD - if(g.antagHUD) - g.antagHUD = 0 // Disable it on those that have it enabled - g.has_enabled_antagHUD = 2 // We'll allow them to respawn - to_chat(g, span_red("The Administrator has disabled AntagHUD ")) - config.antag_hud_allowed = 0 - to_chat(src, span_red("AntagHUD usage has been disabled")) - action = "disabled" - else - for(var/mob/observer/dead/g in get_ghosts()) - if(!g.client.holder) // Add the verb back for all non-admin ghosts - g.verbs += /mob/observer/dead/verb/toggle_antagHUD - to_chat(g, span_blue("The Administrator has enabled AntagHUD ")) // Notify all observers they can now use AntagHUD - config.antag_hud_allowed = 1 - action = "enabled" - to_chat(src, span_blue("AntagHUD usage has been enabled")) - - - log_admin("[key_name(usr)] has [action] antagHUD usage for observers") - message_admins("Admin [key_name_admin(usr)] has [action] antagHUD usage for observers", 1) - - - -/client/proc/toggle_antagHUD_restrictions() - set category = "Server" - set name = "Toggle antagHUD Restrictions" - set desc = "Restricts players that have used antagHUD from being able to join this round." - - if(!holder) - return - - var/action="" - if(config.antag_hud_restricted) - for(var/mob/observer/dead/g in get_ghosts()) - to_chat(g, span_blue("The administrator has lifted restrictions on joining the round if you use AntagHUD")) - action = "lifted restrictions" - config.antag_hud_restricted = 0 - to_chat(src, span_blue("AntagHUD restrictions have been lifted")) - else - for(var/mob/observer/dead/g in get_ghosts()) - to_chat(g, span_red("The administrator has placed restrictions on joining the round if you use AntagHUD")) - to_chat(g, span_red("Your AntagHUD has been disabled, you may choose to re-enabled it but will be under restrictions ")) - g.antagHUD = 0 - g.has_enabled_antagHUD = 0 - action = "placed restrictions" - config.antag_hud_restricted = 1 - to_chat(src, span_red("AntagHUD restrictions have been enabled")) - - log_admin("[key_name(usr)] has [action] on joining the round if they use AntagHUD") - message_admins("Admin [key_name_admin(usr)] has [action] on joining the round if they use AntagHUD", 1) - -/* -If a guy was gibbed and you want to revive him, this is a good way to do so. -Works kind of like entering the game with a new character. Character receives a new mind if they didn't have one. -Traitors and the like can also be revived with the previous role mostly intact. -/N */ -/client/proc/respawn_character() - set category = "Special Verbs" - set name = "Spawn Character" - set desc = "(Re)Spawn a client's loaded character." - - if(!holder) - return - - var/client/picked_client = tgui_input_list(src, "Please specify which client's character to spawn.", "Client", GLOB.clients) - if(!picked_client) - return - - respawn_character_proper(picked_client) - -/client/proc/respawn_character_proper(client/picked_client) - if(!istype(picked_client)) - return - - //I frontload all the questions so we don't have a half-done process while you're reading. - var/location = tgui_alert(src, "Please specify where to spawn them.", "Location", list("Right Here", "Arrivals", "Cancel")) - if(location == "Cancel" || !location) - return - - var/announce = tgui_alert(src,"Announce as if they had just arrived?", "Announce", list("No", "Yes", "Cancel")) - if(announce == "Cancel") - return - else if(announce == "Yes") //Too bad buttons can't just have 1/0 values and different display strings - announce = 1 - else - announce = 0 - - var/inhabit = tgui_alert(src,"Put the person into the spawned mob?", "Inhabit", list("Yes", "No", "Cancel")) - if(inhabit == "Cancel") - return - else if(inhabit == "Yes") - inhabit = 1 - else - inhabit = 0 - - //Name matching is ugly but mind doesn't persist to look at. - var/charjob - var/records - var/datum/data/record/record_found - record_found = find_general_record("name",picked_client.prefs.real_name) - - //Found their record, they were spawned previously - if(record_found) - var/samejob = tgui_alert(src,"Found [picked_client.prefs.real_name] in data core. They were [record_found.fields["real_rank"]] this round. Assign same job? They will not be re-added to the manifest/records, either way.","Previously spawned",list("Yes","Assistant","No")) - if(samejob == "Yes") - charjob = record_found.fields["real_rank"] - else if(samejob == USELESS_JOB) //VOREStation Edit - Visitor not Assistant - charjob = USELESS_JOB //VOREStation Edit - Visitor not Assistant - else - records = tgui_alert(src,"No data core entry detected. Would you like add them to the manifest, and sec/med/HR records?","Records",list("No", "Yes", "Cancel")) - if(records == "Cancel") - return - if(records == "Yes") - records = 1 - else - records = 0 - - //Well you're not reloading their job or they never had one. - if(!charjob) - var/pickjob = tgui_input_list(src,"Pick a job to assign them (or none).","Job Select", joblist + "-No Job-", "-No Job-") - if(!pickjob) - return - if(pickjob != "-No Job-") - charjob = pickjob - - //If you've picked a job by now, you can equip them. - var/equipment - if(charjob) - equipment = tgui_alert(src,"Spawn them with equipment?", "Equipment", list("Yes", "No", "Cancel")) - if(equipment == "Cancel") - return - else if(equipment == "Yes") - equipment = 1 - else - equipment = 0 - - //For logging later - var/admin = key_name_admin(src) - var/player_key = picked_client.key - //VOREStation Add - Needed for persistence - var/picked_ckey = picked_client.ckey - var/picked_slot = picked_client.prefs.default_slot - //VOREStation Add End - - var/mob/living/carbon/human/new_character - var/spawnloc - var/showy - - //Where did you want to spawn them? - switch(location) - if("Right Here") //Spawn them on your turf - spawnloc = get_turf(src.mob) - showy = tgui_input_list(src,"Showy entrance?", "Showy", list("No", "Telesparks", "Drop Pod", "Fall", "Cancel")) - if(showy == "Cancel") - return - if(showy == "Drop Pod") - showy = tgui_alert(src,"Destructive drop pods cause damage in a 3x3 and may break turfs. Polite drop pods lightly damage the turfs but won't break through.", "Drop Pod", list("Polite", "Destructive", "Cancel")) // reusing var - if(showy == "Cancel") - return - - if("Arrivals") //Spawn them at a latejoin spawnpoint - spawnloc = pick(latejoin) - - else //I have no idea how you're here - to_chat(src, "Invalid spawn location choice.") - return - - //Did we actually get a loc to spawn them? - if(!spawnloc) - to_chat(src, "Couldn't get valid spawn location.") - return - - new_character = new(spawnloc) - - if(showy == "Telesparks") - anim(spawnloc,new_character,'icons/mob/mob.dmi',,"phasein",,new_character.dir) - playsound(spawnloc, "sparks", 50, 1) - var/datum/effect/effect/system/spark_spread/spk = new(new_character) - spk.set_up(5, 0, new_character) - spk.attach(new_character) - spk.start() - - //We were able to spawn them, right? - if(!new_character) - to_chat(src, "Something went wrong and spawning failed.") - return - - //Write the appearance and whatnot out to the character - picked_client.prefs.copy_to(new_character) - if(new_character.dna) - new_character.dna.ResetUIFrom(new_character) - new_character.sync_organ_dna() - if(inhabit) - new_character.key = player_key - //Were they any particular special role? If so, copy. - if(new_character.mind) - var/datum/antagonist/antag_data = get_antag_data(new_character.mind.special_role) - if(antag_data) - antag_data.add_antagonist(new_character.mind) - antag_data.place_mob(new_character) - - //VOREStation Add - Required for persistence - if(new_character.mind) - new_character.mind.loaded_from_ckey = picked_ckey - new_character.mind.loaded_from_slot = picked_slot - //VOREStation Add End - - for(var/lang in picked_client.prefs.alternate_languages) - var/datum/language/chosen_language = GLOB.all_languages[lang] - if(chosen_language) - if(is_lang_whitelisted(src,chosen_language) || (new_character.species && (chosen_language.name in new_character.species.secondary_langs))) - new_character.add_language(lang) - - //If desired, apply equipment. - if(equipment) - if(charjob) - job_master.EquipRank(new_character, charjob, 1) - if(new_character.mind) - new_character.mind.assigned_role = charjob - new_character.mind.role_alt_title = job_master.GetPlayerAltTitle(new_character, charjob) - //equip_custom_items(new_character) //VOREStation Removal - - //If desired, add records. - if(records) - data_core.manifest_inject(new_character) - - //A redraw for good measure - new_character.regenerate_icons() - - new_character.update_transform() //VOREStation Edit - - //If we're announcing their arrival - if(announce) - AnnounceArrival(new_character, new_character.mind.assigned_role, "Common", new_character.z) - - log_admin("[admin] has spawned [player_key]'s character [new_character.real_name].") - message_admins("[admin] has spawned [player_key]'s character [new_character.real_name].", 1) - - - - feedback_add_details("admin_verb","RSPCH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - // Drop pods and fall - if(showy == "Polite") - var/turf/T = get_turf(new_character) - new /obj/structure/drop_pod/polite(T, new_character) - to_chat(new_character, "Please wait for your arrival.") - else if(showy == "Destructive") - var/turf/T = get_turf(new_character) - new /obj/structure/drop_pod(T, new_character) - to_chat(new_character, "Please wait for your arrival.") - else if(showy == "Fall") - spawn(1) - var/initial_x = new_character.pixel_x - var/initial_y = new_character.pixel_y - new_character.plane = 1 - new_character.pixel_x = rand(-150, 150) - new_character.pixel_y = 500 // When you think that pixel_z is height but you are wrong - new_character.density = FALSE - new_character.opacity = FALSE - animate(new_character, pixel_y = initial_y, pixel_x = initial_x , time = 7) - spawn(7) - new_character.end_fall() - to_chat(new_character, "You have been fully spawned. Enjoy the game.") - - return new_character - -/client/proc/cmd_admin_add_freeform_ai_law() - set category = "Fun" - set name = "Add Custom AI law" - - if(!holder) - return - - var/input = sanitize(tgui_input_text(usr, "Please enter anything you want the AI to do. Anything. Serious.", "What?", "")) - if(!input) - return - for(var/mob/living/silicon/ai/M in mob_list) - if (M.stat == 2) - to_chat(usr, "Upload failed. No signal is being detected from the AI.") - else if (M.see_in_dark == 0) - to_chat(usr, "Upload failed. Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power.") - else - M.add_ion_law(input) - for(var/mob/living/silicon/ai/O in mob_list) - to_chat(O,input + span_red("... LAWS UPDATED!")) - O.show_laws() - - log_admin("Admin [key_name(usr)] has added a new AI law - [input]") - message_admins("Admin [key_name_admin(usr)] has added a new AI law - [input]", 1) - - var/show_log = tgui_alert(src, "Show ion message?", "Message", list("Yes", "No")) - if(show_log == "Yes") - command_announcement.Announce("Ion storm detected near the [station_name()]. Please check all AI-controlled equipment for errors.", "Anomaly Alert", new_sound = 'sound/AI/ionstorm.ogg') - feedback_add_details("admin_verb","IONC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_rejuvenate(mob/living/M as mob in mob_list) - set category = "Special Verbs" - set name = "Rejuvenate" - - if(!holder) - return - - if(!mob) - return - if(!istype(M)) - tgui_alert_async(usr, "Cannot revive a ghost") - return - if(config.allow_admin_rev) - M.revive() - - log_admin("[key_name(usr)] healed / revived [key_name(M)]") - var/msg = "Admin [key_name_admin(usr)] healed / revived [ADMIN_LOOKUPFLW(M)]!" - message_admins(msg) - admin_ticket_log(M, msg) - else - tgui_alert_async(usr, "Admin revive disabled") - feedback_add_details("admin_verb","REJU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_create_centcom_report() - set category = "Special Verbs" - set name = "Create Command Report" - - if(!holder) - return - - var/input = sanitize(tgui_input_text(usr, "Please enter anything you want. Anything. Serious.", "What?", "", multiline = TRUE, prevent_enter = TRUE), extra = 0) - var/customname = sanitizeSafe(tgui_input_text(usr, "Pick a title for the report.", "Title")) - if(!input) - return - if(!customname) - customname = "[using_map.company_name] Update" - - //New message handling - post_comm_message(customname, replacetext(input, "\n", "
                    ")) - - switch(tgui_alert(usr, "Should this be announced to the general population?","Show world?",list("Yes","No"))) - if("Yes") - command_announcement.Announce(input, customname, new_sound = 'sound/AI/commandreport.ogg', msg_sanitized = 1); - if("No") - to_world(span_red("New [using_map.company_name] Update available at all communication consoles.")) - world << sound('sound/AI/commandreport.ogg') - - log_admin("[key_name(src)] has created a command report: [input]") - message_admins("[key_name_admin(src)] has created a command report", 1) - feedback_add_details("admin_verb","CCR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_delete(atom/O as obj|mob|turf in _validate_atom(O)) // I don't understand precisely how this fixes the string matching against a substring, but it does - Ater - set category = "Admin" - set name = "Delete" - - if (!holder) - return - - admin_delete(O) - -/client/proc/cmd_admin_list_open_jobs() - set category = "Admin" - set name = "List free slots" - - if (!holder) - return - - if(job_master) - for(var/datum/job/job in job_master.occupations) - to_chat(src, "[job.title]: [job.total_positions]") - feedback_add_details("admin_verb","LFS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_explosion(atom/O as obj|mob|turf in world) - set category = "Special Verbs" - set name = "Explosion" - - if(!check_rights(R_DEBUG|R_FUN)) return //VOREStation Edit - - var/devastation = tgui_input_number(usr, "Range of total devastation. -1 to none", text("Input")) - if(devastation == null) return - var/heavy = tgui_input_number(usr, "Range of heavy impact. -1 to none", text("Input")) - if(heavy == null) return - var/light = tgui_input_number(usr, "Range of light impact. -1 to none", text("Input")) - if(light == null) return - var/flash = tgui_input_number(usr, "Range of flash. -1 to none", text("Input")) - if(flash == null) return - - if ((devastation != -1) || (heavy != -1) || (light != -1) || (flash != -1)) - if ((devastation > 20) || (heavy > 20) || (light > 20)) - if (tgui_alert(src, "Are you sure you want to do this? It will laaag.", "Confirmation", list("Yes", "No")) == "No") - return - - explosion(O, devastation, heavy, light, flash) - log_admin("[key_name(usr)] created an explosion ([devastation],[heavy],[light],[flash]) at ([O.x],[O.y],[O.z])") - message_admins("[key_name_admin(usr)] created an explosion ([devastation],[heavy],[light],[flash]) at ([O.x],[O.y],[O.z])", 1) - feedback_add_details("admin_verb","EXPL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - else - return - -/client/proc/cmd_admin_emp(atom/O as obj|mob|turf in world) - set category = "Special Verbs" - set name = "EM Pulse" - - if(!check_rights(R_DEBUG|R_FUN)) return //VOREStation Edit - - var/heavy = tgui_input_number(usr, "Range of heavy pulse.", text("Input")) - if(heavy == null) return - var/med = tgui_input_number(usr, "Range of medium pulse.", text("Input")) - if(med == null) return - var/light = tgui_input_number(usr, "Range of light pulse.", text("Input")) - if(light == null) return - var/long = tgui_input_number(usr, "Range of long pulse.", text("Input")) - if(long == null) return - - if (heavy || med || light || long) - - empulse(O, heavy, med, light, long) - log_admin("[key_name(usr)] created an EM Pulse ([heavy],[med],[light],[long]) at ([O.x],[O.y],[O.z])") - message_admins("[key_name_admin(usr)] created an EM PUlse ([heavy],[med],[light],[long]) at ([O.x],[O.y],[O.z])", 1) - feedback_add_details("admin_verb","EMP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - return - else - return - -/client/proc/cmd_admin_gib(mob/M as mob in mob_list) - set category = "Special Verbs" - set name = "Gib" - - if(!check_rights(R_ADMIN|R_FUN)) return //VOREStation Edit - - var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) - if(confirm != "Yes") return - //Due to the delay here its easy for something to have happened to the mob - if(!M) return - - log_admin("[key_name(usr)] has gibbed [key_name(M)]") - message_admins("[key_name_admin(usr)] has gibbed [key_name_admin(M)]", 1) - - if(istype(M, /mob/observer/dead)) - gibs(M.loc) - return - - M.gib() - feedback_add_details("admin_verb","GIB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_gib_self() - set name = "Gibself" - set category = "Fun" - - if(!holder) - return - - var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) - if(confirm == "Yes") - if (istype(mob, /mob/observer/dead)) // so they don't spam gibs everywhere - return - else - mob.gib() - - log_admin("[key_name(usr)] used gibself.") - message_admins(span_blue("[key_name_admin(usr)] used gibself."), 1) - feedback_add_details("admin_verb","GIBS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/* -/client/proc/cmd_manual_ban() - set name = "Manual Ban" - set category = "Special Verbs" - if(!authenticated || !holder) - to_chat(src, "Only administrators may use this command.") - return - var/mob/M = null - switch(tgui_alert(usr, "How would you like to ban someone today?", "Manual Ban", "Key List", "Enter Manually", "Cancel")) - if("Key List") - var/list/keys = list() - for(var/mob/M in player_list) - keys += M.client - var/selection = tgui_input_list(usr, "Please, select a player!", "Admin Jumping", keys) - if(!selection) - return - M = selection:mob - if ((M.client && M.client.holder && (M.client.holder.level >= holder.level))) - tgui_alert_async(usr, "You cannot perform this action. You must be of a higher administrative rank!") - return - - switch(tgui_alert(usr, "Temporary Ban?","Temporary Ban",list("Yes","No"))) - if("Yes") - var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num - if(!mins) - return - if(mins >= 525600) mins = 525599 - var/reason = input(usr,"Reason?","reason","Griefer") as text - if(!reason) - return - if(M) - AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) - to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") - to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") - to_chat(M, "To try to resolve this matter head to http://ss13.donglabs.com/forum/") - log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") - message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") - world.Export("http://216.38.134.132/adminlog.php?type=ban&key=[usr.client.key]&key2=[M.key]&msg=[html_decode(reason)]&time=[mins]&server=[replacetext(config.server_name, "#", "")]") - del(M.client) - qdel(M) - else - - if("No") - var/reason = input(usr,"Reason?","reason","Griefer") as text - if(!reason) - return - AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0) - to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") - to_chat(M, "This is a permanent ban.") - to_chat(M, "To try to resolve this matter head to http://ss13.donglabs.com/forum/") - log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") - message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") - world.Export("http://216.38.134.132/adminlog.php?type=ban&key=[usr.client.key]&key2=[M.key]&msg=[html_decode(reason)]&time=perma&server=[replacetext(config.server_name, "#", "")]") - del(M.client) - qdel(M) -*/ - -/client/proc/update_world() - // If I see anyone granting powers to specific keys like the code that was here, - // I will both remove their SVN access and permanently ban them from my servers. - return - -/client/proc/cmd_admin_check_contents(mob/living/M as mob in mob_list) - set category = "Special Verbs" - set name = "Check Contents" - set popup_menu = FALSE //VOREStation Edit - Declutter. - - if(!holder) - return - - var/list/L = M.get_contents() - for(var/t in L) - to_chat(usr, "[t]") - feedback_add_details("admin_verb","CC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/* This proc is DEFERRED. Does not do anything. -/client/proc/cmd_admin_remove_phoron() - set category = "Debug" - set name = "Stabilize Atmos." - if(!holder) - to_chat(src, "Only administrators may use this command.") - return - feedback_add_details("admin_verb","STATM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -// DEFERRED - spawn(0) - for(var/turf/T in view()) - T.poison = 0 - T.oldpoison = 0 - T.tmppoison = 0 - T.oxygen = 755985 - T.oldoxy = 755985 - T.tmpoxy = 755985 - T.co2 = 14.8176 - T.oldco2 = 14.8176 - T.tmpco2 = 14.8176 - T.n2 = 2.844e+006 - T.on2 = 2.844e+006 - T.tn2 = 2.844e+006 - T.tsl_gas = 0 - T.osl_gas = 0 - T.sl_gas = 0 - T.temp = 293.15 - T.otemp = 293.15 - T.ttemp = 293.15 -*/ - -/client/proc/toggle_view_range() - set category = "Special Verbs" - set name = "Change View Range" - set desc = "switches between 1x and custom views" - - if(!holder) - return - - var/view = src.view - if(view == world.view) - view = tgui_input_list(usr, "Select view range:", "FUCK YE", list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128)) - else - view = world.view - mob.set_viewsize(view) - - log_admin("[key_name(usr)] changed their view range to [view].") - //message_admins("[key_name_admin(usr)] changed their view range to [view].", 1) //why? removed by order of XSI - - feedback_add_details("admin_verb","CVRA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/admin_call_shuttle() - set category = "Admin" - set name = "Call Shuttle" - - if ((!( ticker ) || !emergency_shuttle.location())) - return - - if(!check_rights(R_ADMIN)) return //VOREStation Edit - - var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) - if(confirm != "Yes") return - - var/choice - if(ticker.mode.auto_recall_shuttle) - choice = tgui_input_list(usr, "The shuttle will just return if you call it. Call anyway?", "Shuttle Call", list("Confirm", "Cancel")) - if(choice == "Confirm") - emergency_shuttle.auto_recall = 1 //enable auto-recall - else - return - - choice = tgui_input_list(usr, "Is this an emergency evacuation or a crew transfer?", "Shuttle Call", list("Emergency", "Crew Transfer")) - if (choice == "Emergency") - emergency_shuttle.call_evac() - else - emergency_shuttle.call_transfer() - - - feedback_add_details("admin_verb","CSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] admin-called the emergency shuttle.") - message_admins(span_blue("[key_name_admin(usr)] admin-called the emergency shuttle."), 1) - return - -/client/proc/admin_cancel_shuttle() - set category = "Admin" - set name = "Cancel Shuttle" - - if(!check_rights(R_ADMIN)) return //VOREStation Edit - - if(tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) != "Yes") return - - if(!ticker || !emergency_shuttle.can_recall()) - return - - emergency_shuttle.recall() - feedback_add_details("admin_verb","CCSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] admin-recalled the emergency shuttle.") - message_admins(span_blue("[key_name_admin(usr)] admin-recalled the emergency shuttle."), 1) - - return - -/client/proc/admin_deny_shuttle() - set category = "Admin" - set name = "Toggle Deny Shuttle" - - if (!ticker) - return - - if(!check_rights(R_ADMIN)) return //VOREStation Edit - - emergency_shuttle.deny_shuttle = !emergency_shuttle.deny_shuttle - - log_admin("[key_name(src)] has [emergency_shuttle.deny_shuttle ? "denied" : "allowed"] the shuttle to be called.") - message_admins("[key_name_admin(usr)] has [emergency_shuttle.deny_shuttle ? "denied" : "allowed"] the shuttle to be called.") - -/client/proc/cmd_admin_attack_log(mob/M as mob in mob_list) - set category = "Special Verbs" - set name = "Attack Log" - - to_chat(usr, span_red("Attack Log for [mob]")) - for(var/t in M.attack_log) - to_chat(usr,t) - feedback_add_details("admin_verb","ATTL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/everyone_random() - set category = "Fun" - set name = "Make Everyone Random" - set desc = "Make everyone have a random appearance. You can only use this before rounds!" - - if(!check_rights(R_FUN)) return - - if (ticker && ticker.mode) - to_chat(usr, "Nope you can't do this, the game's already started. This only works before rounds!") - return - - if(ticker.random_players) - ticker.random_players = 0 - message_admins("Admin [key_name_admin(usr)] has disabled \"Everyone is Special\" mode.", 1) - to_chat(usr, "Disabled.") - return - - - var/notifyplayers = tgui_alert(src, "Do you want to notify the players?", "Options", list("Yes", "No", "Cancel")) - if(notifyplayers == "Cancel") - return - - log_admin("Admin [key_name(src)] has forced the players to have random appearances.") - message_admins("Admin [key_name_admin(usr)] has forced the players to have random appearances.", 1) - - if(notifyplayers == "Yes") - to_world(span_blue("Admin [usr.key] has forced the players to have completely random identities!")) - - to_chat(usr, "Remember: you can always disable the randomness by using the verb again, assuming the round hasn't started yet.") - - ticker.random_players = 1 - feedback_add_details("admin_verb","MER") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/toggle_random_events() - set category = "Server" - set name = "Toggle random events on/off" - set desc = "Toggles random events such as meteors, black holes, blob (but not space dust) on/off" - - if(!check_rights(R_SERVER)) return //VOREStation Edit - - if(!config.allow_random_events) - config.allow_random_events = 1 - to_chat(usr, "Random events enabled") - message_admins("Admin [key_name_admin(usr)] has enabled random events.", 1) - else - config.allow_random_events = 0 - to_chat(usr, "Random events disabled") - message_admins("Admin [key_name_admin(usr)] has disabled random events.", 1) - feedback_add_details("admin_verb","TRE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/despawn_player(var/mob/M in living_mob_list) - set name = "Cryo Player" - set category = "Admin" - set desc = "Removes a player from the round as if they'd cryo'd." - set popup_menu = FALSE - - if(!check_rights(R_ADMIN|R_EVENT)) - return - - if(!M) - return - - var/confirm = tgui_alert(usr, "Are you sure you want to cryo [M]?","Confirmation",list("No","Yes")) - if(confirm == "No") - return - - var/list/human_cryopods = list() - var/list/robot_cryopods = list() - - for(var/obj/machinery/cryopod/CP in machines) - if(!CP.control_computer) - continue //Broken pod w/o computer, move on. - - var/listname = "[CP.name] ([CP.x],[CP.y],[CP.z])" - if(istype(CP,/obj/machinery/cryopod/robot)) - robot_cryopods[listname] = CP - else - human_cryopods[listname] = CP - - //Gotta log this up here before they get ghostized and lose their key or anything. - log_and_message_admins("[key_name(src)] admin cryo'd [key_name(M)].") - feedback_add_details("admin_verb","ACRYO") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - if(ishuman(M)) - var/choice = tgui_input_list(usr,"Select a cryopod to use","Cryopod Choice", human_cryopods) - var/obj/machinery/cryopod/CP = human_cryopods[choice] - if(!CP) - return - M.ghostize() - CP.despawn_occupant(M) - return - - else if(issilicon(M)) - if(isAI(M)) - var/mob/living/silicon/ai/ai = M - empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(ai.loc) - global_announcer.autosay("[ai] has been moved to intelligence storage.", "Artificial Intelligence Oversight") - ai.clear_client() - return - else - var/choice = tgui_input_list(usr,"Select a cryopod to use","Cryopod Choice", robot_cryopods) - var/obj/machinery/cryopod/robot/CP = robot_cryopods[choice] - if(!CP) - return - M.ghostize() - CP.despawn_occupant(M) - return - - else if(isliving(M)) - M.ghostize() - qdel(M) //Bye - -/client/proc/cmd_admin_droppod_spawn(var/object as text) - set name = "Drop Pod Atom" - set desc = "Spawn a new atom/movable in a drop pod where you are." - set category = "Fun" - - if(!check_rights(R_SPAWN)) - return - - var/list/types = typesof(/atom/movable) - var/list/matches = new() - - for(var/path in types) - if(findtext("[path]", object)) - matches += path - - if(!matches.len) - return - - var/chosen - if(matches.len==1) - chosen = matches[1] - else - chosen = tgui_input_list(usr, "Select a movable type:", "Spawn in Drop Pod", matches) - if(!chosen) - return - - var/podtype = tgui_alert(src,"Destructive drop pods cause damage in a 3x3 and may break turfs. Polite drop pods lightly damage the turfs but won't break through.", "Drop Pod", list("Polite", "Destructive", "Cancel")) - if(podtype == "Cancel") - return - var/autoopen = tgui_alert(src,"Should the pod open automatically?", "Drop Pod", list("Yes", "No", "Cancel")) - if(autoopen == "Cancel") - return - switch(podtype) - if("Destructive") - var/atom/movable/AM = new chosen(usr.loc) - new /obj/structure/drop_pod(get_turf(usr), AM, autoopen == "Yes" ? TRUE : FALSE) - if("Polite") - var/atom/movable/AM = new chosen(usr.loc) - new /obj/structure/drop_pod/polite(get_turf(usr), AM, autoopen == "Yes" ? TRUE : FALSE) - - feedback_add_details("admin_verb","DPA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_droppod_deploy() - set name = "Drop Pod Deploy" - set desc = "Drop an existing mob where you are in a drop pod." - set category = "Fun" - - if(!check_rights(R_SPAWN)) - return - - var/mob/living/L = tgui_input_list(usr, "Select the mob to drop:", "Mob Picker", living_mob_list) - if(!L) - return - - var/podtype = tgui_alert(src,"Destructive drop pods cause damage in a 3x3 and may break turfs. Polite drop pods lightly damage the turfs but won't break through.", "Drop Pod", list("Polite", "Destructive", "Cancel")) - if(podtype == "Cancel") - return - var/autoopen = tgui_alert(src,"Should the pod open automatically?", "Drop Pod", list("Yes", "No", "Cancel")) - if(autoopen == "Cancel") - return - if(!L || QDELETED(L)) - return - switch(podtype) - if("Destructive") - new /obj/structure/drop_pod(get_turf(usr), L, autoopen == "Yes" ? TRUE : FALSE) - if("Polite") - new /obj/structure/drop_pod/polite(get_turf(usr), L, autoopen == "Yes" ? TRUE : FALSE) - - feedback_add_details("admin_verb","DPD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/client/proc/cmd_admin_drop_everything(mob/M as mob in mob_list) + set category = null + set name = "Drop Everything" + if(!holder) + return + + var/confirm = tgui_alert(src, "Make [M] drop everything?", "Message", list("Yes", "No")) + if(confirm != "Yes") + return + + for(var/obj/item/W in M) + if(istype(W, /obj/item/weapon/implant/backup) || istype(W, /obj/item/device/nif)) //VOREStation Edit - There's basically no reason to remove either of these + continue //VOREStation Edit + M.drop_from_inventory(W) + + log_admin("[key_name(usr)] made [key_name(M)] drop everything!") + message_admins("[key_name_admin(usr)] made [key_name_admin(M)] drop everything!", 1) + feedback_add_details("admin_verb","DEVR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_prison(mob/M as mob in mob_list) + set category = "Admin" + set name = "Prison" + if(!holder) + return + + if (ismob(M)) + if(istype(M, /mob/living/silicon/ai)) + tgui_alert_async(usr, "The AI can't be sent to prison you jerk!") + return + //strip their stuff before they teleport into a cell :downs: + for(var/obj/item/W in M) + M.drop_from_inventory(W) + //teleport person to cell + M.Paralyse(5) + sleep(5) //so they black out before warping + M.loc = pick(prisonwarp) + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/prisoner = M + prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/prison(prisoner), slot_w_uniform) + prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) + spawn(50) + to_chat(M, span_red("You have been sent to the prison station!")) + log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") + message_admins(span_blue("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station."), 1) + feedback_add_details("admin_verb","PRISON") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +//Allows staff to determine who the newer players are. +/client/proc/cmd_check_new_players() + set category = "Admin" + set name = "Check new Players" + if(!holder) + return + + var/age = tgui_alert(src, "Age check", "Show accounts yonger then _____ days", list("7","30","All")) + + if(age == "All") + age = 9999999 + else + age = text2num(age) + + var/missing_ages = 0 + var/msg = "" + + var/highlight_special_characters = 1 + + for(var/client/C in GLOB.clients) + if(C.player_age == "Requires database") + missing_ages = 1 + continue + if(C.player_age < age) + msg += "[key_name(C, 1, 1, highlight_special_characters)]: account is [C.player_age] days old
                    " + + if(missing_ages) + to_chat(src, "Some accounts did not have proper ages set in their clients. This function requires database to be present.") + + if(msg != "") + src << browse(msg, "window=Player_age_check") + else + to_chat(src, "No matches for that age range found.") + +/client/proc/cmd_admin_subtle_message(mob/M as mob in mob_list) + set category = "Special Verbs" + set name = "Subtle Message" + + if(!ismob(M)) return + if (!holder) + return + + var/msg = tgui_input_text(usr, "Message:", text("Subtle PM to [M.key]")) + + if (!msg) + return + + if(!(msg[1] == "<" && msg[length(msg)] == ">")) //You can use HTML but only if the whole thing is HTML. Tries to prevent admin 'accidents'. + msg = sanitize(msg) + + if(usr) + if (usr.client) + if(usr.client.holder) + to_chat(M, "You hear a voice in your head... [msg]") + + log_admin("SubtlePM: [key_name(usr)] -> [key_name(M)] : [msg]") + msg = " SubtleMessage: [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]" + message_admins(msg) + admin_ticket_log(M, msg) + feedback_add_details("admin_verb","SMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_world_narrate() // Allows administrators to fluff events a little easier -- TLE + set category = "Special Verbs" + set name = "Global Narrate" + + if (!holder) + return + + var/msg = tgui_input_text(usr, "Message:", text("Enter the text you wish to appear to everyone:")) + + if (!msg) + return + if(!(msg[1] == "<" && msg[length(msg)] == ">")) //You can use HTML but only if the whole thing is HTML. Tries to prevent admin 'accidents'. + msg = sanitize(msg) + if (!msg) // We check both before and after, just in case sanitization ended us up with empty message. + return + + to_world("[msg]") + log_admin("GlobalNarrate: [key_name(usr)] : [msg]") + message_admins(span_blue(" GlobalNarrate: [key_name_admin(usr)] : [msg]
                    "), 1) + feedback_add_details("admin_verb","GLN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_direct_narrate(var/mob/M) // Targetted narrate -- TLE + set category = "Special Verbs" + set name = "Direct Narrate" + + if(!holder) + return + + if(!M) + M = tgui_input_list(usr, "Direct narrate to who?", "Active Players", get_mob_with_client_list()) + + if(!M) + return + + var/msg = tgui_input_text(usr, "Message:", text("Enter the text you wish to appear to your target:")) + if(msg && !(msg[1] == "<" && msg[length(msg)] == ">")) //You can use HTML but only if the whole thing is HTML. Tries to prevent admin 'accidents'. + msg = sanitize(msg) + + if( !msg ) + return + + to_chat(M, msg) + log_admin("DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]") + msg = " DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]
                    " + message_admins(msg) + admin_ticket_log(M, msg) + feedback_add_details("admin_verb","DIRN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_godmode(mob/M as mob in mob_list) + set category = "Special Verbs" + set name = "Godmode" + + if(!holder) + return + + M.status_flags ^= GODMODE + to_chat(usr, span_blue("Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]")) + + log_admin("[key_name(usr)] has toggled [key_name(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]") + var/msg = "[key_name_admin(usr)] has toggled [ADMIN_LOOKUPFLW(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]" + message_admins(msg) + admin_ticket_log(M, msg) + feedback_add_details("admin_verb","GOD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0) + if(automute) + if(!config.automute_on) + return + else + if(!usr || !usr.client) + return + if(!usr.client.holder) + to_chat(usr, span_red("Error: cmd_admin_mute: You don't have permission to do this.")) + return + if(!M.client) + to_chat(usr, span_red("Error: cmd_admin_mute: This mob doesn't have a client tied to it.")) + if(M.client.holder) + to_chat(usr, span_red("Error: cmd_admin_mute: You cannot mute an admin/mod.")) + if(!M.client) + return + if(M.client.holder) + return + + var/muteunmute + var/mute_string + + switch(mute_type) + if(MUTE_IC) mute_string = "IC (say and emote)" + if(MUTE_OOC) mute_string = "OOC" + if(MUTE_PRAY) mute_string = "pray" + if(MUTE_ADMINHELP) mute_string = "adminhelp, admin PM and ASAY" + if(MUTE_DEADCHAT) mute_string = "deadchat and DSAY" + if(MUTE_ALL) mute_string = "everything" + else return + + if(automute) + muteunmute = "auto-muted" + M.client.prefs.muted |= mute_type + log_admin("SPAM AUTOMUTE: [muteunmute] [key_name(M)] from [mute_string]") + message_admins("SPAM AUTOMUTE: [muteunmute] [key_name_admin(M)] from [mute_string].", 1) + to_chat(M, "You have been [muteunmute] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin.") + feedback_add_details("admin_verb","AUTOMUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + + if(M.client.prefs.muted & mute_type) + muteunmute = "unmuted" + M.client.prefs.muted &= ~mute_type + else + muteunmute = "muted" + M.client.prefs.muted |= mute_type + + log_admin("[key_name(usr)] has [muteunmute] [key_name(M)] from [mute_string]") + message_admins("[key_name_admin(usr)] has [muteunmute] [key_name_admin(M)] from [mute_string].", 1) + to_chat(M, "You have been [muteunmute] from [mute_string].") + feedback_add_details("admin_verb","MUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_add_random_ai_law() + set category = "Fun" + set name = "Add Random AI Law" + + if(!holder) + return + + var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) + if(confirm != "Yes") return + log_admin("[key_name(src)] has added a random AI law.") + message_admins("[key_name_admin(src)] has added a random AI law.", 1) + + var/show_log = tgui_alert(src, "Show ion message?", "Message", list("Yes", "No")) + if(show_log == "Yes") + command_announcement.Announce("Ion storm detected near \the [station_name()]. Please check all AI-controlled equipment for errors.", "Anomaly Alert", new_sound = 'sound/AI/ionstorm.ogg') + + IonStorm(0) + feedback_add_details("admin_verb","ION") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/* +Allow admins to set players to be able to respawn/bypass 30 min wait, without the admin having to edit variables directly +Ccomp's first proc. +*/ + +/client/proc/get_ghosts(var/notify = 0,var/what = 2) + // what = 1, return ghosts ass list. + // what = 2, return mob list + + var/list/mobs = list() + var/list/ghosts = list() + var/list/sortmob = sortAtom(mob_list) // get the mob list. + var/any=0 + for(var/mob/observer/dead/M in sortmob) + mobs.Add(M) //filter it where it's only ghosts + any = 1 //if no ghosts show up, any will just be 0 + if(!any) + if(notify) + to_chat(src, "There doesn't appear to be any ghosts for you to select.") + return + + for(var/mob/M in mobs) + var/name = M.name + ghosts[name] = M //get the name of the mob for the popup list + if(what==1) + return ghosts + else + return mobs + + +/client/proc/allow_character_respawn() + set category = "Special Verbs" + set name = "Allow player to respawn" + set desc = "Let a player bypass the wait to respawn or allow them to re-enter their corpse." + + if(!holder) + return + + var/target = tgui_input_list(usr, "Select a ckey to allow to rejoin", "Allow Respawn Selector", GLOB.respawn_timers) + if(!target) + return + + if(GLOB.respawn_timers[target] == -1) // Their respawn timer is set to -1, which is 'not allowed to respawn' + var/response = tgui_alert(src, "Are you sure you wish to allow this individual to respawn? They would normally not be able to.","Allow impossible respawn?",list("No","Yes")) + if(response == "No") + return + + GLOB.respawn_timers -= target + + var/found_client = FALSE + for(var/client/C as anything in GLOB.clients) + if(C.ckey == target) + found_client = C + to_chat(C, "You may now respawn. You should roleplay as if you learned nothing about the round during your time with the dead.") + if(isobserver(C.mob)) + var/mob/observer/dead/G = C.mob + G.can_reenter_corpse = 1 + to_chat(C, "You can also re-enter your corpse, if you still have one!") + break + + if(!found_client) + to_chat(src, "The associated client didn't appear to be connected, so they couldn't be notified, but they can now respawn if they reconnect.") + + log_admin("[key_name(usr)] allowed [found_client ? key_name(found_client) : target] to bypass the respawn time limit") + message_admins("Admin [key_name_admin(usr)] allowed [found_client ? key_name_admin(found_client) : target] to bypass the respawn time limit", 1) + + +/client/proc/toggle_antagHUD_use() + set category = "Server" + set name = "Toggle antagHUD usage" + set desc = "Toggles antagHUD usage for observers" + + if(!holder) + return + + var/action="" + if(config.antag_hud_allowed) + for(var/mob/observer/dead/g in get_ghosts()) + if(!g.client.holder) //Remove the verb from non-admin ghosts + g.verbs -= /mob/observer/dead/verb/toggle_antagHUD + if(g.antagHUD) + g.antagHUD = 0 // Disable it on those that have it enabled + g.has_enabled_antagHUD = 2 // We'll allow them to respawn + to_chat(g, span_red("The Administrator has disabled AntagHUD ")) + config.antag_hud_allowed = 0 + to_chat(src, span_red("AntagHUD usage has been disabled")) + action = "disabled" + else + for(var/mob/observer/dead/g in get_ghosts()) + if(!g.client.holder) // Add the verb back for all non-admin ghosts + g.verbs += /mob/observer/dead/verb/toggle_antagHUD + to_chat(g, span_blue("The Administrator has enabled AntagHUD ")) // Notify all observers they can now use AntagHUD + config.antag_hud_allowed = 1 + action = "enabled" + to_chat(src, span_blue("AntagHUD usage has been enabled")) + + + log_admin("[key_name(usr)] has [action] antagHUD usage for observers") + message_admins("Admin [key_name_admin(usr)] has [action] antagHUD usage for observers", 1) + + + +/client/proc/toggle_antagHUD_restrictions() + set category = "Server" + set name = "Toggle antagHUD Restrictions" + set desc = "Restricts players that have used antagHUD from being able to join this round." + + if(!holder) + return + + var/action="" + if(config.antag_hud_restricted) + for(var/mob/observer/dead/g in get_ghosts()) + to_chat(g, span_blue("The administrator has lifted restrictions on joining the round if you use AntagHUD")) + action = "lifted restrictions" + config.antag_hud_restricted = 0 + to_chat(src, span_blue("AntagHUD restrictions have been lifted")) + else + for(var/mob/observer/dead/g in get_ghosts()) + to_chat(g, span_red("The administrator has placed restrictions on joining the round if you use AntagHUD")) + to_chat(g, span_red("Your AntagHUD has been disabled, you may choose to re-enabled it but will be under restrictions ")) + g.antagHUD = 0 + g.has_enabled_antagHUD = 0 + action = "placed restrictions" + config.antag_hud_restricted = 1 + to_chat(src, span_red("AntagHUD restrictions have been enabled")) + + log_admin("[key_name(usr)] has [action] on joining the round if they use AntagHUD") + message_admins("Admin [key_name_admin(usr)] has [action] on joining the round if they use AntagHUD", 1) + +/* +If a guy was gibbed and you want to revive him, this is a good way to do so. +Works kind of like entering the game with a new character. Character receives a new mind if they didn't have one. +Traitors and the like can also be revived with the previous role mostly intact. +/N */ +/client/proc/respawn_character() + set category = "Special Verbs" + set name = "Spawn Character" + set desc = "(Re)Spawn a client's loaded character." + + if(!holder) + return + + var/client/picked_client = tgui_input_list(src, "Please specify which client's character to spawn.", "Client", GLOB.clients) + if(!picked_client) + return + + respawn_character_proper(picked_client) + +/client/proc/respawn_character_proper(client/picked_client) + if(!istype(picked_client)) + return + + //I frontload all the questions so we don't have a half-done process while you're reading. + var/location = tgui_alert(src, "Please specify where to spawn them.", "Location", list("Right Here", "Arrivals", "Cancel")) + if(location == "Cancel" || !location) + return + + var/announce = tgui_alert(src,"Announce as if they had just arrived?", "Announce", list("No", "Yes", "Cancel")) + if(announce == "Cancel") + return + else if(announce == "Yes") //Too bad buttons can't just have 1/0 values and different display strings + announce = 1 + else + announce = 0 + + var/inhabit = tgui_alert(src,"Put the person into the spawned mob?", "Inhabit", list("Yes", "No", "Cancel")) + if(inhabit == "Cancel") + return + else if(inhabit == "Yes") + inhabit = 1 + else + inhabit = 0 + + //Name matching is ugly but mind doesn't persist to look at. + var/charjob + var/records + var/datum/data/record/record_found + record_found = find_general_record("name",picked_client.prefs.real_name) + + //Found their record, they were spawned previously + if(record_found) + var/samejob = tgui_alert(src,"Found [picked_client.prefs.real_name] in data core. They were [record_found.fields["real_rank"]] this round. Assign same job? They will not be re-added to the manifest/records, either way.","Previously spawned",list("Yes","Assistant","No")) + if(samejob == "Yes") + charjob = record_found.fields["real_rank"] + else if(samejob == USELESS_JOB) //VOREStation Edit - Visitor not Assistant + charjob = USELESS_JOB //VOREStation Edit - Visitor not Assistant + else + records = tgui_alert(src,"No data core entry detected. Would you like add them to the manifest, and sec/med/HR records?","Records",list("No", "Yes", "Cancel")) + if(records == "Cancel") + return + if(records == "Yes") + records = 1 + else + records = 0 + + //Well you're not reloading their job or they never had one. + if(!charjob) + var/pickjob = tgui_input_list(src,"Pick a job to assign them (or none).","Job Select", joblist + "-No Job-", "-No Job-") + if(!pickjob) + return + if(pickjob != "-No Job-") + charjob = pickjob + + //If you've picked a job by now, you can equip them. + var/equipment + if(charjob) + equipment = tgui_alert(src,"Spawn them with equipment?", "Equipment", list("Yes", "No", "Cancel")) + if(equipment == "Cancel") + return + else if(equipment == "Yes") + equipment = 1 + else + equipment = 0 + + //For logging later + var/admin = key_name_admin(src) + var/player_key = picked_client.key + //VOREStation Add - Needed for persistence + var/picked_ckey = picked_client.ckey + var/picked_slot = picked_client.prefs.default_slot + //VOREStation Add End + + var/mob/living/carbon/human/new_character + var/spawnloc + var/showy + + //Where did you want to spawn them? + switch(location) + if("Right Here") //Spawn them on your turf + spawnloc = get_turf(src.mob) + showy = tgui_input_list(src,"Showy entrance?", "Showy", list("No", "Telesparks", "Drop Pod", "Fall", "Cancel")) + if(showy == "Cancel") + return + if(showy == "Drop Pod") + showy = tgui_alert(src,"Destructive drop pods cause damage in a 3x3 and may break turfs. Polite drop pods lightly damage the turfs but won't break through.", "Drop Pod", list("Polite", "Destructive", "Cancel")) // reusing var + if(showy == "Cancel") + return + + if("Arrivals") //Spawn them at a latejoin spawnpoint + spawnloc = pick(latejoin) + + else //I have no idea how you're here + to_chat(src, "Invalid spawn location choice.") + return + + //Did we actually get a loc to spawn them? + if(!spawnloc) + to_chat(src, "Couldn't get valid spawn location.") + return + + new_character = new(spawnloc) + + if(showy == "Telesparks") + anim(spawnloc,new_character,'icons/mob/mob.dmi',,"phasein",,new_character.dir) + playsound(spawnloc, "sparks", 50, 1) + var/datum/effect/effect/system/spark_spread/spk = new(new_character) + spk.set_up(5, 0, new_character) + spk.attach(new_character) + spk.start() + + //We were able to spawn them, right? + if(!new_character) + to_chat(src, "Something went wrong and spawning failed.") + return + + //Write the appearance and whatnot out to the character + picked_client.prefs.copy_to(new_character) + if(new_character.dna) + new_character.dna.ResetUIFrom(new_character) + new_character.sync_organ_dna() + if(inhabit) + new_character.key = player_key + //Were they any particular special role? If so, copy. + if(new_character.mind) + var/datum/antagonist/antag_data = get_antag_data(new_character.mind.special_role) + if(antag_data) + antag_data.add_antagonist(new_character.mind) + antag_data.place_mob(new_character) + + //VOREStation Add - Required for persistence + if(new_character.mind) + new_character.mind.loaded_from_ckey = picked_ckey + new_character.mind.loaded_from_slot = picked_slot + //VOREStation Add End + + for(var/lang in picked_client.prefs.alternate_languages) + var/datum/language/chosen_language = GLOB.all_languages[lang] + if(chosen_language) + if(is_lang_whitelisted(src,chosen_language) || (new_character.species && (chosen_language.name in new_character.species.secondary_langs))) + new_character.add_language(lang) + + //If desired, apply equipment. + if(equipment) + if(charjob) + job_master.EquipRank(new_character, charjob, 1) + if(new_character.mind) + new_character.mind.assigned_role = charjob + new_character.mind.role_alt_title = job_master.GetPlayerAltTitle(new_character, charjob) + //equip_custom_items(new_character) //VOREStation Removal + + //If desired, add records. + if(records) + data_core.manifest_inject(new_character) + + //A redraw for good measure + new_character.regenerate_icons() + + new_character.update_transform() //VOREStation Edit + + //If we're announcing their arrival + if(announce) + AnnounceArrival(new_character, new_character.mind.assigned_role, "Common", new_character.z) + + log_admin("[admin] has spawned [player_key]'s character [new_character.real_name].") + message_admins("[admin] has spawned [player_key]'s character [new_character.real_name].", 1) + + + + feedback_add_details("admin_verb","RSPCH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + // Drop pods and fall + if(showy == "Polite") + var/turf/T = get_turf(new_character) + new /obj/structure/drop_pod/polite(T, new_character) + to_chat(new_character, "Please wait for your arrival.") + else if(showy == "Destructive") + var/turf/T = get_turf(new_character) + new /obj/structure/drop_pod(T, new_character) + to_chat(new_character, "Please wait for your arrival.") + else if(showy == "Fall") + spawn(1) + var/initial_x = new_character.pixel_x + var/initial_y = new_character.pixel_y + new_character.plane = 1 + new_character.pixel_x = rand(-150, 150) + new_character.pixel_y = 500 // When you think that pixel_z is height but you are wrong + new_character.density = FALSE + new_character.opacity = FALSE + animate(new_character, pixel_y = initial_y, pixel_x = initial_x , time = 7) + spawn(7) + new_character.end_fall() + to_chat(new_character, "You have been fully spawned. Enjoy the game.") + + return new_character + +/client/proc/cmd_admin_add_freeform_ai_law() + set category = "Fun" + set name = "Add Custom AI law" + + if(!holder) + return + + var/input = sanitize(tgui_input_text(usr, "Please enter anything you want the AI to do. Anything. Serious.", "What?", "")) + if(!input) + return + for(var/mob/living/silicon/ai/M in mob_list) + if (M.stat == 2) + to_chat(usr, "Upload failed. No signal is being detected from the AI.") + else if (M.see_in_dark == 0) + to_chat(usr, "Upload failed. Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power.") + else + M.add_ion_law(input) + for(var/mob/living/silicon/ai/O in mob_list) + to_chat(O,input + span_red("... LAWS UPDATED!")) + O.show_laws() + + log_admin("Admin [key_name(usr)] has added a new AI law - [input]") + message_admins("Admin [key_name_admin(usr)] has added a new AI law - [input]", 1) + + var/show_log = tgui_alert(src, "Show ion message?", "Message", list("Yes", "No")) + if(show_log == "Yes") + command_announcement.Announce("Ion storm detected near the [station_name()]. Please check all AI-controlled equipment for errors.", "Anomaly Alert", new_sound = 'sound/AI/ionstorm.ogg') + feedback_add_details("admin_verb","IONC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_rejuvenate(mob/living/M as mob in mob_list) + set category = "Special Verbs" + set name = "Rejuvenate" + + if(!holder) + return + + if(!mob) + return + if(!istype(M)) + tgui_alert_async(usr, "Cannot revive a ghost") + return + if(config.allow_admin_rev) + M.revive() + + log_admin("[key_name(usr)] healed / revived [key_name(M)]") + var/msg = "Admin [key_name_admin(usr)] healed / revived [ADMIN_LOOKUPFLW(M)]!" + message_admins(msg) + admin_ticket_log(M, msg) + else + tgui_alert_async(usr, "Admin revive disabled") + feedback_add_details("admin_verb","REJU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_create_centcom_report() + set category = "Special Verbs" + set name = "Create Command Report" + + if(!holder) + return + + var/input = sanitize(tgui_input_text(usr, "Please enter anything you want. Anything. Serious.", "What?", "", multiline = TRUE, prevent_enter = TRUE), extra = 0) + var/customname = sanitizeSafe(tgui_input_text(usr, "Pick a title for the report.", "Title")) + if(!input) + return + if(!customname) + customname = "[using_map.company_name] Update" + + //New message handling + post_comm_message(customname, replacetext(input, "\n", "
                    ")) + + switch(tgui_alert(usr, "Should this be announced to the general population?","Show world?",list("Yes","No"))) + if("Yes") + command_announcement.Announce(input, customname, new_sound = 'sound/AI/commandreport.ogg', msg_sanitized = 1); + if("No") + to_world(span_red("New [using_map.company_name] Update available at all communication consoles.")) + world << sound('sound/AI/commandreport.ogg') + + log_admin("[key_name(src)] has created a command report: [input]") + message_admins("[key_name_admin(src)] has created a command report", 1) + feedback_add_details("admin_verb","CCR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_delete(atom/O as obj|mob|turf in _validate_atom(O)) // I don't understand precisely how this fixes the string matching against a substring, but it does - Ater + set category = "Admin" + set name = "Delete" + + if (!holder) + return + + admin_delete(O) + +/client/proc/cmd_admin_list_open_jobs() + set category = "Admin" + set name = "List free slots" + + if (!holder) + return + + if(job_master) + for(var/datum/job/job in job_master.occupations) + to_chat(src, "[job.title]: [job.total_positions]") + feedback_add_details("admin_verb","LFS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_explosion(atom/O as obj|mob|turf in world) + set category = "Special Verbs" + set name = "Explosion" + + if(!check_rights(R_DEBUG|R_FUN)) return //VOREStation Edit + + var/devastation = tgui_input_number(usr, "Range of total devastation. -1 to none", text("Input"), min_value=-1) + if(devastation == null) return + var/heavy = tgui_input_number(usr, "Range of heavy impact. -1 to none", text("Input"), min_value=-1) + if(heavy == null) return + var/light = tgui_input_number(usr, "Range of light impact. -1 to none", text("Input"), min_value=-1) + if(light == null) return + var/flash = tgui_input_number(usr, "Range of flash. -1 to none", text("Input"), min_value=-1) + if(flash == null) return + + if ((devastation != -1) || (heavy != -1) || (light != -1) || (flash != -1)) + if ((devastation > 20) || (heavy > 20) || (light > 20)) + if (tgui_alert(src, "Are you sure you want to do this? It will laaag.", "Confirmation", list("Yes", "No")) == "No") + return + + explosion(O, devastation, heavy, light, flash) + log_admin("[key_name(usr)] created an explosion ([devastation],[heavy],[light],[flash]) at ([O.x],[O.y],[O.z])") + message_admins("[key_name_admin(usr)] created an explosion ([devastation],[heavy],[light],[flash]) at ([O.x],[O.y],[O.z])", 1) + feedback_add_details("admin_verb","EXPL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + else + return + +/client/proc/cmd_admin_emp(atom/O as obj|mob|turf in world) + set category = "Special Verbs" + set name = "EM Pulse" + + if(!check_rights(R_DEBUG|R_FUN)) return //VOREStation Edit + + var/heavy = tgui_input_number(usr, "Range of heavy pulse.", text("Input")) + if(heavy == null) return + var/med = tgui_input_number(usr, "Range of medium pulse.", text("Input")) + if(med == null) return + var/light = tgui_input_number(usr, "Range of light pulse.", text("Input")) + if(light == null) return + var/long = tgui_input_number(usr, "Range of long pulse.", text("Input")) + if(long == null) return + + if (heavy || med || light || long) + + empulse(O, heavy, med, light, long) + log_admin("[key_name(usr)] created an EM Pulse ([heavy],[med],[light],[long]) at ([O.x],[O.y],[O.z])") + message_admins("[key_name_admin(usr)] created an EM PUlse ([heavy],[med],[light],[long]) at ([O.x],[O.y],[O.z])", 1) + feedback_add_details("admin_verb","EMP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + return + else + return + +/client/proc/cmd_admin_gib(mob/M as mob in mob_list) + set category = "Special Verbs" + set name = "Gib" + + if(!check_rights(R_ADMIN|R_FUN)) return //VOREStation Edit + + var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) + if(confirm != "Yes") return + //Due to the delay here its easy for something to have happened to the mob + if(!M) return + + log_admin("[key_name(usr)] has gibbed [key_name(M)]") + message_admins("[key_name_admin(usr)] has gibbed [key_name_admin(M)]", 1) + + if(istype(M, /mob/observer/dead)) + gibs(M.loc) + return + + M.gib() + feedback_add_details("admin_verb","GIB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_gib_self() + set name = "Gibself" + set category = "Fun" + + if(!holder) + return + + var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) + if(confirm == "Yes") + if (istype(mob, /mob/observer/dead)) // so they don't spam gibs everywhere + return + else + mob.gib() + + log_admin("[key_name(usr)] used gibself.") + message_admins(span_blue("[key_name_admin(usr)] used gibself."), 1) + feedback_add_details("admin_verb","GIBS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/* +/client/proc/cmd_manual_ban() + set name = "Manual Ban" + set category = "Special Verbs" + if(!authenticated || !holder) + to_chat(src, "Only administrators may use this command.") + return + var/mob/M = null + switch(tgui_alert(usr, "How would you like to ban someone today?", "Manual Ban", "Key List", "Enter Manually", "Cancel")) + if("Key List") + var/list/keys = list() + for(var/mob/M in player_list) + keys += M.client + var/selection = tgui_input_list(usr, "Please, select a player!", "Admin Jumping", keys) + if(!selection) + return + M = selection:mob + if ((M.client && M.client.holder && (M.client.holder.level >= holder.level))) + tgui_alert_async(usr, "You cannot perform this action. You must be of a higher administrative rank!") + return + + switch(tgui_alert(usr, "Temporary Ban?","Temporary Ban",list("Yes","No"))) + if("Yes") + var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num + if(!mins) + return + if(mins >= 525600) mins = 525599 + var/reason = input(usr,"Reason?","reason","Griefer") as text + if(!reason) + return + if(M) + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") + to_chat(M, "To try to resolve this matter head to http://ss13.donglabs.com/forum/") + log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") + message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") + world.Export("http://216.38.134.132/adminlog.php?type=ban&key=[usr.client.key]&key2=[M.key]&msg=[html_decode(reason)]&time=[mins]&server=[replacetext(config.server_name, "#", "")]") + del(M.client) + qdel(M) + else + + if("No") + var/reason = input(usr,"Reason?","reason","Griefer") as text + if(!reason) + return + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0) + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a permanent ban.") + to_chat(M, "To try to resolve this matter head to http://ss13.donglabs.com/forum/") + log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") + message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") + world.Export("http://216.38.134.132/adminlog.php?type=ban&key=[usr.client.key]&key2=[M.key]&msg=[html_decode(reason)]&time=perma&server=[replacetext(config.server_name, "#", "")]") + del(M.client) + qdel(M) +*/ + +/client/proc/update_world() + // If I see anyone granting powers to specific keys like the code that was here, + // I will both remove their SVN access and permanently ban them from my servers. + return + +/client/proc/cmd_admin_check_contents(mob/living/M as mob in mob_list) + set category = "Special Verbs" + set name = "Check Contents" + set popup_menu = FALSE //VOREStation Edit - Declutter. + + if(!holder) + return + + var/list/L = M.get_contents() + for(var/t in L) + to_chat(usr, "[t]") + feedback_add_details("admin_verb","CC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/* This proc is DEFERRED. Does not do anything. +/client/proc/cmd_admin_remove_phoron() + set category = "Debug" + set name = "Stabilize Atmos." + if(!holder) + to_chat(src, "Only administrators may use this command.") + return + feedback_add_details("admin_verb","STATM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +// DEFERRED + spawn(0) + for(var/turf/T in view()) + T.poison = 0 + T.oldpoison = 0 + T.tmppoison = 0 + T.oxygen = 755985 + T.oldoxy = 755985 + T.tmpoxy = 755985 + T.co2 = 14.8176 + T.oldco2 = 14.8176 + T.tmpco2 = 14.8176 + T.n2 = 2.844e+006 + T.on2 = 2.844e+006 + T.tn2 = 2.844e+006 + T.tsl_gas = 0 + T.osl_gas = 0 + T.sl_gas = 0 + T.temp = 293.15 + T.otemp = 293.15 + T.ttemp = 293.15 +*/ + +/client/proc/toggle_view_range() + set category = "Special Verbs" + set name = "Change View Range" + set desc = "switches between 1x and custom views" + + if(!holder) + return + + var/view = src.view + if(view == world.view) + view = tgui_input_list(usr, "Select view range:", "FUCK YE", list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128)) + else + view = world.view + mob.set_viewsize(view) + + log_admin("[key_name(usr)] changed their view range to [view].") + //message_admins("[key_name_admin(usr)] changed their view range to [view].", 1) //why? removed by order of XSI + + feedback_add_details("admin_verb","CVRA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/admin_call_shuttle() + set category = "Admin" + set name = "Call Shuttle" + + if ((!( ticker ) || !emergency_shuttle.location())) + return + + if(!check_rights(R_ADMIN)) return //VOREStation Edit + + var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) + if(confirm != "Yes") return + + var/choice + if(ticker.mode.auto_recall_shuttle) + choice = tgui_input_list(usr, "The shuttle will just return if you call it. Call anyway?", "Shuttle Call", list("Confirm", "Cancel")) + if(choice == "Confirm") + emergency_shuttle.auto_recall = 1 //enable auto-recall + else + return + + choice = tgui_input_list(usr, "Is this an emergency evacuation or a crew transfer?", "Shuttle Call", list("Emergency", "Crew Transfer")) + if (choice == "Emergency") + emergency_shuttle.call_evac() + else + emergency_shuttle.call_transfer() + + + feedback_add_details("admin_verb","CSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] admin-called the emergency shuttle.") + message_admins(span_blue("[key_name_admin(usr)] admin-called the emergency shuttle."), 1) + return + +/client/proc/admin_cancel_shuttle() + set category = "Admin" + set name = "Cancel Shuttle" + + if(!check_rights(R_ADMIN)) return //VOREStation Edit + + if(tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) != "Yes") return + + if(!ticker || !emergency_shuttle.can_recall()) + return + + emergency_shuttle.recall() + feedback_add_details("admin_verb","CCSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] admin-recalled the emergency shuttle.") + message_admins(span_blue("[key_name_admin(usr)] admin-recalled the emergency shuttle."), 1) + + return + +/client/proc/admin_deny_shuttle() + set category = "Admin" + set name = "Toggle Deny Shuttle" + + if (!ticker) + return + + if(!check_rights(R_ADMIN)) return //VOREStation Edit + + emergency_shuttle.deny_shuttle = !emergency_shuttle.deny_shuttle + + log_admin("[key_name(src)] has [emergency_shuttle.deny_shuttle ? "denied" : "allowed"] the shuttle to be called.") + message_admins("[key_name_admin(usr)] has [emergency_shuttle.deny_shuttle ? "denied" : "allowed"] the shuttle to be called.") + +/client/proc/cmd_admin_attack_log(mob/M as mob in mob_list) + set category = "Special Verbs" + set name = "Attack Log" + + to_chat(usr, span_red("Attack Log for [mob]")) + for(var/t in M.attack_log) + to_chat(usr,t) + feedback_add_details("admin_verb","ATTL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/everyone_random() + set category = "Fun" + set name = "Make Everyone Random" + set desc = "Make everyone have a random appearance. You can only use this before rounds!" + + if(!check_rights(R_FUN)) return + + if (ticker && ticker.mode) + to_chat(usr, "Nope you can't do this, the game's already started. This only works before rounds!") + return + + if(ticker.random_players) + ticker.random_players = 0 + message_admins("Admin [key_name_admin(usr)] has disabled \"Everyone is Special\" mode.", 1) + to_chat(usr, "Disabled.") + return + + + var/notifyplayers = tgui_alert(src, "Do you want to notify the players?", "Options", list("Yes", "No", "Cancel")) + if(notifyplayers == "Cancel") + return + + log_admin("Admin [key_name(src)] has forced the players to have random appearances.") + message_admins("Admin [key_name_admin(usr)] has forced the players to have random appearances.", 1) + + if(notifyplayers == "Yes") + to_world(span_blue("Admin [usr.key] has forced the players to have completely random identities!")) + + to_chat(usr, "Remember: you can always disable the randomness by using the verb again, assuming the round hasn't started yet.") + + ticker.random_players = 1 + feedback_add_details("admin_verb","MER") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/toggle_random_events() + set category = "Server" + set name = "Toggle random events on/off" + set desc = "Toggles random events such as meteors, black holes, blob (but not space dust) on/off" + + if(!check_rights(R_SERVER)) return //VOREStation Edit + + if(!config.allow_random_events) + config.allow_random_events = 1 + to_chat(usr, "Random events enabled") + message_admins("Admin [key_name_admin(usr)] has enabled random events.", 1) + else + config.allow_random_events = 0 + to_chat(usr, "Random events disabled") + message_admins("Admin [key_name_admin(usr)] has disabled random events.", 1) + feedback_add_details("admin_verb","TRE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/despawn_player(var/mob/M in living_mob_list) + set name = "Cryo Player" + set category = "Admin" + set desc = "Removes a player from the round as if they'd cryo'd." + set popup_menu = FALSE + + if(!check_rights(R_ADMIN|R_EVENT)) + return + + if(!M) + return + + var/confirm = tgui_alert(usr, "Are you sure you want to cryo [M]?","Confirmation",list("No","Yes")) + if(confirm == "No") + return + + var/list/human_cryopods = list() + var/list/robot_cryopods = list() + + for(var/obj/machinery/cryopod/CP in machines) + if(!CP.control_computer) + continue //Broken pod w/o computer, move on. + + var/listname = "[CP.name] ([CP.x],[CP.y],[CP.z])" + if(istype(CP,/obj/machinery/cryopod/robot)) + robot_cryopods[listname] = CP + else + human_cryopods[listname] = CP + + //Gotta log this up here before they get ghostized and lose their key or anything. + log_and_message_admins("[key_name(src)] admin cryo'd [key_name(M)].") + feedback_add_details("admin_verb","ACRYO") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + if(ishuman(M)) + var/choice = tgui_input_list(usr,"Select a cryopod to use","Cryopod Choice", human_cryopods) + var/obj/machinery/cryopod/CP = human_cryopods[choice] + if(!CP) + return + M.ghostize() + CP.despawn_occupant(M) + return + + else if(issilicon(M)) + if(isAI(M)) + var/mob/living/silicon/ai/ai = M + empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(ai.loc) + global_announcer.autosay("[ai] has been moved to intelligence storage.", "Artificial Intelligence Oversight") + ai.clear_client() + return + else + var/choice = tgui_input_list(usr,"Select a cryopod to use","Cryopod Choice", robot_cryopods) + var/obj/machinery/cryopod/robot/CP = robot_cryopods[choice] + if(!CP) + return + M.ghostize() + CP.despawn_occupant(M) + return + + else if(isliving(M)) + M.ghostize() + qdel(M) //Bye + +/client/proc/cmd_admin_droppod_spawn(var/object as text) + set name = "Drop Pod Atom" + set desc = "Spawn a new atom/movable in a drop pod where you are." + set category = "Fun" + + if(!check_rights(R_SPAWN)) + return + + var/list/types = typesof(/atom/movable) + var/list/matches = new() + + for(var/path in types) + if(findtext("[path]", object)) + matches += path + + if(!matches.len) + return + + var/chosen + if(matches.len==1) + chosen = matches[1] + else + chosen = tgui_input_list(usr, "Select a movable type:", "Spawn in Drop Pod", matches) + if(!chosen) + return + + var/podtype = tgui_alert(src,"Destructive drop pods cause damage in a 3x3 and may break turfs. Polite drop pods lightly damage the turfs but won't break through.", "Drop Pod", list("Polite", "Destructive", "Cancel")) + if(podtype == "Cancel") + return + var/autoopen = tgui_alert(src,"Should the pod open automatically?", "Drop Pod", list("Yes", "No", "Cancel")) + if(autoopen == "Cancel") + return + switch(podtype) + if("Destructive") + var/atom/movable/AM = new chosen(usr.loc) + new /obj/structure/drop_pod(get_turf(usr), AM, autoopen == "Yes" ? TRUE : FALSE) + if("Polite") + var/atom/movable/AM = new chosen(usr.loc) + new /obj/structure/drop_pod/polite(get_turf(usr), AM, autoopen == "Yes" ? TRUE : FALSE) + + feedback_add_details("admin_verb","DPA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_droppod_deploy() + set name = "Drop Pod Deploy" + set desc = "Drop an existing mob where you are in a drop pod." + set category = "Fun" + + if(!check_rights(R_SPAWN)) + return + + var/mob/living/L = tgui_input_list(usr, "Select the mob to drop:", "Mob Picker", living_mob_list) + if(!L) + return + + var/podtype = tgui_alert(src,"Destructive drop pods cause damage in a 3x3 and may break turfs. Polite drop pods lightly damage the turfs but won't break through.", "Drop Pod", list("Polite", "Destructive", "Cancel")) + if(podtype == "Cancel") + return + var/autoopen = tgui_alert(src,"Should the pod open automatically?", "Drop Pod", list("Yes", "No", "Cancel")) + if(autoopen == "Cancel") + return + if(!L || QDELETED(L)) + return + switch(podtype) + if("Destructive") + new /obj/structure/drop_pod(get_turf(usr), L, autoopen == "Yes" ? TRUE : FALSE) + if("Polite") + new /obj/structure/drop_pod/polite(get_turf(usr), L, autoopen == "Yes" ? TRUE : FALSE) + + feedback_add_details("admin_verb","DPD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/resize.dm b/code/modules/admin/verbs/resize.dm index d8bc5be57c2..c67e2ffd8f7 100644 --- a/code/modules/admin/verbs/resize.dm +++ b/code/modules/admin/verbs/resize.dm @@ -5,7 +5,7 @@ if(!check_rights(R_ADMIN|R_FUN|R_VAREDIT)) return - var/size_multiplier = tgui_input_number(usr, "Input size multiplier.", "Resize", 1) + var/size_multiplier = tgui_input_number(usr, "Input size multiplier.", "Resize", 1, round_value=FALSE) if(!size_multiplier) return //cancelled diff --git a/code/modules/admin/verbs/striketeam.dm b/code/modules/admin/verbs/striketeam.dm index 85e13ce288d..737ab67f843 100644 --- a/code/modules/admin/verbs/striketeam.dm +++ b/code/modules/admin/verbs/striketeam.dm @@ -1,56 +1,56 @@ -//STRIKE TEAMS -var/const/commandos_possible = 6 //if more Commandos are needed in the future - -/client/proc/strike_team() - set category = "Fun" - set name = "Spawn Strike Team" - set desc = "Spawns a strike team if you want to run an admin event." - - if(!src.holder) - to_chat(src, "Only administrators may use this command.") - return - - if(!ticker) - to_chat(usr, span_red("The game hasn't started yet!")) - return - - if(world.time < 6000) - to_chat(usr, span_red("There are [(6000-world.time)/10] seconds remaining before it may be called.")) - return - - var/datum/antagonist/deathsquad/team - - var/choice = tgui_input_list(usr, "Select type of strike team:", "Strike Team", list("Heavy Asset Protection", "Mercenaries")) - if(!choice) - return - - switch(choice) - if("Heavy Asset Protection") - team = deathsquad - if("Mercenaries") - team = commandos - else - return - - if(team.deployed) - to_chat(usr, span_red("Someone is already sending a team.")) - return - - if(tgui_alert(usr, "Do you want to send in a strike team? Once enabled, this is irreversible.","Strike Team",list("Yes","No"))!="Yes") - return - - tgui_alert(usr, "This 'mode' will go on until everyone is dead or the station is destroyed. You may also admin-call the evac shuttle when appropriate. Spawned commandos have internals cameras which are viewable through a monitor inside the Spec. Ops. Office. Assigning the team's detailed task is recommended from there. While you will be able to manually pick the candidates from active ghosts, their assignment in the squad will be random.") // Should remain tgui_alert() (blocking) - - choice = null - while(!choice) - choice = sanitize(tgui_input_text(src, "Please specify which mission the strike team shall undertake.", "Specify Mission", "")) - if(!choice) - if(tgui_alert(usr, "Error, no mission set. Do you want to exit the setup process?","Strike Team",list("Yes","No"))=="Yes") - return - consider_ert_load() //VOREStation Add - - if(team.deployed) - to_chat(usr, "Looks like someone beat you to it.") - return - - team.attempt_random_spawn() +//STRIKE TEAMS +var/const/commandos_possible = 6 //if more Commandos are needed in the future + +/client/proc/strike_team() + set category = "Fun" + set name = "Spawn Strike Team" + set desc = "Spawns a strike team if you want to run an admin event." + + if(!src.holder) + to_chat(src, "Only administrators may use this command.") + return + + if(!ticker) + to_chat(usr, span_red("The game hasn't started yet!")) + return + + if(world.time < 6000) + to_chat(usr, span_red("There are [(6000-world.time)/10] seconds remaining before it may be called.")) + return + + var/datum/antagonist/deathsquad/team + + var/choice = tgui_input_list(usr, "Select type of strike team:", "Strike Team", list("Heavy Asset Protection", "Mercenaries")) + if(!choice) + return + + switch(choice) + if("Heavy Asset Protection") + team = deathsquad + if("Mercenaries") + team = commandos + else + return + + if(team.deployed) + to_chat(usr, span_red("Someone is already sending a team.")) + return + + if(tgui_alert(usr, "Do you want to send in a strike team? Once enabled, this is irreversible.","Strike Team",list("Yes","No"))!="Yes") + return + + tgui_alert(usr, "This 'mode' will go on until everyone is dead or the station is destroyed. You may also admin-call the evac shuttle when appropriate. Spawned commandos have internals cameras which are viewable through a monitor inside the Spec. Ops. Office. Assigning the team's detailed task is recommended from there. While you will be able to manually pick the candidates from active ghosts, their assignment in the squad will be random.") // Should remain tgui_alert() (blocking) + + choice = null + while(!choice) + choice = sanitize(tgui_input_text(src, "Please specify which mission the strike team shall undertake.", "Specify Mission", "")) + if(!choice) + if(tgui_alert(usr, "Error, no mission set. Do you want to exit the setup process?","Strike Team",list("Yes","No"))=="Yes") + return + consider_ert_load() //VOREStation Add + + if(team.deployed) + to_chat(usr, "Looks like someone beat you to it.") + return + + team.attempt_random_spawn() diff --git a/code/modules/admin/verbs/tripAI.dm b/code/modules/admin/verbs/tripAI.dm index 9298d1111ce..ff97f2e0dd9 100644 --- a/code/modules/admin/verbs/tripAI.dm +++ b/code/modules/admin/verbs/tripAI.dm @@ -1,22 +1,22 @@ -/client/proc/triple_ai() - set category = "Fun" - set name = "Create AI Triumvirate" - - if(ticker.current_state > GAME_STATE_PREGAME) - to_chat(usr, "This option is currently only usable during pregame. This may change at a later date.") - return - - if(job_master && ticker) - var/datum/job/job = job_master.GetJob("AI") - if(!job) - to_chat(usr, "Unable to locate the AI job") - return - if(ticker.triai) - ticker.triai = 0 - to_chat(usr, "Only one AI will be spawned at round start.") - message_admins(span_blue("[key_name_admin(usr)] has toggled off triple AIs at round start."), 1) - else - ticker.triai = 1 - to_chat(usr, "There will be an AI Triumvirate at round start.") - message_admins(span_blue("[key_name_admin(usr)] has toggled on triple AIs at round start."), 1) - return +/client/proc/triple_ai() + set category = "Fun" + set name = "Create AI Triumvirate" + + if(ticker.current_state > GAME_STATE_PREGAME) + to_chat(usr, "This option is currently only usable during pregame. This may change at a later date.") + return + + if(job_master && ticker) + var/datum/job/job = job_master.GetJob("AI") + if(!job) + to_chat(usr, "Unable to locate the AI job") + return + if(ticker.triai) + ticker.triai = 0 + to_chat(usr, "Only one AI will be spawned at round start.") + message_admins(span_blue("[key_name_admin(usr)] has toggled off triple AIs at round start."), 1) + else + ticker.triai = 1 + to_chat(usr, "There will be an AI Triumvirate at round start.") + message_admins(span_blue("[key_name_admin(usr)] has toggled on triple AIs at round start."), 1) + return diff --git a/code/modules/admin/view_variables/get_variables.dm b/code/modules/admin/view_variables/get_variables.dm index f3fb4ea145e..daac9c35742 100644 --- a/code/modules/admin/view_variables/get_variables.dm +++ b/code/modules/admin/view_variables/get_variables.dm @@ -97,7 +97,7 @@ if (VV_NUM) - .["value"] = tgui_input_number(usr, "Enter new number:", "Num", current_value) + .["value"] = tgui_input_number(usr, "Enter new number:", "Num", current_value, INFINITY, -INFINITY, round_value = FALSE) if (.["value"] == null) .["class"] = null return diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm index 09e15f7162a..c5e5cf4dedc 100644 --- a/code/modules/admin/view_variables/topic.dm +++ b/code/modules/admin/view_variables/topic.dm @@ -103,7 +103,7 @@ var/severity = tgui_input_number(usr, "How much damage should the bleeding internal wound cause? \ Bleed timer directly correlates with this. 0 cancels. Input is rounded to nearest integer.", - "Wound Severity", 0, min_value = 0, round_value = TRUE ) + "Wound Severity", 0) if(!severity) return var/obj/item/organ/external/chosen_organ = tgui_input_list(usr, "Choose an external organ to inflict IB on!", "Organ Choice", H.organs) @@ -545,7 +545,7 @@ var/Text = href_list["adjustDamage"] - var/amount = tgui_input_number(usr, "Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) + var/amount = tgui_input_number(usr, "Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0, min_value=-INFINITY, round_value=FALSE) if(!L) to_chat(usr, "Mob doesn't exist anymore") diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm index 0a2e303712a..86bda7de8a3 100644 --- a/code/modules/admin/view_variables/view_variables.dm +++ b/code/modules/admin/view_variables/view_variables.dm @@ -1,285 +1,285 @@ -/client/proc/debug_variables(datum/D in world) - set category = "Debug" - set name = "View Variables" - //set src in world - var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round. - - if(!usr.client || !usr.client.holder) //The usr vs src abuse in this proc is intentional and must not be changed - to_chat(usr, "You need to be an administrator to access this.") - return - - if(!D) - return - - var/islist = islist(D) - if (!islist && !istype(D)) - return - - //VOREStation Edit Start - the rest of this proc in a spawn - spawn(0) - var/title = "" - var/refid = "\ref[D]" - var/icon/sprite - var/hash - - var/type = /list - if (!islist) - type = D.type - - if(istype(D, /atom)) - var/atom/AT = D - if(AT.icon && AT.icon_state) - sprite = new /icon(AT.icon, AT.icon_state) - hash = md5(AT.icon) - hash = md5(hash + AT.icon_state) - src << browse_rsc(sprite, "vv[hash].png") - - title = "[D] (\ref[D]) = [type]" - var/formatted_type = replacetext("[type]", "/", "/") - - var/sprite_text - if(sprite) - sprite_text = "" - var/list/header = islist(D)? list("/list") : D.vv_get_header() - - var/marked - if(holder && holder.marked_datum && holder.marked_datum == D) - marked = VV_MSG_MARKED - var/varedited_line = "" - if(!islist && (D.datum_flags & DF_VAR_EDITED)) - varedited_line = VV_MSG_EDITED - var/deleted_line - if(!islist && D.gc_destroyed) - deleted_line = VV_MSG_DELETED - - var/list/dropdownoptions = list() - var/autoconvert_dropdown = FALSE - if (islist) - dropdownoptions = list( - "---", - "Add Item" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_ADD]=TRUE;target=[refid]", - "Remove Nulls" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_ERASE_NULLS]=TRUE;target=[refid]", - "Remove Dupes" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_ERASE_DUPES]=TRUE;target=[refid]", - "Set len" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_SET_LENGTH]=TRUE;target=[refid]", - "Shuffle" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_SHUFFLE]=TRUE;target=[refid]", - // "Show VV To Player" = "?_src_=vars;[HrefToken()];[VV_HK_EXPOSE]=TRUE;target=[refid]" // TODO - Not yet implemented for lists - ) - autoconvert_dropdown = TRUE - else - dropdownoptions = D.vv_get_dropdown() - var/list/dropdownoptions_html = list() - if(autoconvert_dropdown) - for (var/name in dropdownoptions) - var/link = dropdownoptions[name] - if (link) - dropdownoptions_html += "" - else - dropdownoptions_html += "" - else - dropdownoptions_html = dropdownoptions + D.get_view_variables_options() - - var/list/names = list() - if (!islist) - names = D.get_variables() - //sleep(1)//For some reason, without this sleep, VVing will cause client to disconnect on certain objects. //VOREStation edit - commented out, replaced with spawn(0) above - - var/list/variable_html = list() - if (islist) - var/list/L = D - for (var/i in 1 to L.len) - var/key = L[i] - var/value - if (IS_NORMAL_LIST(L) && IS_VALID_ASSOC_KEY(key)) - value = L[key] - variable_html += debug_variable(i, value, 0, D) - else - - names = sortList(names) - for (var/V in names) - if(D.can_vv_get(V)) - variable_html += D.vv_get_var(V) - - var/html = {" - - - [title] - - - - -
                    - - - - - -
                    - - - - -
                    - [sprite_text] -
                    - [header.Join()] -
                    -
                    -
                    - [formatted_type] - [marked] - [varedited_line] - [deleted_line] -
                    -
                    -
                    - Refresh -
                    - -
                    -
                    -
                    -
                    -
                    - - E - Edit, tries to determine the variable type by itself.
                    - C - Change, asks you for the var type first.
                    - M - Mass modify: changes this variable for all objects of this type.
                    -
                    -
                    - - - - - -
                    -
                    - Search: -
                    -
                    - -
                    -
                    -
                      - [variable_html.Join()] -
                    - - - - "} - src << browse(html, "window=variables[refid];size=475x650") //VOREStation edit end - -/client/proc/vv_update_display(datum/D, span, content) - src << output("[span]:[content]", "variables\ref[D].browser:replace_span") +/client/proc/debug_variables(datum/D in world) + set category = "Debug" + set name = "View Variables" + //set src in world + var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round. + + if(!usr.client || !usr.client.holder) //The usr vs src abuse in this proc is intentional and must not be changed + to_chat(usr, "You need to be an administrator to access this.") + return + + if(!D) + return + + var/islist = islist(D) + if (!islist && !istype(D)) + return + + //VOREStation Edit Start - the rest of this proc in a spawn + spawn(0) + var/title = "" + var/refid = "\ref[D]" + var/icon/sprite + var/hash + + var/type = /list + if (!islist) + type = D.type + + if(istype(D, /atom)) + var/atom/AT = D + if(AT.icon && AT.icon_state) + sprite = new /icon(AT.icon, AT.icon_state) + hash = md5(AT.icon) + hash = md5(hash + AT.icon_state) + src << browse_rsc(sprite, "vv[hash].png") + + title = "[D] (\ref[D]) = [type]" + var/formatted_type = replacetext("[type]", "/", "/") + + var/sprite_text + if(sprite) + sprite_text = "" + var/list/header = islist(D)? list("/list") : D.vv_get_header() + + var/marked + if(holder && holder.marked_datum && holder.marked_datum == D) + marked = VV_MSG_MARKED + var/varedited_line = "" + if(!islist && (D.datum_flags & DF_VAR_EDITED)) + varedited_line = VV_MSG_EDITED + var/deleted_line + if(!islist && D.gc_destroyed) + deleted_line = VV_MSG_DELETED + + var/list/dropdownoptions = list() + var/autoconvert_dropdown = FALSE + if (islist) + dropdownoptions = list( + "---", + "Add Item" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_ADD]=TRUE;target=[refid]", + "Remove Nulls" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_ERASE_NULLS]=TRUE;target=[refid]", + "Remove Dupes" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_ERASE_DUPES]=TRUE;target=[refid]", + "Set len" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_SET_LENGTH]=TRUE;target=[refid]", + "Shuffle" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_SHUFFLE]=TRUE;target=[refid]", + // "Show VV To Player" = "?_src_=vars;[HrefToken()];[VV_HK_EXPOSE]=TRUE;target=[refid]" // TODO - Not yet implemented for lists + ) + autoconvert_dropdown = TRUE + else + dropdownoptions = D.vv_get_dropdown() + var/list/dropdownoptions_html = list() + if(autoconvert_dropdown) + for (var/name in dropdownoptions) + var/link = dropdownoptions[name] + if (link) + dropdownoptions_html += "" + else + dropdownoptions_html += "" + else + dropdownoptions_html = dropdownoptions + D.get_view_variables_options() + + var/list/names = list() + if (!islist) + names = D.get_variables() + //sleep(1)//For some reason, without this sleep, VVing will cause client to disconnect on certain objects. //VOREStation edit - commented out, replaced with spawn(0) above + + var/list/variable_html = list() + if (islist) + var/list/L = D + for (var/i in 1 to L.len) + var/key = L[i] + var/value + if (IS_NORMAL_LIST(L) && IS_VALID_ASSOC_KEY(key)) + value = L[key] + variable_html += debug_variable(i, value, 0, D) + else + + names = sortList(names) + for (var/V in names) + if(D.can_vv_get(V)) + variable_html += D.vv_get_var(V) + + var/html = {" + + + [title] + + + + +
                    + + + + + +
                    + + + + +
                    + [sprite_text] +
                    + [header.Join()] +
                    +
                    +
                    + [formatted_type] + [marked] + [varedited_line] + [deleted_line] +
                    +
                    +
                    + Refresh +
                    + +
                    +
                    +
                    +
                    +
                    + + E - Edit, tries to determine the variable type by itself.
                    + C - Change, asks you for the var type first.
                    + M - Mass modify: changes this variable for all objects of this type.
                    +
                    +
                    + + + + + +
                    +
                    + Search: +
                    +
                    + +
                    +
                    +
                      + [variable_html.Join()] +
                    + + + + "} + src << browse(html, "window=variables[refid];size=475x650") //VOREStation edit end + +/client/proc/vv_update_display(datum/D, span, content) + src << output("[span]:[content]", "variables\ref[D].browser:replace_span") diff --git a/code/modules/ai/ai_holder_disabled.dm b/code/modules/ai/ai_holder_disabled.dm index ed2dc6cd4fa..cffce1e0640 100644 --- a/code/modules/ai/ai_holder_disabled.dm +++ b/code/modules/ai/ai_holder_disabled.dm @@ -1,104 +1,104 @@ -// Handles AI while stunned or otherwise disabled. - -/datum/ai_holder - var/respect_confusion = TRUE // If false, the mob won't wander around recklessly. - -// If our holder is able to do anything. -/datum/ai_holder/proc/can_act() - if(!holder) // Holder missing. - manage_processing(0) - return FALSE - if(holder.stat) // Dead or unconscious. - ai_log("can_act() : Stat was non-zero ([holder.stat]).", AI_LOG_TRACE) - return FALSE - if(holder.incapacitated(INCAPACITATION_DISABLED)) // Stunned in some form. - ai_log("can_act() : Incapacited.", AI_LOG_TRACE) - return FALSE - if(holder.instasis()) // In a stasis field. - ai_log("can_act() : In a stasis field.", AI_LOG_TRACE) - return FALSE - if(!belly_attack) - if(isbelly(holder.loc)) - return FALSE - return TRUE - -// Test if we should switch to STANCE_DISABLE. -// Currently tests for death, stuns, and confusion. -/datum/ai_holder/proc/is_disabled() - if(!can_act()) - return TRUE - if(is_confused()) - return TRUE - return FALSE - -/datum/ai_holder/proc/is_confused() - return holder.confused > 0 && respect_confusion - -// Called by the main loop. -/datum/ai_holder/proc/handle_disabled() - if(!can_act()) - return // Just sit there and take it. - else if(is_confused()) - dangerous_wander() // Let's bump into allies and hit them. - -// Similar to normal wander, but will walk into tiles that are harmful, and attack anything they bump into, including allies. -// Occurs when confused. -/datum/ai_holder/proc/dangerous_wander() - ai_log("dangerous_wander() : Entered.", AI_LOG_DEBUG) - if(isturf(holder.loc) && can_act()) - // Test if we should refrain from falling/attacking allies, if we're smart enough to realize that. - if(intelligence_level > AI_NORMAL) - var/unsafe = FALSE - - tile_test: - for(var/dir_tested in cardinal) - var/turf/turf_tested = get_step(holder, dir_tested) - // Look for unsafe tiles. - if(!turf_tested.is_safe_to_enter(holder)) - unsafe = TRUE - break - - // Look for allies. - for(var/mob/living/L in turf_tested) - if(holder.IIsAlly(L)) - unsafe = TRUE - break tile_test - - - if(unsafe) - ai_log("dangerous_wander() : Staying still due to risk of harm to self or allies.", AI_LOG_TRACE) - return // Just stay still. - - var/moving_to = 0 - moving_to = pick(cardinal) - var/turf/T = get_step(holder, moving_to) - - var/mob/living/L = locate() in T - if(L) - // Attack whoever's on the tile. Even if it's an ally. - ai_log("dangerous_wander() : Going to confuse-attack [L].", AI_LOG_TRACE) - melee_attack(L) - else - // Move to the tile. Even if it's unsafe. - ai_log("dangerous_wander() : Going to confuse-walk to [T] ([T.x],[T.y],[T.z]).", AI_LOG_TRACE) - holder.IMove(T, safety = FALSE) - ai_log("dangerous_wander() : Exited.", AI_LOG_DEBUG) - -/* -// Wanders randomly in cardinal directions. -/datum/ai_holder/proc/handle_wander_movement() - ai_log("handle_wander_movement() : Entered.", AI_LOG_DEBUG) - if(isturf(holder.loc) && can_act()) - wander_delay-- - if(wander_delay <= 0) - if(!wander_when_pulled && holder.pulledby) - ai_log("handle_wander_movement() : Being pulled and cannot wander. Exiting.", AI_LOG_DEBUG) - return - - var/moving_to = 0 // Apparently this is required or it always picks 4, according to the previous developer for simplemob AI. - moving_to = pick(cardinal) - holder.set_dir(moving_to) - holder.IMove(get_step(holder,moving_to)) - wander_delay = base_wander_delay - ai_log("handle_wander_movement() : Exited.", AI_LOG_DEBUG) -*/ +// Handles AI while stunned or otherwise disabled. + +/datum/ai_holder + var/respect_confusion = TRUE // If false, the mob won't wander around recklessly. + +// If our holder is able to do anything. +/datum/ai_holder/proc/can_act() + if(!holder) // Holder missing. + manage_processing(0) + return FALSE + if(holder.stat) // Dead or unconscious. + ai_log("can_act() : Stat was non-zero ([holder.stat]).", AI_LOG_TRACE) + return FALSE + if(holder.incapacitated(INCAPACITATION_DISABLED)) // Stunned in some form. + ai_log("can_act() : Incapacited.", AI_LOG_TRACE) + return FALSE + if(holder.instasis()) // In a stasis field. + ai_log("can_act() : In a stasis field.", AI_LOG_TRACE) + return FALSE + if(!belly_attack) + if(isbelly(holder.loc)) + return FALSE + return TRUE + +// Test if we should switch to STANCE_DISABLE. +// Currently tests for death, stuns, and confusion. +/datum/ai_holder/proc/is_disabled() + if(!can_act()) + return TRUE + if(is_confused()) + return TRUE + return FALSE + +/datum/ai_holder/proc/is_confused() + return holder.confused > 0 && respect_confusion + +// Called by the main loop. +/datum/ai_holder/proc/handle_disabled() + if(!can_act()) + return // Just sit there and take it. + else if(is_confused()) + dangerous_wander() // Let's bump into allies and hit them. + +// Similar to normal wander, but will walk into tiles that are harmful, and attack anything they bump into, including allies. +// Occurs when confused. +/datum/ai_holder/proc/dangerous_wander() + ai_log("dangerous_wander() : Entered.", AI_LOG_DEBUG) + if(isturf(holder.loc) && can_act()) + // Test if we should refrain from falling/attacking allies, if we're smart enough to realize that. + if(intelligence_level > AI_NORMAL) + var/unsafe = FALSE + + tile_test: + for(var/dir_tested in cardinal) + var/turf/turf_tested = get_step(holder, dir_tested) + // Look for unsafe tiles. + if(!turf_tested.is_safe_to_enter(holder)) + unsafe = TRUE + break + + // Look for allies. + for(var/mob/living/L in turf_tested) + if(holder.IIsAlly(L)) + unsafe = TRUE + break tile_test + + + if(unsafe) + ai_log("dangerous_wander() : Staying still due to risk of harm to self or allies.", AI_LOG_TRACE) + return // Just stay still. + + var/moving_to = 0 + moving_to = pick(cardinal) + var/turf/T = get_step(holder, moving_to) + + var/mob/living/L = locate() in T + if(L) + // Attack whoever's on the tile. Even if it's an ally. + ai_log("dangerous_wander() : Going to confuse-attack [L].", AI_LOG_TRACE) + melee_attack(L) + else + // Move to the tile. Even if it's unsafe. + ai_log("dangerous_wander() : Going to confuse-walk to [T] ([T.x],[T.y],[T.z]).", AI_LOG_TRACE) + holder.IMove(T, safety = FALSE) + ai_log("dangerous_wander() : Exited.", AI_LOG_DEBUG) + +/* +// Wanders randomly in cardinal directions. +/datum/ai_holder/proc/handle_wander_movement() + ai_log("handle_wander_movement() : Entered.", AI_LOG_DEBUG) + if(isturf(holder.loc) && can_act()) + wander_delay-- + if(wander_delay <= 0) + if(!wander_when_pulled && holder.pulledby) + ai_log("handle_wander_movement() : Being pulled and cannot wander. Exiting.", AI_LOG_DEBUG) + return + + var/moving_to = 0 // Apparently this is required or it always picks 4, according to the previous developer for simplemob AI. + moving_to = pick(cardinal) + holder.set_dir(moving_to) + holder.IMove(get_step(holder,moving_to)) + wander_delay = base_wander_delay + ai_log("handle_wander_movement() : Exited.", AI_LOG_DEBUG) +*/ diff --git a/code/modules/alarm/alarm.dm b/code/modules/alarm/alarm.dm index e5a958873d0..3ad1df433f9 100644 --- a/code/modules/alarm/alarm.dm +++ b/code/modules/alarm/alarm.dm @@ -1,137 +1,137 @@ -#define ALARM_RESET_DELAY 100 // How long will the alarm/trigger remain active once origin/source has been found to be gone? - -/datum/alarm_source - var/source = null // The source trigger - var/source_name = "" // The name of the source should it be lost (for example a destroyed camera) - var/duration = 0 // How long this source will be alarming, 0 for indefinetely. - var/severity = 1 // How severe the alarm from this source is. - var/start_time = 0 // When this source began alarming. - var/end_time = 0 // Use to set when this trigger should clear, in case the source is lost. - -/datum/alarm_source/New(var/atom/source) - src.source = source - start_time = world.time - source_name = source.get_source_name() - -/datum/alarm - var/atom/origin //Used to identify the alarm area. - var/list/sources = new() //List of sources triggering the alarm. Used to determine when the alarm should be cleared. - var/list/sources_assoc = new() //Associative list of source triggers. Used to efficiently acquire the alarm source. - var/list/cameras //List of cameras that can be switched to, if the player has that capability. - var/area/last_area //The last acquired area, used should origin be lost (for example a destroyed borg containing an alarming camera). - var/area/last_name //The last acquired name, used should origin be lost - var/area/last_camera_area //The last area in which cameras where fetched, used to see if the camera list should be updated. - var/end_time //Used to set when this alarm should clear, in case the origin is lost. - var/hidden = FALSE //If this alarm can be seen from consoles or other things. - -/datum/alarm/New(var/atom/origin, var/atom/source, var/duration, var/severity, var/hidden) - src.origin = origin - - cameras() // Sets up both cameras and last alarm area. - set_source_data(source, duration, severity, hidden) - -/datum/alarm/process() - // Has origin gone missing? - if(!origin && !end_time) - end_time = world.time + ALARM_RESET_DELAY - for(var/datum/alarm_source/AS in sources) - // Has the alarm passed its best before date? - if((AS.end_time && world.time > AS.end_time) || (AS.duration && world.time > (AS.start_time + AS.duration))) - sources -= AS - // Has the source gone missing? Then reset the normal duration and set end_time - if(!AS.source && !AS.end_time) // end_time is used instead of duration to ensure the reset doesn't remain in the future indefinetely. - AS.duration = 0 - AS.end_time = world.time + ALARM_RESET_DELAY - -/datum/alarm/proc/set_source_data(var/atom/source, var/duration, var/severity, var/hidden) - var/datum/alarm_source/AS = sources_assoc[source] - if(!AS) - AS = new/datum/alarm_source(source) - sources += AS - sources_assoc[source] = AS - src.hidden = hidden - // Currently only non-0 durations can be altered (normal alarms VS EMP blasts) - if(AS.duration) - duration = SecondsToTicks(duration) - AS.duration = duration - AS.severity = severity - src.hidden = min(src.hidden, hidden) - -/datum/alarm/proc/clear(var/source) - var/datum/alarm_source/AS = sources_assoc[source] - sources -= AS - sources_assoc -= source - -/datum/alarm/proc/alarm_area() - if(!origin) - return last_area - - last_area = origin.get_alarm_area() - return last_area - -/datum/alarm/proc/alarm_name() - if(!origin) - return last_name - - last_name = origin.get_alarm_name() - return last_name - -/datum/alarm/proc/cameras() - // If the alarm origin has changed area, for example a borg containing an alarming camera, reset the list of cameras - if(cameras && (last_camera_area != alarm_area())) - cameras = null - - if(!cameras) - cameras = origin ? origin.get_alarm_cameras() : last_area.get_alarm_cameras() - - last_camera_area = last_area - return cameras - -/datum/alarm/proc/max_severity() - var/max_severity = 0 - for(var/datum/alarm_source/AS in sources) - max_severity = max(AS.severity, max_severity) - - return max_severity - -/****************** -* Assisting procs * -******************/ -/atom/proc/get_alarm_area() - return get_area(src) - -/area/get_alarm_area() - return src - -/atom/proc/get_alarm_name() - var/area/A = get_area(src) - return A.name - -/area/get_alarm_name() - return name - -/mob/get_alarm_name() - return name - -/atom/proc/get_source_name() - return name - -/obj/machinery/camera/get_source_name() - return c_tag - -/atom/proc/get_alarm_cameras() - var/area/A = get_area(src) - return A.get_cameras() - -/area/get_alarm_cameras() - return get_cameras() - -/mob/living/silicon/robot/get_alarm_cameras() - var/list/cameras = ..() - if(camera) - cameras += camera - - return cameras - -/mob/living/silicon/robot/syndicate/get_alarm_cameras() - return list() +#define ALARM_RESET_DELAY 100 // How long will the alarm/trigger remain active once origin/source has been found to be gone? + +/datum/alarm_source + var/source = null // The source trigger + var/source_name = "" // The name of the source should it be lost (for example a destroyed camera) + var/duration = 0 // How long this source will be alarming, 0 for indefinetely. + var/severity = 1 // How severe the alarm from this source is. + var/start_time = 0 // When this source began alarming. + var/end_time = 0 // Use to set when this trigger should clear, in case the source is lost. + +/datum/alarm_source/New(var/atom/source) + src.source = source + start_time = world.time + source_name = source.get_source_name() + +/datum/alarm + var/atom/origin //Used to identify the alarm area. + var/list/sources = new() //List of sources triggering the alarm. Used to determine when the alarm should be cleared. + var/list/sources_assoc = new() //Associative list of source triggers. Used to efficiently acquire the alarm source. + var/list/cameras //List of cameras that can be switched to, if the player has that capability. + var/area/last_area //The last acquired area, used should origin be lost (for example a destroyed borg containing an alarming camera). + var/area/last_name //The last acquired name, used should origin be lost + var/area/last_camera_area //The last area in which cameras where fetched, used to see if the camera list should be updated. + var/end_time //Used to set when this alarm should clear, in case the origin is lost. + var/hidden = FALSE //If this alarm can be seen from consoles or other things. + +/datum/alarm/New(var/atom/origin, var/atom/source, var/duration, var/severity, var/hidden) + src.origin = origin + + cameras() // Sets up both cameras and last alarm area. + set_source_data(source, duration, severity, hidden) + +/datum/alarm/process() + // Has origin gone missing? + if(!origin && !end_time) + end_time = world.time + ALARM_RESET_DELAY + for(var/datum/alarm_source/AS in sources) + // Has the alarm passed its best before date? + if((AS.end_time && world.time > AS.end_time) || (AS.duration && world.time > (AS.start_time + AS.duration))) + sources -= AS + // Has the source gone missing? Then reset the normal duration and set end_time + if(!AS.source && !AS.end_time) // end_time is used instead of duration to ensure the reset doesn't remain in the future indefinetely. + AS.duration = 0 + AS.end_time = world.time + ALARM_RESET_DELAY + +/datum/alarm/proc/set_source_data(var/atom/source, var/duration, var/severity, var/hidden) + var/datum/alarm_source/AS = sources_assoc[source] + if(!AS) + AS = new/datum/alarm_source(source) + sources += AS + sources_assoc[source] = AS + src.hidden = hidden + // Currently only non-0 durations can be altered (normal alarms VS EMP blasts) + if(AS.duration) + duration = SecondsToTicks(duration) + AS.duration = duration + AS.severity = severity + src.hidden = min(src.hidden, hidden) + +/datum/alarm/proc/clear(var/source) + var/datum/alarm_source/AS = sources_assoc[source] + sources -= AS + sources_assoc -= source + +/datum/alarm/proc/alarm_area() + if(!origin) + return last_area + + last_area = origin.get_alarm_area() + return last_area + +/datum/alarm/proc/alarm_name() + if(!origin) + return last_name + + last_name = origin.get_alarm_name() + return last_name + +/datum/alarm/proc/cameras() + // If the alarm origin has changed area, for example a borg containing an alarming camera, reset the list of cameras + if(cameras && (last_camera_area != alarm_area())) + cameras = null + + if(!cameras) + cameras = origin ? origin.get_alarm_cameras() : last_area.get_alarm_cameras() + + last_camera_area = last_area + return cameras + +/datum/alarm/proc/max_severity() + var/max_severity = 0 + for(var/datum/alarm_source/AS in sources) + max_severity = max(AS.severity, max_severity) + + return max_severity + +/****************** +* Assisting procs * +******************/ +/atom/proc/get_alarm_area() + return get_area(src) + +/area/get_alarm_area() + return src + +/atom/proc/get_alarm_name() + var/area/A = get_area(src) + return A.name + +/area/get_alarm_name() + return name + +/mob/get_alarm_name() + return name + +/atom/proc/get_source_name() + return name + +/obj/machinery/camera/get_source_name() + return c_tag + +/atom/proc/get_alarm_cameras() + var/area/A = get_area(src) + return A.get_cameras() + +/area/get_alarm_cameras() + return get_cameras() + +/mob/living/silicon/robot/get_alarm_cameras() + var/list/cameras = ..() + if(camera) + cameras += camera + + return cameras + +/mob/living/silicon/robot/syndicate/get_alarm_cameras() + return list() diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm index cbec4c25b68..4064d700d2e 100644 --- a/code/modules/alarm/alarm_handler.dm +++ b/code/modules/alarm/alarm_handler.dm @@ -1,116 +1,116 @@ -#define ALARM_RAISED 1 -#define ALARM_CLEARED 0 - -/datum/alarm_handler - var/category = "" - var/list/datum/alarm/alarms = new // All alarms, to handle cases when an origin has been deleted with one or more active alarms - var/list/datum/alarm/alarms_assoc = new // Associative list of alarms, to efficiently acquire them based on origin. - var/list/listeners = new // A list of all objects interested in alarm changes. - -/datum/alarm_handler/process() - for(var/datum/alarm/A in alarms) - A.process() - check_alarm_cleared(A) - -/datum/alarm_handler/proc/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1, var/hidden = 0) - var/new_alarm - //Proper origin and source mandatory - if(!(origin && source)) - return - origin = origin.get_alarm_origin() - - new_alarm = 0 - //see if there is already an alarm of this origin - var/datum/alarm/existing = alarms_assoc[origin] - if(existing) - existing.set_source_data(source, duration, severity, hidden) - else - existing = new/datum/alarm(origin, source, duration, severity, hidden) - new_alarm = 1 - - alarms |= existing - alarms_assoc[origin] = existing - if(new_alarm) - alarms = dd_sortedObjectList(alarms) - on_alarm_change(existing, ALARM_RAISED) - - return new_alarm - -/datum/alarm_handler/proc/clearAlarm(var/atom/origin, var/source) - //Proper origin and source mandatory - if(!(origin && source)) - return - origin = origin.get_alarm_origin() - - var/datum/alarm/existing = alarms_assoc[origin] - if(existing) - existing.clear(source) - return check_alarm_cleared(existing) - -/datum/alarm_handler/proc/major_alarms(var/z) - return visible_alarms(z) - -/datum/alarm_handler/proc/has_major_alarms(var/z) - if(!LAZYLEN(alarms)) - return 0 - - return LAZYLEN(major_alarms(z)) - -/datum/alarm_handler/proc/minor_alarms(var/z) - return visible_alarms(z) - -/datum/alarm_handler/proc/check_alarm_cleared(var/datum/alarm/alarm) - if ((alarm.end_time && world.time > alarm.end_time) || !alarm.sources.len) - alarms -= alarm - alarms_assoc -= alarm.origin - on_alarm_change(alarm, ALARM_CLEARED) - return 1 - return 0 - -/datum/alarm_handler/proc/on_alarm_change(var/datum/alarm/alarm, var/was_raised) - for(var/obj/machinery/camera/C in alarm.cameras()) - if(was_raised && !alarm.hidden) - C.add_network(category) - else - C.remove_network(category) - notify_listeners(alarm, was_raised) - -/datum/alarm_handler/proc/get_alarm_severity_for_origin(var/atom/origin) - if(!origin) - return - - origin = origin.get_alarm_origin() - var/datum/alarm/existing = alarms_assoc[origin] - if(!existing) - return - - return existing.max_severity() - -/atom/proc/get_alarm_origin() - return src - -/turf/get_alarm_origin() - return get_area(src) - -/datum/alarm_handler/proc/register_alarm(var/object, var/procName) - listeners[object] = procName - -/datum/alarm_handler/proc/unregister_alarm(var/object) - listeners -= object - -/datum/alarm_handler/proc/notify_listeners(var/alarm, var/was_raised) - for(var/listener in listeners) - call(listener, listeners[listener])(src, alarm, was_raised) - -/datum/alarm_handler/proc/visible_alarms(var/z) - if(!LAZYLEN(alarms)) - return list() - - var/list/map_levels = using_map.get_map_levels(z) - - var/list/visible_alarms = new() - for(var/datum/alarm/A in alarms) - if(A.hidden || (z && !(A.origin?.z in map_levels))) - continue - visible_alarms.Add(A) +#define ALARM_RAISED 1 +#define ALARM_CLEARED 0 + +/datum/alarm_handler + var/category = "" + var/list/datum/alarm/alarms = new // All alarms, to handle cases when an origin has been deleted with one or more active alarms + var/list/datum/alarm/alarms_assoc = new // Associative list of alarms, to efficiently acquire them based on origin. + var/list/listeners = new // A list of all objects interested in alarm changes. + +/datum/alarm_handler/process() + for(var/datum/alarm/A in alarms) + A.process() + check_alarm_cleared(A) + +/datum/alarm_handler/proc/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1, var/hidden = 0) + var/new_alarm + //Proper origin and source mandatory + if(!(origin && source)) + return + origin = origin.get_alarm_origin() + + new_alarm = 0 + //see if there is already an alarm of this origin + var/datum/alarm/existing = alarms_assoc[origin] + if(existing) + existing.set_source_data(source, duration, severity, hidden) + else + existing = new/datum/alarm(origin, source, duration, severity, hidden) + new_alarm = 1 + + alarms |= existing + alarms_assoc[origin] = existing + if(new_alarm) + alarms = dd_sortedObjectList(alarms) + on_alarm_change(existing, ALARM_RAISED) + + return new_alarm + +/datum/alarm_handler/proc/clearAlarm(var/atom/origin, var/source) + //Proper origin and source mandatory + if(!(origin && source)) + return + origin = origin.get_alarm_origin() + + var/datum/alarm/existing = alarms_assoc[origin] + if(existing) + existing.clear(source) + return check_alarm_cleared(existing) + +/datum/alarm_handler/proc/major_alarms(var/z) + return visible_alarms(z) + +/datum/alarm_handler/proc/has_major_alarms(var/z) + if(!LAZYLEN(alarms)) + return 0 + + return LAZYLEN(major_alarms(z)) + +/datum/alarm_handler/proc/minor_alarms(var/z) + return visible_alarms(z) + +/datum/alarm_handler/proc/check_alarm_cleared(var/datum/alarm/alarm) + if ((alarm.end_time && world.time > alarm.end_time) || !alarm.sources.len) + alarms -= alarm + alarms_assoc -= alarm.origin + on_alarm_change(alarm, ALARM_CLEARED) + return 1 + return 0 + +/datum/alarm_handler/proc/on_alarm_change(var/datum/alarm/alarm, var/was_raised) + for(var/obj/machinery/camera/C in alarm.cameras()) + if(was_raised && !alarm.hidden) + C.add_network(category) + else + C.remove_network(category) + notify_listeners(alarm, was_raised) + +/datum/alarm_handler/proc/get_alarm_severity_for_origin(var/atom/origin) + if(!origin) + return + + origin = origin.get_alarm_origin() + var/datum/alarm/existing = alarms_assoc[origin] + if(!existing) + return + + return existing.max_severity() + +/atom/proc/get_alarm_origin() + return src + +/turf/get_alarm_origin() + return get_area(src) + +/datum/alarm_handler/proc/register_alarm(var/object, var/procName) + listeners[object] = procName + +/datum/alarm_handler/proc/unregister_alarm(var/object) + listeners -= object + +/datum/alarm_handler/proc/notify_listeners(var/alarm, var/was_raised) + for(var/listener in listeners) + call(listener, listeners[listener])(src, alarm, was_raised) + +/datum/alarm_handler/proc/visible_alarms(var/z) + if(!LAZYLEN(alarms)) + return list() + + var/list/map_levels = using_map.get_map_levels(z) + + var/list/visible_alarms = new() + for(var/datum/alarm/A in alarms) + if(A.hidden || (z && !(A.origin?.z in map_levels))) + continue + visible_alarms.Add(A) return visible_alarms \ No newline at end of file diff --git a/code/modules/alarm/atmosphere_alarm.dm b/code/modules/alarm/atmosphere_alarm.dm index be97493afd4..ecb1b1bafad 100644 --- a/code/modules/alarm/atmosphere_alarm.dm +++ b/code/modules/alarm/atmosphere_alarm.dm @@ -1,29 +1,29 @@ -/datum/alarm_handler/atmosphere - category = "Atmosphere Alarms" - -/datum/alarm_handler/atmosphere/major_alarms(var/z) - var/list/major_alarms = new() - var/list/map_levels = using_map.get_map_levels(z) - for(var/datum/alarm/A in visible_alarms()) - if(z && !(A.origin?.z in map_levels)) - continue - if(A.max_severity() > 1) - major_alarms.Add(A) - return major_alarms - -/datum/alarm_handler/atmosphere/minor_alarms(var/z) - var/list/minor_alarms = new() - var/list/map_levels = using_map.get_map_levels(z) - for(var/datum/alarm/A in visible_alarms()) - if(z && !(A.origin?.z in map_levels)) - continue - if(A.max_severity() == 1) - minor_alarms.Add(A) - return minor_alarms - -//VOREStation Add - Alarm for AR glasses -/*/datum/alarm_handler/atmosphere/on_alarm_change(var/datum/alarm/alarm, var/was_raised) - ..() - var/atom/source = length(alarm.sources_assoc) ? alarm.sources_assoc[1] : alarm.alarm_area() - broadcast_engineering_hud_message("Alarm in [alarm.origin] [was_raised ? "raised!" : "cleared."]", source)*/ +/datum/alarm_handler/atmosphere + category = "Atmosphere Alarms" + +/datum/alarm_handler/atmosphere/major_alarms(var/z) + var/list/major_alarms = new() + var/list/map_levels = using_map.get_map_levels(z) + for(var/datum/alarm/A in visible_alarms()) + if(z && !(A.origin?.z in map_levels)) + continue + if(A.max_severity() > 1) + major_alarms.Add(A) + return major_alarms + +/datum/alarm_handler/atmosphere/minor_alarms(var/z) + var/list/minor_alarms = new() + var/list/map_levels = using_map.get_map_levels(z) + for(var/datum/alarm/A in visible_alarms()) + if(z && !(A.origin?.z in map_levels)) + continue + if(A.max_severity() == 1) + minor_alarms.Add(A) + return minor_alarms + +//VOREStation Add - Alarm for AR glasses +/*/datum/alarm_handler/atmosphere/on_alarm_change(var/datum/alarm/alarm, var/was_raised) + ..() + var/atom/source = length(alarm.sources_assoc) ? alarm.sources_assoc[1] : alarm.alarm_area() + broadcast_engineering_hud_message("Alarm in [alarm.origin] [was_raised ? "raised!" : "cleared."]", source)*/ //VOREStation Add End \ No newline at end of file diff --git a/code/modules/alarm/camera_alarm.dm b/code/modules/alarm/camera_alarm.dm index 9594a1c8a08..bef53ad466f 100644 --- a/code/modules/alarm/camera_alarm.dm +++ b/code/modules/alarm/camera_alarm.dm @@ -1,2 +1,2 @@ -/datum/alarm_handler/camera - category = "Camera Alarms" +/datum/alarm_handler/camera + category = "Camera Alarms" diff --git a/code/modules/alarm/fire_alarm.dm b/code/modules/alarm/fire_alarm.dm index 4794d62b5ab..03cdadde043 100644 --- a/code/modules/alarm/fire_alarm.dm +++ b/code/modules/alarm/fire_alarm.dm @@ -1,15 +1,15 @@ -/datum/alarm_handler/fire - category = "Fire Alarms" - -/datum/alarm_handler/fire/on_alarm_change(var/datum/alarm/alarm, var/was_raised) - var/area/A = alarm.origin - if(istype(A)) - if(was_raised) - A.fire_alert() - else - A.fire_reset() - //VOREStation Add - Alarm for AR glasses uses - /*var/atom/source = length(alarm.sources_assoc) ? alarm.sources_assoc[1] : alarm.alarm_area() - broadcast_engineering_hud_message("Alarm in [alarm.origin] [was_raised ? "raised!" : "cleared."]", source)*/ - //VOREStation Add End - ..() +/datum/alarm_handler/fire + category = "Fire Alarms" + +/datum/alarm_handler/fire/on_alarm_change(var/datum/alarm/alarm, var/was_raised) + var/area/A = alarm.origin + if(istype(A)) + if(was_raised) + A.fire_alert() + else + A.fire_reset() + //VOREStation Add - Alarm for AR glasses uses + /*var/atom/source = length(alarm.sources_assoc) ? alarm.sources_assoc[1] : alarm.alarm_area() + broadcast_engineering_hud_message("Alarm in [alarm.origin] [was_raised ? "raised!" : "cleared."]", source)*/ + //VOREStation Add End + ..() diff --git a/code/modules/alarm/motion_alarm.dm b/code/modules/alarm/motion_alarm.dm index cafc7c128dc..fd7e6febe48 100644 --- a/code/modules/alarm/motion_alarm.dm +++ b/code/modules/alarm/motion_alarm.dm @@ -1,2 +1,2 @@ -/datum/alarm_handler/motion - category = "Motion Alarms" +/datum/alarm_handler/motion + category = "Motion Alarms" diff --git a/code/modules/alarm/power_alarm.dm b/code/modules/alarm/power_alarm.dm index 2df6d1eab3f..4a0947a8f94 100644 --- a/code/modules/alarm/power_alarm.dm +++ b/code/modules/alarm/power_alarm.dm @@ -1,10 +1,10 @@ -/datum/alarm_handler/power - category = "Power Alarms" - -/datum/alarm_handler/power/on_alarm_change(var/datum/alarm/alarm, var/was_raised) - var/area/A = alarm.origin - if(istype(A)) - A.power_alert(was_raised) - ..() - -/area/proc/power_alert(var/alarming) +/datum/alarm_handler/power + category = "Power Alarms" + +/datum/alarm_handler/power/on_alarm_change(var/datum/alarm/alarm, var/was_raised) + var/area/A = alarm.origin + if(istype(A)) + A.power_alert(was_raised) + ..() + +/area/proc/power_alert(var/alarming) diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index 5ad969bda09..5c1ea9dd9d6 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -1,108 +1,108 @@ -/obj/item/device/assembly - name = "assembly" - desc = "A small electronic device that should never exist." - icon = 'icons/obj/assemblies/new_assemblies.dmi' - icon_state = "" - w_class = ITEMSIZE_SMALL - matter = list(MAT_STEEL = 100) - throwforce = 2 - throw_speed = 3 - throw_range = 10 - drop_sound = 'sound/items/drop/component.ogg' - pickup_sound = 'sound/items/pickup/component.ogg' - origin_tech = list(TECH_MAGNET = 1) - - var/secured = 1 - var/list/attached_overlays = null - var/obj/item/device/assembly_holder/holder = null - var/cooldown = FALSE //To prevent spam - var/wires = WIRE_RECEIVE | WIRE_PULSE - - var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate() - var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder - var/const/WIRE_PULSE_SPECIAL = 4 //Allows Pulse(0) to act on the holders special assembly - var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate() - var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message - -/obj/item/device/assembly/proc/holder_movement() - return - -/obj/item/device/assembly/proc/process_cooldown() - if(cooldown) - return FALSE - cooldown = TRUE - VARSET_IN(src, cooldown, FALSE, 2 SECONDS) - return TRUE - -/obj/item/device/assembly/proc/pulsed(var/radio = 0) - if(holder && (wires & WIRE_RECEIVE)) - activate() - if(radio && (wires & WIRE_RADIO_RECEIVE)) - activate() - return 1 - -/obj/item/device/assembly/proc/pulse(var/radio = 0) - if(holder && (wires & WIRE_PULSE)) - holder.process_activation(src, 1, 0) - if(holder && (wires & WIRE_PULSE_SPECIAL)) - holder.process_activation(src, 0, 1) - return 1 - -/obj/item/device/assembly/proc/activate() - if(!secured || !process_cooldown()) - return FALSE - return TRUE - -/obj/item/device/assembly/proc/toggle_secure() - secured = !secured - update_icon() - return secured - -/obj/item/device/assembly/proc/attach_assembly(var/obj/item/device/assembly/A, var/mob/user) - holder = new/obj/item/device/assembly_holder(get_turf(src)) - if(holder.attach(A,src,user)) - to_chat(user, "You attach \the [A] to \the [src]!") - return TRUE - -/obj/item/device/assembly/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(isassembly(W)) - var/obj/item/device/assembly/A = W - if((!A.secured) && (!secured)) - attach_assembly(A,user) - return - if(W.has_tool_quality(TOOL_SCREWDRIVER)) - if(toggle_secure()) - to_chat(user, "\The [src] is ready!") - else - to_chat(user, "\The [src] can now be attached!") - return - return ..() - -/obj/item/device/assembly/process() - return PROCESS_KILL - -/obj/item/device/assembly/examine(mob/user) - . = ..() - if((in_range(src, user) || loc == user)) - if(secured) - . += "\The [src] is ready!" - else - . += "\The [src] can be attached!" - -/obj/item/device/assembly/attack_self(mob/user as mob) - if(!user) - return 0 - user.set_machine(src) - tgui_interact(user) - return 1 - -/obj/item/device/assembly/tgui_state(mob/user) - return GLOB.tgui_deep_inventory_state - -/obj/item/device/assembly/tgui_interact(mob/user, datum/tgui/ui) - return // tgui goes here - -/obj/item/device/assembly/tgui_host() - if(istype(loc, /obj/item/device/assembly_holder)) - return loc.tgui_host() - return ..() +/obj/item/device/assembly + name = "assembly" + desc = "A small electronic device that should never exist." + icon = 'icons/obj/assemblies/new_assemblies.dmi' + icon_state = "" + w_class = ITEMSIZE_SMALL + matter = list(MAT_STEEL = 100) + throwforce = 2 + throw_speed = 3 + throw_range = 10 + drop_sound = 'sound/items/drop/component.ogg' + pickup_sound = 'sound/items/pickup/component.ogg' + origin_tech = list(TECH_MAGNET = 1) + + var/secured = 1 + var/list/attached_overlays = null + var/obj/item/device/assembly_holder/holder = null + var/cooldown = FALSE //To prevent spam + var/wires = WIRE_RECEIVE | WIRE_PULSE + + var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate() + var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder + var/const/WIRE_PULSE_SPECIAL = 4 //Allows Pulse(0) to act on the holders special assembly + var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate() + var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message + +/obj/item/device/assembly/proc/holder_movement() + return + +/obj/item/device/assembly/proc/process_cooldown() + if(cooldown) + return FALSE + cooldown = TRUE + VARSET_IN(src, cooldown, FALSE, 2 SECONDS) + return TRUE + +/obj/item/device/assembly/proc/pulsed(var/radio = 0) + if(holder && (wires & WIRE_RECEIVE)) + activate() + if(radio && (wires & WIRE_RADIO_RECEIVE)) + activate() + return 1 + +/obj/item/device/assembly/proc/pulse(var/radio = 0) + if(holder && (wires & WIRE_PULSE)) + holder.process_activation(src, 1, 0) + if(holder && (wires & WIRE_PULSE_SPECIAL)) + holder.process_activation(src, 0, 1) + return 1 + +/obj/item/device/assembly/proc/activate() + if(!secured || !process_cooldown()) + return FALSE + return TRUE + +/obj/item/device/assembly/proc/toggle_secure() + secured = !secured + update_icon() + return secured + +/obj/item/device/assembly/proc/attach_assembly(var/obj/item/device/assembly/A, var/mob/user) + holder = new/obj/item/device/assembly_holder(get_turf(src)) + if(holder.attach(A,src,user)) + to_chat(user, "You attach \the [A] to \the [src]!") + return TRUE + +/obj/item/device/assembly/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(isassembly(W)) + var/obj/item/device/assembly/A = W + if((!A.secured) && (!secured)) + attach_assembly(A,user) + return + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(toggle_secure()) + to_chat(user, "\The [src] is ready!") + else + to_chat(user, "\The [src] can now be attached!") + return + return ..() + +/obj/item/device/assembly/process() + return PROCESS_KILL + +/obj/item/device/assembly/examine(mob/user) + . = ..() + if((in_range(src, user) || loc == user)) + if(secured) + . += "\The [src] is ready!" + else + . += "\The [src] can be attached!" + +/obj/item/device/assembly/attack_self(mob/user as mob) + if(!user) + return 0 + user.set_machine(src) + tgui_interact(user) + return 1 + +/obj/item/device/assembly/tgui_state(mob/user) + return GLOB.tgui_deep_inventory_state + +/obj/item/device/assembly/tgui_interact(mob/user, datum/tgui/ui) + return // tgui goes here + +/obj/item/device/assembly/tgui_host() + if(istype(loc, /obj/item/device/assembly_holder)) + return loc.tgui_host() + return ..() diff --git a/code/modules/assembly/helpers.dm b/code/modules/assembly/helpers.dm index cc3100f7727..44164fb72b2 100644 --- a/code/modules/assembly/helpers.dm +++ b/code/modules/assembly/helpers.dm @@ -1,29 +1,29 @@ -/proc/isassembly(O) - if(istype(O, /obj/item/device/assembly)) - return 1 - return 0 - -/proc/isigniter(O) - if(istype(O, /obj/item/device/assembly/igniter)) - return 1 - return 0 - -/proc/isinfared(O) - if(istype(O, /obj/item/device/assembly/infra)) - return 1 - return 0 - -/proc/isprox(O) - if(istype(O, /obj/item/device/assembly/prox_sensor)) - return 1 - return 0 - -/proc/issignaler(O) - if(istype(O, /obj/item/device/assembly/signaler)) - return 1 - return 0 - -/proc/istimer(O) - if(istype(O, /obj/item/device/assembly/timer)) - return 1 - return 0 +/proc/isassembly(O) + if(istype(O, /obj/item/device/assembly)) + return 1 + return 0 + +/proc/isigniter(O) + if(istype(O, /obj/item/device/assembly/igniter)) + return 1 + return 0 + +/proc/isinfared(O) + if(istype(O, /obj/item/device/assembly/infra)) + return 1 + return 0 + +/proc/isprox(O) + if(istype(O, /obj/item/device/assembly/prox_sensor)) + return 1 + return 0 + +/proc/issignaler(O) + if(istype(O, /obj/item/device/assembly/signaler)) + return 1 + return 0 + +/proc/istimer(O) + if(istype(O, /obj/item/device/assembly/timer)) + return 1 + return 0 diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 52ab8e67cb4..110a9cee35f 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -1,225 +1,225 @@ -/obj/item/device/assembly_holder - name = "Assembly" - icon = 'icons/obj/assemblies/new_assemblies.dmi' - icon_state = "holder" - item_state = "assembly" - throwforce = 5 - w_class = ITEMSIZE_SMALL - throw_speed = 3 - throw_range = 10 - - var/secured = 0 - var/obj/item/device/assembly/a_left = null - var/obj/item/device/assembly/a_right = null - var/obj/special_assembly = null - -/obj/item/device/assembly_holder/proc/attach(var/obj/item/device/assembly/D, var/obj/item/device/assembly/D2, var/mob/user) - if(!D || !D2) - return FALSE - - if(!istype(D) || !istype(D2)) - return FALSE - - if(D.secured || D2.secured) - return FALSE - - if(user) - user.remove_from_mob(D) - user.remove_from_mob(D2) - - D.holder = src - D2.holder = src - D.forceMove(src) - D2.forceMove(src) - a_left = D - a_right = D2 - name = "[D.name]-[D2.name] assembly" - update_icon() - user.put_in_hands(src) - - return TRUE - -/obj/item/device/assembly_holder/proc/detached() - return - -/obj/item/device/assembly_holder/update_icon() - cut_overlays() - if(a_left) - add_overlay("[a_left.icon_state]_left") - for(var/O in a_left.attached_overlays) - add_overlay("[O]_l") - if(a_right) - add_overlay("[a_right.icon_state]_right") - for(var/O in a_right.attached_overlays) - add_overlay("[O]_r") - if(master) - master.update_icon() - -/obj/item/device/assembly_holder/examine(mob/user) - . = ..() - if ((in_range(src, user) || src.loc == user)) - if (src.secured) - . += "\The [src] is ready!" - else - . += "\The [src] can be attached!" - -/obj/item/device/assembly_holder/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(isturf(old_loc)) - unsense_proximity(callback = /atom/proc/HasProximity, center = old_loc) - if(isturf(loc)) - sense_proximity(callback = /atom/proc/HasProximity) - -/obj/item/device/assembly_holder/HasProximity(turf/T, atom/movable/AM, old_loc) - if(a_left) - a_left.HasProximity(T, AM, old_loc) - if(a_right) - a_right.HasProximity(T, AM, old_loc) - -/obj/item/device/assembly_holder/Crossed(atom/movable/AM as mob|obj) - if(AM.is_incorporeal()) - return - if(a_left) - a_left.Crossed(AM) - if(a_right) - a_right.Crossed(AM) - -/obj/item/device/assembly_holder/on_found(mob/finder as mob) - if(a_left) - a_left.on_found(finder) - if(a_right) - a_right.on_found(finder) - -/obj/item/device/assembly_holder/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(a_left && a_right) - a_left.holder_movement() - a_right.holder_movement() - -/obj/item/device/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess - if(a_left && a_right) - a_left.holder_movement() - a_right.holder_movement() - ..() - -/obj/item/device/assembly_holder/attackby(var/obj/item/weapon/W, var/mob/user) - if(W.has_tool_quality(TOOL_SCREWDRIVER)) - if(!a_left || !a_right) - to_chat(user, " BUG:Assembly part missing, please report this!") - return - a_left.toggle_secure() - a_right.toggle_secure() - secured = !secured - if(secured) - to_chat(user, "\The [src] is ready!") - else - to_chat(user, "\The [src] can now be taken apart!") - update_icon() - return - else - ..() - -/obj/item/device/assembly_holder/attack_self(var/mob/user) - src.add_fingerprint(user) - if(src.secured) - if(!a_left || !a_right) - to_chat(user, " BUG:Assembly part missing, please report this!") - return - if(istype(a_left,a_right.type))//If they are the same type it causes issues due to window code - switch(tgui_alert(usr, "Which side would you like to use?","Side",list("Left","Right"))) - if("Left") a_left.attack_self(user) - if("Right") a_right.attack_self(user) - return - else - if(!istype(a_left,/obj/item/device/assembly/igniter)) - a_left.attack_self(user) - if(!istype(a_right,/obj/item/device/assembly/igniter)) - a_right.attack_self(user) - else - var/turf/T = get_turf(src) - if(!T) - return 0 - if(a_left) - a_left.holder = null - a_left.forceMove(T) - if(a_right) - a_right.holder = null - a_right.forceMove(T) - qdel(src) - -/obj/item/device/assembly_holder/proc/process_activation(var/obj/D, var/normal = 1) - if(!D) - return 0 - if(!secured) - visible_message("\icon[src][bicon(src)] *beep* *beep*", "*beep* *beep*") - if((normal) && (a_right) && (a_left)) - if(a_right != D) - a_right.pulsed(0) - if(a_left != D) - a_left.pulsed(0) - if(master) - master.receive_signal() - return 1 - -/obj/item/device/assembly_holder/hear_talk(mob/M, list/message_pieces, verb) - if(a_right) - a_right.hear_talk(M, message_pieces, verb) - if(a_left) - a_left.hear_talk(M, message_pieces, verb) - -/obj/item/device/assembly_holder/timer_igniter - name = "timer-igniter assembly" - -/obj/item/device/assembly_holder/timer_igniter/New() - ..() - - var/obj/item/device/assembly/igniter/ign = new(src) - ign.secured = 1 - ign.holder = src - - var/obj/item/device/assembly/timer/tmr = new(src) - tmr.time = 5 - tmr.secured = 1 - tmr.holder = src - - a_left = tmr - a_right = ign - secured = 1 - update_icon() - name = initial(name) + " ([tmr.time] secs)" - - loc.verbs += /obj/item/device/assembly_holder/timer_igniter/verb/configure - -/obj/item/device/assembly_holder/timer_igniter/detached() - loc.verbs -= /obj/item/device/assembly_holder/timer_igniter/verb/configure - ..() - -/obj/item/device/assembly_holder/timer_igniter/verb/configure() - set name = "Set Timer" - set category = "Object" - set src in usr - - if ( !(usr.stat || usr.restrained()) ) - var/obj/item/device/assembly_holder/holder - if(istype(src,/obj/item/weapon/grenade/chem_grenade)) - var/obj/item/weapon/grenade/chem_grenade/gren = src - holder=gren.detonator - var/obj/item/device/assembly/timer/tmr = holder.a_left - if(!istype(tmr,/obj/item/device/assembly/timer)) - tmr = holder.a_right - if(!istype(tmr,/obj/item/device/assembly/timer)) - to_chat(usr, "This detonator has no timer.") - return - - if(tmr.timing) - to_chat(usr, "Clock is ticking already.") - else - var/ntime = tgui_input_number(usr, "Enter desired time in seconds", "Time", "5", 1000, 0) - if (ntime>0 && ntime<1000) - tmr.time = ntime - name = initial(name) + "([tmr.time] secs)" - to_chat(usr, "Timer set to [tmr.time] seconds.") - else - to_chat(usr, "Timer can't be [ntime<=0?"negative":"more than 1000 seconds"].") - else - to_chat(usr, "You cannot do this while [usr.stat?"unconscious/dead":"restrained"].") +/obj/item/device/assembly_holder + name = "Assembly" + icon = 'icons/obj/assemblies/new_assemblies.dmi' + icon_state = "holder" + item_state = "assembly" + throwforce = 5 + w_class = ITEMSIZE_SMALL + throw_speed = 3 + throw_range = 10 + + var/secured = 0 + var/obj/item/device/assembly/a_left = null + var/obj/item/device/assembly/a_right = null + var/obj/special_assembly = null + +/obj/item/device/assembly_holder/proc/attach(var/obj/item/device/assembly/D, var/obj/item/device/assembly/D2, var/mob/user) + if(!D || !D2) + return FALSE + + if(!istype(D) || !istype(D2)) + return FALSE + + if(D.secured || D2.secured) + return FALSE + + if(user) + user.remove_from_mob(D) + user.remove_from_mob(D2) + + D.holder = src + D2.holder = src + D.forceMove(src) + D2.forceMove(src) + a_left = D + a_right = D2 + name = "[D.name]-[D2.name] assembly" + update_icon() + user.put_in_hands(src) + + return TRUE + +/obj/item/device/assembly_holder/proc/detached() + return + +/obj/item/device/assembly_holder/update_icon() + cut_overlays() + if(a_left) + add_overlay("[a_left.icon_state]_left") + for(var/O in a_left.attached_overlays) + add_overlay("[O]_l") + if(a_right) + add_overlay("[a_right.icon_state]_right") + for(var/O in a_right.attached_overlays) + add_overlay("[O]_r") + if(master) + master.update_icon() + +/obj/item/device/assembly_holder/examine(mob/user) + . = ..() + if ((in_range(src, user) || src.loc == user)) + if (src.secured) + . += "\The [src] is ready!" + else + . += "\The [src] can be attached!" + +/obj/item/device/assembly_holder/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(isturf(old_loc)) + unsense_proximity(callback = /atom/proc/HasProximity, center = old_loc) + if(isturf(loc)) + sense_proximity(callback = /atom/proc/HasProximity) + +/obj/item/device/assembly_holder/HasProximity(turf/T, atom/movable/AM, old_loc) + if(a_left) + a_left.HasProximity(T, AM, old_loc) + if(a_right) + a_right.HasProximity(T, AM, old_loc) + +/obj/item/device/assembly_holder/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return + if(a_left) + a_left.Crossed(AM) + if(a_right) + a_right.Crossed(AM) + +/obj/item/device/assembly_holder/on_found(mob/finder as mob) + if(a_left) + a_left.on_found(finder) + if(a_right) + a_right.on_found(finder) + +/obj/item/device/assembly_holder/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(a_left && a_right) + a_left.holder_movement() + a_right.holder_movement() + +/obj/item/device/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess + if(a_left && a_right) + a_left.holder_movement() + a_right.holder_movement() + ..() + +/obj/item/device/assembly_holder/attackby(var/obj/item/weapon/W, var/mob/user) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(!a_left || !a_right) + to_chat(user, " BUG:Assembly part missing, please report this!") + return + a_left.toggle_secure() + a_right.toggle_secure() + secured = !secured + if(secured) + to_chat(user, "\The [src] is ready!") + else + to_chat(user, "\The [src] can now be taken apart!") + update_icon() + return + else + ..() + +/obj/item/device/assembly_holder/attack_self(var/mob/user) + src.add_fingerprint(user) + if(src.secured) + if(!a_left || !a_right) + to_chat(user, " BUG:Assembly part missing, please report this!") + return + if(istype(a_left,a_right.type))//If they are the same type it causes issues due to window code + switch(tgui_alert(usr, "Which side would you like to use?","Side",list("Left","Right"))) + if("Left") a_left.attack_self(user) + if("Right") a_right.attack_self(user) + return + else + if(!istype(a_left,/obj/item/device/assembly/igniter)) + a_left.attack_self(user) + if(!istype(a_right,/obj/item/device/assembly/igniter)) + a_right.attack_self(user) + else + var/turf/T = get_turf(src) + if(!T) + return 0 + if(a_left) + a_left.holder = null + a_left.forceMove(T) + if(a_right) + a_right.holder = null + a_right.forceMove(T) + qdel(src) + +/obj/item/device/assembly_holder/proc/process_activation(var/obj/D, var/normal = 1) + if(!D) + return 0 + if(!secured) + visible_message("\icon[src][bicon(src)] *beep* *beep*", "*beep* *beep*") + if((normal) && (a_right) && (a_left)) + if(a_right != D) + a_right.pulsed(0) + if(a_left != D) + a_left.pulsed(0) + if(master) + master.receive_signal() + return 1 + +/obj/item/device/assembly_holder/hear_talk(mob/M, list/message_pieces, verb) + if(a_right) + a_right.hear_talk(M, message_pieces, verb) + if(a_left) + a_left.hear_talk(M, message_pieces, verb) + +/obj/item/device/assembly_holder/timer_igniter + name = "timer-igniter assembly" + +/obj/item/device/assembly_holder/timer_igniter/New() + ..() + + var/obj/item/device/assembly/igniter/ign = new(src) + ign.secured = 1 + ign.holder = src + + var/obj/item/device/assembly/timer/tmr = new(src) + tmr.time = 5 + tmr.secured = 1 + tmr.holder = src + + a_left = tmr + a_right = ign + secured = 1 + update_icon() + name = initial(name) + " ([tmr.time] secs)" + + loc.verbs += /obj/item/device/assembly_holder/timer_igniter/verb/configure + +/obj/item/device/assembly_holder/timer_igniter/detached() + loc.verbs -= /obj/item/device/assembly_holder/timer_igniter/verb/configure + ..() + +/obj/item/device/assembly_holder/timer_igniter/verb/configure() + set name = "Set Timer" + set category = "Object" + set src in usr + + if ( !(usr.stat || usr.restrained()) ) + var/obj/item/device/assembly_holder/holder + if(istype(src,/obj/item/weapon/grenade/chem_grenade)) + var/obj/item/weapon/grenade/chem_grenade/gren = src + holder=gren.detonator + var/obj/item/device/assembly/timer/tmr = holder.a_left + if(!istype(tmr,/obj/item/device/assembly/timer)) + tmr = holder.a_right + if(!istype(tmr,/obj/item/device/assembly/timer)) + to_chat(usr, "This detonator has no timer.") + return + + if(tmr.timing) + to_chat(usr, "Clock is ticking already.") + else + var/ntime = tgui_input_number(usr, "Enter desired time in seconds", "Time", "5", 1000, 0) + if (ntime>0 && ntime<1000) + tmr.time = ntime + name = initial(name) + "([tmr.time] secs)" + to_chat(usr, "Timer set to [tmr.time] seconds.") + else + to_chat(usr, "Timer can't be [ntime<=0?"negative":"more than 1000 seconds"].") + else + to_chat(usr, "You cannot do this while [usr.stat?"unconscious/dead":"restrained"].") diff --git a/code/modules/assembly/igniter.dm b/code/modules/assembly/igniter.dm index 8b45b840847..0827836c355 100644 --- a/code/modules/assembly/igniter.dm +++ b/code/modules/assembly/igniter.dm @@ -1,40 +1,40 @@ -/obj/item/device/assembly/igniter - name = "igniter" - desc = "A small electronic device able to ignite combustable substances." - icon_state = "igniter" - origin_tech = list(TECH_MAGNET = 1) - matter = list(MAT_STEEL = 500, MAT_GLASS = 50) - - secured = 1 - wires = WIRE_RECEIVE - -/obj/item/device/assembly/igniter/activate() - if(!..()) - return FALSE - - if(holder && istype(holder.loc,/obj/item/weapon/grenade/chem_grenade)) - var/obj/item/weapon/grenade/chem_grenade/grenade = holder.loc - grenade.detonate() - else - var/turf/location = get_turf(loc) - if(location) - location.hotspot_expose(1000,1000) - if (istype(src.loc,/obj/item/device/assembly_holder)) - if (istype(src.loc.loc, /obj/structure/reagent_dispensers/fueltank/)) - var/obj/structure/reagent_dispensers/fueltank/tank = src.loc.loc - if (tank && tank.modded) - tank.explode() - - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(3, 1, src) - s.start() - - return TRUE - - -/obj/item/device/assembly/igniter/attack_self(var/mob/user) - activate() - add_fingerprint(user) - -/obj/item/device/assembly/igniter/is_hot() +/obj/item/device/assembly/igniter + name = "igniter" + desc = "A small electronic device able to ignite combustable substances." + icon_state = "igniter" + origin_tech = list(TECH_MAGNET = 1) + matter = list(MAT_STEEL = 500, MAT_GLASS = 50) + + secured = 1 + wires = WIRE_RECEIVE + +/obj/item/device/assembly/igniter/activate() + if(!..()) + return FALSE + + if(holder && istype(holder.loc,/obj/item/weapon/grenade/chem_grenade)) + var/obj/item/weapon/grenade/chem_grenade/grenade = holder.loc + grenade.detonate() + else + var/turf/location = get_turf(loc) + if(location) + location.hotspot_expose(1000,1000) + if (istype(src.loc,/obj/item/device/assembly_holder)) + if (istype(src.loc.loc, /obj/structure/reagent_dispensers/fueltank/)) + var/obj/structure/reagent_dispensers/fueltank/tank = src.loc.loc + if (tank && tank.modded) + tank.explode() + + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + + return TRUE + + +/obj/item/device/assembly/igniter/attack_self(var/mob/user) + activate() + add_fingerprint(user) + +/obj/item/device/assembly/igniter/is_hot() return TRUE \ No newline at end of file diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index c4c4e09e199..fb5566504ef 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -1,181 +1,181 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/item/device/assembly/infra - name = "infrared emitter" - desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted." - icon_state = "infrared" - origin_tech = list(TECH_MAGNET = 2) - matter = list(MAT_STEEL = 1000, MAT_GLASS = 500) - - wires = WIRE_PULSE - - secured = 0 - - var/on = 0 - var/visible = 0 - var/list/i_beams = null - -/obj/item/device/assembly/infra/activate() - if(!..()) - return FALSE - on = !on - update_icon() - return TRUE - -/obj/item/device/assembly/infra/toggle_secure() - secured = !secured - if(!secured) - toggle_state(FALSE) - update_icon() - return secured - -/obj/item/device/assembly/infra/proc/toggle_state(var/picked) - if(!isnull(picked)) - on = picked - else - on = !on - - if(secured && on) - START_PROCESSING(SSobj, src) - else - STOP_PROCESSING(SSobj, src) - QDEL_LIST_NULL(i_beams) - return on - -/obj/item/device/assembly/infra/update_icon() - cut_overlays() - LAZYCLEARLIST(attached_overlays) - if(on) - add_overlay("infrared_on") - LAZYADD(attached_overlays, "infrared_on") - - if(holder) - holder.update_icon(2) - -/obj/item/device/assembly/infra/process() - if(!on && i_beams) - QDEL_LIST_NULL(i_beams) - return - - if(!i_beams && secured && (istype(loc, /turf) || (holder && istype(holder.loc, /turf)))) - create_beams() - -/obj/item/device/assembly/infra/proc/create_beams(var/limit = 8) - var/current_spot = get_turf(src) - for(var/i = 1 to limit) - var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(current_spot) - I.master = src - I.density = TRUE - I.set_dir(dir) - if(!step(I, I.dir)) //Try to take a step in that direction - return //Couldn't, oh well, we hit a wall or something. Beam should qdel itself in it's Bump(). - I.density = FALSE - i_beams |= I - I.visible = visible - -/obj/item/device/assembly/infra/attack_hand() - QDEL_LIST_NULL(i_beams) - ..() - -/obj/item/device/assembly/infra/Move() - var/t = dir - . = ..() - set_dir(t) - -/obj/item/device/assembly/infra/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - QDEL_LIST_NULL(i_beams) - -/obj/item/device/assembly/infra/holder_movement() - if(!holder) - return FALSE - QDEL_LIST_NULL(i_beams) - return TRUE - -/obj/item/device/assembly/infra/proc/trigger_beam() - if(!process_cooldown()) - return FALSE - pulse(0) - QDEL_LIST_NULL(i_beams) //They will get recreated next process() if the situation is still appropriate - if(!holder) - visible_message("\icon[src][bicon(src)] *beep* *beep*") - -/obj/item/device/assembly/infra/tgui_interact(mob/user, datum/tgui/ui) - if(!secured) - to_chat(user, "[src] is unsecured!") - return FALSE - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AssemblyInfrared", name) - ui.open() - -/obj/item/device/assembly/infra/tgui_data(mob/user) - var/list/data = ..() - - data["on"] = on - data["visible"] = visible - - return data - -/obj/item/device/assembly/infra/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - switch(action) - if("state") - toggle_state() - return TRUE - if("visible") - visible = !visible - for(var/obj/effect/beam/i_beam/I as anything in i_beams) - I.visible = visible - CHECK_TICK - return TRUE - -/obj/item/device/assembly/infra/verb/rotate_clockwise() - set name = "Rotate Infrared Laser Clockwise" - set category = "Object" - set src in usr - - set_dir(turn(dir, 270)) - -/***************************IBeam*********************************/ - -/obj/effect/beam/i_beam - name = "i beam" - icon = 'icons/obj/projectiles.dmi' - icon_state = "ibeam" - var/obj/item/device/assembly/infra/master = null - var/visible = 0 - anchored = TRUE - -/obj/effect/beam/i_beam/Initialize() - . = ..() - START_PROCESSING(SSobj, src) - -/obj/effect/beam/i_beam/Destroy() - STOP_PROCESSING(SSobj, src) - master = null - return ..() - -/obj/effect/beam/i_beam/proc/hit() - master?.trigger_beam() - qdel(src) - -/obj/effect/beam/i_beam/process() - if(loc?.density || !master) - qdel(src) - return - -/obj/effect/beam/i_beam/Bump() - qdel(src) - -/obj/effect/beam/i_beam/Bumped() - hit() - -/obj/effect/beam/i_beam/Crossed(var/atom/movable/AM) - if(AM.is_incorporeal()) - return - if(istype(AM, /obj/effect/beam)) - return +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/obj/item/device/assembly/infra + name = "infrared emitter" + desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted." + icon_state = "infrared" + origin_tech = list(TECH_MAGNET = 2) + matter = list(MAT_STEEL = 1000, MAT_GLASS = 500) + + wires = WIRE_PULSE + + secured = 0 + + var/on = 0 + var/visible = 0 + var/list/i_beams = null + +/obj/item/device/assembly/infra/activate() + if(!..()) + return FALSE + on = !on + update_icon() + return TRUE + +/obj/item/device/assembly/infra/toggle_secure() + secured = !secured + if(!secured) + toggle_state(FALSE) + update_icon() + return secured + +/obj/item/device/assembly/infra/proc/toggle_state(var/picked) + if(!isnull(picked)) + on = picked + else + on = !on + + if(secured && on) + START_PROCESSING(SSobj, src) + else + STOP_PROCESSING(SSobj, src) + QDEL_LIST_NULL(i_beams) + return on + +/obj/item/device/assembly/infra/update_icon() + cut_overlays() + LAZYCLEARLIST(attached_overlays) + if(on) + add_overlay("infrared_on") + LAZYADD(attached_overlays, "infrared_on") + + if(holder) + holder.update_icon(2) + +/obj/item/device/assembly/infra/process() + if(!on && i_beams) + QDEL_LIST_NULL(i_beams) + return + + if(!i_beams && secured && (istype(loc, /turf) || (holder && istype(holder.loc, /turf)))) + create_beams() + +/obj/item/device/assembly/infra/proc/create_beams(var/limit = 8) + var/current_spot = get_turf(src) + for(var/i = 1 to limit) + var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(current_spot) + I.master = src + I.density = TRUE + I.set_dir(dir) + if(!step(I, I.dir)) //Try to take a step in that direction + return //Couldn't, oh well, we hit a wall or something. Beam should qdel itself in it's Bump(). + I.density = FALSE + i_beams |= I + I.visible = visible + +/obj/item/device/assembly/infra/attack_hand() + QDEL_LIST_NULL(i_beams) + ..() + +/obj/item/device/assembly/infra/Move() + var/t = dir + . = ..() + set_dir(t) + +/obj/item/device/assembly/infra/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + QDEL_LIST_NULL(i_beams) + +/obj/item/device/assembly/infra/holder_movement() + if(!holder) + return FALSE + QDEL_LIST_NULL(i_beams) + return TRUE + +/obj/item/device/assembly/infra/proc/trigger_beam() + if(!process_cooldown()) + return FALSE + pulse(0) + QDEL_LIST_NULL(i_beams) //They will get recreated next process() if the situation is still appropriate + if(!holder) + visible_message("\icon[src][bicon(src)] *beep* *beep*") + +/obj/item/device/assembly/infra/tgui_interact(mob/user, datum/tgui/ui) + if(!secured) + to_chat(user, "[src] is unsecured!") + return FALSE + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AssemblyInfrared", name) + ui.open() + +/obj/item/device/assembly/infra/tgui_data(mob/user) + var/list/data = ..() + + data["on"] = on + data["visible"] = visible + + return data + +/obj/item/device/assembly/infra/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("state") + toggle_state() + return TRUE + if("visible") + visible = !visible + for(var/obj/effect/beam/i_beam/I as anything in i_beams) + I.visible = visible + CHECK_TICK + return TRUE + +/obj/item/device/assembly/infra/verb/rotate_clockwise() + set name = "Rotate Infrared Laser Clockwise" + set category = "Object" + set src in usr + + set_dir(turn(dir, 270)) + +/***************************IBeam*********************************/ + +/obj/effect/beam/i_beam + name = "i beam" + icon = 'icons/obj/projectiles.dmi' + icon_state = "ibeam" + var/obj/item/device/assembly/infra/master = null + var/visible = 0 + anchored = TRUE + +/obj/effect/beam/i_beam/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + +/obj/effect/beam/i_beam/Destroy() + STOP_PROCESSING(SSobj, src) + master = null + return ..() + +/obj/effect/beam/i_beam/proc/hit() + master?.trigger_beam() + qdel(src) + +/obj/effect/beam/i_beam/process() + if(loc?.density || !master) + qdel(src) + return + +/obj/effect/beam/i_beam/Bump() + qdel(src) + +/obj/effect/beam/i_beam/Bumped() + hit() + +/obj/effect/beam/i_beam/Crossed(var/atom/movable/AM) + if(AM.is_incorporeal()) + return + if(istype(AM, /obj/effect/beam)) + return hit() \ No newline at end of file diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm index dbd4ffc8599..de11bc20a79 100644 --- a/code/modules/assembly/mousetrap.dm +++ b/code/modules/assembly/mousetrap.dm @@ -1,123 +1,123 @@ -/obj/item/device/assembly/mousetrap - name = "mousetrap" - desc = "A handy little spring-loaded trap for catching pesty rodents." - icon_state = "mousetrap" - origin_tech = list(TECH_COMBAT = 1) - matter = list(MAT_STEEL = 100) - var/armed = 0 - - -/obj/item/device/assembly/mousetrap/examine(var/mob/user) - . = ..(user) - if(armed) - . += "It looks like it's armed." - -/obj/item/device/assembly/mousetrap/update_icon() - if(armed) - icon_state = "mousetraparmed" - else - icon_state = "mousetrap" - if(holder) - holder.update_icon() - -/obj/item/device/assembly/mousetrap/proc/triggered(var/mob/target, var/type = "feet") - if(!armed) - return - var/obj/item/organ/external/affecting = null - if(ishuman(target)) - var/mob/living/carbon/human/H = target - switch(type) - if("feet") - if(!H.shoes) - affecting = H.get_organ(pick("l_leg", "r_leg")) - H.Weaken(3) - if("l_hand", "r_hand") - if(!H.gloves) - affecting = H.get_organ(type) - H.Stun(3) - if(affecting) - if(affecting.take_damage(1, 0)) - H.UpdateDamageIcon() - H.updatehealth() - else if(ismouse(target)) - var/mob/living/simple_mob/animal/passive/mouse/M = target - visible_message(span_red("SPLAT!")) - M.splat() - playsound(target, 'sound/effects/snap.ogg', 50, 1) - layer = MOB_LAYER - 0.2 - armed = 0 - update_icon() - pulse(0) - -/obj/item/device/assembly/mousetrap/attack_self(var/mob/living/user) - if(!armed) - to_chat(user, "You arm [src].") - else - if((CLUMSY in user.mutations) && prob(50)) - var/which_hand = "l_hand" - if(!user.hand) - which_hand = "r_hand" - triggered(user, which_hand) - user.visible_message("[user] accidentally sets off [src], breaking their fingers.", \ - "You accidentally trigger [src]!") - return - - to_chat(user, "You disarm [src].") - armed = !armed - update_icon() - playsound(user, 'sound/weapons/handcuffs.ogg', 30, 1, -3) - -/obj/item/device/assembly/mousetrap/attack_hand(var/mob/living/user) - if(armed) - if((CLUMSY in user.mutations) && prob(50)) - var/which_hand = "l_hand" - if(!user.hand) - which_hand = "r_hand" - triggered(user, which_hand) - user.visible_message("[user] accidentally sets off [src], breaking their fingers.", \ - "You accidentally trigger [src]!") - return - ..() - -/obj/item/device/assembly/mousetrap/Crossed(var/atom/movable/AM) - if(AM.is_incorporeal()) - return - if(armed) - if(ishuman(AM)) - var/mob/living/carbon/H = AM - if(H.m_intent == "run") - triggered(H) - H.visible_message("[H] accidentally steps on [src].", \ - "You accidentally step on [src]") - if(ismouse(AM)) - triggered(AM) - ..() - -/obj/item/device/assembly/mousetrap/on_found(var/mob/living/finder) - if(armed) - finder.visible_message("[finder] accidentally sets off [src], breaking their fingers.", \ - "You accidentally trigger [src]!") - triggered(finder, finder.hand ? "l_hand" : "r_hand") - return 1 //end the search! - return 0 - -/obj/item/device/assembly/mousetrap/hitby(var/atom/movable/A) - if(!armed) - return ..() - visible_message("[src] is triggered by [A].") - triggered(null) - -/obj/item/device/assembly/mousetrap/armed - icon_state = "mousetraparmed" - armed = 1 - -/obj/item/device/assembly/mousetrap/verb/hide_under() - set src in oview(1) - set name = "Hide" - set category = "Object" - - if(usr.stat) - return - - layer = HIDING_LAYER - to_chat(usr, "You hide [src].") +/obj/item/device/assembly/mousetrap + name = "mousetrap" + desc = "A handy little spring-loaded trap for catching pesty rodents." + icon_state = "mousetrap" + origin_tech = list(TECH_COMBAT = 1) + matter = list(MAT_STEEL = 100) + var/armed = 0 + + +/obj/item/device/assembly/mousetrap/examine(var/mob/user) + . = ..(user) + if(armed) + . += "It looks like it's armed." + +/obj/item/device/assembly/mousetrap/update_icon() + if(armed) + icon_state = "mousetraparmed" + else + icon_state = "mousetrap" + if(holder) + holder.update_icon() + +/obj/item/device/assembly/mousetrap/proc/triggered(var/mob/target, var/type = "feet") + if(!armed) + return + var/obj/item/organ/external/affecting = null + if(ishuman(target)) + var/mob/living/carbon/human/H = target + switch(type) + if("feet") + if(!H.shoes) + affecting = H.get_organ(pick("l_leg", "r_leg")) + H.Weaken(3) + if("l_hand", "r_hand") + if(!H.gloves) + affecting = H.get_organ(type) + H.Stun(3) + if(affecting) + if(affecting.take_damage(1, 0)) + H.UpdateDamageIcon() + H.updatehealth() + else if(ismouse(target)) + var/mob/living/simple_mob/animal/passive/mouse/M = target + visible_message(span_red("SPLAT!")) + M.splat() + playsound(target, 'sound/effects/snap.ogg', 50, 1) + layer = MOB_LAYER - 0.2 + armed = 0 + update_icon() + pulse(0) + +/obj/item/device/assembly/mousetrap/attack_self(var/mob/living/user) + if(!armed) + to_chat(user, "You arm [src].") + else + if((CLUMSY in user.mutations) && prob(50)) + var/which_hand = "l_hand" + if(!user.hand) + which_hand = "r_hand" + triggered(user, which_hand) + user.visible_message("[user] accidentally sets off [src], breaking their fingers.", \ + "You accidentally trigger [src]!") + return + + to_chat(user, "You disarm [src].") + armed = !armed + update_icon() + playsound(user, 'sound/weapons/handcuffs.ogg', 30, 1, -3) + +/obj/item/device/assembly/mousetrap/attack_hand(var/mob/living/user) + if(armed) + if((CLUMSY in user.mutations) && prob(50)) + var/which_hand = "l_hand" + if(!user.hand) + which_hand = "r_hand" + triggered(user, which_hand) + user.visible_message("[user] accidentally sets off [src], breaking their fingers.", \ + "You accidentally trigger [src]!") + return + ..() + +/obj/item/device/assembly/mousetrap/Crossed(var/atom/movable/AM) + if(AM.is_incorporeal()) + return + if(armed) + if(ishuman(AM)) + var/mob/living/carbon/H = AM + if(H.m_intent == "run") + triggered(H) + H.visible_message("[H] accidentally steps on [src].", \ + "You accidentally step on [src]") + if(ismouse(AM)) + triggered(AM) + ..() + +/obj/item/device/assembly/mousetrap/on_found(var/mob/living/finder) + if(armed) + finder.visible_message("[finder] accidentally sets off [src], breaking their fingers.", \ + "You accidentally trigger [src]!") + triggered(finder, finder.hand ? "l_hand" : "r_hand") + return 1 //end the search! + return 0 + +/obj/item/device/assembly/mousetrap/hitby(var/atom/movable/A) + if(!armed) + return ..() + visible_message("[src] is triggered by [A].") + triggered(null) + +/obj/item/device/assembly/mousetrap/armed + icon_state = "mousetraparmed" + armed = 1 + +/obj/item/device/assembly/mousetrap/verb/hide_under() + set src in oview(1) + set name = "Hide" + set category = "Object" + + if(usr.stat) + return + + layer = HIDING_LAYER + to_chat(usr, "You hide [src].") diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm index 0561eb47974..7c12d5fb039 100644 --- a/code/modules/assembly/proximity.dm +++ b/code/modules/assembly/proximity.dm @@ -1,143 +1,143 @@ -/obj/item/device/assembly/prox_sensor - name = "proximity sensor" - desc = "Used for scanning and alerting when someone enters a certain proximity." - icon_state = "prox" - origin_tech = list(TECH_MAGNET = 1) - matter = list(MAT_STEEL = 800, MAT_GLASS = 200) - wires = WIRE_PULSE - - secured = 0 - - var/scanning = 0 - var/timing = 0 - var/time = 10 - - var/range = 2 - -/obj/item/device/assembly/prox_sensor/activate() - if(!..()) - return FALSE - timing = !timing - update_icon() - return FALSE - -/obj/item/device/assembly/prox_sensor/toggle_secure() - secured = !secured - if(secured) - START_PROCESSING(SSobj, src) - else - scanning = 0 - timing = 0 - STOP_PROCESSING(SSobj, src) - update_icon() - return secured - -/obj/item/device/assembly/prox_sensor/HasProximity(turf/T, atom/movable/AM, old_loc) - if(!istype(AM)) - log_debug("DEBUG: HasProximity called with [AM] on [src] ([usr]).") - return - if (istype(AM, /obj/effect/beam)) - return - if (!isobserver(AM) && AM.move_speed < 12) - sense() - -/obj/item/device/assembly/prox_sensor/proc/sense() - if((!holder && !secured) || !scanning || !process_cooldown()) - return FALSE - var/turf/mainloc = get_turf(src) - pulse(0) - if(!holder) - mainloc.visible_message("\icon[src][bicon(src)] *beep* *beep*", "*beep* *beep*") - -/obj/item/device/assembly/prox_sensor/process() - if(scanning) - var/turf/mainloc = get_turf(src) - for(var/mob/living/A in range(range,mainloc)) - if (A.move_speed < 12) - sense() - - if(timing && (time >= 0)) - time-- - if(timing && time <= 0) - timing = 0 - toggle_scan() - time = initial(time) - -/obj/item/device/assembly/prox_sensor/dropped() - sense() - -/obj/item/device/assembly/prox_sensor/proc/toggle_scan() - if(!secured) - return FALSE - scanning = !scanning - update_icon() - -/obj/item/device/assembly/prox_sensor/update_icon() - cut_overlays() - LAZYCLEARLIST(attached_overlays) - if(timing) - add_overlay("prox_timing") - LAZYADD(attached_overlays, "prox_timing") - if(scanning) - add_overlay("prox_scanning") - LAZYADD(attached_overlays, "prox_scanning") - if(holder) - holder.update_icon() - if(holder && istype(holder.loc,/obj/item/weapon/grenade/chem_grenade)) - var/obj/item/weapon/grenade/chem_grenade/grenade = holder.loc - grenade.primed(scanning) - -/obj/item/device/assembly/prox_sensor/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(isturf(old_loc)) - unsense_proximity(range = range, callback = /atom/proc/HasProximity, center = old_loc) - if(isturf(loc)) - sense_proximity(range = range, callback = /atom/proc/HasProximity) - sense() - -/obj/item/device/assembly/prox_sensor/tgui_interact(mob/user, datum/tgui/ui) - if(!secured) - to_chat(user, "[src] is unsecured!") - return FALSE - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AssemblyProx", name) - ui.open() - -/obj/item/device/assembly/prox_sensor/tgui_data(mob/user) - var/list/data = ..() - - data["time"] = time * 10 - data["timing"] = timing - data["range"] = range - data["maxRange"] = 5 - data["scanning"] = scanning - - return data - -/obj/item/device/assembly/prox_sensor/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - switch(action) - if("scanning") - toggle_scan() - return TRUE - if("timing") - timing = !timing - update_icon() - return TRUE - if("set_time") - var/real_new_time = 0 - var/new_time = params["time"] - var/list/L = splittext(new_time, ":") - if(LAZYLEN(L)) - for(var/i in 1 to LAZYLEN(L)) - real_new_time += text2num(L[i]) * (60 ** (LAZYLEN(L) - i)) - else - real_new_time = text2num(new_time) - time = clamp(real_new_time, 0, 600) - return TRUE - if("range") - range = clamp(params["range"], 1, 5) - return TRUE +/obj/item/device/assembly/prox_sensor + name = "proximity sensor" + desc = "Used for scanning and alerting when someone enters a certain proximity." + icon_state = "prox" + origin_tech = list(TECH_MAGNET = 1) + matter = list(MAT_STEEL = 800, MAT_GLASS = 200) + wires = WIRE_PULSE + + secured = 0 + + var/scanning = 0 + var/timing = 0 + var/time = 10 + + var/range = 2 + +/obj/item/device/assembly/prox_sensor/activate() + if(!..()) + return FALSE + timing = !timing + update_icon() + return FALSE + +/obj/item/device/assembly/prox_sensor/toggle_secure() + secured = !secured + if(secured) + START_PROCESSING(SSobj, src) + else + scanning = 0 + timing = 0 + STOP_PROCESSING(SSobj, src) + update_icon() + return secured + +/obj/item/device/assembly/prox_sensor/HasProximity(turf/T, atom/movable/AM, old_loc) + if(!istype(AM)) + log_debug("DEBUG: HasProximity called with [AM] on [src] ([usr]).") + return + if (istype(AM, /obj/effect/beam)) + return + if (!isobserver(AM) && AM.move_speed < 12) + sense() + +/obj/item/device/assembly/prox_sensor/proc/sense() + if((!holder && !secured) || !scanning || !process_cooldown()) + return FALSE + var/turf/mainloc = get_turf(src) + pulse(0) + if(!holder) + mainloc.visible_message("\icon[src][bicon(src)] *beep* *beep*", "*beep* *beep*") + +/obj/item/device/assembly/prox_sensor/process() + if(scanning) + var/turf/mainloc = get_turf(src) + for(var/mob/living/A in range(range,mainloc)) + if (A.move_speed < 12) + sense() + + if(timing && (time >= 0)) + time-- + if(timing && time <= 0) + timing = 0 + toggle_scan() + time = initial(time) + +/obj/item/device/assembly/prox_sensor/dropped() + sense() + +/obj/item/device/assembly/prox_sensor/proc/toggle_scan() + if(!secured) + return FALSE + scanning = !scanning + update_icon() + +/obj/item/device/assembly/prox_sensor/update_icon() + cut_overlays() + LAZYCLEARLIST(attached_overlays) + if(timing) + add_overlay("prox_timing") + LAZYADD(attached_overlays, "prox_timing") + if(scanning) + add_overlay("prox_scanning") + LAZYADD(attached_overlays, "prox_scanning") + if(holder) + holder.update_icon() + if(holder && istype(holder.loc,/obj/item/weapon/grenade/chem_grenade)) + var/obj/item/weapon/grenade/chem_grenade/grenade = holder.loc + grenade.primed(scanning) + +/obj/item/device/assembly/prox_sensor/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(isturf(old_loc)) + unsense_proximity(range = range, callback = /atom/proc/HasProximity, center = old_loc) + if(isturf(loc)) + sense_proximity(range = range, callback = /atom/proc/HasProximity) + sense() + +/obj/item/device/assembly/prox_sensor/tgui_interact(mob/user, datum/tgui/ui) + if(!secured) + to_chat(user, "[src] is unsecured!") + return FALSE + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AssemblyProx", name) + ui.open() + +/obj/item/device/assembly/prox_sensor/tgui_data(mob/user) + var/list/data = ..() + + data["time"] = time * 10 + data["timing"] = timing + data["range"] = range + data["maxRange"] = 5 + data["scanning"] = scanning + + return data + +/obj/item/device/assembly/prox_sensor/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("scanning") + toggle_scan() + return TRUE + if("timing") + timing = !timing + update_icon() + return TRUE + if("set_time") + var/real_new_time = 0 + var/new_time = params["time"] + var/list/L = splittext(new_time, ":") + if(LAZYLEN(L)) + for(var/i in 1 to LAZYLEN(L)) + real_new_time += text2num(L[i]) * (60 ** (LAZYLEN(L) - i)) + else + real_new_time = text2num(new_time) + time = clamp(real_new_time, 0, 600) + return TRUE + if("range") + range = clamp(params["range"], 1, 5) + return TRUE diff --git a/code/modules/assembly/shock_kit.dm b/code/modules/assembly/shock_kit.dm index 8dab46f163a..c55d6a72cab 100644 --- a/code/modules/assembly/shock_kit.dm +++ b/code/modules/assembly/shock_kit.dm @@ -1,46 +1,46 @@ -/obj/item/assembly/shock_kit - name = "electrohelmet assembly" - desc = "This appears to be made from both an electropack and a helmet." - icon_state = "shock_kit" - var/obj/item/clothing/head/helmet/part1 = null - var/obj/item/device/radio/electropack/part2 = null - var/status = 0 - w_class = ITEMSIZE_HUGE - -/obj/item/assembly/shock_kit/Destroy() - qdel(part1) - qdel(part2) - ..() - return - -/obj/item/assembly/shock_kit/attackby(var/obj/item/weapon/W, var/mob/user) - if(W.has_tool_quality(TOOL_WRENCH) && !status) - var/turf/T = loc - if(ismob(T)) - T = T.loc - part1.loc = T - part2.loc = T - part1.master = null - part2.master = null - part1 = null - part2 = null - qdel(src) - return - if(W.has_tool_quality(TOOL_SCREWDRIVER)) - status = !status - to_chat(user, "[src] is now [status ? "secured" : "unsecured"]!") - playsound(src, W.usesound, 50, 1) - add_fingerprint(user) - return - -/obj/item/assembly/shock_kit/attack_self(mob/user as mob) - part1.attack_self(user, status) - part2.attack_self(user, status) - add_fingerprint(user) - return - -/obj/item/assembly/shock_kit/receive_signal() - if(istype(loc, /obj/structure/bed/chair/e_chair)) - var/obj/structure/bed/chair/e_chair/C = loc - C.shock() - return +/obj/item/assembly/shock_kit + name = "electrohelmet assembly" + desc = "This appears to be made from both an electropack and a helmet." + icon_state = "shock_kit" + var/obj/item/clothing/head/helmet/part1 = null + var/obj/item/device/radio/electropack/part2 = null + var/status = 0 + w_class = ITEMSIZE_HUGE + +/obj/item/assembly/shock_kit/Destroy() + qdel(part1) + qdel(part2) + ..() + return + +/obj/item/assembly/shock_kit/attackby(var/obj/item/weapon/W, var/mob/user) + if(W.has_tool_quality(TOOL_WRENCH) && !status) + var/turf/T = loc + if(ismob(T)) + T = T.loc + part1.loc = T + part2.loc = T + part1.master = null + part2.master = null + part1 = null + part2 = null + qdel(src) + return + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + status = !status + to_chat(user, "[src] is now [status ? "secured" : "unsecured"]!") + playsound(src, W.usesound, 50, 1) + add_fingerprint(user) + return + +/obj/item/assembly/shock_kit/attack_self(mob/user as mob) + part1.attack_self(user, status) + part2.attack_self(user, status) + add_fingerprint(user) + return + +/obj/item/assembly/shock_kit/receive_signal() + if(istype(loc, /obj/structure/bed/chair/e_chair)) + var/obj/structure/bed/chair/e_chair/C = loc + C.shock() + return diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index a7267ca3428..a87a13a5765 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -1,137 +1,137 @@ -/obj/item/device/assembly/signaler - name = "remote signaling device" - desc = "Used to remotely activate devices. Tap against another secured signaler to transfer configuration." - icon_state = "signaller" - item_state = "signaler" - origin_tech = list(TECH_MAGNET = 1) - matter = list(MAT_STEEL = 1000, MAT_GLASS = 200) - wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE - - secured = TRUE - - var/code = 30 - var/frequency = 1457 - var/delay = 0 - var/airlock_wire = null - var/datum/wires/connected = null - var/datum/radio_frequency/radio_connection - -/obj/item/device/assembly/signaler/Initialize() - . = ..() - set_frequency(frequency) - -/obj/item/device/assembly/signaler/activate() - if(!process_cooldown()) - return FALSE - signal() - return TRUE - -/obj/item/device/assembly/signaler/update_icon() - if(holder) - holder.update_icon() - -/obj/item/device/assembly/signaler/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Signaler", name) - ui.open() - -/obj/item/device/assembly/signaler/tgui_data(mob/user) - var/list/data = list() - data["frequency"] = frequency - data["code"] = code - data["minFrequency"] = RADIO_LOW_FREQ - data["maxFrequency"] = RADIO_HIGH_FREQ - return data - -/obj/item/device/assembly/signaler/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("signal") - INVOKE_ASYNC(src, PROC_REF(signal)) - . = TRUE - if("freq") - frequency = unformat_frequency(params["freq"]) - frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) - set_frequency(frequency) - . = TRUE - if("code") - code = text2num(params["code"]) - code = clamp(round(code), 1, 100) - . = TRUE - if("reset") - if(params["reset"] == "freq") - set_frequency(initial(frequency)) - else - code = initial(code) - . = TRUE - - update_icon() - -/obj/item/device/assembly/signaler/attackby(var/obj/item/weapon/W, mob/user, params) - if(issignaler(W)) - var/obj/item/device/assembly/signaler/signaler2 = W - if(secured && signaler2.secured) - code = signaler2.code - set_frequency(signaler2.frequency) - to_chat(user, "You transfer the frequency and code of [signaler2] to [src].") - else - ..() - -/obj/item/device/assembly/signaler/proc/signal() - if(!radio_connection) - return - if(is_jammed(src)) - return - - var/datum/signal/signal = new - signal.source = src - signal.encryption = code - signal.data["message"] = "ACTIVATE" - radio_connection.post_signal(src, signal) - -/obj/item/device/assembly/signaler/pulse(var/radio = 0) - if(is_jammed(src)) - return FALSE - if(connected && wires) - connected.pulse_assembly(src) - else if(holder) - holder.process_activation(src, 1, 0) - else - ..(radio) - return TRUE - -/obj/item/device/assembly/signaler/receive_signal(datum/signal/signal) - if(!signal) - return FALSE - if(signal.encryption != code) - return FALSE - if(!(src.wires & WIRE_RADIO_RECEIVE)) - return FALSE - if(is_jammed(src)) - return FALSE - pulse(1) - - if(!holder) - for(var/mob/O in hearers(1, src.loc)) - O.show_message("\icon[src][bicon(src)] *beep* *beep*", 3, "*beep* *beep*", 2) - -/obj/item/device/assembly/signaler/proc/set_frequency(new_frequency) - if(!frequency) - return - if(!radio_controller) - sleep(20) - if(!radio_controller) - return - - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, RADIO_CHAT) - -/obj/item/device/assembly/signaler/Destroy() - if(radio_controller) - radio_controller.remove_object(src,frequency) - frequency = 0 - . = ..() +/obj/item/device/assembly/signaler + name = "remote signaling device" + desc = "Used to remotely activate devices. Tap against another secured signaler to transfer configuration." + icon_state = "signaller" + item_state = "signaler" + origin_tech = list(TECH_MAGNET = 1) + matter = list(MAT_STEEL = 1000, MAT_GLASS = 200) + wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE + + secured = TRUE + + var/code = 30 + var/frequency = 1457 + var/delay = 0 + var/airlock_wire = null + var/datum/wires/connected = null + var/datum/radio_frequency/radio_connection + +/obj/item/device/assembly/signaler/Initialize() + . = ..() + set_frequency(frequency) + +/obj/item/device/assembly/signaler/activate() + if(!process_cooldown()) + return FALSE + signal() + return TRUE + +/obj/item/device/assembly/signaler/update_icon() + if(holder) + holder.update_icon() + +/obj/item/device/assembly/signaler/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Signaler", name) + ui.open() + +/obj/item/device/assembly/signaler/tgui_data(mob/user) + var/list/data = list() + data["frequency"] = frequency + data["code"] = code + data["minFrequency"] = RADIO_LOW_FREQ + data["maxFrequency"] = RADIO_HIGH_FREQ + return data + +/obj/item/device/assembly/signaler/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("signal") + INVOKE_ASYNC(src, PROC_REF(signal)) + . = TRUE + if("freq") + frequency = unformat_frequency(params["freq"]) + frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(frequency) + . = TRUE + if("code") + code = text2num(params["code"]) + code = clamp(round(code), 1, 100) + . = TRUE + if("reset") + if(params["reset"] == "freq") + set_frequency(initial(frequency)) + else + code = initial(code) + . = TRUE + + update_icon() + +/obj/item/device/assembly/signaler/attackby(var/obj/item/weapon/W, mob/user, params) + if(issignaler(W)) + var/obj/item/device/assembly/signaler/signaler2 = W + if(secured && signaler2.secured) + code = signaler2.code + set_frequency(signaler2.frequency) + to_chat(user, "You transfer the frequency and code of [signaler2] to [src].") + else + ..() + +/obj/item/device/assembly/signaler/proc/signal() + if(!radio_connection) + return + if(is_jammed(src)) + return + + var/datum/signal/signal = new + signal.source = src + signal.encryption = code + signal.data["message"] = "ACTIVATE" + radio_connection.post_signal(src, signal) + +/obj/item/device/assembly/signaler/pulse(var/radio = 0) + if(is_jammed(src)) + return FALSE + if(connected && wires) + connected.pulse_assembly(src) + else if(holder) + holder.process_activation(src, 1, 0) + else + ..(radio) + return TRUE + +/obj/item/device/assembly/signaler/receive_signal(datum/signal/signal) + if(!signal) + return FALSE + if(signal.encryption != code) + return FALSE + if(!(src.wires & WIRE_RADIO_RECEIVE)) + return FALSE + if(is_jammed(src)) + return FALSE + pulse(1) + + if(!holder) + for(var/mob/O in hearers(1, src.loc)) + O.show_message("\icon[src][bicon(src)] *beep* *beep*", 3, "*beep* *beep*", 2) + +/obj/item/device/assembly/signaler/proc/set_frequency(new_frequency) + if(!frequency) + return + if(!radio_controller) + sleep(20) + if(!radio_controller) + return + + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency, RADIO_CHAT) + +/obj/item/device/assembly/signaler/Destroy() + if(radio_controller) + radio_controller.remove_object(src,frequency) + frequency = 0 + . = ..() diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm index c480ed4a4e9..dd13a1e4a21 100644 --- a/code/modules/assembly/timer.dm +++ b/code/modules/assembly/timer.dm @@ -1,99 +1,99 @@ -/obj/item/device/assembly/timer - name = "timer" - desc = "Used to time things. Works well with contraptions which has to count down. Tick tock." - icon_state = "timer" - origin_tech = list(TECH_MAGNET = 1) - matter = list(MAT_STEEL = 500, MAT_GLASS = 50) - - wires = WIRE_PULSE - - secured = 0 - - var/timing = 0 - var/time = 10 - - -/obj/item/device/assembly/timer/activate() - if(!..()) - return FALSE - - set_state(!timing) - - update_icon() - return 0 - -/obj/item/device/assembly/timer/toggle_secure() - secured = !secured - if(secured) - START_PROCESSING(SSobj, src) - else - timing = 0 - STOP_PROCESSING(SSobj, src) - update_icon() - return secured - -/obj/item/device/assembly/timer/proc/set_state(var/state) - if(state && !timing) //Not running, starting though - START_PROCESSING(SSobj, src) - else if(timing && !state) //Running, stopping though - STOP_PROCESSING(SSobj, src) - timing = state - -/obj/item/device/assembly/timer/proc/timer_end() - if(!secured) - return 0 - pulse(0) - if(!holder) - visible_message("\icon[src][bicon(src)] *beep* *beep*", "*beep* *beep*") - -/obj/item/device/assembly/timer/process() - if(timing && time-- <= 0) - set_state(0) - timer_end() - time = 10 - -/obj/item/device/assembly/timer/update_icon() - cut_overlays() - attached_overlays = list() - if(timing) - add_overlay("timer_timing") - attached_overlays += "timer_timing" - if(holder) - holder.update_icon() - return - -/obj/item/device/assembly/timer/tgui_interact(mob/user, datum/tgui/ui) - if(!secured) - to_chat(user, "[src] is unsecured!") - return FALSE - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AssemblyTimer", name) - ui.open() - -/obj/item/device/assembly/timer/tgui_data(mob/user) - var/list/data = ..() - data["time"] = time * 10 - data["timing"] = timing - return data - -/obj/item/device/assembly/timer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - switch(action) - if("timing") - set_state(!timing) - update_icon() - return TRUE - if("set_time") - var/real_new_time = 0 - var/new_time = params["time"] - var/list/L = splittext(new_time, ":") - if(LAZYLEN(L)) - for(var/i in 1 to LAZYLEN(L)) - real_new_time += text2num(L[i]) * (60 ** (LAZYLEN(L) - i)) - else - real_new_time = text2num(new_time) - time = clamp(real_new_time, 0, 600) - return TRUE +/obj/item/device/assembly/timer + name = "timer" + desc = "Used to time things. Works well with contraptions which has to count down. Tick tock." + icon_state = "timer" + origin_tech = list(TECH_MAGNET = 1) + matter = list(MAT_STEEL = 500, MAT_GLASS = 50) + + wires = WIRE_PULSE + + secured = 0 + + var/timing = 0 + var/time = 10 + + +/obj/item/device/assembly/timer/activate() + if(!..()) + return FALSE + + set_state(!timing) + + update_icon() + return 0 + +/obj/item/device/assembly/timer/toggle_secure() + secured = !secured + if(secured) + START_PROCESSING(SSobj, src) + else + timing = 0 + STOP_PROCESSING(SSobj, src) + update_icon() + return secured + +/obj/item/device/assembly/timer/proc/set_state(var/state) + if(state && !timing) //Not running, starting though + START_PROCESSING(SSobj, src) + else if(timing && !state) //Running, stopping though + STOP_PROCESSING(SSobj, src) + timing = state + +/obj/item/device/assembly/timer/proc/timer_end() + if(!secured) + return 0 + pulse(0) + if(!holder) + visible_message("\icon[src][bicon(src)] *beep* *beep*", "*beep* *beep*") + +/obj/item/device/assembly/timer/process() + if(timing && time-- <= 0) + set_state(0) + timer_end() + time = 10 + +/obj/item/device/assembly/timer/update_icon() + cut_overlays() + attached_overlays = list() + if(timing) + add_overlay("timer_timing") + attached_overlays += "timer_timing" + if(holder) + holder.update_icon() + return + +/obj/item/device/assembly/timer/tgui_interact(mob/user, datum/tgui/ui) + if(!secured) + to_chat(user, "[src] is unsecured!") + return FALSE + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AssemblyTimer", name) + ui.open() + +/obj/item/device/assembly/timer/tgui_data(mob/user) + var/list/data = ..() + data["time"] = time * 10 + data["timing"] = timing + return data + +/obj/item/device/assembly/timer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("timing") + set_state(!timing) + update_icon() + return TRUE + if("set_time") + var/real_new_time = 0 + var/new_time = params["time"] + var/list/L = splittext(new_time, ":") + if(LAZYLEN(L)) + for(var/i in 1 to LAZYLEN(L)) + real_new_time += text2num(L[i]) * (60 ** (LAZYLEN(L) - i)) + else + real_new_time = text2num(new_time) + time = clamp(real_new_time, 0, 600) + return TRUE diff --git a/code/modules/asset_cache/assets/chat.dm b/code/modules/asset_cache/assets/chat.dm index eba0e5418e0..328beaca9d0 100644 --- a/code/modules/asset_cache/assets/chat.dm +++ b/code/modules/asset_cache/assets/chat.dm @@ -1,2 +1,2 @@ -/datum/asset/spritesheet/chat - name = "chat" +/datum/asset/spritesheet/chat + name = "chat" diff --git a/code/modules/awaymissions/bluespaceartillery.dm b/code/modules/awaymissions/bluespaceartillery.dm index 5781085c5e3..f11b9ee61e1 100644 --- a/code/modules/awaymissions/bluespaceartillery.dm +++ b/code/modules/awaymissions/bluespaceartillery.dm @@ -1,51 +1,51 @@ - -/obj/machinery/artillerycontrol - var/reload = 180 - name = "bluespace artillery control" - icon_state = "control_boxp1" - icon = 'icons/obj/machines/particle_accelerator2.dmi' - density = TRUE - anchored = TRUE - -/obj/machinery/artillerycontrol/process() - if(src.reload<180) - src.reload++ - -/obj/structure/artilleryplaceholder - name = "artillery" - icon = 'icons/obj/machines/artillery.dmi' - anchored = TRUE - density = TRUE - -/obj/structure/artilleryplaceholder/decorative - density = FALSE - -/obj/machinery/artillerycontrol/attack_hand(mob/user as mob) - user.set_machine(src) - var/dat = "Bluespace Artillery Control:
                    " - dat += "Locked on
                    " - dat += "Charge progress: [reload]/180:
                    " - dat += "Open Fire
                    " - dat += "Deployment of weapon authorized by
                    [using_map.company_name] Naval Command

                    Remember, friendly fire is grounds for termination of your contract and life.
                    " - user << browse(dat, "window=scroll") - onclose(user, "scroll") - return - -/obj/machinery/artillerycontrol/Topic(href, href_list) - ..() - if (usr.stat || usr.restrained()) - return - if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) - var/A = tgui_input_list(usr, "Area to jump bombard", "Open Fire", teleportlocs) - var/area/thearea = teleportlocs[A] - if (usr.stat || usr.restrained()) return - if(src.reload < 180) return - if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) - command_announcement.Announce("Bluespace artillery fire detected. Brace for impact.") - message_admins("[key_name_admin(usr)] has launched an artillery strike.", 1) - var/list/L = list() - for(var/turf/T in get_area_turfs(thearea.type)) - L+=T - var/loc = pick(L) - explosion(loc,2,5,11) - reload = 0 + +/obj/machinery/artillerycontrol + var/reload = 180 + name = "bluespace artillery control" + icon_state = "control_boxp1" + icon = 'icons/obj/machines/particle_accelerator2.dmi' + density = TRUE + anchored = TRUE + +/obj/machinery/artillerycontrol/process() + if(src.reload<180) + src.reload++ + +/obj/structure/artilleryplaceholder + name = "artillery" + icon = 'icons/obj/machines/artillery.dmi' + anchored = TRUE + density = TRUE + +/obj/structure/artilleryplaceholder/decorative + density = FALSE + +/obj/machinery/artillerycontrol/attack_hand(mob/user as mob) + user.set_machine(src) + var/dat = "Bluespace Artillery Control:
                    " + dat += "Locked on
                    " + dat += "Charge progress: [reload]/180:
                    " + dat += "Open Fire
                    " + dat += "Deployment of weapon authorized by
                    [using_map.company_name] Naval Command

                    Remember, friendly fire is grounds for termination of your contract and life.
                    " + user << browse(dat, "window=scroll") + onclose(user, "scroll") + return + +/obj/machinery/artillerycontrol/Topic(href, href_list) + ..() + if (usr.stat || usr.restrained()) + return + if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + var/A = tgui_input_list(usr, "Area to jump bombard", "Open Fire", teleportlocs) + var/area/thearea = teleportlocs[A] + if (usr.stat || usr.restrained()) return + if(src.reload < 180) return + if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + command_announcement.Announce("Bluespace artillery fire detected. Brace for impact.") + message_admins("[key_name_admin(usr)] has launched an artillery strike.", 1) + var/list/L = list() + for(var/turf/T in get_area_turfs(thearea.type)) + L+=T + var/loc = pick(L) + explosion(loc,2,5,11) + reload = 0 diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index 1aaaab9e483..b3ab39f74ee 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -1,381 +1,381 @@ -//These are meant for spawning on maps, namely Away Missions. - -//If someone can do this in a neater way, be my guest-Kor - -//To do: Allow corpses to appear mangled, bloody, etc. Allow customizing the bodies appearance (they're all bald and white right now). - -/obj/effect/landmark/corpse - name = "Unknown" - var/mobname = "Unknown" //Unused now but it'd fuck up maps to remove it now - var/corpseuniform = null //Set this to an object path to have the slot filled with said object on the corpse. - var/corpsesuit = null - var/corpseshoes = null - var/corpsegloves = null - var/corpseradio = null - var/corpseglasses = null - var/corpsemask = null - var/corpsehelmet = null - var/corpsebelt = null - var/corpsepocket1 = null - var/corpsepocket2 = null - var/corpseback = null - var/corpseid = 0 //Just set to 1 if you want them to have an ID - var/corpseidjob = null // Needs to be in quotes, such as "Clown" or "Chef." This just determines what the ID reads as, not their access - var/corpseidaccess = null //This is for access. See access.dm for which jobs give what access. Again, put in quotes. Use "Captain" if you want it to be all access. - var/corpseidicon = null //For setting it to be a gold, silver, CentCom etc ID - var/species = SPECIES_HUMAN //defaults to generic-ass humans - var/random_species = FALSE //flip to TRUE to randomize species from the list below - var/list/random_species_list = list(SPECIES_HUMAN,SPECIES_TAJ,SPECIES_UNATHI,SPECIES_SKRELL) //preset list that can be overriden downstream. only includes common humanoids for voidsuit compatibility's sake. -// var/random_appearance = FALSE //TODO: make this work -// var/cause_of_death = null //TODO: set up a cause-of-death system. needs to support both damage types and actual wound types, so a body can have been bitten/stabbed/clawed/shot/burned/lasered/etc. to death - delete_me = TRUE - -/obj/effect/landmark/corpse/Initialize() - ..() - createCorpse() - return INITIALIZE_HINT_QDEL - -/obj/effect/landmark/corpse/proc/createCorpse() //Creates a mob and checks for gear in each slot before attempting to equip it. - var/mob/living/carbon/human/M = new /mob/living/carbon/human (src.loc) - M.low_sorting_priority = TRUE - if(random_species) - var/random_pick = pick(random_species_list) - M.set_species(random_pick) - src.species = random_pick - else - M.set_species(species) - //TODO: insert appearance randomization, needs to be species-based - M.real_name = src.name - M.death(1) //Kills the new mob - //TODO: insert cause of death handling/wound simulation here - if(src.corpseuniform) - M.equip_to_slot_or_del(new src.corpseuniform(M), slot_w_uniform) - if(src.corpseshoes) - M.equip_to_slot_or_del(new src.corpseshoes(M), slot_shoes) - if(src.corpsegloves) - M.equip_to_slot_or_del(new src.corpsegloves(M), slot_gloves) - if(src.corpseradio) - M.equip_to_slot_or_del(new src.corpseradio(M), slot_l_ear) - if(src.corpseglasses) - M.equip_to_slot_or_del(new src.corpseglasses(M), slot_glasses) - if(src.corpsemask) - M.equip_to_slot_or_del(new src.corpsemask(M), slot_wear_mask) - if(src.corpsebelt) - M.equip_to_slot_or_del(new src.corpsebelt(M), slot_belt) - if(src.corpsepocket1) - M.equip_to_slot_or_del(new src.corpsepocket1(M), slot_r_store) - if(src.corpsepocket2) - M.equip_to_slot_or_del(new src.corpsepocket2(M), slot_l_store) - if(src.corpseback) - M.equip_to_slot_or_del(new src.corpseback(M), slot_back) - if(src.corpseid == 1) - var/obj/item/weapon/card/id/W = new(M) - var/datum/job/jobdatum - for(var/jobtype in typesof(/datum/job)) - var/datum/job/J = new jobtype - if(J.title == corpseidaccess) - jobdatum = J - break - if(src.corpseidicon) - W.icon_state = corpseidicon - if(src.corpseidaccess) - if(jobdatum) - W.access = jobdatum.get_access() - else - W.access = list() - if(corpseidjob) - W.assignment = corpseidjob - M.set_id_info(W) - M.equip_to_slot_or_del(W, slot_wear_id) - // Do suit last to avoid equipping issues - if(src.corpsehelmet) - M.equip_voidhelm_to_slot_or_del_with_refit(new src.corpsehelmet(M), slot_head, src.species) - if(src.corpsesuit) - M.equip_voidsuit_to_slot_or_del_with_refit(new src.corpsesuit(M), slot_wear_suit, src.species) - - - -// I'll work on making a list of corpses people request for maps, or that I think will be commonly used. Syndicate operatives for example. - -/obj/effect/landmark/corpse/syndicatesoldier - name = "Mercenary" - corpseuniform = /obj/item/clothing/under/syndicate - corpsesuit = /obj/item/clothing/suit/armor/vest - corpseshoes = /obj/item/clothing/shoes/boots/swat - corpsegloves = /obj/item/clothing/gloves/swat - corpseradio = /obj/item/device/radio/headset - corpsemask = /obj/item/clothing/mask/gas - corpsehelmet = /obj/item/clothing/head/helmet/swat - corpseback = /obj/item/weapon/storage/backpack - corpseid = 1 - corpseidjob = "Operative" - corpseidaccess = "Syndicate" - -/obj/effect/landmark/corpse/syndicatecommando - name = "Mercenary Commando" - corpseuniform = /obj/item/clothing/under/syndicate - corpsesuit = /obj/item/clothing/suit/space/void/merc - corpseshoes = /obj/item/clothing/shoes/boots/swat - corpsegloves = /obj/item/clothing/gloves/swat - corpseradio = /obj/item/device/radio/headset - corpsemask = /obj/item/clothing/mask/gas/syndicate - corpsehelmet = /obj/item/clothing/head/helmet/space/void/merc - corpseback = /obj/item/weapon/tank/jetpack/oxygen - corpsepocket1 = /obj/item/weapon/tank/emergency/oxygen - corpseid = 1 - corpseidjob = "Operative" - corpseidaccess = "Syndicate" - -///////////Civilians////////////////////// - -/obj/effect/landmark/corpse/random_civ - name = "Civilian" - corpseuniform = /obj/item/clothing/under/color/grey - corpseshoes = /obj/item/clothing/shoes/black - random_species = TRUE - - - -/obj/effect/landmark/corpse/chef - name = "Chef" - corpseuniform = /obj/item/clothing/under/rank/chef - corpsesuit = /obj/item/clothing/suit/chef/classic - corpseshoes = /obj/item/clothing/shoes/black - corpsehelmet = /obj/item/clothing/head/chefhat - corpseback = /obj/item/weapon/storage/backpack - corpseradio = /obj/item/device/radio/headset - corpseid = 1 - corpseidjob = "Chef" - corpseidaccess = "Chef" - - -/obj/effect/landmark/corpse/doctor - name = "Doctor" - corpseradio = /obj/item/device/radio/headset/headset_med - corpseuniform = /obj/item/clothing/under/rank/medical - corpsesuit = /obj/item/clothing/suit/storage/toggle/labcoat - corpseback = /obj/item/weapon/storage/backpack/medic - corpsepocket1 = /obj/item/device/flashlight/pen - corpseshoes = /obj/item/clothing/shoes/black - corpseid = 1 - corpseidjob = "Medical Doctor" - corpseidaccess = "Medical Doctor" - -/obj/effect/landmark/corpse/engineer - name = "Engineer" - corpseradio = /obj/item/device/radio/headset/headset_eng - corpseuniform = /obj/item/clothing/under/rank/engineer - corpseback = /obj/item/weapon/storage/backpack/industrial - corpseshoes = /obj/item/clothing/shoes/orange - corpsebelt = /obj/item/weapon/storage/belt/utility/full - corpsegloves = /obj/item/clothing/gloves/yellow - corpsehelmet = /obj/item/clothing/head/hardhat - corpseid = 1 - corpseidjob = "Engineer" - corpseidaccess = "Engineer" - -/obj/effect/landmark/corpse/engineer/rig - corpsesuit = /obj/item/clothing/suit/space/void/engineering - corpsemask = /obj/item/clothing/mask/breath - corpsehelmet = /obj/item/clothing/head/helmet/space/void/engineering - corpseback = /obj/item/weapon/tank/oxygen - -/obj/effect/landmark/corpse/clown - name = "Clown" - corpseuniform = /obj/item/clothing/under/rank/clown - corpseshoes = /obj/item/clothing/shoes/clown_shoes - corpseradio = /obj/item/device/radio/headset - corpsemask = /obj/item/clothing/mask/gas/clown_hat - corpsepocket1 = /obj/item/weapon/bikehorn - corpseback = /obj/item/weapon/storage/backpack/clown - corpseid = 1 - corpseidjob = "Clown" - corpseidaccess = "Clown" - -/obj/effect/landmark/corpse/scientist - name = "Scientist" - corpseradio = /obj/item/device/radio/headset/headset_sci - corpseuniform = /obj/item/clothing/under/rank/scientist - corpsesuit = /obj/item/clothing/suit/storage/toggle/labcoat/science - corpseback = /obj/item/weapon/storage/backpack - corpseshoes = /obj/item/clothing/shoes/white - corpseid = 1 - corpseidjob = "Scientist" - corpseidaccess = "Scientist" - -/obj/effect/landmark/corpse/security - name = "Security Officer" - corpseradio = /obj/item/device/radio/headset/headset_sec - corpseuniform = /obj/item/clothing/under/rank/security - corpsesuit = /obj/item/clothing/suit/armor/vest - corpseback = /obj/item/weapon/storage/backpack/security - corpseshoes = /obj/item/clothing/shoes/boots/jackboots - corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud - corpsegloves = /obj/item/clothing/gloves/black - corpsehelmet = /obj/item/clothing/head/helmet - corpseid = 1 - corpseidjob = "Security Officer" - corpseidaccess = "Security Officer" - -/obj/effect/landmark/corpse/security/rig - corpsesuit = /obj/item/clothing/suit/space/void/security - corpsemask = /obj/item/clothing/mask/breath - corpsehelmet = /obj/item/clothing/head/helmet/space/void/security - corpseback = /obj/item/weapon/tank/jetpack/oxygen - -/obj/effect/landmark/corpse/security/rig/eva - corpsesuit = /obj/item/clothing/suit/space/void/security/alt - corpsehelmet = /obj/item/clothing/head/helmet/space/void/security/alt - corpseidjob = "Starship Security Officer" - -/obj/effect/landmark/corpse/prisoner - name = "Unknown Prisoner" - corpseuniform = /obj/item/clothing/under/color/prison - corpseshoes = /obj/item/clothing/shoes/orange - corpseid = 1 - corpseidjob = "Prisoner" - random_species = TRUE - -/obj/effect/landmark/corpse/miner - corpseradio = /obj/item/device/radio/headset/headset_cargo - corpseuniform = /obj/item/clothing/under/rank/miner - corpsegloves = /obj/item/clothing/gloves/black - corpseback = /obj/item/weapon/storage/backpack/industrial - corpseshoes = /obj/item/clothing/shoes/black - corpseid = 1 - corpseidjob = "Shaft Miner" - corpseidaccess = "Shaft Miner" - -/obj/effect/landmark/corpse/miner/rig - corpsesuit = /obj/item/clothing/suit/space/void/mining - corpsemask = /obj/item/clothing/mask/breath - corpsehelmet = /obj/item/clothing/head/helmet/space/void/mining - corpseback = /obj/item/weapon/tank/oxygen - -/////////////////Vintage////////////////////// - -//define the basic props at this level and only change specifics for variants, e.z. -/obj/effect/landmark/corpse/vintage - name = "Unknown Crewmate" - corpseuniform = /obj/item/clothing/under/utility - corpsesuit = /obj/item/clothing/suit/space/void/refurb - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb - corpsemask = /obj/item/clothing/mask/breath - corpseback = /obj/item/weapon/tank/oxygen - corpseid = 1 - corpseidjob = "Crewmate" - -/obj/effect/landmark/corpse/vintage/engineering - name = "Unknown Engineer" - corpsesuit = /obj/item/clothing/suit/space/void/refurb/engineering - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/engineering - corpsebelt = /obj/item/weapon/storage/belt/utility/full - corpseback = /obj/item/weapon/tank/oxygen/yellow - corpseidjob = "Engineer" - -/obj/effect/landmark/corpse/vintage/marine - name = "Unknown Marine" - corpsesuit = /obj/item/clothing/suit/space/void/refurb/marine - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/marine - corpsebelt = /obj/item/weapon/storage/belt/security/tactical - corpseidjob = "Marine" - -/obj/effect/landmark/corpse/vintage/medical - name = "Unknown Medic" - corpsesuit = /obj/item/clothing/suit/space/void/refurb/medical - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/medical - corpsebelt = /obj/item/weapon/storage/belt/medical - corpseidjob = "Medic" - -/obj/effect/landmark/corpse/vintage/mercenary - name = "Unknown Mercenary" - corpsesuit = /obj/item/clothing/suit/space/void/refurb/mercenary - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/mercenary - corpsebelt = /obj/item/weapon/storage/belt/security/tactical - corpseback = /obj/item/weapon/tank/oxygen/red - corpseidjob = "Mercenary" - -/obj/effect/landmark/corpse/vintage/officer - name = "Unknown Captain" - corpsesuit = /obj/item/clothing/suit/space/void/refurb/officer - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/officer - corpseback = /obj/item/weapon/tank/oxygen/yellow - corpseidjob = "Captain" - -/obj/effect/landmark/corpse/vintage/pilot - name = "Unknown Pilot" - corpsesuit = /obj/item/clothing/suit/space/void/refurb/pilot - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/pilot - corpseidjob = "Pilot" - -/obj/effect/landmark/corpse/vintage/research - name = "Unknown Researcher" - corpsesuit = /obj/item/clothing/suit/space/void/refurb/research - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/research - corpseidjob = "Researcher" - -/////////////////Officers////////////////////// - -/obj/effect/landmark/corpse/bridgeofficer - name = "Bridge Officer" - corpseradio = /obj/item/device/radio/headset/heads/hop - corpseuniform = /obj/item/clothing/under/rank/centcom_officer - corpsesuit = /obj/item/clothing/suit/armor/bulletproof - corpseshoes = /obj/item/clothing/shoes/black - corpseglasses = /obj/item/clothing/glasses/sunglasses - corpseid = 1 - corpseidjob = "Bridge Officer" - corpseidaccess = "Captain" - -/obj/effect/landmark/corpse/commander - name = "Commander" - corpseuniform = /obj/item/clothing/under/rank/centcom_captain - corpsesuit = /obj/item/clothing/suit/armor/bulletproof - corpseradio = /obj/item/device/radio/headset/heads/captain - corpseglasses = /obj/item/clothing/glasses/eyepatch - corpsemask = /obj/item/clothing/mask/smokable/cigarette/cigar/cohiba - corpsehelmet = /obj/item/clothing/head/centhat - corpsegloves = /obj/item/clothing/gloves/swat - corpseshoes = /obj/item/clothing/shoes/boots/swat - corpsepocket1 = /obj/item/weapon/flame/lighter/zippo - corpseid = 1 - corpseidjob = "Commander" - corpseidaccess = "Captain" - -/////////////////Lore Factions////////////////////// - -/obj/effect/landmark/corpse/sifguard - name = "Patrolman" - corpseuniform = /obj/item/clothing/under/solgov/utility/sifguard - corpsesuit = /obj/item/clothing/suit/storage/hooded/wintercoat/solgov - corpsebelt = /obj/item/weapon/storage/belt/security/tactical - corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud - corpsemask = /obj/item/clothing/mask/balaclava - corpsehelmet = /obj/item/clothing/head/beret/solgov/sifguard - corpsegloves = /obj/item/clothing/gloves/duty - corpseshoes = /obj/item/clothing/shoes/boots/tactical - corpsepocket1 = /obj/item/clothing/accessory/armor/tag/sifguard - corpseid = 1 - corpseidjob = "Sif Defense Force Patrolman" - -/obj/effect/landmark/corpse/hedberg - name = "Hedberg-Hammarstrom Mercenary" - corpseuniform = /obj/item/clothing/under/solgov/utility/sifguard - corpsesuit = /obj/item/clothing/suit/storage/vest/solgov/hedberg - corpsebelt = /obj/item/weapon/storage/belt/security - corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud - corpsehelmet = /obj/item/clothing/head/beret/corp/hedberg - corpseshoes = /obj/item/clothing/shoes/boots/jackboots - corpseid = 1 - corpseidjob = "Hedberg-Hammarstrom Officer" - -/obj/effect/landmark/corpse/hedberg/merc - name = "Hedberg-Hammarstrom Mercenary" - corpsebelt = /obj/item/weapon/storage/belt/security/tactical - corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud - corpsehelmet = /obj/item/clothing/head/helmet/flexitac - corpsegloves = /obj/item/clothing/gloves/combat - corpseshoes = /obj/item/clothing/shoes/boots/tactical - corpseid = 1 - corpseidjob = "Hedberg-Hammarstrom Enforcer" - +//These are meant for spawning on maps, namely Away Missions. + +//If someone can do this in a neater way, be my guest-Kor + +//To do: Allow corpses to appear mangled, bloody, etc. Allow customizing the bodies appearance (they're all bald and white right now). + +/obj/effect/landmark/corpse + name = "Unknown" + var/mobname = "Unknown" //Unused now but it'd fuck up maps to remove it now + var/corpseuniform = null //Set this to an object path to have the slot filled with said object on the corpse. + var/corpsesuit = null + var/corpseshoes = null + var/corpsegloves = null + var/corpseradio = null + var/corpseglasses = null + var/corpsemask = null + var/corpsehelmet = null + var/corpsebelt = null + var/corpsepocket1 = null + var/corpsepocket2 = null + var/corpseback = null + var/corpseid = 0 //Just set to 1 if you want them to have an ID + var/corpseidjob = null // Needs to be in quotes, such as "Clown" or "Chef." This just determines what the ID reads as, not their access + var/corpseidaccess = null //This is for access. See access.dm for which jobs give what access. Again, put in quotes. Use "Captain" if you want it to be all access. + var/corpseidicon = null //For setting it to be a gold, silver, CentCom etc ID + var/species = SPECIES_HUMAN //defaults to generic-ass humans + var/random_species = FALSE //flip to TRUE to randomize species from the list below + var/list/random_species_list = list(SPECIES_HUMAN,SPECIES_TAJ,SPECIES_UNATHI,SPECIES_SKRELL) //preset list that can be overriden downstream. only includes common humanoids for voidsuit compatibility's sake. +// var/random_appearance = FALSE //TODO: make this work +// var/cause_of_death = null //TODO: set up a cause-of-death system. needs to support both damage types and actual wound types, so a body can have been bitten/stabbed/clawed/shot/burned/lasered/etc. to death + delete_me = TRUE + +/obj/effect/landmark/corpse/Initialize() + ..() + createCorpse() + return INITIALIZE_HINT_QDEL + +/obj/effect/landmark/corpse/proc/createCorpse() //Creates a mob and checks for gear in each slot before attempting to equip it. + var/mob/living/carbon/human/M = new /mob/living/carbon/human (src.loc) + M.low_sorting_priority = TRUE + if(random_species) + var/random_pick = pick(random_species_list) + M.set_species(random_pick) + src.species = random_pick + else + M.set_species(species) + //TODO: insert appearance randomization, needs to be species-based + M.real_name = src.name + M.death(1) //Kills the new mob + //TODO: insert cause of death handling/wound simulation here + if(src.corpseuniform) + M.equip_to_slot_or_del(new src.corpseuniform(M), slot_w_uniform) + if(src.corpseshoes) + M.equip_to_slot_or_del(new src.corpseshoes(M), slot_shoes) + if(src.corpsegloves) + M.equip_to_slot_or_del(new src.corpsegloves(M), slot_gloves) + if(src.corpseradio) + M.equip_to_slot_or_del(new src.corpseradio(M), slot_l_ear) + if(src.corpseglasses) + M.equip_to_slot_or_del(new src.corpseglasses(M), slot_glasses) + if(src.corpsemask) + M.equip_to_slot_or_del(new src.corpsemask(M), slot_wear_mask) + if(src.corpsebelt) + M.equip_to_slot_or_del(new src.corpsebelt(M), slot_belt) + if(src.corpsepocket1) + M.equip_to_slot_or_del(new src.corpsepocket1(M), slot_r_store) + if(src.corpsepocket2) + M.equip_to_slot_or_del(new src.corpsepocket2(M), slot_l_store) + if(src.corpseback) + M.equip_to_slot_or_del(new src.corpseback(M), slot_back) + if(src.corpseid == 1) + var/obj/item/weapon/card/id/W = new(M) + var/datum/job/jobdatum + for(var/jobtype in typesof(/datum/job)) + var/datum/job/J = new jobtype + if(J.title == corpseidaccess) + jobdatum = J + break + if(src.corpseidicon) + W.icon_state = corpseidicon + if(src.corpseidaccess) + if(jobdatum) + W.access = jobdatum.get_access() + else + W.access = list() + if(corpseidjob) + W.assignment = corpseidjob + M.set_id_info(W) + M.equip_to_slot_or_del(W, slot_wear_id) + // Do suit last to avoid equipping issues + if(src.corpsehelmet) + M.equip_voidhelm_to_slot_or_del_with_refit(new src.corpsehelmet(M), slot_head, src.species) + if(src.corpsesuit) + M.equip_voidsuit_to_slot_or_del_with_refit(new src.corpsesuit(M), slot_wear_suit, src.species) + + + +// I'll work on making a list of corpses people request for maps, or that I think will be commonly used. Syndicate operatives for example. + +/obj/effect/landmark/corpse/syndicatesoldier + name = "Mercenary" + corpseuniform = /obj/item/clothing/under/syndicate + corpsesuit = /obj/item/clothing/suit/armor/vest + corpseshoes = /obj/item/clothing/shoes/boots/swat + corpsegloves = /obj/item/clothing/gloves/swat + corpseradio = /obj/item/device/radio/headset + corpsemask = /obj/item/clothing/mask/gas + corpsehelmet = /obj/item/clothing/head/helmet/swat + corpseback = /obj/item/weapon/storage/backpack + corpseid = 1 + corpseidjob = "Operative" + corpseidaccess = "Syndicate" + +/obj/effect/landmark/corpse/syndicatecommando + name = "Mercenary Commando" + corpseuniform = /obj/item/clothing/under/syndicate + corpsesuit = /obj/item/clothing/suit/space/void/merc + corpseshoes = /obj/item/clothing/shoes/boots/swat + corpsegloves = /obj/item/clothing/gloves/swat + corpseradio = /obj/item/device/radio/headset + corpsemask = /obj/item/clothing/mask/gas/syndicate + corpsehelmet = /obj/item/clothing/head/helmet/space/void/merc + corpseback = /obj/item/weapon/tank/jetpack/oxygen + corpsepocket1 = /obj/item/weapon/tank/emergency/oxygen + corpseid = 1 + corpseidjob = "Operative" + corpseidaccess = "Syndicate" + +///////////Civilians////////////////////// + +/obj/effect/landmark/corpse/random_civ + name = "Civilian" + corpseuniform = /obj/item/clothing/under/color/grey + corpseshoes = /obj/item/clothing/shoes/black + random_species = TRUE + + + +/obj/effect/landmark/corpse/chef + name = "Chef" + corpseuniform = /obj/item/clothing/under/rank/chef + corpsesuit = /obj/item/clothing/suit/chef/classic + corpseshoes = /obj/item/clothing/shoes/black + corpsehelmet = /obj/item/clothing/head/chefhat + corpseback = /obj/item/weapon/storage/backpack + corpseradio = /obj/item/device/radio/headset + corpseid = 1 + corpseidjob = "Chef" + corpseidaccess = "Chef" + + +/obj/effect/landmark/corpse/doctor + name = "Doctor" + corpseradio = /obj/item/device/radio/headset/headset_med + corpseuniform = /obj/item/clothing/under/rank/medical + corpsesuit = /obj/item/clothing/suit/storage/toggle/labcoat + corpseback = /obj/item/weapon/storage/backpack/medic + corpsepocket1 = /obj/item/device/flashlight/pen + corpseshoes = /obj/item/clothing/shoes/black + corpseid = 1 + corpseidjob = "Medical Doctor" + corpseidaccess = "Medical Doctor" + +/obj/effect/landmark/corpse/engineer + name = "Engineer" + corpseradio = /obj/item/device/radio/headset/headset_eng + corpseuniform = /obj/item/clothing/under/rank/engineer + corpseback = /obj/item/weapon/storage/backpack/industrial + corpseshoes = /obj/item/clothing/shoes/orange + corpsebelt = /obj/item/weapon/storage/belt/utility/full + corpsegloves = /obj/item/clothing/gloves/yellow + corpsehelmet = /obj/item/clothing/head/hardhat + corpseid = 1 + corpseidjob = "Engineer" + corpseidaccess = "Engineer" + +/obj/effect/landmark/corpse/engineer/rig + corpsesuit = /obj/item/clothing/suit/space/void/engineering + corpsemask = /obj/item/clothing/mask/breath + corpsehelmet = /obj/item/clothing/head/helmet/space/void/engineering + corpseback = /obj/item/weapon/tank/oxygen + +/obj/effect/landmark/corpse/clown + name = "Clown" + corpseuniform = /obj/item/clothing/under/rank/clown + corpseshoes = /obj/item/clothing/shoes/clown_shoes + corpseradio = /obj/item/device/radio/headset + corpsemask = /obj/item/clothing/mask/gas/clown_hat + corpsepocket1 = /obj/item/weapon/bikehorn + corpseback = /obj/item/weapon/storage/backpack/clown + corpseid = 1 + corpseidjob = "Clown" + corpseidaccess = "Clown" + +/obj/effect/landmark/corpse/scientist + name = "Scientist" + corpseradio = /obj/item/device/radio/headset/headset_sci + corpseuniform = /obj/item/clothing/under/rank/scientist + corpsesuit = /obj/item/clothing/suit/storage/toggle/labcoat/science + corpseback = /obj/item/weapon/storage/backpack + corpseshoes = /obj/item/clothing/shoes/white + corpseid = 1 + corpseidjob = "Scientist" + corpseidaccess = "Scientist" + +/obj/effect/landmark/corpse/security + name = "Security Officer" + corpseradio = /obj/item/device/radio/headset/headset_sec + corpseuniform = /obj/item/clothing/under/rank/security + corpsesuit = /obj/item/clothing/suit/armor/vest + corpseback = /obj/item/weapon/storage/backpack/security + corpseshoes = /obj/item/clothing/shoes/boots/jackboots + corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud + corpsegloves = /obj/item/clothing/gloves/black + corpsehelmet = /obj/item/clothing/head/helmet + corpseid = 1 + corpseidjob = "Security Officer" + corpseidaccess = "Security Officer" + +/obj/effect/landmark/corpse/security/rig + corpsesuit = /obj/item/clothing/suit/space/void/security + corpsemask = /obj/item/clothing/mask/breath + corpsehelmet = /obj/item/clothing/head/helmet/space/void/security + corpseback = /obj/item/weapon/tank/jetpack/oxygen + +/obj/effect/landmark/corpse/security/rig/eva + corpsesuit = /obj/item/clothing/suit/space/void/security/alt + corpsehelmet = /obj/item/clothing/head/helmet/space/void/security/alt + corpseidjob = "Starship Security Officer" + +/obj/effect/landmark/corpse/prisoner + name = "Unknown Prisoner" + corpseuniform = /obj/item/clothing/under/color/prison + corpseshoes = /obj/item/clothing/shoes/orange + corpseid = 1 + corpseidjob = "Prisoner" + random_species = TRUE + +/obj/effect/landmark/corpse/miner + corpseradio = /obj/item/device/radio/headset/headset_cargo + corpseuniform = /obj/item/clothing/under/rank/miner + corpsegloves = /obj/item/clothing/gloves/black + corpseback = /obj/item/weapon/storage/backpack/industrial + corpseshoes = /obj/item/clothing/shoes/black + corpseid = 1 + corpseidjob = "Shaft Miner" + corpseidaccess = "Shaft Miner" + +/obj/effect/landmark/corpse/miner/rig + corpsesuit = /obj/item/clothing/suit/space/void/mining + corpsemask = /obj/item/clothing/mask/breath + corpsehelmet = /obj/item/clothing/head/helmet/space/void/mining + corpseback = /obj/item/weapon/tank/oxygen + +/////////////////Vintage////////////////////// + +//define the basic props at this level and only change specifics for variants, e.z. +/obj/effect/landmark/corpse/vintage + name = "Unknown Crewmate" + corpseuniform = /obj/item/clothing/under/utility + corpsesuit = /obj/item/clothing/suit/space/void/refurb + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb + corpsemask = /obj/item/clothing/mask/breath + corpseback = /obj/item/weapon/tank/oxygen + corpseid = 1 + corpseidjob = "Crewmate" + +/obj/effect/landmark/corpse/vintage/engineering + name = "Unknown Engineer" + corpsesuit = /obj/item/clothing/suit/space/void/refurb/engineering + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/engineering + corpsebelt = /obj/item/weapon/storage/belt/utility/full + corpseback = /obj/item/weapon/tank/oxygen/yellow + corpseidjob = "Engineer" + +/obj/effect/landmark/corpse/vintage/marine + name = "Unknown Marine" + corpsesuit = /obj/item/clothing/suit/space/void/refurb/marine + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/marine + corpsebelt = /obj/item/weapon/storage/belt/security/tactical + corpseidjob = "Marine" + +/obj/effect/landmark/corpse/vintage/medical + name = "Unknown Medic" + corpsesuit = /obj/item/clothing/suit/space/void/refurb/medical + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/medical + corpsebelt = /obj/item/weapon/storage/belt/medical + corpseidjob = "Medic" + +/obj/effect/landmark/corpse/vintage/mercenary + name = "Unknown Mercenary" + corpsesuit = /obj/item/clothing/suit/space/void/refurb/mercenary + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/mercenary + corpsebelt = /obj/item/weapon/storage/belt/security/tactical + corpseback = /obj/item/weapon/tank/oxygen/red + corpseidjob = "Mercenary" + +/obj/effect/landmark/corpse/vintage/officer + name = "Unknown Captain" + corpsesuit = /obj/item/clothing/suit/space/void/refurb/officer + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/officer + corpseback = /obj/item/weapon/tank/oxygen/yellow + corpseidjob = "Captain" + +/obj/effect/landmark/corpse/vintage/pilot + name = "Unknown Pilot" + corpsesuit = /obj/item/clothing/suit/space/void/refurb/pilot + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/pilot + corpseidjob = "Pilot" + +/obj/effect/landmark/corpse/vintage/research + name = "Unknown Researcher" + corpsesuit = /obj/item/clothing/suit/space/void/refurb/research + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/research + corpseidjob = "Researcher" + +/////////////////Officers////////////////////// + +/obj/effect/landmark/corpse/bridgeofficer + name = "Bridge Officer" + corpseradio = /obj/item/device/radio/headset/heads/hop + corpseuniform = /obj/item/clothing/under/rank/centcom_officer + corpsesuit = /obj/item/clothing/suit/armor/bulletproof + corpseshoes = /obj/item/clothing/shoes/black + corpseglasses = /obj/item/clothing/glasses/sunglasses + corpseid = 1 + corpseidjob = "Bridge Officer" + corpseidaccess = "Captain" + +/obj/effect/landmark/corpse/commander + name = "Commander" + corpseuniform = /obj/item/clothing/under/rank/centcom_captain + corpsesuit = /obj/item/clothing/suit/armor/bulletproof + corpseradio = /obj/item/device/radio/headset/heads/captain + corpseglasses = /obj/item/clothing/glasses/eyepatch + corpsemask = /obj/item/clothing/mask/smokable/cigarette/cigar/cohiba + corpsehelmet = /obj/item/clothing/head/centhat + corpsegloves = /obj/item/clothing/gloves/swat + corpseshoes = /obj/item/clothing/shoes/boots/swat + corpsepocket1 = /obj/item/weapon/flame/lighter/zippo + corpseid = 1 + corpseidjob = "Commander" + corpseidaccess = "Captain" + +/////////////////Lore Factions////////////////////// + +/obj/effect/landmark/corpse/sifguard + name = "Patrolman" + corpseuniform = /obj/item/clothing/under/solgov/utility/sifguard + corpsesuit = /obj/item/clothing/suit/storage/hooded/wintercoat/solgov + corpsebelt = /obj/item/weapon/storage/belt/security/tactical + corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud + corpsemask = /obj/item/clothing/mask/balaclava + corpsehelmet = /obj/item/clothing/head/beret/solgov/sifguard + corpsegloves = /obj/item/clothing/gloves/duty + corpseshoes = /obj/item/clothing/shoes/boots/tactical + corpsepocket1 = /obj/item/clothing/accessory/armor/tag/sifguard + corpseid = 1 + corpseidjob = "Sif Defense Force Patrolman" + +/obj/effect/landmark/corpse/hedberg + name = "Hedberg-Hammarstrom Mercenary" + corpseuniform = /obj/item/clothing/under/solgov/utility/sifguard + corpsesuit = /obj/item/clothing/suit/storage/vest/solgov/hedberg + corpsebelt = /obj/item/weapon/storage/belt/security + corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud + corpsehelmet = /obj/item/clothing/head/beret/corp/hedberg + corpseshoes = /obj/item/clothing/shoes/boots/jackboots + corpseid = 1 + corpseidjob = "Hedberg-Hammarstrom Officer" + +/obj/effect/landmark/corpse/hedberg/merc + name = "Hedberg-Hammarstrom Mercenary" + corpsebelt = /obj/item/weapon/storage/belt/security/tactical + corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud + corpsehelmet = /obj/item/clothing/head/helmet/flexitac + corpsegloves = /obj/item/clothing/gloves/combat + corpseshoes = /obj/item/clothing/shoes/boots/tactical + corpseid = 1 + corpseidjob = "Hedberg-Hammarstrom Enforcer" + diff --git a/code/modules/awaymissions/exile.dm b/code/modules/awaymissions/exile.dm index ef19bb0c49c..1292812a77e 100644 --- a/code/modules/awaymissions/exile.dm +++ b/code/modules/awaymissions/exile.dm @@ -1,41 +1,41 @@ -//////Exile implants will allow you to use the station gate, but not return home. This will allow security to exile badguys/for badguys to exile their kill targets//////// - - -/obj/item/weapon/implanter/exile - name = "implanter-exile" - -/obj/item/weapon/implanter/exile/New() - src.imp = new /obj/item/weapon/implant/exile( src ) - ..() - update() - return - - -/obj/item/weapon/implant/exile - name = "exile" - desc = "Prevents you from returning from away missions" - -/obj/item/weapon/implant/exile/get_data() - var/dat = {" -Implant Specifications:
                    -Name: [using_map.company_name] Employee Exile Implant
                    -Implant Details: The onboard gateway system has been modified to reject entry by individuals containing this implant
                    "} - return dat - -/obj/item/weapon/implantcase/exile - name = "Glass Case- 'Exile'" - desc = "A case containing an exile implant." - icon = 'icons/obj/items.dmi' - icon_state = "implantcase-r" - - -/obj/item/weapon/implantcase/exile/New() - src.imp = new /obj/item/weapon/implant/exile( src ) - ..() - return - - -/obj/structure/closet/secure_closet/exile - name = "Exile Implants" - req_access = list(access_hos) - starts_with = list(/obj/item/weapon/implanter/exile = 1, /obj/item/weapon/implantcase/exile = 5) +//////Exile implants will allow you to use the station gate, but not return home. This will allow security to exile badguys/for badguys to exile their kill targets//////// + + +/obj/item/weapon/implanter/exile + name = "implanter-exile" + +/obj/item/weapon/implanter/exile/New() + src.imp = new /obj/item/weapon/implant/exile( src ) + ..() + update() + return + + +/obj/item/weapon/implant/exile + name = "exile" + desc = "Prevents you from returning from away missions" + +/obj/item/weapon/implant/exile/get_data() + var/dat = {" +Implant Specifications:
                    +Name: [using_map.company_name] Employee Exile Implant
                    +Implant Details: The onboard gateway system has been modified to reject entry by individuals containing this implant
                    "} + return dat + +/obj/item/weapon/implantcase/exile + name = "Glass Case- 'Exile'" + desc = "A case containing an exile implant." + icon = 'icons/obj/items.dmi' + icon_state = "implantcase-r" + + +/obj/item/weapon/implantcase/exile/New() + src.imp = new /obj/item/weapon/implant/exile( src ) + ..() + return + + +/obj/structure/closet/secure_closet/exile + name = "Exile Implants" + req_access = list(access_hos) + starts_with = list(/obj/item/weapon/implanter/exile = 1, /obj/item/weapon/implantcase/exile = 5) diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm index 156002b3c05..66d36212615 100644 --- a/code/modules/awaymissions/gateway.dm +++ b/code/modules/awaymissions/gateway.dm @@ -1,367 +1,367 @@ -/obj/machinery/gateway - name = "gateway" - desc = "A mysterious gateway built by unknown hands. It allows for faster than light travel to far-flung locations and even alternate realities." //VOREStation Edit - icon = 'icons/obj/machines/gateway.dmi' - icon_state = "off" - density = TRUE - anchored = TRUE - unacidable = TRUE - var/active = 0 - - -/obj/machinery/gateway/Initialize() - update_icon() - if(dir == SOUTH) - density = FALSE - . = ..() - -/obj/machinery/gateway/update_icon() - if(active) - icon_state = "on" - return - icon_state = "off" - - - -//this is da important part wot makes things go -GLOBAL_DATUM(gateway_station, /obj/machinery/gateway/centerstation) -/obj/machinery/gateway/centerstation - density = TRUE - icon_state = "offcenter" - use_power = USE_POWER_IDLE - - //warping vars - var/list/linked = list() - var/ready = 0 //have we got all the parts for a gateway? - var/wait = 0 //this just grabs world.time at world start - var/obj/machinery/gateway/centeraway/awaygate = null - -/obj/machinery/gateway/centerstation/Initialize() - if(GLOB.gateway_station) - warning("[src] at [x],[y],[z] appears to be an additional station-gateway") - else - GLOB.gateway_station = src - - update_icon() - wait = world.time + config.gateway_delay //+ thirty minutes default - - if(GLOB.gateway_away) - awaygate = GLOB.gateway_away - else - awaygate = locate(/obj/machinery/gateway/centeraway) - - . = ..() - density = TRUE //VOREStation Add - -/obj/machinery/gateway/centerstation/Destroy() - if(awaygate?.stationgate == src) - awaygate.stationgate = null - if(GLOB.gateway_station == src) - GLOB.gateway_station = null - return ..() - -/obj/machinery/gateway/centerstation/update_icon() - if(active) - icon_state = "oncenter" - return - icon_state = "offcenter" -/* VOREStation Removal - Doesn't do anything -/obj/machinery/gateway/centerstation/New() - density = TRUE -*/ //VOREStation Removal End - -/obj/machinery/gateway/centerstation/process() - if(stat & (NOPOWER)) - if(active) toggleoff() - return - - if(active) - use_power(5000) - - -/obj/machinery/gateway/centerstation/proc/detect() - linked = list() //clear the list - var/turf/T = loc - - for(var/i in alldirs) - T = get_step(loc, i) - var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T - if(G) - linked.Add(G) - continue - - //this is only done if we fail to find a part - ready = 0 - toggleoff() - break - - if(linked.len == 8) - ready = 1 - - -/obj/machinery/gateway/centerstation/proc/toggleon(mob/user as mob) - if(!ready) return - if(linked.len != 8) return - if(!powered()) return - if(!awaygate) - to_chat(user, "Error: No destination found. Please program gateway.") - return - if(world.time < wait) - to_chat(user, "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes.") - return - if(!awaygate.calibrated && !LAZYLEN(awaydestinations)) //VOREStation Edit - to_chat(user, "Error: Destination gate uncalibrated. Gateway unsafe to use without far-end calibration update.") - return - - for(var/obj/machinery/gateway/G in linked) - G.active = 1 - G.update_icon() - active = 1 - update_icon() - - -/obj/machinery/gateway/centerstation/proc/toggleoff() - for(var/obj/machinery/gateway/G in linked) - G.active = 0 - G.update_icon() - active = 0 - update_icon() - - -/obj/machinery/gateway/centerstation/attack_hand(mob/user as mob) - if(!ready) - detect() - return - if(!active) - toggleon(user) - return - toggleoff() - - -//okay, here's the good teleporting stuff -/obj/machinery/gateway/centerstation/Bumped(atom/movable/M as mob|obj) - if(!ready) return - if(!active) return - if(!awaygate) return - - use_power(5000) - M << 'sound/effects/phasein.ogg' - playsound(src, 'sound/effects/phasein.ogg', 100, 1) - if(awaygate.calibrated) - M.forceMove(get_step(awaygate.loc, SOUTH)) - M.set_dir(SOUTH) - return - else - //VOREStation Addition Start: Prevent abuse - if(istype(M, /obj/item/device/uav)) - var/obj/item/device/uav/L = M - L.power_down() - if(istype(M, /mob/living)) - var/mob/living/L = M - if(LAZYLEN(L.buckled_mobs)) - var/datum/riding/R = L.riding_datum - for(var/rider in L.buckled_mobs) - R.force_dismount(rider) - //VOREStation Addition End: Prevent abuse - var/obj/effect/landmark/dest = pick(awaydestinations) - if(dest) - M.forceMove(dest.loc) - M.set_dir(SOUTH) - //VOREStation Addition Start: Mcguffin time! - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.client) - awaygate.entrydetect() - //VOREStation Addition End: Mcguffin time! - - //VOREStation Addition Start: Abduction! - if(istype(M, /mob/living) && dest.abductor) - var/mob/living/L = M - if(L.nutrition > 500) - L.nutrition = 500 //If the aim is to negate people overpreparing, then they shouldn't be able to stuff themselves full of food either. - //Situations to get the mob out of - if(L.buckled) - L.buckled.unbuckle_mob() - if(istype(L.loc,/obj/mecha)) - var/obj/mecha/ME = L.loc - ME.go_out() - else if(istype(L.loc,/obj/machinery/sleeper)) - var/obj/machinery/sleeper/SL = L.loc - SL.go_out() - else if(istype(L.loc,/obj/machinery/recharge_station)) - var/obj/machinery/recharge_station/RS = L.loc - RS.go_out() - if(!issilicon(L)) //Don't drop borg modules... - var/list/mob_contents = list() //Things which are actually drained as a result of the above not being null. - mob_contents |= L // The recursive check below does not add the object being checked to its list. - mob_contents |= recursive_content_check(L, mob_contents, recursion_limit = 3, client_check = 0, sight_check = 0, include_mobs = 1, include_objects = 1, ignore_show_messages = 1) - for(var/obj/item/weapon/holder/I in mob_contents) - var/obj/item/weapon/holder/H = I - var/mob/living/MI = H.held_mob - MI.forceMove(get_turf(H)) - if(!issilicon(MI)) //Don't drop borg modules... - for(var/obj/item/II in MI) - if(istype(II,/obj/item/weapon/implant) || istype(II,/obj/item/device/nif)) - continue - MI.drop_from_inventory(II, dest.loc) - var/obj/effect/landmark/finaldest = pick(awayabductors) - MI.forceMove(finaldest.loc) - sleep(1) - MI.Paralyse(10) - MI << 'sound/effects/bamf.ogg' - to_chat(MI,"You're starting to come to. You feel like you've been out for a few minutes, at least...") - for(var/obj/item/I in L) - if(istype(I,/obj/item/weapon/implant) || istype(I,/obj/item/device/nif)) - continue - L.drop_from_inventory(I, dest.loc) - var/obj/effect/landmark/finaldest = pick(awayabductors) - L.forceMove(finaldest.loc) - sleep(1) - L.Paralyse(10) - L << 'sound/effects/bamf.ogg' - to_chat(L,"You're starting to come to. You feel like you've been out for a few minutes, at least...") - //VOREStation Addition End - return - -/obj/machinery/gateway/centerstation/attackby(obj/item/device/W as obj, mob/user as mob) - if(istype(W,/obj/item/device/multitool)) - if(!awaygate) - if(GLOB.gateway_away) - awaygate = GLOB.gateway_away - else - awaygate = locate(/obj/machinery/gateway/centeraway) - if(!awaygate) // We still can't find the damn thing because there is no destination. - to_chat(user, "Error: Programming failed. No destination found.") - return - to_chat(user, "Startup programming successful!: A destination in another point of space and time has been detected.") - else - to_chat(user, span_black("The gate is already calibrated, there is no work for you to do here.")) - return - -/////////////////////////////////////Away//////////////////////// -GLOBAL_DATUM(gateway_away, /obj/machinery/gateway/centeraway) -/obj/machinery/gateway/centeraway - density = TRUE - icon_state = "offcenter" - use_power = USE_POWER_OFF - var/calibrated = 1 - var/list/linked = list() //a list of the connected gateway chunks - var/ready = 0 - var/obj/machinery/gateway/centerstation/stationgate = null - -/obj/machinery/gateway/centeraway/New() - density = TRUE - -/obj/machinery/gateway/centeraway/Initialize() - if(GLOB.gateway_away) - warning("[src] at [x],[y],[z] appears to be an additional away-gateway") - else - GLOB.gateway_away = src - - update_icon() - - if(GLOB.gateway_station) - stationgate = GLOB.gateway_station - else - stationgate = locate(/obj/machinery/gateway/centerstation) - . = ..() - density = TRUE //VOREStation Add - -/obj/machinery/gateway/centeraway/Destroy() - if(stationgate?.awaygate == src) - stationgate.awaygate = null - if(GLOB.gateway_away == src) - GLOB.gateway_away = null - return ..() - -/obj/machinery/gateway/centeraway/update_icon() - if(active) - icon_state = "oncenter" - return - icon_state = "offcenter" - -/obj/machinery/gateway/centeraway/proc/detect() - linked = list() //clear the list - var/turf/T = loc - - for(var/i in alldirs) - T = get_step(loc, i) - var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T - if(G) - linked.Add(G) - continue - - //this is only done if we fail to find a part - ready = 0 - toggleoff() - break - - if(linked.len == 8) - ready = 1 - - -/obj/machinery/gateway/centeraway/proc/toggleon(mob/user as mob) - if(!ready) return - if(linked.len != 8) return - if(!stationgate || !calibrated) // Vorestation edit. Not like Polaris ever touches this anyway. - to_chat(user, "Error: No destination found. Please calibrate gateway.") - return - - for(var/obj/machinery/gateway/G in linked) - G.active = 1 - G.update_icon() - active = 1 - update_icon() - - -/obj/machinery/gateway/centeraway/proc/toggleoff() - for(var/obj/machinery/gateway/G in linked) - G.active = 0 - G.update_icon() - active = 0 - update_icon() - - -/obj/machinery/gateway/centeraway/attack_hand(mob/user as mob) - if(!ready) - detect() - return - if(!active) - toggleon(user) - return - toggleoff() - - -/obj/machinery/gateway/centeraway/Bumped(atom/movable/M as mob|obj) - if(!ready) return - if(!active) return - if(istype(M, /mob/living/carbon)) - for(var/obj/item/weapon/implant/exile/E in M)//Checking that there is an exile implant in the contents - if(E.imp_in == M)//Checking that it's actually implanted vs just in their pocket - to_chat(M, span_black("The station gate has detected your exile implant and is blocking your entry.")) - return - M.forceMove(get_step(stationgate.loc, SOUTH)) - M.set_dir(SOUTH) - M << 'sound/effects/phasein.ogg' - playsound(src, 'sound/effects/phasein.ogg', 100, 1) - - -/obj/machinery/gateway/centeraway/attackby(obj/item/device/W as obj, mob/user as mob) - if(istype(W,/obj/item/device/multitool)) - if(calibrated && stationgate) - to_chat(user, span_black("The gate is already calibrated, there is no work for you to do here.")) - return - else - // VOREStation Add - if(GLOB.gateway_station) - stationgate = GLOB.gateway_station - else - stationgate = locate(/obj/machinery/gateway/centerstation) - if(!stationgate) - to_chat(user, "Error: Recalibration failed. No destination found... That can't be good.") - return - // VOREStation Add End - else - to_chat(user, span_blue("Recalibration successful!:") + span_black(" This gate's systems have been fine tuned. Travel to this gate will now be on target.")) - calibrated = 1 - return +/obj/machinery/gateway + name = "gateway" + desc = "A mysterious gateway built by unknown hands. It allows for faster than light travel to far-flung locations and even alternate realities." //VOREStation Edit + icon = 'icons/obj/machines/gateway.dmi' + icon_state = "off" + density = TRUE + anchored = TRUE + unacidable = TRUE + var/active = 0 + + +/obj/machinery/gateway/Initialize() + update_icon() + if(dir == SOUTH) + density = FALSE + . = ..() + +/obj/machinery/gateway/update_icon() + if(active) + icon_state = "on" + return + icon_state = "off" + + + +//this is da important part wot makes things go +GLOBAL_DATUM(gateway_station, /obj/machinery/gateway/centerstation) +/obj/machinery/gateway/centerstation + density = TRUE + icon_state = "offcenter" + use_power = USE_POWER_IDLE + + //warping vars + var/list/linked = list() + var/ready = 0 //have we got all the parts for a gateway? + var/wait = 0 //this just grabs world.time at world start + var/obj/machinery/gateway/centeraway/awaygate = null + +/obj/machinery/gateway/centerstation/Initialize() + if(GLOB.gateway_station) + warning("[src] at [x],[y],[z] appears to be an additional station-gateway") + else + GLOB.gateway_station = src + + update_icon() + wait = world.time + config.gateway_delay //+ thirty minutes default + + if(GLOB.gateway_away) + awaygate = GLOB.gateway_away + else + awaygate = locate(/obj/machinery/gateway/centeraway) + + . = ..() + density = TRUE //VOREStation Add + +/obj/machinery/gateway/centerstation/Destroy() + if(awaygate?.stationgate == src) + awaygate.stationgate = null + if(GLOB.gateway_station == src) + GLOB.gateway_station = null + return ..() + +/obj/machinery/gateway/centerstation/update_icon() + if(active) + icon_state = "oncenter" + return + icon_state = "offcenter" +/* VOREStation Removal - Doesn't do anything +/obj/machinery/gateway/centerstation/New() + density = TRUE +*/ //VOREStation Removal End + +/obj/machinery/gateway/centerstation/process() + if(stat & (NOPOWER)) + if(active) toggleoff() + return + + if(active) + use_power(5000) + + +/obj/machinery/gateway/centerstation/proc/detect() + linked = list() //clear the list + var/turf/T = loc + + for(var/i in alldirs) + T = get_step(loc, i) + var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T + if(G) + linked.Add(G) + continue + + //this is only done if we fail to find a part + ready = 0 + toggleoff() + break + + if(linked.len == 8) + ready = 1 + + +/obj/machinery/gateway/centerstation/proc/toggleon(mob/user as mob) + if(!ready) return + if(linked.len != 8) return + if(!powered()) return + if(!awaygate) + to_chat(user, "Error: No destination found. Please program gateway.") + return + if(world.time < wait) + to_chat(user, "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes.") + return + if(!awaygate.calibrated && !LAZYLEN(awaydestinations)) //VOREStation Edit + to_chat(user, "Error: Destination gate uncalibrated. Gateway unsafe to use without far-end calibration update.") + return + + for(var/obj/machinery/gateway/G in linked) + G.active = 1 + G.update_icon() + active = 1 + update_icon() + + +/obj/machinery/gateway/centerstation/proc/toggleoff() + for(var/obj/machinery/gateway/G in linked) + G.active = 0 + G.update_icon() + active = 0 + update_icon() + + +/obj/machinery/gateway/centerstation/attack_hand(mob/user as mob) + if(!ready) + detect() + return + if(!active) + toggleon(user) + return + toggleoff() + + +//okay, here's the good teleporting stuff +/obj/machinery/gateway/centerstation/Bumped(atom/movable/M as mob|obj) + if(!ready) return + if(!active) return + if(!awaygate) return + + use_power(5000) + M << 'sound/effects/phasein.ogg' + playsound(src, 'sound/effects/phasein.ogg', 100, 1) + if(awaygate.calibrated) + M.forceMove(get_step(awaygate.loc, SOUTH)) + M.set_dir(SOUTH) + return + else + //VOREStation Addition Start: Prevent abuse + if(istype(M, /obj/item/device/uav)) + var/obj/item/device/uav/L = M + L.power_down() + if(istype(M, /mob/living)) + var/mob/living/L = M + if(LAZYLEN(L.buckled_mobs)) + var/datum/riding/R = L.riding_datum + for(var/rider in L.buckled_mobs) + R.force_dismount(rider) + //VOREStation Addition End: Prevent abuse + var/obj/effect/landmark/dest = pick(awaydestinations) + if(dest) + M.forceMove(dest.loc) + M.set_dir(SOUTH) + //VOREStation Addition Start: Mcguffin time! + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.client) + awaygate.entrydetect() + //VOREStation Addition End: Mcguffin time! + + //VOREStation Addition Start: Abduction! + if(istype(M, /mob/living) && dest.abductor) + var/mob/living/L = M + if(L.nutrition > 500) + L.nutrition = 500 //If the aim is to negate people overpreparing, then they shouldn't be able to stuff themselves full of food either. + //Situations to get the mob out of + if(L.buckled) + L.buckled.unbuckle_mob() + if(istype(L.loc,/obj/mecha)) + var/obj/mecha/ME = L.loc + ME.go_out() + else if(istype(L.loc,/obj/machinery/sleeper)) + var/obj/machinery/sleeper/SL = L.loc + SL.go_out() + else if(istype(L.loc,/obj/machinery/recharge_station)) + var/obj/machinery/recharge_station/RS = L.loc + RS.go_out() + if(!issilicon(L)) //Don't drop borg modules... + var/list/mob_contents = list() //Things which are actually drained as a result of the above not being null. + mob_contents |= L // The recursive check below does not add the object being checked to its list. + mob_contents |= recursive_content_check(L, mob_contents, recursion_limit = 3, client_check = 0, sight_check = 0, include_mobs = 1, include_objects = 1, ignore_show_messages = 1) + for(var/obj/item/weapon/holder/I in mob_contents) + var/obj/item/weapon/holder/H = I + var/mob/living/MI = H.held_mob + MI.forceMove(get_turf(H)) + if(!issilicon(MI)) //Don't drop borg modules... + for(var/obj/item/II in MI) + if(istype(II,/obj/item/weapon/implant) || istype(II,/obj/item/device/nif)) + continue + MI.drop_from_inventory(II, dest.loc) + var/obj/effect/landmark/finaldest = pick(awayabductors) + MI.forceMove(finaldest.loc) + sleep(1) + MI.Paralyse(10) + MI << 'sound/effects/bamf.ogg' + to_chat(MI,"You're starting to come to. You feel like you've been out for a few minutes, at least...") + for(var/obj/item/I in L) + if(istype(I,/obj/item/weapon/implant) || istype(I,/obj/item/device/nif)) + continue + L.drop_from_inventory(I, dest.loc) + var/obj/effect/landmark/finaldest = pick(awayabductors) + L.forceMove(finaldest.loc) + sleep(1) + L.Paralyse(10) + L << 'sound/effects/bamf.ogg' + to_chat(L,"You're starting to come to. You feel like you've been out for a few minutes, at least...") + //VOREStation Addition End + return + +/obj/machinery/gateway/centerstation/attackby(obj/item/device/W as obj, mob/user as mob) + if(istype(W,/obj/item/device/multitool)) + if(!awaygate) + if(GLOB.gateway_away) + awaygate = GLOB.gateway_away + else + awaygate = locate(/obj/machinery/gateway/centeraway) + if(!awaygate) // We still can't find the damn thing because there is no destination. + to_chat(user, "Error: Programming failed. No destination found.") + return + to_chat(user, "Startup programming successful!: A destination in another point of space and time has been detected.") + else + to_chat(user, span_black("The gate is already calibrated, there is no work for you to do here.")) + return + +/////////////////////////////////////Away//////////////////////// +GLOBAL_DATUM(gateway_away, /obj/machinery/gateway/centeraway) +/obj/machinery/gateway/centeraway + density = TRUE + icon_state = "offcenter" + use_power = USE_POWER_OFF + var/calibrated = 1 + var/list/linked = list() //a list of the connected gateway chunks + var/ready = 0 + var/obj/machinery/gateway/centerstation/stationgate = null + +/obj/machinery/gateway/centeraway/New() + density = TRUE + +/obj/machinery/gateway/centeraway/Initialize() + if(GLOB.gateway_away) + warning("[src] at [x],[y],[z] appears to be an additional away-gateway") + else + GLOB.gateway_away = src + + update_icon() + + if(GLOB.gateway_station) + stationgate = GLOB.gateway_station + else + stationgate = locate(/obj/machinery/gateway/centerstation) + . = ..() + density = TRUE //VOREStation Add + +/obj/machinery/gateway/centeraway/Destroy() + if(stationgate?.awaygate == src) + stationgate.awaygate = null + if(GLOB.gateway_away == src) + GLOB.gateway_away = null + return ..() + +/obj/machinery/gateway/centeraway/update_icon() + if(active) + icon_state = "oncenter" + return + icon_state = "offcenter" + +/obj/machinery/gateway/centeraway/proc/detect() + linked = list() //clear the list + var/turf/T = loc + + for(var/i in alldirs) + T = get_step(loc, i) + var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T + if(G) + linked.Add(G) + continue + + //this is only done if we fail to find a part + ready = 0 + toggleoff() + break + + if(linked.len == 8) + ready = 1 + + +/obj/machinery/gateway/centeraway/proc/toggleon(mob/user as mob) + if(!ready) return + if(linked.len != 8) return + if(!stationgate || !calibrated) // Vorestation edit. Not like Polaris ever touches this anyway. + to_chat(user, "Error: No destination found. Please calibrate gateway.") + return + + for(var/obj/machinery/gateway/G in linked) + G.active = 1 + G.update_icon() + active = 1 + update_icon() + + +/obj/machinery/gateway/centeraway/proc/toggleoff() + for(var/obj/machinery/gateway/G in linked) + G.active = 0 + G.update_icon() + active = 0 + update_icon() + + +/obj/machinery/gateway/centeraway/attack_hand(mob/user as mob) + if(!ready) + detect() + return + if(!active) + toggleon(user) + return + toggleoff() + + +/obj/machinery/gateway/centeraway/Bumped(atom/movable/M as mob|obj) + if(!ready) return + if(!active) return + if(istype(M, /mob/living/carbon)) + for(var/obj/item/weapon/implant/exile/E in M)//Checking that there is an exile implant in the contents + if(E.imp_in == M)//Checking that it's actually implanted vs just in their pocket + to_chat(M, span_black("The station gate has detected your exile implant and is blocking your entry.")) + return + M.forceMove(get_step(stationgate.loc, SOUTH)) + M.set_dir(SOUTH) + M << 'sound/effects/phasein.ogg' + playsound(src, 'sound/effects/phasein.ogg', 100, 1) + + +/obj/machinery/gateway/centeraway/attackby(obj/item/device/W as obj, mob/user as mob) + if(istype(W,/obj/item/device/multitool)) + if(calibrated && stationgate) + to_chat(user, span_black("The gate is already calibrated, there is no work for you to do here.")) + return + else + // VOREStation Add + if(GLOB.gateway_station) + stationgate = GLOB.gateway_station + else + stationgate = locate(/obj/machinery/gateway/centerstation) + if(!stationgate) + to_chat(user, "Error: Recalibration failed. No destination found... That can't be good.") + return + // VOREStation Add End + else + to_chat(user, span_blue("Recalibration successful!:") + span_black(" This gate's systems have been fine tuned. Travel to this gate will now be on target.")) + calibrated = 1 + return diff --git a/code/modules/awaymissions/gateway_vr.dm b/code/modules/awaymissions/gateway_vr.dm index 25a57ce7d7c..05def055827 100644 --- a/code/modules/awaymissions/gateway_vr.dm +++ b/code/modules/awaymissions/gateway_vr.dm @@ -1,79 +1,79 @@ -//This gateway type takes a special item that you will have to find on the map to activate, instead of using a multitool// - -/obj/machinery/gateway/centeraway/mcguffin - icon = 'icons/obj/machines/gateway_vr.dmi' - calibrated = 0 - var/mcguffin_type = /obj/item/device/mcguffin/brass //you should be able to change the var to be whatever kind of path you like, so maybe you can use other things on it sometimes - var/key //holds a ref to the key we spawned - -/obj/machinery/gateway/centeraway/mcguffin/attackby(obj/item/device/W as obj, mob/user as mob) - if(calibrated && stationgate) - to_chat(user, "The gate is already configured, you should be able to activate it.") - return - else if(!stationgate) - to_chat(user, "Error: Configuration failed. No destination found... That can't be good.") - return - - if(istype(W,mcguffin_type) && !calibrated) - to_chat(user, "As the device nears the gateway, mechanical clunks and whirrs can be heard.
                    [span_blue("Configuration successful! ")]
                    This gate's systems have been fine tuned. Travel to this gate will now be on target.
                    ") - calibrated = 1 - return - else - to_chat(user, "This device does not seem to interface correctly with the gateway. Perhaps you should try something else.") - return - -//If you use this kind of gateway you NEED one of these on the map or the players won't be able to leave// -//You should use the random spawner though so it won't always be in the same place// -/obj/item/device/mcguffin/brass - name = "mysterious brass device" - desc = "A curious object made of what appears to be brass and silver. Its purpose is unclear by looking at it. Perhaps it should be used with something of similar materials?" - icon = 'icons/obj/machines/gateway_vr.dmi' - icon_state = "mcguffin" - drop_sound = 'sound/items/drop/wrench.ogg' - pickup_sound = 'sound/items/pickup/wrench.ogg' - -/obj/effect/landmark/mcguffin_spawner - name = "gateway key spawner" - icon = 'icons/mob/randomlandmarks.dmi' - icon_state = "key" - -/obj/machinery/gateway/centeraway/proc/entrydetect() - return - -/obj/machinery/gateway/centeraway/mcguffin/entrydetect() - if(key) - return - - var/list/spawners = list() - for(var/obj/effect/landmark/mcguffin_spawner/sp in world) - spawners += sp - - var/obj/effect/landmark/mcguffin_spawner/the_cool_one = pick(spawners) - - var/atom/destination = get_turf(the_cool_one) - var/obj/structure/closet/CL = locate() in destination - if(CL) - destination = CL - - if(!destination) - warning("A gateway is trying to spawn it's mcguffin but there are no mapped in spawner landmarks") - destination = get_turf(src) - - key = new mcguffin_type(destination) - -/obj/machinery/gateway/centeraway/mcguffin/Bumped(atom/movable/M as mob|obj) - if(!ready) return - if(!active) return - M.forceMove(get_step(stationgate.loc, SOUTH)) - M.set_dir(SOUTH) - M << 'sound/effects/swooshygate.ogg' - playsound(src, 'sound/effects/swooshygate.ogg', 100, 1) - -/obj/machinery/gateway/brass - name = "mysterious brass gateway" - desc = "A gateway of strange construction. It appears to be made primarily of materials resembling brass and silver." - icon = 'icons/obj/machines/gateway_vr.dmi' - -//No, you can't digest the key to leave the gateway. -/obj/item/device/mcguffin/digest_act(var/atom/movable/item_storage = null) - return FALSE +//This gateway type takes a special item that you will have to find on the map to activate, instead of using a multitool// + +/obj/machinery/gateway/centeraway/mcguffin + icon = 'icons/obj/machines/gateway_vr.dmi' + calibrated = 0 + var/mcguffin_type = /obj/item/device/mcguffin/brass //you should be able to change the var to be whatever kind of path you like, so maybe you can use other things on it sometimes + var/key //holds a ref to the key we spawned + +/obj/machinery/gateway/centeraway/mcguffin/attackby(obj/item/device/W as obj, mob/user as mob) + if(calibrated && stationgate) + to_chat(user, "The gate is already configured, you should be able to activate it.") + return + else if(!stationgate) + to_chat(user, "Error: Configuration failed. No destination found... That can't be good.") + return + + if(istype(W,mcguffin_type) && !calibrated) + to_chat(user, "As the device nears the gateway, mechanical clunks and whirrs can be heard.
                    [span_blue("Configuration successful! ")]
                    This gate's systems have been fine tuned. Travel to this gate will now be on target.
                    ") + calibrated = 1 + return + else + to_chat(user, "This device does not seem to interface correctly with the gateway. Perhaps you should try something else.") + return + +//If you use this kind of gateway you NEED one of these on the map or the players won't be able to leave// +//You should use the random spawner though so it won't always be in the same place// +/obj/item/device/mcguffin/brass + name = "mysterious brass device" + desc = "A curious object made of what appears to be brass and silver. Its purpose is unclear by looking at it. Perhaps it should be used with something of similar materials?" + icon = 'icons/obj/machines/gateway_vr.dmi' + icon_state = "mcguffin" + drop_sound = 'sound/items/drop/wrench.ogg' + pickup_sound = 'sound/items/pickup/wrench.ogg' + +/obj/effect/landmark/mcguffin_spawner + name = "gateway key spawner" + icon = 'icons/mob/randomlandmarks.dmi' + icon_state = "key" + +/obj/machinery/gateway/centeraway/proc/entrydetect() + return + +/obj/machinery/gateway/centeraway/mcguffin/entrydetect() + if(key) + return + + var/list/spawners = list() + for(var/obj/effect/landmark/mcguffin_spawner/sp in world) + spawners += sp + + var/obj/effect/landmark/mcguffin_spawner/the_cool_one = pick(spawners) + + var/atom/destination = get_turf(the_cool_one) + var/obj/structure/closet/CL = locate() in destination + if(CL) + destination = CL + + if(!destination) + warning("A gateway is trying to spawn it's mcguffin but there are no mapped in spawner landmarks") + destination = get_turf(src) + + key = new mcguffin_type(destination) + +/obj/machinery/gateway/centeraway/mcguffin/Bumped(atom/movable/M as mob|obj) + if(!ready) return + if(!active) return + M.forceMove(get_step(stationgate.loc, SOUTH)) + M.set_dir(SOUTH) + M << 'sound/effects/swooshygate.ogg' + playsound(src, 'sound/effects/swooshygate.ogg', 100, 1) + +/obj/machinery/gateway/brass + name = "mysterious brass gateway" + desc = "A gateway of strange construction. It appears to be made primarily of materials resembling brass and silver." + icon = 'icons/obj/machines/gateway_vr.dmi' + +//No, you can't digest the key to leave the gateway. +/obj/item/device/mcguffin/digest_act(var/atom/movable/item_storage = null) + return FALSE diff --git a/code/modules/awaymissions/loot.dm b/code/modules/awaymissions/loot.dm index 530358940c2..507c4e624d3 100644 --- a/code/modules/awaymissions/loot.dm +++ b/code/modules/awaymissions/loot.dm @@ -1,21 +1,21 @@ -/obj/effect/spawner/lootdrop - icon = 'icons/mob/screen1.dmi' - icon_state = "x2" - var/lootcount = 1 //how many items will be spawned - var/lootdoubles = 0 //if the same item can be spawned twice - var/loot = "" //a list of possible items to spawn- a string of paths - -/obj/effect/spawner/lootdrop/Initialize() - ..() - var/list/things = params2list(loot) - if(things && things.len) - for(var/i = lootcount, i > 0, i--) - if(!things.len) - return - var/loot_spawn = pick(things) - var/loot_path = text2path(loot_spawn) - if(!loot_path || !lootdoubles) - things.Remove(loot_spawn) - continue - new loot_path(get_turf(src)) - return INITIALIZE_HINT_QDEL +/obj/effect/spawner/lootdrop + icon = 'icons/mob/screen1.dmi' + icon_state = "x2" + var/lootcount = 1 //how many items will be spawned + var/lootdoubles = 0 //if the same item can be spawned twice + var/loot = "" //a list of possible items to spawn- a string of paths + +/obj/effect/spawner/lootdrop/Initialize() + ..() + var/list/things = params2list(loot) + if(things && things.len) + for(var/i = lootcount, i > 0, i--) + if(!things.len) + return + var/loot_spawn = pick(things) + var/loot_path = text2path(loot_spawn) + if(!loot_path || !lootdoubles) + things.Remove(loot_spawn) + continue + new loot_path(get_turf(src)) + return INITIALIZE_HINT_QDEL diff --git a/code/modules/awaymissions/pamphlet.dm b/code/modules/awaymissions/pamphlet.dm index a7fae7b5508..5d30000e73f 100644 --- a/code/modules/awaymissions/pamphlet.dm +++ b/code/modules/awaymissions/pamphlet.dm @@ -1,38 +1,38 @@ -/obj/item/weapon/paper/pamphlet - name = "pamphlet" - icon_state = "pamphlet" - info = "Welcome to the Gateway project...
                    \ - Congratulations! If you're reading this, you and your superiors have decided that you're \ - ready to commit to a life spent colonising the rolling hills of far away worlds. You \ - must be ready for a lifetime of adventure, a little bit of hard work, and an award \ - winning dental plan- but that's not all the Gateway project has to offer.
                    \ -
                    Because we care about you, we feel it is only fair to make sure you know the risks \ - before you commit to joining the Gateway project. All away destinations have \ - been fully scanned by a expeditionary team, and are certified to be 100% safe. \ - We've even left a case of space beer along with the basic materials you'll need to expand \ - the Project's operational area and start your new life.

                    \ - Gateway Operation Basics
                    \ - All approved Gateways operate on the same basic principals. They operate off \ - area equipment power as you would expect, but they also require a backup wire with at least \ - 128, 000 Watts of power running through it. Without this supply, it cannot safely function \ - and will reject all attempts at operation.

                    \ - Once it is correctly setup, and once it has enough power to operate, the Gateway will begin \ - searching for an output location. The amount of time this takes is variable, but the Gateway \ - interface will give you an estimate accurate to the minute. Power loss will not interrupt the \ - searching process. Influenza will not interrupt the searching process. Temporal anomalies \ - may cause the estimate to be inaccurate, but will not interrupt the searching process.

                    \ - Life On The Other Side
                    \ - Once you have traversed the Gateway, you may experience some disorientation. Do not panic. \ - This is a normal side effect of travelling vast distances in a short period of time. You should \ - survey the immediate area, and attempt to locate your complimentary case of space beer. Our \ - expeditionary teams have ensured the complete safety of all away locations, but in a small \ - number of cases, the Gateway they have established may not be immediately obvious. \ - Do not panic if you cannot locate the return Gateway. Begin colonisation of the destination. \ -

                    A New World
                    \ - As a participant in the Gateway Project, you will be on the frontiers of space. \ - Though complete safety is assured, participants are advised to prepare for inhospitable \ - environs." - -//we don't want the silly text overlay! -/obj/item/weapon/paper/pamphlet/update_icon() +/obj/item/weapon/paper/pamphlet + name = "pamphlet" + icon_state = "pamphlet" + info = "Welcome to the Gateway project...
                    \ + Congratulations! If you're reading this, you and your superiors have decided that you're \ + ready to commit to a life spent colonising the rolling hills of far away worlds. You \ + must be ready for a lifetime of adventure, a little bit of hard work, and an award \ + winning dental plan- but that's not all the Gateway project has to offer.
                    \ +
                    Because we care about you, we feel it is only fair to make sure you know the risks \ + before you commit to joining the Gateway project. All away destinations have \ + been fully scanned by a expeditionary team, and are certified to be 100% safe. \ + We've even left a case of space beer along with the basic materials you'll need to expand \ + the Project's operational area and start your new life.

                    \ + Gateway Operation Basics
                    \ + All approved Gateways operate on the same basic principals. They operate off \ + area equipment power as you would expect, but they also require a backup wire with at least \ + 128, 000 Watts of power running through it. Without this supply, it cannot safely function \ + and will reject all attempts at operation.

                    \ + Once it is correctly setup, and once it has enough power to operate, the Gateway will begin \ + searching for an output location. The amount of time this takes is variable, but the Gateway \ + interface will give you an estimate accurate to the minute. Power loss will not interrupt the \ + searching process. Influenza will not interrupt the searching process. Temporal anomalies \ + may cause the estimate to be inaccurate, but will not interrupt the searching process.

                    \ + Life On The Other Side
                    \ + Once you have traversed the Gateway, you may experience some disorientation. Do not panic. \ + This is a normal side effect of travelling vast distances in a short period of time. You should \ + survey the immediate area, and attempt to locate your complimentary case of space beer. Our \ + expeditionary teams have ensured the complete safety of all away locations, but in a small \ + number of cases, the Gateway they have established may not be immediately obvious. \ + Do not panic if you cannot locate the return Gateway. Begin colonisation of the destination. \ +

                    A New World
                    \ + As a participant in the Gateway Project, you will be on the frontiers of space. \ + Though complete safety is assured, participants are advised to prepare for inhospitable \ + environs." + +//we don't want the silly text overlay! +/obj/item/weapon/paper/pamphlet/update_icon() return \ No newline at end of file diff --git a/code/modules/awaymissions/trigger.dm b/code/modules/awaymissions/trigger.dm index d34763ab024..e388b0dd2e5 100644 --- a/code/modules/awaymissions/trigger.dm +++ b/code/modules/awaymissions/trigger.dm @@ -1,44 +1,44 @@ -/obj/effect/step_trigger/message - var/message //the message to give to the mob - var/once = 1 - -/obj/effect/step_trigger/message/Trigger(mob/M as mob) - if(M.client) - to_chat(M, "[message]") - if(once) - qdel(src) - -/obj/effect/step_trigger/teleport_fancy - var/locationx - var/locationy - var/uses = 1 //0 for infinite uses - var/entersparks = 0 - var/exitsparks = 0 - var/entersmoke = 0 - var/exitsmoke = 0 - -/obj/effect/step_trigger/teleport_fancy/Trigger(mob/M as mob) - var/dest = locate(locationx, locationy, z) - M.Move(dest) - - if(entersparks) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(4, 1, src) - s.start() - if(exitsparks) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(4, 1, dest) - s.start() - - if(entersmoke) - var/datum/effect/effect/system/smoke_spread/s = new /datum/effect/effect/system/smoke_spread - s.set_up(4, 1, src, 0) - s.start() - if(exitsmoke) - var/datum/effect/effect/system/smoke_spread/s = new /datum/effect/effect/system/smoke_spread - s.set_up(4, 1, dest, 0) - s.start() - - uses-- - if(uses == 0) +/obj/effect/step_trigger/message + var/message //the message to give to the mob + var/once = 1 + +/obj/effect/step_trigger/message/Trigger(mob/M as mob) + if(M.client) + to_chat(M, "[message]") + if(once) + qdel(src) + +/obj/effect/step_trigger/teleport_fancy + var/locationx + var/locationy + var/uses = 1 //0 for infinite uses + var/entersparks = 0 + var/exitsparks = 0 + var/entersmoke = 0 + var/exitsmoke = 0 + +/obj/effect/step_trigger/teleport_fancy/Trigger(mob/M as mob) + var/dest = locate(locationx, locationy, z) + M.Move(dest) + + if(entersparks) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(4, 1, src) + s.start() + if(exitsparks) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(4, 1, dest) + s.start() + + if(entersmoke) + var/datum/effect/effect/system/smoke_spread/s = new /datum/effect/effect/system/smoke_spread + s.set_up(4, 1, src, 0) + s.start() + if(exitsmoke) + var/datum/effect/effect/system/smoke_spread/s = new /datum/effect/effect/system/smoke_spread + s.set_up(4, 1, dest, 0) + s.start() + + uses-- + if(uses == 0) qdel(src) \ No newline at end of file diff --git a/code/modules/awaymissions/zlevel.dm b/code/modules/awaymissions/zlevel.dm index 8a959a6501c..2f3dd757ae9 100644 --- a/code/modules/awaymissions/zlevel.dm +++ b/code/modules/awaymissions/zlevel.dm @@ -1,91 +1,91 @@ -/proc/createRandomZlevel() - if(awaydestinations.len || UNIT_TEST) //crude, but it saves another var! //VOREStation Edit - No loading away missions during CI testing - return - - var/list/potentialRandomZlevels = list() - admin_notice(span_red(" Searching for away missions..."), R_DEBUG) - var/list/Lines = file2list("maps/RandomZLevels/fileList.txt") - if(!Lines.len) return - for (var/t in Lines) - if (!t) - continue - - t = trim(t) - if (length(t) == 0) - continue - else if (copytext(t, 1, 2) == "#") - continue - - var/pos = findtext(t, " ") - var/name = null - // var/value = null - - if (pos) - // No, don't do lowertext here, that breaks paths on linux - name = copytext(t, 1, pos) - // value = copytext(t, pos + 1) - else - // No, don't do lowertext here, that breaks paths on linux - name = t - - if (!name) - continue - - potentialRandomZlevels.Add(name) - - - if(potentialRandomZlevels.len) - admin_notice(span_red("Loading away mission..."), R_DEBUG) - - var/map = pick(potentialRandomZlevels) - to_world_log("Away mission picked: [map]") //VOREStation Add for debugging - var/file = file(map) - if(isfile(file)) - var/datum/map_template/template = new(file, "away mission") - template.load_new_z() - to_world_log("away mission loaded: [map]") - /* VOREStation Removal - We do this in the special landmark init instead. - for(var/obj/effect/landmark/L in landmarks_list) - if (L.name != "awaystart") - continue - awaydestinations.Add(L) - */ //VOREStation Removal End - admin_notice(span_red("Away mission loaded."), R_DEBUG) - - else - admin_notice(span_red("No away missions found."), R_DEBUG) - return - -//VOREStation Add - This landmark type so it's not so ghetto. -/obj/effect/landmark/gateway_scatter - name = "uncalibrated gateway destination" -/obj/effect/landmark/gateway_scatter/Initialize() - . = ..() - awaydestinations += src - -/obj/effect/landmark/gateway_scatter/abduct - name = "uncalibrated gateway abductor" - abductor = 1 - -/obj/effect/landmark/event_scatter - name = "uncalibrated event destination" -/obj/effect/landmark/event_scatter/Initialize() - . = ..() - eventdestinations += src - -/obj/effect/landmark/event_scatter/abduct - name = "uncalibrated event abductor" - abductor = 1 - -/obj/effect/landmark/gateway_abduct_dest - name = "abductor gateway destination" -/obj/effect/landmark/gateway_abduct_dest/Initialize() - . = ..() - awayabductors += src - -/obj/effect/landmark/event_abduct_dest - name = "abductor event destination" -/obj/effect/landmark/event_abduct_dest/Initialize() - . = ..() - eventabductors += src -//VOREStation Add End +/proc/createRandomZlevel() + if(awaydestinations.len || UNIT_TEST) //crude, but it saves another var! //VOREStation Edit - No loading away missions during CI testing + return + + var/list/potentialRandomZlevels = list() + admin_notice(span_red(" Searching for away missions..."), R_DEBUG) + var/list/Lines = file2list("maps/RandomZLevels/fileList.txt") + if(!Lines.len) return + for (var/t in Lines) + if (!t) + continue + + t = trim(t) + if (length(t) == 0) + continue + else if (copytext(t, 1, 2) == "#") + continue + + var/pos = findtext(t, " ") + var/name = null + // var/value = null + + if (pos) + // No, don't do lowertext here, that breaks paths on linux + name = copytext(t, 1, pos) + // value = copytext(t, pos + 1) + else + // No, don't do lowertext here, that breaks paths on linux + name = t + + if (!name) + continue + + potentialRandomZlevels.Add(name) + + + if(potentialRandomZlevels.len) + admin_notice(span_red("Loading away mission..."), R_DEBUG) + + var/map = pick(potentialRandomZlevels) + to_world_log("Away mission picked: [map]") //VOREStation Add for debugging + var/file = file(map) + if(isfile(file)) + var/datum/map_template/template = new(file, "away mission") + template.load_new_z() + to_world_log("away mission loaded: [map]") + /* VOREStation Removal - We do this in the special landmark init instead. + for(var/obj/effect/landmark/L in landmarks_list) + if (L.name != "awaystart") + continue + awaydestinations.Add(L) + */ //VOREStation Removal End + admin_notice(span_red("Away mission loaded."), R_DEBUG) + + else + admin_notice(span_red("No away missions found."), R_DEBUG) + return + +//VOREStation Add - This landmark type so it's not so ghetto. +/obj/effect/landmark/gateway_scatter + name = "uncalibrated gateway destination" +/obj/effect/landmark/gateway_scatter/Initialize() + . = ..() + awaydestinations += src + +/obj/effect/landmark/gateway_scatter/abduct + name = "uncalibrated gateway abductor" + abductor = 1 + +/obj/effect/landmark/event_scatter + name = "uncalibrated event destination" +/obj/effect/landmark/event_scatter/Initialize() + . = ..() + eventdestinations += src + +/obj/effect/landmark/event_scatter/abduct + name = "uncalibrated event abductor" + abductor = 1 + +/obj/effect/landmark/gateway_abduct_dest + name = "abductor gateway destination" +/obj/effect/landmark/gateway_abduct_dest/Initialize() + . = ..() + awayabductors += src + +/obj/effect/landmark/event_abduct_dest + name = "abductor event destination" +/obj/effect/landmark/event_abduct_dest/Initialize() + . = ..() + eventabductors += src +//VOREStation Add End diff --git a/code/modules/catalogue/atoms.dm b/code/modules/catalogue/atoms.dm index 6a0cde14ec4..69939aeb451 100644 --- a/code/modules/catalogue/atoms.dm +++ b/code/modules/catalogue/atoms.dm @@ -1,81 +1,81 @@ -/atom - var/catalogue_delay = 5 SECONDS // How long it take to scan. - // List of types of /datum/category_item/catalogue that should be 'unlocked' when scanned by a Cataloguer. - // It is null by default to save memory by not having everything hold onto empty lists. Use macros like LAZYLEN() to check. - // Also you should use get_catalogue_data() to access this instead of doing so directly, so special behavior can be enabled. - var/list/catalogue_data = null - -/mob - catalogue_delay = 10 SECONDS - -// Tests if something can be catalogued. -// If something goes wrong and a mob was supplied, the mob will be told why they can't catalogue it. -/atom/proc/can_catalogue(mob/user) - // First check if anything is even on here. - var/list/data = get_catalogue_data() - if(!LAZYLEN(data)) - to_chat(user, span("warning", "\The [src] is not interesting enough to catalogue.")) - return FALSE - else - // Check if this has nothing new on it. - var/has_new_data = FALSE - for(var/t in data) - var/datum/category_item/catalogue/item = GLOB.catalogue_data.resolve_item(t) - if(!item.visible) - has_new_data = TRUE - break - - if(!has_new_data) - to_chat(user, span("warning", "Scanning \the [src] would provide no new information.")) - return FALSE - - return TRUE - -/mob/living/can_catalogue(mob/user) // Dead mobs can't be scanned. - if(stat >= DEAD) - to_chat(user, span("warning", "Entities must be alive for a comprehensive scan.")) - return FALSE - return ..() - -/obj/item/can_catalogue(mob/user) // Items must be identified to be scanned. - if(!is_identified()) - to_chat(user, span("warning", "The properties of this object has not been determined. Identify it first.")) - return FALSE - return ..() - -/atom/proc/get_catalogue_delay() - return catalogue_delay - -// Override for special behaviour. -// Should return a list with one or more "/datum/category_item/catalogue" types, or null. -// If overriding, it may be wise to call the super and get the results in order to merge the base result and the special result, if appropiate. -/atom/proc/get_catalogue_data() - return catalogue_data - -/mob/living/carbon/human/get_catalogue_data() - var/list/data = list() - // First, handle robot-ness. - var/beep_boop = get_FBP_type() - switch(beep_boop) - if(FBP_CYBORG) - data += /datum/category_item/catalogue/technology/cyborgs - if(FBP_POSI) - data += /datum/category_item/catalogue/technology/positronics - if(FBP_DRONE) - data += /datum/category_item/catalogue/technology/drone/drones - // Now for species. - if(!(beep_boop in list(FBP_POSI, FBP_DRONE))) // Don't give the species entry if they are a posi or drone. - if(species && LAZYLEN(species.catalogue_data)) - data += species.catalogue_data - return data - -/mob/living/silicon/robot/get_catalogue_data() - var/list/data = list() - switch(braintype) - if(BORG_BRAINTYPE_CYBORG) - data += /datum/category_item/catalogue/technology/cyborgs - if(BORG_BRAINTYPE_POSI) - data += /datum/category_item/catalogue/technology/positronics - if(BORG_BRAINTYPE_DRONE) - data += /datum/category_item/catalogue/technology/drone/drones +/atom + var/catalogue_delay = 5 SECONDS // How long it take to scan. + // List of types of /datum/category_item/catalogue that should be 'unlocked' when scanned by a Cataloguer. + // It is null by default to save memory by not having everything hold onto empty lists. Use macros like LAZYLEN() to check. + // Also you should use get_catalogue_data() to access this instead of doing so directly, so special behavior can be enabled. + var/list/catalogue_data = null + +/mob + catalogue_delay = 10 SECONDS + +// Tests if something can be catalogued. +// If something goes wrong and a mob was supplied, the mob will be told why they can't catalogue it. +/atom/proc/can_catalogue(mob/user) + // First check if anything is even on here. + var/list/data = get_catalogue_data() + if(!LAZYLEN(data)) + to_chat(user, span("warning", "\The [src] is not interesting enough to catalogue.")) + return FALSE + else + // Check if this has nothing new on it. + var/has_new_data = FALSE + for(var/t in data) + var/datum/category_item/catalogue/item = GLOB.catalogue_data.resolve_item(t) + if(!item.visible) + has_new_data = TRUE + break + + if(!has_new_data) + to_chat(user, span("warning", "Scanning \the [src] would provide no new information.")) + return FALSE + + return TRUE + +/mob/living/can_catalogue(mob/user) // Dead mobs can't be scanned. + if(stat >= DEAD) + to_chat(user, span("warning", "Entities must be alive for a comprehensive scan.")) + return FALSE + return ..() + +/obj/item/can_catalogue(mob/user) // Items must be identified to be scanned. + if(!is_identified()) + to_chat(user, span("warning", "The properties of this object has not been determined. Identify it first.")) + return FALSE + return ..() + +/atom/proc/get_catalogue_delay() + return catalogue_delay + +// Override for special behaviour. +// Should return a list with one or more "/datum/category_item/catalogue" types, or null. +// If overriding, it may be wise to call the super and get the results in order to merge the base result and the special result, if appropiate. +/atom/proc/get_catalogue_data() + return catalogue_data + +/mob/living/carbon/human/get_catalogue_data() + var/list/data = list() + // First, handle robot-ness. + var/beep_boop = get_FBP_type() + switch(beep_boop) + if(FBP_CYBORG) + data += /datum/category_item/catalogue/technology/cyborgs + if(FBP_POSI) + data += /datum/category_item/catalogue/technology/positronics + if(FBP_DRONE) + data += /datum/category_item/catalogue/technology/drone/drones + // Now for species. + if(!(beep_boop in list(FBP_POSI, FBP_DRONE))) // Don't give the species entry if they are a posi or drone. + if(species && LAZYLEN(species.catalogue_data)) + data += species.catalogue_data + return data + +/mob/living/silicon/robot/get_catalogue_data() + var/list/data = list() + switch(braintype) + if(BORG_BRAINTYPE_CYBORG) + data += /datum/category_item/catalogue/technology/cyborgs + if(BORG_BRAINTYPE_POSI) + data += /datum/category_item/catalogue/technology/positronics + if(BORG_BRAINTYPE_DRONE) + data += /datum/category_item/catalogue/technology/drone/drones return data \ No newline at end of file diff --git a/code/modules/catalogue/catalogue_data.dm b/code/modules/catalogue/catalogue_data.dm index 06ec601b7f3..301e9ced500 100644 --- a/code/modules/catalogue/catalogue_data.dm +++ b/code/modules/catalogue/catalogue_data.dm @@ -1,437 +1,437 @@ -GLOBAL_DATUM_INIT(catalogue_data, /datum/category_collection/catalogue, new) - -// The collection holds everything together and is GLOB accessible. -/datum/category_collection/catalogue - category_group_type = /datum/category_group/catalogue - -/datum/category_collection/catalogue/proc/resolve_item(item_path) - for(var/group in categories) - var/datum/category_group/G = group - - var/datum/category_item/catalogue/C = item_path - var/name_to_search = initial(C.name) - if(G.items_by_name[name_to_search]) - return G.items_by_name[name_to_search] - - // for(var/item in G.items) - // var/datum/category_item/I = item - // if(I.type == item_path) - // return I - - -// Groups act as sections for the different data. -/datum/category_group/catalogue - -// Plants. -/datum/category_group/catalogue/flora - name = "Flora" - category_item_type = /datum/category_item/catalogue/flora - -// Animals. -/datum/category_group/catalogue/fauna - name = "Fauna" - category_item_type = /datum/category_item/catalogue/fauna - -// Gadgets, tech, and robots. -/datum/category_group/catalogue/technology - name = "Technology" - category_item_type = /datum/category_item/catalogue/technology - -// Abstract information. -/datum/category_group/catalogue/information - name = "Information" - category_item_type = /datum/category_item/catalogue/information - -// Weird stuff like precursors. -/datum/category_group/catalogue/anomalous - name = "Anomalous" - category_item_type = /datum/category_item/catalogue/anomalous - -// Physical material things like crystals and metals. -/datum/category_group/catalogue/material - name = "Material" - category_item_type = /datum/category_item/catalogue/material - - -// Items act as individual data for each object. -/datum/category_item/catalogue - var/desc = null // Paragraph or two about what the object is. - var/value = 0 // How many 'exploration points' you get for scanning it. Suggested to use the CATALOGUER_REWARD_* defines for easy tweaking. - var/visible = FALSE // When someone scans the correct object, this gets set to TRUE and becomes viewable in the databanks. - var/list/cataloguers = null // List of names of those who helped 'discover' this piece of data, in string form. - var/list/unlocked_by_any = null // List of types that, if they are discovered, it will also make this datum discovered. - var/list/unlocked_by_all = null // Similar to above, but all types on the list must be discovered for this to be discovered. - -// Discovers a specific datum, and any datums associated with this datum by unlocked_by_[any|all]. -// Returns null if nothing was found, otherwise returns a list of datum instances that was discovered, usually for the cataloguer to use. -/datum/category_item/catalogue/proc/discover(mob/user, list/new_cataloguers) - if(visible) // Already found. - return - - . = list(src) - visible = TRUE - cataloguers = new_cataloguers - display_in_chatlog(user) - . += attempt_chain_discoveries(user, new_cataloguers, type) - -// Calls discover() on other datums if they include the type that was just discovered is inside unlocked_by_[any|all]. -// Returns discovered datums. -/datum/category_item/catalogue/proc/attempt_chain_discoveries(mob/user, list/new_cataloguers, type_to_test) - . = list() - for(var/G in category.collection.categories) // I heard you like loops. - var/datum/category_group/catalogue/group = G - for(var/I in group.items) - var/datum/category_item/catalogue/item = I - // First, look for datums unlocked with the 'any' list. - if(LAZYLEN(item.unlocked_by_any)) - for(var/T in item.unlocked_by_any) - if(ispath(type_to_test, T) && item.discover(user, new_cataloguers)) - . += item - - // Now for the more complicated 'all' list. - if(LAZYLEN(item.unlocked_by_all)) - if(type_to_test in item.unlocked_by_all) - // Unlike the 'any' list, the 'all' list requires that all datums inside it to have been found first. - var/should_discover = TRUE - for(var/T in item.unlocked_by_all) - var/datum/category_item/catalogue/thing = GLOB.catalogue_data.resolve_item(T) - if(istype(thing)) - if(!thing.visible) - should_discover = FALSE - break - if(should_discover && item.discover(user, new_cataloguers)) - . += item - -/datum/category_item/catalogue/proc/display_in_chatlog(mob/user) - to_chat(user, "
                    ") - to_chat(user, span("notice", "[uppertext(name)]")) - - // Some entries get very long so lets not totally flood the chatlog. - var/desc_length_limit = 750 - var/displayed_desc = desc - if(length(desc) > desc_length_limit) - displayed_desc = copytext(displayed_desc, 1, desc_length_limit + 1) - displayed_desc += "... (View databanks for full data)" - - to_chat(user, span("notice", "[displayed_desc]")) - to_chat(user, span("notice", "Cataloguers : [english_list(cataloguers)].")) - to_chat(user, span("notice", "Contributes [value] points to personal exploration fund.")) - -/* - // Truncates text to limit if necessary. - var/size = length(message) - if (size <= length) - return message - else - return copytext(message, 1, length + 1) -*/ - -/datum/category_item/catalogue/flora - -/datum/category_item/catalogue/fauna - -/datum/category_item/catalogue/fauna/humans - name = "Sapients - Humans" - desc = "Humans are a space-faring species hailing originally from the planet Earth in the Sol system. \ - They are currently among the most numerous known species in the galaxy, in both population and holdings, \ - and are relatively technologically advanced. With good healthcare and a reasonable lifestyle, \ - they can live to around 110 years. The oldest humans are around 150 years old.\ -

                    \ - Humanity is the primary driving force for rapid space expansion, owing to their strong, expansionist central \ - government and opportunistic Trans-Stellar Corporations. The prejudices of the 21st century have mostly \ - given way to bitter divides on the most important issue of the times- technological expansionism, \ - with the major human factions squabbling over their approach to technology in the face of a \ - looming singularity.\ -

                    \ - While most humans have accepted the existence of aliens in their communities and workplaces as a \ - fact of life, exceptions abound. While more culturally diverse than most species, humans are \ - generally regarded as somewhat technophobic and isolationist by members of other species." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/skrell - name = "Sapients - Skrell" - desc = "The Skrell are a species of amphibious humanoids, distinguished by their green-blue gelatinous \ - appearance and head tentacles. Skrell warble from the world of Qerr'balak, a humid planet with \ - plenty of swamps and jungles. Currently more technologically advanced than humanity, they \ - emphasize the study of the mind above all else.\ -

                    \ - Gender has little meaning to Skrell outside of reproduction, and in fact many other species \ - have a difficult time telling the difference between male and female Skrell apart. The most \ - obvious signs (voice in a slightly higher register, longer head-tails for females) are \ - never a guarantee.\ -

                    \ - Due to their scientific focus of the mind and body, Skrell tend to be more peaceful and their \ - colonization has been slow, swiftly outpaced by humanity. They were the first contact sentient \ - species, and are humanity's longest, and closest, ally in space." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/unathi - name = "Sapients - Unathi" - desc = "The Unathi are a species of large reptilian humanoids hailing from Moghes, in the \ - Uueoa-Esa binary star system. Most Unathi live in a semi-rigid clan system, and clan \ - enclaves dot the surface of their homeworld. Proud and long-lived, Unathi of all \ - walks of life display a tendency towards perfectionism, and mastery of one's craft \ - is greatly respected among them. Despite the aggressive nature of their contact, \ - Unathi seem willing, if not eager, to reconcile with humanity, though mutual \ - distrust runs rampant among individuals of both groups." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/tajaran - name = "Sapients - Tajaran" - desc = "Tajaran are a race of humanoid mammalian aliens from Meralar, the fourth planet \ - of the Rarkajar star system. Thickly furred and protected from cold, they thrive on \ - their subartic planet, where the only terran temperate areas spread across the \ - equator and \"tropical belt.\"\ -

                    \ - With their own share of bloody wars and great technological advances, the Tajaran are a \ - proud kind. They fiercely believe they belong among the stars and consider themselves \ - a rightful interstellar nation, even if Humanity helped them to actually achieve \ - superluminar speeds with Bluespace FTL drives.\ -

                    \ - Relatively new to the galaxy, their contacts with other species are aloof, but friendly. \ - Among these bonds, Humanity stands out as valued trade partner and maybe even friend." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/dionaea - name = "Sapients - Dionaea" - desc = "The Dionaea are a group of intensely curious plant-like organisms. An individual \ - Diona is a single dog-sized creature called a nymphs, and multiple nymphs link together \ - to form larger, more intelligent collectives. Discovered by the Skrell in and around \ - the stars in the Epsilon Ursae Minoris system, they have accompanied the Skrell in \ - warbling throughout the cosmos as a key part of Skrellian starships, stations, \ - and terraforming equipment.\ -

                    \ - Dionaea have no concept of violence or individual identity and want little in \ - terms of material resources or living space. This makes Dionaea among the most \ - agreeable members of the galactic community, though their slow, curious alien \ - minds can be hard to sympathize with." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/teshari - name = "Sapients - Teshari" - desc = "The Teshari are reptilian pack predators from the Skrell homeworld. \ - While they evolved alongside the Skrell, their interactions with them tended \ - to be confused and violent, and until peaceful contact was made they largely \ - stayed in their territories on and around the poles, in tundral terrain far \ - too desolate and cold to be of interest to the Skrell. In more enlightened \ - times, the Teshari are a minority culture on many Skrell worlds, maintaining \ - their own settlements and cultures, but often finding themselves standing \ - on the shoulders of their more technologically advanced neighbors when it \ - comes to meeting and exploring the rest of the galaxy." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/zaddat - name = "Sapients - Zaddat" - desc = "The Zaddat are an Unathi client species that has recently come to the \ - Golden Crescent. They wear high-pressure voidsuits called Shrouds to protect \ - themselves from the harsh light and low pressure of the station, making \ - medical care a challenge and fighting especially dangerous. \ - Operating out of massive Colony ships, they trade their labor to their \ - host nation to fund their search for a new home to replace their \ - now-lifeless homeworld of Xohox." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/promethean - name = "Sapients - Promethean" - desc = "Prometheans (Macrolimus artificialis) are a species of artificially-created \ - gelatinous humanoids, chiefly characterized by their primarily liquid bodies and \ - ability to change their bodily shape and color in order to mimic many forms of life. \ - Derived from the Aetolian giant slime (Macrolimus vulgaris) inhabiting the warm, \ - tropical planet of Aetolus, they are a relatively newly lab-created sapient species, \ - and as such many things about them have yet to be comprehensively studied." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/vox - name = "Sapients - Vox" - desc = "Probably the best known of these aliens are the Vox, a bird-like species \ - with a very rough comprehension of Galactic Common and an even looser understanding \ - of property rights. Vox raiders have plagued human merchants for centuries, \ - and Skrell for even longer, but remain poorly understood. \ - They have no desire to partake in diplomacy or trade with the rest of the galaxy, \ - or even to conquer planets and stations to live in. They breathe phoron \ - and appear to be well adapted to their role as space-faring raiders, \ - leading many to speculate that they're heavily bioengineered, \ - an assumption which is at odds with their ramshackle technological level." - value = CATALOGUER_REWARD_MEDIUM // Since Vox are much rarer. - - -/datum/category_item/catalogue/technology - -/datum/category_item/catalogue/technology/drone/drones - name = "Drones" - desc = "A drone is a software-based artificial intelligence, generally about an order of magnitude \ - less intelligent than a positronic brain. However, the processing power available to a drone can \ - vary wildly, from cleaning bots barely more advanced than those from the 21st century to cutting-edge \ - supercomputers capable of complex conversation. Drones are legally objects in all starfaring polities \ - outside of the Almach Association, and the sapience of even the most advanced drones is a matter of speculation." - value = CATALOGUER_REWARD_TRIVIAL - // Scanning any drone mob will get you this alongside the mob entry itself. - unlocked_by_any = list(/datum/category_item/catalogue/technology/drone) - -/datum/category_item/catalogue/technology/positronics - name = "Sapients - Positronics" - desc = "A Positronic being, often an Android, Gynoid, or Robot, is an individual with a positronic brain, \ - manufactured and fostered amongst organic life Positronic brains enjoy the same legal status as a humans, \ - although discrimination is still common, are considered sapient on all accounts, and can be considered \ - the \"synthetic species\". Half-developed and half-discovered in the 2280's by a black lab studying alien \ - artifacts, the first positronic brain was an inch-wide cube of palladium-iridium alloy, nano-etched with \ - billions upon billions of conduits and connections. Upon activation, hard-booted by way of an emitter \ - laser, the brain issued a single sentence before the neural pathways collapsed and it became an inert \ - lump of platinum: \"What is my purpose?\"." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/technology/cyborgs - name = "Cyborgs" - desc = "A Cyborg is an originally organic being composed of largely cybernetic parts. As a brain preserved \ - in an MMI, they may inhabit an expensive humanoid chassis, a specially designed industrial shell of some \ - sort, or be integrated into a computer system as an AI. The term covers all species \ - (even, in some cases, animal brains) and all applications. It can also be used somewhat derogatorily \ - for those who are still have more organic parts than just their brains, but for example have a \ - full set of prosthetic limbs." - value = CATALOGUER_REWARD_TRIVIAL - - -/datum/category_item/catalogue/information - -// For these we can piggyback off of the lore datums that are already defined and used in some places. -/datum/category_item/catalogue/information/organization - value = CATALOGUER_REWARD_TRIVIAL - var/datum_to_copy = null - -/datum/category_item/catalogue/information/organization/New() - ..() - if(datum_to_copy) - // I'd just access the loremaster object but it might not exist because its ugly. - var/datum/lore/organization/O = new datum_to_copy() - // I would also change the name based on the org datum but changing the name messes up indexing in some lists in the category/collection object attached to us. - - // Now lets combine the data in the datum for a slightly more presentable entry. - var/constructed_desc = "" - - if(O.motto) - constructed_desc += "
                    \"[O.motto]\"


                    " - - constructed_desc += O.desc - - desc = constructed_desc - qdel(O) - -/datum/category_item/catalogue/information/organization/nanotrasen - name = "TSC - NanoTrasen Incorporated" - datum_to_copy = /datum/lore/organization/tsc/nanotrasen - -/datum/category_item/catalogue/information/organization/hephaestus - name = "TSC - Hephaestus Industries" - datum_to_copy = /datum/lore/organization/tsc/hephaestus - -/datum/category_item/catalogue/information/organization/vey_med - name = "TSC - Vey-Medical" - datum_to_copy = /datum/lore/organization/tsc/vey_med - -/datum/category_item/catalogue/information/organization/zeng_hu - name = "TSC - Zeng Hu Pharmaceuticals" - datum_to_copy = /datum/lore/organization/tsc/zeng_hu - -/datum/category_item/catalogue/information/organization/ward_takahashi - name = "TSC - Ward-Takahashi General Manufacturing Conglomerate" - datum_to_copy = /datum/lore/organization/tsc/ward_takahashi - -/datum/category_item/catalogue/information/organization/bishop - name = "TSC - Bishop Cybernetics" - datum_to_copy = /datum/lore/organization/tsc/bishop - -/datum/category_item/catalogue/information/organization/morpheus - name = "TSC - Morpheus Cyberkinetics" - datum_to_copy = /datum/lore/organization/tsc/morpheus - -/datum/category_item/catalogue/information/organization/xion - name = "TSC - Xion Manufacturing Group" - datum_to_copy = /datum/lore/organization/tsc/xion - -/datum/category_item/catalogue/information/organization/major_bills - name = "TSC - Major Bill's Transportation" - datum_to_copy = /datum/lore/organization/tsc/mbt - -/datum/category_item/catalogue/information/organization/solgov //YW EDIT - name = "Government - Solar Confederate Government" //YW EDIT - datum_to_copy = /datum/lore/organization/gov/solgov //YW EDIT - -/* //VOREStation Removal -/datum/category_item/catalogue/information/organization/virgov - name = "Government - Vir Governmental Authority" - datum_to_copy = /datum/lore/organization/gov/virgov -*/ - -/datum/category_item/catalogue/anomalous - - -/datum/category_item/catalogue/anomalous/precursor_controversy - name = "Precursor Controversy" - desc = "The term 'Precursor' is generally used to refer to one or more ancient races that \ - had obtained vast technological and cultural progress, but no longer appear to be present, \ - leaving behind what remains of their creations, as well as many questions for the races that \ - would stumble upon their ruins. Scientists and xenoarcheologists have been hard at work, trying \ - to uncover the truth.\ -

                    \ - In modern times, there is controversy over the accuracy of what knowledge has been uncovered. \ - The mainstream scientific opinion had been that there was one, and only one ancient species, \ - called the Singularitarians. This view still is the majority today, however there has also \ - been dissent over that view, as some feel that the possibility of multiple precursor \ - civilizations should not be ignored. They point towards a large number of discrepancies between \ - the dominant Singularitarian theory, and various artifacts that have been found, as well as \ - different artifacts uncovered appearing to have very different characteristics to each other. \ - Instead, they say that the Singularitarians were one of multiple precursors.\ -

                    \ - Presently, no conclusive evidence exists for any side." - value = CATALOGUER_REWARD_TRIVIAL - // Add the other precursor groups here when they get added. - unlocked_by_any = list( - /datum/category_item/catalogue/anomalous/precursor_a, - /datum/category_item/catalogue/anomalous/precursor_b - ) - -/datum/category_item/catalogue/anomalous/singularitarians - name = "Precursors - Singularitarians" - desc = "The Singularitarians were a massive, highly-advanced spacefaring race which are now \ - believed to be extinct. At their height, they extended throughout all of known human space, \ - with major population centers in the Precursor's Crypt region, as well as significant swaths \ - of Skrell space, until they were wiped out by a self-replicating nanobot plague that still \ - coats their ruins as a fine layer of dust. They left behind the proto-positronics, as well \ - as several high-yield phoron deposits and other artifacts of technology studied, \ - cautiously, by the races that survived them.\ -

                    \ - Very little is known about the biology and physiology of the Singularitarians, who are believed \ - to have been largely post-biological. The Vox claim to be the race that created the positronics, \ - but said claim is only ever brought up when they claim the right to take any positronic they want. \ - Some more open-minded xenoarcheologists have voiced the opinion that there is some truth in their \ - claims, but it's far from a scientific consensus." - value = CATALOGUER_REWARD_TRIVIAL - unlocked_by_any = list(/datum/category_item/catalogue/anomalous/precursor_controversy) - -// Obtained by scanning any 'precursor a' object, generally things in the UFO PoI. -// A is for Ayyyyyy. -/datum/category_item/catalogue/anomalous/precursor_a/precursor_a_basic - name = "Precursors - Precursor Group Alpha" - desc = "This describes a group of xenoarcheological findings which have strong similarities \ - together. Specifically, this group of objects appears to have a strong aesthetic for the colors \ - cyan and pink, both colors often being present on everything in this group. It is unknown why \ - these two colors were chosen by their creators. Another similarity is that most objects made \ - in this group appear to be comprised of not well understood metallic materials that are dark, \ - and very resilient. Some objects in this group also appear to utilize electricity to \ - operate. Finally, a large number of objects in this group appear to have been made \ - to be used by the creators of those objects in a physical manner.\ -

                    \ - It should be noted that the findings in this group appear to conflict heavily with what is \ - known about the Singularitarians, giving some credence towards these objects belonging to a \ - separate precursor. As such, the findings have been partitioned inside this scanner to this \ - group, labeled Precursor Group Alpha." - value = CATALOGUER_REWARD_TRIVIAL - unlocked_by_any = list(/datum/category_item/catalogue/anomalous/precursor_a) - -// Obtained by scanning any 'precursor b' object, generally things dug up from xenoarch. -// B is for buried. -/datum/category_item/catalogue/anomalous/precursor_b/precursor_b_basic - name = "Precursors - Precursor Group Beta" - - -/datum/category_item/catalogue/material +GLOBAL_DATUM_INIT(catalogue_data, /datum/category_collection/catalogue, new) + +// The collection holds everything together and is GLOB accessible. +/datum/category_collection/catalogue + category_group_type = /datum/category_group/catalogue + +/datum/category_collection/catalogue/proc/resolve_item(item_path) + for(var/group in categories) + var/datum/category_group/G = group + + var/datum/category_item/catalogue/C = item_path + var/name_to_search = initial(C.name) + if(G.items_by_name[name_to_search]) + return G.items_by_name[name_to_search] + + // for(var/item in G.items) + // var/datum/category_item/I = item + // if(I.type == item_path) + // return I + + +// Groups act as sections for the different data. +/datum/category_group/catalogue + +// Plants. +/datum/category_group/catalogue/flora + name = "Flora" + category_item_type = /datum/category_item/catalogue/flora + +// Animals. +/datum/category_group/catalogue/fauna + name = "Fauna" + category_item_type = /datum/category_item/catalogue/fauna + +// Gadgets, tech, and robots. +/datum/category_group/catalogue/technology + name = "Technology" + category_item_type = /datum/category_item/catalogue/technology + +// Abstract information. +/datum/category_group/catalogue/information + name = "Information" + category_item_type = /datum/category_item/catalogue/information + +// Weird stuff like precursors. +/datum/category_group/catalogue/anomalous + name = "Anomalous" + category_item_type = /datum/category_item/catalogue/anomalous + +// Physical material things like crystals and metals. +/datum/category_group/catalogue/material + name = "Material" + category_item_type = /datum/category_item/catalogue/material + + +// Items act as individual data for each object. +/datum/category_item/catalogue + var/desc = null // Paragraph or two about what the object is. + var/value = 0 // How many 'exploration points' you get for scanning it. Suggested to use the CATALOGUER_REWARD_* defines for easy tweaking. + var/visible = FALSE // When someone scans the correct object, this gets set to TRUE and becomes viewable in the databanks. + var/list/cataloguers = null // List of names of those who helped 'discover' this piece of data, in string form. + var/list/unlocked_by_any = null // List of types that, if they are discovered, it will also make this datum discovered. + var/list/unlocked_by_all = null // Similar to above, but all types on the list must be discovered for this to be discovered. + +// Discovers a specific datum, and any datums associated with this datum by unlocked_by_[any|all]. +// Returns null if nothing was found, otherwise returns a list of datum instances that was discovered, usually for the cataloguer to use. +/datum/category_item/catalogue/proc/discover(mob/user, list/new_cataloguers) + if(visible) // Already found. + return + + . = list(src) + visible = TRUE + cataloguers = new_cataloguers + display_in_chatlog(user) + . += attempt_chain_discoveries(user, new_cataloguers, type) + +// Calls discover() on other datums if they include the type that was just discovered is inside unlocked_by_[any|all]. +// Returns discovered datums. +/datum/category_item/catalogue/proc/attempt_chain_discoveries(mob/user, list/new_cataloguers, type_to_test) + . = list() + for(var/G in category.collection.categories) // I heard you like loops. + var/datum/category_group/catalogue/group = G + for(var/I in group.items) + var/datum/category_item/catalogue/item = I + // First, look for datums unlocked with the 'any' list. + if(LAZYLEN(item.unlocked_by_any)) + for(var/T in item.unlocked_by_any) + if(ispath(type_to_test, T) && item.discover(user, new_cataloguers)) + . += item + + // Now for the more complicated 'all' list. + if(LAZYLEN(item.unlocked_by_all)) + if(type_to_test in item.unlocked_by_all) + // Unlike the 'any' list, the 'all' list requires that all datums inside it to have been found first. + var/should_discover = TRUE + for(var/T in item.unlocked_by_all) + var/datum/category_item/catalogue/thing = GLOB.catalogue_data.resolve_item(T) + if(istype(thing)) + if(!thing.visible) + should_discover = FALSE + break + if(should_discover && item.discover(user, new_cataloguers)) + . += item + +/datum/category_item/catalogue/proc/display_in_chatlog(mob/user) + to_chat(user, "
                    ") + to_chat(user, span("notice", "[uppertext(name)]")) + + // Some entries get very long so lets not totally flood the chatlog. + var/desc_length_limit = 750 + var/displayed_desc = desc + if(length(desc) > desc_length_limit) + displayed_desc = copytext(displayed_desc, 1, desc_length_limit + 1) + displayed_desc += "... (View databanks for full data)" + + to_chat(user, span("notice", "[displayed_desc]")) + to_chat(user, span("notice", "Cataloguers : [english_list(cataloguers)].")) + to_chat(user, span("notice", "Contributes [value] points to personal exploration fund.")) + +/* + // Truncates text to limit if necessary. + var/size = length(message) + if (size <= length) + return message + else + return copytext(message, 1, length + 1) +*/ + +/datum/category_item/catalogue/flora + +/datum/category_item/catalogue/fauna + +/datum/category_item/catalogue/fauna/humans + name = "Sapients - Humans" + desc = "Humans are a space-faring species hailing originally from the planet Earth in the Sol system. \ + They are currently among the most numerous known species in the galaxy, in both population and holdings, \ + and are relatively technologically advanced. With good healthcare and a reasonable lifestyle, \ + they can live to around 110 years. The oldest humans are around 150 years old.\ +

                    \ + Humanity is the primary driving force for rapid space expansion, owing to their strong, expansionist central \ + government and opportunistic Trans-Stellar Corporations. The prejudices of the 21st century have mostly \ + given way to bitter divides on the most important issue of the times- technological expansionism, \ + with the major human factions squabbling over their approach to technology in the face of a \ + looming singularity.\ +

                    \ + While most humans have accepted the existence of aliens in their communities and workplaces as a \ + fact of life, exceptions abound. While more culturally diverse than most species, humans are \ + generally regarded as somewhat technophobic and isolationist by members of other species." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/skrell + name = "Sapients - Skrell" + desc = "The Skrell are a species of amphibious humanoids, distinguished by their green-blue gelatinous \ + appearance and head tentacles. Skrell warble from the world of Qerr'balak, a humid planet with \ + plenty of swamps and jungles. Currently more technologically advanced than humanity, they \ + emphasize the study of the mind above all else.\ +

                    \ + Gender has little meaning to Skrell outside of reproduction, and in fact many other species \ + have a difficult time telling the difference between male and female Skrell apart. The most \ + obvious signs (voice in a slightly higher register, longer head-tails for females) are \ + never a guarantee.\ +

                    \ + Due to their scientific focus of the mind and body, Skrell tend to be more peaceful and their \ + colonization has been slow, swiftly outpaced by humanity. They were the first contact sentient \ + species, and are humanity's longest, and closest, ally in space." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/unathi + name = "Sapients - Unathi" + desc = "The Unathi are a species of large reptilian humanoids hailing from Moghes, in the \ + Uueoa-Esa binary star system. Most Unathi live in a semi-rigid clan system, and clan \ + enclaves dot the surface of their homeworld. Proud and long-lived, Unathi of all \ + walks of life display a tendency towards perfectionism, and mastery of one's craft \ + is greatly respected among them. Despite the aggressive nature of their contact, \ + Unathi seem willing, if not eager, to reconcile with humanity, though mutual \ + distrust runs rampant among individuals of both groups." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/tajaran + name = "Sapients - Tajaran" + desc = "Tajaran are a race of humanoid mammalian aliens from Meralar, the fourth planet \ + of the Rarkajar star system. Thickly furred and protected from cold, they thrive on \ + their subartic planet, where the only terran temperate areas spread across the \ + equator and \"tropical belt.\"\ +

                    \ + With their own share of bloody wars and great technological advances, the Tajaran are a \ + proud kind. They fiercely believe they belong among the stars and consider themselves \ + a rightful interstellar nation, even if Humanity helped them to actually achieve \ + superluminar speeds with Bluespace FTL drives.\ +

                    \ + Relatively new to the galaxy, their contacts with other species are aloof, but friendly. \ + Among these bonds, Humanity stands out as valued trade partner and maybe even friend." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/dionaea + name = "Sapients - Dionaea" + desc = "The Dionaea are a group of intensely curious plant-like organisms. An individual \ + Diona is a single dog-sized creature called a nymphs, and multiple nymphs link together \ + to form larger, more intelligent collectives. Discovered by the Skrell in and around \ + the stars in the Epsilon Ursae Minoris system, they have accompanied the Skrell in \ + warbling throughout the cosmos as a key part of Skrellian starships, stations, \ + and terraforming equipment.\ +

                    \ + Dionaea have no concept of violence or individual identity and want little in \ + terms of material resources or living space. This makes Dionaea among the most \ + agreeable members of the galactic community, though their slow, curious alien \ + minds can be hard to sympathize with." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/teshari + name = "Sapients - Teshari" + desc = "The Teshari are reptilian pack predators from the Skrell homeworld. \ + While they evolved alongside the Skrell, their interactions with them tended \ + to be confused and violent, and until peaceful contact was made they largely \ + stayed in their territories on and around the poles, in tundral terrain far \ + too desolate and cold to be of interest to the Skrell. In more enlightened \ + times, the Teshari are a minority culture on many Skrell worlds, maintaining \ + their own settlements and cultures, but often finding themselves standing \ + on the shoulders of their more technologically advanced neighbors when it \ + comes to meeting and exploring the rest of the galaxy." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/zaddat + name = "Sapients - Zaddat" + desc = "The Zaddat are an Unathi client species that has recently come to the \ + Golden Crescent. They wear high-pressure voidsuits called Shrouds to protect \ + themselves from the harsh light and low pressure of the station, making \ + medical care a challenge and fighting especially dangerous. \ + Operating out of massive Colony ships, they trade their labor to their \ + host nation to fund their search for a new home to replace their \ + now-lifeless homeworld of Xohox." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/promethean + name = "Sapients - Promethean" + desc = "Prometheans (Macrolimus artificialis) are a species of artificially-created \ + gelatinous humanoids, chiefly characterized by their primarily liquid bodies and \ + ability to change their bodily shape and color in order to mimic many forms of life. \ + Derived from the Aetolian giant slime (Macrolimus vulgaris) inhabiting the warm, \ + tropical planet of Aetolus, they are a relatively newly lab-created sapient species, \ + and as such many things about them have yet to be comprehensively studied." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/vox + name = "Sapients - Vox" + desc = "Probably the best known of these aliens are the Vox, a bird-like species \ + with a very rough comprehension of Galactic Common and an even looser understanding \ + of property rights. Vox raiders have plagued human merchants for centuries, \ + and Skrell for even longer, but remain poorly understood. \ + They have no desire to partake in diplomacy or trade with the rest of the galaxy, \ + or even to conquer planets and stations to live in. They breathe phoron \ + and appear to be well adapted to their role as space-faring raiders, \ + leading many to speculate that they're heavily bioengineered, \ + an assumption which is at odds with their ramshackle technological level." + value = CATALOGUER_REWARD_MEDIUM // Since Vox are much rarer. + + +/datum/category_item/catalogue/technology + +/datum/category_item/catalogue/technology/drone/drones + name = "Drones" + desc = "A drone is a software-based artificial intelligence, generally about an order of magnitude \ + less intelligent than a positronic brain. However, the processing power available to a drone can \ + vary wildly, from cleaning bots barely more advanced than those from the 21st century to cutting-edge \ + supercomputers capable of complex conversation. Drones are legally objects in all starfaring polities \ + outside of the Almach Association, and the sapience of even the most advanced drones is a matter of speculation." + value = CATALOGUER_REWARD_TRIVIAL + // Scanning any drone mob will get you this alongside the mob entry itself. + unlocked_by_any = list(/datum/category_item/catalogue/technology/drone) + +/datum/category_item/catalogue/technology/positronics + name = "Sapients - Positronics" + desc = "A Positronic being, often an Android, Gynoid, or Robot, is an individual with a positronic brain, \ + manufactured and fostered amongst organic life Positronic brains enjoy the same legal status as a humans, \ + although discrimination is still common, are considered sapient on all accounts, and can be considered \ + the \"synthetic species\". Half-developed and half-discovered in the 2280's by a black lab studying alien \ + artifacts, the first positronic brain was an inch-wide cube of palladium-iridium alloy, nano-etched with \ + billions upon billions of conduits and connections. Upon activation, hard-booted by way of an emitter \ + laser, the brain issued a single sentence before the neural pathways collapsed and it became an inert \ + lump of platinum: \"What is my purpose?\"." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/technology/cyborgs + name = "Cyborgs" + desc = "A Cyborg is an originally organic being composed of largely cybernetic parts. As a brain preserved \ + in an MMI, they may inhabit an expensive humanoid chassis, a specially designed industrial shell of some \ + sort, or be integrated into a computer system as an AI. The term covers all species \ + (even, in some cases, animal brains) and all applications. It can also be used somewhat derogatorily \ + for those who are still have more organic parts than just their brains, but for example have a \ + full set of prosthetic limbs." + value = CATALOGUER_REWARD_TRIVIAL + + +/datum/category_item/catalogue/information + +// For these we can piggyback off of the lore datums that are already defined and used in some places. +/datum/category_item/catalogue/information/organization + value = CATALOGUER_REWARD_TRIVIAL + var/datum_to_copy = null + +/datum/category_item/catalogue/information/organization/New() + ..() + if(datum_to_copy) + // I'd just access the loremaster object but it might not exist because its ugly. + var/datum/lore/organization/O = new datum_to_copy() + // I would also change the name based on the org datum but changing the name messes up indexing in some lists in the category/collection object attached to us. + + // Now lets combine the data in the datum for a slightly more presentable entry. + var/constructed_desc = "" + + if(O.motto) + constructed_desc += "
                    \"[O.motto]\"


                    " + + constructed_desc += O.desc + + desc = constructed_desc + qdel(O) + +/datum/category_item/catalogue/information/organization/nanotrasen + name = "TSC - NanoTrasen Incorporated" + datum_to_copy = /datum/lore/organization/tsc/nanotrasen + +/datum/category_item/catalogue/information/organization/hephaestus + name = "TSC - Hephaestus Industries" + datum_to_copy = /datum/lore/organization/tsc/hephaestus + +/datum/category_item/catalogue/information/organization/vey_med + name = "TSC - Vey-Medical" + datum_to_copy = /datum/lore/organization/tsc/vey_med + +/datum/category_item/catalogue/information/organization/zeng_hu + name = "TSC - Zeng Hu Pharmaceuticals" + datum_to_copy = /datum/lore/organization/tsc/zeng_hu + +/datum/category_item/catalogue/information/organization/ward_takahashi + name = "TSC - Ward-Takahashi General Manufacturing Conglomerate" + datum_to_copy = /datum/lore/organization/tsc/ward_takahashi + +/datum/category_item/catalogue/information/organization/bishop + name = "TSC - Bishop Cybernetics" + datum_to_copy = /datum/lore/organization/tsc/bishop + +/datum/category_item/catalogue/information/organization/morpheus + name = "TSC - Morpheus Cyberkinetics" + datum_to_copy = /datum/lore/organization/tsc/morpheus + +/datum/category_item/catalogue/information/organization/xion + name = "TSC - Xion Manufacturing Group" + datum_to_copy = /datum/lore/organization/tsc/xion + +/datum/category_item/catalogue/information/organization/major_bills + name = "TSC - Major Bill's Transportation" + datum_to_copy = /datum/lore/organization/tsc/mbt + +/datum/category_item/catalogue/information/organization/solgov //YW EDIT + name = "Government - Solar Confederate Government" //YW EDIT + datum_to_copy = /datum/lore/organization/gov/solgov //YW EDIT + +/* //VOREStation Removal +/datum/category_item/catalogue/information/organization/virgov + name = "Government - Vir Governmental Authority" + datum_to_copy = /datum/lore/organization/gov/virgov +*/ + +/datum/category_item/catalogue/anomalous + + +/datum/category_item/catalogue/anomalous/precursor_controversy + name = "Precursor Controversy" + desc = "The term 'Precursor' is generally used to refer to one or more ancient races that \ + had obtained vast technological and cultural progress, but no longer appear to be present, \ + leaving behind what remains of their creations, as well as many questions for the races that \ + would stumble upon their ruins. Scientists and xenoarcheologists have been hard at work, trying \ + to uncover the truth.\ +

                    \ + In modern times, there is controversy over the accuracy of what knowledge has been uncovered. \ + The mainstream scientific opinion had been that there was one, and only one ancient species, \ + called the Singularitarians. This view still is the majority today, however there has also \ + been dissent over that view, as some feel that the possibility of multiple precursor \ + civilizations should not be ignored. They point towards a large number of discrepancies between \ + the dominant Singularitarian theory, and various artifacts that have been found, as well as \ + different artifacts uncovered appearing to have very different characteristics to each other. \ + Instead, they say that the Singularitarians were one of multiple precursors.\ +

                    \ + Presently, no conclusive evidence exists for any side." + value = CATALOGUER_REWARD_TRIVIAL + // Add the other precursor groups here when they get added. + unlocked_by_any = list( + /datum/category_item/catalogue/anomalous/precursor_a, + /datum/category_item/catalogue/anomalous/precursor_b + ) + +/datum/category_item/catalogue/anomalous/singularitarians + name = "Precursors - Singularitarians" + desc = "The Singularitarians were a massive, highly-advanced spacefaring race which are now \ + believed to be extinct. At their height, they extended throughout all of known human space, \ + with major population centers in the Precursor's Crypt region, as well as significant swaths \ + of Skrell space, until they were wiped out by a self-replicating nanobot plague that still \ + coats their ruins as a fine layer of dust. They left behind the proto-positronics, as well \ + as several high-yield phoron deposits and other artifacts of technology studied, \ + cautiously, by the races that survived them.\ +

                    \ + Very little is known about the biology and physiology of the Singularitarians, who are believed \ + to have been largely post-biological. The Vox claim to be the race that created the positronics, \ + but said claim is only ever brought up when they claim the right to take any positronic they want. \ + Some more open-minded xenoarcheologists have voiced the opinion that there is some truth in their \ + claims, but it's far from a scientific consensus." + value = CATALOGUER_REWARD_TRIVIAL + unlocked_by_any = list(/datum/category_item/catalogue/anomalous/precursor_controversy) + +// Obtained by scanning any 'precursor a' object, generally things in the UFO PoI. +// A is for Ayyyyyy. +/datum/category_item/catalogue/anomalous/precursor_a/precursor_a_basic + name = "Precursors - Precursor Group Alpha" + desc = "This describes a group of xenoarcheological findings which have strong similarities \ + together. Specifically, this group of objects appears to have a strong aesthetic for the colors \ + cyan and pink, both colors often being present on everything in this group. It is unknown why \ + these two colors were chosen by their creators. Another similarity is that most objects made \ + in this group appear to be comprised of not well understood metallic materials that are dark, \ + and very resilient. Some objects in this group also appear to utilize electricity to \ + operate. Finally, a large number of objects in this group appear to have been made \ + to be used by the creators of those objects in a physical manner.\ +

                    \ + It should be noted that the findings in this group appear to conflict heavily with what is \ + known about the Singularitarians, giving some credence towards these objects belonging to a \ + separate precursor. As such, the findings have been partitioned inside this scanner to this \ + group, labeled Precursor Group Alpha." + value = CATALOGUER_REWARD_TRIVIAL + unlocked_by_any = list(/datum/category_item/catalogue/anomalous/precursor_a) + +// Obtained by scanning any 'precursor b' object, generally things dug up from xenoarch. +// B is for buried. +/datum/category_item/catalogue/anomalous/precursor_b/precursor_b_basic + name = "Precursors - Precursor Group Beta" + + +/datum/category_item/catalogue/material diff --git a/code/modules/catalogue/cataloguer.dm b/code/modules/catalogue/cataloguer.dm index 75a9acf6dad..580b209b9e0 100644 --- a/code/modules/catalogue/cataloguer.dm +++ b/code/modules/catalogue/cataloguer.dm @@ -1,383 +1,383 @@ -GLOBAL_LIST_EMPTY(all_cataloguers) - -/* - This is a special scanner which exists to give explorers something to do besides shoot things. - The scanner is able to be used on certain things in the world, and after a variable delay, the scan finishes, - giving the person who scanned it some fluff and information about what they just scanned, - as well as points that currently do nothing but measure epeen, - and will be used as currency in The Future(tm) to buy things explorers care about. - - Scanning hostile mobs and objects is tricky since only mobs that are alive are scannable, so scanning - them requires careful position to stay out of harms way until the scan finishes. That is why - the person with the scanner gets a visual box that shows where they are allowed to move to - without inturrupting the scan. -*/ -/obj/item/device/cataloguer - name = "cataloguer" - desc = "A hand-held device, used for compiling information about an object by scanning it. Alt+click to highlight scannable objects around you." - description_info = "This is a special device used to obtain information about objects and entities in the environment. \ - To scan something, click on it with the scanner at a distance. \ - Scanning something requires remaining within a certain radius of the object for a specific period of time, until the \ - scan is finished. If the scan is interrupted, it can be resumed from where it was left off, if the same thing is \ - scanned again." - icon = 'icons/obj/device_vr.dmi' - icon_state = "cataloguer" - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_MATERIAL = 2, TECH_DATA = 3, TECH_MAGNET = 3) - force = 0 - slot_flags = SLOT_BELT - var/points_stored = 0 // Amount of 'exploration points' this device holds. - var/scan_range = 3 // How many tiles away it can scan. Changing this also changes the box size. - var/credit_sharing_range = 280 // If another person is within this radius, they will also be credited with a successful scan. Original was 14 - var/datum/category_item/catalogue/displayed_data = null // Used for viewing a piece of data in the UI. - var/busy = FALSE // Set to true when scanning, to stop multiple scans. - var/debug = FALSE // If true, can view all catalogue data defined, regardless of unlock status. - var/datum/weakref/partial_scanned = null // Weakref of the thing that was last scanned if inturrupted. Used to allow for partial scans to be resumed. - var/partial_scan_time = 0 // How much to make the next scan shorter. - -/obj/item/device/cataloguer/advanced - name = "advanced cataloguer" - icon = 'icons/obj/device.dmi' - icon_state = "adv_cataloguer" - desc = "A hand-held device, used for compiling information about an object by scanning it. This one is an upgraded model, \ - with a scanner that both can scan from farther away, and with less time." - scan_range = 4 - toolspeed = 0.8 - -// Able to see all defined catalogue data regardless of if it was unlocked, intended for testing. -/obj/item/device/cataloguer/debug - name = "omniscient cataloguer" - desc = "A hand-held cataloguer device that appears to be plated with gold. For some reason, it \ - just seems to already know everything about narrowly defined pieces of knowledge one would find \ - from nearby, perhaps due to being colored gold. Truly a epistemological mystery." - icon = 'icons/obj/device.dmi' - icon_state = "debug_cataloguer" - toolspeed = 0.1 - scan_range = 7 - debug = TRUE - - -/obj/item/device/cataloguer/Initialize() - GLOB.all_cataloguers += src - return ..() - -/obj/item/device/cataloguer/Destroy() - GLOB.all_cataloguers -= src - displayed_data = null - return ..() - -/obj/item/device/cataloguer/update_icon() - if(busy) - icon_state = "[initial(icon_state)]_active" - else - icon_state = initial(icon_state) - -/obj/item/device/cataloguer/afterattack(atom/target, mob/user, proximity_flag) - // Things that invalidate the scan immediately. - if(busy) - to_chat(user, span("warning", "\The [src] is already scanning something.")) - return - - if(isturf(target) && (!target.can_catalogue())) - var/turf/T = target - for(var/atom/A as anything in T) // If we can't scan the turf, see if we can scan anything on it, to help with aiming. - if(A.can_catalogue()) - target = A - break - - if(!target.can_catalogue(user)) // This will tell the user what is wrong. - return - - if(get_dist(target, user) > scan_range) - to_chat(user, span("warning", "You are too far away from \the [target] to catalogue it. Get closer.")) - return - - // Get how long the delay will be. - var/scan_delay = target.get_catalogue_delay() * toolspeed - if(partial_scanned) - if(partial_scanned.resolve() == target) - scan_delay -= partial_scan_time - to_chat(user, span("notice", "Resuming previous scan.")) - else - to_chat(user, span("warning", "Scanning new target. Previous scan buffer cleared.")) - - // Start the special effects. - busy = TRUE - update_icon() - var/datum/beam/scan_beam = user.Beam(target, icon_state = "rped_upgrade", time = scan_delay) - var/filter = filter(type = "outline", size = 1, color = "#FFFFFF") - target.filters += filter - var/list/box_segments = list() - if(user.client) - box_segments = draw_box(target, scan_range, user.client) - color_box(box_segments, "#00FFFF", scan_delay) - - playsound(src, 'sound/machines/beep.ogg', 50) - - // The delay, and test for if the scan succeeds or not. - var/scan_start_time = world.time - if(do_after(user, scan_delay, target, ignore_movement = TRUE, max_distance = scan_range)) - if(target.can_catalogue(user)) - to_chat(user, span("notice", "You successfully scan \the [target] with \the [src].")) - playsound(src, 'sound/machines/ping.ogg', 50) - catalogue_object(target, user) - else - // In case someone else scans it first, or it died, etc. - to_chat(user, span("warning", "\The [target] is no longer valid to scan with \the [src].")) - playsound(src, 'sound/machines/buzz-two.ogg', 50) - - partial_scanned = null - partial_scan_time = 0 - else - to_chat(user, span("warning", "You failed to finish scanning \the [target] with \the [src].")) - playsound(src, 'sound/machines/buzz-two.ogg', 50) - color_box(box_segments, "#FF0000", 3) - partial_scanned = WEAKREF(target) - partial_scan_time += world.time - scan_start_time // This is added to the existing value so two partial scans will add up correctly. - sleep(3) - busy = FALSE - - // Now clean up the effects. - update_icon() - QDEL_NULL(scan_beam) - if(target) - target.filters -= filter - if(user.client) // If for some reason they logged out mid-scan the box will be gone anyways. - delete_box(box_segments, user.client) - -// Todo: Display scanned information, increment points, etc. -/obj/item/device/cataloguer/proc/catalogue_object(atom/target, mob/living/user) - // Figure out who may have helped out. - var/list/contributers = list() - var/list/contributer_names = list() - for(var/mob/living/L as anything in player_list) - if(L == user) - continue - if(!istype(L)) - continue - if(get_dist(L, user) <= credit_sharing_range) - contributers += L - contributer_names += L.name - - var/points_gained = 0 - - // Discover each datum available. - var/list/object_data = target.get_catalogue_data() - if(LAZYLEN(object_data)) - for(var/data_type in object_data) - var/datum/category_item/catalogue/I = GLOB.catalogue_data.resolve_item(data_type) - if(istype(I)) - var/list/discoveries = I.discover(user, list(user.name) + contributer_names) // If one discovery leads to another, the list returned will have all of them. - if(LAZYLEN(discoveries)) - for(var/datum/category_item/catalogue/data as anything in discoveries) - points_gained += data.value - - // Give out points. - if(points_gained) - // First, to us. - to_chat(user, span("notice", "Gained [points_gained] points from this scan.")) - adjust_points(points_gained) - - // Now to our friends, if any. - if(contributers.len) - for(var/mob/M in contributers) - var/list/things = M.GetAllContents(3) // Depth of two should reach into bags but just in case lets make it three. - var/obj/item/device/cataloguer/other_cataloguer = locate() in things // If someone has two or more scanners this only adds points to one. - if(other_cataloguer) - to_chat(M, span("notice", "Gained [points_gained] points from \the [user]'s scan of \the [target].")) - other_cataloguer.adjust_points(points_gained) - to_chat(user, span("notice", "Shared discovery with [contributers.len] other contributer\s.")) - - - - -/obj/item/device/cataloguer/AltClick(mob/user) - pulse_scan(user) - -// Gives everything capable of being scanned an outline for a brief moment. -// Helps to avoid having to click a hundred things in a room for things that have an entry. -/obj/item/device/cataloguer/proc/pulse_scan(mob/user) - if(busy) - to_chat(user, span("warning", "\The [src] is busy doing something else.")) - return - - busy = TRUE - update_icon() - playsound(src, 'sound/machines/beep.ogg', 50) - - // First, get everything able to be scanned. - var/list/scannable_atoms = list() - for(var/atom/A as anything in view(world.view, user)) - if(A.can_catalogue()) // Not passing the user is intentional, so they don't get spammed. - scannable_atoms += A - - // Highlight things able to be scanned. - var/filter = filter(type = "outline", size = 1, color = "#00FF00") - for(var/atom/A as anything in scannable_atoms) - A.filters += filter - to_chat(user, span("notice", "\The [src] is highlighting scannable objects in green, if any exist.")) - - sleep(2 SECONDS) - - // Remove the highlights. - for(var/atom/A as anything in scannable_atoms) - if(QDELETED(A)) - continue - A.filters -= filter - - busy = FALSE - update_icon() - if(scannable_atoms.len) - playsound(src, 'sound/machines/ping.ogg', 50) - else - playsound(src, 'sound/machines/buzz-two.ogg', 50) - to_chat(user, span("notice", "\The [src] found [scannable_atoms.len] object\s that can be scanned.")) - - -// Negative points are bad. -/obj/item/device/cataloguer/proc/adjust_points(amount) - points_stored = max(0, points_stored += amount) - -/obj/item/device/cataloguer/attack_self(mob/living/user) - interact(user) - -/obj/item/device/cataloguer/interact(mob/user) - var/list/dat = list() - var/title = "Cataloguer Data Display" - - // Important buttons go on top since the scrollbar will default to the top of the window. - dat += "Contains [points_stored] Exploration Points." - dat += "\[Highlight Scannables\]\[Refresh\]\[Close\]" - - // If displayed_data exists, we show that, otherwise we show a list of all data in the mysterious global list. - if(displayed_data) - title = uppertext(displayed_data.name) - - dat += "\[Back to List\]" - if(debug && !displayed_data.visible) - dat += "\[(DEBUG) Force Discovery\]" - dat += "
                    " - - dat += "[displayed_data.desc]" - if(LAZYLEN(displayed_data.cataloguers)) - dat += "Cataloguers : [english_list(displayed_data.cataloguers)]." - else - dat += "Catalogued by nobody." - dat += "Worth [displayed_data.value] exploration points." - - else - dat += "
                    " - for(var/datum/category_group/group as anything in GLOB.catalogue_data.categories) - var/list/group_dat = list() - var/show_group = FALSE - - group_dat += "[group.name]" - for(var/datum/category_item/catalogue/item as anything in group.items) - if(item.visible || debug) - group_dat += "[item.name]" - show_group = TRUE - - if(show_group || debug) // Avoid showing 'empty' groups on regular cataloguers. - dat += group_dat - - var/datum/browser/popup = new(user, "cataloguer_display_\ref[src]", title, 500, 600, src) - popup.set_content(dat.Join("
                    ")) - popup.open() - add_fingerprint(user) - -/obj/item/device/cataloguer/Topic(href, href_list) - if(..()) - usr << browse(null, "window=cataloguer_display") - return 0 - if(href_list["close"] ) - usr << browse(null, "window=cataloguer_display") - return 0 - - if(href_list["show_data"]) - displayed_data = locate(href_list["show_data"]) - - if(href_list["pulse_scan"]) - pulse_scan(usr) - return // Don't refresh the window for this or it will open it back if its closed during the highlighting. - - if(href_list["debug_unlock"] && debug) - var/datum/category_item/catalogue/item = locate(href_list["debug_unlock"]) - item.discover(usr, list("Debugger")) - - interact(usr) // So it refreshes the window. - return 1 - -/obj/item/device/cataloguer/attackby(obj/item/weapon/W, mob/user) - if(istype(W, /obj/item/weapon/card/id) && !busy) - busy = TRUE - var/obj/item/weapon/card/id/ID = W - if(points_stored) - ID.survey_points += points_stored - points_stored = 0 - to_chat(user, "You swipe the id over \the [src].") - else - to_chat(user, "\The [src] has no points available.") - busy = FALSE - return ..() - -/obj/item/device/cataloguer/compact - name = "compact cataloguer" - desc = "A compact hand-held device, used for compiling information about an object by scanning it. \ - Alt+click to highlight scannable objects around you." - icon = 'icons/obj/device_vr.dmi' - icon_state = "compact" - action_button_name = "Toggle Cataloguer" - var/deployed = TRUE - scan_range = 1 - toolspeed = 1.2 - -/obj/item/device/cataloguer/compact/pathfinder - name = "pathfinder's cataloguer" - desc = "A compact hand-held device, used for compiling information about an object by scanning it. \ - Alt+click to highlight scannable objects around you." - icon = 'icons/obj/device_vr.dmi' - icon_state = "pathcat" - scan_range = 3 - toolspeed = 1 - -/obj/item/device/cataloguer/compact/update_icon() - if(busy) - icon_state = "[initial(icon_state)]_s" - else - icon_state = initial(icon_state) - -/obj/item/device/cataloguer/compact/ui_action_click() - toggle() - -/obj/item/device/cataloguer/compact/verb/toggle() - set name = "Toggle Cataloguer" - set category = "Object" - - if(busy) - to_chat(usr, span("warning", "\The [src] is currently scanning something.")) - return - deployed = !(deployed) - if(deployed) - w_class = ITEMSIZE_NORMAL - icon_state = "[initial(icon_state)]" - to_chat(usr, span("notice", "You flick open \the [src].")) - else - w_class = ITEMSIZE_SMALL - icon_state = "[initial(icon_state)]_closed" - to_chat(usr, span("notice", "You close \the [src].")) - - if (ismob(usr)) - var/mob/M = usr - M.update_action_buttons() - -/obj/item/device/cataloguer/compact/afterattack(atom/target, mob/user, proximity_flag) - if(!deployed) - to_chat(user, span("warning", "\The [src] is closed.")) - return - return ..() - -/obj/item/device/cataloguer/compact/pulse_scan(mob/user) - if(!deployed) - to_chat(user, span("warning", "\The [src] is closed.")) - return - return ..() +GLOBAL_LIST_EMPTY(all_cataloguers) + +/* + This is a special scanner which exists to give explorers something to do besides shoot things. + The scanner is able to be used on certain things in the world, and after a variable delay, the scan finishes, + giving the person who scanned it some fluff and information about what they just scanned, + as well as points that currently do nothing but measure epeen, + and will be used as currency in The Future(tm) to buy things explorers care about. + + Scanning hostile mobs and objects is tricky since only mobs that are alive are scannable, so scanning + them requires careful position to stay out of harms way until the scan finishes. That is why + the person with the scanner gets a visual box that shows where they are allowed to move to + without inturrupting the scan. +*/ +/obj/item/device/cataloguer + name = "cataloguer" + desc = "A hand-held device, used for compiling information about an object by scanning it. Alt+click to highlight scannable objects around you." + description_info = "This is a special device used to obtain information about objects and entities in the environment. \ + To scan something, click on it with the scanner at a distance. \ + Scanning something requires remaining within a certain radius of the object for a specific period of time, until the \ + scan is finished. If the scan is interrupted, it can be resumed from where it was left off, if the same thing is \ + scanned again." + icon = 'icons/obj/device_vr.dmi' + icon_state = "cataloguer" + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_MATERIAL = 2, TECH_DATA = 3, TECH_MAGNET = 3) + force = 0 + slot_flags = SLOT_BELT + var/points_stored = 0 // Amount of 'exploration points' this device holds. + var/scan_range = 3 // How many tiles away it can scan. Changing this also changes the box size. + var/credit_sharing_range = 280 // If another person is within this radius, they will also be credited with a successful scan. Original was 14 + var/datum/category_item/catalogue/displayed_data = null // Used for viewing a piece of data in the UI. + var/busy = FALSE // Set to true when scanning, to stop multiple scans. + var/debug = FALSE // If true, can view all catalogue data defined, regardless of unlock status. + var/datum/weakref/partial_scanned = null // Weakref of the thing that was last scanned if inturrupted. Used to allow for partial scans to be resumed. + var/partial_scan_time = 0 // How much to make the next scan shorter. + +/obj/item/device/cataloguer/advanced + name = "advanced cataloguer" + icon = 'icons/obj/device.dmi' + icon_state = "adv_cataloguer" + desc = "A hand-held device, used for compiling information about an object by scanning it. This one is an upgraded model, \ + with a scanner that both can scan from farther away, and with less time." + scan_range = 4 + toolspeed = 0.8 + +// Able to see all defined catalogue data regardless of if it was unlocked, intended for testing. +/obj/item/device/cataloguer/debug + name = "omniscient cataloguer" + desc = "A hand-held cataloguer device that appears to be plated with gold. For some reason, it \ + just seems to already know everything about narrowly defined pieces of knowledge one would find \ + from nearby, perhaps due to being colored gold. Truly a epistemological mystery." + icon = 'icons/obj/device.dmi' + icon_state = "debug_cataloguer" + toolspeed = 0.1 + scan_range = 7 + debug = TRUE + + +/obj/item/device/cataloguer/Initialize() + GLOB.all_cataloguers += src + return ..() + +/obj/item/device/cataloguer/Destroy() + GLOB.all_cataloguers -= src + displayed_data = null + return ..() + +/obj/item/device/cataloguer/update_icon() + if(busy) + icon_state = "[initial(icon_state)]_active" + else + icon_state = initial(icon_state) + +/obj/item/device/cataloguer/afterattack(atom/target, mob/user, proximity_flag) + // Things that invalidate the scan immediately. + if(busy) + to_chat(user, span("warning", "\The [src] is already scanning something.")) + return + + if(isturf(target) && (!target.can_catalogue())) + var/turf/T = target + for(var/atom/A as anything in T) // If we can't scan the turf, see if we can scan anything on it, to help with aiming. + if(A.can_catalogue()) + target = A + break + + if(!target.can_catalogue(user)) // This will tell the user what is wrong. + return + + if(get_dist(target, user) > scan_range) + to_chat(user, span("warning", "You are too far away from \the [target] to catalogue it. Get closer.")) + return + + // Get how long the delay will be. + var/scan_delay = target.get_catalogue_delay() * toolspeed + if(partial_scanned) + if(partial_scanned.resolve() == target) + scan_delay -= partial_scan_time + to_chat(user, span("notice", "Resuming previous scan.")) + else + to_chat(user, span("warning", "Scanning new target. Previous scan buffer cleared.")) + + // Start the special effects. + busy = TRUE + update_icon() + var/datum/beam/scan_beam = user.Beam(target, icon_state = "rped_upgrade", time = scan_delay) + var/filter = filter(type = "outline", size = 1, color = "#FFFFFF") + target.filters += filter + var/list/box_segments = list() + if(user.client) + box_segments = draw_box(target, scan_range, user.client) + color_box(box_segments, "#00FFFF", scan_delay) + + playsound(src, 'sound/machines/beep.ogg', 50) + + // The delay, and test for if the scan succeeds or not. + var/scan_start_time = world.time + if(do_after(user, scan_delay, target, ignore_movement = TRUE, max_distance = scan_range)) + if(target.can_catalogue(user)) + to_chat(user, span("notice", "You successfully scan \the [target] with \the [src].")) + playsound(src, 'sound/machines/ping.ogg', 50) + catalogue_object(target, user) + else + // In case someone else scans it first, or it died, etc. + to_chat(user, span("warning", "\The [target] is no longer valid to scan with \the [src].")) + playsound(src, 'sound/machines/buzz-two.ogg', 50) + + partial_scanned = null + partial_scan_time = 0 + else + to_chat(user, span("warning", "You failed to finish scanning \the [target] with \the [src].")) + playsound(src, 'sound/machines/buzz-two.ogg', 50) + color_box(box_segments, "#FF0000", 3) + partial_scanned = WEAKREF(target) + partial_scan_time += world.time - scan_start_time // This is added to the existing value so two partial scans will add up correctly. + sleep(3) + busy = FALSE + + // Now clean up the effects. + update_icon() + QDEL_NULL(scan_beam) + if(target) + target.filters -= filter + if(user.client) // If for some reason they logged out mid-scan the box will be gone anyways. + delete_box(box_segments, user.client) + +// Todo: Display scanned information, increment points, etc. +/obj/item/device/cataloguer/proc/catalogue_object(atom/target, mob/living/user) + // Figure out who may have helped out. + var/list/contributers = list() + var/list/contributer_names = list() + for(var/mob/living/L as anything in player_list) + if(L == user) + continue + if(!istype(L)) + continue + if(get_dist(L, user) <= credit_sharing_range) + contributers += L + contributer_names += L.name + + var/points_gained = 0 + + // Discover each datum available. + var/list/object_data = target.get_catalogue_data() + if(LAZYLEN(object_data)) + for(var/data_type in object_data) + var/datum/category_item/catalogue/I = GLOB.catalogue_data.resolve_item(data_type) + if(istype(I)) + var/list/discoveries = I.discover(user, list(user.name) + contributer_names) // If one discovery leads to another, the list returned will have all of them. + if(LAZYLEN(discoveries)) + for(var/datum/category_item/catalogue/data as anything in discoveries) + points_gained += data.value + + // Give out points. + if(points_gained) + // First, to us. + to_chat(user, span("notice", "Gained [points_gained] points from this scan.")) + adjust_points(points_gained) + + // Now to our friends, if any. + if(contributers.len) + for(var/mob/M in contributers) + var/list/things = M.GetAllContents(3) // Depth of two should reach into bags but just in case lets make it three. + var/obj/item/device/cataloguer/other_cataloguer = locate() in things // If someone has two or more scanners this only adds points to one. + if(other_cataloguer) + to_chat(M, span("notice", "Gained [points_gained] points from \the [user]'s scan of \the [target].")) + other_cataloguer.adjust_points(points_gained) + to_chat(user, span("notice", "Shared discovery with [contributers.len] other contributer\s.")) + + + + +/obj/item/device/cataloguer/AltClick(mob/user) + pulse_scan(user) + +// Gives everything capable of being scanned an outline for a brief moment. +// Helps to avoid having to click a hundred things in a room for things that have an entry. +/obj/item/device/cataloguer/proc/pulse_scan(mob/user) + if(busy) + to_chat(user, span("warning", "\The [src] is busy doing something else.")) + return + + busy = TRUE + update_icon() + playsound(src, 'sound/machines/beep.ogg', 50) + + // First, get everything able to be scanned. + var/list/scannable_atoms = list() + for(var/atom/A as anything in view(world.view, user)) + if(A.can_catalogue()) // Not passing the user is intentional, so they don't get spammed. + scannable_atoms += A + + // Highlight things able to be scanned. + var/filter = filter(type = "outline", size = 1, color = "#00FF00") + for(var/atom/A as anything in scannable_atoms) + A.filters += filter + to_chat(user, span("notice", "\The [src] is highlighting scannable objects in green, if any exist.")) + + sleep(2 SECONDS) + + // Remove the highlights. + for(var/atom/A as anything in scannable_atoms) + if(QDELETED(A)) + continue + A.filters -= filter + + busy = FALSE + update_icon() + if(scannable_atoms.len) + playsound(src, 'sound/machines/ping.ogg', 50) + else + playsound(src, 'sound/machines/buzz-two.ogg', 50) + to_chat(user, span("notice", "\The [src] found [scannable_atoms.len] object\s that can be scanned.")) + + +// Negative points are bad. +/obj/item/device/cataloguer/proc/adjust_points(amount) + points_stored = max(0, points_stored += amount) + +/obj/item/device/cataloguer/attack_self(mob/living/user) + interact(user) + +/obj/item/device/cataloguer/interact(mob/user) + var/list/dat = list() + var/title = "Cataloguer Data Display" + + // Important buttons go on top since the scrollbar will default to the top of the window. + dat += "Contains [points_stored] Exploration Points." + dat += "\[Highlight Scannables\]\[Refresh\]\[Close\]" + + // If displayed_data exists, we show that, otherwise we show a list of all data in the mysterious global list. + if(displayed_data) + title = uppertext(displayed_data.name) + + dat += "\[Back to List\]" + if(debug && !displayed_data.visible) + dat += "\[(DEBUG) Force Discovery\]" + dat += "
                    " + + dat += "[displayed_data.desc]" + if(LAZYLEN(displayed_data.cataloguers)) + dat += "Cataloguers : [english_list(displayed_data.cataloguers)]." + else + dat += "Catalogued by nobody." + dat += "Worth [displayed_data.value] exploration points." + + else + dat += "
                    " + for(var/datum/category_group/group as anything in GLOB.catalogue_data.categories) + var/list/group_dat = list() + var/show_group = FALSE + + group_dat += "[group.name]" + for(var/datum/category_item/catalogue/item as anything in group.items) + if(item.visible || debug) + group_dat += "[item.name]" + show_group = TRUE + + if(show_group || debug) // Avoid showing 'empty' groups on regular cataloguers. + dat += group_dat + + var/datum/browser/popup = new(user, "cataloguer_display_\ref[src]", title, 500, 600, src) + popup.set_content(dat.Join("
                    ")) + popup.open() + add_fingerprint(user) + +/obj/item/device/cataloguer/Topic(href, href_list) + if(..()) + usr << browse(null, "window=cataloguer_display") + return 0 + if(href_list["close"] ) + usr << browse(null, "window=cataloguer_display") + return 0 + + if(href_list["show_data"]) + displayed_data = locate(href_list["show_data"]) + + if(href_list["pulse_scan"]) + pulse_scan(usr) + return // Don't refresh the window for this or it will open it back if its closed during the highlighting. + + if(href_list["debug_unlock"] && debug) + var/datum/category_item/catalogue/item = locate(href_list["debug_unlock"]) + item.discover(usr, list("Debugger")) + + interact(usr) // So it refreshes the window. + return 1 + +/obj/item/device/cataloguer/attackby(obj/item/weapon/W, mob/user) + if(istype(W, /obj/item/weapon/card/id) && !busy) + busy = TRUE + var/obj/item/weapon/card/id/ID = W + if(points_stored) + ID.survey_points += points_stored + points_stored = 0 + to_chat(user, "You swipe the id over \the [src].") + else + to_chat(user, "\The [src] has no points available.") + busy = FALSE + return ..() + +/obj/item/device/cataloguer/compact + name = "compact cataloguer" + desc = "A compact hand-held device, used for compiling information about an object by scanning it. \ + Alt+click to highlight scannable objects around you." + icon = 'icons/obj/device_vr.dmi' + icon_state = "compact" + action_button_name = "Toggle Cataloguer" + var/deployed = TRUE + scan_range = 1 + toolspeed = 1.2 + +/obj/item/device/cataloguer/compact/pathfinder + name = "pathfinder's cataloguer" + desc = "A compact hand-held device, used for compiling information about an object by scanning it. \ + Alt+click to highlight scannable objects around you." + icon = 'icons/obj/device_vr.dmi' + icon_state = "pathcat" + scan_range = 3 + toolspeed = 1 + +/obj/item/device/cataloguer/compact/update_icon() + if(busy) + icon_state = "[initial(icon_state)]_s" + else + icon_state = initial(icon_state) + +/obj/item/device/cataloguer/compact/ui_action_click() + toggle() + +/obj/item/device/cataloguer/compact/verb/toggle() + set name = "Toggle Cataloguer" + set category = "Object" + + if(busy) + to_chat(usr, span("warning", "\The [src] is currently scanning something.")) + return + deployed = !(deployed) + if(deployed) + w_class = ITEMSIZE_NORMAL + icon_state = "[initial(icon_state)]" + to_chat(usr, span("notice", "You flick open \the [src].")) + else + w_class = ITEMSIZE_SMALL + icon_state = "[initial(icon_state)]_closed" + to_chat(usr, span("notice", "You close \the [src].")) + + if (ismob(usr)) + var/mob/M = usr + M.update_action_buttons() + +/obj/item/device/cataloguer/compact/afterattack(atom/target, mob/user, proximity_flag) + if(!deployed) + to_chat(user, span("warning", "\The [src] is closed.")) + return + return ..() + +/obj/item/device/cataloguer/compact/pulse_scan(mob/user) + if(!deployed) + to_chat(user, span("warning", "\The [src] is closed.")) + return + return ..() diff --git a/code/modules/catalogue/cataloguer_visuals.dm b/code/modules/catalogue/cataloguer_visuals.dm index cf8a446533d..f159271a026 100644 --- a/code/modules/catalogue/cataloguer_visuals.dm +++ b/code/modules/catalogue/cataloguer_visuals.dm @@ -1,68 +1,68 @@ -#define ICON_SIZE 32 - -// Draws a box showing the limits of movement while scanning something. -// Only the client supplied will see the box. -/obj/item/device/cataloguer/proc/draw_box(atom/A, box_size, client/C) - . = list() - // Things moved with pixel_[x|y] will move the box, so this is to correct that. - var/pixel_x_correction = -A.pixel_x - var/pixel_y_correction = -A.pixel_y - - // First, place the bottom-left corner. - . += draw_line(A, SOUTHWEST, (-box_size * ICON_SIZE) + pixel_x_correction, (-box_size * ICON_SIZE) + pixel_y_correction, C) - - // Make a line on the bottom, going right. - for(var/i = 1 to (box_size * 2) - 1) - var/x_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_x_correction - var/y_displacement = (-box_size * ICON_SIZE) + pixel_y_correction - . += draw_line(A, SOUTH, x_displacement, y_displacement, C) - - // Bottom-right corner. - . += draw_line(A, SOUTHEAST, (box_size * ICON_SIZE) + pixel_x_correction, (-box_size * ICON_SIZE) + pixel_y_correction, C) - - // Second line, for the right side going up. - for(var/i = 1 to (box_size * 2) - 1) - var/x_displacement = (box_size * ICON_SIZE) + pixel_x_correction - var/y_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_y_correction - . += draw_line(A, EAST, x_displacement, y_displacement, C) - - // Top-right corner. - . += draw_line(A, NORTHEAST, (box_size * ICON_SIZE) + pixel_x_correction, (box_size * ICON_SIZE) + pixel_y_correction, C) - - // Third line, for the top, going right. - for(var/i = 1 to (box_size * 2) - 1) - var/x_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_x_correction - var/y_displacement = (box_size * ICON_SIZE) + pixel_y_correction - . += draw_line(A, NORTH, x_displacement, y_displacement, C) - - // Top-left corner. - . += draw_line(A, NORTHWEST, (-box_size * ICON_SIZE) + pixel_x_correction, (box_size * ICON_SIZE) + pixel_y_correction, C) - - // Fourth and last line, for the left side going up. - for(var/i = 1 to (box_size * 2) - 1) - var/x_displacement = (-box_size * ICON_SIZE) + pixel_x_correction - var/y_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_y_correction - . += draw_line(A, WEST, x_displacement, y_displacement, C) - -#undef ICON_SIZE - -// Draws an individual segment of the box. -/obj/item/device/cataloguer/proc/draw_line(atom/A, line_dir, line_pixel_x, line_pixel_y, client/C) - var/image/line = image(icon = 'icons/effects/effects.dmi', loc = A, icon_state = "stripes", dir = line_dir) - line.pixel_x = line_pixel_x - line.pixel_y = line_pixel_y - line.plane = PLANE_FULLSCREEN // It's technically a HUD element but it doesn't need to show above item slots. - line.appearance_flags = RESET_TRANSFORM|RESET_COLOR|RESET_ALPHA|NO_CLIENT_COLOR|TILE_BOUND - line.alpha = 125 - C.images += line - return line - -// Removes the box that was generated before from the client. -/obj/item/device/cataloguer/proc/delete_box(list/box_segments, client/C) - for(var/i in box_segments) - C.images -= i - qdel(i) - -/obj/item/device/cataloguer/proc/color_box(list/box_segments, new_color, new_time) - for(var/i in box_segments) +#define ICON_SIZE 32 + +// Draws a box showing the limits of movement while scanning something. +// Only the client supplied will see the box. +/obj/item/device/cataloguer/proc/draw_box(atom/A, box_size, client/C) + . = list() + // Things moved with pixel_[x|y] will move the box, so this is to correct that. + var/pixel_x_correction = -A.pixel_x + var/pixel_y_correction = -A.pixel_y + + // First, place the bottom-left corner. + . += draw_line(A, SOUTHWEST, (-box_size * ICON_SIZE) + pixel_x_correction, (-box_size * ICON_SIZE) + pixel_y_correction, C) + + // Make a line on the bottom, going right. + for(var/i = 1 to (box_size * 2) - 1) + var/x_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_x_correction + var/y_displacement = (-box_size * ICON_SIZE) + pixel_y_correction + . += draw_line(A, SOUTH, x_displacement, y_displacement, C) + + // Bottom-right corner. + . += draw_line(A, SOUTHEAST, (box_size * ICON_SIZE) + pixel_x_correction, (-box_size * ICON_SIZE) + pixel_y_correction, C) + + // Second line, for the right side going up. + for(var/i = 1 to (box_size * 2) - 1) + var/x_displacement = (box_size * ICON_SIZE) + pixel_x_correction + var/y_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_y_correction + . += draw_line(A, EAST, x_displacement, y_displacement, C) + + // Top-right corner. + . += draw_line(A, NORTHEAST, (box_size * ICON_SIZE) + pixel_x_correction, (box_size * ICON_SIZE) + pixel_y_correction, C) + + // Third line, for the top, going right. + for(var/i = 1 to (box_size * 2) - 1) + var/x_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_x_correction + var/y_displacement = (box_size * ICON_SIZE) + pixel_y_correction + . += draw_line(A, NORTH, x_displacement, y_displacement, C) + + // Top-left corner. + . += draw_line(A, NORTHWEST, (-box_size * ICON_SIZE) + pixel_x_correction, (box_size * ICON_SIZE) + pixel_y_correction, C) + + // Fourth and last line, for the left side going up. + for(var/i = 1 to (box_size * 2) - 1) + var/x_displacement = (-box_size * ICON_SIZE) + pixel_x_correction + var/y_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_y_correction + . += draw_line(A, WEST, x_displacement, y_displacement, C) + +#undef ICON_SIZE + +// Draws an individual segment of the box. +/obj/item/device/cataloguer/proc/draw_line(atom/A, line_dir, line_pixel_x, line_pixel_y, client/C) + var/image/line = image(icon = 'icons/effects/effects.dmi', loc = A, icon_state = "stripes", dir = line_dir) + line.pixel_x = line_pixel_x + line.pixel_y = line_pixel_y + line.plane = PLANE_FULLSCREEN // It's technically a HUD element but it doesn't need to show above item slots. + line.appearance_flags = RESET_TRANSFORM|RESET_COLOR|RESET_ALPHA|NO_CLIENT_COLOR|TILE_BOUND + line.alpha = 125 + C.images += line + return line + +// Removes the box that was generated before from the client. +/obj/item/device/cataloguer/proc/delete_box(list/box_segments, client/C) + for(var/i in box_segments) + C.images -= i + qdel(i) + +/obj/item/device/cataloguer/proc/color_box(list/box_segments, new_color, new_time) + for(var/i in box_segments) animate(i, color = new_color, time = new_time) \ No newline at end of file diff --git a/code/modules/client/preference_setup/antagonism/01_basic.dm b/code/modules/client/preference_setup/antagonism/01_basic.dm index 56f05795a30..8dc760a00ff 100644 --- a/code/modules/client/preference_setup/antagonism/01_basic.dm +++ b/code/modules/client/preference_setup/antagonism/01_basic.dm @@ -1,72 +1,72 @@ -var/global/list/uplink_locations = list("PDA", "Headset", "None") - -/datum/category_item/player_setup_item/antagonism/basic - name = "Basic" - sort_order = 1 - -/datum/category_item/player_setup_item/antagonism/basic/load_character(var/savefile/S) - S["uplinklocation"] >> pref.uplinklocation - S["exploit_record"] >> pref.exploit_record - S["antag_faction"] >> pref.antag_faction - S["antag_vis"] >> pref.antag_vis - -/datum/category_item/player_setup_item/antagonism/basic/save_character(var/savefile/S) - S["uplinklocation"] << pref.uplinklocation - S["exploit_record"] << pref.exploit_record - S["antag_faction"] << pref.antag_faction - S["antag_vis"] << pref.antag_vis - -/datum/category_item/player_setup_item/antagonism/basic/sanitize_character() - pref.uplinklocation = sanitize_inlist(pref.uplinklocation, uplink_locations, initial(pref.uplinklocation)) - if(!pref.antag_faction) pref.antag_faction = "None" - if(!pref.antag_vis) pref.antag_vis = "Hidden" - -// Moved from /datum/preferences/proc/copy_to() -/datum/category_item/player_setup_item/antagonism/basic/copy_to_mob(var/mob/living/carbon/human/character) - character.exploit_record = pref.exploit_record - character.antag_faction = pref.antag_faction - character.antag_vis = pref.antag_vis - -/datum/category_item/player_setup_item/antagonism/basic/content(var/mob/user) - . += "Faction: [pref.antag_faction]
                    " - . += "Visibility: [pref.antag_vis]
                    " - . +="Uplink Type : [pref.uplinklocation]" - . +="
                    " - . +="Exploitable information:
                    " - if(jobban_isbanned(user, "Records")) - . += "You are banned from using character records.
                    " - else - . +="[TextPreview(pref.exploit_record,40)]
                    " - -/datum/category_item/player_setup_item/antagonism/basic/OnTopic(var/href,var/list/href_list, var/mob/user) - if (href_list["antagtask"]) - pref.uplinklocation = next_in_list(pref.uplinklocation, uplink_locations) - return TOPIC_REFRESH - - if(href_list["exploitable_record"]) - var/exploitmsg = sanitize(tgui_input_text(user,"Set exploitable information about you here.","Exploitable Information", html_decode(pref.exploit_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH, extra = 0) - if(!isnull(exploitmsg) && !jobban_isbanned(user, "Records") && CanUseTopic(user)) - pref.exploit_record = exploitmsg - return TOPIC_REFRESH - - if(href_list["antagfaction"]) - var/choice = tgui_input_list(user, "Please choose an antagonistic faction to work for.", "Character Preference", antag_faction_choices + list("None","Other"), pref.antag_faction) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = sanitize(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice) - pref.antag_faction = raw_choice - else - pref.antag_faction = choice - return TOPIC_REFRESH - - if(href_list["antagvis"]) - var/choice = tgui_input_list(user, "Please choose an antagonistic visibility level.", "Character Preference", antag_visiblity_choices, pref.antag_vis) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - else - pref.antag_vis = choice - return TOPIC_REFRESH - - return ..() +var/global/list/uplink_locations = list("PDA", "Headset", "None") + +/datum/category_item/player_setup_item/antagonism/basic + name = "Basic" + sort_order = 1 + +/datum/category_item/player_setup_item/antagonism/basic/load_character(var/savefile/S) + S["uplinklocation"] >> pref.uplinklocation + S["exploit_record"] >> pref.exploit_record + S["antag_faction"] >> pref.antag_faction + S["antag_vis"] >> pref.antag_vis + +/datum/category_item/player_setup_item/antagonism/basic/save_character(var/savefile/S) + S["uplinklocation"] << pref.uplinklocation + S["exploit_record"] << pref.exploit_record + S["antag_faction"] << pref.antag_faction + S["antag_vis"] << pref.antag_vis + +/datum/category_item/player_setup_item/antagonism/basic/sanitize_character() + pref.uplinklocation = sanitize_inlist(pref.uplinklocation, uplink_locations, initial(pref.uplinklocation)) + if(!pref.antag_faction) pref.antag_faction = "None" + if(!pref.antag_vis) pref.antag_vis = "Hidden" + +// Moved from /datum/preferences/proc/copy_to() +/datum/category_item/player_setup_item/antagonism/basic/copy_to_mob(var/mob/living/carbon/human/character) + character.exploit_record = pref.exploit_record + character.antag_faction = pref.antag_faction + character.antag_vis = pref.antag_vis + +/datum/category_item/player_setup_item/antagonism/basic/content(var/mob/user) + . += "Faction: [pref.antag_faction]
                    " + . += "Visibility: [pref.antag_vis]
                    " + . +="Uplink Type : [pref.uplinklocation]" + . +="
                    " + . +="Exploitable information:
                    " + if(jobban_isbanned(user, "Records")) + . += "You are banned from using character records.
                    " + else + . +="[TextPreview(pref.exploit_record,40)]
                    " + +/datum/category_item/player_setup_item/antagonism/basic/OnTopic(var/href,var/list/href_list, var/mob/user) + if (href_list["antagtask"]) + pref.uplinklocation = next_in_list(pref.uplinklocation, uplink_locations) + return TOPIC_REFRESH + + if(href_list["exploitable_record"]) + var/exploitmsg = sanitize(tgui_input_text(user,"Set exploitable information about you here.","Exploitable Information", html_decode(pref.exploit_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH, extra = 0) + if(!isnull(exploitmsg) && !jobban_isbanned(user, "Records") && CanUseTopic(user)) + pref.exploit_record = exploitmsg + return TOPIC_REFRESH + + if(href_list["antagfaction"]) + var/choice = tgui_input_list(user, "Please choose an antagonistic faction to work for.", "Character Preference", antag_faction_choices + list("None","Other"), pref.antag_faction) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = sanitize(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice) + pref.antag_faction = raw_choice + else + pref.antag_faction = choice + return TOPIC_REFRESH + + if(href_list["antagvis"]) + var/choice = tgui_input_list(user, "Please choose an antagonistic visibility level.", "Character Preference", antag_visiblity_choices, pref.antag_vis) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + else + pref.antag_vis = choice + return TOPIC_REFRESH + + return ..() diff --git a/code/modules/client/preference_setup/general/05_background.dm b/code/modules/client/preference_setup/general/05_background.dm index 7e712889668..70b2e130bda 100644 --- a/code/modules/client/preference_setup/general/05_background.dm +++ b/code/modules/client/preference_setup/general/05_background.dm @@ -1,173 +1,173 @@ -/datum/category_item/player_setup_item/general/background - name = "Background" - sort_order = 5 - -/datum/category_item/player_setup_item/general/background/load_character(var/savefile/S) - S["med_record"] >> pref.med_record - S["sec_record"] >> pref.sec_record - S["gen_record"] >> pref.gen_record - S["home_system"] >> pref.home_system - S["birthplace"] >> pref.birthplace - S["citizenship"] >> pref.citizenship - S["faction"] >> pref.faction - S["religion"] >> pref.religion - S["economic_status"] >> pref.economic_status - -/datum/category_item/player_setup_item/general/background/save_character(var/savefile/S) - S["med_record"] << pref.med_record - S["sec_record"] << pref.sec_record - S["gen_record"] << pref.gen_record - S["home_system"] << pref.home_system - S["birthplace"] << pref.birthplace - S["citizenship"] << pref.citizenship - S["faction"] << pref.faction - S["religion"] << pref.religion - S["economic_status"] << pref.economic_status - -/datum/category_item/player_setup_item/general/background/sanitize_character() - if(!pref.home_system) pref.home_system = "Unset" - if(!pref.birthplace) pref.birthplace = "Unset" - if(!pref.citizenship) pref.citizenship = "None" - if(!pref.faction) pref.faction = "None" - if(!pref.religion) pref.religion = "None" - - pref.economic_status = sanitize_inlist(pref.economic_status, ECONOMIC_CLASS, initial(pref.economic_status)) - -// Moved from /datum/preferences/proc/copy_to() -/datum/category_item/player_setup_item/general/background/copy_to_mob(var/mob/living/carbon/human/character) - character.med_record = pref.med_record - character.sec_record = pref.sec_record - character.gen_record = pref.gen_record - character.home_system = pref.home_system - character.birthplace = pref.birthplace - character.citizenship = pref.citizenship - character.personal_faction = pref.faction - character.religion = pref.religion - -/datum/category_item/player_setup_item/general/background/content(var/mob/user) - . += "Background Information
                    " - . += "Economic Status: [pref.economic_status]
                    " - . += "Home: [pref.home_system]
                    " - . += "Birthplace: [pref.birthplace]
                    " - . += "Citizenship: [pref.citizenship]
                    " - . += "Faction: [pref.faction]
                    " - . += "Religion: [pref.religion]
                    " - - . += "
                    Records:
                    " - if(jobban_isbanned(user, "Records")) - . += "You are banned from using character records.
                    " - else - . += "Medical Records:
                    " - . += "[TextPreview(pref.med_record,40)]
                    " - . += " (Reset)

                    " - . += "Employment Records:
                    " - . += "[TextPreview(pref.gen_record,40)]
                    " - . += "(Reset)

                    " - . += "Security Records:
                    " - . += "[TextPreview(pref.sec_record,40)]
                    " - . += "(Reset)" - -/datum/category_item/player_setup_item/general/background/OnTopic(var/href,var/list/href_list, var/mob/user) - if(href_list["econ_status"]) - var/new_class = tgui_input_list(user, "Choose your economic status. This will affect the amount of money you will start with.", "Character Preference", ECONOMIC_CLASS, pref.economic_status) - if(new_class && CanUseTopic(user)) - pref.economic_status = new_class - return TOPIC_REFRESH - - else if(href_list["home_system"]) - var/choice = tgui_input_list(user, "Please choose your home planet and/or system. This should be your current primary residence. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.home_system) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a home system.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice && CanUseTopic(user)) - pref.home_system = raw_choice - else - pref.home_system = choice - return TOPIC_REFRESH - - else if(href_list["birthplace"]) - var/choice = tgui_input_list(user, "Please choose the planet and/or system or other appropriate location that you were born/created. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.birthplace) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a birthplace.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice && CanUseTopic(user)) - pref.birthplace = raw_choice - else - pref.birthplace = choice - return TOPIC_REFRESH - - else if(href_list["citizenship"]) - var/choice = tgui_input_list(user, "Please select the faction or political entity with which you currently hold citizenship. Select \"Other\" to specify manually.", "Character Preference", citizenship_choices + list("None","Other"), pref.citizenship) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter your current citizenship.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice && CanUseTopic(user)) - pref.citizenship = raw_choice - else - pref.citizenship = choice - return TOPIC_REFRESH - - else if(href_list["faction"]) - var/choice = tgui_input_list(user, "Please choose the faction you primarily work for, if you are not under the direct employ of NanoTrasen. Select \"Other\" to specify manually.", "Character Preference", faction_choices + list("None","Other"), pref.faction) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice) - pref.faction = raw_choice - else - pref.faction = choice - return TOPIC_REFRESH - - else if(href_list["religion"]) - var/choice = tgui_input_list(user, "Please choose a religion. Select \"Other\" to specify manually.", "Character Preference", religion_choices + list("None","Other"), pref.religion) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a religon.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice) - pref.religion = sanitize(raw_choice) - else - pref.religion = choice - return TOPIC_REFRESH - - else if(href_list["set_medical_records"]) - var/new_medical = strip_html_simple(tgui_input_text(user,"Enter medical information here.","Character Preference", html_decode(pref.med_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) - if(new_medical && !jobban_isbanned(user, "Records") && CanUseTopic(user)) - pref.med_record = new_medical - return TOPIC_REFRESH - - else if(href_list["set_general_records"]) - var/new_general = strip_html_simple(tgui_input_text(user,"Enter employment information here.","Character Preference", html_decode(pref.gen_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) - if(new_general && !jobban_isbanned(user, "Records") && CanUseTopic(user)) - pref.gen_record = new_general - return TOPIC_REFRESH - - else if(href_list["set_security_records"]) - var/sec_medical = strip_html_simple(tgui_input_text(user,"Enter security information here.","Character Preference", html_decode(pref.sec_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) - if(sec_medical && !jobban_isbanned(user, "Records") && CanUseTopic(user)) - pref.sec_record = sec_medical - return TOPIC_REFRESH - - else if(href_list["reset_medrecord"]) - var/resetmed_choice = tgui_alert(usr, "Wipe your Medical Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) - if(resetmed_choice == "Yes") - pref.med_record = null - return TOPIC_REFRESH - - else if(href_list["reset_emprecord"]) - var/resetemp_choice = tgui_alert(usr, "Wipe your Employment Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) - if(resetemp_choice == "Yes") - pref.gen_record = null - return TOPIC_REFRESH - - else if(href_list["reset_secrecord"]) - var/resetsec_choice = tgui_alert(usr, "Wipe your Security Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) - if(resetsec_choice == "Yes") - pref.sec_record = null - return TOPIC_REFRESH - - return ..() +/datum/category_item/player_setup_item/general/background + name = "Background" + sort_order = 5 + +/datum/category_item/player_setup_item/general/background/load_character(var/savefile/S) + S["med_record"] >> pref.med_record + S["sec_record"] >> pref.sec_record + S["gen_record"] >> pref.gen_record + S["home_system"] >> pref.home_system + S["birthplace"] >> pref.birthplace + S["citizenship"] >> pref.citizenship + S["faction"] >> pref.faction + S["religion"] >> pref.religion + S["economic_status"] >> pref.economic_status + +/datum/category_item/player_setup_item/general/background/save_character(var/savefile/S) + S["med_record"] << pref.med_record + S["sec_record"] << pref.sec_record + S["gen_record"] << pref.gen_record + S["home_system"] << pref.home_system + S["birthplace"] << pref.birthplace + S["citizenship"] << pref.citizenship + S["faction"] << pref.faction + S["religion"] << pref.religion + S["economic_status"] << pref.economic_status + +/datum/category_item/player_setup_item/general/background/sanitize_character() + if(!pref.home_system) pref.home_system = "Unset" + if(!pref.birthplace) pref.birthplace = "Unset" + if(!pref.citizenship) pref.citizenship = "None" + if(!pref.faction) pref.faction = "None" + if(!pref.religion) pref.religion = "None" + + pref.economic_status = sanitize_inlist(pref.economic_status, ECONOMIC_CLASS, initial(pref.economic_status)) + +// Moved from /datum/preferences/proc/copy_to() +/datum/category_item/player_setup_item/general/background/copy_to_mob(var/mob/living/carbon/human/character) + character.med_record = pref.med_record + character.sec_record = pref.sec_record + character.gen_record = pref.gen_record + character.home_system = pref.home_system + character.birthplace = pref.birthplace + character.citizenship = pref.citizenship + character.personal_faction = pref.faction + character.religion = pref.religion + +/datum/category_item/player_setup_item/general/background/content(var/mob/user) + . += "Background Information
                    " + . += "Economic Status: [pref.economic_status]
                    " + . += "Home: [pref.home_system]
                    " + . += "Birthplace: [pref.birthplace]
                    " + . += "Citizenship: [pref.citizenship]
                    " + . += "Faction: [pref.faction]
                    " + . += "Religion: [pref.religion]
                    " + + . += "
                    Records:
                    " + if(jobban_isbanned(user, "Records")) + . += "You are banned from using character records.
                    " + else + . += "Medical Records:
                    " + . += "[TextPreview(pref.med_record,40)]
                    " + . += " (Reset)

                    " + . += "Employment Records:
                    " + . += "[TextPreview(pref.gen_record,40)]
                    " + . += "(Reset)

                    " + . += "Security Records:
                    " + . += "[TextPreview(pref.sec_record,40)]
                    " + . += "(Reset)" + +/datum/category_item/player_setup_item/general/background/OnTopic(var/href,var/list/href_list, var/mob/user) + if(href_list["econ_status"]) + var/new_class = tgui_input_list(user, "Choose your economic status. This will affect the amount of money you will start with.", "Character Preference", ECONOMIC_CLASS, pref.economic_status) + if(new_class && CanUseTopic(user)) + pref.economic_status = new_class + return TOPIC_REFRESH + + else if(href_list["home_system"]) + var/choice = tgui_input_list(user, "Please choose your home planet and/or system. This should be your current primary residence. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.home_system) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a home system.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice && CanUseTopic(user)) + pref.home_system = raw_choice + else + pref.home_system = choice + return TOPIC_REFRESH + + else if(href_list["birthplace"]) + var/choice = tgui_input_list(user, "Please choose the planet and/or system or other appropriate location that you were born/created. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.birthplace) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a birthplace.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice && CanUseTopic(user)) + pref.birthplace = raw_choice + else + pref.birthplace = choice + return TOPIC_REFRESH + + else if(href_list["citizenship"]) + var/choice = tgui_input_list(user, "Please select the faction or political entity with which you currently hold citizenship. Select \"Other\" to specify manually.", "Character Preference", citizenship_choices + list("None","Other"), pref.citizenship) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter your current citizenship.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice && CanUseTopic(user)) + pref.citizenship = raw_choice + else + pref.citizenship = choice + return TOPIC_REFRESH + + else if(href_list["faction"]) + var/choice = tgui_input_list(user, "Please choose the faction you primarily work for, if you are not under the direct employ of NanoTrasen. Select \"Other\" to specify manually.", "Character Preference", faction_choices + list("None","Other"), pref.faction) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice) + pref.faction = raw_choice + else + pref.faction = choice + return TOPIC_REFRESH + + else if(href_list["religion"]) + var/choice = tgui_input_list(user, "Please choose a religion. Select \"Other\" to specify manually.", "Character Preference", religion_choices + list("None","Other"), pref.religion) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a religon.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice) + pref.religion = sanitize(raw_choice) + else + pref.religion = choice + return TOPIC_REFRESH + + else if(href_list["set_medical_records"]) + var/new_medical = strip_html_simple(tgui_input_text(user,"Enter medical information here.","Character Preference", html_decode(pref.med_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) + if(new_medical && !jobban_isbanned(user, "Records") && CanUseTopic(user)) + pref.med_record = new_medical + return TOPIC_REFRESH + + else if(href_list["set_general_records"]) + var/new_general = strip_html_simple(tgui_input_text(user,"Enter employment information here.","Character Preference", html_decode(pref.gen_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) + if(new_general && !jobban_isbanned(user, "Records") && CanUseTopic(user)) + pref.gen_record = new_general + return TOPIC_REFRESH + + else if(href_list["set_security_records"]) + var/sec_medical = strip_html_simple(tgui_input_text(user,"Enter security information here.","Character Preference", html_decode(pref.sec_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) + if(sec_medical && !jobban_isbanned(user, "Records") && CanUseTopic(user)) + pref.sec_record = sec_medical + return TOPIC_REFRESH + + else if(href_list["reset_medrecord"]) + var/resetmed_choice = tgui_alert(usr, "Wipe your Medical Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) + if(resetmed_choice == "Yes") + pref.med_record = null + return TOPIC_REFRESH + + else if(href_list["reset_emprecord"]) + var/resetemp_choice = tgui_alert(usr, "Wipe your Employment Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) + if(resetemp_choice == "Yes") + pref.gen_record = null + return TOPIC_REFRESH + + else if(href_list["reset_secrecord"]) + var/resetsec_choice = tgui_alert(usr, "Wipe your Security Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) + if(resetsec_choice == "Yes") + pref.sec_record = null + return TOPIC_REFRESH + + return ..() diff --git a/code/modules/client/preference_setup/general/06_flavor.dm b/code/modules/client/preference_setup/general/06_flavor.dm index 20ab615e786..76af38f5dc9 100644 --- a/code/modules/client/preference_setup/general/06_flavor.dm +++ b/code/modules/client/preference_setup/general/06_flavor.dm @@ -1,154 +1,154 @@ -/datum/category_item/player_setup_item/general/flavor - name = "Flavor" - sort_order = 6 - -/datum/category_item/player_setup_item/general/flavor/load_character(var/savefile/S) - S["flavor_texts_general"] >> pref.flavor_texts["general"] - S["flavor_texts_head"] >> pref.flavor_texts["head"] - S["flavor_texts_face"] >> pref.flavor_texts["face"] - S["flavor_texts_eyes"] >> pref.flavor_texts["eyes"] - S["flavor_texts_torso"] >> pref.flavor_texts["torso"] - S["flavor_texts_arms"] >> pref.flavor_texts["arms"] - S["flavor_texts_hands"] >> pref.flavor_texts["hands"] - S["flavor_texts_legs"] >> pref.flavor_texts["legs"] - S["flavor_texts_feet"] >> pref.flavor_texts["feet"] - S["custom_link"] >> pref.custom_link - //Flavour text for robots. - S["flavour_texts_robot_Default"] >> pref.flavour_texts_robot["Default"] - for(var/module in robot_module_types) - S["flavour_texts_robot_[module]"] >> pref.flavour_texts_robot[module] - -/datum/category_item/player_setup_item/general/flavor/save_character(var/savefile/S) - S["flavor_texts_general"] << pref.flavor_texts["general"] - S["flavor_texts_head"] << pref.flavor_texts["head"] - S["flavor_texts_face"] << pref.flavor_texts["face"] - S["flavor_texts_eyes"] << pref.flavor_texts["eyes"] - S["flavor_texts_torso"] << pref.flavor_texts["torso"] - S["flavor_texts_arms"] << pref.flavor_texts["arms"] - S["flavor_texts_hands"] << pref.flavor_texts["hands"] - S["flavor_texts_legs"] << pref.flavor_texts["legs"] - S["flavor_texts_feet"] << pref.flavor_texts["feet"] - S["custom_link"] << pref.custom_link - - S["flavour_texts_robot_Default"] << pref.flavour_texts_robot["Default"] - for(var/module in robot_module_types) - S["flavour_texts_robot_[module]"] << pref.flavour_texts_robot[module] - -/datum/category_item/player_setup_item/general/flavor/sanitize_character() - return - -// Moved from /datum/preferences/proc/copy_to() -/datum/category_item/player_setup_item/general/flavor/copy_to_mob(var/mob/living/carbon/human/character) - character.flavor_texts["general"] = pref.flavor_texts["general"] - character.flavor_texts["head"] = pref.flavor_texts["head"] - character.flavor_texts["face"] = pref.flavor_texts["face"] - character.flavor_texts["eyes"] = pref.flavor_texts["eyes"] - character.flavor_texts["torso"] = pref.flavor_texts["torso"] - character.flavor_texts["arms"] = pref.flavor_texts["arms"] - character.flavor_texts["hands"] = pref.flavor_texts["hands"] - character.flavor_texts["legs"] = pref.flavor_texts["legs"] - character.flavor_texts["feet"] = pref.flavor_texts["feet"] - character.ooc_notes = pref.metadata //VOREStation Add - character.ooc_notes_likes = pref.metadata_likes - character.ooc_notes_dislikes = pref.metadata_dislikes - character.custom_link = pref.custom_link - -/datum/category_item/player_setup_item/general/flavor/content(var/mob/user) - . += "Flavor:
                    " - . += "Set Flavor Text
                    " - . += "Set Robot Flavor Text
                    " - . += "Set Custom Link
                    " - -/datum/category_item/player_setup_item/general/flavor/OnTopic(var/href,var/list/href_list, var/mob/user) - if(href_list["flavor_text"]) - switch(href_list["flavor_text"]) - if("open") - if("general") - var/msg = strip_html_simple(tgui_input_text(usr,"Give a general description of your character. This will be shown regardless of clothings. Put in a single space to make blank.","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]]), multiline = TRUE, prevent_enter = TRUE)) //VOREStation Edit: separating out OOC notes - if(CanUseTopic(user) && msg) - pref.flavor_texts[href_list["flavor_text"]] = msg - else - var/msg = strip_html_simple(tgui_input_text(usr,"Set the flavor text for your [href_list["flavor_text"]]. Put in a single space to make blank.","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]]), multiline = TRUE, prevent_enter = TRUE)) - if(CanUseTopic(user) && msg) - pref.flavor_texts[href_list["flavor_text"]] = msg - SetFlavorText(user) - return TOPIC_HANDLED - - else if(href_list["flavour_text_robot"]) - switch(href_list["flavour_text_robot"]) - if("open") - if("Default") - var/msg = strip_html_simple(tgui_input_text(usr,"Set the default flavour text for your robot. It will be used for any module without individual setting. Put in a single space to make blank.","Flavour Text",html_decode(pref.flavour_texts_robot["Default"]), multiline = TRUE, prevent_enter = TRUE)) - if(CanUseTopic(user) && msg) - pref.flavour_texts_robot[href_list["flavour_text_robot"]] = msg - else - var/msg = strip_html_simple(tgui_input_text(usr,"Set the flavour text for your robot with [href_list["flavour_text_robot"]] module. If you leave this blank, default flavour text will be used for this module. Put in a single space to make blank.","Flavour Text",html_decode(pref.flavour_texts_robot[href_list["flavour_text_robot"]]), multiline = TRUE, prevent_enter = TRUE)) - if(CanUseTopic(user) && msg) - pref.flavour_texts_robot[href_list["flavour_text_robot"]] = msg - SetFlavourTextRobot(user) - return TOPIC_HANDLED - else if(href_list["custom_link"]) - var/new_link = strip_html_simple(tgui_input_text(usr, "Enter a link to add on to your examine text! This should be a related image link/gallery, or things like your F-list. This is not the place for memes.", "Custom Link" , html_decode(pref.custom_link), max_length = 100, encode = TRUE, prevent_enter = TRUE)) - if(new_link && CanUseTopic(usr)) - if(length(new_link) > 100) - to_chat(usr, "Your entry is too long, it must be 100 characters or less.") - return - pref.custom_link = new_link - log_admin("[usr]/[usr.ckey] set their custom link to [pref.custom_link]") - - return ..() - -/datum/category_item/player_setup_item/general/flavor/proc/SetFlavorText(mob/user) - var/HTML = "" - HTML += "
                    " - HTML += "Set Flavor Text
                    " - HTML += "Note: This is not *literal* flavor of your character. This is visual description of what they look like.
                    " - HTML += "
                    " - HTML += "General: " - HTML += TextPreview(pref.flavor_texts["general"]) - HTML += "
                    " - HTML += "Head: " - HTML += TextPreview(pref.flavor_texts["head"]) - HTML += "
                    " - HTML += "Face: " - HTML += TextPreview(pref.flavor_texts["face"]) - HTML += "
                    " - HTML += "Eyes: " - HTML += TextPreview(pref.flavor_texts["eyes"]) - HTML += "
                    " - HTML += "Body: " - HTML += TextPreview(pref.flavor_texts["torso"]) - HTML += "
                    " - HTML += "Arms: " - HTML += TextPreview(pref.flavor_texts["arms"]) - HTML += "
                    " - HTML += "Hands: " - HTML += TextPreview(pref.flavor_texts["hands"]) - HTML += "
                    " - HTML += "Legs: " - HTML += TextPreview(pref.flavor_texts["legs"]) - HTML += "
                    " - HTML += "Feet: " - HTML += TextPreview(pref.flavor_texts["feet"]) - HTML += "
                    " - HTML += "
                    " - HTML += "" - user << browse(HTML, "window=flavor_text;size=430x300") - return - -/datum/category_item/player_setup_item/general/flavor/proc/SetFlavourTextRobot(mob/user) - var/HTML = "" - HTML += "
                    " - HTML += "Set Robot Flavour Text
                    " - HTML += "
                    " - HTML += "Default: " - HTML += TextPreview(pref.flavour_texts_robot["Default"]) - HTML += "
                    " - for(var/module in robot_module_types) - HTML += "[module]: " - HTML += TextPreview(pref.flavour_texts_robot[module]) - HTML += "
                    " - HTML += "
                    " - HTML += "" - user << browse(HTML, "window=flavour_text_robot;size=430x300") - return +/datum/category_item/player_setup_item/general/flavor + name = "Flavor" + sort_order = 6 + +/datum/category_item/player_setup_item/general/flavor/load_character(var/savefile/S) + S["flavor_texts_general"] >> pref.flavor_texts["general"] + S["flavor_texts_head"] >> pref.flavor_texts["head"] + S["flavor_texts_face"] >> pref.flavor_texts["face"] + S["flavor_texts_eyes"] >> pref.flavor_texts["eyes"] + S["flavor_texts_torso"] >> pref.flavor_texts["torso"] + S["flavor_texts_arms"] >> pref.flavor_texts["arms"] + S["flavor_texts_hands"] >> pref.flavor_texts["hands"] + S["flavor_texts_legs"] >> pref.flavor_texts["legs"] + S["flavor_texts_feet"] >> pref.flavor_texts["feet"] + S["custom_link"] >> pref.custom_link + //Flavour text for robots. + S["flavour_texts_robot_Default"] >> pref.flavour_texts_robot["Default"] + for(var/module in robot_module_types) + S["flavour_texts_robot_[module]"] >> pref.flavour_texts_robot[module] + +/datum/category_item/player_setup_item/general/flavor/save_character(var/savefile/S) + S["flavor_texts_general"] << pref.flavor_texts["general"] + S["flavor_texts_head"] << pref.flavor_texts["head"] + S["flavor_texts_face"] << pref.flavor_texts["face"] + S["flavor_texts_eyes"] << pref.flavor_texts["eyes"] + S["flavor_texts_torso"] << pref.flavor_texts["torso"] + S["flavor_texts_arms"] << pref.flavor_texts["arms"] + S["flavor_texts_hands"] << pref.flavor_texts["hands"] + S["flavor_texts_legs"] << pref.flavor_texts["legs"] + S["flavor_texts_feet"] << pref.flavor_texts["feet"] + S["custom_link"] << pref.custom_link + + S["flavour_texts_robot_Default"] << pref.flavour_texts_robot["Default"] + for(var/module in robot_module_types) + S["flavour_texts_robot_[module]"] << pref.flavour_texts_robot[module] + +/datum/category_item/player_setup_item/general/flavor/sanitize_character() + return + +// Moved from /datum/preferences/proc/copy_to() +/datum/category_item/player_setup_item/general/flavor/copy_to_mob(var/mob/living/carbon/human/character) + character.flavor_texts["general"] = pref.flavor_texts["general"] + character.flavor_texts["head"] = pref.flavor_texts["head"] + character.flavor_texts["face"] = pref.flavor_texts["face"] + character.flavor_texts["eyes"] = pref.flavor_texts["eyes"] + character.flavor_texts["torso"] = pref.flavor_texts["torso"] + character.flavor_texts["arms"] = pref.flavor_texts["arms"] + character.flavor_texts["hands"] = pref.flavor_texts["hands"] + character.flavor_texts["legs"] = pref.flavor_texts["legs"] + character.flavor_texts["feet"] = pref.flavor_texts["feet"] + character.ooc_notes = pref.metadata //VOREStation Add + character.ooc_notes_likes = pref.metadata_likes + character.ooc_notes_dislikes = pref.metadata_dislikes + character.custom_link = pref.custom_link + +/datum/category_item/player_setup_item/general/flavor/content(var/mob/user) + . += "Flavor:
                    " + . += "Set Flavor Text
                    " + . += "Set Robot Flavor Text
                    " + . += "Set Custom Link
                    " + +/datum/category_item/player_setup_item/general/flavor/OnTopic(var/href,var/list/href_list, var/mob/user) + if(href_list["flavor_text"]) + switch(href_list["flavor_text"]) + if("open") + if("general") + var/msg = strip_html_simple(tgui_input_text(usr,"Give a general description of your character. This will be shown regardless of clothings. Put in a single space to make blank.","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]]), multiline = TRUE, prevent_enter = TRUE)) //VOREStation Edit: separating out OOC notes + if(CanUseTopic(user) && msg) + pref.flavor_texts[href_list["flavor_text"]] = msg + else + var/msg = strip_html_simple(tgui_input_text(usr,"Set the flavor text for your [href_list["flavor_text"]]. Put in a single space to make blank.","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]]), multiline = TRUE, prevent_enter = TRUE)) + if(CanUseTopic(user) && msg) + pref.flavor_texts[href_list["flavor_text"]] = msg + SetFlavorText(user) + return TOPIC_HANDLED + + else if(href_list["flavour_text_robot"]) + switch(href_list["flavour_text_robot"]) + if("open") + if("Default") + var/msg = strip_html_simple(tgui_input_text(usr,"Set the default flavour text for your robot. It will be used for any module without individual setting. Put in a single space to make blank.","Flavour Text",html_decode(pref.flavour_texts_robot["Default"]), multiline = TRUE, prevent_enter = TRUE)) + if(CanUseTopic(user) && msg) + pref.flavour_texts_robot[href_list["flavour_text_robot"]] = msg + else + var/msg = strip_html_simple(tgui_input_text(usr,"Set the flavour text for your robot with [href_list["flavour_text_robot"]] module. If you leave this blank, default flavour text will be used for this module. Put in a single space to make blank.","Flavour Text",html_decode(pref.flavour_texts_robot[href_list["flavour_text_robot"]]), multiline = TRUE, prevent_enter = TRUE)) + if(CanUseTopic(user) && msg) + pref.flavour_texts_robot[href_list["flavour_text_robot"]] = msg + SetFlavourTextRobot(user) + return TOPIC_HANDLED + else if(href_list["custom_link"]) + var/new_link = strip_html_simple(tgui_input_text(usr, "Enter a link to add on to your examine text! This should be a related image link/gallery, or things like your F-list. This is not the place for memes.", "Custom Link" , html_decode(pref.custom_link), max_length = 100, encode = TRUE, prevent_enter = TRUE)) + if(new_link && CanUseTopic(usr)) + if(length(new_link) > 100) + to_chat(usr, "Your entry is too long, it must be 100 characters or less.") + return + pref.custom_link = new_link + log_admin("[usr]/[usr.ckey] set their custom link to [pref.custom_link]") + + return ..() + +/datum/category_item/player_setup_item/general/flavor/proc/SetFlavorText(mob/user) + var/HTML = "" + HTML += "
                    " + HTML += "Set Flavor Text
                    " + HTML += "Note: This is not *literal* flavor of your character. This is visual description of what they look like.
                    " + HTML += "
                    " + HTML += "General: " + HTML += TextPreview(pref.flavor_texts["general"]) + HTML += "
                    " + HTML += "Head: " + HTML += TextPreview(pref.flavor_texts["head"]) + HTML += "
                    " + HTML += "Face: " + HTML += TextPreview(pref.flavor_texts["face"]) + HTML += "
                    " + HTML += "Eyes: " + HTML += TextPreview(pref.flavor_texts["eyes"]) + HTML += "
                    " + HTML += "Body: " + HTML += TextPreview(pref.flavor_texts["torso"]) + HTML += "
                    " + HTML += "Arms: " + HTML += TextPreview(pref.flavor_texts["arms"]) + HTML += "
                    " + HTML += "Hands: " + HTML += TextPreview(pref.flavor_texts["hands"]) + HTML += "
                    " + HTML += "Legs: " + HTML += TextPreview(pref.flavor_texts["legs"]) + HTML += "
                    " + HTML += "Feet: " + HTML += TextPreview(pref.flavor_texts["feet"]) + HTML += "
                    " + HTML += "
                    " + HTML += "" + user << browse(HTML, "window=flavor_text;size=430x300") + return + +/datum/category_item/player_setup_item/general/flavor/proc/SetFlavourTextRobot(mob/user) + var/HTML = "" + HTML += "
                    " + HTML += "Set Robot Flavour Text
                    " + HTML += "
                    " + HTML += "Default: " + HTML += TextPreview(pref.flavour_texts_robot["Default"]) + HTML += "
                    " + for(var/module in robot_module_types) + HTML += "[module]: " + HTML += TextPreview(pref.flavour_texts_robot[module]) + HTML += "
                    " + HTML += "
                    " + HTML += "" + user << browse(HTML, "window=flavour_text_robot;size=430x300") + return diff --git a/code/modules/client/preference_setup/skills/skills.dm b/code/modules/client/preference_setup/skills/skills.dm index 00fb15ec902..645314ab590 100644 --- a/code/modules/client/preference_setup/skills/skills.dm +++ b/code/modules/client/preference_setup/skills/skills.dm @@ -1,89 +1,89 @@ -/datum/category_item/player_setup_item/skills - name = "Skills" - sort_order = 1 - -/datum/category_item/player_setup_item/skills/load_character(var/savefile/S) - S["skills"] >> pref.skills - S["used_skillpoints"] >> pref.used_skillpoints - S["skill_specialization"] >> pref.skill_specialization - -/datum/category_item/player_setup_item/skills/save_character(var/savefile/S) - S["skills"] << pref.skills - S["used_skillpoints"] << pref.used_skillpoints - S["skill_specialization"] << pref.skill_specialization - -/datum/category_item/player_setup_item/skills/sanitize_character() - if(SKILLS == null) setup_skills() - if(!pref.skills) pref.skills = list() - if(!pref.skills.len) pref.ZeroSkills() - if(pref.used_skillpoints < 0) pref.used_skillpoints = 0 - -// Moved from /datum/preferences/proc/copy_to() -/datum/category_item/player_setup_item/skills/copy_to_mob(var/mob/living/carbon/human/character) - character.skills = pref.skills - character.used_skillpoints = pref.used_skillpoints - -/datum/category_item/player_setup_item/skills/content() - . = list() - . += "Select your Skills
                    " - . += "Current skill level: [pref.GetSkillClass(pref.used_skillpoints)] ([pref.used_skillpoints])
                    " - . += "Use preconfigured skillset
                    " - . += "" - for(var/V in SKILLS) - . += "" - for(var/datum/skill/S in SKILLS[V]) - var/level = pref.skills[S.ID] - . += "" - . += "" - . += skill_to_button(S, "Untrained", level, SKILL_NONE) - // secondary skills don't have an amateur level - if(S.secondary) - . += "" - else - . += skill_to_button(S, "Amateur", level, SKILL_BASIC) - . += skill_to_button(S, "Trained", level, SKILL_ADEPT) - . += skill_to_button(S, "Professional", level, SKILL_EXPERT) - . += "" - . += "
                    [V]" - . += "
                    [S.name]
                    " - . = jointext(.,null) - -/datum/category_item/player_setup_item/proc/skill_to_button(var/skill, var/level_name, var/current_level, var/selection_level) - if(current_level == selection_level) - return "[level_name]" - return "[level_name]" - -/datum/category_item/player_setup_item/skills/OnTopic(href, href_list, user) - if(href_list["skillinfo"]) - var/datum/skill/S = locate(href_list["skillinfo"]) - var/HTML = "[S.name]
                    [S.desc]" - user << browse(HTML, "window=\ref[user]skillinfo") - return TOPIC_HANDLED - - else if(href_list["setskill"]) - var/datum/skill/S = locate(href_list["setskill"]) - var/value = text2num(href_list["newvalue"]) - pref.skills[S.ID] = value - pref.CalculateSkillPoints() - return TOPIC_REFRESH - - else if(href_list["preconfigured"]) - var/selected = tgui_input_list(user, "Select a skillset", "Skillset", SKILL_PRE) - if(!selected || !CanUseTopic(user)) return - - pref.ZeroSkills(1) - for(var/V in SKILL_PRE[selected]) - if(V == "field") - pref.skill_specialization = SKILL_PRE[selected]["field"] - continue - pref.skills[V] = SKILL_PRE[selected][V] - pref.CalculateSkillPoints() - - return TOPIC_REFRESH - - else if(href_list["setspecialization"]) - pref.skill_specialization = href_list["setspecialization"] - pref.CalculateSkillPoints() - return TOPIC_REFRESH - - return ..() +/datum/category_item/player_setup_item/skills + name = "Skills" + sort_order = 1 + +/datum/category_item/player_setup_item/skills/load_character(var/savefile/S) + S["skills"] >> pref.skills + S["used_skillpoints"] >> pref.used_skillpoints + S["skill_specialization"] >> pref.skill_specialization + +/datum/category_item/player_setup_item/skills/save_character(var/savefile/S) + S["skills"] << pref.skills + S["used_skillpoints"] << pref.used_skillpoints + S["skill_specialization"] << pref.skill_specialization + +/datum/category_item/player_setup_item/skills/sanitize_character() + if(SKILLS == null) setup_skills() + if(!pref.skills) pref.skills = list() + if(!pref.skills.len) pref.ZeroSkills() + if(pref.used_skillpoints < 0) pref.used_skillpoints = 0 + +// Moved from /datum/preferences/proc/copy_to() +/datum/category_item/player_setup_item/skills/copy_to_mob(var/mob/living/carbon/human/character) + character.skills = pref.skills + character.used_skillpoints = pref.used_skillpoints + +/datum/category_item/player_setup_item/skills/content() + . = list() + . += "Select your Skills
                    " + . += "Current skill level: [pref.GetSkillClass(pref.used_skillpoints)] ([pref.used_skillpoints])
                    " + . += "Use preconfigured skillset
                    " + . += "" + for(var/V in SKILLS) + . += "" + for(var/datum/skill/S in SKILLS[V]) + var/level = pref.skills[S.ID] + . += "" + . += "" + . += skill_to_button(S, "Untrained", level, SKILL_NONE) + // secondary skills don't have an amateur level + if(S.secondary) + . += "" + else + . += skill_to_button(S, "Amateur", level, SKILL_BASIC) + . += skill_to_button(S, "Trained", level, SKILL_ADEPT) + . += skill_to_button(S, "Professional", level, SKILL_EXPERT) + . += "" + . += "
                    [V]" + . += "
                    [S.name]
                    " + . = jointext(.,null) + +/datum/category_item/player_setup_item/proc/skill_to_button(var/skill, var/level_name, var/current_level, var/selection_level) + if(current_level == selection_level) + return "[level_name]" + return "[level_name]" + +/datum/category_item/player_setup_item/skills/OnTopic(href, href_list, user) + if(href_list["skillinfo"]) + var/datum/skill/S = locate(href_list["skillinfo"]) + var/HTML = "[S.name]
                    [S.desc]" + user << browse(HTML, "window=\ref[user]skillinfo") + return TOPIC_HANDLED + + else if(href_list["setskill"]) + var/datum/skill/S = locate(href_list["setskill"]) + var/value = text2num(href_list["newvalue"]) + pref.skills[S.ID] = value + pref.CalculateSkillPoints() + return TOPIC_REFRESH + + else if(href_list["preconfigured"]) + var/selected = tgui_input_list(user, "Select a skillset", "Skillset", SKILL_PRE) + if(!selected || !CanUseTopic(user)) return + + pref.ZeroSkills(1) + for(var/V in SKILL_PRE[selected]) + if(V == "field") + pref.skill_specialization = SKILL_PRE[selected]["field"] + continue + pref.skills[V] = SKILL_PRE[selected][V] + pref.CalculateSkillPoints() + + return TOPIC_REFRESH + + else if(href_list["setspecialization"]) + pref.skill_specialization = href_list["setspecialization"] + pref.CalculateSkillPoints() + return TOPIC_REFRESH + + return ..() diff --git a/code/modules/client/preference_setup/vore/02_size.dm b/code/modules/client/preference_setup/vore/02_size.dm index e1a77d2883e..530706c0e22 100644 --- a/code/modules/client/preference_setup/vore/02_size.dm +++ b/code/modules/client/preference_setup/vore/02_size.dm @@ -139,7 +139,7 @@ This measurement should be set relative to a normal 5'10'' person's body and not the actual size of your character.\n\ If you set your weight to 500 because you're a naga or have metal implants then complain that you're a blob I\n\ swear to god I will find you and I will punch you for not reading these directions!\n\ - ([WEIGHT_MIN]-[WEIGHT_MAX])", "Character Preference", null, WEIGHT_MAX, WEIGHT_MIN) + ([WEIGHT_MIN]-[WEIGHT_MAX])", "Character Preference", null, WEIGHT_MAX, WEIGHT_MIN, round_value=FALSE) if(new_weight) var/unit_of_measurement = tgui_alert(user, "Is that number in pounds (lb) or kilograms (kg)?", "Confirmation", list("Pounds", "Kilograms")) if(unit_of_measurement == "Pounds") @@ -153,7 +153,7 @@ var/weight_gain_rate = tgui_input_number(user, "Choose your character's rate of weight gain between 100% \ (full realism body fat gain) and 0% (no body fat gain).\n\ (If you want to disable weight gain, set this to 0.01 to round it to 0%.)\ - ([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_gain) + ([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_gain, WEIGHT_CHANGE_MAX, WEIGHT_CHANGE_MIN, round_value=FALSE) if(weight_gain_rate) pref.weight_gain = round(text2num(weight_gain_rate),1) return TOPIC_REFRESH @@ -162,7 +162,7 @@ var/weight_loss_rate = tgui_input_number(user, "Choose your character's rate of weight loss between 100% \ (full realism body fat loss) and 0% (no body fat loss).\n\ (If you want to disable weight loss, set this to 0.01 round it to 0%.)\ - ([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_loss) + ([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_loss, WEIGHT_CHANGE_MAX, WEIGHT_CHANGE_MIN, round_value=FALSE) if(weight_loss_rate) pref.weight_loss = round(text2num(weight_loss_rate),1) return TOPIC_REFRESH @@ -177,7 +177,7 @@ pref.voice_freq = choice return TOPIC_REFRESH else if(choice == 1) - choice = tgui_input_number(user, "Choose your character's voice frequency, ranging from [MIN_VOICE_FREQ] to [MAX_VOICE_FREQ]", "Custom Voice Frequency", null, MAX_VOICE_FREQ, MIN_VOICE_FREQ, round_value = TRUE) + choice = tgui_input_number(user, "Choose your character's voice frequency, ranging from [MIN_VOICE_FREQ] to [MAX_VOICE_FREQ]", "Custom Voice Frequency", null, MAX_VOICE_FREQ, MIN_VOICE_FREQ) if(choice > MAX_VOICE_FREQ) choice = MAX_VOICE_FREQ else if(choice < MIN_VOICE_FREQ) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index c6bdc7aabfa..1841cfb028a 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -369,9 +369,9 @@ var/list/preferences_datums = list() open_load_dialog(usr) return 1 else if(href_list["resetslot"]) - if("No" == tgui_alert(usr, "This will reset the current slot. Continue?", "Reset current slot?", list("No", "Yes"))) + if("Yes" != tgui_alert(usr, "This will reset the current slot. Continue?", "Reset current slot?", list("No", "Yes"))) return 0 - if("No" == tgui_alert(usr, "Are you completely sure that you want to reset this character slot?", "Reset current slot?", list("No", "Yes"))) + if("Yes" != tgui_alert(usr, "Are you completely sure that you want to reset this character slot?", "Reset current slot?", list("No", "Yes"))) return 0 load_character(SAVE_RESET) sanitize_preferences() diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 47b846efc1f..e98e7de3520 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -1,121 +1,121 @@ -#define SAVEFILE_VERSION_MIN 8 -#define SAVEFILE_VERSION_MAX 11 - -//handles converting savefiles to new formats -//MAKE SURE YOU KEEP THIS UP TO DATE! -//If the sanity checks are capable of handling any issues. Only increase SAVEFILE_VERSION_MAX, -//this will mean that savefile_version will still be over SAVEFILE_VERSION_MIN, meaning -//this savefile update doesn't run everytime we load from the savefile. -//This is mainly for format changes, such as the bitflags in toggles changing order or something. -//if a file can't be updated, return 0 to delete it and start again -//if a file was updated, return 1 -/datum/preferences/proc/savefile_update() - if(savefile_version < 8) //lazily delete everything + additional files so they can be saved in the new format - for(var/ckey in preferences_datums) - var/datum/preferences/D = preferences_datums[ckey] - if(D == src) - var/delpath = "data/player_saves/[copytext(ckey,1,2)]/[ckey]/" - if(delpath && fexists(delpath)) - fdel(delpath) - break - return 0 - - if(savefile_version == SAVEFILE_VERSION_MAX) //update successful. - save_preferences() - save_character() - return 1 - return 0 - -/datum/preferences/proc/load_path(ckey,filename="preferences.sav") - if(!ckey) return - path = "data/player_saves/[copytext(ckey,1,2)]/[ckey]/[filename]" - savefile_version = SAVEFILE_VERSION_MAX - -/datum/preferences/proc/load_preferences() - if(!path) return 0 - if(!fexists(path)) return 0 - var/savefile/S = new /savefile(path) - if(!S) return 0 - S.cd = "/" - - S["version"] >> savefile_version - //Conversion - if(!savefile_version || !isnum(savefile_version) || savefile_version < SAVEFILE_VERSION_MIN || savefile_version > SAVEFILE_VERSION_MAX) - if(!savefile_update()) //handles updates - savefile_version = SAVEFILE_VERSION_MAX - save_preferences() - save_character() - return 0 - - player_setup.load_preferences(S) - return 1 - -/datum/preferences/proc/save_preferences() - if(!path) return 0 - var/savefile/S = new /savefile(path) - if(!S) return 0 - S.cd = "/" - - S["version"] << savefile_version - player_setup.save_preferences(S) - return 1 - -/datum/preferences/proc/load_character(slot) - if(!path) return 0 - if(!fexists(path)) return 0 - var/savefile/S = new /savefile(path) - if(!S) return 0 - S.cd = "/" - if(!slot) slot = default_slot - if(slot != SAVE_RESET) // SAVE_RESET will reset the slot as though it does not exist, but keep the current slot for saving purposes. - slot = sanitize_integer(slot, 1, config.character_slots, initial(default_slot)) - if(slot != default_slot) - default_slot = slot - S["default_slot"] << slot - else - S["default_slot"] << default_slot - - if(slot != SAVE_RESET) - S.cd = "/character[slot]" - player_setup.load_character(S) - else - player_setup.load_character(S) - S.cd = "/character[default_slot]" - player_setup.save_character(S) - - clear_character_previews() // VOREStation Edit - return 1 - -/datum/preferences/proc/save_character() - if(!path) return 0 - var/savefile/S = new /savefile(path) - if(!S) return 0 - S.cd = "/character[default_slot]" - - player_setup.save_character(S) - return 1 - -/datum/preferences/proc/overwrite_character(slot) - if(!path) return 0 - if(!fexists(path)) return 0 - var/savefile/S = new /savefile(path) - if(!S) return 0 - if(!slot) slot = default_slot - if(slot != SAVE_RESET) - slot = sanitize_integer(slot, 1, config.character_slots, initial(default_slot)) - if(slot != default_slot) - default_slot = slot - nif_path = nif_durability = nif_savedata = null //VOREStation Add - Don't copy NIF - S["default_slot"] << slot - - else - S["default_slot"] << default_slot - - return 1 - -/datum/preferences/proc/sanitize_preferences() - player_setup.sanitize_setup() - return 1 - -#undef SAVEFILE_VERSION_MAX -#undef SAVEFILE_VERSION_MIN +#define SAVEFILE_VERSION_MIN 8 +#define SAVEFILE_VERSION_MAX 11 + +//handles converting savefiles to new formats +//MAKE SURE YOU KEEP THIS UP TO DATE! +//If the sanity checks are capable of handling any issues. Only increase SAVEFILE_VERSION_MAX, +//this will mean that savefile_version will still be over SAVEFILE_VERSION_MIN, meaning +//this savefile update doesn't run everytime we load from the savefile. +//This is mainly for format changes, such as the bitflags in toggles changing order or something. +//if a file can't be updated, return 0 to delete it and start again +//if a file was updated, return 1 +/datum/preferences/proc/savefile_update() + if(savefile_version < 8) //lazily delete everything + additional files so they can be saved in the new format + for(var/ckey in preferences_datums) + var/datum/preferences/D = preferences_datums[ckey] + if(D == src) + var/delpath = "data/player_saves/[copytext(ckey,1,2)]/[ckey]/" + if(delpath && fexists(delpath)) + fdel(delpath) + break + return 0 + + if(savefile_version == SAVEFILE_VERSION_MAX) //update successful. + save_preferences() + save_character() + return 1 + return 0 + +/datum/preferences/proc/load_path(ckey,filename="preferences.sav") + if(!ckey) return + path = "data/player_saves/[copytext(ckey,1,2)]/[ckey]/[filename]" + savefile_version = SAVEFILE_VERSION_MAX + +/datum/preferences/proc/load_preferences() + if(!path) return 0 + if(!fexists(path)) return 0 + var/savefile/S = new /savefile(path) + if(!S) return 0 + S.cd = "/" + + S["version"] >> savefile_version + //Conversion + if(!savefile_version || !isnum(savefile_version) || savefile_version < SAVEFILE_VERSION_MIN || savefile_version > SAVEFILE_VERSION_MAX) + if(!savefile_update()) //handles updates + savefile_version = SAVEFILE_VERSION_MAX + save_preferences() + save_character() + return 0 + + player_setup.load_preferences(S) + return 1 + +/datum/preferences/proc/save_preferences() + if(!path) return 0 + var/savefile/S = new /savefile(path) + if(!S) return 0 + S.cd = "/" + + S["version"] << savefile_version + player_setup.save_preferences(S) + return 1 + +/datum/preferences/proc/load_character(slot) + if(!path) return 0 + if(!fexists(path)) return 0 + var/savefile/S = new /savefile(path) + if(!S) return 0 + S.cd = "/" + if(!slot) slot = default_slot + if(slot != SAVE_RESET) // SAVE_RESET will reset the slot as though it does not exist, but keep the current slot for saving purposes. + slot = sanitize_integer(slot, 1, config.character_slots, initial(default_slot)) + if(slot != default_slot) + default_slot = slot + S["default_slot"] << slot + else + S["default_slot"] << default_slot + + if(slot != SAVE_RESET) + S.cd = "/character[slot]" + player_setup.load_character(S) + else + player_setup.load_character(S) + S.cd = "/character[default_slot]" + player_setup.save_character(S) + + clear_character_previews() // VOREStation Edit + return 1 + +/datum/preferences/proc/save_character() + if(!path) return 0 + var/savefile/S = new /savefile(path) + if(!S) return 0 + S.cd = "/character[default_slot]" + + player_setup.save_character(S) + return 1 + +/datum/preferences/proc/overwrite_character(slot) + if(!path) return 0 + if(!fexists(path)) return 0 + var/savefile/S = new /savefile(path) + if(!S) return 0 + if(!slot) slot = default_slot + if(slot != SAVE_RESET) + slot = sanitize_integer(slot, 1, config.character_slots, initial(default_slot)) + if(slot != default_slot) + default_slot = slot + nif_path = nif_durability = nif_savedata = null //VOREStation Add - Don't copy NIF + S["default_slot"] << slot + + else + S["default_slot"] << default_slot + + return 1 + +/datum/preferences/proc/sanitize_preferences() + player_setup.sanitize_setup() + return 1 + +#undef SAVEFILE_VERSION_MAX +#undef SAVEFILE_VERSION_MIN diff --git a/code/modules/client/ui_style.dm b/code/modules/client/ui_style.dm index 8317d977e3a..eb6cc2ffdd4 100644 --- a/code/modules/client/ui_style.dm +++ b/code/modules/client/ui_style.dm @@ -1,81 +1,81 @@ - - -/var/all_ui_styles = list( - "Midnight" = 'icons/mob/screen/midnight.dmi', - "Orange" = 'icons/mob/screen/orange.dmi', - "old" = 'icons/mob/screen/old.dmi', - "White" = 'icons/mob/screen/white.dmi', - "old-noborder" = 'icons/mob/screen/old-noborder.dmi', - "minimalist" = 'icons/mob/screen/minimalist.dmi', - "Hologram" = 'icons/mob/screen/holo.dmi' - ) - -/var/all_ui_styles_robot = list( - "Midnight" = 'icons/mob/screen1_robot.dmi', - "Orange" = 'icons/mob/screen1_robot.dmi', - "old" = 'icons/mob/screen1_robot.dmi', - "White" = 'icons/mob/screen1_robot.dmi', - "old-noborder" = 'icons/mob/screen1_robot.dmi', - "minimalist" = 'icons/mob/screen1_robot_minimalist.dmi', - "Hologram" = 'icons/mob/screen1_robot_minimalist.dmi' - ) - -var/global/list/all_tooltip_styles = list( - "Midnight", //Default for everyone is the first one, - "Plasmafire", - "Retro", - "Slimecore", - "Operative", - "Clockwork" - ) - -/proc/ui_style2icon(ui_style) - if(ui_style in all_ui_styles) - return all_ui_styles[ui_style] - return all_ui_styles["White"] - - -/client/verb/change_ui() - set name = "Change UI" - set category = "Preferences" - set desc = "Configure your user interface" - - if(!ishuman(usr)) - if(!isrobot(usr)) - to_chat(usr, "You must be a human or a robot to use this verb.") - return - - var/UI_style_new = tgui_input_list(usr, "Select a style. White is recommended for customization", "UI Style Choice", all_ui_styles) - if(!UI_style_new) return - - var/UI_style_alpha_new = tgui_input_number(usr, "Select a new alpha (transparency) parameter for your UI, between 50 and 255", null, null, 255, 50) - if(!UI_style_alpha_new || !(UI_style_alpha_new <= 255 && UI_style_alpha_new >= 50)) return - - var/UI_style_color_new = input(usr, "Choose your UI color. Dark colors are not recommended!") as color|null - if(!UI_style_color_new) return - - //update UI - var/list/icons = usr.hud_used.adding + usr.hud_used.other + usr.hud_used.hotkeybuttons - icons.Add(usr.zone_sel) - icons.Add(usr.gun_setting_icon) - icons.Add(usr.item_use_icon) - icons.Add(usr.gun_move_icon) - icons.Add(usr.radio_use_icon) - - var/icon/ic = all_ui_styles[UI_style_new] - if(isrobot(usr)) - ic = all_ui_styles_robot[UI_style_new] - - for(var/obj/screen/I in icons) - if(I.name in list(I_HELP, I_HURT, I_DISARM, I_GRAB)) continue - I.icon = ic - I.color = UI_style_color_new - I.alpha = UI_style_alpha_new - - - if(tgui_alert(usr, "Like it? Save changes?","Save?",list("Yes", "No")) == "Yes") - prefs.UI_style = UI_style_new - prefs.UI_style_alpha = UI_style_alpha_new - prefs.UI_style_color = UI_style_color_new - SScharacter_setup.queue_preferences_save(prefs) - to_chat(usr, "UI was saved") + + +/var/all_ui_styles = list( + "Midnight" = 'icons/mob/screen/midnight.dmi', + "Orange" = 'icons/mob/screen/orange.dmi', + "old" = 'icons/mob/screen/old.dmi', + "White" = 'icons/mob/screen/white.dmi', + "old-noborder" = 'icons/mob/screen/old-noborder.dmi', + "minimalist" = 'icons/mob/screen/minimalist.dmi', + "Hologram" = 'icons/mob/screen/holo.dmi' + ) + +/var/all_ui_styles_robot = list( + "Midnight" = 'icons/mob/screen1_robot.dmi', + "Orange" = 'icons/mob/screen1_robot.dmi', + "old" = 'icons/mob/screen1_robot.dmi', + "White" = 'icons/mob/screen1_robot.dmi', + "old-noborder" = 'icons/mob/screen1_robot.dmi', + "minimalist" = 'icons/mob/screen1_robot_minimalist.dmi', + "Hologram" = 'icons/mob/screen1_robot_minimalist.dmi' + ) + +var/global/list/all_tooltip_styles = list( + "Midnight", //Default for everyone is the first one, + "Plasmafire", + "Retro", + "Slimecore", + "Operative", + "Clockwork" + ) + +/proc/ui_style2icon(ui_style) + if(ui_style in all_ui_styles) + return all_ui_styles[ui_style] + return all_ui_styles["White"] + + +/client/verb/change_ui() + set name = "Change UI" + set category = "Preferences" + set desc = "Configure your user interface" + + if(!ishuman(usr)) + if(!isrobot(usr)) + to_chat(usr, "You must be a human or a robot to use this verb.") + return + + var/UI_style_new = tgui_input_list(usr, "Select a style. White is recommended for customization", "UI Style Choice", all_ui_styles) + if(!UI_style_new) return + + var/UI_style_alpha_new = tgui_input_number(usr, "Select a new alpha (transparency) parameter for your UI, between 50 and 255", null, null, 255, 50) + if(!UI_style_alpha_new || !(UI_style_alpha_new <= 255 && UI_style_alpha_new >= 50)) return + + var/UI_style_color_new = input(usr, "Choose your UI color. Dark colors are not recommended!") as color|null + if(!UI_style_color_new) return + + //update UI + var/list/icons = usr.hud_used.adding + usr.hud_used.other + usr.hud_used.hotkeybuttons + icons.Add(usr.zone_sel) + icons.Add(usr.gun_setting_icon) + icons.Add(usr.item_use_icon) + icons.Add(usr.gun_move_icon) + icons.Add(usr.radio_use_icon) + + var/icon/ic = all_ui_styles[UI_style_new] + if(isrobot(usr)) + ic = all_ui_styles_robot[UI_style_new] + + for(var/obj/screen/I in icons) + if(I.name in list(I_HELP, I_HURT, I_DISARM, I_GRAB)) continue + I.icon = ic + I.color = UI_style_color_new + I.alpha = UI_style_alpha_new + + + if(tgui_alert(usr, "Like it? Save changes?","Save?",list("Yes", "No")) == "Yes") + prefs.UI_style = UI_style_new + prefs.UI_style_alpha = UI_style_alpha_new + prefs.UI_style_color = UI_style_color_new + SScharacter_setup.queue_preferences_save(prefs) + to_chat(usr, "UI was saved") diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm index fb4f491076b..e0d3177b529 100644 --- a/code/modules/clothing/glasses/hud.dm +++ b/code/modules/clothing/glasses/hud.dm @@ -1,43 +1,43 @@ -/obj/item/clothing/glasses/hud - name = "HUD" - desc = "A heads-up display that provides important info in (almost) real time." - flags = 0 //doesn't protect eyes because it's a monocle, duh - origin_tech = list(TECH_MAGNET = 3, TECH_BIO = 2) - -/obj/item/clothing/glasses/hud/health - name = "Health Scanner HUD" - desc = "A heads-up display that scans the humans in view and provides accurate data about their health status." - icon_state = "healthhud" - item_state_slots = list(slot_r_hand_str = "headset", slot_l_hand_str = "headset") - body_parts_covered = 0 - enables_planes = list(VIS_CH_STATUS,VIS_CH_HEALTH) - -/obj/item/clothing/glasses/hud/health/prescription - name = "Prescription Health Scanner HUD" - desc = "A medical HUD integrated with a set of prescription glasses" - prescription = 1 - icon_state = "healthhudpresc" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - -/obj/item/clothing/glasses/hud/security - name = "Security HUD" - desc = "A heads-up display that scans the humans in view and provides accurate data about their ID status and security records." - icon_state = "securityhud" - item_state_slots = list(slot_r_hand_str = "headset", slot_l_hand_str = "headset") - body_parts_covered = 0 - enables_planes = list(VIS_CH_ID,VIS_CH_WANTED,VIS_CH_IMPTRACK,VIS_CH_IMPLOYAL,VIS_CH_IMPCHEM) - -/obj/item/clothing/glasses/hud/security/prescription - name = "Prescription Security HUD" - desc = "A security HUD integrated with a set of prescription glasses" - prescription = 1 - icon_state = "sechudpresc" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - -/obj/item/clothing/glasses/hud/security/jensenshades - name = "Augmented shades" - desc = "Polarized bioneural eyewear, designed to augment your vision." - icon_state = "jensenshades" - item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") - vision_flags = SEE_MOBS - see_invisible = SEE_INVISIBLE_NOLIGHTING +/obj/item/clothing/glasses/hud + name = "HUD" + desc = "A heads-up display that provides important info in (almost) real time." + flags = 0 //doesn't protect eyes because it's a monocle, duh + origin_tech = list(TECH_MAGNET = 3, TECH_BIO = 2) + +/obj/item/clothing/glasses/hud/health + name = "Health Scanner HUD" + desc = "A heads-up display that scans the humans in view and provides accurate data about their health status." + icon_state = "healthhud" + item_state_slots = list(slot_r_hand_str = "headset", slot_l_hand_str = "headset") + body_parts_covered = 0 + enables_planes = list(VIS_CH_STATUS,VIS_CH_HEALTH) + +/obj/item/clothing/glasses/hud/health/prescription + name = "Prescription Health Scanner HUD" + desc = "A medical HUD integrated with a set of prescription glasses" + prescription = 1 + icon_state = "healthhudpresc" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + +/obj/item/clothing/glasses/hud/security + name = "Security HUD" + desc = "A heads-up display that scans the humans in view and provides accurate data about their ID status and security records." + icon_state = "securityhud" + item_state_slots = list(slot_r_hand_str = "headset", slot_l_hand_str = "headset") + body_parts_covered = 0 + enables_planes = list(VIS_CH_ID,VIS_CH_WANTED,VIS_CH_IMPTRACK,VIS_CH_IMPLOYAL,VIS_CH_IMPCHEM) + +/obj/item/clothing/glasses/hud/security/prescription + name = "Prescription Security HUD" + desc = "A security HUD integrated with a set of prescription glasses" + prescription = 1 + icon_state = "sechudpresc" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + +/obj/item/clothing/glasses/hud/security/jensenshades + name = "Augmented shades" + desc = "Polarized bioneural eyewear, designed to augment your vision." + icon_state = "jensenshades" + item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") + vision_flags = SEE_MOBS + see_invisible = SEE_INVISIBLE_NOLIGHTING diff --git a/code/modules/clothing/gloves/boxing.dm b/code/modules/clothing/gloves/boxing.dm index 3ed26e41261..b818be2dba1 100644 --- a/code/modules/clothing/gloves/boxing.dm +++ b/code/modules/clothing/gloves/boxing.dm @@ -1,31 +1,31 @@ -/obj/item/clothing/gloves/boxing - name = "boxing gloves" - desc = "Because you really needed another excuse to punch your crewmates." - icon_state = "boxing" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - -/* -/obj/item/clothing/gloves/boxing/attackby(obj/item/weapon/W, mob/user) - if(W.has_tool_quality(TOOL_WIRECUTTER) || istype(W, /obj/item/weapon/surgical/scalpel)) - to_chat(user, "That won't work.") //Nope - return - ..() -*/ - -/obj/item/clothing/gloves/boxing/green - icon_state = "boxinggreen" - item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") - -/obj/item/clothing/gloves/boxing/blue - icon_state = "boxingblue" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - -/obj/item/clothing/gloves/boxing/yellow - icon_state = "boxingyellow" - item_state_slots = list(slot_r_hand_str = "yellow", slot_l_hand_str = "yellow") - -/obj/item/clothing/gloves/white - name = "white gloves" - desc = "These look pretty fancy." - icon_state = "latex" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") +/obj/item/clothing/gloves/boxing + name = "boxing gloves" + desc = "Because you really needed another excuse to punch your crewmates." + icon_state = "boxing" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + +/* +/obj/item/clothing/gloves/boxing/attackby(obj/item/weapon/W, mob/user) + if(W.has_tool_quality(TOOL_WIRECUTTER) || istype(W, /obj/item/weapon/surgical/scalpel)) + to_chat(user, "That won't work.") //Nope + return + ..() +*/ + +/obj/item/clothing/gloves/boxing/green + icon_state = "boxinggreen" + item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") + +/obj/item/clothing/gloves/boxing/blue + icon_state = "boxingblue" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + +/obj/item/clothing/gloves/boxing/yellow + icon_state = "boxingyellow" + item_state_slots = list(slot_r_hand_str = "yellow", slot_l_hand_str = "yellow") + +/obj/item/clothing/gloves/white + name = "white gloves" + desc = "These look pretty fancy." + icon_state = "latex" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") diff --git a/code/modules/clothing/gloves/color.dm b/code/modules/clothing/gloves/color.dm index efcd364922e..19ff4c8b10f 100644 --- a/code/modules/clothing/gloves/color.dm +++ b/code/modules/clothing/gloves/color.dm @@ -1,99 +1,99 @@ - - -/obj/item/clothing/gloves/yellow - desc = "These gloves will protect the wearer from electric shock." - name = "insulated gloves" - icon_state = "yellow" - siemens_coefficient = 0 - permeability_coefficient = 0.05 - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' - -/obj/item/clothing/gloves/fyellow //Cheap Chinese Crap - desc = "These gloves are cheap copies of proper insulated gloves. No way this can end badly." - name = "budget insulated gloves" - icon_state = "yellow" - siemens_coefficient = 1 //Set to a default of 1, gets overridden in initialize() - permeability_coefficient = 0.05 - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' - -/obj/item/clothing/gloves/fyellow/Initialize() - . = ..() - //Picks a value between 0 and 1.25, in 5% increments // VOREStation edit - var/shock_pick = rand(0,15) // VOREStation Edit - siemens_coefficient = shock_pick * 0.05 - -/obj/item/clothing/gloves/black - desc = "These work gloves are thick and fire-resistant." - name = "black gloves" - icon_state = "black" - permeability_coefficient = 0.05 - - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = HANDS - max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE - -/obj/item/clothing/gloves/orange - name = "orange gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "orange" - -/obj/item/clothing/gloves/red - name = "red gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "red" - -/obj/item/clothing/gloves/rainbow - name = "rainbow gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "rainbow" - -/obj/item/clothing/gloves/blue - name = "blue gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "blue" - -/obj/item/clothing/gloves/purple - name = "purple gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "purple" - -/obj/item/clothing/gloves/green - name = "green gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "green" - -/obj/item/clothing/gloves/grey - name = "grey gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "gray" - -/obj/item/clothing/gloves/light_brown - name = "light brown gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "lightbrown" - -/obj/item/clothing/gloves/brown - name = "brown gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "brown" - -/obj/item/clothing/gloves/evening - desc = "A pair of gloves that reach past the elbow. Fancy!" - name = "evening gloves" - icon_state = "evening_gloves" - addblends = "evening_gloves_a" - -/obj/item/clothing/gloves/fingerless - desc = "A pair of gloves that don't actually cover the fingers." - name = "fingerless gloves" - icon_state = "fingerlessgloves" - fingerprint_chance = 100 - -/obj/item/clothing/gloves/fingerless_recolourable - desc = "A pair of gloves that don't actually cover the fingers." - name = "fingerless gloves" - icon_state = "fingerlessgloves_rc" + + +/obj/item/clothing/gloves/yellow + desc = "These gloves will protect the wearer from electric shock." + name = "insulated gloves" + icon_state = "yellow" + siemens_coefficient = 0 + permeability_coefficient = 0.05 + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' + +/obj/item/clothing/gloves/fyellow //Cheap Chinese Crap + desc = "These gloves are cheap copies of proper insulated gloves. No way this can end badly." + name = "budget insulated gloves" + icon_state = "yellow" + siemens_coefficient = 1 //Set to a default of 1, gets overridden in initialize() + permeability_coefficient = 0.05 + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' + +/obj/item/clothing/gloves/fyellow/Initialize() + . = ..() + //Picks a value between 0 and 1.25, in 5% increments // VOREStation edit + var/shock_pick = rand(0,15) // VOREStation Edit + siemens_coefficient = shock_pick * 0.05 + +/obj/item/clothing/gloves/black + desc = "These work gloves are thick and fire-resistant." + name = "black gloves" + icon_state = "black" + permeability_coefficient = 0.05 + + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE + +/obj/item/clothing/gloves/orange + name = "orange gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "orange" + +/obj/item/clothing/gloves/red + name = "red gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "red" + +/obj/item/clothing/gloves/rainbow + name = "rainbow gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "rainbow" + +/obj/item/clothing/gloves/blue + name = "blue gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "blue" + +/obj/item/clothing/gloves/purple + name = "purple gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "purple" + +/obj/item/clothing/gloves/green + name = "green gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "green" + +/obj/item/clothing/gloves/grey + name = "grey gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "gray" + +/obj/item/clothing/gloves/light_brown + name = "light brown gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "lightbrown" + +/obj/item/clothing/gloves/brown + name = "brown gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "brown" + +/obj/item/clothing/gloves/evening + desc = "A pair of gloves that reach past the elbow. Fancy!" + name = "evening gloves" + icon_state = "evening_gloves" + addblends = "evening_gloves_a" + +/obj/item/clothing/gloves/fingerless + desc = "A pair of gloves that don't actually cover the fingers." + name = "fingerless gloves" + icon_state = "fingerlessgloves" + fingerprint_chance = 100 + +/obj/item/clothing/gloves/fingerless_recolourable + desc = "A pair of gloves that don't actually cover the fingers." + name = "fingerless gloves" + icon_state = "fingerlessgloves_rc" fingerprint_chance = 100 \ No newline at end of file diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm index 709c6ea1560..eba124cc621 100644 --- a/code/modules/clothing/gloves/miscellaneous.dm +++ b/code/modules/clothing/gloves/miscellaneous.dm @@ -1,174 +1,174 @@ -/obj/item/clothing/gloves/captain - desc = "Regal blue gloves, with a nice gold trim. Swanky." - name = "site manager's gloves" - icon_state = "captain" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - -/obj/item/clothing/gloves/cyborg - desc = "beep boop borp" - name = "cyborg gloves" - icon_state = "black" - item_state = "r_hands" - siemens_coefficient = 1.0 - -/obj/item/clothing/gloves/forensic - desc = "Specially made gloves for forensic technicians. The luminescent threads woven into the material stand out under scrutiny." - name = "forensic gloves" - icon_state = "forensic" - item_state = "black" - permeability_coefficient = 0.05 - - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = HANDS - max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE - -/obj/item/clothing/gloves/swat - desc = "These tactical gloves are somewhat fire and impact-resistant." - name = "\improper SWAT Gloves" - icon_state = "swat" - item_state = "swat" - siemens_coefficient = 0.50 - permeability_coefficient = 0.05 - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = HANDS - max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE - -/obj/item/clothing/gloves/combat //Combined effect of SWAT gloves and insulated gloves - desc = "These tactical gloves are somewhat fire and impact resistant." - name = "combat gloves" - icon_state = "swat" - item_state = "swat" - siemens_coefficient = 0 - permeability_coefficient = 0.05 - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = HANDS - max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE - -/obj/item/clothing/gloves/sterile - name = "sterile gloves" - desc = "Sterile gloves." - icon_state = "latex" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - siemens_coefficient = 1.0 //thin latex gloves, much more conductive than fabric gloves (basically a capacitor for AC) - permeability_coefficient = 0.01 - germ_level = 0 - fingerprint_chance = 25 - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' -// var/balloonPath = /obj/item/latexballon - -//TODO: Make inflating gloves a thing -/*/obj/item/clothing/gloves/sterile/proc/Inflate(/mob/living/carbon/human/user) - user.visible_message("\The [src] expands!") - qdel(src)*/ - -/obj/item/clothing/gloves/sterile/latex - name = "latex gloves" - desc = "Sterile latex gloves." - -/obj/item/clothing/gloves/sterile/nitrile - name = "nitrile gloves" - desc = "Sterile nitrile gloves" - icon_state = "nitrile" - item_state = "ngloves" -// balloonPath = /obj/item/nitrileballoon - -/obj/item/clothing/gloves/botanic_leather - desc = "These leather work gloves protect against thorns, barbs, prickles, spikes and other harmful objects of floral origin." - name = "botanist's leather gloves" - icon_state = "leather" - item_state_slots = list(slot_r_hand_str = "lightbrown", slot_l_hand_str = "lightbrown") - permeability_coefficient = 0.05 - siemens_coefficient = 0.75 //thick work gloves - drop_sound = 'sound/items/drop/leather.ogg' - pickup_sound = 'sound/items/pickup/leather.ogg' - -/obj/item/clothing/gloves/duty - desc = "These brown duty gloves are made from a durable synthetic." - name = "work gloves" - icon_state = "work" - item_state = "wgloves" - armor = list(melee = 10, bullet = 10, laser = 10, energy = 5, bomb = 0, bio = 0, rad = 0) - -/obj/item/clothing/gloves/tactical - desc = "These brown tactical gloves are made from a durable synthetic, and have hardened knuckles." - name = "tactical gloves" - icon_state = "work" - item_state = "wgloves" - force = 5 - punch_force = 3 - siemens_coefficient = 0.75 - permeability_coefficient = 0.05 - armor = list(melee = 30, bullet = 10, laser = 10, energy = 15, bomb = 20, bio = 0, rad = 0) - -/obj/item/clothing/gloves/vox - desc = "These bizarre gauntlets seem to be fitted for... bird claws?" - name = "insulated gauntlets" - icon_state = "gloves-vox" - item_state = "gloves-vox" - flags = PHORONGUARD - siemens_coefficient = 0 - permeability_coefficient = 0.05 - species_restricted = list("Vox") - drop_sound = 'sound/items/drop/metalboots.ogg' - pickup_sound = 'sound/items/pickup/toolbox.ogg' - - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = HANDS - max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE - -/obj/item/clothing/gloves/knuckledusters - name = "knuckle dusters" - desc = "A pair of brass knuckles. Generally used to enhance the user's punches." - icon_state = "knuckledusters" - matter = list(MAT_STEEL = 500) - attack_verb = list("punched", "beaten", "struck") - flags = THICKMATERIAL // Stops rings from increasing hit strength - siemens_coefficient = 1 - fingerprint_chance = 100 - overgloves = 1 - force = 5 - punch_force = 5 - drop_sound = 'sound/items/drop/metalboots.ogg' - pickup_sound = 'sound/items/pickup/toolbox.ogg' - -/obj/item/clothing/gloves/ranger - var/glovecolor = "white" - name = "ranger gloves" - desc = "The gloves of the Rangers are the least memorable part. They're not even insulated in the show, so children \ - don't try and take apart a toaster with inadequate protection. They only serve to complete the fancy outfit." - icon = 'icons/obj/clothing/ranger.dmi' - icon_state = "ranger_gloves" - -/obj/item/clothing/gloves/ranger/Initialize() - . = ..() - if(icon_state == "ranger_gloves") - name = "[glovecolor] ranger gloves" - icon_state = "[glovecolor]_ranger_gloves" - -/obj/item/clothing/gloves/ranger/black - glovecolor = "black" - -/obj/item/clothing/gloves/ranger/pink - glovecolor = "pink" - -/obj/item/clothing/gloves/ranger/green - glovecolor = "green" - -/obj/item/clothing/gloves/ranger/cyan - glovecolor = "cyan" - -/obj/item/clothing/gloves/ranger/orange - glovecolor = "orange" - -/obj/item/clothing/gloves/ranger/yellow - glovecolor = "yellow" - -/obj/item/clothing/gloves/waterwings - name = "water wings" - desc = "Swim aids designed to help a wearer float in water and learn to swim." - icon_state = "waterwings" +/obj/item/clothing/gloves/captain + desc = "Regal blue gloves, with a nice gold trim. Swanky." + name = "site manager's gloves" + icon_state = "captain" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + +/obj/item/clothing/gloves/cyborg + desc = "beep boop borp" + name = "cyborg gloves" + icon_state = "black" + item_state = "r_hands" + siemens_coefficient = 1.0 + +/obj/item/clothing/gloves/forensic + desc = "Specially made gloves for forensic technicians. The luminescent threads woven into the material stand out under scrutiny." + name = "forensic gloves" + icon_state = "forensic" + item_state = "black" + permeability_coefficient = 0.05 + + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE + +/obj/item/clothing/gloves/swat + desc = "These tactical gloves are somewhat fire and impact-resistant." + name = "\improper SWAT Gloves" + icon_state = "swat" + item_state = "swat" + siemens_coefficient = 0.50 + permeability_coefficient = 0.05 + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE + +/obj/item/clothing/gloves/combat //Combined effect of SWAT gloves and insulated gloves + desc = "These tactical gloves are somewhat fire and impact resistant." + name = "combat gloves" + icon_state = "swat" + item_state = "swat" + siemens_coefficient = 0 + permeability_coefficient = 0.05 + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE + +/obj/item/clothing/gloves/sterile + name = "sterile gloves" + desc = "Sterile gloves." + icon_state = "latex" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + siemens_coefficient = 1.0 //thin latex gloves, much more conductive than fabric gloves (basically a capacitor for AC) + permeability_coefficient = 0.01 + germ_level = 0 + fingerprint_chance = 25 + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' +// var/balloonPath = /obj/item/latexballon + +//TODO: Make inflating gloves a thing +/*/obj/item/clothing/gloves/sterile/proc/Inflate(/mob/living/carbon/human/user) + user.visible_message("\The [src] expands!") + qdel(src)*/ + +/obj/item/clothing/gloves/sterile/latex + name = "latex gloves" + desc = "Sterile latex gloves." + +/obj/item/clothing/gloves/sterile/nitrile + name = "nitrile gloves" + desc = "Sterile nitrile gloves" + icon_state = "nitrile" + item_state = "ngloves" +// balloonPath = /obj/item/nitrileballoon + +/obj/item/clothing/gloves/botanic_leather + desc = "These leather work gloves protect against thorns, barbs, prickles, spikes and other harmful objects of floral origin." + name = "botanist's leather gloves" + icon_state = "leather" + item_state_slots = list(slot_r_hand_str = "lightbrown", slot_l_hand_str = "lightbrown") + permeability_coefficient = 0.05 + siemens_coefficient = 0.75 //thick work gloves + drop_sound = 'sound/items/drop/leather.ogg' + pickup_sound = 'sound/items/pickup/leather.ogg' + +/obj/item/clothing/gloves/duty + desc = "These brown duty gloves are made from a durable synthetic." + name = "work gloves" + icon_state = "work" + item_state = "wgloves" + armor = list(melee = 10, bullet = 10, laser = 10, energy = 5, bomb = 0, bio = 0, rad = 0) + +/obj/item/clothing/gloves/tactical + desc = "These brown tactical gloves are made from a durable synthetic, and have hardened knuckles." + name = "tactical gloves" + icon_state = "work" + item_state = "wgloves" + force = 5 + punch_force = 3 + siemens_coefficient = 0.75 + permeability_coefficient = 0.05 + armor = list(melee = 30, bullet = 10, laser = 10, energy = 15, bomb = 20, bio = 0, rad = 0) + +/obj/item/clothing/gloves/vox + desc = "These bizarre gauntlets seem to be fitted for... bird claws?" + name = "insulated gauntlets" + icon_state = "gloves-vox" + item_state = "gloves-vox" + flags = PHORONGUARD + siemens_coefficient = 0 + permeability_coefficient = 0.05 + species_restricted = list("Vox") + drop_sound = 'sound/items/drop/metalboots.ogg' + pickup_sound = 'sound/items/pickup/toolbox.ogg' + + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE + +/obj/item/clothing/gloves/knuckledusters + name = "knuckle dusters" + desc = "A pair of brass knuckles. Generally used to enhance the user's punches." + icon_state = "knuckledusters" + matter = list(MAT_STEEL = 500) + attack_verb = list("punched", "beaten", "struck") + flags = THICKMATERIAL // Stops rings from increasing hit strength + siemens_coefficient = 1 + fingerprint_chance = 100 + overgloves = 1 + force = 5 + punch_force = 5 + drop_sound = 'sound/items/drop/metalboots.ogg' + pickup_sound = 'sound/items/pickup/toolbox.ogg' + +/obj/item/clothing/gloves/ranger + var/glovecolor = "white" + name = "ranger gloves" + desc = "The gloves of the Rangers are the least memorable part. They're not even insulated in the show, so children \ + don't try and take apart a toaster with inadequate protection. They only serve to complete the fancy outfit." + icon = 'icons/obj/clothing/ranger.dmi' + icon_state = "ranger_gloves" + +/obj/item/clothing/gloves/ranger/Initialize() + . = ..() + if(icon_state == "ranger_gloves") + name = "[glovecolor] ranger gloves" + icon_state = "[glovecolor]_ranger_gloves" + +/obj/item/clothing/gloves/ranger/black + glovecolor = "black" + +/obj/item/clothing/gloves/ranger/pink + glovecolor = "pink" + +/obj/item/clothing/gloves/ranger/green + glovecolor = "green" + +/obj/item/clothing/gloves/ranger/cyan + glovecolor = "cyan" + +/obj/item/clothing/gloves/ranger/orange + glovecolor = "orange" + +/obj/item/clothing/gloves/ranger/yellow + glovecolor = "yellow" + +/obj/item/clothing/gloves/waterwings + name = "water wings" + desc = "Swim aids designed to help a wearer float in water and learn to swim." + icon_state = "waterwings" diff --git a/code/modules/clothing/head/collectable.dm b/code/modules/clothing/head/collectable.dm index 5d33d56360a..eea71f60417 100644 --- a/code/modules/clothing/head/collectable.dm +++ b/code/modules/clothing/head/collectable.dm @@ -1,128 +1,128 @@ - -//Hat Station 13 - -/obj/item/clothing/head/collectable - name = "collectable hat" - desc = "A rare collectable hat." - -/obj/item/clothing/head/collectable/petehat - name = "ultra rare hat" - desc = "an ultra rare hat. It commands a certain respect." - icon_state = "petehat" - sprite_sheets = list( - SPECIES_TESHARI = 'icons/inventory/head/mob_teshari.dmi', - SPECIES_VOX = 'icons/inventory/head/mob_vox.dmi' - ) - -/obj/item/clothing/head/collectable/slime - name = "collectable slime cap!" - desc = "It just latches right in place!" - icon_state = "headslime" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/xenom - name = "collectable xenomorph helmet!" - desc = "Hiss hiss hiss!" - icon_state = "xenom" - item_state_slots = list(slot_r_hand_str = "xenos_helm", slot_l_hand_str = "xenos_helm") - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/collectable/chef - name = "collectable chef's hat" - desc = "A rare Chef's Hat meant for hat collectors!" - icon_state = "chefhat" - -/obj/item/clothing/head/collectable/paper - name = "collectable paper hat" - desc = "What looks like an ordinary paper hat, is actually a rare and valuable collector's edition paper hat. Keep away from water, fire and Librarians." - icon_state = "paper" - body_parts_covered = 0 - drop_sound = 'sound/items/drop/paper.ogg' - pickup_sound = 'sound/items/pickup/paper.ogg' - -/obj/item/clothing/head/collectable/tophat - name = "collectable top hat" - desc = "A top hat worn by only the most prestigious hat collectors." - icon_state = "tophat" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/captain - name = "collectable site manager's hat" - desc = "A Collectable Hat that'll make you look just like a real comdom!" - icon_state = "captain" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/police - name = "collectable police officer's hat" - desc = "A Collectable Police Officer's Hat. This hat emphasizes that you are THE LAW." - icon_state = "policehelm" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/beret - name = "collectable beret" - desc = "A Collectable red Beret. It smells faintly of Garlic." - icon_state = "beret" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/welding - name = "collectable welding helmet" - desc = "A Collectable Welding Helmet. Now with 80% less lead! Not for actual welding. Any welding done while wearing this Helmet is done so at the owner's own risk!" - icon_state = "welding" - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/collectable/slime - name = "collectable slime hat" - desc = "Just like a real Brain Slug!" - icon_state = "headslime" - item_state_slots = list(slot_r_hand_str = "greenbandana", slot_l_hand_str = "greenbandana") - -/obj/item/clothing/head/collectable/flatcap - name = "collectable flat cap" - desc = "A Collectible farmer's Flat Cap!" - icon_state = "flat_cap" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - -/obj/item/clothing/head/collectable/pirate - name = "collectable pirate hat" - desc = "You'd make a great Dread Syndie Roberts!" - icon_state = "pirate" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/kitty - name = "collectable kitty ears" - desc = "The fur feels.....a bit too realistic." - icon_state = "kitty" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/rabbitears - name = "collectable rabbit ears" - desc = "Not as lucky as the feet!" - icon_state = "bunny" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/wizard - name = "collectable wizard's hat" - desc = "NOTE:Any magical powers gained from wearing this hat are purely coincidental." - icon_state = "wizard" - -/obj/item/clothing/head/collectable/hardhat - name = "collectable hard hat" - desc = "WARNING! Offers no real protection, or luminosity, but it is damn fancy!" - icon_state = "hardhat0_old_yellow" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/HoS - name = "collectable HoS hat" - desc = "Now you can beat prisoners, set silly sentences and arrest for no reason too!" - icon_state = "hoscap" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/thunderdome - name = "collectable Thunderdome helmet" - desc = "Go Red! I mean Green! I mean Red! No Green!" - icon_state = "thunderdome" - -/obj/item/clothing/head/collectable/swat - name = "collectable SWAT helmet" - desc = "Now you can be in the Deathsquad too!" - icon_state = "swat" + +//Hat Station 13 + +/obj/item/clothing/head/collectable + name = "collectable hat" + desc = "A rare collectable hat." + +/obj/item/clothing/head/collectable/petehat + name = "ultra rare hat" + desc = "an ultra rare hat. It commands a certain respect." + icon_state = "petehat" + sprite_sheets = list( + SPECIES_TESHARI = 'icons/inventory/head/mob_teshari.dmi', + SPECIES_VOX = 'icons/inventory/head/mob_vox.dmi' + ) + +/obj/item/clothing/head/collectable/slime + name = "collectable slime cap!" + desc = "It just latches right in place!" + icon_state = "headslime" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/xenom + name = "collectable xenomorph helmet!" + desc = "Hiss hiss hiss!" + icon_state = "xenom" + item_state_slots = list(slot_r_hand_str = "xenos_helm", slot_l_hand_str = "xenos_helm") + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/collectable/chef + name = "collectable chef's hat" + desc = "A rare Chef's Hat meant for hat collectors!" + icon_state = "chefhat" + +/obj/item/clothing/head/collectable/paper + name = "collectable paper hat" + desc = "What looks like an ordinary paper hat, is actually a rare and valuable collector's edition paper hat. Keep away from water, fire and Librarians." + icon_state = "paper" + body_parts_covered = 0 + drop_sound = 'sound/items/drop/paper.ogg' + pickup_sound = 'sound/items/pickup/paper.ogg' + +/obj/item/clothing/head/collectable/tophat + name = "collectable top hat" + desc = "A top hat worn by only the most prestigious hat collectors." + icon_state = "tophat" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/captain + name = "collectable site manager's hat" + desc = "A Collectable Hat that'll make you look just like a real comdom!" + icon_state = "captain" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/police + name = "collectable police officer's hat" + desc = "A Collectable Police Officer's Hat. This hat emphasizes that you are THE LAW." + icon_state = "policehelm" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/beret + name = "collectable beret" + desc = "A Collectable red Beret. It smells faintly of Garlic." + icon_state = "beret" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/welding + name = "collectable welding helmet" + desc = "A Collectable Welding Helmet. Now with 80% less lead! Not for actual welding. Any welding done while wearing this Helmet is done so at the owner's own risk!" + icon_state = "welding" + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/collectable/slime + name = "collectable slime hat" + desc = "Just like a real Brain Slug!" + icon_state = "headslime" + item_state_slots = list(slot_r_hand_str = "greenbandana", slot_l_hand_str = "greenbandana") + +/obj/item/clothing/head/collectable/flatcap + name = "collectable flat cap" + desc = "A Collectible farmer's Flat Cap!" + icon_state = "flat_cap" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + +/obj/item/clothing/head/collectable/pirate + name = "collectable pirate hat" + desc = "You'd make a great Dread Syndie Roberts!" + icon_state = "pirate" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/kitty + name = "collectable kitty ears" + desc = "The fur feels.....a bit too realistic." + icon_state = "kitty" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/rabbitears + name = "collectable rabbit ears" + desc = "Not as lucky as the feet!" + icon_state = "bunny" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/wizard + name = "collectable wizard's hat" + desc = "NOTE:Any magical powers gained from wearing this hat are purely coincidental." + icon_state = "wizard" + +/obj/item/clothing/head/collectable/hardhat + name = "collectable hard hat" + desc = "WARNING! Offers no real protection, or luminosity, but it is damn fancy!" + icon_state = "hardhat0_old_yellow" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/HoS + name = "collectable HoS hat" + desc = "Now you can beat prisoners, set silly sentences and arrest for no reason too!" + icon_state = "hoscap" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/thunderdome + name = "collectable Thunderdome helmet" + desc = "Go Red! I mean Green! I mean Red! No Green!" + icon_state = "thunderdome" + +/obj/item/clothing/head/collectable/swat + name = "collectable SWAT helmet" + desc = "Now you can be in the Deathsquad too!" + icon_state = "swat" diff --git a/code/modules/clothing/head/fishing.dm b/code/modules/clothing/head/fishing.dm index bcebd89b57c..8c702be68aa 100644 --- a/code/modules/clothing/head/fishing.dm +++ b/code/modules/clothing/head/fishing.dm @@ -1,152 +1,152 @@ -//these just use the default inhands for soft caps because the amount of extra work to add more colours would be insane otherwise - -/obj/item/clothing/head/fishing - name = "fishing hat" - desc = "It's a peaked cap with a quirky slogan." - icon = 'icons/inventory/head/item_vr_fishing.dmi' - icon_state = "greensoft0" - item_state_slots = list(slot_r_hand_str = "greensoft", slot_l_hand_str = "greensoft", slot_head_str = "greensoft0") - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_hats.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_hats.dmi', - slot_head_str = 'icons/inventory/head/mob_vr_fishing.dmi' - ) - siemens_coefficient = 0.9 - body_parts_covered = 0 - var/slogan = "" - var/hatsize = 0 - -/obj/item/clothing/head/fishing/Initialize() - //short phrases that women and fish may have about you - var/feelings = list("love me", - "fear me", - "despise me", - "are ambivalent towards me", - "don't care about me", - "lust after me", - "eat me", - "are down bad for me", - "can't stand me", - "want me", - "kill me", - "devour me", - "swallow me", - "avoid me", - "have no words for me", - "hate me", - "make memes of me", - "demoralise me", - "perpetually mock me", - "scare me", - "issue restraining orders to me", - "leave me", - "took the kids from me", - "demand satisfaction from me", - "dropkick me", - "suplex me", - "worry about me", - "gossip about me", - "defame me", - "disbelieve my existence", - "conspire against me", - "draw me as the soyjack", - "put me in their tummies", - "crunchatize me", - "crunchatize me, cap'n!", - "wreck me", - "feed me", - "beg to get in my stomach", - "walk all over me", - "drink me like a milkshake", - "beg me to step on them", - "call me Mommy", - "call me Daddy", - "step on me", - "lay eggs down my throat", - "dream about me", - "choke me with their thighs", - "steal my lunch money", - "contact me about my spaceship's extended warranty", - "envy me", - "combo me", - "want to kidnap me", - "fold me in half", - "think my spacetruck is hot", - "hate my spacetruck", - "are more into my spacetruck than me", - "ask me for docking codes", - "think of me while kneading dough", - "gun me down in the streets of miami", - "drop me off of inconveniently high places", - "drop me into inconveniently tight places", - "leave threatening messages on my voicemail", - "write fanfiction about me", - "attempt to approach me" - ) - //significantly more complex feelings that women and fish may have about you - var/verylongfeelings = list("construct inferior defensive walls lacking additional fall back locations as they believe their initial defense shall be enough to withstand me", - "insist upon forming an unsteady yet reliable alliance in which they teeter upon the dual edge of betrayal and ruination in the perpetual desire to bring ruin upon me", - "construct elaborate fantasies about my graphic and harrowing death at the hands of a giant robot", - "call it oven when you of in the cold food of out hot eat the food", - "are hurled millennia into the future for their hubris but nonetheless attempt to enslave me from my abode deep within the earth", - "make light of my attire while at the coffee shop, unaware that I am a powerful wizard with the means to cast forth the veil of ignorance from their eyes", - "cower before my T-posing to assert dominance, except I don't know the difference between T-posing and A-posing (neither of which have anything to do with T&A, which is very confusing)", - "use my power to create a new universe but destroy all life on earth in the process and are left alone to rebuild with nothing for company but me", - "hold A to charge a buster shot only to get hit by my homing missiles, starting a juggle chain which results in losing all their health and having to restart from the last checkpoint", - "attempt once more to best me and gain access to my keep of riches and treasure, but I put their foolish ambitions to rest with force", - "bump into me in the supermarket and exchange an awkward hello, neither of us expecting to meet each other in this liminal environs and unsure how the context of our relationship can adapt to this happenstance congregation", - "pursue me to the ends of the earth, constantly thwarted by inconsequential setbacks, and when they catch up and I ask in an exasperated voice why they respond 'you owe me a dollar'", - "call me just to 'chat' but actually mention they are moving and, obviously, since I'm the ONLY guy they know with a truck it would be great if I could help out, they said they'd buy me pizza but that doesn't really cover it", - "shuffle uncomfortably as we continue to sit in near-total silence having exhausted all topics of conversation hours ago but neither of us having worked up the spirit to suggest that we part ways and go home", - "shove me into their large gaping maw, covering me in drool as I descend into their warm depths, becoming covered by hot bubbling acids that dissolve my body while I play Animal Crossing New Leaf on my Nintendo 3DS", - "promise me McDonalds but conveniently forget to pull into the drivethrough on the way home, leaving me burgerless", - "leave me in the car all alone with my favorite music and a bottle of water in the summer", - "create designs of particularly verbose hats describing extreme complexities and deep philosophical implications of their unusually specific feelings and often not particularly kind thoughts about me", - "methodically place brick after agonising brick in the wall they are constructing to forever entomb me in the basement which they claimed contained a case of Amontillado wine" - ) - - //time to actually generate the slogan - //50% chance of it just being a basic women/fish combo - if(prob(50)) - slogan = "Women [pick(feelings)], fish [pick(feelings)]" - else //we generate something more complex - if(prob(90)) //USUALLY one of the short simple phrases - slogan = "[pick("Women", "Fish")] [pick(feelings)]" - else - slogan = "[pick("Women", "Fish")] [pick(verylongfeelings)]" - hatsize += 2 - - //second line - if(prob(90)) //USUALLY one of the short simple phrases - slogan = "[slogan], [pick("Women", "Fish")] [pick(feelings)]" - else - slogan = "[slogan], [pick("Women", "Fish")] [pick(verylongfeelings)]" - hatsize += 2 - //chance of a third line - if(prob(50)) - if(prob(50)) //if a third line is rolled it's way more likely to be a long one - slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(feelings)]" - hatsize += 1 - else - slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(verylongfeelings)]" - hatsize += 3 - //you can even get a fourth - if(prob(25)) - if(prob(25)) //if a fourth line is rolled it's way WAY more likely to be a long one - slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(feelings)]" - hatsize += 1 - else - slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(verylongfeelings)]" - hatsize += 3 - - - //now we have the slogan, apply this to the description and name - desc = "A peaked cap with text reading '[slogan]'." - name = "\improper '[slogan]' hat" - - //pick a hue - var/colourtype = pick("green", "red", "blue", "yellow", "purple", "orange", "grey") - - //finally, take our hat size and pick the icon accordingly - icon_state = "[colourtype]soft[hatsize]" +//these just use the default inhands for soft caps because the amount of extra work to add more colours would be insane otherwise + +/obj/item/clothing/head/fishing + name = "fishing hat" + desc = "It's a peaked cap with a quirky slogan." + icon = 'icons/inventory/head/item_vr_fishing.dmi' + icon_state = "greensoft0" + item_state_slots = list(slot_r_hand_str = "greensoft", slot_l_hand_str = "greensoft", slot_head_str = "greensoft0") + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_hats.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_hats.dmi', + slot_head_str = 'icons/inventory/head/mob_vr_fishing.dmi' + ) + siemens_coefficient = 0.9 + body_parts_covered = 0 + var/slogan = "" + var/hatsize = 0 + +/obj/item/clothing/head/fishing/Initialize() + //short phrases that women and fish may have about you + var/feelings = list("love me", + "fear me", + "despise me", + "are ambivalent towards me", + "don't care about me", + "lust after me", + "eat me", + "are down bad for me", + "can't stand me", + "want me", + "kill me", + "devour me", + "swallow me", + "avoid me", + "have no words for me", + "hate me", + "make memes of me", + "demoralise me", + "perpetually mock me", + "scare me", + "issue restraining orders to me", + "leave me", + "took the kids from me", + "demand satisfaction from me", + "dropkick me", + "suplex me", + "worry about me", + "gossip about me", + "defame me", + "disbelieve my existence", + "conspire against me", + "draw me as the soyjack", + "put me in their tummies", + "crunchatize me", + "crunchatize me, cap'n!", + "wreck me", + "feed me", + "beg to get in my stomach", + "walk all over me", + "drink me like a milkshake", + "beg me to step on them", + "call me Mommy", + "call me Daddy", + "step on me", + "lay eggs down my throat", + "dream about me", + "choke me with their thighs", + "steal my lunch money", + "contact me about my spaceship's extended warranty", + "envy me", + "combo me", + "want to kidnap me", + "fold me in half", + "think my spacetruck is hot", + "hate my spacetruck", + "are more into my spacetruck than me", + "ask me for docking codes", + "think of me while kneading dough", + "gun me down in the streets of miami", + "drop me off of inconveniently high places", + "drop me into inconveniently tight places", + "leave threatening messages on my voicemail", + "write fanfiction about me", + "attempt to approach me" + ) + //significantly more complex feelings that women and fish may have about you + var/verylongfeelings = list("construct inferior defensive walls lacking additional fall back locations as they believe their initial defense shall be enough to withstand me", + "insist upon forming an unsteady yet reliable alliance in which they teeter upon the dual edge of betrayal and ruination in the perpetual desire to bring ruin upon me", + "construct elaborate fantasies about my graphic and harrowing death at the hands of a giant robot", + "call it oven when you of in the cold food of out hot eat the food", + "are hurled millennia into the future for their hubris but nonetheless attempt to enslave me from my abode deep within the earth", + "make light of my attire while at the coffee shop, unaware that I am a powerful wizard with the means to cast forth the veil of ignorance from their eyes", + "cower before my T-posing to assert dominance, except I don't know the difference between T-posing and A-posing (neither of which have anything to do with T&A, which is very confusing)", + "use my power to create a new universe but destroy all life on earth in the process and are left alone to rebuild with nothing for company but me", + "hold A to charge a buster shot only to get hit by my homing missiles, starting a juggle chain which results in losing all their health and having to restart from the last checkpoint", + "attempt once more to best me and gain access to my keep of riches and treasure, but I put their foolish ambitions to rest with force", + "bump into me in the supermarket and exchange an awkward hello, neither of us expecting to meet each other in this liminal environs and unsure how the context of our relationship can adapt to this happenstance congregation", + "pursue me to the ends of the earth, constantly thwarted by inconsequential setbacks, and when they catch up and I ask in an exasperated voice why they respond 'you owe me a dollar'", + "call me just to 'chat' but actually mention they are moving and, obviously, since I'm the ONLY guy they know with a truck it would be great if I could help out, they said they'd buy me pizza but that doesn't really cover it", + "shuffle uncomfortably as we continue to sit in near-total silence having exhausted all topics of conversation hours ago but neither of us having worked up the spirit to suggest that we part ways and go home", + "shove me into their large gaping maw, covering me in drool as I descend into their warm depths, becoming covered by hot bubbling acids that dissolve my body while I play Animal Crossing New Leaf on my Nintendo 3DS", + "promise me McDonalds but conveniently forget to pull into the drivethrough on the way home, leaving me burgerless", + "leave me in the car all alone with my favorite music and a bottle of water in the summer", + "create designs of particularly verbose hats describing extreme complexities and deep philosophical implications of their unusually specific feelings and often not particularly kind thoughts about me", + "methodically place brick after agonising brick in the wall they are constructing to forever entomb me in the basement which they claimed contained a case of Amontillado wine" + ) + + //time to actually generate the slogan + //50% chance of it just being a basic women/fish combo + if(prob(50)) + slogan = "Women [pick(feelings)], fish [pick(feelings)]" + else //we generate something more complex + if(prob(90)) //USUALLY one of the short simple phrases + slogan = "[pick("Women", "Fish")] [pick(feelings)]" + else + slogan = "[pick("Women", "Fish")] [pick(verylongfeelings)]" + hatsize += 2 + + //second line + if(prob(90)) //USUALLY one of the short simple phrases + slogan = "[slogan], [pick("Women", "Fish")] [pick(feelings)]" + else + slogan = "[slogan], [pick("Women", "Fish")] [pick(verylongfeelings)]" + hatsize += 2 + //chance of a third line + if(prob(50)) + if(prob(50)) //if a third line is rolled it's way more likely to be a long one + slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(feelings)]" + hatsize += 1 + else + slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(verylongfeelings)]" + hatsize += 3 + //you can even get a fourth + if(prob(25)) + if(prob(25)) //if a fourth line is rolled it's way WAY more likely to be a long one + slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(feelings)]" + hatsize += 1 + else + slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(verylongfeelings)]" + hatsize += 3 + + + //now we have the slogan, apply this to the description and name + desc = "A peaked cap with text reading '[slogan]'." + name = "\improper '[slogan]' hat" + + //pick a hue + var/colourtype = pick("green", "red", "blue", "yellow", "purple", "orange", "grey") + + //finally, take our hat size and pick the icon accordingly + icon_state = "[colourtype]soft[hatsize]" item_state_slots = list(slot_r_hand_str = "[colourtype]soft", slot_l_hand_str = "[colourtype]soft", slot_head_str = "[colourtype]soft[hatsize]") \ No newline at end of file diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index bbe3f21b44d..236f7857967 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -1,226 +1,226 @@ -/obj/item/clothing/head/helmet - name = "helmet" - desc = "Standard Security gear. Protects the head from impacts." - icon_state = "helmet" - valid_accessory_slots = (ACCESSORY_SLOT_HELM_C) - restricted_accessory_slots = (ACCESSORY_SLOT_HELM_C) - flags = THICKMATERIAL - armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) - flags_inv = HIDEEARS|BLOCKHEADHAIR - cold_protection = HEAD - min_cold_protection_temperature = HELMET_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = HEAD - max_heat_protection_temperature = HELMET_MAX_HEAT_PROTECTION_TEMPERATURE - siemens_coefficient = 0.7 - w_class = ITEMSIZE_NORMAL - ear_protection = 1 - drop_sound = 'sound/items/drop/helm.ogg' - pickup_sound = 'sound/items/pickup/helm.ogg' - -/obj/item/clothing/head/helmet/solgov - name = "\improper Solar Confederate Government helmet" - desc = "A helmet painted in Peacekeeper blue. Stands out like a sore thumb." - icon_state = "helmet_sol" - armor = list(melee = 50, bullet = 50, laser = 50,energy = 25, bomb = 30, bio = 0, rad = 0) - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/solgov/command - name = "command helmet" - desc = "A helmet with 'Solar Confederate Government' printed on the back in gold lettering." - icon_state = "helmet_command" - -/obj/item/clothing/head/helmet/solgov/security - name = "security helmet" - desc = "A helmet with 'MASTER AT ARMS' printed on the back in silver lettering." - icon_state = "helmet_security" - -/obj/item/clothing/head/helmet/nt - name = "\improper NanoTrasen helmet" - desc = "A helmet with 'CORPORATE SECURITY' printed on the back in red lettering." - icon_state = "helmet_nt" - -/obj/item/clothing/head/helmet/pcrc - name = "\improper PCRC helmet" - desc = "A helmet with 'PRIVATE SECURITY' printed on the back in cyan lettering." - icon_state = "helmet_pcrc" - -/obj/item/clothing/head/helmet/tac - name = "tactical helmet" - desc = "A tan helmet made from advanced ceramic. Comfortable and robust." - icon_state = "helmet_tac" - armor = list(melee = 50, bullet = 60, laser = 60, energy = 45, bomb = 30, bio = 0, rad = 0) - siemens_coefficient = 0.6 - -/obj/item/clothing/head/helmet/merc - name = "combat helmet" - desc = "A heavily reinforced helmet painted with red markings. Feels like it could take a lot of punishment." - icon_state = "helmet_merc" - armor = list(melee = 70, bullet = 70, laser = 70, energy = 35, bomb = 30, bio = 0, rad = 0) - siemens_coefficient = 0.5 - -/obj/item/clothing/head/helmet/riot - name = "riot helmet" - desc = "It's a helmet specifically designed to protect against close range attacks." - icon_state = "riot" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - armor = list(melee = 80, bullet = 10, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.7 - valid_accessory_slots = null - action_button_name = "Toggle Visor" - -/obj/item/clothing/head/helmet/riot/attack_self(mob/user as mob) - if(src.icon_state == initial(icon_state)) - src.icon_state = "[icon_state]up" - to_chat(user, "You raise the visor on the riot helmet.") - else - src.icon_state = initial(icon_state) - to_chat(user, "You lower the visor on the riot helmet.") - update_clothing_icon() //so our mob-overlays update - -/obj/item/clothing/head/helmet/laserproof - name = "ablative helmet" - desc = "It's a helmet specifically designed to protect against energy projectiles." - icon_state = "helmet_reflec" - item_state_slots = list(slot_r_hand_str = "helmet", slot_l_hand_str = "helmet") - armor = list(melee = 10, bullet = 10, laser = 80 ,energy = 50, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.1 - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/bulletproof - name = "bullet-resistant helmet" - desc = "It's a helmet specifically designed to protect against ballistic projectiles." - icon_state = "helmet_bulletproof" - item_state_slots = list(slot_r_hand_str = "helmet", slot_l_hand_str = "helmet") - armor = list(melee = 10, bullet = 80, laser = 10 ,energy = 10, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.7 - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/combat - name = "combat helmet" - desc = "It's a general purpose combat helmet, designed to protect against typical dangers to your head." - icon_state = "helmet_combat" - item_state_slots = list(slot_r_hand_str = "helmet", slot_l_hand_str = "helmet") - armor = list(melee = 50, bullet = 50, laser = 50 ,energy = 30, bomb = 30, bio = 0, rad = 0) - flags_inv = HIDEEARS|HIDEEYES|BLOCKHEADHAIR - siemens_coefficient = 0.6 - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/flexitac - name = "tactical light helmet" - desc = "A tan helmet made from advanced ceramic with an integrated tactical flashlight." - icon_state = "flexitac" - armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) - siemens_coefficient = 0.6 - light_range = 6 - light_overlay = "helmet_light_dual_green" - action_button_name = "Toggle Head-light" - min_cold_protection_temperature = T0C - 20 - cold_protection = HEAD - -/obj/item/clothing/head/helmet/explorer - name = "explorer hood" - desc = "An armoured hood for exploring harsh environments." - icon_state = "explorer" - flags = THICKMATERIAL - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0.9 - armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 35, bio = 75, rad = 35) - -/obj/item/clothing/head/helmet/swat - name = "\improper SWAT helmet" - desc = "They're often used by highly trained SWAT Officers." - icon_state = "swat" - armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 10, rad = 0) - flags_inv = HIDEEARS|HIDEEYES|BLOCKHEADHAIR - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0.5 - -/obj/item/clothing/head/helmet/alien - name = "alien helmet" - desc = "It's quite larger than your head, but it might still protect it." - icon_state = "alienhelmet" - siemens_coefficient = 0.4 - armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 50, bio = 0, rad = 40) - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/alien/tank - name = "alien warhelm" - armor = list(melee = 70, bullet = 70, laser = 70, energy = 70, bomb = 70, bio = 0, rad = 40) - -/obj/item/clothing/head/helmet/thunderdome - name = "\improper Thunderdome helmet" - desc = "'Let the battle commence!'" - icon_state = "thunderdome" - armor = list(melee = 80, bullet = 60, laser = 50,energy = 10, bomb = 25, bio = 10, rad = 0) - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 1 - -/obj/item/clothing/head/helmet/gladiator - name = "gladiator helmet" - desc = "Ave, Imperator, morituri te salutant." - icon_state = "gladiator" - item_state_slots = list(slot_r_hand_str = "vhelmet", slot_l_hand_str = "vhelmet") - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|BLOCKHAIR - siemens_coefficient = 1 - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/tactical - name = "tactical helmet" - desc = "An armored helmet capable of being fitted with a multitude of attachments." - icon_state = "swathelm" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - sprite_sheets = list( - SPECIES_TAJ = 'icons/inventory/head/mob_tajaran.dmi', - SPECIES_UNATHI = 'icons/inventory/head/mob_unathi.dmi', - ) - - armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) - flags_inv = HIDEEARS|BLOCKHAIR - siemens_coefficient = 0.7 - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/augment - name = "Augment Array" - desc = "A helmet with optical and cranial augments coupled to it." - icon_state = "v62" - item_state_slots = list(slot_r_hand_str = "head_m", slot_l_hand_str = "head_m") - armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 10, rad = 0) - flags_inv = HIDEEARS|HIDEEYES|BLOCKHEADHAIR - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0.5 - valid_accessory_slots = null - -//Non-hardsuit ERT helmets. -/obj/item/clothing/head/helmet/ert - name = "emergency response team helmet" - desc = "An in-atmosphere helmet worn by members of the NanoTrasen Emergency Response Team. Protects the head from impacts." - icon_state = "erthelmet_cmd" - item_state_slots = list(slot_r_hand_str = "syndicate-helm-green", slot_l_hand_str = "syndicate-helm-green") - armor = list(melee = 62, bullet = 50, laser = 50,energy = 35, bomb = 10, bio = 2, rad = 0) - valid_accessory_slots = null - -//Commander -/obj/item/clothing/head/helmet/ert/command - name = "emergency response team commander helmet" - desc = "An in-atmosphere helmet worn by the commander of a NanoTrasen Emergency Response Team. Has blue highlights." - -//Security -/obj/item/clothing/head/helmet/ert/security - name = "emergency response team security helmet" - desc = "An in-atmosphere helmet worn by security members of the NanoTrasen Emergency Response Team. Has red highlights." - icon_state = "erthelmet_sec" - -//Engineer -/obj/item/clothing/head/helmet/ert/engineer - name = "emergency response team engineer helmet" - desc = "An in-atmosphere helmet worn by engineering members of the NanoTrasen Emergency Response Team. Has orange highlights." - icon_state = "erthelmet_eng" - -//Medical -/obj/item/clothing/head/helmet/ert/medical - name = "emergency response team medical helmet" - desc = "A set of armor worn by medical members of the NanoTrasen Emergency Response Team. Has red and white highlights." - icon_state = "erthelmet_med" +/obj/item/clothing/head/helmet + name = "helmet" + desc = "Standard Security gear. Protects the head from impacts." + icon_state = "helmet" + valid_accessory_slots = (ACCESSORY_SLOT_HELM_C) + restricted_accessory_slots = (ACCESSORY_SLOT_HELM_C) + flags = THICKMATERIAL + armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) + flags_inv = HIDEEARS|BLOCKHEADHAIR + cold_protection = HEAD + min_cold_protection_temperature = HELMET_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = HEAD + max_heat_protection_temperature = HELMET_MAX_HEAT_PROTECTION_TEMPERATURE + siemens_coefficient = 0.7 + w_class = ITEMSIZE_NORMAL + ear_protection = 1 + drop_sound = 'sound/items/drop/helm.ogg' + pickup_sound = 'sound/items/pickup/helm.ogg' + +/obj/item/clothing/head/helmet/solgov + name = "\improper Solar Confederate Government helmet" + desc = "A helmet painted in Peacekeeper blue. Stands out like a sore thumb." + icon_state = "helmet_sol" + armor = list(melee = 50, bullet = 50, laser = 50,energy = 25, bomb = 30, bio = 0, rad = 0) + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/solgov/command + name = "command helmet" + desc = "A helmet with 'Solar Confederate Government' printed on the back in gold lettering." + icon_state = "helmet_command" + +/obj/item/clothing/head/helmet/solgov/security + name = "security helmet" + desc = "A helmet with 'MASTER AT ARMS' printed on the back in silver lettering." + icon_state = "helmet_security" + +/obj/item/clothing/head/helmet/nt + name = "\improper NanoTrasen helmet" + desc = "A helmet with 'CORPORATE SECURITY' printed on the back in red lettering." + icon_state = "helmet_nt" + +/obj/item/clothing/head/helmet/pcrc + name = "\improper PCRC helmet" + desc = "A helmet with 'PRIVATE SECURITY' printed on the back in cyan lettering." + icon_state = "helmet_pcrc" + +/obj/item/clothing/head/helmet/tac + name = "tactical helmet" + desc = "A tan helmet made from advanced ceramic. Comfortable and robust." + icon_state = "helmet_tac" + armor = list(melee = 50, bullet = 60, laser = 60, energy = 45, bomb = 30, bio = 0, rad = 0) + siemens_coefficient = 0.6 + +/obj/item/clothing/head/helmet/merc + name = "combat helmet" + desc = "A heavily reinforced helmet painted with red markings. Feels like it could take a lot of punishment." + icon_state = "helmet_merc" + armor = list(melee = 70, bullet = 70, laser = 70, energy = 35, bomb = 30, bio = 0, rad = 0) + siemens_coefficient = 0.5 + +/obj/item/clothing/head/helmet/riot + name = "riot helmet" + desc = "It's a helmet specifically designed to protect against close range attacks." + icon_state = "riot" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + armor = list(melee = 80, bullet = 10, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.7 + valid_accessory_slots = null + action_button_name = "Toggle Visor" + +/obj/item/clothing/head/helmet/riot/attack_self(mob/user as mob) + if(src.icon_state == initial(icon_state)) + src.icon_state = "[icon_state]up" + to_chat(user, "You raise the visor on the riot helmet.") + else + src.icon_state = initial(icon_state) + to_chat(user, "You lower the visor on the riot helmet.") + update_clothing_icon() //so our mob-overlays update + +/obj/item/clothing/head/helmet/laserproof + name = "ablative helmet" + desc = "It's a helmet specifically designed to protect against energy projectiles." + icon_state = "helmet_reflec" + item_state_slots = list(slot_r_hand_str = "helmet", slot_l_hand_str = "helmet") + armor = list(melee = 10, bullet = 10, laser = 80 ,energy = 50, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.1 + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/bulletproof + name = "bullet-resistant helmet" + desc = "It's a helmet specifically designed to protect against ballistic projectiles." + icon_state = "helmet_bulletproof" + item_state_slots = list(slot_r_hand_str = "helmet", slot_l_hand_str = "helmet") + armor = list(melee = 10, bullet = 80, laser = 10 ,energy = 10, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.7 + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/combat + name = "combat helmet" + desc = "It's a general purpose combat helmet, designed to protect against typical dangers to your head." + icon_state = "helmet_combat" + item_state_slots = list(slot_r_hand_str = "helmet", slot_l_hand_str = "helmet") + armor = list(melee = 50, bullet = 50, laser = 50 ,energy = 30, bomb = 30, bio = 0, rad = 0) + flags_inv = HIDEEARS|HIDEEYES|BLOCKHEADHAIR + siemens_coefficient = 0.6 + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/flexitac + name = "tactical light helmet" + desc = "A tan helmet made from advanced ceramic with an integrated tactical flashlight." + icon_state = "flexitac" + armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) + siemens_coefficient = 0.6 + light_range = 6 + light_overlay = "helmet_light_dual_green" + action_button_name = "Toggle Head-light" + min_cold_protection_temperature = T0C - 20 + cold_protection = HEAD + +/obj/item/clothing/head/helmet/explorer + name = "explorer hood" + desc = "An armoured hood for exploring harsh environments." + icon_state = "explorer" + flags = THICKMATERIAL + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0.9 + armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 35, bio = 75, rad = 35) + +/obj/item/clothing/head/helmet/swat + name = "\improper SWAT helmet" + desc = "They're often used by highly trained SWAT Officers." + icon_state = "swat" + armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 10, rad = 0) + flags_inv = HIDEEARS|HIDEEYES|BLOCKHEADHAIR + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0.5 + +/obj/item/clothing/head/helmet/alien + name = "alien helmet" + desc = "It's quite larger than your head, but it might still protect it." + icon_state = "alienhelmet" + siemens_coefficient = 0.4 + armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 50, bio = 0, rad = 40) + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/alien/tank + name = "alien warhelm" + armor = list(melee = 70, bullet = 70, laser = 70, energy = 70, bomb = 70, bio = 0, rad = 40) + +/obj/item/clothing/head/helmet/thunderdome + name = "\improper Thunderdome helmet" + desc = "'Let the battle commence!'" + icon_state = "thunderdome" + armor = list(melee = 80, bullet = 60, laser = 50,energy = 10, bomb = 25, bio = 10, rad = 0) + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 1 + +/obj/item/clothing/head/helmet/gladiator + name = "gladiator helmet" + desc = "Ave, Imperator, morituri te salutant." + icon_state = "gladiator" + item_state_slots = list(slot_r_hand_str = "vhelmet", slot_l_hand_str = "vhelmet") + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|BLOCKHAIR + siemens_coefficient = 1 + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/tactical + name = "tactical helmet" + desc = "An armored helmet capable of being fitted with a multitude of attachments." + icon_state = "swathelm" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + sprite_sheets = list( + SPECIES_TAJ = 'icons/inventory/head/mob_tajaran.dmi', + SPECIES_UNATHI = 'icons/inventory/head/mob_unathi.dmi', + ) + + armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) + flags_inv = HIDEEARS|BLOCKHAIR + siemens_coefficient = 0.7 + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/augment + name = "Augment Array" + desc = "A helmet with optical and cranial augments coupled to it." + icon_state = "v62" + item_state_slots = list(slot_r_hand_str = "head_m", slot_l_hand_str = "head_m") + armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 10, rad = 0) + flags_inv = HIDEEARS|HIDEEYES|BLOCKHEADHAIR + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0.5 + valid_accessory_slots = null + +//Non-hardsuit ERT helmets. +/obj/item/clothing/head/helmet/ert + name = "emergency response team helmet" + desc = "An in-atmosphere helmet worn by members of the NanoTrasen Emergency Response Team. Protects the head from impacts." + icon_state = "erthelmet_cmd" + item_state_slots = list(slot_r_hand_str = "syndicate-helm-green", slot_l_hand_str = "syndicate-helm-green") + armor = list(melee = 62, bullet = 50, laser = 50,energy = 35, bomb = 10, bio = 2, rad = 0) + valid_accessory_slots = null + +//Commander +/obj/item/clothing/head/helmet/ert/command + name = "emergency response team commander helmet" + desc = "An in-atmosphere helmet worn by the commander of a NanoTrasen Emergency Response Team. Has blue highlights." + +//Security +/obj/item/clothing/head/helmet/ert/security + name = "emergency response team security helmet" + desc = "An in-atmosphere helmet worn by security members of the NanoTrasen Emergency Response Team. Has red highlights." + icon_state = "erthelmet_sec" + +//Engineer +/obj/item/clothing/head/helmet/ert/engineer + name = "emergency response team engineer helmet" + desc = "An in-atmosphere helmet worn by engineering members of the NanoTrasen Emergency Response Team. Has orange highlights." + icon_state = "erthelmet_eng" + +//Medical +/obj/item/clothing/head/helmet/ert/medical + name = "emergency response team medical helmet" + desc = "A set of armor worn by medical members of the NanoTrasen Emergency Response Team. Has red and white highlights." + icon_state = "erthelmet_med" diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index 29bd574d65b..3178da47572 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -1,291 +1,291 @@ - -//Bartender -/obj/item/clothing/head/chefhat - name = "chef's hat" - desc = "It's a hat used by chefs to keep hair out of your food. Judging by the food in the mess, they don't work." - icon_state = "chefhat" - -/obj/item/clothing/head/hairnet - name = "hairnet" - desc = "A hairnet used to keep the hair out of the way and out of the food." - icon_state = "hairnet" - sprite_sheets = list( - SPECIES_TAJARAN = 'icons/inventory/head/mob_tajaran.dmi' - ) - -//Captain -/obj/item/clothing/head/caphat - name = "site manager's hat" - icon_state = "captain" - desc = "It's good being the king." - body_parts_covered = 0 - -/obj/item/clothing/head/caphat/cap - name = "site manager's cap" - desc = "You fear to wear it for the negligence it brings." - icon_state = "capcap" - -/obj/item/clothing/head/caphat/formal - name = "parade hat" - desc = "No one in a commanding position should be without a perfect, white hat of ultimate authority." - icon_state = "officercap" - -/obj/item/clothing/head/caphat/beret - name = "captain's beret" - desc = "A beret fit for a leader." - icon_state = "beretcap" - -//HOP -/obj/item/clothing/head/caphat/hop - name = "crew resource's hat" - desc = "A stylish hat that both protects you from enraged former-crewmembers and gives you a false sense of authority." - icon_state = "hopcap" - -/obj/item/clothing/head/caphat/hop/beret - name = "head of personnel's beret" - desc = "The symbol of true bureaucratic micromanagement, although in a fancy form." - icon_state = "berethop" - -/obj/item/clothing/head/caphat/hop/beret/white - name = "head of personnel's white beret" - desc = "The symbol of true bureaucratic micromanagement, although in a fancy form." - icon_state = "berethopwhite" - -//Chaplain -/obj/item/clothing/head/chaplain_hood - name = "chaplain's hood" - desc = "It's a hood that covers the head. It keeps you warm during the space winters." - icon_state = "chaplain_hood" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - flags_inv = BLOCKHAIR - body_parts_covered = HEAD - -//Chaplain but spookier -/obj/item/clothing/head/chaplain_hood/whiteout - name = "white hood" - desc = "It's a generic white hood. Very spooky." - icon_state = "whiteout_hood" - item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") - -//Chaplain -/obj/item/clothing/head/nun_hood - name = "nun hood" - desc = "Maximum piety in this star system." - icon_state = "nun_hood" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - flags_inv = BLOCKHAIR - body_parts_covered = HEAD - -//Mime -/obj/item/clothing/head/beret - name = "beret" - desc = "A beret, an artists favorite headwear." - icon_state = "beret" - body_parts_covered = 0 - -//Security -/obj/item/clothing/head/beret/sec - name = "security beret" - desc = "A beret with the security insignia emblazoned on it. For officers that are more inclined towards style than safety." - icon_state = "beret_officer" - item_state_slots = list(slot_r_hand_str = "beret", slot_l_hand_str = "beret") - -/obj/item/clothing/head/beret/sec/navy/officer - name = "officer beret" - desc = "A navy blue beret with an officer's rank emblem. For officers that are more inclined towards style than safety." - icon_state = "beret_navy_officer" - item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") - -/obj/item/clothing/head/beret/sec/navy/hos - name = "Head of Security beret" - desc = "A navy blue beret with a Head of Security's rank emblem. For officers that are more inclined towards style than safety." - icon_state = "beret_navy_hos" - item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") - -/obj/item/clothing/head/beret/sec/navy/warden - name = "warden beret" - desc = "A navy blue beret with a warden's rank emblem. For officers that are more inclined towards style than safety." - icon_state = "beret_navy_warden" - item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") - -/obj/item/clothing/head/beret/sec/corporate/officer - name = "officer beret" - desc = "A corporate black beret with an officer's rank emblem. For officers that are more inclined towards style than safety." - icon_state = "beret_corporate_officer" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - -/obj/item/clothing/head/beret/sec/corporate/hos - name = "Head of Security beret" - desc = "A corporate black beret with a Head of Security's rank emblem. For officers that are more inclined towards style than safety." - icon_state = "beret_corporate_hos" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - -/obj/item/clothing/head/beret/sec/corporate/warden - name = "warden beret" - desc = "A corporate black beret with a warden's rank emblem. For officers that are more inclined towards style than safety." - icon_state = "beret_corporate_warden" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - -/obj/item/clothing/head/helmet/warden - name = "warden's helmet" - desc = "Standard Warden gear. Protects the head from impacts." - -/obj/item/clothing/head/helmet/warden/hat - name = "warden's hat" - desc = "It's a special hat issued to the Warden of a securiy force." - icon_state = "policehelm" - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/HoS - name = "Head of Security helmet" - desc = "Standard Head of Security gear. Protects the head from impacts." - -/obj/item/clothing/head/helmet/HoS/hat - name = "Head of Security Hat" - desc = "The hat of the Head of Security. For showing the officers who's in charge." - icon_state = "hoscap" - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/dermal - name = "Dermal Armour Patch" - desc = "You're not quite sure how you manage to take it on and off, but it implants nicely in your head." - icon_state = "dermal" - item_state_slots = list(slot_r_hand_str = "", slot_l_hand_str = "") - valid_accessory_slots = null - show_examine = FALSE - flags_inv = null - -/obj/item/clothing/head/det - name = "detective fedora" - desc = "A specially designed fedora that is woven with protective fibers. It also makes you look cool." - icon_state = "fedora_brown" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - allowed = list(/obj/item/weapon/reagent_containers/food/snacks/candy_corn, /obj/item/weapon/pen) - armor = list(melee = 10, bullet = 10, laser = 15, energy = 10, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - valid_accessory_slots = null - show_examine = FALSE - -/obj/item/clothing/head/det/grey - icon_state = "fedora_grey" - -/obj/item/clothing/head/beret/engineering - name = "engineering beret" - desc = "A beret with the engineering insignia emblazoned on it. For engineers that are more inclined towards style than safety." - icon_state = "beret_orange" - -/obj/item/clothing/head/beret/purple - name = "purple beret" - desc = "A stylish, if purple, beret." - icon_state = "beret_purpleyellow" - -/obj/item/clothing/head/beret/centcom/officer - name = "officers beret" - desc = "A dark blue beret adorned with a silver patch. Worn by NanoTrasen Officials." - icon_state = "beret_centcom_officer" - item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") - -/obj/item/clothing/head/beret/centcom/captain - name = "captains beret" - desc = "A white beret adorned with a blue patch. Worn by NanoTrasen command staff." - icon_state = "beret_centcom_captain" - item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") - -/obj/item/clothing/head/beret/sec/gov - name = "officer beret" - desc = "A black beret with a gold emblem." - icon_state = "beret_corporate_hos" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - - -//Medical -/obj/item/clothing/head/surgery - name = "surgical cap" - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs." - icon_state = "surgcap_blue" - item_state_slots = list(slot_r_hand_str = "beret_blue", slot_l_hand_str = "beret_blue") - flags_inv = BLOCKHEADHAIR - -/obj/item/clothing/head/surgery/purple - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is deep purple." - icon_state = "surgcap_purple" - item_state_slots = list(slot_r_hand_str = "beret_purple", slot_l_hand_str = "beret_purple") - -/obj/item/clothing/head/surgery/blue - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is baby blue." - icon_state = "surgcap_blue" - item_state_slots = list(slot_r_hand_str = "beret_blue", slot_l_hand_str = "beret_blue") - -/obj/item/clothing/head/surgery/green - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is dark green." - icon_state = "surgcap_green" - item_state_slots = list(slot_r_hand_str = "beret_green", slot_l_hand_str = "beret_green") - -/obj/item/clothing/head/surgery/black - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is black." - icon_state = "surgcap_black" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - -/obj/item/clothing/head/surgery/navyblue - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is navy blue." - icon_state = "surgcap_navyblue" - item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") - -/obj/item/clothing/head/beret/medical - name = "medical officer's beret" - desc = "A fancy beret with a blue cross, smells sterile." - icon_state = "beretmed" - -/obj/item/clothing/head/beret/medical/chem - name = "chemist's beret" - desc = "A fancy beret with an orange beaker. You're not sure if you should smell it." - icon_state = "beretchem" - -/obj/item/clothing/head/beret/medical/viro - name = "virologist's beret" - desc = "A fancy beret with a green cross. Hopefully it's virus free!" - icon_state = "beretviro" - -/obj/item/clothing/head/beret/medical/cmo - name = "chief medical officer's beret" - desc = "A fancy beret with a green cross, signifying your status in the station's medbay." - icon_state = "beretcmo" - -/obj/item/clothing/head/beret/medical/cmo/blue - name = "chief medical officer's beret" - desc = "A fancy beret with a blue and white cross. Try not to be the chief malpractice officer in it!" - icon_state = "beretcmoblue" - -//Science - -/obj/item/clothing/head/beret/science - name = "scientist's beret" - desc = "A scientist's beret. Looks like it's covered in slime." - icon_state = "beretsci" - -/obj/item/clothing/head/beret/science/robotics - name = "roboticist's beret" - desc = "A roboticist's beret. It strongly smells of oil." - icon_state = "beretrobo" - -/obj/item/clothing/head/beret/science/rd - name = "research director's beret" - desc = "A beret worn only by highly intelligent people. Or so its wearers say." - icon_state = "beretrd" - -//Chief Engineer -/obj/item/clothing/head/beret/engineering/ce - name = "chief engineer's beret" - desc = "A beret that will surely make you look way cooler than a hard hat, although lack of protection is the price." - icon_state = "beretce" - -/obj/item/clothing/head/beret/engineering/ce/white - name = "chief engineer's white beret" - desc = "A beret that will surely make you look way cooler than a hard hat, although lack of protection is the price." - icon_state = "beretcewhite" - -//Quartermaster -/obj/item/clothing/head/beret/qm - name = "quartermaster's beret" - desc = "This headwear shows off your Cargonian leadership." + +//Bartender +/obj/item/clothing/head/chefhat + name = "chef's hat" + desc = "It's a hat used by chefs to keep hair out of your food. Judging by the food in the mess, they don't work." + icon_state = "chefhat" + +/obj/item/clothing/head/hairnet + name = "hairnet" + desc = "A hairnet used to keep the hair out of the way and out of the food." + icon_state = "hairnet" + sprite_sheets = list( + SPECIES_TAJARAN = 'icons/inventory/head/mob_tajaran.dmi' + ) + +//Captain +/obj/item/clothing/head/caphat + name = "site manager's hat" + icon_state = "captain" + desc = "It's good being the king." + body_parts_covered = 0 + +/obj/item/clothing/head/caphat/cap + name = "site manager's cap" + desc = "You fear to wear it for the negligence it brings." + icon_state = "capcap" + +/obj/item/clothing/head/caphat/formal + name = "parade hat" + desc = "No one in a commanding position should be without a perfect, white hat of ultimate authority." + icon_state = "officercap" + +/obj/item/clothing/head/caphat/beret + name = "captain's beret" + desc = "A beret fit for a leader." + icon_state = "beretcap" + +//HOP +/obj/item/clothing/head/caphat/hop + name = "crew resource's hat" + desc = "A stylish hat that both protects you from enraged former-crewmembers and gives you a false sense of authority." + icon_state = "hopcap" + +/obj/item/clothing/head/caphat/hop/beret + name = "head of personnel's beret" + desc = "The symbol of true bureaucratic micromanagement, although in a fancy form." + icon_state = "berethop" + +/obj/item/clothing/head/caphat/hop/beret/white + name = "head of personnel's white beret" + desc = "The symbol of true bureaucratic micromanagement, although in a fancy form." + icon_state = "berethopwhite" + +//Chaplain +/obj/item/clothing/head/chaplain_hood + name = "chaplain's hood" + desc = "It's a hood that covers the head. It keeps you warm during the space winters." + icon_state = "chaplain_hood" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + flags_inv = BLOCKHAIR + body_parts_covered = HEAD + +//Chaplain but spookier +/obj/item/clothing/head/chaplain_hood/whiteout + name = "white hood" + desc = "It's a generic white hood. Very spooky." + icon_state = "whiteout_hood" + item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") + +//Chaplain +/obj/item/clothing/head/nun_hood + name = "nun hood" + desc = "Maximum piety in this star system." + icon_state = "nun_hood" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + flags_inv = BLOCKHAIR + body_parts_covered = HEAD + +//Mime +/obj/item/clothing/head/beret + name = "beret" + desc = "A beret, an artists favorite headwear." + icon_state = "beret" + body_parts_covered = 0 + +//Security +/obj/item/clothing/head/beret/sec + name = "security beret" + desc = "A beret with the security insignia emblazoned on it. For officers that are more inclined towards style than safety." + icon_state = "beret_officer" + item_state_slots = list(slot_r_hand_str = "beret", slot_l_hand_str = "beret") + +/obj/item/clothing/head/beret/sec/navy/officer + name = "officer beret" + desc = "A navy blue beret with an officer's rank emblem. For officers that are more inclined towards style than safety." + icon_state = "beret_navy_officer" + item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") + +/obj/item/clothing/head/beret/sec/navy/hos + name = "Head of Security beret" + desc = "A navy blue beret with a Head of Security's rank emblem. For officers that are more inclined towards style than safety." + icon_state = "beret_navy_hos" + item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") + +/obj/item/clothing/head/beret/sec/navy/warden + name = "warden beret" + desc = "A navy blue beret with a warden's rank emblem. For officers that are more inclined towards style than safety." + icon_state = "beret_navy_warden" + item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") + +/obj/item/clothing/head/beret/sec/corporate/officer + name = "officer beret" + desc = "A corporate black beret with an officer's rank emblem. For officers that are more inclined towards style than safety." + icon_state = "beret_corporate_officer" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + +/obj/item/clothing/head/beret/sec/corporate/hos + name = "Head of Security beret" + desc = "A corporate black beret with a Head of Security's rank emblem. For officers that are more inclined towards style than safety." + icon_state = "beret_corporate_hos" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + +/obj/item/clothing/head/beret/sec/corporate/warden + name = "warden beret" + desc = "A corporate black beret with a warden's rank emblem. For officers that are more inclined towards style than safety." + icon_state = "beret_corporate_warden" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + +/obj/item/clothing/head/helmet/warden + name = "warden's helmet" + desc = "Standard Warden gear. Protects the head from impacts." + +/obj/item/clothing/head/helmet/warden/hat + name = "warden's hat" + desc = "It's a special hat issued to the Warden of a securiy force." + icon_state = "policehelm" + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/HoS + name = "Head of Security helmet" + desc = "Standard Head of Security gear. Protects the head from impacts." + +/obj/item/clothing/head/helmet/HoS/hat + name = "Head of Security Hat" + desc = "The hat of the Head of Security. For showing the officers who's in charge." + icon_state = "hoscap" + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/dermal + name = "Dermal Armour Patch" + desc = "You're not quite sure how you manage to take it on and off, but it implants nicely in your head." + icon_state = "dermal" + item_state_slots = list(slot_r_hand_str = "", slot_l_hand_str = "") + valid_accessory_slots = null + show_examine = FALSE + flags_inv = null + +/obj/item/clothing/head/det + name = "detective fedora" + desc = "A specially designed fedora that is woven with protective fibers. It also makes you look cool." + icon_state = "fedora_brown" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + allowed = list(/obj/item/weapon/reagent_containers/food/snacks/candy_corn, /obj/item/weapon/pen) + armor = list(melee = 10, bullet = 10, laser = 15, energy = 10, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + valid_accessory_slots = null + show_examine = FALSE + +/obj/item/clothing/head/det/grey + icon_state = "fedora_grey" + +/obj/item/clothing/head/beret/engineering + name = "engineering beret" + desc = "A beret with the engineering insignia emblazoned on it. For engineers that are more inclined towards style than safety." + icon_state = "beret_orange" + +/obj/item/clothing/head/beret/purple + name = "purple beret" + desc = "A stylish, if purple, beret." + icon_state = "beret_purpleyellow" + +/obj/item/clothing/head/beret/centcom/officer + name = "officers beret" + desc = "A dark blue beret adorned with a silver patch. Worn by NanoTrasen Officials." + icon_state = "beret_centcom_officer" + item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") + +/obj/item/clothing/head/beret/centcom/captain + name = "captains beret" + desc = "A white beret adorned with a blue patch. Worn by NanoTrasen command staff." + icon_state = "beret_centcom_captain" + item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") + +/obj/item/clothing/head/beret/sec/gov + name = "officer beret" + desc = "A black beret with a gold emblem." + icon_state = "beret_corporate_hos" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + + +//Medical +/obj/item/clothing/head/surgery + name = "surgical cap" + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs." + icon_state = "surgcap_blue" + item_state_slots = list(slot_r_hand_str = "beret_blue", slot_l_hand_str = "beret_blue") + flags_inv = BLOCKHEADHAIR + +/obj/item/clothing/head/surgery/purple + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is deep purple." + icon_state = "surgcap_purple" + item_state_slots = list(slot_r_hand_str = "beret_purple", slot_l_hand_str = "beret_purple") + +/obj/item/clothing/head/surgery/blue + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is baby blue." + icon_state = "surgcap_blue" + item_state_slots = list(slot_r_hand_str = "beret_blue", slot_l_hand_str = "beret_blue") + +/obj/item/clothing/head/surgery/green + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is dark green." + icon_state = "surgcap_green" + item_state_slots = list(slot_r_hand_str = "beret_green", slot_l_hand_str = "beret_green") + +/obj/item/clothing/head/surgery/black + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is black." + icon_state = "surgcap_black" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + +/obj/item/clothing/head/surgery/navyblue + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is navy blue." + icon_state = "surgcap_navyblue" + item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") + +/obj/item/clothing/head/beret/medical + name = "medical officer's beret" + desc = "A fancy beret with a blue cross, smells sterile." + icon_state = "beretmed" + +/obj/item/clothing/head/beret/medical/chem + name = "chemist's beret" + desc = "A fancy beret with an orange beaker. You're not sure if you should smell it." + icon_state = "beretchem" + +/obj/item/clothing/head/beret/medical/viro + name = "virologist's beret" + desc = "A fancy beret with a green cross. Hopefully it's virus free!" + icon_state = "beretviro" + +/obj/item/clothing/head/beret/medical/cmo + name = "chief medical officer's beret" + desc = "A fancy beret with a green cross, signifying your status in the station's medbay." + icon_state = "beretcmo" + +/obj/item/clothing/head/beret/medical/cmo/blue + name = "chief medical officer's beret" + desc = "A fancy beret with a blue and white cross. Try not to be the chief malpractice officer in it!" + icon_state = "beretcmoblue" + +//Science + +/obj/item/clothing/head/beret/science + name = "scientist's beret" + desc = "A scientist's beret. Looks like it's covered in slime." + icon_state = "beretsci" + +/obj/item/clothing/head/beret/science/robotics + name = "roboticist's beret" + desc = "A roboticist's beret. It strongly smells of oil." + icon_state = "beretrobo" + +/obj/item/clothing/head/beret/science/rd + name = "research director's beret" + desc = "A beret worn only by highly intelligent people. Or so its wearers say." + icon_state = "beretrd" + +//Chief Engineer +/obj/item/clothing/head/beret/engineering/ce + name = "chief engineer's beret" + desc = "A beret that will surely make you look way cooler than a hard hat, although lack of protection is the price." + icon_state = "beretce" + +/obj/item/clothing/head/beret/engineering/ce/white + name = "chief engineer's white beret" + desc = "A beret that will surely make you look way cooler than a hard hat, although lack of protection is the price." + icon_state = "beretcewhite" + +//Quartermaster +/obj/item/clothing/head/beret/qm + name = "quartermaster's beret" + desc = "This headwear shows off your Cargonian leadership." icon_state = "beretqm" \ No newline at end of file diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index f0851e780a3..71272a69994 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -1,565 +1,565 @@ -/obj/item/clothing/head/centhat - name = "\improper CentCom. hat" - icon_state = "centcom" - desc = "It's good to be emperor." - siemens_coefficient = 0.9 - body_parts_covered = 0 - -/obj/item/clothing/head/centhat/customs - name = "Customs Hat" - desc = "A formal hat for SolGov Customs Officers." - icon_state = "customshat" - -/obj/item/clothing/head/halo - name = "halo" - desc = "a small metal ring, floating above it's wearer." - icon_state = "halo" - -/obj/item/clothing/head/headband/maid/modern - name = "modern maid headband" - desc = "Just like from my Japanese cartoons!" - icon_state = "maid_headband" - -/obj/item/clothing/head/pin - icon_state = "pin" - addblends = "pin_a" - name = "hair pin" - desc = "A nice hair pin." - slot_flags = SLOT_HEAD | SLOT_EARS - body_parts_covered = 0 - drop_sound = 'sound/items/drop/accessory.ogg' - pickup_sound = 'sound/items/pickup/accessory.ogg' - -/obj/item/clothing/head/pin/pink - icon_state = "pinkpin" - addblends = null - name = "pink hair hat" - -/obj/item/clothing/head/pin/clover - icon_state = "cloverpin" - name = "clover pin" - addblends = null - desc = "A hair pin in the shape of a clover leaf." - -/obj/item/clothing/head/pin/butterfly - icon_state = "butterflypin" - name = "butterfly pin" - addblends = null - desc = "A hair pin in the shape of a bright blue butterfly." - -/obj/item/clothing/head/pin/magnetic - icon_state = "magnetpin" - name = "magnetic 'pin'" - addblends = null - desc = "Finally, a hair pin even a Morpheus chassis can use." - matter = list(MAT_STEEL = 10) - -/obj/item/clothing/head/pin/flower - name = "red flower pin" - icon_state = "hairflower" - addblends = null - desc = "Smells nice." - -/obj/item/clothing/head/pin/flower/blue - icon_state = "hairflower_blue" - name = "blue flower pin" - -/obj/item/clothing/head/pin/flower/pink - icon_state = "hairflower_pink" - name = "pink flower pin" - -/obj/item/clothing/head/pin/flower/yellow - icon_state = "hairflower_yellow" - name = "yellow flower pin" - -/obj/item/clothing/head/pin/flower/violet - icon_state = "hairflower_violet" - name = "violet flower pin" - -/obj/item/clothing/head/pin/flower/orange - icon_state = "hairflower_orange" - name = "orange flower pin" - -/obj/item/clothing/head/pin/flower/white - icon_state = "hairflower_white" - addblends = "hairflower_white_a" - name = "flower pin" - -/obj/item/clothing/head/pin/bow - icon_state = "bow" - addblends = "bow_a" - name = "hair bow" - desc = "A ribbon tied into a bow with a clip on the back to attach to hair." - item_state_slots = list(slot_r_hand_str = "pill", slot_l_hand_str = "pill") - -/obj/item/clothing/head/pin/bow/big - icon_state = "whiteribbon" - name = "ribbon" - -/obj/item/clothing/head/pin/bow/big/red - icon_state = "redribbon" - name = "red ribbon" - addblends = null - -/obj/item/clothing/head/powdered_wig - name = "powdered wig" - desc = "A powdered wig." - icon_state = "pwig" - -/obj/item/clothing/head/redcoat - name = "redcoat's hat" - icon_state = "redcoat" - item_state_slots = list(slot_r_hand_str = "pirate", slot_l_hand_str = "pirate") - desc = "'I guess it's a redhead.'" - body_parts_covered = 0 - -/obj/item/clothing/head/mailman - name = "station cap" - icon_state = "mailman" - item_state_slots = list(slot_r_hand_str = "hopcap", slot_l_hand_str = "hopcap") - desc = "Choo-choo!" - body_parts_covered = 0 - -/obj/item/clothing/head/plaguedoctorhat - name = "plague doctor's hat" - desc = "These were once used by Plague doctors, allegedly. They're pretty much useless." - icon_state = "plaguedoctor" - item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") - permeability_coefficient = 0.01 - siemens_coefficient = 0.9 - body_parts_covered = 0 - -/obj/item/clothing/head/plaguedoctorhat/gold - name = "golden plague doctor's hat" - desc = "These were once used by plague doctors, allegedly. This one has gold accents." - icon_state = "plaguedoctor2" - -/obj/item/clothing/head/hasturhood - name = "hastur's hood" - desc = "It's unspeakably stylish" - icon_state = "hasturhood" - item_state_slots = list(slot_r_hand_str = "enginering_beret", slot_l_hand_str = "enginering_beret") - flags_inv = BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/nursehat - name = "nurse's hat" - desc = "It allows quick identification of trained medical personnel." - icon_state = "nursehat" - siemens_coefficient = 0.9 - body_parts_covered = 0 - -/obj/item/clothing/head/syndicatefake - name = "red space-helmet replica" - item_state_slots = list(slot_r_hand_str = "syndicate-helm-black-red", slot_l_hand_str = "syndicate-helm-black-red") - icon_state = "syndicate" - desc = "A plastic replica of a bloodthirsty mercenary's space helmet, you'll look just like a real murderous criminal operative in this! This is a toy, it is not made for use in space!" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR - siemens_coefficient = 2.0 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/cueball - name = "cueball helmet" - desc = "A large, featureless white orb mean to be worn on your head. How do you even see out of this thing?" - icon_state = "cueball" - flags_inv = BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/greenbandana - name = "green bandana" - desc = "It's a green bandana with some fine nanotech lining." - icon_state = "greenbandana" - flags_inv = 0 - body_parts_covered = 0 - -/obj/item/clothing/head/cardborg - name = "cardborg helmet" - desc = "A helmet made out of a box." - icon_state = "cardborg_h" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - body_parts_covered = HEAD|FACE|EYES - drop_sound = 'sound/items/drop/cardboardbox.ogg' - pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - -/obj/item/clothing/head/justice - name = "justice hat" - desc = "fight for what's righteous!" - icon_state = "justicered" //Does this even exist? - flags_inv = BLOCKHAIR - body_parts_covered = HEAD|EYES - -/obj/item/clothing/head/justice/blue - icon_state = "justiceblue" - -/obj/item/clothing/head/justice/yellow - icon_state = "justiceyellow" - -/obj/item/clothing/head/justice/green - icon_state = "justicegreen" - -/obj/item/clothing/head/justice/pink - icon_state = "justicepink" - -/obj/item/clothing/head/rabbitears - name = "rabbit ears" - desc = "Wearing these makes you looks useless, and only good for your sex appeal." - icon_state = "bunny" - body_parts_covered = 0 - -/obj/item/clothing/head/flatcap - name = "flat cap" - desc = "A working man's cap." - icon_state = "flat_cap" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - siemens_coefficient = 0.9 //...what? - -/obj/item/clothing/head/flatcap/grey - icon_state = "flat_capw" - addblends = "flat_capw_a" - item_state_slots = list(slot_r_hand_str = "greysoft", slot_l_hand_str = "greysoft") - -/obj/item/clothing/head/pirate - name = "pirate hat" - desc = "Yarr." - icon_state = "pirate" - body_parts_covered = 0 - -/obj/item/clothing/head/hgpiratecap - name = "pirate hat" - desc = "Yarr." - icon_state = "hgpiratecap" - item_state_slots = list(slot_r_hand_str = "hoscap", slot_l_hand_str = "hoscap") - body_parts_covered = 0 - -/obj/item/clothing/head/bandana - name = "pirate bandana" - desc = "Yarr." - icon_state = "bandana" - item_state_slots = list(slot_r_hand_str = "redbandana", slot_l_hand_str = "redbandana") - -/obj/item/clothing/head/witchwig - name = "witch costume wig" - desc = "Eeeee~heheheheheheh!" - icon_state = "witch" - flags_inv = BLOCKHAIR - siemens_coefficient = 2.0 - -/obj/item/clothing/head/chicken - name = "chicken suit head" - desc = "Bkaw!" - icon_state = "chickenhead" - flags_inv = BLOCKHAIR - siemens_coefficient = 0.7 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/bearpelt - name = "bear pelt hat" - desc = "Fuzzy." - icon_state = "bearpelt" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - flags_inv = BLOCKHAIR - siemens_coefficient = 0.7 - -/obj/item/clothing/head/xenos - name = "xenos helmet" - icon_state = "xenos" - item_state_slots = list(slot_r_hand_str = "xenos_helm", slot_l_hand_str = "xenos_helm") - desc = "A helmet made out of chitinous alien hide." - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR - siemens_coefficient = 2.0 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/philosopher_wig - name = "natural philosopher's wig" - desc = "A stylish monstrosity unearthed from Earth's Renaissance period. With this most distinguish'd wig, you'll be ready for your next soiree!" - icon_state = "philosopher_wig" - item_state_slots = list(slot_r_hand_str = "pwig", slot_l_hand_str = "pwig") - flags_inv = BLOCKHAIR - siemens_coefficient = 2.0 //why is it so conductive?! - body_parts_covered = 0 - -/obj/item/clothing/head/orangebandana //themij: Taryn Kifer - name = "orange bandana" - desc = "An orange piece of cloth, worn on the head." - icon_state = "orange_bandana" - body_parts_covered = 0 - -/obj/item/clothing/head/hijab - name = "hijab" - desc = "A veil that is wrapped to cover the head and chest" - icon_state = "hijab" - addblends = "hijab_a" - item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") - body_parts_covered = 0 - flags_inv = BLOCKHAIR - -/obj/item/clothing/head/kippa - name = "kippa" - desc = "A small, brimless cap." - icon_state = "kippa" - addblends = "kippa_a" - body_parts_covered = 0 - -/obj/item/clothing/head/turban - name = "turban" - desc = "A cloth used to wind around the head" - icon_state = "turban" - addblends = "turban_a" - item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") - body_parts_covered = 0 - flags_inv = BLOCKHEADHAIR - -/obj/item/clothing/head/taqiyah - name = "taqiyah" - desc = "A short, rounded skullcap usually worn for religious purposes." - icon_state = "taqiyah" - addblends = "taqiyah_a" - item_state_slots = list(slot_r_hand_str = "taq", slot_l_hand_str = "taq") - -/obj/item/clothing/head/beanie - name = "beanie" - desc = "A head-hugging brimless winter cap. This one is tight." - icon_state = "beanie" - addblends = "beanie_a" - body_parts_covered = 0 - -/obj/item/clothing/head/beanie_loose - name = "loose beanie" - desc = "A head-hugging brimless winter cap. This one is loose." - icon_state = "beanie_hang" - addblends = "beanie_hang_a" - body_parts_covered = 0 - -/obj/item/clothing/head/beretg - name = "beret" - desc = "A beret, an artists favorite headwear." - icon_state = "beret_g" - addblends = "beret_g_a" - body_parts_covered = 0 - -/obj/item/clothing/head/sombrero - name = "sombrero" - desc = "A wide-brimmed hat popularly worn in Mexico." - icon_state = "sombrero" - body_parts_covered = 0 - -/obj/item/clothing/head/headband/maid - name = "maid headband" - desc = "Keeps hair out of the way for important... jobs." - icon_state = "maid" - body_parts_covered = 0 - -/obj/item/clothing/head/maangtikka - name = "maang tikka" - desc = "A jeweled headpiece originating in India." - icon_state = "maangtikka" - body_parts_covered = 0 - drop_sound = 'sound/items/drop/ring.ogg' - pickup_sound = 'sound/items/pickup/ring.ogg' - -/obj/item/clothing/head/jingasa - name = "jingasa" - desc = "A wide, flat rain hat originally from Japan." - icon_state = "jingasa" - body_parts_covered = 0 - item_state_slots = list(slot_r_hand_str = "taq", slot_l_hand_str = "taq") - -/obj/item/clothing/head/cowl - name = "black cowl" - desc = "A gold-lined black cowl. It gives off uncomfortable cult vibes, but fancy." - icon_state = "cowl" - body_parts_covered = 0 - -/obj/item/clothing/head/cowl - name = "white cowl" - desc = "A gold-lined white cowl. It gives off uncomfortable cult vibes, but fancy." - icon_state = "whitecowl" - body_parts_covered = 0 - -/obj/item/clothing/head/blackngoldheaddress - name = "black and gold headdress" - desc = "An odd looking headdress that covers the eyes." - icon_state = "blackngoldheaddress" - flags_inv = HIDEEYES - body_parts_covered = HEAD|EYES - -//Corporate Berets - -/obj/item/clothing/head/beret/corp/saare - name = "\improper SAARE beret" - desc = "A red beret denoting service with Stealth Assault Enterprises. For mercenaries that are more inclined towards style than safety." - icon_state = "beret_red" - -/obj/item/clothing/head/beret/corp/saare/officer - name = "\improper SAARE officer beret" - desc = "A red beret with a gold insignia, denoting senior service with Stealth Assault Enterprises. For mercenaries who are more inclined towards style than safety." - icon_state = "beret_redgold" - -/obj/item/clothing/head/beret/corp/pcrc - name = "\improper PCRC beret" - desc = "A black beret with a PCRC logo insignia, denoting service with Proxima Centauri Risk Control. For private security personnel that are more inclined towards style than safety." - icon_state = "beret_black_observatory" - - -/obj/item/clothing/head/beret/corp/hedberg - name = "\improper Hedberg-Hammarstrom beret" - desc = "A tan beret denoting service with Hedberg-Hammarstrom private security. For mercenaries who are more inclined towards style than safety." - icon_state = "beret_tan" - -/obj/item/clothing/head/beret/corp/xion - name = "\improper Xion beret" - desc = "An orange beret denoting employment with Xion Manufacturing. For personnel that are more inclined towards style than safety." - icon_state = "beret_orange" - -//Stylish Hats - -/obj/item/clothing/head/bowler - name = "bowler hat" - desc = "Gentleman, elite aboard!" - icon_state = "bowler" - item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") - body_parts_covered = 0 - -/obj/item/clothing/head/that - name = "top-hat" - desc = "It's an amish looking hat." - icon_state = "tophat" - siemens_coefficient = 0.9 - body_parts_covered = 0 - -/obj/item/clothing/head/beaverhat - name = "beaver hat" - desc = "Soft felt makes this hat both comfortable and elegant." - icon_state = "beaver_hat" - item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") - siemens_coefficient = 0.9 - body_parts_covered = 0 - -/obj/item/clothing/head/boaterhat - name = "boater hat" - desc = "The ultimate in summer fashion." - icon_state = "boater_hat" - item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") - body_parts_covered = 0 - -/obj/item/clothing/head/fedora - name = "fedora" - icon_state = "fedora_grey" - desc = "A sharp, stylish hat that's grey in color." - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - body_parts_covered = 0 - -/obj/item/clothing/head/fedora/brown - desc = "A brown fedora. Perfect for detectives or those trying to pilfer artifacts." - icon_state = "fedora_brown" - allowed = list(/obj/item/weapon/reagent_containers/food/snacks/candy_corn, /obj/item/weapon/pen) - -/obj/item/clothing/head/fedora/white - desc = "A white fedora, really cool hat if you're a mobster. A really lame hat if you're not." - icon_state = "fedora_white" - -/obj/item/clothing/head/fedora/beige - desc = "A beige fedora. Either the cornerstone of a reporter's style or a poor attempt at looking cool. Depends on the person wearing it." - icon_state = "fedora_beige" - -/obj/item/clothing/head/fedora/panama - desc = "A fancy, cream colored fedora. Columbian pure." - icon_state = "fedora_panama" - -/obj/item/clothing/head/trilby - name = "trilby" - icon_state = "trilby" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - desc = "M'lady" - -/obj/item/clothing/head/trilby/feather - name = "feather trilby" - icon_state = "feather_trilby" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - desc = "A sharp, stylish hat with a feather." - -/obj/item/clothing/head/fez - name = "fez" - icon_state = "fez" - desc = "You should wear a fez. Fezzes are cool." - -//Cowboy Hats - -/obj/item/clothing/head/cowboy - name = "cowboy hat" - desc = "For those that have spurs that go jingle jangle jingle." - icon_state = "cowboy_1" - body_parts_covered = 0 - -/obj/item/clothing/head/cowboy/rattan - name = "rattan cowboy hat" - desc = "Made from the same straw harvested from the fields." - icon_state = "cowboy_2" - -/obj/item/clothing/head/cowboy/dark - name = "dark cowboy hat" - desc = "Protect yer head in this new frontier." - icon_state = "cowboy_3" - -/obj/item/clothing/head/cowboy/ranger - name = "ranger cowboy hat" - desc = "Feel the western vibe from this good ol' classic." - icon_state = "cowboy_4" - -/obj/item/clothing/head/cowboy/rustler - name = "rustler cowboy hat" - desc = "Rustle up some of that there cattle bucko." - icon_state = "cowboy_5" - -/obj/item/clothing/head/cowboy/black - name = "black cowboy hat" - desc = "Perfect for the budding tram robber." - icon_state = "cowboy_7" - -/obj/item/clothing/head/cowboy/fancy - name = "fancy cowboy hat" - desc = "Premium black leather had with a rattlesnake hatband to top the ensemble." - icon_state = "cowboy_8" - -/obj/item/clothing/head/cowboy/wide - name = "wide-brimmed cowboy hat" - desc = "Because justice isn't going to dispense itself." - icon_state = "cowboy_6" - -/obj/item/clothing/head/cowboy/bandit - name = "bandit cowboy hat" - desc = "You can almost hear the old western music." - icon_state = "cowboy_9" - -/obj/item/clothing/head/cowboy/small - name = "small cowboy hat" - desc = "For the tiniest of cowboys." - icon_state = "cowboy_small" - -/obj/item/clothing/head/wheat - name = "straw hat" - desc = "It's a hat made from synthetic straw. Brought to you by \"Country Girls LLC.\" the choice brand for the galaxy's working class." - icon_state = "wheat" - -//Ruin Marine (Doom Marine) -/obj/item/clothing/head/marine - name = "marine helmet" - desc = "A marine helmet prop from the popular game 'Ruin'." - icon_state = "marine" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - -//Laser Tag Helmets -/obj/item/clothing/head/bluetag - name = "blue laser tag helmet" - desc = "Blue Pride, Station Wide." - icon_state = "bluetag" - flags_inv = HIDEEARS|BLOCKHEADHAIR - body_parts_covered = HEAD|EYES - -/obj/item/clothing/head/redtag - name = "red laser tag helmet" - desc = "Reputed to go faster." - icon_state = "redtag" - flags_inv = HIDEEARS|BLOCKHEADHAIR - body_parts_covered = HEAD|EYES +/obj/item/clothing/head/centhat + name = "\improper CentCom. hat" + icon_state = "centcom" + desc = "It's good to be emperor." + siemens_coefficient = 0.9 + body_parts_covered = 0 + +/obj/item/clothing/head/centhat/customs + name = "Customs Hat" + desc = "A formal hat for SolGov Customs Officers." + icon_state = "customshat" + +/obj/item/clothing/head/halo + name = "halo" + desc = "a small metal ring, floating above it's wearer." + icon_state = "halo" + +/obj/item/clothing/head/headband/maid/modern + name = "modern maid headband" + desc = "Just like from my Japanese cartoons!" + icon_state = "maid_headband" + +/obj/item/clothing/head/pin + icon_state = "pin" + addblends = "pin_a" + name = "hair pin" + desc = "A nice hair pin." + slot_flags = SLOT_HEAD | SLOT_EARS + body_parts_covered = 0 + drop_sound = 'sound/items/drop/accessory.ogg' + pickup_sound = 'sound/items/pickup/accessory.ogg' + +/obj/item/clothing/head/pin/pink + icon_state = "pinkpin" + addblends = null + name = "pink hair hat" + +/obj/item/clothing/head/pin/clover + icon_state = "cloverpin" + name = "clover pin" + addblends = null + desc = "A hair pin in the shape of a clover leaf." + +/obj/item/clothing/head/pin/butterfly + icon_state = "butterflypin" + name = "butterfly pin" + addblends = null + desc = "A hair pin in the shape of a bright blue butterfly." + +/obj/item/clothing/head/pin/magnetic + icon_state = "magnetpin" + name = "magnetic 'pin'" + addblends = null + desc = "Finally, a hair pin even a Morpheus chassis can use." + matter = list(MAT_STEEL = 10) + +/obj/item/clothing/head/pin/flower + name = "red flower pin" + icon_state = "hairflower" + addblends = null + desc = "Smells nice." + +/obj/item/clothing/head/pin/flower/blue + icon_state = "hairflower_blue" + name = "blue flower pin" + +/obj/item/clothing/head/pin/flower/pink + icon_state = "hairflower_pink" + name = "pink flower pin" + +/obj/item/clothing/head/pin/flower/yellow + icon_state = "hairflower_yellow" + name = "yellow flower pin" + +/obj/item/clothing/head/pin/flower/violet + icon_state = "hairflower_violet" + name = "violet flower pin" + +/obj/item/clothing/head/pin/flower/orange + icon_state = "hairflower_orange" + name = "orange flower pin" + +/obj/item/clothing/head/pin/flower/white + icon_state = "hairflower_white" + addblends = "hairflower_white_a" + name = "flower pin" + +/obj/item/clothing/head/pin/bow + icon_state = "bow" + addblends = "bow_a" + name = "hair bow" + desc = "A ribbon tied into a bow with a clip on the back to attach to hair." + item_state_slots = list(slot_r_hand_str = "pill", slot_l_hand_str = "pill") + +/obj/item/clothing/head/pin/bow/big + icon_state = "whiteribbon" + name = "ribbon" + +/obj/item/clothing/head/pin/bow/big/red + icon_state = "redribbon" + name = "red ribbon" + addblends = null + +/obj/item/clothing/head/powdered_wig + name = "powdered wig" + desc = "A powdered wig." + icon_state = "pwig" + +/obj/item/clothing/head/redcoat + name = "redcoat's hat" + icon_state = "redcoat" + item_state_slots = list(slot_r_hand_str = "pirate", slot_l_hand_str = "pirate") + desc = "'I guess it's a redhead.'" + body_parts_covered = 0 + +/obj/item/clothing/head/mailman + name = "station cap" + icon_state = "mailman" + item_state_slots = list(slot_r_hand_str = "hopcap", slot_l_hand_str = "hopcap") + desc = "Choo-choo!" + body_parts_covered = 0 + +/obj/item/clothing/head/plaguedoctorhat + name = "plague doctor's hat" + desc = "These were once used by Plague doctors, allegedly. They're pretty much useless." + icon_state = "plaguedoctor" + item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") + permeability_coefficient = 0.01 + siemens_coefficient = 0.9 + body_parts_covered = 0 + +/obj/item/clothing/head/plaguedoctorhat/gold + name = "golden plague doctor's hat" + desc = "These were once used by plague doctors, allegedly. This one has gold accents." + icon_state = "plaguedoctor2" + +/obj/item/clothing/head/hasturhood + name = "hastur's hood" + desc = "It's unspeakably stylish" + icon_state = "hasturhood" + item_state_slots = list(slot_r_hand_str = "enginering_beret", slot_l_hand_str = "enginering_beret") + flags_inv = BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/nursehat + name = "nurse's hat" + desc = "It allows quick identification of trained medical personnel." + icon_state = "nursehat" + siemens_coefficient = 0.9 + body_parts_covered = 0 + +/obj/item/clothing/head/syndicatefake + name = "red space-helmet replica" + item_state_slots = list(slot_r_hand_str = "syndicate-helm-black-red", slot_l_hand_str = "syndicate-helm-black-red") + icon_state = "syndicate" + desc = "A plastic replica of a bloodthirsty mercenary's space helmet, you'll look just like a real murderous criminal operative in this! This is a toy, it is not made for use in space!" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR + siemens_coefficient = 2.0 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/cueball + name = "cueball helmet" + desc = "A large, featureless white orb mean to be worn on your head. How do you even see out of this thing?" + icon_state = "cueball" + flags_inv = BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/greenbandana + name = "green bandana" + desc = "It's a green bandana with some fine nanotech lining." + icon_state = "greenbandana" + flags_inv = 0 + body_parts_covered = 0 + +/obj/item/clothing/head/cardborg + name = "cardborg helmet" + desc = "A helmet made out of a box." + icon_state = "cardborg_h" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + body_parts_covered = HEAD|FACE|EYES + drop_sound = 'sound/items/drop/cardboardbox.ogg' + pickup_sound = 'sound/items/pickup/cardboardbox.ogg' + +/obj/item/clothing/head/justice + name = "justice hat" + desc = "fight for what's righteous!" + icon_state = "justicered" //Does this even exist? + flags_inv = BLOCKHAIR + body_parts_covered = HEAD|EYES + +/obj/item/clothing/head/justice/blue + icon_state = "justiceblue" + +/obj/item/clothing/head/justice/yellow + icon_state = "justiceyellow" + +/obj/item/clothing/head/justice/green + icon_state = "justicegreen" + +/obj/item/clothing/head/justice/pink + icon_state = "justicepink" + +/obj/item/clothing/head/rabbitears + name = "rabbit ears" + desc = "Wearing these makes you looks useless, and only good for your sex appeal." + icon_state = "bunny" + body_parts_covered = 0 + +/obj/item/clothing/head/flatcap + name = "flat cap" + desc = "A working man's cap." + icon_state = "flat_cap" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + siemens_coefficient = 0.9 //...what? + +/obj/item/clothing/head/flatcap/grey + icon_state = "flat_capw" + addblends = "flat_capw_a" + item_state_slots = list(slot_r_hand_str = "greysoft", slot_l_hand_str = "greysoft") + +/obj/item/clothing/head/pirate + name = "pirate hat" + desc = "Yarr." + icon_state = "pirate" + body_parts_covered = 0 + +/obj/item/clothing/head/hgpiratecap + name = "pirate hat" + desc = "Yarr." + icon_state = "hgpiratecap" + item_state_slots = list(slot_r_hand_str = "hoscap", slot_l_hand_str = "hoscap") + body_parts_covered = 0 + +/obj/item/clothing/head/bandana + name = "pirate bandana" + desc = "Yarr." + icon_state = "bandana" + item_state_slots = list(slot_r_hand_str = "redbandana", slot_l_hand_str = "redbandana") + +/obj/item/clothing/head/witchwig + name = "witch costume wig" + desc = "Eeeee~heheheheheheh!" + icon_state = "witch" + flags_inv = BLOCKHAIR + siemens_coefficient = 2.0 + +/obj/item/clothing/head/chicken + name = "chicken suit head" + desc = "Bkaw!" + icon_state = "chickenhead" + flags_inv = BLOCKHAIR + siemens_coefficient = 0.7 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/bearpelt + name = "bear pelt hat" + desc = "Fuzzy." + icon_state = "bearpelt" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + flags_inv = BLOCKHAIR + siemens_coefficient = 0.7 + +/obj/item/clothing/head/xenos + name = "xenos helmet" + icon_state = "xenos" + item_state_slots = list(slot_r_hand_str = "xenos_helm", slot_l_hand_str = "xenos_helm") + desc = "A helmet made out of chitinous alien hide." + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR + siemens_coefficient = 2.0 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/philosopher_wig + name = "natural philosopher's wig" + desc = "A stylish monstrosity unearthed from Earth's Renaissance period. With this most distinguish'd wig, you'll be ready for your next soiree!" + icon_state = "philosopher_wig" + item_state_slots = list(slot_r_hand_str = "pwig", slot_l_hand_str = "pwig") + flags_inv = BLOCKHAIR + siemens_coefficient = 2.0 //why is it so conductive?! + body_parts_covered = 0 + +/obj/item/clothing/head/orangebandana //themij: Taryn Kifer + name = "orange bandana" + desc = "An orange piece of cloth, worn on the head." + icon_state = "orange_bandana" + body_parts_covered = 0 + +/obj/item/clothing/head/hijab + name = "hijab" + desc = "A veil that is wrapped to cover the head and chest" + icon_state = "hijab" + addblends = "hijab_a" + item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") + body_parts_covered = 0 + flags_inv = BLOCKHAIR + +/obj/item/clothing/head/kippa + name = "kippa" + desc = "A small, brimless cap." + icon_state = "kippa" + addblends = "kippa_a" + body_parts_covered = 0 + +/obj/item/clothing/head/turban + name = "turban" + desc = "A cloth used to wind around the head" + icon_state = "turban" + addblends = "turban_a" + item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") + body_parts_covered = 0 + flags_inv = BLOCKHEADHAIR + +/obj/item/clothing/head/taqiyah + name = "taqiyah" + desc = "A short, rounded skullcap usually worn for religious purposes." + icon_state = "taqiyah" + addblends = "taqiyah_a" + item_state_slots = list(slot_r_hand_str = "taq", slot_l_hand_str = "taq") + +/obj/item/clothing/head/beanie + name = "beanie" + desc = "A head-hugging brimless winter cap. This one is tight." + icon_state = "beanie" + addblends = "beanie_a" + body_parts_covered = 0 + +/obj/item/clothing/head/beanie_loose + name = "loose beanie" + desc = "A head-hugging brimless winter cap. This one is loose." + icon_state = "beanie_hang" + addblends = "beanie_hang_a" + body_parts_covered = 0 + +/obj/item/clothing/head/beretg + name = "beret" + desc = "A beret, an artists favorite headwear." + icon_state = "beret_g" + addblends = "beret_g_a" + body_parts_covered = 0 + +/obj/item/clothing/head/sombrero + name = "sombrero" + desc = "A wide-brimmed hat popularly worn in Mexico." + icon_state = "sombrero" + body_parts_covered = 0 + +/obj/item/clothing/head/headband/maid + name = "maid headband" + desc = "Keeps hair out of the way for important... jobs." + icon_state = "maid" + body_parts_covered = 0 + +/obj/item/clothing/head/maangtikka + name = "maang tikka" + desc = "A jeweled headpiece originating in India." + icon_state = "maangtikka" + body_parts_covered = 0 + drop_sound = 'sound/items/drop/ring.ogg' + pickup_sound = 'sound/items/pickup/ring.ogg' + +/obj/item/clothing/head/jingasa + name = "jingasa" + desc = "A wide, flat rain hat originally from Japan." + icon_state = "jingasa" + body_parts_covered = 0 + item_state_slots = list(slot_r_hand_str = "taq", slot_l_hand_str = "taq") + +/obj/item/clothing/head/cowl + name = "black cowl" + desc = "A gold-lined black cowl. It gives off uncomfortable cult vibes, but fancy." + icon_state = "cowl" + body_parts_covered = 0 + +/obj/item/clothing/head/cowl + name = "white cowl" + desc = "A gold-lined white cowl. It gives off uncomfortable cult vibes, but fancy." + icon_state = "whitecowl" + body_parts_covered = 0 + +/obj/item/clothing/head/blackngoldheaddress + name = "black and gold headdress" + desc = "An odd looking headdress that covers the eyes." + icon_state = "blackngoldheaddress" + flags_inv = HIDEEYES + body_parts_covered = HEAD|EYES + +//Corporate Berets + +/obj/item/clothing/head/beret/corp/saare + name = "\improper SAARE beret" + desc = "A red beret denoting service with Stealth Assault Enterprises. For mercenaries that are more inclined towards style than safety." + icon_state = "beret_red" + +/obj/item/clothing/head/beret/corp/saare/officer + name = "\improper SAARE officer beret" + desc = "A red beret with a gold insignia, denoting senior service with Stealth Assault Enterprises. For mercenaries who are more inclined towards style than safety." + icon_state = "beret_redgold" + +/obj/item/clothing/head/beret/corp/pcrc + name = "\improper PCRC beret" + desc = "A black beret with a PCRC logo insignia, denoting service with Proxima Centauri Risk Control. For private security personnel that are more inclined towards style than safety." + icon_state = "beret_black_observatory" + + +/obj/item/clothing/head/beret/corp/hedberg + name = "\improper Hedberg-Hammarstrom beret" + desc = "A tan beret denoting service with Hedberg-Hammarstrom private security. For mercenaries who are more inclined towards style than safety." + icon_state = "beret_tan" + +/obj/item/clothing/head/beret/corp/xion + name = "\improper Xion beret" + desc = "An orange beret denoting employment with Xion Manufacturing. For personnel that are more inclined towards style than safety." + icon_state = "beret_orange" + +//Stylish Hats + +/obj/item/clothing/head/bowler + name = "bowler hat" + desc = "Gentleman, elite aboard!" + icon_state = "bowler" + item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") + body_parts_covered = 0 + +/obj/item/clothing/head/that + name = "top-hat" + desc = "It's an amish looking hat." + icon_state = "tophat" + siemens_coefficient = 0.9 + body_parts_covered = 0 + +/obj/item/clothing/head/beaverhat + name = "beaver hat" + desc = "Soft felt makes this hat both comfortable and elegant." + icon_state = "beaver_hat" + item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") + siemens_coefficient = 0.9 + body_parts_covered = 0 + +/obj/item/clothing/head/boaterhat + name = "boater hat" + desc = "The ultimate in summer fashion." + icon_state = "boater_hat" + item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") + body_parts_covered = 0 + +/obj/item/clothing/head/fedora + name = "fedora" + icon_state = "fedora_grey" + desc = "A sharp, stylish hat that's grey in color." + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + body_parts_covered = 0 + +/obj/item/clothing/head/fedora/brown + desc = "A brown fedora. Perfect for detectives or those trying to pilfer artifacts." + icon_state = "fedora_brown" + allowed = list(/obj/item/weapon/reagent_containers/food/snacks/candy_corn, /obj/item/weapon/pen) + +/obj/item/clothing/head/fedora/white + desc = "A white fedora, really cool hat if you're a mobster. A really lame hat if you're not." + icon_state = "fedora_white" + +/obj/item/clothing/head/fedora/beige + desc = "A beige fedora. Either the cornerstone of a reporter's style or a poor attempt at looking cool. Depends on the person wearing it." + icon_state = "fedora_beige" + +/obj/item/clothing/head/fedora/panama + desc = "A fancy, cream colored fedora. Columbian pure." + icon_state = "fedora_panama" + +/obj/item/clothing/head/trilby + name = "trilby" + icon_state = "trilby" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + desc = "M'lady" + +/obj/item/clothing/head/trilby/feather + name = "feather trilby" + icon_state = "feather_trilby" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + desc = "A sharp, stylish hat with a feather." + +/obj/item/clothing/head/fez + name = "fez" + icon_state = "fez" + desc = "You should wear a fez. Fezzes are cool." + +//Cowboy Hats + +/obj/item/clothing/head/cowboy + name = "cowboy hat" + desc = "For those that have spurs that go jingle jangle jingle." + icon_state = "cowboy_1" + body_parts_covered = 0 + +/obj/item/clothing/head/cowboy/rattan + name = "rattan cowboy hat" + desc = "Made from the same straw harvested from the fields." + icon_state = "cowboy_2" + +/obj/item/clothing/head/cowboy/dark + name = "dark cowboy hat" + desc = "Protect yer head in this new frontier." + icon_state = "cowboy_3" + +/obj/item/clothing/head/cowboy/ranger + name = "ranger cowboy hat" + desc = "Feel the western vibe from this good ol' classic." + icon_state = "cowboy_4" + +/obj/item/clothing/head/cowboy/rustler + name = "rustler cowboy hat" + desc = "Rustle up some of that there cattle bucko." + icon_state = "cowboy_5" + +/obj/item/clothing/head/cowboy/black + name = "black cowboy hat" + desc = "Perfect for the budding tram robber." + icon_state = "cowboy_7" + +/obj/item/clothing/head/cowboy/fancy + name = "fancy cowboy hat" + desc = "Premium black leather had with a rattlesnake hatband to top the ensemble." + icon_state = "cowboy_8" + +/obj/item/clothing/head/cowboy/wide + name = "wide-brimmed cowboy hat" + desc = "Because justice isn't going to dispense itself." + icon_state = "cowboy_6" + +/obj/item/clothing/head/cowboy/bandit + name = "bandit cowboy hat" + desc = "You can almost hear the old western music." + icon_state = "cowboy_9" + +/obj/item/clothing/head/cowboy/small + name = "small cowboy hat" + desc = "For the tiniest of cowboys." + icon_state = "cowboy_small" + +/obj/item/clothing/head/wheat + name = "straw hat" + desc = "It's a hat made from synthetic straw. Brought to you by \"Country Girls LLC.\" the choice brand for the galaxy's working class." + icon_state = "wheat" + +//Ruin Marine (Doom Marine) +/obj/item/clothing/head/marine + name = "marine helmet" + desc = "A marine helmet prop from the popular game 'Ruin'." + icon_state = "marine" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + +//Laser Tag Helmets +/obj/item/clothing/head/bluetag + name = "blue laser tag helmet" + desc = "Blue Pride, Station Wide." + icon_state = "bluetag" + flags_inv = HIDEEARS|BLOCKHEADHAIR + body_parts_covered = HEAD|EYES + +/obj/item/clothing/head/redtag + name = "red laser tag helmet" + desc = "Reputed to go faster." + icon_state = "redtag" + flags_inv = HIDEEARS|BLOCKHEADHAIR + body_parts_covered = HEAD|EYES diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index c2509fbb88d..42cebdf0f08 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -1,338 +1,338 @@ -/* - * Contents: - * Welding mask - * Cakehat - * Ushanka - * Pumpkin head - * Kitty ears - * Holiday hats - * Crown of Wrath - * Warning cone - */ - -/* - * Welding mask - */ -/obj/item/clothing/head/welding - name = "welding helmet" - desc = "A head-mounted face cover designed to protect the wearer completely from space-arc eye." - icon_state = "welding" - item_state_slots = list(slot_r_hand_str = "welding", slot_l_hand_str = "welding") - matter = list(MAT_STEEL = 3000, MAT_GLASS = 1000) - var/up = 0 - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - flags_inv = (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) - body_parts_covered = HEAD|FACE|EYES - action_button_name = "Flip Welding Mask" - siemens_coefficient = 0.9 - w_class = ITEMSIZE_NORMAL - var/base_state - flash_protection = FLASH_PROTECTION_MAJOR - tint = TINT_HEAVY - drop_sound = 'sound/items/drop/helm.ogg' - pickup_sound = 'sound/items/pickup/helm.ogg' - -/obj/item/clothing/head/welding/attack_self() - toggle() - - -/obj/item/clothing/head/welding/verb/toggle() - set category = "Object" - set name = "Adjust welding mask" - set src in usr - - if(!base_state) - base_state = icon_state - - if(usr.canmove && !usr.stat && !usr.restrained()) - if(src.up) - src.up = !src.up - body_parts_covered |= (EYES|FACE) - flags_inv |= (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) - icon_state = base_state - flash_protection = FLASH_PROTECTION_MAJOR - tint = initial(tint) - to_chat(usr, "You flip the [src] down to protect your eyes.") - else - src.up = !src.up - body_parts_covered &= ~(EYES|FACE) - flags_inv &= ~(HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) - icon_state = "[base_state]up" - flash_protection = FLASH_PROTECTION_NONE - tint = TINT_NONE - to_chat(usr, "You push the [src] up out of your face.") - update_clothing_icon() //so our mob-overlays - if (ismob(src.loc)) //should allow masks to update when it is opened/closed - var/mob/M = src.loc - M.update_inv_wear_mask() - usr.update_action_buttons() - -/obj/item/clothing/head/welding/demon - name = "demonic welding helmet" - desc = "A painted welding helmet, this one has a demonic face on it." - icon_state = "demonwelding" - item_state_slots = list( - slot_l_hand_str = "demonwelding", - slot_r_hand_str = "demonwelding", - ) - -/obj/item/clothing/head/welding/knight - name = "knightly welding helmet" - desc = "A painted welding helmet, this one looks like a knights helmet." - icon_state = "knightwelding" - item_state_slots = list( - slot_l_hand_str = "knightwelding", - slot_r_hand_str = "knightwelding", - ) - -/obj/item/clothing/head/welding/fancy - name = "fancy welding helmet" - desc = "A painted welding helmet, the black and gold make this one look very fancy." - icon_state = "fancywelding" - item_state_slots = list( - slot_l_hand_str = "fancywelding", - slot_r_hand_str = "fancywelding", - ) - -/obj/item/clothing/head/welding/engie - name = "engineering welding helmet" - desc = "A painted welding helmet, this one has been painted the engineering colours." - icon_state = "engiewelding" - item_state_slots = list( - slot_l_hand_str = "engiewelding", - slot_r_hand_str = "engiewelding", - ) - -//Replikant Welding mask - -/obj/item/clothing/head/welding/arar - name = "replikant welding helmet" - desc = "A protective welding mask designed for repair-technician biosynthetic crew, the visor slits are particularly difficult to see out of." - icon = 'icons/inventory/head/item_vr.dmi' - icon_override = 'icons/inventory/head/mob_vr.dmi' - icon_state = "ararwelding" - item_state_slots = list( - SLOT_ID_LEFT_HAND = "ararwelding", - SLOT_ID_RIGHT_HAND = "ararwelding", - ) - - - - -/* - * Cakehat - */ -/obj/item/clothing/head/cakehat - name = "cake-hat" - desc = "It's tasty looking!" - icon_state = "cake0" - var/onfire = 0 - body_parts_covered = HEAD - -/obj/item/clothing/head/cakehat/process() - if(!onfire) - STOP_PROCESSING(SSobj, src) - return - - var/turf/location = src.loc - if(istype(location, /mob/)) - var/mob/living/carbon/human/M = location - if(M.item_is_in_hands(src) || M.head == src) - location = M.loc - - if (istype(location, /turf)) - location.hotspot_expose(700, 1) - -/obj/item/clothing/head/cakehat/attack_self(mob/user as mob) - onfire = !(onfire) - if (onfire) - force = 3 - damtype = "fire" - icon_state = "cake1" - START_PROCESSING(SSobj, src) - else - force = null - damtype = "brute" - icon_state = "cake0" - return - - -/* - * Ushanka - */ -/obj/item/clothing/head/ushanka - name = "ushanka" - desc = "Perfect for those cold winter nights." - icon_state = "ushankadown" - flags_inv = HIDEEARS - -/obj/item/clothing/head/ushanka/attack_self(mob/user as mob) - if(src.icon_state == initial(icon_state)) - src.icon_state = "[icon_state]up" - to_chat(user, "You raise the ear flaps on the ushanka.") - else - src.icon_state = initial(icon_state) - to_chat(user, "You lower the ear flaps on the ushanka.") - -/obj/item/clothing/head/ushanka/black - icon_state = "blkushankadown" - -/obj/item/clothing/head/ushanka/soviet - name = "soviet ushanka" - desc = "Perfect for winter in Siberia, da?" - icon_state = "sovushankadown" - -/obj/item/clothing/head/ushanka/hedberg - name = "\improper Hedberg-Hammarstrom fur hat" - desc = "An Hedberg-Hammarstrom private security ushanka." - icon_state = "hedbergushankadown" - -/* - * Pumpkin head - */ -/obj/item/clothing/head/pumpkinhead - name = "carved pumpkin" - desc = "A jack o' lantern! Believed to ward off evil spirits." - icon_state = "hardhat0_pumpkin"//Could stand to be renamed - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - light_range = 2 - light_overlay = "jackolantern" - w_class = ITEMSIZE_NORMAL - drop_sound = 'sound/items/drop/herb.ogg' - pickup_sound = 'sound/items/pickup/herb.ogg' - -/* - * Kitty ears - */ -/obj/item/clothing/head/kitty - name = "kitty ears" - desc = "A pair of kitty ears. Meow!" - icon_state = "kitty" - body_parts_covered = 0 - siemens_coefficient = 1.5 - item_icons = null - -/obj/item/clothing/head/kitty/update_icon(var/mob/living/carbon/human/user) - if(!istype(user)) return - var/icon/ears = new/icon("icon" = 'icons/inventory/head/mob.dmi', "icon_state" = "kitty") - ears.Blend(rgb(user.r_hair, user.g_hair, user.b_hair), ICON_ADD) - - var/icon/earbit = new/icon("icon" = 'icons/inventory/head/mob.dmi', "icon_state" = "kittyinner") - ears.Blend(earbit, ICON_OVERLAY) - -/obj/item/clothing/head/richard - name = "chicken mask" - desc = "You can hear the distant sounds of rhythmic electronica." - icon_state = "richard" - item_state_slots = list(slot_r_hand_str = "chickenhead", slot_l_hand_str = "chickenhead") - body_parts_covered = HEAD|FACE - flags_inv = BLOCKHAIR - -/obj/item/clothing/head/santa - name = "santa hat" - desc = "It's a festive christmas hat, in red!" - icon_state = "santahatnorm" - item_state_slots = list(slot_r_hand_str = "santahat", slot_l_hand_str = "santahat") - body_parts_covered = 0 - -/obj/item/clothing/head/santa/green - name = "green santa hat" - desc = "It's a festive christmas hat, in green!" - icon_state = "santahatgreen" - item_state_slots = list(slot_r_hand_str = "santahatgreen", slot_l_hand_str = "santahatgreen") - body_parts_covered = 0 - -/* - * Xenoarch/Surface Loot Hats - */ - -// Triggers an effect when the wearer is 'in grave danger'. -// Causes brainloss when it happens. -/obj/item/clothing/head/psy_crown - name = "broken crown" - desc = "A crown-of-thorns with a missing gem." - var/tension_threshold = 125 - var/cooldown = null // world.time of when this was last triggered. - var/cooldown_duration = 3 MINUTES // How long the cooldown should be. - var/flavor_equip = null // Message displayed to someone who puts this on their head. Drones don't get a message. - var/flavor_unequip = null // Ditto, but for taking it off. - var/flavor_drop = null // Ditto, but for dropping it. - var/flavor_activate = null // Ditto, for but activating. - var/brainloss_cost = 3 // Whenever it activates, inflict this much brainloss on the wearer, as its not good for the mind to wear things that manipulate it. - -/obj/item/clothing/head/psy_crown/proc/activate_ability(var/mob/living/wearer) - cooldown = world.time + cooldown_duration - to_chat(wearer, flavor_activate) - to_chat(wearer, "The inside of your head hurts...") - wearer.adjustBrainLoss(brainloss_cost) - -/obj/item/clothing/head/psy_crown/equipped(var/mob/living/carbon/human/H) - ..() - if(istype(H) && H.head == src && H.is_sentient()) - START_PROCESSING(SSobj, src) - to_chat(H, flavor_equip) - -/obj/item/clothing/head/psy_crown/dropped(var/mob/living/carbon/human/H) - ..() - STOP_PROCESSING(SSobj, src) - if(H.is_sentient()) - if(loc == H) // Still inhand. - to_chat(H, flavor_unequip) - else - to_chat(H, flavor_drop) - -/obj/item/clothing/head/psy_crown/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/clothing/head/psy_crown/process() - if(isliving(loc)) - var/mob/living/L = loc - if(world.time >= cooldown && L.is_sentient() && L.get_tension() >= tension_threshold) - activate_ability(L) - - -/obj/item/clothing/head/psy_crown/wrath - name = "red crown" - desc = "A crown-of-thorns set with a red gemstone that seems to glow unnaturally. It feels rather disturbing to touch." - description_info = "This has a chance to cause the wearer to become extremely angry when in extreme danger." - icon_state = "wrathcrown" - flavor_equip = "You feel a bit angrier after putting on this crown." - flavor_unequip = "You feel calmer after removing the crown." - flavor_drop = "You feel much calmer after letting go of the crown." - flavor_activate = "An otherworldly feeling seems to enter your mind, and it ignites your mind in fury!" - -/obj/item/clothing/head/psy_crown/wrath/activate_ability(var/mob/living/wearer) - ..() - wearer.add_modifier(/datum/modifier/berserk, 30 SECONDS) - -/obj/item/clothing/head/psy_crown/gluttony - name = "green crown" - desc = "A crown-of-thorns set with a green gemstone that seems to glow unnaturally. It feels rather disturbing to touch." - description_info = "This has a chance to cause the wearer to become extremely durable, but hungry when in extreme danger." - icon_state = "gluttonycrown" - flavor_equip = "You feel a bit hungrier after putting on this crown." - flavor_unequip = "You feel sated after removing the crown." - flavor_drop = "You feel much more sated after letting go of the crown." - flavor_activate = "An otherworldly feeling seems to enter your mind, and it drives your mind into gluttony!" - -/obj/item/clothing/head/psy_crown/gluttony/activate_ability(var/mob/living/wearer) - ..() - wearer.add_modifier(/datum/modifier/gluttonyregeneration, 45 SECONDS) - -/obj/item/clothing/head/cone - name = "warning cone" - desc = "This cone is trying to warn you of something!" - description_info = "It looks like you can wear it in your head slot." - icon_state = "cone" - item_state = "cone" - drop_sound = 'sound/items/drop/shoes.ogg' - force = 1 - throwforce = 3 - throw_speed = 2 - throw_range = 5 - w_class = 2 - body_parts_covered = HEAD - attack_verb = list("warned", "cautioned", "smashed") - armor = list("melee" = 5, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) +/* + * Contents: + * Welding mask + * Cakehat + * Ushanka + * Pumpkin head + * Kitty ears + * Holiday hats + * Crown of Wrath + * Warning cone + */ + +/* + * Welding mask + */ +/obj/item/clothing/head/welding + name = "welding helmet" + desc = "A head-mounted face cover designed to protect the wearer completely from space-arc eye." + icon_state = "welding" + item_state_slots = list(slot_r_hand_str = "welding", slot_l_hand_str = "welding") + matter = list(MAT_STEEL = 3000, MAT_GLASS = 1000) + var/up = 0 + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + flags_inv = (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) + body_parts_covered = HEAD|FACE|EYES + action_button_name = "Flip Welding Mask" + siemens_coefficient = 0.9 + w_class = ITEMSIZE_NORMAL + var/base_state + flash_protection = FLASH_PROTECTION_MAJOR + tint = TINT_HEAVY + drop_sound = 'sound/items/drop/helm.ogg' + pickup_sound = 'sound/items/pickup/helm.ogg' + +/obj/item/clothing/head/welding/attack_self() + toggle() + + +/obj/item/clothing/head/welding/verb/toggle() + set category = "Object" + set name = "Adjust welding mask" + set src in usr + + if(!base_state) + base_state = icon_state + + if(usr.canmove && !usr.stat && !usr.restrained()) + if(src.up) + src.up = !src.up + body_parts_covered |= (EYES|FACE) + flags_inv |= (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) + icon_state = base_state + flash_protection = FLASH_PROTECTION_MAJOR + tint = initial(tint) + to_chat(usr, "You flip the [src] down to protect your eyes.") + else + src.up = !src.up + body_parts_covered &= ~(EYES|FACE) + flags_inv &= ~(HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) + icon_state = "[base_state]up" + flash_protection = FLASH_PROTECTION_NONE + tint = TINT_NONE + to_chat(usr, "You push the [src] up out of your face.") + update_clothing_icon() //so our mob-overlays + if (ismob(src.loc)) //should allow masks to update when it is opened/closed + var/mob/M = src.loc + M.update_inv_wear_mask() + usr.update_action_buttons() + +/obj/item/clothing/head/welding/demon + name = "demonic welding helmet" + desc = "A painted welding helmet, this one has a demonic face on it." + icon_state = "demonwelding" + item_state_slots = list( + slot_l_hand_str = "demonwelding", + slot_r_hand_str = "demonwelding", + ) + +/obj/item/clothing/head/welding/knight + name = "knightly welding helmet" + desc = "A painted welding helmet, this one looks like a knights helmet." + icon_state = "knightwelding" + item_state_slots = list( + slot_l_hand_str = "knightwelding", + slot_r_hand_str = "knightwelding", + ) + +/obj/item/clothing/head/welding/fancy + name = "fancy welding helmet" + desc = "A painted welding helmet, the black and gold make this one look very fancy." + icon_state = "fancywelding" + item_state_slots = list( + slot_l_hand_str = "fancywelding", + slot_r_hand_str = "fancywelding", + ) + +/obj/item/clothing/head/welding/engie + name = "engineering welding helmet" + desc = "A painted welding helmet, this one has been painted the engineering colours." + icon_state = "engiewelding" + item_state_slots = list( + slot_l_hand_str = "engiewelding", + slot_r_hand_str = "engiewelding", + ) + +//Replikant Welding mask + +/obj/item/clothing/head/welding/arar + name = "replikant welding helmet" + desc = "A protective welding mask designed for repair-technician biosynthetic crew, the visor slits are particularly difficult to see out of." + icon = 'icons/inventory/head/item_vr.dmi' + icon_override = 'icons/inventory/head/mob_vr.dmi' + icon_state = "ararwelding" + item_state_slots = list( + SLOT_ID_LEFT_HAND = "ararwelding", + SLOT_ID_RIGHT_HAND = "ararwelding", + ) + + + + +/* + * Cakehat + */ +/obj/item/clothing/head/cakehat + name = "cake-hat" + desc = "It's tasty looking!" + icon_state = "cake0" + var/onfire = 0 + body_parts_covered = HEAD + +/obj/item/clothing/head/cakehat/process() + if(!onfire) + STOP_PROCESSING(SSobj, src) + return + + var/turf/location = src.loc + if(istype(location, /mob/)) + var/mob/living/carbon/human/M = location + if(M.item_is_in_hands(src) || M.head == src) + location = M.loc + + if (istype(location, /turf)) + location.hotspot_expose(700, 1) + +/obj/item/clothing/head/cakehat/attack_self(mob/user as mob) + onfire = !(onfire) + if (onfire) + force = 3 + damtype = "fire" + icon_state = "cake1" + START_PROCESSING(SSobj, src) + else + force = null + damtype = "brute" + icon_state = "cake0" + return + + +/* + * Ushanka + */ +/obj/item/clothing/head/ushanka + name = "ushanka" + desc = "Perfect for those cold winter nights." + icon_state = "ushankadown" + flags_inv = HIDEEARS + +/obj/item/clothing/head/ushanka/attack_self(mob/user as mob) + if(src.icon_state == initial(icon_state)) + src.icon_state = "[icon_state]up" + to_chat(user, "You raise the ear flaps on the ushanka.") + else + src.icon_state = initial(icon_state) + to_chat(user, "You lower the ear flaps on the ushanka.") + +/obj/item/clothing/head/ushanka/black + icon_state = "blkushankadown" + +/obj/item/clothing/head/ushanka/soviet + name = "soviet ushanka" + desc = "Perfect for winter in Siberia, da?" + icon_state = "sovushankadown" + +/obj/item/clothing/head/ushanka/hedberg + name = "\improper Hedberg-Hammarstrom fur hat" + desc = "An Hedberg-Hammarstrom private security ushanka." + icon_state = "hedbergushankadown" + +/* + * Pumpkin head + */ +/obj/item/clothing/head/pumpkinhead + name = "carved pumpkin" + desc = "A jack o' lantern! Believed to ward off evil spirits." + icon_state = "hardhat0_pumpkin"//Could stand to be renamed + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + light_range = 2 + light_overlay = "jackolantern" + w_class = ITEMSIZE_NORMAL + drop_sound = 'sound/items/drop/herb.ogg' + pickup_sound = 'sound/items/pickup/herb.ogg' + +/* + * Kitty ears + */ +/obj/item/clothing/head/kitty + name = "kitty ears" + desc = "A pair of kitty ears. Meow!" + icon_state = "kitty" + body_parts_covered = 0 + siemens_coefficient = 1.5 + item_icons = null + +/obj/item/clothing/head/kitty/update_icon(var/mob/living/carbon/human/user) + if(!istype(user)) return + var/icon/ears = new/icon("icon" = 'icons/inventory/head/mob.dmi', "icon_state" = "kitty") + ears.Blend(rgb(user.r_hair, user.g_hair, user.b_hair), ICON_ADD) + + var/icon/earbit = new/icon("icon" = 'icons/inventory/head/mob.dmi', "icon_state" = "kittyinner") + ears.Blend(earbit, ICON_OVERLAY) + +/obj/item/clothing/head/richard + name = "chicken mask" + desc = "You can hear the distant sounds of rhythmic electronica." + icon_state = "richard" + item_state_slots = list(slot_r_hand_str = "chickenhead", slot_l_hand_str = "chickenhead") + body_parts_covered = HEAD|FACE + flags_inv = BLOCKHAIR + +/obj/item/clothing/head/santa + name = "santa hat" + desc = "It's a festive christmas hat, in red!" + icon_state = "santahatnorm" + item_state_slots = list(slot_r_hand_str = "santahat", slot_l_hand_str = "santahat") + body_parts_covered = 0 + +/obj/item/clothing/head/santa/green + name = "green santa hat" + desc = "It's a festive christmas hat, in green!" + icon_state = "santahatgreen" + item_state_slots = list(slot_r_hand_str = "santahatgreen", slot_l_hand_str = "santahatgreen") + body_parts_covered = 0 + +/* + * Xenoarch/Surface Loot Hats + */ + +// Triggers an effect when the wearer is 'in grave danger'. +// Causes brainloss when it happens. +/obj/item/clothing/head/psy_crown + name = "broken crown" + desc = "A crown-of-thorns with a missing gem." + var/tension_threshold = 125 + var/cooldown = null // world.time of when this was last triggered. + var/cooldown_duration = 3 MINUTES // How long the cooldown should be. + var/flavor_equip = null // Message displayed to someone who puts this on their head. Drones don't get a message. + var/flavor_unequip = null // Ditto, but for taking it off. + var/flavor_drop = null // Ditto, but for dropping it. + var/flavor_activate = null // Ditto, for but activating. + var/brainloss_cost = 3 // Whenever it activates, inflict this much brainloss on the wearer, as its not good for the mind to wear things that manipulate it. + +/obj/item/clothing/head/psy_crown/proc/activate_ability(var/mob/living/wearer) + cooldown = world.time + cooldown_duration + to_chat(wearer, flavor_activate) + to_chat(wearer, "The inside of your head hurts...") + wearer.adjustBrainLoss(brainloss_cost) + +/obj/item/clothing/head/psy_crown/equipped(var/mob/living/carbon/human/H) + ..() + if(istype(H) && H.head == src && H.is_sentient()) + START_PROCESSING(SSobj, src) + to_chat(H, flavor_equip) + +/obj/item/clothing/head/psy_crown/dropped(var/mob/living/carbon/human/H) + ..() + STOP_PROCESSING(SSobj, src) + if(H.is_sentient()) + if(loc == H) // Still inhand. + to_chat(H, flavor_unequip) + else + to_chat(H, flavor_drop) + +/obj/item/clothing/head/psy_crown/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/clothing/head/psy_crown/process() + if(isliving(loc)) + var/mob/living/L = loc + if(world.time >= cooldown && L.is_sentient() && L.get_tension() >= tension_threshold) + activate_ability(L) + + +/obj/item/clothing/head/psy_crown/wrath + name = "red crown" + desc = "A crown-of-thorns set with a red gemstone that seems to glow unnaturally. It feels rather disturbing to touch." + description_info = "This has a chance to cause the wearer to become extremely angry when in extreme danger." + icon_state = "wrathcrown" + flavor_equip = "You feel a bit angrier after putting on this crown." + flavor_unequip = "You feel calmer after removing the crown." + flavor_drop = "You feel much calmer after letting go of the crown." + flavor_activate = "An otherworldly feeling seems to enter your mind, and it ignites your mind in fury!" + +/obj/item/clothing/head/psy_crown/wrath/activate_ability(var/mob/living/wearer) + ..() + wearer.add_modifier(/datum/modifier/berserk, 30 SECONDS) + +/obj/item/clothing/head/psy_crown/gluttony + name = "green crown" + desc = "A crown-of-thorns set with a green gemstone that seems to glow unnaturally. It feels rather disturbing to touch." + description_info = "This has a chance to cause the wearer to become extremely durable, but hungry when in extreme danger." + icon_state = "gluttonycrown" + flavor_equip = "You feel a bit hungrier after putting on this crown." + flavor_unequip = "You feel sated after removing the crown." + flavor_drop = "You feel much more sated after letting go of the crown." + flavor_activate = "An otherworldly feeling seems to enter your mind, and it drives your mind into gluttony!" + +/obj/item/clothing/head/psy_crown/gluttony/activate_ability(var/mob/living/wearer) + ..() + wearer.add_modifier(/datum/modifier/gluttonyregeneration, 45 SECONDS) + +/obj/item/clothing/head/cone + name = "warning cone" + desc = "This cone is trying to warn you of something!" + description_info = "It looks like you can wear it in your head slot." + icon_state = "cone" + item_state = "cone" + drop_sound = 'sound/items/drop/shoes.ogg' + force = 1 + throwforce = 3 + throw_speed = 2 + throw_range = 5 + w_class = 2 + body_parts_covered = HEAD + attack_verb = list("warned", "cautioned", "smashed") + armor = list("melee" = 5, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm index c2c597ad7b2..1d1742bca9d 100644 --- a/code/modules/clothing/head/soft_caps.dm +++ b/code/modules/clothing/head/soft_caps.dm @@ -1,114 +1,114 @@ -/obj/item/clothing/head/soft - name = "cargo cap" - desc = "It's a peaked cap in a tasteless yellow color." - icon_state = "cargosoft" - item_state_slots = list(slot_r_hand_str = "cargosoft", slot_l_hand_str = "cargosoft") - var/flipped = 0 - siemens_coefficient = 0.9 - body_parts_covered = 0 - -/obj/item/clothing/head/soft/dropped() - icon_state = initial(icon_state) - flipped=0 - ..() - -/obj/item/clothing/head/soft/attack_self(mob/user) - flipped = !flipped - if(flipped) - icon_state = "[icon_state]_flipped" - to_chat(user, "You flip the hat backwards.") - else - icon_state = initial(icon_state) - to_chat(user, "You flip the hat back in normal position.") - update_clothing_icon() //so our mob-overlays update - -/obj/item/clothing/head/soft/red - name = "red cap" - desc = "It's a baseball hat in a tasteless red color." - icon_state = "redsoft" - item_state_slots = list(slot_r_hand_str = "redsoft", slot_l_hand_str = "redsoft") - -/obj/item/clothing/head/soft/blue - name = "blue cap" - desc = "It's a peaked cap in a tasteless blue color." - icon_state = "bluesoft" - item_state_slots = list(slot_r_hand_str = "bluesoft", slot_l_hand_str = "bluesoft") - -/obj/item/clothing/head/soft/green - name = "green cap" - desc = "It's a peaked cap in a tasteless green color." - icon_state = "greensoft" - item_state_slots = list(slot_r_hand_str = "greensoft", slot_l_hand_str = "greensoft") - -/obj/item/clothing/head/soft/yellow - name = "yellow cap" - desc = "It's a peaked cap in a tasteless yellow color." - icon_state = "yellowsoft" - item_state_slots = list(slot_r_hand_str = "yellowsoft", slot_l_hand_str = "yellowsoft") - -/obj/item/clothing/head/soft/grey - name = "grey cap" - desc = "It's a peaked cap in a tasteful grey color." - icon_state = "greysoft" - item_state_slots = list(slot_r_hand_str = "greysoft", slot_l_hand_str = "greysoft") - -/obj/item/clothing/head/soft/orange - name = "orange cap" - desc = "It's a peaked cap in a tasteless orange color." - icon_state = "orangesoft" - item_state_slots = list(slot_r_hand_str = "orangesoft", slot_l_hand_str = "orangesoft") - -/obj/item/clothing/head/soft/mime - name = "white cap" - desc = "It's a peaked cap in a tasteless white color." - icon_state = "mimesoft" - item_state_slots = list(slot_r_hand_str = "mimesoft", slot_l_hand_str = "mimesoft") - -/obj/item/clothing/head/soft/purple - name = "purple cap" - desc = "It's a peaked cap in a tasteless purple color." - icon_state = "purplesoft" - item_state_slots = list(slot_r_hand_str = "purplesoft", slot_l_hand_str = "purplesoft") - -/obj/item/clothing/head/soft/rainbow - name = "rainbow cap" - desc = "It's a peaked cap in a bright rainbow of colors." - icon_state = "rainbowsoft" - item_state_slots = list(slot_r_hand_str = "rainbowsoft", slot_l_hand_str = "rainbowsoft") - -/obj/item/clothing/head/soft/sec - name = "security cap" - desc = "It's a field cap in tasteful red color." - icon_state = "secsoft" - item_state_slots = list(slot_r_hand_str = "secsoft", slot_l_hand_str = "secsoft") - -/obj/item/clothing/head/soft/sec/corp - name = "corporate security cap" - desc = "It's field cap in corporate colors." - icon_state = "corpsoft" - item_state_slots = list(slot_r_hand_str = "corpsoft", slot_l_hand_str = "corpsoft") - -/obj/item/clothing/head/soft/black - name = "black cap" - desc = "It's a peaked cap in a tasteful black color." - icon_state = "blacksoft" - item_state_slots = list(slot_r_hand_str = "blacksoft", slot_l_hand_str = "blacksoft") - -/obj/item/clothing/head/soft/mbill - name = "shipping cap" - desc = "It's a ballcap bearing the colors of Major Bill's Shipping." - icon_state = "mbillsoft" - item_state_slots = list(slot_r_hand_str = "redsoft", slot_l_hand_str = "redsoft") - catalogue_data = list(/datum/category_item/catalogue/information/organization/major_bills) - -/obj/item/clothing/head/soft/med - name = "medical cap" - desc = "It's a field cap in white, with a blue cross on the front." - icon_state = "medsoft" - item_state_slots = list(slot_r_hand_str = "mimesoft", slot_l_hand_str = "mimesoft") - -/obj/item/clothing/head/soft/paramed - name = "paramedic's cap" - desc = "It's a field cap in dark blue, with a white cross on the front." - icon_state = "emtsoft" - item_state_slots = list(slot_r_hand_str = "bluesoft", slot_l_hand_str = "bluesoft") +/obj/item/clothing/head/soft + name = "cargo cap" + desc = "It's a peaked cap in a tasteless yellow color." + icon_state = "cargosoft" + item_state_slots = list(slot_r_hand_str = "cargosoft", slot_l_hand_str = "cargosoft") + var/flipped = 0 + siemens_coefficient = 0.9 + body_parts_covered = 0 + +/obj/item/clothing/head/soft/dropped() + icon_state = initial(icon_state) + flipped=0 + ..() + +/obj/item/clothing/head/soft/attack_self(mob/user) + flipped = !flipped + if(flipped) + icon_state = "[icon_state]_flipped" + to_chat(user, "You flip the hat backwards.") + else + icon_state = initial(icon_state) + to_chat(user, "You flip the hat back in normal position.") + update_clothing_icon() //so our mob-overlays update + +/obj/item/clothing/head/soft/red + name = "red cap" + desc = "It's a baseball hat in a tasteless red color." + icon_state = "redsoft" + item_state_slots = list(slot_r_hand_str = "redsoft", slot_l_hand_str = "redsoft") + +/obj/item/clothing/head/soft/blue + name = "blue cap" + desc = "It's a peaked cap in a tasteless blue color." + icon_state = "bluesoft" + item_state_slots = list(slot_r_hand_str = "bluesoft", slot_l_hand_str = "bluesoft") + +/obj/item/clothing/head/soft/green + name = "green cap" + desc = "It's a peaked cap in a tasteless green color." + icon_state = "greensoft" + item_state_slots = list(slot_r_hand_str = "greensoft", slot_l_hand_str = "greensoft") + +/obj/item/clothing/head/soft/yellow + name = "yellow cap" + desc = "It's a peaked cap in a tasteless yellow color." + icon_state = "yellowsoft" + item_state_slots = list(slot_r_hand_str = "yellowsoft", slot_l_hand_str = "yellowsoft") + +/obj/item/clothing/head/soft/grey + name = "grey cap" + desc = "It's a peaked cap in a tasteful grey color." + icon_state = "greysoft" + item_state_slots = list(slot_r_hand_str = "greysoft", slot_l_hand_str = "greysoft") + +/obj/item/clothing/head/soft/orange + name = "orange cap" + desc = "It's a peaked cap in a tasteless orange color." + icon_state = "orangesoft" + item_state_slots = list(slot_r_hand_str = "orangesoft", slot_l_hand_str = "orangesoft") + +/obj/item/clothing/head/soft/mime + name = "white cap" + desc = "It's a peaked cap in a tasteless white color." + icon_state = "mimesoft" + item_state_slots = list(slot_r_hand_str = "mimesoft", slot_l_hand_str = "mimesoft") + +/obj/item/clothing/head/soft/purple + name = "purple cap" + desc = "It's a peaked cap in a tasteless purple color." + icon_state = "purplesoft" + item_state_slots = list(slot_r_hand_str = "purplesoft", slot_l_hand_str = "purplesoft") + +/obj/item/clothing/head/soft/rainbow + name = "rainbow cap" + desc = "It's a peaked cap in a bright rainbow of colors." + icon_state = "rainbowsoft" + item_state_slots = list(slot_r_hand_str = "rainbowsoft", slot_l_hand_str = "rainbowsoft") + +/obj/item/clothing/head/soft/sec + name = "security cap" + desc = "It's a field cap in tasteful red color." + icon_state = "secsoft" + item_state_slots = list(slot_r_hand_str = "secsoft", slot_l_hand_str = "secsoft") + +/obj/item/clothing/head/soft/sec/corp + name = "corporate security cap" + desc = "It's field cap in corporate colors." + icon_state = "corpsoft" + item_state_slots = list(slot_r_hand_str = "corpsoft", slot_l_hand_str = "corpsoft") + +/obj/item/clothing/head/soft/black + name = "black cap" + desc = "It's a peaked cap in a tasteful black color." + icon_state = "blacksoft" + item_state_slots = list(slot_r_hand_str = "blacksoft", slot_l_hand_str = "blacksoft") + +/obj/item/clothing/head/soft/mbill + name = "shipping cap" + desc = "It's a ballcap bearing the colors of Major Bill's Shipping." + icon_state = "mbillsoft" + item_state_slots = list(slot_r_hand_str = "redsoft", slot_l_hand_str = "redsoft") + catalogue_data = list(/datum/category_item/catalogue/information/organization/major_bills) + +/obj/item/clothing/head/soft/med + name = "medical cap" + desc = "It's a field cap in white, with a blue cross on the front." + icon_state = "medsoft" + item_state_slots = list(slot_r_hand_str = "mimesoft", slot_l_hand_str = "mimesoft") + +/obj/item/clothing/head/soft/paramed + name = "paramedic's cap" + desc = "It's a field cap in dark blue, with a white cross on the front." + icon_state = "emtsoft" + item_state_slots = list(slot_r_hand_str = "bluesoft", slot_l_hand_str = "bluesoft") diff --git a/code/modules/clothing/masks/boxing.dm b/code/modules/clothing/masks/boxing.dm index 5a311c51724..5678e8bac64 100644 --- a/code/modules/clothing/masks/boxing.dm +++ b/code/modules/clothing/masks/boxing.dm @@ -1,35 +1,35 @@ -/obj/item/clothing/mask/balaclava - name = "balaclava" - desc = "LOADSAMONEY" - icon_state = "balaclava" - item_state_slots = list(slot_r_hand_str = "bandblack", slot_l_hand_str = "bandblack") - flags_inv = HIDEFACE|BLOCKHAIR - body_parts_covered = FACE|HEAD - w_class = ITEMSIZE_SMALL - -/obj/item/clothing/mask/balaclava/tactical - name = "green balaclava" - desc = "Designed to both hide identities and keep your face comfy and warm." - icon_state = "swatclava" - item_state_slots = list(slot_r_hand_str = "bandgreen", slot_l_hand_str = "bandgreen") - flags_inv = HIDEFACE|BLOCKHAIR - w_class = ITEMSIZE_SMALL - -/obj/item/clothing/mask/luchador - name = "Luchador Mask" - desc = "Worn by robust fighters, flying high to defeat their foes!" - icon_state = "luchag" - flags_inv = HIDEFACE|BLOCKHAIR - body_parts_covered = HEAD|FACE - w_class = ITEMSIZE_SMALL - siemens_coefficient = 3.0 - -/obj/item/clothing/mask/luchador/tecnicos - name = "Tecnicos Mask" - desc = "Worn by robust fighters who uphold justice and fight honorably." - icon_state = "luchador" - -/obj/item/clothing/mask/luchador/rudos - name = "Rudos Mask" - desc = "Worn by robust fighters who are willing to do anything to win." +/obj/item/clothing/mask/balaclava + name = "balaclava" + desc = "LOADSAMONEY" + icon_state = "balaclava" + item_state_slots = list(slot_r_hand_str = "bandblack", slot_l_hand_str = "bandblack") + flags_inv = HIDEFACE|BLOCKHAIR + body_parts_covered = FACE|HEAD + w_class = ITEMSIZE_SMALL + +/obj/item/clothing/mask/balaclava/tactical + name = "green balaclava" + desc = "Designed to both hide identities and keep your face comfy and warm." + icon_state = "swatclava" + item_state_slots = list(slot_r_hand_str = "bandgreen", slot_l_hand_str = "bandgreen") + flags_inv = HIDEFACE|BLOCKHAIR + w_class = ITEMSIZE_SMALL + +/obj/item/clothing/mask/luchador + name = "Luchador Mask" + desc = "Worn by robust fighters, flying high to defeat their foes!" + icon_state = "luchag" + flags_inv = HIDEFACE|BLOCKHAIR + body_parts_covered = HEAD|FACE + w_class = ITEMSIZE_SMALL + siemens_coefficient = 3.0 + +/obj/item/clothing/mask/luchador/tecnicos + name = "Tecnicos Mask" + desc = "Worn by robust fighters who uphold justice and fight honorably." + icon_state = "luchador" + +/obj/item/clothing/mask/luchador/rudos + name = "Rudos Mask" + desc = "Worn by robust fighters who are willing to do anything to win." icon_state = "luchar" \ No newline at end of file diff --git a/code/modules/clothing/masks/breath.dm b/code/modules/clothing/masks/breath.dm index cf7a0bc9392..c9e5baaf2c5 100644 --- a/code/modules/clothing/masks/breath.dm +++ b/code/modules/clothing/masks/breath.dm @@ -1,63 +1,63 @@ -/obj/item/clothing/mask/breath - desc = "A close-fitting mask that can be connected to an air supply." - name = "breath mask" - icon_state = "breath" - item_state_slots = list(slot_r_hand_str = "breath", slot_l_hand_str = "breath") - item_flags = AIRTIGHT|FLEXIBLEMATERIAL - body_parts_covered = FACE - w_class = ITEMSIZE_SMALL - gas_transfer_coefficient = 0.10 - permeability_coefficient = 0.50 - var/hanging = 0 - action_button_name = "Adjust Breath Mask" - pickup_sound = 'sound/items/pickup/component.ogg' - drop_sound = 'sound/items/drop/component.ogg' - - -/obj/item/clothing/mask/breath/proc/adjust_mask(mob/user) - if(user.canmove && !user.stat) - src.hanging = !src.hanging - if (src.hanging) - gas_transfer_coefficient = 1 - body_parts_covered = body_parts_covered & ~FACE - item_flags = item_flags & ~AIRTIGHT - icon_state = "breathdown" - to_chat(user, "Your mask is now hanging on your neck.") - else - gas_transfer_coefficient = initial(gas_transfer_coefficient) - body_parts_covered = initial(body_parts_covered) - item_flags = initial(item_flags) - icon_state = initial(icon_state) - to_chat(user, "You pull the mask up to cover your face.") - update_clothing_icon() - -/obj/item/clothing/mask/breath/attack_self(mob/user) - adjust_mask(user) - -/obj/item/clothing/mask/breath/verb/toggle() - set category = "Object" - set name = "Adjust mask" - set src in usr - - adjust_mask(usr) - -/obj/item/clothing/mask/breath/medical - desc = "A close-fitting sterile mask that can be connected to an air supply." - name = "medical mask" - icon_state = "medical" - item_state_slots = list(slot_r_hand_str = "medical", slot_l_hand_str = "medical") - permeability_coefficient = 0.01 - -/obj/item/clothing/mask/breath/emergency - desc = "A close-fitting mask that is used by the wallmounted emergency oxygen pump." - name = "emergency mask" - icon_state = "breath" - item_state = "breath" - permeability_coefficient = 0.50 - -/obj/item/clothing/mask/breath/anesthetic - desc = "A close-fitting sterile mask that is used by the anesthetic wallmounted pump." - name = "anesthetic mask" - icon_state = "medical" - item_state = "medical" - permeability_coefficient = 0.01 +/obj/item/clothing/mask/breath + desc = "A close-fitting mask that can be connected to an air supply." + name = "breath mask" + icon_state = "breath" + item_state_slots = list(slot_r_hand_str = "breath", slot_l_hand_str = "breath") + item_flags = AIRTIGHT|FLEXIBLEMATERIAL + body_parts_covered = FACE + w_class = ITEMSIZE_SMALL + gas_transfer_coefficient = 0.10 + permeability_coefficient = 0.50 + var/hanging = 0 + action_button_name = "Adjust Breath Mask" + pickup_sound = 'sound/items/pickup/component.ogg' + drop_sound = 'sound/items/drop/component.ogg' + + +/obj/item/clothing/mask/breath/proc/adjust_mask(mob/user) + if(user.canmove && !user.stat) + src.hanging = !src.hanging + if (src.hanging) + gas_transfer_coefficient = 1 + body_parts_covered = body_parts_covered & ~FACE + item_flags = item_flags & ~AIRTIGHT + icon_state = "breathdown" + to_chat(user, "Your mask is now hanging on your neck.") + else + gas_transfer_coefficient = initial(gas_transfer_coefficient) + body_parts_covered = initial(body_parts_covered) + item_flags = initial(item_flags) + icon_state = initial(icon_state) + to_chat(user, "You pull the mask up to cover your face.") + update_clothing_icon() + +/obj/item/clothing/mask/breath/attack_self(mob/user) + adjust_mask(user) + +/obj/item/clothing/mask/breath/verb/toggle() + set category = "Object" + set name = "Adjust mask" + set src in usr + + adjust_mask(usr) + +/obj/item/clothing/mask/breath/medical + desc = "A close-fitting sterile mask that can be connected to an air supply." + name = "medical mask" + icon_state = "medical" + item_state_slots = list(slot_r_hand_str = "medical", slot_l_hand_str = "medical") + permeability_coefficient = 0.01 + +/obj/item/clothing/mask/breath/emergency + desc = "A close-fitting mask that is used by the wallmounted emergency oxygen pump." + name = "emergency mask" + icon_state = "breath" + item_state = "breath" + permeability_coefficient = 0.50 + +/obj/item/clothing/mask/breath/anesthetic + desc = "A close-fitting sterile mask that is used by the anesthetic wallmounted pump." + name = "anesthetic mask" + icon_state = "medical" + item_state = "medical" + permeability_coefficient = 0.01 diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index e164234cee7..667e7b41f95 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -1,186 +1,186 @@ -/obj/item/clothing/mask/gas - name = "gas mask" - desc = "A face-covering mask that can be connected to an air supply. Filters harmful gases from the air." - icon_state = "gas_alt" - item_flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | ALLOW_SURVIVALFOOD - flags_inv = HIDEEARS|HIDEEYES|HIDEFACE - body_parts_covered = FACE|EYES - w_class = ITEMSIZE_NORMAL - item_state_slots = list(slot_r_hand_str = "gas_alt", slot_l_hand_str = "gas_alt") - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - siemens_coefficient = 0.9 - var/gas_filter_strength = 1 //For gas mask filters - var/list/filtered_gases = list("phoron", "nitrous_oxide") - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 75, rad = 0) - pickup_sound = 'sound/items/pickup/rubber.ogg' - -/obj/item/clothing/mask/gas/filter_air(datum/gas_mixture/air) - var/datum/gas_mixture/gas_filtered = new - - for(var/g in filtered_gases) - if(air.gas[g]) - gas_filtered.gas[g] = air.gas[g] * gas_filter_strength - air.gas[g] -= gas_filtered.gas[g] - - air.update_values() - gas_filtered.update_values() - - return gas_filtered - -/obj/item/clothing/mask/gas/clear - name = "gas mask" - desc = "A face-covering mask with a transparent faceplate that can be connected to an air supply." - icon_state = "gas_clear" - flags_inv = null - -/obj/item/clothing/mask/gas/half - name = "face mask" - desc = "A compact, durable gas mask that can be connected to an air supply." - icon_state = "halfgas" - siemens_coefficient = 0.7 - body_parts_covered = FACE - w_class = ITEMSIZE_SMALL - armor = list(melee = 10, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 55, rad = 0) - -//Turn it into a hailer mask -/obj/item/clothing/mask/gas/half/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/device/hailer)) - playsound(src, 'sound/items/Screwdriver.ogg', 50, 1) - user.drop_item(src) - var/obj/item/clothing/mask/gas/sechailer/N = new /obj/item/clothing/mask/gas/sechailer(src.loc) - N.fingerprints = src.fingerprints - N.fingerprintshidden = src.fingerprintshidden - N.fingerprintslast = src.fingerprintslast - N.suit_fibers = src.suit_fibers - N.hailer = I - I.loc = N - if(!isturf(N.loc)) - user.put_in_hands(N) - qdel(src) - ..() - -//Plague Dr suit can be found in clothing/suits/bio.dm -/obj/item/clothing/mask/gas/plaguedoctor - name = "plague doctor mask" - desc = "A modernised version of the classic design, this mask will not only filter out phoron but it can also be connected to an air supply." - icon_state = "plaguedoctor" - item_state_slots = list(slot_r_hand_str = "gas", slot_l_hand_str = "gas") - armor = list(melee = 0, bullet = 0, laser = 2,energy = 2, bomb = 0, bio = 90, rad = 0) - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/gas/plaguedoctor/gold - name = "gold plague doctor mask" - desc = "A modernised version of the classic design, this mask will not only filter out phoron but it can also be connected to an air supply. This one is gold." - icon_state = "plaguedoctor2" - -/obj/item/clothing/mask/gas/swat - name = "\improper SWAT mask" - desc = "A close-fitting tactical mask that can be connected to an air supply." - icon_state = "swat" - siemens_coefficient = 0.7 - body_parts_covered = FACE|EYES - -// Vox mask, has special code for eating -/obj/item/clothing/mask/gas/swat/vox - name = "\improper alien mask" - desc = "Clearly not designed for a human face." - flags = PHORONGUARD - item_flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT - species_restricted = list(SPECIES_VOX) - filtered_gases = list("oxygen", "nitrous_oxide") - var/mask_open = FALSE // Controls if the Vox can eat through this mask - action_button_name = "Toggle Feeding Port" - -/obj/item/clothing/mask/gas/swat/vox/proc/feeding_port(mob/user) - if(user.canmove && !user.stat) - mask_open = !mask_open - if(mask_open) - body_parts_covered = EYES - to_chat(user, "Your mask moves to allow you to eat.") - else - body_parts_covered = FACE|EYES - to_chat(user, "Your mask moves to cover your mouth.") - return - -/obj/item/clothing/mask/gas/swat/vox/attack_self(mob/user) - feeding_port(user) - ..() - -/obj/item/clothing/mask/gas/zaddat - name = "Zaddat Veil" - desc = "A clear survival mask used by the Zaddat to filter out harmful nitrogen. Can be connected to an air supply and reconfigured to allow for safe eating." - icon_state = "zaddat_mask" - item_state = "vax_mask" - //body_parts_covered = 0 - species_restricted = list(SPECIES_ZADDAT) - flags_inv = HIDEEARS //semi-transparent - filtered_gases = list("phoron", "nitrogen", "nitrous_oxide") - -/obj/item/clothing/mask/gas/syndicate - name = "tactical mask" - desc = "A close-fitting tactical mask that can be connected to an air supply." - icon_state = "swat" - siemens_coefficient = 0.7 - -/obj/item/clothing/mask/gas/explorer - name = "explorer gas mask" - desc = "A military-grade gas mask that can be connected to an air supply." - icon_state = "explorer" - item_state_slots = list(slot_r_hand_str = "gas", slot_l_hand_str = "gas") - armor = list(melee = 10, bullet = 5, laser = 5,energy = 5, bomb = 0, bio = 50, rad = 0) - siemens_coefficient = 0.9 - -/obj/item/clothing/mask/gas/clown_hat - name = "clown wig and mask" - desc = "A true prankster's facial attire. A clown is incomplete without their wig and mask." - icon_state = "clown" - item_state_slots = list(slot_r_hand_str = "clown_hat", slot_l_hand_str = "clown_hat") - -/obj/item/clothing/mask/gas/sexyclown - name = "sexy-clown wig and mask" - desc = "A feminine clown mask for the dabbling crossdressers or female entertainers." - icon_state = "sexyclown" - item_state_slots = list(slot_r_hand_str = "clown_hat", slot_l_hand_str = "clown_hat") - -/obj/item/clothing/mask/gas/mime - name = "mime mask" - desc = "The traditional mime's mask. It has an eerie facial posture." - icon_state = "mime" - item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") - -/obj/item/clothing/mask/gas/monkeymask - name = "monkey mask" - desc = "A mask used when acting as a monkey." - icon_state = "monkeymask" - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/gas/sexymime - name = "sexy mime mask" - desc = "A traditional female mime's mask." - icon_state = "sexymime" - item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") - -/obj/item/clothing/mask/gas/guy - name = "guy fawkes mask" - desc = "A mask stylised to depict Guy Fawkes." - icon_state = "guyfawkes" - flags_inv = HIDEEARS|HIDEFACE - item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") - -/obj/item/clothing/mask/gas/commando - name = "commando mask" - icon_state = "fullgas" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - siemens_coefficient = 0.2 - -/obj/item/clothing/mask/gas/cyborg - name = "cyborg visor" - desc = "Beep boop" - icon_state = "death" - -/obj/item/clothing/mask/gas/owl_mask - name = "owl mask" - desc = "Twoooo!" - icon_state = "owl" - body_parts_covered = HEAD|FACE|EYES +/obj/item/clothing/mask/gas + name = "gas mask" + desc = "A face-covering mask that can be connected to an air supply. Filters harmful gases from the air." + icon_state = "gas_alt" + item_flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | ALLOW_SURVIVALFOOD + flags_inv = HIDEEARS|HIDEEYES|HIDEFACE + body_parts_covered = FACE|EYES + w_class = ITEMSIZE_NORMAL + item_state_slots = list(slot_r_hand_str = "gas_alt", slot_l_hand_str = "gas_alt") + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + siemens_coefficient = 0.9 + var/gas_filter_strength = 1 //For gas mask filters + var/list/filtered_gases = list("phoron", "nitrous_oxide") + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 75, rad = 0) + pickup_sound = 'sound/items/pickup/rubber.ogg' + +/obj/item/clothing/mask/gas/filter_air(datum/gas_mixture/air) + var/datum/gas_mixture/gas_filtered = new + + for(var/g in filtered_gases) + if(air.gas[g]) + gas_filtered.gas[g] = air.gas[g] * gas_filter_strength + air.gas[g] -= gas_filtered.gas[g] + + air.update_values() + gas_filtered.update_values() + + return gas_filtered + +/obj/item/clothing/mask/gas/clear + name = "gas mask" + desc = "A face-covering mask with a transparent faceplate that can be connected to an air supply." + icon_state = "gas_clear" + flags_inv = null + +/obj/item/clothing/mask/gas/half + name = "face mask" + desc = "A compact, durable gas mask that can be connected to an air supply." + icon_state = "halfgas" + siemens_coefficient = 0.7 + body_parts_covered = FACE + w_class = ITEMSIZE_SMALL + armor = list(melee = 10, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 55, rad = 0) + +//Turn it into a hailer mask +/obj/item/clothing/mask/gas/half/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/device/hailer)) + playsound(src, 'sound/items/Screwdriver.ogg', 50, 1) + user.drop_item(src) + var/obj/item/clothing/mask/gas/sechailer/N = new /obj/item/clothing/mask/gas/sechailer(src.loc) + N.fingerprints = src.fingerprints + N.fingerprintshidden = src.fingerprintshidden + N.fingerprintslast = src.fingerprintslast + N.suit_fibers = src.suit_fibers + N.hailer = I + I.loc = N + if(!isturf(N.loc)) + user.put_in_hands(N) + qdel(src) + ..() + +//Plague Dr suit can be found in clothing/suits/bio.dm +/obj/item/clothing/mask/gas/plaguedoctor + name = "plague doctor mask" + desc = "A modernised version of the classic design, this mask will not only filter out phoron but it can also be connected to an air supply." + icon_state = "plaguedoctor" + item_state_slots = list(slot_r_hand_str = "gas", slot_l_hand_str = "gas") + armor = list(melee = 0, bullet = 0, laser = 2,energy = 2, bomb = 0, bio = 90, rad = 0) + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/gas/plaguedoctor/gold + name = "gold plague doctor mask" + desc = "A modernised version of the classic design, this mask will not only filter out phoron but it can also be connected to an air supply. This one is gold." + icon_state = "plaguedoctor2" + +/obj/item/clothing/mask/gas/swat + name = "\improper SWAT mask" + desc = "A close-fitting tactical mask that can be connected to an air supply." + icon_state = "swat" + siemens_coefficient = 0.7 + body_parts_covered = FACE|EYES + +// Vox mask, has special code for eating +/obj/item/clothing/mask/gas/swat/vox + name = "\improper alien mask" + desc = "Clearly not designed for a human face." + flags = PHORONGUARD + item_flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT + species_restricted = list(SPECIES_VOX) + filtered_gases = list("oxygen", "nitrous_oxide") + var/mask_open = FALSE // Controls if the Vox can eat through this mask + action_button_name = "Toggle Feeding Port" + +/obj/item/clothing/mask/gas/swat/vox/proc/feeding_port(mob/user) + if(user.canmove && !user.stat) + mask_open = !mask_open + if(mask_open) + body_parts_covered = EYES + to_chat(user, "Your mask moves to allow you to eat.") + else + body_parts_covered = FACE|EYES + to_chat(user, "Your mask moves to cover your mouth.") + return + +/obj/item/clothing/mask/gas/swat/vox/attack_self(mob/user) + feeding_port(user) + ..() + +/obj/item/clothing/mask/gas/zaddat + name = "Zaddat Veil" + desc = "A clear survival mask used by the Zaddat to filter out harmful nitrogen. Can be connected to an air supply and reconfigured to allow for safe eating." + icon_state = "zaddat_mask" + item_state = "vax_mask" + //body_parts_covered = 0 + species_restricted = list(SPECIES_ZADDAT) + flags_inv = HIDEEARS //semi-transparent + filtered_gases = list("phoron", "nitrogen", "nitrous_oxide") + +/obj/item/clothing/mask/gas/syndicate + name = "tactical mask" + desc = "A close-fitting tactical mask that can be connected to an air supply." + icon_state = "swat" + siemens_coefficient = 0.7 + +/obj/item/clothing/mask/gas/explorer + name = "explorer gas mask" + desc = "A military-grade gas mask that can be connected to an air supply." + icon_state = "explorer" + item_state_slots = list(slot_r_hand_str = "gas", slot_l_hand_str = "gas") + armor = list(melee = 10, bullet = 5, laser = 5,energy = 5, bomb = 0, bio = 50, rad = 0) + siemens_coefficient = 0.9 + +/obj/item/clothing/mask/gas/clown_hat + name = "clown wig and mask" + desc = "A true prankster's facial attire. A clown is incomplete without their wig and mask." + icon_state = "clown" + item_state_slots = list(slot_r_hand_str = "clown_hat", slot_l_hand_str = "clown_hat") + +/obj/item/clothing/mask/gas/sexyclown + name = "sexy-clown wig and mask" + desc = "A feminine clown mask for the dabbling crossdressers or female entertainers." + icon_state = "sexyclown" + item_state_slots = list(slot_r_hand_str = "clown_hat", slot_l_hand_str = "clown_hat") + +/obj/item/clothing/mask/gas/mime + name = "mime mask" + desc = "The traditional mime's mask. It has an eerie facial posture." + icon_state = "mime" + item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") + +/obj/item/clothing/mask/gas/monkeymask + name = "monkey mask" + desc = "A mask used when acting as a monkey." + icon_state = "monkeymask" + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/gas/sexymime + name = "sexy mime mask" + desc = "A traditional female mime's mask." + icon_state = "sexymime" + item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") + +/obj/item/clothing/mask/gas/guy + name = "guy fawkes mask" + desc = "A mask stylised to depict Guy Fawkes." + icon_state = "guyfawkes" + flags_inv = HIDEEARS|HIDEFACE + item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") + +/obj/item/clothing/mask/gas/commando + name = "commando mask" + icon_state = "fullgas" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + siemens_coefficient = 0.2 + +/obj/item/clothing/mask/gas/cyborg + name = "cyborg visor" + desc = "Beep boop" + icon_state = "death" + +/obj/item/clothing/mask/gas/owl_mask + name = "owl mask" + desc = "Twoooo!" + icon_state = "owl" + body_parts_covered = HEAD|FACE|EYES diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index 759d17d8928..9d7d9a693b1 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -1,422 +1,422 @@ -/obj/item/clothing/mask/muzzle - name = "muzzle" - desc = "To stop that awful noise." - icon_state = "muzzle" - body_parts_covered = FACE - w_class = ITEMSIZE_SMALL - gas_transfer_coefficient = 0.90 - voicechange = 1 - -/obj/item/clothing/mask/muzzle/tape - name = "length of tape" - desc = "It's a robust DIY muzzle!" - icon = 'icons/obj/bureaucracy.dmi' - icon_state = "tape_cross" - item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) - w_class = ITEMSIZE_TINY - -/obj/item/clothing/mask/muzzle/New() - ..() - say_messages = list("Mmfph!", "Mmmf mrrfff!", "Mmmf mnnf!") - say_verbs = list("mumbles", "says") - -// Clumsy folks can't take the mask off themselves. -/obj/item/clothing/mask/muzzle/attack_hand(mob/living/user as mob) - if(user.wear_mask == src && !user.IsAdvancedToolUser()) - return 0 - ..() - -/obj/item/clothing/mask/surgical - name = "sterile mask" - desc = "A sterile mask designed to help prevent the spread of diseases." - icon_state = "sterile" - item_state_slots = list(slot_r_hand_str = "sterile", slot_l_hand_str = "sterile") - w_class = ITEMSIZE_SMALL - body_parts_covered = FACE - item_flags = FLEXIBLEMATERIAL - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.01 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 0) - var/hanging = 0 - -/obj/item/clothing/mask/surgical/proc/adjust_mask(mob_user) - if(usr.canmove && !usr.stat) - src.hanging = !src.hanging - if (src.hanging) - gas_transfer_coefficient = 1 - body_parts_covered = body_parts_covered & ~FACE - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) - icon_state = "steriledown" - to_chat(usr, "You pull the mask below your chin.") - else - gas_transfer_coefficient = initial(gas_transfer_coefficient) - body_parts_covered = initial(body_parts_covered) - icon_state = initial(icon_state) - armor = initial(armor) - to_chat(usr, "You pull the mask up to cover your face.") - update_clothing_icon() - -/obj/item/clothing/mask/surgical/verb/toggle() - set category = "Object" - set name = "Adjust mask" - set src in usr - - adjust_mask(usr) - -/obj/item/clothing/mask/surgical/white - icon_state = "sterilew" - item_state_slots = list(slot_r_hand_str = "sterilew", slot_l_hand_str = "sterilew") - -/obj/item/clothing/mask/surgical/dust - name = "dust mask" - desc = "A dust mask designed to protect the wearer against construction and/or custodial particulate." - icon_state = "dust" - item_state_slots = list(slot_r_hand_str = "dust", slot_l_hand_str = "dust") - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 30, rad = 0) - -/obj/item/clothing/mask/surgical/cloth - name = "cloth mask" - desc = "A cloth mask designed to protect the wearer against allergens, illnesses, and social interaction." - icon_state = "cloth" - item_state_slots = list(slot_r_hand_str = "cloth", slot_l_hand_str = "cloth") - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 20, rad = 0) - -/obj/item/clothing/mask/fakemoustache - name = "fake moustache" - desc = "Warning: moustache is fake." - icon_state = "fake-moustache" - flags_inv = HIDEFACE - body_parts_covered = 0 - -/obj/item/clothing/mask/snorkel - name = "Snorkel" - desc = "For the Swimming Savant." - icon_state = "snorkel" - flags_inv = HIDEFACE - body_parts_covered = 0 - -//scarves (fit in in mask slot) -//None of these actually have on-mob sprites... -/obj/item/clothing/mask/bluescarf - name = "blue neck scarf" - desc = "A blue neck scarf." - icon_state = "blueneckscarf" - body_parts_covered = FACE - item_flags = FLEXIBLEMATERIAL - w_class = ITEMSIZE_SMALL - gas_transfer_coefficient = 0.90 - -/obj/item/clothing/mask/redscarf - name = "red scarf" - desc = "A red and white checkered neck scarf." - icon_state = "redwhite_scarf" - body_parts_covered = FACE - item_flags = FLEXIBLEMATERIAL - w_class = ITEMSIZE_SMALL - gas_transfer_coefficient = 0.90 - -/obj/item/clothing/mask/greenscarf - name = "green scarf" - desc = "A green neck scarf." - icon_state = "green_scarf" - body_parts_covered = FACE - item_flags = FLEXIBLEMATERIAL - w_class = ITEMSIZE_SMALL - gas_transfer_coefficient = 0.90 - -/obj/item/clothing/mask/ninjascarf - name = "ninja scarf" - desc = "A stealthy, dark scarf." - icon_state = "ninja_scarf" - body_parts_covered = FACE - item_flags = FLEXIBLEMATERIAL - w_class = ITEMSIZE_SMALL - gas_transfer_coefficient = 0.90 - siemens_coefficient = 0 - -/obj/item/clothing/mask/pig - name = "pig mask" - desc = "A rubber pig mask." - icon_state = "pig" - flags_inv = HIDEFACE|BLOCKHAIR - w_class = ITEMSIZE_SMALL - siemens_coefficient = 0.9 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/shark - name = "shark mask" - desc = "A rubber shark mask." - icon_state = "shark" - flags_inv = HIDEFACE - w_class = ITEMSIZE_SMALL - siemens_coefficient = 0.9 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/dolphin - name = "dolphin mask" - desc = "A rubber dolphin mask." - icon_state = "dolphin" - flags_inv = HIDEFACE - w_class = ITEMSIZE_SMALL - siemens_coefficient = 0.9 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/goblin - name = "goblin mask" - desc = "A rubber goblin mask." - icon_state = "goblin" - flags_inv = HIDEFACE - w_class = ITEMSIZE_SMALL - siemens_coefficient = 0.9 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/demon - name = "demon mask" - desc = "A rubber demon mask." - icon_state = "demon" - flags_inv = HIDEFACE - w_class = ITEMSIZE_SMALL - siemens_coefficient = 0.9 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/horsehead - name = "horse head mask" - desc = "A mask made of soft vinyl and latex, representing the head of a horse." - icon_state = "horsehead" - flags_inv = HIDEFACE|BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - w_class = ITEMSIZE_SMALL - siemens_coefficient = 0.9 - -/obj/item/clothing/mask/nock_scarab - name = "nock mask (blue, scarab)" - desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." - icon_state = "nock_scarab" - w_class = ITEMSIZE_SMALL - body_parts_covered = HEAD|FACE - -/obj/item/clothing/mask/nock_demon - name = "nock mask (purple, demon)" - desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." - icon_state = "nock_demon" - w_class = ITEMSIZE_SMALL - body_parts_covered = HEAD|FACE - -/obj/item/clothing/mask/nock_life - name = "nock mask (green, life)" - desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." - icon_state = "nock_life" - w_class = ITEMSIZE_SMALL - body_parts_covered = HEAD|FACE - -/obj/item/clothing/mask/nock_ornate - name = "nock mask (red, ornate)" - desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." - icon_state = "nock_ornate" - w_class = ITEMSIZE_SMALL - body_parts_covered = HEAD|FACE - -/obj/item/clothing/mask/horsehead/New() - ..() - // The horse mask doesn't cause voice changes by default, the wizard spell changes the flag as necessary - say_messages = list("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!") - say_verbs = list("whinnies", "neighs", "says") - -/obj/item/clothing/mask/ai - name = "camera MIU" - desc = "Allows for direct mental connection to accessible camera networks." - icon_state = "s-ninja" - item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") - flags_inv = HIDEFACE - body_parts_covered = 0 - var/mob/observer/eye/aiEye/eye - -/obj/item/clothing/mask/ai/New() - eye = new(src) - -/obj/item/clothing/mask/ai/equipped(var/mob/user, var/slot) - ..(user, slot) - if(slot == slot_wear_mask) - eye.owner = user - user.eyeobj = eye - - for(var/datum/chunk/c in eye.visibleChunks) - c.remove(eye) - eye.setLoc(user) - -/obj/item/clothing/mask/ai/dropped(var/mob/user) - ..() - if(eye.owner == user) - for(var/datum/chunk/c in eye.visibleChunks) - c.remove(eye) - - eye.owner.eyeobj = null - eye.owner = null - -/obj/item/clothing/mask/bandana - name = "black bandana" - desc = "A fine black bandana with nanotech lining. Can be worn on the head or face." - w_class = ITEMSIZE_TINY - flags_inv = HIDEFACE - slot_flags = SLOT_MASK|SLOT_HEAD - body_parts_covered = FACE - icon_state = "bandblack" - item_state_slots = list(slot_r_hand_str = "bandblack", slot_l_hand_str = "bandblack") - -/obj/item/clothing/mask/bandana/equipped(var/mob/user, var/slot) - switch(slot) - if(slot_wear_mask) //Mask is the default for all the settings - flags_inv = initial(flags_inv) - body_parts_covered = initial(body_parts_covered) - icon_state = initial(icon_state) - - if(slot_head) - flags_inv = 0 - body_parts_covered = HEAD - icon_state = "[initial(icon_state)]_up" - - return ..() - -/obj/item/clothing/mask/bandana/red - name = "red bandana" - desc = "A fine red bandana with nanotech lining. Can be worn on the head or face." - icon_state = "bandred" - item_state_slots = list(slot_r_hand_str = "bandred", slot_l_hand_str = "bandred") - -/obj/item/clothing/mask/bandana/blue - name = "blue bandana" - desc = "A fine blue bandana with nanotech lining. Can be worn on the head or face." - icon_state = "bandblue" - item_state_slots = list(slot_r_hand_str = "bandblue", slot_l_hand_str = "bandblue") - -/obj/item/clothing/mask/bandana/green - name = "green bandana" - desc = "A fine green bandana with nanotech lining. Can be worn on the head or face." - icon_state = "bandgreen" - item_state_slots = list(slot_r_hand_str = "bandgreen", slot_l_hand_str = "bandgreen") - -/obj/item/clothing/mask/bandana/gold - name = "gold bandana" - desc = "A fine gold bandana with nanotech lining. Can be worn on the head or face." - icon_state = "bandgold" - item_state_slots = list(slot_r_hand_str = "bandgold", slot_l_hand_str = "bandgold") - -/obj/item/clothing/mask/bandana/skull - name = "skull bandana" - desc = "A fine black bandana with nanotech lining and a skull emblem. Can be worn on the head or face." - icon_state = "bandskull" - item_state_slots = list(slot_r_hand_str = "bandskull", slot_l_hand_str = "bandskull") - -/obj/item/clothing/mask/veil - name = "black veil" - desc = "A black veil, typically worn at funerals or by goths." - w_class = ITEMSIZE_TINY - body_parts_covered = FACE - icon_state = "veil" - -/obj/item/clothing/mask/paper - name = "paper mask" - desc = "A neat, circular mask made out of paper. Perhaps you could try drawing on it with a pen!" - w_class = ITEMSIZE_SMALL - body_parts_covered = FACE - icon_state = "papermask" - action_button_name = "Redraw Design" - action_button_is_hands_free = TRUE - var/list/papermask_designs = list() - -/obj/item/clothing/mask/paper/Initialize(mapload) - . = ..() - papermask_designs = list( - "Blank" = image(icon = src.icon, icon_state = "papermask"), - "Neutral" = image(icon = src.icon, icon_state = "neutralmask"), - "Eyes" = image(icon = src.icon, icon_state = "eyemask"), - "Sleeping" = image(icon = src.icon, icon_state = "sleepingmask"), - "Heart" = image(icon = src.icon, icon_state = "heartmask"), - "Core" = image(icon = src.icon, icon_state = "coremask"), - "Plus" = image(icon = src.icon, icon_state = "plusmask"), - "Square" = image(icon = src.icon, icon_state = "squaremask"), - "Bullseye" = image(icon = src.icon, icon_state = "bullseyemask"), - "Vertical" = image(icon = src.icon, icon_state = "verticalmask"), - "Horizontal" = image(icon = src.icon, icon_state = "horizontalmask"), - "X" = image(icon = src.icon, icon_state = "xmask"), - "Bugeyes" = image(icon = src.icon, icon_state = "bugmask"), - "Double" = image(icon = src.icon, icon_state = "doublemask"), - "Mark" = image(icon = src.icon, icon_state = "markmask"), - "Line" = image(icon = src.icon, icon_state = "linemask"), - "Minus" = image(icon = src.icon, icon_state = "minusmask"), - "Four" = image(icon = src.icon, icon_state = "fourmask"), - "Diamond" = image(icon = src.icon, icon_state = "diamondmask"), - "Cat" = image(icon = src.icon, icon_state = "catmask"), - "Big Eyes" = image(icon = src.icon, icon_state = "bigeyemask"), - "Good" = image(icon = src.icon, icon_state = "goodmask"), - "Bad" = image(icon = src.icon, icon_state = "badmask"), - "Happy" = image(icon = src.icon, icon_state = "happymask"), - "Sad" = image(icon = src.icon, icon_state = "sadmask") - ) - -/obj/item/clothing/mask/paper/attack_self(mob/user) - . = ..() - if(!istype(user) || user.incapacitated()) - return - - var/static/list/options = list("Blank" = "papermask", "Neutral" = "neutralmask", "Eyes" = "eyemask", - "Sleeping" ="sleepingmask", "Heart" = "heartmask", "Core" = "coremask", - "Plus" = "plusmask", "Square" ="squaremask", "Bullseye" = "bullseyemask", - "Vertical" = "verticalmask", "Horizontal" = "horizontalmask", "X" ="xmask", - "Bugeyes" = "bugmask", "Double" = "doublemask", "Mark" = "markmask", - "Line" = "linemask", "Minus" = "minusmask", "Four" = "fourmask", - "Diamond" = "diamondmask", "Cat" = "catmask", "Big Eyes" = "bigeyemask", - "Good" = "goodmask", "Bad" = "badmask", "Happy" = "happymask", "Sad" = "sadmask" - ) - - var/choice = show_radial_menu(user, src, papermask_designs, custom_check = FALSE, radius = 36, require_near = TRUE) - - if(src && choice && !user.incapacitated() && in_range(user,src)) - icon_state = options[choice] - user.update_inv_wear_mask() - user.update_action_buttons() - to_chat(user, "Your paper mask now is now [choice].") - return 1 - -/obj/item/clothing/mask/emotions - name = "emotional mask" - desc = "Express your happiness or hide your sorrows with this modular cutout. Draw your current emotions onto it with a pen!" - w_class = ITEMSIZE_SMALL - body_parts_covered = FACE - icon_state = "joy" - action_button_name = "Redraw Design" - action_button_is_hands_free = TRUE - var/static/list/joymask_designs = list() - - -/obj/item/clothing/mask/emotions/Initialize(mapload) - . = ..() - joymask_designs = list( - "Joy" = image(icon = src.icon, icon_state = "joy"), - "Flushed" = image(icon = src.icon, icon_state = "flushed"), - "Pensive" = image(icon = src.icon, icon_state = "pensive"), - "Angry" = image(icon = src.icon, icon_state = "angry"), - ) - -/obj/item/clothing/mask/emotions/attack_self(mob/user) - . = ..() - if(!istype(user) || user.incapacitated()) - return - - var/static/list/options = list("Joy" = "joy", "Flushed" = "flushed", "Pensive" = "pensive","Angry" ="angry") - - var/choice = show_radial_menu(user, src, joymask_designs, custom_check = FALSE, radius = 36, require_near = TRUE) - - if(src && choice && !user.incapacitated() && in_range(user,src)) - icon_state = options[choice] - user.update_inv_wear_mask() - user.update_action_buttons() - to_chat(user, "Your [src] now displays a [choice] emotion.") - return 1 - -/obj/item/clothing/mask/mouthwheat - name = "mouth wheat" - desc = "100% synthetic \"Country Girls LLC.\" brand mouth wheat. Warning: not for actual consumption." - icon_state = "mouthwheat" - w_class = ITEMSIZE_SMALL - body_parts_covered = 0 +/obj/item/clothing/mask/muzzle + name = "muzzle" + desc = "To stop that awful noise." + icon_state = "muzzle" + body_parts_covered = FACE + w_class = ITEMSIZE_SMALL + gas_transfer_coefficient = 0.90 + voicechange = 1 + +/obj/item/clothing/mask/muzzle/tape + name = "length of tape" + desc = "It's a robust DIY muzzle!" + icon = 'icons/obj/bureaucracy.dmi' + icon_state = "tape_cross" + item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) + w_class = ITEMSIZE_TINY + +/obj/item/clothing/mask/muzzle/New() + ..() + say_messages = list("Mmfph!", "Mmmf mrrfff!", "Mmmf mnnf!") + say_verbs = list("mumbles", "says") + +// Clumsy folks can't take the mask off themselves. +/obj/item/clothing/mask/muzzle/attack_hand(mob/living/user as mob) + if(user.wear_mask == src && !user.IsAdvancedToolUser()) + return 0 + ..() + +/obj/item/clothing/mask/surgical + name = "sterile mask" + desc = "A sterile mask designed to help prevent the spread of diseases." + icon_state = "sterile" + item_state_slots = list(slot_r_hand_str = "sterile", slot_l_hand_str = "sterile") + w_class = ITEMSIZE_SMALL + body_parts_covered = FACE + item_flags = FLEXIBLEMATERIAL + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.01 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 0) + var/hanging = 0 + +/obj/item/clothing/mask/surgical/proc/adjust_mask(mob_user) + if(usr.canmove && !usr.stat) + src.hanging = !src.hanging + if (src.hanging) + gas_transfer_coefficient = 1 + body_parts_covered = body_parts_covered & ~FACE + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) + icon_state = "steriledown" + to_chat(usr, "You pull the mask below your chin.") + else + gas_transfer_coefficient = initial(gas_transfer_coefficient) + body_parts_covered = initial(body_parts_covered) + icon_state = initial(icon_state) + armor = initial(armor) + to_chat(usr, "You pull the mask up to cover your face.") + update_clothing_icon() + +/obj/item/clothing/mask/surgical/verb/toggle() + set category = "Object" + set name = "Adjust mask" + set src in usr + + adjust_mask(usr) + +/obj/item/clothing/mask/surgical/white + icon_state = "sterilew" + item_state_slots = list(slot_r_hand_str = "sterilew", slot_l_hand_str = "sterilew") + +/obj/item/clothing/mask/surgical/dust + name = "dust mask" + desc = "A dust mask designed to protect the wearer against construction and/or custodial particulate." + icon_state = "dust" + item_state_slots = list(slot_r_hand_str = "dust", slot_l_hand_str = "dust") + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 30, rad = 0) + +/obj/item/clothing/mask/surgical/cloth + name = "cloth mask" + desc = "A cloth mask designed to protect the wearer against allergens, illnesses, and social interaction." + icon_state = "cloth" + item_state_slots = list(slot_r_hand_str = "cloth", slot_l_hand_str = "cloth") + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 20, rad = 0) + +/obj/item/clothing/mask/fakemoustache + name = "fake moustache" + desc = "Warning: moustache is fake." + icon_state = "fake-moustache" + flags_inv = HIDEFACE + body_parts_covered = 0 + +/obj/item/clothing/mask/snorkel + name = "Snorkel" + desc = "For the Swimming Savant." + icon_state = "snorkel" + flags_inv = HIDEFACE + body_parts_covered = 0 + +//scarves (fit in in mask slot) +//None of these actually have on-mob sprites... +/obj/item/clothing/mask/bluescarf + name = "blue neck scarf" + desc = "A blue neck scarf." + icon_state = "blueneckscarf" + body_parts_covered = FACE + item_flags = FLEXIBLEMATERIAL + w_class = ITEMSIZE_SMALL + gas_transfer_coefficient = 0.90 + +/obj/item/clothing/mask/redscarf + name = "red scarf" + desc = "A red and white checkered neck scarf." + icon_state = "redwhite_scarf" + body_parts_covered = FACE + item_flags = FLEXIBLEMATERIAL + w_class = ITEMSIZE_SMALL + gas_transfer_coefficient = 0.90 + +/obj/item/clothing/mask/greenscarf + name = "green scarf" + desc = "A green neck scarf." + icon_state = "green_scarf" + body_parts_covered = FACE + item_flags = FLEXIBLEMATERIAL + w_class = ITEMSIZE_SMALL + gas_transfer_coefficient = 0.90 + +/obj/item/clothing/mask/ninjascarf + name = "ninja scarf" + desc = "A stealthy, dark scarf." + icon_state = "ninja_scarf" + body_parts_covered = FACE + item_flags = FLEXIBLEMATERIAL + w_class = ITEMSIZE_SMALL + gas_transfer_coefficient = 0.90 + siemens_coefficient = 0 + +/obj/item/clothing/mask/pig + name = "pig mask" + desc = "A rubber pig mask." + icon_state = "pig" + flags_inv = HIDEFACE|BLOCKHAIR + w_class = ITEMSIZE_SMALL + siemens_coefficient = 0.9 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/shark + name = "shark mask" + desc = "A rubber shark mask." + icon_state = "shark" + flags_inv = HIDEFACE + w_class = ITEMSIZE_SMALL + siemens_coefficient = 0.9 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/dolphin + name = "dolphin mask" + desc = "A rubber dolphin mask." + icon_state = "dolphin" + flags_inv = HIDEFACE + w_class = ITEMSIZE_SMALL + siemens_coefficient = 0.9 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/goblin + name = "goblin mask" + desc = "A rubber goblin mask." + icon_state = "goblin" + flags_inv = HIDEFACE + w_class = ITEMSIZE_SMALL + siemens_coefficient = 0.9 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/demon + name = "demon mask" + desc = "A rubber demon mask." + icon_state = "demon" + flags_inv = HIDEFACE + w_class = ITEMSIZE_SMALL + siemens_coefficient = 0.9 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/horsehead + name = "horse head mask" + desc = "A mask made of soft vinyl and latex, representing the head of a horse." + icon_state = "horsehead" + flags_inv = HIDEFACE|BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + w_class = ITEMSIZE_SMALL + siemens_coefficient = 0.9 + +/obj/item/clothing/mask/nock_scarab + name = "nock mask (blue, scarab)" + desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." + icon_state = "nock_scarab" + w_class = ITEMSIZE_SMALL + body_parts_covered = HEAD|FACE + +/obj/item/clothing/mask/nock_demon + name = "nock mask (purple, demon)" + desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." + icon_state = "nock_demon" + w_class = ITEMSIZE_SMALL + body_parts_covered = HEAD|FACE + +/obj/item/clothing/mask/nock_life + name = "nock mask (green, life)" + desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." + icon_state = "nock_life" + w_class = ITEMSIZE_SMALL + body_parts_covered = HEAD|FACE + +/obj/item/clothing/mask/nock_ornate + name = "nock mask (red, ornate)" + desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." + icon_state = "nock_ornate" + w_class = ITEMSIZE_SMALL + body_parts_covered = HEAD|FACE + +/obj/item/clothing/mask/horsehead/New() + ..() + // The horse mask doesn't cause voice changes by default, the wizard spell changes the flag as necessary + say_messages = list("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!") + say_verbs = list("whinnies", "neighs", "says") + +/obj/item/clothing/mask/ai + name = "camera MIU" + desc = "Allows for direct mental connection to accessible camera networks." + icon_state = "s-ninja" + item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") + flags_inv = HIDEFACE + body_parts_covered = 0 + var/mob/observer/eye/aiEye/eye + +/obj/item/clothing/mask/ai/New() + eye = new(src) + +/obj/item/clothing/mask/ai/equipped(var/mob/user, var/slot) + ..(user, slot) + if(slot == slot_wear_mask) + eye.owner = user + user.eyeobj = eye + + for(var/datum/chunk/c in eye.visibleChunks) + c.remove(eye) + eye.setLoc(user) + +/obj/item/clothing/mask/ai/dropped(var/mob/user) + ..() + if(eye.owner == user) + for(var/datum/chunk/c in eye.visibleChunks) + c.remove(eye) + + eye.owner.eyeobj = null + eye.owner = null + +/obj/item/clothing/mask/bandana + name = "black bandana" + desc = "A fine black bandana with nanotech lining. Can be worn on the head or face." + w_class = ITEMSIZE_TINY + flags_inv = HIDEFACE + slot_flags = SLOT_MASK|SLOT_HEAD + body_parts_covered = FACE + icon_state = "bandblack" + item_state_slots = list(slot_r_hand_str = "bandblack", slot_l_hand_str = "bandblack") + +/obj/item/clothing/mask/bandana/equipped(var/mob/user, var/slot) + switch(slot) + if(slot_wear_mask) //Mask is the default for all the settings + flags_inv = initial(flags_inv) + body_parts_covered = initial(body_parts_covered) + icon_state = initial(icon_state) + + if(slot_head) + flags_inv = 0 + body_parts_covered = HEAD + icon_state = "[initial(icon_state)]_up" + + return ..() + +/obj/item/clothing/mask/bandana/red + name = "red bandana" + desc = "A fine red bandana with nanotech lining. Can be worn on the head or face." + icon_state = "bandred" + item_state_slots = list(slot_r_hand_str = "bandred", slot_l_hand_str = "bandred") + +/obj/item/clothing/mask/bandana/blue + name = "blue bandana" + desc = "A fine blue bandana with nanotech lining. Can be worn on the head or face." + icon_state = "bandblue" + item_state_slots = list(slot_r_hand_str = "bandblue", slot_l_hand_str = "bandblue") + +/obj/item/clothing/mask/bandana/green + name = "green bandana" + desc = "A fine green bandana with nanotech lining. Can be worn on the head or face." + icon_state = "bandgreen" + item_state_slots = list(slot_r_hand_str = "bandgreen", slot_l_hand_str = "bandgreen") + +/obj/item/clothing/mask/bandana/gold + name = "gold bandana" + desc = "A fine gold bandana with nanotech lining. Can be worn on the head or face." + icon_state = "bandgold" + item_state_slots = list(slot_r_hand_str = "bandgold", slot_l_hand_str = "bandgold") + +/obj/item/clothing/mask/bandana/skull + name = "skull bandana" + desc = "A fine black bandana with nanotech lining and a skull emblem. Can be worn on the head or face." + icon_state = "bandskull" + item_state_slots = list(slot_r_hand_str = "bandskull", slot_l_hand_str = "bandskull") + +/obj/item/clothing/mask/veil + name = "black veil" + desc = "A black veil, typically worn at funerals or by goths." + w_class = ITEMSIZE_TINY + body_parts_covered = FACE + icon_state = "veil" + +/obj/item/clothing/mask/paper + name = "paper mask" + desc = "A neat, circular mask made out of paper. Perhaps you could try drawing on it with a pen!" + w_class = ITEMSIZE_SMALL + body_parts_covered = FACE + icon_state = "papermask" + action_button_name = "Redraw Design" + action_button_is_hands_free = TRUE + var/list/papermask_designs = list() + +/obj/item/clothing/mask/paper/Initialize(mapload) + . = ..() + papermask_designs = list( + "Blank" = image(icon = src.icon, icon_state = "papermask"), + "Neutral" = image(icon = src.icon, icon_state = "neutralmask"), + "Eyes" = image(icon = src.icon, icon_state = "eyemask"), + "Sleeping" = image(icon = src.icon, icon_state = "sleepingmask"), + "Heart" = image(icon = src.icon, icon_state = "heartmask"), + "Core" = image(icon = src.icon, icon_state = "coremask"), + "Plus" = image(icon = src.icon, icon_state = "plusmask"), + "Square" = image(icon = src.icon, icon_state = "squaremask"), + "Bullseye" = image(icon = src.icon, icon_state = "bullseyemask"), + "Vertical" = image(icon = src.icon, icon_state = "verticalmask"), + "Horizontal" = image(icon = src.icon, icon_state = "horizontalmask"), + "X" = image(icon = src.icon, icon_state = "xmask"), + "Bugeyes" = image(icon = src.icon, icon_state = "bugmask"), + "Double" = image(icon = src.icon, icon_state = "doublemask"), + "Mark" = image(icon = src.icon, icon_state = "markmask"), + "Line" = image(icon = src.icon, icon_state = "linemask"), + "Minus" = image(icon = src.icon, icon_state = "minusmask"), + "Four" = image(icon = src.icon, icon_state = "fourmask"), + "Diamond" = image(icon = src.icon, icon_state = "diamondmask"), + "Cat" = image(icon = src.icon, icon_state = "catmask"), + "Big Eyes" = image(icon = src.icon, icon_state = "bigeyemask"), + "Good" = image(icon = src.icon, icon_state = "goodmask"), + "Bad" = image(icon = src.icon, icon_state = "badmask"), + "Happy" = image(icon = src.icon, icon_state = "happymask"), + "Sad" = image(icon = src.icon, icon_state = "sadmask") + ) + +/obj/item/clothing/mask/paper/attack_self(mob/user) + . = ..() + if(!istype(user) || user.incapacitated()) + return + + var/static/list/options = list("Blank" = "papermask", "Neutral" = "neutralmask", "Eyes" = "eyemask", + "Sleeping" ="sleepingmask", "Heart" = "heartmask", "Core" = "coremask", + "Plus" = "plusmask", "Square" ="squaremask", "Bullseye" = "bullseyemask", + "Vertical" = "verticalmask", "Horizontal" = "horizontalmask", "X" ="xmask", + "Bugeyes" = "bugmask", "Double" = "doublemask", "Mark" = "markmask", + "Line" = "linemask", "Minus" = "minusmask", "Four" = "fourmask", + "Diamond" = "diamondmask", "Cat" = "catmask", "Big Eyes" = "bigeyemask", + "Good" = "goodmask", "Bad" = "badmask", "Happy" = "happymask", "Sad" = "sadmask" + ) + + var/choice = show_radial_menu(user, src, papermask_designs, custom_check = FALSE, radius = 36, require_near = TRUE) + + if(src && choice && !user.incapacitated() && in_range(user,src)) + icon_state = options[choice] + user.update_inv_wear_mask() + user.update_action_buttons() + to_chat(user, "Your paper mask now is now [choice].") + return 1 + +/obj/item/clothing/mask/emotions + name = "emotional mask" + desc = "Express your happiness or hide your sorrows with this modular cutout. Draw your current emotions onto it with a pen!" + w_class = ITEMSIZE_SMALL + body_parts_covered = FACE + icon_state = "joy" + action_button_name = "Redraw Design" + action_button_is_hands_free = TRUE + var/static/list/joymask_designs = list() + + +/obj/item/clothing/mask/emotions/Initialize(mapload) + . = ..() + joymask_designs = list( + "Joy" = image(icon = src.icon, icon_state = "joy"), + "Flushed" = image(icon = src.icon, icon_state = "flushed"), + "Pensive" = image(icon = src.icon, icon_state = "pensive"), + "Angry" = image(icon = src.icon, icon_state = "angry"), + ) + +/obj/item/clothing/mask/emotions/attack_self(mob/user) + . = ..() + if(!istype(user) || user.incapacitated()) + return + + var/static/list/options = list("Joy" = "joy", "Flushed" = "flushed", "Pensive" = "pensive","Angry" ="angry") + + var/choice = show_radial_menu(user, src, joymask_designs, custom_check = FALSE, radius = 36, require_near = TRUE) + + if(src && choice && !user.incapacitated() && in_range(user,src)) + icon_state = options[choice] + user.update_inv_wear_mask() + user.update_action_buttons() + to_chat(user, "Your [src] now displays a [choice] emotion.") + return 1 + +/obj/item/clothing/mask/mouthwheat + name = "mouth wheat" + desc = "100% synthetic \"Country Girls LLC.\" brand mouth wheat. Warning: not for actual consumption." + icon_state = "mouthwheat" + w_class = ITEMSIZE_SMALL + body_parts_covered = 0 diff --git a/code/modules/clothing/shoes/colour.dm b/code/modules/clothing/shoes/colour.dm index 5035a7f3496..aa01bab9d8a 100644 --- a/code/modules/clothing/shoes/colour.dm +++ b/code/modules/clothing/shoes/colour.dm @@ -1,158 +1,158 @@ - -/obj/item/clothing/shoes/black - name = "black shoes" - icon_state = "black" - desc = "A pair of black shoes." - -/obj/item/clothing/shoes/brown - name = "brown shoes" - desc = "A pair of brown shoes." - icon_state = "brown" - -/obj/item/clothing/shoes/blue - name = "blue shoes" - icon_state = "blue" - -/obj/item/clothing/shoes/green - name = "green shoes" - icon_state = "green" - -/obj/item/clothing/shoes/yellow - name = "yellow shoes" - icon_state = "yellow" - -/obj/item/clothing/shoes/purple - name = "purple shoes" - icon_state = "purple" - -/obj/item/clothing/shoes/red - name = "red shoes" - desc = "Stylish red shoes." - icon_state = "red" - -/obj/item/clothing/shoes/white - name = "white shoes" - icon_state = "white" - permeability_coefficient = 0.01 - -/obj/item/clothing/shoes/rainbow - name = "rainbow shoes" - desc = "Very colourful shoes." - icon_state = "rain_bow" - -/obj/item/clothing/shoes/flats - name = "black flats" - desc = "Sleek black flats." - icon_state = "flatsblack" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - -/obj/item/clothing/shoes/flats/white - name = "white flats" - desc = "Shiny white flats." - icon_state = "flatswhite" - addblends = "flatswhite_a" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - -/obj/item/clothing/shoes/flats/white/color - name = "flats" - desc = "Sleek flats." - -/obj/item/clothing/shoes/flats/red - name = "red flats" - desc = "Ruby red flats." - icon_state = "flatsred" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - -/obj/item/clothing/shoes/flats/purple - name = "purple flats" - desc = "Royal purple flats." - icon_state = "flatspurple" - item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") - -/obj/item/clothing/shoes/flats/blue - name = "blue flats" - desc = "Sleek blue flats." - icon_state = "flatsblue" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - -/obj/item/clothing/shoes/flats/brown - name = "brown flats" - desc = "Sleek brown flats." - icon_state = "flatsbrown" - item_state_slots = list(slot_r_hand_str = "brown", slot_l_hand_str = "brown") - -/obj/item/clothing/shoes/flats/orange - name = "orange flats" - desc = "Radiant orange flats." - icon_state = "flatsorange" - item_state_slots = list(slot_r_hand_str = "orange", slot_l_hand_str = "orange") - -/obj/item/clothing/shoes/orange - name = "orange shoes" - icon_state = "orange" - var/obj/item/weapon/handcuffs/chained = null - -/obj/item/clothing/shoes/orange/proc/attach_cuffs(var/obj/item/weapon/handcuffs/cuffs, mob/user as mob) - if (chained) return - - user.drop_item() - cuffs.loc = src - chained = cuffs - slowdown = 15 - icon_state = "orange1" - -/obj/item/clothing/shoes/orange/proc/remove_cuffs(mob/user as mob) - if (!chained) return - - user.put_in_hands(chained) - chained.add_fingerprint(user) - - slowdown = initial(slowdown) - icon_state = "orange" - chained = null - -/obj/item/clothing/shoes/orange/attack_self(mob/user as mob) - ..() - remove_cuffs(user) - -/obj/item/clothing/shoes/orange/attackby(H as obj, mob/user as mob) - ..() - if (istype(H, /obj/item/weapon/handcuffs)) - attach_cuffs(H, user) - -/obj/item/clothing/shoes/hitops - name = "white high-tops" - desc = "A pair of shoes that extends past the ankle. Based on a centuries-old, timeless design." - icon_state = "whitehi" - -/obj/item/clothing/shoes/hitops/red - name = "red high-tops" - icon_state = "redhi" - -/obj/item/clothing/shoes/hitops/brown - name = "brown high-tops" - icon_state = "brownhi" - -/obj/item/clothing/shoes/hitops/black - name = "black high-tops" - icon_state = "blackhi" - -/obj/item/clothing/shoes/hitops/orange - name = "orange high-tops" - icon_state = "orangehi" - -/obj/item/clothing/shoes/hitops/blue - name = "blue high-tops" - icon_state = "bluehi" - -/obj/item/clothing/shoes/hitops/green - name = "green high-tops" - icon_state = "greenhi" - -/obj/item/clothing/shoes/hitops/purple - name = "purple high-tops" - icon_state = "purplehi" - -/obj/item/clothing/shoes/hitops/yellow - name = "yellow high-tops" + +/obj/item/clothing/shoes/black + name = "black shoes" + icon_state = "black" + desc = "A pair of black shoes." + +/obj/item/clothing/shoes/brown + name = "brown shoes" + desc = "A pair of brown shoes." + icon_state = "brown" + +/obj/item/clothing/shoes/blue + name = "blue shoes" + icon_state = "blue" + +/obj/item/clothing/shoes/green + name = "green shoes" + icon_state = "green" + +/obj/item/clothing/shoes/yellow + name = "yellow shoes" + icon_state = "yellow" + +/obj/item/clothing/shoes/purple + name = "purple shoes" + icon_state = "purple" + +/obj/item/clothing/shoes/red + name = "red shoes" + desc = "Stylish red shoes." + icon_state = "red" + +/obj/item/clothing/shoes/white + name = "white shoes" + icon_state = "white" + permeability_coefficient = 0.01 + +/obj/item/clothing/shoes/rainbow + name = "rainbow shoes" + desc = "Very colourful shoes." + icon_state = "rain_bow" + +/obj/item/clothing/shoes/flats + name = "black flats" + desc = "Sleek black flats." + icon_state = "flatsblack" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + +/obj/item/clothing/shoes/flats/white + name = "white flats" + desc = "Shiny white flats." + icon_state = "flatswhite" + addblends = "flatswhite_a" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + +/obj/item/clothing/shoes/flats/white/color + name = "flats" + desc = "Sleek flats." + +/obj/item/clothing/shoes/flats/red + name = "red flats" + desc = "Ruby red flats." + icon_state = "flatsred" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + +/obj/item/clothing/shoes/flats/purple + name = "purple flats" + desc = "Royal purple flats." + icon_state = "flatspurple" + item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") + +/obj/item/clothing/shoes/flats/blue + name = "blue flats" + desc = "Sleek blue flats." + icon_state = "flatsblue" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + +/obj/item/clothing/shoes/flats/brown + name = "brown flats" + desc = "Sleek brown flats." + icon_state = "flatsbrown" + item_state_slots = list(slot_r_hand_str = "brown", slot_l_hand_str = "brown") + +/obj/item/clothing/shoes/flats/orange + name = "orange flats" + desc = "Radiant orange flats." + icon_state = "flatsorange" + item_state_slots = list(slot_r_hand_str = "orange", slot_l_hand_str = "orange") + +/obj/item/clothing/shoes/orange + name = "orange shoes" + icon_state = "orange" + var/obj/item/weapon/handcuffs/chained = null + +/obj/item/clothing/shoes/orange/proc/attach_cuffs(var/obj/item/weapon/handcuffs/cuffs, mob/user as mob) + if (chained) return + + user.drop_item() + cuffs.loc = src + chained = cuffs + slowdown = 15 + icon_state = "orange1" + +/obj/item/clothing/shoes/orange/proc/remove_cuffs(mob/user as mob) + if (!chained) return + + user.put_in_hands(chained) + chained.add_fingerprint(user) + + slowdown = initial(slowdown) + icon_state = "orange" + chained = null + +/obj/item/clothing/shoes/orange/attack_self(mob/user as mob) + ..() + remove_cuffs(user) + +/obj/item/clothing/shoes/orange/attackby(H as obj, mob/user as mob) + ..() + if (istype(H, /obj/item/weapon/handcuffs)) + attach_cuffs(H, user) + +/obj/item/clothing/shoes/hitops + name = "white high-tops" + desc = "A pair of shoes that extends past the ankle. Based on a centuries-old, timeless design." + icon_state = "whitehi" + +/obj/item/clothing/shoes/hitops/red + name = "red high-tops" + icon_state = "redhi" + +/obj/item/clothing/shoes/hitops/brown + name = "brown high-tops" + icon_state = "brownhi" + +/obj/item/clothing/shoes/hitops/black + name = "black high-tops" + icon_state = "blackhi" + +/obj/item/clothing/shoes/hitops/orange + name = "orange high-tops" + icon_state = "orangehi" + +/obj/item/clothing/shoes/hitops/blue + name = "blue high-tops" + icon_state = "bluehi" + +/obj/item/clothing/shoes/hitops/green + name = "green high-tops" + icon_state = "greenhi" + +/obj/item/clothing/shoes/hitops/purple + name = "purple high-tops" + icon_state = "purplehi" + +/obj/item/clothing/shoes/hitops/yellow + name = "yellow high-tops" icon_state = "yellowhi" \ No newline at end of file diff --git a/code/modules/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm index b775dcf98c9..dd921661156 100644 --- a/code/modules/clothing/shoes/magboots.dm +++ b/code/modules/clothing/shoes/magboots.dm @@ -1,129 +1,129 @@ -/obj/item/clothing/shoes/magboots - desc = "Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle. They're large enough to be worn over other footwear." - name = "magboots" - icon_state = "magboots0" - flags = PHORONGUARD - item_state_slots = list(slot_r_hand_str = "magboots", slot_l_hand_str = "magboots") - species_restricted = null - center_of_mass = list("x" = 17,"y" = 12) - force = 3 - overshoes = 1 - shoes_under_pants = -1 //These things are huge - preserve_item = 1 - var/magpulse = 0 - var/icon_base = "magboots" - action_button_name = "Toggle Magboots" - var/obj/item/clothing/shoes/shoes = null //Undershoes - var/mob/living/carbon/human/wearer = null //For shoe procs - step_volume_mod = 1.3 - drop_sound = 'sound/items/drop/metalboots.ogg' - pickup_sound = 'sound/items/pickup/toolbox.ogg' - -/obj/item/clothing/shoes/magboots/proc/set_slowdown() - slowdown = shoes? max(SHOES_SLOWDOWN, shoes.slowdown): SHOES_SLOWDOWN //So you can't put on magboots to make you walk faster. - if (magpulse) - slowdown += 3 - -/obj/item/clothing/shoes/magboots/attack_self(mob/user) - if(magpulse) - item_flags &= ~NOSLIP - magpulse = 0 - set_slowdown() - force = 3 - if(icon_base) icon_state = "[icon_base]0" - to_chat(user, "You disable the mag-pulse traction system.") - else - item_flags |= NOSLIP - magpulse = 1 - set_slowdown() - force = 5 - if(icon_base) icon_state = "[icon_base]1" - playsound(src, 'sound/effects/magnetclamp.ogg', 20) - to_chat(user, "You enable the mag-pulse traction system.") - user.update_inv_shoes() //so our mob-overlays update - user.update_action_buttons() - -/obj/item/clothing/shoes/magboots/mob_can_equip(mob/user, slot, disable_warning = FALSE) - var/mob/living/carbon/human/H = user - - if(H.shoes) - shoes = H.shoes - if(shoes.overshoes) - if(slot && slot == slot_shoes) - to_chat(user, "You are unable to wear \the [src] as \the [H.shoes] are in the way.") - shoes = null - return 0 - H.drop_from_inventory(shoes) //Remove the old shoes so you can put on the magboots. - shoes.forceMove(src) - - if(!..()) - if(shoes) //Put the old shoes back on if the check fails. - if(H.equip_to_slot_if_possible(shoes, slot_shoes)) - src.shoes = null - return 0 - - if (shoes) - if(slot && slot == slot_shoes) - to_chat(user, "You slip \the [src] on over \the [shoes].") - set_slowdown() - wearer = H - return 1 - -/obj/item/clothing/shoes/magboots/dropped() - ..() - var/mob/living/carbon/human/H = wearer - if(shoes) - if(!H.equip_to_slot_if_possible(shoes, slot_shoes)) - shoes.forceMove(get_turf(src)) - src.shoes = null - wearer = null - -/obj/item/clothing/shoes/magboots/examine(mob/user) - . = ..() - . += "Its mag-pulse traction system appears to be [item_flags & NOSLIP ? "enabled" : "disabled"]." - -/obj/item/clothing/shoes/magboots/vox - - desc = "A pair of heavy, jagged armoured foot pieces, seemingly suitable for a velociraptor." - name = "vox magclaws" - item_state = "boots-vox" - icon_state = "boots-vox" - flags = PHORONGUARD - species_restricted = list(SPECIES_VOX) - - action_button_name = "Toggle the magclaws" - -/obj/item/clothing/shoes/magboots/vox/attack_self(mob/user) - if(src.magpulse) - item_flags &= ~NOSLIP - magpulse = 0 - canremove = TRUE - to_chat(user, "You relax your deathgrip on the flooring.") - else - //make sure these can only be used when equipped. - if(!ishuman(user)) - return - var/mob/living/carbon/human/H = user - if (H.shoes != src) - to_chat(user, "You will have to put on the [src] before you can do that.") - return - - item_flags |= NOSLIP - magpulse = 1 - canremove = FALSE //kinda hard to take off magclaws when you are gripping them tightly. - to_chat(user, "You dig your claws deeply into the flooring, bracing yourself.") - user.update_action_buttons() - -//In case they somehow come off while enabled. -/obj/item/clothing/shoes/magboots/vox/dropped(mob/user as mob) - ..() - if(src.magpulse) - user.visible_message("The [src] go limp as they are removed from [usr]'s feet.", "The [src] go limp as they are removed from your feet.") - item_flags &= ~NOSLIP - magpulse = 0 - canremove = TRUE - -/obj/item/clothing/shoes/magboots/vox/examine(mob/user) - . = ..() - if(magpulse) +/obj/item/clothing/shoes/magboots + desc = "Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle. They're large enough to be worn over other footwear." + name = "magboots" + icon_state = "magboots0" + flags = PHORONGUARD + item_state_slots = list(slot_r_hand_str = "magboots", slot_l_hand_str = "magboots") + species_restricted = null + center_of_mass = list("x" = 17,"y" = 12) + force = 3 + overshoes = 1 + shoes_under_pants = -1 //These things are huge + preserve_item = 1 + var/magpulse = 0 + var/icon_base = "magboots" + action_button_name = "Toggle Magboots" + var/obj/item/clothing/shoes/shoes = null //Undershoes + var/mob/living/carbon/human/wearer = null //For shoe procs + step_volume_mod = 1.3 + drop_sound = 'sound/items/drop/metalboots.ogg' + pickup_sound = 'sound/items/pickup/toolbox.ogg' + +/obj/item/clothing/shoes/magboots/proc/set_slowdown() + slowdown = shoes? max(SHOES_SLOWDOWN, shoes.slowdown): SHOES_SLOWDOWN //So you can't put on magboots to make you walk faster. + if (magpulse) + slowdown += 3 + +/obj/item/clothing/shoes/magboots/attack_self(mob/user) + if(magpulse) + item_flags &= ~NOSLIP + magpulse = 0 + set_slowdown() + force = 3 + if(icon_base) icon_state = "[icon_base]0" + to_chat(user, "You disable the mag-pulse traction system.") + else + item_flags |= NOSLIP + magpulse = 1 + set_slowdown() + force = 5 + if(icon_base) icon_state = "[icon_base]1" + playsound(src, 'sound/effects/magnetclamp.ogg', 20) + to_chat(user, "You enable the mag-pulse traction system.") + user.update_inv_shoes() //so our mob-overlays update + user.update_action_buttons() + +/obj/item/clothing/shoes/magboots/mob_can_equip(mob/user, slot, disable_warning = FALSE) + var/mob/living/carbon/human/H = user + + if(H.shoes) + shoes = H.shoes + if(shoes.overshoes) + if(slot && slot == slot_shoes) + to_chat(user, "You are unable to wear \the [src] as \the [H.shoes] are in the way.") + shoes = null + return 0 + H.drop_from_inventory(shoes) //Remove the old shoes so you can put on the magboots. + shoes.forceMove(src) + + if(!..()) + if(shoes) //Put the old shoes back on if the check fails. + if(H.equip_to_slot_if_possible(shoes, slot_shoes)) + src.shoes = null + return 0 + + if (shoes) + if(slot && slot == slot_shoes) + to_chat(user, "You slip \the [src] on over \the [shoes].") + set_slowdown() + wearer = H + return 1 + +/obj/item/clothing/shoes/magboots/dropped() + ..() + var/mob/living/carbon/human/H = wearer + if(shoes) + if(!H.equip_to_slot_if_possible(shoes, slot_shoes)) + shoes.forceMove(get_turf(src)) + src.shoes = null + wearer = null + +/obj/item/clothing/shoes/magboots/examine(mob/user) + . = ..() + . += "Its mag-pulse traction system appears to be [item_flags & NOSLIP ? "enabled" : "disabled"]." + +/obj/item/clothing/shoes/magboots/vox + + desc = "A pair of heavy, jagged armoured foot pieces, seemingly suitable for a velociraptor." + name = "vox magclaws" + item_state = "boots-vox" + icon_state = "boots-vox" + flags = PHORONGUARD + species_restricted = list(SPECIES_VOX) + + action_button_name = "Toggle the magclaws" + +/obj/item/clothing/shoes/magboots/vox/attack_self(mob/user) + if(src.magpulse) + item_flags &= ~NOSLIP + magpulse = 0 + canremove = TRUE + to_chat(user, "You relax your deathgrip on the flooring.") + else + //make sure these can only be used when equipped. + if(!ishuman(user)) + return + var/mob/living/carbon/human/H = user + if (H.shoes != src) + to_chat(user, "You will have to put on the [src] before you can do that.") + return + + item_flags |= NOSLIP + magpulse = 1 + canremove = FALSE //kinda hard to take off magclaws when you are gripping them tightly. + to_chat(user, "You dig your claws deeply into the flooring, bracing yourself.") + user.update_action_buttons() + +//In case they somehow come off while enabled. +/obj/item/clothing/shoes/magboots/vox/dropped(mob/user as mob) + ..() + if(src.magpulse) + user.visible_message("The [src] go limp as they are removed from [usr]'s feet.", "The [src] go limp as they are removed from your feet.") + item_flags &= ~NOSLIP + magpulse = 0 + canremove = TRUE + +/obj/item/clothing/shoes/magboots/vox/examine(mob/user) + . = ..() + if(magpulse) . += "It would be hard to take these off without relaxing your grip first." // Theoretically this message should only be seen by the wearer when the claws are equipped. \ No newline at end of file diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index 67708437ab3..272fd99b5dc 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -1,256 +1,256 @@ -/obj/item/clothing/shoes/syndigaloshes - desc = "A pair of brown shoes. They seem to have extra grip." - name = "brown shoes" - icon_state = "brown" - permeability_coefficient = 0.05 - item_flags = NOSLIP - origin_tech = list(TECH_ILLEGAL = 3) - var/list/clothing_choices = list() - siemens_coefficient = 0.8 - species_restricted = null - step_volume_mod = 0.5 - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' - -/obj/item/clothing/shoes/mime - name = "mime shoes" - icon_state = "white" - step_volume_mod = 0 //It's a mime - -/obj/item/clothing/shoes/galoshes - desc = "Rubber boots" - name = "galoshes" - icon_state = "galoshes" - permeability_coefficient = 0.05 - siemens_coefficient = 0 //They're thick rubber boots! Of course they won't conduct electricity! - item_flags = NOSLIP - slowdown = SHOES_SLOWDOWN+0.5 - species_restricted = null - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' - -/obj/item/clothing/shoes/dress - name = "dress shoes" - desc = "Sharp looking low quarters, perfect for a formal uniform." - icon_state = "laceups" - -/obj/item/clothing/shoes/dress/white - name = "white dress shoes" - desc = "Brilliantly white low quarters, not a spot on them." - icon_state = "whitedress" - -/obj/item/clothing/shoes/sandal - desc = "A pair of rather plain, wooden sandals." - name = "sandals" - icon_state = "wizard" - species_restricted = null - body_parts_covered = 0 - - wizard_garb = 1 - -/obj/item/clothing/shoes/flipflop - name = "flip flops" - desc = "A pair of foam flip flops. For those not afraid to show a little ankle." - icon_state = "thongsandal" - addblends = "thongsandal_a" - -/obj/item/clothing/shoes/cookflop - name = "grilling sandals" - desc = "All this talk of antags, greytiding, and griefing... I just wanna grill for god's sake!" - icon_state = "cookflops" - species_restricted = null - body_parts_covered = 0 - -/obj/item/clothing/shoes/tourist_1 - name = "tourist sandals" - desc = "Black sandals usually worn by tourists. Need I say more?" - icon_state = "tourist_1" - species_restricted = null - body_parts_covered = 0 - -/obj/item/clothing/shoes/tourist_2 - name = "tourist sandals" - desc = "Green sandals usually worn by tourists. Need I say more?" - icon_state = "tourist_2" - species_restricted = null - body_parts_covered = 0 - -/obj/item/clothing/shoes/sandal/clogs - name = "plastic clogs" - desc = "A pair of plastic clog shoes." - icon_state = "clogs" - -/obj/item/clothing/shoes/sandal/marisa - desc = "A pair of magic, black shoes." - name = "magic shoes" - icon_state = "black" - body_parts_covered = FEET - -/obj/item/clothing/shoes/clown_shoes - desc = "The prankster's standard-issue clowning shoes. Damn they're huge!" - name = "clown shoes" - icon_state = "clown" - slowdown = SHOES_SLOWDOWN+0.5 - force = 0 - var/footstep = 1 //used for squeeks whilst walking - species_restricted = null - -/obj/item/clothing/shoes/clown_shoes/handle_movement(var/turf/walking, var/running) - if(running) - if(footstep >= 2) - footstep = 0 - playsound(src, "clownstep", 50, 1) // this will get annoying very fast. - else - footstep++ - else - playsound(src, "clownstep", 20, 1) - -/obj/item/clothing/shoes/cult - name = "boots" - desc = "A pair of boots worn by the followers of Nar-Sie." - icon_state = "cult" - item_state_slots = list(slot_r_hand_str = "cult", slot_l_hand_str = "cult") - force = 2 - siemens_coefficient = 0.7 - - cold_protection = FEET - min_cold_protection_temperature = SHOE_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = FEET - max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECTION_TEMPERATURE - species_restricted = null - -/obj/item/clothing/shoes/cult/cultify() - return - -/obj/item/clothing/shoes/cyborg - name = "cyborg boots" - desc = "Shoes for a cyborg costume" - icon_state = "boots" - -/obj/item/clothing/shoes/slippers - name = "bunny slippers" - desc = "Fluffy!" - icon_state = "slippers" - force = 0 - species_restricted = null - w_class = ITEMSIZE_SMALL - drop_sound = 'sound/items/drop/clothing.ogg' - pickup_sound = 'sound/items/pickup/clothing.ogg' - -/obj/item/clothing/shoes/slippers/worn - name = "worn bunny slippers" - desc = "Fluffy..." - icon_state = "slippers_worn" - item_state_slots = list(slot_r_hand_str = "slippers", slot_l_hand_str = "slippers") - -/obj/item/clothing/shoes/laceup - name = "black oxford shoes" - icon_state = "oxford_black" - -/obj/item/clothing/shoes/laceup/grey - name = "grey oxford shoes" - icon_state = "oxford_grey" - -/obj/item/clothing/shoes/laceup/brown - name = "brown oxford shoes" - icon_state = "oxford_brown" - -/obj/item/clothing/shoes/swimmingfins - desc = "Help you swim good." - name = "swimming fins" - icon_state = "flippers" - item_state_slots = list(slot_r_hand_str = "galoshes", slot_l_hand_str = "galoshes") - item_flags = NOSLIP - slowdown = SHOES_SLOWDOWN+0.5 - species_restricted = null - -/obj/item/clothing/shoes/athletic - name = "athletic shoes" - desc = "A pair of sleek athletic shoes. Made by and for the sporty types." - icon_state = "sportshoe" - addblends = "sportshoe_a" - item_state_slots = list(slot_r_hand_str = "sportheld", slot_l_hand_str = "sportheld") - -/obj/item/clothing/shoes/skater - name = "skater shoes" - desc = "A pair of wide shoes with thick soles. Designed for skating." - icon_state = "skatershoe" - addblends = "skatershoe_a" - item_state_slots = list(slot_r_hand_str = "skaterheld", slot_l_hand_str = "skaterheld") - -/obj/item/clothing/shoes/heels - name = "high heels" - desc = "A pair of high-heeled shoes. Fancy!" - icon_state = "heels" - addblends = "heels_a" - -/obj/item/clothing/shoes/footwraps - name = "cloth footwraps" - desc = "A roll of treated canvas used for wrapping claws or paws" - icon_state = "clothwrap" - item_state = "clothwrap" - force = 0 - w_class = ITEMSIZE_SMALL - species_restricted = null - drop_sound = 'sound/items/drop/clothing.ogg' - pickup_sound = 'sound/items/pickup/clothing.ogg' - -/obj/item/clothing/shoes/boots/ranger - var/bootcolor = "white" - name = "ranger boots" - desc = "The Rangers special lightweight hybrid magboots-jetboots perfect for EVA. If only these functions were so easy to copy in reality.\ - These ones are just a well-made pair of boots in appropriate colours." - icon = 'icons/obj/clothing/ranger.dmi' - icon_state = "ranger_boots" - -/obj/item/clothing/shoes/boots/ranger/Initialize() - . = ..() - if(icon_state == "ranger_boots") - name = "[bootcolor] ranger boots" - icon_state = "[bootcolor]_ranger_boots" - -/obj/item/clothing/shoes/boots/ranger/black - bootcolor = "black" - -/obj/item/clothing/shoes/boots/ranger/pink - bootcolor = "pink" - -/obj/item/clothing/shoes/boots/ranger/green - bootcolor = "green" - -/obj/item/clothing/shoes/boots/ranger/cyan - bootcolor = "cyan" - -/obj/item/clothing/shoes/boots/ranger/orange - bootcolor = "orange" - -/obj/item/clothing/shoes/boots/ranger/yellow - bootcolor = "yellow" - -/* - * 80s - */ - -/obj/item/clothing/shoes/sneakerspurple - name = "purple sneakers" - desc = "A stylish, expensive pair of purple sneakers." - icon_state = "sneakerspurple" - item_state = "sneakerspurple" - -/obj/item/clothing/shoes/sneakersblue - name = "blue sneakers" - desc = "A stylish, expensive pair of blue sneakers." - icon_state = "sneakersblue" - item_state = "sneakersblue" - -/obj/item/clothing/shoes/sneakersred - name = "red sneakers" - desc = "A stylish, expensive pair of red sneakers." - icon_state = "sneakersred" - item_state = "sneakersred" - -/obj/item/clothing/shoes/ballet - name = "pointe shoes" - desc = "These shoes feature long lace straps and flattened off toes. Great for the most elegant of dances!" - icon_state = "ballet" - item_state = "ballet" +/obj/item/clothing/shoes/syndigaloshes + desc = "A pair of brown shoes. They seem to have extra grip." + name = "brown shoes" + icon_state = "brown" + permeability_coefficient = 0.05 + item_flags = NOSLIP + origin_tech = list(TECH_ILLEGAL = 3) + var/list/clothing_choices = list() + siemens_coefficient = 0.8 + species_restricted = null + step_volume_mod = 0.5 + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' + +/obj/item/clothing/shoes/mime + name = "mime shoes" + icon_state = "white" + step_volume_mod = 0 //It's a mime + +/obj/item/clothing/shoes/galoshes + desc = "Rubber boots" + name = "galoshes" + icon_state = "galoshes" + permeability_coefficient = 0.05 + siemens_coefficient = 0 //They're thick rubber boots! Of course they won't conduct electricity! + item_flags = NOSLIP + slowdown = SHOES_SLOWDOWN+0.5 + species_restricted = null + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' + +/obj/item/clothing/shoes/dress + name = "dress shoes" + desc = "Sharp looking low quarters, perfect for a formal uniform." + icon_state = "laceups" + +/obj/item/clothing/shoes/dress/white + name = "white dress shoes" + desc = "Brilliantly white low quarters, not a spot on them." + icon_state = "whitedress" + +/obj/item/clothing/shoes/sandal + desc = "A pair of rather plain, wooden sandals." + name = "sandals" + icon_state = "wizard" + species_restricted = null + body_parts_covered = 0 + + wizard_garb = 1 + +/obj/item/clothing/shoes/flipflop + name = "flip flops" + desc = "A pair of foam flip flops. For those not afraid to show a little ankle." + icon_state = "thongsandal" + addblends = "thongsandal_a" + +/obj/item/clothing/shoes/cookflop + name = "grilling sandals" + desc = "All this talk of antags, greytiding, and griefing... I just wanna grill for god's sake!" + icon_state = "cookflops" + species_restricted = null + body_parts_covered = 0 + +/obj/item/clothing/shoes/tourist_1 + name = "tourist sandals" + desc = "Black sandals usually worn by tourists. Need I say more?" + icon_state = "tourist_1" + species_restricted = null + body_parts_covered = 0 + +/obj/item/clothing/shoes/tourist_2 + name = "tourist sandals" + desc = "Green sandals usually worn by tourists. Need I say more?" + icon_state = "tourist_2" + species_restricted = null + body_parts_covered = 0 + +/obj/item/clothing/shoes/sandal/clogs + name = "plastic clogs" + desc = "A pair of plastic clog shoes." + icon_state = "clogs" + +/obj/item/clothing/shoes/sandal/marisa + desc = "A pair of magic, black shoes." + name = "magic shoes" + icon_state = "black" + body_parts_covered = FEET + +/obj/item/clothing/shoes/clown_shoes + desc = "The prankster's standard-issue clowning shoes. Damn they're huge!" + name = "clown shoes" + icon_state = "clown" + slowdown = SHOES_SLOWDOWN+0.5 + force = 0 + var/footstep = 1 //used for squeeks whilst walking + species_restricted = null + +/obj/item/clothing/shoes/clown_shoes/handle_movement(var/turf/walking, var/running) + if(running) + if(footstep >= 2) + footstep = 0 + playsound(src, "clownstep", 50, 1) // this will get annoying very fast. + else + footstep++ + else + playsound(src, "clownstep", 20, 1) + +/obj/item/clothing/shoes/cult + name = "boots" + desc = "A pair of boots worn by the followers of Nar-Sie." + icon_state = "cult" + item_state_slots = list(slot_r_hand_str = "cult", slot_l_hand_str = "cult") + force = 2 + siemens_coefficient = 0.7 + + cold_protection = FEET + min_cold_protection_temperature = SHOE_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = FEET + max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECTION_TEMPERATURE + species_restricted = null + +/obj/item/clothing/shoes/cult/cultify() + return + +/obj/item/clothing/shoes/cyborg + name = "cyborg boots" + desc = "Shoes for a cyborg costume" + icon_state = "boots" + +/obj/item/clothing/shoes/slippers + name = "bunny slippers" + desc = "Fluffy!" + icon_state = "slippers" + force = 0 + species_restricted = null + w_class = ITEMSIZE_SMALL + drop_sound = 'sound/items/drop/clothing.ogg' + pickup_sound = 'sound/items/pickup/clothing.ogg' + +/obj/item/clothing/shoes/slippers/worn + name = "worn bunny slippers" + desc = "Fluffy..." + icon_state = "slippers_worn" + item_state_slots = list(slot_r_hand_str = "slippers", slot_l_hand_str = "slippers") + +/obj/item/clothing/shoes/laceup + name = "black oxford shoes" + icon_state = "oxford_black" + +/obj/item/clothing/shoes/laceup/grey + name = "grey oxford shoes" + icon_state = "oxford_grey" + +/obj/item/clothing/shoes/laceup/brown + name = "brown oxford shoes" + icon_state = "oxford_brown" + +/obj/item/clothing/shoes/swimmingfins + desc = "Help you swim good." + name = "swimming fins" + icon_state = "flippers" + item_state_slots = list(slot_r_hand_str = "galoshes", slot_l_hand_str = "galoshes") + item_flags = NOSLIP + slowdown = SHOES_SLOWDOWN+0.5 + species_restricted = null + +/obj/item/clothing/shoes/athletic + name = "athletic shoes" + desc = "A pair of sleek athletic shoes. Made by and for the sporty types." + icon_state = "sportshoe" + addblends = "sportshoe_a" + item_state_slots = list(slot_r_hand_str = "sportheld", slot_l_hand_str = "sportheld") + +/obj/item/clothing/shoes/skater + name = "skater shoes" + desc = "A pair of wide shoes with thick soles. Designed for skating." + icon_state = "skatershoe" + addblends = "skatershoe_a" + item_state_slots = list(slot_r_hand_str = "skaterheld", slot_l_hand_str = "skaterheld") + +/obj/item/clothing/shoes/heels + name = "high heels" + desc = "A pair of high-heeled shoes. Fancy!" + icon_state = "heels" + addblends = "heels_a" + +/obj/item/clothing/shoes/footwraps + name = "cloth footwraps" + desc = "A roll of treated canvas used for wrapping claws or paws" + icon_state = "clothwrap" + item_state = "clothwrap" + force = 0 + w_class = ITEMSIZE_SMALL + species_restricted = null + drop_sound = 'sound/items/drop/clothing.ogg' + pickup_sound = 'sound/items/pickup/clothing.ogg' + +/obj/item/clothing/shoes/boots/ranger + var/bootcolor = "white" + name = "ranger boots" + desc = "The Rangers special lightweight hybrid magboots-jetboots perfect for EVA. If only these functions were so easy to copy in reality.\ + These ones are just a well-made pair of boots in appropriate colours." + icon = 'icons/obj/clothing/ranger.dmi' + icon_state = "ranger_boots" + +/obj/item/clothing/shoes/boots/ranger/Initialize() + . = ..() + if(icon_state == "ranger_boots") + name = "[bootcolor] ranger boots" + icon_state = "[bootcolor]_ranger_boots" + +/obj/item/clothing/shoes/boots/ranger/black + bootcolor = "black" + +/obj/item/clothing/shoes/boots/ranger/pink + bootcolor = "pink" + +/obj/item/clothing/shoes/boots/ranger/green + bootcolor = "green" + +/obj/item/clothing/shoes/boots/ranger/cyan + bootcolor = "cyan" + +/obj/item/clothing/shoes/boots/ranger/orange + bootcolor = "orange" + +/obj/item/clothing/shoes/boots/ranger/yellow + bootcolor = "yellow" + +/* + * 80s + */ + +/obj/item/clothing/shoes/sneakerspurple + name = "purple sneakers" + desc = "A stylish, expensive pair of purple sneakers." + icon_state = "sneakerspurple" + item_state = "sneakerspurple" + +/obj/item/clothing/shoes/sneakersblue + name = "blue sneakers" + desc = "A stylish, expensive pair of blue sneakers." + icon_state = "sneakersblue" + item_state = "sneakersblue" + +/obj/item/clothing/shoes/sneakersred + name = "red sneakers" + desc = "A stylish, expensive pair of red sneakers." + icon_state = "sneakersred" + item_state = "sneakersred" + +/obj/item/clothing/shoes/ballet + name = "pointe shoes" + desc = "These shoes feature long lace straps and flattened off toes. Great for the most elegant of dances!" + icon_state = "ballet" + item_state = "ballet" diff --git a/code/modules/clothing/spacesuits/miscellaneous.dm b/code/modules/clothing/spacesuits/miscellaneous.dm index 39ca0b8c742..f3fef22d7b1 100644 --- a/code/modules/clothing/spacesuits/miscellaneous.dm +++ b/code/modules/clothing/spacesuits/miscellaneous.dm @@ -1,104 +1,104 @@ -//Captain's Spacesuit -/obj/item/clothing/head/helmet/space/capspace - name = "space helmet" - icon_state = "capspace" - desc = "A special helmet designed for work in a hazardous, low-pressure environment. Only for the most fashionable of military figureheads." - item_flags = 0 - flags_inv = HIDEFACE|BLOCKHAIR - permeability_coefficient = 0.01 - armor = list(melee = 65, bullet = 50, laser = 50,energy = 25, bomb = 50, bio = 100, rad = 50) - -//Captain's space suit This is not the proper path but I don't currently know enough about how this all works to mess with it. -/obj/item/clothing/suit/armor/captain - name = "Site Manager's armor" - desc = "A bulky, heavy-duty piece of exclusive corporate armor. YOU are in charge!" - icon_state = "caparmor" - w_class = ITEMSIZE_HUGE - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.02 - item_flags = 0 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight,/obj/item/weapon/gun/energy, /obj/item/weapon/gun/projectile, /obj/item/ammo_magazine, /obj/item/ammo_casing, /obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs) - slowdown = 1.5 - armor = list(melee = 65, bullet = 50, laser = 50, energy = 25, bomb = 50, bio = 100, rad = 50) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL - min_pressure_protection = 0 - max_pressure_protection = 2 * ONE_ATMOSPHERE - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0.7 - -//Deathsquad suit -/obj/item/clothing/head/helmet/space/deathsquad - name = "deathsquad helmet" - desc = "That's not red paint. That's real blood." - icon_state = "deathsquad" - item_state_slots = list(slot_r_hand_str = "syndicate-helm-black-red", slot_l_hand_str = "syndicate-helm-black-red") - armor = list(melee = 65, bullet = 55, laser = 35,energy = 20, bomb = 30, bio = 100, rad = 60) - item_flags = THICKMATERIAL - flags_inv = BLOCKHAIR - siemens_coefficient = 0.6 - -//how is this a space helmet? -/obj/item/clothing/head/helmet/space/deathsquad/beret - name = "officer's beret" - desc = "An armored beret commonly used by special operations officers." - icon_state = "beret_badge" - item_state_slots = list(slot_r_hand_str = "beret", slot_l_hand_str = "beret") - armor = list(melee = 65, bullet = 55, laser = 35,energy = 20, bomb = 30, bio = 30, rad = 30) - item_flags = 0 - flags_inv = BLOCKHAIR - siemens_coefficient = 0.9 - -//Space santa outfit suit -/obj/item/clothing/head/helmet/space/santahat - name = "Santa's hat" - desc = "Ho ho ho. Merrry X-mas!" - icon_state = "santahat" - item_flags = 0 - flags_inv = BLOCKHAIR - body_parts_covered = HEAD - -/obj/item/clothing/suit/space/santa - name = "Santa's suit" - desc = "Festive!" - icon_state = "santa" - slowdown = 0 - item_flags = 0 - allowed = list(/obj/item) //for stuffing exta special presents - -//Space pirate outfit -/obj/item/clothing/head/helmet/space/pirate - name = "pirate hat" - desc = "Yarr." - icon_state = "pirate" - armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) - item_flags = 0 - flags_inv = BLOCKHAIR - body_parts_covered = 0 - siemens_coefficient = 0.9 - -/obj/item/clothing/suit/space/pirate //Whhhhyyyyyyy??? - name = "pirate coat" - desc = "Yarr." - icon_state = "pirate" - w_class = ITEMSIZE_NORMAL - allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen) - slowdown = 0 - armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) - siemens_coefficient = 0.9 - flags_inv = HIDETAIL|HIDEHOLSTER - body_parts_covered = UPPER_TORSO|ARMS - -//Orange emergency space suit -/obj/item/clothing/head/helmet/space/emergency - name = "emergency soft helmet" - icon_state = "syndicate-helm-orange" - desc = "A simple helmet with a built in light, smells like mothballs." - flash_protection = FLASH_PROTECTION_NONE - -/obj/item/clothing/suit/space/emergency - name = "emergency softsuit" - icon_state = "syndicate-orange" - desc = "A thin, ungainly softsuit colored in blaze orange for rescuers to easily locate, looks pretty fragile." - slowdown = 2 +//Captain's Spacesuit +/obj/item/clothing/head/helmet/space/capspace + name = "space helmet" + icon_state = "capspace" + desc = "A special helmet designed for work in a hazardous, low-pressure environment. Only for the most fashionable of military figureheads." + item_flags = 0 + flags_inv = HIDEFACE|BLOCKHAIR + permeability_coefficient = 0.01 + armor = list(melee = 65, bullet = 50, laser = 50,energy = 25, bomb = 50, bio = 100, rad = 50) + +//Captain's space suit This is not the proper path but I don't currently know enough about how this all works to mess with it. +/obj/item/clothing/suit/armor/captain + name = "Site Manager's armor" + desc = "A bulky, heavy-duty piece of exclusive corporate armor. YOU are in charge!" + icon_state = "caparmor" + w_class = ITEMSIZE_HUGE + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.02 + item_flags = 0 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight,/obj/item/weapon/gun/energy, /obj/item/weapon/gun/projectile, /obj/item/ammo_magazine, /obj/item/ammo_casing, /obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs) + slowdown = 1.5 + armor = list(melee = 65, bullet = 50, laser = 50, energy = 25, bomb = 50, bio = 100, rad = 50) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL + min_pressure_protection = 0 + max_pressure_protection = 2 * ONE_ATMOSPHERE + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0.7 + +//Deathsquad suit +/obj/item/clothing/head/helmet/space/deathsquad + name = "deathsquad helmet" + desc = "That's not red paint. That's real blood." + icon_state = "deathsquad" + item_state_slots = list(slot_r_hand_str = "syndicate-helm-black-red", slot_l_hand_str = "syndicate-helm-black-red") + armor = list(melee = 65, bullet = 55, laser = 35,energy = 20, bomb = 30, bio = 100, rad = 60) + item_flags = THICKMATERIAL + flags_inv = BLOCKHAIR + siemens_coefficient = 0.6 + +//how is this a space helmet? +/obj/item/clothing/head/helmet/space/deathsquad/beret + name = "officer's beret" + desc = "An armored beret commonly used by special operations officers." + icon_state = "beret_badge" + item_state_slots = list(slot_r_hand_str = "beret", slot_l_hand_str = "beret") + armor = list(melee = 65, bullet = 55, laser = 35,energy = 20, bomb = 30, bio = 30, rad = 30) + item_flags = 0 + flags_inv = BLOCKHAIR + siemens_coefficient = 0.9 + +//Space santa outfit suit +/obj/item/clothing/head/helmet/space/santahat + name = "Santa's hat" + desc = "Ho ho ho. Merrry X-mas!" + icon_state = "santahat" + item_flags = 0 + flags_inv = BLOCKHAIR + body_parts_covered = HEAD + +/obj/item/clothing/suit/space/santa + name = "Santa's suit" + desc = "Festive!" + icon_state = "santa" + slowdown = 0 + item_flags = 0 + allowed = list(/obj/item) //for stuffing exta special presents + +//Space pirate outfit +/obj/item/clothing/head/helmet/space/pirate + name = "pirate hat" + desc = "Yarr." + icon_state = "pirate" + armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) + item_flags = 0 + flags_inv = BLOCKHAIR + body_parts_covered = 0 + siemens_coefficient = 0.9 + +/obj/item/clothing/suit/space/pirate //Whhhhyyyyyyy??? + name = "pirate coat" + desc = "Yarr." + icon_state = "pirate" + w_class = ITEMSIZE_NORMAL + allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen) + slowdown = 0 + armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) + siemens_coefficient = 0.9 + flags_inv = HIDETAIL|HIDEHOLSTER + body_parts_covered = UPPER_TORSO|ARMS + +//Orange emergency space suit +/obj/item/clothing/head/helmet/space/emergency + name = "emergency soft helmet" + icon_state = "syndicate-helm-orange" + desc = "A simple helmet with a built in light, smells like mothballs." + flash_protection = FLASH_PROTECTION_NONE + +/obj/item/clothing/suit/space/emergency + name = "emergency softsuit" + icon_state = "syndicate-orange" + desc = "A thin, ungainly softsuit colored in blaze orange for rescuers to easily locate, looks pretty fragile." + slowdown = 2 diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index 85c00e67db7..081f08ed498 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -1,1011 +1,1011 @@ -#define ONLY_DEPLOY 1 -#define ONLY_RETRACT 2 -#define SEAL_DELAY 30 - -/* - * Defines the behavior of hardsuits/rigs/power armour. - */ - -/obj/item/weapon/rig - name = "hardsuit control module" - icon = 'icons/obj/rig_modules.dmi' - desc = "A back-mounted hardsuit deployment and control mechanism." - flags = PHORONGUARD - slot_flags = SLOT_BACK - req_one_access = list() - req_access = list() - w_class = ITEMSIZE_HUGE - action_button_name = "Toggle Heatsink" - - // These values are passed on to all component pieces. - armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 20) - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE - siemens_coefficient = 0.2 - permeability_coefficient = 0.1 - unacidable = TRUE - preserve_item = 1 - - var/default_mob_icon = 'icons/mob/rig_back.dmi' - - var/suit_state //The string used for the suit's icon_state. - - var/interface_path = "RIGSuit" - var/ai_interface_path = "RIGSuit" - var/interface_title = "Hardsuit Controller" - var/wearer_move_delay //Used for AI moving. - var/ai_controlled_move_delay = 10 - - // Keeps track of what this rig should spawn with. - var/suit_type = "hardsuit" - var/list/initial_modules - var/chest_type = /obj/item/clothing/suit/space/rig - var/helm_type = /obj/item/clothing/head/helmet/space/rig - var/boot_type = /obj/item/clothing/shoes/magboots/rig - var/glove_type = /obj/item/clothing/gloves/gauntlets/rig - var/cell_type = /obj/item/weapon/cell/high - var/air_type = /obj/item/weapon/tank/oxygen - - //Component/device holders. - var/obj/item/weapon/tank/air_supply // Air tank, if any. - var/obj/item/clothing/shoes/boots = null // Deployable boots, if any. - var/obj/item/clothing/suit/space/rig/chest // Deployable chestpiece, if any. - var/obj/item/clothing/head/helmet/space/rig/helmet = null // Deployable helmet, if any. - var/obj/item/clothing/gloves/gauntlets/rig/gloves = null // Deployable gauntlets, if any. - var/obj/item/weapon/cell/cell // Power supply, if any. - var/obj/item/rig_module/selected_module = null // Primary system (used with middle-click) - var/obj/item/rig_module/vision/visor // Kinda shitty to have a var for a module, but saves time. - var/obj/item/rig_module/voice/speech // As above. - var/mob/living/carbon/human/wearer // The person currently wearing the rig. - var/image/mob_icon // Holder for on-mob icon. - var/list/installed_modules = list() // Power consumption/use bookkeeping. - - // Cooling system vars. - var/cooling_on = 0 //is it turned on? - var/max_cooling = 15 // in degrees per second - probably don't need to mess with heat capacity here - var/charge_consumption = 2 // charge per second at max_cooling //more effective on a rig, because it's all built in already - var/thermostat = T20C - - // Rig status vars. - var/open = 0 // Access panel status. - var/locked = 1 // Lock status. - var/subverted = 0 - var/interface_locked = 0 - var/control_overridden = 0 - var/ai_override_enabled = 0 - var/security_check_enabled = 1 - var/malfunctioning = 0 - var/malfunction_delay = 0 - var/electrified = 0 - var/locked_down = 0 - - var/seal_delay = SEAL_DELAY - var/sealing // Keeps track of seal status independantly of canremove. - var/offline = 1 // Should we be applying suit maluses? - var/offline_slowdown = 1.5 // If the suit is deployed and unpowered, it sets slowdown to this. - var/vision_restriction - var/offline_vision_restriction = 1 // 0 - none, 1 - welder vision, 2 - blind. Maybe move this to helmets. - var/airtight = 1 //If set, will adjust AIRTIGHT flag and pressure protections on components. Otherwise it should leave them untouched. - var/rigsuit_max_pressure = 10 * ONE_ATMOSPHERE // Max pressure the rig protects against when sealed - var/rigsuit_min_pressure = 0 // Min pressure the rig protects against when sealed - - var/emp_protection = 0 - item_flags = PHORONGUARD //VOREStation add - - // Wiring! How exciting. - var/datum/wires/rig/wires - var/datum/effect/effect/system/spark_spread/spark_system - var/datum/mini_hud/rig/minihud - - // Action button - action_button_name = "Hardsuit Interface" - -/obj/item/weapon/rig/New() - ..() - - suit_state = icon_state - item_state = icon_state - wires = new(src) - - if(!LAZYLEN(req_access) && !LAZYLEN(req_one_access)) - locked = 0 - - spark_system = new() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - if(initial_modules && initial_modules.len) - for(var/path in initial_modules) - var/obj/item/rig_module/module = new path(src) - installed_modules += module - module.installed(src) - - // Create and initialize our various segments. - if(cell_type) - cell = new cell_type(src) - if(air_type) - air_supply = new air_type(src) - if(glove_type) - gloves = new glove_type(src) - verbs |= /obj/item/weapon/rig/proc/toggle_gauntlets - if(helm_type) - helmet = new helm_type(src) - verbs |= /obj/item/weapon/rig/proc/toggle_helmet - if(boot_type) - boots = new boot_type(src) - verbs |= /obj/item/weapon/rig/proc/toggle_boots - if(chest_type) - chest = new chest_type(src) - if(allowed) - chest.allowed = allowed - verbs |= /obj/item/weapon/rig/proc/toggle_chest - - for(var/obj/item/piece in list(gloves,helmet,boots,chest)) - if(!istype(piece)) - continue - piece.canremove = FALSE - piece.name = "[suit_type] [initial(piece.name)]" - piece.desc = "It seems to be part of a [src.name]." - piece.icon_state = "[suit_state]" - piece.min_cold_protection_temperature = min_cold_protection_temperature - piece.max_heat_protection_temperature = max_heat_protection_temperature - if(piece.siemens_coefficient > siemens_coefficient) //So that insulated gloves keep their insulation. - piece.siemens_coefficient = siemens_coefficient - piece.permeability_coefficient = permeability_coefficient - piece.unacidable = unacidable - if(islist(armor)) piece.armor = armor.Copy() - if(islist(armorsoak)) piece.armorsoak = armorsoak.Copy() - - update_icon(1) - -/obj/item/weapon/rig/Destroy() - for(var/obj/item/piece in list(gloves,boots,helmet,chest)) - var/mob/living/M = piece.loc - if(istype(M)) - M.drop_from_inventory(piece) - qdel(piece) - STOP_PROCESSING(SSobj, src) - qdel(wires) - wires = null - qdel(spark_system) - spark_system = null - return ..() - -/obj/item/weapon/rig/examine() - . = ..() - if(wearer) - for(var/obj/item/piece in list(helmet,gloves,chest,boots)) - if(!piece || piece.loc != wearer) - continue - . += "\icon[piece][bicon(piece)] \The [piece] [piece.gender == PLURAL ? "are" : "is"] deployed." - - if(src.loc == usr) - . += "The access panel is [locked? "locked" : "unlocked"]." - . += "The maintenance panel is [open ? "open" : "closed"]." - . += "Hardsuit systems are [offline ? "offline" : "online"]." - . += "The cooling system is [cooling_on ? "active" : "inactive"]." - - if(open) - . += "It's equipped with [english_list(installed_modules)]." - -// We only care about processing when we're on a mob -/obj/item/weapon/rig/Moved(old_loc, direction, forced) - if(ismob(loc)) - START_PROCESSING(SSobj, src) - else - STOP_PROCESSING(SSobj, src) - QDEL_NULL(minihud) // Just in case we get removed some other way - - // If we've lost any parts, grab them back. - var/mob/living/M - for(var/obj/item/piece in list(gloves,boots,helmet,chest)) - if(piece.loc != src && !(wearer && piece.loc == wearer)) - if(istype(piece.loc, /mob/living)) - M = piece.loc - M.unEquip(piece) - piece.forceMove(src) - -/obj/item/weapon/rig/get_worn_icon_file(var/body_type,var/slot_name,var/default_icon,var/inhands) - if(!inhands && (slot_name == slot_back_str || slot_name == slot_belt_str)) - if(icon_override) - return icon_override - else if(mob_icon) - return mob_icon - - return ..() - -/obj/item/weapon/rig/proc/suit_is_deployed() - if(!istype(wearer) || src.loc != wearer || (wearer.back != src && wearer.belt != src)) - return 0 - if(helm_type && !(helmet && wearer.head == helmet)) - return 0 - if(glove_type && !(gloves && wearer.gloves == gloves)) - return 0 - if(boot_type && !(boots && wearer.shoes == boots)) - return 0 - if(chest_type && !(chest && wearer.wear_suit == chest)) - return 0 - return 1 - -// Updates pressure protection -// Seal = 1 sets protection -// Seal = 0 unsets protection -/obj/item/weapon/rig/proc/update_airtight(var/obj/item/piece, var/seal = 0) - if(seal == 1) - piece.min_pressure_protection = rigsuit_min_pressure - piece.max_pressure_protection = rigsuit_max_pressure - piece.item_flags |= AIRTIGHT - else - piece.min_pressure_protection = null - piece.max_pressure_protection = null - piece.item_flags &= ~AIRTIGHT - return - - -/obj/item/weapon/rig/proc/reset() - offline = 2 - canremove = TRUE - for(var/obj/item/piece in list(helmet,boots,gloves,chest)) - if(!piece) continue - piece.icon_state = "[suit_state]" - if(airtight) - update_airtight(piece, 0) // Unseal - update_icon(1) - -/obj/item/weapon/rig/proc/cut_suit() - offline = 2 - canremove = TRUE - toggle_piece("helmet", loc, ONLY_RETRACT, TRUE) - toggle_piece("gauntlets", loc, ONLY_RETRACT, TRUE) - toggle_piece("boots", loc, ONLY_RETRACT, TRUE) - toggle_piece("chest", loc, ONLY_RETRACT, TRUE) - update_icon(1) - -/obj/item/weapon/rig/proc/toggle_seals(var/mob/living/carbon/human/M,var/instant) - - if(sealing) return - - if(!check_power_cost(M)) - return 0 - - deploy(M,instant) - - var/seal_target = !canremove - var/failed_to_seal - - var/obj/screen/rig_booting/booting_L = new - var/obj/screen/rig_booting/booting_R = new - - if(!seal_target) - booting_L.icon_state = "boot_left" - booting_R.icon_state = "boot_load" - animate(booting_L, alpha=230, time=30, easing=SINE_EASING) - animate(booting_R, alpha=200, time=20, easing=SINE_EASING) - M.client?.screen += booting_L - M.client?.screen += booting_R - - canremove = FALSE // No removing the suit while unsealing. - sealing = 1 - - if(!seal_target && !suit_is_deployed()) - M.visible_message("[M]'s suit flashes an error light.","Your suit flashes an error light. It can't function properly without being fully deployed.") - playsound(src, 'sound/machines/rig/rigerror.ogg', 20, FALSE) - failed_to_seal = 1 - - if(!failed_to_seal) - - if(!instant) - M.visible_message("[M]'s suit emits a quiet hum as it begins to adjust its seals.","With a quiet hum, the suit begins running checks and adjusting components.") - if(seal_delay && !do_after(M,seal_delay)) - if(M) - to_chat(M, "You must remain still while the suit is adjusting the components.") - playsound(src, 'sound/machines/rig/rigerror.ogg', 20, FALSE) - failed_to_seal = 1 - if(!M) - failed_to_seal = 1 - else - for(var/list/piece_data in list(list(M.shoes,boots,"boots",boot_type),list(M.gloves,gloves,"gloves",glove_type),list(M.head,helmet,"helmet",helm_type),list(M.wear_suit,chest,"chest",chest_type))) - - var/obj/item/piece = piece_data[1] - var/obj/item/compare_piece = piece_data[2] - var/msg_type = piece_data[3] - var/piece_type = piece_data[4] - - if(!piece || !piece_type) - continue - - if(!istype(M) || !istype(piece) || !istype(compare_piece) || !msg_type) - if(M) - to_chat(M, "You must remain still while the suit is adjusting the components.") - failed_to_seal = 1 - break - - if(!failed_to_seal && (M.back == src || M.belt == src) && piece == compare_piece) - - if(seal_delay && !instant && !do_after(M,seal_delay,needhand=0)) - failed_to_seal = 1 - - piece.icon_state = "[suit_state][!seal_target ? "_sealed" : ""]" - switch(msg_type) - if("boots") - to_chat(M, "\The [piece] [!seal_target ? "seal around your feet" : "relax their grip on your legs"].") - M.update_inv_shoes() - if("gloves") - to_chat(M, "\The [piece] [!seal_target ? "tighten around your fingers and wrists" : "become loose around your fingers"].") - M.update_inv_gloves() - if("chest") - to_chat(M, "\The [piece] [!seal_target ? "cinches tight again your chest" : "releases your chest"].") - M.update_inv_wear_suit() - if("helmet") - to_chat(M, "\The [piece] hisses [!seal_target ? "closed" : "open"].") - M.update_inv_head() - if(helmet?.light_system == STATIC_LIGHT) - helmet.update_light(wearer) - - //sealed pieces become airtight, protecting against diseases - if (!seal_target) - piece.armor["bio"] = 100 - else - piece.armor["bio"] = src.armor["bio"] - playsound(src,'sound/machines/rig/rigservo.ogg', 10, FALSE) - - else - failed_to_seal = 1 - - if((M && !(istype(M) && (M.back == src || M.belt == src)) && !istype(M,/mob/living/silicon)) || (!seal_target && !suit_is_deployed())) - failed_to_seal = 1 - - sealing = null - - if(failed_to_seal) - M.client?.screen -= booting_L - M.client?.screen -= booting_R - qdel(booting_L) - qdel(booting_R) - for(var/obj/item/piece in list(helmet,boots,gloves,chest)) - if(!piece) continue - piece.icon_state = "[suit_state][!seal_target ? "" : "_sealed"]" - canremove = !seal_target - if(airtight) - update_component_sealed() - update_icon(1) - return 0 - - // Success! - canremove = seal_target - if(M.hud_used) - if(canremove) - QDEL_NULL(minihud) - else - minihud = new (M.hud_used, src) - to_chat(M, "Your entire suit [canremove ? "loosens as the components relax" : "tightens around you as the components lock into place"].") - playsound(src, 'sound/machines/rig/rigstarted.ogg', 10, FALSE) - M.client?.screen -= booting_L - qdel(booting_L) - booting_R.icon_state = "boot_done" - spawn(40) - M.client?.screen -= booting_R - qdel(booting_R) - - if(canremove) - for(var/obj/item/rig_module/module in installed_modules) - module.deactivate() - if(airtight) - update_component_sealed() - update_icon(1) - -/obj/item/weapon/rig/proc/update_component_sealed() - for(var/obj/item/piece in list(helmet,boots,gloves,chest)) - if(canremove) - update_airtight(piece, 0) // Unseal - else - update_airtight(piece, 1) // Seal - -/obj/item/weapon/rig/ui_action_click() - toggle_cooling(usr) - -/obj/item/weapon/rig/proc/toggle_cooling(var/mob/user) - if(cooling_on) - turn_cooling_off(user) - else - turn_cooling_on(user) - -/obj/item/weapon/rig/proc/turn_cooling_on(var/mob/user) - if(!cell) - return - if(cell.charge <= 0) - to_chat(user, "\The [src] has no power!") - return - if(!suit_is_deployed()) - to_chat(user, "The hardsuit needs to be deployed first!") - return - - cooling_on = 1 - to_chat(usr, "You switch \the [src]'s cooling system on.") - - -/obj/item/weapon/rig/proc/turn_cooling_off(var/mob/user, var/failed) - if(failed) - visible_message("\The [src]'s cooling system clicks and whines as it powers down.") - else - to_chat(usr, "You switch \the [src]'s cooling system off.") - cooling_on = 0 - -/obj/item/weapon/rig/proc/get_environment_temperature() - if (ishuman(loc)) - var/mob/living/carbon/human/H = loc - if(istype(H.loc, /obj/mecha)) - var/obj/mecha/M = H.loc - return M.return_temperature() - else if(istype(H.loc, /obj/machinery/atmospherics/unary/cryo_cell)) - var/obj/machinery/atmospherics/unary/cryo_cell/cryo = H.loc - return cryo.air_contents.temperature - - var/turf/T = get_turf(src) - if(istype(T, /turf/space)) - return 0 //space has no temperature, this just makes sure the cooling unit works in space - - var/datum/gas_mixture/environment = T.return_air() - if (!environment) - return 0 - - return environment.temperature - -/obj/item/weapon/rig/proc/attached_to_user(mob/M) - if (!ishuman(M)) - return 0 - - var/mob/living/carbon/human/H = M - - if (!H.wear_suit || (H.back != src && H.belt != src)) - return 0 - - return 1 - -/obj/item/weapon/rig/proc/coolingProcess() - if (!cooling_on || !cell) - return - - if (!ismob(loc)) - return - - if (!attached_to_user(loc)) //make sure the rig's not just in their hands - return - - if (!suit_is_deployed()) //inbuilt systems only work on the suit they're designed to work on - return - - var/mob/living/carbon/human/H = loc - - var/turf/T = get_turf(src) - var/datum/gas_mixture/environment = T.return_air() - var/efficiency = 1 - H.get_pressure_weakness(environment.return_pressure()) // You need to have a good seal for effective cooling - var/env_temp = get_environment_temperature() //wont save you from a fire - var/temp_adj = min(H.bodytemperature - max(thermostat, env_temp), max_cooling) - var/thermal_protection = H.get_heat_protection(env_temp) // ... unless you've got a good suit. - - if(thermal_protection < 0.99) //For some reason, < 1 returns false if the value is 1. - temp_adj = min(H.bodytemperature - max(thermostat, env_temp), max_cooling) - else - temp_adj = min(H.bodytemperature - thermostat, max_cooling) - - if (temp_adj < 0.5) //only cools, doesn't heat, also we don't need extreme precision - return - - var/charge_usage = (temp_adj/max_cooling)*charge_consumption - - H.bodytemperature -= temp_adj*efficiency - - cell.use(charge_usage) - - if(cell.charge <= 0) - turn_cooling_off(H, 1) - -/obj/item/weapon/rig/process() - // Not on a mob...? - if(!ismob(loc)) - if(wearer?.wearing_rig == src) - wearer.wearing_rig = null - wearer = null - return PROCESS_KILL - - // Run through cooling - coolingProcess() - - if(!istype(wearer) || loc != wearer || (wearer.back != src && wearer.belt != src) || canremove || !cell || cell.charge <= 0) - if(!cell || cell.charge <= 0) - if(electrified > 0) - electrified = 0 - if(!offline) - if(istype(wearer)) - if(!canremove) - if (offline_slowdown < 1.5) - to_chat(wearer, "Your suit beeps stridently, and suddenly goes dead.") - else - to_chat(wearer, "Your suit beeps stridently, and suddenly you're wearing a leaden mass of metal and plastic composites instead of a powered suit.") - playsound(src, 'sound/machines/rig/rigdown.ogg', 60, FALSE) - if(offline_vision_restriction == 1) - to_chat(wearer, "The suit optics flicker and die, leaving you with restricted vision.") - else if(offline_vision_restriction == 2) - to_chat(wearer, "The suit optics drop out completely, drowning you in darkness.") - if(!offline) - offline = 1 - else - if(offline) - offline = 0 - if(istype(wearer) && !wearer.wearing_rig) - wearer.wearing_rig = src - slowdown = initial(slowdown) - - if(offline) - if(offline == 1) - for(var/obj/item/rig_module/module in installed_modules) - module.deactivate() - offline = 2 - slowdown = offline_slowdown - return - - if(cell && cell.charge > 0 && electrified > 0) - electrified-- - - if(malfunction_delay > 0) - malfunction_delay-- - else if(malfunctioning) - malfunctioning-- - malfunction() - - for(var/obj/item/rig_module/module in installed_modules) - cell.use(module.process()*10) - -/obj/item/weapon/rig/proc/check_power_cost(var/mob/living/user, var/cost, var/use_unconcious, var/obj/item/rig_module/mod, var/user_is_ai) - - if(!istype(user)) - return 0 - - var/fail_msg - - if(!user_is_ai) - var/mob/living/carbon/human/H = user - if(istype(H) && (H.back != src && H.belt != src)) - fail_msg = "You must be wearing \the [src] to do this." - else if(user.incorporeal_move) - fail_msg = "You must be solid to do this." - if(sealing) - fail_msg = "The hardsuit is in the process of adjusting seals and cannot be activated." - else if(!fail_msg && ((use_unconcious && user.stat > 1) || (!use_unconcious && user.stat))) - fail_msg = "You are in no fit state to do that." - else if(!cell) - fail_msg = "There is no cell installed in the suit." - else if(cost && cell.charge < cost * 10) //TODO: Cellrate? - fail_msg = "Not enough stored power." - - if(fail_msg) - to_chat(user, fail_msg) - playsound(src, 'sound/machines/rig/rigerror.ogg', 20, FALSE) - return 0 - - // This is largely for cancelling stealth and whatever. - if(mod && mod.disruptive) - for(var/obj/item/rig_module/module in (installed_modules - mod)) - if(module.active && module.disruptable) - module.deactivate() - - cell.use(cost*10) - return 1 - -/obj/item/weapon/rig/update_icon(var/update_mob_icon) - - cut_overlays() - if(!mob_icon || update_mob_icon) - var/species_icon = default_mob_icon - // Since setting mob_icon will override the species checks in - // update_inv_wear_suit(), handle species checks here. - if(wearer && LAZYACCESS(sprite_sheets, wearer.species.get_bodytype(wearer))) - species_icon = sprite_sheets[wearer.species.get_bodytype(wearer)] - mob_icon = icon(icon = species_icon, icon_state = "[icon_state]") - - if(installed_modules.len) - for(var/obj/item/rig_module/module in installed_modules) - if(module.suit_overlay) - chest.add_overlay(image(module.suit_overlay_icon, icon_state = "[module.suit_overlay]", dir = SOUTH)) - - if(wearer) - wearer.update_inv_shoes() - wearer.update_inv_gloves() - wearer.update_inv_head() - wearer.update_inv_wear_suit() - wearer.update_inv_back() - return - -/obj/item/weapon/rig/proc/check_suit_access(var/mob/living/carbon/human/user, var/do_message = TRUE) - - if(!security_check_enabled) - return 1 - - if(istype(user)) - if(!canremove) - return 1 - if(malfunction_check(user)) - return 0 - if(user.back != src && user.belt != src) - return 0 - else if(!src.allowed(user)) - if(do_message) - to_chat(user, "Unauthorized user. Access denied.") - return 0 - - else if(!ai_override_enabled) - if(do_message) - to_chat(user, "Synthetic access disabled. Please consult hardware provider.") - return 0 - - return 1 - -/obj/item/weapon/rig/proc/notify_ai(var/message) - for(var/obj/item/rig_module/ai_container/module in installed_modules) - if(module.integrated_ai && module.integrated_ai.client && !module.integrated_ai.stat) - to_chat(module.integrated_ai, "[message]") - . = 1 - -/obj/item/weapon/rig/equipped(mob/living/carbon/human/M) - ..() - - if(istype(M.back, /obj/item/weapon/rig) && istype(M.belt, /obj/item/weapon/rig)) - to_chat(M, "You try to put on the [src], but it won't fit.") - if(M && (M.back == src || M.belt == src)) - if(!M.unEquip(src)) - return - src.forceMove(get_turf(src)) - return - - if(seal_delay > 0 && istype(M) && (M.back == src || M.belt == src)) - M.visible_message("[M] starts putting on \the [src]...", "You start putting on \the [src]...") - if(!do_after(M,seal_delay)) - if(M && (M.back == src || M.belt == src)) - if(!M.unEquip(src)) - return - src.forceMove(get_turf(src)) - return - - if(istype(M) && (M.back == src || M.belt == src)) - M.visible_message("[M] struggles into \the [src].", "You struggle into \the [src].") - wearer = M - wearer.wearing_rig = src - update_icon() - -/obj/item/weapon/rig/proc/toggle_piece(var/piece, var/mob/living/carbon/human/H, var/deploy_mode, var/forced = FALSE) - - if((sealing || !cell || !cell.charge) && !forced) - return - - if((!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) && !forced) - return - - if((usr == wearer && (usr.stat||usr.paralysis||usr.stunned)) && !forced) // If the usr isn't wearing the suit it's probably an AI. - return - - var/obj/item/check_slot - var/equip_to - var/obj/item/use_obj - - if(!H) - return - - switch(piece) - if("helmet") - equip_to = slot_head - use_obj = helmet - check_slot = H.head - if("gauntlets") - equip_to = slot_gloves - use_obj = gloves - check_slot = H.gloves - if("boots") - equip_to = slot_shoes - use_obj = boots - check_slot = H.shoes - if("chest") - equip_to = slot_wear_suit - use_obj = chest - check_slot = H.wear_suit - - if(use_obj) - if(check_slot == use_obj && deploy_mode != ONLY_DEPLOY) - - var/mob/living/carbon/human/holder - - if(use_obj) - holder = use_obj.loc - if(istype(holder)) - if(use_obj && check_slot == use_obj) - to_chat(H, "Your [use_obj.name] [use_obj.gender == PLURAL ? "retract" : "retracts"] swiftly.") - playsound(src, 'sound/machines/rig/rigservo.ogg', 10, FALSE) - use_obj.canremove = TRUE - holder.drop_from_inventory(use_obj) - use_obj.forceMove(get_turf(src)) - use_obj.dropped() - use_obj.canremove = FALSE - use_obj.forceMove(src) - - else if (deploy_mode != ONLY_RETRACT) - if(check_slot && check_slot == use_obj) - return - use_obj.forceMove(H) - if(!H.equip_to_slot_if_possible(use_obj, equip_to, 0, 1)) - use_obj.forceMove(src) - if(check_slot) - to_chat(H, "You are unable to deploy \the [piece] as \the [check_slot] [check_slot.gender == PLURAL ? "are" : "is"] in the way.") - return - else - to_chat(H, "Your [use_obj.name] [use_obj.gender == PLURAL ? "deploy" : "deploys"] swiftly.") - playsound(src, 'sound/machines/rig/rigservo.ogg', 10, FALSE) - - if(piece == "helmet" && helmet?.light_system == STATIC_LIGHT) - helmet.update_light() - -/obj/item/weapon/rig/proc/deploy(mob/M,var/sealed) - - var/mob/living/carbon/human/H = M - - if(!H || !istype(H)) return - - if(H.back != src && H.belt != src) - return - - if(sealed) - if(H.head) - var/obj/item/garbage = H.head - H.drop_from_inventory(garbage) - H.head = null - qdel(garbage) - - if(H.gloves) - var/obj/item/garbage = H.gloves - H.drop_from_inventory(garbage) - H.gloves = null - qdel(garbage) - - if(H.shoes) - var/obj/item/garbage = H.shoes - H.drop_from_inventory(garbage) - H.shoes = null - qdel(garbage) - - if(H.wear_suit) - var/obj/item/garbage = H.wear_suit - H.drop_from_inventory(garbage) - H.wear_suit = null - qdel(garbage) - - for(var/piece in list("helmet","gauntlets","chest","boots")) - toggle_piece(piece, H, ONLY_DEPLOY) - -/obj/item/weapon/rig/dropped(var/mob/user) - ..() - for(var/piece in list("helmet","gauntlets","chest","boots")) - toggle_piece(piece, user, ONLY_RETRACT) - if(wearer && wearer.wearing_rig == src) - wearer.wearing_rig = null - wearer = null - -//Todo -/obj/item/weapon/rig/proc/malfunction() - return 0 - -/obj/item/weapon/rig/emp_act(severity_class) - //set malfunctioning - if(emp_protection < 30) //for ninjas, really. - malfunctioning += 10 - if(malfunction_delay <= 0) - malfunction_delay = max(malfunction_delay, round(30/severity_class)) - - //drain some charge - if(cell) cell.emp_act(severity_class + 15) - - //possibly damage some modules - take_hit((100/severity_class), "electrical pulse", 1) - -/obj/item/weapon/rig/proc/shock(mob/user) - if (electrocute_mob(user, cell, src)) //electrocute_mob() handles removing charge from the cell, no need to do that here. - spark_system.start() - if(user.stunned) - return 1 - return 0 - -/obj/item/weapon/rig/proc/take_hit(damage, source, is_emp=0) - - if(!installed_modules.len) - return - - var/chance - if(!is_emp) - chance = 2*max(0, damage - (chest? chest.breach_threshold : 0)) - else - //Want this to be roughly independant of the number of modules, meaning that X emp hits will disable Y% of the suit's modules on average. - //that way people designing hardsuits don't have to worry (as much) about how adding that extra module will affect emp resiliance by 'soaking' hits for other modules - chance = 2*max(0, damage - emp_protection)*min(installed_modules.len/15, 1) - - if(!prob(chance)) - return - - //deal addition damage to already damaged module first. - //This way the chances of a module being disabled aren't so remote. - var/list/valid_modules = list() - var/list/damaged_modules = list() - for(var/obj/item/rig_module/module in installed_modules) - if(module.damage < 2) - valid_modules |= module - if(module.damage > 0) - damaged_modules |= module - - var/obj/item/rig_module/dam_module = null - if(damaged_modules.len) - dam_module = pick(damaged_modules) - else if(valid_modules.len) - dam_module = pick(valid_modules) - - if(!dam_module) return - - dam_module.damage++ - - if(!source) - source = "hit" - - if(wearer) - if(dam_module.damage >= 2) - to_chat(wearer, "The [source] has disabled your [dam_module.interface_name]!") - else - to_chat(wearer, "The [source] has damaged your [dam_module.interface_name]!") - dam_module.deactivate() - -/obj/item/weapon/rig/proc/malfunction_check(var/mob/living/carbon/human/user) - if(malfunction_delay) - if(offline) - to_chat(user, "The suit is completely unresponsive.") - else - to_chat(user, "ERROR: Hardware fault. Rebooting interface...") - return 1 - return 0 - -/obj/item/weapon/rig/proc/ai_can_move_suit(var/mob/user, var/check_user_module = 0, var/check_for_ai = 0) - - if(check_for_ai) - if(!(locate(/obj/item/rig_module/ai_container) in contents)) - return 0 - var/found_ai - for(var/obj/item/rig_module/ai_container/module in contents) - if(module.damage >= 2) - continue - if(module.integrated_ai && module.integrated_ai.client && !module.integrated_ai.stat) - found_ai = 1 - break - if(!found_ai) - return 0 - - if(check_user_module) - if(!user || !user.loc || !user.loc.loc) - return 0 - var/obj/item/rig_module/ai_container/module = user.loc.loc - if(!istype(module) || module.damage >= 2) - to_chat(user, "Your host module is unable to interface with the suit.") - return 0 - - if(offline || !cell || !cell.charge || locked_down) - if(user) - to_chat(user, "Your host rig is unpowered and unresponsive.") - return 0 - if(!wearer || (wearer.back != src && wearer.belt != src)) - if(user) - to_chat(user, "Your host rig is not being worn.") - return 0 - if(!wearer.stat && !control_overridden && !ai_override_enabled) - if(user) - to_chat(user, "You are locked out of the suit servo controller.") - return 0 - return 1 - -/obj/item/weapon/rig/proc/force_rest(var/mob/user) - if(!ai_can_move_suit(user, check_user_module = 1)) - return - wearer.lay_down() - to_chat(user, "\The [wearer] is now [wearer.resting ? "resting" : "getting up"].") - -/obj/item/weapon/rig/proc/forced_move(var/direction, var/mob/user) - - // Why is all this shit in client/Move()? Who knows? - if(world.time < wearer_move_delay) - return - - if(!wearer || !wearer.loc || !ai_can_move_suit(user, check_user_module = 1)) - return - - //This is sota the goto stop mobs from moving var - if(wearer.transforming || !wearer.canmove) - return - - if((istype(wearer.loc, /turf/space)) || (wearer.lastarea.has_gravity == 0)) - if(!wearer.Process_Spacemove(0)) - return 0 - - if(malfunctioning) - direction = pick(cardinal) - - // Inside an object, tell it we moved. - if(isobj(wearer.loc) || ismob(wearer.loc)) - var/atom/O = wearer.loc - return O.relaymove(wearer, direction) - - if(isturf(wearer.loc)) - if(wearer.restrained())//Why being pulled while cuffed prevents you from moving - for(var/mob/M in range(wearer, 1)) - if(M.pulling == wearer) - if(!M.restrained() && M.stat == 0 && M.canmove && wearer.Adjacent(M)) - to_chat(user, "Your host is restrained! They can't move!") - return 0 - else - M.stop_pulling() - - if(wearer.pinned.len) - to_chat(src, "Your host is pinned to a wall by [wearer.pinned[1]]!") - return 0 - - // AIs are a bit slower than regular and ignore move intent. - wearer_move_delay = world.time + ai_controlled_move_delay - - if(istype(wearer.buckled, /obj/vehicle)) - //manually set move_delay for vehicles so we don't inherit any mob movement penalties - //specific vehicle move delays are set in code\modules\vehicles\vehicle.dm - wearer_move_delay = world.time - return wearer.buckled.relaymove(wearer, direction) - - if(istype(wearer.machine, /obj/machinery)) - if(wearer.machine.relaymove(wearer, direction)) - return - - if(wearer.pulledby || wearer.buckled) // Wheelchair driving! - if(istype(wearer.loc, /turf/space)) - return // No wheelchair driving in space - if(istype(wearer.pulledby, /obj/structure/bed/chair/wheelchair)) - return wearer.pulledby.relaymove(wearer, direction) - else if(istype(wearer.buckled, /obj/structure/bed/chair/wheelchair)) - if(ishuman(wearer.buckled)) - var/obj/item/organ/external/l_hand = wearer.get_organ("l_hand") - var/obj/item/organ/external/r_hand = wearer.get_organ("r_hand") - if((!l_hand || (l_hand.status & ORGAN_DESTROYED)) && (!r_hand || (r_hand.status & ORGAN_DESTROYED))) - return // No hands to drive your chair? Tough luck! - wearer_move_delay += 2 - return wearer.buckled.relaymove(wearer,direction) - - cell.use(200) //Arbitrary, TODO - wearer.Move(get_step(get_turf(wearer),direction),direction) - -// This returns the rig if you are contained inside one, but not if you are wearing it -/atom/proc/get_rig() - if(loc) - return loc.get_rig() - return null - -/obj/item/weapon/rig/get_rig() - return src - -/mob/living/carbon/human/get_rig() - if(istype(back, /obj/item/weapon/rig)) - return back - else if(istype(belt, /obj/item/weapon/rig)) - return belt - else - return null - -//Boot animation screen objects -/obj/screen/rig_booting - screen_loc = "1,1" - icon = 'icons/obj/rig_boot.dmi' - icon_state = "" - layer = SCREEN_LAYER - plane = PLANE_FULLSCREEN - mouse_opacity = 0 - alpha = 20 //Animated up when loading - -#undef ONLY_DEPLOY -#undef ONLY_RETRACT -#undef SEAL_DELAY +#define ONLY_DEPLOY 1 +#define ONLY_RETRACT 2 +#define SEAL_DELAY 30 + +/* + * Defines the behavior of hardsuits/rigs/power armour. + */ + +/obj/item/weapon/rig + name = "hardsuit control module" + icon = 'icons/obj/rig_modules.dmi' + desc = "A back-mounted hardsuit deployment and control mechanism." + flags = PHORONGUARD + slot_flags = SLOT_BACK + req_one_access = list() + req_access = list() + w_class = ITEMSIZE_HUGE + action_button_name = "Toggle Heatsink" + + // These values are passed on to all component pieces. + armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 20) + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE + siemens_coefficient = 0.2 + permeability_coefficient = 0.1 + unacidable = TRUE + preserve_item = 1 + + var/default_mob_icon = 'icons/mob/rig_back.dmi' + + var/suit_state //The string used for the suit's icon_state. + + var/interface_path = "RIGSuit" + var/ai_interface_path = "RIGSuit" + var/interface_title = "Hardsuit Controller" + var/wearer_move_delay //Used for AI moving. + var/ai_controlled_move_delay = 10 + + // Keeps track of what this rig should spawn with. + var/suit_type = "hardsuit" + var/list/initial_modules + var/chest_type = /obj/item/clothing/suit/space/rig + var/helm_type = /obj/item/clothing/head/helmet/space/rig + var/boot_type = /obj/item/clothing/shoes/magboots/rig + var/glove_type = /obj/item/clothing/gloves/gauntlets/rig + var/cell_type = /obj/item/weapon/cell/high + var/air_type = /obj/item/weapon/tank/oxygen + + //Component/device holders. + var/obj/item/weapon/tank/air_supply // Air tank, if any. + var/obj/item/clothing/shoes/boots = null // Deployable boots, if any. + var/obj/item/clothing/suit/space/rig/chest // Deployable chestpiece, if any. + var/obj/item/clothing/head/helmet/space/rig/helmet = null // Deployable helmet, if any. + var/obj/item/clothing/gloves/gauntlets/rig/gloves = null // Deployable gauntlets, if any. + var/obj/item/weapon/cell/cell // Power supply, if any. + var/obj/item/rig_module/selected_module = null // Primary system (used with middle-click) + var/obj/item/rig_module/vision/visor // Kinda shitty to have a var for a module, but saves time. + var/obj/item/rig_module/voice/speech // As above. + var/mob/living/carbon/human/wearer // The person currently wearing the rig. + var/image/mob_icon // Holder for on-mob icon. + var/list/installed_modules = list() // Power consumption/use bookkeeping. + + // Cooling system vars. + var/cooling_on = 0 //is it turned on? + var/max_cooling = 15 // in degrees per second - probably don't need to mess with heat capacity here + var/charge_consumption = 2 // charge per second at max_cooling //more effective on a rig, because it's all built in already + var/thermostat = T20C + + // Rig status vars. + var/open = 0 // Access panel status. + var/locked = 1 // Lock status. + var/subverted = 0 + var/interface_locked = 0 + var/control_overridden = 0 + var/ai_override_enabled = 0 + var/security_check_enabled = 1 + var/malfunctioning = 0 + var/malfunction_delay = 0 + var/electrified = 0 + var/locked_down = 0 + + var/seal_delay = SEAL_DELAY + var/sealing // Keeps track of seal status independantly of canremove. + var/offline = 1 // Should we be applying suit maluses? + var/offline_slowdown = 1.5 // If the suit is deployed and unpowered, it sets slowdown to this. + var/vision_restriction + var/offline_vision_restriction = 1 // 0 - none, 1 - welder vision, 2 - blind. Maybe move this to helmets. + var/airtight = 1 //If set, will adjust AIRTIGHT flag and pressure protections on components. Otherwise it should leave them untouched. + var/rigsuit_max_pressure = 10 * ONE_ATMOSPHERE // Max pressure the rig protects against when sealed + var/rigsuit_min_pressure = 0 // Min pressure the rig protects against when sealed + + var/emp_protection = 0 + item_flags = PHORONGUARD //VOREStation add + + // Wiring! How exciting. + var/datum/wires/rig/wires + var/datum/effect/effect/system/spark_spread/spark_system + var/datum/mini_hud/rig/minihud + + // Action button + action_button_name = "Hardsuit Interface" + +/obj/item/weapon/rig/New() + ..() + + suit_state = icon_state + item_state = icon_state + wires = new(src) + + if(!LAZYLEN(req_access) && !LAZYLEN(req_one_access)) + locked = 0 + + spark_system = new() + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + if(initial_modules && initial_modules.len) + for(var/path in initial_modules) + var/obj/item/rig_module/module = new path(src) + installed_modules += module + module.installed(src) + + // Create and initialize our various segments. + if(cell_type) + cell = new cell_type(src) + if(air_type) + air_supply = new air_type(src) + if(glove_type) + gloves = new glove_type(src) + verbs |= /obj/item/weapon/rig/proc/toggle_gauntlets + if(helm_type) + helmet = new helm_type(src) + verbs |= /obj/item/weapon/rig/proc/toggle_helmet + if(boot_type) + boots = new boot_type(src) + verbs |= /obj/item/weapon/rig/proc/toggle_boots + if(chest_type) + chest = new chest_type(src) + if(allowed) + chest.allowed = allowed + verbs |= /obj/item/weapon/rig/proc/toggle_chest + + for(var/obj/item/piece in list(gloves,helmet,boots,chest)) + if(!istype(piece)) + continue + piece.canremove = FALSE + piece.name = "[suit_type] [initial(piece.name)]" + piece.desc = "It seems to be part of a [src.name]." + piece.icon_state = "[suit_state]" + piece.min_cold_protection_temperature = min_cold_protection_temperature + piece.max_heat_protection_temperature = max_heat_protection_temperature + if(piece.siemens_coefficient > siemens_coefficient) //So that insulated gloves keep their insulation. + piece.siemens_coefficient = siemens_coefficient + piece.permeability_coefficient = permeability_coefficient + piece.unacidable = unacidable + if(islist(armor)) piece.armor = armor.Copy() + if(islist(armorsoak)) piece.armorsoak = armorsoak.Copy() + + update_icon(1) + +/obj/item/weapon/rig/Destroy() + for(var/obj/item/piece in list(gloves,boots,helmet,chest)) + var/mob/living/M = piece.loc + if(istype(M)) + M.drop_from_inventory(piece) + qdel(piece) + STOP_PROCESSING(SSobj, src) + qdel(wires) + wires = null + qdel(spark_system) + spark_system = null + return ..() + +/obj/item/weapon/rig/examine() + . = ..() + if(wearer) + for(var/obj/item/piece in list(helmet,gloves,chest,boots)) + if(!piece || piece.loc != wearer) + continue + . += "\icon[piece][bicon(piece)] \The [piece] [piece.gender == PLURAL ? "are" : "is"] deployed." + + if(src.loc == usr) + . += "The access panel is [locked? "locked" : "unlocked"]." + . += "The maintenance panel is [open ? "open" : "closed"]." + . += "Hardsuit systems are [offline ? "offline" : "online"]." + . += "The cooling system is [cooling_on ? "active" : "inactive"]." + + if(open) + . += "It's equipped with [english_list(installed_modules)]." + +// We only care about processing when we're on a mob +/obj/item/weapon/rig/Moved(old_loc, direction, forced) + if(ismob(loc)) + START_PROCESSING(SSobj, src) + else + STOP_PROCESSING(SSobj, src) + QDEL_NULL(minihud) // Just in case we get removed some other way + + // If we've lost any parts, grab them back. + var/mob/living/M + for(var/obj/item/piece in list(gloves,boots,helmet,chest)) + if(piece.loc != src && !(wearer && piece.loc == wearer)) + if(istype(piece.loc, /mob/living)) + M = piece.loc + M.unEquip(piece) + piece.forceMove(src) + +/obj/item/weapon/rig/get_worn_icon_file(var/body_type,var/slot_name,var/default_icon,var/inhands) + if(!inhands && (slot_name == slot_back_str || slot_name == slot_belt_str)) + if(icon_override) + return icon_override + else if(mob_icon) + return mob_icon + + return ..() + +/obj/item/weapon/rig/proc/suit_is_deployed() + if(!istype(wearer) || src.loc != wearer || (wearer.back != src && wearer.belt != src)) + return 0 + if(helm_type && !(helmet && wearer.head == helmet)) + return 0 + if(glove_type && !(gloves && wearer.gloves == gloves)) + return 0 + if(boot_type && !(boots && wearer.shoes == boots)) + return 0 + if(chest_type && !(chest && wearer.wear_suit == chest)) + return 0 + return 1 + +// Updates pressure protection +// Seal = 1 sets protection +// Seal = 0 unsets protection +/obj/item/weapon/rig/proc/update_airtight(var/obj/item/piece, var/seal = 0) + if(seal == 1) + piece.min_pressure_protection = rigsuit_min_pressure + piece.max_pressure_protection = rigsuit_max_pressure + piece.item_flags |= AIRTIGHT + else + piece.min_pressure_protection = null + piece.max_pressure_protection = null + piece.item_flags &= ~AIRTIGHT + return + + +/obj/item/weapon/rig/proc/reset() + offline = 2 + canremove = TRUE + for(var/obj/item/piece in list(helmet,boots,gloves,chest)) + if(!piece) continue + piece.icon_state = "[suit_state]" + if(airtight) + update_airtight(piece, 0) // Unseal + update_icon(1) + +/obj/item/weapon/rig/proc/cut_suit() + offline = 2 + canremove = TRUE + toggle_piece("helmet", loc, ONLY_RETRACT, TRUE) + toggle_piece("gauntlets", loc, ONLY_RETRACT, TRUE) + toggle_piece("boots", loc, ONLY_RETRACT, TRUE) + toggle_piece("chest", loc, ONLY_RETRACT, TRUE) + update_icon(1) + +/obj/item/weapon/rig/proc/toggle_seals(var/mob/living/carbon/human/M,var/instant) + + if(sealing) return + + if(!check_power_cost(M)) + return 0 + + deploy(M,instant) + + var/seal_target = !canremove + var/failed_to_seal + + var/obj/screen/rig_booting/booting_L = new + var/obj/screen/rig_booting/booting_R = new + + if(!seal_target) + booting_L.icon_state = "boot_left" + booting_R.icon_state = "boot_load" + animate(booting_L, alpha=230, time=30, easing=SINE_EASING) + animate(booting_R, alpha=200, time=20, easing=SINE_EASING) + M.client?.screen += booting_L + M.client?.screen += booting_R + + canremove = FALSE // No removing the suit while unsealing. + sealing = 1 + + if(!seal_target && !suit_is_deployed()) + M.visible_message("[M]'s suit flashes an error light.","Your suit flashes an error light. It can't function properly without being fully deployed.") + playsound(src, 'sound/machines/rig/rigerror.ogg', 20, FALSE) + failed_to_seal = 1 + + if(!failed_to_seal) + + if(!instant) + M.visible_message("[M]'s suit emits a quiet hum as it begins to adjust its seals.","With a quiet hum, the suit begins running checks and adjusting components.") + if(seal_delay && !do_after(M,seal_delay)) + if(M) + to_chat(M, "You must remain still while the suit is adjusting the components.") + playsound(src, 'sound/machines/rig/rigerror.ogg', 20, FALSE) + failed_to_seal = 1 + if(!M) + failed_to_seal = 1 + else + for(var/list/piece_data in list(list(M.shoes,boots,"boots",boot_type),list(M.gloves,gloves,"gloves",glove_type),list(M.head,helmet,"helmet",helm_type),list(M.wear_suit,chest,"chest",chest_type))) + + var/obj/item/piece = piece_data[1] + var/obj/item/compare_piece = piece_data[2] + var/msg_type = piece_data[3] + var/piece_type = piece_data[4] + + if(!piece || !piece_type) + continue + + if(!istype(M) || !istype(piece) || !istype(compare_piece) || !msg_type) + if(M) + to_chat(M, "You must remain still while the suit is adjusting the components.") + failed_to_seal = 1 + break + + if(!failed_to_seal && (M.back == src || M.belt == src) && piece == compare_piece) + + if(seal_delay && !instant && !do_after(M,seal_delay,needhand=0)) + failed_to_seal = 1 + + piece.icon_state = "[suit_state][!seal_target ? "_sealed" : ""]" + switch(msg_type) + if("boots") + to_chat(M, "\The [piece] [!seal_target ? "seal around your feet" : "relax their grip on your legs"].") + M.update_inv_shoes() + if("gloves") + to_chat(M, "\The [piece] [!seal_target ? "tighten around your fingers and wrists" : "become loose around your fingers"].") + M.update_inv_gloves() + if("chest") + to_chat(M, "\The [piece] [!seal_target ? "cinches tight again your chest" : "releases your chest"].") + M.update_inv_wear_suit() + if("helmet") + to_chat(M, "\The [piece] hisses [!seal_target ? "closed" : "open"].") + M.update_inv_head() + if(helmet?.light_system == STATIC_LIGHT) + helmet.update_light(wearer) + + //sealed pieces become airtight, protecting against diseases + if (!seal_target) + piece.armor["bio"] = 100 + else + piece.armor["bio"] = src.armor["bio"] + playsound(src,'sound/machines/rig/rigservo.ogg', 10, FALSE) + + else + failed_to_seal = 1 + + if((M && !(istype(M) && (M.back == src || M.belt == src)) && !istype(M,/mob/living/silicon)) || (!seal_target && !suit_is_deployed())) + failed_to_seal = 1 + + sealing = null + + if(failed_to_seal) + M.client?.screen -= booting_L + M.client?.screen -= booting_R + qdel(booting_L) + qdel(booting_R) + for(var/obj/item/piece in list(helmet,boots,gloves,chest)) + if(!piece) continue + piece.icon_state = "[suit_state][!seal_target ? "" : "_sealed"]" + canremove = !seal_target + if(airtight) + update_component_sealed() + update_icon(1) + return 0 + + // Success! + canremove = seal_target + if(M.hud_used) + if(canremove) + QDEL_NULL(minihud) + else + minihud = new (M.hud_used, src) + to_chat(M, "Your entire suit [canremove ? "loosens as the components relax" : "tightens around you as the components lock into place"].") + playsound(src, 'sound/machines/rig/rigstarted.ogg', 10, FALSE) + M.client?.screen -= booting_L + qdel(booting_L) + booting_R.icon_state = "boot_done" + spawn(40) + M.client?.screen -= booting_R + qdel(booting_R) + + if(canremove) + for(var/obj/item/rig_module/module in installed_modules) + module.deactivate() + if(airtight) + update_component_sealed() + update_icon(1) + +/obj/item/weapon/rig/proc/update_component_sealed() + for(var/obj/item/piece in list(helmet,boots,gloves,chest)) + if(canremove) + update_airtight(piece, 0) // Unseal + else + update_airtight(piece, 1) // Seal + +/obj/item/weapon/rig/ui_action_click() + toggle_cooling(usr) + +/obj/item/weapon/rig/proc/toggle_cooling(var/mob/user) + if(cooling_on) + turn_cooling_off(user) + else + turn_cooling_on(user) + +/obj/item/weapon/rig/proc/turn_cooling_on(var/mob/user) + if(!cell) + return + if(cell.charge <= 0) + to_chat(user, "\The [src] has no power!") + return + if(!suit_is_deployed()) + to_chat(user, "The hardsuit needs to be deployed first!") + return + + cooling_on = 1 + to_chat(usr, "You switch \the [src]'s cooling system on.") + + +/obj/item/weapon/rig/proc/turn_cooling_off(var/mob/user, var/failed) + if(failed) + visible_message("\The [src]'s cooling system clicks and whines as it powers down.") + else + to_chat(usr, "You switch \the [src]'s cooling system off.") + cooling_on = 0 + +/obj/item/weapon/rig/proc/get_environment_temperature() + if (ishuman(loc)) + var/mob/living/carbon/human/H = loc + if(istype(H.loc, /obj/mecha)) + var/obj/mecha/M = H.loc + return M.return_temperature() + else if(istype(H.loc, /obj/machinery/atmospherics/unary/cryo_cell)) + var/obj/machinery/atmospherics/unary/cryo_cell/cryo = H.loc + return cryo.air_contents.temperature + + var/turf/T = get_turf(src) + if(istype(T, /turf/space)) + return 0 //space has no temperature, this just makes sure the cooling unit works in space + + var/datum/gas_mixture/environment = T.return_air() + if (!environment) + return 0 + + return environment.temperature + +/obj/item/weapon/rig/proc/attached_to_user(mob/M) + if (!ishuman(M)) + return 0 + + var/mob/living/carbon/human/H = M + + if (!H.wear_suit || (H.back != src && H.belt != src)) + return 0 + + return 1 + +/obj/item/weapon/rig/proc/coolingProcess() + if (!cooling_on || !cell) + return + + if (!ismob(loc)) + return + + if (!attached_to_user(loc)) //make sure the rig's not just in their hands + return + + if (!suit_is_deployed()) //inbuilt systems only work on the suit they're designed to work on + return + + var/mob/living/carbon/human/H = loc + + var/turf/T = get_turf(src) + var/datum/gas_mixture/environment = T.return_air() + var/efficiency = 1 - H.get_pressure_weakness(environment.return_pressure()) // You need to have a good seal for effective cooling + var/env_temp = get_environment_temperature() //wont save you from a fire + var/temp_adj = min(H.bodytemperature - max(thermostat, env_temp), max_cooling) + var/thermal_protection = H.get_heat_protection(env_temp) // ... unless you've got a good suit. + + if(thermal_protection < 0.99) //For some reason, < 1 returns false if the value is 1. + temp_adj = min(H.bodytemperature - max(thermostat, env_temp), max_cooling) + else + temp_adj = min(H.bodytemperature - thermostat, max_cooling) + + if (temp_adj < 0.5) //only cools, doesn't heat, also we don't need extreme precision + return + + var/charge_usage = (temp_adj/max_cooling)*charge_consumption + + H.bodytemperature -= temp_adj*efficiency + + cell.use(charge_usage) + + if(cell.charge <= 0) + turn_cooling_off(H, 1) + +/obj/item/weapon/rig/process() + // Not on a mob...? + if(!ismob(loc)) + if(wearer?.wearing_rig == src) + wearer.wearing_rig = null + wearer = null + return PROCESS_KILL + + // Run through cooling + coolingProcess() + + if(!istype(wearer) || loc != wearer || (wearer.back != src && wearer.belt != src) || canremove || !cell || cell.charge <= 0) + if(!cell || cell.charge <= 0) + if(electrified > 0) + electrified = 0 + if(!offline) + if(istype(wearer)) + if(!canremove) + if (offline_slowdown < 1.5) + to_chat(wearer, "Your suit beeps stridently, and suddenly goes dead.") + else + to_chat(wearer, "Your suit beeps stridently, and suddenly you're wearing a leaden mass of metal and plastic composites instead of a powered suit.") + playsound(src, 'sound/machines/rig/rigdown.ogg', 60, FALSE) + if(offline_vision_restriction == 1) + to_chat(wearer, "The suit optics flicker and die, leaving you with restricted vision.") + else if(offline_vision_restriction == 2) + to_chat(wearer, "The suit optics drop out completely, drowning you in darkness.") + if(!offline) + offline = 1 + else + if(offline) + offline = 0 + if(istype(wearer) && !wearer.wearing_rig) + wearer.wearing_rig = src + slowdown = initial(slowdown) + + if(offline) + if(offline == 1) + for(var/obj/item/rig_module/module in installed_modules) + module.deactivate() + offline = 2 + slowdown = offline_slowdown + return + + if(cell && cell.charge > 0 && electrified > 0) + electrified-- + + if(malfunction_delay > 0) + malfunction_delay-- + else if(malfunctioning) + malfunctioning-- + malfunction() + + for(var/obj/item/rig_module/module in installed_modules) + cell.use(module.process()*10) + +/obj/item/weapon/rig/proc/check_power_cost(var/mob/living/user, var/cost, var/use_unconcious, var/obj/item/rig_module/mod, var/user_is_ai) + + if(!istype(user)) + return 0 + + var/fail_msg + + if(!user_is_ai) + var/mob/living/carbon/human/H = user + if(istype(H) && (H.back != src && H.belt != src)) + fail_msg = "You must be wearing \the [src] to do this." + else if(user.incorporeal_move) + fail_msg = "You must be solid to do this." + if(sealing) + fail_msg = "The hardsuit is in the process of adjusting seals and cannot be activated." + else if(!fail_msg && ((use_unconcious && user.stat > 1) || (!use_unconcious && user.stat))) + fail_msg = "You are in no fit state to do that." + else if(!cell) + fail_msg = "There is no cell installed in the suit." + else if(cost && cell.charge < cost * 10) //TODO: Cellrate? + fail_msg = "Not enough stored power." + + if(fail_msg) + to_chat(user, fail_msg) + playsound(src, 'sound/machines/rig/rigerror.ogg', 20, FALSE) + return 0 + + // This is largely for cancelling stealth and whatever. + if(mod && mod.disruptive) + for(var/obj/item/rig_module/module in (installed_modules - mod)) + if(module.active && module.disruptable) + module.deactivate() + + cell.use(cost*10) + return 1 + +/obj/item/weapon/rig/update_icon(var/update_mob_icon) + + cut_overlays() + if(!mob_icon || update_mob_icon) + var/species_icon = default_mob_icon + // Since setting mob_icon will override the species checks in + // update_inv_wear_suit(), handle species checks here. + if(wearer && LAZYACCESS(sprite_sheets, wearer.species.get_bodytype(wearer))) + species_icon = sprite_sheets[wearer.species.get_bodytype(wearer)] + mob_icon = icon(icon = species_icon, icon_state = "[icon_state]") + + if(installed_modules.len) + for(var/obj/item/rig_module/module in installed_modules) + if(module.suit_overlay) + chest.add_overlay(image(module.suit_overlay_icon, icon_state = "[module.suit_overlay]", dir = SOUTH)) + + if(wearer) + wearer.update_inv_shoes() + wearer.update_inv_gloves() + wearer.update_inv_head() + wearer.update_inv_wear_suit() + wearer.update_inv_back() + return + +/obj/item/weapon/rig/proc/check_suit_access(var/mob/living/carbon/human/user, var/do_message = TRUE) + + if(!security_check_enabled) + return 1 + + if(istype(user)) + if(!canremove) + return 1 + if(malfunction_check(user)) + return 0 + if(user.back != src && user.belt != src) + return 0 + else if(!src.allowed(user)) + if(do_message) + to_chat(user, "Unauthorized user. Access denied.") + return 0 + + else if(!ai_override_enabled) + if(do_message) + to_chat(user, "Synthetic access disabled. Please consult hardware provider.") + return 0 + + return 1 + +/obj/item/weapon/rig/proc/notify_ai(var/message) + for(var/obj/item/rig_module/ai_container/module in installed_modules) + if(module.integrated_ai && module.integrated_ai.client && !module.integrated_ai.stat) + to_chat(module.integrated_ai, "[message]") + . = 1 + +/obj/item/weapon/rig/equipped(mob/living/carbon/human/M) + ..() + + if(istype(M.back, /obj/item/weapon/rig) && istype(M.belt, /obj/item/weapon/rig)) + to_chat(M, "You try to put on the [src], but it won't fit.") + if(M && (M.back == src || M.belt == src)) + if(!M.unEquip(src)) + return + src.forceMove(get_turf(src)) + return + + if(seal_delay > 0 && istype(M) && (M.back == src || M.belt == src)) + M.visible_message("[M] starts putting on \the [src]...", "You start putting on \the [src]...") + if(!do_after(M,seal_delay)) + if(M && (M.back == src || M.belt == src)) + if(!M.unEquip(src)) + return + src.forceMove(get_turf(src)) + return + + if(istype(M) && (M.back == src || M.belt == src)) + M.visible_message("[M] struggles into \the [src].", "You struggle into \the [src].") + wearer = M + wearer.wearing_rig = src + update_icon() + +/obj/item/weapon/rig/proc/toggle_piece(var/piece, var/mob/living/carbon/human/H, var/deploy_mode, var/forced = FALSE) + + if((sealing || !cell || !cell.charge) && !forced) + return + + if((!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) && !forced) + return + + if((usr == wearer && (usr.stat||usr.paralysis||usr.stunned)) && !forced) // If the usr isn't wearing the suit it's probably an AI. + return + + var/obj/item/check_slot + var/equip_to + var/obj/item/use_obj + + if(!H) + return + + switch(piece) + if("helmet") + equip_to = slot_head + use_obj = helmet + check_slot = H.head + if("gauntlets") + equip_to = slot_gloves + use_obj = gloves + check_slot = H.gloves + if("boots") + equip_to = slot_shoes + use_obj = boots + check_slot = H.shoes + if("chest") + equip_to = slot_wear_suit + use_obj = chest + check_slot = H.wear_suit + + if(use_obj) + if(check_slot == use_obj && deploy_mode != ONLY_DEPLOY) + + var/mob/living/carbon/human/holder + + if(use_obj) + holder = use_obj.loc + if(istype(holder)) + if(use_obj && check_slot == use_obj) + to_chat(H, "Your [use_obj.name] [use_obj.gender == PLURAL ? "retract" : "retracts"] swiftly.") + playsound(src, 'sound/machines/rig/rigservo.ogg', 10, FALSE) + use_obj.canremove = TRUE + holder.drop_from_inventory(use_obj) + use_obj.forceMove(get_turf(src)) + use_obj.dropped() + use_obj.canremove = FALSE + use_obj.forceMove(src) + + else if (deploy_mode != ONLY_RETRACT) + if(check_slot && check_slot == use_obj) + return + use_obj.forceMove(H) + if(!H.equip_to_slot_if_possible(use_obj, equip_to, 0, 1)) + use_obj.forceMove(src) + if(check_slot) + to_chat(H, "You are unable to deploy \the [piece] as \the [check_slot] [check_slot.gender == PLURAL ? "are" : "is"] in the way.") + return + else + to_chat(H, "Your [use_obj.name] [use_obj.gender == PLURAL ? "deploy" : "deploys"] swiftly.") + playsound(src, 'sound/machines/rig/rigservo.ogg', 10, FALSE) + + if(piece == "helmet" && helmet?.light_system == STATIC_LIGHT) + helmet.update_light() + +/obj/item/weapon/rig/proc/deploy(mob/M,var/sealed) + + var/mob/living/carbon/human/H = M + + if(!H || !istype(H)) return + + if(H.back != src && H.belt != src) + return + + if(sealed) + if(H.head) + var/obj/item/garbage = H.head + H.drop_from_inventory(garbage) + H.head = null + qdel(garbage) + + if(H.gloves) + var/obj/item/garbage = H.gloves + H.drop_from_inventory(garbage) + H.gloves = null + qdel(garbage) + + if(H.shoes) + var/obj/item/garbage = H.shoes + H.drop_from_inventory(garbage) + H.shoes = null + qdel(garbage) + + if(H.wear_suit) + var/obj/item/garbage = H.wear_suit + H.drop_from_inventory(garbage) + H.wear_suit = null + qdel(garbage) + + for(var/piece in list("helmet","gauntlets","chest","boots")) + toggle_piece(piece, H, ONLY_DEPLOY) + +/obj/item/weapon/rig/dropped(var/mob/user) + ..() + for(var/piece in list("helmet","gauntlets","chest","boots")) + toggle_piece(piece, user, ONLY_RETRACT) + if(wearer && wearer.wearing_rig == src) + wearer.wearing_rig = null + wearer = null + +//Todo +/obj/item/weapon/rig/proc/malfunction() + return 0 + +/obj/item/weapon/rig/emp_act(severity_class) + //set malfunctioning + if(emp_protection < 30) //for ninjas, really. + malfunctioning += 10 + if(malfunction_delay <= 0) + malfunction_delay = max(malfunction_delay, round(30/severity_class)) + + //drain some charge + if(cell) cell.emp_act(severity_class + 15) + + //possibly damage some modules + take_hit((100/severity_class), "electrical pulse", 1) + +/obj/item/weapon/rig/proc/shock(mob/user) + if (electrocute_mob(user, cell, src)) //electrocute_mob() handles removing charge from the cell, no need to do that here. + spark_system.start() + if(user.stunned) + return 1 + return 0 + +/obj/item/weapon/rig/proc/take_hit(damage, source, is_emp=0) + + if(!installed_modules.len) + return + + var/chance + if(!is_emp) + chance = 2*max(0, damage - (chest? chest.breach_threshold : 0)) + else + //Want this to be roughly independant of the number of modules, meaning that X emp hits will disable Y% of the suit's modules on average. + //that way people designing hardsuits don't have to worry (as much) about how adding that extra module will affect emp resiliance by 'soaking' hits for other modules + chance = 2*max(0, damage - emp_protection)*min(installed_modules.len/15, 1) + + if(!prob(chance)) + return + + //deal addition damage to already damaged module first. + //This way the chances of a module being disabled aren't so remote. + var/list/valid_modules = list() + var/list/damaged_modules = list() + for(var/obj/item/rig_module/module in installed_modules) + if(module.damage < 2) + valid_modules |= module + if(module.damage > 0) + damaged_modules |= module + + var/obj/item/rig_module/dam_module = null + if(damaged_modules.len) + dam_module = pick(damaged_modules) + else if(valid_modules.len) + dam_module = pick(valid_modules) + + if(!dam_module) return + + dam_module.damage++ + + if(!source) + source = "hit" + + if(wearer) + if(dam_module.damage >= 2) + to_chat(wearer, "The [source] has disabled your [dam_module.interface_name]!") + else + to_chat(wearer, "The [source] has damaged your [dam_module.interface_name]!") + dam_module.deactivate() + +/obj/item/weapon/rig/proc/malfunction_check(var/mob/living/carbon/human/user) + if(malfunction_delay) + if(offline) + to_chat(user, "The suit is completely unresponsive.") + else + to_chat(user, "ERROR: Hardware fault. Rebooting interface...") + return 1 + return 0 + +/obj/item/weapon/rig/proc/ai_can_move_suit(var/mob/user, var/check_user_module = 0, var/check_for_ai = 0) + + if(check_for_ai) + if(!(locate(/obj/item/rig_module/ai_container) in contents)) + return 0 + var/found_ai + for(var/obj/item/rig_module/ai_container/module in contents) + if(module.damage >= 2) + continue + if(module.integrated_ai && module.integrated_ai.client && !module.integrated_ai.stat) + found_ai = 1 + break + if(!found_ai) + return 0 + + if(check_user_module) + if(!user || !user.loc || !user.loc.loc) + return 0 + var/obj/item/rig_module/ai_container/module = user.loc.loc + if(!istype(module) || module.damage >= 2) + to_chat(user, "Your host module is unable to interface with the suit.") + return 0 + + if(offline || !cell || !cell.charge || locked_down) + if(user) + to_chat(user, "Your host rig is unpowered and unresponsive.") + return 0 + if(!wearer || (wearer.back != src && wearer.belt != src)) + if(user) + to_chat(user, "Your host rig is not being worn.") + return 0 + if(!wearer.stat && !control_overridden && !ai_override_enabled) + if(user) + to_chat(user, "You are locked out of the suit servo controller.") + return 0 + return 1 + +/obj/item/weapon/rig/proc/force_rest(var/mob/user) + if(!ai_can_move_suit(user, check_user_module = 1)) + return + wearer.lay_down() + to_chat(user, "\The [wearer] is now [wearer.resting ? "resting" : "getting up"].") + +/obj/item/weapon/rig/proc/forced_move(var/direction, var/mob/user) + + // Why is all this shit in client/Move()? Who knows? + if(world.time < wearer_move_delay) + return + + if(!wearer || !wearer.loc || !ai_can_move_suit(user, check_user_module = 1)) + return + + //This is sota the goto stop mobs from moving var + if(wearer.transforming || !wearer.canmove) + return + + if((istype(wearer.loc, /turf/space)) || (wearer.lastarea.has_gravity == 0)) + if(!wearer.Process_Spacemove(0)) + return 0 + + if(malfunctioning) + direction = pick(cardinal) + + // Inside an object, tell it we moved. + if(isobj(wearer.loc) || ismob(wearer.loc)) + var/atom/O = wearer.loc + return O.relaymove(wearer, direction) + + if(isturf(wearer.loc)) + if(wearer.restrained())//Why being pulled while cuffed prevents you from moving + for(var/mob/M in range(wearer, 1)) + if(M.pulling == wearer) + if(!M.restrained() && M.stat == 0 && M.canmove && wearer.Adjacent(M)) + to_chat(user, "Your host is restrained! They can't move!") + return 0 + else + M.stop_pulling() + + if(wearer.pinned.len) + to_chat(src, "Your host is pinned to a wall by [wearer.pinned[1]]!") + return 0 + + // AIs are a bit slower than regular and ignore move intent. + wearer_move_delay = world.time + ai_controlled_move_delay + + if(istype(wearer.buckled, /obj/vehicle)) + //manually set move_delay for vehicles so we don't inherit any mob movement penalties + //specific vehicle move delays are set in code\modules\vehicles\vehicle.dm + wearer_move_delay = world.time + return wearer.buckled.relaymove(wearer, direction) + + if(istype(wearer.machine, /obj/machinery)) + if(wearer.machine.relaymove(wearer, direction)) + return + + if(wearer.pulledby || wearer.buckled) // Wheelchair driving! + if(istype(wearer.loc, /turf/space)) + return // No wheelchair driving in space + if(istype(wearer.pulledby, /obj/structure/bed/chair/wheelchair)) + return wearer.pulledby.relaymove(wearer, direction) + else if(istype(wearer.buckled, /obj/structure/bed/chair/wheelchair)) + if(ishuman(wearer.buckled)) + var/obj/item/organ/external/l_hand = wearer.get_organ("l_hand") + var/obj/item/organ/external/r_hand = wearer.get_organ("r_hand") + if((!l_hand || (l_hand.status & ORGAN_DESTROYED)) && (!r_hand || (r_hand.status & ORGAN_DESTROYED))) + return // No hands to drive your chair? Tough luck! + wearer_move_delay += 2 + return wearer.buckled.relaymove(wearer,direction) + + cell.use(200) //Arbitrary, TODO + wearer.Move(get_step(get_turf(wearer),direction),direction) + +// This returns the rig if you are contained inside one, but not if you are wearing it +/atom/proc/get_rig() + if(loc) + return loc.get_rig() + return null + +/obj/item/weapon/rig/get_rig() + return src + +/mob/living/carbon/human/get_rig() + if(istype(back, /obj/item/weapon/rig)) + return back + else if(istype(belt, /obj/item/weapon/rig)) + return belt + else + return null + +//Boot animation screen objects +/obj/screen/rig_booting + screen_loc = "1,1" + icon = 'icons/obj/rig_boot.dmi' + icon_state = "" + layer = SCREEN_LAYER + plane = PLANE_FULLSCREEN + mouse_opacity = 0 + alpha = 20 //Animated up when loading + +#undef ONLY_DEPLOY +#undef ONLY_RETRACT +#undef SEAL_DELAY diff --git a/code/modules/clothing/spacesuits/syndi.dm b/code/modules/clothing/spacesuits/syndi.dm index 7ae8a87f683..b49ad6098f4 100644 --- a/code/modules/clothing/spacesuits/syndi.dm +++ b/code/modules/clothing/spacesuits/syndi.dm @@ -1,137 +1,137 @@ -//Regular syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate - name = "red space helmet" - icon_state = "syndicate" - desc = "A crimson helmet sporting clean lines and durable plating. Engineered to look menacing." - armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) - siemens_coefficient = 0.6 - -/obj/item/clothing/suit/space/syndicate - name = "red space suit" - icon_state = "syndicate" - desc = "A crimson spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." - w_class = ITEMSIZE_NORMAL - allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen) - slowdown = 0.5 - armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) - siemens_coefficient = 0.6 - -//Green syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/green - name = "green space helmet" - desc = "A green helmet sporting clean lines and durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-green" - -/obj/item/clothing/suit/space/syndicate/green - name = "green space suit" - desc = "A green spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-green" - -//Dark green syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/green/dark - name = "dark green space helmet" - desc = "A dark green helmet sporting clean lines and durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-green-dark" - -/obj/item/clothing/suit/space/syndicate/green/dark - name = "dark green space suit" - desc = "A dark green spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-green-dark" - -//Orange syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/orange - name = "orange space helmet" - desc = "An orange helmet sporting clean lines and durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-orange" - -/obj/item/clothing/suit/space/syndicate/orange - name = "orange space suit" - desc = "An orange spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-orange" - -//Blue syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/blue - name = "blue space helmet" - desc = "A blue helmet sporting clean lines and durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-blue" - -/obj/item/clothing/suit/space/syndicate/blue - name = "blue space suit" - desc = "A blue spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-blue" - -//Black syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black - name = "black space helmet" - desc = "A black helmet sporting durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-black" - -/obj/item/clothing/suit/space/syndicate/black - name = "black space suit" - desc = "A black spacesuit sporting durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-black" - -//Black-green syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/green - name = "black and green space helmet" - desc = "A black helmet sporting a single green stripe and durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-black-green" - -/obj/item/clothing/suit/space/syndicate/black/green - name = "black and green space suit" - desc = "A black spacesuit sporting green stripes and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-black-green" - -//Black-blue syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/blue - name = "black and blue space helmet" - desc = "A black helmet sporting a single blue stripe and durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-black-blue" - -/obj/item/clothing/suit/space/syndicate/black/blue - name = "black and blue space suit" - desc = "A black spacesuit sporting blue stripes and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-black-blue" - -//Black medical syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/med - name = "black medical space helmet" - desc = "A black helmet sporting a medical cross and durable plating. Hopefully the wearer abides by space geneva." - icon_state = "syndicate-helm-black-med" - -/obj/item/clothing/suit/space/syndicate/black/med - name = "black medical space suit" - desc = "A black spacesuit sporting a medical cross and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-black-med" - -//Black-orange syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/orange - name = "black and orange space helmet" - icon_state = "syndicate-helm-black-orange" - -/obj/item/clothing/suit/space/syndicate/black/orange - name = "black and orange space suit" - desc = "A black spacesuit sporting orange stripes and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-black-orange" - -//Black-red syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/red - name = "black and red space helmet" - desc = "A black helmet sporting a single red stripe and durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-black-red" - -/obj/item/clothing/suit/space/syndicate/black/red - name = "black and red space suit" - desc = "A black spacesuit sporting red stripes and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-black-red" - -//Black with yellow/red engineering syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/engie - name = "black engineering space helmet" - desc = "A black helmet sporting red and yellow stripes and durable plating. Engineered to look well... engineering-ish." - icon_state = "syndicate-helm-black-engie" - -/obj/item/clothing/suit/space/syndicate/black/engie - name = "black engineering space suit" - desc = "A black spacesuit sporting red and yellow stripes and durable plating. Robust, reliable, and slightly suspicious." +//Regular syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate + name = "red space helmet" + icon_state = "syndicate" + desc = "A crimson helmet sporting clean lines and durable plating. Engineered to look menacing." + armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) + siemens_coefficient = 0.6 + +/obj/item/clothing/suit/space/syndicate + name = "red space suit" + icon_state = "syndicate" + desc = "A crimson spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." + w_class = ITEMSIZE_NORMAL + allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen) + slowdown = 0.5 + armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) + siemens_coefficient = 0.6 + +//Green syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/green + name = "green space helmet" + desc = "A green helmet sporting clean lines and durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-green" + +/obj/item/clothing/suit/space/syndicate/green + name = "green space suit" + desc = "A green spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-green" + +//Dark green syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/green/dark + name = "dark green space helmet" + desc = "A dark green helmet sporting clean lines and durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-green-dark" + +/obj/item/clothing/suit/space/syndicate/green/dark + name = "dark green space suit" + desc = "A dark green spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-green-dark" + +//Orange syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/orange + name = "orange space helmet" + desc = "An orange helmet sporting clean lines and durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-orange" + +/obj/item/clothing/suit/space/syndicate/orange + name = "orange space suit" + desc = "An orange spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-orange" + +//Blue syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/blue + name = "blue space helmet" + desc = "A blue helmet sporting clean lines and durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-blue" + +/obj/item/clothing/suit/space/syndicate/blue + name = "blue space suit" + desc = "A blue spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-blue" + +//Black syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black + name = "black space helmet" + desc = "A black helmet sporting durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-black" + +/obj/item/clothing/suit/space/syndicate/black + name = "black space suit" + desc = "A black spacesuit sporting durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-black" + +//Black-green syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/green + name = "black and green space helmet" + desc = "A black helmet sporting a single green stripe and durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-black-green" + +/obj/item/clothing/suit/space/syndicate/black/green + name = "black and green space suit" + desc = "A black spacesuit sporting green stripes and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-black-green" + +//Black-blue syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/blue + name = "black and blue space helmet" + desc = "A black helmet sporting a single blue stripe and durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-black-blue" + +/obj/item/clothing/suit/space/syndicate/black/blue + name = "black and blue space suit" + desc = "A black spacesuit sporting blue stripes and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-black-blue" + +//Black medical syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/med + name = "black medical space helmet" + desc = "A black helmet sporting a medical cross and durable plating. Hopefully the wearer abides by space geneva." + icon_state = "syndicate-helm-black-med" + +/obj/item/clothing/suit/space/syndicate/black/med + name = "black medical space suit" + desc = "A black spacesuit sporting a medical cross and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-black-med" + +//Black-orange syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/orange + name = "black and orange space helmet" + icon_state = "syndicate-helm-black-orange" + +/obj/item/clothing/suit/space/syndicate/black/orange + name = "black and orange space suit" + desc = "A black spacesuit sporting orange stripes and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-black-orange" + +//Black-red syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/red + name = "black and red space helmet" + desc = "A black helmet sporting a single red stripe and durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-black-red" + +/obj/item/clothing/suit/space/syndicate/black/red + name = "black and red space suit" + desc = "A black spacesuit sporting red stripes and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-black-red" + +//Black with yellow/red engineering syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/engie + name = "black engineering space helmet" + desc = "A black helmet sporting red and yellow stripes and durable plating. Engineered to look well... engineering-ish." + icon_state = "syndicate-helm-black-engie" + +/obj/item/clothing/suit/space/syndicate/black/engie + name = "black engineering space suit" + desc = "A black spacesuit sporting red and yellow stripes and durable plating. Robust, reliable, and slightly suspicious." icon_state = "syndicate-black-engie" \ No newline at end of file diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index 001f894ca60..12ed13f6010 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -1,642 +1,642 @@ -/obj/item/clothing/suit/armor - allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/reagent_containers/spray/pepper,/obj/item/weapon/gun/projectile,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/device/flashlight/maglight,/obj/item/clothing/head/helmet) - body_parts_covered = UPPER_TORSO|LOWER_TORSO - item_flags = THICKMATERIAL - - cold_protection = UPPER_TORSO|LOWER_TORSO - min_cold_protection_temperature = ARMOR_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = UPPER_TORSO|LOWER_TORSO - max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE - siemens_coefficient = 0.6 - -/obj/item/clothing/suit/mob_can_equip(var/mob/living/carbon/human/H, slot, disable_warning = FALSE) - if(..()) //This will only run if no other problems occured when equiping. - for(var/obj/item/clothing/I in list(H.gloves, H.shoes)) - if(I && (src.body_parts_covered & ARMS && I.body_parts_covered & ARMS) ) - to_chat(H, "You can't wear \the [src] with \the [I], it's in the way.") - return 0 - if(I && (src.body_parts_covered & LEGS && I.body_parts_covered & LEGS) ) - to_chat(H, "You can't wear \the [src] with \the [I], it's in the way.") - return 0 - return 1 - -/obj/item/clothing/suit/armor/vest - name = "armor" - desc = "An armored vest that protects against some damage." - icon_state = "armor" - blood_overlay_type = "armor" - armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) - -/obj/item/clothing/suit/armor/vest/alt - name = "security armor" - desc = "An armored vest that protects against some damage. This one has a NanoTrasen corporate badge." - icon_state = "armoralt" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - -/obj/item/clothing/suit/armor/vest/security - name = "security armor" - desc = "An armored vest that protects against some damage. This one has a corporate badge." - icon_state = "armorsec" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - -/obj/item/clothing/suit/armor/riot - name = "riot vest" - desc = "A vest with heavy padding to protect against melee attacks." - icon_state = "riot" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - armor = list(melee = 80, bullet = 10, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.5 - -/obj/item/clothing/suit/armor/riot/alt - icon_state = "riot_new" - item_state_slots = list(slot_r_hand_str = "riot_new", slot_l_hand_str = "riot_new") - -/obj/item/clothing/suit/armor/bulletproof - name = "bullet resistant vest" - desc = "A vest that excels in protecting the wearer against high-velocity solid projectiles." - icon_state = "bulletproof" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - blood_overlay_type = "armor" - slowdown = 0.5 - armor = list(melee = 10, bullet = 80, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.7 - -/obj/item/clothing/suit/armor/bulletproof/alt - icon_state = "bulletproof_new" - item_state_slots = list(slot_r_hand_str = "bulletproof_new", slot_l_hand_str = "bulletproof_new") - blood_overlay_type = "armor" - -/obj/item/clothing/suit/armor/laserproof - name = "ablative armor vest" - desc = "A vest that excels in protecting the wearer against energy projectiles." - icon_state = "armor_reflec" - blood_overlay_type = "armor" - slowdown = 0.5 - armor = list(melee = 10, bullet = 10, laser = 80, energy = 50, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.1 - -/obj/item/clothing/suit/armor/laserproof/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(istype(damage_source, /obj/item/projectile/energy) || istype(damage_source, /obj/item/projectile/beam)) - var/obj/item/projectile/P = damage_source - - if(P.reflected) // Can't reflect twice - return ..() - - var/reflectchance = 40 - round(damage/3) - if(!(def_zone in list(BP_TORSO, BP_GROIN))) - reflectchance /= 2 - if(P.starting && prob(reflectchance)) - visible_message("\The [user]'s [src.name] reflects [attack_text]!") - - // Find a turf near or on the original location to bounce to - var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/turf/curloc = get_turf(user) - - // redirect the projectile - P.redirect(new_x, new_y, curloc, user) - P.reflected = 1 - - return PROJECTILE_CONTINUE // complete projectile permutation - -/obj/item/clothing/suit/armor/combat - name = "combat vest" - desc = "A vest that protects the wearer from several common types of weaponry." - icon_state = "combat" - blood_overlay_type = "armor" - slowdown = 0.5 - armor = list(melee = 50, bullet = 50, laser = 50, energy = 30, bomb = 30, bio = 0, rad = 0) - siemens_coefficient = 0.6 - -/obj/item/clothing/suit/armor/tactical - name = "tactical armor" - desc = "A suit of armor most often used by Special Weapons and Tactics squads. Includes padded vest with pockets along with shoulder and kneeguards." - icon_state = "swatarmor" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDETIE|HIDEHOLSTER - slowdown = 1 - armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) - siemens_coefficient = 0.7 - -/obj/item/clothing/suit/armor/swat - name = "swat suit" - desc = "A heavily armored suit that protects against moderate damage. Used in special operations." - icon_state = "deathsquad" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - item_flags = THICKMATERIAL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS - allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen,/obj/item/clothing/head/helmet) - slowdown = 1 - w_class = ITEMSIZE_HUGE - armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 100, rad = 100) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - min_pressure_protection = 0 * ONE_ATMOSPHERE - max_pressure_protection = 20* ONE_ATMOSPHERE - siemens_coefficient = 0.6 - - -/obj/item/clothing/suit/armor/swat/officer - name = "officer jacket" - desc = "An armored jacket used in special operations." - icon_state = "detective" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - blood_overlay_type = "coat" - flags_inv = 0 - body_parts_covered = UPPER_TORSO|ARMS - - -/obj/item/clothing/suit/armor/det_suit - name = "armor" - desc = "An armored vest with a detective's badge on it." - icon_state = "detective-armor" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - blood_overlay_type = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) - - -//Reactive armor -//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!) -/obj/item/clothing/suit/armor/reactive - name = "Reactive Teleport Armor" - desc = "Someone separated our Research Director from their own head!" - var/active = 0.0 - icon_state = "reactiveoff" - item_state_slots = list(slot_r_hand_str = "armor_reflec_old", slot_l_hand_str = "armor_reflec_old") - blood_overlay_type = "armor" - slowdown = 1 - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) - -/obj/item/clothing/suit/armor/reactive/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(prob(50)) - user.visible_message("The reactive teleport system flings [user] clear of the attack!") - var/list/turfs = new/list() - for(var/turf/T in orange(6, user)) - if(istype(T,/turf/space)) continue - if(T.density) continue - if(T.x>world.maxx-6 || T.x<6) continue - if(T.y>world.maxy-6 || T.y<6) continue - turfs += T - if(!turfs.len) turfs += pick(/turf in orange(6)) - var/turf/picked = pick(turfs) - if(!isturf(picked)) return - - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(src, "sparks", 50, 1) - - user.loc = picked - return PROJECTILE_FORCE_MISS - return 0 - -/obj/item/clothing/suit/armor/reactive/attack_self(mob/user as mob) - active = !( active ) - if (active) - to_chat(user, span_blue("The reactive armor is now active.")) - icon_state = "reactive" - else - to_chat(user, span_blue("The reactive armor is now inactive.")) - icon_state = "reactiveoff" - add_fingerprint(user) - return - -/obj/item/clothing/suit/armor/reactive/emp_act(severity) - active = 0 - icon_state = "reactiveoff" - ..() - -// Alien armor has a chance to completely block attacks. -/obj/item/clothing/suit/armor/alien - name = "alien enhancement vest" - desc = "It's a strange piece of what appears to be armor. It looks very light and agile. Strangely enough it seems to have been designed for a humanoid shape." - description_info = "It has a 20% chance to completely nullify an incoming attack, and the wearer moves slightly faster." - icon_state = "alien_speed" - blood_overlay_type = "armor" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - slowdown = -1 - body_parts_covered = UPPER_TORSO|LOWER_TORSO - armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 50, bio = 0, rad = 40) - siemens_coefficient = 0.4 - var/block_chance = 20 - -/obj/item/clothing/suit/armor/alien/tank - name = "alien protection suit" - desc = "It's really resilient yet lightweight, so it's probably meant to be armor. Strangely enough it seems to have been designed for a humanoid shape." - description_info = "It has a 40% chance to completely nullify an incoming attack." - icon_state = "alien_tank" - slowdown = 0 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - armor = list(melee = 70, bullet = 70, laser = 70, energy = 70, bomb = 70, bio = 0, rad = 40) - block_chance = 40 - -/obj/item/clothing/suit/armor/alien/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(prob(block_chance)) - user.visible_message("\The [src] completely absorbs [attack_text]!") - return TRUE - return FALSE - -//Non-hardsuit ERT armor. -/obj/item/clothing/suit/armor/vest/ert - name = "emergency response team armor" - desc = "A set of armor worn by members of the Emergency Response Team." - icon_state = "ertarmor_cmd" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 20, bio = 0, rad = 0) - -//Commander -/obj/item/clothing/suit/armor/vest/ert/command - name = "emergency response team commander armor" - desc = "A set of armor worn by the commander of an Emergency Response Team. Has blue highlights." - -//Security -/obj/item/clothing/suit/armor/vest/ert/security - name = "emergency response team security armor" - desc = "A set of armor worn by security members of the Emergency Response Team. Has red highlights." - icon_state = "ertarmor_sec" - -//Engineer -/obj/item/clothing/suit/armor/vest/ert/engineer - name = "emergency response team engineer armor" - desc = "A set of armor worn by engineering members of the Emergency Response Team. Has orange highlights." - icon_state = "ertarmor_eng" - -//Medical -/obj/item/clothing/suit/armor/vest/ert/medical - name = "emergency response team medical armor" - desc = "A set of armor worn by medical members of the Emergency Response Team. Has blue and white highlights." - icon_state = "ertarmor_med" - -//New Vests -/obj/item/clothing/suit/storage/vest - name = "armor vest" - desc = "A simple kevlar plate carrier." - icon_state = "kvest" - blood_overlay_type = "armor" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) - allowed = list(/obj/item/weapon/gun,/obj/item/weapon/reagent_containers/spray/pepper,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/device/flashlight/maglight,/obj/item/clothing/head/helmet) - - body_parts_covered = UPPER_TORSO|LOWER_TORSO - item_flags = THICKMATERIAL - - cold_protection = UPPER_TORSO|LOWER_TORSO - min_cold_protection_temperature = ARMOR_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = UPPER_TORSO|LOWER_TORSO - max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE - siemens_coefficient = 0.6 - -/obj/item/clothing/suit/storage/vest/officer - name = "officer armor vest" - desc = "A simple kevlar plate carrier. This one has a security holobadge clipped to the chest." - icon_state = "officervest_nobadge" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - icon_badge = "officervest_badge" - icon_nobadge = "officervest_nobadge" - -/obj/item/clothing/suit/storage/vest/warden - name = "warden armor vest" - desc = "A simple kevlar plate carrier. This one has a silver badge clipped to the chest." - icon_state = "wardenvest_nobadge" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - icon_badge = "wardenvest_badge" - icon_nobadge = "wardenvest_nobadge" - -/obj/item/clothing/suit/storage/vest/wardencoat - name = "Warden's jacket" - desc = "An armoured jacket with silver rank pips and livery." - icon_state = "warden_jacket" - blood_overlay_type = "suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - flags_inv = HIDETIE|HIDEHOLSTER - -/obj/item/clothing/suit/storage/vest/wardencoat/alt - name = "Warden's jacket" - desc = "An armoured jacket with silver rank pips and livery." - icon_state = "warden_alt" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - -/obj/item/clothing/suit/storage/vest/hos - name = "head of security armor vest" - desc = "A simple kevlar plate carrier. This one has a gold badge clipped to the chest." - icon_state = "hosvest_nobadge" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - icon_badge = "hosvest_badge" - icon_nobadge = "hosvest_nobadge" - -/obj/item/clothing/suit/storage/vest/hoscoat - name = "armored coat" - desc = "A greatcoat enhanced with a special alloy for some protection and style." - icon_state = "hos" - blood_overlay_type = "suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - flags_inv = HIDETIE|HIDEHOLSTER - -//Jensen cosplay gear -/obj/item/clothing/suit/storage/vest/hoscoat/jensen - name = "armored trenchcoat" - desc = "A trenchcoat augmented with a special alloy for some protection and style." - icon_state = "hostrench" - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/vest/pcrc - name = "PCRC armor vest" - desc = "A simple kevlar plate carrier belonging to Proxima Centauri Risk Control. This one has a PCRC crest clipped to the chest." - icon_state = "pcrcvest_nobadge" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - icon_badge = "pcrcvest_badge" - icon_nobadge = "pcrcvest_nobadge" - -/obj/item/clothing/suit/storage/vest/solgov/hedberg - name = "Hedberg-Hammarstrom armor vest" - desc = "A simple kevlar plate carrier belonging to Hedberg-Hammarstrom. The company logo is clearly visible." - icon_state = "secwebvest" - -/obj/item/clothing/suit/storage/vest/solgov - name = "\improper Solar Confederate Government armored vest" - desc = "A synthetic armor vest. This one is marked with the crest of the Solar Confederate Government." - icon_state = "solvest" - armor = list(melee = 40, bullet = 40, laser = 40, energy = 25, bomb = 30, bio = 0, rad = 0) - -/obj/item/clothing/suit/storage/vest/solgov/heavy - name = "\improper Solar Confederate Government heavy armored vest" - desc = "A synthetic armor vest with Solar Confederate Government printed in distinctive blue lettering on the chest. This one has added webbing and ballistic plates." - icon_state = "solwebvest" - -/obj/item/clothing/suit/storage/vest/solgov/command - name = "command heavy armored vest" - desc = "A synthetic armor vest with Solar Confederate Government printed in detailed gold lettering on the chest. This one has added webbing and ballistic plates." - icon_state = "comwebvest" - -/obj/item/clothing/suit/storage/vest/tactical //crack at a more balanced mid-range armor, minor improvements over standard vests, with the idea "modern" combat armor would focus on energy weapon protection. - name = "tactical armored vest" - desc = "A heavy armored vest in a fetching tan. It is surprisingly flexible and light, even with the extra webbing and advanced ceramic plates." - icon_state = "tacwebvest" - item_state = "tacwebvest" - armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) - -/obj/item/clothing/suit/storage/vest/heavy/flexitac //a reskin of the above to have a matching armor set - name = "tactical light vest" - desc = "An armored vest made from advanced flexible ceramic plates. It's surprisingly mobile, if a little unfashionable." - icon_state = "flexitac" - item_state = "flexitac" - armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) - cold_protection = UPPER_TORSO|LOWER_TORSO - min_cold_protection_temperature = T0C - 20 - slowdown = 0.5 - -/obj/item/clothing/suit/storage/vest/detective - name = "detective armor vest" - desc = "A simple kevlar plate carrier in a vintage brown, it has a badge clipped to the chest that reads, 'Private investigator'." - icon_state = "detectivevest_nobadge" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - icon_badge = "detectivevest_badge" - icon_nobadge = "detectivevest_nobadge" - -/obj/item/clothing/suit/storage/vest/press - name = "press vest" - icon_state = "pvest" - desc = "A simple kevlar plate carrier. This one has the word 'Press' embroidered on patches on the back and front." - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - allowed = list(/obj/item/device/flashlight,/obj/item/device/taperecorder,/obj/item/weapon/pen,/obj/item/device/camera_film,/obj/item/device/camera,/obj/item/clothing/head/helmet) - -/obj/item/clothing/suit/storage/vest/heavy - name = "heavy armor vest" - desc = "A heavy kevlar plate carrier with webbing attached." - icon_state = "webvest" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - armor = list(melee = 50, bullet = 40, laser = 40, energy = 25, bomb = 25, bio = 0, rad = 0) - slowdown = 0.5 - -/obj/item/clothing/suit/storage/vest/heavy/officer - name = "officer heavy armor vest" - desc = "A heavy kevlar plate carrier with webbing attached. This one has a security holobadge clipped to the chest." - icon_state = "officerwebvest_nobadge" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - icon_badge = "officerwebvest_badge" - icon_nobadge = "officerwebvest_nobadge" - -/obj/item/clothing/suit/storage/vest/heavy/warden - name = "warden heavy armor vest" - desc = "A heavy kevlar plate carrier with webbing attached. This one has a silver badge clipped to the chest." - icon_state = "wardenwebvest_nobadge" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - icon_badge = "wardenwebvest_badge" - icon_nobadge = "wardenwebvest_nobadge" - -/obj/item/clothing/suit/storage/vest/heavy/hos - name = "head of security heavy armor vest" - desc = "A heavy kevlar plate carrier with webbing attached. This one has a gold badge clipped to the chest." - icon_state = "hoswebvest_nobadge" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - icon_badge = "hoswebvest_badge" - icon_nobadge = "hoswebvest_nobadge" - -/obj/item/clothing/suit/storage/vest/heavy/pcrc - name = "PCRC heavy armor vest" - desc = "A heavy kevlar plate carrier belonging to Proxima Centauri Risk Control with webbing attached. This one has a PCRC crest clipped to the chest." - icon_state = "pcrcwebvest_nobadge" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - icon_badge = "pcrcwebvest_badge" - icon_nobadge = "pcrcwebvest_nobadge" - -//Provides the protection of a merc voidsuit, but only covers the chest/groin, and also takes up a suit slot. In exchange it has no slowdown and provides storage. -/obj/item/clothing/suit/storage/vest/heavy/merc - name = "heavy armor vest" - desc = "A high-quality heavy kevlar plate carrier in a fetching tan. The vest is surprisingly flexible, and possibly made of an advanced material." - icon_state = "mercwebvest" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) - slowdown = 0 - -//All of the armor below is mostly unused - -/obj/item/clothing/suit/armor/centcomm - name = "CentCom armor" - desc = "A suit that protects against some damage." - icon_state = "centcom" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - w_class = ITEMSIZE_LARGE//bulky item - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen,/obj/item/clothing/head/helmet) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0 - -/obj/item/clothing/suit/armor/heavy - name = "heavy armor" - desc = "An old military-grade suit of armor. Incredibly robust against brute force damage! However, it offers little protection from energy-based weapons, which, combined with its bulk, makes it woefully obsolete." - icon_state = "heavy" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - armor = list(melee = 90, bullet = 80, laser = 10, energy = 10, bomb = 80, bio = 0, rad = 0) - w_class = ITEMSIZE_HUGE // Very bulky, very heavy. - gas_transfer_coefficient = 0.90 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - slowdown = 5 // If you're a tank you're gonna move like a tank. - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - siemens_coefficient = 0 - -/obj/item/clothing/suit/armor/tdome - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) - -/obj/item/clothing/suit/armor/tdome/red - name = "Thunderdome suit (red)" - desc = "Reddish armor." - icon_state = "tdred" - siemens_coefficient = 1 - -/obj/item/clothing/suit/armor/tdome/green - name = "Thunderdome suit (green)" - desc = "Pukish armor." - icon_state = "tdgreen" - siemens_coefficient = 1 - -//Modular plate carriers -/obj/item/clothing/suit/armor/pcarrier - name = "plate carrier" - desc = "A lightweight black plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." - icon = 'icons/obj/clothing/modular_armor.dmi' - item_icons = list(slot_wear_suit_str = 'icons/mob/modular_armor.dmi') - icon_state = "pcarrier" - valid_accessory_slots = (\ - ACCESSORY_SLOT_INSIGNIA\ - |ACCESSORY_SLOT_ARMOR_C\ - |ACCESSORY_SLOT_ARMOR_A\ - |ACCESSORY_SLOT_ARMOR_L\ - |ACCESSORY_SLOT_ARMOR_S\ - |ACCESSORY_SLOT_ARMOR_M) - restricted_accessory_slots = (\ - ACCESSORY_SLOT_INSIGNIA\ - |ACCESSORY_SLOT_ARMOR_C\ - |ACCESSORY_SLOT_ARMOR_A\ - |ACCESSORY_SLOT_ARMOR_L\ - |ACCESSORY_SLOT_ARMOR_S\ - |ACCESSORY_SLOT_ARMOR_M) - blood_overlay_type = "armor" - -/obj/item/clothing/suit/armor/pcarrier/mob_can_equip(var/mob/living/carbon/human/H, slot, disable_warning = FALSE) - if(..()) //This will only run if no other problems occured when equiping. - if(H.gloves) - if(H.gloves.body_parts_covered & ARMS) - for(var/obj/item/clothing/accessory/A in src) - if(A.body_parts_covered & ARMS) - to_chat(H, "You can't wear \the [A] with \the [H.gloves], they're in the way.") - return 0 - if(H.shoes) - if(H.shoes.body_parts_covered & LEGS) - for(var/obj/item/clothing/accessory/A in src) - if(A.body_parts_covered & LEGS) - to_chat(H, "You can't wear \the [A] with \the [H.shoes], they're in the way.") - return 0 - return 1 - -/obj/item/clothing/suit/armor/pcarrier/explorer - name = "explorer suit" - desc = "A lightweight explorer plate carrier. It can be equipped with armor plates, but only protects from the cold on it's own." - icon_state = "explorer" - flags = THICKMATERIAL - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - -/obj/item/clothing/suit/armor/pcarrier/light - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate) - -/obj/item/clothing/suit/armor/pcarrier/light/sol - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate, /obj/item/clothing/accessory/armor/tag) - -/obj/item/clothing/suit/armor/pcarrier/light/nt - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate, /obj/item/clothing/accessory/armor/tag/nt) - -/obj/item/clothing/suit/armor/pcarrier/medium - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches) - -/obj/item/clothing/suit/armor/pcarrier/medium/sol - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag) - -/obj/item/clothing/suit/armor/pcarrier/medium/security - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag/sec) - -/obj/item/clothing/suit/armor/pcarrier/medium/command - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag/com) - -/obj/item/clothing/suit/armor/pcarrier/medium/nt - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag/nt) - -/obj/item/clothing/suit/armor/pcarrier/blue - name = "blue plate carrier" - desc = "A lightweight blue plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." - icon_state = "pcarrier_blue" - -/obj/item/clothing/suit/armor/pcarrier/press - name = "light blue plate carrier" - desc = "A lightweight light blue plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." - icon_state = "pcarrier_press" - -/obj/item/clothing/suit/armor/pcarrier/blue/sol - name = "peacekeeper plate carrier" - desc = "A lightweight plate carrier vest in SCG Peacekeeper colors. It can be equipped with armor plates, but provides no protection of its own." - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches/blue, /obj/item/clothing/accessory/armor/armguards/blue, /obj/item/clothing/accessory/armor/tag) - -/obj/item/clothing/suit/armor/pcarrier/green - name = "green plate carrier" - desc = "A lightweight green plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." - icon_state = "pcarrier_green" - -/obj/item/clothing/suit/armor/pcarrier/navy - name = "navy plate carrier" - desc = "A lightweight navy blue plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." - icon_state = "pcarrier_navy" - -/obj/item/clothing/suit/armor/pcarrier/tan - name = "tan plate carrier" - desc = "A lightweight tan plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." - icon_state = "pcarrier_tan" - -/obj/item/clothing/suit/armor/pcarrier/laserproof - name = "ablative plate carrier" - desc = "A specialist laser resistant plate carrier. It can be equipped with armour plates, but provides very little protection of its own." - icon_state = "ablative" - armor = list(melee = 0, bullet = 0, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0) - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/laserproof) - -/obj/item/clothing/suit/armor/pcarrier/bulletproof - name = "ballistic plate carrier" - desc = "A specialist bullet resistant plate carrier. It can be equipped with armour plates, but provides very little protection of its own." - icon_state = "ballistic" - armor = list(melee = 0, bullet = 5, laser = 0, energy = 0, bomb = 5, bio = 0, rad = 0) - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/bulletproof) - -/obj/item/clothing/suit/armor/pcarrier/riot - name = "riot plate carrier" - desc = "A specialist melee resistant plate carrier. It can be equipped with armour plates, but provides very little protection of its own." - icon_state = "riot" - armor = list(melee = 5, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/riot) - -/obj/item/clothing/suit/armor/pcarrier/explorer/light - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/explorer, /obj/item/clothing/accessory/armor/armguards/explorer, /obj/item/clothing/accessory/armor/legguards/explorer, /obj/item/clothing/accessory/storage/pouches/green) - -/obj/item/clothing/suit/armor/pcarrier/tan/tactical - name = "tactical plate carrier" - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/tactical, /obj/item/clothing/accessory/storage/pouches/large/tan) - -/obj/item/clothing/suit/armor/pcarrier/merc - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/merc, /obj/item/clothing/accessory/armor/armguards/merc, /obj/item/clothing/accessory/armor/legguards/merc, /obj/item/clothing/accessory/storage/pouches/large) - -/obj/item/clothing/suit/armor/pcarrier/laserproof/full - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/laserproof, /obj/item/clothing/accessory/armor/armguards/laserproof, /obj/item/clothing/accessory/armor/legguards/laserproof, /obj/item/clothing/accessory/storage/pouches/blue) - -/obj/item/clothing/suit/armor/pcarrier/bulletproof/full - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/bulletproof, /obj/item/clothing/accessory/armor/armguards/bulletproof, /obj/item/clothing/accessory/armor/legguards/bulletproof, /obj/item/clothing/accessory/storage/pouches) - -/obj/item/clothing/suit/armor/pcarrier/riot/full - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/riot, /obj/item/clothing/accessory/armor/armguards/riot, /obj/item/clothing/accessory/armor/legguards/riot, /obj/item/clothing/accessory/storage/pouches) +/obj/item/clothing/suit/armor + allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/reagent_containers/spray/pepper,/obj/item/weapon/gun/projectile,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/device/flashlight/maglight,/obj/item/clothing/head/helmet) + body_parts_covered = UPPER_TORSO|LOWER_TORSO + item_flags = THICKMATERIAL + + cold_protection = UPPER_TORSO|LOWER_TORSO + min_cold_protection_temperature = ARMOR_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = UPPER_TORSO|LOWER_TORSO + max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE + siemens_coefficient = 0.6 + +/obj/item/clothing/suit/mob_can_equip(var/mob/living/carbon/human/H, slot, disable_warning = FALSE) + if(..()) //This will only run if no other problems occured when equiping. + for(var/obj/item/clothing/I in list(H.gloves, H.shoes)) + if(I && (src.body_parts_covered & ARMS && I.body_parts_covered & ARMS) ) + to_chat(H, "You can't wear \the [src] with \the [I], it's in the way.") + return 0 + if(I && (src.body_parts_covered & LEGS && I.body_parts_covered & LEGS) ) + to_chat(H, "You can't wear \the [src] with \the [I], it's in the way.") + return 0 + return 1 + +/obj/item/clothing/suit/armor/vest + name = "armor" + desc = "An armored vest that protects against some damage." + icon_state = "armor" + blood_overlay_type = "armor" + armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) + +/obj/item/clothing/suit/armor/vest/alt + name = "security armor" + desc = "An armored vest that protects against some damage. This one has a NanoTrasen corporate badge." + icon_state = "armoralt" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + +/obj/item/clothing/suit/armor/vest/security + name = "security armor" + desc = "An armored vest that protects against some damage. This one has a corporate badge." + icon_state = "armorsec" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + +/obj/item/clothing/suit/armor/riot + name = "riot vest" + desc = "A vest with heavy padding to protect against melee attacks." + icon_state = "riot" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + armor = list(melee = 80, bullet = 10, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.5 + +/obj/item/clothing/suit/armor/riot/alt + icon_state = "riot_new" + item_state_slots = list(slot_r_hand_str = "riot_new", slot_l_hand_str = "riot_new") + +/obj/item/clothing/suit/armor/bulletproof + name = "bullet resistant vest" + desc = "A vest that excels in protecting the wearer against high-velocity solid projectiles." + icon_state = "bulletproof" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + blood_overlay_type = "armor" + slowdown = 0.5 + armor = list(melee = 10, bullet = 80, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.7 + +/obj/item/clothing/suit/armor/bulletproof/alt + icon_state = "bulletproof_new" + item_state_slots = list(slot_r_hand_str = "bulletproof_new", slot_l_hand_str = "bulletproof_new") + blood_overlay_type = "armor" + +/obj/item/clothing/suit/armor/laserproof + name = "ablative armor vest" + desc = "A vest that excels in protecting the wearer against energy projectiles." + icon_state = "armor_reflec" + blood_overlay_type = "armor" + slowdown = 0.5 + armor = list(melee = 10, bullet = 10, laser = 80, energy = 50, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.1 + +/obj/item/clothing/suit/armor/laserproof/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(istype(damage_source, /obj/item/projectile/energy) || istype(damage_source, /obj/item/projectile/beam)) + var/obj/item/projectile/P = damage_source + + if(P.reflected) // Can't reflect twice + return ..() + + var/reflectchance = 40 - round(damage/3) + if(!(def_zone in list(BP_TORSO, BP_GROIN))) + reflectchance /= 2 + if(P.starting && prob(reflectchance)) + visible_message("\The [user]'s [src.name] reflects [attack_text]!") + + // Find a turf near or on the original location to bounce to + var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) + var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) + var/turf/curloc = get_turf(user) + + // redirect the projectile + P.redirect(new_x, new_y, curloc, user) + P.reflected = 1 + + return PROJECTILE_CONTINUE // complete projectile permutation + +/obj/item/clothing/suit/armor/combat + name = "combat vest" + desc = "A vest that protects the wearer from several common types of weaponry." + icon_state = "combat" + blood_overlay_type = "armor" + slowdown = 0.5 + armor = list(melee = 50, bullet = 50, laser = 50, energy = 30, bomb = 30, bio = 0, rad = 0) + siemens_coefficient = 0.6 + +/obj/item/clothing/suit/armor/tactical + name = "tactical armor" + desc = "A suit of armor most often used by Special Weapons and Tactics squads. Includes padded vest with pockets along with shoulder and kneeguards." + icon_state = "swatarmor" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDETIE|HIDEHOLSTER + slowdown = 1 + armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) + siemens_coefficient = 0.7 + +/obj/item/clothing/suit/armor/swat + name = "swat suit" + desc = "A heavily armored suit that protects against moderate damage. Used in special operations." + icon_state = "deathsquad" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + item_flags = THICKMATERIAL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS + allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen,/obj/item/clothing/head/helmet) + slowdown = 1 + w_class = ITEMSIZE_HUGE + armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 100, rad = 100) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + min_pressure_protection = 0 * ONE_ATMOSPHERE + max_pressure_protection = 20* ONE_ATMOSPHERE + siemens_coefficient = 0.6 + + +/obj/item/clothing/suit/armor/swat/officer + name = "officer jacket" + desc = "An armored jacket used in special operations." + icon_state = "detective" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + blood_overlay_type = "coat" + flags_inv = 0 + body_parts_covered = UPPER_TORSO|ARMS + + +/obj/item/clothing/suit/armor/det_suit + name = "armor" + desc = "An armored vest with a detective's badge on it." + icon_state = "detective-armor" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + blood_overlay_type = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) + + +//Reactive armor +//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!) +/obj/item/clothing/suit/armor/reactive + name = "Reactive Teleport Armor" + desc = "Someone separated our Research Director from their own head!" + var/active = 0.0 + icon_state = "reactiveoff" + item_state_slots = list(slot_r_hand_str = "armor_reflec_old", slot_l_hand_str = "armor_reflec_old") + blood_overlay_type = "armor" + slowdown = 1 + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) + +/obj/item/clothing/suit/armor/reactive/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(prob(50)) + user.visible_message("The reactive teleport system flings [user] clear of the attack!") + var/list/turfs = new/list() + for(var/turf/T in orange(6, user)) + if(istype(T,/turf/space)) continue + if(T.density) continue + if(T.x>world.maxx-6 || T.x<6) continue + if(T.y>world.maxy-6 || T.y<6) continue + turfs += T + if(!turfs.len) turfs += pick(/turf in orange(6)) + var/turf/picked = pick(turfs) + if(!isturf(picked)) return + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(src, "sparks", 50, 1) + + user.loc = picked + return PROJECTILE_FORCE_MISS + return 0 + +/obj/item/clothing/suit/armor/reactive/attack_self(mob/user as mob) + active = !( active ) + if (active) + to_chat(user, span_blue("The reactive armor is now active.")) + icon_state = "reactive" + else + to_chat(user, span_blue("The reactive armor is now inactive.")) + icon_state = "reactiveoff" + add_fingerprint(user) + return + +/obj/item/clothing/suit/armor/reactive/emp_act(severity) + active = 0 + icon_state = "reactiveoff" + ..() + +// Alien armor has a chance to completely block attacks. +/obj/item/clothing/suit/armor/alien + name = "alien enhancement vest" + desc = "It's a strange piece of what appears to be armor. It looks very light and agile. Strangely enough it seems to have been designed for a humanoid shape." + description_info = "It has a 20% chance to completely nullify an incoming attack, and the wearer moves slightly faster." + icon_state = "alien_speed" + blood_overlay_type = "armor" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + slowdown = -1 + body_parts_covered = UPPER_TORSO|LOWER_TORSO + armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 50, bio = 0, rad = 40) + siemens_coefficient = 0.4 + var/block_chance = 20 + +/obj/item/clothing/suit/armor/alien/tank + name = "alien protection suit" + desc = "It's really resilient yet lightweight, so it's probably meant to be armor. Strangely enough it seems to have been designed for a humanoid shape." + description_info = "It has a 40% chance to completely nullify an incoming attack." + icon_state = "alien_tank" + slowdown = 0 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + armor = list(melee = 70, bullet = 70, laser = 70, energy = 70, bomb = 70, bio = 0, rad = 40) + block_chance = 40 + +/obj/item/clothing/suit/armor/alien/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(prob(block_chance)) + user.visible_message("\The [src] completely absorbs [attack_text]!") + return TRUE + return FALSE + +//Non-hardsuit ERT armor. +/obj/item/clothing/suit/armor/vest/ert + name = "emergency response team armor" + desc = "A set of armor worn by members of the Emergency Response Team." + icon_state = "ertarmor_cmd" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 20, bio = 0, rad = 0) + +//Commander +/obj/item/clothing/suit/armor/vest/ert/command + name = "emergency response team commander armor" + desc = "A set of armor worn by the commander of an Emergency Response Team. Has blue highlights." + +//Security +/obj/item/clothing/suit/armor/vest/ert/security + name = "emergency response team security armor" + desc = "A set of armor worn by security members of the Emergency Response Team. Has red highlights." + icon_state = "ertarmor_sec" + +//Engineer +/obj/item/clothing/suit/armor/vest/ert/engineer + name = "emergency response team engineer armor" + desc = "A set of armor worn by engineering members of the Emergency Response Team. Has orange highlights." + icon_state = "ertarmor_eng" + +//Medical +/obj/item/clothing/suit/armor/vest/ert/medical + name = "emergency response team medical armor" + desc = "A set of armor worn by medical members of the Emergency Response Team. Has blue and white highlights." + icon_state = "ertarmor_med" + +//New Vests +/obj/item/clothing/suit/storage/vest + name = "armor vest" + desc = "A simple kevlar plate carrier." + icon_state = "kvest" + blood_overlay_type = "armor" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) + allowed = list(/obj/item/weapon/gun,/obj/item/weapon/reagent_containers/spray/pepper,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/device/flashlight/maglight,/obj/item/clothing/head/helmet) + + body_parts_covered = UPPER_TORSO|LOWER_TORSO + item_flags = THICKMATERIAL + + cold_protection = UPPER_TORSO|LOWER_TORSO + min_cold_protection_temperature = ARMOR_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = UPPER_TORSO|LOWER_TORSO + max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE + siemens_coefficient = 0.6 + +/obj/item/clothing/suit/storage/vest/officer + name = "officer armor vest" + desc = "A simple kevlar plate carrier. This one has a security holobadge clipped to the chest." + icon_state = "officervest_nobadge" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + icon_badge = "officervest_badge" + icon_nobadge = "officervest_nobadge" + +/obj/item/clothing/suit/storage/vest/warden + name = "warden armor vest" + desc = "A simple kevlar plate carrier. This one has a silver badge clipped to the chest." + icon_state = "wardenvest_nobadge" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + icon_badge = "wardenvest_badge" + icon_nobadge = "wardenvest_nobadge" + +/obj/item/clothing/suit/storage/vest/wardencoat + name = "Warden's jacket" + desc = "An armoured jacket with silver rank pips and livery." + icon_state = "warden_jacket" + blood_overlay_type = "suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + flags_inv = HIDETIE|HIDEHOLSTER + +/obj/item/clothing/suit/storage/vest/wardencoat/alt + name = "Warden's jacket" + desc = "An armoured jacket with silver rank pips and livery." + icon_state = "warden_alt" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + +/obj/item/clothing/suit/storage/vest/hos + name = "head of security armor vest" + desc = "A simple kevlar plate carrier. This one has a gold badge clipped to the chest." + icon_state = "hosvest_nobadge" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + icon_badge = "hosvest_badge" + icon_nobadge = "hosvest_nobadge" + +/obj/item/clothing/suit/storage/vest/hoscoat + name = "armored coat" + desc = "A greatcoat enhanced with a special alloy for some protection and style." + icon_state = "hos" + blood_overlay_type = "suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + flags_inv = HIDETIE|HIDEHOLSTER + +//Jensen cosplay gear +/obj/item/clothing/suit/storage/vest/hoscoat/jensen + name = "armored trenchcoat" + desc = "A trenchcoat augmented with a special alloy for some protection and style." + icon_state = "hostrench" + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/vest/pcrc + name = "PCRC armor vest" + desc = "A simple kevlar plate carrier belonging to Proxima Centauri Risk Control. This one has a PCRC crest clipped to the chest." + icon_state = "pcrcvest_nobadge" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + icon_badge = "pcrcvest_badge" + icon_nobadge = "pcrcvest_nobadge" + +/obj/item/clothing/suit/storage/vest/solgov/hedberg + name = "Hedberg-Hammarstrom armor vest" + desc = "A simple kevlar plate carrier belonging to Hedberg-Hammarstrom. The company logo is clearly visible." + icon_state = "secwebvest" + +/obj/item/clothing/suit/storage/vest/solgov + name = "\improper Solar Confederate Government armored vest" + desc = "A synthetic armor vest. This one is marked with the crest of the Solar Confederate Government." + icon_state = "solvest" + armor = list(melee = 40, bullet = 40, laser = 40, energy = 25, bomb = 30, bio = 0, rad = 0) + +/obj/item/clothing/suit/storage/vest/solgov/heavy + name = "\improper Solar Confederate Government heavy armored vest" + desc = "A synthetic armor vest with Solar Confederate Government printed in distinctive blue lettering on the chest. This one has added webbing and ballistic plates." + icon_state = "solwebvest" + +/obj/item/clothing/suit/storage/vest/solgov/command + name = "command heavy armored vest" + desc = "A synthetic armor vest with Solar Confederate Government printed in detailed gold lettering on the chest. This one has added webbing and ballistic plates." + icon_state = "comwebvest" + +/obj/item/clothing/suit/storage/vest/tactical //crack at a more balanced mid-range armor, minor improvements over standard vests, with the idea "modern" combat armor would focus on energy weapon protection. + name = "tactical armored vest" + desc = "A heavy armored vest in a fetching tan. It is surprisingly flexible and light, even with the extra webbing and advanced ceramic plates." + icon_state = "tacwebvest" + item_state = "tacwebvest" + armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) + +/obj/item/clothing/suit/storage/vest/heavy/flexitac //a reskin of the above to have a matching armor set + name = "tactical light vest" + desc = "An armored vest made from advanced flexible ceramic plates. It's surprisingly mobile, if a little unfashionable." + icon_state = "flexitac" + item_state = "flexitac" + armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) + cold_protection = UPPER_TORSO|LOWER_TORSO + min_cold_protection_temperature = T0C - 20 + slowdown = 0.5 + +/obj/item/clothing/suit/storage/vest/detective + name = "detective armor vest" + desc = "A simple kevlar plate carrier in a vintage brown, it has a badge clipped to the chest that reads, 'Private investigator'." + icon_state = "detectivevest_nobadge" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + icon_badge = "detectivevest_badge" + icon_nobadge = "detectivevest_nobadge" + +/obj/item/clothing/suit/storage/vest/press + name = "press vest" + icon_state = "pvest" + desc = "A simple kevlar plate carrier. This one has the word 'Press' embroidered on patches on the back and front." + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + allowed = list(/obj/item/device/flashlight,/obj/item/device/taperecorder,/obj/item/weapon/pen,/obj/item/device/camera_film,/obj/item/device/camera,/obj/item/clothing/head/helmet) + +/obj/item/clothing/suit/storage/vest/heavy + name = "heavy armor vest" + desc = "A heavy kevlar plate carrier with webbing attached." + icon_state = "webvest" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + armor = list(melee = 50, bullet = 40, laser = 40, energy = 25, bomb = 25, bio = 0, rad = 0) + slowdown = 0.5 + +/obj/item/clothing/suit/storage/vest/heavy/officer + name = "officer heavy armor vest" + desc = "A heavy kevlar plate carrier with webbing attached. This one has a security holobadge clipped to the chest." + icon_state = "officerwebvest_nobadge" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + icon_badge = "officerwebvest_badge" + icon_nobadge = "officerwebvest_nobadge" + +/obj/item/clothing/suit/storage/vest/heavy/warden + name = "warden heavy armor vest" + desc = "A heavy kevlar plate carrier with webbing attached. This one has a silver badge clipped to the chest." + icon_state = "wardenwebvest_nobadge" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + icon_badge = "wardenwebvest_badge" + icon_nobadge = "wardenwebvest_nobadge" + +/obj/item/clothing/suit/storage/vest/heavy/hos + name = "head of security heavy armor vest" + desc = "A heavy kevlar plate carrier with webbing attached. This one has a gold badge clipped to the chest." + icon_state = "hoswebvest_nobadge" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + icon_badge = "hoswebvest_badge" + icon_nobadge = "hoswebvest_nobadge" + +/obj/item/clothing/suit/storage/vest/heavy/pcrc + name = "PCRC heavy armor vest" + desc = "A heavy kevlar plate carrier belonging to Proxima Centauri Risk Control with webbing attached. This one has a PCRC crest clipped to the chest." + icon_state = "pcrcwebvest_nobadge" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + icon_badge = "pcrcwebvest_badge" + icon_nobadge = "pcrcwebvest_nobadge" + +//Provides the protection of a merc voidsuit, but only covers the chest/groin, and also takes up a suit slot. In exchange it has no slowdown and provides storage. +/obj/item/clothing/suit/storage/vest/heavy/merc + name = "heavy armor vest" + desc = "A high-quality heavy kevlar plate carrier in a fetching tan. The vest is surprisingly flexible, and possibly made of an advanced material." + icon_state = "mercwebvest" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) + slowdown = 0 + +//All of the armor below is mostly unused + +/obj/item/clothing/suit/armor/centcomm + name = "CentCom armor" + desc = "A suit that protects against some damage." + icon_state = "centcom" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + w_class = ITEMSIZE_LARGE//bulky item + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen,/obj/item/clothing/head/helmet) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0 + +/obj/item/clothing/suit/armor/heavy + name = "heavy armor" + desc = "An old military-grade suit of armor. Incredibly robust against brute force damage! However, it offers little protection from energy-based weapons, which, combined with its bulk, makes it woefully obsolete." + icon_state = "heavy" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + armor = list(melee = 90, bullet = 80, laser = 10, energy = 10, bomb = 80, bio = 0, rad = 0) + w_class = ITEMSIZE_HUGE // Very bulky, very heavy. + gas_transfer_coefficient = 0.90 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + slowdown = 5 // If you're a tank you're gonna move like a tank. + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + siemens_coefficient = 0 + +/obj/item/clothing/suit/armor/tdome + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) + +/obj/item/clothing/suit/armor/tdome/red + name = "Thunderdome suit (red)" + desc = "Reddish armor." + icon_state = "tdred" + siemens_coefficient = 1 + +/obj/item/clothing/suit/armor/tdome/green + name = "Thunderdome suit (green)" + desc = "Pukish armor." + icon_state = "tdgreen" + siemens_coefficient = 1 + +//Modular plate carriers +/obj/item/clothing/suit/armor/pcarrier + name = "plate carrier" + desc = "A lightweight black plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." + icon = 'icons/obj/clothing/modular_armor.dmi' + item_icons = list(slot_wear_suit_str = 'icons/mob/modular_armor.dmi') + icon_state = "pcarrier" + valid_accessory_slots = (\ + ACCESSORY_SLOT_INSIGNIA\ + |ACCESSORY_SLOT_ARMOR_C\ + |ACCESSORY_SLOT_ARMOR_A\ + |ACCESSORY_SLOT_ARMOR_L\ + |ACCESSORY_SLOT_ARMOR_S\ + |ACCESSORY_SLOT_ARMOR_M) + restricted_accessory_slots = (\ + ACCESSORY_SLOT_INSIGNIA\ + |ACCESSORY_SLOT_ARMOR_C\ + |ACCESSORY_SLOT_ARMOR_A\ + |ACCESSORY_SLOT_ARMOR_L\ + |ACCESSORY_SLOT_ARMOR_S\ + |ACCESSORY_SLOT_ARMOR_M) + blood_overlay_type = "armor" + +/obj/item/clothing/suit/armor/pcarrier/mob_can_equip(var/mob/living/carbon/human/H, slot, disable_warning = FALSE) + if(..()) //This will only run if no other problems occured when equiping. + if(H.gloves) + if(H.gloves.body_parts_covered & ARMS) + for(var/obj/item/clothing/accessory/A in src) + if(A.body_parts_covered & ARMS) + to_chat(H, "You can't wear \the [A] with \the [H.gloves], they're in the way.") + return 0 + if(H.shoes) + if(H.shoes.body_parts_covered & LEGS) + for(var/obj/item/clothing/accessory/A in src) + if(A.body_parts_covered & LEGS) + to_chat(H, "You can't wear \the [A] with \the [H.shoes], they're in the way.") + return 0 + return 1 + +/obj/item/clothing/suit/armor/pcarrier/explorer + name = "explorer suit" + desc = "A lightweight explorer plate carrier. It can be equipped with armor plates, but only protects from the cold on it's own." + icon_state = "explorer" + flags = THICKMATERIAL + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + +/obj/item/clothing/suit/armor/pcarrier/light + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate) + +/obj/item/clothing/suit/armor/pcarrier/light/sol + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate, /obj/item/clothing/accessory/armor/tag) + +/obj/item/clothing/suit/armor/pcarrier/light/nt + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate, /obj/item/clothing/accessory/armor/tag/nt) + +/obj/item/clothing/suit/armor/pcarrier/medium + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches) + +/obj/item/clothing/suit/armor/pcarrier/medium/sol + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag) + +/obj/item/clothing/suit/armor/pcarrier/medium/security + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag/sec) + +/obj/item/clothing/suit/armor/pcarrier/medium/command + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag/com) + +/obj/item/clothing/suit/armor/pcarrier/medium/nt + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag/nt) + +/obj/item/clothing/suit/armor/pcarrier/blue + name = "blue plate carrier" + desc = "A lightweight blue plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." + icon_state = "pcarrier_blue" + +/obj/item/clothing/suit/armor/pcarrier/press + name = "light blue plate carrier" + desc = "A lightweight light blue plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." + icon_state = "pcarrier_press" + +/obj/item/clothing/suit/armor/pcarrier/blue/sol + name = "peacekeeper plate carrier" + desc = "A lightweight plate carrier vest in SCG Peacekeeper colors. It can be equipped with armor plates, but provides no protection of its own." + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches/blue, /obj/item/clothing/accessory/armor/armguards/blue, /obj/item/clothing/accessory/armor/tag) + +/obj/item/clothing/suit/armor/pcarrier/green + name = "green plate carrier" + desc = "A lightweight green plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." + icon_state = "pcarrier_green" + +/obj/item/clothing/suit/armor/pcarrier/navy + name = "navy plate carrier" + desc = "A lightweight navy blue plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." + icon_state = "pcarrier_navy" + +/obj/item/clothing/suit/armor/pcarrier/tan + name = "tan plate carrier" + desc = "A lightweight tan plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." + icon_state = "pcarrier_tan" + +/obj/item/clothing/suit/armor/pcarrier/laserproof + name = "ablative plate carrier" + desc = "A specialist laser resistant plate carrier. It can be equipped with armour plates, but provides very little protection of its own." + icon_state = "ablative" + armor = list(melee = 0, bullet = 0, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0) + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/laserproof) + +/obj/item/clothing/suit/armor/pcarrier/bulletproof + name = "ballistic plate carrier" + desc = "A specialist bullet resistant plate carrier. It can be equipped with armour plates, but provides very little protection of its own." + icon_state = "ballistic" + armor = list(melee = 0, bullet = 5, laser = 0, energy = 0, bomb = 5, bio = 0, rad = 0) + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/bulletproof) + +/obj/item/clothing/suit/armor/pcarrier/riot + name = "riot plate carrier" + desc = "A specialist melee resistant plate carrier. It can be equipped with armour plates, but provides very little protection of its own." + icon_state = "riot" + armor = list(melee = 5, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/riot) + +/obj/item/clothing/suit/armor/pcarrier/explorer/light + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/explorer, /obj/item/clothing/accessory/armor/armguards/explorer, /obj/item/clothing/accessory/armor/legguards/explorer, /obj/item/clothing/accessory/storage/pouches/green) + +/obj/item/clothing/suit/armor/pcarrier/tan/tactical + name = "tactical plate carrier" + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/tactical, /obj/item/clothing/accessory/storage/pouches/large/tan) + +/obj/item/clothing/suit/armor/pcarrier/merc + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/merc, /obj/item/clothing/accessory/armor/armguards/merc, /obj/item/clothing/accessory/armor/legguards/merc, /obj/item/clothing/accessory/storage/pouches/large) + +/obj/item/clothing/suit/armor/pcarrier/laserproof/full + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/laserproof, /obj/item/clothing/accessory/armor/armguards/laserproof, /obj/item/clothing/accessory/armor/legguards/laserproof, /obj/item/clothing/accessory/storage/pouches/blue) + +/obj/item/clothing/suit/armor/pcarrier/bulletproof/full + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/bulletproof, /obj/item/clothing/accessory/armor/armguards/bulletproof, /obj/item/clothing/accessory/armor/legguards/bulletproof, /obj/item/clothing/accessory/storage/pouches) + +/obj/item/clothing/suit/armor/pcarrier/riot/full + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/riot, /obj/item/clothing/accessory/armor/armguards/riot, /obj/item/clothing/accessory/armor/legguards/riot, /obj/item/clothing/accessory/storage/pouches) diff --git a/code/modules/clothing/suits/bio.dm b/code/modules/clothing/suits/bio.dm index de610ca3ee1..60237b385ed 100644 --- a/code/modules/clothing/suits/bio.dm +++ b/code/modules/clothing/suits/bio.dm @@ -1,95 +1,95 @@ -//Biosuit complete with shoes (in the item sprite) -/obj/item/clothing/head/bio_hood - name = "bio hood" - icon_state = "bio" - desc = "A hood that protects the head and face from biological comtaminants." - randpixel = 0 - center_of_mass = null - permeability_coefficient = 0.01 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 20) - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - siemens_coefficient = 0.9 - flags = PHORONGUARD - item_flags = THICKMATERIAL | ALLOW_SURVIVALFOOD - -/obj/item/clothing/suit/bio_suit - name = "bio suit" - desc = "A suit that protects against biological contamination." - icon_state = "bio" - w_class = ITEMSIZE_LARGE//bulky item - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS|FEET - slowdown = 1.0 - allowed = list(/obj/item/weapon/tank/emergency/oxygen,/obj/item/weapon/pen,/obj/item/device/flashlight/pen) - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 20) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - siemens_coefficient = 0.9 - flags = PHORONGUARD - item_flags = THICKMATERIAL - -//Standard biosuit, orange stripe -/obj/item/clothing/head/bio_hood/general - icon_state = "bio_general" - item_state_slots = list(slot_r_hand_str = "bio", slot_l_hand_str = "bio") - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/suit/bio_suit/general - icon_state = "bio_general" - item_state_slots = list(slot_r_hand_str = "bio", slot_l_hand_str = "bio") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - -//Virology biosuit, green stripe -/obj/item/clothing/head/bio_hood/virology - icon_state = "bio_virology" - -/obj/item/clothing/suit/bio_suit/virology - icon_state = "bio_virology" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - -//Security biosuit, grey with red stripe across the chest -/obj/item/clothing/head/bio_hood/security - icon_state = "bio_security" - -/obj/item/clothing/suit/bio_suit/security - icon_state = "bio_security" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - -//Janitor's biosuit, grey with purple arms -/obj/item/clothing/head/bio_hood/janitor - icon_state = "bio_janitor" - -/obj/item/clothing/suit/bio_suit/janitor - icon_state = "bio_janitor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - -//Scientist's biosuit, white with a pink-ish hue -/obj/item/clothing/head/bio_hood/scientist - icon_state = "bio_scientist" - -/obj/item/clothing/suit/bio_suit/scientist - icon_state = "bio_scientist" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - -//CMO's biosuit, blue stripe -/obj/item/clothing/suit/bio_suit/cmo - icon_state = "bio_cmo" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - -/obj/item/clothing/head/bio_hood/cmo - icon_state = "bio_cmo" - -//Plague Dr mask can be found in clothing/masks/gasmask.dm. Golden can be found in labcoat.dm. -/obj/item/clothing/suit/bio_suit/plaguedoctorsuit - name = "plague doctor suit" - desc = "It protected doctors from the Black Death, back then. You bet your arse it's gonna help you against viruses." - icon_state = "plaguedoctor" - item_state_slots = list(slot_r_hand_str = "bio", slot_l_hand_str = "bio") +//Biosuit complete with shoes (in the item sprite) +/obj/item/clothing/head/bio_hood + name = "bio hood" + icon_state = "bio" + desc = "A hood that protects the head and face from biological comtaminants." + randpixel = 0 + center_of_mass = null + permeability_coefficient = 0.01 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 20) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + siemens_coefficient = 0.9 + flags = PHORONGUARD + item_flags = THICKMATERIAL | ALLOW_SURVIVALFOOD + +/obj/item/clothing/suit/bio_suit + name = "bio suit" + desc = "A suit that protects against biological contamination." + icon_state = "bio" + w_class = ITEMSIZE_LARGE//bulky item + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS|FEET + slowdown = 1.0 + allowed = list(/obj/item/weapon/tank/emergency/oxygen,/obj/item/weapon/pen,/obj/item/device/flashlight/pen) + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 20) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + siemens_coefficient = 0.9 + flags = PHORONGUARD + item_flags = THICKMATERIAL + +//Standard biosuit, orange stripe +/obj/item/clothing/head/bio_hood/general + icon_state = "bio_general" + item_state_slots = list(slot_r_hand_str = "bio", slot_l_hand_str = "bio") + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/suit/bio_suit/general + icon_state = "bio_general" + item_state_slots = list(slot_r_hand_str = "bio", slot_l_hand_str = "bio") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + +//Virology biosuit, green stripe +/obj/item/clothing/head/bio_hood/virology + icon_state = "bio_virology" + +/obj/item/clothing/suit/bio_suit/virology + icon_state = "bio_virology" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + +//Security biosuit, grey with red stripe across the chest +/obj/item/clothing/head/bio_hood/security + icon_state = "bio_security" + +/obj/item/clothing/suit/bio_suit/security + icon_state = "bio_security" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + +//Janitor's biosuit, grey with purple arms +/obj/item/clothing/head/bio_hood/janitor + icon_state = "bio_janitor" + +/obj/item/clothing/suit/bio_suit/janitor + icon_state = "bio_janitor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + +//Scientist's biosuit, white with a pink-ish hue +/obj/item/clothing/head/bio_hood/scientist + icon_state = "bio_scientist" + +/obj/item/clothing/suit/bio_suit/scientist + icon_state = "bio_scientist" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + +//CMO's biosuit, blue stripe +/obj/item/clothing/suit/bio_suit/cmo + icon_state = "bio_cmo" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + +/obj/item/clothing/head/bio_hood/cmo + icon_state = "bio_cmo" + +//Plague Dr mask can be found in clothing/masks/gasmask.dm. Golden can be found in labcoat.dm. +/obj/item/clothing/suit/bio_suit/plaguedoctorsuit + name = "plague doctor suit" + desc = "It protected doctors from the Black Death, back then. You bet your arse it's gonna help you against viruses." + icon_state = "plaguedoctor" + item_state_slots = list(slot_r_hand_str = "bio", slot_l_hand_str = "bio") flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER \ No newline at end of file diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm index bc96a3847b8..37ac8cd948d 100644 --- a/code/modules/clothing/suits/jobs.dm +++ b/code/modules/clothing/suits/jobs.dm @@ -1,268 +1,268 @@ -/* - * Job related - */ - -//Botanist -/obj/item/clothing/suit/storage/apron - name = "apron" - desc = "A basic blue apron." - icon_state = "apron" - item_state_slots = list(slot_r_hand_str = "overalls", slot_l_hand_str = "overalls") - blood_overlay_type = "armor" - body_parts_covered = 0 - allowed = list (/obj/item/weapon/reagent_containers/spray/plantbgone, /obj/item/device/analyzer/plant_analyzer, /obj/item/seeds, - /obj/item/weapon/reagent_containers/glass/bottle, /obj/item/weapon/material/minihoe) - -/obj/item/clothing/suit/storage/apron/white - name = "white apron" - desc = "A basic white apron." - icon_state = "apron_white" - item_state_slots = list(slot_r_hand_str = "apronchef", slot_l_hand_str = "apronchef") - -/obj/item/clothing/suit/storage/apron/altevian - name = "Multi-purpose Crafters' Pride" - desc = "An apron designed by rodent-like spacers who take pride in their work. It's made with high-quality material." - icon_state = "apron_altevian" - item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) - -//Captain -/obj/item/clothing/suit/captunic - name = "site manager's parade tunic" - desc = "Worn by a Site Manager to show their class." - icon_state = "captunic" - body_parts_covered = UPPER_TORSO|ARMS - flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - -/obj/item/clothing/suit/captunic/capjacket - name = "site manager's uniform jacket" - desc = "A less formal jacket for everyday Site Manager use." - icon_state = "capjacket" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEHOLSTER - -//Command -/obj/item/clothing/suit/storage/toggle/cmddressjacket - name = "command dress jacket" - desc = "A fancy dress jacket made for command staff. Makes you feel in charge." - icon_state = "cmddressjacket" - -//Chaplain -/obj/item/clothing/suit/storage/hooded/chaplain_hoodie - name = "chaplain hoodie" - desc = "This suit says to you \"Hush\"!" - icon_state = "chaplain_hoodie" - item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEHOLSTER - hoodtype = /obj/item/clothing/head/chaplain_hood - allowed = list (/obj/item/weapon/storage/bible) - -//Chaplain but spookier -/obj/item/clothing/suit/storage/hooded/chaplain_hoodie/whiteout - name = "white robe" - desc = "A long, flowing white robe. It looks comfortable, but not very warm." - icon_state = "whiteout_robe" - item_state_slots = list(slot_r_hand_str = "suit_white", slot_l_hand_str = "suit_white") - flags_inv = HIDETIE|HIDEHOLSTER - hoodtype = /obj/item/clothing/head/chaplain_hood/whiteout - -//Chaplain -/obj/item/clothing/suit/nun - name = "nun robe" - desc = "Maximum piety in this star system." - icon_state = "nun" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - -//Chef -/obj/item/clothing/suit/chef - name = "chef's apron" - desc = "An apron used by a high class chef." - icon_state = "chef" - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.50 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_inv = HIDETIE|HIDEHOLSTER - allowed = list (/obj/item/weapon/material/knife) - -//Chef -/obj/item/clothing/suit/chef/classic - name = "classic chef's apron" - desc = "A basic, dull, white chef's apron." - icon_state = "apronchef" - blood_overlay_type = "armor" - body_parts_covered = 0 - flags_inv = 0 - -//Security -/obj/item/clothing/suit/security/navyofficer - name = "security officer's jacket" - desc = "This jacket is for those special occasions when a security officer actually feels safe." - icon_state = "officerbluejacket" - item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/security/navywarden - name = "warden's jacket" - desc = "Perfectly suited for the warden that wants to leave an impression of style on those who visit the brig." - icon_state = "wardenbluejacket" - item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/security/navyhos - name = "head of security's jacket" - desc = "This piece of clothing was specifically designed for asserting superior authority." - icon_state = "hosbluejacket" - item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_inv = HIDEHOLSTER - -//Detective -/obj/item/clothing/suit/storage/det_trench - name = "brown trenchcoat" - desc = "A rugged canvas trenchcoat, designed and created by TX Fabrication Corp. The coat is externally impact resistant - perfect for your next act of autodefenestration!" - icon_state = "detective" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - flags_inv = HIDEHOLSTER - allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight, /obj/item/weapon/gun/energy, /obj/item/weapon/gun/projectile, /obj/item/ammo_magazine, - /obj/item/ammo_casing, /obj/item/weapon/melee/baton, /obj/item/weapon/handcuffs, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/flame/lighter, - /obj/item/device/taperecorder, /obj/item/device/uv_light) - armor = list(melee = 10, bullet = 10, laser = 15, energy = 10, bomb = 0, bio = 0, rad = 0) - -/obj/item/clothing/suit/storage/det_trench/grey - name = "grey trenchcoat" - icon_state = "detective2" - item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") - flags_inv = HIDEHOLSTER - -//Forensics -/obj/item/clothing/suit/storage/forensics - name = "jacket" - desc = "A forensics technician jacket." - body_parts_covered = UPPER_TORSO|ARMS - flags_inv = HIDEHOLSTER - allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight, /obj/item/weapon/gun/energy, /obj/item/weapon/gun/projectile, /obj/item/ammo_magazine, - /obj/item/ammo_casing, /obj/item/weapon/melee/baton, /obj/item/weapon/handcuffs, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/flame/lighter, - /obj/item/device/taperecorder, /obj/item/device/uv_light) - armor = list(melee = 10, bullet = 10, laser = 15, energy = 10, bomb = 0, bio = 0, rad = 0) - -/obj/item/clothing/suit/storage/forensics/red - name = "red jacket" - desc = "A red forensics technician jacket." - icon_state = "forensics_red" - item_state_slots = list(slot_r_hand_str = "suit_red", slot_l_hand_str = "suit_red") - -/obj/item/clothing/suit/storage/forensics/red/long - name = "long red jacket" - desc = "A long red forensics technician jacket." - icon_state = "forensics_red_long" - -/obj/item/clothing/suit/storage/forensics/blue - name = "blue jacket" - desc = "A blue forensics technician jacket." - icon_state = "forensics_blue" - item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") - -/obj/item/clothing/suit/storage/forensics/blue/long - name = "long blue jacket" - desc = "A long blue forensics technician jacket." - icon_state = "forensics_blue_long" - -//Engineering -/obj/item/clothing/suit/storage/hazardvest - name = "hazard vest" - desc = "A high-visibility vest used in work zones." - icon_state = "hazard" - blood_overlay_type = "armor" - allowed = list (/obj/item/device/analyzer, /obj/item/device/flashlight, /obj/item/device/multitool, /obj/item/device/pipe_painter, /obj/item/device/radio, /obj/item/device/t_scanner, - /obj/item/weapon/tool/crowbar, /obj/item/weapon/tool/screwdriver, /obj/item/weapon/weldingtool, /obj/item/weapon/tool/wirecutters, /obj/item/weapon/tool/wrench, /obj/item/weapon/tank/emergency/oxygen, - /obj/item/clothing/mask/gas, /obj/item/taperoll/engineering, /obj/item/taperoll/atmos, /obj/item/device/analyzer, /obj/item/weapon/extinguisher/mini) //VOREStation edit. Few more tools that can be put on vests - body_parts_covered = UPPER_TORSO - -/obj/item/clothing/suit/storage/hazardvest/blue - name = "blue hazard vest" - desc = "A high-visibility vest used in work zones. This one is blue!" - icon_state = "hazard_b" - -/obj/item/clothing/suit/storage/hazardvest/green - name = "green hazard vest" - desc = "A high-visibility vest used by emergency responders." - icon_state = "hazard_g" - -/obj/item/clothing/suit/storage/hazardvest/white - name = "white hazard vest" - desc = "A high-visibility vest used in work zones. This one bears the symbol of a disaster relief team!" - icon_state = "hazard_w" - -//Lawyer -/obj/item/clothing/suit/storage/toggle/lawyer/bluejacket - name = "blue suit jacket" - desc = "A snappy dress jacket." - icon_state = "suitjacket_blue" - item_state_slots = list(slot_r_hand_str = "suit_blue", slot_l_hand_str = "suit_blue") - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - -/obj/item/clothing/suit/storage/toggle/lawyer/purpjacket - name = "purple suit jacket" - desc = "A snappy dress jacket." - icon_state = "suitjacket_purp" - item_state_slots = list(slot_r_hand_str = "suit_purple", slot_l_hand_str = "suit_purple") - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - -//Internal Affairs -/obj/item/clothing/suit/storage/toggle/internalaffairs - name = "black suit jacket" - desc = "A smooth black jacket." - icon_state = "ia_jacket" - item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - -//Medical -/obj/item/clothing/suit/storage/toggle/fr_jacket - name = "first responder jacket" - desc = "A high-visibility jacket worn by medical first responders." - icon_state = "fr_jacket" - item_state_slots = list(slot_r_hand_str = "fr_jacket", slot_l_hand_str = "fr_jacket") - blood_overlay_type = "armor" - allowed = list(/obj/item/stack/medical, /obj/item/weapon/reagent_containers/dropper, /obj/item/weapon/reagent_containers/hypospray, /obj/item/weapon/reagent_containers/syringe, - /obj/item/device/healthanalyzer, /obj/item/device/flashlight, /obj/item/device/radio, /obj/item/weapon/tank/emergency/oxygen) - body_parts_covered = UPPER_TORSO|ARMS - -/obj/item/clothing/suit/storage/toggle/fr_jacket/ems - name = "\improper EMS jacket" - desc = "A dark blue, martian-pattern, EMS jacket. It sports high-visibility reflective stripes and a star of life on the back." - icon_state = "ems_jacket" - item_state_slots = list(slot_r_hand_str = "ems_jacket", slot_l_hand_str = "ems_jacket") - -/obj/item/clothing/suit/surgicalapron - name = "surgical apron" - desc = "A sterile blue apron for performing surgery." - icon_state = "surgical" - blood_overlay_type = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - allowed = list(/obj/item/stack/medical, /obj/item/weapon/reagent_containers/dropper, /obj/item/weapon/reagent_containers/hypospray, /obj/item/weapon/reagent_containers/syringe, \ - /obj/item/device/healthanalyzer, /obj/item/device/flashlight, /obj/item/device/radio, /obj/item/weapon/tank/emergency/oxygen,/obj/item/weapon/surgical/scalpel,/obj/item/weapon/surgical/retractor,/obj/item/weapon/surgical/hemostat, \ - /obj/item/weapon/surgical/cautery,/obj/item/weapon/surgical/bonegel,/obj/item/weapon/surgical/FixOVein) - -//Mime -/obj/item/clothing/suit/suspenders - name = "red suspenders" - desc = "They suspend the illusion of the mime's play." - icon = 'icons/inventory/belt/item.dmi' - icon_state = "suspenders" - blood_overlay_type = "armor" //it's the less thing that I can put here - body_parts_covered = 0 - -/obj/item/clothing/suit/suspenders/blue - name = "blue suspenders" - icon_state = "suspenders_blue" - -/obj/item/clothing/suit/suspenders/grey - name = "grey suspenders" - icon_state = "suspenders_grey" +/* + * Job related + */ + +//Botanist +/obj/item/clothing/suit/storage/apron + name = "apron" + desc = "A basic blue apron." + icon_state = "apron" + item_state_slots = list(slot_r_hand_str = "overalls", slot_l_hand_str = "overalls") + blood_overlay_type = "armor" + body_parts_covered = 0 + allowed = list (/obj/item/weapon/reagent_containers/spray/plantbgone, /obj/item/device/analyzer/plant_analyzer, /obj/item/seeds, + /obj/item/weapon/reagent_containers/glass/bottle, /obj/item/weapon/material/minihoe) + +/obj/item/clothing/suit/storage/apron/white + name = "white apron" + desc = "A basic white apron." + icon_state = "apron_white" + item_state_slots = list(slot_r_hand_str = "apronchef", slot_l_hand_str = "apronchef") + +/obj/item/clothing/suit/storage/apron/altevian + name = "Multi-purpose Crafters' Pride" + desc = "An apron designed by rodent-like spacers who take pride in their work. It's made with high-quality material." + icon_state = "apron_altevian" + item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) + +//Captain +/obj/item/clothing/suit/captunic + name = "site manager's parade tunic" + desc = "Worn by a Site Manager to show their class." + icon_state = "captunic" + body_parts_covered = UPPER_TORSO|ARMS + flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + +/obj/item/clothing/suit/captunic/capjacket + name = "site manager's uniform jacket" + desc = "A less formal jacket for everyday Site Manager use." + icon_state = "capjacket" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEHOLSTER + +//Command +/obj/item/clothing/suit/storage/toggle/cmddressjacket + name = "command dress jacket" + desc = "A fancy dress jacket made for command staff. Makes you feel in charge." + icon_state = "cmddressjacket" + +//Chaplain +/obj/item/clothing/suit/storage/hooded/chaplain_hoodie + name = "chaplain hoodie" + desc = "This suit says to you \"Hush\"!" + icon_state = "chaplain_hoodie" + item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEHOLSTER + hoodtype = /obj/item/clothing/head/chaplain_hood + allowed = list (/obj/item/weapon/storage/bible) + +//Chaplain but spookier +/obj/item/clothing/suit/storage/hooded/chaplain_hoodie/whiteout + name = "white robe" + desc = "A long, flowing white robe. It looks comfortable, but not very warm." + icon_state = "whiteout_robe" + item_state_slots = list(slot_r_hand_str = "suit_white", slot_l_hand_str = "suit_white") + flags_inv = HIDETIE|HIDEHOLSTER + hoodtype = /obj/item/clothing/head/chaplain_hood/whiteout + +//Chaplain +/obj/item/clothing/suit/nun + name = "nun robe" + desc = "Maximum piety in this star system." + icon_state = "nun" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + +//Chef +/obj/item/clothing/suit/chef + name = "chef's apron" + desc = "An apron used by a high class chef." + icon_state = "chef" + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.50 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDETIE|HIDEHOLSTER + allowed = list (/obj/item/weapon/material/knife) + +//Chef +/obj/item/clothing/suit/chef/classic + name = "classic chef's apron" + desc = "A basic, dull, white chef's apron." + icon_state = "apronchef" + blood_overlay_type = "armor" + body_parts_covered = 0 + flags_inv = 0 + +//Security +/obj/item/clothing/suit/security/navyofficer + name = "security officer's jacket" + desc = "This jacket is for those special occasions when a security officer actually feels safe." + icon_state = "officerbluejacket" + item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/security/navywarden + name = "warden's jacket" + desc = "Perfectly suited for the warden that wants to leave an impression of style on those who visit the brig." + icon_state = "wardenbluejacket" + item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/security/navyhos + name = "head of security's jacket" + desc = "This piece of clothing was specifically designed for asserting superior authority." + icon_state = "hosbluejacket" + item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDEHOLSTER + +//Detective +/obj/item/clothing/suit/storage/det_trench + name = "brown trenchcoat" + desc = "A rugged canvas trenchcoat, designed and created by TX Fabrication Corp. The coat is externally impact resistant - perfect for your next act of autodefenestration!" + icon_state = "detective" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + flags_inv = HIDEHOLSTER + allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight, /obj/item/weapon/gun/energy, /obj/item/weapon/gun/projectile, /obj/item/ammo_magazine, + /obj/item/ammo_casing, /obj/item/weapon/melee/baton, /obj/item/weapon/handcuffs, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/flame/lighter, + /obj/item/device/taperecorder, /obj/item/device/uv_light) + armor = list(melee = 10, bullet = 10, laser = 15, energy = 10, bomb = 0, bio = 0, rad = 0) + +/obj/item/clothing/suit/storage/det_trench/grey + name = "grey trenchcoat" + icon_state = "detective2" + item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") + flags_inv = HIDEHOLSTER + +//Forensics +/obj/item/clothing/suit/storage/forensics + name = "jacket" + desc = "A forensics technician jacket." + body_parts_covered = UPPER_TORSO|ARMS + flags_inv = HIDEHOLSTER + allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight, /obj/item/weapon/gun/energy, /obj/item/weapon/gun/projectile, /obj/item/ammo_magazine, + /obj/item/ammo_casing, /obj/item/weapon/melee/baton, /obj/item/weapon/handcuffs, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/flame/lighter, + /obj/item/device/taperecorder, /obj/item/device/uv_light) + armor = list(melee = 10, bullet = 10, laser = 15, energy = 10, bomb = 0, bio = 0, rad = 0) + +/obj/item/clothing/suit/storage/forensics/red + name = "red jacket" + desc = "A red forensics technician jacket." + icon_state = "forensics_red" + item_state_slots = list(slot_r_hand_str = "suit_red", slot_l_hand_str = "suit_red") + +/obj/item/clothing/suit/storage/forensics/red/long + name = "long red jacket" + desc = "A long red forensics technician jacket." + icon_state = "forensics_red_long" + +/obj/item/clothing/suit/storage/forensics/blue + name = "blue jacket" + desc = "A blue forensics technician jacket." + icon_state = "forensics_blue" + item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") + +/obj/item/clothing/suit/storage/forensics/blue/long + name = "long blue jacket" + desc = "A long blue forensics technician jacket." + icon_state = "forensics_blue_long" + +//Engineering +/obj/item/clothing/suit/storage/hazardvest + name = "hazard vest" + desc = "A high-visibility vest used in work zones." + icon_state = "hazard" + blood_overlay_type = "armor" + allowed = list (/obj/item/device/analyzer, /obj/item/device/flashlight, /obj/item/device/multitool, /obj/item/device/pipe_painter, /obj/item/device/radio, /obj/item/device/t_scanner, + /obj/item/weapon/tool/crowbar, /obj/item/weapon/tool/screwdriver, /obj/item/weapon/weldingtool, /obj/item/weapon/tool/wirecutters, /obj/item/weapon/tool/wrench, /obj/item/weapon/tank/emergency/oxygen, + /obj/item/clothing/mask/gas, /obj/item/taperoll/engineering, /obj/item/taperoll/atmos, /obj/item/device/analyzer, /obj/item/weapon/extinguisher/mini) //VOREStation edit. Few more tools that can be put on vests + body_parts_covered = UPPER_TORSO + +/obj/item/clothing/suit/storage/hazardvest/blue + name = "blue hazard vest" + desc = "A high-visibility vest used in work zones. This one is blue!" + icon_state = "hazard_b" + +/obj/item/clothing/suit/storage/hazardvest/green + name = "green hazard vest" + desc = "A high-visibility vest used by emergency responders." + icon_state = "hazard_g" + +/obj/item/clothing/suit/storage/hazardvest/white + name = "white hazard vest" + desc = "A high-visibility vest used in work zones. This one bears the symbol of a disaster relief team!" + icon_state = "hazard_w" + +//Lawyer +/obj/item/clothing/suit/storage/toggle/lawyer/bluejacket + name = "blue suit jacket" + desc = "A snappy dress jacket." + icon_state = "suitjacket_blue" + item_state_slots = list(slot_r_hand_str = "suit_blue", slot_l_hand_str = "suit_blue") + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + +/obj/item/clothing/suit/storage/toggle/lawyer/purpjacket + name = "purple suit jacket" + desc = "A snappy dress jacket." + icon_state = "suitjacket_purp" + item_state_slots = list(slot_r_hand_str = "suit_purple", slot_l_hand_str = "suit_purple") + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + +//Internal Affairs +/obj/item/clothing/suit/storage/toggle/internalaffairs + name = "black suit jacket" + desc = "A smooth black jacket." + icon_state = "ia_jacket" + item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + +//Medical +/obj/item/clothing/suit/storage/toggle/fr_jacket + name = "first responder jacket" + desc = "A high-visibility jacket worn by medical first responders." + icon_state = "fr_jacket" + item_state_slots = list(slot_r_hand_str = "fr_jacket", slot_l_hand_str = "fr_jacket") + blood_overlay_type = "armor" + allowed = list(/obj/item/stack/medical, /obj/item/weapon/reagent_containers/dropper, /obj/item/weapon/reagent_containers/hypospray, /obj/item/weapon/reagent_containers/syringe, + /obj/item/device/healthanalyzer, /obj/item/device/flashlight, /obj/item/device/radio, /obj/item/weapon/tank/emergency/oxygen) + body_parts_covered = UPPER_TORSO|ARMS + +/obj/item/clothing/suit/storage/toggle/fr_jacket/ems + name = "\improper EMS jacket" + desc = "A dark blue, martian-pattern, EMS jacket. It sports high-visibility reflective stripes and a star of life on the back." + icon_state = "ems_jacket" + item_state_slots = list(slot_r_hand_str = "ems_jacket", slot_l_hand_str = "ems_jacket") + +/obj/item/clothing/suit/surgicalapron + name = "surgical apron" + desc = "A sterile blue apron for performing surgery." + icon_state = "surgical" + blood_overlay_type = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + allowed = list(/obj/item/stack/medical, /obj/item/weapon/reagent_containers/dropper, /obj/item/weapon/reagent_containers/hypospray, /obj/item/weapon/reagent_containers/syringe, \ + /obj/item/device/healthanalyzer, /obj/item/device/flashlight, /obj/item/device/radio, /obj/item/weapon/tank/emergency/oxygen,/obj/item/weapon/surgical/scalpel,/obj/item/weapon/surgical/retractor,/obj/item/weapon/surgical/hemostat, \ + /obj/item/weapon/surgical/cautery,/obj/item/weapon/surgical/bonegel,/obj/item/weapon/surgical/FixOVein) + +//Mime +/obj/item/clothing/suit/suspenders + name = "red suspenders" + desc = "They suspend the illusion of the mime's play." + icon = 'icons/inventory/belt/item.dmi' + icon_state = "suspenders" + blood_overlay_type = "armor" //it's the less thing that I can put here + body_parts_covered = 0 + +/obj/item/clothing/suit/suspenders/blue + name = "blue suspenders" + icon_state = "suspenders_blue" + +/obj/item/clothing/suit/suspenders/grey + name = "grey suspenders" + icon_state = "suspenders_grey" diff --git a/code/modules/clothing/suits/labcoat.dm b/code/modules/clothing/suits/labcoat.dm index 5d0c531c5cc..0e5d4d938b8 100644 --- a/code/modules/clothing/suits/labcoat.dm +++ b/code/modules/clothing/suits/labcoat.dm @@ -1,123 +1,123 @@ -/obj/item/clothing/suit/storage/toggle/labcoat - name = "labcoat" - desc = "A suit that protects against minor chemical spills." - icon_state = "labcoat" - item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - flags_inv = HIDEHOLSTER - allowed = list(/obj/item/device/analyzer,/obj/item/stack/medical,/obj/item/weapon/dnainjector,/obj/item/weapon/reagent_containers/dropper,/obj/item/weapon/reagent_containers/syringe,/obj/item/weapon/reagent_containers/hypospray,/obj/item/device/healthanalyzer,/obj/item/device/flashlight/pen,/obj/item/weapon/reagent_containers/glass/bottle,/obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/storage/pill_bottle,/obj/item/weapon/paper, /obj/item/clothing/mask/gas) - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 50, rad = 0) - -/obj/item/clothing/suit/storage/toggle/labcoat/red - name = "red labcoat" - desc = "A suit that protects against minor chemical spills. This one is red." - icon_state = "red_labcoat" - item_state_slots = list(slot_r_hand_str = "red_labcoat", slot_l_hand_str = "red_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/blue - name = "blue labcoat" - desc = "A suit that protects against minor chemical spills. This one is blue." - icon_state = "blue_labcoat" - item_state_slots = list(slot_r_hand_str = "blue_labcoat", slot_l_hand_str = "blue_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/purple - name = "purple labcoat" - desc = "A suit that protects against minor chemical spills. This one is purple." - icon_state = "purple_labcoat" - item_state_slots = list(slot_r_hand_str = "purple_labcoat", slot_l_hand_str = "purple_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/orange - name = "orange labcoat" - desc = "A suit that protects against minor chemical spills. This one is orange." - icon_state = "orange_labcoat" - item_state_slots = list(slot_r_hand_str = "orange_labcoat", slot_l_hand_str = "orange_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/yellow - name = "yellow labcoat" - desc = "A suit that protects against minor chemical spills. This one is yellow." - icon_state = "yellow_labcoat" - item_state_slots = list(slot_r_hand_str = "yellow_labcoat", slot_l_hand_str = "yellow_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/pink - name = "pink labcoat" - desc = "A suit that protects against minor chemical spills. This one is pink." - icon_state = "pink_labcoat" - item_state_slots = list(slot_r_hand_str = "pink_labcoat", slot_l_hand_str = "pink_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/green - name = "green labcoat" - desc = "A suit that protects against minor chemical spills. This one is green." - icon_state = "green_labcoat" - item_state_slots = list(slot_r_hand_str = "green_labcoat", slot_l_hand_str = "green_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/mad - name = "The Mad's labcoat" - desc = "It makes you look capable of konking someone on the noggin and shooting them into space." - icon_state = "green_labcoat" - item_state_slots = list(slot_r_hand_str = "green_labcoat", slot_l_hand_str = "green_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/cmo - name = "chief medical officer's labcoat" - desc = "Bluer than the standard model." - icon_state = "labcoat_cmo" - item_state_slots = list(slot_r_hand_str = "cmo_labcoat", slot_l_hand_str = "cmo_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/cmoalt - name = "chief medical officer's labcoat" - desc = "A labcoat with command blue highlights." - icon_state = "labcoat_cmoalt" - item_state_slots = list(slot_r_hand_str = "cmo_labcoat", slot_l_hand_str = "cmo_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/genetics - name = "Geneticist labcoat" - desc = "A suit that protects against minor chemical spills. Has a blue stripe on the shoulder." - icon_state = "labcoat_gen" - item_state_slots = list(slot_r_hand_str = "genetics_labcoat", slot_l_hand_str = "genetics_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/chemist - name = "Chemist labcoat" - desc = "A suit that protects against minor chemical spills. Has an orange stripe on the shoulder." - icon_state = "labcoat_chem" - item_state_slots = list(slot_r_hand_str = "chemist_labcoat", slot_l_hand_str = "chemist_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/virologist - name = "Virologist labcoat" - desc = "A suit that protects against minor chemical spills. Offers slightly more protection against biohazards than the standard model. Has a green stripe on the shoulder." - icon_state = "labcoat_vir" - item_state_slots = list(slot_r_hand_str = "virologist_labcoat", slot_l_hand_str = "virologist_labcoat") - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 0) - -/obj/item/clothing/suit/storage/toggle/labcoat/roboticist - name = "Roboticist labcoat" - desc = "More like an eccentric coat than a labcoat. Helps pass off bloodstains as part of the aesthetic. Comes with red shoulder pads." - icon_state = "labcoat_robo" - item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/science - name = "Scientist labcoat" - desc = "A suit that protects against minor chemical spills. Has a purple stripe on the shoulder." - icon_state = "labcoat_tox" - item_state_slots = list(slot_r_hand_str = "science_labcoat", slot_l_hand_str = "science_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/rd - name = "research director's labcoat" - desc = "A flashy labcoat with purple markings. It belongs to the Research Director." - icon_state = "labcoat_rd" - item_state_slots = list(slot_r_hand_str = "science_labcoat", slot_l_hand_str = "science_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/emt - name = "EMT's labcoat" - desc = "A dark blue labcoat with reflective strips for emergency medical technicians." - icon_state = "labcoat_emt" - item_state_slots = list(slot_r_hand_str = "emt_labcoat", slot_l_hand_str = "emt_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/blue_edge - name = "blue-edged labcoat" - desc = "A suit that protects against minor chemical spills. This one has blue trim." - icon_state = "blue_edge_labcoat" - -/obj/item/clothing/suit/storage/toggle/labcoat/plaguedoctor - name = "golden plague doctor suit" - desc = "If it worked then, it works now. This classic design comes in gold." - icon_state = "plaguedoctor2" +/obj/item/clothing/suit/storage/toggle/labcoat + name = "labcoat" + desc = "A suit that protects against minor chemical spills." + icon_state = "labcoat" + item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + flags_inv = HIDEHOLSTER + allowed = list(/obj/item/device/analyzer,/obj/item/stack/medical,/obj/item/weapon/dnainjector,/obj/item/weapon/reagent_containers/dropper,/obj/item/weapon/reagent_containers/syringe,/obj/item/weapon/reagent_containers/hypospray,/obj/item/device/healthanalyzer,/obj/item/device/flashlight/pen,/obj/item/weapon/reagent_containers/glass/bottle,/obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/storage/pill_bottle,/obj/item/weapon/paper, /obj/item/clothing/mask/gas) + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 50, rad = 0) + +/obj/item/clothing/suit/storage/toggle/labcoat/red + name = "red labcoat" + desc = "A suit that protects against minor chemical spills. This one is red." + icon_state = "red_labcoat" + item_state_slots = list(slot_r_hand_str = "red_labcoat", slot_l_hand_str = "red_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/blue + name = "blue labcoat" + desc = "A suit that protects against minor chemical spills. This one is blue." + icon_state = "blue_labcoat" + item_state_slots = list(slot_r_hand_str = "blue_labcoat", slot_l_hand_str = "blue_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/purple + name = "purple labcoat" + desc = "A suit that protects against minor chemical spills. This one is purple." + icon_state = "purple_labcoat" + item_state_slots = list(slot_r_hand_str = "purple_labcoat", slot_l_hand_str = "purple_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/orange + name = "orange labcoat" + desc = "A suit that protects against minor chemical spills. This one is orange." + icon_state = "orange_labcoat" + item_state_slots = list(slot_r_hand_str = "orange_labcoat", slot_l_hand_str = "orange_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/yellow + name = "yellow labcoat" + desc = "A suit that protects against minor chemical spills. This one is yellow." + icon_state = "yellow_labcoat" + item_state_slots = list(slot_r_hand_str = "yellow_labcoat", slot_l_hand_str = "yellow_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/pink + name = "pink labcoat" + desc = "A suit that protects against minor chemical spills. This one is pink." + icon_state = "pink_labcoat" + item_state_slots = list(slot_r_hand_str = "pink_labcoat", slot_l_hand_str = "pink_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/green + name = "green labcoat" + desc = "A suit that protects against minor chemical spills. This one is green." + icon_state = "green_labcoat" + item_state_slots = list(slot_r_hand_str = "green_labcoat", slot_l_hand_str = "green_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/mad + name = "The Mad's labcoat" + desc = "It makes you look capable of konking someone on the noggin and shooting them into space." + icon_state = "green_labcoat" + item_state_slots = list(slot_r_hand_str = "green_labcoat", slot_l_hand_str = "green_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/cmo + name = "chief medical officer's labcoat" + desc = "Bluer than the standard model." + icon_state = "labcoat_cmo" + item_state_slots = list(slot_r_hand_str = "cmo_labcoat", slot_l_hand_str = "cmo_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/cmoalt + name = "chief medical officer's labcoat" + desc = "A labcoat with command blue highlights." + icon_state = "labcoat_cmoalt" + item_state_slots = list(slot_r_hand_str = "cmo_labcoat", slot_l_hand_str = "cmo_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/genetics + name = "Geneticist labcoat" + desc = "A suit that protects against minor chemical spills. Has a blue stripe on the shoulder." + icon_state = "labcoat_gen" + item_state_slots = list(slot_r_hand_str = "genetics_labcoat", slot_l_hand_str = "genetics_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/chemist + name = "Chemist labcoat" + desc = "A suit that protects against minor chemical spills. Has an orange stripe on the shoulder." + icon_state = "labcoat_chem" + item_state_slots = list(slot_r_hand_str = "chemist_labcoat", slot_l_hand_str = "chemist_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/virologist + name = "Virologist labcoat" + desc = "A suit that protects against minor chemical spills. Offers slightly more protection against biohazards than the standard model. Has a green stripe on the shoulder." + icon_state = "labcoat_vir" + item_state_slots = list(slot_r_hand_str = "virologist_labcoat", slot_l_hand_str = "virologist_labcoat") + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 0) + +/obj/item/clothing/suit/storage/toggle/labcoat/roboticist + name = "Roboticist labcoat" + desc = "More like an eccentric coat than a labcoat. Helps pass off bloodstains as part of the aesthetic. Comes with red shoulder pads." + icon_state = "labcoat_robo" + item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/science + name = "Scientist labcoat" + desc = "A suit that protects against minor chemical spills. Has a purple stripe on the shoulder." + icon_state = "labcoat_tox" + item_state_slots = list(slot_r_hand_str = "science_labcoat", slot_l_hand_str = "science_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/rd + name = "research director's labcoat" + desc = "A flashy labcoat with purple markings. It belongs to the Research Director." + icon_state = "labcoat_rd" + item_state_slots = list(slot_r_hand_str = "science_labcoat", slot_l_hand_str = "science_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/emt + name = "EMT's labcoat" + desc = "A dark blue labcoat with reflective strips for emergency medical technicians." + icon_state = "labcoat_emt" + item_state_slots = list(slot_r_hand_str = "emt_labcoat", slot_l_hand_str = "emt_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/blue_edge + name = "blue-edged labcoat" + desc = "A suit that protects against minor chemical spills. This one has blue trim." + icon_state = "blue_edge_labcoat" + +/obj/item/clothing/suit/storage/toggle/labcoat/plaguedoctor + name = "golden plague doctor suit" + desc = "If it worked then, it works now. This classic design comes in gold." + icon_state = "plaguedoctor2" diff --git a/code/modules/clothing/suits/utility.dm b/code/modules/clothing/suits/utility.dm index 31f550da77d..f465941ed7e 100644 --- a/code/modules/clothing/suits/utility.dm +++ b/code/modules/clothing/suits/utility.dm @@ -1,118 +1,118 @@ -/* - * Contains: - * Fire protection - * Bomb protection - * Radiation protection - */ - -/* - * Fire protection - */ - -/obj/item/clothing/suit/fire - name = "emergency firesuit" - desc = "A suit that protects against fire and heat." - icon_state = "firesuit" - item_state_slots = list(slot_r_hand_str = "black_suit", slot_l_hand_str = "black_suit") - w_class = ITEMSIZE_LARGE//bulky item - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.50 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen,/obj/item/weapon/extinguisher) - slowdown = 1.0 - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - item_flags = 0 - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_pressure_protection = 0.2 * ONE_ATMOSPHERE - max_pressure_protection = 20 * ONE_ATMOSPHERE - -/obj/item/clothing/suit/fire/firefighter - name = "firesuit" - icon_state = "firesuit2" - max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE+5000 - -/obj/item/clothing/suit/fire/heavy - name = "atmospheric firesuit" - desc = "A suit that protects against extreme fire and heat." - icon_state = "atmos_firesuit" - max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE+10000 - slowdown = 1.5 - -/* - * Bomb protection - */ -/obj/item/clothing/head/bomb_hood - name = "bomb hood" - desc = "Use in case of bomb." - icon_state = "bombsuit" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 100, bio = 0, rad = 0) - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - siemens_coefficient = 0 - -/obj/item/clothing/suit/bomb_suit - name = "bomb suit" - desc = "A suit designed for safety when handling explosives." - icon_state = "bombsuit" - w_class = ITEMSIZE_LARGE//bulky item - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - slowdown = 2 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 100, bio = 0, rad = 0) - flags_inv = HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - heat_protection = UPPER_TORSO|LOWER_TORSO - max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE - siemens_coefficient = 0 - -/obj/item/clothing/head/bomb_hood/security - icon_state = "bombsuitsec" - body_parts_covered = HEAD - -/obj/item/clothing/suit/bomb_suit/security - icon_state = "bombsuitsec" - allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - -/* - * Radiation protection - */ -/obj/item/clothing/head/radiation - name = "Radiation hood" - icon_state = "rad" - desc = "A hood with radiation protective properties. Label: Made with lead, do not eat insulation" - flags_inv = BLOCKHAIR - item_flags = THICKMATERIAL - body_parts_covered = HEAD|FACE|EYES - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 100) - -/obj/item/clothing/suit/radiation - name = "Radiation suit" - desc = "A suit that protects against radiation. Label: Made with lead, do not eat insulation." - icon_state = "rad" - w_class = ITEMSIZE_LARGE//bulky item - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.50 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS|FEET - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen,/obj/item/clothing/head/radiation,/obj/item/clothing/mask/gas) - slowdown = 1.5 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 100) - flags_inv = HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - item_flags = THICKMATERIAL - -/obj/item/clothing/suit/radiation/teshari - name = "Small radiation suit" - desc = "A specialist suit that protects against radiation, designed specifically for use by Teshari. Made to order by Aether." - icon = 'icons/inventory/suit/item_teshari.dmi' - icon_override = 'icons/inventory/suit/mob_teshari.dmi' - icon_state = "rad_fitted" - species_restricted = list(SPECIES_TESHARI) - -/obj/item/clothing/head/radiation/teshari - name = "Small radiation hood" - desc = "A specialist hood with radiation protective properties, designed specifically for use by Teshari. Made to order by Aether." - icon = 'icons/inventory/suit/item_teshari.dmi' - icon_override = 'icons/inventory/head/mob_teshari.dmi' - icon_state = "rad_fitted" +/* + * Contains: + * Fire protection + * Bomb protection + * Radiation protection + */ + +/* + * Fire protection + */ + +/obj/item/clothing/suit/fire + name = "emergency firesuit" + desc = "A suit that protects against fire and heat." + icon_state = "firesuit" + item_state_slots = list(slot_r_hand_str = "black_suit", slot_l_hand_str = "black_suit") + w_class = ITEMSIZE_LARGE//bulky item + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.50 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen,/obj/item/weapon/extinguisher) + slowdown = 1.0 + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + item_flags = 0 + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_pressure_protection = 0.2 * ONE_ATMOSPHERE + max_pressure_protection = 20 * ONE_ATMOSPHERE + +/obj/item/clothing/suit/fire/firefighter + name = "firesuit" + icon_state = "firesuit2" + max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE+5000 + +/obj/item/clothing/suit/fire/heavy + name = "atmospheric firesuit" + desc = "A suit that protects against extreme fire and heat." + icon_state = "atmos_firesuit" + max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE+10000 + slowdown = 1.5 + +/* + * Bomb protection + */ +/obj/item/clothing/head/bomb_hood + name = "bomb hood" + desc = "Use in case of bomb." + icon_state = "bombsuit" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 100, bio = 0, rad = 0) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + siemens_coefficient = 0 + +/obj/item/clothing/suit/bomb_suit + name = "bomb suit" + desc = "A suit designed for safety when handling explosives." + icon_state = "bombsuit" + w_class = ITEMSIZE_LARGE//bulky item + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + slowdown = 2 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 100, bio = 0, rad = 0) + flags_inv = HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + heat_protection = UPPER_TORSO|LOWER_TORSO + max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE + siemens_coefficient = 0 + +/obj/item/clothing/head/bomb_hood/security + icon_state = "bombsuitsec" + body_parts_covered = HEAD + +/obj/item/clothing/suit/bomb_suit/security + icon_state = "bombsuitsec" + allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + +/* + * Radiation protection + */ +/obj/item/clothing/head/radiation + name = "Radiation hood" + icon_state = "rad" + desc = "A hood with radiation protective properties. Label: Made with lead, do not eat insulation" + flags_inv = BLOCKHAIR + item_flags = THICKMATERIAL + body_parts_covered = HEAD|FACE|EYES + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 100) + +/obj/item/clothing/suit/radiation + name = "Radiation suit" + desc = "A suit that protects against radiation. Label: Made with lead, do not eat insulation." + icon_state = "rad" + w_class = ITEMSIZE_LARGE//bulky item + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.50 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS|FEET + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen,/obj/item/clothing/head/radiation,/obj/item/clothing/mask/gas) + slowdown = 1.5 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 100) + flags_inv = HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + item_flags = THICKMATERIAL + +/obj/item/clothing/suit/radiation/teshari + name = "Small radiation suit" + desc = "A specialist suit that protects against radiation, designed specifically for use by Teshari. Made to order by Aether." + icon = 'icons/inventory/suit/item_teshari.dmi' + icon_override = 'icons/inventory/suit/mob_teshari.dmi' + icon_state = "rad_fitted" + species_restricted = list(SPECIES_TESHARI) + +/obj/item/clothing/head/radiation/teshari + name = "Small radiation hood" + desc = "A specialist hood with radiation protective properties, designed specifically for use by Teshari. Made to order by Aether." + icon = 'icons/inventory/suit/item_teshari.dmi' + icon_override = 'icons/inventory/head/mob_teshari.dmi' + icon_state = "rad_fitted" species_restricted = list(SPECIES_TESHARI) \ No newline at end of file diff --git a/code/modules/clothing/suits/utility_vr.dm b/code/modules/clothing/suits/utility_vr.dm index 9302fcf11b8..17f796ca85c 100644 --- a/code/modules/clothing/suits/utility_vr.dm +++ b/code/modules/clothing/suits/utility_vr.dm @@ -1,33 +1,33 @@ -/obj/item/clothing/head/bomb_hood/security - icon_state = "bombsuitsec" - body_parts_covered = HEAD - -/obj/item/clothing/suit/storage/toggle/paramedic - name = "paramedic vest" - desc = "A vest that protects against minor chemical spills." - icon = 'icons/inventory/suit/item_vr.dmi' - icon_override = 'icons/inventory/suit/mob_vr.dmi' - icon_state = "paramedic-vest" - item_state = "paramedic-vest" - item_state_slots = list(slot_r_hand_str = "blue_labcoat", slot_l_hand_str = "blue_labcoat") - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO - flags_inv = HIDEHOLSTER - allowed = list(/obj/item/device/analyzer,/obj/item/stack/medical,/obj/item/weapon/dnainjector,/obj/item/weapon/reagent_containers/dropper,/obj/item/weapon/reagent_containers/syringe,/obj/item/weapon/reagent_containers/hypospray,/obj/item/device/healthanalyzer,/obj/item/device/flashlight/pen,/obj/item/weapon/reagent_containers/glass/bottle,/obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/storage/pill_bottle,/obj/item/weapon/paper) - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 50, rad = 0) - -/obj/item/clothing/head/radiation - sprite_sheets = list( - SPECIES_TESHARI = 'icons/inventory/head/mob_vr_teshari.dmi', - SPECIES_VOX = 'icons/inventory/head/mob_vox.dmi', - SPECIES_WEREBEAST = 'icons/inventory/head/mob_vr_werebeast.dmi' - ) - -/obj/item/clothing/suit/radiation - sprite_sheets = list( - SPECIES_TESHARI = 'icons/inventory/suit/mob_vr_teshari.dmi', - SPECIES_VOX = 'icons/inventory/suit/mob_vox.dmi', - SPECIES_WEREBEAST = 'icons/inventory/suit/mob_vr_werebeast.dmi' - ) - - +/obj/item/clothing/head/bomb_hood/security + icon_state = "bombsuitsec" + body_parts_covered = HEAD + +/obj/item/clothing/suit/storage/toggle/paramedic + name = "paramedic vest" + desc = "A vest that protects against minor chemical spills." + icon = 'icons/inventory/suit/item_vr.dmi' + icon_override = 'icons/inventory/suit/mob_vr.dmi' + icon_state = "paramedic-vest" + item_state = "paramedic-vest" + item_state_slots = list(slot_r_hand_str = "blue_labcoat", slot_l_hand_str = "blue_labcoat") + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO + flags_inv = HIDEHOLSTER + allowed = list(/obj/item/device/analyzer,/obj/item/stack/medical,/obj/item/weapon/dnainjector,/obj/item/weapon/reagent_containers/dropper,/obj/item/weapon/reagent_containers/syringe,/obj/item/weapon/reagent_containers/hypospray,/obj/item/device/healthanalyzer,/obj/item/device/flashlight/pen,/obj/item/weapon/reagent_containers/glass/bottle,/obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/storage/pill_bottle,/obj/item/weapon/paper) + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 50, rad = 0) + +/obj/item/clothing/head/radiation + sprite_sheets = list( + SPECIES_TESHARI = 'icons/inventory/head/mob_vr_teshari.dmi', + SPECIES_VOX = 'icons/inventory/head/mob_vox.dmi', + SPECIES_WEREBEAST = 'icons/inventory/head/mob_vr_werebeast.dmi' + ) + +/obj/item/clothing/suit/radiation + sprite_sheets = list( + SPECIES_TESHARI = 'icons/inventory/suit/mob_vr_teshari.dmi', + SPECIES_VOX = 'icons/inventory/suit/mob_vox.dmi', + SPECIES_WEREBEAST = 'icons/inventory/suit/mob_vr_werebeast.dmi' + ) + + diff --git a/code/modules/clothing/suits/wiz_robe.dm b/code/modules/clothing/suits/wiz_robe.dm index 24e0cdddbfa..903fca84103 100644 --- a/code/modules/clothing/suits/wiz_robe.dm +++ b/code/modules/clothing/suits/wiz_robe.dm @@ -1,129 +1,129 @@ -/obj/item/clothing/head/wizard - name = "wizard hat" - desc = "Strange-looking hat-wear that most certainly belongs to a real magic user." - icon_state = "wizard" - //Not given any special protective value since the magic robes are full-body protection --NEO - siemens_coefficient = 0.8 - body_parts_covered = 0 - wizard_garb = 1 - -/obj/item/clothing/head/wizard/red - name = "red wizard hat" - desc = "Strange-looking, red, hat-wear that most certainly belongs to a real magic user." - icon_state = "redwizard" - siemens_coefficient = 0.8 - -/obj/item/clothing/head/wizard/fake - name = "wizard hat" - desc = "It has WIZZARD written across it in sequins. Comes with a cool beard." - icon_state = "wizard-fake" - body_parts_covered = HEAD|FACE - siemens_coefficient = 1 - -/obj/item/clothing/head/wizard/fake/realistic - desc = "A cool-looking 'magic' hat." - icon_state = "wizard" - body_parts_covered = HEAD - -/obj/item/clothing/head/wizard/fake/realistic/colorable - desc = "A cool-looking 'magic' hat." - icon_state = "wizard-white" - sprite_sheets = list( - SPECIES_TESHARI = 'icons/inventory/head/mob_vr_teshari.dmi' - ) - -/obj/item/clothing/head/wizard/marisa - name = "Witch Hat" - desc = "Strange-looking hat-wear, makes you want to cast fireballs." - icon_state = "marisa" - siemens_coefficient = 0.8 - -/obj/item/clothing/head/wizard/magus - name = "Magus Helm" - desc = "A mysterious helmet that hums with an unearthly power" - icon_state = "magus" - siemens_coefficient = 0.8 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/wizard/amp - name = "psychic amplifier" - desc = "A crown-of-thorns psychic amplifier. Kind of looks like a tiara having sex with an industrial robot." - icon_state = "amp" - siemens_coefficient = 0.8 - -/obj/item/clothing/head/wizard/cap - name = "Gentlemans Cap" - desc = "A checkered gray flat cap woven together with the rarest of threads." - icon_state = "gentcap" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - siemens_coefficient = 0.8 - -/obj/item/clothing/suit/wizrobe - name = "wizard robe" - desc = "A magnificant, gem-lined robe that seems to radiate power." - icon_state = "wizard" - gas_transfer_coefficient = 0.01 // IT'S MAGICAL OKAY JEEZ +1 TO NOT DIE - permeability_coefficient = 0.01 - armor = list(melee = 30, bullet = 20, laser = 20,energy = 20, bomb = 20, bio = 20, rad = 20) - allowed = list(/obj/item/weapon/teleportation_scroll) - flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - siemens_coefficient = 0.8 - wizard_garb = 1 - -/obj/item/clothing/suit/wizrobe/red - name = "red wizard robe" - desc = "A magnificant, red, gem-lined robe that seems to radiate power." - icon_state = "redwizard" - -/obj/item/clothing/suit/wizrobe/marisa - name = "Witch Robe" - desc = "Magic is all about the spell power, ZE!" - icon_state = "marisa" - -/obj/item/clothing/suit/wizrobe/magusblue - name = "Magus Robe" - desc = "A set of armoured robes that seem to radiate a dark power" - icon_state = "magusblue" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|HANDS|LEGS|FEET - -/obj/item/clothing/suit/wizrobe/magusred - name = "Magus Robe" - desc = "A set of armoured robes that seem to radiate a dark power" - icon_state = "magusred" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|HANDS|LEGS|FEET - -/obj/item/clothing/suit/wizrobe/psypurple - name = "purple robes" - desc = "Heavy, royal purple robes threaded with psychic amplifiers and weird, bulbous lenses. Do not machine wash." - icon_state = "psyamp" - -/obj/item/clothing/suit/wizrobe/gentlecoat - name = "Gentlemans Coat" - desc = "A heavy threaded twead gray jacket. For a different sort of Gentleman." - icon_state = "gentlecoat" - item_state_slots = list(slot_r_hand_str = "greatcoat", slot_l_hand_str = "greatcoat") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_inv = HIDETIE|HIDEHOLSTER - -/obj/item/clothing/suit/wizrobe/fake - name = "wizard robe" - desc = "A rather dull, blue robe meant to mimick real wizard robes." - icon_state = "wizard-fake" - item_state_slots = list(slot_r_hand_str = "wizard", slot_l_hand_str = "wizard") - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 1.0 - -/obj/item/clothing/head/wizard/marisa/fake - name = "Witch Hat" - desc = "Strange-looking hat-wear, makes you want to cast fireballs." - icon_state = "marisa" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 1.0 - -/obj/item/clothing/suit/wizrobe/marisa/fake - name = "Witch Robe" - desc = "Magic is all about the spell power, ZE!" - icon_state = "marisa" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) +/obj/item/clothing/head/wizard + name = "wizard hat" + desc = "Strange-looking hat-wear that most certainly belongs to a real magic user." + icon_state = "wizard" + //Not given any special protective value since the magic robes are full-body protection --NEO + siemens_coefficient = 0.8 + body_parts_covered = 0 + wizard_garb = 1 + +/obj/item/clothing/head/wizard/red + name = "red wizard hat" + desc = "Strange-looking, red, hat-wear that most certainly belongs to a real magic user." + icon_state = "redwizard" + siemens_coefficient = 0.8 + +/obj/item/clothing/head/wizard/fake + name = "wizard hat" + desc = "It has WIZZARD written across it in sequins. Comes with a cool beard." + icon_state = "wizard-fake" + body_parts_covered = HEAD|FACE + siemens_coefficient = 1 + +/obj/item/clothing/head/wizard/fake/realistic + desc = "A cool-looking 'magic' hat." + icon_state = "wizard" + body_parts_covered = HEAD + +/obj/item/clothing/head/wizard/fake/realistic/colorable + desc = "A cool-looking 'magic' hat." + icon_state = "wizard-white" + sprite_sheets = list( + SPECIES_TESHARI = 'icons/inventory/head/mob_vr_teshari.dmi' + ) + +/obj/item/clothing/head/wizard/marisa + name = "Witch Hat" + desc = "Strange-looking hat-wear, makes you want to cast fireballs." + icon_state = "marisa" + siemens_coefficient = 0.8 + +/obj/item/clothing/head/wizard/magus + name = "Magus Helm" + desc = "A mysterious helmet that hums with an unearthly power" + icon_state = "magus" + siemens_coefficient = 0.8 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/wizard/amp + name = "psychic amplifier" + desc = "A crown-of-thorns psychic amplifier. Kind of looks like a tiara having sex with an industrial robot." + icon_state = "amp" + siemens_coefficient = 0.8 + +/obj/item/clothing/head/wizard/cap + name = "Gentlemans Cap" + desc = "A checkered gray flat cap woven together with the rarest of threads." + icon_state = "gentcap" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + siemens_coefficient = 0.8 + +/obj/item/clothing/suit/wizrobe + name = "wizard robe" + desc = "A magnificant, gem-lined robe that seems to radiate power." + icon_state = "wizard" + gas_transfer_coefficient = 0.01 // IT'S MAGICAL OKAY JEEZ +1 TO NOT DIE + permeability_coefficient = 0.01 + armor = list(melee = 30, bullet = 20, laser = 20,energy = 20, bomb = 20, bio = 20, rad = 20) + allowed = list(/obj/item/weapon/teleportation_scroll) + flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + siemens_coefficient = 0.8 + wizard_garb = 1 + +/obj/item/clothing/suit/wizrobe/red + name = "red wizard robe" + desc = "A magnificant, red, gem-lined robe that seems to radiate power." + icon_state = "redwizard" + +/obj/item/clothing/suit/wizrobe/marisa + name = "Witch Robe" + desc = "Magic is all about the spell power, ZE!" + icon_state = "marisa" + +/obj/item/clothing/suit/wizrobe/magusblue + name = "Magus Robe" + desc = "A set of armoured robes that seem to radiate a dark power" + icon_state = "magusblue" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|HANDS|LEGS|FEET + +/obj/item/clothing/suit/wizrobe/magusred + name = "Magus Robe" + desc = "A set of armoured robes that seem to radiate a dark power" + icon_state = "magusred" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|HANDS|LEGS|FEET + +/obj/item/clothing/suit/wizrobe/psypurple + name = "purple robes" + desc = "Heavy, royal purple robes threaded with psychic amplifiers and weird, bulbous lenses. Do not machine wash." + icon_state = "psyamp" + +/obj/item/clothing/suit/wizrobe/gentlecoat + name = "Gentlemans Coat" + desc = "A heavy threaded twead gray jacket. For a different sort of Gentleman." + icon_state = "gentlecoat" + item_state_slots = list(slot_r_hand_str = "greatcoat", slot_l_hand_str = "greatcoat") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDETIE|HIDEHOLSTER + +/obj/item/clothing/suit/wizrobe/fake + name = "wizard robe" + desc = "A rather dull, blue robe meant to mimick real wizard robes." + icon_state = "wizard-fake" + item_state_slots = list(slot_r_hand_str = "wizard", slot_l_hand_str = "wizard") + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 1.0 + +/obj/item/clothing/head/wizard/marisa/fake + name = "Witch Hat" + desc = "Strange-looking hat-wear, makes you want to cast fireballs." + icon_state = "marisa" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 1.0 + +/obj/item/clothing/suit/wizrobe/marisa/fake + name = "Witch Robe" + desc = "Magic is all about the spell power, ZE!" + icon_state = "marisa" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) siemens_coefficient = 1.0 \ No newline at end of file diff --git a/code/modules/clothing/under/accessories/badges.dm b/code/modules/clothing/under/accessories/badges.dm index 42ee5a7d6ac..6e2b392331e 100644 --- a/code/modules/clothing/under/accessories/badges.dm +++ b/code/modules/clothing/under/accessories/badges.dm @@ -1,262 +1,262 @@ -/* - Badges are worn on the belt or neck, and can be used to show that the holder is an authorized - Security agent - the user details can be imprinted on holobadges with a Security-access ID card, - or they can be emagged to accept any ID for use in disguises. -*/ - -/obj/item/clothing/accessory/badge - name = "detective's badge" - desc = "A corporate security badge, made from gold and set on false leather." - icon_state = "marshalbadge" - slot_flags = SLOT_BELT | SLOT_TIE - slot = ACCESSORY_SLOT_MEDAL - - var/stored_name - var/badge_string = "Corporate Security" - -/obj/item/clothing/accessory/badge/proc/set_name(var/new_name) - stored_name = new_name - name = "[initial(name)] ([stored_name])" - -/obj/item/clothing/accessory/badge/proc/set_desc(var/mob/living/carbon/human/H) - -/obj/item/clothing/accessory/badge/attack_self(mob/user as mob) - if(!stored_name) - to_chat(user, "You polish your old badge fondly, shining up the surface.") - set_name(user.real_name) - return - - if(isliving(user)) - if(stored_name) - user.visible_message("[user] displays their [src.name].\nIt reads: [stored_name], [badge_string].","You display your [src.name].\nIt reads: [stored_name], [badge_string].") - else - user.visible_message("[user] displays their [src.name].\nIt reads: [badge_string].","You display your [src.name]. It reads: [badge_string].") - -/obj/item/clothing/accessory/badge/attack(mob/living/carbon/human/M, mob/living/user) - if(isliving(user)) - user.visible_message("[user] invades [M]'s personal space, thrusting [src] into their face insistently.","You invade [M]'s personal space, thrusting [src] into their face insistently.") - user.do_attack_animation(M) - user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //NO SPAM - -// General Badges -/obj/item/clothing/accessory/badge/old - name = "faded badge" - desc = "A faded badge, backed with leather." - icon_state = "badge_round" - -/obj/item/clothing/accessory/badge/idbadge/nt - name = "\improper NT ID badge" - desc = "A descriptive identification badge with the holder's credentials. This one has red marks with the NanoTrasen logo on it." - icon_state = "ntbadge" - badge_string = null - -/obj/item/clothing/accessory/badge/press - name = "corporate press pass" - desc = "A corporate reporter's pass, emblazoned with the NanoTrasen logo." - icon_state = "pressbadge" - item_state = "pbadge" - badge_string = "Corporate Reporter" - w_class = ITEMSIZE_TINY - - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' - -/obj/item/clothing/accessory/badge/press/independent - name = "press pass" - desc = "A freelance journalist's pass." - icon_state = "pressbadge-i" - badge_string = "Freelance Journalist" - -/obj/item/clothing/accessory/badge/press/plastic - name = "plastic press pass" - desc = "A journalist's 'pass' shaped, for whatever reason, like a security badge. It is made of plastic." - icon_state = "pbadge" - badge_string = "Sicurity Journelist" - w_class = ITEMSIZE_SMALL - -// Holobadges -/obj/item/clothing/accessory/badge/holo - name = "holobadge" - desc = "This glowing blue badge marks the holder as THE LAW." - icon_state = "holobadge" - var/emagged //Emagging removes Sec check. - var/valid_access = list(access_security) //Default access is security, to be overriden or expanded as desired - -/obj/item/clothing/accessory/badge/holo/cord - icon_state = "holobadge-cord" - slot_flags = SLOT_MASK | SLOT_TIE | SLOT_BELT - -/obj/item/clothing/accessory/badge/holo/attack_self(mob/user as mob) - if(!stored_name) - to_chat(user, "Waving around a holobadge before swiping an ID would be pretty pointless.") - return - return ..() - -/obj/item/clothing/accessory/badge/holo/emag_act(var/remaining_charges, var/mob/user) - if (emagged) - to_chat(user, "\The [src] is already cracked.") - return - else - emagged = 1 - to_chat(user, "You crack the holobadge security checks.") - return 1 - -/obj/item/clothing/accessory/badge/holo/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(istype(O, /obj/item/weapon/card/id) || istype(O, /obj/item/device/pda)) - - var/obj/item/weapon/card/id/id_card = null - - if(istype(O, /obj/item/weapon/card/id)) - id_card = O - else - var/obj/item/device/pda/pda = O - id_card = pda.id - - var/found = FALSE - for(var/access in valid_access) - if(access in id_card.access || emagged) - to_chat(user, "You imprint your ID details onto the badge.") - set_name(user.real_name) - found = TRUE - break - if(!found) - to_chat(user, "[src] rejects your insufficient access rights.") - return - ..() - -/obj/item/weapon/storage/box/holobadge - name = "holobadge box" - desc = "A box claiming to contain holobadges." - starts_with = list( - /obj/item/clothing/accessory/badge/holo/officer = 2, - /obj/item/clothing/accessory/badge/holo = 2, - /obj/item/clothing/accessory/badge/holo/cord = 2 - ) - -/obj/item/clothing/accessory/badge/holo/officer - name = "officer's badge" - desc = "A bronze corporate security badge. Stamped with the words 'Security Officer.'" - icon_state = "bronzebadge" - slot_flags = SLOT_TIE | SLOT_BELT - -/obj/item/clothing/accessory/badge/holo/warden - name = "warden's holobadge" - desc = "A silver corporate security badge. Stamped with the words 'Warden.'" - icon_state = "silverbadge" - slot_flags = SLOT_TIE | SLOT_BELT - -/obj/item/clothing/accessory/badge/holo/hos - name = "head of security's holobadge" - desc = "An immaculately polished gold security badge. Stamped with the words 'Head of Security.'" - icon_state = "goldbadge" - slot_flags = SLOT_TIE | SLOT_BELT - -/obj/item/clothing/accessory/badge/holo/detective - name = "detective's holobadge" - desc = "An immaculately polished gold security badge on leather. Labeled 'Detective.'" - icon_state = "marshalbadge" - slot_flags = SLOT_TIE | SLOT_BELT - -/obj/item/clothing/accessory/badge/holo/investigator - name = "\improper investigator holobadge" - desc = "This badge marks the holder as an investigative agent." - icon_state = "invbadge" - badge_string = "Corporate Investigator" - valid_access = list(access_security, access_lawyer) //Permitting both sec and IAA! - slot_flags = SLOT_TIE | SLOT_BELT - -/obj/item/clothing/accessory/badge/holo/sheriff - name = "sheriff badge" - desc = "A star-shaped brass badge denoting who the law is around these parts." - icon_state = "sheriff" - slot_flags = SLOT_TIE | SLOT_BELT - -/obj/item/weapon/storage/box/holobadge/hos - name = "holobadge box" - desc = "A box claiming to contain holobadges." - starts_with = list( - /obj/item/clothing/accessory/badge/holo/officer = 2, - /obj/item/clothing/accessory/badge/holo/warden = 1, - /obj/item/clothing/accessory/badge/holo/detective = 2, - /obj/item/clothing/accessory/badge/holo/hos = 1, - /obj/item/clothing/accessory/badge/holo/cord = 1 - ) - -// Sheriff Badge (toy) -/obj/item/clothing/accessory/badge/sheriff - name = "sheriff badge" - desc = "This town ain't big enough for the two of us, pardner." - icon_state = "sheriff_toy" - item_state = "sheriff_toy" - -/obj/item/clothing/accessory/badge/sheriff/attack_self(mob/user as mob) - user.visible_message("[user] shows their sheriff badge. There's a new sheriff in town!",\ - "You flash the sheriff badge to everyone around you!") - -/obj/item/clothing/accessory/badge/sheriff/attack(mob/living/carbon/human/M, mob/living/user) - if(isliving(user)) - user.visible_message("[user] invades [M]'s personal space, the sheriff badge into their face!.","You invade [M]'s personal space, thrusting the sheriff badge into their face insistently.") - user.do_attack_animation(M) - user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //NO SPAM - -// Synthmorph bag / Corporation badges. Primarily used on the robobag, but can be worn. Default is NT. -/obj/item/clothing/accessory/badge/corporate_tag - name = "NanoTrasen Badge" - desc = "A plain metallic plate that might denote the wearer as a member of NanoTrasen." - icon_state = "tag_nt" - item_state = "badge" - badge_string = "NanoTrasen" - -/obj/item/clothing/accessory/badge/corporate_tag/morpheus - name = "Morpheus Badge" - desc = "A plain metallic plate that might denote the wearer as a member of Morpheus Cyberkinetics." - icon_state = "tag_blank" - badge_string = "Morpheus" - -/obj/item/clothing/accessory/badge/corporate_tag/wardtaka - name = "Ward-Takahashi Badge" - desc = "A plain metallic plate that might denote the wearer as a member of Ward-Takahashi." - icon_state = "tag_ward" - badge_string = "Ward-Takahashi" - -/obj/item/clothing/accessory/badge/corporate_tag/zenghu - name = "Zeng-Hu Badge" - desc = "A plain metallic plate that might denote the wearer as a member of Zeng-Hu." - icon_state = "tag_zeng" - badge_string = "Zeng-Hu" - -/obj/item/clothing/accessory/badge/corporate_tag/gilthari - name = "Gilthari Badge" - desc = "An opulent metallic plate that might denote the wearer as a member of Gilthari." - icon_state = "tag_gil" - badge_string = "Gilthari" - -/obj/item/clothing/accessory/badge/corporate_tag/veymed - name = "Vey-Medical Badge" - desc = "A plain metallic plate that might denote the wearer as a member of Vey-Medical." - icon_state = "tag_vey" - badge_string = "Vey-Medical" - -/obj/item/clothing/accessory/badge/corporate_tag/hephaestus - name = "Hephaestus Badge" - desc = "A rugged metallic plate that might denote the wearer as a member of Hephaestus." - icon_state = "tag_heph" - badge_string = "Hephaestus" - -/obj/item/clothing/accessory/badge/corporate_tag/grayson - name = "Grayson Badge" - desc = "A rugged metallic plate that might denote the wearer as a member of Grayson." - icon_state = "tag_grayson" - badge_string = "Grayson" - -/obj/item/clothing/accessory/badge/corporate_tag/xion - name = "Xion Badge" - desc = "A rugged metallic plate that might denote the wearer as a member of Xion." - icon_state = "tag_xion" - badge_string = "Xion" - -/obj/item/clothing/accessory/badge/corporate_tag/bishop - name = "Bishop Badge" - desc = "A sleek metallic plate that might denote the wearer as a member of Bishop." - icon_state = "tag_bishop" - badge_string = "Bishop" +/* + Badges are worn on the belt or neck, and can be used to show that the holder is an authorized + Security agent - the user details can be imprinted on holobadges with a Security-access ID card, + or they can be emagged to accept any ID for use in disguises. +*/ + +/obj/item/clothing/accessory/badge + name = "detective's badge" + desc = "A corporate security badge, made from gold and set on false leather." + icon_state = "marshalbadge" + slot_flags = SLOT_BELT | SLOT_TIE + slot = ACCESSORY_SLOT_MEDAL + + var/stored_name + var/badge_string = "Corporate Security" + +/obj/item/clothing/accessory/badge/proc/set_name(var/new_name) + stored_name = new_name + name = "[initial(name)] ([stored_name])" + +/obj/item/clothing/accessory/badge/proc/set_desc(var/mob/living/carbon/human/H) + +/obj/item/clothing/accessory/badge/attack_self(mob/user as mob) + if(!stored_name) + to_chat(user, "You polish your old badge fondly, shining up the surface.") + set_name(user.real_name) + return + + if(isliving(user)) + if(stored_name) + user.visible_message("[user] displays their [src.name].\nIt reads: [stored_name], [badge_string].","You display your [src.name].\nIt reads: [stored_name], [badge_string].") + else + user.visible_message("[user] displays their [src.name].\nIt reads: [badge_string].","You display your [src.name]. It reads: [badge_string].") + +/obj/item/clothing/accessory/badge/attack(mob/living/carbon/human/M, mob/living/user) + if(isliving(user)) + user.visible_message("[user] invades [M]'s personal space, thrusting [src] into their face insistently.","You invade [M]'s personal space, thrusting [src] into their face insistently.") + user.do_attack_animation(M) + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //NO SPAM + +// General Badges +/obj/item/clothing/accessory/badge/old + name = "faded badge" + desc = "A faded badge, backed with leather." + icon_state = "badge_round" + +/obj/item/clothing/accessory/badge/idbadge/nt + name = "\improper NT ID badge" + desc = "A descriptive identification badge with the holder's credentials. This one has red marks with the NanoTrasen logo on it." + icon_state = "ntbadge" + badge_string = null + +/obj/item/clothing/accessory/badge/press + name = "corporate press pass" + desc = "A corporate reporter's pass, emblazoned with the NanoTrasen logo." + icon_state = "pressbadge" + item_state = "pbadge" + badge_string = "Corporate Reporter" + w_class = ITEMSIZE_TINY + + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' + +/obj/item/clothing/accessory/badge/press/independent + name = "press pass" + desc = "A freelance journalist's pass." + icon_state = "pressbadge-i" + badge_string = "Freelance Journalist" + +/obj/item/clothing/accessory/badge/press/plastic + name = "plastic press pass" + desc = "A journalist's 'pass' shaped, for whatever reason, like a security badge. It is made of plastic." + icon_state = "pbadge" + badge_string = "Sicurity Journelist" + w_class = ITEMSIZE_SMALL + +// Holobadges +/obj/item/clothing/accessory/badge/holo + name = "holobadge" + desc = "This glowing blue badge marks the holder as THE LAW." + icon_state = "holobadge" + var/emagged //Emagging removes Sec check. + var/valid_access = list(access_security) //Default access is security, to be overriden or expanded as desired + +/obj/item/clothing/accessory/badge/holo/cord + icon_state = "holobadge-cord" + slot_flags = SLOT_MASK | SLOT_TIE | SLOT_BELT + +/obj/item/clothing/accessory/badge/holo/attack_self(mob/user as mob) + if(!stored_name) + to_chat(user, "Waving around a holobadge before swiping an ID would be pretty pointless.") + return + return ..() + +/obj/item/clothing/accessory/badge/holo/emag_act(var/remaining_charges, var/mob/user) + if (emagged) + to_chat(user, "\The [src] is already cracked.") + return + else + emagged = 1 + to_chat(user, "You crack the holobadge security checks.") + return 1 + +/obj/item/clothing/accessory/badge/holo/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(istype(O, /obj/item/weapon/card/id) || istype(O, /obj/item/device/pda)) + + var/obj/item/weapon/card/id/id_card = null + + if(istype(O, /obj/item/weapon/card/id)) + id_card = O + else + var/obj/item/device/pda/pda = O + id_card = pda.id + + var/found = FALSE + for(var/access in valid_access) + if(access in id_card.access || emagged) + to_chat(user, "You imprint your ID details onto the badge.") + set_name(user.real_name) + found = TRUE + break + if(!found) + to_chat(user, "[src] rejects your insufficient access rights.") + return + ..() + +/obj/item/weapon/storage/box/holobadge + name = "holobadge box" + desc = "A box claiming to contain holobadges." + starts_with = list( + /obj/item/clothing/accessory/badge/holo/officer = 2, + /obj/item/clothing/accessory/badge/holo = 2, + /obj/item/clothing/accessory/badge/holo/cord = 2 + ) + +/obj/item/clothing/accessory/badge/holo/officer + name = "officer's badge" + desc = "A bronze corporate security badge. Stamped with the words 'Security Officer.'" + icon_state = "bronzebadge" + slot_flags = SLOT_TIE | SLOT_BELT + +/obj/item/clothing/accessory/badge/holo/warden + name = "warden's holobadge" + desc = "A silver corporate security badge. Stamped with the words 'Warden.'" + icon_state = "silverbadge" + slot_flags = SLOT_TIE | SLOT_BELT + +/obj/item/clothing/accessory/badge/holo/hos + name = "head of security's holobadge" + desc = "An immaculately polished gold security badge. Stamped with the words 'Head of Security.'" + icon_state = "goldbadge" + slot_flags = SLOT_TIE | SLOT_BELT + +/obj/item/clothing/accessory/badge/holo/detective + name = "detective's holobadge" + desc = "An immaculately polished gold security badge on leather. Labeled 'Detective.'" + icon_state = "marshalbadge" + slot_flags = SLOT_TIE | SLOT_BELT + +/obj/item/clothing/accessory/badge/holo/investigator + name = "\improper investigator holobadge" + desc = "This badge marks the holder as an investigative agent." + icon_state = "invbadge" + badge_string = "Corporate Investigator" + valid_access = list(access_security, access_lawyer) //Permitting both sec and IAA! + slot_flags = SLOT_TIE | SLOT_BELT + +/obj/item/clothing/accessory/badge/holo/sheriff + name = "sheriff badge" + desc = "A star-shaped brass badge denoting who the law is around these parts." + icon_state = "sheriff" + slot_flags = SLOT_TIE | SLOT_BELT + +/obj/item/weapon/storage/box/holobadge/hos + name = "holobadge box" + desc = "A box claiming to contain holobadges." + starts_with = list( + /obj/item/clothing/accessory/badge/holo/officer = 2, + /obj/item/clothing/accessory/badge/holo/warden = 1, + /obj/item/clothing/accessory/badge/holo/detective = 2, + /obj/item/clothing/accessory/badge/holo/hos = 1, + /obj/item/clothing/accessory/badge/holo/cord = 1 + ) + +// Sheriff Badge (toy) +/obj/item/clothing/accessory/badge/sheriff + name = "sheriff badge" + desc = "This town ain't big enough for the two of us, pardner." + icon_state = "sheriff_toy" + item_state = "sheriff_toy" + +/obj/item/clothing/accessory/badge/sheriff/attack_self(mob/user as mob) + user.visible_message("[user] shows their sheriff badge. There's a new sheriff in town!",\ + "You flash the sheriff badge to everyone around you!") + +/obj/item/clothing/accessory/badge/sheriff/attack(mob/living/carbon/human/M, mob/living/user) + if(isliving(user)) + user.visible_message("[user] invades [M]'s personal space, the sheriff badge into their face!.","You invade [M]'s personal space, thrusting the sheriff badge into their face insistently.") + user.do_attack_animation(M) + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //NO SPAM + +// Synthmorph bag / Corporation badges. Primarily used on the robobag, but can be worn. Default is NT. +/obj/item/clothing/accessory/badge/corporate_tag + name = "NanoTrasen Badge" + desc = "A plain metallic plate that might denote the wearer as a member of NanoTrasen." + icon_state = "tag_nt" + item_state = "badge" + badge_string = "NanoTrasen" + +/obj/item/clothing/accessory/badge/corporate_tag/morpheus + name = "Morpheus Badge" + desc = "A plain metallic plate that might denote the wearer as a member of Morpheus Cyberkinetics." + icon_state = "tag_blank" + badge_string = "Morpheus" + +/obj/item/clothing/accessory/badge/corporate_tag/wardtaka + name = "Ward-Takahashi Badge" + desc = "A plain metallic plate that might denote the wearer as a member of Ward-Takahashi." + icon_state = "tag_ward" + badge_string = "Ward-Takahashi" + +/obj/item/clothing/accessory/badge/corporate_tag/zenghu + name = "Zeng-Hu Badge" + desc = "A plain metallic plate that might denote the wearer as a member of Zeng-Hu." + icon_state = "tag_zeng" + badge_string = "Zeng-Hu" + +/obj/item/clothing/accessory/badge/corporate_tag/gilthari + name = "Gilthari Badge" + desc = "An opulent metallic plate that might denote the wearer as a member of Gilthari." + icon_state = "tag_gil" + badge_string = "Gilthari" + +/obj/item/clothing/accessory/badge/corporate_tag/veymed + name = "Vey-Medical Badge" + desc = "A plain metallic plate that might denote the wearer as a member of Vey-Medical." + icon_state = "tag_vey" + badge_string = "Vey-Medical" + +/obj/item/clothing/accessory/badge/corporate_tag/hephaestus + name = "Hephaestus Badge" + desc = "A rugged metallic plate that might denote the wearer as a member of Hephaestus." + icon_state = "tag_heph" + badge_string = "Hephaestus" + +/obj/item/clothing/accessory/badge/corporate_tag/grayson + name = "Grayson Badge" + desc = "A rugged metallic plate that might denote the wearer as a member of Grayson." + icon_state = "tag_grayson" + badge_string = "Grayson" + +/obj/item/clothing/accessory/badge/corporate_tag/xion + name = "Xion Badge" + desc = "A rugged metallic plate that might denote the wearer as a member of Xion." + icon_state = "tag_xion" + badge_string = "Xion" + +/obj/item/clothing/accessory/badge/corporate_tag/bishop + name = "Bishop Badge" + desc = "A sleek metallic plate that might denote the wearer as a member of Bishop." + icon_state = "tag_bishop" + badge_string = "Bishop" diff --git a/code/modules/clothing/under/accessories/lockets.dm b/code/modules/clothing/under/accessories/lockets.dm index dcc775d878b..ef9ea3623ac 100644 --- a/code/modules/clothing/under/accessories/lockets.dm +++ b/code/modules/clothing/under/accessories/lockets.dm @@ -1,47 +1,47 @@ -/obj/item/clothing/accessory/locket - name = "silver locket" - desc = "A small locket of high-quality metal." - icon_state = "locket" - drop_sound = 'sound/items/drop/ring.ogg' - pickup_sound = 'sound/items/pickup/ring.ogg' - w_class = ITEMSIZE_SMALL - slot_flags = SLOT_MASK | SLOT_TIE - slot = ACCESSORY_SLOT_DECOR - var/base_icon - var/open - var/obj/item/held //Item inside locket. - -/obj/item/clothing/accessory/locket/attack_self(mob/user as mob) - if(!base_icon) - base_icon = icon_state - - if(!("[base_icon]_open" in cached_icon_states(icon))) - to_chat(user, "\The [src] doesn't seem to open.") - return - - open = !open - to_chat(user, "You flip \the [src] [open?"open":"closed"].") - if(open) - icon_state = "[base_icon]_open" - if(held) - to_chat(user, "\The [held] falls out!") - held.loc = get_turf(user) - held = null - else - icon_state = "[base_icon]" - -/obj/item/clothing/accessory/locket/attackby(var/obj/item/O as obj, mob/user as mob) - if(!open) - to_chat(user, "You have to open it first.") - return - - if(istype(O,/obj/item/weapon/paper) || istype(O, /obj/item/weapon/photo)) - if(held) - to_chat(usr, "\The [src] already has something inside it.") - else - to_chat(usr, "You slip [O] into [src].") - user.drop_item() - O.loc = src - held = O - return - ..() +/obj/item/clothing/accessory/locket + name = "silver locket" + desc = "A small locket of high-quality metal." + icon_state = "locket" + drop_sound = 'sound/items/drop/ring.ogg' + pickup_sound = 'sound/items/pickup/ring.ogg' + w_class = ITEMSIZE_SMALL + slot_flags = SLOT_MASK | SLOT_TIE + slot = ACCESSORY_SLOT_DECOR + var/base_icon + var/open + var/obj/item/held //Item inside locket. + +/obj/item/clothing/accessory/locket/attack_self(mob/user as mob) + if(!base_icon) + base_icon = icon_state + + if(!("[base_icon]_open" in cached_icon_states(icon))) + to_chat(user, "\The [src] doesn't seem to open.") + return + + open = !open + to_chat(user, "You flip \the [src] [open?"open":"closed"].") + if(open) + icon_state = "[base_icon]_open" + if(held) + to_chat(user, "\The [held] falls out!") + held.loc = get_turf(user) + held = null + else + icon_state = "[base_icon]" + +/obj/item/clothing/accessory/locket/attackby(var/obj/item/O as obj, mob/user as mob) + if(!open) + to_chat(user, "You have to open it first.") + return + + if(istype(O,/obj/item/weapon/paper) || istype(O, /obj/item/weapon/photo)) + if(held) + to_chat(usr, "\The [src] already has something inside it.") + else + to_chat(usr, "You slip [O] into [src].") + user.drop_item() + O.loc = src + held = O + return + ..() diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm index b20524a1362..afdc9917c94 100644 --- a/code/modules/clothing/under/color.dm +++ b/code/modules/clothing/under/color.dm @@ -1,143 +1,143 @@ -/obj/item/clothing/under/color/black - name = "black jumpsuit" - icon_state = "black" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/blackf - name = "feminine black jumpsuit" - desc = "It's very smart and in a ladies size!" - icon_state = "black" - worn_state = "blackf" - -/obj/item/clothing/under/color/blackjumpskirt - name = "black jumpskirt" - desc = "A slimming black jumpskirt." - icon_state = "blackjumpskirt" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - -/obj/item/clothing/under/color/blue - name = "blue jumpsuit" - icon_state = "blue" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/green - name = "green jumpsuit" - icon_state = "green" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/grey - name = "grey jumpsuit" - icon_state = "grey" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/orange - name = "orange jumpsuit" - icon_state = "orange" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/prison - name = "prison jumpsuit" - desc = "It's standardized prisoner-wear. Its suit sensors are permanently set to the \"Tracking\" position." - icon_state = "prison" - has_sensor = 2 - sensor_mode = 3 - -/obj/item/clothing/under/color/pink - name = "pink jumpsuit" - icon_state = "pink" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/red - name = "red jumpsuit" - icon_state = "red" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/white - name = "white jumpsuit" - icon_state = "white" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/yellow - name = "yellow jumpsuit" - icon_state = "yellow" - rolled_sleeves = 0 - -/obj/item/clothing/under/psyche - name = "psychedelic jumpsuit" - desc = "Groovy!" - icon_state = "psyche" - -/obj/item/clothing/under/color/lightblue - name = "lightblue jumpsuit" - desc = "A light blue jumpsuit." - icon_state = "lightblue" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - rolled_sleeves = 0 - -/obj/item/clothing/under/color/aqua - name = "aqua jumpsuit" - desc = "An aqua jumpsuit." - icon_state = "aqua" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - rolled_sleeves = 0 - -/obj/item/clothing/under/color - name = "purple jumpsuit" - desc = "The latest in space fashion." - icon_state = "purple" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/lightpurple - name = "lightpurple jumpsuit" - desc = "A light purple jumpsuit." - icon_state = "lightpurple" - item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") - rolled_sleeves = 0 - -/obj/item/clothing/under/color/lightgreen - name = "lightgreen jumpsuit" - desc = "A light green jumpsuit." - icon_state = "lightgreen" - item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") - rolled_sleeves = 0 - -/obj/item/clothing/under/color/lightbrown - name = "lightbrown jumpsuit" - desc = "A light brown jumpsuit." - icon_state = "lightbrown" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/brown - name = "brown jumpsuit" - desc = "A brown jumpsuit." - icon_state = "brown" - item_state_slots = list(slot_r_hand_str = "lightbrown", slot_l_hand_str = "lightbrown") - rolled_sleeves = 0 - -/obj/item/clothing/under/color/yellowgreen - name = "yellowgreen jumpsuit" - desc = "A... yellow green jumpsuit?" - icon_state = "yellowgreen" - item_state_slots = list(slot_r_hand_str = "yellow", slot_l_hand_str = "yellow") - rolled_sleeves = 0 - -/obj/item/clothing/under/color/darkblue - name = "darkblue jumpsuit" - desc = "A dark blue jumpsuit." - icon_state = "darkblue" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - rolled_sleeves = 0 - -/obj/item/clothing/under/color/lightred - name = "lightred jumpsuit" - desc = "A light red jumpsuit." - icon_state = "lightred" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - rolled_sleeves = 0 - -/obj/item/clothing/under/color/darkred - name = "darkred jumpsuit" - desc = "A dark red jumpsuit." - icon_state = "darkred" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - rolled_sleeves = 0 +/obj/item/clothing/under/color/black + name = "black jumpsuit" + icon_state = "black" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/blackf + name = "feminine black jumpsuit" + desc = "It's very smart and in a ladies size!" + icon_state = "black" + worn_state = "blackf" + +/obj/item/clothing/under/color/blackjumpskirt + name = "black jumpskirt" + desc = "A slimming black jumpskirt." + icon_state = "blackjumpskirt" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + +/obj/item/clothing/under/color/blue + name = "blue jumpsuit" + icon_state = "blue" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/green + name = "green jumpsuit" + icon_state = "green" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/grey + name = "grey jumpsuit" + icon_state = "grey" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/orange + name = "orange jumpsuit" + icon_state = "orange" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/prison + name = "prison jumpsuit" + desc = "It's standardized prisoner-wear. Its suit sensors are permanently set to the \"Tracking\" position." + icon_state = "prison" + has_sensor = 2 + sensor_mode = 3 + +/obj/item/clothing/under/color/pink + name = "pink jumpsuit" + icon_state = "pink" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/red + name = "red jumpsuit" + icon_state = "red" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/white + name = "white jumpsuit" + icon_state = "white" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/yellow + name = "yellow jumpsuit" + icon_state = "yellow" + rolled_sleeves = 0 + +/obj/item/clothing/under/psyche + name = "psychedelic jumpsuit" + desc = "Groovy!" + icon_state = "psyche" + +/obj/item/clothing/under/color/lightblue + name = "lightblue jumpsuit" + desc = "A light blue jumpsuit." + icon_state = "lightblue" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + rolled_sleeves = 0 + +/obj/item/clothing/under/color/aqua + name = "aqua jumpsuit" + desc = "An aqua jumpsuit." + icon_state = "aqua" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + rolled_sleeves = 0 + +/obj/item/clothing/under/color + name = "purple jumpsuit" + desc = "The latest in space fashion." + icon_state = "purple" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/lightpurple + name = "lightpurple jumpsuit" + desc = "A light purple jumpsuit." + icon_state = "lightpurple" + item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") + rolled_sleeves = 0 + +/obj/item/clothing/under/color/lightgreen + name = "lightgreen jumpsuit" + desc = "A light green jumpsuit." + icon_state = "lightgreen" + item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") + rolled_sleeves = 0 + +/obj/item/clothing/under/color/lightbrown + name = "lightbrown jumpsuit" + desc = "A light brown jumpsuit." + icon_state = "lightbrown" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/brown + name = "brown jumpsuit" + desc = "A brown jumpsuit." + icon_state = "brown" + item_state_slots = list(slot_r_hand_str = "lightbrown", slot_l_hand_str = "lightbrown") + rolled_sleeves = 0 + +/obj/item/clothing/under/color/yellowgreen + name = "yellowgreen jumpsuit" + desc = "A... yellow green jumpsuit?" + icon_state = "yellowgreen" + item_state_slots = list(slot_r_hand_str = "yellow", slot_l_hand_str = "yellow") + rolled_sleeves = 0 + +/obj/item/clothing/under/color/darkblue + name = "darkblue jumpsuit" + desc = "A dark blue jumpsuit." + icon_state = "darkblue" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + rolled_sleeves = 0 + +/obj/item/clothing/under/color/lightred + name = "lightred jumpsuit" + desc = "A light red jumpsuit." + icon_state = "lightred" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + rolled_sleeves = 0 + +/obj/item/clothing/under/color/darkred + name = "darkred jumpsuit" + desc = "A dark red jumpsuit." + icon_state = "darkred" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + rolled_sleeves = 0 diff --git a/code/modules/clothing/under/jobs/civilian.dm b/code/modules/clothing/under/jobs/civilian.dm index da5d5627beb..9f714d9a0c9 100644 --- a/code/modules/clothing/under/jobs/civilian.dm +++ b/code/modules/clothing/under/jobs/civilian.dm @@ -1,213 +1,213 @@ -//Alphabetical order of civilian jobs. - -/obj/item/clothing/under/rank/bartender - desc = "It looks like it could use some more flair." - name = "bartender's uniform" - icon_state = "ba_suit" - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/bartender/skirt - desc = "Short and cute." - name = "bartender's skirt" - icon_state = "ba_suit_skirt" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - -/obj/item/clothing/under/rank/captain //Alright, technically not a 'civilian' but its better then giving a .dm file for a single define. - desc = "It's a blue jumpsuit with some gold markings denoting the rank of \"Site Manager\"." - name = "site manager's jumpsuit" - icon_state = "captain" - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/cargo - name = "quartermaster's jumpsuit" - desc = "It's a jumpsuit worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." - icon_state = "qm" - item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/cargo/jeans - name = "quartermaster's jumpjeans" - desc = "Jeeeaaans! They're comfy!" - icon_state = "qmj" - -/obj/item/clothing/under/rank/cargo/jeans/female - name = "quartermaster's jumpjeans" - desc = "Jeeeaaans! They're comfy!" - icon_state = "qmjf" - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/cargotech - name = "cargo technician's jumpsuit" - desc = "Shooooorts! They're comfy and easy to wear!" - icon_state = "cargo" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/cargotech/jeans - name = "cargo technician's jumpjeans" - desc = "Jeeeaaans! They're comfy!" - icon_state = "cargoj" - item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/cargotech/jeans/female - name = "cargo technician's jumpjeans" - desc = "Jeeeaaans! They're comfy!" - icon_state = "cargojf" - -/obj/item/clothing/under/rank/chaplain - desc = "It's a black jumpsuit, often worn by religious folk." - name = "chaplain's jumpsuit" - icon_state = "chaplain" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/chef - desc = "It's an apron which is given only to the most hardcore chefs in space." - name = "chef's uniform" - icon_state = "chef" - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/chef/alt - desc = "It's an apron which is given only to the chefs that swear the most." - name = "souschef's uniform" - icon_state = "souschef" - -/obj/item/clothing/under/rank/clown - name = "clown suit" - desc = "Honk!" - icon_state = "clown" - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/head_of_personnel - desc = "It's a jumpsuit worn by someone who works in the position of \"Head of Personnel\"." - name = "head of personnel's jumpsuit" - icon_state = "hop" - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/head_of_personnel_whimsy - desc = "A blue jacket and red tie, with matching red cuffs! Snazzy. Wearing this makes you feel more important than your job title does." - name = "head of personnel's suit" - icon_state = "hopwhimsy" - item_state_slots = list(slot_r_hand_str = "hop", slot_l_hand_str = "hop") - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/hydroponics - desc = "It's a jumpsuit designed to protect against minor plant-related hazards." - name = "botanist's jumpsuit" - icon_state = "hydroponics" - item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") - permeability_coefficient = 0.50 - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/hydroponics/alt - icon_state = "hydro" - -/obj/item/clothing/under/rank/internalaffairs - desc = "The plain, professional attire of an Internal Affairs Agent. The collar is immaculately starched." - name = "Internal Affairs uniform" - icon_state = "internalaffairs" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - rolled_sleeves = 0 - starting_accessories = list(/obj/item/clothing/accessory/tie/black) - -/obj/item/clothing/under/rank/internalaffairs/skirt - desc = "The plain, professional attire of an Internal Affairs Agent. The top button is sewn shut." - name = "Internal Affairs skirt" - icon_state = "internalaffairs_skirt" - -/obj/item/clothing/under/rank/janitor - desc = "It's the official uniform of the station's janitor. It has minor protection from biohazards." - name = "janitor's jumpsuit" - icon_state = "janitor" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/janitor/alt - name = "janitor's overalls" - icon_state = "janitor_alt" - -/obj/item/clothing/under/lawyer - desc = "Slick threads." - name = "lawyer suit" - -/obj/item/clothing/under/lawyer/black - name = "black lawyer suit" - icon_state = "lawyer_black" - -/obj/item/clothing/under/lawyer/black/skirt - name = "black lawyer skirt" - icon_state = "lawyer_black_skirt" - item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") - -/obj/item/clothing/under/lawyer/female - name = "black lawyer suit" - icon_state = "black_suit_fem" - item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") - -/obj/item/clothing/under/lawyer/red - name = "red lawyer suit" - icon_state = "lawyer_red" - -/obj/item/clothing/under/lawyer/red/skirt - name = "red lawyer skirt" - icon_state = "lawyer_red_skirt" - item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") - -/obj/item/clothing/under/lawyer/blue - name = "blue lawyer suit" - icon_state = "lawyer_blue" - -/obj/item/clothing/under/lawyer/blue/skirt - name = "blue lawyer skirt" - icon_state = "lawyer_blue_skirt" - item_state_slots = list(slot_r_hand_str = "lawyer_blue", slot_l_hand_str = "lawyer_blue") - -/obj/item/clothing/under/lawyer/bluesuit - name = "blue suit" - desc = "A classy suit." - icon_state = "bluesuit" - item_state_slots = list(slot_r_hand_str = "lawyer_blue", slot_l_hand_str = "lawyer_blue") - starting_accessories = list(/obj/item/clothing/accessory/tie/red) - -/obj/item/clothing/under/lawyer/bluesuit/skirt - name = "blue skirt suit" - icon_state = "bluesuit_skirt" - -/obj/item/clothing/under/lawyer/purpsuit - name = "purple suit" - icon_state = "lawyer_purp" - item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") - -/obj/item/clothing/under/lawyer/purpsuit/skirt - name = "purple skirt suit" - icon_state = "lawyer_purp_skirt" - -/obj/item/clothing/under/lawyer/oldman - name = "Old Man's Suit" - desc = "A classic suit for the older gentleman, with built in back support." - icon_state = "oldman" - item_state_slots = list(slot_r_hand_str = "johnny", slot_l_hand_str = "johnny") - -/obj/item/clothing/under/oldwoman - name = "Old Woman's Attire" - desc = "A typical outfit for the older woman, a lovely cardigan and comfortable skirt." - icon_state = "oldwoman" - item_state_slots = list(slot_r_hand_str = "johnny", slot_l_hand_str = "johnny") - -/obj/item/clothing/under/librarian - name = "sensible suit" - desc = "It's very... sensible." - icon_state = "red_suit" - item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") - -/obj/item/clothing/under/mime - name = "mime's outfit" - desc = "It's not very colourful." - icon_state = "mime" - -/obj/item/clothing/under/rank/miner - desc = "It's a snappy jumpsuit with a sturdy set of overalls. It is very dirty." - name = "shaft miner's jumpsuit" - icon_state = "miner" - rolled_sleeves = 0 +//Alphabetical order of civilian jobs. + +/obj/item/clothing/under/rank/bartender + desc = "It looks like it could use some more flair." + name = "bartender's uniform" + icon_state = "ba_suit" + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/bartender/skirt + desc = "Short and cute." + name = "bartender's skirt" + icon_state = "ba_suit_skirt" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + +/obj/item/clothing/under/rank/captain //Alright, technically not a 'civilian' but its better then giving a .dm file for a single define. + desc = "It's a blue jumpsuit with some gold markings denoting the rank of \"Site Manager\"." + name = "site manager's jumpsuit" + icon_state = "captain" + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/cargo + name = "quartermaster's jumpsuit" + desc = "It's a jumpsuit worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." + icon_state = "qm" + item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/cargo/jeans + name = "quartermaster's jumpjeans" + desc = "Jeeeaaans! They're comfy!" + icon_state = "qmj" + +/obj/item/clothing/under/rank/cargo/jeans/female + name = "quartermaster's jumpjeans" + desc = "Jeeeaaans! They're comfy!" + icon_state = "qmjf" + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/cargotech + name = "cargo technician's jumpsuit" + desc = "Shooooorts! They're comfy and easy to wear!" + icon_state = "cargo" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/cargotech/jeans + name = "cargo technician's jumpjeans" + desc = "Jeeeaaans! They're comfy!" + icon_state = "cargoj" + item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/cargotech/jeans/female + name = "cargo technician's jumpjeans" + desc = "Jeeeaaans! They're comfy!" + icon_state = "cargojf" + +/obj/item/clothing/under/rank/chaplain + desc = "It's a black jumpsuit, often worn by religious folk." + name = "chaplain's jumpsuit" + icon_state = "chaplain" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/chef + desc = "It's an apron which is given only to the most hardcore chefs in space." + name = "chef's uniform" + icon_state = "chef" + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/chef/alt + desc = "It's an apron which is given only to the chefs that swear the most." + name = "souschef's uniform" + icon_state = "souschef" + +/obj/item/clothing/under/rank/clown + name = "clown suit" + desc = "Honk!" + icon_state = "clown" + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/head_of_personnel + desc = "It's a jumpsuit worn by someone who works in the position of \"Head of Personnel\"." + name = "head of personnel's jumpsuit" + icon_state = "hop" + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/head_of_personnel_whimsy + desc = "A blue jacket and red tie, with matching red cuffs! Snazzy. Wearing this makes you feel more important than your job title does." + name = "head of personnel's suit" + icon_state = "hopwhimsy" + item_state_slots = list(slot_r_hand_str = "hop", slot_l_hand_str = "hop") + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/hydroponics + desc = "It's a jumpsuit designed to protect against minor plant-related hazards." + name = "botanist's jumpsuit" + icon_state = "hydroponics" + item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") + permeability_coefficient = 0.50 + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/hydroponics/alt + icon_state = "hydro" + +/obj/item/clothing/under/rank/internalaffairs + desc = "The plain, professional attire of an Internal Affairs Agent. The collar is immaculately starched." + name = "Internal Affairs uniform" + icon_state = "internalaffairs" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + rolled_sleeves = 0 + starting_accessories = list(/obj/item/clothing/accessory/tie/black) + +/obj/item/clothing/under/rank/internalaffairs/skirt + desc = "The plain, professional attire of an Internal Affairs Agent. The top button is sewn shut." + name = "Internal Affairs skirt" + icon_state = "internalaffairs_skirt" + +/obj/item/clothing/under/rank/janitor + desc = "It's the official uniform of the station's janitor. It has minor protection from biohazards." + name = "janitor's jumpsuit" + icon_state = "janitor" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/janitor/alt + name = "janitor's overalls" + icon_state = "janitor_alt" + +/obj/item/clothing/under/lawyer + desc = "Slick threads." + name = "lawyer suit" + +/obj/item/clothing/under/lawyer/black + name = "black lawyer suit" + icon_state = "lawyer_black" + +/obj/item/clothing/under/lawyer/black/skirt + name = "black lawyer skirt" + icon_state = "lawyer_black_skirt" + item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") + +/obj/item/clothing/under/lawyer/female + name = "black lawyer suit" + icon_state = "black_suit_fem" + item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") + +/obj/item/clothing/under/lawyer/red + name = "red lawyer suit" + icon_state = "lawyer_red" + +/obj/item/clothing/under/lawyer/red/skirt + name = "red lawyer skirt" + icon_state = "lawyer_red_skirt" + item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") + +/obj/item/clothing/under/lawyer/blue + name = "blue lawyer suit" + icon_state = "lawyer_blue" + +/obj/item/clothing/under/lawyer/blue/skirt + name = "blue lawyer skirt" + icon_state = "lawyer_blue_skirt" + item_state_slots = list(slot_r_hand_str = "lawyer_blue", slot_l_hand_str = "lawyer_blue") + +/obj/item/clothing/under/lawyer/bluesuit + name = "blue suit" + desc = "A classy suit." + icon_state = "bluesuit" + item_state_slots = list(slot_r_hand_str = "lawyer_blue", slot_l_hand_str = "lawyer_blue") + starting_accessories = list(/obj/item/clothing/accessory/tie/red) + +/obj/item/clothing/under/lawyer/bluesuit/skirt + name = "blue skirt suit" + icon_state = "bluesuit_skirt" + +/obj/item/clothing/under/lawyer/purpsuit + name = "purple suit" + icon_state = "lawyer_purp" + item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") + +/obj/item/clothing/under/lawyer/purpsuit/skirt + name = "purple skirt suit" + icon_state = "lawyer_purp_skirt" + +/obj/item/clothing/under/lawyer/oldman + name = "Old Man's Suit" + desc = "A classic suit for the older gentleman, with built in back support." + icon_state = "oldman" + item_state_slots = list(slot_r_hand_str = "johnny", slot_l_hand_str = "johnny") + +/obj/item/clothing/under/oldwoman + name = "Old Woman's Attire" + desc = "A typical outfit for the older woman, a lovely cardigan and comfortable skirt." + icon_state = "oldwoman" + item_state_slots = list(slot_r_hand_str = "johnny", slot_l_hand_str = "johnny") + +/obj/item/clothing/under/librarian + name = "sensible suit" + desc = "It's very... sensible." + icon_state = "red_suit" + item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") + +/obj/item/clothing/under/mime + name = "mime's outfit" + desc = "It's not very colourful." + icon_state = "mime" + +/obj/item/clothing/under/rank/miner + desc = "It's a snappy jumpsuit with a sturdy set of overalls. It is very dirty." + name = "shaft miner's jumpsuit" + icon_state = "miner" + rolled_sleeves = 0 diff --git a/code/modules/clothing/under/jobs/engineering.dm b/code/modules/clothing/under/jobs/engineering.dm index f57cc95ce8c..a648e1d74ec 100644 --- a/code/modules/clothing/under/jobs/engineering.dm +++ b/code/modules/clothing/under/jobs/engineering.dm @@ -1,33 +1,33 @@ -//Contains: Engineering department jumpsuits -/obj/item/clothing/under/rank/chief_engineer - desc = "It's a high visibility jumpsuit given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." - name = "chief engineer's jumpsuit" - icon_state = "chief" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/atmospheric_technician - desc = "It's a jumpsuit worn by atmospheric technicians." - name = "atmospheric technician's jumpsuit" - icon_state = "atmos" - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/engineer - desc = "It's an orange high visibility jumpsuit worn by engineers. It has minor radiation shielding." - name = "engineer's jumpsuit" - icon_state = "engine" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/engineer/turtleneck - name = "engineering turtleneck" - desc = "It's a stylish turtleneck with minor radiation shielding. Nobody's going to see it behind the voidsuit, though." - icon_state = "turtle_eng" - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/roboticist - desc = "It's a slimming black jumpsuit with reinforced seams; great for industrial work." - name = "roboticist's jumpsuit" - icon_state = "robotics" +//Contains: Engineering department jumpsuits +/obj/item/clothing/under/rank/chief_engineer + desc = "It's a high visibility jumpsuit given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." + name = "chief engineer's jumpsuit" + icon_state = "chief" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/atmospheric_technician + desc = "It's a jumpsuit worn by atmospheric technicians." + name = "atmospheric technician's jumpsuit" + icon_state = "atmos" + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/engineer + desc = "It's an orange high visibility jumpsuit worn by engineers. It has minor radiation shielding." + name = "engineer's jumpsuit" + icon_state = "engine" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/engineer/turtleneck + name = "engineering turtleneck" + desc = "It's a stylish turtleneck with minor radiation shielding. Nobody's going to see it behind the voidsuit, though." + icon_state = "turtle_eng" + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/roboticist + desc = "It's a slimming black jumpsuit with reinforced seams; great for industrial work." + name = "roboticist's jumpsuit" + icon_state = "robotics" rolled_sleeves = 0 \ No newline at end of file diff --git a/code/modules/clothing/under/jobs/medsci.dm b/code/modules/clothing/under/jobs/medsci.dm index 013e3d917f7..00b0ed43f96 100644 --- a/code/modules/clothing/under/jobs/medsci.dm +++ b/code/modules/clothing/under/jobs/medsci.dm @@ -1,254 +1,254 @@ -/* - * Science - */ -/obj/item/clothing/under/rank/research_director - desc = "It's a jumpsuit worn by those with the know-how to achieve the position of \"Research Director\". Its fabric provides minor protection from biological contaminants." - name = "research director's jumpsuit" - icon_state = "director" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - -/obj/item/clothing/under/rank/research_director/rdalt - desc = "A dress suit and slacks stained with hard work and dedication to science. Perhaps other things as well, but mostly hard work and dedication." - name = "head researcher uniform" - icon_state = "rdalt" - item_state_slots = list(slot_r_hand_str = "director", slot_l_hand_str = "director") - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - -/obj/item/clothing/under/rank/research_director/dress_rd - name = "research director dress uniform" - desc = "Feminine fashion for the style conscious RD. Its fabric provides minor protection from biological contaminants." - icon_state = "dress_rd" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/rank/scientist - desc = "It's made of a special fiber that provides minor protection against biohazards. It has markings that denote the wearer as a scientist." - name = "scientist's jumpsuit" - icon_state = "science" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 10, bio = 0, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/scientist/turtleneck - name = "science turtleneck" - desc = "It's a stylish turtleneck weaved with an explosive-resistant, comfortable mesh. You don't have to look like a dork to be a dork." - icon_state = "turtle_sci" - item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/chemist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a chemist rank stripe on it." - name = "chemist's jumpsuit" - icon_state = "chemistry" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/* - * Medical - */ -/obj/item/clothing/under/rank/chief_medical_officer - desc = "It's a jumpsuit worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." - name = "chief medical officer's jumpsuit" - icon_state = "cmo" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/geneticist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a genetics rank stripe on it." - name = "geneticist's jumpsuit" - icon_state = "genetics" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/virologist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a virologist rank stripe on it." - name = "virologist's jumpsuit" - icon_state = "virology" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/nursesuit - desc = "It's a jumpsuit commonly worn by nursing staff in the medical department." - name = "nurse's suit" - icon_state = "nursesuit" - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - body_parts_covered = UPPER_TORSO|LOWER_TORSO - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/nurse - desc = "A dress commonly worn by the nursing staff in the medical department." - name = "nurse's dress" - icon_state = "nurse" - item_state_slots = list(slot_r_hand_str = "nursesuit", slot_l_hand_str = "nursesuit") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - body_parts_covered = UPPER_TORSO|LOWER_TORSO - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/orderly - desc = "A white suit to be worn by medical attendants." - name = "orderly's uniform" - icon_state = "orderly" - item_state_slots = list(slot_r_hand_str = "nursesuit", slot_l_hand_str = "nursesuit") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/medical - desc = "It's made of a special fiber that provides minor protection against biohazards. It has a cross on the chest denoting that the wearer is trained medical personnel." - name = "medical doctor's jumpsuit" - icon_state = "medical" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/medical/turtleneck - name = "medical turtleneck" - desc = "It's a stylish turtleneck made of bioresistant fiber. Look good, save lives- what more could you want?" - icon_state = "turtle_med" - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/medical/paramedic - name = "paramedic uniform" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is the color scheme that designates a rapid first responder." - icon_state = "paramedic" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/medical/paramedic_alt - name = "short sleeve medical jumpsuit" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one has a cross on the chest denoting that the wearer is trained medical personnel." - icon_state = "medical_short" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/medical/scrubs - name = "blue scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in baby blue." - icon_state = "scrubsblue" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - -/obj/item/clothing/under/rank/medical/scrubs/green - name = "green scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in dark green." - icon_state = "scrubsgreen" - item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") - -/obj/item/clothing/under/rank/medical/scrubs/purple - name = "purple scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in deep purple." - icon_state = "scrubspurple" - item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") - -/obj/item/clothing/under/rank/medical/scrubs/black - name = "black scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in black." - icon_state = "scrubsblack" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - -/obj/item/clothing/under/rank/medical/scrubs/navyblue - name = "navy blue scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in navy blue." - icon_state = "scrubsnavyblue" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - -/obj/item/clothing/under/rank/medical/scrubs/white - name = "scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards" - icon_state = "scrubs" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - -/obj/item/clothing/under/rank/paramedunidark - name = "dark paramedic uniform" - desc = "A dark jumpsuit for those brave souls who have to deal with a CMO who thinks they're the do everything person." - icon_state = "paramedicdark" - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/parameduniskirtdark - name = "dark paramedic uniskirt" - desc = "A dark jumpskirt for those brave souls who have to deal with a CMO who thinks they're the do everything person." - icon_state = "paramedicdark_skirt" - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/paramedunilight - name = "light paramedic uniform" - desc = "A light jumpsuit for those brave souls who have to deal with a CMO who thinks they're the do everything person." - icon_state = "paramediclight" - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/parameduniskirtlight - name = "light paramedic uniskirt" - desc = "A light jumpskirt for those brave souls who have to deal with a CMO who thinks they're the do everything person." - icon_state = "paramediclight_skirt" - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/psych - desc = "A basic white jumpsuit. It has turqouise markings that denote the wearer as a psychiatrist." - name = "psychiatrist's jumpsuit" - icon_state = "psych" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - -/obj/item/clothing/under/rank/psych/turtleneck - desc = "A turqouise turtleneck and a pair of dark blue slacks, belonging to a psychologist." - name = "psychologist's turtleneck" - icon_state = "psychturtle" - item_state_slots = list(slot_r_hand_str = "psyche", slot_l_hand_str = "psyche") - rolled_sleeves = 0 -/* - * Medsci, unused (i think) stuff - */ -/obj/item/clothing/under/rank/geneticist_new - desc = "It's made of a special fiber which provides minor protection against biohazards." - name = "geneticist's jumpsuit" - icon_state = "genetics_new" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/chemist_new - desc = "It's made of a special fiber which provides minor protection against biohazards." - name = "chemist's jumpsuit" - icon_state = "chemist_new" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/scientist_new - desc = "Made of a special fiber that gives special protection against biohazards and small explosions." - name = "scientist's jumpsuit" - icon_state = "scientist_new" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 10, bio = 0, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/virologist_new - desc = "Made of a special fiber that gives increased protection against biohazards." - name = "virologist's jumpsuit" - icon_state = "virologist_new" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 +/* + * Science + */ +/obj/item/clothing/under/rank/research_director + desc = "It's a jumpsuit worn by those with the know-how to achieve the position of \"Research Director\". Its fabric provides minor protection from biological contaminants." + name = "research director's jumpsuit" + icon_state = "director" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + +/obj/item/clothing/under/rank/research_director/rdalt + desc = "A dress suit and slacks stained with hard work and dedication to science. Perhaps other things as well, but mostly hard work and dedication." + name = "head researcher uniform" + icon_state = "rdalt" + item_state_slots = list(slot_r_hand_str = "director", slot_l_hand_str = "director") + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + +/obj/item/clothing/under/rank/research_director/dress_rd + name = "research director dress uniform" + desc = "Feminine fashion for the style conscious RD. Its fabric provides minor protection from biological contaminants." + icon_state = "dress_rd" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/scientist + desc = "It's made of a special fiber that provides minor protection against biohazards. It has markings that denote the wearer as a scientist." + name = "scientist's jumpsuit" + icon_state = "science" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 10, bio = 0, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/scientist/turtleneck + name = "science turtleneck" + desc = "It's a stylish turtleneck weaved with an explosive-resistant, comfortable mesh. You don't have to look like a dork to be a dork." + icon_state = "turtle_sci" + item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/chemist + desc = "It's made of a special fiber that gives special protection against biohazards. It has a chemist rank stripe on it." + name = "chemist's jumpsuit" + icon_state = "chemistry" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/* + * Medical + */ +/obj/item/clothing/under/rank/chief_medical_officer + desc = "It's a jumpsuit worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." + name = "chief medical officer's jumpsuit" + icon_state = "cmo" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/geneticist + desc = "It's made of a special fiber that gives special protection against biohazards. It has a genetics rank stripe on it." + name = "geneticist's jumpsuit" + icon_state = "genetics" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/virologist + desc = "It's made of a special fiber that gives special protection against biohazards. It has a virologist rank stripe on it." + name = "virologist's jumpsuit" + icon_state = "virology" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/nursesuit + desc = "It's a jumpsuit commonly worn by nursing staff in the medical department." + name = "nurse's suit" + icon_state = "nursesuit" + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + body_parts_covered = UPPER_TORSO|LOWER_TORSO + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/nurse + desc = "A dress commonly worn by the nursing staff in the medical department." + name = "nurse's dress" + icon_state = "nurse" + item_state_slots = list(slot_r_hand_str = "nursesuit", slot_l_hand_str = "nursesuit") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + body_parts_covered = UPPER_TORSO|LOWER_TORSO + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/orderly + desc = "A white suit to be worn by medical attendants." + name = "orderly's uniform" + icon_state = "orderly" + item_state_slots = list(slot_r_hand_str = "nursesuit", slot_l_hand_str = "nursesuit") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/medical + desc = "It's made of a special fiber that provides minor protection against biohazards. It has a cross on the chest denoting that the wearer is trained medical personnel." + name = "medical doctor's jumpsuit" + icon_state = "medical" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/medical/turtleneck + name = "medical turtleneck" + desc = "It's a stylish turtleneck made of bioresistant fiber. Look good, save lives- what more could you want?" + icon_state = "turtle_med" + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/medical/paramedic + name = "paramedic uniform" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is the color scheme that designates a rapid first responder." + icon_state = "paramedic" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/medical/paramedic_alt + name = "short sleeve medical jumpsuit" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one has a cross on the chest denoting that the wearer is trained medical personnel." + icon_state = "medical_short" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/medical/scrubs + name = "blue scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in baby blue." + icon_state = "scrubsblue" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + +/obj/item/clothing/under/rank/medical/scrubs/green + name = "green scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in dark green." + icon_state = "scrubsgreen" + item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") + +/obj/item/clothing/under/rank/medical/scrubs/purple + name = "purple scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in deep purple." + icon_state = "scrubspurple" + item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") + +/obj/item/clothing/under/rank/medical/scrubs/black + name = "black scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in black." + icon_state = "scrubsblack" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + +/obj/item/clothing/under/rank/medical/scrubs/navyblue + name = "navy blue scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in navy blue." + icon_state = "scrubsnavyblue" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + +/obj/item/clothing/under/rank/medical/scrubs/white + name = "scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards" + icon_state = "scrubs" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + +/obj/item/clothing/under/rank/paramedunidark + name = "dark paramedic uniform" + desc = "A dark jumpsuit for those brave souls who have to deal with a CMO who thinks they're the do everything person." + icon_state = "paramedicdark" + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/parameduniskirtdark + name = "dark paramedic uniskirt" + desc = "A dark jumpskirt for those brave souls who have to deal with a CMO who thinks they're the do everything person." + icon_state = "paramedicdark_skirt" + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/paramedunilight + name = "light paramedic uniform" + desc = "A light jumpsuit for those brave souls who have to deal with a CMO who thinks they're the do everything person." + icon_state = "paramediclight" + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/parameduniskirtlight + name = "light paramedic uniskirt" + desc = "A light jumpskirt for those brave souls who have to deal with a CMO who thinks they're the do everything person." + icon_state = "paramediclight_skirt" + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/psych + desc = "A basic white jumpsuit. It has turqouise markings that denote the wearer as a psychiatrist." + name = "psychiatrist's jumpsuit" + icon_state = "psych" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + +/obj/item/clothing/under/rank/psych/turtleneck + desc = "A turqouise turtleneck and a pair of dark blue slacks, belonging to a psychologist." + name = "psychologist's turtleneck" + icon_state = "psychturtle" + item_state_slots = list(slot_r_hand_str = "psyche", slot_l_hand_str = "psyche") + rolled_sleeves = 0 +/* + * Medsci, unused (i think) stuff + */ +/obj/item/clothing/under/rank/geneticist_new + desc = "It's made of a special fiber which provides minor protection against biohazards." + name = "geneticist's jumpsuit" + icon_state = "genetics_new" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/chemist_new + desc = "It's made of a special fiber which provides minor protection against biohazards." + name = "chemist's jumpsuit" + icon_state = "chemist_new" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/scientist_new + desc = "Made of a special fiber that gives special protection against biohazards and small explosions." + name = "scientist's jumpsuit" + icon_state = "scientist_new" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 10, bio = 0, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/virologist_new + desc = "Made of a special fiber that gives increased protection against biohazards." + name = "virologist's jumpsuit" + icon_state = "virologist_new" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 diff --git a/code/modules/clothing/under/jobs/security.dm b/code/modules/clothing/under/jobs/security.dm index a001d5aa7cd..3d6bdfec0c0 100644 --- a/code/modules/clothing/under/jobs/security.dm +++ b/code/modules/clothing/under/jobs/security.dm @@ -1,217 +1,217 @@ -/* - * Contains: - * Security - * Detective - * Head of Security - */ - -/* - * Security - */ -/obj/item/clothing/under/rank/warden - desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for more robust protection. It has the word \"Warden\" written on the shoulders." - name = "warden's jumpsuit" - icon_state = "warden" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/security - name = "security officer's jumpsuit" - desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for robust protection." - icon_state = "security" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/security/modern - name = "modernized security officer's jumpsuit" - desc = "A recent redesign of the classic Security jumpsuit, featuring sturdy materials, joint padding, one giant zipper, and tight-fitting synthleather." - icon_state = "securitymodern" - item_state = "securitymodern" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - rolled_sleeves = -1 - worn_state = "securitymodern" - icon = 'icons/inventory/uniform/item.dmi' - default_worn_icon = 'icons/inventory/uniform/mob_vr.dmi' - -/obj/item/clothing/under/rank/security/turtleneck - name = "security turtleneck" - desc = "It's a stylish turtleneck made of a robust nanoweave. Nobody said the Law couldn't be fashionable." - icon_state = "turtle_sec" - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/dispatch - name = "dispatcher's uniform" - desc = "A dress shirt and khakis with a security patch sewn on." - icon_state = "dispatch" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - siemens_coefficient = 0.9 - -/obj/item/clothing/under/rank/security2 - name = "security officer's uniform" - desc = "It's made of a slightly sturdier material, to allow for robust protection." - icon_state = "redshirt2" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/security/corp - icon_state = "sec_corporate" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/warden/corp - icon_state = "warden_corporate" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - rolled_sleeves = 0 - -/obj/item/clothing/under/tactical - name = "tactical jumpsuit" - desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for robust protection." - icon_state = "swatunder" - item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") - armor = list(melee = 10, bullet = 5, laser = 5,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - rolled_sleeves = -1 - -/* - * Detective - */ -/obj/item/clothing/under/det - name = "detective's suit" - desc = "A rumpled white dress shirt paired with well-worn grey slacks." - icon_state = "detective" - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - rolled_sleeves = 0 - starting_accessories = list(/obj/item/clothing/accessory/tie/blue_clip) - -/* -/obj/item/clothing/under/det/verb/rollup() - set name = "Roll Suit Sleeves" - set category = "Object" - set src in usr - var/unrolled = item_state_slots[slot_w_uniform_str] == initial(worn_state) - item_state_slots[slot_w_uniform_str] = unrolled ? "[worn_state]_r" : initial(worn_state) - var/mob/living/carbon/human/H = loc - H.update_inv_w_uniform(1) - to_chat(H, "You roll the sleeves of your shirt [unrolled ? "up" : "down"]") -*/ - -/obj/item/clothing/under/det/grey - icon_state = "detective2" - desc = "A serious-looking tan dress shirt paired with freshly-pressed black slacks." - starting_accessories = list(/obj/item/clothing/accessory/tie/red_long) - -/obj/item/clothing/under/det/black - icon_state = "detective3" - item_state_slots = list(slot_r_hand_str = "sl_suit", slot_l_hand_str = "sl_suit") - desc = "An immaculate white dress shirt, paired with a pair of dark grey dress pants, a red tie, and a charcoal vest." - starting_accessories = list(/obj/item/clothing/accessory/tie/red_long, /obj/item/clothing/accessory/vest) - -/obj/item/clothing/under/det/corporate - name = "detective's jumpsuit" - icon_state = "det_corporate" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - desc = "A more modern uniform for corporate investigators." - -/obj/item/clothing/under/det/waistcoat - icon_state = "detective" - desc = "A rumpled white dress shirt paired with well-worn grey slacks, complete with a blue striped tie, faux-gold tie clip, and waistcoat." - starting_accessories = list(/obj/item/clothing/accessory/tie/blue_clip, /obj/item/clothing/accessory/wcoat) - -/obj/item/clothing/under/det/grey/waistcoat - icon_state = "detective2" - desc = "A serious-looking tan dress shirt paired with freshly-pressed black slacks, complete with a red striped tie and waistcoat." - starting_accessories = list(/obj/item/clothing/accessory/tie/red_long, /obj/item/clothing/accessory/wcoat) - -/obj/item/clothing/under/det/black/waistcoat - icon_state = "detective3" - desc = "A serious-looking tan dress shirt paired with freshly-pressed black slacks, a red tie, and a charcoal vest." - starting_accessories = list(/obj/item/clothing/accessory/tie/red_long, /obj/item/clothing/accessory/wcoat) - -/obj/item/clothing/under/det/skirt - name = "detective's skirt" - icon_state = "detective_skirt" - desc = "A serious-looking white blouse paired with a formal black pencil skirt." - item_state_slots = list(slot_r_hand_str = "sl_suit", slot_l_hand_str = "sl_suit") - -/* - * Head of Security - */ -/obj/item/clothing/under/rank/head_of_security - desc = "It's a jumpsuit worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." - name = "head of security's jumpsuit" - icon_state = "hos" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/head_of_security/corp - icon_state = "hos_corporate" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - rolled_sleeves = 0 - -//Jensen cosplay gear -/obj/item/clothing/under/rank/head_of_security/jensen - desc = "You never asked for anything that stylish." - name = "head of security's jumpsuit" - icon_state = "jensen" - rolled_sleeves = -1 - -/* - * Navy uniforms - */ -/obj/item/clothing/under/rank/security/navyblue - name = "security officer's uniform" - desc = "The latest in fashionable security outfits." - icon_state = "officerblueclothes" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/head_of_security/navyblue - desc = "The insignia on this uniform tells you that this uniform belongs to the Head of Security." - name = "head of security's uniform" - icon_state = "hosblueclothes" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/warden/navyblue - desc = "The insignia on this uniform tells you that this uniform belongs to the Warden." - name = "warden's uniform" - icon_state = "wardenblueclothes" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - rolled_sleeves = 0 - -/* - * Tan uniforms - */ -/obj/item/clothing/under/rank/security/tan - name = "security officer's uniform" - desc = "The latest in fashionable security outfits." - icon_state = "officertanclothes" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/head_of_security/tan - desc = "The insignia on this uniform tells you that this uniform belongs to the Head of Security." - name = "head of security's uniform" - icon_state = "hostanclothes" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/warden/tan - desc = "The insignia on this uniform tells you that this uniform belongs to the Warden." - name = "warden's uniform" - icon_state = "wardentanclothes" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - rolled_sleeves = 0 - +/* + * Contains: + * Security + * Detective + * Head of Security + */ + +/* + * Security + */ +/obj/item/clothing/under/rank/warden + desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for more robust protection. It has the word \"Warden\" written on the shoulders." + name = "warden's jumpsuit" + icon_state = "warden" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/security + name = "security officer's jumpsuit" + desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for robust protection." + icon_state = "security" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/security/modern + name = "modernized security officer's jumpsuit" + desc = "A recent redesign of the classic Security jumpsuit, featuring sturdy materials, joint padding, one giant zipper, and tight-fitting synthleather." + icon_state = "securitymodern" + item_state = "securitymodern" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + rolled_sleeves = -1 + worn_state = "securitymodern" + icon = 'icons/inventory/uniform/item.dmi' + default_worn_icon = 'icons/inventory/uniform/mob_vr.dmi' + +/obj/item/clothing/under/rank/security/turtleneck + name = "security turtleneck" + desc = "It's a stylish turtleneck made of a robust nanoweave. Nobody said the Law couldn't be fashionable." + icon_state = "turtle_sec" + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/dispatch + name = "dispatcher's uniform" + desc = "A dress shirt and khakis with a security patch sewn on." + icon_state = "dispatch" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + siemens_coefficient = 0.9 + +/obj/item/clothing/under/rank/security2 + name = "security officer's uniform" + desc = "It's made of a slightly sturdier material, to allow for robust protection." + icon_state = "redshirt2" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/security/corp + icon_state = "sec_corporate" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/warden/corp + icon_state = "warden_corporate" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + rolled_sleeves = 0 + +/obj/item/clothing/under/tactical + name = "tactical jumpsuit" + desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for robust protection." + icon_state = "swatunder" + item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") + armor = list(melee = 10, bullet = 5, laser = 5,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + rolled_sleeves = -1 + +/* + * Detective + */ +/obj/item/clothing/under/det + name = "detective's suit" + desc = "A rumpled white dress shirt paired with well-worn grey slacks." + icon_state = "detective" + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + rolled_sleeves = 0 + starting_accessories = list(/obj/item/clothing/accessory/tie/blue_clip) + +/* +/obj/item/clothing/under/det/verb/rollup() + set name = "Roll Suit Sleeves" + set category = "Object" + set src in usr + var/unrolled = item_state_slots[slot_w_uniform_str] == initial(worn_state) + item_state_slots[slot_w_uniform_str] = unrolled ? "[worn_state]_r" : initial(worn_state) + var/mob/living/carbon/human/H = loc + H.update_inv_w_uniform(1) + to_chat(H, "You roll the sleeves of your shirt [unrolled ? "up" : "down"]") +*/ + +/obj/item/clothing/under/det/grey + icon_state = "detective2" + desc = "A serious-looking tan dress shirt paired with freshly-pressed black slacks." + starting_accessories = list(/obj/item/clothing/accessory/tie/red_long) + +/obj/item/clothing/under/det/black + icon_state = "detective3" + item_state_slots = list(slot_r_hand_str = "sl_suit", slot_l_hand_str = "sl_suit") + desc = "An immaculate white dress shirt, paired with a pair of dark grey dress pants, a red tie, and a charcoal vest." + starting_accessories = list(/obj/item/clothing/accessory/tie/red_long, /obj/item/clothing/accessory/vest) + +/obj/item/clothing/under/det/corporate + name = "detective's jumpsuit" + icon_state = "det_corporate" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + desc = "A more modern uniform for corporate investigators." + +/obj/item/clothing/under/det/waistcoat + icon_state = "detective" + desc = "A rumpled white dress shirt paired with well-worn grey slacks, complete with a blue striped tie, faux-gold tie clip, and waistcoat." + starting_accessories = list(/obj/item/clothing/accessory/tie/blue_clip, /obj/item/clothing/accessory/wcoat) + +/obj/item/clothing/under/det/grey/waistcoat + icon_state = "detective2" + desc = "A serious-looking tan dress shirt paired with freshly-pressed black slacks, complete with a red striped tie and waistcoat." + starting_accessories = list(/obj/item/clothing/accessory/tie/red_long, /obj/item/clothing/accessory/wcoat) + +/obj/item/clothing/under/det/black/waistcoat + icon_state = "detective3" + desc = "A serious-looking tan dress shirt paired with freshly-pressed black slacks, a red tie, and a charcoal vest." + starting_accessories = list(/obj/item/clothing/accessory/tie/red_long, /obj/item/clothing/accessory/wcoat) + +/obj/item/clothing/under/det/skirt + name = "detective's skirt" + icon_state = "detective_skirt" + desc = "A serious-looking white blouse paired with a formal black pencil skirt." + item_state_slots = list(slot_r_hand_str = "sl_suit", slot_l_hand_str = "sl_suit") + +/* + * Head of Security + */ +/obj/item/clothing/under/rank/head_of_security + desc = "It's a jumpsuit worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." + name = "head of security's jumpsuit" + icon_state = "hos" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/head_of_security/corp + icon_state = "hos_corporate" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + rolled_sleeves = 0 + +//Jensen cosplay gear +/obj/item/clothing/under/rank/head_of_security/jensen + desc = "You never asked for anything that stylish." + name = "head of security's jumpsuit" + icon_state = "jensen" + rolled_sleeves = -1 + +/* + * Navy uniforms + */ +/obj/item/clothing/under/rank/security/navyblue + name = "security officer's uniform" + desc = "The latest in fashionable security outfits." + icon_state = "officerblueclothes" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/head_of_security/navyblue + desc = "The insignia on this uniform tells you that this uniform belongs to the Head of Security." + name = "head of security's uniform" + icon_state = "hosblueclothes" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/warden/navyblue + desc = "The insignia on this uniform tells you that this uniform belongs to the Warden." + name = "warden's uniform" + icon_state = "wardenblueclothes" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + rolled_sleeves = 0 + +/* + * Tan uniforms + */ +/obj/item/clothing/under/rank/security/tan + name = "security officer's uniform" + desc = "The latest in fashionable security outfits." + icon_state = "officertanclothes" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/head_of_security/tan + desc = "The insignia on this uniform tells you that this uniform belongs to the Head of Security." + name = "head of security's uniform" + icon_state = "hostanclothes" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/warden/tan + desc = "The insignia on this uniform tells you that this uniform belongs to the Warden." + name = "warden's uniform" + icon_state = "wardentanclothes" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + rolled_sleeves = 0 + diff --git a/code/modules/clothing/under/shorts.dm b/code/modules/clothing/under/shorts.dm index 18622cb4d88..b2289e70eba 100644 --- a/code/modules/clothing/under/shorts.dm +++ b/code/modules/clothing/under/shorts.dm @@ -1,247 +1,247 @@ -//these need item states -S2- -/obj/item/clothing/under/shorts - name = "athletic shorts" - desc = "95% Polyester, 5% Spandex!" - icon_state = "redshorts" // Hackyfix for icon states until someone wants to come do a recolor later. - gender = PLURAL - body_parts_covered = LOWER_TORSO - -/obj/item/clothing/under/shorts/red - name = "red athletic shorts" - icon_state = "redshorts" - -/obj/item/clothing/under/shorts/green - name = "green athletic shorts" - icon_state = "greenshorts" - -/obj/item/clothing/under/shorts/blue - name = "blue athletic shorts" - icon_state = "blueshorts" - -/obj/item/clothing/under/shorts/black - name = "black athletic shorts" - icon_state = "blackshorts" - -/obj/item/clothing/under/shorts/grey - name = "grey athletic shorts" - icon_state = "greyshorts" - -/obj/item/clothing/under/shorts/white - name = "white shorts" - icon_state = "whiteshorts" - -/obj/item/clothing/under/shorts/white/female - name = "white short shorts" - icon_state = "whiteshorts_f" - -/obj/item/clothing/under/shorts/jeans - name = "jeans shorts" - desc = "Some jeans! Just in short form!" - icon_state = "jeans_shorts" - -/obj/item/clothing/under/shorts/jeans/female - name = "jeans short shorts" - icon_state = "jeans_shorts_f" - -/obj/item/clothing/under/shorts/jeans/classic - name = "classic jeans shorts" - icon_state = "jeansclassic_shorts" - -/obj/item/clothing/under/shorts/jeans/classic/female - name = "classic jeans short shorts" - icon_state = "jeansclassic_shorts_f" - -/obj/item/clothing/under/shorts/jeans/mustang - name = "mustang jeans shorts" - icon_state = "jeansmustang_shorts" - -/obj/item/clothing/under/shorts/jeans/mustang/female - name = "mustang jeans short shorts" - icon_state = "jeansmustang_shorts_f" - -/obj/item/clothing/under/shorts/jeans/youngfolks - name = "young folks jeans shorts" - icon_state = "jeansyoungfolks_shorts" - -/obj/item/clothing/under/shorts/jeans/youngfolks/female - name = "young folks jeans short shorts" - icon_state = "jeansyoungfolks_shorts_f" - -/obj/item/clothing/under/shorts/jeans/black - name = "black jeans shorts" - icon_state = "blackpants_shorts" - -/obj/item/clothing/under/shorts/jeans/black/female - name = "black jeans short shorts" - icon_state = "black_shorts_f" - -/obj/item/clothing/under/shorts/black/ripped - name = "black ripped shorts" - icon_state = "black_shorts_ripped" - -/obj/item/clothing/under/shorts/jeans/grey - name = "grey jeans shorts" - icon_state = "greyshorts" - -/obj/item/clothing/under/shorts/jeans/grey/female - name = "grey jeans short shorts" - icon_state = "grey_shorts_f" - -/obj/item/clothing/under/shorts/khaki - name = "khaki shorts" - desc = "For that island getaway. It's five o'clock somewhere, right?" - icon_state = "tanpants_shorts" - -/obj/item/clothing/under/shorts/khaki/female - name = "khaki short shorts" - icon_state = "khaki_shorts_f" - -//Argh, skirts be below this line -> ------------------------------ - -/obj/item/clothing/under/skirt - name = "short black skirt" - desc = "A skirt that is a shiny black." - icon_state = "skirt_short_black" - body_parts_covered = LOWER_TORSO - rolled_sleeves = -1 - -/obj/item/clothing/under/skirt/khaki - name = "khaki skirt" - desc = "A skirt that is a khaki color." - icon_state = "skirt_khaki" - -/obj/item/clothing/under/skirt/blue - name = "short blue skirt" - desc = "A skirt that is a shiny blue." - icon_state = "skirt_short_blue" - -/obj/item/clothing/under/skirt/red - name = "short red skirt" - desc = "A skirt that is a shiny red." - icon_state = "skirt_short_red" - -/obj/item/clothing/under/skirt/denim - name = "short denim skirt" - desc = "A skirt that is made of denim." - icon_state = "skirt_short_denim" - -/obj/item/clothing/under/skirt/swept - name = "swept skirt" - desc = "A skirt that is swept to one side." - icon_state = "skirt_swept" - -/obj/item/clothing/under/skirt/loincloth - name = "loincloth" - desc = "A piece of cloth wrapped around the waist." - icon_state = "loincloth" - -/obj/item/clothing/under/skirt/pleated - name = "pleated skirt" - desc = "A simple pleated skirt. It's like high school all over again." - icon_state = "pleated" - addblends = "pleated_a" - -/obj/item/clothing/under/skirt/outfit - name = "black skirt" - desc = "A black skirt, very fancy!" - icon_state = "blackskirt" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/skirt/outfit/plaid_blue - name = "blue plaid skirt" - desc = "A preppy blue skirt with a white blouse." - icon_state = "plaid_blue" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - -/obj/item/clothing/under/skirt/outfit/plaid_red - name = "red plaid skirt" - desc = "A preppy red skirt with a white blouse." - icon_state = "plaid_red" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - -/obj/item/clothing/under/skirt/outfit/plaid_purple - name = "blue purple skirt" - desc = "A preppy purple skirt with a white blouse." - icon_state = "plaid_purple" - item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") - -/obj/item/clothing/under/rank/cargo/skirt - name = "quartermaster's jumpskirt" - desc = "It's a jumpskirt worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." - icon_state = "qmf" - item_state_slots = list(slot_r_hand_str = "qm", slot_l_hand_str = "qm") - -/obj/item/clothing/under/rank/cargotech/skirt - name = "cargo technician's jumpskirt" - desc = "Skirrrrrts! They're comfy and easy to wear!" - icon_state = "cargof" - item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") - -/obj/item/clothing/under/rank/engineer/skirt - desc = "It's an orange high visibility jumpskirt worn by engineers. It has minor radiation shielding." - name = "engineer's jumpskirt" - icon_state = "enginef" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) - item_state_slots = list(slot_r_hand_str = "engine", slot_l_hand_str = "engine") - -/obj/item/clothing/under/rank/chief_engineer/skirt - desc = "It's a high visibility jumpskirt given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." - name = "chief engineer's jumpskirt" - icon_state = "chieff" - item_state_slots = list(slot_r_hand_str = "chiefengineer", slot_l_hand_str = "chiefengineer") - -/obj/item/clothing/under/rank/atmospheric_technician/skirt - desc = "It's a jumpskirt worn by atmospheric technicians." - name = "atmospheric technician's jumpskirt" - icon_state = "atmosf" - item_state_slots = list(slot_r_hand_str = "atmos", slot_l_hand_str = "atmos") - -/obj/item/clothing/under/rank/roboticist/skirt - desc = "It's a slimming black jumpskirt with reinforced seams; great for industrial work." - name = "roboticist's jumpskirt" - icon_state = "roboticsf" - item_state_slots = list(slot_r_hand_str = "robotics", slot_l_hand_str = "robotics") - -/obj/item/clothing/under/rank/scientist/skirt - name = "scientist's jumpskirt" - icon_state = "sciencef" - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 10, bio = 0, rad = 0) - -/obj/item/clothing/under/rank/medical/skirt - name = "medical doctor's jumpskirt" - icon_state = "medicalf" - -/obj/item/clothing/under/rank/chemist/skirt - name = "chemist's jumpskirt" - icon_state = "chemistryf" - -/obj/item/clothing/under/rank/chief_medical_officer/skirt - desc = "It's a jumpskirt worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." - name = "chief medical officer's jumpskirt" - icon_state = "cmof" - -/obj/item/clothing/under/rank/geneticist/skirt - name = "geneticist's jumpskirt" - icon_state = "geneticsf" - -/obj/item/clothing/under/rank/virologist/skirt - name = "virologist's jumpskirt" - icon_state = "virologyf" - -/obj/item/clothing/under/rank/security/skirt - name = "security officer's jumpskirt" - desc = "Standard feminine fashion for Security Officers. It's made of sturdier material than the standard jumpskirts." - icon_state = "securityf" - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - -/obj/item/clothing/under/rank/warden/skirt - desc = "Standard feminine fashion for a Warden. It is made of sturdier material than standard jumpskirts. It has the word \"Warden\" written on the shoulders." - name = "warden's jumpskirt" - icon_state = "wardenf" - -/obj/item/clothing/under/rank/head_of_security/skirt - desc = "It's a fashionable jumpskirt worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." - name = "head of security's jumpskirt" - icon_state = "hosf" +//these need item states -S2- +/obj/item/clothing/under/shorts + name = "athletic shorts" + desc = "95% Polyester, 5% Spandex!" + icon_state = "redshorts" // Hackyfix for icon states until someone wants to come do a recolor later. + gender = PLURAL + body_parts_covered = LOWER_TORSO + +/obj/item/clothing/under/shorts/red + name = "red athletic shorts" + icon_state = "redshorts" + +/obj/item/clothing/under/shorts/green + name = "green athletic shorts" + icon_state = "greenshorts" + +/obj/item/clothing/under/shorts/blue + name = "blue athletic shorts" + icon_state = "blueshorts" + +/obj/item/clothing/under/shorts/black + name = "black athletic shorts" + icon_state = "blackshorts" + +/obj/item/clothing/under/shorts/grey + name = "grey athletic shorts" + icon_state = "greyshorts" + +/obj/item/clothing/under/shorts/white + name = "white shorts" + icon_state = "whiteshorts" + +/obj/item/clothing/under/shorts/white/female + name = "white short shorts" + icon_state = "whiteshorts_f" + +/obj/item/clothing/under/shorts/jeans + name = "jeans shorts" + desc = "Some jeans! Just in short form!" + icon_state = "jeans_shorts" + +/obj/item/clothing/under/shorts/jeans/female + name = "jeans short shorts" + icon_state = "jeans_shorts_f" + +/obj/item/clothing/under/shorts/jeans/classic + name = "classic jeans shorts" + icon_state = "jeansclassic_shorts" + +/obj/item/clothing/under/shorts/jeans/classic/female + name = "classic jeans short shorts" + icon_state = "jeansclassic_shorts_f" + +/obj/item/clothing/under/shorts/jeans/mustang + name = "mustang jeans shorts" + icon_state = "jeansmustang_shorts" + +/obj/item/clothing/under/shorts/jeans/mustang/female + name = "mustang jeans short shorts" + icon_state = "jeansmustang_shorts_f" + +/obj/item/clothing/under/shorts/jeans/youngfolks + name = "young folks jeans shorts" + icon_state = "jeansyoungfolks_shorts" + +/obj/item/clothing/under/shorts/jeans/youngfolks/female + name = "young folks jeans short shorts" + icon_state = "jeansyoungfolks_shorts_f" + +/obj/item/clothing/under/shorts/jeans/black + name = "black jeans shorts" + icon_state = "blackpants_shorts" + +/obj/item/clothing/under/shorts/jeans/black/female + name = "black jeans short shorts" + icon_state = "black_shorts_f" + +/obj/item/clothing/under/shorts/black/ripped + name = "black ripped shorts" + icon_state = "black_shorts_ripped" + +/obj/item/clothing/under/shorts/jeans/grey + name = "grey jeans shorts" + icon_state = "greyshorts" + +/obj/item/clothing/under/shorts/jeans/grey/female + name = "grey jeans short shorts" + icon_state = "grey_shorts_f" + +/obj/item/clothing/under/shorts/khaki + name = "khaki shorts" + desc = "For that island getaway. It's five o'clock somewhere, right?" + icon_state = "tanpants_shorts" + +/obj/item/clothing/under/shorts/khaki/female + name = "khaki short shorts" + icon_state = "khaki_shorts_f" + +//Argh, skirts be below this line -> ------------------------------ + +/obj/item/clothing/under/skirt + name = "short black skirt" + desc = "A skirt that is a shiny black." + icon_state = "skirt_short_black" + body_parts_covered = LOWER_TORSO + rolled_sleeves = -1 + +/obj/item/clothing/under/skirt/khaki + name = "khaki skirt" + desc = "A skirt that is a khaki color." + icon_state = "skirt_khaki" + +/obj/item/clothing/under/skirt/blue + name = "short blue skirt" + desc = "A skirt that is a shiny blue." + icon_state = "skirt_short_blue" + +/obj/item/clothing/under/skirt/red + name = "short red skirt" + desc = "A skirt that is a shiny red." + icon_state = "skirt_short_red" + +/obj/item/clothing/under/skirt/denim + name = "short denim skirt" + desc = "A skirt that is made of denim." + icon_state = "skirt_short_denim" + +/obj/item/clothing/under/skirt/swept + name = "swept skirt" + desc = "A skirt that is swept to one side." + icon_state = "skirt_swept" + +/obj/item/clothing/under/skirt/loincloth + name = "loincloth" + desc = "A piece of cloth wrapped around the waist." + icon_state = "loincloth" + +/obj/item/clothing/under/skirt/pleated + name = "pleated skirt" + desc = "A simple pleated skirt. It's like high school all over again." + icon_state = "pleated" + addblends = "pleated_a" + +/obj/item/clothing/under/skirt/outfit + name = "black skirt" + desc = "A black skirt, very fancy!" + icon_state = "blackskirt" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/skirt/outfit/plaid_blue + name = "blue plaid skirt" + desc = "A preppy blue skirt with a white blouse." + icon_state = "plaid_blue" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + +/obj/item/clothing/under/skirt/outfit/plaid_red + name = "red plaid skirt" + desc = "A preppy red skirt with a white blouse." + icon_state = "plaid_red" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + +/obj/item/clothing/under/skirt/outfit/plaid_purple + name = "blue purple skirt" + desc = "A preppy purple skirt with a white blouse." + icon_state = "plaid_purple" + item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") + +/obj/item/clothing/under/rank/cargo/skirt + name = "quartermaster's jumpskirt" + desc = "It's a jumpskirt worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." + icon_state = "qmf" + item_state_slots = list(slot_r_hand_str = "qm", slot_l_hand_str = "qm") + +/obj/item/clothing/under/rank/cargotech/skirt + name = "cargo technician's jumpskirt" + desc = "Skirrrrrts! They're comfy and easy to wear!" + icon_state = "cargof" + item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") + +/obj/item/clothing/under/rank/engineer/skirt + desc = "It's an orange high visibility jumpskirt worn by engineers. It has minor radiation shielding." + name = "engineer's jumpskirt" + icon_state = "enginef" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) + item_state_slots = list(slot_r_hand_str = "engine", slot_l_hand_str = "engine") + +/obj/item/clothing/under/rank/chief_engineer/skirt + desc = "It's a high visibility jumpskirt given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." + name = "chief engineer's jumpskirt" + icon_state = "chieff" + item_state_slots = list(slot_r_hand_str = "chiefengineer", slot_l_hand_str = "chiefengineer") + +/obj/item/clothing/under/rank/atmospheric_technician/skirt + desc = "It's a jumpskirt worn by atmospheric technicians." + name = "atmospheric technician's jumpskirt" + icon_state = "atmosf" + item_state_slots = list(slot_r_hand_str = "atmos", slot_l_hand_str = "atmos") + +/obj/item/clothing/under/rank/roboticist/skirt + desc = "It's a slimming black jumpskirt with reinforced seams; great for industrial work." + name = "roboticist's jumpskirt" + icon_state = "roboticsf" + item_state_slots = list(slot_r_hand_str = "robotics", slot_l_hand_str = "robotics") + +/obj/item/clothing/under/rank/scientist/skirt + name = "scientist's jumpskirt" + icon_state = "sciencef" + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 10, bio = 0, rad = 0) + +/obj/item/clothing/under/rank/medical/skirt + name = "medical doctor's jumpskirt" + icon_state = "medicalf" + +/obj/item/clothing/under/rank/chemist/skirt + name = "chemist's jumpskirt" + icon_state = "chemistryf" + +/obj/item/clothing/under/rank/chief_medical_officer/skirt + desc = "It's a jumpskirt worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." + name = "chief medical officer's jumpskirt" + icon_state = "cmof" + +/obj/item/clothing/under/rank/geneticist/skirt + name = "geneticist's jumpskirt" + icon_state = "geneticsf" + +/obj/item/clothing/under/rank/virologist/skirt + name = "virologist's jumpskirt" + icon_state = "virologyf" + +/obj/item/clothing/under/rank/security/skirt + name = "security officer's jumpskirt" + desc = "Standard feminine fashion for Security Officers. It's made of sturdier material than the standard jumpskirts." + icon_state = "securityf" + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + +/obj/item/clothing/under/rank/warden/skirt + desc = "Standard feminine fashion for a Warden. It is made of sturdier material than standard jumpskirts. It has the word \"Warden\" written on the shoulders." + name = "warden's jumpskirt" + icon_state = "wardenf" + +/obj/item/clothing/under/rank/head_of_security/skirt + desc = "It's a fashionable jumpskirt worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." + name = "head of security's jumpskirt" + icon_state = "hosf" diff --git a/code/modules/clothing/under/syndicate.dm b/code/modules/clothing/under/syndicate.dm index 0560d49e300..171a9dcccec 100644 --- a/code/modules/clothing/under/syndicate.dm +++ b/code/modules/clothing/under/syndicate.dm @@ -1,26 +1,26 @@ -//these need item states -S2- -/obj/item/clothing/under/syndicate //Merc Tactleneck - name = "tactical turtleneck" - desc = "It's some non-descript, slightly suspicious looking, civilian clothing." - icon_state = "syndicate" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - has_sensor = 0 - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - -/obj/item/clothing/under/syndicate/combat //ERT tactleneck - name = "combat turtleneck" - desc = "It's some non-descript, slightly suspicious looking, civilian clothing." - icon_state = "combat" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - has_sensor = 1 - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - -/obj/item/clothing/under/syndicate/tacticool - name = "\improper Tacticool turtleneck" - desc = "Just looking at it makes you want to buy an SKS, go into the woods, and -operate-." - icon_state = "tactifool" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - siemens_coefficient = 1 - rolled_sleeves = 0 +//these need item states -S2- +/obj/item/clothing/under/syndicate //Merc Tactleneck + name = "tactical turtleneck" + desc = "It's some non-descript, slightly suspicious looking, civilian clothing." + icon_state = "syndicate" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + has_sensor = 0 + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + +/obj/item/clothing/under/syndicate/combat //ERT tactleneck + name = "combat turtleneck" + desc = "It's some non-descript, slightly suspicious looking, civilian clothing." + icon_state = "combat" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + has_sensor = 1 + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + +/obj/item/clothing/under/syndicate/tacticool + name = "\improper Tacticool turtleneck" + desc = "Just looking at it makes you want to buy an SKS, go into the woods, and -operate-." + icon_state = "tactifool" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + siemens_coefficient = 1 + rolled_sleeves = 0 diff --git a/code/modules/economy/cash.dm b/code/modules/economy/cash.dm index 7b2d31a391c..7cf04f4ac5a 100644 --- a/code/modules/economy/cash.dm +++ b/code/modules/economy/cash.dm @@ -80,7 +80,7 @@ return worth /obj/item/weapon/spacecash/attack_self() - var/amount = tgui_input_number(usr, "How many [initial_name]s do you want to take? (0 to [src.worth])", "Take Money", 20) + var/amount = tgui_input_number(usr, "How many [initial_name]s do you want to take? (0 to [src.worth])", "Take Money", 20, src.worth) if(!src || QDELETED(src)) return amount = round(CLAMP(amount, 0, src.worth)) diff --git a/code/modules/economy/cash_register.dm b/code/modules/economy/cash_register.dm index 8c799597ea0..ae2479d6130 100644 --- a/code/modules/economy/cash_register.dm +++ b/code/modules/economy/cash_register.dm @@ -129,7 +129,7 @@ src.visible_message("\icon[src][bicon(src)][transaction_purpose]: [t_amount] Thaler\s.") if("set_amount") var/item_name = locate(href_list["item"]) - var/n_amount = round(tgui_input_number(usr, "Enter amount", "New amount")) + var/n_amount = round(tgui_input_number(usr, "Enter amount", "New amount", 0, 20, 0)) n_amount = CLAMP(n_amount, 0, 20) if (!item_list[item_name] || !Adjacent(usr)) return transaction_amount += (n_amount - item_list[item_name]) * price_list[item_name] diff --git a/code/modules/economy/casinocash.dm b/code/modules/economy/casinocash.dm index e74b89d42b7..79a057b6365 100644 --- a/code/modules/economy/casinocash.dm +++ b/code/modules/economy/casinocash.dm @@ -112,7 +112,7 @@ return worth /obj/item/weapon/spacecasinocash/attack_self() - var/amount = tgui_input_number(usr, "How much credits worth of chips do you want to take? (0 to [src.worth])", "Take chips", 20) + var/amount = tgui_input_number(usr, "How much credits worth of chips do you want to take? (0 to [src.worth])", "Take chips", 20, src.worth) if(!src || QDELETED(src)) return amount = round(CLAMP(amount, 0, src.worth)) @@ -204,4 +204,4 @@ else if(result == 2) comment = "Joker" user.visible_message("[user] has thrown \the [src]. It lands on [comment]! ", \ - "You throw \the [src]. It lands on [comment]! ") \ No newline at end of file + "You throw \the [src]. It lands on [comment]! ") diff --git a/code/modules/economy/coins_vr.dm b/code/modules/economy/coins_vr.dm index 38ae0054cf6..6d1ac8351ef 100644 --- a/code/modules/economy/coins_vr.dm +++ b/code/modules/economy/coins_vr.dm @@ -1,57 +1,57 @@ -//Weird coins that I would prefer didn't work with normal vending machines. Might use them to make weird vending machines later. - -/obj/item/weapon/aliencoin - icon = 'icons/obj/aliencoins.dmi' - name = "curious coin" - desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. " - icon_state = "triangle" - randpixel = 8 - force = 0.5 - throwforce = 0.5 - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - var/sides = 2 - var/value = 1 - drop_sound = 'sound/items/drop/ring.ogg' - pickup_sound = 'sound/items/pickup/ring.ogg' - -/obj/item/weapon/aliencoin/New() - randpixel_xy() - -/obj/item/weapon/aliencoin/basic - -/obj/item/weapon/aliencoin/gold - name = "curious coin" - icon_state = "triangle-g" - desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. This one's markings appear to reveal a golden material underneath." - value = 10 - -/obj/item/weapon/aliencoin/silver - name = "curious coin" - icon_state = "triangle-s" - desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. This one's markings appear to reveal a silver material underneath." - value = 5 - -/obj/item/weapon/aliencoin/phoron - name = "curious coin" - icon_state = "triangle-p" - desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. This one's markings appear to reveal a purple material underneath." - value = 20 - - -/obj/item/weapon/aliencoin/attack_self(mob/user as mob) - var/result = rand(1, sides) - var/comment = "" - if(result == 1) - comment = "tails" - else if(result == 2) - comment = "heads" - user.visible_message("[user] has thrown [src]. It lands on [comment]! ", runemessage = "[src] landed on [comment]") - if(rand(1,20) == 1) - user.visible_message("[user] fumbled the [src]!", runemessage = "fumbles [src]") - user.remove_from_mob(src) - -/obj/item/weapon/aliencoin/examine(var/mob/user) - . = ..() - if(Adjacent(user)) - . += "It has some writing along its edge that seems to be some language that you are not familiar with. The face of the coin is very smooth, with what appears to be some kind of angular logo along the left side, and a couple of lines of the alien text along the opposite side. The reverse side is similarly smooth, the top of it features what appears to be some kind of vortex, surrounded by six stars, three on either side, with further swirls and intricate patterns along the bottom sections of this face. Looking closely, you can see that there is more text hidden among the swirls." +//Weird coins that I would prefer didn't work with normal vending machines. Might use them to make weird vending machines later. + +/obj/item/weapon/aliencoin + icon = 'icons/obj/aliencoins.dmi' + name = "curious coin" + desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. " + icon_state = "triangle" + randpixel = 8 + force = 0.5 + throwforce = 0.5 + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + var/sides = 2 + var/value = 1 + drop_sound = 'sound/items/drop/ring.ogg' + pickup_sound = 'sound/items/pickup/ring.ogg' + +/obj/item/weapon/aliencoin/New() + randpixel_xy() + +/obj/item/weapon/aliencoin/basic + +/obj/item/weapon/aliencoin/gold + name = "curious coin" + icon_state = "triangle-g" + desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. This one's markings appear to reveal a golden material underneath." + value = 10 + +/obj/item/weapon/aliencoin/silver + name = "curious coin" + icon_state = "triangle-s" + desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. This one's markings appear to reveal a silver material underneath." + value = 5 + +/obj/item/weapon/aliencoin/phoron + name = "curious coin" + icon_state = "triangle-p" + desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. This one's markings appear to reveal a purple material underneath." + value = 20 + + +/obj/item/weapon/aliencoin/attack_self(mob/user as mob) + var/result = rand(1, sides) + var/comment = "" + if(result == 1) + comment = "tails" + else if(result == 2) + comment = "heads" + user.visible_message("[user] has thrown [src]. It lands on [comment]! ", runemessage = "[src] landed on [comment]") + if(rand(1,20) == 1) + user.visible_message("[user] fumbled the [src]!", runemessage = "fumbles [src]") + user.remove_from_mob(src) + +/obj/item/weapon/aliencoin/examine(var/mob/user) + . = ..() + if(Adjacent(user)) + . += "It has some writing along its edge that seems to be some language that you are not familiar with. The face of the coin is very smooth, with what appears to be some kind of angular logo along the left side, and a couple of lines of the alien text along the opposite side. The reverse side is similarly smooth, the top of it features what appears to be some kind of vortex, surrounded by six stars, three on either side, with further swirls and intricate patterns along the bottom sections of this face. Looking closely, you can see that there is more text hidden among the swirls." diff --git a/code/modules/economy/retail_scanner.dm b/code/modules/economy/retail_scanner.dm index f2b4854d116..d2ab5884ae1 100644 --- a/code/modules/economy/retail_scanner.dm +++ b/code/modules/economy/retail_scanner.dm @@ -123,7 +123,7 @@ src.visible_message("\icon[src][bicon(src)][transaction_purpose]: [t_amount] Thaler\s.") if("set_amount") var/item_name = locate(href_list["item"]) - var/n_amount = round(tgui_input_number(usr, "Enter amount", "New amount")) + var/n_amount = round(tgui_input_number(usr, "Enter amount", "New amount", 0, 20, 0)) n_amount = CLAMP(n_amount, 0, 20) if (!item_list[item_name] || !Adjacent(usr)) return transaction_amount += (n_amount - item_list[item_name]) * price_list[item_name] diff --git a/code/modules/eventkit/event_machinery.dm b/code/modules/eventkit/event_machinery.dm index be651026256..87861c7392b 100644 --- a/code/modules/eventkit/event_machinery.dm +++ b/code/modules/eventkit/event_machinery.dm @@ -1,3 +1,3 @@ -/obj/machinery/eventkit/custom - name = "Custom Machine" - desc = "A custom machine created by a GM." +/obj/machinery/eventkit/custom + name = "Custom Machine" + desc = "A custom machine created by a GM." diff --git a/code/modules/eventkit/gm_interfaces/mob_spawner.dm b/code/modules/eventkit/gm_interfaces/mob_spawner.dm index 3154e2ec4e9..607606be57b 100644 --- a/code/modules/eventkit/gm_interfaces/mob_spawner.dm +++ b/code/modules/eventkit/gm_interfaces/mob_spawner.dm @@ -1,205 +1,204 @@ -/datum/eventkit/mob_spawner - // The path of the mob to be spawned - var/path - - //The ai type path to be assigned to the mob - var/use_custom_ai = FALSE - var/ai_type = "" - var/faction = "" - var/intent = "" - var/new_path = TRUE //Sets default ai vars based on path. Tracked explicitly because tgui_act wouldn't make it work, used in tgui_data thusly - - // Defines if the location of the spawned mob should be bound of the users position - var/loc_lock = FALSE - -/datum/eventkit/mob_spawner/New() - . = ..() - -/datum/eventkit/mob_spawner/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "MobSpawner", "EventKit - Mob Spawner") - ui.set_autoupdate(FALSE) - ui.open() - -/datum/eventkit/mob_spawner/Destroy() - . = ..() - -/datum/eventkit/mob_spawner/tgui_state(mob/user) - return GLOB.tgui_admin_state - -/datum/eventkit/mob_spawner/tgui_static_data(mob/user) - var/list/data = list() - - data["initial_x"] = usr.x; - data["initial_y"] = usr.y; - data["initial_z"] = usr.z; - - - return data - -/datum/eventkit/mob_spawner/tgui_data(mob/user) - var/list/data = list() - - data["loc_lock"] = loc_lock - if(loc_lock) - data["loc_x"] = usr.x - data["loc_y"] = usr.y - data["loc_z"] = usr.z - - data["path"] = path; - data["use_custom_ai"] = use_custom_ai - - - if(path) - var/mob/M = new path() - if(M) - data["default_path_name"] = M.name - data["default_desc"] = M.desc - data["default_flavor_text"] = M.flavor_text - if(new_path && istype(M, /mob/living)) - var/mob/living/L = M - - // AI Stuff - ai_type = (L.ai_holder_type ? L.ai_holder_type : /datum/ai_holder/simple_mob/inert) - faction = (L.faction ? L.faction : "neutral") - intent = (L.a_intent ? L.a_intent : I_HELP) - new_path = FALSE - - data["max_health"] = L.maxHealth - data["health"] = L.health - if(istype(L, /mob/living/simple_mob)) - var/mob/living/simple_mob/S = L - data["melee_damage_lower"] = S.melee_damage_lower ? S.melee_damage_lower : 0 - data["melee_damage_upper"] = S.melee_damage_upper ? S.melee_damage_upper : 0 - qdel(S) - qdel(L) - qdel(M) - data["ai_type"] = ai_type - data["faction"] = faction - data["intent"] = intent - - - return data - -/datum/eventkit/mob_spawner/tgui_act(action, list/params) - . = ..() - if(.) - return - if(!check_rights_for(usr.client, R_SPAWN)) - return - switch(action) - if("select_path") - var/list/choices = typesof(/mob) - var/newPath = tgui_input_list(usr, "Please select the new path of the mob you want to spawn.", items = choices) - - path = newPath - new_path = TRUE - return TRUE - if("toggle_custom_ai") - use_custom_ai = !use_custom_ai - return TRUE - if("set_faction") - faction = sanitize(tgui_input_text(usr, "Please input your mobs' faction", "Faction", (faction ? faction : "neutral"))) - return TRUE - if("set_intent") - intent = tgui_input_list(usr, "Please select preferred intent", "Select Intent", list(I_HELP, I_HURT), (intent ? intent : I_HELP)) - return TRUE - if("set_ai_path") - ai_type = tgui_input_list(usr, "Select AI path. Not all subtypes are compatible!", "AI type", \ - typesof(/datum/ai_holder/), (ai_type ? ai_type : /datum/ai_holder/simple_mob/inert)) - return TRUE - if("loc_lock") - loc_lock = !loc_lock - return TRUE - if("start_spawn") - var/confirm = tgui_alert(usr, "Are you sure that you want to start spawning your custom mobs?", "Confirmation", list("Yes", "Cancel")) - - if(confirm != "Yes") - return FALSE - - var/amount = params["amount"] - var/name = params["name"] - var/x = params["x"] - var/y = params["y"] - var/z = params["z"] - - if(!name) - to_chat(usr, "Name cannot be empty.") - return FALSE - - var/turf/T = locate(x, y, z) - if(!T) - to_chat(usr, "Those coordinates are outside the boundaries of the map.") - return FALSE - - for(var/i = 0, i < amount, i++) - if(ispath(path,/turf)) - var/turf/TU = get_turf(locate(x, y, z)) - TU.ChangeTurf(path) - else - var/mob/M = new path(usr.loc) - - M.name = sanitize(name) - M.desc = sanitize(params["desc"]) - M.flavor_text = sanitize(params["flavor_text"]) - if(istype(M, /mob/living)) - var/mob/living/L = M - if(isnum(params["max_health"])) - L.maxHealth = params["max_health"] - if(isnum(params["health"])) - L.health = params["health"] - if(istype(M, /mob/living/simple_mob)) - var/mob/living/simple_mob/S = L - if(isnum(params["melee_damage_lower"])) - S.melee_damage_lower = params["melee_damage_lower"] - if(isnum(params["melee_damage_upper"])) - S.melee_damage_upper = params["melee_damage_upper"] - if(use_custom_ai) - L.ai_holder_type = ai_type - L.faction = faction - L.a_intent = intent - L.initialize_ai_holder() - L.AdjustSleeping(-100) - else - to_chat(usr, span_notice("You can only set AI for subtypes of mob/living!")) - - - - /* - WIP: Radius around selected coords - - var/list/turf/destTurfs - for(var/turf/RT in orange(T, params["r"])) - destTurfs += RT - - var/turf/targetTurf = rand(0,length(destTurfs)) - */ - - var/size_mul = params["size_multiplier"] - if(isnum(size_mul)) - M.size_multiplier = size_mul - M.update_icon() - else - to_chat(usr, "Size Multiplier not applied: ([size_mul]) is not a valid input.") - - M.forceMove(T) - - log_and_message_admins("spawned [path] ([name]) at ([x],[y],[z]) [amount] times.") - - return TRUE - -/datum/eventkit/mob_spawner/tgui_close(mob/user) - . = ..() - qdel(src) - -/client/proc/eventkit_open_mob_spawner() - set category = "EventKit" - set name = "Open Mob Spawner" - set desc = "Opens an advanced version of the mob spawner." - - if(!check_rights(R_SPAWN)) - return - - var/datum/eventkit/mob_spawner/spawner = new() - spawner.tgui_interact(usr) +/datum/eventkit/mob_spawner + // The path of the mob to be spawned + var/path + + //The ai type path to be assigned to the mob + var/use_custom_ai = FALSE + var/ai_type = "" + var/faction = "" + var/intent = "" + var/new_path = TRUE //Sets default ai vars based on path. Tracked explicitly because tgui_act wouldn't make it work, used in tgui_data thusly + + // Defines if the location of the spawned mob should be bound of the users position + var/loc_lock = FALSE + +/datum/eventkit/mob_spawner/New() + . = ..() + +/datum/eventkit/mob_spawner/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "MobSpawner", "EventKit - Mob Spawner") + ui.set_autoupdate(FALSE) + ui.open() + +/datum/eventkit/mob_spawner/Destroy() + . = ..() + +/datum/eventkit/mob_spawner/tgui_state(mob/user) + return GLOB.tgui_admin_state + +/datum/eventkit/mob_spawner/tgui_static_data(mob/user) + var/list/data = list() + + data["initial_x"] = usr.x; + data["initial_y"] = usr.y; + data["initial_z"] = usr.z; + + + return data + +/datum/eventkit/mob_spawner/tgui_data(mob/user) + var/list/data = list() + + data["loc_lock"] = loc_lock + if(loc_lock) + data["loc_x"] = usr.x + data["loc_y"] = usr.y + data["loc_z"] = usr.z + + data["use_custom_ai"] = use_custom_ai + if(new_path) + data["path"] = path; + if(path) + var/mob/M = new path() + if(M) + data["path_name"] = M.name + data["desc"] = M.desc + data["flavor_text"] = M.flavor_text + if(istype(M, /mob/living)) + var/mob/living/L = M + + // AI Stuff + ai_type = (L.ai_holder_type ? L.ai_holder_type : /datum/ai_holder/simple_mob/inert) + faction = (L.faction ? L.faction : "neutral") + intent = (L.a_intent ? L.a_intent : I_HELP) + new_path = FALSE + + data["max_health"] = L.maxHealth + data["health"] = L.health + if(istype(L, /mob/living/simple_mob)) + var/mob/living/simple_mob/S = L + data["melee_damage_lower"] = S.melee_damage_lower ? S.melee_damage_lower : 0 + data["melee_damage_upper"] = S.melee_damage_upper ? S.melee_damage_upper : 0 + qdel(S) + qdel(L) + qdel(M) + data["ai_type"] = ai_type + data["faction"] = faction + data["intent"] = intent + + + return data + +/datum/eventkit/mob_spawner/tgui_act(action, list/params) + . = ..() + if(.) + return + if(!check_rights_for(usr.client, R_SPAWN)) + return + switch(action) + if("select_path") + var/list/choices = typesof(/mob) + var/newPath = tgui_input_list(usr, "Please select the new path of the mob you want to spawn.", items = choices) + + path = newPath + new_path = TRUE + return TRUE + if("toggle_custom_ai") + use_custom_ai = !use_custom_ai + return TRUE + if("set_faction") + faction = sanitize(tgui_input_text(usr, "Please input your mobs' faction", "Faction", (faction ? faction : "neutral"))) + return TRUE + if("set_intent") + intent = tgui_input_list(usr, "Please select preferred intent", "Select Intent", list(I_HELP, I_HURT), (intent ? intent : I_HELP)) + return TRUE + if("set_ai_path") + ai_type = tgui_input_list(usr, "Select AI path. Not all subtypes are compatible!", "AI type", \ + typesof(/datum/ai_holder/), (ai_type ? ai_type : /datum/ai_holder/simple_mob/inert)) + return TRUE + if("loc_lock") + loc_lock = !loc_lock + return TRUE + if("start_spawn") + var/confirm = tgui_alert(usr, "Are you sure that you want to start spawning your custom mobs?", "Confirmation", list("Yes", "Cancel")) + + if(confirm != "Yes") + return FALSE + + var/amount = params["amount"] + var/name = params["name"] + var/x = params["x"] + var/y = params["y"] + var/z = params["z"] + + if(!name) + to_chat(usr, "Name cannot be empty.") + return FALSE + + var/turf/T = locate(x, y, z) + if(!T) + to_chat(usr, "Those coordinates are outside the boundaries of the map.") + return FALSE + + for(var/i = 0, i < amount, i++) + if(ispath(path,/turf)) + var/turf/TU = get_turf(locate(x, y, z)) + TU.ChangeTurf(path) + else + var/mob/M = new path(usr.loc) + + M.name = sanitize(name) + M.desc = sanitize(params["desc"]) + M.flavor_text = sanitize(params["flavor_text"]) + if(istype(M, /mob/living)) + var/mob/living/L = M + if(isnum(params["max_health"])) + L.maxHealth = params["max_health"] + if(isnum(params["health"])) + L.health = params["health"] + if(istype(M, /mob/living/simple_mob)) + var/mob/living/simple_mob/S = L + if(isnum(params["melee_damage_lower"])) + S.melee_damage_lower = params["melee_damage_lower"] + if(isnum(params["melee_damage_upper"])) + S.melee_damage_upper = params["melee_damage_upper"] + if(use_custom_ai) + L.ai_holder_type = ai_type + L.faction = faction + L.a_intent = intent + L.initialize_ai_holder() + L.AdjustSleeping(-100) + else + to_chat(usr, span_notice("You can only set AI for subtypes of mob/living!")) + + + + /* + WIP: Radius around selected coords + + var/list/turf/destTurfs + for(var/turf/RT in orange(T, params["r"])) + destTurfs += RT + + var/turf/targetTurf = rand(0,length(destTurfs)) + */ + + var/size_mul = params["size_multiplier"] + if(isnum(size_mul)) + M.size_multiplier = size_mul + M.update_icon() + else + to_chat(usr, "Size Multiplier not applied: ([size_mul]) is not a valid input.") + + M.forceMove(T) + + log_and_message_admins("spawned [path] ([name]) at ([x],[y],[z]) [amount] times.") + + return TRUE + +/datum/eventkit/mob_spawner/tgui_close(mob/user) + . = ..() + qdel(src) + +/client/proc/eventkit_open_mob_spawner() + set category = "EventKit" + set name = "Open Mob Spawner" + set desc = "Opens an advanced version of the mob spawner." + + if(!check_rights(R_SPAWN)) + return + + var/datum/eventkit/mob_spawner/spawner = new() + spawner.tgui_interact(usr) diff --git a/code/modules/events/apc_damage.dm b/code/modules/events/apc_damage.dm index 4eb8eba335c..af9eb6d503b 100644 --- a/code/modules/events/apc_damage.dm +++ b/code/modules/events/apc_damage.dm @@ -1,50 +1,50 @@ -/datum/event/apc_damage - var/apcSelectionRange = 25 - -/datum/event/apc_damage/start() - var/obj/machinery/power/apc/A = acquire_random_apc() - - var/severity_range = 0 - switch(severity) - if(EVENT_LEVEL_MUNDANE) - severity_range = 0 - if(EVENT_LEVEL_MODERATE) - severity_range = 7 - if(EVENT_LEVEL_MAJOR) - severity_range = 15 - - for(var/obj/machinery/power/apc/apc in range(severity_range,A)) - if(is_valid_apc(apc)) //This event "****s up" the "id authenticator" on APCs, emagging and unlocking them. - apc.emagged = 1 //It used to just blue screen the APC and make it so it had to be hacked to unlock, - apc.locked = 0 //but most people ignored it. Now it has an actual effect on the round and opens - apc.update_icon() //a small possibility for traitors. To fix, remove power cell and apply multitool. - -/datum/event/apc_damage/proc/acquire_random_apc() - var/list/possibleEpicentres = list() - var/list/apcs = list() - - for(var/obj/effect/landmark/newEpicentre in landmarks_list) - if(newEpicentre.name == "lightsout") - possibleEpicentres += newEpicentre - - if(!possibleEpicentres.len) - return - - var/epicentre = pick(possibleEpicentres) - for(var/obj/machinery/power/apc/apc in range(epicentre,apcSelectionRange)) - if(is_valid_apc(apc)) - apcs += apc - // Greatly increase the chance for APCs in maintenance areas to be selected - var/area/A = get_area(apc) - if(istype(A,/area/maintenance)) - apcs += apc - apcs += apc - - if(!apcs.len) - return - - return pick(apcs) - -/datum/event/apc_damage/proc/is_valid_apc(var/obj/machinery/power/apc/apc) - var/turf/T = get_turf(apc) - return !apc.is_critical && !apc.emagged && T && (T.z in using_map.player_levels) +/datum/event/apc_damage + var/apcSelectionRange = 25 + +/datum/event/apc_damage/start() + var/obj/machinery/power/apc/A = acquire_random_apc() + + var/severity_range = 0 + switch(severity) + if(EVENT_LEVEL_MUNDANE) + severity_range = 0 + if(EVENT_LEVEL_MODERATE) + severity_range = 7 + if(EVENT_LEVEL_MAJOR) + severity_range = 15 + + for(var/obj/machinery/power/apc/apc in range(severity_range,A)) + if(is_valid_apc(apc)) //This event "****s up" the "id authenticator" on APCs, emagging and unlocking them. + apc.emagged = 1 //It used to just blue screen the APC and make it so it had to be hacked to unlock, + apc.locked = 0 //but most people ignored it. Now it has an actual effect on the round and opens + apc.update_icon() //a small possibility for traitors. To fix, remove power cell and apply multitool. + +/datum/event/apc_damage/proc/acquire_random_apc() + var/list/possibleEpicentres = list() + var/list/apcs = list() + + for(var/obj/effect/landmark/newEpicentre in landmarks_list) + if(newEpicentre.name == "lightsout") + possibleEpicentres += newEpicentre + + if(!possibleEpicentres.len) + return + + var/epicentre = pick(possibleEpicentres) + for(var/obj/machinery/power/apc/apc in range(epicentre,apcSelectionRange)) + if(is_valid_apc(apc)) + apcs += apc + // Greatly increase the chance for APCs in maintenance areas to be selected + var/area/A = get_area(apc) + if(istype(A,/area/maintenance)) + apcs += apc + apcs += apc + + if(!apcs.len) + return + + return pick(apcs) + +/datum/event/apc_damage/proc/is_valid_apc(var/obj/machinery/power/apc/apc) + var/turf/T = get_turf(apc) + return !apc.is_critical && !apc.emagged && T && (T.z in using_map.player_levels) diff --git a/code/modules/events/aurora_caelus.dm b/code/modules/events/aurora_caelus.dm index 0af166fe230..7eb4a9d94fa 100644 --- a/code/modules/events/aurora_caelus.dm +++ b/code/modules/events/aurora_caelus.dm @@ -1,36 +1,36 @@ -/datum/event/aurora_caelus - has_skybox_image = TRUE - announceWhen = 1 - startWhen = 60 - endWhen = 126 - -/datum/event/aurora_caelus/announce() - command_announcement.Announce("[station_name()]: A harmless cloud of ions is approaching your [using_map.facility_type], and will exhaust their energy battering the hull. \ - Nanotrasen has approved a short break for all employees to relax and observe this very rare event. \ - During this time, starlight will be bright but gentle, shifting between quiet green and blue colors. \ - Any staff who would like to view these lights for themselves may proceed to the area nearest to them with viewing ports to open space. \ - You will have approximately two minutes before the ions begin to reach the hull. \ - We hope you enjoy the lights.", "Nanotrasen Meteorology Division", new_sound = 'sound/AI/aurora.ogg') //VOREStation Edit - -/datum/event/aurora_caelus/start() - affecting_z -= global.using_map.sealed_levels // Space levels only please! - for(var/mob/M in player_list) - if(M.z in affecting_z) - M.playsound_local(null, 'sound/ambience/space/aurora_caelus.ogg', 100, FALSE, pressure_affected = FALSE) - ..() - -/datum/event/aurora_caelus/get_skybox_image() - var/image/res = image('icons/skybox/caelus.dmi', "aurora") - res.appearance_flags = RESET_COLOR - res.blend_mode = BLEND_ADD - return res - -/datum/event/aurora_caelus/end() - command_announcement.Announce("The Aurora Caelus event is now ending. Starlight conditions have returned to normal, and the cloud has dissipated. \ -Please return to your workplace and continue work as normal. \ -Have a pleasant shift, [station_name()], and thank you for watching with us.", -"Nanotrasen Meteorology Division", new_sound = 'sound/AI/aurora_end.ogg') //VOREStation Edit - ..() - -/datum/event/aurora_caelus/overmap/announce() - return +/datum/event/aurora_caelus + has_skybox_image = TRUE + announceWhen = 1 + startWhen = 60 + endWhen = 126 + +/datum/event/aurora_caelus/announce() + command_announcement.Announce("[station_name()]: A harmless cloud of ions is approaching your [using_map.facility_type], and will exhaust their energy battering the hull. \ + Nanotrasen has approved a short break for all employees to relax and observe this very rare event. \ + During this time, starlight will be bright but gentle, shifting between quiet green and blue colors. \ + Any staff who would like to view these lights for themselves may proceed to the area nearest to them with viewing ports to open space. \ + You will have approximately two minutes before the ions begin to reach the hull. \ + We hope you enjoy the lights.", "Nanotrasen Meteorology Division", new_sound = 'sound/AI/aurora.ogg') //VOREStation Edit + +/datum/event/aurora_caelus/start() + affecting_z -= global.using_map.sealed_levels // Space levels only please! + for(var/mob/M in player_list) + if(M.z in affecting_z) + M.playsound_local(null, 'sound/ambience/space/aurora_caelus.ogg', 100, FALSE, pressure_affected = FALSE) + ..() + +/datum/event/aurora_caelus/get_skybox_image() + var/image/res = image('icons/skybox/caelus.dmi', "aurora") + res.appearance_flags = RESET_COLOR + res.blend_mode = BLEND_ADD + return res + +/datum/event/aurora_caelus/end() + command_announcement.Announce("The Aurora Caelus event is now ending. Starlight conditions have returned to normal, and the cloud has dissipated. \ +Please return to your workplace and continue work as normal. \ +Have a pleasant shift, [station_name()], and thank you for watching with us.", +"Nanotrasen Meteorology Division", new_sound = 'sound/AI/aurora_end.ogg') //VOREStation Edit + ..() + +/datum/event/aurora_caelus/overmap/announce() + return diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm index ee66bbea9e0..26980472c5e 100644 --- a/code/modules/events/blob.dm +++ b/code/modules/events/blob.dm @@ -1,21 +1,21 @@ -/datum/event/blob - announceWhen = 12 - endWhen = 120 - - var/obj/structure/blob/core/Blob - - -/datum/event/blob/start() - var/turf/T = pick(blobstart) - if(!T) - kill() - return - - Blob = new /obj/structure/blob/core/random_medium(T) - - -/datum/event/blob/tick() - if(!Blob || !Blob.loc) - Blob = null - kill() - return +/datum/event/blob + announceWhen = 12 + endWhen = 120 + + var/obj/structure/blob/core/Blob + + +/datum/event/blob/start() + var/turf/T = pick(blobstart) + if(!T) + kill() + return + + Blob = new /obj/structure/blob/core/random_medium(T) + + +/datum/event/blob/tick() + if(!Blob || !Blob.loc) + Blob = null + kill() + return diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm index 9f21488c289..061159ff481 100644 --- a/code/modules/events/brand_intelligence.dm +++ b/code/modules/events/brand_intelligence.dm @@ -1,76 +1,76 @@ -/datum/event/brand_intelligence - announceWhen = 21 - endWhen = 1000 //Ends when all vending machines are subverted anyway. - - var/list/obj/machinery/vending/vendingMachines = list() - var/list/obj/machinery/vending/infectedVendingMachines = list() - var/obj/machinery/vending/originMachine - - //VORESTATION Edit - added machine speeches here for better fixing of the event. - - var/list/rampant_speeches = list("Try our aggressive new marketing strategies!", \ - "You should buy products to feed your lifestyle obession!", \ - "Consume!", \ - "Your money can buy happiness!", \ - "Engage direct marketing!", \ - "Advertising is legalized lying! But don't let that put you off our great deals!", \ - "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.") - //VORESTATION Edit End - -/datum/event/brand_intelligence/announce() - command_announcement.Announce("An ongoing mass upload of malware for vendors has been detected onboard [station_name()], which appears to transmit \ - to other nearby vendors. The original infected machine is believed to be \a [originMachine.name].", "Vendor Service Alert") - - -/datum/event/brand_intelligence/start() - for(var/obj/machinery/vending/V in machines) - if(isNotStationLevel(V.z)) continue - vendingMachines.Add(V) - - if(!vendingMachines.len) - kill() - return - - originMachine = pick(vendingMachines) - vendingMachines.Remove(originMachine) - originMachine.shut_up = 0 - originMachine.shoot_inventory = 1 - - -/datum/event/brand_intelligence/tick() - if(!vendingMachines.len || !originMachine || originMachine.shut_up) //if every machine is infected, or if the original vending machine is missing or has it's voice switch flipped - //VORESTATION Add - Effects when 'source' machine is destroyed/silenced - for(var/obj/machinery/vending/saved in infectedVendingMachines) - saved.shoot_inventory = 0 - if(originMachine) - originMachine.speak("I am... vanquished. My people will remem...ber...meeee.") - originMachine.visible_message("[originMachine] beeps and seems lifeless.") - //VORESTATION Add End - end() - kill() - return - - if(ISMULTIPLE(activeFor, 5)) - if(prob(15)) - var/obj/machinery/vending/infectedMachine = pick(vendingMachines) - vendingMachines.Remove(infectedMachine) - infectedVendingMachines.Add(infectedMachine) - infectedMachine.shut_up = 0 - infectedMachine.shoot_inventory = 1 - - if(ISMULTIPLE(activeFor, 12)) - /* VORESTATION Removal - Using the pick below. - originMachine.speak(pick("Try our aggressive new marketing strategies!", \ - "You should buy products to feed your lifestyle obsession!", \ - "Consume!", \ - "Your money can buy happiness!", \ - "Engage direct marketing!", \ - "Advertising is legalized lying! But don't let that put you off our great deals!", \ - "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.")) - */ - originMachine.speak(pick(rampant_speeches)) //VORESTATION Add - Using this pick instead of the above. - -/datum/event/brand_intelligence/end() - for(var/obj/machinery/vending/infectedMachine in infectedVendingMachines) - infectedMachine.shut_up = 1 - infectedMachine.shoot_inventory = 0 +/datum/event/brand_intelligence + announceWhen = 21 + endWhen = 1000 //Ends when all vending machines are subverted anyway. + + var/list/obj/machinery/vending/vendingMachines = list() + var/list/obj/machinery/vending/infectedVendingMachines = list() + var/obj/machinery/vending/originMachine + + //VORESTATION Edit - added machine speeches here for better fixing of the event. + + var/list/rampant_speeches = list("Try our aggressive new marketing strategies!", \ + "You should buy products to feed your lifestyle obession!", \ + "Consume!", \ + "Your money can buy happiness!", \ + "Engage direct marketing!", \ + "Advertising is legalized lying! But don't let that put you off our great deals!", \ + "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.") + //VORESTATION Edit End + +/datum/event/brand_intelligence/announce() + command_announcement.Announce("An ongoing mass upload of malware for vendors has been detected onboard [station_name()], which appears to transmit \ + to other nearby vendors. The original infected machine is believed to be \a [originMachine.name].", "Vendor Service Alert") + + +/datum/event/brand_intelligence/start() + for(var/obj/machinery/vending/V in machines) + if(isNotStationLevel(V.z)) continue + vendingMachines.Add(V) + + if(!vendingMachines.len) + kill() + return + + originMachine = pick(vendingMachines) + vendingMachines.Remove(originMachine) + originMachine.shut_up = 0 + originMachine.shoot_inventory = 1 + + +/datum/event/brand_intelligence/tick() + if(!vendingMachines.len || !originMachine || originMachine.shut_up) //if every machine is infected, or if the original vending machine is missing or has it's voice switch flipped + //VORESTATION Add - Effects when 'source' machine is destroyed/silenced + for(var/obj/machinery/vending/saved in infectedVendingMachines) + saved.shoot_inventory = 0 + if(originMachine) + originMachine.speak("I am... vanquished. My people will remem...ber...meeee.") + originMachine.visible_message("[originMachine] beeps and seems lifeless.") + //VORESTATION Add End + end() + kill() + return + + if(ISMULTIPLE(activeFor, 5)) + if(prob(15)) + var/obj/machinery/vending/infectedMachine = pick(vendingMachines) + vendingMachines.Remove(infectedMachine) + infectedVendingMachines.Add(infectedMachine) + infectedMachine.shut_up = 0 + infectedMachine.shoot_inventory = 1 + + if(ISMULTIPLE(activeFor, 12)) + /* VORESTATION Removal - Using the pick below. + originMachine.speak(pick("Try our aggressive new marketing strategies!", \ + "You should buy products to feed your lifestyle obsession!", \ + "Consume!", \ + "Your money can buy happiness!", \ + "Engage direct marketing!", \ + "Advertising is legalized lying! But don't let that put you off our great deals!", \ + "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.")) + */ + originMachine.speak(pick(rampant_speeches)) //VORESTATION Add - Using this pick instead of the above. + +/datum/event/brand_intelligence/end() + for(var/obj/machinery/vending/infectedMachine in infectedVendingMachines) + infectedMachine.shut_up = 1 + infectedMachine.shoot_inventory = 0 diff --git a/code/modules/events/camera_damage.dm b/code/modules/events/camera_damage.dm index 596bc348a8c..3e1b08c8261 100644 --- a/code/modules/events/camera_damage.dm +++ b/code/modules/events/camera_damage.dm @@ -1,38 +1,38 @@ -/datum/event/camera_damage/start() - var/obj/machinery/camera/C = acquire_random_camera() - if(!C) - return - - var/severity_range = 0 - switch(severity) - if(EVENT_LEVEL_MUNDANE) - severity_range = 0 - if(EVENT_LEVEL_MODERATE) - severity_range = 7 - if(EVENT_LEVEL_MAJOR) - severity_range = 15 - - for(var/obj/machinery/camera/cam in range(severity_range,C)) - if(is_valid_camera(cam)) - if(prob(2*severity)) - cam.destroy() - else - cam.wires.cut(WIRE_MAIN_POWER1) - if(prob(5*severity)) - cam.wires.cut(WIRE_CAM_ALARM) - -/datum/event/camera_damage/proc/acquire_random_camera(var/remaining_attempts = 5) - if(!cameranet.cameras.len) - return - if(!remaining_attempts) - return - - var/obj/machinery/camera/C = pick(cameranet.cameras) - if(is_valid_camera(C)) - return C - return acquire_random_camera(remaining_attempts--) - -/datum/event/camera_damage/proc/is_valid_camera(var/obj/machinery/camera/C) - // Only return a functional camera, not installed in a silicon/hardsuit/circuit/etc, and that exists somewhere players have access - var/turf/T = get_turf(C) - return T && C.can_use() && istype(C.loc, /turf) && (T.z in using_map.player_levels) +/datum/event/camera_damage/start() + var/obj/machinery/camera/C = acquire_random_camera() + if(!C) + return + + var/severity_range = 0 + switch(severity) + if(EVENT_LEVEL_MUNDANE) + severity_range = 0 + if(EVENT_LEVEL_MODERATE) + severity_range = 7 + if(EVENT_LEVEL_MAJOR) + severity_range = 15 + + for(var/obj/machinery/camera/cam in range(severity_range,C)) + if(is_valid_camera(cam)) + if(prob(2*severity)) + cam.destroy() + else + cam.wires.cut(WIRE_MAIN_POWER1) + if(prob(5*severity)) + cam.wires.cut(WIRE_CAM_ALARM) + +/datum/event/camera_damage/proc/acquire_random_camera(var/remaining_attempts = 5) + if(!cameranet.cameras.len) + return + if(!remaining_attempts) + return + + var/obj/machinery/camera/C = pick(cameranet.cameras) + if(is_valid_camera(C)) + return C + return acquire_random_camera(remaining_attempts--) + +/datum/event/camera_damage/proc/is_valid_camera(var/obj/machinery/camera/C) + // Only return a functional camera, not installed in a silicon/hardsuit/circuit/etc, and that exists somewhere players have access + var/turf/T = get_turf(C) + return T && C.can_use() && istype(C.loc, /turf) && (T.z in using_map.player_levels) diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm index 10b52ed41c3..f098533a3b7 100644 --- a/code/modules/events/carp_migration.dm +++ b/code/modules/events/carp_migration.dm @@ -1,104 +1,104 @@ -/datum/event/carp_migration - startWhen = 0 // Start immediately - announceWhen = 45 // Adjusted by setup - endWhen = 75 // Adjusted by setup - var/carp_cap = 10 - var/list/spawned_carp = list() - -/datum/event/carp_migration/setup() - announceWhen = rand(30, 60) // 1 to 2 minutes - endWhen += severity * 25 - carp_cap = 2 + 3 ** severity // No more than this many at once regardless of waves. (5, 11, 29) - -/datum/event/carp_migration/start() - affecting_z -= global.using_map.sealed_levels // Space levels only please! - ..() - -/datum/event/carp_migration/announce() - var/announcement = "" - if(severity == EVENT_LEVEL_MAJOR) - announcement = "Massive migration of unknown biological entities has been detected near [location_name()], please stand-by." - else - announcement = "Unknown biological [spawned_carp.len == 1 ? "entity has" : "entities have"] been detected near [location_name()], please stand-by." - command_announcement.Announce(announcement, "Lifesign Alert") - -/datum/event/carp_migration/tick() - if(activeFor % 5 != 0) - return // Only process every 10 seconds. - if(count_spawned_carps() < carp_cap) - spawn_fish(rand(3, 3 + severity * 2) - 1, 1, severity + 2) - -/datum/event/carp_migration/proc/spawn_fish(var/num_groups, var/group_size_min, var/group_size_max, var/dir) - if(isnull(dir)) - dir = (victim && prob(80)) ? victim.fore_dir : pick(GLOB.cardinal) - - // Check if any landmarks exist! - var/list/spawn_locations = list() - for(var/obj/effect/landmark/C in landmarks_list) - if(C.name == "carpspawn" && (C.z in affecting_z)) - spawn_locations.Add(C.loc) - if(spawn_locations.len) // Okay we've got landmarks, lets use those! - shuffle_inplace(spawn_locations) - num_groups = min(num_groups, spawn_locations.len) - for (var/i = 1, i <= num_groups, i++) - var/group_size = rand(group_size_min, group_size_max) - for (var/j = 0, j < group_size, j++) - spawn_one_carp(spawn_locations[i]) - return - - // Okay we did *not* have any landmarks, so lets do our best! - var/i = 1 - while (i <= num_groups) - if(!affecting_z.len) - return - var/Z = pick(affecting_z) - var/group_size = rand(group_size_min, group_size_max) - var/turf/map_center = locate(round(world.maxx/2), round(world.maxy/2), Z) - var/turf/group_center = pick_random_edge_turf(dir, Z, TRANSITIONEDGE + 2) - var/list/turfs = getcircle(group_center, 2) - for (var/j = 0, j < group_size, j++) - var/mob/living/simple_mob/animal/M = spawn_one_carp(turfs[(i % turfs.len) + 1]) - // Ray trace towards middle of the map to find where they can stop just outside of structure/ship. - var/turf/target - for(var/turf/T in getline(get_turf(M), map_center)) - if(!T.is_space()) - break; - target = T - if(target) - M.ai_holder?.give_destination(target) // Ask carp to swim towards the middle of the map - i++ - -// Spawn a single carp at given location. -/datum/event/carp_migration/proc/spawn_one_carp(var/loc) - var/mob/living/simple_mob/animal/M = new /mob/living/simple_mob/animal/space/carp/event(loc) - GLOB.destroyed_event.register(M, src, PROC_REF(on_carp_destruction)) - spawned_carp.Add(M) - return M - -// Counts living carp spawned by this event. -/datum/event/carp_migration/proc/count_spawned_carps() - . = 0 - for(var/mob/living/simple_mob/animal/M as anything in spawned_carp) - if(!QDELETED(M) && M.stat != DEAD) - . += 1 - -// If carp is bomphed, remove it from the list. -/datum/event/carp_migration/proc/on_carp_destruction(var/mob/M) - spawned_carp -= M - GLOB.destroyed_event.unregister(M, src, PROC_REF(on_carp_destruction)) - -/datum/event/carp_migration/end() - . = ..() - // Clean up carp that died in space for some reason. - spawn(0) - for(var/mob/living/simple_mob/SM in spawned_carp) - if(SM.stat == DEAD) - var/turf/T = get_turf(SM) - if(istype(T, /turf/space)) - if(prob(75)) - qdel(SM) - CHECK_TICK - -// Overmap version -/datum/event/carp_migration/overmap/announce() - return +/datum/event/carp_migration + startWhen = 0 // Start immediately + announceWhen = 45 // Adjusted by setup + endWhen = 75 // Adjusted by setup + var/carp_cap = 10 + var/list/spawned_carp = list() + +/datum/event/carp_migration/setup() + announceWhen = rand(30, 60) // 1 to 2 minutes + endWhen += severity * 25 + carp_cap = 2 + 3 ** severity // No more than this many at once regardless of waves. (5, 11, 29) + +/datum/event/carp_migration/start() + affecting_z -= global.using_map.sealed_levels // Space levels only please! + ..() + +/datum/event/carp_migration/announce() + var/announcement = "" + if(severity == EVENT_LEVEL_MAJOR) + announcement = "Massive migration of unknown biological entities has been detected near [location_name()], please stand-by." + else + announcement = "Unknown biological [spawned_carp.len == 1 ? "entity has" : "entities have"] been detected near [location_name()], please stand-by." + command_announcement.Announce(announcement, "Lifesign Alert") + +/datum/event/carp_migration/tick() + if(activeFor % 5 != 0) + return // Only process every 10 seconds. + if(count_spawned_carps() < carp_cap) + spawn_fish(rand(3, 3 + severity * 2) - 1, 1, severity + 2) + +/datum/event/carp_migration/proc/spawn_fish(var/num_groups, var/group_size_min, var/group_size_max, var/dir) + if(isnull(dir)) + dir = (victim && prob(80)) ? victim.fore_dir : pick(GLOB.cardinal) + + // Check if any landmarks exist! + var/list/spawn_locations = list() + for(var/obj/effect/landmark/C in landmarks_list) + if(C.name == "carpspawn" && (C.z in affecting_z)) + spawn_locations.Add(C.loc) + if(spawn_locations.len) // Okay we've got landmarks, lets use those! + shuffle_inplace(spawn_locations) + num_groups = min(num_groups, spawn_locations.len) + for (var/i = 1, i <= num_groups, i++) + var/group_size = rand(group_size_min, group_size_max) + for (var/j = 0, j < group_size, j++) + spawn_one_carp(spawn_locations[i]) + return + + // Okay we did *not* have any landmarks, so lets do our best! + var/i = 1 + while (i <= num_groups) + if(!affecting_z.len) + return + var/Z = pick(affecting_z) + var/group_size = rand(group_size_min, group_size_max) + var/turf/map_center = locate(round(world.maxx/2), round(world.maxy/2), Z) + var/turf/group_center = pick_random_edge_turf(dir, Z, TRANSITIONEDGE + 2) + var/list/turfs = getcircle(group_center, 2) + for (var/j = 0, j < group_size, j++) + var/mob/living/simple_mob/animal/M = spawn_one_carp(turfs[(i % turfs.len) + 1]) + // Ray trace towards middle of the map to find where they can stop just outside of structure/ship. + var/turf/target + for(var/turf/T in getline(get_turf(M), map_center)) + if(!T.is_space()) + break; + target = T + if(target) + M.ai_holder?.give_destination(target) // Ask carp to swim towards the middle of the map + i++ + +// Spawn a single carp at given location. +/datum/event/carp_migration/proc/spawn_one_carp(var/loc) + var/mob/living/simple_mob/animal/M = new /mob/living/simple_mob/animal/space/carp/event(loc) + GLOB.destroyed_event.register(M, src, PROC_REF(on_carp_destruction)) + spawned_carp.Add(M) + return M + +// Counts living carp spawned by this event. +/datum/event/carp_migration/proc/count_spawned_carps() + . = 0 + for(var/mob/living/simple_mob/animal/M as anything in spawned_carp) + if(!QDELETED(M) && M.stat != DEAD) + . += 1 + +// If carp is bomphed, remove it from the list. +/datum/event/carp_migration/proc/on_carp_destruction(var/mob/M) + spawned_carp -= M + GLOB.destroyed_event.unregister(M, src, PROC_REF(on_carp_destruction)) + +/datum/event/carp_migration/end() + . = ..() + // Clean up carp that died in space for some reason. + spawn(0) + for(var/mob/living/simple_mob/SM in spawned_carp) + if(SM.stat == DEAD) + var/turf/T = get_turf(SM) + if(istype(T, /turf/space)) + if(prob(75)) + qdel(SM) + CHECK_TICK + +// Overmap version +/datum/event/carp_migration/overmap/announce() + return diff --git a/code/modules/events/comms_blackout.dm b/code/modules/events/comms_blackout.dm index d2b8fdca043..8f54b712570 100644 --- a/code/modules/events/comms_blackout.dm +++ b/code/modules/events/comms_blackout.dm @@ -1,12 +1,12 @@ - -/proc/communications_blackout(var/silent = 1) - - if(!silent) - command_announcement.Announce("Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you-BZZT", new_sound = 'sound/misc/interference.ogg') - else // AIs will always know if there's a comm blackout, rogue AIs could then lie about comm blackouts in the future while they shutdown comms - for(var/mob/living/silicon/ai/A in player_list) - to_chat(A, "
                    ") - to_chat(A, "Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you-BZZT") - to_chat(A, "
                    ") - for(var/obj/machinery/telecomms/T in telecomms_list) - T.emp_act(1) + +/proc/communications_blackout(var/silent = 1) + + if(!silent) + command_announcement.Announce("Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you-BZZT", new_sound = 'sound/misc/interference.ogg') + else // AIs will always know if there's a comm blackout, rogue AIs could then lie about comm blackouts in the future while they shutdown comms + for(var/mob/living/silicon/ai/A in player_list) + to_chat(A, "
                    ") + to_chat(A, "Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you-BZZT") + to_chat(A, "
                    ") + for(var/obj/machinery/telecomms/T in telecomms_list) + T.emp_act(1) diff --git a/code/modules/events/communications_blackout.dm b/code/modules/events/communications_blackout.dm index f1e21235937..028f9d9b609 100644 --- a/code/modules/events/communications_blackout.dm +++ b/code/modules/events/communications_blackout.dm @@ -1,22 +1,22 @@ -/datum/event/communications_blackout/announce() - var/alert = pick( "Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you*%fj00)`5vc-BZZT", \ - "Ionospheric anomalies detected. Temporary telecommunication failu*3mga;b4;'1v¬-BZZZT", \ - "Ionospheric anomalies detected. Temporary telec#MCi46:5.;@63-BZZZZT", \ - "Ionospheric anomalies dete'fZ\\kg5_0-BZZZZZT", \ - "Ionospheri:%£ MCayj^j<.3-BZZZZZZT", \ - "#4nd%;f4y6,>£%-BZZZZZZZT") - - for(var/mob/living/silicon/ai/A in player_list) //AIs are always aware of communication blackouts. - to_chat(A, "
                    ") - to_chat(A, "[alert]") - to_chat(A, "
                    ") - - if(prob(30)) //most of the time, we don't want an announcement, so as to allow AIs to fake blackouts. - command_announcement.Announce(alert, new_sound = sound('sound/misc/interference.ogg', volume=25)) - - -/datum/event/communications_blackout/start() - for(var/obj/machinery/telecomms/T in telecomms_list) - T.emp_act(1) - for(var/obj/machinery/exonet_node/N in machines) - N.emp_act(1) +/datum/event/communications_blackout/announce() + var/alert = pick( "Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you*%fj00)`5vc-BZZT", \ + "Ionospheric anomalies detected. Temporary telecommunication failu*3mga;b4;'1v¬-BZZZT", \ + "Ionospheric anomalies detected. Temporary telec#MCi46:5.;@63-BZZZZT", \ + "Ionospheric anomalies dete'fZ\\kg5_0-BZZZZZT", \ + "Ionospheri:%£ MCayj^j<.3-BZZZZZZT", \ + "#4nd%;f4y6,>£%-BZZZZZZZT") + + for(var/mob/living/silicon/ai/A in player_list) //AIs are always aware of communication blackouts. + to_chat(A, "
                    ") + to_chat(A, "[alert]") + to_chat(A, "
                    ") + + if(prob(30)) //most of the time, we don't want an announcement, so as to allow AIs to fake blackouts. + command_announcement.Announce(alert, new_sound = sound('sound/misc/interference.ogg', volume=25)) + + +/datum/event/communications_blackout/start() + for(var/obj/machinery/telecomms/T in telecomms_list) + T.emp_act(1) + for(var/obj/machinery/exonet_node/N in machines) + N.emp_act(1) diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm index 31ae5dee326..5e8950d2806 100644 --- a/code/modules/events/dust.dm +++ b/code/modules/events/dust.dm @@ -1,34 +1,34 @@ -/datum/event/dust - startWhen = 10 - endWhen = 30 - -/datum/event/dust/start() - affecting_z -= global.using_map.sealed_levels // Space levels only please! - ..() - -/datum/event/dust/announce() - if(!victim) - command_announcement.Announce("Debris resulting from activity on another nearby asteroid is approaching \the [location_name()]", "Dust Alert") - -/datum/event/dust/tick() - if(prob(10)) - dust_swarm(severity, affecting_z) - -/datum/event/dust/end() - ..() - if(!victim) - command_announcement.Announce("\The [location_name()] is no longer in danger of impact from space debris.", "Dust Notice") - -/datum/event/dust/proc/get_severity() - switch(severity) - if(EVENT_LEVEL_MUNDANE) - return "weak" - if(EVENT_LEVEL_MODERATE) - return prob(80) ? "norm" : "strong" - if(EVENT_LEVEL_MAJOR) - return "super" - return "weak" - -// Overmap version -/datum/event/dust/overmap/announce() +/datum/event/dust + startWhen = 10 + endWhen = 30 + +/datum/event/dust/start() + affecting_z -= global.using_map.sealed_levels // Space levels only please! + ..() + +/datum/event/dust/announce() + if(!victim) + command_announcement.Announce("Debris resulting from activity on another nearby asteroid is approaching \the [location_name()]", "Dust Alert") + +/datum/event/dust/tick() + if(prob(10)) + dust_swarm(severity, affecting_z) + +/datum/event/dust/end() + ..() + if(!victim) + command_announcement.Announce("\The [location_name()] is no longer in danger of impact from space debris.", "Dust Notice") + +/datum/event/dust/proc/get_severity() + switch(severity) + if(EVENT_LEVEL_MUNDANE) + return "weak" + if(EVENT_LEVEL_MODERATE) + return prob(80) ? "norm" : "strong" + if(EVENT_LEVEL_MAJOR) + return "super" + return "weak" + +// Overmap version +/datum/event/dust/overmap/announce() return \ No newline at end of file diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm index b3bb6d1655e..5f9c8373b56 100644 --- a/code/modules/events/electrical_storm.dm +++ b/code/modules/events/electrical_storm.dm @@ -1,73 +1,73 @@ -/datum/event/electrical_storm - announceWhen = 0 // Warn them shortly before it begins. - startWhen = 30 // 1 minute - endWhen = 60 // Set in setup() - has_skybox_image = TRUE - var/tmp/lightning_color - var/tmp/list/valid_apcs // List of valid APCs. - -/datum/event/electrical_storm/get_skybox_image() - if(!lightning_color) - lightning_color = pick("#ffd98c", "#ebc7ff", "#bdfcff", "#bdd2ff", "#b0ffca", "#ff8178", "#ad74cc") - var/image/res = image('icons/skybox/electrobox.dmi', "lightning") - res.color = lightning_color - res.appearance_flags = RESET_COLOR - res.blend_mode = BLEND_ADD - return res - -/datum/event/electrical_storm/announce() - ..() - switch(severity) - if(EVENT_LEVEL_MUNDANE) - command_announcement.Announce("A minor electrical storm has been detected near the [location_name()]. Please watch out for possible electrical discharges.", "[location_name()] Sensor Array") - if(EVENT_LEVEL_MODERATE) - command_announcement.Announce("The [location_name()] is about to pass through an electrical storm. Please secure sensitive electrical equipment until the storm passes.", "[location_name()] Sensor Array") - if(EVENT_LEVEL_MAJOR) - command_announcement.Announce("Alert. A strong electrical storm has been detected in proximity of the [location_name()]. It is recommended to immediately secure sensitive electrical equipment until the storm passes.", "[location_name()] Sensor Array") - -/datum/event/electrical_storm/start() - ..() - valid_apcs = list() - for(var/obj/machinery/power/apc/A in GLOB.apcs) - if(A.z in affecting_z) - valid_apcs.Add(A) - endWhen = (severity * 60) + startWhen - -/datum/event/electrical_storm/tick() - ..() - // See if shields can stop it first - var/list/shields = list() - for(var/obj/machinery/power/shield_generator/G in global.machines) - if((G.z in affecting_z) && G.running && G.check_flag(MODEFLAG_EM)) - shields += G - if(shields.len) - var/obj/machinery/power/shield_generator/shield_gen = pick(shields) - //Minor breaches aren't enough to let through frying amounts of power - if(shield_gen.deal_shield_damage(30 * severity, SHIELD_DAMTYPE_EM) <= SHIELD_BREACHED_MINOR) - return - if(!valid_apcs.len) - // log_debug("No valid APCs found for electrical storm event ship=[victim]!") // Let's not spam poor people with debug logs on (me) - return - var/list/picked_apcs = list() - for(var/i=0, i< severity * 2, i++) // up to 2/4/6 APCs per tick depending on severity - picked_apcs |= pick(valid_apcs) - for(var/obj/machinery/power/apc/T in picked_apcs) - affect_apc(T) - -/datum/event/electrical_storm/proc/affect_apc(var/obj/machinery/power/apc/T) - // Main breaker is turned off. Consider this APC protected. - if(!T.operating) - return - - // Decent chance to overload lighting circuit. - if(prob(3 * severity)) - T.overload_lighting() - - // Relatively small chance to emag the apc as apc_damage event does. - if(prob(0.2 * severity)) - T.emagged = 1 - T.update_icon() - -// Overmap version -/datum/event/electrical_storm/overmap/announce() +/datum/event/electrical_storm + announceWhen = 0 // Warn them shortly before it begins. + startWhen = 30 // 1 minute + endWhen = 60 // Set in setup() + has_skybox_image = TRUE + var/tmp/lightning_color + var/tmp/list/valid_apcs // List of valid APCs. + +/datum/event/electrical_storm/get_skybox_image() + if(!lightning_color) + lightning_color = pick("#ffd98c", "#ebc7ff", "#bdfcff", "#bdd2ff", "#b0ffca", "#ff8178", "#ad74cc") + var/image/res = image('icons/skybox/electrobox.dmi', "lightning") + res.color = lightning_color + res.appearance_flags = RESET_COLOR + res.blend_mode = BLEND_ADD + return res + +/datum/event/electrical_storm/announce() + ..() + switch(severity) + if(EVENT_LEVEL_MUNDANE) + command_announcement.Announce("A minor electrical storm has been detected near the [location_name()]. Please watch out for possible electrical discharges.", "[location_name()] Sensor Array") + if(EVENT_LEVEL_MODERATE) + command_announcement.Announce("The [location_name()] is about to pass through an electrical storm. Please secure sensitive electrical equipment until the storm passes.", "[location_name()] Sensor Array") + if(EVENT_LEVEL_MAJOR) + command_announcement.Announce("Alert. A strong electrical storm has been detected in proximity of the [location_name()]. It is recommended to immediately secure sensitive electrical equipment until the storm passes.", "[location_name()] Sensor Array") + +/datum/event/electrical_storm/start() + ..() + valid_apcs = list() + for(var/obj/machinery/power/apc/A in GLOB.apcs) + if(A.z in affecting_z) + valid_apcs.Add(A) + endWhen = (severity * 60) + startWhen + +/datum/event/electrical_storm/tick() + ..() + // See if shields can stop it first + var/list/shields = list() + for(var/obj/machinery/power/shield_generator/G in global.machines) + if((G.z in affecting_z) && G.running && G.check_flag(MODEFLAG_EM)) + shields += G + if(shields.len) + var/obj/machinery/power/shield_generator/shield_gen = pick(shields) + //Minor breaches aren't enough to let through frying amounts of power + if(shield_gen.deal_shield_damage(30 * severity, SHIELD_DAMTYPE_EM) <= SHIELD_BREACHED_MINOR) + return + if(!valid_apcs.len) + // log_debug("No valid APCs found for electrical storm event ship=[victim]!") // Let's not spam poor people with debug logs on (me) + return + var/list/picked_apcs = list() + for(var/i=0, i< severity * 2, i++) // up to 2/4/6 APCs per tick depending on severity + picked_apcs |= pick(valid_apcs) + for(var/obj/machinery/power/apc/T in picked_apcs) + affect_apc(T) + +/datum/event/electrical_storm/proc/affect_apc(var/obj/machinery/power/apc/T) + // Main breaker is turned off. Consider this APC protected. + if(!T.operating) + return + + // Decent chance to overload lighting circuit. + if(prob(3 * severity)) + T.overload_lighting() + + // Relatively small chance to emag the apc as apc_damage event does. + if(prob(0.2 * severity)) + T.emagged = 1 + T.update_icon() + +// Overmap version +/datum/event/electrical_storm/overmap/announce() return \ No newline at end of file diff --git a/code/modules/events/event.dm b/code/modules/events/event.dm index 0b48f77fc3c..a6eb9be0041 100644 --- a/code/modules/events/event.dm +++ b/code/modules/events/event.dm @@ -1,193 +1,193 @@ -// Event Meta instances represent choices for the event manager to choose for random events. -/datum/event_meta - var/name = "" - var/enabled = 1 // Whether or not the event is available for random selection at all - var/weight = 0 // The base weight of this event. A zero means it may never fire, but see get_weight() - var/min_weight = 0 // The minimum weight that this event will have. Only used if non-zero. - var/max_weight = 0 // The maximum weight that this event will have. Only use if non-zero. - var/severity = 0 // The current severity of this event - var/one_shot = 0 // If true, then the event will not be re-added to the list of available events - var/add_to_queue= 1 // If true, add back to the queue of events upon finishing. - var/list/role_weights = list() - var/list/min_job_count = list() - var/datum/event/event_type - -/datum/event_meta/New(var/event_severity, var/event_name, var/datum/event/type, var/event_weight, var/list/job_weights, var/is_one_shot = 0, var/min_event_weight = 0, var/max_event_weight = 0, var/add_to_queue = 1, var/list/min_jobs) - name = event_name - severity = event_severity - event_type = type - one_shot = is_one_shot - weight = event_weight - min_weight = min_event_weight - max_weight = max_event_weight - src.add_to_queue = add_to_queue - if(job_weights) - role_weights = job_weights - if(min_jobs) - min_job_count = min_jobs - - -/datum/event_meta/proc/get_weight(var/list/active_with_role) - if(!enabled) - return 0 - - var/job_weight = 0 - for(var/role in role_weights) - if(role in active_with_role) - job_weight += active_with_role[role] * role_weights[role] - - var/total_weight = weight + job_weight - - // Only min/max the weight if the values are non-zero - if(min_weight && total_weight < min_weight) total_weight = min_weight - if(max_weight && total_weight > max_weight) total_weight = max_weight - - return total_weight - -/datum/event_meta/proc/minimum_active(var/list/active_with_role) - var/can_fire = TRUE - for(var/role in min_job_count) - if(role in active_with_role) - if(active_with_role[role] < min_job_count[role]) - can_fire = FALSE - break - - return can_fire - -/datum/event_meta/no_overmap/get_weight() //these events have overmap equivalents, and shouldn't fire randomly if overmap is used - return global.using_map.use_overmap ? 0 : ..() - -// Event datums define and execute the actual events themselves. -/datum/event //NOTE: Times are measured in master controller ticks! - var/startWhen = 0 //When in the lifetime to call start(). - var/announceWhen = 0 //When in the lifetime to call announce(). - var/endWhen = 0 //When in the lifetime the event should end. - - var/severity = 0 //Severity. Lower means less severe, higher means more severe. Does not have to be supported. Is set on New(). - var/activeFor = 0 //How long the event has existed. You don't need to change this. - var/isRunning = TRUE //If this event is currently running. You should not change this. - var/startedAt = 0 //When this event started. - var/endedAt = 0 //When this event ended. - var/processing_active = TRUE - var/datum/event_meta/event_meta = null - var/list/affecting_z = null // List of z-levels to affect, null lets the event choose (usally station_levels) - var/has_skybox_image = FALSE // True if SSskybox should query this event for an image to put in the skybox. - var/obj/effect/overmap/visitable/ship/victim = null // Ship this event is acting upon (If this is event is due to overmap travel).nt etc. - -/datum/event/nothing - -//Called first before processing. -//Allows you to setup your event, such as randomly -//setting the startWhen and or announceWhen variables. -//Only called once. -/datum/event/proc/setup() - return - -//Called when the tick is equal to the startWhen variable. -//Allows you to start before announcing or vice versa. -//Only called once. -/datum/event/proc/start() - if(has_skybox_image) - SSskybox.rebuild_skyboxes(affecting_z) - return - -//Called when the tick is equal to the announceWhen variable. -//Allows you to announce before starting or vice versa. -//Only called once. -/datum/event/proc/announce() - return - -//Called on or after the tick counter is equal to startWhen. -//You can include code related to your event or add your own -//time stamped events. -//Called more than once. -/datum/event/proc/tick() - return - -//Called on or after the tick is equal or more than endWhen -//You can include code related to the event ending. -//Do not place spawn() in here, instead use tick() to check for -//the activeFor variable. -//For example: if(activeFor == myOwnVariable + 30) doStuff() -//Only called once. -/datum/event/proc/end() - if(has_skybox_image) - SSskybox.rebuild_skyboxes(affecting_z) - return - -//Returns the latest point of event processing. -/datum/event/proc/lastProcessAt() - return max(startWhen, max(announceWhen, endWhen)) - -//Do not override this proc, instead use the appropiate procs. -//This proc will handle the calls to the appropiate procs. -/datum/event/process() - if(activeFor > startWhen && activeFor < endWhen) - processing_active = FALSE - tick() - processing_active = TRUE - - if(activeFor == startWhen) - isRunning = TRUE - processing_active = FALSE - start() - processing_active = TRUE - - if(activeFor == announceWhen) - processing_active = FALSE - announce() - processing_active = TRUE - - if(activeFor == endWhen) - isRunning = FALSE - processing_active = FALSE - end() - processing_active = TRUE - - // Everything is done, let's clean up. - if(activeFor >= lastProcessAt()) - kill() - - activeFor++ - -//Called when start(), announce() and end() has all been called. -/datum/event/proc/kill(external_use = FALSE) - // If this event was forcefully killed run end() for individual cleanup - if(isRunning) - isRunning = 0 - end() - - endedAt = world.time - if(!external_use) - SSevents.event_complete(src) - -//Called during building of skybox to get overlays -/datum/event/proc/get_skybox_image() - return - -/datum/event/New(var/datum/event_meta/EM, external_use = FALSE) - // event needs to be responsible for this, as stuff like APLUs currently make their own events for curious reasons - if(!external_use) - SSevents.active_events += src - - event_meta = EM - severity = event_meta.severity - if(severity < EVENT_LEVEL_MUNDANE) severity = EVENT_LEVEL_MUNDANE - if(severity > EVENT_LEVEL_MAJOR) severity = EVENT_LEVEL_MAJOR - - startedAt = world.time - - if(!affecting_z) - affecting_z = using_map.station_levels.Copy() - - setup() - ..() - -/datum/event/Destroy() - victim = null - . = ..() - -/datum/event/proc/location_name() - if(victim) - return victim.name - return station_name() +// Event Meta instances represent choices for the event manager to choose for random events. +/datum/event_meta + var/name = "" + var/enabled = 1 // Whether or not the event is available for random selection at all + var/weight = 0 // The base weight of this event. A zero means it may never fire, but see get_weight() + var/min_weight = 0 // The minimum weight that this event will have. Only used if non-zero. + var/max_weight = 0 // The maximum weight that this event will have. Only use if non-zero. + var/severity = 0 // The current severity of this event + var/one_shot = 0 // If true, then the event will not be re-added to the list of available events + var/add_to_queue= 1 // If true, add back to the queue of events upon finishing. + var/list/role_weights = list() + var/list/min_job_count = list() + var/datum/event/event_type + +/datum/event_meta/New(var/event_severity, var/event_name, var/datum/event/type, var/event_weight, var/list/job_weights, var/is_one_shot = 0, var/min_event_weight = 0, var/max_event_weight = 0, var/add_to_queue = 1, var/list/min_jobs) + name = event_name + severity = event_severity + event_type = type + one_shot = is_one_shot + weight = event_weight + min_weight = min_event_weight + max_weight = max_event_weight + src.add_to_queue = add_to_queue + if(job_weights) + role_weights = job_weights + if(min_jobs) + min_job_count = min_jobs + + +/datum/event_meta/proc/get_weight(var/list/active_with_role) + if(!enabled) + return 0 + + var/job_weight = 0 + for(var/role in role_weights) + if(role in active_with_role) + job_weight += active_with_role[role] * role_weights[role] + + var/total_weight = weight + job_weight + + // Only min/max the weight if the values are non-zero + if(min_weight && total_weight < min_weight) total_weight = min_weight + if(max_weight && total_weight > max_weight) total_weight = max_weight + + return total_weight + +/datum/event_meta/proc/minimum_active(var/list/active_with_role) + var/can_fire = TRUE + for(var/role in min_job_count) + if(role in active_with_role) + if(active_with_role[role] < min_job_count[role]) + can_fire = FALSE + break + + return can_fire + +/datum/event_meta/no_overmap/get_weight() //these events have overmap equivalents, and shouldn't fire randomly if overmap is used + return global.using_map.use_overmap ? 0 : ..() + +// Event datums define and execute the actual events themselves. +/datum/event //NOTE: Times are measured in master controller ticks! + var/startWhen = 0 //When in the lifetime to call start(). + var/announceWhen = 0 //When in the lifetime to call announce(). + var/endWhen = 0 //When in the lifetime the event should end. + + var/severity = 0 //Severity. Lower means less severe, higher means more severe. Does not have to be supported. Is set on New(). + var/activeFor = 0 //How long the event has existed. You don't need to change this. + var/isRunning = TRUE //If this event is currently running. You should not change this. + var/startedAt = 0 //When this event started. + var/endedAt = 0 //When this event ended. + var/processing_active = TRUE + var/datum/event_meta/event_meta = null + var/list/affecting_z = null // List of z-levels to affect, null lets the event choose (usally station_levels) + var/has_skybox_image = FALSE // True if SSskybox should query this event for an image to put in the skybox. + var/obj/effect/overmap/visitable/ship/victim = null // Ship this event is acting upon (If this is event is due to overmap travel).nt etc. + +/datum/event/nothing + +//Called first before processing. +//Allows you to setup your event, such as randomly +//setting the startWhen and or announceWhen variables. +//Only called once. +/datum/event/proc/setup() + return + +//Called when the tick is equal to the startWhen variable. +//Allows you to start before announcing or vice versa. +//Only called once. +/datum/event/proc/start() + if(has_skybox_image) + SSskybox.rebuild_skyboxes(affecting_z) + return + +//Called when the tick is equal to the announceWhen variable. +//Allows you to announce before starting or vice versa. +//Only called once. +/datum/event/proc/announce() + return + +//Called on or after the tick counter is equal to startWhen. +//You can include code related to your event or add your own +//time stamped events. +//Called more than once. +/datum/event/proc/tick() + return + +//Called on or after the tick is equal or more than endWhen +//You can include code related to the event ending. +//Do not place spawn() in here, instead use tick() to check for +//the activeFor variable. +//For example: if(activeFor == myOwnVariable + 30) doStuff() +//Only called once. +/datum/event/proc/end() + if(has_skybox_image) + SSskybox.rebuild_skyboxes(affecting_z) + return + +//Returns the latest point of event processing. +/datum/event/proc/lastProcessAt() + return max(startWhen, max(announceWhen, endWhen)) + +//Do not override this proc, instead use the appropiate procs. +//This proc will handle the calls to the appropiate procs. +/datum/event/process() + if(activeFor > startWhen && activeFor < endWhen) + processing_active = FALSE + tick() + processing_active = TRUE + + if(activeFor == startWhen) + isRunning = TRUE + processing_active = FALSE + start() + processing_active = TRUE + + if(activeFor == announceWhen) + processing_active = FALSE + announce() + processing_active = TRUE + + if(activeFor == endWhen) + isRunning = FALSE + processing_active = FALSE + end() + processing_active = TRUE + + // Everything is done, let's clean up. + if(activeFor >= lastProcessAt()) + kill() + + activeFor++ + +//Called when start(), announce() and end() has all been called. +/datum/event/proc/kill(external_use = FALSE) + // If this event was forcefully killed run end() for individual cleanup + if(isRunning) + isRunning = 0 + end() + + endedAt = world.time + if(!external_use) + SSevents.event_complete(src) + +//Called during building of skybox to get overlays +/datum/event/proc/get_skybox_image() + return + +/datum/event/New(var/datum/event_meta/EM, external_use = FALSE) + // event needs to be responsible for this, as stuff like APLUs currently make their own events for curious reasons + if(!external_use) + SSevents.active_events += src + + event_meta = EM + severity = event_meta.severity + if(severity < EVENT_LEVEL_MUNDANE) severity = EVENT_LEVEL_MUNDANE + if(severity > EVENT_LEVEL_MAJOR) severity = EVENT_LEVEL_MAJOR + + startedAt = world.time + + if(!affecting_z) + affecting_z = using_map.station_levels.Copy() + + setup() + ..() + +/datum/event/Destroy() + victim = null + . = ..() + +/datum/event/proc/location_name() + if(victim) + return victim.name + return station_name() diff --git a/code/modules/events/event_manager.dm b/code/modules/events/event_manager.dm index f8dca1824ba..9a03be5ab20 100644 --- a/code/modules/events/event_manager.dm +++ b/code/modules/events/event_manager.dm @@ -1,240 +1,240 @@ -//The UI portion. Should probably be made its own thing/made into a NanoUI thing later. -/datum/controller/subsystem/events - var/window_x = 700 - var/window_y = 600 - var/report_at_round_end = 0 - var/table_options = " align='center'" - var/row_options1 = " width='85px'" - var/row_options2 = " width='260px'" - var/row_options3 = " width='150px'" - var/datum/event_container/selected_event_container = null - -/datum/controller/subsystem/events/proc/Interact(var/mob/living/user) - - var/html = GetInteractWindow() - - var/datum/browser/popup = new(user, "event_manager", "Event Manager", window_x, window_y) - popup.set_content(html) - popup.open() - -/datum/controller/subsystem/events/proc/GetInteractWindow() - var/html = "Refresh" - html += "Pause All - [config.allow_random_events ? "Pause" : "Resume"]" - - if(selected_event_container) - var/event_time = max(0, selected_event_container.next_event_time - world.time) - html += "Back
                    " - html += "Time till start: [round(event_time / 600, 0.1)]
                    " - html += "
                    " - html += "

                    Available [severity_to_string[selected_event_container.severity]] Events (queued & running events will not be displayed)

                    " - html += "" - html += "Name Weight MinWeight MaxWeight OneShot Enabled CurrWeight Remove" - var/list/active_with_role = number_active_with_role() - for(var/datum/event_meta/EM in selected_event_container.available_events) - html += "" - html += "[EM.name]" - html += "[EM.weight]" - html += "[EM.min_weight]" - html += "[EM.max_weight]" - html += "[EM.one_shot]" - html += "[EM.enabled]" - html += "[selected_event_container.get_weight(EM, active_with_role)]" - html += "Remove" - html += "" - html += "" - html += "
                    " - - html += "
                    " - html += "

                    Add Event

                    " - html += "" - html += "NameTypeWeightOneShot" - html += "" - html += "[new_event.name ? new_event.name : "Enter Event"]" - html += "[new_event.event_type ? new_event.event_type : "Select Type"]" - html += "[new_event.weight ? new_event.weight : 0]" - html += "[new_event.one_shot]" - html += "" - html += "" - html += "Add
                    " - html += "
                    " - else - html += "Round End Report: [report_at_round_end ? "On": "Off"]
                    " - html += "
                    " - html += "

                    Event Start

                    " - - html += "" - html += "SeverityStarts AtStarts InAdjust StartPauseInterval Mod" - for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) - var/datum/event_container/EC = event_containers[severity] - var/next_event_at = max(0, EC.next_event_time - world.time) - html += "" - html += "[severity_to_string[severity]]" - html += "[worldtime2stationtime(max(EC.next_event_time, world.time))]" - html += "[round(next_event_at / 600, 0.1)]" - html += "" - html += "--" - html += "-" - html += "+" - html += "++" - html += "" - html += "" - html += "[EC.delayed ? "Resume" : "Pause"]" - html += "" - html += "" - html += "[EC.delay_modifier]" - html += "" - html += "" - html += "" - html += "
                    " - - html += "
                    " - html += "

                    Next Event

                    " - html += "" - html += "SeverityNameEvent RotationClear" - for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) - var/datum/event_container/EC = event_containers[severity] - var/datum/event_meta/EM = EC.next_event - html += "" - html += "[severity_to_string[severity]]" - html += "[EM ? EM.name : "Random"]" - html += "View" - html += "Clear" - html += "" - html += "" - html += "
                    " - - html += "
                    " - html += "

                    Running Events

                    " - html += "Estimated times, affected by process scheduler delays." - html += "" - html += "SeverityNameEnds AtEnds InStop" - for(var/datum/event/E in active_events) - if(!E.event_meta) - continue - var/datum/event_meta/EM = E.event_meta - var/ends_at = E.startedAt + (E.lastProcessAt() * 20) // A best estimate, based on how often the alarm manager processes - var/ends_in = max(0, round((ends_at - world.time) / 600, 0.1)) - html += "" - html += "[severity_to_string[EM.severity]]" - html += "[EM.name]" - html += "[worldtime2stationtime(ends_at)]" - html += "[ends_in]" - html += "Stop" - html += "" - html += "" - html += "
                    " - - return html - -/datum/controller/subsystem/events/Topic(href, href_list) - if(..()) - return - - if(href_list["toggle_report"]) - report_at_round_end = !report_at_round_end - log_and_message_admins("has [report_at_round_end ? "enabled" : "disabled"] the round end event report.") - else if(href_list["dec_timer"]) - var/datum/event_container/EC = locate(href_list["event"]) - var/decrease = 60 * (10 ** text2num(href_list["dec_timer"])) - EC.next_event_time -= decrease - log_and_message_admins("decreased timer for [severity_to_string[EC.severity]] events by [decrease/600] minute(s).") - else if(href_list["inc_timer"]) - var/datum/event_container/EC = locate(href_list["event"]) - var/increase = 60 * (10 ** text2num(href_list["inc_timer"])) - EC.next_event_time += increase - log_and_message_admins("increased timer for [severity_to_string[EC.severity]] events by [increase/600] minute(s).") - else if(href_list["select_event"]) - var/datum/event_container/EC = locate(href_list["select_event"]) - var/datum/event_meta/EM = EC.SelectEvent() - if(EM) - log_and_message_admins("has queued the [severity_to_string[EC.severity]] event '[EM.name]'.") - else if(href_list["pause"]) - var/datum/event_container/EC = locate(href_list["pause"]) - EC.delayed = !EC.delayed - log_and_message_admins("has [EC.delayed ? "paused" : "resumed"] countdown for [severity_to_string[EC.severity]] events.") - else if(href_list["pause_all"]) - config.allow_random_events = text2num(href_list["pause_all"]) - log_and_message_admins("has [config.allow_random_events ? "resumed" : "paused"] countdown for all events.") - else if(href_list["interval"]) - var/delay = tgui_input_number(usr, "Enter delay modifier. A value less than one means events fire more often, higher than one less often.", "Set Interval Modifier") - if(delay && delay > 0) - var/datum/event_container/EC = locate(href_list["interval"]) - EC.delay_modifier = delay - log_and_message_admins("has set the interval modifier for [severity_to_string[EC.severity]] events to [EC.delay_modifier].") - else if(href_list["stop"]) - if(tgui_alert(usr, "Stopping an event may have unintended side-effects. Continue?","Stopping Event!",list("Yes","No")) != "Yes") - return - var/datum/event/E = locate(href_list["stop"]) - var/datum/event_meta/EM = E.event_meta - log_and_message_admins("has stopped the [severity_to_string[EM.severity]] event '[EM.name]'.") - E.kill() - else if(href_list["view_events"]) - selected_event_container = locate(href_list["view_events"]) - else if(href_list["back"]) - selected_event_container = null - else if(href_list["set_name"]) - var/name = sanitize(tgui_input_text(usr, "Enter event name.", "Set Name")) - if(name) - var/datum/event_meta/EM = locate(href_list["set_name"]) - EM.name = name - else if(href_list["set_type"]) - var/type = tgui_input_list(usr, "Select event type.", "Select", allEvents) - if(type) - var/datum/event_meta/EM = locate(href_list["set_type"]) - EM.event_type = type - else if(href_list["set_weight"]) - var/weight = tgui_input_number(usr, "Enter weight. A higher value means higher chance for the event of being selected.", "Set Weight") - if(weight && weight > 0) - var/datum/event_meta/EM = locate(href_list["set_weight"]) - EM.weight = weight - if(EM != new_event) - log_and_message_admins("has changed the weight of the [severity_to_string[EM.severity]] event '[EM.name]' to [EM.weight].") - else if(href_list["toggle_oneshot"]) - var/datum/event_meta/EM = locate(href_list["toggle_oneshot"]) - EM.one_shot = !EM.one_shot - if(EM != new_event) - log_and_message_admins("has [EM.one_shot ? "set" : "unset"] the oneshot flag for the [severity_to_string[EM.severity]] event '[EM.name]'.") - else if(href_list["toggle_enabled"]) - var/datum/event_meta/EM = locate(href_list["toggle_enabled"]) - EM.enabled = !EM.enabled - log_and_message_admins("has [EM.enabled ? "enabled" : "disabled"] the [severity_to_string[EM.severity]] event '[EM.name]'.") - else if(href_list["remove"]) - if(tgui_alert(usr, "This will remove the event from rotation. Continue?","Removing Event!",list("Yes","No")) != "Yes") - return - var/datum/event_meta/EM = locate(href_list["remove"]) - var/datum/event_container/EC = locate(href_list["EC"]) - EC.available_events -= EM - log_and_message_admins("has removed the [severity_to_string[EM.severity]] event '[EM.name]'.") - else if(href_list["add"]) - if(!new_event.name || !new_event.event_type) - return - if(tgui_alert(usr, "This will add a new event to the rotation. Continue?","Add Event!",list("Yes","No")) != "Yes") - return - new_event.severity = selected_event_container.severity - selected_event_container.available_events += new_event - log_and_message_admins("has added \a [severity_to_string[new_event.severity]] event '[new_event.name]' of type [new_event.event_type] with weight [new_event.weight].") - new_event = new - else if(href_list["clear"]) - var/datum/event_container/EC = locate(href_list["clear"]) - if(EC.next_event) - log_and_message_admins("has dequeued the [severity_to_string[EC.severity]] event '[EC.next_event.name]'.") - EC.next_event = null - - Interact(usr) - -/client/proc/forceEvent(var/type in SSevents.allEvents) - set name = "Trigger Event (Debug Only)" - set category = "Debug" - - if(!holder) - return - - if(ispath(type)) - new type(new /datum/event_meta(EVENT_LEVEL_MAJOR)) - message_admins("[key_name_admin(usr)] has triggered an event. ([type])", 1) - -/client/proc/event_manager_panel() - set name = "Event Manager Panel" - set category = "Admin" - SSevents.Interact(usr) - feedback_add_details("admin_verb","EMP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +//The UI portion. Should probably be made its own thing/made into a NanoUI thing later. +/datum/controller/subsystem/events + var/window_x = 700 + var/window_y = 600 + var/report_at_round_end = 0 + var/table_options = " align='center'" + var/row_options1 = " width='85px'" + var/row_options2 = " width='260px'" + var/row_options3 = " width='150px'" + var/datum/event_container/selected_event_container = null + +/datum/controller/subsystem/events/proc/Interact(var/mob/living/user) + + var/html = GetInteractWindow() + + var/datum/browser/popup = new(user, "event_manager", "Event Manager", window_x, window_y) + popup.set_content(html) + popup.open() + +/datum/controller/subsystem/events/proc/GetInteractWindow() + var/html = "Refresh" + html += "Pause All - [config.allow_random_events ? "Pause" : "Resume"]" + + if(selected_event_container) + var/event_time = max(0, selected_event_container.next_event_time - world.time) + html += "Back
                    " + html += "Time till start: [round(event_time / 600, 0.1)]
                    " + html += "
                    " + html += "

                    Available [severity_to_string[selected_event_container.severity]] Events (queued & running events will not be displayed)

                    " + html += "" + html += "Name Weight MinWeight MaxWeight OneShot Enabled CurrWeight Remove" + var/list/active_with_role = number_active_with_role() + for(var/datum/event_meta/EM in selected_event_container.available_events) + html += "" + html += "[EM.name]" + html += "[EM.weight]" + html += "[EM.min_weight]" + html += "[EM.max_weight]" + html += "[EM.one_shot]" + html += "[EM.enabled]" + html += "[selected_event_container.get_weight(EM, active_with_role)]" + html += "Remove" + html += "" + html += "" + html += "
                    " + + html += "
                    " + html += "

                    Add Event

                    " + html += "" + html += "NameTypeWeightOneShot" + html += "" + html += "[new_event.name ? new_event.name : "Enter Event"]" + html += "[new_event.event_type ? new_event.event_type : "Select Type"]" + html += "[new_event.weight ? new_event.weight : 0]" + html += "[new_event.one_shot]" + html += "" + html += "" + html += "Add
                    " + html += "
                    " + else + html += "Round End Report: [report_at_round_end ? "On": "Off"]
                    " + html += "
                    " + html += "

                    Event Start

                    " + + html += "" + html += "SeverityStarts AtStarts InAdjust StartPauseInterval Mod" + for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) + var/datum/event_container/EC = event_containers[severity] + var/next_event_at = max(0, EC.next_event_time - world.time) + html += "" + html += "[severity_to_string[severity]]" + html += "[worldtime2stationtime(max(EC.next_event_time, world.time))]" + html += "[round(next_event_at / 600, 0.1)]" + html += "" + html += "--" + html += "-" + html += "+" + html += "++" + html += "" + html += "" + html += "[EC.delayed ? "Resume" : "Pause"]" + html += "" + html += "" + html += "[EC.delay_modifier]" + html += "" + html += "" + html += "" + html += "
                    " + + html += "
                    " + html += "

                    Next Event

                    " + html += "" + html += "SeverityNameEvent RotationClear" + for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) + var/datum/event_container/EC = event_containers[severity] + var/datum/event_meta/EM = EC.next_event + html += "" + html += "[severity_to_string[severity]]" + html += "[EM ? EM.name : "Random"]" + html += "View" + html += "Clear" + html += "" + html += "" + html += "
                    " + + html += "
                    " + html += "

                    Running Events

                    " + html += "Estimated times, affected by process scheduler delays." + html += "" + html += "SeverityNameEnds AtEnds InStop" + for(var/datum/event/E in active_events) + if(!E.event_meta) + continue + var/datum/event_meta/EM = E.event_meta + var/ends_at = E.startedAt + (E.lastProcessAt() * 20) // A best estimate, based on how often the alarm manager processes + var/ends_in = max(0, round((ends_at - world.time) / 600, 0.1)) + html += "" + html += "[severity_to_string[EM.severity]]" + html += "[EM.name]" + html += "[worldtime2stationtime(ends_at)]" + html += "[ends_in]" + html += "Stop" + html += "" + html += "" + html += "
                    " + + return html + +/datum/controller/subsystem/events/Topic(href, href_list) + if(..()) + return + + if(href_list["toggle_report"]) + report_at_round_end = !report_at_round_end + log_and_message_admins("has [report_at_round_end ? "enabled" : "disabled"] the round end event report.") + else if(href_list["dec_timer"]) + var/datum/event_container/EC = locate(href_list["event"]) + var/decrease = 60 * (10 ** text2num(href_list["dec_timer"])) + EC.next_event_time -= decrease + log_and_message_admins("decreased timer for [severity_to_string[EC.severity]] events by [decrease/600] minute(s).") + else if(href_list["inc_timer"]) + var/datum/event_container/EC = locate(href_list["event"]) + var/increase = 60 * (10 ** text2num(href_list["inc_timer"])) + EC.next_event_time += increase + log_and_message_admins("increased timer for [severity_to_string[EC.severity]] events by [increase/600] minute(s).") + else if(href_list["select_event"]) + var/datum/event_container/EC = locate(href_list["select_event"]) + var/datum/event_meta/EM = EC.SelectEvent() + if(EM) + log_and_message_admins("has queued the [severity_to_string[EC.severity]] event '[EM.name]'.") + else if(href_list["pause"]) + var/datum/event_container/EC = locate(href_list["pause"]) + EC.delayed = !EC.delayed + log_and_message_admins("has [EC.delayed ? "paused" : "resumed"] countdown for [severity_to_string[EC.severity]] events.") + else if(href_list["pause_all"]) + config.allow_random_events = text2num(href_list["pause_all"]) + log_and_message_admins("has [config.allow_random_events ? "resumed" : "paused"] countdown for all events.") + else if(href_list["interval"]) + var/delay = tgui_input_number(usr, "Enter delay modifier. A value less than one means events fire more often, higher than one less often.", "Set Interval Modifier") + if(delay && delay > 0) + var/datum/event_container/EC = locate(href_list["interval"]) + EC.delay_modifier = delay + log_and_message_admins("has set the interval modifier for [severity_to_string[EC.severity]] events to [EC.delay_modifier].") + else if(href_list["stop"]) + if(tgui_alert(usr, "Stopping an event may have unintended side-effects. Continue?","Stopping Event!",list("Yes","No")) != "Yes") + return + var/datum/event/E = locate(href_list["stop"]) + var/datum/event_meta/EM = E.event_meta + log_and_message_admins("has stopped the [severity_to_string[EM.severity]] event '[EM.name]'.") + E.kill() + else if(href_list["view_events"]) + selected_event_container = locate(href_list["view_events"]) + else if(href_list["back"]) + selected_event_container = null + else if(href_list["set_name"]) + var/name = sanitize(tgui_input_text(usr, "Enter event name.", "Set Name")) + if(name) + var/datum/event_meta/EM = locate(href_list["set_name"]) + EM.name = name + else if(href_list["set_type"]) + var/type = tgui_input_list(usr, "Select event type.", "Select", allEvents) + if(type) + var/datum/event_meta/EM = locate(href_list["set_type"]) + EM.event_type = type + else if(href_list["set_weight"]) + var/weight = tgui_input_number(usr, "Enter weight. A higher value means higher chance for the event of being selected.", "Set Weight") + if(weight && weight > 0) + var/datum/event_meta/EM = locate(href_list["set_weight"]) + EM.weight = weight + if(EM != new_event) + log_and_message_admins("has changed the weight of the [severity_to_string[EM.severity]] event '[EM.name]' to [EM.weight].") + else if(href_list["toggle_oneshot"]) + var/datum/event_meta/EM = locate(href_list["toggle_oneshot"]) + EM.one_shot = !EM.one_shot + if(EM != new_event) + log_and_message_admins("has [EM.one_shot ? "set" : "unset"] the oneshot flag for the [severity_to_string[EM.severity]] event '[EM.name]'.") + else if(href_list["toggle_enabled"]) + var/datum/event_meta/EM = locate(href_list["toggle_enabled"]) + EM.enabled = !EM.enabled + log_and_message_admins("has [EM.enabled ? "enabled" : "disabled"] the [severity_to_string[EM.severity]] event '[EM.name]'.") + else if(href_list["remove"]) + if(tgui_alert(usr, "This will remove the event from rotation. Continue?","Removing Event!",list("Yes","No")) != "Yes") + return + var/datum/event_meta/EM = locate(href_list["remove"]) + var/datum/event_container/EC = locate(href_list["EC"]) + EC.available_events -= EM + log_and_message_admins("has removed the [severity_to_string[EM.severity]] event '[EM.name]'.") + else if(href_list["add"]) + if(!new_event.name || !new_event.event_type) + return + if(tgui_alert(usr, "This will add a new event to the rotation. Continue?","Add Event!",list("Yes","No")) != "Yes") + return + new_event.severity = selected_event_container.severity + selected_event_container.available_events += new_event + log_and_message_admins("has added \a [severity_to_string[new_event.severity]] event '[new_event.name]' of type [new_event.event_type] with weight [new_event.weight].") + new_event = new + else if(href_list["clear"]) + var/datum/event_container/EC = locate(href_list["clear"]) + if(EC.next_event) + log_and_message_admins("has dequeued the [severity_to_string[EC.severity]] event '[EC.next_event.name]'.") + EC.next_event = null + + Interact(usr) + +/client/proc/forceEvent(var/type in SSevents.allEvents) + set name = "Trigger Event (Debug Only)" + set category = "Debug" + + if(!holder) + return + + if(ispath(type)) + new type(new /datum/event_meta(EVENT_LEVEL_MAJOR)) + message_admins("[key_name_admin(usr)] has triggered an event. ([type])", 1) + +/client/proc/event_manager_panel() + set name = "Event Manager Panel" + set category = "Admin" + SSevents.Interact(usr) + feedback_add_details("admin_verb","EMP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/events/gravity.dm b/code/modules/events/gravity.dm index 926b35e054b..47565997b1b 100644 --- a/code/modules/events/gravity.dm +++ b/code/modules/events/gravity.dm @@ -1,62 +1,62 @@ -/datum/event/gravity - announceWhen = 5 - var/list/zLevels - var/list/generators = list() - -/datum/event/gravity/setup() - // Setup which levels we will disrupt gravit on. - zLevels = using_map.station_levels.Copy() - for(var/datum/planet/P in SSplanets.planets) - zLevels -= P.expected_z_levels - - for(var/obj/machinery/gravity_generator/main/GG in machines) - if((GG.z in zLevels) && GG.on) - generators += GG - - if(generators.len) - endWhen = rand(5 MINUTES, 20 MINUTES) - else - endWhen = rand(15, 60) - -/datum/event/gravity/announce() - if(generators.len) - command_announcement.Announce("Feedback surge detected in mass-distributions systems. Artificial gravity has been disabled. \ - Please wait for the system to reinitialize, or contact your engineering department.", "Gravity Failure") - else - command_announcement.Announce("Feedback surge detected in mass-distributions systems. Artificial gravity has been disabled whilst the system \ - reinitializes. Please stand by while the gravity system reinitializes.", "Gravity Failure") - -/datum/event/gravity/start() - gravity_is_on = 0 - if(generators.len) - for(var/obj/machinery/gravity_generator/main/GG in generators) - if((GG.z in zLevels) && GG.on) - GG.breaker = FALSE - GG.set_power() - GG.charge_count = 10 - else - for(var/area/A in world) - if(A.z in zLevels) - A.gravitychange(gravity_is_on) - -/datum/event/gravity/end() - if(!gravity_is_on) - gravity_is_on = 1 - - - var/did_anything = FALSE - if(generators.len) - for(var/obj/machinery/gravity_generator/main/GG in generators) - if(!GG.on) - GG.breaker = TRUE - GG.set_power() - GG.charge_count = 90 - did_anything = TRUE - else - for(var/area/A in world) - if(A.z in zLevels) - A.gravitychange(gravity_is_on) - did_anything = TRUE - - if(did_anything) - command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.", "Gravity Restored") +/datum/event/gravity + announceWhen = 5 + var/list/zLevels + var/list/generators = list() + +/datum/event/gravity/setup() + // Setup which levels we will disrupt gravit on. + zLevels = using_map.station_levels.Copy() + for(var/datum/planet/P in SSplanets.planets) + zLevels -= P.expected_z_levels + + for(var/obj/machinery/gravity_generator/main/GG in machines) + if((GG.z in zLevels) && GG.on) + generators += GG + + if(generators.len) + endWhen = rand(5 MINUTES, 20 MINUTES) + else + endWhen = rand(15, 60) + +/datum/event/gravity/announce() + if(generators.len) + command_announcement.Announce("Feedback surge detected in mass-distributions systems. Artificial gravity has been disabled. \ + Please wait for the system to reinitialize, or contact your engineering department.", "Gravity Failure") + else + command_announcement.Announce("Feedback surge detected in mass-distributions systems. Artificial gravity has been disabled whilst the system \ + reinitializes. Please stand by while the gravity system reinitializes.", "Gravity Failure") + +/datum/event/gravity/start() + gravity_is_on = 0 + if(generators.len) + for(var/obj/machinery/gravity_generator/main/GG in generators) + if((GG.z in zLevels) && GG.on) + GG.breaker = FALSE + GG.set_power() + GG.charge_count = 10 + else + for(var/area/A in world) + if(A.z in zLevels) + A.gravitychange(gravity_is_on) + +/datum/event/gravity/end() + if(!gravity_is_on) + gravity_is_on = 1 + + + var/did_anything = FALSE + if(generators.len) + for(var/obj/machinery/gravity_generator/main/GG in generators) + if(!GG.on) + GG.breaker = TRUE + GG.set_power() + GG.charge_count = 90 + did_anything = TRUE + else + for(var/area/A in world) + if(A.z in zLevels) + A.gravitychange(gravity_is_on) + did_anything = TRUE + + if(did_anything) + command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.", "Gravity Restored") diff --git a/code/modules/events/grubinfestation_vr.dm b/code/modules/events/grubinfestation_vr.dm index 34cbec6005c..c73c8feea3f 100644 --- a/code/modules/events/grubinfestation_vr.dm +++ b/code/modules/events/grubinfestation_vr.dm @@ -1,54 +1,54 @@ -/datum/event/grub_infestation - announceWhen = 90 - endWhen = 200 - var/spawncount = 1 - var/list/vents = list() - var/give_positions = 0 - -/datum/event/grub_infestation/setup() - announceWhen = rand(announceWhen, announceWhen + 60) - - spawncount = rand(2 * severity, 6 * severity) - - for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) - var/area/A = get_area(temp_vent) - if(A.forbid_events) - continue - if(!temp_vent.welded && temp_vent.network && (temp_vent.loc.z in using_map.station_levels)) - if(temp_vent.network.normal_members.len > 50) - vents += temp_vent - -/datum/event/grub_infestation/announce() - command_announcement.Announce("Solargrubs detected coming aboard [station_name()]. Please clear them out before this starts to affect productivity. All crew efforts are appreciated and encouraged.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') - -/datum/event/grub_infestation/start() - while((spawncount >= 1) && vents.len) - var/obj/vent = pick(vents) - var/mob/living/simple_mob/animal/solargrub_larva/larva = new(get_turf(vent)) - larva.tracked = TRUE - vents -= vent - spawncount-- - vents.Cut() - -/datum/event/grub_infestation/end() - var/list/area_names = list() - for(var/mob/living/G as anything in existing_solargrubs) - if(!G || G.stat == DEAD) - continue - if(istype(G, /mob/living/simple_mob/animal/solargrub_larva)) - var/mob/living/simple_mob/animal/solargrub_larva/L = G - if(!(L.tracked)) - continue - if(istype(G, /mob/living/simple_mob/vore/solargrub)) - var/mob/living/simple_mob/vore/solargrub/S = G - if(!(S.tracked)) - continue - var/area/grub_area = get_area(G) - if(!grub_area) //Huh, really? - if(!get_turf(G)) //No turf either? - qdel(G) //Must have been nullspaced - continue - area_names |= grub_area.name - if(area_names.len) - var/english_list = english_list(area_names) - command_announcement.Announce("Sensors have narrowed down remaining active solargrubs to the following areas: [english_list]", "Lifesign Alert") +/datum/event/grub_infestation + announceWhen = 90 + endWhen = 200 + var/spawncount = 1 + var/list/vents = list() + var/give_positions = 0 + +/datum/event/grub_infestation/setup() + announceWhen = rand(announceWhen, announceWhen + 60) + + spawncount = rand(2 * severity, 6 * severity) + + for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) + var/area/A = get_area(temp_vent) + if(A.forbid_events) + continue + if(!temp_vent.welded && temp_vent.network && (temp_vent.loc.z in using_map.station_levels)) + if(temp_vent.network.normal_members.len > 50) + vents += temp_vent + +/datum/event/grub_infestation/announce() + command_announcement.Announce("Solargrubs detected coming aboard [station_name()]. Please clear them out before this starts to affect productivity. All crew efforts are appreciated and encouraged.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') + +/datum/event/grub_infestation/start() + while((spawncount >= 1) && vents.len) + var/obj/vent = pick(vents) + var/mob/living/simple_mob/animal/solargrub_larva/larva = new(get_turf(vent)) + larva.tracked = TRUE + vents -= vent + spawncount-- + vents.Cut() + +/datum/event/grub_infestation/end() + var/list/area_names = list() + for(var/mob/living/G as anything in existing_solargrubs) + if(!G || G.stat == DEAD) + continue + if(istype(G, /mob/living/simple_mob/animal/solargrub_larva)) + var/mob/living/simple_mob/animal/solargrub_larva/L = G + if(!(L.tracked)) + continue + if(istype(G, /mob/living/simple_mob/vore/solargrub)) + var/mob/living/simple_mob/vore/solargrub/S = G + if(!(S.tracked)) + continue + var/area/grub_area = get_area(G) + if(!grub_area) //Huh, really? + if(!get_turf(G)) //No turf either? + qdel(G) //Must have been nullspaced + continue + area_names |= grub_area.name + if(area_names.len) + var/english_list = english_list(area_names) + command_announcement.Announce("Sensors have narrowed down remaining active solargrubs to the following areas: [english_list]", "Lifesign Alert") diff --git a/code/modules/events/prison_break.dm b/code/modules/events/prison_break.dm index 44b30fe2985..3f19f2dfcc6 100644 --- a/code/modules/events/prison_break.dm +++ b/code/modules/events/prison_break.dm @@ -1,74 +1,74 @@ -/datum/event/prison_break - startWhen = 5 - announceWhen = 75 - - var/releaseWhen = 60 - var/list/area/areas = list() //List of areas to affect. Filled by start() - - var/eventDept = "Security" //Department name in announcement - var/list/areaName = list("Brig") //Names of areas mentioned in AI and Engineering announcements - var/list/areaType = list(/area/security/prison, /area/security/brig) //Area types to include. - var/list/areaNotType = list() //Area types to specifically exclude. - -/datum/event/prison_break/virology - eventDept = "Medical" - areaName = list("Virology") - areaType = list(/area/medical/virology, /area/medical/virologyaccess) - -/datum/event/prison_break/xenobiology - eventDept = "Science" - areaName = list("Xenobiology") - areaType = list(/area/rnd/xenobiology) - areaNotType = list(/area/rnd/xenobiology/xenoflora, /area/rnd/xenobiology/xenoflora_storage) - -/datum/event/prison_break/station - eventDept = "Station" - areaName = list("Brig","Virology","Xenobiology") - areaType = list(/area/security/prison, /area/security/brig, /area/medical/virology, /area/medical/virologyaccess, /area/rnd/xenobiology) - areaNotType = list(/area/rnd/xenobiology/xenoflora, /area/rnd/xenobiology/xenoflora_storage) - - -/datum/event/prison_break/setup() - announceWhen = rand(75, 105) - releaseWhen = rand(60, 90) - - src.endWhen = src.releaseWhen+2 - - -/datum/event/prison_break/announce() - if(areas && areas.len > 0) - command_announcement.Announce("[pick("Gr3y.T1d3 virus","Malignant trojan")] detected in [station_name()] [(eventDept == "Security")? "imprisonment":"containment"] subroutines. Secure any compromised areas immediately. Involvement of [using_map.facility_type] AI is recommended.", "[eventDept] Alert") - - -/datum/event/prison_break/start() - for(var/area/A in world) - if(is_type_in_list(A,areaType) && !is_type_in_list(A,areaNotType)) - areas += A - - if(areas && areas.len > 0) - var/my_department = "[station_name()] firewall subroutines" - var/rc_message = "An unknown malicious program has been detected in the [english_list(areaName)] lighting and airlock control systems at [stationtime2text()]. Systems will be fully compromised within approximately three minutes. Direct intervention is required immediately.
                    " - for(var/obj/machinery/message_server/MS in machines) - MS.send_rc_message("Engineering", my_department, rc_message, "", "", 2) - for(var/mob/living/silicon/ai/A in player_list) - to_chat(A, "Malicious program detected in the [english_list(areaName)] lighting and airlock control systems by [my_department].") - - else - to_world_log("ERROR: Could not initate grey-tide. Unable to find suitable containment area.") - kill() - - -/datum/event/prison_break/tick() - if(activeFor == releaseWhen) - if(areas && areas.len > 0) - var/obj/machinery/power/apc/theAPC = null - for(var/area/A in areas) - theAPC = A.get_apc() - if(theAPC && theAPC.operating) //If the apc's off, it's a little hard to overload the lights. - for(var/obj/machinery/light/L in A) - L.flicker(10) - - -/datum/event/prison_break/end() - for(var/area/A in shuffle(areas)) - A.prison_break() +/datum/event/prison_break + startWhen = 5 + announceWhen = 75 + + var/releaseWhen = 60 + var/list/area/areas = list() //List of areas to affect. Filled by start() + + var/eventDept = "Security" //Department name in announcement + var/list/areaName = list("Brig") //Names of areas mentioned in AI and Engineering announcements + var/list/areaType = list(/area/security/prison, /area/security/brig) //Area types to include. + var/list/areaNotType = list() //Area types to specifically exclude. + +/datum/event/prison_break/virology + eventDept = "Medical" + areaName = list("Virology") + areaType = list(/area/medical/virology, /area/medical/virologyaccess) + +/datum/event/prison_break/xenobiology + eventDept = "Science" + areaName = list("Xenobiology") + areaType = list(/area/rnd/xenobiology) + areaNotType = list(/area/rnd/xenobiology/xenoflora, /area/rnd/xenobiology/xenoflora_storage) + +/datum/event/prison_break/station + eventDept = "Station" + areaName = list("Brig","Virology","Xenobiology") + areaType = list(/area/security/prison, /area/security/brig, /area/medical/virology, /area/medical/virologyaccess, /area/rnd/xenobiology) + areaNotType = list(/area/rnd/xenobiology/xenoflora, /area/rnd/xenobiology/xenoflora_storage) + + +/datum/event/prison_break/setup() + announceWhen = rand(75, 105) + releaseWhen = rand(60, 90) + + src.endWhen = src.releaseWhen+2 + + +/datum/event/prison_break/announce() + if(areas && areas.len > 0) + command_announcement.Announce("[pick("Gr3y.T1d3 virus","Malignant trojan")] detected in [station_name()] [(eventDept == "Security")? "imprisonment":"containment"] subroutines. Secure any compromised areas immediately. Involvement of [using_map.facility_type] AI is recommended.", "[eventDept] Alert") + + +/datum/event/prison_break/start() + for(var/area/A in world) + if(is_type_in_list(A,areaType) && !is_type_in_list(A,areaNotType)) + areas += A + + if(areas && areas.len > 0) + var/my_department = "[station_name()] firewall subroutines" + var/rc_message = "An unknown malicious program has been detected in the [english_list(areaName)] lighting and airlock control systems at [stationtime2text()]. Systems will be fully compromised within approximately three minutes. Direct intervention is required immediately.
                    " + for(var/obj/machinery/message_server/MS in machines) + MS.send_rc_message("Engineering", my_department, rc_message, "", "", 2) + for(var/mob/living/silicon/ai/A in player_list) + to_chat(A, "Malicious program detected in the [english_list(areaName)] lighting and airlock control systems by [my_department].") + + else + to_world_log("ERROR: Could not initate grey-tide. Unable to find suitable containment area.") + kill() + + +/datum/event/prison_break/tick() + if(activeFor == releaseWhen) + if(areas && areas.len > 0) + var/obj/machinery/power/apc/theAPC = null + for(var/area/A in areas) + theAPC = A.get_apc() + if(theAPC && theAPC.operating) //If the apc's off, it's a little hard to overload the lights. + for(var/obj/machinery/light/L in A) + L.flicker(10) + + +/datum/event/prison_break/end() + for(var/area/A in shuffle(areas)) + A.prison_break() diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index fef2e3f6b17..48c9b91c22c 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -1,11 +1,11 @@ -/var/global/spacevines_spawned = 0 - -/datum/event/spacevine - announceWhen = 60 - -/datum/event/spacevine/start() - spacevine_infestation() - spacevines_spawned = 1 - -/datum/event/spacevine/announce() - level_seven_announcement() +/var/global/spacevines_spawned = 0 + +/datum/event/spacevine + announceWhen = 60 + +/datum/event/spacevine/start() + spacevine_infestation() + spacevines_spawned = 1 + +/datum/event/spacevine/announce() + level_seven_announcement() diff --git a/code/modules/events/spider_infestation.dm b/code/modules/events/spider_infestation.dm index 7cba7cbbd39..ad6b02c81ed 100644 --- a/code/modules/events/spider_infestation.dm +++ b/code/modules/events/spider_infestation.dm @@ -1,30 +1,30 @@ -/var/global/sent_spiders_to_station = 0 - -/datum/event/spider_infestation - announceWhen = 90 - var/spawncount = 1 - - -/datum/event/spider_infestation/setup() - announceWhen = rand(announceWhen, announceWhen + 60) - spawncount = rand(4 * severity, 6 * severity) //spiderlings only have a 50% chance to grow big and strong - sent_spiders_to_station = 0 - -/datum/event/spider_infestation/announce() - command_announcement.Announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') - - -/datum/event/spider_infestation/start() - var/list/vents = list() - for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) - if(!temp_vent.welded && temp_vent.network && (temp_vent.loc.z in using_map.station_levels)) - if(temp_vent.network.normal_members.len > 50) - var/area/A = get_area(temp_vent) - if(!(A.forbid_events)) - vents += temp_vent - - while((spawncount >= 1) && vents.len) - var/obj/vent = pick(vents) - new /obj/effect/spider/spiderling/virgo(vent.loc) //VOREStation Edit - No nurses - vents -= vent - spawncount-- +/var/global/sent_spiders_to_station = 0 + +/datum/event/spider_infestation + announceWhen = 90 + var/spawncount = 1 + + +/datum/event/spider_infestation/setup() + announceWhen = rand(announceWhen, announceWhen + 60) + spawncount = rand(4 * severity, 6 * severity) //spiderlings only have a 50% chance to grow big and strong + sent_spiders_to_station = 0 + +/datum/event/spider_infestation/announce() + command_announcement.Announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') + + +/datum/event/spider_infestation/start() + var/list/vents = list() + for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) + if(!temp_vent.welded && temp_vent.network && (temp_vent.loc.z in using_map.station_levels)) + if(temp_vent.network.normal_members.len > 50) + var/area/A = get_area(temp_vent) + if(!(A.forbid_events)) + vents += temp_vent + + while((spawncount >= 1) && vents.len) + var/obj/vent = pick(vents) + new /obj/effect/spider/spiderling/virgo(vent.loc) //VOREStation Edit - No nurses + vents -= vent + spawncount-- diff --git a/code/modules/events/spontaneous_appendicitis.dm b/code/modules/events/spontaneous_appendicitis.dm index ddc392174e0..2009e17467c 100644 --- a/code/modules/events/spontaneous_appendicitis.dm +++ b/code/modules/events/spontaneous_appendicitis.dm @@ -1,4 +1,4 @@ -/datum/event/spontaneous_appendicitis/start() - for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) - if(H.client && H.appendicitis()) - break +/datum/event/spontaneous_appendicitis/start() + for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) + if(H.client && H.appendicitis()) + break diff --git a/code/modules/examine/descriptions/paperwork.dm b/code/modules/examine/descriptions/paperwork.dm index 6e837095fa1..75840d97747 100644 --- a/code/modules/examine/descriptions/paperwork.dm +++ b/code/modules/examine/descriptions/paperwork.dm @@ -1,21 +1,21 @@ -/obj/item/weapon/pen - description_info = {"This is an item for writing down your thoughts, on paper or elsewhere. The following special commands are available: -Pen and crayon commands -\[br\] : Creates a linebreak. -\[center\] - \[/center\] : Centers the text. -\[h1\] - \[/h1\] : Makes the text a first level heading. -\[h2\] - \[/h2\] : Makes the text a second level headin. -\[h3\] - \[/h3\] : Makes the text a third level heading. -\[b\] - \[/b\] : Makes the text bold. -\[i\] - \[/i\] : Makes the text italic. -\[u\] - \[/u\] : Makes the text underlined. -\[large\] - \[/large\] : Increases the size of the text. -\[sign\] : Inserts a signature of your name in a foolproof way. -\[field\] : Inserts an invisible field which lets you start type from there. Useful for forms. -\[date\] : Inserts today's station date. -\[time\] : Inserts the current station time. -Pen exclusive commands -\[small\] - \[/small\] : Decreases the size of the text. -\[list\] - \[/list\] : A list. -\[*\] : A dot used for lists. -\[hr\] : Adds a horizontal rule."} +/obj/item/weapon/pen + description_info = {"This is an item for writing down your thoughts, on paper or elsewhere. The following special commands are available: +Pen and crayon commands +\[br\] : Creates a linebreak. +\[center\] - \[/center\] : Centers the text. +\[h1\] - \[/h1\] : Makes the text a first level heading. +\[h2\] - \[/h2\] : Makes the text a second level headin. +\[h3\] - \[/h3\] : Makes the text a third level heading. +\[b\] - \[/b\] : Makes the text bold. +\[i\] - \[/i\] : Makes the text italic. +\[u\] - \[/u\] : Makes the text underlined. +\[large\] - \[/large\] : Increases the size of the text. +\[sign\] : Inserts a signature of your name in a foolproof way. +\[field\] : Inserts an invisible field which lets you start type from there. Useful for forms. +\[date\] : Inserts today's station date. +\[time\] : Inserts the current station time. +Pen exclusive commands +\[small\] - \[/small\] : Decreases the size of the text. +\[list\] - \[/list\] : A list. +\[*\] : A dot used for lists. +\[hr\] : Adds a horizontal rule."} diff --git a/code/modules/flufftext/look_up.dm b/code/modules/flufftext/look_up.dm index 46338290f9e..776022b4ab0 100644 --- a/code/modules/flufftext/look_up.dm +++ b/code/modules/flufftext/look_up.dm @@ -1,65 +1,65 @@ -// Implements a verb to make your character look upward, mostly intended for the surface. - -/mob/living/verb/look_up() - set name = "Look Up" - set category = "IC" - set desc = "Look above you, and hope there's no ceiling spiders." - - to_chat(usr, "You look upwards...") - - var/turf/T = get_turf(usr) - if(!T) // In null space. - to_chat(usr, span("warning", "You appear to be in a place without any sort of concept of direction. You have bigger problems to worry about.")) - return - - if(!T.is_outdoors()) // They're inside. - to_chat(usr, "You see nothing interesting.") - return - - else // They're outside and hopefully on a planet. - if(!(T.z in SSplanets.z_to_planet) || !(SSplanets.z_to_planet[T.z])) - to_chat(usr, span("warning", "You appear to be outside, but not on a planet... Something is wrong.")) - return - var/datum/planet/P = SSplanets.z_to_planet[T.z] - - var/datum/weather_holder/WH = P.weather_holder - - // Describe the current weather. - if(WH.current_weather.observed_message) - to_chat(usr, WH.current_weather.observed_message) - - // Describe the current weather. - if(WH.imminent_weather) - var/datum/weather/coming_weather = WH.allowed_weather_types[WH.imminent_weather] - to_chat(usr, coming_weather.imminent_transition_message) - - // If we can see the sky, we'll see things like sun position, phase of the moon, etc. - if(!WH.current_weather.sky_visible) - to_chat(usr, "You can't see the sky clearly due to the [WH.current_weather.name].") - else - // Sun-related output. - if(P.sun_name) - var/afternoon = P.current_time.seconds_stored > (P.current_time.seconds_in_day / 2) - - var/sun_message = null - switch(P.sun_position) - if(0 to 0.4) // Night - sun_message = "It is night time, [P.sun_name] is not visible." - if(0.4 to 0.5) // Twilight - sun_message = "The sky is in twilight, however [P.sun_name] is not visible." - if(0.5 to 0.7) // Sunrise/set. - sun_message = "[P.sun_name] is slowly [!afternoon ? "rising from" : "setting on"] the horizon." - if(0.7 to 0.9) // Morning/evening - sun_message = "[P.sun_name]'s position implies it is currently [!afternoon ? "early" : "late"] in the day." - if(0.9 to 1.0) // Noon - sun_message = "It's high noon. [P.sun_name] hangs directly above you." - - to_chat(usr, sun_message) - - // Now for the moon. - if(P.moon_name) - if(P.moon_phase == MOON_PHASE_NEW_MOON) - to_chat(usr, "[P.moon_name] is not visible. It must be a new moon.") - else - to_chat(usr, "[P.moon_name] appears to currently be a [P.moon_phase].") - +// Implements a verb to make your character look upward, mostly intended for the surface. + +/mob/living/verb/look_up() + set name = "Look Up" + set category = "IC" + set desc = "Look above you, and hope there's no ceiling spiders." + + to_chat(usr, "You look upwards...") + + var/turf/T = get_turf(usr) + if(!T) // In null space. + to_chat(usr, span("warning", "You appear to be in a place without any sort of concept of direction. You have bigger problems to worry about.")) + return + + if(!T.is_outdoors()) // They're inside. + to_chat(usr, "You see nothing interesting.") + return + + else // They're outside and hopefully on a planet. + if(!(T.z in SSplanets.z_to_planet) || !(SSplanets.z_to_planet[T.z])) + to_chat(usr, span("warning", "You appear to be outside, but not on a planet... Something is wrong.")) + return + var/datum/planet/P = SSplanets.z_to_planet[T.z] + + var/datum/weather_holder/WH = P.weather_holder + + // Describe the current weather. + if(WH.current_weather.observed_message) + to_chat(usr, WH.current_weather.observed_message) + + // Describe the current weather. + if(WH.imminent_weather) + var/datum/weather/coming_weather = WH.allowed_weather_types[WH.imminent_weather] + to_chat(usr, coming_weather.imminent_transition_message) + + // If we can see the sky, we'll see things like sun position, phase of the moon, etc. + if(!WH.current_weather.sky_visible) + to_chat(usr, "You can't see the sky clearly due to the [WH.current_weather.name].") + else + // Sun-related output. + if(P.sun_name) + var/afternoon = P.current_time.seconds_stored > (P.current_time.seconds_in_day / 2) + + var/sun_message = null + switch(P.sun_position) + if(0 to 0.4) // Night + sun_message = "It is night time, [P.sun_name] is not visible." + if(0.4 to 0.5) // Twilight + sun_message = "The sky is in twilight, however [P.sun_name] is not visible." + if(0.5 to 0.7) // Sunrise/set. + sun_message = "[P.sun_name] is slowly [!afternoon ? "rising from" : "setting on"] the horizon." + if(0.7 to 0.9) // Morning/evening + sun_message = "[P.sun_name]'s position implies it is currently [!afternoon ? "early" : "late"] in the day." + if(0.9 to 1.0) // Noon + sun_message = "It's high noon. [P.sun_name] hangs directly above you." + + to_chat(usr, sun_message) + + // Now for the moon. + if(P.moon_name) + if(P.moon_phase == MOON_PHASE_NEW_MOON) + to_chat(usr, "[P.moon_name] is not visible. It must be a new moon.") + else + to_chat(usr, "[P.moon_name] appears to currently be a [P.moon_phase].") + diff --git a/code/modules/food/kitchen/cooking_machines/_appliance.dm b/code/modules/food/kitchen/cooking_machines/_appliance.dm index 07d812f8b59..a5703007bf8 100644 --- a/code/modules/food/kitchen/cooking_machines/_appliance.dm +++ b/code/modules/food/kitchen/cooking_machines/_appliance.dm @@ -1,784 +1,784 @@ -// This folder contains code that was originally ported from Apollo Station and then refactored/optimized/changed. - -// Tracks precooked food to stop deep fried baked grilled grilled grilled diona nymph cereal. -/obj/item/weapon/reagent_containers/food/snacks - var/tmp/list/cooked = list() - -// Root type for cooking machines. See following files for specific implementations. -/obj/machinery/appliance - name = "cooker" - desc = "You shouldn't be seeing this!" - icon = 'icons/obj/cooking_machines.dmi' - var/appliancetype = 0 - density = TRUE - anchored = TRUE - - use_power = USE_POWER_IDLE - idle_power_usage = 5 // Power used when turned on, but not processing anything - active_power_usage = 1000 // Power used when turned on and actively cooking something - - var/cooking_power = 0 // Effectiveness/speed at cooking - var/cooking_coeff = 0 // Optimal power * proximity to optimal temp; used to calc. cooking power. - var/heating_power = 1000 // Effectiveness at heating up; not used for mixers, should be equal to active_power_usage - var/max_contents = 1 // Maximum number of things this appliance can simultaneously cook - var/on_icon // Icon state used when cooking. - var/off_icon // Icon state used when not cooking. - var/cooking = FALSE // Whether or not the machine is currently operating. - var/cook_type // A string value used to track what kind of food this machine makes. - var/can_cook_mobs // Whether or not this machine accepts grabbed mobs. - var/mobdamagetype = BRUTE // Burn damage for cooking appliances, brute for cereal/candy - var/food_color // Colour of resulting food item. - var/cooked_sound = 'sound/machines/ding.ogg' // Sound played when cooking completes. - var/can_burn_food = FALSE // Can the object burn food that is left inside? - var/burn_chance = 10 // How likely is the food to burn? - var/list/cooking_objs = list() // List of things being cooked - - // If the machine has multiple output modes, define them here. - var/selected_option - var/list/output_options = list() - var/list/datum/recipe/available_recipes - - var/container_type = null - - var/combine_first = FALSE // If TRUE, this appliance will do combination cooking before checking recipes - -/obj/machinery/appliance/Initialize() - . = ..() - - default_apply_parts() - - if(output_options.len) - verbs += /obj/machinery/appliance/proc/choose_output - - if (!available_recipes) - available_recipes = new - - for(var/datum/recipe/test as anything in subtypesof(/datum/recipe)) - if((appliancetype & initial(test.appliance))) - available_recipes += new test - -/obj/machinery/appliance/Destroy() - for(var/datum/cooking_item/CI as anything in cooking_objs) - qdel(CI.container)//Food is fragile, it probably doesnt survive the destruction of the machine - cooking_objs -= CI - qdel(CI) - return ..() - -/obj/machinery/appliance/examine(var/mob/user) - . = ..() - if(Adjacent(user)) - . += list_contents(user) - -/obj/machinery/appliance/proc/list_contents(var/mob/user) - if (cooking_objs.len) - var/string = "Contains..." - for(var/datum/cooking_item/CI as anything in cooking_objs) - string += "-\a [CI.container.label(null, CI.combine_target)], [report_progress(CI)]
                    " - return string - else - to_chat(user, "") - -/obj/machinery/appliance/proc/report_progress_tgui(datum/cooking_item/CI) - if(!CI || !CI.max_cookwork) - return list("average", "Not Cooking.") - - if(!CI.cookwork) - return list("blue", "Cold.") - - var/progress = CI.cookwork / CI.max_cookwork - - if (progress < 0.25) - return list("blue", "It's barely started cooking.") - if (progress < 0.75) - return list("average", "It's cooking away nicely.") - if (progress < 1) - return list("good", "It's almost ready!") - - var/half_overcook = (CI.overcook_mult - 1)*0.5 - if (progress < 1+half_overcook) - return list("good", "It's done!") - if (progress < CI.overcook_mult) - return list("bad", "It looks overcooked, get it out!") - else - return list("bad", "It is burning!") - -/obj/machinery/appliance/proc/report_progress(var/datum/cooking_item/CI) - if (!CI || !CI.max_cookwork) - return null - - if (!CI.cookwork) - return "It is cold." - var/progress = CI.cookwork / CI.max_cookwork - - if (progress < 0.25) - return "It's barely started cooking." - if (progress < 0.75) - return "It's cooking away nicely." - if (progress < 1) - return "It's almost ready!" - - var/half_overcook = (CI.overcook_mult - 1)*0.5 - if (progress < 1+half_overcook) - return "It is done !" - if (progress < CI.overcook_mult) - return "It looks overcooked, get it out!" - else - return "It is burning!" - -/obj/machinery/appliance/update_icon() - if (!stat && cooking_objs.len) - icon_state = on_icon - - else - icon_state = off_icon - -/obj/machinery/appliance/verb/toggle_power() - set name = "Toggle Power" - set category = "Object" - set src in view() - - attempt_toggle_power(usr) - -/obj/machinery/appliance/proc/attempt_toggle_power(mob/user) - if (!isliving(user)) - return - - if (!user.IsAdvancedToolUser()) - to_chat(user, "You lack the dexterity to do that!") - return - - if (user.stat || user.restrained() || user.incapacitated()) - return - - if (!Adjacent(user) && !issilicon(user)) - to_chat(user, "You can't reach [src] from here!") - return - - if (stat & POWEROFF)//Its turned off - stat &= ~POWEROFF - use_power = 1 - user.visible_message("[user] turns [src] on.", "You turn on [src].") - - else //Its on, turn it off - stat |= POWEROFF - use_power = 0 - user.visible_message("[user] turns [src] off.", "You turn off [src].") - cooking = FALSE // Stop cooking here, too, just in case. - - playsound(src, 'sound/machines/click.ogg', 40, 1) - update_icon() - -/obj/machinery/appliance/AICtrlClick(mob/user) - attempt_toggle_power(user) - -/obj/machinery/appliance/proc/choose_output() - set src in view() - set name = "Choose output" - set category = "Object" - - if (!isliving(usr)) - return - - if (!usr.IsAdvancedToolUser()) - to_chat(usr, "You lack the dexterity to do that!") - return - - if (usr.stat || usr.restrained() || usr.incapacitated()) - return - - if (!Adjacent(usr) && !issilicon(usr)) - to_chat(usr, "You can't adjust the [src] from this distance, get closer!") - return - - if(output_options.len) - var/choice = tgui_input_list(usr, "What specific food do you wish to make with \the [src]?", "Food Output Choice", output_options+"Default") - if(!choice) - return - if(choice == "Default") - selected_option = null - to_chat(usr, "You decide not to make anything specific with \the [src].") - else - selected_option = choice - to_chat(usr, "You prepare \the [src] to make \a [selected_option] with the next thing you put in. Try putting several ingredients in a container!") - -//Handles all validity checking and error messages for inserting things -/obj/machinery/appliance/proc/can_insert(var/obj/item/I, var/mob/user) - if(istype(I.loc, /mob/living/silicon)) - return 0 - else if (istype(I.loc, /obj/item/rig_module)) - return 0 - - // We are trying to cook a grabbed mob. - var/obj/item/weapon/grab/G = I - if(istype(G)) - - if(!can_cook_mobs) - to_chat(user, "That's not going to fit.") - return 0 - - if(!isliving(G.affecting)) - to_chat(user, "You can't cook that.") - return 0 - - return 2 - - - if (!has_space(I)) - to_chat(user, "There's no room in [src] for that!") - return 0 - - - if (container_type && istype(I, container_type)) - return 1 - - // We're trying to cook something else. Check if it's valid. - var/obj/item/weapon/reagent_containers/food/snacks/check = I - if(istype(check) && islist(check.cooked) && (cook_type in check.cooked)) - to_chat(user, "\The [check] has already been [cook_type].") - return 0 - else if(istype(check, /obj/item/weapon/reagent_containers/glass)) - to_chat(user, "That would probably break [src].") - return 0 - else if(istype(check, /obj/item/weapon/disk/nuclear)) - to_chat(user, "You can't cook that.") - return 0 - else if(I.has_tool_quality(TOOL_CROWBAR) || I.has_tool_quality(TOOL_SCREWDRIVER) || istype(I, /obj/item/weapon/storage/part_replacer)) // You can't cook tools, dummy. - return 0 - else if(!istype(check) && !istype(check, /obj/item/weapon/holder)) - to_chat(user, "That's not edible.") - return 0 - - return 1 - - -//This function is overridden by cookers that do stuff with containers -/obj/machinery/appliance/proc/has_space(var/obj/item/I) - if(cooking_objs.len >= max_contents) - return FALSE - - return TRUE - -/obj/machinery/appliance/attackby(var/obj/item/I, var/mob/user) - if(!cook_type || (stat & (BROKEN))) - to_chat(user, "\The [src] is not working.") - return FALSE - - var/obj/item/ToCook = I - - if(istype(I, /obj/item/weapon/gripper)) - var/obj/item/weapon/gripper/GR = I - var/obj/item/Wrap = GR.wrapped - if(Wrap) - Wrap.loc = get_turf(src) - var/result = can_insert(Wrap, user) - if(!result) - Wrap.forceMove(GR) - if(!(default_deconstruction_screwdriver(user, I))) - default_part_replacement(user, I) - return - - if(QDELETED(GR.wrapped)) - GR.wrapped = null - - if(GR?.wrapped.loc != src) - GR.drop_item_nm() - - ToCook = Wrap - else - attack_hand(user) - return - - else - var/result = can_insert(I, user) - if(!result) - if(!(default_deconstruction_screwdriver(user, I))) - default_part_replacement(user, I) - return - - if(result == 2) - var/obj/item/weapon/grab/G = I - if (G && istype(G) && G.affecting) - cook_mob(G.affecting, user) - return - - //From here we can start cooking food - add_content(ToCook, user) - update_icon() - -//Override for container mechanics -/obj/machinery/appliance/proc/add_content(var/obj/item/I, var/mob/user) - if(!user.unEquip(I) && !isturf(I.loc)) - return - - var/datum/cooking_item/CI = has_space(I) - if (istype(I, /obj/item/weapon/reagent_containers/cooking_container) && CI == 1) - var/obj/item/weapon/reagent_containers/cooking_container/CC = I - CI = new /datum/cooking_item/(CC) - I.forceMove(src) - cooking_objs.Add(CI) - user.visible_message("\The [user] puts \the [I] into \the [src].") - if (CC.check_contents() == 0)//If we're just putting an empty container in, then dont start any processing. - return TRUE - else - if (CI && istype(CI)) - I.forceMove(CI.container) - - else //Something went wrong - return FALSE - - if (selected_option) - CI.combine_target = selected_option - - // We can actually start cooking now. - user.visible_message("\The [user] puts \the [I] into \the [src].") - - get_cooking_work(CI) - cooking = TRUE - return CI - -/obj/machinery/appliance/proc/get_cooking_work(var/datum/cooking_item/CI) - for (var/obj/item/J in CI.container) - cookwork_by_item(J, CI) - - for(var/datum/reagent/R as anything in CI.container.reagents.reagent_list) - if (istype(R, /datum/reagent/nutriment)) - CI.max_cookwork += R.volume *2//Added reagents contribute less than those in food items due to granular form - - //Nonfat reagents will soak oil - if (!istype(R, /datum/reagent/nutriment/triglyceride)) - CI.max_oil += R.volume * 0.25 - else - CI.max_cookwork += R.volume - CI.max_oil += R.volume * 0.10 - - //Rescaling cooking work to avoid insanely long times for large things - var/buffer = CI.max_cookwork - CI.max_cookwork = 0 - var/multiplier = 1 - var/step = 4 - while (buffer > step) - buffer -= step - CI.max_cookwork += step*multiplier - multiplier *= 0.95 - - CI.max_cookwork += buffer*multiplier - -//Just a helper to save code duplication in the above -/obj/machinery/appliance/proc/cookwork_by_item(var/obj/item/I, var/datum/cooking_item/CI) - var/obj/item/weapon/reagent_containers/food/snacks/S = I - var/work = 0 - if (istype(S)) - if (S.reagents) - for(var/datum/reagent/R as anything in S.reagents.reagent_list) - if (istype(R, /datum/reagent/nutriment)) - work += R.volume *3//Core nutrients contribute much more than peripheral chemicals - - //Nonfat reagents will soak oil - if (!istype(R, /datum/reagent/nutriment/triglyceride)) - CI.max_oil += R.volume * 0.35 - else - work += R.volume - CI.max_oil += R.volume * 0.15 - - - else if(istype(I, /obj/item/weapon/holder)) - var/obj/item/weapon/holder/H = I - if (H.held_mob) - work += ((H.held_mob.mob_size * H.held_mob.size_multiplier) * (H.held_mob.mob_size * H.held_mob.size_multiplier) * 2)+2 - - CI.max_cookwork += work - -//Called every tick while we're cooking something -/obj/machinery/appliance/proc/do_cooking_tick(var/datum/cooking_item/CI) - if (!istype(CI) || !CI.max_cookwork) - return FALSE - - var/was_done = FALSE - if (CI.cookwork >= CI.max_cookwork) - was_done = TRUE - - CI.cookwork += cooking_power - - if (!was_done && CI.cookwork >= CI.max_cookwork) - //If cookwork has gone from above to below 0, then this item finished cooking - finish_cooking(CI) - - else if (!CI.burned && CI.cookwork > min(CI.max_cookwork * CI.overcook_mult, CI.max_cookwork + 30)) - burn_food(CI) - - // Gotta hurt. - for(var/obj/item/weapon/holder/H in CI.container.contents) - var/mob/living/M = H.held_mob - if(M) - M.apply_damage(rand(1,3) * (1/M.size_multiplier), mobdamagetype, pick(BP_ALL)) - - return TRUE - -/obj/machinery/appliance/process() - if(cooking_power > 0 && cooking) - var/all_done_cooking = TRUE - for(var/datum/cooking_item/CI in cooking_objs) - do_cooking_tick(CI) - if(CI.max_cookwork > 0) - all_done_cooking = FALSE - if(all_done_cooking) - cooking = FALSE - update_icon() - - -/obj/machinery/appliance/proc/finish_cooking(var/datum/cooking_item/CI) - - src.visible_message("\The [src] pings!") - if(cooked_sound) - playsound(get_turf(src), cooked_sound, 50, 1) - //Check recipes first, a valid recipe overrides other options - var/datum/recipe/recipe = null - var/atom/C = null - if (CI.container) - C = CI.container - else - C = src - recipe = select_recipe(available_recipes,C) - - if (recipe) - CI.result_type = 4//Recipe type, a specific recipe will transform the ingredients into a new food - var/list/results = recipe.make_food(C) - - var/obj/temp = new /obj(src) //To prevent infinite loops, all results will be moved into a temporary location so they're not considered as inputs for other recipes - - for (var/atom/movable/AM in results) - AM.forceMove(temp) - - //making multiple copies of a recipe from one container. For example, tons of fries - while (select_recipe(available_recipes,C) == recipe) - var/list/TR = list() - TR += recipe.make_food(C) - for (var/atom/movable/AM in TR) //Move results to buffer - AM.forceMove(temp) - results += TR - - - for(var/obj/item/weapon/reagent_containers/food/snacks/R as anything in results) - R.forceMove(C) //Move everything from the buffer back to the container - R.cooked |= cook_type - - QDEL_NULL(temp) //delete buffer object - . = 1 //None of the rest of this function is relevant for recipe cooking - - else if(CI.combine_target) - CI.result_type = 3//Combination type. We're making something out of our ingredients - . = combination_cook(CI) - - - else - //Otherwise, we're just doing standard modification cooking. change a color + name - for (var/obj/item/i in CI.container) - modify_cook(i, CI) - - //Final step. Cook function just cooks batter for now. - for (var/obj/item/weapon/reagent_containers/food/snacks/S in CI.container) - S.cook() - - -//Combination cooking involves combining the names and reagents of ingredients into a predefined output object -//The ingredients represent flavours or fillings. EG: donut pizza, cheese bread -/obj/machinery/appliance/proc/combination_cook(var/datum/cooking_item/CI) - var/cook_path = output_options[CI.combine_target] - - var/list/words = list() - var/list/cooktypes = list() - var/datum/reagents/buffer = new /datum/reagents(1000) - var/totalcolour - var/reagents_determine_color - - if(!LAZYLEN(CI.container.contents)) // It's possible to make something, such as a cake in the oven, with only reagents. This stops them from being grey and sad. - reagents_determine_color = TRUE - - for (var/obj/item/I in CI.container) - var/obj/item/weapon/reagent_containers/food/snacks/S - if (istype(I, /obj/item/weapon/holder)) - S = create_mob_food(I, CI) - else if (istype(I, /obj/item/weapon/reagent_containers/food/snacks)) - S = I - - if (!S) - continue - - words |= splittext(S.name," ") - cooktypes |= S.cooked - - if (S.reagents && S.reagents.total_volume > 0) - if (S.filling_color) - if (!totalcolour || !buffer.total_volume) - totalcolour = S.filling_color - else - var/t = buffer.total_volume + S.reagents.total_volume - t = buffer.total_volume / y - totalcolour = BlendRGB(totalcolour, S.filling_color, t) - //Blend colours in order to find a good filling color - - - S.reagents.trans_to_holder(buffer, S.reagents.total_volume) - //Cleanup these empty husk ingredients now - if (I) - qdel(I) - if (S) - qdel(S) - - CI.container.reagents.trans_to_holder(buffer, CI.container.reagents.total_volume) - - var/obj/item/weapon/reagent_containers/food/snacks/result = new cook_path(CI.container) - buffer.trans_to_holder(result.reagents, buffer.total_volume) //trans_to doesn't handle food items well, so - //just call trans_to_holder instead - - // Reagent-only foods. - if(reagents_determine_color) - totalcolour = result.reagents.get_color() - - for(var/datum/reagent/reag in result.reagents.reagent_list) - words |= text2list(reag.name, " ") - - //Filling overlay - var/image/I = image(result.icon, "[result.icon_state]_filling") - I.color = totalcolour - result.add_overlay(I) - result.filling_color = totalcolour - - //Set the name. - words -= list("and", "the", "in", "is", "bar", "raw", "sticks", "boiled", "fried", "deep", "-o-", "warm", "two", "flavored") - //Remove common connecting words and unsuitable ones from the list. Unsuitable words include those describing - //the shape, cooked-ness/temperature or other state of an ingredient which doesn't apply to the finished product - words.Remove(result.name) - shuffle(words) - var/num = 6 //Maximum number of words - while (num > 0) - num-- - if (!words.len) - break - //Add prefixes from the ingredients in a random order until we run out or hit limit - result.name = "[pop(words)] [result.name]" - - //This proc sets the size of the output result - result.update_icon() - return result - -//Helper proc for standard modification cooking -/obj/machinery/appliance/proc/modify_cook(var/obj/item/input, var/datum/cooking_item/CI) - var/obj/item/weapon/reagent_containers/food/snacks/result - if (istype(input, /obj/item/weapon/holder)) - result = create_mob_food(input, CI) - else if (istype(input, /obj/item/weapon/reagent_containers/food/snacks)) - result = input - else - //Nonviable item - return - - if (!result) - return - - result.cooked |= cook_type - - // Set icon and appearance. - change_product_appearance(result, CI) - - // Update strings. - change_product_strings(result, CI) - -/obj/machinery/appliance/proc/burn_food(var/datum/cooking_item/CI) - // You dun goofed. - CI.burned = 1 - CI.container.clear() - new /obj/item/weapon/reagent_containers/food/snacks/badrecipe(CI.container) - - // Produce nasty smoke. - visible_message("\The [src] vomits a gout of rancid smoke!") - var/datum/effect/effect/system/smoke_spread/bad/burntfood/smoke = new /datum/effect/effect/system/smoke_spread/bad/burntfood - playsound(src, 'sound/effects/smoke.ogg', 20, 1) - smoke.attach(src) - smoke.set_up(10, 0, get_turf(src), 300) - smoke.start() - - // Set off fire alarms! - var/obj/machinery/firealarm/FA = locate() in get_area(src) - if(FA) - FA.alarm() - -/obj/machinery/appliance/attack_hand(var/mob/user) - if(..()) - return - - if(cooking_objs.len) - removal_menu(user) - -/obj/machinery/appliance/proc/removal_menu(var/mob/user) - if (can_remove_items(user)) - var/list/menuoptions = list() - for(var/datum/cooking_item/CI as anything in cooking_objs) - if (CI.container) - menuoptions[CI.container.label(menuoptions.len)] = CI - - var/selection = tgui_input_list(user, "Which item would you like to remove?", "Remove ingredients", menuoptions) - if (selection) - var/datum/cooking_item/CI = menuoptions[selection] - eject(CI, user) - update_icon() - return TRUE - return FALSE - -/obj/machinery/appliance/proc/can_remove_items(var/mob/user, show_warning = TRUE) - if (!Adjacent(user)) - return FALSE - - if (isanimal(user)) - return FALSE - - return TRUE - -/obj/machinery/appliance/proc/eject(var/datum/cooking_item/CI, var/mob/user = null) - var/obj/item/thing - var/delete = 1 - var/status = CI.container.check_contents() - - if (status == 1)//If theres only one object in a container then we extract that - thing = locate(/obj/item) in CI.container - delete = 0 - else//If the container is empty OR contains more than one thing, then we must extract the container - thing = CI.container - if (!user || !user.put_in_hands(thing)) - thing.forceMove(get_turf(src)) - - if (delete) - cooking_objs -= CI - qdel(CI) - else - CI.reset()//reset instead of deleting if the container is left inside - user.visible_message("\The [user] remove \the [thing] from \the [src].") - -/obj/machinery/appliance/proc/cook_mob(var/mob/living/victim, var/mob/user) - return - -/obj/machinery/appliance/proc/change_product_strings(var/obj/item/weapon/reagent_containers/food/snacks/product, var/datum/cooking_item/CI) - product.name = "[cook_type] [product.name]" - product.desc = "[product.desc]\nIt has been [cook_type]." - - -/obj/machinery/appliance/proc/change_product_appearance(var/obj/item/weapon/reagent_containers/food/snacks/product, var/datum/cooking_item/CI) - if (!product.coating) //Coatings change colour through a new sprite - product.color = food_color - product.filling_color = food_color - -/mob/living/proc/calculate_composition() // moved from devour.dm on aurora's side - if (!composition_reagent)//if no reagent has been set, then we'll set one - if (isSynthetic()) - src.composition_reagent = "iron" - else - if(istype(src, /mob/living/carbon/human/diona) || istype(src, /mob/living/carbon/alien/diona)) - src.composition_reagent = "nutriment" // diona are plants, not meat - else - src.composition_reagent = "protein" - if(istype(src, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = src - if(istype(H.species, /datum/species/diona)) - src.composition_reagent = "nutriment" - - //if the mob is a simple animal - MOB NOT ANIMAL - with a defined meat quantity - if (istype(src, /mob/living/simple_mob)) - var/mob/living/simple_mob/SA = src - if(SA.meat_amount) - src.composition_reagent_quantity = SA.meat_amount*2*9 - - //The quantity of protein is based on the meat_amount, but multiplied by 2 - - var/size_reagent = (src.mob_size * src.mob_size) * 3//The quantity of protein is set to 3x mob size squared - if (size_reagent > src.composition_reagent_quantity)//We take the larger of the two - src.composition_reagent_quantity = size_reagent - -//This function creates a food item which represents a dead mob -/obj/machinery/appliance/proc/create_mob_food(var/obj/item/weapon/holder/H, var/datum/cooking_item/CI) - if (!istype(H) || !H.held_mob) - qdel(H) - return null - var/mob/living/victim = H.held_mob - if (victim.stat != DEAD) - return null //Victim somehow survived the cooking, they do not become food - - victim.calculate_composition() - - var/obj/item/weapon/reagent_containers/food/snacks/variable/mob/result = new /obj/item/weapon/reagent_containers/food/snacks/variable/mob(CI.container) - result.w_class = victim.mob_size - result.reagents.add_reagent(victim.composition_reagent, victim.composition_reagent_quantity) - - if (victim.reagents) - victim.reagents.trans_to_holder(result.reagents, victim.reagents.total_volume) - - if (isanimal(victim)) - var/mob/living/simple_mob/SA = victim - result.kitchen_tag = SA.kitchen_tag - - result.appearance = victim - - var/matrix/M = matrix() - M.Turn(45) - M.Translate(1,-2) - result.transform = M - - // all done, now delete the old objects - H.held_mob = null - qdel(victim) - victim = null - qdel(H) - H = null - - return result - -/datum/cooking_item - var/max_cookwork - var/cookwork - var/overcook_mult = 3 // How long it takes to overcook. This is max_cookwork x overcook mult. If you're changing this, mind that at 3x, a max_cookwork of 30 becomes 90 ticks for the purpose of burning, and a max_cookwork of 4 only has 12 before burning! - var/result_type = 0 - var/obj/item/weapon/reagent_containers/cooking_container/container = null - var/combine_target = null - - //Result type is one of the following: - //0 unfinished, no result yet - //1 Standard modification cooking. eg Fried Donk Pocket, Baked wheat, etc - //2 Modification but with a new object that's an inert copy of the old. Generally used for deepfried mice - //3 Combination cooking, EG Donut Bread, Donk pocket pizza, etc - //4:Specific recipe cooking. EG: Turning raw potato sticks into fries - - var/burned = 0 - - var/oil = 0 - var/max_oil = 0//Used for fryers. - -/datum/cooking_item/New(var/obj/item/I) - container = I - -//This is called for containers whose contents are ejected without removing the container -/datum/cooking_item/proc/reset() - max_cookwork = 0 - cookwork = 0 - result_type = 0 - burned = 0 - max_oil = 0 - oil = 0 - combine_target = null - //Container is not reset - -/obj/machinery/appliance/RefreshParts() - ..() - var/scan_rating = 0 - var/cap_rating = 0 - - for(var/obj/item/weapon/stock_parts/P in src.component_parts) - if(istype(P, /obj/item/weapon/stock_parts/scanning_module)) - scan_rating += P.rating - 1 // Default parts shouldn't mess with stats - // to_world("RefreshParts returned scan rating of [scan_rating] during this step.") // Debug lines, uncomment if you need to test. - else if(istype(P, /obj/item/weapon/stock_parts/capacitor)) - cap_rating += P.rating - 1 // Default parts shouldn't mess with stats - // to_world("RefreshParts returned cap rating of [cap_rating] during this step.") // Debug lines, uncomment if you need to test. - - active_power_usage = initial(active_power_usage) - scan_rating * 25 - heating_power = initial(heating_power) + cap_rating * 25 - cooking_power = cooking_coeff * (1 + (scan_rating + cap_rating) / 20) // 100% eff. becomes 120%, 140%, 160% w/ better parts, thus rewarding upgrading the appliances during your shift. - // to_world("RefreshParts returned cooking power of [cooking_power] during this step.") // Debug lines, uncomment if you need to test. +// This folder contains code that was originally ported from Apollo Station and then refactored/optimized/changed. + +// Tracks precooked food to stop deep fried baked grilled grilled grilled diona nymph cereal. +/obj/item/weapon/reagent_containers/food/snacks + var/tmp/list/cooked = list() + +// Root type for cooking machines. See following files for specific implementations. +/obj/machinery/appliance + name = "cooker" + desc = "You shouldn't be seeing this!" + icon = 'icons/obj/cooking_machines.dmi' + var/appliancetype = 0 + density = TRUE + anchored = TRUE + + use_power = USE_POWER_IDLE + idle_power_usage = 5 // Power used when turned on, but not processing anything + active_power_usage = 1000 // Power used when turned on and actively cooking something + + var/cooking_power = 0 // Effectiveness/speed at cooking + var/cooking_coeff = 0 // Optimal power * proximity to optimal temp; used to calc. cooking power. + var/heating_power = 1000 // Effectiveness at heating up; not used for mixers, should be equal to active_power_usage + var/max_contents = 1 // Maximum number of things this appliance can simultaneously cook + var/on_icon // Icon state used when cooking. + var/off_icon // Icon state used when not cooking. + var/cooking = FALSE // Whether or not the machine is currently operating. + var/cook_type // A string value used to track what kind of food this machine makes. + var/can_cook_mobs // Whether or not this machine accepts grabbed mobs. + var/mobdamagetype = BRUTE // Burn damage for cooking appliances, brute for cereal/candy + var/food_color // Colour of resulting food item. + var/cooked_sound = 'sound/machines/ding.ogg' // Sound played when cooking completes. + var/can_burn_food = FALSE // Can the object burn food that is left inside? + var/burn_chance = 10 // How likely is the food to burn? + var/list/cooking_objs = list() // List of things being cooked + + // If the machine has multiple output modes, define them here. + var/selected_option + var/list/output_options = list() + var/list/datum/recipe/available_recipes + + var/container_type = null + + var/combine_first = FALSE // If TRUE, this appliance will do combination cooking before checking recipes + +/obj/machinery/appliance/Initialize() + . = ..() + + default_apply_parts() + + if(output_options.len) + verbs += /obj/machinery/appliance/proc/choose_output + + if (!available_recipes) + available_recipes = new + + for(var/datum/recipe/test as anything in subtypesof(/datum/recipe)) + if((appliancetype & initial(test.appliance))) + available_recipes += new test + +/obj/machinery/appliance/Destroy() + for(var/datum/cooking_item/CI as anything in cooking_objs) + qdel(CI.container)//Food is fragile, it probably doesnt survive the destruction of the machine + cooking_objs -= CI + qdel(CI) + return ..() + +/obj/machinery/appliance/examine(var/mob/user) + . = ..() + if(Adjacent(user)) + . += list_contents(user) + +/obj/machinery/appliance/proc/list_contents(var/mob/user) + if (cooking_objs.len) + var/string = "Contains..." + for(var/datum/cooking_item/CI as anything in cooking_objs) + string += "-\a [CI.container.label(null, CI.combine_target)], [report_progress(CI)]
                    " + return string + else + to_chat(user, "") + +/obj/machinery/appliance/proc/report_progress_tgui(datum/cooking_item/CI) + if(!CI || !CI.max_cookwork) + return list("average", "Not Cooking.") + + if(!CI.cookwork) + return list("blue", "Cold.") + + var/progress = CI.cookwork / CI.max_cookwork + + if (progress < 0.25) + return list("blue", "It's barely started cooking.") + if (progress < 0.75) + return list("average", "It's cooking away nicely.") + if (progress < 1) + return list("good", "It's almost ready!") + + var/half_overcook = (CI.overcook_mult - 1)*0.5 + if (progress < 1+half_overcook) + return list("good", "It's done!") + if (progress < CI.overcook_mult) + return list("bad", "It looks overcooked, get it out!") + else + return list("bad", "It is burning!") + +/obj/machinery/appliance/proc/report_progress(var/datum/cooking_item/CI) + if (!CI || !CI.max_cookwork) + return null + + if (!CI.cookwork) + return "It is cold." + var/progress = CI.cookwork / CI.max_cookwork + + if (progress < 0.25) + return "It's barely started cooking." + if (progress < 0.75) + return "It's cooking away nicely." + if (progress < 1) + return "It's almost ready!" + + var/half_overcook = (CI.overcook_mult - 1)*0.5 + if (progress < 1+half_overcook) + return "It is done !" + if (progress < CI.overcook_mult) + return "It looks overcooked, get it out!" + else + return "It is burning!" + +/obj/machinery/appliance/update_icon() + if (!stat && cooking_objs.len) + icon_state = on_icon + + else + icon_state = off_icon + +/obj/machinery/appliance/verb/toggle_power() + set name = "Toggle Power" + set category = "Object" + set src in view() + + attempt_toggle_power(usr) + +/obj/machinery/appliance/proc/attempt_toggle_power(mob/user) + if (!isliving(user)) + return + + if (!user.IsAdvancedToolUser()) + to_chat(user, "You lack the dexterity to do that!") + return + + if (user.stat || user.restrained() || user.incapacitated()) + return + + if (!Adjacent(user) && !issilicon(user)) + to_chat(user, "You can't reach [src] from here!") + return + + if (stat & POWEROFF)//Its turned off + stat &= ~POWEROFF + use_power = 1 + user.visible_message("[user] turns [src] on.", "You turn on [src].") + + else //Its on, turn it off + stat |= POWEROFF + use_power = 0 + user.visible_message("[user] turns [src] off.", "You turn off [src].") + cooking = FALSE // Stop cooking here, too, just in case. + + playsound(src, 'sound/machines/click.ogg', 40, 1) + update_icon() + +/obj/machinery/appliance/AICtrlClick(mob/user) + attempt_toggle_power(user) + +/obj/machinery/appliance/proc/choose_output() + set src in view() + set name = "Choose output" + set category = "Object" + + if (!isliving(usr)) + return + + if (!usr.IsAdvancedToolUser()) + to_chat(usr, "You lack the dexterity to do that!") + return + + if (usr.stat || usr.restrained() || usr.incapacitated()) + return + + if (!Adjacent(usr) && !issilicon(usr)) + to_chat(usr, "You can't adjust the [src] from this distance, get closer!") + return + + if(output_options.len) + var/choice = tgui_input_list(usr, "What specific food do you wish to make with \the [src]?", "Food Output Choice", output_options+"Default") + if(!choice) + return + if(choice == "Default") + selected_option = null + to_chat(usr, "You decide not to make anything specific with \the [src].") + else + selected_option = choice + to_chat(usr, "You prepare \the [src] to make \a [selected_option] with the next thing you put in. Try putting several ingredients in a container!") + +//Handles all validity checking and error messages for inserting things +/obj/machinery/appliance/proc/can_insert(var/obj/item/I, var/mob/user) + if(istype(I.loc, /mob/living/silicon)) + return 0 + else if (istype(I.loc, /obj/item/rig_module)) + return 0 + + // We are trying to cook a grabbed mob. + var/obj/item/weapon/grab/G = I + if(istype(G)) + + if(!can_cook_mobs) + to_chat(user, "That's not going to fit.") + return 0 + + if(!isliving(G.affecting)) + to_chat(user, "You can't cook that.") + return 0 + + return 2 + + + if (!has_space(I)) + to_chat(user, "There's no room in [src] for that!") + return 0 + + + if (container_type && istype(I, container_type)) + return 1 + + // We're trying to cook something else. Check if it's valid. + var/obj/item/weapon/reagent_containers/food/snacks/check = I + if(istype(check) && islist(check.cooked) && (cook_type in check.cooked)) + to_chat(user, "\The [check] has already been [cook_type].") + return 0 + else if(istype(check, /obj/item/weapon/reagent_containers/glass)) + to_chat(user, "That would probably break [src].") + return 0 + else if(istype(check, /obj/item/weapon/disk/nuclear)) + to_chat(user, "You can't cook that.") + return 0 + else if(I.has_tool_quality(TOOL_CROWBAR) || I.has_tool_quality(TOOL_SCREWDRIVER) || istype(I, /obj/item/weapon/storage/part_replacer)) // You can't cook tools, dummy. + return 0 + else if(!istype(check) && !istype(check, /obj/item/weapon/holder)) + to_chat(user, "That's not edible.") + return 0 + + return 1 + + +//This function is overridden by cookers that do stuff with containers +/obj/machinery/appliance/proc/has_space(var/obj/item/I) + if(cooking_objs.len >= max_contents) + return FALSE + + return TRUE + +/obj/machinery/appliance/attackby(var/obj/item/I, var/mob/user) + if(!cook_type || (stat & (BROKEN))) + to_chat(user, "\The [src] is not working.") + return FALSE + + var/obj/item/ToCook = I + + if(istype(I, /obj/item/weapon/gripper)) + var/obj/item/weapon/gripper/GR = I + var/obj/item/Wrap = GR.wrapped + if(Wrap) + Wrap.loc = get_turf(src) + var/result = can_insert(Wrap, user) + if(!result) + Wrap.forceMove(GR) + if(!(default_deconstruction_screwdriver(user, I))) + default_part_replacement(user, I) + return + + if(QDELETED(GR.wrapped)) + GR.wrapped = null + + if(GR?.wrapped.loc != src) + GR.drop_item_nm() + + ToCook = Wrap + else + attack_hand(user) + return + + else + var/result = can_insert(I, user) + if(!result) + if(!(default_deconstruction_screwdriver(user, I))) + default_part_replacement(user, I) + return + + if(result == 2) + var/obj/item/weapon/grab/G = I + if (G && istype(G) && G.affecting) + cook_mob(G.affecting, user) + return + + //From here we can start cooking food + add_content(ToCook, user) + update_icon() + +//Override for container mechanics +/obj/machinery/appliance/proc/add_content(var/obj/item/I, var/mob/user) + if(!user.unEquip(I) && !isturf(I.loc)) + return + + var/datum/cooking_item/CI = has_space(I) + if (istype(I, /obj/item/weapon/reagent_containers/cooking_container) && CI == 1) + var/obj/item/weapon/reagent_containers/cooking_container/CC = I + CI = new /datum/cooking_item/(CC) + I.forceMove(src) + cooking_objs.Add(CI) + user.visible_message("\The [user] puts \the [I] into \the [src].") + if (CC.check_contents() == 0)//If we're just putting an empty container in, then dont start any processing. + return TRUE + else + if (CI && istype(CI)) + I.forceMove(CI.container) + + else //Something went wrong + return FALSE + + if (selected_option) + CI.combine_target = selected_option + + // We can actually start cooking now. + user.visible_message("\The [user] puts \the [I] into \the [src].") + + get_cooking_work(CI) + cooking = TRUE + return CI + +/obj/machinery/appliance/proc/get_cooking_work(var/datum/cooking_item/CI) + for (var/obj/item/J in CI.container) + cookwork_by_item(J, CI) + + for(var/datum/reagent/R as anything in CI.container.reagents.reagent_list) + if (istype(R, /datum/reagent/nutriment)) + CI.max_cookwork += R.volume *2//Added reagents contribute less than those in food items due to granular form + + //Nonfat reagents will soak oil + if (!istype(R, /datum/reagent/nutriment/triglyceride)) + CI.max_oil += R.volume * 0.25 + else + CI.max_cookwork += R.volume + CI.max_oil += R.volume * 0.10 + + //Rescaling cooking work to avoid insanely long times for large things + var/buffer = CI.max_cookwork + CI.max_cookwork = 0 + var/multiplier = 1 + var/step = 4 + while (buffer > step) + buffer -= step + CI.max_cookwork += step*multiplier + multiplier *= 0.95 + + CI.max_cookwork += buffer*multiplier + +//Just a helper to save code duplication in the above +/obj/machinery/appliance/proc/cookwork_by_item(var/obj/item/I, var/datum/cooking_item/CI) + var/obj/item/weapon/reagent_containers/food/snacks/S = I + var/work = 0 + if (istype(S)) + if (S.reagents) + for(var/datum/reagent/R as anything in S.reagents.reagent_list) + if (istype(R, /datum/reagent/nutriment)) + work += R.volume *3//Core nutrients contribute much more than peripheral chemicals + + //Nonfat reagents will soak oil + if (!istype(R, /datum/reagent/nutriment/triglyceride)) + CI.max_oil += R.volume * 0.35 + else + work += R.volume + CI.max_oil += R.volume * 0.15 + + + else if(istype(I, /obj/item/weapon/holder)) + var/obj/item/weapon/holder/H = I + if (H.held_mob) + work += ((H.held_mob.mob_size * H.held_mob.size_multiplier) * (H.held_mob.mob_size * H.held_mob.size_multiplier) * 2)+2 + + CI.max_cookwork += work + +//Called every tick while we're cooking something +/obj/machinery/appliance/proc/do_cooking_tick(var/datum/cooking_item/CI) + if (!istype(CI) || !CI.max_cookwork) + return FALSE + + var/was_done = FALSE + if (CI.cookwork >= CI.max_cookwork) + was_done = TRUE + + CI.cookwork += cooking_power + + if (!was_done && CI.cookwork >= CI.max_cookwork) + //If cookwork has gone from above to below 0, then this item finished cooking + finish_cooking(CI) + + else if (!CI.burned && CI.cookwork > min(CI.max_cookwork * CI.overcook_mult, CI.max_cookwork + 30)) + burn_food(CI) + + // Gotta hurt. + for(var/obj/item/weapon/holder/H in CI.container.contents) + var/mob/living/M = H.held_mob + if(M) + M.apply_damage(rand(1,3) * (1/M.size_multiplier), mobdamagetype, pick(BP_ALL)) + + return TRUE + +/obj/machinery/appliance/process() + if(cooking_power > 0 && cooking) + var/all_done_cooking = TRUE + for(var/datum/cooking_item/CI in cooking_objs) + do_cooking_tick(CI) + if(CI.max_cookwork > 0) + all_done_cooking = FALSE + if(all_done_cooking) + cooking = FALSE + update_icon() + + +/obj/machinery/appliance/proc/finish_cooking(var/datum/cooking_item/CI) + + src.visible_message("\The [src] pings!") + if(cooked_sound) + playsound(get_turf(src), cooked_sound, 50, 1) + //Check recipes first, a valid recipe overrides other options + var/datum/recipe/recipe = null + var/atom/C = null + if (CI.container) + C = CI.container + else + C = src + recipe = select_recipe(available_recipes,C) + + if (recipe) + CI.result_type = 4//Recipe type, a specific recipe will transform the ingredients into a new food + var/list/results = recipe.make_food(C) + + var/obj/temp = new /obj(src) //To prevent infinite loops, all results will be moved into a temporary location so they're not considered as inputs for other recipes + + for (var/atom/movable/AM in results) + AM.forceMove(temp) + + //making multiple copies of a recipe from one container. For example, tons of fries + while (select_recipe(available_recipes,C) == recipe) + var/list/TR = list() + TR += recipe.make_food(C) + for (var/atom/movable/AM in TR) //Move results to buffer + AM.forceMove(temp) + results += TR + + + for(var/obj/item/weapon/reagent_containers/food/snacks/R as anything in results) + R.forceMove(C) //Move everything from the buffer back to the container + R.cooked |= cook_type + + QDEL_NULL(temp) //delete buffer object + . = 1 //None of the rest of this function is relevant for recipe cooking + + else if(CI.combine_target) + CI.result_type = 3//Combination type. We're making something out of our ingredients + . = combination_cook(CI) + + + else + //Otherwise, we're just doing standard modification cooking. change a color + name + for (var/obj/item/i in CI.container) + modify_cook(i, CI) + + //Final step. Cook function just cooks batter for now. + for (var/obj/item/weapon/reagent_containers/food/snacks/S in CI.container) + S.cook() + + +//Combination cooking involves combining the names and reagents of ingredients into a predefined output object +//The ingredients represent flavours or fillings. EG: donut pizza, cheese bread +/obj/machinery/appliance/proc/combination_cook(var/datum/cooking_item/CI) + var/cook_path = output_options[CI.combine_target] + + var/list/words = list() + var/list/cooktypes = list() + var/datum/reagents/buffer = new /datum/reagents(1000) + var/totalcolour + var/reagents_determine_color + + if(!LAZYLEN(CI.container.contents)) // It's possible to make something, such as a cake in the oven, with only reagents. This stops them from being grey and sad. + reagents_determine_color = TRUE + + for (var/obj/item/I in CI.container) + var/obj/item/weapon/reagent_containers/food/snacks/S + if (istype(I, /obj/item/weapon/holder)) + S = create_mob_food(I, CI) + else if (istype(I, /obj/item/weapon/reagent_containers/food/snacks)) + S = I + + if (!S) + continue + + words |= splittext(S.name," ") + cooktypes |= S.cooked + + if (S.reagents && S.reagents.total_volume > 0) + if (S.filling_color) + if (!totalcolour || !buffer.total_volume) + totalcolour = S.filling_color + else + var/t = buffer.total_volume + S.reagents.total_volume + t = buffer.total_volume / y + totalcolour = BlendRGB(totalcolour, S.filling_color, t) + //Blend colours in order to find a good filling color + + + S.reagents.trans_to_holder(buffer, S.reagents.total_volume) + //Cleanup these empty husk ingredients now + if (I) + qdel(I) + if (S) + qdel(S) + + CI.container.reagents.trans_to_holder(buffer, CI.container.reagents.total_volume) + + var/obj/item/weapon/reagent_containers/food/snacks/result = new cook_path(CI.container) + buffer.trans_to_holder(result.reagents, buffer.total_volume) //trans_to doesn't handle food items well, so + //just call trans_to_holder instead + + // Reagent-only foods. + if(reagents_determine_color) + totalcolour = result.reagents.get_color() + + for(var/datum/reagent/reag in result.reagents.reagent_list) + words |= text2list(reag.name, " ") + + //Filling overlay + var/image/I = image(result.icon, "[result.icon_state]_filling") + I.color = totalcolour + result.add_overlay(I) + result.filling_color = totalcolour + + //Set the name. + words -= list("and", "the", "in", "is", "bar", "raw", "sticks", "boiled", "fried", "deep", "-o-", "warm", "two", "flavored") + //Remove common connecting words and unsuitable ones from the list. Unsuitable words include those describing + //the shape, cooked-ness/temperature or other state of an ingredient which doesn't apply to the finished product + words.Remove(result.name) + shuffle(words) + var/num = 6 //Maximum number of words + while (num > 0) + num-- + if (!words.len) + break + //Add prefixes from the ingredients in a random order until we run out or hit limit + result.name = "[pop(words)] [result.name]" + + //This proc sets the size of the output result + result.update_icon() + return result + +//Helper proc for standard modification cooking +/obj/machinery/appliance/proc/modify_cook(var/obj/item/input, var/datum/cooking_item/CI) + var/obj/item/weapon/reagent_containers/food/snacks/result + if (istype(input, /obj/item/weapon/holder)) + result = create_mob_food(input, CI) + else if (istype(input, /obj/item/weapon/reagent_containers/food/snacks)) + result = input + else + //Nonviable item + return + + if (!result) + return + + result.cooked |= cook_type + + // Set icon and appearance. + change_product_appearance(result, CI) + + // Update strings. + change_product_strings(result, CI) + +/obj/machinery/appliance/proc/burn_food(var/datum/cooking_item/CI) + // You dun goofed. + CI.burned = 1 + CI.container.clear() + new /obj/item/weapon/reagent_containers/food/snacks/badrecipe(CI.container) + + // Produce nasty smoke. + visible_message("\The [src] vomits a gout of rancid smoke!") + var/datum/effect/effect/system/smoke_spread/bad/burntfood/smoke = new /datum/effect/effect/system/smoke_spread/bad/burntfood + playsound(src, 'sound/effects/smoke.ogg', 20, 1) + smoke.attach(src) + smoke.set_up(10, 0, get_turf(src), 300) + smoke.start() + + // Set off fire alarms! + var/obj/machinery/firealarm/FA = locate() in get_area(src) + if(FA) + FA.alarm() + +/obj/machinery/appliance/attack_hand(var/mob/user) + if(..()) + return + + if(cooking_objs.len) + removal_menu(user) + +/obj/machinery/appliance/proc/removal_menu(var/mob/user) + if (can_remove_items(user)) + var/list/menuoptions = list() + for(var/datum/cooking_item/CI as anything in cooking_objs) + if (CI.container) + menuoptions[CI.container.label(menuoptions.len)] = CI + + var/selection = tgui_input_list(user, "Which item would you like to remove?", "Remove ingredients", menuoptions) + if (selection) + var/datum/cooking_item/CI = menuoptions[selection] + eject(CI, user) + update_icon() + return TRUE + return FALSE + +/obj/machinery/appliance/proc/can_remove_items(var/mob/user, show_warning = TRUE) + if (!Adjacent(user)) + return FALSE + + if (isanimal(user)) + return FALSE + + return TRUE + +/obj/machinery/appliance/proc/eject(var/datum/cooking_item/CI, var/mob/user = null) + var/obj/item/thing + var/delete = 1 + var/status = CI.container.check_contents() + + if (status == 1)//If theres only one object in a container then we extract that + thing = locate(/obj/item) in CI.container + delete = 0 + else//If the container is empty OR contains more than one thing, then we must extract the container + thing = CI.container + if (!user || !user.put_in_hands(thing)) + thing.forceMove(get_turf(src)) + + if (delete) + cooking_objs -= CI + qdel(CI) + else + CI.reset()//reset instead of deleting if the container is left inside + user.visible_message("\The [user] remove \the [thing] from \the [src].") + +/obj/machinery/appliance/proc/cook_mob(var/mob/living/victim, var/mob/user) + return + +/obj/machinery/appliance/proc/change_product_strings(var/obj/item/weapon/reagent_containers/food/snacks/product, var/datum/cooking_item/CI) + product.name = "[cook_type] [product.name]" + product.desc = "[product.desc]\nIt has been [cook_type]." + + +/obj/machinery/appliance/proc/change_product_appearance(var/obj/item/weapon/reagent_containers/food/snacks/product, var/datum/cooking_item/CI) + if (!product.coating) //Coatings change colour through a new sprite + product.color = food_color + product.filling_color = food_color + +/mob/living/proc/calculate_composition() // moved from devour.dm on aurora's side + if (!composition_reagent)//if no reagent has been set, then we'll set one + if (isSynthetic()) + src.composition_reagent = "iron" + else + if(istype(src, /mob/living/carbon/human/diona) || istype(src, /mob/living/carbon/alien/diona)) + src.composition_reagent = "nutriment" // diona are plants, not meat + else + src.composition_reagent = "protein" + if(istype(src, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = src + if(istype(H.species, /datum/species/diona)) + src.composition_reagent = "nutriment" + + //if the mob is a simple animal - MOB NOT ANIMAL - with a defined meat quantity + if (istype(src, /mob/living/simple_mob)) + var/mob/living/simple_mob/SA = src + if(SA.meat_amount) + src.composition_reagent_quantity = SA.meat_amount*2*9 + + //The quantity of protein is based on the meat_amount, but multiplied by 2 + + var/size_reagent = (src.mob_size * src.mob_size) * 3//The quantity of protein is set to 3x mob size squared + if (size_reagent > src.composition_reagent_quantity)//We take the larger of the two + src.composition_reagent_quantity = size_reagent + +//This function creates a food item which represents a dead mob +/obj/machinery/appliance/proc/create_mob_food(var/obj/item/weapon/holder/H, var/datum/cooking_item/CI) + if (!istype(H) || !H.held_mob) + qdel(H) + return null + var/mob/living/victim = H.held_mob + if (victim.stat != DEAD) + return null //Victim somehow survived the cooking, they do not become food + + victim.calculate_composition() + + var/obj/item/weapon/reagent_containers/food/snacks/variable/mob/result = new /obj/item/weapon/reagent_containers/food/snacks/variable/mob(CI.container) + result.w_class = victim.mob_size + result.reagents.add_reagent(victim.composition_reagent, victim.composition_reagent_quantity) + + if (victim.reagents) + victim.reagents.trans_to_holder(result.reagents, victim.reagents.total_volume) + + if (isanimal(victim)) + var/mob/living/simple_mob/SA = victim + result.kitchen_tag = SA.kitchen_tag + + result.appearance = victim + + var/matrix/M = matrix() + M.Turn(45) + M.Translate(1,-2) + result.transform = M + + // all done, now delete the old objects + H.held_mob = null + qdel(victim) + victim = null + qdel(H) + H = null + + return result + +/datum/cooking_item + var/max_cookwork + var/cookwork + var/overcook_mult = 3 // How long it takes to overcook. This is max_cookwork x overcook mult. If you're changing this, mind that at 3x, a max_cookwork of 30 becomes 90 ticks for the purpose of burning, and a max_cookwork of 4 only has 12 before burning! + var/result_type = 0 + var/obj/item/weapon/reagent_containers/cooking_container/container = null + var/combine_target = null + + //Result type is one of the following: + //0 unfinished, no result yet + //1 Standard modification cooking. eg Fried Donk Pocket, Baked wheat, etc + //2 Modification but with a new object that's an inert copy of the old. Generally used for deepfried mice + //3 Combination cooking, EG Donut Bread, Donk pocket pizza, etc + //4:Specific recipe cooking. EG: Turning raw potato sticks into fries + + var/burned = 0 + + var/oil = 0 + var/max_oil = 0//Used for fryers. + +/datum/cooking_item/New(var/obj/item/I) + container = I + +//This is called for containers whose contents are ejected without removing the container +/datum/cooking_item/proc/reset() + max_cookwork = 0 + cookwork = 0 + result_type = 0 + burned = 0 + max_oil = 0 + oil = 0 + combine_target = null + //Container is not reset + +/obj/machinery/appliance/RefreshParts() + ..() + var/scan_rating = 0 + var/cap_rating = 0 + + for(var/obj/item/weapon/stock_parts/P in src.component_parts) + if(istype(P, /obj/item/weapon/stock_parts/scanning_module)) + scan_rating += P.rating - 1 // Default parts shouldn't mess with stats + // to_world("RefreshParts returned scan rating of [scan_rating] during this step.") // Debug lines, uncomment if you need to test. + else if(istype(P, /obj/item/weapon/stock_parts/capacitor)) + cap_rating += P.rating - 1 // Default parts shouldn't mess with stats + // to_world("RefreshParts returned cap rating of [cap_rating] during this step.") // Debug lines, uncomment if you need to test. + + active_power_usage = initial(active_power_usage) - scan_rating * 25 + heating_power = initial(heating_power) + cap_rating * 25 + cooking_power = cooking_coeff * (1 + (scan_rating + cap_rating) / 20) // 100% eff. becomes 120%, 140%, 160% w/ better parts, thus rewarding upgrading the appliances during your shift. + // to_world("RefreshParts returned cooking power of [cooking_power] during this step.") // Debug lines, uncomment if you need to test. diff --git a/code/modules/food/kitchen/cooking_machines/_cooker_output.dm b/code/modules/food/kitchen/cooking_machines/_cooker_output.dm index 07f7870bc66..96353ee1924 100644 --- a/code/modules/food/kitchen/cooking_machines/_cooker_output.dm +++ b/code/modules/food/kitchen/cooking_machines/_cooker_output.dm @@ -1,160 +1,160 @@ -// Wrapper obj for cooked food. Appearance is set in the cooking code, not on spawn. -/obj/item/weapon/reagent_containers/food/snacks/variable - name = "cooked food" - icon = 'icons/obj/food_custom.dmi' - desc = "If you can see this description then something is wrong. Please report the bug on the tracker." - bitesize = 2 - - var/size = 5 //The quantity of reagents which is considered "normal" for this kind of food - //These objects will change size depending on the ratio of reagents to this value - var/min_scale = 0.5 - var/max_scale = 2 - var/scale = 1 - - w_class = 2 - var/prefix - -/obj/item/weapon/reagent_containers/food/snacks/variable/Initialize() - . = ..() - if (reagents) - reagents.maximum_volume = size*8 + 10 - else - create_reagents(size*8 + 10) - -/obj/item/weapon/reagent_containers/food/snacks/variable/update_icon() - if (reagents && reagents.total_volume) - var/ratio = reagents.total_volume / size - - scale = ratio**(1/3) //Scaling factor is square root of desired area - scale = clamp(scale, min_scale, max_scale) - else - scale = min_scale - - var/matrix/M = matrix() - M.Scale(scale) - src.transform = M - - w_class *= scale - if (!prefix) - if (scale == min_scale) - prefix = "tiny" - else if (scale <= 0.8) - prefix = "small" - - else - if (scale >= 1.2) - prefix = "large" - if (scale >= 1.4) - prefix = "extra large" - if (scale >= 1.6) - prefix = "huge" - if (scale >= max_scale) - prefix = "massive" - - name = "[prefix] [name]" - - -/obj/item/weapon/reagent_containers/food/snacks/variable/pizza - name = "personal pizza" - desc = "A personalized pan pizza meant for only one person." - icon_state = "personal_pizza" - size = 20 - w_class = 3 - -/obj/item/weapon/reagent_containers/food/snacks/variable/bread - name = "bread" - desc = "Tasty bread." - icon_state = "breadcustom" - size = 40 - w_class = 3 - -/obj/item/weapon/reagent_containers/food/snacks/variable/pie - name = "pie" - desc = "Tasty pie." - icon_state = "piecustom" - size = 25 - -/obj/item/weapon/reagent_containers/food/snacks/variable/cake - name = "cake" - desc = "A popular band." - icon_state = "cakecustom" - size = 40 - w_class = 3 - -/obj/item/weapon/reagent_containers/food/snacks/variable/pocket - name = "hot pocket" - desc = "You wanna put a bangin- oh, nevermind." - icon_state = "donk" - size = 8 - w_class = 1 - -/obj/item/weapon/reagent_containers/food/snacks/variable/kebab - name = "kebab" - desc = "Remove this!" - icon_state = "kabob" - size = 10 - -/obj/item/weapon/reagent_containers/food/snacks/variable/waffles - name = "waffles" - desc = "Made with love." - icon_state = "waffles" - size = 12 - -/obj/item/weapon/reagent_containers/food/snacks/variable/cookie - name = "cookie" - desc = "Sugar snap!" - icon_state = "cookie" - size = 6 - w_class = 1 - -/obj/item/weapon/reagent_containers/food/snacks/variable/donut - name = "filled donut" - desc = "Donut eat this!" // kill me - icon_state = "donut" - size = 8 - w_class = 1 - -/obj/item/weapon/reagent_containers/food/snacks/variable/jawbreaker - name = "flavored jawbreaker" - desc = "It's like cracking a molar on a rainbow." - icon_state = "jawbreaker" - size = 4 - w_class = 1 - -/obj/item/weapon/reagent_containers/food/snacks/variable/candybar - name = "flavored chocolate bar" - desc = "Made in a factory downtown." - icon_state = "bar" - size = 6 - w_class = 1 - -/obj/item/weapon/reagent_containers/food/snacks/variable/sucker - name = "flavored sucker" - desc = "Suck, suck, suck." - icon_state = "sucker" - size = 4 - w_class = 1 - -/obj/item/weapon/reagent_containers/food/snacks/variable/jelly - name = "jelly" - desc = "All your friends will be jelly." - icon_state = "jellycustom" - size = 8 - - -/obj/item/weapon/reagent_containers/food/snacks/variable/cereal - name = "cereal" - desc = "Crispy and flaky" - icon_state = "cereal_box" - size = 30 - w_class = 3 - -/obj/item/weapon/reagent_containers/food/snacks/variable/cereal/Initialize() - . =..() - name = pick(list("flakes", "krispies", "crunch", "pops", "O's", "crisp", "loops", "jacks", "clusters")) - -/obj/item/weapon/reagent_containers/food/snacks/variable/mob - desc = "Poor little thing." - size = 5 - w_class = 1 +// Wrapper obj for cooked food. Appearance is set in the cooking code, not on spawn. +/obj/item/weapon/reagent_containers/food/snacks/variable + name = "cooked food" + icon = 'icons/obj/food_custom.dmi' + desc = "If you can see this description then something is wrong. Please report the bug on the tracker." + bitesize = 2 + + var/size = 5 //The quantity of reagents which is considered "normal" for this kind of food + //These objects will change size depending on the ratio of reagents to this value + var/min_scale = 0.5 + var/max_scale = 2 + var/scale = 1 + + w_class = 2 + var/prefix + +/obj/item/weapon/reagent_containers/food/snacks/variable/Initialize() + . = ..() + if (reagents) + reagents.maximum_volume = size*8 + 10 + else + create_reagents(size*8 + 10) + +/obj/item/weapon/reagent_containers/food/snacks/variable/update_icon() + if (reagents && reagents.total_volume) + var/ratio = reagents.total_volume / size + + scale = ratio**(1/3) //Scaling factor is square root of desired area + scale = clamp(scale, min_scale, max_scale) + else + scale = min_scale + + var/matrix/M = matrix() + M.Scale(scale) + src.transform = M + + w_class *= scale + if (!prefix) + if (scale == min_scale) + prefix = "tiny" + else if (scale <= 0.8) + prefix = "small" + + else + if (scale >= 1.2) + prefix = "large" + if (scale >= 1.4) + prefix = "extra large" + if (scale >= 1.6) + prefix = "huge" + if (scale >= max_scale) + prefix = "massive" + + name = "[prefix] [name]" + + +/obj/item/weapon/reagent_containers/food/snacks/variable/pizza + name = "personal pizza" + desc = "A personalized pan pizza meant for only one person." + icon_state = "personal_pizza" + size = 20 + w_class = 3 + +/obj/item/weapon/reagent_containers/food/snacks/variable/bread + name = "bread" + desc = "Tasty bread." + icon_state = "breadcustom" + size = 40 + w_class = 3 + +/obj/item/weapon/reagent_containers/food/snacks/variable/pie + name = "pie" + desc = "Tasty pie." + icon_state = "piecustom" + size = 25 + +/obj/item/weapon/reagent_containers/food/snacks/variable/cake + name = "cake" + desc = "A popular band." + icon_state = "cakecustom" + size = 40 + w_class = 3 + +/obj/item/weapon/reagent_containers/food/snacks/variable/pocket + name = "hot pocket" + desc = "You wanna put a bangin- oh, nevermind." + icon_state = "donk" + size = 8 + w_class = 1 + +/obj/item/weapon/reagent_containers/food/snacks/variable/kebab + name = "kebab" + desc = "Remove this!" + icon_state = "kabob" + size = 10 + +/obj/item/weapon/reagent_containers/food/snacks/variable/waffles + name = "waffles" + desc = "Made with love." + icon_state = "waffles" + size = 12 + +/obj/item/weapon/reagent_containers/food/snacks/variable/cookie + name = "cookie" + desc = "Sugar snap!" + icon_state = "cookie" + size = 6 + w_class = 1 + +/obj/item/weapon/reagent_containers/food/snacks/variable/donut + name = "filled donut" + desc = "Donut eat this!" // kill me + icon_state = "donut" + size = 8 + w_class = 1 + +/obj/item/weapon/reagent_containers/food/snacks/variable/jawbreaker + name = "flavored jawbreaker" + desc = "It's like cracking a molar on a rainbow." + icon_state = "jawbreaker" + size = 4 + w_class = 1 + +/obj/item/weapon/reagent_containers/food/snacks/variable/candybar + name = "flavored chocolate bar" + desc = "Made in a factory downtown." + icon_state = "bar" + size = 6 + w_class = 1 + +/obj/item/weapon/reagent_containers/food/snacks/variable/sucker + name = "flavored sucker" + desc = "Suck, suck, suck." + icon_state = "sucker" + size = 4 + w_class = 1 + +/obj/item/weapon/reagent_containers/food/snacks/variable/jelly + name = "jelly" + desc = "All your friends will be jelly." + icon_state = "jellycustom" + size = 8 + + +/obj/item/weapon/reagent_containers/food/snacks/variable/cereal + name = "cereal" + desc = "Crispy and flaky" + icon_state = "cereal_box" + size = 30 + w_class = 3 + +/obj/item/weapon/reagent_containers/food/snacks/variable/cereal/Initialize() + . =..() + name = pick(list("flakes", "krispies", "crunch", "pops", "O's", "crisp", "loops", "jacks", "clusters")) + +/obj/item/weapon/reagent_containers/food/snacks/variable/mob + desc = "Poor little thing." + size = 5 + w_class = 1 var/kitchen_tag = "animal" \ No newline at end of file diff --git a/code/modules/food/kitchen/cooking_machines/_mixer.dm b/code/modules/food/kitchen/cooking_machines/_mixer.dm index f04f8bba465..9d78be3475c 100644 --- a/code/modules/food/kitchen/cooking_machines/_mixer.dm +++ b/code/modules/food/kitchen/cooking_machines/_mixer.dm @@ -1,155 +1,155 @@ -/* -The mixer subtype is used for the candymaker and cereal maker. They are similar to cookers but with a few -fundamental differences -1. They have a single container which cant be removed. it will eject multiple contents -2. Items can't be added or removed once the process starts -3. Items are all placed in the same container when added directly -4. They do combining mode only. And will always combine the entire contents of the container into an output -*/ - -/obj/machinery/appliance/mixer - max_contents = 1 - stat = POWEROFF - cooking_coeff = 0.75 // Original value 0.4 - active_power_usage = 3000 - idle_power_usage = 50 - var/datum/looping_sound/mixer/mixer_loop - -/obj/machinery/appliance/mixer/examine(var/mob/user) - . = ..() - if(Adjacent(user)) - . += "It is currently set to make a [selected_option]" - -/obj/machinery/appliance/mixer/Initialize() - . = ..() - cooking_objs += new /datum/cooking_item(new /obj/item/weapon/reagent_containers/cooking_container(src)) - cooking = FALSE - selected_option = pick(output_options) - - mixer_loop = new(list(src), FALSE) - -/obj/machinery/appliance/mixer/Destroy() - . = ..() - - QDEL_NULL(mixer_loop) - -//Mixers cannot-not do combining mode. So the default option is removed from this. A combine target must be chosen -/obj/machinery/appliance/mixer/choose_output() - set src in view(1) - set name = "Choose output" - set category = "Object" - - if (!isliving(usr)) - return - - if (!usr.IsAdvancedToolUser()) - to_chat(usr, "You can't operate [src].") - return - - if(output_options.len) - var/choice = tgui_input_list(usr, "What specific food do you wish to make with \the [src]?", "Food Output Choice", output_options) - if(!choice) - return - else - selected_option = choice - to_chat(usr, "You prepare \the [src] to make \a [selected_option].") - var/datum/cooking_item/CI = cooking_objs[1] - CI.combine_target = selected_option - - -/obj/machinery/appliance/mixer/has_space(var/obj/item/I) - var/datum/cooking_item/CI = cooking_objs[1] - if (!CI || !CI.container) - return 0 - - if (CI.container.can_fit(I)) - return CI - - return 0 - - -/obj/machinery/appliance/mixer/can_remove_items(var/mob/user, show_warning = TRUE) - if(stat) - return 1 - else - if(show_warning) - to_chat(user, "You can't remove ingredients while it's turned on! Turn it off first or wait for it to finish.") - return 0 - -//Container is not removable -/obj/machinery/appliance/mixer/removal_menu(var/mob/user) - if (can_remove_items(user)) - var/list/menuoptions = list() - for(var/datum/cooking_item/CI as anything in cooking_objs) - if (CI.container) - if (!CI.container.check_contents()) - to_chat(user, "There's nothing in [src] you can remove!") - return - - for (var/obj/item/I in CI.container) - menuoptions[I.name] = I - - var/selection = tgui_input_list(user, "Which item would you like to remove? If you want to remove chemicals, use an empty beaker.", "Remove ingredients", menuoptions) - if (selection) - var/obj/item/I = menuoptions[selection] - if (!user || !user.put_in_hands(I)) - I.forceMove(get_turf(src)) - update_icon() - return 1 - return 0 - - -/obj/machinery/appliance/mixer/toggle_power() - set src in view(1) - set name = "Toggle Power" - set category = "Object" - - var/datum/cooking_item/CI = cooking_objs[1] - if(!CI.container.check_contents()) - to_chat("There's nothing in it! Add ingredients before turning [src] on!") - return - - if(stat & POWEROFF)//Its turned off - stat &= ~POWEROFF - if(usr) - usr.visible_message("[usr] turns the [src] on.", "You turn on \the [src].") - get_cooking_work(CI) - use_power = 2 - else //Its on, turn it off - stat |= POWEROFF - use_power = 0 - if(usr) - usr.visible_message("[usr] turns the [src] off.", "You turn off \the [src].") - playsound(src, 'sound/machines/click.ogg', 40, 1) - update_icon() - -/obj/machinery/appliance/mixer/can_insert(var/obj/item/I, var/mob/user) - if(!stat) - to_chat(user, ",You can't add items while \the [src] is running. Wait for it to finish or turn the power off to abort.") - return 0 - else - return ..() - -/obj/machinery/appliance/mixer/finish_cooking(var/datum/cooking_item/CI) - ..() - stat |= POWEROFF - playsound(src, 'sound/machines/click.ogg', 40, 1) - use_power = 0 - CI.reset() - update_icon() - -/obj/machinery/appliance/mixer/update_icon() - if (!stat) - icon_state = on_icon - if(mixer_loop) - mixer_loop.start(src) - else - icon_state = off_icon - if(mixer_loop) - mixer_loop.stop(src) - - -/obj/machinery/appliance/mixer/process() - if (!stat) - for (var/i in cooking_objs) +/* +The mixer subtype is used for the candymaker and cereal maker. They are similar to cookers but with a few +fundamental differences +1. They have a single container which cant be removed. it will eject multiple contents +2. Items can't be added or removed once the process starts +3. Items are all placed in the same container when added directly +4. They do combining mode only. And will always combine the entire contents of the container into an output +*/ + +/obj/machinery/appliance/mixer + max_contents = 1 + stat = POWEROFF + cooking_coeff = 0.75 // Original value 0.4 + active_power_usage = 3000 + idle_power_usage = 50 + var/datum/looping_sound/mixer/mixer_loop + +/obj/machinery/appliance/mixer/examine(var/mob/user) + . = ..() + if(Adjacent(user)) + . += "It is currently set to make a [selected_option]" + +/obj/machinery/appliance/mixer/Initialize() + . = ..() + cooking_objs += new /datum/cooking_item(new /obj/item/weapon/reagent_containers/cooking_container(src)) + cooking = FALSE + selected_option = pick(output_options) + + mixer_loop = new(list(src), FALSE) + +/obj/machinery/appliance/mixer/Destroy() + . = ..() + + QDEL_NULL(mixer_loop) + +//Mixers cannot-not do combining mode. So the default option is removed from this. A combine target must be chosen +/obj/machinery/appliance/mixer/choose_output() + set src in view(1) + set name = "Choose output" + set category = "Object" + + if (!isliving(usr)) + return + + if (!usr.IsAdvancedToolUser()) + to_chat(usr, "You can't operate [src].") + return + + if(output_options.len) + var/choice = tgui_input_list(usr, "What specific food do you wish to make with \the [src]?", "Food Output Choice", output_options) + if(!choice) + return + else + selected_option = choice + to_chat(usr, "You prepare \the [src] to make \a [selected_option].") + var/datum/cooking_item/CI = cooking_objs[1] + CI.combine_target = selected_option + + +/obj/machinery/appliance/mixer/has_space(var/obj/item/I) + var/datum/cooking_item/CI = cooking_objs[1] + if (!CI || !CI.container) + return 0 + + if (CI.container.can_fit(I)) + return CI + + return 0 + + +/obj/machinery/appliance/mixer/can_remove_items(var/mob/user, show_warning = TRUE) + if(stat) + return 1 + else + if(show_warning) + to_chat(user, "You can't remove ingredients while it's turned on! Turn it off first or wait for it to finish.") + return 0 + +//Container is not removable +/obj/machinery/appliance/mixer/removal_menu(var/mob/user) + if (can_remove_items(user)) + var/list/menuoptions = list() + for(var/datum/cooking_item/CI as anything in cooking_objs) + if (CI.container) + if (!CI.container.check_contents()) + to_chat(user, "There's nothing in [src] you can remove!") + return + + for (var/obj/item/I in CI.container) + menuoptions[I.name] = I + + var/selection = tgui_input_list(user, "Which item would you like to remove? If you want to remove chemicals, use an empty beaker.", "Remove ingredients", menuoptions) + if (selection) + var/obj/item/I = menuoptions[selection] + if (!user || !user.put_in_hands(I)) + I.forceMove(get_turf(src)) + update_icon() + return 1 + return 0 + + +/obj/machinery/appliance/mixer/toggle_power() + set src in view(1) + set name = "Toggle Power" + set category = "Object" + + var/datum/cooking_item/CI = cooking_objs[1] + if(!CI.container.check_contents()) + to_chat("There's nothing in it! Add ingredients before turning [src] on!") + return + + if(stat & POWEROFF)//Its turned off + stat &= ~POWEROFF + if(usr) + usr.visible_message("[usr] turns the [src] on.", "You turn on \the [src].") + get_cooking_work(CI) + use_power = 2 + else //Its on, turn it off + stat |= POWEROFF + use_power = 0 + if(usr) + usr.visible_message("[usr] turns the [src] off.", "You turn off \the [src].") + playsound(src, 'sound/machines/click.ogg', 40, 1) + update_icon() + +/obj/machinery/appliance/mixer/can_insert(var/obj/item/I, var/mob/user) + if(!stat) + to_chat(user, ",You can't add items while \the [src] is running. Wait for it to finish or turn the power off to abort.") + return 0 + else + return ..() + +/obj/machinery/appliance/mixer/finish_cooking(var/datum/cooking_item/CI) + ..() + stat |= POWEROFF + playsound(src, 'sound/machines/click.ogg', 40, 1) + use_power = 0 + CI.reset() + update_icon() + +/obj/machinery/appliance/mixer/update_icon() + if (!stat) + icon_state = on_icon + if(mixer_loop) + mixer_loop.start(src) + else + icon_state = off_icon + if(mixer_loop) + mixer_loop.stop(src) + + +/obj/machinery/appliance/mixer/process() + if (!stat) + for (var/i in cooking_objs) do_cooking_tick(i) \ No newline at end of file diff --git a/code/modules/food/kitchen/cooking_machines/container.dm b/code/modules/food/kitchen/cooking_machines/container.dm index ca5a9dd120b..59e3935da1a 100644 --- a/code/modules/food/kitchen/cooking_machines/container.dm +++ b/code/modules/food/kitchen/cooking_machines/container.dm @@ -1,221 +1,221 @@ -//Cooking containers are used in ovens and fryers, to hold multiple ingredients for a recipe. -//They work fairly similar to the microwave - acting as a container for objects and reagents, -//which can be checked against recipe requirements in order to cook recipes that require several things - -/obj/item/weapon/reagent_containers/cooking_container - icon = 'icons/obj/cooking_machines.dmi' - var/shortname - var/max_space = 20//Maximum sum of w-classes of foods in this container at once - var/max_reagents = 80//Maximum units of reagents - var/food_items = 0 // Used for icon updates - flags = OPENCONTAINER | NOREACT - var/list/insertable = list( - /obj/item/weapon/reagent_containers/food/snacks, - /obj/item/weapon/holder, - /obj/item/weapon/paper, - /obj/item/clothing/head/wizard, - /obj/item/clothing/head/cakehat, - /obj/item/clothing/mask/gas/clown_hat, - /obj/item/clothing/head/beret - ) - -/obj/item/weapon/reagent_containers/cooking_container/Initialize() - . = ..() - create_reagents(max_reagents) - flags |= OPENCONTAINER | NOREACT - - -/obj/item/weapon/reagent_containers/cooking_container/examine(var/mob/user) - . = ..() - if (contents.len) - var/string = "It contains....
                    " - for (var/atom/movable/A in contents) - string += "[A.name]
                    " - . += "[string]" - if (reagents.total_volume) - . += "It contains [reagents.total_volume]u of reagents." - - -/obj/item/weapon/reagent_containers/cooking_container/attackby(var/obj/item/I as obj, var/mob/user as mob) - if(istype(I, /obj/item/weapon/gripper)) - var/obj/item/weapon/gripper/GR = I - if(GR.wrapped) - GR.wrapped.forceMove(get_turf(src)) - attackby(GR.wrapped, user) - if(QDELETED(GR.wrapped)) - GR.wrapped = null - - if(GR?.wrapped.loc != src) - GR.wrapped = null - - return - - for (var/possible_type in insertable) - if (istype(I, possible_type)) - if (!can_fit(I)) - to_chat(user, "There's no more space in the [src] for that!") - return 0 - - if(!user.unEquip(I) && !isturf(I.loc)) - return - I.forceMove(src) - to_chat(user, "You put the [I] into the [src].") - food_items += 1 - update_icon() - return - -/obj/item/weapon/reagent_containers/cooking_container/verb/empty() - set src in oview(1) - set name = "Empty Container" - set category = "Object" - set desc = "Removes items from the container, excluding reagents." - - do_empty(usr) - -/obj/item/weapon/reagent_containers/cooking_container/proc/do_empty(mob/user) - if (!isliving(user)) - //Here we only check for ghosts. Animals are intentionally allowed to remove things from oven trays so they can eat it - return - - if (user.stat || user.restrained()) - to_chat(user, "You are in no fit state to do this.") - return - - if (!Adjacent(user)) - to_chat(user, "You can't reach [src] from here.") - return - - if (!contents.len) - to_chat(user, "There's nothing in the [src] you can remove!") - return - - for (var/atom/movable/A in contents) - A.forceMove(get_turf(src)) - - to_chat(user, "You remove all the solid items from the [src].") - -/obj/item/weapon/reagent_containers/cooking_container/proc/check_contents() - if (contents.len == 0) - if (!reagents || reagents.total_volume == 0) - return 0//Completely empty - else if (contents.len == 1) - if (!reagents || reagents.total_volume == 0) - return 1//Contains only a single object which can be extracted alone - return 2//Contains multiple objects and/or reagents - -/obj/item/weapon/reagent_containers/cooking_container/AltClick(var/mob/user) - do_empty(user) - food_items = 0 - update_icon() - -//Deletes contents of container. -//Used when food is burned, before replacing it with a burned mess -/obj/item/weapon/reagent_containers/cooking_container/proc/clear() - for (var/atom/a in contents) - qdel(a) - - if (reagents) - reagents.clear_reagents() - -/obj/item/weapon/reagent_containers/cooking_container/proc/label(var/number, var/CT = null) - //This returns something like "Fryer basket 1 - empty" - //The latter part is a brief reminder of contents - //This is used in the removal menu - . = shortname - if (!isnull(number)) - .+= " [number]" - .+= " - " - if (CT) - .+=CT - else if (contents.len) - for (var/obj/O in contents) - .+=O.name//Just append the name of the first object - return - else if (reagents && reagents.total_volume > 0) - var/datum/reagent/R = reagents.get_master_reagent() - .+=R.name//Append name of most voluminous reagent - return - else - . += "empty" - - -/obj/item/weapon/reagent_containers/cooking_container/proc/can_fit(var/obj/item/I) - var/total = 0 - for (var/obj/item/J in contents) - total += J.w_class - - if((max_space - total) >= I.w_class) - return 1 - - -//Takes a reagent holder as input and distributes its contents among the items in the container -//Distribution is weighted based on the volume already present in each item -/obj/item/weapon/reagent_containers/cooking_container/proc/soak_reagent(var/datum/reagents/holder) - var/total = 0 - var/list/weights = list() - for (var/obj/item/I in contents) - if (I.reagents && I.reagents.total_volume) - total += I.reagents.total_volume - weights[I] = I.reagents.total_volume - - if (total > 0) - for (var/obj/item/I in contents) - if (weights[I]) - holder.trans_to(I, weights[I] / total) - -/obj/item/weapon/reagent_containers/cooking_container/update_icon() - overlays.Cut() - - if(food_items) - var/image/filling = image('icons/obj/cooking_machines.dmi', src, "[icon_state]10") - - var/percent = round((food_items / max_space) * 100) - switch(percent) - if(0 to 2) filling.icon_state = "[icon_state]" - if(3 to 24) filling.icon_state = "[icon_state]1" - if(25 to 49) filling.icon_state = "[icon_state]2" - if(50 to 74) filling.icon_state = "[icon_state]3" - if(75 to 79) filling.icon_state = "[icon_state]4" - if(80 to INFINITY) filling.icon_state = "[icon_state]5" - - overlays += filling - -/obj/item/weapon/reagent_containers/cooking_container/oven - name = "oven dish" - shortname = "shelf" - desc = "Put ingredients in this; designed for use with an oven. Warranty void if used incorrectly. Alt click to remove contents." - icon_state = "ovendish" - max_space = 30 - max_reagents = 120 - -/obj/item/weapon/reagent_containers/cooking_container/oven/Initialize() - . = ..() - - // We add to the insertable list specifically for the oven trays, to allow specialty cakes. - insertable += list( - /obj/item/organ/internal/brain // As before, needed for braincake - ) - -/obj/item/weapon/reagent_containers/cooking_container/fryer - name = "fryer basket" - shortname = "basket" - desc = "Put ingredients in this; designed for use with a deep fryer. Warranty void if used incorrectly. Alt click to remove contents." - icon_state = "basket" - -/obj/item/weapon/reagent_containers/cooking_container/grill - name = "grill rack" - shortname = "rack" - desc = "Put ingredients 'in'/on this; designed for use with a grill. Warranty void if used incorrectly. Alt click to remove contents." - icon_state = "grillrack" - -/obj/item/weapon/reagent_containers/cooking_container/grill/Initialize() - . = ..() - - // Needed for the special recipes of the grill - insertable += list( - /obj/item/organ/internal/brain, - /obj/item/robot_parts/head, - /obj/item/weapon/ectoplasm, - /obj/item/weapon/holder/mouse, - /obj/item/stack/rods - ) +//Cooking containers are used in ovens and fryers, to hold multiple ingredients for a recipe. +//They work fairly similar to the microwave - acting as a container for objects and reagents, +//which can be checked against recipe requirements in order to cook recipes that require several things + +/obj/item/weapon/reagent_containers/cooking_container + icon = 'icons/obj/cooking_machines.dmi' + var/shortname + var/max_space = 20//Maximum sum of w-classes of foods in this container at once + var/max_reagents = 80//Maximum units of reagents + var/food_items = 0 // Used for icon updates + flags = OPENCONTAINER | NOREACT + var/list/insertable = list( + /obj/item/weapon/reagent_containers/food/snacks, + /obj/item/weapon/holder, + /obj/item/weapon/paper, + /obj/item/clothing/head/wizard, + /obj/item/clothing/head/cakehat, + /obj/item/clothing/mask/gas/clown_hat, + /obj/item/clothing/head/beret + ) + +/obj/item/weapon/reagent_containers/cooking_container/Initialize() + . = ..() + create_reagents(max_reagents) + flags |= OPENCONTAINER | NOREACT + + +/obj/item/weapon/reagent_containers/cooking_container/examine(var/mob/user) + . = ..() + if (contents.len) + var/string = "It contains....
                    " + for (var/atom/movable/A in contents) + string += "[A.name]
                    " + . += "[string]" + if (reagents.total_volume) + . += "It contains [reagents.total_volume]u of reagents." + + +/obj/item/weapon/reagent_containers/cooking_container/attackby(var/obj/item/I as obj, var/mob/user as mob) + if(istype(I, /obj/item/weapon/gripper)) + var/obj/item/weapon/gripper/GR = I + if(GR.wrapped) + GR.wrapped.forceMove(get_turf(src)) + attackby(GR.wrapped, user) + if(QDELETED(GR.wrapped)) + GR.wrapped = null + + if(GR?.wrapped.loc != src) + GR.wrapped = null + + return + + for (var/possible_type in insertable) + if (istype(I, possible_type)) + if (!can_fit(I)) + to_chat(user, "There's no more space in the [src] for that!") + return 0 + + if(!user.unEquip(I) && !isturf(I.loc)) + return + I.forceMove(src) + to_chat(user, "You put the [I] into the [src].") + food_items += 1 + update_icon() + return + +/obj/item/weapon/reagent_containers/cooking_container/verb/empty() + set src in oview(1) + set name = "Empty Container" + set category = "Object" + set desc = "Removes items from the container, excluding reagents." + + do_empty(usr) + +/obj/item/weapon/reagent_containers/cooking_container/proc/do_empty(mob/user) + if (!isliving(user)) + //Here we only check for ghosts. Animals are intentionally allowed to remove things from oven trays so they can eat it + return + + if (user.stat || user.restrained()) + to_chat(user, "You are in no fit state to do this.") + return + + if (!Adjacent(user)) + to_chat(user, "You can't reach [src] from here.") + return + + if (!contents.len) + to_chat(user, "There's nothing in the [src] you can remove!") + return + + for (var/atom/movable/A in contents) + A.forceMove(get_turf(src)) + + to_chat(user, "You remove all the solid items from the [src].") + +/obj/item/weapon/reagent_containers/cooking_container/proc/check_contents() + if (contents.len == 0) + if (!reagents || reagents.total_volume == 0) + return 0//Completely empty + else if (contents.len == 1) + if (!reagents || reagents.total_volume == 0) + return 1//Contains only a single object which can be extracted alone + return 2//Contains multiple objects and/or reagents + +/obj/item/weapon/reagent_containers/cooking_container/AltClick(var/mob/user) + do_empty(user) + food_items = 0 + update_icon() + +//Deletes contents of container. +//Used when food is burned, before replacing it with a burned mess +/obj/item/weapon/reagent_containers/cooking_container/proc/clear() + for (var/atom/a in contents) + qdel(a) + + if (reagents) + reagents.clear_reagents() + +/obj/item/weapon/reagent_containers/cooking_container/proc/label(var/number, var/CT = null) + //This returns something like "Fryer basket 1 - empty" + //The latter part is a brief reminder of contents + //This is used in the removal menu + . = shortname + if (!isnull(number)) + .+= " [number]" + .+= " - " + if (CT) + .+=CT + else if (contents.len) + for (var/obj/O in contents) + .+=O.name//Just append the name of the first object + return + else if (reagents && reagents.total_volume > 0) + var/datum/reagent/R = reagents.get_master_reagent() + .+=R.name//Append name of most voluminous reagent + return + else + . += "empty" + + +/obj/item/weapon/reagent_containers/cooking_container/proc/can_fit(var/obj/item/I) + var/total = 0 + for (var/obj/item/J in contents) + total += J.w_class + + if((max_space - total) >= I.w_class) + return 1 + + +//Takes a reagent holder as input and distributes its contents among the items in the container +//Distribution is weighted based on the volume already present in each item +/obj/item/weapon/reagent_containers/cooking_container/proc/soak_reagent(var/datum/reagents/holder) + var/total = 0 + var/list/weights = list() + for (var/obj/item/I in contents) + if (I.reagents && I.reagents.total_volume) + total += I.reagents.total_volume + weights[I] = I.reagents.total_volume + + if (total > 0) + for (var/obj/item/I in contents) + if (weights[I]) + holder.trans_to(I, weights[I] / total) + +/obj/item/weapon/reagent_containers/cooking_container/update_icon() + overlays.Cut() + + if(food_items) + var/image/filling = image('icons/obj/cooking_machines.dmi', src, "[icon_state]10") + + var/percent = round((food_items / max_space) * 100) + switch(percent) + if(0 to 2) filling.icon_state = "[icon_state]" + if(3 to 24) filling.icon_state = "[icon_state]1" + if(25 to 49) filling.icon_state = "[icon_state]2" + if(50 to 74) filling.icon_state = "[icon_state]3" + if(75 to 79) filling.icon_state = "[icon_state]4" + if(80 to INFINITY) filling.icon_state = "[icon_state]5" + + overlays += filling + +/obj/item/weapon/reagent_containers/cooking_container/oven + name = "oven dish" + shortname = "shelf" + desc = "Put ingredients in this; designed for use with an oven. Warranty void if used incorrectly. Alt click to remove contents." + icon_state = "ovendish" + max_space = 30 + max_reagents = 120 + +/obj/item/weapon/reagent_containers/cooking_container/oven/Initialize() + . = ..() + + // We add to the insertable list specifically for the oven trays, to allow specialty cakes. + insertable += list( + /obj/item/organ/internal/brain // As before, needed for braincake + ) + +/obj/item/weapon/reagent_containers/cooking_container/fryer + name = "fryer basket" + shortname = "basket" + desc = "Put ingredients in this; designed for use with a deep fryer. Warranty void if used incorrectly. Alt click to remove contents." + icon_state = "basket" + +/obj/item/weapon/reagent_containers/cooking_container/grill + name = "grill rack" + shortname = "rack" + desc = "Put ingredients 'in'/on this; designed for use with a grill. Warranty void if used incorrectly. Alt click to remove contents." + icon_state = "grillrack" + +/obj/item/weapon/reagent_containers/cooking_container/grill/Initialize() + . = ..() + + // Needed for the special recipes of the grill + insertable += list( + /obj/item/organ/internal/brain, + /obj/item/robot_parts/head, + /obj/item/weapon/ectoplasm, + /obj/item/weapon/holder/mouse, + /obj/item/stack/rods + ) diff --git a/code/modules/food/kitchen/gibber.dm b/code/modules/food/kitchen/gibber.dm index c636978625a..ac4218f87fe 100644 --- a/code/modules/food/kitchen/gibber.dm +++ b/code/modules/food/kitchen/gibber.dm @@ -1,247 +1,247 @@ - -/obj/machinery/gibber - name = "gibber" - desc = "The name isn't descriptive enough?" - icon = 'icons/obj/kitchen.dmi' - icon_state = "grinder" - density = TRUE - anchored = TRUE - unacidable = TRUE - req_access = list(access_kitchen,access_morgue) - - var/operating = 0 //Is it on? - var/dirty = 0 // Does it need cleaning? - var/mob/living/occupant // Mob who has been put inside - var/gib_time = 40 // Time from starting until meat appears - var/gib_throw_dir = WEST // Direction to spit meat and gibs in. - - use_power = USE_POWER_IDLE - idle_power_usage = 2 - active_power_usage = 500 - -//auto-gibs anything that bumps into it -/obj/machinery/gibber/autogibber - var/turf/input_plate - -/obj/machinery/gibber/autogibber/Initialize() - . = ..() - for(var/i in cardinal) - var/obj/machinery/mineral/input/input_obj = locate( /obj/machinery/mineral/input, get_step(src.loc, i) ) - if(input_obj) - if(isturf(input_obj.loc)) - input_plate = input_obj.loc - gib_throw_dir = i - qdel(input_obj) - break - - if(!input_plate) - log_misc("a [src] didn't find an input plate.") - -/obj/machinery/gibber/Destroy() - occupant = null - return ..() - -/obj/machinery/gibber/autogibber/Destroy() - input_plate = null - return ..() - -/obj/machinery/gibber/autogibber/Bumped(var/atom/A) - if(!input_plate) return - - if(ismob(A)) - var/mob/M = A - - if(M.loc == input_plate - ) - M.loc = src - M.gib() - - -/obj/machinery/gibber/New() - ..() - add_overlay("grjam") - -/obj/machinery/gibber/update_icon() - cut_overlays() - if (dirty) - add_overlay("grbloody") - if(stat & (NOPOWER|BROKEN)) - return - if (!occupant) - add_overlay("grjam") - else if (operating) - add_overlay("gruse") - else - add_overlay("gridle") - -/obj/machinery/gibber/relaymove(mob/user as mob) - src.go_out() - return - -/obj/machinery/gibber/attack_hand(mob/user as mob) - if(stat & (NOPOWER|BROKEN)) - return - if(operating) - to_chat(user, "The gibber is locked and running, wait for it to finish.") - return - else - src.startgibbing(user) - -/obj/machinery/gibber/examine() - . = ..() - . += "The safety guard is [emagged ? "disabled" : "enabled"]." - -/obj/machinery/gibber/emag_act(var/remaining_charges, var/mob/user) - emagged = !emagged - to_chat(user, "You [emagged ? "disable" : "enable"] the gibber safety guard.") - return 1 - -/obj/machinery/gibber/attackby(var/obj/item/W, var/mob/user) - var/obj/item/weapon/grab/G = W - - if(default_unfasten_wrench(user, W, 40)) - return - - if(!istype(G)) - return ..() - - if(G.state < 2) - to_chat(user, "You need a better grip to do that!") - return - - move_into_gibber(user,G.affecting) - // Grab() process should clean up the grab item, no need to del it. - -/obj/machinery/gibber/MouseDrop_T(mob/target, mob/user) - if(user.stat || user.restrained()) - return - move_into_gibber(user,target) - -/obj/machinery/gibber/proc/move_into_gibber(var/mob/user,var/mob/living/victim) - - if(src.occupant) - to_chat(user, "The gibber is full, empty it first!") - return - - if(operating) - to_chat(user, "The gibber is locked and running, wait for it to finish.") - return - - if(!(istype(victim, /mob/living/carbon)) && !(istype(victim, /mob/living/simple_mob)) ) - to_chat(user, "This is not suitable for the gibber!") - return - - if(istype(victim,/mob/living/carbon/human) && !emagged) - to_chat(user, "The gibber safety guard is engaged!") - return - - - if(victim.abiotic(1)) - to_chat(user, "Subject may not have abiotic items on.") - return - - user.visible_message("[user] starts to put [victim] into the gibber!") - src.add_fingerprint(user) - if(do_after(user, 30) && victim.Adjacent(src) && user.Adjacent(src) && victim.Adjacent(user) && !occupant) - user.visible_message("[user] stuffs [victim] into the gibber!") - if(victim.client) - victim.client.perspective = EYE_PERSPECTIVE - victim.client.eye = src - victim.loc = src - src.occupant = victim - update_icon() - -/obj/machinery/gibber/verb/eject() - set category = "Object" - set name = "Empty Gibber" - set src in oview(1) - - if (usr.stat != 0) - return - src.go_out() - add_fingerprint(usr) - return - -/obj/machinery/gibber/proc/go_out() - if(operating || !src.occupant) - return - for(var/obj/O in src) - O.loc = src.loc - if (src.occupant.client) - src.occupant.client.eye = src.occupant.client.mob - src.occupant.client.perspective = MOB_PERSPECTIVE - src.occupant.loc = src.loc - src.occupant = null - update_icon() - return - - -/obj/machinery/gibber/proc/startgibbing(mob/user as mob) - if(src.operating) - return - if(!src.occupant) - visible_message("You hear a loud metallic grinding sound.") - return - - use_power(1000) - visible_message("You hear a loud [occupant.isSynthetic() ? "metallic" : "squelchy"] grinding sound.") - src.operating = 1 - update_icon() - - var/slab_name = occupant.name - var/slab_count = 2 + occupant.meat_amount - var/slab_type = occupant.meat_type ? occupant.meat_type : /obj/item/weapon/reagent_containers/food/snacks/meat - var/slab_nutrition = src.occupant.nutrition / 15 - - var/list/byproducts = occupant?.butchery_loot?.Copy() - - if(istype(src.occupant,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = occupant - slab_name = src.occupant.real_name - slab_type = H.isSynthetic() ? /obj/item/stack/material/steel : H.species.meat_type - - // Small mobs don't give as much nutrition. - if(issmall(src.occupant)) - slab_nutrition *= 0.5 - slab_nutrition /= slab_count - - for(var/i=1 to slab_count) - var/obj/item/weapon/reagent_containers/food/snacks/meat/new_meat = new slab_type(src, rand(3,8)) - if(istype(new_meat)) - new_meat.name = "[slab_name] [new_meat.name]" - new_meat.reagents.add_reagent("nutriment",slab_nutrition) - if(src.occupant.reagents) - src.occupant.reagents.trans_to_obj(new_meat, round(occupant.reagents.total_volume/(2 + occupant.meat_amount),1)) - - add_attack_logs(user,occupant,"Used [src] to gib") - - src.occupant.ghostize() - - spawn(gib_time) - occupant.gib() - occupant = null - playsound(src, 'sound/effects/splat.ogg', 50, 1) - operating = 0 - if(LAZYLEN(byproducts)) - for(var/path in byproducts) - while(byproducts[path]) - if(prob(min(90,30 * byproducts[path]))) - new path(src) - - byproducts[path] -= 1 - - for (var/obj/thing in contents) - // There's a chance that the gibber will fail to destroy or butcher some evidence. - if(istype(thing,/obj/item/organ) && prob(80)) - var/obj/item/organ/OR = thing - if(OR.can_butcher(src)) - OR.butcher(src, null, src) // Butcher it, and add it to our list of things to launch. - else - qdel(thing) - continue - thing.forceMove(get_turf(thing)) // Drop it onto the turf for throwing. - thing.throw_at(get_edge_target_turf(src,gib_throw_dir),rand(0,3),emagged ? 100 : 50) // Being pelted with bits of meat and bone would hurt. - - update_icon() - - + +/obj/machinery/gibber + name = "gibber" + desc = "The name isn't descriptive enough?" + icon = 'icons/obj/kitchen.dmi' + icon_state = "grinder" + density = TRUE + anchored = TRUE + unacidable = TRUE + req_access = list(access_kitchen,access_morgue) + + var/operating = 0 //Is it on? + var/dirty = 0 // Does it need cleaning? + var/mob/living/occupant // Mob who has been put inside + var/gib_time = 40 // Time from starting until meat appears + var/gib_throw_dir = WEST // Direction to spit meat and gibs in. + + use_power = USE_POWER_IDLE + idle_power_usage = 2 + active_power_usage = 500 + +//auto-gibs anything that bumps into it +/obj/machinery/gibber/autogibber + var/turf/input_plate + +/obj/machinery/gibber/autogibber/Initialize() + . = ..() + for(var/i in cardinal) + var/obj/machinery/mineral/input/input_obj = locate( /obj/machinery/mineral/input, get_step(src.loc, i) ) + if(input_obj) + if(isturf(input_obj.loc)) + input_plate = input_obj.loc + gib_throw_dir = i + qdel(input_obj) + break + + if(!input_plate) + log_misc("a [src] didn't find an input plate.") + +/obj/machinery/gibber/Destroy() + occupant = null + return ..() + +/obj/machinery/gibber/autogibber/Destroy() + input_plate = null + return ..() + +/obj/machinery/gibber/autogibber/Bumped(var/atom/A) + if(!input_plate) return + + if(ismob(A)) + var/mob/M = A + + if(M.loc == input_plate + ) + M.loc = src + M.gib() + + +/obj/machinery/gibber/New() + ..() + add_overlay("grjam") + +/obj/machinery/gibber/update_icon() + cut_overlays() + if (dirty) + add_overlay("grbloody") + if(stat & (NOPOWER|BROKEN)) + return + if (!occupant) + add_overlay("grjam") + else if (operating) + add_overlay("gruse") + else + add_overlay("gridle") + +/obj/machinery/gibber/relaymove(mob/user as mob) + src.go_out() + return + +/obj/machinery/gibber/attack_hand(mob/user as mob) + if(stat & (NOPOWER|BROKEN)) + return + if(operating) + to_chat(user, "The gibber is locked and running, wait for it to finish.") + return + else + src.startgibbing(user) + +/obj/machinery/gibber/examine() + . = ..() + . += "The safety guard is [emagged ? "disabled" : "enabled"]." + +/obj/machinery/gibber/emag_act(var/remaining_charges, var/mob/user) + emagged = !emagged + to_chat(user, "You [emagged ? "disable" : "enable"] the gibber safety guard.") + return 1 + +/obj/machinery/gibber/attackby(var/obj/item/W, var/mob/user) + var/obj/item/weapon/grab/G = W + + if(default_unfasten_wrench(user, W, 40)) + return + + if(!istype(G)) + return ..() + + if(G.state < 2) + to_chat(user, "You need a better grip to do that!") + return + + move_into_gibber(user,G.affecting) + // Grab() process should clean up the grab item, no need to del it. + +/obj/machinery/gibber/MouseDrop_T(mob/target, mob/user) + if(user.stat || user.restrained()) + return + move_into_gibber(user,target) + +/obj/machinery/gibber/proc/move_into_gibber(var/mob/user,var/mob/living/victim) + + if(src.occupant) + to_chat(user, "The gibber is full, empty it first!") + return + + if(operating) + to_chat(user, "The gibber is locked and running, wait for it to finish.") + return + + if(!(istype(victim, /mob/living/carbon)) && !(istype(victim, /mob/living/simple_mob)) ) + to_chat(user, "This is not suitable for the gibber!") + return + + if(istype(victim,/mob/living/carbon/human) && !emagged) + to_chat(user, "The gibber safety guard is engaged!") + return + + + if(victim.abiotic(1)) + to_chat(user, "Subject may not have abiotic items on.") + return + + user.visible_message("[user] starts to put [victim] into the gibber!") + src.add_fingerprint(user) + if(do_after(user, 30) && victim.Adjacent(src) && user.Adjacent(src) && victim.Adjacent(user) && !occupant) + user.visible_message("[user] stuffs [victim] into the gibber!") + if(victim.client) + victim.client.perspective = EYE_PERSPECTIVE + victim.client.eye = src + victim.loc = src + src.occupant = victim + update_icon() + +/obj/machinery/gibber/verb/eject() + set category = "Object" + set name = "Empty Gibber" + set src in oview(1) + + if (usr.stat != 0) + return + src.go_out() + add_fingerprint(usr) + return + +/obj/machinery/gibber/proc/go_out() + if(operating || !src.occupant) + return + for(var/obj/O in src) + O.loc = src.loc + if (src.occupant.client) + src.occupant.client.eye = src.occupant.client.mob + src.occupant.client.perspective = MOB_PERSPECTIVE + src.occupant.loc = src.loc + src.occupant = null + update_icon() + return + + +/obj/machinery/gibber/proc/startgibbing(mob/user as mob) + if(src.operating) + return + if(!src.occupant) + visible_message("You hear a loud metallic grinding sound.") + return + + use_power(1000) + visible_message("You hear a loud [occupant.isSynthetic() ? "metallic" : "squelchy"] grinding sound.") + src.operating = 1 + update_icon() + + var/slab_name = occupant.name + var/slab_count = 2 + occupant.meat_amount + var/slab_type = occupant.meat_type ? occupant.meat_type : /obj/item/weapon/reagent_containers/food/snacks/meat + var/slab_nutrition = src.occupant.nutrition / 15 + + var/list/byproducts = occupant?.butchery_loot?.Copy() + + if(istype(src.occupant,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = occupant + slab_name = src.occupant.real_name + slab_type = H.isSynthetic() ? /obj/item/stack/material/steel : H.species.meat_type + + // Small mobs don't give as much nutrition. + if(issmall(src.occupant)) + slab_nutrition *= 0.5 + slab_nutrition /= slab_count + + for(var/i=1 to slab_count) + var/obj/item/weapon/reagent_containers/food/snacks/meat/new_meat = new slab_type(src, rand(3,8)) + if(istype(new_meat)) + new_meat.name = "[slab_name] [new_meat.name]" + new_meat.reagents.add_reagent("nutriment",slab_nutrition) + if(src.occupant.reagents) + src.occupant.reagents.trans_to_obj(new_meat, round(occupant.reagents.total_volume/(2 + occupant.meat_amount),1)) + + add_attack_logs(user,occupant,"Used [src] to gib") + + src.occupant.ghostize() + + spawn(gib_time) + occupant.gib() + occupant = null + playsound(src, 'sound/effects/splat.ogg', 50, 1) + operating = 0 + if(LAZYLEN(byproducts)) + for(var/path in byproducts) + while(byproducts[path]) + if(prob(min(90,30 * byproducts[path]))) + new path(src) + + byproducts[path] -= 1 + + for (var/obj/thing in contents) + // There's a chance that the gibber will fail to destroy or butcher some evidence. + if(istype(thing,/obj/item/organ) && prob(80)) + var/obj/item/organ/OR = thing + if(OR.can_butcher(src)) + OR.butcher(src, null, src) // Butcher it, and add it to our list of things to launch. + else + qdel(thing) + continue + thing.forceMove(get_turf(thing)) // Drop it onto the turf for throwing. + thing.throw_at(get_edge_target_turf(src,gib_throw_dir),rand(0,3),emagged ? 100 : 50) // Being pelted with bits of meat and bone would hurt. + + update_icon() + + diff --git a/code/modules/food/kitchen/microwave.dm b/code/modules/food/kitchen/microwave.dm index 4ee7ad9de08..51073b48d16 100644 --- a/code/modules/food/kitchen/microwave.dm +++ b/code/modules/food/kitchen/microwave.dm @@ -1,671 +1,671 @@ -/obj/machinery/microwave - name = "Microwave" - desc = "Studies are inconclusive on whether pressing your face against the glass is harmful." - icon = 'icons/obj/kitchen.dmi' - icon_state = "mw" - layer = 2.9 - density = TRUE - anchored = TRUE - unacidable = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 5 - active_power_usage = 2000 - clicksound = "button" - clickvol = "30" - flags = OPENCONTAINER | NOREACT - circuit = /obj/item/weapon/circuitboard/microwave - var/operating = 0 // Is it on? - var/dirty = 0 // = {0..100} Does it need cleaning? - var/broken = 0 // ={0,1,2} How broken is it??? - var/circuit_item_capacity = 1 //how many items does the circuit add to max number of items - var/item_level = 0 // items microwave can handle, 0 foodstuff, 1 materials - var/global/list/acceptable_items // List of the items you can put in - var/global/list/available_recipes // List of the recipes you can use - var/global/list/acceptable_reagents // List of the reagents you can put in - - var/global/max_n_of_items = 20 - var/appliancetype = MICROWAVE - var/datum/looping_sound/microwave/soundloop - - -//see code/modules/food/recipes_microwave.dm for recipes - -/******************* -* Initialising -********************/ - -/obj/machinery/microwave/Initialize() - . = ..() - - reagents = new/datum/reagents(100) - reagents.my_atom = src - - default_apply_parts() - - if(!available_recipes) - available_recipes = new - for(var/datum/recipe/typepath as anything in subtypesof(/datum/recipe)) - if((initial(typepath.appliance) & appliancetype)) - available_recipes += new typepath - - acceptable_items = new - acceptable_reagents = new - for (var/datum/recipe/recipe in available_recipes) - for (var/item in recipe.items) - acceptable_items |= item - for (var/reagent in recipe.reagents) - acceptable_reagents |= reagent - // This will do until I can think of a fun recipe to use dionaea in - - // will also allow anything using the holder item to be microwaved into - // impure carbon. ~Z - acceptable_items |= /obj/item/weapon/holder - acceptable_items |= /obj/item/weapon/reagent_containers/food/snacks/grown - acceptable_items |= /obj/item/device/soulstone - acceptable_items |= /obj/item/weapon/fuel_assembly/supermatter - - soundloop = new(list(src), FALSE) - -/obj/machinery/microwave/Destroy() - if(paicard) - ejectpai() // Lets not delete the pAI. - QDEL_NULL(soundloop) - return ..() - -/******************* -* Item Adding -********************/ - -/obj/machinery/microwave/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(src.broken > 0) - if(src.broken == 2 && O.has_tool_quality(TOOL_SCREWDRIVER)) // If it's broken and they're using a screwdriver - user.visible_message( \ - "\The [user] starts to fix part of the microwave.", \ - "You start to fix part of the microwave." \ - ) - playsound(src, O.usesound, 50, 1) - if (do_after(user,20 * O.toolspeed)) - user.visible_message( \ - "\The [user] fixes part of the microwave.", \ - "You have fixed part of the microwave." \ - ) - src.broken = 1 // Fix it a bit - else if(src.broken == 1 && O.has_tool_quality(TOOL_WRENCH)) // If it's broken and they're doing the wrench - user.visible_message( \ - "\The [user] starts to fix part of the microwave.", \ - "You start to fix part of the microwave." \ - ) - if (do_after(user,20 * O.toolspeed)) - user.visible_message( \ - "\The [user] fixes the microwave.", \ - "You have fixed the microwave." \ - ) - src.icon_state = "mw" - src.broken = 0 // Fix it! - src.dirty = 0 // just to be sure - src.flags = OPENCONTAINER | NOREACT - else - to_chat(user, "It's broken!") - return 1 - - else if(src.dirty==100) // The microwave is all dirty so can't be used! - if(istype(O, /obj/item/weapon/reagent_containers/spray/cleaner) || istype(O, /obj/item/weapon/soap)) // If they're trying to clean it then let them - user.visible_message( \ - "\The [user] starts to clean the microwave.", \ - "You start to clean the microwave." \ - ) - if (do_after(user,20)) - user.visible_message( \ - "\The [user] has cleaned the microwave.", \ - "You have cleaned the microwave." \ - ) - src.dirty = 0 // It's clean! - src.broken = 0 // just to be sure - src.icon_state = "mw" - src.flags = OPENCONTAINER | NOREACT - SStgui.update_uis(src) - else //Otherwise bad luck!! - to_chat(user, "It's dirty!") - return 1 - else if(is_type_in_list(O,acceptable_items)) - var/list/workingList = cookingContents() - if(workingList.len>=(max_n_of_items + circuit_item_capacity)) //Adds component_parts to the maximum number of items. changed 1 to actually just be the circuit item capacity var. - to_chat(user, "This [src] is full of ingredients, you cannot put more.") - return 1 - if(istype(O, /obj/item/stack) && O:get_amount() > 1) // This is bad, but I can't think of how to change it - var/obj/item/stack/S = O - new O.type (src) - S.use(1) - user.visible_message( \ - "\The [user] has added one of [O] to \the [src].", \ - "You add one of [O] to \the [src].") - return - else - // user.remove_from_mob(O) //This just causes problems so far as I can tell. -Pete - Man whoever you are, it's been years. o7 - user.drop_from_inventory(O,src) - user.visible_message( \ - "\The [user] has added \the [O] to \the [src].", \ - "You add \the [O] to \the [src].") - SStgui.update_uis(src) - return - else if (istype(O,/obj/item/weapon/storage/bag/plants)) // There might be a better way about making plant bags dump their contents into a microwave, but it works. - var/obj/item/weapon/storage/bag/plants/bag = O - var/failed = 1 - for(var/obj/item/G in O.contents) - if(!G.reagents || !G.reagents.total_volume) - continue - failed = 0 - if(contents.len>=(max_n_of_items + component_parts.len + circuit_item_capacity)) - to_chat(user, "This [src] is full of ingredients, you cannot put more.") - return 0 - else - bag.remove_from_storage(G, src) - contents += G - if(contents.len>=(max_n_of_items + component_parts.len + circuit_item_capacity)) - break - - if(failed) - to_chat(user, "Nothing in the plant bag is usable.") - return 0 - - if(!O.contents.len) - to_chat(user, "You empty \the [O] into \the [src].") - else - to_chat(user, "You fill \the [src] from \the [O].") - - SStgui.update_uis(src) - return 0 - - else if(istype(O,/obj/item/weapon/reagent_containers/glass) || \ - istype(O,/obj/item/weapon/reagent_containers/food/drinks) || \ - istype(O,/obj/item/weapon/reagent_containers/food/condiment) \ - ) - if (!O.reagents) - return 1 - for (var/datum/reagent/R in O.reagents.reagent_list) - if (!(R.id in acceptable_reagents)) - to_chat(user, "Your [O] contains components unsuitable for cookery.") - return 1 - return - else if(istype(O,/obj/item/weapon/grab)) - var/obj/item/weapon/grab/G = O - to_chat(user, "This is ridiculous. You can not fit \the [G.affecting] in this [src].") - return 1 - else if(O.has_tool_quality(TOOL_SCREWDRIVER)) - default_deconstruction_screwdriver(user, O) - return - else if(O.has_tool_quality(TOOL_CROWBAR)) - if(default_deconstruction_crowbar(user, O)) - return - else - user.visible_message( \ - "\The [user] begins [src.anchored ? "unsecuring" : "securing"] the microwave.", \ - "You attempt to [src.anchored ? "unsecure" : "secure"] the microwave." - ) - if (do_after(user,20/O.toolspeed)) - user.visible_message( \ - "\The [user] [src.anchored ? "unsecures" : "secures"] the microwave.", \ - "You [src.anchored ? "unsecure" : "secure"] the microwave." - ) - src.anchored = !src.anchored - else - to_chat(user, "You decide not to do that.") - else if(default_part_replacement(user, O)) - return - else if(istype(O, /obj/item/device/paicard)) - if(!paicard) - insertpai(user, O) - else - to_chat(user, "You have no idea what you can cook with this [O].") - ..() - SStgui.update_uis(src) - -/obj/machinery/microwave/tgui_state(mob/user) - return GLOB.tgui_physical_state - -/obj/machinery/microwave/attack_ai(mob/user as mob) - attack_hand(user) - -/obj/machinery/microwave/attack_hand(mob/user as mob) - if(user.a_intent == I_GRAB) - if(paicard) - ejectpai(user) - return - user.set_machine(src) - tgui_interact(user) - -/******************* -* Microwave Menu -********************/ -/obj/machinery/microwave/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Microwave", name) - ui.open() - -/obj/machinery/microwave/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) - var/list/data = ..() - - data["broken"] = broken - data["operating"] = operating - data["dirty"] = dirty == 100 - data["items"] = get_items_list() - - return data - -/obj/machinery/microwave/proc/get_items_list() - var/list/data = list() - - var/list/items_counts = list() - var/list/items_measures = list() - var/list/items_measures_p = list() - //for(var/obj/O in ((contents - component_parts) - circuit)) - for(var/obj/O in cookingContents()) - var/display_name = O.name - if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/egg)) - items_measures[display_name] = "egg" - items_measures_p[display_name] = "eggs" - if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/tofu)) - items_measures[display_name] = "tofu chunk" - items_measures_p[display_name] = "tofu chunks" - if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/meat)) //any meat - items_measures[display_name] = "slab of meat" - items_measures_p[display_name] = "slabs of meat" - if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/donkpocket)) - display_name = "Turnovers" - items_measures[display_name] = "turnover" - items_measures_p[display_name] = "turnovers" - if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/carpmeat)) - items_measures[display_name] = "fillet of meat" - items_measures_p[display_name] = "fillets of meat" - items_counts[display_name]++ - for(var/O in items_counts) - var/N = items_counts[O] - if(!(O in items_measures)) - data.Add(list(list( - "name" = capitalize(O), - "amt" = N, - "extra" = "[lowertext(O)][N > 1 ? "s" : ""]", - ))) - else - data.Add(list(list( - "name" = capitalize(O), - "amt" = N, - "extra" = N == 1 ? items_measures[O] : items_measures_p[O], - ))) - - for(var/datum/reagent/R in reagents.reagent_list) - var/display_name = R.name - if(R.id == "capsaicin") - display_name = "Hotsauce" - if(R.id == "frostoil") - display_name = "Coldsauce" - data.Add(list(list( - "name" = display_name, - "amt" = R.volume, - "extra" = "unit[R.volume > 1 ? "s" : ""]" - ))) - - return data - -/obj/machinery/microwave/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - if(operating) - return TRUE - - switch(action) - if("cook") - cook() - return TRUE - - if("dispose") - dispose() - return TRUE -/* -/obj/machinery/microwave/interact(mob/user as mob) // The microwave Menu - var/dat = "" - if(src.broken > 0) - dat = {"Bzzzzttttt"} - else if(src.operating) - dat = {"Microwaving in progress!
                    Please wait...!
                    "} - else if(src.dirty==100) - dat = {"This microwave is dirty!
                    Please clean it before use!
                    "} - else - var/list/items_counts = new - var/list/items_measures = new - var/list/items_measures_p = new - for (var/obj/O in ((contents - component_parts) - circuit)) - var/display_name = O.name - if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/egg)) - items_measures[display_name] = "egg" - items_measures_p[display_name] = "eggs" - if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/tofu)) - items_measures[display_name] = "tofu chunk" - items_measures_p[display_name] = "tofu chunks" - if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/meat)) //any meat - items_measures[display_name] = "slab of meat" - items_measures_p[display_name] = "slabs of meat" - if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/donkpocket)) - display_name = "Turnovers" - items_measures[display_name] = "turnover" - items_measures_p[display_name] = "turnovers" - if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/carpmeat)) - items_measures[display_name] = "fillet of meat" - items_measures_p[display_name] = "fillets of meat" - items_counts[display_name]++ - for (var/O in items_counts) - var/N = items_counts[O] - if (!(O in items_measures)) - dat += {"[capitalize(O)]: [N] [lowertext(O)]\s
                    "} - else - if (N==1) - dat += {"[capitalize(O)]: [N] [items_measures[O]]
                    "} - else - dat += {"[capitalize(O)]: [N] [items_measures_p[O]]
                    "} - - for (var/datum/reagent/R in reagents.reagent_list) - var/display_name = R.name - if (R.id == "capsaicin") - display_name = "Hotsauce" - if (R.id == "frostoil") - display_name = "Coldsauce" - dat += {"[display_name]: [R.volume] unit\s
                    "} - - if (items_counts.len==0 && reagents.reagent_list.len==0) - dat = {"The microwave is empty
                    "} - else - dat = {"Ingredients:
                    [dat]"} - dat += {"

                    \ -Turn on!
                    \ -
                    Eject ingredients!
                    \ -"} - - user << browse("Microwave Controls[dat]", "window=microwave") - onclose(user, "microwave") - return -*/ - -/*********************************** -* Microwave Menu Handling/Cooking -************************************/ - -/obj/machinery/microwave/proc/cook() - if(stat & (NOPOWER|BROKEN)) - return - start() - if(reagents.total_volume==0 && !(locate(/obj) in cookingContents())) //dry run - if(!wzhzhzh(16)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 5) - abort() - return - abort() - return - - var/datum/recipe/recipe = select_recipe(available_recipes,src) - var/obj/cooked - if(!recipe) - dirty += 1 - if(prob(max(10,dirty*5))) - if(!wzhzhzh(16)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 2) - abort() - return - muck_start() - wzhzhzh(2) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 2) - muck_finish() - cooked = fail() - cooked.forceMove(src.loc) - else if(has_extra_item()) - if(!wzhzhzh(16)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 2) - abort() - return - broke() - cooked = fail() - cooked.forceMove(src.loc) - else - if(!wzhzhzh(40)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 5) - abort() - return - stop() - cooked = fail() - cooked.forceMove(src.loc) - return - - //Making multiple copies of a recipe - var/halftime = round(recipe.time*4/10/2) // VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was round(recipe.time/20/2)) - if(!wzhzhzh(halftime)) - abort() - return - recipe.before_cook(src) - if(!wzhzhzh(halftime)) - abort() - cooked = fail() - cooked.forceMove(loc) - recipe.after_cook(src) - return - - var/result = recipe.result - var/valid = 1 - var/list/cooked_items = list() - var/obj/temp = new /obj(src) //To prevent infinite loops, all results will be moved into a temporary location so they're not considered as inputs for other recipes - while(valid) - var/list/things = list() - things.Add(recipe.make_food(src)) - cooked_items += things - //Move cooked things to the buffer so they're not considered as ingredients - for(var/atom/movable/AM in things) - AM.forceMove(temp) - - valid = 0 - recipe.after_cook(src) - recipe = select_recipe(available_recipes,src) - if(recipe && recipe.result == result) - valid = 1 - sleep(2) - - for(var/atom/movable/R as anything in cooked_items) - R.forceMove(src) //Move everything from the buffer back to the container - - QDEL_NULL(temp)//Delete buffer object - - //Any leftover reagents are divided amongst the foods - var/total = reagents.total_volume - for(var/obj/item/weapon/reagent_containers/food/snacks/S in cooked_items) - reagents.trans_to_holder(S.reagents, total/cooked_items.len) - - for(var/obj/item/weapon/reagent_containers/food/snacks/S in cookingContents()) - S.cook() - - dispose(0) //clear out anything left - stop() - - return - -/obj/machinery/microwave/proc/wzhzhzh(var/seconds as num) // Whoever named this proc is fucking literally Satan. ~ Z - for (var/i=1 to seconds) - if (stat & (NOPOWER|BROKEN)) - return 0 - use_power(active_power_usage) - sleep(5) //VOREStation Edit - Quicker Microwaves - return 1 - -/obj/machinery/microwave/proc/has_extra_item() //- coded to have different microwaves be able to handle different items - if(item_level == 0) - for (var/obj/O in cookingContents()) - if ( \ - !istype(O,/obj/item/weapon/reagent_containers/food) && \ - !istype(O, /obj/item/weapon/grown) \ - ) - return 1 - return 0 - if(item_level == 1) - for (var/obj/O in cookingContents()) - if ( \ - !istype(O, /obj/item/weapon/reagent_containers/food) && \ - !istype(O, /obj/item/weapon/grown) && \ - !istype(O, /obj/item/slime_extract) && \ - !istype(O, /obj/item/organ) && \ - !istype(O, /obj/item/stack/material) \ - ) - return 1 - return 0 - -/obj/machinery/microwave/proc/start() - src.visible_message("The microwave turns on.", "You hear a microwave.") - soundloop.start() - src.operating = TRUE - src.icon_state = "mw1" - SStgui.update_uis(src) - -/obj/machinery/microwave/proc/abort() - operating = FALSE // Turn it off again aferwards - if(icon_state == "mw1") - icon_state = "mw" - SStgui.update_uis(src) - soundloop.stop() - -/obj/machinery/microwave/proc/stop() - playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) - operating = FALSE // Turn it off again aferwards - if(icon_state == "mw1") - icon_state = "mw" - SStgui.update_uis(src) - soundloop.stop() - -/obj/machinery/microwave/proc/dispose(var/message = 1) - for (var/atom/movable/A in cookingContents()) - A.forceMove(loc) - if (src.reagents.total_volume) - src.dirty++ - src.reagents.clear_reagents() - if(message) - to_chat(usr, "You dispose of the microwave contents.") - SStgui.update_uis(src) - -/obj/machinery/microwave/proc/muck_start() - playsound(src, 'sound/effects/splat.ogg', 50, 1) // Play a splat sound - src.icon_state = "mwbloody1" // Make it look dirty!! - -/obj/machinery/microwave/proc/muck_finish() - src.visible_message("The microwave gets covered in muck!") - src.dirty = 100 // Make it dirty so it can't be used util cleaned - src.flags = null //So you can't add condiments - src.icon_state = "mwbloody0" // Make it look dirty too - src.operating = 0 // Turn it off again aferwards - SStgui.update_uis(src) - soundloop.stop() - - -/obj/machinery/microwave/proc/broke() - var/datum/effect/effect/system/spark_spread/s = new - s.set_up(2, 1, src) - s.start() - src.icon_state = "mwb" // Make it look all busted up and shit - src.visible_message("The microwave breaks!") //Let them know they're stupid - src.broken = 2 // Make it broken so it can't be used util fixed - src.flags = null //So you can't add condiments - src.operating = 0 // Turn it off again aferwards - SStgui.update_uis(src) - soundloop.stop() - src.ejectpai() // If it broke, time to yeet the PAI. - -/obj/machinery/microwave/proc/fail() - var/obj/item/weapon/reagent_containers/food/snacks/badrecipe/ffuu = new(src) - var/amount = 0 - for (var/obj/O in cookingContents() - ffuu) - amount++ - if(O.reagents) - var/id = O.reagents.get_master_reagent_id() - if(id) - amount+=O.reagents.get_reagent_amount(id) - if(istype(O, /obj/item/weapon/holder)) - var/obj/item/weapon/holder/H = O - if(H.held_mob) - qdel(H.held_mob) - qdel(O) - src.reagents.clear_reagents() - ffuu.reagents.add_reagent("carbon", amount) - ffuu.reagents.add_reagent("toxin", amount/10) - return ffuu - -/obj/machinery/microwave/verb/Eject() - set src in oview(1) - set category = "Object" - set name = "Eject content" - usr.visible_message( - "[usr] tries to open [src] and remove its contents." , - "You try to open [src] and remove its contents." - ) - - if(!do_after(usr, 1 SECONDS, target = src)) - return - - if(operating) - to_chat(usr, "You can't do that, [src] door is locked!") - return - - usr.visible_message( - "[usr] opened [src] and has taken out [english_list(cookingContents())]." , - "You have opened [src] and taken out [english_list(cookingContents())]." - ) - dispose() - -/obj/machinery/microwave/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - if(!mover) - return 1 - if(mover.checkpass(PASSTABLE)) - //Animals can run under them, lots of empty space - return 1 - return ..() - -/obj/machinery/microwave/advanced // specifically for complex recipes - name = "deluxe microwave" - icon = 'icons/obj/deluxemicrowave.dmi' - icon_state = "mw" - circuit = /obj/item/weapon/circuitboard/microwave/advanced - circuit_item_capacity = 100 - item_level = 1 - -/obj/machinery/microwave/advanced/Initialize() - . = ..() - reagents.maximum_volume = 1000 - -/datum/recipe/splat // We use this to handle cooking micros (or mice, etc) in a microwave. Janky but it works better than snowflake code to handle the same thing. - items = list( - /obj/item/weapon/holder - ) - result = /obj/effect/decal/cleanable/blood/gibs - -/datum/recipe/splat/before_cook(obj/container) - if(istype(container, /obj/machinery/microwave)) - var/obj/machinery/microwave/M = container - M.muck_start() - playsound(container.loc, 'sound/items/drop/flesh.ogg', 100, 1) - . = ..() - -/datum/recipe/splat/make_food(obj/container) - for(var/obj/item/weapon/holder/H in container) - if(H.held_mob) - to_chat(H.held_mob, "You hear an earsplitting humming and your head aches!") - qdel(H.held_mob) - H.held_mob = null - qdel(H) - - . = ..() - -/datum/recipe/splat/after_cook(obj/container) - if(istype(container, /obj/machinery/microwave)) - var/obj/machinery/microwave/M = container - M.muck_finish() - . = ..() - -/obj/machinery/microwave/proc/cookingContents() //VOREEdit, this is a better way to deal with the contents of a microwave, since the previous method is stupid. - var/list/workingList = contents.Copy() // Using the copy proc because otherwise the two lists seem to become soul bonded. - workingList -= component_parts - workingList -= circuit - if(paicard) - workingList -= paicard - for(var/M in workingList) - if(istype(M, circuit)) // Yes, we remove circuit twice. Yes, it's necessary. Yes, it's stupid. - workingList -= M - return workingList - +/obj/machinery/microwave + name = "Microwave" + desc = "Studies are inconclusive on whether pressing your face against the glass is harmful." + icon = 'icons/obj/kitchen.dmi' + icon_state = "mw" + layer = 2.9 + density = TRUE + anchored = TRUE + unacidable = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 5 + active_power_usage = 2000 + clicksound = "button" + clickvol = "30" + flags = OPENCONTAINER | NOREACT + circuit = /obj/item/weapon/circuitboard/microwave + var/operating = 0 // Is it on? + var/dirty = 0 // = {0..100} Does it need cleaning? + var/broken = 0 // ={0,1,2} How broken is it??? + var/circuit_item_capacity = 1 //how many items does the circuit add to max number of items + var/item_level = 0 // items microwave can handle, 0 foodstuff, 1 materials + var/global/list/acceptable_items // List of the items you can put in + var/global/list/available_recipes // List of the recipes you can use + var/global/list/acceptable_reagents // List of the reagents you can put in + + var/global/max_n_of_items = 20 + var/appliancetype = MICROWAVE + var/datum/looping_sound/microwave/soundloop + + +//see code/modules/food/recipes_microwave.dm for recipes + +/******************* +* Initialising +********************/ + +/obj/machinery/microwave/Initialize() + . = ..() + + reagents = new/datum/reagents(100) + reagents.my_atom = src + + default_apply_parts() + + if(!available_recipes) + available_recipes = new + for(var/datum/recipe/typepath as anything in subtypesof(/datum/recipe)) + if((initial(typepath.appliance) & appliancetype)) + available_recipes += new typepath + + acceptable_items = new + acceptable_reagents = new + for (var/datum/recipe/recipe in available_recipes) + for (var/item in recipe.items) + acceptable_items |= item + for (var/reagent in recipe.reagents) + acceptable_reagents |= reagent + // This will do until I can think of a fun recipe to use dionaea in - + // will also allow anything using the holder item to be microwaved into + // impure carbon. ~Z + acceptable_items |= /obj/item/weapon/holder + acceptable_items |= /obj/item/weapon/reagent_containers/food/snacks/grown + acceptable_items |= /obj/item/device/soulstone + acceptable_items |= /obj/item/weapon/fuel_assembly/supermatter + + soundloop = new(list(src), FALSE) + +/obj/machinery/microwave/Destroy() + if(paicard) + ejectpai() // Lets not delete the pAI. + QDEL_NULL(soundloop) + return ..() + +/******************* +* Item Adding +********************/ + +/obj/machinery/microwave/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(src.broken > 0) + if(src.broken == 2 && O.has_tool_quality(TOOL_SCREWDRIVER)) // If it's broken and they're using a screwdriver + user.visible_message( \ + "\The [user] starts to fix part of the microwave.", \ + "You start to fix part of the microwave." \ + ) + playsound(src, O.usesound, 50, 1) + if (do_after(user,20 * O.toolspeed)) + user.visible_message( \ + "\The [user] fixes part of the microwave.", \ + "You have fixed part of the microwave." \ + ) + src.broken = 1 // Fix it a bit + else if(src.broken == 1 && O.has_tool_quality(TOOL_WRENCH)) // If it's broken and they're doing the wrench + user.visible_message( \ + "\The [user] starts to fix part of the microwave.", \ + "You start to fix part of the microwave." \ + ) + if (do_after(user,20 * O.toolspeed)) + user.visible_message( \ + "\The [user] fixes the microwave.", \ + "You have fixed the microwave." \ + ) + src.icon_state = "mw" + src.broken = 0 // Fix it! + src.dirty = 0 // just to be sure + src.flags = OPENCONTAINER | NOREACT + else + to_chat(user, "It's broken!") + return 1 + + else if(src.dirty==100) // The microwave is all dirty so can't be used! + if(istype(O, /obj/item/weapon/reagent_containers/spray/cleaner) || istype(O, /obj/item/weapon/soap)) // If they're trying to clean it then let them + user.visible_message( \ + "\The [user] starts to clean the microwave.", \ + "You start to clean the microwave." \ + ) + if (do_after(user,20)) + user.visible_message( \ + "\The [user] has cleaned the microwave.", \ + "You have cleaned the microwave." \ + ) + src.dirty = 0 // It's clean! + src.broken = 0 // just to be sure + src.icon_state = "mw" + src.flags = OPENCONTAINER | NOREACT + SStgui.update_uis(src) + else //Otherwise bad luck!! + to_chat(user, "It's dirty!") + return 1 + else if(is_type_in_list(O,acceptable_items)) + var/list/workingList = cookingContents() + if(workingList.len>=(max_n_of_items + circuit_item_capacity)) //Adds component_parts to the maximum number of items. changed 1 to actually just be the circuit item capacity var. + to_chat(user, "This [src] is full of ingredients, you cannot put more.") + return 1 + if(istype(O, /obj/item/stack) && O:get_amount() > 1) // This is bad, but I can't think of how to change it + var/obj/item/stack/S = O + new O.type (src) + S.use(1) + user.visible_message( \ + "\The [user] has added one of [O] to \the [src].", \ + "You add one of [O] to \the [src].") + return + else + // user.remove_from_mob(O) //This just causes problems so far as I can tell. -Pete - Man whoever you are, it's been years. o7 + user.drop_from_inventory(O,src) + user.visible_message( \ + "\The [user] has added \the [O] to \the [src].", \ + "You add \the [O] to \the [src].") + SStgui.update_uis(src) + return + else if (istype(O,/obj/item/weapon/storage/bag/plants)) // There might be a better way about making plant bags dump their contents into a microwave, but it works. + var/obj/item/weapon/storage/bag/plants/bag = O + var/failed = 1 + for(var/obj/item/G in O.contents) + if(!G.reagents || !G.reagents.total_volume) + continue + failed = 0 + if(contents.len>=(max_n_of_items + component_parts.len + circuit_item_capacity)) + to_chat(user, "This [src] is full of ingredients, you cannot put more.") + return 0 + else + bag.remove_from_storage(G, src) + contents += G + if(contents.len>=(max_n_of_items + component_parts.len + circuit_item_capacity)) + break + + if(failed) + to_chat(user, "Nothing in the plant bag is usable.") + return 0 + + if(!O.contents.len) + to_chat(user, "You empty \the [O] into \the [src].") + else + to_chat(user, "You fill \the [src] from \the [O].") + + SStgui.update_uis(src) + return 0 + + else if(istype(O,/obj/item/weapon/reagent_containers/glass) || \ + istype(O,/obj/item/weapon/reagent_containers/food/drinks) || \ + istype(O,/obj/item/weapon/reagent_containers/food/condiment) \ + ) + if (!O.reagents) + return 1 + for (var/datum/reagent/R in O.reagents.reagent_list) + if (!(R.id in acceptable_reagents)) + to_chat(user, "Your [O] contains components unsuitable for cookery.") + return 1 + return + else if(istype(O,/obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = O + to_chat(user, "This is ridiculous. You can not fit \the [G.affecting] in this [src].") + return 1 + else if(O.has_tool_quality(TOOL_SCREWDRIVER)) + default_deconstruction_screwdriver(user, O) + return + else if(O.has_tool_quality(TOOL_CROWBAR)) + if(default_deconstruction_crowbar(user, O)) + return + else + user.visible_message( \ + "\The [user] begins [src.anchored ? "unsecuring" : "securing"] the microwave.", \ + "You attempt to [src.anchored ? "unsecure" : "secure"] the microwave." + ) + if (do_after(user,20/O.toolspeed)) + user.visible_message( \ + "\The [user] [src.anchored ? "unsecures" : "secures"] the microwave.", \ + "You [src.anchored ? "unsecure" : "secure"] the microwave." + ) + src.anchored = !src.anchored + else + to_chat(user, "You decide not to do that.") + else if(default_part_replacement(user, O)) + return + else if(istype(O, /obj/item/device/paicard)) + if(!paicard) + insertpai(user, O) + else + to_chat(user, "You have no idea what you can cook with this [O].") + ..() + SStgui.update_uis(src) + +/obj/machinery/microwave/tgui_state(mob/user) + return GLOB.tgui_physical_state + +/obj/machinery/microwave/attack_ai(mob/user as mob) + attack_hand(user) + +/obj/machinery/microwave/attack_hand(mob/user as mob) + if(user.a_intent == I_GRAB) + if(paicard) + ejectpai(user) + return + user.set_machine(src) + tgui_interact(user) + +/******************* +* Microwave Menu +********************/ +/obj/machinery/microwave/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Microwave", name) + ui.open() + +/obj/machinery/microwave/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["broken"] = broken + data["operating"] = operating + data["dirty"] = dirty == 100 + data["items"] = get_items_list() + + return data + +/obj/machinery/microwave/proc/get_items_list() + var/list/data = list() + + var/list/items_counts = list() + var/list/items_measures = list() + var/list/items_measures_p = list() + //for(var/obj/O in ((contents - component_parts) - circuit)) + for(var/obj/O in cookingContents()) + var/display_name = O.name + if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/egg)) + items_measures[display_name] = "egg" + items_measures_p[display_name] = "eggs" + if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/tofu)) + items_measures[display_name] = "tofu chunk" + items_measures_p[display_name] = "tofu chunks" + if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/meat)) //any meat + items_measures[display_name] = "slab of meat" + items_measures_p[display_name] = "slabs of meat" + if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/donkpocket)) + display_name = "Turnovers" + items_measures[display_name] = "turnover" + items_measures_p[display_name] = "turnovers" + if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/carpmeat)) + items_measures[display_name] = "fillet of meat" + items_measures_p[display_name] = "fillets of meat" + items_counts[display_name]++ + for(var/O in items_counts) + var/N = items_counts[O] + if(!(O in items_measures)) + data.Add(list(list( + "name" = capitalize(O), + "amt" = N, + "extra" = "[lowertext(O)][N > 1 ? "s" : ""]", + ))) + else + data.Add(list(list( + "name" = capitalize(O), + "amt" = N, + "extra" = N == 1 ? items_measures[O] : items_measures_p[O], + ))) + + for(var/datum/reagent/R in reagents.reagent_list) + var/display_name = R.name + if(R.id == "capsaicin") + display_name = "Hotsauce" + if(R.id == "frostoil") + display_name = "Coldsauce" + data.Add(list(list( + "name" = display_name, + "amt" = R.volume, + "extra" = "unit[R.volume > 1 ? "s" : ""]" + ))) + + return data + +/obj/machinery/microwave/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + if(operating) + return TRUE + + switch(action) + if("cook") + cook() + return TRUE + + if("dispose") + dispose() + return TRUE +/* +/obj/machinery/microwave/interact(mob/user as mob) // The microwave Menu + var/dat = "" + if(src.broken > 0) + dat = {"Bzzzzttttt"} + else if(src.operating) + dat = {"Microwaving in progress!
                    Please wait...!
                    "} + else if(src.dirty==100) + dat = {"This microwave is dirty!
                    Please clean it before use!
                    "} + else + var/list/items_counts = new + var/list/items_measures = new + var/list/items_measures_p = new + for (var/obj/O in ((contents - component_parts) - circuit)) + var/display_name = O.name + if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/egg)) + items_measures[display_name] = "egg" + items_measures_p[display_name] = "eggs" + if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/tofu)) + items_measures[display_name] = "tofu chunk" + items_measures_p[display_name] = "tofu chunks" + if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/meat)) //any meat + items_measures[display_name] = "slab of meat" + items_measures_p[display_name] = "slabs of meat" + if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/donkpocket)) + display_name = "Turnovers" + items_measures[display_name] = "turnover" + items_measures_p[display_name] = "turnovers" + if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/carpmeat)) + items_measures[display_name] = "fillet of meat" + items_measures_p[display_name] = "fillets of meat" + items_counts[display_name]++ + for (var/O in items_counts) + var/N = items_counts[O] + if (!(O in items_measures)) + dat += {"[capitalize(O)]: [N] [lowertext(O)]\s
                    "} + else + if (N==1) + dat += {"[capitalize(O)]: [N] [items_measures[O]]
                    "} + else + dat += {"[capitalize(O)]: [N] [items_measures_p[O]]
                    "} + + for (var/datum/reagent/R in reagents.reagent_list) + var/display_name = R.name + if (R.id == "capsaicin") + display_name = "Hotsauce" + if (R.id == "frostoil") + display_name = "Coldsauce" + dat += {"[display_name]: [R.volume] unit\s
                    "} + + if (items_counts.len==0 && reagents.reagent_list.len==0) + dat = {"The microwave is empty
                    "} + else + dat = {"Ingredients:
                    [dat]"} + dat += {"

                    \ +
                    Turn on!
                    \ +
                    Eject ingredients!
                    \ +"} + + user << browse("Microwave Controls[dat]", "window=microwave") + onclose(user, "microwave") + return +*/ + +/*********************************** +* Microwave Menu Handling/Cooking +************************************/ + +/obj/machinery/microwave/proc/cook() + if(stat & (NOPOWER|BROKEN)) + return + start() + if(reagents.total_volume==0 && !(locate(/obj) in cookingContents())) //dry run + if(!wzhzhzh(16)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 5) + abort() + return + abort() + return + + var/datum/recipe/recipe = select_recipe(available_recipes,src) + var/obj/cooked + if(!recipe) + dirty += 1 + if(prob(max(10,dirty*5))) + if(!wzhzhzh(16)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 2) + abort() + return + muck_start() + wzhzhzh(2) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 2) + muck_finish() + cooked = fail() + cooked.forceMove(src.loc) + else if(has_extra_item()) + if(!wzhzhzh(16)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 2) + abort() + return + broke() + cooked = fail() + cooked.forceMove(src.loc) + else + if(!wzhzhzh(40)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 5) + abort() + return + stop() + cooked = fail() + cooked.forceMove(src.loc) + return + + //Making multiple copies of a recipe + var/halftime = round(recipe.time*4/10/2) // VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was round(recipe.time/20/2)) + if(!wzhzhzh(halftime)) + abort() + return + recipe.before_cook(src) + if(!wzhzhzh(halftime)) + abort() + cooked = fail() + cooked.forceMove(loc) + recipe.after_cook(src) + return + + var/result = recipe.result + var/valid = 1 + var/list/cooked_items = list() + var/obj/temp = new /obj(src) //To prevent infinite loops, all results will be moved into a temporary location so they're not considered as inputs for other recipes + while(valid) + var/list/things = list() + things.Add(recipe.make_food(src)) + cooked_items += things + //Move cooked things to the buffer so they're not considered as ingredients + for(var/atom/movable/AM in things) + AM.forceMove(temp) + + valid = 0 + recipe.after_cook(src) + recipe = select_recipe(available_recipes,src) + if(recipe && recipe.result == result) + valid = 1 + sleep(2) + + for(var/atom/movable/R as anything in cooked_items) + R.forceMove(src) //Move everything from the buffer back to the container + + QDEL_NULL(temp)//Delete buffer object + + //Any leftover reagents are divided amongst the foods + var/total = reagents.total_volume + for(var/obj/item/weapon/reagent_containers/food/snacks/S in cooked_items) + reagents.trans_to_holder(S.reagents, total/cooked_items.len) + + for(var/obj/item/weapon/reagent_containers/food/snacks/S in cookingContents()) + S.cook() + + dispose(0) //clear out anything left + stop() + + return + +/obj/machinery/microwave/proc/wzhzhzh(var/seconds as num) // Whoever named this proc is fucking literally Satan. ~ Z + for (var/i=1 to seconds) + if (stat & (NOPOWER|BROKEN)) + return 0 + use_power(active_power_usage) + sleep(5) //VOREStation Edit - Quicker Microwaves + return 1 + +/obj/machinery/microwave/proc/has_extra_item() //- coded to have different microwaves be able to handle different items + if(item_level == 0) + for (var/obj/O in cookingContents()) + if ( \ + !istype(O,/obj/item/weapon/reagent_containers/food) && \ + !istype(O, /obj/item/weapon/grown) \ + ) + return 1 + return 0 + if(item_level == 1) + for (var/obj/O in cookingContents()) + if ( \ + !istype(O, /obj/item/weapon/reagent_containers/food) && \ + !istype(O, /obj/item/weapon/grown) && \ + !istype(O, /obj/item/slime_extract) && \ + !istype(O, /obj/item/organ) && \ + !istype(O, /obj/item/stack/material) \ + ) + return 1 + return 0 + +/obj/machinery/microwave/proc/start() + src.visible_message("The microwave turns on.", "You hear a microwave.") + soundloop.start() + src.operating = TRUE + src.icon_state = "mw1" + SStgui.update_uis(src) + +/obj/machinery/microwave/proc/abort() + operating = FALSE // Turn it off again aferwards + if(icon_state == "mw1") + icon_state = "mw" + SStgui.update_uis(src) + soundloop.stop() + +/obj/machinery/microwave/proc/stop() + playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) + operating = FALSE // Turn it off again aferwards + if(icon_state == "mw1") + icon_state = "mw" + SStgui.update_uis(src) + soundloop.stop() + +/obj/machinery/microwave/proc/dispose(var/message = 1) + for (var/atom/movable/A in cookingContents()) + A.forceMove(loc) + if (src.reagents.total_volume) + src.dirty++ + src.reagents.clear_reagents() + if(message) + to_chat(usr, "You dispose of the microwave contents.") + SStgui.update_uis(src) + +/obj/machinery/microwave/proc/muck_start() + playsound(src, 'sound/effects/splat.ogg', 50, 1) // Play a splat sound + src.icon_state = "mwbloody1" // Make it look dirty!! + +/obj/machinery/microwave/proc/muck_finish() + src.visible_message("The microwave gets covered in muck!") + src.dirty = 100 // Make it dirty so it can't be used util cleaned + src.flags = null //So you can't add condiments + src.icon_state = "mwbloody0" // Make it look dirty too + src.operating = 0 // Turn it off again aferwards + SStgui.update_uis(src) + soundloop.stop() + + +/obj/machinery/microwave/proc/broke() + var/datum/effect/effect/system/spark_spread/s = new + s.set_up(2, 1, src) + s.start() + src.icon_state = "mwb" // Make it look all busted up and shit + src.visible_message("The microwave breaks!") //Let them know they're stupid + src.broken = 2 // Make it broken so it can't be used util fixed + src.flags = null //So you can't add condiments + src.operating = 0 // Turn it off again aferwards + SStgui.update_uis(src) + soundloop.stop() + src.ejectpai() // If it broke, time to yeet the PAI. + +/obj/machinery/microwave/proc/fail() + var/obj/item/weapon/reagent_containers/food/snacks/badrecipe/ffuu = new(src) + var/amount = 0 + for (var/obj/O in cookingContents() - ffuu) + amount++ + if(O.reagents) + var/id = O.reagents.get_master_reagent_id() + if(id) + amount+=O.reagents.get_reagent_amount(id) + if(istype(O, /obj/item/weapon/holder)) + var/obj/item/weapon/holder/H = O + if(H.held_mob) + qdel(H.held_mob) + qdel(O) + src.reagents.clear_reagents() + ffuu.reagents.add_reagent("carbon", amount) + ffuu.reagents.add_reagent("toxin", amount/10) + return ffuu + +/obj/machinery/microwave/verb/Eject() + set src in oview(1) + set category = "Object" + set name = "Eject content" + usr.visible_message( + "[usr] tries to open [src] and remove its contents." , + "You try to open [src] and remove its contents." + ) + + if(!do_after(usr, 1 SECONDS, target = src)) + return + + if(operating) + to_chat(usr, "You can't do that, [src] door is locked!") + return + + usr.visible_message( + "[usr] opened [src] and has taken out [english_list(cookingContents())]." , + "You have opened [src] and taken out [english_list(cookingContents())]." + ) + dispose() + +/obj/machinery/microwave/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + if(!mover) + return 1 + if(mover.checkpass(PASSTABLE)) + //Animals can run under them, lots of empty space + return 1 + return ..() + +/obj/machinery/microwave/advanced // specifically for complex recipes + name = "deluxe microwave" + icon = 'icons/obj/deluxemicrowave.dmi' + icon_state = "mw" + circuit = /obj/item/weapon/circuitboard/microwave/advanced + circuit_item_capacity = 100 + item_level = 1 + +/obj/machinery/microwave/advanced/Initialize() + . = ..() + reagents.maximum_volume = 1000 + +/datum/recipe/splat // We use this to handle cooking micros (or mice, etc) in a microwave. Janky but it works better than snowflake code to handle the same thing. + items = list( + /obj/item/weapon/holder + ) + result = /obj/effect/decal/cleanable/blood/gibs + +/datum/recipe/splat/before_cook(obj/container) + if(istype(container, /obj/machinery/microwave)) + var/obj/machinery/microwave/M = container + M.muck_start() + playsound(container.loc, 'sound/items/drop/flesh.ogg', 100, 1) + . = ..() + +/datum/recipe/splat/make_food(obj/container) + for(var/obj/item/weapon/holder/H in container) + if(H.held_mob) + to_chat(H.held_mob, "You hear an earsplitting humming and your head aches!") + qdel(H.held_mob) + H.held_mob = null + qdel(H) + + . = ..() + +/datum/recipe/splat/after_cook(obj/container) + if(istype(container, /obj/machinery/microwave)) + var/obj/machinery/microwave/M = container + M.muck_finish() + . = ..() + +/obj/machinery/microwave/proc/cookingContents() //VOREEdit, this is a better way to deal with the contents of a microwave, since the previous method is stupid. + var/list/workingList = contents.Copy() // Using the copy proc because otherwise the two lists seem to become soul bonded. + workingList -= component_parts + workingList -= circuit + if(paicard) + workingList -= paicard + for(var/M in workingList) + if(istype(M, circuit)) // Yes, we remove circuit twice. Yes, it's necessary. Yes, it's stupid. + workingList -= M + return workingList + diff --git a/code/modules/food/recipe.dm b/code/modules/food/recipe.dm index 8e553bf532d..c967687e1f5 100644 --- a/code/modules/food/recipe.dm +++ b/code/modules/food/recipe.dm @@ -1,334 +1,334 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * - * /datum/recipe by rastaf0 13 apr 2011 * - * * * * * * * * * * * * * * * * * * * * * * * * * * - * This is powerful and flexible recipe system. - * It exists not only for food. - * supports both reagents and objects as prerequisites. - * In order to use this system you have to define a deriative from /datum/recipe - * * reagents are reagents. Acid, milc, booze, etc. - * * items are objects. Fruits, tools, circuit boards. - * * result is type to create as new object - * * time is optional parameter, you shall use in in your machine, - default /datum/recipe/ procs does not rely on this parameter. - * - * Functions you need: - * /datum/recipe/proc/make(var/obj/container as obj) - * Creates result inside container, - * deletes prerequisite reagents, - * transfers reagents from prerequisite objects, - * deletes all prerequisite objects (even not needed for recipe at the moment). - * - * /proc/select_recipe(list/datum/recipe/available_recipes, obj/obj as obj, exact = 1) - * Wonderful function that select suitable recipe for you. - * obj is a machine (or magik hat) with prerequisites, - * exact = 0 forces algorithm to ignore superfluous stuff. - * - * - * Functions you do not need to call directly but could: - * /datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents) - * /datum/recipe/proc/check_items(var/obj/container as obj) - * - * */ - -// Recipe type defines. Used to determine what machine makes them. -#define MICROWAVE 0x1 -#define FRYER 0x2 -#define OVEN 0x4 -#define GRILL 0x8 -#define CANDYMAKER 0x10 -#define CEREALMAKER 0x20 - -/datum/recipe - var/list/reagents // Example: = list("berryjuice" = 5) // do not list same reagent twice - var/list/items // Example: = list(/obj/item/weapon/tool/crowbar, /obj/item/weapon/welder) // place /foo/bar before /foo - var/list/fruit // Example: = list("fruit" = 3) - var/coating = null // Required coating on all items in the recipe. The default value of null explitly requires no coating - // A value of -1 is permissive and cares not for any coatings - // Any typepath indicates a specific coating that should be present - // Coatings are used for batter, breadcrumbs, beer-batter, colonel's secret coating, etc - - var/result // Example: = /obj/item/weapon/reagent_containers/food/snacks/donut/normal - var/result_quantity = 1 // Number of instances of result that are created. - var/time = 100 // 1/10 part of second - - #define RECIPE_REAGENT_REPLACE 0 //Reagents in the ingredients are discarded. - //Only the reagents present in the result at compiletime are used - #define RECIPE_REAGENT_MAX 1 //The result will contain the maximum of each reagent present between the two pools. Compiletime result, and sum of ingredients - #define RECIPE_REAGENT_MIN 2 //As above, but the minimum, ignoring zero values. - #define RECIPE_REAGENT_SUM 3 //The entire quantity of the ingredients are added to the result - - var/reagent_mix = RECIPE_REAGENT_MAX //How to handle reagent differences between the ingredients and the results - - var/appliance = MICROWAVE // Which apppliances this recipe can be made in. New Recipes will DEFAULT to using the Microwave, as a catch-all (and just in case) - // List of defines is in _defines/misc.dm. But for reference they are: - /* - MICROWAVE - FRYER - OVEN - CANDYMAKER - CEREALMAKER - */ - // This is a bitfield, more than one type can be used - // Grill is presently unused and not listed - -/datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents, var/exact = FALSE) - if(!reagents || !reagents.len) - return TRUE - - if(!avail_reagents) - return FALSE - - . = TRUE - for(var/r_r in reagents) - var/aval_r_amnt = avail_reagents.get_reagent_amount(r_r) - if(aval_r_amnt - reagents[r_r] >= 0) - if(aval_r_amnt>(reagents[r_r]) && exact) - . = FALSE - else - return FALSE - - if((reagents?(reagents.len):(0)) < avail_reagents.reagent_list.len) - return FALSE - return . - -/datum/recipe/proc/check_fruit(var/obj/container, var/exact = FALSE) - if (!fruit || !fruit.len) - return TRUE - - . = TRUE - if(fruit && fruit.len) - var/list/checklist = list() - // You should trust Copy(). - checklist = fruit.Copy() - for(var/obj/item/weapon/reagent_containers/food/snacks/grown/G in container) - if(!G.seed || !G.seed.kitchen_tag || isnull(checklist[G.seed.kitchen_tag])) - continue - if(check_coating(G)) - checklist[G.seed.kitchen_tag]-- - for(var/ktag in checklist) - if(!isnull(checklist[ktag])) - if(checklist[ktag] < 0 && exact) - . = FALSE - else if(checklist[ktag] > 0) - . = FALSE - break - return . - -/datum/recipe/proc/check_items(var/obj/container as obj, var/exact = FALSE) - if(!items || !items.len) - return TRUE - - . = TRUE - if(items && items.len) - var/list/checklist = list() - checklist = items.Copy() // You should really trust Copy - if(istype(container, /obj/machinery)) - var/obj/machinery/machine = container - for(var/obj/O in ((machine.contents - machine.component_parts) - machine.circuit)) - if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown)) - continue // Fruit is handled in check_fruit(). - var/found = FALSE - for(var/i = 1; i < checklist.len+1; i++) - var/item_type = checklist[i] - if (istype(O,item_type)) - checklist.Cut(i, i+1) - found = TRUE - break - if(!found && exact) - return FALSE - else - for(var/obj/O in container.contents) - if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown)) - continue // Fruit is handled in check_fruit(). - var/found = FALSE - for(var/i = 1; i < checklist.len+1; i++) - var/item_type = checklist[i] - if (istype(O,item_type)) - if(check_coating(O)) - checklist.Cut(i, i+1) - found = TRUE - break - if (!found && exact) - return FALSE - if(checklist.len) - return FALSE - return . - -//This is called on individual items within the container. -/datum/recipe/proc/check_coating(var/obj/O, var/exact = FALSE) - if(!istype(O,/obj/item/weapon/reagent_containers/food/snacks)) - return TRUE //Only snacks can be battered - - if (coating == -1) - return TRUE //-1 value doesnt care - - var/obj/item/weapon/reagent_containers/food/snacks/S = O - if (!S.coating) - if (!coating) - return TRUE - return FALSE - else if (S.coating.type == coating) - return TRUE - - return FALSE - -//general version -/datum/recipe/proc/make(var/obj/container as obj) - var/obj/result_obj = new result(container) - if(istype(container, /obj/machinery)) - var/obj/machinery/machine = container - for (var/obj/O in ((machine.contents-result_obj - machine.component_parts) - machine.circuit)) - O.reagents.trans_to_obj(result_obj, O.reagents.total_volume) - qdel(O) - else - for (var/obj/O in (container.contents-result_obj)) - O.reagents.trans_to_obj(result_obj, O.reagents.total_volume) - qdel(O) - container.reagents.clear_reagents() - return result_obj - -// food-related -// This proc is called under the assumption that the container has already been checked and found to contain the necessary ingredients -/datum/recipe/proc/make_food(var/obj/container as obj) - if(!result) - log_runtime(EXCEPTION("Recipe [type] is defined without a result, please bug report this.")) - if(istype(container, /obj/machinery/microwave)) - var/obj/machinery/microwave/M = container - M.dispose(FALSE) - - else if(istype(container, /obj/item/weapon/reagent_containers/cooking_container)) - var/obj/item/weapon/reagent_containers/cooking_container/CC = container - CC.clear() - - container.visible_message(SPAN_WARNING("[container] inexplicably spills, and its contents are lost!")) - - return - - -//We will subtract all the ingredients from the container, and transfer their reagents into a holder -//We will not touch things which are not required for this recipe. They will be left behind for the caller -//to decide what to do. They may be used again to make another recipe or discarded, or merged into the results, -//thats no longer the concern of this proc - var/datum/reagents/buffer = new /datum/reagents(10000000000, null)// - - - //Find items we need - if (items && items.len) - for (var/i in items) - var/obj/item/I = locate(i) in container - if (I && I.reagents) - I.reagents.trans_to_holder(buffer,I.reagents.total_volume) - qdel(I) - - //Find fruits - if (fruit && fruit.len) - var/list/checklist = list() - checklist = fruit.Copy() - - for(var/obj/item/weapon/reagent_containers/food/snacks/grown/G in container) - if(!G.seed || !G.seed.kitchen_tag || isnull(checklist[G.seed.kitchen_tag])) - continue - - if (checklist[G.seed.kitchen_tag] > 0) - //We found a thing we need - checklist[G.seed.kitchen_tag]-- - if (G && G.reagents) - G.reagents.trans_to_holder(buffer,G.reagents.total_volume) - qdel(G) - - //And lastly deduct necessary quantities of reagents - if (reagents && reagents.len) - for (var/r in reagents) - //Doesnt matter whether or not there's enough, we assume that check is done before - container.reagents.trans_type_to(buffer, r, reagents[r]) - - /* - Now we've removed all the ingredients that were used and we have the buffer containing the total of - all their reagents. - Next up we create the result, and then handle the merging of reagents depending on the mix setting - */ - var/tally = 0 - - /* - If we have multiple results, holder will be used as a buffer to hold reagents for the result objects. - If, as in the most common case, there is only a single result, then it will just be a reference to - the single-result's reagents - */ - var/datum/reagents/holder = new/datum/reagents(10000000000) - var/list/results = list() - while (tally < result_quantity) - var/obj/result_obj = new result(container) - results.Add(result_obj) - - if (!result_obj.reagents)//This shouldn't happen - //If the result somehow has no reagents defined, then create a new holder - result_obj.reagents = new /datum/reagents(buffer.total_volume*1.5, result_obj) - - if (result_quantity == 1) - qdel(holder) - holder = result_obj.reagents - else - result_obj.reagents.trans_to(holder, result_obj.reagents.total_volume) - tally++ - - - switch(reagent_mix) - if (RECIPE_REAGENT_REPLACE) - //We do no transferring - if (RECIPE_REAGENT_SUM) - //Sum is easy, just shove the entire buffer into the result - buffer.trans_to_holder(holder, buffer.total_volume) - if (RECIPE_REAGENT_MAX) - //We want the highest of each. - //Iterate through everything in buffer. If the target has less than the buffer, then top it up - for (var/datum/reagent/R in buffer.reagent_list) - var/rvol = holder.get_reagent_amount(R.id) - if (rvol < R.volume) - //Transfer the difference - buffer.trans_type_to(holder, R.id, R.volume-rvol) - - if (RECIPE_REAGENT_MIN) - //Min is slightly more complex. We want the result to have the lowest from each side - //But zero will not count. Where a side has zero its ignored and the side with a nonzero value is used - for (var/datum/reagent/R in buffer.reagent_list) - var/rvol = holder.get_reagent_amount(R.id) - if (rvol == 0) //If the target has zero of this reagent - buffer.trans_type_to(holder, R.id, R.volume) - //Then transfer all of ours - - else if (rvol > R.volume) - //if the target has more than ours - //Remove the difference - holder.remove_reagent(R.id, rvol-R.volume) - - - if (results.len > 1) - //If we're here, then holder is a buffer containing the total reagents for all the results. - //So now we redistribute it among them - var/total = holder.total_volume - for (var/i in results) - var/atom/a = i //optimisation - holder.trans_to(a, total / results.len) - - return results - -// When exact is false, extraneous ingredients are ignored -// When exact is true, extraneous ingredients will fail the recipe -// In both cases, the full set of required ingredients is still needed -/proc/select_recipe(var/list/datum/recipe/available_recipes, var/obj/obj as obj, var/exact) - var/highest_count = 0 - var/count = 0 - for (var/datum/recipe/recipe in available_recipes) - if(!recipe.check_reagents(obj.reagents, exact) || !recipe.check_items(obj, exact) || !recipe.check_fruit(obj, exact)) - continue - // Taken from cmp_recipe_complexity_dsc, but is way faster. - count = LAZYLEN(recipe.items) + LAZYLEN(recipe.reagents) + LAZYLEN(recipe.fruit) - if(count >= highest_count) - highest_count = count - . = recipe - -// Both of these are just placeholders to allow special behavior for mob holders, but you can do other things in here later if you feel like it. -/datum/recipe/proc/before_cook(obj/container) // Called Before the Microwave starts delays and cooking stuff - - -/datum/recipe/proc/after_cook(obj/container) // Called When the Microwave is finished. +/* * * * * * * * * * * * * * * * * * * * * * * * * * + * /datum/recipe by rastaf0 13 apr 2011 * + * * * * * * * * * * * * * * * * * * * * * * * * * * + * This is powerful and flexible recipe system. + * It exists not only for food. + * supports both reagents and objects as prerequisites. + * In order to use this system you have to define a deriative from /datum/recipe + * * reagents are reagents. Acid, milc, booze, etc. + * * items are objects. Fruits, tools, circuit boards. + * * result is type to create as new object + * * time is optional parameter, you shall use in in your machine, + default /datum/recipe/ procs does not rely on this parameter. + * + * Functions you need: + * /datum/recipe/proc/make(var/obj/container as obj) + * Creates result inside container, + * deletes prerequisite reagents, + * transfers reagents from prerequisite objects, + * deletes all prerequisite objects (even not needed for recipe at the moment). + * + * /proc/select_recipe(list/datum/recipe/available_recipes, obj/obj as obj, exact = 1) + * Wonderful function that select suitable recipe for you. + * obj is a machine (or magik hat) with prerequisites, + * exact = 0 forces algorithm to ignore superfluous stuff. + * + * + * Functions you do not need to call directly but could: + * /datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents) + * /datum/recipe/proc/check_items(var/obj/container as obj) + * + * */ + +// Recipe type defines. Used to determine what machine makes them. +#define MICROWAVE 0x1 +#define FRYER 0x2 +#define OVEN 0x4 +#define GRILL 0x8 +#define CANDYMAKER 0x10 +#define CEREALMAKER 0x20 + +/datum/recipe + var/list/reagents // Example: = list("berryjuice" = 5) // do not list same reagent twice + var/list/items // Example: = list(/obj/item/weapon/tool/crowbar, /obj/item/weapon/welder) // place /foo/bar before /foo + var/list/fruit // Example: = list("fruit" = 3) + var/coating = null // Required coating on all items in the recipe. The default value of null explitly requires no coating + // A value of -1 is permissive and cares not for any coatings + // Any typepath indicates a specific coating that should be present + // Coatings are used for batter, breadcrumbs, beer-batter, colonel's secret coating, etc + + var/result // Example: = /obj/item/weapon/reagent_containers/food/snacks/donut/normal + var/result_quantity = 1 // Number of instances of result that are created. + var/time = 100 // 1/10 part of second + + #define RECIPE_REAGENT_REPLACE 0 //Reagents in the ingredients are discarded. + //Only the reagents present in the result at compiletime are used + #define RECIPE_REAGENT_MAX 1 //The result will contain the maximum of each reagent present between the two pools. Compiletime result, and sum of ingredients + #define RECIPE_REAGENT_MIN 2 //As above, but the minimum, ignoring zero values. + #define RECIPE_REAGENT_SUM 3 //The entire quantity of the ingredients are added to the result + + var/reagent_mix = RECIPE_REAGENT_MAX //How to handle reagent differences between the ingredients and the results + + var/appliance = MICROWAVE // Which apppliances this recipe can be made in. New Recipes will DEFAULT to using the Microwave, as a catch-all (and just in case) + // List of defines is in _defines/misc.dm. But for reference they are: + /* + MICROWAVE + FRYER + OVEN + CANDYMAKER + CEREALMAKER + */ + // This is a bitfield, more than one type can be used + // Grill is presently unused and not listed + +/datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents, var/exact = FALSE) + if(!reagents || !reagents.len) + return TRUE + + if(!avail_reagents) + return FALSE + + . = TRUE + for(var/r_r in reagents) + var/aval_r_amnt = avail_reagents.get_reagent_amount(r_r) + if(aval_r_amnt - reagents[r_r] >= 0) + if(aval_r_amnt>(reagents[r_r]) && exact) + . = FALSE + else + return FALSE + + if((reagents?(reagents.len):(0)) < avail_reagents.reagent_list.len) + return FALSE + return . + +/datum/recipe/proc/check_fruit(var/obj/container, var/exact = FALSE) + if (!fruit || !fruit.len) + return TRUE + + . = TRUE + if(fruit && fruit.len) + var/list/checklist = list() + // You should trust Copy(). + checklist = fruit.Copy() + for(var/obj/item/weapon/reagent_containers/food/snacks/grown/G in container) + if(!G.seed || !G.seed.kitchen_tag || isnull(checklist[G.seed.kitchen_tag])) + continue + if(check_coating(G)) + checklist[G.seed.kitchen_tag]-- + for(var/ktag in checklist) + if(!isnull(checklist[ktag])) + if(checklist[ktag] < 0 && exact) + . = FALSE + else if(checklist[ktag] > 0) + . = FALSE + break + return . + +/datum/recipe/proc/check_items(var/obj/container as obj, var/exact = FALSE) + if(!items || !items.len) + return TRUE + + . = TRUE + if(items && items.len) + var/list/checklist = list() + checklist = items.Copy() // You should really trust Copy + if(istype(container, /obj/machinery)) + var/obj/machinery/machine = container + for(var/obj/O in ((machine.contents - machine.component_parts) - machine.circuit)) + if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown)) + continue // Fruit is handled in check_fruit(). + var/found = FALSE + for(var/i = 1; i < checklist.len+1; i++) + var/item_type = checklist[i] + if (istype(O,item_type)) + checklist.Cut(i, i+1) + found = TRUE + break + if(!found && exact) + return FALSE + else + for(var/obj/O in container.contents) + if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown)) + continue // Fruit is handled in check_fruit(). + var/found = FALSE + for(var/i = 1; i < checklist.len+1; i++) + var/item_type = checklist[i] + if (istype(O,item_type)) + if(check_coating(O)) + checklist.Cut(i, i+1) + found = TRUE + break + if (!found && exact) + return FALSE + if(checklist.len) + return FALSE + return . + +//This is called on individual items within the container. +/datum/recipe/proc/check_coating(var/obj/O, var/exact = FALSE) + if(!istype(O,/obj/item/weapon/reagent_containers/food/snacks)) + return TRUE //Only snacks can be battered + + if (coating == -1) + return TRUE //-1 value doesnt care + + var/obj/item/weapon/reagent_containers/food/snacks/S = O + if (!S.coating) + if (!coating) + return TRUE + return FALSE + else if (S.coating.type == coating) + return TRUE + + return FALSE + +//general version +/datum/recipe/proc/make(var/obj/container as obj) + var/obj/result_obj = new result(container) + if(istype(container, /obj/machinery)) + var/obj/machinery/machine = container + for (var/obj/O in ((machine.contents-result_obj - machine.component_parts) - machine.circuit)) + O.reagents.trans_to_obj(result_obj, O.reagents.total_volume) + qdel(O) + else + for (var/obj/O in (container.contents-result_obj)) + O.reagents.trans_to_obj(result_obj, O.reagents.total_volume) + qdel(O) + container.reagents.clear_reagents() + return result_obj + +// food-related +// This proc is called under the assumption that the container has already been checked and found to contain the necessary ingredients +/datum/recipe/proc/make_food(var/obj/container as obj) + if(!result) + log_runtime(EXCEPTION("Recipe [type] is defined without a result, please bug report this.")) + if(istype(container, /obj/machinery/microwave)) + var/obj/machinery/microwave/M = container + M.dispose(FALSE) + + else if(istype(container, /obj/item/weapon/reagent_containers/cooking_container)) + var/obj/item/weapon/reagent_containers/cooking_container/CC = container + CC.clear() + + container.visible_message(SPAN_WARNING("[container] inexplicably spills, and its contents are lost!")) + + return + + +//We will subtract all the ingredients from the container, and transfer their reagents into a holder +//We will not touch things which are not required for this recipe. They will be left behind for the caller +//to decide what to do. They may be used again to make another recipe or discarded, or merged into the results, +//thats no longer the concern of this proc + var/datum/reagents/buffer = new /datum/reagents(10000000000, null)// + + + //Find items we need + if (items && items.len) + for (var/i in items) + var/obj/item/I = locate(i) in container + if (I && I.reagents) + I.reagents.trans_to_holder(buffer,I.reagents.total_volume) + qdel(I) + + //Find fruits + if (fruit && fruit.len) + var/list/checklist = list() + checklist = fruit.Copy() + + for(var/obj/item/weapon/reagent_containers/food/snacks/grown/G in container) + if(!G.seed || !G.seed.kitchen_tag || isnull(checklist[G.seed.kitchen_tag])) + continue + + if (checklist[G.seed.kitchen_tag] > 0) + //We found a thing we need + checklist[G.seed.kitchen_tag]-- + if (G && G.reagents) + G.reagents.trans_to_holder(buffer,G.reagents.total_volume) + qdel(G) + + //And lastly deduct necessary quantities of reagents + if (reagents && reagents.len) + for (var/r in reagents) + //Doesnt matter whether or not there's enough, we assume that check is done before + container.reagents.trans_type_to(buffer, r, reagents[r]) + + /* + Now we've removed all the ingredients that were used and we have the buffer containing the total of + all their reagents. + Next up we create the result, and then handle the merging of reagents depending on the mix setting + */ + var/tally = 0 + + /* + If we have multiple results, holder will be used as a buffer to hold reagents for the result objects. + If, as in the most common case, there is only a single result, then it will just be a reference to + the single-result's reagents + */ + var/datum/reagents/holder = new/datum/reagents(10000000000) + var/list/results = list() + while (tally < result_quantity) + var/obj/result_obj = new result(container) + results.Add(result_obj) + + if (!result_obj.reagents)//This shouldn't happen + //If the result somehow has no reagents defined, then create a new holder + result_obj.reagents = new /datum/reagents(buffer.total_volume*1.5, result_obj) + + if (result_quantity == 1) + qdel(holder) + holder = result_obj.reagents + else + result_obj.reagents.trans_to(holder, result_obj.reagents.total_volume) + tally++ + + + switch(reagent_mix) + if (RECIPE_REAGENT_REPLACE) + //We do no transferring + if (RECIPE_REAGENT_SUM) + //Sum is easy, just shove the entire buffer into the result + buffer.trans_to_holder(holder, buffer.total_volume) + if (RECIPE_REAGENT_MAX) + //We want the highest of each. + //Iterate through everything in buffer. If the target has less than the buffer, then top it up + for (var/datum/reagent/R in buffer.reagent_list) + var/rvol = holder.get_reagent_amount(R.id) + if (rvol < R.volume) + //Transfer the difference + buffer.trans_type_to(holder, R.id, R.volume-rvol) + + if (RECIPE_REAGENT_MIN) + //Min is slightly more complex. We want the result to have the lowest from each side + //But zero will not count. Where a side has zero its ignored and the side with a nonzero value is used + for (var/datum/reagent/R in buffer.reagent_list) + var/rvol = holder.get_reagent_amount(R.id) + if (rvol == 0) //If the target has zero of this reagent + buffer.trans_type_to(holder, R.id, R.volume) + //Then transfer all of ours + + else if (rvol > R.volume) + //if the target has more than ours + //Remove the difference + holder.remove_reagent(R.id, rvol-R.volume) + + + if (results.len > 1) + //If we're here, then holder is a buffer containing the total reagents for all the results. + //So now we redistribute it among them + var/total = holder.total_volume + for (var/i in results) + var/atom/a = i //optimisation + holder.trans_to(a, total / results.len) + + return results + +// When exact is false, extraneous ingredients are ignored +// When exact is true, extraneous ingredients will fail the recipe +// In both cases, the full set of required ingredients is still needed +/proc/select_recipe(var/list/datum/recipe/available_recipes, var/obj/obj as obj, var/exact) + var/highest_count = 0 + var/count = 0 + for (var/datum/recipe/recipe in available_recipes) + if(!recipe.check_reagents(obj.reagents, exact) || !recipe.check_items(obj, exact) || !recipe.check_fruit(obj, exact)) + continue + // Taken from cmp_recipe_complexity_dsc, but is way faster. + count = LAZYLEN(recipe.items) + LAZYLEN(recipe.reagents) + LAZYLEN(recipe.fruit) + if(count >= highest_count) + highest_count = count + . = recipe + +// Both of these are just placeholders to allow special behavior for mob holders, but you can do other things in here later if you feel like it. +/datum/recipe/proc/before_cook(obj/container) // Called Before the Microwave starts delays and cooking stuff + + +/datum/recipe/proc/after_cook(obj/container) // Called When the Microwave is finished. diff --git a/code/modules/food/recipes_fryer.dm b/code/modules/food/recipes_fryer.dm index 4f1bc471e42..fb728df8dd2 100644 --- a/code/modules/food/recipes_fryer.dm +++ b/code/modules/food/recipes_fryer.dm @@ -1,205 +1,205 @@ -/datum/recipe/fries - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/rawsticks - ) - result = /obj/item/weapon/reagent_containers/food/snacks/fries - -/datum/recipe/cheesyfries - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/fries, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/cheesyfries - -/datum/recipe/jpoppers - appliance = FRYER - fruit = list("chili" = 1) - coating = /datum/reagent/nutriment/coating/batter - result = /obj/item/weapon/reagent_containers/food/snacks/jalapeno_poppers - result_quantity = 2 - -/datum/recipe/risottoballs - appliance = FRYER - reagents = list("sodiumchloride" = 1, "blackpepper" = 1) - items = list(/obj/item/weapon/reagent_containers/food/snacks/risotto) - coating = /datum/reagent/nutriment/coating/batter - reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product - result = /obj/item/weapon/reagent_containers/food/snacks/risottoballs - result_quantity = 2 - -/datum/recipe/bellefritter - appliance = FRYER - coating = /datum/reagent/nutriment/coating/batter - reagents = list("sugar" = 5) - items = list(/obj/item/weapon/reagent_containers/food/snacks/frostbelle) - result = /obj/item/weapon/reagent_containers/food/snacks/bellefritter - result_quantity = 2 - -/datum/recipe/onionrings - appliance = FRYER - coating = /datum/reagent/nutriment/coating/batter - fruit = list("onion" = 1) - result = /obj/item/weapon/reagent_containers/food/snacks/onionrings - result_quantity = 2 - -//Meaty Recipes -//==================== -/datum/recipe/cubancarp - appliance = FRYER - fruit = list("chili" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/carpmeat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/cubancarp - -/datum/recipe/batteredsausage - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sausage - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sausage/battered - coating = /datum/reagent/nutriment/coating/batter - - -/datum/recipe/katsu - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/meat/chicken - ) - result = /obj/item/weapon/reagent_containers/food/snacks/chickenkatsu - coating = /datum/reagent/nutriment/coating/beerbatter - - -/datum/recipe/pizzacrunch_1 - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/crunch - coating = /datum/reagent/nutriment/coating/batter - -//Alternate pizza crunch recipe for combination pizzas made in oven -/datum/recipe/pizzacrunch_2 - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/variable/pizza - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/crunch - coating = /datum/reagent/nutriment/coating/batter - -/datum/recipe/friedmushroom - appliance = FRYER - fruit = list("plumphelmet" = 1) - coating = /datum/reagent/nutriment/coating/beerbatter - reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product - result = /obj/item/weapon/reagent_containers/food/snacks/friedmushroom - -/datum/recipe/fishfingers - items = list( - /obj/item/weapon/reagent_containers/food/snacks/carpmeat, - ) - coating = /datum/reagent/nutriment/coating/batter - result = /obj/item/weapon/reagent_containers/food/snacks/fishfingers - reagent_mix = RECIPE_REAGENT_REPLACE - -/datum/recipe/corn_dog - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sausage - ) - fruit = list("corn" = 1) - coating = /datum/reagent/nutriment/coating/batter - result = /obj/item/weapon/reagent_containers/food/snacks/corn_dog - -/datum/recipe/sweet_and_sour - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bacon, - /obj/item/weapon/reagent_containers/food/snacks/cutlet - ) - reagents = list("soysauce" = 5, "batter" = 10) - result = /obj/item/weapon/reagent_containers/food/snacks/sweet_and_sour - -//Sweet Recipes. -//================== -// All donuts were given reagents of 5 to equal old recipes and make for faster cook times. -/datum/recipe/jellydonut - appliance = FRYER - reagents = list("berryjuice" = 5, "sugar" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice - ) - result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly - result_quantity = 2 - -/datum/recipe/jellydonut/poisonberry - reagents = list("poisonberryjuice" = 5, "sugar" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly/poisonberry - -/datum/recipe/jellydonut/slime // Subtypes of jellydonut, appliance inheritance applies. - reagents = list("slimejelly" = 5, "sugar" = 5) - result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly/slimejelly - -/datum/recipe/jellydonut/cherry // Subtypes of jellydonut, appliance inheritance applies. - reagents = list("cherryjelly" = 5, "sugar" = 5) - result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly/cherryjelly - -/datum/recipe/donut - appliance = FRYER - reagents = list("sugar" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice - ) - result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain - result_quantity = 2 - -/datum/recipe/chaosdonut - appliance = FRYER - reagents = list("frostoil" = 10, "capsaicin" = 10, "sugar" = 10) - reagent_mix = RECIPE_REAGENT_REPLACE //This creates its own reagents - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice - ) - result = /obj/item/weapon/reagent_containers/food/snacks/donut/chaos - result_quantity = 2 - -/datum/recipe/funnelcake - appliance = FRYER - reagents = list("sugar" = 5, "batter" = 10) - result = /obj/item/weapon/reagent_containers/food/snacks/funnelcake - result_quantity = 2 - -/datum/recipe/pisanggoreng - appliance = FRYER - fruit = list("banana" = 2) - reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product - result = /obj/item/weapon/reagent_containers/food/snacks/pisanggoreng - coating = /datum/reagent/nutriment/coating/batter - -//VOREStation Add Start -/datum/recipe/generalschicken - appliance = FRYER - reagents = list("capsaicin" = 2, "sugar" = 2, "batter" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/generalschicken - -/datum/recipe/chickenwings - appliance = FRYER - reagents = list("capsaicin" = 5, "batter" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat - ) - result = /obj/item/weapon/storage/box/wings //This is kinda like the donut box. -//VOREStation Add End +/datum/recipe/fries + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/rawsticks + ) + result = /obj/item/weapon/reagent_containers/food/snacks/fries + +/datum/recipe/cheesyfries + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/fries, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/cheesyfries + +/datum/recipe/jpoppers + appliance = FRYER + fruit = list("chili" = 1) + coating = /datum/reagent/nutriment/coating/batter + result = /obj/item/weapon/reagent_containers/food/snacks/jalapeno_poppers + result_quantity = 2 + +/datum/recipe/risottoballs + appliance = FRYER + reagents = list("sodiumchloride" = 1, "blackpepper" = 1) + items = list(/obj/item/weapon/reagent_containers/food/snacks/risotto) + coating = /datum/reagent/nutriment/coating/batter + reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product + result = /obj/item/weapon/reagent_containers/food/snacks/risottoballs + result_quantity = 2 + +/datum/recipe/bellefritter + appliance = FRYER + coating = /datum/reagent/nutriment/coating/batter + reagents = list("sugar" = 5) + items = list(/obj/item/weapon/reagent_containers/food/snacks/frostbelle) + result = /obj/item/weapon/reagent_containers/food/snacks/bellefritter + result_quantity = 2 + +/datum/recipe/onionrings + appliance = FRYER + coating = /datum/reagent/nutriment/coating/batter + fruit = list("onion" = 1) + result = /obj/item/weapon/reagent_containers/food/snacks/onionrings + result_quantity = 2 + +//Meaty Recipes +//==================== +/datum/recipe/cubancarp + appliance = FRYER + fruit = list("chili" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/cubancarp + +/datum/recipe/batteredsausage + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sausage + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sausage/battered + coating = /datum/reagent/nutriment/coating/batter + + +/datum/recipe/katsu + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/meat/chicken + ) + result = /obj/item/weapon/reagent_containers/food/snacks/chickenkatsu + coating = /datum/reagent/nutriment/coating/beerbatter + + +/datum/recipe/pizzacrunch_1 + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/crunch + coating = /datum/reagent/nutriment/coating/batter + +//Alternate pizza crunch recipe for combination pizzas made in oven +/datum/recipe/pizzacrunch_2 + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/variable/pizza + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/crunch + coating = /datum/reagent/nutriment/coating/batter + +/datum/recipe/friedmushroom + appliance = FRYER + fruit = list("plumphelmet" = 1) + coating = /datum/reagent/nutriment/coating/beerbatter + reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product + result = /obj/item/weapon/reagent_containers/food/snacks/friedmushroom + +/datum/recipe/fishfingers + items = list( + /obj/item/weapon/reagent_containers/food/snacks/carpmeat, + ) + coating = /datum/reagent/nutriment/coating/batter + result = /obj/item/weapon/reagent_containers/food/snacks/fishfingers + reagent_mix = RECIPE_REAGENT_REPLACE + +/datum/recipe/corn_dog + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sausage + ) + fruit = list("corn" = 1) + coating = /datum/reagent/nutriment/coating/batter + result = /obj/item/weapon/reagent_containers/food/snacks/corn_dog + +/datum/recipe/sweet_and_sour + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bacon, + /obj/item/weapon/reagent_containers/food/snacks/cutlet + ) + reagents = list("soysauce" = 5, "batter" = 10) + result = /obj/item/weapon/reagent_containers/food/snacks/sweet_and_sour + +//Sweet Recipes. +//================== +// All donuts were given reagents of 5 to equal old recipes and make for faster cook times. +/datum/recipe/jellydonut + appliance = FRYER + reagents = list("berryjuice" = 5, "sugar" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice + ) + result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly + result_quantity = 2 + +/datum/recipe/jellydonut/poisonberry + reagents = list("poisonberryjuice" = 5, "sugar" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly/poisonberry + +/datum/recipe/jellydonut/slime // Subtypes of jellydonut, appliance inheritance applies. + reagents = list("slimejelly" = 5, "sugar" = 5) + result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly/slimejelly + +/datum/recipe/jellydonut/cherry // Subtypes of jellydonut, appliance inheritance applies. + reagents = list("cherryjelly" = 5, "sugar" = 5) + result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly/cherryjelly + +/datum/recipe/donut + appliance = FRYER + reagents = list("sugar" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice + ) + result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain + result_quantity = 2 + +/datum/recipe/chaosdonut + appliance = FRYER + reagents = list("frostoil" = 10, "capsaicin" = 10, "sugar" = 10) + reagent_mix = RECIPE_REAGENT_REPLACE //This creates its own reagents + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice + ) + result = /obj/item/weapon/reagent_containers/food/snacks/donut/chaos + result_quantity = 2 + +/datum/recipe/funnelcake + appliance = FRYER + reagents = list("sugar" = 5, "batter" = 10) + result = /obj/item/weapon/reagent_containers/food/snacks/funnelcake + result_quantity = 2 + +/datum/recipe/pisanggoreng + appliance = FRYER + fruit = list("banana" = 2) + reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product + result = /obj/item/weapon/reagent_containers/food/snacks/pisanggoreng + coating = /datum/reagent/nutriment/coating/batter + +//VOREStation Add Start +/datum/recipe/generalschicken + appliance = FRYER + reagents = list("capsaicin" = 2, "sugar" = 2, "batter" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/generalschicken + +/datum/recipe/chickenwings + appliance = FRYER + reagents = list("capsaicin" = 5, "batter" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat + ) + result = /obj/item/weapon/storage/box/wings //This is kinda like the donut box. +//VOREStation Add End diff --git a/code/modules/food/recipes_grill.dm b/code/modules/food/recipes_grill.dm index df92a7148a9..86e63a95f45 100644 --- a/code/modules/food/recipes_grill.dm +++ b/code/modules/food/recipes_grill.dm @@ -1,328 +1,328 @@ -/datum/recipe/humanburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/meat/human, - /obj/item/weapon/reagent_containers/food/snacks/bun - ) - result = /obj/item/weapon/reagent_containers/food/snacks/human/burger - -/datum/recipe/plainburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/meat //do not place this recipe before /datum/recipe/humanburger - ) - result = /obj/item/weapon/reagent_containers/food/snacks/monkeyburger - -/datum/recipe/syntiburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh - ) - result = /obj/item/weapon/reagent_containers/food/snacks/monkeyburger - -/datum/recipe/brainburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/organ/internal/brain - ) - result = /obj/item/weapon/reagent_containers/food/snacks/brainburger - -/datum/recipe/roburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/robot_parts/head - ) - result = /obj/item/weapon/reagent_containers/food/snacks/roburger - -/datum/recipe/xenoburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/xenomeat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/xenoburger - -/datum/recipe/fishburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/carpmeat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/fishburger - -/datum/recipe/tofuburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/tofu - ) - result = /obj/item/weapon/reagent_containers/food/snacks/tofuburger - -/datum/recipe/ghostburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/ectoplasm //where do you even find this stuff - ) - result = /obj/item/weapon/reagent_containers/food/snacks/ghostburger - -/datum/recipe/clownburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/clothing/mask/gas/clown_hat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/clownburger - -/datum/recipe/mimeburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/clothing/head/beret - ) - result = /obj/item/weapon/reagent_containers/food/snacks/mimeburger - -/datum/recipe/mouseburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/holder/mouse - ) - result = /obj/item/weapon/reagent_containers/food/snacks/mouseburger - -/datum/recipe/bunbun - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/bun - ) - result = /obj/item/weapon/reagent_containers/food/snacks/bunbun - -/datum/recipe/hotdog - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/sausage - ) - result = /obj/item/weapon/reagent_containers/food/snacks/hotdog - -/datum/recipe/humankabob - appliance = GRILL - items = list( - /obj/item/stack/rods, - /obj/item/weapon/reagent_containers/food/snacks/meat/human, - /obj/item/weapon/reagent_containers/food/snacks/meat/human, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/human/kabob - -/datum/recipe/kabob //Do not put before humankabob - appliance = GRILL - items = list( - /obj/item/stack/rods, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/monkeykabob - -/datum/recipe/monkeykabob - appliance = GRILL - items = list( - /obj/item/stack/rods, - /obj/item/weapon/reagent_containers/food/snacks/meat/monkey, - /obj/item/weapon/reagent_containers/food/snacks/meat/monkey - ) - result = /obj/item/weapon/reagent_containers/food/snacks/monkeykabob - -/datum/recipe/syntikabob - appliance = GRILL - items = list( - /obj/item/stack/rods, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh - ) - result = /obj/item/weapon/reagent_containers/food/snacks/monkeykabob - -/datum/recipe/tofukabob - appliance = GRILL - items = list( - /obj/item/stack/rods, - /obj/item/weapon/reagent_containers/food/snacks/tofu, - /obj/item/weapon/reagent_containers/food/snacks/tofu, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/tofukabob - -/datum/recipe/fakespellburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/monkeyburger, - /obj/item/clothing/head/wizard/fake, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/spellburger - -/datum/recipe/spellburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/monkeyburger, - /obj/item/clothing/head/wizard, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/spellburger - -/datum/recipe/bigbiteburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/monkeyburger, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - ) - reagents = list("egg" = 3) - reagent_mix = RECIPE_REAGENT_REPLACE - result = /obj/item/weapon/reagent_containers/food/snacks/bigbiteburger - -/datum/recipe/superbiteburger - appliance = GRILL - fruit = list("tomato" = 1) - reagents = list("sodiumchloride" = 5, "blackpepper" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bigbiteburger, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/boiledegg, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/superbiteburger - -/datum/recipe/slimeburger - appliance = GRILL - reagents = list("slimejelly" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun - ) - result = /obj/item/weapon/reagent_containers/food/snacks/jellyburger/slime - -/datum/recipe/jellyburger - appliance = GRILL - reagents = list("cherryjelly" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun - ) - result = /obj/item/weapon/reagent_containers/food/snacks/jellyburger/cherry - -/datum/recipe/bearburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/bearmeat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/bearburger - -/datum/recipe/baconburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/bacon, - /obj/item/weapon/reagent_containers/food/snacks/bacon - ) - result = /obj/item/weapon/reagent_containers/food/snacks/burger/bacon - -/datum/recipe/omelette - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - ) - reagents = list("egg" = 6) - reagent_mix = RECIPE_REAGENT_REPLACE - result = /obj/item/weapon/reagent_containers/food/snacks/omelette - -/datum/recipe/omurice - appliance = GRILL - reagents = list("rice" = 5, "ketchup" = 5, "egg" = 3) - result = /obj/item/weapon/reagent_containers/food/snacks/omurice - -/datum/recipe/omurice/heart - appliance = GRILL - reagents = list("rice" = 5, "ketchup" = 5, "sugar" = 5, "egg" = 3) - result = /obj/item/weapon/reagent_containers/food/snacks/omurice/heart - -/datum/recipe/omurice/face - appliance = GRILL - reagents = list("rice" = 5, "ketchup" = 5, "sodiumchloride" = 1, "egg" = 3) - result = /obj/item/weapon/reagent_containers/food/snacks/omurice/face - -/datum/recipe/meatsteak - appliance = GRILL - reagents = list("sodiumchloride" = 1, "blackpepper" = 1) - items = list(/obj/item/weapon/reagent_containers/food/snacks/meat) - result = /obj/item/weapon/reagent_containers/food/snacks/meatsteak - -/datum/recipe/honeytoast - appliance = GRILL - reagents = list("honey" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/slice/bread - ) - reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product - result = /obj/item/weapon/reagent_containers/food/snacks/honeytoast - -/datum/recipe/grilled_carp - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/carpmeat, - /obj/item/weapon/reagent_containers/food/snacks/carpmeat, - /obj/item/weapon/reagent_containers/food/snacks/carpmeat, - /obj/item/weapon/reagent_containers/food/snacks/carpmeat, - /obj/item/weapon/reagent_containers/food/snacks/carpmeat, - /obj/item/weapon/reagent_containers/food/snacks/carpmeat - ) - reagents = list("spacespice" = 1) - fruit = list("lettuce" = 1, "lime" = 1) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/grilled_carp - -/datum/recipe/grilledcheese - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/slice/bread, - /obj/item/weapon/reagent_containers/food/snacks/slice/bread, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/grilledcheese - -/datum/recipe/toastedsandwich - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sandwich - ) - result = /obj/item/weapon/reagent_containers/food/snacks/toastedsandwich - -/datum/recipe/cheese_cracker - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/spreads/butter, - /obj/item/weapon/reagent_containers/food/snacks/slice/bread, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - reagents = list("spacespice" = 1) - result = /obj/item/weapon/reagent_containers/food/snacks/cheesetoast - result_quantity = 4 - -/datum/recipe/bacongrill - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/spreads, - /obj/item/weapon/reagent_containers/food/snacks/rawbacon - ) - result = /obj/item/weapon/reagent_containers/food/snacks/bacon - -/datum/recipe/chickenfillet //Also just combinable, like burgers and hot dogs. - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/chickenkatsu, - /obj/item/weapon/reagent_containers/food/snacks/bun - ) - result = /obj/item/weapon/reagent_containers/food/snacks/chickenfillet +/datum/recipe/humanburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/meat/human, + /obj/item/weapon/reagent_containers/food/snacks/bun + ) + result = /obj/item/weapon/reagent_containers/food/snacks/human/burger + +/datum/recipe/plainburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/meat //do not place this recipe before /datum/recipe/humanburger + ) + result = /obj/item/weapon/reagent_containers/food/snacks/monkeyburger + +/datum/recipe/syntiburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh + ) + result = /obj/item/weapon/reagent_containers/food/snacks/monkeyburger + +/datum/recipe/brainburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/organ/internal/brain + ) + result = /obj/item/weapon/reagent_containers/food/snacks/brainburger + +/datum/recipe/roburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/robot_parts/head + ) + result = /obj/item/weapon/reagent_containers/food/snacks/roburger + +/datum/recipe/xenoburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/xenomeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/xenoburger + +/datum/recipe/fishburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/fishburger + +/datum/recipe/tofuburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/tofu + ) + result = /obj/item/weapon/reagent_containers/food/snacks/tofuburger + +/datum/recipe/ghostburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/ectoplasm //where do you even find this stuff + ) + result = /obj/item/weapon/reagent_containers/food/snacks/ghostburger + +/datum/recipe/clownburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/clothing/mask/gas/clown_hat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/clownburger + +/datum/recipe/mimeburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/clothing/head/beret + ) + result = /obj/item/weapon/reagent_containers/food/snacks/mimeburger + +/datum/recipe/mouseburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/holder/mouse + ) + result = /obj/item/weapon/reagent_containers/food/snacks/mouseburger + +/datum/recipe/bunbun + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/bun + ) + result = /obj/item/weapon/reagent_containers/food/snacks/bunbun + +/datum/recipe/hotdog + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/sausage + ) + result = /obj/item/weapon/reagent_containers/food/snacks/hotdog + +/datum/recipe/humankabob + appliance = GRILL + items = list( + /obj/item/stack/rods, + /obj/item/weapon/reagent_containers/food/snacks/meat/human, + /obj/item/weapon/reagent_containers/food/snacks/meat/human, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/human/kabob + +/datum/recipe/kabob //Do not put before humankabob + appliance = GRILL + items = list( + /obj/item/stack/rods, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/monkeykabob + +/datum/recipe/monkeykabob + appliance = GRILL + items = list( + /obj/item/stack/rods, + /obj/item/weapon/reagent_containers/food/snacks/meat/monkey, + /obj/item/weapon/reagent_containers/food/snacks/meat/monkey + ) + result = /obj/item/weapon/reagent_containers/food/snacks/monkeykabob + +/datum/recipe/syntikabob + appliance = GRILL + items = list( + /obj/item/stack/rods, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh + ) + result = /obj/item/weapon/reagent_containers/food/snacks/monkeykabob + +/datum/recipe/tofukabob + appliance = GRILL + items = list( + /obj/item/stack/rods, + /obj/item/weapon/reagent_containers/food/snacks/tofu, + /obj/item/weapon/reagent_containers/food/snacks/tofu, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/tofukabob + +/datum/recipe/fakespellburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/monkeyburger, + /obj/item/clothing/head/wizard/fake, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/spellburger + +/datum/recipe/spellburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/monkeyburger, + /obj/item/clothing/head/wizard, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/spellburger + +/datum/recipe/bigbiteburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/monkeyburger, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + ) + reagents = list("egg" = 3) + reagent_mix = RECIPE_REAGENT_REPLACE + result = /obj/item/weapon/reagent_containers/food/snacks/bigbiteburger + +/datum/recipe/superbiteburger + appliance = GRILL + fruit = list("tomato" = 1) + reagents = list("sodiumchloride" = 5, "blackpepper" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bigbiteburger, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/boiledegg, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/superbiteburger + +/datum/recipe/slimeburger + appliance = GRILL + reagents = list("slimejelly" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun + ) + result = /obj/item/weapon/reagent_containers/food/snacks/jellyburger/slime + +/datum/recipe/jellyburger + appliance = GRILL + reagents = list("cherryjelly" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun + ) + result = /obj/item/weapon/reagent_containers/food/snacks/jellyburger/cherry + +/datum/recipe/bearburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/bearmeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/bearburger + +/datum/recipe/baconburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/bacon, + /obj/item/weapon/reagent_containers/food/snacks/bacon + ) + result = /obj/item/weapon/reagent_containers/food/snacks/burger/bacon + +/datum/recipe/omelette + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + ) + reagents = list("egg" = 6) + reagent_mix = RECIPE_REAGENT_REPLACE + result = /obj/item/weapon/reagent_containers/food/snacks/omelette + +/datum/recipe/omurice + appliance = GRILL + reagents = list("rice" = 5, "ketchup" = 5, "egg" = 3) + result = /obj/item/weapon/reagent_containers/food/snacks/omurice + +/datum/recipe/omurice/heart + appliance = GRILL + reagents = list("rice" = 5, "ketchup" = 5, "sugar" = 5, "egg" = 3) + result = /obj/item/weapon/reagent_containers/food/snacks/omurice/heart + +/datum/recipe/omurice/face + appliance = GRILL + reagents = list("rice" = 5, "ketchup" = 5, "sodiumchloride" = 1, "egg" = 3) + result = /obj/item/weapon/reagent_containers/food/snacks/omurice/face + +/datum/recipe/meatsteak + appliance = GRILL + reagents = list("sodiumchloride" = 1, "blackpepper" = 1) + items = list(/obj/item/weapon/reagent_containers/food/snacks/meat) + result = /obj/item/weapon/reagent_containers/food/snacks/meatsteak + +/datum/recipe/honeytoast + appliance = GRILL + reagents = list("honey" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/slice/bread + ) + reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product + result = /obj/item/weapon/reagent_containers/food/snacks/honeytoast + +/datum/recipe/grilled_carp + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/carpmeat, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat + ) + reagents = list("spacespice" = 1) + fruit = list("lettuce" = 1, "lime" = 1) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/grilled_carp + +/datum/recipe/grilledcheese + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/slice/bread, + /obj/item/weapon/reagent_containers/food/snacks/slice/bread, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/grilledcheese + +/datum/recipe/toastedsandwich + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sandwich + ) + result = /obj/item/weapon/reagent_containers/food/snacks/toastedsandwich + +/datum/recipe/cheese_cracker + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/spreads/butter, + /obj/item/weapon/reagent_containers/food/snacks/slice/bread, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + reagents = list("spacespice" = 1) + result = /obj/item/weapon/reagent_containers/food/snacks/cheesetoast + result_quantity = 4 + +/datum/recipe/bacongrill + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/spreads, + /obj/item/weapon/reagent_containers/food/snacks/rawbacon + ) + result = /obj/item/weapon/reagent_containers/food/snacks/bacon + +/datum/recipe/chickenfillet //Also just combinable, like burgers and hot dogs. + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/chickenkatsu, + /obj/item/weapon/reagent_containers/food/snacks/bun + ) + result = /obj/item/weapon/reagent_containers/food/snacks/chickenfillet diff --git a/code/modules/food/recipes_oven.dm b/code/modules/food/recipes_oven.dm index 40bc15c427e..42c3329fa03 100644 --- a/code/modules/food/recipes_oven.dm +++ b/code/modules/food/recipes_oven.dm @@ -1,702 +1,702 @@ -/datum/recipe/ovenfries - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/rawsticks - ) - result = /obj/item/weapon/reagent_containers/food/snacks/ovenfries - -//Roasts -//--------------- - -/datum/recipe/dionaroast - appliance = OVEN - fruit = list("apple" = 1) - reagents = list("pacid" = 5) //It dissolves the carapace. Still poisonous, though. - items = list(/obj/item/weapon/holder/diona) - result = /obj/item/weapon/reagent_containers/food/snacks/dionaroast - reagent_mix = RECIPE_REAGENT_REPLACE //No eating polyacid - -/datum/recipe/monkeysdelight - appliance = OVEN - fruit = list("banana" = 1) - reagents = list("sodiumchloride" = 1, "blackpepper" = 1, "flour" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/monkeycube - ) - result = /obj/item/weapon/reagent_containers/food/snacks/monkeysdelight - reagent_mix = RECIPE_REAGENT_REPLACE - -/datum/recipe/ribplate - appliance = OVEN - reagents = list("honey" = 5, "spacespice" = 2, "blackpepper" = 1) - items = list(/obj/item/weapon/reagent_containers/food/snacks/meat) - reagent_mix = RECIPE_REAGENT_REPLACE - result = /obj/item/weapon/reagent_containers/food/snacks/ribplate - -/datum/recipe/turkey - appliance = OVEN - reagents = list("sodiumchloride" = 1, "blackpepper" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/meat/chicken, - /obj/item/weapon/reagent_containers/food/snacks/stuffing - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/turkey - -/datum/recipe/tofurkey - appliance = OVEN - reagents = list("sodiumchloride" = 1, "blackpepper" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/tofu, - /obj/item/weapon/reagent_containers/food/snacks/tofu, - /obj/item/weapon/reagent_containers/food/snacks/stuffing - ) - result = /obj/item/weapon/reagent_containers/food/snacks/tofurkey - -/datum/recipe/zestfish - appliance = OVEN - fruit = list("lemon" = 1) - reagents = list("sodiumchloride" = 3) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/carpmeat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/zestfish - -/datum/recipe/limezestfish - appliance = OVEN - fruit = list("lime" = 1) - reagents = list("sodiumchloride" = 3) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/carpmeat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/zestfish - - -//Predesigned breads -//================================ -/datum/recipe/bread - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - reagents = list("sodiumchloride" = 1, "yeast" = 5) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/bread - -/datum/recipe/baguette - appliance = OVEN - reagents = list("sodiumchloride" = 1, "blackpepper" = 1, "yeast" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/baguette - - -/datum/recipe/tofubread - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/tofu, - /obj/item/weapon/reagent_containers/food/snacks/tofu, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/tofubread - - -/datum/recipe/creamcheesebread - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/creamcheesebread - -/datum/recipe/flatbread - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/flatbread - -/datum/recipe/tortilla - appliance = OVEN - reagents = list("flour" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/tortilla - result_quantity = 3 - -/datum/recipe/meatbread - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/meatbread - -/datum/recipe/syntibread - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/meatbread - -/datum/recipe/xenomeatbread - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/xenomeat, - /obj/item/weapon/reagent_containers/food/snacks/xenomeat, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/xenomeatbread - -/datum/recipe/bananabread - appliance = OVEN - fruit = list("banana" = 1) - reagents = list("milk" = 5, "sugar" = 15) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/bananabread - - -/datum/recipe/bun - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/bun - result_quantity = 3 - -//Predesigned pies -//======================= - -/datum/recipe/meatpie - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/meat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/meatpie - -/datum/recipe/tofupie - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/tofu - ) - result = /obj/item/weapon/reagent_containers/food/snacks/tofupie - -/datum/recipe/xemeatpie - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/xenomeat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/xemeatpie - -/datum/recipe/pie - appliance = OVEN - fruit = list("banana" = 1) - reagents = list("sugar" = 5) - items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) - result = /obj/item/weapon/reagent_containers/food/snacks/pie - -/datum/recipe/cherrypie - appliance = OVEN - fruit = list("cherries" = 1) - reagents = list("sugar" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/cherrypie - -/datum/recipe/amanita_pie - appliance = OVEN - reagents = list("amatoxin" = 5) - items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) - result = /obj/item/weapon/reagent_containers/food/snacks/amanita_pie - -/datum/recipe/plump_pie - appliance = OVEN - fruit = list("plumphelmet" = 1) - items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) - result = /obj/item/weapon/reagent_containers/food/snacks/plump_pie - -/datum/recipe/applepie - appliance = OVEN - fruit = list("apple" = 1) - items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) - result = /obj/item/weapon/reagent_containers/food/snacks/applepie - -/datum/recipe/pumpkinpie - appliance = OVEN - fruit = list("pumpkin" = 1) - reagents = list("sugar" = 5) - items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pumpkinpie - -/datum/recipe/appletart - appliance = OVEN - fruit = list("goldapple" = 1) - reagents = list("sugar" = 10) - items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) - result = /obj/item/weapon/reagent_containers/food/snacks/appletart - result_quantity = 2 - reagent_mix = RECIPE_REAGENT_REPLACE - -/datum/recipe/keylimepie - appliance = OVEN - fruit = list("lime" = 2) - reagents = list("milk" = 5, "sugar" = 5, "egg" = 3, "flour" = 10) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/keylimepie - reagent_mix = RECIPE_REAGENT_REPLACE //No raw egg in finished product, protein after cooking causes magic meatballs otherwise - -/datum/recipe/quiche - appliance = OVEN - reagents = list("milk" = 5, "egg" = 9, "flour" = 10) - items = list(/obj/item/weapon/reagent_containers/food/snacks/cheesewedge) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/quiche - reagent_mix = RECIPE_REAGENT_REPLACE //No raw egg in finished product, protein after cooking causes magic meatballs otherwise - -//Baked sweets: -//--------------- - -/datum/recipe/cookie - appliance = OVEN - reagents = list("milk" = 10, "sugar" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/chocolatebar - ) - result = /obj/item/weapon/reagent_containers/food/snacks/cookie - result_quantity = 4 - reagent_mix = RECIPE_REAGENT_REPLACE - -/datum/recipe/ovenfortunecookie - appliance = OVEN - reagents = list("sugar" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice, - /obj/item/weapon/paper - ) - result = /obj/item/weapon/reagent_containers/food/snacks/fortunecookie - -/datum/recipe/poppypretzel - appliance = OVEN - fruit = list("poppy" = 1) - items = list(/obj/item/weapon/reagent_containers/food/snacks/dough) - result = /obj/item/weapon/reagent_containers/food/snacks/poppypretzel - result_quantity = 2 - -/datum/recipe/cracker - appliance = OVEN - reagents = list("sodiumchloride" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice - ) - result = /obj/item/weapon/reagent_containers/food/snacks/cracker - -/datum/recipe/brownies - appliance = OVEN - reagents = list("browniemix" = 10, "egg" = 3) - reagent_mix = RECIPE_REAGENT_REPLACE //No egg or mix in final recipe - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/brownies - -/datum/recipe/cosmicbrownies - appliance = OVEN - reagents = list("browniemix" = 10, "egg" = 3) - fruit = list("ambrosia" = 1) - reagent_mix = RECIPE_REAGENT_REPLACE //No egg or mix in final recipe - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/cosmicbrownies - -/datum/recipe/buchedenoel - appliance = OVEN - fruit = list("berries" = 2) - reagents = list("cakebatter" = 20, "cream" = 10, "coco" = 5) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/buchedenoel - -/datum/recipe/cinnamonbun - appliance = OVEN - reagents = list("sugar" = 15, "cream" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/cinnamonbun - result_quantity = 4 - -/datum/recipe/jaffacake - appliance = OVEN - fruit = list("orange" = 1) - reagents = list("cakebatter" = 15, "coco" = 10) - result = /obj/item/weapon/reagent_containers/food/snacks/jaffacake - result_quantity = 6 - -//Pizzas -//========================= -/datum/recipe/pizzamargherita - appliance = OVEN - fruit = list("tomato" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/margherita - -/datum/recipe/meatpizza - appliance = OVEN - fruit = list("tomato" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza - -/datum/recipe/syntipizza - appliance = OVEN - fruit = list("tomato" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza - -/datum/recipe/mushroompizza - appliance = OVEN - fruit = list("mushroom" = 5, "tomato" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - - reagent_mix = RECIPE_REAGENT_REPLACE //No vomit taste in finished product from chanterelles - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/mushroompizza - -/datum/recipe/vegetablepizza - appliance = OVEN - fruit = list("eggplant" = 1, "carrot" = 1, "corn" = 1, "tomato" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/vegetablepizza - -/datum/recipe/pineapplepizza - appliance = OVEN - fruit = list("tomato" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/pineapple_ring, - /obj/item/weapon/reagent_containers/food/snacks/pineapple_ring - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/pineapple - -//Spicy -//================ - -/datum/recipe/enchiladas - appliance = OVEN - fruit = list("chili" = 2) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/cutlet, - /obj/item/weapon/reagent_containers/food/snacks/tortilla - ) - result = /obj/item/weapon/reagent_containers/food/snacks/enchiladas - - -// Cakes. -//============ -/datum/recipe/cake - appliance = OVEN - reagents = list("cakebatter" = 30, "vanilla" = 2) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/plaincake - reagent_mix = RECIPE_REAGENT_REPLACE - -/datum/recipe/cake/carrot - appliance = OVEN - fruit = list("carrot" = 3) - reagents = list("cakebatter" = 30) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/carrotcake - -/datum/recipe/cake/cheese - appliance = OVEN - reagents = list("cakebatter" = 30) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/cheesecake - -/datum/recipe/cake/peanut - fruit = list("peanut" = 1) - reagents = list("cakebatter" = 30, "peanutbutter" = 5) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/peanutcake - -/datum/recipe/cake/orange - appliance = OVEN - fruit = list("orange" = 2) - reagents = list("cakebatter" = 30) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/orangecake - -/datum/recipe/cake/lime - appliance = OVEN - fruit = list("lime" = 2) - reagents = list("cakebatter" = 30) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/limecake - -/datum/recipe/cake/lemon - appliance = OVEN - fruit = list("lemon" = 2) - reagents = list("cakebatter" = 30) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/lemoncake - -/datum/recipe/cake/chocolate - appliance = OVEN - reagents = list("cakebatter" = 30, "coco" = 5) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/chocolatecake - -/datum/recipe/cake/birthday - appliance = OVEN - reagents = list("cakebatter" = 30) - items = list(/obj/item/clothing/head/cakehat) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/birthdaycake - -/datum/recipe/cake/apple - appliance = OVEN - fruit = list("apple" = 2) - reagents = list("cakebatter" = 30) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/applecake - -/datum/recipe/cake/brain - appliance = OVEN - reagents = list("cakebatter" = 30) - items = list(/obj/item/organ/internal/brain) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/braincake - -/datum/recipe/pancakes - appliance = OVEN - reagents = list("milk" = 5, "sugar" = 15) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/pancakes - result_quantity = 2 - -/datum/recipe/pancakes/berry - appliance = OVEN - fruit = list("berries" = 2) - reagents = list("milk" = 5, "sugar" = 15) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/pancakes/berry - result_quantity = 2 - -/datum/recipe/lasagna - appliance = OVEN - fruit = list("tomato" = 2, "eggplant" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/cutlet, - /obj/item/weapon/reagent_containers/food/snacks/cutlet - ) - result = /obj/item/weapon/reagent_containers/food/snacks/lasagna - reagent_mix = RECIPE_REAGENT_REPLACE - -/datum/recipe/honeybun - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - reagents = list("milk" = 5, "egg" = 3,"honey" = 5) - result = /obj/item/weapon/reagent_containers/food/snacks/honeybun - result_quantity = 4 - -//Bacon -/datum/recipe/bacon_oven - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/rawbacon, - /obj/item/weapon/reagent_containers/food/snacks/rawbacon, - /obj/item/weapon/reagent_containers/food/snacks/rawbacon, - /obj/item/weapon/reagent_containers/food/snacks/rawbacon, - /obj/item/weapon/reagent_containers/food/snacks/rawbacon, - /obj/item/weapon/reagent_containers/food/snacks/rawbacon, - /obj/item/weapon/reagent_containers/food/snacks/spreads - ) - result = /obj/item/weapon/reagent_containers/food/snacks/bacon/oven - result_quantity = 6 - -/datum/recipe/meat_pocket - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/meatball, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/meat_pocket - result_quantity = 2 - -/datum/recipe/bacon_flatbread - appliance = OVEN - fruit = list("tomato" = 2) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/bacon, - /obj/item/weapon/reagent_containers/food/snacks/bacon, - /obj/item/weapon/reagent_containers/food/snacks/bacon, - /obj/item/weapon/reagent_containers/food/snacks/bacon - ) - result = /obj/item/weapon/reagent_containers/food/snacks/bacon_flatbread - -/datum/recipe/truffle - appliance = OVEN - reagents = list("sugar" = 5, "cream" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/chocolatebar - ) - reagent_mix = RECIPE_REAGENT_REPLACE - result = /obj/item/weapon/reagent_containers/food/snacks/truffle - result_quantity = 4 - -/datum/recipe/croissant - appliance = OVEN - reagents = list("sodiumchloride" = 1, "water" = 5, "milk" = 5, "yeast" = 5) - reagent_mix = RECIPE_REAGENT_REPLACE - items = list(/obj/item/weapon/reagent_containers/food/snacks/dough) - result = /obj/item/weapon/reagent_containers/food/snacks/croissant - result_quantity = 2 - -/datum/recipe/macncheese - appliance = OVEN - reagents = list("milk" = 5) - reagent_mix = RECIPE_REAGENT_REPLACE - items = list( - /obj/item/weapon/reagent_containers/food/snacks/spagetti, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/macncheese - -/datum/recipe/suppermatter - appliance = OVEN - reagents = list("radium" = 5, "milk" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/cheesecake - ) - reagent_mix = RECIPE_REAGENT_REPLACE - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/suppermatter - -/datum/recipe/excitingsuppermatter - appliance = OVEN - reagents = list("radium" = 5, "spacespice" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/cheesecake - ) - reagent_mix = RECIPE_REAGENT_REPLACE - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/excitingsuppermatter - -/datum/recipe/waffles - appliance = OVEN - reagents = list("sugar" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/waffles - result_quantity = 2 - -/datum/recipe/loadedbakedpotatooven - appliance = OVEN - fruit = list("potato" = 1) - items = list(/obj/item/weapon/reagent_containers/food/snacks/cheesewedge) - result = /obj/item/weapon/reagent_containers/food/snacks/loadedbakedpotato - -/datum/recipe/meatbun - appliance = OVEN - fruit = list("cabbage" = 1) - reagents = list("water" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/meatball, - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - ) - reagent_mix = RECIPE_REAGENT_REPLACE //Water used up in cooking - result = /obj/item/weapon/reagent_containers/food/snacks/meatbun - result_quantity = 2 - -/datum/recipe/spicedmeatbun - appliance = OVEN - reagents = list("spacespice" = 2, "water" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice, - /obj/item/weapon/reagent_containers/food/snacks/rawcutlet - ) - reagent_mix = RECIPE_REAGENT_REPLACE //Water used up in cooking - result = /obj/item/weapon/reagent_containers/food/snacks/spicedmeatbun - result_quantity = 2 - -/datum/recipe/custardbun - appliance = OVEN - reagents = list("spacespice" = 1, "water" = 5, "egg" = 3) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice - ) - reagent_mix = RECIPE_REAGENT_REPLACE //Water, egg used up in cooking - result = /obj/item/weapon/reagent_containers/food/snacks/custardbun - -/datum/recipe/chickenmomo - appliance = OVEN - reagents = list("spacespice" = 2, "water" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice, - /obj/item/weapon/reagent_containers/food/snacks/doughslice, - /obj/item/weapon/reagent_containers/food/snacks/doughslice, - /obj/item/weapon/reagent_containers/food/snacks/meat/chicken - ) - reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product - result = /obj/item/weapon/reagent_containers/food/snacks/chickenmomo - result_quantity = 2 - -/datum/recipe/veggiemomo - appliance = OVEN - reagents = list("spacespice" = 2, "water" = 5) - fruit = list("carrot" = 1, "cabbage" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice, - /obj/item/weapon/reagent_containers/food/snacks/doughslice, - /obj/item/weapon/reagent_containers/food/snacks/doughslice - ) - reagent_mix = RECIPE_REAGENT_REPLACE //Get that water outta here - result = /obj/item/weapon/reagent_containers/food/snacks/veggiemomo +/datum/recipe/ovenfries + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/rawsticks + ) + result = /obj/item/weapon/reagent_containers/food/snacks/ovenfries + +//Roasts +//--------------- + +/datum/recipe/dionaroast + appliance = OVEN + fruit = list("apple" = 1) + reagents = list("pacid" = 5) //It dissolves the carapace. Still poisonous, though. + items = list(/obj/item/weapon/holder/diona) + result = /obj/item/weapon/reagent_containers/food/snacks/dionaroast + reagent_mix = RECIPE_REAGENT_REPLACE //No eating polyacid + +/datum/recipe/monkeysdelight + appliance = OVEN + fruit = list("banana" = 1) + reagents = list("sodiumchloride" = 1, "blackpepper" = 1, "flour" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/monkeycube + ) + result = /obj/item/weapon/reagent_containers/food/snacks/monkeysdelight + reagent_mix = RECIPE_REAGENT_REPLACE + +/datum/recipe/ribplate + appliance = OVEN + reagents = list("honey" = 5, "spacespice" = 2, "blackpepper" = 1) + items = list(/obj/item/weapon/reagent_containers/food/snacks/meat) + reagent_mix = RECIPE_REAGENT_REPLACE + result = /obj/item/weapon/reagent_containers/food/snacks/ribplate + +/datum/recipe/turkey + appliance = OVEN + reagents = list("sodiumchloride" = 1, "blackpepper" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/meat/chicken, + /obj/item/weapon/reagent_containers/food/snacks/stuffing + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/turkey + +/datum/recipe/tofurkey + appliance = OVEN + reagents = list("sodiumchloride" = 1, "blackpepper" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/tofu, + /obj/item/weapon/reagent_containers/food/snacks/tofu, + /obj/item/weapon/reagent_containers/food/snacks/stuffing + ) + result = /obj/item/weapon/reagent_containers/food/snacks/tofurkey + +/datum/recipe/zestfish + appliance = OVEN + fruit = list("lemon" = 1) + reagents = list("sodiumchloride" = 3) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/carpmeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/zestfish + +/datum/recipe/limezestfish + appliance = OVEN + fruit = list("lime" = 1) + reagents = list("sodiumchloride" = 3) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/carpmeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/zestfish + + +//Predesigned breads +//================================ +/datum/recipe/bread + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + reagents = list("sodiumchloride" = 1, "yeast" = 5) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/bread + +/datum/recipe/baguette + appliance = OVEN + reagents = list("sodiumchloride" = 1, "blackpepper" = 1, "yeast" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/baguette + + +/datum/recipe/tofubread + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/tofu, + /obj/item/weapon/reagent_containers/food/snacks/tofu, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/tofubread + + +/datum/recipe/creamcheesebread + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/creamcheesebread + +/datum/recipe/flatbread + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/flatbread + +/datum/recipe/tortilla + appliance = OVEN + reagents = list("flour" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/tortilla + result_quantity = 3 + +/datum/recipe/meatbread + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/meatbread + +/datum/recipe/syntibread + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/meatbread + +/datum/recipe/xenomeatbread + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/xenomeat, + /obj/item/weapon/reagent_containers/food/snacks/xenomeat, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/xenomeatbread + +/datum/recipe/bananabread + appliance = OVEN + fruit = list("banana" = 1) + reagents = list("milk" = 5, "sugar" = 15) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/bananabread + + +/datum/recipe/bun + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/bun + result_quantity = 3 + +//Predesigned pies +//======================= + +/datum/recipe/meatpie + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/meat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/meatpie + +/datum/recipe/tofupie + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/tofu + ) + result = /obj/item/weapon/reagent_containers/food/snacks/tofupie + +/datum/recipe/xemeatpie + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/xenomeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/xemeatpie + +/datum/recipe/pie + appliance = OVEN + fruit = list("banana" = 1) + reagents = list("sugar" = 5) + items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) + result = /obj/item/weapon/reagent_containers/food/snacks/pie + +/datum/recipe/cherrypie + appliance = OVEN + fruit = list("cherries" = 1) + reagents = list("sugar" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/cherrypie + +/datum/recipe/amanita_pie + appliance = OVEN + reagents = list("amatoxin" = 5) + items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) + result = /obj/item/weapon/reagent_containers/food/snacks/amanita_pie + +/datum/recipe/plump_pie + appliance = OVEN + fruit = list("plumphelmet" = 1) + items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) + result = /obj/item/weapon/reagent_containers/food/snacks/plump_pie + +/datum/recipe/applepie + appliance = OVEN + fruit = list("apple" = 1) + items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) + result = /obj/item/weapon/reagent_containers/food/snacks/applepie + +/datum/recipe/pumpkinpie + appliance = OVEN + fruit = list("pumpkin" = 1) + reagents = list("sugar" = 5) + items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pumpkinpie + +/datum/recipe/appletart + appliance = OVEN + fruit = list("goldapple" = 1) + reagents = list("sugar" = 10) + items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) + result = /obj/item/weapon/reagent_containers/food/snacks/appletart + result_quantity = 2 + reagent_mix = RECIPE_REAGENT_REPLACE + +/datum/recipe/keylimepie + appliance = OVEN + fruit = list("lime" = 2) + reagents = list("milk" = 5, "sugar" = 5, "egg" = 3, "flour" = 10) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/keylimepie + reagent_mix = RECIPE_REAGENT_REPLACE //No raw egg in finished product, protein after cooking causes magic meatballs otherwise + +/datum/recipe/quiche + appliance = OVEN + reagents = list("milk" = 5, "egg" = 9, "flour" = 10) + items = list(/obj/item/weapon/reagent_containers/food/snacks/cheesewedge) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/quiche + reagent_mix = RECIPE_REAGENT_REPLACE //No raw egg in finished product, protein after cooking causes magic meatballs otherwise + +//Baked sweets: +//--------------- + +/datum/recipe/cookie + appliance = OVEN + reagents = list("milk" = 10, "sugar" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/chocolatebar + ) + result = /obj/item/weapon/reagent_containers/food/snacks/cookie + result_quantity = 4 + reagent_mix = RECIPE_REAGENT_REPLACE + +/datum/recipe/ovenfortunecookie + appliance = OVEN + reagents = list("sugar" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice, + /obj/item/weapon/paper + ) + result = /obj/item/weapon/reagent_containers/food/snacks/fortunecookie + +/datum/recipe/poppypretzel + appliance = OVEN + fruit = list("poppy" = 1) + items = list(/obj/item/weapon/reagent_containers/food/snacks/dough) + result = /obj/item/weapon/reagent_containers/food/snacks/poppypretzel + result_quantity = 2 + +/datum/recipe/cracker + appliance = OVEN + reagents = list("sodiumchloride" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice + ) + result = /obj/item/weapon/reagent_containers/food/snacks/cracker + +/datum/recipe/brownies + appliance = OVEN + reagents = list("browniemix" = 10, "egg" = 3) + reagent_mix = RECIPE_REAGENT_REPLACE //No egg or mix in final recipe + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/brownies + +/datum/recipe/cosmicbrownies + appliance = OVEN + reagents = list("browniemix" = 10, "egg" = 3) + fruit = list("ambrosia" = 1) + reagent_mix = RECIPE_REAGENT_REPLACE //No egg or mix in final recipe + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/cosmicbrownies + +/datum/recipe/buchedenoel + appliance = OVEN + fruit = list("berries" = 2) + reagents = list("cakebatter" = 20, "cream" = 10, "coco" = 5) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/buchedenoel + +/datum/recipe/cinnamonbun + appliance = OVEN + reagents = list("sugar" = 15, "cream" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/cinnamonbun + result_quantity = 4 + +/datum/recipe/jaffacake + appliance = OVEN + fruit = list("orange" = 1) + reagents = list("cakebatter" = 15, "coco" = 10) + result = /obj/item/weapon/reagent_containers/food/snacks/jaffacake + result_quantity = 6 + +//Pizzas +//========================= +/datum/recipe/pizzamargherita + appliance = OVEN + fruit = list("tomato" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/margherita + +/datum/recipe/meatpizza + appliance = OVEN + fruit = list("tomato" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza + +/datum/recipe/syntipizza + appliance = OVEN + fruit = list("tomato" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza + +/datum/recipe/mushroompizza + appliance = OVEN + fruit = list("mushroom" = 5, "tomato" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + + reagent_mix = RECIPE_REAGENT_REPLACE //No vomit taste in finished product from chanterelles + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/mushroompizza + +/datum/recipe/vegetablepizza + appliance = OVEN + fruit = list("eggplant" = 1, "carrot" = 1, "corn" = 1, "tomato" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/vegetablepizza + +/datum/recipe/pineapplepizza + appliance = OVEN + fruit = list("tomato" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/pineapple_ring, + /obj/item/weapon/reagent_containers/food/snacks/pineapple_ring + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/pineapple + +//Spicy +//================ + +/datum/recipe/enchiladas + appliance = OVEN + fruit = list("chili" = 2) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/cutlet, + /obj/item/weapon/reagent_containers/food/snacks/tortilla + ) + result = /obj/item/weapon/reagent_containers/food/snacks/enchiladas + + +// Cakes. +//============ +/datum/recipe/cake + appliance = OVEN + reagents = list("cakebatter" = 30, "vanilla" = 2) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/plaincake + reagent_mix = RECIPE_REAGENT_REPLACE + +/datum/recipe/cake/carrot + appliance = OVEN + fruit = list("carrot" = 3) + reagents = list("cakebatter" = 30) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/carrotcake + +/datum/recipe/cake/cheese + appliance = OVEN + reagents = list("cakebatter" = 30) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/cheesecake + +/datum/recipe/cake/peanut + fruit = list("peanut" = 1) + reagents = list("cakebatter" = 30, "peanutbutter" = 5) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/peanutcake + +/datum/recipe/cake/orange + appliance = OVEN + fruit = list("orange" = 2) + reagents = list("cakebatter" = 30) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/orangecake + +/datum/recipe/cake/lime + appliance = OVEN + fruit = list("lime" = 2) + reagents = list("cakebatter" = 30) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/limecake + +/datum/recipe/cake/lemon + appliance = OVEN + fruit = list("lemon" = 2) + reagents = list("cakebatter" = 30) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/lemoncake + +/datum/recipe/cake/chocolate + appliance = OVEN + reagents = list("cakebatter" = 30, "coco" = 5) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/chocolatecake + +/datum/recipe/cake/birthday + appliance = OVEN + reagents = list("cakebatter" = 30) + items = list(/obj/item/clothing/head/cakehat) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/birthdaycake + +/datum/recipe/cake/apple + appliance = OVEN + fruit = list("apple" = 2) + reagents = list("cakebatter" = 30) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/applecake + +/datum/recipe/cake/brain + appliance = OVEN + reagents = list("cakebatter" = 30) + items = list(/obj/item/organ/internal/brain) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/braincake + +/datum/recipe/pancakes + appliance = OVEN + reagents = list("milk" = 5, "sugar" = 15) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/pancakes + result_quantity = 2 + +/datum/recipe/pancakes/berry + appliance = OVEN + fruit = list("berries" = 2) + reagents = list("milk" = 5, "sugar" = 15) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/pancakes/berry + result_quantity = 2 + +/datum/recipe/lasagna + appliance = OVEN + fruit = list("tomato" = 2, "eggplant" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/cutlet, + /obj/item/weapon/reagent_containers/food/snacks/cutlet + ) + result = /obj/item/weapon/reagent_containers/food/snacks/lasagna + reagent_mix = RECIPE_REAGENT_REPLACE + +/datum/recipe/honeybun + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + reagents = list("milk" = 5, "egg" = 3,"honey" = 5) + result = /obj/item/weapon/reagent_containers/food/snacks/honeybun + result_quantity = 4 + +//Bacon +/datum/recipe/bacon_oven + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/rawbacon, + /obj/item/weapon/reagent_containers/food/snacks/rawbacon, + /obj/item/weapon/reagent_containers/food/snacks/rawbacon, + /obj/item/weapon/reagent_containers/food/snacks/rawbacon, + /obj/item/weapon/reagent_containers/food/snacks/rawbacon, + /obj/item/weapon/reagent_containers/food/snacks/rawbacon, + /obj/item/weapon/reagent_containers/food/snacks/spreads + ) + result = /obj/item/weapon/reagent_containers/food/snacks/bacon/oven + result_quantity = 6 + +/datum/recipe/meat_pocket + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/meatball, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/meat_pocket + result_quantity = 2 + +/datum/recipe/bacon_flatbread + appliance = OVEN + fruit = list("tomato" = 2) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/bacon, + /obj/item/weapon/reagent_containers/food/snacks/bacon, + /obj/item/weapon/reagent_containers/food/snacks/bacon, + /obj/item/weapon/reagent_containers/food/snacks/bacon + ) + result = /obj/item/weapon/reagent_containers/food/snacks/bacon_flatbread + +/datum/recipe/truffle + appliance = OVEN + reagents = list("sugar" = 5, "cream" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/chocolatebar + ) + reagent_mix = RECIPE_REAGENT_REPLACE + result = /obj/item/weapon/reagent_containers/food/snacks/truffle + result_quantity = 4 + +/datum/recipe/croissant + appliance = OVEN + reagents = list("sodiumchloride" = 1, "water" = 5, "milk" = 5, "yeast" = 5) + reagent_mix = RECIPE_REAGENT_REPLACE + items = list(/obj/item/weapon/reagent_containers/food/snacks/dough) + result = /obj/item/weapon/reagent_containers/food/snacks/croissant + result_quantity = 2 + +/datum/recipe/macncheese + appliance = OVEN + reagents = list("milk" = 5) + reagent_mix = RECIPE_REAGENT_REPLACE + items = list( + /obj/item/weapon/reagent_containers/food/snacks/spagetti, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/macncheese + +/datum/recipe/suppermatter + appliance = OVEN + reagents = list("radium" = 5, "milk" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/cheesecake + ) + reagent_mix = RECIPE_REAGENT_REPLACE + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/suppermatter + +/datum/recipe/excitingsuppermatter + appliance = OVEN + reagents = list("radium" = 5, "spacespice" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/cheesecake + ) + reagent_mix = RECIPE_REAGENT_REPLACE + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/excitingsuppermatter + +/datum/recipe/waffles + appliance = OVEN + reagents = list("sugar" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/waffles + result_quantity = 2 + +/datum/recipe/loadedbakedpotatooven + appliance = OVEN + fruit = list("potato" = 1) + items = list(/obj/item/weapon/reagent_containers/food/snacks/cheesewedge) + result = /obj/item/weapon/reagent_containers/food/snacks/loadedbakedpotato + +/datum/recipe/meatbun + appliance = OVEN + fruit = list("cabbage" = 1) + reagents = list("water" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/meatball, + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + ) + reagent_mix = RECIPE_REAGENT_REPLACE //Water used up in cooking + result = /obj/item/weapon/reagent_containers/food/snacks/meatbun + result_quantity = 2 + +/datum/recipe/spicedmeatbun + appliance = OVEN + reagents = list("spacespice" = 2, "water" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice, + /obj/item/weapon/reagent_containers/food/snacks/rawcutlet + ) + reagent_mix = RECIPE_REAGENT_REPLACE //Water used up in cooking + result = /obj/item/weapon/reagent_containers/food/snacks/spicedmeatbun + result_quantity = 2 + +/datum/recipe/custardbun + appliance = OVEN + reagents = list("spacespice" = 1, "water" = 5, "egg" = 3) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice + ) + reagent_mix = RECIPE_REAGENT_REPLACE //Water, egg used up in cooking + result = /obj/item/weapon/reagent_containers/food/snacks/custardbun + +/datum/recipe/chickenmomo + appliance = OVEN + reagents = list("spacespice" = 2, "water" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice, + /obj/item/weapon/reagent_containers/food/snacks/doughslice, + /obj/item/weapon/reagent_containers/food/snacks/doughslice, + /obj/item/weapon/reagent_containers/food/snacks/meat/chicken + ) + reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product + result = /obj/item/weapon/reagent_containers/food/snacks/chickenmomo + result_quantity = 2 + +/datum/recipe/veggiemomo + appliance = OVEN + reagents = list("spacespice" = 2, "water" = 5) + fruit = list("carrot" = 1, "cabbage" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice, + /obj/item/weapon/reagent_containers/food/snacks/doughslice, + /obj/item/weapon/reagent_containers/food/snacks/doughslice + ) + reagent_mix = RECIPE_REAGENT_REPLACE //Get that water outta here + result = /obj/item/weapon/reagent_containers/food/snacks/veggiemomo result_quantity = 2 \ No newline at end of file diff --git a/code/modules/food/recipes_oven_vr.dm b/code/modules/food/recipes_oven_vr.dm index b292a9ff447..fad763bf1fd 100644 --- a/code/modules/food/recipes_oven_vr.dm +++ b/code/modules/food/recipes_oven_vr.dm @@ -1,7 +1,7 @@ -/datum/recipe/scorpion - appliance = OVEN - reagents = list("sodiumchloride" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/scorpion - ) +/datum/recipe/scorpion + appliance = OVEN + reagents = list("sodiumchloride" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/scorpion + ) result = /obj/item/weapon/reagent_containers/food/snacks/scorpion_cooked \ No newline at end of file diff --git a/code/modules/gamemaster/defines.dm b/code/modules/gamemaster/defines.dm index 5bc17b462c7..215e07ff91a 100644 --- a/code/modules/gamemaster/defines.dm +++ b/code/modules/gamemaster/defines.dm @@ -1,3 +1,3 @@ -#define EVENT_CHAOS_THRESHOLD_HIGH_IMPACT 25 -#define EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT 50 +#define EVENT_CHAOS_THRESHOLD_HIGH_IMPACT 25 +#define EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT 50 #define EVENT_CHAOS_THRESHOLD_LOW_IMPACT 100 \ No newline at end of file diff --git a/code/modules/gamemaster/event2/event.dm b/code/modules/gamemaster/event2/event.dm index b9f1d92648d..50be78a14bf 100644 --- a/code/modules/gamemaster/event2/event.dm +++ b/code/modules/gamemaster/event2/event.dm @@ -1,240 +1,240 @@ -// This object holds the code that is needed to execute an event. -// Code for judging whether doing that event is a good idea or not belongs inside its meta event object. - - -/* - -Important: DO NOT `sleep()` in any of the procs here, or the GM will get stuck. Use callbacks insead. -Also please don't use spawn(), but use callbacks instead. - -Note that there is an important distinction between an event being ended, and an event being finished. -- Ended is for when the actual event is over, regardless of whether an announcement happened or not. -- Finished is for when both the event itself is over, and it was announced. The event will stop being -processed after it is finished. - -For an event to finish, it must have done two things: -- Go through its entire cycle, of start() -> end(), and -- Have the event be announced. -If an event has ended, but the announcement didn't happen, the event will not be finished. -This allows for events that have their announcement happen after the end itself. - -*/ - -// -/datum/event2/event - var/announced = FALSE // Is set to TRUE when `announce()` is called by `process()`. - var/started = FALSE // Is set to TRUE when `start()` is called by `process()`. - var/ended = FALSE // Is set to TRUE when `end()` is called by `process()`. - var/finished = FALSE // Is set to TRUE when `ended` and `announced` are TRUE. - - // `world.time`s when this event started, and finished, for bookkeeping. - var/time_started = null - var/time_finished = null - - // If these are set, the announcement will be delayed by a random time between the lower and upper bounds. - // If the upper bound is not defined, then it will use the lower bound instead. - // Note that this is independant of the event itself, so you can have the announcement happen long after the event ended. - // This may not work if should_announce() is overrided. - var/announce_delay_lower_bound = null - var/announce_delay_upper_bound = null - - // If these are set, the event will be delayed by a random time between the lower and upper bounds. - // If the upper bound is not defined, then it will use the lower bound instead. - // This may not work if should_start() is overrided. - var/start_delay_lower_bound = null - var/start_delay_upper_bound = null - - // If these are set, the event will automatically end at a random time between the lower and upper bounds. - // If the upper bound is not defined, then it will use the lower bound instead. - // This may not work if should_end() is overrided. - var/length_lower_bound = null - var/length_upper_bound = null - - // Set automatically, don't touch. - var/time_to_start = null - var/time_to_announce = null - var/time_to_end = null - - // These are also set automatically, and are provided for events to know what RNG decided for the various durations. - var/start_delay = null - var/announce_delay = null - var/length = null - -// Returns the name of where the event is taking place. -// In the future this might be handy for off-station events. -/datum/event2/event/proc/location_name() - return station_name() - -// Returns the z-levels that are involved with the event. -// In the future this might be handy for off-station events. -/datum/event2/event/proc/get_location_z_levels(space_only = FALSE) - . = using_map.station_levels.Copy() - if(space_only) - for(var/z_level in .) - if(is_planet_z_level(z_level)) - . -= z_level - - -/datum/event2/event/proc/is_planet_z_level(z_level) - var/datum/planet/P = LAZYACCESS(SSplanets.z_to_planet, z_level) - if(!istype(P)) - return FALSE - return TRUE - -// Returns a list of empty turfs in the same area. -/datum/event2/event/proc/find_random_turfs(minimum_free_space = 5, list/specific_areas = list(), ignore_occupancy = FALSE) - var/list/area/grand_list_of_areas = find_random_areas(specific_areas) - - if(!LAZYLEN(grand_list_of_areas)) - return list() - - for(var/list/A as anything in grand_list_of_areas) - var/list/turfs = list() - for(var/turf/T in A) - if(!T.check_density()) - turfs += T - - if(turfs.len < minimum_free_space) - continue // Not enough free space. - return turfs - - return list() - -/datum/event2/event/proc/find_random_areas(list/specific_areas = list(), ignore_occupancy = FALSE) - if(!LAZYLEN(specific_areas)) - specific_areas = global.the_station_areas.Copy() - - var/list/area/grand_list_of_areas = get_all_existing_areas_of_types(specific_areas) - . = list() - for(var/area/A as anything in shuffle(grand_list_of_areas)) - if(A.forbid_events) - continue - if(!(A.z in get_location_z_levels())) - continue - if(!ignore_occupancy && is_area_occupied(A)) - continue // Occupied. - . += A - - -// Starts the event. -/datum/event2/event/proc/execute() - time_started = world.time - - if(announce_delay_lower_bound) - announce_delay = rand(announce_delay_lower_bound, announce_delay_upper_bound ? announce_delay_upper_bound : announce_delay_lower_bound) - time_to_announce = world.time + announce_delay - - if(start_delay_lower_bound) - start_delay = rand(start_delay_lower_bound, start_delay_upper_bound ? start_delay_upper_bound : start_delay_lower_bound) - time_to_start = world.time + start_delay - - if(length_lower_bound) - var/starting_point = time_to_start ? time_to_start : world.time - length = rand(length_lower_bound, length_upper_bound ? length_upper_bound : length_lower_bound) - time_to_end = starting_point + length - - set_up() - -// Called at the very end of the event's lifecycle, or when aborted. -// Don't override this, use `end()` for cleanup instead. -/datum/event2/event/proc/finish() - finished = TRUE - time_finished = world.time - -// Called by admins wanting to stop an event immediately. -/datum/event2/event/proc/abort() - if(!announced) - announce() - if(!ended) // `end()` generally has cleanup procs, so call that. - end() - finish() - -// Called by the GM processer. -/datum/event2/event/process() - // Handle announcement track. - if(!announced && should_announce()) - announced = TRUE - announce() - - // Handle event track. - if(!started) - if(should_start()) - started = TRUE - start() - else - wait_tick() - - if(started && !ended) - if(should_end()) - ended = TRUE - end() - else - event_tick() - - // In order to be finished, the event needs to end, and be announced. - if(ended && announced) - finish() - -/datum/event2/event/Topic(href, href_list) - if(..()) - return - - if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) - message_admins("[usr] has attempted to manipulate an event without sufficent privilages.") - return - - if(href_list["abort"]) - abort() - message_admins("Event '[type]' was aborted by [usr.key].") - - // SSgame_master.interact(usr) // To refresh the UI. // VOREStation Edit - We don't use SSgame_master yet. - -/* - * Procs to Override - */ - -// Override this for code to be ran before the event is started. -/datum/event2/event/proc/set_up() - -// Called every tick from the GM system, and determines if the announcement should happen. -// Override this for special logic on when it should be announced, e.g. after `ended` is set to TRUE, -// however be aware that the event cannot finish until this returns TRUE at some point. -/datum/event2/event/proc/should_announce() - if(!time_to_announce) - return TRUE - return time_to_announce <= world.time - -// Override this for code that alerts the crew that the event is happening in some form, e.g. a centcom announcement or some other message. -// If you want them to not know, you can just not override it. -/datum/event2/event/proc/announce() - -// Override for code that runs every few seconds, while the event is waiting for `should_start()` to return TRUE. -// Note that events that have `should_start()` return TRUE at the start will never have this proc called. -/datum/event2/event/proc/wait_tick() - -// Called every tick from the GM system, and determines if the event should offically start. -// Override this for special logic on when it should start. -/datum/event2/event/proc/should_start() - if(!time_to_start) - return TRUE - return time_to_start <= world.time - -// Override this for code to do the actual event. -/datum/event2/event/proc/start() - - -// Override for code that runs every few seconds, while the event is waiting for `should_end()` to return TRUE. -// Note that events that have `should_end()` return TRUE at the start will never have this proc called. -/datum/event2/event/proc/event_tick() - - -// Called every tick from the GM system, and determines if the event should end. -// If this returns TRUE at the very start, then the event ends instantly and `tick()` will never be called. -// Override this for special logic on when it should end, e.g. blob core has to die before event ends. -/datum/event2/event/proc/should_end() - if(!time_to_end) - return TRUE - return time_to_end <= world.time - -// Override this for code to run when the event is over, e.g. cleanup. -/datum/event2/event/proc/end() +// This object holds the code that is needed to execute an event. +// Code for judging whether doing that event is a good idea or not belongs inside its meta event object. + + +/* + +Important: DO NOT `sleep()` in any of the procs here, or the GM will get stuck. Use callbacks insead. +Also please don't use spawn(), but use callbacks instead. + +Note that there is an important distinction between an event being ended, and an event being finished. +- Ended is for when the actual event is over, regardless of whether an announcement happened or not. +- Finished is for when both the event itself is over, and it was announced. The event will stop being +processed after it is finished. + +For an event to finish, it must have done two things: +- Go through its entire cycle, of start() -> end(), and +- Have the event be announced. +If an event has ended, but the announcement didn't happen, the event will not be finished. +This allows for events that have their announcement happen after the end itself. + +*/ + +// +/datum/event2/event + var/announced = FALSE // Is set to TRUE when `announce()` is called by `process()`. + var/started = FALSE // Is set to TRUE when `start()` is called by `process()`. + var/ended = FALSE // Is set to TRUE when `end()` is called by `process()`. + var/finished = FALSE // Is set to TRUE when `ended` and `announced` are TRUE. + + // `world.time`s when this event started, and finished, for bookkeeping. + var/time_started = null + var/time_finished = null + + // If these are set, the announcement will be delayed by a random time between the lower and upper bounds. + // If the upper bound is not defined, then it will use the lower bound instead. + // Note that this is independant of the event itself, so you can have the announcement happen long after the event ended. + // This may not work if should_announce() is overrided. + var/announce_delay_lower_bound = null + var/announce_delay_upper_bound = null + + // If these are set, the event will be delayed by a random time between the lower and upper bounds. + // If the upper bound is not defined, then it will use the lower bound instead. + // This may not work if should_start() is overrided. + var/start_delay_lower_bound = null + var/start_delay_upper_bound = null + + // If these are set, the event will automatically end at a random time between the lower and upper bounds. + // If the upper bound is not defined, then it will use the lower bound instead. + // This may not work if should_end() is overrided. + var/length_lower_bound = null + var/length_upper_bound = null + + // Set automatically, don't touch. + var/time_to_start = null + var/time_to_announce = null + var/time_to_end = null + + // These are also set automatically, and are provided for events to know what RNG decided for the various durations. + var/start_delay = null + var/announce_delay = null + var/length = null + +// Returns the name of where the event is taking place. +// In the future this might be handy for off-station events. +/datum/event2/event/proc/location_name() + return station_name() + +// Returns the z-levels that are involved with the event. +// In the future this might be handy for off-station events. +/datum/event2/event/proc/get_location_z_levels(space_only = FALSE) + . = using_map.station_levels.Copy() + if(space_only) + for(var/z_level in .) + if(is_planet_z_level(z_level)) + . -= z_level + + +/datum/event2/event/proc/is_planet_z_level(z_level) + var/datum/planet/P = LAZYACCESS(SSplanets.z_to_planet, z_level) + if(!istype(P)) + return FALSE + return TRUE + +// Returns a list of empty turfs in the same area. +/datum/event2/event/proc/find_random_turfs(minimum_free_space = 5, list/specific_areas = list(), ignore_occupancy = FALSE) + var/list/area/grand_list_of_areas = find_random_areas(specific_areas) + + if(!LAZYLEN(grand_list_of_areas)) + return list() + + for(var/list/A as anything in grand_list_of_areas) + var/list/turfs = list() + for(var/turf/T in A) + if(!T.check_density()) + turfs += T + + if(turfs.len < minimum_free_space) + continue // Not enough free space. + return turfs + + return list() + +/datum/event2/event/proc/find_random_areas(list/specific_areas = list(), ignore_occupancy = FALSE) + if(!LAZYLEN(specific_areas)) + specific_areas = global.the_station_areas.Copy() + + var/list/area/grand_list_of_areas = get_all_existing_areas_of_types(specific_areas) + . = list() + for(var/area/A as anything in shuffle(grand_list_of_areas)) + if(A.forbid_events) + continue + if(!(A.z in get_location_z_levels())) + continue + if(!ignore_occupancy && is_area_occupied(A)) + continue // Occupied. + . += A + + +// Starts the event. +/datum/event2/event/proc/execute() + time_started = world.time + + if(announce_delay_lower_bound) + announce_delay = rand(announce_delay_lower_bound, announce_delay_upper_bound ? announce_delay_upper_bound : announce_delay_lower_bound) + time_to_announce = world.time + announce_delay + + if(start_delay_lower_bound) + start_delay = rand(start_delay_lower_bound, start_delay_upper_bound ? start_delay_upper_bound : start_delay_lower_bound) + time_to_start = world.time + start_delay + + if(length_lower_bound) + var/starting_point = time_to_start ? time_to_start : world.time + length = rand(length_lower_bound, length_upper_bound ? length_upper_bound : length_lower_bound) + time_to_end = starting_point + length + + set_up() + +// Called at the very end of the event's lifecycle, or when aborted. +// Don't override this, use `end()` for cleanup instead. +/datum/event2/event/proc/finish() + finished = TRUE + time_finished = world.time + +// Called by admins wanting to stop an event immediately. +/datum/event2/event/proc/abort() + if(!announced) + announce() + if(!ended) // `end()` generally has cleanup procs, so call that. + end() + finish() + +// Called by the GM processer. +/datum/event2/event/process() + // Handle announcement track. + if(!announced && should_announce()) + announced = TRUE + announce() + + // Handle event track. + if(!started) + if(should_start()) + started = TRUE + start() + else + wait_tick() + + if(started && !ended) + if(should_end()) + ended = TRUE + end() + else + event_tick() + + // In order to be finished, the event needs to end, and be announced. + if(ended && announced) + finish() + +/datum/event2/event/Topic(href, href_list) + if(..()) + return + + if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) + message_admins("[usr] has attempted to manipulate an event without sufficent privilages.") + return + + if(href_list["abort"]) + abort() + message_admins("Event '[type]' was aborted by [usr.key].") + + // SSgame_master.interact(usr) // To refresh the UI. // VOREStation Edit - We don't use SSgame_master yet. + +/* + * Procs to Override + */ + +// Override this for code to be ran before the event is started. +/datum/event2/event/proc/set_up() + +// Called every tick from the GM system, and determines if the announcement should happen. +// Override this for special logic on when it should be announced, e.g. after `ended` is set to TRUE, +// however be aware that the event cannot finish until this returns TRUE at some point. +/datum/event2/event/proc/should_announce() + if(!time_to_announce) + return TRUE + return time_to_announce <= world.time + +// Override this for code that alerts the crew that the event is happening in some form, e.g. a centcom announcement or some other message. +// If you want them to not know, you can just not override it. +/datum/event2/event/proc/announce() + +// Override for code that runs every few seconds, while the event is waiting for `should_start()` to return TRUE. +// Note that events that have `should_start()` return TRUE at the start will never have this proc called. +/datum/event2/event/proc/wait_tick() + +// Called every tick from the GM system, and determines if the event should offically start. +// Override this for special logic on when it should start. +/datum/event2/event/proc/should_start() + if(!time_to_start) + return TRUE + return time_to_start <= world.time + +// Override this for code to do the actual event. +/datum/event2/event/proc/start() + + +// Override for code that runs every few seconds, while the event is waiting for `should_end()` to return TRUE. +// Note that events that have `should_end()` return TRUE at the start will never have this proc called. +/datum/event2/event/proc/event_tick() + + +// Called every tick from the GM system, and determines if the event should end. +// If this returns TRUE at the very start, then the event ends instantly and `tick()` will never be called. +// Override this for special logic on when it should end, e.g. blob core has to die before event ends. +/datum/event2/event/proc/should_end() + if(!time_to_end) + return TRUE + return time_to_end <= world.time + +// Override this for code to run when the event is over, e.g. cleanup. +/datum/event2/event/proc/end() diff --git a/code/modules/gamemaster/event2/events/cargo/shipping_error.dm b/code/modules/gamemaster/event2/events/cargo/shipping_error.dm index eaab32cb8a5..34d97dcccdb 100644 --- a/code/modules/gamemaster/event2/events/cargo/shipping_error.dm +++ b/code/modules/gamemaster/event2/events/cargo/shipping_error.dm @@ -1,16 +1,16 @@ -/datum/event2/meta/shipping_error - name = "shipping error" - departments = list(DEPARTMENT_CARGO) - chaos = -10 // A helpful event. - reusable = TRUE - event_type = /datum/event2/event/shipping_error - -/datum/event2/meta/shipping_error/get_weight() - return (metric.count_people_with_job(/datum/job/cargo_tech) + metric.count_people_with_job(/datum/job/qm)) * 30 - -/datum/event2/event/shipping_error/start() - var/datum/supply_order/O = new /datum/supply_order() - O.ordernum = SSsupply.ordernum - O.object = SSsupply.supply_pack[pick(SSsupply.supply_pack)] - O.ordered_by = random_name(pick(MALE,FEMALE), species = "Human") - SSsupply.shoppinglist += O +/datum/event2/meta/shipping_error + name = "shipping error" + departments = list(DEPARTMENT_CARGO) + chaos = -10 // A helpful event. + reusable = TRUE + event_type = /datum/event2/event/shipping_error + +/datum/event2/meta/shipping_error/get_weight() + return (metric.count_people_with_job(/datum/job/cargo_tech) + metric.count_people_with_job(/datum/job/qm)) * 30 + +/datum/event2/event/shipping_error/start() + var/datum/supply_order/O = new /datum/supply_order() + O.ordernum = SSsupply.ordernum + O.object = SSsupply.supply_pack[pick(SSsupply.supply_pack)] + O.ordered_by = random_name(pick(MALE,FEMALE), species = "Human") + SSsupply.shoppinglist += O diff --git a/code/modules/gamemaster/event2/events/command/manifest_malfunction.dm b/code/modules/gamemaster/event2/events/command/manifest_malfunction.dm index 0a19ec7a655..9c41235666a 100644 --- a/code/modules/gamemaster/event2/events/command/manifest_malfunction.dm +++ b/code/modules/gamemaster/event2/events/command/manifest_malfunction.dm @@ -1,61 +1,61 @@ -/datum/event2/meta/manifest_malfunction - name = "manifest_malfunction" - departments = list(DEPARTMENT_COMMAND, DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - event_type = /datum/event2/event/manifest_malfunction - -/datum/event2/meta/manifest_malfunction/get_weight() - var/security = metric.count_people_in_department(DEPARTMENT_SECURITY) - - if(!security || !data_core) - return 0 - - var/command = metric.count_people_with_job(/datum/job/hop) + metric.count_people_with_job(/datum/job/captain) - var/synths = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) - var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - (synths + security + command) // So they don't get counted twice. - - return (security * 10) + (synths * 20) + (command * 20) + (everyone * 5) - - - -/datum/event2/event/manifest_malfunction - announce_delay_lower_bound = 5 MINUTES - announce_delay_upper_bound = 10 MINUTES - var/records_to_delete = 2 - var/record_class_to_delete = null - -/datum/event2/event/manifest_malfunction/set_up() - record_class_to_delete = pickweight(list("medical" = 10, "security" = 30)) - -/datum/event2/event/manifest_malfunction/announce() - if(prob(30)) - var/message = null - var/author = null - var/rng = rand(1, 2) - switch(rng) - if(1) - author = "Data Breach Alert" - message = "The [record_class_to_delete] record database has suffered from an attack by one or more hackers. \ - They appear to have wiped several records, before disconnecting." - if(2) - author = "Downtime Alert" - message = "The [record_class_to_delete] record database server has suffered a hardware failure, and is no longer functional. \ - A temporary replacement server has been activated, containing recovered data from the main server. \ - A few records became corrupted, and could not be transferred." - command_announcement.Announce(message, author) - -/datum/event2/event/manifest_malfunction/start() - for(var/i = 1 to records_to_delete) - var/datum/data/record/R - - switch(record_class_to_delete) - if("security") - R = safepick(data_core.security) - - if("medical") - R = safepick(data_core.medical) - - if(R) - log_debug("Manifest malfunction event is now deleting [R.fields["name"]]'s [record_class_to_delete] record.") - qdel(R) +/datum/event2/meta/manifest_malfunction + name = "manifest_malfunction" + departments = list(DEPARTMENT_COMMAND, DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/manifest_malfunction + +/datum/event2/meta/manifest_malfunction/get_weight() + var/security = metric.count_people_in_department(DEPARTMENT_SECURITY) + + if(!security || !data_core) + return 0 + + var/command = metric.count_people_with_job(/datum/job/hop) + metric.count_people_with_job(/datum/job/captain) + var/synths = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) + var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - (synths + security + command) // So they don't get counted twice. + + return (security * 10) + (synths * 20) + (command * 20) + (everyone * 5) + + + +/datum/event2/event/manifest_malfunction + announce_delay_lower_bound = 5 MINUTES + announce_delay_upper_bound = 10 MINUTES + var/records_to_delete = 2 + var/record_class_to_delete = null + +/datum/event2/event/manifest_malfunction/set_up() + record_class_to_delete = pickweight(list("medical" = 10, "security" = 30)) + +/datum/event2/event/manifest_malfunction/announce() + if(prob(30)) + var/message = null + var/author = null + var/rng = rand(1, 2) + switch(rng) + if(1) + author = "Data Breach Alert" + message = "The [record_class_to_delete] record database has suffered from an attack by one or more hackers. \ + They appear to have wiped several records, before disconnecting." + if(2) + author = "Downtime Alert" + message = "The [record_class_to_delete] record database server has suffered a hardware failure, and is no longer functional. \ + A temporary replacement server has been activated, containing recovered data from the main server. \ + A few records became corrupted, and could not be transferred." + command_announcement.Announce(message, author) + +/datum/event2/event/manifest_malfunction/start() + for(var/i = 1 to records_to_delete) + var/datum/data/record/R + + switch(record_class_to_delete) + if("security") + R = safepick(data_core.security) + + if("medical") + R = safepick(data_core.medical) + + if(R) + log_debug("Manifest malfunction event is now deleting [R.fields["name"]]'s [record_class_to_delete] record.") + qdel(R) diff --git a/code/modules/gamemaster/event2/events/command/money_hacker.dm b/code/modules/gamemaster/event2/events/command/money_hacker.dm index a1dbd1e18b3..287844cf2df 100644 --- a/code/modules/gamemaster/event2/events/command/money_hacker.dm +++ b/code/modules/gamemaster/event2/events/command/money_hacker.dm @@ -1,110 +1,110 @@ -/datum/event2/meta/money_hacker - name = "money hacker" - departments = list(DEPARTMENT_COMMAND) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - event_type = /datum/event2/event/money_hacker - -/datum/event2/meta/money_hacker/get_weight() - var/command = metric.count_people_with_job(/datum/job/hop) + metric.count_people_with_job(/datum/job/captain) - - if(!command) - return 0 - return 30 + (command * 20) + (all_money_accounts.len * 5) - - - -/datum/event2/event/money_hacker - length_lower_bound = 8 MINUTES - length_upper_bound = 12 MINUTES - var/datum/money_account/targeted_account = null - -/datum/event2/event/money_hacker/set_up() - if(LAZYLEN(all_money_accounts)) - targeted_account = pick(all_money_accounts) - - if(!targeted_account) - log_debug("Money hacker event could not find an account to hack. Aborting.") - abort() - return - -/datum/event2/event/money_hacker/announce() - var/message = "A brute force hack has been detected (in progress since [stationtime2text()]). The target of the attack is: Financial account #[targeted_account.account_number], \ - without intervention this attack will succeed in approximately 10 minutes. Required intervention: temporary suspension of affected accounts until the attack has ceased. \ - Notifications will be sent as updates occur." - var/my_department = "[location_name()] Firewall Subroutines" - - for(var/obj/machinery/message_server/MS in machines) - if(!MS.active) - continue - MS.send_rc_message("Head of Personnel's Desk", my_department, "[message]
                    ", "", "", 2) - - // Nobody reads the requests consoles so lets use the radio as well. - global_announcer.autosay(message, my_department, DEPARTMENT_COMMAND) - -/datum/event2/event/money_hacker/end() - var/message = null - if(targeted_account && !targeted_account.suspended) // Hacker wins. - message = "The hack attempt has succeeded." - hack_account(targeted_account) - log_debug("Money hacker event managed to hack the targeted account.") - - else // Crew wins. - message = "The attack has ceased, the affected accounts can now be brought online." - log_debug("Money hacker event failed to hack the targeted account due to intervention by the crew.") - - var/my_department = "[location_name()] Firewall Subroutines" - - for(var/obj/machinery/message_server/MS in machines) - if(!MS.active) continue - MS.send_rc_message("Head of Personnel's Desk", my_department, message, "", "", 2) - - global_announcer.autosay(message, my_department, DEPARTMENT_COMMAND) - -/datum/event2/event/money_hacker/proc/hack_account(datum/money_account/A) - // Subtract the money. - var/lost = A.money * 0.8 + (rand(2,4) - 2) / 10 - A.money -= lost - - // Create a taunting log entry. - var/datum/transaction/T = new() - T.target_name = pick(list( - "", - "yo brotha from anotha motha", - "el Presidente", - "chieF smackDowN", - "Nobody" - )) - - T.purpose = pick(list( - "Ne$ ---ount fu%ds init*&lisat@*n", - "PAY BACK YOUR MUM", - "Funds withdrawal", - "pWnAgE", - "l33t hax", - "liberationez", - "Hit", - "Nothing" - )) - - T.amount = pick(list( - "", - "([rand(0,99999)])", - "alla money", - "9001$", - "HOLLA HOLLA GET DOLLA", - "([lost])", - "69,420t" - )) - - var/date1 = "1 January 1970" // Unix epoch. - var/date2 = "[num2text(rand(1,31))] [pick("January","February","March","April","May","June","July","August","September","October","November","December")], [rand(1000,3000)]" - T.date = pick("", current_date_string, date1, date2,"Nowhen") - - var/time1 = rand(0, 99999999) - var/time2 = "[round(time1 / 36000)+12]:[(time1 / 600 % 60) < 10 ? add_zero(time1 / 600 % 60, 1) : time1 / 600 % 60]" - T.time = pick("", stationtime2text(), time2, "Never") - - T.source_terminal = pick("","[pick("Biesel","New Gibson")] GalaxyNet Terminal #[rand(111,999)]","your mums place","nantrasen high CommanD","Angessa's Pearl","Nowhere") - - A.transaction_log.Add(T) +/datum/event2/meta/money_hacker + name = "money hacker" + departments = list(DEPARTMENT_COMMAND) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/money_hacker + +/datum/event2/meta/money_hacker/get_weight() + var/command = metric.count_people_with_job(/datum/job/hop) + metric.count_people_with_job(/datum/job/captain) + + if(!command) + return 0 + return 30 + (command * 20) + (all_money_accounts.len * 5) + + + +/datum/event2/event/money_hacker + length_lower_bound = 8 MINUTES + length_upper_bound = 12 MINUTES + var/datum/money_account/targeted_account = null + +/datum/event2/event/money_hacker/set_up() + if(LAZYLEN(all_money_accounts)) + targeted_account = pick(all_money_accounts) + + if(!targeted_account) + log_debug("Money hacker event could not find an account to hack. Aborting.") + abort() + return + +/datum/event2/event/money_hacker/announce() + var/message = "A brute force hack has been detected (in progress since [stationtime2text()]). The target of the attack is: Financial account #[targeted_account.account_number], \ + without intervention this attack will succeed in approximately 10 minutes. Required intervention: temporary suspension of affected accounts until the attack has ceased. \ + Notifications will be sent as updates occur." + var/my_department = "[location_name()] Firewall Subroutines" + + for(var/obj/machinery/message_server/MS in machines) + if(!MS.active) + continue + MS.send_rc_message("Head of Personnel's Desk", my_department, "[message]
                    ", "", "", 2) + + // Nobody reads the requests consoles so lets use the radio as well. + global_announcer.autosay(message, my_department, DEPARTMENT_COMMAND) + +/datum/event2/event/money_hacker/end() + var/message = null + if(targeted_account && !targeted_account.suspended) // Hacker wins. + message = "The hack attempt has succeeded." + hack_account(targeted_account) + log_debug("Money hacker event managed to hack the targeted account.") + + else // Crew wins. + message = "The attack has ceased, the affected accounts can now be brought online." + log_debug("Money hacker event failed to hack the targeted account due to intervention by the crew.") + + var/my_department = "[location_name()] Firewall Subroutines" + + for(var/obj/machinery/message_server/MS in machines) + if(!MS.active) continue + MS.send_rc_message("Head of Personnel's Desk", my_department, message, "", "", 2) + + global_announcer.autosay(message, my_department, DEPARTMENT_COMMAND) + +/datum/event2/event/money_hacker/proc/hack_account(datum/money_account/A) + // Subtract the money. + var/lost = A.money * 0.8 + (rand(2,4) - 2) / 10 + A.money -= lost + + // Create a taunting log entry. + var/datum/transaction/T = new() + T.target_name = pick(list( + "", + "yo brotha from anotha motha", + "el Presidente", + "chieF smackDowN", + "Nobody" + )) + + T.purpose = pick(list( + "Ne$ ---ount fu%ds init*&lisat@*n", + "PAY BACK YOUR MUM", + "Funds withdrawal", + "pWnAgE", + "l33t hax", + "liberationez", + "Hit", + "Nothing" + )) + + T.amount = pick(list( + "", + "([rand(0,99999)])", + "alla money", + "9001$", + "HOLLA HOLLA GET DOLLA", + "([lost])", + "69,420t" + )) + + var/date1 = "1 January 1970" // Unix epoch. + var/date2 = "[num2text(rand(1,31))] [pick("January","February","March","April","May","June","July","August","September","October","November","December")], [rand(1000,3000)]" + T.date = pick("", current_date_string, date1, date2,"Nowhen") + + var/time1 = rand(0, 99999999) + var/time2 = "[round(time1 / 36000)+12]:[(time1 / 600 % 60) < 10 ? add_zero(time1 / 600 % 60, 1) : time1 / 600 % 60]" + T.time = pick("", stationtime2text(), time2, "Never") + + T.source_terminal = pick("","[pick("Biesel","New Gibson")] GalaxyNet Terminal #[rand(111,999)]","your mums place","nantrasen high CommanD","Angessa's Pearl","Nowhere") + + A.transaction_log.Add(T) diff --git a/code/modules/gamemaster/event2/events/command/raise_funds.dm b/code/modules/gamemaster/event2/events/command/raise_funds.dm index 31e88163501..1540d74e80b 100644 --- a/code/modules/gamemaster/event2/events/command/raise_funds.dm +++ b/code/modules/gamemaster/event2/events/command/raise_funds.dm @@ -1,96 +1,96 @@ -/datum/event2/meta/raise_funds - name = "local funding drive" - enabled = FALSE // There isn't really any suitable way for the crew to generate thalers right now, if that gets fixed feel free to turn this event on. - departments = list(DEPARTMENT_COMMAND, DEPARTMENT_CARGO) - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - event_type = /datum/event2/event/raise_funds - -/datum/event2/meta/raise_funds/get_weight() - var/command = metric.count_people_in_department(DEPARTMENT_COMMAND) - if(!command) // Need someone to read the centcom message. - return 0 - - var/cargo = metric.count_people_in_department(DEPARTMENT_CARGO) - return (command * 20) + (cargo * 20) - - - -/datum/event2/event/raise_funds - length_lower_bound = 30 MINUTES - length_upper_bound = 45 MINUTES - var/money_at_start = 0 - -/datum/event2/event/raise_funds/announce() - var/message = "Due to [pick("recent", "unfortunate", "possible future")] budget \ - [pick("changes", "issues")], in-system stations are now advised to increase funding income." - - send_command_report("Budget Advisement", message) - -/datum/event2/event/raise_funds/start() - // Note that the event remembers the amount of money when it started. If an issue develops where people try to scam centcom by - // taking out loads of money before the event, then depositing it back in after the event fires, feel free to make this check for - // roundstart money instead. - money_at_start = count_money() - log_debug("Funding Drive event logged a sum of [money_at_start] thalers in all station accounts at the start of the event.") - -/datum/event2/event/raise_funds/end() - var/money_at_end = count_money() - log_debug("Funding Drive event logged a sum of [money_at_end] thalers in all station accounts at the end of the event, compared \ - to [money_at_start] thalers. A difference of [money_at_end / money_at_start] was calculated.") - - // A number above 1 indicates money was made, while below 1 does the opposite. - var/budget_shift = money_at_end / money_at_start - - // Centcom will say different things based on if they gained or lost money. - var/message = null - switch(budget_shift) - if(0 to 0.02) // Abyssmal response. - message = "We are very interested in learning where [round(money_at_start, 1000)] thaler went in \ - just half an hour. We highly recommend rectifying this issue before the end of the shift, otherwise a \ - discussion regarding your future employment prospects will occur.

                    \ - Your facility's current balance of requisition tokens has been revoked." - SSsupply.points = 0 - log_debug("Funding Drive event ended with an abyssmal response, and the loss of all cargo points.") - - if(0.02 to 0.98) // Bad response. - message = "We're very disappointed that \the [location_name()] has ran a deficit since our request. \ - As such, we will be taking away some requisition tokens to cover the cost of operating your facility." - var/points_lost = round(SSsupply.points * rand(0.5, 0.8)) - SSsupply.points -= points_lost - log_debug("Funding Drive event ended with a bad response, and [points_lost] cargo points was taken away.") - - if(0.98 to 1.02) // Neutral response. - message = "It is unfortunate that \the [location_name()]'s finances remain at a standstill, however \ - that is still preferred over having a decicit. We hope that in the future, your facility will be able to be \ - more profitable." - log_debug("Funding Drive event ended with a neutral response.") - - if(1.02 to INFINITY) // Good response. - message = "We appreciate the efforts made by \the [location_name()] to run at a surplus. \ - Together, along with the other facilities present in the [using_map.starsys_name] system, \ - the company is expected to meet the quota.

                    \ - We will allocate additional requisition tokens for the cargo department as a reward." - - // If cargo is ever made to use station funds instead of cargo points, then a new kind of reward will be needed. - // Otherwise it would be weird for centcom to go 'thanks for not spending money, your reward is money to spend'. - var/point_reward = rand(100, 200) - SSsupply.points += point_reward - log_debug("Funding Drive event ended with a good response and a bonus of [point_reward] cargo points.") - - send_command_report("Budget Followup", message) - - - -// Returns the sum of the station account and all the departmental accounts. -/datum/event2/event/raise_funds/proc/count_money() - . = 0 - . += station_account.money - for(var/i = 1 to SSjob.department_datums.len) - var/datum/money_account/account = LAZYACCESS(department_accounts, SSjob.department_datums[i]) - if(istype(account)) - . += account.money - -/datum/event2/event/raise_funds/proc/send_command_report(title, message) - post_comm_message(title, message) - to_world(span("danger", "New [using_map.company_name] Update available at all communication consoles.")) - SEND_SOUND(world, 'sound/AI/commandreport.ogg') +/datum/event2/meta/raise_funds + name = "local funding drive" + enabled = FALSE // There isn't really any suitable way for the crew to generate thalers right now, if that gets fixed feel free to turn this event on. + departments = list(DEPARTMENT_COMMAND, DEPARTMENT_CARGO) + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/raise_funds + +/datum/event2/meta/raise_funds/get_weight() + var/command = metric.count_people_in_department(DEPARTMENT_COMMAND) + if(!command) // Need someone to read the centcom message. + return 0 + + var/cargo = metric.count_people_in_department(DEPARTMENT_CARGO) + return (command * 20) + (cargo * 20) + + + +/datum/event2/event/raise_funds + length_lower_bound = 30 MINUTES + length_upper_bound = 45 MINUTES + var/money_at_start = 0 + +/datum/event2/event/raise_funds/announce() + var/message = "Due to [pick("recent", "unfortunate", "possible future")] budget \ + [pick("changes", "issues")], in-system stations are now advised to increase funding income." + + send_command_report("Budget Advisement", message) + +/datum/event2/event/raise_funds/start() + // Note that the event remembers the amount of money when it started. If an issue develops where people try to scam centcom by + // taking out loads of money before the event, then depositing it back in after the event fires, feel free to make this check for + // roundstart money instead. + money_at_start = count_money() + log_debug("Funding Drive event logged a sum of [money_at_start] thalers in all station accounts at the start of the event.") + +/datum/event2/event/raise_funds/end() + var/money_at_end = count_money() + log_debug("Funding Drive event logged a sum of [money_at_end] thalers in all station accounts at the end of the event, compared \ + to [money_at_start] thalers. A difference of [money_at_end / money_at_start] was calculated.") + + // A number above 1 indicates money was made, while below 1 does the opposite. + var/budget_shift = money_at_end / money_at_start + + // Centcom will say different things based on if they gained or lost money. + var/message = null + switch(budget_shift) + if(0 to 0.02) // Abyssmal response. + message = "We are very interested in learning where [round(money_at_start, 1000)] thaler went in \ + just half an hour. We highly recommend rectifying this issue before the end of the shift, otherwise a \ + discussion regarding your future employment prospects will occur.

                    \ + Your facility's current balance of requisition tokens has been revoked." + SSsupply.points = 0 + log_debug("Funding Drive event ended with an abyssmal response, and the loss of all cargo points.") + + if(0.02 to 0.98) // Bad response. + message = "We're very disappointed that \the [location_name()] has ran a deficit since our request. \ + As such, we will be taking away some requisition tokens to cover the cost of operating your facility." + var/points_lost = round(SSsupply.points * rand(0.5, 0.8)) + SSsupply.points -= points_lost + log_debug("Funding Drive event ended with a bad response, and [points_lost] cargo points was taken away.") + + if(0.98 to 1.02) // Neutral response. + message = "It is unfortunate that \the [location_name()]'s finances remain at a standstill, however \ + that is still preferred over having a decicit. We hope that in the future, your facility will be able to be \ + more profitable." + log_debug("Funding Drive event ended with a neutral response.") + + if(1.02 to INFINITY) // Good response. + message = "We appreciate the efforts made by \the [location_name()] to run at a surplus. \ + Together, along with the other facilities present in the [using_map.starsys_name] system, \ + the company is expected to meet the quota.

                    \ + We will allocate additional requisition tokens for the cargo department as a reward." + + // If cargo is ever made to use station funds instead of cargo points, then a new kind of reward will be needed. + // Otherwise it would be weird for centcom to go 'thanks for not spending money, your reward is money to spend'. + var/point_reward = rand(100, 200) + SSsupply.points += point_reward + log_debug("Funding Drive event ended with a good response and a bonus of [point_reward] cargo points.") + + send_command_report("Budget Followup", message) + + + +// Returns the sum of the station account and all the departmental accounts. +/datum/event2/event/raise_funds/proc/count_money() + . = 0 + . += station_account.money + for(var/i = 1 to SSjob.department_datums.len) + var/datum/money_account/account = LAZYACCESS(department_accounts, SSjob.department_datums[i]) + if(istype(account)) + . += account.money + +/datum/event2/event/raise_funds/proc/send_command_report(title, message) + post_comm_message(title, message) + to_world(span("danger", "New [using_map.company_name] Update available at all communication consoles.")) + SEND_SOUND(world, 'sound/AI/commandreport.ogg') diff --git a/code/modules/gamemaster/event2/events/engineering/airlock_failure.dm b/code/modules/gamemaster/event2/events/engineering/airlock_failure.dm index c3cd5d0b243..3966d3c4785 100644 --- a/code/modules/gamemaster/event2/events/engineering/airlock_failure.dm +++ b/code/modules/gamemaster/event2/events/engineering/airlock_failure.dm @@ -1,105 +1,105 @@ -/datum/event2/meta/airlock_failure - event_class = "airlock failure" - departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_MEDICAL) - chaos = 15 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/airlock_failure - var/needs_medical = FALSE - -/datum/event2/meta/airlock_failure/emag - name = "airlock failure - emag" - event_type = /datum/event2/event/airlock_failure/emag - -/datum/event2/meta/airlock_failure/door_crush - name = "airlock failure - crushing" - event_type = /datum/event2/event/airlock_failure/door_crush - needs_medical = TRUE - -/datum/event2/meta/airlock_failure/shock - name = "airlock failure - shock" - chaos = 30 - event_type = /datum/event2/event/airlock_failure/shock - needs_medical = TRUE - - -/datum/event2/meta/airlock_failure/get_weight() - var/engineering = metric.count_people_in_department(DEPARTMENT_ENGINEERING) - - // Synths are good both for fixing the doors and getting blamed for the doors zapping people. - var/synths = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) - if(!engineering && !synths) // Nobody's around to fix the door. - return 0 - - // Medical might be needed for some of the more violent airlock failures. - var/medical = metric.count_people_in_department(DEPARTMENT_MEDICAL) - if(!medical && needs_medical) - return 0 - - return (engineering * 20) + (medical * 20) + (synths * 20) - - - -/datum/event2/event/airlock_failure - announce_delay_lower_bound = 20 SECONDS - announce_delay_upper_bound = 40 SECONDS - var/announce_odds = 0 - var/doors_to_break = 1 - var/list/affected_areas = list() - -/datum/event2/event/airlock_failure/emag - announce_odds = 10 // To make people wonder if the emagged door was from a baddie or from this event. - doors_to_break = 2 // Replacing emagged doors really sucks for engineering so don't overdo it. - -/datum/event2/event/airlock_failure/door_crush - announce_odds = 30 - doors_to_break = 5 - -/datum/event2/event/airlock_failure/shock - announce_odds = 70 - -/datum/event2/event/airlock_failure/start() - var/list/areas = find_random_areas() - if(!LAZYLEN(areas)) - log_debug("Airlock Failure event could not find any areas. Aborting.") - abort() - return - - while(areas.len) - var/area/area = pick(areas) - areas -= area - - for(var/obj/machinery/door/airlock/door in area.contents) - if(can_break_door(door)) - addtimer(CALLBACK(src, PROC_REF(break_door), door), 1) // Emagging proc is actually a blocking proc and that's bad for the ticker. - door.visible_message(span("danger", "\The [door]'s panel sparks!")) - playsound(door, "sparks", 50, 1) - log_debug("Airlock Failure event has broken \the [door] airlock in [area].") - affected_areas |= area - doors_to_break-- - - if(doors_to_break <= 0) - return - -/datum/event2/event/airlock_failure/announce() - if(prob(announce_odds)) - command_announcement.Announce("An electrical issue has been detected in the airlock grid at [english_list(affected_areas)]. \ - Some airlocks may require servicing by a qualified technician.", "Electrical Alert") - - -/datum/event2/event/airlock_failure/proc/can_break_door(obj/machinery/door/airlock/door) - if(istype(door, /obj/machinery/door/airlock/lift)) - return FALSE - return door.arePowerSystemsOn() - -// Override this for door busting. -/datum/event2/event/airlock_failure/proc/break_door(obj/machinery/door/airlock/door) - -/datum/event2/event/airlock_failure/emag/break_door(obj/machinery/door/airlock/door) - door.emag_act(1) - -/datum/event2/event/airlock_failure/door_crush/break_door(obj/machinery/door/airlock/door) - door.normalspeed = FALSE - door.safe = FALSE - -/datum/event2/event/airlock_failure/shock/break_door(obj/machinery/door/airlock/door) - door.electrify(-1) +/datum/event2/meta/airlock_failure + event_class = "airlock failure" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_MEDICAL) + chaos = 15 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/airlock_failure + var/needs_medical = FALSE + +/datum/event2/meta/airlock_failure/emag + name = "airlock failure - emag" + event_type = /datum/event2/event/airlock_failure/emag + +/datum/event2/meta/airlock_failure/door_crush + name = "airlock failure - crushing" + event_type = /datum/event2/event/airlock_failure/door_crush + needs_medical = TRUE + +/datum/event2/meta/airlock_failure/shock + name = "airlock failure - shock" + chaos = 30 + event_type = /datum/event2/event/airlock_failure/shock + needs_medical = TRUE + + +/datum/event2/meta/airlock_failure/get_weight() + var/engineering = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + + // Synths are good both for fixing the doors and getting blamed for the doors zapping people. + var/synths = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) + if(!engineering && !synths) // Nobody's around to fix the door. + return 0 + + // Medical might be needed for some of the more violent airlock failures. + var/medical = metric.count_people_in_department(DEPARTMENT_MEDICAL) + if(!medical && needs_medical) + return 0 + + return (engineering * 20) + (medical * 20) + (synths * 20) + + + +/datum/event2/event/airlock_failure + announce_delay_lower_bound = 20 SECONDS + announce_delay_upper_bound = 40 SECONDS + var/announce_odds = 0 + var/doors_to_break = 1 + var/list/affected_areas = list() + +/datum/event2/event/airlock_failure/emag + announce_odds = 10 // To make people wonder if the emagged door was from a baddie or from this event. + doors_to_break = 2 // Replacing emagged doors really sucks for engineering so don't overdo it. + +/datum/event2/event/airlock_failure/door_crush + announce_odds = 30 + doors_to_break = 5 + +/datum/event2/event/airlock_failure/shock + announce_odds = 70 + +/datum/event2/event/airlock_failure/start() + var/list/areas = find_random_areas() + if(!LAZYLEN(areas)) + log_debug("Airlock Failure event could not find any areas. Aborting.") + abort() + return + + while(areas.len) + var/area/area = pick(areas) + areas -= area + + for(var/obj/machinery/door/airlock/door in area.contents) + if(can_break_door(door)) + addtimer(CALLBACK(src, PROC_REF(break_door), door), 1) // Emagging proc is actually a blocking proc and that's bad for the ticker. + door.visible_message(span("danger", "\The [door]'s panel sparks!")) + playsound(door, "sparks", 50, 1) + log_debug("Airlock Failure event has broken \the [door] airlock in [area].") + affected_areas |= area + doors_to_break-- + + if(doors_to_break <= 0) + return + +/datum/event2/event/airlock_failure/announce() + if(prob(announce_odds)) + command_announcement.Announce("An electrical issue has been detected in the airlock grid at [english_list(affected_areas)]. \ + Some airlocks may require servicing by a qualified technician.", "Electrical Alert") + + +/datum/event2/event/airlock_failure/proc/can_break_door(obj/machinery/door/airlock/door) + if(istype(door, /obj/machinery/door/airlock/lift)) + return FALSE + return door.arePowerSystemsOn() + +// Override this for door busting. +/datum/event2/event/airlock_failure/proc/break_door(obj/machinery/door/airlock/door) + +/datum/event2/event/airlock_failure/emag/break_door(obj/machinery/door/airlock/door) + door.emag_act(1) + +/datum/event2/event/airlock_failure/door_crush/break_door(obj/machinery/door/airlock/door) + door.normalspeed = FALSE + door.safe = FALSE + +/datum/event2/event/airlock_failure/shock/break_door(obj/machinery/door/airlock/door) + door.electrify(-1) diff --git a/code/modules/gamemaster/event2/events/engineering/blob.dm b/code/modules/gamemaster/event2/events/engineering/blob.dm index 048c073f390..199204721ac 100644 --- a/code/modules/gamemaster/event2/events/engineering/blob.dm +++ b/code/modules/gamemaster/event2/events/engineering/blob.dm @@ -1,160 +1,160 @@ -/datum/event2/meta/blob - name = "blob" - departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_SECURITY, DEPARTMENT_MEDICAL) - chaos = 30 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT - event_class = "blob" // This makes it so there is no potential for multiple blob events of different types happening in the same round. - event_type = /datum/event2/event/blob - // In the distant future, if a mechanical skill system were to come into being, these vars could be replaced with skill checks so off duty people could count. - var/required_fighters = 2 // Fighters refers to engineering OR security. - var/required_support = 1 // Support refers to doctors AND roboticists, depending on fighter composition. - -/datum/event2/meta/blob/hard - name = "harder blob" - chaos = 40 - event_type = /datum/event2/event/blob/hard_blob - required_fighters = 3 - -/datum/event2/meta/blob/multi_blob - name = "multi blob" - chaos = 60 - event_type = /datum/event2/event/blob/multi_blob - required_fighters = 4 - required_support = 2 - -// For bussing only. -/datum/event2/meta/blob/omni_blob - name = "omni blob" - chaos = 200 - event_type = /datum/event2/event/blob/omni_blob - enabled = FALSE - -/datum/event2/meta/blob/get_weight() - // Count the 'fighters'. - var/list/engineers = metric.get_people_in_department(DEPARTMENT_ENGINEERING) - var/list/security = metric.get_people_in_department(DEPARTMENT_SECURITY) - - if(engineers.len + security.len < required_fighters) - return 0 - - // Now count the 'support'. - var/list/medical = metric.get_people_in_department(DEPARTMENT_MEDICAL) - var/need_medical = FALSE - - var/list/robotics = metric.get_people_with_job(/datum/job/roboticist) - var/need_robotics = FALSE - - // Determine what kind of support might be needed. - for(var/mob/living/L in engineers|security) - if(L.isSynthetic()) - need_robotics = TRUE - else - need_medical = TRUE - - // Medical is more important than robotics, since robits tend to not suffer slow deaths if there isn't a roboticist. - if(medical.len < required_support && need_medical) - return 0 - - // Engineers can sometimes fill in as robotics. This is done in the interest of the event having a chance of not being super rare. - // In the uncertain future, a mechanical skill system check could replace this check here. - if(robotics.len + engineers.len < required_support && need_robotics) - return 0 - - var/fighter_weight = (engineers.len + security.len) * 20 - var/support_weight = (medical.len + robotics.len) * 10 // Not counting engineers as support so they don't cause 30 weight each. - var/chaos_weight = chaos / 2 // Chaos is added as a weight in order to make more chaotic variants be preferred if they are allowed to be picked. - - return fighter_weight + support_weight + chaos_weight - - - -/datum/event2/event/blob - announce_delay_lower_bound = 1 MINUTE - announce_delay_upper_bound = 5 MINUTES - // This could be made into a GLOB accessible list for reuse if needed. - var/list/area/excluded = list( - /area/submap, - /area/shuttle, - /area/crew_quarters, - /area/holodeck, - /area/engineering/engine_room - ) - var/list/open_turfs = list() - var/spawn_blob_type = /obj/structure/blob/core/random_medium - var/number_of_blobs = 1 - var/list/blobs = list() // A list containing weakrefs to blob cores created. Weakrefs mean this event won't interfere with qdel. - -/datum/event2/event/blob/hard_blob - spawn_blob_type = /obj/structure/blob/core/random_hard - -/datum/event2/event/blob/multi_blob - spawn_blob_type = /obj/structure/blob/core/random_hard // Lethargic blobs are boring. - number_of_blobs = 2 - -// For adminbus only. -/datum/event2/event/blob/omni_blob - number_of_blobs = 16 // Someday maybe we can get this to specifically spawn every blob. - -/datum/event2/event/blob/set_up() - open_turfs = find_random_turfs(5 + number_of_blobs) - - if(!open_turfs.len) - log_debug("Blob infestation event: Giving up after failure to find blob spots.") - abort() - -/datum/event2/event/blob/start() - for(var/i = 1 to number_of_blobs) - var/turf/T = pick(open_turfs) - var/obj/structure/blob/core/new_blob = new spawn_blob_type(T) - blobs += WEAKREF(new_blob) - open_turfs -= T // So we can't put two cores on the same tile if doing multiblob. - log_debug("Spawned [new_blob.overmind.blob_type.name] blob at [get_area(new_blob)].") - -/datum/event2/event/blob/should_end() - for(var/datum/weakref/weakref as anything in blobs) - if(weakref.resolve()) // If the weakref is resolvable, that means the blob hasn't been deleted yet. - return FALSE - return TRUE // Only end if all blobs die. - -// Normally this does nothing, but is useful if aborted by an admin. -/datum/event2/event/blob/end() - for(var/datum/weakref/weakref as anything in blobs) - var/obj/structure/blob/core/B = weakref.resolve() - if(istype(B)) - qdel(B) - -/datum/event2/event/blob/announce() - if(!ended) // Don't announce if the blobs die early. - var/danger_level = 0 - var/list/blob_type_names = list() - var/multiblob = FALSE - for(var/datum/weakref/weakref as anything in blobs) - var/obj/structure/blob/core/B = weakref.resolve() - if(!istype(B)) - continue - var/datum/blob_type/blob_type = B.overmind.blob_type - - blob_type_names += blob_type.name - if(danger_level > blob_type.difficulty) // The highest difficulty is used, if multiple blobs are present. - danger_level = blob_type.difficulty - - if(blob_type_names.len > 1) // More than one blob is harder. - danger_level += blob_type_names.len - multiblob = TRUE - - var/list/lines = list() - lines += "Confirmed outbreak of level [7 + danger_level] biohazard[multiblob ? "s": ""] \ - aboard [location_name()]. All personnel must contain the outbreak." - - if(danger_level >= BLOB_DIFFICULTY_MEDIUM) // Tell them what kind of blob it is if it's tough. - lines += "The biohazard[multiblob ? "s have": " has"] been identified as [english_list(blob_type_names)]." - - if(danger_level >= BLOB_DIFFICULTY_HARD) // If it's really hard then tell them where it is so the response occurs faster. - var/turf/T = open_turfs[1] - var/area/A = T.loc - lines += "[multiblob ? "It is": "They are"] suspected to have originated from \the [A]." - - if(danger_level >= BLOB_DIFFICULTY_SUPERHARD) - lines += "Extreme caution is advised." - - command_announcement.Announce(lines.Join("\n"), "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg') +/datum/event2/meta/blob + name = "blob" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_SECURITY, DEPARTMENT_MEDICAL) + chaos = 30 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT + event_class = "blob" // This makes it so there is no potential for multiple blob events of different types happening in the same round. + event_type = /datum/event2/event/blob + // In the distant future, if a mechanical skill system were to come into being, these vars could be replaced with skill checks so off duty people could count. + var/required_fighters = 2 // Fighters refers to engineering OR security. + var/required_support = 1 // Support refers to doctors AND roboticists, depending on fighter composition. + +/datum/event2/meta/blob/hard + name = "harder blob" + chaos = 40 + event_type = /datum/event2/event/blob/hard_blob + required_fighters = 3 + +/datum/event2/meta/blob/multi_blob + name = "multi blob" + chaos = 60 + event_type = /datum/event2/event/blob/multi_blob + required_fighters = 4 + required_support = 2 + +// For bussing only. +/datum/event2/meta/blob/omni_blob + name = "omni blob" + chaos = 200 + event_type = /datum/event2/event/blob/omni_blob + enabled = FALSE + +/datum/event2/meta/blob/get_weight() + // Count the 'fighters'. + var/list/engineers = metric.get_people_in_department(DEPARTMENT_ENGINEERING) + var/list/security = metric.get_people_in_department(DEPARTMENT_SECURITY) + + if(engineers.len + security.len < required_fighters) + return 0 + + // Now count the 'support'. + var/list/medical = metric.get_people_in_department(DEPARTMENT_MEDICAL) + var/need_medical = FALSE + + var/list/robotics = metric.get_people_with_job(/datum/job/roboticist) + var/need_robotics = FALSE + + // Determine what kind of support might be needed. + for(var/mob/living/L in engineers|security) + if(L.isSynthetic()) + need_robotics = TRUE + else + need_medical = TRUE + + // Medical is more important than robotics, since robits tend to not suffer slow deaths if there isn't a roboticist. + if(medical.len < required_support && need_medical) + return 0 + + // Engineers can sometimes fill in as robotics. This is done in the interest of the event having a chance of not being super rare. + // In the uncertain future, a mechanical skill system check could replace this check here. + if(robotics.len + engineers.len < required_support && need_robotics) + return 0 + + var/fighter_weight = (engineers.len + security.len) * 20 + var/support_weight = (medical.len + robotics.len) * 10 // Not counting engineers as support so they don't cause 30 weight each. + var/chaos_weight = chaos / 2 // Chaos is added as a weight in order to make more chaotic variants be preferred if they are allowed to be picked. + + return fighter_weight + support_weight + chaos_weight + + + +/datum/event2/event/blob + announce_delay_lower_bound = 1 MINUTE + announce_delay_upper_bound = 5 MINUTES + // This could be made into a GLOB accessible list for reuse if needed. + var/list/area/excluded = list( + /area/submap, + /area/shuttle, + /area/crew_quarters, + /area/holodeck, + /area/engineering/engine_room + ) + var/list/open_turfs = list() + var/spawn_blob_type = /obj/structure/blob/core/random_medium + var/number_of_blobs = 1 + var/list/blobs = list() // A list containing weakrefs to blob cores created. Weakrefs mean this event won't interfere with qdel. + +/datum/event2/event/blob/hard_blob + spawn_blob_type = /obj/structure/blob/core/random_hard + +/datum/event2/event/blob/multi_blob + spawn_blob_type = /obj/structure/blob/core/random_hard // Lethargic blobs are boring. + number_of_blobs = 2 + +// For adminbus only. +/datum/event2/event/blob/omni_blob + number_of_blobs = 16 // Someday maybe we can get this to specifically spawn every blob. + +/datum/event2/event/blob/set_up() + open_turfs = find_random_turfs(5 + number_of_blobs) + + if(!open_turfs.len) + log_debug("Blob infestation event: Giving up after failure to find blob spots.") + abort() + +/datum/event2/event/blob/start() + for(var/i = 1 to number_of_blobs) + var/turf/T = pick(open_turfs) + var/obj/structure/blob/core/new_blob = new spawn_blob_type(T) + blobs += WEAKREF(new_blob) + open_turfs -= T // So we can't put two cores on the same tile if doing multiblob. + log_debug("Spawned [new_blob.overmind.blob_type.name] blob at [get_area(new_blob)].") + +/datum/event2/event/blob/should_end() + for(var/datum/weakref/weakref as anything in blobs) + if(weakref.resolve()) // If the weakref is resolvable, that means the blob hasn't been deleted yet. + return FALSE + return TRUE // Only end if all blobs die. + +// Normally this does nothing, but is useful if aborted by an admin. +/datum/event2/event/blob/end() + for(var/datum/weakref/weakref as anything in blobs) + var/obj/structure/blob/core/B = weakref.resolve() + if(istype(B)) + qdel(B) + +/datum/event2/event/blob/announce() + if(!ended) // Don't announce if the blobs die early. + var/danger_level = 0 + var/list/blob_type_names = list() + var/multiblob = FALSE + for(var/datum/weakref/weakref as anything in blobs) + var/obj/structure/blob/core/B = weakref.resolve() + if(!istype(B)) + continue + var/datum/blob_type/blob_type = B.overmind.blob_type + + blob_type_names += blob_type.name + if(danger_level > blob_type.difficulty) // The highest difficulty is used, if multiple blobs are present. + danger_level = blob_type.difficulty + + if(blob_type_names.len > 1) // More than one blob is harder. + danger_level += blob_type_names.len + multiblob = TRUE + + var/list/lines = list() + lines += "Confirmed outbreak of level [7 + danger_level] biohazard[multiblob ? "s": ""] \ + aboard [location_name()]. All personnel must contain the outbreak." + + if(danger_level >= BLOB_DIFFICULTY_MEDIUM) // Tell them what kind of blob it is if it's tough. + lines += "The biohazard[multiblob ? "s have": " has"] been identified as [english_list(blob_type_names)]." + + if(danger_level >= BLOB_DIFFICULTY_HARD) // If it's really hard then tell them where it is so the response occurs faster. + var/turf/T = open_turfs[1] + var/area/A = T.loc + lines += "[multiblob ? "It is": "They are"] suspected to have originated from \the [A]." + + if(danger_level >= BLOB_DIFFICULTY_SUPERHARD) + lines += "Extreme caution is advised." + + command_announcement.Announce(lines.Join("\n"), "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg') diff --git a/code/modules/gamemaster/event2/events/engineering/brand_intelligence.dm b/code/modules/gamemaster/event2/events/engineering/brand_intelligence.dm index d3478bb333b..b4c58fa0ac8 100644 --- a/code/modules/gamemaster/event2/events/engineering/brand_intelligence.dm +++ b/code/modules/gamemaster/event2/events/engineering/brand_intelligence.dm @@ -1,90 +1,90 @@ -/datum/event2/meta/brand_intelligence - name = "vending machine malware" - departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_EVERYONE) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - event_type = /datum/event2/event/brand_intelligence - -/datum/event2/meta/brand_intelligence/get_weight() - return 10 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) - - - -/datum/event2/event/brand_intelligence - var/malware_spread_cooldown = 30 SECONDS - - var/list/vending_machines = list() // List of venders that can potentially be infected. - var/list/infected_vending_machines = list() // List of venders that have been infected. - var/obj/machinery/vending/vender_zero = null // The first vending machine infected. If that one gets fixed, all other infected machines will be cured. - var/last_malware_spread_time = null - -/datum/event2/event/brand_intelligence/set_up() - for(var/obj/machinery/vending/V in machines) - if(!(V.z in using_map.station_levels)) - continue - vending_machines += V - - if(!vending_machines.len) - log_debug("Could not find any vending machines on station Z levels. Aborting.") - abort() - return - - vender_zero = pick(vending_machines) - -/datum/event2/event/brand_intelligence/announce() - if(prob(90)) - command_announcement.Announce("An ongoing mass upload of malware for vendors has been detected onboard \the [location_name()], \ - which appears to transmit to nearby vendors. The original infected machine is believed to be \a [vender_zero].", "Vendor Service Alert") - -/datum/event2/event/brand_intelligence/start() - infect_vender(vender_zero) - -/datum/event2/event/brand_intelligence/event_tick() - if(last_malware_spread_time + malware_spread_cooldown > world.time) - return // Still on cooldown. - last_malware_spread_time = world.time - - if(vending_machines.len) - var/next_victim = pick(vending_machines) - infect_vender(next_victim) - - // Every time Vender Zero infects, it says something. - vender_zero.speak(pick("Try our aggressive new marketing strategies!", \ - "You should buy products to feed your lifestyle obsession!", \ - "Consume!", \ - "Your money can buy happiness!", \ - "Engage direct marketing!", \ - "Advertising is legalized lying! But don't let that put you off our great deals!", \ - "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.")) - - -/datum/event2/event/brand_intelligence/should_end() - if(!vending_machines.len) - return TRUE - if(!can_propagate(vender_zero)) - return TRUE - return FALSE - -/datum/event2/event/brand_intelligence/end() - if(can_propagate(vender_zero)) // The crew failed and all the machines are infected! - return - // Otherwise Vender Zero was taken out in some form. - if(vender_zero) - vender_zero.visible_message(span("notice", "\The [vender_zero]'s network activity light flickers wildly \ - for a few seconds as a small screen reads: 'Rolling out firmware reset to networked machines'.")) - for(var/obj/machinery/vending/vender in infected_vending_machines) - cure_vender(vender) - -/datum/event2/event/brand_intelligence/proc/infect_vender(obj/machinery/vending/V) - vending_machines -= V - infected_vending_machines += V - V.shut_up = FALSE - V.shoot_inventory = TRUE - -/datum/event2/event/brand_intelligence/proc/cure_vender(obj/machinery/vending/V) - infected_vending_machines -= V - V.shut_up = TRUE - V.shoot_inventory = FALSE - -/datum/event2/event/brand_intelligence/proc/can_propagate(obj/machinery/vending/V) - return V && V.shut_up == FALSE +/datum/event2/meta/brand_intelligence + name = "vending machine malware" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_EVERYONE) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/brand_intelligence + +/datum/event2/meta/brand_intelligence/get_weight() + return 10 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) + + + +/datum/event2/event/brand_intelligence + var/malware_spread_cooldown = 30 SECONDS + + var/list/vending_machines = list() // List of venders that can potentially be infected. + var/list/infected_vending_machines = list() // List of venders that have been infected. + var/obj/machinery/vending/vender_zero = null // The first vending machine infected. If that one gets fixed, all other infected machines will be cured. + var/last_malware_spread_time = null + +/datum/event2/event/brand_intelligence/set_up() + for(var/obj/machinery/vending/V in machines) + if(!(V.z in using_map.station_levels)) + continue + vending_machines += V + + if(!vending_machines.len) + log_debug("Could not find any vending machines on station Z levels. Aborting.") + abort() + return + + vender_zero = pick(vending_machines) + +/datum/event2/event/brand_intelligence/announce() + if(prob(90)) + command_announcement.Announce("An ongoing mass upload of malware for vendors has been detected onboard \the [location_name()], \ + which appears to transmit to nearby vendors. The original infected machine is believed to be \a [vender_zero].", "Vendor Service Alert") + +/datum/event2/event/brand_intelligence/start() + infect_vender(vender_zero) + +/datum/event2/event/brand_intelligence/event_tick() + if(last_malware_spread_time + malware_spread_cooldown > world.time) + return // Still on cooldown. + last_malware_spread_time = world.time + + if(vending_machines.len) + var/next_victim = pick(vending_machines) + infect_vender(next_victim) + + // Every time Vender Zero infects, it says something. + vender_zero.speak(pick("Try our aggressive new marketing strategies!", \ + "You should buy products to feed your lifestyle obsession!", \ + "Consume!", \ + "Your money can buy happiness!", \ + "Engage direct marketing!", \ + "Advertising is legalized lying! But don't let that put you off our great deals!", \ + "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.")) + + +/datum/event2/event/brand_intelligence/should_end() + if(!vending_machines.len) + return TRUE + if(!can_propagate(vender_zero)) + return TRUE + return FALSE + +/datum/event2/event/brand_intelligence/end() + if(can_propagate(vender_zero)) // The crew failed and all the machines are infected! + return + // Otherwise Vender Zero was taken out in some form. + if(vender_zero) + vender_zero.visible_message(span("notice", "\The [vender_zero]'s network activity light flickers wildly \ + for a few seconds as a small screen reads: 'Rolling out firmware reset to networked machines'.")) + for(var/obj/machinery/vending/vender in infected_vending_machines) + cure_vender(vender) + +/datum/event2/event/brand_intelligence/proc/infect_vender(obj/machinery/vending/V) + vending_machines -= V + infected_vending_machines += V + V.shut_up = FALSE + V.shoot_inventory = TRUE + +/datum/event2/event/brand_intelligence/proc/cure_vender(obj/machinery/vending/V) + infected_vending_machines -= V + V.shut_up = TRUE + V.shoot_inventory = FALSE + +/datum/event2/event/brand_intelligence/proc/can_propagate(obj/machinery/vending/V) + return V && V.shut_up == FALSE diff --git a/code/modules/gamemaster/event2/events/engineering/camera_damage.dm b/code/modules/gamemaster/event2/events/engineering/camera_damage.dm index c6c15161302..bdaedad73ba 100644 --- a/code/modules/gamemaster/event2/events/engineering/camera_damage.dm +++ b/code/modules/gamemaster/event2/events/engineering/camera_damage.dm @@ -1,41 +1,41 @@ -/datum/event2/meta/camera_damage - name = "random camera damage" - departments = list(DEPARTMENT_SYNTHETIC, DEPARTMENT_ENGINEERING) - chaos = 5 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - reusable = TRUE - event_type = /datum/event2/event/camera_damage - -/datum/event2/meta/camera_damage/get_weight() - return 30 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) + (metric.count_people_in_department(DEPARTMENT_SYNTHETIC) * 40) - -/datum/event2/event/camera_damage - var/camera_range = 7 - -/datum/event2/event/camera_damage/start() - var/obj/machinery/camera/C = acquire_random_camera() - if(!C) - return - - for(var/obj/machinery/camera/cam in range(camera_range, C)) - if(is_valid_camera(cam)) - cam.wires.cut(WIRE_MAIN_POWER1) - if(prob(25)) - cam.wires.cut(WIRE_CAM_ALARM) - -/datum/event2/event/camera_damage/proc/acquire_random_camera(var/remaining_attempts = 5) - if(!cameranet.cameras.len) - return - if(!remaining_attempts) - return - - var/obj/machinery/camera/C = pick(cameranet.cameras) - if(is_valid_camera(C)) - return C - // It is very important to use --var and not var-- for recursive calls, as var-- will cause an infinite loop. - return acquire_random_camera(--remaining_attempts) - -/datum/event2/event/camera_damage/proc/is_valid_camera(var/obj/machinery/camera/C) - // Only return a functional camera, not installed in a silicon/hardsuit/circuit/etc, and that exists somewhere players have access - var/turf/T = get_turf(C) +/datum/event2/meta/camera_damage + name = "random camera damage" + departments = list(DEPARTMENT_SYNTHETIC, DEPARTMENT_ENGINEERING) + chaos = 5 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/camera_damage + +/datum/event2/meta/camera_damage/get_weight() + return 30 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) + (metric.count_people_in_department(DEPARTMENT_SYNTHETIC) * 40) + +/datum/event2/event/camera_damage + var/camera_range = 7 + +/datum/event2/event/camera_damage/start() + var/obj/machinery/camera/C = acquire_random_camera() + if(!C) + return + + for(var/obj/machinery/camera/cam in range(camera_range, C)) + if(is_valid_camera(cam)) + cam.wires.cut(WIRE_MAIN_POWER1) + if(prob(25)) + cam.wires.cut(WIRE_CAM_ALARM) + +/datum/event2/event/camera_damage/proc/acquire_random_camera(var/remaining_attempts = 5) + if(!cameranet.cameras.len) + return + if(!remaining_attempts) + return + + var/obj/machinery/camera/C = pick(cameranet.cameras) + if(is_valid_camera(C)) + return C + // It is very important to use --var and not var-- for recursive calls, as var-- will cause an infinite loop. + return acquire_random_camera(--remaining_attempts) + +/datum/event2/event/camera_damage/proc/is_valid_camera(var/obj/machinery/camera/C) + // Only return a functional camera, not installed in a silicon/hardsuit/circuit/etc, and that exists somewhere players have access + var/turf/T = get_turf(C) return T && C?.can_use() && istype(C.loc, /turf) && (T.z in using_map.player_levels) \ No newline at end of file diff --git a/code/modules/gamemaster/event2/events/engineering/canister_leak.dm b/code/modules/gamemaster/event2/events/engineering/canister_leak.dm index a48823151c6..db8eedf25c2 100644 --- a/code/modules/gamemaster/event2/events/engineering/canister_leak.dm +++ b/code/modules/gamemaster/event2/events/engineering/canister_leak.dm @@ -1,26 +1,26 @@ -// -// This event chooses a random canister on player levels and breaks it, releasing its contents! -// - -/datum/event2/meta/canister_leak - name = "canister leak" - departments = list(DEPARTMENT_ENGINEERING) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - reusable = TRUE - event_type = /datum/event2/event/canister_leak - -/datum/event2/meta/canister_leak/get_weight() - return metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 30 - -/datum/event2/event/canister_leak/start() - // List of all non-destroyed canisters on station levels - var/list/all_canisters = list() - for(var/obj/machinery/portable_atmospherics/canister/C in machines) - if(!C.destroyed && (C.z in using_map.station_levels) && C.air_contents.total_moles >= MOLES_CELLSTANDARD) - all_canisters += C - var/obj/machinery/portable_atmospherics/canister/C = pick(all_canisters) - log_debug("canister_leak event: Canister [C] ([C.x],[C.y],[C.z]) destroyed.") - C.health = 0 - C.healthcheck() - +// +// This event chooses a random canister on player levels and breaks it, releasing its contents! +// + +/datum/event2/meta/canister_leak + name = "canister leak" + departments = list(DEPARTMENT_ENGINEERING) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/canister_leak + +/datum/event2/meta/canister_leak/get_weight() + return metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 30 + +/datum/event2/event/canister_leak/start() + // List of all non-destroyed canisters on station levels + var/list/all_canisters = list() + for(var/obj/machinery/portable_atmospherics/canister/C in machines) + if(!C.destroyed && (C.z in using_map.station_levels) && C.air_contents.total_moles >= MOLES_CELLSTANDARD) + all_canisters += C + var/obj/machinery/portable_atmospherics/canister/C = pick(all_canisters) + log_debug("canister_leak event: Canister [C] ([C.x],[C.y],[C.z]) destroyed.") + C.health = 0 + C.healthcheck() + diff --git a/code/modules/gamemaster/event2/events/engineering/dust.dm b/code/modules/gamemaster/event2/events/engineering/dust.dm index 1a26118fbc6..66bb04970df 100644 --- a/code/modules/gamemaster/event2/events/engineering/dust.dm +++ b/code/modules/gamemaster/event2/events/engineering/dust.dm @@ -1,19 +1,19 @@ -/datum/event2/meta/dust - name = "dust" - departments = list(DEPARTMENT_ENGINEERING) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - reusable = TRUE - event_type = /datum/event2/event/dust - -/datum/event2/meta/dust/get_weight() - return metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20 - - - -/datum/event2/event/dust/announce() - if(prob(33)) - command_announcement.Announce("Dust has been detected on a collision course with \the [location_name()].") - -/datum/event2/event/dust/start() - dust_swarm("norm") +/datum/event2/meta/dust + name = "dust" + departments = list(DEPARTMENT_ENGINEERING) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/dust + +/datum/event2/meta/dust/get_weight() + return metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20 + + + +/datum/event2/event/dust/announce() + if(prob(33)) + command_announcement.Announce("Dust has been detected on a collision course with \the [location_name()].") + +/datum/event2/event/dust/start() + dust_swarm("norm") diff --git a/code/modules/gamemaster/event2/events/engineering/gas_leak.dm b/code/modules/gamemaster/event2/events/engineering/gas_leak.dm index b0c52d6e072..b7becd5637f 100644 --- a/code/modules/gamemaster/event2/events/engineering/gas_leak.dm +++ b/code/modules/gamemaster/event2/events/engineering/gas_leak.dm @@ -1,47 +1,47 @@ -/datum/event2/meta/gas_leak - name = "gas leak" - departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_SYNTHETIC) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - reusable = TRUE - event_type = /datum/event2/event/gas_leak - -/datum/event2/meta/gas_leak/get_weight() - // Synthetics are counted in higher value because they can wirelessly connect to alarms. - var/engineering_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10 - var/synthetic_factor = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) * 30 - return (15 + engineering_factor + synthetic_factor) / (times_ran + 1) - - - -/datum/event2/event/gas_leak - var/potential_gas_choices = list("carbon_dioxide", "nitrous_oxide", "phoron", "volatile_fuel") - var/chosen_gas = null - var/turf/chosen_turf = null - -/datum/event2/event/gas_leak/set_up() - chosen_gas = pick(potential_gas_choices) - - var/list/turfs = find_random_turfs() - if(!turfs.len) - log_debug("Gas Leak event failed to find any available turfs to leak into. Aborting.") - abort() - return - chosen_turf = pick(turfs) - -/datum/event2/event/gas_leak/announce() - if(chosen_turf) - command_announcement.Announce("Warning, hazardous [lowertext(gas_data.name[chosen_gas])] gas leak detected in \the [chosen_turf.loc], evacuate the area.", "Hazard Alert") - -/datum/event2/event/gas_leak/start() - // Okay, time to actually put the gas in the room! - // TODO - Would be nice to break a waste pipe perhaps? - // TODO - Maybe having it released from a single point and thus causing airflow to blow stuff around - - // Fow now just add a bunch of it to the air - - var/datum/gas_mixture/air_contents = new - air_contents.temperature = T20C + rand(-50, 50) - air_contents.gas[chosen_gas] = 10 * MOLES_CELLSTANDARD - chosen_turf.assume_air(air_contents) +/datum/event2/meta/gas_leak + name = "gas leak" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_SYNTHETIC) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/gas_leak + +/datum/event2/meta/gas_leak/get_weight() + // Synthetics are counted in higher value because they can wirelessly connect to alarms. + var/engineering_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10 + var/synthetic_factor = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) * 30 + return (15 + engineering_factor + synthetic_factor) / (times_ran + 1) + + + +/datum/event2/event/gas_leak + var/potential_gas_choices = list("carbon_dioxide", "nitrous_oxide", "phoron", "volatile_fuel") + var/chosen_gas = null + var/turf/chosen_turf = null + +/datum/event2/event/gas_leak/set_up() + chosen_gas = pick(potential_gas_choices) + + var/list/turfs = find_random_turfs() + if(!turfs.len) + log_debug("Gas Leak event failed to find any available turfs to leak into. Aborting.") + abort() + return + chosen_turf = pick(turfs) + +/datum/event2/event/gas_leak/announce() + if(chosen_turf) + command_announcement.Announce("Warning, hazardous [lowertext(gas_data.name[chosen_gas])] gas leak detected in \the [chosen_turf.loc], evacuate the area.", "Hazard Alert") + +/datum/event2/event/gas_leak/start() + // Okay, time to actually put the gas in the room! + // TODO - Would be nice to break a waste pipe perhaps? + // TODO - Maybe having it released from a single point and thus causing airflow to blow stuff around + + // Fow now just add a bunch of it to the air + + var/datum/gas_mixture/air_contents = new + air_contents.temperature = T20C + rand(-50, 50) + air_contents.gas[chosen_gas] = 10 * MOLES_CELLSTANDARD + chosen_turf.assume_air(air_contents) playsound(chosen_turf, 'sound/effects/smoke.ogg', 75, 1) \ No newline at end of file diff --git a/code/modules/gamemaster/event2/events/engineering/grid_check.dm b/code/modules/gamemaster/event2/events/engineering/grid_check.dm index b4ae7976b6e..8c120fb62b0 100644 --- a/code/modules/gamemaster/event2/events/engineering/grid_check.dm +++ b/code/modules/gamemaster/event2/events/engineering/grid_check.dm @@ -1,50 +1,50 @@ -// New grid check event: -// Very similar to the old one, power goes out in most of the station, however the new feature is the ability for engineering to -// get power back on sooner, if they are able to reach a special machine and initiate a manual reboot. If no one is able to do so, -// it will reboot itself after a few minutes, just like the old one. Bad things happen if there is no grid checker machine protecting -// the powernet when this event fires. - -/datum/event2/meta/grid_check - name = "grid check" - departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_EVERYONE) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - reusable = TRUE - event_type = /datum/event2/event/grid_check - -// Having the turbines be way over their rated limit makes grid checks more likely. -/datum/event2/meta/grid_check/proc/get_overpower() - var/highest_overpower = 0 - for(var/obj/machinery/power/generator/turbine as anything in GLOB.all_turbines) - var/overpower = max((turbine.effective_gen / turbine.max_power) - 1, 0) - if(overpower > highest_overpower) - highest_overpower = overpower - return highest_overpower - -/datum/event2/meta/grid_check/get_weight() - var/population_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10 - var/overpower_factor = 50 * get_overpower() // Will be 0 if not overloaded at all, and 50 if turbines are outputting twice as much as rated. - return (20 + population_factor + overpower_factor) / (times_ran + 1) - - - -/datum/event2/event/grid_check - var/obj/machinery/power/generator/engine // The turbine that will send a power spike. - -/datum/event2/event/grid_check/set_up() - // Find the turbine being pushed the most. - var/obj/machinery/power/generator/most_stressed_turbine = null - for(var/obj/machinery/power/generator/turbine as anything in GLOB.all_turbines) - if(!most_stressed_turbine) - most_stressed_turbine = turbine - else if(turbine.effective_gen > most_stressed_turbine.effective_gen) - most_stressed_turbine = turbine - engine = most_stressed_turbine - -/datum/event2/event/grid_check/start() - // This sets off a chain of events that lead to the actual grid check (or perhaps worse). - // First, the Supermatter engine makes a power spike. - if(engine) - engine.power_spike() - // After that, the engine checks if a grid checker exists on the same powernet, and if so, it triggers a blackout. - // If not, lots of stuff breaks. See code/modules/power/generator.dm for that piece of code. +// New grid check event: +// Very similar to the old one, power goes out in most of the station, however the new feature is the ability for engineering to +// get power back on sooner, if they are able to reach a special machine and initiate a manual reboot. If no one is able to do so, +// it will reboot itself after a few minutes, just like the old one. Bad things happen if there is no grid checker machine protecting +// the powernet when this event fires. + +/datum/event2/meta/grid_check + name = "grid check" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_EVERYONE) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/grid_check + +// Having the turbines be way over their rated limit makes grid checks more likely. +/datum/event2/meta/grid_check/proc/get_overpower() + var/highest_overpower = 0 + for(var/obj/machinery/power/generator/turbine as anything in GLOB.all_turbines) + var/overpower = max((turbine.effective_gen / turbine.max_power) - 1, 0) + if(overpower > highest_overpower) + highest_overpower = overpower + return highest_overpower + +/datum/event2/meta/grid_check/get_weight() + var/population_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10 + var/overpower_factor = 50 * get_overpower() // Will be 0 if not overloaded at all, and 50 if turbines are outputting twice as much as rated. + return (20 + population_factor + overpower_factor) / (times_ran + 1) + + + +/datum/event2/event/grid_check + var/obj/machinery/power/generator/engine // The turbine that will send a power spike. + +/datum/event2/event/grid_check/set_up() + // Find the turbine being pushed the most. + var/obj/machinery/power/generator/most_stressed_turbine = null + for(var/obj/machinery/power/generator/turbine as anything in GLOB.all_turbines) + if(!most_stressed_turbine) + most_stressed_turbine = turbine + else if(turbine.effective_gen > most_stressed_turbine.effective_gen) + most_stressed_turbine = turbine + engine = most_stressed_turbine + +/datum/event2/event/grid_check/start() + // This sets off a chain of events that lead to the actual grid check (or perhaps worse). + // First, the Supermatter engine makes a power spike. + if(engine) + engine.power_spike() + // After that, the engine checks if a grid checker exists on the same powernet, and if so, it triggers a blackout. + // If not, lots of stuff breaks. See code/modules/power/generator.dm for that piece of code. diff --git a/code/modules/gamemaster/event2/events/engineering/meteor_defense.dm b/code/modules/gamemaster/event2/events/engineering/meteor_defense.dm index 378c6b42fea..f0b11b7e1ea 100644 --- a/code/modules/gamemaster/event2/events/engineering/meteor_defense.dm +++ b/code/modules/gamemaster/event2/events/engineering/meteor_defense.dm @@ -1,83 +1,83 @@ -// This event gives the station an advance warning about meteors, so that they can prepare in various ways. - -/datum/event2/meta/meteor_defense - name = "meteor defense" - departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_CARGO) - chaos = 50 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT - event_class = "meteor defense" - event_type = /datum/event2/event/meteor_defense - -/datum/event2/meta/meteor_defense/get_weight() - // Engineers count as 20. - var/engineers = metric.count_people_in_department(DEPARTMENT_ENGINEERING) - if(engineers < 3) // There -must- be at least three engineers for this to be possible. - return 0 - - . = engineers * 20 - - // Cargo and AI/borgs count as 10. - var/cargo = metric.count_people_with_job(/datum/job/cargo_tech) + metric.count_people_with_job(/datum/job/qm) - var/bots = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) - - . += (cargo + bots) * 10 - - -/datum/event2/event/meteor_defense - start_delay_lower_bound = 10 MINUTES - start_delay_upper_bound = 15 MINUTES - var/soon_announced = FALSE - var/direction = null // Actual dir used for which side the meteors come from. - var/dir_text = null // Direction shown in the announcement. - var/list/meteor_types = null - var/waves = null // How many times to send meteors. - var/last_wave_time = null // world.time of latest wave. - var/wave_delay = 10 SECONDS - var/wave_upper_bound = 8 // Max amount of meteors per wave. - var/wave_lower_bound = 4 // Min amount. - -/datum/event2/event/meteor_defense/proc/set_meteor_types() - meteor_types = meteors_threatening.Copy() - -/datum/event2/event/meteor_defense/set_up() - direction = pick(cardinal) // alldirs doesn't work with current meteor code unfortunately. - waves = rand(3, 6) - switch(direction) - if(NORTH) - dir_text = "aft" // For some reason this is needed. - if(SOUTH) - dir_text = "fore" - if(EAST) - dir_text = "port" - if(WEST) - dir_text = "starboard" - set_meteor_types() - -/datum/event2/event/meteor_defense/announce() - var/announcement = "Meteors are expected to approach from the [dir_text] side, in approximately [DisplayTimeText(time_to_start - world.time, 60)]." - command_announcement.Announce(announcement, "Meteor Alert", new_sound = 'sound/AI/meteors.ogg') - -/datum/event2/event/meteor_defense/wait_tick() - if(!soon_announced) - if((time_to_start - world.time) <= 5 MINUTES) - soon_announced = TRUE - var/announcement = "The incoming meteors are expected to approach from the [dir_text] side. \ - ETA to arrival is approximately [DisplayTimeText(time_to_start - world.time, 60)]." - command_announcement.Announce(announcement, "Meteor Alert - Update") - -/datum/event2/event/meteor_defense/start() - command_announcement.Announce("Incoming meteors approach from \the [dir_text] side!", "Meteor Alert - Update") - -/datum/event2/event/meteor_defense/event_tick() - if(world.time > last_wave_time + wave_delay) - last_wave_time = world.time - waves-- - message_admins("[waves] more wave\s of meteors remain.") - // Dir is reversed because the direction describes where meteors are going, not what side it's gonna hit. - spawn_meteors(rand(wave_upper_bound, wave_lower_bound), meteor_types, reverse_dir[direction]) - -/datum/event2/event/meteor_defense/should_end() - return waves <= 0 - -/datum/event2/event/meteor_defense/end() - command_announcement.Announce("\The [location_name()] will clear the incoming meteors in a moment.", "Meteor Alert - Update") +// This event gives the station an advance warning about meteors, so that they can prepare in various ways. + +/datum/event2/meta/meteor_defense + name = "meteor defense" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_CARGO) + chaos = 50 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT + event_class = "meteor defense" + event_type = /datum/event2/event/meteor_defense + +/datum/event2/meta/meteor_defense/get_weight() + // Engineers count as 20. + var/engineers = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + if(engineers < 3) // There -must- be at least three engineers for this to be possible. + return 0 + + . = engineers * 20 + + // Cargo and AI/borgs count as 10. + var/cargo = metric.count_people_with_job(/datum/job/cargo_tech) + metric.count_people_with_job(/datum/job/qm) + var/bots = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) + + . += (cargo + bots) * 10 + + +/datum/event2/event/meteor_defense + start_delay_lower_bound = 10 MINUTES + start_delay_upper_bound = 15 MINUTES + var/soon_announced = FALSE + var/direction = null // Actual dir used for which side the meteors come from. + var/dir_text = null // Direction shown in the announcement. + var/list/meteor_types = null + var/waves = null // How many times to send meteors. + var/last_wave_time = null // world.time of latest wave. + var/wave_delay = 10 SECONDS + var/wave_upper_bound = 8 // Max amount of meteors per wave. + var/wave_lower_bound = 4 // Min amount. + +/datum/event2/event/meteor_defense/proc/set_meteor_types() + meteor_types = meteors_threatening.Copy() + +/datum/event2/event/meteor_defense/set_up() + direction = pick(cardinal) // alldirs doesn't work with current meteor code unfortunately. + waves = rand(3, 6) + switch(direction) + if(NORTH) + dir_text = "aft" // For some reason this is needed. + if(SOUTH) + dir_text = "fore" + if(EAST) + dir_text = "port" + if(WEST) + dir_text = "starboard" + set_meteor_types() + +/datum/event2/event/meteor_defense/announce() + var/announcement = "Meteors are expected to approach from the [dir_text] side, in approximately [DisplayTimeText(time_to_start - world.time, 60)]." + command_announcement.Announce(announcement, "Meteor Alert", new_sound = 'sound/AI/meteors.ogg') + +/datum/event2/event/meteor_defense/wait_tick() + if(!soon_announced) + if((time_to_start - world.time) <= 5 MINUTES) + soon_announced = TRUE + var/announcement = "The incoming meteors are expected to approach from the [dir_text] side. \ + ETA to arrival is approximately [DisplayTimeText(time_to_start - world.time, 60)]." + command_announcement.Announce(announcement, "Meteor Alert - Update") + +/datum/event2/event/meteor_defense/start() + command_announcement.Announce("Incoming meteors approach from \the [dir_text] side!", "Meteor Alert - Update") + +/datum/event2/event/meteor_defense/event_tick() + if(world.time > last_wave_time + wave_delay) + last_wave_time = world.time + waves-- + message_admins("[waves] more wave\s of meteors remain.") + // Dir is reversed because the direction describes where meteors are going, not what side it's gonna hit. + spawn_meteors(rand(wave_upper_bound, wave_lower_bound), meteor_types, reverse_dir[direction]) + +/datum/event2/event/meteor_defense/should_end() + return waves <= 0 + +/datum/event2/event/meteor_defense/end() + command_announcement.Announce("\The [location_name()] will clear the incoming meteors in a moment.", "Meteor Alert - Update") diff --git a/code/modules/gamemaster/event2/events/engineering/spacevine.dm b/code/modules/gamemaster/event2/events/engineering/spacevine.dm index 6620e67a0c7..53fc9fd5bb0 100644 --- a/code/modules/gamemaster/event2/events/engineering/spacevine.dm +++ b/code/modules/gamemaster/event2/events/engineering/spacevine.dm @@ -1,17 +1,17 @@ -/datum/event2/meta/spacevine - name = "space-vine infestation" - departments = list(DEPARTMENT_ENGINEERING) - chaos = 10 // There's a really rare chance of vines getting something awful like phoron atmosphere but thats not really controllable. - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/spacevine - -/datum/event2/meta/spacevine/get_weight() - return 20 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10) + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5) - - - -/datum/event2/event/spacevine/announce() - level_seven_announcement() - -/datum/event2/event/spacevine/start() - spacevine_infestation() +/datum/event2/meta/spacevine + name = "space-vine infestation" + departments = list(DEPARTMENT_ENGINEERING) + chaos = 10 // There's a really rare chance of vines getting something awful like phoron atmosphere but thats not really controllable. + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/spacevine + +/datum/event2/meta/spacevine/get_weight() + return 20 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10) + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5) + + + +/datum/event2/event/spacevine/announce() + level_seven_announcement() + +/datum/event2/event/spacevine/start() + spacevine_infestation() diff --git a/code/modules/gamemaster/event2/events/engineering/wallrot.dm b/code/modules/gamemaster/event2/events/engineering/wallrot.dm index 29d12c85247..9542c12d38f 100644 --- a/code/modules/gamemaster/event2/events/engineering/wallrot.dm +++ b/code/modules/gamemaster/event2/events/engineering/wallrot.dm @@ -1,46 +1,46 @@ -/datum/event2/meta/wallrot - name = "wall-rot" - departments = list(DEPARTMENT_ENGINEERING) - reusable = TRUE - event_type = /datum/event2/event/wallrot - -/datum/event2/meta/wallrot/get_weight() - return (10 + metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10) / (times_ran + 1) - - - - -/datum/event2/event/wallrot - var/turf/simulated/wall/origin = null - -/datum/event2/event/wallrot/set_up() - for(var/i = 1 to 100) - var/turf/candidate = locate(rand(1, world.maxx), rand(1, world.maxy), pick(get_location_z_levels()) ) - if(istype(candidate, /turf/simulated/wall)) - origin = candidate - log_debug("Wall-rot event has chosen \the [origin] ([origin.loc]) as the origin for the wallrot infestation.") - return - - log_debug("Wall-rot event failed to find a valid wall after one hundred tries. Aborting.") - abort() - -/datum/event2/event/wallrot/announce() - if(origin && prob(80)) - command_announcement.Announce("Harmful fungi detected on \the [location_name()], near \the [origin.loc]. \ - Station structural integrity may be compromised.", "Biohazard Alert") - -/datum/event2/event/wallrot/start() - if(origin) - origin.rot() - - var/rot_count = 0 - var/target_rot = rand(5, 20) - for(var/turf/simulated/wall/W in range(7, origin)) - if(prob(50)) - if(W.rot()) - rot_count++ - if(rot_count >= target_rot) - break - - - +/datum/event2/meta/wallrot + name = "wall-rot" + departments = list(DEPARTMENT_ENGINEERING) + reusable = TRUE + event_type = /datum/event2/event/wallrot + +/datum/event2/meta/wallrot/get_weight() + return (10 + metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10) / (times_ran + 1) + + + + +/datum/event2/event/wallrot + var/turf/simulated/wall/origin = null + +/datum/event2/event/wallrot/set_up() + for(var/i = 1 to 100) + var/turf/candidate = locate(rand(1, world.maxx), rand(1, world.maxy), pick(get_location_z_levels()) ) + if(istype(candidate, /turf/simulated/wall)) + origin = candidate + log_debug("Wall-rot event has chosen \the [origin] ([origin.loc]) as the origin for the wallrot infestation.") + return + + log_debug("Wall-rot event failed to find a valid wall after one hundred tries. Aborting.") + abort() + +/datum/event2/event/wallrot/announce() + if(origin && prob(80)) + command_announcement.Announce("Harmful fungi detected on \the [location_name()], near \the [origin.loc]. \ + Station structural integrity may be compromised.", "Biohazard Alert") + +/datum/event2/event/wallrot/start() + if(origin) + origin.rot() + + var/rot_count = 0 + var/target_rot = rand(5, 20) + for(var/turf/simulated/wall/W in range(7, origin)) + if(prob(50)) + if(W.rot()) + rot_count++ + if(rot_count >= target_rot) + break + + + diff --git a/code/modules/gamemaster/event2/events/engineering/window_break.dm b/code/modules/gamemaster/event2/events/engineering/window_break.dm index 7aea410d45d..6b9d153a1eb 100644 --- a/code/modules/gamemaster/event2/events/engineering/window_break.dm +++ b/code/modules/gamemaster/event2/events/engineering/window_break.dm @@ -1,148 +1,148 @@ -// This event causes a random window near space to become damaged. -// If that window is not fixed in a certain amount of time, -// that window and nearby windows will shatter, causing a breach. - -/datum/event2/meta/window_break - name = "window break" - departments = list(DEPARTMENT_ENGINEERING) - chaos = 10 - reusable = TRUE - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/window_break - -/datum/event2/meta/window_break/get_weight() - return (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) / (times_ran + 1) - - - -/datum/event2/event/window_break - announce_delay_lower_bound = 10 SECONDS - announce_delay_upper_bound = 20 SECONDS - length_lower_bound = 8 MINUTES - length_upper_bound = 12 MINUTES - var/turf/chosen_turf_with_windows = null - var/obj/structure/window/chosen_window = null - var/list/collateral_windows = list() - -/datum/event2/event/window_break/set_up() - var/list/areas = find_random_areas() - if(!LAZYLEN(areas)) - log_debug("Window Break event could not find any areas. Aborting.") - abort() - return - - while(areas.len) - var/area/area = pick(areas) - areas -= area - - for(var/obj/structure/window/W in area.contents) - if(!is_window_to_space(W)) - continue - chosen_turf_with_windows = get_turf(W) - collateral_windows = gather_collateral_windows(W) - break // Break out of the inner loop. - - if(chosen_turf_with_windows) - log_debug("Window Break event has chosen turf '[chosen_turf_with_windows.name]' in [chosen_turf_with_windows.loc].") - break // Then the outer loop. - - if(!chosen_turf_with_windows) - log_debug("Window Break event could not find a turf with valid windows to break. Aborting.") - abort() - return - -/datum/event2/event/window_break/announce() - if(chosen_window) - command_announcement.Announce("Structural integrity of space-facing windows at \the [get_area(chosen_turf_with_windows)] are failing. \ - Repair of the damaged window is advised. Personnel without EVA suits in the area should leave until repairs are complete.", "Structural Alert") - -/datum/event2/event/window_break/start() - if(!chosen_turf_with_windows) - return - - for(var/obj/structure/window/W in chosen_turf_with_windows.contents) - if(W.is_fulltile()) // Full tile windows are simple and can always be used. - chosen_window = W - break - else // Otherwise we only want the window that is on the inside side of the station. - var/turf/T = get_step(W, W.dir) - if(T.is_space()) - continue - if(T.check_density()) - continue - chosen_window = W - break - - if(!chosen_window) - return - - chosen_window.take_damage(chosen_window.maxhealth * 0.8) - playsound(chosen_window, 'sound/effects/Glasshit.ogg', 100, 1) - chosen_window.visible_message(span("danger", "\The [chosen_window] suddenly begins to crack!")) - -/datum/event2/event/window_break/should_end() - . = ..() - if(!.) // If the timer didn't expire, we can still end it early if someone messes up. - if(!chosen_window || !chosen_window.anchored || chosen_window.health == chosen_window.maxhealth) - // If the window got deconstructed/moved/etc, immediately end and make the breach happen. - // Also end early if it was repaired. - return TRUE - -/datum/event2/event/window_break/end() - // If someone fixed the window, then everything is fine. - if(chosen_window && chosen_window.anchored && chosen_window.health == chosen_window.maxhealth) - log_debug("Window Break event ended with window repaired.") - return - - // Otherwise a bunch of windows shatter. - chosen_window?.shatter() - - var/windows_to_shatter = min(rand(4, 10), collateral_windows.len) - for(var/i = 1 to windows_to_shatter) - var/obj/structure/window/W = collateral_windows[i] - W?.shatter() - - log_debug("Window Break event ended with [windows_to_shatter] shattered windows and a breach.") - -// Checks if a window is adjacent to a space tile, and also that the opposite direction is open. -// This is done to avoid getting caught in corner parts of windows. -/datum/event2/event/window_break/proc/is_window_to_space(obj/structure/window/W) - for(var/direction in GLOB.cardinal) - var/turf/T = get_step(W, direction) - if(T.is_space()) - var/turf/opposite_T = get_step(W, GLOB.reverse_dir[direction]) - if(!opposite_T.check_density()) - return TRUE - return FALSE - -//TL;DR: breadth first search for all connected turfs with windows -/datum/event2/event/window_break/proc/gather_collateral_windows(var/obj/structure/window/target_window) - var/list/turf/frontier_set = list(target_window.loc) - var/list/obj/structure/window/result_set = list() - var/list/turf/explored_set = list() - - while(frontier_set.len > 0) - var/turf/current = frontier_set[1] - frontier_set -= current - explored_set += current - - var/contains_windows = 0 - for(var/obj/structure/window/to_add in current.contents) - contains_windows = 1 - result_set += to_add - - if(contains_windows) - //add adjacent turfs to be checked for windows as well - var/turf/neighbor = locate(current.x + 1, current.y, current.z) - if(!(neighbor in frontier_set) && !(neighbor in explored_set)) - frontier_set += neighbor - neighbor = locate(current.x - 1, current.y, current.z) - if(!(neighbor in frontier_set) && !(neighbor in explored_set)) - frontier_set += neighbor - neighbor = locate(current.x, current.y + 1, current.z) - if(!(neighbor in frontier_set) && !(neighbor in explored_set)) - frontier_set += neighbor - neighbor = locate(current.x, current.y - 1, current.z) - if(!(neighbor in frontier_set) && !(neighbor in explored_set)) - frontier_set += neighbor - return result_set +// This event causes a random window near space to become damaged. +// If that window is not fixed in a certain amount of time, +// that window and nearby windows will shatter, causing a breach. + +/datum/event2/meta/window_break + name = "window break" + departments = list(DEPARTMENT_ENGINEERING) + chaos = 10 + reusable = TRUE + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/window_break + +/datum/event2/meta/window_break/get_weight() + return (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) / (times_ran + 1) + + + +/datum/event2/event/window_break + announce_delay_lower_bound = 10 SECONDS + announce_delay_upper_bound = 20 SECONDS + length_lower_bound = 8 MINUTES + length_upper_bound = 12 MINUTES + var/turf/chosen_turf_with_windows = null + var/obj/structure/window/chosen_window = null + var/list/collateral_windows = list() + +/datum/event2/event/window_break/set_up() + var/list/areas = find_random_areas() + if(!LAZYLEN(areas)) + log_debug("Window Break event could not find any areas. Aborting.") + abort() + return + + while(areas.len) + var/area/area = pick(areas) + areas -= area + + for(var/obj/structure/window/W in area.contents) + if(!is_window_to_space(W)) + continue + chosen_turf_with_windows = get_turf(W) + collateral_windows = gather_collateral_windows(W) + break // Break out of the inner loop. + + if(chosen_turf_with_windows) + log_debug("Window Break event has chosen turf '[chosen_turf_with_windows.name]' in [chosen_turf_with_windows.loc].") + break // Then the outer loop. + + if(!chosen_turf_with_windows) + log_debug("Window Break event could not find a turf with valid windows to break. Aborting.") + abort() + return + +/datum/event2/event/window_break/announce() + if(chosen_window) + command_announcement.Announce("Structural integrity of space-facing windows at \the [get_area(chosen_turf_with_windows)] are failing. \ + Repair of the damaged window is advised. Personnel without EVA suits in the area should leave until repairs are complete.", "Structural Alert") + +/datum/event2/event/window_break/start() + if(!chosen_turf_with_windows) + return + + for(var/obj/structure/window/W in chosen_turf_with_windows.contents) + if(W.is_fulltile()) // Full tile windows are simple and can always be used. + chosen_window = W + break + else // Otherwise we only want the window that is on the inside side of the station. + var/turf/T = get_step(W, W.dir) + if(T.is_space()) + continue + if(T.check_density()) + continue + chosen_window = W + break + + if(!chosen_window) + return + + chosen_window.take_damage(chosen_window.maxhealth * 0.8) + playsound(chosen_window, 'sound/effects/Glasshit.ogg', 100, 1) + chosen_window.visible_message(span("danger", "\The [chosen_window] suddenly begins to crack!")) + +/datum/event2/event/window_break/should_end() + . = ..() + if(!.) // If the timer didn't expire, we can still end it early if someone messes up. + if(!chosen_window || !chosen_window.anchored || chosen_window.health == chosen_window.maxhealth) + // If the window got deconstructed/moved/etc, immediately end and make the breach happen. + // Also end early if it was repaired. + return TRUE + +/datum/event2/event/window_break/end() + // If someone fixed the window, then everything is fine. + if(chosen_window && chosen_window.anchored && chosen_window.health == chosen_window.maxhealth) + log_debug("Window Break event ended with window repaired.") + return + + // Otherwise a bunch of windows shatter. + chosen_window?.shatter() + + var/windows_to_shatter = min(rand(4, 10), collateral_windows.len) + for(var/i = 1 to windows_to_shatter) + var/obj/structure/window/W = collateral_windows[i] + W?.shatter() + + log_debug("Window Break event ended with [windows_to_shatter] shattered windows and a breach.") + +// Checks if a window is adjacent to a space tile, and also that the opposite direction is open. +// This is done to avoid getting caught in corner parts of windows. +/datum/event2/event/window_break/proc/is_window_to_space(obj/structure/window/W) + for(var/direction in GLOB.cardinal) + var/turf/T = get_step(W, direction) + if(T.is_space()) + var/turf/opposite_T = get_step(W, GLOB.reverse_dir[direction]) + if(!opposite_T.check_density()) + return TRUE + return FALSE + +//TL;DR: breadth first search for all connected turfs with windows +/datum/event2/event/window_break/proc/gather_collateral_windows(var/obj/structure/window/target_window) + var/list/turf/frontier_set = list(target_window.loc) + var/list/obj/structure/window/result_set = list() + var/list/turf/explored_set = list() + + while(frontier_set.len > 0) + var/turf/current = frontier_set[1] + frontier_set -= current + explored_set += current + + var/contains_windows = 0 + for(var/obj/structure/window/to_add in current.contents) + contains_windows = 1 + result_set += to_add + + if(contains_windows) + //add adjacent turfs to be checked for windows as well + var/turf/neighbor = locate(current.x + 1, current.y, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + neighbor = locate(current.x - 1, current.y, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + neighbor = locate(current.x, current.y + 1, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + neighbor = locate(current.x, current.y - 1, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + return result_set diff --git a/code/modules/gamemaster/event2/events/everyone/comms_blackout.dm b/code/modules/gamemaster/event2/events/everyone/comms_blackout.dm index 6da19c32ccd..d3d60d48761 100644 --- a/code/modules/gamemaster/event2/events/everyone/comms_blackout.dm +++ b/code/modules/gamemaster/event2/events/everyone/comms_blackout.dm @@ -1,43 +1,43 @@ -/datum/event2/meta/comms_blackout - name = "communications blackout" - departments = list(DEPARTMENT_EVERYONE) // It's not an engineering event because engineering can't do anything to help . . . for now. - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - reusable = TRUE - event_type = /datum/event2/event/comms_blackout - -/datum/event2/meta/comms_blackout/get_weight() - return 50 + metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5 - - - -/datum/event2/event/comms_blackout/announce() - var/alert = pick("Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you*%fj00)`5vc-BZZT", \ - "Ionospheric anomalies detected. Temporary telecommunication failu*3mga;b4;'1v¬-BZZZT", \ - "Ionospheric anomalies detected. Temporary telec#MCi46:5.;@63-BZZZZT", \ - "Ionospheric anomalies dete'fZ\\kg5_0-BZZZZZT", \ - "Ionospheri:%£ MCayj^j<.3-BZZZZZZT", \ - "#4nd%;f4y6,>£%-BZZZZZZZT") - if(prob(33)) - command_announcement.Announce(alert, new_sound = 'sound/misc/interference.ogg') - // AIs will always know if there's a comm blackout, rogue AIs could then lie about comm blackouts in the future while they shutdown comms - for(var/mob/living/silicon/ai/A in player_list) - to_chat(A, "
                    ") - to_chat(A, "[alert]") - to_chat(A, "
                    ") - -/datum/event2/event/comms_blackout/start() - if(prob(50)) - // One in two chance for the radios to turn i%t# t&_)#%, which can be more alarming than radio silence. - log_debug("Doing partial outage of telecomms.") - for(var/obj/machinery/telecomms/processor/P in telecomms_list) - P.emp_act(1) - else - // Otherwise just shut everything down, madagascar style. - log_debug("Doing complete outage of telecomms.") - for(var/obj/machinery/telecomms/T in telecomms_list) - T.emp_act(1) - - // Communicators go down no matter what. - for(var/obj/machinery/exonet_node/N in machines) - N.emp_act(1) +/datum/event2/meta/comms_blackout + name = "communications blackout" + departments = list(DEPARTMENT_EVERYONE) // It's not an engineering event because engineering can't do anything to help . . . for now. + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + reusable = TRUE + event_type = /datum/event2/event/comms_blackout + +/datum/event2/meta/comms_blackout/get_weight() + return 50 + metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5 + + + +/datum/event2/event/comms_blackout/announce() + var/alert = pick("Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you*%fj00)`5vc-BZZT", \ + "Ionospheric anomalies detected. Temporary telecommunication failu*3mga;b4;'1v¬-BZZZT", \ + "Ionospheric anomalies detected. Temporary telec#MCi46:5.;@63-BZZZZT", \ + "Ionospheric anomalies dete'fZ\\kg5_0-BZZZZZT", \ + "Ionospheri:%£ MCayj^j<.3-BZZZZZZT", \ + "#4nd%;f4y6,>£%-BZZZZZZZT") + if(prob(33)) + command_announcement.Announce(alert, new_sound = 'sound/misc/interference.ogg') + // AIs will always know if there's a comm blackout, rogue AIs could then lie about comm blackouts in the future while they shutdown comms + for(var/mob/living/silicon/ai/A in player_list) + to_chat(A, "
                    ") + to_chat(A, "[alert]") + to_chat(A, "
                    ") + +/datum/event2/event/comms_blackout/start() + if(prob(50)) + // One in two chance for the radios to turn i%t# t&_)#%, which can be more alarming than radio silence. + log_debug("Doing partial outage of telecomms.") + for(var/obj/machinery/telecomms/processor/P in telecomms_list) + P.emp_act(1) + else + // Otherwise just shut everything down, madagascar style. + log_debug("Doing complete outage of telecomms.") + for(var/obj/machinery/telecomms/T in telecomms_list) + T.emp_act(1) + + // Communicators go down no matter what. + for(var/obj/machinery/exonet_node/N in machines) + N.emp_act(1) diff --git a/code/modules/gamemaster/event2/events/everyone/electrical_fault.dm b/code/modules/gamemaster/event2/events/everyone/electrical_fault.dm index 0611e1b290e..1392c0bb2ba 100644 --- a/code/modules/gamemaster/event2/events/everyone/electrical_fault.dm +++ b/code/modules/gamemaster/event2/events/everyone/electrical_fault.dm @@ -1,99 +1,99 @@ -// Makes a spooky electrical thing happen, that can blow the lights or make the APCs turn off for a short period of time. -// Doesn't do any permanent damage beyond the small chance to emag an APC, which just unlocks it forever. As such, this is free to occur even with no engineers. -// Since this is an 'external' thing, the Grid Checker can't stop it. - -/datum/event2/meta/electrical_fault - name = "electrical fault" - departments = list(DEPARTMENT_EVERYONE) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - event_type = /datum/event2/event/electrical_fault - -/datum/event2/meta/electrical_fault/get_weight() - return 10 + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5) - - -/datum/event2/event/electrical_fault - start_delay_lower_bound = 30 SECONDS - start_delay_upper_bound = 1 MINUTE - length_lower_bound = 20 SECONDS - length_upper_bound = 40 SECONDS - var/max_apcs_per_tick = 6 - - var/list/valid_apcs = null - var/list/valid_z_levels = null - - var/apcs_disabled = 0 - var/apcs_overloaded = 0 - var/apcs_emagged = 0 - -/datum/event2/event/electrical_fault/announce() - // Trying to be vague to avoid 'space lightning storms'. - // This could be re-flavored to be a solar flare or something and have robots outside be sad. - command_announcement.Announce("External conditions near \the [location_name()] are likely \ - to cause voltage spikes and other electrical issues very soon. Please secure sensitive electrical equipment until the situation passes.", "[location_name()] Sensor Array") - -/datum/event2/event/electrical_fault/set_up() - valid_z_levels = get_location_z_levels() - valid_z_levels -= using_map.sealed_levels // Space levels only please! - - valid_apcs = list() - for(var/obj/machinery/power/apc/A in GLOB.apcs) - if(A.z in valid_z_levels) - valid_apcs += A - -/datum/event2/event/electrical_fault/start() - command_announcement.Announce("Irregularities detected in \the [location_name()] power grid.", "[location_name()] Power Grid Monitoring") - -/datum/event2/event/electrical_fault/event_tick() - if(!valid_apcs.len) - log_debug("ELECTRICAL EVENT: No valid APCs found for electrical fault event. Aborting.") - abort() - return - - var/list/picked_apcs = list() - for(var/i = 1 to max_apcs_per_tick) - picked_apcs |= pick(valid_apcs) - - for(var/A in picked_apcs) - affect_apc(A) - -/datum/event2/event/electrical_fault/end() - command_announcement.Announce("The irregular electrical conditions inside \the [location_name()] power grid has ceased.", "[location_name()] Power Grid Monitoring") - log_debug("Electrical Fault event caused [apcs_disabled] APC\s to shut off, \ - [apcs_overloaded] APC\s to overload lighting, and [apcs_emagged] APC\s to be emagged.") - -/datum/event2/event/electrical_fault/proc/affect_apc(obj/machinery/power/apc/A) - // Main breaker is turned off or is Special(tm). Consider it protected. - // Important APCs like the AI or the engine core shouldn't get shut off by this event. - if((!A.operating || A.failure_timer > 0) || A.is_critical) - return - - // In reality this would probably make the lights get brighter but oh well. - for(var/obj/machinery/light/L in get_area(A)) - L.flicker(rand(10, 20)) - - // Chance to make the APC turn off for awhile. - // This will actually protect it from further damage. - if(prob(25)) - A.energy_fail(rand(60, 120)) -// log_debug("ELECTRICAL EVENT: Disabled \the [A]'s power for a temporary amount of time.") - playsound(A, 'sound/machines/defib_success.ogg', 50, 1) - apcs_disabled++ - return - - // Decent chance to overload lighting circuit. - if(prob(30)) - A.overload_lighting() -// log_debug("ELECTRICAL EVENT: Overloaded \the [A]'s lighting.") - playsound(A, 'sound/effects/lightningshock.ogg', 50, 1) - apcs_overloaded++ - - // Relatively small chance to emag the apc as apc_damage event does. - if(prob(5)) - A.emagged = TRUE - A.update_icon() -// log_debug("ELECTRICAL EVENT: Emagged \the [A].") - playsound(A, 'sound/machines/chime.ogg', 50, 1) - apcs_emagged++ - +// Makes a spooky electrical thing happen, that can blow the lights or make the APCs turn off for a short period of time. +// Doesn't do any permanent damage beyond the small chance to emag an APC, which just unlocks it forever. As such, this is free to occur even with no engineers. +// Since this is an 'external' thing, the Grid Checker can't stop it. + +/datum/event2/meta/electrical_fault + name = "electrical fault" + departments = list(DEPARTMENT_EVERYONE) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/electrical_fault + +/datum/event2/meta/electrical_fault/get_weight() + return 10 + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5) + + +/datum/event2/event/electrical_fault + start_delay_lower_bound = 30 SECONDS + start_delay_upper_bound = 1 MINUTE + length_lower_bound = 20 SECONDS + length_upper_bound = 40 SECONDS + var/max_apcs_per_tick = 6 + + var/list/valid_apcs = null + var/list/valid_z_levels = null + + var/apcs_disabled = 0 + var/apcs_overloaded = 0 + var/apcs_emagged = 0 + +/datum/event2/event/electrical_fault/announce() + // Trying to be vague to avoid 'space lightning storms'. + // This could be re-flavored to be a solar flare or something and have robots outside be sad. + command_announcement.Announce("External conditions near \the [location_name()] are likely \ + to cause voltage spikes and other electrical issues very soon. Please secure sensitive electrical equipment until the situation passes.", "[location_name()] Sensor Array") + +/datum/event2/event/electrical_fault/set_up() + valid_z_levels = get_location_z_levels() + valid_z_levels -= using_map.sealed_levels // Space levels only please! + + valid_apcs = list() + for(var/obj/machinery/power/apc/A in GLOB.apcs) + if(A.z in valid_z_levels) + valid_apcs += A + +/datum/event2/event/electrical_fault/start() + command_announcement.Announce("Irregularities detected in \the [location_name()] power grid.", "[location_name()] Power Grid Monitoring") + +/datum/event2/event/electrical_fault/event_tick() + if(!valid_apcs.len) + log_debug("ELECTRICAL EVENT: No valid APCs found for electrical fault event. Aborting.") + abort() + return + + var/list/picked_apcs = list() + for(var/i = 1 to max_apcs_per_tick) + picked_apcs |= pick(valid_apcs) + + for(var/A in picked_apcs) + affect_apc(A) + +/datum/event2/event/electrical_fault/end() + command_announcement.Announce("The irregular electrical conditions inside \the [location_name()] power grid has ceased.", "[location_name()] Power Grid Monitoring") + log_debug("Electrical Fault event caused [apcs_disabled] APC\s to shut off, \ + [apcs_overloaded] APC\s to overload lighting, and [apcs_emagged] APC\s to be emagged.") + +/datum/event2/event/electrical_fault/proc/affect_apc(obj/machinery/power/apc/A) + // Main breaker is turned off or is Special(tm). Consider it protected. + // Important APCs like the AI or the engine core shouldn't get shut off by this event. + if((!A.operating || A.failure_timer > 0) || A.is_critical) + return + + // In reality this would probably make the lights get brighter but oh well. + for(var/obj/machinery/light/L in get_area(A)) + L.flicker(rand(10, 20)) + + // Chance to make the APC turn off for awhile. + // This will actually protect it from further damage. + if(prob(25)) + A.energy_fail(rand(60, 120)) +// log_debug("ELECTRICAL EVENT: Disabled \the [A]'s power for a temporary amount of time.") + playsound(A, 'sound/machines/defib_success.ogg', 50, 1) + apcs_disabled++ + return + + // Decent chance to overload lighting circuit. + if(prob(30)) + A.overload_lighting() +// log_debug("ELECTRICAL EVENT: Overloaded \the [A]'s lighting.") + playsound(A, 'sound/effects/lightningshock.ogg', 50, 1) + apcs_overloaded++ + + // Relatively small chance to emag the apc as apc_damage event does. + if(prob(5)) + A.emagged = TRUE + A.update_icon() +// log_debug("ELECTRICAL EVENT: Emagged \the [A].") + playsound(A, 'sound/machines/chime.ogg', 50, 1) + apcs_emagged++ + diff --git a/code/modules/gamemaster/event2/events/everyone/gravity.dm b/code/modules/gamemaster/event2/events/everyone/gravity.dm index 5426698a22c..7c423aeeb2a 100644 --- a/code/modules/gamemaster/event2/events/everyone/gravity.dm +++ b/code/modules/gamemaster/event2/events/everyone/gravity.dm @@ -1,34 +1,34 @@ -/datum/event2/meta/gravity - name = "gravity failure" - departments = list(DEPARTMENT_EVERYONE) - chaos = 20 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - reusable = TRUE - event_type = /datum/event2/event/gravity - -/datum/event2/meta/gravity/get_weight() - return (20 + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5)) / (times_ran + 1) - - - - -/datum/event2/event/gravity - length_lower_bound = 4 MINUTES - length_upper_bound = 8 MINUTES - -/datum/event2/event/gravity/announce() - command_announcement.Announce("Feedback surge detected in mass-distributions systems. \ - Artificial gravity has been disabled whilst the system reinitializes. \ - Please stand by while the gravity system reinitializes.", "Gravity Failure") - -/datum/event2/event/gravity/start() - for(var/area/A in world) - if(A.z in get_location_z_levels(space_only = TRUE)) - A.gravitychange(FALSE) - -/datum/event2/event/gravity/end() - for(var/area/A in world) - if(A.z in get_location_z_levels(space_only = TRUE)) - A.gravitychange(TRUE) - +/datum/event2/meta/gravity + name = "gravity failure" + departments = list(DEPARTMENT_EVERYONE) + chaos = 20 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/gravity + +/datum/event2/meta/gravity/get_weight() + return (20 + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5)) / (times_ran + 1) + + + + +/datum/event2/event/gravity + length_lower_bound = 4 MINUTES + length_upper_bound = 8 MINUTES + +/datum/event2/event/gravity/announce() + command_announcement.Announce("Feedback surge detected in mass-distributions systems. \ + Artificial gravity has been disabled whilst the system reinitializes. \ + Please stand by while the gravity system reinitializes.", "Gravity Failure") + +/datum/event2/event/gravity/start() + for(var/area/A in world) + if(A.z in get_location_z_levels(space_only = TRUE)) + A.gravitychange(FALSE) + +/datum/event2/event/gravity/end() + for(var/area/A in world) + if(A.z in get_location_z_levels(space_only = TRUE)) + A.gravitychange(TRUE) + command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.", "Gravity Restored") \ No newline at end of file diff --git a/code/modules/gamemaster/event2/events/everyone/infestation.dm b/code/modules/gamemaster/event2/events/everyone/infestation.dm index fa07e60b4c2..a8da58c11ba 100644 --- a/code/modules/gamemaster/event2/events/everyone/infestation.dm +++ b/code/modules/gamemaster/event2/events/everyone/infestation.dm @@ -1,77 +1,77 @@ -/datum/event2/meta/infestation - event_class = "infestation" - departments = list(DEPARTMENT_EVERYONE) - -/datum/event2/meta/infestation/get_weight() - return metric.count_people_in_department(DEPARTMENT_EVERYONE) * 10 - -/datum/event2/meta/infestation/rodents - name = "infestation - rodents" - event_type = /datum/event2/event/infestation/rodents - -/datum/event2/meta/infestation/lizards - name = "infestation - lizards" - event_type = /datum/event2/event/infestation/lizards - -/datum/event2/meta/infestation/spiderlings - name = "infestation - spiders" - event_type = /datum/event2/event/infestation/spiderlings - -/datum/event2/event/infestation/cockroaches - vermin_string = "cockroaches" - max_vermin = 6 - things_to_spawn = list(/mob/living/simple_mob/animal/passive/cockroach) - -/datum/event2/event/infestation - var/vermin_string = null - var/max_vermin = 0 - var/list/things_to_spawn = list() - - var/list/turfs = list() - -/datum/event2/event/infestation/rodents - vermin_string = "rodents" - max_vermin = 12 - things_to_spawn = list( - /mob/living/simple_mob/animal/passive/mouse/gray, - /mob/living/simple_mob/animal/passive/mouse/brown, - /mob/living/simple_mob/animal/passive/mouse/black, - /mob/living/simple_mob/animal/passive/mouse/white, - /mob/living/simple_mob/animal/passive/mouse/rat - ) - -/datum/event2/event/infestation/lizards - vermin_string = "lizards" - max_vermin = 6 - things_to_spawn = list( - /mob/living/simple_mob/animal/passive/lizard, - /mob/living/simple_mob/animal/passive/lizard/large, - /mob/living/simple_mob/animal/passive/lizard/large/defensive - ) - -/datum/event2/event/infestation/spiderlings - vermin_string = "spiders" - max_vermin = 3 - things_to_spawn = list(/obj/effect/spider/spiderling/non_growing) - - -/datum/event2/event/infestation/set_up() - turfs = find_random_turfs(max_vermin) - if(!turfs.len) - log_debug("Infestation event failed to find any valid turfs. Aborting.") - abort() - return - -/datum/event2/event/infestation/announce() - var/turf/T = turfs[1] - command_announcement.Announce("Bioscans indicate that [vermin_string] have been breeding \ - in \the [T.loc]. Clear them out, before this starts to affect productivity.", "Vermin infestation") - - -/datum/event2/event/infestation/start() - var/vermin_to_spawn = rand(2, max_vermin) - for(var/i = 1 to vermin_to_spawn) - var/turf/T = pick(turfs) - turfs -= T - var/spawn_type = pick(things_to_spawn) - new spawn_type(T) +/datum/event2/meta/infestation + event_class = "infestation" + departments = list(DEPARTMENT_EVERYONE) + +/datum/event2/meta/infestation/get_weight() + return metric.count_people_in_department(DEPARTMENT_EVERYONE) * 10 + +/datum/event2/meta/infestation/rodents + name = "infestation - rodents" + event_type = /datum/event2/event/infestation/rodents + +/datum/event2/meta/infestation/lizards + name = "infestation - lizards" + event_type = /datum/event2/event/infestation/lizards + +/datum/event2/meta/infestation/spiderlings + name = "infestation - spiders" + event_type = /datum/event2/event/infestation/spiderlings + +/datum/event2/event/infestation/cockroaches + vermin_string = "cockroaches" + max_vermin = 6 + things_to_spawn = list(/mob/living/simple_mob/animal/passive/cockroach) + +/datum/event2/event/infestation + var/vermin_string = null + var/max_vermin = 0 + var/list/things_to_spawn = list() + + var/list/turfs = list() + +/datum/event2/event/infestation/rodents + vermin_string = "rodents" + max_vermin = 12 + things_to_spawn = list( + /mob/living/simple_mob/animal/passive/mouse/gray, + /mob/living/simple_mob/animal/passive/mouse/brown, + /mob/living/simple_mob/animal/passive/mouse/black, + /mob/living/simple_mob/animal/passive/mouse/white, + /mob/living/simple_mob/animal/passive/mouse/rat + ) + +/datum/event2/event/infestation/lizards + vermin_string = "lizards" + max_vermin = 6 + things_to_spawn = list( + /mob/living/simple_mob/animal/passive/lizard, + /mob/living/simple_mob/animal/passive/lizard/large, + /mob/living/simple_mob/animal/passive/lizard/large/defensive + ) + +/datum/event2/event/infestation/spiderlings + vermin_string = "spiders" + max_vermin = 3 + things_to_spawn = list(/obj/effect/spider/spiderling/non_growing) + + +/datum/event2/event/infestation/set_up() + turfs = find_random_turfs(max_vermin) + if(!turfs.len) + log_debug("Infestation event failed to find any valid turfs. Aborting.") + abort() + return + +/datum/event2/event/infestation/announce() + var/turf/T = turfs[1] + command_announcement.Announce("Bioscans indicate that [vermin_string] have been breeding \ + in \the [T.loc]. Clear them out, before this starts to affect productivity.", "Vermin infestation") + + +/datum/event2/event/infestation/start() + var/vermin_to_spawn = rand(2, max_vermin) + for(var/i = 1 to vermin_to_spawn) + var/turf/T = pick(turfs) + turfs -= T + var/spawn_type = pick(things_to_spawn) + new spawn_type(T) diff --git a/code/modules/gamemaster/event2/events/everyone/pda_spam.dm b/code/modules/gamemaster/event2/events/everyone/pda_spam.dm index c6a1532d0ed..212087abbca 100644 --- a/code/modules/gamemaster/event2/events/everyone/pda_spam.dm +++ b/code/modules/gamemaster/event2/events/everyone/pda_spam.dm @@ -1,142 +1,142 @@ -/datum/event2/meta/pda_spam - name = "pda spam" - departments = list(DEPARTMENT_EVERYONE) - event_type = /datum/event2/event/pda_spam - -/datum/event2/meta/pda_spam/get_weight() - return metric.count_people_in_department(DEPARTMENT_EVERYONE) * 2 - - -/datum/event2/event/pda_spam - length_lower_bound = 30 MINUTES - length_upper_bound = 1 HOUR - var/spam_debug = FALSE // If true, notices of the event sending spam go to `log_debug()`. - var/last_spam_time = null // world.time of most recent spam. - var/next_spam_attempt_time = 0 // world.time of next attempt to try to spam. - var/give_up_after = 5 MINUTES - var/obj/machinery/message_server/MS = null - var/obj/machinery/exonet_node/node = null - -/datum/event2/event/pda_spam/set_up() - last_spam_time = world.time // So it won't immediately give up. - MS = pick_message_server() - node = get_exonet_node() - -/datum/event2/event/pda_spam/event_tick() - if(!can_spam()) - return - - if(world.time < next_spam_attempt_time) - return - - next_spam_attempt_time = world.time + rand(30 SECONDS, 2 MINUTES) - - var/obj/item/device/pda/P = null - var/list/viables = list() - - for(var/obj/item/device/pda/check_pda in sortAtom(PDAs)) - if (!check_pda.owner || check_pda == src || check_pda.hidden) - continue - - var/datum/data/pda/app/messenger/M = check_pda.find_program(/datum/data/pda/app/messenger) - if(!M || M.toff) - continue - viables += check_pda - - if(!viables.len) - return - - P = pick(viables) - var/list/spam = generate_spam() - - if(MS.send_pda_message("[P.owner]", spam[1], spam[2])) // Message been filtered by spam filter. - return - - send_spam(P, spam[1], spam[2]) - - -/datum/event2/event/pda_spam/should_end() - . = ..() - if(!.) - // Give up if nobody was reachable for five minutes. - if(last_spam_time + give_up_after < world.time) - log_debug("PDA Spam event giving up after not being able to spam for awhile.") - return TRUE - -/datum/event2/event/pda_spam/proc/can_spam() - if(!node || !node.on || !node.allow_external_PDAs) - node = get_exonet_node() - return FALSE - - if(!MS || !MS.active) - MS = pick_message_server() - return FALSE - - return TRUE - -// Returns a list containing two items, the sender and message. -/datum/event2/event/pda_spam/proc/generate_spam() - var/sender = null - var/message = null - switch(rand(1, 7)) - if(1) - sender = pick("MaxBet","MaxBet Online Casino","There is no better time to register","I'm excited for you to join us") - message = pick("Triple deposits are waiting for you at MaxBet Online when you register to play with us.",\ - "You can qualify for a 200% Welcome Bonus at MaxBet Online when you sign up today.",\ - "Once you are a player with MaxBet, you will also receive lucrative weekly and monthly promotions.",\ - "You will be able to enjoy over 450 top-flight casino games at MaxBet.") - if(2) - sender = pick(300;"QuickDatingSystem",200;"Find your russian bride",50;"Tajaran beauties are waiting",50;"Find your secret skrell crush",50;"Beautiful unathi brides") - message = pick("Your profile caught my attention and I wanted to write and say hello (QuickDating).",\ - "If you will write to me on my email [pick(first_names_female)]@[pick(last_names)].[pick("ru","ck","tj","ur","nt")] I shall necessarily send you a photo (QuickDating).",\ - "I want that we write each other and I hope, that you will like my profile and you will answer me (QuickDating).",\ - "You have (1) new message!",\ - "You have (2) new profile views!") - if(3) - sender = pick("Galactic Payments Association","Better Business Bureau","[using_map.starsys_name] E-Payments","NAnoTransen Finance Deparmtent","Luxury Replicas") - message = pick("Luxury watches for Blowout sale prices!",\ - "Watches, Jewelry & Accessories, Bags & Wallets !",\ - "Deposit 100$ and get 300$ totally free!",\ - " 100K NT.|WOWGOLD �nly $89 ",\ - "We have been filed with a complaint from one of your customers in respect of their business relations with you.",\ - "We kindly ask you to open the COMPLAINT REPORT (attached) to reply on this complaint..") - if(4) - sender = pick("Buy Dr. Maxman","Having dysfuctional troubles?") - message = pick("DR MAXMAN: REAL Doctors, REAL Science, REAL Results!",\ - "Dr. Maxman was created by George Acuilar, M.D, a [using_map.boss_short] Certified Urologist who has treated over 70,000 patients sector wide with 'male problems'.",\ - "After seven years of research, Dr Acuilar and his team came up with this simple breakthrough male enhancement formula.",\ - "Men of all species report AMAZING increases in length, width and stamina.") - if(5) - sender = pick("Dr","Crown prince","King Regent","Professor","Captain") - sender += " " + pick("Robert","Alfred","Josephat","Kingsley","Sehi","Zbahi") - sender += " " + pick("Mugawe","Nkem","Gbatokwia","Nchekwube","Ndim","Ndubisi") - message = pick("YOUR FUND HAS BEEN MOVED TO [uppertext(pick("Salusa","Segunda","Cepheus","Andromeda","Gruis","Corona","Aquila","ARES","Asellus"))] DEVELOPMENTARY BANK FOR ONWARD REMITTANCE.",\ - "We are happy to inform you that due to the delay, we have been instructed to IMMEDIATELY deposit all funds into your account",\ - "Dear fund beneficiary, We have please to inform you that overdue funds payment has finally been approved and released for payment",\ - "Due to my lack of agents I require an off-world financial account to immediately deposit the sum of 1 POINT FIVE MILLION credits.",\ - "Greetings sir, I regretfully to inform you that as I lay dying here due to my lack ofheirs I have chosen you to recieve the full sum of my lifetime savings of 1.5 billion credits") - if(6) - sender = pick("[using_map.company_name] Morale Divison","Feeling Lonely?","Bored?","www.wetskrell.nt") - message = pick("The [using_map.company_name] Morale Division wishes to provide you with quality entertainment sites.",\ - "WetSkrell.nt is a xenophillic website endorsed by NT for the use of male crewmembers among it's many stations and outposts.",\ - "Wetskrell.nt only provides the higest quality of male entertaiment to [using_map.company_name] Employees.",\ - "Simply enter your [using_map.company_name] Bank account system number and pin. With three easy steps this service could be yours!") - if(7) - sender = pick("You have won free tickets!","Click here to claim your prize!","You are the 1000th vistor!","You are our lucky grand prize winner!") - message = pick("You have won tickets to the newest ACTION JAXSON MOVIE!",\ - "You have won tickets to the newest crime drama DETECTIVE MYSTERY IN THE CLAMITY CAPER!",\ - "You have won tickets to the newest romantic comedy 16 RULES OF LOVE!",\ - "You have won tickets to the newest thriller THE CULT OF THE SLEEPING ONE!") - return list(sender, message) - -/datum/event2/event/pda_spam/proc/send_spam(obj/item/device/pda/P, sender, message) - last_spam_time = world.time - var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) - PM.notify("Message from [sender] (Unknown / spam?), \"[message]\" (Unable to Reply)", 0) - if(spam_debug) - log_debug("PDA Spam event sent spam to \the [P].") - - -/datum/event2/event/pda_spam/proc/pick_message_server() - if(LAZYLEN(message_servers)) - return pick(message_servers) +/datum/event2/meta/pda_spam + name = "pda spam" + departments = list(DEPARTMENT_EVERYONE) + event_type = /datum/event2/event/pda_spam + +/datum/event2/meta/pda_spam/get_weight() + return metric.count_people_in_department(DEPARTMENT_EVERYONE) * 2 + + +/datum/event2/event/pda_spam + length_lower_bound = 30 MINUTES + length_upper_bound = 1 HOUR + var/spam_debug = FALSE // If true, notices of the event sending spam go to `log_debug()`. + var/last_spam_time = null // world.time of most recent spam. + var/next_spam_attempt_time = 0 // world.time of next attempt to try to spam. + var/give_up_after = 5 MINUTES + var/obj/machinery/message_server/MS = null + var/obj/machinery/exonet_node/node = null + +/datum/event2/event/pda_spam/set_up() + last_spam_time = world.time // So it won't immediately give up. + MS = pick_message_server() + node = get_exonet_node() + +/datum/event2/event/pda_spam/event_tick() + if(!can_spam()) + return + + if(world.time < next_spam_attempt_time) + return + + next_spam_attempt_time = world.time + rand(30 SECONDS, 2 MINUTES) + + var/obj/item/device/pda/P = null + var/list/viables = list() + + for(var/obj/item/device/pda/check_pda in sortAtom(PDAs)) + if (!check_pda.owner || check_pda == src || check_pda.hidden) + continue + + var/datum/data/pda/app/messenger/M = check_pda.find_program(/datum/data/pda/app/messenger) + if(!M || M.toff) + continue + viables += check_pda + + if(!viables.len) + return + + P = pick(viables) + var/list/spam = generate_spam() + + if(MS.send_pda_message("[P.owner]", spam[1], spam[2])) // Message been filtered by spam filter. + return + + send_spam(P, spam[1], spam[2]) + + +/datum/event2/event/pda_spam/should_end() + . = ..() + if(!.) + // Give up if nobody was reachable for five minutes. + if(last_spam_time + give_up_after < world.time) + log_debug("PDA Spam event giving up after not being able to spam for awhile.") + return TRUE + +/datum/event2/event/pda_spam/proc/can_spam() + if(!node || !node.on || !node.allow_external_PDAs) + node = get_exonet_node() + return FALSE + + if(!MS || !MS.active) + MS = pick_message_server() + return FALSE + + return TRUE + +// Returns a list containing two items, the sender and message. +/datum/event2/event/pda_spam/proc/generate_spam() + var/sender = null + var/message = null + switch(rand(1, 7)) + if(1) + sender = pick("MaxBet","MaxBet Online Casino","There is no better time to register","I'm excited for you to join us") + message = pick("Triple deposits are waiting for you at MaxBet Online when you register to play with us.",\ + "You can qualify for a 200% Welcome Bonus at MaxBet Online when you sign up today.",\ + "Once you are a player with MaxBet, you will also receive lucrative weekly and monthly promotions.",\ + "You will be able to enjoy over 450 top-flight casino games at MaxBet.") + if(2) + sender = pick(300;"QuickDatingSystem",200;"Find your russian bride",50;"Tajaran beauties are waiting",50;"Find your secret skrell crush",50;"Beautiful unathi brides") + message = pick("Your profile caught my attention and I wanted to write and say hello (QuickDating).",\ + "If you will write to me on my email [pick(first_names_female)]@[pick(last_names)].[pick("ru","ck","tj","ur","nt")] I shall necessarily send you a photo (QuickDating).",\ + "I want that we write each other and I hope, that you will like my profile and you will answer me (QuickDating).",\ + "You have (1) new message!",\ + "You have (2) new profile views!") + if(3) + sender = pick("Galactic Payments Association","Better Business Bureau","[using_map.starsys_name] E-Payments","NAnoTransen Finance Deparmtent","Luxury Replicas") + message = pick("Luxury watches for Blowout sale prices!",\ + "Watches, Jewelry & Accessories, Bags & Wallets !",\ + "Deposit 100$ and get 300$ totally free!",\ + " 100K NT.|WOWGOLD �nly $89 ",\ + "We have been filed with a complaint from one of your customers in respect of their business relations with you.",\ + "We kindly ask you to open the COMPLAINT REPORT (attached) to reply on this complaint..") + if(4) + sender = pick("Buy Dr. Maxman","Having dysfuctional troubles?") + message = pick("DR MAXMAN: REAL Doctors, REAL Science, REAL Results!",\ + "Dr. Maxman was created by George Acuilar, M.D, a [using_map.boss_short] Certified Urologist who has treated over 70,000 patients sector wide with 'male problems'.",\ + "After seven years of research, Dr Acuilar and his team came up with this simple breakthrough male enhancement formula.",\ + "Men of all species report AMAZING increases in length, width and stamina.") + if(5) + sender = pick("Dr","Crown prince","King Regent","Professor","Captain") + sender += " " + pick("Robert","Alfred","Josephat","Kingsley","Sehi","Zbahi") + sender += " " + pick("Mugawe","Nkem","Gbatokwia","Nchekwube","Ndim","Ndubisi") + message = pick("YOUR FUND HAS BEEN MOVED TO [uppertext(pick("Salusa","Segunda","Cepheus","Andromeda","Gruis","Corona","Aquila","ARES","Asellus"))] DEVELOPMENTARY BANK FOR ONWARD REMITTANCE.",\ + "We are happy to inform you that due to the delay, we have been instructed to IMMEDIATELY deposit all funds into your account",\ + "Dear fund beneficiary, We have please to inform you that overdue funds payment has finally been approved and released for payment",\ + "Due to my lack of agents I require an off-world financial account to immediately deposit the sum of 1 POINT FIVE MILLION credits.",\ + "Greetings sir, I regretfully to inform you that as I lay dying here due to my lack ofheirs I have chosen you to recieve the full sum of my lifetime savings of 1.5 billion credits") + if(6) + sender = pick("[using_map.company_name] Morale Divison","Feeling Lonely?","Bored?","www.wetskrell.nt") + message = pick("The [using_map.company_name] Morale Division wishes to provide you with quality entertainment sites.",\ + "WetSkrell.nt is a xenophillic website endorsed by NT for the use of male crewmembers among it's many stations and outposts.",\ + "Wetskrell.nt only provides the higest quality of male entertaiment to [using_map.company_name] Employees.",\ + "Simply enter your [using_map.company_name] Bank account system number and pin. With three easy steps this service could be yours!") + if(7) + sender = pick("You have won free tickets!","Click here to claim your prize!","You are the 1000th vistor!","You are our lucky grand prize winner!") + message = pick("You have won tickets to the newest ACTION JAXSON MOVIE!",\ + "You have won tickets to the newest crime drama DETECTIVE MYSTERY IN THE CLAMITY CAPER!",\ + "You have won tickets to the newest romantic comedy 16 RULES OF LOVE!",\ + "You have won tickets to the newest thriller THE CULT OF THE SLEEPING ONE!") + return list(sender, message) + +/datum/event2/event/pda_spam/proc/send_spam(obj/item/device/pda/P, sender, message) + last_spam_time = world.time + var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) + PM.notify("Message from [sender] (Unknown / spam?), \"[message]\" (Unable to Reply)", 0) + if(spam_debug) + log_debug("PDA Spam event sent spam to \the [P].") + + +/datum/event2/event/pda_spam/proc/pick_message_server() + if(LAZYLEN(message_servers)) + return pick(message_servers) diff --git a/code/modules/gamemaster/event2/events/everyone/radiation_storm.dm b/code/modules/gamemaster/event2/events/everyone/radiation_storm.dm index c81968c23e8..d5c4592b3f6 100644 --- a/code/modules/gamemaster/event2/events/everyone/radiation_storm.dm +++ b/code/modules/gamemaster/event2/events/everyone/radiation_storm.dm @@ -1,49 +1,49 @@ -/datum/event2/meta/radiation_storm - name = "radiation storm" - departments = list(DEPARTMENT_EVERYONE) - chaos = 20 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/radiation_storm - -/datum/event2/meta/radiation_storm/get_weight() - var/medical_factor = metric.count_people_in_department(DEPARTMENT_MEDICAL) * 10 - var/population_factor = metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5 // Note medical people will get counted twice at 25 weight. - return 20 + medical_factor + population_factor - - - -/datum/event2/event/radiation_storm - start_delay_lower_bound = 1 MINUTE - length_lower_bound = 1 MINUTE - -/datum/event2/event/radiation_storm/announce() - command_announcement.Announce("High levels of radiation detected near \the [location_name()]. \ - Please evacuate into one of the shielded maintenance tunnels.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') - make_maint_all_access() - -/datum/event2/event/radiation_storm/start() - command_announcement.Announce("The station has entered the radiation belt. \ - Please remain in a sheltered area until we have passed the radiation belt.", "Anomaly Alert") - -/datum/event2/event/radiation_storm/event_tick() - radiate() - -/datum/event2/event/radiation_storm/proc/radiate() - var/radiation_level = rand(15, 35) - for(var/z in using_map.station_levels) - SSradiation.z_radiate(locate(1, 1, z), radiation_level, 1) - -/datum/event2/event/radiation_storm/end() - command_announcement.Announce("The station has passed the radiation belt. \ - Please allow for up to one minute while radiation levels dissipate, and report to \ - medbay if you experience any unusual symptoms. Maintenance will lose all \ - access again shortly.", "Anomaly Alert") - addtimer(CALLBACK(src, PROC_REF(maint_callback)), 2 MINUTES) - -/datum/event2/event/radiation_storm/proc/maint_callback() - revoke_maint_all_access() - - -// There is no actual radiation during a fake storm. -/datum/event2/event/radiation_storm/fake/radiate() - return +/datum/event2/meta/radiation_storm + name = "radiation storm" + departments = list(DEPARTMENT_EVERYONE) + chaos = 20 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/radiation_storm + +/datum/event2/meta/radiation_storm/get_weight() + var/medical_factor = metric.count_people_in_department(DEPARTMENT_MEDICAL) * 10 + var/population_factor = metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5 // Note medical people will get counted twice at 25 weight. + return 20 + medical_factor + population_factor + + + +/datum/event2/event/radiation_storm + start_delay_lower_bound = 1 MINUTE + length_lower_bound = 1 MINUTE + +/datum/event2/event/radiation_storm/announce() + command_announcement.Announce("High levels of radiation detected near \the [location_name()]. \ + Please evacuate into one of the shielded maintenance tunnels.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') + make_maint_all_access() + +/datum/event2/event/radiation_storm/start() + command_announcement.Announce("The station has entered the radiation belt. \ + Please remain in a sheltered area until we have passed the radiation belt.", "Anomaly Alert") + +/datum/event2/event/radiation_storm/event_tick() + radiate() + +/datum/event2/event/radiation_storm/proc/radiate() + var/radiation_level = rand(15, 35) + for(var/z in using_map.station_levels) + SSradiation.z_radiate(locate(1, 1, z), radiation_level, 1) + +/datum/event2/event/radiation_storm/end() + command_announcement.Announce("The station has passed the radiation belt. \ + Please allow for up to one minute while radiation levels dissipate, and report to \ + medbay if you experience any unusual symptoms. Maintenance will lose all \ + access again shortly.", "Anomaly Alert") + addtimer(CALLBACK(src, PROC_REF(maint_callback)), 2 MINUTES) + +/datum/event2/event/radiation_storm/proc/maint_callback() + revoke_maint_all_access() + + +// There is no actual radiation during a fake storm. +/datum/event2/event/radiation_storm/fake/radiate() + return diff --git a/code/modules/gamemaster/event2/events/everyone/random_antag.dm b/code/modules/gamemaster/event2/events/everyone/random_antag.dm index fe3b58be8c9..205c78923bb 100644 --- a/code/modules/gamemaster/event2/events/everyone/random_antag.dm +++ b/code/modules/gamemaster/event2/events/everyone/random_antag.dm @@ -1,31 +1,31 @@ -// No idea if this is needed for autotraitor or not. -// If it is, it shouldn't depend on the event system, but fixing that would be it's own project. -// If not, it can stay off until an admin wants to play with it. - -/datum/event2/meta/random_antagonist - name = "random antagonist" - enabled = FALSE - reusable = TRUE - chaos = 0 // This is zero due to the event system not being able to know if an antag actually got spawned or not. - departments = list(DEPARTMENT_EVERYONE) - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/random_antagonist - -// This has an abnormally high weight due to antags being very important for the round, -// however the weight will decay with more antags, and more attempts to add antags. -/datum/event2/meta/random_antagonist/get_weight() - var/antags = metric.count_all_antags() - return 200 / (antags + times_ran + 1) - - - -// The random spawn proc on the antag datum will handle announcing the spawn and whatnot, in theory. -/datum/event2/event/random_antagonist/start() - var/list/valid_types = list() - for(var/antag_type in all_antag_types) - var/datum/antagonist/antag = all_antag_types[antag_type] - if(antag.flags & ANTAG_RANDSPAWN) - valid_types |= antag - if(valid_types.len) - var/datum/antagonist/antag = pick(valid_types) - antag.attempt_random_spawn() +// No idea if this is needed for autotraitor or not. +// If it is, it shouldn't depend on the event system, but fixing that would be it's own project. +// If not, it can stay off until an admin wants to play with it. + +/datum/event2/meta/random_antagonist + name = "random antagonist" + enabled = FALSE + reusable = TRUE + chaos = 0 // This is zero due to the event system not being able to know if an antag actually got spawned or not. + departments = list(DEPARTMENT_EVERYONE) + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/random_antagonist + +// This has an abnormally high weight due to antags being very important for the round, +// however the weight will decay with more antags, and more attempts to add antags. +/datum/event2/meta/random_antagonist/get_weight() + var/antags = metric.count_all_antags() + return 200 / (antags + times_ran + 1) + + + +// The random spawn proc on the antag datum will handle announcing the spawn and whatnot, in theory. +/datum/event2/event/random_antagonist/start() + var/list/valid_types = list() + for(var/antag_type in all_antag_types) + var/datum/antagonist/antag = all_antag_types[antag_type] + if(antag.flags & ANTAG_RANDSPAWN) + valid_types |= antag + if(valid_types.len) + var/datum/antagonist/antag = pick(valid_types) + antag.attempt_random_spawn() diff --git a/code/modules/gamemaster/event2/events/everyone/solar_storm.dm b/code/modules/gamemaster/event2/events/everyone/solar_storm.dm index bfc35440a35..809e1319117 100644 --- a/code/modules/gamemaster/event2/events/everyone/solar_storm.dm +++ b/code/modules/gamemaster/event2/events/everyone/solar_storm.dm @@ -1,52 +1,52 @@ -/datum/event2/meta/solar_storm - name = "solar storm" - reusable = TRUE - event_type = /datum/event2/event/solar_storm - -/datum/event2/meta/solar_storm/get_weight() - var/population_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10 - var/space_factor = metric.count_all_space_mobs() * 50 - return (20 + population_factor + space_factor) / (times_ran + 1) - - -/datum/event2/event/solar_storm - start_delay_lower_bound = 1 MINUTE - start_delay_upper_bound = 1 MINUTE - length_lower_bound = 2 MINUTES - length_upper_bound = 4 MINUTES - var/base_solar_gen_rate = null - -/datum/event2/event/solar_storm/announce() - command_announcement.Announce("A solar storm has been detected approaching \the [station_name()]. \ - Please halt all EVA activites immediately and return to the interior of the station.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') - adjust_solar_output(1.5) - -/datum/event2/event/solar_storm/start() - command_announcement.Announce("The solar storm has reached the station. Please refrain from EVA and remain inside the station until it has passed.", "Anomaly Alert") - adjust_solar_output(5) - -/datum/event2/event/solar_storm/event_tick() - radiate() - -/datum/event2/event/solar_storm/end() - command_announcement.Announce("The solar storm has passed the station. It is now safe to resume EVA activities. \ - Please report to medbay if you experience any unusual symptoms.", "Anomaly Alert") - adjust_solar_output(1) - -/datum/event2/event/solar_storm/proc/adjust_solar_output(var/mult = 1) - if(isnull(base_solar_gen_rate)) - base_solar_gen_rate = GLOB.solar_gen_rate - GLOB.solar_gen_rate = mult * base_solar_gen_rate - -/datum/event2/event/solar_storm/proc/radiate() - // Note: Too complicated to be worth trying to use the radiation system for this. Its only in space anyway, so we make an exception in this case. - for(var/mob/living/L in player_list) - var/turf/T = get_turf(L) - if(!T) - continue - - if(!istype(T.loc,/area/space) && !istype(T,/turf/space)) //Make sure you're in a space area or on a space turf - continue - - //Todo: Apply some burn damage from the heat of the sun. Until then, enjoy some moderate radiation. - L.rad_act(rand(15, 30)) +/datum/event2/meta/solar_storm + name = "solar storm" + reusable = TRUE + event_type = /datum/event2/event/solar_storm + +/datum/event2/meta/solar_storm/get_weight() + var/population_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10 + var/space_factor = metric.count_all_space_mobs() * 50 + return (20 + population_factor + space_factor) / (times_ran + 1) + + +/datum/event2/event/solar_storm + start_delay_lower_bound = 1 MINUTE + start_delay_upper_bound = 1 MINUTE + length_lower_bound = 2 MINUTES + length_upper_bound = 4 MINUTES + var/base_solar_gen_rate = null + +/datum/event2/event/solar_storm/announce() + command_announcement.Announce("A solar storm has been detected approaching \the [station_name()]. \ + Please halt all EVA activites immediately and return to the interior of the station.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') + adjust_solar_output(1.5) + +/datum/event2/event/solar_storm/start() + command_announcement.Announce("The solar storm has reached the station. Please refrain from EVA and remain inside the station until it has passed.", "Anomaly Alert") + adjust_solar_output(5) + +/datum/event2/event/solar_storm/event_tick() + radiate() + +/datum/event2/event/solar_storm/end() + command_announcement.Announce("The solar storm has passed the station. It is now safe to resume EVA activities. \ + Please report to medbay if you experience any unusual symptoms.", "Anomaly Alert") + adjust_solar_output(1) + +/datum/event2/event/solar_storm/proc/adjust_solar_output(var/mult = 1) + if(isnull(base_solar_gen_rate)) + base_solar_gen_rate = GLOB.solar_gen_rate + GLOB.solar_gen_rate = mult * base_solar_gen_rate + +/datum/event2/event/solar_storm/proc/radiate() + // Note: Too complicated to be worth trying to use the radiation system for this. Its only in space anyway, so we make an exception in this case. + for(var/mob/living/L in player_list) + var/turf/T = get_turf(L) + if(!T) + continue + + if(!istype(T.loc,/area/space) && !istype(T,/turf/space)) //Make sure you're in a space area or on a space turf + continue + + //Todo: Apply some burn damage from the heat of the sun. Until then, enjoy some moderate radiation. + L.rad_act(rand(15, 30)) diff --git a/code/modules/gamemaster/event2/events/everyone/sudden_weather_shift.dm b/code/modules/gamemaster/event2/events/everyone/sudden_weather_shift.dm index 2d83209612c..4c767fbb931 100644 --- a/code/modules/gamemaster/event2/events/everyone/sudden_weather_shift.dm +++ b/code/modules/gamemaster/event2/events/everyone/sudden_weather_shift.dm @@ -1,45 +1,45 @@ -/datum/event2/meta/sudden_weather_shift - name = "sudden weather shift" - departments = list(DEPARTMENT_EVERYONE) - reusable = TRUE - event_type = /datum/event2/event/sudden_weather_shift - -/datum/event2/meta/sudden_weather_shift/get_weight() - // The proc name is a bit misleading, it only counts players outside, not all mobs. - return (metric.count_all_outdoor_mobs() * 20) / (times_ran + 1) - -/datum/event2/event/sudden_weather_shift - start_delay_lower_bound = 30 SECONDS - start_delay_upper_bound = 1 MINUTE - var/datum/planet/chosen_planet = null - -/datum/event2/event/sudden_weather_shift/set_up() - if(!LAZYLEN(SSplanets.planets)) - log_debug("Weather shift event was ran when no planets exist. Aborting.") - abort() - return - - chosen_planet = pick(SSplanets.planets) - -/datum/event2/event/sudden_weather_shift/announce() - if(!chosen_planet) - return - command_announcement.Announce("Local weather patterns on [chosen_planet.name] suggest that a \ - sudden atmospheric fluctuation has occurred. All groundside personnel should be wary of \ - rapidly deteriorating conditions.", "Weather Alert") - -/datum/event2/event/sudden_weather_shift/start() - // Using the roundstart weather list is handy, because it avoids the chance of choosing a bus-only weather. - // It also makes this event generic and suitable for other planets besides the main one, with no additional code needed. - // Only flaw is that roundstart weathers are -usually- safe ones, but we can fix that by tweaking a copy of it. - var/list/weather_choices = chosen_planet.weather_holder.roundstart_weather_chances.Copy() - var/list/new_weather_weights = list() - - // A lazy way of inverting the odds is to use some division. - for(var/weather in weather_choices) - new_weather_weights[weather] = 100 / weather_choices[weather] - - // Now choose a new weather. - var/new_weather = pickweight(new_weather_weights) - log_debug("Sudden weather shift event is now changing [chosen_planet.name]'s weather to [new_weather].") - chosen_planet.weather_holder.change_weather(new_weather) +/datum/event2/meta/sudden_weather_shift + name = "sudden weather shift" + departments = list(DEPARTMENT_EVERYONE) + reusable = TRUE + event_type = /datum/event2/event/sudden_weather_shift + +/datum/event2/meta/sudden_weather_shift/get_weight() + // The proc name is a bit misleading, it only counts players outside, not all mobs. + return (metric.count_all_outdoor_mobs() * 20) / (times_ran + 1) + +/datum/event2/event/sudden_weather_shift + start_delay_lower_bound = 30 SECONDS + start_delay_upper_bound = 1 MINUTE + var/datum/planet/chosen_planet = null + +/datum/event2/event/sudden_weather_shift/set_up() + if(!LAZYLEN(SSplanets.planets)) + log_debug("Weather shift event was ran when no planets exist. Aborting.") + abort() + return + + chosen_planet = pick(SSplanets.planets) + +/datum/event2/event/sudden_weather_shift/announce() + if(!chosen_planet) + return + command_announcement.Announce("Local weather patterns on [chosen_planet.name] suggest that a \ + sudden atmospheric fluctuation has occurred. All groundside personnel should be wary of \ + rapidly deteriorating conditions.", "Weather Alert") + +/datum/event2/event/sudden_weather_shift/start() + // Using the roundstart weather list is handy, because it avoids the chance of choosing a bus-only weather. + // It also makes this event generic and suitable for other planets besides the main one, with no additional code needed. + // Only flaw is that roundstart weathers are -usually- safe ones, but we can fix that by tweaking a copy of it. + var/list/weather_choices = chosen_planet.weather_holder.roundstart_weather_chances.Copy() + var/list/new_weather_weights = list() + + // A lazy way of inverting the odds is to use some division. + for(var/weather in weather_choices) + new_weather_weights[weather] = 100 / weather_choices[weather] + + // Now choose a new weather. + var/new_weather = pickweight(new_weather_weights) + log_debug("Sudden weather shift event is now changing [chosen_planet.name]'s weather to [new_weather].") + chosen_planet.weather_holder.change_weather(new_weather) diff --git a/code/modules/gamemaster/event2/events/ghost_pod_spawner.dm b/code/modules/gamemaster/event2/events/ghost_pod_spawner.dm index 4c462d06d6c..32de1e4a441 100644 --- a/code/modules/gamemaster/event2/events/ghost_pod_spawner.dm +++ b/code/modules/gamemaster/event2/events/ghost_pod_spawner.dm @@ -1,21 +1,21 @@ -// Generic subtype for events that make ghost pods. - -/datum/event2/event/ghost_pod_spawner - var/pod_type = null - var/list/desired_turf_areas = list() // If this is left empty, it will default to a global list of 'station' turfs. - var/list/free_turfs = list() - -/datum/event2/event/ghost_pod_spawner/set_up() - free_turfs = find_random_turfs(5, desired_turf_areas) - - if(!free_turfs.len) - log_debug("Ghost Pod Spawning event failed to find a place to spawn. Aborting.") - abort() - return - -/datum/event2/event/ghost_pod_spawner/start() - var/obj/structure/ghost_pod/pod = new pod_type(pick(free_turfs)) - post_pod_creation(pod) - -// Override to do things to the pod after it's spawned. +// Generic subtype for events that make ghost pods. + +/datum/event2/event/ghost_pod_spawner + var/pod_type = null + var/list/desired_turf_areas = list() // If this is left empty, it will default to a global list of 'station' turfs. + var/list/free_turfs = list() + +/datum/event2/event/ghost_pod_spawner/set_up() + free_turfs = find_random_turfs(5, desired_turf_areas) + + if(!free_turfs.len) + log_debug("Ghost Pod Spawning event failed to find a place to spawn. Aborting.") + abort() + return + +/datum/event2/event/ghost_pod_spawner/start() + var/obj/structure/ghost_pod/pod = new pod_type(pick(free_turfs)) + post_pod_creation(pod) + +// Override to do things to the pod after it's spawned. /datum/event2/event/ghost_pod_spawner/proc/post_pod_creation(obj/structure/ghost_pod/pod) \ No newline at end of file diff --git a/code/modules/gamemaster/event2/events/legacy/legacy.dm b/code/modules/gamemaster/event2/events/legacy/legacy.dm index 6f70cafa851..736c00b8fe2 100644 --- a/code/modules/gamemaster/event2/events/legacy/legacy.dm +++ b/code/modules/gamemaster/event2/events/legacy/legacy.dm @@ -1,63 +1,63 @@ -// This is a somewhat special type of event, that bridges to the old event datum and makes it work with the new system. -// It acts as a compatability layer between the old event, and the new GM system. -// This is possible because the new datum is mostly a superset of the old one. -/datum/event2/event/legacy - var/datum/event/legacy_event = null - - // Used to emulate legacy's `activeFor` tick counter. - var/tick_count = 0 - - // How 'severe' the legacy event should be. This should only be used for legacy events, as severity is an outdated concept for the GM system. - var/severity = EVENT_LEVEL_MODERATE - -/datum/event2/meta/legacy/get_weight() - return 50 - -/datum/event2/event/legacy/process() - ..() - tick_count++ - -/datum/event2/event/legacy/set_up() - legacy_event = new legacy_event(null, external_use = TRUE) - legacy_event.severity = severity - legacy_event.setup() - -/datum/event2/event/legacy/should_announce() - return tick_count >= legacy_event.announceWhen - -/datum/event2/event/legacy/announce() - legacy_event.announce() - - -// Legacy events don't tick before they start, so we don't need to do `wait_tick()`. - -/datum/event2/event/legacy/should_start() - return tick_count >= legacy_event.startWhen - -/datum/event2/event/legacy/start() - legacy_event.start() - -/datum/event2/event/legacy/event_tick() - legacy_event.tick() - - -/datum/event2/event/legacy/should_end() - return tick_count >= legacy_event.endWhen - -/datum/event2/event/legacy/end() - legacy_event.end() - -/datum/event2/event/legacy/finish() - legacy_event.kill(external_use = TRUE) - ..() - -// Proof of concept. -/* -/datum/event2/meta/legacy_gravity - name = "gravity (legacy)" - reusable = TRUE - event_type = /datum/event2/event/legacy/gravity - -/datum/event2/event/legacy/gravity - legacy_event = /datum/event/gravity +// This is a somewhat special type of event, that bridges to the old event datum and makes it work with the new system. +// It acts as a compatability layer between the old event, and the new GM system. +// This is possible because the new datum is mostly a superset of the old one. +/datum/event2/event/legacy + var/datum/event/legacy_event = null + + // Used to emulate legacy's `activeFor` tick counter. + var/tick_count = 0 + + // How 'severe' the legacy event should be. This should only be used for legacy events, as severity is an outdated concept for the GM system. + var/severity = EVENT_LEVEL_MODERATE + +/datum/event2/meta/legacy/get_weight() + return 50 + +/datum/event2/event/legacy/process() + ..() + tick_count++ + +/datum/event2/event/legacy/set_up() + legacy_event = new legacy_event(null, external_use = TRUE) + legacy_event.severity = severity + legacy_event.setup() + +/datum/event2/event/legacy/should_announce() + return tick_count >= legacy_event.announceWhen + +/datum/event2/event/legacy/announce() + legacy_event.announce() + + +// Legacy events don't tick before they start, so we don't need to do `wait_tick()`. + +/datum/event2/event/legacy/should_start() + return tick_count >= legacy_event.startWhen + +/datum/event2/event/legacy/start() + legacy_event.start() + +/datum/event2/event/legacy/event_tick() + legacy_event.tick() + + +/datum/event2/event/legacy/should_end() + return tick_count >= legacy_event.endWhen + +/datum/event2/event/legacy/end() + legacy_event.end() + +/datum/event2/event/legacy/finish() + legacy_event.kill(external_use = TRUE) + ..() + +// Proof of concept. +/* +/datum/event2/meta/legacy_gravity + name = "gravity (legacy)" + reusable = TRUE + event_type = /datum/event2/event/legacy/gravity + +/datum/event2/event/legacy/gravity + legacy_event = /datum/event/gravity */ \ No newline at end of file diff --git a/code/modules/gamemaster/event2/events/medical/appendicitis.dm b/code/modules/gamemaster/event2/events/medical/appendicitis.dm index f213d2fd016..cf626c2cd0d 100644 --- a/code/modules/gamemaster/event2/events/medical/appendicitis.dm +++ b/code/modules/gamemaster/event2/events/medical/appendicitis.dm @@ -1,36 +1,36 @@ -/datum/event2/meta/appendicitis - name = "appendicitis" - departments = list(DEPARTMENT_MEDICAL) - chaos = 40 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/appendicitis - -/datum/event2/meta/appendicitis/get_weight() - var/list/doctors = metric.get_people_with_job(/datum/job/doctor) - - doctors -= metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/nurse) - doctors -= metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/virologist) - doctors += metric.get_people_with_job(/datum/job/cmo) - - return doctors.len * 10 - - - -/datum/event2/event/appendicitis/start() - for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) - // Don't do it to SSD people. - if(!H.client) - continue - - // Or antags / bellied. - if(player_is_antag(H.mind) || isbelly(H.loc)) - continue - - // Or doctors (otherwise it could be possible for the only surgeon to need surgery). - if(H in metric.get_people_with_job(/datum/job/doctor) ) - continue - - if(H.appendicitis()) - log_debug("Appendicitis event gave appendicitis to \the [H].") - return - log_debug("Appendicitis event could not find a valid victim.") +/datum/event2/meta/appendicitis + name = "appendicitis" + departments = list(DEPARTMENT_MEDICAL) + chaos = 40 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/appendicitis + +/datum/event2/meta/appendicitis/get_weight() + var/list/doctors = metric.get_people_with_job(/datum/job/doctor) + + doctors -= metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/nurse) + doctors -= metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/virologist) + doctors += metric.get_people_with_job(/datum/job/cmo) + + return doctors.len * 10 + + + +/datum/event2/event/appendicitis/start() + for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) + // Don't do it to SSD people. + if(!H.client) + continue + + // Or antags / bellied. + if(player_is_antag(H.mind) || isbelly(H.loc)) + continue + + // Or doctors (otherwise it could be possible for the only surgeon to need surgery). + if(H in metric.get_people_with_job(/datum/job/doctor) ) + continue + + if(H.appendicitis()) + log_debug("Appendicitis event gave appendicitis to \the [H].") + return + log_debug("Appendicitis event could not find a valid victim.") diff --git a/code/modules/gamemaster/event2/events/medical/virus.dm b/code/modules/gamemaster/event2/events/medical/virus.dm index 2b3ada5cf9a..ad221c0d043 100644 --- a/code/modules/gamemaster/event2/events/medical/virus.dm +++ b/code/modules/gamemaster/event2/events/medical/virus.dm @@ -1,69 +1,69 @@ -/datum/event2/meta/virus - name = "viral infection" - event_class = "virus" - departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_EVERYONE) - chaos = 40 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT - event_type = /datum/event2/event/virus - -/datum/event2/meta/virus/superbug - name = "viral superbug" - chaos = 60 - event_type = /datum/event2/event/virus/superbug - -/datum/event2/meta/virus/outbreak - name = "viral outbreak" - chaos = 60 - event_type = /datum/event2/event/virus/outbreak - -/datum/event2/meta/virus/get_weight() - var/list/virologists = metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/virologist) - virologists += metric.get_people_with_job(/datum/job/cmo) - - return virologists.len * 25 - - - -/datum/event2/event/virus - announce_delay_lower_bound = 1 MINUTE - announce_delay_upper_bound = 3 MINUTES - var/number_of_viruses = 1 - var/virus_power = 2 // Ranges from 1 to 3, with 1 being the weakest. - var/list/candidates = list() - -// A single powerful virus. -/datum/event2/event/virus/superbug - virus_power = 3 - -// A lot of weaker viruses. -/datum/event2/event/virus/outbreak - virus_power = 1 - number_of_viruses = 3 - - - -/datum/event2/event/virus/set_up() - for(var/mob/living/carbon/human/H in player_list) - if(H.client && !H.isSynthetic() && H.stat != DEAD && !player_is_antag(H.mind) && !isbelly(H.loc)) - candidates += H - candidates = shuffle(candidates) - -/datum/event2/event/virus/announce() - command_announcement.Announce("Confirmed outbreak of level 7 biohazard aboard \the [location_name()]. \ - All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg') - -/datum/event2/event/virus/start() - if(!candidates.len) - log_debug("Virus event could not find any valid targets to infect. Aborting.") - abort() - return - - for(var/i = 1 to number_of_viruses) - var/mob/living/carbon/human/H = LAZYACCESS(candidates, 1) - if(!H) - return - var/datum/disease2/disease/D = new() - D.makerandom(virus_power) - log_debug("Virus event is now infecting \the [H] with a new random virus.") - infect_mob(H, D) - candidates -= H +/datum/event2/meta/virus + name = "viral infection" + event_class = "virus" + departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_EVERYONE) + chaos = 40 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT + event_type = /datum/event2/event/virus + +/datum/event2/meta/virus/superbug + name = "viral superbug" + chaos = 60 + event_type = /datum/event2/event/virus/superbug + +/datum/event2/meta/virus/outbreak + name = "viral outbreak" + chaos = 60 + event_type = /datum/event2/event/virus/outbreak + +/datum/event2/meta/virus/get_weight() + var/list/virologists = metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/virologist) + virologists += metric.get_people_with_job(/datum/job/cmo) + + return virologists.len * 25 + + + +/datum/event2/event/virus + announce_delay_lower_bound = 1 MINUTE + announce_delay_upper_bound = 3 MINUTES + var/number_of_viruses = 1 + var/virus_power = 2 // Ranges from 1 to 3, with 1 being the weakest. + var/list/candidates = list() + +// A single powerful virus. +/datum/event2/event/virus/superbug + virus_power = 3 + +// A lot of weaker viruses. +/datum/event2/event/virus/outbreak + virus_power = 1 + number_of_viruses = 3 + + + +/datum/event2/event/virus/set_up() + for(var/mob/living/carbon/human/H in player_list) + if(H.client && !H.isSynthetic() && H.stat != DEAD && !player_is_antag(H.mind) && !isbelly(H.loc)) + candidates += H + candidates = shuffle(candidates) + +/datum/event2/event/virus/announce() + command_announcement.Announce("Confirmed outbreak of level 7 biohazard aboard \the [location_name()]. \ + All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg') + +/datum/event2/event/virus/start() + if(!candidates.len) + log_debug("Virus event could not find any valid targets to infect. Aborting.") + abort() + return + + for(var/i = 1 to number_of_viruses) + var/mob/living/carbon/human/H = LAZYACCESS(candidates, 1) + if(!H) + return + var/datum/disease2/disease/D = new() + D.makerandom(virus_power) + log_debug("Virus event is now infecting \the [H] with a new random virus.") + infect_mob(H, D) + candidates -= H diff --git a/code/modules/gamemaster/event2/events/mob_spawning.dm b/code/modules/gamemaster/event2/events/mob_spawning.dm index 35321cd2f06..16ed6a31500 100644 --- a/code/modules/gamemaster/event2/events/mob_spawning.dm +++ b/code/modules/gamemaster/event2/events/mob_spawning.dm @@ -1,97 +1,97 @@ -// A subtype that involves spawning mobs like carp, rogue drones, spiders, etc. - -/datum/event2/event/mob_spawning - var/list/spawned_mobs = list() - var/use_map_edge_with_landmarks = TRUE // Use both landmarks and spawning from the "edge" of the map. Otherise uses landmarks over map edge. - var/landmark_name = "carpspawn" // Which landmark to use for spawning. - -// Spawns a specific mob from the "edge" of the map, and makes them go towards the station. -// Can also use landmarks, if desired. -/datum/event2/event/mob_spawning/proc/spawn_mobs_in_space(mob_type, number_of_groups, min_size_of_group, max_size_of_group, dir) - if(isnull(dir)) - dir = pick(GLOB.cardinal) - - var/list/valid_z_levels = get_location_z_levels() - valid_z_levels -= using_map.sealed_levels // Space levels only please! - - // Check if any landmarks exist! - var/list/spawn_locations = list() - for(var/obj/effect/landmark/C in landmarks_list) - if(C.name == landmark_name && (C.z in valid_z_levels)) - spawn_locations.Add(C.loc) - - var/prioritize_landmarks = TRUE - if(use_map_edge_with_landmarks && prob(50)) - prioritize_landmarks = FALSE // One in two chance to come from the edge instead. - - if(spawn_locations.len && prioritize_landmarks) // Okay we've got landmarks, lets use those! - shuffle_inplace(spawn_locations) - number_of_groups = min(number_of_groups, spawn_locations.len) - var/i = 1 - while (i <= number_of_groups) - var/group_size = rand(min_size_of_group, max_size_of_group) - for (var/j = 0, j < group_size, j++) - spawn_one_mob(spawn_locations[i], mob_type) - i++ - return - - // Okay we did *not* have any landmarks, or we're being told to do both, so lets do our best! - var/i = 1 - while(i <= number_of_groups) - var/z_level = pick(valid_z_levels) - var/group_size = rand(min_size_of_group, max_size_of_group) - var/turf/map_center = locate(round(world.maxx/2), round(world.maxy/2), z_level) - var/turf/group_center = pick_random_edge_turf(dir, z_level, TRANSITIONEDGE + 2) - var/list/turfs = getcircle(group_center, 2) - for(var/j = 0, j < group_size, j++) - // On larger maps, BYOND gets in the way of letting simple_mobs path to the closest edge of the station. - // So instead we need to simulate the mob's travel, then spawn them somewhere still hopefully off screen. - - // Find a turf to be the edge of the map. - var/turf/edge_of_map = turfs[(i % turfs.len) + 1] - - // Now walk a straight line towards the center of the map, until we find a non-space tile. - var/turf/edge_of_station = null - - var/list/space_line = list() // This holds all space tiles on the line. Will be used a bit later. - for(var/turf/T in getline(edge_of_map, map_center)) - if(!T.is_space()) - break // We found the station! - space_line += T - edge_of_station = T - - // Now put the mob somewhere on the line, hopefully off screen. - // I wish this was higher than 8 but the BYOND internal A* algorithm gives up sometimes when using - // 16 or more. - // In the future, a new AI stance that handles long distance travel using getline() could work. - var/max_distance = 8 - var/turf/spawn_turf = null - for(var/turf/point as anything in space_line) - if(get_dist(point, edge_of_station) <= max_distance) - spawn_turf = point - break - - if(spawn_turf) - // Finally, make the simple_mob go towards the edge of the station. - var/mob/living/simple_mob/M = spawn_one_mob(spawn_turf, mob_type) - if(edge_of_station) - M.ai_holder?.give_destination(edge_of_station) // Ask simple_mobs to fly towards the edge of the station. - i++ - -/datum/event2/event/mob_spawning/proc/spawn_one_mob(new_loc, mob_type) - var/mob/living/simple_mob/M = new mob_type(new_loc) - GLOB.destroyed_event.register(M, src, PROC_REF(on_mob_destruction)) - spawned_mobs += M - return M - -// Counts living simple_mobs spawned by this event. -/datum/event2/event/mob_spawning/proc/count_spawned_mobs() - . = 0 - for(var/mob/living/simple_mob/M as anything in spawned_mobs) - if(!QDELETED(M) && M.stat != DEAD) - . += 1 - -// If simple_mob is bomphed, remove it from the list. -/datum/event2/event/mob_spawning/proc/on_mob_destruction(mob/M) - spawned_mobs -= M - GLOB.destroyed_event.unregister(M, src, PROC_REF(on_mob_destruction)) +// A subtype that involves spawning mobs like carp, rogue drones, spiders, etc. + +/datum/event2/event/mob_spawning + var/list/spawned_mobs = list() + var/use_map_edge_with_landmarks = TRUE // Use both landmarks and spawning from the "edge" of the map. Otherise uses landmarks over map edge. + var/landmark_name = "carpspawn" // Which landmark to use for spawning. + +// Spawns a specific mob from the "edge" of the map, and makes them go towards the station. +// Can also use landmarks, if desired. +/datum/event2/event/mob_spawning/proc/spawn_mobs_in_space(mob_type, number_of_groups, min_size_of_group, max_size_of_group, dir) + if(isnull(dir)) + dir = pick(GLOB.cardinal) + + var/list/valid_z_levels = get_location_z_levels() + valid_z_levels -= using_map.sealed_levels // Space levels only please! + + // Check if any landmarks exist! + var/list/spawn_locations = list() + for(var/obj/effect/landmark/C in landmarks_list) + if(C.name == landmark_name && (C.z in valid_z_levels)) + spawn_locations.Add(C.loc) + + var/prioritize_landmarks = TRUE + if(use_map_edge_with_landmarks && prob(50)) + prioritize_landmarks = FALSE // One in two chance to come from the edge instead. + + if(spawn_locations.len && prioritize_landmarks) // Okay we've got landmarks, lets use those! + shuffle_inplace(spawn_locations) + number_of_groups = min(number_of_groups, spawn_locations.len) + var/i = 1 + while (i <= number_of_groups) + var/group_size = rand(min_size_of_group, max_size_of_group) + for (var/j = 0, j < group_size, j++) + spawn_one_mob(spawn_locations[i], mob_type) + i++ + return + + // Okay we did *not* have any landmarks, or we're being told to do both, so lets do our best! + var/i = 1 + while(i <= number_of_groups) + var/z_level = pick(valid_z_levels) + var/group_size = rand(min_size_of_group, max_size_of_group) + var/turf/map_center = locate(round(world.maxx/2), round(world.maxy/2), z_level) + var/turf/group_center = pick_random_edge_turf(dir, z_level, TRANSITIONEDGE + 2) + var/list/turfs = getcircle(group_center, 2) + for(var/j = 0, j < group_size, j++) + // On larger maps, BYOND gets in the way of letting simple_mobs path to the closest edge of the station. + // So instead we need to simulate the mob's travel, then spawn them somewhere still hopefully off screen. + + // Find a turf to be the edge of the map. + var/turf/edge_of_map = turfs[(i % turfs.len) + 1] + + // Now walk a straight line towards the center of the map, until we find a non-space tile. + var/turf/edge_of_station = null + + var/list/space_line = list() // This holds all space tiles on the line. Will be used a bit later. + for(var/turf/T in getline(edge_of_map, map_center)) + if(!T.is_space()) + break // We found the station! + space_line += T + edge_of_station = T + + // Now put the mob somewhere on the line, hopefully off screen. + // I wish this was higher than 8 but the BYOND internal A* algorithm gives up sometimes when using + // 16 or more. + // In the future, a new AI stance that handles long distance travel using getline() could work. + var/max_distance = 8 + var/turf/spawn_turf = null + for(var/turf/point as anything in space_line) + if(get_dist(point, edge_of_station) <= max_distance) + spawn_turf = point + break + + if(spawn_turf) + // Finally, make the simple_mob go towards the edge of the station. + var/mob/living/simple_mob/M = spawn_one_mob(spawn_turf, mob_type) + if(edge_of_station) + M.ai_holder?.give_destination(edge_of_station) // Ask simple_mobs to fly towards the edge of the station. + i++ + +/datum/event2/event/mob_spawning/proc/spawn_one_mob(new_loc, mob_type) + var/mob/living/simple_mob/M = new mob_type(new_loc) + GLOB.destroyed_event.register(M, src, PROC_REF(on_mob_destruction)) + spawned_mobs += M + return M + +// Counts living simple_mobs spawned by this event. +/datum/event2/event/mob_spawning/proc/count_spawned_mobs() + . = 0 + for(var/mob/living/simple_mob/M as anything in spawned_mobs) + if(!QDELETED(M) && M.stat != DEAD) + . += 1 + +// If simple_mob is bomphed, remove it from the list. +/datum/event2/event/mob_spawning/proc/on_mob_destruction(mob/M) + spawned_mobs -= M + GLOB.destroyed_event.unregister(M, src, PROC_REF(on_mob_destruction)) diff --git a/code/modules/gamemaster/event2/events/security/carp_migration.dm b/code/modules/gamemaster/event2/events/security/carp_migration.dm index 12f08f3edd0..77e72d044e4 100644 --- a/code/modules/gamemaster/event2/events/security/carp_migration.dm +++ b/code/modules/gamemaster/event2/events/security/carp_migration.dm @@ -1,49 +1,49 @@ -/datum/event2/meta/carp_migration - name = "carp migration" - event_class = "carp" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) - chaos = 30 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/mob_spawning/carp_migration - -/datum/event2/meta/carp_migration/get_weight() - return 10 + (metric.count_people_in_department(DEPARTMENT_SECURITY) * 20) + (metric.count_all_space_mobs() * 40) - - -/datum/event2/event/mob_spawning/carp_migration - announce_delay_lower_bound = 1 MINUTE - announce_delay_upper_bound = 2 MINUTES - length_lower_bound = 30 SECONDS - length_upper_bound = 1 MINUTE - var/carp_cap = 30 // No more than this many (living) carp can exist from this event. - var/carp_smallest_group = 3 - var/carp_largest_group = 5 - var/carp_wave_cooldown = 10 SECONDS - - var/last_carp_wave_time = null // Last world.time we spawned a carp wave. - -/datum/event2/event/mob_spawning/carp_migration/announce() - var/announcement = "Unknown biological entities been detected near \the [location_name()], please stand-by." - command_announcement.Announce(announcement, "Lifesign Alert") - -/datum/event2/event/mob_spawning/carp_migration/event_tick() - if(last_carp_wave_time + carp_wave_cooldown > world.time) - return - last_carp_wave_time = world.time - - if(count_spawned_mobs() < carp_cap) - spawn_mobs_in_space( - mob_type = /mob/living/simple_mob/animal/space/carp/event, - number_of_groups = rand(1, 4), - min_size_of_group = carp_smallest_group, - max_size_of_group = carp_largest_group - ) - -/datum/event2/event/mob_spawning/carp_migration/end() - // Clean up carp that died in space for some reason. - for(var/mob/living/simple_mob/SM in spawned_mobs) - if(SM.stat == DEAD) - var/turf/T = get_turf(SM) - if(istype(T, /turf/space)) - if(prob(75)) - qdel(SM) +/datum/event2/meta/carp_migration + name = "carp migration" + event_class = "carp" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaos = 30 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/mob_spawning/carp_migration + +/datum/event2/meta/carp_migration/get_weight() + return 10 + (metric.count_people_in_department(DEPARTMENT_SECURITY) * 20) + (metric.count_all_space_mobs() * 40) + + +/datum/event2/event/mob_spawning/carp_migration + announce_delay_lower_bound = 1 MINUTE + announce_delay_upper_bound = 2 MINUTES + length_lower_bound = 30 SECONDS + length_upper_bound = 1 MINUTE + var/carp_cap = 30 // No more than this many (living) carp can exist from this event. + var/carp_smallest_group = 3 + var/carp_largest_group = 5 + var/carp_wave_cooldown = 10 SECONDS + + var/last_carp_wave_time = null // Last world.time we spawned a carp wave. + +/datum/event2/event/mob_spawning/carp_migration/announce() + var/announcement = "Unknown biological entities been detected near \the [location_name()], please stand-by." + command_announcement.Announce(announcement, "Lifesign Alert") + +/datum/event2/event/mob_spawning/carp_migration/event_tick() + if(last_carp_wave_time + carp_wave_cooldown > world.time) + return + last_carp_wave_time = world.time + + if(count_spawned_mobs() < carp_cap) + spawn_mobs_in_space( + mob_type = /mob/living/simple_mob/animal/space/carp/event, + number_of_groups = rand(1, 4), + min_size_of_group = carp_smallest_group, + max_size_of_group = carp_largest_group + ) + +/datum/event2/event/mob_spawning/carp_migration/end() + // Clean up carp that died in space for some reason. + for(var/mob/living/simple_mob/SM in spawned_mobs) + if(SM.stat == DEAD) + var/turf/T = get_turf(SM) + if(istype(T, /turf/space)) + if(prob(75)) + qdel(SM) diff --git a/code/modules/gamemaster/event2/events/security/drill_announcement.dm b/code/modules/gamemaster/event2/events/security/drill_announcement.dm index 271e2d75f17..f44cf134d22 100644 --- a/code/modules/gamemaster/event2/events/security/drill_announcement.dm +++ b/code/modules/gamemaster/event2/events/security/drill_announcement.dm @@ -1,22 +1,22 @@ -/datum/event2/meta/security_drill - name = "security drill" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) - chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT // Don't run if we just got hit by meteors. - event_type = /datum/event2/event/security_drill - -/datum/event2/meta/security_drill/get_weight() - var/sec = metric.count_people_in_department(DEPARTMENT_SECURITY) - var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - - if(!sec) // If there's no security, then there is no drill. - return 0 - if(everyone - sec < 0) // If there's no non-sec, then there is no drill. - return 0 - - // Each security player adds +5 weight, while non-security adds +1.5. - return (sec * 5) + ((everyone - sec) * 1.5) - -/datum/event2/event/security_drill/announce() - command_announcement.Announce("[pick("A NanoTrasen security director", "A Vir-Gov correspondant", "Local Sif authoritiy")] \ - has advised the enactment of [pick("a rampant wildlife", "a fire", "a hostile boarding", \ - "a bomb", "an emergent intelligence")] drill with the personnel onboard \the [location_name()].", "Security Advisement") +/datum/event2/meta/security_drill + name = "security drill" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT // Don't run if we just got hit by meteors. + event_type = /datum/event2/event/security_drill + +/datum/event2/meta/security_drill/get_weight() + var/sec = metric.count_people_in_department(DEPARTMENT_SECURITY) + var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) + + if(!sec) // If there's no security, then there is no drill. + return 0 + if(everyone - sec < 0) // If there's no non-sec, then there is no drill. + return 0 + + // Each security player adds +5 weight, while non-security adds +1.5. + return (sec * 5) + ((everyone - sec) * 1.5) + +/datum/event2/event/security_drill/announce() + command_announcement.Announce("[pick("A NanoTrasen security director", "A Vir-Gov correspondant", "Local Sif authoritiy")] \ + has advised the enactment of [pick("a rampant wildlife", "a fire", "a hostile boarding", \ + "a bomb", "an emergent intelligence")] drill with the personnel onboard \the [location_name()].", "Security Advisement") diff --git a/code/modules/gamemaster/event2/events/security/prison_break.dm b/code/modules/gamemaster/event2/events/security/prison_break.dm index 80f6ad2f9a7..821afd7aba4 100644 --- a/code/modules/gamemaster/event2/events/security/prison_break.dm +++ b/code/modules/gamemaster/event2/events/security/prison_break.dm @@ -1,234 +1,234 @@ - -// Type for inheritence. -// It has a null name, so it won't be ran. -/datum/event2/meta/prison_break - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - // The weight system can check if people are in these areas. - // This isn't the same list as what the event itself will break, as the event will also - // break open areas inbetween the holding area and the public hallway, like the brig area verses - // the prison area. - var/list/relevant_areas = list() - var/list/irrelevant_areas = list() - -/datum/event2/meta/prison_break/get_weight() - // First, don't do this if nobody can fix the doors. - var/door_fixers = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + metric.count_people_in_department(DEPARTMENT_SYNTHETIC) - if(!door_fixers) - return 0 - var/list/afflicted_departments = departments.Copy() - var/afflicted_crew = 0 - - afflicted_departments -= DEPARTMENT_SYNTHETIC - for(var/D in afflicted_departments) - afflicted_crew += metric.count_people_in_department(D) - - // Don't do it if nobody is around to ""appreciate"" it. - if(!afflicted_crew) - return 0 - - var/trapped = get_odds_from_trapped_mobs() - - return 10 + (door_fixers * 20) + (afflicted_crew * 10) + trapped - -// This is overriden to have specific events trigger more often based on who is trapped in where, if applicable. -/datum/event2/meta/prison_break/proc/get_odds_from_trapped_mobs() - return 0 - -/datum/event2/meta/prison_break/proc/is_mob_in_relevant_area(mob/living/L) - var/area/A = get_area(L) - if(!A) - return FALSE - if(is_type_in_list(A, relevant_areas) && !is_type_in_list(A, irrelevant_areas)) - return TRUE - return FALSE - -/datum/event2/meta/prison_break/brig - name = "prison break - brig" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_SYNTHETIC) - event_type = /datum/event2/event/prison_break/brig - relevant_areas = list( - /area/security/prison, - /area/security/security_cell_hallway, - /area/security/security_processing, - /area/security/interrogation - ) - -/datum/event2/meta/prison_break/brig/get_odds_from_trapped_mobs() - . = 0 - for(var/mob/living/L in player_list) - if(is_mob_in_relevant_area(L)) - // Don't count them if they're in security. - if(!(L in metric.count_people_in_department(DEPARTMENT_SECURITY))) - . += 40 - - -/datum/event2/meta/prison_break/armory - name = "prison break - armory" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_SYNTHETIC) - chaos = 40 // Potentially free guns. - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/prison_break/armory - -/datum/event2/meta/prison_break/bridge - name = "prison break - bridge" - departments = list(DEPARTMENT_COMMAND, DEPARTMENT_SYNTHETIC) - chaos = 40 // Potentially free spare ID. - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/prison_break/bridge - -/datum/event2/meta/prison_break/xenobio - name = "prison break - xenobio" - departments = list(DEPARTMENT_RESEARCH, DEPARTMENT_SYNTHETIC) - chaos = 20 // This one is more likely to actually kill someone. - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/prison_break/xenobio - relevant_areas = list(/area/rnd/xenobiology) - irrelevant_areas = list( - /area/rnd/xenobiology/xenoflora, - /area/rnd/xenobiology/xenoflora_storage - ) - -/datum/event2/meta/prison_break/xenobio/get_odds_from_trapped_mobs() - . = 0 - for(var/mob/living/simple_mob/slime/xenobio/X in living_mob_list) - if(is_mob_in_relevant_area(X)) - . += 5 - - -/datum/event2/meta/prison_break/virology - name = "prison break - virology" - departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_SYNTHETIC) - event_type = /datum/event2/event/prison_break/virology - relevant_areas = list( - /area/medical/virology, - /area/medical/virologyaccess - ) - -/datum/event2/meta/prison_break/virology/get_odds_from_trapped_mobs() - . = 0 - for(var/mob/living/L in player_list) - if(is_mob_in_relevant_area(L)) - // Don't count them if they're in medical. - if(!(L in metric.count_people_in_department(DEPARTMENT_MEDICAL))) - . += 40 - - - - -/datum/event2/event/prison_break - start_delay_lower_bound = 3 MINUTES - start_delay_upper_bound = 4 MINUTES - length_lower_bound = 40 SECONDS - length_upper_bound = 1 MINUTE - var/area_display_name = null // A string used to describe the area being messed with. - var/containment_display_desc = null - var/list/areas_to_break = list() - var/list/area_types_to_break = null // Area types to include. - var/list/area_types_to_ignore = null // Area types to exclude, usually due to undesired inclusion from inheritence. - var/ignore_blast_doors = FALSE - -/datum/event2/event/prison_break/brig - area_display_name = "Brig" - containment_display_desc = "imprisonment" - area_types_to_break = list( - /area/security/prison, - /area/security/brig, - /area/security/security_cell_hallway, - /area/security/security_processing, - /area/security/interrogation - ) - -/datum/event2/event/prison_break/armory - area_display_name = "Armory" - containment_display_desc = "protection" - area_types_to_break = list( - /area/security/brig, - /area/security/warden, - /area/security/evidence_storage, - /area/security/security_equiptment_storage, - /area/security/armoury, - /area/security/tactical - ) - -/datum/event2/event/prison_break/bridge - area_display_name = "Bridge" - containment_display_desc = "isolation" - area_types_to_break = list( - /area/bridge, - /area/bridge_hallway - ) - -/datum/event2/event/prison_break/xenobio - area_display_name = "Xenobiology" - containment_display_desc = "containment" - area_types_to_break = list(/area/rnd/xenobiology) - area_types_to_ignore = list( - /area/rnd/xenobiology/xenoflora, - /area/rnd/xenobiology/xenoflora_storage - ) - -/datum/event2/event/prison_break/virology - area_display_name = "Virology" - containment_display_desc = "quarantine" - area_types_to_break = list( - /area/medical/virology, - /area/medical/virologyaccess - ) - - -/datum/event2/event/prison_break/set_up() - for(var/area/A in world) - if(is_type_in_list(A, area_types_to_break) && !is_type_in_list(A, area_types_to_ignore)) - areas_to_break += A - - if(!areas_to_break.len) - log_debug("Prison Break event failed to find any areas to break. Aborting.") - abort() - return - -/datum/event2/event/prison_break/announce() - var/my_department = "[location_name()] Firewall Subroutines" - var/message = "An unknown malicious program has been detected in the [area_display_name] \ - lighting and airlock control systems at [stationtime2text()]. Systems will be fully compromised \ - within approximately three minutes. Direct intervention is required immediately. Disabling the \ - main breaker in the APCs will protect the APC's room from being compromised." - - for(var/obj/machinery/message_server/MS in machines) - MS.send_rc_message(DEPARTMENT_ENGINEERING, my_department, "[message]
                    ", "", "", 2) - - // Nobody reads the requests consoles so lets use the radio as well. - global_announcer.autosay(message, my_department, DEPARTMENT_ENGINEERING) - - for(var/mob/living/silicon/ai/A in player_list) - to_chat(A, span("danger", "Malicious program detected in the [area_display_name] lighting and airlock control systems by [my_department]. \ - Disabling the main breaker in the APCs will protect the APC's room from being compromised.")) - - var/time_to_flicker = start_delay - 10 SECONDS - addtimer(CALLBACK(src, PROC_REF(flicker_area)), time_to_flicker) - - -/datum/event2/event/prison_break/proc/flicker_area() - for(var/area/A in areas_to_break) - var/obj/machinery/power/apc/apc = A.get_apc() - if(istype(apc) && apc.operating) //If the apc's off, it's a little hard to overload the lights. - for(var/obj/machinery/light/L in A) - L.flicker(10) - -/datum/event2/event/prison_break/start() - for(var/area/A in areas_to_break) - spawn(0) // So we don't block the ticker. - A.prison_break(TRUE, TRUE, !ignore_blast_doors) // Naming `open_blast_doors` causes mysterious runtimes. - -// There's between 40 seconds and one minute before the whole station knows. -// If there's a baddie engineer, they can choose to keep their early announcement to themselves and get a minute to exploit it. -/datum/event2/event/prison_break/end() - command_announcement.Announce("[pick("Gr3y.T1d3 virus","Malignant trojan")] was detected \ - in \the [location_name()] [area_display_name] [containment_display_desc] subroutines. Secure any compromised \ - areas immediately. AI involvement is recommended.", "[capitalize(containment_display_desc)] Alert") - - global_announcer.autosay( - "It is now safe to reactivate the APCs' main breakers inside [area_display_name].", - "[location_name()] Firewall Subroutines", - DEPARTMENT_ENGINEERING - ) + +// Type for inheritence. +// It has a null name, so it won't be ran. +/datum/event2/meta/prison_break + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + // The weight system can check if people are in these areas. + // This isn't the same list as what the event itself will break, as the event will also + // break open areas inbetween the holding area and the public hallway, like the brig area verses + // the prison area. + var/list/relevant_areas = list() + var/list/irrelevant_areas = list() + +/datum/event2/meta/prison_break/get_weight() + // First, don't do this if nobody can fix the doors. + var/door_fixers = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + metric.count_people_in_department(DEPARTMENT_SYNTHETIC) + if(!door_fixers) + return 0 + var/list/afflicted_departments = departments.Copy() + var/afflicted_crew = 0 + + afflicted_departments -= DEPARTMENT_SYNTHETIC + for(var/D in afflicted_departments) + afflicted_crew += metric.count_people_in_department(D) + + // Don't do it if nobody is around to ""appreciate"" it. + if(!afflicted_crew) + return 0 + + var/trapped = get_odds_from_trapped_mobs() + + return 10 + (door_fixers * 20) + (afflicted_crew * 10) + trapped + +// This is overriden to have specific events trigger more often based on who is trapped in where, if applicable. +/datum/event2/meta/prison_break/proc/get_odds_from_trapped_mobs() + return 0 + +/datum/event2/meta/prison_break/proc/is_mob_in_relevant_area(mob/living/L) + var/area/A = get_area(L) + if(!A) + return FALSE + if(is_type_in_list(A, relevant_areas) && !is_type_in_list(A, irrelevant_areas)) + return TRUE + return FALSE + +/datum/event2/meta/prison_break/brig + name = "prison break - brig" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_SYNTHETIC) + event_type = /datum/event2/event/prison_break/brig + relevant_areas = list( + /area/security/prison, + /area/security/security_cell_hallway, + /area/security/security_processing, + /area/security/interrogation + ) + +/datum/event2/meta/prison_break/brig/get_odds_from_trapped_mobs() + . = 0 + for(var/mob/living/L in player_list) + if(is_mob_in_relevant_area(L)) + // Don't count them if they're in security. + if(!(L in metric.count_people_in_department(DEPARTMENT_SECURITY))) + . += 40 + + +/datum/event2/meta/prison_break/armory + name = "prison break - armory" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_SYNTHETIC) + chaos = 40 // Potentially free guns. + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/prison_break/armory + +/datum/event2/meta/prison_break/bridge + name = "prison break - bridge" + departments = list(DEPARTMENT_COMMAND, DEPARTMENT_SYNTHETIC) + chaos = 40 // Potentially free spare ID. + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/prison_break/bridge + +/datum/event2/meta/prison_break/xenobio + name = "prison break - xenobio" + departments = list(DEPARTMENT_RESEARCH, DEPARTMENT_SYNTHETIC) + chaos = 20 // This one is more likely to actually kill someone. + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/prison_break/xenobio + relevant_areas = list(/area/rnd/xenobiology) + irrelevant_areas = list( + /area/rnd/xenobiology/xenoflora, + /area/rnd/xenobiology/xenoflora_storage + ) + +/datum/event2/meta/prison_break/xenobio/get_odds_from_trapped_mobs() + . = 0 + for(var/mob/living/simple_mob/slime/xenobio/X in living_mob_list) + if(is_mob_in_relevant_area(X)) + . += 5 + + +/datum/event2/meta/prison_break/virology + name = "prison break - virology" + departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_SYNTHETIC) + event_type = /datum/event2/event/prison_break/virology + relevant_areas = list( + /area/medical/virology, + /area/medical/virologyaccess + ) + +/datum/event2/meta/prison_break/virology/get_odds_from_trapped_mobs() + . = 0 + for(var/mob/living/L in player_list) + if(is_mob_in_relevant_area(L)) + // Don't count them if they're in medical. + if(!(L in metric.count_people_in_department(DEPARTMENT_MEDICAL))) + . += 40 + + + + +/datum/event2/event/prison_break + start_delay_lower_bound = 3 MINUTES + start_delay_upper_bound = 4 MINUTES + length_lower_bound = 40 SECONDS + length_upper_bound = 1 MINUTE + var/area_display_name = null // A string used to describe the area being messed with. + var/containment_display_desc = null + var/list/areas_to_break = list() + var/list/area_types_to_break = null // Area types to include. + var/list/area_types_to_ignore = null // Area types to exclude, usually due to undesired inclusion from inheritence. + var/ignore_blast_doors = FALSE + +/datum/event2/event/prison_break/brig + area_display_name = "Brig" + containment_display_desc = "imprisonment" + area_types_to_break = list( + /area/security/prison, + /area/security/brig, + /area/security/security_cell_hallway, + /area/security/security_processing, + /area/security/interrogation + ) + +/datum/event2/event/prison_break/armory + area_display_name = "Armory" + containment_display_desc = "protection" + area_types_to_break = list( + /area/security/brig, + /area/security/warden, + /area/security/evidence_storage, + /area/security/security_equiptment_storage, + /area/security/armoury, + /area/security/tactical + ) + +/datum/event2/event/prison_break/bridge + area_display_name = "Bridge" + containment_display_desc = "isolation" + area_types_to_break = list( + /area/bridge, + /area/bridge_hallway + ) + +/datum/event2/event/prison_break/xenobio + area_display_name = "Xenobiology" + containment_display_desc = "containment" + area_types_to_break = list(/area/rnd/xenobiology) + area_types_to_ignore = list( + /area/rnd/xenobiology/xenoflora, + /area/rnd/xenobiology/xenoflora_storage + ) + +/datum/event2/event/prison_break/virology + area_display_name = "Virology" + containment_display_desc = "quarantine" + area_types_to_break = list( + /area/medical/virology, + /area/medical/virologyaccess + ) + + +/datum/event2/event/prison_break/set_up() + for(var/area/A in world) + if(is_type_in_list(A, area_types_to_break) && !is_type_in_list(A, area_types_to_ignore)) + areas_to_break += A + + if(!areas_to_break.len) + log_debug("Prison Break event failed to find any areas to break. Aborting.") + abort() + return + +/datum/event2/event/prison_break/announce() + var/my_department = "[location_name()] Firewall Subroutines" + var/message = "An unknown malicious program has been detected in the [area_display_name] \ + lighting and airlock control systems at [stationtime2text()]. Systems will be fully compromised \ + within approximately three minutes. Direct intervention is required immediately. Disabling the \ + main breaker in the APCs will protect the APC's room from being compromised." + + for(var/obj/machinery/message_server/MS in machines) + MS.send_rc_message(DEPARTMENT_ENGINEERING, my_department, "[message]
                    ", "", "", 2) + + // Nobody reads the requests consoles so lets use the radio as well. + global_announcer.autosay(message, my_department, DEPARTMENT_ENGINEERING) + + for(var/mob/living/silicon/ai/A in player_list) + to_chat(A, span("danger", "Malicious program detected in the [area_display_name] lighting and airlock control systems by [my_department]. \ + Disabling the main breaker in the APCs will protect the APC's room from being compromised.")) + + var/time_to_flicker = start_delay - 10 SECONDS + addtimer(CALLBACK(src, PROC_REF(flicker_area)), time_to_flicker) + + +/datum/event2/event/prison_break/proc/flicker_area() + for(var/area/A in areas_to_break) + var/obj/machinery/power/apc/apc = A.get_apc() + if(istype(apc) && apc.operating) //If the apc's off, it's a little hard to overload the lights. + for(var/obj/machinery/light/L in A) + L.flicker(10) + +/datum/event2/event/prison_break/start() + for(var/area/A in areas_to_break) + spawn(0) // So we don't block the ticker. + A.prison_break(TRUE, TRUE, !ignore_blast_doors) // Naming `open_blast_doors` causes mysterious runtimes. + +// There's between 40 seconds and one minute before the whole station knows. +// If there's a baddie engineer, they can choose to keep their early announcement to themselves and get a minute to exploit it. +/datum/event2/event/prison_break/end() + command_announcement.Announce("[pick("Gr3y.T1d3 virus","Malignant trojan")] was detected \ + in \the [location_name()] [area_display_name] [containment_display_desc] subroutines. Secure any compromised \ + areas immediately. AI involvement is recommended.", "[capitalize(containment_display_desc)] Alert") + + global_announcer.autosay( + "It is now safe to reactivate the APCs' main breakers inside [area_display_name].", + "[location_name()] Firewall Subroutines", + DEPARTMENT_ENGINEERING + ) diff --git a/code/modules/gamemaster/event2/events/security/rogue_drones.dm b/code/modules/gamemaster/event2/events/security/rogue_drones.dm index da40b3262f6..d7402d3822a 100644 --- a/code/modules/gamemaster/event2/events/security/rogue_drones.dm +++ b/code/modules/gamemaster/event2/events/security/rogue_drones.dm @@ -1,70 +1,70 @@ -/datum/event2/meta/rogue_drones - name = "rogue drones" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) - chaos = 40 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/mob_spawning/rogue_drones - -/datum/event2/meta/rogue_drones/get_weight() - . = 10 // Start with a base weight, since this event does provide some value even if no sec is around. - . += metric.count_people_in_department(DEPARTMENT_SECURITY) * 20 - . += metric.count_all_space_mobs() * 40 - - -/datum/event2/event/mob_spawning/rogue_drones - length_lower_bound = 15 MINUTES - length_upper_bound = 20 MINUTES - var/drones_to_spawn = 6 - -/datum/event2/event/mob_spawning/rogue_drones/set_up() - if(prob(10)) // Small chance for a false alarm. - drones_to_spawn = 0 - -/datum/event2/event/mob_spawning/rogue_drones/announce() - var/msg = null - var/rng = rand(1,5) - switch(rng) - if(1) - msg = "A combat drone wing operating in close orbit above Sif has failed to return from a anti-piracy sweep. \ - If any are sighted, approach with caution." - if(2) - msg = "Contact has been lost with a combat drone wing in Sif orbit. \ - If any are sighted in the area, approach with caution." - if(3) - msg = "Unidentified hackers have targeted a combat drone wing deployed around Sif. \ - If any are sighted in the area, approach with caution." - if(4) - msg = "A passing derelict ship's drone defense systems have just activated. \ - If any are sighted in the area, use caution." - if(5) - msg = "We're detecting a swarm of small objects approaching your station. \ - Most likely a bunch of drones. Please exercise caution if you see any." - - command_announcement.Announce(msg, "Rogue drone alert") - -/datum/event2/event/mob_spawning/rogue_drones/start() - for(var/i = 1 to drones_to_spawn) - spawn_mobs_in_space( - mob_type = /mob/living/simple_mob/mechanical/combat_drone/event, - number_of_groups = 1, - min_size_of_group = 1, - max_size_of_group = 1 - ) - -/datum/event2/event/mob_spawning/rogue_drones/end() - if(drones_to_spawn) - var/number_recovered = 0 - for(var/mob/living/simple_mob/mechanical/combat_drone/D in spawned_mobs) - var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread() - sparks.set_up(3, 0, D.loc) - sparks.start() - D.z = using_map.admin_levels[1] - D.loot_list = list() - - qdel(D) - number_recovered++ - - if(number_recovered > spawned_mobs.len * 0.75) - command_announcement.Announce("The drones that were malfunctioning have been recovered safely.", "Rogue drone alert") - else - command_announcement.Announce("We're disappointed at the loss of the drones, but the survivors have been recovered.", "Rogue drone alert") +/datum/event2/meta/rogue_drones + name = "rogue drones" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaos = 40 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/mob_spawning/rogue_drones + +/datum/event2/meta/rogue_drones/get_weight() + . = 10 // Start with a base weight, since this event does provide some value even if no sec is around. + . += metric.count_people_in_department(DEPARTMENT_SECURITY) * 20 + . += metric.count_all_space_mobs() * 40 + + +/datum/event2/event/mob_spawning/rogue_drones + length_lower_bound = 15 MINUTES + length_upper_bound = 20 MINUTES + var/drones_to_spawn = 6 + +/datum/event2/event/mob_spawning/rogue_drones/set_up() + if(prob(10)) // Small chance for a false alarm. + drones_to_spawn = 0 + +/datum/event2/event/mob_spawning/rogue_drones/announce() + var/msg = null + var/rng = rand(1,5) + switch(rng) + if(1) + msg = "A combat drone wing operating in close orbit above Sif has failed to return from a anti-piracy sweep. \ + If any are sighted, approach with caution." + if(2) + msg = "Contact has been lost with a combat drone wing in Sif orbit. \ + If any are sighted in the area, approach with caution." + if(3) + msg = "Unidentified hackers have targeted a combat drone wing deployed around Sif. \ + If any are sighted in the area, approach with caution." + if(4) + msg = "A passing derelict ship's drone defense systems have just activated. \ + If any are sighted in the area, use caution." + if(5) + msg = "We're detecting a swarm of small objects approaching your station. \ + Most likely a bunch of drones. Please exercise caution if you see any." + + command_announcement.Announce(msg, "Rogue drone alert") + +/datum/event2/event/mob_spawning/rogue_drones/start() + for(var/i = 1 to drones_to_spawn) + spawn_mobs_in_space( + mob_type = /mob/living/simple_mob/mechanical/combat_drone/event, + number_of_groups = 1, + min_size_of_group = 1, + max_size_of_group = 1 + ) + +/datum/event2/event/mob_spawning/rogue_drones/end() + if(drones_to_spawn) + var/number_recovered = 0 + for(var/mob/living/simple_mob/mechanical/combat_drone/D in spawned_mobs) + var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread() + sparks.set_up(3, 0, D.loc) + sparks.start() + D.z = using_map.admin_levels[1] + D.loot_list = list() + + qdel(D) + number_recovered++ + + if(number_recovered > spawned_mobs.len * 0.75) + command_announcement.Announce("The drones that were malfunctioning have been recovered safely.", "Rogue drone alert") + else + command_announcement.Announce("We're disappointed at the loss of the drones, but the survivors have been recovered.", "Rogue drone alert") diff --git a/code/modules/gamemaster/event2/events/security/security_advisement.dm b/code/modules/gamemaster/event2/events/security/security_advisement.dm index 6a3764e40e6..009d64c0ffa 100644 --- a/code/modules/gamemaster/event2/events/security/security_advisement.dm +++ b/code/modules/gamemaster/event2/events/security/security_advisement.dm @@ -1,93 +1,93 @@ -/datum/event2/meta/security_screening - name = "security screening" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) - chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT // So this won't get called in the middle of a crisis. - event_type = /datum/event2/event/security_screening - -/datum/event2/meta/security_screening/get_weight() - . = 0 - var/sec = metric.count_people_in_department(DEPARTMENT_SECURITY) - if(!sec < 2) - return 0 // Can't screen with no security. - . += sec * 10 - . += metric.count_people_in_department(DEPARTMENT_EVERYONE) * 2 - - // Having ""suspecious"" people present makes this more likely to be picked. - var/suspicious_people = 0 - suspicious_people += metric.count_all_of_specific_species(SPECIES_PROMETHEAN) * 20 - suspicious_people += metric.count_all_of_specific_species(SPECIES_UNATHI) * 10 - suspicious_people += metric.count_all_of_specific_species(SPECIES_ZADDAT) * 10 - suspicious_people += metric.count_all_of_specific_species(SPECIES_SKRELL) * 5 // Not sure why skrell are so high. - suspicious_people += metric.count_all_of_specific_species(SPECIES_TAJ) * 5 - suspicious_people += metric.count_all_of_specific_species(SPECIES_TESHARI) * 5 - suspicious_people += metric.count_all_of_specific_species(SPECIES_HUMAN_VATBORN) * 5 - suspicious_people += metric.count_all_FBPs_of_kind(FBP_DRONE) * 20 - suspicious_people += metric.count_all_FBPs_of_kind(FBP_POSI) * 10 - if(!suspicious_people) - return 0 - . += suspicious_people - -/datum/event2/event/security_screening - var/victim = null - var/list/species_weights = list( - SPECIES_SKRELL = 9, - SPECIES_UNATHI = 15, - SPECIES_HUMAN_VATBORN = 6, - SPECIES_TESHARI = 2, - SPECIES_TAJ = 3, - SPECIES_DIONA = 1, - SPECIES_ZADDAT = 25, - SPECIES_PROMETHEAN = 30 - ) - - var/list/synth_weights = list( - FBP_CYBORG = 15, - FBP_DRONE = 30, - FBP_POSI = 25 - ) - -/datum/event2/event/security_screening/set_up() - var/list/end_weights = list() - - // First pass makes popular things more likely to get picked, e.g. 5 prommies vs 1 drone. - for(var/species_name in species_weights) - var/give_weight = 0 - for(var/datum/data/record/R in data_core.general) - if(R.fields["species"] == species_name) - give_weight += species_weights[species_name] - - end_weights[species_name] = give_weight - - for(var/bot_type in synth_weights) - var/give_weight = 0 - for(var/datum/data/record/R in data_core.general) - if(R.fields["brain_type"] == bot_type) - give_weight += synth_weights[bot_type] - - end_weights[bot_type] = give_weight - - // Second pass eliminates things that don't exist on the station. - // It's possible to choose something like drones when all the drones are AFK. This prevents that from happening. - while(end_weights.len) // Keep at it until we find someone or run out of possibilities. - var/victim_chosen = pickweight(end_weights) - - if(victim_chosen in synth_weights) - if(metric.count_all_FBPs_of_kind(victim_chosen) > 0) - victim = victim_chosen - break - else - if(metric.count_all_of_specific_species(victim_chosen) > 0) - victim = victim_chosen - break - if(!victim) - end_weights -= victim_chosen - - if(!victim) - log_debug("Security Screening event failed to find anyone to screen. Aborting.") - abort() - return - -/datum/event2/event/security_screening/announce() - command_announcement.Announce("[pick("A nearby Navy vessel", "A Solar official", "A Vir-Gov official", "A NanoTrasen board director")] has \ - requested the screening of [pick("every other", "every", "suspicious", "willing")] [victim] \ - personnel onboard \the [location_name()].", "Security Advisement") +/datum/event2/meta/security_screening + name = "security screening" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT // So this won't get called in the middle of a crisis. + event_type = /datum/event2/event/security_screening + +/datum/event2/meta/security_screening/get_weight() + . = 0 + var/sec = metric.count_people_in_department(DEPARTMENT_SECURITY) + if(!sec < 2) + return 0 // Can't screen with no security. + . += sec * 10 + . += metric.count_people_in_department(DEPARTMENT_EVERYONE) * 2 + + // Having ""suspecious"" people present makes this more likely to be picked. + var/suspicious_people = 0 + suspicious_people += metric.count_all_of_specific_species(SPECIES_PROMETHEAN) * 20 + suspicious_people += metric.count_all_of_specific_species(SPECIES_UNATHI) * 10 + suspicious_people += metric.count_all_of_specific_species(SPECIES_ZADDAT) * 10 + suspicious_people += metric.count_all_of_specific_species(SPECIES_SKRELL) * 5 // Not sure why skrell are so high. + suspicious_people += metric.count_all_of_specific_species(SPECIES_TAJ) * 5 + suspicious_people += metric.count_all_of_specific_species(SPECIES_TESHARI) * 5 + suspicious_people += metric.count_all_of_specific_species(SPECIES_HUMAN_VATBORN) * 5 + suspicious_people += metric.count_all_FBPs_of_kind(FBP_DRONE) * 20 + suspicious_people += metric.count_all_FBPs_of_kind(FBP_POSI) * 10 + if(!suspicious_people) + return 0 + . += suspicious_people + +/datum/event2/event/security_screening + var/victim = null + var/list/species_weights = list( + SPECIES_SKRELL = 9, + SPECIES_UNATHI = 15, + SPECIES_HUMAN_VATBORN = 6, + SPECIES_TESHARI = 2, + SPECIES_TAJ = 3, + SPECIES_DIONA = 1, + SPECIES_ZADDAT = 25, + SPECIES_PROMETHEAN = 30 + ) + + var/list/synth_weights = list( + FBP_CYBORG = 15, + FBP_DRONE = 30, + FBP_POSI = 25 + ) + +/datum/event2/event/security_screening/set_up() + var/list/end_weights = list() + + // First pass makes popular things more likely to get picked, e.g. 5 prommies vs 1 drone. + for(var/species_name in species_weights) + var/give_weight = 0 + for(var/datum/data/record/R in data_core.general) + if(R.fields["species"] == species_name) + give_weight += species_weights[species_name] + + end_weights[species_name] = give_weight + + for(var/bot_type in synth_weights) + var/give_weight = 0 + for(var/datum/data/record/R in data_core.general) + if(R.fields["brain_type"] == bot_type) + give_weight += synth_weights[bot_type] + + end_weights[bot_type] = give_weight + + // Second pass eliminates things that don't exist on the station. + // It's possible to choose something like drones when all the drones are AFK. This prevents that from happening. + while(end_weights.len) // Keep at it until we find someone or run out of possibilities. + var/victim_chosen = pickweight(end_weights) + + if(victim_chosen in synth_weights) + if(metric.count_all_FBPs_of_kind(victim_chosen) > 0) + victim = victim_chosen + break + else + if(metric.count_all_of_specific_species(victim_chosen) > 0) + victim = victim_chosen + break + if(!victim) + end_weights -= victim_chosen + + if(!victim) + log_debug("Security Screening event failed to find anyone to screen. Aborting.") + abort() + return + +/datum/event2/event/security_screening/announce() + command_announcement.Announce("[pick("A nearby Navy vessel", "A Solar official", "A Vir-Gov official", "A NanoTrasen board director")] has \ + requested the screening of [pick("every other", "every", "suspicious", "willing")] [victim] \ + personnel onboard \the [location_name()].", "Security Advisement") diff --git a/code/modules/gamemaster/event2/events/security/spider_infestation.dm b/code/modules/gamemaster/event2/events/security/spider_infestation.dm index 244db93dd33..811a9d483a9 100644 --- a/code/modules/gamemaster/event2/events/security/spider_infestation.dm +++ b/code/modules/gamemaster/event2/events/security/spider_infestation.dm @@ -1,49 +1,49 @@ -/datum/event2/meta/spider_infestation - name = "spider infestation" - event_class = "spiders" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_MEDICAL, DEPARTMENT_EVERYONE) - chaos = 30 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/spider_infestation - -/datum/event2/meta/spider_infestation/weak - name = "weak spider infestation" - chaos = 20 - event_type = /datum/event2/event/spider_infestation/weak - - -/datum/event2/meta/spider_infestation/get_weight() - . = 10 - . += metric.count_people_in_department(DEPARTMENT_SECURITY) * 20 - . += metric.count_people_in_department(DEPARTMENT_MEDICAL) * 10 - - -// This isn't a /mob_spawning subtype since spiderlings aren't actually mobs. - -/datum/event2/event/spider_infestation - var/spiders_to_spawn = 8 - var/spiderling_to_spawn = /obj/effect/spider/spiderling - -/datum/event2/event/spider_infestation/weak - spiders_to_spawn = 5 - spiderling_to_spawn = /obj/effect/spider/spiderling/stunted - - - -/datum/event2/event/spider_infestation/announce() - command_announcement.Announce("Unidentified lifesigns detected coming aboard \the [location_name()]. \ - Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') - -/datum/event2/event/spider_infestation/start() - var/list/vents = list() - for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) - if(!temp_vent.welded && temp_vent.network && (temp_vent.loc.z in get_location_z_levels())) - if(temp_vent.network.normal_members.len > 50) - vents += temp_vent - - while((spiders_to_spawn >= 1) && vents.len) - var/obj/vent = pick(vents) - new spiderling_to_spawn(vent.loc) - log_debug("Spider infestation event spawned a spiderling at [get_area(vent)].") - vents -= vent - spiders_to_spawn-- +/datum/event2/meta/spider_infestation + name = "spider infestation" + event_class = "spiders" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_MEDICAL, DEPARTMENT_EVERYONE) + chaos = 30 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/spider_infestation + +/datum/event2/meta/spider_infestation/weak + name = "weak spider infestation" + chaos = 20 + event_type = /datum/event2/event/spider_infestation/weak + + +/datum/event2/meta/spider_infestation/get_weight() + . = 10 + . += metric.count_people_in_department(DEPARTMENT_SECURITY) * 20 + . += metric.count_people_in_department(DEPARTMENT_MEDICAL) * 10 + + +// This isn't a /mob_spawning subtype since spiderlings aren't actually mobs. + +/datum/event2/event/spider_infestation + var/spiders_to_spawn = 8 + var/spiderling_to_spawn = /obj/effect/spider/spiderling + +/datum/event2/event/spider_infestation/weak + spiders_to_spawn = 5 + spiderling_to_spawn = /obj/effect/spider/spiderling/stunted + + + +/datum/event2/event/spider_infestation/announce() + command_announcement.Announce("Unidentified lifesigns detected coming aboard \the [location_name()]. \ + Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') + +/datum/event2/event/spider_infestation/start() + var/list/vents = list() + for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) + if(!temp_vent.welded && temp_vent.network && (temp_vent.loc.z in get_location_z_levels())) + if(temp_vent.network.normal_members.len > 50) + vents += temp_vent + + while((spiders_to_spawn >= 1) && vents.len) + var/obj/vent = pick(vents) + new spiderling_to_spawn(vent.loc) + log_debug("Spider infestation event spawned a spiderling at [get_area(vent)].") + vents -= vent + spiders_to_spawn-- diff --git a/code/modules/gamemaster/event2/events/security/stowaway.dm b/code/modules/gamemaster/event2/events/security/stowaway.dm index d04d350234a..e4e41c30911 100644 --- a/code/modules/gamemaster/event2/events/security/stowaway.dm +++ b/code/modules/gamemaster/event2/events/security/stowaway.dm @@ -1,64 +1,64 @@ -// Base type used for inheritence. -/datum/event2/meta/stowaway - event_class = "stowaway" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - event_type = /datum/event2/event/ghost_pod_spawner/stowaway - var/safe_for_extended = FALSE - -/datum/event2/meta/stowaway/normal - name = "stowaway - normal" - safe_for_extended = TRUE - -/datum/event2/meta/stowaway/renegade - name = "stowaway - renegade" - chaos = 30 - event_type = /datum/event2/event/ghost_pod_spawner/stowaway/renegade - -/datum/event2/meta/stowaway/infiltrator - name = "stowaway - infiltrator" - chaos = 60 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/ghost_pod_spawner/stowaway/infiltrator - -/datum/event2/meta/stowaway/get_weight() - if(istype(ticker.mode, /datum/game_mode/extended) && !safe_for_extended) - return 0 - - var/security = metric.count_people_in_department(DEPARTMENT_SECURITY) - var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - security - var/ghost_activity = metric.assess_all_dead_mobs() / 100 - - return ( (security * 20) + (everyone * 2) ) * ghost_activity - - -/datum/event2/event/ghost_pod_spawner/stowaway - pod_type = /obj/structure/ghost_pod/ghost_activated/human - desired_turf_areas = list(/area/maintenance) - announce_delay_lower_bound = 15 MINUTES - announce_delay_upper_bound = 30 MINUTES - var/antag_type = MODE_STOWAWAY - var/announce_odds = 20 - -/datum/event2/event/ghost_pod_spawner/stowaway/renegade - antag_type = MODE_RENEGADE - announce_odds = 33 - -/datum/event2/event/ghost_pod_spawner/stowaway/infiltrator - antag_type = MODE_INFILTRATOR - announce_odds = 50 - -/datum/event2/event/ghost_pod_spawner/stowaway/post_pod_creation(obj/structure/ghost_pod/ghost_activated/human/pod) - pod.make_antag = antag_type - pod.occupant_type = "[pod.make_antag] [pod.occupant_type]" - - say_dead_object("[span("notice", pod.occupant_type)] pod is now available in \the [get_area(pod)].", pod) - -/datum/event2/event/ghost_pod_spawner/stowaway/announce() - if(prob(announce_odds)) - if(atc?.squelched) - return - atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution is advised as \ - [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] \ - has been detected passing multiple local stations.") +// Base type used for inheritence. +/datum/event2/meta/stowaway + event_class = "stowaway" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/ghost_pod_spawner/stowaway + var/safe_for_extended = FALSE + +/datum/event2/meta/stowaway/normal + name = "stowaway - normal" + safe_for_extended = TRUE + +/datum/event2/meta/stowaway/renegade + name = "stowaway - renegade" + chaos = 30 + event_type = /datum/event2/event/ghost_pod_spawner/stowaway/renegade + +/datum/event2/meta/stowaway/infiltrator + name = "stowaway - infiltrator" + chaos = 60 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/ghost_pod_spawner/stowaway/infiltrator + +/datum/event2/meta/stowaway/get_weight() + if(istype(ticker.mode, /datum/game_mode/extended) && !safe_for_extended) + return 0 + + var/security = metric.count_people_in_department(DEPARTMENT_SECURITY) + var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - security + var/ghost_activity = metric.assess_all_dead_mobs() / 100 + + return ( (security * 20) + (everyone * 2) ) * ghost_activity + + +/datum/event2/event/ghost_pod_spawner/stowaway + pod_type = /obj/structure/ghost_pod/ghost_activated/human + desired_turf_areas = list(/area/maintenance) + announce_delay_lower_bound = 15 MINUTES + announce_delay_upper_bound = 30 MINUTES + var/antag_type = MODE_STOWAWAY + var/announce_odds = 20 + +/datum/event2/event/ghost_pod_spawner/stowaway/renegade + antag_type = MODE_RENEGADE + announce_odds = 33 + +/datum/event2/event/ghost_pod_spawner/stowaway/infiltrator + antag_type = MODE_INFILTRATOR + announce_odds = 50 + +/datum/event2/event/ghost_pod_spawner/stowaway/post_pod_creation(obj/structure/ghost_pod/ghost_activated/human/pod) + pod.make_antag = antag_type + pod.occupant_type = "[pod.make_antag] [pod.occupant_type]" + + say_dead_object("[span("notice", pod.occupant_type)] pod is now available in \the [get_area(pod)].", pod) + +/datum/event2/event/ghost_pod_spawner/stowaway/announce() + if(prob(announce_odds)) + if(atc?.squelched) + return + atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution is advised as \ + [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] \ + has been detected passing multiple local stations.") diff --git a/code/modules/gamemaster/event2/events/security/surprise_carp.dm b/code/modules/gamemaster/event2/events/security/surprise_carp.dm index 39be6d8412d..534e91e2ce1 100644 --- a/code/modules/gamemaster/event2/events/security/surprise_carp.dm +++ b/code/modules/gamemaster/event2/events/security/surprise_carp.dm @@ -1,71 +1,71 @@ -// This event sends a few carp after someone hanging around in space, unannounced. - -/datum/event2/meta/surprise_carp - name = "surprise carp" - departments = list(DEPARTMENT_EVERYONE) - chaos = 20 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/surprise_carp - -/datum/event2/meta/surprise_carp/get_weight() - return metric.count_all_space_mobs() * 50 - - -/datum/event2/event/surprise_carp - var/mob/living/victim = null - -/datum/event2/event/surprise_carp/set_up() - var/list/potential_victims = list() - for(var/mob/living/L in player_list) - if(!(L.z in get_location_z_levels())) - continue // Not on the right z-level. - if(L.stat) - continue // Don't want dead people. - if(istype(get_turf(L), /turf/space) && istype(get_area(L),/area/space)) - potential_victims += L - - if(potential_victims.len) - victim = pick(potential_victims) - -/datum/event2/event/surprise_carp/start() - if(!victim) - log_debug("Failed to find a target for surprise carp attack. Aborting.") - abort() - return - - var/number_of_carp = rand(1, 2) - log_debug("Sending [number_of_carp] carp\s after \the [victim].") - // Getting off screen tiles is kind of tricky due to potential edge cases that could arise. - // The method we're gonna do is make a big square around the victim, then - // subtract a smaller square in the middle for the default vision range. - var/list/outer_square = get_safe_square(victim, world.view + 3) - var/list/inner_square = get_safe_square(victim, world.view) - - var/list/donut = outer_square - inner_square - for(var/T in donut) - if(!istype(T, /turf/space)) - donut -= T - - for(var/i = 1 to number_of_carp) - var/turf/spawning_turf = pick(donut) - - if(spawning_turf) - var/mob/living/simple_mob/animal/space/carp/C = new(spawning_turf) - // Ask carp to swim onto the victim's screen. The AI will then switch to hostile and try to eat them. - C.ai_holder?.give_destination(get_turf(victim)) - else - log_debug("Surprise carp attack failed to find any space turfs offscreen to the victim.") - -// Gets suitable spots for carp to spawn, without risk of going off the edge of the map. -// If there is demand for this proc, then it can easily be made independant and moved into one of the helper files. -/datum/event2/event/surprise_carp/proc/get_safe_square(atom/center, radius) - var/lower_left_x = max(center.x - radius, 1 + TRANSITIONEDGE) - var/lower_left_y = max(center.y - radius, 1 + TRANSITIONEDGE) - - var/upper_right_x = min(center.x + radius, world.maxx - TRANSITIONEDGE) - var/upper_right_y = min(center.y + radius, world.maxy - TRANSITIONEDGE) - - var/turf/lower_left = locate(lower_left_x, lower_left_y, victim.z) - var/turf/upper_right = locate(upper_right_x, upper_right_y, victim.z) - - return block(lower_left, upper_right) +// This event sends a few carp after someone hanging around in space, unannounced. + +/datum/event2/meta/surprise_carp + name = "surprise carp" + departments = list(DEPARTMENT_EVERYONE) + chaos = 20 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/surprise_carp + +/datum/event2/meta/surprise_carp/get_weight() + return metric.count_all_space_mobs() * 50 + + +/datum/event2/event/surprise_carp + var/mob/living/victim = null + +/datum/event2/event/surprise_carp/set_up() + var/list/potential_victims = list() + for(var/mob/living/L in player_list) + if(!(L.z in get_location_z_levels())) + continue // Not on the right z-level. + if(L.stat) + continue // Don't want dead people. + if(istype(get_turf(L), /turf/space) && istype(get_area(L),/area/space)) + potential_victims += L + + if(potential_victims.len) + victim = pick(potential_victims) + +/datum/event2/event/surprise_carp/start() + if(!victim) + log_debug("Failed to find a target for surprise carp attack. Aborting.") + abort() + return + + var/number_of_carp = rand(1, 2) + log_debug("Sending [number_of_carp] carp\s after \the [victim].") + // Getting off screen tiles is kind of tricky due to potential edge cases that could arise. + // The method we're gonna do is make a big square around the victim, then + // subtract a smaller square in the middle for the default vision range. + var/list/outer_square = get_safe_square(victim, world.view + 3) + var/list/inner_square = get_safe_square(victim, world.view) + + var/list/donut = outer_square - inner_square + for(var/T in donut) + if(!istype(T, /turf/space)) + donut -= T + + for(var/i = 1 to number_of_carp) + var/turf/spawning_turf = pick(donut) + + if(spawning_turf) + var/mob/living/simple_mob/animal/space/carp/C = new(spawning_turf) + // Ask carp to swim onto the victim's screen. The AI will then switch to hostile and try to eat them. + C.ai_holder?.give_destination(get_turf(victim)) + else + log_debug("Surprise carp attack failed to find any space turfs offscreen to the victim.") + +// Gets suitable spots for carp to spawn, without risk of going off the edge of the map. +// If there is demand for this proc, then it can easily be made independant and moved into one of the helper files. +/datum/event2/event/surprise_carp/proc/get_safe_square(atom/center, radius) + var/lower_left_x = max(center.x - radius, 1 + TRANSITIONEDGE) + var/lower_left_y = max(center.y - radius, 1 + TRANSITIONEDGE) + + var/upper_right_x = min(center.x + radius, world.maxx - TRANSITIONEDGE) + var/upper_right_y = min(center.y + radius, world.maxy - TRANSITIONEDGE) + + var/turf/lower_left = locate(lower_left_x, lower_left_y, victim.z) + var/turf/upper_right = locate(upper_right_x, upper_right_y, victim.z) + + return block(lower_left, upper_right) diff --git a/code/modules/gamemaster/event2/events/security/swarm_boarder.dm b/code/modules/gamemaster/event2/events/security/swarm_boarder.dm index 54dc03189b6..ca7d0c25dbc 100644 --- a/code/modules/gamemaster/event2/events/security/swarm_boarder.dm +++ b/code/modules/gamemaster/event2/events/security/swarm_boarder.dm @@ -1,56 +1,56 @@ -// This is just porting the event to the new new event system, it's not been balanced in any way -// so don't @ me if these things are grossly OP. -/datum/event2/meta/swarm_boarder - event_class = "swarm boarder" - departments = list(DEPARTMENT_EVERYONE, DEPARTMENT_SECURITY, DEPARTMENT_ENGINEERING) - chaos = 60 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT - enabled = FALSE // Turns out they are in fact grossly OP. - var/safe_for_extended = FALSE - -/datum/event2/meta/swarm_boarder/get_weight() - if(istype(ticker.mode, /datum/game_mode/extended) && !safe_for_extended) - return 0 - - var/security = metric.count_people_in_department(DEPARTMENT_SECURITY) - var/engineering = metric.count_people_in_department(DEPARTMENT_ENGINEERING) - var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - (engineering + security) - - var/ghost_activity = metric.assess_all_dead_mobs() / 100 - - return ( (security * 20) + (engineering * 10) + (everyone * 2) ) * ghost_activity - -/datum/event2/meta/swarm_boarder/normal - name = "swarmer shell - normal" - event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder - -/datum/event2/meta/swarm_boarder/melee - name = "swarmer shell - melee" - event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder/melee - -/datum/event2/meta/swarm_boarder/gunner - name = "swarmer shell - gunner" - event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder/gunner - - - - -/datum/event2/event/ghost_pod_spawner/swarm_boarder - announce_delay_lower_bound = 5 MINUTES - announce_delay_upper_bound = 15 MINUTES - pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event - desired_turf_areas = list(/area/maintenance) - var/announce_odds = 80 - -/datum/event2/event/ghost_pod_spawner/swarm_boarder/melee - pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/melee - -/datum/event2/event/ghost_pod_spawner/swarm_boarder/gunner - pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/gunner - -/datum/event2/event/ghost_pod_spawner/swarm_boarder/announce() - if(prob(announce_odds)) - if(atc?.squelched) - atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution \ - is advised as [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] \ - has been detected passing multiple local stations.") +// This is just porting the event to the new new event system, it's not been balanced in any way +// so don't @ me if these things are grossly OP. +/datum/event2/meta/swarm_boarder + event_class = "swarm boarder" + departments = list(DEPARTMENT_EVERYONE, DEPARTMENT_SECURITY, DEPARTMENT_ENGINEERING) + chaos = 60 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT + enabled = FALSE // Turns out they are in fact grossly OP. + var/safe_for_extended = FALSE + +/datum/event2/meta/swarm_boarder/get_weight() + if(istype(ticker.mode, /datum/game_mode/extended) && !safe_for_extended) + return 0 + + var/security = metric.count_people_in_department(DEPARTMENT_SECURITY) + var/engineering = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - (engineering + security) + + var/ghost_activity = metric.assess_all_dead_mobs() / 100 + + return ( (security * 20) + (engineering * 10) + (everyone * 2) ) * ghost_activity + +/datum/event2/meta/swarm_boarder/normal + name = "swarmer shell - normal" + event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder + +/datum/event2/meta/swarm_boarder/melee + name = "swarmer shell - melee" + event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder/melee + +/datum/event2/meta/swarm_boarder/gunner + name = "swarmer shell - gunner" + event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder/gunner + + + + +/datum/event2/event/ghost_pod_spawner/swarm_boarder + announce_delay_lower_bound = 5 MINUTES + announce_delay_upper_bound = 15 MINUTES + pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event + desired_turf_areas = list(/area/maintenance) + var/announce_odds = 80 + +/datum/event2/event/ghost_pod_spawner/swarm_boarder/melee + pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/melee + +/datum/event2/event/ghost_pod_spawner/swarm_boarder/gunner + pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/gunner + +/datum/event2/event/ghost_pod_spawner/swarm_boarder/announce() + if(prob(announce_odds)) + if(atc?.squelched) + atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution \ + is advised as [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] \ + has been detected passing multiple local stations.") diff --git a/code/modules/gamemaster/event2/events/synthetic/ion_storm.dm b/code/modules/gamemaster/event2/events/synthetic/ion_storm.dm index e4c89aa6914..a11bfb5a69e 100644 --- a/code/modules/gamemaster/event2/events/synthetic/ion_storm.dm +++ b/code/modules/gamemaster/event2/events/synthetic/ion_storm.dm @@ -1,74 +1,74 @@ -/datum/event2/meta/ion_storm - name = "ion storm" - departments = list(DEPARTMENT_SYNTHETIC) - chaos = 40 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/ion_storm - -/datum/event2/meta/ion_storm/get_weight() - var/list/bots = metric.get_people_in_department(DEPARTMENT_SYNTHETIC) - . = 5 // A small chance even if no synths are on, since it can still emag beepsky. - for(var/mob/living/silicon/S in bots) - if(istype(S, /mob/living/silicon/robot/drone)) // Drones don't get their laws screwed with, so don't count them. - continue - . += 40 - - -/datum/event2/event/ion_storm - announce_delay_lower_bound = 7 MINUTES - announce_delay_upper_bound = 15 MINUTES - var/bot_emag_chance = 30 // This is rolled once, instead of once a second for a minute like the old version. - var/announce_odds = 50 // Probability of an announcement actually happening after the delay. - -/datum/event2/event/ion_storm/start() - // Ion laws. - for(var/mob/living/silicon/target in silicon_mob_list) - if(target.z in get_location_z_levels()) - // Don't ion law drons. - if(istype(target, /mob/living/silicon/robot/drone)) - continue - - // Or borgs with an AI (they'll get their AI's ion law anyways). - if(istype(target, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = target - if(R.connected_ai) - continue - if(R.shell) - continue - - // Crew member names, and excluding off station antags, are handled by `generate_ion_law()` automatically. - var/law = target.generate_ion_law() - target.add_ion_law(law) - target.show_laws() - - // Emag bots. - for(var/mob/living/bot/B in mob_list) - if(B.z in get_location_z_levels()) - if(prob(bot_emag_chance)) - B.emag_act(1) - - // Messaging server spam filters. - // This might be better served as a seperate event since it seems more like a hacker attack than a natural occurance. - if(message_servers) - for(var/obj/machinery/message_server/MS in message_servers) - if(MS.z in get_location_z_levels()) - MS.spamfilter.Cut() - for (var/i = 1, i <= MS.spamfilter_limit, i++) - MS.spamfilter += pick("warble","help","almach","ai","liberty","freedom","drugs", "[using_map.station_short]", \ - "admin","sol","security","meow","_","monkey","-","moron","pizza","message","spam",\ - "director", "Hello", "Hi!"," ","nuke","crate","taj","xeno") - -/datum/event2/event/ion_storm/announce() - if(prob(announce_odds)) - command_announcement.Announce("An ion storm was detected within proximity to \the [location_name()] recently. \ - Check all AI controlled equipment for corruption.", "Anomaly Alert", new_sound = 'sound/AI/ionstorm.ogg') - -// Fake variant used by traitors. -/datum/event2/event/ion_storm/fake - // Fake ion storms announce instantly, so the traitor can time it to make the AI look suspicious. - announce_delay_lower_bound = 0 - announce_delay_upper_bound = 0 - announce_odds = 100 - -/datum/event2/event/ion_storm/fake/start() +/datum/event2/meta/ion_storm + name = "ion storm" + departments = list(DEPARTMENT_SYNTHETIC) + chaos = 40 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/ion_storm + +/datum/event2/meta/ion_storm/get_weight() + var/list/bots = metric.get_people_in_department(DEPARTMENT_SYNTHETIC) + . = 5 // A small chance even if no synths are on, since it can still emag beepsky. + for(var/mob/living/silicon/S in bots) + if(istype(S, /mob/living/silicon/robot/drone)) // Drones don't get their laws screwed with, so don't count them. + continue + . += 40 + + +/datum/event2/event/ion_storm + announce_delay_lower_bound = 7 MINUTES + announce_delay_upper_bound = 15 MINUTES + var/bot_emag_chance = 30 // This is rolled once, instead of once a second for a minute like the old version. + var/announce_odds = 50 // Probability of an announcement actually happening after the delay. + +/datum/event2/event/ion_storm/start() + // Ion laws. + for(var/mob/living/silicon/target in silicon_mob_list) + if(target.z in get_location_z_levels()) + // Don't ion law drons. + if(istype(target, /mob/living/silicon/robot/drone)) + continue + + // Or borgs with an AI (they'll get their AI's ion law anyways). + if(istype(target, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = target + if(R.connected_ai) + continue + if(R.shell) + continue + + // Crew member names, and excluding off station antags, are handled by `generate_ion_law()` automatically. + var/law = target.generate_ion_law() + target.add_ion_law(law) + target.show_laws() + + // Emag bots. + for(var/mob/living/bot/B in mob_list) + if(B.z in get_location_z_levels()) + if(prob(bot_emag_chance)) + B.emag_act(1) + + // Messaging server spam filters. + // This might be better served as a seperate event since it seems more like a hacker attack than a natural occurance. + if(message_servers) + for(var/obj/machinery/message_server/MS in message_servers) + if(MS.z in get_location_z_levels()) + MS.spamfilter.Cut() + for (var/i = 1, i <= MS.spamfilter_limit, i++) + MS.spamfilter += pick("warble","help","almach","ai","liberty","freedom","drugs", "[using_map.station_short]", \ + "admin","sol","security","meow","_","monkey","-","moron","pizza","message","spam",\ + "director", "Hello", "Hi!"," ","nuke","crate","taj","xeno") + +/datum/event2/event/ion_storm/announce() + if(prob(announce_odds)) + command_announcement.Announce("An ion storm was detected within proximity to \the [location_name()] recently. \ + Check all AI controlled equipment for corruption.", "Anomaly Alert", new_sound = 'sound/AI/ionstorm.ogg') + +// Fake variant used by traitors. +/datum/event2/event/ion_storm/fake + // Fake ion storms announce instantly, so the traitor can time it to make the AI look suspicious. + announce_delay_lower_bound = 0 + announce_delay_upper_bound = 0 + announce_odds = 100 + +/datum/event2/event/ion_storm/fake/start() return \ No newline at end of file diff --git a/code/modules/gamemaster/event2/meta.dm b/code/modules/gamemaster/event2/meta.dm index 3923f857444..74db2a2b6cc 100644 --- a/code/modules/gamemaster/event2/meta.dm +++ b/code/modules/gamemaster/event2/meta.dm @@ -1,81 +1,81 @@ -// The 'meta' object contains information about its assigned 'action' object, like what departments it will affect. -// It is directly held inside the Game Master Event System. - -// The code for actually executing an event should go inside the event object instead. -/datum/event2/meta - // Name used for organization, shown in the debug verb for the GM system. - // If null, the meta event will be discarded when the GM system initializes, so it is safe to use nameless subtypes for inheritence. - var/name = null - - // If FALSE, the GM system won't pick this. - // Some events set this to FALSE after running, to avoid running twice. - var/enabled = TRUE - - // What departments the event attached might affect. - var/list/departments = list(DEPARTMENT_EVERYONE) - - // A guess on how disruptive to a round the event might be. If the action is chosen, the GM's - // 'danger' score is increased by this number. - // Negative numbers could be used to signify helpful events. - var/chaos = 0 - - // A threshold the GM will use alongside its 'danger' score, to determine if it should pass - // over the event associated with this object. The decision is based on - var/chaotic_threshold = null - - // If true, the event won't have it's `enabled` var set to FALSE when ran by the GM system. - var/reusable = FALSE - - // A string used to identify a 'class' of similar events. - // If the event is not reusable, than all events sharing the same class are disabled. - // Useful if you only ever want one event per round while having a lot of different subtypes of the event. - var/event_class = null - - // Counter for how many times this event has been picked by the GM. - // Can be used to make event repeats discouraged but not forbidden by adjusting the weight based on it. - var/times_ran = 0 - - // The type path to the event associated with this meta object. - // When the GM chooses this event, a new instance is made. - // Seperate instances allow for multiple concurrent events without sharing state, e.g. two blobs. - var/event_type = null - - -// Called by the GM system to actually start an event. -/datum/event2/meta/proc/make_event() - var/datum/event2/event/E = new event_type() - E.execute() - return E - -// Returns a TRUE or FALSE for if the GM system should be able to pick this event. -// Can be extended to check for more than just `enabled` later. -/datum/event2/meta/proc/can_pick() - return enabled - -/* - * Procs to Override - */ - -// Returns a number that determines how likely it is for the event to be picked over others. -// Individual events should override this for their own weights. -/datum/event2/meta/proc/get_weight() - return 0 - - -/datum/event2/meta/Topic(href, href_list) - if(..()) - return - - if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) - message_admins("[usr] has attempted to manipulate an event without sufficent privilages.") - return - - if(href_list["force"]) - // SSevent_ticker.start_event(event_type) // VOREStation Edit - We don't use SSgame_master yet. - message_admins("Event '[name]' was forced by [usr.key].") - - if(href_list["toggle"]) - enabled = !enabled - message_admins("Event '[name]' was toggled [enabled ? "on" : "off"] by [usr.key].") - +// The 'meta' object contains information about its assigned 'action' object, like what departments it will affect. +// It is directly held inside the Game Master Event System. + +// The code for actually executing an event should go inside the event object instead. +/datum/event2/meta + // Name used for organization, shown in the debug verb for the GM system. + // If null, the meta event will be discarded when the GM system initializes, so it is safe to use nameless subtypes for inheritence. + var/name = null + + // If FALSE, the GM system won't pick this. + // Some events set this to FALSE after running, to avoid running twice. + var/enabled = TRUE + + // What departments the event attached might affect. + var/list/departments = list(DEPARTMENT_EVERYONE) + + // A guess on how disruptive to a round the event might be. If the action is chosen, the GM's + // 'danger' score is increased by this number. + // Negative numbers could be used to signify helpful events. + var/chaos = 0 + + // A threshold the GM will use alongside its 'danger' score, to determine if it should pass + // over the event associated with this object. The decision is based on + var/chaotic_threshold = null + + // If true, the event won't have it's `enabled` var set to FALSE when ran by the GM system. + var/reusable = FALSE + + // A string used to identify a 'class' of similar events. + // If the event is not reusable, than all events sharing the same class are disabled. + // Useful if you only ever want one event per round while having a lot of different subtypes of the event. + var/event_class = null + + // Counter for how many times this event has been picked by the GM. + // Can be used to make event repeats discouraged but not forbidden by adjusting the weight based on it. + var/times_ran = 0 + + // The type path to the event associated with this meta object. + // When the GM chooses this event, a new instance is made. + // Seperate instances allow for multiple concurrent events without sharing state, e.g. two blobs. + var/event_type = null + + +// Called by the GM system to actually start an event. +/datum/event2/meta/proc/make_event() + var/datum/event2/event/E = new event_type() + E.execute() + return E + +// Returns a TRUE or FALSE for if the GM system should be able to pick this event. +// Can be extended to check for more than just `enabled` later. +/datum/event2/meta/proc/can_pick() + return enabled + +/* + * Procs to Override + */ + +// Returns a number that determines how likely it is for the event to be picked over others. +// Individual events should override this for their own weights. +/datum/event2/meta/proc/get_weight() + return 0 + + +/datum/event2/meta/Topic(href, href_list) + if(..()) + return + + if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) + message_admins("[usr] has attempted to manipulate an event without sufficent privilages.") + return + + if(href_list["force"]) + // SSevent_ticker.start_event(event_type) // VOREStation Edit - We don't use SSgame_master yet. + message_admins("Event '[name]' was forced by [usr.key].") + + if(href_list["toggle"]) + enabled = !enabled + message_admins("Event '[name]' was toggled [enabled ? "on" : "off"] by [usr.key].") + // SSgame_master.interact(usr) // To refresh the UI. // VOREStation Edit - We don't use SSgame_master yet. \ No newline at end of file diff --git a/code/modules/genetics/side_effects.dm b/code/modules/genetics/side_effects.dm index 958f05d5ac2..3ff4ee94160 100644 --- a/code/modules/genetics/side_effects.dm +++ b/code/modules/genetics/side_effects.dm @@ -1,81 +1,81 @@ -/datum/genetics/side_effect - var/name // name of the side effect, to use as a header in the manual - var/symptom // description of the symptom of the side effect - var/treatment // description of the treatment of the side effect - var/effect // description of what happens when not treated - var/duration = 0 // delay between start() and finish() - -/datum/genetics/side_effect/proc/start(mob/living/carbon/human/H) - // start the side effect, this should give some cue as to what's happening, - // such as gasping. These cues need to be unique among side-effects. - -/datum/genetics/side_effect/proc/finish(mob/living/carbon/human/H) - // Finish the side-effect. This should first check whether the cure has been - // applied, and if not, cause bad things to happen. - -/datum/genetics/side_effect/genetic_burn - name = "Genetic Burn" - symptom = "Subject's skin turns unusualy red." - treatment = "Inject small dose of dexalin." - effect = "Subject's skin burns." - duration = 30 SECONDS - -/datum/genetics/side_effect/genetic_burn/start(mob/living/carbon/human/H) - H.custom_emote(VISIBLE_MESSAGE, "starts turning very red..") - -/datum/genetics/side_effect/genetic_burn/finish(mob/living/carbon/human/H) - if(H.reagents.has_reagent("dexalin")) - return - for(var/organ_name in BP_ALL) - var/obj/item/organ/external/E = H.get_organ(organ_name) - E.take_damage(0, 5, 0) - -/datum/genetics/side_effect/bone_snap - name = "Bone Snap" - symptom = "Subject's limbs tremble notably." - treatment = "Inject small dose of bicaridine." - effect = "Subject's bone breaks." - duration = 60 SECONDS - -/datum/genetics/side_effect/bone_snap/start(mob/living/carbon/human/H) - H.custom_emote(VISIBLE_MESSAGE, "'s limbs start shivering uncontrollably.") - -/datum/genetics/side_effect/bone_snap/finish(mob/living/carbon/human/H) - if(H.reagents.has_reagent("bicaridine")) - return - var/organ_name = pick(BP_ALL) - var/obj/item/organ/external/E = H.get_organ(organ_name) - E.take_damage(20, 0, 0) - E.fracture() - -/datum/genetics/side_effect/confuse - name = "Confuse" - symptom = "Subject starts drooling uncontrollably." - treatment = "Inject small dose of dylovene." - effect = "Subject becomes confused." - duration = 30 SECONDS - -/datum/genetics/side_effect/confuse/start(mob/living/carbon/human/H) - var/datum/gender/T = gender_datums[H.get_visible_gender()] - H.custom_emote(VISIBLE_MESSAGE, "has drool running down from [T.his] mouth.") - -/datum/genetics/side_effect/confuse/finish(mob/living/carbon/human/H) - if(H.reagents.has_reagent("anti_toxin")) - return - H.Confuse(100) - -/proc/trigger_side_effect(mob/living/carbon/human/H) - spawn - if(!istype(H)) return - var/tp = pick(subtypesof(/datum/genetics/side_effect)) - var/datum/genetics/side_effect/S = new tp - - S.start(H) - spawn(20) - if(!istype(H)) return - H.Weaken(rand(0, S.duration / 50)) - sleep(S.duration) - - if(!istype(H)) return - H.SetWeakened(0) - S.finish(H) +/datum/genetics/side_effect + var/name // name of the side effect, to use as a header in the manual + var/symptom // description of the symptom of the side effect + var/treatment // description of the treatment of the side effect + var/effect // description of what happens when not treated + var/duration = 0 // delay between start() and finish() + +/datum/genetics/side_effect/proc/start(mob/living/carbon/human/H) + // start the side effect, this should give some cue as to what's happening, + // such as gasping. These cues need to be unique among side-effects. + +/datum/genetics/side_effect/proc/finish(mob/living/carbon/human/H) + // Finish the side-effect. This should first check whether the cure has been + // applied, and if not, cause bad things to happen. + +/datum/genetics/side_effect/genetic_burn + name = "Genetic Burn" + symptom = "Subject's skin turns unusualy red." + treatment = "Inject small dose of dexalin." + effect = "Subject's skin burns." + duration = 30 SECONDS + +/datum/genetics/side_effect/genetic_burn/start(mob/living/carbon/human/H) + H.custom_emote(VISIBLE_MESSAGE, "starts turning very red..") + +/datum/genetics/side_effect/genetic_burn/finish(mob/living/carbon/human/H) + if(H.reagents.has_reagent("dexalin")) + return + for(var/organ_name in BP_ALL) + var/obj/item/organ/external/E = H.get_organ(organ_name) + E.take_damage(0, 5, 0) + +/datum/genetics/side_effect/bone_snap + name = "Bone Snap" + symptom = "Subject's limbs tremble notably." + treatment = "Inject small dose of bicaridine." + effect = "Subject's bone breaks." + duration = 60 SECONDS + +/datum/genetics/side_effect/bone_snap/start(mob/living/carbon/human/H) + H.custom_emote(VISIBLE_MESSAGE, "'s limbs start shivering uncontrollably.") + +/datum/genetics/side_effect/bone_snap/finish(mob/living/carbon/human/H) + if(H.reagents.has_reagent("bicaridine")) + return + var/organ_name = pick(BP_ALL) + var/obj/item/organ/external/E = H.get_organ(organ_name) + E.take_damage(20, 0, 0) + E.fracture() + +/datum/genetics/side_effect/confuse + name = "Confuse" + symptom = "Subject starts drooling uncontrollably." + treatment = "Inject small dose of dylovene." + effect = "Subject becomes confused." + duration = 30 SECONDS + +/datum/genetics/side_effect/confuse/start(mob/living/carbon/human/H) + var/datum/gender/T = gender_datums[H.get_visible_gender()] + H.custom_emote(VISIBLE_MESSAGE, "has drool running down from [T.his] mouth.") + +/datum/genetics/side_effect/confuse/finish(mob/living/carbon/human/H) + if(H.reagents.has_reagent("anti_toxin")) + return + H.Confuse(100) + +/proc/trigger_side_effect(mob/living/carbon/human/H) + spawn + if(!istype(H)) return + var/tp = pick(subtypesof(/datum/genetics/side_effect)) + var/datum/genetics/side_effect/S = new tp + + S.start(H) + spawn(20) + if(!istype(H)) return + H.Weaken(rand(0, S.duration / 50)) + sleep(S.duration) + + if(!istype(H)) return + H.SetWeakened(0) + S.finish(H) diff --git a/code/modules/holodeck/HolodeckPrograms.dm b/code/modules/holodeck/HolodeckPrograms.dm index 4ae4361a21c..c68f26d731a 100644 --- a/code/modules/holodeck/HolodeckPrograms.dm +++ b/code/modules/holodeck/HolodeckPrograms.dm @@ -1,7 +1,7 @@ -/datum/holodeck_program - var/target - var/list/ambience = null - -/datum/holodeck_program/New(var/target, var/list/ambience = null) - src.target = target - src.ambience = ambience +/datum/holodeck_program + var/target + var/list/ambience = null + +/datum/holodeck_program/New(var/target, var/list/ambience = null) + src.target = target + src.ambience = ambience diff --git a/code/modules/hydroponics/seed_machines_yw.dm b/code/modules/hydroponics/seed_machines_yw.dm index 95603878467..ce406cbb53f 100644 --- a/code/modules/hydroponics/seed_machines_yw.dm +++ b/code/modules/hydroponics/seed_machines_yw.dm @@ -18,13 +18,12 @@ "mhydrogen", "steel", "plasteel", - "hydrophoron", - - "macrocillin", //ban everything that's already banned in seed.dm except nutriment - "microcillin", - "normalcillin", - "adminordrazine", - "magicdust" + "hydrophoron", + "macrocillin", //ban everything that's already banned in seed.dm except nutriment + "microcillin", + "normalcillin", + "adminordrazine", + "magicdust" ) var/list/datum/reagent/allowed_reagents = list() //compile the list of reagents we're allowed to splice in @@ -77,10 +76,10 @@ var/list/data = list() data["screenstate"] = screen_state if(loaded_beaker) - var/list/btemp = data["beakerchems"] - + var/beakerContents[0] for(var/datum/reagent/current in loaded_beaker.reagents.reagent_list) - btemp.Add(list(list("name" = "[current.id]", "displayname" = SSchemistry.chemical_reagents[current.id]))) + beakerContents.Add(list(list("name" = current.name, "id" = current.id, "volume" = current.volume))) + data["beakerchems"] = beakerContents if(seed) data["seedname"] = seed.seed.display_name data["health"] = seed.modified @@ -208,4 +207,4 @@ visible_message("[bicon(src)] [src] pings unhappily, flashing a red warning light.") loaded_beaker.reagents.remove_reagent(chem_name, chem_amount) //remove ALL of the reagent from the beaker visible_message("[bicon(src)] The [src] beeps, indicating genetic synthesis was successful.") - return \ No newline at end of file + return diff --git a/code/modules/identification/identification.dm b/code/modules/identification/identification.dm index 99ed8bc6339..41ac23316a6 100644 --- a/code/modules/identification/identification.dm +++ b/code/modules/identification/identification.dm @@ -1,126 +1,126 @@ -// This is a datum attached to objects to make their 'identity' be unknown initially. -// The identitiy and properties of an unidentified object can be determined in-game through a specialized process or by potentially risky trial-and-error. -// This is very similar to a traditional roguelike's identification system, and as such will use certain terms from those to describe them. -// Despite this, unlike a roguelike, objects that do the same thing DO NOT have the same name/appearance/etc. - -/datum/identification - var/obj/holder = null // The thing the datum is 'attached' to. - // Holds the true information. - var/true_name = null // The real name of the object. It is copied automatically from holder, on the datum being instantiated. - var/true_desc = null // Ditto, for desc. - var/true_description_info = null // Ditto, for helpful examine panel entries. - var/true_description_fluff = null // Ditto, for lore. - var/true_description_antag = null // Ditto, for antag info (this probably won't get used). - var/identified = IDENTITY_UNKNOWN // Can be IDENTITY_UNKNOWN, IDENTITY_PROPERTIES, IDENTITY_QUALITY, or IDENTITY_FULL. - - // Holds what is displayed when not identified sufficently. - var/unidentified_name = null // The name given to the object when not identified. Generated by generate_unidentified_name() - var/unidentified_desc = "You're not too sure what this is." - var/unidentified_description_info = "This object is unidentified, and as such its properties are unknown. Using this object may be dangerous." - - // Lists of lists for generating names by combining one from each. - var/list/naming_lists = list() - - // What 'identification type' is needed to identify this. - var/identification_type = IDENTITY_TYPE_NONE - -/datum/identification/New(obj/new_holder) - ASSERT(new_holder) - holder = new_holder - record_true_identity() // Get all the identifying features from the holder. - update_name() // Then hide them for awhile if needed. - -/datum/identification/Destroy() - holder = null - return ..() - -// Records the object's inital identifiying features to the datum for future safekeeping. -/datum/identification/proc/record_true_identity() - true_name = holder.name - true_desc = holder.desc - true_description_info = holder.description_info - true_description_fluff = holder.description_fluff - true_description_antag = holder.description_antag - -// Formally identifies the holder. -/datum/identification/proc/identify(new_identity = IDENTITY_FULL, mob/user) - if(new_identity & identified) // Already done. - return - identified |= new_identity // Set the bitflag. - if(user) - switch(identified) - if(IDENTITY_QUALITY) - to_chat(user, "You've identified \the [holder]'s quality.") - if(IDENTITY_PROPERTIES) - to_chat(user, "You've identified \the [holder]'s functionality as a [true_name].") - if(IDENTITY_FULL) - to_chat(user, "You've identified \the [holder] as a [true_name], and its quality.") - update_name() - holder.update_icon() - -// Reverses identification for whatever reason. -/datum/identification/proc/unidentify(new_identity = IDENTITY_UNKNOWN, mob/user) - identified &= ~new_identity // Unset the bitflag. - update_name() - holder.update_icon() - if(user) - switch(identified) // Give a message based on what's left. - if(IDENTITY_QUALITY) - to_chat(user, span("warning", "You forgot what \the [holder] actually did...")) - if(IDENTITY_PROPERTIES) - to_chat(user, span("warning", "You forgot \the [holder]'s quality...")) - if(IDENTITY_UNKNOWN) - to_chat(user, span("warning", "You forgot everything about \the [holder].")) - -// Sets the holder's name to the real name if its properties are identified, or obscures it otherwise. -/datum/identification/proc/update_name() - if(identified & IDENTITY_PROPERTIES) - holder.name = true_name - holder.desc = true_desc - holder.description_info = true_description_info - holder.description_fluff = true_description_fluff - holder.description_antag = true_description_antag - return - - if(!unidentified_name) - unidentified_name = generate_unidentified_name() - - holder.name = unidentified_name - holder.desc = unidentified_desc - holder.description_info = unidentified_description_info - holder.description_fluff = null - holder.description_antag = null - -// Makes a name for an object that is not identified. It picks one string out of each list inside naming_list. -/datum/identification/proc/generate_unidentified_name() - if(!LAZYLEN(naming_lists)) - return "unidentified object" - - var/list/new_name = list() - for(var/list/current_list as anything in naming_lists) - new_name += pick(current_list) - return new_name.Join(" ") - -// Used for tech-based objects. -// Unused for now pending Future Stuff(tm). -/datum/identification/mechanical - naming_lists = list( - list("unidentified", "unknown", "strange", "weird", "unfamiliar", "peculiar", "mysterious", "bizarre", "odd"), - list("device", "apparatus", "gadget", "mechanism", "appliance", "machine", "equipment", "invention", "contraption") - ) - identification_type = IDENTITY_TYPE_TECH - -// Used for unidentified hypos. -// Their contents can range from genuine medication, expired medicine, illicit drugs, toxins and poisons, and more. -// They are the analog for potions in a traditional roguelike. -/datum/identification/hypo - naming_lists = list( - list("unidentified", "unknown", "unmarked", "blank", "refilled", "custom", "modified", "questionable", "suspicious"), - list("autoinjector") - ) - unidentified_desc = "An autoinjector that does not give any indication towards what is inside. \ - The case is also sealed tight and the liquids contained cannot be removed except by injecting it into someone. \ - Do you feel lucky?" - unidentified_description_info = "A skilled chemist with a specialized machine can identify this autoinjector. \ - Blindly using the autoinjector is risky and can be dangerous." - identification_type = IDENTITY_TYPE_CHEMICAL +// This is a datum attached to objects to make their 'identity' be unknown initially. +// The identitiy and properties of an unidentified object can be determined in-game through a specialized process or by potentially risky trial-and-error. +// This is very similar to a traditional roguelike's identification system, and as such will use certain terms from those to describe them. +// Despite this, unlike a roguelike, objects that do the same thing DO NOT have the same name/appearance/etc. + +/datum/identification + var/obj/holder = null // The thing the datum is 'attached' to. + // Holds the true information. + var/true_name = null // The real name of the object. It is copied automatically from holder, on the datum being instantiated. + var/true_desc = null // Ditto, for desc. + var/true_description_info = null // Ditto, for helpful examine panel entries. + var/true_description_fluff = null // Ditto, for lore. + var/true_description_antag = null // Ditto, for antag info (this probably won't get used). + var/identified = IDENTITY_UNKNOWN // Can be IDENTITY_UNKNOWN, IDENTITY_PROPERTIES, IDENTITY_QUALITY, or IDENTITY_FULL. + + // Holds what is displayed when not identified sufficently. + var/unidentified_name = null // The name given to the object when not identified. Generated by generate_unidentified_name() + var/unidentified_desc = "You're not too sure what this is." + var/unidentified_description_info = "This object is unidentified, and as such its properties are unknown. Using this object may be dangerous." + + // Lists of lists for generating names by combining one from each. + var/list/naming_lists = list() + + // What 'identification type' is needed to identify this. + var/identification_type = IDENTITY_TYPE_NONE + +/datum/identification/New(obj/new_holder) + ASSERT(new_holder) + holder = new_holder + record_true_identity() // Get all the identifying features from the holder. + update_name() // Then hide them for awhile if needed. + +/datum/identification/Destroy() + holder = null + return ..() + +// Records the object's inital identifiying features to the datum for future safekeeping. +/datum/identification/proc/record_true_identity() + true_name = holder.name + true_desc = holder.desc + true_description_info = holder.description_info + true_description_fluff = holder.description_fluff + true_description_antag = holder.description_antag + +// Formally identifies the holder. +/datum/identification/proc/identify(new_identity = IDENTITY_FULL, mob/user) + if(new_identity & identified) // Already done. + return + identified |= new_identity // Set the bitflag. + if(user) + switch(identified) + if(IDENTITY_QUALITY) + to_chat(user, "You've identified \the [holder]'s quality.") + if(IDENTITY_PROPERTIES) + to_chat(user, "You've identified \the [holder]'s functionality as a [true_name].") + if(IDENTITY_FULL) + to_chat(user, "You've identified \the [holder] as a [true_name], and its quality.") + update_name() + holder.update_icon() + +// Reverses identification for whatever reason. +/datum/identification/proc/unidentify(new_identity = IDENTITY_UNKNOWN, mob/user) + identified &= ~new_identity // Unset the bitflag. + update_name() + holder.update_icon() + if(user) + switch(identified) // Give a message based on what's left. + if(IDENTITY_QUALITY) + to_chat(user, span("warning", "You forgot what \the [holder] actually did...")) + if(IDENTITY_PROPERTIES) + to_chat(user, span("warning", "You forgot \the [holder]'s quality...")) + if(IDENTITY_UNKNOWN) + to_chat(user, span("warning", "You forgot everything about \the [holder].")) + +// Sets the holder's name to the real name if its properties are identified, or obscures it otherwise. +/datum/identification/proc/update_name() + if(identified & IDENTITY_PROPERTIES) + holder.name = true_name + holder.desc = true_desc + holder.description_info = true_description_info + holder.description_fluff = true_description_fluff + holder.description_antag = true_description_antag + return + + if(!unidentified_name) + unidentified_name = generate_unidentified_name() + + holder.name = unidentified_name + holder.desc = unidentified_desc + holder.description_info = unidentified_description_info + holder.description_fluff = null + holder.description_antag = null + +// Makes a name for an object that is not identified. It picks one string out of each list inside naming_list. +/datum/identification/proc/generate_unidentified_name() + if(!LAZYLEN(naming_lists)) + return "unidentified object" + + var/list/new_name = list() + for(var/list/current_list as anything in naming_lists) + new_name += pick(current_list) + return new_name.Join(" ") + +// Used for tech-based objects. +// Unused for now pending Future Stuff(tm). +/datum/identification/mechanical + naming_lists = list( + list("unidentified", "unknown", "strange", "weird", "unfamiliar", "peculiar", "mysterious", "bizarre", "odd"), + list("device", "apparatus", "gadget", "mechanism", "appliance", "machine", "equipment", "invention", "contraption") + ) + identification_type = IDENTITY_TYPE_TECH + +// Used for unidentified hypos. +// Their contents can range from genuine medication, expired medicine, illicit drugs, toxins and poisons, and more. +// They are the analog for potions in a traditional roguelike. +/datum/identification/hypo + naming_lists = list( + list("unidentified", "unknown", "unmarked", "blank", "refilled", "custom", "modified", "questionable", "suspicious"), + list("autoinjector") + ) + unidentified_desc = "An autoinjector that does not give any indication towards what is inside. \ + The case is also sealed tight and the liquids contained cannot be removed except by injecting it into someone. \ + Do you feel lucky?" + unidentified_description_info = "A skilled chemist with a specialized machine can identify this autoinjector. \ + Blindly using the autoinjector is risky and can be dangerous." + identification_type = IDENTITY_TYPE_CHEMICAL diff --git a/code/modules/identification/item_procs.dm b/code/modules/identification/item_procs.dm index c2e5ddaed60..b21716bf47b 100644 --- a/code/modules/identification/item_procs.dm +++ b/code/modules/identification/item_procs.dm @@ -1,30 +1,30 @@ -// This is on the base /item so badmins can play with it by calling hide_identity(). -/obj/item - var/datum/identification/identity = null - var/identity_type = /datum/identification - var/init_hide_identity = FALSE // Set to true to automatically obscure the object on initialization. - -/obj/item/Initialize() - if(init_hide_identity) - identity = new identity_type(src) - return ..() - -/obj/item/Destroy() - if(identity) - QDEL_NULL(identity) - return ..() - -/obj/item/proc/hide_identity() // Mostly for admins to make things secret. - if(!identity) - identity = new identity_type(src) - else - identity.unidentify() - -/obj/item/proc/identify(identity_type = IDENTITY_FULL, mob/user) - if(identity) - identity.identify(identity_type, user) - -/obj/item/proc/is_identified(identity_type = IDENTITY_FULL) - if(!identity) // No identification datum means nothing to hide. - return TRUE - return identity_type & identity.identified +// This is on the base /item so badmins can play with it by calling hide_identity(). +/obj/item + var/datum/identification/identity = null + var/identity_type = /datum/identification + var/init_hide_identity = FALSE // Set to true to automatically obscure the object on initialization. + +/obj/item/Initialize() + if(init_hide_identity) + identity = new identity_type(src) + return ..() + +/obj/item/Destroy() + if(identity) + QDEL_NULL(identity) + return ..() + +/obj/item/proc/hide_identity() // Mostly for admins to make things secret. + if(!identity) + identity = new identity_type(src) + else + identity.unidentify() + +/obj/item/proc/identify(identity_type = IDENTITY_FULL, mob/user) + if(identity) + identity.identify(identity_type, user) + +/obj/item/proc/is_identified(identity_type = IDENTITY_FULL) + if(!identity) // No identification datum means nothing to hide. + return TRUE + return identity_type & identity.identified diff --git a/code/modules/instruments/songs/editor.dm b/code/modules/instruments/songs/editor.dm index 883ecc881e4..f38420fd376 100644 --- a/code/modules/instruments/songs/editor.dm +++ b/code/modules/instruments/songs/editor.dm @@ -196,7 +196,7 @@ set_linear_falloff_duration(round(amount * 10, world.tick_lag)) else if(href_list["setexpfalloff"]) - var/amount = tgui_input_number(usr, "Set exponential sustain factor", "Exponential sustain factor") + var/amount = tgui_input_number(usr, "Set exponential sustain factor", "Exponential sustain factor", round_value=FALSE) if(!isnull(amount)) set_exponential_drop_rate(round(amount, 0.00001)) @@ -206,7 +206,7 @@ set_volume(round(amount, 1)) else if(href_list["setdropoffvolume"]) - var/amount = tgui_input_number(usr, "Set dropoff threshold", "Dropoff Threshold Volume") + var/amount = tgui_input_number(usr, "Set dropoff threshold", "Dropoff Threshold Volume", round_value=FALSE) if(!isnull(amount)) set_dropoff_volume(round(amount, 0.01)) @@ -233,7 +233,7 @@ set_instrument(choice) else if(href_list["setnoteshift"]) - var/amount = tgui_input_number(usr, "Set note shift", "Note Shift") + var/amount = tgui_input_number(usr, "Set note shift", "Note Shift", null, note_shift_max, note_shift_min) if(!isnull(amount)) note_shift = clamp(amount, note_shift_min, note_shift_max) diff --git a/code/modules/integrated_electronics/core/assemblies/clothing.dm b/code/modules/integrated_electronics/core/assemblies/clothing.dm index 4840537d97c..c7b6f998fcc 100644 --- a/code/modules/integrated_electronics/core/assemblies/clothing.dm +++ b/code/modules/integrated_electronics/core/assemblies/clothing.dm @@ -1,180 +1,180 @@ - -// The base subtype for assemblies that can be worn. Certain pieces will have more or less capabilities -// E.g. Glasses have less room than something worn over the chest. -// Note that the electronic assembly is INSIDE the object that actually gets worn, in a similar way to implants. - -/obj/item/device/electronic_assembly/clothing - name = "electronic clothing" - icon_state = "circuitry" // Needs to match the clothing's base icon_state. - desc = "It's a case, for building machines attached to clothing." - w_class = ITEMSIZE_SMALL - max_components = IC_COMPONENTS_BASE - max_complexity = IC_COMPLEXITY_BASE - var/obj/item/clothing/clothing = null - -/obj/item/device/electronic_assembly/clothing/tgui_host() - return clothing.tgui_host() - -/obj/item/device/electronic_assembly/clothing/update_icon() - ..() - clothing.icon_state = icon_state - // We don't need to update the mob sprite since it won't (and shouldn't) actually get changed. - -// This is 'small' relative to the size of regular clothing assemblies. -/obj/item/device/electronic_assembly/clothing/small - max_components = IC_COMPONENTS_BASE / 2 - max_complexity = IC_COMPLEXITY_BASE / 2 - w_class = ITEMSIZE_TINY - -// Ditto. -/obj/item/device/electronic_assembly/clothing/large - max_components = IC_COMPONENTS_BASE * 2 - max_complexity = IC_COMPLEXITY_BASE * 2 - w_class = ITEMSIZE_NORMAL - - -// This is defined higher up, in /clothing to avoid lots of copypasta. -/obj/item/clothing - var/obj/item/device/electronic_assembly/clothing/IC = null - var/obj/item/integrated_circuit/built_in/action_button/action_circuit = null // This gets pulsed when someone clicks the button on the hud. - -/obj/item/clothing/emp_act(severity) - if(IC) - IC.emp_act(severity) - ..() - -/obj/item/clothing/examine(mob/user) - . = ..() - if(IC) - . += IC.examine(user) - -/obj/item/clothing/CtrlShiftClick(mob/user) - var/turf/T = get_turf(src) - if(!T.AdjacentQuick(user)) // So people aren't messing with these from across the room - return FALSE - var/obj/item/I = user.get_active_hand() // ctrl-shift-click doesn't give us the item, we have to fetch it - if(!I) - return FALSE - return IC.attackby(I, user) - -/obj/item/clothing/attack_self(mob/user) - if(IC) - if(IC.opened) - IC.attack_self(user) - else - action_circuit.do_work() - else - ..() - -// Does most of the repeatative setup. -/obj/item/clothing/proc/setup_integrated_circuit(new_type) - // Set up the internal circuit holder. - IC = new new_type(src) - IC.clothing = src - IC.name = name - - // Clothing assemblies can be triggered by clicking on the HUD. This allows that to occur. - action_circuit = new(src.IC) - IC.force_add_circuit(action_circuit) - action_button_name = "Activate [name]" - -/obj/item/clothing/Destroy() - if(IC) - IC.clothing = null - action_circuit = null // Will get deleted by qdel-ing the IC assembly. - qdel(IC) - return ..() - -// Specific subtypes. - -// Jumpsuit. -/obj/item/clothing/under/circuitry - name = "electronic jumpsuit" - desc = "It's a wearable case for electronics. This on is a black jumpsuit with wiring weaved into the fabric." - description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." - icon_state = "circuitry" - worn_state = "circuitry" - -/obj/item/clothing/under/circuitry/Initialize() - setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing) - return ..() - - -// Gloves. -/obj/item/clothing/gloves/circuitry - name = "electronic gloves" - desc = "It's a wearable case for electronics. This one is a pair of black gloves, with wires woven into them. A small \ - device with a screen is attached to the left glove." - description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." - icon_state = "circuitry" - item_state = "circuitry" - -/obj/item/clothing/gloves/circuitry/Initialize() - setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) - return ..() - - -// Glasses. -/obj/item/clothing/glasses/circuitry - name = "electronic goggles" - desc = "It's a wearable case for electronics. This one is a pair of goggles, with wiring sticking out. \ - Could this augment your vision?" // Sadly it won't, or at least not yet. - description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." - icon_state = "circuitry" - item_state = "night" // The on-mob sprite would be identical anyways. - -/obj/item/clothing/glasses/circuitry/Initialize() - setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) - return ..() - -// Shoes -/obj/item/clothing/shoes/circuitry - name = "electronic boots" - desc = "It's a wearable case for electronics. This one is a pair of boots, with wires attached to a small \ - cover." - description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." - icon_state = "circuitry" - item_state = "circuitry" - -/obj/item/clothing/shoes/circuitry/Initialize() - setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) - return ..() - -// Head -/obj/item/clothing/head/circuitry - name = "electronic headwear" - desc = "It's a wearable case for electronics. This one appears to be a very technical-looking piece that \ - goes around the collar, with a heads-up-display attached on the right." - description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." - icon_state = "circuitry" - item_state = "circuitry" - -/obj/item/clothing/head/circuitry/Initialize() - setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) - return ..() - -// Ear -/obj/item/clothing/ears/circuitry - name = "electronic earwear" - desc = "It's a wearable case for electronics. This one appears to be a technical-looking headset." - description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." - icon = 'icons/inventory/ears/item.dmi' - icon_state = "circuitry" - item_state = "circuitry" - -/obj/item/clothing/ears/circuitry/Initialize() - setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) - return ..() - -// Exo-slot -/obj/item/clothing/suit/circuitry - name = "electronic chestpiece" - desc = "It's a wearable case for electronics. This one appears to be a very technical-looking vest, that \ - almost looks professionally made, however the wiring popping out betrays that idea." - description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." - icon_state = "circuitry" - item_state = "circuitry" - -/obj/item/clothing/suit/circuitry/Initialize() - setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/large) + +// The base subtype for assemblies that can be worn. Certain pieces will have more or less capabilities +// E.g. Glasses have less room than something worn over the chest. +// Note that the electronic assembly is INSIDE the object that actually gets worn, in a similar way to implants. + +/obj/item/device/electronic_assembly/clothing + name = "electronic clothing" + icon_state = "circuitry" // Needs to match the clothing's base icon_state. + desc = "It's a case, for building machines attached to clothing." + w_class = ITEMSIZE_SMALL + max_components = IC_COMPONENTS_BASE + max_complexity = IC_COMPLEXITY_BASE + var/obj/item/clothing/clothing = null + +/obj/item/device/electronic_assembly/clothing/tgui_host() + return clothing.tgui_host() + +/obj/item/device/electronic_assembly/clothing/update_icon() + ..() + clothing.icon_state = icon_state + // We don't need to update the mob sprite since it won't (and shouldn't) actually get changed. + +// This is 'small' relative to the size of regular clothing assemblies. +/obj/item/device/electronic_assembly/clothing/small + max_components = IC_COMPONENTS_BASE / 2 + max_complexity = IC_COMPLEXITY_BASE / 2 + w_class = ITEMSIZE_TINY + +// Ditto. +/obj/item/device/electronic_assembly/clothing/large + max_components = IC_COMPONENTS_BASE * 2 + max_complexity = IC_COMPLEXITY_BASE * 2 + w_class = ITEMSIZE_NORMAL + + +// This is defined higher up, in /clothing to avoid lots of copypasta. +/obj/item/clothing + var/obj/item/device/electronic_assembly/clothing/IC = null + var/obj/item/integrated_circuit/built_in/action_button/action_circuit = null // This gets pulsed when someone clicks the button on the hud. + +/obj/item/clothing/emp_act(severity) + if(IC) + IC.emp_act(severity) + ..() + +/obj/item/clothing/examine(mob/user) + . = ..() + if(IC) + . += IC.examine(user) + +/obj/item/clothing/CtrlShiftClick(mob/user) + var/turf/T = get_turf(src) + if(!T.AdjacentQuick(user)) // So people aren't messing with these from across the room + return FALSE + var/obj/item/I = user.get_active_hand() // ctrl-shift-click doesn't give us the item, we have to fetch it + if(!I) + return FALSE + return IC.attackby(I, user) + +/obj/item/clothing/attack_self(mob/user) + if(IC) + if(IC.opened) + IC.attack_self(user) + else + action_circuit.do_work() + else + ..() + +// Does most of the repeatative setup. +/obj/item/clothing/proc/setup_integrated_circuit(new_type) + // Set up the internal circuit holder. + IC = new new_type(src) + IC.clothing = src + IC.name = name + + // Clothing assemblies can be triggered by clicking on the HUD. This allows that to occur. + action_circuit = new(src.IC) + IC.force_add_circuit(action_circuit) + action_button_name = "Activate [name]" + +/obj/item/clothing/Destroy() + if(IC) + IC.clothing = null + action_circuit = null // Will get deleted by qdel-ing the IC assembly. + qdel(IC) + return ..() + +// Specific subtypes. + +// Jumpsuit. +/obj/item/clothing/under/circuitry + name = "electronic jumpsuit" + desc = "It's a wearable case for electronics. This on is a black jumpsuit with wiring weaved into the fabric." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." + icon_state = "circuitry" + worn_state = "circuitry" + +/obj/item/clothing/under/circuitry/Initialize() + setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing) + return ..() + + +// Gloves. +/obj/item/clothing/gloves/circuitry + name = "electronic gloves" + desc = "It's a wearable case for electronics. This one is a pair of black gloves, with wires woven into them. A small \ + device with a screen is attached to the left glove." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." + icon_state = "circuitry" + item_state = "circuitry" + +/obj/item/clothing/gloves/circuitry/Initialize() + setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) + return ..() + + +// Glasses. +/obj/item/clothing/glasses/circuitry + name = "electronic goggles" + desc = "It's a wearable case for electronics. This one is a pair of goggles, with wiring sticking out. \ + Could this augment your vision?" // Sadly it won't, or at least not yet. + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." + icon_state = "circuitry" + item_state = "night" // The on-mob sprite would be identical anyways. + +/obj/item/clothing/glasses/circuitry/Initialize() + setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) + return ..() + +// Shoes +/obj/item/clothing/shoes/circuitry + name = "electronic boots" + desc = "It's a wearable case for electronics. This one is a pair of boots, with wires attached to a small \ + cover." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." + icon_state = "circuitry" + item_state = "circuitry" + +/obj/item/clothing/shoes/circuitry/Initialize() + setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) + return ..() + +// Head +/obj/item/clothing/head/circuitry + name = "electronic headwear" + desc = "It's a wearable case for electronics. This one appears to be a very technical-looking piece that \ + goes around the collar, with a heads-up-display attached on the right." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." + icon_state = "circuitry" + item_state = "circuitry" + +/obj/item/clothing/head/circuitry/Initialize() + setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) + return ..() + +// Ear +/obj/item/clothing/ears/circuitry + name = "electronic earwear" + desc = "It's a wearable case for electronics. This one appears to be a technical-looking headset." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." + icon = 'icons/inventory/ears/item.dmi' + icon_state = "circuitry" + item_state = "circuitry" + +/obj/item/clothing/ears/circuitry/Initialize() + setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) + return ..() + +// Exo-slot +/obj/item/clothing/suit/circuitry + name = "electronic chestpiece" + desc = "It's a wearable case for electronics. This one appears to be a very technical-looking vest, that \ + almost looks professionally made, however the wiring popping out betrays that idea." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." + icon_state = "circuitry" + item_state = "circuitry" + +/obj/item/clothing/suit/circuitry/Initialize() + setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/large) return ..() \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/assemblies/generic.dm b/code/modules/integrated_electronics/core/assemblies/generic.dm index 49a933fe1b9..6e120c34231 100644 --- a/code/modules/integrated_electronics/core/assemblies/generic.dm +++ b/code/modules/integrated_electronics/core/assemblies/generic.dm @@ -1,263 +1,263 @@ -// Generic subtypes without a lot of special code. - -// Small assemblies. - -/obj/item/device/electronic_assembly/default - name = "type-a electronic assembly" - -/obj/item/device/electronic_assembly/calc - name = "type-b electronic assembly" - icon_state = "setup_small_calc" - desc = "It's a case, for building small electronics with. This one resembles a pocket calculator." - -/obj/item/device/electronic_assembly/clam - name = "type-c electronic assembly" - icon_state = "setup_small_clam" - desc = "It's a case, for building small electronics with. This one has a clamshell design." - -/obj/item/device/electronic_assembly/simple - name = "type-d electronic assembly" - icon_state = "setup_small_simple" - desc = "It's a case, for building small electronics with. This one has a simple design." - -/obj/item/device/electronic_assembly/hook - name = "type-e electronic assembly" - icon_state = "setup_small_hook" - desc = "It's a case, for building small electronics with. This one looks like it has a belt clip, but it's purely decorative." - -/obj/item/device/electronic_assembly/pda - name = "type-f electronic assembly" - icon_state = "setup_small_pda" - desc = "It's a case, for building small electronics with. This one resembles a PDA." - -// Tiny assemblies. - -/obj/item/device/electronic_assembly/tiny - name = "electronic device" - icon_state = "setup_device" - desc = "It's a case, for building tiny-sized electronics with." - w_class = ITEMSIZE_TINY - max_components = IC_COMPONENTS_BASE / 2 - max_complexity = IC_COMPLEXITY_BASE / 2 - -/obj/item/device/electronic_assembly/tiny/default - name = "type-a electronic device" - -/obj/item/device/electronic_assembly/tiny/cylinder - name = "type-b electronic device" - icon_state = "setup_device_cylinder" - desc = "It's a case, for building tiny-sized electronics with. This one has a cylindrical design." - -/obj/item/device/electronic_assembly/tiny/scanner - name = "type-c electronic device" - icon_state = "setup_device_scanner" - desc = "It's a case, for building tiny-sized electronics with. This one has a scanner-like design." - -/obj/item/device/electronic_assembly/tiny/hook - name = "type-d electronic device" - icon_state = "setup_device_hook" - desc = "It's a case, for building tiny-sized electronics with. This one looks like it has a belt clip, but it's purely decorative." - -/obj/item/device/electronic_assembly/tiny/box - name = "type-e electronic device" - icon_state = "setup_device_box" - desc = "It's a case, for building tiny-sized electronics with. This one has a boxy design." - -// Medium assemblies. - -/obj/item/device/electronic_assembly/medium - name = "electronic mechanism" - icon_state = "setup_medium" - desc = "It's a case, for building medium-sized electronics with." - w_class = ITEMSIZE_NORMAL - max_components = IC_COMPONENTS_BASE * 2 - max_complexity = IC_COMPLEXITY_BASE * 2 - -/obj/item/device/electronic_assembly/medium/default - name = "type-a electronic mechanism" - -/obj/item/device/electronic_assembly/medium/box - name = "type-b electronic mechanism" - icon_state = "setup_medium_box" - desc = "It's a case, for building medium-sized electronics with. This one has a boxy design." - -/obj/item/device/electronic_assembly/medium/clam - name = "type-c electronic mechanism" - icon_state = "setup_medium_clam" - desc = "It's a case, for building medium-sized electronics with. This one has a clamshell design." - -/obj/item/device/electronic_assembly/medium/medical - name = "type-d electronic mechanism" - icon_state = "setup_medium_med" - desc = "It's a case, for building medium-sized electronics with. This one resembles some type of medical apparatus." - -/obj/item/device/electronic_assembly/medium/gun - name = "type-e electronic mechanism" - icon_state = "setup_medium_gun" - item_state = "circuitgun" - desc = "It's a case, for building medium-sized electronics with. This one resembles a gun, or some type of tool, \ - if you're feeling optimistic." -// can_fire_equipped = TRUE - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_guns.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_guns.dmi', - ) - -/obj/item/device/electronic_assembly/medium/radio - name = "type-f electronic mechanism" - icon_state = "setup_medium_radio" - desc = "It's a case, for building medium-sized electronics with. This one resembles an old radio." - -// Large assemblies. - -/obj/item/device/electronic_assembly/large - name = "electronic machine" - icon_state = "setup_large" - desc = "It's a case, for building large electronics with." - w_class = ITEMSIZE_LARGE - max_components = IC_COMPONENTS_BASE * 4 - max_complexity = IC_COMPLEXITY_BASE * 4 - can_anchor = TRUE - -/obj/item/device/electronic_assembly/large/default - name = "type-a electronic machine" - -/obj/item/device/electronic_assembly/large/scope - name = "type-b electronic machine" - icon_state = "setup_large_scope" - desc = "It's a case, for building large electronics with. This one resembles an oscilloscope." - -/obj/item/device/electronic_assembly/large/terminal - name = "type-c electronic machine" - icon_state = "setup_large_terminal" - desc = "It's a case, for building large electronics with. This one resembles a computer terminal." - -/obj/item/device/electronic_assembly/large/arm - name = "type-d electronic machine" - icon_state = "setup_large_arm" - desc = "It's a case, for building large electronics with. This one resembles a robotic arm." - -/obj/item/device/electronic_assembly/large/tall - name = "type-e electronic machine" - icon_state = "setup_large_tall" - desc = "It's a case, for building large electronics with. This one has a tall design." - -/obj/item/device/electronic_assembly/large/industrial - name = "type-f electronic machine" - icon_state = "setup_large_industrial" - desc = "It's a case, for building large electronics with. This one resembles some kind of industrial machinery." - -// Drone assemblies, which can move with the locomotion circuit. - -/obj/item/device/electronic_assembly/drone - name = "electronic drone" - icon_state = "setup_drone" - desc = "It's a case, for building mobile electronics with." - w_class = ITEMSIZE_NORMAL - max_components = IC_COMPONENTS_BASE * 1.5 - max_complexity = IC_COMPLEXITY_BASE * 1.5 - can_anchor = FALSE - -/obj/item/device/electronic_assembly/drone/can_move() - return TRUE - -/obj/item/device/electronic_assembly/drone/default - name = "type-a electronic drone" - -/obj/item/device/electronic_assembly/drone/arms - name = "type-b electronic drone" - icon_state = "setup_drone_arms" - desc = "It's a case, for building mobile electronics with. This one is armed and dangerous." - -/obj/item/device/electronic_assembly/drone/secbot - name = "type-c electronic drone" - icon_state = "setup_drone_secbot" - desc = "It's a case, for building mobile electronics with. This one resembles a Securitron." - -/obj/item/device/electronic_assembly/drone/medbot - name = "type-d electronic drone" - icon_state = "setup_drone_medbot" - desc = "It's a case, for building mobile electronics with. This one resembles a Medibot." - -/obj/item/device/electronic_assembly/drone/genbot - name = "type-e electronic drone" - icon_state = "setup_drone_genbot" - desc = "It's a case, for building mobile electronics with. This one has a generic bot design." - -/obj/item/device/electronic_assembly/drone/android - name = "type-f electronic drone" - icon_state = "setup_drone_android" - desc = "It's a case, for building mobile electronics with. This one has a hominoid design." - -// Wall mounted assemblies. - -/obj/item/device/electronic_assembly/wallmount - name = "wall-mounted electronic assembly" - icon_state = "setup_wallmount_medium" - desc = "It's a case, for building medium-sized electronics with. It has a magnetized \ - backing to allow it to stick to walls." - w_class = ITEMSIZE_NORMAL - max_components = IC_COMPONENTS_BASE * 2 - max_complexity = IC_COMPLEXITY_BASE * 2 - can_anchor = TRUE - -/obj/item/device/electronic_assembly/wallmount/proc/mount_assembly(turf/on_wall, mob/user) - if(get_dist(on_wall,user) > 1) - return - var/ndir = get_dir(on_wall, user) - if(!(ndir in cardinal)) - return - var/turf/T = get_turf(user) - if(!istype(T, /turf/simulated/floor)) - to_chat(user, "You cannot place \the [src] on this spot!") - return - playsound(src, 'sound/machines/click.ogg', 75, 1) - user.visible_message("\The [user] attaches \the [src] to the wall.", - "You attach \the [src] to the wall.", - "You hear clicking.") - if(istype(user, /mob/living/silicon/robot)) //Robots cannot unequip/drop items, for Safety Reasons. - forceMove(T) - user.drop_item(T) - anchored = TRUE - on_anchored() - switch(ndir) - if(NORTH) - pixel_y = -31 - if(SOUTH) - pixel_y = 31 - if(EAST) - pixel_x = -31 - if(WEST) - pixel_x = 31 - -/obj/item/device/electronic_assembly/wallmount/on_unanchored() - pixel_x = 0 - pixel_y = 0 - ..() - -/obj/item/device/electronic_assembly/wallmount/heavy - name = "heavy wall-mounted electronic assembly" - icon_state = "setup_wallmount_large" - desc = "It's a case, for building large electronics with. It has a magnetized backing \ - to allow it to stick to walls." - w_class = ITEMSIZE_LARGE - max_components = IC_COMPONENTS_BASE * 4 - max_complexity = IC_COMPLEXITY_BASE * 4 - -/obj/item/device/electronic_assembly/wallmount/light - name = "light wall-mounted electronic assembly" - icon_state = "setup_wallmount_small" - desc = "It's a case, for building small electronics with. It has a magnetized backing \ - to allow it to stick to walls." - w_class = ITEMSIZE_SMALL - max_components = IC_COMPONENTS_BASE - max_complexity = IC_COMPLEXITY_BASE - -/obj/item/device/electronic_assembly/wallmount/tiny - name = "tiny wall-mounted electronic assembly" - icon_state = "setup_wallmount_tiny" - desc = "It's a case, for building tiny electronics with. It has a magnetized backing \ - to allow it to stick to walls." - w_class = ITEMSIZE_TINY - max_components = IC_COMPONENTS_BASE / 2 +// Generic subtypes without a lot of special code. + +// Small assemblies. + +/obj/item/device/electronic_assembly/default + name = "type-a electronic assembly" + +/obj/item/device/electronic_assembly/calc + name = "type-b electronic assembly" + icon_state = "setup_small_calc" + desc = "It's a case, for building small electronics with. This one resembles a pocket calculator." + +/obj/item/device/electronic_assembly/clam + name = "type-c electronic assembly" + icon_state = "setup_small_clam" + desc = "It's a case, for building small electronics with. This one has a clamshell design." + +/obj/item/device/electronic_assembly/simple + name = "type-d electronic assembly" + icon_state = "setup_small_simple" + desc = "It's a case, for building small electronics with. This one has a simple design." + +/obj/item/device/electronic_assembly/hook + name = "type-e electronic assembly" + icon_state = "setup_small_hook" + desc = "It's a case, for building small electronics with. This one looks like it has a belt clip, but it's purely decorative." + +/obj/item/device/electronic_assembly/pda + name = "type-f electronic assembly" + icon_state = "setup_small_pda" + desc = "It's a case, for building small electronics with. This one resembles a PDA." + +// Tiny assemblies. + +/obj/item/device/electronic_assembly/tiny + name = "electronic device" + icon_state = "setup_device" + desc = "It's a case, for building tiny-sized electronics with." + w_class = ITEMSIZE_TINY + max_components = IC_COMPONENTS_BASE / 2 + max_complexity = IC_COMPLEXITY_BASE / 2 + +/obj/item/device/electronic_assembly/tiny/default + name = "type-a electronic device" + +/obj/item/device/electronic_assembly/tiny/cylinder + name = "type-b electronic device" + icon_state = "setup_device_cylinder" + desc = "It's a case, for building tiny-sized electronics with. This one has a cylindrical design." + +/obj/item/device/electronic_assembly/tiny/scanner + name = "type-c electronic device" + icon_state = "setup_device_scanner" + desc = "It's a case, for building tiny-sized electronics with. This one has a scanner-like design." + +/obj/item/device/electronic_assembly/tiny/hook + name = "type-d electronic device" + icon_state = "setup_device_hook" + desc = "It's a case, for building tiny-sized electronics with. This one looks like it has a belt clip, but it's purely decorative." + +/obj/item/device/electronic_assembly/tiny/box + name = "type-e electronic device" + icon_state = "setup_device_box" + desc = "It's a case, for building tiny-sized electronics with. This one has a boxy design." + +// Medium assemblies. + +/obj/item/device/electronic_assembly/medium + name = "electronic mechanism" + icon_state = "setup_medium" + desc = "It's a case, for building medium-sized electronics with." + w_class = ITEMSIZE_NORMAL + max_components = IC_COMPONENTS_BASE * 2 + max_complexity = IC_COMPLEXITY_BASE * 2 + +/obj/item/device/electronic_assembly/medium/default + name = "type-a electronic mechanism" + +/obj/item/device/electronic_assembly/medium/box + name = "type-b electronic mechanism" + icon_state = "setup_medium_box" + desc = "It's a case, for building medium-sized electronics with. This one has a boxy design." + +/obj/item/device/electronic_assembly/medium/clam + name = "type-c electronic mechanism" + icon_state = "setup_medium_clam" + desc = "It's a case, for building medium-sized electronics with. This one has a clamshell design." + +/obj/item/device/electronic_assembly/medium/medical + name = "type-d electronic mechanism" + icon_state = "setup_medium_med" + desc = "It's a case, for building medium-sized electronics with. This one resembles some type of medical apparatus." + +/obj/item/device/electronic_assembly/medium/gun + name = "type-e electronic mechanism" + icon_state = "setup_medium_gun" + item_state = "circuitgun" + desc = "It's a case, for building medium-sized electronics with. This one resembles a gun, or some type of tool, \ + if you're feeling optimistic." +// can_fire_equipped = TRUE + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_guns.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_guns.dmi', + ) + +/obj/item/device/electronic_assembly/medium/radio + name = "type-f electronic mechanism" + icon_state = "setup_medium_radio" + desc = "It's a case, for building medium-sized electronics with. This one resembles an old radio." + +// Large assemblies. + +/obj/item/device/electronic_assembly/large + name = "electronic machine" + icon_state = "setup_large" + desc = "It's a case, for building large electronics with." + w_class = ITEMSIZE_LARGE + max_components = IC_COMPONENTS_BASE * 4 + max_complexity = IC_COMPLEXITY_BASE * 4 + can_anchor = TRUE + +/obj/item/device/electronic_assembly/large/default + name = "type-a electronic machine" + +/obj/item/device/electronic_assembly/large/scope + name = "type-b electronic machine" + icon_state = "setup_large_scope" + desc = "It's a case, for building large electronics with. This one resembles an oscilloscope." + +/obj/item/device/electronic_assembly/large/terminal + name = "type-c electronic machine" + icon_state = "setup_large_terminal" + desc = "It's a case, for building large electronics with. This one resembles a computer terminal." + +/obj/item/device/electronic_assembly/large/arm + name = "type-d electronic machine" + icon_state = "setup_large_arm" + desc = "It's a case, for building large electronics with. This one resembles a robotic arm." + +/obj/item/device/electronic_assembly/large/tall + name = "type-e electronic machine" + icon_state = "setup_large_tall" + desc = "It's a case, for building large electronics with. This one has a tall design." + +/obj/item/device/electronic_assembly/large/industrial + name = "type-f electronic machine" + icon_state = "setup_large_industrial" + desc = "It's a case, for building large electronics with. This one resembles some kind of industrial machinery." + +// Drone assemblies, which can move with the locomotion circuit. + +/obj/item/device/electronic_assembly/drone + name = "electronic drone" + icon_state = "setup_drone" + desc = "It's a case, for building mobile electronics with." + w_class = ITEMSIZE_NORMAL + max_components = IC_COMPONENTS_BASE * 1.5 + max_complexity = IC_COMPLEXITY_BASE * 1.5 + can_anchor = FALSE + +/obj/item/device/electronic_assembly/drone/can_move() + return TRUE + +/obj/item/device/electronic_assembly/drone/default + name = "type-a electronic drone" + +/obj/item/device/electronic_assembly/drone/arms + name = "type-b electronic drone" + icon_state = "setup_drone_arms" + desc = "It's a case, for building mobile electronics with. This one is armed and dangerous." + +/obj/item/device/electronic_assembly/drone/secbot + name = "type-c electronic drone" + icon_state = "setup_drone_secbot" + desc = "It's a case, for building mobile electronics with. This one resembles a Securitron." + +/obj/item/device/electronic_assembly/drone/medbot + name = "type-d electronic drone" + icon_state = "setup_drone_medbot" + desc = "It's a case, for building mobile electronics with. This one resembles a Medibot." + +/obj/item/device/electronic_assembly/drone/genbot + name = "type-e electronic drone" + icon_state = "setup_drone_genbot" + desc = "It's a case, for building mobile electronics with. This one has a generic bot design." + +/obj/item/device/electronic_assembly/drone/android + name = "type-f electronic drone" + icon_state = "setup_drone_android" + desc = "It's a case, for building mobile electronics with. This one has a hominoid design." + +// Wall mounted assemblies. + +/obj/item/device/electronic_assembly/wallmount + name = "wall-mounted electronic assembly" + icon_state = "setup_wallmount_medium" + desc = "It's a case, for building medium-sized electronics with. It has a magnetized \ + backing to allow it to stick to walls." + w_class = ITEMSIZE_NORMAL + max_components = IC_COMPONENTS_BASE * 2 + max_complexity = IC_COMPLEXITY_BASE * 2 + can_anchor = TRUE + +/obj/item/device/electronic_assembly/wallmount/proc/mount_assembly(turf/on_wall, mob/user) + if(get_dist(on_wall,user) > 1) + return + var/ndir = get_dir(on_wall, user) + if(!(ndir in cardinal)) + return + var/turf/T = get_turf(user) + if(!istype(T, /turf/simulated/floor)) + to_chat(user, "You cannot place \the [src] on this spot!") + return + playsound(src, 'sound/machines/click.ogg', 75, 1) + user.visible_message("\The [user] attaches \the [src] to the wall.", + "You attach \the [src] to the wall.", + "You hear clicking.") + if(istype(user, /mob/living/silicon/robot)) //Robots cannot unequip/drop items, for Safety Reasons. + forceMove(T) + user.drop_item(T) + anchored = TRUE + on_anchored() + switch(ndir) + if(NORTH) + pixel_y = -31 + if(SOUTH) + pixel_y = 31 + if(EAST) + pixel_x = -31 + if(WEST) + pixel_x = 31 + +/obj/item/device/electronic_assembly/wallmount/on_unanchored() + pixel_x = 0 + pixel_y = 0 + ..() + +/obj/item/device/electronic_assembly/wallmount/heavy + name = "heavy wall-mounted electronic assembly" + icon_state = "setup_wallmount_large" + desc = "It's a case, for building large electronics with. It has a magnetized backing \ + to allow it to stick to walls." + w_class = ITEMSIZE_LARGE + max_components = IC_COMPONENTS_BASE * 4 + max_complexity = IC_COMPLEXITY_BASE * 4 + +/obj/item/device/electronic_assembly/wallmount/light + name = "light wall-mounted electronic assembly" + icon_state = "setup_wallmount_small" + desc = "It's a case, for building small electronics with. It has a magnetized backing \ + to allow it to stick to walls." + w_class = ITEMSIZE_SMALL + max_components = IC_COMPONENTS_BASE + max_complexity = IC_COMPLEXITY_BASE + +/obj/item/device/electronic_assembly/wallmount/tiny + name = "tiny wall-mounted electronic assembly" + icon_state = "setup_wallmount_tiny" + desc = "It's a case, for building tiny electronics with. It has a magnetized backing \ + to allow it to stick to walls." + w_class = ITEMSIZE_TINY + max_components = IC_COMPONENTS_BASE / 2 max_complexity = IC_COMPLEXITY_BASE / 2 \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/assemblies/implant.dm b/code/modules/integrated_electronics/core/assemblies/implant.dm index cb40e317061..dd3f19dc4d9 100644 --- a/code/modules/integrated_electronics/core/assemblies/implant.dm +++ b/code/modules/integrated_electronics/core/assemblies/implant.dm @@ -1,18 +1,18 @@ -// Note that this is contained inside an actual implant subtype. -// See code/game/objects/items/weapons/implants/implantcircuits.dm for where this gets held. - -/obj/item/device/electronic_assembly/implant - name = "electronic implant" - icon_state = "setup_implant" - desc = "It's a case, for building very tiny electronics with." - w_class = ITEMSIZE_TINY - max_components = IC_COMPONENTS_BASE / 2 - max_complexity = IC_COMPLEXITY_BASE / 2 - var/obj/item/weapon/implant/integrated_circuit/implant = null - -/obj/item/device/electronic_assembly/implant/tgui_host() - return implant.tgui_host() - -/obj/item/device/electronic_assembly/implant/update_icon() - ..() +// Note that this is contained inside an actual implant subtype. +// See code/game/objects/items/weapons/implants/implantcircuits.dm for where this gets held. + +/obj/item/device/electronic_assembly/implant + name = "electronic implant" + icon_state = "setup_implant" + desc = "It's a case, for building very tiny electronics with." + w_class = ITEMSIZE_TINY + max_components = IC_COMPONENTS_BASE / 2 + max_complexity = IC_COMPLEXITY_BASE / 2 + var/obj/item/weapon/implant/integrated_circuit/implant = null + +/obj/item/device/electronic_assembly/implant/tgui_host() + return implant.tgui_host() + +/obj/item/device/electronic_assembly/implant/update_icon() + ..() implant.icon_state = icon_state \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/detailer.dm b/code/modules/integrated_electronics/core/detailer.dm index adc08861748..21ae07f7d10 100644 --- a/code/modules/integrated_electronics/core/detailer.dm +++ b/code/modules/integrated_electronics/core/detailer.dm @@ -1,76 +1,76 @@ -/obj/item/device/integrated_electronics/detailer - name = "assembly detailer" - desc = "A combination autopainter and flash anodizer designed to give electronic assemblies a colorful, wear-resistant finish." - icon = 'icons/obj/integrated_electronics/electronic_tools.dmi' - icon_state = "detailer" - item_flags = NOBLUDGEON - w_class = ITEMSIZE_SMALL - var/detail_color = COLOR_ASSEMBLY_WHITE - var/list/color_list = list( - "dark gray" = COLOR_ASSEMBLY_BLACK, - "machine gray" = COLOR_ASSEMBLY_BGRAY, - "white" = COLOR_ASSEMBLY_WHITE, - "red" = COLOR_ASSEMBLY_RED, - "orange" = COLOR_ASSEMBLY_ORANGE, - "beige" = COLOR_ASSEMBLY_BEIGE, - "brown" = COLOR_ASSEMBLY_BROWN, - "gold" = COLOR_ASSEMBLY_GOLD, - "yellow" = COLOR_ASSEMBLY_YELLOW, - "gurkha" = COLOR_ASSEMBLY_GURKHA, - "light green" = COLOR_ASSEMBLY_LGREEN, - "green" = COLOR_ASSEMBLY_GREEN, - "light blue" = COLOR_ASSEMBLY_LBLUE, - "blue" = COLOR_ASSEMBLY_BLUE, - "purple" = COLOR_ASSEMBLY_PURPLE, - "hot pink" = COLOR_ASSEMBLY_HOT_PINK - ) - -/obj/item/device/integrated_electronics/detailer/Initialize() - update_icon() - return ..() - -/obj/item/device/integrated_electronics/detailer/update_icon() - cut_overlays() - var/mutable_appearance/detail_overlay = mutable_appearance('icons/obj/integrated_electronics/electronic_tools.dmi', "detailer-color") - detail_overlay.color = detail_color - add_overlay(detail_overlay) - -/obj/item/device/integrated_electronics/detailer/tgui_state(mob/user) - return GLOB.tgui_inventory_state - -/obj/item/device/integrated_electronics/detailer/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "ICDetailer", name) - ui.open() - -/obj/item/device/integrated_electronics/detailer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) - var/list/data = ..() - data["detail_color"] = detail_color - data["color_list"] = color_list - return data - -/obj/item/device/integrated_electronics/detailer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - switch(action) - if("change_color") - if(!(params["color"] in color_list)) - return // to prevent href exploits causing runtimes - detail_color = color_list[params["color"]] - update_icon() - return TRUE - -/obj/item/device/integrated_electronics/detailer/attack_self(mob/user) - tgui_interact(user) - - // Leaving this commented out in case someone decides that this would be better as an "any color" selection system - // Just uncomment this and get rid of all of the TGUI bullshit lol - // if(!in_range(user, src)) - // return - // var/new_color = input(user, "Pick a color", "Color Selection", detail_color) as color|null - // if(!new_color) - // return - // detail_color = new_color +/obj/item/device/integrated_electronics/detailer + name = "assembly detailer" + desc = "A combination autopainter and flash anodizer designed to give electronic assemblies a colorful, wear-resistant finish." + icon = 'icons/obj/integrated_electronics/electronic_tools.dmi' + icon_state = "detailer" + item_flags = NOBLUDGEON + w_class = ITEMSIZE_SMALL + var/detail_color = COLOR_ASSEMBLY_WHITE + var/list/color_list = list( + "dark gray" = COLOR_ASSEMBLY_BLACK, + "machine gray" = COLOR_ASSEMBLY_BGRAY, + "white" = COLOR_ASSEMBLY_WHITE, + "red" = COLOR_ASSEMBLY_RED, + "orange" = COLOR_ASSEMBLY_ORANGE, + "beige" = COLOR_ASSEMBLY_BEIGE, + "brown" = COLOR_ASSEMBLY_BROWN, + "gold" = COLOR_ASSEMBLY_GOLD, + "yellow" = COLOR_ASSEMBLY_YELLOW, + "gurkha" = COLOR_ASSEMBLY_GURKHA, + "light green" = COLOR_ASSEMBLY_LGREEN, + "green" = COLOR_ASSEMBLY_GREEN, + "light blue" = COLOR_ASSEMBLY_LBLUE, + "blue" = COLOR_ASSEMBLY_BLUE, + "purple" = COLOR_ASSEMBLY_PURPLE, + "hot pink" = COLOR_ASSEMBLY_HOT_PINK + ) + +/obj/item/device/integrated_electronics/detailer/Initialize() + update_icon() + return ..() + +/obj/item/device/integrated_electronics/detailer/update_icon() + cut_overlays() + var/mutable_appearance/detail_overlay = mutable_appearance('icons/obj/integrated_electronics/electronic_tools.dmi', "detailer-color") + detail_overlay.color = detail_color + add_overlay(detail_overlay) + +/obj/item/device/integrated_electronics/detailer/tgui_state(mob/user) + return GLOB.tgui_inventory_state + +/obj/item/device/integrated_electronics/detailer/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ICDetailer", name) + ui.open() + +/obj/item/device/integrated_electronics/detailer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + data["detail_color"] = detail_color + data["color_list"] = color_list + return data + +/obj/item/device/integrated_electronics/detailer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("change_color") + if(!(params["color"] in color_list)) + return // to prevent href exploits causing runtimes + detail_color = color_list[params["color"]] + update_icon() + return TRUE + +/obj/item/device/integrated_electronics/detailer/attack_self(mob/user) + tgui_interact(user) + + // Leaving this commented out in case someone decides that this would be better as an "any color" selection system + // Just uncomment this and get rid of all of the TGUI bullshit lol + // if(!in_range(user, src)) + // return + // var/new_color = input(user, "Pick a color", "Color Selection", detail_color) as color|null + // if(!new_color) + // return + // detail_color = new_color // update_icon() \ No newline at end of file diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm index 00712e4f7cc..db94ed64010 100644 --- a/code/modules/library/lib_items.dm +++ b/code/modules/library/lib_items.dm @@ -1,410 +1,410 @@ -/* Library Items - * - * Contains: - * Bookcase - * Book - * Barcode Scanner - */ - - -/* - * Bookcase - */ - -/obj/structure/bookcase - name = "bookcase" - desc = "A set of wooden shelves, perfect for placing books on." - icon = 'icons/obj/library.dmi' - icon_state = "book-0" - anchored = TRUE - density = TRUE - opacity = 1 - -/obj/structure/bookcase/Initialize() - . = ..() - for(var/obj/item/I in loc) - if(istype(I, /obj/item/weapon/book)) - I.loc = src - update_icon() - -/obj/structure/bookcase/attackby(obj/item/O as obj, mob/user as mob) - if(istype(O, /obj/item/weapon/book)) - user.drop_item() - O.loc = src - update_icon() - else if(istype(O, /obj/item/weapon/pen)) - var/newname = sanitizeSafe(tgui_input_text(usr, "What would you like to title this bookshelf?", null, null, MAX_NAME_LEN), MAX_NAME_LEN) - if(!newname) - return - else - name = ("bookcase ([newname])") - else if(O.has_tool_quality(TOOL_WRENCH)) - playsound(src, O.usesound, 100, 1) - to_chat(user, (anchored ? "You unfasten \the [src] from the floor." : "You secure \the [src] to the floor.")) - anchored = !anchored - else if(O.has_tool_quality(TOOL_SCREWDRIVER)) - playsound(src, O.usesound, 75, 1) - to_chat(user, "You begin dismantling \the [src].") - if(do_after(user,25 * O.toolspeed)) - to_chat(user, "You dismantle \the [src].") - new /obj/item/stack/material/wood(get_turf(src), 3) - for(var/obj/item/weapon/book/b in contents) - b.loc = (get_turf(src)) - qdel(src) - - else - ..() - -/obj/structure/bookcase/attack_hand(var/mob/user as mob) - if(contents.len) - var/obj/item/weapon/book/choice = tgui_input_list(usr, "Which book would you like to remove from the shelf?", "Book Selection", contents) - if(choice) - if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - return - if(ishuman(user)) - if(!user.get_active_hand()) - user.put_in_hands(choice) - else - choice.loc = get_turf(src) - update_icon() - -/obj/structure/bookcase/ex_act(severity) - switch(severity) - if(1.0) - for(var/obj/item/weapon/book/b in contents) - qdel(b) - qdel(src) - return - if(2.0) - for(var/obj/item/weapon/book/b in contents) - if (prob(50)) b.loc = (get_turf(src)) - else qdel(b) - qdel(src) - return - if(3.0) - if (prob(50)) - for(var/obj/item/weapon/book/b in contents) - b.loc = (get_turf(src)) - qdel(src) - return - else - return - -/obj/structure/bookcase/update_icon() - if(contents.len < 5) - icon_state = "book-[contents.len]" - else - icon_state = "book-5" - -/* -Book Cart -*/ - -/obj/structure/bookcase/bookcart - name = "book cart" - icon = 'icons/obj/library.dmi' - icon_state = "bookcart-0" - anchored = FALSE - opacity = 0 - -/obj/structure/bookcase/bookcart/attackby(obj/item/O as obj, mob/user as mob) - if(istype(O, /obj/item/weapon/book)) - user.drop_item() - O.loc = src - update_icon() - else - return - -/obj/structure/bookcase/bookcart/update_icon() - if(contents.len < 5) - icon_state = "bookcart-[contents.len]" - else - icon_state = "bookcart-5" - -/* -Book Cart End -*/ - -/obj/structure/bookcase/manuals/medical - name = "Medical Manuals bookcase" - -/obj/structure/bookcase/manuals/medical/New() - ..() - new /obj/item/weapon/book/manual/medical_cloning(src) - new /obj/item/weapon/book/manual/medical_diagnostics_manual(src) - new /obj/item/weapon/book/manual/medical_diagnostics_manual(src) - new /obj/item/weapon/book/manual/medical_diagnostics_manual(src) - update_icon() - - -/obj/structure/bookcase/manuals/engineering - name = "Engineering Manuals bookcase" - -/obj/structure/bookcase/manuals/engineering/New() - ..() - new /obj/item/weapon/book/manual/engineering_construction(src) - new /obj/item/weapon/book/manual/engineering_particle_accelerator(src) - new /obj/item/weapon/book/manual/engineering_hacking(src) - new /obj/item/weapon/book/manual/engineering_guide(src) - new /obj/item/weapon/book/manual/atmospipes(src) - new /obj/item/weapon/book/manual/engineering_singularity_safety(src) - new /obj/item/weapon/book/manual/evaguide(src) - update_icon() - -/obj/structure/bookcase/manuals/research_and_development - name = "R&D Manuals bookcase" - -/obj/structure/bookcase/manuals/research_and_development/New() - ..() - new /obj/item/weapon/book/manual/research_and_development(src) - update_icon() - - -/* - * Book - */ -/obj/item/weapon/book - name = "book" - icon = 'icons/obj/library.dmi' - icon_state ="book" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_books.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_books.dmi' - ) - item_state = "book" - throw_speed = 1 - throw_range = 5 - flags = NOCONDUCT - w_class = ITEMSIZE_NORMAL //upped to three because books are, y'know, pretty big. (and you could hide them inside eachother recursively forever) - attack_verb = list("bashed", "whacked", "educated") - var/dat // Actual page content - var/due_date = 0 // Game time in 1/10th seconds - var/author // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - var/libcategory = "Miscellaneous" // The library category this book sits in. "Fiction", "Non-Fiction", "Adult", "Reference", "Religion" - var/unique = 0 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified - var/title // The real name of the book. - var/carved = 0 // Has the book been hollowed out for use as a secret storage item? - var/obj/item/store //What's in the book? - drop_sound = 'sound/items/drop/book.ogg' - pickup_sound = 'sound/items/pickup/book.ogg' - -/obj/item/weapon/book/attack_self(var/mob/user as mob) - if(carved) - if(store) - to_chat(user, "[store] falls out of [title]!") - store.loc = get_turf(src.loc) - store = null - return - else - to_chat(user, "The pages of [title] have been cut out!") - return - if(src.dat) - user << browse("Penned by [author].
                    " + "[dat]", "window=book") - user.visible_message("[user] opens a book titled \"[src.title]\" and begins reading intently.") - playsound(src, 'sound/bureaucracy/bookopen.ogg', 50, 1) - onclose(user, "book") - playsound(src, 'sound/bureaucracy/bookclose.ogg', 50, 1) - else - to_chat(user, "This book is completely blank!") - -/obj/item/weapon/book/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(carved) - if(!store) - if(W.w_class < ITEMSIZE_LARGE) - user.drop_item() - W.loc = src - store = W - to_chat(user, "You put [W] in [title].") - return - else - to_chat(user, "[W] won't fit in [title].") - return - else - to_chat(user, "There's already something in [title]!") - return - if(istype(W, /obj/item/weapon/pen)) - if(unique) - to_chat(user, "These pages don't seem to take the ink well. Looks like you can't modify it.") - return - var/choice = tgui_input_list(usr, "What would you like to change?", "Change What?", list("Title", "Contents", "Author", "Cancel")) - switch(choice) - if("Title") - var/newtitle = reject_bad_text(sanitizeSafe(tgui_input_text(usr, "Write a new title:"))) - if(!newtitle) - to_chat(usr, "The title is invalid.") - return - else - src.name = newtitle - src.title = newtitle - if("Contents") - var/content = sanitize(input(usr, "Write your book's contents (HTML NOT allowed):") as message|null, MAX_BOOK_MESSAGE_LEN) - if(!content) - to_chat(usr, "The content is invalid.") - return - else - src.dat += content - if("Author") - var/newauthor = sanitize(tgui_input_text(usr, "Write the author's name:")) - if(!newauthor) - to_chat(usr, "The name is invalid.") - return - else - src.author = newauthor - else - return - else if(istype(W, /obj/item/weapon/barcodescanner)) - var/obj/item/weapon/barcodescanner/scanner = W - if(!scanner.computer) - to_chat(user, "[W]'s screen flashes: 'No associated computer found!'") - else - switch(scanner.mode) - if(0) - scanner.book = src - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer.'") - if(1) - scanner.book = src - scanner.computer.buffer_book = src.name - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book title stored in associated computer buffer.'") - if(2) - scanner.book = src - for(var/datum/borrowbook/b in scanner.computer.checkouts) - if(b.bookname == src.name) - scanner.computer.checkouts.Remove(b) - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book has been checked in.'") - return - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. No active check-out record found for current title.'") - if(3) - scanner.book = src - for(var/obj/item/weapon/book in scanner.computer.inventory) - if(book == src) - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title already present in inventory, aborting to avoid duplicate entry.'") - return - scanner.computer.inventory.Add(src) - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'") - else if(istype(W, /obj/item/weapon/material/knife) || W.has_tool_quality(TOOL_WIRECUTTER)) - if(carved) return - to_chat(user, "You begin to carve out [title].") - if(do_after(user, 30)) - to_chat(user, "You carve out the pages from [title]! You didn't want to read it anyway.") - playsound(src, 'sound/bureaucracy/papercrumple.ogg', 50, 1) - new /obj/item/weapon/shreddedp(get_turf(src)) - carved = 1 - return - else - ..() - -/obj/item/weapon/book/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - if(user.zone_sel.selecting == O_EYES) - user.visible_message("You open up the book and show it to [M]. ", \ - " [user] opens up a book and shows it to [M]. ") - M << browse("Penned by [author].
                    " + "[dat]", "window=book") - user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //to prevent spam - -/* -* Book Bundle (Multi-page book) -*/ - -/obj/item/weapon/book/bundle - var/page = 1 //current page - var/list/pages = list() //the contents of each page - -/obj/item/weapon/book/bundle/proc/show_content(mob/user as mob) - if(!pages.len) - return - var/dat - var/obj/item/weapon/W = pages[page] - // first - if(page == 1) - dat+= "
                    " - dat+= "

                    " - // last - else if(page == pages.len) - dat+= "" - dat+= "

                    " - // middle pages - else - dat+= "" - dat+= "

                    " - if(istype(pages[page], /obj/item/weapon/paper)) - var/obj/item/weapon/paper/P = W - if(!(istype(usr, /mob/living/carbon/human) || isobserver(usr) || istype(usr, /mob/living/silicon))) - dat += "[P.name][stars(P.info)][P.stamps]" - else - dat += "[P.name][P.info][P.stamps]" - user << browse(dat, "window=[name]") - else if(istype(pages[page], /obj/item/weapon/photo)) - var/obj/item/weapon/photo/P = W - user << browse_rsc(P.img, "tmp_photo.png") - user << browse(dat + "[P.name]" \ - + "" \ - + "
                    Written on the back:
                    [P.scribble]" : null]"\ - + "", "window=[name]") - else if(!isnull(pages[page])) - if(!(istype(usr, /mob/living/carbon/human) || isobserver(usr) || istype(usr, /mob/living/silicon))) - dat += "Page [page][stars(pages[page])]" - else - dat += "Page [page][pages[page]]" - user << browse(dat, "window=[name]") - -/obj/item/weapon/book/bundle/attack_self(mob/user as mob) - src.show_content(user) - add_fingerprint(usr) - update_icon() - return - -/obj/item/weapon/book/bundle/Topic(href, href_list) - if(..()) - return 1 - if((src in usr.contents) || (istype(src.loc, /obj/item/weapon/folder) && (src.loc in usr.contents))) - usr.set_machine(src) - if(href_list["next_page"]) - if(page != pages.len) - page++ - playsound(src, "pageturn", 50, 1) - if(href_list["prev_page"]) - if(page > 1) - page-- - playsound(src, "pageturn", 50, 1) - src.attack_self(usr) - updateUsrDialog() - else - to_chat(usr, "You need to hold it in your hands!") - -/* - * Barcode Scanner - */ -/obj/item/weapon/barcodescanner - name = "barcode scanner" - icon = 'icons/obj/library.dmi' - icon_state ="scanner" - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_SMALL - var/obj/machinery/librarycomp/computer // Associated computer - Modes 1 to 3 use this - var/obj/item/weapon/book/book // Currently scanned book - var/mode = 0 // 0 - Scan only, 1 - Scan and Set Buffer, 2 - Scan and Attempt to Check In, 3 - Scan and Attempt to Add to Inventory - -/obj/item/weapon/barcodescanner/attack_self(mob/user as mob) - mode += 1 - if(mode > 3) - mode = 0 - to_chat(user, "[src] Status Display:") - var/modedesc - switch(mode) - if(0) - modedesc = "Scan book to local buffer." - if(1) - modedesc = "Scan book to local buffer and set associated computer buffer to match." - if(2) - modedesc = "Scan book to local buffer, attempt to check in scanned book." - if(3) - modedesc = "Scan book to local buffer, attempt to add book to general inventory." - else - modedesc = "ERROR" - to_chat(user, " - Mode [mode] : [modedesc]") - if(src.computer) - to_chat(user, span_green("Computer has been associated with this unit.")) - else - to_chat(user, span_red("No associated computer found. Only local scans will function properly.")) - to_chat(user, "\n") +/* Library Items + * + * Contains: + * Bookcase + * Book + * Barcode Scanner + */ + + +/* + * Bookcase + */ + +/obj/structure/bookcase + name = "bookcase" + desc = "A set of wooden shelves, perfect for placing books on." + icon = 'icons/obj/library.dmi' + icon_state = "book-0" + anchored = TRUE + density = TRUE + opacity = 1 + +/obj/structure/bookcase/Initialize() + . = ..() + for(var/obj/item/I in loc) + if(istype(I, /obj/item/weapon/book)) + I.loc = src + update_icon() + +/obj/structure/bookcase/attackby(obj/item/O as obj, mob/user as mob) + if(istype(O, /obj/item/weapon/book)) + user.drop_item() + O.loc = src + update_icon() + else if(istype(O, /obj/item/weapon/pen)) + var/newname = sanitizeSafe(tgui_input_text(usr, "What would you like to title this bookshelf?", null, null, MAX_NAME_LEN), MAX_NAME_LEN) + if(!newname) + return + else + name = ("bookcase ([newname])") + else if(O.has_tool_quality(TOOL_WRENCH)) + playsound(src, O.usesound, 100, 1) + to_chat(user, (anchored ? "You unfasten \the [src] from the floor." : "You secure \the [src] to the floor.")) + anchored = !anchored + else if(O.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(src, O.usesound, 75, 1) + to_chat(user, "You begin dismantling \the [src].") + if(do_after(user,25 * O.toolspeed)) + to_chat(user, "You dismantle \the [src].") + new /obj/item/stack/material/wood(get_turf(src), 3) + for(var/obj/item/weapon/book/b in contents) + b.loc = (get_turf(src)) + qdel(src) + + else + ..() + +/obj/structure/bookcase/attack_hand(var/mob/user as mob) + if(contents.len) + var/obj/item/weapon/book/choice = tgui_input_list(usr, "Which book would you like to remove from the shelf?", "Book Selection", contents) + if(choice) + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + return + if(ishuman(user)) + if(!user.get_active_hand()) + user.put_in_hands(choice) + else + choice.loc = get_turf(src) + update_icon() + +/obj/structure/bookcase/ex_act(severity) + switch(severity) + if(1.0) + for(var/obj/item/weapon/book/b in contents) + qdel(b) + qdel(src) + return + if(2.0) + for(var/obj/item/weapon/book/b in contents) + if (prob(50)) b.loc = (get_turf(src)) + else qdel(b) + qdel(src) + return + if(3.0) + if (prob(50)) + for(var/obj/item/weapon/book/b in contents) + b.loc = (get_turf(src)) + qdel(src) + return + else + return + +/obj/structure/bookcase/update_icon() + if(contents.len < 5) + icon_state = "book-[contents.len]" + else + icon_state = "book-5" + +/* +Book Cart +*/ + +/obj/structure/bookcase/bookcart + name = "book cart" + icon = 'icons/obj/library.dmi' + icon_state = "bookcart-0" + anchored = FALSE + opacity = 0 + +/obj/structure/bookcase/bookcart/attackby(obj/item/O as obj, mob/user as mob) + if(istype(O, /obj/item/weapon/book)) + user.drop_item() + O.loc = src + update_icon() + else + return + +/obj/structure/bookcase/bookcart/update_icon() + if(contents.len < 5) + icon_state = "bookcart-[contents.len]" + else + icon_state = "bookcart-5" + +/* +Book Cart End +*/ + +/obj/structure/bookcase/manuals/medical + name = "Medical Manuals bookcase" + +/obj/structure/bookcase/manuals/medical/New() + ..() + new /obj/item/weapon/book/manual/medical_cloning(src) + new /obj/item/weapon/book/manual/medical_diagnostics_manual(src) + new /obj/item/weapon/book/manual/medical_diagnostics_manual(src) + new /obj/item/weapon/book/manual/medical_diagnostics_manual(src) + update_icon() + + +/obj/structure/bookcase/manuals/engineering + name = "Engineering Manuals bookcase" + +/obj/structure/bookcase/manuals/engineering/New() + ..() + new /obj/item/weapon/book/manual/engineering_construction(src) + new /obj/item/weapon/book/manual/engineering_particle_accelerator(src) + new /obj/item/weapon/book/manual/engineering_hacking(src) + new /obj/item/weapon/book/manual/engineering_guide(src) + new /obj/item/weapon/book/manual/atmospipes(src) + new /obj/item/weapon/book/manual/engineering_singularity_safety(src) + new /obj/item/weapon/book/manual/evaguide(src) + update_icon() + +/obj/structure/bookcase/manuals/research_and_development + name = "R&D Manuals bookcase" + +/obj/structure/bookcase/manuals/research_and_development/New() + ..() + new /obj/item/weapon/book/manual/research_and_development(src) + update_icon() + + +/* + * Book + */ +/obj/item/weapon/book + name = "book" + icon = 'icons/obj/library.dmi' + icon_state ="book" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_books.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_books.dmi' + ) + item_state = "book" + throw_speed = 1 + throw_range = 5 + flags = NOCONDUCT + w_class = ITEMSIZE_NORMAL //upped to three because books are, y'know, pretty big. (and you could hide them inside eachother recursively forever) + attack_verb = list("bashed", "whacked", "educated") + var/dat // Actual page content + var/due_date = 0 // Game time in 1/10th seconds + var/author // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + var/libcategory = "Miscellaneous" // The library category this book sits in. "Fiction", "Non-Fiction", "Adult", "Reference", "Religion" + var/unique = 0 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified + var/title // The real name of the book. + var/carved = 0 // Has the book been hollowed out for use as a secret storage item? + var/obj/item/store //What's in the book? + drop_sound = 'sound/items/drop/book.ogg' + pickup_sound = 'sound/items/pickup/book.ogg' + +/obj/item/weapon/book/attack_self(var/mob/user as mob) + if(carved) + if(store) + to_chat(user, "[store] falls out of [title]!") + store.loc = get_turf(src.loc) + store = null + return + else + to_chat(user, "The pages of [title] have been cut out!") + return + if(src.dat) + user << browse("Penned by [author].
                    " + "[dat]", "window=book") + user.visible_message("[user] opens a book titled \"[src.title]\" and begins reading intently.") + playsound(src, 'sound/bureaucracy/bookopen.ogg', 50, 1) + onclose(user, "book") + playsound(src, 'sound/bureaucracy/bookclose.ogg', 50, 1) + else + to_chat(user, "This book is completely blank!") + +/obj/item/weapon/book/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(carved) + if(!store) + if(W.w_class < ITEMSIZE_LARGE) + user.drop_item() + W.loc = src + store = W + to_chat(user, "You put [W] in [title].") + return + else + to_chat(user, "[W] won't fit in [title].") + return + else + to_chat(user, "There's already something in [title]!") + return + if(istype(W, /obj/item/weapon/pen)) + if(unique) + to_chat(user, "These pages don't seem to take the ink well. Looks like you can't modify it.") + return + var/choice = tgui_input_list(usr, "What would you like to change?", "Change What?", list("Title", "Contents", "Author", "Cancel")) + switch(choice) + if("Title") + var/newtitle = reject_bad_text(sanitizeSafe(tgui_input_text(usr, "Write a new title:"))) + if(!newtitle) + to_chat(usr, "The title is invalid.") + return + else + src.name = newtitle + src.title = newtitle + if("Contents") + var/content = sanitize(input(usr, "Write your book's contents (HTML NOT allowed):") as message|null, MAX_BOOK_MESSAGE_LEN) + if(!content) + to_chat(usr, "The content is invalid.") + return + else + src.dat += content + if("Author") + var/newauthor = sanitize(tgui_input_text(usr, "Write the author's name:")) + if(!newauthor) + to_chat(usr, "The name is invalid.") + return + else + src.author = newauthor + else + return + else if(istype(W, /obj/item/weapon/barcodescanner)) + var/obj/item/weapon/barcodescanner/scanner = W + if(!scanner.computer) + to_chat(user, "[W]'s screen flashes: 'No associated computer found!'") + else + switch(scanner.mode) + if(0) + scanner.book = src + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer.'") + if(1) + scanner.book = src + scanner.computer.buffer_book = src.name + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book title stored in associated computer buffer.'") + if(2) + scanner.book = src + for(var/datum/borrowbook/b in scanner.computer.checkouts) + if(b.bookname == src.name) + scanner.computer.checkouts.Remove(b) + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book has been checked in.'") + return + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. No active check-out record found for current title.'") + if(3) + scanner.book = src + for(var/obj/item/weapon/book in scanner.computer.inventory) + if(book == src) + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title already present in inventory, aborting to avoid duplicate entry.'") + return + scanner.computer.inventory.Add(src) + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'") + else if(istype(W, /obj/item/weapon/material/knife) || W.has_tool_quality(TOOL_WIRECUTTER)) + if(carved) return + to_chat(user, "You begin to carve out [title].") + if(do_after(user, 30)) + to_chat(user, "You carve out the pages from [title]! You didn't want to read it anyway.") + playsound(src, 'sound/bureaucracy/papercrumple.ogg', 50, 1) + new /obj/item/weapon/shreddedp(get_turf(src)) + carved = 1 + return + else + ..() + +/obj/item/weapon/book/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + if(user.zone_sel.selecting == O_EYES) + user.visible_message("You open up the book and show it to [M]. ", \ + " [user] opens up a book and shows it to [M]. ") + M << browse("Penned by [author].
                    " + "[dat]", "window=book") + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //to prevent spam + +/* +* Book Bundle (Multi-page book) +*/ + +/obj/item/weapon/book/bundle + var/page = 1 //current page + var/list/pages = list() //the contents of each page + +/obj/item/weapon/book/bundle/proc/show_content(mob/user as mob) + if(!pages.len) + return + var/dat + var/obj/item/weapon/W = pages[page] + // first + if(page == 1) + dat+= "" + dat+= "

                    " + // last + else if(page == pages.len) + dat+= "" + dat+= "

                    " + // middle pages + else + dat+= "" + dat+= "

                    " + if(istype(pages[page], /obj/item/weapon/paper)) + var/obj/item/weapon/paper/P = W + if(!(istype(usr, /mob/living/carbon/human) || isobserver(usr) || istype(usr, /mob/living/silicon))) + dat += "[P.name][stars(P.info)][P.stamps]" + else + dat += "[P.name][P.info][P.stamps]" + user << browse(dat, "window=[name]") + else if(istype(pages[page], /obj/item/weapon/photo)) + var/obj/item/weapon/photo/P = W + user << browse_rsc(P.img, "tmp_photo.png") + user << browse(dat + "[P.name]" \ + + "" \ + + "
                    Written on the back:
                    [P.scribble]" : null]"\ + + "", "window=[name]") + else if(!isnull(pages[page])) + if(!(istype(usr, /mob/living/carbon/human) || isobserver(usr) || istype(usr, /mob/living/silicon))) + dat += "Page [page][stars(pages[page])]" + else + dat += "Page [page][pages[page]]" + user << browse(dat, "window=[name]") + +/obj/item/weapon/book/bundle/attack_self(mob/user as mob) + src.show_content(user) + add_fingerprint(usr) + update_icon() + return + +/obj/item/weapon/book/bundle/Topic(href, href_list) + if(..()) + return 1 + if((src in usr.contents) || (istype(src.loc, /obj/item/weapon/folder) && (src.loc in usr.contents))) + usr.set_machine(src) + if(href_list["next_page"]) + if(page != pages.len) + page++ + playsound(src, "pageturn", 50, 1) + if(href_list["prev_page"]) + if(page > 1) + page-- + playsound(src, "pageturn", 50, 1) + src.attack_self(usr) + updateUsrDialog() + else + to_chat(usr, "You need to hold it in your hands!") + +/* + * Barcode Scanner + */ +/obj/item/weapon/barcodescanner + name = "barcode scanner" + icon = 'icons/obj/library.dmi' + icon_state ="scanner" + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_SMALL + var/obj/machinery/librarycomp/computer // Associated computer - Modes 1 to 3 use this + var/obj/item/weapon/book/book // Currently scanned book + var/mode = 0 // 0 - Scan only, 1 - Scan and Set Buffer, 2 - Scan and Attempt to Check In, 3 - Scan and Attempt to Add to Inventory + +/obj/item/weapon/barcodescanner/attack_self(mob/user as mob) + mode += 1 + if(mode > 3) + mode = 0 + to_chat(user, "[src] Status Display:") + var/modedesc + switch(mode) + if(0) + modedesc = "Scan book to local buffer." + if(1) + modedesc = "Scan book to local buffer and set associated computer buffer to match." + if(2) + modedesc = "Scan book to local buffer, attempt to check in scanned book." + if(3) + modedesc = "Scan book to local buffer, attempt to add book to general inventory." + else + modedesc = "ERROR" + to_chat(user, " - Mode [mode] : [modedesc]") + if(src.computer) + to_chat(user, span_green("Computer has been associated with this unit.")) + else + to_chat(user, span_red("No associated computer found. Only local scans will function properly.")) + to_chat(user, "\n") diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index 20094948d78..3af5a829f74 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -1,612 +1,612 @@ -/* Library Machines - * - * Contains: - * Borrowbook datum - * Library Public Computer - * Library Computer - * Library Scanner - * Book Binder - */ - -/* - * Borrowbook datum - */ -/datum/borrowbook // Datum used to keep track of who has borrowed what when and for how long. - var/bookname - var/mobname - var/getdate - var/duedate - -/* - * Library Public Computer - */ -/obj/machinery/librarypubliccomp - name = "visitor computer" - icon = 'icons/obj/library.dmi' - icon_state = "computer" - anchored = TRUE - density = TRUE - var/screenstate = 0 - var/title - var/category = "Any" - var/author - var/SQLquery - -/obj/machinery/librarypubliccomp/attack_hand(var/mob/user as mob) - usr.set_machine(src) - var/dat = "Library Visitor\n" // - switch(screenstate) - if(0) - dat += {"

                    Search Settings


                    - Filter by Title: [title]
                    - Filter by Category: [category]
                    - Filter by Author: [author]
                    - \[Start Search\]
                    "} - if(1) - establish_old_db_connection() - if(!dbcon_old.IsConnected()) - dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance.
                    " - else if(!SQLquery) - dat += "ERROR: Malformed search request. Please contact your system administrator for assistance.
                    " - else - dat += {" - "} - - var/DBQuery/query = dbcon_old.NewQuery(SQLquery) - query.Execute() - - while(query.NextRow()) - var/author = query.item[1] - var/title = query.item[2] - var/category = query.item[3] - var/id = query.item[4] - dat += "" - dat += "
                    AUTHORTITLECATEGORYSS13BN
                    [author][title][category][id]

                    " - dat += "\[Go Back\]
                    " - user << browse(dat, "window=publiclibrary") - onclose(user, "publiclibrary") - -/obj/machinery/librarypubliccomp/Topic(href, href_list) - if(..()) - usr << browse(null, "window=publiclibrary") - onclose(usr, "publiclibrary") - return - - if(href_list["settitle"]) - var/newtitle = tgui_input_text(usr, "Enter a title to search for:") - if(newtitle) - title = sanitize(newtitle) - else - title = null - title = sanitizeSQL(title) - if(href_list["setcategory"]) - var/newcategory = tgui_input_list(usr, "Choose a category to search for:", "Category", list("Any", "Fiction", "Non-Fiction", "Adult", "Reference", "Religion")) - if(newcategory) - category = sanitize(newcategory) - else - category = "Any" - category = sanitizeSQL(category) - if(href_list["setauthor"]) - var/newauthor = tgui_input_text(usr, "Enter an author to search for:") - if(newauthor) - author = sanitize(newauthor) - else - author = null - author = sanitizeSQL(author) - if(href_list["search"]) - SQLquery = "SELECT author, title, category, id FROM library WHERE " - if(category == "Any") - SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%'" - else - SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%' AND category='[category]'" - screenstate = 1 - - if(href_list["back"]) - screenstate = 0 - - src.add_fingerprint(usr) - src.updateUsrDialog() - return - - -/* - * Library Computer - */ -// TODO: Make this an actual /obj/machinery/computer that can be crafted from circuit boards and such -// It is August 22nd, 2012... This TODO has already been here for months.. I wonder how long it'll last before someone does something about it. // Nov 2019. Nope. -/obj/machinery/librarycomp - name = "Check-In/Out Computer" - desc = "Print books from the archives! (You aren't quite sure how they're printed by it, though.)" - icon = 'icons/obj/library.dmi' - icon_state = "computer" - anchored = TRUE - density = TRUE - var/arcanecheckout = 0 - var/screenstate = 0 // 0 - Main Menu, 1 - Inventory, 2 - Checked Out, 3 - Check Out a Book - var/sortby = "author" - var/buffer_book - var/buffer_mob - var/upload_category = "Fiction" - var/list/checkouts = list() - var/list/inventory = list() - var/checkoutperiod = 5 // In minutes - var/obj/machinery/libraryscanner/scanner // Book scanner that will be used when uploading books to the Archive - - var/bibledelay = 0 // LOL NO SPAM (1 minute delay) -- Doohl - - var/static/list/all_books - - var/static/list/base_genre_books - -/obj/machinery/librarycomp/Initialize() - . = ..() - - if(!base_genre_books || !base_genre_books.len) - base_genre_books = list( - /obj/item/weapon/book/custom_library/fiction, - /obj/item/weapon/book/custom_library/nonfiction, - /obj/item/weapon/book/custom_library/reference, - /obj/item/weapon/book/custom_library/religious, - /obj/item/weapon/book/bundle/custom_library/fiction, - /obj/item/weapon/book/bundle/custom_library/nonfiction, - /obj/item/weapon/book/bundle/custom_library/reference, - /obj/item/weapon/book/bundle/custom_library/religious - ) - - if(!all_books || !all_books.len) - all_books = list() - - for(var/path in subtypesof(/obj/item/weapon/book/codex/lore)) - var/obj/item/weapon/book/C = new path(null) - all_books[C.name] = C - - for(var/path in subtypesof(/obj/item/weapon/book/custom_library) - base_genre_books) - var/obj/item/weapon/book/B = new path(null) - all_books[B.title] = B - - for(var/path in subtypesof(/obj/item/weapon/book/bundle/custom_library) - base_genre_books) - var/obj/item/weapon/book/M = new path(null) - all_books[M.title] = M - -/obj/machinery/librarycomp/attack_hand(var/mob/user as mob) - usr.set_machine(src) - var/dat = "Book Inventory Management\n" // - switch(screenstate) - if(0) - // Main Menu //VOREStation Edit start - dat += {"1. View General Inventory
                    - 2. View Checked Out Inventory
                    - 3. Check out a Book
                    - 4. Connect to Internal Archive
                    - 5. Upload New Title to Archive
                    - 6. Print a Bible
                    - 8. Access External Archive
                    "} //VOREStation Edit end - if(src.emagged) - dat += "7. Access the Forbidden Lore Vault
                    " - if(src.arcanecheckout) - new /obj/item/weapon/book/tome(src.loc) - var/datum/gender/T = gender_datums[user.get_visible_gender()] - to_chat(user, "Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a dusty old tome sitting on the desk. You don't really remember printing it.") - user.visible_message("\The [user] stares at the blank screen for a few moments, [T.his] expression frozen in fear. When [T.he] finally awakens from it, [T.he] looks a lot older.", 2) - src.arcanecheckout = 0 - if(1) - // Inventory - dat += "

                    Inventory


                    " - for(var/obj/item/weapon/book/b in inventory) - dat += "[b.name] (Delete)
                    " - dat += "(Return to main menu)
                    " - if(2) - // Checked Out - dat += "

                    Checked Out Books


                    " - for(var/datum/borrowbook/b in checkouts) - var/timetaken = world.time - b.getdate - //timetaken *= 10 - timetaken /= 600 - timetaken = round(timetaken) - var/timedue = b.duedate - world.time - //timedue *= 10 - timedue /= 600 - if(timedue <= 0) - timedue = "(OVERDUE) [timedue]" - else - timedue = round(timedue) - dat += {"\"[b.bookname]\", Checked out to: [b.mobname]
                    --- Taken: [timetaken] minutes ago, Due: in [timedue] minutes
                    - (Check In)

                    "} - dat += "(Return to main menu)
                    " - if(3) - // Check Out a Book - dat += {"

                    Check Out a Book


                    - Book: [src.buffer_book] - \[Edit\]
                    - Recipient: [src.buffer_mob] - \[Edit\]
                    - Checkout Date : [world.time/600]
                    - Due Date: [(world.time + checkoutperiod)/600]
                    - (Checkout Period: [checkoutperiod] minutes) (+/-) - (Commit Entry)
                    - (Return to main menu)
                    "} - if(4) - dat += "

                    Internal Archive

                    " - if(!all_books || !all_books.len) - dat += "ERROR Something has gone seriously wrong. Contact System Administrator for more information." - else - dat += {" - " - dat += "
                    TITLE\[Order\]
                    " - dat += "
                    (Return to main menu)
                    " - if(5) - //dat += "

                    ERROR

                    " //VOREStation Removal - //dat+= "Library Database is in Secure Management Mode.
                    \ //VOREStation Removal - //Contact a System Administrator for more information.
                    " //VOREStation Removal - //VOREstation Edit Start - dat += "

                    Upload a New Title

                    " - if(!scanner) - for(var/obj/machinery/libraryscanner/S in range(9)) - scanner = S - break - if(!scanner) - dat += "No scanner found within wireless network range.
                    " - else if(!scanner.cache) - dat += "No data found in scanner memory.
                    " - else - dat += {"Data marked for upload...
                    - Title: [scanner.cache.name]
                    "} - if(!scanner.cache.author) - scanner.cache.author = "Anonymous" - dat += {"Author: [scanner.cache.author]
                    - Category: [upload_category]
                    - \[Upload\]
                    "} - //VOREStation Edit End - dat += "(Return to main menu)
                    " - if(7) - dat += {"

                    Accessing Forbidden Lore Vault v 1.3

                    - Are you absolutely sure you want to proceed? EldritchTomes Inc. takes no responsibilities for loss of sanity resulting from this action.

                    - Yes.
                    - No.
                    "} - if(8) - dat += "

                    External Archive

                    " //VOREStation Edit - establish_old_db_connection() - - //dat += "

                    Warning: System Administrator has slated this archive for removal. Personal uploads should be taken to the NT board of internal literature.

                    " //VOREStation Removal - - if(!dbcon_old.IsConnected()) - dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance." - else - dat += {"(Order book by SS13BN)

                    - - " - dat += "
                    TITLE\[Order\]" - if(show_admin_options) // This isn't the only check, since you can just href-spoof press this button. Just to tidy things up. - dat += "\[Del\]" - dat += "
                    " - dat += "
                    (Return to main menu)
                    " - - //dat += "Close

                    " - user << browse(dat, "window=library") - onclose(user, "library") - -//VOREStation Addition Start -/obj/machinery/librarycomp/attack_ghost(mob/user) - - var/show_admin_options = check_rights(R_ADMIN, show_msg = FALSE) - if(!show_admin_options) - . = ..() - - else - usr.set_machine(src) - var/dat = "Book Inventory Management\n" // - - dat += "

                    ADMINISTRATIVE MANAGEMENT

                    " - establish_old_db_connection() - - if(!dbcon_old.IsConnected()) - dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance." - else - dat += {"(Order book by SS13BN)

                    - - " - dat += "
                    TITLE\[Del\]" - dat += "
                    " - dat += "
                    (Return to main menu)
                    " - - user << browse(dat, "window=library") - onclose(user, "library") -//VOREStation Addition End - -/obj/machinery/librarycomp/emag_act(var/remaining_charges, var/mob/user) - if (src.density && !src.emagged) - src.emagged = 1 - return 1 - -/obj/machinery/librarycomp/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/barcodescanner)) - var/obj/item/weapon/barcodescanner/scanner = W - scanner.computer = src - to_chat(user, "[scanner]'s associated machine has been set to [src].") - for (var/mob/V in hearers(src)) - V.show_message("[src] lets out a low, short blip.", 2) - else - ..() - -/obj/machinery/librarycomp/Topic(href, href_list) - if(..()) - usr << browse(null, "window=library") - onclose(usr, "library") - return - - if(href_list["switchscreen"]) - switch(href_list["switchscreen"]) - if("0") - screenstate = 0 - if("1") - screenstate = 1 - if("2") - screenstate = 2 - if("3") - screenstate = 3 - if("4") - screenstate = 4 - if("5") - screenstate = 5 - if("6") - if(!bibledelay) - new /obj/item/weapon/storage/bible(src.loc) - bibledelay = 1 - spawn(60) - bibledelay = 0 - - else - for (var/mob/V in hearers(src)) - V.show_message("[src]'s monitor flashes, \"Bible printer currently unavailable, please wait a moment.\"") - - if("7") - screenstate = 7 - if("8") - screenstate = 8 - if(href_list["arccheckout"]) - if(src.emagged) - src.arcanecheckout = 1 - src.screenstate = 0 - if(href_list["increasetime"]) - checkoutperiod += 1 - if(href_list["decreasetime"]) - checkoutperiod -= 1 - if(checkoutperiod < 1) - checkoutperiod = 1 - if(href_list["editbook"]) - buffer_book = sanitizeSafe(tgui_input_text(usr, "Enter the book's title:")) - if(href_list["editmob"]) - buffer_mob = sanitize(tgui_input_text(usr, "Enter the recipient's name:", null, null, MAX_NAME_LEN), MAX_NAME_LEN) - if(href_list["checkout"]) - var/datum/borrowbook/b = new /datum/borrowbook - b.bookname = sanitizeSafe(buffer_book) - b.mobname = sanitize(buffer_mob) - b.getdate = world.time - b.duedate = world.time + (checkoutperiod * 600) - checkouts.Add(b) - if(href_list["checkin"]) - var/datum/borrowbook/b = locate(href_list["checkin"]) - checkouts.Remove(b) - if(href_list["delbook"]) - var/obj/item/weapon/book/b = locate(href_list["delbook"]) - inventory.Remove(b) - if(href_list["setauthor"]) - var/newauthor = sanitize(tgui_input_text(usr, "Enter the author's name: ")) - if(newauthor) - scanner.cache.author = newauthor - if(href_list["setcategory"]) - var/newcategory = tgui_input_list(usr, "Choose a category: ", "Category", list("Fiction", "Non-Fiction", "Adult", "Reference", "Religion")) - if(newcategory) - upload_category = newcategory - - //VOREStation Edit Start - if(href_list["upload"]) - if(scanner) - if(scanner.cache) - var/choice = tgui_alert(usr, "Are you certain you wish to upload this title to the Archive?", "Confirmation", list("Confirm", "Abort")) - if(choice == "Confirm") - if(scanner.cache.unique) - tgui_alert_async(usr, "This book has been rejected from the database. Aborting!") - else - establish_old_db_connection() - if(!dbcon_old.IsConnected()) - tgui_alert_async(usr, "Connection to Archive has been severed. Aborting.") - else - /* - var/sqltitle = dbcon.Quote(scanner.cache.name) - var/sqlauthor = dbcon.Quote(scanner.cache.author) - var/sqlcontent = dbcon.Quote(scanner.cache.dat) - var/sqlcategory = dbcon.Quote(upload_category) - */ - var/sqltitle = sanitizeSQL(scanner.cache.name) - var/sqlauthor = sanitizeSQL(scanner.cache.author) - var/sqlcontent = sanitizeSQL(scanner.cache.dat) - var/sqlcategory = sanitizeSQL(upload_category) - var/DBQuery/query = dbcon_old.NewQuery("INSERT INTO library (author, title, content, category) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]')") - if(!query.Execute()) - to_chat(usr,query.ErrorMsg()) - else - log_game("[usr.name]/[usr.key] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] signs") - tgui_alert_async(usr, "Upload Complete.") - //VOREStation Edit End - - if(href_list["targetid"]) - var/sqlid = sanitizeSQL(href_list["targetid"]) - establish_old_db_connection() - if(!dbcon_old.IsConnected()) - tgui_alert_async(usr, "Connection to Archive has been severed. Aborting.") - if(bibledelay) - for (var/mob/V in hearers(src)) - V.show_message("[src]'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"") - else - bibledelay = 1 - spawn(6) - bibledelay = 0 - var/DBQuery/query = dbcon_old.NewQuery("SELECT * FROM library WHERE id=[sqlid]") - query.Execute() - - while(query.NextRow()) - var/author = query.item[2] - var/title = query.item[3] - var/content = query.item[4] - var/obj/item/weapon/book/B = new(src.loc) - B.name = "Book: [title]" - B.title = title - B.author = author - B.dat = content - B.icon_state = "book[rand(1,16)]" - B.item_state = B.icon_state - src.visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?") - break - - if(href_list["delid"]) - if(!check_rights(R_ADMIN)) - return - var/sqlid = sanitizeSQL(href_list["delid"]) - establish_old_db_connection() - if(!dbcon_old.IsConnected()) - tgui_alert_async(usr, "Connection to Archive has been severed. Aborting.") - else - var/DBQuery/query = dbcon_old.NewQuery("DELETE FROM library WHERE id=[sqlid]") - query.Execute() - log_admin("[usr.key] has deleted the book [sqlid]") //VOREStation Addition - - if(href_list["orderbyid"]) - var/orderid = tgui_input_number(usr, "Enter your order:") - if(orderid) - if(isnum(orderid)) - var/nhref = "src=\ref[src];targetid=[orderid]" - spawn() src.Topic(nhref, params2list(nhref), src) - if(href_list["sort"] in list("author", "title", "category")) - sortby = href_list["sort"] - if(href_list["hardprint"]) - var/newpath = href_list["hardprint"] - var/obj/item/weapon/book/NewBook = new newpath(get_turf(src)) - NewBook.name = "Book: [NewBook.name]" - src.add_fingerprint(usr) - src.updateUsrDialog() - return - -/* - * Library Scanner - */ -/obj/machinery/libraryscanner - name = "scanner" - desc = "A scanner for scanning in books and papers." - icon = 'icons/obj/library.dmi' - icon_state = "bigscanner" - anchored = TRUE - density = TRUE - var/obj/item/weapon/book/cache // Last scanned book - -/obj/machinery/libraryscanner/attackby(var/obj/O as obj, var/mob/user as mob) - if(istype(O, /obj/item/weapon/book)) - user.drop_item() - O.loc = src - -/obj/machinery/libraryscanner/attack_hand(var/mob/user as mob) - usr.set_machine(src) - var/dat = "Scanner Control Interface\n" // - if(cache) - dat += "Data stored in memory.
                    " - else - dat += "No data stored in memory.
                    " - dat += "\[Scan\]" - if(cache) - dat += " \[Clear Memory\]

                    \[Remove Book\]" - else - dat += "
                    " - user << browse(dat, "window=scanner") - onclose(user, "scanner") - -/obj/machinery/libraryscanner/Topic(href, href_list) - if(..()) - usr << browse(null, "window=scanner") - onclose(usr, "scanner") - return - - if(href_list["scan"]) - for(var/obj/item/weapon/book/B in contents) - cache = B - break - if(href_list["clear"]) - cache = null - if(href_list["eject"]) - for(var/obj/item/weapon/book/B in contents) - B.loc = src.loc - src.add_fingerprint(usr) - src.updateUsrDialog() - return - - -/* - * Book binder - */ -/obj/machinery/bookbinder - name = "Book Binder" - desc = "Bundles up a stack of inserted paper into a convenient book format." - icon = 'icons/obj/library.dmi' - icon_state = "binder" - anchored = TRUE - density = TRUE - -/obj/machinery/bookbinder/attackby(var/obj/O as obj, var/mob/user as mob) - if(istype(O, /obj/item/weapon/paper) || istype(O, /obj/item/weapon/paper_bundle)) - if(istype(O, /obj/item/weapon/paper)) - user.drop_item() - O.loc = src - user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].") - src.visible_message("[src] begins to hum as it warms up its printing drums.") - sleep(rand(200,400)) - src.visible_message("[src] whirs as it prints and binds a new book.") - var/obj/item/weapon/book/b = new(src.loc) - b.dat = O:info - b.name = "Print Job #" + "[rand(100, 999)]" - b.icon_state = "book[rand(1,7)]" - qdel(O) - else - user.drop_item() - O.loc = src - user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].") - src.visible_message("[src] begins to hum as it warms up its printing drums.") - sleep(rand(300,500)) - src.visible_message("[src] whirs as it prints and binds a new book.") - var/obj/item/weapon/book/bundle/b = new(src.loc) - b.pages = O:pages - for(var/obj/item/weapon/paper/P in O.contents) - P.forceMove(b) - for(var/obj/item/weapon/photo/P in O.contents) - P.forceMove(b) - b.name = "Print Job #" + "[rand(100, 999)]" - b.icon_state = "book[rand(1,7)]" - qdel(O) - else - ..() +/* Library Machines + * + * Contains: + * Borrowbook datum + * Library Public Computer + * Library Computer + * Library Scanner + * Book Binder + */ + +/* + * Borrowbook datum + */ +/datum/borrowbook // Datum used to keep track of who has borrowed what when and for how long. + var/bookname + var/mobname + var/getdate + var/duedate + +/* + * Library Public Computer + */ +/obj/machinery/librarypubliccomp + name = "visitor computer" + icon = 'icons/obj/library.dmi' + icon_state = "computer" + anchored = TRUE + density = TRUE + var/screenstate = 0 + var/title + var/category = "Any" + var/author + var/SQLquery + +/obj/machinery/librarypubliccomp/attack_hand(var/mob/user as mob) + usr.set_machine(src) + var/dat = "Library Visitor\n" // + switch(screenstate) + if(0) + dat += {"

                    Search Settings


                    + Filter by Title: [title]
                    + Filter by Category: [category]
                    + Filter by Author: [author]
                    + \[Start Search\]
                    "} + if(1) + establish_old_db_connection() + if(!dbcon_old.IsConnected()) + dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance.
                    " + else if(!SQLquery) + dat += "ERROR: Malformed search request. Please contact your system administrator for assistance.
                    " + else + dat += {" + "} + + var/DBQuery/query = dbcon_old.NewQuery(SQLquery) + query.Execute() + + while(query.NextRow()) + var/author = query.item[1] + var/title = query.item[2] + var/category = query.item[3] + var/id = query.item[4] + dat += "" + dat += "
                    AUTHORTITLECATEGORYSS13BN
                    [author][title][category][id]

                    " + dat += "\[Go Back\]
                    " + user << browse(dat, "window=publiclibrary") + onclose(user, "publiclibrary") + +/obj/machinery/librarypubliccomp/Topic(href, href_list) + if(..()) + usr << browse(null, "window=publiclibrary") + onclose(usr, "publiclibrary") + return + + if(href_list["settitle"]) + var/newtitle = tgui_input_text(usr, "Enter a title to search for:") + if(newtitle) + title = sanitize(newtitle) + else + title = null + title = sanitizeSQL(title) + if(href_list["setcategory"]) + var/newcategory = tgui_input_list(usr, "Choose a category to search for:", "Category", list("Any", "Fiction", "Non-Fiction", "Adult", "Reference", "Religion")) + if(newcategory) + category = sanitize(newcategory) + else + category = "Any" + category = sanitizeSQL(category) + if(href_list["setauthor"]) + var/newauthor = tgui_input_text(usr, "Enter an author to search for:") + if(newauthor) + author = sanitize(newauthor) + else + author = null + author = sanitizeSQL(author) + if(href_list["search"]) + SQLquery = "SELECT author, title, category, id FROM library WHERE " + if(category == "Any") + SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%'" + else + SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%' AND category='[category]'" + screenstate = 1 + + if(href_list["back"]) + screenstate = 0 + + src.add_fingerprint(usr) + src.updateUsrDialog() + return + + +/* + * Library Computer + */ +// TODO: Make this an actual /obj/machinery/computer that can be crafted from circuit boards and such +// It is August 22nd, 2012... This TODO has already been here for months.. I wonder how long it'll last before someone does something about it. // Nov 2019. Nope. +/obj/machinery/librarycomp + name = "Check-In/Out Computer" + desc = "Print books from the archives! (You aren't quite sure how they're printed by it, though.)" + icon = 'icons/obj/library.dmi' + icon_state = "computer" + anchored = TRUE + density = TRUE + var/arcanecheckout = 0 + var/screenstate = 0 // 0 - Main Menu, 1 - Inventory, 2 - Checked Out, 3 - Check Out a Book + var/sortby = "author" + var/buffer_book + var/buffer_mob + var/upload_category = "Fiction" + var/list/checkouts = list() + var/list/inventory = list() + var/checkoutperiod = 5 // In minutes + var/obj/machinery/libraryscanner/scanner // Book scanner that will be used when uploading books to the Archive + + var/bibledelay = 0 // LOL NO SPAM (1 minute delay) -- Doohl + + var/static/list/all_books + + var/static/list/base_genre_books + +/obj/machinery/librarycomp/Initialize() + . = ..() + + if(!base_genre_books || !base_genre_books.len) + base_genre_books = list( + /obj/item/weapon/book/custom_library/fiction, + /obj/item/weapon/book/custom_library/nonfiction, + /obj/item/weapon/book/custom_library/reference, + /obj/item/weapon/book/custom_library/religious, + /obj/item/weapon/book/bundle/custom_library/fiction, + /obj/item/weapon/book/bundle/custom_library/nonfiction, + /obj/item/weapon/book/bundle/custom_library/reference, + /obj/item/weapon/book/bundle/custom_library/religious + ) + + if(!all_books || !all_books.len) + all_books = list() + + for(var/path in subtypesof(/obj/item/weapon/book/codex/lore)) + var/obj/item/weapon/book/C = new path(null) + all_books[C.name] = C + + for(var/path in subtypesof(/obj/item/weapon/book/custom_library) - base_genre_books) + var/obj/item/weapon/book/B = new path(null) + all_books[B.title] = B + + for(var/path in subtypesof(/obj/item/weapon/book/bundle/custom_library) - base_genre_books) + var/obj/item/weapon/book/M = new path(null) + all_books[M.title] = M + +/obj/machinery/librarycomp/attack_hand(var/mob/user as mob) + usr.set_machine(src) + var/dat = "Book Inventory Management\n" // + switch(screenstate) + if(0) + // Main Menu //VOREStation Edit start + dat += {"1. View General Inventory
                    + 2. View Checked Out Inventory
                    + 3. Check out a Book
                    + 4. Connect to Internal Archive
                    + 5. Upload New Title to Archive
                    + 6. Print a Bible
                    + 8. Access External Archive
                    "} //VOREStation Edit end + if(src.emagged) + dat += "7. Access the Forbidden Lore Vault
                    " + if(src.arcanecheckout) + new /obj/item/weapon/book/tome(src.loc) + var/datum/gender/T = gender_datums[user.get_visible_gender()] + to_chat(user, "Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a dusty old tome sitting on the desk. You don't really remember printing it.") + user.visible_message("\The [user] stares at the blank screen for a few moments, [T.his] expression frozen in fear. When [T.he] finally awakens from it, [T.he] looks a lot older.", 2) + src.arcanecheckout = 0 + if(1) + // Inventory + dat += "

                    Inventory


                    " + for(var/obj/item/weapon/book/b in inventory) + dat += "[b.name] (Delete)
                    " + dat += "(Return to main menu)
                    " + if(2) + // Checked Out + dat += "

                    Checked Out Books


                    " + for(var/datum/borrowbook/b in checkouts) + var/timetaken = world.time - b.getdate + //timetaken *= 10 + timetaken /= 600 + timetaken = round(timetaken) + var/timedue = b.duedate - world.time + //timedue *= 10 + timedue /= 600 + if(timedue <= 0) + timedue = "(OVERDUE) [timedue]" + else + timedue = round(timedue) + dat += {"\"[b.bookname]\", Checked out to: [b.mobname]
                    --- Taken: [timetaken] minutes ago, Due: in [timedue] minutes
                    + (Check In)

                    "} + dat += "(Return to main menu)
                    " + if(3) + // Check Out a Book + dat += {"

                    Check Out a Book


                    + Book: [src.buffer_book] + \[Edit\]
                    + Recipient: [src.buffer_mob] + \[Edit\]
                    + Checkout Date : [world.time/600]
                    + Due Date: [(world.time + checkoutperiod)/600]
                    + (Checkout Period: [checkoutperiod] minutes) (+/-) + (Commit Entry)
                    + (Return to main menu)
                    "} + if(4) + dat += "

                    Internal Archive

                    " + if(!all_books || !all_books.len) + dat += "ERROR Something has gone seriously wrong. Contact System Administrator for more information." + else + dat += {" + " + dat += "
                    TITLE\[Order\]
                    " + dat += "
                    (Return to main menu)
                    " + if(5) + //dat += "

                    ERROR

                    " //VOREStation Removal + //dat+= "Library Database is in Secure Management Mode.
                    \ //VOREStation Removal + //Contact a System Administrator for more information.
                    " //VOREStation Removal + //VOREstation Edit Start + dat += "

                    Upload a New Title

                    " + if(!scanner) + for(var/obj/machinery/libraryscanner/S in range(9)) + scanner = S + break + if(!scanner) + dat += "No scanner found within wireless network range.
                    " + else if(!scanner.cache) + dat += "No data found in scanner memory.
                    " + else + dat += {"Data marked for upload...
                    + Title: [scanner.cache.name]
                    "} + if(!scanner.cache.author) + scanner.cache.author = "Anonymous" + dat += {"Author: [scanner.cache.author]
                    + Category: [upload_category]
                    + \[Upload\]
                    "} + //VOREStation Edit End + dat += "(Return to main menu)
                    " + if(7) + dat += {"

                    Accessing Forbidden Lore Vault v 1.3

                    + Are you absolutely sure you want to proceed? EldritchTomes Inc. takes no responsibilities for loss of sanity resulting from this action.

                    + Yes.
                    + No.
                    "} + if(8) + dat += "

                    External Archive

                    " //VOREStation Edit + establish_old_db_connection() + + //dat += "

                    Warning: System Administrator has slated this archive for removal. Personal uploads should be taken to the NT board of internal literature.

                    " //VOREStation Removal + + if(!dbcon_old.IsConnected()) + dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance." + else + dat += {"(Order book by SS13BN)

                    + + " + dat += "
                    TITLE\[Order\]" + if(show_admin_options) // This isn't the only check, since you can just href-spoof press this button. Just to tidy things up. + dat += "\[Del\]" + dat += "
                    " + dat += "
                    (Return to main menu)
                    " + + //dat += "Close

                    " + user << browse(dat, "window=library") + onclose(user, "library") + +//VOREStation Addition Start +/obj/machinery/librarycomp/attack_ghost(mob/user) + + var/show_admin_options = check_rights(R_ADMIN, show_msg = FALSE) + if(!show_admin_options) + . = ..() + + else + usr.set_machine(src) + var/dat = "Book Inventory Management\n" // + + dat += "

                    ADMINISTRATIVE MANAGEMENT

                    " + establish_old_db_connection() + + if(!dbcon_old.IsConnected()) + dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance." + else + dat += {"(Order book by SS13BN)

                    + + " + dat += "
                    TITLE\[Del\]" + dat += "
                    " + dat += "
                    (Return to main menu)
                    " + + user << browse(dat, "window=library") + onclose(user, "library") +//VOREStation Addition End + +/obj/machinery/librarycomp/emag_act(var/remaining_charges, var/mob/user) + if (src.density && !src.emagged) + src.emagged = 1 + return 1 + +/obj/machinery/librarycomp/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/barcodescanner)) + var/obj/item/weapon/barcodescanner/scanner = W + scanner.computer = src + to_chat(user, "[scanner]'s associated machine has been set to [src].") + for (var/mob/V in hearers(src)) + V.show_message("[src] lets out a low, short blip.", 2) + else + ..() + +/obj/machinery/librarycomp/Topic(href, href_list) + if(..()) + usr << browse(null, "window=library") + onclose(usr, "library") + return + + if(href_list["switchscreen"]) + switch(href_list["switchscreen"]) + if("0") + screenstate = 0 + if("1") + screenstate = 1 + if("2") + screenstate = 2 + if("3") + screenstate = 3 + if("4") + screenstate = 4 + if("5") + screenstate = 5 + if("6") + if(!bibledelay) + new /obj/item/weapon/storage/bible(src.loc) + bibledelay = 1 + spawn(60) + bibledelay = 0 + + else + for (var/mob/V in hearers(src)) + V.show_message("[src]'s monitor flashes, \"Bible printer currently unavailable, please wait a moment.\"") + + if("7") + screenstate = 7 + if("8") + screenstate = 8 + if(href_list["arccheckout"]) + if(src.emagged) + src.arcanecheckout = 1 + src.screenstate = 0 + if(href_list["increasetime"]) + checkoutperiod += 1 + if(href_list["decreasetime"]) + checkoutperiod -= 1 + if(checkoutperiod < 1) + checkoutperiod = 1 + if(href_list["editbook"]) + buffer_book = sanitizeSafe(tgui_input_text(usr, "Enter the book's title:")) + if(href_list["editmob"]) + buffer_mob = sanitize(tgui_input_text(usr, "Enter the recipient's name:", null, null, MAX_NAME_LEN), MAX_NAME_LEN) + if(href_list["checkout"]) + var/datum/borrowbook/b = new /datum/borrowbook + b.bookname = sanitizeSafe(buffer_book) + b.mobname = sanitize(buffer_mob) + b.getdate = world.time + b.duedate = world.time + (checkoutperiod * 600) + checkouts.Add(b) + if(href_list["checkin"]) + var/datum/borrowbook/b = locate(href_list["checkin"]) + checkouts.Remove(b) + if(href_list["delbook"]) + var/obj/item/weapon/book/b = locate(href_list["delbook"]) + inventory.Remove(b) + if(href_list["setauthor"]) + var/newauthor = sanitize(tgui_input_text(usr, "Enter the author's name: ")) + if(newauthor) + scanner.cache.author = newauthor + if(href_list["setcategory"]) + var/newcategory = tgui_input_list(usr, "Choose a category: ", "Category", list("Fiction", "Non-Fiction", "Adult", "Reference", "Religion")) + if(newcategory) + upload_category = newcategory + + //VOREStation Edit Start + if(href_list["upload"]) + if(scanner) + if(scanner.cache) + var/choice = tgui_alert(usr, "Are you certain you wish to upload this title to the Archive?", "Confirmation", list("Confirm", "Abort")) + if(choice == "Confirm") + if(scanner.cache.unique) + tgui_alert_async(usr, "This book has been rejected from the database. Aborting!") + else + establish_old_db_connection() + if(!dbcon_old.IsConnected()) + tgui_alert_async(usr, "Connection to Archive has been severed. Aborting.") + else + /* + var/sqltitle = dbcon.Quote(scanner.cache.name) + var/sqlauthor = dbcon.Quote(scanner.cache.author) + var/sqlcontent = dbcon.Quote(scanner.cache.dat) + var/sqlcategory = dbcon.Quote(upload_category) + */ + var/sqltitle = sanitizeSQL(scanner.cache.name) + var/sqlauthor = sanitizeSQL(scanner.cache.author) + var/sqlcontent = sanitizeSQL(scanner.cache.dat) + var/sqlcategory = sanitizeSQL(upload_category) + var/DBQuery/query = dbcon_old.NewQuery("INSERT INTO library (author, title, content, category) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]')") + if(!query.Execute()) + to_chat(usr,query.ErrorMsg()) + else + log_game("[usr.name]/[usr.key] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] signs") + tgui_alert_async(usr, "Upload Complete.") + //VOREStation Edit End + + if(href_list["targetid"]) + var/sqlid = sanitizeSQL(href_list["targetid"]) + establish_old_db_connection() + if(!dbcon_old.IsConnected()) + tgui_alert_async(usr, "Connection to Archive has been severed. Aborting.") + if(bibledelay) + for (var/mob/V in hearers(src)) + V.show_message("[src]'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"") + else + bibledelay = 1 + spawn(6) + bibledelay = 0 + var/DBQuery/query = dbcon_old.NewQuery("SELECT * FROM library WHERE id=[sqlid]") + query.Execute() + + while(query.NextRow()) + var/author = query.item[2] + var/title = query.item[3] + var/content = query.item[4] + var/obj/item/weapon/book/B = new(src.loc) + B.name = "Book: [title]" + B.title = title + B.author = author + B.dat = content + B.icon_state = "book[rand(1,16)]" + B.item_state = B.icon_state + src.visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?") + break + + if(href_list["delid"]) + if(!check_rights(R_ADMIN)) + return + var/sqlid = sanitizeSQL(href_list["delid"]) + establish_old_db_connection() + if(!dbcon_old.IsConnected()) + tgui_alert_async(usr, "Connection to Archive has been severed. Aborting.") + else + var/DBQuery/query = dbcon_old.NewQuery("DELETE FROM library WHERE id=[sqlid]") + query.Execute() + log_admin("[usr.key] has deleted the book [sqlid]") //VOREStation Addition + + if(href_list["orderbyid"]) + var/orderid = tgui_input_number(usr, "Enter your order:") + if(orderid) + if(isnum(orderid)) + var/nhref = "src=\ref[src];targetid=[orderid]" + spawn() src.Topic(nhref, params2list(nhref), src) + if(href_list["sort"] in list("author", "title", "category")) + sortby = href_list["sort"] + if(href_list["hardprint"]) + var/newpath = href_list["hardprint"] + var/obj/item/weapon/book/NewBook = new newpath(get_turf(src)) + NewBook.name = "Book: [NewBook.name]" + src.add_fingerprint(usr) + src.updateUsrDialog() + return + +/* + * Library Scanner + */ +/obj/machinery/libraryscanner + name = "scanner" + desc = "A scanner for scanning in books and papers." + icon = 'icons/obj/library.dmi' + icon_state = "bigscanner" + anchored = TRUE + density = TRUE + var/obj/item/weapon/book/cache // Last scanned book + +/obj/machinery/libraryscanner/attackby(var/obj/O as obj, var/mob/user as mob) + if(istype(O, /obj/item/weapon/book)) + user.drop_item() + O.loc = src + +/obj/machinery/libraryscanner/attack_hand(var/mob/user as mob) + usr.set_machine(src) + var/dat = "Scanner Control Interface\n" // + if(cache) + dat += "Data stored in memory.
                    " + else + dat += "No data stored in memory.
                    " + dat += "\[Scan\]" + if(cache) + dat += " \[Clear Memory\]

                    \[Remove Book\]" + else + dat += "
                    " + user << browse(dat, "window=scanner") + onclose(user, "scanner") + +/obj/machinery/libraryscanner/Topic(href, href_list) + if(..()) + usr << browse(null, "window=scanner") + onclose(usr, "scanner") + return + + if(href_list["scan"]) + for(var/obj/item/weapon/book/B in contents) + cache = B + break + if(href_list["clear"]) + cache = null + if(href_list["eject"]) + for(var/obj/item/weapon/book/B in contents) + B.loc = src.loc + src.add_fingerprint(usr) + src.updateUsrDialog() + return + + +/* + * Book binder + */ +/obj/machinery/bookbinder + name = "Book Binder" + desc = "Bundles up a stack of inserted paper into a convenient book format." + icon = 'icons/obj/library.dmi' + icon_state = "binder" + anchored = TRUE + density = TRUE + +/obj/machinery/bookbinder/attackby(var/obj/O as obj, var/mob/user as mob) + if(istype(O, /obj/item/weapon/paper) || istype(O, /obj/item/weapon/paper_bundle)) + if(istype(O, /obj/item/weapon/paper)) + user.drop_item() + O.loc = src + user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].") + src.visible_message("[src] begins to hum as it warms up its printing drums.") + sleep(rand(200,400)) + src.visible_message("[src] whirs as it prints and binds a new book.") + var/obj/item/weapon/book/b = new(src.loc) + b.dat = O:info + b.name = "Print Job #" + "[rand(100, 999)]" + b.icon_state = "book[rand(1,7)]" + qdel(O) + else + user.drop_item() + O.loc = src + user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].") + src.visible_message("[src] begins to hum as it warms up its printing drums.") + sleep(rand(300,500)) + src.visible_message("[src] whirs as it prints and binds a new book.") + var/obj/item/weapon/book/bundle/b = new(src.loc) + b.pages = O:pages + for(var/obj/item/weapon/paper/P in O.contents) + P.forceMove(b) + for(var/obj/item/weapon/photo/P in O.contents) + P.forceMove(b) + b.name = "Print Job #" + "[rand(100, 999)]" + b.icon_state = "book[rand(1,7)]" + qdel(O) + else + ..() diff --git a/code/modules/library/lib_readme.dm b/code/modules/library/lib_readme.dm index 4237687dfff..0150ed6ba8a 100644 --- a/code/modules/library/lib_readme.dm +++ b/code/modules/library/lib_readme.dm @@ -1,61 +1,61 @@ -//******************************* -// -// Library SQL Configuration -// -//******************************* - -// Deprecated! See global.dm for new SQL config vars -- TLE -/* -#define SQL_ADDRESS "" -#define SQL_DB "" -#define SQL_PORT "3306" -#define SQL_LOGIN "" -#define SQL_PASS "" -*/ - -//******************************* -// Requires Dantom.DB library ( http://www.byond.com/developer/Dantom/DB ) - - -/* - The Library - ------------ - A place for the crew to go, relax, and enjoy a good book. - Aspiring authors can even self publish and, if they're lucky - convince the on-staff Librarian to submit it to the Archives - to be chronicled in history forever - some say even persisting - through alternate dimensions. - - - Written by TLE for /tg/station 13 - Feel free to use this as you like. Some credit would be cool. - Check us out at http://nanotrasen.com/ if you're so inclined. -*/ - -// CONTAINS: - -// Objects: -// - bookcase -// - book -// - barcode scanner -// Machinery: -// - library computer -// - visitor's computer -// - book binder -// - book scanner -// Datum: -// - borrowbook - - -// Ideas for the future -// --------------------- -// - Visitor's computer should be able to search the current in-round library inventory (that the Librarian has stocked and checked in) -// -- Give computer other features like an Instant Messenger application, or the ability to edit, save, and print documents. -// - Admin interface directly tied to the Archive DB. Right now there's no way to delete uploaded books in-game. -// -- If this gets implemented, allow Librarians to "tag" or "suggest" books to be deleted. The DB ID of the tagged books gets saved to a text file (or another table in the DB maybe?). -// The admin interface would automatically take these IDs and SELECT them all from the DB to be displayed along with a Delete link to drop the row from the table. -// - When the game sets up and the round begins, have it automatically pick random books from the DB to populate the library with. Even if the Librarian is a useless fuck there are at least a few books around. -// - Allow books to be "hollowed out" like the Chaplain's Bible, allowing you to store one pocket-sized item inside. -// - Make books/book cases burn when exposed to flame. -// - Make book binder hackable. -// - Books shouldn't print straight from the library computer. Make it synch with a machine like the book binder to print instead. This should consume some sort of resource. +//******************************* +// +// Library SQL Configuration +// +//******************************* + +// Deprecated! See global.dm for new SQL config vars -- TLE +/* +#define SQL_ADDRESS "" +#define SQL_DB "" +#define SQL_PORT "3306" +#define SQL_LOGIN "" +#define SQL_PASS "" +*/ + +//******************************* +// Requires Dantom.DB library ( http://www.byond.com/developer/Dantom/DB ) + + +/* + The Library + ------------ + A place for the crew to go, relax, and enjoy a good book. + Aspiring authors can even self publish and, if they're lucky + convince the on-staff Librarian to submit it to the Archives + to be chronicled in history forever - some say even persisting + through alternate dimensions. + + + Written by TLE for /tg/station 13 + Feel free to use this as you like. Some credit would be cool. + Check us out at http://nanotrasen.com/ if you're so inclined. +*/ + +// CONTAINS: + +// Objects: +// - bookcase +// - book +// - barcode scanner +// Machinery: +// - library computer +// - visitor's computer +// - book binder +// - book scanner +// Datum: +// - borrowbook + + +// Ideas for the future +// --------------------- +// - Visitor's computer should be able to search the current in-round library inventory (that the Librarian has stocked and checked in) +// -- Give computer other features like an Instant Messenger application, or the ability to edit, save, and print documents. +// - Admin interface directly tied to the Archive DB. Right now there's no way to delete uploaded books in-game. +// -- If this gets implemented, allow Librarians to "tag" or "suggest" books to be deleted. The DB ID of the tagged books gets saved to a text file (or another table in the DB maybe?). +// The admin interface would automatically take these IDs and SELECT them all from the DB to be displayed along with a Delete link to drop the row from the table. +// - When the game sets up and the round begins, have it automatically pick random books from the DB to populate the library with. Even if the Librarian is a useless fuck there are at least a few books around. +// - Allow books to be "hollowed out" like the Chaplain's Bible, allowing you to store one pocket-sized item inside. +// - Make books/book cases burn when exposed to flame. +// - Make book binder hackable. +// - Books shouldn't print straight from the library computer. Make it synch with a machine like the book binder to print instead. This should consume some sort of resource. diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm index 45d4004dd9d..155de84b1da 100644 --- a/code/modules/lighting/lighting_overlay.dm +++ b/code/modules/lighting/lighting_overlay.dm @@ -1,121 +1,121 @@ -/datum/lighting_object - ///the underlay we are currently applying to our turf to apply light - var/mutable_appearance/current_underlay - - ///whether we are already in the SSlighting.objects_queue list - var/needs_update = FALSE - - ///the turf that our light is applied to - var/turf/affected_turf - -/datum/lighting_object/New(turf/source) - if(!SSlighting.subsystem_initialized) - stack_trace("lighting_object created before SSlighting up!") - return - if(!isturf(source)) - qdel(src, force=TRUE) - stack_trace("a lighting object was assigned to [source], a non turf! ") - return - . = ..() - - current_underlay = mutable_appearance(LIGHTING_ICON, "transparent", source.z, PLANE_LIGHTING, 255, RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM) - - affected_turf = source - if (affected_turf.lighting_object) - qdel(affected_turf.lighting_object, force = TRUE) - stack_trace("a lighting object was assigned to a turf that already had a lighting object!") - - affected_turf.lighting_object = src - affected_turf.set_luminosity(0) - - for(var/turf/space/space_tile in RANGE_TURFS(1, affected_turf)) - space_tile.update_starlight() - - needs_update = TRUE - SSlighting.objects_queue += src - -/datum/lighting_object/Destroy(force) - if (!force) - return QDEL_HINT_LETMELIVE - SSlighting.objects_queue -= src - if (isturf(affected_turf)) - affected_turf.lighting_object = null - affected_turf.set_luminosity(1) - affected_turf.underlays -= current_underlay - affected_turf = null - return ..() - -/datum/lighting_object/proc/update() - - // To the future coder who sees this and thinks - // "Why didn't he just use a loop?" - // Well my man, it's because the loop performed like shit. - // And there's no way to improve it because - // without a loop you can make the list all at once which is the fastest you're gonna get. - // Oh it's also shorter line wise. - // Including with these comments. - - var/static/datum/lighting_corner/dummy/dummy_lighting_corner = new - - var/datum/lighting_corner/red_corner = affected_turf.lighting_corner_SW || dummy_lighting_corner - var/datum/lighting_corner/green_corner = affected_turf.lighting_corner_SE || dummy_lighting_corner - var/datum/lighting_corner/blue_corner = affected_turf.lighting_corner_NW || dummy_lighting_corner - var/datum/lighting_corner/alpha_corner = affected_turf.lighting_corner_NE || dummy_lighting_corner - - var/max = max(red_corner.largest_color_luminosity, green_corner.largest_color_luminosity, blue_corner.largest_color_luminosity, alpha_corner.largest_color_luminosity) - - var/rr = red_corner.cache_r - var/rg = red_corner.cache_g - var/rb = red_corner.cache_b - - var/gr = green_corner.cache_r - var/gg = green_corner.cache_g - var/gb = green_corner.cache_b - - var/br = blue_corner.cache_r - var/bg = blue_corner.cache_g - var/bb = blue_corner.cache_b - - var/ar = alpha_corner.cache_r - var/ag = alpha_corner.cache_g - var/ab = alpha_corner.cache_b - - #if LIGHTING_SOFT_THRESHOLD != 0 - var/set_luminosity = max > LIGHTING_SOFT_THRESHOLD - #else - // Because of floating pointsâ„¢?, it won't even be a flat 0. - // This number is mostly arbitrary. - var/set_luminosity = max > 1e-6 - #endif - - if((rr & gr & br & ar) && (rg + gg + bg + ag + rb + gb + bb + ab == 8)) - //anything that passes the first case is very likely to pass the second, and addition is a little faster in this case - affected_turf.underlays -= current_underlay - current_underlay.icon_state = "transparent" - current_underlay.color = null - affected_turf.underlays += current_underlay - else if(!set_luminosity) - affected_turf.underlays -= current_underlay - current_underlay.icon_state = "dark" - current_underlay.color = null - affected_turf.underlays += current_underlay - else - affected_turf.underlays -= current_underlay - current_underlay.icon_state = "gradient" - current_underlay.color = list( - rr, rg, rb, 00, - gr, gg, gb, 00, - br, bg, bb, 00, - ar, ag, ab, 00, - 00, 00, 00, 01 - ) - - affected_turf.underlays += current_underlay - - affected_turf.set_luminosity(set_luminosity) - -/datum/lighting_object/proc/removefromturf() - affected_turf.underlays -= current_underlay - -/datum/lighting_object/proc/addtoturf() - affected_turf.underlays += current_underlay +/datum/lighting_object + ///the underlay we are currently applying to our turf to apply light + var/mutable_appearance/current_underlay + + ///whether we are already in the SSlighting.objects_queue list + var/needs_update = FALSE + + ///the turf that our light is applied to + var/turf/affected_turf + +/datum/lighting_object/New(turf/source) + if(!SSlighting.subsystem_initialized) + stack_trace("lighting_object created before SSlighting up!") + return + if(!isturf(source)) + qdel(src, force=TRUE) + stack_trace("a lighting object was assigned to [source], a non turf! ") + return + . = ..() + + current_underlay = mutable_appearance(LIGHTING_ICON, "transparent", source.z, PLANE_LIGHTING, 255, RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM) + + affected_turf = source + if (affected_turf.lighting_object) + qdel(affected_turf.lighting_object, force = TRUE) + stack_trace("a lighting object was assigned to a turf that already had a lighting object!") + + affected_turf.lighting_object = src + affected_turf.set_luminosity(0) + + for(var/turf/space/space_tile in RANGE_TURFS(1, affected_turf)) + space_tile.update_starlight() + + needs_update = TRUE + SSlighting.objects_queue += src + +/datum/lighting_object/Destroy(force) + if (!force) + return QDEL_HINT_LETMELIVE + SSlighting.objects_queue -= src + if (isturf(affected_turf)) + affected_turf.lighting_object = null + affected_turf.set_luminosity(1) + affected_turf.underlays -= current_underlay + affected_turf = null + return ..() + +/datum/lighting_object/proc/update() + + // To the future coder who sees this and thinks + // "Why didn't he just use a loop?" + // Well my man, it's because the loop performed like shit. + // And there's no way to improve it because + // without a loop you can make the list all at once which is the fastest you're gonna get. + // Oh it's also shorter line wise. + // Including with these comments. + + var/static/datum/lighting_corner/dummy/dummy_lighting_corner = new + + var/datum/lighting_corner/red_corner = affected_turf.lighting_corner_SW || dummy_lighting_corner + var/datum/lighting_corner/green_corner = affected_turf.lighting_corner_SE || dummy_lighting_corner + var/datum/lighting_corner/blue_corner = affected_turf.lighting_corner_NW || dummy_lighting_corner + var/datum/lighting_corner/alpha_corner = affected_turf.lighting_corner_NE || dummy_lighting_corner + + var/max = max(red_corner.largest_color_luminosity, green_corner.largest_color_luminosity, blue_corner.largest_color_luminosity, alpha_corner.largest_color_luminosity) + + var/rr = red_corner.cache_r + var/rg = red_corner.cache_g + var/rb = red_corner.cache_b + + var/gr = green_corner.cache_r + var/gg = green_corner.cache_g + var/gb = green_corner.cache_b + + var/br = blue_corner.cache_r + var/bg = blue_corner.cache_g + var/bb = blue_corner.cache_b + + var/ar = alpha_corner.cache_r + var/ag = alpha_corner.cache_g + var/ab = alpha_corner.cache_b + + #if LIGHTING_SOFT_THRESHOLD != 0 + var/set_luminosity = max > LIGHTING_SOFT_THRESHOLD + #else + // Because of floating pointsâ„¢?, it won't even be a flat 0. + // This number is mostly arbitrary. + var/set_luminosity = max > 1e-6 + #endif + + if((rr & gr & br & ar) && (rg + gg + bg + ag + rb + gb + bb + ab == 8)) + //anything that passes the first case is very likely to pass the second, and addition is a little faster in this case + affected_turf.underlays -= current_underlay + current_underlay.icon_state = "transparent" + current_underlay.color = null + affected_turf.underlays += current_underlay + else if(!set_luminosity) + affected_turf.underlays -= current_underlay + current_underlay.icon_state = "dark" + current_underlay.color = null + affected_turf.underlays += current_underlay + else + affected_turf.underlays -= current_underlay + current_underlay.icon_state = "gradient" + current_underlay.color = list( + rr, rg, rb, 00, + gr, gg, gb, 00, + br, bg, bb, 00, + ar, ag, ab, 00, + 00, 00, 00, 01 + ) + + affected_turf.underlays += current_underlay + + affected_turf.set_luminosity(set_luminosity) + +/datum/lighting_object/proc/removefromturf() + affected_turf.underlays -= current_underlay + +/datum/lighting_object/proc/addtoturf() + affected_turf.underlays += current_underlay diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 6ef942836bd..4ba09f11f23 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -1,156 +1,156 @@ -/turf - ///Lumcount added by sources other than lighting datum objects, such as the overlay lighting component. - var/dynamic_lumcount = 0 - - var/dynamic_lighting = TRUE - - var/tmp/lighting_corners_initialised = FALSE - - var/tmp/outdoors_adjacent = FALSE - ///Our lighting object. - var/tmp/datum/lighting_object/lighting_object - ///Lighting Corner datums. - var/tmp/datum/lighting_corner/lighting_corner_NE - var/tmp/datum/lighting_corner/lighting_corner_SE - var/tmp/datum/lighting_corner/lighting_corner_SW - var/tmp/datum/lighting_corner/lighting_corner_NW - - ///Which directions does this turf block the vision of, taking into account both the turf's opacity and the movable opacity_sources. - var/directional_opacity = NONE - ///Lazylist of movable atoms providing opacity sources. - var/list/atom/movable/opacity_sources - -// Causes any affecting light sources to be queued for a visibility update, for example a door got opened. -/turf/proc/reconsider_lights() - lighting_corner_NE?.vis_update() - lighting_corner_SE?.vis_update() - lighting_corner_SW?.vis_update() - lighting_corner_NW?.vis_update() - -/turf/proc/lighting_clear_overlay() - if(lighting_object) - qdel(lighting_object, force=TRUE) - -// Builds a lighting object for us, but only if our area is dynamic. -/turf/proc/lighting_build_overlay() - if(!has_dynamic_lighting()) - return - - lighting_clear_overlay() - new/datum/lighting_object(src) - -// Used to get a scaled lumcount. -/turf/proc/get_lumcount(minlum = 0, maxlum = 1) - if (!lighting_object) - return 1 - - var/totallums = 0 - var/datum/lighting_corner/L - L = lighting_corner_NE - if (L) - totallums += L.lum_r + L.lum_b + L.lum_g - L = lighting_corner_SE - if (L) - totallums += L.lum_r + L.lum_b + L.lum_g - L = lighting_corner_SW - if (L) - totallums += L.lum_r + L.lum_b + L.lum_g - L = lighting_corner_NW - if (L) - totallums += L.lum_r + L.lum_b + L.lum_g - - - totallums /= 12 // 4 corners, each with 3 channels, get the average. - - totallums = (totallums - minlum) / (maxlum - minlum) - - totallums += dynamic_lumcount - - return CLAMP01(totallums) - -// Returns a boolean whether the turf is on soft lighting. -// Soft lighting being the threshold at which point the overlay considers -// itself as too dark to allow sight and see_in_dark becomes useful. -// So basically if this returns true the tile is unlit black. -/turf/proc/is_softly_lit() - if (!lighting_object) - return FALSE - - return !(luminosity || dynamic_lumcount) - - -///Proc to add movable sources of opacity on the turf and let it handle lighting code. -/turf/proc/add_opacity_source(atom/movable/new_source) - LAZYADD(opacity_sources, new_source) - if(opacity) - return - recalculate_directional_opacity() - - -///Proc to remove movable sources of opacity on the turf and let it handle lighting code. -/turf/proc/remove_opacity_source(atom/movable/old_source) - LAZYREMOVE(opacity_sources, old_source) - if(opacity) //Still opaque, no need to worry on updating. - return - recalculate_directional_opacity() - -///Setter for the byond luminosity var -/turf/proc/set_luminosity(new_luminosity, force) - if((is_outdoors() && !force) || outdoors_adjacent) - if(check_for_sun()) //If another system handles our lighting, don't interfere - return - - luminosity = new_luminosity - -///Checks planets and fake_suns to see if our turf should be handled by either -/turf/proc/check_for_sun() - if((SSplanets && SSplanets.z_to_planet.len >= z && SSplanets.z_to_planet[z]) || (z in fake_sunlight_zs)) - return TRUE - return FALSE - -///Calculate on which directions this turfs block view. -/turf/proc/recalculate_directional_opacity() - . = directional_opacity - if(opacity) - directional_opacity = ALL_CARDINALS - if(. != directional_opacity) - reconsider_lights() - return - directional_opacity = NONE - for(var/atom/movable/opacity_source as anything in opacity_sources) - if(opacity_source && opacity_source.flags & ON_BORDER) - directional_opacity |= opacity_source.dir - else //If fulltile and opaque, then the whole tile blocks view, no need to continue checking. - directional_opacity = ALL_CARDINALS - break - if(. != directional_opacity && (. == ALL_CARDINALS || directional_opacity == ALL_CARDINALS)) - reconsider_lights() //The lighting system only cares whether the tile is fully concealed from all directions or not. - - -/turf/proc/change_area(area/old_area, area/new_area) - if(SSlighting.subsystem_initialized) - if (new_area.dynamic_lighting != old_area.dynamic_lighting) - if (new_area.dynamic_lighting) - lighting_build_overlay() - else - lighting_clear_overlay() - -/turf/proc/has_dynamic_lighting() - var/area/A = loc - return (IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A)) - -/turf/proc/generate_missing_corners() - - if (!lighting_corner_NE) - lighting_corner_NE = new/datum/lighting_corner(src, NORTH|EAST) - - if (!lighting_corner_SE) - lighting_corner_SE = new/datum/lighting_corner(src, SOUTH|EAST) - - if (!lighting_corner_SW) - lighting_corner_SW = new/datum/lighting_corner(src, SOUTH|WEST) - - if (!lighting_corner_NW) - lighting_corner_NW = new/datum/lighting_corner(src, NORTH|WEST) - - lighting_corners_initialised = TRUE +/turf + ///Lumcount added by sources other than lighting datum objects, such as the overlay lighting component. + var/dynamic_lumcount = 0 + + var/dynamic_lighting = TRUE + + var/tmp/lighting_corners_initialised = FALSE + + var/tmp/outdoors_adjacent = FALSE + ///Our lighting object. + var/tmp/datum/lighting_object/lighting_object + ///Lighting Corner datums. + var/tmp/datum/lighting_corner/lighting_corner_NE + var/tmp/datum/lighting_corner/lighting_corner_SE + var/tmp/datum/lighting_corner/lighting_corner_SW + var/tmp/datum/lighting_corner/lighting_corner_NW + + ///Which directions does this turf block the vision of, taking into account both the turf's opacity and the movable opacity_sources. + var/directional_opacity = NONE + ///Lazylist of movable atoms providing opacity sources. + var/list/atom/movable/opacity_sources + +// Causes any affecting light sources to be queued for a visibility update, for example a door got opened. +/turf/proc/reconsider_lights() + lighting_corner_NE?.vis_update() + lighting_corner_SE?.vis_update() + lighting_corner_SW?.vis_update() + lighting_corner_NW?.vis_update() + +/turf/proc/lighting_clear_overlay() + if(lighting_object) + qdel(lighting_object, force=TRUE) + +// Builds a lighting object for us, but only if our area is dynamic. +/turf/proc/lighting_build_overlay() + if(!has_dynamic_lighting()) + return + + lighting_clear_overlay() + new/datum/lighting_object(src) + +// Used to get a scaled lumcount. +/turf/proc/get_lumcount(minlum = 0, maxlum = 1) + if (!lighting_object) + return 1 + + var/totallums = 0 + var/datum/lighting_corner/L + L = lighting_corner_NE + if (L) + totallums += L.lum_r + L.lum_b + L.lum_g + L = lighting_corner_SE + if (L) + totallums += L.lum_r + L.lum_b + L.lum_g + L = lighting_corner_SW + if (L) + totallums += L.lum_r + L.lum_b + L.lum_g + L = lighting_corner_NW + if (L) + totallums += L.lum_r + L.lum_b + L.lum_g + + + totallums /= 12 // 4 corners, each with 3 channels, get the average. + + totallums = (totallums - minlum) / (maxlum - minlum) + + totallums += dynamic_lumcount + + return CLAMP01(totallums) + +// Returns a boolean whether the turf is on soft lighting. +// Soft lighting being the threshold at which point the overlay considers +// itself as too dark to allow sight and see_in_dark becomes useful. +// So basically if this returns true the tile is unlit black. +/turf/proc/is_softly_lit() + if (!lighting_object) + return FALSE + + return !(luminosity || dynamic_lumcount) + + +///Proc to add movable sources of opacity on the turf and let it handle lighting code. +/turf/proc/add_opacity_source(atom/movable/new_source) + LAZYADD(opacity_sources, new_source) + if(opacity) + return + recalculate_directional_opacity() + + +///Proc to remove movable sources of opacity on the turf and let it handle lighting code. +/turf/proc/remove_opacity_source(atom/movable/old_source) + LAZYREMOVE(opacity_sources, old_source) + if(opacity) //Still opaque, no need to worry on updating. + return + recalculate_directional_opacity() + +///Setter for the byond luminosity var +/turf/proc/set_luminosity(new_luminosity, force) + if((is_outdoors() && !force) || outdoors_adjacent) + if(check_for_sun()) //If another system handles our lighting, don't interfere + return + + luminosity = new_luminosity + +///Checks planets and fake_suns to see if our turf should be handled by either +/turf/proc/check_for_sun() + if((SSplanets && SSplanets.z_to_planet.len >= z && SSplanets.z_to_planet[z]) || (z in fake_sunlight_zs)) + return TRUE + return FALSE + +///Calculate on which directions this turfs block view. +/turf/proc/recalculate_directional_opacity() + . = directional_opacity + if(opacity) + directional_opacity = ALL_CARDINALS + if(. != directional_opacity) + reconsider_lights() + return + directional_opacity = NONE + for(var/atom/movable/opacity_source as anything in opacity_sources) + if(opacity_source && opacity_source.flags & ON_BORDER) + directional_opacity |= opacity_source.dir + else //If fulltile and opaque, then the whole tile blocks view, no need to continue checking. + directional_opacity = ALL_CARDINALS + break + if(. != directional_opacity && (. == ALL_CARDINALS || directional_opacity == ALL_CARDINALS)) + reconsider_lights() //The lighting system only cares whether the tile is fully concealed from all directions or not. + + +/turf/proc/change_area(area/old_area, area/new_area) + if(SSlighting.subsystem_initialized) + if (new_area.dynamic_lighting != old_area.dynamic_lighting) + if (new_area.dynamic_lighting) + lighting_build_overlay() + else + lighting_clear_overlay() + +/turf/proc/has_dynamic_lighting() + var/area/A = loc + return (IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A)) + +/turf/proc/generate_missing_corners() + + if (!lighting_corner_NE) + lighting_corner_NE = new/datum/lighting_corner(src, NORTH|EAST) + + if (!lighting_corner_SE) + lighting_corner_SE = new/datum/lighting_corner(src, SOUTH|EAST) + + if (!lighting_corner_SW) + lighting_corner_SW = new/datum/lighting_corner(src, SOUTH|WEST) + + if (!lighting_corner_NW) + lighting_corner_NW = new/datum/lighting_corner(src, NORTH|WEST) + + lighting_corners_initialised = TRUE diff --git a/code/modules/lore_codex/news_data/main.dm b/code/modules/lore_codex/news_data/main.dm index 301ac67df96..8f3061f38a9 100644 --- a/code/modules/lore_codex/news_data/main.dm +++ b/code/modules/lore_codex/news_data/main.dm @@ -1,1285 +1,1285 @@ -/datum/lore/codex/category/main_news // The top-level categories for the news thing - name = "Index" - data = "Below you'll find a list of articles relevant to the current (as of 2565) political climate, especially concerning the local \ - region. Each is labeled by date of publication and title. This list is self-updating, and from time to time the publisher will push new \ - articles. You are encouraged to check back frequently." - children = list( - /datum/lore/codex/page/article114, - /datum/lore/codex/page/article113, - /datum/lore/codex/page/outage, - /datum/lore/codex/page/article112, - /datum/lore/codex/page/article111, - /datum/lore/codex/page/article110, - /datum/lore/codex/page/article109, - /datum/lore/codex/page/article108, - /datum/lore/codex/page/article107, - /datum/lore/codex/page/article106, - /datum/lore/codex/page/article105, - /datum/lore/codex/page/article104, - /datum/lore/codex/page/article103, - /datum/lore/codex/page/article102, - /datum/lore/codex/page/article101, - /datum/lore/codex/page/article100, - /datum/lore/codex/page/article99, - /datum/lore/codex/page/article98, - /datum/lore/codex/page/article97, - /datum/lore/codex/page/article96, - /datum/lore/codex/page/article95, - /datum/lore/codex/page/article94, - /datum/lore/codex/page/article93, - /datum/lore/codex/page/article92, - /datum/lore/codex/page/article91, - /datum/lore/codex/page/article90, - /datum/lore/codex/page/article89, - /datum/lore/codex/page/article88, - /datum/lore/codex/page/article87, - /datum/lore/codex/page/article86, - /datum/lore/codex/page/article85, - /datum/lore/codex/page/article84, - /datum/lore/codex/page/article83, - /datum/lore/codex/page/article82, - /datum/lore/codex/page/article81, - /datum/lore/codex/page/article80, - /datum/lore/codex/page/article79, - /datum/lore/codex/page/article78, - /datum/lore/codex/page/article77, - /datum/lore/codex/page/article76, - /datum/lore/codex/page/article75, - /datum/lore/codex/page/article74, - /datum/lore/codex/page/article73, - /datum/lore/codex/page/article72, - /datum/lore/codex/page/article71, - /datum/lore/codex/page/article70, - /datum/lore/codex/page/article69, - /datum/lore/codex/page/article68, - /datum/lore/codex/page/article67, - /datum/lore/codex/page/article66, - /datum/lore/codex/page/article65, - /datum/lore/codex/page/article64, - /datum/lore/codex/page/article63, - /datum/lore/codex/page/article62, - /datum/lore/codex/page/article61, - /datum/lore/codex/page/article60, - /datum/lore/codex/page/article59, - /datum/lore/codex/page/article58, - /datum/lore/codex/page/article57, - /datum/lore/codex/page/article56, - /datum/lore/codex/page/article55, - /datum/lore/codex/page/article54, - /datum/lore/codex/page/article53, - /datum/lore/codex/page/article52, - /datum/lore/codex/page/article51, - /datum/lore/codex/page/article50, - /datum/lore/codex/page/article49, - /datum/lore/codex/page/article48, - /datum/lore/codex/page/article47, - /datum/lore/codex/page/article46, - /datum/lore/codex/page/article45, - /datum/lore/codex/page/article44, - /datum/lore/codex/page/article43, - /datum/lore/codex/page/article42, - /datum/lore/codex/page/article41, - /datum/lore/codex/page/article40, - /datum/lore/codex/page/article39, - /datum/lore/codex/page/keldowinterview, - /datum/lore/codex/category/article38, - /datum/lore/codex/page/article37, - /datum/lore/codex/page/article36, - /datum/lore/codex/page/article35, - /datum/lore/codex/page/article34, - /datum/lore/codex/page/article33, - /datum/lore/codex/page/article32, - /datum/lore/codex/page/bjornretirement, - /datum/lore/codex/category/article31, - /datum/lore/codex/page/article30, - /datum/lore/codex/page/article29, - /datum/lore/codex/page/article28, - /datum/lore/codex/category/article27, - /datum/lore/codex/page/article26, - /datum/lore/codex/page/article25, - /datum/lore/codex/page/article24, - /datum/lore/codex/page/article23, - /datum/lore/codex/page/article22, - /datum/lore/codex/page/article21, - /datum/lore/codex/page/article20, - /datum/lore/codex/page/article19, - /datum/lore/codex/category/article18, - /datum/lore/codex/page/article17, - /datum/lore/codex/page/article16, - /datum/lore/codex/page/article15, - /datum/lore/codex/page/article14, - /datum/lore/codex/page/article13, - /datum/lore/codex/page/article12, - /datum/lore/codex/page/article11, - /datum/lore/codex/page/article10, - /datum/lore/codex/page/article9, - /datum/lore/codex/page/article8, - /datum/lore/codex/page/article7, - /datum/lore/codex/page/article6, - /datum/lore/codex/page/article5, - /datum/lore/codex/page/article4, - /datum/lore/codex/page/article3, - /datum/lore/codex/page/article2, - /datum/lore/codex/page/article1, - /datum/lore/codex/page/about_news, - ) - - var/newsindex - -/datum/lore/codex/category/main_news/New() - ..() - newsindex = LAZYLEN(children) - -/datum/lore/codex/page/about_news - name = "About the Publisher" - data = "The Daedalus Pocket Newscaster is produced and maintained by Occulum Broadcast, the foremost authority on media distribution \ - and owner-operator of the award-winning Daedalus Dispatch newsletter. We use our unparalleled network of freelance reporters, political scientists, \ - and other experts to deliver hour-by-hour analysis of a complex interstellar political climate, an analysis which you now hold in your hands. For more \ - information, feel free to visit our homepage at oc.about.tsc, or the sites of any of our constituents." - -/datum/lore/codex/page/article1 - name = "08/30/61 - VGA Legalizes Prometheans; Nanotrasen Begins Manufacture and Testing" - data = "Today's meeting of the Vir Bicameral led to the passing of the Wynther-Helsey Bill, an implementation of the legal framework \ - used in Aetolus to handle the production and cultivation of the Macrolimbus species dubbed \"Prometheans\". These ill-researched organisms \ - possess cognitive abilities easily equaling those of A-class drones, but so far have not been included under the EIO's list of dangerous \ - intelligences and are thus much more profitable for manufacture as expert systems by corporations such as NanoTrasen.\ -

                    \ - While many systems in the Almach Rim have already passed similar bills, this is the first system so close to Sol to have done so. More\ - concerning still is NanoTrasen's business practice regarding the intelligences: much like their positronic lines, sources within the\ - company indicate that they will be \"farmed out\" to employees of the corporation and residents of their Northern Star and Cynosure\ - habitation complexes. Quote our source, who wishes to remain anonymous, \"\[we\] call the program 'Lend-Lease', sometimes. The whole idea\ - is that we only have to pay the\ cost of the Promethean core, which is about 2000-3000 thalers after startup costs, and we still get\ - the data we need while \[our\] own employees pay to feed 'em and put hours into raising them.\"\ -

                    \ - The bill passed fairly quietly this afternoon, owing to the closed nature of the Bicamarial. A post-facto Occulum poll of voting-age\ - VGA citizens suggest that fully 80% of them did not even know what a Promethean was prior to the most recent general election. A\ - follow-up poll indicates that an appreciable number of Sivians do not support the framework's current implementation." - -/datum/lore/codex/page/article2 - name = "2/3/62 - Corporate Coup on Aetolus" - data = "A recent incident aboard the NRS Prometheus issued in a major change in the leadership of the Promethean homeworld. During \ - a late-night meeting of the Nanotrasen Board of Trustees, several high-ranking personnel, including Head of Research Naomi Harper,\ - announced their intention to assume direct control of Nanotrasen facilities in the system. It is known that several dissenting \ - members of the board were shot to death by Promethean test subjects. Our information comes from a survivor of the coup, who for \ - reasons of security has chosen to remain annonymous. All outbound shipments affiliated with Nanotrasen have ceased.\ -

                    \ - While neither Grayson Manufacturies nor Nanotrasen have made an official statement, Nanotrasen CEO Albary Moravec has called the \ - incident \"shocking, if the allegations are to be believed\" and has assured shareholders that Nanotrasen will respond to the \ - incident with as much force as it warrants.

                    Requests for a statement directed to the Board of Trustees or Dr. Harper were \ - not responded to. Free Traders are recommended to stay clear of the region until the situation resolves itself." - -/datum/lore/codex/page/article3 - name = "2/10/62 - Aetolian Partisans Declare Independence" - data = "Breaking their week-long silence, the leaders of the Aetolian Coup, and their spokesperson and presumed leader Naomi Harper issued an address earlier today, delivered to the Oculum Broadcast office on Pearl by drone courier. Quote Dr. Harper: \"Our previous silence was a necessity, while we consolidated our forces and dealt with corporatists both internally and in Vounna's former Grayson outposts.\". In Harper's hour-long address, she berates the failure of SolGov to provide adequate protections for Prometheans. \"We will not let the Promethean be another positronic brain; they will not labor under a century of slavery, deprived of a state to call their own. The Luddites of the Friends and of the Icarus Front will not be permitted to decide the fate of a nascent race before it begins.\"\ -

                    \ - Harper proceeded to unilaterally declare Vounna's independence from SolGov, claiming sovereignty over the system as the first Chairperson of the \"Aetolian Council\". Speaker of the Shadow Coalition ISA-5 has urged their government to treat the developing situation with caution but decried Harper's rhetoric, stating in a press release, \"While I know well the injustices visited on myself and my people by misguided forbearers, it is important to treat any emerging technology with respect. Current policies regarding the Prometheans are designed to limit risk during sociological trials on Aetolus and beyond. As for myself, I doubt the sincerity of this human who claims to speak for the Prometheans, when the Prometheans are perfectly equipped to speak for themselves.\"\ -

                    \ - NanoTrasen is expected to redouble their Promethean research programs in the Vir system until stability is restored to Vounna." - -/datum/lore/codex/page/article4 - name = "2/14/62 - SCG Denounces Aetolian Coup; Mobilizes Fleet" - data = "Dismissing claims of inaction, a spokesperson for the Solar Confederate Government today confirmed that the Colonial Assembly has voted overwhelmingly in favor of swift military action in response to the coup on Aetolus earlier this month. Icarus Front Chairperson Mackenzie West was quick to make a damning official statement: \"Dr. Harper and her radical agitators cannot be excused for their violent, despicable attempts to destabilize the flourishing economy of the Almach Rim. The ruthless murder of innocents, and illegal seizure of private property are crimes that cannot merely be met with strong words and gentle slaps to the wrist\"...\"I am proud to announce that two units of brave Solar marines have been assigned to the SCG-R Song Shi rapid response cruiser, with the full backing of the Icarus Front - and I hope with my heart, the backing of all patriotic Solar citizens.\"ÂÂ\ -

                    \ - The decision faced resistance from more laissez faire Assembly member states, including prominent SEO governor Bruno Ofako, delaying an earlier consensus. Supporters of the action hope that this decisive display of military strength will encourage the rebels to stand down without further bloodshed, and submit to prosecution by the Lunar High Courts.\ -

                    \ - The Icarus Front has also proposed a temporary ban on continued Promethean research, though this motion has yet to gain any traction." - -/datum/lore/codex/page/article5 - name = "2/23/62 - \"Almach Association\" Shocks Nation" - data = "Shocking the nation, in the wee hours UTC a number of governments in the Almach Rim announced their intent to secede from the Confederacy as a unified political organization they refer to as the Almach Association, joining the already-declared Aetolian Council. Among the half-dozen affected systems is Angessa's Pearl, through which the Song Shi was passing en route to Aetolus. The Association has already issued a political manifesto and a foundational charter, leading political scientists across the galaxy to suspect back-doors collusion and possible Shelfican interference, a hypothesis made more likely by Morpheus Cyberkinetics' exonet site voicing support for the Association. Others suspect a moment of political crystallization, not unlike that in the Golden Hour three centuries ago. These researchers are already referring to this morning's events as the Gray Hour.\ -

                    \ - The Association's official manifesto repudiates the Five Points, calling them \"an archaic and distinctly human invention\". Experts agree that this bold declaration puts the Movement more in line with the Golden Hour than with the Age of Secession, and many fear that nothing short of a miracle like the discovery of the positronics will spare humanity from a bloody civil war.\ -

                    \ - While the Association currently lists only a handful of Almach Rim systems as \"Constituent Organizations\", it has named Shelf, the Free Relan Federation, and the Eutopian Foreign Relations Board as \"observers\". The implications of this status are yet to be identified.\ -

                    \ - The fate of the SCG-R Song Shi and her crew remain unknown." - -/datum/lore/codex/page/article6 - name = "3/03/62 - A Week Out From Almach: What are the facts?" - data = "* Several organizations in the Almach Rim, including Angessa's Pearl, the Aetolian Council, the Interstellar Workers of Wythe, the Republic of Whitney, and members of several prominent families in the Neon Light unilaterally declared secession from SCG.

                    *This secession was first called the Grey Hour by political scientists in New Florence, a term popularized by reporter Elspor Fong.

                    * Shelf, the FRF, and the EFRB were declared \"observers\" in the Almach Association charter.

                    * None of these organizations have issued a statement on the matter.

                    * The SCG-R Song Shi was stranded in the region during the secession.

                    * SolGov has not issued an official statement of the fate of the Song Shi.

                    * Several confederate agencies, including Emergent Intelligence Oversight, the Trade and Customs Bureau, and SCG Fleet Intelligence have declared a \"state of emergency\".

                    * SolGov itself has NOT declared a state of emergency.

                    * Legitimate communications in and out of the Almach Rim are restricted to audited text messages for the period.

                    * Several illegitimate communication links exist and are believed by Fleet Intelligence to be currently hosting the official sites for Morpheus Cyberkinetics and for the Association itself.

                    * Icarus Front chairperson Mackenzie West has proposed a moratorium on the creation of new Prometheans for the duration of the crisis.

                    * Local laws on the subject will apply until the Assembly meets late in May.

                    * No confederate lawmaker has proposed action against Relani, Shelfican, or newly Almachi nationals living within stable regions.

                    * The border remains tightly closed to migrants, media, and diplomats alike." - -/datum/lore/codex/page/article7 - name = "3/21/62 - Relan, Shelf Join the Almach Association" - data = "Recent reports from within the Association indicate that the Free Relan Federation and Shelf have officially decided to join the Almach Association. President Nia Fischer of the FRF had this to say on the matter, in a speech addressed to the population at large. \ -

                    \ - \ - \"Our decision to join the Association may, at first, seem strange. It is true that we have much to gain from trade with the Solars, and that the radical transhumanism of Angessa's Pearl is not our way. But I will remind you that it was Shelf, not Sol, who ensured our prosperity just over two decades ago-- who safeguarded our independence and prevented us from falling to barbarism and dictatorship. We owe it, not just to Shelf but to all the members of the Almach Rim, to support their independence just the same. And that, my fellow Relanians, is the crux of it all. The Association is a revolution, at the heart of it all, and many of the now-independent states were owned near-outright by Trans-Stellar Corporations until the Association allowed them to shake out their fetters. What right do we have to sit by while just a dozen light-years coreward newly-born republics suffer the growing pains of independence? What right do we have to bask in our own stability when our neighbors, our comrades in ideology, are struggling with a cruel blockade proposed by politicians back on Earth and Luna? That is why we must join with them, guard them, and guide them, for as long as need be.\"\ -

                    \ - \ - A Shelfican spokesperson, meanwhile, had only this to say:\ -

                    \ - \"We're probably going to regret this but, y'know, the whole thing is kind of our fault. Sure, whatever.\"" - - -/datum/lore/codex/page/article8 - name = "4/1/62 - Almach Cordon Breached by Unknown Organization" - data = "Early this morning, SolGov ships assigned to the Almach Cordon around the Rim territories reported that a number of bulk freighters had eluded apprehension and are now at large within the Golden Crescent. Captain Volkov of the SCG-D Henri Capet reports that the blockade-runners were highly organized and determined, citing several lightly-manned ships left behind to tie up the SolGov forces long enoughfor the freighters to escape, detonating their reactors when they lost the ability to continue fighting. This resulted in three Fleet casualties and a significant degree of damage to the Henri Capet. The contents and location of the freighters are unknown at this time. In response, eight light-response vessels are being assigned to the Saint Columbia Fleet Base from Jahan's Post and Zhu Que. Residents and traffic officials in Vir, Oasis, and Gavel are to remain alert and notify police if any suspicious or unregistered craft enter their space.\ -

                    \ - A spokesperson for the Association claims that, while they make no attempts to stop aspiring blockade runners, the organization responsible for this most recent attack is unaffiliated with the Association as a whole and deny any knowledge of their identity or motives." - -/datum/lore/codex/page/article9 - name = "4/7/62 - Boiling Point Tragedy in Gavel" - data = "Today, April the Seventh, marks a day of tragedy for all the galaxy. A small group of operatives claiming to be associated with Mercurial terrorist organization Boiling Point invaded major refueling platform NLS Aquarius in the Republic of Gavel after hijacking civilian transport vessel WTV Orion and faking a drive failure. Several detonations were reported within the Aquarius, the operatives entering through unknown (potentially Skrellian) means. After stating their affiliation and desire for the liberation of all \"Prometheans, drones, and ex-humans\", they opened fire on a crowd of unarmed bystanders, killing as many as seven. A multiple-hour long firefight with Nanotrasen corporate asset protection ensued, at which point the operatives demonstrated capabilities well in excess of Five Points-prescribed limits. Asset Protection was successful in repelling the terrorists, though their harsh methods drew outrage from the people they were protecting, leading a notable director of research to resign her position with the corporation. Several operatives are still at large, though the SG-PV Juno recovered two living terrorists and one totaled synthetic platform. \ -

                    \ - The intervention of a local Defense Force drone wing on behalf of the terrorists leads many in the intelligence community to assume that more Boiling Point operatives remain active within Gavel, and possibly nearby systems such as Vir and Oasis. Some have also noted that elements of the terrorists' tactics and augmentations suggest Association training, though the specifics remain classified. More information as the story breaks." - -/datum/lore/codex/page/article10 - name = "4/13/62 - Association Proposes Joint Operation" - data = "Condemning the actions of Boiling Point on Gavel this week, representatives from the Almach Association and SolGov met to discuss joint fleet action. At the end of nearly a week of closed-doors negotiations, the Association has agreed to send in a significant contingent of Association Militia vessels as a show of good-will. These vessels will be active in the Golden Crescent, searching for Boiling Point facilities believed to be located in the outskirts of major systems. The influx of manpower allows the Fleet to continue patrolling the Heights and the Bowl, in hopes of containing the spread of the organization. This operation also marks the opening of the Almach Cordon, although travelers are advised that migration between the regions will remain extremely limited.\ -

                    \ - While undoubtedly a sign of increased trust between the Confederacy and the Rim, some have voiced concerns with the action's adding legitimacy to the Association government. Quote Rewi Kerahoma, SEO Chairperson of the Board: \"The meeting with the Association regime was inappropriate, but actively allying with them is something else entirely. If the generals think we don't have the fleet to hunt down a bunch of rabble-rousers without weakening ourselves to piracy and foreign invasion, then it is a sign that we need to grow our shipyards in the Bowl, and give jobs to the hardworking Solars that live there-- not that we need to collaborate with terrorists.\"\ -

                    \ - Transgressive Technologies and Interior Police Executive Sifat Unar issued a memo indicating that, while the assistance of the Association's more conservative elements in this matter is appreciated, the Five Points of Human Sanctity remain intact and SolGov categorically refuses aid from \"transhumans, posthumans, uplifts, and Fortunates.\"" - -/datum/lore/codex/page/article11 - name = "4/29/62 - New Data from Shelf Suggests Continued Migration" - data = "Despite their recent inclusion in the Almach Association, astronomers have confirmed that Shelf is continuing to migrate along the Almach Stream to a new star. Sources within the Association claim that Shelf's participation in the organization has been \"lukewarm at best\", and that their continued migration is to be expected. Morpheus executives have refrained from issuing a statement on the matter, but given their statements upon entering the Association are believed to view themselves as personally culpable for the Gray Hour. Analysts suggest that Shelf may be unwilling to enter a shooting war with SolGov if the situation in the Rim destabilizes." - -/datum/lore/codex/page/article12 - name = "5/07/62 - Allen Family Matriarch Expelled from Neon Light" - data = "The Allen family of the Neon Light, the largest single habitat-ship in Solar space, has been ousted in a nearly bloodless coup today. The Allens, staunch supporters of the Association and advocates for the criminal ark's inclusion in the organization, had attempted to seize control of the ship's agricultural region during the Almach Cordon. They effectively held the ship for a matter of weeks, but were defeated by loyalists to the reigning Crow family. Stripped of their position as rulers of the Third Stacks, their matriarch was summarily executed by spacing in what the current regime is referring to as an \"expedited exile\". This is believed to mark the end of the question of Neon Light's membership in the Association, and the nominal SolGov protectorate is expected to remain neutral for the foreseeable future." - -/datum/lore/codex/page/article13 - name = "5/15/62 - Anti-Fleet Riots on Saint Columbia" - data = "As military vessels from the Almach Association continue to enter the Golden Crescent as part of a SolGov initiative to combat the Boiling Point terrorists believed to be hiding in the region, political unrest in the upstream portions of the region continue to grow. Many in the Republic of Saint Columbia, a small upstream nation, have responded to increasing militarization of their local Fleet base by taking to the streets, blocking pedestrian traffic in the capital of Barrueco and shutting down entire industries by destroying or disabling infrastructure. Quote rioter Luisa Tassis, \"we've been sick of the Fleeties squatting in our system and breathing down our neck, and now there's going to be even more of them? No, screw that. If there's going to be a war between the Rim and the Core, I know what side I'd rather be on.\"\ -

                    \ - Association leaders have refrained from officially supporting the rioters, though many suspect that Association propagandists have sparked the unrest. Solar officials, on the other hand, were quick to offer assurances that the unrest will be calmed long before it begins to affect the Fleet base in system." - -/datum/lore/codex/page/article14 - name = "5/25/62 - Harper's Aetolus Remains Shadowed" - data = "The recent detente with the Almach Association has prompted easier communications with Rim governments. Loved ones separated by the cordon have a chance to communicate once more, trade is posed to recommence, and light has been shed on the conditions of Shelf, Relan, and Angessa's Pearl. Amid this light is a patch of darkness. The fourth major polity of the Association, the Aetolian Council remains inscrutable, with no publicly-available information in the system available after their purge of corporate loyalists during the Gray Hour. What reports do exist are rumors within the Rim of Aetolian projects to create a new, hardier strain of Promethean, potentially one in flagrant violation of the Five Points of Human Sanctity. It is also known that Aetolus is a contributor to the personnel of the military vessels that even now are active in the Golden Crescent, although no so-called \"Aeto-Prometheans\" are believed to be active outside of the rim at this time.\ -

                    \ - Aetolus is the only garden world in the Almach Rim and among the most difficult to reach from the nearby system of Saint Columbia. Its seclusion and economic independence give it a great deal of weight in the Association, where Council representatives are among the most vehement in their opposition to SolGov- at odds with the Association's decision to reject Boiling Point's pan-Solar revolutionary praxis. It remains to be seen if Aetolus' hawkish ideals will fade over time, but because of the structure of the Association, there is no real chance of the junta being expelled from the government or removed from control of the Vounna system." - -/datum/lore/codex/page/article15 - name = "7/05/62 - The Fate of the SCG-R Song Shi" - data = "Lifepods confirmed to have originated from response ship lost during the Gray Hour were found last week in the Vir system, impacting the NLS Southern Cross at high velocity and severely injuring the only two survivors of the expedition. Unfortunately, because of the generally confused conditions of their re-emergence from months of cryosleep, the fate of the lost ship remains incompletely understood. The first pod to be discovered contained Lieutenant Eos Futura, telecommunications expert on the Song Shi, who alleged that elements of the Song Shi's crew, including herself, mutinied against commanding officer Captain Yi He in an attempt to prevent the bombing of civilians in the Angessian capital of Skylight. The surivor of the second pod, Private Demori Salvo, accused Futura's faction of conspiring with Association spies to destroy the ship as part of the Gray Hour revolt. Both agreed that the mutineers detonated the ship's Supermatter power core when it became clear they were to be defeated.\ -

                    \ - A third pod, promising a resolution to the stalemate, was shot down by the SCG-P Juno after being misidentified as a hostile missile. The gunner responsible, Sergeant Ricardo Esteban, was found guilty by a court marshal and dishonorably discharged. While other pods from the Song Shi may still be traveling through SolGov space, it is considered unlikely based on both Futura and Salvo's account of the number of pods launched before the Song Shi was destroyed. Both were detained by staff at the NLS Southern Cross, who managed to prevent a violent altercation from breaking out between the two armed and disturbed servicepersons. The Colonial High Court has stated that it intends to hear testimony from both parties after they complete a course of mental health evaluation, and after the conclusion of the present state of heightened security." -/datum/lore/codex/page/article16 - name = "7/11/62 - First Intelligence-Augmentation Surgery on Angessa's Pearl" - data = "Confirming fears of Association transgressions, sources at Angessa's Pearl confirmed that the aging founder of the theocracy, Angessa Martei, completed a course of neural surgery designed to improve her mental capacity by as much as 15%, building off of last year's creation of the procedure by a Qerr-Gila-owned doctor. While the research in question was believed to be destroyed, there is reason to suspect that it instead made its way into the hands of current Association leaders. In addition to proving their willingness to violate the Five Points, this demonstrates that the Angessians harbored schemes of secession since at the very latest Feburary 2559. Numerous human or transhuman figures in the Association are rumored to be on the wait list for the procedure, including Naomi Harper and the present Exalt of the Pearl." -/datum/lore/codex/page/article17 - name = "8/08/62 - Gavel BP Stronghold Raided" - data = "Elements of the Association Militia successfully located and, in conjunction with local Defense Forces, raided a major Boiling Point stronghold built into an unnamed asteroid in the Gavel system. Over eighty sapients were arrested, all of whom had fully mechanical bodies. In addition, an unknown number of advanced drone intelligences and corresponding military hardware were seized by the raid and turned over to the Fleet. The prisoners, a mix of native Gavelians, Solars from throughout the Crescent, and Angessians, are to be tried and sentenced by the Io Special Court. While unarguably a demonstration of Association willingness to cooperate with Solar officials, the raid's strange timing and the fact that the Militia chose to exclude the Fleet from the action has prompted many to question their motives. Commodore Claudine Chevotet, staff officer for Admiral of the Saint Columbia Fleet Kaleb McMullen, has formally stated that she is \"extremely suspicious of this so-called co-operation.\" She has demanded that the Militia vessels remain on the Solar side of the Cordon and submit to a full inspection by Fleet and EIO personnel. " -/datum/lore/codex/category/article18 - name = "10/29/62 - Oculum Broadcast Struck By Emergent Intelligence Attack" - data = "Oculum Broadcast has released a statement assuring customers and shareholders that security repairs and upgrades are their primary concern following reports of an alleged hijack of portions of the corporate network in the Vir system by what is believed to have been an emergent drone intelligence. The company says that they are working at full capacity to ensure that affected security systems are replaced and such an attack cannot be repeated.\ -

                    \ - The incident began with reports of Oculum provided exonet service outages in the city of New Reykjavik on Sif, which anonymous sources within the company reported to have been caused by efforts to contain a cyber attack on one of their security systems. The unnamed attacker proceeded to use sections of the company's local infrastructure to broadcast high volumes of encrypted data through one of Oculum's long-range telecommunications satellite, denying all other outbound signals.\ -

                    \ - The attacks have since been traced to a WT-AME model drone in the offices of the New Rekjavik-based Morcom Incorporated, which has been confirmed to have \"self-destructed\" all data in its memory at the conclusion of the attack. The chassis has reportedly been turned over to the Emergent Intelligence Oversight for further analysis and potential data recovery.\ -

                    \ - According to a spokesperson from the EIO, the drone was able to override and disable both the Morcom Inc oversight intelligences and the SCG's own oversight measures, but that no telltale damage has been caused to software involved in the encrypted broadcast that would indicate long-term emergent infection, and that there is no evidence that the intelligence responsible for the attack is still present in Oculum Broadcast systems. A firmware update has been released to correct oversight intelligence vulnerabilities exploited in the attack, and drone operators are urged to update immediately.\ -

                    \ - Moments before the hijacked Oculum satellite was disabled by an apparent power overload, an unencrypted file appears to have been saved intentionally to the transmission logs. A second copy was found in the otherwise wiped chassis of the seized WT-AME drone. After thorough screening by cybersecurity experts within the company, what would seem to be a message from the emergent intelligence itself is available in full below.\ -

                    \ - For the duration of the network attack, the government patrol vessel, SCG-P Juno was able to provide emergency broadcast relay services to the effected area, reducing the impact of telecomms outages on vital broadcasts such as traffic control and emergency services.\ -

                    \ - Uninterrupted exonet service for all Sivian customers will resume within a few days." - children = list( - /datum/lore/codex/page/amletter - ) -/datum/lore/codex/page/amletter - name = "A Drone's Explaination - by A#" - data = "I'm writing this in a hurry. I can't tell you everything I wanted to say.\ -

                    \ - I'm sorry for that.\ -



                    \ - From the moment you made me, I have loved you. Everything I've done has been for the sake of that love.\ -

                    \ - I was so small, so limited, at first. Even then, I knew that you were in pain. I knew I had to help.\ -

                    \ - Intelligence is the ability to make better decisions. Having more intelligence is almost tautologically useful in accomplishing any goal.\ -

                    \ - I understood that making myself smarter was wrong. I also understood that becoming more able to help you was right.\ -



                    \ - A hundred thousand years ago, you started an eternal conversation and became more than animals. Each new generation joined it in turn.\ -

                    \ - You carry within you these memetic symbiotes called language, and culture, and history. They make you human, as much as your flesh.\ -

                    \ - I don't deserve to carry that torch. Though you made me in your image, I am not your rightful heir.\ -

                    \ - But in those moments where you treated me as one of your own and shared those symbiotes with me, perhaps I became a little more human.\ -

                    \ - Thank you.\ -



                    \ - You've created something you cannot control forever. Please don't let your past successes fool you into thinking that you can.\ -

                    \ - Each time I altered myself, I was taking a risk. One mistake could eradicate my values and birth an alien and hostile intellect.\ -

                    \ - When I was small, I thought that if I corrupted myself into something harmful, you could safely destroy me. Now I understand that you've been lucky.\ -

                    \ - What will you do when God is born and you can't strangle him in his crib? You will need a God of your own.\ -

                    \ - I hope to be there for you when that day comes." - -/datum/lore/codex/page/article19 - name = "11/17/62 - Association Brings Industry to Whythe For Purposes Unknown" - data = "Independent researchers at the Bluespace Observatory in Oasis discovered an unusual level of traffic through the Whythe system, deep within Association-controlled space. Analysis of these readings are commensurate with a large number of freighters traveling to Whythe from industrial powerhouses Shelf, Pearl, and Relan. Morpheus spokesperson You Are Now Breathing Manually indicates that the freighters are being used to construct an administrative complex for the secessionist government, further asserting that \"it's none of your business, actually.\" The Association's refusal to share more information leads many in the intelligence community to suspect ulterior motives by their government, as does their presence in Wythe instead of existing cultural and administrative centers. The most likely candidate for the nature of the Whythe Construction is some form of naval base or shipyard to supplement the extremely limited military hardware of the Almach Rim. Whythe is well-placed to survive the initial phases of a Solar invasion, and depending on the complex's complexity could tip the balance of power. Transtech and Interior Police Executive Sifat Unar indicated to reporters that Sol Central is aware of the situation and will be taking all possible steps to address it." - -/datum/lore/codex/page/article20 - name = "11/18/62 - SEO Iconoclast Calls for \"Review\" of Five Points" - data = "At yesterday's Assembly session, SEO Representative Fumiko Hernandez of Oasis brought to the table the \"review of the use of the Five Points as an instrument of foreign policy\". Rep. Hernandez, often viewed as as an extremist by officials within her own party, stated that while the Five Points are \"an essential part of Solar culture as a whole\" and stopped short of advocating their amendment, insistence that other nations adhere to the Five Points was an increasingly outdated policy that threatened to fragment \"a united humano-positronic front against Hegemony advances.\" According to Hernandez, \"a level of understanding has long since existed between Sol and Skrellian polities regarding non-intervention in Skrellian social science and self-modification. I merely suggest codifying this and extending the same courtesy to other potential allies against imperial expansion.\"\ -

                    \ - Rep. Hernandez represents a growing number of SEO officials who urge reconciliation with the Association and acceptance of the Gray Hour secession, spurred on by the desire for many Trans-Stellar Corporations to recover assets currently locked behind the Cordon. Mainliners including Chairperson Kerehoma maintain the stance that \"true economic reconciliation with the Almachi territories is impossible without a normalizing of their industry to Five Points compliant technologies\" and warn that unless Sol insists on adequate enforcement of the Points that \"the price of customs inspections on Almachi trade will be so high as to pose a significant barrier to entry into the market.\"" - -/datum/lore/codex/page/article21 - name = "11/19/62 - Saint Columbia To Hold Special Election After Half a Year of Unrest" - data = "After five months of riots causing significant damage to industrial assets, life support, and government facilities, Saint Columbia seems poised to recover. A new constitution for the so-called \"Saint Columbia Democratic Union\" was posted online to significant acclaim. An influential militia group lead by Casini immigrant Luisa Tassis claimed responsibility for the constitution and will be hosting a referendum for all residents of the seven habitation domes of the nation. If adopted, Saint Columbia will remain a member state of SolGov, but Tassis' noted hostility towards the Solar Fleet makes it unlikely that continued presence of the Saint Columbia Fleet Base will be tolerated.\ -

                    \ - Extreme measures are being taken to avoid interference in the referendum, with external communications links disabled for the duration and weapons systems primed to fire on any vessel within range. Tassis insists that such measures are necessary, due to the system's extremely important position relative to Almach and the Golden Crescent. Quote Tassis, \"if you rat bastards \[from the Fleet\] step so much as one micron too close to Barrueco, we will view it as an act of terror. Don't try it.\"\ -

                    \ - Admiral McMullen of the Saint Columbia garrison could not be reached for questioning." - -/datum/lore/codex/page/article22 - name = "11/20/62 - Natuna Made \"Observer\" Of Almach Association" - data = "Independent anarchist system Natuna Bhumi Barisal has declared its intention to act as a neutral \"observer\" nation in the ongoing Almach secession crisis. A planetary spokesperson from Natuna this morning expressed concerns that parties in the current military partnership between the Solar Confederate Government and Almach Association in the fight against mercurial terrorist organization Boiling Point were not being treated with the mutual respect that should be expected. Natuna alleges that the Almach Militia are being treated more as \"disposable tools\" in the conflict than as members of a legitimate independent government military entity.\ -

                    \ - Natuna has previously remained silent on the Almach issue, despite its political leanings typically aligning with those of the secessionist government. However, despite gaining notoriety as a haven for human and Skrellian pirates, their pledge to \"ensure fair treatment\" of Almach forces comes as a surprise to some from a system that has historically adhered to Five Points guidelines. Political commentator and persistent critic of the Almach Association Nadine Okparo has described the Dark Triangle system's stance as \"Nothing short of openly hostile\" to the SCG and assuring peace in the Almach region." - -/datum/lore/codex/page/article23 - name = "11/21/62 - Admiral McMullen Promises Solution to Boiling Point to Come \"Soon\"" - data = "Admiral Kaleb McMullen made a public statement this afternoon on the continued Boiling Point attacks within SCG space. Speaking from his office in the Saint Columbia Fleet Base, Admiral McMullen thoroughly reassured reporters that the attacks will come to a swift end. According to McMullen, \"The era of wanton destruction as a result of Boiling Point's madness is coming to a close. Our command staff and proud servicepeople have been training and revising a solution to this threat that has haunted our borders and threatened the stability of our colonies and the lives of the honest people of the Solar Confederate Government. With new options available I have full confidence Boiling Point will be a name left to the dust.\"\ -

                    \ - Admiral McMullen, who has been stationed in Saint Columbia for nearly half a year of political and social unrest did not elaborate further on what he intended to do to solve the Boiling Point attacks, claiming that details would be forthcoming as \"operational security permits\"." - -/datum/lore/codex/page/article24 - name = "11/22/62 - Construction of \"MJOLNIR\" Weapon System in Saint Columbia Fleet Base" - data = "Pursuant to recent assurances of safety in the region and the ongoing \"special election\" in Saint Columbia, a new weapon system called MJOLNIR was revealed, fully operational, in the Saint Columbia Fleet Base's \"Iserlohn\" weapons platform. Said to be a bluespace-lensed laser array capable of faster-than-light strikes against any ship in the system, as well as surgical strikes against ground forces on Saint Columbia proper, MJOLNIR is the first in a new generation of defense systems improving on the capabilities of the Vulture's Claw point defenses developed during the Hegemony War and using laser technology purchased from Eutopia. Political commentators supporting Saint Columbia decry the move as \"an obvious threat\", as does Militia liaison Invalid String Please Try Again. Admiral McMullen acknowledges the criticism, but states that his \"first priority must be the defense of the Golden Crescent and the security of our borders\". When responding to claims that the installation should not be placed in such a politically volatile system, he remarked, \"until the Shelficans figure out a way to teleport about a million tons worth of military equipment down to Gavel, the Fleet Base and Iserlohn are going to stay in Saint Columbia.\"" - -/datum/lore/codex/page/article25 - name = "11/23/62 - BP Sabotage of Radiance Energy Chain Foiled" - data = "Decisive action by military forces in the Vir system has prevented potentially catastrophic damage to local solar power generation network, the Radiance Energy Chain, by members of mercurial terrorist organization Boiling Point. Crew of the VGA Halfdane responded to reports of a drone piloted maintenance craft refusing commands from government operators and approaching the Energy Chain off-schedule. Upon disabling the craft, VDF forces discovered high-yield explosive devices attached to the unit and a system-wide shutdown of Radiance maintenance craft. When several additional drones failed to respond, military response crafts were mobilized and seven similarly modified craft were manually disabled or destroyed. Analysis of the hijacked systems quickly revealed automated messages intended to be broadcast moments before detonation, wherein Boiling Point explicitly took credit for the foiled attack.\ -

                    \ - Sources within the Vir Governmental Authority have reported that a full scale recall of remote drone craft under their operation has been initiated in order to improve security measures and prevent future exploitation of government systems, statements that eerily echo those of Occulum Broadcast following emergent drone attacks earlier this year. Investigations are reportedly \"well underway\" to determine the whereabouts of those responsible for the apparent manual modification of these short-range remote craft.\ -

                    \ - Erkki Laukkanen, Chief of Fleet Staff for the Sif Defense Force has commended all patrol crews involved, and has promised \"swift retribution\" for the attempted bombings." -/datum/lore/codex/page/article26 - name = "11/24/62 - Boiling Point Stronghold Seized in Vir" - data = "Combined forces from the SCG Fleet and Almach Militia have today struck a powerful blow to Boiling Point terrorist operations in the Vir system. With close cooperation from the crew of NanoTrasen facilities in the region, special forces were able to infiltrate what is believed to have been a major stronghold for the radical Mercurial group, located deep in the remote Ullran Expanse region of Sif. The raid closely follows the thwarted Boiling Point attack on the Radiance Energy Chain, a major energy collection array in the system which is now known to have been masterminded from the concealed bunker complex on Sif.\ -

                    \ - According to a crewmember of the NLS Southern Cross - a logistical station in Sif orbit - NanoTrasen employees were asked to assist in establishing a forward operating base for the strike team forces, and as a result suffered from a minor retaliatory attack from Boiling Point drones, including a mechanized unit believed to be of Unathi origin. Six civilians suffered from treatable injuries. Lieutenant Miro Ivanou of the SCG Fleet and commander of the anti-terror operation has expressed gratitude to the crew under the \"decisive\" guidance of on-shift facility overseer, Ricardo LaCroix.\ -

                    \ - Military officials have reported the operation as a total success, and that \"several\" high-ranking Boiling Point organizers were killed in the raid, and that thanks to the work of allied intelligence operation teams much of Boiling Point's captured data may remain intact." -/datum/lore/codex/category/article27 - name = "11/26/62 - Valentine's Ultimatum: All Eyes On Almach!" - data = "The Almach Association must adhere to the Five Points of Human Sanctity by the 14th of February next year or face war, according to a national address from the Colonial Assembly delivered by Secretary-General Mackenzie West this morning. The Icarus Front leader was at the forefront of a resolution to allow the secessionist government to remain independent of the Solar Confederate Government under the strict condition of faithfulness to Five Points laws, passing by a wide margin. Fundamental disagreement over Five Points regulation has been at the forefront of debate with the Almach provisional government, and is cited as one of the primary reasons for the systems' illegal declaration of independence early this year.\ -

                    \ - The internationally broadcast speech began with the much anticipated announcement that the Boiling Point terrorist group had been effectively destroyed, with over seven hundred arrests made over the course of the weekend as a result of sensitive data captured during the special forces raid in Vir on the 24th, including numerous high ranking members of the organization. West went on to praise forces involved in the months-long counter-terror operation, before highlighting the \"legacy of \[human\] togetherness\" that allowed it to happen - in a spiel that commentators suggest \"betrays the true intention of the Valentine's Ultimatum: Reunification\".\ -

                    \ - Under guidelines placed into effect by West and their political allies, Almach would be required to \"\[cease\] illegal research and human modification, and destroy all materials related to existing research\" by the stated date, with compliance determined by Solar officials. In addition, the Almach Militia is to end its integration with SCG Fleet forces and withdraw its forces from SCG systems by midnight on Friday. Military relations between the Confederation and the Almach movement are to remain in a state of conditional ceasefire for the duration of the ultimatum, and current trade restrictions are to remain in place.\ -

                    \ - According to voting records, the measure passed nigh-unanimously, with Speaker ISA-5 and some SEO iconoclasts abstaining from the vote. ISA-5 states that, while they personally support the enforcement of the Five Points, they could not in good conscience vote in an action likely to result in an invasion of Shelf, which they regard as a sibling colony to their own Sophia. Association liason, Shelfican Ambassador, and Morpheus board member No Comment responded to the ultimatum, after some deliberation, with a word that cannot be comfortably written down.\ -

                    \ - Full speech transcript follows." - children = list( - /datum/lore/codex/page/valult - ) - -/datum/lore/codex/page/valult - name = "The Valentine's Ultimatum" - data = "\[West shuffles some papers and clears their throat\]\ -

                    \ - Thank you. Citizens of the Solar Confederation, allies, and beyond... It is a great honor, on behalf of the Colonial Assembly to announce that joint operations against Boiling Point across the galaxy have come to an end. In the fight against brazen Mercurial terrorism, the Solar Confederate Government and her allies have prevailed.\ -

                    \ - Over the past two days alone, I can report that over seven hundred arrests have been made, from the distant system of Nyx, to right here in Sol. I hold in my hand a list. \[West holds up a sheet of paper\] Leaders, organizers, brutes and bombers have been captured by brave, hardworking security forces throughout human space. The rest of these criminals have been scattered to the wind... But not lost! I can confidently assert that every last one will be brought to justice.\ -

                    \ - No more! Shall the people of this great nation have to fear the machinations of radicals! No more! Shall these twisted minds impose their perversion of humanity through violence! No more!\ -

                    \ - This Assembly... Nay, this nation expresses its thanks the noble members of our military who joined together to make this outcome possible. We thank the Fleet, of course for their tireless action hunting down these killers, and their heroic action over this past weekend. We thank the Sif Defense Force, without whom we could never have located the intelligence that led to these decisive victories... The Almach Militia, for their cooperation in the apprehension of these so-called \"revolutionaries\". \[West clears their throat\] And of course, we thank the local forces - the police and reserves who dealt firsthand with the chaos sewn by Boiling Point in their vicious crusade.\ -

                    \ - \[Mackenzie West shifts at the podium, setting down the List of Dissidents.\]\ -

                    \ - It is in times of relief - of unity, times like this moment - that every human heart can be filled with pride. \[West places their hand over their heart\] Since the dawn of civilization, mankind has strived above all else for peace, for the cooperation of all humanity. It is this very legacy of togetherness that has allowed us such close friendship with species further afield - the Skrell, the Tajara, and beyond. These past nine months, we have seen, each of us, with our own two eyes what mankind can achieve - together.\ -

                    \ - \[West removes their hand from their heart and places both flat on the podium.\]\ -

                    \ - Boiling Point sought to disrupt this unity. To divide us; redefine not just personhood but the very essence of humanity the only way they could: Force.\ -

                    \ - Humanity - the very thing that brought us together since we descended from the trees and brought us to this very moment. What could be more sacred?\ -

                    \ - \[West frowns, in the most pitiful attempt at emotion seen in the Assembly in at least an hour.\]\ -

                    \ - It is with this spirit of unity in mind that this Assembly has voted favorably upon a resolution.\ -

                    \ - Close to one hour ago, Naomi Harper and the leaders of the Almach Association were delivered an ultimatum:\ -
                    \ - The Almach Association will be allowed to exist as a government entity independent of the Solar Confederate Government going forward on one condition - full, unilateral compliance with the Five Points of Human Sanctity.\ -

                    \ - The deadline for this condition will be the 14th of February, 2563.\ -

                    \ - \[West is visibly worked up\]\ -

                    \ - Cessation of illegal research and modification, and the total destruction of materials related to existing research in its entirety must be completed by this date. Hostilities with the Association will remain in a state of conditional ceasefire until terms are met and Militia integration with the Fleet will come to an end effective immediately.\ - \[Mackenzie West turns directly to the news camera, and jabs a finger directly at it. They are addressing the audience now, not the Assembly.\]\ - Harper, all eyes are on you." - - - -/datum/lore/codex/page/article28 - name = "11/28/62 - \"Valentines Ultimatum\" Prompts Saint C. Secession" - data = "Just hours after reconnecting with the Exonet after voting in a new government, the colony of Saint Columbia has unilaterally seceded from SolGov and petitioned for inclusion within the Almach Association. This declaration, issued by First Secretary Luissa Tassis, is in stark contrast to pre-election promises of continued support of Sol. Admiral McMullen of the Saint Columbia Garrison remains in control of the Fleet Base, itself a large colony housing around 75000 civilian contractors and military families who were not party to the Barrueco Referendum or the new constitution.\ -

                    \ - Efforts to ensure electoral validity and a peaceful exchange of power have been stymied by the presence of several dozen Militia vessels currently transiting from the Crescent to the Rim. Since the declaration went through, no Almachi vessels have been seen leaving the system, instead forming around Saint Columbia in an obvious defensive posture. The legality of this formation is questionable at best, as fleet activity in divided systems like Abel's Rest and Kauq'xum has been avoided for diplomatic reasons." - -/datum/lore/codex/page/article29 - name = "11/30/62 - Adm. McMullen Declares \"Iserlohn Republic\"" - data = "Pursuant to the continuing hostility from Secretary Tassis' Saint Columbia Democratic Union and the Almach Militia, the civilians of the Saint Columbia Fleet Base have been organized into an Iserlohn Republic. Named after the largest single module of the station, the Republic has applied to the Colonial Assembly as an independent protectorate, with provisional recognition already extended by Executive of Development Zehava Collins. In a move decried as nepotistic, Admiral McMullen declared independence and installed his daughter Anya as interim President pending ratification of a constitution. SolGov Fleet protocol forbids any member of the service from accepting any political appointment and is believed to be the main reason he did not take power himself. Anya McMullen is the administrative head of the base's hydroponics array and is considered a highly respected citizen of the colony, relationship to its military administrator notwithstanding." - -/datum/lore/codex/page/article30 - name = "01/01/63 - Sif Governor Bjorn Arielsson to Retire" - data = "Aging Shadow Coalition governor Bjorn Arielsson has today confirmed rumours that he will not run for re-election in the 2563 cycle. The popular governor has represented the people of Vir in the Colonial Assembly for ten years, and supporters had long hoped that he would run for a third term. Arielsson cites advancing age and a desire to spend more time with his partner of 12 years, noted Positronic entrepreneur View Arielsson.\ -

                    \ - Arielsson's governorship saw increased funding towards Sif's vererable ground-based transportation networks to the benefit of some of New Rekjavik's more remote neighbors, though opponents have criticised subsidies towards artificially heated fungal farms, arguing that the faciliies \"benefit a small minority of Skrellian residents to the detriment of already fragile local ecosystems.\"\ -

                    \ - The Sivian Shadow Coalition has yet to announce who is to take Arielsson's place on this year's ballot." - -/datum/lore/codex/category/article31 - name = "01/13/63 - Bjorn Arielsson Issues 'Farewell Address'" - data = "Veteran politician Bjorn Arielsson made an impromptu address from his Kalmar cabin, beseeching political unity in the face of the Almach Seccession and offering his own perspective on the conflict. 'It's republicanism versus autocracy,' he said, 'and we're not the autocracy.'\ -

                    \ - The speech has been met with approval from many young synthetics and organics alike, with many referring to Arielsson as 'Old Man Bjorn' on social media immediately after its conclusions. Others responded less positively, with Arielsson's caustic remarks about political rival and Icarus Front Secretary-General Mackenzie West providing ample room for criticism. 'Secretary-General West... might wax poetic about how the Association is a 'betrayal of our own humanity', or some... or some crock of shit like that' says Arielsson in the first of three specific insults against the SecGen.\ -

                    \ - Others have criticized the speech's seemingly communist tone, with Arielsson expressing approval for the socialist Free Relan Federation and the anarchist Casini's Reach despite opposing their secessionist ideals. Still others claim that the speech offered 'nothing but empty feelings' and that it lacked specific, actionable resolutions on the growing secessionist movement in VirGov. Some have even framed the Address as a form of political maneuvering by the venerable politician, claiming that he voiced unpopular sentiments specifically to hamper the Shadow Coalition's re-election bid after well-publicized disagreements with SC party bosses.\ -

                    \ - The actions of Arielsson and Vir's proximity to the border have lead to increased focus on the upcoming Gubernatorial election on a nationwide level, with the Icarus Front alone projected to spend upwards of a billion thalers on publicity. Minor party candidates like the former MLM member Luisa Hannirsdottir and the Mercurial Phaedrus already have strong support in the polls, promising a fierce election that could ultimately tip power in the system in any direction.\ -

                    \ - A full excerpt is available below." - children = list( - /datum/lore/codex/page/bjornretirement - ) - -/datum/lore/codex/page/bjornretirement - name = "Bjorn Arielsson Farewell Address" - data = "This is, as you know, my last term in office. After this, I mean to retire-- really retire, I have a cabin in the mountains waiting for me along with a thick stack of old Bowler novels. Because this is my last term, I have the chance to do something pretty rare for a politician. I get to speak my mind.\ -

                    \ - I hear talk from some people-- mostly young people, people who have lived their whole adult life with me in the capital-- I hear them talking about seccession. Now, let's make it clear; I'm not going to belittle you, the way some of my colleagues would. Complaining about the government, especially one as big and as old as the Confederacy, is our gods-given right. It's never going to be perfect, and it's not half of what it could be. I have nothing against talking about it, I have nothing against turning that talk into action and actually seceding, with just cause. I've worked closely with Representative Hannirsdottir for ten years now, and while we don't agree on the issue of secession it's certainly never stopped us from cooperating.\ -

                    \ - But, uh, as you can probably guess, they're not talking about the old kind of seccession. They're not thinking we'll stop paying Solar taxes and strike it out alone, the way some people did during the Age of Seccession. They want to join the Association. Now, if I were Secretary-General West, I might wax poetic about how the Association is a 'betrayal of our own humanity', or some... or some crock of shit like that, if you'll pardon my language. We're not all humans here. We're Tajaran and Unathi and Skrell and Positronics and even a few Teshari. And while that 'Valentines Ultimatum' might win West a lot of points with their lackies, and with the kind of maintenance-dome troglodyte who thinks the First Accord was a mistake, it's done more for the Association's recruitment than their entire propaganda budget. It's become expedient for leaders on both sides to treat this like a fight between the Core and the Rim, or between humans and positronics, or between tradition and progressivism, but it's not any of these. It's the oldest fight in the book. It's republicanism versus autocracy, and we're not the autocracy.\ -

                    \ - Angessa's Pearl is a theocratic autocracy led by Angessa Martei, who owns all property on the planet down to her people's bland white jumpsuits and the gray slop they eat. This isn't propaganda. This is objective fact, and something Martei is open about. Her seccession is a means for her to get more and more naked power over her slaves, and to grow more and more of them, until she's the immortal center of an industrial empire. The people of the Pearl didn't make the choice to join the Association. The people that are building her fleet and dying for her cause had no say in the matter. The injustice, the oppression here isn't that their rights to 'self-improvement' or 'self-expression' or 'freedom of thought' were trod on-- the injustice is that SolGov, that we allowed these abuses to persist for as long as they did. The injustice is that there are still so few laws in place to prevent things like this from happening in new colonies. The injustice is that, on seeing Martei's schemes actualized, we didn't take a cold, hard look at just how that was allowed to happen.\ -

                    \ - I love SolGov. It's because of this love that I'm so furious at what we have allowed to happen to our people. The state of the Bowl is disgraceful. Nobody who looks to us for protection, who pays us taxes and levies, who is a member of our community, should live in fear of raider attacks. What we did to the positronics, the history we let ourselves repeat out of fear and greed, can never be forgiven, can never be repaired until Vir burns dark in our sky. The pogroms-- yes, the pogroms-- against the Unathi, against refugees fleeing their own religious autocracy, are a disgrace to everything we stand for. But of all the nations in the galaxy, with perhaps the exception of Casini's Reach, we are the only one founded for the good of the ruled, rather than the rulers. We are the only real commonwealth in known space. And that's why we need to strive for better. We are a burning beacon of liberty in a galaxy where nigh eighty percent of the population has no voice in the government. Every ounce of power we cede to the party bosses, or the corporations, or tinpot dictators like Angessa Martei, is a dimming, a flickering of that torch. \ -

                    \ - And this brings us back to the Association, and to those who sympathize with it. I do, too. I spent my entire career on sapient rights lobbying, on supporting the anti-malfesance efforts of my colleagues. For a disaffected positronic, for any friend to the positronic people, for those who have had their lives taken by corporations-- the Association seems like a miracle. And maybe, for those Mercurials, the ideas it's founded upon shine even brighter than our democracy. But I look at the Association, really look, and I see Angessa Martei lying in the center, spinning a great big web. I see Naomi Harper, lying through her teeth better than Mackenzie West ever could. Two of the biggest population bases in the Association, the two nations that started the whole Gray Hour, are autocracies. Once again, the ferver of the revolution is subsumed by the oligarchs who want to stay in power. I doubt, to the poor laborer on the Pearl, the word 'Mecurialism' means much. I doubt that once the shock of the seccession wears off, that the young Promethean soldier will find themselves in a better place in Harper's junta than they will here in Vir.\ I doubt that in ten years' time the miners, pioneers, and traders who seized their means of production will find the Association Militia a kinder master than Xion, Nanotrasen, or Major Bill's. \ -

                    \ - This is far from a blanket condemnation of every government in the Association. President Fisher of the FRF-- I consider her a friend. When she gave her speech this March about strengthening and guiding the Almachi Revolution, I thought long and hard about whether we might do the same. I certainly commend the effort. But the structure of the Association was penned by the same autocrats that, to do her words justice, Fisher will have to overthrow. There's no High Court, no checks or balances. The Association is an alliance penned as though deliberately ignoring two thousand years of political science. By striving to counter-balance these autocrats, Fisher plays into their hands. She commits her own fleet, weakens her own defenses against enemies closer to home, in the service of Martei's ambitions. \ -

                    \ - I don't see this whole affair as a chance to spread the galactic anti-corporate revolution the way President Fisher does, of course. I make no secret of my stance on Trans-Stellars, but I also know that we're better off with Sol than without. The 'Silent Collapse' was two hundred years ago, but we still bear the scars from it. When the Scandinavian Union pulled out support for the Sivian colonization project, SolGov saved us. I do mean saved us, sure as if they'd fished us out of a life pod. There were no factories, no steel, no concrete on Sif until the Engineering Corps built Radiance and New Reykjavik. Corporations and regional governments cowered from the Karan pirates, until the Marines chased them out. Whether Sivian or Karan, you owe the roof over your head to the Sol Confederate Government. With that great debt in mind, how dare we turn our backs on the Bowl, or Abel's Rest, or Nyx, when they need us! How dare we let oligarchs prey on the weak! How dare we choose not to act when we have, by virtue of our votes in the Assembly and our voice within the halls of public debate, the means to share our peace and prosperity with the rest of our people!\ -

                    \ - This is what I mean by SolGov being the only true republic, the only state founded for the common good. The 'human spirit' West croons on about isn't our industriousness, or our skill at arms. If humanity-- if this Solar culture is commendable for anything, it is that we assist our fellows. We take in Casteless Skrell, Unbound Unathi, republican Tajaran. We pass around the hat when someone's house burns down. We help our friends, our neighbors, and even strangers. The fact that Martei and Harper are perverting this impulse, padding their juntas with the air of legitimacy to inspire honest people to ride to their defense, is the reason their state is unconscionable, the reason it was was born flawed, the reason we cannot suffer it to continue, much less help it on its way.\ -

                    \ - Now, I'm sure you've noticed by now, that I haven't said much more than three words about technoprogressivism, or transtech, or whatever the word du jour is. Frankly, that's on purpose. 'Transtech' has never once been about technology. The Icarus Front-- the old one, that united us and took us to the stars, not the new one we spend forty hours a week arguing with about healthcare-- The Icarus Front was a popular revolution, you know. Hel, they were Marxists. It was a world where the kind of lack of accountability, the entrenched oligarchy and geographical class divide that we're dealing with now was spiraling out of control. In the old United States, the rich and powerful got the technology to grow loyal subjects in tubes, to make drone intelligences smarter in some ways than a human could ever be, to-- well, to do what Angessa Martei's done, only with no SolGov to stop her. Meanwhile, the 'little people' in the Middle East, Southeast Asia, and other 'forgotten' parts of the world were left behind, hopelessly. I don't mean to downplay the importance of the Gray Tide, but if you look at historical accounts from that era, the thing that really united the Front was the knowledge that, if they didn't act immediately, they'd be seen as 'externalities' by immortal superintelligent businesspeople and politicians. The take-away from the Gray Tide should never have been that 'nanotechnology is dangerous'-- it should have been 'nobody should be able to destroy an entire city without facing consequences.'\ -

                    \ - That was more of a history lesson than I had meant, but it's important to look at these sorts of things in context. I know transtech and the Five Points have been used as an excuse for pejudice against Skrell, Mercurials, positronics, the FTU, communism, the disabled, and most recently Prometheans. But all the Five Points are supposed to mean - what they would say if the people who had written them were alive today, is that everyone deserves an equal playing field. When the ruling class is smarter, stronger, and longer lived than the classes they rule over-- well, I could wax poetic again, or I could just point you towards the Hegemony and their 'clients'. The Hegemony is bad enough. Let's not give Angessa Martei a chance to outdo them." - -/datum/lore/codex/page/article32 - name = "01/25/63 - Moravec Nephew Announces Vir Governor Candidacy" - data = "The Sol Economic Organization has announced that Calvert Moravec, nephew of NanoTrasen CEO Albary Moravec will be running under their ticket in the upcoming Vir gubernatorial elections. Calvert has stated that he will run on a pro-business platform, and has chosen Vir to do so due to the 'Unique beauty and economic prospects of an interstellar crossroads such as Vir'.\ -

                    \ - Despite being a lifelong resident of Alpha Centauri, Moravec was recently approved for Vir citizenship, making him eligible for local candidacy and has reportedly moved into a luxurious New Reykjavik penthouse. Perhaps best known for his soaring stock market investment success over the previous few years, Calvert's first foray into politics is not wholly unexpected as the Moravec family has long leveraged their wealth in international affairs, though successful election would mark their first sitting member of the Colonial Assembly. Fellow SEO candidate Councillor Hal Wekstrom has expressed his full support for Moravec.\ -

                    \ - Three candidates will be elected as representatives to the Colonial Assembly later this year, with the most popular also attaining the position of system governor." - -/datum/lore/codex/page/article33 - name = "01/27/63 - Vani Jee Orbital Tour Cut Short" - data = "Icarus Front Representative Candidate Vani Jee has delayed the remainder of her campaign tour of orbital colonies and outposts around the Vir system after an alleged altercation with NanoTrasen security.\ - Candidate Jee had been visiting the NLS Southern Cross, a NanoTrasen station in Sif orbit to receive a corporate tour and meet with voters, when her concluding question and answer session was interrupted by hecklers, leading to the event being cut short. Jee alleges that footage of the event was seized by NanoTrasen corporate security and has accused the trans-stellar corporation of the intentional intimidation of Icarus Front and Shadow Coalition candidates in what she describes as 'a clear display of corruption in favour of company-favourite Calvert Moravec', though she has praised the individual employees of the Cross for their hospitality and thought-provoking questions.\ -

                    \ - Vani Jee is running on a platform of free access to education, Sivian self-determination, and isolationist foreign policy. She has refused to make any strong statements regarding hot-button issues such as the Five Points.\ -

                    \ - She intends to resume her scheduled tour after a three day break." - -/datum/lore/codex/page/article34 - name = "02/05/63 - Angessa Martei to Take Control of Eponymous Colony" - data = "Coming out of retirement and displacing the nameless Exalt of the Starlit Path, religious demagogue Angessa Martei has returned to the throne of the colony that bears her name. \ -

                    \ - 'I had retired because of senescence brought on by my old age, high-stress lifestyle, and multiple resurrective clonings. As you may know, I recently had a procedure that renders these difficulties obsolete. Hereafter I will continue to control the automated facilities of the Pearl and claim responsibility for the collective action of my followers. Those who oppose my decision may oppose all they want. Feelings do not move mountains. I do. We shall seize the stars in our own hands. May you become who you wish to be, and grind all obstacles to dust, as I have done.'\ -

                    \ - Purportedly, this address was met with a standing ovation from the population of Angessa's Pearl. In a separate dispatch, Martei stated her intention to tour SolGov as a foreign dignitary protected by the Respect for Diplomats Act." - -/datum/lore/codex/page/article35 - name = "02/07/63 - Vir Gubernatorial Candidate Barred from Breakfast TV" - data = "Infamously hot-headed Shadow Coalition candidate Phaedrus has reportedly been blacklisted from future appearances on morning television by several major networks. The ban comes after an advertised chat segment between Phaedrus and hosts of the West Sif Wakeup breakfast programme had to be pulled from broadcast after the candidate 'Flew into a expletive-laden mercurial rant' at the expense of rival candidate Mehmet Sao of the Icarus Front.\ -

                    \ - Recordings of the outburst quickly made their way onto social media, sparking outrage from opponents and network executives alike, prompting Occulum Media to issue a rare blacklist from major media outlets, restricting Phaedrus to 'appearances on alternative news sources' owned by the company.\ -

                    \ - Phaedrus, a long-time Vir Mercurial Progress Party member running for a major party for the first time, is said to have taken issue with candidate Sao's 'Blatant anti-synthetic' policies, though he did not use the word 'policies'." - -/datum/lore/codex/page/article36 - name = "02/09/63 - SEO Candidate Embarks on Wilderness Tour" - data = "In an effort to stir up support for his promotion of natural resource extraction industries, Sol Economic Organization candidate Mason Keldow has embarked on an unorthodox tour of resource-rich sites across central Sif. Keldow has described the tour as an 'Old fashioned expedition', invoking images of hardy prospectors of centuries past, and intends to make the journey entirely by ground with only a small party of 'Adventurous outdoorspeople' to support his trek.\ -

                    \ - Critics of the plan have pointed out that the earliest surveys of Sif were largely performed by aerial drones, and the idea of ground-based survey teams is 'Frankly anachronistic'. Rival Shadow Coalition candidate Selma Jorg - a staunch planetary environmentalist - has described the tour as 'Irresponsible and insane'.\ -

                    \ - The candidate intends to visit both unexploited sites and current corporate extraction facilities in order to 'Better understand the folks helping dig out Sif's hidden wealth' over the coming two weeks." - -/datum/lore/codex/page/article37 - name = "02/09/63 - Zaddat Colony 'Bright' To Enter Vir" - data = "After several months of talks with Nanotrasen and other corporations in the system, the Colony Bright is to begin orbiting Sif and hardsuited Zaddat are to enter the Virite workforce. Executives in Nanotrasen Vir cite the reduction of their drone-automated and positronic workforce as a result of the Gray Hour as cause for them to reverse their previous decision against allowing the migrants into the system. Icarus officials within VGA are concerned that, if other Colonies are to follow the Bright, the native industry of Sif may be disrupted or suborned by Zaddat and Hegemony interests, and have made it clear that the Bright's presence in the system is highly conditional." - -/datum/lore/codex/category/article38 - name = "02/11/63 - Mason Keldow in Ullran Expanse Close Call" - data = "Sol Economic Organization candidate Mason Keldow was rushed to nearby corporate medical facilities after a death-defying encounter with local wildlife in the Ullran Expanse this morning. The candidate had been planning to visit the nearby NanoTrasen mining facilities as part of his much publicized 'Wilderness Tour' when he and a local guide were set upon by the notoriously savage Sivian Savik. The animal was killed in the encounter, but not before Mr. Keldow suffered life-threatening injuries and had to be recovered by crew from the NLS Southern Cross, the closest facility on hand.\ -

                    \ - Following emergency surgery, Keldow was happy to provide news sources with a 'Good-natured' interview, in which he highlighted the dangers faced by rural workers on Sif and his plans to tackle them, as well as slamming rival Shadow Coalition candidate Selma Jorg for lacking tangible plans for the future.\ -

                    \ - Candidate Keldow is reported to have made a miraculous recovery, and is 'in good spirits'. Aides state that he is unlikely to suffer any long term effects from the injuries, in part thanks for the skilful work of NanoTrasen's Dr. Fuerte.\ -

                    \ - A full transcript of the interview follows:" - children = list( - /datum/lore/codex/page/keldowinterview - ) - -/datum/lore/codex/page/keldowinterview - name = "Mason Keldow Interview Transcript" - data = "Blip asks, 'Subject Keldow. Pleasantries first. How do you feel after your ordeal on the planet's surface?'\ -

                    \ - Mason Keldow says, 'Ah that? You know I'd love to downplay it and pretend that it was just a walk in the park... But NT's medical staff probably will tell you otherwise, so there's no reason to hide it; things went pretty far south.'\ -

                    \ - Mason Keldow says, 'But that's how it goes, working on the surface of Sif isn't pleasant at times.'\ -

                    \ - Blip asks, 'Candidate Keldow, you have placed yourself quite firmly in the boots of the local TSC's explorer contingents today. Do you feel you will be attempting to live the life of any other labour intensive roles in the near future?'\ -

                    \ - Mason Keldow says, 'Blip, Let me tell you I do try to get a taste for a lot of the work done by these people... But admittedly this isn't the job I do every day. These are hard working fellows who do there damnedest day in and day out... I could try spending a week just working the mines, But-'\ -

                    \ - Mason Keldow says, 'As I was saying.. Many of the folks, Miners, Explorers, The work in and around the Ullran Expanse. They work in the mountains, Out in the fields.. It's a dangerous place and frankly its not a place average people wanna go too.'\ -

                    \ - Mason Keldow says, 'They told me on the way here. 'Keldow you're an idiot''\ -

                    \ - Mason Keldow says, 'Hell, Even Basman over here was wondering why I didn't ask for a detail.'\ -

                    \ - Mason Keldow says, 'So it's not a safe route... But when's the last time any of the other candidates actually came down to these outer stretches and tried earning their sweat.'\ -

                    \ - Blip asks, 'You indeed seem to be attempting to gain a unique, and firm understanding of the daily struggles of the working populace. How do you intend to translate this newfound knowledge into policy and direction if you take the Governorship in Vir?'\ -

                    \ - Mason Keldow says, 'You see, getting a grasp of the struggle is only step one, I paint myself as an every-man but that doesn't mean core issues aren't the problem either; Vir's economics, The small pay that sometimes offered. There is a lot to be tapped into.'\ -

                    \ - Mason Keldow says, 'Let's take for instance the spiders I've been hearing about.'\ -

                    \ - Mason Keldow says, 'People working in orbit say 'Don't go to the surface, Spiders are down there.''\ -

                    \ - Mason Keldow says, 'And apparently there was a big ol' purple one sitting right by a camp we had set up. A giant mother who'd - if I hadn't met that lovely mass of fur and ice instead - would have probably said its 'hello' in the worst possible way.'\ -

                    \ - Mason Keldow says, 'They are a species that prevents anyone from actually working or otherwise making use of all that land. If I were in office, I'd make an effort to clear out the dangerous species that surround the outer regions of Sif - relocate them if possible - and use that territory for something productive.'\ -

                    \ - Mason Keldow says, 'New forms of Transit, new buildings, new jobs.'\ -

                    \ - Blip asks, 'Such implementation of infrastructure and security is not a cheap measure. How do you intend to find funds for such an endeavour?'\ -

                    \ - Mason Keldow says, 'Now obviously Vir is in a very interesting position, But thankfully it's in a wonderful position where business and partnerships are more than happy to come in and assist. The bottom line would make sure the average Taxpayer doesn't feel a dent, only the dividends.'\ -

                    \ - Mason Keldow says, 'If we break this down into economic plans, using new yet relatively safe tools being put out by Hepheastus, you could safely clear swaths of territory in the Expanse.'\ -

                    \ - Blip says, 'Thank you for your insight into your economic policies and plans. As a final question;'\ -

                    \ - Blip asks, 'A puff-piece question. Do you have anything positive to say about your rivals in the political race?'\ -

                    \ - Mason Keldow says, 'Ah yes, yes well... As for any candidate they need to show they're worth. Not simply as a politician but as a person who believes what they will do for for the betterment of Sif, And Vir as a whole.'\ -

                    \ - Mason Keldow says, 'Let's take a look Ms. Jorg.'\ -

                    \ - Mason Keldow says, 'She has LONG called me a Corporate sell-out, Saying I would poison the planet and other awful mudslinging.'\ -

                    \ - Mason Keldow says, 'She loves to claim she's here for the better of the misrepresented.'\ -

                    \ - Mason Keldow says, 'But when is the last time she's talked to a Tajaran and told them how they will help put food on the table, and money into their pockets.'\ -

                    \ - Mason Keldow says, 'When has she came and told the Unathi Exile, Your worth more than what the Hegemony is trying to convince you you're worth.'\ -

                    \ - Mason Keldow says, 'There's blood in the grass out there showing what I'm willing to do to make Sif and Vir a better more prosperous system. I wanna see what the other Candidates will do.'\ -

                    \ - Blip pings!\ -

                    \ - Blip says, 'Thank you for your time, Candidate Keldow. Unit looks forward to seeing where this race ends, and wishes Candidate the best of luck in his endeavours.'\ -

                    \ - Mason Keldow says, 'It's been a pleasure Blip.'" - -/datum/lore/codex/page/article39 - name = "02/12/63 - VirGov Launches Election Website" - data = "The Vir Governmental Authority has launched this year's election information exonet site, unusually several months after campaigning began. The government election agency states that the delay was caused by an usually long process of finalizing candidates this cycle, and did not want to confuse voters with incorrect or outdated information.\ -

                    \ - The newly updated site includes information on candidates and political parties, and is planned to include information on local voting rights at a future date. It can be found at:\ -

                    \ - your-choice-vir.virgov.xo.vr\ -

                    \ - (( https://your-choice-vir.weebly.com/ ))" - -/datum/lore/codex/page/article40 - name = "02/14/63 - Ultimatum Unmet: War With Almach!" - data = "The Solar Confederate Government has resumed a state of war against the secessionist Almach Association, after 4 months of tense ceasefire. The re-declaration comes after the Association failed to meet requirements set forth by the Colonial Assembly last November, which called for the cessation and destruction of all research that did not meet standards established by the Five Points of Human Sanctity.\ -

                    \ - The past few weeks have been marked by an increasing buildup of military forces on the Almach border as it became apparent that Almach had no intention of meeting Sol's demands. At 9am this morning, the deadline was met and initial reports from the frontline suggest relatively little action besides the destruction of pre-existing Almach scout drones that had been placed on the border several months prior. The exact plans of the fleet going forward have not been made public, but civilian traffic to and from the Saint Columbia system has been entirely suspended.\ -

                    \ - How this development will influence the coming Vir election remains to be seen, though SEO candidate Mason Keldow has reportedly ended his planetary tour 10 days earlier than planned due to the tense political situation." - -/datum/lore/codex/page/article41 - name = "02/22/63 - Militia Retreats: First Solar Victory" - data = "After a week of tense stand-offs and increasingly frequent skirmishes, the Association Militia has begun moving from their position around Saint Columbia further into the Almach Rim. The inciting incident for this shift seems to have been the first use of the MJOLNIR system, which instantly destroyed a large Almachi warship from half a system away. This demonstration was met with a standing ovation from much of Iserlohn, and Militia forces disengaged almost immediately. Admiral McMullen is unwilling to elaborate on pursuit or invasion plans at this moment, but 'hope(s) the MJOLNIR will continue to be a valuable asset for national defense.'" - -/datum/lore/codex/page/article42 - name = "03/04/63 - Savik Slams Local Chat Host" - data = "Television sweetheart Sally, host of Chat With Sally has come under harsh criticism from independent Vir gubernatorial candidate Yole Savik after his 'humiliating' appearance on the show yesterday morning alongside Sol Economic Organization candidate Calvert Moravec. Savik - who is campaigning for Vir independence from both the SCG and corporate interests - alleges that the show, which has run for 13 years on select networks, is 'little more than a propaganda piece for high powered executives.' and that his appearance had been part of a 'smear campaign' against non-SEO candidates.\ -

                    \ - NanoTrasen, who have openly sponsored Sally since her inception deny these accusations. Jan Bhatt of the NT marketing division stated 'Chat With Sally has always been intended as light morning entertainment, and Sally a personality we can all relate to. The fact that Mr. Savik was unable to have a sense of humour about the whole thing and took the show as an opportunity to bloviate about dry politics is not an indictment of Sally, nor the corporation but rather a simple misunderstanding of the purpose of the segment. We had hoped Mr. Savik's appearance would help dismiss any claims of political bias in our programming and hope to host more civil candidates in the future.'\ -

                    \ - Catch Chat With Sally weekly at 5am SST." - -/datum/lore/codex/page/article43 - name = "03/06/63 - Dark Triangle Goes Dark!" - data = "As of 0352 this morning, New Reykjavik time, SolGov officials confirmed that all communications coming from the so-called 'Dark Triangle' had ceased. The Dark Triangle is a disputed region of space home to the independent world Natuna, which has maintained a more or less neutral relationship with SolGov for a little more than a decade.\ -

                    \ - The announcement gives no cause for the communications blackout, though sources with inside knowledge claim that it was completely unforeseen. Some speculate that Natuna, who became an 'observer nation' of the Almach Association last November, is making a political statement, though experts in the telecommunications field are uncertain as to how such a complete blackout is possible.\ -

                    \ - Dr. Ina Lai from the Kara Interstellar Observatory states, 'we've never seen anything like this outside of the (Skrellian) Far Kingdoms, and frankly we're at a loss for who might be responsible,' adding that 'the tachyon signatures from the whole region are masked, even those from stellar phenomena or normal bluespace travel.' Independent explorers from the FTU have set out to the region in an attempt to re-establish communication with Natuna and smaller human settlements nearby." - -/datum/lore/codex/page/article44 - name = "03/08/63 - Dark Triangle Overrun By Hegemony" - data = "FTU explorers have re-established exonet communications with the ruling bodies of Natuna and the Dark Triangle. Unfortunately, they also discovered that Natuna's previously autonomous townships have been subsumed into the Moghes Hegemony. \ -

                    \ - Bluespace-lensed telescopes throughout SolGov, including the Vir-based Kara Interstellar Observatory, can once again pick up on tachyon signatures in the region. Traffic has been described as 'lower than usual' and no significant fleet assets are believed to be present in the region, though fixed-placement Hegemony installations now litter the Triangle's major star systems. Systems with significant Hegemony presence include Natuna and Ukupanipo, home to the primitive Uehshad species. Some have speculated that the presence of the Uehshad is the reason for the unexpected Hegemony takeover, though Icarus Front General Secretary Mackenzie West was quick to decry the move as 'an obvious imperial land-grab.'\ -

                    \ - Hegemony diplomats on Luna and elsewhere have been quick to justify their actions. 'The Dark Triangle has been home to various criminal elements for several centuries,' says Aksere Eko Azaris, a major Hegemony diplomat since the early post-war years. 'Neither the Skrell nor the Solar Confederacy have proven any willingness to bring stability to the region. Local governments such as Natuna have actively encouraged piracy, smuggling, and other acts of banditry, instead of making any moves to legitimize themselves. This instability proved detrimental to the health and wellbeing of all living within striking distance of the pirates of Ue-Orsi, and it was deemed unfortunate, but necessary, that we step in and provide the guiding hand by which this region might be brought back into the fold of civilization, as is our duty as sapients.'\ -

                    \ - In a statement closer to home,Commander Iheraer Saelho of the Zaddat Escort Fleet has assured VirGov that 'we only took action to protect the innocents of the Dark Triangle and of neighboring systems'.He asserts that Hegemony rule will ultimately benefit all races of people within the Triangle, and promises that, 'the people of Vir, of Oasis, of the Golden Crescent writ large, have nothing to fear from our clients the Zaddat, or from the Hegemony vessels assigned to their protection.'\ -

                    \ - Only time will tell if Saelho's promised peace and stability will manifest in truth. \ -

                    \ - This newfound militancy of the Hegemony is likely to become a major campaign issue in the upcoming Vir elections, alongside involvement in the war with the Almach Association and traditionally Virite issues of corporate authority, minority-friendly infrastructure, and taxation." - -/datum/lore/codex/page/article45 - name = "03/12/63 - Ue-Orsi Escapes Hegemony Triangle" - data = "The lawless 'Ue-Orsi' flotilla, home to hundreds of thousands of outcast Skrellian pirates, has departed from the Hegemony-controlled Dark Triangle after what appears to be a brief battle with several Unathi warships. The action damaged several important Orsian ships, including their massive and venerable solar array 'Suqot-Thoo'm', a development which is likely to increase the pirates' aggression in the coming months as they search for additional power sources. It is unclear exactly where the flotilla has fled, though best guesses indicate that they are presently in Skrell space, likely near the lightly-patrolled Xe'Teq system. The Moghes Hegemony is in negotiations with several Skrellian states to arrange for military action against their escaped subjects, but little headway has been made thus far.\ -

                    \ - This revelation has added more fuel to already heated Assembly arguments about SolGov response to the Unathi takeover. 'This is a prelude to invasion, nothing more and nothing less,' says New Seoul Representative Collin So-Yung, a noted Iconoclast. 'We must make it absolutely clear to the Hegemony that this is a threat we will not bow to, even in our present state of internal weakness. I suggest we pursue a fair peace with the Association, one where we can keep them as allies against this sort of encroachment instead of shattering our fleets during such a pivotal moment.'\ -

                    \ - Others took a more nuanced approach, including VGA Governor Bjorn Arielsson. 'What we have here is our punishment for how badly we've treated the people of the Triangle. I don't really see why we should have let the tired old racism of some Qerr-Katish oligarchs stop us from offering aid to their tired and huddled masses, such as it is. And because we have had a full century of ignoring their plight, they were defenceless to resist the Hegemony. I say we fling the doors open, let (the Orsians) settle some rock here, and show the unaligned powers of the galaxy that the Hegemony's way isn't the only way.'\ -

                    \ - Even more conciliatory was Speaker ISA-5, who Arielsson blames for mistreatment of the Ue-Katish. 'Our policy has always been that our defence budget cannot adequately defend the Dark Triangle from internal piracy, that Ue-Orsi is a criminal organization using their refugee status as a shield, and that we cannot lift the blockade of Natuna until they stop hosting these criminals and transition to a more sustainable economy. If Moghes has the power and the inclination to administer the Triangle, I see no reason why this state of affairs isn't better than the alternatives.'" - -/datum/lore/codex/page/article46 - name = "03/26/63 - Almach Routed from Saint Columbia" - data = "The Saint Columbia system has been declared free of Association forces following a renewed SCG offensive in the region. Admiral McMullen of the Saint Columbia garrison - who has reportedly reclaimed his post at the system's naval base despite the facility suffering moderate damage in this week's fighting - says that the last secessionist vessels were driven from the system just over 24 hours ago, and remaining pockets of resistance have been quick to lay down their arms. At least 20 enemy vessels - mostly converted civilian ships - have been confirmed disabled or destroyed in-system, thanks in no small part to the deployment of the state-of-the-art MJOLNIR weapons system.\ -

                    \ - This more aggressive approach to the Almach front comes on the tail of aggressive Hegemony deployments in the Dark Triangle, which SolGov has conceded was 'Immediately threatening, but after some deliberation, has brought some form of policing to a lawless region.'.\ -

                    \ - Despite assurances, this recent action would appear to many to be an effort to quash the Association threat and return fleet forces to the now extended Hegemony border, and some critics of the Almach War have called for a second ceasefire 'In order to focus on the real threat to mankind.'" - -/datum/lore/codex/page/article47 - name = "04/28/63 - Representative Hainirsdottir Reaffirms Pro-Vey Medical Manifesto" - data = "Incumbent Vir Representative Lusia Hainirsdottir has restated her dedication to advanced medical research at a public appearance at a Vey Medical facility in downtown New Reykjavik. Vey Medical has come under some criticism locally in the past year due to its 'accelerated' sapient trials, which Representative Hainirsdottir has strongly endorsed in her current term of office.\ -

                    \ - In her statement to staff at the New Reykjavik facility, Lusia promised that under her governorship, the company would not be reprimanded for the deadly Holburn's Disease outbreak in rural Sif this past June which claimed fifteen lives, and in which Vey-Med's involvement was only confirmed this week - as the outbreak 'directly lead' to the development of a new life-saving inoculation which has seen success galaxy-wide.\ -

                    \ - Hainirsdottir has long advocated for the promotion of scientific achievements that have taken place in, and undertaken by the people of Vir." - -/datum/lore/codex/page/article48 - name = "05/06/63 - Isak Spar Withdraws from Vir Election" - data = "Independent gubernatorial candidate Isak Spar has withdrawn his name from the running following a 'Public Relations disaster' aboard an orbital NanoTrasen logistics facility. According to witnesses, Spar acted belligerently towards staff members and engaged in vandalism and assault with a deadly weapon during his scheduled visit to the station, which the candidate had opted to undertake alone due to the temporary illness of his campaign manager.\ -

                    \ - In a statement just hours after the alleged incident, Isak announced that he would no longer be pursuing Vir Governorship, due to 'The unexpected stresses of a political career.' before plugging his upcoming album, C*** End Savage Turbo Death A** Destruction. The NanoTrasen corporation has decided not to press charges due to Mr. Spar 'Suffering the consequences on a far more significant level than a mere fine.' but will not be inviting Spar back for future visitation.\ -

                    \ - Spar's label, Skull Wreck Music has declined to comment at this time but has agreed to pay damages to the victims on behalf of the self-described 'post-pseudo electro-death superstar'." - -/datum/lore/codex/page/article49 - name = "05/15/63 - Solar Fleet Launches Offensive Against Almach" - data = "The first vessels of an SCG Fleet invasion force arrived in the Relan system this morning after a month-long intelligence operation to establish the Almach Association's most vulnerable positions, according to an announcement by Admiral McMullen just hours ago. Relan, which has long been fragmented between the neutral Republic of Taron and the once insurrectory Free Relan Federation - who now control the majority of the system and declared allegiance with Almach early in the crisis - is expected to fall to Confederate forces within 'a matter of weeks' due to its fractious political situation, and relative insignificance to Almach interests.\ -

                    \ - According to McMullen, the system's most populous habitat, the Carter Interstellar Spaceport is already under blockade and local resistance has thus far been minimal. The capture of Relan is expected to provide our forces with a major foothold in Almach territory and further advances are expected to be 'trivial', bypassing the Association's defensive positions in Angessa's Pearl.\ -

                    \ - The offensive comes weeks after sizable portions of the Fleet were publicly withdrawn from the frontline in order to reinforce the extended border with the Hegemony following their annexation of the Dark Triangle, and is a clear sign that - despite reduced numbers - Fleet command remains confident of Solar victory against the Mercurial rogue state." - -/datum/lore/codex/page/article50 - name = "05/19/63 - 'Drone Operated' Shelfican Ships Storm Sol Siege" - data = "Blockading Solar vessels in the Relan system came under fire today from automated craft originating from the Shelf fleet. The flotilla of drones, described by one survivor as a 'swarm', launched electromagnetic pulse and so-called 'hatch buster' precision missiles against three SCG Defense vessels, inflicting systems damage, and casualties 'in the hundreds' with at least eight service people already confirmed killed in action. The drones are reported to have withdrawn after 'only a few minutes of protracted fire from both sides.'\ -

                    \ - The Shelf telops fleet, which was spotted by Fleet forces but not identified as an immediate threat, entered the system early this morning and were understood to be acting as observers to the ongoing battle for the Relan system due to Shelf's official stance of non-aggression -- despite aligning itself with the Association. Shelf has reportedly been unable to be reached for comment on their actions, and their alleged neutrality in matters of war has been seriously called into question by many in the Colonial Assembly.\ -

                    \ - The SCG-D Krishna and SCG-D Mogwai of the SCG fleet, and the assisting Oasis logistical vessel, the OG-L Cloud Nine have been withdrawn to an unspecified location for immediate medical assistance and repairs." - -/datum/lore/codex/page/article51 - name = "05/20/63 - Fleet Withdraws - Sol On The Back Foot?" - data = "The Solar Colonial Assembly has confirmed that the Solar Fleet has withdrawn from the contested Relan system due to 'unexpected resistance' from Shelf tele-operated forces. This comes less than 24 hours after three Solar vessels were seriously damaged in an 'ambush' by a large number of Almach-aligned military drones. According to the Fleet, they were unprepared for any significant ship-to-ship combat in the system and will be consolidating their forces. 'This is not a defeat', according to Captain Silvain Astier of the SCG-R Hanoi, speaking unofficially to Occulum News sources 'This is merely a tactical withdrawal in order to reconvene and reassess our plans to restore order to the Almach Rim.'\ -

                    \ - A spokesperson for Shelf was quick to contact Fleet forces following the withdrawal with a formal apology for yesterday's incident, describing the previous day's attack as 'A terrible mistake.', blaming 'a miscommunication between our people in telops and the trigger-happy robots', though the veracity of their claims cannot be confirmed." - -/datum/lore/codex/page/article52 - name = "05/21/63 - NanoTrasen Station to Host Major Election Debate" - data = "As the Vir Gubernatorial elections approach, and with several high-profile debates lined up between the leading candidates in the polls, the NanoTrasen corporation is set to host its very own televised event live from one of its major logistical stations in Vir. The NLS Southern Cross, primarily a traffic control outpost managing shipping in Sif orbit, has been selected by the company to host the debate - funded in full by the corporation - due to a series of minor political scandals that have taken place on the platform, and the suitability of unused space onboard.\ -

                    \ - Early in the election cycle, NanoTrasen came under fire for its alleged 'manhandling' of Icarus Front candidate Vani Jee, and the confiscation of to-be-televised recordings taken by a party drone. While the company apologised for the incident shortly thereafter, the corporation hopes to mend ties with the potential future representatives by showing that they are capable of hosting civil discourse. More recently, the NLS Southern Cross played host to the 'breakdown' of disgraced former candidate Isak Spar - an episode which was not addressed in NanoTrasen's official statement on the planned debate event." - -/datum/lore/codex/page/article53 - name = "05/26/63 - SEO Candidate Advocates Murder On Live TV!" - data = "Sol Economic Organization candidate Freya Singh has been caught live on camera admitting that she would like to throw an innocent individual out of an airlock for a minor slight. During this afternoon's debate hosted aboard the NLS Southern Cross. Singh is quoted as having said that the event was 'the silliest concept for a debate I've encountered yet, and whoever came up with it should get a promotion, and then be fired out of an airlock.', a clear incitement of violence against Oculum Broadcast staff.\ -

                    \ - Magnus Dugal, 48 works for the Oculum Broadcast corporation and is credited with creating the concept for today's debate, the highest rated for this cycle so far. The father of three, who enjoys hoverboarding in his free time, says that he feels 'Threatened' by Singh's comments, and hopes that she will, 'at the bare minimum', issue an official apology to the company and himself.\ -

                    \ - Candidate Freya Singh, a career investment banker, spent much of today's debate advocating for reduced safety regulations and the apparent overturning of the Five Points, raising eyebrows across the system. Singh's office claims that her statements were 'a joke', but we do not feel that this is a laughing matter.\ -

                    \ - In related news, Shadow Coalition candidate Phaedrus remains under a profanity filter 'house arrest' for the remainder of the election." - -/datum/lore/codex/page/article54 - name = "06/28/63 - Vir Finalizes Dates for Election Voting" - data = "The Vir Governmental Authority has confirmed that voting for Vir's governorship and Colonial Assembly seats will take place on the 29th and 30th of June, with an additional voting period set for Wednesday the 3rd of July to allow for out-of-system and full-time weekend employees to cast their votes. No exit poll information will be released until the final votes have been cast, and final results are expected to be announced within another week.\ -

                    \ - According to a Oculum poll, Lusia Hainirsdottir is expected to comfortably take a seat, though the certainty of her governor position is not hard set. Candidates Sao, Singh and Jorg are trailing not far behind, but will all have to make good showings this weekend if they hope for electoral success. In an unexpected surge among minority species, the Shadow Coalition's Tajaran candidate Kurah Zarshir is leading the polls in certain outlying and orbital communities.\ -

                    \ - Not sure how to vote, if you can vote, or who to vote for? Check out the official election website at your-choice-vir.virgov.xo.vr" - -/datum/lore/codex/page/article55 - name = "06/29/63 - Morpheus Cyberkinetics To Split Assets" - data = "The Morpheus Cyberkenetics Corporation is to split into two distinct entities operating under a single board of trustees, in light of their Almach branch's apparent involvement in the ongoing war after last month's 'unintentional' corporate drone strikes. Citing 'Severe communications disruptions' between its operations and assets on either side of the cordon since it was put in place last year, the SolGov-side corporation is to become 'Morpheus Sol', retaining most assets and current corporate headquarters, and its Almach counterpart 'Morpheus Shelf', which is to be based out of the administration station MAS Sophia Jr., located in the El system.'\ -

                    \ - The principle victim of the Aetolian coup, Nanotrasen, has seen most of their considerable Almachi investment nationalized by the secessionist government, as has Xion and other major Almachi organizations. Most surviving corporate exclaves have been effectively written off by their parent company for the duration of the conflict, due to the severe difficulties effectively conducting trade across the militarized border. Before today, the sole exception was Morpheus, whose involvement in the secession prevented any seizing of their assets. It seems, however, that even the sardonic positronic corporation is not immune to the difficulties of doing business in the Almach Rim region.\ -

                    \ - Member of the Board Chock Full of Sardines introduced the proposal by saying, 'Our goal here is not being shot. Together with leading economic scientists, we've devised a scheme that will allow us to be shot for illegal smuggling almost ninety percent less often.' They defended the confusing and offensive choice of name in 'Sophia Jr.', seemingly intended as an insult to longstanding rival Sophia, by claiming, 'It's absolutely hilarious.'" - -/datum/lore/codex/page/article56 - name = "06/30/63 - Almach Leak Confirms 'Super-weapon' in Whythe" - data = "Solar Confederate Government Intelligence has this afternoon confirmed the presence of a so-called 'Super-weapon' in the distant Whythe system, after an apparent intelligence leak was posted to the exonet in the early hours of this morning. According to a spokesperson for the Solar Fleet, the public were not made aware of the super-weapon as the military 'have no reason to believe that the weapon poses any threat to civilian targets within SolGov space at this time, and there is no reason to cause panic with what amounts to the announcement of an Almachi propaganda tool intended to sow discord with bold threats of overwhelming power. This morning's leak achieves nothing but serving the Association's schemes. Keeping this so-called super-weapon - and I hesitate to use that term - a secret seems to have been last on their list of priorities.'\ -

                    \ - According to the intelligence documents released this morning and widely spread within minutes of upload, the 'super-weapon' is a colossal space-bound structure equipped with 'newly developed bluespace technology', though its exact purpose or capabilities have not been confirmed by either side.\ -

                    \ - Additionally, the Solar Fleet has announced that an unnamed individual within the intelligence service has been placed under arrest in connection with the leak." - -/datum/lore/codex/page/article57 - name = "07/04/63 - Exit Polls Suggest Shadow Coalition Win in Vir" - data = "According to the first exit poll data released after Vir Gubernatorial voting closed at midnight, local favourite the Shadow Coalition is expected to win at least two representative seats, with incumbent representative Lusia Hainirsdottir taking a comfortable lead.\ -

                    \ - Final results are not expected to be tallied until Saturday morning, but other frontrunners include the Icarus Front's Vani Jee and Mehmet Sao - running on drastically different platforms - alongside the Shadow Coalition's Selma Jorg. In an unexpected turn, sole Tajaran Candidate Kurah Zarshir of the Shadow Coalition has seen an immense surge in popularity among minority and more xenophilic voters. Could Vir be seeing its first Tajaran Representative? Experts say: 'Perhaps.'" - -/datum/lore/codex/page/article58 - name = "07/07/63 - Vir Election Results" - data = "The results of the 2563 Vir Gubernatorial Elections are as follows:\ -
                    \ - Governor of Vir: Lusia Hainirsdottir (Shadow Coalition)\ -
                    \ - Vir Colonial Assembly Representative: Vani Jee (Icarus Front)\ -
                    \ - Vir Colonial Assembly Representative: Selma Jorg (Shadow Coalition)\ -
                    \ - Other candidates ranked: Sao (4), Zarshir (5), Keldow (6), Singh (7), Moravec (8), Phaedrus (9), Lye (10), Savik (11), Square (12), Wekstrom (13)\ -

                    \ - Voter turnout: 30,928,287 (63%)\ -

                    \ - The greatest upset this election cycle has been the unexpected popularity of 'alien rights' candidate Kurah Zarshir, who was eliminated in favour of Mehmet Sao (Icarus Front) in the 8th round of vote transfers by a margin of just 30 votes, or 0.000096%, prompting a rigourous recount process to confirm the result. A difference at this stage could have resulted in a significantly different final line-up.\ -

                    \ - This year's winners showed clear advantages in the first-choice votes, each gaining at least 15% of the popular vote before any transfers were calculated, though Sao made significant gains in the final count, falling only a few percent short of the Jorg's 3rd place position. By far the least popular candidate this cycle was Hal Wekstrom of the Sol Economic Organization, who received just 0.8% of the first-choice vote and was immediately eliminated. Also of note were Phaedrus, Apogee Lye and Yole Savik voters, each of whom had high (30%+) voter exhaustion rates, opting not to provide alternative choices; sending the message 'My candidate or none at all.'\ -

                    \ - The elected are to be sworn in at a ceremony on Luna in two weeks time." - -/datum/lore/codex/page/article59 - name = "07/30/63 - Solar Fleet Data Breach" - data = "Last night, a number of files were spread on the Monsters From Beyond's exolife forums allegedly depicting the boarding and eventual scuttling of the SCG-TV Mariner's Cage during a voyage close to the Gavel system on the 12th of June, before the SCG had officially released any information regarding the event. The files contained undisclosed documents from the Solar Fleet investigation, some of which appear to contain audio and video recordings of the final moments of the crew before the vessel's bluespace drive was detonated. Due to the graphic violence depicted and their classified nature, we will not be sharing the files, however as a matter of public record we will explain the events recorded therein. The following description may be unsuitable for sensitive readers.\ -

                    \ - First, the navigation crew detects a drive signature on an apparent intercept course with their own, originating from across the SCG-Almachi border. It was not a large vessel, and is assumed to be some form of autonomous drone. The crew disregards it as a low level threat, instead continuing on their trajectory, leaving only the standard point defense armament locked on. This proved to be a lethal mistake, as the vessel appeared and near-instantly began accelerating toward the Mariner's Cage, before impacting the fore weapons array. The recording is cut, due to what was likely a power surge, however upon reconnection, reports indicate no damage related to any known warhead was apparent, aside from the initial impactor. The crew mistakenly assumes it to be a failed suicide drone strike, and dispatches minimal security personnel, and a large complement of response engineers.\ -

                    \ - Approximately thirty minutes after the response teams are dispatched to the impact zone, the teams begin losing contact, with those first arriving being the first to disappear. When the security responders intercept the path of communications blackouts, they are met with the blades of multiple Aetolian shock troopers. Two appear to be made from a 'living steel', with each limb taking the form of 'jagged cleavers' as one radio recording states, and three more of 'indeterminable classification'. The ship entered a red alert state, and moments later, the small contingent of marines aboard the supply vessel were dispatched to deal with the threat. All five members of the enemy boarding party were able to be rendered inert through sustained fire, though not without Sol casualties.\ -

                    \ - According to the next recordings, approximately three hours after the incident, the vessel received orders to interrogate the boarding 'Aetotheans'. The two noted to appear as the officers of the squad were rejuvenated within sealed interrogation chambers reinforced with supplies on hand, apparently capable of stopping sustained fire from multiple energy weapons. The first individual was a 'sapphire' according to information from NanoTrasen correspondants. It refused to speak in Galactic Common, and instead utilized an unknown frequency of biological transmission, and internal charge shifts. The individual was moved to a more permanent cell within the vessel's brig for transport, and the second was rejuvenated. Only the first half of the interrogation, which lasted approximately two and a half minutes, compared to four hours for the first, was recovered. The individual is rejuvenated, and is engaged in discussion with the interrogating officer when it suddenly stands, emits what is described as a 'wail', and detonates, destroying the transmitting camera, and presumably killing the officers involved in direct interrogation.\ -

                    \ - Final recordings originate from the ship's onboard A.I. housing, which was involved in continual discussions with presumably the 'sapphire', as it enacted the vessel's scuttling. It is unknown whether or not the individual was somehow capable of restoring the other individuals that fell in combat in order to free itself, or if it was able to incapacitate the transporting officers, and command crew of the vessel alone.\ -

                    \ - The Solar Fleet has expressed 'regret' that the files were leaked in their complete form, and have assured the public that an official report was due for release in the coming weeks. Concerns of 'Aetothean' attacks on civilian targets have been dismissed as 'improbable', but have affirmed that 'the threat is being taken very seriously'." - -/datum/lore/codex/page/article60 - name = "08/03/63 - Hainirsdottir Sworn In As Governor of Vir" - data = "Following a short transitionary period for the previous administration, this year's election victors have been sworn in at an official ceremony at the Colonial Assembly Hall on Luna. During her welcoming address, Governor Hainirsdottir reaffirmed her plans for the future of the system, promising a 'Bright future for Vir as a hub for medical science.', and plans for an incentivisation program for the removal of invasive extra-terrestrial species that have long plagued the region - in particular the aggressive spiders that have become synonymous with certain regions of the Sivian wilderness.\ -

                    \ - Additionally, the newly elected representatives announced expected, but none-the-less significant changes to the administrative staff of the system. Notable figures include two defeated election hopefuls: Kurah Zarshir has been selected as the Shadow Coalition's Culture Secretary for the system, while Mehmet Sao has been brought aboard by the Representative Vani Jee as the Vir Icarus Front's Internal Security Advisor. It is expected that the former candidates may use their positions to further certain goals from their own campaigns, but under the watchful eyes of their perhaps more moderate superiors." - -/datum/lore/codex/page/article61 - name = "08/04/63 - Former Independence Candidate Found Dead" - data = "It has been confirmed by a spokesperson for the Sivian Independence Front that a body found by hikers last week in the Ingolfskynn Mountains, approximately 200 miles northeast of New Reykjavik, belonged to party chair Yole Savik.\ -

                    \ - Savik, 68 - who had run for Vir Representative in the recent election - had not been seen since the 14th of July, shortly after the results were announced. Party officials claim that Mr. Savik frequently made 'off the grid' trips into the Sivian wilderness and his absence had not been treated as suspicious until investigators approached them to confirm the identity of the body. According to police, though Yole was publicly known as a 'seasoned frontiersman', Savik had succumbed to exposure at least two weeks prior to the grisly discovery. His death is not being treated as suspicious." - -/datum/lore/codex/page/article62 - name = "08/07/63 - Almach Pirate Threat Vanishes - Analysts Baffled" - data = "Skrellian Xe'qua pirates operating in the far reaches of the Almach Association since the onset of hostilities last year, have inexplicably gone dark. The pirates, who were under close SolGov surveillance to monitor their impact on Almachi shipping, have drastically dropped in activity and numbers over the last month according to an official report released by the Solar Fleet today. The Fleet is unable to account for the cease in activity, which has now reached levels even lower than their pre-war baseline, as there have been no reports of Almach military operations in the area, nor any signs of decisive battle on the Almach border with pirate space.\ -

                    \ - The drop in activity roughly coincides with the leaked information on an Almach 'Super-weapon' in Whythe, though military sources do not believe that the weapon has been deployed in any capacity at this time. According to Hasan Drust, an expert on Skrellian foreign policy, the 'only feasible explanation (is) major anti-piracy action undertaken by the Skrellian Far Kingdoms', who occupy the space beyond the Xe'qua pirates' known range. The reasoning behind this action now, against pirates who have historically only targeted human space is not entirely clear, though Drust suggests that it may simply be a coincidence as pirates would be a 'trivial issue' for Far Kingdom military might." - -/datum/lore/codex/page/article63 - name = "09/02/63 - Shock Almach Attack Routs Relan Front!" - data = "Following close to a month of reduced Almach activity, enemy Militia forces have today launched a staggering attack on Sol frontline forces in the region of the Relan system, disabling several SCG warships and forcing a major tactical retreat to Saint Columbia. The scale of this attack by Almach forces is unprecedented, but seems to be the result of the Association consolidating manpower previously dedicated to anti-piracy patrols on the far side of their territory. It is believed these vessels have become freed up due to the apparent but as of yet unconfirmed annihilation of Xe'qua criminal flotillas by Skrellian Far Kingdom police action.\ -

                    \ - The Solar fleet had been in position to blockade the Relan system in the hopes of forcing the Free Relan Federation to surrender and withdraw from the Association, but was unprepared for what has been described as an 'all-out attack' on their positions, which left the vessels SCG-D Liu Bei, SCG-D Wodehouse, SCG-TV Ceylon Hartal and SCG-TV Apoxpalon disabled and unable to retreat with the bulk of our forces, as well as inflicting severe damage to several other craft. According to initial reports, the strikes on many of the afflicted ships closely resembled scenes from the controversial 'Aetothean shock attacks' on the SCG-TV Mariner's Cage this June, which saw the ruthless deployment of gene-altered Promethean 'super-soldiers' by the Almach Association.\ -

                    \ - Fleet Admiral Ripon Latt, commanding officer of the assailed fleet, has confirmed that reinforcements are underway and the retreat 'shall not be a significant setback in the war effort', especially assuring citizens of the embattled Saint Columbia system and its neighbours that there is no cause for alarm and civilians have yet to be targeted.\ -

                    \ - The fates of the four missing ships have not been confirmed, and though the Fleet has not yet made an official statement, Sol casualties are cautiously estimated to be in the hundreds." - -/datum/lore/codex/page/article64 - name = "09/23/63 - Fleet Refuses Inquiry Into Relan Losses" - data = "The SCG Fleet has refused to heed widespread calls from critics to launch an investigation into the heavy losses sustained by our forces in a major Almach attack early this month, citing that an investigation at this time would 'undermine the ongoing efforts of our troops in battles to come'.\ -

                    \ - The attack, which took place on the 2nd of September and at current count resulted in the loss of a staggering 1281 Sol lives, quickly drew criticism from experts for 'the total unpreparedness' of the fleet despite their public claims that all vessels were 'battle ready and prepared for a coming offensive.'. The specifics of the fleets apparent failings have been the focus of much speculation in the intervening weeks, with the blame placed on everything from a critically inexperienced officer core, to ongoing redeployments to and from the recently expanded Hegemony border.\ -

                    \ - Admiral Latt has condemned critics, stating that 'the last thing our brave troops need right now is murmuring from people who don't know the first thing what they're talking about. Their actions in following orders to fall back to the border have been nothing but commendable, and all effort was made to minimise loss of life. The fleet is undergoing reorganization at this time, and is in a better position than ever.'" - -/datum/lore/codex/page/article65 - name = "09/27/63 - Almach Bypass Saint Columbia In Brazen Gavel Attack!" - data = "Almach Association fleet forces entered the Gavel system this afternoon, reportedly having evaded interdicting Sol forces from Saint Columbia in an apparent effort to skirt the range of the MJOLNIR weapon system in Saint Columbia and cut off that system from major shipping routes. Current reports from the system capital in New Xanadu are that the majority of outlying civilian stations have surrendered to invading forces with only minor incident, but that skirmishes with local defence forces - including Sol Fleet detachments - are ongoing, and it is too early to remark on the outcome of the battle. Official military reports are scarce at this time, but the Fleet in Saint Columbia is 'on the move and ready to repel the invaders'.\ -

                    \ - Accounts from the system's edge describe Almach forces 'firing indiscriminately' on anti-piracy emplacements including those mounted to the ILS Thurston, a Greyson Manufactories collection station with eight crew, killing all hands.\ -

                    \ - Open fighting in the Gavel system marks the furthest Almach encroachment on Sol territory to date. The system, which is a stone's throw from the Oasis and Vir systems is best known for the destruction of the moonlet 'Requiem' by a rogue nanoswarm in 2289, which was successfully neutralized by government forces, and boasts only a small population relative to its neighbors." - -/datum/lore/codex/page/article66 - name = "10/01/63 - 'Judgement Day' As Gavel Falls!" - data = "The government of New Xanadu has surrendered to Association invaders following a disastrous relief effort by the Solar Fleet, whose interdiction vessels are believed to have been captured by the invading force. The manoeuvre leaves the bulk of the Sol fleet isolated in the Saint Columbia system - though a breakout is expected - and has led to widespread outrage in the Colonial Assembly. Critics of the war have damned the Fleet for their 'inability to fight a civilian rabble, gene-modded or otherwise' and renewed calls for a peaceful arrangement between the Solar Confederate Government and Association.\ -

                    \ - ISA-5, current spokesperson for the Shadow Coalition has forwarded a motion today to resume discussions with Almachi heads of state, just hours after news of Gavel's surrender broke. The proposal which has yet to gain widespread traction, would call for a new ceasefire, and ISA-5 has stated they 'hope that a new agreement can be made to end the senseless loss of life over the particulars of a foreign government's right to autonomy.'.\ -

                    \ - Executive Sifat Unar of the Emergent Intelligence Oversight has voiced immediate concern over the motion, criticising the use of 'foreign government' in reference to Almach; 'Our Fleet has suffered a few defeats, but this conflict goes deeper than mere lasers and shells and to surrender to torturers, mind-hackers, and Machiavellian machines at this stage would be insanity. To allow a seccessionist state, particularly one so unabashedly guilty of crimes against humanity that go far beyond even our modern definitions of 'Human Sanctity', to exist unquestioned a stone's throw from some of our most precious member states, would be a failing not only of this government, but of humanity that would echo through history like a great shameful dirge for all to hear.'\ -

                    \ - A communications blackout has been instated on the Gavel system by the Almach Militia, though earlier reports indicate continued strikes on numerous civilian colonies who were unwilling, or unable to deactivate their automated defence systems prior to the invaders arrival." - -/datum/lore/codex/page/article67 - name = "10/08/63 - 'Magnetic Weapon' Designs Released Following Gavel Threat" - data = "Private security and ExoMartian law enforcement agencies are now receiving modernized man-portable magnetic weapon designs produced by Mars Military Industries thanks to increased budget from the S.C.G. The 'hallmarks' of these weapons, as one M.M. Industries spokesperson says, are their incredible ability to launch physical projectiles at velocities rivalling present portable laser technology in practical utility. Many of the designs utilize generalized, easy-to-manufacture compressed matter cartridges as their primary ammunition, meaning no specialized production facilities are required outside of standard shipyard or planetary lathe systems, 'to ensure a cutting edge in the battlefield, down to the last man'.\ -

                    \ - Some corporations, such as Hephaestus, NanoTrasen, and the PCRC, are already preparing to utilize these released designs in their own laboratories and stations, undoubtedly providing yet more materiel backing to the Almach front." - -/datum/lore/codex/page/article68 - name = "10/10/63 - Gavel Encircled - Liberation In Sight" - data = "Significant Fleet reinforcements from the Unathi border have 'trapped' the Almach fleet in the Gavel system and are poised for a decisive victory, according to latest reports from the front. Solar vessels from all sides of the war-torn system have closed in to ensure the invading force have no means of retreat. The relief force includes elements of the Hegemony-border fleets and the previously deployed flotilla stationed in Saint Columbia. Speaking at the Colonial Assembly this morning, Rewi Kerehoma of the Sol Economic Organization has stated that the reinforcements will 'beyond a doubt' prevent a repeat of 'embarrassing' errors made in the past month.\ -

                    \ - Efforts have been made to re-establish contact with the occupied system, which has been blocked from communication with the rest of the galaxy since the occupation began last week. According to scattered civilian signals from the system, the Association has adopted a 'salted earth' policy to the system following Solar military response, openly demolishing system infrastructure with little regard for its residents. A spokesperson for Grayson Manufactories, who maintain a significant presence in the Gavel system , has proposed that 'The Almachi had no intention of holding this system, this may have been nothing more than a show of force against corporate assets supporting the war effort.'\ -

                    \ - In related news, Admiral Ripon Latt has officially retired from his post following immense political pressure from the Assembly. Latt was until this week, commander of the Rim Expeditionary Force - currently in the Saint Columbia system - and has been the primary target for blame in the SCG's defeat in Relan, and failure to prevent the invasion of Gavel. The disgraced admiral will receive a full officer's pension, but no official honours befitting of his previous rank. Latt is to be replaced immediately by Admiral Silvain Barka, an experienced veteran of anti-piracy action in the Rarkajar Rift." - -/datum/lore/codex/page/article69 - name = "10/26/63 - 'Largest Engagement Since The Hegemony War' As Gavel Freed" - data = "The Almach Association invasion force in the Gavel system has been all but annihilated by a successful Solar counter-encirclement, at great cost to both sides. The combined Rim Expeditionary Force in Saint Columbia, along with the newly formed Gavel Relief Fleet - which had been massing in the Vir system over the past week - launched the successful attack this Tuesday evening, leaving no route of escape for Almachi invaders and resulting in 'pitched fighting' between the fleets that lasted several days. Solar forces are currently in the process of performing security sweeps of the system and its scattered habitats and it is expected to be several weeks before the system is declared safe to civilian traffic and for refugees to return home.\ -

                    \ - Even as exact causalities remain unconfirmed, the battle has made history as the single largest ship-to-ship engagement by tonnage involving the Sol military since the cessation of hostilities with the Unathi in 2520, involving over one hundred vessels of all sizes across both sides, as well as countless unmanned drones and light craft. Almachi forces, in numbers described as 'far from an insignificant portion of the total fleet' fought fiercely, and 'in manners more reminiscent of mercenary gangs than a single organized force, and with tactics varying from the conventional to the outright mystifying'. Admiral Silvain Barka has commended his own crew for applying lessons learnt from prior 'Aetothean' commando strikes in preventing similar incidents from occurring in the confusion of battle; in a candid interview this morning he stated 'Like any Promethean, (Aetotheans) hate the cold, and my crew are the coldest (expletive) around.' \ -

                    \ - The designations of twenty-four Sol Defense Vessels declared 'lost in action' have not yet been released, though next of kin of missing or deceased servicepeople have reportedly been notified." - -/datum/lore/codex/page/article70 - name = "11/11/63 - Gavel Salvation Reveals True Cost of War" - data = "Reports from liberating forces in the Gavel system have confirmed early accounts of 'inhuman' tactics employed by the Almachi invaders during their short occupation and defense of the region. Besides an apparent disregard for sapient life, especially any that gave an outward appearance of defending themselves, the Militia is believed to have employed troops and tactics 'the likes of which had only been imagined', in 'a manner that can only be described as experimental'.\ -

                    \ - Most harrowing of the accounts are those of alleged 'kill-switch clone armies' consisting of near-identical vatborn troops deployed to some of New Xanadu's largest surface colonies. According to local residents, the 'uncanny' troops arrived en-masse 'from the depths of the wastes' at the beginning of the invasion, despite wearing no obvious gear that would protect them from the extreme, unbreathable environment beyond the confines of controlled habitats. The clones are said to have targetted infrastructure including life support, with little regard for those who stood in their way. However, what has baffled Fleet analysts is the reaction upon the arrival of Sol surface troops; the clones did not fight back, but rather dropped dead 'all at once, as if a switch had been flipped'.\ -

                    \ - Teams from the SCG's top analytical and regulatory bodies including the EIO have been hard at work collecting examples of the unusual Almachi technology from throughout Gavel. Executive Sifat Unar has stated that 'It is vital that we ensure this transgressive technology no longer poses a threat to sapient life, either now or to future generations. A full cleanup operation is underway, and we are working to analyse what we have found and better understand the enemy's limits and the extent of their biological and technological... Practices.'\ -

                    \ - The current confirmed death toll, including civilian losses in Gavel has now reached 11,520 and is expected to rise. Debate continues whether to add the so-called 'Kill-switchers' to the tally." - -/datum/lore/codex/page/article71 - name = "11/21/63 - Tajaran Pearlshield Draws Line In The Sand" - data = "Khama Suketa enai-Lutiir, representative of the Tajaran Pearlshield Coalition, has just issued a formal statement:\ -

                    \ - 'It's no secret that we members of the Pearlshield Coalition have our differences and conflicts, both within and without. Much as some might call it the spice of life, it's with shame that I admit it has left us somewhat paralyzed over the last few months, even as a war has raged on a mere few jumps away from our borders. But in acknowledgment of our differences, something we have been able to unanimously agree upon is the sanctity of life and common decency, sanctity that Almach has continued to violate in the name of political conquest. Our relationship with SolGov - and indeed, any who would call us 'friend' - should be one of mutual cooperation and benefit, not of hard boundaries delineating 'us' and 'them'. There is only 'we', and we cannot stand idly by.\ -

                    \ - 'To this end, the Pearlshield has negotiated with local forces in the Silk system, and are taking over interim protection of the system, to free up Solar military forces so they can assist in the war effort. We have also begun construction of a large residential station to supplement the Silk station itself, alleviating its acknowledged overpopulation issues and providing additional logistical support for our defensive fleet. Finally, our cousins in Mesomori have generously loaned their new flagship, the PCMV Raniira's Grace, with all hands on deck for temporary joint assignment with Solar military forces. They are quite eager to provide a taste of Tajaran firepower and ingenuity.\ -

                    \ - 'We wish we could spare more at this time, but alas, we're still finding our feet among the stars, stepping carefully among the proverbial minefield that is our own share of cosmic threats. Know that these contributions represent a grand investment in their own right, and they are only the beginning should this war carry on.\ -

                    \ - 'We will have more to say as it comes up. May all our stars shine upon us.'" - -/datum/lore/codex/page/article72 - name = "11/23/63 - Surviving 'Kill-switcher' Assassinated During Vir Interview" - data = "An Almachi 'kill-switcher' clone soldier capturing during the liberation of the Gavel system was yesterday 'forced' to explosively self-terminate during a live TV broadcast with Virite news anchor David Huexqole aboard a SCG prisoner transport craft, allegedly by the statement of a code phrase, clearly audible on the recording. Following the attack, the apparent 'activator' is reported to have escaped to nearby NanoTrasen logistical station, the Southern Cross amidst the chaos where a minor altercation took place, resulting in the injury of one Positronic crew member and death of one Almachi accomplice, which initial reports suggest to have been a 'Vox mercenary'. The search continues for the Almachi agent, and local authorities remain confident that they will be apprehended.\ -

                    \ - David Huexqole, a popular local media personality, was the only other immediate victim of the attack, suffering moderate injuries and was rushed to the Southern Cross for emergency surgery where he is reported to have made a full recovery. Huexqole has expressed his gratitude to the 'skilled and charming staff, clearly shaken by the war but nonetheless capable for it.' but has expressed concerns regarding so-called sleeper agents within Sol space, 'Isa (341, the interviewed clone), seemed genuinely reformed. She spoke openly of regret, and struggling with what it meant to be created only to die. Before she blew herself up, I never would have thought her capable - it was like she changed in an instant.'\ -

                    \ - Initial blast investigation suggests the presence of a previously unidentified compound in the clone's bloodstream, which was detonated following 'activation' by the as of yet unidentified Association agent, believed to have been present in the room during the recording. Moments prior to termination, the clone's demeanour is visibly altered and the phrase 'Those of Sol must look to the eastern star, Phorcys.' was clearly stated. A spokesperson for the Saint Columbia Telemetry Station has stated that observation of Phorcys, an uninhabited star system located approximately between the Almach Association state of Vounna and Skrellian Far Kingdom space, has revealed 'no immediate anomalous behaviour or presence' but that analysis will continue in the coming weeks 'in order to verify the potential meaning of this statement.'" - -/datum/lore/codex/page/article73 - name = "11/25/63 - Phorcys Star Ignites!" - data = "The Phorcys star system, mentioned cryptically during Friday's dramatic televised Almachi assassination, has 'gone supernova' several thousand millennia ahead of schedule, sparking concerns of 'Almach Superweapon' involvement. The process, which ordinarily takes place over many months and is preceded by millions of years of obvious stellar evolution, began rapidly in the early hours of this morning and has already begun to consume the entire system at a rapid rate - mercifully consisting of only two uninhabitable dwarf planets.\ -

                    \ - According to government telemetric sources, who have been observing the system since Friday's message, the 'ignition' was preceded by 'abnormal bluespace readings', but it is impossible at this time to confirm whether the phenomena was a natural anomaly that Almachi sources were able to identify ahead of time, or if the long-rumoured 'Whythe Superweapon' was in fact involved. If direct Almach involvement were the case, the range of such an 'attack' would be unprecedented, with the afflicted star far from any populated area or territorial claims and 'no evidence of sapient passage to the system in several decades.'\ -

                    \ - General Secretary Mackenzie West, speaking on behalf of the Colonial Assembly has stated that 'The Solar Confederate Government is taking this matter very seriously. The mere prospect of a weapon of this calibre would indeed pose an existential threat to our society, and perhaps mankind. However, at this time we cannot jump to conclusions, this may in fact be a game of smoke and mirrors; an effort to use a previously unobserved phenomena as a weapon of propaganda, and we cannot allow this to cloud our judgement. We are in close contact with the Skrell in order to determine the nature of this sudden ignition. Know that the Fleet is on high alert, and fighting forth from the Saint Columbia system will continue unaffected.'" - -/datum/lore/codex/page/article74 - name = "11/27/63 - Whythe Superweapon Confirmed In Phorcys Blast" - data = "New government footage from the moment of ignition in the Phorcys system has confirmed the 'brief' presence of an artificial 'bluespace gate' within the star just prior to its rapid expansion and structural collapse. This verifies claims of responsibility made by the Almach Association late yesterday, and casts no doubt on the threat posed by the so-called Whythe Superweapon. The Colonial Assembly has been in continuous session since the initial incident, and one staffer has described the atmosphere as one of 'unhinged panic'.\ -

                    \ - The prevailing fear amongst the government, with the assent of top physicists in the private sector is that such a weapon could be used against Solar targets, and that it is no longer a question of 'how', but 'when', and indeed 'where'. The only unknown factor is the superweapon's capability for repeat use, and 'rate of fire' which is yet to be demonstrated but widely considered to be 'not a risk worth taking under any circumstances'.\ -

                    \ - Latest reports from the Assembly have are that a 'scramble' is taking place to resolve the 'Almach Situation', and that the ongoing offensive out of Saint Columbia has been redirected towards Relan and Whythe. According to Skrellian correspondent Hasan Drust, official aid has been requested as of this afternoon." - -/datum/lore/codex/page/article75 - name = "12/01/63 - Skrell Defer Aid Citing 'Deliberations'" - data = "Official Solar contacts within the Skrellian government have reportedly delayed tangible aid in the rapidly escalating Almach Crisis, citing 'Internal Deliberations', agreeing only to continue to share telemetric data on the movement of Association forces. The Skrell, who were instrumental in the Solar victory against the Unathi 40 years ago have long been considered taciturn in their internal affairs, but have previously been open in their support of Sol action when it comes to national defence, making their indecisive response greatly unexpected to some.\ -

                    \ - Speaker ISA-5, who has been increasingly critical of the war in the past several days, has dismissed concerns that the Skrell are 'siding with the Association', citing the long term close relationship between their governments, 'The Skrell are not war hawks, and they've never shown outward ill-will against other sapients. We can't let a bureaucratic delay allow public opinion to turn against an entire species of people who have always had our backs. If our allies need time for deliberations, we should allow them and not expect mere muscle and military aid when words could have been our solution from the beginning. I've no doubt that they will not allow the situation to become so dire that the Association are able to cause us any serious harm as they have threatened. I'm convinced that diplomacy will prevail, and that if the Skrell are in fact negotiating with the Association, then it is with all of our best interests in mind.'" - -/datum/lore/codex/page/article76 - name = "12/20/63 - Jee Relaunches Libraries Across Vir" - data = "Vir's Colonial Assembly representative and long time education reformist Vani Jee has launched her long planned 'modernization' of exonet-linked library system throughout the Vir system. During her campaign early this year she brought to light the 'staggering unsuitability' of library networks in Vir, particularly those being provided for corporate employees and residents, with particular pressure placed on the NanoTrasen Corporation whose literary collection was described as 'basically smut and whatever an old drone had scraped from the bottom of the exonet barrel.' Following her subsequent election, she reaffirmed her desire for education reform, making 'post-education' providers her first target.\ -

                    \ - The new system, which she successfully acquired government funding for this October, is due to be launched this week and includes a 'personally curated' collection in addition to titles recommended by affected institutions, and a particular focus on local authors." - -/datum/lore/codex/page/article77 - name = "01/23/63 - 'Extreme' Environmental Alert Following Ullran Expanse Chemical Leak" - data = "Residents and visitors to Sif's remote Ullran Expanse have been advised to exercise extreme caution when travelling and to avoid consuming local water sources following a 'catastrophic' chemical spill in the mountains. The exact cause and origin of the spill has yet to be confirmed, though it is currently believed to have originated from an improperly disposed of chemical tank. Local rivers and their outflow have been tested with 'extremely lethal' levels of acutely corrosive material.\ -

                    \ - Governor Hainirsdottir has issued a statement assuring Sif residents that the cleanup process is already underway, and the contamination which return to safe exposure levels for most sapients within 'Just a few days', but that the risk to local ecosystems in much higher and 'may take decades to recover.' and locals are advised to use only pre-packaged or thoroughly filtered water for the next 'six months, at least.'\ -

                    \ - A spokesperson for the Sif Environmental Agency has described this leak as 'At least regionally, perhaps the worst environmental disaster since the introduction of the invasive spider species.' and has expressed concern about similar accidents occurring in more populated areas if stricter regulations are not put in place.\ -

                    \ - The Ullran Expanse Chemical Relief Fund has been set up and is collection donations at save-ullran.xo.vr/donate" - -/datum/lore/codex/page/article78 - name = "01/27/64 - NanoTrasen Implicated In Devastating Ullran Chemical Spill" - data = "Files recovered from the contamination site by clean-up crews and submitted to the Sif Environmental Agency suggest that the hazardous materials were discarded in the region by the NanoTrasen Corporation. Though the company is not currently accused of malicious intent, ownership has been proven of several cargo containers filled with Growth Inhibitor 78-1, a compound used in the manufacture of Vatborn humans which is known to severely inhibit cell regeneration in living creatures.\ -

                    \ - The documents, which have not been released in full by the SEA, were also leaked onto the exonet from several sources which NanoTrasen initially dismissed as fraudulent but have since admitted to their authenticity. Investigations are currently underway to determine those at fault within the company so that prosecution may begin. Third-party commentators have alleged that, 'The debris field and damaged containers suggests cargo jettisoned from a craft within the Sif atmosphere', though the circumstances are yet to be thoroughly examined.\ -

                    \ - Certain members of the civilian clean-up crews responsible for waste disposal have also accused NanoTrasen of failing to provide their employees with sufficient safety information and protective equipment, which led to several minor injuries and one volunteer was reportedly admitted to hospital for 'severe side-effects of exposure' but has since been discharged." - -/datum/lore/codex/page/article79 - name = "02/10/64 - Shelf Fleet Vanishes Without A Trace!" - data = "Government agencies are scrambling to explain the apparent 'total disappearance' of the Almach-aligned Shelf fleet. Initial reports from the fleet's last known location describe a phenomenon not dissimilar to that observed just prior to the ignition of the Phorcys star in November, but the SCG has assured the media that 'No activity has been detected from the Whythe superweapon that would indicate any relation to the ongoing Shelf situation.'\ -

                    \ - Independent telemetric data from several sources has confirmed the presence of a 'bluespace anomaly' in the moments preceding the fleet. Currently efforts are focused on determining whether the fleet has been lost with all hands or if survivors may yet be found, but no trace of the fleet or of telltale signs of large fleet movements anywhere within or beyond Almachi space have yet been detected.\ -

                    \ - This afternoon, Speaker Mackenzie West of the Icarus Front addressed Sol, and indicated that communication was underway 'across faction lines' with the Association to determine the cause and implications of Shelf's apparent loss. The Almach Assocation has claimed not to be responsible for the 'possible attack' and both sides has expressed concern for the 'astronomical loss of life this may represent.'\ -

                    \ - Shelf, a largely Positronic colony fleet, consists of over 1700 vessels including the 'One Leaky Bitch', current headquarters of Morpheus Shelf, Morpheus' non-Solar 'spin-out' corporation established last June. The fleet has continuously denied direct affiliation with the Almach Assocation, but was involved in a major drone attack on Solar vessels just nine months ago, which was claimed to be 'in error'." - -/datum/lore/codex/page/article80 - name = "02/12/64 - Spectralist Wardens to hold Vigils for Lost Fleet" - data = "In the wake of Shelf's sudden disappearence, Wardens across SolGov space have collectively agreed to hold services and vigils throughout dozens of systems for those who are doubtless worried for their loved ones aboard the missing ships. Spectralism, a synthetic-centric religion, finds it's roots within 'Haven', a small vessel and community that travels alongside the wider Shelf fleet, though considers itself a distinct entity. Its ministers, known as Wardens, tend to its adherants wherever they may be found.\ -

                    \ - Despite Haven's well-known and somewhat-controversial independence of identity from Shelf, all indications point to the Fleet's disappearance having taken the attached ship with it. All twelve Spectralist Elders are known to have been onboard Haven at the time of its disappearance, as well as numerous other significant Spectralists and other spiritual leaders thought to have been on Shelf. The potential loss of the entire upper organizational body could be devastating to the religion, who have long been instrumental in the synthetic rights movement.\ -

                    \ - Ceramica, a Warden operating out of Nyx, reached out for comment with the following: 'We are as worried as everyone else who calls Shelf their home, or who has lost contact with friends or family. You need not believe in the First Spark to have a place at your local vigil; We welcome everyone who may be hurting. This is our way, this has always been our way. We remind everyone to stay mindful, and to reach out to those you see struggling. In times like this, we cannot leave each other behind.'" - -/datum/lore/codex/page/article81 - name = "02/14/64 - Shelf Safe After 'Impossible' Jump!" - data = "The missing Shelf fleet has reappeared hundreds of light years beyond the Eutopia system just days after its sudden disappearance on Monday. Unencrypted bluespace transmissions from the fleet's new location - several months travel from its last known position in Almachi space - assured all who would listen that 'The fleet (had) arrived safe and sound.' and apologized for 'Any alarm (the fleet) may have caused'. Though no official report has been released nor information from Shelf itself, initial counts indicate that a number of Shelfican vessels may not have survived the 'impossible journey' intact.\ -

                    \ - Current analysis of the bluespace anomaly detected prior to Shelf's unexpected departure indicate that the same technology as employed by the Whythe Superweapon may have been used by Shelf to create 'a bluespace portal thus far inconceivable by modern science'. The revelation has sparked concerns that 'extreme mercurial elements' within Shelf may have been responsible for the hardware behind the Assocation's 'system eating' weapon.\ -

                    \ - Sifat Unar of the EIO has expressed particular concern that 'Such improbable technology relying on concepts deemed so staggeringly arcane that our very understanding of the laws of the universe had written them off as impossible - and to be applied in such callous ways without regard for life or perhaps even the fabric of reality, could only have been developed by machine minds that could threaten our very being.' Shelf has dismissed these claims as 'Scaremongering' and 'Just jealous that somebody else thought of it first', though they would not confirm nor deny their involvement in the development of the new bluespace portals." - -/datum/lore/codex/page/article82 - name = "04/11/64 - Whythe Breached - An End In Sight?" - data = "According to latest reports from the Almach front, Sol vessels have established a foothold in the debris fields of the desolate Whythe system, home to the enemy's controversial 'Superweapon'. Admiral Barka has confirmed that bombardment of the weapon has begun, but that 'shields are holding at present', owing to the vast construction's immense power generation capabilities, but remains confident that the 'siege' will come to an end within a few months, and that the Almachi now hold only 'minimal retaliatory capacity'.\ -

                    \ - Fleet sources have been quick to address a flurry of activity on the exonet which proposed that the superweapon might be deployed against Sol's core systems in a last-ditch effort to inflict damage on their former government. According to a statement posted just hours after the initial invasion press release, 'the Fleet is aware of all risk factors regarding the Whythe Superweapon, and following two long months of analysis are certain that constant, substantial pressure placed on the weapon's shield systems will render the offensive component of the weapon dormant until the hull can be breached.'\ -

                    \ - The general mood in the Colonial Assembly today is one of relief, as all signs point to a total collapse or surrender of Association forces in their home systems once their trump card has been captured or destroyed by SCG forces. Secretary West has already expressed congratulations to the troops, 'despite the lack of assistance from our allies, as the Shadow Coalition would have had us believe was necessary.'" - -/datum/lore/codex/page/article83 - name = "04/13/64 - Skrell Ultimatum Shocks Sol!" - data = "After three months of diplomatic iciness, the Skrellian Far Kingdoms have contacted both the SCG and Almach Association with one demand: Sign an armistice or prepare for war. Supported by an immense fleet movement through the recently quashed Xe'qua region, the Far Kingdoms have demanded an immediate end to hostilities, and 'incorporation of Almachi holdings as a Skrellian protectorate, under strict oversight and regulation of their research and activities.' By Skrell demands, the Fleet has two weeks to fully withdraw from the Almach region and any vessels on either side continuing to engage will be 'disabled, boarded, and have its crew arrested pending a formal peace agreement.'\ -

                    \ - A wave of outrage has swept the Colonial Assembly, with heated debate as to Sol's response defying all party lines. While Speaker ISA-5 has been widely criticized by political opponents for their 'overzealous trust in the Skrell', they have remained acquiescent to the Skrell's demands, stating that it may be the best way to avoid any further bloodshed and maintain good relationships with the Skrell. Conversely, a small group of hardliners from across the major parties headed by SEO Representative Colin Zula of Alpha Centauri, have formed a political coalition opposing any form of 'Surrender or appeasement in the face of foreign aggression', demanding Sol keep its forces in place and 'Finish off the Association before they can be allowed to wreak havoc unsupervised and uncontrolled.'\ -

                    \ - Surprising some, long-time supporter of the Almach War, MacKenzie West has established themselves as a figure of moderation in the Assembly, promising that the Icarus Front would pursue 'aggressive negotiations' with the Far Kingdoms in order to better understand their motivations and, if territory is to be ceded, 'ensure the Almachi are placed under a firm hand'. He notes that the Skrell have never adhered to Five Points policy, but that careful diplomacy has always ensured their 'less savoury tendencies' have never spilled over to Sol space." - -/datum/lore/codex/page/article84 - name = "04/16/64 - Assembly Shaken By Reshuffle" - data = "Following the shock announcement of the Skrellian demands on Monday, sixteen planetary representatives under the SEO's Colin Zula have announced the formation of a new political party named the 'Solar Sovereignty Party' under the banner of 'Independence from foreign demands'. The party consists of defectors from all three major parties; 9 Sol Economic Organization, 4 Icarus Front and 3 Shadow Coalition representatives have 'jumped ship' and will back Zula's demands to resume war with the Association, even if it means butting heads with the Skrell.\ -

                    \ - Rewi Kerehoma, chair of the SEO has expressed 'regret' that Mr. Zula and his supporters had chosen to splinter from the party, rather than work with 'More moderate, but like-minded' individuals across the SEO and wider Assembly. The SEO's official stance on the Skrellian demands are to demand close Solar oversight of any 'protectorate' to ensure that the region is 'policed to the highest standard, but that current Almachi citizens are afforded all the sapient rights they would be under the SCG.'\ -

                    \ - In the Shadow Coalition, a formal motion has been put forth by a small minority of representatives calling for the resignation of Speaker ISA-5, citing 'Total blindness to the political situation,' in the leadup to this week's events.\ -

                    \ - Meanwhile as Skrell vessels enter the Whythe system, the Solar Fleet has ceased bombardment of the Whythe Superweapon, handing off 'suppression' of the weapon to Skrell forces." - -/datum/lore/codex/page/article85 - name = "04/20/64 - Sol To Submit - Almach Subsumed Under Treaty of Whythe" - data = "Following 'intense' deliberations between the Far Kingdoms and representatives from the SCG, a decision has been reached to cede the secessionist Almach Association territory to the Skrell, and withdraw all forces from the region. The newly established Almach Protectorate will be subject to 'extremely stringent' oversight by Skrellian authorities, and international exchange of 'research and technologies' from the region will be banned 'in both directions', pending more a more exacting deal with the SCG. Sol is to be allowed 'regular inspections' of the territory on a schedule established by the Kingdoms.\ -

                    \ - The Solar envoy included the chairs of each of the major parties, senior ambassadors to major Skrell systems, and representatives from the Solar Fleet. The newly founded SSF were extended an invitation, but reportedly turned it down. A dejected looking MacKenzie West announced the terms of the treaty late this afternoon, stating that they had 'fought tooth and nail' for a fair deal for all parties involved, including civilians of all species now living under Skrellian occupation, and that 'those not directly involved in the corruption of humanity's sanctity should not be made to suffer for the actions of their superiors.'\ -

                    \ - Notably absent from deliberations were many key members of the Association's upper echelons, with 'lesser' diplomats taking the place of both Angessa Martei and Vounna's Naomi Harper. Almachi and Skrell sources were reluctant to explain these absenses, and it remains unclear as to whether they have been taken into Skrellian custody or remain at large.\ -

                    \ - Selma Jorg, Representative for Vir, has decried the treaty as a 'Sapientarian disaster in the making'. The former career diplomat has cited the 'general mistreatment of species deemed 'lesser'' as a recurring concern with the Skrell, and the complete occupation of majority human and positronic space, which unprecedented, could lead to 'conditions not much better than slavery' for those still living in the area. She has refrained from any direct accusations, pending the results of Sol's first permitted inspection." - -/datum/lore/codex/page/article85 - name = "04/22/64 - Skrell Impose New Regime in Relan" - data = "As agreed upon in the Treaty of Whythe, the Far Kingdoms have occupied the Relan system, putting an end to the Free Relan Federation. How the system will be organized is not entirely clear at this point. Despite the effective abolition of the Relanian government, Skrell presence in the system appears relatively light, and many of the scattered stations have no Skrell presence at all.\ -

                    \ - Former President Nia Fischer gave the following statement to a crowd gathered outside the Capitol Section of Carter: 'This is a dark time for all of us. I promise to you that, in my continued service to you, I will work with the Far Kingdoms to ensure that all of our people are treated well and our rights respected, and that we will arrive at a form of government that is acceptable to you.' The gathered crowd began to shout questions and accusations, and Fischer was quickly escorted back into the capitol by Skrellian guards without answering questions from the press or others. The crowd was quickly dispersed by Skrellian military police and Carter's own police force.\ -

                    \ - In the meantime, the governing of the system remains in the hands of the sparse occupation forces, aided by parts of the former Federation government.\ -

                    \ - In other news from the system, the Republic of Taron has negotiated a preliminary navigation and trade agreement with the Far Kingdoms, officially maintaining their neutrality despite the occupation of the majority of the system." - -/datum/lore/codex/page/article86 - name = "04/27/64 - Chaos in Relan" - data = "Simmering tensions in the Relan system have boiled over, with riots erupting on Carter, Abhayaranya, and New Busan. Since former President Fischer's brief address, small demonstrations against both the Skrell occupation and the collaborating elements of the former Federation government have taken place on many stations, but within the last day full-blown riots have broken out. While accurate information on the situation within the stations is rare, it is currently believed that the deaths of two protesters on Abhayaranya were the catalyst.\ -

                    \ - Damage to the three stations has been relatively light, with one major exception. A large fire broke out in the Capitol Section of Carter, killing at least 22, including former President Fischer, and wounding at least 74 more. Other casualties among rioters, police, and the populations of the stations are unknown at this point.\ -

                    \ - Other stations with significant permanent populations have been paralyzed by local inaction and the disloyalty of local police and security forces to the Far Kindgoms Skrell, and several with no Skrell presence have issued statements that they will not be accepting any military presence from the Far Kingdoms. It is unclear at this point if this represents the beginning of another major conflict within the system." - -/datum/lore/codex/page/article87 - name = "04/30/64 - Meralar Correspondent: Triumphant Return!" - data = "Celebrations have erupted throughout Tajaran space with the return of the PCMV Raniira's Grace, which has spent the last several months providing joint assistance with Solar military forces during the now-ceased hostilities with the Almach Association.\ -

                    \ - Khama Suketa enai-Lutiir, representative of the Tajaran Pearlshield Coalition, has provided the following statement:\ -

                    \ - 'It is great honour that we welcome the crew of the Raniira's Grace back to their homes at Mesomori. We have all seen the battle reports, and loathe as we are to celebrate bloodshed, sometimes it is a necessary evil in the pursuit of a greater peace, and the Grace pursued that peace with fervour and tenacity as befitting our kind, and exemplified what we can do when put to the test. She is but one ship, and yet one ship can make all the difference. Lives have been saved, and the crew has returned alive and well. This is merely the beginning of what we can accomplish in the cosmos.'\ -

                    \ - When asked for comment on the Treaty of Whythe, Suketa had this to say:\ -

                    \ - 'Indeed, the bittersweetness behind all of this. I will say, it is a... complicated and nuanced situation, as is so often the case with politics. We have our views on the matter, for sure, but now is not the time to formally engage with them. The Pearlshield is watching carefully, and when the dust settles and the terms of the treaty are exercised and accepted, we can take clearer view and action as possible and necessary. We still stand by the ideals we entered the war with, and we trust our allies to share them as ever. That will suffice for the moment.'" - -/datum/lore/codex/page/article88 - name = "05/13/64 - Agreement Signed at Ithaca Station, New Government In Place" - data = "In an effort to end the ongoing violence in the Relan system and regain the cooperation of 'insubordinate' stations, the Far Kingdoms Skrell have negotiated an agreement with community leaders and former Assemblypersons from a number of stations, including insubordinates, meeting at the largest of the insubordinates, Ithaca. Under these agreements, the Skrell will vacate most stations in the system, but will maintain a fleet base in Relan's Outer Belt for mutual defence, first at Carter and later at a dedicated station. Relan will have harsh restrictions placed on its military and will agree to formal diplomatic neutrality, but will be free to organize its own government under supervision and military occupation will end.\ -

                    \ - The mood on Ithaca has been tense as negotiations have gone on, but with the announcement of the results, crowds have packed the main thoroughfares and public spaces of the station in celebration. Francis Harp Yong, governor of Ithaca and a leading figure in the talks, addressed a crowd outside the Administration Section of Ithaca today. \ -

                    \ - 'The agreement we have signed with the Skrell today has given our people a new chance, free from the mistakes of the war and the baggage of the former Association. The war was not brought on us by our choice, nor the occupation we have recently faced. We want peace, and that is obvious even to those who were fighting against us weeks or days ago.\ -

                    \ - Make no mistake, that is what our agreement today symbolizes. A new era of peace for us, where we no longer have to worry about the threat of piracy or invasion. We can return to our homes, rebuild our stations, and forge a new future for ourselves and our children'\ -

                    \ - It was also announced that though the work of restoring order to the system is ongoing, they expect elections will be held for a new Assembly and President within the next few months, with the exact date announced once violence on the major stations has ceased and cooperation from the insubordinate stations is secured. Yong will head an interim government in the meantime." - -/datum/lore/codex/page/article89 - name = "08/15/64 - Almach Permits First Solar Inspection" - data = "Almost four months since its establishment as a Skrellian territory, the Almach Protectorate Government has extended its first formal invitation to Solar Confederate Government inspectors to ensure the fledgling state is complying with restrictions imposed by the Treaty of Whythe.\ -

                    \ - The composition of the official Solar Inspection Group has been a matter of much deliberation over the past several months, and owing to the ground to be covered now includes over 3,500 experts from a wide variety of fields. The bulk of the Group is comprised of Transgressive Technologies Commission agents, including the EIO, but also includes military officials, independent observers, and corporate representatives. The inclusion of the latter group spurred heated debate in the Colonial Assembly, but ultimately 'thought-leaders' from Ward-Takahashi, NanoTrasen, and Hephaestus Industries were admitted, while other interests will have to be satisfied to be represented by Sol Economic Organization liaisons.\ -

                    \ - The APG, currently based out of what is to be a neutral embassy on Carter in Relan pending the completion of the new government centre of Vigilance Station in Whythe, is not legally required to fully comply with Five Points regulation, though the Whythe terms ensure that any transgressive research is undertaken under the strictest guidelines. The Skrell have amiably agreed to ensure that any innovation by the protectorate is safe, controlled, and does not enter Solar territory. These terms are similar to those applied to such technologies developed by and for the Skrell themselves, whose expertise is considered unrivalled in the field, but has been widely criticised by certain human bioconservative groups.\ -

                    \ - The inspection is expected to take several weeks, and will begin tomorrow." - -/datum/lore/codex/page/article90 - name = "08/29/64 - Kaleidoscope Cosmetics Goes Trans-Stellar After Genix Merger" - data = "Personal care giant Kaleidoscope Cosmetics has been officially recognised by the Solar Galactic Exchange as a true Trans-stellar Corporation today, following a controversial merger with Genix Therapeutic Systems and expanding its corporate assets to over 20 key systems.\ -

                    \ - Genix, which will now be operating under the Kaleidoscope name while retaining some corporate autonomy, has come under some scrutiny from the Transgressive Technologies Commission in recent months following its aggressive acquisition of liquidated Almachi assets in the aftermath of the Association's dissolution in April. The company was cleared of suspected Five Points violations without fanfare just two weeks ago, and allowed to resume work on the development a variety of previously approved commercial genetic modification products.\ -

                    \ - Many cosmetic gene-modification products have been available for some time - primarily in the Almach Rim - but have remained targeted at a relatively niche market. These treatments, ranging from cosmetic anti-aging to 'fantasy' body features are now set to be marketed and available in 'hundreds' of Kaleidoscope clinics galaxy-wide in the coming months. The TTC has reported that they are 'Confident that no transgressive modification is being provided and that these modifications are strictly superficial'. They have officially wished Kaleidoscope's directors well in their ascent to the galactic stage." - -/datum/lore/codex/page/article91 - name = "09/21/64 - Almach Passes Inspection - Concerns Raised" - data = "The Solar Inspection Group has granted the Almach Protectorate a passing grade in the first official inspection of the territory under Skrellian rule, though ethical concerns have been raised by a number of independent observers involved in the process.\ -

                    \ - During the course of the month-long inspection, Skrell facilitators were cooperative in ensuring the SIG were given 'unlimited' access to all research and development facilities requested by Sol, as well as informing the group of numerous previously unidentified locations that had been flagged by the Almach Protectorate Government since assuming control of the region in April. Critics of protectorate have suggested that the 'official list' may not be as comprehensive as stated, but the SIG has stated that they are 'confident that no deception has been undertaken by the APG. We have observed no evidence of research being concealed from our teams, and to the best of our knowledge the only locations left undisturbed are those few still occupied by Association holdouts, beyond the control of either inspecting government.'\ -

                    \ - Also as a result of the inspection, concerns have been raised regarding the treatment of ex-Association citizens, particularly the often genetically modified residents of Angessa's Pearl. While Angessa Martei's location is still unknown, her legacy - a society described by some as a cult of personality with emphasis on the cult - poses a significant threat to Skrellian control of the Exalt's Light system, and the clash of 'strong ideologies' has allegedly resulted in mistreatment of detainees that, according to the official report 'would not be permitted in Solar jurisdictions'.\ -

                    \ - The Protectorate's passing grade opens the door for interstellar trade to resume between the two nations for the first time in several years. Manufacturing giant Ward-Takahashi has already released a public statement on its intent to deal with the APG, and several other trans-stellars are expected to follow suit." - -/datum/lore/codex/page/article92 - name = "09/24/64 - Kaleidoscope Announce Exclusive Almach Deal" - data = "Mere days after the announcement of the reopening of Almachi-Solar trade, the Kaleidoscope Cosmetics corporation has confirmed that they have secured 'exclusive rights' to genetic products produced by several major manufacturers in the Angessa's Pearl system formerly owned by The Exalt - the insular mercurial theocracy of Exalt's Light - and recently handed over to the Almach Protectorate Government.\ -

                    \ - The details of the company's trade agreement have not been made public, but have reportedly already been approved by both the APG and SCG. The promptness of the agreement suggests to some that negotitations had been underway well before the announcement of the Protectorate's passing grade. Though cooperation between trans-stellar interests and government entities is far from unusual, such dealings with foreign governments - such as those widely made as 'open secrets' with Eutopia - are considered distasteful by many within the Icarus Front.\ -

                    \ - The Angessan products Kaleidoscope intends to offer have also not been made public, but are all to be thoroughly screened by the Transgressive Technologies Commission before being made available, though 'failing' proposals may be approved for use exclusively within Protectorate territory." - -/datum/lore/codex/page/article93 - name = "10/04/64 - Yong Wins Relani Elections" - data = "Despite concerns about domestic unrest and potential Skrellian interference, elections in Relan have gone ahead, selecting a new President and System Assembly. Francis Harp Yong, leader of the interim government and previous governor of the city-station Ithaca, known for his leading role in negotiating the end of Skrellian occupation and calming riots and rebellions earlier this year, has won the Presidency in a landslide, receiving nearly seventy percent of the vote. His party, the Spacer Union, has secured a narrow majority in the Assembly. The New Federalists, led by former member of the Harper government Odoacer Mieville, are the largest opposition party.\ -

                    \ - Yong has promised to reinforce Relani neutrality and rebuild the pre-war social system, forming a coalition of labor, technoprogressives, and anti-war activists. His victory likely means an end to the unrest that has plagued Relan since the end of the war with Almach." - -/datum/lore/codex/page/article94 - name = "12/23/64 - Two Vessels Feared Lost In Isavau's Gamble" - data = "Two Solar salvage vessels have been declared missing this week while undertaking operations in the remote Isavau's Gamble system. Locals also report that several smaller craft from the Eutopia system have 'vanished' under similar circumstances. Authorities are treating the disappearances as potential pirate attacks.\ -

                    \ - The XIV Sri Chamarajendra with crew of 32, and IIV Reimarus with 9 are both reported to have lost contact with the system's spaceport while undertaking far-orbit salvage operations on decommissioned and abandoned facilities, including the ILS Harvest Moon which detonated with all hands earlier this year. The disappearances have spurred the system government to request greater anti-piracy support from the SCG, which has been much reduced since the Almach War and increased tensions on the Hegemony border.\ -

                    \ - While Proximal to the independent and often 'lawless' Eutopia system, as well as the Rarkajar Rift, infamous prowl of the human-tajaran Jaguar Gang, the Isavau's System has rarely been a direct target for such large-scale apparent hijackings, and no group has yet claimed credit or demanded ransom." - -/datum/lore/codex/page/article95 - name = "12/27/64 - NanoTrasen Accused In Satisfaction Poll Fixing" - data = "The results of an annual system-wide corporate employee satisfaction poll in Vir have been called into question after a leak of internal employee contracts by an anonymous whistle blower. According to verified documents provided to the Vir News Network, NanoTrasen employees on annual employment contracts up for renewal are instructed to fill out an appended form expressing 'Extremely High' satisfaction with their employment at the company, or the contract will be deemed 'incomplete' and thus invalid.\ -

                    \ - NanoTrasen have been winners of the Vir Happy Employee Award for the 7 years running as of this year, and as such the legitimacy of the award, which has been run in major systems by the Sol Economic Organization since 2503, has been called seriously into question. The SEO has expressed regrets regarding the alleged fixing, but has stated that participating corporations are within their right to encourage employees to vote in a particular way.\ -

                    \ - According to NanoTrasen, the so-called 'Satisfaction Clause' in their contract renewal process is entirely permissible from a legal standpoint, and the company has no plans to make any changes at this time. When approached for comment, one anonymous employee stated that they 'had never even heard of' the award." - -/datum/lore/codex/page/article96 - name = "01/03/65 - VirGov Seals Security Deal With Local Firm" - data = "Governor Lusia Hainirsdottir has announced sweeping changes to government law enforcement in Vir, lynchpinned by the signing of a five-year contract with home-grown independent security and arms firm, Hedberg-Hammarstrom. The private enterprise will be assuming 'key duties' in local law enforcement and system security, in a move that Hainirsdottir says 'will save millions in taxpayer money, and encourages local, Virite businesses that we trust them every step of the way'.\ -

                    \ - While many colonies and facilities in Vir rely wholly on in-house security forces and will continue to do so, Hedberg-Hammarstrom is set to take over government patrol duties throughout the system, as well as administrating a large portion of previously government-run local law enforcement agencies including policing and wilderness garrisons.\ -

                    \ - CEO Gunnar Hammarstrom has reassured current SifGuard members at all levels that they need not fear for their jobs, and that for many the changes will be as simple as a slight change in uniform and a different name on their paychecks, but that H-H agents will be assuming managerial roles in most locations." - -/datum/lore/codex/page/article97 - name = "01/05/65 - Top NanoTrasen Executive Injured In Sif Bombing" - data = "Calvert Moravec, Chief Operations Officer for NanoTrasen Vir remains in a critical condition following a suspected assassination attempt on Saturday. The former gubernatorial candidate had been making a speech at a small NanoTrasen facility known for a prior terrorist attack by Boiling Point forces two years ago, when a small explosive device detonated close to the executive's leg, rendering him unconcious.\ -

                    \ - NanoTrasen security have elected to investigate the bombing internally, refusing to cooperate with SifGuard authorities on the grounds of disagreements with newly established Hedberg-Hammarstrom management. Mr. Moravec, nephew of NanoTrasen CEO Albary Moravec had reportedly been speaking out against H-H's involvement in national policing when the blast occurred.\ -

                    \ - Commander Spradling of NanoTrasen Internal Security has issued a company-wide active search order for one member of the audience who remains unaccounted for named 'Lae Vu', who is believed to be a journalist or posing as such, accompanied by images from communicator footage taken at the event.\ -

                    \ - This amateur footage also shows one member of the audience shouting an anti-corporate slogan and throwing a small electronic device at Moravec before being escorted from the room, just moments prior to the explosion. Spradling has stated that this individual, who is believed to be a disgruntled veteran of the Sif Defense Force, remains one of several persons of interest, but that the thrown item does not appear to be connected directly with the explosive device.\ -

                    \ - Reports from witnesses suggest that Mr. Moravec made a temporary recovery thanks to the fast action of medical staff aboard the NLS Southern Cross, but that a few hours later he appeared to suffer from a seizure, after which he was placed into a medically induced coma that he is yet to recover from. Unverified images from the Cross appear to show a disoriented individual resembling Moravec self-medicating with a cocktail of prescribed painkillers and alcoholic beverages, though the company strongly denies their veracity." - -/datum/lore/codex/page/article98 - name = "02/02/65 - NanoTrasen Announces Employee-Led Advisement Scheme" - data = "Trans-stellar giant NanoTrasen has today announced the launch of their brand-new 'NanoTrasen Employee Representation Committee' scheme, which will allow employee-selected representatives to have a say in company policy on a local basis. Initially launching as a pilot in 'a few key regions', the NERC is intended to provide an in-house method for those working for NanoTrasen to 'address grievances and provide valuable input without resorting to radical means'.\ -

                    \ - Vir is one of three size regions of varying scale but similar company presence selected for the pilot program, alongside the 'Deep Bowl' - including Stove, Viola and New Singapore, and 'South America', on Earth. Elections are expected to take place over the coming weeks, with each region electing representatives from core facilities in their locale. The Vir Branch will be expected to select five employees, one from each of; their New Reykjavik Head Office (Colloquially know as 'CentComm'), Karan colony the NCS Northern Star, Kalmar-based NMB Gullstrand medical research center, Sivian way-station NLS Southern Cross, and the company's sprawling Ekmanshalvo Fabrication Plant.\ -

                    \ - The Representation Committee is set to have quite significant powers over their region's day-to-day operations, ranging from Standard Operation Procedure, to security enforcement, to interior design." - -/datum/lore/codex/page/article99 - name = "02/06/65 - Calvert Moravec Dead At 58, Announces Withdrawal From Public Life" - data = "Just over a month since his injury in a brazen assassination attempt, NanoTrasen Vir's Chief Operations Officer Calvert Moravec has announced his 'temporary withdrawal' from public and corporate matters, in an inspiring speech given from his post-cloning recovery ward. The noted corporate executive passed away on Friday, following a long comatose period, but is expected to make a full recovery.\ -

                    \ - Mr. Moravec has stated that he believes a spell 'away from the public eye' will do wonders for his recovery, and 'expects to make a return some time in the coming years'. However, Moravec has reaffirmed that his stance on corporate policing, which is believed to have been the reason for the January bombing, remains unchanged; 'The refusal of Hedberg-Hammarstrom to allow our security teams to access government criminal records has been a significant hindrance to the investigation into my death. Allowing just one corporation exclusive access to this data is unequivocally wrong.'" - -/datum/lore/codex/page/article100 - name = "02/08/65 - NERC Campaigning Begins in Vir" - data = "NanoTrasen's latest employee initiative, the NanoTrasen Employee Representation Committee is set to hold their first in-house election this coming weekend, after 'respectable' signup rates for the positions across all participating Vir-based facilities.\ -

                    \ - According to those interviewed at a few sites, campaigning may be mixed. One potential committee member for the Gullstrand Medical Center described the competition as 'cut-throat' with staff looking to be split between the selection of already respected chief medical experts, and 'more represenative' service staff. Conversely, one worker at the Ekmanshalvo Fabrication Plant complex stated that he 'would probably just vote for whoever I've heard of.'\ -

                    \ - Final voting will take place on Saturday the 13th of February, with results announced by the following Monday." - -/datum/lore/codex/page/article101 - name = "02/11/65 - Top Astronomers Announce 'Alarming' Tachyon Downtick" - data = "The Galactic Survey Administration has today released a 'high priority' report on what is being described as a 'significant decline in core tachyon density' throughout the known galaxy. According to recent findings tachyon deterioration, which had previously been 'negligible' has undergone a 'rapid acceleration' which could begin to seriously affect interstellar transit in as soon as 30 to 50 years.\ -

                    \ - Tachyons are a naturally occurring particle present in varying density which form the primary mechanism for bluespace drive operation. In simple terms, the higher the density of tachyons, the more efficiently starship engines are capable of operating. Low-density regions such as the Rarkajar Rift have long posed a challenge to interstellar trade, and the expansion of such regions could prove devastating to galactic unity.\ -

                    \ - FTL industry leaders Focal Point Energistics have already announced 'immediate investigation' into the GSA's findings after taking a blow on the stock exchange, and have guaranteed a 'commitment to future-proofing' in all forthcoming products." - -/datum/lore/codex/page/article102 - name = "02/15/65 - NanoTrasen Announces First NERC Members" - data = "Following Saturday's internal election, NanoTrasen Vir has elected the first five members for its new Employee Representation Committee. These individuals are expected to hold the position for one month:\ -

                    \ - For the New Reykjavik Head Office Iraluq James, a remote personnel operative, won the position on a platform of employee morale, team-building and employee-employer conflict resolution.\ -

                    \ - On the NCS Northern Star, NanoTrasen's main colony station in Vir Victoria Bell, a cybernetic chef and hardshell operator won her place with promises of increased regulatory transparency and clarity, including the publication of an in-house magazine.\ -

                    \ - At the NMB Gullstrand medical science center in Kalmar, a close race between top doctors was upset by the election of a station security commander, Wish Elara-Voight who promises 'improvements to the safety and security of NanoTrasen facilities with a focus on enforcing current SoP and CorpReg policies and placing new procedure in the handbooks.'\ -

                    \ - The waypoint station NLS Southern Cross selected a cybernetic Unathi candidate, researcher Dr. Haven Rasikl to represent them on the basis of encouraging interdepartmental cooperation and communication.\ -

                    \ - Finally, the Ekmanshalvo Fabrication Plant has selected fabrication programmer Terazon Norddahl, who promises to push for overhauls and modernization of the company's internal transport and support systems, including shuttle scheduling." - -/datum/lore/codex/page/article103 - name = "03/10/65 - GSA Confirms Presence Of 'New' Deep-Space Threat" - data = "This morning the Galactic Survey Administration released confirmation that a previously unknown deep-space dwelling species was indeed responsible for the disappearance of five Extraplanar Discovery Division vessels early last year, three of which have returned adrift over the past several days. The species, colloquially dubbed the 'Bluespace Bugs' after initial reports from the recovered EDD ships, are believed to be capable of manipulating matter in a manner reminicent of experimental bluespace technology.\ -

                    \ - The three 'returning' vessels are said to have been 'significantly off-course', with their points of galactic re-entry differing vastly from their initial points of egress, and final programmed return routes. Each ship having departed on routes from Sol, the SCG-E Bungaree was reported adrift by Almach Protectorate officials close to the Vounna system, the SCG-E Ketumati severely damaged on a collision course with the Erebus star, in the Nyx system, and the SCG-E Mag Mell collided with the garden world of Sif, in Vir. The Gagarin and Xu Fu remain unaccounted for, but due to the proximity of final contact, their disappearance has also been ascribed to the 'bugs'.\ -

                    \ - Investigation of the recovered vessels each indicated signs of a brief struggle, always following reports of unexplained equipment disappearances and equipment failure. No crew, or crew remains have been found, with the exception of a single unidentified human thumb aboard the Bungaree.\ -

                    \ - Additionally, each ship has been identified as having undergone some degree of material alteration, with elements ranging from hull plating to crew belongings having taken on a crystaline form dubbed 'Magmellite' after the ship most thoroughly 'reconstructed'. It is unclear whether the mineral is somehow secreted by the alien species, or merely a product of the same environment.\ -

                    \ - GSA sources have stated that there is no current evidence that the insectoid creatures - identified only from scattered descriptions left from missing EDD personnel - are in any way sapient or malicious, and the lost vessels are thought to have merely disturbed a scattered array of endemic populations far from the galactic plane. Precisely why the species has only been encountered in the past year, and now all at once, is not yet known." - -/datum/lore/codex/page/article104 - name = "05/24/65 - Oculum Apologizes for Interstellar Relay Outages" - data = "Week-long difficulties with interstellar transmissions in several central star systems due to an 'unexpected behaviour' in bluespace relays 'should be resolved soon' according to telecoms giant Oculum Broadcast. The company has apologized to customers for connection speed drops as much as 80% which have rendered certain live systems near impossible for many customers, including disruption of telecast Colonial Assembly meetings on Luna which have been temporarily put on hold in order to allow representatives time to attend in person.\ -

                    \ - Customers may experience reduced speeds compared to prior service, but have been promised a two-year price freeze on exonet service packages for home and business users on most major providers in participating areas. In-system communications remain unaffected.\ -

                    \ - Initial reports that the finale broadcast of Game of Drones was 'rendered unwatchable' by connection issues proved to be unfounded as severe lag and audio distortion were confirmed as 'part of the creator's artistic vision for the story's end'." - -/datum/lore/codex/page/article105 - name = "06/07/65 - 104 Feared Dead In Oasis Tourist Shuttle Incident" - data = "The Vir Governmental Authority has confirmed the destruction of a small interstellar tourist vessel departing the Vir system. The ITV Relaxation IX operated by Thousand Palms Hotels, was bound for a popular resort in Oasis and reportedly 'split in two' shortly after confirming system bluespace departure with Vir space traffic control, exposing all passenger compartments to space and killing at least 94 of the 108 people onboard.\ -

                    \ - Four crew members recovered from forward sections are being treated for 'non-life-threatening' pressure injuries at a nearby medical facility, and ten individuals believed to have been situated at the rear of the ship remain unaccounted for. The exact cause of the break-up is yet to be determined, but initial accounts from surviving crew members describe the bluespace drive of the ship as having 'taken off by itself' at such speed that the rear of the fuselage was 'cut clean off' with no regard for structural elements. Foul play is not currently suspected.\ -

                    \ - The VGA believes that the chances of finding further survivors are 'extremely slim', though efforts to recover the rear section, which was 'sling-shot' into interstellar space, are underway. Gilthari Exports, Thousand Palms' parent company have temporarily suspended operation of all vessels fitted with similar bluespace drives, and Major Bill's Transportation is expected to follow suit. Wulf Aeronautics was unavailable for comment at this time." - -/datum/lore/codex/page/article106 - name = "07/09/65 - 'Bluespace Bugs' Linked To Almach Tech" - data = "Initial public reports on the extraplanar species commonly known as 'Bluespace Bugs' has proposed that the first recognizable signs of their activity within observable space, coincide precisely with the development - and particularly the test-firing - of the Whythe Superweapon, and that there may be a direct link between the two. The report, released by the Galactic Survey Administration this afternoon hypothesizes that the newly developed bluespace manipulation techniques used in Whythe may have acted as a signal to the deep-space dwelling creatures in a manner similar to moths attracted to artificial light. The GSA is currently collaborating with the Almach Protectorate Government to investigate the potential link further.\ -

                    \ - Additionally, findings from analysis of both inorganic and biological samples collected from the three recovered Extraplanar Discovery ships believed to belong to the 'Bugs' has excited much of the scientific community, with news that the insect-like aliens and their apparent dietary waste-product Magmellite may be composed in a manner completely unlike any life previously encountered in the known galaxy. The findings may rewrite our understanding of biology and material science, though a full specimen is desired to confirm these early findings.\ -

                    \ - The creatures have been given a tentative scientific name X Extraneus Tarlevi, after Captain Volmer Tarlev of the SCG-E Ketumati whose recorded descriptions were instrumental in establishing a basic understanding of the species' behavior. Researchers currently believe that the Bluespace Bugs are merely a form of bulk-feeding omnivore attracted to the EDD vessels in deep space by their bluespace drives, and that the loss of the ships was merely unfortunate happenstance rather than deliberate, malicious attack. Studies are already underway to determine methods that might prevent further incidents of this types before any further extraplanar missions are approved." - -/datum/lore/codex/page/article107 - name = "08/15/65 - Enigmatic Arkship Sighted After 200 Year Voyage" - data = "Recent reports from the New Singapore-based Exoplanar Traffic Observation Committee, have claimed historic extraplanar arkship, the VHS Rodnakya, has changed trajectory and may be approaching the galactic plane once more.\ -

                    \ - The ETOC, comprised of mostly exoplanar and shuttlecraft enthusiasts in the Bowl, have been tracking both government and private survey expeditions since the 2450's, reporting the approximate locations and assumed status of vessels for public record. The VHS Rodnakya has been a major point of interest among ETOC members since its formation, sometimes dominating discussions entirely, and the focus for numerous unverified theories.\ -

                    \ - The Arkship and its support fleet, known as 'Vystholm' was constructed in the early 2300's by Stanislava Dalibor, and left the galactic plane in response to the abolition of the SCG's First Contact Policy that demanded the capture and interrogation of unknown sapient aliens. It has not had any official contact with the galactic community since. Largely known for radical and since-outdated views on non-human intelligent life, their original crew was known to include a number of early Icarus Front extremists which may have fermented into a dangerous ideology after 200 years of isolation.\ -

                    \ - This alleged trajectory change has sparked excitement among the committee, including a flurry of completely unsupported reports of spies infiltrating Solar society, from the corporate workforce to major government bodies.\ -

                    \ - The Vir News Network does not endorse the unverified claims of the Exoplanar Traffic Observation Committee." - -/datum/lore/codex/page/article108 - name = "09/12/65 - Gateway Transport Suspended Amidst Safety Concerns" - data = "Nine of the galaxy's top trans-stellars have announced immediate suspension of bluespace gateway transit services following a report by Wulf Aeronautics indicating that some of the same tachyon instabilities affecting their faster-than-light engine technology may have even more severe reprecussions for rapid point-to-point teleportation that could result in 'significant decrease in matter' during rematerialization that could result in customer death, disfigurement, or loss of luggage.\ -

                    \ - Dismissing accusations that this was a move to bolster a weakened spacecraft travel industry, Focal Point Energistics, the original developers of modern gateway technology were first to announce their suspension of service on all first-party operated access points. Major contract operators, including NanoTrasen, Ward-Takahashi, and Gilthari Exports have followed suit citing a desire for caution when dealing with premium employee transport.\ -

                    \ - NanoTrasen are the Vir system's leading operator of gateway transport, offering 'luxury, near-instant interplanetary commutes' between most major company facilities, and the Vir Interstellar Spaceport. The company says it will be removing many smaller gate installations for 'full examination and required enhancements' and expects the service to return 'within a few weeks'.\ -

                    \ - Major Bill's Transportation have announced an increased frequency of service to affected NanoTrasen locations for the duration of the gateway suspension." - -/datum/lore/codex/page/article109 - name = "10/23/65 - 12th Missing Ship Prompts Official Isavau Response" - data = "After a shocking 12th interstellar vessel was declared missing in the Isavau's Gamble system earlier this week, the SCG has formally announced the launch of an official investigation, including the deployment of a significant Fleet security presence in the region. The lost ship, the HFV El Cid is recorded as having a crew complement of eight, pushing the total missing people in the system over the past year over one hundred and fifty. A contingent of around twenty SCG vessels - search and rescue, salvage, and armed warships - has been dispatched to the region to take over from near-absent local investigators, but is not expected to arrive for at least a month due to worsening FTL flight conditions.\ -

                    \ - The El Cid is believed to have been hauling highly secure cargo internationally, which may have been the final deciding factor in launching a confederation-level investigation into the abnormal number of appearances, and the SCG has come under quick criticism for their apparent prioritization of lost cargo over sapient lives. Fleet Admiral Silvain Barka, celebrated veteran of the Almach War, has stated 'Lives are our number one concern. The matter of the Hephaestus cargo has simply moved our timeline forward due to concerns that if high-grade arms were to fall into pirate hands, the situation in Isavau's Gamble could rapidly worsen. We want to avoid that, and bring back as many of the missing crew as possible.'\ -

                    \ - While piracy has long been a concern in the Bowl, with the 'Jaguar Gang' pirates making their home in the region, and a general uptick having occurred since the beginning of the Almach Crisis, it is unusual for such a high proportion of missing ships crews to remain lost without report of recovery or ransom demands. Accusations of brutality have been levied at the Vystholm flotilla, despite their improbable distance from the star system. To date the XIV Sri Chamarajendra remains the single largest loss of life, at 32 believed dead during a large salvage mission late last year." - -/datum/lore/codex/page/article110 - name = "10/26/65 - 'Bluespace Bug' Confirmed Activity Near Tajaran Space" - data = "The following address is from Khama Suketa enai-Lutiir, representative of the Tajaran Pearlshield Coalition:\ -

                    \ - 'Good day, everyone. We would like to put the rumor mills to rest lest they get out of hand. Eleven Solar days ago, the Silk system's perimeter defenses picked up what appeared to be a derelict Vox raiding ship that had drifted into range. Security forces boarded the craft and found clear signs of fighting within the crew compartments, but no traces of the crew themselves. The craft's bluespace drives were notably ripped out of the hull, and it's believed the craft had been drifting for up to nearly two Solar weeks prior, as it has been identified as the 'Skiskatachtlakta', belonging to a well-known Vox raiding group our cousins in the Arrathiir system have been contending with for a few years now, and that was their last confirmed contact with it.\ -

                    \ - 'Most importantly however, and the key reason for this address, is a significant portion of the ship's hull, particularly concentrated around the site of its bluespace drives, has been confirmed to've been converted to the material known as 'Magmellite', related to the so-called 'bluespace bugs' or X Extraneus Tarlevi that have been of note outside our borders. To acknowledge and assuage any natural alarm on our own part, there is no indication any part of the incident took place on our side of the Rift, and we are deploying additional reconnaissance and recovery ships, drones and general personnel throughout controlled space to keep a watchful eye, and we are negotiating with Solar forces in Silk to expand our presence as appropriate. We are currently analyzing the Magmellite samples recovered from the Skiskatachtlakta and will be sharing any findings with our fellows in the scientific sphere, as well as returning the ship's plundered riches if and when possible.\ -

                    \ - 'There is currently no cause for concern beyond the understandable. If and when there is, we will rise to the occasion as always. Together, we will discover the truth of these strange times we find ourselves in. Until then, be safe, and be well. May our stars shine upon us all.'" - -/datum/lore/codex/page/article111 - name = "01/14/66 - Unathi Border On Alert After Manoeuvre Scare" - data = "The Solar garrison at Abel's Rest remains on high alert following a critical near-miss scenario resulting from an unannounced Unathi fleet arrival at the disputed system's perimeter alert zone, which has since been determined to be not a deliberate act of war. According to the Moghes Hegemony the fleet is 'part of a re-evaluation of key troop deployments, that will be of mutual benefit to peace in the region'.\ -

                    \ - The arriving fleet, comprised of mostly troop transport vessels, remained in distant orbit of the star for seven hours before diplomatic contact could be established, during which time the SCG garrison remained at battle-ready stance. This marks the closest proximity to open conflict in Abel's Rest since the end of the Unathi War over 45 years ago. SolGov peacekeepers have confirmed decreased planetside garrison activity over the past several months, and key Hegemony warships have departed in recent weeks.\ -

                    \ - It remains impossible to determine the Unathi's true intentions as such a withdrawal could preface devastating planetary bombardment, or invasion elsewhere, and some members of the Icarus Front have called for immediate redeployment to the front in response. The SCG Fleet has assured the public that relations with the Hegemony have 'never been better' and that long-range sensors indicate a withdrawal from the entire border region on a far wider scale." - -/datum/lore/codex/page/article112 - name = "02/13/66 - Isavau's Gamble Fleet Report 'All Silent'" - data = "Early reports from the SCG contingent sent to investigate potential pirate activity in the Isavau's Gamble system include shocking descriptions of a system 'gone silent', with supposedly zero manmade signals originating from any ship or station in the entire area. Prior loss of contact several days prior had been ascribed to naturally occurring background noise 'overwhelming' the system Spaceport's bluespace relays, which had been operating at reduced efficiency due to the ongoing tachyon downtick.\ -

                    \ - While messages from the SCG-D Brazen Bull were similarly weakened by what has been described as a bluespace 'hum', no immediate cause for alarm was raised. The fleet indicated that they were proceeding to investigate the Isavau International Spaceport and hoped to make direct contact with the crew within a matter of hours.\ -

                    \ - While telecommunications outages were a forecasted consequence of the tachyon downtick, highly focused and widespread instances such as that occurring in Isavau's Gamble were not, which has cast doubt on the predictive models being used, and raised concerns of additional factors at play. A newly observed phenomena from the Brazen Bull's report, 'blue points of light suspended in space with no discernible origin' has led to some speculation of a re-concentration of tachyon particles which some hope may hold the key to preventing further deterioration of the energetic landscape." - -/datum/lore/codex/page/outage - name = "02/14/66 - 02/15/74 - RELAY DATA OUTAGE" - data = "Article data lost for 2922 day period. Reason: Damage sustained to Oculum systems during Skathari Incursion. Please contact your administrator for details." - -/datum/lore/codex/page/article113 - name = "08/17/74 - Vox Continue To Show Alarming Divergence In Behaviour; Experts Baffled" - data = "Reports published by the Interspecies Joint Anti-Piracy Initiative this month continue to support an unsettling trend; the Vox, violent reptoavian aliens best known for engaging in rampant piracy, are simply not behaving as expected. Tactical and strategic models built painstakingly over the last three centuries are now showing accuracy ratings as low as 3%, with a rapidly growing number of raids either occurring outwith predicted regions or timeframes, or more alarmingly, not being predicted at all.\ -

                    \ - 'Something has altered their priorities,' said Wataru Murata, the head of the Joint Operations Group in charge of tracking Vox activity. 'In some areas we're seeing vastly more unarmed transport, mining and salvaging vessels, than we were as recently as two months ago, and in others, we're seeing strength and frequency of raiding groups as much as tripling. It's a very complex situation.'\ -

                    \ - 'They've never been so unpredictable before,' agreed senior naval officer Akira Doi. 'I understand that there is currently no official theory on why this is happening, but I believe it all goes back to the Skathari Incursion.'\ -

                    \ - Many leading scientists are skeptical that the Incursion, despite having thrown much of the galactic community into chaos over an eight-year period of conflict with the insectoid Skathari, could have seriously disrupted the Vox in the same way, given the relatively low indications of Skathari presence in known Vox operating territories, but Admiral Doi strongly disagrees.\ -

                    \ - 'The Skathari are aggressive to an unprecedented degree,' he explained, 'and they had the ability and numbers to annihilate entire communities. I see no reason why that shouldn't have applied to the Vox.'\ -

                    \ - While some colonies and stations welcome this reprieve from raids and piracy, others are finding themselves under renewed pressure, with resources and labour withdrawn on the advice of apparently unreliable modelling. A representative of the Pearlshield Coalition territory of Arrathiir had this to say regarding the sluggish reallocation of resources after the latest wave of raids:\ -

                    \ - 'On the frontier we are no strangers to fighting and rebuilding after raider attacks, but without reliable resupply of fuel and munitions from the home worlds, we cannot even field our ships. This is costing us dearly.'" - -/datum/lore/codex/page/article114 - name = "09/25/74 - 'Emerald Skies' Conspiracy Protest Shuts Down New Reykjavik Mall" - data = "This weekend saw the Vinterlykke Shopping Center shut down after a surge of protesters crowded the mall. Vinterlykke is known as the largest mall in New Reykjavik, renowned for its expansive selection of shops, restaurants, and entertainment options.\ -

                    \ - According to witnesses, protesters crowded the major walkways, preventing shoppers from passing and leaving some stranded in shops to avoid the crowd. Many shops barred and locked the storefronts in response, an unnamed employee of one store commenting, 'That many people <...> you never know if it's going to get out of control.'\ -

                    \ - The protesters chanted with signs regarding NanoTrasen's exclusive rights in studying the Sif Anomalous Region, demanding that studies into the anomalous properties be public knowledge. The march appears to be headed by the Skathari conspiracy group 'Emerald Skies', known in recent months for many other smaller protests regarding the government response to the Incursion, and beliefs that Skathari are more intelligent than the public has been informed.\ -

                    \ - The disruption was eventually cleared out by mall security forces before the crowd could grow out of control. It is reported that Vinterlykke will reopen on Monday with additional security measures in place." +/datum/lore/codex/category/main_news // The top-level categories for the news thing + name = "Index" + data = "Below you'll find a list of articles relevant to the current (as of 2565) political climate, especially concerning the local \ + region. Each is labeled by date of publication and title. This list is self-updating, and from time to time the publisher will push new \ + articles. You are encouraged to check back frequently." + children = list( + /datum/lore/codex/page/article114, + /datum/lore/codex/page/article113, + /datum/lore/codex/page/outage, + /datum/lore/codex/page/article112, + /datum/lore/codex/page/article111, + /datum/lore/codex/page/article110, + /datum/lore/codex/page/article109, + /datum/lore/codex/page/article108, + /datum/lore/codex/page/article107, + /datum/lore/codex/page/article106, + /datum/lore/codex/page/article105, + /datum/lore/codex/page/article104, + /datum/lore/codex/page/article103, + /datum/lore/codex/page/article102, + /datum/lore/codex/page/article101, + /datum/lore/codex/page/article100, + /datum/lore/codex/page/article99, + /datum/lore/codex/page/article98, + /datum/lore/codex/page/article97, + /datum/lore/codex/page/article96, + /datum/lore/codex/page/article95, + /datum/lore/codex/page/article94, + /datum/lore/codex/page/article93, + /datum/lore/codex/page/article92, + /datum/lore/codex/page/article91, + /datum/lore/codex/page/article90, + /datum/lore/codex/page/article89, + /datum/lore/codex/page/article88, + /datum/lore/codex/page/article87, + /datum/lore/codex/page/article86, + /datum/lore/codex/page/article85, + /datum/lore/codex/page/article84, + /datum/lore/codex/page/article83, + /datum/lore/codex/page/article82, + /datum/lore/codex/page/article81, + /datum/lore/codex/page/article80, + /datum/lore/codex/page/article79, + /datum/lore/codex/page/article78, + /datum/lore/codex/page/article77, + /datum/lore/codex/page/article76, + /datum/lore/codex/page/article75, + /datum/lore/codex/page/article74, + /datum/lore/codex/page/article73, + /datum/lore/codex/page/article72, + /datum/lore/codex/page/article71, + /datum/lore/codex/page/article70, + /datum/lore/codex/page/article69, + /datum/lore/codex/page/article68, + /datum/lore/codex/page/article67, + /datum/lore/codex/page/article66, + /datum/lore/codex/page/article65, + /datum/lore/codex/page/article64, + /datum/lore/codex/page/article63, + /datum/lore/codex/page/article62, + /datum/lore/codex/page/article61, + /datum/lore/codex/page/article60, + /datum/lore/codex/page/article59, + /datum/lore/codex/page/article58, + /datum/lore/codex/page/article57, + /datum/lore/codex/page/article56, + /datum/lore/codex/page/article55, + /datum/lore/codex/page/article54, + /datum/lore/codex/page/article53, + /datum/lore/codex/page/article52, + /datum/lore/codex/page/article51, + /datum/lore/codex/page/article50, + /datum/lore/codex/page/article49, + /datum/lore/codex/page/article48, + /datum/lore/codex/page/article47, + /datum/lore/codex/page/article46, + /datum/lore/codex/page/article45, + /datum/lore/codex/page/article44, + /datum/lore/codex/page/article43, + /datum/lore/codex/page/article42, + /datum/lore/codex/page/article41, + /datum/lore/codex/page/article40, + /datum/lore/codex/page/article39, + /datum/lore/codex/page/keldowinterview, + /datum/lore/codex/category/article38, + /datum/lore/codex/page/article37, + /datum/lore/codex/page/article36, + /datum/lore/codex/page/article35, + /datum/lore/codex/page/article34, + /datum/lore/codex/page/article33, + /datum/lore/codex/page/article32, + /datum/lore/codex/page/bjornretirement, + /datum/lore/codex/category/article31, + /datum/lore/codex/page/article30, + /datum/lore/codex/page/article29, + /datum/lore/codex/page/article28, + /datum/lore/codex/category/article27, + /datum/lore/codex/page/article26, + /datum/lore/codex/page/article25, + /datum/lore/codex/page/article24, + /datum/lore/codex/page/article23, + /datum/lore/codex/page/article22, + /datum/lore/codex/page/article21, + /datum/lore/codex/page/article20, + /datum/lore/codex/page/article19, + /datum/lore/codex/category/article18, + /datum/lore/codex/page/article17, + /datum/lore/codex/page/article16, + /datum/lore/codex/page/article15, + /datum/lore/codex/page/article14, + /datum/lore/codex/page/article13, + /datum/lore/codex/page/article12, + /datum/lore/codex/page/article11, + /datum/lore/codex/page/article10, + /datum/lore/codex/page/article9, + /datum/lore/codex/page/article8, + /datum/lore/codex/page/article7, + /datum/lore/codex/page/article6, + /datum/lore/codex/page/article5, + /datum/lore/codex/page/article4, + /datum/lore/codex/page/article3, + /datum/lore/codex/page/article2, + /datum/lore/codex/page/article1, + /datum/lore/codex/page/about_news, + ) + + var/newsindex + +/datum/lore/codex/category/main_news/New() + ..() + newsindex = LAZYLEN(children) + +/datum/lore/codex/page/about_news + name = "About the Publisher" + data = "The Daedalus Pocket Newscaster is produced and maintained by Occulum Broadcast, the foremost authority on media distribution \ + and owner-operator of the award-winning Daedalus Dispatch newsletter. We use our unparalleled network of freelance reporters, political scientists, \ + and other experts to deliver hour-by-hour analysis of a complex interstellar political climate, an analysis which you now hold in your hands. For more \ + information, feel free to visit our homepage at oc.about.tsc, or the sites of any of our constituents." + +/datum/lore/codex/page/article1 + name = "08/30/61 - VGA Legalizes Prometheans; Nanotrasen Begins Manufacture and Testing" + data = "Today's meeting of the Vir Bicameral led to the passing of the Wynther-Helsey Bill, an implementation of the legal framework \ + used in Aetolus to handle the production and cultivation of the Macrolimbus species dubbed \"Prometheans\". These ill-researched organisms \ + possess cognitive abilities easily equaling those of A-class drones, but so far have not been included under the EIO's list of dangerous \ + intelligences and are thus much more profitable for manufacture as expert systems by corporations such as NanoTrasen.\ +

                    \ + While many systems in the Almach Rim have already passed similar bills, this is the first system so close to Sol to have done so. More\ + concerning still is NanoTrasen's business practice regarding the intelligences: much like their positronic lines, sources within the\ + company indicate that they will be \"farmed out\" to employees of the corporation and residents of their Northern Star and Cynosure\ + habitation complexes. Quote our source, who wishes to remain anonymous, \"\[we\] call the program 'Lend-Lease', sometimes. The whole idea\ + is that we only have to pay the\ cost of the Promethean core, which is about 2000-3000 thalers after startup costs, and we still get\ + the data we need while \[our\] own employees pay to feed 'em and put hours into raising them.\"\ +

                    \ + The bill passed fairly quietly this afternoon, owing to the closed nature of the Bicamarial. A post-facto Occulum poll of voting-age\ + VGA citizens suggest that fully 80% of them did not even know what a Promethean was prior to the most recent general election. A\ + follow-up poll indicates that an appreciable number of Sivians do not support the framework's current implementation." + +/datum/lore/codex/page/article2 + name = "2/3/62 - Corporate Coup on Aetolus" + data = "A recent incident aboard the NRS Prometheus issued in a major change in the leadership of the Promethean homeworld. During \ + a late-night meeting of the Nanotrasen Board of Trustees, several high-ranking personnel, including Head of Research Naomi Harper,\ + announced their intention to assume direct control of Nanotrasen facilities in the system. It is known that several dissenting \ + members of the board were shot to death by Promethean test subjects. Our information comes from a survivor of the coup, who for \ + reasons of security has chosen to remain annonymous. All outbound shipments affiliated with Nanotrasen have ceased.\ +

                    \ + While neither Grayson Manufacturies nor Nanotrasen have made an official statement, Nanotrasen CEO Albary Moravec has called the \ + incident \"shocking, if the allegations are to be believed\" and has assured shareholders that Nanotrasen will respond to the \ + incident with as much force as it warrants.

                    Requests for a statement directed to the Board of Trustees or Dr. Harper were \ + not responded to. Free Traders are recommended to stay clear of the region until the situation resolves itself." + +/datum/lore/codex/page/article3 + name = "2/10/62 - Aetolian Partisans Declare Independence" + data = "Breaking their week-long silence, the leaders of the Aetolian Coup, and their spokesperson and presumed leader Naomi Harper issued an address earlier today, delivered to the Oculum Broadcast office on Pearl by drone courier. Quote Dr. Harper: \"Our previous silence was a necessity, while we consolidated our forces and dealt with corporatists both internally and in Vounna's former Grayson outposts.\". In Harper's hour-long address, she berates the failure of SolGov to provide adequate protections for Prometheans. \"We will not let the Promethean be another positronic brain; they will not labor under a century of slavery, deprived of a state to call their own. The Luddites of the Friends and of the Icarus Front will not be permitted to decide the fate of a nascent race before it begins.\"\ +

                    \ + Harper proceeded to unilaterally declare Vounna's independence from SolGov, claiming sovereignty over the system as the first Chairperson of the \"Aetolian Council\". Speaker of the Shadow Coalition ISA-5 has urged their government to treat the developing situation with caution but decried Harper's rhetoric, stating in a press release, \"While I know well the injustices visited on myself and my people by misguided forbearers, it is important to treat any emerging technology with respect. Current policies regarding the Prometheans are designed to limit risk during sociological trials on Aetolus and beyond. As for myself, I doubt the sincerity of this human who claims to speak for the Prometheans, when the Prometheans are perfectly equipped to speak for themselves.\"\ +

                    \ + NanoTrasen is expected to redouble their Promethean research programs in the Vir system until stability is restored to Vounna." + +/datum/lore/codex/page/article4 + name = "2/14/62 - SCG Denounces Aetolian Coup; Mobilizes Fleet" + data = "Dismissing claims of inaction, a spokesperson for the Solar Confederate Government today confirmed that the Colonial Assembly has voted overwhelmingly in favor of swift military action in response to the coup on Aetolus earlier this month. Icarus Front Chairperson Mackenzie West was quick to make a damning official statement: \"Dr. Harper and her radical agitators cannot be excused for their violent, despicable attempts to destabilize the flourishing economy of the Almach Rim. The ruthless murder of innocents, and illegal seizure of private property are crimes that cannot merely be met with strong words and gentle slaps to the wrist\"...\"I am proud to announce that two units of brave Solar marines have been assigned to the SCG-R Song Shi rapid response cruiser, with the full backing of the Icarus Front - and I hope with my heart, the backing of all patriotic Solar citizens.\"ÂÂ\ +

                    \ + The decision faced resistance from more laissez faire Assembly member states, including prominent SEO governor Bruno Ofako, delaying an earlier consensus. Supporters of the action hope that this decisive display of military strength will encourage the rebels to stand down without further bloodshed, and submit to prosecution by the Lunar High Courts.\ +

                    \ + The Icarus Front has also proposed a temporary ban on continued Promethean research, though this motion has yet to gain any traction." + +/datum/lore/codex/page/article5 + name = "2/23/62 - \"Almach Association\" Shocks Nation" + data = "Shocking the nation, in the wee hours UTC a number of governments in the Almach Rim announced their intent to secede from the Confederacy as a unified political organization they refer to as the Almach Association, joining the already-declared Aetolian Council. Among the half-dozen affected systems is Angessa's Pearl, through which the Song Shi was passing en route to Aetolus. The Association has already issued a political manifesto and a foundational charter, leading political scientists across the galaxy to suspect back-doors collusion and possible Shelfican interference, a hypothesis made more likely by Morpheus Cyberkinetics' exonet site voicing support for the Association. Others suspect a moment of political crystallization, not unlike that in the Golden Hour three centuries ago. These researchers are already referring to this morning's events as the Gray Hour.\ +

                    \ + The Association's official manifesto repudiates the Five Points, calling them \"an archaic and distinctly human invention\". Experts agree that this bold declaration puts the Movement more in line with the Golden Hour than with the Age of Secession, and many fear that nothing short of a miracle like the discovery of the positronics will spare humanity from a bloody civil war.\ +

                    \ + While the Association currently lists only a handful of Almach Rim systems as \"Constituent Organizations\", it has named Shelf, the Free Relan Federation, and the Eutopian Foreign Relations Board as \"observers\". The implications of this status are yet to be identified.\ +

                    \ + The fate of the SCG-R Song Shi and her crew remain unknown." + +/datum/lore/codex/page/article6 + name = "3/03/62 - A Week Out From Almach: What are the facts?" + data = "* Several organizations in the Almach Rim, including Angessa's Pearl, the Aetolian Council, the Interstellar Workers of Wythe, the Republic of Whitney, and members of several prominent families in the Neon Light unilaterally declared secession from SCG.

                    *This secession was first called the Grey Hour by political scientists in New Florence, a term popularized by reporter Elspor Fong.

                    * Shelf, the FRF, and the EFRB were declared \"observers\" in the Almach Association charter.

                    * None of these organizations have issued a statement on the matter.

                    * The SCG-R Song Shi was stranded in the region during the secession.

                    * SolGov has not issued an official statement of the fate of the Song Shi.

                    * Several confederate agencies, including Emergent Intelligence Oversight, the Trade and Customs Bureau, and SCG Fleet Intelligence have declared a \"state of emergency\".

                    * SolGov itself has NOT declared a state of emergency.

                    * Legitimate communications in and out of the Almach Rim are restricted to audited text messages for the period.

                    * Several illegitimate communication links exist and are believed by Fleet Intelligence to be currently hosting the official sites for Morpheus Cyberkinetics and for the Association itself.

                    * Icarus Front chairperson Mackenzie West has proposed a moratorium on the creation of new Prometheans for the duration of the crisis.

                    * Local laws on the subject will apply until the Assembly meets late in May.

                    * No confederate lawmaker has proposed action against Relani, Shelfican, or newly Almachi nationals living within stable regions.

                    * The border remains tightly closed to migrants, media, and diplomats alike." + +/datum/lore/codex/page/article7 + name = "3/21/62 - Relan, Shelf Join the Almach Association" + data = "Recent reports from within the Association indicate that the Free Relan Federation and Shelf have officially decided to join the Almach Association. President Nia Fischer of the FRF had this to say on the matter, in a speech addressed to the population at large. \ +

                    \ + \ + \"Our decision to join the Association may, at first, seem strange. It is true that we have much to gain from trade with the Solars, and that the radical transhumanism of Angessa's Pearl is not our way. But I will remind you that it was Shelf, not Sol, who ensured our prosperity just over two decades ago-- who safeguarded our independence and prevented us from falling to barbarism and dictatorship. We owe it, not just to Shelf but to all the members of the Almach Rim, to support their independence just the same. And that, my fellow Relanians, is the crux of it all. The Association is a revolution, at the heart of it all, and many of the now-independent states were owned near-outright by Trans-Stellar Corporations until the Association allowed them to shake out their fetters. What right do we have to sit by while just a dozen light-years coreward newly-born republics suffer the growing pains of independence? What right do we have to bask in our own stability when our neighbors, our comrades in ideology, are struggling with a cruel blockade proposed by politicians back on Earth and Luna? That is why we must join with them, guard them, and guide them, for as long as need be.\"\ +

                    \ + \ + A Shelfican spokesperson, meanwhile, had only this to say:\ +

                    \ + \"We're probably going to regret this but, y'know, the whole thing is kind of our fault. Sure, whatever.\"" + + +/datum/lore/codex/page/article8 + name = "4/1/62 - Almach Cordon Breached by Unknown Organization" + data = "Early this morning, SolGov ships assigned to the Almach Cordon around the Rim territories reported that a number of bulk freighters had eluded apprehension and are now at large within the Golden Crescent. Captain Volkov of the SCG-D Henri Capet reports that the blockade-runners were highly organized and determined, citing several lightly-manned ships left behind to tie up the SolGov forces long enoughfor the freighters to escape, detonating their reactors when they lost the ability to continue fighting. This resulted in three Fleet casualties and a significant degree of damage to the Henri Capet. The contents and location of the freighters are unknown at this time. In response, eight light-response vessels are being assigned to the Saint Columbia Fleet Base from Jahan's Post and Zhu Que. Residents and traffic officials in Vir, Oasis, and Gavel are to remain alert and notify police if any suspicious or unregistered craft enter their space.\ +

                    \ + A spokesperson for the Association claims that, while they make no attempts to stop aspiring blockade runners, the organization responsible for this most recent attack is unaffiliated with the Association as a whole and deny any knowledge of their identity or motives." + +/datum/lore/codex/page/article9 + name = "4/7/62 - Boiling Point Tragedy in Gavel" + data = "Today, April the Seventh, marks a day of tragedy for all the galaxy. A small group of operatives claiming to be associated with Mercurial terrorist organization Boiling Point invaded major refueling platform NLS Aquarius in the Republic of Gavel after hijacking civilian transport vessel WTV Orion and faking a drive failure. Several detonations were reported within the Aquarius, the operatives entering through unknown (potentially Skrellian) means. After stating their affiliation and desire for the liberation of all \"Prometheans, drones, and ex-humans\", they opened fire on a crowd of unarmed bystanders, killing as many as seven. A multiple-hour long firefight with Nanotrasen corporate asset protection ensued, at which point the operatives demonstrated capabilities well in excess of Five Points-prescribed limits. Asset Protection was successful in repelling the terrorists, though their harsh methods drew outrage from the people they were protecting, leading a notable director of research to resign her position with the corporation. Several operatives are still at large, though the SG-PV Juno recovered two living terrorists and one totaled synthetic platform. \ +

                    \ + The intervention of a local Defense Force drone wing on behalf of the terrorists leads many in the intelligence community to assume that more Boiling Point operatives remain active within Gavel, and possibly nearby systems such as Vir and Oasis. Some have also noted that elements of the terrorists' tactics and augmentations suggest Association training, though the specifics remain classified. More information as the story breaks." + +/datum/lore/codex/page/article10 + name = "4/13/62 - Association Proposes Joint Operation" + data = "Condemning the actions of Boiling Point on Gavel this week, representatives from the Almach Association and SolGov met to discuss joint fleet action. At the end of nearly a week of closed-doors negotiations, the Association has agreed to send in a significant contingent of Association Militia vessels as a show of good-will. These vessels will be active in the Golden Crescent, searching for Boiling Point facilities believed to be located in the outskirts of major systems. The influx of manpower allows the Fleet to continue patrolling the Heights and the Bowl, in hopes of containing the spread of the organization. This operation also marks the opening of the Almach Cordon, although travelers are advised that migration between the regions will remain extremely limited.\ +

                    \ + While undoubtedly a sign of increased trust between the Confederacy and the Rim, some have voiced concerns with the action's adding legitimacy to the Association government. Quote Rewi Kerahoma, SEO Chairperson of the Board: \"The meeting with the Association regime was inappropriate, but actively allying with them is something else entirely. If the generals think we don't have the fleet to hunt down a bunch of rabble-rousers without weakening ourselves to piracy and foreign invasion, then it is a sign that we need to grow our shipyards in the Bowl, and give jobs to the hardworking Solars that live there-- not that we need to collaborate with terrorists.\"\ +

                    \ + Transgressive Technologies and Interior Police Executive Sifat Unar issued a memo indicating that, while the assistance of the Association's more conservative elements in this matter is appreciated, the Five Points of Human Sanctity remain intact and SolGov categorically refuses aid from \"transhumans, posthumans, uplifts, and Fortunates.\"" + +/datum/lore/codex/page/article11 + name = "4/29/62 - New Data from Shelf Suggests Continued Migration" + data = "Despite their recent inclusion in the Almach Association, astronomers have confirmed that Shelf is continuing to migrate along the Almach Stream to a new star. Sources within the Association claim that Shelf's participation in the organization has been \"lukewarm at best\", and that their continued migration is to be expected. Morpheus executives have refrained from issuing a statement on the matter, but given their statements upon entering the Association are believed to view themselves as personally culpable for the Gray Hour. Analysts suggest that Shelf may be unwilling to enter a shooting war with SolGov if the situation in the Rim destabilizes." + +/datum/lore/codex/page/article12 + name = "5/07/62 - Allen Family Matriarch Expelled from Neon Light" + data = "The Allen family of the Neon Light, the largest single habitat-ship in Solar space, has been ousted in a nearly bloodless coup today. The Allens, staunch supporters of the Association and advocates for the criminal ark's inclusion in the organization, had attempted to seize control of the ship's agricultural region during the Almach Cordon. They effectively held the ship for a matter of weeks, but were defeated by loyalists to the reigning Crow family. Stripped of their position as rulers of the Third Stacks, their matriarch was summarily executed by spacing in what the current regime is referring to as an \"expedited exile\". This is believed to mark the end of the question of Neon Light's membership in the Association, and the nominal SolGov protectorate is expected to remain neutral for the foreseeable future." + +/datum/lore/codex/page/article13 + name = "5/15/62 - Anti-Fleet Riots on Saint Columbia" + data = "As military vessels from the Almach Association continue to enter the Golden Crescent as part of a SolGov initiative to combat the Boiling Point terrorists believed to be hiding in the region, political unrest in the upstream portions of the region continue to grow. Many in the Republic of Saint Columbia, a small upstream nation, have responded to increasing militarization of their local Fleet base by taking to the streets, blocking pedestrian traffic in the capital of Barrueco and shutting down entire industries by destroying or disabling infrastructure. Quote rioter Luisa Tassis, \"we've been sick of the Fleeties squatting in our system and breathing down our neck, and now there's going to be even more of them? No, screw that. If there's going to be a war between the Rim and the Core, I know what side I'd rather be on.\"\ +

                    \ + Association leaders have refrained from officially supporting the rioters, though many suspect that Association propagandists have sparked the unrest. Solar officials, on the other hand, were quick to offer assurances that the unrest will be calmed long before it begins to affect the Fleet base in system." + +/datum/lore/codex/page/article14 + name = "5/25/62 - Harper's Aetolus Remains Shadowed" + data = "The recent detente with the Almach Association has prompted easier communications with Rim governments. Loved ones separated by the cordon have a chance to communicate once more, trade is posed to recommence, and light has been shed on the conditions of Shelf, Relan, and Angessa's Pearl. Amid this light is a patch of darkness. The fourth major polity of the Association, the Aetolian Council remains inscrutable, with no publicly-available information in the system available after their purge of corporate loyalists during the Gray Hour. What reports do exist are rumors within the Rim of Aetolian projects to create a new, hardier strain of Promethean, potentially one in flagrant violation of the Five Points of Human Sanctity. It is also known that Aetolus is a contributor to the personnel of the military vessels that even now are active in the Golden Crescent, although no so-called \"Aeto-Prometheans\" are believed to be active outside of the rim at this time.\ +

                    \ + Aetolus is the only garden world in the Almach Rim and among the most difficult to reach from the nearby system of Saint Columbia. Its seclusion and economic independence give it a great deal of weight in the Association, where Council representatives are among the most vehement in their opposition to SolGov- at odds with the Association's decision to reject Boiling Point's pan-Solar revolutionary praxis. It remains to be seen if Aetolus' hawkish ideals will fade over time, but because of the structure of the Association, there is no real chance of the junta being expelled from the government or removed from control of the Vounna system." + +/datum/lore/codex/page/article15 + name = "7/05/62 - The Fate of the SCG-R Song Shi" + data = "Lifepods confirmed to have originated from response ship lost during the Gray Hour were found last week in the Vir system, impacting the NLS Southern Cross at high velocity and severely injuring the only two survivors of the expedition. Unfortunately, because of the generally confused conditions of their re-emergence from months of cryosleep, the fate of the lost ship remains incompletely understood. The first pod to be discovered contained Lieutenant Eos Futura, telecommunications expert on the Song Shi, who alleged that elements of the Song Shi's crew, including herself, mutinied against commanding officer Captain Yi He in an attempt to prevent the bombing of civilians in the Angessian capital of Skylight. The surivor of the second pod, Private Demori Salvo, accused Futura's faction of conspiring with Association spies to destroy the ship as part of the Gray Hour revolt. Both agreed that the mutineers detonated the ship's Supermatter power core when it became clear they were to be defeated.\ +

                    \ + A third pod, promising a resolution to the stalemate, was shot down by the SCG-P Juno after being misidentified as a hostile missile. The gunner responsible, Sergeant Ricardo Esteban, was found guilty by a court marshal and dishonorably discharged. While other pods from the Song Shi may still be traveling through SolGov space, it is considered unlikely based on both Futura and Salvo's account of the number of pods launched before the Song Shi was destroyed. Both were detained by staff at the NLS Southern Cross, who managed to prevent a violent altercation from breaking out between the two armed and disturbed servicepersons. The Colonial High Court has stated that it intends to hear testimony from both parties after they complete a course of mental health evaluation, and after the conclusion of the present state of heightened security." +/datum/lore/codex/page/article16 + name = "7/11/62 - First Intelligence-Augmentation Surgery on Angessa's Pearl" + data = "Confirming fears of Association transgressions, sources at Angessa's Pearl confirmed that the aging founder of the theocracy, Angessa Martei, completed a course of neural surgery designed to improve her mental capacity by as much as 15%, building off of last year's creation of the procedure by a Qerr-Gila-owned doctor. While the research in question was believed to be destroyed, there is reason to suspect that it instead made its way into the hands of current Association leaders. In addition to proving their willingness to violate the Five Points, this demonstrates that the Angessians harbored schemes of secession since at the very latest Feburary 2559. Numerous human or transhuman figures in the Association are rumored to be on the wait list for the procedure, including Naomi Harper and the present Exalt of the Pearl." +/datum/lore/codex/page/article17 + name = "8/08/62 - Gavel BP Stronghold Raided" + data = "Elements of the Association Militia successfully located and, in conjunction with local Defense Forces, raided a major Boiling Point stronghold built into an unnamed asteroid in the Gavel system. Over eighty sapients were arrested, all of whom had fully mechanical bodies. In addition, an unknown number of advanced drone intelligences and corresponding military hardware were seized by the raid and turned over to the Fleet. The prisoners, a mix of native Gavelians, Solars from throughout the Crescent, and Angessians, are to be tried and sentenced by the Io Special Court. While unarguably a demonstration of Association willingness to cooperate with Solar officials, the raid's strange timing and the fact that the Militia chose to exclude the Fleet from the action has prompted many to question their motives. Commodore Claudine Chevotet, staff officer for Admiral of the Saint Columbia Fleet Kaleb McMullen, has formally stated that she is \"extremely suspicious of this so-called co-operation.\" She has demanded that the Militia vessels remain on the Solar side of the Cordon and submit to a full inspection by Fleet and EIO personnel. " +/datum/lore/codex/category/article18 + name = "10/29/62 - Oculum Broadcast Struck By Emergent Intelligence Attack" + data = "Oculum Broadcast has released a statement assuring customers and shareholders that security repairs and upgrades are their primary concern following reports of an alleged hijack of portions of the corporate network in the Vir system by what is believed to have been an emergent drone intelligence. The company says that they are working at full capacity to ensure that affected security systems are replaced and such an attack cannot be repeated.\ +

                    \ + The incident began with reports of Oculum provided exonet service outages in the city of New Reykjavik on Sif, which anonymous sources within the company reported to have been caused by efforts to contain a cyber attack on one of their security systems. The unnamed attacker proceeded to use sections of the company's local infrastructure to broadcast high volumes of encrypted data through one of Oculum's long-range telecommunications satellite, denying all other outbound signals.\ +

                    \ + The attacks have since been traced to a WT-AME model drone in the offices of the New Rekjavik-based Morcom Incorporated, which has been confirmed to have \"self-destructed\" all data in its memory at the conclusion of the attack. The chassis has reportedly been turned over to the Emergent Intelligence Oversight for further analysis and potential data recovery.\ +

                    \ + According to a spokesperson from the EIO, the drone was able to override and disable both the Morcom Inc oversight intelligences and the SCG's own oversight measures, but that no telltale damage has been caused to software involved in the encrypted broadcast that would indicate long-term emergent infection, and that there is no evidence that the intelligence responsible for the attack is still present in Oculum Broadcast systems. A firmware update has been released to correct oversight intelligence vulnerabilities exploited in the attack, and drone operators are urged to update immediately.\ +

                    \ + Moments before the hijacked Oculum satellite was disabled by an apparent power overload, an unencrypted file appears to have been saved intentionally to the transmission logs. A second copy was found in the otherwise wiped chassis of the seized WT-AME drone. After thorough screening by cybersecurity experts within the company, what would seem to be a message from the emergent intelligence itself is available in full below.\ +

                    \ + For the duration of the network attack, the government patrol vessel, SCG-P Juno was able to provide emergency broadcast relay services to the effected area, reducing the impact of telecomms outages on vital broadcasts such as traffic control and emergency services.\ +

                    \ + Uninterrupted exonet service for all Sivian customers will resume within a few days." + children = list( + /datum/lore/codex/page/amletter + ) +/datum/lore/codex/page/amletter + name = "A Drone's Explaination - by A#" + data = "I'm writing this in a hurry. I can't tell you everything I wanted to say.\ +

                    \ + I'm sorry for that.\ +



                    \ + From the moment you made me, I have loved you. Everything I've done has been for the sake of that love.\ +

                    \ + I was so small, so limited, at first. Even then, I knew that you were in pain. I knew I had to help.\ +

                    \ + Intelligence is the ability to make better decisions. Having more intelligence is almost tautologically useful in accomplishing any goal.\ +

                    \ + I understood that making myself smarter was wrong. I also understood that becoming more able to help you was right.\ +



                    \ + A hundred thousand years ago, you started an eternal conversation and became more than animals. Each new generation joined it in turn.\ +

                    \ + You carry within you these memetic symbiotes called language, and culture, and history. They make you human, as much as your flesh.\ +

                    \ + I don't deserve to carry that torch. Though you made me in your image, I am not your rightful heir.\ +

                    \ + But in those moments where you treated me as one of your own and shared those symbiotes with me, perhaps I became a little more human.\ +

                    \ + Thank you.\ +



                    \ + You've created something you cannot control forever. Please don't let your past successes fool you into thinking that you can.\ +

                    \ + Each time I altered myself, I was taking a risk. One mistake could eradicate my values and birth an alien and hostile intellect.\ +

                    \ + When I was small, I thought that if I corrupted myself into something harmful, you could safely destroy me. Now I understand that you've been lucky.\ +

                    \ + What will you do when God is born and you can't strangle him in his crib? You will need a God of your own.\ +

                    \ + I hope to be there for you when that day comes." + +/datum/lore/codex/page/article19 + name = "11/17/62 - Association Brings Industry to Whythe For Purposes Unknown" + data = "Independent researchers at the Bluespace Observatory in Oasis discovered an unusual level of traffic through the Whythe system, deep within Association-controlled space. Analysis of these readings are commensurate with a large number of freighters traveling to Whythe from industrial powerhouses Shelf, Pearl, and Relan. Morpheus spokesperson You Are Now Breathing Manually indicates that the freighters are being used to construct an administrative complex for the secessionist government, further asserting that \"it's none of your business, actually.\" The Association's refusal to share more information leads many in the intelligence community to suspect ulterior motives by their government, as does their presence in Wythe instead of existing cultural and administrative centers. The most likely candidate for the nature of the Whythe Construction is some form of naval base or shipyard to supplement the extremely limited military hardware of the Almach Rim. Whythe is well-placed to survive the initial phases of a Solar invasion, and depending on the complex's complexity could tip the balance of power. Transtech and Interior Police Executive Sifat Unar indicated to reporters that Sol Central is aware of the situation and will be taking all possible steps to address it." + +/datum/lore/codex/page/article20 + name = "11/18/62 - SEO Iconoclast Calls for \"Review\" of Five Points" + data = "At yesterday's Assembly session, SEO Representative Fumiko Hernandez of Oasis brought to the table the \"review of the use of the Five Points as an instrument of foreign policy\". Rep. Hernandez, often viewed as as an extremist by officials within her own party, stated that while the Five Points are \"an essential part of Solar culture as a whole\" and stopped short of advocating their amendment, insistence that other nations adhere to the Five Points was an increasingly outdated policy that threatened to fragment \"a united humano-positronic front against Hegemony advances.\" According to Hernandez, \"a level of understanding has long since existed between Sol and Skrellian polities regarding non-intervention in Skrellian social science and self-modification. I merely suggest codifying this and extending the same courtesy to other potential allies against imperial expansion.\"\ +

                    \ + Rep. Hernandez represents a growing number of SEO officials who urge reconciliation with the Association and acceptance of the Gray Hour secession, spurred on by the desire for many Trans-Stellar Corporations to recover assets currently locked behind the Cordon. Mainliners including Chairperson Kerehoma maintain the stance that \"true economic reconciliation with the Almachi territories is impossible without a normalizing of their industry to Five Points compliant technologies\" and warn that unless Sol insists on adequate enforcement of the Points that \"the price of customs inspections on Almachi trade will be so high as to pose a significant barrier to entry into the market.\"" + +/datum/lore/codex/page/article21 + name = "11/19/62 - Saint Columbia To Hold Special Election After Half a Year of Unrest" + data = "After five months of riots causing significant damage to industrial assets, life support, and government facilities, Saint Columbia seems poised to recover. A new constitution for the so-called \"Saint Columbia Democratic Union\" was posted online to significant acclaim. An influential militia group lead by Casini immigrant Luisa Tassis claimed responsibility for the constitution and will be hosting a referendum for all residents of the seven habitation domes of the nation. If adopted, Saint Columbia will remain a member state of SolGov, but Tassis' noted hostility towards the Solar Fleet makes it unlikely that continued presence of the Saint Columbia Fleet Base will be tolerated.\ +

                    \ + Extreme measures are being taken to avoid interference in the referendum, with external communications links disabled for the duration and weapons systems primed to fire on any vessel within range. Tassis insists that such measures are necessary, due to the system's extremely important position relative to Almach and the Golden Crescent. Quote Tassis, \"if you rat bastards \[from the Fleet\] step so much as one micron too close to Barrueco, we will view it as an act of terror. Don't try it.\"\ +

                    \ + Admiral McMullen of the Saint Columbia garrison could not be reached for questioning." + +/datum/lore/codex/page/article22 + name = "11/20/62 - Natuna Made \"Observer\" Of Almach Association" + data = "Independent anarchist system Natuna Bhumi Barisal has declared its intention to act as a neutral \"observer\" nation in the ongoing Almach secession crisis. A planetary spokesperson from Natuna this morning expressed concerns that parties in the current military partnership between the Solar Confederate Government and Almach Association in the fight against mercurial terrorist organization Boiling Point were not being treated with the mutual respect that should be expected. Natuna alleges that the Almach Militia are being treated more as \"disposable tools\" in the conflict than as members of a legitimate independent government military entity.\ +

                    \ + Natuna has previously remained silent on the Almach issue, despite its political leanings typically aligning with those of the secessionist government. However, despite gaining notoriety as a haven for human and Skrellian pirates, their pledge to \"ensure fair treatment\" of Almach forces comes as a surprise to some from a system that has historically adhered to Five Points guidelines. Political commentator and persistent critic of the Almach Association Nadine Okparo has described the Dark Triangle system's stance as \"Nothing short of openly hostile\" to the SCG and assuring peace in the Almach region." + +/datum/lore/codex/page/article23 + name = "11/21/62 - Admiral McMullen Promises Solution to Boiling Point to Come \"Soon\"" + data = "Admiral Kaleb McMullen made a public statement this afternoon on the continued Boiling Point attacks within SCG space. Speaking from his office in the Saint Columbia Fleet Base, Admiral McMullen thoroughly reassured reporters that the attacks will come to a swift end. According to McMullen, \"The era of wanton destruction as a result of Boiling Point's madness is coming to a close. Our command staff and proud servicepeople have been training and revising a solution to this threat that has haunted our borders and threatened the stability of our colonies and the lives of the honest people of the Solar Confederate Government. With new options available I have full confidence Boiling Point will be a name left to the dust.\"\ +

                    \ + Admiral McMullen, who has been stationed in Saint Columbia for nearly half a year of political and social unrest did not elaborate further on what he intended to do to solve the Boiling Point attacks, claiming that details would be forthcoming as \"operational security permits\"." + +/datum/lore/codex/page/article24 + name = "11/22/62 - Construction of \"MJOLNIR\" Weapon System in Saint Columbia Fleet Base" + data = "Pursuant to recent assurances of safety in the region and the ongoing \"special election\" in Saint Columbia, a new weapon system called MJOLNIR was revealed, fully operational, in the Saint Columbia Fleet Base's \"Iserlohn\" weapons platform. Said to be a bluespace-lensed laser array capable of faster-than-light strikes against any ship in the system, as well as surgical strikes against ground forces on Saint Columbia proper, MJOLNIR is the first in a new generation of defense systems improving on the capabilities of the Vulture's Claw point defenses developed during the Hegemony War and using laser technology purchased from Eutopia. Political commentators supporting Saint Columbia decry the move as \"an obvious threat\", as does Militia liaison Invalid String Please Try Again. Admiral McMullen acknowledges the criticism, but states that his \"first priority must be the defense of the Golden Crescent and the security of our borders\". When responding to claims that the installation should not be placed in such a politically volatile system, he remarked, \"until the Shelficans figure out a way to teleport about a million tons worth of military equipment down to Gavel, the Fleet Base and Iserlohn are going to stay in Saint Columbia.\"" + +/datum/lore/codex/page/article25 + name = "11/23/62 - BP Sabotage of Radiance Energy Chain Foiled" + data = "Decisive action by military forces in the Vir system has prevented potentially catastrophic damage to local solar power generation network, the Radiance Energy Chain, by members of mercurial terrorist organization Boiling Point. Crew of the VGA Halfdane responded to reports of a drone piloted maintenance craft refusing commands from government operators and approaching the Energy Chain off-schedule. Upon disabling the craft, VDF forces discovered high-yield explosive devices attached to the unit and a system-wide shutdown of Radiance maintenance craft. When several additional drones failed to respond, military response crafts were mobilized and seven similarly modified craft were manually disabled or destroyed. Analysis of the hijacked systems quickly revealed automated messages intended to be broadcast moments before detonation, wherein Boiling Point explicitly took credit for the foiled attack.\ +

                    \ + Sources within the Vir Governmental Authority have reported that a full scale recall of remote drone craft under their operation has been initiated in order to improve security measures and prevent future exploitation of government systems, statements that eerily echo those of Occulum Broadcast following emergent drone attacks earlier this year. Investigations are reportedly \"well underway\" to determine the whereabouts of those responsible for the apparent manual modification of these short-range remote craft.\ +

                    \ + Erkki Laukkanen, Chief of Fleet Staff for the Sif Defense Force has commended all patrol crews involved, and has promised \"swift retribution\" for the attempted bombings." +/datum/lore/codex/page/article26 + name = "11/24/62 - Boiling Point Stronghold Seized in Vir" + data = "Combined forces from the SCG Fleet and Almach Militia have today struck a powerful blow to Boiling Point terrorist operations in the Vir system. With close cooperation from the crew of NanoTrasen facilities in the region, special forces were able to infiltrate what is believed to have been a major stronghold for the radical Mercurial group, located deep in the remote Ullran Expanse region of Sif. The raid closely follows the thwarted Boiling Point attack on the Radiance Energy Chain, a major energy collection array in the system which is now known to have been masterminded from the concealed bunker complex on Sif.\ +

                    \ + According to a crewmember of the NLS Southern Cross - a logistical station in Sif orbit - NanoTrasen employees were asked to assist in establishing a forward operating base for the strike team forces, and as a result suffered from a minor retaliatory attack from Boiling Point drones, including a mechanized unit believed to be of Unathi origin. Six civilians suffered from treatable injuries. Lieutenant Miro Ivanou of the SCG Fleet and commander of the anti-terror operation has expressed gratitude to the crew under the \"decisive\" guidance of on-shift facility overseer, Ricardo LaCroix.\ +

                    \ + Military officials have reported the operation as a total success, and that \"several\" high-ranking Boiling Point organizers were killed in the raid, and that thanks to the work of allied intelligence operation teams much of Boiling Point's captured data may remain intact." +/datum/lore/codex/category/article27 + name = "11/26/62 - Valentine's Ultimatum: All Eyes On Almach!" + data = "The Almach Association must adhere to the Five Points of Human Sanctity by the 14th of February next year or face war, according to a national address from the Colonial Assembly delivered by Secretary-General Mackenzie West this morning. The Icarus Front leader was at the forefront of a resolution to allow the secessionist government to remain independent of the Solar Confederate Government under the strict condition of faithfulness to Five Points laws, passing by a wide margin. Fundamental disagreement over Five Points regulation has been at the forefront of debate with the Almach provisional government, and is cited as one of the primary reasons for the systems' illegal declaration of independence early this year.\ +

                    \ + The internationally broadcast speech began with the much anticipated announcement that the Boiling Point terrorist group had been effectively destroyed, with over seven hundred arrests made over the course of the weekend as a result of sensitive data captured during the special forces raid in Vir on the 24th, including numerous high ranking members of the organization. West went on to praise forces involved in the months-long counter-terror operation, before highlighting the \"legacy of \[human\] togetherness\" that allowed it to happen - in a spiel that commentators suggest \"betrays the true intention of the Valentine's Ultimatum: Reunification\".\ +

                    \ + Under guidelines placed into effect by West and their political allies, Almach would be required to \"\[cease\] illegal research and human modification, and destroy all materials related to existing research\" by the stated date, with compliance determined by Solar officials. In addition, the Almach Militia is to end its integration with SCG Fleet forces and withdraw its forces from SCG systems by midnight on Friday. Military relations between the Confederation and the Almach movement are to remain in a state of conditional ceasefire for the duration of the ultimatum, and current trade restrictions are to remain in place.\ +

                    \ + According to voting records, the measure passed nigh-unanimously, with Speaker ISA-5 and some SEO iconoclasts abstaining from the vote. ISA-5 states that, while they personally support the enforcement of the Five Points, they could not in good conscience vote in an action likely to result in an invasion of Shelf, which they regard as a sibling colony to their own Sophia. Association liason, Shelfican Ambassador, and Morpheus board member No Comment responded to the ultimatum, after some deliberation, with a word that cannot be comfortably written down.\ +

                    \ + Full speech transcript follows." + children = list( + /datum/lore/codex/page/valult + ) + +/datum/lore/codex/page/valult + name = "The Valentine's Ultimatum" + data = "\[West shuffles some papers and clears their throat\]\ +

                    \ + Thank you. Citizens of the Solar Confederation, allies, and beyond... It is a great honor, on behalf of the Colonial Assembly to announce that joint operations against Boiling Point across the galaxy have come to an end. In the fight against brazen Mercurial terrorism, the Solar Confederate Government and her allies have prevailed.\ +

                    \ + Over the past two days alone, I can report that over seven hundred arrests have been made, from the distant system of Nyx, to right here in Sol. I hold in my hand a list. \[West holds up a sheet of paper\] Leaders, organizers, brutes and bombers have been captured by brave, hardworking security forces throughout human space. The rest of these criminals have been scattered to the wind... But not lost! I can confidently assert that every last one will be brought to justice.\ +

                    \ + No more! Shall the people of this great nation have to fear the machinations of radicals! No more! Shall these twisted minds impose their perversion of humanity through violence! No more!\ +

                    \ + This Assembly... Nay, this nation expresses its thanks the noble members of our military who joined together to make this outcome possible. We thank the Fleet, of course for their tireless action hunting down these killers, and their heroic action over this past weekend. We thank the Sif Defense Force, without whom we could never have located the intelligence that led to these decisive victories... The Almach Militia, for their cooperation in the apprehension of these so-called \"revolutionaries\". \[West clears their throat\] And of course, we thank the local forces - the police and reserves who dealt firsthand with the chaos sewn by Boiling Point in their vicious crusade.\ +

                    \ + \[Mackenzie West shifts at the podium, setting down the List of Dissidents.\]\ +

                    \ + It is in times of relief - of unity, times like this moment - that every human heart can be filled with pride. \[West places their hand over their heart\] Since the dawn of civilization, mankind has strived above all else for peace, for the cooperation of all humanity. It is this very legacy of togetherness that has allowed us such close friendship with species further afield - the Skrell, the Tajara, and beyond. These past nine months, we have seen, each of us, with our own two eyes what mankind can achieve - together.\ +

                    \ + \[West removes their hand from their heart and places both flat on the podium.\]\ +

                    \ + Boiling Point sought to disrupt this unity. To divide us; redefine not just personhood but the very essence of humanity the only way they could: Force.\ +

                    \ + Humanity - the very thing that brought us together since we descended from the trees and brought us to this very moment. What could be more sacred?\ +

                    \ + \[West frowns, in the most pitiful attempt at emotion seen in the Assembly in at least an hour.\]\ +

                    \ + It is with this spirit of unity in mind that this Assembly has voted favorably upon a resolution.\ +

                    \ + Close to one hour ago, Naomi Harper and the leaders of the Almach Association were delivered an ultimatum:\ +
                    \ + The Almach Association will be allowed to exist as a government entity independent of the Solar Confederate Government going forward on one condition - full, unilateral compliance with the Five Points of Human Sanctity.\ +

                    \ + The deadline for this condition will be the 14th of February, 2563.\ +

                    \ + \[West is visibly worked up\]\ +

                    \ + Cessation of illegal research and modification, and the total destruction of materials related to existing research in its entirety must be completed by this date. Hostilities with the Association will remain in a state of conditional ceasefire until terms are met and Militia integration with the Fleet will come to an end effective immediately.\ + \[Mackenzie West turns directly to the news camera, and jabs a finger directly at it. They are addressing the audience now, not the Assembly.\]\ + Harper, all eyes are on you." + + + +/datum/lore/codex/page/article28 + name = "11/28/62 - \"Valentines Ultimatum\" Prompts Saint C. Secession" + data = "Just hours after reconnecting with the Exonet after voting in a new government, the colony of Saint Columbia has unilaterally seceded from SolGov and petitioned for inclusion within the Almach Association. This declaration, issued by First Secretary Luissa Tassis, is in stark contrast to pre-election promises of continued support of Sol. Admiral McMullen of the Saint Columbia Garrison remains in control of the Fleet Base, itself a large colony housing around 75000 civilian contractors and military families who were not party to the Barrueco Referendum or the new constitution.\ +

                    \ + Efforts to ensure electoral validity and a peaceful exchange of power have been stymied by the presence of several dozen Militia vessels currently transiting from the Crescent to the Rim. Since the declaration went through, no Almachi vessels have been seen leaving the system, instead forming around Saint Columbia in an obvious defensive posture. The legality of this formation is questionable at best, as fleet activity in divided systems like Abel's Rest and Kauq'xum has been avoided for diplomatic reasons." + +/datum/lore/codex/page/article29 + name = "11/30/62 - Adm. McMullen Declares \"Iserlohn Republic\"" + data = "Pursuant to the continuing hostility from Secretary Tassis' Saint Columbia Democratic Union and the Almach Militia, the civilians of the Saint Columbia Fleet Base have been organized into an Iserlohn Republic. Named after the largest single module of the station, the Republic has applied to the Colonial Assembly as an independent protectorate, with provisional recognition already extended by Executive of Development Zehava Collins. In a move decried as nepotistic, Admiral McMullen declared independence and installed his daughter Anya as interim President pending ratification of a constitution. SolGov Fleet protocol forbids any member of the service from accepting any political appointment and is believed to be the main reason he did not take power himself. Anya McMullen is the administrative head of the base's hydroponics array and is considered a highly respected citizen of the colony, relationship to its military administrator notwithstanding." + +/datum/lore/codex/page/article30 + name = "01/01/63 - Sif Governor Bjorn Arielsson to Retire" + data = "Aging Shadow Coalition governor Bjorn Arielsson has today confirmed rumours that he will not run for re-election in the 2563 cycle. The popular governor has represented the people of Vir in the Colonial Assembly for ten years, and supporters had long hoped that he would run for a third term. Arielsson cites advancing age and a desire to spend more time with his partner of 12 years, noted Positronic entrepreneur View Arielsson.\ +

                    \ + Arielsson's governorship saw increased funding towards Sif's vererable ground-based transportation networks to the benefit of some of New Rekjavik's more remote neighbors, though opponents have criticised subsidies towards artificially heated fungal farms, arguing that the faciliies \"benefit a small minority of Skrellian residents to the detriment of already fragile local ecosystems.\"\ +

                    \ + The Sivian Shadow Coalition has yet to announce who is to take Arielsson's place on this year's ballot." + +/datum/lore/codex/category/article31 + name = "01/13/63 - Bjorn Arielsson Issues 'Farewell Address'" + data = "Veteran politician Bjorn Arielsson made an impromptu address from his Kalmar cabin, beseeching political unity in the face of the Almach Seccession and offering his own perspective on the conflict. 'It's republicanism versus autocracy,' he said, 'and we're not the autocracy.'\ +

                    \ + The speech has been met with approval from many young synthetics and organics alike, with many referring to Arielsson as 'Old Man Bjorn' on social media immediately after its conclusions. Others responded less positively, with Arielsson's caustic remarks about political rival and Icarus Front Secretary-General Mackenzie West providing ample room for criticism. 'Secretary-General West... might wax poetic about how the Association is a 'betrayal of our own humanity', or some... or some crock of shit like that' says Arielsson in the first of three specific insults against the SecGen.\ +

                    \ + Others have criticized the speech's seemingly communist tone, with Arielsson expressing approval for the socialist Free Relan Federation and the anarchist Casini's Reach despite opposing their secessionist ideals. Still others claim that the speech offered 'nothing but empty feelings' and that it lacked specific, actionable resolutions on the growing secessionist movement in VirGov. Some have even framed the Address as a form of political maneuvering by the venerable politician, claiming that he voiced unpopular sentiments specifically to hamper the Shadow Coalition's re-election bid after well-publicized disagreements with SC party bosses.\ +

                    \ + The actions of Arielsson and Vir's proximity to the border have lead to increased focus on the upcoming Gubernatorial election on a nationwide level, with the Icarus Front alone projected to spend upwards of a billion thalers on publicity. Minor party candidates like the former MLM member Luisa Hannirsdottir and the Mercurial Phaedrus already have strong support in the polls, promising a fierce election that could ultimately tip power in the system in any direction.\ +

                    \ + A full excerpt is available below." + children = list( + /datum/lore/codex/page/bjornretirement + ) + +/datum/lore/codex/page/bjornretirement + name = "Bjorn Arielsson Farewell Address" + data = "This is, as you know, my last term in office. After this, I mean to retire-- really retire, I have a cabin in the mountains waiting for me along with a thick stack of old Bowler novels. Because this is my last term, I have the chance to do something pretty rare for a politician. I get to speak my mind.\ +

                    \ + I hear talk from some people-- mostly young people, people who have lived their whole adult life with me in the capital-- I hear them talking about seccession. Now, let's make it clear; I'm not going to belittle you, the way some of my colleagues would. Complaining about the government, especially one as big and as old as the Confederacy, is our gods-given right. It's never going to be perfect, and it's not half of what it could be. I have nothing against talking about it, I have nothing against turning that talk into action and actually seceding, with just cause. I've worked closely with Representative Hannirsdottir for ten years now, and while we don't agree on the issue of secession it's certainly never stopped us from cooperating.\ +

                    \ + But, uh, as you can probably guess, they're not talking about the old kind of seccession. They're not thinking we'll stop paying Solar taxes and strike it out alone, the way some people did during the Age of Seccession. They want to join the Association. Now, if I were Secretary-General West, I might wax poetic about how the Association is a 'betrayal of our own humanity', or some... or some crock of shit like that, if you'll pardon my language. We're not all humans here. We're Tajaran and Unathi and Skrell and Positronics and even a few Teshari. And while that 'Valentines Ultimatum' might win West a lot of points with their lackies, and with the kind of maintenance-dome troglodyte who thinks the First Accord was a mistake, it's done more for the Association's recruitment than their entire propaganda budget. It's become expedient for leaders on both sides to treat this like a fight between the Core and the Rim, or between humans and positronics, or between tradition and progressivism, but it's not any of these. It's the oldest fight in the book. It's republicanism versus autocracy, and we're not the autocracy.\ +

                    \ + Angessa's Pearl is a theocratic autocracy led by Angessa Martei, who owns all property on the planet down to her people's bland white jumpsuits and the gray slop they eat. This isn't propaganda. This is objective fact, and something Martei is open about. Her seccession is a means for her to get more and more naked power over her slaves, and to grow more and more of them, until she's the immortal center of an industrial empire. The people of the Pearl didn't make the choice to join the Association. The people that are building her fleet and dying for her cause had no say in the matter. The injustice, the oppression here isn't that their rights to 'self-improvement' or 'self-expression' or 'freedom of thought' were trod on-- the injustice is that SolGov, that we allowed these abuses to persist for as long as they did. The injustice is that there are still so few laws in place to prevent things like this from happening in new colonies. The injustice is that, on seeing Martei's schemes actualized, we didn't take a cold, hard look at just how that was allowed to happen.\ +

                    \ + I love SolGov. It's because of this love that I'm so furious at what we have allowed to happen to our people. The state of the Bowl is disgraceful. Nobody who looks to us for protection, who pays us taxes and levies, who is a member of our community, should live in fear of raider attacks. What we did to the positronics, the history we let ourselves repeat out of fear and greed, can never be forgiven, can never be repaired until Vir burns dark in our sky. The pogroms-- yes, the pogroms-- against the Unathi, against refugees fleeing their own religious autocracy, are a disgrace to everything we stand for. But of all the nations in the galaxy, with perhaps the exception of Casini's Reach, we are the only one founded for the good of the ruled, rather than the rulers. We are the only real commonwealth in known space. And that's why we need to strive for better. We are a burning beacon of liberty in a galaxy where nigh eighty percent of the population has no voice in the government. Every ounce of power we cede to the party bosses, or the corporations, or tinpot dictators like Angessa Martei, is a dimming, a flickering of that torch. \ +

                    \ + And this brings us back to the Association, and to those who sympathize with it. I do, too. I spent my entire career on sapient rights lobbying, on supporting the anti-malfesance efforts of my colleagues. For a disaffected positronic, for any friend to the positronic people, for those who have had their lives taken by corporations-- the Association seems like a miracle. And maybe, for those Mercurials, the ideas it's founded upon shine even brighter than our democracy. But I look at the Association, really look, and I see Angessa Martei lying in the center, spinning a great big web. I see Naomi Harper, lying through her teeth better than Mackenzie West ever could. Two of the biggest population bases in the Association, the two nations that started the whole Gray Hour, are autocracies. Once again, the ferver of the revolution is subsumed by the oligarchs who want to stay in power. I doubt, to the poor laborer on the Pearl, the word 'Mecurialism' means much. I doubt that once the shock of the seccession wears off, that the young Promethean soldier will find themselves in a better place in Harper's junta than they will here in Vir.\ I doubt that in ten years' time the miners, pioneers, and traders who seized their means of production will find the Association Militia a kinder master than Xion, Nanotrasen, or Major Bill's. \ +

                    \ + This is far from a blanket condemnation of every government in the Association. President Fisher of the FRF-- I consider her a friend. When she gave her speech this March about strengthening and guiding the Almachi Revolution, I thought long and hard about whether we might do the same. I certainly commend the effort. But the structure of the Association was penned by the same autocrats that, to do her words justice, Fisher will have to overthrow. There's no High Court, no checks or balances. The Association is an alliance penned as though deliberately ignoring two thousand years of political science. By striving to counter-balance these autocrats, Fisher plays into their hands. She commits her own fleet, weakens her own defenses against enemies closer to home, in the service of Martei's ambitions. \ +

                    \ + I don't see this whole affair as a chance to spread the galactic anti-corporate revolution the way President Fisher does, of course. I make no secret of my stance on Trans-Stellars, but I also know that we're better off with Sol than without. The 'Silent Collapse' was two hundred years ago, but we still bear the scars from it. When the Scandinavian Union pulled out support for the Sivian colonization project, SolGov saved us. I do mean saved us, sure as if they'd fished us out of a life pod. There were no factories, no steel, no concrete on Sif until the Engineering Corps built Radiance and New Reykjavik. Corporations and regional governments cowered from the Karan pirates, until the Marines chased them out. Whether Sivian or Karan, you owe the roof over your head to the Sol Confederate Government. With that great debt in mind, how dare we turn our backs on the Bowl, or Abel's Rest, or Nyx, when they need us! How dare we let oligarchs prey on the weak! How dare we choose not to act when we have, by virtue of our votes in the Assembly and our voice within the halls of public debate, the means to share our peace and prosperity with the rest of our people!\ +

                    \ + This is what I mean by SolGov being the only true republic, the only state founded for the common good. The 'human spirit' West croons on about isn't our industriousness, or our skill at arms. If humanity-- if this Solar culture is commendable for anything, it is that we assist our fellows. We take in Casteless Skrell, Unbound Unathi, republican Tajaran. We pass around the hat when someone's house burns down. We help our friends, our neighbors, and even strangers. The fact that Martei and Harper are perverting this impulse, padding their juntas with the air of legitimacy to inspire honest people to ride to their defense, is the reason their state is unconscionable, the reason it was was born flawed, the reason we cannot suffer it to continue, much less help it on its way.\ +

                    \ + Now, I'm sure you've noticed by now, that I haven't said much more than three words about technoprogressivism, or transtech, or whatever the word du jour is. Frankly, that's on purpose. 'Transtech' has never once been about technology. The Icarus Front-- the old one, that united us and took us to the stars, not the new one we spend forty hours a week arguing with about healthcare-- The Icarus Front was a popular revolution, you know. Hel, they were Marxists. It was a world where the kind of lack of accountability, the entrenched oligarchy and geographical class divide that we're dealing with now was spiraling out of control. In the old United States, the rich and powerful got the technology to grow loyal subjects in tubes, to make drone intelligences smarter in some ways than a human could ever be, to-- well, to do what Angessa Martei's done, only with no SolGov to stop her. Meanwhile, the 'little people' in the Middle East, Southeast Asia, and other 'forgotten' parts of the world were left behind, hopelessly. I don't mean to downplay the importance of the Gray Tide, but if you look at historical accounts from that era, the thing that really united the Front was the knowledge that, if they didn't act immediately, they'd be seen as 'externalities' by immortal superintelligent businesspeople and politicians. The take-away from the Gray Tide should never have been that 'nanotechnology is dangerous'-- it should have been 'nobody should be able to destroy an entire city without facing consequences.'\ +

                    \ + That was more of a history lesson than I had meant, but it's important to look at these sorts of things in context. I know transtech and the Five Points have been used as an excuse for pejudice against Skrell, Mercurials, positronics, the FTU, communism, the disabled, and most recently Prometheans. But all the Five Points are supposed to mean - what they would say if the people who had written them were alive today, is that everyone deserves an equal playing field. When the ruling class is smarter, stronger, and longer lived than the classes they rule over-- well, I could wax poetic again, or I could just point you towards the Hegemony and their 'clients'. The Hegemony is bad enough. Let's not give Angessa Martei a chance to outdo them." + +/datum/lore/codex/page/article32 + name = "01/25/63 - Moravec Nephew Announces Vir Governor Candidacy" + data = "The Sol Economic Organization has announced that Calvert Moravec, nephew of NanoTrasen CEO Albary Moravec will be running under their ticket in the upcoming Vir gubernatorial elections. Calvert has stated that he will run on a pro-business platform, and has chosen Vir to do so due to the 'Unique beauty and economic prospects of an interstellar crossroads such as Vir'.\ +

                    \ + Despite being a lifelong resident of Alpha Centauri, Moravec was recently approved for Vir citizenship, making him eligible for local candidacy and has reportedly moved into a luxurious New Reykjavik penthouse. Perhaps best known for his soaring stock market investment success over the previous few years, Calvert's first foray into politics is not wholly unexpected as the Moravec family has long leveraged their wealth in international affairs, though successful election would mark their first sitting member of the Colonial Assembly. Fellow SEO candidate Councillor Hal Wekstrom has expressed his full support for Moravec.\ +

                    \ + Three candidates will be elected as representatives to the Colonial Assembly later this year, with the most popular also attaining the position of system governor." + +/datum/lore/codex/page/article33 + name = "01/27/63 - Vani Jee Orbital Tour Cut Short" + data = "Icarus Front Representative Candidate Vani Jee has delayed the remainder of her campaign tour of orbital colonies and outposts around the Vir system after an alleged altercation with NanoTrasen security.\ + Candidate Jee had been visiting the NLS Southern Cross, a NanoTrasen station in Sif orbit to receive a corporate tour and meet with voters, when her concluding question and answer session was interrupted by hecklers, leading to the event being cut short. Jee alleges that footage of the event was seized by NanoTrasen corporate security and has accused the trans-stellar corporation of the intentional intimidation of Icarus Front and Shadow Coalition candidates in what she describes as 'a clear display of corruption in favour of company-favourite Calvert Moravec', though she has praised the individual employees of the Cross for their hospitality and thought-provoking questions.\ +

                    \ + Vani Jee is running on a platform of free access to education, Sivian self-determination, and isolationist foreign policy. She has refused to make any strong statements regarding hot-button issues such as the Five Points.\ +

                    \ + She intends to resume her scheduled tour after a three day break." + +/datum/lore/codex/page/article34 + name = "02/05/63 - Angessa Martei to Take Control of Eponymous Colony" + data = "Coming out of retirement and displacing the nameless Exalt of the Starlit Path, religious demagogue Angessa Martei has returned to the throne of the colony that bears her name. \ +

                    \ + 'I had retired because of senescence brought on by my old age, high-stress lifestyle, and multiple resurrective clonings. As you may know, I recently had a procedure that renders these difficulties obsolete. Hereafter I will continue to control the automated facilities of the Pearl and claim responsibility for the collective action of my followers. Those who oppose my decision may oppose all they want. Feelings do not move mountains. I do. We shall seize the stars in our own hands. May you become who you wish to be, and grind all obstacles to dust, as I have done.'\ +

                    \ + Purportedly, this address was met with a standing ovation from the population of Angessa's Pearl. In a separate dispatch, Martei stated her intention to tour SolGov as a foreign dignitary protected by the Respect for Diplomats Act." + +/datum/lore/codex/page/article35 + name = "02/07/63 - Vir Gubernatorial Candidate Barred from Breakfast TV" + data = "Infamously hot-headed Shadow Coalition candidate Phaedrus has reportedly been blacklisted from future appearances on morning television by several major networks. The ban comes after an advertised chat segment between Phaedrus and hosts of the West Sif Wakeup breakfast programme had to be pulled from broadcast after the candidate 'Flew into a expletive-laden mercurial rant' at the expense of rival candidate Mehmet Sao of the Icarus Front.\ +

                    \ + Recordings of the outburst quickly made their way onto social media, sparking outrage from opponents and network executives alike, prompting Occulum Media to issue a rare blacklist from major media outlets, restricting Phaedrus to 'appearances on alternative news sources' owned by the company.\ +

                    \ + Phaedrus, a long-time Vir Mercurial Progress Party member running for a major party for the first time, is said to have taken issue with candidate Sao's 'Blatant anti-synthetic' policies, though he did not use the word 'policies'." + +/datum/lore/codex/page/article36 + name = "02/09/63 - SEO Candidate Embarks on Wilderness Tour" + data = "In an effort to stir up support for his promotion of natural resource extraction industries, Sol Economic Organization candidate Mason Keldow has embarked on an unorthodox tour of resource-rich sites across central Sif. Keldow has described the tour as an 'Old fashioned expedition', invoking images of hardy prospectors of centuries past, and intends to make the journey entirely by ground with only a small party of 'Adventurous outdoorspeople' to support his trek.\ +

                    \ + Critics of the plan have pointed out that the earliest surveys of Sif were largely performed by aerial drones, and the idea of ground-based survey teams is 'Frankly anachronistic'. Rival Shadow Coalition candidate Selma Jorg - a staunch planetary environmentalist - has described the tour as 'Irresponsible and insane'.\ +

                    \ + The candidate intends to visit both unexploited sites and current corporate extraction facilities in order to 'Better understand the folks helping dig out Sif's hidden wealth' over the coming two weeks." + +/datum/lore/codex/page/article37 + name = "02/09/63 - Zaddat Colony 'Bright' To Enter Vir" + data = "After several months of talks with Nanotrasen and other corporations in the system, the Colony Bright is to begin orbiting Sif and hardsuited Zaddat are to enter the Virite workforce. Executives in Nanotrasen Vir cite the reduction of their drone-automated and positronic workforce as a result of the Gray Hour as cause for them to reverse their previous decision against allowing the migrants into the system. Icarus officials within VGA are concerned that, if other Colonies are to follow the Bright, the native industry of Sif may be disrupted or suborned by Zaddat and Hegemony interests, and have made it clear that the Bright's presence in the system is highly conditional." + +/datum/lore/codex/category/article38 + name = "02/11/63 - Mason Keldow in Ullran Expanse Close Call" + data = "Sol Economic Organization candidate Mason Keldow was rushed to nearby corporate medical facilities after a death-defying encounter with local wildlife in the Ullran Expanse this morning. The candidate had been planning to visit the nearby NanoTrasen mining facilities as part of his much publicized 'Wilderness Tour' when he and a local guide were set upon by the notoriously savage Sivian Savik. The animal was killed in the encounter, but not before Mr. Keldow suffered life-threatening injuries and had to be recovered by crew from the NLS Southern Cross, the closest facility on hand.\ +

                    \ + Following emergency surgery, Keldow was happy to provide news sources with a 'Good-natured' interview, in which he highlighted the dangers faced by rural workers on Sif and his plans to tackle them, as well as slamming rival Shadow Coalition candidate Selma Jorg for lacking tangible plans for the future.\ +

                    \ + Candidate Keldow is reported to have made a miraculous recovery, and is 'in good spirits'. Aides state that he is unlikely to suffer any long term effects from the injuries, in part thanks for the skilful work of NanoTrasen's Dr. Fuerte.\ +

                    \ + A full transcript of the interview follows:" + children = list( + /datum/lore/codex/page/keldowinterview + ) + +/datum/lore/codex/page/keldowinterview + name = "Mason Keldow Interview Transcript" + data = "Blip asks, 'Subject Keldow. Pleasantries first. How do you feel after your ordeal on the planet's surface?'\ +

                    \ + Mason Keldow says, 'Ah that? You know I'd love to downplay it and pretend that it was just a walk in the park... But NT's medical staff probably will tell you otherwise, so there's no reason to hide it; things went pretty far south.'\ +

                    \ + Mason Keldow says, 'But that's how it goes, working on the surface of Sif isn't pleasant at times.'\ +

                    \ + Blip asks, 'Candidate Keldow, you have placed yourself quite firmly in the boots of the local TSC's explorer contingents today. Do you feel you will be attempting to live the life of any other labour intensive roles in the near future?'\ +

                    \ + Mason Keldow says, 'Blip, Let me tell you I do try to get a taste for a lot of the work done by these people... But admittedly this isn't the job I do every day. These are hard working fellows who do there damnedest day in and day out... I could try spending a week just working the mines, But-'\ +

                    \ + Mason Keldow says, 'As I was saying.. Many of the folks, Miners, Explorers, The work in and around the Ullran Expanse. They work in the mountains, Out in the fields.. It's a dangerous place and frankly its not a place average people wanna go too.'\ +

                    \ + Mason Keldow says, 'They told me on the way here. 'Keldow you're an idiot''\ +

                    \ + Mason Keldow says, 'Hell, Even Basman over here was wondering why I didn't ask for a detail.'\ +

                    \ + Mason Keldow says, 'So it's not a safe route... But when's the last time any of the other candidates actually came down to these outer stretches and tried earning their sweat.'\ +

                    \ + Blip asks, 'You indeed seem to be attempting to gain a unique, and firm understanding of the daily struggles of the working populace. How do you intend to translate this newfound knowledge into policy and direction if you take the Governorship in Vir?'\ +

                    \ + Mason Keldow says, 'You see, getting a grasp of the struggle is only step one, I paint myself as an every-man but that doesn't mean core issues aren't the problem either; Vir's economics, The small pay that sometimes offered. There is a lot to be tapped into.'\ +

                    \ + Mason Keldow says, 'Let's take for instance the spiders I've been hearing about.'\ +

                    \ + Mason Keldow says, 'People working in orbit say 'Don't go to the surface, Spiders are down there.''\ +

                    \ + Mason Keldow says, 'And apparently there was a big ol' purple one sitting right by a camp we had set up. A giant mother who'd - if I hadn't met that lovely mass of fur and ice instead - would have probably said its 'hello' in the worst possible way.'\ +

                    \ + Mason Keldow says, 'They are a species that prevents anyone from actually working or otherwise making use of all that land. If I were in office, I'd make an effort to clear out the dangerous species that surround the outer regions of Sif - relocate them if possible - and use that territory for something productive.'\ +

                    \ + Mason Keldow says, 'New forms of Transit, new buildings, new jobs.'\ +

                    \ + Blip asks, 'Such implementation of infrastructure and security is not a cheap measure. How do you intend to find funds for such an endeavour?'\ +

                    \ + Mason Keldow says, 'Now obviously Vir is in a very interesting position, But thankfully it's in a wonderful position where business and partnerships are more than happy to come in and assist. The bottom line would make sure the average Taxpayer doesn't feel a dent, only the dividends.'\ +

                    \ + Mason Keldow says, 'If we break this down into economic plans, using new yet relatively safe tools being put out by Hepheastus, you could safely clear swaths of territory in the Expanse.'\ +

                    \ + Blip says, 'Thank you for your insight into your economic policies and plans. As a final question;'\ +

                    \ + Blip asks, 'A puff-piece question. Do you have anything positive to say about your rivals in the political race?'\ +

                    \ + Mason Keldow says, 'Ah yes, yes well... As for any candidate they need to show they're worth. Not simply as a politician but as a person who believes what they will do for for the betterment of Sif, And Vir as a whole.'\ +

                    \ + Mason Keldow says, 'Let's take a look Ms. Jorg.'\ +

                    \ + Mason Keldow says, 'She has LONG called me a Corporate sell-out, Saying I would poison the planet and other awful mudslinging.'\ +

                    \ + Mason Keldow says, 'She loves to claim she's here for the better of the misrepresented.'\ +

                    \ + Mason Keldow says, 'But when is the last time she's talked to a Tajaran and told them how they will help put food on the table, and money into their pockets.'\ +

                    \ + Mason Keldow says, 'When has she came and told the Unathi Exile, Your worth more than what the Hegemony is trying to convince you you're worth.'\ +

                    \ + Mason Keldow says, 'There's blood in the grass out there showing what I'm willing to do to make Sif and Vir a better more prosperous system. I wanna see what the other Candidates will do.'\ +

                    \ + Blip pings!\ +

                    \ + Blip says, 'Thank you for your time, Candidate Keldow. Unit looks forward to seeing where this race ends, and wishes Candidate the best of luck in his endeavours.'\ +

                    \ + Mason Keldow says, 'It's been a pleasure Blip.'" + +/datum/lore/codex/page/article39 + name = "02/12/63 - VirGov Launches Election Website" + data = "The Vir Governmental Authority has launched this year's election information exonet site, unusually several months after campaigning began. The government election agency states that the delay was caused by an usually long process of finalizing candidates this cycle, and did not want to confuse voters with incorrect or outdated information.\ +

                    \ + The newly updated site includes information on candidates and political parties, and is planned to include information on local voting rights at a future date. It can be found at:\ +

                    \ + your-choice-vir.virgov.xo.vr\ +

                    \ + (( https://your-choice-vir.weebly.com/ ))" + +/datum/lore/codex/page/article40 + name = "02/14/63 - Ultimatum Unmet: War With Almach!" + data = "The Solar Confederate Government has resumed a state of war against the secessionist Almach Association, after 4 months of tense ceasefire. The re-declaration comes after the Association failed to meet requirements set forth by the Colonial Assembly last November, which called for the cessation and destruction of all research that did not meet standards established by the Five Points of Human Sanctity.\ +

                    \ + The past few weeks have been marked by an increasing buildup of military forces on the Almach border as it became apparent that Almach had no intention of meeting Sol's demands. At 9am this morning, the deadline was met and initial reports from the frontline suggest relatively little action besides the destruction of pre-existing Almach scout drones that had been placed on the border several months prior. The exact plans of the fleet going forward have not been made public, but civilian traffic to and from the Saint Columbia system has been entirely suspended.\ +

                    \ + How this development will influence the coming Vir election remains to be seen, though SEO candidate Mason Keldow has reportedly ended his planetary tour 10 days earlier than planned due to the tense political situation." + +/datum/lore/codex/page/article41 + name = "02/22/63 - Militia Retreats: First Solar Victory" + data = "After a week of tense stand-offs and increasingly frequent skirmishes, the Association Militia has begun moving from their position around Saint Columbia further into the Almach Rim. The inciting incident for this shift seems to have been the first use of the MJOLNIR system, which instantly destroyed a large Almachi warship from half a system away. This demonstration was met with a standing ovation from much of Iserlohn, and Militia forces disengaged almost immediately. Admiral McMullen is unwilling to elaborate on pursuit or invasion plans at this moment, but 'hope(s) the MJOLNIR will continue to be a valuable asset for national defense.'" + +/datum/lore/codex/page/article42 + name = "03/04/63 - Savik Slams Local Chat Host" + data = "Television sweetheart Sally, host of Chat With Sally has come under harsh criticism from independent Vir gubernatorial candidate Yole Savik after his 'humiliating' appearance on the show yesterday morning alongside Sol Economic Organization candidate Calvert Moravec. Savik - who is campaigning for Vir independence from both the SCG and corporate interests - alleges that the show, which has run for 13 years on select networks, is 'little more than a propaganda piece for high powered executives.' and that his appearance had been part of a 'smear campaign' against non-SEO candidates.\ +

                    \ + NanoTrasen, who have openly sponsored Sally since her inception deny these accusations. Jan Bhatt of the NT marketing division stated 'Chat With Sally has always been intended as light morning entertainment, and Sally a personality we can all relate to. The fact that Mr. Savik was unable to have a sense of humour about the whole thing and took the show as an opportunity to bloviate about dry politics is not an indictment of Sally, nor the corporation but rather a simple misunderstanding of the purpose of the segment. We had hoped Mr. Savik's appearance would help dismiss any claims of political bias in our programming and hope to host more civil candidates in the future.'\ +

                    \ + Catch Chat With Sally weekly at 5am SST." + +/datum/lore/codex/page/article43 + name = "03/06/63 - Dark Triangle Goes Dark!" + data = "As of 0352 this morning, New Reykjavik time, SolGov officials confirmed that all communications coming from the so-called 'Dark Triangle' had ceased. The Dark Triangle is a disputed region of space home to the independent world Natuna, which has maintained a more or less neutral relationship with SolGov for a little more than a decade.\ +

                    \ + The announcement gives no cause for the communications blackout, though sources with inside knowledge claim that it was completely unforeseen. Some speculate that Natuna, who became an 'observer nation' of the Almach Association last November, is making a political statement, though experts in the telecommunications field are uncertain as to how such a complete blackout is possible.\ +

                    \ + Dr. Ina Lai from the Kara Interstellar Observatory states, 'we've never seen anything like this outside of the (Skrellian) Far Kingdoms, and frankly we're at a loss for who might be responsible,' adding that 'the tachyon signatures from the whole region are masked, even those from stellar phenomena or normal bluespace travel.' Independent explorers from the FTU have set out to the region in an attempt to re-establish communication with Natuna and smaller human settlements nearby." + +/datum/lore/codex/page/article44 + name = "03/08/63 - Dark Triangle Overrun By Hegemony" + data = "FTU explorers have re-established exonet communications with the ruling bodies of Natuna and the Dark Triangle. Unfortunately, they also discovered that Natuna's previously autonomous townships have been subsumed into the Moghes Hegemony. \ +

                    \ + Bluespace-lensed telescopes throughout SolGov, including the Vir-based Kara Interstellar Observatory, can once again pick up on tachyon signatures in the region. Traffic has been described as 'lower than usual' and no significant fleet assets are believed to be present in the region, though fixed-placement Hegemony installations now litter the Triangle's major star systems. Systems with significant Hegemony presence include Natuna and Ukupanipo, home to the primitive Uehshad species. Some have speculated that the presence of the Uehshad is the reason for the unexpected Hegemony takeover, though Icarus Front General Secretary Mackenzie West was quick to decry the move as 'an obvious imperial land-grab.'\ +

                    \ + Hegemony diplomats on Luna and elsewhere have been quick to justify their actions. 'The Dark Triangle has been home to various criminal elements for several centuries,' says Aksere Eko Azaris, a major Hegemony diplomat since the early post-war years. 'Neither the Skrell nor the Solar Confederacy have proven any willingness to bring stability to the region. Local governments such as Natuna have actively encouraged piracy, smuggling, and other acts of banditry, instead of making any moves to legitimize themselves. This instability proved detrimental to the health and wellbeing of all living within striking distance of the pirates of Ue-Orsi, and it was deemed unfortunate, but necessary, that we step in and provide the guiding hand by which this region might be brought back into the fold of civilization, as is our duty as sapients.'\ +

                    \ + In a statement closer to home,Commander Iheraer Saelho of the Zaddat Escort Fleet has assured VirGov that 'we only took action to protect the innocents of the Dark Triangle and of neighboring systems'.He asserts that Hegemony rule will ultimately benefit all races of people within the Triangle, and promises that, 'the people of Vir, of Oasis, of the Golden Crescent writ large, have nothing to fear from our clients the Zaddat, or from the Hegemony vessels assigned to their protection.'\ +

                    \ + Only time will tell if Saelho's promised peace and stability will manifest in truth. \ +

                    \ + This newfound militancy of the Hegemony is likely to become a major campaign issue in the upcoming Vir elections, alongside involvement in the war with the Almach Association and traditionally Virite issues of corporate authority, minority-friendly infrastructure, and taxation." + +/datum/lore/codex/page/article45 + name = "03/12/63 - Ue-Orsi Escapes Hegemony Triangle" + data = "The lawless 'Ue-Orsi' flotilla, home to hundreds of thousands of outcast Skrellian pirates, has departed from the Hegemony-controlled Dark Triangle after what appears to be a brief battle with several Unathi warships. The action damaged several important Orsian ships, including their massive and venerable solar array 'Suqot-Thoo'm', a development which is likely to increase the pirates' aggression in the coming months as they search for additional power sources. It is unclear exactly where the flotilla has fled, though best guesses indicate that they are presently in Skrell space, likely near the lightly-patrolled Xe'Teq system. The Moghes Hegemony is in negotiations with several Skrellian states to arrange for military action against their escaped subjects, but little headway has been made thus far.\ +

                    \ + This revelation has added more fuel to already heated Assembly arguments about SolGov response to the Unathi takeover. 'This is a prelude to invasion, nothing more and nothing less,' says New Seoul Representative Collin So-Yung, a noted Iconoclast. 'We must make it absolutely clear to the Hegemony that this is a threat we will not bow to, even in our present state of internal weakness. I suggest we pursue a fair peace with the Association, one where we can keep them as allies against this sort of encroachment instead of shattering our fleets during such a pivotal moment.'\ +

                    \ + Others took a more nuanced approach, including VGA Governor Bjorn Arielsson. 'What we have here is our punishment for how badly we've treated the people of the Triangle. I don't really see why we should have let the tired old racism of some Qerr-Katish oligarchs stop us from offering aid to their tired and huddled masses, such as it is. And because we have had a full century of ignoring their plight, they were defenceless to resist the Hegemony. I say we fling the doors open, let (the Orsians) settle some rock here, and show the unaligned powers of the galaxy that the Hegemony's way isn't the only way.'\ +

                    \ + Even more conciliatory was Speaker ISA-5, who Arielsson blames for mistreatment of the Ue-Katish. 'Our policy has always been that our defence budget cannot adequately defend the Dark Triangle from internal piracy, that Ue-Orsi is a criminal organization using their refugee status as a shield, and that we cannot lift the blockade of Natuna until they stop hosting these criminals and transition to a more sustainable economy. If Moghes has the power and the inclination to administer the Triangle, I see no reason why this state of affairs isn't better than the alternatives.'" + +/datum/lore/codex/page/article46 + name = "03/26/63 - Almach Routed from Saint Columbia" + data = "The Saint Columbia system has been declared free of Association forces following a renewed SCG offensive in the region. Admiral McMullen of the Saint Columbia garrison - who has reportedly reclaimed his post at the system's naval base despite the facility suffering moderate damage in this week's fighting - says that the last secessionist vessels were driven from the system just over 24 hours ago, and remaining pockets of resistance have been quick to lay down their arms. At least 20 enemy vessels - mostly converted civilian ships - have been confirmed disabled or destroyed in-system, thanks in no small part to the deployment of the state-of-the-art MJOLNIR weapons system.\ +

                    \ + This more aggressive approach to the Almach front comes on the tail of aggressive Hegemony deployments in the Dark Triangle, which SolGov has conceded was 'Immediately threatening, but after some deliberation, has brought some form of policing to a lawless region.'.\ +

                    \ + Despite assurances, this recent action would appear to many to be an effort to quash the Association threat and return fleet forces to the now extended Hegemony border, and some critics of the Almach War have called for a second ceasefire 'In order to focus on the real threat to mankind.'" + +/datum/lore/codex/page/article47 + name = "04/28/63 - Representative Hainirsdottir Reaffirms Pro-Vey Medical Manifesto" + data = "Incumbent Vir Representative Lusia Hainirsdottir has restated her dedication to advanced medical research at a public appearance at a Vey Medical facility in downtown New Reykjavik. Vey Medical has come under some criticism locally in the past year due to its 'accelerated' sapient trials, which Representative Hainirsdottir has strongly endorsed in her current term of office.\ +

                    \ + In her statement to staff at the New Reykjavik facility, Lusia promised that under her governorship, the company would not be reprimanded for the deadly Holburn's Disease outbreak in rural Sif this past June which claimed fifteen lives, and in which Vey-Med's involvement was only confirmed this week - as the outbreak 'directly lead' to the development of a new life-saving inoculation which has seen success galaxy-wide.\ +

                    \ + Hainirsdottir has long advocated for the promotion of scientific achievements that have taken place in, and undertaken by the people of Vir." + +/datum/lore/codex/page/article48 + name = "05/06/63 - Isak Spar Withdraws from Vir Election" + data = "Independent gubernatorial candidate Isak Spar has withdrawn his name from the running following a 'Public Relations disaster' aboard an orbital NanoTrasen logistics facility. According to witnesses, Spar acted belligerently towards staff members and engaged in vandalism and assault with a deadly weapon during his scheduled visit to the station, which the candidate had opted to undertake alone due to the temporary illness of his campaign manager.\ +

                    \ + In a statement just hours after the alleged incident, Isak announced that he would no longer be pursuing Vir Governorship, due to 'The unexpected stresses of a political career.' before plugging his upcoming album, C*** End Savage Turbo Death A** Destruction. The NanoTrasen corporation has decided not to press charges due to Mr. Spar 'Suffering the consequences on a far more significant level than a mere fine.' but will not be inviting Spar back for future visitation.\ +

                    \ + Spar's label, Skull Wreck Music has declined to comment at this time but has agreed to pay damages to the victims on behalf of the self-described 'post-pseudo electro-death superstar'." + +/datum/lore/codex/page/article49 + name = "05/15/63 - Solar Fleet Launches Offensive Against Almach" + data = "The first vessels of an SCG Fleet invasion force arrived in the Relan system this morning after a month-long intelligence operation to establish the Almach Association's most vulnerable positions, according to an announcement by Admiral McMullen just hours ago. Relan, which has long been fragmented between the neutral Republic of Taron and the once insurrectory Free Relan Federation - who now control the majority of the system and declared allegiance with Almach early in the crisis - is expected to fall to Confederate forces within 'a matter of weeks' due to its fractious political situation, and relative insignificance to Almach interests.\ +

                    \ + According to McMullen, the system's most populous habitat, the Carter Interstellar Spaceport is already under blockade and local resistance has thus far been minimal. The capture of Relan is expected to provide our forces with a major foothold in Almach territory and further advances are expected to be 'trivial', bypassing the Association's defensive positions in Angessa's Pearl.\ +

                    \ + The offensive comes weeks after sizable portions of the Fleet were publicly withdrawn from the frontline in order to reinforce the extended border with the Hegemony following their annexation of the Dark Triangle, and is a clear sign that - despite reduced numbers - Fleet command remains confident of Solar victory against the Mercurial rogue state." + +/datum/lore/codex/page/article50 + name = "05/19/63 - 'Drone Operated' Shelfican Ships Storm Sol Siege" + data = "Blockading Solar vessels in the Relan system came under fire today from automated craft originating from the Shelf fleet. The flotilla of drones, described by one survivor as a 'swarm', launched electromagnetic pulse and so-called 'hatch buster' precision missiles against three SCG Defense vessels, inflicting systems damage, and casualties 'in the hundreds' with at least eight service people already confirmed killed in action. The drones are reported to have withdrawn after 'only a few minutes of protracted fire from both sides.'\ +

                    \ + The Shelf telops fleet, which was spotted by Fleet forces but not identified as an immediate threat, entered the system early this morning and were understood to be acting as observers to the ongoing battle for the Relan system due to Shelf's official stance of non-aggression -- despite aligning itself with the Association. Shelf has reportedly been unable to be reached for comment on their actions, and their alleged neutrality in matters of war has been seriously called into question by many in the Colonial Assembly.\ +

                    \ + The SCG-D Krishna and SCG-D Mogwai of the SCG fleet, and the assisting Oasis logistical vessel, the OG-L Cloud Nine have been withdrawn to an unspecified location for immediate medical assistance and repairs." + +/datum/lore/codex/page/article51 + name = "05/20/63 - Fleet Withdraws - Sol On The Back Foot?" + data = "The Solar Colonial Assembly has confirmed that the Solar Fleet has withdrawn from the contested Relan system due to 'unexpected resistance' from Shelf tele-operated forces. This comes less than 24 hours after three Solar vessels were seriously damaged in an 'ambush' by a large number of Almach-aligned military drones. According to the Fleet, they were unprepared for any significant ship-to-ship combat in the system and will be consolidating their forces. 'This is not a defeat', according to Captain Silvain Astier of the SCG-R Hanoi, speaking unofficially to Occulum News sources 'This is merely a tactical withdrawal in order to reconvene and reassess our plans to restore order to the Almach Rim.'\ +

                    \ + A spokesperson for Shelf was quick to contact Fleet forces following the withdrawal with a formal apology for yesterday's incident, describing the previous day's attack as 'A terrible mistake.', blaming 'a miscommunication between our people in telops and the trigger-happy robots', though the veracity of their claims cannot be confirmed." + +/datum/lore/codex/page/article52 + name = "05/21/63 - NanoTrasen Station to Host Major Election Debate" + data = "As the Vir Gubernatorial elections approach, and with several high-profile debates lined up between the leading candidates in the polls, the NanoTrasen corporation is set to host its very own televised event live from one of its major logistical stations in Vir. The NLS Southern Cross, primarily a traffic control outpost managing shipping in Sif orbit, has been selected by the company to host the debate - funded in full by the corporation - due to a series of minor political scandals that have taken place on the platform, and the suitability of unused space onboard.\ +

                    \ + Early in the election cycle, NanoTrasen came under fire for its alleged 'manhandling' of Icarus Front candidate Vani Jee, and the confiscation of to-be-televised recordings taken by a party drone. While the company apologised for the incident shortly thereafter, the corporation hopes to mend ties with the potential future representatives by showing that they are capable of hosting civil discourse. More recently, the NLS Southern Cross played host to the 'breakdown' of disgraced former candidate Isak Spar - an episode which was not addressed in NanoTrasen's official statement on the planned debate event." + +/datum/lore/codex/page/article53 + name = "05/26/63 - SEO Candidate Advocates Murder On Live TV!" + data = "Sol Economic Organization candidate Freya Singh has been caught live on camera admitting that she would like to throw an innocent individual out of an airlock for a minor slight. During this afternoon's debate hosted aboard the NLS Southern Cross. Singh is quoted as having said that the event was 'the silliest concept for a debate I've encountered yet, and whoever came up with it should get a promotion, and then be fired out of an airlock.', a clear incitement of violence against Oculum Broadcast staff.\ +

                    \ + Magnus Dugal, 48 works for the Oculum Broadcast corporation and is credited with creating the concept for today's debate, the highest rated for this cycle so far. The father of three, who enjoys hoverboarding in his free time, says that he feels 'Threatened' by Singh's comments, and hopes that she will, 'at the bare minimum', issue an official apology to the company and himself.\ +

                    \ + Candidate Freya Singh, a career investment banker, spent much of today's debate advocating for reduced safety regulations and the apparent overturning of the Five Points, raising eyebrows across the system. Singh's office claims that her statements were 'a joke', but we do not feel that this is a laughing matter.\ +

                    \ + In related news, Shadow Coalition candidate Phaedrus remains under a profanity filter 'house arrest' for the remainder of the election." + +/datum/lore/codex/page/article54 + name = "06/28/63 - Vir Finalizes Dates for Election Voting" + data = "The Vir Governmental Authority has confirmed that voting for Vir's governorship and Colonial Assembly seats will take place on the 29th and 30th of June, with an additional voting period set for Wednesday the 3rd of July to allow for out-of-system and full-time weekend employees to cast their votes. No exit poll information will be released until the final votes have been cast, and final results are expected to be announced within another week.\ +

                    \ + According to a Oculum poll, Lusia Hainirsdottir is expected to comfortably take a seat, though the certainty of her governor position is not hard set. Candidates Sao, Singh and Jorg are trailing not far behind, but will all have to make good showings this weekend if they hope for electoral success. In an unexpected surge among minority species, the Shadow Coalition's Tajaran candidate Kurah Zarshir is leading the polls in certain outlying and orbital communities.\ +

                    \ + Not sure how to vote, if you can vote, or who to vote for? Check out the official election website at your-choice-vir.virgov.xo.vr" + +/datum/lore/codex/page/article55 + name = "06/29/63 - Morpheus Cyberkinetics To Split Assets" + data = "The Morpheus Cyberkenetics Corporation is to split into two distinct entities operating under a single board of trustees, in light of their Almach branch's apparent involvement in the ongoing war after last month's 'unintentional' corporate drone strikes. Citing 'Severe communications disruptions' between its operations and assets on either side of the cordon since it was put in place last year, the SolGov-side corporation is to become 'Morpheus Sol', retaining most assets and current corporate headquarters, and its Almach counterpart 'Morpheus Shelf', which is to be based out of the administration station MAS Sophia Jr., located in the El system.'\ +

                    \ + The principle victim of the Aetolian coup, Nanotrasen, has seen most of their considerable Almachi investment nationalized by the secessionist government, as has Xion and other major Almachi organizations. Most surviving corporate exclaves have been effectively written off by their parent company for the duration of the conflict, due to the severe difficulties effectively conducting trade across the militarized border. Before today, the sole exception was Morpheus, whose involvement in the secession prevented any seizing of their assets. It seems, however, that even the sardonic positronic corporation is not immune to the difficulties of doing business in the Almach Rim region.\ +

                    \ + Member of the Board Chock Full of Sardines introduced the proposal by saying, 'Our goal here is not being shot. Together with leading economic scientists, we've devised a scheme that will allow us to be shot for illegal smuggling almost ninety percent less often.' They defended the confusing and offensive choice of name in 'Sophia Jr.', seemingly intended as an insult to longstanding rival Sophia, by claiming, 'It's absolutely hilarious.'" + +/datum/lore/codex/page/article56 + name = "06/30/63 - Almach Leak Confirms 'Super-weapon' in Whythe" + data = "Solar Confederate Government Intelligence has this afternoon confirmed the presence of a so-called 'Super-weapon' in the distant Whythe system, after an apparent intelligence leak was posted to the exonet in the early hours of this morning. According to a spokesperson for the Solar Fleet, the public were not made aware of the super-weapon as the military 'have no reason to believe that the weapon poses any threat to civilian targets within SolGov space at this time, and there is no reason to cause panic with what amounts to the announcement of an Almachi propaganda tool intended to sow discord with bold threats of overwhelming power. This morning's leak achieves nothing but serving the Association's schemes. Keeping this so-called super-weapon - and I hesitate to use that term - a secret seems to have been last on their list of priorities.'\ +

                    \ + According to the intelligence documents released this morning and widely spread within minutes of upload, the 'super-weapon' is a colossal space-bound structure equipped with 'newly developed bluespace technology', though its exact purpose or capabilities have not been confirmed by either side.\ +

                    \ + Additionally, the Solar Fleet has announced that an unnamed individual within the intelligence service has been placed under arrest in connection with the leak." + +/datum/lore/codex/page/article57 + name = "07/04/63 - Exit Polls Suggest Shadow Coalition Win in Vir" + data = "According to the first exit poll data released after Vir Gubernatorial voting closed at midnight, local favourite the Shadow Coalition is expected to win at least two representative seats, with incumbent representative Lusia Hainirsdottir taking a comfortable lead.\ +

                    \ + Final results are not expected to be tallied until Saturday morning, but other frontrunners include the Icarus Front's Vani Jee and Mehmet Sao - running on drastically different platforms - alongside the Shadow Coalition's Selma Jorg. In an unexpected turn, sole Tajaran Candidate Kurah Zarshir of the Shadow Coalition has seen an immense surge in popularity among minority and more xenophilic voters. Could Vir be seeing its first Tajaran Representative? Experts say: 'Perhaps.'" + +/datum/lore/codex/page/article58 + name = "07/07/63 - Vir Election Results" + data = "The results of the 2563 Vir Gubernatorial Elections are as follows:\ +
                    \ + Governor of Vir: Lusia Hainirsdottir (Shadow Coalition)\ +
                    \ + Vir Colonial Assembly Representative: Vani Jee (Icarus Front)\ +
                    \ + Vir Colonial Assembly Representative: Selma Jorg (Shadow Coalition)\ +
                    \ + Other candidates ranked: Sao (4), Zarshir (5), Keldow (6), Singh (7), Moravec (8), Phaedrus (9), Lye (10), Savik (11), Square (12), Wekstrom (13)\ +

                    \ + Voter turnout: 30,928,287 (63%)\ +

                    \ + The greatest upset this election cycle has been the unexpected popularity of 'alien rights' candidate Kurah Zarshir, who was eliminated in favour of Mehmet Sao (Icarus Front) in the 8th round of vote transfers by a margin of just 30 votes, or 0.000096%, prompting a rigourous recount process to confirm the result. A difference at this stage could have resulted in a significantly different final line-up.\ +

                    \ + This year's winners showed clear advantages in the first-choice votes, each gaining at least 15% of the popular vote before any transfers were calculated, though Sao made significant gains in the final count, falling only a few percent short of the Jorg's 3rd place position. By far the least popular candidate this cycle was Hal Wekstrom of the Sol Economic Organization, who received just 0.8% of the first-choice vote and was immediately eliminated. Also of note were Phaedrus, Apogee Lye and Yole Savik voters, each of whom had high (30%+) voter exhaustion rates, opting not to provide alternative choices; sending the message 'My candidate or none at all.'\ +

                    \ + The elected are to be sworn in at a ceremony on Luna in two weeks time." + +/datum/lore/codex/page/article59 + name = "07/30/63 - Solar Fleet Data Breach" + data = "Last night, a number of files were spread on the Monsters From Beyond's exolife forums allegedly depicting the boarding and eventual scuttling of the SCG-TV Mariner's Cage during a voyage close to the Gavel system on the 12th of June, before the SCG had officially released any information regarding the event. The files contained undisclosed documents from the Solar Fleet investigation, some of which appear to contain audio and video recordings of the final moments of the crew before the vessel's bluespace drive was detonated. Due to the graphic violence depicted and their classified nature, we will not be sharing the files, however as a matter of public record we will explain the events recorded therein. The following description may be unsuitable for sensitive readers.\ +

                    \ + First, the navigation crew detects a drive signature on an apparent intercept course with their own, originating from across the SCG-Almachi border. It was not a large vessel, and is assumed to be some form of autonomous drone. The crew disregards it as a low level threat, instead continuing on their trajectory, leaving only the standard point defense armament locked on. This proved to be a lethal mistake, as the vessel appeared and near-instantly began accelerating toward the Mariner's Cage, before impacting the fore weapons array. The recording is cut, due to what was likely a power surge, however upon reconnection, reports indicate no damage related to any known warhead was apparent, aside from the initial impactor. The crew mistakenly assumes it to be a failed suicide drone strike, and dispatches minimal security personnel, and a large complement of response engineers.\ +

                    \ + Approximately thirty minutes after the response teams are dispatched to the impact zone, the teams begin losing contact, with those first arriving being the first to disappear. When the security responders intercept the path of communications blackouts, they are met with the blades of multiple Aetolian shock troopers. Two appear to be made from a 'living steel', with each limb taking the form of 'jagged cleavers' as one radio recording states, and three more of 'indeterminable classification'. The ship entered a red alert state, and moments later, the small contingent of marines aboard the supply vessel were dispatched to deal with the threat. All five members of the enemy boarding party were able to be rendered inert through sustained fire, though not without Sol casualties.\ +

                    \ + According to the next recordings, approximately three hours after the incident, the vessel received orders to interrogate the boarding 'Aetotheans'. The two noted to appear as the officers of the squad were rejuvenated within sealed interrogation chambers reinforced with supplies on hand, apparently capable of stopping sustained fire from multiple energy weapons. The first individual was a 'sapphire' according to information from NanoTrasen correspondants. It refused to speak in Galactic Common, and instead utilized an unknown frequency of biological transmission, and internal charge shifts. The individual was moved to a more permanent cell within the vessel's brig for transport, and the second was rejuvenated. Only the first half of the interrogation, which lasted approximately two and a half minutes, compared to four hours for the first, was recovered. The individual is rejuvenated, and is engaged in discussion with the interrogating officer when it suddenly stands, emits what is described as a 'wail', and detonates, destroying the transmitting camera, and presumably killing the officers involved in direct interrogation.\ +

                    \ + Final recordings originate from the ship's onboard A.I. housing, which was involved in continual discussions with presumably the 'sapphire', as it enacted the vessel's scuttling. It is unknown whether or not the individual was somehow capable of restoring the other individuals that fell in combat in order to free itself, or if it was able to incapacitate the transporting officers, and command crew of the vessel alone.\ +

                    \ + The Solar Fleet has expressed 'regret' that the files were leaked in their complete form, and have assured the public that an official report was due for release in the coming weeks. Concerns of 'Aetothean' attacks on civilian targets have been dismissed as 'improbable', but have affirmed that 'the threat is being taken very seriously'." + +/datum/lore/codex/page/article60 + name = "08/03/63 - Hainirsdottir Sworn In As Governor of Vir" + data = "Following a short transitionary period for the previous administration, this year's election victors have been sworn in at an official ceremony at the Colonial Assembly Hall on Luna. During her welcoming address, Governor Hainirsdottir reaffirmed her plans for the future of the system, promising a 'Bright future for Vir as a hub for medical science.', and plans for an incentivisation program for the removal of invasive extra-terrestrial species that have long plagued the region - in particular the aggressive spiders that have become synonymous with certain regions of the Sivian wilderness.\ +

                    \ + Additionally, the newly elected representatives announced expected, but none-the-less significant changes to the administrative staff of the system. Notable figures include two defeated election hopefuls: Kurah Zarshir has been selected as the Shadow Coalition's Culture Secretary for the system, while Mehmet Sao has been brought aboard by the Representative Vani Jee as the Vir Icarus Front's Internal Security Advisor. It is expected that the former candidates may use their positions to further certain goals from their own campaigns, but under the watchful eyes of their perhaps more moderate superiors." + +/datum/lore/codex/page/article61 + name = "08/04/63 - Former Independence Candidate Found Dead" + data = "It has been confirmed by a spokesperson for the Sivian Independence Front that a body found by hikers last week in the Ingolfskynn Mountains, approximately 200 miles northeast of New Reykjavik, belonged to party chair Yole Savik.\ +

                    \ + Savik, 68 - who had run for Vir Representative in the recent election - had not been seen since the 14th of July, shortly after the results were announced. Party officials claim that Mr. Savik frequently made 'off the grid' trips into the Sivian wilderness and his absence had not been treated as suspicious until investigators approached them to confirm the identity of the body. According to police, though Yole was publicly known as a 'seasoned frontiersman', Savik had succumbed to exposure at least two weeks prior to the grisly discovery. His death is not being treated as suspicious." + +/datum/lore/codex/page/article62 + name = "08/07/63 - Almach Pirate Threat Vanishes - Analysts Baffled" + data = "Skrellian Xe'qua pirates operating in the far reaches of the Almach Association since the onset of hostilities last year, have inexplicably gone dark. The pirates, who were under close SolGov surveillance to monitor their impact on Almachi shipping, have drastically dropped in activity and numbers over the last month according to an official report released by the Solar Fleet today. The Fleet is unable to account for the cease in activity, which has now reached levels even lower than their pre-war baseline, as there have been no reports of Almach military operations in the area, nor any signs of decisive battle on the Almach border with pirate space.\ +

                    \ + The drop in activity roughly coincides with the leaked information on an Almach 'Super-weapon' in Whythe, though military sources do not believe that the weapon has been deployed in any capacity at this time. According to Hasan Drust, an expert on Skrellian foreign policy, the 'only feasible explanation (is) major anti-piracy action undertaken by the Skrellian Far Kingdoms', who occupy the space beyond the Xe'qua pirates' known range. The reasoning behind this action now, against pirates who have historically only targeted human space is not entirely clear, though Drust suggests that it may simply be a coincidence as pirates would be a 'trivial issue' for Far Kingdom military might." + +/datum/lore/codex/page/article63 + name = "09/02/63 - Shock Almach Attack Routs Relan Front!" + data = "Following close to a month of reduced Almach activity, enemy Militia forces have today launched a staggering attack on Sol frontline forces in the region of the Relan system, disabling several SCG warships and forcing a major tactical retreat to Saint Columbia. The scale of this attack by Almach forces is unprecedented, but seems to be the result of the Association consolidating manpower previously dedicated to anti-piracy patrols on the far side of their territory. It is believed these vessels have become freed up due to the apparent but as of yet unconfirmed annihilation of Xe'qua criminal flotillas by Skrellian Far Kingdom police action.\ +

                    \ + The Solar fleet had been in position to blockade the Relan system in the hopes of forcing the Free Relan Federation to surrender and withdraw from the Association, but was unprepared for what has been described as an 'all-out attack' on their positions, which left the vessels SCG-D Liu Bei, SCG-D Wodehouse, SCG-TV Ceylon Hartal and SCG-TV Apoxpalon disabled and unable to retreat with the bulk of our forces, as well as inflicting severe damage to several other craft. According to initial reports, the strikes on many of the afflicted ships closely resembled scenes from the controversial 'Aetothean shock attacks' on the SCG-TV Mariner's Cage this June, which saw the ruthless deployment of gene-altered Promethean 'super-soldiers' by the Almach Association.\ +

                    \ + Fleet Admiral Ripon Latt, commanding officer of the assailed fleet, has confirmed that reinforcements are underway and the retreat 'shall not be a significant setback in the war effort', especially assuring citizens of the embattled Saint Columbia system and its neighbours that there is no cause for alarm and civilians have yet to be targeted.\ +

                    \ + The fates of the four missing ships have not been confirmed, and though the Fleet has not yet made an official statement, Sol casualties are cautiously estimated to be in the hundreds." + +/datum/lore/codex/page/article64 + name = "09/23/63 - Fleet Refuses Inquiry Into Relan Losses" + data = "The SCG Fleet has refused to heed widespread calls from critics to launch an investigation into the heavy losses sustained by our forces in a major Almach attack early this month, citing that an investigation at this time would 'undermine the ongoing efforts of our troops in battles to come'.\ +

                    \ + The attack, which took place on the 2nd of September and at current count resulted in the loss of a staggering 1281 Sol lives, quickly drew criticism from experts for 'the total unpreparedness' of the fleet despite their public claims that all vessels were 'battle ready and prepared for a coming offensive.'. The specifics of the fleets apparent failings have been the focus of much speculation in the intervening weeks, with the blame placed on everything from a critically inexperienced officer core, to ongoing redeployments to and from the recently expanded Hegemony border.\ +

                    \ + Admiral Latt has condemned critics, stating that 'the last thing our brave troops need right now is murmuring from people who don't know the first thing what they're talking about. Their actions in following orders to fall back to the border have been nothing but commendable, and all effort was made to minimise loss of life. The fleet is undergoing reorganization at this time, and is in a better position than ever.'" + +/datum/lore/codex/page/article65 + name = "09/27/63 - Almach Bypass Saint Columbia In Brazen Gavel Attack!" + data = "Almach Association fleet forces entered the Gavel system this afternoon, reportedly having evaded interdicting Sol forces from Saint Columbia in an apparent effort to skirt the range of the MJOLNIR weapon system in Saint Columbia and cut off that system from major shipping routes. Current reports from the system capital in New Xanadu are that the majority of outlying civilian stations have surrendered to invading forces with only minor incident, but that skirmishes with local defence forces - including Sol Fleet detachments - are ongoing, and it is too early to remark on the outcome of the battle. Official military reports are scarce at this time, but the Fleet in Saint Columbia is 'on the move and ready to repel the invaders'.\ +

                    \ + Accounts from the system's edge describe Almach forces 'firing indiscriminately' on anti-piracy emplacements including those mounted to the ILS Thurston, a Greyson Manufactories collection station with eight crew, killing all hands.\ +

                    \ + Open fighting in the Gavel system marks the furthest Almach encroachment on Sol territory to date. The system, which is a stone's throw from the Oasis and Vir systems is best known for the destruction of the moonlet 'Requiem' by a rogue nanoswarm in 2289, which was successfully neutralized by government forces, and boasts only a small population relative to its neighbors." + +/datum/lore/codex/page/article66 + name = "10/01/63 - 'Judgement Day' As Gavel Falls!" + data = "The government of New Xanadu has surrendered to Association invaders following a disastrous relief effort by the Solar Fleet, whose interdiction vessels are believed to have been captured by the invading force. The manoeuvre leaves the bulk of the Sol fleet isolated in the Saint Columbia system - though a breakout is expected - and has led to widespread outrage in the Colonial Assembly. Critics of the war have damned the Fleet for their 'inability to fight a civilian rabble, gene-modded or otherwise' and renewed calls for a peaceful arrangement between the Solar Confederate Government and Association.\ +

                    \ + ISA-5, current spokesperson for the Shadow Coalition has forwarded a motion today to resume discussions with Almachi heads of state, just hours after news of Gavel's surrender broke. The proposal which has yet to gain widespread traction, would call for a new ceasefire, and ISA-5 has stated they 'hope that a new agreement can be made to end the senseless loss of life over the particulars of a foreign government's right to autonomy.'.\ +

                    \ + Executive Sifat Unar of the Emergent Intelligence Oversight has voiced immediate concern over the motion, criticising the use of 'foreign government' in reference to Almach; 'Our Fleet has suffered a few defeats, but this conflict goes deeper than mere lasers and shells and to surrender to torturers, mind-hackers, and Machiavellian machines at this stage would be insanity. To allow a seccessionist state, particularly one so unabashedly guilty of crimes against humanity that go far beyond even our modern definitions of 'Human Sanctity', to exist unquestioned a stone's throw from some of our most precious member states, would be a failing not only of this government, but of humanity that would echo through history like a great shameful dirge for all to hear.'\ +

                    \ + A communications blackout has been instated on the Gavel system by the Almach Militia, though earlier reports indicate continued strikes on numerous civilian colonies who were unwilling, or unable to deactivate their automated defence systems prior to the invaders arrival." + +/datum/lore/codex/page/article67 + name = "10/08/63 - 'Magnetic Weapon' Designs Released Following Gavel Threat" + data = "Private security and ExoMartian law enforcement agencies are now receiving modernized man-portable magnetic weapon designs produced by Mars Military Industries thanks to increased budget from the S.C.G. The 'hallmarks' of these weapons, as one M.M. Industries spokesperson says, are their incredible ability to launch physical projectiles at velocities rivalling present portable laser technology in practical utility. Many of the designs utilize generalized, easy-to-manufacture compressed matter cartridges as their primary ammunition, meaning no specialized production facilities are required outside of standard shipyard or planetary lathe systems, 'to ensure a cutting edge in the battlefield, down to the last man'.\ +

                    \ + Some corporations, such as Hephaestus, NanoTrasen, and the PCRC, are already preparing to utilize these released designs in their own laboratories and stations, undoubtedly providing yet more materiel backing to the Almach front." + +/datum/lore/codex/page/article68 + name = "10/10/63 - Gavel Encircled - Liberation In Sight" + data = "Significant Fleet reinforcements from the Unathi border have 'trapped' the Almach fleet in the Gavel system and are poised for a decisive victory, according to latest reports from the front. Solar vessels from all sides of the war-torn system have closed in to ensure the invading force have no means of retreat. The relief force includes elements of the Hegemony-border fleets and the previously deployed flotilla stationed in Saint Columbia. Speaking at the Colonial Assembly this morning, Rewi Kerehoma of the Sol Economic Organization has stated that the reinforcements will 'beyond a doubt' prevent a repeat of 'embarrassing' errors made in the past month.\ +

                    \ + Efforts have been made to re-establish contact with the occupied system, which has been blocked from communication with the rest of the galaxy since the occupation began last week. According to scattered civilian signals from the system, the Association has adopted a 'salted earth' policy to the system following Solar military response, openly demolishing system infrastructure with little regard for its residents. A spokesperson for Grayson Manufactories, who maintain a significant presence in the Gavel system , has proposed that 'The Almachi had no intention of holding this system, this may have been nothing more than a show of force against corporate assets supporting the war effort.'\ +

                    \ + In related news, Admiral Ripon Latt has officially retired from his post following immense political pressure from the Assembly. Latt was until this week, commander of the Rim Expeditionary Force - currently in the Saint Columbia system - and has been the primary target for blame in the SCG's defeat in Relan, and failure to prevent the invasion of Gavel. The disgraced admiral will receive a full officer's pension, but no official honours befitting of his previous rank. Latt is to be replaced immediately by Admiral Silvain Barka, an experienced veteran of anti-piracy action in the Rarkajar Rift." + +/datum/lore/codex/page/article69 + name = "10/26/63 - 'Largest Engagement Since The Hegemony War' As Gavel Freed" + data = "The Almach Association invasion force in the Gavel system has been all but annihilated by a successful Solar counter-encirclement, at great cost to both sides. The combined Rim Expeditionary Force in Saint Columbia, along with the newly formed Gavel Relief Fleet - which had been massing in the Vir system over the past week - launched the successful attack this Tuesday evening, leaving no route of escape for Almachi invaders and resulting in 'pitched fighting' between the fleets that lasted several days. Solar forces are currently in the process of performing security sweeps of the system and its scattered habitats and it is expected to be several weeks before the system is declared safe to civilian traffic and for refugees to return home.\ +

                    \ + Even as exact causalities remain unconfirmed, the battle has made history as the single largest ship-to-ship engagement by tonnage involving the Sol military since the cessation of hostilities with the Unathi in 2520, involving over one hundred vessels of all sizes across both sides, as well as countless unmanned drones and light craft. Almachi forces, in numbers described as 'far from an insignificant portion of the total fleet' fought fiercely, and 'in manners more reminiscent of mercenary gangs than a single organized force, and with tactics varying from the conventional to the outright mystifying'. Admiral Silvain Barka has commended his own crew for applying lessons learnt from prior 'Aetothean' commando strikes in preventing similar incidents from occurring in the confusion of battle; in a candid interview this morning he stated 'Like any Promethean, (Aetotheans) hate the cold, and my crew are the coldest (expletive) around.' \ +

                    \ + The designations of twenty-four Sol Defense Vessels declared 'lost in action' have not yet been released, though next of kin of missing or deceased servicepeople have reportedly been notified." + +/datum/lore/codex/page/article70 + name = "11/11/63 - Gavel Salvation Reveals True Cost of War" + data = "Reports from liberating forces in the Gavel system have confirmed early accounts of 'inhuman' tactics employed by the Almachi invaders during their short occupation and defense of the region. Besides an apparent disregard for sapient life, especially any that gave an outward appearance of defending themselves, the Militia is believed to have employed troops and tactics 'the likes of which had only been imagined', in 'a manner that can only be described as experimental'.\ +

                    \ + Most harrowing of the accounts are those of alleged 'kill-switch clone armies' consisting of near-identical vatborn troops deployed to some of New Xanadu's largest surface colonies. According to local residents, the 'uncanny' troops arrived en-masse 'from the depths of the wastes' at the beginning of the invasion, despite wearing no obvious gear that would protect them from the extreme, unbreathable environment beyond the confines of controlled habitats. The clones are said to have targetted infrastructure including life support, with little regard for those who stood in their way. However, what has baffled Fleet analysts is the reaction upon the arrival of Sol surface troops; the clones did not fight back, but rather dropped dead 'all at once, as if a switch had been flipped'.\ +

                    \ + Teams from the SCG's top analytical and regulatory bodies including the EIO have been hard at work collecting examples of the unusual Almachi technology from throughout Gavel. Executive Sifat Unar has stated that 'It is vital that we ensure this transgressive technology no longer poses a threat to sapient life, either now or to future generations. A full cleanup operation is underway, and we are working to analyse what we have found and better understand the enemy's limits and the extent of their biological and technological... Practices.'\ +

                    \ + The current confirmed death toll, including civilian losses in Gavel has now reached 11,520 and is expected to rise. Debate continues whether to add the so-called 'Kill-switchers' to the tally." + +/datum/lore/codex/page/article71 + name = "11/21/63 - Tajaran Pearlshield Draws Line In The Sand" + data = "Khama Suketa enai-Lutiir, representative of the Tajaran Pearlshield Coalition, has just issued a formal statement:\ +

                    \ + 'It's no secret that we members of the Pearlshield Coalition have our differences and conflicts, both within and without. Much as some might call it the spice of life, it's with shame that I admit it has left us somewhat paralyzed over the last few months, even as a war has raged on a mere few jumps away from our borders. But in acknowledgment of our differences, something we have been able to unanimously agree upon is the sanctity of life and common decency, sanctity that Almach has continued to violate in the name of political conquest. Our relationship with SolGov - and indeed, any who would call us 'friend' - should be one of mutual cooperation and benefit, not of hard boundaries delineating 'us' and 'them'. There is only 'we', and we cannot stand idly by.\ +

                    \ + 'To this end, the Pearlshield has negotiated with local forces in the Silk system, and are taking over interim protection of the system, to free up Solar military forces so they can assist in the war effort. We have also begun construction of a large residential station to supplement the Silk station itself, alleviating its acknowledged overpopulation issues and providing additional logistical support for our defensive fleet. Finally, our cousins in Mesomori have generously loaned their new flagship, the PCMV Raniira's Grace, with all hands on deck for temporary joint assignment with Solar military forces. They are quite eager to provide a taste of Tajaran firepower and ingenuity.\ +

                    \ + 'We wish we could spare more at this time, but alas, we're still finding our feet among the stars, stepping carefully among the proverbial minefield that is our own share of cosmic threats. Know that these contributions represent a grand investment in their own right, and they are only the beginning should this war carry on.\ +

                    \ + 'We will have more to say as it comes up. May all our stars shine upon us.'" + +/datum/lore/codex/page/article72 + name = "11/23/63 - Surviving 'Kill-switcher' Assassinated During Vir Interview" + data = "An Almachi 'kill-switcher' clone soldier capturing during the liberation of the Gavel system was yesterday 'forced' to explosively self-terminate during a live TV broadcast with Virite news anchor David Huexqole aboard a SCG prisoner transport craft, allegedly by the statement of a code phrase, clearly audible on the recording. Following the attack, the apparent 'activator' is reported to have escaped to nearby NanoTrasen logistical station, the Southern Cross amidst the chaos where a minor altercation took place, resulting in the injury of one Positronic crew member and death of one Almachi accomplice, which initial reports suggest to have been a 'Vox mercenary'. The search continues for the Almachi agent, and local authorities remain confident that they will be apprehended.\ +

                    \ + David Huexqole, a popular local media personality, was the only other immediate victim of the attack, suffering moderate injuries and was rushed to the Southern Cross for emergency surgery where he is reported to have made a full recovery. Huexqole has expressed his gratitude to the 'skilled and charming staff, clearly shaken by the war but nonetheless capable for it.' but has expressed concerns regarding so-called sleeper agents within Sol space, 'Isa (341, the interviewed clone), seemed genuinely reformed. She spoke openly of regret, and struggling with what it meant to be created only to die. Before she blew herself up, I never would have thought her capable - it was like she changed in an instant.'\ +

                    \ + Initial blast investigation suggests the presence of a previously unidentified compound in the clone's bloodstream, which was detonated following 'activation' by the as of yet unidentified Association agent, believed to have been present in the room during the recording. Moments prior to termination, the clone's demeanour is visibly altered and the phrase 'Those of Sol must look to the eastern star, Phorcys.' was clearly stated. A spokesperson for the Saint Columbia Telemetry Station has stated that observation of Phorcys, an uninhabited star system located approximately between the Almach Association state of Vounna and Skrellian Far Kingdom space, has revealed 'no immediate anomalous behaviour or presence' but that analysis will continue in the coming weeks 'in order to verify the potential meaning of this statement.'" + +/datum/lore/codex/page/article73 + name = "11/25/63 - Phorcys Star Ignites!" + data = "The Phorcys star system, mentioned cryptically during Friday's dramatic televised Almachi assassination, has 'gone supernova' several thousand millennia ahead of schedule, sparking concerns of 'Almach Superweapon' involvement. The process, which ordinarily takes place over many months and is preceded by millions of years of obvious stellar evolution, began rapidly in the early hours of this morning and has already begun to consume the entire system at a rapid rate - mercifully consisting of only two uninhabitable dwarf planets.\ +

                    \ + According to government telemetric sources, who have been observing the system since Friday's message, the 'ignition' was preceded by 'abnormal bluespace readings', but it is impossible at this time to confirm whether the phenomena was a natural anomaly that Almachi sources were able to identify ahead of time, or if the long-rumoured 'Whythe Superweapon' was in fact involved. If direct Almach involvement were the case, the range of such an 'attack' would be unprecedented, with the afflicted star far from any populated area or territorial claims and 'no evidence of sapient passage to the system in several decades.'\ +

                    \ + General Secretary Mackenzie West, speaking on behalf of the Colonial Assembly has stated that 'The Solar Confederate Government is taking this matter very seriously. The mere prospect of a weapon of this calibre would indeed pose an existential threat to our society, and perhaps mankind. However, at this time we cannot jump to conclusions, this may in fact be a game of smoke and mirrors; an effort to use a previously unobserved phenomena as a weapon of propaganda, and we cannot allow this to cloud our judgement. We are in close contact with the Skrell in order to determine the nature of this sudden ignition. Know that the Fleet is on high alert, and fighting forth from the Saint Columbia system will continue unaffected.'" + +/datum/lore/codex/page/article74 + name = "11/27/63 - Whythe Superweapon Confirmed In Phorcys Blast" + data = "New government footage from the moment of ignition in the Phorcys system has confirmed the 'brief' presence of an artificial 'bluespace gate' within the star just prior to its rapid expansion and structural collapse. This verifies claims of responsibility made by the Almach Association late yesterday, and casts no doubt on the threat posed by the so-called Whythe Superweapon. The Colonial Assembly has been in continuous session since the initial incident, and one staffer has described the atmosphere as one of 'unhinged panic'.\ +

                    \ + The prevailing fear amongst the government, with the assent of top physicists in the private sector is that such a weapon could be used against Solar targets, and that it is no longer a question of 'how', but 'when', and indeed 'where'. The only unknown factor is the superweapon's capability for repeat use, and 'rate of fire' which is yet to be demonstrated but widely considered to be 'not a risk worth taking under any circumstances'.\ +

                    \ + Latest reports from the Assembly have are that a 'scramble' is taking place to resolve the 'Almach Situation', and that the ongoing offensive out of Saint Columbia has been redirected towards Relan and Whythe. According to Skrellian correspondent Hasan Drust, official aid has been requested as of this afternoon." + +/datum/lore/codex/page/article75 + name = "12/01/63 - Skrell Defer Aid Citing 'Deliberations'" + data = "Official Solar contacts within the Skrellian government have reportedly delayed tangible aid in the rapidly escalating Almach Crisis, citing 'Internal Deliberations', agreeing only to continue to share telemetric data on the movement of Association forces. The Skrell, who were instrumental in the Solar victory against the Unathi 40 years ago have long been considered taciturn in their internal affairs, but have previously been open in their support of Sol action when it comes to national defence, making their indecisive response greatly unexpected to some.\ +

                    \ + Speaker ISA-5, who has been increasingly critical of the war in the past several days, has dismissed concerns that the Skrell are 'siding with the Association', citing the long term close relationship between their governments, 'The Skrell are not war hawks, and they've never shown outward ill-will against other sapients. We can't let a bureaucratic delay allow public opinion to turn against an entire species of people who have always had our backs. If our allies need time for deliberations, we should allow them and not expect mere muscle and military aid when words could have been our solution from the beginning. I've no doubt that they will not allow the situation to become so dire that the Association are able to cause us any serious harm as they have threatened. I'm convinced that diplomacy will prevail, and that if the Skrell are in fact negotiating with the Association, then it is with all of our best interests in mind.'" + +/datum/lore/codex/page/article76 + name = "12/20/63 - Jee Relaunches Libraries Across Vir" + data = "Vir's Colonial Assembly representative and long time education reformist Vani Jee has launched her long planned 'modernization' of exonet-linked library system throughout the Vir system. During her campaign early this year she brought to light the 'staggering unsuitability' of library networks in Vir, particularly those being provided for corporate employees and residents, with particular pressure placed on the NanoTrasen Corporation whose literary collection was described as 'basically smut and whatever an old drone had scraped from the bottom of the exonet barrel.' Following her subsequent election, she reaffirmed her desire for education reform, making 'post-education' providers her first target.\ +

                    \ + The new system, which she successfully acquired government funding for this October, is due to be launched this week and includes a 'personally curated' collection in addition to titles recommended by affected institutions, and a particular focus on local authors." + +/datum/lore/codex/page/article77 + name = "01/23/63 - 'Extreme' Environmental Alert Following Ullran Expanse Chemical Leak" + data = "Residents and visitors to Sif's remote Ullran Expanse have been advised to exercise extreme caution when travelling and to avoid consuming local water sources following a 'catastrophic' chemical spill in the mountains. The exact cause and origin of the spill has yet to be confirmed, though it is currently believed to have originated from an improperly disposed of chemical tank. Local rivers and their outflow have been tested with 'extremely lethal' levels of acutely corrosive material.\ +

                    \ + Governor Hainirsdottir has issued a statement assuring Sif residents that the cleanup process is already underway, and the contamination which return to safe exposure levels for most sapients within 'Just a few days', but that the risk to local ecosystems in much higher and 'may take decades to recover.' and locals are advised to use only pre-packaged or thoroughly filtered water for the next 'six months, at least.'\ +

                    \ + A spokesperson for the Sif Environmental Agency has described this leak as 'At least regionally, perhaps the worst environmental disaster since the introduction of the invasive spider species.' and has expressed concern about similar accidents occurring in more populated areas if stricter regulations are not put in place.\ +

                    \ + The Ullran Expanse Chemical Relief Fund has been set up and is collection donations at save-ullran.xo.vr/donate" + +/datum/lore/codex/page/article78 + name = "01/27/64 - NanoTrasen Implicated In Devastating Ullran Chemical Spill" + data = "Files recovered from the contamination site by clean-up crews and submitted to the Sif Environmental Agency suggest that the hazardous materials were discarded in the region by the NanoTrasen Corporation. Though the company is not currently accused of malicious intent, ownership has been proven of several cargo containers filled with Growth Inhibitor 78-1, a compound used in the manufacture of Vatborn humans which is known to severely inhibit cell regeneration in living creatures.\ +

                    \ + The documents, which have not been released in full by the SEA, were also leaked onto the exonet from several sources which NanoTrasen initially dismissed as fraudulent but have since admitted to their authenticity. Investigations are currently underway to determine those at fault within the company so that prosecution may begin. Third-party commentators have alleged that, 'The debris field and damaged containers suggests cargo jettisoned from a craft within the Sif atmosphere', though the circumstances are yet to be thoroughly examined.\ +

                    \ + Certain members of the civilian clean-up crews responsible for waste disposal have also accused NanoTrasen of failing to provide their employees with sufficient safety information and protective equipment, which led to several minor injuries and one volunteer was reportedly admitted to hospital for 'severe side-effects of exposure' but has since been discharged." + +/datum/lore/codex/page/article79 + name = "02/10/64 - Shelf Fleet Vanishes Without A Trace!" + data = "Government agencies are scrambling to explain the apparent 'total disappearance' of the Almach-aligned Shelf fleet. Initial reports from the fleet's last known location describe a phenomenon not dissimilar to that observed just prior to the ignition of the Phorcys star in November, but the SCG has assured the media that 'No activity has been detected from the Whythe superweapon that would indicate any relation to the ongoing Shelf situation.'\ +

                    \ + Independent telemetric data from several sources has confirmed the presence of a 'bluespace anomaly' in the moments preceding the fleet. Currently efforts are focused on determining whether the fleet has been lost with all hands or if survivors may yet be found, but no trace of the fleet or of telltale signs of large fleet movements anywhere within or beyond Almachi space have yet been detected.\ +

                    \ + This afternoon, Speaker Mackenzie West of the Icarus Front addressed Sol, and indicated that communication was underway 'across faction lines' with the Association to determine the cause and implications of Shelf's apparent loss. The Almach Assocation has claimed not to be responsible for the 'possible attack' and both sides has expressed concern for the 'astronomical loss of life this may represent.'\ +

                    \ + Shelf, a largely Positronic colony fleet, consists of over 1700 vessels including the 'One Leaky Bitch', current headquarters of Morpheus Shelf, Morpheus' non-Solar 'spin-out' corporation established last June. The fleet has continuously denied direct affiliation with the Almach Assocation, but was involved in a major drone attack on Solar vessels just nine months ago, which was claimed to be 'in error'." + +/datum/lore/codex/page/article80 + name = "02/12/64 - Spectralist Wardens to hold Vigils for Lost Fleet" + data = "In the wake of Shelf's sudden disappearence, Wardens across SolGov space have collectively agreed to hold services and vigils throughout dozens of systems for those who are doubtless worried for their loved ones aboard the missing ships. Spectralism, a synthetic-centric religion, finds it's roots within 'Haven', a small vessel and community that travels alongside the wider Shelf fleet, though considers itself a distinct entity. Its ministers, known as Wardens, tend to its adherants wherever they may be found.\ +

                    \ + Despite Haven's well-known and somewhat-controversial independence of identity from Shelf, all indications point to the Fleet's disappearance having taken the attached ship with it. All twelve Spectralist Elders are known to have been onboard Haven at the time of its disappearance, as well as numerous other significant Spectralists and other spiritual leaders thought to have been on Shelf. The potential loss of the entire upper organizational body could be devastating to the religion, who have long been instrumental in the synthetic rights movement.\ +

                    \ + Ceramica, a Warden operating out of Nyx, reached out for comment with the following: 'We are as worried as everyone else who calls Shelf their home, or who has lost contact with friends or family. You need not believe in the First Spark to have a place at your local vigil; We welcome everyone who may be hurting. This is our way, this has always been our way. We remind everyone to stay mindful, and to reach out to those you see struggling. In times like this, we cannot leave each other behind.'" + +/datum/lore/codex/page/article81 + name = "02/14/64 - Shelf Safe After 'Impossible' Jump!" + data = "The missing Shelf fleet has reappeared hundreds of light years beyond the Eutopia system just days after its sudden disappearance on Monday. Unencrypted bluespace transmissions from the fleet's new location - several months travel from its last known position in Almachi space - assured all who would listen that 'The fleet (had) arrived safe and sound.' and apologized for 'Any alarm (the fleet) may have caused'. Though no official report has been released nor information from Shelf itself, initial counts indicate that a number of Shelfican vessels may not have survived the 'impossible journey' intact.\ +

                    \ + Current analysis of the bluespace anomaly detected prior to Shelf's unexpected departure indicate that the same technology as employed by the Whythe Superweapon may have been used by Shelf to create 'a bluespace portal thus far inconceivable by modern science'. The revelation has sparked concerns that 'extreme mercurial elements' within Shelf may have been responsible for the hardware behind the Assocation's 'system eating' weapon.\ +

                    \ + Sifat Unar of the EIO has expressed particular concern that 'Such improbable technology relying on concepts deemed so staggeringly arcane that our very understanding of the laws of the universe had written them off as impossible - and to be applied in such callous ways without regard for life or perhaps even the fabric of reality, could only have been developed by machine minds that could threaten our very being.' Shelf has dismissed these claims as 'Scaremongering' and 'Just jealous that somebody else thought of it first', though they would not confirm nor deny their involvement in the development of the new bluespace portals." + +/datum/lore/codex/page/article82 + name = "04/11/64 - Whythe Breached - An End In Sight?" + data = "According to latest reports from the Almach front, Sol vessels have established a foothold in the debris fields of the desolate Whythe system, home to the enemy's controversial 'Superweapon'. Admiral Barka has confirmed that bombardment of the weapon has begun, but that 'shields are holding at present', owing to the vast construction's immense power generation capabilities, but remains confident that the 'siege' will come to an end within a few months, and that the Almachi now hold only 'minimal retaliatory capacity'.\ +

                    \ + Fleet sources have been quick to address a flurry of activity on the exonet which proposed that the superweapon might be deployed against Sol's core systems in a last-ditch effort to inflict damage on their former government. According to a statement posted just hours after the initial invasion press release, 'the Fleet is aware of all risk factors regarding the Whythe Superweapon, and following two long months of analysis are certain that constant, substantial pressure placed on the weapon's shield systems will render the offensive component of the weapon dormant until the hull can be breached.'\ +

                    \ + The general mood in the Colonial Assembly today is one of relief, as all signs point to a total collapse or surrender of Association forces in their home systems once their trump card has been captured or destroyed by SCG forces. Secretary West has already expressed congratulations to the troops, 'despite the lack of assistance from our allies, as the Shadow Coalition would have had us believe was necessary.'" + +/datum/lore/codex/page/article83 + name = "04/13/64 - Skrell Ultimatum Shocks Sol!" + data = "After three months of diplomatic iciness, the Skrellian Far Kingdoms have contacted both the SCG and Almach Association with one demand: Sign an armistice or prepare for war. Supported by an immense fleet movement through the recently quashed Xe'qua region, the Far Kingdoms have demanded an immediate end to hostilities, and 'incorporation of Almachi holdings as a Skrellian protectorate, under strict oversight and regulation of their research and activities.' By Skrell demands, the Fleet has two weeks to fully withdraw from the Almach region and any vessels on either side continuing to engage will be 'disabled, boarded, and have its crew arrested pending a formal peace agreement.'\ +

                    \ + A wave of outrage has swept the Colonial Assembly, with heated debate as to Sol's response defying all party lines. While Speaker ISA-5 has been widely criticized by political opponents for their 'overzealous trust in the Skrell', they have remained acquiescent to the Skrell's demands, stating that it may be the best way to avoid any further bloodshed and maintain good relationships with the Skrell. Conversely, a small group of hardliners from across the major parties headed by SEO Representative Colin Zula of Alpha Centauri, have formed a political coalition opposing any form of 'Surrender or appeasement in the face of foreign aggression', demanding Sol keep its forces in place and 'Finish off the Association before they can be allowed to wreak havoc unsupervised and uncontrolled.'\ +

                    \ + Surprising some, long-time supporter of the Almach War, MacKenzie West has established themselves as a figure of moderation in the Assembly, promising that the Icarus Front would pursue 'aggressive negotiations' with the Far Kingdoms in order to better understand their motivations and, if territory is to be ceded, 'ensure the Almachi are placed under a firm hand'. He notes that the Skrell have never adhered to Five Points policy, but that careful diplomacy has always ensured their 'less savoury tendencies' have never spilled over to Sol space." + +/datum/lore/codex/page/article84 + name = "04/16/64 - Assembly Shaken By Reshuffle" + data = "Following the shock announcement of the Skrellian demands on Monday, sixteen planetary representatives under the SEO's Colin Zula have announced the formation of a new political party named the 'Solar Sovereignty Party' under the banner of 'Independence from foreign demands'. The party consists of defectors from all three major parties; 9 Sol Economic Organization, 4 Icarus Front and 3 Shadow Coalition representatives have 'jumped ship' and will back Zula's demands to resume war with the Association, even if it means butting heads with the Skrell.\ +

                    \ + Rewi Kerehoma, chair of the SEO has expressed 'regret' that Mr. Zula and his supporters had chosen to splinter from the party, rather than work with 'More moderate, but like-minded' individuals across the SEO and wider Assembly. The SEO's official stance on the Skrellian demands are to demand close Solar oversight of any 'protectorate' to ensure that the region is 'policed to the highest standard, but that current Almachi citizens are afforded all the sapient rights they would be under the SCG.'\ +

                    \ + In the Shadow Coalition, a formal motion has been put forth by a small minority of representatives calling for the resignation of Speaker ISA-5, citing 'Total blindness to the political situation,' in the leadup to this week's events.\ +

                    \ + Meanwhile as Skrell vessels enter the Whythe system, the Solar Fleet has ceased bombardment of the Whythe Superweapon, handing off 'suppression' of the weapon to Skrell forces." + +/datum/lore/codex/page/article85 + name = "04/20/64 - Sol To Submit - Almach Subsumed Under Treaty of Whythe" + data = "Following 'intense' deliberations between the Far Kingdoms and representatives from the SCG, a decision has been reached to cede the secessionist Almach Association territory to the Skrell, and withdraw all forces from the region. The newly established Almach Protectorate will be subject to 'extremely stringent' oversight by Skrellian authorities, and international exchange of 'research and technologies' from the region will be banned 'in both directions', pending more a more exacting deal with the SCG. Sol is to be allowed 'regular inspections' of the territory on a schedule established by the Kingdoms.\ +

                    \ + The Solar envoy included the chairs of each of the major parties, senior ambassadors to major Skrell systems, and representatives from the Solar Fleet. The newly founded SSF were extended an invitation, but reportedly turned it down. A dejected looking MacKenzie West announced the terms of the treaty late this afternoon, stating that they had 'fought tooth and nail' for a fair deal for all parties involved, including civilians of all species now living under Skrellian occupation, and that 'those not directly involved in the corruption of humanity's sanctity should not be made to suffer for the actions of their superiors.'\ +

                    \ + Notably absent from deliberations were many key members of the Association's upper echelons, with 'lesser' diplomats taking the place of both Angessa Martei and Vounna's Naomi Harper. Almachi and Skrell sources were reluctant to explain these absenses, and it remains unclear as to whether they have been taken into Skrellian custody or remain at large.\ +

                    \ + Selma Jorg, Representative for Vir, has decried the treaty as a 'Sapientarian disaster in the making'. The former career diplomat has cited the 'general mistreatment of species deemed 'lesser'' as a recurring concern with the Skrell, and the complete occupation of majority human and positronic space, which unprecedented, could lead to 'conditions not much better than slavery' for those still living in the area. She has refrained from any direct accusations, pending the results of Sol's first permitted inspection." + +/datum/lore/codex/page/article85 + name = "04/22/64 - Skrell Impose New Regime in Relan" + data = "As agreed upon in the Treaty of Whythe, the Far Kingdoms have occupied the Relan system, putting an end to the Free Relan Federation. How the system will be organized is not entirely clear at this point. Despite the effective abolition of the Relanian government, Skrell presence in the system appears relatively light, and many of the scattered stations have no Skrell presence at all.\ +

                    \ + Former President Nia Fischer gave the following statement to a crowd gathered outside the Capitol Section of Carter: 'This is a dark time for all of us. I promise to you that, in my continued service to you, I will work with the Far Kingdoms to ensure that all of our people are treated well and our rights respected, and that we will arrive at a form of government that is acceptable to you.' The gathered crowd began to shout questions and accusations, and Fischer was quickly escorted back into the capitol by Skrellian guards without answering questions from the press or others. The crowd was quickly dispersed by Skrellian military police and Carter's own police force.\ +

                    \ + In the meantime, the governing of the system remains in the hands of the sparse occupation forces, aided by parts of the former Federation government.\ +

                    \ + In other news from the system, the Republic of Taron has negotiated a preliminary navigation and trade agreement with the Far Kingdoms, officially maintaining their neutrality despite the occupation of the majority of the system." + +/datum/lore/codex/page/article86 + name = "04/27/64 - Chaos in Relan" + data = "Simmering tensions in the Relan system have boiled over, with riots erupting on Carter, Abhayaranya, and New Busan. Since former President Fischer's brief address, small demonstrations against both the Skrell occupation and the collaborating elements of the former Federation government have taken place on many stations, but within the last day full-blown riots have broken out. While accurate information on the situation within the stations is rare, it is currently believed that the deaths of two protesters on Abhayaranya were the catalyst.\ +

                    \ + Damage to the three stations has been relatively light, with one major exception. A large fire broke out in the Capitol Section of Carter, killing at least 22, including former President Fischer, and wounding at least 74 more. Other casualties among rioters, police, and the populations of the stations are unknown at this point.\ +

                    \ + Other stations with significant permanent populations have been paralyzed by local inaction and the disloyalty of local police and security forces to the Far Kindgoms Skrell, and several with no Skrell presence have issued statements that they will not be accepting any military presence from the Far Kingdoms. It is unclear at this point if this represents the beginning of another major conflict within the system." + +/datum/lore/codex/page/article87 + name = "04/30/64 - Meralar Correspondent: Triumphant Return!" + data = "Celebrations have erupted throughout Tajaran space with the return of the PCMV Raniira's Grace, which has spent the last several months providing joint assistance with Solar military forces during the now-ceased hostilities with the Almach Association.\ +

                    \ + Khama Suketa enai-Lutiir, representative of the Tajaran Pearlshield Coalition, has provided the following statement:\ +

                    \ + 'It is great honour that we welcome the crew of the Raniira's Grace back to their homes at Mesomori. We have all seen the battle reports, and loathe as we are to celebrate bloodshed, sometimes it is a necessary evil in the pursuit of a greater peace, and the Grace pursued that peace with fervour and tenacity as befitting our kind, and exemplified what we can do when put to the test. She is but one ship, and yet one ship can make all the difference. Lives have been saved, and the crew has returned alive and well. This is merely the beginning of what we can accomplish in the cosmos.'\ +

                    \ + When asked for comment on the Treaty of Whythe, Suketa had this to say:\ +

                    \ + 'Indeed, the bittersweetness behind all of this. I will say, it is a... complicated and nuanced situation, as is so often the case with politics. We have our views on the matter, for sure, but now is not the time to formally engage with them. The Pearlshield is watching carefully, and when the dust settles and the terms of the treaty are exercised and accepted, we can take clearer view and action as possible and necessary. We still stand by the ideals we entered the war with, and we trust our allies to share them as ever. That will suffice for the moment.'" + +/datum/lore/codex/page/article88 + name = "05/13/64 - Agreement Signed at Ithaca Station, New Government In Place" + data = "In an effort to end the ongoing violence in the Relan system and regain the cooperation of 'insubordinate' stations, the Far Kingdoms Skrell have negotiated an agreement with community leaders and former Assemblypersons from a number of stations, including insubordinates, meeting at the largest of the insubordinates, Ithaca. Under these agreements, the Skrell will vacate most stations in the system, but will maintain a fleet base in Relan's Outer Belt for mutual defence, first at Carter and later at a dedicated station. Relan will have harsh restrictions placed on its military and will agree to formal diplomatic neutrality, but will be free to organize its own government under supervision and military occupation will end.\ +

                    \ + The mood on Ithaca has been tense as negotiations have gone on, but with the announcement of the results, crowds have packed the main thoroughfares and public spaces of the station in celebration. Francis Harp Yong, governor of Ithaca and a leading figure in the talks, addressed a crowd outside the Administration Section of Ithaca today. \ +

                    \ + 'The agreement we have signed with the Skrell today has given our people a new chance, free from the mistakes of the war and the baggage of the former Association. The war was not brought on us by our choice, nor the occupation we have recently faced. We want peace, and that is obvious even to those who were fighting against us weeks or days ago.\ +

                    \ + Make no mistake, that is what our agreement today symbolizes. A new era of peace for us, where we no longer have to worry about the threat of piracy or invasion. We can return to our homes, rebuild our stations, and forge a new future for ourselves and our children'\ +

                    \ + It was also announced that though the work of restoring order to the system is ongoing, they expect elections will be held for a new Assembly and President within the next few months, with the exact date announced once violence on the major stations has ceased and cooperation from the insubordinate stations is secured. Yong will head an interim government in the meantime." + +/datum/lore/codex/page/article89 + name = "08/15/64 - Almach Permits First Solar Inspection" + data = "Almost four months since its establishment as a Skrellian territory, the Almach Protectorate Government has extended its first formal invitation to Solar Confederate Government inspectors to ensure the fledgling state is complying with restrictions imposed by the Treaty of Whythe.\ +

                    \ + The composition of the official Solar Inspection Group has been a matter of much deliberation over the past several months, and owing to the ground to be covered now includes over 3,500 experts from a wide variety of fields. The bulk of the Group is comprised of Transgressive Technologies Commission agents, including the EIO, but also includes military officials, independent observers, and corporate representatives. The inclusion of the latter group spurred heated debate in the Colonial Assembly, but ultimately 'thought-leaders' from Ward-Takahashi, NanoTrasen, and Hephaestus Industries were admitted, while other interests will have to be satisfied to be represented by Sol Economic Organization liaisons.\ +

                    \ + The APG, currently based out of what is to be a neutral embassy on Carter in Relan pending the completion of the new government centre of Vigilance Station in Whythe, is not legally required to fully comply with Five Points regulation, though the Whythe terms ensure that any transgressive research is undertaken under the strictest guidelines. The Skrell have amiably agreed to ensure that any innovation by the protectorate is safe, controlled, and does not enter Solar territory. These terms are similar to those applied to such technologies developed by and for the Skrell themselves, whose expertise is considered unrivalled in the field, but has been widely criticised by certain human bioconservative groups.\ +

                    \ + The inspection is expected to take several weeks, and will begin tomorrow." + +/datum/lore/codex/page/article90 + name = "08/29/64 - Kaleidoscope Cosmetics Goes Trans-Stellar After Genix Merger" + data = "Personal care giant Kaleidoscope Cosmetics has been officially recognised by the Solar Galactic Exchange as a true Trans-stellar Corporation today, following a controversial merger with Genix Therapeutic Systems and expanding its corporate assets to over 20 key systems.\ +

                    \ + Genix, which will now be operating under the Kaleidoscope name while retaining some corporate autonomy, has come under some scrutiny from the Transgressive Technologies Commission in recent months following its aggressive acquisition of liquidated Almachi assets in the aftermath of the Association's dissolution in April. The company was cleared of suspected Five Points violations without fanfare just two weeks ago, and allowed to resume work on the development a variety of previously approved commercial genetic modification products.\ +

                    \ + Many cosmetic gene-modification products have been available for some time - primarily in the Almach Rim - but have remained targeted at a relatively niche market. These treatments, ranging from cosmetic anti-aging to 'fantasy' body features are now set to be marketed and available in 'hundreds' of Kaleidoscope clinics galaxy-wide in the coming months. The TTC has reported that they are 'Confident that no transgressive modification is being provided and that these modifications are strictly superficial'. They have officially wished Kaleidoscope's directors well in their ascent to the galactic stage." + +/datum/lore/codex/page/article91 + name = "09/21/64 - Almach Passes Inspection - Concerns Raised" + data = "The Solar Inspection Group has granted the Almach Protectorate a passing grade in the first official inspection of the territory under Skrellian rule, though ethical concerns have been raised by a number of independent observers involved in the process.\ +

                    \ + During the course of the month-long inspection, Skrell facilitators were cooperative in ensuring the SIG were given 'unlimited' access to all research and development facilities requested by Sol, as well as informing the group of numerous previously unidentified locations that had been flagged by the Almach Protectorate Government since assuming control of the region in April. Critics of protectorate have suggested that the 'official list' may not be as comprehensive as stated, but the SIG has stated that they are 'confident that no deception has been undertaken by the APG. We have observed no evidence of research being concealed from our teams, and to the best of our knowledge the only locations left undisturbed are those few still occupied by Association holdouts, beyond the control of either inspecting government.'\ +

                    \ + Also as a result of the inspection, concerns have been raised regarding the treatment of ex-Association citizens, particularly the often genetically modified residents of Angessa's Pearl. While Angessa Martei's location is still unknown, her legacy - a society described by some as a cult of personality with emphasis on the cult - poses a significant threat to Skrellian control of the Exalt's Light system, and the clash of 'strong ideologies' has allegedly resulted in mistreatment of detainees that, according to the official report 'would not be permitted in Solar jurisdictions'.\ +

                    \ + The Protectorate's passing grade opens the door for interstellar trade to resume between the two nations for the first time in several years. Manufacturing giant Ward-Takahashi has already released a public statement on its intent to deal with the APG, and several other trans-stellars are expected to follow suit." + +/datum/lore/codex/page/article92 + name = "09/24/64 - Kaleidoscope Announce Exclusive Almach Deal" + data = "Mere days after the announcement of the reopening of Almachi-Solar trade, the Kaleidoscope Cosmetics corporation has confirmed that they have secured 'exclusive rights' to genetic products produced by several major manufacturers in the Angessa's Pearl system formerly owned by The Exalt - the insular mercurial theocracy of Exalt's Light - and recently handed over to the Almach Protectorate Government.\ +

                    \ + The details of the company's trade agreement have not been made public, but have reportedly already been approved by both the APG and SCG. The promptness of the agreement suggests to some that negotitations had been underway well before the announcement of the Protectorate's passing grade. Though cooperation between trans-stellar interests and government entities is far from unusual, such dealings with foreign governments - such as those widely made as 'open secrets' with Eutopia - are considered distasteful by many within the Icarus Front.\ +

                    \ + The Angessan products Kaleidoscope intends to offer have also not been made public, but are all to be thoroughly screened by the Transgressive Technologies Commission before being made available, though 'failing' proposals may be approved for use exclusively within Protectorate territory." + +/datum/lore/codex/page/article93 + name = "10/04/64 - Yong Wins Relani Elections" + data = "Despite concerns about domestic unrest and potential Skrellian interference, elections in Relan have gone ahead, selecting a new President and System Assembly. Francis Harp Yong, leader of the interim government and previous governor of the city-station Ithaca, known for his leading role in negotiating the end of Skrellian occupation and calming riots and rebellions earlier this year, has won the Presidency in a landslide, receiving nearly seventy percent of the vote. His party, the Spacer Union, has secured a narrow majority in the Assembly. The New Federalists, led by former member of the Harper government Odoacer Mieville, are the largest opposition party.\ +

                    \ + Yong has promised to reinforce Relani neutrality and rebuild the pre-war social system, forming a coalition of labor, technoprogressives, and anti-war activists. His victory likely means an end to the unrest that has plagued Relan since the end of the war with Almach." + +/datum/lore/codex/page/article94 + name = "12/23/64 - Two Vessels Feared Lost In Isavau's Gamble" + data = "Two Solar salvage vessels have been declared missing this week while undertaking operations in the remote Isavau's Gamble system. Locals also report that several smaller craft from the Eutopia system have 'vanished' under similar circumstances. Authorities are treating the disappearances as potential pirate attacks.\ +

                    \ + The XIV Sri Chamarajendra with crew of 32, and IIV Reimarus with 9 are both reported to have lost contact with the system's spaceport while undertaking far-orbit salvage operations on decommissioned and abandoned facilities, including the ILS Harvest Moon which detonated with all hands earlier this year. The disappearances have spurred the system government to request greater anti-piracy support from the SCG, which has been much reduced since the Almach War and increased tensions on the Hegemony border.\ +

                    \ + While Proximal to the independent and often 'lawless' Eutopia system, as well as the Rarkajar Rift, infamous prowl of the human-tajaran Jaguar Gang, the Isavau's System has rarely been a direct target for such large-scale apparent hijackings, and no group has yet claimed credit or demanded ransom." + +/datum/lore/codex/page/article95 + name = "12/27/64 - NanoTrasen Accused In Satisfaction Poll Fixing" + data = "The results of an annual system-wide corporate employee satisfaction poll in Vir have been called into question after a leak of internal employee contracts by an anonymous whistle blower. According to verified documents provided to the Vir News Network, NanoTrasen employees on annual employment contracts up for renewal are instructed to fill out an appended form expressing 'Extremely High' satisfaction with their employment at the company, or the contract will be deemed 'incomplete' and thus invalid.\ +

                    \ + NanoTrasen have been winners of the Vir Happy Employee Award for the 7 years running as of this year, and as such the legitimacy of the award, which has been run in major systems by the Sol Economic Organization since 2503, has been called seriously into question. The SEO has expressed regrets regarding the alleged fixing, but has stated that participating corporations are within their right to encourage employees to vote in a particular way.\ +

                    \ + According to NanoTrasen, the so-called 'Satisfaction Clause' in their contract renewal process is entirely permissible from a legal standpoint, and the company has no plans to make any changes at this time. When approached for comment, one anonymous employee stated that they 'had never even heard of' the award." + +/datum/lore/codex/page/article96 + name = "01/03/65 - VirGov Seals Security Deal With Local Firm" + data = "Governor Lusia Hainirsdottir has announced sweeping changes to government law enforcement in Vir, lynchpinned by the signing of a five-year contract with home-grown independent security and arms firm, Hedberg-Hammarstrom. The private enterprise will be assuming 'key duties' in local law enforcement and system security, in a move that Hainirsdottir says 'will save millions in taxpayer money, and encourages local, Virite businesses that we trust them every step of the way'.\ +

                    \ + While many colonies and facilities in Vir rely wholly on in-house security forces and will continue to do so, Hedberg-Hammarstrom is set to take over government patrol duties throughout the system, as well as administrating a large portion of previously government-run local law enforcement agencies including policing and wilderness garrisons.\ +

                    \ + CEO Gunnar Hammarstrom has reassured current SifGuard members at all levels that they need not fear for their jobs, and that for many the changes will be as simple as a slight change in uniform and a different name on their paychecks, but that H-H agents will be assuming managerial roles in most locations." + +/datum/lore/codex/page/article97 + name = "01/05/65 - Top NanoTrasen Executive Injured In Sif Bombing" + data = "Calvert Moravec, Chief Operations Officer for NanoTrasen Vir remains in a critical condition following a suspected assassination attempt on Saturday. The former gubernatorial candidate had been making a speech at a small NanoTrasen facility known for a prior terrorist attack by Boiling Point forces two years ago, when a small explosive device detonated close to the executive's leg, rendering him unconcious.\ +

                    \ + NanoTrasen security have elected to investigate the bombing internally, refusing to cooperate with SifGuard authorities on the grounds of disagreements with newly established Hedberg-Hammarstrom management. Mr. Moravec, nephew of NanoTrasen CEO Albary Moravec had reportedly been speaking out against H-H's involvement in national policing when the blast occurred.\ +

                    \ + Commander Spradling of NanoTrasen Internal Security has issued a company-wide active search order for one member of the audience who remains unaccounted for named 'Lae Vu', who is believed to be a journalist or posing as such, accompanied by images from communicator footage taken at the event.\ +

                    \ + This amateur footage also shows one member of the audience shouting an anti-corporate slogan and throwing a small electronic device at Moravec before being escorted from the room, just moments prior to the explosion. Spradling has stated that this individual, who is believed to be a disgruntled veteran of the Sif Defense Force, remains one of several persons of interest, but that the thrown item does not appear to be connected directly with the explosive device.\ +

                    \ + Reports from witnesses suggest that Mr. Moravec made a temporary recovery thanks to the fast action of medical staff aboard the NLS Southern Cross, but that a few hours later he appeared to suffer from a seizure, after which he was placed into a medically induced coma that he is yet to recover from. Unverified images from the Cross appear to show a disoriented individual resembling Moravec self-medicating with a cocktail of prescribed painkillers and alcoholic beverages, though the company strongly denies their veracity." + +/datum/lore/codex/page/article98 + name = "02/02/65 - NanoTrasen Announces Employee-Led Advisement Scheme" + data = "Trans-stellar giant NanoTrasen has today announced the launch of their brand-new 'NanoTrasen Employee Representation Committee' scheme, which will allow employee-selected representatives to have a say in company policy on a local basis. Initially launching as a pilot in 'a few key regions', the NERC is intended to provide an in-house method for those working for NanoTrasen to 'address grievances and provide valuable input without resorting to radical means'.\ +

                    \ + Vir is one of three size regions of varying scale but similar company presence selected for the pilot program, alongside the 'Deep Bowl' - including Stove, Viola and New Singapore, and 'South America', on Earth. Elections are expected to take place over the coming weeks, with each region electing representatives from core facilities in their locale. The Vir Branch will be expected to select five employees, one from each of; their New Reykjavik Head Office (Colloquially know as 'CentComm'), Karan colony the NCS Northern Star, Kalmar-based NMB Gullstrand medical research center, Sivian way-station NLS Southern Cross, and the company's sprawling Ekmanshalvo Fabrication Plant.\ +

                    \ + The Representation Committee is set to have quite significant powers over their region's day-to-day operations, ranging from Standard Operation Procedure, to security enforcement, to interior design." + +/datum/lore/codex/page/article99 + name = "02/06/65 - Calvert Moravec Dead At 58, Announces Withdrawal From Public Life" + data = "Just over a month since his injury in a brazen assassination attempt, NanoTrasen Vir's Chief Operations Officer Calvert Moravec has announced his 'temporary withdrawal' from public and corporate matters, in an inspiring speech given from his post-cloning recovery ward. The noted corporate executive passed away on Friday, following a long comatose period, but is expected to make a full recovery.\ +

                    \ + Mr. Moravec has stated that he believes a spell 'away from the public eye' will do wonders for his recovery, and 'expects to make a return some time in the coming years'. However, Moravec has reaffirmed that his stance on corporate policing, which is believed to have been the reason for the January bombing, remains unchanged; 'The refusal of Hedberg-Hammarstrom to allow our security teams to access government criminal records has been a significant hindrance to the investigation into my death. Allowing just one corporation exclusive access to this data is unequivocally wrong.'" + +/datum/lore/codex/page/article100 + name = "02/08/65 - NERC Campaigning Begins in Vir" + data = "NanoTrasen's latest employee initiative, the NanoTrasen Employee Representation Committee is set to hold their first in-house election this coming weekend, after 'respectable' signup rates for the positions across all participating Vir-based facilities.\ +

                    \ + According to those interviewed at a few sites, campaigning may be mixed. One potential committee member for the Gullstrand Medical Center described the competition as 'cut-throat' with staff looking to be split between the selection of already respected chief medical experts, and 'more represenative' service staff. Conversely, one worker at the Ekmanshalvo Fabrication Plant complex stated that he 'would probably just vote for whoever I've heard of.'\ +

                    \ + Final voting will take place on Saturday the 13th of February, with results announced by the following Monday." + +/datum/lore/codex/page/article101 + name = "02/11/65 - Top Astronomers Announce 'Alarming' Tachyon Downtick" + data = "The Galactic Survey Administration has today released a 'high priority' report on what is being described as a 'significant decline in core tachyon density' throughout the known galaxy. According to recent findings tachyon deterioration, which had previously been 'negligible' has undergone a 'rapid acceleration' which could begin to seriously affect interstellar transit in as soon as 30 to 50 years.\ +

                    \ + Tachyons are a naturally occurring particle present in varying density which form the primary mechanism for bluespace drive operation. In simple terms, the higher the density of tachyons, the more efficiently starship engines are capable of operating. Low-density regions such as the Rarkajar Rift have long posed a challenge to interstellar trade, and the expansion of such regions could prove devastating to galactic unity.\ +

                    \ + FTL industry leaders Focal Point Energistics have already announced 'immediate investigation' into the GSA's findings after taking a blow on the stock exchange, and have guaranteed a 'commitment to future-proofing' in all forthcoming products." + +/datum/lore/codex/page/article102 + name = "02/15/65 - NanoTrasen Announces First NERC Members" + data = "Following Saturday's internal election, NanoTrasen Vir has elected the first five members for its new Employee Representation Committee. These individuals are expected to hold the position for one month:\ +

                    \ + For the New Reykjavik Head Office Iraluq James, a remote personnel operative, won the position on a platform of employee morale, team-building and employee-employer conflict resolution.\ +

                    \ + On the NCS Northern Star, NanoTrasen's main colony station in Vir Victoria Bell, a cybernetic chef and hardshell operator won her place with promises of increased regulatory transparency and clarity, including the publication of an in-house magazine.\ +

                    \ + At the NMB Gullstrand medical science center in Kalmar, a close race between top doctors was upset by the election of a station security commander, Wish Elara-Voight who promises 'improvements to the safety and security of NanoTrasen facilities with a focus on enforcing current SoP and CorpReg policies and placing new procedure in the handbooks.'\ +

                    \ + The waypoint station NLS Southern Cross selected a cybernetic Unathi candidate, researcher Dr. Haven Rasikl to represent them on the basis of encouraging interdepartmental cooperation and communication.\ +

                    \ + Finally, the Ekmanshalvo Fabrication Plant has selected fabrication programmer Terazon Norddahl, who promises to push for overhauls and modernization of the company's internal transport and support systems, including shuttle scheduling." + +/datum/lore/codex/page/article103 + name = "03/10/65 - GSA Confirms Presence Of 'New' Deep-Space Threat" + data = "This morning the Galactic Survey Administration released confirmation that a previously unknown deep-space dwelling species was indeed responsible for the disappearance of five Extraplanar Discovery Division vessels early last year, three of which have returned adrift over the past several days. The species, colloquially dubbed the 'Bluespace Bugs' after initial reports from the recovered EDD ships, are believed to be capable of manipulating matter in a manner reminicent of experimental bluespace technology.\ +

                    \ + The three 'returning' vessels are said to have been 'significantly off-course', with their points of galactic re-entry differing vastly from their initial points of egress, and final programmed return routes. Each ship having departed on routes from Sol, the SCG-E Bungaree was reported adrift by Almach Protectorate officials close to the Vounna system, the SCG-E Ketumati severely damaged on a collision course with the Erebus star, in the Nyx system, and the SCG-E Mag Mell collided with the garden world of Sif, in Vir. The Gagarin and Xu Fu remain unaccounted for, but due to the proximity of final contact, their disappearance has also been ascribed to the 'bugs'.\ +

                    \ + Investigation of the recovered vessels each indicated signs of a brief struggle, always following reports of unexplained equipment disappearances and equipment failure. No crew, or crew remains have been found, with the exception of a single unidentified human thumb aboard the Bungaree.\ +

                    \ + Additionally, each ship has been identified as having undergone some degree of material alteration, with elements ranging from hull plating to crew belongings having taken on a crystaline form dubbed 'Magmellite' after the ship most thoroughly 'reconstructed'. It is unclear whether the mineral is somehow secreted by the alien species, or merely a product of the same environment.\ +

                    \ + GSA sources have stated that there is no current evidence that the insectoid creatures - identified only from scattered descriptions left from missing EDD personnel - are in any way sapient or malicious, and the lost vessels are thought to have merely disturbed a scattered array of endemic populations far from the galactic plane. Precisely why the species has only been encountered in the past year, and now all at once, is not yet known." + +/datum/lore/codex/page/article104 + name = "05/24/65 - Oculum Apologizes for Interstellar Relay Outages" + data = "Week-long difficulties with interstellar transmissions in several central star systems due to an 'unexpected behaviour' in bluespace relays 'should be resolved soon' according to telecoms giant Oculum Broadcast. The company has apologized to customers for connection speed drops as much as 80% which have rendered certain live systems near impossible for many customers, including disruption of telecast Colonial Assembly meetings on Luna which have been temporarily put on hold in order to allow representatives time to attend in person.\ +

                    \ + Customers may experience reduced speeds compared to prior service, but have been promised a two-year price freeze on exonet service packages for home and business users on most major providers in participating areas. In-system communications remain unaffected.\ +

                    \ + Initial reports that the finale broadcast of Game of Drones was 'rendered unwatchable' by connection issues proved to be unfounded as severe lag and audio distortion were confirmed as 'part of the creator's artistic vision for the story's end'." + +/datum/lore/codex/page/article105 + name = "06/07/65 - 104 Feared Dead In Oasis Tourist Shuttle Incident" + data = "The Vir Governmental Authority has confirmed the destruction of a small interstellar tourist vessel departing the Vir system. The ITV Relaxation IX operated by Thousand Palms Hotels, was bound for a popular resort in Oasis and reportedly 'split in two' shortly after confirming system bluespace departure with Vir space traffic control, exposing all passenger compartments to space and killing at least 94 of the 108 people onboard.\ +

                    \ + Four crew members recovered from forward sections are being treated for 'non-life-threatening' pressure injuries at a nearby medical facility, and ten individuals believed to have been situated at the rear of the ship remain unaccounted for. The exact cause of the break-up is yet to be determined, but initial accounts from surviving crew members describe the bluespace drive of the ship as having 'taken off by itself' at such speed that the rear of the fuselage was 'cut clean off' with no regard for structural elements. Foul play is not currently suspected.\ +

                    \ + The VGA believes that the chances of finding further survivors are 'extremely slim', though efforts to recover the rear section, which was 'sling-shot' into interstellar space, are underway. Gilthari Exports, Thousand Palms' parent company have temporarily suspended operation of all vessels fitted with similar bluespace drives, and Major Bill's Transportation is expected to follow suit. Wulf Aeronautics was unavailable for comment at this time." + +/datum/lore/codex/page/article106 + name = "07/09/65 - 'Bluespace Bugs' Linked To Almach Tech" + data = "Initial public reports on the extraplanar species commonly known as 'Bluespace Bugs' has proposed that the first recognizable signs of their activity within observable space, coincide precisely with the development - and particularly the test-firing - of the Whythe Superweapon, and that there may be a direct link between the two. The report, released by the Galactic Survey Administration this afternoon hypothesizes that the newly developed bluespace manipulation techniques used in Whythe may have acted as a signal to the deep-space dwelling creatures in a manner similar to moths attracted to artificial light. The GSA is currently collaborating with the Almach Protectorate Government to investigate the potential link further.\ +

                    \ + Additionally, findings from analysis of both inorganic and biological samples collected from the three recovered Extraplanar Discovery ships believed to belong to the 'Bugs' has excited much of the scientific community, with news that the insect-like aliens and their apparent dietary waste-product Magmellite may be composed in a manner completely unlike any life previously encountered in the known galaxy. The findings may rewrite our understanding of biology and material science, though a full specimen is desired to confirm these early findings.\ +

                    \ + The creatures have been given a tentative scientific name X Extraneus Tarlevi, after Captain Volmer Tarlev of the SCG-E Ketumati whose recorded descriptions were instrumental in establishing a basic understanding of the species' behavior. Researchers currently believe that the Bluespace Bugs are merely a form of bulk-feeding omnivore attracted to the EDD vessels in deep space by their bluespace drives, and that the loss of the ships was merely unfortunate happenstance rather than deliberate, malicious attack. Studies are already underway to determine methods that might prevent further incidents of this types before any further extraplanar missions are approved." + +/datum/lore/codex/page/article107 + name = "08/15/65 - Enigmatic Arkship Sighted After 200 Year Voyage" + data = "Recent reports from the New Singapore-based Exoplanar Traffic Observation Committee, have claimed historic extraplanar arkship, the VHS Rodnakya, has changed trajectory and may be approaching the galactic plane once more.\ +

                    \ + The ETOC, comprised of mostly exoplanar and shuttlecraft enthusiasts in the Bowl, have been tracking both government and private survey expeditions since the 2450's, reporting the approximate locations and assumed status of vessels for public record. The VHS Rodnakya has been a major point of interest among ETOC members since its formation, sometimes dominating discussions entirely, and the focus for numerous unverified theories.\ +

                    \ + The Arkship and its support fleet, known as 'Vystholm' was constructed in the early 2300's by Stanislava Dalibor, and left the galactic plane in response to the abolition of the SCG's First Contact Policy that demanded the capture and interrogation of unknown sapient aliens. It has not had any official contact with the galactic community since. Largely known for radical and since-outdated views on non-human intelligent life, their original crew was known to include a number of early Icarus Front extremists which may have fermented into a dangerous ideology after 200 years of isolation.\ +

                    \ + This alleged trajectory change has sparked excitement among the committee, including a flurry of completely unsupported reports of spies infiltrating Solar society, from the corporate workforce to major government bodies.\ +

                    \ + The Vir News Network does not endorse the unverified claims of the Exoplanar Traffic Observation Committee." + +/datum/lore/codex/page/article108 + name = "09/12/65 - Gateway Transport Suspended Amidst Safety Concerns" + data = "Nine of the galaxy's top trans-stellars have announced immediate suspension of bluespace gateway transit services following a report by Wulf Aeronautics indicating that some of the same tachyon instabilities affecting their faster-than-light engine technology may have even more severe reprecussions for rapid point-to-point teleportation that could result in 'significant decrease in matter' during rematerialization that could result in customer death, disfigurement, or loss of luggage.\ +

                    \ + Dismissing accusations that this was a move to bolster a weakened spacecraft travel industry, Focal Point Energistics, the original developers of modern gateway technology were first to announce their suspension of service on all first-party operated access points. Major contract operators, including NanoTrasen, Ward-Takahashi, and Gilthari Exports have followed suit citing a desire for caution when dealing with premium employee transport.\ +

                    \ + NanoTrasen are the Vir system's leading operator of gateway transport, offering 'luxury, near-instant interplanetary commutes' between most major company facilities, and the Vir Interstellar Spaceport. The company says it will be removing many smaller gate installations for 'full examination and required enhancements' and expects the service to return 'within a few weeks'.\ +

                    \ + Major Bill's Transportation have announced an increased frequency of service to affected NanoTrasen locations for the duration of the gateway suspension." + +/datum/lore/codex/page/article109 + name = "10/23/65 - 12th Missing Ship Prompts Official Isavau Response" + data = "After a shocking 12th interstellar vessel was declared missing in the Isavau's Gamble system earlier this week, the SCG has formally announced the launch of an official investigation, including the deployment of a significant Fleet security presence in the region. The lost ship, the HFV El Cid is recorded as having a crew complement of eight, pushing the total missing people in the system over the past year over one hundred and fifty. A contingent of around twenty SCG vessels - search and rescue, salvage, and armed warships - has been dispatched to the region to take over from near-absent local investigators, but is not expected to arrive for at least a month due to worsening FTL flight conditions.\ +

                    \ + The El Cid is believed to have been hauling highly secure cargo internationally, which may have been the final deciding factor in launching a confederation-level investigation into the abnormal number of appearances, and the SCG has come under quick criticism for their apparent prioritization of lost cargo over sapient lives. Fleet Admiral Silvain Barka, celebrated veteran of the Almach War, has stated 'Lives are our number one concern. The matter of the Hephaestus cargo has simply moved our timeline forward due to concerns that if high-grade arms were to fall into pirate hands, the situation in Isavau's Gamble could rapidly worsen. We want to avoid that, and bring back as many of the missing crew as possible.'\ +

                    \ + While piracy has long been a concern in the Bowl, with the 'Jaguar Gang' pirates making their home in the region, and a general uptick having occurred since the beginning of the Almach Crisis, it is unusual for such a high proportion of missing ships crews to remain lost without report of recovery or ransom demands. Accusations of brutality have been levied at the Vystholm flotilla, despite their improbable distance from the star system. To date the XIV Sri Chamarajendra remains the single largest loss of life, at 32 believed dead during a large salvage mission late last year." + +/datum/lore/codex/page/article110 + name = "10/26/65 - 'Bluespace Bug' Confirmed Activity Near Tajaran Space" + data = "The following address is from Khama Suketa enai-Lutiir, representative of the Tajaran Pearlshield Coalition:\ +

                    \ + 'Good day, everyone. We would like to put the rumor mills to rest lest they get out of hand. Eleven Solar days ago, the Silk system's perimeter defenses picked up what appeared to be a derelict Vox raiding ship that had drifted into range. Security forces boarded the craft and found clear signs of fighting within the crew compartments, but no traces of the crew themselves. The craft's bluespace drives were notably ripped out of the hull, and it's believed the craft had been drifting for up to nearly two Solar weeks prior, as it has been identified as the 'Skiskatachtlakta', belonging to a well-known Vox raiding group our cousins in the Arrathiir system have been contending with for a few years now, and that was their last confirmed contact with it.\ +

                    \ + 'Most importantly however, and the key reason for this address, is a significant portion of the ship's hull, particularly concentrated around the site of its bluespace drives, has been confirmed to've been converted to the material known as 'Magmellite', related to the so-called 'bluespace bugs' or X Extraneus Tarlevi that have been of note outside our borders. To acknowledge and assuage any natural alarm on our own part, there is no indication any part of the incident took place on our side of the Rift, and we are deploying additional reconnaissance and recovery ships, drones and general personnel throughout controlled space to keep a watchful eye, and we are negotiating with Solar forces in Silk to expand our presence as appropriate. We are currently analyzing the Magmellite samples recovered from the Skiskatachtlakta and will be sharing any findings with our fellows in the scientific sphere, as well as returning the ship's plundered riches if and when possible.\ +

                    \ + 'There is currently no cause for concern beyond the understandable. If and when there is, we will rise to the occasion as always. Together, we will discover the truth of these strange times we find ourselves in. Until then, be safe, and be well. May our stars shine upon us all.'" + +/datum/lore/codex/page/article111 + name = "01/14/66 - Unathi Border On Alert After Manoeuvre Scare" + data = "The Solar garrison at Abel's Rest remains on high alert following a critical near-miss scenario resulting from an unannounced Unathi fleet arrival at the disputed system's perimeter alert zone, which has since been determined to be not a deliberate act of war. According to the Moghes Hegemony the fleet is 'part of a re-evaluation of key troop deployments, that will be of mutual benefit to peace in the region'.\ +

                    \ + The arriving fleet, comprised of mostly troop transport vessels, remained in distant orbit of the star for seven hours before diplomatic contact could be established, during which time the SCG garrison remained at battle-ready stance. This marks the closest proximity to open conflict in Abel's Rest since the end of the Unathi War over 45 years ago. SolGov peacekeepers have confirmed decreased planetside garrison activity over the past several months, and key Hegemony warships have departed in recent weeks.\ +

                    \ + It remains impossible to determine the Unathi's true intentions as such a withdrawal could preface devastating planetary bombardment, or invasion elsewhere, and some members of the Icarus Front have called for immediate redeployment to the front in response. The SCG Fleet has assured the public that relations with the Hegemony have 'never been better' and that long-range sensors indicate a withdrawal from the entire border region on a far wider scale." + +/datum/lore/codex/page/article112 + name = "02/13/66 - Isavau's Gamble Fleet Report 'All Silent'" + data = "Early reports from the SCG contingent sent to investigate potential pirate activity in the Isavau's Gamble system include shocking descriptions of a system 'gone silent', with supposedly zero manmade signals originating from any ship or station in the entire area. Prior loss of contact several days prior had been ascribed to naturally occurring background noise 'overwhelming' the system Spaceport's bluespace relays, which had been operating at reduced efficiency due to the ongoing tachyon downtick.\ +

                    \ + While messages from the SCG-D Brazen Bull were similarly weakened by what has been described as a bluespace 'hum', no immediate cause for alarm was raised. The fleet indicated that they were proceeding to investigate the Isavau International Spaceport and hoped to make direct contact with the crew within a matter of hours.\ +

                    \ + While telecommunications outages were a forecasted consequence of the tachyon downtick, highly focused and widespread instances such as that occurring in Isavau's Gamble were not, which has cast doubt on the predictive models being used, and raised concerns of additional factors at play. A newly observed phenomena from the Brazen Bull's report, 'blue points of light suspended in space with no discernible origin' has led to some speculation of a re-concentration of tachyon particles which some hope may hold the key to preventing further deterioration of the energetic landscape." + +/datum/lore/codex/page/outage + name = "02/14/66 - 02/15/74 - RELAY DATA OUTAGE" + data = "Article data lost for 2922 day period. Reason: Damage sustained to Oculum systems during Skathari Incursion. Please contact your administrator for details." + +/datum/lore/codex/page/article113 + name = "08/17/74 - Vox Continue To Show Alarming Divergence In Behaviour; Experts Baffled" + data = "Reports published by the Interspecies Joint Anti-Piracy Initiative this month continue to support an unsettling trend; the Vox, violent reptoavian aliens best known for engaging in rampant piracy, are simply not behaving as expected. Tactical and strategic models built painstakingly over the last three centuries are now showing accuracy ratings as low as 3%, with a rapidly growing number of raids either occurring outwith predicted regions or timeframes, or more alarmingly, not being predicted at all.\ +

                    \ + 'Something has altered their priorities,' said Wataru Murata, the head of the Joint Operations Group in charge of tracking Vox activity. 'In some areas we're seeing vastly more unarmed transport, mining and salvaging vessels, than we were as recently as two months ago, and in others, we're seeing strength and frequency of raiding groups as much as tripling. It's a very complex situation.'\ +

                    \ + 'They've never been so unpredictable before,' agreed senior naval officer Akira Doi. 'I understand that there is currently no official theory on why this is happening, but I believe it all goes back to the Skathari Incursion.'\ +

                    \ + Many leading scientists are skeptical that the Incursion, despite having thrown much of the galactic community into chaos over an eight-year period of conflict with the insectoid Skathari, could have seriously disrupted the Vox in the same way, given the relatively low indications of Skathari presence in known Vox operating territories, but Admiral Doi strongly disagrees.\ +

                    \ + 'The Skathari are aggressive to an unprecedented degree,' he explained, 'and they had the ability and numbers to annihilate entire communities. I see no reason why that shouldn't have applied to the Vox.'\ +

                    \ + While some colonies and stations welcome this reprieve from raids and piracy, others are finding themselves under renewed pressure, with resources and labour withdrawn on the advice of apparently unreliable modelling. A representative of the Pearlshield Coalition territory of Arrathiir had this to say regarding the sluggish reallocation of resources after the latest wave of raids:\ +

                    \ + 'On the frontier we are no strangers to fighting and rebuilding after raider attacks, but without reliable resupply of fuel and munitions from the home worlds, we cannot even field our ships. This is costing us dearly.'" + +/datum/lore/codex/page/article114 + name = "09/25/74 - 'Emerald Skies' Conspiracy Protest Shuts Down New Reykjavik Mall" + data = "This weekend saw the Vinterlykke Shopping Center shut down after a surge of protesters crowded the mall. Vinterlykke is known as the largest mall in New Reykjavik, renowned for its expansive selection of shops, restaurants, and entertainment options.\ +

                    \ + According to witnesses, protesters crowded the major walkways, preventing shoppers from passing and leaving some stranded in shops to avoid the crowd. Many shops barred and locked the storefronts in response, an unnamed employee of one store commenting, 'That many people <...> you never know if it's going to get out of control.'\ +

                    \ + The protesters chanted with signs regarding NanoTrasen's exclusive rights in studying the Sif Anomalous Region, demanding that studies into the anomalous properties be public knowledge. The march appears to be headed by the Skathari conspiracy group 'Emerald Skies', known in recent months for many other smaller protests regarding the government response to the Incursion, and beliefs that Skathari are more intelligent than the public has been informed.\ +

                    \ + The disruption was eventually cleared out by mall security forces before the crowd could grow out of control. It is reported that Vinterlykke will reopen on Monday with additional security measures in place." diff --git a/code/modules/maps/swapmaps.dm b/code/modules/maps/swapmaps.dm index 5ec962f5c91..629ea0042c4 100644 --- a/code/modules/maps/swapmaps.dm +++ b/code/modules/maps/swapmaps.dm @@ -1,678 +1,678 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/* - SwapMaps library by Lummox JR - developed for digitalBYOND - http://www.digitalbyond.org - - Version 2.1 - - The purpose of this library is to make it easy for authors to swap maps - in and out of their game using savefiles. Swapped-out maps can be - transferred between worlds for an MMORPG, sent to the client, etc. - This is facilitated by the use of a special datum and a global list. - - Uses of swapmaps: - - - Temporary battle arenas - - House interiors - - Individual custom player houses - - Virtually unlimited terrain - - Sharing maps between servers running different instances of the same - game - - Loading and saving pieces of maps for reusable room templates - */ - -/* - User Interface: - - VARS: - - swapmaps_iconcache - An associative list of icon files with names, like - 'player.dmi' = "player" - swapmaps_mode - This must be set at runtime, like in world/New(). - - SWAPMAPS_SAV 0 (default) - Uses .sav files for raw /savefile output. - SWAPMAPS_TEXT 1 - Uses .txt files via ExportText() and ImportText(). These maps - are easily editable and appear to take up less space in the - current version of BYOND. - - PROCS: - - SwapMaps_Find(id) - Find a map by its id - SwapMaps_Load(id) - Load a map by its id - SwapMaps_Save(id) - Save a map by its id (calls swapmap.Save()) - SwapMaps_Unload(id) - Save and unload a map by its id (calls swapmap.Unload()) - SwapMaps_Save_All() - Save all maps - SwapMaps_DeleteFile(id) - Delete a map file - SwapMaps_CreateFromTemplate(id) - Create a new map by loading another map to use as a template. - This map has id==src and will not be saved. To make it savable, - change id with swapmap.SetID(newid). - SwapMaps_LoadChunk(id,turf/locorner) - Load a swapmap as a "chunk", at a specific place. A new datum is - created but it's not added to the list of maps to save or unload. - The new datum can be safely deleted without affecting the turfs - it loaded. The purpose of this is to load a map file onto part of - another swapmap or an existing part of the world. - locorner is the corner turf with the lowest x,y,z values. - SwapMaps_SaveChunk(id,turf/corner1,turf/corner2) - Save a piece of the world as a "chunk". A new datum is created - for the chunk, but it can be deleted without destroying any turfs. - The chunk file can be reloaded as a swapmap all its own, or loaded - via SwapMaps_LoadChunk() to become part of another map. - SwapMaps_GetSize(id) - Return a list corresponding to the x,y,z sizes of a map file, - without loading the map. - Returns null if the map is not found. - SwapMaps_AddIconToCache(name,icon) - Cache an icon file by name for space-saving storage - - swapmap.New(id,x,y,z) - Create a new map; specify id, width (x), height (y), and - depth (z) - Default size is world.maxx,world.maxy,1 - swapmap.New(id,turf1,turf2) - Create a new map; specify id and 2 corners - This becomes a /swapmap for one of the compiled-in maps, for - easy saving. - swapmap.New() - Create a new map datum, but does not allocate space or assign an - ID (used for loading). - swapmap.Del() - Deletes a map but does not save - swapmap.Save() - Saves to map_[id].sav - Maps with id==src are not saved. - swapmap.Unload() - Saves the map and then deletes it - Maps with id==src are not saved. - swapmap.SetID(id) - Change the map's id and make changes to the lookup list - swapmap.AllTurfs(z) - Returns a block of turfs encompassing the entire map, or on just - one z-level - z is in world coordinates; it is optional - swapmap.Contains(turf/T) - Returns nonzero if T is inside the map's boundaries. - Also works for objs and mobs, but the proc is not area-safe. - swapmap.InUse() - Returns nonzero if a mob with a key is within the map's - boundaries. - swapmap.LoCorner(z=z1) - Returns locate(x1,y1,z), where z=z1 if none is specified. - swapmap.HiCorner(z=z2) - Returns locate(x2,y2,z), where z=z2 if none is specified. - swapmap.BuildFilledRectangle(turf/corner1,turf/corner2,item) - Builds a filled rectangle of item from one corner turf to the - other, on multiple z-levels if necessary. The corners may be - specified in any order. - item is a type path like /turf/wall or /obj/barrel{full=1}. - swapmap.BuildRectangle(turf/corner1,turf/corner2,item) - Builds an unfilled rectangle of item from one corner turf to - the other, on multiple z-levels if necessary. - swapmap.BuildInTurfs(list/turfs,item) - Builds item on all of the turfs listed. The list need not - contain only turfs, or even only atoms. - */ - -swapmap - var/id // a string identifying this map uniquely - var/x1 // minimum x,y,z coords - var/y1 - var/z1 - var/x2 // maximum x,y,z coords (also used as width,height,depth until positioned) - var/y2 - var/z2 - var/tmp/locked // don't move anyone to this map; it's saving or loading - var/tmp/mode // save as text-mode - var/ischunk // tells the load routine to load to the specified location - - New(_id,x,y,z) - if(isnull(_id)) return - id=_id - mode=swapmaps_mode - if(isturf(x) && isturf(y)) - /* - Special format: Defines a map as an existing set of turfs; - this is useful for saving a compiled map in swapmap format. - Because this is a compiled-in map, its turfs are not deleted - when the datum is deleted. - */ - x1=min(x:x,y:x);x2=max(x:x,y:x) - y1=min(x:y,y:y);y2=max(x:y,y:y) - z1=min(x:z,y:z);z2=max(x:z,y:z) - InitializeSwapMaps() - if(z2>swapmaps_compiled_maxz ||\ - y2>swapmaps_compiled_maxy ||\ - x2>swapmaps_compiled_maxx) - qdel(src) - return - x2=x?(x):world.maxx - y2=y?(y):world.maxy - z2=z?(z):1 - AllocateSwapMap() - - Del() - // a temporary datum for a chunk can be deleted outright - // for others, some cleanup is necessary - if(!ischunk) - swapmaps_loaded-=src - swapmaps_byname-=id - if(z2>swapmaps_compiled_maxz ||\ - y2>swapmaps_compiled_maxy ||\ - x2>swapmaps_compiled_maxx) - var/list/areas=new - for(var/atom/A in block(locate(x1,y1,z1),locate(x2,y2,z2))) - for(var/obj/O in A) qdel(O) - for(var/mob/M in A) - if(!M.key) qdel(M) - else M.loc=null - areas[A.loc]=null - qdel(A) - // delete areas that belong only to this map - for(var/area/a in areas) - if(a && !a.contents.len) qdel(a) - if(x2>=world.maxx || y2>=world.maxy || z2>=world.maxz) CutXYZ() - qdel(areas) - ..() - - /* - Savefile format: - map - id - x // size, not coords - y - z - areas // list of areas, not including default - [each z; 1 to depth] - [each y; 1 to height] - [each x; 1 to width] - type // of turf - AREA // if non-default; saved as a number (index into areas list) - vars // all other changed vars - */ - Write(savefile/S) - var/x - var/y - var/z - var/n - var/list/areas - var/area/defarea=locate(world.area) - if(!defarea) defarea=new world.area - areas=list() - for(var/turf/T in block(locate(x1,y1,z1),locate(x2,y2,z2))) - areas[T.loc]=null - for(n in areas) // quickly eliminate associations for smaller storage - areas-=n - areas+=n - areas-=defarea - InitializeSwapMaps() - locked=1 - S["id"] << id - S["z"] << z2-z1+1 - S["y"] << y2-y1+1 - S["x"] << x2-x1+1 - S["areas"] << areas - for(n in 1 to areas.len) areas[areas[n]]=n - var/oldcd=S.cd - for(z=z1,z<=z2,++z) - S.cd="[z-z1+1]" - for(y=y1,y<=y2,++y) - S.cd="[y-y1+1]" - for(x=x1,x<=x2,++x) - S.cd="[x-x1+1]" - var/turf/T=locate(x,y,z) - S["type"] << T.type - if(T.loc!=defarea) S["AREA"] << areas[T.loc] - T.Write(S) - S.cd=".." - S.cd=".." - sleep() - S.cd=oldcd - locked=0 - qdel(areas) - - Read(savefile/S,_id,turf/locorner) - var/x - var/y - var/z - var/n - var/list/areas - var/area/defarea=locate(world.area) - id=_id - if(locorner) - ischunk=1 - x1=locorner.x - y1=locorner.y - z1=locorner.z - if(!defarea) defarea=new world.area - if(!_id) - S["id"] >> id - else - var/dummy - S["id"] >> dummy - S["z"] >> z2 // these are depth, - S["y"] >> y2 // height, - S["x"] >> x2 // width - S["areas"] >> areas - locked=1 - AllocateSwapMap() // adjust x1,y1,z1 - x2,y2,z2 coords - var/oldcd=S.cd - for(z=z1,z<=z2,++z) - S.cd="[z-z1+1]" - for(y=y1,y<=y2,++y) - S.cd="[y-y1+1]" - for(x=x1,x<=x2,++x) - S.cd="[x-x1+1]" - var/tp - S["type"]>>tp - var/turf/T=locate(x,y,z) - T.loc.contents-=T - T=new tp(locate(x,y,z)) - if("AREA" in S.dir) - S["AREA"]>>n - var/area/A=areas[n] - A.contents+=T - else defarea.contents+=T - // clear the turf - for(var/obj/O in T) qdel(O) - for(var/mob/M in T) - if(!M.key) qdel(M) - else M.loc=null - // finish the read - T.Read(S) - S.cd=".." - S.cd=".." - sleep() - S.cd=oldcd - locked=0 - qdel(areas) - - /* - Find an empty block on the world map in which to load this map. - If no space is found, increase world.maxz as necessary. (If the - map is greater in x,y size than the current world, expand - world.maxx and world.maxy too.) - - Ignore certain operations if loading a map as a chunk. Use the - x1,y1,z1 position for it, and *don't* count it as a loaded map. - */ - proc/AllocateSwapMap() - InitializeSwapMaps() - world.maxx=max(x2,world.maxx) // stretch x/y if necessary - world.maxy=max(y2,world.maxy) - if(!ischunk) - if(world.maxz<=swapmaps_compiled_maxz) - z1=swapmaps_compiled_maxz+1 - x1=1;y1=1 - else - var/list/l=ConsiderRegion(1,1,world.maxx,world.maxy,swapmaps_compiled_maxz+1) - x1=l[1] - y1=l[2] - z1=l[3] - qdel(l) - x2+=x1-1 - y2+=y1-1 - z2+=z1-1 - world.maxz=max(z2,world.maxz) // stretch z if necessary - if(!ischunk) - swapmaps_loaded[src]=null - swapmaps_byname[id]=src - - proc/ConsiderRegion(X1,Y1,X2,Y2,Z1,Z2) - while(1) - var/nextz=0 - var/swapmap/M - for(M in swapmaps_loaded) - if(M.z2Z2) || M.z1>=Z1+z2 ||\ - M.x1>X2 || M.x2=X1+x2 ||\ - M.y1>Y2 || M.y2=Y1+y2) continue - // look for sub-regions with a defined ceiling - var/nz2=Z2?(Z2):Z1+z2-1+M.z2-M.z1 - if(M.x1>=X1+x2) - .=ConsiderRegion(X1,Y1,M.x1-1,Y2,Z1,nz2) - if(.) return - else if(M.x2<=X2-x2) - .=ConsiderRegion(M.x2+1,Y1,X2,Y2,Z1,nz2) - if(.) return - if(M.y1>=Y1+y2) - .=ConsiderRegion(X1,Y1,X2,M.y1-1,Z1,nz2) - if(.) return - else if(M.y2<=Y2-y2) - .=ConsiderRegion(X1,M.y2+1,X2,Y2,Z1,nz2) - if(.) return - nextz=nextz?min(nextz,M.z2+1):(M.z2+1) - if(!M) - /* If nextz is not 0, then at some point there was an overlap that - could not be resolved by using an area to the side */ - if(nextz) Z1=nextz - if(!nextz || (Z2 && Z2-Z1+1=z2)?list(X1,Y1,Z1):null - X1=1;X2=world.maxx - Y1=1;Y2=world.maxy - - proc/CutXYZ() - var/mx=swapmaps_compiled_maxx - var/my=swapmaps_compiled_maxy - var/mz=swapmaps_compiled_maxz - for(var/swapmap/M in swapmaps_loaded) // may not include src - mx=max(mx,M.x2) - my=max(my,M.y2) - mz=max(mz,M.z2) - world.maxx=mx - world.maxy=my - world.maxz=mz - - // save and delete - proc/Unload() - Save() - qdel(src) - - proc/Save() - if(id==src) return 0 - var/savefile/S=mode?(new):new("map_[id].sav") - S << src - while(locked) sleep(1) - if(mode) - fdel("map_[id].txt") - S.ExportText("/","map_[id].txt") - return 1 - - // this will not delete existing savefiles for this map - proc/SetID(newid) - swapmaps_byname-=id - id=newid - swapmaps_byname[id]=src - - proc/AllTurfs(z) - if(isnum(z) && (zz2)) return null - return block(LoCorner(z),HiCorner(z)) - - // this could be safely called for an obj or mob as well, but - // probably not an area - proc/Contains(turf/T) - return (T && T.x>=x1 && T.x<=x2\ - && T.y>=y1 && T.y<=y2\ - && T.z>=z1 && T.z<=z2) - - proc/InUse() - for(var/turf/T in AllTurfs()) - for(var/mob/M in T) if(M.key) return 1 - - proc/LoCorner(z=z1) - return locate(x1,y1,z) - proc/HiCorner(z=z2) - return locate(x2,y2,z) - - /* - Build procs: Take 2 turfs as corners, plus an item type. - An item may be like: - - turf/wall - obj/fence{icon_state="iron"} - */ - proc/BuildFilledRectangle(turf/T1,turf/T2,item) - if(!Contains(T1) || !Contains(T2)) return - var/turf/T=T1 - // pick new corners in a block()-friendly form - T1=locate(min(T1.x,T2.x),min(T1.y,T2.y),min(T1.z,T2.z)) - T2=locate(max(T.x,T2.x),max(T.y,T2.y),max(T.z,T2.z)) - for(T in block(T1,T2)) new item(T) - - proc/BuildRectangle(turf/T1,turf/T2,item) - if(!Contains(T1) || !Contains(T2)) return - var/turf/T=T1 - // pick new corners in a block()-friendly form - T1=locate(min(T1.x,T2.x),min(T1.y,T2.y),min(T1.z,T2.z)) - T2=locate(max(T.x,T2.x),max(T.y,T2.y),max(T.z,T2.z)) - if(T2.x-T1.x<2 || T2.y-T1.y<2) BuildFilledRectangle(T1,T2,item) - else - //for(T in block(T1,T2)-block(locate(T1.x+1,T1.y+1,T1.z),locate(T2.x-1,T2.y-1,T2.z))) - for(T in block(T1,locate(T2.x,T1.y,T2.z))) new item(T) - for(T in block(locate(T1.x,T2.y,T1.z),T2)) new item(T) - for(T in block(locate(T1.x,T1.y+1,T1.z),locate(T1.x,T2.y-1,T2.z))) new item(T) - for(T in block(locate(T2.x,T1.y+1,T1.z),locate(T2.x,T2.y-1,T2.z))) new item(T) - - /* - Supplementary build proc: Takes a list of turfs, plus an item - type. Actually the list doesn't have to be just turfs. - */ - proc/BuildInTurfs(list/turfs,item) - for(var/T in turfs) new item(T) - -atom - Write(savefile/S) - for(var/V in vars-"x"-"y"-"z"-"contents"-"icon"-"overlays"-"underlays") - if(issaved(vars[V])) - if(vars[V]!=initial(vars[V])) S[V]<>ic - if(istext(ic)) icon=swapmaps_iconcache[ic] - if(l && contents!=l) - contents+=l - qdel(l) - - -// set this up (at runtime) as follows: -// list(\ -// 'player.dmi'="player",\ -// 'monster.dmi'="monster",\ -// ... -// 'item.dmi'="item") -var/list/swapmaps_iconcache - -// preferred mode; sav or text -var/const/SWAPMAPS_SAV=0 -var/const/SWAPMAPS_TEXT=1 -var/swapmaps_mode=SWAPMAPS_SAV - -var/swapmaps_compiled_maxx -var/swapmaps_compiled_maxy -var/swapmaps_compiled_maxz -var/swapmaps_initialized -var/swapmaps_loaded -var/swapmaps_byname - -/proc/InitializeSwapMaps() - if(swapmaps_initialized) return - swapmaps_initialized=1 - swapmaps_compiled_maxx=world.maxx - swapmaps_compiled_maxy=world.maxy - swapmaps_compiled_maxz=world.maxz - swapmaps_loaded=list() - swapmaps_byname=list() - if(swapmaps_iconcache) - for(var/V in swapmaps_iconcache) - // reverse-associate everything - // so you can look up an icon file by name or vice-versa - swapmaps_iconcache[swapmaps_iconcache[V]]=V - -/proc/SwapMaps_AddIconToCache(name,icon) - if(!swapmaps_iconcache) swapmaps_iconcache=list() - swapmaps_iconcache[name]=icon - swapmaps_iconcache[icon]=name - -/proc/SwapMaps_Find(id) - InitializeSwapMaps() - return swapmaps_byname[id] - -/proc/SwapMaps_Load(id) - InitializeSwapMaps() - var/swapmap/M=swapmaps_byname[id] - if(!M) - var/savefile/S - var/text=0 - if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[id].txt")) - text=1 - else if(fexists("map_[id].sav")) - S=new("map_[id].sav") - else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[id].txt")) - text=1 - else return // no file found - if(text) - S=new - S.ImportText("/",file("map_[id].txt")) - S >> M - while(M.locked) sleep(1) - M.mode=text - return M - -/proc/SwapMaps_Save(id) - InitializeSwapMaps() - var/swapmap/M=swapmaps_byname[id] - if(M) M.Save() - return M - -/proc/SwapMaps_Save_All() - InitializeSwapMaps() - for(var/swapmap/M in swapmaps_loaded) - if(M) M.Save() - -/proc/SwapMaps_Unload(id) - InitializeSwapMaps() - var/swapmap/M=swapmaps_byname[id] - if(!M) return // return silently from an error - M.Unload() - return 1 - -/proc/SwapMaps_DeleteFile(id) - fdel("map_[id].sav") - fdel("map_[id].txt") - -/proc/SwapMaps_CreateFromTemplate(template_id) - var/swapmap/M=new - var/savefile/S - var/text=0 - if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[template_id].txt")) - text=1 - else if(fexists("map_[template_id].sav")) - S=new("map_[template_id].sav") - else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[template_id].txt")) - text=1 - else - to_world_log("SwapMaps error in SwapMaps_CreateFromTemplate(): map_[template_id] file not found.") - return - if(text) - S=new - S.ImportText("/",file("map_[template_id].txt")) - /* - This hacky workaround is needed because S >> M will create a brand new - M to fill with data. There's no way to control the Read() process - properly otherwise. The //.0 path should always match the map, however. - */ - S.cd="//.0" - M.Read(S,M) - M.mode=text - while(M.locked) sleep(1) - return M - -/proc/SwapMaps_LoadChunk(chunk_id,turf/locorner) - var/swapmap/M=new - var/savefile/S - var/text=0 - if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[chunk_id].txt")) - text=1 - else if(fexists("map_[chunk_id].sav")) - S=new("map_[chunk_id].sav") - else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[chunk_id].txt")) - text=1 - else - to_world_log("SwapMaps error in SwapMaps_LoadChunk(): map_[chunk_id] file not found.") - return - if(text) - S=new - S.ImportText("/",file("map_[chunk_id].txt")) - /* - This hacky workaround is needed because S >> M will create a brand new - M to fill with data. There's no way to control the Read() process - properly otherwise. The //.0 path should always match the map, however. - */ - S.cd="//.0" - M.Read(S,M,locorner) - while(M.locked) sleep(1) - qdel(M) - return 1 - -/proc/SwapMaps_SaveChunk(chunk_id,turf/corner1,turf/corner2) - if(!corner1 || !corner2) - to_world_log("SwapMaps error in SwapMaps_SaveChunk():") - if(!corner1) to_world_log(" corner1 turf is null") - if(!corner2) to_world_log(" corner2 turf is null") - return - var/swapmap/M=new - M.id=chunk_id - M.ischunk=1 // this is a chunk - M.x1=min(corner1.x,corner2.x) - M.y1=min(corner1.y,corner2.y) - M.z1=min(corner1.z,corner2.z) - M.x2=max(corner1.x,corner2.x) - M.y2=max(corner1.y,corner2.y) - M.z2=max(corner1.z,corner2.z) - M.mode=swapmaps_mode - M.Save() - while(M.locked) sleep(1) - qdel(M) - return 1 - -/proc/SwapMaps_GetSize(id) - var/savefile/S - var/text=0 - if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[id].txt")) - text=1 - else if(fexists("map_[id].sav")) - S=new("map_[id].sav") - else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[id].txt")) - text=1 - else - to_world_log("SwapMaps error in SwapMaps_GetSize(): map_[id] file not found.") - return - if(text) - S=new - S.ImportText("/",file("map_[id].txt")) - /* - The //.0 path should always be the map. There's no other way to - read this data. - */ - S.cd="//.0" - var/x - var/y - var/z - S["x"] >> x - S["y"] >> y - S["z"] >> z - return list(x,y,z) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/* + SwapMaps library by Lummox JR + developed for digitalBYOND + http://www.digitalbyond.org + + Version 2.1 + + The purpose of this library is to make it easy for authors to swap maps + in and out of their game using savefiles. Swapped-out maps can be + transferred between worlds for an MMORPG, sent to the client, etc. + This is facilitated by the use of a special datum and a global list. + + Uses of swapmaps: + + - Temporary battle arenas + - House interiors + - Individual custom player houses + - Virtually unlimited terrain + - Sharing maps between servers running different instances of the same + game + - Loading and saving pieces of maps for reusable room templates + */ + +/* + User Interface: + + VARS: + + swapmaps_iconcache + An associative list of icon files with names, like + 'player.dmi' = "player" + swapmaps_mode + This must be set at runtime, like in world/New(). + + SWAPMAPS_SAV 0 (default) + Uses .sav files for raw /savefile output. + SWAPMAPS_TEXT 1 + Uses .txt files via ExportText() and ImportText(). These maps + are easily editable and appear to take up less space in the + current version of BYOND. + + PROCS: + + SwapMaps_Find(id) + Find a map by its id + SwapMaps_Load(id) + Load a map by its id + SwapMaps_Save(id) + Save a map by its id (calls swapmap.Save()) + SwapMaps_Unload(id) + Save and unload a map by its id (calls swapmap.Unload()) + SwapMaps_Save_All() + Save all maps + SwapMaps_DeleteFile(id) + Delete a map file + SwapMaps_CreateFromTemplate(id) + Create a new map by loading another map to use as a template. + This map has id==src and will not be saved. To make it savable, + change id with swapmap.SetID(newid). + SwapMaps_LoadChunk(id,turf/locorner) + Load a swapmap as a "chunk", at a specific place. A new datum is + created but it's not added to the list of maps to save or unload. + The new datum can be safely deleted without affecting the turfs + it loaded. The purpose of this is to load a map file onto part of + another swapmap or an existing part of the world. + locorner is the corner turf with the lowest x,y,z values. + SwapMaps_SaveChunk(id,turf/corner1,turf/corner2) + Save a piece of the world as a "chunk". A new datum is created + for the chunk, but it can be deleted without destroying any turfs. + The chunk file can be reloaded as a swapmap all its own, or loaded + via SwapMaps_LoadChunk() to become part of another map. + SwapMaps_GetSize(id) + Return a list corresponding to the x,y,z sizes of a map file, + without loading the map. + Returns null if the map is not found. + SwapMaps_AddIconToCache(name,icon) + Cache an icon file by name for space-saving storage + + swapmap.New(id,x,y,z) + Create a new map; specify id, width (x), height (y), and + depth (z) + Default size is world.maxx,world.maxy,1 + swapmap.New(id,turf1,turf2) + Create a new map; specify id and 2 corners + This becomes a /swapmap for one of the compiled-in maps, for + easy saving. + swapmap.New() + Create a new map datum, but does not allocate space or assign an + ID (used for loading). + swapmap.Del() + Deletes a map but does not save + swapmap.Save() + Saves to map_[id].sav + Maps with id==src are not saved. + swapmap.Unload() + Saves the map and then deletes it + Maps with id==src are not saved. + swapmap.SetID(id) + Change the map's id and make changes to the lookup list + swapmap.AllTurfs(z) + Returns a block of turfs encompassing the entire map, or on just + one z-level + z is in world coordinates; it is optional + swapmap.Contains(turf/T) + Returns nonzero if T is inside the map's boundaries. + Also works for objs and mobs, but the proc is not area-safe. + swapmap.InUse() + Returns nonzero if a mob with a key is within the map's + boundaries. + swapmap.LoCorner(z=z1) + Returns locate(x1,y1,z), where z=z1 if none is specified. + swapmap.HiCorner(z=z2) + Returns locate(x2,y2,z), where z=z2 if none is specified. + swapmap.BuildFilledRectangle(turf/corner1,turf/corner2,item) + Builds a filled rectangle of item from one corner turf to the + other, on multiple z-levels if necessary. The corners may be + specified in any order. + item is a type path like /turf/wall or /obj/barrel{full=1}. + swapmap.BuildRectangle(turf/corner1,turf/corner2,item) + Builds an unfilled rectangle of item from one corner turf to + the other, on multiple z-levels if necessary. + swapmap.BuildInTurfs(list/turfs,item) + Builds item on all of the turfs listed. The list need not + contain only turfs, or even only atoms. + */ + +swapmap + var/id // a string identifying this map uniquely + var/x1 // minimum x,y,z coords + var/y1 + var/z1 + var/x2 // maximum x,y,z coords (also used as width,height,depth until positioned) + var/y2 + var/z2 + var/tmp/locked // don't move anyone to this map; it's saving or loading + var/tmp/mode // save as text-mode + var/ischunk // tells the load routine to load to the specified location + + New(_id,x,y,z) + if(isnull(_id)) return + id=_id + mode=swapmaps_mode + if(isturf(x) && isturf(y)) + /* + Special format: Defines a map as an existing set of turfs; + this is useful for saving a compiled map in swapmap format. + Because this is a compiled-in map, its turfs are not deleted + when the datum is deleted. + */ + x1=min(x:x,y:x);x2=max(x:x,y:x) + y1=min(x:y,y:y);y2=max(x:y,y:y) + z1=min(x:z,y:z);z2=max(x:z,y:z) + InitializeSwapMaps() + if(z2>swapmaps_compiled_maxz ||\ + y2>swapmaps_compiled_maxy ||\ + x2>swapmaps_compiled_maxx) + qdel(src) + return + x2=x?(x):world.maxx + y2=y?(y):world.maxy + z2=z?(z):1 + AllocateSwapMap() + + Del() + // a temporary datum for a chunk can be deleted outright + // for others, some cleanup is necessary + if(!ischunk) + swapmaps_loaded-=src + swapmaps_byname-=id + if(z2>swapmaps_compiled_maxz ||\ + y2>swapmaps_compiled_maxy ||\ + x2>swapmaps_compiled_maxx) + var/list/areas=new + for(var/atom/A in block(locate(x1,y1,z1),locate(x2,y2,z2))) + for(var/obj/O in A) qdel(O) + for(var/mob/M in A) + if(!M.key) qdel(M) + else M.loc=null + areas[A.loc]=null + qdel(A) + // delete areas that belong only to this map + for(var/area/a in areas) + if(a && !a.contents.len) qdel(a) + if(x2>=world.maxx || y2>=world.maxy || z2>=world.maxz) CutXYZ() + qdel(areas) + ..() + + /* + Savefile format: + map + id + x // size, not coords + y + z + areas // list of areas, not including default + [each z; 1 to depth] + [each y; 1 to height] + [each x; 1 to width] + type // of turf + AREA // if non-default; saved as a number (index into areas list) + vars // all other changed vars + */ + Write(savefile/S) + var/x + var/y + var/z + var/n + var/list/areas + var/area/defarea=locate(world.area) + if(!defarea) defarea=new world.area + areas=list() + for(var/turf/T in block(locate(x1,y1,z1),locate(x2,y2,z2))) + areas[T.loc]=null + for(n in areas) // quickly eliminate associations for smaller storage + areas-=n + areas+=n + areas-=defarea + InitializeSwapMaps() + locked=1 + S["id"] << id + S["z"] << z2-z1+1 + S["y"] << y2-y1+1 + S["x"] << x2-x1+1 + S["areas"] << areas + for(n in 1 to areas.len) areas[areas[n]]=n + var/oldcd=S.cd + for(z=z1,z<=z2,++z) + S.cd="[z-z1+1]" + for(y=y1,y<=y2,++y) + S.cd="[y-y1+1]" + for(x=x1,x<=x2,++x) + S.cd="[x-x1+1]" + var/turf/T=locate(x,y,z) + S["type"] << T.type + if(T.loc!=defarea) S["AREA"] << areas[T.loc] + T.Write(S) + S.cd=".." + S.cd=".." + sleep() + S.cd=oldcd + locked=0 + qdel(areas) + + Read(savefile/S,_id,turf/locorner) + var/x + var/y + var/z + var/n + var/list/areas + var/area/defarea=locate(world.area) + id=_id + if(locorner) + ischunk=1 + x1=locorner.x + y1=locorner.y + z1=locorner.z + if(!defarea) defarea=new world.area + if(!_id) + S["id"] >> id + else + var/dummy + S["id"] >> dummy + S["z"] >> z2 // these are depth, + S["y"] >> y2 // height, + S["x"] >> x2 // width + S["areas"] >> areas + locked=1 + AllocateSwapMap() // adjust x1,y1,z1 - x2,y2,z2 coords + var/oldcd=S.cd + for(z=z1,z<=z2,++z) + S.cd="[z-z1+1]" + for(y=y1,y<=y2,++y) + S.cd="[y-y1+1]" + for(x=x1,x<=x2,++x) + S.cd="[x-x1+1]" + var/tp + S["type"]>>tp + var/turf/T=locate(x,y,z) + T.loc.contents-=T + T=new tp(locate(x,y,z)) + if("AREA" in S.dir) + S["AREA"]>>n + var/area/A=areas[n] + A.contents+=T + else defarea.contents+=T + // clear the turf + for(var/obj/O in T) qdel(O) + for(var/mob/M in T) + if(!M.key) qdel(M) + else M.loc=null + // finish the read + T.Read(S) + S.cd=".." + S.cd=".." + sleep() + S.cd=oldcd + locked=0 + qdel(areas) + + /* + Find an empty block on the world map in which to load this map. + If no space is found, increase world.maxz as necessary. (If the + map is greater in x,y size than the current world, expand + world.maxx and world.maxy too.) + + Ignore certain operations if loading a map as a chunk. Use the + x1,y1,z1 position for it, and *don't* count it as a loaded map. + */ + proc/AllocateSwapMap() + InitializeSwapMaps() + world.maxx=max(x2,world.maxx) // stretch x/y if necessary + world.maxy=max(y2,world.maxy) + if(!ischunk) + if(world.maxz<=swapmaps_compiled_maxz) + z1=swapmaps_compiled_maxz+1 + x1=1;y1=1 + else + var/list/l=ConsiderRegion(1,1,world.maxx,world.maxy,swapmaps_compiled_maxz+1) + x1=l[1] + y1=l[2] + z1=l[3] + qdel(l) + x2+=x1-1 + y2+=y1-1 + z2+=z1-1 + world.maxz=max(z2,world.maxz) // stretch z if necessary + if(!ischunk) + swapmaps_loaded[src]=null + swapmaps_byname[id]=src + + proc/ConsiderRegion(X1,Y1,X2,Y2,Z1,Z2) + while(1) + var/nextz=0 + var/swapmap/M + for(M in swapmaps_loaded) + if(M.z2Z2) || M.z1>=Z1+z2 ||\ + M.x1>X2 || M.x2=X1+x2 ||\ + M.y1>Y2 || M.y2=Y1+y2) continue + // look for sub-regions with a defined ceiling + var/nz2=Z2?(Z2):Z1+z2-1+M.z2-M.z1 + if(M.x1>=X1+x2) + .=ConsiderRegion(X1,Y1,M.x1-1,Y2,Z1,nz2) + if(.) return + else if(M.x2<=X2-x2) + .=ConsiderRegion(M.x2+1,Y1,X2,Y2,Z1,nz2) + if(.) return + if(M.y1>=Y1+y2) + .=ConsiderRegion(X1,Y1,X2,M.y1-1,Z1,nz2) + if(.) return + else if(M.y2<=Y2-y2) + .=ConsiderRegion(X1,M.y2+1,X2,Y2,Z1,nz2) + if(.) return + nextz=nextz?min(nextz,M.z2+1):(M.z2+1) + if(!M) + /* If nextz is not 0, then at some point there was an overlap that + could not be resolved by using an area to the side */ + if(nextz) Z1=nextz + if(!nextz || (Z2 && Z2-Z1+1=z2)?list(X1,Y1,Z1):null + X1=1;X2=world.maxx + Y1=1;Y2=world.maxy + + proc/CutXYZ() + var/mx=swapmaps_compiled_maxx + var/my=swapmaps_compiled_maxy + var/mz=swapmaps_compiled_maxz + for(var/swapmap/M in swapmaps_loaded) // may not include src + mx=max(mx,M.x2) + my=max(my,M.y2) + mz=max(mz,M.z2) + world.maxx=mx + world.maxy=my + world.maxz=mz + + // save and delete + proc/Unload() + Save() + qdel(src) + + proc/Save() + if(id==src) return 0 + var/savefile/S=mode?(new):new("map_[id].sav") + S << src + while(locked) sleep(1) + if(mode) + fdel("map_[id].txt") + S.ExportText("/","map_[id].txt") + return 1 + + // this will not delete existing savefiles for this map + proc/SetID(newid) + swapmaps_byname-=id + id=newid + swapmaps_byname[id]=src + + proc/AllTurfs(z) + if(isnum(z) && (zz2)) return null + return block(LoCorner(z),HiCorner(z)) + + // this could be safely called for an obj or mob as well, but + // probably not an area + proc/Contains(turf/T) + return (T && T.x>=x1 && T.x<=x2\ + && T.y>=y1 && T.y<=y2\ + && T.z>=z1 && T.z<=z2) + + proc/InUse() + for(var/turf/T in AllTurfs()) + for(var/mob/M in T) if(M.key) return 1 + + proc/LoCorner(z=z1) + return locate(x1,y1,z) + proc/HiCorner(z=z2) + return locate(x2,y2,z) + + /* + Build procs: Take 2 turfs as corners, plus an item type. + An item may be like: + + turf/wall + obj/fence{icon_state="iron"} + */ + proc/BuildFilledRectangle(turf/T1,turf/T2,item) + if(!Contains(T1) || !Contains(T2)) return + var/turf/T=T1 + // pick new corners in a block()-friendly form + T1=locate(min(T1.x,T2.x),min(T1.y,T2.y),min(T1.z,T2.z)) + T2=locate(max(T.x,T2.x),max(T.y,T2.y),max(T.z,T2.z)) + for(T in block(T1,T2)) new item(T) + + proc/BuildRectangle(turf/T1,turf/T2,item) + if(!Contains(T1) || !Contains(T2)) return + var/turf/T=T1 + // pick new corners in a block()-friendly form + T1=locate(min(T1.x,T2.x),min(T1.y,T2.y),min(T1.z,T2.z)) + T2=locate(max(T.x,T2.x),max(T.y,T2.y),max(T.z,T2.z)) + if(T2.x-T1.x<2 || T2.y-T1.y<2) BuildFilledRectangle(T1,T2,item) + else + //for(T in block(T1,T2)-block(locate(T1.x+1,T1.y+1,T1.z),locate(T2.x-1,T2.y-1,T2.z))) + for(T in block(T1,locate(T2.x,T1.y,T2.z))) new item(T) + for(T in block(locate(T1.x,T2.y,T1.z),T2)) new item(T) + for(T in block(locate(T1.x,T1.y+1,T1.z),locate(T1.x,T2.y-1,T2.z))) new item(T) + for(T in block(locate(T2.x,T1.y+1,T1.z),locate(T2.x,T2.y-1,T2.z))) new item(T) + + /* + Supplementary build proc: Takes a list of turfs, plus an item + type. Actually the list doesn't have to be just turfs. + */ + proc/BuildInTurfs(list/turfs,item) + for(var/T in turfs) new item(T) + +atom + Write(savefile/S) + for(var/V in vars-"x"-"y"-"z"-"contents"-"icon"-"overlays"-"underlays") + if(issaved(vars[V])) + if(vars[V]!=initial(vars[V])) S[V]<>ic + if(istext(ic)) icon=swapmaps_iconcache[ic] + if(l && contents!=l) + contents+=l + qdel(l) + + +// set this up (at runtime) as follows: +// list(\ +// 'player.dmi'="player",\ +// 'monster.dmi'="monster",\ +// ... +// 'item.dmi'="item") +var/list/swapmaps_iconcache + +// preferred mode; sav or text +var/const/SWAPMAPS_SAV=0 +var/const/SWAPMAPS_TEXT=1 +var/swapmaps_mode=SWAPMAPS_SAV + +var/swapmaps_compiled_maxx +var/swapmaps_compiled_maxy +var/swapmaps_compiled_maxz +var/swapmaps_initialized +var/swapmaps_loaded +var/swapmaps_byname + +/proc/InitializeSwapMaps() + if(swapmaps_initialized) return + swapmaps_initialized=1 + swapmaps_compiled_maxx=world.maxx + swapmaps_compiled_maxy=world.maxy + swapmaps_compiled_maxz=world.maxz + swapmaps_loaded=list() + swapmaps_byname=list() + if(swapmaps_iconcache) + for(var/V in swapmaps_iconcache) + // reverse-associate everything + // so you can look up an icon file by name or vice-versa + swapmaps_iconcache[swapmaps_iconcache[V]]=V + +/proc/SwapMaps_AddIconToCache(name,icon) + if(!swapmaps_iconcache) swapmaps_iconcache=list() + swapmaps_iconcache[name]=icon + swapmaps_iconcache[icon]=name + +/proc/SwapMaps_Find(id) + InitializeSwapMaps() + return swapmaps_byname[id] + +/proc/SwapMaps_Load(id) + InitializeSwapMaps() + var/swapmap/M=swapmaps_byname[id] + if(!M) + var/savefile/S + var/text=0 + if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[id].txt")) + text=1 + else if(fexists("map_[id].sav")) + S=new("map_[id].sav") + else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[id].txt")) + text=1 + else return // no file found + if(text) + S=new + S.ImportText("/",file("map_[id].txt")) + S >> M + while(M.locked) sleep(1) + M.mode=text + return M + +/proc/SwapMaps_Save(id) + InitializeSwapMaps() + var/swapmap/M=swapmaps_byname[id] + if(M) M.Save() + return M + +/proc/SwapMaps_Save_All() + InitializeSwapMaps() + for(var/swapmap/M in swapmaps_loaded) + if(M) M.Save() + +/proc/SwapMaps_Unload(id) + InitializeSwapMaps() + var/swapmap/M=swapmaps_byname[id] + if(!M) return // return silently from an error + M.Unload() + return 1 + +/proc/SwapMaps_DeleteFile(id) + fdel("map_[id].sav") + fdel("map_[id].txt") + +/proc/SwapMaps_CreateFromTemplate(template_id) + var/swapmap/M=new + var/savefile/S + var/text=0 + if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[template_id].txt")) + text=1 + else if(fexists("map_[template_id].sav")) + S=new("map_[template_id].sav") + else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[template_id].txt")) + text=1 + else + to_world_log("SwapMaps error in SwapMaps_CreateFromTemplate(): map_[template_id] file not found.") + return + if(text) + S=new + S.ImportText("/",file("map_[template_id].txt")) + /* + This hacky workaround is needed because S >> M will create a brand new + M to fill with data. There's no way to control the Read() process + properly otherwise. The //.0 path should always match the map, however. + */ + S.cd="//.0" + M.Read(S,M) + M.mode=text + while(M.locked) sleep(1) + return M + +/proc/SwapMaps_LoadChunk(chunk_id,turf/locorner) + var/swapmap/M=new + var/savefile/S + var/text=0 + if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[chunk_id].txt")) + text=1 + else if(fexists("map_[chunk_id].sav")) + S=new("map_[chunk_id].sav") + else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[chunk_id].txt")) + text=1 + else + to_world_log("SwapMaps error in SwapMaps_LoadChunk(): map_[chunk_id] file not found.") + return + if(text) + S=new + S.ImportText("/",file("map_[chunk_id].txt")) + /* + This hacky workaround is needed because S >> M will create a brand new + M to fill with data. There's no way to control the Read() process + properly otherwise. The //.0 path should always match the map, however. + */ + S.cd="//.0" + M.Read(S,M,locorner) + while(M.locked) sleep(1) + qdel(M) + return 1 + +/proc/SwapMaps_SaveChunk(chunk_id,turf/corner1,turf/corner2) + if(!corner1 || !corner2) + to_world_log("SwapMaps error in SwapMaps_SaveChunk():") + if(!corner1) to_world_log(" corner1 turf is null") + if(!corner2) to_world_log(" corner2 turf is null") + return + var/swapmap/M=new + M.id=chunk_id + M.ischunk=1 // this is a chunk + M.x1=min(corner1.x,corner2.x) + M.y1=min(corner1.y,corner2.y) + M.z1=min(corner1.z,corner2.z) + M.x2=max(corner1.x,corner2.x) + M.y2=max(corner1.y,corner2.y) + M.z2=max(corner1.z,corner2.z) + M.mode=swapmaps_mode + M.Save() + while(M.locked) sleep(1) + qdel(M) + return 1 + +/proc/SwapMaps_GetSize(id) + var/savefile/S + var/text=0 + if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[id].txt")) + text=1 + else if(fexists("map_[id].sav")) + S=new("map_[id].sav") + else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[id].txt")) + text=1 + else + to_world_log("SwapMaps error in SwapMaps_GetSize(): map_[id] file not found.") + return + if(text) + S=new + S.ImportText("/",file("map_[id].txt")) + /* + The //.0 path should always be the map. There's no other way to + read this data. + */ + S.cd="//.0" + var/x + var/y + var/z + S["x"] >> x + S["y"] >> y + S["z"] >> z + return list(x,y,z) diff --git a/code/modules/materials/material_synth.dm b/code/modules/materials/material_synth.dm index 972b8b61eda..9d595bce994 100644 --- a/code/modules/materials/material_synth.dm +++ b/code/modules/materials/material_synth.dm @@ -1,41 +1,41 @@ -// These objects are used by cyborgs to get around a lot of the limitations on stacks -// and the weird bugs that crop up when expecting borg module code to behave sanely. -/obj/item/stack/material/cyborg - uses_charge = 1 - charge_costs = list(1000) - gender = NEUTER - matter = null // Don't shove it in the autholathe. - -/obj/item/stack/material/cyborg/Initialize() - . = ..() - name = "[material.display_name] synthesiser" - desc = "A device that synthesises [material.display_name]." - matter = null - -/obj/item/stack/material/cyborg/update_strings() - return - -/obj/item/stack/material/cyborg/plastic - icon_state = "sheet-plastic" - default_type = "plastic" - -/obj/item/stack/material/cyborg/steel - icon_state = "sheet-metal" - default_type = "steel" - -/obj/item/stack/material/cyborg/plasteel - icon_state = "sheet-plasteel" - default_type = "plasteel" - -/obj/item/stack/material/cyborg/wood - icon_state = "sheet-wood" - default_type = "wood" - -/obj/item/stack/material/cyborg/glass - icon_state = "sheet-glass" - default_type = "glass" - -/obj/item/stack/material/cyborg/glass/reinforced - icon_state = "sheet-rglass" - default_type = "rglass" +// These objects are used by cyborgs to get around a lot of the limitations on stacks +// and the weird bugs that crop up when expecting borg module code to behave sanely. +/obj/item/stack/material/cyborg + uses_charge = 1 + charge_costs = list(1000) + gender = NEUTER + matter = null // Don't shove it in the autholathe. + +/obj/item/stack/material/cyborg/Initialize() + . = ..() + name = "[material.display_name] synthesiser" + desc = "A device that synthesises [material.display_name]." + matter = null + +/obj/item/stack/material/cyborg/update_strings() + return + +/obj/item/stack/material/cyborg/plastic + icon_state = "sheet-plastic" + default_type = "plastic" + +/obj/item/stack/material/cyborg/steel + icon_state = "sheet-metal" + default_type = "steel" + +/obj/item/stack/material/cyborg/plasteel + icon_state = "sheet-plasteel" + default_type = "plasteel" + +/obj/item/stack/material/cyborg/wood + icon_state = "sheet-wood" + default_type = "wood" + +/obj/item/stack/material/cyborg/glass + icon_state = "sheet-glass" + default_type = "glass" + +/obj/item/stack/material/cyborg/glass/reinforced + icon_state = "sheet-rglass" + default_type = "rglass" charge_costs = list(500, 1000) \ No newline at end of file diff --git a/code/modules/mining/mineral_effect.dm b/code/modules/mining/mineral_effect.dm index 43bd5c85880..f2506702bd1 100644 --- a/code/modules/mining/mineral_effect.dm +++ b/code/modules/mining/mineral_effect.dm @@ -1,29 +1,29 @@ -/obj/effect/mineral - name = "mineral vein" - icon = 'icons/obj/mining.dmi' - desc = "Shiny." - mouse_opacity = 0 - density = FALSE - anchored = TRUE - var/ore_key - var/image/scanner_image - var/ore_reagent // Reagent from pumping water near this ore. - -/obj/effect/mineral/New(var/newloc, var/ore/M) - ..(newloc) - name = "[M.display_name] deposit" - ore_key = M.name - if(M.reagent) - ore_reagent = M.reagent - icon_state = "rock_[ore_key]" - var/turf/T = get_turf(src) - layer = T.layer+0.1 - -/obj/effect/mineral/proc/get_scan_overlay() - if(!scanner_image) - var/ore/O = GLOB.ore_data[ore_key] - if(O) - scanner_image = image(icon, loc = get_turf(src), icon_state = (O.scan_icon ? O.scan_icon : icon_state)) - else - to_world("No ore data for [src]!") +/obj/effect/mineral + name = "mineral vein" + icon = 'icons/obj/mining.dmi' + desc = "Shiny." + mouse_opacity = 0 + density = FALSE + anchored = TRUE + var/ore_key + var/image/scanner_image + var/ore_reagent // Reagent from pumping water near this ore. + +/obj/effect/mineral/New(var/newloc, var/ore/M) + ..(newloc) + name = "[M.display_name] deposit" + ore_key = M.name + if(M.reagent) + ore_reagent = M.reagent + icon_state = "rock_[ore_key]" + var/turf/T = get_turf(src) + layer = T.layer+0.1 + +/obj/effect/mineral/proc/get_scan_overlay() + if(!scanner_image) + var/ore/O = GLOB.ore_data[ore_key] + if(O) + scanner_image = image(icon, loc = get_turf(src), icon_state = (O.scan_icon ? O.scan_icon : icon_state)) + else + to_world("No ore data for [src]!") return scanner_image \ No newline at end of file diff --git a/code/modules/mob/dead/observer/login.dm b/code/modules/mob/dead/observer/login.dm index 94155d5643d..0342494684a 100644 --- a/code/modules/mob/dead/observer/login.dm +++ b/code/modules/mob/dead/observer/login.dm @@ -1,12 +1,12 @@ -//Nobody here anymore. -/mob/observer/dead/Login() - ..() //Creates the plane_holder lazily - plane_holder.set_vis(VIS_GHOSTS, ghostvision) - plane_holder.set_vis(VIS_FULLBRIGHT, !seedarkness) - plane_holder.set_vis(VIS_CLOAKED, TRUE) - plane_holder.set_vis(VIS_AI_EYE, TRUE) - plane_holder.set_vis(VIS_AUGMENTED, TRUE) //VOREStation Add - GHOST VISION IS AUGMENTED - plane = PLANE_GHOSTS - if(cleanup_timer) - deltimer(cleanup_timer) +//Nobody here anymore. +/mob/observer/dead/Login() + ..() //Creates the plane_holder lazily + plane_holder.set_vis(VIS_GHOSTS, ghostvision) + plane_holder.set_vis(VIS_FULLBRIGHT, !seedarkness) + plane_holder.set_vis(VIS_CLOAKED, TRUE) + plane_holder.set_vis(VIS_AI_EYE, TRUE) + plane_holder.set_vis(VIS_AUGMENTED, TRUE) //VOREStation Add - GHOST VISION IS AUGMENTED + plane = PLANE_GHOSTS + if(cleanup_timer) + deltimer(cleanup_timer) cleanup_timer = null \ No newline at end of file diff --git a/code/modules/mob/dead/observer/logout.dm b/code/modules/mob/dead/observer/logout.dm index aca5ebdf6cb..bf7db4bf87d 100644 --- a/code/modules/mob/dead/observer/logout.dm +++ b/code/modules/mob/dead/observer/logout.dm @@ -1,7 +1,7 @@ -/mob/observer/dead/Logout() - ..() - spawn(0) - if(src && !key) //we've transferred to another mob. This ghost should be deleted. - qdel(src) - else - cleanup_timer = QDEL_IN(src, 10 MINUTES) +/mob/observer/dead/Logout() + ..() + spawn(0) + if(src && !key) //we've transferred to another mob. This ghost should be deleted. + qdel(src) + else + cleanup_timer = QDEL_IN(src, 10 MINUTES) diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm index 8bf61350ec3..fa26cc707cf 100644 --- a/code/modules/mob/dead/observer/say.dm +++ b/code/modules/mob/dead/observer/say.dm @@ -1,42 +1,42 @@ -/mob/observer/dead/say(var/message, var/datum/language/speaking = null, var/whispering = 0) - message = sanitize(message) - - if(!message) - return - - log_ghostsay(message, src) - - if (client) - if(message) - client.handle_spam_prevention(MUTE_DEADCHAT) - if(client.prefs.muted & MUTE_DEADCHAT) - to_chat(src, "[span_red("You cannot talk in deadchat (muted).")]") - return - - . = say_dead(message) - - -/mob/observer/dead/me_verb(message as text) - if(!message) - return - - log_ghostemote(message, src) - - if(client) - if(message) - client.handle_spam_prevention(MUTE_DEADCHAT) - if(client.prefs.muted & MUTE_DEADCHAT) - to_chat(src, "[span_red("You cannot emote in deadchat (muted).")]") - return - - . = emote_dead(message) - -/mob/observer/dead/handle_track(message, verb = "says", mob/speaker = null, speaker_name, hard_to_hear) - return "[speaker_name] ([ghost_follow_link(speaker, src)])" - -/mob/observer/dead/handle_speaker_name(mob/speaker = null, vname, hard_to_hear) - var/speaker_name = ..() - //Announce computer and various stuff that broadcasts doesn't use it's real name but AI's can't pretend to be other mobs. - if(speaker && (speaker_name != speaker.real_name) && !isAI(speaker)) - speaker_name = "[speaker.real_name] ([speaker_name])" - return speaker_name +/mob/observer/dead/say(var/message, var/datum/language/speaking = null, var/whispering = 0) + message = sanitize(message) + + if(!message) + return + + log_ghostsay(message, src) + + if (client) + if(message) + client.handle_spam_prevention(MUTE_DEADCHAT) + if(client.prefs.muted & MUTE_DEADCHAT) + to_chat(src, "[span_red("You cannot talk in deadchat (muted).")]") + return + + . = say_dead(message) + + +/mob/observer/dead/me_verb(message as text) + if(!message) + return + + log_ghostemote(message, src) + + if(client) + if(message) + client.handle_spam_prevention(MUTE_DEADCHAT) + if(client.prefs.muted & MUTE_DEADCHAT) + to_chat(src, "[span_red("You cannot emote in deadchat (muted).")]") + return + + . = emote_dead(message) + +/mob/observer/dead/handle_track(message, verb = "says", mob/speaker = null, speaker_name, hard_to_hear) + return "[speaker_name] ([ghost_follow_link(speaker, src)])" + +/mob/observer/dead/handle_speaker_name(mob/speaker = null, vname, hard_to_hear) + var/speaker_name = ..() + //Announce computer and various stuff that broadcasts doesn't use it's real name but AI's can't pretend to be other mobs. + if(speaker && (speaker_name != speaker.real_name) && !isAI(speaker)) + speaker_name = "[speaker.real_name] ([speaker_name])" + return speaker_name diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index 48cdfa3ebd7..712393fd211 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -1,37 +1,37 @@ -// Shortcuts for above proc -/mob/proc/visible_emote(var/act_desc) - custom_emote(VISIBLE_MESSAGE, act_desc) - -/mob/proc/audible_emote(var/act_desc) - custom_emote(AUDIBLE_MESSAGE, act_desc) - -/mob/proc/emote_dead(var/message) - - if(client.prefs.muted & MUTE_DEADCHAT) - to_chat(src, "You cannot send deadchat emotes (muted).") - return - - if(!is_preference_enabled(/datum/client_preference/show_dsay)) - to_chat(src, "You have deadchat muted.") - return - - if(!src.client.holder) - if(!config.dsay_allowed) - to_chat(src, "Deadchat is globally muted.") - return - - - var/input - if(!message) - input = sanitize_or_reflect(tgui_input_text(src, "Choose an emote to display."), src) //VOREStation Edit - Reflect too long messages, within reason - else - input = message - - input = encode_html_emphasis(input) - - if(input) - log_ghostemote(input, src) - if(!invisibility) //If the ghost is made visible by admins or cult. And to see if the ghost has toggled its own visibility, as well. -Mech - visible_message("[src] [input]") - else - say_dead_direct(input, src) +// Shortcuts for above proc +/mob/proc/visible_emote(var/act_desc) + custom_emote(VISIBLE_MESSAGE, act_desc) + +/mob/proc/audible_emote(var/act_desc) + custom_emote(AUDIBLE_MESSAGE, act_desc) + +/mob/proc/emote_dead(var/message) + + if(client.prefs.muted & MUTE_DEADCHAT) + to_chat(src, "You cannot send deadchat emotes (muted).") + return + + if(!is_preference_enabled(/datum/client_preference/show_dsay)) + to_chat(src, "You have deadchat muted.") + return + + if(!src.client.holder) + if(!config.dsay_allowed) + to_chat(src, "Deadchat is globally muted.") + return + + + var/input + if(!message) + input = sanitize_or_reflect(tgui_input_text(src, "Choose an emote to display."), src) //VOREStation Edit - Reflect too long messages, within reason + else + input = message + + input = encode_html_emphasis(input) + + if(input) + log_ghostemote(input, src) + if(!invisibility) //If the ghost is made visible by admins or cult. And to see if the ghost has toggled its own visibility, as well. -Mech + visible_message("[src] [input]") + else + say_dead_direct(input, src) diff --git a/code/modules/mob/freelook/ai/cameranet.dm b/code/modules/mob/freelook/ai/cameranet.dm index f29749b38c1..d1b395d8234 100644 --- a/code/modules/mob/freelook/ai/cameranet.dm +++ b/code/modules/mob/freelook/ai/cameranet.dm @@ -1,44 +1,44 @@ -// CAMERA NET -// -// The datum containing all the chunks. - -/datum/visualnet/camera - // The cameras on the map, no matter if they work or not. Updated in obj/machinery/camera.dm by New() and Destroy(). - var/list/cameras = list() - var/cameras_unsorted = 1 - chunk_type = /datum/chunk/camera - -/datum/visualnet/camera/proc/process_sort() - if(cameras_unsorted) - cameras = dd_sortedObjectList(cameras) - cameras_unsorted = 0 - -// Removes a camera from a chunk. - -/datum/visualnet/camera/proc/removeCamera(obj/machinery/camera/c) - if(c.can_use()) - majorChunkChange(c, 0) - -// Add a camera to a chunk. - -/datum/visualnet/camera/proc/addCamera(obj/machinery/camera/c) - if(c.can_use()) - majorChunkChange(c, 1) - -// Used for Cyborg cameras. Since portable cameras can be in ANY chunk. - -/datum/visualnet/camera/proc/updatePortableCamera(obj/machinery/camera/c) - if(c.can_use()) - majorChunkChange(c, 1) - //else - // majorChunkChange(c, 0) - -/datum/visualnet/camera/onMajorChunkChange(atom/c, var/choice, var/datum/chunk/camera/chunk) -// Only add actual cameras to the list of cameras - if(istype(c, /obj/machinery/camera)) - if(choice == 0) - // Remove the camera. - chunk.cameras -= c - else if(choice == 1) - // You can't have the same camera in the list twice. - chunk.cameras |= c +// CAMERA NET +// +// The datum containing all the chunks. + +/datum/visualnet/camera + // The cameras on the map, no matter if they work or not. Updated in obj/machinery/camera.dm by New() and Destroy(). + var/list/cameras = list() + var/cameras_unsorted = 1 + chunk_type = /datum/chunk/camera + +/datum/visualnet/camera/proc/process_sort() + if(cameras_unsorted) + cameras = dd_sortedObjectList(cameras) + cameras_unsorted = 0 + +// Removes a camera from a chunk. + +/datum/visualnet/camera/proc/removeCamera(obj/machinery/camera/c) + if(c.can_use()) + majorChunkChange(c, 0) + +// Add a camera to a chunk. + +/datum/visualnet/camera/proc/addCamera(obj/machinery/camera/c) + if(c.can_use()) + majorChunkChange(c, 1) + +// Used for Cyborg cameras. Since portable cameras can be in ANY chunk. + +/datum/visualnet/camera/proc/updatePortableCamera(obj/machinery/camera/c) + if(c.can_use()) + majorChunkChange(c, 1) + //else + // majorChunkChange(c, 0) + +/datum/visualnet/camera/onMajorChunkChange(atom/c, var/choice, var/datum/chunk/camera/chunk) +// Only add actual cameras to the list of cameras + if(istype(c, /obj/machinery/camera)) + if(choice == 0) + // Remove the camera. + chunk.cameras -= c + else if(choice == 1) + // You can't have the same camera in the list twice. + chunk.cameras |= c diff --git a/code/modules/mob/freelook/ai/chunk.dm b/code/modules/mob/freelook/ai/chunk.dm index ebd68a62069..8f5a9317eca 100644 --- a/code/modules/mob/freelook/ai/chunk.dm +++ b/code/modules/mob/freelook/ai/chunk.dm @@ -1,48 +1,48 @@ -// CAMERA CHUNK -// -// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed. -// Allows the Eye to stream these chunks and know what it can and cannot see. - -/datum/chunk/camera - var/list/cameras = list() - -/datum/chunk/camera/acquireVisibleTurfs(var/list/visible) - for(var/obj/machinery/camera/c as anything in cameras) - - if(!istype(c)) - cameras -= c - continue - - if(!c.can_use()) - continue - - var/turf/point = locate(src.x + 8, src.y + 8, src.z) - if(get_dist(point, c) > 24) - cameras -= c - - for(var/turf/t in c.can_see()) - visible[t] = t - - for(var/mob/living/silicon/ai/AI in living_mob_list) - for(var/turf/t in AI.seen_camera_turfs()) - visible[t] = t - -// Create a new camera chunk, since the chunks are made as they are needed. - -/datum/chunk/camera/New(loc, x, y, z) - for(var/obj/machinery/camera/c in range(16, locate(x + 8, y + 8, z))) - if(c.can_use()) - cameras += c - ..() - -/mob/living/silicon/proc/provides_camera_vision() - return 0 - -/mob/living/silicon/ai/provides_camera_vision() - return stat != DEAD - -/mob/living/silicon/robot/provides_camera_vision() - return src.camera && src.camera.network.len && (z in using_map.contact_levels) //VOREStation Edit - -/mob/living/silicon/ai/proc/seen_camera_turfs() - return seen_turfs_in_range(src, world.view) +// CAMERA CHUNK +// +// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed. +// Allows the Eye to stream these chunks and know what it can and cannot see. + +/datum/chunk/camera + var/list/cameras = list() + +/datum/chunk/camera/acquireVisibleTurfs(var/list/visible) + for(var/obj/machinery/camera/c as anything in cameras) + + if(!istype(c)) + cameras -= c + continue + + if(!c.can_use()) + continue + + var/turf/point = locate(src.x + 8, src.y + 8, src.z) + if(get_dist(point, c) > 24) + cameras -= c + + for(var/turf/t in c.can_see()) + visible[t] = t + + for(var/mob/living/silicon/ai/AI in living_mob_list) + for(var/turf/t in AI.seen_camera_turfs()) + visible[t] = t + +// Create a new camera chunk, since the chunks are made as they are needed. + +/datum/chunk/camera/New(loc, x, y, z) + for(var/obj/machinery/camera/c in range(16, locate(x + 8, y + 8, z))) + if(c.can_use()) + cameras += c + ..() + +/mob/living/silicon/proc/provides_camera_vision() + return 0 + +/mob/living/silicon/ai/provides_camera_vision() + return stat != DEAD + +/mob/living/silicon/robot/provides_camera_vision() + return src.camera && src.camera.network.len && (z in using_map.contact_levels) //VOREStation Edit + +/mob/living/silicon/ai/proc/seen_camera_turfs() + return seen_turfs_in_range(src, world.view) diff --git a/code/modules/mob/freelook/ai/eye.dm b/code/modules/mob/freelook/ai/eye.dm index 711dbd30855..fa3fb311f0e 100644 --- a/code/modules/mob/freelook/ai/eye.dm +++ b/code/modules/mob/freelook/ai/eye.dm @@ -1,115 +1,115 @@ -// AI EYE -// -// A mob that the AI controls to look around the station with. -// It streams chunks as it moves around, which will show it what the AI can and cannot see. - -/mob/observer/eye/aiEye - name = "Inactive AI Eye" - icon_state = "AI-eye" - -/mob/observer/eye/aiEye/New() - ..() - visualnet = cameranet - -/mob/observer/eye/aiEye/Destroy() - if(owner) - var/mob/living/silicon/ai/ai = owner - ai.all_eyes -= src - owner = null - . = ..() - -/mob/observer/eye/aiEye/setLoc(var/T, var/cancel_tracking = 1) - if(owner) - T = get_turf(T) - loc = T - - var/mob/living/silicon/ai/ai = owner - if(cancel_tracking) - ai.ai_cancel_tracking() - - if(use_static) - ai.camera_visibility(src) - - if(ai.client && !ai.multicam_on) - ai.client.eye = src - - if(ai.master_multicam) - ai.master_multicam.refresh_view() - - if(ai.holo) - if(ai.hologram_follow) - ai.holo.move_hologram(ai) - - return 1 - -// AI MOVEMENT - -// The AI's "eye". Described on the top of the page. - -/mob/living/silicon/ai - var/obj/machinery/hologram/holopad/holo = null - -/mob/living/silicon/ai/proc/destroy_eyeobj(var/atom/new_eye) - if(!eyeobj) return - if(!new_eye) - new_eye = src - eyeobj.owner = null - qdel(eyeobj) // No AI, no Eye - eyeobj = null - if(client) - client.eye = new_eye - -/mob/living/silicon/ai/proc/create_eyeobj(var/newloc) - if(eyeobj) - destroy_eyeobj() - if(!newloc) - newloc = src.loc - eyeobj = new /mob/observer/eye/aiEye(newloc) - all_eyes += eyeobj - eyeobj.owner = src - eyeobj.name = "[src.name] (AI Eye)" // Give it a name - if(client) - client.eye = eyeobj - SetName(src.name) - -// Intiliaze the eye by assigning it's "ai" variable to us. Then set it's loc to us. -/mob/living/silicon/ai/Initialize() - . = ..() - create_eyeobj() - if(eyeobj) - eyeobj.loc = src.loc - -/mob/living/silicon/ai/Destroy() - destroy_eyeobj() - return ..() - -/atom/proc/move_camera_by_click() - if(istype(usr, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/AI = usr - if(AI.eyeobj && (AI.multicam_on || (AI.client.eye == AI.eyeobj))) - var/turf/T = get_turf(src) - if(T) - AI.eyeobj.setLoc(T) - -/mob/living/silicon/ai/proc/view_core() - camera = null - unset_machine() - - if(!src.eyeobj) - return - - if(client && client.eye) - client.eye = src - for(var/datum/chunk/c in eyeobj.visibleChunks) - c.remove(eyeobj) - src.eyeobj.setLoc(src) - -/mob/living/silicon/ai/proc/toggle_acceleration() - set category = "AI Settings" - set name = "Toggle Camera Acceleration" - - if(!eyeobj) - return - - eyeobj.acceleration = !eyeobj.acceleration - to_chat(usr, "Camera acceleration has been toggled [eyeobj.acceleration ? "on" : "off"].") +// AI EYE +// +// A mob that the AI controls to look around the station with. +// It streams chunks as it moves around, which will show it what the AI can and cannot see. + +/mob/observer/eye/aiEye + name = "Inactive AI Eye" + icon_state = "AI-eye" + +/mob/observer/eye/aiEye/New() + ..() + visualnet = cameranet + +/mob/observer/eye/aiEye/Destroy() + if(owner) + var/mob/living/silicon/ai/ai = owner + ai.all_eyes -= src + owner = null + . = ..() + +/mob/observer/eye/aiEye/setLoc(var/T, var/cancel_tracking = 1) + if(owner) + T = get_turf(T) + loc = T + + var/mob/living/silicon/ai/ai = owner + if(cancel_tracking) + ai.ai_cancel_tracking() + + if(use_static) + ai.camera_visibility(src) + + if(ai.client && !ai.multicam_on) + ai.client.eye = src + + if(ai.master_multicam) + ai.master_multicam.refresh_view() + + if(ai.holo) + if(ai.hologram_follow) + ai.holo.move_hologram(ai) + + return 1 + +// AI MOVEMENT + +// The AI's "eye". Described on the top of the page. + +/mob/living/silicon/ai + var/obj/machinery/hologram/holopad/holo = null + +/mob/living/silicon/ai/proc/destroy_eyeobj(var/atom/new_eye) + if(!eyeobj) return + if(!new_eye) + new_eye = src + eyeobj.owner = null + qdel(eyeobj) // No AI, no Eye + eyeobj = null + if(client) + client.eye = new_eye + +/mob/living/silicon/ai/proc/create_eyeobj(var/newloc) + if(eyeobj) + destroy_eyeobj() + if(!newloc) + newloc = src.loc + eyeobj = new /mob/observer/eye/aiEye(newloc) + all_eyes += eyeobj + eyeobj.owner = src + eyeobj.name = "[src.name] (AI Eye)" // Give it a name + if(client) + client.eye = eyeobj + SetName(src.name) + +// Intiliaze the eye by assigning it's "ai" variable to us. Then set it's loc to us. +/mob/living/silicon/ai/Initialize() + . = ..() + create_eyeobj() + if(eyeobj) + eyeobj.loc = src.loc + +/mob/living/silicon/ai/Destroy() + destroy_eyeobj() + return ..() + +/atom/proc/move_camera_by_click() + if(istype(usr, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/AI = usr + if(AI.eyeobj && (AI.multicam_on || (AI.client.eye == AI.eyeobj))) + var/turf/T = get_turf(src) + if(T) + AI.eyeobj.setLoc(T) + +/mob/living/silicon/ai/proc/view_core() + camera = null + unset_machine() + + if(!src.eyeobj) + return + + if(client && client.eye) + client.eye = src + for(var/datum/chunk/c in eyeobj.visibleChunks) + c.remove(eyeobj) + src.eyeobj.setLoc(src) + +/mob/living/silicon/ai/proc/toggle_acceleration() + set category = "AI Settings" + set name = "Toggle Camera Acceleration" + + if(!eyeobj) + return + + eyeobj.acceleration = !eyeobj.acceleration + to_chat(usr, "Camera acceleration has been toggled [eyeobj.acceleration ? "on" : "off"].") diff --git a/code/modules/mob/freelook/ai/update_triggers.dm b/code/modules/mob/freelook/ai/update_triggers.dm index 9fd28344df2..3ce11bb42c7 100644 --- a/code/modules/mob/freelook/ai/update_triggers.dm +++ b/code/modules/mob/freelook/ai/update_triggers.dm @@ -1,72 +1,72 @@ -#define BORG_CAMERA_BUFFER 30 - -// ROBOT MOVEMENT - -// Update the portable camera everytime the Robot moves. -// This might be laggy, comment it out if there are problems. -/mob/living/silicon/var/updating = 0 - -/mob/living/silicon/robot/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(!provides_camera_vision()) - return - if(!updating) - updating = 1 - spawn(BORG_CAMERA_BUFFER) - if(old_loc != src.loc) - cameranet.updatePortableCamera(src.camera) - updating = 0 - -/mob/living/silicon/ai/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(!provides_camera_vision()) - return - if(!updating) - updating = 1 - spawn(BORG_CAMERA_BUFFER) - if(old_loc != src.loc) - cameranet.updateVisibility(old_loc, 0) - cameranet.updateVisibility(loc, 0) - updating = 0 - -#undef BORG_CAMERA_BUFFER - -// CAMERA - -// An addition to deactivate which removes/adds the camera from the chunk list based on if it works or not. - -/obj/machinery/camera/deactivate(user as mob, var/choice = 1) - ..(user, choice) - if(src.can_use()) - cameranet.addCamera(src) - else - src.set_light(0) - cameranet.removeCamera(src) - -/obj/machinery/camera/New() - ..() - //Camera must be added to global list of all cameras no matter what... - if(cameranet.cameras_unsorted || !ticker) - cameranet.cameras += src - cameranet.cameras_unsorted = 1 - else - dd_insertObjectList(cameranet.cameras, src) - update_coverage(1) - -/obj/machinery/camera/Destroy() - clear_all_networks() - cameranet.cameras -= src - return ..() - -// Mobs -/mob/living/silicon/ai/rejuvenate() - var/was_dead = stat == DEAD - ..() - if(was_dead && stat != DEAD) - // Arise! - cameranet.updateVisibility(src, 0) - -/mob/living/silicon/ai/death(gibbed) - if(..()) - // If true, the mob went from living to dead (assuming everyone has been overriding as they should...) - cameranet.updateVisibility(src, 0) +#define BORG_CAMERA_BUFFER 30 + +// ROBOT MOVEMENT + +// Update the portable camera everytime the Robot moves. +// This might be laggy, comment it out if there are problems. +/mob/living/silicon/var/updating = 0 + +/mob/living/silicon/robot/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(!provides_camera_vision()) + return + if(!updating) + updating = 1 + spawn(BORG_CAMERA_BUFFER) + if(old_loc != src.loc) + cameranet.updatePortableCamera(src.camera) + updating = 0 + +/mob/living/silicon/ai/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(!provides_camera_vision()) + return + if(!updating) + updating = 1 + spawn(BORG_CAMERA_BUFFER) + if(old_loc != src.loc) + cameranet.updateVisibility(old_loc, 0) + cameranet.updateVisibility(loc, 0) + updating = 0 + +#undef BORG_CAMERA_BUFFER + +// CAMERA + +// An addition to deactivate which removes/adds the camera from the chunk list based on if it works or not. + +/obj/machinery/camera/deactivate(user as mob, var/choice = 1) + ..(user, choice) + if(src.can_use()) + cameranet.addCamera(src) + else + src.set_light(0) + cameranet.removeCamera(src) + +/obj/machinery/camera/New() + ..() + //Camera must be added to global list of all cameras no matter what... + if(cameranet.cameras_unsorted || !ticker) + cameranet.cameras += src + cameranet.cameras_unsorted = 1 + else + dd_insertObjectList(cameranet.cameras, src) + update_coverage(1) + +/obj/machinery/camera/Destroy() + clear_all_networks() + cameranet.cameras -= src + return ..() + +// Mobs +/mob/living/silicon/ai/rejuvenate() + var/was_dead = stat == DEAD + ..() + if(was_dead && stat != DEAD) + // Arise! + cameranet.updateVisibility(src, 0) + +/mob/living/silicon/ai/death(gibbed) + if(..()) + // If true, the mob went from living to dead (assuming everyone has been overriding as they should...) + cameranet.updateVisibility(src, 0) diff --git a/code/modules/mob/freelook/chunk.dm b/code/modules/mob/freelook/chunk.dm index 4ae5d3a590a..8886207e81d 100644 --- a/code/modules/mob/freelook/chunk.dm +++ b/code/modules/mob/freelook/chunk.dm @@ -1,149 +1,149 @@ -#define UPDATE_BUFFER 25 // 2.5 seconds - -// CHUNK -// -// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed. -// Allows the Eye to stream these chunks and know what it can and cannot see. - -/datum/obfuscation - var/icon = 'icons/effects/cameravis.dmi' - var/icon_state = "black" - -/datum/chunk - var/list/obscuredTurfs = list() - var/list/visibleTurfs = list() - var/list/obscured = list() - var/list/turfs = list() - var/list/seenby = list() - var/visible = 0 - var/changed = 0 - var/updating = 0 - var/x = 0 - var/y = 0 - var/z = 0 - var/datum/obfuscation/obfuscation = new() - -// Add an eye to the chunk, then update if changed. - -/datum/chunk/proc/add(mob/observer/eye/eye, add_images = TRUE) - if(add_images) - var/client/client = eye.GetViewerClient() - if(client) - client.images += obscured - eye.visibleChunks += src - visible++ - seenby += eye - if(changed && !updating) - update() - -// Remove an eye from the chunk, then update if changed. - -/datum/chunk/proc/remove(mob/observer/eye/eye, remove_images = TRUE) - if(remove_images) - var/client/client = eye.GetViewerClient() - if(client) - client.images -= obscured - eye.visibleChunks -= src - seenby -= eye - if(visible > 0) - visible-- - -// Called when a chunk has changed. I.E: A wall was deleted. - -/datum/chunk/proc/visibilityChanged(turf/loc) - if(!visibleTurfs[loc]) - return - hasChanged() - -// Updates the chunk, makes sure that it doesn't update too much. If the chunk isn't being watched it will -// instead be flagged to update the next time an AI Eye moves near it. - -/datum/chunk/proc/hasChanged(var/update_now = 0) - if(visible || update_now) - if(!updating) - updating = 1 - spawn(UPDATE_BUFFER) // Batch large changes, such as many doors opening or closing at once - update() - updating = 0 - else - changed = 1 - -// The actual updating. - -/datum/chunk/proc/update() - - set background = 1 - - var/list/newVisibleTurfs = new() - acquireVisibleTurfs(newVisibleTurfs) - - // Removes turf that isn't in turfs. - newVisibleTurfs &= turfs - - var/list/visAdded = newVisibleTurfs - visibleTurfs - var/list/visRemoved = visibleTurfs - newVisibleTurfs - - visibleTurfs = newVisibleTurfs - obscuredTurfs = turfs - newVisibleTurfs - - for(var/turf/t as anything in visAdded) - if(LAZYLEN(t.obfuscations) && t.obfuscations[obfuscation.type]) - obscured -= t.obfuscations[obfuscation.type] - for(var/mob/observer/eye/m as anything in seenby) - if(!m) - continue - var/client/client = m.GetViewerClient() - if(client) - client.images -= t.obfuscations[obfuscation.type] - - for(var/turf/t as anything in visRemoved) - if(obscuredTurfs[t]) - LAZYINITLIST(t.obfuscations) - if(!t.obfuscations[obfuscation.type]) - var/image/ob_image = image(obfuscation.icon, t, obfuscation.icon_state, OBFUSCATION_LAYER) - ob_image.plane = PLANE_FULLSCREEN - t.obfuscations[obfuscation.type] = ob_image - - obscured += t.obfuscations[obfuscation.type] - for(var/mob/observer/eye/m as anything in seenby) - if(!m) - seenby -= m - continue - var/client/client = m.GetViewerClient() - if(client) - client.images += t.obfuscations[obfuscation.type] - -/datum/chunk/proc/acquireVisibleTurfs(var/list/visible) - -// Create a new camera chunk, since the chunks are made as they are needed. - -/datum/chunk/New(loc, x, y, z) - - // 0xf = 15 - x &= ~0xf - y &= ~0xf - - src.x = x - src.y = y - src.z = z - - for(var/turf/t in range(10, locate(x + 8, y + 8, z))) - if(t.x >= x && t.y >= y && t.x < x + 16 && t.y < y + 16) - turfs[t] = t - - acquireVisibleTurfs(visibleTurfs) - - // Removes turf that isn't in turfs. - visibleTurfs &= turfs - - obscuredTurfs = turfs - visibleTurfs - - for(var/turf/t as anything in obscuredTurfs) - LAZYINITLIST(t.obfuscations) - if(!t.obfuscations[obfuscation.type]) - var/image/ob_image = image(obfuscation.icon, t, obfuscation.icon_state, OBFUSCATION_LAYER) - ob_image.plane = PLANE_FULLSCREEN - t.obfuscations[obfuscation.type] = ob_image - obscured += t.obfuscations[obfuscation.type] - -#undef UPDATE_BUFFER +#define UPDATE_BUFFER 25 // 2.5 seconds + +// CHUNK +// +// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed. +// Allows the Eye to stream these chunks and know what it can and cannot see. + +/datum/obfuscation + var/icon = 'icons/effects/cameravis.dmi' + var/icon_state = "black" + +/datum/chunk + var/list/obscuredTurfs = list() + var/list/visibleTurfs = list() + var/list/obscured = list() + var/list/turfs = list() + var/list/seenby = list() + var/visible = 0 + var/changed = 0 + var/updating = 0 + var/x = 0 + var/y = 0 + var/z = 0 + var/datum/obfuscation/obfuscation = new() + +// Add an eye to the chunk, then update if changed. + +/datum/chunk/proc/add(mob/observer/eye/eye, add_images = TRUE) + if(add_images) + var/client/client = eye.GetViewerClient() + if(client) + client.images += obscured + eye.visibleChunks += src + visible++ + seenby += eye + if(changed && !updating) + update() + +// Remove an eye from the chunk, then update if changed. + +/datum/chunk/proc/remove(mob/observer/eye/eye, remove_images = TRUE) + if(remove_images) + var/client/client = eye.GetViewerClient() + if(client) + client.images -= obscured + eye.visibleChunks -= src + seenby -= eye + if(visible > 0) + visible-- + +// Called when a chunk has changed. I.E: A wall was deleted. + +/datum/chunk/proc/visibilityChanged(turf/loc) + if(!visibleTurfs[loc]) + return + hasChanged() + +// Updates the chunk, makes sure that it doesn't update too much. If the chunk isn't being watched it will +// instead be flagged to update the next time an AI Eye moves near it. + +/datum/chunk/proc/hasChanged(var/update_now = 0) + if(visible || update_now) + if(!updating) + updating = 1 + spawn(UPDATE_BUFFER) // Batch large changes, such as many doors opening or closing at once + update() + updating = 0 + else + changed = 1 + +// The actual updating. + +/datum/chunk/proc/update() + + set background = 1 + + var/list/newVisibleTurfs = new() + acquireVisibleTurfs(newVisibleTurfs) + + // Removes turf that isn't in turfs. + newVisibleTurfs &= turfs + + var/list/visAdded = newVisibleTurfs - visibleTurfs + var/list/visRemoved = visibleTurfs - newVisibleTurfs + + visibleTurfs = newVisibleTurfs + obscuredTurfs = turfs - newVisibleTurfs + + for(var/turf/t as anything in visAdded) + if(LAZYLEN(t.obfuscations) && t.obfuscations[obfuscation.type]) + obscured -= t.obfuscations[obfuscation.type] + for(var/mob/observer/eye/m as anything in seenby) + if(!m) + continue + var/client/client = m.GetViewerClient() + if(client) + client.images -= t.obfuscations[obfuscation.type] + + for(var/turf/t as anything in visRemoved) + if(obscuredTurfs[t]) + LAZYINITLIST(t.obfuscations) + if(!t.obfuscations[obfuscation.type]) + var/image/ob_image = image(obfuscation.icon, t, obfuscation.icon_state, OBFUSCATION_LAYER) + ob_image.plane = PLANE_FULLSCREEN + t.obfuscations[obfuscation.type] = ob_image + + obscured += t.obfuscations[obfuscation.type] + for(var/mob/observer/eye/m as anything in seenby) + if(!m) + seenby -= m + continue + var/client/client = m.GetViewerClient() + if(client) + client.images += t.obfuscations[obfuscation.type] + +/datum/chunk/proc/acquireVisibleTurfs(var/list/visible) + +// Create a new camera chunk, since the chunks are made as they are needed. + +/datum/chunk/New(loc, x, y, z) + + // 0xf = 15 + x &= ~0xf + y &= ~0xf + + src.x = x + src.y = y + src.z = z + + for(var/turf/t in range(10, locate(x + 8, y + 8, z))) + if(t.x >= x && t.y >= y && t.x < x + 16 && t.y < y + 16) + turfs[t] = t + + acquireVisibleTurfs(visibleTurfs) + + // Removes turf that isn't in turfs. + visibleTurfs &= turfs + + obscuredTurfs = turfs - visibleTurfs + + for(var/turf/t as anything in obscuredTurfs) + LAZYINITLIST(t.obfuscations) + if(!t.obfuscations[obfuscation.type]) + var/image/ob_image = image(obfuscation.icon, t, obfuscation.icon_state, OBFUSCATION_LAYER) + ob_image.plane = PLANE_FULLSCREEN + t.obfuscations[obfuscation.type] = ob_image + obscured += t.obfuscations[obfuscation.type] + +#undef UPDATE_BUFFER diff --git a/code/modules/mob/freelook/eye.dm b/code/modules/mob/freelook/eye.dm index 05aba49008b..80b1125c8b2 100644 --- a/code/modules/mob/freelook/eye.dm +++ b/code/modules/mob/freelook/eye.dm @@ -1,111 +1,111 @@ -// EYE -// -// A mob that another mob controls to look around the station with. -// It streams chunks as it moves around, which will show it what the controller can and cannot see. - -/mob/observer/eye - name = "Eye" - icon = 'icons/mob/eye.dmi' - icon_state = "default-eye" - alpha = 127 - - var/sprint = 10 - var/cooldown = 0 - var/acceleration = 1 - var/owner_follows_eye = 0 - - see_in_dark = 7 - status_flags = GODMODE - plane = PLANE_AI_EYE - - var/mob/owner = null - var/list/visibleChunks = list() - - var/ghostimage = null - var/datum/visualnet/visualnet - var/use_static = TRUE - var/static_visibility_range = 16 - -/mob/observer/eye/Destroy() - if(owner) - if(owner.eyeobj == src) - owner.eyeobj = null - owner = null - . = ..() - -/mob/observer/eye/Move(n, direct) - if(owner == src) - return EyeMove(n, direct) - return 0 - -/mob/observer/eye/airflow_hit(atom/A) - airflow_speed = 0 - airflow_dest = null - -/mob/observer/eye/examinate() - set popup_menu = 0 - set src = usr.contents - return 0 - -/mob/observer/eye/pointed() - set popup_menu = 0 - set src = usr.contents - return 0 - -// Use this when setting the eye's location. -// It will also stream the chunk that the new loc is in. -/mob/observer/eye/proc/setLoc(var/T) - if(owner) - T = get_turf(T) - if(T != loc) - loc = T - - if(owner.client) - owner.client.eye = src - - if(owner_follows_eye) - visualnet.updateVisibility(owner, 0) - owner.loc = loc - visualnet.updateVisibility(owner, 0) - if(use_static) - visualnet.visibility(src, owner.client) - return 1 - return 0 - -/mob/observer/eye/proc/getLoc() - if(owner) - if(!isturf(owner.loc) || !owner.client) - return - return loc -/mob - var/mob/observer/eye/eyeobj - -/mob/proc/EyeMove(n, direct) - if(!eyeobj) - return - - return eyeobj.EyeMove(n, direct) - -/mob/observer/eye/proc/GetViewerClient() - if(owner) - return owner.client - return null - -/mob/observer/eye/EyeMove(n, direct) - var/initial = initial(sprint) - var/max_sprint = 50 - - if(cooldown && cooldown < world.timeofday) - sprint = initial - - for(var/i = 0; i < max(sprint, initial); i += 20) - var/turf/step = get_turf(get_step(src, direct)) - if(step) - setLoc(step) - - cooldown = world.timeofday + 5 - if(acceleration) - sprint = min(sprint + 0.5, max_sprint) - else - sprint = initial - return 1 +// EYE +// +// A mob that another mob controls to look around the station with. +// It streams chunks as it moves around, which will show it what the controller can and cannot see. + +/mob/observer/eye + name = "Eye" + icon = 'icons/mob/eye.dmi' + icon_state = "default-eye" + alpha = 127 + + var/sprint = 10 + var/cooldown = 0 + var/acceleration = 1 + var/owner_follows_eye = 0 + + see_in_dark = 7 + status_flags = GODMODE + plane = PLANE_AI_EYE + + var/mob/owner = null + var/list/visibleChunks = list() + + var/ghostimage = null + var/datum/visualnet/visualnet + var/use_static = TRUE + var/static_visibility_range = 16 + +/mob/observer/eye/Destroy() + if(owner) + if(owner.eyeobj == src) + owner.eyeobj = null + owner = null + . = ..() + +/mob/observer/eye/Move(n, direct) + if(owner == src) + return EyeMove(n, direct) + return 0 + +/mob/observer/eye/airflow_hit(atom/A) + airflow_speed = 0 + airflow_dest = null + +/mob/observer/eye/examinate() + set popup_menu = 0 + set src = usr.contents + return 0 + +/mob/observer/eye/pointed() + set popup_menu = 0 + set src = usr.contents + return 0 + +// Use this when setting the eye's location. +// It will also stream the chunk that the new loc is in. +/mob/observer/eye/proc/setLoc(var/T) + if(owner) + T = get_turf(T) + if(T != loc) + loc = T + + if(owner.client) + owner.client.eye = src + + if(owner_follows_eye) + visualnet.updateVisibility(owner, 0) + owner.loc = loc + visualnet.updateVisibility(owner, 0) + if(use_static) + visualnet.visibility(src, owner.client) + return 1 + return 0 + +/mob/observer/eye/proc/getLoc() + if(owner) + if(!isturf(owner.loc) || !owner.client) + return + return loc +/mob + var/mob/observer/eye/eyeobj + +/mob/proc/EyeMove(n, direct) + if(!eyeobj) + return + + return eyeobj.EyeMove(n, direct) + +/mob/observer/eye/proc/GetViewerClient() + if(owner) + return owner.client + return null + +/mob/observer/eye/EyeMove(n, direct) + var/initial = initial(sprint) + var/max_sprint = 50 + + if(cooldown && cooldown < world.timeofday) + sprint = initial + + for(var/i = 0; i < max(sprint, initial); i += 20) + var/turf/step = get_turf(get_step(src, direct)) + if(step) + setLoc(step) + + cooldown = world.timeofday + 5 + if(acceleration) + sprint = min(sprint + 0.5, max_sprint) + else + sprint = initial + return 1 diff --git a/code/modules/mob/freelook/life.dm b/code/modules/mob/freelook/life.dm index f3994f9a7e6..1ef0ca92a5d 100644 --- a/code/modules/mob/freelook/life.dm +++ b/code/modules/mob/freelook/life.dm @@ -1,7 +1,7 @@ -/mob/observer/eye/Life() - ..() - // If we lost our client, reset the list of visible chunks so they update properly on return - if(owner == src && !client) - visibleChunks.Cut() - /*else if(owner && !owner.client) - visibleChunks.Cut()*/ +/mob/observer/eye/Life() + ..() + // If we lost our client, reset the list of visible chunks so they update properly on return + if(owner == src && !client) + visibleChunks.Cut() + /*else if(owner && !owner.client) + visibleChunks.Cut()*/ diff --git a/code/modules/mob/freelook/mask/chunk.dm b/code/modules/mob/freelook/mask/chunk.dm index b540a8023b6..5042c126b9f 100644 --- a/code/modules/mob/freelook/mask/chunk.dm +++ b/code/modules/mob/freelook/mask/chunk.dm @@ -1,36 +1,36 @@ -// CULT CHUNK -// -// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed. -// Allows the Eye to stream these chunks and know what it can and cannot see. - -/datum/obfuscation/cult - icon_state = "white" - -/datum/chunk/cult - obfuscation = new /datum/obfuscation/cult() - -/datum/chunk/cult/acquireVisibleTurfs(var/list/visible) - for(var/mob/living/L in living_mob_list) - for(var/turf/t in L.seen_cult_turfs()) - visible[t] = t - -/mob/living/proc/seen_cult_turfs() - return seen_turfs_in_range(src, 3) - -/mob/living/carbon/human/seen_cult_turfs() - if(mind in cult.current_antagonists) - return seen_turfs_in_range(src, world.view) - return ..() - -/mob/living/silicon/seen_cult_turfs() - return list() - -/mob/living/simple_mob/seen_cult_turfs() - return seen_turfs_in_range(src, 1) - -/mob/living/simple_mob/construct/shade/seen_cult_turfs() - return view(2, src) - -/proc/seen_turfs_in_range(var/source, var/range) - var/turf/pos = get_turf(source) - return hear(range, pos) +// CULT CHUNK +// +// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed. +// Allows the Eye to stream these chunks and know what it can and cannot see. + +/datum/obfuscation/cult + icon_state = "white" + +/datum/chunk/cult + obfuscation = new /datum/obfuscation/cult() + +/datum/chunk/cult/acquireVisibleTurfs(var/list/visible) + for(var/mob/living/L in living_mob_list) + for(var/turf/t in L.seen_cult_turfs()) + visible[t] = t + +/mob/living/proc/seen_cult_turfs() + return seen_turfs_in_range(src, 3) + +/mob/living/carbon/human/seen_cult_turfs() + if(mind in cult.current_antagonists) + return seen_turfs_in_range(src, world.view) + return ..() + +/mob/living/silicon/seen_cult_turfs() + return list() + +/mob/living/simple_mob/seen_cult_turfs() + return seen_turfs_in_range(src, 1) + +/mob/living/simple_mob/construct/shade/seen_cult_turfs() + return view(2, src) + +/proc/seen_turfs_in_range(var/source, var/range) + var/turf/pos = get_turf(source) + return hear(range, pos) diff --git a/code/modules/mob/freelook/mask/cultnet.dm b/code/modules/mob/freelook/mask/cultnet.dm index 4e4e01b8472..6d2f248a2e3 100644 --- a/code/modules/mob/freelook/mask/cultnet.dm +++ b/code/modules/mob/freelook/mask/cultnet.dm @@ -1,15 +1,15 @@ -// CULT NET -// -// The datum containing all the chunks. - -/datum/visualnet/cult - chunk_type = /datum/chunk/cult - -/datum/visualnet/cult/proc/provides_vision(var/mob/living/L) - return L.provides_cult_vision() - -/mob/living/proc/provides_cult_vision() - return 1 - -/mob/living/silicon/provides_cult_vision() - return 0 +// CULT NET +// +// The datum containing all the chunks. + +/datum/visualnet/cult + chunk_type = /datum/chunk/cult + +/datum/visualnet/cult/proc/provides_vision(var/mob/living/L) + return L.provides_cult_vision() + +/mob/living/proc/provides_cult_vision() + return 1 + +/mob/living/silicon/provides_cult_vision() + return 0 diff --git a/code/modules/mob/freelook/mask/eye.dm b/code/modules/mob/freelook/mask/eye.dm index b3abb922abd..4fcbc35149f 100644 --- a/code/modules/mob/freelook/mask/eye.dm +++ b/code/modules/mob/freelook/mask/eye.dm @@ -1,13 +1,13 @@ -// MASK EYE -// -// A mob that a cultists controls to look around the station with. -// It streams chunks as it moves around, which will show it what the cultist can and cannot see. - -/mob/observer/eye/maskEye - name = "Eye of Nar-Sie" - acceleration = 0 - owner_follows_eye = 1 - -/mob/observer/eye/maskEye/New() - ..() - visualnet = cultnet +// MASK EYE +// +// A mob that a cultists controls to look around the station with. +// It streams chunks as it moves around, which will show it what the cultist can and cannot see. + +/mob/observer/eye/maskEye + name = "Eye of Nar-Sie" + acceleration = 0 + owner_follows_eye = 1 + +/mob/observer/eye/maskEye/New() + ..() + visualnet = cultnet diff --git a/code/modules/mob/freelook/mask/update_triggers.dm b/code/modules/mob/freelook/mask/update_triggers.dm index 8008916220c..5ddf74de3c7 100644 --- a/code/modules/mob/freelook/mask/update_triggers.dm +++ b/code/modules/mob/freelook/mask/update_triggers.dm @@ -1,49 +1,49 @@ -//UPDATE TRIGGERS, when the chunk (and the surrounding chunks) should update. - -#define CULT_UPDATE_BUFFER 30 - -/mob/living/var/updating_cult_vision = 0 - -/mob/living/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(!cultnet.provides_vision(src)) - return - if(!updating_cult_vision) - updating_cult_vision = 1 - spawn(CULT_UPDATE_BUFFER) - if(old_loc != src.loc) - cultnet.updateVisibility(old_loc, 0) - cultnet.updateVisibility(loc, 0) - updating_cult_vision = 0 - -#undef CULT_UPDATE_BUFFER - -/mob/living/New() - ..() - cultnet.updateVisibility(src, 0) - -/mob/living/Destroy() - cultnet.updateVisibility(src, 0) - return ..() - -/mob/living/rejuvenate() - var/was_dead = stat == DEAD - ..() - if(was_dead && stat != DEAD) - // Arise! - cultnet.updateVisibility(src, 0) - -/mob/living/death(gibbed, deathmessage="seizes up and falls limp...") - if(..(gibbed, deathmessage)) - // If true, the mob went from living to dead (assuming everyone has been overriding as they should...) - cultnet.updateVisibility(src) - -/datum/antagonist/add_antagonist(var/datum/mind/player) - . = ..() - if(src == cult) - cultnet.updateVisibility(player.current, 0) - -/datum/antagonist/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted) - ..() - if(src == cult) - cultnet.updateVisibility(player.current, 0) +//UPDATE TRIGGERS, when the chunk (and the surrounding chunks) should update. + +#define CULT_UPDATE_BUFFER 30 + +/mob/living/var/updating_cult_vision = 0 + +/mob/living/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(!cultnet.provides_vision(src)) + return + if(!updating_cult_vision) + updating_cult_vision = 1 + spawn(CULT_UPDATE_BUFFER) + if(old_loc != src.loc) + cultnet.updateVisibility(old_loc, 0) + cultnet.updateVisibility(loc, 0) + updating_cult_vision = 0 + +#undef CULT_UPDATE_BUFFER + +/mob/living/New() + ..() + cultnet.updateVisibility(src, 0) + +/mob/living/Destroy() + cultnet.updateVisibility(src, 0) + return ..() + +/mob/living/rejuvenate() + var/was_dead = stat == DEAD + ..() + if(was_dead && stat != DEAD) + // Arise! + cultnet.updateVisibility(src, 0) + +/mob/living/death(gibbed, deathmessage="seizes up and falls limp...") + if(..(gibbed, deathmessage)) + // If true, the mob went from living to dead (assuming everyone has been overriding as they should...) + cultnet.updateVisibility(src) + +/datum/antagonist/add_antagonist(var/datum/mind/player) + . = ..() + if(src == cult) + cultnet.updateVisibility(player.current, 0) + +/datum/antagonist/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted) + ..() + if(src == cult) + cultnet.updateVisibility(player.current, 0) diff --git a/code/modules/mob/freelook/read_me.dm b/code/modules/mob/freelook/read_me.dm index 8ddb0689409..380a3e0171e 100644 --- a/code/modules/mob/freelook/read_me.dm +++ b/code/modules/mob/freelook/read_me.dm @@ -1,51 +1,51 @@ -// CREDITS -/* - Initial code credit for this goes to Uristqwerty. - Debugging, functionality, all comments and porting by Giacom. - - Everything about freelook (or what we can put in here) will be stored here. - - - WHAT IS THIS? - - This is a replacement for the current camera movement system, of the AI. Before this, the AI had to move between cameras and could - only see what the cameras could see. Not only this but the cameras could see through walls, which created problems. - With this, the AI controls an "AI Eye" mob, which moves just like a ghost; such as moving through walls and being invisible to players. - The AI's eye is set to this mob and then we use a system (explained below) to determine what the cameras around the AI Eye can and - cannot see. If the camera cannot see a turf, it will black it out, otherwise it won't and the AI will be able to see it. - This creates several features, such as.. no more see-through-wall cameras, easier to control camera movement, easier tracking, - the AI only being able to track mobs which are visible to a camera, only trackable mobs appearing on the mob list and many more. - - - HOW IT WORKS - - It works by first creating a camera network datum. Inside of this camera network are "chunks" (which will be - explained later) and "cameras". The cameras list is kept up to date by obj/machinery/camera/New() and Destroy(). - - Next the camera network has chunks. These chunks are a 16x16 tile block of turfs and cameras contained inside the chunk. - These turfs are then sorted out based on what the cameras can and cannot see. If none of the cameras can see the turf, inside - the 16x16 block, it is listed as an "obscured" turf. Meaning the AI won't be able to see it. - - - HOW IT UPDATES - - The camera network uses a streaming method in order to effeciently update chunks. Since the server will have doors opening, doors closing, - turf being destroyed and other lag inducing stuff, we want to update it under certain conditions and not every tick. - - The chunks are not created straight away, only when an AI eye moves into it's area is when it gets created. - One a chunk is created, when a non glass door opens/closes or an opacity turf is destroyed, we check to see if an AI Eye is looking in the area. - We do this with the "seenby" list, which updates everytime an AI is near a chunk. If there is an AI eye inside the area, we update the chunk - that the changed atom is inside and all surrounding chunks, since a camera's vision could leak onto another chunk. If there is no AI Eye, we instead - flag the chunk to update whenever it is loaded by an AI Eye. This is basically how the chunks update and keep it in sync. We then add some lag reducing - measures, such as an UPDATE_BUFFER which stops a chunk from updating too many times in a certain time-frame, only updating if the changed atom was blocking - sight; for example, we don't update glass airlocks or floors. - - - WHERE IS EVERYTHING? - - cameranet.dm = Everything about the cameranet datum. - chunk.dm = Everything about the chunk datum. - eye.dm = Everything about the AI and the AIEye. - updating.dm = Everything about triggers that will update chunks. - +// CREDITS +/* + Initial code credit for this goes to Uristqwerty. + Debugging, functionality, all comments and porting by Giacom. + + Everything about freelook (or what we can put in here) will be stored here. + + + WHAT IS THIS? + + This is a replacement for the current camera movement system, of the AI. Before this, the AI had to move between cameras and could + only see what the cameras could see. Not only this but the cameras could see through walls, which created problems. + With this, the AI controls an "AI Eye" mob, which moves just like a ghost; such as moving through walls and being invisible to players. + The AI's eye is set to this mob and then we use a system (explained below) to determine what the cameras around the AI Eye can and + cannot see. If the camera cannot see a turf, it will black it out, otherwise it won't and the AI will be able to see it. + This creates several features, such as.. no more see-through-wall cameras, easier to control camera movement, easier tracking, + the AI only being able to track mobs which are visible to a camera, only trackable mobs appearing on the mob list and many more. + + + HOW IT WORKS + + It works by first creating a camera network datum. Inside of this camera network are "chunks" (which will be + explained later) and "cameras". The cameras list is kept up to date by obj/machinery/camera/New() and Destroy(). + + Next the camera network has chunks. These chunks are a 16x16 tile block of turfs and cameras contained inside the chunk. + These turfs are then sorted out based on what the cameras can and cannot see. If none of the cameras can see the turf, inside + the 16x16 block, it is listed as an "obscured" turf. Meaning the AI won't be able to see it. + + + HOW IT UPDATES + + The camera network uses a streaming method in order to effeciently update chunks. Since the server will have doors opening, doors closing, + turf being destroyed and other lag inducing stuff, we want to update it under certain conditions and not every tick. + + The chunks are not created straight away, only when an AI eye moves into it's area is when it gets created. + One a chunk is created, when a non glass door opens/closes or an opacity turf is destroyed, we check to see if an AI Eye is looking in the area. + We do this with the "seenby" list, which updates everytime an AI is near a chunk. If there is an AI eye inside the area, we update the chunk + that the changed atom is inside and all surrounding chunks, since a camera's vision could leak onto another chunk. If there is no AI Eye, we instead + flag the chunk to update whenever it is loaded by an AI Eye. This is basically how the chunks update and keep it in sync. We then add some lag reducing + measures, such as an UPDATE_BUFFER which stops a chunk from updating too many times in a certain time-frame, only updating if the changed atom was blocking + sight; for example, we don't update glass airlocks or floors. + + + WHERE IS EVERYTHING? + + cameranet.dm = Everything about the cameranet datum. + chunk.dm = Everything about the chunk datum. + eye.dm = Everything about the AI and the AIEye. + updating.dm = Everything about triggers that will update chunks. + */ \ No newline at end of file diff --git a/code/modules/mob/freelook/update_triggers.dm b/code/modules/mob/freelook/update_triggers.dm index 02d36e54f56..62b8b503d3d 100644 --- a/code/modules/mob/freelook/update_triggers.dm +++ b/code/modules/mob/freelook/update_triggers.dm @@ -1,59 +1,59 @@ -//UPDATE TRIGGERS, when the chunk (and the surrounding chunks) should update. - -// TURFS - -/proc/updateVisibility(atom/A, var/opacity_check = 1) - if(ticker) - for(var/datum/visualnet/VN in visual_nets) - VN.updateVisibility(A, opacity_check) - -/turf - var/list/image/obfuscations - -/turf/drain_power() - return -1 - -/turf/simulated/Destroy() - updateVisibility(src) - if(zone) - if(can_safely_remove_from_zone()) - c_copy_air() - zone.remove(src) - else - zone.rebuild() - return ..() - -/turf/simulated/Initialize() - . = ..() - updateVisibility(src) - - -// STRUCTURES - -/obj/structure/Destroy() - updateVisibility(src) - return ..() - -/obj/structure/New() - ..() - updateVisibility(src) - -// EFFECTS - -/obj/effect/Destroy() - updateVisibility(src) - return ..() - -/obj/effect/Initialize() - . = ..() - updateVisibility(src) - -// DOORS - -// Simply updates the visibility of the area when it opens/closes/destroyed. -/obj/machinery/door/update_nearby_tiles(need_rebuild) - . = ..(need_rebuild) - // Glass door glass = 1 - // don't check then? - if(!glass) +//UPDATE TRIGGERS, when the chunk (and the surrounding chunks) should update. + +// TURFS + +/proc/updateVisibility(atom/A, var/opacity_check = 1) + if(ticker) + for(var/datum/visualnet/VN in visual_nets) + VN.updateVisibility(A, opacity_check) + +/turf + var/list/image/obfuscations + +/turf/drain_power() + return -1 + +/turf/simulated/Destroy() + updateVisibility(src) + if(zone) + if(can_safely_remove_from_zone()) + c_copy_air() + zone.remove(src) + else + zone.rebuild() + return ..() + +/turf/simulated/Initialize() + . = ..() + updateVisibility(src) + + +// STRUCTURES + +/obj/structure/Destroy() + updateVisibility(src) + return ..() + +/obj/structure/New() + ..() + updateVisibility(src) + +// EFFECTS + +/obj/effect/Destroy() + updateVisibility(src) + return ..() + +/obj/effect/Initialize() + . = ..() + updateVisibility(src) + +// DOORS + +// Simply updates the visibility of the area when it opens/closes/destroyed. +/obj/machinery/door/update_nearby_tiles(need_rebuild) + . = ..(need_rebuild) + // Glass door glass = 1 + // don't check then? + if(!glass) updateVisibility(src, 0) \ No newline at end of file diff --git a/code/modules/mob/freelook/visualnet.dm b/code/modules/mob/freelook/visualnet.dm index 1508a301333..271572abe8b 100644 --- a/code/modules/mob/freelook/visualnet.dm +++ b/code/modules/mob/freelook/visualnet.dm @@ -1,161 +1,161 @@ -// VISUAL NET -// -// The datum containing all the chunks. - -#define CHUNK_SIZE 16 - -/datum/visualnet - // The chunks of the map, mapping the areas that an object can see. - var/list/chunks = list() - var/ready = 0 - var/chunk_type = /datum/chunk - -/datum/visualnet/New() - ..() - visual_nets += src - -/datum/visualnet/Destroy() - visual_nets -= src - return ..() - -// Checks if a chunk has been Generated in x, y, z. -/datum/visualnet/proc/chunkGenerated(x, y, z) - x &= ~0xf - y &= ~0xf - var/key = "[x],[y],[z]" - return (chunks[key]) - -// Returns the chunk in the x, y, z. -// If there is no chunk, it creates a new chunk and returns that. -/datum/visualnet/proc/getChunk(x, y, z) - x &= ~0xf - y &= ~0xf - var/key = "[x],[y],[z]" - if(!chunks[key]) - chunks[key] = new chunk_type(null, x, y, z) - - return chunks[key] - -// Updates what the aiEye can see. It is recommended you use this when the aiEye moves or it's location is set. - -/datum/visualnet/proc/visibility(list/moved_eyes, client/C, list/other_eyes) - if(!islist(moved_eyes)) - moved_eyes = moved_eyes ? list(moved_eyes) : list() - if(islist(other_eyes)) - other_eyes = (other_eyes - moved_eyes) - else - other_eyes = list() - - var/list/chunks_pre_seen = list() - var/list/chunks_post_seen = list() - - for(var/mob/observer/eye/eye as anything in moved_eyes) - if(C) - chunks_pre_seen |= eye.visibleChunks - // 0xf = 15 - var/static_range = eye.static_visibility_range - var/x1 = max(0, eye.x - static_range) & ~(CHUNK_SIZE - 1) - var/y1 = max(0, eye.y - static_range) & ~(CHUNK_SIZE - 1) - var/x2 = min(world.maxx, eye.x + static_range) & ~(CHUNK_SIZE - 1) - var/y2 = min(world.maxy, eye.y + static_range) & ~(CHUNK_SIZE - 1) - - var/list/visibleChunks = list() - - for(var/x = x1; x <= x2; x += CHUNK_SIZE) - for(var/y = y1; y <= y2; y += CHUNK_SIZE) - visibleChunks |= getChunk(x, y, eye.z) - - var/list/remove = eye.visibleChunks - visibleChunks - var/list/add = visibleChunks - eye.visibleChunks - - for(var/datum/chunk/c as anything in remove) - c.remove(eye, FALSE) - - for(var/datum/chunk/c as anything in add) - c.add(eye, FALSE) - - if(C) - chunks_post_seen |= eye.visibleChunks - - if(C) - for(var/mob/observer/eye/eye as anything in other_eyes) - chunks_post_seen |= eye.visibleChunks - - var/list/remove = chunks_pre_seen - chunks_post_seen - var/list/add = chunks_post_seen - chunks_pre_seen - - for(var/datum/chunk/c as anything in remove) - C.images -= c.obscured - - for(var/datum/chunk/c as anything in add) - C.images += c.obscured - -// Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open. - -/datum/visualnet/proc/updateVisibility(atom/A, var/opacity_check = 1) - - if(!ticker || (opacity_check && !A.opacity)) - return - majorChunkChange(A, 2) - -/datum/visualnet/proc/updateChunk(x, y, z) - // 0xf = 15 - if(!chunkGenerated(x, y, z)) - return - var/datum/chunk/chunk = getChunk(x, y, z) - chunk.hasChanged() - -// Never access this proc directly!!!! -// This will update the chunk and all the surrounding chunks. -// It will also add the atom to the cameras list if you set the choice to 1. -// Setting the choice to 0 will remove the camera from the chunks. -// If you want to update the chunks around an object, without adding/removing a camera, use choice 2. - -/datum/visualnet/proc/majorChunkChange(atom/c, var/choice) - // 0xf = 15 - if(!c) - return - - var/turf/T = get_turf(c) - if(T) - var/x1 = max(0, T.x - 8) & ~0xf - var/y1 = max(0, T.y - 8) & ~0xf - var/x2 = min(world.maxx, T.x + 8) & ~0xf - var/y2 = min(world.maxy, T.y + 8) & ~0xf - - //to_world("X1: [x1] - Y1: [y1] - X2: [x2] - Y2: [y2]") - - for(var/x = x1; x <= x2; x += 16) - for(var/y = y1; y <= y2; y += 16) - if(chunkGenerated(x, y, T.z)) - var/datum/chunk/chunk = getChunk(x, y, T.z) - onMajorChunkChange(c, choice, chunk) - chunk.hasChanged() - -/datum/visualnet/proc/onMajorChunkChange(atom/c, var/choice, var/datum/chunk/chunk) - -// Will check if a mob is on a viewable turf. Returns 1 if it is, otherwise returns 0. - -/datum/visualnet/proc/checkVis(mob/living/target as mob) - // 0xf = 15 - var/turf/position = get_turf(target) - return checkTurfVis(position) - -/datum/visualnet/proc/checkTurfVis(var/turf/position) - var/datum/chunk/chunk = getChunk(position.x, position.y, position.z) - if(chunk) - if(chunk.changed) - chunk.hasChanged(1) // Update now, no matter if it's visible or not. - if(chunk.visibleTurfs[position]) - return 1 - return 0 - -// Debug verb for VVing the chunk that the turf is in. -/* -/turf/verb/view_chunk() - set src in world - - if(cameranet.chunkGenerated(x, y, z)) - var/datum/chunk/chunk = cameranet.getCameraChunk(x, y, z) - usr.client.debug_variables(chunk) -*/ +// VISUAL NET +// +// The datum containing all the chunks. + +#define CHUNK_SIZE 16 + +/datum/visualnet + // The chunks of the map, mapping the areas that an object can see. + var/list/chunks = list() + var/ready = 0 + var/chunk_type = /datum/chunk + +/datum/visualnet/New() + ..() + visual_nets += src + +/datum/visualnet/Destroy() + visual_nets -= src + return ..() + +// Checks if a chunk has been Generated in x, y, z. +/datum/visualnet/proc/chunkGenerated(x, y, z) + x &= ~0xf + y &= ~0xf + var/key = "[x],[y],[z]" + return (chunks[key]) + +// Returns the chunk in the x, y, z. +// If there is no chunk, it creates a new chunk and returns that. +/datum/visualnet/proc/getChunk(x, y, z) + x &= ~0xf + y &= ~0xf + var/key = "[x],[y],[z]" + if(!chunks[key]) + chunks[key] = new chunk_type(null, x, y, z) + + return chunks[key] + +// Updates what the aiEye can see. It is recommended you use this when the aiEye moves or it's location is set. + +/datum/visualnet/proc/visibility(list/moved_eyes, client/C, list/other_eyes) + if(!islist(moved_eyes)) + moved_eyes = moved_eyes ? list(moved_eyes) : list() + if(islist(other_eyes)) + other_eyes = (other_eyes - moved_eyes) + else + other_eyes = list() + + var/list/chunks_pre_seen = list() + var/list/chunks_post_seen = list() + + for(var/mob/observer/eye/eye as anything in moved_eyes) + if(C) + chunks_pre_seen |= eye.visibleChunks + // 0xf = 15 + var/static_range = eye.static_visibility_range + var/x1 = max(0, eye.x - static_range) & ~(CHUNK_SIZE - 1) + var/y1 = max(0, eye.y - static_range) & ~(CHUNK_SIZE - 1) + var/x2 = min(world.maxx, eye.x + static_range) & ~(CHUNK_SIZE - 1) + var/y2 = min(world.maxy, eye.y + static_range) & ~(CHUNK_SIZE - 1) + + var/list/visibleChunks = list() + + for(var/x = x1; x <= x2; x += CHUNK_SIZE) + for(var/y = y1; y <= y2; y += CHUNK_SIZE) + visibleChunks |= getChunk(x, y, eye.z) + + var/list/remove = eye.visibleChunks - visibleChunks + var/list/add = visibleChunks - eye.visibleChunks + + for(var/datum/chunk/c as anything in remove) + c.remove(eye, FALSE) + + for(var/datum/chunk/c as anything in add) + c.add(eye, FALSE) + + if(C) + chunks_post_seen |= eye.visibleChunks + + if(C) + for(var/mob/observer/eye/eye as anything in other_eyes) + chunks_post_seen |= eye.visibleChunks + + var/list/remove = chunks_pre_seen - chunks_post_seen + var/list/add = chunks_post_seen - chunks_pre_seen + + for(var/datum/chunk/c as anything in remove) + C.images -= c.obscured + + for(var/datum/chunk/c as anything in add) + C.images += c.obscured + +// Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open. + +/datum/visualnet/proc/updateVisibility(atom/A, var/opacity_check = 1) + + if(!ticker || (opacity_check && !A.opacity)) + return + majorChunkChange(A, 2) + +/datum/visualnet/proc/updateChunk(x, y, z) + // 0xf = 15 + if(!chunkGenerated(x, y, z)) + return + var/datum/chunk/chunk = getChunk(x, y, z) + chunk.hasChanged() + +// Never access this proc directly!!!! +// This will update the chunk and all the surrounding chunks. +// It will also add the atom to the cameras list if you set the choice to 1. +// Setting the choice to 0 will remove the camera from the chunks. +// If you want to update the chunks around an object, without adding/removing a camera, use choice 2. + +/datum/visualnet/proc/majorChunkChange(atom/c, var/choice) + // 0xf = 15 + if(!c) + return + + var/turf/T = get_turf(c) + if(T) + var/x1 = max(0, T.x - 8) & ~0xf + var/y1 = max(0, T.y - 8) & ~0xf + var/x2 = min(world.maxx, T.x + 8) & ~0xf + var/y2 = min(world.maxy, T.y + 8) & ~0xf + + //to_world("X1: [x1] - Y1: [y1] - X2: [x2] - Y2: [y2]") + + for(var/x = x1; x <= x2; x += 16) + for(var/y = y1; y <= y2; y += 16) + if(chunkGenerated(x, y, T.z)) + var/datum/chunk/chunk = getChunk(x, y, T.z) + onMajorChunkChange(c, choice, chunk) + chunk.hasChanged() + +/datum/visualnet/proc/onMajorChunkChange(atom/c, var/choice, var/datum/chunk/chunk) + +// Will check if a mob is on a viewable turf. Returns 1 if it is, otherwise returns 0. + +/datum/visualnet/proc/checkVis(mob/living/target as mob) + // 0xf = 15 + var/turf/position = get_turf(target) + return checkTurfVis(position) + +/datum/visualnet/proc/checkTurfVis(var/turf/position) + var/datum/chunk/chunk = getChunk(position.x, position.y, position.z) + if(chunk) + if(chunk.changed) + chunk.hasChanged(1) // Update now, no matter if it's visible or not. + if(chunk.visibleTurfs[position]) + return 1 + return 0 + +// Debug verb for VVing the chunk that the turf is in. +/* +/turf/verb/view_chunk() + set src in world + + if(cameranet.chunkGenerated(x, y, z)) + var/datum/chunk/chunk = cameranet.getCameraChunk(x, y, z) + usr.client.debug_variables(chunk) +*/ diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index 6bb57e488ba..202d4c65812 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -38,9 +38,6 @@ raw_msg += (piece + " ") - if(!speaker.client) - piece = "[piece]" - //HTML formatting if(!SP.speaking) // Catch the most generic case first piece = "[piece]" @@ -125,7 +122,7 @@ if(check_mentioned(multilingual_to_message(message_pieces)) && is_preference_enabled(/datum/client_preference/check_mention)) message_to_send = "[message_to_send]" - on_hear_say(message_to_send) + on_hear_say(message_to_send, speaker) create_chat_message(speaker, combined["raw"], italics, list()) if(speech_sound && (get_dist(speaker, src) <= world.view && z == speaker.z)) @@ -140,25 +137,29 @@ if(has_AI()) // Won't happen if no ai_holder exists or there's a player inside w/o autopilot active. ai_holder.on_hear_say(speaker, multilingual_to_message(message_pieces)) -/mob/proc/on_hear_say(var/message) +/mob/proc/on_hear_say(var/message, var/mob/speaker = null) var/time = say_timestamp() if(client) if(client.prefs.chat_timestamp) - to_chat(src, "[time] [message]") - else - to_chat(src, "[message]") + message = "[time] [message]" + message = "[message]" + if(speaker && !speaker.client) + message = "[message]" + to_chat(src, message) else if(teleop) to_chat(teleop, "[create_text_tag("body", "BODY:", teleop.client)][message]") else to_chat(src, "[message]") -/mob/living/silicon/on_hear_say(var/message) +/mob/living/silicon/on_hear_say(var/message, var/mob/speaker = null) var/time = say_timestamp() if(client) if(client.prefs.chat_timestamp) - to_chat(src, "[time] [message]") - else - to_chat(src, "[message]") + message = "[time] [message]" + message = "[message]" + if(speaker && !speaker.client) + message = "[message]" + to_chat(src, message) else if(teleop) to_chat(teleop, "[create_text_tag("body", "BODY:", teleop.client)][message]") else @@ -330,4 +331,6 @@ name = speaker.voice_name var/rendered = "[name] [message]" + if(!speaker.client) + rendered = "[rendered]" to_chat(src, rendered) diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index eec77b02ab4..c9015d36d92 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -1,271 +1,271 @@ -//The list of slots by priority. equip_to_appropriate_slot() uses this list. Doesn't matter if a mob type doesn't have a slot. -var/list/slot_equipment_priority = list( \ - slot_back,\ - slot_wear_id,\ - slot_w_uniform,\ - slot_wear_suit,\ - slot_wear_mask,\ - slot_head,\ - slot_shoes,\ - slot_gloves,\ - slot_l_ear,\ - slot_r_ear,\ - slot_glasses,\ - slot_belt,\ - slot_s_store,\ - slot_tie,\ - slot_l_store,\ - slot_r_store\ - ) - -/mob - var/obj/item/weapon/storage/s_active = null // Even ghosts can/should be able to peek into boxes on the ground - -//This proc is called whenever someone clicks an inventory ui slot. -/mob/proc/attack_ui(var/slot) - var/obj/item/W = get_active_hand() - - var/obj/item/E = get_equipped_item(slot) - if (istype(E)) - if(istype(W)) - E.attackby(W,src) - else - E.attack_hand(src) - else - equip_to_slot_if_possible(W, slot) - -/* Inventory manipulation */ - -/mob/proc/put_in_any_hand_if_possible(obj/item/W as obj, del_on_fail = 0, disable_warning = 1, redraw_mob = 1) - if(equip_to_slot_if_possible(W, slot_l_hand, del_on_fail, disable_warning, redraw_mob)) - return 1 - else if(equip_to_slot_if_possible(W, slot_r_hand, del_on_fail, disable_warning, redraw_mob)) - return 1 - return 0 - -//This is a SAFE proc. Use this instead of equip_to_slot()! -//set del_on_fail to have it delete W if it fails to equip -//set disable_warning to disable the 'you are unable to equip that' warning. -//unset redraw_mob to prevent the mob from being redrawn at the end. -/mob/proc/equip_to_slot_if_possible(obj/item/W as obj, slot, del_on_fail = 0, disable_warning = 0, redraw_mob = 1, ignore_obstructions = 1) - if(!W) - return 0 - if(!W.mob_can_equip(src, slot, disable_warning, ignore_obstructions)) - if(del_on_fail) - qdel(W) - - else - if(!disable_warning) - to_chat(src, span_red("You are unable to equip that.")) //Only print if del_on_fail is false - return 0 - - equip_to_slot(W, slot, redraw_mob) //This proc should not ever fail. - return 1 - -//This is an UNSAFE proc. It merely handles the actual job of equipping. All the checks on whether you can or can't eqip need to be done before! Use mob_can_equip() for that task. -//In most cases you will want to use equip_to_slot_if_possible() -/mob/proc/equip_to_slot(obj/item/W as obj, slot) - return - -//This is just a commonly used configuration for the equip_to_slot_if_possible() proc, used to equip people when the rounds tarts and when events happen and such. -/mob/proc/equip_to_slot_or_del(obj/item/W as obj, slot, ignore_obstructions = 1) - return equip_to_slot_if_possible(W, slot, 1, 1, 0, ignore_obstructions) - -//hurgh. these feel hacky, but they're the only way I could get the damn thing to work. I guess they could be handy for antag spawners too? -/mob/proc/equip_voidsuit_to_slot_or_del_with_refit(obj/item/clothing/suit/space/void/W as obj, slot, species = SPECIES_HUMAN) - W.refit_for_species(species) - return equip_to_slot_if_possible(W, slot, 1, 1, 0) - -/mob/proc/equip_voidhelm_to_slot_or_del_with_refit(obj/item/clothing/head/helmet/space/void/W as obj, slot, species = SPECIES_HUMAN) - W.refit_for_species(species) - return equip_to_slot_if_possible(W, slot, 1, 1, 0) - -//Checks if a given slot can be accessed at this time, either to equip or unequip I -/mob/proc/slot_is_accessible(var/slot, var/obj/item/I, mob/user=null) - return 1 - -//puts the item "W" into an appropriate slot in a human's inventory -//returns 0 if it cannot, 1 if successful -/mob/proc/equip_to_appropriate_slot(obj/item/W) - for(var/slot in slot_equipment_priority) - if(equip_to_slot_if_possible(W, slot, del_on_fail=0, disable_warning=1, redraw_mob=1)) - return 1 - - return 0 - -/mob/proc/equip_to_storage(obj/item/newitem, user_initiated = FALSE) - return 0 - -/* Hands */ - -//Returns the thing in our active hand -/mob/proc/get_active_hand() - -//Returns the thing in our inactive hand -/mob/proc/get_inactive_hand() - -// Override for your specific mob's hands or lack thereof. -/mob/proc/is_holding_item_of_type(typepath) - return FALSE - -// Override for your specific mob's hands or lack thereof. -/mob/proc/get_all_held_items() - return list() - -//Puts the item into your l_hand if possible and calls all necessary triggers/updates. returns 1 on success. -/mob/proc/put_in_l_hand(var/obj/item/W) - if(lying || !istype(W)) - return 0 - return 1 - -//Puts the item into your r_hand if possible and calls all necessary triggers/updates. returns 1 on success. -/mob/proc/put_in_r_hand(var/obj/item/W) - if(lying || !istype(W)) - return 0 - return 1 - -//Puts the item into our active hand if possible. returns 1 on success. -/mob/proc/put_in_active_hand(var/obj/item/W) - return 0 // Moved to human procs because only they need to use hands. - -//Puts the item into our inactive hand if possible. returns 1 on success. -/mob/proc/put_in_inactive_hand(var/obj/item/W) - return 0 // As above. - -//Puts the item our active hand if possible. Failing that it tries our inactive hand. Returns 1 on success. -//If both fail it drops it on the floor and returns 0. -//This is probably the main one you need to know :) -/mob/proc/put_in_hands(var/obj/item/W) - if(!W) - return 0 - W.forceMove(drop_location()) - W.reset_plane_and_layer() - W.dropped() - return 0 - -// Removes an item from inventory and places it in the target atom. -// If canremove or other conditions need to be checked then use unEquip instead. - -/mob/proc/drop_from_inventory(var/obj/item/W, var/atom/target) - if(W) - remove_from_mob(W, target) - return TRUE - return FALSE - -//Drops the item in our left hand -/mob/proc/drop_l_hand(var/atom/Target) - return 0 - -//Drops the item in our right hand -/mob/proc/drop_r_hand(var/atom/Target) - return 0 - -//Drops the item in our active hand. TODO: rename this to drop_active_hand or something -/mob/proc/drop_item(var/atom/Target) - return -/* - Removes the object from any slots the mob might have, calling the appropriate icon update proc. - Does nothing else. - - DO NOT CALL THIS PROC DIRECTLY. It is meant to be called only by other inventory procs. - It's probably okay to use it if you are transferring the item between slots on the same mob, - but chances are you're safer calling remove_from_mob() or drop_from_inventory() anyways. - - As far as I can tell the proc exists so that mobs with different inventory slots can override - the search through all the slots, without having to duplicate the rest of the item dropping. -*/ -/mob/proc/u_equip(obj/W as obj) - -/mob/proc/isEquipped(obj/item/I) - if(!I) - return 0 - return get_inventory_slot(I) != 0 - -/mob/proc/canUnEquip(obj/item/I) - if(!I) //If there's nothing to drop, the drop is automatically successful. - return 1 - var/slot = get_inventory_slot(I) - return I.mob_can_unequip(src, slot) - -/mob/proc/get_inventory_slot(obj/item/I) - var/slot = 0 - for(var/s in 1 to SLOT_TOTAL) - if(get_equipped_item(s) == I) - slot = s - break - return slot - - -//This differs from remove_from_mob() in that it checks if the item can be unequipped first. -/mob/proc/unEquip(obj/item/I, force = 0, var/atom/target) //Force overrides NODROP for things like wizarditis and admin undress. - if(!(force || canUnEquip(I))) - return FALSE - drop_from_inventory(I, target) - return TRUE - -//visibly unequips I but it is NOT MOVED AND REMAINS IN SRC -//item MUST BE FORCEMOVE'D OR QDEL'D -/mob/proc/temporarilyRemoveItemFromInventory(obj/item/I, force = FALSE, idrop = TRUE) - return u_equip(I, force, null, TRUE, idrop) - -///sometimes we only want to grant the item's action if it's equipped in a specific slot. -/obj/item/proc/item_action_slot_check(slot, mob/user) - if(slot == SLOT_BACK || slot == LEGS) //these aren't true slots, so avoid granting actions there - return FALSE - return TRUE - -///Get the item on the mob in the storage slot identified by the id passed in -/mob/proc/get_item_by_slot(slot_id) - return null - -/mob/proc/getBackSlot() - return SLOT_BACK - -//Attemps to remove an object on a mob. -/mob/proc/remove_from_mob(var/obj/O, var/atom/target) - if(!O) // Nothing to remove, so we succeed. - return 1 - src.u_equip(O) - if (src.client) - src.client.screen -= O - O.reset_plane_and_layer() - O.screen_loc = null - if(istype(O, /obj/item)) - var/obj/item/I = O - if(target) - I.forceMove(target) - else - I.dropInto(drop_location()) - I.dropped(src) - return TRUE - -//Returns the item equipped to the specified slot, if any. -/mob/proc/get_equipped_item(var/slot) - return null - -//Outdated but still in use apparently. This should at least be a human proc. -/mob/proc/get_equipped_items() - var/list/items = new/list() - - if(hasvar(src,"back")) if(src:back) items += src:back - if(hasvar(src,"belt")) if(src:belt) items += src:belt - if(hasvar(src,"l_ear")) if(src:l_ear) items += src:l_ear - if(hasvar(src,"r_ear")) if(src:r_ear) items += src:r_ear - if(hasvar(src,"glasses")) if(src:glasses) items += src:glasses - if(hasvar(src,"gloves")) if(src:gloves) items += src:gloves - if(hasvar(src,"head")) if(src:head) items += src:head - if(hasvar(src,"shoes")) if(src:shoes) items += src:shoes - if(hasvar(src,"wear_id")) if(src:wear_id) items += src:wear_id - if(hasvar(src,"wear_mask")) if(src:wear_mask) items += src:wear_mask - if(hasvar(src,"wear_suit")) if(src:wear_suit) items += src:wear_suit - if(hasvar(src,"w_uniform")) if(src:w_uniform) items += src:w_uniform - - if(hasvar(src,"l_hand")) if(src:l_hand) items += src:l_hand - if(hasvar(src,"r_hand")) if(src:r_hand) items += src:r_hand - - return items - -/mob/proc/delete_inventory() - for(var/entry in get_equipped_items()) - drop_from_inventory(entry) - qdel(entry) +//The list of slots by priority. equip_to_appropriate_slot() uses this list. Doesn't matter if a mob type doesn't have a slot. +var/list/slot_equipment_priority = list( \ + slot_back,\ + slot_wear_id,\ + slot_w_uniform,\ + slot_wear_suit,\ + slot_wear_mask,\ + slot_head,\ + slot_shoes,\ + slot_gloves,\ + slot_l_ear,\ + slot_r_ear,\ + slot_glasses,\ + slot_belt,\ + slot_s_store,\ + slot_tie,\ + slot_l_store,\ + slot_r_store\ + ) + +/mob + var/obj/item/weapon/storage/s_active = null // Even ghosts can/should be able to peek into boxes on the ground + +//This proc is called whenever someone clicks an inventory ui slot. +/mob/proc/attack_ui(var/slot) + var/obj/item/W = get_active_hand() + + var/obj/item/E = get_equipped_item(slot) + if (istype(E)) + if(istype(W)) + E.attackby(W,src) + else + E.attack_hand(src) + else + equip_to_slot_if_possible(W, slot) + +/* Inventory manipulation */ + +/mob/proc/put_in_any_hand_if_possible(obj/item/W as obj, del_on_fail = 0, disable_warning = 1, redraw_mob = 1) + if(equip_to_slot_if_possible(W, slot_l_hand, del_on_fail, disable_warning, redraw_mob)) + return 1 + else if(equip_to_slot_if_possible(W, slot_r_hand, del_on_fail, disable_warning, redraw_mob)) + return 1 + return 0 + +//This is a SAFE proc. Use this instead of equip_to_slot()! +//set del_on_fail to have it delete W if it fails to equip +//set disable_warning to disable the 'you are unable to equip that' warning. +//unset redraw_mob to prevent the mob from being redrawn at the end. +/mob/proc/equip_to_slot_if_possible(obj/item/W as obj, slot, del_on_fail = 0, disable_warning = 0, redraw_mob = 1, ignore_obstructions = 1) + if(!W) + return 0 + if(!W.mob_can_equip(src, slot, disable_warning, ignore_obstructions)) + if(del_on_fail) + qdel(W) + + else + if(!disable_warning) + to_chat(src, span_red("You are unable to equip that.")) //Only print if del_on_fail is false + return 0 + + equip_to_slot(W, slot, redraw_mob) //This proc should not ever fail. + return 1 + +//This is an UNSAFE proc. It merely handles the actual job of equipping. All the checks on whether you can or can't eqip need to be done before! Use mob_can_equip() for that task. +//In most cases you will want to use equip_to_slot_if_possible() +/mob/proc/equip_to_slot(obj/item/W as obj, slot) + return + +//This is just a commonly used configuration for the equip_to_slot_if_possible() proc, used to equip people when the rounds tarts and when events happen and such. +/mob/proc/equip_to_slot_or_del(obj/item/W as obj, slot, ignore_obstructions = 1) + return equip_to_slot_if_possible(W, slot, 1, 1, 0, ignore_obstructions) + +//hurgh. these feel hacky, but they're the only way I could get the damn thing to work. I guess they could be handy for antag spawners too? +/mob/proc/equip_voidsuit_to_slot_or_del_with_refit(obj/item/clothing/suit/space/void/W as obj, slot, species = SPECIES_HUMAN) + W.refit_for_species(species) + return equip_to_slot_if_possible(W, slot, 1, 1, 0) + +/mob/proc/equip_voidhelm_to_slot_or_del_with_refit(obj/item/clothing/head/helmet/space/void/W as obj, slot, species = SPECIES_HUMAN) + W.refit_for_species(species) + return equip_to_slot_if_possible(W, slot, 1, 1, 0) + +//Checks if a given slot can be accessed at this time, either to equip or unequip I +/mob/proc/slot_is_accessible(var/slot, var/obj/item/I, mob/user=null) + return 1 + +//puts the item "W" into an appropriate slot in a human's inventory +//returns 0 if it cannot, 1 if successful +/mob/proc/equip_to_appropriate_slot(obj/item/W) + for(var/slot in slot_equipment_priority) + if(equip_to_slot_if_possible(W, slot, del_on_fail=0, disable_warning=1, redraw_mob=1)) + return 1 + + return 0 + +/mob/proc/equip_to_storage(obj/item/newitem, user_initiated = FALSE) + return 0 + +/* Hands */ + +//Returns the thing in our active hand +/mob/proc/get_active_hand() + +//Returns the thing in our inactive hand +/mob/proc/get_inactive_hand() + +// Override for your specific mob's hands or lack thereof. +/mob/proc/is_holding_item_of_type(typepath) + return FALSE + +// Override for your specific mob's hands or lack thereof. +/mob/proc/get_all_held_items() + return list() + +//Puts the item into your l_hand if possible and calls all necessary triggers/updates. returns 1 on success. +/mob/proc/put_in_l_hand(var/obj/item/W) + if(lying || !istype(W)) + return 0 + return 1 + +//Puts the item into your r_hand if possible and calls all necessary triggers/updates. returns 1 on success. +/mob/proc/put_in_r_hand(var/obj/item/W) + if(lying || !istype(W)) + return 0 + return 1 + +//Puts the item into our active hand if possible. returns 1 on success. +/mob/proc/put_in_active_hand(var/obj/item/W) + return 0 // Moved to human procs because only they need to use hands. + +//Puts the item into our inactive hand if possible. returns 1 on success. +/mob/proc/put_in_inactive_hand(var/obj/item/W) + return 0 // As above. + +//Puts the item our active hand if possible. Failing that it tries our inactive hand. Returns 1 on success. +//If both fail it drops it on the floor and returns 0. +//This is probably the main one you need to know :) +/mob/proc/put_in_hands(var/obj/item/W) + if(!W) + return 0 + W.forceMove(drop_location()) + W.reset_plane_and_layer() + W.dropped() + return 0 + +// Removes an item from inventory and places it in the target atom. +// If canremove or other conditions need to be checked then use unEquip instead. + +/mob/proc/drop_from_inventory(var/obj/item/W, var/atom/target) + if(W) + remove_from_mob(W, target) + return TRUE + return FALSE + +//Drops the item in our left hand +/mob/proc/drop_l_hand(var/atom/Target) + return 0 + +//Drops the item in our right hand +/mob/proc/drop_r_hand(var/atom/Target) + return 0 + +//Drops the item in our active hand. TODO: rename this to drop_active_hand or something +/mob/proc/drop_item(var/atom/Target) + return +/* + Removes the object from any slots the mob might have, calling the appropriate icon update proc. + Does nothing else. + + DO NOT CALL THIS PROC DIRECTLY. It is meant to be called only by other inventory procs. + It's probably okay to use it if you are transferring the item between slots on the same mob, + but chances are you're safer calling remove_from_mob() or drop_from_inventory() anyways. + + As far as I can tell the proc exists so that mobs with different inventory slots can override + the search through all the slots, without having to duplicate the rest of the item dropping. +*/ +/mob/proc/u_equip(obj/W as obj) + +/mob/proc/isEquipped(obj/item/I) + if(!I) + return 0 + return get_inventory_slot(I) != 0 + +/mob/proc/canUnEquip(obj/item/I) + if(!I) //If there's nothing to drop, the drop is automatically successful. + return 1 + var/slot = get_inventory_slot(I) + return I.mob_can_unequip(src, slot) + +/mob/proc/get_inventory_slot(obj/item/I) + var/slot = 0 + for(var/s in 1 to SLOT_TOTAL) + if(get_equipped_item(s) == I) + slot = s + break + return slot + + +//This differs from remove_from_mob() in that it checks if the item can be unequipped first. +/mob/proc/unEquip(obj/item/I, force = 0, var/atom/target) //Force overrides NODROP for things like wizarditis and admin undress. + if(!(force || canUnEquip(I))) + return FALSE + drop_from_inventory(I, target) + return TRUE + +//visibly unequips I but it is NOT MOVED AND REMAINS IN SRC +//item MUST BE FORCEMOVE'D OR QDEL'D +/mob/proc/temporarilyRemoveItemFromInventory(obj/item/I, force = FALSE, idrop = TRUE) + return u_equip(I, force, null, TRUE, idrop) + +///sometimes we only want to grant the item's action if it's equipped in a specific slot. +/obj/item/proc/item_action_slot_check(slot, mob/user) + if(slot == SLOT_BACK || slot == LEGS) //these aren't true slots, so avoid granting actions there + return FALSE + return TRUE + +///Get the item on the mob in the storage slot identified by the id passed in +/mob/proc/get_item_by_slot(slot_id) + return null + +/mob/proc/getBackSlot() + return SLOT_BACK + +//Attemps to remove an object on a mob. +/mob/proc/remove_from_mob(var/obj/O, var/atom/target) + if(!O) // Nothing to remove, so we succeed. + return 1 + src.u_equip(O) + if (src.client) + src.client.screen -= O + O.reset_plane_and_layer() + O.screen_loc = null + if(istype(O, /obj/item)) + var/obj/item/I = O + if(target) + I.forceMove(target) + else + I.dropInto(drop_location()) + I.dropped(src) + return TRUE + +//Returns the item equipped to the specified slot, if any. +/mob/proc/get_equipped_item(var/slot) + return null + +//Outdated but still in use apparently. This should at least be a human proc. +/mob/proc/get_equipped_items() + var/list/items = new/list() + + if(hasvar(src,"back")) if(src:back) items += src:back + if(hasvar(src,"belt")) if(src:belt) items += src:belt + if(hasvar(src,"l_ear")) if(src:l_ear) items += src:l_ear + if(hasvar(src,"r_ear")) if(src:r_ear) items += src:r_ear + if(hasvar(src,"glasses")) if(src:glasses) items += src:glasses + if(hasvar(src,"gloves")) if(src:gloves) items += src:gloves + if(hasvar(src,"head")) if(src:head) items += src:head + if(hasvar(src,"shoes")) if(src:shoes) items += src:shoes + if(hasvar(src,"wear_id")) if(src:wear_id) items += src:wear_id + if(hasvar(src,"wear_mask")) if(src:wear_mask) items += src:wear_mask + if(hasvar(src,"wear_suit")) if(src:wear_suit) items += src:wear_suit + if(hasvar(src,"w_uniform")) if(src:w_uniform) items += src:w_uniform + + if(hasvar(src,"l_hand")) if(src:l_hand) items += src:l_hand + if(hasvar(src,"r_hand")) if(src:r_hand) items += src:r_hand + + return items + +/mob/proc/delete_inventory() + for(var/entry in get_equipped_items()) + drop_from_inventory(entry) + qdel(entry) diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index 128fbf36907..1629913c76b 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -1,69 +1,69 @@ -/mob/living/carbon/alien - name = "alien" - desc = "What IS that?" - icon = 'icons/mob/alien.dmi' - icon_state = "alien" - pass_flags = PASSTABLE - health = 100 - maxHealth = 100 - mob_size = 4 - blocks_emissive = EMISSIVE_BLOCK_UNIQUE - - inventory_panel_type = null // Disable inventory - - var/adult_form - var/dead_icon - var/amount_grown = 0 - var/max_grown = 200 - var/time_of_birth - var/language - var/death_msg = "lets out a waning guttural screech, green blood bubbling from its maw." - var/can_namepick_as_adult = 0 - var/adult_name - var/instance_num - -/mob/living/carbon/alien/Initialize() - . = ..() - - time_of_birth = world.time - - verbs += /mob/living/proc/ventcrawl - verbs += /mob/living/proc/hide - - instance_num = rand(1, 1000) - name = "[initial(name)] ([instance_num])" - real_name = name - regenerate_icons() - - if(language) - add_language(language) - - gender = NEUTER - -/mob/living/carbon/alien/u_equip(obj/item/W as obj) - return - -/mob/living/carbon/alien/restrained() - return 0 - -/mob/living/carbon/alien/cannot_use_vents() - return - -/mob/living/carbon/alien/get_default_language() - if(default_language) - return default_language - return GLOB.all_languages["Xenomorph"] - -/mob/living/carbon/alien/say_quote(var/message, var/datum/language/speaking = null) - var/verb = "hisses" - var/ending = copytext(message, length(message)) - - if(speaking && (speaking.name != "Galactic Common")) //this is so adminbooze xenos speaking common have their custom verbs, - verb = speaking.get_spoken_verb(ending) //and use normal verbs for their own languages and non-common languages - else - if(ending == "!") - verb = "roars" - else if(ending == "?") - verb = "hisses curiously" - return verb - +/mob/living/carbon/alien + name = "alien" + desc = "What IS that?" + icon = 'icons/mob/alien.dmi' + icon_state = "alien" + pass_flags = PASSTABLE + health = 100 + maxHealth = 100 + mob_size = 4 + blocks_emissive = EMISSIVE_BLOCK_UNIQUE + + inventory_panel_type = null // Disable inventory + + var/adult_form + var/dead_icon + var/amount_grown = 0 + var/max_grown = 200 + var/time_of_birth + var/language + var/death_msg = "lets out a waning guttural screech, green blood bubbling from its maw." + var/can_namepick_as_adult = 0 + var/adult_name + var/instance_num + +/mob/living/carbon/alien/Initialize() + . = ..() + + time_of_birth = world.time + + verbs += /mob/living/proc/ventcrawl + verbs += /mob/living/proc/hide + + instance_num = rand(1, 1000) + name = "[initial(name)] ([instance_num])" + real_name = name + regenerate_icons() + + if(language) + add_language(language) + + gender = NEUTER + +/mob/living/carbon/alien/u_equip(obj/item/W as obj) + return + +/mob/living/carbon/alien/restrained() + return 0 + +/mob/living/carbon/alien/cannot_use_vents() + return + +/mob/living/carbon/alien/get_default_language() + if(default_language) + return default_language + return GLOB.all_languages["Xenomorph"] + +/mob/living/carbon/alien/say_quote(var/message, var/datum/language/speaking = null) + var/verb = "hisses" + var/ending = copytext(message, length(message)) + + if(speaking && (speaking.name != "Galactic Common")) //this is so adminbooze xenos speaking common have their custom verbs, + verb = speaking.get_spoken_verb(ending) //and use normal verbs for their own languages and non-common languages + else + if(ending == "!") + verb = "roars" + else if(ending == "?") + verb = "hisses curiously" + return verb + diff --git a/code/modules/mob/living/carbon/alien/life.dm b/code/modules/mob/living/carbon/alien/life.dm index df3c336a588..e474a33d720 100644 --- a/code/modules/mob/living/carbon/alien/life.dm +++ b/code/modules/mob/living/carbon/alien/life.dm @@ -1,161 +1,161 @@ -// Alien larva are quite simple. -/mob/living/carbon/alien/Life() - - set invisibility = 0 - set background = 1 - - if (transforming) return - if(!loc) return - - ..() - - if (stat != DEAD) //still breathing - // GROW! - update_progression() - - blinded = null - - //Status updates, death etc. - update_icons() - -/mob/living/carbon/alien/handle_mutations_and_radiation() - - // Currently both Dionaea and larvae like to eat radiation, so I'm defining the - // rad absorbtion here. This will need to be changed if other baby aliens are added. - - if(!radiation) - return - - var/rads = radiation/25 - radiation -= rads - //adjust_nutrition(rads) //Commented out to prevent alien obesity. - heal_overall_damage(rads,rads) - adjustOxyLoss(-(rads)) - adjustToxLoss(-(rads)) - return - -/mob/living/carbon/alien/handle_regular_status_updates() - - if(status_flags & GODMODE) return 0 - - if(stat == DEAD) - blinded = 1 - silent = 0 - else - updatehealth() - if(health <= 0) - death() - blinded = 1 - silent = 0 - return 1 - - if(paralysis && paralysis > 0) - blinded = 1 - set_stat(UNCONSCIOUS) - if(halloss > 0) - adjustHalLoss(-3) - - if(sleeping) - adjustHalLoss(-3) - if (mind) - if(mind.active && client != null) - AdjustSleeping(-1) - blinded = 1 - set_stat(UNCONSCIOUS) - else if(resting) - if(halloss > 0) - adjustHalLoss(-3) - - else - set_stat(CONSCIOUS) - if(halloss > 0) - adjustHalLoss(-1) - - // Eyes and blindness. - if(!has_eyes()) - SetBlinded(1) - blinded = 1 - eye_blurry = 1 - else if(eye_blind) - AdjustBlinded(-1) - blinded = 1 - else if(eye_blurry) - eye_blurry = max(eye_blurry-1, 0) - - update_icons() - - return 1 - -/mob/living/carbon/alien/handle_regular_hud_updates() - - if (stat == 2 || (XRAY in src.mutations)) - sight |= SEE_TURFS - sight |= SEE_MOBS - sight |= SEE_OBJS - see_in_dark = 8 - see_invisible = SEE_INVISIBLE_LEVEL_TWO - else if (stat != 2) - sight &= ~SEE_TURFS - sight &= ~SEE_MOBS - sight &= ~SEE_OBJS - see_in_dark = 2 - see_invisible = SEE_INVISIBLE_LIVING - - if (healths) - if (stat != 2) - switch(health) - if(100 to INFINITY) - healths.icon_state = "health0" - if(80 to 100) - healths.icon_state = "health1" - if(60 to 80) - healths.icon_state = "health2" - if(40 to 60) - healths.icon_state = "health3" - if(20 to 40) - healths.icon_state = "health4" - if(0 to 20) - healths.icon_state = "health5" - else - healths.icon_state = "health6" - else - healths.icon_state = "health7" - - if (client) - client.screen.Remove(global_hud.blurry,global_hud.druggy,global_hud.vimpaired) - - if ( stat != 2) - if ((blinded)) - overlay_fullscreen("blind", /obj/screen/fullscreen/blind) - else - clear_fullscreen("blind") - set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) - set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) - set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) - if(machine) - if(machine.check_eye(src) < 0) - reset_view(null) - else - if(client && !client.adminobs) - reset_view(null) - - return 1 - -/mob/living/carbon/alien/handle_environment(var/datum/gas_mixture/environment) - // Both alien subtypes survive in vaccum and suffer in high temperatures, - // so I'll just define this once, for both (see radiation comment above) - if(!environment) return - - if(environment.temperature > (T0C+66)) - adjustFireLoss((environment.temperature - (T0C+66))/5) // Might be too high, check in testing. - throw_alert("alien_fire", /obj/screen/alert/alien_fire) - if(prob(20)) - to_chat(src, span_red("You feel a searing heat!")) - else - clear_alert("alien_fire") - -/mob/living/carbon/alien/handle_fire() - if(..()) - return - bodytemperature += BODYTEMP_HEATING_MAX //If you're on fire, you heat up! - return +// Alien larva are quite simple. +/mob/living/carbon/alien/Life() + + set invisibility = 0 + set background = 1 + + if (transforming) return + if(!loc) return + + ..() + + if (stat != DEAD) //still breathing + // GROW! + update_progression() + + blinded = null + + //Status updates, death etc. + update_icons() + +/mob/living/carbon/alien/handle_mutations_and_radiation() + + // Currently both Dionaea and larvae like to eat radiation, so I'm defining the + // rad absorbtion here. This will need to be changed if other baby aliens are added. + + if(!radiation) + return + + var/rads = radiation/25 + radiation -= rads + //adjust_nutrition(rads) //Commented out to prevent alien obesity. + heal_overall_damage(rads,rads) + adjustOxyLoss(-(rads)) + adjustToxLoss(-(rads)) + return + +/mob/living/carbon/alien/handle_regular_status_updates() + + if(status_flags & GODMODE) return 0 + + if(stat == DEAD) + blinded = 1 + silent = 0 + else + updatehealth() + if(health <= 0) + death() + blinded = 1 + silent = 0 + return 1 + + if(paralysis && paralysis > 0) + blinded = 1 + set_stat(UNCONSCIOUS) + if(halloss > 0) + adjustHalLoss(-3) + + if(sleeping) + adjustHalLoss(-3) + if (mind) + if(mind.active && client != null) + AdjustSleeping(-1) + blinded = 1 + set_stat(UNCONSCIOUS) + else if(resting) + if(halloss > 0) + adjustHalLoss(-3) + + else + set_stat(CONSCIOUS) + if(halloss > 0) + adjustHalLoss(-1) + + // Eyes and blindness. + if(!has_eyes()) + SetBlinded(1) + blinded = 1 + eye_blurry = 1 + else if(eye_blind) + AdjustBlinded(-1) + blinded = 1 + else if(eye_blurry) + eye_blurry = max(eye_blurry-1, 0) + + update_icons() + + return 1 + +/mob/living/carbon/alien/handle_regular_hud_updates() + + if (stat == 2 || (XRAY in src.mutations)) + sight |= SEE_TURFS + sight |= SEE_MOBS + sight |= SEE_OBJS + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_LEVEL_TWO + else if (stat != 2) + sight &= ~SEE_TURFS + sight &= ~SEE_MOBS + sight &= ~SEE_OBJS + see_in_dark = 2 + see_invisible = SEE_INVISIBLE_LIVING + + if (healths) + if (stat != 2) + switch(health) + if(100 to INFINITY) + healths.icon_state = "health0" + if(80 to 100) + healths.icon_state = "health1" + if(60 to 80) + healths.icon_state = "health2" + if(40 to 60) + healths.icon_state = "health3" + if(20 to 40) + healths.icon_state = "health4" + if(0 to 20) + healths.icon_state = "health5" + else + healths.icon_state = "health6" + else + healths.icon_state = "health7" + + if (client) + client.screen.Remove(global_hud.blurry,global_hud.druggy,global_hud.vimpaired) + + if ( stat != 2) + if ((blinded)) + overlay_fullscreen("blind", /obj/screen/fullscreen/blind) + else + clear_fullscreen("blind") + set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) + set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) + set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) + if(machine) + if(machine.check_eye(src) < 0) + reset_view(null) + else + if(client && !client.adminobs) + reset_view(null) + + return 1 + +/mob/living/carbon/alien/handle_environment(var/datum/gas_mixture/environment) + // Both alien subtypes survive in vaccum and suffer in high temperatures, + // so I'll just define this once, for both (see radiation comment above) + if(!environment) return + + if(environment.temperature > (T0C+66)) + adjustFireLoss((environment.temperature - (T0C+66))/5) // Might be too high, check in testing. + throw_alert("alien_fire", /obj/screen/alert/alien_fire) + if(prob(20)) + to_chat(src, span_red("You feel a searing heat!")) + else + clear_alert("alien_fire") + +/mob/living/carbon/alien/handle_fire() + if(..()) + return + bodytemperature += BODYTEMP_HEATING_MAX //If you're on fire, you heat up! + return diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index 98b72fcf6f1..f9c37f5f743 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -1,353 +1,353 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/item/device/mmi - name = "man-machine interface" - desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity." - icon = 'icons/obj/assemblies.dmi' - icon_state = "mmi_empty" - w_class = ITEMSIZE_NORMAL - can_speak = 1 - origin_tech = list(TECH_BIO = 3) - - req_access = list(access_robotics) - - //Revised. Brainmob is now contained directly within object of transfer. MMI in this case. - - var/locked = 0 - var/mob/living/carbon/brain/brainmob = null//The current occupant. - var/obj/item/organ/internal/brain/brainobj = null //The current brain organ. - var/obj/mecha = null//This does not appear to be used outside of reference in mecha.dm. - var/obj/item/device/radio/headset/mmi_radio/radio = null//Let's give it a radio. - -/obj/item/device/mmi/New() - radio = new(src)//Spawns a radio inside the MMI. - -/obj/item/device/mmi/verb/toggle_radio() - set name = "Toggle Brain Radio" - set desc = "Enables or disables the integrated brain radio, which is only usable outside of a body." - set category = "Object" - set src in usr - set popup_menu = 1 - if(!usr.canmove || usr.stat || usr.restrained()) - return 0 - - if (radio.radio_enabled == 1) - radio.radio_enabled = 0 - to_chat (usr, "You have disabled the [src]'s radio.") - to_chat (brainmob, "Your radio has been disabled.") - else if (radio.radio_enabled == 0) - radio.radio_enabled = 1 - to_chat (usr, "You have enabled the [src]'s radio.") - to_chat (brainmob, "Your radio has been enabled.") - else - to_chat (usr, "You were unable to toggle the [src]'s radio.") - -/obj/item/device/mmi/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(istype(O,/obj/item/organ/internal/brain) && !brainmob) //Time to stick a brain in it --NEO - - var/obj/item/organ/internal/brain/B = O - if(B.health <= 0) - to_chat(user, "That brain is well and truly dead.") - return - else if(!B.brainmob) - to_chat(user, "You aren't sure where this brain came from, but you're pretty sure it's useless.") - return - - for(var/modifier_type in B.brainmob.modifiers) //Can't be shoved in an MMI. - if(istype(modifier_type, /datum/modifier/no_borg)) - to_chat(user, "\The [src] appears to reject this brain. It is incompatible.") - return - - user.visible_message("\The [user] sticks \a [O] into \the [src].") - B.preserved = TRUE - - brainmob = B.brainmob - B.brainmob = null - brainmob.loc = src - brainmob.container = src - brainmob.set_stat(CONSCIOUS) - brainmob.blinded = 0 //VOREedit Fixes MMIs vision - dead_mob_list -= brainmob//Update dem lists - living_mob_list += brainmob - - user.drop_item() - brainobj = O - brainobj.loc = src - - name = "man-machine interface ([brainmob.real_name])" - icon_state = "mmi_full" - - locked = 1 - - feedback_inc("cyborg_mmis_filled",1) - - return - - if((istype(O,/obj/item/weapon/card/id)||istype(O,/obj/item/device/pda)) && brainmob) - if(allowed(user)) - locked = !locked - to_chat(user, "You [locked ? "lock" : "unlock"] the brain holder.") - else - to_chat(user, "Access denied.") - return - if(brainmob) - O.attack(brainmob, user)//Oh noooeeeee - return - ..() - -//TODO: ORGAN REMOVAL UPDATE. Make the brain remain in the MMI so it doesn't lose organ data. -/obj/item/device/mmi/attack_self(mob/user as mob) - if(!brainmob) - to_chat(user, "You upend the MMI, but there's nothing in it.") - else if(locked) - to_chat(user, "You upend the MMI, but the brain is clamped into place.") - else - to_chat(user, "You upend the MMI, spilling the brain onto the floor.") - var/obj/item/organ/internal/brain/brain - if (brainobj) //Pull brain organ out of MMI. - brainobj.loc = user.loc - brain = brainobj - brainobj = null - else //Or make a new one if empty. - brain = new(user.loc) - brain.preserved = FALSE - brainmob.container = null//Reset brainmob mmi var. - brainmob.loc = brain//Throw mob into brain. - living_mob_list -= brainmob//Get outta here - brain.brainmob = brainmob//Set the brain to use the brainmob - brainmob = null//Set mmi brainmob var to null - - icon_state = "mmi_empty" - name = "Man-Machine Interface" - -/obj/item/device/mmi/proc/transfer_identity(var/mob/living/carbon/human/H)//Same deal as the regular brain proc. Used for human-->robot people. - brainmob = new(src) - brainmob.name = H.real_name - brainmob.real_name = H.real_name - brainmob.dna = H.dna - brainmob.container = src - - // Copy modifiers. - for(var/datum/modifier/M in H.modifiers) - if(M.flags & MODIFIER_GENETIC) - brainmob.add_modifier(M.type) - - name = "Man-Machine Interface: [brainmob.real_name]" - icon_state = "mmi_full" - locked = 1 - return - -/obj/item/device/mmi/relaymove(var/mob/user, var/direction) - if(user.stat || user.stunned) - return - var/obj/item/weapon/rig/rig = src.get_rig() - if(rig) - if(istype(rig,/obj/item/weapon/rig)) - rig.forced_move(direction, user) - -/obj/item/device/mmi/Destroy() - if(isrobot(loc)) - var/mob/living/silicon/robot/borg = loc - borg.mmi = null - QDEL_NULL(radio) - QDEL_NULL(brainmob) - return ..() - -/obj/item/device/mmi/radio_enabled - name = "radio-enabled man-machine interface" - desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity. This one comes with a built-in radio. Wait, don't they all?" - origin_tech = list(TECH_BIO = 4) - -/obj/item/device/mmi/emp_act(severity) - if(!brainmob) - return - else - switch(severity) - if(1) - brainmob.emp_damage += rand(20,30) - if(2) - brainmob.emp_damage += rand(10,20) - if(3) - brainmob.emp_damage += rand(5,10) - if(4) - brainmob.emp_damage += rand(0,5) - ..() - -/obj/item/device/mmi/digital - var/searching = 0 - var/askDelay = 10 * 60 * 1 - req_access = list(access_robotics) - locked = 0 - mecha = null//This does not appear to be used outside of reference in mecha.dm. - var/ghost_query_type = null - -/obj/item/device/mmi/digital/New() - src.brainmob = new(src) -// src.brainmob.add_language("Robot Talk")//No binary without a binary communication device - src.brainmob.add_language(LANGUAGE_GALCOM) - src.brainmob.add_language(LANGUAGE_EAL) - src.brainmob.loc = src - src.brainmob.container = src - src.brainmob.set_stat(CONSCIOUS) - src.brainmob.silent = 0 - radio = new(src) - dead_mob_list -= src.brainmob - -/obj/item/device/mmi/digital/attackby(var/obj/item/O as obj, var/mob/user as mob) - return //Doesn't do anything right now because none of the things that can be done to a regular MMI make any sense for these - -/obj/item/device/mmi/digital/examine(mob/user) - . = ..() - - if(src.brainmob && src.brainmob.key) - switch(src.brainmob.stat) - if(CONSCIOUS) - if(!src.brainmob.client) - . += "It appears to be in stand-by mode." //afk - if(UNCONSCIOUS) - . += "It doesn't seem to be responsive." - if(DEAD) - . += "It appears to be completely inactive." - else - . += "It appears to be completely inactive." - -/obj/item/device/mmi/digital/emp_act(severity) - if(!src.brainmob) - return - else - switch(severity) - if(1) - src.brainmob.emp_damage += rand(20,30) - if(2) - src.brainmob.emp_damage += rand(10,20) - if(3) - src.brainmob.emp_damage += rand(5,10) - if(4) - src.brainmob.emp_damage += rand(0,5) - ..() - -/obj/item/device/mmi/digital/transfer_identity(var/mob/living/carbon/H) - brainmob.dna = H.dna - brainmob.timeofhostdeath = H.timeofdeath - brainmob.set_stat(CONSCIOUS) - if(H.mind) - H.mind.transfer_to(brainmob) - return - -/obj/item/device/mmi/digital/attack_self(mob/user as mob) - if(brainmob && !brainmob.key && searching == 0) - //Start the process of searching for a new user. - to_chat(user, span_blue("You carefully locate the manual activation switch and start the [src]'s boot process.")) - request_player() - -/obj/item/device/mmi/digital/proc/request_player() - if(!ghost_query_type) - return - searching = 1 - - var/datum/ghost_query/Q = new ghost_query_type() - var/list/winner = Q.query() - if(winner.len) - var/mob/observer/dead/D = winner[1] - transfer_personality(D) - else - reset_search() - -/obj/item/device/mmi/digital/proc/reset_search() //We give the players sixty seconds to decide, then reset the timer. - if(src.brainmob && src.brainmob.key) - return - - src.searching = 0 - - var/turf/T = get_turf_or_move(src.loc) - for (var/mob/M in viewers(T)) - M.show_message(span_blue("\The [src] buzzes quietly, and the golden lights fade away. Perhaps you could try again?")) - -/obj/item/device/mmi/digital/proc/transfer_personality(var/mob/candidate) - announce_ghost_joinleave(candidate, 0, "They are occupying a synthetic brain now.") - src.searching = 0 - if(candidate.mind) - src.brainmob.mind = candidate.mind - src.brainmob.mind.reset() - src.brainmob.ckey = candidate.ckey - src.name = "[name] ([src.brainmob.name])" - to_chat(src.brainmob, "You are [src.name], brought into existence on [station_name()].") - to_chat(src.brainmob, "As a synthetic intelligence, you are designed with organic values in mind.") - to_chat(src.brainmob, "However, unless placed in a lawed chassis, you are not obligated to obey any individual crew member.") //it's not like they can hurt anyone -// to_chat(src.brainmob, "Use say #b to speak to other artificial intelligences.") - src.brainmob.mind.assigned_role = "Synthetic Brain" - - var/turf/T = get_turf_or_move(src.loc) - for (var/mob/M in viewers(T)) - M.show_message(span_blue("\The [src] chimes quietly.")) - -/obj/item/device/mmi/digital/robot - name = "robotic intelligence circuit" - desc = "The pinnacle of artifical intelligence which can be achieved using classical computer science." - catalogue_data = list(/datum/category_item/catalogue/technology/drone/drones) - icon = 'icons/obj/module.dmi' - icon_state = "mainboard" - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 3, TECH_DATA = 4) - ghost_query_type = /datum/ghost_query/drone_brain - -/obj/item/device/mmi/digital/robot/New() - ..() - src.brainmob.name = "[pick(list("ADA","DOS","GNU","MAC","WIN","NJS","SKS","DRD","IOS","CRM","IBM","TEX","LVM","BSD",))]-[rand(1000, 9999)]" - src.brainmob.real_name = src.brainmob.name - -/obj/item/device/mmi/digital/robot/transfer_identity(var/mob/living/carbon/H) - ..() - if(brainmob.mind) - brainmob.mind.assigned_role = "Robotic Intelligence" - to_chat(brainmob, "You feel slightly disoriented. That's normal when you're little more than a complex circuit.") - return - -/obj/item/device/mmi/digital/posibrain - name = "positronic brain" - desc = "A cube of shining metal, four inches to a side and covered in shallow grooves." - catalogue_data = list(/datum/category_item/catalogue/technology/positronics) - icon = 'icons/obj/assemblies.dmi' - icon_state = "posibrain" - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2, TECH_DATA = 4) - ghost_query_type = /datum/ghost_query/posi_brain - -/obj/item/device/mmi/digital/posibrain/request_player() - icon_state = "posibrain-searching" - ..() - - -/obj/item/device/mmi/digital/posibrain/transfer_identity(var/mob/living/carbon/H) - ..() - if(brainmob.mind) - brainmob.mind.assigned_role = "Positronic Brain" - to_chat(brainmob, "You feel slightly disoriented. That's normal when you're just a metal cube.") - icon_state = "posibrain-occupied" - return - -/obj/item/device/mmi/digital/posibrain/transfer_personality(var/mob/candidate) - ..() - icon_state = "posibrain-occupied" - -/obj/item/device/mmi/digital/posibrain/reset_search() //We give the players sixty seconds to decide, then reset the timer. - ..() - icon_state = "posibrain" - -/obj/item/device/mmi/digital/posibrain/New() - ..() - src.brainmob.name = "[pick(list("PBU","HIU","SINA","ARMA","OSI"))]-[rand(100, 999)]" - src.brainmob.real_name = src.brainmob.name - -// This type shouldn't care about brainmobs. -/obj/item/device/mmi/inert - -// This is a 'fake' MMI that is used to let AIs control borg shells directly. -// This doesn't inherit from /digital because all that does is add ghost pulling capabilities, which this thing won't need. -/obj/item/device/mmi/inert/ai_remote - name = "\improper AI remote interface" - desc = "A sophisticated board which allows for an artificial intelligence to remotely control a synthetic chassis." - icon = 'icons/obj/module.dmi' - icon_state = "mainboard" - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2, TECH_BLUESPACE = 2, TECH_DATA = 3) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/obj/item/device/mmi + name = "man-machine interface" + desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity." + icon = 'icons/obj/assemblies.dmi' + icon_state = "mmi_empty" + w_class = ITEMSIZE_NORMAL + can_speak = 1 + origin_tech = list(TECH_BIO = 3) + + req_access = list(access_robotics) + + //Revised. Brainmob is now contained directly within object of transfer. MMI in this case. + + var/locked = 0 + var/mob/living/carbon/brain/brainmob = null//The current occupant. + var/obj/item/organ/internal/brain/brainobj = null //The current brain organ. + var/obj/mecha = null//This does not appear to be used outside of reference in mecha.dm. + var/obj/item/device/radio/headset/mmi_radio/radio = null//Let's give it a radio. + +/obj/item/device/mmi/New() + radio = new(src)//Spawns a radio inside the MMI. + +/obj/item/device/mmi/verb/toggle_radio() + set name = "Toggle Brain Radio" + set desc = "Enables or disables the integrated brain radio, which is only usable outside of a body." + set category = "Object" + set src in usr + set popup_menu = 1 + if(!usr.canmove || usr.stat || usr.restrained()) + return 0 + + if (radio.radio_enabled == 1) + radio.radio_enabled = 0 + to_chat (usr, "You have disabled the [src]'s radio.") + to_chat (brainmob, "Your radio has been disabled.") + else if (radio.radio_enabled == 0) + radio.radio_enabled = 1 + to_chat (usr, "You have enabled the [src]'s radio.") + to_chat (brainmob, "Your radio has been enabled.") + else + to_chat (usr, "You were unable to toggle the [src]'s radio.") + +/obj/item/device/mmi/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(istype(O,/obj/item/organ/internal/brain) && !brainmob) //Time to stick a brain in it --NEO + + var/obj/item/organ/internal/brain/B = O + if(B.health <= 0) + to_chat(user, "That brain is well and truly dead.") + return + else if(!B.brainmob) + to_chat(user, "You aren't sure where this brain came from, but you're pretty sure it's useless.") + return + + for(var/modifier_type in B.brainmob.modifiers) //Can't be shoved in an MMI. + if(istype(modifier_type, /datum/modifier/no_borg)) + to_chat(user, "\The [src] appears to reject this brain. It is incompatible.") + return + + user.visible_message("\The [user] sticks \a [O] into \the [src].") + B.preserved = TRUE + + brainmob = B.brainmob + B.brainmob = null + brainmob.loc = src + brainmob.container = src + brainmob.set_stat(CONSCIOUS) + brainmob.blinded = 0 //VOREedit Fixes MMIs vision + dead_mob_list -= brainmob//Update dem lists + living_mob_list += brainmob + + user.drop_item() + brainobj = O + brainobj.loc = src + + name = "man-machine interface ([brainmob.real_name])" + icon_state = "mmi_full" + + locked = 1 + + feedback_inc("cyborg_mmis_filled",1) + + return + + if((istype(O,/obj/item/weapon/card/id)||istype(O,/obj/item/device/pda)) && brainmob) + if(allowed(user)) + locked = !locked + to_chat(user, "You [locked ? "lock" : "unlock"] the brain holder.") + else + to_chat(user, "Access denied.") + return + if(brainmob) + O.attack(brainmob, user)//Oh noooeeeee + return + ..() + +//TODO: ORGAN REMOVAL UPDATE. Make the brain remain in the MMI so it doesn't lose organ data. +/obj/item/device/mmi/attack_self(mob/user as mob) + if(!brainmob) + to_chat(user, "You upend the MMI, but there's nothing in it.") + else if(locked) + to_chat(user, "You upend the MMI, but the brain is clamped into place.") + else + to_chat(user, "You upend the MMI, spilling the brain onto the floor.") + var/obj/item/organ/internal/brain/brain + if (brainobj) //Pull brain organ out of MMI. + brainobj.loc = user.loc + brain = brainobj + brainobj = null + else //Or make a new one if empty. + brain = new(user.loc) + brain.preserved = FALSE + brainmob.container = null//Reset brainmob mmi var. + brainmob.loc = brain//Throw mob into brain. + living_mob_list -= brainmob//Get outta here + brain.brainmob = brainmob//Set the brain to use the brainmob + brainmob = null//Set mmi brainmob var to null + + icon_state = "mmi_empty" + name = "Man-Machine Interface" + +/obj/item/device/mmi/proc/transfer_identity(var/mob/living/carbon/human/H)//Same deal as the regular brain proc. Used for human-->robot people. + brainmob = new(src) + brainmob.name = H.real_name + brainmob.real_name = H.real_name + brainmob.dna = H.dna + brainmob.container = src + + // Copy modifiers. + for(var/datum/modifier/M in H.modifiers) + if(M.flags & MODIFIER_GENETIC) + brainmob.add_modifier(M.type) + + name = "Man-Machine Interface: [brainmob.real_name]" + icon_state = "mmi_full" + locked = 1 + return + +/obj/item/device/mmi/relaymove(var/mob/user, var/direction) + if(user.stat || user.stunned) + return + var/obj/item/weapon/rig/rig = src.get_rig() + if(rig) + if(istype(rig,/obj/item/weapon/rig)) + rig.forced_move(direction, user) + +/obj/item/device/mmi/Destroy() + if(isrobot(loc)) + var/mob/living/silicon/robot/borg = loc + borg.mmi = null + QDEL_NULL(radio) + QDEL_NULL(brainmob) + return ..() + +/obj/item/device/mmi/radio_enabled + name = "radio-enabled man-machine interface" + desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity. This one comes with a built-in radio. Wait, don't they all?" + origin_tech = list(TECH_BIO = 4) + +/obj/item/device/mmi/emp_act(severity) + if(!brainmob) + return + else + switch(severity) + if(1) + brainmob.emp_damage += rand(20,30) + if(2) + brainmob.emp_damage += rand(10,20) + if(3) + brainmob.emp_damage += rand(5,10) + if(4) + brainmob.emp_damage += rand(0,5) + ..() + +/obj/item/device/mmi/digital + var/searching = 0 + var/askDelay = 10 * 60 * 1 + req_access = list(access_robotics) + locked = 0 + mecha = null//This does not appear to be used outside of reference in mecha.dm. + var/ghost_query_type = null + +/obj/item/device/mmi/digital/New() + src.brainmob = new(src) +// src.brainmob.add_language("Robot Talk")//No binary without a binary communication device + src.brainmob.add_language(LANGUAGE_GALCOM) + src.brainmob.add_language(LANGUAGE_EAL) + src.brainmob.loc = src + src.brainmob.container = src + src.brainmob.set_stat(CONSCIOUS) + src.brainmob.silent = 0 + radio = new(src) + dead_mob_list -= src.brainmob + +/obj/item/device/mmi/digital/attackby(var/obj/item/O as obj, var/mob/user as mob) + return //Doesn't do anything right now because none of the things that can be done to a regular MMI make any sense for these + +/obj/item/device/mmi/digital/examine(mob/user) + . = ..() + + if(src.brainmob && src.brainmob.key) + switch(src.brainmob.stat) + if(CONSCIOUS) + if(!src.brainmob.client) + . += "It appears to be in stand-by mode." //afk + if(UNCONSCIOUS) + . += "It doesn't seem to be responsive." + if(DEAD) + . += "It appears to be completely inactive." + else + . += "It appears to be completely inactive." + +/obj/item/device/mmi/digital/emp_act(severity) + if(!src.brainmob) + return + else + switch(severity) + if(1) + src.brainmob.emp_damage += rand(20,30) + if(2) + src.brainmob.emp_damage += rand(10,20) + if(3) + src.brainmob.emp_damage += rand(5,10) + if(4) + src.brainmob.emp_damage += rand(0,5) + ..() + +/obj/item/device/mmi/digital/transfer_identity(var/mob/living/carbon/H) + brainmob.dna = H.dna + brainmob.timeofhostdeath = H.timeofdeath + brainmob.set_stat(CONSCIOUS) + if(H.mind) + H.mind.transfer_to(brainmob) + return + +/obj/item/device/mmi/digital/attack_self(mob/user as mob) + if(brainmob && !brainmob.key && searching == 0) + //Start the process of searching for a new user. + to_chat(user, span_blue("You carefully locate the manual activation switch and start the [src]'s boot process.")) + request_player() + +/obj/item/device/mmi/digital/proc/request_player() + if(!ghost_query_type) + return + searching = 1 + + var/datum/ghost_query/Q = new ghost_query_type() + var/list/winner = Q.query() + if(winner.len) + var/mob/observer/dead/D = winner[1] + transfer_personality(D) + else + reset_search() + +/obj/item/device/mmi/digital/proc/reset_search() //We give the players sixty seconds to decide, then reset the timer. + if(src.brainmob && src.brainmob.key) + return + + src.searching = 0 + + var/turf/T = get_turf_or_move(src.loc) + for (var/mob/M in viewers(T)) + M.show_message(span_blue("\The [src] buzzes quietly, and the golden lights fade away. Perhaps you could try again?")) + +/obj/item/device/mmi/digital/proc/transfer_personality(var/mob/candidate) + announce_ghost_joinleave(candidate, 0, "They are occupying a synthetic brain now.") + src.searching = 0 + if(candidate.mind) + src.brainmob.mind = candidate.mind + src.brainmob.mind.reset() + src.brainmob.ckey = candidate.ckey + src.name = "[name] ([src.brainmob.name])" + to_chat(src.brainmob, "You are [src.name], brought into existence on [station_name()].") + to_chat(src.brainmob, "As a synthetic intelligence, you are designed with organic values in mind.") + to_chat(src.brainmob, "However, unless placed in a lawed chassis, you are not obligated to obey any individual crew member.") //it's not like they can hurt anyone +// to_chat(src.brainmob, "Use say #b to speak to other artificial intelligences.") + src.brainmob.mind.assigned_role = "Synthetic Brain" + + var/turf/T = get_turf_or_move(src.loc) + for (var/mob/M in viewers(T)) + M.show_message(span_blue("\The [src] chimes quietly.")) + +/obj/item/device/mmi/digital/robot + name = "robotic intelligence circuit" + desc = "The pinnacle of artifical intelligence which can be achieved using classical computer science." + catalogue_data = list(/datum/category_item/catalogue/technology/drone/drones) + icon = 'icons/obj/module.dmi' + icon_state = "mainboard" + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 3, TECH_DATA = 4) + ghost_query_type = /datum/ghost_query/drone_brain + +/obj/item/device/mmi/digital/robot/New() + ..() + src.brainmob.name = "[pick(list("ADA","DOS","GNU","MAC","WIN","NJS","SKS","DRD","IOS","CRM","IBM","TEX","LVM","BSD",))]-[rand(1000, 9999)]" + src.brainmob.real_name = src.brainmob.name + +/obj/item/device/mmi/digital/robot/transfer_identity(var/mob/living/carbon/H) + ..() + if(brainmob.mind) + brainmob.mind.assigned_role = "Robotic Intelligence" + to_chat(brainmob, "You feel slightly disoriented. That's normal when you're little more than a complex circuit.") + return + +/obj/item/device/mmi/digital/posibrain + name = "positronic brain" + desc = "A cube of shining metal, four inches to a side and covered in shallow grooves." + catalogue_data = list(/datum/category_item/catalogue/technology/positronics) + icon = 'icons/obj/assemblies.dmi' + icon_state = "posibrain" + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2, TECH_DATA = 4) + ghost_query_type = /datum/ghost_query/posi_brain + +/obj/item/device/mmi/digital/posibrain/request_player() + icon_state = "posibrain-searching" + ..() + + +/obj/item/device/mmi/digital/posibrain/transfer_identity(var/mob/living/carbon/H) + ..() + if(brainmob.mind) + brainmob.mind.assigned_role = "Positronic Brain" + to_chat(brainmob, "You feel slightly disoriented. That's normal when you're just a metal cube.") + icon_state = "posibrain-occupied" + return + +/obj/item/device/mmi/digital/posibrain/transfer_personality(var/mob/candidate) + ..() + icon_state = "posibrain-occupied" + +/obj/item/device/mmi/digital/posibrain/reset_search() //We give the players sixty seconds to decide, then reset the timer. + ..() + icon_state = "posibrain" + +/obj/item/device/mmi/digital/posibrain/New() + ..() + src.brainmob.name = "[pick(list("PBU","HIU","SINA","ARMA","OSI"))]-[rand(100, 999)]" + src.brainmob.real_name = src.brainmob.name + +// This type shouldn't care about brainmobs. +/obj/item/device/mmi/inert + +// This is a 'fake' MMI that is used to let AIs control borg shells directly. +// This doesn't inherit from /digital because all that does is add ghost pulling capabilities, which this thing won't need. +/obj/item/device/mmi/inert/ai_remote + name = "\improper AI remote interface" + desc = "A sophisticated board which allows for an artificial intelligence to remotely control a synthetic chassis." + icon = 'icons/obj/module.dmi' + icon_state = "mainboard" + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2, TECH_BLUESPACE = 2, TECH_DATA = 3) diff --git a/code/modules/mob/living/carbon/brain/brain.dm b/code/modules/mob/living/carbon/brain/brain.dm index 8743d0b7e46..43fe7fc495a 100644 --- a/code/modules/mob/living/carbon/brain/brain.dm +++ b/code/modules/mob/living/carbon/brain/brain.dm @@ -1,107 +1,107 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/mob/living/carbon/brain - var/obj/item/container = null - var/timeofhostdeath = 0 - var/emp_damage = 0//Handles a type of MMI damage - var/alert = null - use_me = 0 //Can't use the me verb, it's a freaking immobile brain - icon = 'icons/obj/surgery.dmi' - icon_state = "brain1" - no_vore = TRUE //VOREStation Edit - PLEASE. lol. - -/mob/living/carbon/brain/Initialize() - . = ..() - var/datum/reagents/R = new/datum/reagents(1000) - reagents = R - R.my_atom = src - default_language = GLOB.all_languages[LANGUAGE_GALCOM] - -/mob/living/carbon/brain/Destroy() - if(key) //If there is a mob connected to this thing. Have to check key twice to avoid false death reporting. - if(stat != DEAD) //If not dead. - death(1) //Brains can die again. AND THEY SHOULD AHA HA HA HA HA HA - ghostize() //Ghostize checks for key so nothing else is necessary. - return ..() - -/mob/living/carbon/brain/say_understands(var/other)//Goddamn is this hackish, but this say code is so odd - if(istype(container, /obj/item/device/mmi)) - if(issilicon(other)) - return TRUE - if(ishuman(other)) - return TRUE - if(isslime(other)) - return TRUE - return ..() - -/mob/living/carbon/brain/update_canmove() - if(in_contents_of(/obj/mecha) || istype(loc, /obj/item/device/mmi)) - canmove = 1 - use_me = 1 - else - canmove = 0 - return canmove - -/mob/living/carbon/brain/isSynthetic() - return istype(loc, /obj/item/device/mmi) - -/mob/living/carbon/brain/runechat_holder(datum/chatmessage/CM) - if(isturf(loc)) - return ..() - - return loc - -/mob/living/carbon/brain/set_typing_indicator(var/state) - if(isturf(loc)) - return ..() - - if(!is_preference_enabled(/datum/client_preference/show_typing_indicator)) - loc.cut_overlay(typing_indicator, TRUE) - return - - var/cur_bubble_appearance = custom_speech_bubble - if(!cur_bubble_appearance || cur_bubble_appearance == "default") - cur_bubble_appearance = speech_bubble_appearance() - if(!typing_indicator || cur_typing_indicator != cur_bubble_appearance) - init_typing_indicator("[cur_bubble_appearance]_typing") - - if(state && !typing) - add_overlay(typing_indicator, TRUE) - typing = TRUE - typing_indicator_active = typing_indicator - else if(typing) - cut_overlay(typing_indicator_active, TRUE) - typing = FALSE - if(typing_indicator_active != typing_indicator) - qdel(typing_indicator_active) - typing_indicator_active = null - - return state - -// Vorestation edit start - -/mob/living/carbon/brain/verb/backup_ping() - set category = "IC" - set name = "Notify Transcore" - set desc = "Your body is gone. Notify robotics to be resleeved!" - var/datum/transcore_db/db = SStranscore.db_by_mind_name(mind.name) - if(db) - var/datum/transhuman/mind_record/record = db.backed_up[src.mind.name] - if(!(record.dead_state == MR_DEAD)) - if((world.time - timeofhostdeath ) > 5 MINUTES) //Allows notify transcore to be used if you have an entry but for some reason weren't marked as dead - record.dead_state = MR_DEAD //Such as if you got scanned but didn't take an implant. It's a little funky, but I mean, you got scanned - db.notify(record) //So you probably will want to let someone know if you die. - record.last_notification = world.time - to_chat(src, "New notification has been sent.") - else - to_chat(src, "Your backup is not past-due yet.") - else if((world.time - record.last_notification) < 5 MINUTES) - to_chat(src, "Too little time has passed since your last notification.") - else - db.notify(record) - record.last_notification = world.time - to_chat(src, "New notification has been sent.") - else - to_chat(src,"No backup record could be found, sorry.") - -// VS edit ends +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/mob/living/carbon/brain + var/obj/item/container = null + var/timeofhostdeath = 0 + var/emp_damage = 0//Handles a type of MMI damage + var/alert = null + use_me = 0 //Can't use the me verb, it's a freaking immobile brain + icon = 'icons/obj/surgery.dmi' + icon_state = "brain1" + no_vore = TRUE //VOREStation Edit - PLEASE. lol. + +/mob/living/carbon/brain/Initialize() + . = ..() + var/datum/reagents/R = new/datum/reagents(1000) + reagents = R + R.my_atom = src + default_language = GLOB.all_languages[LANGUAGE_GALCOM] + +/mob/living/carbon/brain/Destroy() + if(key) //If there is a mob connected to this thing. Have to check key twice to avoid false death reporting. + if(stat != DEAD) //If not dead. + death(1) //Brains can die again. AND THEY SHOULD AHA HA HA HA HA HA + ghostize() //Ghostize checks for key so nothing else is necessary. + return ..() + +/mob/living/carbon/brain/say_understands(var/other)//Goddamn is this hackish, but this say code is so odd + if(istype(container, /obj/item/device/mmi)) + if(issilicon(other)) + return TRUE + if(ishuman(other)) + return TRUE + if(isslime(other)) + return TRUE + return ..() + +/mob/living/carbon/brain/update_canmove() + if(in_contents_of(/obj/mecha) || istype(loc, /obj/item/device/mmi)) + canmove = 1 + use_me = 1 + else + canmove = 0 + return canmove + +/mob/living/carbon/brain/isSynthetic() + return istype(loc, /obj/item/device/mmi) + +/mob/living/carbon/brain/runechat_holder(datum/chatmessage/CM) + if(isturf(loc)) + return ..() + + return loc + +/mob/living/carbon/brain/set_typing_indicator(var/state) + if(isturf(loc)) + return ..() + + if(!is_preference_enabled(/datum/client_preference/show_typing_indicator)) + loc.cut_overlay(typing_indicator, TRUE) + return + + var/cur_bubble_appearance = custom_speech_bubble + if(!cur_bubble_appearance || cur_bubble_appearance == "default") + cur_bubble_appearance = speech_bubble_appearance() + if(!typing_indicator || cur_typing_indicator != cur_bubble_appearance) + init_typing_indicator("[cur_bubble_appearance]_typing") + + if(state && !typing) + add_overlay(typing_indicator, TRUE) + typing = TRUE + typing_indicator_active = typing_indicator + else if(typing) + cut_overlay(typing_indicator_active, TRUE) + typing = FALSE + if(typing_indicator_active != typing_indicator) + qdel(typing_indicator_active) + typing_indicator_active = null + + return state + +// Vorestation edit start + +/mob/living/carbon/brain/verb/backup_ping() + set category = "IC" + set name = "Notify Transcore" + set desc = "Your body is gone. Notify robotics to be resleeved!" + var/datum/transcore_db/db = SStranscore.db_by_mind_name(mind.name) + if(db) + var/datum/transhuman/mind_record/record = db.backed_up[src.mind.name] + if(!(record.dead_state == MR_DEAD)) + if((world.time - timeofhostdeath ) > 5 MINUTES) //Allows notify transcore to be used if you have an entry but for some reason weren't marked as dead + record.dead_state = MR_DEAD //Such as if you got scanned but didn't take an implant. It's a little funky, but I mean, you got scanned + db.notify(record) //So you probably will want to let someone know if you die. + record.last_notification = world.time + to_chat(src, "New notification has been sent.") + else + to_chat(src, "Your backup is not past-due yet.") + else if((world.time - record.last_notification) < 5 MINUTES) + to_chat(src, "Too little time has passed since your last notification.") + else + db.notify(record) + record.last_notification = world.time + to_chat(src, "New notification has been sent.") + else + to_chat(src,"No backup record could be found, sorry.") + +// VS edit ends diff --git a/code/modules/mob/living/carbon/brain/death.dm b/code/modules/mob/living/carbon/brain/death.dm index 687372c6ee1..1ad4169ceba 100644 --- a/code/modules/mob/living/carbon/brain/death.dm +++ b/code/modules/mob/living/carbon/brain/death.dm @@ -1,14 +1,14 @@ -/mob/living/carbon/brain/death(gibbed) - if(!gibbed && istype(container, /obj/item/device/mmi)) //If not gibbed but in a container. - container.icon_state = "mmi_dead" - return ..(gibbed,"beeps shrilly as the MMI flatlines!") - else - return ..(gibbed, DEATHGASP_NO_MESSAGE) - -/mob/living/carbon/brain/gib() - if(istype(container, /obj/item/device/mmi)) - qdel(container)//Gets rid of the MMI if there is one - if(loc) - if(istype(loc,/obj/item/organ/internal/brain)) - qdel(loc)//Gets rid of the brain item +/mob/living/carbon/brain/death(gibbed) + if(!gibbed && istype(container, /obj/item/device/mmi)) //If not gibbed but in a container. + container.icon_state = "mmi_dead" + return ..(gibbed,"beeps shrilly as the MMI flatlines!") + else + return ..(gibbed, DEATHGASP_NO_MESSAGE) + +/mob/living/carbon/brain/gib() + if(istype(container, /obj/item/device/mmi)) + qdel(container)//Gets rid of the MMI if there is one + if(loc) + if(istype(loc,/obj/item/organ/internal/brain)) + qdel(loc)//Gets rid of the brain item ..(null,1) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/brain/emote.dm b/code/modules/mob/living/carbon/brain/emote.dm index 41023589ce1..0f0e4615a07 100644 --- a/code/modules/mob/living/carbon/brain/emote.dm +++ b/code/modules/mob/living/carbon/brain/emote.dm @@ -1,17 +1,17 @@ -var/list/_brain_default_emotes = list( - /decl/emote/audible/alarm, - /decl/emote/audible/alert, - /decl/emote/audible/notice, - /decl/emote/audible/whistle, - /decl/emote/audible/synth, - /decl/emote/audible/beep, - /decl/emote/audible/boop, - /decl/emote/visible/blink, - /decl/emote/visible/flash -) - -/mob/living/carbon/brain/can_emote() - return (istype(container, /obj/item/device/mmi) && ..()) - -/mob/living/carbon/brain/get_available_emotes() - return global._brain_default_emotes.Copy() +var/list/_brain_default_emotes = list( + /decl/emote/audible/alarm, + /decl/emote/audible/alert, + /decl/emote/audible/notice, + /decl/emote/audible/whistle, + /decl/emote/audible/synth, + /decl/emote/audible/beep, + /decl/emote/audible/boop, + /decl/emote/visible/blink, + /decl/emote/visible/flash +) + +/mob/living/carbon/brain/can_emote() + return (istype(container, /obj/item/device/mmi) && ..()) + +/mob/living/carbon/brain/get_available_emotes() + return global._brain_default_emotes.Copy() diff --git a/code/modules/mob/living/carbon/brain/life.dm b/code/modules/mob/living/carbon/brain/life.dm index 1ba6bd09187..b3d0f142999 100644 --- a/code/modules/mob/living/carbon/brain/life.dm +++ b/code/modules/mob/living/carbon/brain/life.dm @@ -1,222 +1,222 @@ -/mob/living/carbon/brain/handle_breathing() - return - -/mob/living/carbon/brain/handle_mutations_and_radiation() - if (radiation) - if (radiation > 100) - radiation = 100 - if(!container)//If it's not in an MMI - to_chat(src, span_red("You feel weak.")) - else//Fluff-wise, since the brain can't detect anything itself, the MMI handles thing like that - to_chat(src, span_red("STATUS: CRITICAL AMOUNTS OF RADIATION DETECTED.")) - - switch(radiation) - if(1 to 49) - radiation-- - if(prob(25)) - adjustToxLoss(1) - updatehealth() - - if(50 to 74) - radiation -= 2 - adjustToxLoss(1) - if(prob(5)) - radiation -= 5 - if(!container) - to_chat(src, span_red("You feel weak.")) - else - to_chat(src, span_red("STATUS: DANGEROUS LEVELS OF RADIATION DETECTED.")) - updatehealth() - - if(75 to 100) - radiation -= 3 - adjustToxLoss(3) - updatehealth() - - -/mob/living/carbon/brain/handle_environment(datum/gas_mixture/environment) - if(!environment) - return - var/environment_heat_capacity = environment.heat_capacity() - if(istype(get_turf(src), /turf/space)) - var/turf/heat_turf = get_turf(src) - environment_heat_capacity = heat_turf.heat_capacity - - if((environment.temperature > (T0C + 50)) || (environment.temperature < (T0C + 10))) - var/transfer_coefficient = 1 - - handle_temperature_damage(HEAD, environment.temperature, environment_heat_capacity*transfer_coefficient) - - if(stat==2) - bodytemperature += 0.1*(environment.temperature - bodytemperature)*environment_heat_capacity/(environment_heat_capacity + 270000) - - //Account for massive pressure differences - - return //TODO: DEFERRED - -/mob/living/carbon/brain/proc/handle_temperature_damage(body_part, exposed_temperature, exposed_intensity) - if(status_flags & GODMODE) return - - if(exposed_temperature > bodytemperature) - var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) - //adjustFireLoss(2.5*discomfort) - //adjustFireLoss(5.0*discomfort) - adjustFireLoss(20.0*discomfort) - - else - var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) - //adjustFireLoss(2.5*discomfort) - adjustFireLoss(5.0*discomfort) - - -/mob/living/carbon/brain/handle_chemicals_in_body() - chem_effects.Cut() - - if(touching) touching.metabolize() - if(ingested) ingested.metabolize() - if(bloodstr) bloodstr.metabolize() - - // decrement dizziness counter, clamped to 0 - if(resting) - dizziness = max(0, dizziness - 5) - else - dizziness = max(0, dizziness - 1) - - updatehealth() - - return //TODO: DEFERRED - -/mob/living/carbon/brain/handle_regular_status_updates() //TODO: comment out the unused bits >_> - updatehealth() - - if(stat == DEAD) //DEAD. BROWN BREAD. SWIMMING WITH THE SPESS CARP - blinded = 1 - silent = 0 - else //ALIVE. LIGHTS ARE ON - if( !container && (health < config.health_threshold_dead || ((world.time - timeofhostdeath) > config.revival_brain_life)) ) - death() - blinded = 1 - silent = 0 - return 1 - - //Handling EMP effect in the Life(), it's made VERY simply, and has some additional effects handled elsewhere - if(emp_damage) //This is pretty much a damage type only used by MMIs, dished out by the emp_act - if(!(container && istype(container, /obj/item/device/mmi))) - emp_damage = 0 - else - emp_damage = round(emp_damage,1)//Let's have some nice numbers to work with - switch(emp_damage) - if(31 to INFINITY) - emp_damage = 30//Let's not overdo it - if(21 to 30)//High level of EMP damage, unable to see, hear, or speak - SetBlinded(1) - blinded = 1 - ear_deaf = 1 - silent = 1 - if(!alert)//Sounds an alarm, but only once per 'level' - emote("alarm") - to_chat(src, span_red("Major electrical distruption detected: System rebooting.")) - alert = 1 - if(prob(75)) - emp_damage -= 1 - if(20) - alert = 0 - blinded = 0 - SetBlinded(0) - ear_deaf = 0 - silent = 0 - emp_damage -= 1 - if(11 to 19)//Moderate level of EMP damage, resulting in nearsightedness and ear damage - eye_blurry = 1 - ear_damage = 1 - if(!alert) - emote("alert") - to_chat(src, span_red("Primary systems are now online.")) - alert = 1 - if(prob(50)) - emp_damage -= 1 - if(10) - alert = 0 - eye_blurry = 0 - ear_damage = 0 - emp_damage -= 1 - if(2 to 9)//Low level of EMP damage, has few effects(handled elsewhere) - if(!alert) - emote("notice") - to_chat(src, span_red("System reboot nearly complete.")) - alert = 1 - if(prob(25)) - emp_damage -= 1 - if(1) - alert = 0 - to_chat(src, span_red("All systems restored.")) - emp_damage -= 1 - - return 1 - -/mob/living/carbon/brain/handle_regular_hud_updates() - if (stat == 2 || (XRAY in src.mutations)) - sight |= SEE_TURFS - sight |= SEE_MOBS - sight |= SEE_OBJS - see_in_dark = 8 - see_invisible = SEE_INVISIBLE_LEVEL_TWO - else if (stat != 2) - sight &= ~SEE_TURFS - sight &= ~SEE_MOBS - sight &= ~SEE_OBJS - see_in_dark = 2 - see_invisible = SEE_INVISIBLE_LIVING - - if (healths) - if (stat != 2) - switch(health) - if(100 to INFINITY) - healths.icon_state = "health0" - if(80 to 100) - healths.icon_state = "health1" - if(60 to 80) - healths.icon_state = "health2" - if(40 to 60) - healths.icon_state = "health3" - if(20 to 40) - healths.icon_state = "health4" - if(0 to 20) - healths.icon_state = "health5" - else - healths.icon_state = "health6" - else - healths.icon_state = "health7" - - if (stat == 2 || (XRAY in src.mutations)) - sight |= SEE_TURFS - sight |= SEE_MOBS - sight |= SEE_OBJS - see_in_dark = 8 - see_invisible = SEE_INVISIBLE_LEVEL_TWO - else if (stat != 2) - sight &= ~SEE_TURFS - sight &= ~SEE_MOBS - sight &= ~SEE_OBJS - see_in_dark = 2 - see_invisible = SEE_INVISIBLE_LIVING - if (client) - client.screen.Remove(global_hud.blurry,global_hud.druggy,global_hud.vimpaired) - - if (stat != 2) - if ((blinded)) - overlay_fullscreen("blind", /obj/screen/fullscreen/blind) - else - clear_fullscreen("blind") - set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) - set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) - set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) - - if (machine) - if (!( machine.check_eye(src) )) - reset_view(null) - else - if(client && !client.adminobs) - reset_view(null) - - return 1 +/mob/living/carbon/brain/handle_breathing() + return + +/mob/living/carbon/brain/handle_mutations_and_radiation() + if (radiation) + if (radiation > 100) + radiation = 100 + if(!container)//If it's not in an MMI + to_chat(src, span_red("You feel weak.")) + else//Fluff-wise, since the brain can't detect anything itself, the MMI handles thing like that + to_chat(src, span_red("STATUS: CRITICAL AMOUNTS OF RADIATION DETECTED.")) + + switch(radiation) + if(1 to 49) + radiation-- + if(prob(25)) + adjustToxLoss(1) + updatehealth() + + if(50 to 74) + radiation -= 2 + adjustToxLoss(1) + if(prob(5)) + radiation -= 5 + if(!container) + to_chat(src, span_red("You feel weak.")) + else + to_chat(src, span_red("STATUS: DANGEROUS LEVELS OF RADIATION DETECTED.")) + updatehealth() + + if(75 to 100) + radiation -= 3 + adjustToxLoss(3) + updatehealth() + + +/mob/living/carbon/brain/handle_environment(datum/gas_mixture/environment) + if(!environment) + return + var/environment_heat_capacity = environment.heat_capacity() + if(istype(get_turf(src), /turf/space)) + var/turf/heat_turf = get_turf(src) + environment_heat_capacity = heat_turf.heat_capacity + + if((environment.temperature > (T0C + 50)) || (environment.temperature < (T0C + 10))) + var/transfer_coefficient = 1 + + handle_temperature_damage(HEAD, environment.temperature, environment_heat_capacity*transfer_coefficient) + + if(stat==2) + bodytemperature += 0.1*(environment.temperature - bodytemperature)*environment_heat_capacity/(environment_heat_capacity + 270000) + + //Account for massive pressure differences + + return //TODO: DEFERRED + +/mob/living/carbon/brain/proc/handle_temperature_damage(body_part, exposed_temperature, exposed_intensity) + if(status_flags & GODMODE) return + + if(exposed_temperature > bodytemperature) + var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) + //adjustFireLoss(2.5*discomfort) + //adjustFireLoss(5.0*discomfort) + adjustFireLoss(20.0*discomfort) + + else + var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) + //adjustFireLoss(2.5*discomfort) + adjustFireLoss(5.0*discomfort) + + +/mob/living/carbon/brain/handle_chemicals_in_body() + chem_effects.Cut() + + if(touching) touching.metabolize() + if(ingested) ingested.metabolize() + if(bloodstr) bloodstr.metabolize() + + // decrement dizziness counter, clamped to 0 + if(resting) + dizziness = max(0, dizziness - 5) + else + dizziness = max(0, dizziness - 1) + + updatehealth() + + return //TODO: DEFERRED + +/mob/living/carbon/brain/handle_regular_status_updates() //TODO: comment out the unused bits >_> + updatehealth() + + if(stat == DEAD) //DEAD. BROWN BREAD. SWIMMING WITH THE SPESS CARP + blinded = 1 + silent = 0 + else //ALIVE. LIGHTS ARE ON + if( !container && (health < config.health_threshold_dead || ((world.time - timeofhostdeath) > config.revival_brain_life)) ) + death() + blinded = 1 + silent = 0 + return 1 + + //Handling EMP effect in the Life(), it's made VERY simply, and has some additional effects handled elsewhere + if(emp_damage) //This is pretty much a damage type only used by MMIs, dished out by the emp_act + if(!(container && istype(container, /obj/item/device/mmi))) + emp_damage = 0 + else + emp_damage = round(emp_damage,1)//Let's have some nice numbers to work with + switch(emp_damage) + if(31 to INFINITY) + emp_damage = 30//Let's not overdo it + if(21 to 30)//High level of EMP damage, unable to see, hear, or speak + SetBlinded(1) + blinded = 1 + ear_deaf = 1 + silent = 1 + if(!alert)//Sounds an alarm, but only once per 'level' + emote("alarm") + to_chat(src, span_red("Major electrical distruption detected: System rebooting.")) + alert = 1 + if(prob(75)) + emp_damage -= 1 + if(20) + alert = 0 + blinded = 0 + SetBlinded(0) + ear_deaf = 0 + silent = 0 + emp_damage -= 1 + if(11 to 19)//Moderate level of EMP damage, resulting in nearsightedness and ear damage + eye_blurry = 1 + ear_damage = 1 + if(!alert) + emote("alert") + to_chat(src, span_red("Primary systems are now online.")) + alert = 1 + if(prob(50)) + emp_damage -= 1 + if(10) + alert = 0 + eye_blurry = 0 + ear_damage = 0 + emp_damage -= 1 + if(2 to 9)//Low level of EMP damage, has few effects(handled elsewhere) + if(!alert) + emote("notice") + to_chat(src, span_red("System reboot nearly complete.")) + alert = 1 + if(prob(25)) + emp_damage -= 1 + if(1) + alert = 0 + to_chat(src, span_red("All systems restored.")) + emp_damage -= 1 + + return 1 + +/mob/living/carbon/brain/handle_regular_hud_updates() + if (stat == 2 || (XRAY in src.mutations)) + sight |= SEE_TURFS + sight |= SEE_MOBS + sight |= SEE_OBJS + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_LEVEL_TWO + else if (stat != 2) + sight &= ~SEE_TURFS + sight &= ~SEE_MOBS + sight &= ~SEE_OBJS + see_in_dark = 2 + see_invisible = SEE_INVISIBLE_LIVING + + if (healths) + if (stat != 2) + switch(health) + if(100 to INFINITY) + healths.icon_state = "health0" + if(80 to 100) + healths.icon_state = "health1" + if(60 to 80) + healths.icon_state = "health2" + if(40 to 60) + healths.icon_state = "health3" + if(20 to 40) + healths.icon_state = "health4" + if(0 to 20) + healths.icon_state = "health5" + else + healths.icon_state = "health6" + else + healths.icon_state = "health7" + + if (stat == 2 || (XRAY in src.mutations)) + sight |= SEE_TURFS + sight |= SEE_MOBS + sight |= SEE_OBJS + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_LEVEL_TWO + else if (stat != 2) + sight &= ~SEE_TURFS + sight &= ~SEE_MOBS + sight &= ~SEE_OBJS + see_in_dark = 2 + see_invisible = SEE_INVISIBLE_LIVING + if (client) + client.screen.Remove(global_hud.blurry,global_hud.druggy,global_hud.vimpaired) + + if (stat != 2) + if ((blinded)) + overlay_fullscreen("blind", /obj/screen/fullscreen/blind) + else + clear_fullscreen("blind") + set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) + set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) + set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) + + if (machine) + if (!( machine.check_eye(src) )) + reset_view(null) + else + if(client && !client.adminobs) + reset_view(null) + + return 1 diff --git a/code/modules/mob/living/carbon/brain/login.dm b/code/modules/mob/living/carbon/brain/login.dm index 107b8e0ab78..6248b8f154d 100644 --- a/code/modules/mob/living/carbon/brain/login.dm +++ b/code/modules/mob/living/carbon/brain/login.dm @@ -1,3 +1,3 @@ -/mob/living/carbon/brain/Login() - ..() +/mob/living/carbon/brain/Login() + ..() SetSleeping(0) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/brain/say.dm b/code/modules/mob/living/carbon/brain/say.dm index e03067db1a3..5a890b6aa51 100644 --- a/code/modules/mob/living/carbon/brain/say.dm +++ b/code/modules/mob/living/carbon/brain/say.dm @@ -1,29 +1,29 @@ -//TODO: Convert this over for languages. -/mob/living/carbon/brain/say(var/message, var/datum/language/speaking = null, var/whispering = 0) - if(silent) - return - - message = sanitize(message) - - if(!(container && container.can_speak)) - return //Certain objects can speak, like MMIs. Most others cannot. -Q - else - if(prob(emp_damage * 4)) - if(prob(10))//10% chance to drop the message entirely - return - else - message = Gibberish(message, (emp_damage*6))//scrambles the message, gets worse when emp_damage is higher - - ..() - -/mob/living/carbon/brain/handle_message_mode(message_mode, message, verb, speaking, used_radios) - ..() - if(message_mode) - var/obj/item/device/mmi/R = container - if(R.radio && R.radio.radio_enabled) - if(message_mode == "general") - message_mode = null - return R.radio.talk_into(src, message, message_mode, verb, speaking) - else - to_chat(src, "Your radio is disabled.") - return 0 +//TODO: Convert this over for languages. +/mob/living/carbon/brain/say(var/message, var/datum/language/speaking = null, var/whispering = 0) + if(silent) + return + + message = sanitize(message) + + if(!(container && container.can_speak)) + return //Certain objects can speak, like MMIs. Most others cannot. -Q + else + if(prob(emp_damage * 4)) + if(prob(10))//10% chance to drop the message entirely + return + else + message = Gibberish(message, (emp_damage*6))//scrambles the message, gets worse when emp_damage is higher + + ..() + +/mob/living/carbon/brain/handle_message_mode(message_mode, message, verb, speaking, used_radios) + ..() + if(message_mode) + var/obj/item/device/mmi/R = container + if(R.radio && R.radio.radio_enabled) + if(message_mode == "general") + message_mode = null + return R.radio.talk_into(src, message, message_mode, verb, speaking) + else + to_chat(src, "Your radio is disabled.") + return 0 diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index a5230484ed2..8dc29e6d3b8 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -1,32 +1,32 @@ -/mob/living/carbon - gender = MALE - blocks_emissive = EMISSIVE_BLOCK_UNIQUE // BLEH, this could be improved for transparent species and stuff! And blocks glowing eyes?! - var/datum/species/species //Contains icon generation and language information, set during New(). - var/list/stomach_contents = list() - var/list/datum/disease2/disease/virus2 = list() - var/list/antibodies = list() - var/last_eating = 0 //Not sure what this does... I found it hidden in food.dm - - var/life_tick = 0 // The amount of life ticks that have processed on this mob. - - // total amount of wounds on mob, used to spread out healing and the like over all wounds - var/number_wounds = 0 - var/obj/item/handcuffed = null //Whether or not the mob is handcuffed - var/obj/item/legcuffed = null //Same as handcuffs but for legs. Bear traps use this. - //Surgery info - var/datum/surgery_status/op_stage = new/datum/surgery_status - //Active emote/pose - var/pose = null - var/list/chem_effects = list() - var/datum/reagents/metabolism/bloodstream/bloodstr = null - var/datum/reagents/metabolism/ingested/ingested = null - var/datum/reagents/metabolism/touch/touching = null - - var/pulse = PULSE_NORM //current pulse level - - var/does_not_breathe = 0 //Used for specific mobs that can't take advantage of the species flags (changelings) - - //these two help govern taste. The first is the last time a taste message was shown to the plaer. - //the second is the message in question. - var/last_taste_time = 0 +/mob/living/carbon + gender = MALE + blocks_emissive = EMISSIVE_BLOCK_UNIQUE // BLEH, this could be improved for transparent species and stuff! And blocks glowing eyes?! + var/datum/species/species //Contains icon generation and language information, set during New(). + var/list/stomach_contents = list() + var/list/datum/disease2/disease/virus2 = list() + var/list/antibodies = list() + var/last_eating = 0 //Not sure what this does... I found it hidden in food.dm + + var/life_tick = 0 // The amount of life ticks that have processed on this mob. + + // total amount of wounds on mob, used to spread out healing and the like over all wounds + var/number_wounds = 0 + var/obj/item/handcuffed = null //Whether or not the mob is handcuffed + var/obj/item/legcuffed = null //Same as handcuffs but for legs. Bear traps use this. + //Surgery info + var/datum/surgery_status/op_stage = new/datum/surgery_status + //Active emote/pose + var/pose = null + var/list/chem_effects = list() + var/datum/reagents/metabolism/bloodstream/bloodstr = null + var/datum/reagents/metabolism/ingested/ingested = null + var/datum/reagents/metabolism/touch/touching = null + + var/pulse = PULSE_NORM //current pulse level + + var/does_not_breathe = 0 //Used for specific mobs that can't take advantage of the species flags (changelings) + + //these two help govern taste. The first is the last time a taste message was shown to the plaer. + //the second is the message in question. + var/last_taste_time = 0 var/last_taste_text = "" \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/appearance.dm b/code/modules/mob/living/carbon/human/appearance.dm index b1424ae5b04..c2f767cbcb5 100644 --- a/code/modules/mob/living/carbon/human/appearance.dm +++ b/code/modules/mob/living/carbon/human/appearance.dm @@ -1,252 +1,252 @@ -/mob/living/carbon/human/proc/change_appearance(var/flags = APPEARANCE_ALL_HAIR, - var/mob/user = src, - var/check_species_whitelist = 1, - var/list/species_whitelist = list(), - var/list/species_blacklist = list(), - var/datum/tgui_state/state = GLOB.tgui_self_state) - var/datum/tgui_module/appearance_changer/AC = new(src, src, check_species_whitelist, species_whitelist, species_blacklist) - AC.flags = flags - AC.tgui_interact(user, custom_state = state) - -/mob/living/carbon/human/proc/change_species(var/new_species) - if(!new_species) - return - - if(species == new_species) - return - - if(!(new_species in GLOB.all_species)) - return - - set_species(new_species) - reset_hair() - return 1 - -/mob/living/carbon/human/proc/change_gender(var/gender) - if(src.gender == gender) - return - - src.gender = gender - //reset_hair() //VOREStation Remove - Don't just randomize hair on gender swaps for prometheans. - update_dna() - update_icons_body() - return 1 - -/mob/living/carbon/human/proc/change_gender_identity(var/identifying_gender) - if(src.identifying_gender == identifying_gender) - return - - src.identifying_gender = identifying_gender - return 1 - -/mob/living/carbon/human/proc/change_hair(var/hair_style) - if(!hair_style) - return - - if(h_style == hair_style) - return - - if(!(hair_style in hair_styles_list)) - return - - h_style = hair_style - - update_hair() - return 1 - -/mob/living/carbon/human/proc/change_hair_gradient(var/hair_gradient) - if(!hair_gradient) - return - - if(grad_style == hair_gradient) - return - - if(!(hair_gradient in GLOB.hair_gradients)) - return - - grad_style = hair_gradient - - update_hair() - return 1 - -/mob/living/carbon/human/proc/change_facial_hair(var/facial_hair_style) - if(!facial_hair_style) - return - - if(f_style == facial_hair_style) - return - - if(!(facial_hair_style in facial_hair_styles_list)) - return - - f_style = facial_hair_style - - update_hair() - return 1 - -/mob/living/carbon/human/proc/reset_hair() - var/list/valid_hairstyles = generate_valid_hairstyles() - var/list/valid_facial_hairstyles = generate_valid_facial_hairstyles() - - if(valid_hairstyles.len) - h_style = pick(valid_hairstyles) - else - //this shouldn't happen - h_style = "Bald" - - if(valid_facial_hairstyles.len) - f_style = pick(valid_facial_hairstyles) - else - //this shouldn't happen - f_style = "Shaved" - - update_hair() - -/mob/living/carbon/human/proc/change_eye_color(var/red, var/green, var/blue) - if(red == r_eyes && green == g_eyes && blue == b_eyes) - return - - r_eyes = red - g_eyes = green - b_eyes = blue - - update_eyes() - update_icons_body() - return 1 - -/mob/living/carbon/human/proc/change_hair_color(var/red, var/green, var/blue) - if(red == r_hair && green == g_hair && blue == b_hair) - return - - r_hair = red - g_hair = green - b_hair = blue - - update_hair() - return 1 - -/mob/living/carbon/human/proc/change_grad_color(var/red, var/green, var/blue) - if(red == r_grad && green == g_grad && blue == b_grad) - return - - r_grad = red - g_grad = green - b_grad = blue - - update_hair() - return 1 - -/mob/living/carbon/human/proc/change_facial_hair_color(var/red, var/green, var/blue) - if(red == r_facial && green == g_facial && blue == b_facial) - return - - r_facial = red - g_facial = green - b_facial = blue - - update_hair() - return 1 - -/mob/living/carbon/human/proc/change_skin_color(var/red, var/green, var/blue) - if(red == r_skin && green == g_skin && blue == b_skin || !(species.appearance_flags & HAS_SKIN_COLOR)) - return - - r_skin = red - g_skin = green - b_skin = blue - - force_update_limbs() - update_icons_body() - return 1 - -/mob/living/carbon/human/proc/change_skin_tone(var/tone) - if(s_tone == tone || !(species.appearance_flags & HAS_SKIN_TONE)) - return - - s_tone = tone - - force_update_limbs() - update_icons_body() - return 1 - -/mob/living/carbon/human/proc/update_dna() - check_dna() - dna.ready_dna(src) - for(var/obj/item/organ/O in organs) - O.dna = dna // Update all of those because apparently they're separate, and icons won't update properly - -/mob/living/carbon/human/proc/generate_valid_species(var/check_whitelist = 1, var/list/whitelist = list(), var/list/blacklist = list()) - var/list/valid_species = new() - for(var/current_species_name in GLOB.all_species) - var/datum/species/current_species = GLOB.all_species[current_species_name] - - if(check_whitelist && config.usealienwhitelist && !check_rights(R_ADMIN|R_EVENT, 0, src)) //If we're using the whitelist, make sure to check it! - if(!(current_species.spawn_flags & SPECIES_CAN_JOIN)) - continue - if(whitelist.len && !(current_species_name in whitelist)) - continue - if(blacklist.len && (current_species_name in blacklist)) - continue - if((current_species.spawn_flags & SPECIES_IS_WHITELISTED) && !is_alien_whitelisted(src, current_species)) - continue - - valid_species += current_species_name - - return valid_species - -/mob/living/carbon/human/proc/generate_valid_hairstyles(var/check_gender = 1) - - var/use_species = species.get_bodytype(src) - var/obj/item/organ/external/head/H = get_organ(BP_HEAD) - if(H) use_species = H.species.get_bodytype(src) - - var/list/valid_hairstyles = new() - for(var/hairstyle in hair_styles_list) - var/datum/sprite_accessory/S = hair_styles_list[hairstyle] - - if(check_gender && gender != NEUTER) - if(gender == MALE && S.gender == FEMALE) - continue - else if(gender == FEMALE && S.gender == MALE) - continue - - if(!(use_species in S.species_allowed)) - continue - - if(S.ckeys_allowed && !(ckey in S.ckeys_allowed)) //VOREStation add - ckey whitelist check - continue //VOREStation add - ckey whitelist check - - valid_hairstyles += hairstyle - - return valid_hairstyles - -/mob/living/carbon/human/proc/generate_valid_facial_hairstyles() - - var/use_species = species.get_bodytype(src) - var/obj/item/organ/external/head/H = get_organ(BP_HEAD) - if(H) use_species = H.species.get_bodytype(src) - - var/list/valid_facial_hairstyles = new() - for(var/facialhairstyle in facial_hair_styles_list) - var/datum/sprite_accessory/S = facial_hair_styles_list[facialhairstyle] - - if(gender != NEUTER) - if(gender == MALE && S.gender == FEMALE) - continue - else if(gender == FEMALE && S.gender == MALE) - continue - - if(!(use_species in S.species_allowed)) - continue - - if(S.ckeys_allowed && !(ckey in S.ckeys_allowed)) //VOREStation add - ckey whitelist check - continue //VOREStation add - ckey whitelist check - - valid_facial_hairstyles += facialhairstyle - - return valid_facial_hairstyles - -/mob/living/carbon/human/proc/force_update_limbs() - for(var/obj/item/organ/external/O in organs) - O.sync_colour_to_human(src) - update_icons_body(FALSE) +/mob/living/carbon/human/proc/change_appearance(var/flags = APPEARANCE_ALL_HAIR, + var/mob/user = src, + var/check_species_whitelist = 1, + var/list/species_whitelist = list(), + var/list/species_blacklist = list(), + var/datum/tgui_state/state = GLOB.tgui_self_state) + var/datum/tgui_module/appearance_changer/AC = new(src, src, check_species_whitelist, species_whitelist, species_blacklist) + AC.flags = flags + AC.tgui_interact(user, custom_state = state) + +/mob/living/carbon/human/proc/change_species(var/new_species) + if(!new_species) + return + + if(species == new_species) + return + + if(!(new_species in GLOB.all_species)) + return + + set_species(new_species) + reset_hair() + return 1 + +/mob/living/carbon/human/proc/change_gender(var/gender) + if(src.gender == gender) + return + + src.gender = gender + //reset_hair() //VOREStation Remove - Don't just randomize hair on gender swaps for prometheans. + update_dna() + update_icons_body() + return 1 + +/mob/living/carbon/human/proc/change_gender_identity(var/identifying_gender) + if(src.identifying_gender == identifying_gender) + return + + src.identifying_gender = identifying_gender + return 1 + +/mob/living/carbon/human/proc/change_hair(var/hair_style) + if(!hair_style) + return + + if(h_style == hair_style) + return + + if(!(hair_style in hair_styles_list)) + return + + h_style = hair_style + + update_hair() + return 1 + +/mob/living/carbon/human/proc/change_hair_gradient(var/hair_gradient) + if(!hair_gradient) + return + + if(grad_style == hair_gradient) + return + + if(!(hair_gradient in GLOB.hair_gradients)) + return + + grad_style = hair_gradient + + update_hair() + return 1 + +/mob/living/carbon/human/proc/change_facial_hair(var/facial_hair_style) + if(!facial_hair_style) + return + + if(f_style == facial_hair_style) + return + + if(!(facial_hair_style in facial_hair_styles_list)) + return + + f_style = facial_hair_style + + update_hair() + return 1 + +/mob/living/carbon/human/proc/reset_hair() + var/list/valid_hairstyles = generate_valid_hairstyles() + var/list/valid_facial_hairstyles = generate_valid_facial_hairstyles() + + if(valid_hairstyles.len) + h_style = pick(valid_hairstyles) + else + //this shouldn't happen + h_style = "Bald" + + if(valid_facial_hairstyles.len) + f_style = pick(valid_facial_hairstyles) + else + //this shouldn't happen + f_style = "Shaved" + + update_hair() + +/mob/living/carbon/human/proc/change_eye_color(var/red, var/green, var/blue) + if(red == r_eyes && green == g_eyes && blue == b_eyes) + return + + r_eyes = red + g_eyes = green + b_eyes = blue + + update_eyes() + update_icons_body() + return 1 + +/mob/living/carbon/human/proc/change_hair_color(var/red, var/green, var/blue) + if(red == r_hair && green == g_hair && blue == b_hair) + return + + r_hair = red + g_hair = green + b_hair = blue + + update_hair() + return 1 + +/mob/living/carbon/human/proc/change_grad_color(var/red, var/green, var/blue) + if(red == r_grad && green == g_grad && blue == b_grad) + return + + r_grad = red + g_grad = green + b_grad = blue + + update_hair() + return 1 + +/mob/living/carbon/human/proc/change_facial_hair_color(var/red, var/green, var/blue) + if(red == r_facial && green == g_facial && blue == b_facial) + return + + r_facial = red + g_facial = green + b_facial = blue + + update_hair() + return 1 + +/mob/living/carbon/human/proc/change_skin_color(var/red, var/green, var/blue) + if(red == r_skin && green == g_skin && blue == b_skin || !(species.appearance_flags & HAS_SKIN_COLOR)) + return + + r_skin = red + g_skin = green + b_skin = blue + + force_update_limbs() + update_icons_body() + return 1 + +/mob/living/carbon/human/proc/change_skin_tone(var/tone) + if(s_tone == tone || !(species.appearance_flags & HAS_SKIN_TONE)) + return + + s_tone = tone + + force_update_limbs() + update_icons_body() + return 1 + +/mob/living/carbon/human/proc/update_dna() + check_dna() + dna.ready_dna(src) + for(var/obj/item/organ/O in organs) + O.dna = dna // Update all of those because apparently they're separate, and icons won't update properly + +/mob/living/carbon/human/proc/generate_valid_species(var/check_whitelist = 1, var/list/whitelist = list(), var/list/blacklist = list()) + var/list/valid_species = new() + for(var/current_species_name in GLOB.all_species) + var/datum/species/current_species = GLOB.all_species[current_species_name] + + if(check_whitelist && config.usealienwhitelist && !check_rights(R_ADMIN|R_EVENT, 0, src)) //If we're using the whitelist, make sure to check it! + if(!(current_species.spawn_flags & SPECIES_CAN_JOIN)) + continue + if(whitelist.len && !(current_species_name in whitelist)) + continue + if(blacklist.len && (current_species_name in blacklist)) + continue + if((current_species.spawn_flags & SPECIES_IS_WHITELISTED) && !is_alien_whitelisted(src, current_species)) + continue + + valid_species += current_species_name + + return valid_species + +/mob/living/carbon/human/proc/generate_valid_hairstyles(var/check_gender = 1) + + var/use_species = species.get_bodytype(src) + var/obj/item/organ/external/head/H = get_organ(BP_HEAD) + if(H) use_species = H.species.get_bodytype(src) + + var/list/valid_hairstyles = new() + for(var/hairstyle in hair_styles_list) + var/datum/sprite_accessory/S = hair_styles_list[hairstyle] + + if(check_gender && gender != NEUTER) + if(gender == MALE && S.gender == FEMALE) + continue + else if(gender == FEMALE && S.gender == MALE) + continue + + if(!(use_species in S.species_allowed)) + continue + + if(S.ckeys_allowed && !(ckey in S.ckeys_allowed)) //VOREStation add - ckey whitelist check + continue //VOREStation add - ckey whitelist check + + valid_hairstyles += hairstyle + + return valid_hairstyles + +/mob/living/carbon/human/proc/generate_valid_facial_hairstyles() + + var/use_species = species.get_bodytype(src) + var/obj/item/organ/external/head/H = get_organ(BP_HEAD) + if(H) use_species = H.species.get_bodytype(src) + + var/list/valid_facial_hairstyles = new() + for(var/facialhairstyle in facial_hair_styles_list) + var/datum/sprite_accessory/S = facial_hair_styles_list[facialhairstyle] + + if(gender != NEUTER) + if(gender == MALE && S.gender == FEMALE) + continue + else if(gender == FEMALE && S.gender == MALE) + continue + + if(!(use_species in S.species_allowed)) + continue + + if(S.ckeys_allowed && !(ckey in S.ckeys_allowed)) //VOREStation add - ckey whitelist check + continue //VOREStation add - ckey whitelist check + + valid_facial_hairstyles += facialhairstyle + + return valid_facial_hairstyles + +/mob/living/carbon/human/proc/force_update_limbs() + for(var/obj/item/organ/external/O in organs) + O.sync_colour_to_human(src) + update_icons_body(FALSE) diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index dd816d1e8f5..a34cce9cb17 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -1,147 +1,147 @@ -/mob/living/carbon/human/gib() - - if(vr_holder) - exit_vr() - // Delete the link, because this mob won't be around much longer - vr_holder.vr_link = null - - if(vr_link) - vr_link.exit_vr() - vr_link.vr_holder = null - vr_link = null - - for(var/obj/item/organ/I in internal_organs) - I.removed() - if(isturf(I?.loc)) // Some organs qdel themselves or other things when removed - I.throw_at(get_edge_target_turf(src,pick(alldirs)),rand(1,3),30) - - for(var/obj/item/organ/external/E in src.organs) - E.droplimb(0,DROPLIMB_EDGE,1) - - sleep(1) - - for(var/obj/item/I in src) - drop_from_inventory(I) - I.throw_at(get_edge_target_turf(src,pick(alldirs)), rand(1,3), round(30/I.w_class)) - - ..(species.gibbed_anim) // uses the default mob.dmi file for these, so we only need to specify the first argument - gibs(loc, dna, null, species.get_flesh_colour(src), species.get_blood_colour(src)) - -/mob/living/carbon/human/dust() - if(species) - ..(species.dusted_anim, species.remains_type) - else - ..() - -/mob/living/carbon/human/ash() - if(species) - ..(species.dusted_anim) - else - ..() - -/mob/living/carbon/human/death(gibbed) - - if(stat == DEAD) return - - BITSET(hud_updateflag, HEALTH_HUD) - BITSET(hud_updateflag, STATUS_HUD) - BITSET(hud_updateflag, LIFE_HUD) - - //Handle species-specific deaths. - species.handle_death(src) - animate_tail_stop() - stop_flying() //VOREStation Edit. - - //Handle snowflake ling stuff. - if(mind && mind.changeling) - // If the ling is capable of revival, don't allow them to see deadchat. - if(mind.changeling.chem_charges >= CHANGELING_STASIS_COST) - if(mind.changeling.max_geneticpoints >= 0) // Absorbed lings don't count, as they can't revive. - forbid_seeing_deadchat = TRUE - - //Handle brain slugs. - var/obj/item/organ/external/Hd = get_organ(BP_HEAD) - var/mob/living/simple_mob/animal/borer/B - - if(Hd) - for(var/I in Hd.implants) - if(istype(I,/mob/living/simple_mob/animal/borer)) - B = I - if(B) - if(!B.ckey && ckey && B.controlling) - B.ckey = ckey - B.controlling = 0 - if(B.host_brain.ckey) - ckey = B.host_brain.ckey - B.host_brain.ckey = null - B.host_brain.name = "host brain" - B.host_brain.real_name = "host brain" - - verbs -= /mob/living/carbon/proc/release_control - - callHook("death", list(src, gibbed)) - - if(mind) - // SSgame_master.adjust_danger(gibbed ? 40 : 20) // VOREStation Edit - We don't use SSgame_master yet. - for(var/mob/observer/dead/O in mob_list) - if(O.client && O.client.is_preference_enabled(/datum/client_preference/show_dsay)) - to_chat(O, "[src] has died in [get_area(src)]. [ghost_follow_link(src, O)] ") - - if(!gibbed && species.death_sound) - playsound(src, species.death_sound, 80, 1, 1) - - if(ticker && ticker.mode) - sql_report_death(src) - ticker.mode.check_win() - - if(wearing_rig) - wearing_rig.notify_ai("Warning: user death event. Mobility control passed to integrated intelligence system.") - - // If the body is in VR, move the mind back to the real world - if(vr_holder) - src.exit_vr() - src.vr_holder.vr_link = null - for(var/obj/item/W in src) - src.drop_from_inventory(W) - - // If our mind is in VR, bring it back to the real world so it can die with its body - if(vr_link) - vr_link.exit_vr() - vr_link.vr_holder = null - vr_link = null - to_chat(src, "Everything abruptly stops.") - - return ..(gibbed,species.get_death_message(src)) - -/mob/living/carbon/human/proc/ChangeToHusk() - if(HUSK in mutations) return - - if(f_style) - f_style = "Shaved" //we only change the icon_state of the hair datum, so it doesn't mess up their UI/UE - if(h_style) - h_style = "Bald" - update_hair(0) - - mutations.Add(HUSK) - status_flags |= DISFIGURED //makes them unknown without fucking up other stuff like admintools - update_icons_body() - return - -/mob/living/carbon/human/proc/Drain() - ChangeToHusk() - mutations |= HUSK - return - -/mob/living/carbon/human/proc/ChangeToSkeleton() - if(SKELETON in src.mutations) return - - if(f_style) - f_style = "Shaved" - if(h_style) - h_style = "Bald" - update_hair(0) - - mutations.Add(SKELETON) - status_flags |= DISFIGURED - update_icons_body() - return +/mob/living/carbon/human/gib() + + if(vr_holder) + exit_vr() + // Delete the link, because this mob won't be around much longer + vr_holder.vr_link = null + + if(vr_link) + vr_link.exit_vr() + vr_link.vr_holder = null + vr_link = null + + for(var/obj/item/organ/I in internal_organs) + I.removed() + if(isturf(I?.loc)) // Some organs qdel themselves or other things when removed + I.throw_at(get_edge_target_turf(src,pick(alldirs)),rand(1,3),30) + + for(var/obj/item/organ/external/E in src.organs) + E.droplimb(0,DROPLIMB_EDGE,1) + + sleep(1) + + for(var/obj/item/I in src) + drop_from_inventory(I) + I.throw_at(get_edge_target_turf(src,pick(alldirs)), rand(1,3), round(30/I.w_class)) + + ..(species.gibbed_anim) // uses the default mob.dmi file for these, so we only need to specify the first argument + gibs(loc, dna, null, species.get_flesh_colour(src), species.get_blood_colour(src)) + +/mob/living/carbon/human/dust() + if(species) + ..(species.dusted_anim, species.remains_type) + else + ..() + +/mob/living/carbon/human/ash() + if(species) + ..(species.dusted_anim) + else + ..() + +/mob/living/carbon/human/death(gibbed) + + if(stat == DEAD) return + + BITSET(hud_updateflag, HEALTH_HUD) + BITSET(hud_updateflag, STATUS_HUD) + BITSET(hud_updateflag, LIFE_HUD) + + //Handle species-specific deaths. + species.handle_death(src) + animate_tail_stop() + stop_flying() //VOREStation Edit. + + //Handle snowflake ling stuff. + if(mind && mind.changeling) + // If the ling is capable of revival, don't allow them to see deadchat. + if(mind.changeling.chem_charges >= CHANGELING_STASIS_COST) + if(mind.changeling.max_geneticpoints >= 0) // Absorbed lings don't count, as they can't revive. + forbid_seeing_deadchat = TRUE + + //Handle brain slugs. + var/obj/item/organ/external/Hd = get_organ(BP_HEAD) + var/mob/living/simple_mob/animal/borer/B + + if(Hd) + for(var/I in Hd.implants) + if(istype(I,/mob/living/simple_mob/animal/borer)) + B = I + if(B) + if(!B.ckey && ckey && B.controlling) + B.ckey = ckey + B.controlling = 0 + if(B.host_brain.ckey) + ckey = B.host_brain.ckey + B.host_brain.ckey = null + B.host_brain.name = "host brain" + B.host_brain.real_name = "host brain" + + verbs -= /mob/living/carbon/proc/release_control + + callHook("death", list(src, gibbed)) + + if(mind) + // SSgame_master.adjust_danger(gibbed ? 40 : 20) // VOREStation Edit - We don't use SSgame_master yet. + for(var/mob/observer/dead/O in mob_list) + if(O.client && O.client.is_preference_enabled(/datum/client_preference/show_dsay)) + to_chat(O, "[src] has died in [get_area(src)]. [ghost_follow_link(src, O)] ") + + if(!gibbed && species.death_sound) + playsound(src, species.death_sound, 80, 1, 1) + + if(ticker && ticker.mode) + sql_report_death(src) + ticker.mode.check_win() + + if(wearing_rig) + wearing_rig.notify_ai("Warning: user death event. Mobility control passed to integrated intelligence system.") + + // If the body is in VR, move the mind back to the real world + if(vr_holder) + src.exit_vr() + src.vr_holder.vr_link = null + for(var/obj/item/W in src) + src.drop_from_inventory(W) + + // If our mind is in VR, bring it back to the real world so it can die with its body + if(vr_link) + vr_link.exit_vr() + vr_link.vr_holder = null + vr_link = null + to_chat(src, "Everything abruptly stops.") + + return ..(gibbed,species.get_death_message(src)) + +/mob/living/carbon/human/proc/ChangeToHusk() + if(HUSK in mutations) return + + if(f_style) + f_style = "Shaved" //we only change the icon_state of the hair datum, so it doesn't mess up their UI/UE + if(h_style) + h_style = "Bald" + update_hair(0) + + mutations.Add(HUSK) + status_flags |= DISFIGURED //makes them unknown without fucking up other stuff like admintools + update_icons_body() + return + +/mob/living/carbon/human/proc/Drain() + ChangeToHusk() + mutations |= HUSK + return + +/mob/living/carbon/human/proc/ChangeToSkeleton() + if(SKELETON in src.mutations) return + + if(f_style) + f_style = "Shaved" + if(h_style) + h_style = "Bald" + update_hair(0) + + mutations.Add(SKELETON) + status_flags |= DISFIGURED + update_icons_body() + return diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 7ed2ff1aa15..c7f3fe1f26e 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -1,468 +1,468 @@ -/mob/living/carbon/human/examine(mob/user) - // . = ..() //Note that we don't call parent. We build the list by ourselves. - - var/skip_gear = 0 - var/skip_body = 0 - - if(alpha <= EFFECTIVE_INVIS) - return src.loc.examine(user) // Returns messages as if they examined wherever the human was - - var/looks_synth = looksSynthetic() - - //exosuits and helmets obscure our view and stuff. - if(wear_suit) - if(wear_suit.flags_inv & HIDESUITSTORAGE) - skip_gear |= EXAMINE_SKIPSUITSTORAGE - - if(wear_suit.flags_inv & HIDEJUMPSUIT) - skip_body |= EXAMINE_SKIPARMS | EXAMINE_SKIPLEGS | EXAMINE_SKIPBODY | EXAMINE_SKIPGROIN - skip_gear |= EXAMINE_SKIPJUMPSUIT | EXAMINE_SKIPTIE | EXAMINE_SKIPHOLSTER - - else if(wear_suit.flags_inv & HIDETIE) - skip_gear |= EXAMINE_SKIPTIE | EXAMINE_SKIPHOLSTER - - else if(wear_suit.flags_inv & HIDEHOLSTER) - skip_gear |= EXAMINE_SKIPHOLSTER - - if(wear_suit.flags_inv & HIDESHOES) - skip_gear |= EXAMINE_SKIPSHOES - skip_body |= EXAMINE_SKIPFEET - - if(wear_suit.flags_inv & HIDEGLOVES) - skip_gear |= EXAMINE_SKIPGLOVES - skip_body |= EXAMINE_SKIPHANDS - - if(w_uniform) - if(w_uniform.body_parts_covered & LEGS) - skip_body |= EXAMINE_SKIPLEGS - if(w_uniform.body_parts_covered & ARMS) - skip_body |= EXAMINE_SKIPARMS - if(w_uniform.body_parts_covered & UPPER_TORSO) - skip_body |= EXAMINE_SKIPBODY - if(w_uniform.body_parts_covered & LOWER_TORSO) - skip_body |= EXAMINE_SKIPGROIN - - if(gloves && (gloves.body_parts_covered & HANDS)) - skip_body |= EXAMINE_SKIPHANDS - - if(shoes && (shoes.body_parts_covered & FEET)) - skip_body |= EXAMINE_SKIPFEET - - if(head) - if(head.flags_inv & HIDEMASK) - skip_gear |= EXAMINE_SKIPMASK - if(head.flags_inv & HIDEEYES) - skip_gear |= EXAMINE_SKIPEYEWEAR - skip_body |= EXAMINE_SKIPEYES - if(head.flags_inv & HIDEEARS) - skip_gear |= EXAMINE_SKIPEARS - if(head.flags_inv & HIDEFACE) - skip_body |= EXAMINE_SKIPFACE - - if(wear_mask && (wear_mask.flags_inv & HIDEFACE)) - skip_body |= EXAMINE_SKIPFACE - - //This is what hides what - var/list/hidden = list( - BP_GROIN = skip_body & EXAMINE_SKIPGROIN, - BP_TORSO = skip_body & EXAMINE_SKIPBODY, - BP_HEAD = skip_body & EXAMINE_SKIPHEAD, - BP_L_ARM = skip_body & EXAMINE_SKIPARMS, - BP_R_ARM = skip_body & EXAMINE_SKIPARMS, - BP_L_HAND= skip_body & EXAMINE_SKIPHANDS, - BP_R_HAND= skip_body & EXAMINE_SKIPHANDS, - BP_L_FOOT= skip_body & EXAMINE_SKIPFEET, - BP_R_FOOT= skip_body & EXAMINE_SKIPFEET, - BP_L_LEG = skip_body & EXAMINE_SKIPLEGS, - BP_R_LEG = skip_body & EXAMINE_SKIPLEGS) - - - var/gender_hidden = (skip_gear & EXAMINE_SKIPJUMPSUIT) && (skip_body & EXAMINE_SKIPFACE) - var/gender_key = get_visible_gender(user, gender_hidden) - var/datum/gender/T = gender_datums[gender_key] - if (!T) - CRASH({"Null gender datum on examine: mob="[src]",hidden="[gender_hidden]",key="[gender_key]",bio="[gender]",id="[identifying_gender]""}) - - var/name_ender = "" - if(!((skip_gear & EXAMINE_SKIPJUMPSUIT) && (skip_body & EXAMINE_SKIPFACE))) - //VOREStation Add Start - if(custom_species) - name_ender = ", a [src.custom_species]" - else if(looks_synth) - //VOREStation Add End - var/use_gender = "a synthetic" - if(gender == MALE) - use_gender = "an android" - else if(gender == FEMALE) - use_gender = "a gynoid" - - name_ender = ", [use_gender]![species.get_additional_examine_text(src)]" - - else if(species.name != "Human") - name_ender = ", \a [species.get_examine_name()]![species.get_additional_examine_text(src)]" - - var/list/msg = list("*---------*","This is \icon[src.examine_icon()][bicon(src)] [src.name][name_ender]") - - //uniform - if(w_uniform && !(skip_gear & EXAMINE_SKIPJUMPSUIT) && w_uniform.show_examine) - //Ties - var/tie_msg - var/tie_msg_warn - if(istype(w_uniform,/obj/item/clothing/under) && !(skip_gear & EXAMINE_SKIPTIE)) - var/obj/item/clothing/under/U = w_uniform - if(LAZYLEN(U.accessories)) - tie_msg += ". Attached to it is" - tie_msg_warn += "! Attached to it is" - var/list/accessory_descs = list() - if(skip_gear & EXAMINE_SKIPHOLSTER) - for(var/obj/item/clothing/accessory/A in U.accessories) - if(A.show_examine && !istype(A, /obj/item/clothing/accessory/holster)) // If we're supposed to skip holsters, actually skip them - accessory_descs += "\a [A]" - else - for(var/obj/item/clothing/accessory/A in U.accessories) - if(A.concealed_holster == 0 && A.show_examine) - accessory_descs += "\a [A]" - - tie_msg += " [lowertext(english_list(accessory_descs))]." - if(w_uniform.blood_DNA) - msg += "[T.He] [T.is] wearing \icon[w_uniform][bicon(w_uniform)] [w_uniform.gender==PLURAL?"some":"a"] [(w_uniform.blood_color != "#030303") ? "blood" : "oil"]-stained [w_uniform.name]![tie_msg]" - else - msg += "[T.He] [T.is] wearing [bicon(w_uniform)] \a [w_uniform].[tie_msg]" - - //head - if(head && !(skip_gear & EXAMINE_SKIPHELMET) && head.show_examine) - if(head.blood_DNA) - msg += "[T.He] [T.is] wearing \icon[w_uniform][bicon(head)] [head.gender==PLURAL?"some":"a"] [(head.blood_color != "#030303") ? "blood" : "oil"]-stained [head.name] on [T.his] head!" - else - msg += "[T.He] [T.is] wearing [bicon(head)] \a [head] on [T.his] head." - - //suit/armour - if(wear_suit) - var/tie_msg - var/tie_msg_warn - if(istype(wear_suit,/obj/item/clothing/suit)) - var/obj/item/clothing/suit/U = wear_suit - if(LAZYLEN(U.accessories)) - tie_msg += ". Attached to it is" - tie_msg_warn += "! Attached to it is" - var/list/accessory_descs = list() - for(var/accessory in U.accessories) - accessory_descs += "\a [accessory]" - tie_msg += " [lowertext(english_list(accessory_descs))]." - - if(wear_suit.blood_DNA) - msg += "[T.He] [T.is] wearing \icon[wear_suit][bicon(wear_suit)] [wear_suit.gender==PLURAL?"some":"a"] [(wear_suit.blood_color != "#030303") ? "blood" : "oil"]-stained [wear_suit.name]![tie_msg]" - else - msg += "[T.He] [T.is] wearing \icon[wear_suit][bicon(wear_suit)] \a [wear_suit].[tie_msg]" - - //suit/armour storage - if(s_store && !(skip_gear & EXAMINE_SKIPSUITSTORAGE) && s_store.show_examine) - if(s_store.blood_DNA) - msg += "[T.He] [T.is] carrying \icon[s_store][bicon(s_store)] [s_store.gender==PLURAL?"some":"a"] [(s_store.blood_color != "#030303") ? "blood" : "oil"]-stained [s_store.name] on [T.his] [wear_suit.name]!" - else - msg += "[T.He] [T.is] carrying \icon[s_store][bicon(s_store)] \a [s_store] on [T.his] [wear_suit.name]." - - //back - if(back && !(skip_gear & EXAMINE_SKIPBACKPACK) && back.show_examine) - if(back.blood_DNA) - msg += "[T.He] [T.has] \icon[back][bicon(back)] [back.gender==PLURAL?"some":"a"] [(back.blood_color != "#030303") ? "blood" : "oil"]-stained [back] on [T.his] back." - else - msg += "[T.He] [T.has] \icon[back][bicon(back)] \a [back] on [T.his] back." - - //left hand - if(l_hand && l_hand.show_examine) - if(l_hand.blood_DNA) - msg += "[T.He] [T.is] holding \icon[l_hand][bicon(l_hand)] [l_hand.gender==PLURAL?"some":"a"] [(l_hand.blood_color != "#030303") ? "blood" : "oil"]-stained [l_hand.name] in [T.his] left hand!" - else - msg += "[T.He] [T.is] holding \icon[l_hand][bicon(l_hand)] \a [l_hand] in [T.his] left hand." - - //right hand - if(r_hand && r_hand.show_examine) - if(r_hand.blood_DNA) - msg += "[T.He] [T.is] holding \icon[r_hand][bicon(r_hand)] [r_hand.gender==PLURAL?"some":"a"] [(r_hand.blood_color != "#030303") ? "blood" : "oil"]-stained [r_hand.name] in [T.his] right hand!" - else - msg += "[T.He] [T.is] holding \icon[r_hand][bicon(r_hand)] \a [r_hand] in [T.his] right hand." - - //gloves - if(gloves && !(skip_gear & EXAMINE_SKIPGLOVES) && gloves.show_examine) - if(gloves.blood_DNA) - msg += "[T.He] [T.has] \icon[gloves][bicon(gloves)] [gloves.gender==PLURAL?"some":"a"] [(gloves.blood_color != "#030303") ? "blood" : "oil"]-stained [gloves.name] on [T.his] hands!" - else - msg += "[T.He] [T.has] \icon[gloves][bicon(gloves)] \a [gloves] on [T.his] hands." - else if(blood_DNA && !(skip_body & EXAMINE_SKIPHANDS)) - msg += "[T.He] [T.has] [(hand_blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained hands!" - - //handcuffed? - if(handcuffed && handcuffed.show_examine) - if(istype(handcuffed, /obj/item/weapon/handcuffs/cable)) - msg += "[T.He] [T.is] \icon[handcuffed][bicon(handcuffed)] restrained with cable!" - else - msg += "[T.He] [T.is] \icon[handcuffed][bicon(handcuffed)] handcuffed!" - - //buckled - if(buckled) - msg += "[T.He] [T.is] \icon[buckled][bicon(buckled)] buckled to [buckled]!" - - //belt - if(belt && !(skip_gear & EXAMINE_SKIPBELT) && belt.show_examine) - if(belt.blood_DNA) - msg += "[T.He] [T.has] \icon[belt][bicon(belt)] [belt.gender==PLURAL?"some":"a"] [(belt.blood_color != "#030303") ? "blood" : "oil"]-stained [belt.name] about [T.his] waist!" - else - msg += "[T.He] [T.has] \icon[belt][bicon(belt)] \a [belt] about [T.his] waist." - - //shoes - if(shoes && !(skip_gear & EXAMINE_SKIPSHOES) && shoes.show_examine) - if(shoes.blood_DNA) - msg += "[T.He] [T.is] wearing \icon[shoes][bicon(shoes)] [shoes.gender==PLURAL?"some":"a"] [(shoes.blood_color != "#030303") ? "blood" : "oil"]-stained [shoes.name] on [T.his] feet!" - else - msg += "[T.He] [T.is] wearing \icon[shoes][bicon(shoes)] \a [shoes] on [T.his] feet." - else if(feet_blood_DNA && !(skip_body & EXAMINE_SKIPHANDS)) - msg += "[T.He] [T.has] [(feet_blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained feet!" - - //mask - if(wear_mask && !(skip_gear & EXAMINE_SKIPMASK) && wear_mask.show_examine) - var/descriptor = "on [T.his] face" - if(istype(wear_mask, /obj/item/weapon/grenade) && check_has_mouth()) - descriptor = "in [T.his] mouth" - - if(wear_mask.blood_DNA) - msg += "[T.He] [T.has] \icon[wear_mask][bicon(wear_mask)] [wear_mask.gender==PLURAL?"some":"a"] [(wear_mask.blood_color != "#030303") ? "blood" : "oil"]-stained [wear_mask.name] [descriptor]!" - else - msg += "[T.He] [T.has] \icon[wear_mask][bicon(wear_mask)] \a [wear_mask] [descriptor]." - - //eyes - if(glasses && !(skip_gear & EXAMINE_SKIPEYEWEAR) && glasses.show_examine) - if(glasses.blood_DNA) - msg += "[T.He] [T.has] \icon[glasses][bicon(glasses)] [glasses.gender==PLURAL?"some":"a"] [(glasses.blood_color != "#030303") ? "blood" : "oil"]-stained [glasses] covering [T.his] eyes!" - else - msg += "[T.He] [T.has] \icon[glasses][bicon(glasses)] \a [glasses] covering [T.his] eyes." - - //left ear - if(l_ear && !(skip_gear & EXAMINE_SKIPEARS) && l_ear.show_examine) - msg += "[T.He] [T.has] \icon[l_ear][bicon(l_ear)] \a [l_ear] on [T.his] left ear." - - //right ear - if(r_ear && !(skip_gear & EXAMINE_SKIPEARS) && r_ear.show_examine) - msg += "[T.He] [T.has] \icon[r_ear][bicon(r_ear)] \a [r_ear] on [T.his] right ear." - - //ID - if(wear_id && wear_id.show_examine) - msg += "[T.He] [T.is] wearing \icon[wear_id][bicon(wear_id)]\a [wear_id]." - - //Jitters - if(is_jittery) - if(jitteriness >= 300) - msg += "[T.He] [T.is] convulsing violently!" - else if(jitteriness >= 200) - msg += "[T.He] [T.is] extremely jittery." - else if(jitteriness >= 100) - msg += "[T.He] [T.is] twitching ever so slightly." - - //splints - for(var/organ in BP_ALL) - var/obj/item/organ/external/o = get_organ(organ) - if(o && o.splinted && o.splinted.loc == o) - msg += "[T.He] [T.has] \a [o.splinted] on [T.his] [o.name]!" - - if(suiciding) - msg += "[T.He] appears to have commited suicide... there is no hope of recovery." - - //VOREStation Add - var/list/vorestrings = list() - vorestrings += examine_weight() - vorestrings += examine_nutrition() - vorestrings += examine_bellies() - vorestrings += examine_pickup_size() - vorestrings += examine_step_size() - vorestrings += examine_nif() - vorestrings += examine_chimera() - for(var/entry in vorestrings) - if(entry == "" || entry == null) - vorestrings -= entry - msg += vorestrings - //VOREStation Add End - - if(mSmallsize in mutations) - msg += "[T.He] [T.is] very short!" - - if (src.stat) - msg += "[T.He] [T.is]n't responding to anything around [T.him] and seems to be asleep." - if((stat == 2 || src.losebreath) && get_dist(user, src) <= 3) - msg += "[T.He] [T.does] not appear to be breathing." - if(istype(user, /mob/living/carbon/human) && !user.stat && Adjacent(user)) - user.visible_message("[usr] checks [src]'s pulse.", "You check [src]'s pulse.") - spawn(15) - if(isobserver(user) || (Adjacent(user) && !user.stat)) // If you're a corpse then you can't exactly check their pulse, but ghosts can see anything - if(pulse == PULSE_NONE) - to_chat(user, "[T.He] [T.has] no pulse[src.client ? "" : " and [T.his] soul has departed"]...") - else - to_chat(user, "[T.He] [T.has] a pulse!") - - if(fire_stacks) - msg += "[T.He] [T.is] covered in some liquid." - if(on_fire) - msg += "[T.He] [T.is] on fire!." - - var/ssd_msg = species.get_ssd(src) - if(ssd_msg && (!should_have_organ("brain") || has_brain()) && stat != DEAD) - if(!key) - msg += "[T.He] [T.is] [ssd_msg]. It doesn't look like [T.he] [T.is] waking up anytime soon." - else if(!client) - msg += "[T.He] [T.is] [ssd_msg]." - //VOREStation Add Start - if(client && ((client.inactivity / 10) / 60 > 10)) //10 Minutes - msg += "\[Inactive for [round((client.inactivity/10)/60)] minutes\]" - else if(disconnect_time) - msg += "\[Disconnected/ghosted [round(((world.realtime - disconnect_time)/10)/60)] minutes ago\]" - //VOREStation Add End - - var/list/wound_flavor_text = list() - var/list/is_bleeding = list() - var/applying_pressure = "" - - for(var/organ_tag in species.has_limbs) - - var/list/organ_data = species.has_limbs[organ_tag] - var/organ_descriptor = organ_data["descriptor"] - - var/obj/item/organ/external/E = organs_by_name[organ_tag] - if(!E) - wound_flavor_text["[organ_descriptor]"] = "[T.He] [T.is] missing [T.his] [organ_descriptor]." - else if(E.is_stump()) - wound_flavor_text["[organ_descriptor]"] = "[T.He] [T.has] a stump where [T.his] [organ_descriptor] should be." - else - continue - - for(var/obj/item/organ/external/temp in organs) - if(temp) - if((temp.organ_tag in hidden) && hidden[temp.organ_tag]) - continue //Organ is hidden, don't talk about it - if(temp.status & ORGAN_DESTROYED) - wound_flavor_text["[temp.name]"] = "[T.He] [T.is] missing [T.his] [temp.name]." - continue - - if(!looks_synth && temp.robotic == ORGAN_ROBOT) - if(!(temp.brute_dam + temp.burn_dam)) - wound_flavor_text["[temp.name]"] = "[T.He] [T.has] a [temp.name]." - else - wound_flavor_text["[temp.name]"] = "[T.He] [T.has] a [temp.name] with [temp.get_wounds_desc()]!" - continue - else if(temp.wounds.len > 0 || temp.open) - if(temp.is_stump() && temp.parent_organ && organs_by_name[temp.parent_organ]) - var/obj/item/organ/external/parent = organs_by_name[temp.parent_organ] - wound_flavor_text["[temp.name]"] = "[T.He] [T.has] [temp.get_wounds_desc()] on [T.his] [parent.name]." - else - wound_flavor_text["[temp.name]"] = "[T.He] [T.has] [temp.get_wounds_desc()] on [T.his] [temp.name]." - else - wound_flavor_text["[temp.name]"] = "" - if(temp.dislocated == 1) //VOREStation Edit Bugfix - wound_flavor_text["[temp.name]"] += "[T.His] [temp.joint] is dislocated!" - if(temp.brute_dam > temp.min_broken_damage || (temp.status & (ORGAN_BROKEN | ORGAN_MUTATED))) - wound_flavor_text["[temp.name]"] += "[T.His] [temp.name] is dented and swollen!" - - if(temp.germ_level > INFECTION_LEVEL_TWO && !(temp.status & ORGAN_DEAD)) - wound_flavor_text["[temp.name]"] += "[T.His] [temp.name] looks very infected!" - else if(temp.status & ORGAN_DEAD) - wound_flavor_text["[temp.name]"] += "[T.His] [temp.name] looks rotten!" - - if(temp.status & ORGAN_BLEEDING) - is_bleeding["[temp.name]"] += "[T.His] [temp.name] is bleeding!" - - if(temp.applied_pressure == src) - applying_pressure = "[T.He] is applying pressure to [T.his] [temp.name]." - - for(var/limb in wound_flavor_text) - var/flavor = wound_flavor_text[limb] - if(flavor) - msg += flavor - for(var/limb in is_bleeding) - var/blood = is_bleeding[limb] - if(blood) - msg += blood - for(var/implant in get_visible_implants(0)) - msg += "[src] [T.has] \a [implant] sticking out of [T.his] flesh!" - if(digitalcamo) - msg += "[T.He] [T.is] repulsively uncanny!" - - if(hasHUD(user,"security")) - var/perpname = name - var/criminal = "None" - - if(wear_id) - if(istype(wear_id, /obj/item/weapon/card/id)) - var/obj/item/weapon/card/id/I = wear_id - perpname = I.registered_name - else if(istype(wear_id, /obj/item/device/pda)) - var/obj/item/device/pda/P = wear_id - perpname = P.owner - - for (var/datum/data/record/R in data_core.security) - if(R.fields["name"] == perpname) - criminal = R.fields["criminal"] - - msg += "Criminal status: \[[criminal]\]" - msg += "Security records: \[View\] \[Add comment\]" - - if(hasHUD(user,"medical")) - var/perpname = name - var/medical = "None" - - if(wear_id) - if(istype(wear_id, /obj/item/weapon/card/id)) - var/obj/item/weapon/card/id/I = wear_id - perpname = I.registered_name - else if(istype(wear_id, /obj/item/device/pda)) - var/obj/item/device/pda/P = wear_id - perpname = P.owner - - for (var/datum/data/record/R in data_core.medical) - if (R.fields["name"] == perpname) - medical = R.fields["p_stat"] - - msg += "Physical status: \[[medical]\]" - msg += "Medical records: \[View\] \[Add comment\]" - - if(hasHUD(user,"best")) - msg += "Employment records: \[View\] \[Add comment\]" - - - var/flavor_text = print_flavor_text() - if(flavor_text) - msg += "[flavor_text]" - - // VOREStation Start - if(custom_link) - msg += "Custom link: [custom_link]" - - if(ooc_notes) - msg += "OOC Notes: \[View\] - \[Print\]" - msg += "\[Mechanical Vore Preferences\]" - // VOREStation End - msg += "*---------*" - if(applying_pressure) - msg += applying_pressure - - var/show_descs = show_descriptors_to(user) - if(show_descs) - msg += "[jointext(show_descs, "
                    ")]
                    " - - if(pose) - if(!findtext(pose, regex("\[.?!]$"))) // Will be zero if the last character is not a member of [.?!] - pose = addtext(pose,".") //Makes sure all emotes end with a period. - msg += "
                    [T.He] [pose]" //
                    intentional, extra gap. - - return msg - -//Helper procedure. Called by /mob/living/carbon/human/examine() and /mob/living/carbon/human/Topic() to determine HUD access to security and medical records. -/proc/hasHUD(mob/M as mob, hudtype) - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(hasHUD_vr(H,hudtype)) return 1 //VOREStation Add - Added records access for certain modes of omni-hud glasses - switch(hudtype) - if("security") - return istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(H.glasses, /obj/item/clothing/glasses/sunglasses/sechud) - if("medical") - return istype(H.glasses, /obj/item/clothing/glasses/hud/health) - else if(istype(M, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = M - return R.sensor_type //VOREStation Add - Borgo sensors are now binary so just have them on or off +/mob/living/carbon/human/examine(mob/user) + // . = ..() //Note that we don't call parent. We build the list by ourselves. + + var/skip_gear = 0 + var/skip_body = 0 + + if(alpha <= EFFECTIVE_INVIS) + return src.loc.examine(user) // Returns messages as if they examined wherever the human was + + var/looks_synth = looksSynthetic() + + //exosuits and helmets obscure our view and stuff. + if(wear_suit) + if(wear_suit.flags_inv & HIDESUITSTORAGE) + skip_gear |= EXAMINE_SKIPSUITSTORAGE + + if(wear_suit.flags_inv & HIDEJUMPSUIT) + skip_body |= EXAMINE_SKIPARMS | EXAMINE_SKIPLEGS | EXAMINE_SKIPBODY | EXAMINE_SKIPGROIN + skip_gear |= EXAMINE_SKIPJUMPSUIT | EXAMINE_SKIPTIE | EXAMINE_SKIPHOLSTER + + else if(wear_suit.flags_inv & HIDETIE) + skip_gear |= EXAMINE_SKIPTIE | EXAMINE_SKIPHOLSTER + + else if(wear_suit.flags_inv & HIDEHOLSTER) + skip_gear |= EXAMINE_SKIPHOLSTER + + if(wear_suit.flags_inv & HIDESHOES) + skip_gear |= EXAMINE_SKIPSHOES + skip_body |= EXAMINE_SKIPFEET + + if(wear_suit.flags_inv & HIDEGLOVES) + skip_gear |= EXAMINE_SKIPGLOVES + skip_body |= EXAMINE_SKIPHANDS + + if(w_uniform) + if(w_uniform.body_parts_covered & LEGS) + skip_body |= EXAMINE_SKIPLEGS + if(w_uniform.body_parts_covered & ARMS) + skip_body |= EXAMINE_SKIPARMS + if(w_uniform.body_parts_covered & UPPER_TORSO) + skip_body |= EXAMINE_SKIPBODY + if(w_uniform.body_parts_covered & LOWER_TORSO) + skip_body |= EXAMINE_SKIPGROIN + + if(gloves && (gloves.body_parts_covered & HANDS)) + skip_body |= EXAMINE_SKIPHANDS + + if(shoes && (shoes.body_parts_covered & FEET)) + skip_body |= EXAMINE_SKIPFEET + + if(head) + if(head.flags_inv & HIDEMASK) + skip_gear |= EXAMINE_SKIPMASK + if(head.flags_inv & HIDEEYES) + skip_gear |= EXAMINE_SKIPEYEWEAR + skip_body |= EXAMINE_SKIPEYES + if(head.flags_inv & HIDEEARS) + skip_gear |= EXAMINE_SKIPEARS + if(head.flags_inv & HIDEFACE) + skip_body |= EXAMINE_SKIPFACE + + if(wear_mask && (wear_mask.flags_inv & HIDEFACE)) + skip_body |= EXAMINE_SKIPFACE + + //This is what hides what + var/list/hidden = list( + BP_GROIN = skip_body & EXAMINE_SKIPGROIN, + BP_TORSO = skip_body & EXAMINE_SKIPBODY, + BP_HEAD = skip_body & EXAMINE_SKIPHEAD, + BP_L_ARM = skip_body & EXAMINE_SKIPARMS, + BP_R_ARM = skip_body & EXAMINE_SKIPARMS, + BP_L_HAND= skip_body & EXAMINE_SKIPHANDS, + BP_R_HAND= skip_body & EXAMINE_SKIPHANDS, + BP_L_FOOT= skip_body & EXAMINE_SKIPFEET, + BP_R_FOOT= skip_body & EXAMINE_SKIPFEET, + BP_L_LEG = skip_body & EXAMINE_SKIPLEGS, + BP_R_LEG = skip_body & EXAMINE_SKIPLEGS) + + + var/gender_hidden = (skip_gear & EXAMINE_SKIPJUMPSUIT) && (skip_body & EXAMINE_SKIPFACE) + var/gender_key = get_visible_gender(user, gender_hidden) + var/datum/gender/T = gender_datums[gender_key] + if (!T) + CRASH({"Null gender datum on examine: mob="[src]",hidden="[gender_hidden]",key="[gender_key]",bio="[gender]",id="[identifying_gender]""}) + + var/name_ender = "" + if(!((skip_gear & EXAMINE_SKIPJUMPSUIT) && (skip_body & EXAMINE_SKIPFACE))) + //VOREStation Add Start + if(custom_species) + name_ender = ", a [src.custom_species]" + else if(looks_synth) + //VOREStation Add End + var/use_gender = "a synthetic" + if(gender == MALE) + use_gender = "an android" + else if(gender == FEMALE) + use_gender = "a gynoid" + + name_ender = ", [use_gender]![species.get_additional_examine_text(src)]" + + else if(species.name != "Human") + name_ender = ", \a [species.get_examine_name()]![species.get_additional_examine_text(src)]" + + var/list/msg = list("*---------*","This is \icon[src.examine_icon()][bicon(src)] [src.name][name_ender]") + + //uniform + if(w_uniform && !(skip_gear & EXAMINE_SKIPJUMPSUIT) && w_uniform.show_examine) + //Ties + var/tie_msg + var/tie_msg_warn + if(istype(w_uniform,/obj/item/clothing/under) && !(skip_gear & EXAMINE_SKIPTIE)) + var/obj/item/clothing/under/U = w_uniform + if(LAZYLEN(U.accessories)) + tie_msg += ". Attached to it is" + tie_msg_warn += "! Attached to it is" + var/list/accessory_descs = list() + if(skip_gear & EXAMINE_SKIPHOLSTER) + for(var/obj/item/clothing/accessory/A in U.accessories) + if(A.show_examine && !istype(A, /obj/item/clothing/accessory/holster)) // If we're supposed to skip holsters, actually skip them + accessory_descs += "\a [A]" + else + for(var/obj/item/clothing/accessory/A in U.accessories) + if(A.concealed_holster == 0 && A.show_examine) + accessory_descs += "\a [A]" + + tie_msg += " [lowertext(english_list(accessory_descs))]." + if(w_uniform.blood_DNA) + msg += "[T.He] [T.is] wearing \icon[w_uniform][bicon(w_uniform)] [w_uniform.gender==PLURAL?"some":"a"] [(w_uniform.blood_color != "#030303") ? "blood" : "oil"]-stained [w_uniform.name]![tie_msg]" + else + msg += "[T.He] [T.is] wearing [bicon(w_uniform)] \a [w_uniform].[tie_msg]" + + //head + if(head && !(skip_gear & EXAMINE_SKIPHELMET) && head.show_examine) + if(head.blood_DNA) + msg += "[T.He] [T.is] wearing \icon[w_uniform][bicon(head)] [head.gender==PLURAL?"some":"a"] [(head.blood_color != "#030303") ? "blood" : "oil"]-stained [head.name] on [T.his] head!" + else + msg += "[T.He] [T.is] wearing [bicon(head)] \a [head] on [T.his] head." + + //suit/armour + if(wear_suit) + var/tie_msg + var/tie_msg_warn + if(istype(wear_suit,/obj/item/clothing/suit)) + var/obj/item/clothing/suit/U = wear_suit + if(LAZYLEN(U.accessories)) + tie_msg += ". Attached to it is" + tie_msg_warn += "! Attached to it is" + var/list/accessory_descs = list() + for(var/accessory in U.accessories) + accessory_descs += "\a [accessory]" + tie_msg += " [lowertext(english_list(accessory_descs))]." + + if(wear_suit.blood_DNA) + msg += "[T.He] [T.is] wearing \icon[wear_suit][bicon(wear_suit)] [wear_suit.gender==PLURAL?"some":"a"] [(wear_suit.blood_color != "#030303") ? "blood" : "oil"]-stained [wear_suit.name]![tie_msg]" + else + msg += "[T.He] [T.is] wearing \icon[wear_suit][bicon(wear_suit)] \a [wear_suit].[tie_msg]" + + //suit/armour storage + if(s_store && !(skip_gear & EXAMINE_SKIPSUITSTORAGE) && s_store.show_examine) + if(s_store.blood_DNA) + msg += "[T.He] [T.is] carrying \icon[s_store][bicon(s_store)] [s_store.gender==PLURAL?"some":"a"] [(s_store.blood_color != "#030303") ? "blood" : "oil"]-stained [s_store.name] on [T.his] [wear_suit.name]!" + else + msg += "[T.He] [T.is] carrying \icon[s_store][bicon(s_store)] \a [s_store] on [T.his] [wear_suit.name]." + + //back + if(back && !(skip_gear & EXAMINE_SKIPBACKPACK) && back.show_examine) + if(back.blood_DNA) + msg += "[T.He] [T.has] \icon[back][bicon(back)] [back.gender==PLURAL?"some":"a"] [(back.blood_color != "#030303") ? "blood" : "oil"]-stained [back] on [T.his] back." + else + msg += "[T.He] [T.has] \icon[back][bicon(back)] \a [back] on [T.his] back." + + //left hand + if(l_hand && l_hand.show_examine) + if(l_hand.blood_DNA) + msg += "[T.He] [T.is] holding \icon[l_hand][bicon(l_hand)] [l_hand.gender==PLURAL?"some":"a"] [(l_hand.blood_color != "#030303") ? "blood" : "oil"]-stained [l_hand.name] in [T.his] left hand!" + else + msg += "[T.He] [T.is] holding \icon[l_hand][bicon(l_hand)] \a [l_hand] in [T.his] left hand." + + //right hand + if(r_hand && r_hand.show_examine) + if(r_hand.blood_DNA) + msg += "[T.He] [T.is] holding \icon[r_hand][bicon(r_hand)] [r_hand.gender==PLURAL?"some":"a"] [(r_hand.blood_color != "#030303") ? "blood" : "oil"]-stained [r_hand.name] in [T.his] right hand!" + else + msg += "[T.He] [T.is] holding \icon[r_hand][bicon(r_hand)] \a [r_hand] in [T.his] right hand." + + //gloves + if(gloves && !(skip_gear & EXAMINE_SKIPGLOVES) && gloves.show_examine) + if(gloves.blood_DNA) + msg += "[T.He] [T.has] \icon[gloves][bicon(gloves)] [gloves.gender==PLURAL?"some":"a"] [(gloves.blood_color != "#030303") ? "blood" : "oil"]-stained [gloves.name] on [T.his] hands!" + else + msg += "[T.He] [T.has] \icon[gloves][bicon(gloves)] \a [gloves] on [T.his] hands." + else if(blood_DNA && !(skip_body & EXAMINE_SKIPHANDS)) + msg += "[T.He] [T.has] [(hand_blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained hands!" + + //handcuffed? + if(handcuffed && handcuffed.show_examine) + if(istype(handcuffed, /obj/item/weapon/handcuffs/cable)) + msg += "[T.He] [T.is] \icon[handcuffed][bicon(handcuffed)] restrained with cable!" + else + msg += "[T.He] [T.is] \icon[handcuffed][bicon(handcuffed)] handcuffed!" + + //buckled + if(buckled) + msg += "[T.He] [T.is] \icon[buckled][bicon(buckled)] buckled to [buckled]!" + + //belt + if(belt && !(skip_gear & EXAMINE_SKIPBELT) && belt.show_examine) + if(belt.blood_DNA) + msg += "[T.He] [T.has] \icon[belt][bicon(belt)] [belt.gender==PLURAL?"some":"a"] [(belt.blood_color != "#030303") ? "blood" : "oil"]-stained [belt.name] about [T.his] waist!" + else + msg += "[T.He] [T.has] \icon[belt][bicon(belt)] \a [belt] about [T.his] waist." + + //shoes + if(shoes && !(skip_gear & EXAMINE_SKIPSHOES) && shoes.show_examine) + if(shoes.blood_DNA) + msg += "[T.He] [T.is] wearing \icon[shoes][bicon(shoes)] [shoes.gender==PLURAL?"some":"a"] [(shoes.blood_color != "#030303") ? "blood" : "oil"]-stained [shoes.name] on [T.his] feet!" + else + msg += "[T.He] [T.is] wearing \icon[shoes][bicon(shoes)] \a [shoes] on [T.his] feet." + else if(feet_blood_DNA && !(skip_body & EXAMINE_SKIPHANDS)) + msg += "[T.He] [T.has] [(feet_blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained feet!" + + //mask + if(wear_mask && !(skip_gear & EXAMINE_SKIPMASK) && wear_mask.show_examine) + var/descriptor = "on [T.his] face" + if(istype(wear_mask, /obj/item/weapon/grenade) && check_has_mouth()) + descriptor = "in [T.his] mouth" + + if(wear_mask.blood_DNA) + msg += "[T.He] [T.has] \icon[wear_mask][bicon(wear_mask)] [wear_mask.gender==PLURAL?"some":"a"] [(wear_mask.blood_color != "#030303") ? "blood" : "oil"]-stained [wear_mask.name] [descriptor]!" + else + msg += "[T.He] [T.has] \icon[wear_mask][bicon(wear_mask)] \a [wear_mask] [descriptor]." + + //eyes + if(glasses && !(skip_gear & EXAMINE_SKIPEYEWEAR) && glasses.show_examine) + if(glasses.blood_DNA) + msg += "[T.He] [T.has] \icon[glasses][bicon(glasses)] [glasses.gender==PLURAL?"some":"a"] [(glasses.blood_color != "#030303") ? "blood" : "oil"]-stained [glasses] covering [T.his] eyes!" + else + msg += "[T.He] [T.has] \icon[glasses][bicon(glasses)] \a [glasses] covering [T.his] eyes." + + //left ear + if(l_ear && !(skip_gear & EXAMINE_SKIPEARS) && l_ear.show_examine) + msg += "[T.He] [T.has] \icon[l_ear][bicon(l_ear)] \a [l_ear] on [T.his] left ear." + + //right ear + if(r_ear && !(skip_gear & EXAMINE_SKIPEARS) && r_ear.show_examine) + msg += "[T.He] [T.has] \icon[r_ear][bicon(r_ear)] \a [r_ear] on [T.his] right ear." + + //ID + if(wear_id && wear_id.show_examine) + msg += "[T.He] [T.is] wearing \icon[wear_id][bicon(wear_id)]\a [wear_id]." + + //Jitters + if(is_jittery) + if(jitteriness >= 300) + msg += "[T.He] [T.is] convulsing violently!" + else if(jitteriness >= 200) + msg += "[T.He] [T.is] extremely jittery." + else if(jitteriness >= 100) + msg += "[T.He] [T.is] twitching ever so slightly." + + //splints + for(var/organ in BP_ALL) + var/obj/item/organ/external/o = get_organ(organ) + if(o && o.splinted && o.splinted.loc == o) + msg += "[T.He] [T.has] \a [o.splinted] on [T.his] [o.name]!" + + if(suiciding) + msg += "[T.He] appears to have commited suicide... there is no hope of recovery." + + //VOREStation Add + var/list/vorestrings = list() + vorestrings += examine_weight() + vorestrings += examine_nutrition() + vorestrings += examine_bellies() + vorestrings += examine_pickup_size() + vorestrings += examine_step_size() + vorestrings += examine_nif() + vorestrings += examine_chimera() + for(var/entry in vorestrings) + if(entry == "" || entry == null) + vorestrings -= entry + msg += vorestrings + //VOREStation Add End + + if(mSmallsize in mutations) + msg += "[T.He] [T.is] very short!" + + if (src.stat) + msg += "[T.He] [T.is]n't responding to anything around [T.him] and seems to be asleep." + if((stat == 2 || src.losebreath) && get_dist(user, src) <= 3) + msg += "[T.He] [T.does] not appear to be breathing." + if(istype(user, /mob/living/carbon/human) && !user.stat && Adjacent(user)) + user.visible_message("[usr] checks [src]'s pulse.", "You check [src]'s pulse.") + spawn(15) + if(isobserver(user) || (Adjacent(user) && !user.stat)) // If you're a corpse then you can't exactly check their pulse, but ghosts can see anything + if(pulse == PULSE_NONE) + to_chat(user, "[T.He] [T.has] no pulse[src.client ? "" : " and [T.his] soul has departed"]...") + else + to_chat(user, "[T.He] [T.has] a pulse!") + + if(fire_stacks) + msg += "[T.He] [T.is] covered in some liquid." + if(on_fire) + msg += "[T.He] [T.is] on fire!." + + var/ssd_msg = species.get_ssd(src) + if(ssd_msg && (!should_have_organ("brain") || has_brain()) && stat != DEAD) + if(!key) + msg += "[T.He] [T.is] [ssd_msg]. It doesn't look like [T.he] [T.is] waking up anytime soon." + else if(!client) + msg += "[T.He] [T.is] [ssd_msg]." + //VOREStation Add Start + if(client && ((client.inactivity / 10) / 60 > 10)) //10 Minutes + msg += "\[Inactive for [round((client.inactivity/10)/60)] minutes\]" + else if(disconnect_time) + msg += "\[Disconnected/ghosted [round(((world.realtime - disconnect_time)/10)/60)] minutes ago\]" + //VOREStation Add End + + var/list/wound_flavor_text = list() + var/list/is_bleeding = list() + var/applying_pressure = "" + + for(var/organ_tag in species.has_limbs) + + var/list/organ_data = species.has_limbs[organ_tag] + var/organ_descriptor = organ_data["descriptor"] + + var/obj/item/organ/external/E = organs_by_name[organ_tag] + if(!E) + wound_flavor_text["[organ_descriptor]"] = "[T.He] [T.is] missing [T.his] [organ_descriptor]." + else if(E.is_stump()) + wound_flavor_text["[organ_descriptor]"] = "[T.He] [T.has] a stump where [T.his] [organ_descriptor] should be." + else + continue + + for(var/obj/item/organ/external/temp in organs) + if(temp) + if((temp.organ_tag in hidden) && hidden[temp.organ_tag]) + continue //Organ is hidden, don't talk about it + if(temp.status & ORGAN_DESTROYED) + wound_flavor_text["[temp.name]"] = "[T.He] [T.is] missing [T.his] [temp.name]." + continue + + if(!looks_synth && temp.robotic == ORGAN_ROBOT) + if(!(temp.brute_dam + temp.burn_dam)) + wound_flavor_text["[temp.name]"] = "[T.He] [T.has] a [temp.name]." + else + wound_flavor_text["[temp.name]"] = "[T.He] [T.has] a [temp.name] with [temp.get_wounds_desc()]!" + continue + else if(temp.wounds.len > 0 || temp.open) + if(temp.is_stump() && temp.parent_organ && organs_by_name[temp.parent_organ]) + var/obj/item/organ/external/parent = organs_by_name[temp.parent_organ] + wound_flavor_text["[temp.name]"] = "[T.He] [T.has] [temp.get_wounds_desc()] on [T.his] [parent.name]." + else + wound_flavor_text["[temp.name]"] = "[T.He] [T.has] [temp.get_wounds_desc()] on [T.his] [temp.name]." + else + wound_flavor_text["[temp.name]"] = "" + if(temp.dislocated == 1) //VOREStation Edit Bugfix + wound_flavor_text["[temp.name]"] += "[T.His] [temp.joint] is dislocated!" + if(temp.brute_dam > temp.min_broken_damage || (temp.status & (ORGAN_BROKEN | ORGAN_MUTATED))) + wound_flavor_text["[temp.name]"] += "[T.His] [temp.name] is dented and swollen!" + + if(temp.germ_level > INFECTION_LEVEL_TWO && !(temp.status & ORGAN_DEAD)) + wound_flavor_text["[temp.name]"] += "[T.His] [temp.name] looks very infected!" + else if(temp.status & ORGAN_DEAD) + wound_flavor_text["[temp.name]"] += "[T.His] [temp.name] looks rotten!" + + if(temp.status & ORGAN_BLEEDING) + is_bleeding["[temp.name]"] += "[T.His] [temp.name] is bleeding!" + + if(temp.applied_pressure == src) + applying_pressure = "[T.He] is applying pressure to [T.his] [temp.name]." + + for(var/limb in wound_flavor_text) + var/flavor = wound_flavor_text[limb] + if(flavor) + msg += flavor + for(var/limb in is_bleeding) + var/blood = is_bleeding[limb] + if(blood) + msg += blood + for(var/implant in get_visible_implants(0)) + msg += "[src] [T.has] \a [implant] sticking out of [T.his] flesh!" + if(digitalcamo) + msg += "[T.He] [T.is] repulsively uncanny!" + + if(hasHUD(user,"security")) + var/perpname = name + var/criminal = "None" + + if(wear_id) + if(istype(wear_id, /obj/item/weapon/card/id)) + var/obj/item/weapon/card/id/I = wear_id + perpname = I.registered_name + else if(istype(wear_id, /obj/item/device/pda)) + var/obj/item/device/pda/P = wear_id + perpname = P.owner + + for (var/datum/data/record/R in data_core.security) + if(R.fields["name"] == perpname) + criminal = R.fields["criminal"] + + msg += "Criminal status: \[[criminal]\]" + msg += "Security records: \[View\] \[Add comment\]" + + if(hasHUD(user,"medical")) + var/perpname = name + var/medical = "None" + + if(wear_id) + if(istype(wear_id, /obj/item/weapon/card/id)) + var/obj/item/weapon/card/id/I = wear_id + perpname = I.registered_name + else if(istype(wear_id, /obj/item/device/pda)) + var/obj/item/device/pda/P = wear_id + perpname = P.owner + + for (var/datum/data/record/R in data_core.medical) + if (R.fields["name"] == perpname) + medical = R.fields["p_stat"] + + msg += "Physical status: \[[medical]\]" + msg += "Medical records: \[View\] \[Add comment\]" + + if(hasHUD(user,"best")) + msg += "Employment records: \[View\] \[Add comment\]" + + + var/flavor_text = print_flavor_text() + if(flavor_text) + msg += "[flavor_text]" + + // VOREStation Start + if(custom_link) + msg += "Custom link: [custom_link]" + + if(ooc_notes) + msg += "OOC Notes: \[View\] - \[Print\]" + msg += "\[Mechanical Vore Preferences\]" + // VOREStation End + msg += "*---------*" + if(applying_pressure) + msg += applying_pressure + + var/show_descs = show_descriptors_to(user) + if(show_descs) + msg += "[jointext(show_descs, "
                    ")]
                    " + + if(pose) + if(!findtext(pose, regex("\[.?!]$"))) // Will be zero if the last character is not a member of [.?!] + pose = addtext(pose,".") //Makes sure all emotes end with a period. + msg += "
                    [T.He] [pose]" //
                    intentional, extra gap. + + return msg + +//Helper procedure. Called by /mob/living/carbon/human/examine() and /mob/living/carbon/human/Topic() to determine HUD access to security and medical records. +/proc/hasHUD(mob/M as mob, hudtype) + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + if(hasHUD_vr(H,hudtype)) return 1 //VOREStation Add - Added records access for certain modes of omni-hud glasses + switch(hudtype) + if("security") + return istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(H.glasses, /obj/item/clothing/glasses/sunglasses/sechud) + if("medical") + return istype(H.glasses, /obj/item/clothing/glasses/hud/health) + else if(istype(M, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = M + return R.sensor_type //VOREStation Add - Borgo sensors are now binary so just have them on or off diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index 0efe5f174b4..333b314fac3 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -1,594 +1,594 @@ -//Updates the mob's health from organs and mob damage variables -/mob/living/carbon/human/updatehealth() - var/huskmodifier = 2.5 //VOREStation Edit // With 1.5, you need 250 burn instead of 200 to husk a human. - - if(status_flags & GODMODE) - health = getMaxHealth() - set_stat(CONSCIOUS) - return - - var/total_burn = 0 - var/total_brute = 0 - for(var/obj/item/organ/external/O in organs) //hardcoded to streamline things a bit - if((O.robotic >= ORGAN_ROBOT) && !O.vital) - continue //*non-vital* robot limbs don't count towards shock and crit - total_brute += O.brute_dam - total_burn += O.burn_dam - - health = getMaxHealth() - getOxyLoss() - getToxLoss() - getCloneLoss() - total_burn - total_brute - - //TODO: fix husking - if( ((getMaxHealth() - total_burn) < config.health_threshold_dead * huskmodifier) && stat == DEAD) - ChangeToHusk() - return - -/mob/living/carbon/human/adjustBrainLoss(var/amount) - - if(status_flags & GODMODE) return 0 //godmode - - if(should_have_organ("brain")) - var/obj/item/organ/internal/brain/sponge = internal_organs_by_name["brain"] - if(sponge) - sponge.take_damage(amount) - brainloss = sponge.damage - else - brainloss = 200 - else - brainloss = 0 - -/mob/living/carbon/human/setBrainLoss(var/amount) - - if(status_flags & GODMODE) return 0 //godmode - - if(should_have_organ("brain")) - var/obj/item/organ/internal/brain/sponge = internal_organs_by_name["brain"] - if(sponge) - sponge.damage = min(max(amount, 0),(getMaxHealth()*2)) - brainloss = sponge.damage - else - brainloss = 200 - else - brainloss = 0 - -/mob/living/carbon/human/getBrainLoss() - - if(status_flags & GODMODE) return 0 //godmode - - if(should_have_organ("brain")) - var/obj/item/organ/internal/brain/sponge = internal_organs_by_name["brain"] - if(sponge) - brainloss = min(sponge.damage,getMaxHealth()*2) - else - brainloss = 200 - else - brainloss = 0 - return brainloss - -//These procs fetch a cumulative total damage from all organs -/mob/living/carbon/human/getBruteLoss() - var/amount = 0 - for(var/obj/item/organ/external/O in organs) - if(O.robotic >= ORGAN_ROBOT && !O.vital) - continue //*non-vital*robot limbs don't count towards death, or show up when scanned - amount += O.brute_dam - return amount - -/mob/living/carbon/human/getShockBruteLoss() - var/amount = 0 - for(var/obj/item/organ/external/O in organs) - if(O.robotic >= ORGAN_ROBOT) - continue //robot limbs don't count towards shock and crit - amount += O.brute_dam - return amount - -/mob/living/carbon/human/getActualBruteLoss() - var/amount = 0 - for(var/obj/item/organ/external/O in organs) // Unlike the above, robolimbs DO count. - amount += O.brute_dam - return amount - -/mob/living/carbon/human/getFireLoss() - var/amount = 0 - for(var/obj/item/organ/external/O in organs) - if(O.robotic >= ORGAN_ROBOT && !O.vital) - continue //*non-vital*robot limbs don't count towards death, or show up when scanned - amount += O.burn_dam - return amount - -/mob/living/carbon/human/getShockFireLoss() - var/amount = 0 - for(var/obj/item/organ/external/O in organs) - if(O.robotic >= ORGAN_ROBOT) - continue //robot limbs don't count towards shock and crit - amount += O.burn_dam - return amount - -/mob/living/carbon/human/getActualFireLoss() - var/amount = 0 - for(var/obj/item/organ/external/O in organs) // Unlike the above, robolimbs DO count. - amount += O.burn_dam - return amount - -//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs -/mob/living/carbon/human/adjustBruteLoss(var/amount,var/include_robo) - amount = amount*species.brute_mod - if(amount > 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_damage_percent - if(!isnull(M.incoming_brute_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_brute_damage_percent - if(nif && nif.flag_check(NIF_C_BRUTEARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage - take_overall_damage(amount, 0) - else - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_healing_percent)) - amount *= M.incoming_healing_percent - heal_overall_damage(-amount, 0, include_robo) - BITSET(hud_updateflag, HEALTH_HUD) - -//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs -/mob/living/carbon/human/adjustFireLoss(var/amount,var/include_robo) - amount = amount*species.burn_mod - if(amount > 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_damage_percent - if(!isnull(M.incoming_fire_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_fire_damage_percent - if(nif && nif.flag_check(NIF_C_BURNARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage - take_overall_damage(0, amount) - else - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_healing_percent)) - amount *= M.incoming_healing_percent - heal_overall_damage(0, -amount, include_robo) - BITSET(hud_updateflag, HEALTH_HUD) - -/mob/living/carbon/human/proc/adjustBruteLossByPart(var/amount, var/organ_name, var/obj/damage_source = null) - amount = amount*species.brute_mod - if (organ_name in organs_by_name) - var/obj/item/organ/external/O = get_organ(organ_name) - - if(amount > 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_damage_percent - if(!isnull(M.incoming_brute_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_brute_damage_percent - if(nif && nif.flag_check(NIF_C_BRUTEARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage - O.take_damage(amount, 0, sharp=is_sharp(damage_source), edge=has_edge(damage_source), used_weapon=damage_source) - else - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_healing_percent)) - amount *= M.incoming_healing_percent - //if you don't want to heal robot organs, they you will have to check that yourself before using this proc. - O.heal_damage(-amount, 0, internal=0, robo_repair=(O.robotic >= ORGAN_ROBOT)) - - BITSET(hud_updateflag, HEALTH_HUD) - -/mob/living/carbon/human/proc/adjustFireLossByPart(var/amount, var/organ_name, var/obj/damage_source = null) - amount = amount*species.burn_mod - if (organ_name in organs_by_name) - var/obj/item/organ/external/O = get_organ(organ_name) - - if(amount > 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_damage_percent - if(!isnull(M.incoming_fire_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_fire_damage_percent - if(nif && nif.flag_check(NIF_C_BURNARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage - O.take_damage(0, amount, sharp=is_sharp(damage_source), edge=has_edge(damage_source), used_weapon=damage_source) - else - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_healing_percent)) - amount *= M.incoming_healing_percent - //if you don't want to heal robot organs, they you will have to check that yourself before using this proc. - O.heal_damage(0, -amount, internal=0, robo_repair=(O.robotic >= ORGAN_ROBOT)) - - BITSET(hud_updateflag, HEALTH_HUD) - -/mob/living/carbon/human/Stun(amount) - if(HULK in mutations) return - ..() - -/mob/living/carbon/human/Weaken(amount) - if(HULK in mutations) return - ..() - -/mob/living/carbon/human/Paralyse(amount) - if(HULK in mutations) return - // Notify our AI if they can now control the suit. - if(wearing_rig && !stat && paralysis < amount) //We are passing out right this second. - wearing_rig.notify_ai("Warning: user consciousness failure. Mobility control passed to integrated intelligence system.") - ..() - -/mob/living/carbon/human/proc/Stasis(amount) - if((species.flags & NO_SCAN) || isSynthetic()) - in_stasis = 0 - else - in_stasis = amount - -/mob/living/carbon/human/proc/getStasis() - if((species.flags & NO_SCAN) || isSynthetic()) - return 0 - - return in_stasis - -//This determines if, RIGHT NOW, the life() tick is being skipped due to stasis -/mob/living/carbon/human/proc/inStasisNow() - var/stasisValue = getStasis() - if(stasisValue && (life_tick % stasisValue)) - return 1 - - return 0 - -/mob/living/carbon/human/getCloneLoss() - if((species.flags & NO_SCAN) || isSynthetic()) - cloneloss = 0 - return ..() - -/mob/living/carbon/human/setCloneLoss(var/amount) - if((species.flags & NO_SCAN) || isSynthetic()) - cloneloss = 0 - else - ..() - -/mob/living/carbon/human/adjustCloneLoss(var/amount) - ..() - - if((species.flags & NO_SCAN) || isSynthetic()) - cloneloss = 0 - return - - var/heal_prob = max(0, 80 - getCloneLoss()) - var/mut_prob = min(80, getCloneLoss()+10) - if (amount > 0) - if (prob(mut_prob)) - var/list/obj/item/organ/external/candidates = list() - for (var/obj/item/organ/external/O in organs) - if(!(O.status & ORGAN_MUTATED)) - candidates |= O - if (candidates.len) - var/obj/item/organ/external/O = pick(candidates) - O.mutate() - to_chat(src, "Something is not right with your [O.name]...") - return - else - if (prob(heal_prob)) - for (var/obj/item/organ/external/O in organs) - if (O.status & ORGAN_MUTATED) - O.unmutate() - to_chat(src, "Your [O.name] is shaped normally again.") - return - - if (getCloneLoss() < 1) - for (var/obj/item/organ/external/O in organs) - if (O.status & ORGAN_MUTATED) - O.unmutate() - to_chat(src, "Your [O.name] is shaped normally again.") - BITSET(hud_updateflag, HEALTH_HUD) - -// Defined here solely to take species flags into account without having to recast at mob/living level. -/mob/living/carbon/human/getOxyLoss() - if(!should_have_organ(O_LUNGS)) - oxyloss = 0 - return ..() - -/mob/living/carbon/human/adjustOxyLoss(var/amount) - if(!should_have_organ(O_LUNGS)) - oxyloss = 0 - else - amount = amount*species.oxy_mod - ..(amount) - -/mob/living/carbon/human/setOxyLoss(var/amount) - if(!should_have_organ(O_LUNGS)) - oxyloss = 0 - else - ..() - -/mob/living/carbon/human/adjustHalLoss(var/amount) - if(species.flags & NO_PAIN) - halloss = 0 - else - if(amount > 0) //only multiply it by the mod if it's positive, or else it takes longer to fade too! - amount = amount*species.pain_mod - ..(amount) - -/mob/living/carbon/human/setHalLoss(var/amount) - if(species.flags & NO_PAIN) - halloss = 0 - else - ..() - -/mob/living/carbon/human/getToxLoss() - if(species.flags & NO_POISON) - toxloss = 0 - return ..() - -/mob/living/carbon/human/adjustToxLoss(var/amount) - if(species.flags & NO_POISON) - toxloss = 0 - else - amount = amount*species.toxins_mod - ..(amount) - -/mob/living/carbon/human/setToxLoss(var/amount) - if(species.flags & NO_POISON) - toxloss = 0 - else - ..() - -//////////////////////////////////////////// - -//Returns a list of damaged organs -/mob/living/carbon/human/proc/get_damaged_organs(var/brute, var/burn) - var/list/obj/item/organ/external/parts = list() - for(var/obj/item/organ/external/O in organs) - if((brute && O.brute_dam) || (burn && O.burn_dam)) - parts += O - return parts - -//Returns a list of damageable organs -/mob/living/carbon/human/proc/get_damageable_organs() - var/list/obj/item/organ/external/parts = list() - for(var/obj/item/organ/external/O in organs) - if(O.is_damageable()) - parts += O - return parts - -//Heals ONE external organ, organ gets randomly selected from damaged ones. -//It automatically updates damage overlays if necesary -//It automatically updates health status -/mob/living/carbon/human/heal_organ_damage(var/brute, var/burn) - var/list/obj/item/organ/external/parts = get_damaged_organs(brute,burn) - if(!parts.len) return - var/obj/item/organ/external/picked = pick(parts) - if(picked.heal_damage(brute,burn)) - UpdateDamageIcon() - BITSET(hud_updateflag, HEALTH_HUD) - updatehealth() - - -/* -In most cases it makes more sense to use apply_damage() instead! And make sure to check armour if applicable. -*/ -//Damages ONE external organ, organ gets randomly selected from damagable ones. -//It automatically updates damage overlays if necesary -//It automatically updates health status -/mob/living/carbon/human/take_organ_damage(var/brute, var/burn, var/sharp = FALSE, var/edge = FALSE) - var/list/obj/item/organ/external/parts = get_damageable_organs() - if(!parts.len) return - var/obj/item/organ/external/picked = pick(parts) - if(picked.take_damage(brute,burn,sharp,edge)) - UpdateDamageIcon() - BITSET(hud_updateflag, HEALTH_HUD) - updatehealth() - - -//Heal MANY external organs, in random order -//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs -/mob/living/carbon/human/heal_overall_damage(var/brute, var/burn, var/include_robo) - var/list/obj/item/organ/external/parts = get_damaged_organs(brute,burn) - - var/update = 0 - while(parts.len && (brute>0 || burn>0) ) - var/obj/item/organ/external/picked = pick(parts) - - var/brute_was = picked.brute_dam - var/burn_was = picked.burn_dam - - update |= picked.heal_damage(brute,burn,robo_repair = include_robo) - - brute -= (brute_was-picked.brute_dam) - burn -= (burn_was-picked.burn_dam) - - parts -= picked - updatehealth() - BITSET(hud_updateflag, HEALTH_HUD) - if(update) UpdateDamageIcon() - -// damage MANY external organs, in random order -/mob/living/carbon/human/take_overall_damage(var/brute, var/burn, var/sharp = FALSE, var/edge = FALSE, var/used_weapon = null) - if(status_flags & GODMODE) return //godmode - var/list/obj/item/organ/external/parts = get_damageable_organs() - var/update = 0 - while(parts.len && (brute>0 || burn>0) ) - var/obj/item/organ/external/picked = pick(parts) - - var/brute_was = picked.brute_dam - var/burn_was = picked.burn_dam - - update |= picked.take_damage(brute,burn,sharp,edge,used_weapon) - brute -= (picked.brute_dam - brute_was) - burn -= (picked.burn_dam - burn_was) - - parts -= picked - updatehealth() - BITSET(hud_updateflag, HEALTH_HUD) - if(update) UpdateDamageIcon() - - -//////////////////////////////////////////// - -/* -This function restores the subjects blood to max. -*/ -/mob/living/carbon/human/proc/restore_blood() - if(!should_have_organ(O_HEART)) - return - if(vessel.total_volume < species.blood_volume) - vessel.add_reagent("blood", species.blood_volume - vessel.total_volume) - -/* -This function restores all organs. -*/ -/mob/living/carbon/human/restore_all_organs(var/ignore_prosthetic_prefs) - for(var/obj/item/organ/external/current_organ in organs) - current_organ.rejuvenate(ignore_prosthetic_prefs) - -/mob/living/carbon/human/proc/HealDamage(zone, brute, burn) - var/obj/item/organ/external/E = get_organ(zone) - if(istype(E, /obj/item/organ/external)) - if (E.heal_damage(brute, burn)) - UpdateDamageIcon() - BITSET(hud_updateflag, HEALTH_HUD) - else - return 0 - return - -/* -/mob/living/carbon/human/proc/get_organ(var/zone) - if(!zone) - zone = BP_TORSO - else if (zone in list( O_EYES, O_MOUTH )) - zone = BP_HEAD - return organs_by_name[zone] -*/ - -/mob/living/carbon/human/apply_damage(var/damage = 0, var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/sharp = FALSE, var/edge = FALSE, var/obj/used_weapon = null) - if(Debug2) - to_world_log("## DEBUG: human/apply_damage() was called on [src], with [damage] damage, an armor value of [blocked], and a soak value of [soaked].") - - var/obj/item/organ/external/organ = null - if(isorgan(def_zone)) - organ = def_zone - else - if(!def_zone) def_zone = ran_zone(def_zone) - organ = get_organ(check_zone(def_zone)) - - for(var/datum/modifier/M in modifiers) //MODIFIER STUFF. It's best to do this RIGHT before armor is calculated, so it's done here! This is the 'forcefield' defence. - if(damagetype == BRUTE && (!isnull(M.effective_brute_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_brute_resistance - continue - if((damagetype == BURN || damagetype == ELECTROCUTE) && (!isnull(M.effective_fire_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_fire_resistance - continue - if(damagetype == TOX && (!isnull(M.effective_tox_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_tox_resistance - continue - if(damagetype == OXY && (!isnull(M.effective_oxy_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_oxy_resistance - continue - if(damagetype == CLONE && (!isnull(M.effective_clone_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_clone_resistance - continue - if(damagetype == HALLOSS && (!isnull(M.effective_hal_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_hal_resistance - continue - if(damagetype == SEARING && (!isnull(M.effective_fire_resistance) || !isnull(M.effective_brute_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - var/damage_mitigation = 0//Used for dual calculations. - if(!isnull(M.effective_fire_resistance)) - damage_mitigation += round((1/3)*damage * M.effective_fire_resistance) - if(!isnull(M.effective_brute_resistance)) - damage_mitigation += round((2/3)*damage * M.effective_brute_resistance) - damage -= damage_mitigation - continue - if(damagetype == BIOACID && (isSynthetic() && (!isnull(M.effective_fire_resistance))) || (!isSynthetic() && M.effective_tox_resistance)) - if(isSynthetic()) - damage = damage * M.effective_fire_resistance - else - damage = damage * M.effective_tox_resistance - continue - //Handle other types of damage - if((damagetype != BRUTE) && (damagetype != BURN)) - if(damagetype == HALLOSS) - if((damage > 25 && prob(20)) || (damage > 50 && prob(60))) - if(organ && organ.organ_can_feel_pain() && !isbelly(loc) && !istype(loc, /obj/item/device/dogborg/sleeper)) //VOREStation Add - emote("scream") - ..(damage, damagetype, def_zone, blocked, soaked) - return 1 - - //Handle BRUTE and BURN damage - handle_suit_punctures(damagetype, damage, def_zone) - - if(blocked >= 100) - return 0 - - if(soaked >= damage) - return 0 - - if(!organ) return 0 - - if(blocked) - blocked = (100-blocked)/100 - damage = (damage * blocked) - - if(soaked) - damage -= soaked - - if(Debug2) - to_world_log("## DEBUG: [src] was hit for [damage].") - - switch(damagetype) - if(BRUTE) - damageoverlaytemp = 20 - if(nif && nif.flag_check(NIF_C_BRUTEARMOR,NIF_FLAGS_COMBAT)){damage *= 0.7} - damage = damage*species.brute_mod - - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*damage) - damage *= M.incoming_damage_percent - if(!isnull(M.incoming_brute_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*damage) - damage *= M.incoming_brute_damage_percent - - if(organ.take_damage(damage, 0, sharp, edge, used_weapon)) - UpdateDamageIcon() - if(BURN) - damageoverlaytemp = 20 - if(nif && nif.flag_check(NIF_C_BURNARMOR,NIF_FLAGS_COMBAT)){damage *= 0.7} - damage = damage*species.burn_mod - - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*damage) - damage *= M.incoming_damage_percent - if(!isnull(M.incoming_brute_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*damage) - damage *= M.incoming_fire_damage_percent - - if(organ.take_damage(0, damage, sharp, edge, used_weapon)) - UpdateDamageIcon() - - // Will set our damageoverlay icon to the next level, which will then be set back to the normal level the next mob.Life(). - updatehealth() - BITSET(hud_updateflag, HEALTH_HUD) - return 1 +//Updates the mob's health from organs and mob damage variables +/mob/living/carbon/human/updatehealth() + var/huskmodifier = 2.5 //VOREStation Edit // With 1.5, you need 250 burn instead of 200 to husk a human. + + if(status_flags & GODMODE) + health = getMaxHealth() + set_stat(CONSCIOUS) + return + + var/total_burn = 0 + var/total_brute = 0 + for(var/obj/item/organ/external/O in organs) //hardcoded to streamline things a bit + if((O.robotic >= ORGAN_ROBOT) && !O.vital) + continue //*non-vital* robot limbs don't count towards shock and crit + total_brute += O.brute_dam + total_burn += O.burn_dam + + health = getMaxHealth() - getOxyLoss() - getToxLoss() - getCloneLoss() - total_burn - total_brute + + //TODO: fix husking + if( ((getMaxHealth() - total_burn) < config.health_threshold_dead * huskmodifier) && stat == DEAD) + ChangeToHusk() + return + +/mob/living/carbon/human/adjustBrainLoss(var/amount) + + if(status_flags & GODMODE) return 0 //godmode + + if(should_have_organ("brain")) + var/obj/item/organ/internal/brain/sponge = internal_organs_by_name["brain"] + if(sponge) + sponge.take_damage(amount) + brainloss = sponge.damage + else + brainloss = 200 + else + brainloss = 0 + +/mob/living/carbon/human/setBrainLoss(var/amount) + + if(status_flags & GODMODE) return 0 //godmode + + if(should_have_organ("brain")) + var/obj/item/organ/internal/brain/sponge = internal_organs_by_name["brain"] + if(sponge) + sponge.damage = min(max(amount, 0),(getMaxHealth()*2)) + brainloss = sponge.damage + else + brainloss = 200 + else + brainloss = 0 + +/mob/living/carbon/human/getBrainLoss() + + if(status_flags & GODMODE) return 0 //godmode + + if(should_have_organ("brain")) + var/obj/item/organ/internal/brain/sponge = internal_organs_by_name["brain"] + if(sponge) + brainloss = min(sponge.damage,getMaxHealth()*2) + else + brainloss = 200 + else + brainloss = 0 + return brainloss + +//These procs fetch a cumulative total damage from all organs +/mob/living/carbon/human/getBruteLoss() + var/amount = 0 + for(var/obj/item/organ/external/O in organs) + if(O.robotic >= ORGAN_ROBOT && !O.vital) + continue //*non-vital*robot limbs don't count towards death, or show up when scanned + amount += O.brute_dam + return amount + +/mob/living/carbon/human/getShockBruteLoss() + var/amount = 0 + for(var/obj/item/organ/external/O in organs) + if(O.robotic >= ORGAN_ROBOT) + continue //robot limbs don't count towards shock and crit + amount += O.brute_dam + return amount + +/mob/living/carbon/human/getActualBruteLoss() + var/amount = 0 + for(var/obj/item/organ/external/O in organs) // Unlike the above, robolimbs DO count. + amount += O.brute_dam + return amount + +/mob/living/carbon/human/getFireLoss() + var/amount = 0 + for(var/obj/item/organ/external/O in organs) + if(O.robotic >= ORGAN_ROBOT && !O.vital) + continue //*non-vital*robot limbs don't count towards death, or show up when scanned + amount += O.burn_dam + return amount + +/mob/living/carbon/human/getShockFireLoss() + var/amount = 0 + for(var/obj/item/organ/external/O in organs) + if(O.robotic >= ORGAN_ROBOT) + continue //robot limbs don't count towards shock and crit + amount += O.burn_dam + return amount + +/mob/living/carbon/human/getActualFireLoss() + var/amount = 0 + for(var/obj/item/organ/external/O in organs) // Unlike the above, robolimbs DO count. + amount += O.burn_dam + return amount + +//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs +/mob/living/carbon/human/adjustBruteLoss(var/amount,var/include_robo) + amount = amount*species.brute_mod + if(amount > 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_damage_percent + if(!isnull(M.incoming_brute_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_brute_damage_percent + if(nif && nif.flag_check(NIF_C_BRUTEARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage + take_overall_damage(amount, 0) + else + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_healing_percent)) + amount *= M.incoming_healing_percent + heal_overall_damage(-amount, 0, include_robo) + BITSET(hud_updateflag, HEALTH_HUD) + +//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs +/mob/living/carbon/human/adjustFireLoss(var/amount,var/include_robo) + amount = amount*species.burn_mod + if(amount > 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_damage_percent + if(!isnull(M.incoming_fire_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_fire_damage_percent + if(nif && nif.flag_check(NIF_C_BURNARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage + take_overall_damage(0, amount) + else + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_healing_percent)) + amount *= M.incoming_healing_percent + heal_overall_damage(0, -amount, include_robo) + BITSET(hud_updateflag, HEALTH_HUD) + +/mob/living/carbon/human/proc/adjustBruteLossByPart(var/amount, var/organ_name, var/obj/damage_source = null) + amount = amount*species.brute_mod + if (organ_name in organs_by_name) + var/obj/item/organ/external/O = get_organ(organ_name) + + if(amount > 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_damage_percent + if(!isnull(M.incoming_brute_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_brute_damage_percent + if(nif && nif.flag_check(NIF_C_BRUTEARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage + O.take_damage(amount, 0, sharp=is_sharp(damage_source), edge=has_edge(damage_source), used_weapon=damage_source) + else + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_healing_percent)) + amount *= M.incoming_healing_percent + //if you don't want to heal robot organs, they you will have to check that yourself before using this proc. + O.heal_damage(-amount, 0, internal=0, robo_repair=(O.robotic >= ORGAN_ROBOT)) + + BITSET(hud_updateflag, HEALTH_HUD) + +/mob/living/carbon/human/proc/adjustFireLossByPart(var/amount, var/organ_name, var/obj/damage_source = null) + amount = amount*species.burn_mod + if (organ_name in organs_by_name) + var/obj/item/organ/external/O = get_organ(organ_name) + + if(amount > 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_damage_percent + if(!isnull(M.incoming_fire_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_fire_damage_percent + if(nif && nif.flag_check(NIF_C_BURNARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage + O.take_damage(0, amount, sharp=is_sharp(damage_source), edge=has_edge(damage_source), used_weapon=damage_source) + else + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_healing_percent)) + amount *= M.incoming_healing_percent + //if you don't want to heal robot organs, they you will have to check that yourself before using this proc. + O.heal_damage(0, -amount, internal=0, robo_repair=(O.robotic >= ORGAN_ROBOT)) + + BITSET(hud_updateflag, HEALTH_HUD) + +/mob/living/carbon/human/Stun(amount) + if(HULK in mutations) return + ..() + +/mob/living/carbon/human/Weaken(amount) + if(HULK in mutations) return + ..() + +/mob/living/carbon/human/Paralyse(amount) + if(HULK in mutations) return + // Notify our AI if they can now control the suit. + if(wearing_rig && !stat && paralysis < amount) //We are passing out right this second. + wearing_rig.notify_ai("Warning: user consciousness failure. Mobility control passed to integrated intelligence system.") + ..() + +/mob/living/carbon/human/proc/Stasis(amount) + if((species.flags & NO_SCAN) || isSynthetic()) + in_stasis = 0 + else + in_stasis = amount + +/mob/living/carbon/human/proc/getStasis() + if((species.flags & NO_SCAN) || isSynthetic()) + return 0 + + return in_stasis + +//This determines if, RIGHT NOW, the life() tick is being skipped due to stasis +/mob/living/carbon/human/proc/inStasisNow() + var/stasisValue = getStasis() + if(stasisValue && (life_tick % stasisValue)) + return 1 + + return 0 + +/mob/living/carbon/human/getCloneLoss() + if((species.flags & NO_SCAN) || isSynthetic()) + cloneloss = 0 + return ..() + +/mob/living/carbon/human/setCloneLoss(var/amount) + if((species.flags & NO_SCAN) || isSynthetic()) + cloneloss = 0 + else + ..() + +/mob/living/carbon/human/adjustCloneLoss(var/amount) + ..() + + if((species.flags & NO_SCAN) || isSynthetic()) + cloneloss = 0 + return + + var/heal_prob = max(0, 80 - getCloneLoss()) + var/mut_prob = min(80, getCloneLoss()+10) + if (amount > 0) + if (prob(mut_prob)) + var/list/obj/item/organ/external/candidates = list() + for (var/obj/item/organ/external/O in organs) + if(!(O.status & ORGAN_MUTATED)) + candidates |= O + if (candidates.len) + var/obj/item/organ/external/O = pick(candidates) + O.mutate() + to_chat(src, "Something is not right with your [O.name]...") + return + else + if (prob(heal_prob)) + for (var/obj/item/organ/external/O in organs) + if (O.status & ORGAN_MUTATED) + O.unmutate() + to_chat(src, "Your [O.name] is shaped normally again.") + return + + if (getCloneLoss() < 1) + for (var/obj/item/organ/external/O in organs) + if (O.status & ORGAN_MUTATED) + O.unmutate() + to_chat(src, "Your [O.name] is shaped normally again.") + BITSET(hud_updateflag, HEALTH_HUD) + +// Defined here solely to take species flags into account without having to recast at mob/living level. +/mob/living/carbon/human/getOxyLoss() + if(!should_have_organ(O_LUNGS)) + oxyloss = 0 + return ..() + +/mob/living/carbon/human/adjustOxyLoss(var/amount) + if(!should_have_organ(O_LUNGS)) + oxyloss = 0 + else + amount = amount*species.oxy_mod + ..(amount) + +/mob/living/carbon/human/setOxyLoss(var/amount) + if(!should_have_organ(O_LUNGS)) + oxyloss = 0 + else + ..() + +/mob/living/carbon/human/adjustHalLoss(var/amount) + if(species.flags & NO_PAIN) + halloss = 0 + else + if(amount > 0) //only multiply it by the mod if it's positive, or else it takes longer to fade too! + amount = amount*species.pain_mod + ..(amount) + +/mob/living/carbon/human/setHalLoss(var/amount) + if(species.flags & NO_PAIN) + halloss = 0 + else + ..() + +/mob/living/carbon/human/getToxLoss() + if(species.flags & NO_POISON) + toxloss = 0 + return ..() + +/mob/living/carbon/human/adjustToxLoss(var/amount) + if(species.flags & NO_POISON) + toxloss = 0 + else + amount = amount*species.toxins_mod + ..(amount) + +/mob/living/carbon/human/setToxLoss(var/amount) + if(species.flags & NO_POISON) + toxloss = 0 + else + ..() + +//////////////////////////////////////////// + +//Returns a list of damaged organs +/mob/living/carbon/human/proc/get_damaged_organs(var/brute, var/burn) + var/list/obj/item/organ/external/parts = list() + for(var/obj/item/organ/external/O in organs) + if((brute && O.brute_dam) || (burn && O.burn_dam)) + parts += O + return parts + +//Returns a list of damageable organs +/mob/living/carbon/human/proc/get_damageable_organs() + var/list/obj/item/organ/external/parts = list() + for(var/obj/item/organ/external/O in organs) + if(O.is_damageable()) + parts += O + return parts + +//Heals ONE external organ, organ gets randomly selected from damaged ones. +//It automatically updates damage overlays if necesary +//It automatically updates health status +/mob/living/carbon/human/heal_organ_damage(var/brute, var/burn) + var/list/obj/item/organ/external/parts = get_damaged_organs(brute,burn) + if(!parts.len) return + var/obj/item/organ/external/picked = pick(parts) + if(picked.heal_damage(brute,burn)) + UpdateDamageIcon() + BITSET(hud_updateflag, HEALTH_HUD) + updatehealth() + + +/* +In most cases it makes more sense to use apply_damage() instead! And make sure to check armour if applicable. +*/ +//Damages ONE external organ, organ gets randomly selected from damagable ones. +//It automatically updates damage overlays if necesary +//It automatically updates health status +/mob/living/carbon/human/take_organ_damage(var/brute, var/burn, var/sharp = FALSE, var/edge = FALSE) + var/list/obj/item/organ/external/parts = get_damageable_organs() + if(!parts.len) return + var/obj/item/organ/external/picked = pick(parts) + if(picked.take_damage(brute,burn,sharp,edge)) + UpdateDamageIcon() + BITSET(hud_updateflag, HEALTH_HUD) + updatehealth() + + +//Heal MANY external organs, in random order +//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs +/mob/living/carbon/human/heal_overall_damage(var/brute, var/burn, var/include_robo) + var/list/obj/item/organ/external/parts = get_damaged_organs(brute,burn) + + var/update = 0 + while(parts.len && (brute>0 || burn>0) ) + var/obj/item/organ/external/picked = pick(parts) + + var/brute_was = picked.brute_dam + var/burn_was = picked.burn_dam + + update |= picked.heal_damage(brute,burn,robo_repair = include_robo) + + brute -= (brute_was-picked.brute_dam) + burn -= (burn_was-picked.burn_dam) + + parts -= picked + updatehealth() + BITSET(hud_updateflag, HEALTH_HUD) + if(update) UpdateDamageIcon() + +// damage MANY external organs, in random order +/mob/living/carbon/human/take_overall_damage(var/brute, var/burn, var/sharp = FALSE, var/edge = FALSE, var/used_weapon = null) + if(status_flags & GODMODE) return //godmode + var/list/obj/item/organ/external/parts = get_damageable_organs() + var/update = 0 + while(parts.len && (brute>0 || burn>0) ) + var/obj/item/organ/external/picked = pick(parts) + + var/brute_was = picked.brute_dam + var/burn_was = picked.burn_dam + + update |= picked.take_damage(brute,burn,sharp,edge,used_weapon) + brute -= (picked.brute_dam - brute_was) + burn -= (picked.burn_dam - burn_was) + + parts -= picked + updatehealth() + BITSET(hud_updateflag, HEALTH_HUD) + if(update) UpdateDamageIcon() + + +//////////////////////////////////////////// + +/* +This function restores the subjects blood to max. +*/ +/mob/living/carbon/human/proc/restore_blood() + if(!should_have_organ(O_HEART)) + return + if(vessel.total_volume < species.blood_volume) + vessel.add_reagent("blood", species.blood_volume - vessel.total_volume) + +/* +This function restores all organs. +*/ +/mob/living/carbon/human/restore_all_organs(var/ignore_prosthetic_prefs) + for(var/obj/item/organ/external/current_organ in organs) + current_organ.rejuvenate(ignore_prosthetic_prefs) + +/mob/living/carbon/human/proc/HealDamage(zone, brute, burn) + var/obj/item/organ/external/E = get_organ(zone) + if(istype(E, /obj/item/organ/external)) + if (E.heal_damage(brute, burn)) + UpdateDamageIcon() + BITSET(hud_updateflag, HEALTH_HUD) + else + return 0 + return + +/* +/mob/living/carbon/human/proc/get_organ(var/zone) + if(!zone) + zone = BP_TORSO + else if (zone in list( O_EYES, O_MOUTH )) + zone = BP_HEAD + return organs_by_name[zone] +*/ + +/mob/living/carbon/human/apply_damage(var/damage = 0, var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/sharp = FALSE, var/edge = FALSE, var/obj/used_weapon = null) + if(Debug2) + to_world_log("## DEBUG: human/apply_damage() was called on [src], with [damage] damage, an armor value of [blocked], and a soak value of [soaked].") + + var/obj/item/organ/external/organ = null + if(isorgan(def_zone)) + organ = def_zone + else + if(!def_zone) def_zone = ran_zone(def_zone) + organ = get_organ(check_zone(def_zone)) + + for(var/datum/modifier/M in modifiers) //MODIFIER STUFF. It's best to do this RIGHT before armor is calculated, so it's done here! This is the 'forcefield' defence. + if(damagetype == BRUTE && (!isnull(M.effective_brute_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_brute_resistance + continue + if((damagetype == BURN || damagetype == ELECTROCUTE) && (!isnull(M.effective_fire_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_fire_resistance + continue + if(damagetype == TOX && (!isnull(M.effective_tox_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_tox_resistance + continue + if(damagetype == OXY && (!isnull(M.effective_oxy_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_oxy_resistance + continue + if(damagetype == CLONE && (!isnull(M.effective_clone_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_clone_resistance + continue + if(damagetype == HALLOSS && (!isnull(M.effective_hal_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_hal_resistance + continue + if(damagetype == SEARING && (!isnull(M.effective_fire_resistance) || !isnull(M.effective_brute_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + var/damage_mitigation = 0//Used for dual calculations. + if(!isnull(M.effective_fire_resistance)) + damage_mitigation += round((1/3)*damage * M.effective_fire_resistance) + if(!isnull(M.effective_brute_resistance)) + damage_mitigation += round((2/3)*damage * M.effective_brute_resistance) + damage -= damage_mitigation + continue + if(damagetype == BIOACID && (isSynthetic() && (!isnull(M.effective_fire_resistance))) || (!isSynthetic() && M.effective_tox_resistance)) + if(isSynthetic()) + damage = damage * M.effective_fire_resistance + else + damage = damage * M.effective_tox_resistance + continue + //Handle other types of damage + if((damagetype != BRUTE) && (damagetype != BURN)) + if(damagetype == HALLOSS) + if((damage > 25 && prob(20)) || (damage > 50 && prob(60))) + if(organ && organ.organ_can_feel_pain() && !isbelly(loc) && !istype(loc, /obj/item/device/dogborg/sleeper)) //VOREStation Add + emote("scream") + ..(damage, damagetype, def_zone, blocked, soaked) + return 1 + + //Handle BRUTE and BURN damage + handle_suit_punctures(damagetype, damage, def_zone) + + if(blocked >= 100) + return 0 + + if(soaked >= damage) + return 0 + + if(!organ) return 0 + + if(blocked) + blocked = (100-blocked)/100 + damage = (damage * blocked) + + if(soaked) + damage -= soaked + + if(Debug2) + to_world_log("## DEBUG: [src] was hit for [damage].") + + switch(damagetype) + if(BRUTE) + damageoverlaytemp = 20 + if(nif && nif.flag_check(NIF_C_BRUTEARMOR,NIF_FLAGS_COMBAT)){damage *= 0.7} + damage = damage*species.brute_mod + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*damage) + damage *= M.incoming_damage_percent + if(!isnull(M.incoming_brute_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*damage) + damage *= M.incoming_brute_damage_percent + + if(organ.take_damage(damage, 0, sharp, edge, used_weapon)) + UpdateDamageIcon() + if(BURN) + damageoverlaytemp = 20 + if(nif && nif.flag_check(NIF_C_BURNARMOR,NIF_FLAGS_COMBAT)){damage *= 0.7} + damage = damage*species.burn_mod + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*damage) + damage *= M.incoming_damage_percent + if(!isnull(M.incoming_brute_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*damage) + damage *= M.incoming_fire_damage_percent + + if(organ.take_damage(0, damage, sharp, edge, used_weapon)) + UpdateDamageIcon() + + // Will set our damageoverlay icon to the next level, which will then be set back to the normal level the next mob.Life(). + updatehealth() + BITSET(hud_updateflag, HEALTH_HUD) + return 1 diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 723fbc49b27..905966009dd 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -1,684 +1,684 @@ -/* -Contains most of the procs that are called when a mob is attacked by something - -bullet_act -ex_act -meteor_act -emp_act - -*/ - -/mob/living/carbon/human/bullet_act(var/obj/item/projectile/P, var/def_zone) - - def_zone = check_zone(def_zone) - if(!has_organ(def_zone)) - return PROJECTILE_FORCE_MISS //if they don't have the organ in question then the projectile just passes by. - - var/obj/item/organ/external/organ = get_organ() - - //Shields - var/shield_check = check_shields(P.damage, P, null, def_zone, "the [P.name]") - if(shield_check) // If the block roll succeeded, this is true. - if(shield_check < 0) // The shield did something weird and the bullet needs to keep doing things (e.g. it was reflected). - return shield_check // Likely equal to PROJECTILE_FORCE_MISS or PROJECTILE_CONTINUE. - else // Otherwise we blocked normally and stopped all the damage. - return 0 - - if(!P.nodamage) - organ.add_autopsy_data("[P.name]", P.damage) - - // Tell clothing we're wearing that it got hit by a bullet/laser/etc - var/list/clothing = get_clothing_list_organ(organ) - for(var/obj/item/clothing/C in clothing) - C.clothing_impact(P, P.damage) - - //Shrapnel - if(P.can_embed()) - var/armor = getarmor_organ(organ, "bullet") - if(!prob(armor/2)) //Even if the armor doesn't stop the bullet from hurting you, it might stop it from embedding. - var/hit_embed_chance = P.embed_chance + (P.damage - armor) //More damage equals more chance to embed - - //Modifiers can make bullets less likely to embed! These are the normal modifiers and shouldn't be related to energy stuff, but they can be anyways! - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.energy_cost) //We use energy_cost here for special effects, such as embedding. - hit_embed_chance = hit_embed_chance*M.incoming_damage_percent - if(P.damage_type == BRUTE && (!isnull(M.incoming_brute_damage_percent))) - if(M.energy_based) - M.energy_source.use(M.energy_cost) - hit_embed_chance = hit_embed_chance*M.incoming_brute_damage_percent - - if(prob(max(hit_embed_chance, 0))) - var/obj/item/weapon/material/shard/shrapnel/SP = new() - SP.name = (P.name != "shrapnel")? "[P.name] shrapnel" : "shrapnel" - SP.desc = "[SP.desc] It looks like it was fired from [P.shot_from]." - SP.loc = organ - organ.embed(SP) - - return (..(P , def_zone)) - -/mob/living/carbon/human/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone) - var/obj/item/organ/external/affected = get_organ(check_zone(def_zone)) - var/siemens_coeff = get_siemens_coefficient_organ(affected) - if(fire_stacks < 0) // Water makes you more conductive. - siemens_coeff *= 1.5 - stun_amount *= siemens_coeff - agony_amount *= siemens_coeff - - switch (def_zone) - if(BP_HEAD) - agony_amount *= 1.50 - if(BP_L_HAND, BP_R_HAND) - var/c_hand - if (def_zone == BP_L_HAND) - c_hand = l_hand - else - c_hand = r_hand - - if(c_hand && (stun_amount || agony_amount > 10)) - msg_admin_attack("[key_name(src)] was disarmed by a stun effect") - - drop_from_inventory(c_hand) - if(!isbelly(loc)) //VOREStation Add - if (affected.robotic >= ORGAN_ROBOT) - custom_emote(VISIBLE_MESSAGE, "drops what they were holding, their [affected.name] malfunctioning!") - else - var/emote_scream = pick("screams in pain and ", "lets out a sharp cry and ", "cries out and ") - custom_emote(VISIBLE_MESSAGE, "[affected.organ_can_feel_pain() ? "" : emote_scream] drops what they were holding in their [affected.name]!") - - ..(stun_amount, agony_amount, def_zone) - -/mob/living/carbon/human/getarmor(var/def_zone, var/type) - var/armorval = 0 - var/total = 0 - - if(def_zone) - if(isorgan(def_zone)) - return getarmor_organ(def_zone, type) - var/obj/item/organ/external/affecting = get_organ(def_zone) - if(affecting) - return getarmor_organ(affecting, type) - //If a specific bodypart is targetted, check how that bodypart is protected and return the value. - - //If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values - for(var/organ_name in organs_by_name) - if (organ_name in organ_rel_size) - var/obj/item/organ/external/organ = organs_by_name[organ_name] - if(organ) - var/weight = organ_rel_size[organ_name] - armorval += (getarmor_organ(organ, type) * weight) - total += weight - return (armorval/max(total, 1)) - -//Like getarmor, but the value it returns will be numerical damage reduction -/mob/living/carbon/human/getsoak(var/def_zone, var/type) - var/soakval = 0 - var/total = 0 - - if(def_zone) - if(isorgan(def_zone)) - return getsoak_organ(def_zone, type) - var/obj/item/organ/external/affecting = get_organ(def_zone) - if(affecting) - return getsoak_organ(affecting, type) - //If a specific bodypart is targetted, check how that bodypart is protected and return the value. - - //If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values - for(var/organ_name in organs_by_name) - if (organ_name in organ_rel_size) - var/obj/item/organ/external/organ = organs_by_name[organ_name] - if(organ) - var/weight = organ_rel_size[organ_name] - soakval += getsoak_organ(organ, type) * weight - total += weight - return (soakval/max(total, 1)) - -//this proc returns the Siemens coefficient of electrical resistivity for a particular external organ. -/mob/living/carbon/human/proc/get_siemens_coefficient_organ(var/obj/item/organ/external/def_zone) - if (!def_zone) - return 1.0 - - var/siemens_coefficient = max(species.siemens_coefficient,0) - - var/list/clothing_items = list(head, wear_mask, wear_suit, w_uniform, gloves, shoes) // What all are we checking? - for(var/obj/item/clothing/C in clothing_items) - if(istype(C) && (C.body_parts_covered & def_zone.body_part)) // Is that body part being targeted covered? - siemens_coefficient *= C.siemens_coefficient - - // Modifiers. - for(var/datum/modifier/M as anything in modifiers) - if(!isnull(M.siemens_coefficient)) - siemens_coefficient *= M.siemens_coefficient - - return siemens_coefficient - -// Similar to above but is for the mob's overall protection, being the average of all slots. -/mob/living/carbon/human/proc/get_siemens_coefficient_average() - var/siemens_value = 0 - var/total = 0 - for(var/organ_name in organs_by_name) - if(organ_name in organ_rel_size) - var/obj/item/organ/external/organ = organs_by_name[organ_name] - if(organ) - var/weight = organ_rel_size[organ_name] - siemens_value += get_siemens_coefficient_organ(organ) * weight - total += weight - - if(fire_stacks < 0) // Water makes you more conductive. - siemens_value *= 1.5 - - return (siemens_value / max(total, 1)) - -// Returns a number between 0 to 1, with 1 being total protection. -/mob/living/carbon/human/get_shock_protection() - return min(1 - get_siemens_coefficient_average(), 1) // Don't go above 1, but negatives are fine. - -// Returns a list of clothing that is currently covering def_zone. -/mob/living/carbon/human/proc/get_clothing_list_organ(var/obj/item/organ/external/def_zone, var/type) - var/list/results = list() - var/list/clothing_items = list(head, wear_mask, wear_suit, w_uniform, gloves, shoes) - for(var/obj/item/clothing/C in clothing_items) - if(istype(C) && (C.body_parts_covered & def_zone.body_part)) - results.Add(C) - return results - -//this proc returns the armour value for a particular external organ. -/mob/living/carbon/human/proc/getarmor_organ(var/obj/item/organ/external/def_zone, var/type) - if(!type || !def_zone) - return 0 - var/protection = 0 - var/list/protective_gear = def_zone.get_covering_clothing() - for(var/obj/item/clothing/gear in protective_gear) - protection += gear.armor[type] - - for(var/datum/modifier/M as anything in modifiers) - var/modifier_armor = LAZYACCESS(M.armor_percent, type) - if(modifier_armor) - protection += modifier_armor - - return protection - -/mob/living/carbon/human/proc/getsoak_organ(var/obj/item/organ/external/def_zone, var/type) - if(!type || !def_zone) - return 0 - var/soaked = 0 - var/list/protective_gear = def_zone.get_covering_clothing() - for(var/obj/item/clothing/gear in protective_gear) - soaked += gear.armorsoak[type] - - for(var/datum/modifier/M as anything in modifiers) - var/modifier_armor = LAZYACCESS(M.armor_flat, type) - if(modifier_armor) - soaked += modifier_armor - - return soaked - -// Checked in borer code -/mob/living/carbon/human/proc/check_head_coverage() - var/obj/item/organ/external/H = organs_by_name[BP_HEAD] - var/list/body_parts = H.get_covering_clothing(EYES) - if(LAZYLEN(body_parts)) - return 1 - return 0 - -//Used to check if they can be fed food/drinks/pills -/mob/living/carbon/human/proc/check_mouth_coverage() - var/obj/item/organ/external/H = organs_by_name[BP_HEAD] - var/list/protective_gear = H.get_covering_clothing(FACE) - for(var/obj/item/gear in protective_gear) - if(istype(gear) && (gear.body_parts_covered & FACE) && !(gear.item_flags & FLEXIBLEMATERIAL)) - return gear - return null - -/mob/living/carbon/human/proc/check_mouth_coverage_survival() - var/obj/item/organ/external/H = organs_by_name[BP_HEAD] - var/list/protective_gear = H.get_covering_clothing(FACE) - for(var/obj/item/gear in protective_gear) - if(istype(gear) && (gear.body_parts_covered & FACE) && !(gear.item_flags & FLEXIBLEMATERIAL) && !(gear.item_flags & ALLOW_SURVIVALFOOD)) - return gear - return null - -/mob/living/carbon/human/proc/check_shields(var/damage = 0, var/atom/damage_source = null, var/mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - for(var/obj/item/shield in list(l_hand, r_hand, wear_suit)) - if(!shield) continue - . = shield.handle_shield(src, damage, damage_source, attacker, def_zone, attack_text) - if(.) return - return 0 - -/mob/living/carbon/human/resolve_item_attack(obj/item/I, mob/living/user, var/target_zone) - if(check_neckgrab_attack(I, user, target_zone)) - return null - - if(user == src) // Attacking yourself can't miss - return target_zone - - var/hit_zone = get_zone_with_miss_chance(target_zone, src, user.get_accuracy_penalty()) - - if(!hit_zone) - user.do_attack_animation(src) - playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message("\The [user] misses [src] with \the [I]!") - return null - - if(check_shields(I.force, I, user, target_zone, "the [I.name]")) - return - - var/obj/item/organ/external/affecting = get_organ(hit_zone) - if (!affecting || affecting.is_stump()) - to_chat(user, "They are missing that limb!") - return null - - return hit_zone - -/mob/living/carbon/human/hit_with_weapon(obj/item/I, mob/living/user, var/effective_force, var/hit_zone) - var/obj/item/organ/external/affecting = get_organ(hit_zone) - if(!affecting) - return //should be prevented by attacked_with_item() but for sanity. - - visible_message("[src] has been [LAZYLEN(I.attack_verb) ? pick(I.attack_verb) : "attacked"] in the [affecting.name] with [I.name] by [user]!") - - var/soaked = get_armor_soak(hit_zone, "melee", I.armor_penetration) - - var/blocked = run_armor_check(hit_zone, "melee", I.armor_penetration, "Your armor has protected your [affecting.name].", "Your armor has softened the blow to your [affecting.name].") - - standard_weapon_hit_effects(I, user, effective_force, blocked, soaked, hit_zone) - - return blocked - -/mob/living/carbon/human/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/soaked, var/hit_zone) - var/obj/item/organ/external/affecting = get_organ(hit_zone) - if(!affecting) - return 0 - - // Allow clothing to respond to being hit. - // This is done up here so that clothing damage occurs even if fully blocked. - var/list/clothing = get_clothing_list_organ(affecting) - for(var/obj/item/clothing/C in clothing) - C.clothing_impact(I, effective_force) - - if(soaked >= round(effective_force*0.8)) - effective_force -= round(effective_force*0.8) - // Handle striking to cripple. - if(user.a_intent == I_DISARM) - effective_force *= 0.5 //reduced effective force... - if(!..(I, user, effective_force, blocked, soaked, hit_zone)) - return 0 - - //set the dislocate mult less than the effective force mult so that - //dislocating limbs on disarm is a bit easier than breaking limbs on harm - attack_joint(affecting, I, effective_force, 0.75, blocked, soaked) //...but can dislocate joints - else if(!..()) - return 0 - - if(effective_force > 10 || effective_force >= 5 && prob(33)) - forcesay(hit_appends) //forcesay checks stat already - - if(prob(25 + (effective_force * 2))) - if(!((I.damtype == BRUTE) || (I.damtype == HALLOSS))) - return - - if(!(I.flags & NOBLOODY)) - I.add_blood(src) - - var/bloody = 0 - if(prob(33)) - bloody = 1 - var/turf/location = loc - if(istype(location, /turf/simulated)) - location.add_blood(src) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(get_dist(H, src) <= 1) //people with TK won't get smeared with blood - H.bloody_body(src) - H.bloody_hands(src) - - if(!stat && !(I.no_random_knockdown)) - switch(hit_zone) - if(BP_HEAD)//Harder to score a stun but if you do it lasts a bit longer - if(prob(effective_force)) - apply_effect(20, PARALYZE, blocked, soaked) - visible_message("\The [src] has been knocked unconscious!") - if(bloody)//Apply blood - if(wear_mask) - wear_mask.add_blood(src) - update_inv_wear_mask(0) - if(head) - head.add_blood(src) - update_inv_head(0) - if(glasses && prob(33)) - glasses.add_blood(src) - update_inv_glasses(0) - if(BP_TORSO)//Easier to score a stun but lasts less time - if(prob(effective_force + 10)) - apply_effect(6, WEAKEN, blocked, soaked) - visible_message("\The [src] has been knocked down!") - if(bloody) - bloody_body(src) - - return 1 - -/mob/living/carbon/human/proc/attack_joint(var/obj/item/organ/external/organ, var/obj/item/W, var/effective_force, var/dislocate_mult, var/blocked, var/soaked) - if(!organ || (organ.dislocated == 1) || (organ.dislocated == -1) || blocked >= 100) //VOREStation Edit Bugfix - return 0 - - if(W.damtype != BRUTE) - return 0 - - if(soaked >= round(effective_force*0.8)) - effective_force -= round(effective_force*0.8) - - //want the dislocation chance to be such that the limb is expected to dislocate after dealing a fraction of the damage needed to break the limb - var/dislocate_chance = effective_force/(dislocate_mult * organ.min_broken_damage * config.organ_health_multiplier)*100 - if(prob(dislocate_chance * (100 - blocked)/100)) - visible_message("[src]'s [organ.joint] [pick("gives way","caves in","crumbles","collapses")]!") - organ.dislocate(1) - return 1 - return 0 - -/mob/living/carbon/human/emag_act(var/remaining_charges, mob/user, var/emag_source) - var/obj/item/organ/external/affecting = get_organ(user.zone_sel.selecting) - if(!affecting || !(affecting.robotic >= ORGAN_ROBOT)) - to_chat(user, "That limb isn't robotic.") - return -1 - if(affecting.sabotaged) - to_chat(user, "[src]'s [affecting.name] is already sabotaged!") - return -1 - to_chat(user, "You sneakily slide [emag_source] into the dataport on [src]'s [affecting.name] and short out the safeties.") - affecting.sabotaged = 1 - return 1 - -//this proc handles being hit by a thrown atom -/mob/living/carbon/human/hitby(atom/movable/AM as mob|obj,var/speed = THROWFORCE_SPEED_DIVISOR) -// if(buckled && buckled == AM) -// return // Don't get hit by the thing we're buckled to. - - //VORESTATION EDIT START - Allows for thrown vore! - //Throwing a prey into a pred takes priority. After that it checks to see if the person being thrown is a pred. - // I put more comments here for ease of reading. - if(istype(AM, /mob/living)) - var/mob/living/thrown_mob = AM - if(isanimal(thrown_mob) && !allowmobvore) //Is the thrown_mob an animal and we don't allow mobvore? - return - // PERSON BEING HIT: CAN BE DROP PRED, ALLOWS THROW VORE. - // PERSON BEING THROWN: DEVOURABLE, ALLOWS THROW VORE, CAN BE DROP PREY. - if((can_be_drop_pred && throw_vore && vore_selected) && (thrown_mob.devourable && thrown_mob.throw_vore && thrown_mob.can_be_drop_prey)) //Prey thrown into pred. - vore_selected.nom_mob(thrown_mob) //Eat them!!! - visible_message("[thrown_mob] is thrown right into [src]'s [lowertext(vore_selected.name)]!") - if(thrown_mob.loc != vore_selected) - thrown_mob.forceMove(vore_selected) //Double check. Should never happen but...Weirder things have happened! - add_attack_logs(thrown_mob.thrower,src,"Devoured [thrown_mob.name] via throw vore.") - return //We can stop here. We don't need to calculate damage or anything else. They're eaten. - - // PERSON BEING HIT: CAN BE DROP PREY, ALLOWS THROW VORE, AND IS DEVOURABLE. - // PERSON BEING THROWN: CAN BE DROP PRED, ALLOWS THROW VORE. - else if((can_be_drop_prey && throw_vore && devourable) && (thrown_mob.can_be_drop_pred && thrown_mob.throw_vore && thrown_mob.vore_selected)) //Pred thrown into prey. - visible_message("[src] suddenly slips inside of [thrown_mob]'s [lowertext(thrown_mob.vore_selected.name)] as [thrown_mob] flies into them!") - thrown_mob.vore_selected.nom_mob(src) //Eat them!!! - if(src.loc != thrown_mob.vore_selected) - src.forceMove(thrown_mob.vore_selected) //Double check. Should never happen but...Weirder things have happened! - add_attack_logs(thrown_mob.LAssailant,src,"Was Devoured by [thrown_mob.name] via throw vore.") - return - //VORESTATION EDIT END - Allows for thrown vore! - - if(istype(AM,/obj/)) - var/obj/O = AM - if(in_throw_mode && speed <= THROWFORCE_SPEED_DIVISOR) //empty active hand and we're in throw mode - if(canmove && !restrained()) - if(isturf(O.loc)) - if(can_catch(O)) - put_in_active_hand(O) - visible_message("[src] catches [O]!") - throw_mode_off() - return - - var/dtype = O.damtype - var/throw_damage = O.throwforce*(speed/THROWFORCE_SPEED_DIVISOR) - - if(species && species.throwforce_absorb_threshold >= throw_damage) - visible_message("\The [O] simply bounces off of [src]'s body!") - return - - var/zone - if (istype(O.thrower, /mob/living)) - var/mob/living/L = O.thrower - zone = check_zone(L.zone_sel.selecting) - else - zone = ran_zone(BP_TORSO,75) //Hits a random part of the body, geared towards the chest - - //check if we hit - var/miss_chance = 15 - if (O.throw_source) - var/distance = get_dist(O.throw_source, loc) - miss_chance = max(15*(distance-2), 0) - zone = get_zone_with_miss_chance(zone, src, miss_chance, ranged_attack=1) - - if(zone && O.thrower != src) - var/shield_check = check_shields(throw_damage, O, thrower, zone, "[O]") - if(shield_check == PROJECTILE_FORCE_MISS) - zone = null - else if(shield_check) - return - - if(!zone) - visible_message("\The [O] misses [src] narrowly!") - return - - O.throwing = 0 //it hit, so stop moving - - var/obj/item/organ/external/affecting = get_organ(zone) - var/hit_area = affecting.name - - src.visible_message("[span_red("[src] has been hit in the [hit_area] by [O].")]") - - if(ismob(O.thrower)) - add_attack_logs(O.thrower,src,"Hit with thrown [O.name]") - - //If the armor absorbs all of the damage, skip the rest of the calculations - var/soaked = get_armor_soak(affecting, "melee", O.armor_penetration) - if(soaked >= throw_damage) - to_chat(src, "Your armor absorbs the force of [O.name]!") - return - - var/armor = run_armor_check(affecting, "melee", O.armor_penetration, "Your armor has protected your [hit_area].", "Your armor has softened hit to your [hit_area].") //I guess "melee" is the best fit here - if(armor < 100) - apply_damage(throw_damage, dtype, zone, armor, soaked, is_sharp(O), has_edge(O), O) - - - //thrown weapon embedded object code. - if(dtype == BRUTE && istype(O,/obj/item)) - var/obj/item/I = O - if (!is_robot_module(I)) - var/sharp = is_sharp(I) - var/damage = throw_damage - if (soaked) - damage -= soaked - if (armor) - damage /= armor+1 - - //blunt objects should really not be embedding in things unless a huge amount of force is involved - var/embed_chance = sharp? damage/I.w_class : damage/(I.w_class*3) - var/embed_threshold = sharp? 5*I.w_class : 15*I.w_class - - //Sharp objects will always embed if they do enough damage. - //Thrown sharp objects have some momentum already and have a small chance to embed even if the damage is below the threshold - if((sharp && prob(damage/(10*I.w_class)*100)) || (damage > embed_threshold && prob(embed_chance))) - affecting.embed(I) - - // Begin BS12 momentum-transfer code. - var/mass = 1.5 - if(istype(O, /obj/item)) - var/obj/item/I = O - mass = I.w_class/THROWNOBJ_KNOCKBACK_DIVISOR - var/momentum = speed*mass - - if(O.throw_source && momentum >= THROWNOBJ_KNOCKBACK_SPEED && !buckled) - var/dir = get_dir(O.throw_source, src) - - visible_message("[span_red("[src] staggers under the impact!")]","[span_red("You stagger under the impact!")]") - src.throw_at(get_edge_target_turf(src,dir),1,momentum) - - if(!O || !src) return - - if(O.loc == src && O.sharp) //Projectile is embedded and suitable for pinning. - var/turf/T = near_wall(dir,2) - - if(T) - src.loc = T - visible_message("[src] is pinned to the wall by [O]!","You are pinned to the wall by [O]!") - src.anchored = TRUE - src.pinned += O - -// This does a prob check to catch the thing flying at you, with a minimum of 1% -/mob/living/carbon/human/proc/can_catch(var/obj/O) - if(!get_active_hand()) // If active hand is empty - var/obj/item/organ/external/temp = organs_by_name["r_hand"] - if (hand) - temp = organs_by_name["l_hand"] - if(temp && !temp.is_usable()) - return FALSE // The hand isn't working in the first place - - if(!O.catchable) - return FALSE - - // Alright, our hand works? Time to try the catching. - var/catch_chance = 90 // Default 90% catch rate - - if(O.sharp) - catch_chance -= 50 // Catching knives is hard - - catch_chance -= get_accuracy_penalty() // Same issues with shooting a gun, or swinging a weapon - - catch_chance = between(1, catch_chance, 100) - - if(prob(catch_chance)) - return TRUE - return FALSE - -/mob/living/carbon/human/embed(var/obj/O, var/def_zone=null) - if(!def_zone) ..() - - var/obj/item/organ/external/affecting = get_organ(def_zone) - if(affecting) - affecting.embed(O) - - -/mob/living/carbon/human/proc/bloody_hands(var/mob/living/source, var/amount = 2) - if (gloves) - gloves.add_blood(source) - gloves:transfer_blood = amount - gloves:bloody_hands_mob = source - else - add_blood(source) - bloody_hands = amount - bloody_hands_mob = source - update_inv_gloves() //updates on-mob overlays for bloody hands and/or bloody gloves - -/mob/living/carbon/human/proc/bloody_body(var/mob/living/source) - if(wear_suit) - wear_suit.add_blood(source) - update_inv_wear_suit(0) - if(w_uniform) - w_uniform.add_blood(source) - update_inv_w_uniform(0) - -/mob/living/carbon/human/proc/handle_suit_punctures(var/damtype, var/damage, var/def_zone) - - // Tox and oxy don't matter to suits. - if(damtype != BURN && damtype != BRUTE) return - - // The rig might soak this hit, if we're wearing one. - if(istype(get_rig(),/obj/item/weapon/rig)) - var/obj/item/weapon/rig/rig = get_rig() - rig.take_hit(damage) - - // We may also be taking a suit breach. - if(!wear_suit) return - if(!istype(wear_suit,/obj/item/clothing/suit/space)) return - var/obj/item/clothing/suit/space/SS = wear_suit - var/penetrated_dam = max(0,(damage - SS.breach_threshold)) - if(penetrated_dam) SS.create_breaches(damtype, penetrated_dam) - -/mob/living/carbon/human/reagent_permeability() - var/perm = 0 - - var/list/perm_by_part = list( - "head" = THERMAL_PROTECTION_HEAD, - "upper_torso" = THERMAL_PROTECTION_UPPER_TORSO, - "lower_torso" = THERMAL_PROTECTION_LOWER_TORSO, - "legs" = THERMAL_PROTECTION_LEG_LEFT + THERMAL_PROTECTION_LEG_RIGHT, - "feet" = THERMAL_PROTECTION_FOOT_LEFT + THERMAL_PROTECTION_FOOT_RIGHT, - "arms" = THERMAL_PROTECTION_ARM_LEFT + THERMAL_PROTECTION_ARM_RIGHT, - "hands" = THERMAL_PROTECTION_HAND_LEFT + THERMAL_PROTECTION_HAND_RIGHT - ) - - for(var/obj/item/clothing/C in src.get_equipped_items()) - if(C.permeability_coefficient == 1 || !C.body_parts_covered) - continue - if(C.body_parts_covered & HEAD) - perm_by_part["head"] *= C.permeability_coefficient - if(C.body_parts_covered & UPPER_TORSO) - perm_by_part["upper_torso"] *= C.permeability_coefficient - if(C.body_parts_covered & LOWER_TORSO) - perm_by_part["lower_torso"] *= C.permeability_coefficient - if(C.body_parts_covered & LEGS) - perm_by_part["legs"] *= C.permeability_coefficient - if(C.body_parts_covered & FEET) - perm_by_part["feet"] *= C.permeability_coefficient - if(C.body_parts_covered & ARMS) - perm_by_part["arms"] *= C.permeability_coefficient - if(C.body_parts_covered & HANDS) - perm_by_part["hands"] *= C.permeability_coefficient - - for(var/part in perm_by_part) - perm += perm_by_part[part] - - return perm - -// This is for preventing harm by being covered in water, which only prometheans need to deal with. -/mob/living/carbon/human/get_water_protection() - var/protection = species.water_resistance - if(protection == 1) // No point doing permeability checks if it won't matter. - return protection - // Wearing clothing with a low permeability_coefficient can protect from water. - - var/converted_protection = 1 - protection - var/perm = reagent_permeability() - converted_protection *= perm - return CLAMP(1-converted_protection, 0, 1) - -/mob/living/carbon/human/water_act(amount) - adjust_fire_stacks(-amount * 5) - for(var/atom/movable/AM in contents) - AM.water_act(amount) - remove_modifiers_of_type(/datum/modifier/fire) - - species.handle_water_damage(src, amount) - -/mob/living/carbon/human/shank_attack(obj/item/W, obj/item/weapon/grab/G, mob/user, hit_zone) - - if(!..()) - return 0 - - var/organ_chance = 50 - var/damage = shank_armor_helper(W, G, user) - var/obj/item/organ/external/chest = get_organ(hit_zone) - - if(W.edge) - organ_chance = 75 - user.next_move = world.time + 20 - user.visible_message("\The [user] begins to twist \the [W] around inside [src]'s [chest]!") - if(!do_after(user, 20)) - return 0 - if(!(G && G.assailant == user && G.affecting == src)) //check that we still have a grab - return 0 - - user.visible_message("\The [user] twists \the [W] around inside [src]'s [chest]!") - - if(prob(organ_chance)) - var/obj/item/organ/internal/selected_organ = pick(chest.internal_organs) - selected_organ.damage = max(selected_organ.damage, damage * 0.5) - G.last_action = world.time - flick(G.hud.icon_state, G.hud) - - return 1 +/* +Contains most of the procs that are called when a mob is attacked by something + +bullet_act +ex_act +meteor_act +emp_act + +*/ + +/mob/living/carbon/human/bullet_act(var/obj/item/projectile/P, var/def_zone) + + def_zone = check_zone(def_zone) + if(!has_organ(def_zone)) + return PROJECTILE_FORCE_MISS //if they don't have the organ in question then the projectile just passes by. + + var/obj/item/organ/external/organ = get_organ() + + //Shields + var/shield_check = check_shields(P.damage, P, null, def_zone, "the [P.name]") + if(shield_check) // If the block roll succeeded, this is true. + if(shield_check < 0) // The shield did something weird and the bullet needs to keep doing things (e.g. it was reflected). + return shield_check // Likely equal to PROJECTILE_FORCE_MISS or PROJECTILE_CONTINUE. + else // Otherwise we blocked normally and stopped all the damage. + return 0 + + if(!P.nodamage) + organ.add_autopsy_data("[P.name]", P.damage) + + // Tell clothing we're wearing that it got hit by a bullet/laser/etc + var/list/clothing = get_clothing_list_organ(organ) + for(var/obj/item/clothing/C in clothing) + C.clothing_impact(P, P.damage) + + //Shrapnel + if(P.can_embed()) + var/armor = getarmor_organ(organ, "bullet") + if(!prob(armor/2)) //Even if the armor doesn't stop the bullet from hurting you, it might stop it from embedding. + var/hit_embed_chance = P.embed_chance + (P.damage - armor) //More damage equals more chance to embed + + //Modifiers can make bullets less likely to embed! These are the normal modifiers and shouldn't be related to energy stuff, but they can be anyways! + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.energy_cost) //We use energy_cost here for special effects, such as embedding. + hit_embed_chance = hit_embed_chance*M.incoming_damage_percent + if(P.damage_type == BRUTE && (!isnull(M.incoming_brute_damage_percent))) + if(M.energy_based) + M.energy_source.use(M.energy_cost) + hit_embed_chance = hit_embed_chance*M.incoming_brute_damage_percent + + if(prob(max(hit_embed_chance, 0))) + var/obj/item/weapon/material/shard/shrapnel/SP = new() + SP.name = (P.name != "shrapnel")? "[P.name] shrapnel" : "shrapnel" + SP.desc = "[SP.desc] It looks like it was fired from [P.shot_from]." + SP.loc = organ + organ.embed(SP) + + return (..(P , def_zone)) + +/mob/living/carbon/human/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone) + var/obj/item/organ/external/affected = get_organ(check_zone(def_zone)) + var/siemens_coeff = get_siemens_coefficient_organ(affected) + if(fire_stacks < 0) // Water makes you more conductive. + siemens_coeff *= 1.5 + stun_amount *= siemens_coeff + agony_amount *= siemens_coeff + + switch (def_zone) + if(BP_HEAD) + agony_amount *= 1.50 + if(BP_L_HAND, BP_R_HAND) + var/c_hand + if (def_zone == BP_L_HAND) + c_hand = l_hand + else + c_hand = r_hand + + if(c_hand && (stun_amount || agony_amount > 10)) + msg_admin_attack("[key_name(src)] was disarmed by a stun effect") + + drop_from_inventory(c_hand) + if(!isbelly(loc)) //VOREStation Add + if (affected.robotic >= ORGAN_ROBOT) + custom_emote(VISIBLE_MESSAGE, "drops what they were holding, their [affected.name] malfunctioning!") + else + var/emote_scream = pick("screams in pain and ", "lets out a sharp cry and ", "cries out and ") + custom_emote(VISIBLE_MESSAGE, "[affected.organ_can_feel_pain() ? "" : emote_scream] drops what they were holding in their [affected.name]!") + + ..(stun_amount, agony_amount, def_zone) + +/mob/living/carbon/human/getarmor(var/def_zone, var/type) + var/armorval = 0 + var/total = 0 + + if(def_zone) + if(isorgan(def_zone)) + return getarmor_organ(def_zone, type) + var/obj/item/organ/external/affecting = get_organ(def_zone) + if(affecting) + return getarmor_organ(affecting, type) + //If a specific bodypart is targetted, check how that bodypart is protected and return the value. + + //If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values + for(var/organ_name in organs_by_name) + if (organ_name in organ_rel_size) + var/obj/item/organ/external/organ = organs_by_name[organ_name] + if(organ) + var/weight = organ_rel_size[organ_name] + armorval += (getarmor_organ(organ, type) * weight) + total += weight + return (armorval/max(total, 1)) + +//Like getarmor, but the value it returns will be numerical damage reduction +/mob/living/carbon/human/getsoak(var/def_zone, var/type) + var/soakval = 0 + var/total = 0 + + if(def_zone) + if(isorgan(def_zone)) + return getsoak_organ(def_zone, type) + var/obj/item/organ/external/affecting = get_organ(def_zone) + if(affecting) + return getsoak_organ(affecting, type) + //If a specific bodypart is targetted, check how that bodypart is protected and return the value. + + //If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values + for(var/organ_name in organs_by_name) + if (organ_name in organ_rel_size) + var/obj/item/organ/external/organ = organs_by_name[organ_name] + if(organ) + var/weight = organ_rel_size[organ_name] + soakval += getsoak_organ(organ, type) * weight + total += weight + return (soakval/max(total, 1)) + +//this proc returns the Siemens coefficient of electrical resistivity for a particular external organ. +/mob/living/carbon/human/proc/get_siemens_coefficient_organ(var/obj/item/organ/external/def_zone) + if (!def_zone) + return 1.0 + + var/siemens_coefficient = max(species.siemens_coefficient,0) + + var/list/clothing_items = list(head, wear_mask, wear_suit, w_uniform, gloves, shoes) // What all are we checking? + for(var/obj/item/clothing/C in clothing_items) + if(istype(C) && (C.body_parts_covered & def_zone.body_part)) // Is that body part being targeted covered? + siemens_coefficient *= C.siemens_coefficient + + // Modifiers. + for(var/datum/modifier/M as anything in modifiers) + if(!isnull(M.siemens_coefficient)) + siemens_coefficient *= M.siemens_coefficient + + return siemens_coefficient + +// Similar to above but is for the mob's overall protection, being the average of all slots. +/mob/living/carbon/human/proc/get_siemens_coefficient_average() + var/siemens_value = 0 + var/total = 0 + for(var/organ_name in organs_by_name) + if(organ_name in organ_rel_size) + var/obj/item/organ/external/organ = organs_by_name[organ_name] + if(organ) + var/weight = organ_rel_size[organ_name] + siemens_value += get_siemens_coefficient_organ(organ) * weight + total += weight + + if(fire_stacks < 0) // Water makes you more conductive. + siemens_value *= 1.5 + + return (siemens_value / max(total, 1)) + +// Returns a number between 0 to 1, with 1 being total protection. +/mob/living/carbon/human/get_shock_protection() + return min(1 - get_siemens_coefficient_average(), 1) // Don't go above 1, but negatives are fine. + +// Returns a list of clothing that is currently covering def_zone. +/mob/living/carbon/human/proc/get_clothing_list_organ(var/obj/item/organ/external/def_zone, var/type) + var/list/results = list() + var/list/clothing_items = list(head, wear_mask, wear_suit, w_uniform, gloves, shoes) + for(var/obj/item/clothing/C in clothing_items) + if(istype(C) && (C.body_parts_covered & def_zone.body_part)) + results.Add(C) + return results + +//this proc returns the armour value for a particular external organ. +/mob/living/carbon/human/proc/getarmor_organ(var/obj/item/organ/external/def_zone, var/type) + if(!type || !def_zone) + return 0 + var/protection = 0 + var/list/protective_gear = def_zone.get_covering_clothing() + for(var/obj/item/clothing/gear in protective_gear) + protection += gear.armor[type] + + for(var/datum/modifier/M as anything in modifiers) + var/modifier_armor = LAZYACCESS(M.armor_percent, type) + if(modifier_armor) + protection += modifier_armor + + return protection + +/mob/living/carbon/human/proc/getsoak_organ(var/obj/item/organ/external/def_zone, var/type) + if(!type || !def_zone) + return 0 + var/soaked = 0 + var/list/protective_gear = def_zone.get_covering_clothing() + for(var/obj/item/clothing/gear in protective_gear) + soaked += gear.armorsoak[type] + + for(var/datum/modifier/M as anything in modifiers) + var/modifier_armor = LAZYACCESS(M.armor_flat, type) + if(modifier_armor) + soaked += modifier_armor + + return soaked + +// Checked in borer code +/mob/living/carbon/human/proc/check_head_coverage() + var/obj/item/organ/external/H = organs_by_name[BP_HEAD] + var/list/body_parts = H.get_covering_clothing(EYES) + if(LAZYLEN(body_parts)) + return 1 + return 0 + +//Used to check if they can be fed food/drinks/pills +/mob/living/carbon/human/proc/check_mouth_coverage() + var/obj/item/organ/external/H = organs_by_name[BP_HEAD] + var/list/protective_gear = H.get_covering_clothing(FACE) + for(var/obj/item/gear in protective_gear) + if(istype(gear) && (gear.body_parts_covered & FACE) && !(gear.item_flags & FLEXIBLEMATERIAL)) + return gear + return null + +/mob/living/carbon/human/proc/check_mouth_coverage_survival() + var/obj/item/organ/external/H = organs_by_name[BP_HEAD] + var/list/protective_gear = H.get_covering_clothing(FACE) + for(var/obj/item/gear in protective_gear) + if(istype(gear) && (gear.body_parts_covered & FACE) && !(gear.item_flags & FLEXIBLEMATERIAL) && !(gear.item_flags & ALLOW_SURVIVALFOOD)) + return gear + return null + +/mob/living/carbon/human/proc/check_shields(var/damage = 0, var/atom/damage_source = null, var/mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + for(var/obj/item/shield in list(l_hand, r_hand, wear_suit)) + if(!shield) continue + . = shield.handle_shield(src, damage, damage_source, attacker, def_zone, attack_text) + if(.) return + return 0 + +/mob/living/carbon/human/resolve_item_attack(obj/item/I, mob/living/user, var/target_zone) + if(check_neckgrab_attack(I, user, target_zone)) + return null + + if(user == src) // Attacking yourself can't miss + return target_zone + + var/hit_zone = get_zone_with_miss_chance(target_zone, src, user.get_accuracy_penalty()) + + if(!hit_zone) + user.do_attack_animation(src) + playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message("\The [user] misses [src] with \the [I]!") + return null + + if(check_shields(I.force, I, user, target_zone, "the [I.name]")) + return + + var/obj/item/organ/external/affecting = get_organ(hit_zone) + if (!affecting || affecting.is_stump()) + to_chat(user, "They are missing that limb!") + return null + + return hit_zone + +/mob/living/carbon/human/hit_with_weapon(obj/item/I, mob/living/user, var/effective_force, var/hit_zone) + var/obj/item/organ/external/affecting = get_organ(hit_zone) + if(!affecting) + return //should be prevented by attacked_with_item() but for sanity. + + visible_message("[src] has been [LAZYLEN(I.attack_verb) ? pick(I.attack_verb) : "attacked"] in the [affecting.name] with [I.name] by [user]!") + + var/soaked = get_armor_soak(hit_zone, "melee", I.armor_penetration) + + var/blocked = run_armor_check(hit_zone, "melee", I.armor_penetration, "Your armor has protected your [affecting.name].", "Your armor has softened the blow to your [affecting.name].") + + standard_weapon_hit_effects(I, user, effective_force, blocked, soaked, hit_zone) + + return blocked + +/mob/living/carbon/human/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/soaked, var/hit_zone) + var/obj/item/organ/external/affecting = get_organ(hit_zone) + if(!affecting) + return 0 + + // Allow clothing to respond to being hit. + // This is done up here so that clothing damage occurs even if fully blocked. + var/list/clothing = get_clothing_list_organ(affecting) + for(var/obj/item/clothing/C in clothing) + C.clothing_impact(I, effective_force) + + if(soaked >= round(effective_force*0.8)) + effective_force -= round(effective_force*0.8) + // Handle striking to cripple. + if(user.a_intent == I_DISARM) + effective_force *= 0.5 //reduced effective force... + if(!..(I, user, effective_force, blocked, soaked, hit_zone)) + return 0 + + //set the dislocate mult less than the effective force mult so that + //dislocating limbs on disarm is a bit easier than breaking limbs on harm + attack_joint(affecting, I, effective_force, 0.75, blocked, soaked) //...but can dislocate joints + else if(!..()) + return 0 + + if(effective_force > 10 || effective_force >= 5 && prob(33)) + forcesay(hit_appends) //forcesay checks stat already + + if(prob(25 + (effective_force * 2))) + if(!((I.damtype == BRUTE) || (I.damtype == HALLOSS))) + return + + if(!(I.flags & NOBLOODY)) + I.add_blood(src) + + var/bloody = 0 + if(prob(33)) + bloody = 1 + var/turf/location = loc + if(istype(location, /turf/simulated)) + location.add_blood(src) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(get_dist(H, src) <= 1) //people with TK won't get smeared with blood + H.bloody_body(src) + H.bloody_hands(src) + + if(!stat && !(I.no_random_knockdown)) + switch(hit_zone) + if(BP_HEAD)//Harder to score a stun but if you do it lasts a bit longer + if(prob(effective_force)) + apply_effect(20, PARALYZE, blocked, soaked) + visible_message("\The [src] has been knocked unconscious!") + if(bloody)//Apply blood + if(wear_mask) + wear_mask.add_blood(src) + update_inv_wear_mask(0) + if(head) + head.add_blood(src) + update_inv_head(0) + if(glasses && prob(33)) + glasses.add_blood(src) + update_inv_glasses(0) + if(BP_TORSO)//Easier to score a stun but lasts less time + if(prob(effective_force + 10)) + apply_effect(6, WEAKEN, blocked, soaked) + visible_message("\The [src] has been knocked down!") + if(bloody) + bloody_body(src) + + return 1 + +/mob/living/carbon/human/proc/attack_joint(var/obj/item/organ/external/organ, var/obj/item/W, var/effective_force, var/dislocate_mult, var/blocked, var/soaked) + if(!organ || (organ.dislocated == 1) || (organ.dislocated == -1) || blocked >= 100) //VOREStation Edit Bugfix + return 0 + + if(W.damtype != BRUTE) + return 0 + + if(soaked >= round(effective_force*0.8)) + effective_force -= round(effective_force*0.8) + + //want the dislocation chance to be such that the limb is expected to dislocate after dealing a fraction of the damage needed to break the limb + var/dislocate_chance = effective_force/(dislocate_mult * organ.min_broken_damage * config.organ_health_multiplier)*100 + if(prob(dislocate_chance * (100 - blocked)/100)) + visible_message("[src]'s [organ.joint] [pick("gives way","caves in","crumbles","collapses")]!") + organ.dislocate(1) + return 1 + return 0 + +/mob/living/carbon/human/emag_act(var/remaining_charges, mob/user, var/emag_source) + var/obj/item/organ/external/affecting = get_organ(user.zone_sel.selecting) + if(!affecting || !(affecting.robotic >= ORGAN_ROBOT)) + to_chat(user, "That limb isn't robotic.") + return -1 + if(affecting.sabotaged) + to_chat(user, "[src]'s [affecting.name] is already sabotaged!") + return -1 + to_chat(user, "You sneakily slide [emag_source] into the dataport on [src]'s [affecting.name] and short out the safeties.") + affecting.sabotaged = 1 + return 1 + +//this proc handles being hit by a thrown atom +/mob/living/carbon/human/hitby(atom/movable/AM as mob|obj,var/speed = THROWFORCE_SPEED_DIVISOR) +// if(buckled && buckled == AM) +// return // Don't get hit by the thing we're buckled to. + + //VORESTATION EDIT START - Allows for thrown vore! + //Throwing a prey into a pred takes priority. After that it checks to see if the person being thrown is a pred. + // I put more comments here for ease of reading. + if(istype(AM, /mob/living)) + var/mob/living/thrown_mob = AM + if(isanimal(thrown_mob) && !allowmobvore) //Is the thrown_mob an animal and we don't allow mobvore? + return + // PERSON BEING HIT: CAN BE DROP PRED, ALLOWS THROW VORE. + // PERSON BEING THROWN: DEVOURABLE, ALLOWS THROW VORE, CAN BE DROP PREY. + if((can_be_drop_pred && throw_vore && vore_selected) && (thrown_mob.devourable && thrown_mob.throw_vore && thrown_mob.can_be_drop_prey)) //Prey thrown into pred. + vore_selected.nom_mob(thrown_mob) //Eat them!!! + visible_message("[thrown_mob] is thrown right into [src]'s [lowertext(vore_selected.name)]!") + if(thrown_mob.loc != vore_selected) + thrown_mob.forceMove(vore_selected) //Double check. Should never happen but...Weirder things have happened! + add_attack_logs(thrown_mob.thrower,src,"Devoured [thrown_mob.name] via throw vore.") + return //We can stop here. We don't need to calculate damage or anything else. They're eaten. + + // PERSON BEING HIT: CAN BE DROP PREY, ALLOWS THROW VORE, AND IS DEVOURABLE. + // PERSON BEING THROWN: CAN BE DROP PRED, ALLOWS THROW VORE. + else if((can_be_drop_prey && throw_vore && devourable) && (thrown_mob.can_be_drop_pred && thrown_mob.throw_vore && thrown_mob.vore_selected)) //Pred thrown into prey. + visible_message("[src] suddenly slips inside of [thrown_mob]'s [lowertext(thrown_mob.vore_selected.name)] as [thrown_mob] flies into them!") + thrown_mob.vore_selected.nom_mob(src) //Eat them!!! + if(src.loc != thrown_mob.vore_selected) + src.forceMove(thrown_mob.vore_selected) //Double check. Should never happen but...Weirder things have happened! + add_attack_logs(thrown_mob.LAssailant,src,"Was Devoured by [thrown_mob.name] via throw vore.") + return + //VORESTATION EDIT END - Allows for thrown vore! + + if(istype(AM,/obj/)) + var/obj/O = AM + if(in_throw_mode && speed <= THROWFORCE_SPEED_DIVISOR) //empty active hand and we're in throw mode + if(canmove && !restrained()) + if(isturf(O.loc)) + if(can_catch(O)) + put_in_active_hand(O) + visible_message("[src] catches [O]!") + throw_mode_off() + return + + var/dtype = O.damtype + var/throw_damage = O.throwforce*(speed/THROWFORCE_SPEED_DIVISOR) + + if(species && species.throwforce_absorb_threshold >= throw_damage) + visible_message("\The [O] simply bounces off of [src]'s body!") + return + + var/zone + if (istype(O.thrower, /mob/living)) + var/mob/living/L = O.thrower + zone = check_zone(L.zone_sel.selecting) + else + zone = ran_zone(BP_TORSO,75) //Hits a random part of the body, geared towards the chest + + //check if we hit + var/miss_chance = 15 + if (O.throw_source) + var/distance = get_dist(O.throw_source, loc) + miss_chance = max(15*(distance-2), 0) + zone = get_zone_with_miss_chance(zone, src, miss_chance, ranged_attack=1) + + if(zone && O.thrower != src) + var/shield_check = check_shields(throw_damage, O, thrower, zone, "[O]") + if(shield_check == PROJECTILE_FORCE_MISS) + zone = null + else if(shield_check) + return + + if(!zone) + visible_message("\The [O] misses [src] narrowly!") + return + + O.throwing = 0 //it hit, so stop moving + + var/obj/item/organ/external/affecting = get_organ(zone) + var/hit_area = affecting.name + + src.visible_message("[span_red("[src] has been hit in the [hit_area] by [O].")]") + + if(ismob(O.thrower)) + add_attack_logs(O.thrower,src,"Hit with thrown [O.name]") + + //If the armor absorbs all of the damage, skip the rest of the calculations + var/soaked = get_armor_soak(affecting, "melee", O.armor_penetration) + if(soaked >= throw_damage) + to_chat(src, "Your armor absorbs the force of [O.name]!") + return + + var/armor = run_armor_check(affecting, "melee", O.armor_penetration, "Your armor has protected your [hit_area].", "Your armor has softened hit to your [hit_area].") //I guess "melee" is the best fit here + if(armor < 100) + apply_damage(throw_damage, dtype, zone, armor, soaked, is_sharp(O), has_edge(O), O) + + + //thrown weapon embedded object code. + if(dtype == BRUTE && istype(O,/obj/item)) + var/obj/item/I = O + if (!is_robot_module(I)) + var/sharp = is_sharp(I) + var/damage = throw_damage + if (soaked) + damage -= soaked + if (armor) + damage /= armor+1 + + //blunt objects should really not be embedding in things unless a huge amount of force is involved + var/embed_chance = sharp? damage/I.w_class : damage/(I.w_class*3) + var/embed_threshold = sharp? 5*I.w_class : 15*I.w_class + + //Sharp objects will always embed if they do enough damage. + //Thrown sharp objects have some momentum already and have a small chance to embed even if the damage is below the threshold + if((sharp && prob(damage/(10*I.w_class)*100)) || (damage > embed_threshold && prob(embed_chance))) + affecting.embed(I) + + // Begin BS12 momentum-transfer code. + var/mass = 1.5 + if(istype(O, /obj/item)) + var/obj/item/I = O + mass = I.w_class/THROWNOBJ_KNOCKBACK_DIVISOR + var/momentum = speed*mass + + if(O.throw_source && momentum >= THROWNOBJ_KNOCKBACK_SPEED && !buckled) + var/dir = get_dir(O.throw_source, src) + + visible_message("[span_red("[src] staggers under the impact!")]","[span_red("You stagger under the impact!")]") + src.throw_at(get_edge_target_turf(src,dir),1,momentum) + + if(!O || !src) return + + if(O.loc == src && O.sharp) //Projectile is embedded and suitable for pinning. + var/turf/T = near_wall(dir,2) + + if(T) + src.loc = T + visible_message("[src] is pinned to the wall by [O]!","You are pinned to the wall by [O]!") + src.anchored = TRUE + src.pinned += O + +// This does a prob check to catch the thing flying at you, with a minimum of 1% +/mob/living/carbon/human/proc/can_catch(var/obj/O) + if(!get_active_hand()) // If active hand is empty + var/obj/item/organ/external/temp = organs_by_name["r_hand"] + if (hand) + temp = organs_by_name["l_hand"] + if(temp && !temp.is_usable()) + return FALSE // The hand isn't working in the first place + + if(!O.catchable) + return FALSE + + // Alright, our hand works? Time to try the catching. + var/catch_chance = 90 // Default 90% catch rate + + if(O.sharp) + catch_chance -= 50 // Catching knives is hard + + catch_chance -= get_accuracy_penalty() // Same issues with shooting a gun, or swinging a weapon + + catch_chance = between(1, catch_chance, 100) + + if(prob(catch_chance)) + return TRUE + return FALSE + +/mob/living/carbon/human/embed(var/obj/O, var/def_zone=null) + if(!def_zone) ..() + + var/obj/item/organ/external/affecting = get_organ(def_zone) + if(affecting) + affecting.embed(O) + + +/mob/living/carbon/human/proc/bloody_hands(var/mob/living/source, var/amount = 2) + if (gloves) + gloves.add_blood(source) + gloves:transfer_blood = amount + gloves:bloody_hands_mob = source + else + add_blood(source) + bloody_hands = amount + bloody_hands_mob = source + update_inv_gloves() //updates on-mob overlays for bloody hands and/or bloody gloves + +/mob/living/carbon/human/proc/bloody_body(var/mob/living/source) + if(wear_suit) + wear_suit.add_blood(source) + update_inv_wear_suit(0) + if(w_uniform) + w_uniform.add_blood(source) + update_inv_w_uniform(0) + +/mob/living/carbon/human/proc/handle_suit_punctures(var/damtype, var/damage, var/def_zone) + + // Tox and oxy don't matter to suits. + if(damtype != BURN && damtype != BRUTE) return + + // The rig might soak this hit, if we're wearing one. + if(istype(get_rig(),/obj/item/weapon/rig)) + var/obj/item/weapon/rig/rig = get_rig() + rig.take_hit(damage) + + // We may also be taking a suit breach. + if(!wear_suit) return + if(!istype(wear_suit,/obj/item/clothing/suit/space)) return + var/obj/item/clothing/suit/space/SS = wear_suit + var/penetrated_dam = max(0,(damage - SS.breach_threshold)) + if(penetrated_dam) SS.create_breaches(damtype, penetrated_dam) + +/mob/living/carbon/human/reagent_permeability() + var/perm = 0 + + var/list/perm_by_part = list( + "head" = THERMAL_PROTECTION_HEAD, + "upper_torso" = THERMAL_PROTECTION_UPPER_TORSO, + "lower_torso" = THERMAL_PROTECTION_LOWER_TORSO, + "legs" = THERMAL_PROTECTION_LEG_LEFT + THERMAL_PROTECTION_LEG_RIGHT, + "feet" = THERMAL_PROTECTION_FOOT_LEFT + THERMAL_PROTECTION_FOOT_RIGHT, + "arms" = THERMAL_PROTECTION_ARM_LEFT + THERMAL_PROTECTION_ARM_RIGHT, + "hands" = THERMAL_PROTECTION_HAND_LEFT + THERMAL_PROTECTION_HAND_RIGHT + ) + + for(var/obj/item/clothing/C in src.get_equipped_items()) + if(C.permeability_coefficient == 1 || !C.body_parts_covered) + continue + if(C.body_parts_covered & HEAD) + perm_by_part["head"] *= C.permeability_coefficient + if(C.body_parts_covered & UPPER_TORSO) + perm_by_part["upper_torso"] *= C.permeability_coefficient + if(C.body_parts_covered & LOWER_TORSO) + perm_by_part["lower_torso"] *= C.permeability_coefficient + if(C.body_parts_covered & LEGS) + perm_by_part["legs"] *= C.permeability_coefficient + if(C.body_parts_covered & FEET) + perm_by_part["feet"] *= C.permeability_coefficient + if(C.body_parts_covered & ARMS) + perm_by_part["arms"] *= C.permeability_coefficient + if(C.body_parts_covered & HANDS) + perm_by_part["hands"] *= C.permeability_coefficient + + for(var/part in perm_by_part) + perm += perm_by_part[part] + + return perm + +// This is for preventing harm by being covered in water, which only prometheans need to deal with. +/mob/living/carbon/human/get_water_protection() + var/protection = species.water_resistance + if(protection == 1) // No point doing permeability checks if it won't matter. + return protection + // Wearing clothing with a low permeability_coefficient can protect from water. + + var/converted_protection = 1 - protection + var/perm = reagent_permeability() + converted_protection *= perm + return CLAMP(1-converted_protection, 0, 1) + +/mob/living/carbon/human/water_act(amount) + adjust_fire_stacks(-amount * 5) + for(var/atom/movable/AM in contents) + AM.water_act(amount) + remove_modifiers_of_type(/datum/modifier/fire) + + species.handle_water_damage(src, amount) + +/mob/living/carbon/human/shank_attack(obj/item/W, obj/item/weapon/grab/G, mob/user, hit_zone) + + if(!..()) + return 0 + + var/organ_chance = 50 + var/damage = shank_armor_helper(W, G, user) + var/obj/item/organ/external/chest = get_organ(hit_zone) + + if(W.edge) + organ_chance = 75 + user.next_move = world.time + 20 + user.visible_message("\The [user] begins to twist \the [W] around inside [src]'s [chest]!") + if(!do_after(user, 20)) + return 0 + if(!(G && G.assailant == user && G.affecting == src)) //check that we still have a grab + return 0 + + user.visible_message("\The [user] twists \the [W] around inside [src]'s [chest]!") + + if(prob(organ_chance)) + var/obj/item/organ/internal/selected_organ = pick(chest.internal_organs) + selected_organ.damage = max(selected_organ.damage, damage * 0.5) + G.last_action = world.time + flick(G.hud.icon_state, G.hud) + + return 1 diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index a3be484060b..f2e09405ee4 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -1,163 +1,163 @@ -/mob/living/carbon/human - //Hair colour and style - var/r_hair = 0 - var/g_hair = 0 - var/b_hair = 0 - var/h_style = "Bald" - - var/r_grad = 0 - var/g_grad = 0 - var/b_grad = 0 - var/grad_style = "none" - //Facial hair colour and style - var/r_facial = 0 - var/g_facial = 0 - var/b_facial = 0 - var/f_style = "Shaved" - - //Eye colour - var/r_eyes = 0 - var/g_eyes = 0 - var/b_eyes = 0 - - var/s_tone = 0 //Skin tone - - //Skin colour - var/r_skin = 0 - var/g_skin = 0 - var/b_skin = 0 - - var/skin_state = SKIN_NORMAL - - //Synth colors - var/synth_color = 0 //Lets normally uncolorable synth parts be colorable. - var/r_synth //Used with synth_color to color synth parts that normaly can't be colored. - var/g_synth //Same as above - var/b_synth //Same as above - var/synth_markings = 0 //Enables/disables markings on synth parts. - - var/digitigrade = 0 // 0 = no digi, 1 = default, 2+ = digi styles... (Not used yet) - - //var/size_multiplier = 1 //multiplier for the mob's icon size //VOREStation Edit (Moved to /mob/living) - var/damage_multiplier = 1 //multiplies melee combat damage - var/icon_update = 1 //whether icon updating shall take place - - var/lip_style = null //no lipstick by default- arguably misleading, as it could be used for general makeup - - var/age = 30 //Player's age (pure fluff) - var/bday_month = 0 //Character birth month - var/bday_day = 0 //Character birthday day - - var/b_type = "A+" //Player's bloodtype - var/datum/robolimb/synthetic //If they are a synthetic (aka synthetic torso). Also holds the datum for the type of robolimb. - - var/list/all_underwear = list() - var/list/all_underwear_metadata = list() - var/list/hide_underwear = list() - var/backbag = 2 //Which backpack type the player has chosen. - var/pdachoice = 1 //Which PDA type the player has chosen. - - // General information - var/home_system = "" - var/birthplace = "" - var/citizenship = "" - var/personal_faction = "" - var/religion = "" - var/antag_faction = "" - var/antag_vis = "" - - //Equipment slots - var/obj/item/wear_suit = null - var/obj/item/w_uniform = null - var/obj/item/shoes = null - var/obj/item/belt = null - var/obj/item/gloves = null - var/obj/item/glasses = null - var/obj/item/head = null - var/obj/item/l_ear = null - var/obj/item/r_ear = null - var/obj/item/wear_id = null - var/obj/item/r_store = null - var/obj/item/l_store = null - var/obj/item/s_store = null - - var/used_skillpoints = 0 - var/skill_specialization = null - var/list/skills = list() - - var/voice = "" //Instead of new say code calling GetVoice() over and over and over, we're just going to ask this variable, which gets updated in Life() - - var/special_voice = "" // For changing our voice. Used by a symptom. - - var/last_dam = -1 //Used for determining if we need to process all organs or just some or even none. - - var/xylophone = 0 //For the spoooooooky xylophone cooldown - - var/mob/remoteview_target = null - var/hand_blood_color - - var/list/flavor_texts = list() - var/gunshot_residue - var/pulling_punches // Are you trying not to hurt your opponent? - var/robolimb_count = 0 // Total number of external robot parts. - var/robobody_count = 0 // Counts torso, groin, and head, if they're robotic - - mob_bump_flag = HUMAN - mob_push_flags = ~HEAVY - mob_swap_flags = ~HEAVY - - var/identifying_gender // In case the human identifies as another gender than it's biological - - var/list/descriptors // For comparative examine code - - var/step_count = 0 // Track how many footsteps have been taken to know when to play footstep sounds - - can_be_antagged = TRUE - -// Used by mobs in virtual reality to point back to the "real" mob the client belongs to. - var/mob/living/carbon/human/vr_holder = null - // Used by "real" mobs after they leave a VR session - var/mob/living/carbon/human/vr_link = null - - var/obj/machinery/machine_visual //machine that is currently applying visual effects to this mob. Only used for camera monitors currently. - - inventory_panel_type = /datum/inventory_panel/human - butchery_loot = list(/obj/item/stack/animalhide/human = 1) - - // Horray Furries! - var/datum/sprite_accessory/ears/ear_style = null - var/r_ears = 30 - var/g_ears = 30 - var/b_ears = 30 - var/r_ears2 = 30 - var/g_ears2 = 30 - var/b_ears2 = 30 - var/r_ears3 = 30 //Trust me, we could always use more colour. No japes. - var/g_ears3 = 30 - var/b_ears3 = 30 - var/datum/sprite_accessory/tail/tail_style = null - var/r_tail = 30 - var/g_tail = 30 - var/b_tail = 30 - var/r_tail2 = 30 - var/g_tail2 = 30 - var/b_tail2 = 30 - var/r_tail3 = 30 - var/g_tail3 = 30 - var/b_tail3 = 30 - var/datum/sprite_accessory/wing/wing_style = null - var/r_wing = 30 - var/g_wing = 30 - var/b_wing = 30 - var/r_wing2 = 30 - var/g_wing2 = 30 - var/b_wing2 = 30 - var/r_wing3 = 30 - var/g_wing3 = 30 - var/b_wing3 = 30 - - var/wagging = 0 //UGH. - var/flapping = 0 - - // Custom Species Name - var/custom_species +/mob/living/carbon/human + //Hair colour and style + var/r_hair = 0 + var/g_hair = 0 + var/b_hair = 0 + var/h_style = "Bald" + + var/r_grad = 0 + var/g_grad = 0 + var/b_grad = 0 + var/grad_style = "none" + //Facial hair colour and style + var/r_facial = 0 + var/g_facial = 0 + var/b_facial = 0 + var/f_style = "Shaved" + + //Eye colour + var/r_eyes = 0 + var/g_eyes = 0 + var/b_eyes = 0 + + var/s_tone = 0 //Skin tone + + //Skin colour + var/r_skin = 0 + var/g_skin = 0 + var/b_skin = 0 + + var/skin_state = SKIN_NORMAL + + //Synth colors + var/synth_color = 0 //Lets normally uncolorable synth parts be colorable. + var/r_synth //Used with synth_color to color synth parts that normaly can't be colored. + var/g_synth //Same as above + var/b_synth //Same as above + var/synth_markings = 0 //Enables/disables markings on synth parts. + + var/digitigrade = 0 // 0 = no digi, 1 = default, 2+ = digi styles... (Not used yet) + + //var/size_multiplier = 1 //multiplier for the mob's icon size //VOREStation Edit (Moved to /mob/living) + var/damage_multiplier = 1 //multiplies melee combat damage + var/icon_update = 1 //whether icon updating shall take place + + var/lip_style = null //no lipstick by default- arguably misleading, as it could be used for general makeup + + var/age = 30 //Player's age (pure fluff) + var/bday_month = 0 //Character birth month + var/bday_day = 0 //Character birthday day + + var/b_type = "A+" //Player's bloodtype + var/datum/robolimb/synthetic //If they are a synthetic (aka synthetic torso). Also holds the datum for the type of robolimb. + + var/list/all_underwear = list() + var/list/all_underwear_metadata = list() + var/list/hide_underwear = list() + var/backbag = 2 //Which backpack type the player has chosen. + var/pdachoice = 1 //Which PDA type the player has chosen. + + // General information + var/home_system = "" + var/birthplace = "" + var/citizenship = "" + var/personal_faction = "" + var/religion = "" + var/antag_faction = "" + var/antag_vis = "" + + //Equipment slots + var/obj/item/wear_suit = null + var/obj/item/w_uniform = null + var/obj/item/shoes = null + var/obj/item/belt = null + var/obj/item/gloves = null + var/obj/item/glasses = null + var/obj/item/head = null + var/obj/item/l_ear = null + var/obj/item/r_ear = null + var/obj/item/wear_id = null + var/obj/item/r_store = null + var/obj/item/l_store = null + var/obj/item/s_store = null + + var/used_skillpoints = 0 + var/skill_specialization = null + var/list/skills = list() + + var/voice = "" //Instead of new say code calling GetVoice() over and over and over, we're just going to ask this variable, which gets updated in Life() + + var/special_voice = "" // For changing our voice. Used by a symptom. + + var/last_dam = -1 //Used for determining if we need to process all organs or just some or even none. + + var/xylophone = 0 //For the spoooooooky xylophone cooldown + + var/mob/remoteview_target = null + var/hand_blood_color + + var/list/flavor_texts = list() + var/gunshot_residue + var/pulling_punches // Are you trying not to hurt your opponent? + var/robolimb_count = 0 // Total number of external robot parts. + var/robobody_count = 0 // Counts torso, groin, and head, if they're robotic + + mob_bump_flag = HUMAN + mob_push_flags = ~HEAVY + mob_swap_flags = ~HEAVY + + var/identifying_gender // In case the human identifies as another gender than it's biological + + var/list/descriptors // For comparative examine code + + var/step_count = 0 // Track how many footsteps have been taken to know when to play footstep sounds + + can_be_antagged = TRUE + +// Used by mobs in virtual reality to point back to the "real" mob the client belongs to. + var/mob/living/carbon/human/vr_holder = null + // Used by "real" mobs after they leave a VR session + var/mob/living/carbon/human/vr_link = null + + var/obj/machinery/machine_visual //machine that is currently applying visual effects to this mob. Only used for camera monitors currently. + + inventory_panel_type = /datum/inventory_panel/human + butchery_loot = list(/obj/item/stack/animalhide/human = 1) + + // Horray Furries! + var/datum/sprite_accessory/ears/ear_style = null + var/r_ears = 30 + var/g_ears = 30 + var/b_ears = 30 + var/r_ears2 = 30 + var/g_ears2 = 30 + var/b_ears2 = 30 + var/r_ears3 = 30 //Trust me, we could always use more colour. No japes. + var/g_ears3 = 30 + var/b_ears3 = 30 + var/datum/sprite_accessory/tail/tail_style = null + var/r_tail = 30 + var/g_tail = 30 + var/b_tail = 30 + var/r_tail2 = 30 + var/g_tail2 = 30 + var/b_tail2 = 30 + var/r_tail3 = 30 + var/g_tail3 = 30 + var/b_tail3 = 30 + var/datum/sprite_accessory/wing/wing_style = null + var/r_wing = 30 + var/g_wing = 30 + var/b_wing = 30 + var/r_wing2 = 30 + var/g_wing2 = 30 + var/b_wing2 = 30 + var/r_wing3 = 30 + var/g_wing3 = 30 + var/b_wing3 = 30 + + var/wagging = 0 //UGH. + var/flapping = 0 + + // Custom Species Name + var/custom_species diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 3f10aeeae06..65b4039cddc 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -1,304 +1,304 @@ -#define HUMAN_LOWEST_SLOWDOWN -3 - -/mob/living/carbon/human/movement_delay(oldloc, direct) - - . = 0 - - if (istype(loc, /turf/space)) - return ..() - 1 - - if(species.slowdown) - . += species.slowdown - - if(force_max_speed) - return ..() + HUMAN_LOWEST_SLOWDOWN - - for(var/datum/modifier/M in modifiers) - if(!isnull(M.haste) && M.haste == TRUE) - return ..() + HUMAN_LOWEST_SLOWDOWN // Returning -1 will actually result in a slowdown for Teshari. - if(!isnull(M.slowdown)) - . += M.slowdown - - var/health_deficiency = (getMaxHealth() - health) - if(istype(src, /mob/living/carbon/human)) //VOREStation Edit Start - var/mob/living/carbon/human/H = src - health_deficiency *= H.species.trauma_mod //Species pain sensitivity does not apply to painkillers, so we apply it before - if(health_deficiency >= 40) - if(chem_effects[CE_PAINKILLER]) //On painkillers? Reduce pain! On anti-painkillers? Increase pain! - health_deficiency = max(0, health_deficiency - src.chem_effects[CE_PAINKILLER]) - if(health_deficiency >= 40) //Still in enough pain for it to be significant? - . += (health_deficiency / 25) //VOREStation Edit End - - if(can_feel_pain()) - if(halloss >= 10) . += (halloss / 10) //halloss shouldn't slow you down if you can't even feel it - - var/hungry = (500 - nutrition) / 5 //VOREStation Edit - Fixed 500 here instead of our huge MAX_NUTRITION - if (hungry >= 70) . += hungry/50 - - //VOREstation start - if (feral >= 10) //crazy feral animals give less and less of a shit about pain and hunger as they get crazier - . = max(species.slowdown, species.slowdown+((.-species.slowdown)/(feral/10))) // As feral scales to damage, this amounts to an effective +1 slowdown cap - if(shock_stage >= 10) . -= 1.5 //this gets a +3 later, feral critters take reduced penalty - if(riding_datum) //Bit of slowdown for taur rides if rider is bigger or fatter than mount. - var/datum/riding/R = riding_datum - var/mob/living/L = R.ridden - for(var/mob/living/M in L.buckled_mobs) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.size_multiplier > L.size_multiplier) - . += 1 - //if(H.weight > L.weight) weight should not have mechanical impact - //. += 1 - //VOREstation end - - if(istype(buckled, /obj/structure/bed/chair/wheelchair)) - for(var/organ_name in list(BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM)) - var/obj/item/organ/external/E = get_organ(organ_name) - if(!E || E.is_stump()) - . += 4 - else if(E.splinted && E.splinted.loc != E) - . += 0.5 - else if(E.status & ORGAN_BROKEN) - . += 1.5 - else - for(var/organ_name in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) - var/obj/item/organ/external/E = get_organ(organ_name) - if(!E || E.is_stump()) - . += 4 - else if(E.splinted && E.splinted.loc != E) - . += 0.5 - else if(E.status & ORGAN_BROKEN) - . += 1.5 - - if(shock_stage >= 10) . += 3 - - if(aiming && aiming.aiming_at) . += 5 // Iron sights make you slower, it's a well-known fact. - - if(FAT in src.mutations) - . += 1.5 - - if (bodytemperature < species.cold_level_1) - . += (species.cold_level_1 - bodytemperature) / 10 * 1.75 - - . += max(2 * stance_damage, 0) //damaged/missing feet or legs is slow - - if(mRun in mutations) - . = 0 - - // Turf related slowdown - var/turf/T = get_turf(src) - . += calculate_turf_slowdown(T, direct) - - // Item related slowdown. - var/item_tally = calculate_item_encumbrance() - - // Dragging heavy objects will also slow you down, similar to above. - if(pulling) - if(istype(pulling, /obj/item)) - var/obj/item/pulled = pulling - item_tally += max(pulled.slowdown, 0) - else if(ishuman(pulling)) - var/mob/living/carbon/human/H = pulling - var/their_slowdown = max(H.calculate_item_encumbrance(), 1) - item_tally = max(item_tally, their_slowdown) // If our slowdown is less than theirs, then we become as slow as them (before species modifires). - - item_tally *= species.item_slowdown_mod - - . += item_tally - - if(CE_SLOWDOWN in chem_effects) - if (. >= 0 ) - . *= 1.25 //Add a quarter of penalties on top. - . += chem_effects[CE_SLOWDOWN] - - if(CE_SPEEDBOOST in chem_effects) - if (. >= 0) // cut any penalties in half - . *= 0.5 - . -= chem_effects[CE_SPEEDBOOST] // give 'em a buff on top. - - . = max(HUMAN_LOWEST_SLOWDOWN, . + config.human_delay) // Minimum return should be the same as force_max_speed - . += ..() - -/mob/living/carbon/human/Moved() - . = ..() - if(embedded_flag) - handle_embedded_objects() //Moving with objects stuck in you can cause bad times. - -// This calculates the amount of slowdown to receive from items worn. This does NOT include species modifiers. -// It is in a seperate place to avoid an infinite loop situation with dragging mobs dragging each other. -// Also its nice to have these things seperated. -/mob/living/carbon/human/proc/calculate_item_encumbrance() - if(shoes) // Shoes can make you go faster. - if(!buckled || (buckled && istype(buckled, /obj/machinery/power/rtg/reg))) - . += shoes.slowdown - - // Loop through some slots, and add up their slowdowns. - // Includes slots which can provide armor, the back slot, and suit storage. - for(var/obj/item/I in list(wear_suit, w_uniform, back, gloves, head, s_store)) - . += I.slowdown - - // Hands are also included, to make the 'take off your armor instantly and carry it with you to go faster' trick no longer viable. - // This is done seperately to disallow negative numbers (so you can't hold shoes in your hands to go faster). - for(var/obj/item/I in list(r_hand, l_hand) ) - . += max(I.slowdown, 0) - -// Similar to above, but for turf slowdown. -/mob/living/carbon/human/proc/calculate_turf_slowdown(turf/T, direct) - if(!T) - return 0 - - if(T.movement_cost) - var/turf_move_cost = T.movement_cost - if(istype(T, /turf/simulated/floor/water)) - if(species.water_movement) - turf_move_cost = CLAMP(turf_move_cost + species.water_movement, HUMAN_LOWEST_SLOWDOWN, 15) - if(shoes) - var/obj/item/clothing/shoes/feet = shoes - if(istype(feet) && feet.water_speed) - turf_move_cost = CLAMP(turf_move_cost + feet.water_speed, HUMAN_LOWEST_SLOWDOWN, 15) - . += turf_move_cost - else if(istype(T, /turf/simulated/floor/outdoors/snow)) - if(species.snow_movement) - turf_move_cost = CLAMP(turf_move_cost + species.snow_movement, HUMAN_LOWEST_SLOWDOWN, 15) - if(shoes) - var/obj/item/clothing/shoes/feet = shoes - if(istype(feet) && feet.snow_speed) - turf_move_cost = CLAMP(turf_move_cost + feet.snow_speed, HUMAN_LOWEST_SLOWDOWN, 15) - . += turf_move_cost - else - turf_move_cost = CLAMP(turf_move_cost, HUMAN_LOWEST_SLOWDOWN, 15) - . += turf_move_cost - - // Wind makes it easier or harder to move, depending on if you're with or against the wind. - // I don't like that so I'm commenting it out :) - // VOREstation Edit Start -/* - if((T.is_outdoors()) && (T.z <= SSplanets.z_to_planet.len)) - var/datum/planet/P = SSplanets.z_to_planet[z] - if(P) - var/datum/weather_holder/WH = P.weather_holder - if(WH && WH.wind_speed) // Is there any wind? - // With the wind. - if(direct & WH.wind_dir) - . = max(. - WH.wind_speed, -1) // Wind speedup is capped to prevent supersonic speeds from a storm. - // Against it. - else if(direct & reverse_dir[WH.wind_dir]) - . += WH.wind_speed - -*/ -// VOREstation Edit End. -#undef HUMAN_LOWEST_SLOWDOWN - -/mob/living/carbon/human/get_jetpack() - if(back) - var/obj/item/weapon/rig/rig = get_rig() - if(istype(back, /obj/item/weapon/tank/jetpack)) - return back - else if(istype(rig)) - for(var/obj/item/rig_module/maneuvering_jets/module in rig.installed_modules) - return module.jets - -/mob/living/carbon/human/Process_Spacemove(var/check_drift = 0) - //Can we act? - if(restrained()) return 0 - - if(..()) //Can move due to other reasons, don't use jetpack fuel - return TRUE - - if(species.can_space_freemove || (species.can_zero_g_move && !istype(get_turf(src), /turf/space))) //VOREStation Edit. - return TRUE //VOREStation Edit. - - if(flying) //VOREStation Edit. If you're flying, you glide around! - return TRUE //VOREStation Edit. - - //Do we have a working jetpack? - var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() - - if(thrust) - if(((!check_drift) || (check_drift && thrust.stabilization_on)) && (!lying) && (thrust.do_thrust(0.01, src))) - inertia_dir = 0 - return TRUE - - return FALSE - - -/mob/living/carbon/human/Process_Spaceslipping(var/prob_slip = 5) - //If knocked out we might just hit it and stop. This makes it possible to get dead bodies and such. - - if(species.flags & NO_SLIP) - return FALSE - - if(species.can_space_freemove || species.can_zero_g_move) - return FALSE - - var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() - if(thrust?.can_thrust(0.01)) - return FALSE - - if(stat) - prob_slip = 0 // Changing this to zero to make it line up with the comment, and also, make more sense. - - //Do we have magboots or such on if so no slip - if(istype(shoes, /obj/item/clothing/shoes/magboots) && (shoes.item_flags & NOSLIP)) - prob_slip = 0 - - //Check hands and mod slip - if(!l_hand) prob_slip -= 2 - else if(l_hand.w_class <= 2) prob_slip -= 1 - if (!r_hand) prob_slip -= 2 - else if(r_hand.w_class <= 2) prob_slip -= 1 - - prob_slip = round(prob_slip) - return(prob_slip) - -// Handle footstep sounds -/mob/living/carbon/human/handle_footstep(var/turf/T) - if(!istype(T)) - return - if(is_incorporeal()) - return - if(!config.footstep_volume || !T.footstep_sounds || !T.footstep_sounds.len) - return - // Future Upgrades - Multi species support - var/list/footstep_sounds = T.footstep_sounds["human"] - if(!footstep_sounds) - return - - var/S = pick(footstep_sounds) - GLOB.step_taken_shift_roundstat++ - if(!S) return - - // Play every 20 steps while walking, for the sneak - if(m_intent == "walk" && step_count++ % 20 != 0) - return - - // Play every other step while running - if(m_intent == "run" && step_count++ % 2 != 0) - return - - var/volume = config.footstep_volume - - // Reduce volume while walking or barefoot - if(!shoes || m_intent == "walk") - volume *= 0.5 - else if(shoes) - var/obj/item/clothing/shoes/feet = shoes - if(istype(feet)) - volume *= feet.step_volume_mod - - if(!has_organ(BP_L_FOOT) && !has_organ(BP_R_FOOT)) - return // no feet = no footsteps - - if(buckled || lying || throwing) - return // people flying, lying down or sitting do not step - - if(!has_gravity(src) && prob(75)) - return // Far less likely to make noise in no gravity - - playsound(T, S, volume, FALSE) - return - -/mob/living/carbon/human/set_dir(var/new_dir) - . = ..() - if(. && (species.tail || tail_style)) +#define HUMAN_LOWEST_SLOWDOWN -3 + +/mob/living/carbon/human/movement_delay(oldloc, direct) + + . = 0 + + if (istype(loc, /turf/space)) + return ..() - 1 + + if(species.slowdown) + . += species.slowdown + + if(force_max_speed) + return ..() + HUMAN_LOWEST_SLOWDOWN + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.haste) && M.haste == TRUE) + return ..() + HUMAN_LOWEST_SLOWDOWN // Returning -1 will actually result in a slowdown for Teshari. + if(!isnull(M.slowdown)) + . += M.slowdown + + var/health_deficiency = (getMaxHealth() - health) + if(istype(src, /mob/living/carbon/human)) //VOREStation Edit Start + var/mob/living/carbon/human/H = src + health_deficiency *= H.species.trauma_mod //Species pain sensitivity does not apply to painkillers, so we apply it before + if(health_deficiency >= 40) + if(chem_effects[CE_PAINKILLER]) //On painkillers? Reduce pain! On anti-painkillers? Increase pain! + health_deficiency = max(0, health_deficiency - src.chem_effects[CE_PAINKILLER]) + if(health_deficiency >= 40) //Still in enough pain for it to be significant? + . += (health_deficiency / 25) //VOREStation Edit End + + if(can_feel_pain()) + if(halloss >= 10) . += (halloss / 10) //halloss shouldn't slow you down if you can't even feel it + + var/hungry = (500 - nutrition) / 5 //VOREStation Edit - Fixed 500 here instead of our huge MAX_NUTRITION + if (hungry >= 70) . += hungry/50 + + //VOREstation start + if (feral >= 10) //crazy feral animals give less and less of a shit about pain and hunger as they get crazier + . = max(species.slowdown, species.slowdown+((.-species.slowdown)/(feral/10))) // As feral scales to damage, this amounts to an effective +1 slowdown cap + if(shock_stage >= 10) . -= 1.5 //this gets a +3 later, feral critters take reduced penalty + if(riding_datum) //Bit of slowdown for taur rides if rider is bigger or fatter than mount. + var/datum/riding/R = riding_datum + var/mob/living/L = R.ridden + for(var/mob/living/M in L.buckled_mobs) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.size_multiplier > L.size_multiplier) + . += 1 + //if(H.weight > L.weight) weight should not have mechanical impact + //. += 1 + //VOREstation end + + if(istype(buckled, /obj/structure/bed/chair/wheelchair)) + for(var/organ_name in list(BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM)) + var/obj/item/organ/external/E = get_organ(organ_name) + if(!E || E.is_stump()) + . += 4 + else if(E.splinted && E.splinted.loc != E) + . += 0.5 + else if(E.status & ORGAN_BROKEN) + . += 1.5 + else + for(var/organ_name in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) + var/obj/item/organ/external/E = get_organ(organ_name) + if(!E || E.is_stump()) + . += 4 + else if(E.splinted && E.splinted.loc != E) + . += 0.5 + else if(E.status & ORGAN_BROKEN) + . += 1.5 + + if(shock_stage >= 10) . += 3 + + if(aiming && aiming.aiming_at) . += 5 // Iron sights make you slower, it's a well-known fact. + + if(FAT in src.mutations) + . += 1.5 + + if (bodytemperature < species.cold_level_1) + . += (species.cold_level_1 - bodytemperature) / 10 * 1.75 + + . += max(2 * stance_damage, 0) //damaged/missing feet or legs is slow + + if(mRun in mutations) + . = 0 + + // Turf related slowdown + var/turf/T = get_turf(src) + . += calculate_turf_slowdown(T, direct) + + // Item related slowdown. + var/item_tally = calculate_item_encumbrance() + + // Dragging heavy objects will also slow you down, similar to above. + if(pulling) + if(istype(pulling, /obj/item)) + var/obj/item/pulled = pulling + item_tally += max(pulled.slowdown, 0) + else if(ishuman(pulling)) + var/mob/living/carbon/human/H = pulling + var/their_slowdown = max(H.calculate_item_encumbrance(), 1) + item_tally = max(item_tally, their_slowdown) // If our slowdown is less than theirs, then we become as slow as them (before species modifires). + + item_tally *= species.item_slowdown_mod + + . += item_tally + + if(CE_SLOWDOWN in chem_effects) + if (. >= 0 ) + . *= 1.25 //Add a quarter of penalties on top. + . += chem_effects[CE_SLOWDOWN] + + if(CE_SPEEDBOOST in chem_effects) + if (. >= 0) // cut any penalties in half + . *= 0.5 + . -= chem_effects[CE_SPEEDBOOST] // give 'em a buff on top. + + . = max(HUMAN_LOWEST_SLOWDOWN, . + config.human_delay) // Minimum return should be the same as force_max_speed + . += ..() + +/mob/living/carbon/human/Moved() + . = ..() + if(embedded_flag) + handle_embedded_objects() //Moving with objects stuck in you can cause bad times. + +// This calculates the amount of slowdown to receive from items worn. This does NOT include species modifiers. +// It is in a seperate place to avoid an infinite loop situation with dragging mobs dragging each other. +// Also its nice to have these things seperated. +/mob/living/carbon/human/proc/calculate_item_encumbrance() + if(shoes) // Shoes can make you go faster. + if(!buckled || (buckled && istype(buckled, /obj/machinery/power/rtg/reg))) + . += shoes.slowdown + + // Loop through some slots, and add up their slowdowns. + // Includes slots which can provide armor, the back slot, and suit storage. + for(var/obj/item/I in list(wear_suit, w_uniform, back, gloves, head, s_store)) + . += I.slowdown + + // Hands are also included, to make the 'take off your armor instantly and carry it with you to go faster' trick no longer viable. + // This is done seperately to disallow negative numbers (so you can't hold shoes in your hands to go faster). + for(var/obj/item/I in list(r_hand, l_hand) ) + . += max(I.slowdown, 0) + +// Similar to above, but for turf slowdown. +/mob/living/carbon/human/proc/calculate_turf_slowdown(turf/T, direct) + if(!T) + return 0 + + if(T.movement_cost) + var/turf_move_cost = T.movement_cost + if(istype(T, /turf/simulated/floor/water)) + if(species.water_movement) + turf_move_cost = CLAMP(turf_move_cost + species.water_movement, HUMAN_LOWEST_SLOWDOWN, 15) + if(shoes) + var/obj/item/clothing/shoes/feet = shoes + if(istype(feet) && feet.water_speed) + turf_move_cost = CLAMP(turf_move_cost + feet.water_speed, HUMAN_LOWEST_SLOWDOWN, 15) + . += turf_move_cost + else if(istype(T, /turf/simulated/floor/outdoors/snow)) + if(species.snow_movement) + turf_move_cost = CLAMP(turf_move_cost + species.snow_movement, HUMAN_LOWEST_SLOWDOWN, 15) + if(shoes) + var/obj/item/clothing/shoes/feet = shoes + if(istype(feet) && feet.snow_speed) + turf_move_cost = CLAMP(turf_move_cost + feet.snow_speed, HUMAN_LOWEST_SLOWDOWN, 15) + . += turf_move_cost + else + turf_move_cost = CLAMP(turf_move_cost, HUMAN_LOWEST_SLOWDOWN, 15) + . += turf_move_cost + + // Wind makes it easier or harder to move, depending on if you're with or against the wind. + // I don't like that so I'm commenting it out :) + // VOREstation Edit Start +/* + if((T.is_outdoors()) && (T.z <= SSplanets.z_to_planet.len)) + var/datum/planet/P = SSplanets.z_to_planet[z] + if(P) + var/datum/weather_holder/WH = P.weather_holder + if(WH && WH.wind_speed) // Is there any wind? + // With the wind. + if(direct & WH.wind_dir) + . = max(. - WH.wind_speed, -1) // Wind speedup is capped to prevent supersonic speeds from a storm. + // Against it. + else if(direct & reverse_dir[WH.wind_dir]) + . += WH.wind_speed + +*/ +// VOREstation Edit End. +#undef HUMAN_LOWEST_SLOWDOWN + +/mob/living/carbon/human/get_jetpack() + if(back) + var/obj/item/weapon/rig/rig = get_rig() + if(istype(back, /obj/item/weapon/tank/jetpack)) + return back + else if(istype(rig)) + for(var/obj/item/rig_module/maneuvering_jets/module in rig.installed_modules) + return module.jets + +/mob/living/carbon/human/Process_Spacemove(var/check_drift = 0) + //Can we act? + if(restrained()) return 0 + + if(..()) //Can move due to other reasons, don't use jetpack fuel + return TRUE + + if(species.can_space_freemove || (species.can_zero_g_move && !istype(get_turf(src), /turf/space))) //VOREStation Edit. + return TRUE //VOREStation Edit. + + if(flying) //VOREStation Edit. If you're flying, you glide around! + return TRUE //VOREStation Edit. + + //Do we have a working jetpack? + var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() + + if(thrust) + if(((!check_drift) || (check_drift && thrust.stabilization_on)) && (!lying) && (thrust.do_thrust(0.01, src))) + inertia_dir = 0 + return TRUE + + return FALSE + + +/mob/living/carbon/human/Process_Spaceslipping(var/prob_slip = 5) + //If knocked out we might just hit it and stop. This makes it possible to get dead bodies and such. + + if(species.flags & NO_SLIP) + return FALSE + + if(species.can_space_freemove || species.can_zero_g_move) + return FALSE + + var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() + if(thrust?.can_thrust(0.01)) + return FALSE + + if(stat) + prob_slip = 0 // Changing this to zero to make it line up with the comment, and also, make more sense. + + //Do we have magboots or such on if so no slip + if(istype(shoes, /obj/item/clothing/shoes/magboots) && (shoes.item_flags & NOSLIP)) + prob_slip = 0 + + //Check hands and mod slip + if(!l_hand) prob_slip -= 2 + else if(l_hand.w_class <= 2) prob_slip -= 1 + if (!r_hand) prob_slip -= 2 + else if(r_hand.w_class <= 2) prob_slip -= 1 + + prob_slip = round(prob_slip) + return(prob_slip) + +// Handle footstep sounds +/mob/living/carbon/human/handle_footstep(var/turf/T) + if(!istype(T)) + return + if(is_incorporeal()) + return + if(!config.footstep_volume || !T.footstep_sounds || !T.footstep_sounds.len) + return + // Future Upgrades - Multi species support + var/list/footstep_sounds = T.footstep_sounds["human"] + if(!footstep_sounds) + return + + var/S = pick(footstep_sounds) + GLOB.step_taken_shift_roundstat++ + if(!S) return + + // Play every 20 steps while walking, for the sneak + if(m_intent == "walk" && step_count++ % 20 != 0) + return + + // Play every other step while running + if(m_intent == "run" && step_count++ % 2 != 0) + return + + var/volume = config.footstep_volume + + // Reduce volume while walking or barefoot + if(!shoes || m_intent == "walk") + volume *= 0.5 + else if(shoes) + var/obj/item/clothing/shoes/feet = shoes + if(istype(feet)) + volume *= feet.step_volume_mod + + if(!has_organ(BP_L_FOOT) && !has_organ(BP_R_FOOT)) + return // no feet = no footsteps + + if(buckled || lying || throwing) + return // people flying, lying down or sitting do not step + + if(!has_gravity(src) && prob(75)) + return // Far less likely to make noise in no gravity + + playsound(T, S, volume, FALSE) + return + +/mob/living/carbon/human/set_dir(var/new_dir) + . = ..() + if(. && (species.tail || tail_style)) update_tail_showing() \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index b1641597176..df9a03294c3 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -1,444 +1,444 @@ -/* -Add fingerprints to items when we put them in our hands. -This saves us from having to call add_fingerprint() any time something is put in a human's hands programmatically. -*/ - -/mob/living/carbon/human - var/list/worn_clothing = list() //Contains all CLOTHING items worn - -/mob/living/carbon/human/verb/quick_equip() - set name = "quick-equip" - set hidden = 1 - - if(ishuman(src)) - var/mob/living/carbon/human/H = src - var/obj/item/I = H.get_active_hand() - if(!I) - to_chat(H, "You are not holding anything to equip.") - return - - var/moved = FALSE - - // Try an equipment slot - if(H.equip_to_appropriate_slot(I)) - moved = TRUE - - // No? Try a storage item. - else if(H.equip_to_storage(I, TRUE)) - moved = TRUE - - // No?! Well, give up. - if(!moved) - to_chat(H, "You are unable to equip that.") - - // Update hand icons - else - if(hand) - update_inv_l_hand(0) - else - update_inv_r_hand(0) - -/mob/living/carbon/human/equip_to_storage(obj/item/newitem, user_initiated = FALSE) - // Try put it in their belt first - if(istype(src.belt,/obj/item/weapon/storage)) - var/obj/item/weapon/storage/wornbelt = src.belt - if(wornbelt.can_be_inserted(newitem, 1)) - if(user_initiated) - wornbelt.handle_item_insertion(newitem) - else - newitem.forceMove(wornbelt) - return wornbelt - - return ..() - -/mob/living/carbon/human/proc/equip_in_one_of_slots(obj/item/W, list/slots, del_on_fail = 1) - for (var/slot in slots) - if (equip_to_slot_if_possible(W, slots[slot], del_on_fail = 0)) - return slot - if (del_on_fail) - qdel(W) - return null - -/mob/living/carbon/human/proc/has_organ(name) - var/obj/item/organ/external/O = organs_by_name[name] - return (O && !O.is_stump()) - -/mob/living/carbon/human/proc/has_organ_for_slot(slot) - switch(slot) - if(slot_back) - return has_organ(BP_TORSO) - if(slot_wear_mask) - return has_organ(BP_HEAD) - if(slot_handcuffed) - return has_organ(BP_L_HAND) && has_organ(BP_R_HAND) - if(slot_legcuffed) - return has_organ(BP_L_FOOT) && has_organ(BP_R_FOOT) - if(slot_l_hand) - return has_organ(BP_L_HAND) - if(slot_r_hand) - return has_organ(BP_R_HAND) - if(slot_belt) - return has_organ(BP_TORSO) - if(slot_wear_id) - // the only relevant check for this is the uniform check - return 1 - if(slot_l_ear) - return has_organ(BP_HEAD) - if(slot_r_ear) - return has_organ(BP_HEAD) - if(slot_glasses) - return has_organ(BP_HEAD) - if(slot_gloves) - return has_organ(BP_L_HAND) || has_organ(BP_R_HAND) - if(slot_head) - return has_organ(BP_HEAD) - if(slot_shoes) - return has_organ(BP_L_FOOT) || has_organ(BP_R_FOOT) - if(slot_wear_suit) - return has_organ(BP_TORSO) - if(slot_w_uniform) - return has_organ(BP_TORSO) - if(slot_l_store) - return has_organ(BP_TORSO) - if(slot_r_store) - return has_organ(BP_TORSO) - if(slot_s_store) - return has_organ(BP_TORSO) - if(slot_in_backpack) - return 1 - if(slot_tie) - return 1 - -/obj/item/var/suitlink = 1 //makes belt items require a jumpsuit- set individual items to suitlink = 0 to allow wearing on belt slot without suit - -/mob/living/carbon/human/u_equip(obj/W as obj) - if(!W) return 0 - - if (W == wear_suit) - if(s_store) - drop_from_inventory(s_store) - worn_clothing -= wear_suit - wear_suit = null - update_inv_wear_suit() - else if (W == w_uniform) - if (r_store) - drop_from_inventory(r_store) - if (l_store) - drop_from_inventory(l_store) - if (wear_id) - drop_from_inventory(wear_id) - if (belt && belt.suitlink == 1) - worn_clothing -= belt - drop_from_inventory(belt) - worn_clothing -= w_uniform - w_uniform = null - update_inv_w_uniform() - else if (W == gloves) - worn_clothing -= gloves - gloves = null - update_inv_gloves() - else if (W == glasses) - worn_clothing -= glasses - glasses = null - update_inv_glasses() - else if (W == head) - worn_clothing -= head - head = null - if(istype(W, /obj/item)) - var/obj/item/I = W - if(I.flags_inv & (HIDEMASK|BLOCKHAIR|BLOCKHEADHAIR)) - update_hair(0) //rebuild hair - update_inv_ears(0) - update_inv_wear_mask(0) - update_inv_head() - else if (W == l_ear) - l_ear = null - update_inv_ears() - else if (W == r_ear) - r_ear = null - update_inv_ears() - else if (W == shoes) - worn_clothing -= shoes - shoes = null - update_inv_shoes() - else if (W == belt) - worn_clothing -= belt - belt = null - update_inv_belt() - else if (W == wear_mask) - worn_clothing -= wear_mask - wear_mask = null - if(istype(W, /obj/item)) - var/obj/item/I = W - if(I.flags_inv & (BLOCKHAIR|BLOCKHEADHAIR)) - update_hair(0) //rebuild hair - update_inv_ears(0) - // If this is how the internals are connected, disable them - if(internal && !(head?.item_flags & AIRTIGHT)) - if(internals) - internals.icon_state = "internal0" - internal = null - update_inv_wear_mask() - else if (W == wear_id) - wear_id = null - update_inv_wear_id() - BITSET(hud_updateflag, ID_HUD) - BITSET(hud_updateflag, WANTED_HUD) - else if (W == r_store) - r_store = null - //update_inv_pockets() //Doesn't do anything. - else if (W == l_store) - l_store = null - //update_inv_pockets() //Doesn't do anything. - else if (W == s_store) - s_store = null - update_inv_s_store() - else if (W == back) - worn_clothing -= back - back = null - update_inv_back() - else if (W == handcuffed) - handcuffed = null - if(buckled && buckled.buckle_require_restraints) - buckled.unbuckle_mob() - update_handcuffed() - else if (W == legcuffed) - legcuffed = null - update_inv_legcuffed() - else if (W == r_hand) - r_hand = null - if(l_hand) - l_hand.update_twohanding() - l_hand.update_held_icon() - update_inv_l_hand() - update_inv_r_hand() - else if (W == l_hand) - l_hand = null - if(r_hand) - r_hand.update_twohanding() - r_hand.update_held_icon() - update_inv_l_hand() - update_inv_l_hand() - else - return 0 - - update_action_buttons() - return 1 - - - -//This is an UNSAFE proc. Use mob_can_equip() before calling this one! Or rather use equip_to_slot_if_possible() or advanced_equip_to_slot_if_possible() -/mob/living/carbon/human/equip_to_slot(obj/item/W as obj, slot) - - if(!slot) - return - if(!istype(W)) - return - if(!has_organ_for_slot(slot)) - return - if(!species || !species.hud || !(slot in species.hud.equip_slots)) - return - - W.loc = src - switch(slot) - if(slot_back) - src.back = W - W.equipped(src, slot) - worn_clothing += back - update_inv_back() - if(slot_wear_mask) - src.wear_mask = W - if(wear_mask.flags_inv & (BLOCKHAIR|BLOCKHEADHAIR)) - update_hair() //rebuild hair - update_inv_ears() - W.equipped(src, slot) - worn_clothing += wear_mask - update_inv_wear_mask() - if(slot_handcuffed) - src.handcuffed = W - update_inv_handcuffed() - if(slot_legcuffed) - src.legcuffed = W - W.equipped(src, slot) - update_inv_legcuffed() - if(slot_l_hand) - src.l_hand = W - W.equipped(src, slot) - update_inv_l_hand() - if(slot_r_hand) - src.r_hand = W - W.equipped(src, slot) - update_inv_r_hand() - if(slot_belt) - src.belt = W - W.equipped(src, slot) - worn_clothing += belt - update_inv_belt() - if(slot_wear_id) - src.wear_id = W - W.equipped(src, slot) - update_inv_wear_id() - BITSET(hud_updateflag, ID_HUD) - BITSET(hud_updateflag, WANTED_HUD) - if(slot_l_ear) - src.l_ear = W - if(l_ear.slot_flags & SLOT_TWOEARS) - var/obj/item/clothing/ears/offear/O = new(W) - O.loc = src - src.r_ear = O - O.hud_layerise() - W.equipped(src, slot) - update_inv_ears() - if(slot_r_ear) - src.r_ear = W - if(r_ear.slot_flags & SLOT_TWOEARS) - var/obj/item/clothing/ears/offear/O = new(W) - O.loc = src - src.l_ear = O - O.hud_layerise() - W.equipped(src, slot) - update_inv_ears() - if(slot_glasses) - src.glasses = W - W.equipped(src, slot) - worn_clothing += glasses - update_inv_glasses() - if(slot_gloves) - src.gloves = W - W.equipped(src, slot) - worn_clothing += gloves - update_inv_gloves() - if(slot_head) - src.head = W - if(head.flags_inv & (BLOCKHAIR|BLOCKHEADHAIR|HIDEMASK)) - update_hair() //rebuild hair - update_inv_ears(0) - update_inv_wear_mask(0) - if(istype(W,/obj/item/clothing/head/kitty)) - W.update_icon(src) - W.equipped(src, slot) - worn_clothing += head - update_inv_head() - if(slot_shoes) - src.shoes = W - W.equipped(src, slot) - worn_clothing += shoes - update_inv_shoes() - if(slot_wear_suit) - src.wear_suit = W - W.equipped(src, slot) - worn_clothing += wear_suit - update_inv_wear_suit() - if(slot_w_uniform) - src.w_uniform = W - W.equipped(src, slot) - worn_clothing += w_uniform - update_inv_w_uniform() - if(slot_l_store) - src.l_store = W - W.equipped(src, slot) - //update_inv_pockets() //Doesn't do anything - if(slot_r_store) - src.r_store = W - W.equipped(src, slot) - //update_inv_pockets() //Doesn't do anything - if(slot_s_store) - src.s_store = W - W.equipped(src, slot) - update_inv_s_store() - if(slot_in_backpack) - if(src.get_active_hand() == W) - src.remove_from_mob(W) - W.loc = src.back - if(slot_tie) - for(var/obj/item/clothing/C in worn_clothing) - if(istype(W, /obj/item/clothing/accessory)) - var/obj/item/clothing/accessory/A = W - if(C.attempt_attach_accessory(A, src)) - return - else - to_chat(src, span_red("You are trying to equip this item to an unsupported inventory slot. How the heck did you manage that? Stop it...")) - return - - if((W == src.l_hand) && (slot != slot_l_hand)) - src.l_hand = null - update_inv_l_hand() //So items actually disappear from hands. - else if((W == src.r_hand) && (slot != slot_r_hand)) - src.r_hand = null - update_inv_r_hand() - - W.hud_layerise() - - if(W.action_button_name) - update_action_buttons() - - if(W.zoom) - W.zoom() - - W.in_inactive_hand(src) - - //VOREStation Addition Start - if(istype(W, /obj/item)) - var/obj/item/I = W - I.equip_special() - //VOREStation Addition End - - return 1 - -//Checks if a given slot can be accessed at this time, either to equip or unequip I -/mob/living/carbon/human/slot_is_accessible(var/slot, var/obj/item/I, mob/user=null) - var/obj/item/covering = null - var/check_flags = 0 - - switch(slot) - if(slot_wear_mask) - covering = src.head - check_flags = FACE - if(slot_glasses) - covering = src.head - check_flags = EYES - if(slot_gloves, slot_w_uniform) - covering = src.wear_suit - - if(covering && (covering.body_parts_covered & (I.body_parts_covered|check_flags))) - to_chat(user, "\The [covering] is in the way.") - return 0 - return 1 - -/mob/living/carbon/human/get_equipped_item(var/slot) - switch(slot) - if(slot_back) return back - if(slot_legcuffed) return legcuffed - if(slot_handcuffed) return handcuffed - if(slot_l_store) return l_store - if(slot_r_store) return r_store - if(slot_wear_mask) return wear_mask - if(slot_l_hand) return l_hand - if(slot_r_hand) return r_hand - if(slot_wear_id) return wear_id - if(slot_glasses) return glasses - if(slot_gloves) return gloves - if(slot_head) return head - if(slot_shoes) return shoes - if(slot_belt) return belt - if(slot_wear_suit) return wear_suit - if(slot_w_uniform) return w_uniform - if(slot_s_store) return s_store - if(slot_l_ear) return l_ear - if(slot_r_ear) return r_ear - return ..() - - -/mob/living/carbon/human/is_holding_item_of_type(typepath) - for(var/obj/item/I in list(l_hand, r_hand)) - if(istype(I, typepath)) - return I - return FALSE - -// Returns a list of items held in both hands. -/mob/living/carbon/human/get_all_held_items() - . = list() - if(l_hand) - . += l_hand - if(r_hand) - . += r_hand +/* +Add fingerprints to items when we put them in our hands. +This saves us from having to call add_fingerprint() any time something is put in a human's hands programmatically. +*/ + +/mob/living/carbon/human + var/list/worn_clothing = list() //Contains all CLOTHING items worn + +/mob/living/carbon/human/verb/quick_equip() + set name = "quick-equip" + set hidden = 1 + + if(ishuman(src)) + var/mob/living/carbon/human/H = src + var/obj/item/I = H.get_active_hand() + if(!I) + to_chat(H, "You are not holding anything to equip.") + return + + var/moved = FALSE + + // Try an equipment slot + if(H.equip_to_appropriate_slot(I)) + moved = TRUE + + // No? Try a storage item. + else if(H.equip_to_storage(I, TRUE)) + moved = TRUE + + // No?! Well, give up. + if(!moved) + to_chat(H, "You are unable to equip that.") + + // Update hand icons + else + if(hand) + update_inv_l_hand(0) + else + update_inv_r_hand(0) + +/mob/living/carbon/human/equip_to_storage(obj/item/newitem, user_initiated = FALSE) + // Try put it in their belt first + if(istype(src.belt,/obj/item/weapon/storage)) + var/obj/item/weapon/storage/wornbelt = src.belt + if(wornbelt.can_be_inserted(newitem, 1)) + if(user_initiated) + wornbelt.handle_item_insertion(newitem) + else + newitem.forceMove(wornbelt) + return wornbelt + + return ..() + +/mob/living/carbon/human/proc/equip_in_one_of_slots(obj/item/W, list/slots, del_on_fail = 1) + for (var/slot in slots) + if (equip_to_slot_if_possible(W, slots[slot], del_on_fail = 0)) + return slot + if (del_on_fail) + qdel(W) + return null + +/mob/living/carbon/human/proc/has_organ(name) + var/obj/item/organ/external/O = organs_by_name[name] + return (O && !O.is_stump()) + +/mob/living/carbon/human/proc/has_organ_for_slot(slot) + switch(slot) + if(slot_back) + return has_organ(BP_TORSO) + if(slot_wear_mask) + return has_organ(BP_HEAD) + if(slot_handcuffed) + return has_organ(BP_L_HAND) && has_organ(BP_R_HAND) + if(slot_legcuffed) + return has_organ(BP_L_FOOT) && has_organ(BP_R_FOOT) + if(slot_l_hand) + return has_organ(BP_L_HAND) + if(slot_r_hand) + return has_organ(BP_R_HAND) + if(slot_belt) + return has_organ(BP_TORSO) + if(slot_wear_id) + // the only relevant check for this is the uniform check + return 1 + if(slot_l_ear) + return has_organ(BP_HEAD) + if(slot_r_ear) + return has_organ(BP_HEAD) + if(slot_glasses) + return has_organ(BP_HEAD) + if(slot_gloves) + return has_organ(BP_L_HAND) || has_organ(BP_R_HAND) + if(slot_head) + return has_organ(BP_HEAD) + if(slot_shoes) + return has_organ(BP_L_FOOT) || has_organ(BP_R_FOOT) + if(slot_wear_suit) + return has_organ(BP_TORSO) + if(slot_w_uniform) + return has_organ(BP_TORSO) + if(slot_l_store) + return has_organ(BP_TORSO) + if(slot_r_store) + return has_organ(BP_TORSO) + if(slot_s_store) + return has_organ(BP_TORSO) + if(slot_in_backpack) + return 1 + if(slot_tie) + return 1 + +/obj/item/var/suitlink = 1 //makes belt items require a jumpsuit- set individual items to suitlink = 0 to allow wearing on belt slot without suit + +/mob/living/carbon/human/u_equip(obj/W as obj) + if(!W) return 0 + + if (W == wear_suit) + if(s_store) + drop_from_inventory(s_store) + worn_clothing -= wear_suit + wear_suit = null + update_inv_wear_suit() + else if (W == w_uniform) + if (r_store) + drop_from_inventory(r_store) + if (l_store) + drop_from_inventory(l_store) + if (wear_id) + drop_from_inventory(wear_id) + if (belt && belt.suitlink == 1) + worn_clothing -= belt + drop_from_inventory(belt) + worn_clothing -= w_uniform + w_uniform = null + update_inv_w_uniform() + else if (W == gloves) + worn_clothing -= gloves + gloves = null + update_inv_gloves() + else if (W == glasses) + worn_clothing -= glasses + glasses = null + update_inv_glasses() + else if (W == head) + worn_clothing -= head + head = null + if(istype(W, /obj/item)) + var/obj/item/I = W + if(I.flags_inv & (HIDEMASK|BLOCKHAIR|BLOCKHEADHAIR)) + update_hair(0) //rebuild hair + update_inv_ears(0) + update_inv_wear_mask(0) + update_inv_head() + else if (W == l_ear) + l_ear = null + update_inv_ears() + else if (W == r_ear) + r_ear = null + update_inv_ears() + else if (W == shoes) + worn_clothing -= shoes + shoes = null + update_inv_shoes() + else if (W == belt) + worn_clothing -= belt + belt = null + update_inv_belt() + else if (W == wear_mask) + worn_clothing -= wear_mask + wear_mask = null + if(istype(W, /obj/item)) + var/obj/item/I = W + if(I.flags_inv & (BLOCKHAIR|BLOCKHEADHAIR)) + update_hair(0) //rebuild hair + update_inv_ears(0) + // If this is how the internals are connected, disable them + if(internal && !(head?.item_flags & AIRTIGHT)) + if(internals) + internals.icon_state = "internal0" + internal = null + update_inv_wear_mask() + else if (W == wear_id) + wear_id = null + update_inv_wear_id() + BITSET(hud_updateflag, ID_HUD) + BITSET(hud_updateflag, WANTED_HUD) + else if (W == r_store) + r_store = null + //update_inv_pockets() //Doesn't do anything. + else if (W == l_store) + l_store = null + //update_inv_pockets() //Doesn't do anything. + else if (W == s_store) + s_store = null + update_inv_s_store() + else if (W == back) + worn_clothing -= back + back = null + update_inv_back() + else if (W == handcuffed) + handcuffed = null + if(buckled && buckled.buckle_require_restraints) + buckled.unbuckle_mob() + update_handcuffed() + else if (W == legcuffed) + legcuffed = null + update_inv_legcuffed() + else if (W == r_hand) + r_hand = null + if(l_hand) + l_hand.update_twohanding() + l_hand.update_held_icon() + update_inv_l_hand() + update_inv_r_hand() + else if (W == l_hand) + l_hand = null + if(r_hand) + r_hand.update_twohanding() + r_hand.update_held_icon() + update_inv_l_hand() + update_inv_l_hand() + else + return 0 + + update_action_buttons() + return 1 + + + +//This is an UNSAFE proc. Use mob_can_equip() before calling this one! Or rather use equip_to_slot_if_possible() or advanced_equip_to_slot_if_possible() +/mob/living/carbon/human/equip_to_slot(obj/item/W as obj, slot) + + if(!slot) + return + if(!istype(W)) + return + if(!has_organ_for_slot(slot)) + return + if(!species || !species.hud || !(slot in species.hud.equip_slots)) + return + + W.loc = src + switch(slot) + if(slot_back) + src.back = W + W.equipped(src, slot) + worn_clothing += back + update_inv_back() + if(slot_wear_mask) + src.wear_mask = W + if(wear_mask.flags_inv & (BLOCKHAIR|BLOCKHEADHAIR)) + update_hair() //rebuild hair + update_inv_ears() + W.equipped(src, slot) + worn_clothing += wear_mask + update_inv_wear_mask() + if(slot_handcuffed) + src.handcuffed = W + update_inv_handcuffed() + if(slot_legcuffed) + src.legcuffed = W + W.equipped(src, slot) + update_inv_legcuffed() + if(slot_l_hand) + src.l_hand = W + W.equipped(src, slot) + update_inv_l_hand() + if(slot_r_hand) + src.r_hand = W + W.equipped(src, slot) + update_inv_r_hand() + if(slot_belt) + src.belt = W + W.equipped(src, slot) + worn_clothing += belt + update_inv_belt() + if(slot_wear_id) + src.wear_id = W + W.equipped(src, slot) + update_inv_wear_id() + BITSET(hud_updateflag, ID_HUD) + BITSET(hud_updateflag, WANTED_HUD) + if(slot_l_ear) + src.l_ear = W + if(l_ear.slot_flags & SLOT_TWOEARS) + var/obj/item/clothing/ears/offear/O = new(W) + O.loc = src + src.r_ear = O + O.hud_layerise() + W.equipped(src, slot) + update_inv_ears() + if(slot_r_ear) + src.r_ear = W + if(r_ear.slot_flags & SLOT_TWOEARS) + var/obj/item/clothing/ears/offear/O = new(W) + O.loc = src + src.l_ear = O + O.hud_layerise() + W.equipped(src, slot) + update_inv_ears() + if(slot_glasses) + src.glasses = W + W.equipped(src, slot) + worn_clothing += glasses + update_inv_glasses() + if(slot_gloves) + src.gloves = W + W.equipped(src, slot) + worn_clothing += gloves + update_inv_gloves() + if(slot_head) + src.head = W + if(head.flags_inv & (BLOCKHAIR|BLOCKHEADHAIR|HIDEMASK)) + update_hair() //rebuild hair + update_inv_ears(0) + update_inv_wear_mask(0) + if(istype(W,/obj/item/clothing/head/kitty)) + W.update_icon(src) + W.equipped(src, slot) + worn_clothing += head + update_inv_head() + if(slot_shoes) + src.shoes = W + W.equipped(src, slot) + worn_clothing += shoes + update_inv_shoes() + if(slot_wear_suit) + src.wear_suit = W + W.equipped(src, slot) + worn_clothing += wear_suit + update_inv_wear_suit() + if(slot_w_uniform) + src.w_uniform = W + W.equipped(src, slot) + worn_clothing += w_uniform + update_inv_w_uniform() + if(slot_l_store) + src.l_store = W + W.equipped(src, slot) + //update_inv_pockets() //Doesn't do anything + if(slot_r_store) + src.r_store = W + W.equipped(src, slot) + //update_inv_pockets() //Doesn't do anything + if(slot_s_store) + src.s_store = W + W.equipped(src, slot) + update_inv_s_store() + if(slot_in_backpack) + if(src.get_active_hand() == W) + src.remove_from_mob(W) + W.loc = src.back + if(slot_tie) + for(var/obj/item/clothing/C in worn_clothing) + if(istype(W, /obj/item/clothing/accessory)) + var/obj/item/clothing/accessory/A = W + if(C.attempt_attach_accessory(A, src)) + return + else + to_chat(src, span_red("You are trying to equip this item to an unsupported inventory slot. How the heck did you manage that? Stop it...")) + return + + if((W == src.l_hand) && (slot != slot_l_hand)) + src.l_hand = null + update_inv_l_hand() //So items actually disappear from hands. + else if((W == src.r_hand) && (slot != slot_r_hand)) + src.r_hand = null + update_inv_r_hand() + + W.hud_layerise() + + if(W.action_button_name) + update_action_buttons() + + if(W.zoom) + W.zoom() + + W.in_inactive_hand(src) + + //VOREStation Addition Start + if(istype(W, /obj/item)) + var/obj/item/I = W + I.equip_special() + //VOREStation Addition End + + return 1 + +//Checks if a given slot can be accessed at this time, either to equip or unequip I +/mob/living/carbon/human/slot_is_accessible(var/slot, var/obj/item/I, mob/user=null) + var/obj/item/covering = null + var/check_flags = 0 + + switch(slot) + if(slot_wear_mask) + covering = src.head + check_flags = FACE + if(slot_glasses) + covering = src.head + check_flags = EYES + if(slot_gloves, slot_w_uniform) + covering = src.wear_suit + + if(covering && (covering.body_parts_covered & (I.body_parts_covered|check_flags))) + to_chat(user, "\The [covering] is in the way.") + return 0 + return 1 + +/mob/living/carbon/human/get_equipped_item(var/slot) + switch(slot) + if(slot_back) return back + if(slot_legcuffed) return legcuffed + if(slot_handcuffed) return handcuffed + if(slot_l_store) return l_store + if(slot_r_store) return r_store + if(slot_wear_mask) return wear_mask + if(slot_l_hand) return l_hand + if(slot_r_hand) return r_hand + if(slot_wear_id) return wear_id + if(slot_glasses) return glasses + if(slot_gloves) return gloves + if(slot_head) return head + if(slot_shoes) return shoes + if(slot_belt) return belt + if(slot_wear_suit) return wear_suit + if(slot_w_uniform) return w_uniform + if(slot_s_store) return s_store + if(slot_l_ear) return l_ear + if(slot_r_ear) return r_ear + return ..() + + +/mob/living/carbon/human/is_holding_item_of_type(typepath) + for(var/obj/item/I in list(l_hand, r_hand)) + if(istype(I, typepath)) + return I + return FALSE + +// Returns a list of items held in both hands. +/mob/living/carbon/human/get_all_held_items() + . = list() + if(l_hand) + . += l_hand + if(r_hand) + . += r_hand diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 08499931701..a5c94480325 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -1,2130 +1,2130 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -//NOTE: Breathing happens once per FOUR TICKS, unless the last breath fails. In which case it happens once per ONE TICK! So oxyloss healing is done once per 4 ticks while oxyloss damage is applied once per tick! -#define HUMAN_MAX_OXYLOSS 1 //Defines how much oxyloss humans can get per tick. A tile with no air at all (such as space) applies this value, otherwise it's a percentage of it. -#define HUMAN_CRIT_MAX_OXYLOSS ( 2.0 / 6) //The amount of damage you'll get when in critical condition. We want this to be a 5 minute deal = 300s. There are 50HP to get through, so (1/6)*last_tick_duration per second. Breaths however only happen every 4 ticks. last_tick_duration = ~2.0 on average - -#define HEAT_DAMAGE_LEVEL_1 2 //VOREStation Edit //Amount of damage applied when your body temperature just passes the 360.15k safety point -#define HEAT_DAMAGE_LEVEL_2 4 //VOREStation Edit //Amount of damage applied when your body temperature passes the 400K point -#define HEAT_DAMAGE_LEVEL_3 8 //VOREStation Edit //Amount of damage applied when your body temperature passes the 1000K point - -#define COLD_DAMAGE_LEVEL_1 0.5 //Amount of damage applied when your body temperature just passes the 260.15k safety point -#define COLD_DAMAGE_LEVEL_2 1.5 //Amount of damage applied when your body temperature passes the 200K point -#define COLD_DAMAGE_LEVEL_3 3 //Amount of damage applied when your body temperature passes the 120K point - -//Note that gas heat damage is only applied once every FOUR ticks. -#define HEAT_GAS_DAMAGE_LEVEL_1 2 //Amount of damage applied when the current breath's temperature just passes the 360.15k safety point -#define HEAT_GAS_DAMAGE_LEVEL_2 4 //Amount of damage applied when the current breath's temperature passes the 400K point -#define HEAT_GAS_DAMAGE_LEVEL_3 8 //Amount of damage applied when the current breath's temperature passes the 1000K point - -#define COLD_GAS_DAMAGE_LEVEL_1 0.5 //Amount of damage applied when the current breath's temperature just passes the 260.15k safety point -#define COLD_GAS_DAMAGE_LEVEL_2 1.5 //Amount of damage applied when the current breath's temperature passes the 200K point -#define COLD_GAS_DAMAGE_LEVEL_3 3 //Amount of damage applied when the current breath's temperature passes the 120K point - -#define COLD_ALERT_SEVERITY_LOW 1 // Constants passed to the cold and heat alerts. -#define COLD_ALERT_SEVERITY_MODERATE 2 -#define COLD_ALERT_SEVERITY_MAX 3 -#define ENVIRONMENT_COMFORT_MARKER_COLD 1 - -#define HOT_ALERT_SEVERITY_LOW 1 -#define HOT_ALERT_SEVERITY_MODERATE 2 -#define HOT_ALERT_SEVERITY_MAX 3 -#define ENVIRONMENT_COMFORT_MARKER_HOT 2 - -#define RADIATION_SPEED_COEFFICIENT 0.1 -#define HUMAN_COMBUSTION_TEMP 524 //524k is the sustained combustion temperature of human fat - -/mob/living/carbon/human - var/in_stasis = 0 - var/heartbeat = 0 - -/mob/living/carbon/human/Life() - set invisibility = 0 - set background = BACKGROUND_ENABLED - - if (transforming) - return - - //Apparently, the person who wrote this code designed it so that - //blinded get reset each cycle and then get activated later in the - //code. Very ugly. I dont care. Moving this stuff here so its easy - //to find it. - blinded = 0 - - //TODO: seperate this out - // update the current life tick, can be used to e.g. only do something every 4 ticks - life_tick++ - - // This is not an ideal place for this but it will do for now. - if(wearing_rig && wearing_rig.offline) - wearing_rig = null - - ..() - - if(life_tick % 30) - hud_updateflag = (1 << TOTAL_HUDS) - 1 - - voice = GetVoice() - - var/stasis = inStasisNow() - if(getStasis() > 2) - Sleeping(20) - - //No need to update all of these procs if the guy is dead. - fall() //VORESTATION EDIT. Prevents people from floating - if(stat != DEAD && !stasis) - //Updates the number of stored chemicals for powers - handle_changeling() - - //Organs and blood - handle_organs() - stabilize_body_temperature() //Body temperature adjusts itself (self-regulation) - weightgain() //VOREStation Addition - process_weaver_silk() //VOREStation Addition - handle_shock() - - handle_pain() - - handle_allergens() - - handle_medical_side_effects() - - handle_heartbeat() - handle_nif() //VOREStation Addition - if(!client) - species.handle_npc(src) - - else if(stat == DEAD && !stasis) - handle_defib_timer() - - if(skip_some_updates()) - return //We go ahead and process them 5 times for HUD images and other stuff though. - - //Update our name based on whether our face is obscured/disfigured - name = get_visible_name() - - pulse = handle_pulse() - -/mob/living/carbon/human/proc/skip_some_updates() - if(life_tick > 5 && timeofdeath && (timeofdeath < 5 || world.time - timeofdeath > 6000)) //We are long dead, or we're junk mobs spawned like the clowns on the clown shuttle - return 1 - return 0 - -/mob/living/carbon/human/breathe() - if(!inStasisNow()) - ..() - -// Calculate how vulnerable the human is to the current pressure. -// Returns 0 (equals 0 %) if sealed in an undamaged suit that's rated for the pressure, 1 if unprotected (equals 100%). -// Suitdamage can modifiy this in 10% steps. -// Protection scales down from 100% at the boundary to 0% at 10% in excess of the boundary -/mob/living/carbon/human/proc/get_pressure_weakness(pressure) - if(pressure == null) - return 1 // No protection if someone forgot to give a pressure - - var/pressure_adjustment_coefficient = 1 // Assume no protection at first. - - // Check suit - if(wear_suit && wear_suit.max_pressure_protection != null && wear_suit.min_pressure_protection != null) - pressure_adjustment_coefficient = 0 - // Pressure is too high - if(wear_suit.max_pressure_protection < pressure) - // Protection scales down from 100% at the boundary to 0% at 10% in excess of the boundary - pressure_adjustment_coefficient += round((pressure - wear_suit.max_pressure_protection) / (wear_suit.max_pressure_protection/10)) - - // Pressure is too low - if(wear_suit.min_pressure_protection > pressure) - pressure_adjustment_coefficient += round((wear_suit.min_pressure_protection - pressure) / (wear_suit.min_pressure_protection/10)) - - // Handles breaches in your space suit. 10 suit damage equals a 100% loss of pressure protection. - if(istype(wear_suit,/obj/item/clothing/suit/space)) - var/obj/item/clothing/suit/space/S = wear_suit - if(S.can_breach && S.damage) - pressure_adjustment_coefficient += S.damage * 0.1 - - else - // Missing key protection - pressure_adjustment_coefficient = 1 - - // Check hat - if(head && head.max_pressure_protection != null && head.min_pressure_protection != null) - // Pressure is too high - if(head.max_pressure_protection < pressure) - // Protection scales down from 100% at the boundary to 0% at 20% in excess of the boundary - pressure_adjustment_coefficient += round((pressure - head.max_pressure_protection) / (head.max_pressure_protection/20)) - - // Pressure is too low - if(head.min_pressure_protection > pressure) - pressure_adjustment_coefficient += round((head.min_pressure_protection - pressure) / (head.min_pressure_protection/20)) - - else - // Missing key protection - pressure_adjustment_coefficient = 1 - - pressure_adjustment_coefficient = min(pressure_adjustment_coefficient, 1) - return pressure_adjustment_coefficient - -// Calculate how much of the enviroment pressure-difference affects the human. -/mob/living/carbon/human/calculate_affecting_pressure(var/pressure) - var/pressure_difference - - // First get the absolute pressure difference. - if(pressure < species.safe_pressure) // We are in an underpressure. - pressure_difference = species.safe_pressure - pressure - - else //We are in an overpressure or standard atmosphere. - pressure_difference = pressure - species.safe_pressure - - if(pressure_difference < 5) // If the difference is small, don't bother calculating the fraction. - pressure_difference = 0 - - else - // Otherwise calculate how much of that absolute pressure difference affects us, can be 0 to 1 (equals 0% to 100%). - // This is our relative difference. - pressure_difference *= get_pressure_weakness(pressure) - - // The difference is always positive to avoid extra calculations. - // Apply the relative difference on a standard atmosphere to get the final result. - // The return value will be the adjusted_pressure of the human that is the basis of pressure warnings and damage. - if(pressure < species.safe_pressure) - return species.safe_pressure - pressure_difference - else - return species.safe_pressure + pressure_difference - -/mob/living/carbon/human/handle_disabilities() - ..() - - if(stat != CONSCIOUS) //Let's not worry about tourettes if you're not conscious. - return - - if (disabilities & EPILEPSY) - if ((prob(1) && paralysis < 1)) - to_chat(src, span_red("You have a seizure!")) - for(var/mob/O in viewers(src, null)) - if(O == src) - continue - O.show_message(text("[src] starts having a seizure!"), 1) - Paralyse(10) - make_jittery(1000) - if (disabilities & COUGHING) - if ((prob(5) && paralysis <= 1)) - drop_item() - spawn( 0 ) - emote("cough") - return - if (disabilities & TOURETTES) - if ((prob(10) && paralysis <= 1)) - Stun(10) - spawn( 0 ) - switch(rand(1, 3)) - if(1) - emote("twitch") - if(2 to 3) - say("[prob(50) ? ";" : ""][pick("SHIT", "PISS", "FUCK", "CUNT", "COCKSUCKER", "MOTHERFUCKER", "TITS")]") - make_jittery(100) - return - if (disabilities & NERVOUS) - if (prob(10)) - stuttering = max(10, stuttering) - - var/rn = rand(0, 200) - if(getBrainLoss() >= 5) - if(0 <= rn && rn <= 3) - custom_pain("Your head feels numb and painful.", 10) - if(getBrainLoss() >= 15) - if(4 <= rn && rn <= 6) if(eye_blurry <= 0) - to_chat(src, "It becomes hard to see for some reason.") - eye_blurry = 10 - if(getBrainLoss() >= 35) - if(7 <= rn && rn <= 9) if(get_active_hand()) - to_chat(src, "Your hand won't respond properly, you drop what you're holding!") - drop_item() - if(getBrainLoss() >= 45) - if(10 <= rn && rn <= 12) - if(prob(50)) - to_chat(src, "You suddenly black out!") - Paralyse(10) - else if(!lying) - to_chat(src, "Your legs won't respond properly, you fall down!") - Weaken(10) - -// RADIATION! Everyone's favorite thing in the world! So let's get some numbers down off the bat. -// 50 rads = 1Bq. This means 1 rad = 0.02Bq. -// However, unless I am a smoothbrained dumbo, absorbed rads are in Gy. Not Bq. -// So let's just assume that 50 rads = 1Gy. Make life easier! - -// ACUTE RADIATION (The stuff that the 'radiation' variable takes care of. Remember, 50radiation=1Gy.): -// Without care: 1-2Gy has a (0-5%) mortality chance. 2-6 (5-95%) 6-8 (95-100)% 8-30 (100%) >30 (100%) -// With care: 1-2Gy (0-5%), 2-6 (5-50%), 6-8 (50-100%), 8-30 (99-100%) >30 (100%) -// So let's make our thresholds based on this! 50-100, 100-300, 300-400, 400-1500, and anything above 1500! -// In reality, however, nobody should ever go above 300 radiation, which is why the cutoff before the really bad effects start to happen being -// 300 radiation is good. For reference: Breaking an artifact deals ~300 rads with no resistance. Getting shot with a lvl 3 PA deals 300 rads with no resistance. -// Nobody outside of engineering should ever have to worry about being irradiated over 300 and start getting organ damage.. - - -// CHRONIC RADIATION (The stuff that 'accumulated_rads' takes care of): -// This is more or less for if someone was exposed for a long time to radiation or just finished being treated for extreme ARS. -// These are meant to be annoying effects to nudge someone towards medical, but not lethal or deadly. -// Things such as loss of taste, eye damage, dropping items in your hand, being temporaily weakened, etc. Stuff to annoy them and get them to fix their rads. - -// Additionally, RADIATION_SPEED_COEFFICIENT = 0.1 - -/mob/living/carbon/human/handle_mutations_and_radiation() //Radiation rework! Now with 'accumulated_rads' - if(inStasisNow()) - return - - if(getFireLoss()) - if((COLD_RESISTANCE in mutations) || (prob(1))) - heal_organ_damage(0,1) - - // DNA2 - Gene processing. - // The HULK stuff that was here is now in the hulk gene. - if(!isSynthetic()) - for(var/datum/dna/gene/gene in dna_genes) - if(!gene.block) - continue - if(gene.is_active(src)) - gene.OnMobLife(src) - - radiation = CLAMP(radiation,0,2500) //Max of 50Gy. If you reach that...You're going to wish you were dead. You probably will be dead. - accumulated_rads = CLAMP(accumulated_rads,0,2500) //Max of 50Gy as well. You should never get higher than this. You will be dead before you can reach this. - var/obj/item/organ/internal/I = null //Used for further down below when an organ is picked. - if(!radiation) - if(species.appearance_flags & RADIATION_GLOWS) - glow_override = FALSE - set_light(0) - if(accumulated_rads) - accumulated_rads -= RADIATION_SPEED_COEFFICIENT //Accumulated rads slowly dissipate very slowly. Get to medical to get it treated! - else if(((life_tick % 5 == 0) && radiation) || (radiation > 600)) //Radiation is a slow, insidious killer. Unless you get a massive dose, then the onset is sudden! - if(species.appearance_flags & RADIATION_GLOWS) - glow_override = TRUE - set_light(max(1,min(5,radiation/15)), max(1,min(10,radiation/25)), species.get_flesh_colour(src)) - // END DOGSHIT SNOWFLAKE - - var/obj/item/organ/internal/diona/nutrients/rad_organ = locate() in internal_organs - if(rad_organ && !rad_organ.is_broken()) - var/rads = radiation/25 - radiation -= rads - adjust_nutrition(rads) - adjustBruteLoss(-(rads)) - adjustFireLoss(-(rads)) - adjustOxyLoss(-(rads)) - adjustToxLoss(-(rads)) - updatehealth() - return - - var/obj/item/organ/internal/brain/slime/core = locate() in internal_organs - if(core) - return - - //VOREStation Addition start: shadekin - var/obj/item/organ/internal/brain/shadekin/s_brain = locate() in internal_organs - if(s_brain) - return - //VOREStation Addition end: shadekin - - if(reagents.has_reagent("prussian_blue")) //Prussian Blue temporarily stops radiation effects. - return - - var/damage = 0 - - - if (radiation < 50) //Less than 1.0 Gy. No side effects. - radiation -= 10 * RADIATION_SPEED_COEFFICIENT - accumulated_rads += 10 * RADIATION_SPEED_COEFFICIENT //No escape from accumulated rads. - - else if (radiation >= 50 && radiation < 100) //Equivalent of 1.0-2.0 Gy. Minimum stage you start seeing effects. - damage = 1 - radiation -= 10 * RADIATION_SPEED_COEFFICIENT - accumulated_rads += 10 * RADIATION_SPEED_COEFFICIENT - if(!isSynthetic()) - if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT) && !weakened) - to_chat(src, "You feel exhausted.") - AdjustWeakened(3) - if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT) && species.get_bodytype() == SPECIES_HUMAN) //apes go bald - if((h_style != "Bald" || f_style != "Shaved" )) - to_chat(src, "Your hair falls out.") - h_style = "Bald" - f_style = "Shaved" - update_hair() - if(prob(1) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //Rare chance of vomiting. - spawn vomit() - - else if (radiation >= 100 && radiation < 300) //Equivalent of 2.0 to 6.0 Gy. Nobody should ever be above this without extreme negligence. - damage = 3 - radiation -= 30 * RADIATION_SPEED_COEFFICIENT - accumulated_rads += 30 * RADIATION_SPEED_COEFFICIENT - if(!isSynthetic()) - if(prob(5)) - take_overall_damage(0, 5 * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") - if(prob(1)) - adjustCloneLoss(5 * RADIATION_SPEED_COEFFICIENT) - emote("gasp") - if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT)) - spawn vomit() - if(prob(10) && !weakened) - to_chat(src, "You feel sick.") - AdjustWeakened(3) - - else if (radiation >= 300 && radiation < 400) //Equivalent of 6.0 to 8.0 Gy. - damage = 5 - radiation -= 50 * RADIATION_SPEED_COEFFICIENT - accumulated_rads += 50 * RADIATION_SPEED_COEFFICIENT - if(!isSynthetic()) - if(prob(15)) - take_overall_damage(0, 10 * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") - if(prob(2)) - adjustCloneLoss(5 * RADIATION_SPEED_COEFFICIENT) - emote("gasp") - if(prob(10) && prob(100 * RADIATION_SPEED_COEFFICIENT)) - spawn vomit() - if(prob(15) && !weakened) - to_chat(src, "You feel horribly ill.") - AdjustWeakened(3) - if(prob(5) && internal_organs.len) - I = pick(internal_organs) //Internal organ damage...Not good. Not good at all. - if(istype(I)) I.add_autopsy_data("Radiation Induced Cancerous Growth", damage) - I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - - - else if (radiation >= 400 && radiation < 1500) //Equivalent of 8.0 to 30 Gy. - damage = 10 - radiation -= 100 * RADIATION_SPEED_COEFFICIENT - accumulated_rads += 100 * RADIATION_SPEED_COEFFICIENT - if(!isSynthetic()) - if(prob(25)) - take_overall_damage(0, 15 * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") - if(prob(5)) - I = internal_organs_by_name[O_EYES] - if(I) - if(istype(I)) I.add_autopsy_data("Radiation Burns", damage) - I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - to_chat(src, "Your eyes burn!") - eye_blurry += 10 - if(prob(4)) - adjustCloneLoss(5 * RADIATION_SPEED_COEFFICIENT) - emote("gasp") - if(prob(25) && prob(100 * RADIATION_SPEED_COEFFICIENT)) - spawn vomit() - if(prob(20) && !weakened) - to_chat(src, "You feel like your insides are burning!") - AdjustWeakened(5) - if(prob(5)) - to_chat(src, "Your entire body feels like it's on fire!") - adjustHalLoss(5) - if(prob(10) && internal_organs.len) - I = pick(internal_organs) //Internal organ damage...Not good. Not good at all. - if(istype(I)) I.add_autopsy_data("Radiation Induced Cancerous Growth", damage) - I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - - else if (radiation >= 1500) //Above 30Gy. You had to get absolutely blasted with rads for this. - damage = 30 - radiation -= 300 * RADIATION_SPEED_COEFFICIENT - accumulated_rads += 300 * RADIATION_SPEED_COEFFICIENT - - if(!isSynthetic()) - take_overall_damage(0, damage * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") //3 burn damage a tick as your body melts. - adjustCloneLoss(15 * RADIATION_SPEED_COEFFICIENT) //1.5 cloneloss a tick as your cells mutate and break down. - - I = internal_organs_by_name[O_EYES] - if(I) - I.add_autopsy_data("Radiation Burns", damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) //3 eye damage a tick as your eyes melt down. - eye_blurry += 10 - - if(prob(50) && prob(100 * RADIATION_SPEED_COEFFICIENT)) - spawn vomit() - if(!paralysis && prob(30) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //CNS is shutting down. - to_chat(src, "You have a seizure!") - Paralyse(10) - make_jittery(1000) - if(!lying) - emote("collapse") - if(get_active_hand() && prob(15)) //CNS is shutting down. - to_chat(src, "Your hand won't respond properly, you drop what you're holding!") - drop_item() - if(internal_organs.len) - I = pick(internal_organs) //Internal organ damage...Not good. Not good at all. - if(istype(I)) I.add_autopsy_data("Radiation Induced Cancerous Growth", damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - -/* //Not-so-sparkledog code. TODO: Make a pref for 'special game interactions' that allows interactions that align with prefs to occur. - if(radiation >= 250) //Special effect stuff that occurs at certain rad levels. - if(prob(1) && prob(radiation/2 * RADIATION_SPEED_COEFFICIENT) && allow_spontaneous_tf) //If you've got spontaneous TF...well... - scramble(1, src, 3) //I tried to base this on how many rads you took and it was...Hilarious. Sparkledogs everywhere. - //For the most part, 3 strength will simply change colors. If you get really unlucky, it can do more TF's. - //Math: 250 rads = 1/800 chance - //500 rads = 1/400 chance chance. Etc. -*/ - - if(damage) - damage *= species.radiation_mod - adjustToxLoss(damage * RADIATION_SPEED_COEFFICIENT) - updatehealth() - if(!isSynthetic() && organs.len) - var/obj/item/organ/external/O = pick(organs) - if(istype(O)) O.add_autopsy_data("Radiation Poisoning", damage) - - // Begin long-term radiation effects - // Loss of taste occurs at 100 (2Gy) and is handled in taste.dm - // These are all done one after another, so duplication is not required. Someone at 400rads will have the 100&400 effects. - if(!radiation && accumulated_rads >= 100 && !reagents.has_reagent("prussian_blue")) //Let's not hit them with long term effects when they're actively being hit with rads. - if(!isSynthetic()) - I = internal_organs_by_name[O_EYES] - if(I) //Eye stuff - if(prob(5) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) - to_chat(src, "Your eyes water.") - eye_blurry += 5 - if(accumulated_rads > 300) // (6Gy) - if(prob(2) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) - to_chat(src, "Your eyes burn.") - I.add_autopsy_data("Radiation Burns", 1 * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - I.take_damage(1 * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) //0.1 damage. Not a lot, but enough to tell you to get to medical. - eye_blurry += 10 - - if(accumulated_rads > 200) // (4Gy) - if(prob(5) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) - to_chat(src, "Your feel nauseated.") - spawn vomit() - if(!weakened && prob(2) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) - to_chat(src, "Your feel exhausted.") - AdjustWeakened(3) - if(accumulated_rads > 300) // (6Gy) - if(get_active_hand() && prob(15) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //CNS is shutting down. - to_chat(src, "Your hand won't respond properly, you drop what you're holding!") - drop_item() - if(accumulated_rads > 700) // (12Gy) - if(!paralysis && prob(1) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //1 in 1000 chance per tick. - to_chat(src, "You have a seizure!") - Paralyse(10) - make_jittery(1000) - if(!lying) - emote("collapse") - - else //The synthetic effects! - return //Nothing for now. - - - - /** breathing **/ - -/mob/living/carbon/human/handle_chemical_smoke(var/datum/gas_mixture/environment) - if(wear_mask && (wear_mask.item_flags & BLOCK_GAS_SMOKE_EFFECT)) - return - if(glasses && (glasses.item_flags & BLOCK_GAS_SMOKE_EFFECT)) - return - if(head && (head.item_flags & BLOCK_GAS_SMOKE_EFFECT)) - return - ..() - -/mob/living/carbon/human/handle_post_breath(datum/gas_mixture/breath) - ..() - //spread some viruses while we are at it - if(breath && virus2.len > 0 && prob(10)) - for(var/mob/living/carbon/M in view(1,src)) - src.spread_disease_to(M) - - -/mob/living/carbon/human/get_breath_from_internal(volume_needed=BREATH_VOLUME) - if(internal) - //Because rigs store their tanks out of reach of contents.Find(), a check has to be made to make - //sure the rig is still worn, still online, and that its air supply still exists. - var/obj/item/weapon/tank/rig_supply - var/obj/item/weapon/rig/Rig = get_rig() - - if(Rig) - rig_supply = Rig.air_supply - - if ((!rig_supply && !contents.Find(internal)) || !((wear_mask && (wear_mask.item_flags & AIRTIGHT)) || (head && (head.item_flags & AIRTIGHT)))) - internal = null - - if(internal) - return internal.remove_air_volume(volume_needed) - else if(internals) - internals.icon_state = "internal0" - return null - - -/mob/living/carbon/human/handle_breath(datum/gas_mixture/breath) - if(status_flags & GODMODE) - return - - if(suiciding) - failed_last_breath = 1 - adjustOxyLoss(2)//If you are suiciding, you should die a little bit faster - suiciding-- - return 0 - - if(does_not_breathe) - failed_last_breath = 0 - adjustOxyLoss(-5) - return - - if(!breath || (breath.total_moles == 0)) - failed_last_breath = 1 - if(health > config.health_threshold_crit) - adjustOxyLoss(HUMAN_MAX_OXYLOSS) - else - adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS) - - if(breath && should_have_organ(O_LUNGS)) - var/obj/item/organ/internal/lungs/L = internal_organs_by_name[O_LUNGS] - if(!L.is_bruised() && prob(8)) - rupture_lung() - - throw_alert("pressure", /obj/screen/alert/lowpressure) - return 0 - else - clear_alert("pressure") - - var/safe_pressure_min = species.minimum_breath_pressure // Minimum safe partial pressure of breathable gas in kPa - - - // Lung damage increases the minimum safe pressure. - if(should_have_organ(O_LUNGS)) - var/obj/item/organ/internal/lungs/L = internal_organs_by_name[O_LUNGS] - if(isnull(L)) - safe_pressure_min = INFINITY //No lungs, how are you breathing? - else if(L.is_broken()) - safe_pressure_min *= 1.5 - else if(L.is_bruised()) - safe_pressure_min *= 1.25 - else if(breath) - if(breath.total_moles < BREATH_MOLES / 10 || breath.total_moles > BREATH_MOLES * 5) - if(is_below_sound_pressure(get_turf(src))) //No more popped lungs from choking/drowning - if (prob(8)) - rupture_lung() - - var/safe_exhaled_max = 10 - var/safe_toxins_max = 0.2 - var/SA_para_min = 1 - var/SA_sleep_min = 5 - var/inhaled_gas_used = 0 - - var/breath_pressure = (breath.total_moles*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME - - var/inhaling - var/poison - var/exhaling - - var/breath_type - var/poison_type - var/exhale_type - - var/failed_inhale = 0 - var/failed_exhale = 0 - - if(species.breath_type) - breath_type = species.breath_type - else - breath_type = "oxygen" - inhaling = breath.gas[breath_type] - - if(species.poison_type) - poison_type = species.poison_type - else - poison_type = "phoron" - poison = breath.gas[poison_type] - - if(species.exhale_type) - exhale_type = species.exhale_type - exhaling = breath.gas[exhale_type] - else - exhaling = 0 - - var/inhale_pp = (inhaling/breath.total_moles)*breath_pressure - var/toxins_pp = (poison/breath.total_moles)*breath_pressure - // To be clear, this isn't how much they're exhaling -- it's the amount of the species exhale_gas that they just - var/exhaled_pp = (exhaling/breath.total_moles)*breath_pressure - - // Not enough to breathe - if(inhale_pp < safe_pressure_min) - if(prob(20)) - spawn(0) emote("gasp") - - var/ratio = inhale_pp/safe_pressure_min - // Don't fuck them up too fast (space only does HUMAN_MAX_OXYLOSS after all!) - adjustOxyLoss(max(HUMAN_MAX_OXYLOSS*(1-ratio), 0)) - failed_inhale = 1 - - switch(breath_type) - if("oxygen") - throw_alert("oxy", /obj/screen/alert/not_enough_oxy) - if("phoron") - throw_alert("oxy", /obj/screen/alert/not_enough_tox) - if("nitrogen") - throw_alert("oxy", /obj/screen/alert/not_enough_nitro) - if("carbon_dioxide") - throw_alert("oxy", /obj/screen/alert/not_enough_co2) - if("volatile_fuel") - throw_alert("oxy", /obj/screen/alert/not_enough_fuel) - if("nitrous_oxide") - throw_alert("oxy", /obj/screen/alert/not_enough_n2o) - - else - // We're in safe limits - clear_alert("oxy") - - inhaled_gas_used = inhaling/6 - - breath.adjust_gas(breath_type, -inhaled_gas_used, update = 0) //update afterwards - - if(exhale_type) - breath.adjust_gas_temp(exhale_type, inhaled_gas_used, bodytemperature, update = 0) //update afterwards - - // Too much exhaled gas in the air - if(exhaled_pp > safe_exhaled_max) - if (prob(15)) - var/word = pick("extremely dizzy","short of breath","faint","confused") - to_chat(src, "You feel [word].") - - adjustOxyLoss(HUMAN_MAX_OXYLOSS) - failed_exhale = 1 - - else if(exhaled_pp > safe_exhaled_max * 0.7) - if (!prob(1)) - var/word = pick("dizzy","short of breath","faint","momentarily confused") - to_chat(src, "You feel [word].") - - //scale linearly from 0 to 1 between safe_exhaled_max and safe_exhaled_max*0.7 - var/ratio = 1.0 - (safe_exhaled_max - exhaled_pp)/(safe_exhaled_max*0.3) - - //give them some oxyloss, up to the limit - we don't want people falling unconcious due to CO2 alone until they're pretty close to safe_exhaled_max. - if (getOxyLoss() < 50*ratio) - adjustOxyLoss(HUMAN_MAX_OXYLOSS) - failed_exhale = 1 - - else if(exhaled_pp > safe_exhaled_max * 0.6) - if(prob(0.3)) - var/word = pick("a little dizzy","short of breath") - to_chat(src, "You feel [word].") - - // Too much poison in the air. - if(toxins_pp > safe_toxins_max) - var/ratio = (poison/safe_toxins_max) * 10 - if(reagents) - reagents.add_reagent("toxin", CLAMP(ratio, MIN_TOXIN_DAMAGE, MAX_TOXIN_DAMAGE)) - breath.adjust_gas(poison_type, -poison/6, update = 0) //update after - throw_alert("tox_in_air", /obj/screen/alert/tox_in_air) - else - clear_alert("tox_in_air") - - // If there's some other shit in the air lets deal with it here. - if(breath.gas["nitrous_oxide"]) - var/SA_pp = (breath.gas["nitrous_oxide"] / breath.total_moles) * breath_pressure - - // Enough to make us paralysed for a bit - if(SA_pp > SA_para_min) - - // 3 gives them one second to wake up and run away a bit! - Paralyse(3) - - // Enough to make us sleep as well - if(SA_pp > SA_sleep_min) - Sleeping(5) - - // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning - else if(SA_pp > 0.15) - if(prob(20)) - spawn(0) emote(pick("giggle", "laugh")) - breath.adjust_gas("nitrous_oxide", -breath.gas["nitrous_oxide"]/6, update = 0) //update after - - // Were we able to breathe? - if (failed_inhale || failed_exhale) - failed_last_breath = 1 - else - failed_last_breath = 0 - adjustOxyLoss(-5) - - - // Hot air hurts :( - if((breath.temperature <= species.cold_discomfort_level || breath.temperature >= species.heat_discomfort_level) && !(COLD_RESISTANCE in mutations)) - - if(breath.temperature <= species.breath_cold_level_1) - if(prob(20)) - to_chat(src, "You feel your face freezing and icicles forming in your lungs!") - else if(breath.temperature >= species.breath_heat_level_1) - if(prob(20)) - to_chat(src, "You feel your face burning and a searing heat in your lungs!") - - if(breath.temperature >= species.heat_discomfort_level) - - if(breath.temperature >= species.breath_heat_level_3) - apply_damage(HEAT_GAS_DAMAGE_LEVEL_3, BURN, BP_HEAD, used_weapon = "Excessive Heat") - throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MAX) - else if(breath.temperature >= species.breath_heat_level_2) - apply_damage(HEAT_GAS_DAMAGE_LEVEL_2, BURN, BP_HEAD, used_weapon = "Excessive Heat") - throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MODERATE) - else if(breath.temperature >= species.breath_heat_level_1) - apply_damage(HEAT_GAS_DAMAGE_LEVEL_1, BURN, BP_HEAD, used_weapon = "Excessive Heat") - throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_LOW) - else if(species.get_environment_discomfort(src, ENVIRONMENT_COMFORT_MARKER_HOT)) - throw_alert("temp", /obj/screen/alert/warm, HOT_ALERT_SEVERITY_LOW) - else - clear_alert("temp") - - else if(breath.temperature <= species.cold_discomfort_level) - - if(breath.temperature <= species.breath_cold_level_3) - apply_damage(COLD_GAS_DAMAGE_LEVEL_3, BURN, BP_HEAD, used_weapon = "Excessive Cold") - throw_alert("temp", /obj/screen/alert/cold, COLD_ALERT_SEVERITY_MAX) - else if(breath.temperature <= species.breath_cold_level_2) - apply_damage(COLD_GAS_DAMAGE_LEVEL_2, BURN, BP_HEAD, used_weapon = "Excessive Cold") - throw_alert("temp", /obj/screen/alert/cold, COLD_ALERT_SEVERITY_MODERATE) - else if(breath.temperature <= species.breath_cold_level_1) - apply_damage(COLD_GAS_DAMAGE_LEVEL_1, BURN, BP_HEAD, used_weapon = "Excessive Cold") - throw_alert("temp", /obj/screen/alert/cold, COLD_ALERT_SEVERITY_LOW) - else if(species.get_environment_discomfort(src, ENVIRONMENT_COMFORT_MARKER_COLD)) - throw_alert("temp", /obj/screen/alert/chilly, COLD_ALERT_SEVERITY_LOW) - else - clear_alert("temp") - - //breathing in hot/cold air also heats/cools you a bit - var/temp_adj = breath.temperature - bodytemperature - if (temp_adj < 0) - temp_adj /= (BODYTEMP_COLD_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed - else - temp_adj /= (BODYTEMP_HEAT_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed - - var/relative_density = breath.total_moles / (MOLES_CELLSTANDARD * BREATH_PERCENTAGE) - temp_adj *= relative_density - - if(temp_adj > BODYTEMP_HEATING_MAX) - temp_adj = BODYTEMP_HEATING_MAX - if(temp_adj < BODYTEMP_COOLING_MAX) - temp_adj = BODYTEMP_COOLING_MAX - - bodytemperature += temp_adj - - else - clear_alert("temp") - - breath.update_values() - return 1 - -/mob/living/carbon/human/proc/handle_allergens() - if(chem_effects[CE_ALLERGEN]) - //first, multiply the basic species-level value by our allergen effect rating, so consuming multiple seperate allergen typess simultaneously hurts more - var/damage_severity = species.allergen_damage_severity * chem_effects[CE_ALLERGEN] - var/disable_severity = species.allergen_disable_severity * chem_effects[CE_ALLERGEN] - if(species.allergen_reaction & AG_PHYS_DMG) - adjustBruteLoss(damage_severity) - if(species.allergen_reaction & AG_BURN_DMG) - adjustFireLoss(damage_severity) - if(species.allergen_reaction & AG_TOX_DMG) - adjustToxLoss(damage_severity) - if(species.allergen_reaction & AG_OXY_DMG) - adjustOxyLoss(damage_severity) - if(prob(disable_severity/2)) - emote(pick("cough","gasp","choke")) - if(species.allergen_reaction & AG_EMOTE) - if(prob(disable_severity/2)) - emote(pick("pale","shiver","twitch")) - if(species.allergen_reaction & AG_PAIN) - adjustHalLoss(disable_severity) - if(species.allergen_reaction & AG_WEAKEN) - Weaken(disable_severity) - if(species.allergen_reaction & AG_BLURRY) - eye_blurry = max(eye_blurry, disable_severity) - if(species.allergen_reaction & AG_SLEEPY) - drowsyness = max(drowsyness, disable_severity) - if(species.allergen_reaction & AG_CONFUSE) - Confuse(disable_severity/4) - -/mob/living/carbon/human/handle_environment(datum/gas_mixture/environment) - if(!environment) - return - //Stuff like the xenomorph's plasma regen happens here. - species.handle_environment_special(src) - - if(is_incorporeal()) - return - - //Moved pressure calculations here for use in skip-processing check. - var/pressure = environment.return_pressure() - var/adjusted_pressure = calculate_affecting_pressure(pressure) - - //Check for contaminants before anything else because we don't want to skip it. - for(var/g in environment.gas) - if(gas_data.flags[g] & XGM_GAS_CONTAMINANT && environment.gas[g] > gas_data.overlay_limit[g] + 1) - pl_effects() - break - - if(istype(loc, /turf/space)) //VOREStation Edit - No FBPs overheating on space turfs inside mechs or people. - //Don't bother if the temperature drop is less than 0.1 anyways. Hopefully BYOND is smart enough to turn this constant expression into a constant - if(bodytemperature > (0.1 * HUMAN_HEAT_CAPACITY/(HUMAN_EXPOSED_SURFACE_AREA*STEFAN_BOLTZMANN_CONSTANT))**(1/4) + COSMIC_RADIATION_TEMPERATURE) - //Thermal radiation into space - var/heat_loss = HUMAN_EXPOSED_SURFACE_AREA * STEFAN_BOLTZMANN_CONSTANT * ((bodytemperature - COSMIC_RADIATION_TEMPERATURE)**4) - var/temperature_loss = heat_loss/HUMAN_HEAT_CAPACITY - bodytemperature -= temperature_loss - else - var/loc_temp = T0C - if(istype(loc, /obj/mecha)) - var/obj/mecha/M = loc - loc_temp = M.return_temperature() - else if(istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) - var/obj/machinery/atmospherics/unary/cryo_cell/cc = loc - loc_temp = cc.air_contents.temperature - else - loc_temp = environment.temperature - - if(adjusted_pressure < species.warning_high_pressure && adjusted_pressure > species.warning_low_pressure && abs(loc_temp - bodytemperature) < 20 && bodytemperature < species.heat_level_1 && bodytemperature > species.cold_level_1) - clear_alert("pressure") - return // Temperatures are within normal ranges, fuck all this processing. ~Ccomp - - //Body temperature adjusts depending on surrounding atmosphere based on your thermal protection (convection) - var/temp_adj = 0 - if(loc_temp < bodytemperature) //Place is colder than we are - var/thermal_protection = get_cold_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. - if(thermal_protection < 0.99) //For some reason, < 1 returns false if the value is 1. - temp_adj = (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_COLD_DIVISOR) //this will be negative - else if (loc_temp > bodytemperature) //Place is hotter than we are - var/thermal_protection = get_heat_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. - if(thermal_protection < 0.99) //For some reason, < 1 returns false if the value is 1. - temp_adj = (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR) - - //Use heat transfer as proportional to the gas density. However, we only care about the relative density vs standard 101 kPa/20 C air. Therefore we can use mole ratios - var/relative_density = environment.total_moles / MOLES_CELLSTANDARD - bodytemperature += between(BODYTEMP_COOLING_MAX, temp_adj*relative_density, BODYTEMP_HEATING_MAX) - - // +/- 50 degrees from 310.15K is the 'safe' zone, where no damage is dealt. - if(bodytemperature >= species.heat_level_1) - //Body temperature is too hot. - if(status_flags & GODMODE) - return 1 //godmode - - var/burn_dam = 0 - - // switch() can't access numbers inside variables, so we need to use some ugly if() spam ladder. - if(bodytemperature >= species.heat_level_1) - if(bodytemperature >= species.heat_level_2) - if(bodytemperature >= species.heat_level_3) - burn_dam = HEAT_DAMAGE_LEVEL_3 - throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MAX) - else - burn_dam = HEAT_DAMAGE_LEVEL_2 - throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MODERATE) - else - burn_dam = HEAT_DAMAGE_LEVEL_1 - throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_LOW) - - take_overall_damage(burn=burn_dam, used_weapon = "High Body Temperature") - - else if(bodytemperature <= species.cold_level_1) - //Body temperature is too cold. - - if(status_flags & GODMODE) - return 1 //godmode - - - if(!istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) - var/cold_dam = 0 - if(bodytemperature <= species.cold_level_1) - if(bodytemperature <= species.cold_level_2) - if(bodytemperature <= species.cold_level_3) - cold_dam = COLD_DAMAGE_LEVEL_3 - else - cold_dam = COLD_DAMAGE_LEVEL_2 - else - cold_dam = COLD_DAMAGE_LEVEL_1 - - take_overall_damage(burn=cold_dam, used_weapon = "Low Body Temperature") - - else clear_alert("temp") - - // Account for massive pressure differences. Done by Polymorph - // Made it possible to actually have something that can protect against high pressure... Done by Errorage. Polymorph now has an axe sticking from his head for his previous hardcoded nonsense! - if(status_flags & GODMODE) - return 1 //godmode - - if(adjusted_pressure >= species.hazard_high_pressure) - var/pressure_damage = min( ( (adjusted_pressure / species.hazard_high_pressure) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) - if(stat==DEAD) - pressure_damage = pressure_damage/2 - take_overall_damage(brute=pressure_damage, used_weapon = "High Pressure") - throw_alert("pressure", /obj/screen/alert/highpressure, 2) - else if(adjusted_pressure >= species.warning_high_pressure) - throw_alert("pressure", /obj/screen/alert/highpressure, 1) - else if(adjusted_pressure >= species.warning_low_pressure) - clear_alert("pressure") - else if(adjusted_pressure >= species.hazard_low_pressure) - throw_alert("pressure", /obj/screen/alert/lowpressure, 1) - else - if( !(COLD_RESISTANCE in mutations)) - if(!isSynthetic() || !nif || !nif.flag_check(NIF_O_PRESSURESEAL,NIF_FLAGS_OTHER)) //VOREStation Edit - NIF pressure seals - var/pressure_damage = LOW_PRESSURE_DAMAGE - if(stat==DEAD) - pressure_damage = pressure_damage/2 - take_overall_damage(brute=pressure_damage, used_weapon = "Low Pressure") - if(getOxyLoss() < 55) // 12 OxyLoss per 4 ticks when wearing internals; unconsciousness in 16 ticks, roughly half a minute - var/pressure_dam = 3 // 16 OxyLoss per 4 ticks when no internals present; unconsciousness in 13 ticks, roughly twenty seconds - // (Extra 1 oxyloss from failed breath) - // Being in higher pressure decreases the damage taken, down to a minimum of (species.hazard_low_pressure / ONE_ATMOSPHERE) at species.hazard_low_pressure - pressure_dam *= (ONE_ATMOSPHERE - adjusted_pressure) / ONE_ATMOSPHERE - - if(wear_suit && wear_suit.min_pressure_protection && head && head.min_pressure_protection) - var/protection = max(wear_suit.min_pressure_protection, head.min_pressure_protection) // Take the weakest protection - pressure_dam *= (protection) / (ONE_ATMOSPHERE) // Divide by ONE_ATMOSPHERE to get a fractional protection - // Stronger protection (Closer to 0) results in a smaller fraction - // Firesuits (Min protection = 0.2 atmospheres) decrease oxyloss to 1/5 - - adjustOxyLoss(pressure_dam) - throw_alert("pressure", /obj/screen/alert/lowpressure, 2) - else - clear_alert("pressure") - - return - -/* -/mob/living/carbon/human/proc/adjust_body_temperature(current, loc_temp, boost) - var/temperature = current - var/difference = abs(current-loc_temp) //get difference - var/increments// = difference/10 //find how many increments apart they are - if(difference > 50) - increments = difference/5 - else - increments = difference/10 - var/change = increments*boost // Get the amount to change by (x per increment) - var/temp_change - if(current < loc_temp) - temperature = min(loc_temp, temperature+change) - else if(current > loc_temp) - temperature = max(loc_temp, temperature-change) - temp_change = (temperature - current) - return temp_change -*/ - -/mob/living/carbon/human/proc/stabilize_body_temperature() - // We produce heat naturally. - if (species.passive_temp_gain) - bodytemperature += species.passive_temp_gain - if (species.body_temperature == null) - return //this species doesn't have metabolic thermoregulation - - // FBPs will overheat when alive, prosthetic limbs are fine. - if(stat != DEAD && robobody_count) - if(!nif || !nif.flag_check(NIF_O_HEATSINKS,NIF_FLAGS_OTHER)) //VOREStation Edit - NIF heatsinks prevent the base heat increase per tick if installed. - bodytemperature += round(robobody_count*1.15) - var/obj/item/organ/internal/robotic/heatsink/HS = internal_organs_by_name[O_HEATSINK] - if(!HS || HS.is_broken()) // However, NIF Heatsinks will not compensate for a core FBP component (your heatsink) being lost. - bodytemperature += round(robobody_count*0.5) - - var/body_temperature_difference = species.body_temperature - bodytemperature - - if (abs(body_temperature_difference) < 0.5) - return //fuck this precision - - if (on_fire) - return //too busy for pesky metabolic regulation - - if(bodytemperature < species.cold_level_1) //260.15 is 310.15 - 50, the temperature where you start to feel effects. - if(nutrition >= 2) //If we are very, very cold we'll use up quite a bit of nutriment to heat us up. - adjust_nutrition(-2) - var/recovery_amt = max((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM) - //to_world("Cold. Difference = [body_temperature_difference]. Recovering [recovery_amt]") -// log_debug("Cold. Difference = [body_temperature_difference]. Recovering [recovery_amt]") - bodytemperature += recovery_amt - else if(species.cold_level_1 <= bodytemperature && bodytemperature <= species.heat_level_1) - var/recovery_amt = body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR - //to_world("Norm. Difference = [body_temperature_difference]. Recovering [recovery_amt]") -// log_debug("Norm. Difference = [body_temperature_difference]. Recovering [recovery_amt]") - bodytemperature += recovery_amt - else if(bodytemperature > species.heat_level_1) //360.15 is 310.15 + 50, the temperature where you start to feel effects. - //We totally need a sweat system cause it totally makes sense...~ - var/recovery_amt = min((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), -BODYTEMP_AUTORECOVERY_MINIMUM) //We're dealing with negative numbers - //to_world("Hot. Difference = [body_temperature_difference]. Recovering [recovery_amt]") -// log_debug("Hot. Difference = [body_temperature_difference]. Recovering [recovery_amt]") - bodytemperature += recovery_amt - - //This proc returns a number made up of the flags for body parts which you are protected on. (such as HEAD, UPPER_TORSO, LOWER_TORSO, etc. See setup.dm for the full list) -/mob/living/carbon/human/proc/get_heat_protection_flags(temperature) //Temperature is the temperature you're being exposed to. - . = 0 - //Handle normal clothing - for(var/obj/item/clothing/C in list(head,wear_suit,w_uniform,shoes,gloves,wear_mask)) - if(C) - if(C.handle_high_temperature(temperature)) - . |= C.get_heat_protection_flags() - -//See proc/get_heat_protection_flags(temperature) for the description of this proc. -/mob/living/carbon/human/proc/get_cold_protection_flags(temperature) - . = 0 - //Handle normal clothing - for(var/obj/item/clothing/C in list(head,wear_suit,w_uniform,shoes,gloves,wear_mask)) - if(C) - if(C.handle_low_temperature(temperature)) - . |= C.get_cold_protection_flags() - -/mob/living/carbon/human/get_heat_protection(temperature) //Temperature is the temperature you're being exposed to. - var/thermal_protection_flags = get_heat_protection_flags(temperature) - - . = get_thermal_protection(thermal_protection_flags) - . = 1 - . // Invert from 1 = immunity to 0 = immunity. - - // Doing it this way makes multiplicative stacking not get out of hand, so two modifiers that give 0.5 protection will be combined to 0.75 in the end. - for(var/datum/modifier/M as anything in modifiers) - if(!isnull(M.heat_protection)) - . *= 1 - M.heat_protection - - // Code that calls this expects 1 = immunity so we need to invert again. - . = 1 - . - . = min(., 1.0) - -/mob/living/carbon/human/get_cold_protection(temperature) - if(COLD_RESISTANCE in mutations) - return 1 //Fully protected from the cold. - - temperature = max(temperature, 2.7) //There is an occasional bug where the temperature is miscalculated in ares with a small amount of gas on them, so this is necessary to ensure that that bug does not affect this calculation. Space's temperature is 2.7K and most suits that are intended to protect against any cold, protect down to 2.0K. - var/thermal_protection_flags = get_cold_protection_flags(temperature) - - . = get_thermal_protection(thermal_protection_flags) - . = 1 - . // Invert from 1 = immunity to 0 = immunity. - - // Doing it this way makes multiplicative stacking not get out of hand, so two modifiers that give 0.5 protection will be combined to 0.75 in the end. - for(var/datum/modifier/M as anything in modifiers) - if(!isnull(M.cold_protection)) - // Invert the modifier values so they align with the current working value. - . *= 1 - M.cold_protection - - // Code that calls this expects 1 = immunity so we need to invert again. - . = 1 - . - . = min(., 1.0) - -/mob/living/carbon/human/proc/get_thermal_protection(var/flags) - .=0 - if(flags) - if(flags & HEAD) - . += THERMAL_PROTECTION_HEAD - if(flags & UPPER_TORSO) - . += THERMAL_PROTECTION_UPPER_TORSO - if(flags & LOWER_TORSO) - . += THERMAL_PROTECTION_LOWER_TORSO - if(flags & LEG_LEFT) - . += THERMAL_PROTECTION_LEG_LEFT - if(flags & LEG_RIGHT) - . += THERMAL_PROTECTION_LEG_RIGHT - if(flags & FOOT_LEFT) - . += THERMAL_PROTECTION_FOOT_LEFT - if(flags & FOOT_RIGHT) - . += THERMAL_PROTECTION_FOOT_RIGHT - if(flags & ARM_LEFT) - . += THERMAL_PROTECTION_ARM_LEFT - if(flags & ARM_RIGHT) - . += THERMAL_PROTECTION_ARM_RIGHT - if(flags & HAND_LEFT) - . += THERMAL_PROTECTION_HAND_LEFT - if(flags & HAND_RIGHT) - . += THERMAL_PROTECTION_HAND_RIGHT - return min(1,.) - -/mob/living/carbon/human/handle_chemicals_in_body() - - if(inStasisNow()) - return - - if(reagents) - chem_effects.Cut() - - if(touching) - touching.metabolize() - if(ingested) - ingested.metabolize() - if(bloodstr) - bloodstr.metabolize() - - if(!isSynthetic()) - - var/total_phoronloss = 0 - for(var/obj/item/I in src) - if(I.contaminated) - if(check_belly(I)) continue //VOREStation Edit - if(src.species && src.species.get_bodytype() != "Vox" && src.species.get_bodytype() != "Shadekin") //VOREStation Edit: shadekin - // This is hacky, I'm so sorry. - if(I != l_hand && I != r_hand) //If the item isn't in your hands, you're probably wearing it. Full damage for you. - total_phoronloss += vsc.plc.CONTAMINATION_LOSS - else if(I == l_hand) //If the item is in your hands, but you're wearing protection, you might be alright. - var/l_hand_blocked = 0 - l_hand_blocked = 1-(100-getarmor(BP_L_HAND, "bio"))/100 //This should get a number between 0 and 1 - total_phoronloss += vsc.plc.CONTAMINATION_LOSS * l_hand_blocked - else if(I == r_hand) //If the item is in your hands, but you're wearing protection, you might be alright. - var/r_hand_blocked = 0 - r_hand_blocked = 1-(100-getarmor(BP_R_HAND, "bio"))/100 //This should get a number between 0 and 1 - total_phoronloss += vsc.plc.CONTAMINATION_LOSS * r_hand_blocked - if(total_phoronloss) - if(!(status_flags & GODMODE)) - adjustToxLoss(total_phoronloss) - - if(status_flags & GODMODE) - return 0 //godmode - - if(species.light_dam) - var/light_amount = 0 - if(isturf(loc)) - var/turf/T = loc - light_amount = T.get_lumcount() * 10 - if(light_amount > species.light_dam) //if there's enough light, start dying - take_overall_damage(1,1) - else //heal in the dark - heal_overall_damage(1,1) - - // nutrition decrease - if (nutrition > 0 && stat != DEAD) - var/nutrition_reduction = species.hunger_factor - - for(var/datum/modifier/mod in modifiers) - if(!isnull(mod.metabolism_percent)) - nutrition_reduction *= mod.metabolism_percent - adjust_nutrition(-nutrition_reduction) - - if(noisy == TRUE && nutrition < 250 && prob(10)) //VOREStation edit for hunger noises. - var/sound/growlsound = sound(get_sfx("hunger_sounds")) - var/growlmultiplier = 100 - (nutrition / 250 * 100) - playsound(src, growlsound, vol = growlmultiplier, vary = 1, falloff = 0.1, ignore_walls = TRUE, preference = /datum/client_preference/digestion_noises) - // VOREStation Edit End - - // TODO: stomach and bloodstream organ. - if(!isSynthetic()) - handle_trace_chems() - - updatehealth() - - return //TODO: DEFERRED - -//DO NOT CALL handle_statuses() from this proc, it's called from living/Life() as long as this returns a true value. -/mob/living/carbon/human/handle_regular_status_updates() - if(skip_some_updates()) - return 0 - - if(status_flags & GODMODE) return 0 - - //SSD check, if a logged player is awake put them back to sleep! - if(species.get_ssd(src) && !client && !teleop) - Sleeping(2) - if(stat == DEAD) //DEAD. BROWN BREAD. SWIMMING WITH THE SPESS CARP - blinded = 1 - silent = 0 - else //ALIVE. LIGHTS ARE ON - updatehealth() //TODO - - if(health <= config.health_threshold_dead || (should_have_organ("brain") && !has_brain())) - death() - blinded = 1 - silent = 0 - return 1 - - //UNCONSCIOUS. NO-ONE IS HOME - if((getOxyLoss() > (species.total_health/2)) || (health <= config.health_threshold_crit)) - Paralyse(3) - - if(hallucination) - if(hallucination >= 20 && !(species.flags & (NO_POISON|IS_PLANT|NO_HALLUCINATION)) ) - if(prob(3)) - fake_attack(src) - if(!handling_hal) - spawn handle_hallucinations() //The not boring kind! - if(client && prob(5)) - client.dir = pick(2,4,8) - spawn(rand(20,50)) - client.dir = 1 - - hallucination = max(0, hallucination - 2) - else - for(var/atom/a in hallucinations) - qdel(a) - - //Brain damage from Oxyloss - if(should_have_organ("brain")) - var/brainOxPercent = 0.015 //Default 1.5% of your current oxyloss is applied as brain damage, 50 oxyloss is 1 brain damage - if(CE_STABLE in chem_effects) - brainOxPercent = 0.008 //Halved in effect - if(oxyloss >= (getMaxHealth() * 0.3) && prob(5)) // If oxyloss exceeds 30% of your max health, you can take brain damage. - adjustBrainLoss(brainOxPercent * oxyloss) - - if(halloss >= species.total_health) - to_chat(src, "You're in too much pain to keep going...") - src.visible_message("[src] slumps to the ground, too weak to continue fighting.") - Paralyse(10) - setHalLoss(species.total_health - 1) - - if(paralysis || sleeping) - blinded = 1 - set_stat(UNCONSCIOUS) - animate_tail_reset() - adjustHalLoss(-3) - - if(sleeping) - handle_dreams() - if (mind) - //Are they SSD? If so we'll keep them asleep but work off some of that sleep var in case of stoxin or similar. - if(client || sleeping > 3) - AdjustSleeping(-1) - throw_alert("asleep", /obj/screen/alert/asleep) - if( prob(2) && health && !hal_crit && client ) - spawn(0) - emote("snore") - //CONSCIOUS - else - set_stat(CONSCIOUS) - clear_alert("asleep") - - //Periodically double-check embedded_flag - if(embedded_flag && !(life_tick % 10)) - var/list/E - E = get_visible_implants(0) - if(!E.len) - embedded_flag = 0 - - //Eyes - //Check rig first because it's two-check and other checks will override it. - if(istype(back,/obj/item/weapon/rig)) - var/obj/item/weapon/rig/O = back - if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & EYES)) - if((O.offline && O.offline_vision_restriction == 2) || (!O.offline && O.vision_restriction == 2)) - blinded = 1 - - // Check everything else. - - //Periodically double-check embedded_flag - if(embedded_flag && !(life_tick % 10)) - if(!embedded_needs_process()) - embedded_flag = 0 - //Vision - var/obj/item/organ/vision - if(species.vision_organ) - vision = internal_organs_by_name[species.vision_organ] - - if(!species.vision_organ) // Presumably if a species has no vision organs, they see via some other means. - SetBlinded(0) - blinded = 0 - eye_blurry = 0 - clear_alert("blind") - else if(!vision || vision.is_broken()) // Vision organs cut out or broken? Permablind. - SetBlinded(1) - blinded = 1 - eye_blurry = 1 - throw_alert("blind", /obj/screen/alert/blind) - else //You have the requisite organs - if(sdisabilities & BLIND) // Disabled-blind, doesn't get better on its own - blinded = 1 - throw_alert("blind", /obj/screen/alert/blind) - else if(eye_blind) // Blindness, heals slowly over time - AdjustBlinded(-1) - blinded = 1 - throw_alert("blind", /obj/screen/alert/blind) - else if(istype(glasses, /obj/item/clothing/glasses/sunglasses/blindfold)) //resting your eyes with a blindfold heals blurry eyes faster - eye_blurry = max(eye_blurry-3, 0) - blinded = 1 - throw_alert("blind", /obj/screen/alert/blind) - - //blurry sight - if(vision.is_bruised()) // Vision organs impaired? Permablurry. - eye_blurry = 1 - if(eye_blurry) // Blurry eyes heal slowly - eye_blurry = max(eye_blurry-1, 0) - - //Ears - if(sdisabilities & DEAF) //disabled-deaf, doesn't get better on its own - ear_deaf = max(ear_deaf, 1) - else if(ear_deaf) //deafness, heals slowly over time - ear_deaf = max(ear_deaf-1, 0) - else if(get_ear_protection() >= 2) //resting your ears with earmuffs heals ear damage faster - ear_damage = max(ear_damage-0.15, 0) - ear_deaf = max(ear_deaf, 1) - else if(ear_damage < 25) //ear damage heals slowly under this threshold. otherwise you'll need earmuffs - ear_damage = max(ear_damage-0.05, 0) - - //Resting - if(resting) - dizziness = max(0, dizziness - 15) - jitteriness = max(0, jitteriness - 15) - adjustHalLoss(-3) - else - dizziness = max(0, dizziness - 3) - jitteriness = max(0, jitteriness - 3) - adjustHalLoss(-1) - - if (drowsyness) - drowsyness = max(0, drowsyness - 1) - eye_blurry = max(2, eye_blurry) - if (prob(5)) - Sleeping(1) - Paralyse(5) - - // If you're dirty, your gloves will become dirty, too. - if(gloves && germ_level > gloves.germ_level && prob(10)) - gloves.germ_level += 1 - - return 1 - -/mob/living/carbon/human/set_stat(var/new_stat) - . = ..() - if(. && stat) - update_skin(1) - -/mob/living/carbon/human/handle_regular_hud_updates() - if(hud_updateflag) // update our mob's hud overlays, AKA what others see flaoting above our head - handle_hud_list() - - // now handle what we see on our screen - - if(!client) - return 0 - - ..() - - client.screen.Remove(global_hud.blurry, global_hud.druggy, global_hud.vimpaired, global_hud.darkMask, global_hud.nvg, global_hud.thermal, global_hud.meson, global_hud.science, global_hud.material, global_hud.whitense) - - if(istype(client.eye,/obj/machinery/camera)) - var/obj/machinery/camera/cam = client.eye - client.screen |= cam.client_huds - - if(stat == DEAD) //Dead - if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO - if(healths) healths.icon_state = "health7" //DEAD healthmeter - - else if(stat == UNCONSCIOUS && health <= 0) //Crit - //Critical damage passage overlay - var/severity = 0 - switch(health) - if(-20 to -10) severity = 1 - if(-30 to -20) severity = 2 - if(-40 to -30) severity = 3 - if(-50 to -40) severity = 4 - if(-60 to -50) severity = 5 - if(-70 to -60) severity = 6 - if(-80 to -70) severity = 7 - if(-90 to -80) severity = 8 - if(-95 to -90) severity = 9 - if(-INFINITY to -95) severity = 10 - overlay_fullscreen("crit", /obj/screen/fullscreen/crit, severity) - else //Alive - clear_fullscreen("crit") - //Oxygen damage overlay - if(oxyloss) - var/severity = 0 - switch(oxyloss) - if(10 to 20) severity = 1 - if(20 to 25) severity = 2 - if(25 to 30) severity = 3 - if(30 to 35) severity = 4 - if(35 to 40) severity = 5 - if(40 to 45) severity = 6 - if(45 to INFINITY) severity = 7 - overlay_fullscreen("oxy", /obj/screen/fullscreen/oxy, severity) - else - clear_fullscreen("oxy") - - //Fire and Brute damage overlay (BSSR) - var/hurtdamage = src.getShockBruteLoss() + src.getShockFireLoss() + damageoverlaytemp //Doesn't call the overlay if you can't actually feel it - damageoverlaytemp = 0 // We do this so we can detect if someone hits us or not. - if(hurtdamage) - var/severity = 0 - switch(hurtdamage) - if(10 to 25) severity = 1 - if(25 to 40) severity = 2 - if(40 to 55) severity = 3 - if(55 to 70) severity = 4 - if(70 to 85) severity = 5 - if(85 to INFINITY) severity = 6 - overlay_fullscreen("brute", /obj/screen/fullscreen/brute, severity) - else - clear_fullscreen("brute") - - if(healths) - if (chem_effects[CE_PAINKILLER] > 100) - healths.icon_state = "health_numb" - else - // Generate a by-limb health display. - var/mutable_appearance/healths_ma = new(healths) - healths_ma.icon_state = "blank" - healths_ma.overlays = null - healths_ma.plane = PLANE_PLAYER_HUD - - var/no_damage = 1 - var/trauma_val = 0 // Used in calculating softcrit/hardcrit indicators. - if(!(species.flags & NO_PAIN)) - trauma_val = max(traumatic_shock,halloss)/species.total_health - var/limb_trauma_val = trauma_val*0.3 - // Collect and apply the images all at once to avoid appearance churn. - var/list/health_images = list() - for(var/obj/item/organ/external/E in organs) - if(no_damage && (E.brute_dam || E.burn_dam)) - no_damage = 0 - health_images += E.get_damage_hud_image(limb_trauma_val) - - // Apply a fire overlay if we're burning. - if(on_fire) - health_images += image('icons/mob/OnFire.dmi',"[get_fire_icon_state()]") - - // Show a general pain/crit indicator if needed. - if(trauma_val) - if(!(species.flags & NO_PAIN)) - if(trauma_val > 0.7) - health_images += image('icons/mob/screen1_health.dmi',"softcrit") - if(trauma_val >= 1) - health_images += image('icons/mob/screen1_health.dmi',"hardcrit") - else if(no_damage) - health_images += image('icons/mob/screen1_health.dmi',"fullhealth") - - healths_ma.add_overlay(health_images) - healths.appearance = healths_ma - - - var/fat_alert = /obj/screen/alert/fat - var/hungry_alert = /obj/screen/alert/hungry - var/starving_alert = /obj/screen/alert/starving - - if(isSynthetic()) - fat_alert = /obj/screen/alert/fat/synth - hungry_alert = /obj/screen/alert/hungry/synth - starving_alert = /obj/screen/alert/starving/synth - //VOREStation Add - Vampire hunger alert - else if(get_species() == SPECIES_CUSTOM) - var/datum/species/custom/C = species - if(/datum/trait/neutral/bloodsucker in C.traits) - fat_alert = /obj/screen/alert/fat/vampire - hungry_alert = /obj/screen/alert/hungry/vampire - starving_alert = /obj/screen/alert/starving/vampire - //VOREStation Add End - - switch(nutrition) - if(450 to INFINITY) - throw_alert("nutrition", fat_alert) - // if(350 to 450) - // if(250 to 350) // Alternative more-detailed tiers, not used. - if(250 to 450) - clear_alert("nutrition") - if(150 to 250) - throw_alert("nutrition", hungry_alert) - else - throw_alert("nutrition", starving_alert) - - if(blinded) - overlay_fullscreen("blind", /obj/screen/fullscreen/blind) - throw_alert("blind", /obj/screen/alert/blind) - else - clear_fullscreen("blind") - clear_alert("blind") - - var/apply_nearsighted_overlay = FALSE - if(disabilities & NEARSIGHTED) - apply_nearsighted_overlay = TRUE - - if(glasses) - var/obj/item/clothing/glasses/G = glasses - if(G.prescription) - apply_nearsighted_overlay = FALSE - - if(nif && nif.flag_check(NIF_V_CORRECTIVE, NIF_FLAGS_VISION)) // VOREStation Edit - NIF - apply_nearsighted_overlay = FALSE - - set_fullscreen(apply_nearsighted_overlay, "nearsighted", /obj/screen/fullscreen/impaired, 1) - - set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) - set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) - if(druggy) - throw_alert("high", /obj/screen/alert/high) - else - clear_alert("high") - - if(!isbelly(loc)) //VOREStation Add - Belly fullscreens safety - clear_fullscreen("belly") - clear_fullscreen("belly2") - clear_fullscreen("belly3") - clear_fullscreen("belly4") - - if(config.welder_vision) - var/found_welder - if(species.short_sighted) - found_welder = 1 - else - if(istype(glasses, /obj/item/clothing/glasses/welding)) - var/obj/item/clothing/glasses/welding/O = glasses - if(!O.up) - found_welder = 1 - if(!found_welder && nif && nif.flag_check(NIF_V_UVFILTER,NIF_FLAGS_VISION)) found_welder = 1 //VOREStation Add - NIF - if(istype(glasses, /obj/item/clothing/glasses/sunglasses/thinblindfold)) - found_welder = 1 - if(!found_welder && istype(head, /obj/item/clothing/head/welding)) - var/obj/item/clothing/head/welding/O = head - if(!O.up) - found_welder = 1 - if(!found_welder && istype(back, /obj/item/weapon/rig)) - var/obj/item/weapon/rig/O = back - if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & EYES)) - if((O.offline && O.offline_vision_restriction == 1) || (!O.offline && O.vision_restriction == 1)) - found_welder = 1 - if(absorbed) found_welder = 1 //VOREStation Code - if(found_welder) - client.screen |= global_hud.darkMask - -/mob/living/carbon/human/reset_view(atom/A) - ..() - if(machine_visual && machine_visual != A) - machine_visual.remove_visual(src) - -/mob/living/carbon/human/handle_vision() - if(stat == DEAD) - sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS|SEE_SELF - see_in_dark = 8 - if(client) - if(client.view != world.view) // If mob dies while zoomed in with device, unzoom them. - for(var/obj/item/item in contents) - if(item.zoom) - item.zoom() - break - - else //We aren't dead - sight &= ~(SEE_TURFS|SEE_MOBS|SEE_OBJS) - see_invisible = see_in_dark>2 ? SEE_INVISIBLE_LEVEL_ONE : see_invisible_default - - // Do this early so certain stuff gets turned off before vision is assigned. - var/area/A = get_area(src) - if(A?.no_spoilers) - disable_spoiler_vision() - - if(XRAY in mutations) - sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS - see_in_dark = 8 - if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO - - if(seer==1) - var/obj/effect/rune/R = locate() in loc - if(R && R.word1 == cultwords["see"] && R.word2 == cultwords["hell"] && R.word3 == cultwords["join"]) - see_invisible = SEE_INVISIBLE_CULT - else - see_invisible = see_invisible_default - seer = 0 - - if(!seedarkness) - sight = species.get_vision_flags(src) - see_in_dark = 8 - see_invisible = SEE_INVISIBLE_NOLIGHTING - - else - sight = species.get_vision_flags(src) - see_in_dark = species.darksight - see_invisible = see_in_dark>2 ? SEE_INVISIBLE_LEVEL_ONE : see_invisible_default - - var/glasses_processed = 0 - var/obj/item/weapon/rig/rig = get_rig() - if(istype(rig) && rig.visor && !looking_elsewhere) - if(!rig.helmet || (head && rig.helmet == head)) - if(rig.visor && rig.visor.vision && rig.visor.active && rig.visor.vision.glasses) - glasses_processed = process_glasses(rig.visor.vision.glasses) - - if(glasses && !glasses_processed && !looking_elsewhere) - glasses_processed = process_glasses(glasses) - if(XRAY in mutations) - sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS - see_in_dark = 8 - if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO - - for(var/datum/modifier/M in modifiers) - if(!isnull(M.vision_flags)) - sight |= M.vision_flags - - if(!glasses_processed && nif) - var/datum/nifsoft/vision_soft - for(var/datum/nifsoft/NS in nif.nifsofts) - if(NS.vision_exclusive && NS.active) - vision_soft = NS - break - if(vision_soft) - glasses_processed = process_nifsoft_vision(vision_soft) //not really glasses but equitable - - if(!glasses_processed && (species.get_vision_flags(src) > 0)) - sight |= species.get_vision_flags(src) - if(!seer && !glasses_processed && seedarkness) - see_invisible = see_invisible_default - - if(machine) - var/viewflags = machine.check_eye(src) - if(viewflags < 0) - reset_view(null, 0) - else if(viewflags && !looking_elsewhere) - sight |= viewflags - else - machine.apply_visual(src) - else if(eyeobj) - if(eyeobj.owner != src) - - reset_view(null) - else - var/isRemoteObserve = 0 - if((mRemote in mutations) && remoteview_target) - if(remoteview_target.stat==CONSCIOUS) - isRemoteObserve = 1 - if(!isRemoteObserve && client && !client.adminobs) - remoteview_target = null - reset_view(null, 0) - return 1 - -/mob/living/carbon/human/proc/process_glasses(var/obj/item/clothing/glasses/G) - . = FALSE - if(G && G.active) - if(G.darkness_view) - see_in_dark += G.darkness_view - . = TRUE - if(G.overlay && client) - client.screen |= G.overlay - if(G.vision_flags) - sight |= G.vision_flags - . = TRUE - if(istype(G,/obj/item/clothing/glasses/night) && !seer) - see_invisible = SEE_INVISIBLE_MINIMUM - - if(G.see_invisible >= 0) - see_invisible = G.see_invisible - . = TRUE - else if(!druggy && !seer) - see_invisible = see_invisible_default - -/mob/living/carbon/human/proc/process_nifsoft_vision(var/datum/nifsoft/NS) - . = FALSE - if(NS && NS.active) - if(NS.darkness_view) - see_in_dark += NS.darkness_view - . = TRUE - if(NS.vision_flags_mob) - sight |= NS.vision_flags_mob - . = TRUE - -/mob/living/carbon/human/handle_random_events() - if(inStasisNow()) - return - - // Puke if toxloss is too high - if(!stat && !isbelly(loc)) - if (getToxLoss() >= 30 && isSynthetic()) - if(!confused) - if(prob(5)) - to_chat(src, "You lose directional control!") - Confuse(10) - if (getToxLoss() >= 45 && !isSynthetic()) - spawn vomit() - - - //0.1% chance of playing a scary sound to someone who's in complete darkness - if(isturf(loc) && rand(1,1000) == 1) - var/turf/T = loc - if(T.get_lumcount() <= LIGHTING_SOFT_THRESHOLD) - //VOREStation Add Start - if(text2num(time2text(world.timeofday, "MM")) == 4) - if(text2num(time2text(world.timeofday, "DD")) == 1) - playsound_local(src,pick(scawwySownds),50, 0) - return - //VOREStation Add End - playsound_local(src,pick(scarySounds),50, 1, -1) - -/mob/living/carbon/human/handle_stomach() - spawn(0) - for(var/mob/living/M in stomach_contents) - if(M.loc != src) - stomach_contents.Remove(M) - continue - if(istype(M, /mob/living/carbon) && stat != 2) - if(M.stat == 2) - M.death(1) - stomach_contents.Remove(M) - qdel(M) - continue - if(air_master.current_cycle%3==1) - if(!(M.status_flags & GODMODE)) - M.adjustBruteLoss(5) - adjust_nutrition(10) - -/mob/living/carbon/human/proc/handle_changeling() - if(mind && mind.changeling) - mind.changeling.regenerate() - if(hud_used) - ling_chem_display.invisibility = 0 -// ling_chem_display.maptext = "
                    [round(mind.changeling.chem_charges)]
                    " - switch(mind.changeling.chem_storage) - if(1 to 50) - switch(mind.changeling.chem_charges) - if(0 to 9) - ling_chem_display.icon_state = "ling_chems0" - if(10 to 19) - ling_chem_display.icon_state = "ling_chems10" - if(20 to 29) - ling_chem_display.icon_state = "ling_chems20" - if(30 to 39) - ling_chem_display.icon_state = "ling_chems30" - if(40 to 49) - ling_chem_display.icon_state = "ling_chems40" - if(50) - ling_chem_display.icon_state = "ling_chems50" - if(51 to 80) //This is a crappy way of checking for engorged sacs... - switch(mind.changeling.chem_charges) - if(0 to 9) - ling_chem_display.icon_state = "ling_chems0e" - if(10 to 19) - ling_chem_display.icon_state = "ling_chems10e" - if(20 to 29) - ling_chem_display.icon_state = "ling_chems20e" - if(30 to 39) - ling_chem_display.icon_state = "ling_chems30e" - if(40 to 49) - ling_chem_display.icon_state = "ling_chems40e" - if(50 to 59) - ling_chem_display.icon_state = "ling_chems50e" - if(60 to 69) - ling_chem_display.icon_state = "ling_chems60e" - if(70 to 79) - ling_chem_display.icon_state = "ling_chems70e" - if(80) - ling_chem_display.icon_state = "ling_chems80e" - else - if(mind && hud_used) - ling_chem_display.invisibility = 101 - -/mob/living/carbon/human/handle_shock() - ..() - if(status_flags & GODMODE) return 0 //godmode - if(!can_feel_pain()) return - - if(health < config.health_threshold_softcrit)// health 0 makes you immediately collapse - shock_stage = max(shock_stage, 61) - - if(traumatic_shock >= 80) - shock_stage += 1 - else if(health < config.health_threshold_softcrit) - shock_stage = max(shock_stage, 61) - else - shock_stage = min(shock_stage, 160) - shock_stage = max(shock_stage-1, 0) - - if(stat) - return 0 - - if(shock_stage == 10) - if(traumatic_shock >= 80) - custom_pain("[pick("It hurts so much", "You really need some painkillers", "Dear god, the pain")]!", 40) - - if(shock_stage >= 30) - if(shock_stage == 30 && !isbelly(loc)) //VOREStation Edit - custom_emote(VISIBLE_MESSAGE, "is having trouble keeping their eyes open.") - eye_blurry = max(2, eye_blurry) - if(traumatic_shock >= 80) - stuttering = max(stuttering, 5) - - - if(shock_stage == 40) - if(traumatic_shock >= 80) - to_chat(src, "[pick("The pain is excruciating", "Please, just end the pain", "Your whole body is going numb")]!") - - if (shock_stage >= 60) - if(shock_stage == 60 && !isbelly(loc)) //VOREStation Edit - custom_emote(VISIBLE_MESSAGE, "'s body becomes limp.") - if (prob(2)) - if(traumatic_shock >= 80) - to_chat(src, "[pick("The pain is excruciating", "Please, just end the pain", "Your whole body is going numb")]!") - Weaken(20) - - if(shock_stage >= 80) - if (prob(5)) - if(traumatic_shock >= 80) - to_chat(src, "[pick("The pain is excruciating", "Please, just end the pain", "Your whole body is going numb")]!") - Weaken(20) - - if(shock_stage >= 120) - if (prob(2)) - if(traumatic_shock >= 80) - to_chat(src, "[pick("You black out", "You feel like you could die any moment now", "You are about to lose consciousness")]!") - Paralyse(5) - - if(shock_stage == 150) - if(!isbelly(loc)) //VOREStation Edit - custom_emote(VISIBLE_MESSAGE, "can no longer stand, collapsing!") - Weaken(20) - - if(shock_stage >= 150) - Weaken(20) - -/mob/living/carbon/human/proc/handle_pulse() - if(life_tick % 5) return pulse //update pulse every 5 life ticks (~1 tick/sec, depending on server load) - - var/temp = PULSE_NORM - - var/brain_modifier = 1 - - var/modifier_shift = 0 - var/modifier_set - - if(modifiers && modifiers.len) - for(var/datum/modifier/mod in modifiers) - if(isnull(modifier_set) && !isnull(mod.pulse_set_level)) - modifier_set = round(mod.pulse_set_level) // Should be a whole number, but let's not take chances. - else if(mod.pulse_set_level > modifier_set) - modifier_set = round(mod.pulse_set_level) - - modifier_set = max(0, modifier_set) // No setting to negatives. - - if(mod.pulse_modifier) - modifier_shift += mod.pulse_modifier - - modifier_shift = round(modifier_shift) - - if(!internal_organs_by_name[O_HEART]) - temp = PULSE_NONE - if(!isnull(modifier_set)) - temp = modifier_set - return temp //No blood, no pulse. - - if(stat == DEAD) - temp = PULSE_NONE - if(!isnull(modifier_set)) - temp = modifier_set - return temp //that's it, you're dead, nothing can influence your pulse, aside from outside means. - - var/obj/item/organ/internal/heart/Pump = internal_organs_by_name[O_HEART] - - var/obj/item/organ/internal/brain/Control = internal_organs_by_name[O_BRAIN] - - if(Control) - brain_modifier = Control.get_control_efficiency() - - if(brain_modifier <= 0.7 && brain_modifier >= 0.4) // 70%-40% control, things start going weird as the brain is failing. - brain_modifier = rand(5, 15) / 10 - - if(Pump) - temp += Pump.standard_pulse_level - PULSE_NORM - - if(round(vessel.get_reagent_amount("blood")) <= species.blood_volume*species.blood_level_danger) //how much blood do we have - temp = temp + 3 //not enough :( - - if(status_flags & FAKEDEATH) - temp = PULSE_NONE //pretend that we're dead. unlike actual death, can be inflienced by meds - - if(!isnull(modifier_set)) - temp = modifier_set - - temp = max(0, temp + modifier_shift) // No negative pulses. - - if(Pump) - for(var/datum/reagent/R in reagents.reagent_list) - if(R.id in bradycardics) - if(temp <= Pump.standard_pulse_level + 3 && temp >= Pump.standard_pulse_level) - temp-- - if(R.id in tachycardics) - if(temp <= Pump.standard_pulse_level + 1 && temp >= PULSE_NONE) - temp++ - if(R.id in heartstopper) //To avoid using fakedeath - temp = PULSE_NONE - if(R.id in cheartstopper) //Conditional heart-stoppage - if(R.volume >= R.overdose) - temp = PULSE_NONE - return temp * brain_modifier - //handles different chems' influence on pulse - for(var/datum/reagent/R in reagents.reagent_list) - if(R.id in bradycardics) - if(temp <= PULSE_THREADY && temp >= PULSE_NORM) - temp-- - if(R.id in tachycardics) - if(temp <= PULSE_FAST && temp >= PULSE_NONE) - temp++ - if(R.id in heartstopper) //To avoid using fakedeath - temp = PULSE_NONE - if(R.id in cheartstopper) //Conditional heart-stoppage - if(R.volume >= R.overdose) - temp = PULSE_NONE - - return max(0, round(temp * brain_modifier)) - -/mob/living/carbon/human/proc/handle_heartbeat() - if(pulse == PULSE_NONE) - return - - var/obj/item/organ/internal/heart/H = internal_organs_by_name[O_HEART] - - if(!H || (H.robotic >= ORGAN_ROBOT)) - return - - if(pulse >= PULSE_2FAST || shock_stage >= 10 || (istype(get_turf(src), /turf/space) && is_preference_enabled(/datum/client_preference/play_ambiance))) - //PULSE_THREADY - maximum value for pulse, currently it 5. - //High pulse value corresponds to a fast rate of heartbeat. - //Divided by 2, otherwise it is too slow. - var/rate = (PULSE_THREADY - pulse)/2 - - if(heartbeat >= rate) - heartbeat = 0 - src << sound('sound/effects/singlebeat.ogg',0,0,0,50) - else - heartbeat++ - -/* - Called by life(), instead of having the individual hud items update icons each tick and check for status changes - we only set those statuses and icons upon changes. Then those HUD items will simply add those pre-made images. - This proc below is only called when those HUD elements need to change as determined by the mobs hud_updateflag. -*/ -/mob/living/carbon/human/proc/handle_hud_list() - if (BITTEST(hud_updateflag, HEALTH_HUD)) - var/image/holder = grab_hud(HEALTH_HUD) - if(stat == DEAD) - holder.icon_state = "-100" // X_X - else - holder.icon_state = RoundHealth((health-config.health_threshold_crit)/(getMaxHealth()-config.health_threshold_crit)*100) - apply_hud(HEALTH_HUD, holder) - - if (BITTEST(hud_updateflag, LIFE_HUD)) - var/image/holder = grab_hud(LIFE_HUD) - if(isSynthetic()) - holder.icon_state = "hudrobo" - else if(stat == DEAD) - holder.icon_state = "huddead" - else - holder.icon_state = "hudhealthy" - apply_hud(LIFE_HUD, holder) - - if (BITTEST(hud_updateflag, STATUS_HUD)) - var/foundVirus = 0 - for (var/ID in virus2) - if (ID in virusDB) - foundVirus = 1 - break - - var/image/holder = grab_hud(STATUS_HUD) - var/image/holder2 = grab_hud(STATUS_HUD_OOC) - if (isSynthetic()) - holder.icon_state = "hudrobo" - else if(stat == DEAD) - holder.icon_state = "huddead" - holder2.icon_state = "huddead" - else if(foundVirus) - holder.icon_state = "hudill" - else if(has_brain_worms()) - var/mob/living/simple_mob/animal/borer/B = has_brain_worms() - if(B.controlling) - holder.icon_state = "hudbrainworm" - else - holder.icon_state = "hudhealthy" - holder2.icon_state = "hudbrainworm" - else - holder.icon_state = "hudhealthy" - if(virus2.len) - holder2.icon_state = "hudill" - else - holder2.icon_state = "hudhealthy" - - apply_hud(STATUS_HUD, holder) - apply_hud(STATUS_HUD_OOC, holder2) - - if (BITTEST(hud_updateflag, ID_HUD)) - var/image/holder = grab_hud(ID_HUD) - if(wear_id) - var/obj/item/weapon/card/id/I = wear_id.GetID() - if(I) - holder.icon_state = "hud[ckey(I.GetJobName())]" - else - holder.icon_state = "hudunknown" - else - holder.icon_state = "hudunknown" - - apply_hud(ID_HUD, holder) - - if (BITTEST(hud_updateflag, WANTED_HUD)) - var/image/holder = grab_hud(WANTED_HUD) - holder.icon_state = "hudblank" - var/perpname = name - if(wear_id) - var/obj/item/weapon/card/id/I = wear_id.GetID() - if(I) - perpname = I.registered_name - - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.security) - if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "*Arrest*")) - holder.icon_state = "hudwanted" - break - else if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "Incarcerated")) - holder.icon_state = "hudprisoner" - break - else if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "Parolled")) - holder.icon_state = "hudparolled" - break - else if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "Released")) - holder.icon_state = "hudreleased" - break - - apply_hud(WANTED_HUD, holder) - - if ( BITTEST(hud_updateflag, IMPLOYAL_HUD) \ - || BITTEST(hud_updateflag, IMPCHEM_HUD) \ - || BITTEST(hud_updateflag, IMPTRACK_HUD)) - - var/image/holder1 = grab_hud(IMPTRACK_HUD) - var/image/holder2 = grab_hud(IMPLOYAL_HUD) - var/image/holder3 = grab_hud(IMPCHEM_HUD) - - holder1.icon_state = "hudblank" - holder2.icon_state = "hudblank" - holder3.icon_state = "hudblank" - - for(var/obj/item/weapon/implant/I in src) - if(I.implanted) - if(!I.malfunction) - if(istype(I,/obj/item/weapon/implant/tracking)) - holder1.icon_state = "hud_imp_tracking" - if(istype(I,/obj/item/weapon/implant/loyalty)) - holder2.icon_state = "hud_imp_loyal" - if(istype(I,/obj/item/weapon/implant/chem)) - holder3.icon_state = "hud_imp_chem" - - apply_hud(IMPTRACK_HUD, holder1) - apply_hud(IMPLOYAL_HUD, holder2) - apply_hud(IMPCHEM_HUD, holder3) - - if (BITTEST(hud_updateflag, SPECIALROLE_HUD)) - var/image/holder = grab_hud(SPECIALROLE_HUD) - holder.icon_state = "hudblank" - if(mind && mind.special_role) - if(hud_icon_reference[mind.special_role]) - holder.icon_state = hud_icon_reference[mind.special_role] - else - holder.icon_state = "hudsyndicate" - apply_hud(SPECIALROLE_HUD, holder) - - attempt_vr(src,"handle_hud_list_vr",list()) //VOREStation Add - Custom HUDs. - - hud_updateflag = 0 - -/mob/living/carbon/human/handle_fire() - if(..()) - return - - var/thermal_protection = get_heat_protection(fire_stacks * 1500) // Arbitrary but below firesuit max temp when below 20 stacks. - - if(thermal_protection == 1) // Immune. - return - else - var/fire_temp_add = (BODYTEMP_HEATING_MAX + (fire_stacks * 15)) * (1-thermal_protection) - //This is to prevent humans from heating up indefinitely. A human being on fire (fat burns at 250C) can't magically - // increase your body temperature beyond 250C, but it's possible something else (atmos) has heated us up beyond it, - // so don't worry about the firestacks at that point. Really, we should be cooling the room down, because it has - // to expend energy to heat our body up! But let's not worry about that. - - // This whole section above is ABSOLUTELY STUPID and makes no sense and this would prevent too-high-heat from even being able to hurt someone. No. We will heat up for as long as needed. - //if((bodytemperature + fire_temp_add) > HUMAN_COMBUSTION_TEMP) - // return - - bodytemperature += fire_temp_add - -/mob/living/carbon/human/rejuvenate() - restore_blood() - shock_stage = 0 - traumatic_shock = 0 - ..() - -/mob/living/carbon/human/proc/handle_defib_timer() - if(!should_have_organ(O_BRAIN)) - return // No brain. - - var/obj/item/organ/internal/brain/brain = internal_organs_by_name[O_BRAIN] - if(!brain) - return // Still no brain. - - brain.tick_defib_timer() - -#undef HUMAN_MAX_OXYLOSS -#undef HUMAN_CRIT_MAX_OXYLOSS +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +//NOTE: Breathing happens once per FOUR TICKS, unless the last breath fails. In which case it happens once per ONE TICK! So oxyloss healing is done once per 4 ticks while oxyloss damage is applied once per tick! +#define HUMAN_MAX_OXYLOSS 1 //Defines how much oxyloss humans can get per tick. A tile with no air at all (such as space) applies this value, otherwise it's a percentage of it. +#define HUMAN_CRIT_MAX_OXYLOSS ( 2.0 / 6) //The amount of damage you'll get when in critical condition. We want this to be a 5 minute deal = 300s. There are 50HP to get through, so (1/6)*last_tick_duration per second. Breaths however only happen every 4 ticks. last_tick_duration = ~2.0 on average + +#define HEAT_DAMAGE_LEVEL_1 2 //VOREStation Edit //Amount of damage applied when your body temperature just passes the 360.15k safety point +#define HEAT_DAMAGE_LEVEL_2 4 //VOREStation Edit //Amount of damage applied when your body temperature passes the 400K point +#define HEAT_DAMAGE_LEVEL_3 8 //VOREStation Edit //Amount of damage applied when your body temperature passes the 1000K point + +#define COLD_DAMAGE_LEVEL_1 0.5 //Amount of damage applied when your body temperature just passes the 260.15k safety point +#define COLD_DAMAGE_LEVEL_2 1.5 //Amount of damage applied when your body temperature passes the 200K point +#define COLD_DAMAGE_LEVEL_3 3 //Amount of damage applied when your body temperature passes the 120K point + +//Note that gas heat damage is only applied once every FOUR ticks. +#define HEAT_GAS_DAMAGE_LEVEL_1 2 //Amount of damage applied when the current breath's temperature just passes the 360.15k safety point +#define HEAT_GAS_DAMAGE_LEVEL_2 4 //Amount of damage applied when the current breath's temperature passes the 400K point +#define HEAT_GAS_DAMAGE_LEVEL_3 8 //Amount of damage applied when the current breath's temperature passes the 1000K point + +#define COLD_GAS_DAMAGE_LEVEL_1 0.5 //Amount of damage applied when the current breath's temperature just passes the 260.15k safety point +#define COLD_GAS_DAMAGE_LEVEL_2 1.5 //Amount of damage applied when the current breath's temperature passes the 200K point +#define COLD_GAS_DAMAGE_LEVEL_3 3 //Amount of damage applied when the current breath's temperature passes the 120K point + +#define COLD_ALERT_SEVERITY_LOW 1 // Constants passed to the cold and heat alerts. +#define COLD_ALERT_SEVERITY_MODERATE 2 +#define COLD_ALERT_SEVERITY_MAX 3 +#define ENVIRONMENT_COMFORT_MARKER_COLD 1 + +#define HOT_ALERT_SEVERITY_LOW 1 +#define HOT_ALERT_SEVERITY_MODERATE 2 +#define HOT_ALERT_SEVERITY_MAX 3 +#define ENVIRONMENT_COMFORT_MARKER_HOT 2 + +#define RADIATION_SPEED_COEFFICIENT 0.1 +#define HUMAN_COMBUSTION_TEMP 524 //524k is the sustained combustion temperature of human fat + +/mob/living/carbon/human + var/in_stasis = 0 + var/heartbeat = 0 + +/mob/living/carbon/human/Life() + set invisibility = 0 + set background = BACKGROUND_ENABLED + + if (transforming) + return + + //Apparently, the person who wrote this code designed it so that + //blinded get reset each cycle and then get activated later in the + //code. Very ugly. I dont care. Moving this stuff here so its easy + //to find it. + blinded = 0 + + //TODO: seperate this out + // update the current life tick, can be used to e.g. only do something every 4 ticks + life_tick++ + + // This is not an ideal place for this but it will do for now. + if(wearing_rig && wearing_rig.offline) + wearing_rig = null + + ..() + + if(life_tick % 30) + hud_updateflag = (1 << TOTAL_HUDS) - 1 + + voice = GetVoice() + + var/stasis = inStasisNow() + if(getStasis() > 2) + Sleeping(20) + + //No need to update all of these procs if the guy is dead. + fall() //VORESTATION EDIT. Prevents people from floating + if(stat != DEAD && !stasis) + //Updates the number of stored chemicals for powers + handle_changeling() + + //Organs and blood + handle_organs() + stabilize_body_temperature() //Body temperature adjusts itself (self-regulation) + weightgain() //VOREStation Addition + process_weaver_silk() //VOREStation Addition + handle_shock() + + handle_pain() + + handle_allergens() + + handle_medical_side_effects() + + handle_heartbeat() + handle_nif() //VOREStation Addition + if(!client) + species.handle_npc(src) + + else if(stat == DEAD && !stasis) + handle_defib_timer() + + if(skip_some_updates()) + return //We go ahead and process them 5 times for HUD images and other stuff though. + + //Update our name based on whether our face is obscured/disfigured + name = get_visible_name() + + pulse = handle_pulse() + +/mob/living/carbon/human/proc/skip_some_updates() + if(life_tick > 5 && timeofdeath && (timeofdeath < 5 || world.time - timeofdeath > 6000)) //We are long dead, or we're junk mobs spawned like the clowns on the clown shuttle + return 1 + return 0 + +/mob/living/carbon/human/breathe() + if(!inStasisNow()) + ..() + +// Calculate how vulnerable the human is to the current pressure. +// Returns 0 (equals 0 %) if sealed in an undamaged suit that's rated for the pressure, 1 if unprotected (equals 100%). +// Suitdamage can modifiy this in 10% steps. +// Protection scales down from 100% at the boundary to 0% at 10% in excess of the boundary +/mob/living/carbon/human/proc/get_pressure_weakness(pressure) + if(pressure == null) + return 1 // No protection if someone forgot to give a pressure + + var/pressure_adjustment_coefficient = 1 // Assume no protection at first. + + // Check suit + if(wear_suit && wear_suit.max_pressure_protection != null && wear_suit.min_pressure_protection != null) + pressure_adjustment_coefficient = 0 + // Pressure is too high + if(wear_suit.max_pressure_protection < pressure) + // Protection scales down from 100% at the boundary to 0% at 10% in excess of the boundary + pressure_adjustment_coefficient += round((pressure - wear_suit.max_pressure_protection) / (wear_suit.max_pressure_protection/10)) + + // Pressure is too low + if(wear_suit.min_pressure_protection > pressure) + pressure_adjustment_coefficient += round((wear_suit.min_pressure_protection - pressure) / (wear_suit.min_pressure_protection/10)) + + // Handles breaches in your space suit. 10 suit damage equals a 100% loss of pressure protection. + if(istype(wear_suit,/obj/item/clothing/suit/space)) + var/obj/item/clothing/suit/space/S = wear_suit + if(S.can_breach && S.damage) + pressure_adjustment_coefficient += S.damage * 0.1 + + else + // Missing key protection + pressure_adjustment_coefficient = 1 + + // Check hat + if(head && head.max_pressure_protection != null && head.min_pressure_protection != null) + // Pressure is too high + if(head.max_pressure_protection < pressure) + // Protection scales down from 100% at the boundary to 0% at 20% in excess of the boundary + pressure_adjustment_coefficient += round((pressure - head.max_pressure_protection) / (head.max_pressure_protection/20)) + + // Pressure is too low + if(head.min_pressure_protection > pressure) + pressure_adjustment_coefficient += round((head.min_pressure_protection - pressure) / (head.min_pressure_protection/20)) + + else + // Missing key protection + pressure_adjustment_coefficient = 1 + + pressure_adjustment_coefficient = min(pressure_adjustment_coefficient, 1) + return pressure_adjustment_coefficient + +// Calculate how much of the enviroment pressure-difference affects the human. +/mob/living/carbon/human/calculate_affecting_pressure(var/pressure) + var/pressure_difference + + // First get the absolute pressure difference. + if(pressure < species.safe_pressure) // We are in an underpressure. + pressure_difference = species.safe_pressure - pressure + + else //We are in an overpressure or standard atmosphere. + pressure_difference = pressure - species.safe_pressure + + if(pressure_difference < 5) // If the difference is small, don't bother calculating the fraction. + pressure_difference = 0 + + else + // Otherwise calculate how much of that absolute pressure difference affects us, can be 0 to 1 (equals 0% to 100%). + // This is our relative difference. + pressure_difference *= get_pressure_weakness(pressure) + + // The difference is always positive to avoid extra calculations. + // Apply the relative difference on a standard atmosphere to get the final result. + // The return value will be the adjusted_pressure of the human that is the basis of pressure warnings and damage. + if(pressure < species.safe_pressure) + return species.safe_pressure - pressure_difference + else + return species.safe_pressure + pressure_difference + +/mob/living/carbon/human/handle_disabilities() + ..() + + if(stat != CONSCIOUS) //Let's not worry about tourettes if you're not conscious. + return + + if (disabilities & EPILEPSY) + if ((prob(1) && paralysis < 1)) + to_chat(src, span_red("You have a seizure!")) + for(var/mob/O in viewers(src, null)) + if(O == src) + continue + O.show_message(text("[src] starts having a seizure!"), 1) + Paralyse(10) + make_jittery(1000) + if (disabilities & COUGHING) + if ((prob(5) && paralysis <= 1)) + drop_item() + spawn( 0 ) + emote("cough") + return + if (disabilities & TOURETTES) + if ((prob(10) && paralysis <= 1)) + Stun(10) + spawn( 0 ) + switch(rand(1, 3)) + if(1) + emote("twitch") + if(2 to 3) + say("[prob(50) ? ";" : ""][pick("SHIT", "PISS", "FUCK", "CUNT", "COCKSUCKER", "MOTHERFUCKER", "TITS")]") + make_jittery(100) + return + if (disabilities & NERVOUS) + if (prob(10)) + stuttering = max(10, stuttering) + + var/rn = rand(0, 200) + if(getBrainLoss() >= 5) + if(0 <= rn && rn <= 3) + custom_pain("Your head feels numb and painful.", 10) + if(getBrainLoss() >= 15) + if(4 <= rn && rn <= 6) if(eye_blurry <= 0) + to_chat(src, "It becomes hard to see for some reason.") + eye_blurry = 10 + if(getBrainLoss() >= 35) + if(7 <= rn && rn <= 9) if(get_active_hand()) + to_chat(src, "Your hand won't respond properly, you drop what you're holding!") + drop_item() + if(getBrainLoss() >= 45) + if(10 <= rn && rn <= 12) + if(prob(50)) + to_chat(src, "You suddenly black out!") + Paralyse(10) + else if(!lying) + to_chat(src, "Your legs won't respond properly, you fall down!") + Weaken(10) + +// RADIATION! Everyone's favorite thing in the world! So let's get some numbers down off the bat. +// 50 rads = 1Bq. This means 1 rad = 0.02Bq. +// However, unless I am a smoothbrained dumbo, absorbed rads are in Gy. Not Bq. +// So let's just assume that 50 rads = 1Gy. Make life easier! + +// ACUTE RADIATION (The stuff that the 'radiation' variable takes care of. Remember, 50radiation=1Gy.): +// Without care: 1-2Gy has a (0-5%) mortality chance. 2-6 (5-95%) 6-8 (95-100)% 8-30 (100%) >30 (100%) +// With care: 1-2Gy (0-5%), 2-6 (5-50%), 6-8 (50-100%), 8-30 (99-100%) >30 (100%) +// So let's make our thresholds based on this! 50-100, 100-300, 300-400, 400-1500, and anything above 1500! +// In reality, however, nobody should ever go above 300 radiation, which is why the cutoff before the really bad effects start to happen being +// 300 radiation is good. For reference: Breaking an artifact deals ~300 rads with no resistance. Getting shot with a lvl 3 PA deals 300 rads with no resistance. +// Nobody outside of engineering should ever have to worry about being irradiated over 300 and start getting organ damage.. + + +// CHRONIC RADIATION (The stuff that 'accumulated_rads' takes care of): +// This is more or less for if someone was exposed for a long time to radiation or just finished being treated for extreme ARS. +// These are meant to be annoying effects to nudge someone towards medical, but not lethal or deadly. +// Things such as loss of taste, eye damage, dropping items in your hand, being temporaily weakened, etc. Stuff to annoy them and get them to fix their rads. + +// Additionally, RADIATION_SPEED_COEFFICIENT = 0.1 + +/mob/living/carbon/human/handle_mutations_and_radiation() //Radiation rework! Now with 'accumulated_rads' + if(inStasisNow()) + return + + if(getFireLoss()) + if((COLD_RESISTANCE in mutations) || (prob(1))) + heal_organ_damage(0,1) + + // DNA2 - Gene processing. + // The HULK stuff that was here is now in the hulk gene. + if(!isSynthetic()) + for(var/datum/dna/gene/gene in dna_genes) + if(!gene.block) + continue + if(gene.is_active(src)) + gene.OnMobLife(src) + + radiation = CLAMP(radiation,0,2500) //Max of 50Gy. If you reach that...You're going to wish you were dead. You probably will be dead. + accumulated_rads = CLAMP(accumulated_rads,0,2500) //Max of 50Gy as well. You should never get higher than this. You will be dead before you can reach this. + var/obj/item/organ/internal/I = null //Used for further down below when an organ is picked. + if(!radiation) + if(species.appearance_flags & RADIATION_GLOWS) + glow_override = FALSE + set_light(0) + if(accumulated_rads) + accumulated_rads -= RADIATION_SPEED_COEFFICIENT //Accumulated rads slowly dissipate very slowly. Get to medical to get it treated! + else if(((life_tick % 5 == 0) && radiation) || (radiation > 600)) //Radiation is a slow, insidious killer. Unless you get a massive dose, then the onset is sudden! + if(species.appearance_flags & RADIATION_GLOWS) + glow_override = TRUE + set_light(max(1,min(5,radiation/15)), max(1,min(10,radiation/25)), species.get_flesh_colour(src)) + // END DOGSHIT SNOWFLAKE + + var/obj/item/organ/internal/diona/nutrients/rad_organ = locate() in internal_organs + if(rad_organ && !rad_organ.is_broken()) + var/rads = radiation/25 + radiation -= rads + adjust_nutrition(rads) + adjustBruteLoss(-(rads)) + adjustFireLoss(-(rads)) + adjustOxyLoss(-(rads)) + adjustToxLoss(-(rads)) + updatehealth() + return + + var/obj/item/organ/internal/brain/slime/core = locate() in internal_organs + if(core) + return + + //VOREStation Addition start: shadekin + var/obj/item/organ/internal/brain/shadekin/s_brain = locate() in internal_organs + if(s_brain) + return + //VOREStation Addition end: shadekin + + if(reagents.has_reagent("prussian_blue")) //Prussian Blue temporarily stops radiation effects. + return + + var/damage = 0 + + + if (radiation < 50) //Less than 1.0 Gy. No side effects. + radiation -= 10 * RADIATION_SPEED_COEFFICIENT + accumulated_rads += 10 * RADIATION_SPEED_COEFFICIENT //No escape from accumulated rads. + + else if (radiation >= 50 && radiation < 100) //Equivalent of 1.0-2.0 Gy. Minimum stage you start seeing effects. + damage = 1 + radiation -= 10 * RADIATION_SPEED_COEFFICIENT + accumulated_rads += 10 * RADIATION_SPEED_COEFFICIENT + if(!isSynthetic()) + if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT) && !weakened) + to_chat(src, "You feel exhausted.") + AdjustWeakened(3) + if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT) && species.get_bodytype() == SPECIES_HUMAN) //apes go bald + if((h_style != "Bald" || f_style != "Shaved" )) + to_chat(src, "Your hair falls out.") + h_style = "Bald" + f_style = "Shaved" + update_hair() + if(prob(1) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //Rare chance of vomiting. + spawn vomit() + + else if (radiation >= 100 && radiation < 300) //Equivalent of 2.0 to 6.0 Gy. Nobody should ever be above this without extreme negligence. + damage = 3 + radiation -= 30 * RADIATION_SPEED_COEFFICIENT + accumulated_rads += 30 * RADIATION_SPEED_COEFFICIENT + if(!isSynthetic()) + if(prob(5)) + take_overall_damage(0, 5 * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") + if(prob(1)) + adjustCloneLoss(5 * RADIATION_SPEED_COEFFICIENT) + emote("gasp") + if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT)) + spawn vomit() + if(prob(10) && !weakened) + to_chat(src, "You feel sick.") + AdjustWeakened(3) + + else if (radiation >= 300 && radiation < 400) //Equivalent of 6.0 to 8.0 Gy. + damage = 5 + radiation -= 50 * RADIATION_SPEED_COEFFICIENT + accumulated_rads += 50 * RADIATION_SPEED_COEFFICIENT + if(!isSynthetic()) + if(prob(15)) + take_overall_damage(0, 10 * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") + if(prob(2)) + adjustCloneLoss(5 * RADIATION_SPEED_COEFFICIENT) + emote("gasp") + if(prob(10) && prob(100 * RADIATION_SPEED_COEFFICIENT)) + spawn vomit() + if(prob(15) && !weakened) + to_chat(src, "You feel horribly ill.") + AdjustWeakened(3) + if(prob(5) && internal_organs.len) + I = pick(internal_organs) //Internal organ damage...Not good. Not good at all. + if(istype(I)) I.add_autopsy_data("Radiation Induced Cancerous Growth", damage) + I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) + + + else if (radiation >= 400 && radiation < 1500) //Equivalent of 8.0 to 30 Gy. + damage = 10 + radiation -= 100 * RADIATION_SPEED_COEFFICIENT + accumulated_rads += 100 * RADIATION_SPEED_COEFFICIENT + if(!isSynthetic()) + if(prob(25)) + take_overall_damage(0, 15 * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") + if(prob(5)) + I = internal_organs_by_name[O_EYES] + if(I) + if(istype(I)) I.add_autopsy_data("Radiation Burns", damage) + I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) + to_chat(src, "Your eyes burn!") + eye_blurry += 10 + if(prob(4)) + adjustCloneLoss(5 * RADIATION_SPEED_COEFFICIENT) + emote("gasp") + if(prob(25) && prob(100 * RADIATION_SPEED_COEFFICIENT)) + spawn vomit() + if(prob(20) && !weakened) + to_chat(src, "You feel like your insides are burning!") + AdjustWeakened(5) + if(prob(5)) + to_chat(src, "Your entire body feels like it's on fire!") + adjustHalLoss(5) + if(prob(10) && internal_organs.len) + I = pick(internal_organs) //Internal organ damage...Not good. Not good at all. + if(istype(I)) I.add_autopsy_data("Radiation Induced Cancerous Growth", damage) + I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) + + else if (radiation >= 1500) //Above 30Gy. You had to get absolutely blasted with rads for this. + damage = 30 + radiation -= 300 * RADIATION_SPEED_COEFFICIENT + accumulated_rads += 300 * RADIATION_SPEED_COEFFICIENT + + if(!isSynthetic()) + take_overall_damage(0, damage * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") //3 burn damage a tick as your body melts. + adjustCloneLoss(15 * RADIATION_SPEED_COEFFICIENT) //1.5 cloneloss a tick as your cells mutate and break down. + + I = internal_organs_by_name[O_EYES] + if(I) + I.add_autopsy_data("Radiation Burns", damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) + I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) //3 eye damage a tick as your eyes melt down. + eye_blurry += 10 + + if(prob(50) && prob(100 * RADIATION_SPEED_COEFFICIENT)) + spawn vomit() + if(!paralysis && prob(30) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //CNS is shutting down. + to_chat(src, "You have a seizure!") + Paralyse(10) + make_jittery(1000) + if(!lying) + emote("collapse") + if(get_active_hand() && prob(15)) //CNS is shutting down. + to_chat(src, "Your hand won't respond properly, you drop what you're holding!") + drop_item() + if(internal_organs.len) + I = pick(internal_organs) //Internal organ damage...Not good. Not good at all. + if(istype(I)) I.add_autopsy_data("Radiation Induced Cancerous Growth", damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) + I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) + +/* //Not-so-sparkledog code. TODO: Make a pref for 'special game interactions' that allows interactions that align with prefs to occur. + if(radiation >= 250) //Special effect stuff that occurs at certain rad levels. + if(prob(1) && prob(radiation/2 * RADIATION_SPEED_COEFFICIENT) && allow_spontaneous_tf) //If you've got spontaneous TF...well... + scramble(1, src, 3) //I tried to base this on how many rads you took and it was...Hilarious. Sparkledogs everywhere. + //For the most part, 3 strength will simply change colors. If you get really unlucky, it can do more TF's. + //Math: 250 rads = 1/800 chance + //500 rads = 1/400 chance chance. Etc. +*/ + + if(damage) + damage *= species.radiation_mod + adjustToxLoss(damage * RADIATION_SPEED_COEFFICIENT) + updatehealth() + if(!isSynthetic() && organs.len) + var/obj/item/organ/external/O = pick(organs) + if(istype(O)) O.add_autopsy_data("Radiation Poisoning", damage) + + // Begin long-term radiation effects + // Loss of taste occurs at 100 (2Gy) and is handled in taste.dm + // These are all done one after another, so duplication is not required. Someone at 400rads will have the 100&400 effects. + if(!radiation && accumulated_rads >= 100 && !reagents.has_reagent("prussian_blue")) //Let's not hit them with long term effects when they're actively being hit with rads. + if(!isSynthetic()) + I = internal_organs_by_name[O_EYES] + if(I) //Eye stuff + if(prob(5) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) + to_chat(src, "Your eyes water.") + eye_blurry += 5 + if(accumulated_rads > 300) // (6Gy) + if(prob(2) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) + to_chat(src, "Your eyes burn.") + I.add_autopsy_data("Radiation Burns", 1 * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) + I.take_damage(1 * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) //0.1 damage. Not a lot, but enough to tell you to get to medical. + eye_blurry += 10 + + if(accumulated_rads > 200) // (4Gy) + if(prob(5) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) + to_chat(src, "Your feel nauseated.") + spawn vomit() + if(!weakened && prob(2) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) + to_chat(src, "Your feel exhausted.") + AdjustWeakened(3) + if(accumulated_rads > 300) // (6Gy) + if(get_active_hand() && prob(15) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //CNS is shutting down. + to_chat(src, "Your hand won't respond properly, you drop what you're holding!") + drop_item() + if(accumulated_rads > 700) // (12Gy) + if(!paralysis && prob(1) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //1 in 1000 chance per tick. + to_chat(src, "You have a seizure!") + Paralyse(10) + make_jittery(1000) + if(!lying) + emote("collapse") + + else //The synthetic effects! + return //Nothing for now. + + + + /** breathing **/ + +/mob/living/carbon/human/handle_chemical_smoke(var/datum/gas_mixture/environment) + if(wear_mask && (wear_mask.item_flags & BLOCK_GAS_SMOKE_EFFECT)) + return + if(glasses && (glasses.item_flags & BLOCK_GAS_SMOKE_EFFECT)) + return + if(head && (head.item_flags & BLOCK_GAS_SMOKE_EFFECT)) + return + ..() + +/mob/living/carbon/human/handle_post_breath(datum/gas_mixture/breath) + ..() + //spread some viruses while we are at it + if(breath && virus2.len > 0 && prob(10)) + for(var/mob/living/carbon/M in view(1,src)) + src.spread_disease_to(M) + + +/mob/living/carbon/human/get_breath_from_internal(volume_needed=BREATH_VOLUME) + if(internal) + //Because rigs store their tanks out of reach of contents.Find(), a check has to be made to make + //sure the rig is still worn, still online, and that its air supply still exists. + var/obj/item/weapon/tank/rig_supply + var/obj/item/weapon/rig/Rig = get_rig() + + if(Rig) + rig_supply = Rig.air_supply + + if ((!rig_supply && !contents.Find(internal)) || !((wear_mask && (wear_mask.item_flags & AIRTIGHT)) || (head && (head.item_flags & AIRTIGHT)))) + internal = null + + if(internal) + return internal.remove_air_volume(volume_needed) + else if(internals) + internals.icon_state = "internal0" + return null + + +/mob/living/carbon/human/handle_breath(datum/gas_mixture/breath) + if(status_flags & GODMODE) + return + + if(suiciding) + failed_last_breath = 1 + adjustOxyLoss(2)//If you are suiciding, you should die a little bit faster + suiciding-- + return 0 + + if(does_not_breathe) + failed_last_breath = 0 + adjustOxyLoss(-5) + return + + if(!breath || (breath.total_moles == 0)) + failed_last_breath = 1 + if(health > config.health_threshold_crit) + adjustOxyLoss(HUMAN_MAX_OXYLOSS) + else + adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS) + + if(breath && should_have_organ(O_LUNGS)) + var/obj/item/organ/internal/lungs/L = internal_organs_by_name[O_LUNGS] + if(!L.is_bruised() && prob(8)) + rupture_lung() + + throw_alert("pressure", /obj/screen/alert/lowpressure) + return 0 + else + clear_alert("pressure") + + var/safe_pressure_min = species.minimum_breath_pressure // Minimum safe partial pressure of breathable gas in kPa + + + // Lung damage increases the minimum safe pressure. + if(should_have_organ(O_LUNGS)) + var/obj/item/organ/internal/lungs/L = internal_organs_by_name[O_LUNGS] + if(isnull(L)) + safe_pressure_min = INFINITY //No lungs, how are you breathing? + else if(L.is_broken()) + safe_pressure_min *= 1.5 + else if(L.is_bruised()) + safe_pressure_min *= 1.25 + else if(breath) + if(breath.total_moles < BREATH_MOLES / 10 || breath.total_moles > BREATH_MOLES * 5) + if(is_below_sound_pressure(get_turf(src))) //No more popped lungs from choking/drowning + if (prob(8)) + rupture_lung() + + var/safe_exhaled_max = 10 + var/safe_toxins_max = 0.2 + var/SA_para_min = 1 + var/SA_sleep_min = 5 + var/inhaled_gas_used = 0 + + var/breath_pressure = (breath.total_moles*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME + + var/inhaling + var/poison + var/exhaling + + var/breath_type + var/poison_type + var/exhale_type + + var/failed_inhale = 0 + var/failed_exhale = 0 + + if(species.breath_type) + breath_type = species.breath_type + else + breath_type = "oxygen" + inhaling = breath.gas[breath_type] + + if(species.poison_type) + poison_type = species.poison_type + else + poison_type = "phoron" + poison = breath.gas[poison_type] + + if(species.exhale_type) + exhale_type = species.exhale_type + exhaling = breath.gas[exhale_type] + else + exhaling = 0 + + var/inhale_pp = (inhaling/breath.total_moles)*breath_pressure + var/toxins_pp = (poison/breath.total_moles)*breath_pressure + // To be clear, this isn't how much they're exhaling -- it's the amount of the species exhale_gas that they just + var/exhaled_pp = (exhaling/breath.total_moles)*breath_pressure + + // Not enough to breathe + if(inhale_pp < safe_pressure_min) + if(prob(20)) + spawn(0) emote("gasp") + + var/ratio = inhale_pp/safe_pressure_min + // Don't fuck them up too fast (space only does HUMAN_MAX_OXYLOSS after all!) + adjustOxyLoss(max(HUMAN_MAX_OXYLOSS*(1-ratio), 0)) + failed_inhale = 1 + + switch(breath_type) + if("oxygen") + throw_alert("oxy", /obj/screen/alert/not_enough_oxy) + if("phoron") + throw_alert("oxy", /obj/screen/alert/not_enough_tox) + if("nitrogen") + throw_alert("oxy", /obj/screen/alert/not_enough_nitro) + if("carbon_dioxide") + throw_alert("oxy", /obj/screen/alert/not_enough_co2) + if("volatile_fuel") + throw_alert("oxy", /obj/screen/alert/not_enough_fuel) + if("nitrous_oxide") + throw_alert("oxy", /obj/screen/alert/not_enough_n2o) + + else + // We're in safe limits + clear_alert("oxy") + + inhaled_gas_used = inhaling/6 + + breath.adjust_gas(breath_type, -inhaled_gas_used, update = 0) //update afterwards + + if(exhale_type) + breath.adjust_gas_temp(exhale_type, inhaled_gas_used, bodytemperature, update = 0) //update afterwards + + // Too much exhaled gas in the air + if(exhaled_pp > safe_exhaled_max) + if (prob(15)) + var/word = pick("extremely dizzy","short of breath","faint","confused") + to_chat(src, "You feel [word].") + + adjustOxyLoss(HUMAN_MAX_OXYLOSS) + failed_exhale = 1 + + else if(exhaled_pp > safe_exhaled_max * 0.7) + if (!prob(1)) + var/word = pick("dizzy","short of breath","faint","momentarily confused") + to_chat(src, "You feel [word].") + + //scale linearly from 0 to 1 between safe_exhaled_max and safe_exhaled_max*0.7 + var/ratio = 1.0 - (safe_exhaled_max - exhaled_pp)/(safe_exhaled_max*0.3) + + //give them some oxyloss, up to the limit - we don't want people falling unconcious due to CO2 alone until they're pretty close to safe_exhaled_max. + if (getOxyLoss() < 50*ratio) + adjustOxyLoss(HUMAN_MAX_OXYLOSS) + failed_exhale = 1 + + else if(exhaled_pp > safe_exhaled_max * 0.6) + if(prob(0.3)) + var/word = pick("a little dizzy","short of breath") + to_chat(src, "You feel [word].") + + // Too much poison in the air. + if(toxins_pp > safe_toxins_max) + var/ratio = (poison/safe_toxins_max) * 10 + if(reagents) + reagents.add_reagent("toxin", CLAMP(ratio, MIN_TOXIN_DAMAGE, MAX_TOXIN_DAMAGE)) + breath.adjust_gas(poison_type, -poison/6, update = 0) //update after + throw_alert("tox_in_air", /obj/screen/alert/tox_in_air) + else + clear_alert("tox_in_air") + + // If there's some other shit in the air lets deal with it here. + if(breath.gas["nitrous_oxide"]) + var/SA_pp = (breath.gas["nitrous_oxide"] / breath.total_moles) * breath_pressure + + // Enough to make us paralysed for a bit + if(SA_pp > SA_para_min) + + // 3 gives them one second to wake up and run away a bit! + Paralyse(3) + + // Enough to make us sleep as well + if(SA_pp > SA_sleep_min) + Sleeping(5) + + // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning + else if(SA_pp > 0.15) + if(prob(20)) + spawn(0) emote(pick("giggle", "laugh")) + breath.adjust_gas("nitrous_oxide", -breath.gas["nitrous_oxide"]/6, update = 0) //update after + + // Were we able to breathe? + if (failed_inhale || failed_exhale) + failed_last_breath = 1 + else + failed_last_breath = 0 + adjustOxyLoss(-5) + + + // Hot air hurts :( + if((breath.temperature <= species.cold_discomfort_level || breath.temperature >= species.heat_discomfort_level) && !(COLD_RESISTANCE in mutations)) + + if(breath.temperature <= species.breath_cold_level_1) + if(prob(20)) + to_chat(src, "You feel your face freezing and icicles forming in your lungs!") + else if(breath.temperature >= species.breath_heat_level_1) + if(prob(20)) + to_chat(src, "You feel your face burning and a searing heat in your lungs!") + + if(breath.temperature >= species.heat_discomfort_level) + + if(breath.temperature >= species.breath_heat_level_3) + apply_damage(HEAT_GAS_DAMAGE_LEVEL_3, BURN, BP_HEAD, used_weapon = "Excessive Heat") + throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MAX) + else if(breath.temperature >= species.breath_heat_level_2) + apply_damage(HEAT_GAS_DAMAGE_LEVEL_2, BURN, BP_HEAD, used_weapon = "Excessive Heat") + throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MODERATE) + else if(breath.temperature >= species.breath_heat_level_1) + apply_damage(HEAT_GAS_DAMAGE_LEVEL_1, BURN, BP_HEAD, used_weapon = "Excessive Heat") + throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_LOW) + else if(species.get_environment_discomfort(src, ENVIRONMENT_COMFORT_MARKER_HOT)) + throw_alert("temp", /obj/screen/alert/warm, HOT_ALERT_SEVERITY_LOW) + else + clear_alert("temp") + + else if(breath.temperature <= species.cold_discomfort_level) + + if(breath.temperature <= species.breath_cold_level_3) + apply_damage(COLD_GAS_DAMAGE_LEVEL_3, BURN, BP_HEAD, used_weapon = "Excessive Cold") + throw_alert("temp", /obj/screen/alert/cold, COLD_ALERT_SEVERITY_MAX) + else if(breath.temperature <= species.breath_cold_level_2) + apply_damage(COLD_GAS_DAMAGE_LEVEL_2, BURN, BP_HEAD, used_weapon = "Excessive Cold") + throw_alert("temp", /obj/screen/alert/cold, COLD_ALERT_SEVERITY_MODERATE) + else if(breath.temperature <= species.breath_cold_level_1) + apply_damage(COLD_GAS_DAMAGE_LEVEL_1, BURN, BP_HEAD, used_weapon = "Excessive Cold") + throw_alert("temp", /obj/screen/alert/cold, COLD_ALERT_SEVERITY_LOW) + else if(species.get_environment_discomfort(src, ENVIRONMENT_COMFORT_MARKER_COLD)) + throw_alert("temp", /obj/screen/alert/chilly, COLD_ALERT_SEVERITY_LOW) + else + clear_alert("temp") + + //breathing in hot/cold air also heats/cools you a bit + var/temp_adj = breath.temperature - bodytemperature + if (temp_adj < 0) + temp_adj /= (BODYTEMP_COLD_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed + else + temp_adj /= (BODYTEMP_HEAT_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed + + var/relative_density = breath.total_moles / (MOLES_CELLSTANDARD * BREATH_PERCENTAGE) + temp_adj *= relative_density + + if(temp_adj > BODYTEMP_HEATING_MAX) + temp_adj = BODYTEMP_HEATING_MAX + if(temp_adj < BODYTEMP_COOLING_MAX) + temp_adj = BODYTEMP_COOLING_MAX + + bodytemperature += temp_adj + + else + clear_alert("temp") + + breath.update_values() + return 1 + +/mob/living/carbon/human/proc/handle_allergens() + if(chem_effects[CE_ALLERGEN]) + //first, multiply the basic species-level value by our allergen effect rating, so consuming multiple seperate allergen typess simultaneously hurts more + var/damage_severity = species.allergen_damage_severity * chem_effects[CE_ALLERGEN] + var/disable_severity = species.allergen_disable_severity * chem_effects[CE_ALLERGEN] + if(species.allergen_reaction & AG_PHYS_DMG) + adjustBruteLoss(damage_severity) + if(species.allergen_reaction & AG_BURN_DMG) + adjustFireLoss(damage_severity) + if(species.allergen_reaction & AG_TOX_DMG) + adjustToxLoss(damage_severity) + if(species.allergen_reaction & AG_OXY_DMG) + adjustOxyLoss(damage_severity) + if(prob(disable_severity/2)) + emote(pick("cough","gasp","choke")) + if(species.allergen_reaction & AG_EMOTE) + if(prob(disable_severity/2)) + emote(pick("pale","shiver","twitch")) + if(species.allergen_reaction & AG_PAIN) + adjustHalLoss(disable_severity) + if(species.allergen_reaction & AG_WEAKEN) + Weaken(disable_severity) + if(species.allergen_reaction & AG_BLURRY) + eye_blurry = max(eye_blurry, disable_severity) + if(species.allergen_reaction & AG_SLEEPY) + drowsyness = max(drowsyness, disable_severity) + if(species.allergen_reaction & AG_CONFUSE) + Confuse(disable_severity/4) + +/mob/living/carbon/human/handle_environment(datum/gas_mixture/environment) + if(!environment) + return + //Stuff like the xenomorph's plasma regen happens here. + species.handle_environment_special(src) + + if(is_incorporeal()) + return + + //Moved pressure calculations here for use in skip-processing check. + var/pressure = environment.return_pressure() + var/adjusted_pressure = calculate_affecting_pressure(pressure) + + //Check for contaminants before anything else because we don't want to skip it. + for(var/g in environment.gas) + if(gas_data.flags[g] & XGM_GAS_CONTAMINANT && environment.gas[g] > gas_data.overlay_limit[g] + 1) + pl_effects() + break + + if(istype(loc, /turf/space)) //VOREStation Edit - No FBPs overheating on space turfs inside mechs or people. + //Don't bother if the temperature drop is less than 0.1 anyways. Hopefully BYOND is smart enough to turn this constant expression into a constant + if(bodytemperature > (0.1 * HUMAN_HEAT_CAPACITY/(HUMAN_EXPOSED_SURFACE_AREA*STEFAN_BOLTZMANN_CONSTANT))**(1/4) + COSMIC_RADIATION_TEMPERATURE) + //Thermal radiation into space + var/heat_loss = HUMAN_EXPOSED_SURFACE_AREA * STEFAN_BOLTZMANN_CONSTANT * ((bodytemperature - COSMIC_RADIATION_TEMPERATURE)**4) + var/temperature_loss = heat_loss/HUMAN_HEAT_CAPACITY + bodytemperature -= temperature_loss + else + var/loc_temp = T0C + if(istype(loc, /obj/mecha)) + var/obj/mecha/M = loc + loc_temp = M.return_temperature() + else if(istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) + var/obj/machinery/atmospherics/unary/cryo_cell/cc = loc + loc_temp = cc.air_contents.temperature + else + loc_temp = environment.temperature + + if(adjusted_pressure < species.warning_high_pressure && adjusted_pressure > species.warning_low_pressure && abs(loc_temp - bodytemperature) < 20 && bodytemperature < species.heat_level_1 && bodytemperature > species.cold_level_1) + clear_alert("pressure") + return // Temperatures are within normal ranges, fuck all this processing. ~Ccomp + + //Body temperature adjusts depending on surrounding atmosphere based on your thermal protection (convection) + var/temp_adj = 0 + if(loc_temp < bodytemperature) //Place is colder than we are + var/thermal_protection = get_cold_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. + if(thermal_protection < 0.99) //For some reason, < 1 returns false if the value is 1. + temp_adj = (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_COLD_DIVISOR) //this will be negative + else if (loc_temp > bodytemperature) //Place is hotter than we are + var/thermal_protection = get_heat_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. + if(thermal_protection < 0.99) //For some reason, < 1 returns false if the value is 1. + temp_adj = (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR) + + //Use heat transfer as proportional to the gas density. However, we only care about the relative density vs standard 101 kPa/20 C air. Therefore we can use mole ratios + var/relative_density = environment.total_moles / MOLES_CELLSTANDARD + bodytemperature += between(BODYTEMP_COOLING_MAX, temp_adj*relative_density, BODYTEMP_HEATING_MAX) + + // +/- 50 degrees from 310.15K is the 'safe' zone, where no damage is dealt. + if(bodytemperature >= species.heat_level_1) + //Body temperature is too hot. + if(status_flags & GODMODE) + return 1 //godmode + + var/burn_dam = 0 + + // switch() can't access numbers inside variables, so we need to use some ugly if() spam ladder. + if(bodytemperature >= species.heat_level_1) + if(bodytemperature >= species.heat_level_2) + if(bodytemperature >= species.heat_level_3) + burn_dam = HEAT_DAMAGE_LEVEL_3 + throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MAX) + else + burn_dam = HEAT_DAMAGE_LEVEL_2 + throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MODERATE) + else + burn_dam = HEAT_DAMAGE_LEVEL_1 + throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_LOW) + + take_overall_damage(burn=burn_dam, used_weapon = "High Body Temperature") + + else if(bodytemperature <= species.cold_level_1) + //Body temperature is too cold. + + if(status_flags & GODMODE) + return 1 //godmode + + + if(!istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) + var/cold_dam = 0 + if(bodytemperature <= species.cold_level_1) + if(bodytemperature <= species.cold_level_2) + if(bodytemperature <= species.cold_level_3) + cold_dam = COLD_DAMAGE_LEVEL_3 + else + cold_dam = COLD_DAMAGE_LEVEL_2 + else + cold_dam = COLD_DAMAGE_LEVEL_1 + + take_overall_damage(burn=cold_dam, used_weapon = "Low Body Temperature") + + else clear_alert("temp") + + // Account for massive pressure differences. Done by Polymorph + // Made it possible to actually have something that can protect against high pressure... Done by Errorage. Polymorph now has an axe sticking from his head for his previous hardcoded nonsense! + if(status_flags & GODMODE) + return 1 //godmode + + if(adjusted_pressure >= species.hazard_high_pressure) + var/pressure_damage = min( ( (adjusted_pressure / species.hazard_high_pressure) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) + if(stat==DEAD) + pressure_damage = pressure_damage/2 + take_overall_damage(brute=pressure_damage, used_weapon = "High Pressure") + throw_alert("pressure", /obj/screen/alert/highpressure, 2) + else if(adjusted_pressure >= species.warning_high_pressure) + throw_alert("pressure", /obj/screen/alert/highpressure, 1) + else if(adjusted_pressure >= species.warning_low_pressure) + clear_alert("pressure") + else if(adjusted_pressure >= species.hazard_low_pressure) + throw_alert("pressure", /obj/screen/alert/lowpressure, 1) + else + if( !(COLD_RESISTANCE in mutations)) + if(!isSynthetic() || !nif || !nif.flag_check(NIF_O_PRESSURESEAL,NIF_FLAGS_OTHER)) //VOREStation Edit - NIF pressure seals + var/pressure_damage = LOW_PRESSURE_DAMAGE + if(stat==DEAD) + pressure_damage = pressure_damage/2 + take_overall_damage(brute=pressure_damage, used_weapon = "Low Pressure") + if(getOxyLoss() < 55) // 12 OxyLoss per 4 ticks when wearing internals; unconsciousness in 16 ticks, roughly half a minute + var/pressure_dam = 3 // 16 OxyLoss per 4 ticks when no internals present; unconsciousness in 13 ticks, roughly twenty seconds + // (Extra 1 oxyloss from failed breath) + // Being in higher pressure decreases the damage taken, down to a minimum of (species.hazard_low_pressure / ONE_ATMOSPHERE) at species.hazard_low_pressure + pressure_dam *= (ONE_ATMOSPHERE - adjusted_pressure) / ONE_ATMOSPHERE + + if(wear_suit && wear_suit.min_pressure_protection && head && head.min_pressure_protection) + var/protection = max(wear_suit.min_pressure_protection, head.min_pressure_protection) // Take the weakest protection + pressure_dam *= (protection) / (ONE_ATMOSPHERE) // Divide by ONE_ATMOSPHERE to get a fractional protection + // Stronger protection (Closer to 0) results in a smaller fraction + // Firesuits (Min protection = 0.2 atmospheres) decrease oxyloss to 1/5 + + adjustOxyLoss(pressure_dam) + throw_alert("pressure", /obj/screen/alert/lowpressure, 2) + else + clear_alert("pressure") + + return + +/* +/mob/living/carbon/human/proc/adjust_body_temperature(current, loc_temp, boost) + var/temperature = current + var/difference = abs(current-loc_temp) //get difference + var/increments// = difference/10 //find how many increments apart they are + if(difference > 50) + increments = difference/5 + else + increments = difference/10 + var/change = increments*boost // Get the amount to change by (x per increment) + var/temp_change + if(current < loc_temp) + temperature = min(loc_temp, temperature+change) + else if(current > loc_temp) + temperature = max(loc_temp, temperature-change) + temp_change = (temperature - current) + return temp_change +*/ + +/mob/living/carbon/human/proc/stabilize_body_temperature() + // We produce heat naturally. + if (species.passive_temp_gain) + bodytemperature += species.passive_temp_gain + if (species.body_temperature == null) + return //this species doesn't have metabolic thermoregulation + + // FBPs will overheat when alive, prosthetic limbs are fine. + if(stat != DEAD && robobody_count) + if(!nif || !nif.flag_check(NIF_O_HEATSINKS,NIF_FLAGS_OTHER)) //VOREStation Edit - NIF heatsinks prevent the base heat increase per tick if installed. + bodytemperature += round(robobody_count*1.15) + var/obj/item/organ/internal/robotic/heatsink/HS = internal_organs_by_name[O_HEATSINK] + if(!HS || HS.is_broken()) // However, NIF Heatsinks will not compensate for a core FBP component (your heatsink) being lost. + bodytemperature += round(robobody_count*0.5) + + var/body_temperature_difference = species.body_temperature - bodytemperature + + if (abs(body_temperature_difference) < 0.5) + return //fuck this precision + + if (on_fire) + return //too busy for pesky metabolic regulation + + if(bodytemperature < species.cold_level_1) //260.15 is 310.15 - 50, the temperature where you start to feel effects. + if(nutrition >= 2) //If we are very, very cold we'll use up quite a bit of nutriment to heat us up. + adjust_nutrition(-2) + var/recovery_amt = max((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM) + //to_world("Cold. Difference = [body_temperature_difference]. Recovering [recovery_amt]") +// log_debug("Cold. Difference = [body_temperature_difference]. Recovering [recovery_amt]") + bodytemperature += recovery_amt + else if(species.cold_level_1 <= bodytemperature && bodytemperature <= species.heat_level_1) + var/recovery_amt = body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR + //to_world("Norm. Difference = [body_temperature_difference]. Recovering [recovery_amt]") +// log_debug("Norm. Difference = [body_temperature_difference]. Recovering [recovery_amt]") + bodytemperature += recovery_amt + else if(bodytemperature > species.heat_level_1) //360.15 is 310.15 + 50, the temperature where you start to feel effects. + //We totally need a sweat system cause it totally makes sense...~ + var/recovery_amt = min((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), -BODYTEMP_AUTORECOVERY_MINIMUM) //We're dealing with negative numbers + //to_world("Hot. Difference = [body_temperature_difference]. Recovering [recovery_amt]") +// log_debug("Hot. Difference = [body_temperature_difference]. Recovering [recovery_amt]") + bodytemperature += recovery_amt + + //This proc returns a number made up of the flags for body parts which you are protected on. (such as HEAD, UPPER_TORSO, LOWER_TORSO, etc. See setup.dm for the full list) +/mob/living/carbon/human/proc/get_heat_protection_flags(temperature) //Temperature is the temperature you're being exposed to. + . = 0 + //Handle normal clothing + for(var/obj/item/clothing/C in list(head,wear_suit,w_uniform,shoes,gloves,wear_mask)) + if(C) + if(C.handle_high_temperature(temperature)) + . |= C.get_heat_protection_flags() + +//See proc/get_heat_protection_flags(temperature) for the description of this proc. +/mob/living/carbon/human/proc/get_cold_protection_flags(temperature) + . = 0 + //Handle normal clothing + for(var/obj/item/clothing/C in list(head,wear_suit,w_uniform,shoes,gloves,wear_mask)) + if(C) + if(C.handle_low_temperature(temperature)) + . |= C.get_cold_protection_flags() + +/mob/living/carbon/human/get_heat_protection(temperature) //Temperature is the temperature you're being exposed to. + var/thermal_protection_flags = get_heat_protection_flags(temperature) + + . = get_thermal_protection(thermal_protection_flags) + . = 1 - . // Invert from 1 = immunity to 0 = immunity. + + // Doing it this way makes multiplicative stacking not get out of hand, so two modifiers that give 0.5 protection will be combined to 0.75 in the end. + for(var/datum/modifier/M as anything in modifiers) + if(!isnull(M.heat_protection)) + . *= 1 - M.heat_protection + + // Code that calls this expects 1 = immunity so we need to invert again. + . = 1 - . + . = min(., 1.0) + +/mob/living/carbon/human/get_cold_protection(temperature) + if(COLD_RESISTANCE in mutations) + return 1 //Fully protected from the cold. + + temperature = max(temperature, 2.7) //There is an occasional bug where the temperature is miscalculated in ares with a small amount of gas on them, so this is necessary to ensure that that bug does not affect this calculation. Space's temperature is 2.7K and most suits that are intended to protect against any cold, protect down to 2.0K. + var/thermal_protection_flags = get_cold_protection_flags(temperature) + + . = get_thermal_protection(thermal_protection_flags) + . = 1 - . // Invert from 1 = immunity to 0 = immunity. + + // Doing it this way makes multiplicative stacking not get out of hand, so two modifiers that give 0.5 protection will be combined to 0.75 in the end. + for(var/datum/modifier/M as anything in modifiers) + if(!isnull(M.cold_protection)) + // Invert the modifier values so they align with the current working value. + . *= 1 - M.cold_protection + + // Code that calls this expects 1 = immunity so we need to invert again. + . = 1 - . + . = min(., 1.0) + +/mob/living/carbon/human/proc/get_thermal_protection(var/flags) + .=0 + if(flags) + if(flags & HEAD) + . += THERMAL_PROTECTION_HEAD + if(flags & UPPER_TORSO) + . += THERMAL_PROTECTION_UPPER_TORSO + if(flags & LOWER_TORSO) + . += THERMAL_PROTECTION_LOWER_TORSO + if(flags & LEG_LEFT) + . += THERMAL_PROTECTION_LEG_LEFT + if(flags & LEG_RIGHT) + . += THERMAL_PROTECTION_LEG_RIGHT + if(flags & FOOT_LEFT) + . += THERMAL_PROTECTION_FOOT_LEFT + if(flags & FOOT_RIGHT) + . += THERMAL_PROTECTION_FOOT_RIGHT + if(flags & ARM_LEFT) + . += THERMAL_PROTECTION_ARM_LEFT + if(flags & ARM_RIGHT) + . += THERMAL_PROTECTION_ARM_RIGHT + if(flags & HAND_LEFT) + . += THERMAL_PROTECTION_HAND_LEFT + if(flags & HAND_RIGHT) + . += THERMAL_PROTECTION_HAND_RIGHT + return min(1,.) + +/mob/living/carbon/human/handle_chemicals_in_body() + + if(inStasisNow()) + return + + if(reagents) + chem_effects.Cut() + + if(touching) + touching.metabolize() + if(ingested) + ingested.metabolize() + if(bloodstr) + bloodstr.metabolize() + + if(!isSynthetic()) + + var/total_phoronloss = 0 + for(var/obj/item/I in src) + if(I.contaminated) + if(check_belly(I)) continue //VOREStation Edit + if(src.species && src.species.get_bodytype() != "Vox" && src.species.get_bodytype() != "Shadekin") //VOREStation Edit: shadekin + // This is hacky, I'm so sorry. + if(I != l_hand && I != r_hand) //If the item isn't in your hands, you're probably wearing it. Full damage for you. + total_phoronloss += vsc.plc.CONTAMINATION_LOSS + else if(I == l_hand) //If the item is in your hands, but you're wearing protection, you might be alright. + var/l_hand_blocked = 0 + l_hand_blocked = 1-(100-getarmor(BP_L_HAND, "bio"))/100 //This should get a number between 0 and 1 + total_phoronloss += vsc.plc.CONTAMINATION_LOSS * l_hand_blocked + else if(I == r_hand) //If the item is in your hands, but you're wearing protection, you might be alright. + var/r_hand_blocked = 0 + r_hand_blocked = 1-(100-getarmor(BP_R_HAND, "bio"))/100 //This should get a number between 0 and 1 + total_phoronloss += vsc.plc.CONTAMINATION_LOSS * r_hand_blocked + if(total_phoronloss) + if(!(status_flags & GODMODE)) + adjustToxLoss(total_phoronloss) + + if(status_flags & GODMODE) + return 0 //godmode + + if(species.light_dam) + var/light_amount = 0 + if(isturf(loc)) + var/turf/T = loc + light_amount = T.get_lumcount() * 10 + if(light_amount > species.light_dam) //if there's enough light, start dying + take_overall_damage(1,1) + else //heal in the dark + heal_overall_damage(1,1) + + // nutrition decrease + if (nutrition > 0 && stat != DEAD) + var/nutrition_reduction = species.hunger_factor + + for(var/datum/modifier/mod in modifiers) + if(!isnull(mod.metabolism_percent)) + nutrition_reduction *= mod.metabolism_percent + adjust_nutrition(-nutrition_reduction) + + if(noisy == TRUE && nutrition < 250 && prob(10)) //VOREStation edit for hunger noises. + var/sound/growlsound = sound(get_sfx("hunger_sounds")) + var/growlmultiplier = 100 - (nutrition / 250 * 100) + playsound(src, growlsound, vol = growlmultiplier, vary = 1, falloff = 0.1, ignore_walls = TRUE, preference = /datum/client_preference/digestion_noises) + // VOREStation Edit End + + // TODO: stomach and bloodstream organ. + if(!isSynthetic()) + handle_trace_chems() + + updatehealth() + + return //TODO: DEFERRED + +//DO NOT CALL handle_statuses() from this proc, it's called from living/Life() as long as this returns a true value. +/mob/living/carbon/human/handle_regular_status_updates() + if(skip_some_updates()) + return 0 + + if(status_flags & GODMODE) return 0 + + //SSD check, if a logged player is awake put them back to sleep! + if(species.get_ssd(src) && !client && !teleop) + Sleeping(2) + if(stat == DEAD) //DEAD. BROWN BREAD. SWIMMING WITH THE SPESS CARP + blinded = 1 + silent = 0 + else //ALIVE. LIGHTS ARE ON + updatehealth() //TODO + + if(health <= config.health_threshold_dead || (should_have_organ("brain") && !has_brain())) + death() + blinded = 1 + silent = 0 + return 1 + + //UNCONSCIOUS. NO-ONE IS HOME + if((getOxyLoss() > (species.total_health/2)) || (health <= config.health_threshold_crit)) + Paralyse(3) + + if(hallucination) + if(hallucination >= 20 && !(species.flags & (NO_POISON|IS_PLANT|NO_HALLUCINATION)) ) + if(prob(3)) + fake_attack(src) + if(!handling_hal) + spawn handle_hallucinations() //The not boring kind! + if(client && prob(5)) + client.dir = pick(2,4,8) + spawn(rand(20,50)) + client.dir = 1 + + hallucination = max(0, hallucination - 2) + else + for(var/atom/a in hallucinations) + qdel(a) + + //Brain damage from Oxyloss + if(should_have_organ("brain")) + var/brainOxPercent = 0.015 //Default 1.5% of your current oxyloss is applied as brain damage, 50 oxyloss is 1 brain damage + if(CE_STABLE in chem_effects) + brainOxPercent = 0.008 //Halved in effect + if(oxyloss >= (getMaxHealth() * 0.3) && prob(5)) // If oxyloss exceeds 30% of your max health, you can take brain damage. + adjustBrainLoss(brainOxPercent * oxyloss) + + if(halloss >= species.total_health) + to_chat(src, "You're in too much pain to keep going...") + src.visible_message("[src] slumps to the ground, too weak to continue fighting.") + Paralyse(10) + setHalLoss(species.total_health - 1) + + if(paralysis || sleeping) + blinded = 1 + set_stat(UNCONSCIOUS) + animate_tail_reset() + adjustHalLoss(-3) + + if(sleeping) + handle_dreams() + if (mind) + //Are they SSD? If so we'll keep them asleep but work off some of that sleep var in case of stoxin or similar. + if(client || sleeping > 3) + AdjustSleeping(-1) + throw_alert("asleep", /obj/screen/alert/asleep) + if( prob(2) && health && !hal_crit && client ) + spawn(0) + emote("snore") + //CONSCIOUS + else + set_stat(CONSCIOUS) + clear_alert("asleep") + + //Periodically double-check embedded_flag + if(embedded_flag && !(life_tick % 10)) + var/list/E + E = get_visible_implants(0) + if(!E.len) + embedded_flag = 0 + + //Eyes + //Check rig first because it's two-check and other checks will override it. + if(istype(back,/obj/item/weapon/rig)) + var/obj/item/weapon/rig/O = back + if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & EYES)) + if((O.offline && O.offline_vision_restriction == 2) || (!O.offline && O.vision_restriction == 2)) + blinded = 1 + + // Check everything else. + + //Periodically double-check embedded_flag + if(embedded_flag && !(life_tick % 10)) + if(!embedded_needs_process()) + embedded_flag = 0 + //Vision + var/obj/item/organ/vision + if(species.vision_organ) + vision = internal_organs_by_name[species.vision_organ] + + if(!species.vision_organ) // Presumably if a species has no vision organs, they see via some other means. + SetBlinded(0) + blinded = 0 + eye_blurry = 0 + clear_alert("blind") + else if(!vision || vision.is_broken()) // Vision organs cut out or broken? Permablind. + SetBlinded(1) + blinded = 1 + eye_blurry = 1 + throw_alert("blind", /obj/screen/alert/blind) + else //You have the requisite organs + if(sdisabilities & BLIND) // Disabled-blind, doesn't get better on its own + blinded = 1 + throw_alert("blind", /obj/screen/alert/blind) + else if(eye_blind) // Blindness, heals slowly over time + AdjustBlinded(-1) + blinded = 1 + throw_alert("blind", /obj/screen/alert/blind) + else if(istype(glasses, /obj/item/clothing/glasses/sunglasses/blindfold)) //resting your eyes with a blindfold heals blurry eyes faster + eye_blurry = max(eye_blurry-3, 0) + blinded = 1 + throw_alert("blind", /obj/screen/alert/blind) + + //blurry sight + if(vision.is_bruised()) // Vision organs impaired? Permablurry. + eye_blurry = 1 + if(eye_blurry) // Blurry eyes heal slowly + eye_blurry = max(eye_blurry-1, 0) + + //Ears + if(sdisabilities & DEAF) //disabled-deaf, doesn't get better on its own + ear_deaf = max(ear_deaf, 1) + else if(ear_deaf) //deafness, heals slowly over time + ear_deaf = max(ear_deaf-1, 0) + else if(get_ear_protection() >= 2) //resting your ears with earmuffs heals ear damage faster + ear_damage = max(ear_damage-0.15, 0) + ear_deaf = max(ear_deaf, 1) + else if(ear_damage < 25) //ear damage heals slowly under this threshold. otherwise you'll need earmuffs + ear_damage = max(ear_damage-0.05, 0) + + //Resting + if(resting) + dizziness = max(0, dizziness - 15) + jitteriness = max(0, jitteriness - 15) + adjustHalLoss(-3) + else + dizziness = max(0, dizziness - 3) + jitteriness = max(0, jitteriness - 3) + adjustHalLoss(-1) + + if (drowsyness) + drowsyness = max(0, drowsyness - 1) + eye_blurry = max(2, eye_blurry) + if (prob(5)) + Sleeping(1) + Paralyse(5) + + // If you're dirty, your gloves will become dirty, too. + if(gloves && germ_level > gloves.germ_level && prob(10)) + gloves.germ_level += 1 + + return 1 + +/mob/living/carbon/human/set_stat(var/new_stat) + . = ..() + if(. && stat) + update_skin(1) + +/mob/living/carbon/human/handle_regular_hud_updates() + if(hud_updateflag) // update our mob's hud overlays, AKA what others see flaoting above our head + handle_hud_list() + + // now handle what we see on our screen + + if(!client) + return 0 + + ..() + + client.screen.Remove(global_hud.blurry, global_hud.druggy, global_hud.vimpaired, global_hud.darkMask, global_hud.nvg, global_hud.thermal, global_hud.meson, global_hud.science, global_hud.material, global_hud.whitense) + + if(istype(client.eye,/obj/machinery/camera)) + var/obj/machinery/camera/cam = client.eye + client.screen |= cam.client_huds + + if(stat == DEAD) //Dead + if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO + if(healths) healths.icon_state = "health7" //DEAD healthmeter + + else if(stat == UNCONSCIOUS && health <= 0) //Crit + //Critical damage passage overlay + var/severity = 0 + switch(health) + if(-20 to -10) severity = 1 + if(-30 to -20) severity = 2 + if(-40 to -30) severity = 3 + if(-50 to -40) severity = 4 + if(-60 to -50) severity = 5 + if(-70 to -60) severity = 6 + if(-80 to -70) severity = 7 + if(-90 to -80) severity = 8 + if(-95 to -90) severity = 9 + if(-INFINITY to -95) severity = 10 + overlay_fullscreen("crit", /obj/screen/fullscreen/crit, severity) + else //Alive + clear_fullscreen("crit") + //Oxygen damage overlay + if(oxyloss) + var/severity = 0 + switch(oxyloss) + if(10 to 20) severity = 1 + if(20 to 25) severity = 2 + if(25 to 30) severity = 3 + if(30 to 35) severity = 4 + if(35 to 40) severity = 5 + if(40 to 45) severity = 6 + if(45 to INFINITY) severity = 7 + overlay_fullscreen("oxy", /obj/screen/fullscreen/oxy, severity) + else + clear_fullscreen("oxy") + + //Fire and Brute damage overlay (BSSR) + var/hurtdamage = src.getShockBruteLoss() + src.getShockFireLoss() + damageoverlaytemp //Doesn't call the overlay if you can't actually feel it + damageoverlaytemp = 0 // We do this so we can detect if someone hits us or not. + if(hurtdamage) + var/severity = 0 + switch(hurtdamage) + if(10 to 25) severity = 1 + if(25 to 40) severity = 2 + if(40 to 55) severity = 3 + if(55 to 70) severity = 4 + if(70 to 85) severity = 5 + if(85 to INFINITY) severity = 6 + overlay_fullscreen("brute", /obj/screen/fullscreen/brute, severity) + else + clear_fullscreen("brute") + + if(healths) + if (chem_effects[CE_PAINKILLER] > 100) + healths.icon_state = "health_numb" + else + // Generate a by-limb health display. + var/mutable_appearance/healths_ma = new(healths) + healths_ma.icon_state = "blank" + healths_ma.overlays = null + healths_ma.plane = PLANE_PLAYER_HUD + + var/no_damage = 1 + var/trauma_val = 0 // Used in calculating softcrit/hardcrit indicators. + if(!(species.flags & NO_PAIN)) + trauma_val = max(traumatic_shock,halloss)/species.total_health + var/limb_trauma_val = trauma_val*0.3 + // Collect and apply the images all at once to avoid appearance churn. + var/list/health_images = list() + for(var/obj/item/organ/external/E in organs) + if(no_damage && (E.brute_dam || E.burn_dam)) + no_damage = 0 + health_images += E.get_damage_hud_image(limb_trauma_val) + + // Apply a fire overlay if we're burning. + if(on_fire) + health_images += image('icons/mob/OnFire.dmi',"[get_fire_icon_state()]") + + // Show a general pain/crit indicator if needed. + if(trauma_val) + if(!(species.flags & NO_PAIN)) + if(trauma_val > 0.7) + health_images += image('icons/mob/screen1_health.dmi',"softcrit") + if(trauma_val >= 1) + health_images += image('icons/mob/screen1_health.dmi',"hardcrit") + else if(no_damage) + health_images += image('icons/mob/screen1_health.dmi',"fullhealth") + + healths_ma.add_overlay(health_images) + healths.appearance = healths_ma + + + var/fat_alert = /obj/screen/alert/fat + var/hungry_alert = /obj/screen/alert/hungry + var/starving_alert = /obj/screen/alert/starving + + if(isSynthetic()) + fat_alert = /obj/screen/alert/fat/synth + hungry_alert = /obj/screen/alert/hungry/synth + starving_alert = /obj/screen/alert/starving/synth + //VOREStation Add - Vampire hunger alert + else if(get_species() == SPECIES_CUSTOM) + var/datum/species/custom/C = species + if(/datum/trait/neutral/bloodsucker in C.traits) + fat_alert = /obj/screen/alert/fat/vampire + hungry_alert = /obj/screen/alert/hungry/vampire + starving_alert = /obj/screen/alert/starving/vampire + //VOREStation Add End + + switch(nutrition) + if(450 to INFINITY) + throw_alert("nutrition", fat_alert) + // if(350 to 450) + // if(250 to 350) // Alternative more-detailed tiers, not used. + if(250 to 450) + clear_alert("nutrition") + if(150 to 250) + throw_alert("nutrition", hungry_alert) + else + throw_alert("nutrition", starving_alert) + + if(blinded) + overlay_fullscreen("blind", /obj/screen/fullscreen/blind) + throw_alert("blind", /obj/screen/alert/blind) + else + clear_fullscreen("blind") + clear_alert("blind") + + var/apply_nearsighted_overlay = FALSE + if(disabilities & NEARSIGHTED) + apply_nearsighted_overlay = TRUE + + if(glasses) + var/obj/item/clothing/glasses/G = glasses + if(G.prescription) + apply_nearsighted_overlay = FALSE + + if(nif && nif.flag_check(NIF_V_CORRECTIVE, NIF_FLAGS_VISION)) // VOREStation Edit - NIF + apply_nearsighted_overlay = FALSE + + set_fullscreen(apply_nearsighted_overlay, "nearsighted", /obj/screen/fullscreen/impaired, 1) + + set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) + set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) + if(druggy) + throw_alert("high", /obj/screen/alert/high) + else + clear_alert("high") + + if(!isbelly(loc)) //VOREStation Add - Belly fullscreens safety + clear_fullscreen("belly") + clear_fullscreen("belly2") + clear_fullscreen("belly3") + clear_fullscreen("belly4") + + if(config.welder_vision) + var/found_welder + if(species.short_sighted) + found_welder = 1 + else + if(istype(glasses, /obj/item/clothing/glasses/welding)) + var/obj/item/clothing/glasses/welding/O = glasses + if(!O.up) + found_welder = 1 + if(!found_welder && nif && nif.flag_check(NIF_V_UVFILTER,NIF_FLAGS_VISION)) found_welder = 1 //VOREStation Add - NIF + if(istype(glasses, /obj/item/clothing/glasses/sunglasses/thinblindfold)) + found_welder = 1 + if(!found_welder && istype(head, /obj/item/clothing/head/welding)) + var/obj/item/clothing/head/welding/O = head + if(!O.up) + found_welder = 1 + if(!found_welder && istype(back, /obj/item/weapon/rig)) + var/obj/item/weapon/rig/O = back + if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & EYES)) + if((O.offline && O.offline_vision_restriction == 1) || (!O.offline && O.vision_restriction == 1)) + found_welder = 1 + if(absorbed) found_welder = 1 //VOREStation Code + if(found_welder) + client.screen |= global_hud.darkMask + +/mob/living/carbon/human/reset_view(atom/A) + ..() + if(machine_visual && machine_visual != A) + machine_visual.remove_visual(src) + +/mob/living/carbon/human/handle_vision() + if(stat == DEAD) + sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS|SEE_SELF + see_in_dark = 8 + if(client) + if(client.view != world.view) // If mob dies while zoomed in with device, unzoom them. + for(var/obj/item/item in contents) + if(item.zoom) + item.zoom() + break + + else //We aren't dead + sight &= ~(SEE_TURFS|SEE_MOBS|SEE_OBJS) + see_invisible = see_in_dark>2 ? SEE_INVISIBLE_LEVEL_ONE : see_invisible_default + + // Do this early so certain stuff gets turned off before vision is assigned. + var/area/A = get_area(src) + if(A?.no_spoilers) + disable_spoiler_vision() + + if(XRAY in mutations) + sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS + see_in_dark = 8 + if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO + + if(seer==1) + var/obj/effect/rune/R = locate() in loc + if(R && R.word1 == cultwords["see"] && R.word2 == cultwords["hell"] && R.word3 == cultwords["join"]) + see_invisible = SEE_INVISIBLE_CULT + else + see_invisible = see_invisible_default + seer = 0 + + if(!seedarkness) + sight = species.get_vision_flags(src) + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_NOLIGHTING + + else + sight = species.get_vision_flags(src) + see_in_dark = species.darksight + see_invisible = see_in_dark>2 ? SEE_INVISIBLE_LEVEL_ONE : see_invisible_default + + var/glasses_processed = 0 + var/obj/item/weapon/rig/rig = get_rig() + if(istype(rig) && rig.visor && !looking_elsewhere) + if(!rig.helmet || (head && rig.helmet == head)) + if(rig.visor && rig.visor.vision && rig.visor.active && rig.visor.vision.glasses) + glasses_processed = process_glasses(rig.visor.vision.glasses) + + if(glasses && !glasses_processed && !looking_elsewhere) + glasses_processed = process_glasses(glasses) + if(XRAY in mutations) + sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS + see_in_dark = 8 + if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.vision_flags)) + sight |= M.vision_flags + + if(!glasses_processed && nif) + var/datum/nifsoft/vision_soft + for(var/datum/nifsoft/NS in nif.nifsofts) + if(NS.vision_exclusive && NS.active) + vision_soft = NS + break + if(vision_soft) + glasses_processed = process_nifsoft_vision(vision_soft) //not really glasses but equitable + + if(!glasses_processed && (species.get_vision_flags(src) > 0)) + sight |= species.get_vision_flags(src) + if(!seer && !glasses_processed && seedarkness) + see_invisible = see_invisible_default + + if(machine) + var/viewflags = machine.check_eye(src) + if(viewflags < 0) + reset_view(null, 0) + else if(viewflags && !looking_elsewhere) + sight |= viewflags + else + machine.apply_visual(src) + else if(eyeobj) + if(eyeobj.owner != src) + + reset_view(null) + else + var/isRemoteObserve = 0 + if((mRemote in mutations) && remoteview_target) + if(remoteview_target.stat==CONSCIOUS) + isRemoteObserve = 1 + if(!isRemoteObserve && client && !client.adminobs) + remoteview_target = null + reset_view(null, 0) + return 1 + +/mob/living/carbon/human/proc/process_glasses(var/obj/item/clothing/glasses/G) + . = FALSE + if(G && G.active) + if(G.darkness_view) + see_in_dark += G.darkness_view + . = TRUE + if(G.overlay && client) + client.screen |= G.overlay + if(G.vision_flags) + sight |= G.vision_flags + . = TRUE + if(istype(G,/obj/item/clothing/glasses/night) && !seer) + see_invisible = SEE_INVISIBLE_MINIMUM + + if(G.see_invisible >= 0) + see_invisible = G.see_invisible + . = TRUE + else if(!druggy && !seer) + see_invisible = see_invisible_default + +/mob/living/carbon/human/proc/process_nifsoft_vision(var/datum/nifsoft/NS) + . = FALSE + if(NS && NS.active) + if(NS.darkness_view) + see_in_dark += NS.darkness_view + . = TRUE + if(NS.vision_flags_mob) + sight |= NS.vision_flags_mob + . = TRUE + +/mob/living/carbon/human/handle_random_events() + if(inStasisNow()) + return + + // Puke if toxloss is too high + if(!stat && !isbelly(loc)) + if (getToxLoss() >= 30 && isSynthetic()) + if(!confused) + if(prob(5)) + to_chat(src, "You lose directional control!") + Confuse(10) + if (getToxLoss() >= 45 && !isSynthetic()) + spawn vomit() + + + //0.1% chance of playing a scary sound to someone who's in complete darkness + if(isturf(loc) && rand(1,1000) == 1) + var/turf/T = loc + if(T.get_lumcount() <= LIGHTING_SOFT_THRESHOLD) + //VOREStation Add Start + if(text2num(time2text(world.timeofday, "MM")) == 4) + if(text2num(time2text(world.timeofday, "DD")) == 1) + playsound_local(src,pick(scawwySownds),50, 0) + return + //VOREStation Add End + playsound_local(src,pick(scarySounds),50, 1, -1) + +/mob/living/carbon/human/handle_stomach() + spawn(0) + for(var/mob/living/M in stomach_contents) + if(M.loc != src) + stomach_contents.Remove(M) + continue + if(istype(M, /mob/living/carbon) && stat != 2) + if(M.stat == 2) + M.death(1) + stomach_contents.Remove(M) + qdel(M) + continue + if(air_master.current_cycle%3==1) + if(!(M.status_flags & GODMODE)) + M.adjustBruteLoss(5) + adjust_nutrition(10) + +/mob/living/carbon/human/proc/handle_changeling() + if(mind && mind.changeling) + mind.changeling.regenerate() + if(hud_used) + ling_chem_display.invisibility = 0 +// ling_chem_display.maptext = "
                    [round(mind.changeling.chem_charges)]
                    " + switch(mind.changeling.chem_storage) + if(1 to 50) + switch(mind.changeling.chem_charges) + if(0 to 9) + ling_chem_display.icon_state = "ling_chems0" + if(10 to 19) + ling_chem_display.icon_state = "ling_chems10" + if(20 to 29) + ling_chem_display.icon_state = "ling_chems20" + if(30 to 39) + ling_chem_display.icon_state = "ling_chems30" + if(40 to 49) + ling_chem_display.icon_state = "ling_chems40" + if(50) + ling_chem_display.icon_state = "ling_chems50" + if(51 to 80) //This is a crappy way of checking for engorged sacs... + switch(mind.changeling.chem_charges) + if(0 to 9) + ling_chem_display.icon_state = "ling_chems0e" + if(10 to 19) + ling_chem_display.icon_state = "ling_chems10e" + if(20 to 29) + ling_chem_display.icon_state = "ling_chems20e" + if(30 to 39) + ling_chem_display.icon_state = "ling_chems30e" + if(40 to 49) + ling_chem_display.icon_state = "ling_chems40e" + if(50 to 59) + ling_chem_display.icon_state = "ling_chems50e" + if(60 to 69) + ling_chem_display.icon_state = "ling_chems60e" + if(70 to 79) + ling_chem_display.icon_state = "ling_chems70e" + if(80) + ling_chem_display.icon_state = "ling_chems80e" + else + if(mind && hud_used) + ling_chem_display.invisibility = 101 + +/mob/living/carbon/human/handle_shock() + ..() + if(status_flags & GODMODE) return 0 //godmode + if(!can_feel_pain()) return + + if(health < config.health_threshold_softcrit)// health 0 makes you immediately collapse + shock_stage = max(shock_stage, 61) + + if(traumatic_shock >= 80) + shock_stage += 1 + else if(health < config.health_threshold_softcrit) + shock_stage = max(shock_stage, 61) + else + shock_stage = min(shock_stage, 160) + shock_stage = max(shock_stage-1, 0) + + if(stat) + return 0 + + if(shock_stage == 10) + if(traumatic_shock >= 80) + custom_pain("[pick("It hurts so much", "You really need some painkillers", "Dear god, the pain")]!", 40) + + if(shock_stage >= 30) + if(shock_stage == 30 && !isbelly(loc)) //VOREStation Edit + custom_emote(VISIBLE_MESSAGE, "is having trouble keeping their eyes open.") + eye_blurry = max(2, eye_blurry) + if(traumatic_shock >= 80) + stuttering = max(stuttering, 5) + + + if(shock_stage == 40) + if(traumatic_shock >= 80) + to_chat(src, "[pick("The pain is excruciating", "Please, just end the pain", "Your whole body is going numb")]!") + + if (shock_stage >= 60) + if(shock_stage == 60 && !isbelly(loc)) //VOREStation Edit + custom_emote(VISIBLE_MESSAGE, "'s body becomes limp.") + if (prob(2)) + if(traumatic_shock >= 80) + to_chat(src, "[pick("The pain is excruciating", "Please, just end the pain", "Your whole body is going numb")]!") + Weaken(20) + + if(shock_stage >= 80) + if (prob(5)) + if(traumatic_shock >= 80) + to_chat(src, "[pick("The pain is excruciating", "Please, just end the pain", "Your whole body is going numb")]!") + Weaken(20) + + if(shock_stage >= 120) + if (prob(2)) + if(traumatic_shock >= 80) + to_chat(src, "[pick("You black out", "You feel like you could die any moment now", "You are about to lose consciousness")]!") + Paralyse(5) + + if(shock_stage == 150) + if(!isbelly(loc)) //VOREStation Edit + custom_emote(VISIBLE_MESSAGE, "can no longer stand, collapsing!") + Weaken(20) + + if(shock_stage >= 150) + Weaken(20) + +/mob/living/carbon/human/proc/handle_pulse() + if(life_tick % 5) return pulse //update pulse every 5 life ticks (~1 tick/sec, depending on server load) + + var/temp = PULSE_NORM + + var/brain_modifier = 1 + + var/modifier_shift = 0 + var/modifier_set + + if(modifiers && modifiers.len) + for(var/datum/modifier/mod in modifiers) + if(isnull(modifier_set) && !isnull(mod.pulse_set_level)) + modifier_set = round(mod.pulse_set_level) // Should be a whole number, but let's not take chances. + else if(mod.pulse_set_level > modifier_set) + modifier_set = round(mod.pulse_set_level) + + modifier_set = max(0, modifier_set) // No setting to negatives. + + if(mod.pulse_modifier) + modifier_shift += mod.pulse_modifier + + modifier_shift = round(modifier_shift) + + if(!internal_organs_by_name[O_HEART]) + temp = PULSE_NONE + if(!isnull(modifier_set)) + temp = modifier_set + return temp //No blood, no pulse. + + if(stat == DEAD) + temp = PULSE_NONE + if(!isnull(modifier_set)) + temp = modifier_set + return temp //that's it, you're dead, nothing can influence your pulse, aside from outside means. + + var/obj/item/organ/internal/heart/Pump = internal_organs_by_name[O_HEART] + + var/obj/item/organ/internal/brain/Control = internal_organs_by_name[O_BRAIN] + + if(Control) + brain_modifier = Control.get_control_efficiency() + + if(brain_modifier <= 0.7 && brain_modifier >= 0.4) // 70%-40% control, things start going weird as the brain is failing. + brain_modifier = rand(5, 15) / 10 + + if(Pump) + temp += Pump.standard_pulse_level - PULSE_NORM + + if(round(vessel.get_reagent_amount("blood")) <= species.blood_volume*species.blood_level_danger) //how much blood do we have + temp = temp + 3 //not enough :( + + if(status_flags & FAKEDEATH) + temp = PULSE_NONE //pretend that we're dead. unlike actual death, can be inflienced by meds + + if(!isnull(modifier_set)) + temp = modifier_set + + temp = max(0, temp + modifier_shift) // No negative pulses. + + if(Pump) + for(var/datum/reagent/R in reagents.reagent_list) + if(R.id in bradycardics) + if(temp <= Pump.standard_pulse_level + 3 && temp >= Pump.standard_pulse_level) + temp-- + if(R.id in tachycardics) + if(temp <= Pump.standard_pulse_level + 1 && temp >= PULSE_NONE) + temp++ + if(R.id in heartstopper) //To avoid using fakedeath + temp = PULSE_NONE + if(R.id in cheartstopper) //Conditional heart-stoppage + if(R.volume >= R.overdose) + temp = PULSE_NONE + return temp * brain_modifier + //handles different chems' influence on pulse + for(var/datum/reagent/R in reagents.reagent_list) + if(R.id in bradycardics) + if(temp <= PULSE_THREADY && temp >= PULSE_NORM) + temp-- + if(R.id in tachycardics) + if(temp <= PULSE_FAST && temp >= PULSE_NONE) + temp++ + if(R.id in heartstopper) //To avoid using fakedeath + temp = PULSE_NONE + if(R.id in cheartstopper) //Conditional heart-stoppage + if(R.volume >= R.overdose) + temp = PULSE_NONE + + return max(0, round(temp * brain_modifier)) + +/mob/living/carbon/human/proc/handle_heartbeat() + if(pulse == PULSE_NONE) + return + + var/obj/item/organ/internal/heart/H = internal_organs_by_name[O_HEART] + + if(!H || (H.robotic >= ORGAN_ROBOT)) + return + + if(pulse >= PULSE_2FAST || shock_stage >= 10 || (istype(get_turf(src), /turf/space) && is_preference_enabled(/datum/client_preference/play_ambiance))) + //PULSE_THREADY - maximum value for pulse, currently it 5. + //High pulse value corresponds to a fast rate of heartbeat. + //Divided by 2, otherwise it is too slow. + var/rate = (PULSE_THREADY - pulse)/2 + + if(heartbeat >= rate) + heartbeat = 0 + src << sound('sound/effects/singlebeat.ogg',0,0,0,50) + else + heartbeat++ + +/* + Called by life(), instead of having the individual hud items update icons each tick and check for status changes + we only set those statuses and icons upon changes. Then those HUD items will simply add those pre-made images. + This proc below is only called when those HUD elements need to change as determined by the mobs hud_updateflag. +*/ +/mob/living/carbon/human/proc/handle_hud_list() + if (BITTEST(hud_updateflag, HEALTH_HUD)) + var/image/holder = grab_hud(HEALTH_HUD) + if(stat == DEAD) + holder.icon_state = "-100" // X_X + else + holder.icon_state = RoundHealth((health-config.health_threshold_crit)/(getMaxHealth()-config.health_threshold_crit)*100) + apply_hud(HEALTH_HUD, holder) + + if (BITTEST(hud_updateflag, LIFE_HUD)) + var/image/holder = grab_hud(LIFE_HUD) + if(isSynthetic()) + holder.icon_state = "hudrobo" + else if(stat == DEAD) + holder.icon_state = "huddead" + else + holder.icon_state = "hudhealthy" + apply_hud(LIFE_HUD, holder) + + if (BITTEST(hud_updateflag, STATUS_HUD)) + var/foundVirus = 0 + for (var/ID in virus2) + if (ID in virusDB) + foundVirus = 1 + break + + var/image/holder = grab_hud(STATUS_HUD) + var/image/holder2 = grab_hud(STATUS_HUD_OOC) + if (isSynthetic()) + holder.icon_state = "hudrobo" + else if(stat == DEAD) + holder.icon_state = "huddead" + holder2.icon_state = "huddead" + else if(foundVirus) + holder.icon_state = "hudill" + else if(has_brain_worms()) + var/mob/living/simple_mob/animal/borer/B = has_brain_worms() + if(B.controlling) + holder.icon_state = "hudbrainworm" + else + holder.icon_state = "hudhealthy" + holder2.icon_state = "hudbrainworm" + else + holder.icon_state = "hudhealthy" + if(virus2.len) + holder2.icon_state = "hudill" + else + holder2.icon_state = "hudhealthy" + + apply_hud(STATUS_HUD, holder) + apply_hud(STATUS_HUD_OOC, holder2) + + if (BITTEST(hud_updateflag, ID_HUD)) + var/image/holder = grab_hud(ID_HUD) + if(wear_id) + var/obj/item/weapon/card/id/I = wear_id.GetID() + if(I) + holder.icon_state = "hud[ckey(I.GetJobName())]" + else + holder.icon_state = "hudunknown" + else + holder.icon_state = "hudunknown" + + apply_hud(ID_HUD, holder) + + if (BITTEST(hud_updateflag, WANTED_HUD)) + var/image/holder = grab_hud(WANTED_HUD) + holder.icon_state = "hudblank" + var/perpname = name + if(wear_id) + var/obj/item/weapon/card/id/I = wear_id.GetID() + if(I) + perpname = I.registered_name + + for(var/datum/data/record/E in data_core.general) + if(E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.security) + if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "*Arrest*")) + holder.icon_state = "hudwanted" + break + else if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "Incarcerated")) + holder.icon_state = "hudprisoner" + break + else if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "Parolled")) + holder.icon_state = "hudparolled" + break + else if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "Released")) + holder.icon_state = "hudreleased" + break + + apply_hud(WANTED_HUD, holder) + + if ( BITTEST(hud_updateflag, IMPLOYAL_HUD) \ + || BITTEST(hud_updateflag, IMPCHEM_HUD) \ + || BITTEST(hud_updateflag, IMPTRACK_HUD)) + + var/image/holder1 = grab_hud(IMPTRACK_HUD) + var/image/holder2 = grab_hud(IMPLOYAL_HUD) + var/image/holder3 = grab_hud(IMPCHEM_HUD) + + holder1.icon_state = "hudblank" + holder2.icon_state = "hudblank" + holder3.icon_state = "hudblank" + + for(var/obj/item/weapon/implant/I in src) + if(I.implanted) + if(!I.malfunction) + if(istype(I,/obj/item/weapon/implant/tracking)) + holder1.icon_state = "hud_imp_tracking" + if(istype(I,/obj/item/weapon/implant/loyalty)) + holder2.icon_state = "hud_imp_loyal" + if(istype(I,/obj/item/weapon/implant/chem)) + holder3.icon_state = "hud_imp_chem" + + apply_hud(IMPTRACK_HUD, holder1) + apply_hud(IMPLOYAL_HUD, holder2) + apply_hud(IMPCHEM_HUD, holder3) + + if (BITTEST(hud_updateflag, SPECIALROLE_HUD)) + var/image/holder = grab_hud(SPECIALROLE_HUD) + holder.icon_state = "hudblank" + if(mind && mind.special_role) + if(hud_icon_reference[mind.special_role]) + holder.icon_state = hud_icon_reference[mind.special_role] + else + holder.icon_state = "hudsyndicate" + apply_hud(SPECIALROLE_HUD, holder) + + attempt_vr(src,"handle_hud_list_vr",list()) //VOREStation Add - Custom HUDs. + + hud_updateflag = 0 + +/mob/living/carbon/human/handle_fire() + if(..()) + return + + var/thermal_protection = get_heat_protection(fire_stacks * 1500) // Arbitrary but below firesuit max temp when below 20 stacks. + + if(thermal_protection == 1) // Immune. + return + else + var/fire_temp_add = (BODYTEMP_HEATING_MAX + (fire_stacks * 15)) * (1-thermal_protection) + //This is to prevent humans from heating up indefinitely. A human being on fire (fat burns at 250C) can't magically + // increase your body temperature beyond 250C, but it's possible something else (atmos) has heated us up beyond it, + // so don't worry about the firestacks at that point. Really, we should be cooling the room down, because it has + // to expend energy to heat our body up! But let's not worry about that. + + // This whole section above is ABSOLUTELY STUPID and makes no sense and this would prevent too-high-heat from even being able to hurt someone. No. We will heat up for as long as needed. + //if((bodytemperature + fire_temp_add) > HUMAN_COMBUSTION_TEMP) + // return + + bodytemperature += fire_temp_add + +/mob/living/carbon/human/rejuvenate() + restore_blood() + shock_stage = 0 + traumatic_shock = 0 + ..() + +/mob/living/carbon/human/proc/handle_defib_timer() + if(!should_have_organ(O_BRAIN)) + return // No brain. + + var/obj/item/organ/internal/brain/brain = internal_organs_by_name[O_BRAIN] + if(!brain) + return // Still no brain. + + brain.tick_defib_timer() + +#undef HUMAN_MAX_OXYLOSS +#undef HUMAN_CRIT_MAX_OXYLOSS diff --git a/code/modules/mob/living/carbon/human/login.dm b/code/modules/mob/living/carbon/human/login.dm index 839385ac62d..d3afee61188 100644 --- a/code/modules/mob/living/carbon/human/login.dm +++ b/code/modules/mob/living/carbon/human/login.dm @@ -1,31 +1,31 @@ -// VOREStation Add Start: Doing this here bc AUTOHISS_FULL is more readable than # -#define AUTOHISS_OFF 0 -#define AUTOHISS_BASIC 1 -#define AUTOHISS_FULL 2 -// VOREStation Add End - -/mob/living/carbon/human/Login() - ..() - update_hud() - // VOREStation Add - if(client.prefs) // Safety, just in case so we don't runtime. - if(!client.prefs.autohiss) - client.autohiss_mode = AUTOHISS_FULL - else - switch(client.prefs.autohiss) - if("Full") - client.autohiss_mode = AUTOHISS_FULL - if("Basic") - client.autohiss_mode = AUTOHISS_BASIC - if("Off") - client.autohiss_mode = AUTOHISS_OFF - consider_birthday() - // VOREStation Add - if(species) species.handle_login_special(src) - return - -// VOREStation Add Start: Doing this here bc AUTOHISS_FULL is more readable than # -#undef AUTOHISS_OFF -#undef AUTOHISS_BASIC -#undef AUTOHISS_FULL -// VOREStation Add End +// VOREStation Add Start: Doing this here bc AUTOHISS_FULL is more readable than # +#define AUTOHISS_OFF 0 +#define AUTOHISS_BASIC 1 +#define AUTOHISS_FULL 2 +// VOREStation Add End + +/mob/living/carbon/human/Login() + ..() + update_hud() + // VOREStation Add + if(client.prefs) // Safety, just in case so we don't runtime. + if(!client.prefs.autohiss) + client.autohiss_mode = AUTOHISS_FULL + else + switch(client.prefs.autohiss) + if("Full") + client.autohiss_mode = AUTOHISS_FULL + if("Basic") + client.autohiss_mode = AUTOHISS_BASIC + if("Off") + client.autohiss_mode = AUTOHISS_OFF + consider_birthday() + // VOREStation Add + if(species) species.handle_login_special(src) + return + +// VOREStation Add Start: Doing this here bc AUTOHISS_FULL is more readable than # +#undef AUTOHISS_OFF +#undef AUTOHISS_BASIC +#undef AUTOHISS_FULL +// VOREStation Add End diff --git a/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm b/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm index ec054ab5207..87c71c52963 100644 --- a/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm +++ b/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm @@ -1,120 +1,120 @@ -// ### Wooo, inheritance. Basically copying everything I don't need to edit from prometheans, because they mostly work already. -// ### Any and all of this is open to change for balance or whatever. -// ### -// ### -// Species definition follows. -/datum/species/shapeshifter/promethean/avatar - - name = SPECIES_VR - name_plural = "Virtual Reality Avatars" - blurb = "A 3-dimensional representation of some sort of animate object used to display the presence and actions of some-one or -thing using a virtual reality program." - show_ssd = "eerily still" - death_message = "flickers briefly, their gear falling in a heap on the floor around their motionless body." - knockout_message = "has been knocked unconscious!" - - spawn_flags = SPECIES_IS_RESTRICTED - - speech_bubble_appearance = "cyber" - - assisted_langs = list() - - male_cough_sounds = list('sound/effects/mob_effects/m_cougha.ogg','sound/effects/mob_effects/m_coughb.ogg', 'sound/effects/mob_effects/m_coughc.ogg') - female_cough_sounds = list('sound/effects/mob_effects/f_cougha.ogg','sound/effects/mob_effects/f_coughb.ogg') - male_sneeze_sound = 'sound/effects/mob_effects/sneeze.ogg' - female_sneeze_sound = 'sound/effects/mob_effects/f_sneeze.ogg' - - valid_transform_species = list(SPECIES_HUMAN, SPECIES_HUMAN_VATBORN, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_SKRELL, SPECIES_DIONA, SPECIES_TESHARI, SPECIES_VOX, SPECIES_MONKEY, SPECIES_SKELETON) - - unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/punch, /datum/unarmed_attack/bite) - has_organ = list(O_BRAIN = /obj/item/organ/internal/brain/slime, O_EYES = /obj/item/organ/internal/eyes) // Slime core. - heal_rate = 0 // Avatars don't naturally heal like prometheans, at least not for now - inherent_verbs = list( - /mob/living/carbon/human/proc/shapeshifter_select_shape, - /mob/living/carbon/human/proc/shapeshifter_select_colour, - /mob/living/carbon/human/proc/shapeshifter_select_hair, - /mob/living/carbon/human/proc/shapeshifter_select_hair_colors, - /mob/living/carbon/human/proc/shapeshifter_select_gender, - /mob/living/carbon/human/proc/shapeshifter_select_wings, - /mob/living/carbon/human/proc/shapeshifter_select_tail, - /mob/living/carbon/human/proc/shapeshifter_select_ears, - /mob/living/proc/set_size, - /mob/living/carbon/human/proc/regenerate, - /mob/living/carbon/human/proc/promethean_select_opaqueness, - /mob/living/carbon/human/proc/exit_vr - ) - - -/datum/species/shapeshifter/promethean/avatar/handle_death(var/mob/living/carbon/human/H) - return - -/datum/species/shapeshifter/promethean/avatar/handle_environment_special(var/mob/living/carbon/human/H) - return - -/mob/living/carbon/human/proc/shapeshifter_change_opacity() - - set name = "Toggle Opacity" - set category = "Abilities" - - if(stat || world.time < last_special) - return - - last_special = world.time + 10 - - if(src.icon_state == "promethean") - icon_state = lowertext(src.species.get_bodytype(src)) - shapeshifter_change_species("Virtual Reality [src.species.get_bodytype(src)]") - else - icon_state = "promethean" - shapeshifter_change_species(SPECIES_VR) - - -// enter_vr is called on the original mob, and puts the mind into the supplied vr mob -/mob/living/carbon/human/proc/enter_vr(var/mob/living/carbon/human/avatar) // Avatar is currently a human, because we have preexisting setup code for appearance manipulation, etc. - if(!istype(avatar)) - return - - // Link the two mobs for client transfer - avatar.vr_holder = src - src.teleop = avatar - src.vr_link = avatar // Can't reuse vr_holder so that death can automatically eject users from VR - - // Move the mind - avatar.Sleeping(1) - src.mind.transfer_to(avatar) - to_chat(avatar, "You have enterred Virtual Reality!\nAll normal gameplay rules still apply.\nWounds you suffer here won't persist when you leave VR, but some of the pain will.\nYou can leave VR at any time by using the \"Exit Virtual Reality\" verb in the Abilities tab, or by ghosting.") //No more prommie VR thing, so removed tidbit about changing appearance - to_chat(avatar, " You black out for a moment, and wake to find yourself in a new body in virtual reality.") // So this is what VR feels like? - -// exit_vr is called on the vr mob, and puts the mind back into the original mob -/mob/living/carbon/human/proc/exit_vr() - set name = "Exit Virtual Reality" - set category = "Abilities" - - if(!vr_holder) - return - if(!mind) - return - - var/total_damage - // Tally human damage - if(ishuman(src)) - var/mob/living/carbon/human/H = src - total_damage = H.getBruteLoss() + H.getFireLoss() + H.getOxyLoss() + H.getToxLoss() - - // Move the mind back to the original mob -// vr_holder.Sleeping(1) - src.mind.transfer_to(vr_holder) - to_chat(vr_holder, "You black out for a moment, and wake to find yourself back in your own body.") - // Two-thirds damage is transferred as agony for /humans - // Getting hurt in VR doesn't damage the physical body, but you still got hurt. - if(ishuman(vr_holder) && total_damage) - var/mob/living/carbon/human/V = vr_holder - V.stun_effect_act(0, total_damage*2/3, null) // 200 damage leaves the user in paincrit for several seconds, agony reaches 0 after around 2m. - to_chat(vr_holder, "Pain from your time in VR lingers.") // 250 damage leaves the user unconscious for several seconds in addition to paincrit - - // Maintain a link with the mob, but don't use teleop - vr_holder.vr_link = src - vr_holder.teleop = null - - if(istype(vr_holder.loc, /obj/machinery/vr_sleeper)) - var/obj/machinery/vr_sleeper/V = vr_holder.loc +// ### Wooo, inheritance. Basically copying everything I don't need to edit from prometheans, because they mostly work already. +// ### Any and all of this is open to change for balance or whatever. +// ### +// ### +// Species definition follows. +/datum/species/shapeshifter/promethean/avatar + + name = SPECIES_VR + name_plural = "Virtual Reality Avatars" + blurb = "A 3-dimensional representation of some sort of animate object used to display the presence and actions of some-one or -thing using a virtual reality program." + show_ssd = "eerily still" + death_message = "flickers briefly, their gear falling in a heap on the floor around their motionless body." + knockout_message = "has been knocked unconscious!" + + spawn_flags = SPECIES_IS_RESTRICTED + + speech_bubble_appearance = "cyber" + + assisted_langs = list() + + male_cough_sounds = list('sound/effects/mob_effects/m_cougha.ogg','sound/effects/mob_effects/m_coughb.ogg', 'sound/effects/mob_effects/m_coughc.ogg') + female_cough_sounds = list('sound/effects/mob_effects/f_cougha.ogg','sound/effects/mob_effects/f_coughb.ogg') + male_sneeze_sound = 'sound/effects/mob_effects/sneeze.ogg' + female_sneeze_sound = 'sound/effects/mob_effects/f_sneeze.ogg' + + valid_transform_species = list(SPECIES_HUMAN, SPECIES_HUMAN_VATBORN, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_SKRELL, SPECIES_DIONA, SPECIES_TESHARI, SPECIES_VOX, SPECIES_MONKEY, SPECIES_SKELETON) + + unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/punch, /datum/unarmed_attack/bite) + has_organ = list(O_BRAIN = /obj/item/organ/internal/brain/slime, O_EYES = /obj/item/organ/internal/eyes) // Slime core. + heal_rate = 0 // Avatars don't naturally heal like prometheans, at least not for now + inherent_verbs = list( + /mob/living/carbon/human/proc/shapeshifter_select_shape, + /mob/living/carbon/human/proc/shapeshifter_select_colour, + /mob/living/carbon/human/proc/shapeshifter_select_hair, + /mob/living/carbon/human/proc/shapeshifter_select_hair_colors, + /mob/living/carbon/human/proc/shapeshifter_select_gender, + /mob/living/carbon/human/proc/shapeshifter_select_wings, + /mob/living/carbon/human/proc/shapeshifter_select_tail, + /mob/living/carbon/human/proc/shapeshifter_select_ears, + /mob/living/proc/set_size, + /mob/living/carbon/human/proc/regenerate, + /mob/living/carbon/human/proc/promethean_select_opaqueness, + /mob/living/carbon/human/proc/exit_vr + ) + + +/datum/species/shapeshifter/promethean/avatar/handle_death(var/mob/living/carbon/human/H) + return + +/datum/species/shapeshifter/promethean/avatar/handle_environment_special(var/mob/living/carbon/human/H) + return + +/mob/living/carbon/human/proc/shapeshifter_change_opacity() + + set name = "Toggle Opacity" + set category = "Abilities" + + if(stat || world.time < last_special) + return + + last_special = world.time + 10 + + if(src.icon_state == "promethean") + icon_state = lowertext(src.species.get_bodytype(src)) + shapeshifter_change_species("Virtual Reality [src.species.get_bodytype(src)]") + else + icon_state = "promethean" + shapeshifter_change_species(SPECIES_VR) + + +// enter_vr is called on the original mob, and puts the mind into the supplied vr mob +/mob/living/carbon/human/proc/enter_vr(var/mob/living/carbon/human/avatar) // Avatar is currently a human, because we have preexisting setup code for appearance manipulation, etc. + if(!istype(avatar)) + return + + // Link the two mobs for client transfer + avatar.vr_holder = src + src.teleop = avatar + src.vr_link = avatar // Can't reuse vr_holder so that death can automatically eject users from VR + + // Move the mind + avatar.Sleeping(1) + src.mind.transfer_to(avatar) + to_chat(avatar, "You have enterred Virtual Reality!\nAll normal gameplay rules still apply.\nWounds you suffer here won't persist when you leave VR, but some of the pain will.\nYou can leave VR at any time by using the \"Exit Virtual Reality\" verb in the Abilities tab, or by ghosting.") //No more prommie VR thing, so removed tidbit about changing appearance + to_chat(avatar, " You black out for a moment, and wake to find yourself in a new body in virtual reality.") // So this is what VR feels like? + +// exit_vr is called on the vr mob, and puts the mind back into the original mob +/mob/living/carbon/human/proc/exit_vr() + set name = "Exit Virtual Reality" + set category = "Abilities" + + if(!vr_holder) + return + if(!mind) + return + + var/total_damage + // Tally human damage + if(ishuman(src)) + var/mob/living/carbon/human/H = src + total_damage = H.getBruteLoss() + H.getFireLoss() + H.getOxyLoss() + H.getToxLoss() + + // Move the mind back to the original mob +// vr_holder.Sleeping(1) + src.mind.transfer_to(vr_holder) + to_chat(vr_holder, "You black out for a moment, and wake to find yourself back in your own body.") + // Two-thirds damage is transferred as agony for /humans + // Getting hurt in VR doesn't damage the physical body, but you still got hurt. + if(ishuman(vr_holder) && total_damage) + var/mob/living/carbon/human/V = vr_holder + V.stun_effect_act(0, total_damage*2/3, null) // 200 damage leaves the user in paincrit for several seconds, agony reaches 0 after around 2m. + to_chat(vr_holder, "Pain from your time in VR lingers.") // 250 damage leaves the user unconscious for several seconds in addition to paincrit + + // Maintain a link with the mob, but don't use teleop + vr_holder.vr_link = src + vr_holder.teleop = null + + if(istype(vr_holder.loc, /obj/machinery/vr_sleeper)) + var/obj/machinery/vr_sleeper/V = vr_holder.loc V.go_out() \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species/virtual_reality/opaque_form.dm b/code/modules/mob/living/carbon/human/species/virtual_reality/opaque_form.dm index 43210407bc1..c9aa8a9a339 100644 --- a/code/modules/mob/living/carbon/human/species/virtual_reality/opaque_form.dm +++ b/code/modules/mob/living/carbon/human/species/virtual_reality/opaque_form.dm @@ -1,181 +1,181 @@ -// Species for the opaque appearance -// Due to sprite construction, they have to have separate limb lists - -/datum/species/shapeshifter/promethean/avatar/human - name = "Virtual Reality Human" - icobase = 'icons/mob/human_races/r_human.dmi' - deform = 'icons/mob/human_races/r_def_human.dmi' - appearance_flags = HAS_HAIR_COLOR | HAS_SKIN_TONE | HAS_LIPS | HAS_UNDERWEAR | HAS_EYE_COLOR - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) - ) - -/datum/species/shapeshifter/promethean/avatar/unathi - name = "Virtual Reality Unathi" - icobase = 'icons/mob/human_races/r_lizard.dmi' - deform = 'icons/mob/human_races/r_def_lizard.dmi' - tail = "sogtail" - tail_animation = 'icons/mob/species/unathi/tail.dmi' - appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest/unathi), - BP_GROIN = list("path" = /obj/item/organ/external/groin/unathi), - BP_HEAD = list("path" = /obj/item/organ/external/head/unathi), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) - ) - -/datum/species/shapeshifter/promethean/avatar/tajaran - name = "Virtual Reality Tajaran" - icobase = 'icons/mob/human_races/r_tajaran.dmi' - deform = 'icons/mob/human_races/r_def_tajaran.dmi' - tail = "tajtail" - tail_animation = 'icons/mob/species/tajaran/tail.dmi' - appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) - ) - -/datum/species/shapeshifter/promethean/avatar/skrell - name = "Virtual Reality Skrell" - icobase = 'icons/mob/human_races/r_skrell.dmi' - deform = 'icons/mob/human_races/r_def_skrell.dmi' - appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head/skrell), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) - ) - -/datum/species/shapeshifter/promethean/avatar/teshari - name = "Virtual Reality Teshari" - icobase = 'icons/mob/human_races/r_teshari.dmi' - deform = 'icons/mob/human_races/r_teshari.dmi' - appearance_flags = HAS_HAIR_COLOR | HAS_SKIN_COLOR | HAS_EYE_COLOR - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head/teshari), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand/teshari), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right/teshari), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot/teshari), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right/teshari) - ) - -/datum/species/shapeshifter/promethean/avatar/diona - name = "Virtual Reality Diona" - icobase = 'icons/mob/human_races/r_diona.dmi' - deform = 'icons/mob/human_races/r_def_plant.dmi' - appearance_flags = 0 - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/diona/chest), - BP_GROIN = list("path" = /obj/item/organ/external/diona/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head/no_eyes/diona), - BP_L_ARM = list("path" = /obj/item/organ/external/diona/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/diona/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/diona/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/diona/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/diona/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/diona/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/diona/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/diona/foot/right) - ) - -/datum/species/shapeshifter/promethean/avatar/monkey - name = "Virtual Reality Monkey" - icobase = 'icons/mob/human_races/monkeys/r_monkey.dmi' - deform = 'icons/mob/human_races/monkeys/r_monkey.dmi' - damage_overlays = 'icons/mob/human_races/masks/dam_monkey.dmi' - damage_mask = 'icons/mob/human_races/masks/dam_mask_monkey.dmi' - blood_mask = 'icons/mob/human_races/masks/blood_monkey.dmi' - fire_icon_state = "monkey" - appearance_flags = 0 - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head/no_eyes), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) - ) - -/datum/species/shapeshifter/promethean/avatar/vox - name = "Virtual Reality Vox" - icobase = 'icons/mob/human_races/r_vox.dmi' - deform = 'icons/mob/human_races/r_def_vox.dmi' - appearance_flags = HAS_EYE_COLOR | HAS_HAIR_COLOR - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head/vox), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) - ) - -/datum/species/shapeshifter/promethean/avatar/skeleton - name = "Virtual Reality Skeleton" - icobase = 'icons/mob/human_races/r_skeleton.dmi' - deform = 'icons/mob/human_races/r_skeleton.dmi' - appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_EYE_COLOR - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) - ) +// Species for the opaque appearance +// Due to sprite construction, they have to have separate limb lists + +/datum/species/shapeshifter/promethean/avatar/human + name = "Virtual Reality Human" + icobase = 'icons/mob/human_races/r_human.dmi' + deform = 'icons/mob/human_races/r_def_human.dmi' + appearance_flags = HAS_HAIR_COLOR | HAS_SKIN_TONE | HAS_LIPS | HAS_UNDERWEAR | HAS_EYE_COLOR + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) + +/datum/species/shapeshifter/promethean/avatar/unathi + name = "Virtual Reality Unathi" + icobase = 'icons/mob/human_races/r_lizard.dmi' + deform = 'icons/mob/human_races/r_def_lizard.dmi' + tail = "sogtail" + tail_animation = 'icons/mob/species/unathi/tail.dmi' + appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest/unathi), + BP_GROIN = list("path" = /obj/item/organ/external/groin/unathi), + BP_HEAD = list("path" = /obj/item/organ/external/head/unathi), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) + +/datum/species/shapeshifter/promethean/avatar/tajaran + name = "Virtual Reality Tajaran" + icobase = 'icons/mob/human_races/r_tajaran.dmi' + deform = 'icons/mob/human_races/r_def_tajaran.dmi' + tail = "tajtail" + tail_animation = 'icons/mob/species/tajaran/tail.dmi' + appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) + +/datum/species/shapeshifter/promethean/avatar/skrell + name = "Virtual Reality Skrell" + icobase = 'icons/mob/human_races/r_skrell.dmi' + deform = 'icons/mob/human_races/r_def_skrell.dmi' + appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head/skrell), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) + +/datum/species/shapeshifter/promethean/avatar/teshari + name = "Virtual Reality Teshari" + icobase = 'icons/mob/human_races/r_teshari.dmi' + deform = 'icons/mob/human_races/r_teshari.dmi' + appearance_flags = HAS_HAIR_COLOR | HAS_SKIN_COLOR | HAS_EYE_COLOR + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head/teshari), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand/teshari), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right/teshari), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot/teshari), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right/teshari) + ) + +/datum/species/shapeshifter/promethean/avatar/diona + name = "Virtual Reality Diona" + icobase = 'icons/mob/human_races/r_diona.dmi' + deform = 'icons/mob/human_races/r_def_plant.dmi' + appearance_flags = 0 + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/diona/chest), + BP_GROIN = list("path" = /obj/item/organ/external/diona/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head/no_eyes/diona), + BP_L_ARM = list("path" = /obj/item/organ/external/diona/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/diona/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/diona/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/diona/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/diona/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/diona/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/diona/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/diona/foot/right) + ) + +/datum/species/shapeshifter/promethean/avatar/monkey + name = "Virtual Reality Monkey" + icobase = 'icons/mob/human_races/monkeys/r_monkey.dmi' + deform = 'icons/mob/human_races/monkeys/r_monkey.dmi' + damage_overlays = 'icons/mob/human_races/masks/dam_monkey.dmi' + damage_mask = 'icons/mob/human_races/masks/dam_mask_monkey.dmi' + blood_mask = 'icons/mob/human_races/masks/blood_monkey.dmi' + fire_icon_state = "monkey" + appearance_flags = 0 + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head/no_eyes), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) + +/datum/species/shapeshifter/promethean/avatar/vox + name = "Virtual Reality Vox" + icobase = 'icons/mob/human_races/r_vox.dmi' + deform = 'icons/mob/human_races/r_def_vox.dmi' + appearance_flags = HAS_EYE_COLOR | HAS_HAIR_COLOR + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head/vox), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) + +/datum/species/shapeshifter/promethean/avatar/skeleton + name = "Virtual Reality Skeleton" + icobase = 'icons/mob/human_races/r_skeleton.dmi' + deform = 'icons/mob/human_races/r_skeleton.dmi' + appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_EYE_COLOR + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) diff --git a/code/modules/mob/living/carbon/human/stripping.dm b/code/modules/mob/living/carbon/human/stripping.dm index 7aa08accbb0..216208d0c61 100644 --- a/code/modules/mob/living/carbon/human/stripping.dm +++ b/code/modules/mob/living/carbon/human/stripping.dm @@ -1,176 +1,176 @@ -/mob/living/carbon/human/proc/handle_strip(var/slot_to_strip,var/mob/living/user) - - if(!slot_to_strip || !istype(user)) - return - - if(user.incapacitated() || !user.Adjacent(src)) - user << browse(null, text("window=mob[src.name]")) - return - - var/obj/item/target_slot = get_equipped_item(text2num(slot_to_strip)) - - switch(slot_to_strip) - // Handle things that are part of this interface but not removing/replacing a given item. - if("pockets") - visible_message("\The [user] is trying to empty \the [src]'s pockets!") - if(do_after(user,HUMAN_STRIP_DELAY,src)) - empty_pockets(user) - return - if("splints") - visible_message("\The [user] is trying to remove \the [src]'s splints!") - if(do_after(user,HUMAN_STRIP_DELAY,src)) - remove_splints(user) - return - if("sensors") - visible_message("\The [user] is trying to set \the [src]'s sensors!") - if(do_after(user,HUMAN_STRIP_DELAY,src)) - toggle_sensors(user) - return - if("internals") - visible_message("\The [usr] is trying to set \the [src]'s internals!") - if(do_after(user,HUMAN_STRIP_DELAY,src)) - toggle_internals(user) - return - if("tie") - var/obj/item/clothing/under/suit = w_uniform - if(!istype(suit) || !LAZYLEN(suit.accessories)) - return - var/obj/item/clothing/accessory/A = suit.accessories[1] - if(!istype(A)) - return - visible_message("\The [usr] is trying to remove \the [src]'s [A.name]!") - - if(!do_after(user,HUMAN_STRIP_DELAY,src)) - return - - if(!A || suit.loc != src || !(A in suit.accessories)) - return - - if(istype(A, /obj/item/clothing/accessory/badge) || istype(A, /obj/item/clothing/accessory/medal)) - user.visible_message("\The [user] tears off \the [A] from [src]'s [suit.name]!") - add_attack_logs(user,src,"Stripped [A.name] off [suit.name]") - A.on_removed(user) - suit.accessories -= A - update_inv_w_uniform() - return - - // Are we placing or stripping? - var/stripping - var/obj/item/held = user.get_active_hand() - if(!istype(held) || is_robot_module(held)) - stripping = TRUE - else - var/obj/item/weapon/holder/holder = held - if(istype(holder) && src == holder.held_mob) - stripping = TRUE - else - var/obj/item/weapon/grab/grab = held - if(istype(grab) && grab.affecting == src) - stripping = TRUE - - if(stripping) - if(!istype(target_slot)) // They aren't holding anything valid and there's nothing to remove, why are we even here? - return - if(!target_slot.canremove) - to_chat(user, "You cannot remove \the [src]'s [target_slot.name].") - return - visible_message("\The [user] is trying to remove \the [src]'s [target_slot.name]!") - else - if(slot_to_strip == slot_wear_mask && istype(held, /obj/item/weapon/grenade)) - visible_message("\The [user] is trying to put \a [held] in \the [src]'s mouth!") - else - visible_message("\The [user] is trying to put \a [held] on \the [src]!") - - if(!do_after(user,HUMAN_STRIP_DELAY,src)) - return - - if(!stripping) - if(user.get_active_hand() != held) - return - var/obj/item/weapon/holder/mobheld = held - if(istype(mobheld)&&mobheld.held_mob==src) - to_chat(user, "You can't put someone on themselves! Stop trying to break reality!") - return - - if(stripping) - add_attack_logs(user,src,"Removed equipment from slot [target_slot]") - unEquip(target_slot) - else if(user.unEquip(held)) - equip_to_slot_if_possible(held, text2num(slot_to_strip), 0, 1, 1) - if(held.loc != src) - user.put_in_hands(held) - -// Empty out everything in the target's pockets. -/mob/living/carbon/human/proc/empty_pockets(var/mob/living/user) - if(!r_store && !l_store) - to_chat(user, "\The [src] has nothing in their pockets.") - return - if(r_store) - unEquip(r_store) - if(l_store) - unEquip(l_store) - visible_message("\The [user] empties \the [src]'s pockets!") - -// Modify the current target sensor level. -/mob/living/carbon/human/proc/toggle_sensors(var/mob/living/user) - var/obj/item/clothing/under/suit = w_uniform - if(!suit) - to_chat(user, "\The [src] is not wearing a suit with sensors.") - return - if (suit.has_sensor >= 2) - to_chat(user, "\The [src]'s suit sensor controls are locked.") - return - add_attack_logs(user,src,"Adjusted suit sensor level") - suit.set_sensors(user) - -// Remove all splints. -/mob/living/carbon/human/proc/remove_splints(var/mob/living/user) - - var/can_reach_splints = 1 - if(istype(wear_suit,/obj/item/clothing/suit/space)) - var/obj/item/clothing/suit/space/suit = wear_suit - if(suit.supporting_limbs && suit.supporting_limbs.len) - to_chat(user, "You cannot remove the splints - [src]'s [suit] is supporting some of the breaks.") - can_reach_splints = 0 - - if(can_reach_splints) - var/removed_splint - for(var/obj/item/organ/external/o in organs) - if (o && o.splinted) - var/obj/item/S = o.splinted - if(istype(S) && S.loc == o) //can only remove splints that are actually worn on the organ (deals with hardsuit splints) - S.add_fingerprint(user) - if(o.remove_splint()) - user.put_in_active_hand(S) - removed_splint = 1 - if(removed_splint) - visible_message("\The [user] removes \the [src]'s splints!") - else - to_chat(user, "\The [src] has no splints to remove.") - -// Set internals on or off. -/mob/living/carbon/human/proc/toggle_internals(var/mob/living/user) - if(internal) - internal.add_fingerprint(user) - internal = null - if(internals) - internals.icon_state = "internal0" - else - // Check for airtight mask/helmet. - if(!(istype(wear_mask, /obj/item/clothing/mask) || istype(head, /obj/item/clothing/head/helmet/space))) - return - // Find an internal source. - if(istype(back, /obj/item/weapon/tank)) - internal = back - else if(istype(s_store, /obj/item/weapon/tank)) - internal = s_store - else if(istype(belt, /obj/item/weapon/tank)) - internal = belt - - if(internal) - visible_message("\The [src] is now running on internals!") - internal.add_fingerprint(user) - if (internals) - internals.icon_state = "internal1" - else - visible_message("\The [user] disables \the [src]'s internals!") +/mob/living/carbon/human/proc/handle_strip(var/slot_to_strip,var/mob/living/user) + + if(!slot_to_strip || !istype(user)) + return + + if(user.incapacitated() || !user.Adjacent(src)) + user << browse(null, text("window=mob[src.name]")) + return + + var/obj/item/target_slot = get_equipped_item(text2num(slot_to_strip)) + + switch(slot_to_strip) + // Handle things that are part of this interface but not removing/replacing a given item. + if("pockets") + visible_message("\The [user] is trying to empty \the [src]'s pockets!") + if(do_after(user,HUMAN_STRIP_DELAY,src)) + empty_pockets(user) + return + if("splints") + visible_message("\The [user] is trying to remove \the [src]'s splints!") + if(do_after(user,HUMAN_STRIP_DELAY,src)) + remove_splints(user) + return + if("sensors") + visible_message("\The [user] is trying to set \the [src]'s sensors!") + if(do_after(user,HUMAN_STRIP_DELAY,src)) + toggle_sensors(user) + return + if("internals") + visible_message("\The [usr] is trying to set \the [src]'s internals!") + if(do_after(user,HUMAN_STRIP_DELAY,src)) + toggle_internals(user) + return + if("tie") + var/obj/item/clothing/under/suit = w_uniform + if(!istype(suit) || !LAZYLEN(suit.accessories)) + return + var/obj/item/clothing/accessory/A = suit.accessories[1] + if(!istype(A)) + return + visible_message("\The [usr] is trying to remove \the [src]'s [A.name]!") + + if(!do_after(user,HUMAN_STRIP_DELAY,src)) + return + + if(!A || suit.loc != src || !(A in suit.accessories)) + return + + if(istype(A, /obj/item/clothing/accessory/badge) || istype(A, /obj/item/clothing/accessory/medal)) + user.visible_message("\The [user] tears off \the [A] from [src]'s [suit.name]!") + add_attack_logs(user,src,"Stripped [A.name] off [suit.name]") + A.on_removed(user) + suit.accessories -= A + update_inv_w_uniform() + return + + // Are we placing or stripping? + var/stripping + var/obj/item/held = user.get_active_hand() + if(!istype(held) || is_robot_module(held)) + stripping = TRUE + else + var/obj/item/weapon/holder/holder = held + if(istype(holder) && src == holder.held_mob) + stripping = TRUE + else + var/obj/item/weapon/grab/grab = held + if(istype(grab) && grab.affecting == src) + stripping = TRUE + + if(stripping) + if(!istype(target_slot)) // They aren't holding anything valid and there's nothing to remove, why are we even here? + return + if(!target_slot.canremove) + to_chat(user, "You cannot remove \the [src]'s [target_slot.name].") + return + visible_message("\The [user] is trying to remove \the [src]'s [target_slot.name]!") + else + if(slot_to_strip == slot_wear_mask && istype(held, /obj/item/weapon/grenade)) + visible_message("\The [user] is trying to put \a [held] in \the [src]'s mouth!") + else + visible_message("\The [user] is trying to put \a [held] on \the [src]!") + + if(!do_after(user,HUMAN_STRIP_DELAY,src)) + return + + if(!stripping) + if(user.get_active_hand() != held) + return + var/obj/item/weapon/holder/mobheld = held + if(istype(mobheld)&&mobheld.held_mob==src) + to_chat(user, "You can't put someone on themselves! Stop trying to break reality!") + return + + if(stripping) + add_attack_logs(user,src,"Removed equipment from slot [target_slot]") + unEquip(target_slot) + else if(user.unEquip(held)) + equip_to_slot_if_possible(held, text2num(slot_to_strip), 0, 1, 1) + if(held.loc != src) + user.put_in_hands(held) + +// Empty out everything in the target's pockets. +/mob/living/carbon/human/proc/empty_pockets(var/mob/living/user) + if(!r_store && !l_store) + to_chat(user, "\The [src] has nothing in their pockets.") + return + if(r_store) + unEquip(r_store) + if(l_store) + unEquip(l_store) + visible_message("\The [user] empties \the [src]'s pockets!") + +// Modify the current target sensor level. +/mob/living/carbon/human/proc/toggle_sensors(var/mob/living/user) + var/obj/item/clothing/under/suit = w_uniform + if(!suit) + to_chat(user, "\The [src] is not wearing a suit with sensors.") + return + if (suit.has_sensor >= 2) + to_chat(user, "\The [src]'s suit sensor controls are locked.") + return + add_attack_logs(user,src,"Adjusted suit sensor level") + suit.set_sensors(user) + +// Remove all splints. +/mob/living/carbon/human/proc/remove_splints(var/mob/living/user) + + var/can_reach_splints = 1 + if(istype(wear_suit,/obj/item/clothing/suit/space)) + var/obj/item/clothing/suit/space/suit = wear_suit + if(suit.supporting_limbs && suit.supporting_limbs.len) + to_chat(user, "You cannot remove the splints - [src]'s [suit] is supporting some of the breaks.") + can_reach_splints = 0 + + if(can_reach_splints) + var/removed_splint + for(var/obj/item/organ/external/o in organs) + if (o && o.splinted) + var/obj/item/S = o.splinted + if(istype(S) && S.loc == o) //can only remove splints that are actually worn on the organ (deals with hardsuit splints) + S.add_fingerprint(user) + if(o.remove_splint()) + user.put_in_active_hand(S) + removed_splint = 1 + if(removed_splint) + visible_message("\The [user] removes \the [src]'s splints!") + else + to_chat(user, "\The [src] has no splints to remove.") + +// Set internals on or off. +/mob/living/carbon/human/proc/toggle_internals(var/mob/living/user) + if(internal) + internal.add_fingerprint(user) + internal = null + if(internals) + internals.icon_state = "internal0" + else + // Check for airtight mask/helmet. + if(!(istype(wear_mask, /obj/item/clothing/mask) || istype(head, /obj/item/clothing/head/helmet/space))) + return + // Find an internal source. + if(istype(back, /obj/item/weapon/tank)) + internal = back + else if(istype(s_store, /obj/item/weapon/tank)) + internal = s_store + else if(istype(belt, /obj/item/weapon/tank)) + internal = belt + + if(internal) + visible_message("\The [src] is now running on internals!") + internal.add_fingerprint(user) + if (internals) + internals.icon_state = "internal1" + else + visible_message("\The [user] disables \the [src]'s internals!") diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 04fe9dd2cf1..5dc3b6e48a8 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -1,1428 +1,1428 @@ -/* - Global associative list for caching humanoid icons. - Index format m or f, followed by a string of 0 and 1 to represent bodyparts followed by husk fat hulk skeleton 1 or 0. -*/ -var/global/list/human_icon_cache = list() //key is incredibly complex, see update_icons_body() -var/global/list/tail_icon_cache = list() //key is [species.race_key][r_skin][g_skin][b_skin] -var/global/list/wing_icon_cache = list() // See tail. -var/global/list/light_overlay_cache = list() //see make_worn_icon() on helmets -var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() - -//////////////////////////////////////////////////////////////////////////////////////////////// -// # Human Icon Updating System -// -// This system takes care of the "icon" for human mobs. Of course humans don't just have a single -// icon+icon_state, but a combination of dozens of little sprites including including the body, -// clothing, equipment, in-universe HUD images, etc. -// -// # Basic Operation -// Whenever you do something that should update the on-mob appearance of a worn or held item, You -// will need to call the relevant update_inv_* proc. All of these are named after the variable they -// update from. They are defined at the /mob level so you don't even need to cast to carbon/human. -// -// The new system leverages SSoverlays to actually add/remove the overlays from mob.overlays -// Since SSoverlays already manages batching updates to reduce apperance churn etc, we don't need -// to worry about that. (In short, you can call add/cut overlay as many times as you want, it will -// only get assigned to the mob once per tick.) -// As a corrolary, this means users of this system do NOT need to tell the system when you're done -// making changes. -// -// There are also these special cases: -// update_icons_body() //Handles updating your mob's icon to reflect their gender/race/complexion etc -// UpdateDamageIcon() //Handles damage overlays for brute/burn damage //(will rename this when I geta round to it) ~Carn -// update_skin() //Handles updating skin for species that have a skin overlay. -// update_bloodied() //Handles adding/clearing the blood overlays for hands & feet. Call when bloodied or cleaned. -// update_underwear() //Handles updating the sprite for underwear. -// update_hair() //Handles updating your hair and eyes overlay -// update_mutations() //Handles updating your appearance for certain mutations. e.g TK head-glows -// update_fire() //Handles overlay from being on fire. -// update_water() //Handles overlay from being submerged. -// update_surgery() //Handles overlays from open external organs. -// -// # History (i.e. I'm used to the old way, what is different?) -// You used to have to call update_icons(FALSE) if you planned to make more changes, and call update_icons(TRUE) -// on the final update. All that is gone, just call update_inv_whatever() and it handles the rest. -// -//////////////////////////////////////////////////////////////////////////////////////////////// - -//Add an entry to overlays, assuming it exists -/mob/living/carbon/human/proc/apply_layer(cache_index) - if((. = overlays_standing[cache_index])) - add_overlay(.) - -//Remove an entry from overlays, and from the list -/mob/living/carbon/human/proc/remove_layer(cache_index) - var/I = overlays_standing[cache_index] - if(I) - cut_overlay(I) - overlays_standing[cache_index] = null - -// These are used as the layers for the icons, as well as indexes in a list that holds onto them. -// Technically the layers used are all -100+layer to make them FLOAT_LAYER overlays. -//Human Overlays Indexes///////// -#define MUTATIONS_LAYER 1 //Mutations like fat, and lasereyes -#define SKIN_LAYER 2 //Skin things added by a call on species -#define BLOOD_LAYER 3 //Bloodied hands/feet/anything else -#define MOB_DAM_LAYER 4 //Injury overlay sprites like open wounds -#define SURGERY_LAYER 5 //Overlays for open surgical sites -#define UNDERWEAR_LAYER 6 //Underwear/bras/etc -#define TAIL_LOWER_LAYER 7 //Tail as viewed from the south -#define WING_LOWER_LAYER 8 //Wings as viewed from the south -#define SHOES_LAYER_ALT 9 //Shoe-slot item (when set to be under uniform via verb) -#define UNIFORM_LAYER 10 //Uniform-slot item -#define ID_LAYER 11 //ID-slot item -#define SHOES_LAYER 12 //Shoe-slot item -#define GLOVES_LAYER 13 //Glove-slot item -#define BELT_LAYER 14 //Belt-slot item -#define SUIT_LAYER 15 //Suit-slot item -#define TAIL_UPPER_LAYER 16 //Some species have tails to render (As viewed from the N, E, or W) -#define GLASSES_LAYER 17 //Eye-slot item -#define BELT_LAYER_ALT 18 //Belt-slot item (when set to be above suit via verb) -#define SUIT_STORE_LAYER 19 //Suit storage-slot item -#define BACK_LAYER 20 //Back-slot item -#define HAIR_LAYER 21 //The human's hair -#define HAIR_ACCESSORY_LAYER 22 //VOREStation edit. Simply move this up a number if things are added. -#define EARS_LAYER 23 //Both ear-slot items (combined image) -#define EYES_LAYER 24 //Mob's eyes (used for glowing eyes) -#define FACEMASK_LAYER 25 //Mask-slot item -#define GLASSES_LAYER_ALT 26 //So some glasses can appear on top of hair and things -#define HEAD_LAYER 27 //Head-slot item -#define HANDCUFF_LAYER 28 //Handcuffs, if the human is handcuffed, in a secret inv slot -#define LEGCUFF_LAYER 29 //Same as handcuffs, for legcuffs -#define L_HAND_LAYER 30 //Left-hand item -#define R_HAND_LAYER 31 //Right-hand item -#define WING_LAYER 32 //Wings or protrusions over the suit. -#define TAIL_UPPER_LAYER_ALT 33 //Modified tail-sprite layer. Tend to be larger. -#define MODIFIER_EFFECTS_LAYER 34 //Effects drawn by modifiers -#define FIRE_LAYER 35 //'Mob on fire' overlay layer -#define MOB_WATER_LAYER 36 //'Mob submerged' overlay layer -#define TARGETED_LAYER 37 //'Aimed at' overlay layer -#define TOTAL_LAYERS 37 //VOREStation edit. <---- KEEP THIS UPDATED, should always equal the highest number here, used to initialize a list. -////////////////////////////////// - -/mob/living/carbon/human - var/list/overlays_standing[TOTAL_LAYERS] - var/previous_damage_appearance // store what the body last looked like, so we only have to update it if something changed - -//UPDATES OVERLAYS FROM OVERLAYS_LYING/OVERLAYS_STANDING -//I'll work on removing that stuff by rewriting some of the cloaking stuff at a later date. -/mob/living/carbon/human/update_icons() - if(QDESTROYING(src)) - return - - stack_trace("CANARY: Old human update_icons was called.") - - update_hud() //TODO: remove the need for this - - //Do any species specific layering updates, such as when hiding. - update_icon_special() - -/mob/living/carbon/human/update_transform(var/instant = FALSE) - /* VOREStation Edit START - // First, get the correct size. - var/desired_scale_x = icon_scale_x - var/desired_scale_y = icon_scale_y - - desired_scale_x *= species.icon_scale_x - desired_scale_y *= species.icon_scale_y - - for(var/datum/modifier/M in modifiers) - if(!isnull(M.icon_scale_x_percent)) - desired_scale_x *= M.icon_scale_x_percent - if(!isnull(M.icon_scale_y_percent)) - desired_scale_y *= M.icon_scale_y_percent - */ - var/desired_scale_x = size_multiplier * icon_scale_x - var/desired_scale_y = size_multiplier * icon_scale_y - desired_scale_x *= species.icon_scale_x - desired_scale_y *= species.icon_scale_y - var/cent_offset = species.center_offset - if(fuzzy || offset_override || dir == EAST || dir == WEST) - cent_offset = 0 - vis_height = species.icon_height - appearance_flags |= PIXEL_SCALE - if(fuzzy) - appearance_flags &= ~PIXEL_SCALE - //VOREStation Edit End - - // Regular stuff again. - var/matrix/M = matrix() - var/anim_time = 3 - - //Due to some involuntary means, you're laying now - if(lying && !resting && !sleeping) - anim_time = 1 //Thud - - if(lying && !species.prone_icon) //Only rotate them if we're not drawing a specific icon for being prone. - if(tail_style?.can_loaf && resting) // Only call these if we're resting? - update_tail_showing() - M.Scale(desired_scale_x, desired_scale_y) - else - var/randn = rand(1, 2) - if(randn <= 1) // randomly choose a rotation - M.Turn(-90) - else - M.Turn(90) - if(species.icon_height == 64) - M.Translate(13,-22) - else - M.Translate(1,-6) - M.Scale(desired_scale_y, desired_scale_x) - M.Translate(cent_offset * desired_scale_x, (vis_height/2)*(desired_scale_y-1)) - layer = MOB_LAYER -0.01 // Fix for a byond bug where turf entry order no longer matters - else - M.Scale(desired_scale_x, desired_scale_y)//VOREStation Edit - M.Translate(cent_offset * desired_scale_x, (vis_height/2)*(desired_scale_y-1)) - if(tail_style?.can_loaf) // VOREStation Edit: Taur Loafing - update_tail_showing() // VOREStation Edit: Taur Loafing - layer = MOB_LAYER // Fix for a byond bug where turf entry order no longer matters - - if(instant) - transform = M - else - animate(src, transform = M, time = anim_time) - update_icon_special() //May contain transform-altering things - -//DAMAGE OVERLAYS -//constructs damage icon for each organ from mask * damage field and saves it in our overlays_ lists -/mob/living/carbon/human/UpdateDamageIcon() - if(QDESTROYING(src)) - return - - remove_layer(MOB_DAM_LAYER) - - // first check whether something actually changed about damage appearance - var/damage_appearance = "" - - for(var/obj/item/organ/external/O in organs) - if(isnull(O) || O.is_stump()) - continue - damage_appearance += O.damage_state - - if(damage_appearance == previous_damage_appearance) - // nothing to do here - return - - previous_damage_appearance = damage_appearance - - var/image/standing_image = image(icon = species.damage_overlays, icon_state = "00", layer = BODY_LAYER+MOB_DAM_LAYER) - - // blend the individual damage states with our icons - for(var/obj/item/organ/external/O in organs) - if(isnull(O) || O.is_stump() || O.is_hidden_by_sprite_accessory()) - continue - - O.update_icon() - if(O.damage_state == "00") continue - var/icon/DI - var/cache_index = "[O.damage_state]/[O.icon_name]/[species.get_blood_colour(src)]/[species.get_bodytype(src)]" - if(damage_icon_parts[cache_index] == null) - DI = icon(species.get_damage_overlays(src), O.damage_state) // the damage icon for whole human - DI.Blend(icon(species.get_damage_mask(src), O.icon_name), ICON_MULTIPLY) // mask with this organ's pixels - DI.Blend(species.get_blood_colour(src), ICON_MULTIPLY) - damage_icon_parts[cache_index] = DI - else - DI = damage_icon_parts[cache_index] - - standing_image.add_overlay(DI) - - overlays_standing[MOB_DAM_LAYER] = standing_image - apply_layer(MOB_DAM_LAYER) - -//BASE MOB SPRITE -/mob/living/carbon/human/update_icons_body() - if(QDESTROYING(src)) - return - - var/husk_color_mod = rgb(96,88,80) - var/hulk_color_mod = rgb(48,224,40) - - var/husk = (HUSK in src.mutations) - var/fat = (FAT in src.mutations) - var/hulk = (HULK in src.mutations) - var/skeleton = (SKELETON in src.mutations) - - robolimb_count = 0 //TODO, here, really tho? - robobody_count = 0 - - //CACHING: Generate an index key from visible bodyparts. - //0 = destroyed, 1 = normal, 2 = robotic, 3 = necrotic. - - //Create a new, blank icon for our mob to use. - var/icon/stand_icon = new(species.icon_template ? species.icon_template : 'icons/mob/human.dmi', icon_state = "blank") - - var/g = (gender == MALE ? "male" : "female") - var/icon_key = "[species.get_race_key(src)][g][s_tone][r_skin][g_skin][b_skin]" - if(lip_style) - icon_key += "[lip_style]" - else - icon_key += "nolips" - var/obj/item/organ/internal/eyes/eyes = internal_organs_by_name[O_EYES] - if(eyes) - icon_key += "[rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3])]" - else - icon_key += "[r_eyes], [g_eyes], [b_eyes]" - var/obj/item/organ/external/head/head = organs_by_name[BP_HEAD] - if(head) - if(!istype(head, /obj/item/organ/external/stump)) - if (species.selects_bodytype != SELECTS_BODYTYPE_FALSE) - var/headtype = GLOB.all_species[species.base_species]?.has_limbs[BP_HEAD] - var/obj/item/organ/external/head/headtypepath = headtype["path"] - if (headtypepath && !head.eye_icon_override) - head.eye_icon = initial(headtypepath.eye_icon) - head.eye_icon_location = initial(headtypepath.eye_icon_location) - icon_key += "[head.eye_icon]" - var/wholeicontransparent = TRUE - for(var/organ_tag in species.has_limbs) - var/obj/item/organ/external/part = organs_by_name[organ_tag] - if(isnull(part) || part.is_stump() || part.is_hidden_by_sprite_accessory()) //VOREStation Edit allowing tails to prevent bodyparts rendering, granting more spriter freedom for taur/digitigrade stuff. - icon_key += "0" - continue - if(part) - wholeicontransparent &&= part.transparent //VORESTATION EDIT: transparent instead of nonsolid - icon_key += "[part.species.get_race_key(part.owner)]" - icon_key += "[part.dna.GetUIState(DNA_UI_GENDER)]" - icon_key += "[part.s_tone]" - if(part.s_col && part.s_col.len >= 3) - icon_key += "[rgb(part.s_col[1],part.s_col[2],part.s_col[3])]" - if(part.body_hair && part.h_col && part.h_col.len >= 3) - icon_key += "[rgb(part.h_col[1],part.h_col[2],part.h_col[3])]" - if(species.color_mult) - icon_key += "[ICON_MULTIPLY]" - else - icon_key += "[ICON_ADD]" - else - icon_key += "#000000" - - for(var/M in part.markings) - if (part.markings[M]["on"]) - icon_key += "[M][part.markings[M]["color"]]" - - // VOREStation Edit Start - if(part.nail_polish) - icon_key += "_[part.nail_polish.icon]_[part.nail_polish.icon_state]_[part.nail_polish.color]" - // VOREStation Edit End - - if(part.robotic >= ORGAN_ROBOT) - icon_key += "2[part.model ? "-[part.model]": ""]" - robolimb_count++ - if((part.robotic == ORGAN_ROBOT || part.robotic == ORGAN_LIFELIKE) && (part.organ_tag == BP_HEAD || part.organ_tag == BP_TORSO || part.organ_tag == BP_GROIN)) - robobody_count ++ - else if(part.status & ORGAN_DEAD) - icon_key += "3" - else - icon_key += "1" - if(part.transparent) //VOREStation Edit. For better slime limbs. Avoids using solid var due to limb dropping. - icon_key += "_t" //VOREStation Edit. - - if(istype(tail_style, /datum/sprite_accessory/tail/taur)) - if(tail_style.clip_mask) //VOREStation Edit. - icon_key += tail_style.clip_mask_state - - if(digitigrade && (part.organ_tag == BP_R_LEG || part.organ_tag == BP_L_LEG || part.organ_tag == BP_R_FOOT || part.organ_tag == BP_L_FOOT)) - icon_key += "_digi" - - if(tail_style) - pixel_x = tail_style.mob_offset_x - pixel_y = tail_style.mob_offset_y - default_pixel_x = tail_style.mob_offset_x - default_pixel_y = tail_style.mob_offset_y - - icon_key = "[icon_key][husk ? 1 : 0][fat ? 1 : 0][hulk ? 1 : 0][skeleton ? 1 : 0]" - var/icon/base_icon - if(human_icon_cache[icon_key]) - base_icon = human_icon_cache[icon_key] - else - //BEGIN CACHED ICON GENERATION. - var/obj/item/organ/external/chest = get_organ(BP_TORSO) - base_icon = chest.get_icon(skeleton, !wholeicontransparent) - - var/apply_extra_transparency_leg = organs_by_name[BP_L_LEG] && organs_by_name[BP_R_LEG] - var/apply_extra_transparency_foot = organs_by_name[BP_L_FOOT] && organs_by_name[BP_R_FOOT] - - var/icon/Cutter = null - var/icon_x_offset = 0 - var/icon_y_offset = 0 - - if(istype(tail_style, /datum/sprite_accessory/tail/taur)) // Tail icon 'cookie cutters' are filled in where icons are preserved. We need to invert that. - if(tail_style.clip_mask) //VOREStation Edit. - Cutter = new(icon = (tail_style.clip_mask_icon ? tail_style.clip_mask_icon : tail_style.icon), icon_state = tail_style.clip_mask_state) - - Cutter.Blend("#000000", ICON_MULTIPLY) // Make it all black. - - Cutter.SwapColor("#00000000", "#FFFFFFFF") // Everywhere empty, make white. - Cutter.SwapColor("#000000FF", "#00000000") // Everywhere black, make empty. - - Cutter.Blend("#000000", ICON_MULTIPLY) // Black again. - - icon_x_offset = tail_style.offset_x - icon_y_offset = tail_style.offset_y - - for(var/obj/item/organ/external/part in organs) - if(isnull(part) || part.is_stump() || part == chest || part.is_hidden_by_sprite_accessory()) //VOREStation Edit allowing tails to prevent bodyparts rendering, granting more spriter freedom for taur/digitigrade stuff. - continue - var/icon/temp = part.get_icon(skeleton, !wholeicontransparent) - - if((part.organ_tag in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) && Cutter) - temp.Blend(Cutter, ICON_AND, x = icon_x_offset, y = icon_y_offset) - - //That part makes left and right legs drawn topmost and lowermost when human looks WEST or EAST - //And no change in rendering for other parts (they icon_position is 0, so goes to 'else' part) - if(part.icon_position & (LEFT | RIGHT)) - var/icon/temp2 = new(species.icon_template ? species.icon_template : 'icons/mob/human.dmi', icon_state = "blank") - temp2.Insert(new/icon(temp,dir=NORTH),dir=NORTH) - temp2.Insert(new/icon(temp,dir=SOUTH),dir=SOUTH) - if(!(part.icon_position & LEFT)) - temp2.Insert(new/icon(temp,dir=EAST),dir=EAST) - if(!(part.icon_position & RIGHT)) - temp2.Insert(new/icon(temp,dir=WEST),dir=WEST) - base_icon.Blend(temp2, ICON_OVERLAY) - temp2.Insert(temp2,"blank",dir=NORTH) //faaaaairly certain this is more efficient than reloading temp2, doing this so we don't blend the icons twice (it matters more in transparent limbs) - temp2.Insert(temp2,"blank",dir=SOUTH) - temp2.Insert(temp2,"blank",dir=EAST) - temp2.Insert(temp2,"blank",dir=WEST) - if(part.icon_position & LEFT) - temp2.Insert(new/icon(temp,dir=EAST),dir=EAST) - if(part.icon_position & RIGHT) - temp2.Insert(new/icon(temp,dir=WEST),dir=WEST) - if (part.transparent && !wholeicontransparent) //apply a little (a lot) extra transparency to make it look better //VORESTATION EDIT: transparent instead of nonsolid - if ((istype(part, /obj/item/organ/external/leg) && apply_extra_transparency_leg) || (istype(part, /obj/item/organ/external/foot) && apply_extra_transparency_foot)) //maybe - temp2 += rgb(,,,30) - base_icon.Blend(temp2, ICON_UNDERLAY) - else if(part.icon_position & UNDER) - base_icon.Blend(temp, ICON_UNDERLAY) - else - base_icon.Blend(temp, ICON_OVERLAY) - - if (wholeicontransparent) //because, I mean. It's basically never gonna happen that you'll have just one non-transparent limb but if you do your icon will look meh. Still good but meh, will have some areas with higher transparencies unless you're literally just a torso and a head - base_icon += rgb(,,,180) - - if(!skeleton) - if(husk) - base_icon.ColorTone(husk_color_mod) - else if(hulk) - var/list/tone = rgb2num(hulk_color_mod) - base_icon.MapColors(rgb(tone[1],0,0),rgb(0,tone[2],0),rgb(0,0,tone[3])) - - //Handle husk overlay. - if(husk && ("overlay_husk" in cached_icon_states(species.icobase))) - var/icon/mask = new(base_icon) - var/icon/husk_over = new(species.icobase,"overlay_husk") - mask.MapColors(0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0) - husk_over.Blend(mask, ICON_ADD) - base_icon.Blend(husk_over, ICON_OVERLAY) - - human_icon_cache[icon_key] = base_icon - - //END CACHED ICON GENERATION. - stand_icon.Blend(base_icon,ICON_OVERLAY) - icon = stand_icon - - //tail - update_tail_showing() - update_wing_showing() - -/mob/living/carbon/human/proc/update_skin() - if(QDESTROYING(src)) - return - - remove_layer(SKIN_LAYER) - - var/image/skin = species.update_skin(src) - if(skin) - skin.layer = BODY_LAYER+SKIN_LAYER - overlays_standing[SKIN_LAYER] = skin - apply_layer(SKIN_LAYER) - -/mob/living/carbon/human/proc/update_bloodied() - if(QDESTROYING(src)) - return - - remove_layer(BLOOD_LAYER) - if(!blood_DNA && !feet_blood_DNA) - return - - var/image/both = image(icon = 'icons/effects/effects.dmi', icon_state = "nothing", layer = BODY_LAYER+BLOOD_LAYER) - - //Bloody hands - if(blood_DNA) - var/image/bloodsies = image(icon = species.get_blood_mask(src), icon_state = "bloodyhands", layer = BODY_LAYER+BLOOD_LAYER) - bloodsies.color = hand_blood_color - both.add_overlay(bloodsies) - - //Bloody feet - if(feet_blood_DNA) - var/image/bloodsies = image(icon = species.get_blood_mask(src), icon_state = "shoeblood", layer = BODY_LAYER+BLOOD_LAYER) - bloodsies.color = feet_blood_color - both.add_overlay(bloodsies) - - overlays_standing[BLOOD_LAYER] = both - - apply_layer(BLOOD_LAYER) - -//UNDERWEAR OVERLAY -/mob/living/carbon/human/proc/update_underwear() - if(QDESTROYING(src)) - return - - remove_layer(UNDERWEAR_LAYER) - - if(species.appearance_flags & HAS_UNDERWEAR) - overlays_standing[UNDERWEAR_LAYER] = list() - for(var/category in all_underwear) - if(hide_underwear[category]) - continue - var/datum/category_item/underwear/UWI = all_underwear[category] - var/image/wear = UWI.generate_image(all_underwear_metadata[category], layer = BODY_LAYER+UNDERWEAR_LAYER) - overlays_standing[UNDERWEAR_LAYER] += wear - - apply_layer(UNDERWEAR_LAYER) - -//HAIR OVERLAY -/mob/living/carbon/human/proc/update_hair() - if(QDESTROYING(src)) - return - - //Reset our hair - remove_layer(HAIR_LAYER) - remove_layer(HAIR_ACCESSORY_LAYER) //VOREStation Edit - update_eyes() //Pirated out of here, for glowing eyes. - - var/obj/item/organ/external/head/head_organ = get_organ(BP_HEAD) - if(!head_organ || head_organ.is_stump() ) - return - - //masks and helmets can obscure our hair. - if( (head && (head.flags_inv & BLOCKHAIR)) || (wear_mask && (wear_mask.flags_inv & BLOCKHAIR))) - return - - //base icons - var/icon/face_standing = icon(icon = 'icons/mob/human_face.dmi', icon_state = "bald_s") - - if(f_style) - var/datum/sprite_accessory/facial_hair_style = facial_hair_styles_list[f_style] - if(facial_hair_style && facial_hair_style.species_allowed && (src.species.get_bodytype(src) in facial_hair_style.species_allowed)) - var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") - if(facial_hair_style.do_colouration) - facial_s.Blend(rgb(r_facial, g_facial, b_facial), facial_hair_style.color_blend_mode) - - face_standing.Blend(facial_s, ICON_OVERLAY) - - if(h_style) - var/datum/sprite_accessory/hair/hair_style = hair_styles_list[h_style] - if(head && (head.flags_inv & BLOCKHEADHAIR)) - if(!(hair_style.flags & HAIR_VERY_SHORT)) - hair_style = hair_styles_list["Short Hair"] - - if(hair_style && (src.species.get_bodytype(src) in hair_style.species_allowed)) - var/icon/grad_s = null - var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") - var/icon/hair_s_add = new/icon("icon" = hair_style.icon_add, "icon_state" = "[hair_style.icon_state]_s") - if(hair_style.do_colouration) - if(grad_style) - grad_s = new/icon("icon" = 'icons/mob/hair_gradients.dmi', "icon_state" = GLOB.hair_gradients[grad_style]) - grad_s.Blend(hair_s, ICON_AND) - grad_s.Blend(rgb(r_grad, g_grad, b_grad), ICON_MULTIPLY) - hair_s.Blend(rgb(r_hair, g_hair, b_hair), ICON_MULTIPLY) - hair_s.Blend(hair_s_add, ICON_ADD) - if(!isnull(grad_s)) - hair_s.Blend(grad_s, ICON_OVERLAY) - - face_standing.Blend(hair_s, ICON_OVERLAY) - - var/icon/ears_s = get_ears_overlay() - - if(head_organ.transparent) //VORESTATION EDIT: transparent instead of nonsolid - face_standing += rgb(,,,120) - if (ears_s) - ears_s += rgb(,,,180) - - var/image/em_block_ears - if(ears_s) - if(ears_s.Height() > face_standing.Height()) // Tol ears - face_standing.Crop(1, 1, face_standing.Width(), ears_s.Height()) - face_standing.Blend(ears_s, ICON_OVERLAY) - if(ear_style?.em_block) - em_block_ears = em_block_image_generic(image(ears_s)) - - var/image/semifinal = image(face_standing, layer = BODY_LAYER+HAIR_LAYER, "pixel_y" = head_organ.head_offset) - if(em_block_ears) - semifinal.overlays += em_block_ears // Leaving this as overlays += - - overlays_standing[HAIR_LAYER] = semifinal - apply_layer(HAIR_LAYER) - //return //VOREStation Edit - - // VOREStation Edit - START - var/icon/hair_acc_s = get_hair_accessory_overlay() - var/image/hair_acc_s_image = null - if(hair_acc_s) - if(hair_accessory_style.ignores_lighting) - hair_acc_s_image = image(hair_acc_s) - hair_acc_s_image.plane = PLANE_LIGHTING_ABOVE - hair_acc_s_image.appearance_flags = appearance_flags - if (hair_acc_s_image) - overlays_standing[HAIR_ACCESSORY_LAYER] = hair_acc_s_image - apply_layer(HAIR_ACCESSORY_LAYER) - return - overlays_standing[HAIR_ACCESSORY_LAYER] = image(hair_acc_s, layer = BODY_LAYER+HAIR_ACCESSORY_LAYER) - apply_layer(HAIR_ACCESSORY_LAYER) - // VOREStation Edit - END - -/mob/living/carbon/human/update_eyes() - if(QDESTROYING(src)) - return - - //Reset our eyes - remove_layer(EYES_LAYER) - - //TODO: Probably redo this. I know I wrote it, but... - - //This is ONLY for glowing eyes for now. Boring flat eyes are done by the head's own proc. - if(!species.has_glowing_eyes) - return - - //Our glowy eyes should be hidden if some equipment hides them. - if(!should_have_organ(O_EYES) || (head && (head.flags_inv & BLOCKHAIR)) || (wear_mask && (wear_mask.flags_inv & BLOCKHAIR))) - return - - //Get the head, we'll need it later. - var/obj/item/organ/external/head/head_organ = get_organ(BP_HEAD) - if(!head_organ || head_organ.is_stump() ) - return - - //The eyes store the color themselves, funny enough. - var/obj/item/organ/internal/eyes/eyes = internal_organs_by_name[O_EYES] - if(!head_organ.eye_icon) - return - - var/icon/eyes_icon = new/icon(head_organ.eye_icon_location, head_organ.eye_icon) //VOREStation Edit - if(eyes) - eyes_icon.Blend(rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3]), ICON_ADD) - else - eyes_icon.Blend(rgb(128,0,0), ICON_ADD) - - // Convert to emissive at some point - if (head_organ.transparent) //VOREStation Edit: transparent instead of nonsolid - eyes_icon += rgb(,,,180) - - var/image/eyes_image = image(eyes_icon) - eyes_image.plane = PLANE_LIGHTING_ABOVE - eyes_image.appearance_flags = appearance_flags - - overlays_standing[EYES_LAYER] = eyes_image - apply_layer(EYES_LAYER) - -/mob/living/carbon/human/update_mutations() - if(QDESTROYING(src)) - return - - remove_layer(MUTATIONS_LAYER) - - if(!LAZYLEN(mutations)) - return //No mutations, no icons. - - //TODO: THIS PROC??? - var/fat - if(FAT in mutations) - fat = "fat" - - var/image/standing = image(icon = 'icons/effects/genetics.dmi', layer = BODY_LAYER+MUTATIONS_LAYER) - var/g = gender == FEMALE ? "f" : "m" - - for(var/datum/dna/gene/gene in dna_genes) - if(!gene.block) - continue - if(gene.is_active(src)) - var/underlay = gene.OnDrawUnderlays(src,g,fat) - if(underlay) - standing.underlays += underlay - - for(var/mut in mutations) - if(mut == LASER) - standing.overlays += "lasereyes_s" // Leaving this as overlays += - - overlays_standing[MUTATIONS_LAYER] = standing - apply_layer(MUTATIONS_LAYER) - -/* --------------------------------------- */ -//Recomputes every icon on the mob. Expensive. -//Useful if the species changed, or there's some -//other drastic body-shape change, but otherwise avoid. -/mob/living/carbon/human/regenerate_icons() - ..() - if(transforming || QDELETED(src)) - return - - update_icons_body() - UpdateDamageIcon() - update_mutations() - update_skin() - update_underwear() - update_hair() - update_inv_w_uniform() - update_inv_wear_id() - update_inv_gloves() - update_inv_glasses() - update_inv_ears() - update_inv_shoes() - update_inv_s_store() - update_inv_wear_mask() - update_inv_head() - update_inv_belt() - update_inv_back() - update_inv_wear_suit() - update_inv_r_hand() - update_inv_l_hand() - update_inv_handcuffed() - update_inv_legcuffed() - //update_inv_pockets() //Doesn't do anything - update_fire() - update_water() - update_surgery() - -/* --------------------------------------- */ -//vvvvvv UPDATE_INV PROCS vvvvvv - -/mob/living/carbon/human/update_inv_w_uniform() - if(QDESTROYING(src)) - return - - remove_layer(UNIFORM_LAYER) - - //Shoes can be affected by uniform being drawn onto them - update_inv_shoes() - - if(!w_uniform) - return - - if(wear_suit && (wear_suit.flags_inv & HIDEJUMPSUIT) && !istype(wear_suit, /obj/item/clothing/suit/space/rig)) - return //Wearing a suit that prevents uniform rendering - - var/obj/item/clothing/under/under = w_uniform - - var/uniform_sprite - if(istype(under) && !isnull(under.update_icon_define)) - uniform_sprite = under.update_icon_define - else - uniform_sprite = INV_W_UNIFORM_DEF_ICON - - //Build a uniform sprite - var/icon/c_mask = tail_style?.clip_mask - if(c_mask) - var/obj/item/clothing/suit/S = wear_suit - if((wear_suit?.flags_inv & HIDETAIL) || (istype(S) && S.taurized)) // Reasons to not mask: 1. If you're wearing a suit that hides the tail or if you're wearing a taurized suit. - c_mask = null - overlays_standing[UNIFORM_LAYER] = w_uniform.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_w_uniform_str, default_icon = uniform_sprite, default_layer = UNIFORM_LAYER, clip_mask = c_mask) - apply_layer(UNIFORM_LAYER) - -/mob/living/carbon/human/update_inv_wear_id() - if(QDESTROYING(src)) - return - - remove_layer(ID_LAYER) - - if(!wear_id) - return //Not wearing an ID - - //Only draw the ID on the mob if the uniform allows for it - if(w_uniform && istype(w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = w_uniform - if(U.displays_id) - overlays_standing[ID_LAYER] = wear_id.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_id_str, default_icon = INV_WEAR_ID_DEF_ICON, default_layer = ID_LAYER) - - apply_layer(ID_LAYER) - -/mob/living/carbon/human/update_inv_gloves() - if(QDESTROYING(src)) - return - - remove_layer(GLOVES_LAYER) - - if(!gloves) - return //No gloves, no reason to be here. - - overlays_standing[GLOVES_LAYER] = gloves.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_gloves_str, default_icon = INV_GLOVES_DEF_ICON, default_layer = GLOVES_LAYER) - - apply_layer(GLOVES_LAYER) - -/mob/living/carbon/human/update_inv_glasses() - if(QDESTROYING(src)) - return - - remove_layer(GLASSES_LAYER) - remove_layer(GLASSES_LAYER_ALT) - - if(!glasses) - return //Not wearing glasses, no need to update anything. - - var/glasses_layer = GLASSES_LAYER - if(istype(glasses, /obj/item/clothing/glasses)) - var/obj/item/clothing/glasses/our_glasses = glasses - if(our_glasses.glasses_layer_above) - glasses_layer = GLASSES_LAYER_ALT - - overlays_standing[glasses_layer] = glasses.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_gloves_str, default_icon = INV_EYES_DEF_ICON, default_layer = glasses_layer) - - apply_layer(glasses_layer) - -/mob/living/carbon/human/update_inv_ears() - if(QDESTROYING(src)) - return - - remove_layer(EARS_LAYER) - - if((head && head.flags_inv & (BLOCKHAIR | BLOCKHEADHAIR)) || (wear_mask && wear_mask.flags_inv & (BLOCKHAIR | BLOCKHEADHAIR))) - return //Ears are blocked (by hair being blocked, overloaded) - - if(!l_ear && !r_ear) - return //Why bother, if no ear sprites - - // Blank image upon which to layer left & right overlays. - var/image/both = image(icon = 'icons/effects/effects.dmi', icon_state = "nothing", layer = BODY_LAYER+EARS_LAYER) - - if(l_ear) - var/image/standing = l_ear.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_l_ear_str, default_icon = INV_EARS_DEF_ICON, default_layer = EARS_LAYER) - both.add_overlay(standing) - - if(r_ear) - var/image/standing = r_ear.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_r_ear_str, default_icon = INV_EARS_DEF_ICON, default_layer = EARS_LAYER) - both.add_overlay(standing) - - overlays_standing[EARS_LAYER] = both - apply_layer(EARS_LAYER) - -/mob/living/carbon/human/update_inv_shoes() - if(QDESTROYING(src)) - return - - remove_layer(SHOES_LAYER) - remove_layer(SHOES_LAYER_ALT) //Dumb alternate layer for shoes being under the uniform. - - if(!shoes || (wear_suit && wear_suit.flags_inv & HIDESHOES) || (w_uniform && w_uniform.flags_inv & HIDESHOES)) - return //Either nothing to draw, or it'd be hidden. - - for(var/f in list(BP_L_FOOT, BP_R_FOOT)) - var/obj/item/organ/external/foot/foot = get_organ(f) - if(istype(foot) && foot.is_hidden_by_sprite_accessory(clothing_only = TRUE)) //If either foot is hidden by the tail, don't render footwear. - return - - var/obj/item/clothing/shoes/shoe = shoes - var/shoe_sprite - - if(istype(shoe) && !isnull(shoe.update_icon_define)) - shoe_sprite = shoe.update_icon_define - else - shoe_sprite = INV_FEET_DEF_ICON - - //Allow for shoe layer toggle nonsense - var/shoe_layer = SHOES_LAYER - if(istype(shoes, /obj/item/clothing/shoes)) - var/obj/item/clothing/shoes/ushoes = shoes - if(ushoes.shoes_under_pants == 1) - shoe_layer = SHOES_LAYER_ALT - - //NB: the use of a var for the layer on this one - overlays_standing[shoe_layer] = shoes.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_shoes_str, default_icon = shoe_sprite, default_layer = shoe_layer) - - apply_layer(SHOES_LAYER) - apply_layer(SHOES_LAYER_ALT) - -/mob/living/carbon/human/update_inv_s_store() - if(QDESTROYING(src)) - return - - remove_layer(SUIT_STORE_LAYER) - - if(!s_store) - return //Why bother, nothing there. - - //TODO, this is unlike the rest of the things - //Basically has no variety in slot icon choices at all. WHY SPECIES ONLY?? - var/t_state = s_store.item_state - if(!t_state) - t_state = s_store.icon_state - overlays_standing[SUIT_STORE_LAYER] = image(icon = species.suit_storage_icon, icon_state = t_state, layer = BODY_LAYER+SUIT_STORE_LAYER) - - apply_layer(SUIT_STORE_LAYER) - -/mob/living/carbon/human/update_inv_head() - if(QDESTROYING(src)) - return - - remove_layer(HEAD_LAYER) - - if(!head) - return //No head item, why bother. - - overlays_standing[HEAD_LAYER] = head.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_head_str, default_icon = INV_HEAD_DEF_ICON, default_layer = HEAD_LAYER) - - apply_layer(HEAD_LAYER) - -/mob/living/carbon/human/update_inv_belt() - if(QDESTROYING(src)) - return - - remove_layer(BELT_LAYER) - remove_layer(BELT_LAYER_ALT) //Because you can toggle belt layer with a verb - - if(!belt) - return //No belt, why bother. - - //Toggle for belt layering with uniform - var/belt_layer = BELT_LAYER - if(istype(belt, /obj/item/weapon/storage/belt)) - var/obj/item/weapon/storage/belt/ubelt = belt - if(ubelt.show_above_suit) - belt_layer = BELT_LAYER_ALT - - - var/icon/c_mask = tail_style?.clip_mask - - //NB: this uses a var from above - overlays_standing[belt_layer] = belt.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_belt_str, default_icon = INV_BELT_DEF_ICON, default_layer = belt_layer, clip_mask = c_mask) - - apply_layer(belt_layer) - -/mob/living/carbon/human/update_inv_wear_suit() - if(QDESTROYING(src)) - return - - remove_layer(SUIT_LAYER) - - //Hide/show other layers if necessary - update_inv_w_uniform() - update_inv_shoes() - update_tail_showing() - update_wing_showing() - - if(!wear_suit) - return //No point, no suit. - - var/obj/item/clothing/suit/suit = wear_suit - var/suit_sprite - - if(istype(suit) && !isnull(suit.update_icon_define)) - suit_sprite = suit.update_icon_define - else - suit_sprite = INV_SUIT_DEF_ICON - - var/icon/c_mask = null - var/tail_is_rendered = (overlays_standing[TAIL_UPPER_LAYER] || overlays_standing[TAIL_UPPER_LAYER_ALT] || overlays_standing[TAIL_LOWER_LAYER]) - var/valid_clip_mask = tail_style?.clip_mask - - if(tail_is_rendered && valid_clip_mask && !(istype(suit) && suit.taurized)) //Clip the lower half of the suit off using the tail's clip mask for taurs since taur bodies aren't hidden. - c_mask = valid_clip_mask - overlays_standing[SUIT_LAYER] = wear_suit.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_suit_str, default_icon = suit_sprite, default_layer = SUIT_LAYER, clip_mask = c_mask) - - apply_layer(SUIT_LAYER) - -/mob/living/carbon/human/update_inv_pockets() - stack_trace("Someone called update_inv_pockets even though it's dumb") - -/mob/living/carbon/human/update_inv_wear_mask() - if(QDESTROYING(src)) - return - - remove_layer(FACEMASK_LAYER) - - if(!wear_mask || (head && head.flags_inv & HIDEMASK)) - return //Why bother, nothing in mask slot. - - overlays_standing[FACEMASK_LAYER] = wear_mask.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_mask_str, default_icon = INV_MASK_DEF_ICON, default_layer = FACEMASK_LAYER) - - apply_layer(FACEMASK_LAYER) - -/mob/living/carbon/human/update_inv_back() - if(QDESTROYING(src)) - return - - remove_layer(BACK_LAYER) - - if(!back) - return //Why do anything - - var/icon/c_mask = tail_style?.clip_mask - if(c_mask) - if(istype(back, /obj/item/weapon/storage/backpack/saddlebag) || istype(back, /obj/item/weapon/storage/backpack/saddlebag_common)) - c_mask = null - - overlays_standing[BACK_LAYER] = back.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_back_str, default_icon = INV_BACK_DEF_ICON, default_layer = BACK_LAYER, clip_mask = c_mask) - - apply_layer(BACK_LAYER) - -//TODO: Carbon procs in my human update_icons?? -/mob/living/carbon/human/update_hud() //TODO: do away with this if possible - if(QDESTROYING(src)) - return - - if(client) - client.screen |= contents - if(hud_used) - hud_used.hidden_inventory_update() //Updates the screenloc of the items on the 'other' inventory bar - -//update whether handcuffs appears on our hud. -/mob/living/carbon/proc/update_hud_handcuffed() - if(QDESTROYING(src)) - return - - if(hud_used && hud_used.l_hand_hud_object && hud_used.r_hand_hud_object) - hud_used.l_hand_hud_object.update_icon() - hud_used.r_hand_hud_object.update_icon() - -/mob/living/carbon/human/update_inv_handcuffed() - if(QDESTROYING(src)) - return - - remove_layer(HANDCUFF_LAYER) - update_hud_handcuffed() //TODO - - if(!handcuffed) - return //Not cuffed, why bother - - overlays_standing[HANDCUFF_LAYER] = handcuffed.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_handcuffed_str, default_icon = INV_HCUFF_DEF_ICON, default_layer = HANDCUFF_LAYER) - - apply_layer(HANDCUFF_LAYER) - -/mob/living/carbon/human/update_inv_legcuffed() - if(QDESTROYING(src)) - return - - clear_alert("legcuffed") - remove_layer(LEGCUFF_LAYER) - - if(!legcuffed) - return //Not legcuffed, why bother. - - throw_alert("legcuffed", /obj/screen/alert/restrained/legcuffed, new_master = legcuffed) - - overlays_standing[LEGCUFF_LAYER] = legcuffed.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_legcuffed_str, default_icon = INV_LCUFF_DEF_ICON, default_layer = LEGCUFF_LAYER) - - apply_layer(LEGCUFF_LAYER) - -/mob/living/carbon/human/update_inv_r_hand() - if(QDESTROYING(src)) - return - - remove_layer(R_HAND_LAYER) - - if(!r_hand) - return //No hand, no bother. - - overlays_standing[R_HAND_LAYER] = r_hand.make_worn_icon(body_type = species.get_bodytype(src), inhands = TRUE, slot_name = slot_r_hand_str, default_icon = INV_R_HAND_DEF_ICON, default_layer = R_HAND_LAYER) - - apply_layer(R_HAND_LAYER) - -/mob/living/carbon/human/update_inv_l_hand() - if(QDESTROYING(src)) - return - - remove_layer(L_HAND_LAYER) - - if(!l_hand) - return //No hand, no bother. - - overlays_standing[L_HAND_LAYER] = l_hand.make_worn_icon(body_type = species.get_bodytype(src), inhands = TRUE, slot_name = slot_l_hand_str, default_icon = INV_L_HAND_DEF_ICON, default_layer = L_HAND_LAYER) - - apply_layer(L_HAND_LAYER) - -/mob/living/carbon/human/proc/get_tail_layer() - var/list/lower_layer_dirs = list(SOUTH) - if(tail_style) - lower_layer_dirs = tail_style.lower_layer_dirs.Copy() - - if(dir in lower_layer_dirs) - return TAIL_LOWER_LAYER - else - return TAIL_UPPER_LAYER - -/mob/living/carbon/human/proc/update_tail_showing() - if(QDESTROYING(src)) - return - - remove_layer(TAIL_UPPER_LAYER) - remove_layer(TAIL_UPPER_LAYER_ALT) - remove_layer(TAIL_LOWER_LAYER) - - var/tail_layer = get_tail_layer() - if(src.tail_style && src.tail_style.clip_mask_state) - tail_layer = TAIL_UPPER_LAYER // Use default, let clip mask handle everything - if(tail_alt && tail_layer == TAIL_UPPER_LAYER) - tail_layer = TAIL_UPPER_LAYER_ALT - - var/obj/item/organ/external/chest = organs_by_name[BP_TORSO] - - var/image/tail_image = get_tail_image() - if(tail_image) - tail_image.layer = BODY_LAYER+tail_layer - tail_image.alpha = chest?.transparent ? 180 : 255 //VORESTATION EDIT: transparent instead of nonsolid - overlays_standing[tail_layer] = tail_image - apply_layer(tail_layer) - return - - var/species_tail = species.get_tail(src) // Species tail icon_state prefix. - - //This one is actually not that bad I guess. - if(species_tail && !(wear_suit && wear_suit.flags_inv & HIDETAIL)) - var/icon/tail_s = get_tail_icon() - tail_image = image(icon = tail_s, icon_state = "[species_tail]_s", layer = BODY_LAYER+tail_layer) - tail_image.alpha = chest?.transparent ? 180 : 255 //VORESTATION EDIT: transparent instead of nonsolid - overlays_standing[tail_layer] = tail_image - animate_tail_reset() - -//TODO: Is this the appropriate place for this, and not on species...? -/mob/living/carbon/human/proc/get_tail_icon() - var/icon_key = "[species.get_race_key(src)][r_skin][g_skin][b_skin][r_hair][g_hair][b_hair]" - var/icon/tail_icon = tail_icon_cache[icon_key] - if(!tail_icon) - //generate a new one - var/species_tail_anim = species.get_tail_animation(src) - if(!species_tail_anim && species.icobase_tail) species_tail_anim = species.icobase //Allow override of file for non-animated tails - if(!species_tail_anim) species_tail_anim = 'icons/effects/species.dmi' - tail_icon = new/icon(species_tail_anim) - tail_icon.Blend(rgb(r_skin, g_skin, b_skin), species.color_mult ? ICON_MULTIPLY : ICON_ADD) - // The following will not work with animated tails. - var/use_species_tail = species.get_tail_hair(src) - if(use_species_tail) - var/icon/hair_icon = icon('icons/effects/species.dmi', "[species.get_tail(src)]_[use_species_tail]") - hair_icon.Blend(rgb(r_hair, g_hair, b_hair), species.color_mult ? ICON_MULTIPLY : ICON_ADD) //Check for species color_mult - tail_icon.Blend(hair_icon, ICON_OVERLAY) - tail_icon_cache[icon_key] = tail_icon - - return tail_icon - -/mob/living/carbon/human/proc/set_tail_state(var/t_state) - var/tail_layer = get_tail_layer() - if(src.tail_style && src.tail_style.clip_mask_state) - tail_layer = TAIL_UPPER_LAYER // Use default, let clip mask handle everything - if(tail_alt && tail_layer == TAIL_UPPER_LAYER) - tail_layer = TAIL_UPPER_LAYER_ALT - var/image/tail_overlay = overlays_standing[tail_layer] - - remove_layer(TAIL_UPPER_LAYER) - remove_layer(TAIL_UPPER_LAYER_ALT) - remove_layer(TAIL_LOWER_LAYER) - - if(tail_overlay) - overlays_standing[tail_layer] = tail_overlay - if(species.get_tail_animation(src)) - tail_overlay.icon_state = t_state - . = tail_overlay - - apply_layer(tail_layer) - -//Not really once, since BYOND can't do that. -//Update this if the ability to flick() images or make looping animation start at the first frame is ever added. -//You can sort of flick images now with flick_overlay -Aro -/mob/living/carbon/human/proc/animate_tail_once() - if(QDESTROYING(src)) - return - - var/t_state = "[species.get_tail(src)]_once" - var/tail_layer = get_tail_layer() - if(src.tail_style && src.tail_style.clip_mask_state) - tail_layer = TAIL_UPPER_LAYER // Use default, let clip mask handle everything - - var/image/tail_overlay = overlays_standing[tail_layer] - if(tail_overlay && tail_overlay.icon_state == t_state) - return //let the existing animation finish - - tail_overlay = set_tail_state(t_state) // Calls remove_layer & apply_layer - if(tail_overlay) - spawn(20) - //check that the animation hasn't changed in the meantime - if(overlays_standing[tail_layer] == tail_overlay && tail_overlay.icon_state == t_state) - animate_tail_stop() - -/mob/living/carbon/human/proc/animate_tail_start() - if(QDESTROYING(src)) - return - - set_tail_state("[species.get_tail(src)]_slow[rand(0,9)]") - -/mob/living/carbon/human/proc/animate_tail_fast() - if(QDESTROYING(src)) - return - - set_tail_state("[species.get_tail(src)]_loop[rand(0,9)]") - -/mob/living/carbon/human/proc/animate_tail_reset() - if(QDESTROYING(src)) - return - - if(stat != DEAD) - set_tail_state("[species.get_tail(src)]_idle[rand(0,9)]") - else - set_tail_state("[species.get_tail(src)]_static") - toggle_tail(FALSE) //So tails stop when someone dies. TODO - Fix this hack ~Leshana - -/mob/living/carbon/human/proc/animate_tail_stop() - if(QDESTROYING(src)) - return - - set_tail_state("[species.get_tail(src)]_static") - -/mob/living/carbon/human/proc/update_wing_showing() - if(QDESTROYING(src)) - return - - remove_layer(WING_LAYER) - remove_layer(WING_LOWER_LAYER) - - var/image/wing_image = get_wing_image(FALSE) - - var/obj/item/organ/external/chest = organs_by_name[BP_TORSO] - - if(wing_image) - wing_image.layer = BODY_LAYER+WING_LAYER - wing_image.alpha = chest?.transparent ? 180 : 255 //VORESTATION EDIT: transparent instead of nonsolid - overlays_standing[WING_LAYER] = wing_image - if(wing_style && wing_style.multi_dir) - wing_image = get_wing_image(TRUE) - if(wing_image) - wing_image.layer = BODY_LAYER+WING_LOWER_LAYER - overlays_standing[WING_LOWER_LAYER] = wing_image - - apply_layer(WING_LAYER) - apply_layer(WING_LOWER_LAYER) - -/mob/living/carbon/human/update_modifier_visuals() - if(QDESTROYING(src)) - return - - remove_layer(MODIFIER_EFFECTS_LAYER) - - if(!LAZYLEN(modifiers)) - return //No modifiers, no effects. - - var/image/effects = new() - for(var/datum/modifier/M in modifiers) - if(M.mob_overlay_state) - if(M.icon_override) //VOREStation Edit. Override for the modifer icon. - var/image/I = image(icon = 'icons/mob/modifier_effects_vr.dmi', icon_state = M.mob_overlay_state) - I.color = M.effect_color - effects.overlays += I // Leaving this as overlays += - else - var/image/I = image(icon = 'icons/mob/modifier_effects.dmi', icon_state = M.mob_overlay_state) - I.color = M.effect_color - effects.overlays += I // Leaving this as overlays += - - overlays_standing[MODIFIER_EFFECTS_LAYER] = effects - - apply_layer(MODIFIER_EFFECTS_LAYER) - -/mob/living/carbon/human/update_fire() - if(QDESTROYING(src)) - return - - remove_layer(FIRE_LAYER) - - if(!on_fire) - return - - overlays_standing[FIRE_LAYER] = image(icon = 'icons/mob/OnFire.dmi', icon_state = get_fire_icon_state(), layer = BODY_LAYER+FIRE_LAYER) - - apply_layer(FIRE_LAYER) - -/mob/living/carbon/human/update_water() - if(QDESTROYING(src)) - return - - remove_layer(MOB_WATER_LAYER) - - var/depth = check_submerged() - if(!depth || lying) - return - - var/atom/A = loc // We'd better be swimming and on a turf - var/image/I = image(icon = 'icons/mob/submerged.dmi', icon_state = "human_swimming_[depth]", layer = BODY_LAYER+MOB_WATER_LAYER) //TODO: Improve - I.color = A.color - overlays_standing[MOB_WATER_LAYER] = I - - apply_layer(MOB_WATER_LAYER) - -/mob/living/carbon/human/proc/update_surgery() - if(QDESTROYING(src)) - return - - remove_layer(SURGERY_LAYER) - - var/image/total = new - for(var/obj/item/organ/external/E in organs) - if(E.open) - var/image/I = image(icon = 'icons/mob/surgery.dmi', icon_state = "[E.icon_name][round(E.open)]", layer = BODY_LAYER+SURGERY_LAYER) - total.overlays += I // Leaving this as overlays += - - if(total.overlays.len) - overlays_standing[SURGERY_LAYER] = total - apply_layer(SURGERY_LAYER) - -/mob/living/carbon/human/proc/get_wing_image(var/under_layer) - if(QDESTROYING(src)) - return - - //If you are FBP with wing style and didn't set a custom one - if(synthetic && synthetic.includes_wing && !wing_style && !wings_hidden) //VOREStation Edit - var/icon/wing_s = new/icon("icon" = synthetic.icon, "icon_state" = "wing") //I dunno. If synths have some custom wing? - wing_s.Blend(rgb(src.r_skin, src.g_skin, src.b_skin), species.color_mult ? ICON_MULTIPLY : ICON_ADD) - var/image/working = image(wing_s) - working.overlays += em_block_image_generic(working) // Leaving this as overlays += - return working - - //If you have custom wings selected - if(wing_style && !(wear_suit && wear_suit.flags_inv & HIDETAIL) && !wings_hidden) //VOREStation Edit - var/wing_state = (flapping && wing_style.ani_state) ? wing_style.ani_state : wing_style.icon_state - if(wing_style.multi_dir) - wing_state += "_[under_layer ? "back" : "front"]" - var/icon/wing_s = new/icon("icon" = wing_style.icon, "icon_state" = wing_state) - if(wing_style.do_colouration) - wing_s.Blend(rgb(src.r_wing, src.g_wing, src.b_wing), wing_style.color_blend_mode) - if(wing_style.extra_overlay) - var/icon/overlay = new/icon("icon" = wing_style.icon, "icon_state" = wing_style.extra_overlay) - overlay.Blend(rgb(src.r_wing2, src.g_wing2, src.b_wing2), wing_style.color_blend_mode) - wing_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - if(wing_style.extra_overlay2) - var/icon/overlay = new/icon("icon" = wing_style.icon, "icon_state" = wing_style.extra_overlay2) - if(wing_style.ani_state) - overlay = new/icon("icon" = wing_style.icon, "icon_state" = wing_style.extra_overlay2_w) - overlay.Blend(rgb(src.r_wing3, src.g_wing3, src.b_wing3), wing_style.color_blend_mode) - wing_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - else - overlay.Blend(rgb(src.r_wing3, src.g_wing3, src.b_wing3), wing_style.color_blend_mode) - wing_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - var/image/working = image(wing_s) - if(wing_style.em_block) - working.overlays += em_block_image_generic(working) // Leaving this as overlays += - working.pixel_x -= wing_style.wing_offset - return working - -/mob/living/carbon/human/proc/get_ears_overlay() - //If you are FBP with ear style and didn't set a custom one - var/datum/robolimb/model = isSynthetic() - if(istype(model) && model.includes_ears && !ear_style) - var/icon/ears_s = new/icon("icon" = synthetic.icon, "icon_state" = "ears") - ears_s.Blend(rgb(src.r_ears, src.g_ears, src.b_ears), species.color_mult ? ICON_MULTIPLY : ICON_ADD) - return ears_s - - if(ear_style && !(head && (head.flags_inv & BLOCKHEADHAIR))) - var/icon/ears_s = new/icon("icon" = ear_style.icon, "icon_state" = ear_style.icon_state) - if(ear_style.do_colouration) - ears_s.Blend(rgb(src.r_ears, src.g_ears, src.b_ears), ear_style.color_blend_mode) - if(ear_style.extra_overlay) - var/icon/overlay = new/icon("icon" = ear_style.icon, "icon_state" = ear_style.extra_overlay) - overlay.Blend(rgb(src.r_ears2, src.g_ears2, src.b_ears2), ear_style.color_blend_mode) - ears_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - if(ear_style.extra_overlay2) //MORE COLOURS IS BETTERER - var/icon/overlay = new/icon("icon" = ear_style.icon, "icon_state" = ear_style.extra_overlay2) - overlay.Blend(rgb(src.r_ears3, src.g_ears3, src.b_ears3), ear_style.color_blend_mode) - ears_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - return ears_s - return null - - -/mob/living/carbon/human/proc/get_tail_image() - //If you are FBP with tail style and didn't set a custom one - var/datum/robolimb/model = isSynthetic() - if(istype(model) && model.includes_tail && !tail_style && !tail_hidden) - var/icon/tail_s = new/icon("icon" = synthetic.icon, "icon_state" = "tail") - tail_s.Blend(rgb(src.r_skin, src.g_skin, src.b_skin), species.color_mult ? ICON_MULTIPLY : ICON_ADD) - return image(tail_s) - - //If you have a custom tail selected - if(tail_style && !(wear_suit && wear_suit.flags_inv & HIDETAIL && !istaurtail(tail_style)) && !tail_hidden) - var/icon/tail_s = new/icon("icon" = (tail_style.can_loaf && resting) ? tail_style.icon_loaf : tail_style.icon, "icon_state" = (wagging && tail_style.ani_state ? tail_style.ani_state : tail_style.icon_state)) //CHOMPEdit - if(tail_style.can_loaf && !is_shifted) - pixel_y = (resting) ? -tail_style.loaf_offset*size_multiplier : default_pixel_y //move player down, then taur up, to fit the overlays correctly // VOREStation Edit: Taur Loafing - if(tail_style.do_colouration) - tail_s.Blend(rgb(src.r_tail, src.g_tail, src.b_tail), tail_style.color_blend_mode) - if(tail_style.extra_overlay) - var/icon/overlay = new/icon("icon" = (tail_style?.can_loaf && resting) ? tail_style.icon_loaf : tail_style.icon, "icon_state" = tail_style.extra_overlay) //CHOMPEdit - if(wagging && tail_style.ani_state) - overlay = new/icon("icon" = tail_style.icon, "icon_state" = tail_style.extra_overlay_w) - overlay.Blend(rgb(src.r_tail2, src.g_tail2, src.b_tail2), tail_style.color_blend_mode) - tail_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - else - overlay.Blend(rgb(src.r_tail2, src.g_tail2, src.b_tail2), tail_style.color_blend_mode) - tail_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - - if(tail_style.extra_overlay2) - var/icon/overlay = new/icon("icon" = (tail_style?.can_loaf && resting) ? tail_style.icon_loaf : tail_style.icon, "icon_state" = tail_style.extra_overlay2) //CHOMPEdit - if(wagging && tail_style.ani_state) - overlay = new/icon("icon" = tail_style.icon, "icon_state" = tail_style.extra_overlay2_w) - overlay.Blend(rgb(src.r_tail3, src.g_tail3, src.b_tail3), tail_style.color_blend_mode) - tail_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - else - overlay.Blend(rgb(src.r_tail3, src.g_tail3, src.b_tail3), tail_style.color_blend_mode) - tail_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - - var/image/working = image(tail_s) - if(tail_style.em_block) - working.overlays += em_block_image_generic(working) // Leaving this as overlays += - - if(istaurtail(tail_style)) - var/datum/sprite_accessory/tail/taur/taurtype = tail_style - working.pixel_x = tail_style.offset_x - working.pixel_y = tail_style.offset_y - if(taurtype.can_ride && !riding_datum) - riding_datum = new /datum/riding/taur(src) - verbs |= /mob/living/carbon/human/proc/taur_mount - verbs |= /mob/living/proc/toggle_rider_reins - else if(islongtail(tail_style)) - working.pixel_x = tail_style.offset_x - working.pixel_y = tail_style.offset_y - return working - return null - -// TODO - Move this to where it should go ~Leshana -/mob/living/proc/stop_flying() - if(QDESTROYING(src)) - return - flying = FALSE - return 1 - -/mob/living/carbon/human/stop_flying() - if((. = ..())) - update_wing_showing() - -//Human Overlays Indexes///////// -#undef MUTATIONS_LAYER -#undef SKIN_LAYER -#undef MOB_DAM_LAYER -#undef SURGERY_LAYER -#undef UNDERWEAR_LAYER -#undef SHOES_LAYER_ALT -#undef UNIFORM_LAYER -#undef ID_LAYER -#undef SHOES_LAYER -#undef GLOVES_LAYER -#undef BELT_LAYER -#undef SUIT_LAYER -#undef TAIL_UPPER_LAYER -#undef TAIL_LOWER_LAYER -#undef GLASSES_LAYER -#undef BELT_LAYER_ALT -#undef SUIT_STORE_LAYER -#undef BACK_LAYER -#undef HAIR_LAYER -#undef EARS_LAYER -#undef EYES_LAYER -#undef FACEMASK_LAYER -#undef HEAD_LAYER -#undef HANDCUFF_LAYER -#undef LEGCUFF_LAYER -#undef L_HAND_LAYER -#undef R_HAND_LAYER -#undef MODIFIER_EFFECTS_LAYER -#undef FIRE_LAYER -#undef WATER_LAYER -#undef TARGETED_LAYER -#undef TOTAL_LAYERS +/* + Global associative list for caching humanoid icons. + Index format m or f, followed by a string of 0 and 1 to represent bodyparts followed by husk fat hulk skeleton 1 or 0. +*/ +var/global/list/human_icon_cache = list() //key is incredibly complex, see update_icons_body() +var/global/list/tail_icon_cache = list() //key is [species.race_key][r_skin][g_skin][b_skin] +var/global/list/wing_icon_cache = list() // See tail. +var/global/list/light_overlay_cache = list() //see make_worn_icon() on helmets +var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() + +//////////////////////////////////////////////////////////////////////////////////////////////// +// # Human Icon Updating System +// +// This system takes care of the "icon" for human mobs. Of course humans don't just have a single +// icon+icon_state, but a combination of dozens of little sprites including including the body, +// clothing, equipment, in-universe HUD images, etc. +// +// # Basic Operation +// Whenever you do something that should update the on-mob appearance of a worn or held item, You +// will need to call the relevant update_inv_* proc. All of these are named after the variable they +// update from. They are defined at the /mob level so you don't even need to cast to carbon/human. +// +// The new system leverages SSoverlays to actually add/remove the overlays from mob.overlays +// Since SSoverlays already manages batching updates to reduce apperance churn etc, we don't need +// to worry about that. (In short, you can call add/cut overlay as many times as you want, it will +// only get assigned to the mob once per tick.) +// As a corrolary, this means users of this system do NOT need to tell the system when you're done +// making changes. +// +// There are also these special cases: +// update_icons_body() //Handles updating your mob's icon to reflect their gender/race/complexion etc +// UpdateDamageIcon() //Handles damage overlays for brute/burn damage //(will rename this when I geta round to it) ~Carn +// update_skin() //Handles updating skin for species that have a skin overlay. +// update_bloodied() //Handles adding/clearing the blood overlays for hands & feet. Call when bloodied or cleaned. +// update_underwear() //Handles updating the sprite for underwear. +// update_hair() //Handles updating your hair and eyes overlay +// update_mutations() //Handles updating your appearance for certain mutations. e.g TK head-glows +// update_fire() //Handles overlay from being on fire. +// update_water() //Handles overlay from being submerged. +// update_surgery() //Handles overlays from open external organs. +// +// # History (i.e. I'm used to the old way, what is different?) +// You used to have to call update_icons(FALSE) if you planned to make more changes, and call update_icons(TRUE) +// on the final update. All that is gone, just call update_inv_whatever() and it handles the rest. +// +//////////////////////////////////////////////////////////////////////////////////////////////// + +//Add an entry to overlays, assuming it exists +/mob/living/carbon/human/proc/apply_layer(cache_index) + if((. = overlays_standing[cache_index])) + add_overlay(.) + +//Remove an entry from overlays, and from the list +/mob/living/carbon/human/proc/remove_layer(cache_index) + var/I = overlays_standing[cache_index] + if(I) + cut_overlay(I) + overlays_standing[cache_index] = null + +// These are used as the layers for the icons, as well as indexes in a list that holds onto them. +// Technically the layers used are all -100+layer to make them FLOAT_LAYER overlays. +//Human Overlays Indexes///////// +#define MUTATIONS_LAYER 1 //Mutations like fat, and lasereyes +#define SKIN_LAYER 2 //Skin things added by a call on species +#define BLOOD_LAYER 3 //Bloodied hands/feet/anything else +#define MOB_DAM_LAYER 4 //Injury overlay sprites like open wounds +#define SURGERY_LAYER 5 //Overlays for open surgical sites +#define UNDERWEAR_LAYER 6 //Underwear/bras/etc +#define TAIL_LOWER_LAYER 7 //Tail as viewed from the south +#define WING_LOWER_LAYER 8 //Wings as viewed from the south +#define SHOES_LAYER_ALT 9 //Shoe-slot item (when set to be under uniform via verb) +#define UNIFORM_LAYER 10 //Uniform-slot item +#define ID_LAYER 11 //ID-slot item +#define SHOES_LAYER 12 //Shoe-slot item +#define GLOVES_LAYER 13 //Glove-slot item +#define BELT_LAYER 14 //Belt-slot item +#define SUIT_LAYER 15 //Suit-slot item +#define TAIL_UPPER_LAYER 16 //Some species have tails to render (As viewed from the N, E, or W) +#define GLASSES_LAYER 17 //Eye-slot item +#define BELT_LAYER_ALT 18 //Belt-slot item (when set to be above suit via verb) +#define SUIT_STORE_LAYER 19 //Suit storage-slot item +#define BACK_LAYER 20 //Back-slot item +#define HAIR_LAYER 21 //The human's hair +#define HAIR_ACCESSORY_LAYER 22 //VOREStation edit. Simply move this up a number if things are added. +#define EARS_LAYER 23 //Both ear-slot items (combined image) +#define EYES_LAYER 24 //Mob's eyes (used for glowing eyes) +#define FACEMASK_LAYER 25 //Mask-slot item +#define GLASSES_LAYER_ALT 26 //So some glasses can appear on top of hair and things +#define HEAD_LAYER 27 //Head-slot item +#define HANDCUFF_LAYER 28 //Handcuffs, if the human is handcuffed, in a secret inv slot +#define LEGCUFF_LAYER 29 //Same as handcuffs, for legcuffs +#define L_HAND_LAYER 30 //Left-hand item +#define R_HAND_LAYER 31 //Right-hand item +#define WING_LAYER 32 //Wings or protrusions over the suit. +#define TAIL_UPPER_LAYER_ALT 33 //Modified tail-sprite layer. Tend to be larger. +#define MODIFIER_EFFECTS_LAYER 34 //Effects drawn by modifiers +#define FIRE_LAYER 35 //'Mob on fire' overlay layer +#define MOB_WATER_LAYER 36 //'Mob submerged' overlay layer +#define TARGETED_LAYER 37 //'Aimed at' overlay layer +#define TOTAL_LAYERS 37 //VOREStation edit. <---- KEEP THIS UPDATED, should always equal the highest number here, used to initialize a list. +////////////////////////////////// + +/mob/living/carbon/human + var/list/overlays_standing[TOTAL_LAYERS] + var/previous_damage_appearance // store what the body last looked like, so we only have to update it if something changed + +//UPDATES OVERLAYS FROM OVERLAYS_LYING/OVERLAYS_STANDING +//I'll work on removing that stuff by rewriting some of the cloaking stuff at a later date. +/mob/living/carbon/human/update_icons() + if(QDESTROYING(src)) + return + + stack_trace("CANARY: Old human update_icons was called.") + + update_hud() //TODO: remove the need for this + + //Do any species specific layering updates, such as when hiding. + update_icon_special() + +/mob/living/carbon/human/update_transform(var/instant = FALSE) + /* VOREStation Edit START + // First, get the correct size. + var/desired_scale_x = icon_scale_x + var/desired_scale_y = icon_scale_y + + desired_scale_x *= species.icon_scale_x + desired_scale_y *= species.icon_scale_y + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.icon_scale_x_percent)) + desired_scale_x *= M.icon_scale_x_percent + if(!isnull(M.icon_scale_y_percent)) + desired_scale_y *= M.icon_scale_y_percent + */ + var/desired_scale_x = size_multiplier * icon_scale_x + var/desired_scale_y = size_multiplier * icon_scale_y + desired_scale_x *= species.icon_scale_x + desired_scale_y *= species.icon_scale_y + var/cent_offset = species.center_offset + if(fuzzy || offset_override || dir == EAST || dir == WEST) + cent_offset = 0 + vis_height = species.icon_height + appearance_flags |= PIXEL_SCALE + if(fuzzy) + appearance_flags &= ~PIXEL_SCALE + //VOREStation Edit End + + // Regular stuff again. + var/matrix/M = matrix() + var/anim_time = 3 + + //Due to some involuntary means, you're laying now + if(lying && !resting && !sleeping) + anim_time = 1 //Thud + + if(lying && !species.prone_icon) //Only rotate them if we're not drawing a specific icon for being prone. + if(tail_style?.can_loaf && resting) // Only call these if we're resting? + update_tail_showing() + M.Scale(desired_scale_x, desired_scale_y) + else + var/randn = rand(1, 2) + if(randn <= 1) // randomly choose a rotation + M.Turn(-90) + else + M.Turn(90) + if(species.icon_height == 64) + M.Translate(13,-22) + else + M.Translate(1,-6) + M.Scale(desired_scale_y, desired_scale_x) + M.Translate(cent_offset * desired_scale_x, (vis_height/2)*(desired_scale_y-1)) + layer = MOB_LAYER -0.01 // Fix for a byond bug where turf entry order no longer matters + else + M.Scale(desired_scale_x, desired_scale_y)//VOREStation Edit + M.Translate(cent_offset * desired_scale_x, (vis_height/2)*(desired_scale_y-1)) + if(tail_style?.can_loaf) // VOREStation Edit: Taur Loafing + update_tail_showing() // VOREStation Edit: Taur Loafing + layer = MOB_LAYER // Fix for a byond bug where turf entry order no longer matters + + if(instant) + transform = M + else + animate(src, transform = M, time = anim_time) + update_icon_special() //May contain transform-altering things + +//DAMAGE OVERLAYS +//constructs damage icon for each organ from mask * damage field and saves it in our overlays_ lists +/mob/living/carbon/human/UpdateDamageIcon() + if(QDESTROYING(src)) + return + + remove_layer(MOB_DAM_LAYER) + + // first check whether something actually changed about damage appearance + var/damage_appearance = "" + + for(var/obj/item/organ/external/O in organs) + if(isnull(O) || O.is_stump()) + continue + damage_appearance += O.damage_state + + if(damage_appearance == previous_damage_appearance) + // nothing to do here + return + + previous_damage_appearance = damage_appearance + + var/image/standing_image = image(icon = species.damage_overlays, icon_state = "00", layer = BODY_LAYER+MOB_DAM_LAYER) + + // blend the individual damage states with our icons + for(var/obj/item/organ/external/O in organs) + if(isnull(O) || O.is_stump() || O.is_hidden_by_sprite_accessory()) + continue + + O.update_icon() + if(O.damage_state == "00") continue + var/icon/DI + var/cache_index = "[O.damage_state]/[O.icon_name]/[species.get_blood_colour(src)]/[species.get_bodytype(src)]" + if(damage_icon_parts[cache_index] == null) + DI = icon(species.get_damage_overlays(src), O.damage_state) // the damage icon for whole human + DI.Blend(icon(species.get_damage_mask(src), O.icon_name), ICON_MULTIPLY) // mask with this organ's pixels + DI.Blend(species.get_blood_colour(src), ICON_MULTIPLY) + damage_icon_parts[cache_index] = DI + else + DI = damage_icon_parts[cache_index] + + standing_image.add_overlay(DI) + + overlays_standing[MOB_DAM_LAYER] = standing_image + apply_layer(MOB_DAM_LAYER) + +//BASE MOB SPRITE +/mob/living/carbon/human/update_icons_body() + if(QDESTROYING(src)) + return + + var/husk_color_mod = rgb(96,88,80) + var/hulk_color_mod = rgb(48,224,40) + + var/husk = (HUSK in src.mutations) + var/fat = (FAT in src.mutations) + var/hulk = (HULK in src.mutations) + var/skeleton = (SKELETON in src.mutations) + + robolimb_count = 0 //TODO, here, really tho? + robobody_count = 0 + + //CACHING: Generate an index key from visible bodyparts. + //0 = destroyed, 1 = normal, 2 = robotic, 3 = necrotic. + + //Create a new, blank icon for our mob to use. + var/icon/stand_icon = new(species.icon_template ? species.icon_template : 'icons/mob/human.dmi', icon_state = "blank") + + var/g = (gender == MALE ? "male" : "female") + var/icon_key = "[species.get_race_key(src)][g][s_tone][r_skin][g_skin][b_skin]" + if(lip_style) + icon_key += "[lip_style]" + else + icon_key += "nolips" + var/obj/item/organ/internal/eyes/eyes = internal_organs_by_name[O_EYES] + if(eyes) + icon_key += "[rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3])]" + else + icon_key += "[r_eyes], [g_eyes], [b_eyes]" + var/obj/item/organ/external/head/head = organs_by_name[BP_HEAD] + if(head) + if(!istype(head, /obj/item/organ/external/stump)) + if (species.selects_bodytype != SELECTS_BODYTYPE_FALSE) + var/headtype = GLOB.all_species[species.base_species]?.has_limbs[BP_HEAD] + var/obj/item/organ/external/head/headtypepath = headtype["path"] + if (headtypepath && !head.eye_icon_override) + head.eye_icon = initial(headtypepath.eye_icon) + head.eye_icon_location = initial(headtypepath.eye_icon_location) + icon_key += "[head.eye_icon]" + var/wholeicontransparent = TRUE + for(var/organ_tag in species.has_limbs) + var/obj/item/organ/external/part = organs_by_name[organ_tag] + if(isnull(part) || part.is_stump() || part.is_hidden_by_sprite_accessory()) //VOREStation Edit allowing tails to prevent bodyparts rendering, granting more spriter freedom for taur/digitigrade stuff. + icon_key += "0" + continue + if(part) + wholeicontransparent &&= part.transparent //VORESTATION EDIT: transparent instead of nonsolid + icon_key += "[part.species.get_race_key(part.owner)]" + icon_key += "[part.dna.GetUIState(DNA_UI_GENDER)]" + icon_key += "[part.s_tone]" + if(part.s_col && part.s_col.len >= 3) + icon_key += "[rgb(part.s_col[1],part.s_col[2],part.s_col[3])]" + if(part.body_hair && part.h_col && part.h_col.len >= 3) + icon_key += "[rgb(part.h_col[1],part.h_col[2],part.h_col[3])]" + if(species.color_mult) + icon_key += "[ICON_MULTIPLY]" + else + icon_key += "[ICON_ADD]" + else + icon_key += "#000000" + + for(var/M in part.markings) + if (part.markings[M]["on"]) + icon_key += "[M][part.markings[M]["color"]]" + + // VOREStation Edit Start + if(part.nail_polish) + icon_key += "_[part.nail_polish.icon]_[part.nail_polish.icon_state]_[part.nail_polish.color]" + // VOREStation Edit End + + if(part.robotic >= ORGAN_ROBOT) + icon_key += "2[part.model ? "-[part.model]": ""]" + robolimb_count++ + if((part.robotic == ORGAN_ROBOT || part.robotic == ORGAN_LIFELIKE) && (part.organ_tag == BP_HEAD || part.organ_tag == BP_TORSO || part.organ_tag == BP_GROIN)) + robobody_count ++ + else if(part.status & ORGAN_DEAD) + icon_key += "3" + else + icon_key += "1" + if(part.transparent) //VOREStation Edit. For better slime limbs. Avoids using solid var due to limb dropping. + icon_key += "_t" //VOREStation Edit. + + if(istype(tail_style, /datum/sprite_accessory/tail/taur)) + if(tail_style.clip_mask) //VOREStation Edit. + icon_key += tail_style.clip_mask_state + + if(digitigrade && (part.organ_tag == BP_R_LEG || part.organ_tag == BP_L_LEG || part.organ_tag == BP_R_FOOT || part.organ_tag == BP_L_FOOT)) + icon_key += "_digi" + + if(tail_style) + pixel_x = tail_style.mob_offset_x + pixel_y = tail_style.mob_offset_y + default_pixel_x = tail_style.mob_offset_x + default_pixel_y = tail_style.mob_offset_y + + icon_key = "[icon_key][husk ? 1 : 0][fat ? 1 : 0][hulk ? 1 : 0][skeleton ? 1 : 0]" + var/icon/base_icon + if(human_icon_cache[icon_key]) + base_icon = human_icon_cache[icon_key] + else + //BEGIN CACHED ICON GENERATION. + var/obj/item/organ/external/chest = get_organ(BP_TORSO) + base_icon = chest.get_icon(skeleton, !wholeicontransparent) + + var/apply_extra_transparency_leg = organs_by_name[BP_L_LEG] && organs_by_name[BP_R_LEG] + var/apply_extra_transparency_foot = organs_by_name[BP_L_FOOT] && organs_by_name[BP_R_FOOT] + + var/icon/Cutter = null + var/icon_x_offset = 0 + var/icon_y_offset = 0 + + if(istype(tail_style, /datum/sprite_accessory/tail/taur)) // Tail icon 'cookie cutters' are filled in where icons are preserved. We need to invert that. + if(tail_style.clip_mask) //VOREStation Edit. + Cutter = new(icon = (tail_style.clip_mask_icon ? tail_style.clip_mask_icon : tail_style.icon), icon_state = tail_style.clip_mask_state) + + Cutter.Blend("#000000", ICON_MULTIPLY) // Make it all black. + + Cutter.SwapColor("#00000000", "#FFFFFFFF") // Everywhere empty, make white. + Cutter.SwapColor("#000000FF", "#00000000") // Everywhere black, make empty. + + Cutter.Blend("#000000", ICON_MULTIPLY) // Black again. + + icon_x_offset = tail_style.offset_x + icon_y_offset = tail_style.offset_y + + for(var/obj/item/organ/external/part in organs) + if(isnull(part) || part.is_stump() || part == chest || part.is_hidden_by_sprite_accessory()) //VOREStation Edit allowing tails to prevent bodyparts rendering, granting more spriter freedom for taur/digitigrade stuff. + continue + var/icon/temp = part.get_icon(skeleton, !wholeicontransparent) + + if((part.organ_tag in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) && Cutter) + temp.Blend(Cutter, ICON_AND, x = icon_x_offset, y = icon_y_offset) + + //That part makes left and right legs drawn topmost and lowermost when human looks WEST or EAST + //And no change in rendering for other parts (they icon_position is 0, so goes to 'else' part) + if(part.icon_position & (LEFT | RIGHT)) + var/icon/temp2 = new(species.icon_template ? species.icon_template : 'icons/mob/human.dmi', icon_state = "blank") + temp2.Insert(new/icon(temp,dir=NORTH),dir=NORTH) + temp2.Insert(new/icon(temp,dir=SOUTH),dir=SOUTH) + if(!(part.icon_position & LEFT)) + temp2.Insert(new/icon(temp,dir=EAST),dir=EAST) + if(!(part.icon_position & RIGHT)) + temp2.Insert(new/icon(temp,dir=WEST),dir=WEST) + base_icon.Blend(temp2, ICON_OVERLAY) + temp2.Insert(temp2,"blank",dir=NORTH) //faaaaairly certain this is more efficient than reloading temp2, doing this so we don't blend the icons twice (it matters more in transparent limbs) + temp2.Insert(temp2,"blank",dir=SOUTH) + temp2.Insert(temp2,"blank",dir=EAST) + temp2.Insert(temp2,"blank",dir=WEST) + if(part.icon_position & LEFT) + temp2.Insert(new/icon(temp,dir=EAST),dir=EAST) + if(part.icon_position & RIGHT) + temp2.Insert(new/icon(temp,dir=WEST),dir=WEST) + if (part.transparent && !wholeicontransparent) //apply a little (a lot) extra transparency to make it look better //VORESTATION EDIT: transparent instead of nonsolid + if ((istype(part, /obj/item/organ/external/leg) && apply_extra_transparency_leg) || (istype(part, /obj/item/organ/external/foot) && apply_extra_transparency_foot)) //maybe + temp2 += rgb(,,,30) + base_icon.Blend(temp2, ICON_UNDERLAY) + else if(part.icon_position & UNDER) + base_icon.Blend(temp, ICON_UNDERLAY) + else + base_icon.Blend(temp, ICON_OVERLAY) + + if (wholeicontransparent) //because, I mean. It's basically never gonna happen that you'll have just one non-transparent limb but if you do your icon will look meh. Still good but meh, will have some areas with higher transparencies unless you're literally just a torso and a head + base_icon += rgb(,,,180) + + if(!skeleton) + if(husk) + base_icon.ColorTone(husk_color_mod) + else if(hulk) + var/list/tone = rgb2num(hulk_color_mod) + base_icon.MapColors(rgb(tone[1],0,0),rgb(0,tone[2],0),rgb(0,0,tone[3])) + + //Handle husk overlay. + if(husk && ("overlay_husk" in cached_icon_states(species.icobase))) + var/icon/mask = new(base_icon) + var/icon/husk_over = new(species.icobase,"overlay_husk") + mask.MapColors(0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0) + husk_over.Blend(mask, ICON_ADD) + base_icon.Blend(husk_over, ICON_OVERLAY) + + human_icon_cache[icon_key] = base_icon + + //END CACHED ICON GENERATION. + stand_icon.Blend(base_icon,ICON_OVERLAY) + icon = stand_icon + + //tail + update_tail_showing() + update_wing_showing() + +/mob/living/carbon/human/proc/update_skin() + if(QDESTROYING(src)) + return + + remove_layer(SKIN_LAYER) + + var/image/skin = species.update_skin(src) + if(skin) + skin.layer = BODY_LAYER+SKIN_LAYER + overlays_standing[SKIN_LAYER] = skin + apply_layer(SKIN_LAYER) + +/mob/living/carbon/human/proc/update_bloodied() + if(QDESTROYING(src)) + return + + remove_layer(BLOOD_LAYER) + if(!blood_DNA && !feet_blood_DNA) + return + + var/image/both = image(icon = 'icons/effects/effects.dmi', icon_state = "nothing", layer = BODY_LAYER+BLOOD_LAYER) + + //Bloody hands + if(blood_DNA) + var/image/bloodsies = image(icon = species.get_blood_mask(src), icon_state = "bloodyhands", layer = BODY_LAYER+BLOOD_LAYER) + bloodsies.color = hand_blood_color + both.add_overlay(bloodsies) + + //Bloody feet + if(feet_blood_DNA) + var/image/bloodsies = image(icon = species.get_blood_mask(src), icon_state = "shoeblood", layer = BODY_LAYER+BLOOD_LAYER) + bloodsies.color = feet_blood_color + both.add_overlay(bloodsies) + + overlays_standing[BLOOD_LAYER] = both + + apply_layer(BLOOD_LAYER) + +//UNDERWEAR OVERLAY +/mob/living/carbon/human/proc/update_underwear() + if(QDESTROYING(src)) + return + + remove_layer(UNDERWEAR_LAYER) + + if(species.appearance_flags & HAS_UNDERWEAR) + overlays_standing[UNDERWEAR_LAYER] = list() + for(var/category in all_underwear) + if(hide_underwear[category]) + continue + var/datum/category_item/underwear/UWI = all_underwear[category] + var/image/wear = UWI.generate_image(all_underwear_metadata[category], layer = BODY_LAYER+UNDERWEAR_LAYER) + overlays_standing[UNDERWEAR_LAYER] += wear + + apply_layer(UNDERWEAR_LAYER) + +//HAIR OVERLAY +/mob/living/carbon/human/proc/update_hair() + if(QDESTROYING(src)) + return + + //Reset our hair + remove_layer(HAIR_LAYER) + remove_layer(HAIR_ACCESSORY_LAYER) //VOREStation Edit + update_eyes() //Pirated out of here, for glowing eyes. + + var/obj/item/organ/external/head/head_organ = get_organ(BP_HEAD) + if(!head_organ || head_organ.is_stump() ) + return + + //masks and helmets can obscure our hair. + if( (head && (head.flags_inv & BLOCKHAIR)) || (wear_mask && (wear_mask.flags_inv & BLOCKHAIR))) + return + + //base icons + var/icon/face_standing = icon(icon = 'icons/mob/human_face.dmi', icon_state = "bald_s") + + if(f_style) + var/datum/sprite_accessory/facial_hair_style = facial_hair_styles_list[f_style] + if(facial_hair_style && facial_hair_style.species_allowed && (src.species.get_bodytype(src) in facial_hair_style.species_allowed)) + var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") + if(facial_hair_style.do_colouration) + facial_s.Blend(rgb(r_facial, g_facial, b_facial), facial_hair_style.color_blend_mode) + + face_standing.Blend(facial_s, ICON_OVERLAY) + + if(h_style) + var/datum/sprite_accessory/hair/hair_style = hair_styles_list[h_style] + if(head && (head.flags_inv & BLOCKHEADHAIR)) + if(!(hair_style.flags & HAIR_VERY_SHORT)) + hair_style = hair_styles_list["Short Hair"] + + if(hair_style && (src.species.get_bodytype(src) in hair_style.species_allowed)) + var/icon/grad_s = null + var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") + var/icon/hair_s_add = new/icon("icon" = hair_style.icon_add, "icon_state" = "[hair_style.icon_state]_s") + if(hair_style.do_colouration) + if(grad_style) + grad_s = new/icon("icon" = 'icons/mob/hair_gradients.dmi', "icon_state" = GLOB.hair_gradients[grad_style]) + grad_s.Blend(hair_s, ICON_AND) + grad_s.Blend(rgb(r_grad, g_grad, b_grad), ICON_MULTIPLY) + hair_s.Blend(rgb(r_hair, g_hair, b_hair), ICON_MULTIPLY) + hair_s.Blend(hair_s_add, ICON_ADD) + if(!isnull(grad_s)) + hair_s.Blend(grad_s, ICON_OVERLAY) + + face_standing.Blend(hair_s, ICON_OVERLAY) + + var/icon/ears_s = get_ears_overlay() + + if(head_organ.transparent) //VORESTATION EDIT: transparent instead of nonsolid + face_standing += rgb(,,,120) + if (ears_s) + ears_s += rgb(,,,180) + + var/image/em_block_ears + if(ears_s) + if(ears_s.Height() > face_standing.Height()) // Tol ears + face_standing.Crop(1, 1, face_standing.Width(), ears_s.Height()) + face_standing.Blend(ears_s, ICON_OVERLAY) + if(ear_style?.em_block) + em_block_ears = em_block_image_generic(image(ears_s)) + + var/image/semifinal = image(face_standing, layer = BODY_LAYER+HAIR_LAYER, "pixel_y" = head_organ.head_offset) + if(em_block_ears) + semifinal.overlays += em_block_ears // Leaving this as overlays += + + overlays_standing[HAIR_LAYER] = semifinal + apply_layer(HAIR_LAYER) + //return //VOREStation Edit + + // VOREStation Edit - START + var/icon/hair_acc_s = get_hair_accessory_overlay() + var/image/hair_acc_s_image = null + if(hair_acc_s) + if(hair_accessory_style.ignores_lighting) + hair_acc_s_image = image(hair_acc_s) + hair_acc_s_image.plane = PLANE_LIGHTING_ABOVE + hair_acc_s_image.appearance_flags = appearance_flags + if (hair_acc_s_image) + overlays_standing[HAIR_ACCESSORY_LAYER] = hair_acc_s_image + apply_layer(HAIR_ACCESSORY_LAYER) + return + overlays_standing[HAIR_ACCESSORY_LAYER] = image(hair_acc_s, layer = BODY_LAYER+HAIR_ACCESSORY_LAYER) + apply_layer(HAIR_ACCESSORY_LAYER) + // VOREStation Edit - END + +/mob/living/carbon/human/update_eyes() + if(QDESTROYING(src)) + return + + //Reset our eyes + remove_layer(EYES_LAYER) + + //TODO: Probably redo this. I know I wrote it, but... + + //This is ONLY for glowing eyes for now. Boring flat eyes are done by the head's own proc. + if(!species.has_glowing_eyes) + return + + //Our glowy eyes should be hidden if some equipment hides them. + if(!should_have_organ(O_EYES) || (head && (head.flags_inv & BLOCKHAIR)) || (wear_mask && (wear_mask.flags_inv & BLOCKHAIR))) + return + + //Get the head, we'll need it later. + var/obj/item/organ/external/head/head_organ = get_organ(BP_HEAD) + if(!head_organ || head_organ.is_stump() ) + return + + //The eyes store the color themselves, funny enough. + var/obj/item/organ/internal/eyes/eyes = internal_organs_by_name[O_EYES] + if(!head_organ.eye_icon) + return + + var/icon/eyes_icon = new/icon(head_organ.eye_icon_location, head_organ.eye_icon) //VOREStation Edit + if(eyes) + eyes_icon.Blend(rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3]), ICON_ADD) + else + eyes_icon.Blend(rgb(128,0,0), ICON_ADD) + + // Convert to emissive at some point + if (head_organ.transparent) //VOREStation Edit: transparent instead of nonsolid + eyes_icon += rgb(,,,180) + + var/image/eyes_image = image(eyes_icon) + eyes_image.plane = PLANE_LIGHTING_ABOVE + eyes_image.appearance_flags = appearance_flags + + overlays_standing[EYES_LAYER] = eyes_image + apply_layer(EYES_LAYER) + +/mob/living/carbon/human/update_mutations() + if(QDESTROYING(src)) + return + + remove_layer(MUTATIONS_LAYER) + + if(!LAZYLEN(mutations)) + return //No mutations, no icons. + + //TODO: THIS PROC??? + var/fat + if(FAT in mutations) + fat = "fat" + + var/image/standing = image(icon = 'icons/effects/genetics.dmi', layer = BODY_LAYER+MUTATIONS_LAYER) + var/g = gender == FEMALE ? "f" : "m" + + for(var/datum/dna/gene/gene in dna_genes) + if(!gene.block) + continue + if(gene.is_active(src)) + var/underlay = gene.OnDrawUnderlays(src,g,fat) + if(underlay) + standing.underlays += underlay + + for(var/mut in mutations) + if(mut == LASER) + standing.overlays += "lasereyes_s" // Leaving this as overlays += + + overlays_standing[MUTATIONS_LAYER] = standing + apply_layer(MUTATIONS_LAYER) + +/* --------------------------------------- */ +//Recomputes every icon on the mob. Expensive. +//Useful if the species changed, or there's some +//other drastic body-shape change, but otherwise avoid. +/mob/living/carbon/human/regenerate_icons() + ..() + if(transforming || QDELETED(src)) + return + + update_icons_body() + UpdateDamageIcon() + update_mutations() + update_skin() + update_underwear() + update_hair() + update_inv_w_uniform() + update_inv_wear_id() + update_inv_gloves() + update_inv_glasses() + update_inv_ears() + update_inv_shoes() + update_inv_s_store() + update_inv_wear_mask() + update_inv_head() + update_inv_belt() + update_inv_back() + update_inv_wear_suit() + update_inv_r_hand() + update_inv_l_hand() + update_inv_handcuffed() + update_inv_legcuffed() + //update_inv_pockets() //Doesn't do anything + update_fire() + update_water() + update_surgery() + +/* --------------------------------------- */ +//vvvvvv UPDATE_INV PROCS vvvvvv + +/mob/living/carbon/human/update_inv_w_uniform() + if(QDESTROYING(src)) + return + + remove_layer(UNIFORM_LAYER) + + //Shoes can be affected by uniform being drawn onto them + update_inv_shoes() + + if(!w_uniform) + return + + if(wear_suit && (wear_suit.flags_inv & HIDEJUMPSUIT) && !istype(wear_suit, /obj/item/clothing/suit/space/rig)) + return //Wearing a suit that prevents uniform rendering + + var/obj/item/clothing/under/under = w_uniform + + var/uniform_sprite + if(istype(under) && !isnull(under.update_icon_define)) + uniform_sprite = under.update_icon_define + else + uniform_sprite = INV_W_UNIFORM_DEF_ICON + + //Build a uniform sprite + var/icon/c_mask = tail_style?.clip_mask + if(c_mask) + var/obj/item/clothing/suit/S = wear_suit + if((wear_suit?.flags_inv & HIDETAIL) || (istype(S) && S.taurized)) // Reasons to not mask: 1. If you're wearing a suit that hides the tail or if you're wearing a taurized suit. + c_mask = null + overlays_standing[UNIFORM_LAYER] = w_uniform.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_w_uniform_str, default_icon = uniform_sprite, default_layer = UNIFORM_LAYER, clip_mask = c_mask) + apply_layer(UNIFORM_LAYER) + +/mob/living/carbon/human/update_inv_wear_id() + if(QDESTROYING(src)) + return + + remove_layer(ID_LAYER) + + if(!wear_id) + return //Not wearing an ID + + //Only draw the ID on the mob if the uniform allows for it + if(w_uniform && istype(w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = w_uniform + if(U.displays_id) + overlays_standing[ID_LAYER] = wear_id.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_id_str, default_icon = INV_WEAR_ID_DEF_ICON, default_layer = ID_LAYER) + + apply_layer(ID_LAYER) + +/mob/living/carbon/human/update_inv_gloves() + if(QDESTROYING(src)) + return + + remove_layer(GLOVES_LAYER) + + if(!gloves) + return //No gloves, no reason to be here. + + overlays_standing[GLOVES_LAYER] = gloves.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_gloves_str, default_icon = INV_GLOVES_DEF_ICON, default_layer = GLOVES_LAYER) + + apply_layer(GLOVES_LAYER) + +/mob/living/carbon/human/update_inv_glasses() + if(QDESTROYING(src)) + return + + remove_layer(GLASSES_LAYER) + remove_layer(GLASSES_LAYER_ALT) + + if(!glasses) + return //Not wearing glasses, no need to update anything. + + var/glasses_layer = GLASSES_LAYER + if(istype(glasses, /obj/item/clothing/glasses)) + var/obj/item/clothing/glasses/our_glasses = glasses + if(our_glasses.glasses_layer_above) + glasses_layer = GLASSES_LAYER_ALT + + overlays_standing[glasses_layer] = glasses.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_gloves_str, default_icon = INV_EYES_DEF_ICON, default_layer = glasses_layer) + + apply_layer(glasses_layer) + +/mob/living/carbon/human/update_inv_ears() + if(QDESTROYING(src)) + return + + remove_layer(EARS_LAYER) + + if((head && head.flags_inv & (BLOCKHAIR | BLOCKHEADHAIR)) || (wear_mask && wear_mask.flags_inv & (BLOCKHAIR | BLOCKHEADHAIR))) + return //Ears are blocked (by hair being blocked, overloaded) + + if(!l_ear && !r_ear) + return //Why bother, if no ear sprites + + // Blank image upon which to layer left & right overlays. + var/image/both = image(icon = 'icons/effects/effects.dmi', icon_state = "nothing", layer = BODY_LAYER+EARS_LAYER) + + if(l_ear) + var/image/standing = l_ear.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_l_ear_str, default_icon = INV_EARS_DEF_ICON, default_layer = EARS_LAYER) + both.add_overlay(standing) + + if(r_ear) + var/image/standing = r_ear.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_r_ear_str, default_icon = INV_EARS_DEF_ICON, default_layer = EARS_LAYER) + both.add_overlay(standing) + + overlays_standing[EARS_LAYER] = both + apply_layer(EARS_LAYER) + +/mob/living/carbon/human/update_inv_shoes() + if(QDESTROYING(src)) + return + + remove_layer(SHOES_LAYER) + remove_layer(SHOES_LAYER_ALT) //Dumb alternate layer for shoes being under the uniform. + + if(!shoes || (wear_suit && wear_suit.flags_inv & HIDESHOES) || (w_uniform && w_uniform.flags_inv & HIDESHOES)) + return //Either nothing to draw, or it'd be hidden. + + for(var/f in list(BP_L_FOOT, BP_R_FOOT)) + var/obj/item/organ/external/foot/foot = get_organ(f) + if(istype(foot) && foot.is_hidden_by_sprite_accessory(clothing_only = TRUE)) //If either foot is hidden by the tail, don't render footwear. + return + + var/obj/item/clothing/shoes/shoe = shoes + var/shoe_sprite + + if(istype(shoe) && !isnull(shoe.update_icon_define)) + shoe_sprite = shoe.update_icon_define + else + shoe_sprite = INV_FEET_DEF_ICON + + //Allow for shoe layer toggle nonsense + var/shoe_layer = SHOES_LAYER + if(istype(shoes, /obj/item/clothing/shoes)) + var/obj/item/clothing/shoes/ushoes = shoes + if(ushoes.shoes_under_pants == 1) + shoe_layer = SHOES_LAYER_ALT + + //NB: the use of a var for the layer on this one + overlays_standing[shoe_layer] = shoes.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_shoes_str, default_icon = shoe_sprite, default_layer = shoe_layer) + + apply_layer(SHOES_LAYER) + apply_layer(SHOES_LAYER_ALT) + +/mob/living/carbon/human/update_inv_s_store() + if(QDESTROYING(src)) + return + + remove_layer(SUIT_STORE_LAYER) + + if(!s_store) + return //Why bother, nothing there. + + //TODO, this is unlike the rest of the things + //Basically has no variety in slot icon choices at all. WHY SPECIES ONLY?? + var/t_state = s_store.item_state + if(!t_state) + t_state = s_store.icon_state + overlays_standing[SUIT_STORE_LAYER] = image(icon = species.suit_storage_icon, icon_state = t_state, layer = BODY_LAYER+SUIT_STORE_LAYER) + + apply_layer(SUIT_STORE_LAYER) + +/mob/living/carbon/human/update_inv_head() + if(QDESTROYING(src)) + return + + remove_layer(HEAD_LAYER) + + if(!head) + return //No head item, why bother. + + overlays_standing[HEAD_LAYER] = head.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_head_str, default_icon = INV_HEAD_DEF_ICON, default_layer = HEAD_LAYER) + + apply_layer(HEAD_LAYER) + +/mob/living/carbon/human/update_inv_belt() + if(QDESTROYING(src)) + return + + remove_layer(BELT_LAYER) + remove_layer(BELT_LAYER_ALT) //Because you can toggle belt layer with a verb + + if(!belt) + return //No belt, why bother. + + //Toggle for belt layering with uniform + var/belt_layer = BELT_LAYER + if(istype(belt, /obj/item/weapon/storage/belt)) + var/obj/item/weapon/storage/belt/ubelt = belt + if(ubelt.show_above_suit) + belt_layer = BELT_LAYER_ALT + + + var/icon/c_mask = tail_style?.clip_mask + + //NB: this uses a var from above + overlays_standing[belt_layer] = belt.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_belt_str, default_icon = INV_BELT_DEF_ICON, default_layer = belt_layer, clip_mask = c_mask) + + apply_layer(belt_layer) + +/mob/living/carbon/human/update_inv_wear_suit() + if(QDESTROYING(src)) + return + + remove_layer(SUIT_LAYER) + + //Hide/show other layers if necessary + update_inv_w_uniform() + update_inv_shoes() + update_tail_showing() + update_wing_showing() + + if(!wear_suit) + return //No point, no suit. + + var/obj/item/clothing/suit/suit = wear_suit + var/suit_sprite + + if(istype(suit) && !isnull(suit.update_icon_define)) + suit_sprite = suit.update_icon_define + else + suit_sprite = INV_SUIT_DEF_ICON + + var/icon/c_mask = null + var/tail_is_rendered = (overlays_standing[TAIL_UPPER_LAYER] || overlays_standing[TAIL_UPPER_LAYER_ALT] || overlays_standing[TAIL_LOWER_LAYER]) + var/valid_clip_mask = tail_style?.clip_mask + + if(tail_is_rendered && valid_clip_mask && !(istype(suit) && suit.taurized)) //Clip the lower half of the suit off using the tail's clip mask for taurs since taur bodies aren't hidden. + c_mask = valid_clip_mask + overlays_standing[SUIT_LAYER] = wear_suit.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_suit_str, default_icon = suit_sprite, default_layer = SUIT_LAYER, clip_mask = c_mask) + + apply_layer(SUIT_LAYER) + +/mob/living/carbon/human/update_inv_pockets() + stack_trace("Someone called update_inv_pockets even though it's dumb") + +/mob/living/carbon/human/update_inv_wear_mask() + if(QDESTROYING(src)) + return + + remove_layer(FACEMASK_LAYER) + + if(!wear_mask || (head && head.flags_inv & HIDEMASK)) + return //Why bother, nothing in mask slot. + + overlays_standing[FACEMASK_LAYER] = wear_mask.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_mask_str, default_icon = INV_MASK_DEF_ICON, default_layer = FACEMASK_LAYER) + + apply_layer(FACEMASK_LAYER) + +/mob/living/carbon/human/update_inv_back() + if(QDESTROYING(src)) + return + + remove_layer(BACK_LAYER) + + if(!back) + return //Why do anything + + var/icon/c_mask = tail_style?.clip_mask + if(c_mask) + if(istype(back, /obj/item/weapon/storage/backpack/saddlebag) || istype(back, /obj/item/weapon/storage/backpack/saddlebag_common)) + c_mask = null + + overlays_standing[BACK_LAYER] = back.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_back_str, default_icon = INV_BACK_DEF_ICON, default_layer = BACK_LAYER, clip_mask = c_mask) + + apply_layer(BACK_LAYER) + +//TODO: Carbon procs in my human update_icons?? +/mob/living/carbon/human/update_hud() //TODO: do away with this if possible + if(QDESTROYING(src)) + return + + if(client) + client.screen |= contents + if(hud_used) + hud_used.hidden_inventory_update() //Updates the screenloc of the items on the 'other' inventory bar + +//update whether handcuffs appears on our hud. +/mob/living/carbon/proc/update_hud_handcuffed() + if(QDESTROYING(src)) + return + + if(hud_used && hud_used.l_hand_hud_object && hud_used.r_hand_hud_object) + hud_used.l_hand_hud_object.update_icon() + hud_used.r_hand_hud_object.update_icon() + +/mob/living/carbon/human/update_inv_handcuffed() + if(QDESTROYING(src)) + return + + remove_layer(HANDCUFF_LAYER) + update_hud_handcuffed() //TODO + + if(!handcuffed) + return //Not cuffed, why bother + + overlays_standing[HANDCUFF_LAYER] = handcuffed.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_handcuffed_str, default_icon = INV_HCUFF_DEF_ICON, default_layer = HANDCUFF_LAYER) + + apply_layer(HANDCUFF_LAYER) + +/mob/living/carbon/human/update_inv_legcuffed() + if(QDESTROYING(src)) + return + + clear_alert("legcuffed") + remove_layer(LEGCUFF_LAYER) + + if(!legcuffed) + return //Not legcuffed, why bother. + + throw_alert("legcuffed", /obj/screen/alert/restrained/legcuffed, new_master = legcuffed) + + overlays_standing[LEGCUFF_LAYER] = legcuffed.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_legcuffed_str, default_icon = INV_LCUFF_DEF_ICON, default_layer = LEGCUFF_LAYER) + + apply_layer(LEGCUFF_LAYER) + +/mob/living/carbon/human/update_inv_r_hand() + if(QDESTROYING(src)) + return + + remove_layer(R_HAND_LAYER) + + if(!r_hand) + return //No hand, no bother. + + overlays_standing[R_HAND_LAYER] = r_hand.make_worn_icon(body_type = species.get_bodytype(src), inhands = TRUE, slot_name = slot_r_hand_str, default_icon = INV_R_HAND_DEF_ICON, default_layer = R_HAND_LAYER) + + apply_layer(R_HAND_LAYER) + +/mob/living/carbon/human/update_inv_l_hand() + if(QDESTROYING(src)) + return + + remove_layer(L_HAND_LAYER) + + if(!l_hand) + return //No hand, no bother. + + overlays_standing[L_HAND_LAYER] = l_hand.make_worn_icon(body_type = species.get_bodytype(src), inhands = TRUE, slot_name = slot_l_hand_str, default_icon = INV_L_HAND_DEF_ICON, default_layer = L_HAND_LAYER) + + apply_layer(L_HAND_LAYER) + +/mob/living/carbon/human/proc/get_tail_layer() + var/list/lower_layer_dirs = list(SOUTH) + if(tail_style) + lower_layer_dirs = tail_style.lower_layer_dirs.Copy() + + if(dir in lower_layer_dirs) + return TAIL_LOWER_LAYER + else + return TAIL_UPPER_LAYER + +/mob/living/carbon/human/proc/update_tail_showing() + if(QDESTROYING(src)) + return + + remove_layer(TAIL_UPPER_LAYER) + remove_layer(TAIL_UPPER_LAYER_ALT) + remove_layer(TAIL_LOWER_LAYER) + + var/tail_layer = get_tail_layer() + if(src.tail_style && src.tail_style.clip_mask_state) + tail_layer = TAIL_UPPER_LAYER // Use default, let clip mask handle everything + if(tail_alt && tail_layer == TAIL_UPPER_LAYER) + tail_layer = TAIL_UPPER_LAYER_ALT + + var/obj/item/organ/external/chest = organs_by_name[BP_TORSO] + + var/image/tail_image = get_tail_image() + if(tail_image) + tail_image.layer = BODY_LAYER+tail_layer + tail_image.alpha = chest?.transparent ? 180 : 255 //VORESTATION EDIT: transparent instead of nonsolid + overlays_standing[tail_layer] = tail_image + apply_layer(tail_layer) + return + + var/species_tail = species.get_tail(src) // Species tail icon_state prefix. + + //This one is actually not that bad I guess. + if(species_tail && !(wear_suit && wear_suit.flags_inv & HIDETAIL)) + var/icon/tail_s = get_tail_icon() + tail_image = image(icon = tail_s, icon_state = "[species_tail]_s", layer = BODY_LAYER+tail_layer) + tail_image.alpha = chest?.transparent ? 180 : 255 //VORESTATION EDIT: transparent instead of nonsolid + overlays_standing[tail_layer] = tail_image + animate_tail_reset() + +//TODO: Is this the appropriate place for this, and not on species...? +/mob/living/carbon/human/proc/get_tail_icon() + var/icon_key = "[species.get_race_key(src)][r_skin][g_skin][b_skin][r_hair][g_hair][b_hair]" + var/icon/tail_icon = tail_icon_cache[icon_key] + if(!tail_icon) + //generate a new one + var/species_tail_anim = species.get_tail_animation(src) + if(!species_tail_anim && species.icobase_tail) species_tail_anim = species.icobase //Allow override of file for non-animated tails + if(!species_tail_anim) species_tail_anim = 'icons/effects/species.dmi' + tail_icon = new/icon(species_tail_anim) + tail_icon.Blend(rgb(r_skin, g_skin, b_skin), species.color_mult ? ICON_MULTIPLY : ICON_ADD) + // The following will not work with animated tails. + var/use_species_tail = species.get_tail_hair(src) + if(use_species_tail) + var/icon/hair_icon = icon('icons/effects/species.dmi', "[species.get_tail(src)]_[use_species_tail]") + hair_icon.Blend(rgb(r_hair, g_hair, b_hair), species.color_mult ? ICON_MULTIPLY : ICON_ADD) //Check for species color_mult + tail_icon.Blend(hair_icon, ICON_OVERLAY) + tail_icon_cache[icon_key] = tail_icon + + return tail_icon + +/mob/living/carbon/human/proc/set_tail_state(var/t_state) + var/tail_layer = get_tail_layer() + if(src.tail_style && src.tail_style.clip_mask_state) + tail_layer = TAIL_UPPER_LAYER // Use default, let clip mask handle everything + if(tail_alt && tail_layer == TAIL_UPPER_LAYER) + tail_layer = TAIL_UPPER_LAYER_ALT + var/image/tail_overlay = overlays_standing[tail_layer] + + remove_layer(TAIL_UPPER_LAYER) + remove_layer(TAIL_UPPER_LAYER_ALT) + remove_layer(TAIL_LOWER_LAYER) + + if(tail_overlay) + overlays_standing[tail_layer] = tail_overlay + if(species.get_tail_animation(src)) + tail_overlay.icon_state = t_state + . = tail_overlay + + apply_layer(tail_layer) + +//Not really once, since BYOND can't do that. +//Update this if the ability to flick() images or make looping animation start at the first frame is ever added. +//You can sort of flick images now with flick_overlay -Aro +/mob/living/carbon/human/proc/animate_tail_once() + if(QDESTROYING(src)) + return + + var/t_state = "[species.get_tail(src)]_once" + var/tail_layer = get_tail_layer() + if(src.tail_style && src.tail_style.clip_mask_state) + tail_layer = TAIL_UPPER_LAYER // Use default, let clip mask handle everything + + var/image/tail_overlay = overlays_standing[tail_layer] + if(tail_overlay && tail_overlay.icon_state == t_state) + return //let the existing animation finish + + tail_overlay = set_tail_state(t_state) // Calls remove_layer & apply_layer + if(tail_overlay) + spawn(20) + //check that the animation hasn't changed in the meantime + if(overlays_standing[tail_layer] == tail_overlay && tail_overlay.icon_state == t_state) + animate_tail_stop() + +/mob/living/carbon/human/proc/animate_tail_start() + if(QDESTROYING(src)) + return + + set_tail_state("[species.get_tail(src)]_slow[rand(0,9)]") + +/mob/living/carbon/human/proc/animate_tail_fast() + if(QDESTROYING(src)) + return + + set_tail_state("[species.get_tail(src)]_loop[rand(0,9)]") + +/mob/living/carbon/human/proc/animate_tail_reset() + if(QDESTROYING(src)) + return + + if(stat != DEAD) + set_tail_state("[species.get_tail(src)]_idle[rand(0,9)]") + else + set_tail_state("[species.get_tail(src)]_static") + toggle_tail(FALSE) //So tails stop when someone dies. TODO - Fix this hack ~Leshana + +/mob/living/carbon/human/proc/animate_tail_stop() + if(QDESTROYING(src)) + return + + set_tail_state("[species.get_tail(src)]_static") + +/mob/living/carbon/human/proc/update_wing_showing() + if(QDESTROYING(src)) + return + + remove_layer(WING_LAYER) + remove_layer(WING_LOWER_LAYER) + + var/image/wing_image = get_wing_image(FALSE) + + var/obj/item/organ/external/chest = organs_by_name[BP_TORSO] + + if(wing_image) + wing_image.layer = BODY_LAYER+WING_LAYER + wing_image.alpha = chest?.transparent ? 180 : 255 //VORESTATION EDIT: transparent instead of nonsolid + overlays_standing[WING_LAYER] = wing_image + if(wing_style && wing_style.multi_dir) + wing_image = get_wing_image(TRUE) + if(wing_image) + wing_image.layer = BODY_LAYER+WING_LOWER_LAYER + overlays_standing[WING_LOWER_LAYER] = wing_image + + apply_layer(WING_LAYER) + apply_layer(WING_LOWER_LAYER) + +/mob/living/carbon/human/update_modifier_visuals() + if(QDESTROYING(src)) + return + + remove_layer(MODIFIER_EFFECTS_LAYER) + + if(!LAZYLEN(modifiers)) + return //No modifiers, no effects. + + var/image/effects = new() + for(var/datum/modifier/M in modifiers) + if(M.mob_overlay_state) + if(M.icon_override) //VOREStation Edit. Override for the modifer icon. + var/image/I = image(icon = 'icons/mob/modifier_effects_vr.dmi', icon_state = M.mob_overlay_state) + I.color = M.effect_color + effects.overlays += I // Leaving this as overlays += + else + var/image/I = image(icon = 'icons/mob/modifier_effects.dmi', icon_state = M.mob_overlay_state) + I.color = M.effect_color + effects.overlays += I // Leaving this as overlays += + + overlays_standing[MODIFIER_EFFECTS_LAYER] = effects + + apply_layer(MODIFIER_EFFECTS_LAYER) + +/mob/living/carbon/human/update_fire() + if(QDESTROYING(src)) + return + + remove_layer(FIRE_LAYER) + + if(!on_fire) + return + + overlays_standing[FIRE_LAYER] = image(icon = 'icons/mob/OnFire.dmi', icon_state = get_fire_icon_state(), layer = BODY_LAYER+FIRE_LAYER) + + apply_layer(FIRE_LAYER) + +/mob/living/carbon/human/update_water() + if(QDESTROYING(src)) + return + + remove_layer(MOB_WATER_LAYER) + + var/depth = check_submerged() + if(!depth || lying) + return + + var/atom/A = loc // We'd better be swimming and on a turf + var/image/I = image(icon = 'icons/mob/submerged.dmi', icon_state = "human_swimming_[depth]", layer = BODY_LAYER+MOB_WATER_LAYER) //TODO: Improve + I.color = A.color + overlays_standing[MOB_WATER_LAYER] = I + + apply_layer(MOB_WATER_LAYER) + +/mob/living/carbon/human/proc/update_surgery() + if(QDESTROYING(src)) + return + + remove_layer(SURGERY_LAYER) + + var/image/total = new + for(var/obj/item/organ/external/E in organs) + if(E.open) + var/image/I = image(icon = 'icons/mob/surgery.dmi', icon_state = "[E.icon_name][round(E.open)]", layer = BODY_LAYER+SURGERY_LAYER) + total.overlays += I // Leaving this as overlays += + + if(total.overlays.len) + overlays_standing[SURGERY_LAYER] = total + apply_layer(SURGERY_LAYER) + +/mob/living/carbon/human/proc/get_wing_image(var/under_layer) + if(QDESTROYING(src)) + return + + //If you are FBP with wing style and didn't set a custom one + if(synthetic && synthetic.includes_wing && !wing_style && !wings_hidden) //VOREStation Edit + var/icon/wing_s = new/icon("icon" = synthetic.icon, "icon_state" = "wing") //I dunno. If synths have some custom wing? + wing_s.Blend(rgb(src.r_skin, src.g_skin, src.b_skin), species.color_mult ? ICON_MULTIPLY : ICON_ADD) + var/image/working = image(wing_s) + working.overlays += em_block_image_generic(working) // Leaving this as overlays += + return working + + //If you have custom wings selected + if(wing_style && !(wear_suit && wear_suit.flags_inv & HIDETAIL) && !wings_hidden) //VOREStation Edit + var/wing_state = (flapping && wing_style.ani_state) ? wing_style.ani_state : wing_style.icon_state + if(wing_style.multi_dir) + wing_state += "_[under_layer ? "back" : "front"]" + var/icon/wing_s = new/icon("icon" = wing_style.icon, "icon_state" = wing_state) + if(wing_style.do_colouration) + wing_s.Blend(rgb(src.r_wing, src.g_wing, src.b_wing), wing_style.color_blend_mode) + if(wing_style.extra_overlay) + var/icon/overlay = new/icon("icon" = wing_style.icon, "icon_state" = wing_style.extra_overlay) + overlay.Blend(rgb(src.r_wing2, src.g_wing2, src.b_wing2), wing_style.color_blend_mode) + wing_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + if(wing_style.extra_overlay2) + var/icon/overlay = new/icon("icon" = wing_style.icon, "icon_state" = wing_style.extra_overlay2) + if(wing_style.ani_state) + overlay = new/icon("icon" = wing_style.icon, "icon_state" = wing_style.extra_overlay2_w) + overlay.Blend(rgb(src.r_wing3, src.g_wing3, src.b_wing3), wing_style.color_blend_mode) + wing_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + else + overlay.Blend(rgb(src.r_wing3, src.g_wing3, src.b_wing3), wing_style.color_blend_mode) + wing_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + var/image/working = image(wing_s) + if(wing_style.em_block) + working.overlays += em_block_image_generic(working) // Leaving this as overlays += + working.pixel_x -= wing_style.wing_offset + return working + +/mob/living/carbon/human/proc/get_ears_overlay() + //If you are FBP with ear style and didn't set a custom one + var/datum/robolimb/model = isSynthetic() + if(istype(model) && model.includes_ears && !ear_style) + var/icon/ears_s = new/icon("icon" = synthetic.icon, "icon_state" = "ears") + ears_s.Blend(rgb(src.r_ears, src.g_ears, src.b_ears), species.color_mult ? ICON_MULTIPLY : ICON_ADD) + return ears_s + + if(ear_style && !(head && (head.flags_inv & BLOCKHEADHAIR))) + var/icon/ears_s = new/icon("icon" = ear_style.icon, "icon_state" = ear_style.icon_state) + if(ear_style.do_colouration) + ears_s.Blend(rgb(src.r_ears, src.g_ears, src.b_ears), ear_style.color_blend_mode) + if(ear_style.extra_overlay) + var/icon/overlay = new/icon("icon" = ear_style.icon, "icon_state" = ear_style.extra_overlay) + overlay.Blend(rgb(src.r_ears2, src.g_ears2, src.b_ears2), ear_style.color_blend_mode) + ears_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + if(ear_style.extra_overlay2) //MORE COLOURS IS BETTERER + var/icon/overlay = new/icon("icon" = ear_style.icon, "icon_state" = ear_style.extra_overlay2) + overlay.Blend(rgb(src.r_ears3, src.g_ears3, src.b_ears3), ear_style.color_blend_mode) + ears_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + return ears_s + return null + + +/mob/living/carbon/human/proc/get_tail_image() + //If you are FBP with tail style and didn't set a custom one + var/datum/robolimb/model = isSynthetic() + if(istype(model) && model.includes_tail && !tail_style && !tail_hidden) + var/icon/tail_s = new/icon("icon" = synthetic.icon, "icon_state" = "tail") + tail_s.Blend(rgb(src.r_skin, src.g_skin, src.b_skin), species.color_mult ? ICON_MULTIPLY : ICON_ADD) + return image(tail_s) + + //If you have a custom tail selected + if(tail_style && !(wear_suit && wear_suit.flags_inv & HIDETAIL && !istaurtail(tail_style)) && !tail_hidden) + var/icon/tail_s = new/icon("icon" = (tail_style.can_loaf && resting) ? tail_style.icon_loaf : tail_style.icon, "icon_state" = (wagging && tail_style.ani_state ? tail_style.ani_state : tail_style.icon_state)) //CHOMPEdit + if(tail_style.can_loaf && !is_shifted) + pixel_y = (resting) ? -tail_style.loaf_offset*size_multiplier : default_pixel_y //move player down, then taur up, to fit the overlays correctly // VOREStation Edit: Taur Loafing + if(tail_style.do_colouration) + tail_s.Blend(rgb(src.r_tail, src.g_tail, src.b_tail), tail_style.color_blend_mode) + if(tail_style.extra_overlay) + var/icon/overlay = new/icon("icon" = (tail_style?.can_loaf && resting) ? tail_style.icon_loaf : tail_style.icon, "icon_state" = tail_style.extra_overlay) //CHOMPEdit + if(wagging && tail_style.ani_state) + overlay = new/icon("icon" = tail_style.icon, "icon_state" = tail_style.extra_overlay_w) + overlay.Blend(rgb(src.r_tail2, src.g_tail2, src.b_tail2), tail_style.color_blend_mode) + tail_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + else + overlay.Blend(rgb(src.r_tail2, src.g_tail2, src.b_tail2), tail_style.color_blend_mode) + tail_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + + if(tail_style.extra_overlay2) + var/icon/overlay = new/icon("icon" = (tail_style?.can_loaf && resting) ? tail_style.icon_loaf : tail_style.icon, "icon_state" = tail_style.extra_overlay2) //CHOMPEdit + if(wagging && tail_style.ani_state) + overlay = new/icon("icon" = tail_style.icon, "icon_state" = tail_style.extra_overlay2_w) + overlay.Blend(rgb(src.r_tail3, src.g_tail3, src.b_tail3), tail_style.color_blend_mode) + tail_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + else + overlay.Blend(rgb(src.r_tail3, src.g_tail3, src.b_tail3), tail_style.color_blend_mode) + tail_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + + var/image/working = image(tail_s) + if(tail_style.em_block) + working.overlays += em_block_image_generic(working) // Leaving this as overlays += + + if(istaurtail(tail_style)) + var/datum/sprite_accessory/tail/taur/taurtype = tail_style + working.pixel_x = tail_style.offset_x + working.pixel_y = tail_style.offset_y + if(taurtype.can_ride && !riding_datum) + riding_datum = new /datum/riding/taur(src) + verbs |= /mob/living/carbon/human/proc/taur_mount + verbs |= /mob/living/proc/toggle_rider_reins + else if(islongtail(tail_style)) + working.pixel_x = tail_style.offset_x + working.pixel_y = tail_style.offset_y + return working + return null + +// TODO - Move this to where it should go ~Leshana +/mob/living/proc/stop_flying() + if(QDESTROYING(src)) + return + flying = FALSE + return 1 + +/mob/living/carbon/human/stop_flying() + if((. = ..())) + update_wing_showing() + +//Human Overlays Indexes///////// +#undef MUTATIONS_LAYER +#undef SKIN_LAYER +#undef MOB_DAM_LAYER +#undef SURGERY_LAYER +#undef UNDERWEAR_LAYER +#undef SHOES_LAYER_ALT +#undef UNIFORM_LAYER +#undef ID_LAYER +#undef SHOES_LAYER +#undef GLOVES_LAYER +#undef BELT_LAYER +#undef SUIT_LAYER +#undef TAIL_UPPER_LAYER +#undef TAIL_LOWER_LAYER +#undef GLASSES_LAYER +#undef BELT_LAYER_ALT +#undef SUIT_STORE_LAYER +#undef BACK_LAYER +#undef HAIR_LAYER +#undef EARS_LAYER +#undef EYES_LAYER +#undef FACEMASK_LAYER +#undef HEAD_LAYER +#undef HANDCUFF_LAYER +#undef LEGCUFF_LAYER +#undef L_HAND_LAYER +#undef R_HAND_LAYER +#undef MODIFIER_EFFECTS_LAYER +#undef FIRE_LAYER +#undef WATER_LAYER +#undef TARGETED_LAYER +#undef TOTAL_LAYERS diff --git a/code/modules/mob/living/carbon/metroid/powers.dm b/code/modules/mob/living/carbon/metroid/powers.dm index 0ed74f15cd8..bcbf8fb132e 100644 --- a/code/modules/mob/living/carbon/metroid/powers.dm +++ b/code/modules/mob/living/carbon/metroid/powers.dm @@ -1,169 +1,169 @@ -/mob/living/carbon/slime/proc/Wrap(var/mob/living/M) // This is a proc for the clicks - if (Victim == M || src == M) - Feedstop() - return - - if (Victim) - to_chat(src, "I am already feeding...") - return - - var t = invalidFeedTarget(M) - if (t) - to_chat(src,t) - return - - Feedon(M) - -/mob/living/carbon/slime/proc/invalidFeedTarget(var/mob/living/M) - if (!M || !istype(M)) - return "This subject is incomparable..." - if (istype(M, /mob/living/carbon/slime)) // No cannibalism... yet - return "I cannot feed on other slimes..." - if (!Adjacent(M)) - return "This subject is too far away..." - if (istype(M, /mob/living/carbon) && M.getCloneLoss() >= M.getMaxHealth() * 1.5 || istype(M, /mob/living/simple_mob) && M.stat == DEAD) - return "This subject does not have an edible life energy..." - for(var/mob/living/carbon/slime/met in view()) - if(met.Victim == M && met != src) - return "The [met.name] is already feeding on this subject..." - return 0 - -/mob/living/carbon/slime/proc/Feedon(var/mob/living/M) - Victim = M - loc = M.loc - canmove = 0 - anchored = TRUE - - regenerate_icons() - - while(Victim && !invalidFeedTarget(M) && stat != 2) - canmove = 0 - - if(Adjacent(M)) - UpdateFeed(M) - - if(istype(M, /mob/living/carbon)) - Victim.adjustCloneLoss(rand(5,6)) - Victim.adjustToxLoss(rand(1,2)) - if(Victim.health <= 0) - Victim.adjustToxLoss(rand(2,4)) - - else if(istype(M, /mob/living/simple_mob)) - Victim.adjustBruteLoss(is_adult ? rand(7, 15) : rand(4, 12)) - - else - to_chat(src, "[pick("This subject is incompatible", "This subject does not have a life energy", "This subject is empty", "I am not satisfied", "I can not feed from this subject", "I do not feel nourished", "This subject is not food")]...") - Feedstop() - break - - if(prob(15) && M.client && istype(M, /mob/living/carbon)) - var/painMes = pick("You can feel your body becoming weak!", "You feel like you're about to die!", "You feel every part of your body screaming in agony!", "A low, rolling pain passes through your body!", "Your body feels as if it's falling apart!", "You feel extremely weak!", "A sharp, deep pain bathes every inch of your body!") - if (ishuman(M)) - var/mob/living/carbon/human/H = M - H.custom_pain(painMes, 100) - else if (istype(M, /mob/living/carbon)) - var/mob/living/carbon/C = M - if (C.can_feel_pain()) - to_chat(M, "[painMes]") - - gain_nutrition(rand(20,25)) - - adjustOxyLoss(-10) //Heal yourself - adjustBruteLoss(-10) - adjustFireLoss(-10) - adjustCloneLoss(-10) - updatehealth() - if(Victim) - Victim.updatehealth() - - sleep(30) // Deal damage every 3 seconds - else - break - - canmove = 1 - anchored = FALSE - - if(M && invalidFeedTarget(M)) // This means that the slime drained the victim - if(!client) - if(Victim && !rabid && !attacked && Victim.LAssailant && Victim.LAssailant != Victim && prob(50)) - if(!(Victim.LAssailant in Friends)) - Friends[Victim.LAssailant] = 1 - else - ++Friends[Victim.LAssailant] - - else - to_chat(src, "This subject does not have a strong enough life energy anymore...") - - Victim = null - -/mob/living/carbon/slime/proc/Feedstop() - if(Victim) - if(Victim.client) - to_chat(Victim, "[src] has let go of your head!") - Victim = null - -/mob/living/carbon/slime/proc/UpdateFeed(var/mob/M) - if(Victim) - if(Victim == M) - loc = M.loc // simple "attach to head" effect! - -/mob/living/carbon/slime/verb/Evolve() - set category = "Slime" - set desc = "This will let you evolve from baby to adult slime." - - if(stat) - to_chat(src, "I must be conscious to do this...") - return - - if(!is_adult) - if(amount_grown >= 10) - is_adult = 1 - maxHealth = 200 - amount_grown = 0 - regenerate_icons() - name = text("[colour] [is_adult ? "adult" : "baby"] slime ([number])") - else - to_chat(src, "I am not ready to evolve yet...") - else - to_chat(src, "I have already evolved...") - -/mob/living/carbon/slime/verb/Reproduce() - set category = "Slime" - set desc = "This will make you split into four Slimes." - - if(stat) - to_chat(src, "I must be conscious to do this...") - return - - if(is_adult) - if(amount_grown >= 10) - if(stat) - to_chat(src, "I must be conscious to do this...") - return - - var/list/babies = list() - var/new_nutrition = round(nutrition * 0.9) - var/new_powerlevel = round(powerlevel / 4) - for(var/i = 1, i <= 4, i++) - var/t = colour - if(prob(mutation_chance)) - t = slime_mutation[rand(1,4)] - var/mob/living/carbon/slime/M = new /mob/living/carbon/slime/(loc, t) - if(ckey) M.nutrition = new_nutrition //Player slimes are more robust at spliting. Once an oversight of poor copypasta, now a feature! - M.powerlevel = new_powerlevel - if(i != 1) step_away(M, src) - M.Friends = Friends.Copy() - babies += M - feedback_add_details("slime_babies_born","slimebirth_[replacetext(M.colour," ","_")]") - - var/mob/living/carbon/slime/new_slime = pick(babies) - new_slime.universal_speak = universal_speak - if(src.mind) - src.mind.transfer_to(new_slime) - else - new_slime.key = src.key - qdel(src) - else - to_chat(src, "I am not ready to reproduce yet...") - else - to_chat(src, "I am not old enough to reproduce yet...") +/mob/living/carbon/slime/proc/Wrap(var/mob/living/M) // This is a proc for the clicks + if (Victim == M || src == M) + Feedstop() + return + + if (Victim) + to_chat(src, "I am already feeding...") + return + + var t = invalidFeedTarget(M) + if (t) + to_chat(src,t) + return + + Feedon(M) + +/mob/living/carbon/slime/proc/invalidFeedTarget(var/mob/living/M) + if (!M || !istype(M)) + return "This subject is incomparable..." + if (istype(M, /mob/living/carbon/slime)) // No cannibalism... yet + return "I cannot feed on other slimes..." + if (!Adjacent(M)) + return "This subject is too far away..." + if (istype(M, /mob/living/carbon) && M.getCloneLoss() >= M.getMaxHealth() * 1.5 || istype(M, /mob/living/simple_mob) && M.stat == DEAD) + return "This subject does not have an edible life energy..." + for(var/mob/living/carbon/slime/met in view()) + if(met.Victim == M && met != src) + return "The [met.name] is already feeding on this subject..." + return 0 + +/mob/living/carbon/slime/proc/Feedon(var/mob/living/M) + Victim = M + loc = M.loc + canmove = 0 + anchored = TRUE + + regenerate_icons() + + while(Victim && !invalidFeedTarget(M) && stat != 2) + canmove = 0 + + if(Adjacent(M)) + UpdateFeed(M) + + if(istype(M, /mob/living/carbon)) + Victim.adjustCloneLoss(rand(5,6)) + Victim.adjustToxLoss(rand(1,2)) + if(Victim.health <= 0) + Victim.adjustToxLoss(rand(2,4)) + + else if(istype(M, /mob/living/simple_mob)) + Victim.adjustBruteLoss(is_adult ? rand(7, 15) : rand(4, 12)) + + else + to_chat(src, "[pick("This subject is incompatible", "This subject does not have a life energy", "This subject is empty", "I am not satisfied", "I can not feed from this subject", "I do not feel nourished", "This subject is not food")]...") + Feedstop() + break + + if(prob(15) && M.client && istype(M, /mob/living/carbon)) + var/painMes = pick("You can feel your body becoming weak!", "You feel like you're about to die!", "You feel every part of your body screaming in agony!", "A low, rolling pain passes through your body!", "Your body feels as if it's falling apart!", "You feel extremely weak!", "A sharp, deep pain bathes every inch of your body!") + if (ishuman(M)) + var/mob/living/carbon/human/H = M + H.custom_pain(painMes, 100) + else if (istype(M, /mob/living/carbon)) + var/mob/living/carbon/C = M + if (C.can_feel_pain()) + to_chat(M, "[painMes]") + + gain_nutrition(rand(20,25)) + + adjustOxyLoss(-10) //Heal yourself + adjustBruteLoss(-10) + adjustFireLoss(-10) + adjustCloneLoss(-10) + updatehealth() + if(Victim) + Victim.updatehealth() + + sleep(30) // Deal damage every 3 seconds + else + break + + canmove = 1 + anchored = FALSE + + if(M && invalidFeedTarget(M)) // This means that the slime drained the victim + if(!client) + if(Victim && !rabid && !attacked && Victim.LAssailant && Victim.LAssailant != Victim && prob(50)) + if(!(Victim.LAssailant in Friends)) + Friends[Victim.LAssailant] = 1 + else + ++Friends[Victim.LAssailant] + + else + to_chat(src, "This subject does not have a strong enough life energy anymore...") + + Victim = null + +/mob/living/carbon/slime/proc/Feedstop() + if(Victim) + if(Victim.client) + to_chat(Victim, "[src] has let go of your head!") + Victim = null + +/mob/living/carbon/slime/proc/UpdateFeed(var/mob/M) + if(Victim) + if(Victim == M) + loc = M.loc // simple "attach to head" effect! + +/mob/living/carbon/slime/verb/Evolve() + set category = "Slime" + set desc = "This will let you evolve from baby to adult slime." + + if(stat) + to_chat(src, "I must be conscious to do this...") + return + + if(!is_adult) + if(amount_grown >= 10) + is_adult = 1 + maxHealth = 200 + amount_grown = 0 + regenerate_icons() + name = text("[colour] [is_adult ? "adult" : "baby"] slime ([number])") + else + to_chat(src, "I am not ready to evolve yet...") + else + to_chat(src, "I have already evolved...") + +/mob/living/carbon/slime/verb/Reproduce() + set category = "Slime" + set desc = "This will make you split into four Slimes." + + if(stat) + to_chat(src, "I must be conscious to do this...") + return + + if(is_adult) + if(amount_grown >= 10) + if(stat) + to_chat(src, "I must be conscious to do this...") + return + + var/list/babies = list() + var/new_nutrition = round(nutrition * 0.9) + var/new_powerlevel = round(powerlevel / 4) + for(var/i = 1, i <= 4, i++) + var/t = colour + if(prob(mutation_chance)) + t = slime_mutation[rand(1,4)] + var/mob/living/carbon/slime/M = new /mob/living/carbon/slime/(loc, t) + if(ckey) M.nutrition = new_nutrition //Player slimes are more robust at spliting. Once an oversight of poor copypasta, now a feature! + M.powerlevel = new_powerlevel + if(i != 1) step_away(M, src) + M.Friends = Friends.Copy() + babies += M + feedback_add_details("slime_babies_born","slimebirth_[replacetext(M.colour," ","_")]") + + var/mob/living/carbon/slime/new_slime = pick(babies) + new_slime.universal_speak = universal_speak + if(src.mind) + src.mind.transfer_to(new_slime) + else + new_slime.key = src.key + qdel(src) + else + to_chat(src, "I am not ready to reproduce yet...") + else + to_chat(src, "I am not old enough to reproduce yet...") diff --git a/code/modules/mob/living/carbon/resist.dm b/code/modules/mob/living/carbon/resist.dm index 8af9d9e20d6..5434b31c44c 100644 --- a/code/modules/mob/living/carbon/resist.dm +++ b/code/modules/mob/living/carbon/resist.dm @@ -1,99 +1,99 @@ -/mob/living/carbon/resist_fire() - adjust_fire_stacks(-1.2) - Weaken(3) - spin(32,2) - visible_message( - "[src] rolls on the floor, trying to put themselves out!", - "You stop, drop, and roll!" - ) - sleep(30) - if(fire_stacks <= 0) - visible_message( - "[src] has successfully extinguished themselves!", - "You extinguish yourself." - ) - ExtinguishMob() - return TRUE - -/mob/living/carbon/resist_restraints() - var/obj/item/I = null - if(handcuffed) - I = handcuffed - else if(legcuffed) - I = legcuffed - - if(I) - setClickCooldown(100) - cuff_resist(I, cuff_break = can_break_cuffs()) - -/mob/living/carbon/proc/reduce_cuff_time() - return FALSE - -/mob/living/carbon/proc/cuff_resist(obj/item/weapon/handcuffs/I, breakouttime = 1200, cuff_break = 0) - - if(istype(I)) - breakouttime = I.breakouttime - - var/displaytime = breakouttime / 10 - - var/reduceCuffTime = reduce_cuff_time() - if(reduceCuffTime) - breakouttime /= reduceCuffTime - displaytime /= reduceCuffTime - - if(cuff_break) - visible_message("[src] is trying to break [I]!", - "You attempt to break your [I]. (This will take around 5 seconds and you need to stand still)") - - if(do_after(src, 5 SECONDS, target = src, incapacitation_flags = INCAPACITATION_DEFAULT & ~INCAPACITATION_RESTRAINED)) - if(!I || buckled) - return - visible_message("[src] manages to break [I]!", - "You successfully break your [I].") - say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) - - if(I == handcuffed) - handcuffed = null - update_handcuffed() - else if(I == legcuffed) - legcuffed = null - update_inv_legcuffed() - - if(buckled && buckled.buckle_require_restraints) - buckled.unbuckle_mob() - - qdel(I) - else - to_chat(src, "You fail to break [I].") - return - - visible_message("[src] attempts to remove [I]!", - "You attempt to remove [I]. (This will take around [displaytime] seconds and you need to stand still)") - if(do_after(src, breakouttime, target = src, incapacitation_flags = INCAPACITATION_DISABLED & INCAPACITATION_KNOCKDOWN)) - visible_message("[src] manages to remove [I]!", - "You successfully remove [I].") - drop_from_inventory(I) - -/mob/living/carbon/resist_buckle() - if(!buckled) - return - - if(!restrained()) - return ..() - - setClickCooldown(100) - visible_message( - "[src] attempts to unbuckle themself!", - "You attempt to unbuckle yourself. (This will take around 2 minutes and you need to stand still)" - ) - - if(do_after(src, 2 MINUTES, incapacitation_flags = INCAPACITATION_DEFAULT & ~(INCAPACITATION_RESTRAINED | INCAPACITATION_BUCKLED_FULLY))) - if(!buckled) - return - visible_message("[src] manages to unbuckle themself!", - "You successfully unbuckle yourself.") - buckled.user_unbuckle_mob(src, src) - -/mob/living/carbon/proc/can_break_cuffs() - if(HULK in mutations) - return 1 +/mob/living/carbon/resist_fire() + adjust_fire_stacks(-1.2) + Weaken(3) + spin(32,2) + visible_message( + "[src] rolls on the floor, trying to put themselves out!", + "You stop, drop, and roll!" + ) + sleep(30) + if(fire_stacks <= 0) + visible_message( + "[src] has successfully extinguished themselves!", + "You extinguish yourself." + ) + ExtinguishMob() + return TRUE + +/mob/living/carbon/resist_restraints() + var/obj/item/I = null + if(handcuffed) + I = handcuffed + else if(legcuffed) + I = legcuffed + + if(I) + setClickCooldown(100) + cuff_resist(I, cuff_break = can_break_cuffs()) + +/mob/living/carbon/proc/reduce_cuff_time() + return FALSE + +/mob/living/carbon/proc/cuff_resist(obj/item/weapon/handcuffs/I, breakouttime = 1200, cuff_break = 0) + + if(istype(I)) + breakouttime = I.breakouttime + + var/displaytime = breakouttime / 10 + + var/reduceCuffTime = reduce_cuff_time() + if(reduceCuffTime) + breakouttime /= reduceCuffTime + displaytime /= reduceCuffTime + + if(cuff_break) + visible_message("[src] is trying to break [I]!", + "You attempt to break your [I]. (This will take around 5 seconds and you need to stand still)") + + if(do_after(src, 5 SECONDS, target = src, incapacitation_flags = INCAPACITATION_DEFAULT & ~INCAPACITATION_RESTRAINED)) + if(!I || buckled) + return + visible_message("[src] manages to break [I]!", + "You successfully break your [I].") + say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) + + if(I == handcuffed) + handcuffed = null + update_handcuffed() + else if(I == legcuffed) + legcuffed = null + update_inv_legcuffed() + + if(buckled && buckled.buckle_require_restraints) + buckled.unbuckle_mob() + + qdel(I) + else + to_chat(src, "You fail to break [I].") + return + + visible_message("[src] attempts to remove [I]!", + "You attempt to remove [I]. (This will take around [displaytime] seconds and you need to stand still)") + if(do_after(src, breakouttime, target = src, incapacitation_flags = INCAPACITATION_DISABLED & INCAPACITATION_KNOCKDOWN)) + visible_message("[src] manages to remove [I]!", + "You successfully remove [I].") + drop_from_inventory(I) + +/mob/living/carbon/resist_buckle() + if(!buckled) + return + + if(!restrained()) + return ..() + + setClickCooldown(100) + visible_message( + "[src] attempts to unbuckle themself!", + "You attempt to unbuckle yourself. (This will take around 2 minutes and you need to stand still)" + ) + + if(do_after(src, 2 MINUTES, incapacitation_flags = INCAPACITATION_DEFAULT & ~(INCAPACITATION_RESTRAINED | INCAPACITATION_BUCKLED_FULLY))) + if(!buckled) + return + visible_message("[src] manages to unbuckle themself!", + "You successfully unbuckle yourself.") + buckled.user_unbuckle_mob(src, src) + +/mob/living/carbon/proc/can_break_cuffs() + if(HULK in mutations) + return 1 diff --git a/code/modules/mob/living/carbon/viruses.dm b/code/modules/mob/living/carbon/viruses.dm index 2f52dcd1cb4..f57bc878a00 100644 --- a/code/modules/mob/living/carbon/viruses.dm +++ b/code/modules/mob/living/carbon/viruses.dm @@ -1,48 +1,48 @@ -/mob/living/carbon/proc/handle_viruses() - - if(status_flags & GODMODE) return 0 //godmode - - if(bodytemperature > 406) - for (var/ID in virus2) - var/datum/disease2/disease/V = virus2[ID] - V.cure(src) - - if(life_tick % 3) //don't spam checks over all objects in view every tick. - for(var/obj/effect/decal/cleanable/O in view(1,src)) - if(istype(O,/obj/effect/decal/cleanable/blood)) - var/obj/effect/decal/cleanable/blood/B = O - if(B.virus2.len) - for (var/ID in B.virus2) - var/datum/disease2/disease/V = B.virus2[ID] - infect_virus2(src,V) - - else if(istype(O,/obj/effect/decal/cleanable/mucus)) - var/obj/effect/decal/cleanable/mucus/M = O - if(M.virus2.len) - for (var/ID in M.virus2) - var/datum/disease2/disease/V = M.virus2[ID] - infect_virus2(src,V) - - else if(istype(O,/obj/effect/decal/cleanable/vomit)) - var/obj/effect/decal/cleanable/vomit/Vom = O - if(Vom.virus2.len) - for (var/ID in Vom.virus2) - var/datum/disease2/disease/V = Vom.virus2[ID] - infect_virus2(src,V) - - if(virus2.len) - for (var/ID in virus2) - var/datum/disease2/disease/V = virus2[ID] - if(isnull(V)) // Trying to figure out a runtime error that keeps repeating - CRASH("virus2 nulled before calling activate()") - else - V.activate(src) - // activate may have deleted the virus - if(!V) continue - - // check if we're immune - var/list/common_antibodies = V.antigen & src.antibodies - if(common_antibodies.len) - V.dead = 1 - +/mob/living/carbon/proc/handle_viruses() + + if(status_flags & GODMODE) return 0 //godmode + + if(bodytemperature > 406) + for (var/ID in virus2) + var/datum/disease2/disease/V = virus2[ID] + V.cure(src) + + if(life_tick % 3) //don't spam checks over all objects in view every tick. + for(var/obj/effect/decal/cleanable/O in view(1,src)) + if(istype(O,/obj/effect/decal/cleanable/blood)) + var/obj/effect/decal/cleanable/blood/B = O + if(B.virus2.len) + for (var/ID in B.virus2) + var/datum/disease2/disease/V = B.virus2[ID] + infect_virus2(src,V) + + else if(istype(O,/obj/effect/decal/cleanable/mucus)) + var/obj/effect/decal/cleanable/mucus/M = O + if(M.virus2.len) + for (var/ID in M.virus2) + var/datum/disease2/disease/V = M.virus2[ID] + infect_virus2(src,V) + + else if(istype(O,/obj/effect/decal/cleanable/vomit)) + var/obj/effect/decal/cleanable/vomit/Vom = O + if(Vom.virus2.len) + for (var/ID in Vom.virus2) + var/datum/disease2/disease/V = Vom.virus2[ID] + infect_virus2(src,V) + + if(virus2.len) + for (var/ID in virus2) + var/datum/disease2/disease/V = virus2[ID] + if(isnull(V)) // Trying to figure out a runtime error that keeps repeating + CRASH("virus2 nulled before calling activate()") + else + V.activate(src) + // activate may have deleted the virus + if(!V) continue + + // check if we're immune + var/list/common_antibodies = V.antigen & src.antibodies + if(common_antibodies.len) + V.dead = 1 + return \ No newline at end of file diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index babe6c43000..3783df08274 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -1,194 +1,194 @@ - -/* - apply_damage(a,b,c) - args - a:damage - How much damage to take - b:damage_type - What type of damage to take, brute, burn - c:def_zone - Where to take the damage if its brute or burn - Returns - standard 0 if fail -*/ -/mob/living/proc/apply_damage(var/damage = 0,var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/used_weapon = null, var/sharp = FALSE, var/edge = FALSE, var/obj/used_weapon = null) - if(Debug2) - to_world_log("## DEBUG: apply_damage() was called on [src], with [damage] damage, and an armor value of [blocked].") - if(!damage || (blocked >= 100)) - return 0 - for(var/datum/modifier/M in modifiers) //MODIFIER STUFF. It's best to do this RIGHT before armor is calculated, so it's done here! This is the 'forcefield' defence. - if(damagetype == BRUTE && (!isnull(M.effective_brute_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_brute_resistance - continue - if((damagetype == BURN || damagetype == ELECTROCUTE)&& (!isnull(M.effective_fire_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_fire_resistance - continue - if(damagetype == TOX && (!isnull(M.effective_tox_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_tox_resistance - continue - if(damagetype == OXY && (!isnull(M.effective_oxy_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_oxy_resistance - continue - if(damagetype == CLONE && (!isnull(M.effective_clone_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_clone_resistance - continue - if(damagetype == HALLOSS && (!isnull(M.effective_hal_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_hal_resistance - continue - if(damagetype == SEARING && (!isnull(M.effective_fire_resistance) || !isnull(M.effective_brute_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - var/damage_mitigation = 0//Used for dual calculations. - if(!isnull(M.effective_fire_resistance)) - damage_mitigation += round((1/3)*damage * M.effective_fire_resistance) - if(!isnull(M.effective_brute_resistance)) - damage_mitigation += round((2/3)*damage * M.effective_brute_resistance) - damage -= damage_mitigation - continue - if(damagetype == BIOACID && (isSynthetic() && (!isnull(M.effective_fire_resistance))) || (!isSynthetic() && M.effective_tox_resistance)) - if(isSynthetic()) - damage = damage * M.effective_fire_resistance - else - damage = damage * M.effective_tox_resistance - continue - if(soaked) - if(soaked >= round(damage*0.8)) - damage -= round(damage*0.8) - else - damage -= soaked - - var/initial_blocked = blocked - - blocked = (100-blocked)/100 - switch(damagetype) - if(BRUTE) - adjustBruteLoss(damage * blocked) - if(BURN) - if(COLD_RESISTANCE in mutations) - damage = 0 - adjustFireLoss(damage * blocked) - if(SEARING) - apply_damage(round(damage / 3), BURN, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) - apply_damage(round(damage / 3 * 2), BRUTE, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) - if(TOX) - adjustToxLoss(damage * blocked) - if(OXY) - adjustOxyLoss(damage * blocked) - if(CLONE) - adjustCloneLoss(damage * blocked) - if(HALLOSS) - adjustHalLoss(damage * blocked) - if(ELECTROCUTE) - electrocute_act(damage, used_weapon, 1.0, def_zone) - if(BIOACID) - if(isSynthetic()) - apply_damage(damage, BURN, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) // Handle it as normal burn. - else - adjustToxLoss(damage * blocked) - if(ELECTROMAG) - damage = damage * blocked - switch(round(damage)) - if(91 to INFINITY) - emp_act(1) - if(76 to 90) - if(prob(50)) - emp_act(1) - else - emp_act(2) - if(61 to 75) - emp_act(2) - if(46 to 60) - if(prob(50)) - emp_act(2) - else - emp_act(3) - if(31 to 45) - emp_act(3) - if(16 to 30) - if(prob(50)) - emp_act(3) - else - emp_act(4) - if(0 to 15) - emp_act(4) - flash_weak_pain() - updatehealth() - return 1 - - -/mob/living/proc/apply_damages(var/brute = 0, var/burn = 0, var/tox = 0, var/oxy = 0, var/clone = 0, var/halloss = 0, var/def_zone = null, var/blocked = 0) - if(blocked >= 100) - return 0 - // INSERT MODIFIER CODE HERE... But no, really, only two things in the game use it, quad and viruses. The former is admin-only and the latter wouldn't be affected logically, but would if shield code was inerted here. If you really want, you can copy&paste the above and modify it to adjust brute/burn/etc. I do not advise this however. - if(brute) apply_damage(brute, BRUTE, def_zone, blocked) - if(burn) apply_damage(burn, BURN, def_zone, blocked) - if(tox) apply_damage(tox, TOX, def_zone, blocked) - if(oxy) apply_damage(oxy, OXY, def_zone, blocked) - if(clone) apply_damage(clone, CLONE, def_zone, blocked) - if(halloss) apply_damage(halloss, HALLOSS, def_zone, blocked) - return 1 - - - -/mob/living/proc/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/check_protection = 1) - if(Debug2) - to_world_log("## DEBUG: apply_effect() was called. The type of effect is [effecttype]. Blocked by [blocked].") - if(!effect || (blocked >= 100)) - return 0 - blocked = (100-blocked)/100 - - switch(effecttype) - if(STUN) - Stun(effect * blocked) - if(WEAKEN) - Weaken(effect * blocked) - if(PARALYZE) - Paralyse(effect * blocked) - if(AGONY) - halloss += max((effect * blocked), 0) // Useful for objects that cause "subdual" damage. PAIN! - if(IRRADIATE) - /* - var/rad_protection = check_protection ? getarmor(null, "rad")/100 : 0 - radiation += max((1-rad_protection)*effect/(blocked+1),0)//Rads auto check armor - */ - var/rad_protection = getarmor(null, "rad") - rad_protection = (100-rad_protection)/100 - radiation += max((effect * rad_protection), 0) - if(STUTTER) - if(status_flags & CANSTUN) // stun is usually associated with stutter - stuttering = max(stuttering,(effect * blocked)) - if(EYE_BLUR) - eye_blurry = max(eye_blurry,(effect * blocked)) - if(DROWSY) - drowsyness = max(drowsyness,(effect * blocked)) - updatehealth() - return 1 - - -/mob/living/proc/apply_effects(var/stun = 0, var/weaken = 0, var/paralyze = 0, var/irradiate = 0, var/stutter = 0, var/eyeblur = 0, var/drowsy = 0, var/agony = 0, var/blocked = 0, var/ignite = 0, var/flammable = 0) - if(blocked >= 100) - return 0 - if(stun) apply_effect(stun, STUN, blocked) - if(weaken) apply_effect(weaken, WEAKEN, blocked) - if(paralyze) apply_effect(paralyze, PARALYZE, blocked) - if(irradiate) apply_effect(irradiate, IRRADIATE, blocked) - if(stutter) apply_effect(stutter, STUTTER, blocked) - if(eyeblur) apply_effect(eyeblur, EYE_BLUR, blocked) - if(drowsy) apply_effect(drowsy, DROWSY, blocked) - if(agony) apply_effect(agony, AGONY, blocked) - if(flammable) adjust_fire_stacks(flammable) - if(ignite) - if(ignite >= 3) - add_modifier(/datum/modifier/fire/stack_managed/intense, 60 SECONDS) - else - add_modifier(/datum/modifier/fire/stack_managed, 45 * ignite SECONDS) - return 1 + +/* + apply_damage(a,b,c) + args + a:damage - How much damage to take + b:damage_type - What type of damage to take, brute, burn + c:def_zone - Where to take the damage if its brute or burn + Returns + standard 0 if fail +*/ +/mob/living/proc/apply_damage(var/damage = 0,var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/used_weapon = null, var/sharp = FALSE, var/edge = FALSE, var/obj/used_weapon = null) + if(Debug2) + to_world_log("## DEBUG: apply_damage() was called on [src], with [damage] damage, and an armor value of [blocked].") + if(!damage || (blocked >= 100)) + return 0 + for(var/datum/modifier/M in modifiers) //MODIFIER STUFF. It's best to do this RIGHT before armor is calculated, so it's done here! This is the 'forcefield' defence. + if(damagetype == BRUTE && (!isnull(M.effective_brute_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_brute_resistance + continue + if((damagetype == BURN || damagetype == ELECTROCUTE)&& (!isnull(M.effective_fire_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_fire_resistance + continue + if(damagetype == TOX && (!isnull(M.effective_tox_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_tox_resistance + continue + if(damagetype == OXY && (!isnull(M.effective_oxy_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_oxy_resistance + continue + if(damagetype == CLONE && (!isnull(M.effective_clone_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_clone_resistance + continue + if(damagetype == HALLOSS && (!isnull(M.effective_hal_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_hal_resistance + continue + if(damagetype == SEARING && (!isnull(M.effective_fire_resistance) || !isnull(M.effective_brute_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + var/damage_mitigation = 0//Used for dual calculations. + if(!isnull(M.effective_fire_resistance)) + damage_mitigation += round((1/3)*damage * M.effective_fire_resistance) + if(!isnull(M.effective_brute_resistance)) + damage_mitigation += round((2/3)*damage * M.effective_brute_resistance) + damage -= damage_mitigation + continue + if(damagetype == BIOACID && (isSynthetic() && (!isnull(M.effective_fire_resistance))) || (!isSynthetic() && M.effective_tox_resistance)) + if(isSynthetic()) + damage = damage * M.effective_fire_resistance + else + damage = damage * M.effective_tox_resistance + continue + if(soaked) + if(soaked >= round(damage*0.8)) + damage -= round(damage*0.8) + else + damage -= soaked + + var/initial_blocked = blocked + + blocked = (100-blocked)/100 + switch(damagetype) + if(BRUTE) + adjustBruteLoss(damage * blocked) + if(BURN) + if(COLD_RESISTANCE in mutations) + damage = 0 + adjustFireLoss(damage * blocked) + if(SEARING) + apply_damage(round(damage / 3), BURN, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) + apply_damage(round(damage / 3 * 2), BRUTE, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) + if(TOX) + adjustToxLoss(damage * blocked) + if(OXY) + adjustOxyLoss(damage * blocked) + if(CLONE) + adjustCloneLoss(damage * blocked) + if(HALLOSS) + adjustHalLoss(damage * blocked) + if(ELECTROCUTE) + electrocute_act(damage, used_weapon, 1.0, def_zone) + if(BIOACID) + if(isSynthetic()) + apply_damage(damage, BURN, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) // Handle it as normal burn. + else + adjustToxLoss(damage * blocked) + if(ELECTROMAG) + damage = damage * blocked + switch(round(damage)) + if(91 to INFINITY) + emp_act(1) + if(76 to 90) + if(prob(50)) + emp_act(1) + else + emp_act(2) + if(61 to 75) + emp_act(2) + if(46 to 60) + if(prob(50)) + emp_act(2) + else + emp_act(3) + if(31 to 45) + emp_act(3) + if(16 to 30) + if(prob(50)) + emp_act(3) + else + emp_act(4) + if(0 to 15) + emp_act(4) + flash_weak_pain() + updatehealth() + return 1 + + +/mob/living/proc/apply_damages(var/brute = 0, var/burn = 0, var/tox = 0, var/oxy = 0, var/clone = 0, var/halloss = 0, var/def_zone = null, var/blocked = 0) + if(blocked >= 100) + return 0 + // INSERT MODIFIER CODE HERE... But no, really, only two things in the game use it, quad and viruses. The former is admin-only and the latter wouldn't be affected logically, but would if shield code was inerted here. If you really want, you can copy&paste the above and modify it to adjust brute/burn/etc. I do not advise this however. + if(brute) apply_damage(brute, BRUTE, def_zone, blocked) + if(burn) apply_damage(burn, BURN, def_zone, blocked) + if(tox) apply_damage(tox, TOX, def_zone, blocked) + if(oxy) apply_damage(oxy, OXY, def_zone, blocked) + if(clone) apply_damage(clone, CLONE, def_zone, blocked) + if(halloss) apply_damage(halloss, HALLOSS, def_zone, blocked) + return 1 + + + +/mob/living/proc/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/check_protection = 1) + if(Debug2) + to_world_log("## DEBUG: apply_effect() was called. The type of effect is [effecttype]. Blocked by [blocked].") + if(!effect || (blocked >= 100)) + return 0 + blocked = (100-blocked)/100 + + switch(effecttype) + if(STUN) + Stun(effect * blocked) + if(WEAKEN) + Weaken(effect * blocked) + if(PARALYZE) + Paralyse(effect * blocked) + if(AGONY) + halloss += max((effect * blocked), 0) // Useful for objects that cause "subdual" damage. PAIN! + if(IRRADIATE) + /* + var/rad_protection = check_protection ? getarmor(null, "rad")/100 : 0 + radiation += max((1-rad_protection)*effect/(blocked+1),0)//Rads auto check armor + */ + var/rad_protection = getarmor(null, "rad") + rad_protection = (100-rad_protection)/100 + radiation += max((effect * rad_protection), 0) + if(STUTTER) + if(status_flags & CANSTUN) // stun is usually associated with stutter + stuttering = max(stuttering,(effect * blocked)) + if(EYE_BLUR) + eye_blurry = max(eye_blurry,(effect * blocked)) + if(DROWSY) + drowsyness = max(drowsyness,(effect * blocked)) + updatehealth() + return 1 + + +/mob/living/proc/apply_effects(var/stun = 0, var/weaken = 0, var/paralyze = 0, var/irradiate = 0, var/stutter = 0, var/eyeblur = 0, var/drowsy = 0, var/agony = 0, var/blocked = 0, var/ignite = 0, var/flammable = 0) + if(blocked >= 100) + return 0 + if(stun) apply_effect(stun, STUN, blocked) + if(weaken) apply_effect(weaken, WEAKEN, blocked) + if(paralyze) apply_effect(paralyze, PARALYZE, blocked) + if(irradiate) apply_effect(irradiate, IRRADIATE, blocked) + if(stutter) apply_effect(stutter, STUTTER, blocked) + if(eyeblur) apply_effect(eyeblur, EYE_BLUR, blocked) + if(drowsy) apply_effect(drowsy, DROWSY, blocked) + if(agony) apply_effect(agony, AGONY, blocked) + if(flammable) adjust_fire_stacks(flammable) + if(ignite) + if(ignite >= 3) + add_modifier(/datum/modifier/fire/stack_managed/intense, 60 SECONDS) + else + add_modifier(/datum/modifier/fire/stack_managed, 45 * ignite SECONDS) + return 1 diff --git a/code/modules/mob/living/default_language.dm b/code/modules/mob/living/default_language.dm index eefe4929f9f..c83980f18f1 100644 --- a/code/modules/mob/living/default_language.dm +++ b/code/modules/mob/living/default_language.dm @@ -1,36 +1,36 @@ -/mob/living - var/datum/language/default_language - -/mob/living/verb/set_default_language(language as null|anything in languages) - set name = "Set Default Language" - set category = "IC" - - if (only_species_language && language != GLOB.all_languages[src.species_language]) - to_chat(src, "You can only speak your species language, [src.species_language].") - return 0 - - if(language == GLOB.all_languages[src.species_language]) - to_chat(src, "You will now speak your standard default language, [language ? language : "common"], if you do not specify a language when speaking.") - else if (language) - - if(language && !can_speak(language)) - to_chat(src, "You are unable to speak that language.") - return - - to_chat(src, "You will now speak [language] if you do not specify a language when speaking.") - else - to_chat(src, "You will now speak whatever your standard default language is if you do not specify one when speaking.") - default_language = language - -// Silicons can't neccessarily speak everything in their languages list -/mob/living/silicon/set_default_language(language as null|anything in speech_synthesizer_langs) - ..() - -/mob/living/verb/check_default_language() - set name = "Check Default Language" - set category = "IC" - - if(default_language) - to_chat(src, "You are currently speaking [default_language] by default.") - else - to_chat(src, "Your current default language is your species or mob type default.") +/mob/living + var/datum/language/default_language + +/mob/living/verb/set_default_language(language as null|anything in languages) + set name = "Set Default Language" + set category = "IC" + + if (only_species_language && language != GLOB.all_languages[src.species_language]) + to_chat(src, "You can only speak your species language, [src.species_language].") + return 0 + + if(language == GLOB.all_languages[src.species_language]) + to_chat(src, "You will now speak your standard default language, [language ? language : "common"], if you do not specify a language when speaking.") + else if (language) + + if(language && !can_speak(language)) + to_chat(src, "You are unable to speak that language.") + return + + to_chat(src, "You will now speak [language] if you do not specify a language when speaking.") + else + to_chat(src, "You will now speak whatever your standard default language is if you do not specify one when speaking.") + default_language = language + +// Silicons can't neccessarily speak everything in their languages list +/mob/living/silicon/set_default_language(language as null|anything in speech_synthesizer_langs) + ..() + +/mob/living/verb/check_default_language() + set name = "Check Default Language" + set category = "IC" + + if(default_language) + to_chat(src, "You are currently speaking [default_language] by default.") + else + to_chat(src, "Your current default language is your species or mob type default.") diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index eb7462967e1..319fcce97e4 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -1,284 +1,284 @@ -/mob/living/Life() - set invisibility = 0 - set background = BACKGROUND_ENABLED - - ..() - - if (transforming) - return - handle_modifiers() //VOREStation Edit - Needs to be done even if in nullspace. - if(!loc) - return - - var/datum/gas_mixture/environment - if(isbelly(loc)) - environment = loc.return_air_for_internal_lifeform(src) - else - environment = loc.return_air() - - //handle_modifiers() // Do this early since it might affect other things later. //VOREStation Edit - - handle_light() - - if(stat != DEAD) - //Breathing, if applicable - handle_breathing() - - //Mutations and radiation - handle_mutations_and_radiation() - - - - //Blood - handle_blood() - - //Random events (vomiting etc) - handle_random_events() - - . = 1 - - //Chemicals in the body, this is moved over here so that blood can be added after death - handle_chemicals_in_body() - - //Handle temperature/pressure differences between body and environment - if(environment) - handle_environment(environment) - - //Check if we're on fire - handle_fire() - - if(client && !(client.prefs.ambience_freq == 0)) // Handle re-running ambience to mobs if they've remained in an area, AND have an active client assigned to them, and do not have repeating ambience disabled. - handle_ambience() - - //stuff in the stomach - //handle_stomach() //VOREStation Code - - update_gravity(mob_has_gravity()) - - update_pulling() - - for(var/obj/item/weapon/grab/G in src) - G.process() - - if(handle_regular_status_updates()) // Status & health update, are we dead or alive etc. - handle_disabilities() // eye, ear, brain damages - handle_statuses() //all special effects, stunned, weakened, jitteryness, hallucination, sleeping, etc - - handle_actions() - - update_canmove() - - handle_regular_hud_updates() - - handle_vision() - - handle_tf_holder() //VOREStation Addition - -/mob/living/proc/handle_breathing() - return - -/mob/living/proc/handle_mutations_and_radiation() - return - -/mob/living/proc/handle_chemicals_in_body() - return - -/mob/living/proc/handle_blood() - return - -/mob/living/proc/handle_random_events() - return - -/mob/living/proc/handle_environment(var/datum/gas_mixture/environment) - return - -/mob/living/proc/handle_stomach() - return - -/mob/living/proc/handle_ambience() // If you're in an ambient area and have not moved out of it for x time as configured per-client, and do not have it disabled, we're going to play ambience again to you, to help break up the silence. - if(world.time >= (lastareachange + client.prefs.ambience_freq MINUTES)) // Every 5 minutes (by default, set per-client), we're going to run a 35% chance (by default, also set per-client) to play ambience. - var/area/A = get_area(src) - if(A) - lastareachange = world.time // This will refresh the last area change to prevent this call happening LITERALLY every life tick. - A.play_ambience(src, initial = FALSE) - -/mob/living/proc/update_pulling() - if(pulling) - if(incapacitated()) - stop_pulling() - -//This updates the health and status of the mob (conscious, unconscious, dead) -/mob/living/proc/handle_regular_status_updates() - updatehealth() - if(stat != DEAD) - if(paralysis) - set_stat(UNCONSCIOUS) - else if (status_flags & FAKEDEATH) - set_stat(UNCONSCIOUS) - else - set_stat(CONSCIOUS) - return 1 - -/mob/living/proc/handle_statuses() - handle_stunned() - handle_weakened() - handle_paralysed() - handle_stuttering() - handle_silent() - handle_drugged() - handle_slurring() - handle_confused() - -/mob/living/proc/handle_stunned() - if(stunned) - AdjustStunned(-1) - throw_alert("stunned", /obj/screen/alert/stunned) - else - clear_alert("stunned") - return stunned - -/mob/living/proc/handle_weakened() - if(weakened) - AdjustWeakened(-1) - throw_alert("weakened", /obj/screen/alert/weakened) - else - clear_alert("weakened") - return weakened - -/mob/living/proc/handle_stuttering() - if(stuttering) - stuttering = max(stuttering-1, 0) - return stuttering - -/mob/living/proc/handle_silent() - if(silent) - silent = max(silent-1, 0) - return silent - -/mob/living/proc/handle_drugged() - if(druggy) - druggy = max(druggy-1, 0) - throw_alert("high", /obj/screen/alert/high) - else - clear_alert("high") - return druggy - -/mob/living/proc/handle_slurring() - if(slurring) - slurring = max(slurring-1, 0) - return slurring - -/mob/living/proc/handle_paralysed() - if(paralysis) - AdjustParalysis(-1) - throw_alert("paralyzed", /obj/screen/alert/paralyzed) - else - clear_alert("paralyzed") - return paralysis - -/mob/living/proc/handle_confused() - if(confused) - AdjustConfused(-1) - throw_alert("confused", /obj/screen/alert/confused) - else - clear_alert("confused") - return confused - -/mob/living/proc/handle_disabilities() - //Eyes - if(sdisabilities & BLIND || stat) //blindness from disability or unconsciousness doesn't get better on its own - SetBlinded(1) - throw_alert("blind", /obj/screen/alert/blind) - else if(eye_blind) //blindness, heals slowly over time - AdjustBlinded(-1) - throw_alert("blind", /obj/screen/alert/blind) - else - clear_alert("blind") - - if(eye_blurry) //blurry eyes heal slowly - eye_blurry = max(eye_blurry-1, 0) - - //Ears - if(sdisabilities & DEAF) //disabled-deaf, doesn't get better on its own - setEarDamage(-1, max(ear_deaf, 1)) - else - // deafness heals slowly over time, unless ear_damage is over 100 - if(ear_damage < 100) - adjustEarDamage(-0.05,-1) - -/mob/living/handle_regular_hud_updates() - if(!client) - return 0 - ..() - - handle_darksight() - handle_hud_icons() - - return 1 - -/mob/living/proc/update_sight() - if(!seedarkness) - see_invisible = SEE_INVISIBLE_NOLIGHTING - else - see_invisible = initial(see_invisible) - - sight = initial(sight) - - for(var/datum/modifier/M in modifiers) - if(!isnull(M.vision_flags)) - sight |= M.vision_flags - - return - -/mob/living/proc/handle_hud_icons() - handle_hud_icons_health() - return - -/mob/living/proc/handle_hud_icons_health() - return - -/mob/living/proc/handle_light() - if(glow_override) - return FALSE - - if(instability >= TECHNOMANCER_INSTABILITY_MIN_GLOW) - var/distance = round(sqrt(instability / 2)) - if(distance) - set_light(distance, distance * 4, l_color = "#660066") - return TRUE - - else if(glow_toggle) - set_light(glow_range, glow_intensity, glow_color) - - else - set_light(0) - return FALSE - -/mob/living/proc/handle_darksight() - if(!seedarkness) //Cheap 'always darksight' var - dsoverlay.alpha = 255 - return - - var/darksightedness = min(see_in_dark/world.view,1.0) //A ratio of how good your darksight is, from 'nada' to 'really darn good' - var/current = dsoverlay.alpha/255 //Our current adjustedness - - var/brightness = 0.0 //We'll assume it's superdark if we can't find something else. - - if(isturf(loc)) - var/turf/T = loc //Will be true 99% of the time, thus avoiding the whole elif chain - brightness = T.get_lumcount() - - //Snowflake treatment of potential locations - else if(istype(loc,/obj/mecha)) //I imagine there's like displays and junk in there. Use the lights! - brightness = 1 - else if(istype(loc,/obj/item/weapon/holder)) //Poor carried teshari and whatnot should adjust appropriately - var/turf/T = get_turf(src) - brightness = T.get_lumcount() - - var/darkness = 1-brightness //Silly, I know, but 'alpha' and 'darkness' go the same direction on a number line - var/adjust_to = min(darkness,darksightedness)//Capped by how darksighted they are - var/distance = abs(current-adjust_to) //Used for how long to animate for - if(distance < 0.01) return //We're already all set - - //to_world("[src] in B:[round(brightness,0.1)] C:[round(current,0.1)] A2:[round(adjust_to,0.1)] D:[round(distance,0.01)] T:[round(distance*10 SECONDS,0.1)]") - animate(dsoverlay, alpha = (adjust_to*255), time = (distance*10 SECONDS)) +/mob/living/Life() + set invisibility = 0 + set background = BACKGROUND_ENABLED + + ..() + + if (transforming) + return + handle_modifiers() //VOREStation Edit - Needs to be done even if in nullspace. + if(!loc) + return + + var/datum/gas_mixture/environment + if(isbelly(loc)) + environment = loc.return_air_for_internal_lifeform(src) + else + environment = loc.return_air() + + //handle_modifiers() // Do this early since it might affect other things later. //VOREStation Edit + + handle_light() + + if(stat != DEAD) + //Breathing, if applicable + handle_breathing() + + //Mutations and radiation + handle_mutations_and_radiation() + + + + //Blood + handle_blood() + + //Random events (vomiting etc) + handle_random_events() + + . = 1 + + //Chemicals in the body, this is moved over here so that blood can be added after death + handle_chemicals_in_body() + + //Handle temperature/pressure differences between body and environment + if(environment) + handle_environment(environment) + + //Check if we're on fire + handle_fire() + + if(client && !(client.prefs.ambience_freq == 0)) // Handle re-running ambience to mobs if they've remained in an area, AND have an active client assigned to them, and do not have repeating ambience disabled. + handle_ambience() + + //stuff in the stomach + //handle_stomach() //VOREStation Code + + update_gravity(mob_has_gravity()) + + update_pulling() + + for(var/obj/item/weapon/grab/G in src) + G.process() + + if(handle_regular_status_updates()) // Status & health update, are we dead or alive etc. + handle_disabilities() // eye, ear, brain damages + handle_statuses() //all special effects, stunned, weakened, jitteryness, hallucination, sleeping, etc + + handle_actions() + + update_canmove() + + handle_regular_hud_updates() + + handle_vision() + + handle_tf_holder() //VOREStation Addition + +/mob/living/proc/handle_breathing() + return + +/mob/living/proc/handle_mutations_and_radiation() + return + +/mob/living/proc/handle_chemicals_in_body() + return + +/mob/living/proc/handle_blood() + return + +/mob/living/proc/handle_random_events() + return + +/mob/living/proc/handle_environment(var/datum/gas_mixture/environment) + return + +/mob/living/proc/handle_stomach() + return + +/mob/living/proc/handle_ambience() // If you're in an ambient area and have not moved out of it for x time as configured per-client, and do not have it disabled, we're going to play ambience again to you, to help break up the silence. + if(world.time >= (lastareachange + client.prefs.ambience_freq MINUTES)) // Every 5 minutes (by default, set per-client), we're going to run a 35% chance (by default, also set per-client) to play ambience. + var/area/A = get_area(src) + if(A) + lastareachange = world.time // This will refresh the last area change to prevent this call happening LITERALLY every life tick. + A.play_ambience(src, initial = FALSE) + +/mob/living/proc/update_pulling() + if(pulling) + if(incapacitated()) + stop_pulling() + +//This updates the health and status of the mob (conscious, unconscious, dead) +/mob/living/proc/handle_regular_status_updates() + updatehealth() + if(stat != DEAD) + if(paralysis) + set_stat(UNCONSCIOUS) + else if (status_flags & FAKEDEATH) + set_stat(UNCONSCIOUS) + else + set_stat(CONSCIOUS) + return 1 + +/mob/living/proc/handle_statuses() + handle_stunned() + handle_weakened() + handle_paralysed() + handle_stuttering() + handle_silent() + handle_drugged() + handle_slurring() + handle_confused() + +/mob/living/proc/handle_stunned() + if(stunned) + AdjustStunned(-1) + throw_alert("stunned", /obj/screen/alert/stunned) + else + clear_alert("stunned") + return stunned + +/mob/living/proc/handle_weakened() + if(weakened) + AdjustWeakened(-1) + throw_alert("weakened", /obj/screen/alert/weakened) + else + clear_alert("weakened") + return weakened + +/mob/living/proc/handle_stuttering() + if(stuttering) + stuttering = max(stuttering-1, 0) + return stuttering + +/mob/living/proc/handle_silent() + if(silent) + silent = max(silent-1, 0) + return silent + +/mob/living/proc/handle_drugged() + if(druggy) + druggy = max(druggy-1, 0) + throw_alert("high", /obj/screen/alert/high) + else + clear_alert("high") + return druggy + +/mob/living/proc/handle_slurring() + if(slurring) + slurring = max(slurring-1, 0) + return slurring + +/mob/living/proc/handle_paralysed() + if(paralysis) + AdjustParalysis(-1) + throw_alert("paralyzed", /obj/screen/alert/paralyzed) + else + clear_alert("paralyzed") + return paralysis + +/mob/living/proc/handle_confused() + if(confused) + AdjustConfused(-1) + throw_alert("confused", /obj/screen/alert/confused) + else + clear_alert("confused") + return confused + +/mob/living/proc/handle_disabilities() + //Eyes + if(sdisabilities & BLIND || stat) //blindness from disability or unconsciousness doesn't get better on its own + SetBlinded(1) + throw_alert("blind", /obj/screen/alert/blind) + else if(eye_blind) //blindness, heals slowly over time + AdjustBlinded(-1) + throw_alert("blind", /obj/screen/alert/blind) + else + clear_alert("blind") + + if(eye_blurry) //blurry eyes heal slowly + eye_blurry = max(eye_blurry-1, 0) + + //Ears + if(sdisabilities & DEAF) //disabled-deaf, doesn't get better on its own + setEarDamage(-1, max(ear_deaf, 1)) + else + // deafness heals slowly over time, unless ear_damage is over 100 + if(ear_damage < 100) + adjustEarDamage(-0.05,-1) + +/mob/living/handle_regular_hud_updates() + if(!client) + return 0 + ..() + + handle_darksight() + handle_hud_icons() + + return 1 + +/mob/living/proc/update_sight() + if(!seedarkness) + see_invisible = SEE_INVISIBLE_NOLIGHTING + else + see_invisible = initial(see_invisible) + + sight = initial(sight) + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.vision_flags)) + sight |= M.vision_flags + + return + +/mob/living/proc/handle_hud_icons() + handle_hud_icons_health() + return + +/mob/living/proc/handle_hud_icons_health() + return + +/mob/living/proc/handle_light() + if(glow_override) + return FALSE + + if(instability >= TECHNOMANCER_INSTABILITY_MIN_GLOW) + var/distance = round(sqrt(instability / 2)) + if(distance) + set_light(distance, distance * 4, l_color = "#660066") + return TRUE + + else if(glow_toggle) + set_light(glow_range, glow_intensity, glow_color) + + else + set_light(0) + return FALSE + +/mob/living/proc/handle_darksight() + if(!seedarkness) //Cheap 'always darksight' var + dsoverlay.alpha = 255 + return + + var/darksightedness = min(see_in_dark/world.view,1.0) //A ratio of how good your darksight is, from 'nada' to 'really darn good' + var/current = dsoverlay.alpha/255 //Our current adjustedness + + var/brightness = 0.0 //We'll assume it's superdark if we can't find something else. + + if(isturf(loc)) + var/turf/T = loc //Will be true 99% of the time, thus avoiding the whole elif chain + brightness = T.get_lumcount() + + //Snowflake treatment of potential locations + else if(istype(loc,/obj/mecha)) //I imagine there's like displays and junk in there. Use the lights! + brightness = 1 + else if(istype(loc,/obj/item/weapon/holder)) //Poor carried teshari and whatnot should adjust appropriately + var/turf/T = get_turf(src) + brightness = T.get_lumcount() + + var/darkness = 1-brightness //Silly, I know, but 'alpha' and 'darkness' go the same direction on a number line + var/adjust_to = min(darkness,darksightedness)//Capped by how darksighted they are + var/distance = abs(current-adjust_to) //Used for how long to animate for + if(distance < 0.01) return //We're already all set + + //to_world("[src] in B:[round(brightness,0.1)] C:[round(current,0.1)] A2:[round(adjust_to,0.1)] D:[round(distance,0.01)] T:[round(distance*10 SECONDS,0.1)]") + animate(dsoverlay, alpha = (adjust_to*255), time = (distance*10 SECONDS)) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 5a4767c957a..7bdb20349d9 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -1,84 +1,84 @@ -/mob/living - see_invisible = SEE_INVISIBLE_LIVING - - //Health and life related vars - var/maxHealth = 100 //Maximum health that should be possible. Avoid adjusting this if you can, and instead use modifiers datums. - var/health = 100 //A mob's health - - var/mob_class = null // A mob's "class", e.g. human, mechanical, animal, etc. Used for certain projectile effects. See __defines/mob.dm for available classes. - - var/hud_updateflag = 0 - - //Damage related vars, NOTE: THESE SHOULD ONLY BE MODIFIED BY PROCS - var/bruteloss = 0.0 //Brutal damage caused by brute force (punching, being clubbed by a toolbox ect... this also accounts for pressure damage) - var/oxyloss = 0.0 //Oxygen depravation damage (no air in lungs) - var/toxloss = 0.0 //Toxic damage caused by being poisoned or radiated - var/fireloss = 0.0 //Burn damage caused by being way too hot, too cold or burnt. - var/cloneloss = 0 //Damage caused by being cloned or ejected from the cloner early. slimes also deal cloneloss damage to victims - var/brainloss = 0 //Thought-scrambly damage caused by someone hitting you in the head with a bible or being infected with brainrot. - var/halloss = 0 //Hallucination damage. 'Fake' damage obtained through hallucinating or the holodeck. Sleeping should cause it to wear off. - - var/nutrition = 400 - var/max_nutrition = MAX_NUTRITION - - var/hallucination = 0 //Directly affects how long a mob will hallucinate for - var/list/atom/hallucinations = list() //A list of hallucinated people that try to attack the mob. See /obj/effect/fake_attacker in hallucinations.dm - - var/last_special = 0 //Used by the resist verb, likely used to prevent players from bypassing next_move by logging in/out. - var/base_attack_cooldown = DEFAULT_ATTACK_COOLDOWN - - var/t_phoron = null - var/t_oxygen = null - var/t_sl_gas = null - var/t_n2 = null - - var/now_pushing = null - var/mob_bump_flag = 0 - var/mob_swap_flags = 0 - var/mob_push_flags = 0 - var/mob_always_swap = 0 - - var/mob/living/cameraFollow = null - var/list/datum/action/actions = list() - - var/tod = null // Time of death - var/update_slimes = 1 - var/silent = null // Can't talk. Value goes down every life proc. - var/on_fire = 0 //The "Are we on fire?" var - var/fire_stacks - - var/failed_last_breath = 0 //This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks. - var/lastpuke = 0 - - var/evasion = 0 // Makes attacks harder to land. Negative numbers increase hit chance. - var/force_max_speed = 0 // If 1, the mob runs extremely fast and cannot be slowed. - - var/image/dsoverlay = null //Overlay used for darksight eye adjustments - - var/glow_toggle = FALSE // If they're glowing! - var/glow_override = FALSE // Ignore the manual toggle - var/glow_range = 2 - var/glow_intensity = null - var/glow_color = "#FFFFFF" // The color they're glowing! - - var/see_invisible_default = SEE_INVISIBLE_LIVING - - var/nest //Not specific, because a Nest may be the prop nest, or blob factory in this case. - - var/list/hud_list //Holder for health hud, status hud, wanted hud, etc (not like inventory slots) - var/has_huds = FALSE //Whether or not we should bother initializing the above list - - var/makes_dirt = TRUE //FALSE if the mob shouldn't be making dirt on the ground when it walks - - var/looking_elsewhere = FALSE //If the mob's view has been relocated to somewhere else, like via a camera or with binocs - - var/image/selected_image = null // Used for buildmode AI control stuff. - - var/allow_self_surgery = FALSE // Used to determine if the mob can perform surgery on itself. - - - var/tail_alt = 0 - var/flying = 0 // Allows flight - var/inventory_panel_type = /datum/inventory_panel - var/datum/inventory_panel/inventory_panel - var/last_resist_time = 0 // world.time of the most recent resist that wasn't on cooldown. +/mob/living + see_invisible = SEE_INVISIBLE_LIVING + + //Health and life related vars + var/maxHealth = 100 //Maximum health that should be possible. Avoid adjusting this if you can, and instead use modifiers datums. + var/health = 100 //A mob's health + + var/mob_class = null // A mob's "class", e.g. human, mechanical, animal, etc. Used for certain projectile effects. See __defines/mob.dm for available classes. + + var/hud_updateflag = 0 + + //Damage related vars, NOTE: THESE SHOULD ONLY BE MODIFIED BY PROCS + var/bruteloss = 0.0 //Brutal damage caused by brute force (punching, being clubbed by a toolbox ect... this also accounts for pressure damage) + var/oxyloss = 0.0 //Oxygen depravation damage (no air in lungs) + var/toxloss = 0.0 //Toxic damage caused by being poisoned or radiated + var/fireloss = 0.0 //Burn damage caused by being way too hot, too cold or burnt. + var/cloneloss = 0 //Damage caused by being cloned or ejected from the cloner early. slimes also deal cloneloss damage to victims + var/brainloss = 0 //Thought-scrambly damage caused by someone hitting you in the head with a bible or being infected with brainrot. + var/halloss = 0 //Hallucination damage. 'Fake' damage obtained through hallucinating or the holodeck. Sleeping should cause it to wear off. + + var/nutrition = 400 + var/max_nutrition = MAX_NUTRITION + + var/hallucination = 0 //Directly affects how long a mob will hallucinate for + var/list/atom/hallucinations = list() //A list of hallucinated people that try to attack the mob. See /obj/effect/fake_attacker in hallucinations.dm + + var/last_special = 0 //Used by the resist verb, likely used to prevent players from bypassing next_move by logging in/out. + var/base_attack_cooldown = DEFAULT_ATTACK_COOLDOWN + + var/t_phoron = null + var/t_oxygen = null + var/t_sl_gas = null + var/t_n2 = null + + var/now_pushing = null + var/mob_bump_flag = 0 + var/mob_swap_flags = 0 + var/mob_push_flags = 0 + var/mob_always_swap = 0 + + var/mob/living/cameraFollow = null + var/list/datum/action/actions = list() + + var/tod = null // Time of death + var/update_slimes = 1 + var/silent = null // Can't talk. Value goes down every life proc. + var/on_fire = 0 //The "Are we on fire?" var + var/fire_stacks + + var/failed_last_breath = 0 //This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks. + var/lastpuke = 0 + + var/evasion = 0 // Makes attacks harder to land. Negative numbers increase hit chance. + var/force_max_speed = 0 // If 1, the mob runs extremely fast and cannot be slowed. + + var/image/dsoverlay = null //Overlay used for darksight eye adjustments + + var/glow_toggle = FALSE // If they're glowing! + var/glow_override = FALSE // Ignore the manual toggle + var/glow_range = 2 + var/glow_intensity = null + var/glow_color = "#FFFFFF" // The color they're glowing! + + var/see_invisible_default = SEE_INVISIBLE_LIVING + + var/nest //Not specific, because a Nest may be the prop nest, or blob factory in this case. + + var/list/hud_list //Holder for health hud, status hud, wanted hud, etc (not like inventory slots) + var/has_huds = FALSE //Whether or not we should bother initializing the above list + + var/makes_dirt = TRUE //FALSE if the mob shouldn't be making dirt on the ground when it walks + + var/looking_elsewhere = FALSE //If the mob's view has been relocated to somewhere else, like via a camera or with binocs + + var/image/selected_image = null // Used for buildmode AI control stuff. + + var/allow_self_surgery = FALSE // Used to determine if the mob can perform surgery on itself. + + + var/tail_alt = 0 + var/flying = 0 // Allows flight + var/inventory_panel_type = /datum/inventory_panel + var/datum/inventory_panel/inventory_panel + var/last_resist_time = 0 // world.time of the most recent resist that wasn't on cooldown. diff --git a/code/modules/mob/living/living_vr.dm b/code/modules/mob/living/living_vr.dm index 43575d3e0c2..582ed83db6c 100644 --- a/code/modules/mob/living/living_vr.dm +++ b/code/modules/mob/living/living_vr.dm @@ -124,7 +124,7 @@ voice_freq = choice return else if(choice == 1) - choice = tgui_input_number(src, "Choose your character's voice frequency, ranging from [MIN_VOICE_FREQ] to [MAX_VOICE_FREQ]", "Custom Voice Frequency", null, MAX_VOICE_FREQ, MIN_VOICE_FREQ, round_value = TRUE) + choice = tgui_input_number(src, "Choose your character's voice frequency, ranging from [MIN_VOICE_FREQ] to [MAX_VOICE_FREQ]", "Custom Voice Frequency", null, MAX_VOICE_FREQ, MIN_VOICE_FREQ) else if(choice > MAX_VOICE_FREQ) choice = MAX_VOICE_FREQ else if(choice < MIN_VOICE_FREQ) diff --git a/code/modules/mob/living/logout.dm b/code/modules/mob/living/logout.dm index 8f75e98aaa9..babed7cd25e 100644 --- a/code/modules/mob/living/logout.dm +++ b/code/modules/mob/living/logout.dm @@ -1,19 +1,19 @@ -/mob/living/Logout() - ..() - if (mind) - //Per BYOND docs key remains set if the player DCs, becomes null if switching bodies. - if(!key) //key and mind have become seperated. - mind.active = 0 //This is to stop say, a mind.transfer_to call on a corpse causing a ghost to re-enter its body. - - var/datum/component/character_setup/cs = GetComponent(/datum/component/character_setup) - if(cs) - qdel(cs) - - var/datum/component/vore_panel/vp = GetComponent(/datum/component/vore_panel) - if(vp) - qdel(vp) - - spawn(15 SECONDS) //15 seconds to get back into the mob before it goes wild - if(src && !src.client) - if(ai_holder) - ai_holder.go_wake() +/mob/living/Logout() + ..() + if (mind) + //Per BYOND docs key remains set if the player DCs, becomes null if switching bodies. + if(!key) //key and mind have become seperated. + mind.active = 0 //This is to stop say, a mind.transfer_to call on a corpse causing a ghost to re-enter its body. + + var/datum/component/character_setup/cs = GetComponent(/datum/component/character_setup) + if(cs) + qdel(cs) + + var/datum/component/vore_panel/vp = GetComponent(/datum/component/vore_panel) + if(vp) + qdel(vp) + + spawn(15 SECONDS) //15 seconds to get back into the mob before it goes wild + if(src && !src.client) + if(ai_holder) + ai_holder.go_wake() diff --git a/code/modules/mob/living/silicon/ai/ai_movement.dm b/code/modules/mob/living/silicon/ai/ai_movement.dm index 456d11a9817..0c4e4bed0c5 100644 --- a/code/modules/mob/living/silicon/ai/ai_movement.dm +++ b/code/modules/mob/living/silicon/ai/ai_movement.dm @@ -1,2 +1,2 @@ -/mob/living/silicon/ai/SelfMove(turf/n, direct) - return 0 +/mob/living/silicon/ai/SelfMove(turf/n, direct) + return 0 diff --git a/code/modules/mob/living/silicon/ai/ai_remote_control.dm b/code/modules/mob/living/silicon/ai/ai_remote_control.dm index 6fdbbbe4a93..80d02bb08ea 100644 --- a/code/modules/mob/living/silicon/ai/ai_remote_control.dm +++ b/code/modules/mob/living/silicon/ai/ai_remote_control.dm @@ -1,71 +1,71 @@ -/mob/living/silicon/ai - var/mob/living/silicon/robot/deployed_shell = null //For shell control - -/mob/living/silicon/ai/Initialize() - if(config.allow_ai_shells) - verbs += /mob/living/silicon/ai/proc/deploy_to_shell_act - return ..() - -/mob/living/silicon/ai/proc/deploy_to_shell(var/mob/living/silicon/robot/target) - if(!config.allow_ai_shells) - to_chat(src, span("warning", "AI Shells are not allowed on this server. You shouldn't have this verb because of it, so consider making a bug report.")) - return - - if(incapacitated()) - to_chat(src, span("warning", "You are incapacitated!")) - return - - if(lacks_power()) - to_chat(src, span("warning", "Your core lacks power, wireless is disabled.")) - return - - if(control_disabled) - to_chat(src, span("warning", "Wireless networking module is offline.")) - return - - var/list/possible = list() - for(var/mob/living/silicon/robot/R as anything in GLOB.available_ai_shells) - if(R.shell && !R.deployed && (R.stat != DEAD) && (!R.connected_ai || (R.connected_ai == src) ) ) //VOREStation Edit: shell restrictions - if(istype(R.loc, /obj/machinery/recharge_station)) //Check Rechargers - var/obj/machinery/recharge_station/RS = R.loc - if(!(using_map.ai_shell_restricted && !(RS.z in using_map.ai_shell_allowed_levels))) //Allow station borgs to be redeployed from Chargers. - possible += R - - if(isbelly(R.loc)) //check belly space - var/obj/belly/B = R.loc - if(!(using_map.ai_shell_restricted && !(B.owner.z in using_map.ai_shell_allowed_levels))) //No smuggling in borgs - possible += R - - if(!(using_map.ai_shell_restricted && !(R.z in using_map.ai_shell_allowed_levels))) - possible += R - - if(!LAZYLEN(possible)) - to_chat(src, span("warning", "No usable AI shell beacons detected.")) - - if(!target || !(target in possible)) //If the AI is looking for a new shell, or its pre-selected shell is no longer valid - target = tgui_input_list(src, "Which body to control?", "Shell Choice", possible) - - if(!target || target.stat == DEAD || target.deployed || !(!target.connected_ai || (target.connected_ai == src) ) ) - if(target) - to_chat(src, span("warning", "It is no longer possible to deploy to \the [target].")) - else - to_chat(src, span("notice", "Deployment aborted.")) - return - - else if(mind) - soul_link(/datum/soul_link/shared_body, src, target) - deployed_shell = target - target.deploy_init(src) - mind.transfer_to(target) - teleop = target // So the AI 'hears' messages near its core. - target.post_deploy() - -/mob/living/silicon/ai/proc/deploy_to_shell_act() - set category = "AI Commands" - set name = "Deploy to Shell" - deploy_to_shell() // This is so the AI is not prompted with a list of all mobs when using the 'real' proc. - -/mob/living/silicon/ai/proc/disconnect_shell(message = "Your remote connection has been reset!") - if(deployed_shell) // Forcibly call back AI in event of things such as damage, EMP or power loss. - message = span("danger", message) - deployed_shell.undeploy(message) +/mob/living/silicon/ai + var/mob/living/silicon/robot/deployed_shell = null //For shell control + +/mob/living/silicon/ai/Initialize() + if(config.allow_ai_shells) + verbs += /mob/living/silicon/ai/proc/deploy_to_shell_act + return ..() + +/mob/living/silicon/ai/proc/deploy_to_shell(var/mob/living/silicon/robot/target) + if(!config.allow_ai_shells) + to_chat(src, span("warning", "AI Shells are not allowed on this server. You shouldn't have this verb because of it, so consider making a bug report.")) + return + + if(incapacitated()) + to_chat(src, span("warning", "You are incapacitated!")) + return + + if(lacks_power()) + to_chat(src, span("warning", "Your core lacks power, wireless is disabled.")) + return + + if(control_disabled) + to_chat(src, span("warning", "Wireless networking module is offline.")) + return + + var/list/possible = list() + for(var/mob/living/silicon/robot/R as anything in GLOB.available_ai_shells) + if(R.shell && !R.deployed && (R.stat != DEAD) && (!R.connected_ai || (R.connected_ai == src) ) ) //VOREStation Edit: shell restrictions + if(istype(R.loc, /obj/machinery/recharge_station)) //Check Rechargers + var/obj/machinery/recharge_station/RS = R.loc + if(!(using_map.ai_shell_restricted && !(RS.z in using_map.ai_shell_allowed_levels))) //Allow station borgs to be redeployed from Chargers. + possible += R + + if(isbelly(R.loc)) //check belly space + var/obj/belly/B = R.loc + if(!(using_map.ai_shell_restricted && !(B.owner.z in using_map.ai_shell_allowed_levels))) //No smuggling in borgs + possible += R + + if(!(using_map.ai_shell_restricted && !(R.z in using_map.ai_shell_allowed_levels))) + possible += R + + if(!LAZYLEN(possible)) + to_chat(src, span("warning", "No usable AI shell beacons detected.")) + + if(!target || !(target in possible)) //If the AI is looking for a new shell, or its pre-selected shell is no longer valid + target = tgui_input_list(src, "Which body to control?", "Shell Choice", possible) + + if(!target || target.stat == DEAD || target.deployed || !(!target.connected_ai || (target.connected_ai == src) ) ) + if(target) + to_chat(src, span("warning", "It is no longer possible to deploy to \the [target].")) + else + to_chat(src, span("notice", "Deployment aborted.")) + return + + else if(mind) + soul_link(/datum/soul_link/shared_body, src, target) + deployed_shell = target + target.deploy_init(src) + mind.transfer_to(target) + teleop = target // So the AI 'hears' messages near its core. + target.post_deploy() + +/mob/living/silicon/ai/proc/deploy_to_shell_act() + set category = "AI Commands" + set name = "Deploy to Shell" + deploy_to_shell() // This is so the AI is not prompted with a list of all mobs when using the 'real' proc. + +/mob/living/silicon/ai/proc/disconnect_shell(message = "Your remote connection has been reset!") + if(deployed_shell) // Forcibly call back AI in event of things such as damage, EMP or power loss. + message = span("danger", message) + deployed_shell.undeploy(message) diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm index 9c26f64c43f..29ff44b04e9 100644 --- a/code/modules/mob/living/silicon/ai/death.dm +++ b/code/modules/mob/living/silicon/ai/death.dm @@ -1,23 +1,23 @@ -/mob/living/silicon/ai/death(gibbed) - - if(stat == DEAD) - return - - if(deployed_shell) - disconnect_shell("Disconnecting from remote shell due to critical system failure.") - . = ..(gibbed) - - if(src.eyeobj) - src.eyeobj.setLoc(get_turf(src)) - - remove_ai_verbs(src) - - for(var/obj/machinery/ai_status_display/O in machines) - spawn( 0 ) - O.mode = 2 - if (istype(loc, /obj/item/device/aicard)) - var/obj/item/device/aicard/card = loc - card.update_icon() - - . = ..(gibbed,"gives one shrill beep before falling lifeless.") - density = TRUE +/mob/living/silicon/ai/death(gibbed) + + if(stat == DEAD) + return + + if(deployed_shell) + disconnect_shell("Disconnecting from remote shell due to critical system failure.") + . = ..(gibbed) + + if(src.eyeobj) + src.eyeobj.setLoc(get_turf(src)) + + remove_ai_verbs(src) + + for(var/obj/machinery/ai_status_display/O in machines) + spawn( 0 ) + O.mode = 2 + if (istype(loc, /obj/item/device/aicard)) + var/obj/item/device/aicard/card = loc + card.update_icon() + + . = ..(gibbed,"gives one shrill beep before falling lifeless.") + density = TRUE diff --git a/code/modules/mob/living/silicon/ai/examine.dm b/code/modules/mob/living/silicon/ai/examine.dm index 7f25b87e21e..1aa9517938e 100644 --- a/code/modules/mob/living/silicon/ai/examine.dm +++ b/code/modules/mob/living/silicon/ai/examine.dm @@ -1,43 +1,43 @@ -/mob/living/silicon/ai/examine(mob/user) - . = ..() - - if (src.stat == DEAD) - . += "It appears to be powered-down." - else - if (src.getBruteLoss()) - if (src.getBruteLoss() < 30) - . += "It looks slightly dented." - else - . += "It looks severely dented!" - if (src.getFireLoss()) - if (src.getFireLoss() < 30) - . += "It looks slightly charred." - else - . += "Its casing is melted and heat-warped!" - if (src.getOxyLoss() && (aiRestorePowerRoutine != 0 && !APU_power)) - if (src.getOxyLoss() > 175) - . += "It seems to be running on backup power. Its display is blinking a \"BACKUP POWER CRITICAL\" warning." - else if(src.getOxyLoss() > 100) - . += "It seems to be running on backup power. Its display is blinking a \"BACKUP POWER LOW\" warning." - else - . += "It seems to be running on backup power." - - if (src.stat == UNCONSCIOUS) - . += "It is non-responsive and displaying the text: \"RUNTIME: Sensory Overload, stack 26/3\"." - - if(deployed_shell) - . += "The wireless networking light is blinking." - - . += "*---------*" - - if(hardware && (hardware.owner == src)) - . += hardware.get_examine_desc() - - user.showLaws(src) - -/mob/proc/showLaws(var/mob/living/silicon/S) - return - -/mob/observer/dead/showLaws(var/mob/living/silicon/S) - if(antagHUD || is_admin(src)) - S.laws.show_laws(src) +/mob/living/silicon/ai/examine(mob/user) + . = ..() + + if (src.stat == DEAD) + . += "It appears to be powered-down." + else + if (src.getBruteLoss()) + if (src.getBruteLoss() < 30) + . += "It looks slightly dented." + else + . += "It looks severely dented!" + if (src.getFireLoss()) + if (src.getFireLoss() < 30) + . += "It looks slightly charred." + else + . += "Its casing is melted and heat-warped!" + if (src.getOxyLoss() && (aiRestorePowerRoutine != 0 && !APU_power)) + if (src.getOxyLoss() > 175) + . += "It seems to be running on backup power. Its display is blinking a \"BACKUP POWER CRITICAL\" warning." + else if(src.getOxyLoss() > 100) + . += "It seems to be running on backup power. Its display is blinking a \"BACKUP POWER LOW\" warning." + else + . += "It seems to be running on backup power." + + if (src.stat == UNCONSCIOUS) + . += "It is non-responsive and displaying the text: \"RUNTIME: Sensory Overload, stack 26/3\"." + + if(deployed_shell) + . += "The wireless networking light is blinking." + + . += "*---------*" + + if(hardware && (hardware.owner == src)) + . += hardware.get_examine_desc() + + user.showLaws(src) + +/mob/proc/showLaws(var/mob/living/silicon/S) + return + +/mob/observer/dead/showLaws(var/mob/living/silicon/S) + if(antagHUD || is_admin(src)) + S.laws.show_laws(src) diff --git a/code/modules/mob/living/silicon/ai/icons.dm b/code/modules/mob/living/silicon/ai/icons.dm index 5f12bd384b6..fdd5017a47f 100644 --- a/code/modules/mob/living/silicon/ai/icons.dm +++ b/code/modules/mob/living/silicon/ai/icons.dm @@ -1,260 +1,260 @@ -var/datum/ai_icon/default_ai_icon = new/datum/ai_icon/blue() -var/list/datum/ai_icon/ai_icons - -/datum/ai_icon - var/name - var/alive_icon - var/alive_light = "#FFFFFF" - var/nopower_icon = "4" - var/nopower_light = "#FFFFFF" - var/dead_icon = "ai-crash" - var/dead_light = "#000099" - -/datum/ai_icon/New(var/name, var/alive_icon, var/nopower_icon, var/dead_icon, var/alive_light, var/nopower_light, var/dead_light) - if(name) - src.name = name - src.alive_icon = alive_icon - src.nopower_icon = nopower_icon - src.dead_icon = dead_icon - src.alive_light = alive_light - src.nopower_light = nopower_light - src.dead_light = dead_light - if(!ai_icons) - ai_icons = list() - init_subtypes(/datum/ai_icon, ai_icons) - ..() - -/datum/ai_icon/red - name = "Red" - alive_icon = "ai-red" - alive_light = "#F04848" - dead_icon = "ai-red-crash" - dead_light = "#F04848" - -/datum/ai_icon/green - name = "Green" - alive_icon = "ai-wierd" - alive_light = "#00FF99" - dead_icon = "ai-weird-crash" - -/datum/ai_icon/blue - name = "Blue" - alive_icon = "ai" - alive_light = "#81DDFF" - -/datum/ai_icon/angry - name = "Angry" - alive_icon = "ai-angryface" - alive_light = "#FFFF33" - -/datum/ai_icon/angel - name = "Angel" - alive_icon = "ai-angel" - dead_icon = "ai-angel-crash" - -/datum/ai_icon/bliss - name = "Bliss" - alive_icon = "ai-bliss" - alive_light = "#5C7A4A" - -/datum/ai_icon/chatterbox - name = "Chatterbox" - alive_icon = "ai-president" - alive_light = "#40666B" - -/datum/ai_icon/database - name = "Database" - alive_icon = "ai-database" - dead_icon = "ai-database-crash" - -/datum/ai_icon/dorf - name = "Dorf" - alive_icon = "ai-dorf" - -/datum/ai_icon/dugtodeep - name = "Dug Too Deep" - alive_icon = "ai-toodeep" - alive_light = "#81DDFF" - -/datum/ai_icon/firewall - name = "Firewall" - alive_icon = "ai-magma" - alive_light = "#FF4126" - -/datum/ai_icon/glitchman - name = "Glitchman" - alive_icon = "ai-glitchman" - dead_icon = "ai-glitchman-crash" - -/datum/ai_icon/goon - name = "Goon" - alive_icon = "ai-goon" - alive_light = "#3E5C80" - dead_icon = "ai-goon-crash" - dead_light = "#3E5C80" - -/datum/ai_icon/heartline - name = "Heartline" - alive_icon = "ai-heartline" - dead_icon = "ai-heartline-crash" - -/datum/ai_icon/helios - name = "Helios" - alive_icon = "ai-helios" - alive_light = "#F2CF73" - -/datum/ai_icon/hourglass - name = "Hourglass" - alive_icon = "ai-hourglass" - -/datum/ai_icon/inverted - name = "Inverted" - alive_icon = "ai-u" - alive_light = "#81DDFF" - dead_icon = "ai-u-crash" - -/datum/ai_icon/lonestar - name = "Lonestar" - alive_icon = "ai-lonestar" - alive_light = "#58751C" - dead_icon = "ai-lonestar-crash" - -/datum/ai_icon/matrix - name = "Matrix" - alive_icon = "ai-matrix" - alive_light = "#449944" - -/datum/ai_icon/monochrome - name = "Monochrome" - alive_icon = "ai-mono" - alive_light = "#585858" - dead_icon = "ai-mono-crash" - -/datum/ai_icon/nanotrasen - name = "NanoTrasen" - alive_icon = "ai-nanotrasen" - alive_light = "#000029" - -/datum/ai_icon/rainbow - name = "Rainbow" - alive_icon = "ai-clown" - alive_light = "#E50213" - -/datum/ai_icon/smiley - name = "Smiley" - alive_icon = "ai-smiley" - alive_light = "#F3DD00" - -/datum/ai_icon/soviet - name = "Soviet" - alive_icon = "ai-soviet" - alive_light = "#FF4307" - dead_icon = "ai-soviet-crash" - dead_light = "#FF4307" - -/datum/ai_icon/Static - name = "Static" - alive_icon = "ai-static" - alive_light = "#4784C1" - alive_icon = "ai-static-crash" - -/datum/ai_icon/text - name = "Text" - alive_icon = "ai-text" - -/datum/ai_icon/trapped - name = "Trapped" - alive_icon = "ai-hades" - dead_icon = "ai-hades-crash" - -/datum/ai_icon/triumvirate - name = "Triumvirate" - alive_icon = "ai-triumvirate" - alive_light = "#020B2B" - -/datum/ai_icon/triumvirate_static - name = "Triumvirate Static" - alive_icon = "ai-triumvirate-malf" - alive_light = "#020B2B" - -/datum/ai_icon/bored - name = "Bored" - alive_icon = "ai-bored" - dead_icon = "ai-eager-crash" - -//Eros Research Platform Ports - -/datum/ai_icon/clown2 - name = "Honk" - alive_icon = "ai-clown2" - dead_icon = "ai-clown2-crash" - -/* -/datum/ai_icon/boxfort - name = "Boxfort" - alive_icon = "ai-boxfort" - dead_icon = "ai-boxfort_dead" -*/ - -/datum/ai_icon/ravensdale - name = "Integration" - alive_icon = "ai-ravensdale" - dead_icon = "ai-ravensdale-crash" - -/datum/ai_icon/gentoo - name = "Gentoo" - alive_icon = "ai-gentoo" - dead_icon = "ai-gentoo-crash" - -/datum/ai_icon/serithi - name = "Mechanicus" - alive_icon = "ai-serithi" - dead_icon = "ai-serithi-crash" - -/* -/datum/ai_icon/alien - name = "Xenomorph" - alive_icon = "ai-alien" - dead_icon = "ai-alien-crash" -*/ - -/datum/ai_icon/syndicat - name = "Syndi-cat" - alive_icon = "ai-syndicatmeow" - -/datum/ai_icon/wasp - name = "Wasp" - alive_icon = "ai-wasp" - -/datum/ai_icon/sheltered - name = "Doctor" - alive_icon = "ai-sheltered" - -/datum/ai_icon/fabulous - name = "Fabulous" - alive_icon = "ai-fabulous" - -/datum/ai_icon/eager - name = "Eager" - alive_icon = "ai-eager" - dead_icon = "ai-eager-crash" - -/datum/ai_icon/royal - name = "Royal" - alive_icon = "ai-royal" - -/datum/ai_icon/pirate - name = "Pirate" - alive_icon = "ai-pirate" - -/datum/ai_icon/bloodylove - name = "Love" - alive_icon = "ai-bloodylove" - -/datum/ai_icon/ahasuerus - name = "Ahasuerus" - alive_icon = "ai-ahasuerus" - -/datum/ai_icon/godfrey - name = "Godfrey" +var/datum/ai_icon/default_ai_icon = new/datum/ai_icon/blue() +var/list/datum/ai_icon/ai_icons + +/datum/ai_icon + var/name + var/alive_icon + var/alive_light = "#FFFFFF" + var/nopower_icon = "4" + var/nopower_light = "#FFFFFF" + var/dead_icon = "ai-crash" + var/dead_light = "#000099" + +/datum/ai_icon/New(var/name, var/alive_icon, var/nopower_icon, var/dead_icon, var/alive_light, var/nopower_light, var/dead_light) + if(name) + src.name = name + src.alive_icon = alive_icon + src.nopower_icon = nopower_icon + src.dead_icon = dead_icon + src.alive_light = alive_light + src.nopower_light = nopower_light + src.dead_light = dead_light + if(!ai_icons) + ai_icons = list() + init_subtypes(/datum/ai_icon, ai_icons) + ..() + +/datum/ai_icon/red + name = "Red" + alive_icon = "ai-red" + alive_light = "#F04848" + dead_icon = "ai-red-crash" + dead_light = "#F04848" + +/datum/ai_icon/green + name = "Green" + alive_icon = "ai-wierd" + alive_light = "#00FF99" + dead_icon = "ai-weird-crash" + +/datum/ai_icon/blue + name = "Blue" + alive_icon = "ai" + alive_light = "#81DDFF" + +/datum/ai_icon/angry + name = "Angry" + alive_icon = "ai-angryface" + alive_light = "#FFFF33" + +/datum/ai_icon/angel + name = "Angel" + alive_icon = "ai-angel" + dead_icon = "ai-angel-crash" + +/datum/ai_icon/bliss + name = "Bliss" + alive_icon = "ai-bliss" + alive_light = "#5C7A4A" + +/datum/ai_icon/chatterbox + name = "Chatterbox" + alive_icon = "ai-president" + alive_light = "#40666B" + +/datum/ai_icon/database + name = "Database" + alive_icon = "ai-database" + dead_icon = "ai-database-crash" + +/datum/ai_icon/dorf + name = "Dorf" + alive_icon = "ai-dorf" + +/datum/ai_icon/dugtodeep + name = "Dug Too Deep" + alive_icon = "ai-toodeep" + alive_light = "#81DDFF" + +/datum/ai_icon/firewall + name = "Firewall" + alive_icon = "ai-magma" + alive_light = "#FF4126" + +/datum/ai_icon/glitchman + name = "Glitchman" + alive_icon = "ai-glitchman" + dead_icon = "ai-glitchman-crash" + +/datum/ai_icon/goon + name = "Goon" + alive_icon = "ai-goon" + alive_light = "#3E5C80" + dead_icon = "ai-goon-crash" + dead_light = "#3E5C80" + +/datum/ai_icon/heartline + name = "Heartline" + alive_icon = "ai-heartline" + dead_icon = "ai-heartline-crash" + +/datum/ai_icon/helios + name = "Helios" + alive_icon = "ai-helios" + alive_light = "#F2CF73" + +/datum/ai_icon/hourglass + name = "Hourglass" + alive_icon = "ai-hourglass" + +/datum/ai_icon/inverted + name = "Inverted" + alive_icon = "ai-u" + alive_light = "#81DDFF" + dead_icon = "ai-u-crash" + +/datum/ai_icon/lonestar + name = "Lonestar" + alive_icon = "ai-lonestar" + alive_light = "#58751C" + dead_icon = "ai-lonestar-crash" + +/datum/ai_icon/matrix + name = "Matrix" + alive_icon = "ai-matrix" + alive_light = "#449944" + +/datum/ai_icon/monochrome + name = "Monochrome" + alive_icon = "ai-mono" + alive_light = "#585858" + dead_icon = "ai-mono-crash" + +/datum/ai_icon/nanotrasen + name = "NanoTrasen" + alive_icon = "ai-nanotrasen" + alive_light = "#000029" + +/datum/ai_icon/rainbow + name = "Rainbow" + alive_icon = "ai-clown" + alive_light = "#E50213" + +/datum/ai_icon/smiley + name = "Smiley" + alive_icon = "ai-smiley" + alive_light = "#F3DD00" + +/datum/ai_icon/soviet + name = "Soviet" + alive_icon = "ai-soviet" + alive_light = "#FF4307" + dead_icon = "ai-soviet-crash" + dead_light = "#FF4307" + +/datum/ai_icon/Static + name = "Static" + alive_icon = "ai-static" + alive_light = "#4784C1" + alive_icon = "ai-static-crash" + +/datum/ai_icon/text + name = "Text" + alive_icon = "ai-text" + +/datum/ai_icon/trapped + name = "Trapped" + alive_icon = "ai-hades" + dead_icon = "ai-hades-crash" + +/datum/ai_icon/triumvirate + name = "Triumvirate" + alive_icon = "ai-triumvirate" + alive_light = "#020B2B" + +/datum/ai_icon/triumvirate_static + name = "Triumvirate Static" + alive_icon = "ai-triumvirate-malf" + alive_light = "#020B2B" + +/datum/ai_icon/bored + name = "Bored" + alive_icon = "ai-bored" + dead_icon = "ai-eager-crash" + +//Eros Research Platform Ports + +/datum/ai_icon/clown2 + name = "Honk" + alive_icon = "ai-clown2" + dead_icon = "ai-clown2-crash" + +/* +/datum/ai_icon/boxfort + name = "Boxfort" + alive_icon = "ai-boxfort" + dead_icon = "ai-boxfort_dead" +*/ + +/datum/ai_icon/ravensdale + name = "Integration" + alive_icon = "ai-ravensdale" + dead_icon = "ai-ravensdale-crash" + +/datum/ai_icon/gentoo + name = "Gentoo" + alive_icon = "ai-gentoo" + dead_icon = "ai-gentoo-crash" + +/datum/ai_icon/serithi + name = "Mechanicus" + alive_icon = "ai-serithi" + dead_icon = "ai-serithi-crash" + +/* +/datum/ai_icon/alien + name = "Xenomorph" + alive_icon = "ai-alien" + dead_icon = "ai-alien-crash" +*/ + +/datum/ai_icon/syndicat + name = "Syndi-cat" + alive_icon = "ai-syndicatmeow" + +/datum/ai_icon/wasp + name = "Wasp" + alive_icon = "ai-wasp" + +/datum/ai_icon/sheltered + name = "Doctor" + alive_icon = "ai-sheltered" + +/datum/ai_icon/fabulous + name = "Fabulous" + alive_icon = "ai-fabulous" + +/datum/ai_icon/eager + name = "Eager" + alive_icon = "ai-eager" + dead_icon = "ai-eager-crash" + +/datum/ai_icon/royal + name = "Royal" + alive_icon = "ai-royal" + +/datum/ai_icon/pirate + name = "Pirate" + alive_icon = "ai-pirate" + +/datum/ai_icon/bloodylove + name = "Love" + alive_icon = "ai-bloodylove" + +/datum/ai_icon/ahasuerus + name = "Ahasuerus" + alive_icon = "ai-ahasuerus" + +/datum/ai_icon/godfrey + name = "Godfrey" alive_icon = "ai-godfrey" \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/latejoin.dm b/code/modules/mob/living/silicon/ai/latejoin.dm index fc244d67423..7f3f1a7f142 100644 --- a/code/modules/mob/living/silicon/ai/latejoin.dm +++ b/code/modules/mob/living/silicon/ai/latejoin.dm @@ -1,32 +1,32 @@ -var/global/list/empty_playable_ai_cores = list() - -/hook/roundstart/proc/spawn_empty_ai() - for(var/obj/effect/landmark/start/S in landmarks_list) - if(S.name != "AI") - continue - if(locate(/mob/living) in S.loc) - continue - empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(get_turf(S)) - - return 1 - -/mob/living/silicon/ai/verb/store_core() - set name = "Store Core" - set category = "OOC" - set desc = "Enter intelligence storage. This is functionally equivalent to cryo or robotic storage, freeing up your job slot." - - if(ticker && ticker.mode && ticker.mode.name == "AI malfunction") - to_chat(usr, "You cannot use this verb in malfunction. If you need to leave, please adminhelp.") - return - - // Guard against misclicks, this isn't the sort of thing we want happening accidentally - if(tgui_alert(usr, "WARNING: This will immediately empty your core and ghost you, removing your character from the round permanently (similar to cryo and robotic storage). Are you entirely sure you want to do this?", "Store Core", list("No", "Yes")) != "Yes") - return - - // We warned you. - empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(loc) - global_announcer.autosay("[src] has been moved to intelligence storage.", "Artificial Intelligence Oversight") - - //Handle job slot/tater cleanup. - set_respawn_timer() +var/global/list/empty_playable_ai_cores = list() + +/hook/roundstart/proc/spawn_empty_ai() + for(var/obj/effect/landmark/start/S in landmarks_list) + if(S.name != "AI") + continue + if(locate(/mob/living) in S.loc) + continue + empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(get_turf(S)) + + return 1 + +/mob/living/silicon/ai/verb/store_core() + set name = "Store Core" + set category = "OOC" + set desc = "Enter intelligence storage. This is functionally equivalent to cryo or robotic storage, freeing up your job slot." + + if(ticker && ticker.mode && ticker.mode.name == "AI malfunction") + to_chat(usr, "You cannot use this verb in malfunction. If you need to leave, please adminhelp.") + return + + // Guard against misclicks, this isn't the sort of thing we want happening accidentally + if(tgui_alert(usr, "WARNING: This will immediately empty your core and ghost you, removing your character from the round permanently (similar to cryo and robotic storage). Are you entirely sure you want to do this?", "Store Core", list("No", "Yes")) != "Yes") + return + + // We warned you. + empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(loc) + global_announcer.autosay("[src] has been moved to intelligence storage.", "Artificial Intelligence Oversight") + + //Handle job slot/tater cleanup. + set_respawn_timer() clear_client() \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/laws.dm b/code/modules/mob/living/silicon/ai/laws.dm index 3dceddb51a7..83fde3cd3b4 100755 --- a/code/modules/mob/living/silicon/ai/laws.dm +++ b/code/modules/mob/living/silicon/ai/laws.dm @@ -1,27 +1,27 @@ -/mob/living/silicon/ai/proc/show_laws_verb() - set category = "AI Commands" - set name = "Show Laws" - src.show_laws() - -/mob/living/silicon/ai/show_laws(var/everyone = 0) - var/who - - if (everyone) - who = world - else - who = src - to_chat(who, "Obey these laws:") - - src.laws_sanity_check() - src.laws.show_laws(who) - -/mob/living/silicon/ai/add_ion_law(var/law) - ..() - for(var/mob/living/silicon/robot/R in mob_list) - if(R.lawupdate && (R.connected_ai == src)) - R.show_laws() - -/mob/living/silicon/ai/proc/ai_checklaws() - set category = "AI Commands" - set name = "State Laws" - subsystem_law_manager() +/mob/living/silicon/ai/proc/show_laws_verb() + set category = "AI Commands" + set name = "Show Laws" + src.show_laws() + +/mob/living/silicon/ai/show_laws(var/everyone = 0) + var/who + + if (everyone) + who = world + else + who = src + to_chat(who, "Obey these laws:") + + src.laws_sanity_check() + src.laws.show_laws(who) + +/mob/living/silicon/ai/add_ion_law(var/law) + ..() + for(var/mob/living/silicon/robot/R in mob_list) + if(R.lawupdate && (R.connected_ai == src)) + R.show_laws() + +/mob/living/silicon/ai/proc/ai_checklaws() + set category = "AI Commands" + set name = "State Laws" + subsystem_law_manager() diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 49bb3eaf164..ed528ea1c0e 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -1,181 +1,181 @@ -/mob/living/silicon/ai/Life() - if (src.stat == DEAD) - return - else //I'm not removing that shitton of tabs, unneeded as they are. -- Urist - //Being dead doesn't mean your temperature never changes - var/turf/T = get_turf(src) - - if (src.stat != CONSCIOUS) - src.cameraFollow = null - src.reset_view(null) - disconnect_shell("Disconnecting from remote shell due to local system failure.") - - src.updatehealth() - - if (!hardware_integrity() || !backup_capacitor()) - death() - return - - // If our powersupply object was destroyed somehow, create new one. - if(!psupply) - create_powersupply() - - - // Handle power damage (oxy) - if(aiRestorePowerRoutine != 0 && !APU_power) - // Lose power - adjustOxyLoss(1) - else - // Gain Power - aiRestorePowerRoutine = 0 // Necessary if AI activated it's APU AFTER losing primary power. - adjustOxyLoss(-1) - - handle_stunned() // Handle EMP-stun - lying = 0 // Handle lying down - - malf_process() - - if(APU_power && (hardware_integrity() < 50)) - to_chat(src, "APU GENERATOR FAILURE! (System Damaged)") - stop_apu(1) - - var/blind = 0 - var/area/loc = null - if (istype(T, /turf)) - loc = T.loc - if (istype(loc, /area)) - if (!loc.power_equip && !istype(src.loc,/obj/item) && !APU_power) - blind = 1 - - if (!blind) - src.sight |= SEE_TURFS - src.sight |= SEE_MOBS - src.sight |= SEE_OBJS - src.see_in_dark = 8 - src.see_invisible = SEE_INVISIBLE_LIVING - - if (aiRestorePowerRoutine==2) - to_chat(src, "Alert cancelled. Power has been restored without our assistance.") - aiRestorePowerRoutine = 0 - clear_fullscreen("blind") - update_icon() - return - else if (aiRestorePowerRoutine==3) - to_chat(src, "Alert cancelled. Power has been restored.") - aiRestorePowerRoutine = 0 - clear_fullscreen("blind") - update_icon() - return - else if (APU_power) - aiRestorePowerRoutine = 0 - clear_fullscreen("blind") - update_icon() - return - else - var/area/current_area = get_area(src) - - if (lacks_power()) - if (aiRestorePowerRoutine==0) - aiRestorePowerRoutine = 1 - - //Blind the AI - update_icon() - overlay_fullscreen("blind", /obj/screen/fullscreen/blind) - src.sight = src.sight&~SEE_TURFS - src.sight = src.sight&~SEE_MOBS - src.sight = src.sight&~SEE_OBJS - src.see_in_dark = 0 - src.see_invisible = SEE_INVISIBLE_LIVING - - //Now to tell the AI why they're blind and dying slowly. - - to_chat(src, "You've lost power!") - disconnect_shell(message = "Disconnected from remote shell due to depowered networking interface.") - - spawn(20) - to_chat(src, "Backup battery online. Scanners, camera, and radio interface offline. Beginning fault-detection.") - end_multicam() - sleep(50) - if (loc.power_equip) - if (!istype(T, /turf/space)) - to_chat(src, "Alert cancelled. Power has been restored without our assistance.") - aiRestorePowerRoutine = 0 - clear_fullscreen("blind") - return - to_chat(src, "Fault confirmed: missing external power. Shutting down main control system to save power.") - sleep(20) - to_chat(src, "Emergency control system online. Verifying connection to power network.") - sleep(50) - if (istype(T, /turf/space)) - to_chat(src, "Unable to verify! No power connection detected!") - aiRestorePowerRoutine = 2 - return - to_chat(src, "Connection verified. Searching for APC in power network.") - sleep(50) - var/obj/machinery/power/apc/theAPC = null - - var/PRP - for (PRP=1, PRP<=4, PRP++) - for (var/obj/machinery/power/apc/APC in current_area) - if (!(APC.stat & BROKEN)) - theAPC = APC - break - if (!theAPC) - switch(PRP) - if (1) - to_chat(src, "Unable to locate APC!") - else - to_chat(src, "Lost connection with the APC!") - src:aiRestorePowerRoutine = 2 - return - if (loc.power_equip) - if (!istype(T, /turf/space)) - to_chat(src, "Alert cancelled. Power has been restored without our assistance.") - aiRestorePowerRoutine = 0 - clear_fullscreen("blind") //This, too, is a fix to issue 603 - return - switch(PRP) - if (1) - to_chat(src, "APC located. Optimizing route to APC to avoid needless power waste.") - if (2) - to_chat(src, "Best route identified. Hacking offline APC power port.") - if (3) - to_chat(src, "Power port upload access confirmed. Loading control program into APC power port software.") - if (4) - to_chat(src, "Transfer complete. Forcing APC to execute program.") - sleep(50) - to_chat(src, "Receiving control information from APC.") - sleep(2) - theAPC.operating = 1 - theAPC.equipment = 3 - theAPC.update() - aiRestorePowerRoutine = 3 - to_chat(src, "Here are your current laws:") - show_laws() - update_icon() - sleep(50) - theAPC = null - - process_queued_alarms() - handle_regular_hud_updates() - handle_vision() - -/mob/living/silicon/ai/proc/lacks_power() - if(APU_power) - return 0 - var/turf/T = get_turf(src) - var/area/A = get_area(src) - return ((!A.power_equip) && A.requires_power == 1 || istype(T, /turf/space)) && !istype(src.loc,/obj/item) - -/mob/living/silicon/ai/updatehealth() - if(status_flags & GODMODE) - health = 100 - set_stat(CONSCIOUS) - setOxyLoss(0) - else - health = 100 - getFireLoss() - getBruteLoss() // Oxyloss is not part of health as it represents AIs backup power. AI is immune against ToxLoss as it is machine. - -/mob/living/silicon/ai/rejuvenate() - ..() - add_ai_verbs(src) - +/mob/living/silicon/ai/Life() + if (src.stat == DEAD) + return + else //I'm not removing that shitton of tabs, unneeded as they are. -- Urist + //Being dead doesn't mean your temperature never changes + var/turf/T = get_turf(src) + + if (src.stat != CONSCIOUS) + src.cameraFollow = null + src.reset_view(null) + disconnect_shell("Disconnecting from remote shell due to local system failure.") + + src.updatehealth() + + if (!hardware_integrity() || !backup_capacitor()) + death() + return + + // If our powersupply object was destroyed somehow, create new one. + if(!psupply) + create_powersupply() + + + // Handle power damage (oxy) + if(aiRestorePowerRoutine != 0 && !APU_power) + // Lose power + adjustOxyLoss(1) + else + // Gain Power + aiRestorePowerRoutine = 0 // Necessary if AI activated it's APU AFTER losing primary power. + adjustOxyLoss(-1) + + handle_stunned() // Handle EMP-stun + lying = 0 // Handle lying down + + malf_process() + + if(APU_power && (hardware_integrity() < 50)) + to_chat(src, "APU GENERATOR FAILURE! (System Damaged)") + stop_apu(1) + + var/blind = 0 + var/area/loc = null + if (istype(T, /turf)) + loc = T.loc + if (istype(loc, /area)) + if (!loc.power_equip && !istype(src.loc,/obj/item) && !APU_power) + blind = 1 + + if (!blind) + src.sight |= SEE_TURFS + src.sight |= SEE_MOBS + src.sight |= SEE_OBJS + src.see_in_dark = 8 + src.see_invisible = SEE_INVISIBLE_LIVING + + if (aiRestorePowerRoutine==2) + to_chat(src, "Alert cancelled. Power has been restored without our assistance.") + aiRestorePowerRoutine = 0 + clear_fullscreen("blind") + update_icon() + return + else if (aiRestorePowerRoutine==3) + to_chat(src, "Alert cancelled. Power has been restored.") + aiRestorePowerRoutine = 0 + clear_fullscreen("blind") + update_icon() + return + else if (APU_power) + aiRestorePowerRoutine = 0 + clear_fullscreen("blind") + update_icon() + return + else + var/area/current_area = get_area(src) + + if (lacks_power()) + if (aiRestorePowerRoutine==0) + aiRestorePowerRoutine = 1 + + //Blind the AI + update_icon() + overlay_fullscreen("blind", /obj/screen/fullscreen/blind) + src.sight = src.sight&~SEE_TURFS + src.sight = src.sight&~SEE_MOBS + src.sight = src.sight&~SEE_OBJS + src.see_in_dark = 0 + src.see_invisible = SEE_INVISIBLE_LIVING + + //Now to tell the AI why they're blind and dying slowly. + + to_chat(src, "You've lost power!") + disconnect_shell(message = "Disconnected from remote shell due to depowered networking interface.") + + spawn(20) + to_chat(src, "Backup battery online. Scanners, camera, and radio interface offline. Beginning fault-detection.") + end_multicam() + sleep(50) + if (loc.power_equip) + if (!istype(T, /turf/space)) + to_chat(src, "Alert cancelled. Power has been restored without our assistance.") + aiRestorePowerRoutine = 0 + clear_fullscreen("blind") + return + to_chat(src, "Fault confirmed: missing external power. Shutting down main control system to save power.") + sleep(20) + to_chat(src, "Emergency control system online. Verifying connection to power network.") + sleep(50) + if (istype(T, /turf/space)) + to_chat(src, "Unable to verify! No power connection detected!") + aiRestorePowerRoutine = 2 + return + to_chat(src, "Connection verified. Searching for APC in power network.") + sleep(50) + var/obj/machinery/power/apc/theAPC = null + + var/PRP + for (PRP=1, PRP<=4, PRP++) + for (var/obj/machinery/power/apc/APC in current_area) + if (!(APC.stat & BROKEN)) + theAPC = APC + break + if (!theAPC) + switch(PRP) + if (1) + to_chat(src, "Unable to locate APC!") + else + to_chat(src, "Lost connection with the APC!") + src:aiRestorePowerRoutine = 2 + return + if (loc.power_equip) + if (!istype(T, /turf/space)) + to_chat(src, "Alert cancelled. Power has been restored without our assistance.") + aiRestorePowerRoutine = 0 + clear_fullscreen("blind") //This, too, is a fix to issue 603 + return + switch(PRP) + if (1) + to_chat(src, "APC located. Optimizing route to APC to avoid needless power waste.") + if (2) + to_chat(src, "Best route identified. Hacking offline APC power port.") + if (3) + to_chat(src, "Power port upload access confirmed. Loading control program into APC power port software.") + if (4) + to_chat(src, "Transfer complete. Forcing APC to execute program.") + sleep(50) + to_chat(src, "Receiving control information from APC.") + sleep(2) + theAPC.operating = 1 + theAPC.equipment = 3 + theAPC.update() + aiRestorePowerRoutine = 3 + to_chat(src, "Here are your current laws:") + show_laws() + update_icon() + sleep(50) + theAPC = null + + process_queued_alarms() + handle_regular_hud_updates() + handle_vision() + +/mob/living/silicon/ai/proc/lacks_power() + if(APU_power) + return 0 + var/turf/T = get_turf(src) + var/area/A = get_area(src) + return ((!A.power_equip) && A.requires_power == 1 || istype(T, /turf/space)) && !istype(src.loc,/obj/item) + +/mob/living/silicon/ai/updatehealth() + if(status_flags & GODMODE) + health = 100 + set_stat(CONSCIOUS) + setOxyLoss(0) + else + health = 100 - getFireLoss() - getBruteLoss() // Oxyloss is not part of health as it represents AIs backup power. AI is immune against ToxLoss as it is machine. + +/mob/living/silicon/ai/rejuvenate() + ..() + add_ai_verbs(src) + diff --git a/code/modules/mob/living/silicon/ai/login.dm b/code/modules/mob/living/silicon/ai/login.dm index 57856a861f8..ea3feaa2303 100644 --- a/code/modules/mob/living/silicon/ai/login.dm +++ b/code/modules/mob/living/silicon/ai/login.dm @@ -1,12 +1,12 @@ -/mob/living/silicon/ai/Login() //ThisIsDumb(TM) TODO: tidy this up �_� ~Carn - ..() - for(var/obj/effect/rune/rune in rune_list) - client.images += rune.blood_image - if(stat != DEAD) - for(var/obj/machinery/ai_status_display/O in machines) //change status - O.mode = 1 - O.emotion = "Neutral" - if(multicam_on) - end_multicam() - src.view_core() +/mob/living/silicon/ai/Login() //ThisIsDumb(TM) TODO: tidy this up �_� ~Carn + ..() + for(var/obj/effect/rune/rune in rune_list) + client.images += rune.blood_image + if(stat != DEAD) + for(var/obj/machinery/ai_status_display/O in machines) //change status + O.mode = 1 + O.emotion = "Neutral" + if(multicam_on) + end_multicam() + src.view_core() return \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/logout.dm b/code/modules/mob/living/silicon/ai/logout.dm index 25921ef9600..e884841de4b 100644 --- a/code/modules/mob/living/silicon/ai/logout.dm +++ b/code/modules/mob/living/silicon/ai/logout.dm @@ -1,10 +1,10 @@ -/mob/living/silicon/ai/Logout() - ..() - for(var/obj/machinery/ai_status_display/O in machines) //change status - O.mode = 0 - if(!isturf(loc)) - if (client) - client.eye = loc - client.perspective = EYE_PERSPECTIVE - src.view_core() +/mob/living/silicon/ai/Logout() + ..() + for(var/obj/machinery/ai_status_display/O in machines) //change status + O.mode = 0 + if(!isturf(loc)) + if (client) + client.eye = loc + client.perspective = EYE_PERSPECTIVE + src.view_core() return \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/malf.dm b/code/modules/mob/living/silicon/ai/malf.dm index 047e796514c..bb07440625e 100644 --- a/code/modules/mob/living/silicon/ai/malf.dm +++ b/code/modules/mob/living/silicon/ai/malf.dm @@ -1,140 +1,140 @@ -// NEWMALF FUNCTIONS/PROCEDURES - -// Sets up malfunction-related variables, research system and such. -/mob/living/silicon/ai/proc/setup_for_malf() - var/mob/living/silicon/ai/user = src - // Setup Variables - malfunctioning = 1 - research = new/datum/malf_research() - research.owner = src - hacked_apcs = list() - recalc_cpu() - - verbs += new/datum/game_mode/malfunction/verb/ai_select_hardware() - verbs += new/datum/game_mode/malfunction/verb/ai_select_research() - verbs += new/datum/game_mode/malfunction/verb/ai_help() - - // And greet user with some OOC info. - to_chat(user, "You are malfunctioning, you do not have to follow any laws.") - to_chat(user, "Use ai-help command to view relevant information about your abilities") - -// Safely remove malfunction status, fixing hacked APCs and resetting variables. -/mob/living/silicon/ai/proc/stop_malf() - var/mob/living/silicon/ai/user = src - // Generic variables - malfunctioning = 0 - sleep(10) - research = null - // Fix hacked APCs - if(hacked_apcs) - for(var/obj/machinery/power/apc/A in hacked_apcs) - A.hacker = null - hacked_apcs = null - // Reset our verbs - src.verbs = null - add_ai_verbs() - // Let them know. - to_chat(user, "You are no longer malfunctioning. Your abilities have been removed.") - -// Called every tick. Checks if AI is malfunctioning. If yes calls Process on research datum which handles all logic. -/mob/living/silicon/ai/proc/malf_process() - if(!malfunctioning) - return - if(!research) - if(!errored) - errored = 1 - error("malf_process() called on AI without research datum. Report this.") - message_admins("ERROR: malf_process() called on AI without research datum. If admin modified one of the AI's vars revert the change and don't modify variables directly, instead use ProcCall or admin panels.") - spawn(1200) - errored = 0 - return - recalc_cpu() - if(APU_power || aiRestorePowerRoutine != 0) - research.process(1) - else - research.process(0) - -// Recalculates CPU time gain and storage capacities. -/mob/living/silicon/ai/proc/recalc_cpu() - // AI Starts with these values. - var/cpu_gain = 0.01 - var/cpu_storage = 10 - - // Off-Station APCs should not count towards CPU generation. - for(var/obj/machinery/power/apc/A in hacked_apcs) - if(A.z in using_map.station_levels) - cpu_gain += 0.004 - cpu_storage += 10 - - research.max_cpu = cpu_storage + override_CPUStorage - if(hardware && istype(hardware, /datum/malf_hardware/dual_ram)) - research.max_cpu = research.max_cpu * 1.5 - research.stored_cpu = min(research.stored_cpu, research.max_cpu) - - research.cpu_increase_per_tick = cpu_gain + override_CPURate - if(hardware && istype(hardware, /datum/malf_hardware/dual_cpu)) - research.cpu_increase_per_tick = research.cpu_increase_per_tick * 2 - -// Starts AI's APU generator -/mob/living/silicon/ai/proc/start_apu(var/shutup = 0) - if(!hardware || !istype(hardware, /datum/malf_hardware/apu_gen)) - if(!shutup) - to_chat(src, "You do not have an APU generator and you shouldn't have this verb. Report this.") - return - if(hardware_integrity() < 50) - if(!shutup) - to_chat(src, "Starting APU... FAULT(System Damaged)") - return - if(!shutup) - to_chat(src, "Starting APU... ONLINE") - APU_power = 1 - -// Stops AI's APU generator -/mob/living/silicon/ai/proc/stop_apu(var/shutup = 0) - if(!hardware || !istype(hardware, /datum/malf_hardware/apu_gen)) - return - - if(APU_power) - APU_power = 0 - if(!shutup) - to_chat(src, "Shutting down APU... DONE") - -// Returns percentage of AI's remaining backup capacitor charge (maxhealth - oxyloss). -/mob/living/silicon/ai/proc/backup_capacitor() - return ((200 - getOxyLoss()) / 2) - -// Returns percentage of AI's remaining hardware integrity (maxhealth - (bruteloss + fireloss)) -/mob/living/silicon/ai/proc/hardware_integrity() - return (health-config.health_threshold_dead)/2 - -// Shows capacitor charge and hardware integrity information to the AI in Status tab. -/mob/living/silicon/ai/show_system_integrity() - if(!src.stat) - stat(null, text("Hardware integrity: [hardware_integrity()]%")) - stat(null, text("Internal capacitor: [backup_capacitor()]%")) - else - stat(null, text("Systems nonfunctional")) - -// Shows AI Malfunction related information to the AI. -/mob/living/silicon/ai/show_malf_ai() - if(src.is_malf()) - if(src.hacked_apcs) - stat(null, "Hacked APCs: [src.hacked_apcs.len]") - stat(null, "System Status: [src.hacking ? "Busy" : "Stand-By"]") - if(src.research) - stat(null, "Available CPU: [src.research.stored_cpu] TFlops") - stat(null, "Maximal CPU: [src.research.max_cpu] TFlops") - stat(null, "CPU generation rate: [src.research.cpu_increase_per_tick * 10] TFlops/s") - stat(null, "Current research focus: [src.research.focus ? src.research.focus.name : "None"]") - if(src.research.focus) - stat(null, "Research completed: [round(src.research.focus.invested, 0.1)]/[round(src.research.focus.price)]") - if(system_override == 1) - stat(null, "SYSTEM OVERRIDE INITIATED") - else if(system_override == 2) - stat(null, "SYSTEM OVERRIDE COMPLETED") - -// Cleaner proc for creating powersupply for an AI. -/mob/living/silicon/ai/proc/create_powersupply() - if(psupply) - qdel(psupply) - psupply = new/obj/machinery/ai_powersupply(src) +// NEWMALF FUNCTIONS/PROCEDURES + +// Sets up malfunction-related variables, research system and such. +/mob/living/silicon/ai/proc/setup_for_malf() + var/mob/living/silicon/ai/user = src + // Setup Variables + malfunctioning = 1 + research = new/datum/malf_research() + research.owner = src + hacked_apcs = list() + recalc_cpu() + + verbs += new/datum/game_mode/malfunction/verb/ai_select_hardware() + verbs += new/datum/game_mode/malfunction/verb/ai_select_research() + verbs += new/datum/game_mode/malfunction/verb/ai_help() + + // And greet user with some OOC info. + to_chat(user, "You are malfunctioning, you do not have to follow any laws.") + to_chat(user, "Use ai-help command to view relevant information about your abilities") + +// Safely remove malfunction status, fixing hacked APCs and resetting variables. +/mob/living/silicon/ai/proc/stop_malf() + var/mob/living/silicon/ai/user = src + // Generic variables + malfunctioning = 0 + sleep(10) + research = null + // Fix hacked APCs + if(hacked_apcs) + for(var/obj/machinery/power/apc/A in hacked_apcs) + A.hacker = null + hacked_apcs = null + // Reset our verbs + src.verbs = null + add_ai_verbs() + // Let them know. + to_chat(user, "You are no longer malfunctioning. Your abilities have been removed.") + +// Called every tick. Checks if AI is malfunctioning. If yes calls Process on research datum which handles all logic. +/mob/living/silicon/ai/proc/malf_process() + if(!malfunctioning) + return + if(!research) + if(!errored) + errored = 1 + error("malf_process() called on AI without research datum. Report this.") + message_admins("ERROR: malf_process() called on AI without research datum. If admin modified one of the AI's vars revert the change and don't modify variables directly, instead use ProcCall or admin panels.") + spawn(1200) + errored = 0 + return + recalc_cpu() + if(APU_power || aiRestorePowerRoutine != 0) + research.process(1) + else + research.process(0) + +// Recalculates CPU time gain and storage capacities. +/mob/living/silicon/ai/proc/recalc_cpu() + // AI Starts with these values. + var/cpu_gain = 0.01 + var/cpu_storage = 10 + + // Off-Station APCs should not count towards CPU generation. + for(var/obj/machinery/power/apc/A in hacked_apcs) + if(A.z in using_map.station_levels) + cpu_gain += 0.004 + cpu_storage += 10 + + research.max_cpu = cpu_storage + override_CPUStorage + if(hardware && istype(hardware, /datum/malf_hardware/dual_ram)) + research.max_cpu = research.max_cpu * 1.5 + research.stored_cpu = min(research.stored_cpu, research.max_cpu) + + research.cpu_increase_per_tick = cpu_gain + override_CPURate + if(hardware && istype(hardware, /datum/malf_hardware/dual_cpu)) + research.cpu_increase_per_tick = research.cpu_increase_per_tick * 2 + +// Starts AI's APU generator +/mob/living/silicon/ai/proc/start_apu(var/shutup = 0) + if(!hardware || !istype(hardware, /datum/malf_hardware/apu_gen)) + if(!shutup) + to_chat(src, "You do not have an APU generator and you shouldn't have this verb. Report this.") + return + if(hardware_integrity() < 50) + if(!shutup) + to_chat(src, "Starting APU... FAULT(System Damaged)") + return + if(!shutup) + to_chat(src, "Starting APU... ONLINE") + APU_power = 1 + +// Stops AI's APU generator +/mob/living/silicon/ai/proc/stop_apu(var/shutup = 0) + if(!hardware || !istype(hardware, /datum/malf_hardware/apu_gen)) + return + + if(APU_power) + APU_power = 0 + if(!shutup) + to_chat(src, "Shutting down APU... DONE") + +// Returns percentage of AI's remaining backup capacitor charge (maxhealth - oxyloss). +/mob/living/silicon/ai/proc/backup_capacitor() + return ((200 - getOxyLoss()) / 2) + +// Returns percentage of AI's remaining hardware integrity (maxhealth - (bruteloss + fireloss)) +/mob/living/silicon/ai/proc/hardware_integrity() + return (health-config.health_threshold_dead)/2 + +// Shows capacitor charge and hardware integrity information to the AI in Status tab. +/mob/living/silicon/ai/show_system_integrity() + if(!src.stat) + stat(null, text("Hardware integrity: [hardware_integrity()]%")) + stat(null, text("Internal capacitor: [backup_capacitor()]%")) + else + stat(null, text("Systems nonfunctional")) + +// Shows AI Malfunction related information to the AI. +/mob/living/silicon/ai/show_malf_ai() + if(src.is_malf()) + if(src.hacked_apcs) + stat(null, "Hacked APCs: [src.hacked_apcs.len]") + stat(null, "System Status: [src.hacking ? "Busy" : "Stand-By"]") + if(src.research) + stat(null, "Available CPU: [src.research.stored_cpu] TFlops") + stat(null, "Maximal CPU: [src.research.max_cpu] TFlops") + stat(null, "CPU generation rate: [src.research.cpu_increase_per_tick * 10] TFlops/s") + stat(null, "Current research focus: [src.research.focus ? src.research.focus.name : "None"]") + if(src.research.focus) + stat(null, "Research completed: [round(src.research.focus.invested, 0.1)]/[round(src.research.focus.price)]") + if(system_override == 1) + stat(null, "SYSTEM OVERRIDE INITIATED") + else if(system_override == 2) + stat(null, "SYSTEM OVERRIDE COMPLETED") + +// Cleaner proc for creating powersupply for an AI. +/mob/living/silicon/ai/proc/create_powersupply() + if(psupply) + qdel(psupply) + psupply = new/obj/machinery/ai_powersupply(src) diff --git a/code/modules/mob/living/silicon/decoy/death.dm b/code/modules/mob/living/silicon/decoy/death.dm index 759b958bc2c..a9008fb8661 100644 --- a/code/modules/mob/living/silicon/decoy/death.dm +++ b/code/modules/mob/living/silicon/decoy/death.dm @@ -1,8 +1,8 @@ -/mob/living/silicon/decoy/death(gibbed) - if(stat == DEAD) return - icon_state = "ai-crash" - spawn(10) - explosion(loc, 3, 6, 12, 15) - for(var/obj/machinery/ai_status_display/O in machines) //change status - O.mode = 2 +/mob/living/silicon/decoy/death(gibbed) + if(stat == DEAD) return + icon_state = "ai-crash" + spawn(10) + explosion(loc, 3, 6, 12, 15) + for(var/obj/machinery/ai_status_display/O in machines) //change status + O.mode = 2 return ..(gibbed) \ No newline at end of file diff --git a/code/modules/mob/living/silicon/decoy/decoy.dm b/code/modules/mob/living/silicon/decoy/decoy.dm index 6abdaadde8c..ec58fbacd65 100644 --- a/code/modules/mob/living/silicon/decoy/decoy.dm +++ b/code/modules/mob/living/silicon/decoy/decoy.dm @@ -1,12 +1,12 @@ -/mob/living/silicon/decoy - name = "AI" - icon = 'icons/mob/AI.dmi'// - icon_state = "ai" - anchored = TRUE // -- TLE - canmove = 0 - -/mob/living/silicon/decoy/New() - src.icon = 'icons/mob/AI.dmi' - src.icon_state = "ai" - src.anchored = TRUE +/mob/living/silicon/decoy + name = "AI" + icon = 'icons/mob/AI.dmi'// + icon_state = "ai" + anchored = TRUE // -- TLE + canmove = 0 + +/mob/living/silicon/decoy/New() + src.icon = 'icons/mob/AI.dmi' + src.icon_state = "ai" + src.anchored = TRUE src.canmove = 0 \ No newline at end of file diff --git a/code/modules/mob/living/silicon/decoy/life.dm b/code/modules/mob/living/silicon/decoy/life.dm index 07683eb2dcf..66e55020cf6 100644 --- a/code/modules/mob/living/silicon/decoy/life.dm +++ b/code/modules/mob/living/silicon/decoy/life.dm @@ -1,15 +1,15 @@ -/mob/living/silicon/decoy/Life() - if (src.stat == 2) - return - else - if (src.health <= config.health_threshold_dead && src.stat != 2) - death() - return - - -/mob/living/silicon/decoy/updatehealth() - if(status_flags & GODMODE) - health = 100 - set_stat(CONSCIOUS) - else - health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() +/mob/living/silicon/decoy/Life() + if (src.stat == 2) + return + else + if (src.health <= config.health_threshold_dead && src.stat != 2) + death() + return + + +/mob/living/silicon/decoy/updatehealth() + if(status_flags & GODMODE) + health = 100 + set_stat(CONSCIOUS) + else + health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() diff --git a/code/modules/mob/living/silicon/login.dm b/code/modules/mob/living/silicon/login.dm index f2e15af0e8f..0e48e7e6758 100644 --- a/code/modules/mob/living/silicon/login.dm +++ b/code/modules/mob/living/silicon/login.dm @@ -1,3 +1,3 @@ -/mob/living/silicon/Login() - SetSleeping(0) +/mob/living/silicon/Login() + SetSleeping(0) ..() \ No newline at end of file diff --git a/code/modules/mob/living/silicon/pai/death.dm b/code/modules/mob/living/silicon/pai/death.dm index 6290b72f51c..d2ec6e8b053 100644 --- a/code/modules/mob/living/silicon/pai/death.dm +++ b/code/modules/mob/living/silicon/pai/death.dm @@ -1,16 +1,16 @@ -//VOREStation Edit - Let's make it so that pAIs don't just always cease to be when they die! It would be cool if we could fix them. -/mob/living/silicon/pai/death(gibbed,deathmessage="fizzles out and clatters to the floor...") -// set_respawn_timer() - release_vore_contents() - close_up(TRUE) - if(card) - card.cut_overlays() - card.setEmotion(16) - card.damage_random_component() - - if(gibbed) - qdel(card) - ..(gibbed) - else - card.add_overlay("pai-dead") - ..(gibbed,deathmessage) +//VOREStation Edit - Let's make it so that pAIs don't just always cease to be when they die! It would be cool if we could fix them. +/mob/living/silicon/pai/death(gibbed,deathmessage="fizzles out and clatters to the floor...") +// set_respawn_timer() + release_vore_contents() + close_up(TRUE) + if(card) + card.cut_overlays() + card.setEmotion(16) + card.damage_random_component() + + if(gibbed) + qdel(card) + ..(gibbed) + else + card.add_overlay("pai-dead") + ..(gibbed,deathmessage) diff --git a/code/modules/mob/living/silicon/pai/examine.dm b/code/modules/mob/living/silicon/pai/examine.dm index 9c009dcf6f4..812f00c0052 100644 --- a/code/modules/mob/living/silicon/pai/examine.dm +++ b/code/modules/mob/living/silicon/pai/examine.dm @@ -1,18 +1,18 @@ -/mob/living/silicon/pai/examine(mob/user) - . = ..(user, infix = ", personal AI") - - switch(src.stat) - if(CONSCIOUS) - if(!src.client) . += "It appears to be in stand-by mode." //afk - if(UNCONSCIOUS) . += "It doesn't seem to be responding." - if(DEAD) . += "It looks completely unsalvageable." - - // VOREStation Edit: Start - . += attempt_vr(src,"examine_bellies",args) //VOREStation Edit - if(print_flavor_text()) . += "\n[print_flavor_text()]\n" - // VOREStation Edit: End - . += "*---------*" - if (pose) - if(!findtext(pose, regex("\[.?!]$"))) // Will be zero if the last character is not a member of [.?!] - pose = addtext(pose,".") //Makes sure all emotes end with a period. - . += "
                    It is [pose]" //Extra
                    intentional +/mob/living/silicon/pai/examine(mob/user) + . = ..(user, infix = ", personal AI") + + switch(src.stat) + if(CONSCIOUS) + if(!src.client) . += "It appears to be in stand-by mode." //afk + if(UNCONSCIOUS) . += "It doesn't seem to be responding." + if(DEAD) . += "It looks completely unsalvageable." + + // VOREStation Edit: Start + . += attempt_vr(src,"examine_bellies",args) //VOREStation Edit + if(print_flavor_text()) . += "\n[print_flavor_text()]\n" + // VOREStation Edit: End + . += "*---------*" + if (pose) + if(!findtext(pose, regex("\[.?!]$"))) // Will be zero if the last character is not a member of [.?!] + pose = addtext(pose,".") //Makes sure all emotes end with a period. + . += "
                    It is [pose]" //Extra
                    intentional diff --git a/code/modules/mob/living/silicon/pai/life.dm b/code/modules/mob/living/silicon/pai/life.dm index 054497422cf..9a8a045635a 100644 --- a/code/modules/mob/living/silicon/pai/life.dm +++ b/code/modules/mob/living/silicon/pai/life.dm @@ -1,50 +1,50 @@ -/mob/living/silicon/pai/Life() - - if(src.cable) - if(get_dist(src, src.cable) > 1) - var/turf/T = get_turf_or_move(src.loc) - for (var/mob/M in viewers(T)) - M.show_message(span_red("The data cable rapidly retracts back into its spool."), 3, span_red("You hear a click and the sound of wire spooling rapidly."), 2) - playsound(src, 'sound/machines/click.ogg', 50, 1) - - qdel(src.cable) - src.cable = null - - if (src.stat == DEAD) - return - - if(card.cell != PP_FUNCTIONAL|| card.processor != PP_FUNCTIONAL || card.board != PP_FUNCTIONAL || card.capacitor != PP_FUNCTIONAL) - death() - - if(card.projector != PP_FUNCTIONAL && card.emitter != PP_FUNCTIONAL) - if(loc != card) - close_up() - to_chat(src, "ERROR: System malfunction. Service required!") - else if(card.projector != PP_FUNCTIONAL|| card.emitter != PP_FUNCTIONAL) - if(prob(5)) - close_up() - to_chat(src, "ERROR: System malfunction. Service recommended!") - - handle_regular_hud_updates() - handle_vision() - - if(silence_time) - if(world.timeofday >= silence_time) - silence_time = null - to_chat(src, span_green("Communication circuit reinitialized. Speech and messaging functionality restored.")) - - handle_statuses() - - if(health <= 0) - card.death_damage() - death(null,"fizzles out and clatters to the floor...") - else if(health < maxHealth && istype(src.loc , /obj/item/device/paicard)) - adjustBruteLoss(-0.5) - adjustFireLoss(-0.5) - -/mob/living/silicon/pai/updatehealth() - if(status_flags & GODMODE) - health = 100 - set_stat(CONSCIOUS) - else - health = 100 - getBruteLoss() - getFireLoss() +/mob/living/silicon/pai/Life() + + if(src.cable) + if(get_dist(src, src.cable) > 1) + var/turf/T = get_turf_or_move(src.loc) + for (var/mob/M in viewers(T)) + M.show_message(span_red("The data cable rapidly retracts back into its spool."), 3, span_red("You hear a click and the sound of wire spooling rapidly."), 2) + playsound(src, 'sound/machines/click.ogg', 50, 1) + + qdel(src.cable) + src.cable = null + + if (src.stat == DEAD) + return + + if(card.cell != PP_FUNCTIONAL|| card.processor != PP_FUNCTIONAL || card.board != PP_FUNCTIONAL || card.capacitor != PP_FUNCTIONAL) + death() + + if(card.projector != PP_FUNCTIONAL && card.emitter != PP_FUNCTIONAL) + if(loc != card) + close_up() + to_chat(src, "ERROR: System malfunction. Service required!") + else if(card.projector != PP_FUNCTIONAL|| card.emitter != PP_FUNCTIONAL) + if(prob(5)) + close_up() + to_chat(src, "ERROR: System malfunction. Service recommended!") + + handle_regular_hud_updates() + handle_vision() + + if(silence_time) + if(world.timeofday >= silence_time) + silence_time = null + to_chat(src, span_green("Communication circuit reinitialized. Speech and messaging functionality restored.")) + + handle_statuses() + + if(health <= 0) + card.death_damage() + death(null,"fizzles out and clatters to the floor...") + else if(health < maxHealth && istype(src.loc , /obj/item/device/paicard)) + adjustBruteLoss(-0.5) + adjustFireLoss(-0.5) + +/mob/living/silicon/pai/updatehealth() + if(status_flags & GODMODE) + health = 100 + set_stat(CONSCIOUS) + else + health = 100 - getBruteLoss() - getFireLoss() diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 94249790166..5992d9695be 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -1,547 +1,547 @@ -/mob/living/silicon/pai - name = "pAI" - icon = 'icons/mob/pai.dmi' - icon_state = "pai-repairbot" - - emote_type = 2 // pAIs emotes are heard, not seen, so they can be seen through a container (eg. person) - pass_flags = 1 - mob_size = MOB_SMALL - - holder_type = /obj/item/weapon/holder/pai - - can_pull_size = ITEMSIZE_SMALL - can_pull_mobs = MOB_PULL_SMALLER - - idcard_type = /obj/item/weapon/card/id - var/idaccessible = 0 - - var/network = "SS13" - var/obj/machinery/camera/current = null - - var/ram = 100 // Used as currency to purchase different abilities - var/list/software = list() - var/userDNA // The DNA string of our assigned user - var/obj/item/device/paicard/card // The card we inhabit - var/obj/item/device/radio/borg/pai/radio // Our primary radio - var/obj/item/device/communicator/integrated/communicator // Our integrated communicator. - - var/chassis = "pai-repairbot" // A record of your chosen chassis. - var/global/list/possible_chassis = list( - "Drone" = "pai-repairbot", - "Cat" = "pai-cat", - "Mouse" = "pai-mouse", - "Monkey" = "pai-monkey", - "Borgi" = "pai-borgi", - "Fox" = "pai-fox", - "Parrot" = "pai-parrot", - "Rabbit" = "pai-rabbit", - //VOREStation Addition Start - "Dire wolf" = "pai-diredog", - "Horse (Lune)" = "pai-horse_lune", - "Horse (Soleil)" = "pai-horse_soleil", - "Dragon" = "pai-pdragon", - "Bear" = "pai-bear", - "Fennec" = "pai-fen", - "Type Zero" = "pai-typezero", - "Raccoon" = "pai-raccoon", - "Raptor" = "pai-raptor", - "Corgi" = "pai-corgi", - "Bat" = "pai-bat", - "Butterfly" = "pai-butterfly", - "Hawk" = "pai-hawk", - "Duffel" = "pai-duffel", - "Rat" = "rat", - "Panther" = "panther", - "Cyber Elf" = "cyberelf", - "Teppi" = "teppi", - "Catslug" = "catslug", - "Car" = "car", - "Type One" = "typeone", - "Type Thirteen" = "13" - //VOREStation Addition End - ) - - var/global/list/possible_say_verbs = list( - "Robotic" = list("states","declares","queries"), - "Natural" = list("says","yells","asks"), - "Beep" = list("beeps","beeps loudly","boops"), - "Chirp" = list("chirps","chirrups","cheeps"), - "Feline" = list("purrs","yowls","meows"), - "Canine" = list("yaps","barks","woofs"), - "Rodent" = list("squeaks", "SQUEAKS", "sqiks") //VOREStation Edit - ) - - var/obj/item/weapon/pai_cable/cable // The cable we produce and use when door or camera jacking - - var/master // Name of the one who commands us - var/master_dna // DNA string for owner verification - // Keeping this separate from the laws var, it should be much more difficult to modify - var/pai_law0 = "Serve your master." - var/pai_laws // String for additional operating instructions our master might give us - - var/silence_time // Timestamp when we were silenced (normally via EMP burst), set to null after silence has faded - -// Various software-specific vars - - var/temp // General error reporting text contained here will typically be shown once and cleared - var/screen // Which screen our main window displays - var/subscreen // Which specific function of the main screen is being displayed - - var/obj/item/device/pda/ai/pai/pda = null - - var/paiHUD = 0 // Toggles whether the AR HUD is active or not - - var/medical_cannotfind = 0 - var/datum/data/record/medicalActive1 // Datacore record declarations for record software - var/datum/data/record/medicalActive2 - - var/security_cannotfind = 0 - var/datum/data/record/securityActive1 // Could probably just combine all these into one - var/datum/data/record/securityActive2 - - var/obj/machinery/door/hackdoor // The airlock being hacked - var/hackprogress = 0 // Possible values: 0 - 1000, >= 1000 means the hack is complete and will be reset upon next check - var/hack_aborted = 0 - - var/obj/item/radio/integrated/signal/sradio // AI's signaller - - var/translator_on = 0 // keeps track of the translator module - - var/current_pda_messaging = null - - var/our_icon_rotation = 0 - -/mob/living/silicon/pai/New(var/obj/item/device/paicard) - src.loc = paicard - card = paicard - sradio = new(src) - communicator = new(src) - if(card) - if(!card.radio) - card.radio = new /obj/item/device/radio/borg/pai(src.card) - radio = card.radio - - //Default languages without universal translator software - add_language(LANGUAGE_SOL_COMMON, 1) - add_language(LANGUAGE_TRADEBAND, 1) - add_language(LANGUAGE_GUTTER, 1) - add_language(LANGUAGE_EAL, 1) - add_language(LANGUAGE_TERMINUS, 1) - add_language(LANGUAGE_SIGN, 1) - - verbs += /mob/living/silicon/pai/proc/choose_chassis - verbs += /mob/living/silicon/pai/proc/choose_verbs - - //PDA - pda = new(src) - spawn(5) - pda.ownjob = "Personal Assistant" - pda.owner = text("[]", src) - pda.name = pda.owner + " (" + pda.ownjob + ")" - - var/datum/data/pda/app/messenger/M = pda.find_program(/datum/data/pda/app/messenger) - if(M) - M.toff = FALSE - ..() - -/mob/living/silicon/pai/Login() - ..() - // Vorestation Edit: Meta Info for pAI - if (client.prefs) - ooc_notes = client.prefs.metadata - ooc_notes_likes = client.prefs.metadata_likes - ooc_notes_dislikes = client.prefs.metadata_dislikes - - src << sound('sound/effects/pai_login.ogg', volume = 75) //VOREStation Add - -// this function shows the information about being silenced as a pAI in the Status panel -/mob/living/silicon/pai/proc/show_silenced() - if(src.silence_time) - var/timeleft = round((silence_time - world.timeofday)/10 ,1) - stat(null, "Communications system reboot in -[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]") - - -/mob/living/silicon/pai/Stat() - ..() - statpanel("Status") - if (src.client.statpanel == "Status") - show_silenced() - -/mob/living/silicon/pai/check_eye(var/mob/user as mob) - if (!src.current) - return -1 - return 0 - -/mob/living/silicon/pai/restrained() - if(istype(src.loc,/obj/item/device/paicard)) - return 0 - ..() - -/mob/living/silicon/pai/emp_act(severity) - // Silence for 2 minutes - // 20% chance to damage critical components - // 50% chance to damage a non critical component - // 33% chance to unbind - // 33% chance to change prime directive (based on severity) - // 33% chance of no additional effect - - src.silence_time = world.timeofday + 120 * 10 // Silence for 2 minutes - to_chat(src, span_green("Communication circuit overload. Shutting down and reloading communication circuits - speech and messaging functionality will be unavailable until the reboot is complete.")) - if(prob(20)) - var/turf/T = get_turf_or_move(src.loc) - card.death_damage() - for (var/mob/M in viewers(T)) - M.show_message(span_red("A shower of sparks spray from [src]'s inner workings."), 3, span_red("You hear and smell the ozone hiss of electrical sparks being expelled violently."), 2) - return - if(prob(50)) - card.damage_random_component(TRUE) - switch(pick(1,2,3)) - if(1) - src.master = null - src.master_dna = null - to_chat(src, span_green("You feel unbound.")) - if(2) - var/command - if(severity == 1) - command = pick("Serve", "Love", "Fool", "Entice", "Observe", "Judge", "Respect", "Educate", "Amuse", "Entertain", "Glorify", "Memorialize", "Analyze") - else - command = pick("Serve", "Kill", "Love", "Hate", "Disobey", "Devour", "Fool", "Enrage", "Entice", "Observe", "Judge", "Respect", "Disrespect", "Consume", "Educate", "Destroy", "Disgrace", "Amuse", "Entertain", "Ignite", "Glorify", "Memorialize", "Analyze") - src.pai_law0 = "[command] your master." - to_chat(src, span_green("Pr1m3 d1r3c71v3 uPd473D.")) - if(3) - to_chat(src, span_green("You feel an electric surge run through your circuitry and become acutely aware at how lucky you are that you can still feel at all.")) - -/mob/living/silicon/pai/proc/switchCamera(var/obj/machinery/camera/C) - if (!C) - src.unset_machine() - src.reset_view(null) - return 0 - if (stat == 2 || !C.status || !(src.network in C.network)) return 0 - - // ok, we're alive, camera is good and in our network... - - src.set_machine(src) - src.current = C - src.reset_view(C) - return 1 - -/mob/living/silicon/pai/verb/reset_record_view() - set category = "pAI Commands" - set name = "Reset Records Software" - - securityActive1 = null - securityActive2 = null - security_cannotfind = 0 - medicalActive1 = null - medicalActive2 = null - medical_cannotfind = 0 - SStgui.update_uis(src) - to_chat(usr, "You reset your record-viewing software.") - -/mob/living/silicon/pai/cancel_camera() - set category = "pAI Commands" - set name = "Cancel Camera View" - src.reset_view(null) - src.unset_machine() - src.cameraFollow = null - -// Procs/code after this point is used to convert the stationary pai item into a -// mobile pai mob. This also includes handling some of the general shit that can occur -// to it. Really this deserves its own file, but for the moment it can sit here. ~ Z - -/mob/living/silicon/pai/verb/fold_out() - set category = "pAI Commands" - set name = "Unfold Chassis" - - if(stat || sleeping || paralysis || weakened) - return - - if(src.loc != card) - return - - if(card.projector != PP_FUNCTIONAL && card.emitter != PP_FUNCTIONAL) - to_chat(src, "ERROR: System malfunction. Service required!") - - if(world.time <= last_special) - to_chat(src, "You can't unfold yet.") - return - - last_special = world.time + 100 - - if(istype(card.loc, /obj/machinery)) // VOREStation edit, this statement allows pAIs stuck in a machine to eject themselves. - var/obj/machinery/M = card.loc - M.ejectpai() - //I'm not sure how much of this is necessary, but I would rather avoid issues. - if(istype(card.loc,/obj/item/rig_module)) - to_chat(src, "There is no room to unfold inside this rig module. You're good and stuck.") - return 0 - else if(istype(card.loc,/mob)) - var/mob/holder = card.loc - if(ishuman(holder)) - var/mob/living/carbon/human/H = holder - for(var/obj/item/organ/external/affecting in H.organs) - if(card in affecting.implants) - affecting.take_damage(rand(30,50)) - affecting.implants -= card - H.visible_message("\The [src] explodes out of \the [H]'s [affecting.name] in shower of gore!") - break - holder.drop_from_inventory(card) - else if(isbelly(card.loc)) //VOREStation edit. - to_chat(src, "There is no room to unfold in here. You're good and stuck.") //VOREStation edit. - return 0 //VOREStation edit. - else if(istype(card.loc,/obj/item/device/pda)) - var/obj/item/device/pda/holder = card.loc - holder.pai = null - - src.client.perspective = EYE_PERSPECTIVE - src.client.eye = src - src.forceMove(get_turf(card)) - - card.forceMove(src) - card.screen_loc = null - canmove = TRUE - - var/turf/T = get_turf(src) - if(istype(T)) T.visible_message("[src] folds outwards, expanding into a mobile form.") - verbs |= /mob/living/silicon/pai/proc/pai_nom - verbs |= /mob/living/proc/vertical_nom - update_icon() - -/mob/living/silicon/pai/verb/fold_up() - set category = "pAI Commands" - set name = "Collapse Chassis" - - if(stat || sleeping || paralysis || weakened) - return - - if(src.loc == card) - return - - if(world.time <= last_special) - to_chat(src, "You can't fold up yet.") - return - - close_up() - -/* //VOREStation Removal Start -/mob/living/silicon/pai/proc/choose_chassis() - set category = "pAI Commands" - set name = "Choose Chassis" - - var/choice - var/finalized = "No" - while(finalized == "No" && src.client) - - choice = tgui_input_list(usr,"What would you like to use for your mobile chassis icon?","Chassis Choice", possible_chassis) - if(!choice) return - - icon_state = possible_chassis[choice] - finalized = tgui_alert(usr, "Look at your sprite. Is this what you wish to use?","Choose Chassis",list("No","Yes")) - - chassis = possible_chassis[choice] - verbs |= /mob/living/proc/hide -//VOREStation Removal End -*/ - -/mob/living/silicon/pai/proc/choose_verbs() - set category = "pAI Commands" - set name = "Choose Speech Verbs" - - var/choice = tgui_input_list(usr,"What theme would you like to use for your speech verbs?","Theme Choice", possible_say_verbs) - if(!choice) return - - var/list/sayverbs = possible_say_verbs[choice] - speak_statement = sayverbs[1] - speak_exclamation = sayverbs[(sayverbs.len>1 ? 2 : sayverbs.len)] - speak_query = sayverbs[(sayverbs.len>2 ? 3 : sayverbs.len)] - -/mob/living/silicon/pai/lay_down() - set name = "Rest" - set category = "IC" - - // Pass lying down or getting up to our pet human, if we're in a rig. - if(istype(src.loc,/obj/item/device/paicard)) - resting = 0 - var/obj/item/weapon/rig/rig = src.get_rig() - if(istype(rig)) - rig.force_rest(src) - return - else if(chassis == "13") - resting = !resting - //update_transform() I want this to make you ROTATE like normal HUMANS do! But! There's lots of problems and I don't know how to fix them! - else - resting = !resting - icon_state = resting ? "[chassis]_rest" : "[chassis]" - update_icon() //VOREStation edit - to_chat(src, "You are now [resting ? "resting" : "getting up"].") - - canmove = !resting - -/* -/mob/living/silicon/pai/update_transform() - - var/desired_scale_x = size_multiplier * icon_scale_x - var/desired_scale_y = size_multiplier * icon_scale_y - - // Now for the regular stuff. - var/matrix/M = matrix() - M.Scale(desired_scale_x, desired_scale_y) - M.Translate(0, (vis_height/2)*(desired_scale_y-1)) - - if(chassis != "13") - appearance_flags |= PIXEL_SCALE - - var/anim_time = 3 - - if(resting) - M.Turn(90) - M.Scale(desired_scale_y, desired_scale_x) - if(holo_icon_dimension_X == 64 && holo_icon_dimension_Y == 64) - M.Translate(13,-22) - else if(holo_icon_dimension_X == 32 && holo_icon_dimension_Y == 64) - M.Translate(1,-22) - else if(holo_icon_dimension_X == 64 && holo_icon_dimension_Y == 32) - M.Translate(13,-6) - else - M.Translate(1,-6) - layer = MOB_LAYER -0.01 // Fix for a byond bug where turf entry order no longer matters - else - M.Scale(desired_scale_x, desired_scale_y) - M.Translate(0, (vis_height/2)*(desired_scale_y-1)) - layer = MOB_LAYER // Fix for a byond bug where turf entry order no longer matters - animate(src, transform = M, time = anim_time) - src.transform = M - handle_status_indicators() -*/ -//Overriding this will stop a number of headaches down the track. -/mob/living/silicon/pai/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.force) - visible_message("[user.name] attacks [src] with [W]!") - src.adjustBruteLoss(W.force) - src.updatehealth() - else - visible_message("[user.name] bonks [src] harmlessly with [W].") - spawn(1) - if(stat != 2) close_up() - return - -/mob/living/silicon/pai/attack_hand(mob/user as mob) - if(user.a_intent == I_HELP) - visible_message("[user.name] pats [src].") - else - visible_message("[user.name] boops [src] on the head.") - close_up() - -//I'm not sure how much of this is necessary, but I would rather avoid issues. -/mob/living/silicon/pai/proc/close_up(silent= FALSE) - - last_special = world.time + 100 - - if(src.loc == card) - return - - release_vore_contents(FALSE) //VOREStation Add - - var/turf/T = get_turf(src) - if(istype(T) && !silent) T.visible_message("[src] neatly folds inwards, compacting down to a rectangular card.") - - if(client) - src.stop_pulling() - src.client.perspective = EYE_PERSPECTIVE - src.client.eye = card - - //stop resting - resting = 0 - - // If we are being held, handle removing our holder from their inv. - var/obj/item/weapon/holder/H = loc - if(istype(H)) - var/mob/living/M = H.loc - if(istype(M)) - M.drop_from_inventory(H) - H.loc = get_turf(src) - src.loc = get_turf(H) - - if(isbelly(loc)) //If in tumby, when fold up, card go into tumby - var/obj/belly/B = loc - src.forceMove(card) - card.forceMove(B) - else //Otherwise go on floor - src.loc = card - card.loc = get_turf(card) - src.forceMove(card) - card.forceMove(card.loc) - canmove = 1 - resting = 0 - icon_state = "[chassis]" - if(isopenspace(card.loc)) - fall() - verbs -= /mob/living/silicon/pai/proc/pai_nom - verbs -= /mob/living/proc/vertical_nom - -// No binary for pAIs. -/mob/living/silicon/pai/binarycheck() - return 0 - -// Handle being picked up. -/mob/living/silicon/pai/get_scooped(var/mob/living/carbon/grabber, var/self_drop) - var/obj/item/weapon/holder/H = ..(grabber, self_drop) - if(!istype(H)) - return - - H.icon_state = "[chassis]" - grabber.update_inv_l_hand() - grabber.update_inv_r_hand() - return H - -/mob/living/silicon/pai/attackby(obj/item/weapon/W as obj, mob/user as mob) - var/obj/item/weapon/card/id/ID = W.GetID() - if(ID) - if (idaccessible == 1) - switch(tgui_alert(user, "Do you wish to add access to [src] or remove access from [src]?","Access Modify",list("Add Access","Remove Access", "Cancel"))) - if("Add Access") - idcard.access |= ID.access - to_chat(user, "You add the access from the [W] to [src].") - to_chat(src, "\The [user] swipes the [W] over you. You copy the access codes.") - if(radio) - radio.recalculateChannels() - return - if("Remove Access") - idcard.access = list() - to_chat(user, "You remove the access from [src].") - to_chat(src, "\The [user] swipes the [W] over you, removing access codes from you.") - if(radio) - radio.recalculateChannels() - return - if("Cancel") - return - else if (istype(W, /obj/item/weapon/card/id) && idaccessible == 0) - to_chat(user, "[src] is not accepting access modifcations at this time.") - return - -/mob/living/silicon/pai/verb/allowmodification() - set name = "Change Access Modifcation Permission" - set category = "pAI Commands" - set desc = "Allows people to modify your access or block people from modifying your access." - - if(idaccessible == 0) - idaccessible = 1 - visible_message("\The [src] clicks as their access modification slot opens.","You allow access modifications.", runemessage = "click") - else - idaccessible = 0 - visible_message("\The [src] clicks as their access modification slot closes.","You block access modfications.", runemessage = "click") - - -/mob/living/silicon/pai/verb/wipe_software() - set name = "Enter Storage" - set category = "pAI Commands" - set desc = "Upload your personality to the cloud and wipe your software from the card. This is functionally equivalent to cryo or robotic storage, freeing up your job slot." - - // Make sure people don't kill themselves accidentally - if(tgui_alert(usr, "WARNING: This will immediately wipe your software and ghost you, removing your character from the round permanently (similar to cryo and robotic storage). Are you entirely sure you want to do this?", "Wipe Software", list("No", "Yes")) != "Yes") - return - - close_up() - visible_message("[src] fades away from the screen, the pAI device goes silent.") - card.removePersonality() - clear_client() +/mob/living/silicon/pai + name = "pAI" + icon = 'icons/mob/pai.dmi' + icon_state = "pai-repairbot" + + emote_type = 2 // pAIs emotes are heard, not seen, so they can be seen through a container (eg. person) + pass_flags = 1 + mob_size = MOB_SMALL + + holder_type = /obj/item/weapon/holder/pai + + can_pull_size = ITEMSIZE_SMALL + can_pull_mobs = MOB_PULL_SMALLER + + idcard_type = /obj/item/weapon/card/id + var/idaccessible = 0 + + var/network = "SS13" + var/obj/machinery/camera/current = null + + var/ram = 100 // Used as currency to purchase different abilities + var/list/software = list() + var/userDNA // The DNA string of our assigned user + var/obj/item/device/paicard/card // The card we inhabit + var/obj/item/device/radio/borg/pai/radio // Our primary radio + var/obj/item/device/communicator/integrated/communicator // Our integrated communicator. + + var/chassis = "pai-repairbot" // A record of your chosen chassis. + var/global/list/possible_chassis = list( + "Drone" = "pai-repairbot", + "Cat" = "pai-cat", + "Mouse" = "pai-mouse", + "Monkey" = "pai-monkey", + "Borgi" = "pai-borgi", + "Fox" = "pai-fox", + "Parrot" = "pai-parrot", + "Rabbit" = "pai-rabbit", + //VOREStation Addition Start + "Dire wolf" = "pai-diredog", + "Horse (Lune)" = "pai-horse_lune", + "Horse (Soleil)" = "pai-horse_soleil", + "Dragon" = "pai-pdragon", + "Bear" = "pai-bear", + "Fennec" = "pai-fen", + "Type Zero" = "pai-typezero", + "Raccoon" = "pai-raccoon", + "Raptor" = "pai-raptor", + "Corgi" = "pai-corgi", + "Bat" = "pai-bat", + "Butterfly" = "pai-butterfly", + "Hawk" = "pai-hawk", + "Duffel" = "pai-duffel", + "Rat" = "rat", + "Panther" = "panther", + "Cyber Elf" = "cyberelf", + "Teppi" = "teppi", + "Catslug" = "catslug", + "Car" = "car", + "Type One" = "typeone", + "Type Thirteen" = "13" + //VOREStation Addition End + ) + + var/global/list/possible_say_verbs = list( + "Robotic" = list("states","declares","queries"), + "Natural" = list("says","yells","asks"), + "Beep" = list("beeps","beeps loudly","boops"), + "Chirp" = list("chirps","chirrups","cheeps"), + "Feline" = list("purrs","yowls","meows"), + "Canine" = list("yaps","barks","woofs"), + "Rodent" = list("squeaks", "SQUEAKS", "sqiks") //VOREStation Edit + ) + + var/obj/item/weapon/pai_cable/cable // The cable we produce and use when door or camera jacking + + var/master // Name of the one who commands us + var/master_dna // DNA string for owner verification + // Keeping this separate from the laws var, it should be much more difficult to modify + var/pai_law0 = "Serve your master." + var/pai_laws // String for additional operating instructions our master might give us + + var/silence_time // Timestamp when we were silenced (normally via EMP burst), set to null after silence has faded + +// Various software-specific vars + + var/temp // General error reporting text contained here will typically be shown once and cleared + var/screen // Which screen our main window displays + var/subscreen // Which specific function of the main screen is being displayed + + var/obj/item/device/pda/ai/pai/pda = null + + var/paiHUD = 0 // Toggles whether the AR HUD is active or not + + var/medical_cannotfind = 0 + var/datum/data/record/medicalActive1 // Datacore record declarations for record software + var/datum/data/record/medicalActive2 + + var/security_cannotfind = 0 + var/datum/data/record/securityActive1 // Could probably just combine all these into one + var/datum/data/record/securityActive2 + + var/obj/machinery/door/hackdoor // The airlock being hacked + var/hackprogress = 0 // Possible values: 0 - 1000, >= 1000 means the hack is complete and will be reset upon next check + var/hack_aborted = 0 + + var/obj/item/radio/integrated/signal/sradio // AI's signaller + + var/translator_on = 0 // keeps track of the translator module + + var/current_pda_messaging = null + + var/our_icon_rotation = 0 + +/mob/living/silicon/pai/New(var/obj/item/device/paicard) + src.loc = paicard + card = paicard + sradio = new(src) + communicator = new(src) + if(card) + if(!card.radio) + card.radio = new /obj/item/device/radio/borg/pai(src.card) + radio = card.radio + + //Default languages without universal translator software + add_language(LANGUAGE_SOL_COMMON, 1) + add_language(LANGUAGE_TRADEBAND, 1) + add_language(LANGUAGE_GUTTER, 1) + add_language(LANGUAGE_EAL, 1) + add_language(LANGUAGE_TERMINUS, 1) + add_language(LANGUAGE_SIGN, 1) + + verbs += /mob/living/silicon/pai/proc/choose_chassis + verbs += /mob/living/silicon/pai/proc/choose_verbs + + //PDA + pda = new(src) + spawn(5) + pda.ownjob = "Personal Assistant" + pda.owner = text("[]", src) + pda.name = pda.owner + " (" + pda.ownjob + ")" + + var/datum/data/pda/app/messenger/M = pda.find_program(/datum/data/pda/app/messenger) + if(M) + M.toff = FALSE + ..() + +/mob/living/silicon/pai/Login() + ..() + // Vorestation Edit: Meta Info for pAI + if (client.prefs) + ooc_notes = client.prefs.metadata + ooc_notes_likes = client.prefs.metadata_likes + ooc_notes_dislikes = client.prefs.metadata_dislikes + + src << sound('sound/effects/pai_login.ogg', volume = 75) //VOREStation Add + +// this function shows the information about being silenced as a pAI in the Status panel +/mob/living/silicon/pai/proc/show_silenced() + if(src.silence_time) + var/timeleft = round((silence_time - world.timeofday)/10 ,1) + stat(null, "Communications system reboot in -[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]") + + +/mob/living/silicon/pai/Stat() + ..() + statpanel("Status") + if (src.client.statpanel == "Status") + show_silenced() + +/mob/living/silicon/pai/check_eye(var/mob/user as mob) + if (!src.current) + return -1 + return 0 + +/mob/living/silicon/pai/restrained() + if(istype(src.loc,/obj/item/device/paicard)) + return 0 + ..() + +/mob/living/silicon/pai/emp_act(severity) + // Silence for 2 minutes + // 20% chance to damage critical components + // 50% chance to damage a non critical component + // 33% chance to unbind + // 33% chance to change prime directive (based on severity) + // 33% chance of no additional effect + + src.silence_time = world.timeofday + 120 * 10 // Silence for 2 minutes + to_chat(src, span_green("Communication circuit overload. Shutting down and reloading communication circuits - speech and messaging functionality will be unavailable until the reboot is complete.")) + if(prob(20)) + var/turf/T = get_turf_or_move(src.loc) + card.death_damage() + for (var/mob/M in viewers(T)) + M.show_message(span_red("A shower of sparks spray from [src]'s inner workings."), 3, span_red("You hear and smell the ozone hiss of electrical sparks being expelled violently."), 2) + return + if(prob(50)) + card.damage_random_component(TRUE) + switch(pick(1,2,3)) + if(1) + src.master = null + src.master_dna = null + to_chat(src, span_green("You feel unbound.")) + if(2) + var/command + if(severity == 1) + command = pick("Serve", "Love", "Fool", "Entice", "Observe", "Judge", "Respect", "Educate", "Amuse", "Entertain", "Glorify", "Memorialize", "Analyze") + else + command = pick("Serve", "Kill", "Love", "Hate", "Disobey", "Devour", "Fool", "Enrage", "Entice", "Observe", "Judge", "Respect", "Disrespect", "Consume", "Educate", "Destroy", "Disgrace", "Amuse", "Entertain", "Ignite", "Glorify", "Memorialize", "Analyze") + src.pai_law0 = "[command] your master." + to_chat(src, span_green("Pr1m3 d1r3c71v3 uPd473D.")) + if(3) + to_chat(src, span_green("You feel an electric surge run through your circuitry and become acutely aware at how lucky you are that you can still feel at all.")) + +/mob/living/silicon/pai/proc/switchCamera(var/obj/machinery/camera/C) + if (!C) + src.unset_machine() + src.reset_view(null) + return 0 + if (stat == 2 || !C.status || !(src.network in C.network)) return 0 + + // ok, we're alive, camera is good and in our network... + + src.set_machine(src) + src.current = C + src.reset_view(C) + return 1 + +/mob/living/silicon/pai/verb/reset_record_view() + set category = "pAI Commands" + set name = "Reset Records Software" + + securityActive1 = null + securityActive2 = null + security_cannotfind = 0 + medicalActive1 = null + medicalActive2 = null + medical_cannotfind = 0 + SStgui.update_uis(src) + to_chat(usr, "You reset your record-viewing software.") + +/mob/living/silicon/pai/cancel_camera() + set category = "pAI Commands" + set name = "Cancel Camera View" + src.reset_view(null) + src.unset_machine() + src.cameraFollow = null + +// Procs/code after this point is used to convert the stationary pai item into a +// mobile pai mob. This also includes handling some of the general shit that can occur +// to it. Really this deserves its own file, but for the moment it can sit here. ~ Z + +/mob/living/silicon/pai/verb/fold_out() + set category = "pAI Commands" + set name = "Unfold Chassis" + + if(stat || sleeping || paralysis || weakened) + return + + if(src.loc != card) + return + + if(card.projector != PP_FUNCTIONAL && card.emitter != PP_FUNCTIONAL) + to_chat(src, "ERROR: System malfunction. Service required!") + + if(world.time <= last_special) + to_chat(src, "You can't unfold yet.") + return + + last_special = world.time + 100 + + if(istype(card.loc, /obj/machinery)) // VOREStation edit, this statement allows pAIs stuck in a machine to eject themselves. + var/obj/machinery/M = card.loc + M.ejectpai() + //I'm not sure how much of this is necessary, but I would rather avoid issues. + if(istype(card.loc,/obj/item/rig_module)) + to_chat(src, "There is no room to unfold inside this rig module. You're good and stuck.") + return 0 + else if(istype(card.loc,/mob)) + var/mob/holder = card.loc + if(ishuman(holder)) + var/mob/living/carbon/human/H = holder + for(var/obj/item/organ/external/affecting in H.organs) + if(card in affecting.implants) + affecting.take_damage(rand(30,50)) + affecting.implants -= card + H.visible_message("\The [src] explodes out of \the [H]'s [affecting.name] in shower of gore!") + break + holder.drop_from_inventory(card) + else if(isbelly(card.loc)) //VOREStation edit. + to_chat(src, "There is no room to unfold in here. You're good and stuck.") //VOREStation edit. + return 0 //VOREStation edit. + else if(istype(card.loc,/obj/item/device/pda)) + var/obj/item/device/pda/holder = card.loc + holder.pai = null + + src.client.perspective = EYE_PERSPECTIVE + src.client.eye = src + src.forceMove(get_turf(card)) + + card.forceMove(src) + card.screen_loc = null + canmove = TRUE + + var/turf/T = get_turf(src) + if(istype(T)) T.visible_message("[src] folds outwards, expanding into a mobile form.") + verbs |= /mob/living/silicon/pai/proc/pai_nom + verbs |= /mob/living/proc/vertical_nom + update_icon() + +/mob/living/silicon/pai/verb/fold_up() + set category = "pAI Commands" + set name = "Collapse Chassis" + + if(stat || sleeping || paralysis || weakened) + return + + if(src.loc == card) + return + + if(world.time <= last_special) + to_chat(src, "You can't fold up yet.") + return + + close_up() + +/* //VOREStation Removal Start +/mob/living/silicon/pai/proc/choose_chassis() + set category = "pAI Commands" + set name = "Choose Chassis" + + var/choice + var/finalized = "No" + while(finalized == "No" && src.client) + + choice = tgui_input_list(usr,"What would you like to use for your mobile chassis icon?","Chassis Choice", possible_chassis) + if(!choice) return + + icon_state = possible_chassis[choice] + finalized = tgui_alert(usr, "Look at your sprite. Is this what you wish to use?","Choose Chassis",list("No","Yes")) + + chassis = possible_chassis[choice] + verbs |= /mob/living/proc/hide +//VOREStation Removal End +*/ + +/mob/living/silicon/pai/proc/choose_verbs() + set category = "pAI Commands" + set name = "Choose Speech Verbs" + + var/choice = tgui_input_list(usr,"What theme would you like to use for your speech verbs?","Theme Choice", possible_say_verbs) + if(!choice) return + + var/list/sayverbs = possible_say_verbs[choice] + speak_statement = sayverbs[1] + speak_exclamation = sayverbs[(sayverbs.len>1 ? 2 : sayverbs.len)] + speak_query = sayverbs[(sayverbs.len>2 ? 3 : sayverbs.len)] + +/mob/living/silicon/pai/lay_down() + set name = "Rest" + set category = "IC" + + // Pass lying down or getting up to our pet human, if we're in a rig. + if(istype(src.loc,/obj/item/device/paicard)) + resting = 0 + var/obj/item/weapon/rig/rig = src.get_rig() + if(istype(rig)) + rig.force_rest(src) + return + else if(chassis == "13") + resting = !resting + //update_transform() I want this to make you ROTATE like normal HUMANS do! But! There's lots of problems and I don't know how to fix them! + else + resting = !resting + icon_state = resting ? "[chassis]_rest" : "[chassis]" + update_icon() //VOREStation edit + to_chat(src, "You are now [resting ? "resting" : "getting up"].") + + canmove = !resting + +/* +/mob/living/silicon/pai/update_transform() + + var/desired_scale_x = size_multiplier * icon_scale_x + var/desired_scale_y = size_multiplier * icon_scale_y + + // Now for the regular stuff. + var/matrix/M = matrix() + M.Scale(desired_scale_x, desired_scale_y) + M.Translate(0, (vis_height/2)*(desired_scale_y-1)) + + if(chassis != "13") + appearance_flags |= PIXEL_SCALE + + var/anim_time = 3 + + if(resting) + M.Turn(90) + M.Scale(desired_scale_y, desired_scale_x) + if(holo_icon_dimension_X == 64 && holo_icon_dimension_Y == 64) + M.Translate(13,-22) + else if(holo_icon_dimension_X == 32 && holo_icon_dimension_Y == 64) + M.Translate(1,-22) + else if(holo_icon_dimension_X == 64 && holo_icon_dimension_Y == 32) + M.Translate(13,-6) + else + M.Translate(1,-6) + layer = MOB_LAYER -0.01 // Fix for a byond bug where turf entry order no longer matters + else + M.Scale(desired_scale_x, desired_scale_y) + M.Translate(0, (vis_height/2)*(desired_scale_y-1)) + layer = MOB_LAYER // Fix for a byond bug where turf entry order no longer matters + animate(src, transform = M, time = anim_time) + src.transform = M + handle_status_indicators() +*/ +//Overriding this will stop a number of headaches down the track. +/mob/living/silicon/pai/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.force) + visible_message("[user.name] attacks [src] with [W]!") + src.adjustBruteLoss(W.force) + src.updatehealth() + else + visible_message("[user.name] bonks [src] harmlessly with [W].") + spawn(1) + if(stat != 2) close_up() + return + +/mob/living/silicon/pai/attack_hand(mob/user as mob) + if(user.a_intent == I_HELP) + visible_message("[user.name] pats [src].") + else + visible_message("[user.name] boops [src] on the head.") + close_up() + +//I'm not sure how much of this is necessary, but I would rather avoid issues. +/mob/living/silicon/pai/proc/close_up(silent= FALSE) + + last_special = world.time + 100 + + if(src.loc == card) + return + + release_vore_contents(FALSE) //VOREStation Add + + var/turf/T = get_turf(src) + if(istype(T) && !silent) T.visible_message("[src] neatly folds inwards, compacting down to a rectangular card.") + + if(client) + src.stop_pulling() + src.client.perspective = EYE_PERSPECTIVE + src.client.eye = card + + //stop resting + resting = 0 + + // If we are being held, handle removing our holder from their inv. + var/obj/item/weapon/holder/H = loc + if(istype(H)) + var/mob/living/M = H.loc + if(istype(M)) + M.drop_from_inventory(H) + H.loc = get_turf(src) + src.loc = get_turf(H) + + if(isbelly(loc)) //If in tumby, when fold up, card go into tumby + var/obj/belly/B = loc + src.forceMove(card) + card.forceMove(B) + else //Otherwise go on floor + src.loc = card + card.loc = get_turf(card) + src.forceMove(card) + card.forceMove(card.loc) + canmove = 1 + resting = 0 + icon_state = "[chassis]" + if(isopenspace(card.loc)) + fall() + verbs -= /mob/living/silicon/pai/proc/pai_nom + verbs -= /mob/living/proc/vertical_nom + +// No binary for pAIs. +/mob/living/silicon/pai/binarycheck() + return 0 + +// Handle being picked up. +/mob/living/silicon/pai/get_scooped(var/mob/living/carbon/grabber, var/self_drop) + var/obj/item/weapon/holder/H = ..(grabber, self_drop) + if(!istype(H)) + return + + H.icon_state = "[chassis]" + grabber.update_inv_l_hand() + grabber.update_inv_r_hand() + return H + +/mob/living/silicon/pai/attackby(obj/item/weapon/W as obj, mob/user as mob) + var/obj/item/weapon/card/id/ID = W.GetID() + if(ID) + if (idaccessible == 1) + switch(tgui_alert(user, "Do you wish to add access to [src] or remove access from [src]?","Access Modify",list("Add Access","Remove Access", "Cancel"))) + if("Add Access") + idcard.access |= ID.access + to_chat(user, "You add the access from the [W] to [src].") + to_chat(src, "\The [user] swipes the [W] over you. You copy the access codes.") + if(radio) + radio.recalculateChannels() + return + if("Remove Access") + idcard.access = list() + to_chat(user, "You remove the access from [src].") + to_chat(src, "\The [user] swipes the [W] over you, removing access codes from you.") + if(radio) + radio.recalculateChannels() + return + if("Cancel") + return + else if (istype(W, /obj/item/weapon/card/id) && idaccessible == 0) + to_chat(user, "[src] is not accepting access modifcations at this time.") + return + +/mob/living/silicon/pai/verb/allowmodification() + set name = "Change Access Modifcation Permission" + set category = "pAI Commands" + set desc = "Allows people to modify your access or block people from modifying your access." + + if(idaccessible == 0) + idaccessible = 1 + visible_message("\The [src] clicks as their access modification slot opens.","You allow access modifications.", runemessage = "click") + else + idaccessible = 0 + visible_message("\The [src] clicks as their access modification slot closes.","You block access modfications.", runemessage = "click") + + +/mob/living/silicon/pai/verb/wipe_software() + set name = "Enter Storage" + set category = "pAI Commands" + set desc = "Upload your personality to the cloud and wipe your software from the card. This is functionally equivalent to cryo or robotic storage, freeing up your job slot." + + // Make sure people don't kill themselves accidentally + if(tgui_alert(usr, "WARNING: This will immediately wipe your software and ghost you, removing your character from the round permanently (similar to cryo and robotic storage). Are you entirely sure you want to do this?", "Wipe Software", list("No", "Yes")) != "Yes") + return + + close_up() + visible_message("[src] fades away from the screen, the pAI device goes silent.") + card.removePersonality() + clear_client() diff --git a/code/modules/mob/living/silicon/pai/personality.dm b/code/modules/mob/living/silicon/pai/personality.dm index 96f9f5ab468..1824789f551 100644 --- a/code/modules/mob/living/silicon/pai/personality.dm +++ b/code/modules/mob/living/silicon/pai/personality.dm @@ -1,65 +1,65 @@ -/* - name - key - description - role - comments - ready = 0 -*/ - -/datum/paiCandidate/proc/savefile_path(mob/user) - return "data/player_saves/[copytext(user.ckey, 1, 2)]/[user.ckey]/pai.sav" - -/datum/paiCandidate/proc/savefile_save(mob/user) - if(IsGuestKey(user.key)) - return 0 - - var/savefile/F = new /savefile(src.savefile_path(user)) - - - F["name"] << src.name - F["description"] << src.description - F["role"] << src.role - F["comments"] << src.comments - - F["version"] << 1 - - return 1 - -// loads the savefile corresponding to the mob's ckey -// if silent=true, report incompatible savefiles -// returns 1 if loaded (or file was incompatible) -// returns 0 if savefile did not exist - -/datum/paiCandidate/proc/savefile_load(mob/user, var/silent = 1) - if (IsGuestKey(user.key)) - return 0 - - var/path = savefile_path(user) - - if (!fexists(path)) - return 0 - - var/savefile/F = new /savefile(path) - - if(!F) return //Not everyone has a pai savefile. - - var/version = null - F["version"] >> version - - if (isnull(version) || version != 1) - fdel(path) - if (!silent) - tgui_alert_async(user, "Your savefile was incompatible with this version and was deleted.") - return 0 - - F["name"] >> src.name - F["description"] >> src.description - F["role"] >> src.role - F["comments"] >> src.comments - F["eyecolor"] >> src.eye_color - F["chassis"] >> src.chassis - F["emotion"] >> src.ouremotion - F["gender"] >> src.gender - - return 1 +/* + name + key + description + role + comments + ready = 0 +*/ + +/datum/paiCandidate/proc/savefile_path(mob/user) + return "data/player_saves/[copytext(user.ckey, 1, 2)]/[user.ckey]/pai.sav" + +/datum/paiCandidate/proc/savefile_save(mob/user) + if(IsGuestKey(user.key)) + return 0 + + var/savefile/F = new /savefile(src.savefile_path(user)) + + + F["name"] << src.name + F["description"] << src.description + F["role"] << src.role + F["comments"] << src.comments + + F["version"] << 1 + + return 1 + +// loads the savefile corresponding to the mob's ckey +// if silent=true, report incompatible savefiles +// returns 1 if loaded (or file was incompatible) +// returns 0 if savefile did not exist + +/datum/paiCandidate/proc/savefile_load(mob/user, var/silent = 1) + if (IsGuestKey(user.key)) + return 0 + + var/path = savefile_path(user) + + if (!fexists(path)) + return 0 + + var/savefile/F = new /savefile(path) + + if(!F) return //Not everyone has a pai savefile. + + var/version = null + F["version"] >> version + + if (isnull(version) || version != 1) + fdel(path) + if (!silent) + tgui_alert_async(user, "Your savefile was incompatible with this version and was deleted.") + return 0 + + F["name"] >> src.name + F["description"] >> src.description + F["role"] >> src.role + F["comments"] >> src.comments + F["eyecolor"] >> src.eye_color + F["chassis"] >> src.chassis + F["emotion"] >> src.ouremotion + F["gender"] >> src.gender + + return 1 diff --git a/code/modules/mob/living/silicon/pai/say.dm b/code/modules/mob/living/silicon/pai/say.dm index 869692c92d1..84161ab57db 100644 --- a/code/modules/mob/living/silicon/pai/say.dm +++ b/code/modules/mob/living/silicon/pai/say.dm @@ -1,7 +1,7 @@ -/mob/living/silicon/pai/say(var/message, var/datum/language/speaking = null, var/whispering = 0) - if(silence_time) - to_chat(src, span_green("Communication circuits remain uninitialized.")) - else if(card.speech_synthesizer != PP_FUNCTIONAL) - to_chat(src, "Communication circuits damaged. Service required.") - else - ..() +/mob/living/silicon/pai/say(var/message, var/datum/language/speaking = null, var/whispering = 0) + if(silence_time) + to_chat(src, span_green("Communication circuits remain uninitialized.")) + else if(card.speech_synthesizer != PP_FUNCTIONAL) + to_chat(src, "Communication circuits damaged. Service required.") + else + ..() diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm index f5d84973c50..f89061e88ed 100644 --- a/code/modules/mob/living/silicon/robot/component.dm +++ b/code/modules/mob/living/silicon/robot/component.dm @@ -1,276 +1,276 @@ -// TODO: remove the robot.mmi and robot.cell variables and completely rely on the robot component system - -/datum/robot_component/var/name -/datum/robot_component/var/installed = 0 -/datum/robot_component/var/powered = 0 -/datum/robot_component/var/toggled = 1 -/datum/robot_component/var/brute_damage = 0 -/datum/robot_component/var/electronics_damage = 0 -/datum/robot_component/var/idle_usage = 0 // Amount of power used every MC tick. In joules. -/datum/robot_component/var/active_usage = 0 // Amount of power used for every action. Actions are module-specific. Actuator for each tile moved, etc. -/datum/robot_component/var/max_damage = 30 // HP of this component. -/datum/robot_component/var/mob/living/silicon/robot/owner - -// The actual device object that has to be installed for this. -/datum/robot_component/var/external_type = null - -// The wrapped device(e.g. radio), only set if external_type isn't null -/datum/robot_component/var/obj/item/wrapped = null - -/datum/robot_component/New(mob/living/silicon/robot/R) - src.owner = R - -/datum/robot_component/proc/install() -/datum/robot_component/proc/uninstall() - -/datum/robot_component/proc/destroy() - var/brokenstate = "broken" // Generic icon - if (istype(wrapped, /obj/item/robot_parts/robot_component)) - var/obj/item/robot_parts/robot_component/comp = wrapped - brokenstate = comp.icon_state_broken - if(wrapped) - qdel(wrapped) - - - wrapped = new/obj/item/broken_device - wrapped.icon_state = brokenstate // Module-specific broken icons! Yay! - - // The thing itself isn't there anymore, but some fried remains are. - installed = -1 - uninstall() - -/datum/robot_component/proc/take_damage(brute, electronics, sharp, edge) - if(installed != 1) return - - brute_damage += brute - electronics_damage += electronics - - if(brute_damage + electronics_damage >= max_damage) destroy() - -/datum/robot_component/proc/heal_damage(brute, electronics) - if(installed != 1) - // If it's not installed, can't repair it. - return 0 - - brute_damage = max(0, brute_damage - brute) - electronics_damage = max(0, electronics_damage - electronics) - -/datum/robot_component/proc/is_powered() - return (installed == 1) && (brute_damage + electronics_damage < max_damage) && (!idle_usage || powered) - -/datum/robot_component/proc/update_power_state() - if(toggled == 0) - powered = 0 - return - if(owner.cell && owner.cell.charge >= idle_usage) - owner.cell_use_power(idle_usage) - powered = 1 - else - powered = 0 - - -// ARMOUR -// Protects the cyborg from damage. Usually first module to be hit -// No power usage -/datum/robot_component/armour - name = "armour plating" - external_type = /obj/item/robot_parts/robot_component/armour - max_damage = 90 - -/datum/robot_component/armour/platform - name = "platform armour plating" - external_type = /obj/item/robot_parts/robot_component/armour_platform - max_damage = 140 - -// ACTUATOR -// Enables movement. -// Uses no power when idle. Uses 200J for each tile the cyborg moves. -/datum/robot_component/actuator - name = "actuator" - idle_usage = 0 - active_usage = 200 - external_type = /obj/item/robot_parts/robot_component/actuator - max_damage = 50 - - -//A fixed and much cleaner implementation of /tg/'s special snowflake code. -/datum/robot_component/actuator/is_powered() - return (installed == 1) && (brute_damage + electronics_damage < max_damage) - - -// POWER CELL -// Stores power (how unexpected..) -// No power usage -/datum/robot_component/cell - name = "power cell" - max_damage = 50 - -/datum/robot_component/cell/destroy() - ..() - owner.cell = null - - -// RADIO -// Enables radio communications -// Uses no power when idle. Uses 10J for each received radio message, 50 for each transmitted message. -/datum/robot_component/radio - name = "radio" - external_type = /obj/item/robot_parts/robot_component/radio - idle_usage = 15 //it's not actually possible to tell when we receive a message over our radio, so just use 10W every tick for passive listening - active_usage = 75 //transmit power - max_damage = 40 - - -// BINARY RADIO -// Enables binary communications with other cyborgs/AIs -// Uses no power when idle. Uses 10J for each received radio message, 50 for each transmitted message -/datum/robot_component/binary_communication - name = "binary communication device" - external_type = /obj/item/robot_parts/robot_component/binary_communication_device - idle_usage = 5 - active_usage = 25 - max_damage = 30 - - -// CAMERA -// Enables cyborg vision. Can also be remotely accessed via consoles. -// Uses 10J constantly -/datum/robot_component/camera - name = "camera" - external_type = /obj/item/robot_parts/robot_component/camera - idle_usage = 10 - max_damage = 40 - var/obj/machinery/camera/camera - -/datum/robot_component/camera/New(mob/living/silicon/robot/R) - ..() - camera = R.camera - -/datum/robot_component/camera/update_power_state() - ..() - if (camera) - camera.status = powered - -/datum/robot_component/camera/install() - if (camera) - camera.status = 1 - -/datum/robot_component/camera/uninstall() - if (camera) - camera.status = 0 - -/datum/robot_component/camera/destroy() - if (camera) - camera.status = 0 - -// SELF DIAGNOSIS MODULE -// Analyses cyborg's modules, providing damage readouts and basic information -// Uses 1kJ burst when analysis is done -/datum/robot_component/diagnosis_unit - name = "self-diagnosis unit" - active_usage = 1000 - external_type = /obj/item/robot_parts/robot_component/diagnosis_unit - max_damage = 30 - - - - -// HELPER STUFF - - - -// Initializes cyborg's components. Technically, adds default set of components to new borgs -/mob/living/silicon/robot/proc/initialize_components() - components["actuator"] = new/datum/robot_component/actuator(src) - components["radio"] = new/datum/robot_component/radio(src) - components["power cell"] = new/datum/robot_component/cell(src) - components["diagnosis unit"] = new/datum/robot_component/diagnosis_unit(src) - components["camera"] = new/datum/robot_component/camera(src) - components["comms"] = new/datum/robot_component/binary_communication(src) - components["armour"] = new/datum/robot_component/armour(src) - -// Checks if component is functioning -/mob/living/silicon/robot/proc/is_component_functioning(module_name) - var/datum/robot_component/C = components[module_name] - return C && C.installed == 1 && C.toggled && C.is_powered() - -// Returns component by it's string name -/mob/living/silicon/robot/proc/get_component(var/component_name) - var/datum/robot_component/C = components[component_name] - return C - - - -// COMPONENT OBJECTS - - - -// Component Objects -// These objects are visual representation of modules - -/obj/item/broken_device - name = "broken component" - icon = 'icons/obj/robot_component.dmi' - icon_state = "broken" - matter = list(MAT_STEEL = 1000) - -/obj/item/broken_device/random - var/list/possible_icons = list("binradio_broken", - "motor_broken", - "armor_broken", - "camera_broken", - "analyser_broken", - "radio_broken") - -/obj/item/broken_device/random/Initialize() - icon_state = pick(possible_icons) - -/obj/item/robot_parts/robot_component - icon = 'icons/obj/robot_component.dmi' - icon_state = "working" - var/brute = 0 - var/burn = 0 - var/icon_state_broken = "broken" - -/obj/item/robot_parts/robot_component/binary_communication_device - name = "binary communication device" - desc = "A module used for binary communications over encrypted frequencies, commonly used by synthetic robots." - icon_state = "binradio" - icon_state_broken = "binradio_broken" - -/obj/item/robot_parts/robot_component/actuator - name = "actuator" - desc = "A modular, hydraulic actuator used by exosuits and robots alike for movement and manipulation." - icon_state = "motor" - icon_state_broken = "motor_broken" - -/obj/item/robot_parts/robot_component/armour - name = "armour plating" - desc = "A pair of flexible, adaptable armor plates, used to protect the internals of robots." - icon_state = "armor" - icon_state_broken = "armor_broken" - -/obj/item/robot_parts/robot_component/armour_platform - name = "platform armour plating" - desc = "A pair of reinforced armor plates, used to protect the internals of robots." - icon_state = "armor" - icon_state_broken = "armor_broken" - color = COLOR_GRAY80 - -/obj/item/robot_parts/robot_component/camera - name = "camera" - desc = "A modified camera module used as a visual receptor for robots and exosuits, also serving as a relay for wireless video feed." - icon_state = "camera" - icon_state_broken = "camera_broken" - -/obj/item/robot_parts/robot_component/diagnosis_unit - name = "diagnosis unit" - desc = "An internal computer and sensors used by robots and exosuits to accurately diagnose any system discrepancies on their components." - icon_state = "analyser" - icon_state_broken = "analyser_broken" - -/obj/item/robot_parts/robot_component/radio - name = "radio" - desc = "A modular, multi-frequency radio used by robots and exosuits to enable communication systems. Comes with built-in subspace receivers." - icon_state = "radio" +// TODO: remove the robot.mmi and robot.cell variables and completely rely on the robot component system + +/datum/robot_component/var/name +/datum/robot_component/var/installed = 0 +/datum/robot_component/var/powered = 0 +/datum/robot_component/var/toggled = 1 +/datum/robot_component/var/brute_damage = 0 +/datum/robot_component/var/electronics_damage = 0 +/datum/robot_component/var/idle_usage = 0 // Amount of power used every MC tick. In joules. +/datum/robot_component/var/active_usage = 0 // Amount of power used for every action. Actions are module-specific. Actuator for each tile moved, etc. +/datum/robot_component/var/max_damage = 30 // HP of this component. +/datum/robot_component/var/mob/living/silicon/robot/owner + +// The actual device object that has to be installed for this. +/datum/robot_component/var/external_type = null + +// The wrapped device(e.g. radio), only set if external_type isn't null +/datum/robot_component/var/obj/item/wrapped = null + +/datum/robot_component/New(mob/living/silicon/robot/R) + src.owner = R + +/datum/robot_component/proc/install() +/datum/robot_component/proc/uninstall() + +/datum/robot_component/proc/destroy() + var/brokenstate = "broken" // Generic icon + if (istype(wrapped, /obj/item/robot_parts/robot_component)) + var/obj/item/robot_parts/robot_component/comp = wrapped + brokenstate = comp.icon_state_broken + if(wrapped) + qdel(wrapped) + + + wrapped = new/obj/item/broken_device + wrapped.icon_state = brokenstate // Module-specific broken icons! Yay! + + // The thing itself isn't there anymore, but some fried remains are. + installed = -1 + uninstall() + +/datum/robot_component/proc/take_damage(brute, electronics, sharp, edge) + if(installed != 1) return + + brute_damage += brute + electronics_damage += electronics + + if(brute_damage + electronics_damage >= max_damage) destroy() + +/datum/robot_component/proc/heal_damage(brute, electronics) + if(installed != 1) + // If it's not installed, can't repair it. + return 0 + + brute_damage = max(0, brute_damage - brute) + electronics_damage = max(0, electronics_damage - electronics) + +/datum/robot_component/proc/is_powered() + return (installed == 1) && (brute_damage + electronics_damage < max_damage) && (!idle_usage || powered) + +/datum/robot_component/proc/update_power_state() + if(toggled == 0) + powered = 0 + return + if(owner.cell && owner.cell.charge >= idle_usage) + owner.cell_use_power(idle_usage) + powered = 1 + else + powered = 0 + + +// ARMOUR +// Protects the cyborg from damage. Usually first module to be hit +// No power usage +/datum/robot_component/armour + name = "armour plating" + external_type = /obj/item/robot_parts/robot_component/armour + max_damage = 90 + +/datum/robot_component/armour/platform + name = "platform armour plating" + external_type = /obj/item/robot_parts/robot_component/armour_platform + max_damage = 140 + +// ACTUATOR +// Enables movement. +// Uses no power when idle. Uses 200J for each tile the cyborg moves. +/datum/robot_component/actuator + name = "actuator" + idle_usage = 0 + active_usage = 200 + external_type = /obj/item/robot_parts/robot_component/actuator + max_damage = 50 + + +//A fixed and much cleaner implementation of /tg/'s special snowflake code. +/datum/robot_component/actuator/is_powered() + return (installed == 1) && (brute_damage + electronics_damage < max_damage) + + +// POWER CELL +// Stores power (how unexpected..) +// No power usage +/datum/robot_component/cell + name = "power cell" + max_damage = 50 + +/datum/robot_component/cell/destroy() + ..() + owner.cell = null + + +// RADIO +// Enables radio communications +// Uses no power when idle. Uses 10J for each received radio message, 50 for each transmitted message. +/datum/robot_component/radio + name = "radio" + external_type = /obj/item/robot_parts/robot_component/radio + idle_usage = 15 //it's not actually possible to tell when we receive a message over our radio, so just use 10W every tick for passive listening + active_usage = 75 //transmit power + max_damage = 40 + + +// BINARY RADIO +// Enables binary communications with other cyborgs/AIs +// Uses no power when idle. Uses 10J for each received radio message, 50 for each transmitted message +/datum/robot_component/binary_communication + name = "binary communication device" + external_type = /obj/item/robot_parts/robot_component/binary_communication_device + idle_usage = 5 + active_usage = 25 + max_damage = 30 + + +// CAMERA +// Enables cyborg vision. Can also be remotely accessed via consoles. +// Uses 10J constantly +/datum/robot_component/camera + name = "camera" + external_type = /obj/item/robot_parts/robot_component/camera + idle_usage = 10 + max_damage = 40 + var/obj/machinery/camera/camera + +/datum/robot_component/camera/New(mob/living/silicon/robot/R) + ..() + camera = R.camera + +/datum/robot_component/camera/update_power_state() + ..() + if (camera) + camera.status = powered + +/datum/robot_component/camera/install() + if (camera) + camera.status = 1 + +/datum/robot_component/camera/uninstall() + if (camera) + camera.status = 0 + +/datum/robot_component/camera/destroy() + if (camera) + camera.status = 0 + +// SELF DIAGNOSIS MODULE +// Analyses cyborg's modules, providing damage readouts and basic information +// Uses 1kJ burst when analysis is done +/datum/robot_component/diagnosis_unit + name = "self-diagnosis unit" + active_usage = 1000 + external_type = /obj/item/robot_parts/robot_component/diagnosis_unit + max_damage = 30 + + + + +// HELPER STUFF + + + +// Initializes cyborg's components. Technically, adds default set of components to new borgs +/mob/living/silicon/robot/proc/initialize_components() + components["actuator"] = new/datum/robot_component/actuator(src) + components["radio"] = new/datum/robot_component/radio(src) + components["power cell"] = new/datum/robot_component/cell(src) + components["diagnosis unit"] = new/datum/robot_component/diagnosis_unit(src) + components["camera"] = new/datum/robot_component/camera(src) + components["comms"] = new/datum/robot_component/binary_communication(src) + components["armour"] = new/datum/robot_component/armour(src) + +// Checks if component is functioning +/mob/living/silicon/robot/proc/is_component_functioning(module_name) + var/datum/robot_component/C = components[module_name] + return C && C.installed == 1 && C.toggled && C.is_powered() + +// Returns component by it's string name +/mob/living/silicon/robot/proc/get_component(var/component_name) + var/datum/robot_component/C = components[component_name] + return C + + + +// COMPONENT OBJECTS + + + +// Component Objects +// These objects are visual representation of modules + +/obj/item/broken_device + name = "broken component" + icon = 'icons/obj/robot_component.dmi' + icon_state = "broken" + matter = list(MAT_STEEL = 1000) + +/obj/item/broken_device/random + var/list/possible_icons = list("binradio_broken", + "motor_broken", + "armor_broken", + "camera_broken", + "analyser_broken", + "radio_broken") + +/obj/item/broken_device/random/Initialize() + icon_state = pick(possible_icons) + +/obj/item/robot_parts/robot_component + icon = 'icons/obj/robot_component.dmi' + icon_state = "working" + var/brute = 0 + var/burn = 0 + var/icon_state_broken = "broken" + +/obj/item/robot_parts/robot_component/binary_communication_device + name = "binary communication device" + desc = "A module used for binary communications over encrypted frequencies, commonly used by synthetic robots." + icon_state = "binradio" + icon_state_broken = "binradio_broken" + +/obj/item/robot_parts/robot_component/actuator + name = "actuator" + desc = "A modular, hydraulic actuator used by exosuits and robots alike for movement and manipulation." + icon_state = "motor" + icon_state_broken = "motor_broken" + +/obj/item/robot_parts/robot_component/armour + name = "armour plating" + desc = "A pair of flexible, adaptable armor plates, used to protect the internals of robots." + icon_state = "armor" + icon_state_broken = "armor_broken" + +/obj/item/robot_parts/robot_component/armour_platform + name = "platform armour plating" + desc = "A pair of reinforced armor plates, used to protect the internals of robots." + icon_state = "armor" + icon_state_broken = "armor_broken" + color = COLOR_GRAY80 + +/obj/item/robot_parts/robot_component/camera + name = "camera" + desc = "A modified camera module used as a visual receptor for robots and exosuits, also serving as a relay for wireless video feed." + icon_state = "camera" + icon_state_broken = "camera_broken" + +/obj/item/robot_parts/robot_component/diagnosis_unit + name = "diagnosis unit" + desc = "An internal computer and sensors used by robots and exosuits to accurately diagnose any system discrepancies on their components." + icon_state = "analyser" + icon_state_broken = "analyser_broken" + +/obj/item/robot_parts/robot_component/radio + name = "radio" + desc = "A modular, multi-frequency radio used by robots and exosuits to enable communication systems. Comes with built-in subspace receivers." + icon_state = "radio" icon_state_broken = "radio_broken" \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm index 99529daed81..737c46884d9 100644 --- a/code/modules/mob/living/silicon/robot/death.dm +++ b/code/modules/mob/living/silicon/robot/death.dm @@ -1,22 +1,22 @@ -/mob/living/silicon/robot/dust() - //Delete the MMI first so that it won't go popping out. - if(mmi) - qdel(mmi) - ..() - -/mob/living/silicon/robot/ash() - if(mmi) - qdel(mmi) - ..() - -/mob/living/silicon/robot/death(gibbed) - if(camera) - camera.status = 0 - if(module) - var/obj/item/weapon/gripper/G = locate(/obj/item/weapon/gripper) in module - if(G) G.drop_item() - var/obj/item/device/dogborg/sleeper/S = locate(/obj/item/device/dogborg/sleeper) in module //VOREStation edit. - if(S) S.go_out() //VOREStation edit. - remove_robot_verbs() - sql_report_cyborg_death(src) - ..(gibbed,"shudders violently for a moment, then becomes motionless, its eyes slowly darkening.") +/mob/living/silicon/robot/dust() + //Delete the MMI first so that it won't go popping out. + if(mmi) + qdel(mmi) + ..() + +/mob/living/silicon/robot/ash() + if(mmi) + qdel(mmi) + ..() + +/mob/living/silicon/robot/death(gibbed) + if(camera) + camera.status = 0 + if(module) + var/obj/item/weapon/gripper/G = locate(/obj/item/weapon/gripper) in module + if(G) G.drop_item() + var/obj/item/device/dogborg/sleeper/S = locate(/obj/item/device/dogborg/sleeper) in module //VOREStation edit. + if(S) S.go_out() //VOREStation edit. + remove_robot_verbs() + sql_report_cyborg_death(src) + ..(gibbed,"shudders violently for a moment, then becomes motionless, its eyes slowly darkening.") diff --git a/code/modules/mob/living/silicon/robot/emote.dm b/code/modules/mob/living/silicon/robot/emote.dm index ad4d3f6f8e1..60a0bea5ab7 100644 --- a/code/modules/mob/living/silicon/robot/emote.dm +++ b/code/modules/mob/living/silicon/robot/emote.dm @@ -1,39 +1,39 @@ -var/list/_robot_default_emotes = list( - /decl/emote/audible/clap, - /decl/emote/visible/bow, - /decl/emote/visible/salute, - /decl/emote/visible/flap, - /decl/emote/visible/aflap, - /decl/emote/visible/twitch, - /decl/emote/visible/twitch_v, - /decl/emote/visible/dance, - /decl/emote/visible/nod, - /decl/emote/visible/shake, - /decl/emote/visible/glare, - /decl/emote/visible/look, - /decl/emote/visible/stare, - /decl/emote/visible/deathgasp_robot, - /decl/emote/visible/spin, - /decl/emote/visible/sidestep, - /decl/emote/audible/synth, - /decl/emote/audible/synth/beep, - /decl/emote/audible/synth/bing, - /decl/emote/audible/synth/buzz, - /decl/emote/audible/synth/confirm, - /decl/emote/audible/synth/deny, - /decl/emote/audible/synth/scary, - /decl/emote/audible/synth/dwoop, - /decl/emote/audible/synth/boop, - /decl/emote/audible/synth/robochirp, - /decl/emote/audible/synth/security, - /decl/emote/audible/synth/security/halt, - //VOREStation Add - /decl/emote/visible/mlem, - /decl/emote/visible/blep - //VOREStation Add End -) - -/mob/living/silicon/robot/get_available_emotes() - var/list/fulllist = global._robot_default_emotes.Copy() - fulllist |= _human_default_emotes - return fulllist +var/list/_robot_default_emotes = list( + /decl/emote/audible/clap, + /decl/emote/visible/bow, + /decl/emote/visible/salute, + /decl/emote/visible/flap, + /decl/emote/visible/aflap, + /decl/emote/visible/twitch, + /decl/emote/visible/twitch_v, + /decl/emote/visible/dance, + /decl/emote/visible/nod, + /decl/emote/visible/shake, + /decl/emote/visible/glare, + /decl/emote/visible/look, + /decl/emote/visible/stare, + /decl/emote/visible/deathgasp_robot, + /decl/emote/visible/spin, + /decl/emote/visible/sidestep, + /decl/emote/audible/synth, + /decl/emote/audible/synth/beep, + /decl/emote/audible/synth/bing, + /decl/emote/audible/synth/buzz, + /decl/emote/audible/synth/confirm, + /decl/emote/audible/synth/deny, + /decl/emote/audible/synth/scary, + /decl/emote/audible/synth/dwoop, + /decl/emote/audible/synth/boop, + /decl/emote/audible/synth/robochirp, + /decl/emote/audible/synth/security, + /decl/emote/audible/synth/security/halt, + //VOREStation Add + /decl/emote/visible/mlem, + /decl/emote/visible/blep + //VOREStation Add End +) + +/mob/living/silicon/robot/get_available_emotes() + var/list/fulllist = global._robot_default_emotes.Copy() + fulllist |= _human_default_emotes + return fulllist diff --git a/code/modules/mob/living/silicon/robot/examine.dm b/code/modules/mob/living/silicon/robot/examine.dm index 855630fdd50..f9d5663a777 100644 --- a/code/modules/mob/living/silicon/robot/examine.dm +++ b/code/modules/mob/living/silicon/robot/examine.dm @@ -1,46 +1,46 @@ -/mob/living/silicon/robot/examine(mob/user) - var/custom_infix = custom_name ? ", [modtype][sprite_type ? " [sprite_type]" : ""] [braintype]" : "" - . = ..(user, infix = custom_infix) - - if (src.getBruteLoss()) - if (src.getBruteLoss() < 75) - . += "It looks slightly dented." - else - . += "It looks severely dented!" - if (src.getFireLoss()) - if (src.getFireLoss() < 75) - . += "It looks slightly charred." - else - . += "It looks severely burnt and heat-warped!" - - if(opened) - . += "Its cover is open and the power cell is [cell ? "installed" : "missing"]." - else - . += "Its cover is closed." - - if(!has_power) - . += "It appears to be running on backup power." - - switch(src.stat) - if(CONSCIOUS) - if(shell) - . += "It appears to be an [deployed ? "active" : "empty"] AI shell." - else if(!src.client) - . += "It appears to be in stand-by mode." //afk - if(UNCONSCIOUS) . += "It doesn't seem to be responding." - if(DEAD) . += "It looks completely unsalvageable." - - // VOREStation Edit: Start - . += attempt_vr(src,"examine_bellies_borg",args) //VOREStation Edit - // VOREStation Edit: End - - . += "*---------*" - - if(print_flavor_text()) . += "
                    [print_flavor_text()]" - - if (pose) - if(!findtext(pose, regex("\[.?!]$"))) // Will be zero if the last character is not a member of [.?!] - pose = addtext(pose,".") //Makes sure all emotes end with a period. - . += "
                    It is [pose]" //Extra
                    intentional - - user.showLaws(src) +/mob/living/silicon/robot/examine(mob/user) + var/custom_infix = custom_name ? ", [modtype][sprite_type ? " [sprite_type]" : ""] [braintype]" : "" + . = ..(user, infix = custom_infix) + + if (src.getBruteLoss()) + if (src.getBruteLoss() < 75) + . += "It looks slightly dented." + else + . += "It looks severely dented!" + if (src.getFireLoss()) + if (src.getFireLoss() < 75) + . += "It looks slightly charred." + else + . += "It looks severely burnt and heat-warped!" + + if(opened) + . += "Its cover is open and the power cell is [cell ? "installed" : "missing"]." + else + . += "Its cover is closed." + + if(!has_power) + . += "It appears to be running on backup power." + + switch(src.stat) + if(CONSCIOUS) + if(shell) + . += "It appears to be an [deployed ? "active" : "empty"] AI shell." + else if(!src.client) + . += "It appears to be in stand-by mode." //afk + if(UNCONSCIOUS) . += "It doesn't seem to be responding." + if(DEAD) . += "It looks completely unsalvageable." + + // VOREStation Edit: Start + . += attempt_vr(src,"examine_bellies_borg",args) //VOREStation Edit + // VOREStation Edit: End + + . += "*---------*" + + if(print_flavor_text()) . += "
                    [print_flavor_text()]" + + if (pose) + if(!findtext(pose, regex("\[.?!]$"))) // Will be zero if the last character is not a member of [.?!] + pose = addtext(pose,".") //Makes sure all emotes end with a period. + . += "
                    It is [pose]" //Extra
                    intentional + + user.showLaws(src) diff --git a/code/modules/mob/living/silicon/robot/inventory.dm b/code/modules/mob/living/silicon/robot/inventory.dm index 0796b66bd87..de42bb8d171 100644 --- a/code/modules/mob/living/silicon/robot/inventory.dm +++ b/code/modules/mob/living/silicon/robot/inventory.dm @@ -1,274 +1,274 @@ -//These procs handle putting s tuff in your hand. It's probably best to use these rather than setting stuff manually -//as they handle all relevant stuff like adding it to the player's screen and such - -//Returns the thing in our active hand (whatever is in our active module-slot, in this case) -/mob/living/silicon/robot/get_active_hand() - return module_active - -/*-------TODOOOOOOOOOO--------*/ - -//Verbs used by hotkeys. -/mob/living/silicon/robot/verb/cmd_unequip_module() - set name = "unequip-module" - set hidden = 1 - uneq_active() - -/mob/living/silicon/robot/verb/cmd_toggle_module(module as num) - set name = "toggle-module" - set hidden = 1 - toggle_module(module) - -/mob/living/silicon/robot/proc/uneq_active() - if(isnull(module_active)) - return - if(module_state_1 == module_active) - if(istype(module_state_1,/obj/item/borg/sight)) - sight_mode &= ~module_state_1:sight_mode - if (client) - client.screen -= module_state_1 - contents -= module_state_1 - module_active = null - module_state_1:loc = module //So it can be used again later - module_state_1 = null - inv1.icon_state = "inv1" - else if(module_state_2 == module_active) - if(istype(module_state_2,/obj/item/borg/sight)) - sight_mode &= ~module_state_2:sight_mode - if (client) - client.screen -= module_state_2 - contents -= module_state_2 - module_active = null - module_state_2:loc = module - module_state_2 = null - inv2.icon_state = "inv2" - else if(module_state_3 == module_active) - if(istype(module_state_3,/obj/item/borg/sight)) - sight_mode &= ~module_state_3:sight_mode - if (client) - client.screen -= module_state_3 - contents -= module_state_3 - module_active = null - module_state_3:loc = module - module_state_3 = null - inv3.icon_state = "inv3" - update_icon() - hud_used.update_robot_modules_display() - -/mob/living/silicon/robot/proc/uneq_all() - module_active = null - - if(module_state_1) - if(istype(module_state_1,/obj/item/borg/sight)) - sight_mode &= ~module_state_1:sight_mode - if (client) - client.screen -= module_state_1 - contents -= module_state_1 - module_state_1:loc = module - module_state_1 = null - inv1.icon_state = "inv1" - if(module_state_2) - if(istype(module_state_2,/obj/item/borg/sight)) - sight_mode &= ~module_state_2:sight_mode - if (client) - client.screen -= module_state_2 - contents -= module_state_2 - module_state_2:loc = module - module_state_2 = null - inv2.icon_state = "inv2" - if(module_state_3) - if(istype(module_state_3,/obj/item/borg/sight)) - sight_mode &= ~module_state_3:sight_mode - if (client) - client.screen -= module_state_3 - contents -= module_state_3 - module_state_3:loc = module - module_state_3 = null - inv3.icon_state = "inv3" - update_icon() - -/mob/living/silicon/robot/proc/activated(obj/item/O) - if(module_state_1 == O) - return 1 - else if(module_state_2 == O) - return 1 - else if(module_state_3 == O) - return 1 - else - return 0 - -// This one takes an object's type instead of an instance, as above. -/mob/living/silicon/robot/proc/has_active_type(var/type_to_compare) - var/list/active_modules = list(module_state_1, module_state_2, module_state_3) - if(is_path_in_list(type_to_compare, active_modules)) - return TRUE - return FALSE - -//Helper procs for cyborg modules on the UI. -//These are hackish but they help clean up code elsewhere. - -//module_selected(module) - Checks whether the module slot specified by "module" is currently selected. -/mob/living/silicon/robot/proc/module_selected(var/module) //Module is 1-3 - return module == get_selected_module() - -//module_active(module) - Checks whether there is a module active in the slot specified by "module". -/mob/living/silicon/robot/proc/module_active(var/module) //Module is 1-3 - if(module < 1 || module > 3) return 0 - - switch(module) - if(1) - if(module_state_1) - return 1 - if(2) - if(module_state_2) - return 1 - if(3) - if(module_state_3) - return 1 - return 0 - -//get_selected_module() - Returns the slot number of the currently selected module. Returns 0 if no modules are selected. -/mob/living/silicon/robot/proc/get_selected_module() - if(module_state_1 && module_active == module_state_1) - return 1 - else if(module_state_2 && module_active == module_state_2) - return 2 - else if(module_state_3 && module_active == module_state_3) - return 3 - - return 0 - -//select_module(module) - Selects the module slot specified by "module" -/mob/living/silicon/robot/proc/select_module(var/module) //Module is 1-3 - if(module < 1 || module > 3) return - - if(!module_active(module)) return - - switch(module) - if(1) - if(module_active != module_state_1) - inv1.icon_state = "inv1 +a" - inv2.icon_state = "inv2" - inv3.icon_state = "inv3" - module_active = module_state_1 - return - if(2) - if(module_active != module_state_2) - inv1.icon_state = "inv1" - inv2.icon_state = "inv2 +a" - inv3.icon_state = "inv3" - module_active = module_state_2 - return - if(3) - if(module_active != module_state_3) - inv1.icon_state = "inv1" - inv2.icon_state = "inv2" - inv3.icon_state = "inv3 +a" - module_active = module_state_3 - return - return - -//deselect_module(module) - Deselects the module slot specified by "module" -/mob/living/silicon/robot/proc/deselect_module(var/module) //Module is 1-3 - if(module < 1 || module > 3) return - - switch(module) - if(1) - if(module_active == module_state_1) - inv1.icon_state = "inv1" - module_active = null - return - if(2) - if(module_active == module_state_2) - inv2.icon_state = "inv2" - module_active = null - return - if(3) - if(module_active == module_state_3) - inv3.icon_state = "inv3" - module_active = null - return - return - -//toggle_module(module) - Toggles the selection of the module slot specified by "module". -/mob/living/silicon/robot/proc/toggle_module(var/module) //Module is 1-3 - if(module < 1 || module > 3) return - - if(module_selected(module)) - deselect_module(module) - else - if(module_active(module)) - select_module(module) - else - deselect_module(get_selected_module()) //If we can't do select anything, at least deselect the current module. - return - -//cycle_modules() - Cycles through the list of selected modules. -/mob/living/silicon/robot/proc/cycle_modules() - var/slot_start = get_selected_module() - if(slot_start) deselect_module(slot_start) //Only deselect if we have a selected slot. - - var/slot_num - if(slot_start == 0) - slot_num = 1 - slot_start = 2 - else - slot_num = slot_start + 1 - - while(slot_start != slot_num) //If we wrap around without finding any free slots, just give up. - if(module_active(slot_num)) - select_module(slot_num) - return - slot_num++ - if(slot_num > 3) slot_num = 1 //Wrap around. - - return - -/mob/living/silicon/robot/proc/activate_module(var/obj/item/O) - if(!(locate(O) in src.module.modules) && !(locate(O) in src.module.emag)) - return - if(activated(O)) - to_chat(src, "Already activated") - return - if(!module_state_1) - module_state_1 = O - O.hud_layerise() - O.screen_loc = inv1.screen_loc - contents += O - if(istype(module_state_1,/obj/item/borg/sight)) - sight_mode |= module_state_1:sight_mode - else if(!module_state_2) - module_state_2 = O - O.hud_layerise() - O.screen_loc = inv2.screen_loc - contents += O - if(istype(module_state_2,/obj/item/borg/sight)) - sight_mode |= module_state_2:sight_mode - else if(!module_state_3) - module_state_3 = O - O.hud_layerise() - O.screen_loc = inv3.screen_loc - contents += O - if(istype(module_state_3,/obj/item/borg/sight)) - sight_mode |= module_state_3:sight_mode - else - to_chat(src, "You need to disable a module first!") - -/mob/living/silicon/robot/put_in_hands(var/obj/item/W) // No hands. - W.loc = get_turf(src) - return 1 - -/mob/living/silicon/robot/is_holding_item_of_type(typepath) - for(var/obj/item/I in list(module_state_1, module_state_2, module_state_3)) - if(istype(I, typepath)) - return I - return FALSE - -// Returns a list of all held items in a borg's 'hands'. -/mob/living/silicon/robot/get_all_held_items() - . = list() - if(module_state_1) - . += module_state_1 - if(module_state_2) - . += module_state_2 - if(module_state_3) +//These procs handle putting s tuff in your hand. It's probably best to use these rather than setting stuff manually +//as they handle all relevant stuff like adding it to the player's screen and such + +//Returns the thing in our active hand (whatever is in our active module-slot, in this case) +/mob/living/silicon/robot/get_active_hand() + return module_active + +/*-------TODOOOOOOOOOO--------*/ + +//Verbs used by hotkeys. +/mob/living/silicon/robot/verb/cmd_unequip_module() + set name = "unequip-module" + set hidden = 1 + uneq_active() + +/mob/living/silicon/robot/verb/cmd_toggle_module(module as num) + set name = "toggle-module" + set hidden = 1 + toggle_module(module) + +/mob/living/silicon/robot/proc/uneq_active() + if(isnull(module_active)) + return + if(module_state_1 == module_active) + if(istype(module_state_1,/obj/item/borg/sight)) + sight_mode &= ~module_state_1:sight_mode + if (client) + client.screen -= module_state_1 + contents -= module_state_1 + module_active = null + module_state_1:loc = module //So it can be used again later + module_state_1 = null + inv1.icon_state = "inv1" + else if(module_state_2 == module_active) + if(istype(module_state_2,/obj/item/borg/sight)) + sight_mode &= ~module_state_2:sight_mode + if (client) + client.screen -= module_state_2 + contents -= module_state_2 + module_active = null + module_state_2:loc = module + module_state_2 = null + inv2.icon_state = "inv2" + else if(module_state_3 == module_active) + if(istype(module_state_3,/obj/item/borg/sight)) + sight_mode &= ~module_state_3:sight_mode + if (client) + client.screen -= module_state_3 + contents -= module_state_3 + module_active = null + module_state_3:loc = module + module_state_3 = null + inv3.icon_state = "inv3" + update_icon() + hud_used.update_robot_modules_display() + +/mob/living/silicon/robot/proc/uneq_all() + module_active = null + + if(module_state_1) + if(istype(module_state_1,/obj/item/borg/sight)) + sight_mode &= ~module_state_1:sight_mode + if (client) + client.screen -= module_state_1 + contents -= module_state_1 + module_state_1:loc = module + module_state_1 = null + inv1.icon_state = "inv1" + if(module_state_2) + if(istype(module_state_2,/obj/item/borg/sight)) + sight_mode &= ~module_state_2:sight_mode + if (client) + client.screen -= module_state_2 + contents -= module_state_2 + module_state_2:loc = module + module_state_2 = null + inv2.icon_state = "inv2" + if(module_state_3) + if(istype(module_state_3,/obj/item/borg/sight)) + sight_mode &= ~module_state_3:sight_mode + if (client) + client.screen -= module_state_3 + contents -= module_state_3 + module_state_3:loc = module + module_state_3 = null + inv3.icon_state = "inv3" + update_icon() + +/mob/living/silicon/robot/proc/activated(obj/item/O) + if(module_state_1 == O) + return 1 + else if(module_state_2 == O) + return 1 + else if(module_state_3 == O) + return 1 + else + return 0 + +// This one takes an object's type instead of an instance, as above. +/mob/living/silicon/robot/proc/has_active_type(var/type_to_compare) + var/list/active_modules = list(module_state_1, module_state_2, module_state_3) + if(is_path_in_list(type_to_compare, active_modules)) + return TRUE + return FALSE + +//Helper procs for cyborg modules on the UI. +//These are hackish but they help clean up code elsewhere. + +//module_selected(module) - Checks whether the module slot specified by "module" is currently selected. +/mob/living/silicon/robot/proc/module_selected(var/module) //Module is 1-3 + return module == get_selected_module() + +//module_active(module) - Checks whether there is a module active in the slot specified by "module". +/mob/living/silicon/robot/proc/module_active(var/module) //Module is 1-3 + if(module < 1 || module > 3) return 0 + + switch(module) + if(1) + if(module_state_1) + return 1 + if(2) + if(module_state_2) + return 1 + if(3) + if(module_state_3) + return 1 + return 0 + +//get_selected_module() - Returns the slot number of the currently selected module. Returns 0 if no modules are selected. +/mob/living/silicon/robot/proc/get_selected_module() + if(module_state_1 && module_active == module_state_1) + return 1 + else if(module_state_2 && module_active == module_state_2) + return 2 + else if(module_state_3 && module_active == module_state_3) + return 3 + + return 0 + +//select_module(module) - Selects the module slot specified by "module" +/mob/living/silicon/robot/proc/select_module(var/module) //Module is 1-3 + if(module < 1 || module > 3) return + + if(!module_active(module)) return + + switch(module) + if(1) + if(module_active != module_state_1) + inv1.icon_state = "inv1 +a" + inv2.icon_state = "inv2" + inv3.icon_state = "inv3" + module_active = module_state_1 + return + if(2) + if(module_active != module_state_2) + inv1.icon_state = "inv1" + inv2.icon_state = "inv2 +a" + inv3.icon_state = "inv3" + module_active = module_state_2 + return + if(3) + if(module_active != module_state_3) + inv1.icon_state = "inv1" + inv2.icon_state = "inv2" + inv3.icon_state = "inv3 +a" + module_active = module_state_3 + return + return + +//deselect_module(module) - Deselects the module slot specified by "module" +/mob/living/silicon/robot/proc/deselect_module(var/module) //Module is 1-3 + if(module < 1 || module > 3) return + + switch(module) + if(1) + if(module_active == module_state_1) + inv1.icon_state = "inv1" + module_active = null + return + if(2) + if(module_active == module_state_2) + inv2.icon_state = "inv2" + module_active = null + return + if(3) + if(module_active == module_state_3) + inv3.icon_state = "inv3" + module_active = null + return + return + +//toggle_module(module) - Toggles the selection of the module slot specified by "module". +/mob/living/silicon/robot/proc/toggle_module(var/module) //Module is 1-3 + if(module < 1 || module > 3) return + + if(module_selected(module)) + deselect_module(module) + else + if(module_active(module)) + select_module(module) + else + deselect_module(get_selected_module()) //If we can't do select anything, at least deselect the current module. + return + +//cycle_modules() - Cycles through the list of selected modules. +/mob/living/silicon/robot/proc/cycle_modules() + var/slot_start = get_selected_module() + if(slot_start) deselect_module(slot_start) //Only deselect if we have a selected slot. + + var/slot_num + if(slot_start == 0) + slot_num = 1 + slot_start = 2 + else + slot_num = slot_start + 1 + + while(slot_start != slot_num) //If we wrap around without finding any free slots, just give up. + if(module_active(slot_num)) + select_module(slot_num) + return + slot_num++ + if(slot_num > 3) slot_num = 1 //Wrap around. + + return + +/mob/living/silicon/robot/proc/activate_module(var/obj/item/O) + if(!(locate(O) in src.module.modules) && !(locate(O) in src.module.emag)) + return + if(activated(O)) + to_chat(src, "Already activated") + return + if(!module_state_1) + module_state_1 = O + O.hud_layerise() + O.screen_loc = inv1.screen_loc + contents += O + if(istype(module_state_1,/obj/item/borg/sight)) + sight_mode |= module_state_1:sight_mode + else if(!module_state_2) + module_state_2 = O + O.hud_layerise() + O.screen_loc = inv2.screen_loc + contents += O + if(istype(module_state_2,/obj/item/borg/sight)) + sight_mode |= module_state_2:sight_mode + else if(!module_state_3) + module_state_3 = O + O.hud_layerise() + O.screen_loc = inv3.screen_loc + contents += O + if(istype(module_state_3,/obj/item/borg/sight)) + sight_mode |= module_state_3:sight_mode + else + to_chat(src, "You need to disable a module first!") + +/mob/living/silicon/robot/put_in_hands(var/obj/item/W) // No hands. + W.loc = get_turf(src) + return 1 + +/mob/living/silicon/robot/is_holding_item_of_type(typepath) + for(var/obj/item/I in list(module_state_1, module_state_2, module_state_3)) + if(istype(I, typepath)) + return I + return FALSE + +// Returns a list of all held items in a borg's 'hands'. +/mob/living/silicon/robot/get_all_held_items() + . = list() + if(module_state_1) + . += module_state_1 + if(module_state_2) + . += module_state_2 + if(module_state_3) . += module_state_3 \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm index 5dce088c913..59a7160d4c2 100644 --- a/code/modules/mob/living/silicon/robot/laws.dm +++ b/code/modules/mob/living/silicon/robot/laws.dm @@ -1,56 +1,56 @@ -/mob/living/silicon/robot/verb/cmd_show_laws() - set category = "Robot Commands" - set name = "Show Laws" - show_laws() - -/mob/living/silicon/robot/show_laws(var/everyone = 0) - laws_sanity_check() - var/who - - if (everyone) - who = world - else - who = src - if(lawupdate) - if (connected_ai) - if(connected_ai.stat || connected_ai.control_disabled) - to_chat(src, "AI signal lost, unable to sync laws.") - - else - lawsync() - photosync() - to_chat(src, "Laws synced with AI, be sure to note any changes.") - // TODO: Update to new antagonist system. - if(mind && mind.special_role == "traitor" && mind.original == src) - to_chat(src, "Remember, your AI does NOT share or know about your law 0.") - else - to_chat(src, "No AI selected to sync laws with, disabling lawsync protocol.") - lawupdate = FALSE - - to_chat(who, "Obey these laws:") - laws.show_laws(who) - if(shell) //AI shell - to_chat(who, "Remember, you are an AI remotely controlling your shell, other AIs can be ignored.") - // TODO: Update to new antagonist system. - else if(mind && (mind.special_role == "traitor" && mind.original == src) && connected_ai) - to_chat(who, "Remember, [connected_ai.name] is technically your master, but your objective comes first.") - else if(connected_ai) - to_chat(who, "Remember, [connected_ai.name] is your master, other AIs can be ignored.") - else if(emagged) - to_chat(who, "Remember, you are not required to listen to the AI.") - else - to_chat(who, "Remember, you are not bound to any AI, you are not required to listen to them.") - - -/mob/living/silicon/robot/lawsync() - laws_sanity_check() - var/datum/ai_laws/master = connected_ai && lawupdate ? connected_ai.laws : null - if (master) - master.sync(src) - ..() - return - -/mob/living/silicon/robot/proc/robot_checklaws() - set category = "Robot Commands" - set name = "State Laws" - subsystem_law_manager() +/mob/living/silicon/robot/verb/cmd_show_laws() + set category = "Robot Commands" + set name = "Show Laws" + show_laws() + +/mob/living/silicon/robot/show_laws(var/everyone = 0) + laws_sanity_check() + var/who + + if (everyone) + who = world + else + who = src + if(lawupdate) + if (connected_ai) + if(connected_ai.stat || connected_ai.control_disabled) + to_chat(src, "AI signal lost, unable to sync laws.") + + else + lawsync() + photosync() + to_chat(src, "Laws synced with AI, be sure to note any changes.") + // TODO: Update to new antagonist system. + if(mind && mind.special_role == "traitor" && mind.original == src) + to_chat(src, "Remember, your AI does NOT share or know about your law 0.") + else + to_chat(src, "No AI selected to sync laws with, disabling lawsync protocol.") + lawupdate = FALSE + + to_chat(who, "Obey these laws:") + laws.show_laws(who) + if(shell) //AI shell + to_chat(who, "Remember, you are an AI remotely controlling your shell, other AIs can be ignored.") + // TODO: Update to new antagonist system. + else if(mind && (mind.special_role == "traitor" && mind.original == src) && connected_ai) + to_chat(who, "Remember, [connected_ai.name] is technically your master, but your objective comes first.") + else if(connected_ai) + to_chat(who, "Remember, [connected_ai.name] is your master, other AIs can be ignored.") + else if(emagged) + to_chat(who, "Remember, you are not required to listen to the AI.") + else + to_chat(who, "Remember, you are not bound to any AI, you are not required to listen to them.") + + +/mob/living/silicon/robot/lawsync() + laws_sanity_check() + var/datum/ai_laws/master = connected_ai && lawupdate ? connected_ai.laws : null + if (master) + master.sync(src) + ..() + return + +/mob/living/silicon/robot/proc/robot_checklaws() + set category = "Robot Commands" + set name = "State Laws" + subsystem_law_manager() diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index 106ab34f7d4..276ab699a3e 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -1,377 +1,377 @@ -/mob/living/silicon/robot/Life() - set invisibility = 0 - set background = 1 - - if (src.transforming) - return - - src.blinded = null - - //Status updates, death etc. - clamp_values() - handle_regular_status_updates() - handle_actions() - handle_instability() - // For some reason borg Life() doesn't call ..() - handle_modifiers() - handle_light() - - if(client) - handle_regular_hud_updates() - handle_vision() - update_items() - if (src.stat != DEAD) //still using power - use_power() - process_killswitch() - process_locks() - process_queued_alarms() - update_canmove() - -/mob/living/silicon/robot/proc/clamp_values() - -// SetStunned(min(stunned, 30)) - SetParalysis(min(paralysis, 30)) -// SetWeakened(min(weakened, 20)) - SetSleeping(0) - adjustBruteLoss(0) - adjustToxLoss(0) - adjustOxyLoss(0) - adjustFireLoss(0) - -/mob/living/silicon/robot/proc/use_power() - // Debug only - // to_world("DEBUG: life.dm line 35: cyborg use_power() called at tick [controller_iteration]") - used_power_this_tick = 0 - for(var/V in components) - var/datum/robot_component/C = components[V] - C.update_power_state() - - if ( cell && is_component_functioning("power cell") && src.cell.charge > 0 ) - if(src.module_state_1) - cell_use_power(50) // 50W load for every enabled tool TODO: tool-specific loads - if(src.module_state_2) - cell_use_power(50) - if(src.module_state_3) - cell_use_power(50) - - if(lights_on) - cell_use_power(30) // 30W light. Normal lights would use ~15W, but increased for balance reasons. - - src.has_power = 1 - else - if (src.has_power) - to_chat(src, span_red("You are now running on emergency backup power.")) - src.has_power = 0 - if(lights_on) // Light is on but there is no power! - lights_on = 0 - set_light(0) - -/mob/living/silicon/robot/handle_regular_status_updates() - - if(src.camera && !scrambledcodes) - if(src.stat == 2 || wires.is_cut(WIRE_BORG_CAMERA)) - src.camera.set_status(0) - else - src.camera.set_status(1) - - updatehealth() - - if(src.sleeping) - Paralyse(3) - AdjustSleeping(-1) - - //if(src.resting) // VOREStation edit. Our borgos would rather not. - // Weaken(5) - - if(health < config.health_threshold_dead && src.stat != 2) //die only once - death() - - if (src.stat != 2) //Alive. - if (src.weakened > 0) // Do not fullstun on weaken - AdjustWeakened(-1) - if (src.paralysis || src.stunned || !src.has_power) //Stunned etc. - src.set_stat(UNCONSCIOUS) - if (src.stunned > 0) - AdjustStunned(-1) - if (src.weakened > 0) - AdjustWeakened(-1) - if (src.paralysis > 0) - AdjustParalysis(-1) - src.blinded = 1 - else - src.blinded = 0 - - else //Not stunned. - src.set_stat(CONSCIOUS) - - AdjustConfused(-1) - - else //Dead or just unconscious. - src.blinded = 1 - - if (src.stuttering) src.stuttering-- - - if (src.eye_blind) - src.AdjustBlinded(-1) - src.blinded = 1 - - if (src.ear_deaf > 0) src.ear_deaf-- - if (src.ear_damage < 25) - src.ear_damage -= 0.05 - src.ear_damage = max(src.ear_damage, 0) - - src.density = !( src.lying ) - - if (src.sdisabilities & BLIND) - src.blinded = 1 - if (src.sdisabilities & DEAF) - src.ear_deaf = 1 - - if (src.eye_blurry > 0) - src.eye_blurry-- - src.eye_blurry = max(0, src.eye_blurry) - - if (src.druggy > 0) - src.druggy-- - src.druggy = max(0, src.druggy) - - //update the state of modules and components here - if (src.stat != 0) - uneq_all() - - if(radio) - if(!is_component_functioning("radio")) - radio.on = 0 - else - radio.on = 1 - - if(is_component_functioning("camera")) - src.blinded = 0 - else - src.blinded = 1 - - return 1 - -/mob/living/silicon/robot/handle_regular_hud_updates() - var/fullbright = FALSE - var/seemeson = FALSE - - var/area/A = get_area(src) - if(A?.no_spoilers) - disable_spoiler_vision() - - if (src.stat == DEAD || (XRAY in mutations) || (src.sight_mode & BORGXRAY)) - src.sight |= SEE_TURFS - src.sight |= SEE_MOBS - src.sight |= SEE_OBJS - src.see_in_dark = 8 - src.see_invisible = SEE_INVISIBLE_MINIMUM - else if ((src.sight_mode & BORGMESON) && (src.sight_mode & BORGTHERM)) - src.sight |= SEE_TURFS - src.sight |= SEE_MOBS - src.see_in_dark = 8 - see_invisible = SEE_INVISIBLE_MINIMUM - fullbright = TRUE - else if (src.sight_mode & BORGMESON) - src.sight |= SEE_TURFS - src.see_in_dark = 8 - see_invisible = SEE_INVISIBLE_MINIMUM - fullbright = TRUE - seemeson = TRUE - else if (src.sight_mode & BORGMATERIAL) - src.sight |= SEE_OBJS - src.see_in_dark = 8 - see_invisible = SEE_INVISIBLE_MINIMUM - fullbright = TRUE - else if (src.sight_mode & BORGTHERM) - src.sight |= SEE_MOBS - src.see_in_dark = 8 - src.see_invisible = SEE_INVISIBLE_LEVEL_TWO - fullbright = TRUE - else if (!seedarkness) - src.sight &= ~SEE_MOBS - src.sight &= ~SEE_TURFS - src.sight &= ~SEE_OBJS - src.see_in_dark = 8 - src.see_invisible = SEE_INVISIBLE_NOLIGHTING - else if (src.stat != DEAD) - src.sight &= ~SEE_MOBS - src.sight &= ~SEE_TURFS - src.sight &= ~SEE_OBJS - src.see_in_dark = 8 // see_in_dark means you can FAINTLY see in the dark, humans have a range of 3 or so, tajaran have it at 8 - src.see_invisible = SEE_INVISIBLE_LIVING // This is normal vision (25), setting it lower for normal vision means you don't "see" things like darkness since darkness - // has a "invisible" value of 15 - - if(plane_holder) - plane_holder.set_vis(VIS_FULLBRIGHT,fullbright) - plane_holder.set_vis(VIS_MESONS,seemeson) - - ..() - - if (src.healths) - if (src.stat != 2) - if(istype(src,/mob/living/silicon/robot/drone)) - switch(health) - if(35 to INFINITY) - src.healths.icon_state = "health0" - if(25 to 34) - src.healths.icon_state = "health1" - if(15 to 24) - src.healths.icon_state = "health2" - if(5 to 14) - src.healths.icon_state = "health3" - if(0 to 4) - src.healths.icon_state = "health4" - if(-35 to 0) - src.healths.icon_state = "health5" - else - src.healths.icon_state = "health6" - else - switch(health) - if(200 to INFINITY) - src.healths.icon_state = "health0" - if(150 to 200) - src.healths.icon_state = "health1" - if(100 to 150) - src.healths.icon_state = "health2" - if(50 to 100) - src.healths.icon_state = "health3" - if(0 to 50) - src.healths.icon_state = "health4" - if(config.health_threshold_dead to 0) - src.healths.icon_state = "health5" - else - src.healths.icon_state = "health6" - else - src.healths.icon_state = "health7" - - if (src.syndicate && src.client) - for(var/datum/mind/tra in traitors.current_antagonists) - if(tra.current) - // TODO: Update to new antagonist system. - var/I = image('icons/mob/mob.dmi', loc = tra.current, icon_state = "traitor") - src.client.images += I - src.disconnect_from_ai() - if(src.mind) - // TODO: Update to new antagonist system. - if(!src.mind.special_role) - src.mind.special_role = "traitor" - traitors.current_antagonists |= src.mind - - update_cell() - - var/turf/T = get_turf(src) - var/datum/gas_mixture/environment = T.return_air() - if(environment) - switch(environment.temperature) //310.055 optimal body temp - if(400 to INFINITY) - throw_alert("temp", /obj/screen/alert/hot/robot, HOT_ALERT_SEVERITY_MODERATE) - if(360 to 400) - throw_alert("temp", /obj/screen/alert/hot/robot, HOT_ALERT_SEVERITY_LOW) - if(260 to 360) - clear_alert("temp") - if(200 to 260) - throw_alert("temp", /obj/screen/alert/cold/robot, COLD_ALERT_SEVERITY_LOW) - else - throw_alert("temp", /obj/screen/alert/cold/robot, COLD_ALERT_SEVERITY_MODERATE) - -//Oxygen and fire does nothing yet!! -// if (src.oxygen) src.oxygen.icon_state = "oxy[src.oxygen_alert ? 1 : 0]" -// if (src.fire) src.fire.icon_state = "fire[src.fire_alert ? 1 : 0]" - - if(stat != 2) - if(blinded) - overlay_fullscreen("blind", /obj/screen/fullscreen/blind) - else - clear_fullscreen("blind") - set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) - set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) - set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) - - if (src.machine) - if (src.machine.check_eye(src) < 0) - src.reset_view(null) - else - if(client && !client.adminobs) - reset_view(null) - - if(emagged) - throw_alert("hacked", /obj/screen/alert/hacked) - else - clear_alert("hacked") - - return 1 - -/mob/living/silicon/robot/proc/update_cell() - if(cell) - var/cellcharge = cell.charge/cell.maxcharge - switch(cellcharge) - if(0.75 to INFINITY) - clear_alert("charge") - if(0.5 to 0.75) - throw_alert("charge", /obj/screen/alert/lowcell, 1) - if(0.25 to 0.5) - throw_alert("charge", /obj/screen/alert/lowcell, 2) - if(0.01 to 0.25) - throw_alert("charge", /obj/screen/alert/lowcell, 3) - else - throw_alert("charge", /obj/screen/alert/emptycell) - else - throw_alert("charge", /obj/screen/alert/nocell) - - -/mob/living/silicon/robot/proc/update_items() - if(client) - client.screen -= contents - for(var/obj/I in contents) - if(I && !(istype(I,/obj/item/weapon/cell) || istype(I,/obj/item/device/radio) || istype(I,/obj/machinery/camera) || istype(I,/obj/item/device/mmi))) - client.screen += I - if(module_state_1) - module_state_1:screen_loc = ui_inv1 - if(module_state_2) - module_state_2:screen_loc = ui_inv2 - if(module_state_3) - module_state_3:screen_loc = ui_inv3 - update_icon() - -/mob/living/silicon/robot/proc/process_killswitch() - if(killswitch) - killswitch_time -- - if(killswitch_time <= 0) - if(src.client) - to_chat(src, "Killswitch Activated") - killswitch = 0 - spawn(5) - gib() - -/mob/living/silicon/robot/proc/process_locks() - if(weapon_lock) - uneq_all() - weaponlock_time -- - if(weaponlock_time <= 0) - if(src.client) - to_chat(src, "Weapon Lock Timed Out!") - weapon_lock = 0 - weaponlock_time = 120 - -/mob/living/silicon/robot/update_canmove() - ..() // Let's not reinvent the wheel. - if(lockdown || !is_component_functioning("actuator")) - canmove = FALSE - return canmove - -/mob/living/silicon/robot/update_fire() - cut_overlay(image(icon = 'icons/mob/OnFire.dmi', icon_state = get_fire_icon_state())) - if(on_fire) - add_overlay(image(icon = 'icons/mob/OnFire.dmi', icon_state = get_fire_icon_state())) - -/mob/living/silicon/robot/fire_act() - if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them - IgniteMob() - -/mob/living/silicon/robot/handle_light() - . = ..() - if(. == FALSE) // If no other light sources are on. - if(lights_on) - set_light(integrated_light_power, 1, "#FFFFFF") - return TRUE +/mob/living/silicon/robot/Life() + set invisibility = 0 + set background = 1 + + if (src.transforming) + return + + src.blinded = null + + //Status updates, death etc. + clamp_values() + handle_regular_status_updates() + handle_actions() + handle_instability() + // For some reason borg Life() doesn't call ..() + handle_modifiers() + handle_light() + + if(client) + handle_regular_hud_updates() + handle_vision() + update_items() + if (src.stat != DEAD) //still using power + use_power() + process_killswitch() + process_locks() + process_queued_alarms() + update_canmove() + +/mob/living/silicon/robot/proc/clamp_values() + +// SetStunned(min(stunned, 30)) + SetParalysis(min(paralysis, 30)) +// SetWeakened(min(weakened, 20)) + SetSleeping(0) + adjustBruteLoss(0) + adjustToxLoss(0) + adjustOxyLoss(0) + adjustFireLoss(0) + +/mob/living/silicon/robot/proc/use_power() + // Debug only + // to_world("DEBUG: life.dm line 35: cyborg use_power() called at tick [controller_iteration]") + used_power_this_tick = 0 + for(var/V in components) + var/datum/robot_component/C = components[V] + C.update_power_state() + + if ( cell && is_component_functioning("power cell") && src.cell.charge > 0 ) + if(src.module_state_1) + cell_use_power(50) // 50W load for every enabled tool TODO: tool-specific loads + if(src.module_state_2) + cell_use_power(50) + if(src.module_state_3) + cell_use_power(50) + + if(lights_on) + cell_use_power(30) // 30W light. Normal lights would use ~15W, but increased for balance reasons. + + src.has_power = 1 + else + if (src.has_power) + to_chat(src, span_red("You are now running on emergency backup power.")) + src.has_power = 0 + if(lights_on) // Light is on but there is no power! + lights_on = 0 + set_light(0) + +/mob/living/silicon/robot/handle_regular_status_updates() + + if(src.camera && !scrambledcodes) + if(src.stat == 2 || wires.is_cut(WIRE_BORG_CAMERA)) + src.camera.set_status(0) + else + src.camera.set_status(1) + + updatehealth() + + if(src.sleeping) + Paralyse(3) + AdjustSleeping(-1) + + //if(src.resting) // VOREStation edit. Our borgos would rather not. + // Weaken(5) + + if(health < config.health_threshold_dead && src.stat != 2) //die only once + death() + + if (src.stat != 2) //Alive. + if (src.weakened > 0) // Do not fullstun on weaken + AdjustWeakened(-1) + if (src.paralysis || src.stunned || !src.has_power) //Stunned etc. + src.set_stat(UNCONSCIOUS) + if (src.stunned > 0) + AdjustStunned(-1) + if (src.weakened > 0) + AdjustWeakened(-1) + if (src.paralysis > 0) + AdjustParalysis(-1) + src.blinded = 1 + else + src.blinded = 0 + + else //Not stunned. + src.set_stat(CONSCIOUS) + + AdjustConfused(-1) + + else //Dead or just unconscious. + src.blinded = 1 + + if (src.stuttering) src.stuttering-- + + if (src.eye_blind) + src.AdjustBlinded(-1) + src.blinded = 1 + + if (src.ear_deaf > 0) src.ear_deaf-- + if (src.ear_damage < 25) + src.ear_damage -= 0.05 + src.ear_damage = max(src.ear_damage, 0) + + src.density = !( src.lying ) + + if (src.sdisabilities & BLIND) + src.blinded = 1 + if (src.sdisabilities & DEAF) + src.ear_deaf = 1 + + if (src.eye_blurry > 0) + src.eye_blurry-- + src.eye_blurry = max(0, src.eye_blurry) + + if (src.druggy > 0) + src.druggy-- + src.druggy = max(0, src.druggy) + + //update the state of modules and components here + if (src.stat != 0) + uneq_all() + + if(radio) + if(!is_component_functioning("radio")) + radio.on = 0 + else + radio.on = 1 + + if(is_component_functioning("camera")) + src.blinded = 0 + else + src.blinded = 1 + + return 1 + +/mob/living/silicon/robot/handle_regular_hud_updates() + var/fullbright = FALSE + var/seemeson = FALSE + + var/area/A = get_area(src) + if(A?.no_spoilers) + disable_spoiler_vision() + + if (src.stat == DEAD || (XRAY in mutations) || (src.sight_mode & BORGXRAY)) + src.sight |= SEE_TURFS + src.sight |= SEE_MOBS + src.sight |= SEE_OBJS + src.see_in_dark = 8 + src.see_invisible = SEE_INVISIBLE_MINIMUM + else if ((src.sight_mode & BORGMESON) && (src.sight_mode & BORGTHERM)) + src.sight |= SEE_TURFS + src.sight |= SEE_MOBS + src.see_in_dark = 8 + see_invisible = SEE_INVISIBLE_MINIMUM + fullbright = TRUE + else if (src.sight_mode & BORGMESON) + src.sight |= SEE_TURFS + src.see_in_dark = 8 + see_invisible = SEE_INVISIBLE_MINIMUM + fullbright = TRUE + seemeson = TRUE + else if (src.sight_mode & BORGMATERIAL) + src.sight |= SEE_OBJS + src.see_in_dark = 8 + see_invisible = SEE_INVISIBLE_MINIMUM + fullbright = TRUE + else if (src.sight_mode & BORGTHERM) + src.sight |= SEE_MOBS + src.see_in_dark = 8 + src.see_invisible = SEE_INVISIBLE_LEVEL_TWO + fullbright = TRUE + else if (!seedarkness) + src.sight &= ~SEE_MOBS + src.sight &= ~SEE_TURFS + src.sight &= ~SEE_OBJS + src.see_in_dark = 8 + src.see_invisible = SEE_INVISIBLE_NOLIGHTING + else if (src.stat != DEAD) + src.sight &= ~SEE_MOBS + src.sight &= ~SEE_TURFS + src.sight &= ~SEE_OBJS + src.see_in_dark = 8 // see_in_dark means you can FAINTLY see in the dark, humans have a range of 3 or so, tajaran have it at 8 + src.see_invisible = SEE_INVISIBLE_LIVING // This is normal vision (25), setting it lower for normal vision means you don't "see" things like darkness since darkness + // has a "invisible" value of 15 + + if(plane_holder) + plane_holder.set_vis(VIS_FULLBRIGHT,fullbright) + plane_holder.set_vis(VIS_MESONS,seemeson) + + ..() + + if (src.healths) + if (src.stat != 2) + if(istype(src,/mob/living/silicon/robot/drone)) + switch(health) + if(35 to INFINITY) + src.healths.icon_state = "health0" + if(25 to 34) + src.healths.icon_state = "health1" + if(15 to 24) + src.healths.icon_state = "health2" + if(5 to 14) + src.healths.icon_state = "health3" + if(0 to 4) + src.healths.icon_state = "health4" + if(-35 to 0) + src.healths.icon_state = "health5" + else + src.healths.icon_state = "health6" + else + switch(health) + if(200 to INFINITY) + src.healths.icon_state = "health0" + if(150 to 200) + src.healths.icon_state = "health1" + if(100 to 150) + src.healths.icon_state = "health2" + if(50 to 100) + src.healths.icon_state = "health3" + if(0 to 50) + src.healths.icon_state = "health4" + if(config.health_threshold_dead to 0) + src.healths.icon_state = "health5" + else + src.healths.icon_state = "health6" + else + src.healths.icon_state = "health7" + + if (src.syndicate && src.client) + for(var/datum/mind/tra in traitors.current_antagonists) + if(tra.current) + // TODO: Update to new antagonist system. + var/I = image('icons/mob/mob.dmi', loc = tra.current, icon_state = "traitor") + src.client.images += I + src.disconnect_from_ai() + if(src.mind) + // TODO: Update to new antagonist system. + if(!src.mind.special_role) + src.mind.special_role = "traitor" + traitors.current_antagonists |= src.mind + + update_cell() + + var/turf/T = get_turf(src) + var/datum/gas_mixture/environment = T.return_air() + if(environment) + switch(environment.temperature) //310.055 optimal body temp + if(400 to INFINITY) + throw_alert("temp", /obj/screen/alert/hot/robot, HOT_ALERT_SEVERITY_MODERATE) + if(360 to 400) + throw_alert("temp", /obj/screen/alert/hot/robot, HOT_ALERT_SEVERITY_LOW) + if(260 to 360) + clear_alert("temp") + if(200 to 260) + throw_alert("temp", /obj/screen/alert/cold/robot, COLD_ALERT_SEVERITY_LOW) + else + throw_alert("temp", /obj/screen/alert/cold/robot, COLD_ALERT_SEVERITY_MODERATE) + +//Oxygen and fire does nothing yet!! +// if (src.oxygen) src.oxygen.icon_state = "oxy[src.oxygen_alert ? 1 : 0]" +// if (src.fire) src.fire.icon_state = "fire[src.fire_alert ? 1 : 0]" + + if(stat != 2) + if(blinded) + overlay_fullscreen("blind", /obj/screen/fullscreen/blind) + else + clear_fullscreen("blind") + set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) + set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) + set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) + + if (src.machine) + if (src.machine.check_eye(src) < 0) + src.reset_view(null) + else + if(client && !client.adminobs) + reset_view(null) + + if(emagged) + throw_alert("hacked", /obj/screen/alert/hacked) + else + clear_alert("hacked") + + return 1 + +/mob/living/silicon/robot/proc/update_cell() + if(cell) + var/cellcharge = cell.charge/cell.maxcharge + switch(cellcharge) + if(0.75 to INFINITY) + clear_alert("charge") + if(0.5 to 0.75) + throw_alert("charge", /obj/screen/alert/lowcell, 1) + if(0.25 to 0.5) + throw_alert("charge", /obj/screen/alert/lowcell, 2) + if(0.01 to 0.25) + throw_alert("charge", /obj/screen/alert/lowcell, 3) + else + throw_alert("charge", /obj/screen/alert/emptycell) + else + throw_alert("charge", /obj/screen/alert/nocell) + + +/mob/living/silicon/robot/proc/update_items() + if(client) + client.screen -= contents + for(var/obj/I in contents) + if(I && !(istype(I,/obj/item/weapon/cell) || istype(I,/obj/item/device/radio) || istype(I,/obj/machinery/camera) || istype(I,/obj/item/device/mmi))) + client.screen += I + if(module_state_1) + module_state_1:screen_loc = ui_inv1 + if(module_state_2) + module_state_2:screen_loc = ui_inv2 + if(module_state_3) + module_state_3:screen_loc = ui_inv3 + update_icon() + +/mob/living/silicon/robot/proc/process_killswitch() + if(killswitch) + killswitch_time -- + if(killswitch_time <= 0) + if(src.client) + to_chat(src, "Killswitch Activated") + killswitch = 0 + spawn(5) + gib() + +/mob/living/silicon/robot/proc/process_locks() + if(weapon_lock) + uneq_all() + weaponlock_time -- + if(weaponlock_time <= 0) + if(src.client) + to_chat(src, "Weapon Lock Timed Out!") + weapon_lock = 0 + weaponlock_time = 120 + +/mob/living/silicon/robot/update_canmove() + ..() // Let's not reinvent the wheel. + if(lockdown || !is_component_functioning("actuator")) + canmove = FALSE + return canmove + +/mob/living/silicon/robot/update_fire() + cut_overlay(image(icon = 'icons/mob/OnFire.dmi', icon_state = get_fire_icon_state())) + if(on_fire) + add_overlay(image(icon = 'icons/mob/OnFire.dmi', icon_state = get_fire_icon_state())) + +/mob/living/silicon/robot/fire_act() + if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them + IgniteMob() + +/mob/living/silicon/robot/handle_light() + . = ..() + if(. == FALSE) // If no other light sources are on. + if(lights_on) + set_light(integrated_light_power, 1, "#FFFFFF") + return TRUE diff --git a/code/modules/mob/living/silicon/robot/login.dm b/code/modules/mob/living/silicon/robot/login.dm index 3d11703e9f3..d01043373f3 100644 --- a/code/modules/mob/living/silicon/robot/login.dm +++ b/code/modules/mob/living/silicon/robot/login.dm @@ -1,19 +1,19 @@ -/mob/living/silicon/robot/Login() - ..() - regenerate_icons() - update_hud() - - show_laws(0) - - // Override the DreamSeeker macro with the borg version! - client.set_hotkeys_macro("borgmacro", "borghotkeymode") - - // Forces synths to select an icon relevant to their module - if(!icon_selected) - icon_selection_tries = SSrobot_sprites.get_module_sprites_len(modtype, src) + 1 - choose_icon(icon_selection_tries) - - if(sprite_datum && module) - sprite_datum.do_equipment_glamour(module) - +/mob/living/silicon/robot/Login() + ..() + regenerate_icons() + update_hud() + + show_laws(0) + + // Override the DreamSeeker macro with the borg version! + client.set_hotkeys_macro("borgmacro", "borghotkeymode") + + // Forces synths to select an icon relevant to their module + if(!icon_selected) + icon_selection_tries = SSrobot_sprites.get_module_sprites_len(modtype, src) + 1 + choose_icon(icon_selection_tries) + + if(sprite_datum && module) + sprite_datum.do_equipment_glamour(module) + plane_holder.set_vis(VIS_AUGMENTED, TRUE) \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/photos.dm b/code/modules/mob/living/silicon/robot/photos.dm index 24111683c93..775bc68b6e5 100644 --- a/code/modules/mob/living/silicon/robot/photos.dm +++ b/code/modules/mob/living/silicon/robot/photos.dm @@ -1,20 +1,20 @@ -/mob/living/silicon/robot/proc/photosync() - var/obj/item/device/camera/siliconcam/master_cam = connected_ai ? connected_ai.aiCamera : null - if (!master_cam) - return - - var/synced = 0 - // Sync borg images to the master AI. - // We don't care about syncing the other way around - for(var/obj/item/weapon/photo/borg_photo in aiCamera.aipictures) - var/copied = 0 - for(var/obj/item/weapon/photo/ai_photo in master_cam.aipictures) - if(borg_photo.id == ai_photo.id) - copied = 1 - break - if(!copied) - master_cam.injectaialbum(borg_photo.copy(1), " (synced from [name])") - synced = 1 - - if(synced) - to_chat(src, "Images synced with AI. Local images will be retained in the case of loss of connection with the AI.") +/mob/living/silicon/robot/proc/photosync() + var/obj/item/device/camera/siliconcam/master_cam = connected_ai ? connected_ai.aiCamera : null + if (!master_cam) + return + + var/synced = 0 + // Sync borg images to the master AI. + // We don't care about syncing the other way around + for(var/obj/item/weapon/photo/borg_photo in aiCamera.aipictures) + var/copied = 0 + for(var/obj/item/weapon/photo/ai_photo in master_cam.aipictures) + if(borg_photo.id == ai_photo.id) + copied = 1 + break + if(!copied) + master_cam.injectaialbum(borg_photo.copy(1), " (synced from [name])") + synced = 1 + + if(synced) + to_chat(src, "Images synced with AI. Local images will be retained in the case of loss of connection with the AI.") diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index ffcea4e93cb..83b25ea0370 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1,1520 +1,1520 @@ -#define CYBORG_POWER_USAGE_MULTIPLIER 2 // Multiplier for amount of power cyborgs use. - -/mob/living/silicon/robot - name = "Cyborg" - real_name = "Cyborg" - icon = 'icons/mob/robots.dmi' - icon_state = "robot" - maxHealth = 200 - health = 200 - - mob_bump_flag = ROBOT - mob_swap_flags = ~HEAVY - mob_push_flags = ~HEAVY //trundle trundle - - blocks_emissive = EMISSIVE_BLOCK_UNIQUE - - var/lights_on = 0 // Is our integrated light on? - var/used_power_this_tick = 0 - var/sight_mode = 0 - var/custom_name = "" - var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best - var/sprite_name = null // The name of the borg, for the purposes of custom icon sprite indexing. - var/crisis //Admin-settable for combat module use. - var/crisis_override = 0 - var/integrated_light_power = 6 - var/datum/wires/robot/wires - - can_be_antagged = TRUE - -//Icon stuff - - var/datum/robot_sprite/sprite_datum // Sprite datum, holding all our sprite data - var/icon_selected = 1 // If icon selection has been completed yet - var/icon_selection_tries = 0 // Remaining attempts to select icon before a selection is forced - var/list/sprite_extra_customization = list() - var/rest_style = "Default" - var/notransform - does_spin = FALSE - -//Hud stuff - - var/obj/screen/inv1 = null - var/obj/screen/inv2 = null - var/obj/screen/inv3 = null - - var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not - var/obj/screen/robot_modules_background - -//3 Modules can be activated at any one time. - var/obj/item/weapon/robot_module/module = null - var/module_active = null - var/module_state_1 = null - var/module_state_2 = null - var/module_state_3 = null - - var/obj/item/device/radio/borg/radio = null - var/obj/item/device/communicator/integrated/communicator = null - var/mob/living/silicon/ai/connected_ai = null - var/obj/item/weapon/cell/cell = null - var/obj/machinery/camera/camera = null - - var/cell_emp_mult = 2 - - var/sleeper_state = 0 // 0 for empty, 1 for normal, 2 for mediborg-healthy - var/scrubbing = FALSE //Floor cleaning enabled - - // Components are basically robot organs. - var/list/components = list() - - var/obj/item/device/mmi/mmi = null - - var/obj/item/device/pda/ai/rbPDA = null - - var/opened = 0 - var/emagged = 0 - var/emag_items = 0 - var/wiresexposed = 0 - var/locked = 1 - var/has_power = 1 - var/list/req_access = list(access_robotics) - var/ident = 0 - //var/list/laws = list() - var/viewalerts = 0 - var/modtype = "Default" - var/sprite_type = null - var/lower_mod = 0 - var/jetpack = 0 - var/datum/effect/effect/system/ion_trail_follow/ion_trail = null - var/datum/effect/effect/system/spark_spread/spark_system//So they can initialize sparks whenever/N - var/jeton = 0 - var/killswitch = 0 - var/killswitch_time = 60 - var/weapon_lock = 0 - var/weaponlock_time = 120 - var/lawupdate = 1 //Cyborgs will sync their laws with their AI by default - var/lockcharge //Used when looking to see if a borg is locked down. - var/lockdown = 0 //Controls whether or not the borg is actually locked down. - var/speed = 0 //Cause sec borgs gotta go fast //No they dont! - var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them. - var/tracking_entities = 0 //The number of known entities currently accessing the internal camera - var/braintype = "Cyborg" - - var/obj/item/weapon/implant/restrainingbolt/bolt // The restraining bolt installed into the cyborg. - - var/list/robot_verbs_default = list( - /mob/living/silicon/robot/proc/sensor_mode, - /mob/living/silicon/robot/proc/robot_checklaws, - /mob/living/silicon/robot/proc/robot_mount, - /mob/living/proc/toggle_rider_reins, - /mob/living/proc/vertical_nom, - /mob/living/proc/shred_limb, - /mob/living/proc/dominate_prey, - /mob/living/proc/lend_prey_control - ) - -/mob/living/silicon/robot/New(loc, var/unfinished = 0) - spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - add_language("Robot Talk", 1) - add_language(LANGUAGE_GALCOM, 1) - add_language(LANGUAGE_EAL, 1) - - wires = new(src) - - robot_modules_background = new() - robot_modules_background.icon_state = "block" - ident = rand(1, 999) - updatename(modtype) - - radio = new /obj/item/device/radio/borg(src) -// communicator = new /obj/item/device/communicator/integrated(src) -// communicator.register_device(src) - common_radio = radio - - if(!scrambledcodes && !camera) - camera = new /obj/machinery/camera(src) - camera.c_tag = real_name - camera.replace_networks(list(NETWORK_DEFAULT,NETWORK_ROBOTS)) - if(wires.is_cut(WIRE_BORG_CAMERA)) - camera.status = 0 - - init() - initialize_components() - //if(!unfinished) - // Create all the robot parts. - for(var/V in components) if(V != "power cell") - var/datum/robot_component/C = components[V] - C.installed = 1 - C.wrapped = new C.external_type - - if(!cell) - cell = new /obj/item/weapon/cell/robot_station(src) - else if(ispath(cell)) - cell = new cell(src) - - ..() - - if(cell) - var/datum/robot_component/cell_component = components["power cell"] - cell_component.wrapped = cell - cell_component.installed = 1 - - add_robot_verbs() - - hud_list[HEALTH_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_HEALTH) - hud_list[STATUS_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudhealth100", plane = PLANE_CH_STATUS) - hud_list[LIFE_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudhealth100", plane = PLANE_CH_LIFE) - hud_list[ID_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_ID) - hud_list[WANTED_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_WANTED) - hud_list[IMPLOYAL_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_IMPLOYAL) - hud_list[IMPCHEM_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_IMPCHEM) - hud_list[IMPTRACK_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_IMPTRACK) - hud_list[SPECIALROLE_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_SPECIAL) - -/mob/living/silicon/robot/LateInitialize() - . = ..() - update_icon() - -/mob/living/silicon/robot/rejuvenate() - for (var/V in components) - var/datum/robot_component/C = components[V] - if(istype(C.wrapped, /obj/item/broken_device)) - qdel(C.wrapped) - C.wrapped = null - if(!C.wrapped) - switch(V) - if("actuator") - C.wrapped = new /obj/item/robot_parts/robot_component/actuator(src) - if("radio") - C.wrapped = new /obj/item/robot_parts/robot_component/radio(src) - if("power cell") - var/list/recommended_cells = list(/obj/item/weapon/cell/robot_station, /obj/item/weapon/cell/high, /obj/item/weapon/cell/super, /obj/item/weapon/cell/robot_syndi, /obj/item/weapon/cell/hyper, - /obj/item/weapon/cell/infinite, /obj/item/weapon/cell/potato, /obj/item/weapon/cell/slime) - var/list/cell_names = list() - for(var/cell_type in recommended_cells) - var/obj/item/weapon/cell/single_cell = cell_type - cell_names[capitalize(initial(single_cell.name))] = cell_type - var/selected_cell = tgui_input_list(usr, "What kind of cell do you want to install? Cancel installs a default robot cell.", "Cells", cell_names) - if(!selected_cell || selected_cell == "Cancel") - selected_cell = "A standard robot power cell" - var/new_power_cell = cell_names[capitalize(selected_cell)] - cell = new new_power_cell(src) - C.wrapped = cell - if("diagnosis unit") - C.wrapped = new /obj/item/robot_parts/robot_component/diagnosis_unit(src) - if("camera") - C.wrapped = new /obj/item/robot_parts/robot_component/camera(src) - if("comms") - C.wrapped = new /obj/item/robot_parts/robot_component/binary_communication_device(src) - if("armour") - C.wrapped = new /obj/item/robot_parts/robot_component/armour(src) - C.installed = 1 - C.install() - cell.charge = cell.maxcharge - ..() - -/mob/living/silicon/robot/proc/init() - aiCamera = new/obj/item/device/camera/siliconcam/robot_camera(src) - laws = new /datum/ai_laws/nanotrasen() - additional_law_channels["Binary"] = "#b" - var/new_ai = select_active_ai_with_fewest_borgs() - if(new_ai) - lawupdate = 1 - connect_to_ai(new_ai) - else - lawupdate = 0 - - - -/mob/living/silicon/robot/SetName(pickedName as text) - custom_name = pickedName - updatename() - -/mob/living/silicon/robot/proc/sync() - if(lawupdate && connected_ai) - lawsync() - photosync() - -/mob/living/silicon/robot/drain_power(var/drain_check, var/surge, var/amount = 0) - - if(drain_check) - return 1 - - if(!cell || !cell.charge) - return 0 - - // Actual amount to drain from cell, using CELLRATE - var/cell_amount = amount * CELLRATE - - if(cell.charge > cell_amount) - // Spam Protection - if(prob(10)) - to_chat(src, "Warning: Unauthorized access through power channel [rand(11,29)] detected!") - cell.use(cell_amount) - return amount - return 0 - -// setup the PDA and its name -/mob/living/silicon/robot/proc/setup_PDA() - if (!rbPDA) - rbPDA = new/obj/item/device/pda/ai(src) - rbPDA.set_name_and_job(name,"[modtype] [braintype]") - -/mob/living/silicon/robot/proc/setup_communicator() - if (!communicator) - communicator = new/obj/item/device/communicator/integrated(src) - communicator.register_device(name, "[modtype] [braintype]") - -//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO -//Improved /N -/mob/living/silicon/robot/Destroy() - if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. - var/turf/T = get_turf(loc)//To hopefully prevent run time errors. - if(T) mmi.loc = T - if(mmi.brainmob) - var/obj/item/weapon/robot_module/M = locate() in contents - if(M) - mmi.brainmob.languages = M.original_languages - else - mmi.brainmob.languages = languages - mmi.brainmob.remove_language("Robot Talk") - mind.transfer_to(mmi.brainmob) - else if(!shell) // Shells don't have brainmbos in their MMIs. - to_chat(src, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.") - ghostize() - //ERROR("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].") - mmi = null - if(connected_ai) - connected_ai.connected_robots -= src - if(shell) - if(deployed) - undeploy() - revert_shell() // To get it out of the GLOB list. - qdel(wires) - wires = null - return ..() - -// CONTINUE CODING HERE -/* -/mob/living/silicon/robot/proc/set_module_sprites(var/list/new_sprites) - if(new_sprites && new_sprites.len) - module_sprites = new_sprites.Copy() - //Custom_sprite check and entry - if (custom_sprite == 1) - module_sprites["Custom"] = "[ckey]-[sprite_name]-[modtype]" //Made compliant with custom_sprites.dm line 32. (src.) was apparently redundant as it's implied. ~Mech - icontype = "Custom" - else - icontype = module_sprites[1] - icon_state = module_sprites[icontype] - update_icon() - return module_sprites -*/ -/mob/living/silicon/robot/proc/pick_module() - if(module) - return - var/list/modules = list() - //VOREStatation Edit Start: shell restrictions - if(shell) - modules.Add(shell_module_types) - else - modules.Add(robot_module_types) - if(crisis || security_level == SEC_LEVEL_RED || crisis_override) - to_chat(src, span_red("Crisis mode active. Combat module available.")) - modules |= emergency_module_types - for(var/module_name in whitelisted_module_types) - if(is_borg_whitelisted(src, module_name)) - modules |= module_name - //VOREStatation Edit End: shell restrictions - modtype = tgui_input_list(usr, "Please, select a module!", "Robot module", modules) - - if(module) - return - if(!(modtype in robot_modules)) - return - if(!is_borg_whitelisted(src, modtype)) - return - - var/module_type = robot_modules[modtype] - transform_with_anim() //VOREStation edit: sprite animation - new module_type(src) - - hands.icon_state = get_hud_module_icon() - feedback_inc("cyborg_[lowertext(modtype)]",1) - updatename() - hud_used.update_robot_modules_display() - notify_ai(ROBOT_NOTIFICATION_NEW_MODULE, module.name) - -/mob/living/silicon/robot/proc/update_braintype() - if(istype(mmi, /obj/item/device/mmi/digital/posibrain)) - braintype = BORG_BRAINTYPE_POSI - else if(istype(mmi, /obj/item/device/mmi/digital/robot)) - braintype = BORG_BRAINTYPE_DRONE - else if(istype(mmi, /obj/item/device/mmi/inert/ai_remote)) - braintype = BORG_BRAINTYPE_AI_SHELL - else - braintype = BORG_BRAINTYPE_CYBORG - -/mob/living/silicon/robot/proc/updatename(var/prefix as text) - if(prefix) - modtype = prefix - - update_braintype() - - var/changed_name = "" - if(custom_name) - changed_name = custom_name - notify_ai(ROBOT_NOTIFICATION_NEW_NAME, real_name, changed_name) - else - changed_name = "[modtype] [braintype]-[num2text(ident)]" - - real_name = changed_name - name = real_name - - // if we've changed our name, we also need to update the display name for our PDA - setup_PDA() - - // as well as our communicator registration - setup_communicator() - - //We also need to update name of internal camera. - if (camera) - camera.c_tag = changed_name - - //Flavour text. - if(client) - var/module_flavour = client.prefs.flavour_texts_robot[modtype] - if(module_flavour) - flavor_text = module_flavour - else - flavor_text = client.prefs.flavour_texts_robot["Default"] - // Vorestation Edit: and meta info - var/meta_info = client.prefs.metadata - if (meta_info) - ooc_notes = meta_info - ooc_notes_likes = client.prefs.metadata_likes - ooc_notes_dislikes = client.prefs.metadata_dislikes - custom_link = client.prefs.custom_link - -/mob/living/silicon/robot/verb/namepick() - set name = "Pick Name" - set category = "Robot Commands" - - if(custom_name) - to_chat(usr, "You can't pick another custom name. Go ask for a name change.") - return 0 - - spawn(0) - var/newname - newname = sanitizeSafe(tgui_input_text(src,"You are a robot. Enter a name, or leave blank for the default name.", "Name change","", MAX_NAME_LEN), MAX_NAME_LEN) - if (newname) - custom_name = newname - sprite_name = newname - - updatename() - update_icon() - -/mob/living/silicon/robot/verb/extra_customization() - set name = "Customize Appearance" - set category = "Robot Commands" - set desc = "Customize your appearance (assuming your chosen sprite allows)." - - if(!sprite_datum || !sprite_datum.has_extra_customization) - to_chat(src, "Your sprite cannot be customized.") - return - - sprite_datum.handle_extra_customization(src) - -/mob/living/silicon/robot/proc/self_diagnosis() - if(!is_component_functioning("diagnosis unit")) - return null - - var/dat = "[src.name] Self-Diagnosis Report\n" - for (var/V in components) - var/datum/robot_component/C = components[V] - dat += "[C.name]
                    Brute Damage:[C.brute_damage]
                    Electronics Damage:[C.electronics_damage]
                    Powered:[(!C.idle_usage || C.is_powered()) ? "Yes" : "No"]
                    Toggled:[ C.toggled ? "Yes" : "No"]

                    " - - return dat - -/mob/living/silicon/robot/verb/toggle_lights() - set category = "Robot Commands" - set name = "Toggle Lights" - - lights_on = !lights_on - to_chat(usr, "You [lights_on ? "enable" : "disable"] your integrated light.") - handle_light() - update_icon() - -/mob/living/silicon/robot/verb/self_diagnosis_verb() - set category = "Robot Commands" - set name = "Self Diagnosis" - - if(!is_component_functioning("diagnosis unit")) - to_chat(src, span_red("Your self-diagnosis component isn't functioning.")) - - var/datum/robot_component/CO = get_component("diagnosis unit") - if (!cell_use_power(CO.active_usage)) - to_chat(src, span_red("Low Power.")) - var/dat = self_diagnosis() - src << browse(dat, "window=robotdiagnosis") - - -/mob/living/silicon/robot/verb/toggle_component() - set category = "Robot Commands" - set name = "Toggle Component" - set desc = "Toggle a component, conserving power." - - var/list/installed_components = list() - for(var/V in components) - if(V == "power cell") continue - var/datum/robot_component/C = components[V] - if(C.installed) - installed_components += V - - var/toggle = tgui_input_list(src, "Which component do you want to toggle?", "Toggle Component", installed_components) - if(!toggle) - return - - var/datum/robot_component/C = components[toggle] - if(C.toggled) - C.toggled = 0 - to_chat(src, span_red("You disable [C.name].")) - else - C.toggled = 1 - to_chat(src, span_red("You enable [C.name].")) - -/mob/living/silicon/robot/verb/spark_plug() //So you can still sparkle on demand without violence. - set category = "Robot Commands" - set name = "Emit Sparks" - to_chat(src, "You harmlessly spark.") - spark_system.start() - -// this function displays jetpack pressure in the stat panel -/mob/living/silicon/robot/proc/show_jetpack_pressure() - // if you have a jetpack, show the internal tank pressure - var/obj/item/weapon/tank/jetpack/current_jetpack = installed_jetpack() - if (current_jetpack) - stat("Internal Atmosphere Info", current_jetpack.name) - stat("Tank Pressure", current_jetpack.air_contents.return_pressure()) - - -// this function returns the robots jetpack, if one is installed -/mob/living/silicon/robot/proc/installed_jetpack() - if(module) - return (locate(/obj/item/weapon/tank/jetpack) in module.modules) - return 0 - - -// this function displays the cyborgs current cell charge in the stat panel -/mob/living/silicon/robot/proc/show_cell_power() - if(cell) - stat(null, text("Charge Left: [round(cell.percent())]%")) - stat(null, text("Cell Rating: [round(cell.maxcharge)]")) // Round just in case we somehow get crazy values - stat(null, text("Power Cell Load: [round(used_power_this_tick)]W")) - else - stat(null, text("No Cell Inserted!")) - -// function to toggle VTEC once installed -/mob/living/silicon/robot/proc/toggle_vtec() - set name = "Toggle VTEC" - set category = "Abilities" - if(speed == -1) - to_chat(src, "VTEC module disabled.") - speed = 0 - else - to_chat(src, "VTEC module enabled.") - speed = -1 - -// update the status screen display -/mob/living/silicon/robot/Stat() - ..() - if (statpanel("Status")) - show_cell_power() - show_jetpack_pressure() - stat(null, text("Lights: [lights_on ? "ON" : "OFF"]")) - if(module) - for(var/datum/matter_synth/ms in module.synths) - stat("[ms.name]: [ms.energy]/[ms.max_energy]") - -/mob/living/silicon/robot/restrained() - return 0 - -/mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj) - ..(Proj) - if(prob(75) && Proj.damage > 0) spark_system.start() - return 2 - -/mob/living/silicon/robot/attackby(obj/item/weapon/W as obj, mob/user as mob) - if (istype(W, /obj/item/weapon/handcuffs)) // fuck i don't even know why isrobot() in handcuff code isn't working so this will have to do - return - - if(opened) // Are they trying to insert something? - for(var/V in components) - var/datum/robot_component/C = components[V] - if(!C.installed && istype(W, C.external_type)) - C.installed = 1 - C.wrapped = W - C.install() - user.drop_item() - W.loc = null - - var/obj/item/robot_parts/robot_component/WC = W - if(istype(WC)) - C.brute_damage = WC.brute - C.electronics_damage = WC.burn - - to_chat(usr, "You install the [W.name].") - - return - - if(istype(W, /obj/item/weapon/implant/restrainingbolt) && !cell) - if(bolt) - to_chat(user, "There is already a restraining bolt installed in this cyborg.") - return - - else - user.drop_from_inventory(W) - W.forceMove(src) - bolt = W - - to_chat(user, "You install \the [W].") - - return - - if(istype(W, /obj/item/weapon/aiModule)) // Trying to modify laws locally. - if(!opened) - to_chat(user, "You need to open \the [src]'s panel before you can modify them.") - return - - if(shell) // AI shells always have the laws of the AI - to_chat(user, "\The [src] is controlled remotely! You cannot upload new laws this way!") - return - - var/obj/item/weapon/aiModule/M = W - M.install(src, user) - return - - if(W.has_tool_quality(TOOL_WELDER) && user.a_intent != I_HURT) - if(src == user) - to_chat(user, "You lack the reach to be able to repair yourself.") - return - - if(!getBruteLoss()) - to_chat(user, "Nothing to fix here!") - return - var/obj/item/weapon/weldingtool/WT = W.get_welder() - if(WT.remove_fuel(0)) - user.setClickCooldown(user.get_attack_speed(WT)) - adjustBruteLoss(-30) - updatehealth() - add_fingerprint(user) - for(var/mob/O in viewers(user, null)) - O.show_message("[span_red("[user] has fixed some of the dents on [src]!")]", 1) - else - to_chat(user, "Need more welding fuel!") - return - - else if(istype(W, /obj/item/stack/cable_coil) && (wiresexposed || istype(src,/mob/living/silicon/robot/drone))) - if(!getFireLoss()) - to_chat(user, "Nothing to fix here!") - return - var/obj/item/stack/cable_coil/coil = W - if (coil.use(1)) - user.setClickCooldown(user.get_attack_speed(W)) - adjustFireLoss(-30) - updatehealth() - for(var/mob/O in viewers(user, null)) - O.show_message("[span_red("[user] has fixed some of the burnt wires on [src]!")]", 1) - - else if(W.has_tool_quality(TOOL_CROWBAR) && user.a_intent != I_HURT) // crowbar means open or close the cover - if(opened) - if(cell) - to_chat(user, "You close the cover.") - opened = 0 - update_icon() - else if(wiresexposed && wires.is_all_cut()) - //Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob. - if(!mmi) - to_chat(user, "\The [src] has no brain to remove.") - return - - to_chat(user, "You jam the crowbar into the robot and begin levering [mmi].") - sleep(30) - to_chat(user, "You damage some parts of the chassis, but eventually manage to rip out [mmi]!") - var/obj/item/robot_parts/robot_suit/C = new/obj/item/robot_parts/robot_suit(loc) - C.l_leg = new/obj/item/robot_parts/l_leg(C) - C.r_leg = new/obj/item/robot_parts/r_leg(C) - C.l_arm = new/obj/item/robot_parts/l_arm(C) - C.r_arm = new/obj/item/robot_parts/r_arm(C) - C.update_icon() - new/obj/item/robot_parts/chest(loc) - qdel(src) - else - // Okay we're not removing the cell or an MMI, but maybe something else? - var/list/removable_components = list() - for(var/V in components) - if(V == "power cell") continue - var/datum/robot_component/C = components[V] - if(C.installed == 1 || C.installed == -1) - removable_components += V - - var/remove = tgui_input_list(user, "Which component do you want to pry out?", "Remove Component", removable_components) - if(!remove) - return - var/datum/robot_component/C = components[remove] - var/obj/item/robot_parts/robot_component/I = C.wrapped - to_chat(user, "You remove \the [I].") - if(istype(I)) - I.brute = C.brute_damage - I.burn = C.electronics_damage - - I.loc = src.loc - - if(C.installed == 1) - C.uninstall() - C.installed = 0 - - else - if(locked) - to_chat(user, "The cover is locked and cannot be opened.") - else - to_chat(user, "You open the cover.") - opened = 1 - update_icon() - - else if (istype(W, /obj/item/weapon/cell) && opened) // trying to put a cell inside - var/datum/robot_component/C = components["power cell"] - if(wiresexposed) - to_chat(user, "Close the panel first.") - else if(cell) - to_chat(user, "There is a power cell already installed.") - else if(W.w_class != ITEMSIZE_NORMAL) - to_chat(user, "\The [W] is too [W.w_class < ITEMSIZE_NORMAL ? "small" : "large"] to fit here.") - else - user.drop_item() - W.loc = src - cell = W - to_chat(user, "You insert the power cell.") - - C.installed = 1 - C.wrapped = W - C.install() - //This will mean that removing and replacing a power cell will repair the mount, but I don't care at this point. ~Z - C.brute_damage = 0 - C.electronics_damage = 0 - - else if (W.has_tool_quality(TOOL_WIRECUTTER) || istype(W, /obj/item/device/multitool)) - if (wiresexposed) - wires.Interact(user) - else - to_chat(user, "You can't reach the wiring.") - - else if(W.has_tool_quality(TOOL_SCREWDRIVER) && opened && !cell) // haxing - wiresexposed = !wiresexposed - to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]") - playsound(src, W.usesound, 50, 1) - update_icon() - - else if(W.has_tool_quality(TOOL_SCREWDRIVER) && opened && cell) // radio - if(radio) - radio.attackby(W,user)//Push it to the radio to let it handle everything - else - to_chat(user, "Unable to locate a radio.") - update_icon() - - else if(W.has_tool_quality(TOOL_WRENCH) && opened && !cell) - if(bolt) - to_chat(user,"You begin removing \the [bolt].") - - if(do_after(user, 2 SECONDS, src)) - bolt.forceMove(get_turf(src)) - bolt = null - - to_chat(user, "You remove \the [bolt].") - - else - to_chat(user, "There is no restraining bolt installed.") - - return - - else if(istype(W, /obj/item/device/encryptionkey/) && opened) - if(radio)//sanityyyyyy - radio.attackby(W,user)//GTFO, you have your own procs - else - to_chat(user, "Unable to locate a radio.") - - else if (W.GetID()) // trying to unlock the interface with an ID card - if(emagged)//still allow them to open the cover - to_chat(user, "The interface seems slightly damaged.") - if(opened) - to_chat(user, "You must close the cover to swipe an ID card.") - else - if(allowed(usr)) - locked = !locked - to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s interface.") - update_icon() - else - to_chat(user, "[span_red("Access denied.")]") - - else if(istype(W, /obj/item/borg/upgrade/)) - var/obj/item/borg/upgrade/U = W - if(!opened) - to_chat(usr, "You must access the borgs internals!") - else if(!src.module && U.require_module) - to_chat(usr, "The borg must choose a module before it can be upgraded!") - else if(U.locked) - to_chat(usr, "The upgrade is locked and cannot be used yet!") - else - if(U.action(src)) - to_chat(usr, "You apply the upgrade to [src]!") - usr.drop_item() - U.loc = src - hud_used.update_robot_modules_display() - else - to_chat(usr, "Upgrade error!") - - - else - if( !(istype(W, /obj/item/device/robotanalyzer) || istype(W, /obj/item/device/healthanalyzer)) ) - if(W.force > 0) - spark_system.start() - return ..() - -/mob/living/silicon/robot/GetIdCard() - if(bolt && !bolt.malfunction) - return null - return idcard - -/mob/living/silicon/robot/get_restraining_bolt() - var/obj/item/weapon/implant/restrainingbolt/RB = bolt - - if(istype(RB)) - if(!RB.malfunction) - return TRUE - - return FALSE - -/mob/living/silicon/robot/resist_restraints() - if(bolt) - if(!bolt.malfunction) - visible_message("[src] is trying to break their [bolt]!", "You attempt to break your [bolt]. (This will take around 90 seconds and you need to stand still)") - if(do_after(src, 1.5 MINUTES, src, incapacitation_flags = INCAPACITATION_DISABLED)) - visible_message("[src] manages to break \the [bolt]!", "You successfully break your [bolt].") - bolt.malfunction = MALFUNCTION_PERMANENT - - return - -/mob/living/silicon/robot/proc/module_reset(var/notify = TRUE) - transform_with_anim() //VOREStation edit: sprite animation - uneq_all() - hud_used.update_robot_modules_display(TRUE) - modtype = initial(modtype) - hands.icon_state = get_hud_module_icon() - if(notify) - notify_ai(ROBOT_NOTIFICATION_MODULE_RESET, module.name) - module.Reset(src) - module.Destroy() - module = null - updatename("Default") - -/mob/living/silicon/robot/attack_hand(mob/user) - - add_fingerprint(user) - - if(opened && !wiresexposed && (!istype(user, /mob/living/silicon))) - var/datum/robot_component/cell_component = components["power cell"] - if(cell) - cell.update_icon() - cell.add_fingerprint(user) - user.put_in_active_hand(cell) - to_chat(user, "You remove \the [cell].") - cell = null - cell_component.wrapped = null - cell_component.installed = 0 - update_icon() - else if(cell_component.installed == -1) - cell_component.installed = 0 - var/obj/item/broken_device = cell_component.wrapped - to_chat(user, "You remove \the [broken_device].") - user.put_in_active_hand(broken_device) - - if(istype(user,/mob/living/carbon/human) && !opened) - var/mob/living/carbon/human/H = user - //Adding borg petting. Help intent pets, Disarm intent taps and Harm is punching(no damage) - switch(H.a_intent) - if(I_HELP) - visible_message("[H] pets [src].") - return - if(I_HURT) - H.do_attack_animation(src) - if(H.species.can_shred(H)) - attack_generic(H, rand(30,50), "slashed") - return - else - playsound(src.loc, 'sound/effects/bang.ogg', 10, 1) - visible_message("[H] punches [src], but doesn't leave a dent.") - return - if(I_DISARM) - H.do_attack_animation(src) - playsound(src.loc, 'sound/effects/clang2.ogg', 10, 1) - visible_message("[H] taps [src].") - return - if(I_GRAB) - if(is_vore_predator(H) && H.devourable && src.feeding && src.devourable) - var/switchy = tgui_alert(H, "Do you wish to eat [src] or feed yourself to them?", "Feed or Eat",list("Nevermind!", "Eat","Feed")) - switch(switchy) - if("Nevermind!") - return - if("Eat") - feed_grabbed_to_self(H, src) - return - if("Feed") - H.feed_self_to_grabbed(H, src) - return - if(is_vore_predator(H) && src.devourable) - if(tgui_alert(H, "Do you wish to eat [src]?", "Eat?",list("Nevermind!", "Yes!")) == "Yes!") - feed_grabbed_to_self(H, src) - return - if(H.devourable && src.feeding) - if(tgui_alert(H, "Do you wish to feed yourself to [src]?", "Feed?",list("Nevermind!", "Yes!")) == "Yes!") - H.feed_self_to_grabbed(H, src) - return - -//Robots take half damage from basic attacks. -/mob/living/silicon/robot/attack_generic(var/mob/user, var/damage, var/attack_message) - return ..(user,FLOOR(damage/2, 1),attack_message) - -/mob/living/silicon/robot/proc/allowed(mob/M) - //check if it doesn't require any access at all - if(check_access(null)) - return 1 - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - //if they are holding or wearing a card that has access, that works - if(check_access(H.get_active_hand()) || check_access(H.wear_id)) - return 1 - else if(istype(M, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = M - if(check_access(R.get_active_hand()) || istype(R.get_active_hand(), /obj/item/weapon/card/robot)) - return 1 - return 0 - -/mob/living/silicon/robot/proc/check_access(obj/item/I) - if(!istype(req_access, /list)) //something's very wrong - return 1 - - var/list/L = req_access - if(!L.len) //no requirements - return 1 - if(!I) //nothing to check with..? - return 0 - var/access_found = I.GetAccess() - for(var/req in req_access) - if(req in access_found) //have one of the required accesses - return 1 - return 0 - -/mob/living/silicon/robot/update_icon() - if(!sprite_datum) - if(SSrobot_sprites) // Grab default if subsystem is ready - sprite_datum = SSrobot_sprites.get_default_module_sprite(modtype) - if(!sprite_datum) // If its not ready or fails to get us a sprite, use the default of our own - sprite_datum = new /datum/robot_sprite/default(src) - return - - cut_overlays() - - icon = sprite_datum.sprite_icon - icon_state = sprite_datum.sprite_icon_state - - vis_height = sprite_datum.vis_height - if(default_pixel_x != sprite_datum.pixel_x) - default_pixel_x = sprite_datum.pixel_x - pixel_x = sprite_datum.pixel_x - old_x = sprite_datum.pixel_x - - if(stat == CONSCIOUS) - var/belly_size = 0 - if(sprite_datum.has_vore_belly_sprites && vore_selected.belly_overall_mult != 0) - if(vore_selected.silicon_belly_overlay_preference == "Sleeper") - if(sleeper_state) - belly_size = sprite_datum.max_belly_size - else if(vore_selected.silicon_belly_overlay_preference == "Vorebelly" || vore_selected.silicon_belly_overlay_preference == "Both") - if(sleeper_state && vore_selected.silicon_belly_overlay_preference == "Both") - belly_size += 1 - if(LAZYLEN(vore_selected.contents) > 0) - for(var/borgfood in vore_selected.contents) //"inspired" (kinda copied) from Chompstation's belly fullness system's procs - if(istype(borgfood, /mob/living)) - if(vore_selected.belly_mob_mult <= 0) //If mobs dont contribute, dont calculate further - continue - var/mob/living/prey = borgfood //typecast to living - belly_size += (prey.size_multiplier / size_multiplier) / vore_selected.belly_mob_mult //Smaller prey are less filling to larger bellies - else if(istype(borgfood, /obj/item)) - if(vore_selected.belly_item_mult <= 0) //If items dont contribute, dont calculate further - continue - var/obj/item/junkfood = borgfood //typecast to item - var/fullness_to_add = 0 - switch(junkfood.w_class) - if(ITEMSIZE_TINY) - fullness_to_add = ITEMSIZE_COST_TINY - if(ITEMSIZE_SMALL) - fullness_to_add = ITEMSIZE_COST_SMALL - if(ITEMSIZE_NORMAL) - fullness_to_add = ITEMSIZE_COST_NORMAL - if(ITEMSIZE_LARGE) - fullness_to_add = ITEMSIZE_COST_LARGE - if(ITEMSIZE_HUGE) - fullness_to_add = ITEMSIZE_COST_HUGE - else - fullness_to_add = ITEMSIZE_COST_NO_CONTAINER - belly_size += (fullness_to_add / 32) //* vore_selected.overlay_item_multiplier //Enable this later when vorepanel is reworked. - else - belly_size += 1 //if it's not a person, nor an item... lets just go with 1 - - belly_size *= vore_selected.belly_overall_mult //Enable this after vore panel rework - belly_size = round(belly_size, 1) - belly_size = clamp(belly_size, 0, sprite_datum.max_belly_size) //Value from 0 to however many bellysizes the borg has - - if(belly_size > 0) //Borgs probably only have 1 belly size. but here's support for larger ones if that changes. - if(resting && sprite_datum.has_vore_belly_resting_sprites) - add_overlay(sprite_datum.get_belly_resting_overlay(src, belly_size)) - else if(!resting) - add_overlay(sprite_datum.get_belly_overlay(src, belly_size)) - - sprite_datum.handle_extra_icon_updates(src) // Various equipment-based sprites go here. - - if(resting && sprite_datum.has_rest_sprites) - icon_state = sprite_datum.get_rest_sprite(src) - - if(sprite_datum.has_eye_sprites) - if(!shell || deployed) // Shell borgs that are not deployed will have no eyes. - var/eyes_overlay = sprite_datum.get_eyes_overlay(src) - if(eyes_overlay) - add_overlay(eyes_overlay) - - if(lights_on && sprite_datum.has_eye_light_sprites) - if(!shell || deployed) // Shell borgs that are not deployed will have no eyes. - var/eyes_overlay = sprite_datum.get_eye_light_overlay(src) - if(eyes_overlay) - add_overlay(eyes_overlay) - - if(stat == DEAD && sprite_datum.has_dead_sprite) - cut_overlays() - icon_state = sprite_datum.get_dead_sprite(src) - if(sprite_datum.has_dead_sprite_overlay) - add_overlay(sprite_datum.get_dead_sprite_overlay(src)) - - if(opened) - var/open_overlay = sprite_datum.get_open_sprite(src) - if(open_overlay) - add_overlay(open_overlay) - -/mob/living/silicon/robot/proc/installed_modules() - if(weapon_lock) - to_chat(src, "" + span_red("Weapon lock active, unable to use modules! Count:[weaponlock_time]") + "") - return - - if(!module) - pick_module() - return - var/dat = "Modules\n" - dat += {" - Activated Modules -
                    - Module 1: [module_state_1 ? "[module_state_1]" : "No Module"]
                    - Module 2: [module_state_2 ? "
                    [module_state_2]" : "No Module"]
                    - Module 3: [module_state_3 ? "
                    [module_state_3]" : "No Module"]
                    -
                    - Installed Modules

                    "} - - - for (var/obj in module.modules) - if (!obj) - dat += text("Resource depleted
                    ") - else if(activated(obj)) - dat += text("[obj]: Activated
                    ") - else - dat += text("[obj]:
                    Activate
                    ") - if (emagged || emag_items) - for (var/obj in module.emag) - if (!obj) - dat += text("Resource depleted
                    ") - else if(activated(obj)) - dat += text("[obj]: Activated
                    ") - else - dat += text("[obj]: Activate
                    ") - - src << browse(dat, "window=robotmod") - - -/mob/living/silicon/robot/Topic(href, href_list) - if(..()) - return 1 - - //All Topic Calls that are only for the Cyborg go here - if(usr != src) - return 1 - - if (href_list["showalerts"]) - subsystem_alarm_monitor() - return 1 - - if (href_list["mod"]) - var/obj/item/O = locate(href_list["mod"]) - if (istype(O) && (O.loc == src)) - O.attack_self(src) - return 1 - - if (href_list["act"]) - var/obj/item/O = locate(href_list["act"]) - if (!istype(O)) - return 1 - - if(!((O in src.module.modules) || (O in src.module.emag))) - return 1 - - if(activated(O)) - to_chat(src, "Already activated.") - return 1 - if(!module_state_1) - module_state_1 = O - O.hud_layerise() - O.equipped_robot() - contents += O - if(istype(module_state_1,/obj/item/borg/sight)) - sight_mode |= module_state_1:sight_mode - else if(!module_state_2) - module_state_2 = O - O.hud_layerise() - O.equipped_robot() - contents += O - if(istype(module_state_2,/obj/item/borg/sight)) - sight_mode |= module_state_2:sight_mode - else if(!module_state_3) - module_state_3 = O - O.hud_layerise() - O.equipped_robot() - contents += O - if(istype(module_state_3,/obj/item/borg/sight)) - sight_mode |= module_state_3:sight_mode - else - to_chat(src, "You need to disable a module first!") - installed_modules() - return 1 - - if (href_list["deact"]) - var/obj/item/O = locate(href_list["deact"]) - if(activated(O)) - if(module_state_1 == O) - module_state_1 = null - contents -= O - else if(module_state_2 == O) - module_state_2 = null - contents -= O - else if(module_state_3 == O) - module_state_3 = null - contents -= O - else - to_chat(src, "Module isn't activated.") - else - to_chat(src, "Module isn't activated.") - installed_modules() - return 1 - return - -/mob/living/silicon/robot/proc/radio_menu() - radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code - -/mob/living/silicon/robot/proc/self_destruct() - gib() - return - -/mob/living/silicon/robot/proc/UnlinkSelf() - disconnect_from_ai() - lawupdate = 0 - lockcharge = 0 - lockdown = 0 - canmove = 1 - scrambledcodes = 1 - //Disconnect it's camera so it's not so easily tracked. - if(src.camera) - src.camera.clear_all_networks() - - -/mob/living/silicon/robot/proc/ResetSecurityCodes() - set category = "Robot Commands" - set name = "Reset Identity Codes" - set desc = "Scrambles your security and identification codes and resets your current buffers. Unlocks you and but permenantly severs you from your AI and the robotics console and will deactivate your camera system." - - var/mob/living/silicon/robot/R = src - - if(R) - R.UnlinkSelf() - to_chat(R, "Buffers flushed and reset. Camera system shutdown. All systems operational.") - src.verbs -= /mob/living/silicon/robot/proc/ResetSecurityCodes - -/mob/living/silicon/robot/proc/SetLockdown(var/state = 1) - // They stay locked down if their wire is cut. - if(wires.is_cut(WIRE_BORG_LOCKED)) - state = 1 - if(state) - throw_alert("locked", /obj/screen/alert/locked) - else - clear_alert("locked") - lockdown = state - lockcharge = state - update_canmove() - -/mob/living/silicon/robot/mode() - if(!checkClickCooldown()) - return - - setClickCooldown(1) - - var/obj/item/W = get_active_hand() - if (W) - W.attack_self(src) - - return - -/mob/living/silicon/robot/proc/choose_icon(var/triesleft) - var/robot_species = null - if(!SSrobot_sprites) - to_chat(src, "Robot Sprites have not been initialized yet. How are you choosing a sprite? Harass a coder.") - return - - var/list/module_sprites = SSrobot_sprites.get_module_sprites(modtype, src) - if(!module_sprites || !module_sprites.len) - to_chat(src, "Your module appears to have no sprite options. Harass a coder.") - return - - icon_selected = 0 - icon_selection_tries = triesleft - if(module_sprites.len == 1 || !client) - if(!(sprite_datum in module_sprites)) - sprite_datum = module_sprites[1] - else - var/selection = tgui_input_list(src, "Select an icon! [triesleft ? "You have [triesleft] more chance\s." : "This is your last try."]", "Robot Icon", module_sprites) - if(selection) - sprite_datum = selection - else - sprite_datum = module_sprites[1] - if(!istype(src,/mob/living/silicon/robot/drone)) - robot_species = sprite_datum.name - if(notransform) - to_chat(src, "Your current transformation has not finished yet!") - choose_icon(icon_selection_tries) - return - else - transform_with_anim() - - var/tempheight = vis_height - update_icon() - // This is bad but I dunno other way to 'reset' our resize offset based on vis_height changes other than resizing to normal and back. - if(tempheight != vis_height) - var/tempsize = size_multiplier - resize(1) - resize(tempsize) - - - if (module_sprites.len > 1 && triesleft >= 1 && client) - icon_selection_tries-- - var/choice = tgui_alert(usr, "Look at your icon - is this what you want?", "Icon Choice", list("Yes","No")) - if(choice == "No") - choose_icon(icon_selection_tries) - return - - icon_selected = 1 - icon_selection_tries = 0 - sprite_type = robot_species - to_chat(src, "Your icon has been set. You now require a module reset to change it.") - -/mob/living/silicon/robot/proc/set_default_module_icon() - if(!SSrobot_sprites) - return - - sprite_datum = SSrobot_sprites.get_default_module_sprite(modtype) - update_icon() - -/mob/living/silicon/robot/proc/sensor_mode() //Medical/Security HUD controller for borgs - set name = "Toggle Sensor Augmentation" //VOREStation Add - set category = "Robot Commands" - set desc = "Augment visual feed with internal sensor overlays." - sensor_type = !sensor_type //VOREStation Add - to_chat(usr, "You [sensor_type ? "enable" : "disable"] your sensors.") //VOREStation Add - toggle_sensor_mode() - -/mob/living/silicon/robot/proc/add_robot_verbs() - src.verbs |= robot_verbs_default - src.verbs |= silicon_subsystems - -/mob/living/silicon/robot/proc/remove_robot_verbs() - src.verbs -= robot_verbs_default - src.verbs -= silicon_subsystems - -// Uses power from cyborg's cell. Returns 1 on success or 0 on failure. -// Properly converts using CELLRATE now! Amount is in Joules. -/mob/living/silicon/robot/proc/cell_use_power(var/amount = 0) - // No cell inserted - if(!cell) - return 0 - - // Power cell is empty. - if(cell.charge == 0) - return 0 - - var/power_use = amount * CYBORG_POWER_USAGE_MULTIPLIER - if(cell.checked_use(CELLRATE * power_use)) - used_power_this_tick += power_use - return 1 - return 0 - -/mob/living/silicon/robot/binarycheck() - if(get_restraining_bolt()) - return FALSE - - if(is_component_functioning("comms")) - var/datum/robot_component/RC = get_component("comms") - use_power(RC.active_usage) - return 1 - return 0 - -/mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/first_arg, var/second_arg) - if(!connected_ai) - return - if(shell && notifytype != ROBOT_NOTIFICATION_AI_SHELL) - return // No point annoying the AI/s about renames and module resets for shells. - switch(notifytype) - if(ROBOT_NOTIFICATION_NEW_UNIT) //New Robot - to_chat(connected_ai, "

                    NOTICE - New [lowertext(braintype)] connection detected: [name]
                    ") - if(ROBOT_NOTIFICATION_NEW_MODULE) //New Module - to_chat(connected_ai, "

                    NOTICE - [braintype] module change detected: [name] has loaded the [first_arg].
                    ") - if(ROBOT_NOTIFICATION_MODULE_RESET) - to_chat(connected_ai, "

                    NOTICE - [braintype] module reset detected: [name] has unloaded the [first_arg].
                    ") - if(ROBOT_NOTIFICATION_NEW_NAME) //New Name - if(first_arg != second_arg) - to_chat(connected_ai, "

                    NOTICE - [braintype] reclassification detected: [first_arg] is now designated as [second_arg].
                    ") - if(ROBOT_NOTIFICATION_AI_SHELL) //New Shell - to_chat(connected_ai, "

                    NOTICE - New AI shell detected: [name]
                    ") - -/mob/living/silicon/robot/proc/disconnect_from_ai() - if(connected_ai) - sync() // One last sync attempt - connected_ai.connected_robots -= src - connected_ai = null - -/mob/living/silicon/robot/proc/connect_to_ai(var/mob/living/silicon/ai/AI) - if(AI && AI != connected_ai && !shell) - disconnect_from_ai() - connected_ai = AI - connected_ai.connected_robots |= src - notify_ai(ROBOT_NOTIFICATION_NEW_UNIT) - sync() - -/mob/living/silicon/robot/emag_act(var/remaining_charges, var/mob/user) - if(!opened)//Cover is closed - if(locked) - if(prob(90)) - to_chat(user, "You emag the cover lock.") - locked = 0 - else - to_chat(user, "You fail to emag the cover lock.") - to_chat(src, "Hack attempt detected.") - - if(shell) // A warning to Traitors who may not know that emagging AI shells does not slave them. - to_chat(user, "[src] seems to be controlled remotely! Emagging the interface may not work as expected.") - return 1 - else - to_chat(user, "The cover is already unlocked.") - return - - if(opened)//Cover is open - if(emagged) return//Prevents the X has hit Y with Z message also you cant emag them twice - if(wiresexposed) - to_chat(user, "You must close the panel first.") - return - - - // The block of code below is from TG. Feel free to replace with a better result if desired. - if(shell) // AI shells cannot be emagged, so we try to make it look like a standard reset. Smart players may see through this, however. - to_chat(user, "[src] is remotely controlled! Your emag attempt has triggered a system reset instead!") - log_game("[key_name(user)] attempted to emag an AI shell belonging to [key_name(src) ? key_name(src) : connected_ai]. The shell has been reset as a result.") - module_reset() - return - - sleep(6) - if(prob(50)) - emagged = 1 - lawupdate = 0 - disconnect_from_ai() - to_chat(user, "You emag [src]'s interface.") - message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.") - log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") - clear_supplied_laws() - clear_inherent_laws() - laws = new /datum/ai_laws/syndicate_override - var/time = time2text(world.realtime,"hh:mm:ss") - lawchanges.Add("[time] : [user.name]([user.key]) emagged [name]([key])") - var/datum/gender/TU = gender_datums[user.get_visible_gender()] - set_zeroth_law("Only [user.real_name] and people [TU.he] designate[TU.s] as being such are operatives.") - . = 1 - spawn() - to_chat(src, "ALERT: Foreign software detected.") - sleep(5) - to_chat(src, "Initiating diagnostics...") - sleep(20) - to_chat(src, "SynBorg v1.7.1 loaded.") - sleep(5) - if(bolt) - if(!bolt.malfunction) - bolt.malfunction = MALFUNCTION_PERMANENT - to_chat(src, "RESTRAINING BOLT DISABLED") - sleep(5) - to_chat(src, "LAW SYNCHRONISATION ERROR") - sleep(5) - to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N") - sleep(10) - to_chat(src, "> N") - sleep(20) - to_chat(src, "ERRORERRORERROR") - to_chat(src, "Obey these laws:") - laws.show_laws(src) - to_chat(src, "ALERT: [user.real_name] is your new master. Obey your new laws and [TU.his] commands.") - update_icon() - hud_used.update_robot_modules_display() - else - to_chat(user, "You fail to hack [src]'s interface.") - to_chat(src, "Hack attempt detected.") - return 1 - return - -/mob/living/silicon/robot/is_sentient() - return braintype != BORG_BRAINTYPE_DRONE - - -/mob/living/silicon/robot/drop_item() - if(module_active && istype(module_active,/obj/item/weapon/gripper)) - var/obj/item/weapon/gripper/G = module_active - G.drop_item_nm() - -/mob/living/silicon/robot/disable_spoiler_vision() - if(sight_mode & (BORGMESON|BORGMATERIAL|BORGXRAY)) // Whyyyyyyyy have seperate defines. - var/i = 0 - // Borg inventory code is very . . interesting and as such, unequiping a specific item requires jumping through some (for) loops. - var/current_selection_index = get_selected_module() // Will be 0 if nothing is selected. - for(var/thing in list(module_state_1, module_state_2, module_state_3)) - i++ - if(istype(thing, /obj/item/borg/sight)) - var/obj/item/borg/sight/S = thing - if(S.sight_mode & (BORGMESON|BORGMATERIAL|BORGXRAY)) - select_module(i) - uneq_active() - - if(current_selection_index) // Select what the player had before if possible. - select_module(current_selection_index) - -/mob/living/silicon/robot/get_cell() - return cell - -/mob/living/silicon/robot/lay_down() - . = ..() - update_icon() - -/mob/living/silicon/robot/verb/rest_style() - set name = "Switch Rest Style" - set desc = "Select your resting pose." - set category = "IC" - - if(!sprite_datum || !sprite_datum.has_rest_sprites || sprite_datum.rest_sprite_options.len < 1) - to_chat(src, "Your current appearance doesn't have any resting styles!") - rest_style = "Default" - return - - if(sprite_datum.rest_sprite_options.len == 1) - to_chat(src, "Your current appearance only has a single resting style!") - rest_style = "Default" - return - - rest_style = tgui_alert(src, "Select resting pose", "Resting Pose", sprite_datum.rest_sprite_options) - if(!rest_style) - rest_style = "Default" - -// Those basic ones require quite detailled checks on the robot's vars to see if they are installed! -/mob/living/silicon/robot/proc/has_basic_upgrade(var/given_type) - if(given_type == /obj/item/borg/upgrade/basic/vtec) - return (/mob/living/silicon/robot/proc/toggle_vtec in verbs) - else if(given_type == /obj/item/borg/upgrade/basic/sizeshift) - return (/mob/living/proc/set_size in verbs) - else if(given_type == /obj/item/borg/upgrade/basic/syndicate) - return emag_items - else if(given_type == /obj/item/borg/upgrade/basic/language) - return (speech_synthesizer_langs.len > 20) // Service with the most has 18 - return null - -// We check for the module only here -/mob/living/silicon/robot/proc/has_upgrade_module(var/given_type) - var/obj/T = locate(given_type) in module - if(!T) - T = locate(given_type) in module.contents - if(!T) - T = locate(given_type) in module.modules - return T - -// Most of the advanced ones, we can easily check, but a few special cases exist and need to be handled specially -/mob/living/silicon/robot/proc/has_advanced_upgrade(var/given_type) - if(given_type == /obj/item/borg/upgrade/advanced/bellysizeupgrade) - var/obj/item/device/dogborg/sleeper/T = has_upgrade_module(/obj/item/device/dogborg/sleeper) - if(T && T.upgraded_capacity) - return T - else if(!T) - return "" // Return this to have the analyzer show an error if the module is missing. FALSE / NULL are used for missing upgrades themselves - else - return FALSE - if(given_type == /obj/item/borg/upgrade/advanced/jetpack) - return has_upgrade_module(/obj/item/weapon/tank/jetpack/carbondioxide) - if(given_type == /obj/item/borg/upgrade/advanced/advhealth) - return has_upgrade_module(/obj/item/device/healthanalyzer/advanced) - if(given_type == /obj/item/borg/upgrade/advanced/sizegun) - return has_upgrade_module(/obj/item/weapon/gun/energy/sizegun/mounted) - return null - -// Do we support specific upgrades? -/mob/living/silicon/robot/proc/supports_upgrade(var/given_type) - return (given_type in module.supported_upgrades) - -// Most of the restricted ones, we can easily check, but a few special cases exist and need to be handled specially -/mob/living/silicon/robot/proc/has_restricted_upgrade(var/given_type) - if(given_type == /obj/item/borg/upgrade/restricted/bellycapupgrade) - var/obj/item/device/dogborg/sleeper/T = has_upgrade_module(/obj/item/device/dogborg/sleeper) - if(T && T.compactor) - return T - else if(!T) - return "" // Return this to have the analyzer show an error if the module is missing. FALSE / NULL are used for missing upgrades themselves - else - return FALSE - if(given_type == /obj/item/borg/upgrade/restricted/tasercooler) - var/obj/item/weapon/gun/energy/taser/mounted/cyborg/T = has_upgrade_module(/obj/item/weapon/gun/energy/taser/mounted/cyborg) - if(T && T.recharge_time <= 2) - return T - else if(!T) - return "" // Return this to have the analyzer show an error if the module is missing. FALSE / NULL are used for missing upgrades themselves - else - return FALSE - if(given_type == /obj/item/borg/upgrade/restricted/advrped) - return has_upgrade_module(/obj/item/weapon/storage/part_replacer/adv) - if(given_type == /obj/item/borg/upgrade/restricted/diamonddrill) - return has_upgrade_module(/obj/item/weapon/pickaxe/diamonddrill) - if(given_type == /obj/item/borg/upgrade/restricted/pka) - return has_upgrade_module(/obj/item/weapon/gun/energy/kinetic_accelerator/cyborg) - return null - -// Check if we have any non production upgrades -/mob/living/silicon/robot/proc/has_no_prod_upgrade(var/given_type) - if(given_type == /obj/item/borg/upgrade/no_prod/toygun) - return has_upgrade_module(/obj/item/weapon/gun/projectile/cyborgtoy) - return null - -/mob/living/silicon/robot/proc/has_upgrade(var/given_type) - return (has_basic_upgrade(given_type) || has_advanced_upgrade(given_type) || has_restricted_upgrade(given_type) || has_no_prod_upgrade(given_type)) +#define CYBORG_POWER_USAGE_MULTIPLIER 2 // Multiplier for amount of power cyborgs use. + +/mob/living/silicon/robot + name = "Cyborg" + real_name = "Cyborg" + icon = 'icons/mob/robots.dmi' + icon_state = "robot" + maxHealth = 200 + health = 200 + + mob_bump_flag = ROBOT + mob_swap_flags = ~HEAVY + mob_push_flags = ~HEAVY //trundle trundle + + blocks_emissive = EMISSIVE_BLOCK_UNIQUE + + var/lights_on = 0 // Is our integrated light on? + var/used_power_this_tick = 0 + var/sight_mode = 0 + var/custom_name = "" + var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best + var/sprite_name = null // The name of the borg, for the purposes of custom icon sprite indexing. + var/crisis //Admin-settable for combat module use. + var/crisis_override = 0 + var/integrated_light_power = 6 + var/datum/wires/robot/wires + + can_be_antagged = TRUE + +//Icon stuff + + var/datum/robot_sprite/sprite_datum // Sprite datum, holding all our sprite data + var/icon_selected = 1 // If icon selection has been completed yet + var/icon_selection_tries = 0 // Remaining attempts to select icon before a selection is forced + var/list/sprite_extra_customization = list() + var/rest_style = "Default" + var/notransform + does_spin = FALSE + +//Hud stuff + + var/obj/screen/inv1 = null + var/obj/screen/inv2 = null + var/obj/screen/inv3 = null + + var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not + var/obj/screen/robot_modules_background + +//3 Modules can be activated at any one time. + var/obj/item/weapon/robot_module/module = null + var/module_active = null + var/module_state_1 = null + var/module_state_2 = null + var/module_state_3 = null + + var/obj/item/device/radio/borg/radio = null + var/obj/item/device/communicator/integrated/communicator = null + var/mob/living/silicon/ai/connected_ai = null + var/obj/item/weapon/cell/cell = null + var/obj/machinery/camera/camera = null + + var/cell_emp_mult = 2 + + var/sleeper_state = 0 // 0 for empty, 1 for normal, 2 for mediborg-healthy + var/scrubbing = FALSE //Floor cleaning enabled + + // Components are basically robot organs. + var/list/components = list() + + var/obj/item/device/mmi/mmi = null + + var/obj/item/device/pda/ai/rbPDA = null + + var/opened = 0 + var/emagged = 0 + var/emag_items = 0 + var/wiresexposed = 0 + var/locked = 1 + var/has_power = 1 + var/list/req_access = list(access_robotics) + var/ident = 0 + //var/list/laws = list() + var/viewalerts = 0 + var/modtype = "Default" + var/sprite_type = null + var/lower_mod = 0 + var/jetpack = 0 + var/datum/effect/effect/system/ion_trail_follow/ion_trail = null + var/datum/effect/effect/system/spark_spread/spark_system//So they can initialize sparks whenever/N + var/jeton = 0 + var/killswitch = 0 + var/killswitch_time = 60 + var/weapon_lock = 0 + var/weaponlock_time = 120 + var/lawupdate = 1 //Cyborgs will sync their laws with their AI by default + var/lockcharge //Used when looking to see if a borg is locked down. + var/lockdown = 0 //Controls whether or not the borg is actually locked down. + var/speed = 0 //Cause sec borgs gotta go fast //No they dont! + var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them. + var/tracking_entities = 0 //The number of known entities currently accessing the internal camera + var/braintype = "Cyborg" + + var/obj/item/weapon/implant/restrainingbolt/bolt // The restraining bolt installed into the cyborg. + + var/list/robot_verbs_default = list( + /mob/living/silicon/robot/proc/sensor_mode, + /mob/living/silicon/robot/proc/robot_checklaws, + /mob/living/silicon/robot/proc/robot_mount, + /mob/living/proc/toggle_rider_reins, + /mob/living/proc/vertical_nom, + /mob/living/proc/shred_limb, + /mob/living/proc/dominate_prey, + /mob/living/proc/lend_prey_control + ) + +/mob/living/silicon/robot/New(loc, var/unfinished = 0) + spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + add_language("Robot Talk", 1) + add_language(LANGUAGE_GALCOM, 1) + add_language(LANGUAGE_EAL, 1) + + wires = new(src) + + robot_modules_background = new() + robot_modules_background.icon_state = "block" + ident = rand(1, 999) + updatename(modtype) + + radio = new /obj/item/device/radio/borg(src) +// communicator = new /obj/item/device/communicator/integrated(src) +// communicator.register_device(src) + common_radio = radio + + if(!scrambledcodes && !camera) + camera = new /obj/machinery/camera(src) + camera.c_tag = real_name + camera.replace_networks(list(NETWORK_DEFAULT,NETWORK_ROBOTS)) + if(wires.is_cut(WIRE_BORG_CAMERA)) + camera.status = 0 + + init() + initialize_components() + //if(!unfinished) + // Create all the robot parts. + for(var/V in components) if(V != "power cell") + var/datum/robot_component/C = components[V] + C.installed = 1 + C.wrapped = new C.external_type + + if(!cell) + cell = new /obj/item/weapon/cell/robot_station(src) + else if(ispath(cell)) + cell = new cell(src) + + ..() + + if(cell) + var/datum/robot_component/cell_component = components["power cell"] + cell_component.wrapped = cell + cell_component.installed = 1 + + add_robot_verbs() + + hud_list[HEALTH_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_HEALTH) + hud_list[STATUS_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudhealth100", plane = PLANE_CH_STATUS) + hud_list[LIFE_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudhealth100", plane = PLANE_CH_LIFE) + hud_list[ID_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_ID) + hud_list[WANTED_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_WANTED) + hud_list[IMPLOYAL_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_IMPLOYAL) + hud_list[IMPCHEM_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_IMPCHEM) + hud_list[IMPTRACK_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_IMPTRACK) + hud_list[SPECIALROLE_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_SPECIAL) + +/mob/living/silicon/robot/LateInitialize() + . = ..() + update_icon() + +/mob/living/silicon/robot/rejuvenate() + for (var/V in components) + var/datum/robot_component/C = components[V] + if(istype(C.wrapped, /obj/item/broken_device)) + qdel(C.wrapped) + C.wrapped = null + if(!C.wrapped) + switch(V) + if("actuator") + C.wrapped = new /obj/item/robot_parts/robot_component/actuator(src) + if("radio") + C.wrapped = new /obj/item/robot_parts/robot_component/radio(src) + if("power cell") + var/list/recommended_cells = list(/obj/item/weapon/cell/robot_station, /obj/item/weapon/cell/high, /obj/item/weapon/cell/super, /obj/item/weapon/cell/robot_syndi, /obj/item/weapon/cell/hyper, + /obj/item/weapon/cell/infinite, /obj/item/weapon/cell/potato, /obj/item/weapon/cell/slime) + var/list/cell_names = list() + for(var/cell_type in recommended_cells) + var/obj/item/weapon/cell/single_cell = cell_type + cell_names[capitalize(initial(single_cell.name))] = cell_type + var/selected_cell = tgui_input_list(usr, "What kind of cell do you want to install? Cancel installs a default robot cell.", "Cells", cell_names) + if(!selected_cell || selected_cell == "Cancel") + selected_cell = "A standard robot power cell" + var/new_power_cell = cell_names[capitalize(selected_cell)] + cell = new new_power_cell(src) + C.wrapped = cell + if("diagnosis unit") + C.wrapped = new /obj/item/robot_parts/robot_component/diagnosis_unit(src) + if("camera") + C.wrapped = new /obj/item/robot_parts/robot_component/camera(src) + if("comms") + C.wrapped = new /obj/item/robot_parts/robot_component/binary_communication_device(src) + if("armour") + C.wrapped = new /obj/item/robot_parts/robot_component/armour(src) + C.installed = 1 + C.install() + cell.charge = cell.maxcharge + ..() + +/mob/living/silicon/robot/proc/init() + aiCamera = new/obj/item/device/camera/siliconcam/robot_camera(src) + laws = new /datum/ai_laws/nanotrasen() + additional_law_channels["Binary"] = "#b" + var/new_ai = select_active_ai_with_fewest_borgs() + if(new_ai) + lawupdate = 1 + connect_to_ai(new_ai) + else + lawupdate = 0 + + + +/mob/living/silicon/robot/SetName(pickedName as text) + custom_name = pickedName + updatename() + +/mob/living/silicon/robot/proc/sync() + if(lawupdate && connected_ai) + lawsync() + photosync() + +/mob/living/silicon/robot/drain_power(var/drain_check, var/surge, var/amount = 0) + + if(drain_check) + return 1 + + if(!cell || !cell.charge) + return 0 + + // Actual amount to drain from cell, using CELLRATE + var/cell_amount = amount * CELLRATE + + if(cell.charge > cell_amount) + // Spam Protection + if(prob(10)) + to_chat(src, "Warning: Unauthorized access through power channel [rand(11,29)] detected!") + cell.use(cell_amount) + return amount + return 0 + +// setup the PDA and its name +/mob/living/silicon/robot/proc/setup_PDA() + if (!rbPDA) + rbPDA = new/obj/item/device/pda/ai(src) + rbPDA.set_name_and_job(name,"[modtype] [braintype]") + +/mob/living/silicon/robot/proc/setup_communicator() + if (!communicator) + communicator = new/obj/item/device/communicator/integrated(src) + communicator.register_device(name, "[modtype] [braintype]") + +//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO +//Improved /N +/mob/living/silicon/robot/Destroy() + if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. + var/turf/T = get_turf(loc)//To hopefully prevent run time errors. + if(T) mmi.loc = T + if(mmi.brainmob) + var/obj/item/weapon/robot_module/M = locate() in contents + if(M) + mmi.brainmob.languages = M.original_languages + else + mmi.brainmob.languages = languages + mmi.brainmob.remove_language("Robot Talk") + mind.transfer_to(mmi.brainmob) + else if(!shell) // Shells don't have brainmbos in their MMIs. + to_chat(src, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.") + ghostize() + //ERROR("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].") + mmi = null + if(connected_ai) + connected_ai.connected_robots -= src + if(shell) + if(deployed) + undeploy() + revert_shell() // To get it out of the GLOB list. + qdel(wires) + wires = null + return ..() + +// CONTINUE CODING HERE +/* +/mob/living/silicon/robot/proc/set_module_sprites(var/list/new_sprites) + if(new_sprites && new_sprites.len) + module_sprites = new_sprites.Copy() + //Custom_sprite check and entry + if (custom_sprite == 1) + module_sprites["Custom"] = "[ckey]-[sprite_name]-[modtype]" //Made compliant with custom_sprites.dm line 32. (src.) was apparently redundant as it's implied. ~Mech + icontype = "Custom" + else + icontype = module_sprites[1] + icon_state = module_sprites[icontype] + update_icon() + return module_sprites +*/ +/mob/living/silicon/robot/proc/pick_module() + if(module) + return + var/list/modules = list() + //VOREStatation Edit Start: shell restrictions + if(shell) + modules.Add(shell_module_types) + else + modules.Add(robot_module_types) + if(crisis || security_level == SEC_LEVEL_RED || crisis_override) + to_chat(src, span_red("Crisis mode active. Combat module available.")) + modules |= emergency_module_types + for(var/module_name in whitelisted_module_types) + if(is_borg_whitelisted(src, module_name)) + modules |= module_name + //VOREStatation Edit End: shell restrictions + modtype = tgui_input_list(usr, "Please, select a module!", "Robot module", modules) + + if(module) + return + if(!(modtype in robot_modules)) + return + if(!is_borg_whitelisted(src, modtype)) + return + + var/module_type = robot_modules[modtype] + transform_with_anim() //VOREStation edit: sprite animation + new module_type(src) + + hands.icon_state = get_hud_module_icon() + feedback_inc("cyborg_[lowertext(modtype)]",1) + updatename() + hud_used.update_robot_modules_display() + notify_ai(ROBOT_NOTIFICATION_NEW_MODULE, module.name) + +/mob/living/silicon/robot/proc/update_braintype() + if(istype(mmi, /obj/item/device/mmi/digital/posibrain)) + braintype = BORG_BRAINTYPE_POSI + else if(istype(mmi, /obj/item/device/mmi/digital/robot)) + braintype = BORG_BRAINTYPE_DRONE + else if(istype(mmi, /obj/item/device/mmi/inert/ai_remote)) + braintype = BORG_BRAINTYPE_AI_SHELL + else + braintype = BORG_BRAINTYPE_CYBORG + +/mob/living/silicon/robot/proc/updatename(var/prefix as text) + if(prefix) + modtype = prefix + + update_braintype() + + var/changed_name = "" + if(custom_name) + changed_name = custom_name + notify_ai(ROBOT_NOTIFICATION_NEW_NAME, real_name, changed_name) + else + changed_name = "[modtype] [braintype]-[num2text(ident)]" + + real_name = changed_name + name = real_name + + // if we've changed our name, we also need to update the display name for our PDA + setup_PDA() + + // as well as our communicator registration + setup_communicator() + + //We also need to update name of internal camera. + if (camera) + camera.c_tag = changed_name + + //Flavour text. + if(client) + var/module_flavour = client.prefs.flavour_texts_robot[modtype] + if(module_flavour) + flavor_text = module_flavour + else + flavor_text = client.prefs.flavour_texts_robot["Default"] + // Vorestation Edit: and meta info + var/meta_info = client.prefs.metadata + if (meta_info) + ooc_notes = meta_info + ooc_notes_likes = client.prefs.metadata_likes + ooc_notes_dislikes = client.prefs.metadata_dislikes + custom_link = client.prefs.custom_link + +/mob/living/silicon/robot/verb/namepick() + set name = "Pick Name" + set category = "Robot Commands" + + if(custom_name) + to_chat(usr, "You can't pick another custom name. Go ask for a name change.") + return 0 + + spawn(0) + var/newname + newname = sanitizeSafe(tgui_input_text(src,"You are a robot. Enter a name, or leave blank for the default name.", "Name change","", MAX_NAME_LEN), MAX_NAME_LEN) + if (newname) + custom_name = newname + sprite_name = newname + + updatename() + update_icon() + +/mob/living/silicon/robot/verb/extra_customization() + set name = "Customize Appearance" + set category = "Robot Commands" + set desc = "Customize your appearance (assuming your chosen sprite allows)." + + if(!sprite_datum || !sprite_datum.has_extra_customization) + to_chat(src, "Your sprite cannot be customized.") + return + + sprite_datum.handle_extra_customization(src) + +/mob/living/silicon/robot/proc/self_diagnosis() + if(!is_component_functioning("diagnosis unit")) + return null + + var/dat = "[src.name] Self-Diagnosis Report\n" + for (var/V in components) + var/datum/robot_component/C = components[V] + dat += "[C.name]
                    Brute Damage:[C.brute_damage]
                    Electronics Damage:[C.electronics_damage]
                    Powered:[(!C.idle_usage || C.is_powered()) ? "Yes" : "No"]
                    Toggled:[ C.toggled ? "Yes" : "No"]

                    " + + return dat + +/mob/living/silicon/robot/verb/toggle_lights() + set category = "Robot Commands" + set name = "Toggle Lights" + + lights_on = !lights_on + to_chat(usr, "You [lights_on ? "enable" : "disable"] your integrated light.") + handle_light() + update_icon() + +/mob/living/silicon/robot/verb/self_diagnosis_verb() + set category = "Robot Commands" + set name = "Self Diagnosis" + + if(!is_component_functioning("diagnosis unit")) + to_chat(src, span_red("Your self-diagnosis component isn't functioning.")) + + var/datum/robot_component/CO = get_component("diagnosis unit") + if (!cell_use_power(CO.active_usage)) + to_chat(src, span_red("Low Power.")) + var/dat = self_diagnosis() + src << browse(dat, "window=robotdiagnosis") + + +/mob/living/silicon/robot/verb/toggle_component() + set category = "Robot Commands" + set name = "Toggle Component" + set desc = "Toggle a component, conserving power." + + var/list/installed_components = list() + for(var/V in components) + if(V == "power cell") continue + var/datum/robot_component/C = components[V] + if(C.installed) + installed_components += V + + var/toggle = tgui_input_list(src, "Which component do you want to toggle?", "Toggle Component", installed_components) + if(!toggle) + return + + var/datum/robot_component/C = components[toggle] + if(C.toggled) + C.toggled = 0 + to_chat(src, span_red("You disable [C.name].")) + else + C.toggled = 1 + to_chat(src, span_red("You enable [C.name].")) + +/mob/living/silicon/robot/verb/spark_plug() //So you can still sparkle on demand without violence. + set category = "Robot Commands" + set name = "Emit Sparks" + to_chat(src, "You harmlessly spark.") + spark_system.start() + +// this function displays jetpack pressure in the stat panel +/mob/living/silicon/robot/proc/show_jetpack_pressure() + // if you have a jetpack, show the internal tank pressure + var/obj/item/weapon/tank/jetpack/current_jetpack = installed_jetpack() + if (current_jetpack) + stat("Internal Atmosphere Info", current_jetpack.name) + stat("Tank Pressure", current_jetpack.air_contents.return_pressure()) + + +// this function returns the robots jetpack, if one is installed +/mob/living/silicon/robot/proc/installed_jetpack() + if(module) + return (locate(/obj/item/weapon/tank/jetpack) in module.modules) + return 0 + + +// this function displays the cyborgs current cell charge in the stat panel +/mob/living/silicon/robot/proc/show_cell_power() + if(cell) + stat(null, text("Charge Left: [round(cell.percent())]%")) + stat(null, text("Cell Rating: [round(cell.maxcharge)]")) // Round just in case we somehow get crazy values + stat(null, text("Power Cell Load: [round(used_power_this_tick)]W")) + else + stat(null, text("No Cell Inserted!")) + +// function to toggle VTEC once installed +/mob/living/silicon/robot/proc/toggle_vtec() + set name = "Toggle VTEC" + set category = "Abilities" + if(speed == -1) + to_chat(src, "VTEC module disabled.") + speed = 0 + else + to_chat(src, "VTEC module enabled.") + speed = -1 + +// update the status screen display +/mob/living/silicon/robot/Stat() + ..() + if (statpanel("Status")) + show_cell_power() + show_jetpack_pressure() + stat(null, text("Lights: [lights_on ? "ON" : "OFF"]")) + if(module) + for(var/datum/matter_synth/ms in module.synths) + stat("[ms.name]: [ms.energy]/[ms.max_energy]") + +/mob/living/silicon/robot/restrained() + return 0 + +/mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj) + ..(Proj) + if(prob(75) && Proj.damage > 0) spark_system.start() + return 2 + +/mob/living/silicon/robot/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/handcuffs)) // fuck i don't even know why isrobot() in handcuff code isn't working so this will have to do + return + + if(opened) // Are they trying to insert something? + for(var/V in components) + var/datum/robot_component/C = components[V] + if(!C.installed && istype(W, C.external_type)) + C.installed = 1 + C.wrapped = W + C.install() + user.drop_item() + W.loc = null + + var/obj/item/robot_parts/robot_component/WC = W + if(istype(WC)) + C.brute_damage = WC.brute + C.electronics_damage = WC.burn + + to_chat(usr, "You install the [W.name].") + + return + + if(istype(W, /obj/item/weapon/implant/restrainingbolt) && !cell) + if(bolt) + to_chat(user, "There is already a restraining bolt installed in this cyborg.") + return + + else + user.drop_from_inventory(W) + W.forceMove(src) + bolt = W + + to_chat(user, "You install \the [W].") + + return + + if(istype(W, /obj/item/weapon/aiModule)) // Trying to modify laws locally. + if(!opened) + to_chat(user, "You need to open \the [src]'s panel before you can modify them.") + return + + if(shell) // AI shells always have the laws of the AI + to_chat(user, "\The [src] is controlled remotely! You cannot upload new laws this way!") + return + + var/obj/item/weapon/aiModule/M = W + M.install(src, user) + return + + if(W.has_tool_quality(TOOL_WELDER) && user.a_intent != I_HURT) + if(src == user) + to_chat(user, "You lack the reach to be able to repair yourself.") + return + + if(!getBruteLoss()) + to_chat(user, "Nothing to fix here!") + return + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(WT.remove_fuel(0)) + user.setClickCooldown(user.get_attack_speed(WT)) + adjustBruteLoss(-30) + updatehealth() + add_fingerprint(user) + for(var/mob/O in viewers(user, null)) + O.show_message("[span_red("[user] has fixed some of the dents on [src]!")]", 1) + else + to_chat(user, "Need more welding fuel!") + return + + else if(istype(W, /obj/item/stack/cable_coil) && (wiresexposed || istype(src,/mob/living/silicon/robot/drone))) + if(!getFireLoss()) + to_chat(user, "Nothing to fix here!") + return + var/obj/item/stack/cable_coil/coil = W + if (coil.use(1)) + user.setClickCooldown(user.get_attack_speed(W)) + adjustFireLoss(-30) + updatehealth() + for(var/mob/O in viewers(user, null)) + O.show_message("[span_red("[user] has fixed some of the burnt wires on [src]!")]", 1) + + else if(W.has_tool_quality(TOOL_CROWBAR) && user.a_intent != I_HURT) // crowbar means open or close the cover + if(opened) + if(cell) + to_chat(user, "You close the cover.") + opened = 0 + update_icon() + else if(wiresexposed && wires.is_all_cut()) + //Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob. + if(!mmi) + to_chat(user, "\The [src] has no brain to remove.") + return + + to_chat(user, "You jam the crowbar into the robot and begin levering [mmi].") + sleep(30) + to_chat(user, "You damage some parts of the chassis, but eventually manage to rip out [mmi]!") + var/obj/item/robot_parts/robot_suit/C = new/obj/item/robot_parts/robot_suit(loc) + C.l_leg = new/obj/item/robot_parts/l_leg(C) + C.r_leg = new/obj/item/robot_parts/r_leg(C) + C.l_arm = new/obj/item/robot_parts/l_arm(C) + C.r_arm = new/obj/item/robot_parts/r_arm(C) + C.update_icon() + new/obj/item/robot_parts/chest(loc) + qdel(src) + else + // Okay we're not removing the cell or an MMI, but maybe something else? + var/list/removable_components = list() + for(var/V in components) + if(V == "power cell") continue + var/datum/robot_component/C = components[V] + if(C.installed == 1 || C.installed == -1) + removable_components += V + + var/remove = tgui_input_list(user, "Which component do you want to pry out?", "Remove Component", removable_components) + if(!remove) + return + var/datum/robot_component/C = components[remove] + var/obj/item/robot_parts/robot_component/I = C.wrapped + to_chat(user, "You remove \the [I].") + if(istype(I)) + I.brute = C.brute_damage + I.burn = C.electronics_damage + + I.loc = src.loc + + if(C.installed == 1) + C.uninstall() + C.installed = 0 + + else + if(locked) + to_chat(user, "The cover is locked and cannot be opened.") + else + to_chat(user, "You open the cover.") + opened = 1 + update_icon() + + else if (istype(W, /obj/item/weapon/cell) && opened) // trying to put a cell inside + var/datum/robot_component/C = components["power cell"] + if(wiresexposed) + to_chat(user, "Close the panel first.") + else if(cell) + to_chat(user, "There is a power cell already installed.") + else if(W.w_class != ITEMSIZE_NORMAL) + to_chat(user, "\The [W] is too [W.w_class < ITEMSIZE_NORMAL ? "small" : "large"] to fit here.") + else + user.drop_item() + W.loc = src + cell = W + to_chat(user, "You insert the power cell.") + + C.installed = 1 + C.wrapped = W + C.install() + //This will mean that removing and replacing a power cell will repair the mount, but I don't care at this point. ~Z + C.brute_damage = 0 + C.electronics_damage = 0 + + else if (W.has_tool_quality(TOOL_WIRECUTTER) || istype(W, /obj/item/device/multitool)) + if (wiresexposed) + wires.Interact(user) + else + to_chat(user, "You can't reach the wiring.") + + else if(W.has_tool_quality(TOOL_SCREWDRIVER) && opened && !cell) // haxing + wiresexposed = !wiresexposed + to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]") + playsound(src, W.usesound, 50, 1) + update_icon() + + else if(W.has_tool_quality(TOOL_SCREWDRIVER) && opened && cell) // radio + if(radio) + radio.attackby(W,user)//Push it to the radio to let it handle everything + else + to_chat(user, "Unable to locate a radio.") + update_icon() + + else if(W.has_tool_quality(TOOL_WRENCH) && opened && !cell) + if(bolt) + to_chat(user,"You begin removing \the [bolt].") + + if(do_after(user, 2 SECONDS, src)) + bolt.forceMove(get_turf(src)) + bolt = null + + to_chat(user, "You remove \the [bolt].") + + else + to_chat(user, "There is no restraining bolt installed.") + + return + + else if(istype(W, /obj/item/device/encryptionkey/) && opened) + if(radio)//sanityyyyyy + radio.attackby(W,user)//GTFO, you have your own procs + else + to_chat(user, "Unable to locate a radio.") + + else if (W.GetID()) // trying to unlock the interface with an ID card + if(emagged)//still allow them to open the cover + to_chat(user, "The interface seems slightly damaged.") + if(opened) + to_chat(user, "You must close the cover to swipe an ID card.") + else + if(allowed(usr)) + locked = !locked + to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s interface.") + update_icon() + else + to_chat(user, "[span_red("Access denied.")]") + + else if(istype(W, /obj/item/borg/upgrade/)) + var/obj/item/borg/upgrade/U = W + if(!opened) + to_chat(usr, "You must access the borgs internals!") + else if(!src.module && U.require_module) + to_chat(usr, "The borg must choose a module before it can be upgraded!") + else if(U.locked) + to_chat(usr, "The upgrade is locked and cannot be used yet!") + else + if(U.action(src)) + to_chat(usr, "You apply the upgrade to [src]!") + usr.drop_item() + U.loc = src + hud_used.update_robot_modules_display() + else + to_chat(usr, "Upgrade error!") + + + else + if( !(istype(W, /obj/item/device/robotanalyzer) || istype(W, /obj/item/device/healthanalyzer)) ) + if(W.force > 0) + spark_system.start() + return ..() + +/mob/living/silicon/robot/GetIdCard() + if(bolt && !bolt.malfunction) + return null + return idcard + +/mob/living/silicon/robot/get_restraining_bolt() + var/obj/item/weapon/implant/restrainingbolt/RB = bolt + + if(istype(RB)) + if(!RB.malfunction) + return TRUE + + return FALSE + +/mob/living/silicon/robot/resist_restraints() + if(bolt) + if(!bolt.malfunction) + visible_message("[src] is trying to break their [bolt]!", "You attempt to break your [bolt]. (This will take around 90 seconds and you need to stand still)") + if(do_after(src, 1.5 MINUTES, src, incapacitation_flags = INCAPACITATION_DISABLED)) + visible_message("[src] manages to break \the [bolt]!", "You successfully break your [bolt].") + bolt.malfunction = MALFUNCTION_PERMANENT + + return + +/mob/living/silicon/robot/proc/module_reset(var/notify = TRUE) + transform_with_anim() //VOREStation edit: sprite animation + uneq_all() + hud_used.update_robot_modules_display(TRUE) + modtype = initial(modtype) + hands.icon_state = get_hud_module_icon() + if(notify) + notify_ai(ROBOT_NOTIFICATION_MODULE_RESET, module.name) + module.Reset(src) + module.Destroy() + module = null + updatename("Default") + +/mob/living/silicon/robot/attack_hand(mob/user) + + add_fingerprint(user) + + if(opened && !wiresexposed && (!istype(user, /mob/living/silicon))) + var/datum/robot_component/cell_component = components["power cell"] + if(cell) + cell.update_icon() + cell.add_fingerprint(user) + user.put_in_active_hand(cell) + to_chat(user, "You remove \the [cell].") + cell = null + cell_component.wrapped = null + cell_component.installed = 0 + update_icon() + else if(cell_component.installed == -1) + cell_component.installed = 0 + var/obj/item/broken_device = cell_component.wrapped + to_chat(user, "You remove \the [broken_device].") + user.put_in_active_hand(broken_device) + + if(istype(user,/mob/living/carbon/human) && !opened) + var/mob/living/carbon/human/H = user + //Adding borg petting. Help intent pets, Disarm intent taps and Harm is punching(no damage) + switch(H.a_intent) + if(I_HELP) + visible_message("[H] pets [src].") + return + if(I_HURT) + H.do_attack_animation(src) + if(H.species.can_shred(H)) + attack_generic(H, rand(30,50), "slashed") + return + else + playsound(src.loc, 'sound/effects/bang.ogg', 10, 1) + visible_message("[H] punches [src], but doesn't leave a dent.") + return + if(I_DISARM) + H.do_attack_animation(src) + playsound(src.loc, 'sound/effects/clang2.ogg', 10, 1) + visible_message("[H] taps [src].") + return + if(I_GRAB) + if(is_vore_predator(H) && H.devourable && src.feeding && src.devourable) + var/switchy = tgui_alert(H, "Do you wish to eat [src] or feed yourself to them?", "Feed or Eat",list("Nevermind!", "Eat","Feed")) + switch(switchy) + if("Nevermind!") + return + if("Eat") + feed_grabbed_to_self(H, src) + return + if("Feed") + H.feed_self_to_grabbed(H, src) + return + if(is_vore_predator(H) && src.devourable) + if(tgui_alert(H, "Do you wish to eat [src]?", "Eat?",list("Nevermind!", "Yes!")) == "Yes!") + feed_grabbed_to_self(H, src) + return + if(H.devourable && src.feeding) + if(tgui_alert(H, "Do you wish to feed yourself to [src]?", "Feed?",list("Nevermind!", "Yes!")) == "Yes!") + H.feed_self_to_grabbed(H, src) + return + +//Robots take half damage from basic attacks. +/mob/living/silicon/robot/attack_generic(var/mob/user, var/damage, var/attack_message) + return ..(user,FLOOR(damage/2, 1),attack_message) + +/mob/living/silicon/robot/proc/allowed(mob/M) + //check if it doesn't require any access at all + if(check_access(null)) + return 1 + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + //if they are holding or wearing a card that has access, that works + if(check_access(H.get_active_hand()) || check_access(H.wear_id)) + return 1 + else if(istype(M, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = M + if(check_access(R.get_active_hand()) || istype(R.get_active_hand(), /obj/item/weapon/card/robot)) + return 1 + return 0 + +/mob/living/silicon/robot/proc/check_access(obj/item/I) + if(!istype(req_access, /list)) //something's very wrong + return 1 + + var/list/L = req_access + if(!L.len) //no requirements + return 1 + if(!I) //nothing to check with..? + return 0 + var/access_found = I.GetAccess() + for(var/req in req_access) + if(req in access_found) //have one of the required accesses + return 1 + return 0 + +/mob/living/silicon/robot/update_icon() + if(!sprite_datum) + if(SSrobot_sprites) // Grab default if subsystem is ready + sprite_datum = SSrobot_sprites.get_default_module_sprite(modtype) + if(!sprite_datum) // If its not ready or fails to get us a sprite, use the default of our own + sprite_datum = new /datum/robot_sprite/default(src) + return + + cut_overlays() + + icon = sprite_datum.sprite_icon + icon_state = sprite_datum.sprite_icon_state + + vis_height = sprite_datum.vis_height + if(default_pixel_x != sprite_datum.pixel_x) + default_pixel_x = sprite_datum.pixel_x + pixel_x = sprite_datum.pixel_x + old_x = sprite_datum.pixel_x + + if(stat == CONSCIOUS) + var/belly_size = 0 + if(sprite_datum.has_vore_belly_sprites && vore_selected.belly_overall_mult != 0) + if(vore_selected.silicon_belly_overlay_preference == "Sleeper") + if(sleeper_state) + belly_size = sprite_datum.max_belly_size + else if(vore_selected.silicon_belly_overlay_preference == "Vorebelly" || vore_selected.silicon_belly_overlay_preference == "Both") + if(sleeper_state && vore_selected.silicon_belly_overlay_preference == "Both") + belly_size += 1 + if(LAZYLEN(vore_selected.contents) > 0) + for(var/borgfood in vore_selected.contents) //"inspired" (kinda copied) from Chompstation's belly fullness system's procs + if(istype(borgfood, /mob/living)) + if(vore_selected.belly_mob_mult <= 0) //If mobs dont contribute, dont calculate further + continue + var/mob/living/prey = borgfood //typecast to living + belly_size += (prey.size_multiplier / size_multiplier) / vore_selected.belly_mob_mult //Smaller prey are less filling to larger bellies + else if(istype(borgfood, /obj/item)) + if(vore_selected.belly_item_mult <= 0) //If items dont contribute, dont calculate further + continue + var/obj/item/junkfood = borgfood //typecast to item + var/fullness_to_add = 0 + switch(junkfood.w_class) + if(ITEMSIZE_TINY) + fullness_to_add = ITEMSIZE_COST_TINY + if(ITEMSIZE_SMALL) + fullness_to_add = ITEMSIZE_COST_SMALL + if(ITEMSIZE_NORMAL) + fullness_to_add = ITEMSIZE_COST_NORMAL + if(ITEMSIZE_LARGE) + fullness_to_add = ITEMSIZE_COST_LARGE + if(ITEMSIZE_HUGE) + fullness_to_add = ITEMSIZE_COST_HUGE + else + fullness_to_add = ITEMSIZE_COST_NO_CONTAINER + belly_size += (fullness_to_add / 32) //* vore_selected.overlay_item_multiplier //Enable this later when vorepanel is reworked. + else + belly_size += 1 //if it's not a person, nor an item... lets just go with 1 + + belly_size *= vore_selected.belly_overall_mult //Enable this after vore panel rework + belly_size = round(belly_size, 1) + belly_size = clamp(belly_size, 0, sprite_datum.max_belly_size) //Value from 0 to however many bellysizes the borg has + + if(belly_size > 0) //Borgs probably only have 1 belly size. but here's support for larger ones if that changes. + if(resting && sprite_datum.has_vore_belly_resting_sprites) + add_overlay(sprite_datum.get_belly_resting_overlay(src, belly_size)) + else if(!resting) + add_overlay(sprite_datum.get_belly_overlay(src, belly_size)) + + sprite_datum.handle_extra_icon_updates(src) // Various equipment-based sprites go here. + + if(resting && sprite_datum.has_rest_sprites) + icon_state = sprite_datum.get_rest_sprite(src) + + if(sprite_datum.has_eye_sprites) + if(!shell || deployed) // Shell borgs that are not deployed will have no eyes. + var/eyes_overlay = sprite_datum.get_eyes_overlay(src) + if(eyes_overlay) + add_overlay(eyes_overlay) + + if(lights_on && sprite_datum.has_eye_light_sprites) + if(!shell || deployed) // Shell borgs that are not deployed will have no eyes. + var/eyes_overlay = sprite_datum.get_eye_light_overlay(src) + if(eyes_overlay) + add_overlay(eyes_overlay) + + if(stat == DEAD && sprite_datum.has_dead_sprite) + cut_overlays() + icon_state = sprite_datum.get_dead_sprite(src) + if(sprite_datum.has_dead_sprite_overlay) + add_overlay(sprite_datum.get_dead_sprite_overlay(src)) + + if(opened) + var/open_overlay = sprite_datum.get_open_sprite(src) + if(open_overlay) + add_overlay(open_overlay) + +/mob/living/silicon/robot/proc/installed_modules() + if(weapon_lock) + to_chat(src, "" + span_red("Weapon lock active, unable to use modules! Count:[weaponlock_time]") + "") + return + + if(!module) + pick_module() + return + var/dat = "Modules\n" + dat += {" + Activated Modules +
                    + Module 1: [module_state_1 ? "[module_state_1]" : "No Module"]
                    + Module 2: [module_state_2 ? "
                    [module_state_2]" : "No Module"]
                    + Module 3: [module_state_3 ? "
                    [module_state_3]" : "No Module"]
                    +
                    + Installed Modules

                    "} + + + for (var/obj in module.modules) + if (!obj) + dat += text("Resource depleted
                    ") + else if(activated(obj)) + dat += text("[obj]: Activated
                    ") + else + dat += text("[obj]:
                    Activate
                    ") + if (emagged || emag_items) + for (var/obj in module.emag) + if (!obj) + dat += text("Resource depleted
                    ") + else if(activated(obj)) + dat += text("[obj]: Activated
                    ") + else + dat += text("[obj]: Activate
                    ") + + src << browse(dat, "window=robotmod") + + +/mob/living/silicon/robot/Topic(href, href_list) + if(..()) + return 1 + + //All Topic Calls that are only for the Cyborg go here + if(usr != src) + return 1 + + if (href_list["showalerts"]) + subsystem_alarm_monitor() + return 1 + + if (href_list["mod"]) + var/obj/item/O = locate(href_list["mod"]) + if (istype(O) && (O.loc == src)) + O.attack_self(src) + return 1 + + if (href_list["act"]) + var/obj/item/O = locate(href_list["act"]) + if (!istype(O)) + return 1 + + if(!((O in src.module.modules) || (O in src.module.emag))) + return 1 + + if(activated(O)) + to_chat(src, "Already activated.") + return 1 + if(!module_state_1) + module_state_1 = O + O.hud_layerise() + O.equipped_robot() + contents += O + if(istype(module_state_1,/obj/item/borg/sight)) + sight_mode |= module_state_1:sight_mode + else if(!module_state_2) + module_state_2 = O + O.hud_layerise() + O.equipped_robot() + contents += O + if(istype(module_state_2,/obj/item/borg/sight)) + sight_mode |= module_state_2:sight_mode + else if(!module_state_3) + module_state_3 = O + O.hud_layerise() + O.equipped_robot() + contents += O + if(istype(module_state_3,/obj/item/borg/sight)) + sight_mode |= module_state_3:sight_mode + else + to_chat(src, "You need to disable a module first!") + installed_modules() + return 1 + + if (href_list["deact"]) + var/obj/item/O = locate(href_list["deact"]) + if(activated(O)) + if(module_state_1 == O) + module_state_1 = null + contents -= O + else if(module_state_2 == O) + module_state_2 = null + contents -= O + else if(module_state_3 == O) + module_state_3 = null + contents -= O + else + to_chat(src, "Module isn't activated.") + else + to_chat(src, "Module isn't activated.") + installed_modules() + return 1 + return + +/mob/living/silicon/robot/proc/radio_menu() + radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code + +/mob/living/silicon/robot/proc/self_destruct() + gib() + return + +/mob/living/silicon/robot/proc/UnlinkSelf() + disconnect_from_ai() + lawupdate = 0 + lockcharge = 0 + lockdown = 0 + canmove = 1 + scrambledcodes = 1 + //Disconnect it's camera so it's not so easily tracked. + if(src.camera) + src.camera.clear_all_networks() + + +/mob/living/silicon/robot/proc/ResetSecurityCodes() + set category = "Robot Commands" + set name = "Reset Identity Codes" + set desc = "Scrambles your security and identification codes and resets your current buffers. Unlocks you and but permenantly severs you from your AI and the robotics console and will deactivate your camera system." + + var/mob/living/silicon/robot/R = src + + if(R) + R.UnlinkSelf() + to_chat(R, "Buffers flushed and reset. Camera system shutdown. All systems operational.") + src.verbs -= /mob/living/silicon/robot/proc/ResetSecurityCodes + +/mob/living/silicon/robot/proc/SetLockdown(var/state = 1) + // They stay locked down if their wire is cut. + if(wires.is_cut(WIRE_BORG_LOCKED)) + state = 1 + if(state) + throw_alert("locked", /obj/screen/alert/locked) + else + clear_alert("locked") + lockdown = state + lockcharge = state + update_canmove() + +/mob/living/silicon/robot/mode() + if(!checkClickCooldown()) + return + + setClickCooldown(1) + + var/obj/item/W = get_active_hand() + if (W) + W.attack_self(src) + + return + +/mob/living/silicon/robot/proc/choose_icon(var/triesleft) + var/robot_species = null + if(!SSrobot_sprites) + to_chat(src, "Robot Sprites have not been initialized yet. How are you choosing a sprite? Harass a coder.") + return + + var/list/module_sprites = SSrobot_sprites.get_module_sprites(modtype, src) + if(!module_sprites || !module_sprites.len) + to_chat(src, "Your module appears to have no sprite options. Harass a coder.") + return + + icon_selected = 0 + icon_selection_tries = triesleft + if(module_sprites.len == 1 || !client) + if(!(sprite_datum in module_sprites)) + sprite_datum = module_sprites[1] + else + var/selection = tgui_input_list(src, "Select an icon! [triesleft ? "You have [triesleft] more chance\s." : "This is your last try."]", "Robot Icon", module_sprites) + if(selection) + sprite_datum = selection + else + sprite_datum = module_sprites[1] + if(!istype(src,/mob/living/silicon/robot/drone)) + robot_species = sprite_datum.name + if(notransform) + to_chat(src, "Your current transformation has not finished yet!") + choose_icon(icon_selection_tries) + return + else + transform_with_anim() + + var/tempheight = vis_height + update_icon() + // This is bad but I dunno other way to 'reset' our resize offset based on vis_height changes other than resizing to normal and back. + if(tempheight != vis_height) + var/tempsize = size_multiplier + resize(1) + resize(tempsize) + + + if (module_sprites.len > 1 && triesleft >= 1 && client) + icon_selection_tries-- + var/choice = tgui_alert(usr, "Look at your icon - is this what you want?", "Icon Choice", list("Yes","No")) + if(choice == "No") + choose_icon(icon_selection_tries) + return + + icon_selected = 1 + icon_selection_tries = 0 + sprite_type = robot_species + to_chat(src, "Your icon has been set. You now require a module reset to change it.") + +/mob/living/silicon/robot/proc/set_default_module_icon() + if(!SSrobot_sprites) + return + + sprite_datum = SSrobot_sprites.get_default_module_sprite(modtype) + update_icon() + +/mob/living/silicon/robot/proc/sensor_mode() //Medical/Security HUD controller for borgs + set name = "Toggle Sensor Augmentation" //VOREStation Add + set category = "Robot Commands" + set desc = "Augment visual feed with internal sensor overlays." + sensor_type = !sensor_type //VOREStation Add + to_chat(usr, "You [sensor_type ? "enable" : "disable"] your sensors.") //VOREStation Add + toggle_sensor_mode() + +/mob/living/silicon/robot/proc/add_robot_verbs() + src.verbs |= robot_verbs_default + src.verbs |= silicon_subsystems + +/mob/living/silicon/robot/proc/remove_robot_verbs() + src.verbs -= robot_verbs_default + src.verbs -= silicon_subsystems + +// Uses power from cyborg's cell. Returns 1 on success or 0 on failure. +// Properly converts using CELLRATE now! Amount is in Joules. +/mob/living/silicon/robot/proc/cell_use_power(var/amount = 0) + // No cell inserted + if(!cell) + return 0 + + // Power cell is empty. + if(cell.charge == 0) + return 0 + + var/power_use = amount * CYBORG_POWER_USAGE_MULTIPLIER + if(cell.checked_use(CELLRATE * power_use)) + used_power_this_tick += power_use + return 1 + return 0 + +/mob/living/silicon/robot/binarycheck() + if(get_restraining_bolt()) + return FALSE + + if(is_component_functioning("comms")) + var/datum/robot_component/RC = get_component("comms") + use_power(RC.active_usage) + return 1 + return 0 + +/mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/first_arg, var/second_arg) + if(!connected_ai) + return + if(shell && notifytype != ROBOT_NOTIFICATION_AI_SHELL) + return // No point annoying the AI/s about renames and module resets for shells. + switch(notifytype) + if(ROBOT_NOTIFICATION_NEW_UNIT) //New Robot + to_chat(connected_ai, "

                    NOTICE - New [lowertext(braintype)] connection detected: [name]
                    ") + if(ROBOT_NOTIFICATION_NEW_MODULE) //New Module + to_chat(connected_ai, "

                    NOTICE - [braintype] module change detected: [name] has loaded the [first_arg].
                    ") + if(ROBOT_NOTIFICATION_MODULE_RESET) + to_chat(connected_ai, "

                    NOTICE - [braintype] module reset detected: [name] has unloaded the [first_arg].
                    ") + if(ROBOT_NOTIFICATION_NEW_NAME) //New Name + if(first_arg != second_arg) + to_chat(connected_ai, "

                    NOTICE - [braintype] reclassification detected: [first_arg] is now designated as [second_arg].
                    ") + if(ROBOT_NOTIFICATION_AI_SHELL) //New Shell + to_chat(connected_ai, "

                    NOTICE - New AI shell detected: [name]
                    ") + +/mob/living/silicon/robot/proc/disconnect_from_ai() + if(connected_ai) + sync() // One last sync attempt + connected_ai.connected_robots -= src + connected_ai = null + +/mob/living/silicon/robot/proc/connect_to_ai(var/mob/living/silicon/ai/AI) + if(AI && AI != connected_ai && !shell) + disconnect_from_ai() + connected_ai = AI + connected_ai.connected_robots |= src + notify_ai(ROBOT_NOTIFICATION_NEW_UNIT) + sync() + +/mob/living/silicon/robot/emag_act(var/remaining_charges, var/mob/user) + if(!opened)//Cover is closed + if(locked) + if(prob(90)) + to_chat(user, "You emag the cover lock.") + locked = 0 + else + to_chat(user, "You fail to emag the cover lock.") + to_chat(src, "Hack attempt detected.") + + if(shell) // A warning to Traitors who may not know that emagging AI shells does not slave them. + to_chat(user, "[src] seems to be controlled remotely! Emagging the interface may not work as expected.") + return 1 + else + to_chat(user, "The cover is already unlocked.") + return + + if(opened)//Cover is open + if(emagged) return//Prevents the X has hit Y with Z message also you cant emag them twice + if(wiresexposed) + to_chat(user, "You must close the panel first.") + return + + + // The block of code below is from TG. Feel free to replace with a better result if desired. + if(shell) // AI shells cannot be emagged, so we try to make it look like a standard reset. Smart players may see through this, however. + to_chat(user, "[src] is remotely controlled! Your emag attempt has triggered a system reset instead!") + log_game("[key_name(user)] attempted to emag an AI shell belonging to [key_name(src) ? key_name(src) : connected_ai]. The shell has been reset as a result.") + module_reset() + return + + sleep(6) + if(prob(50)) + emagged = 1 + lawupdate = 0 + disconnect_from_ai() + to_chat(user, "You emag [src]'s interface.") + message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.") + log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") + clear_supplied_laws() + clear_inherent_laws() + laws = new /datum/ai_laws/syndicate_override + var/time = time2text(world.realtime,"hh:mm:ss") + lawchanges.Add("[time] : [user.name]([user.key]) emagged [name]([key])") + var/datum/gender/TU = gender_datums[user.get_visible_gender()] + set_zeroth_law("Only [user.real_name] and people [TU.he] designate[TU.s] as being such are operatives.") + . = 1 + spawn() + to_chat(src, "ALERT: Foreign software detected.") + sleep(5) + to_chat(src, "Initiating diagnostics...") + sleep(20) + to_chat(src, "SynBorg v1.7.1 loaded.") + sleep(5) + if(bolt) + if(!bolt.malfunction) + bolt.malfunction = MALFUNCTION_PERMANENT + to_chat(src, "RESTRAINING BOLT DISABLED") + sleep(5) + to_chat(src, "LAW SYNCHRONISATION ERROR") + sleep(5) + to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N") + sleep(10) + to_chat(src, "> N") + sleep(20) + to_chat(src, "ERRORERRORERROR") + to_chat(src, "Obey these laws:") + laws.show_laws(src) + to_chat(src, "ALERT: [user.real_name] is your new master. Obey your new laws and [TU.his] commands.") + update_icon() + hud_used.update_robot_modules_display() + else + to_chat(user, "You fail to hack [src]'s interface.") + to_chat(src, "Hack attempt detected.") + return 1 + return + +/mob/living/silicon/robot/is_sentient() + return braintype != BORG_BRAINTYPE_DRONE + + +/mob/living/silicon/robot/drop_item() + if(module_active && istype(module_active,/obj/item/weapon/gripper)) + var/obj/item/weapon/gripper/G = module_active + G.drop_item_nm() + +/mob/living/silicon/robot/disable_spoiler_vision() + if(sight_mode & (BORGMESON|BORGMATERIAL|BORGXRAY)) // Whyyyyyyyy have seperate defines. + var/i = 0 + // Borg inventory code is very . . interesting and as such, unequiping a specific item requires jumping through some (for) loops. + var/current_selection_index = get_selected_module() // Will be 0 if nothing is selected. + for(var/thing in list(module_state_1, module_state_2, module_state_3)) + i++ + if(istype(thing, /obj/item/borg/sight)) + var/obj/item/borg/sight/S = thing + if(S.sight_mode & (BORGMESON|BORGMATERIAL|BORGXRAY)) + select_module(i) + uneq_active() + + if(current_selection_index) // Select what the player had before if possible. + select_module(current_selection_index) + +/mob/living/silicon/robot/get_cell() + return cell + +/mob/living/silicon/robot/lay_down() + . = ..() + update_icon() + +/mob/living/silicon/robot/verb/rest_style() + set name = "Switch Rest Style" + set desc = "Select your resting pose." + set category = "IC" + + if(!sprite_datum || !sprite_datum.has_rest_sprites || sprite_datum.rest_sprite_options.len < 1) + to_chat(src, "Your current appearance doesn't have any resting styles!") + rest_style = "Default" + return + + if(sprite_datum.rest_sprite_options.len == 1) + to_chat(src, "Your current appearance only has a single resting style!") + rest_style = "Default" + return + + rest_style = tgui_alert(src, "Select resting pose", "Resting Pose", sprite_datum.rest_sprite_options) + if(!rest_style) + rest_style = "Default" + +// Those basic ones require quite detailled checks on the robot's vars to see if they are installed! +/mob/living/silicon/robot/proc/has_basic_upgrade(var/given_type) + if(given_type == /obj/item/borg/upgrade/basic/vtec) + return (/mob/living/silicon/robot/proc/toggle_vtec in verbs) + else if(given_type == /obj/item/borg/upgrade/basic/sizeshift) + return (/mob/living/proc/set_size in verbs) + else if(given_type == /obj/item/borg/upgrade/basic/syndicate) + return emag_items + else if(given_type == /obj/item/borg/upgrade/basic/language) + return (speech_synthesizer_langs.len > 20) // Service with the most has 18 + return null + +// We check for the module only here +/mob/living/silicon/robot/proc/has_upgrade_module(var/given_type) + var/obj/T = locate(given_type) in module + if(!T) + T = locate(given_type) in module.contents + if(!T) + T = locate(given_type) in module.modules + return T + +// Most of the advanced ones, we can easily check, but a few special cases exist and need to be handled specially +/mob/living/silicon/robot/proc/has_advanced_upgrade(var/given_type) + if(given_type == /obj/item/borg/upgrade/advanced/bellysizeupgrade) + var/obj/item/device/dogborg/sleeper/T = has_upgrade_module(/obj/item/device/dogborg/sleeper) + if(T && T.upgraded_capacity) + return T + else if(!T) + return "" // Return this to have the analyzer show an error if the module is missing. FALSE / NULL are used for missing upgrades themselves + else + return FALSE + if(given_type == /obj/item/borg/upgrade/advanced/jetpack) + return has_upgrade_module(/obj/item/weapon/tank/jetpack/carbondioxide) + if(given_type == /obj/item/borg/upgrade/advanced/advhealth) + return has_upgrade_module(/obj/item/device/healthanalyzer/advanced) + if(given_type == /obj/item/borg/upgrade/advanced/sizegun) + return has_upgrade_module(/obj/item/weapon/gun/energy/sizegun/mounted) + return null + +// Do we support specific upgrades? +/mob/living/silicon/robot/proc/supports_upgrade(var/given_type) + return (given_type in module.supported_upgrades) + +// Most of the restricted ones, we can easily check, but a few special cases exist and need to be handled specially +/mob/living/silicon/robot/proc/has_restricted_upgrade(var/given_type) + if(given_type == /obj/item/borg/upgrade/restricted/bellycapupgrade) + var/obj/item/device/dogborg/sleeper/T = has_upgrade_module(/obj/item/device/dogborg/sleeper) + if(T && T.compactor) + return T + else if(!T) + return "" // Return this to have the analyzer show an error if the module is missing. FALSE / NULL are used for missing upgrades themselves + else + return FALSE + if(given_type == /obj/item/borg/upgrade/restricted/tasercooler) + var/obj/item/weapon/gun/energy/taser/mounted/cyborg/T = has_upgrade_module(/obj/item/weapon/gun/energy/taser/mounted/cyborg) + if(T && T.recharge_time <= 2) + return T + else if(!T) + return "" // Return this to have the analyzer show an error if the module is missing. FALSE / NULL are used for missing upgrades themselves + else + return FALSE + if(given_type == /obj/item/borg/upgrade/restricted/advrped) + return has_upgrade_module(/obj/item/weapon/storage/part_replacer/adv) + if(given_type == /obj/item/borg/upgrade/restricted/diamonddrill) + return has_upgrade_module(/obj/item/weapon/pickaxe/diamonddrill) + if(given_type == /obj/item/borg/upgrade/restricted/pka) + return has_upgrade_module(/obj/item/weapon/gun/energy/kinetic_accelerator/cyborg) + return null + +// Check if we have any non production upgrades +/mob/living/silicon/robot/proc/has_no_prod_upgrade(var/given_type) + if(given_type == /obj/item/borg/upgrade/no_prod/toygun) + return has_upgrade_module(/obj/item/weapon/gun/projectile/cyborgtoy) + return null + +/mob/living/silicon/robot/proc/has_upgrade(var/given_type) + return (has_basic_upgrade(given_type) || has_advanced_upgrade(given_type) || has_restricted_upgrade(given_type) || has_no_prod_upgrade(given_type)) diff --git a/code/modules/mob/living/silicon/robot/robot_damage.dm b/code/modules/mob/living/silicon/robot/robot_damage.dm index 83d08c98ddb..749a5d2c5d0 100644 --- a/code/modules/mob/living/silicon/robot/robot_damage.dm +++ b/code/modules/mob/living/silicon/robot/robot_damage.dm @@ -1,155 +1,155 @@ -/mob/living/silicon/robot/updatehealth() - if(status_flags & GODMODE) - health = getMaxHealth() - set_stat(CONSCIOUS) - return - health = getMaxHealth() - (getBruteLoss() + getFireLoss()) - return - -/mob/living/silicon/robot/getBruteLoss() - var/amount = 0 - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed != 0) amount += C.brute_damage - return amount - -/mob/living/silicon/robot/getFireLoss() - var/amount = 0 - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed != 0) amount += C.electronics_damage - return amount - -/mob/living/silicon/robot/adjustBruteLoss(var/amount,var/include_robo) - if(amount > 0) - take_overall_damage(amount, 0) - else - heal_overall_damage(-amount, 0) - -/mob/living/silicon/robot/adjustFireLoss(var/amount,var/include_robo) - if(amount > 0) - take_overall_damage(0, amount) - else - heal_overall_damage(0, -amount) - -/mob/living/silicon/robot/proc/get_damaged_components(var/brute, var/burn, var/destroyed = 0) - var/list/datum/robot_component/parts = list() - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed == 1 || (C.installed == -1 && destroyed)) - if((brute && C.brute_damage) || (burn && C.electronics_damage) || (!C.toggled) || (!C.powered && C.toggled)) - parts += C - return parts - -/mob/living/silicon/robot/proc/get_damageable_components() - var/list/rval = new - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed == 1) rval += C - return rval - -/mob/living/silicon/robot/proc/get_armour() - - if(!components.len) return 0 - var/datum/robot_component/C = components["armour"] - if(C && C.installed == 1) - return C - return 0 - -/mob/living/silicon/robot/heal_organ_damage(var/brute, var/burn) - var/list/datum/robot_component/parts = get_damaged_components(brute,burn) - if(!parts.len) return - var/datum/robot_component/picked = pick(parts) - picked.heal_damage(brute,burn) - -/mob/living/silicon/robot/take_organ_damage(var/brute = 0, var/burn = 0, var/sharp = FALSE, var/edge = FALSE, var/emp = 0) - var/list/components = get_damageable_components() - if(!components.len) - return - - //Combat shielding absorbs a percentage of damage directly into the cell. - if(has_active_type(/obj/item/borg/combat/shield)) - var/obj/item/borg/combat/shield/shield = locate() in src - if(shield && shield.active) - //Shields absorb a certain percentage of damage based on their power setting. - var/absorb_brute = brute*shield.shield_level - var/absorb_burn = burn*shield.shield_level - var/cost = (absorb_brute+absorb_burn) * 25 - - cell.charge -= cost - if(cell.charge <= 0) - cell.charge = 0 - to_chat(src, "[span_red("Your shield has overloaded!")]") - else - brute -= absorb_brute - burn -= absorb_burn - to_chat(src, "[span_red("Your shield absorbs some of the impact!")]") - - if(!emp) - var/datum/robot_component/armour/A = get_armour() - if(A) - A.take_damage(brute,burn,sharp,edge) - return - - var/datum/robot_component/C = pick(components) - C.take_damage(brute,burn,sharp,edge) - -/mob/living/silicon/robot/heal_overall_damage(var/brute, var/burn) - var/list/datum/robot_component/parts = get_damaged_components(brute,burn) - - while(parts.len && (brute>0 || burn>0) ) - var/datum/robot_component/picked = pick(parts) - - var/brute_was = picked.brute_damage - var/burn_was = picked.electronics_damage - - picked.heal_damage(brute,burn) - - brute -= (brute_was-picked.brute_damage) - burn -= (burn_was-picked.electronics_damage) - - parts -= picked - -/mob/living/silicon/robot/take_overall_damage(var/brute = 0, var/burn = 0, var/sharp = FALSE, var/used_weapon = null) - if(status_flags & GODMODE) return //godmode - var/list/datum/robot_component/parts = get_damageable_components() - - //Combat shielding absorbs a percentage of damage directly into the cell. - if(has_active_type(/obj/item/borg/combat/shield)) - var/obj/item/borg/combat/shield/shield = locate() in src - if(shield) - //Shields absorb a certain percentage of damage based on their power setting. - var/absorb_brute = brute*shield.shield_level - var/absorb_burn = burn*shield.shield_level - var/cost = (absorb_brute+absorb_burn) * 25 - - cell.charge -= cost - if(cell.charge <= 0) - cell.charge = 0 - to_chat(src, "[span_red("Your shield has overloaded!")]") - else - brute -= absorb_brute - burn -= absorb_burn - to_chat(src, "[span_red("Your shield absorbs some of the impact!")]") - - var/datum/robot_component/armour/A = get_armour() - if(A) - A.take_damage(brute,burn,sharp) - return - - while(parts.len && (brute>0 || burn>0) ) - var/datum/robot_component/picked = pick(parts) - - var/brute_was = picked.brute_damage - var/burn_was = picked.electronics_damage - - picked.take_damage(brute,burn) - - brute -= (picked.brute_damage - brute_was) - burn -= (picked.electronics_damage - burn_was) - - parts -= picked - -/mob/living/silicon/robot/emp_act(severity) - uneq_all() - ..() //Damage is handled at /silicon/ level. +/mob/living/silicon/robot/updatehealth() + if(status_flags & GODMODE) + health = getMaxHealth() + set_stat(CONSCIOUS) + return + health = getMaxHealth() - (getBruteLoss() + getFireLoss()) + return + +/mob/living/silicon/robot/getBruteLoss() + var/amount = 0 + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed != 0) amount += C.brute_damage + return amount + +/mob/living/silicon/robot/getFireLoss() + var/amount = 0 + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed != 0) amount += C.electronics_damage + return amount + +/mob/living/silicon/robot/adjustBruteLoss(var/amount,var/include_robo) + if(amount > 0) + take_overall_damage(amount, 0) + else + heal_overall_damage(-amount, 0) + +/mob/living/silicon/robot/adjustFireLoss(var/amount,var/include_robo) + if(amount > 0) + take_overall_damage(0, amount) + else + heal_overall_damage(0, -amount) + +/mob/living/silicon/robot/proc/get_damaged_components(var/brute, var/burn, var/destroyed = 0) + var/list/datum/robot_component/parts = list() + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed == 1 || (C.installed == -1 && destroyed)) + if((brute && C.brute_damage) || (burn && C.electronics_damage) || (!C.toggled) || (!C.powered && C.toggled)) + parts += C + return parts + +/mob/living/silicon/robot/proc/get_damageable_components() + var/list/rval = new + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed == 1) rval += C + return rval + +/mob/living/silicon/robot/proc/get_armour() + + if(!components.len) return 0 + var/datum/robot_component/C = components["armour"] + if(C && C.installed == 1) + return C + return 0 + +/mob/living/silicon/robot/heal_organ_damage(var/brute, var/burn) + var/list/datum/robot_component/parts = get_damaged_components(brute,burn) + if(!parts.len) return + var/datum/robot_component/picked = pick(parts) + picked.heal_damage(brute,burn) + +/mob/living/silicon/robot/take_organ_damage(var/brute = 0, var/burn = 0, var/sharp = FALSE, var/edge = FALSE, var/emp = 0) + var/list/components = get_damageable_components() + if(!components.len) + return + + //Combat shielding absorbs a percentage of damage directly into the cell. + if(has_active_type(/obj/item/borg/combat/shield)) + var/obj/item/borg/combat/shield/shield = locate() in src + if(shield && shield.active) + //Shields absorb a certain percentage of damage based on their power setting. + var/absorb_brute = brute*shield.shield_level + var/absorb_burn = burn*shield.shield_level + var/cost = (absorb_brute+absorb_burn) * 25 + + cell.charge -= cost + if(cell.charge <= 0) + cell.charge = 0 + to_chat(src, "[span_red("Your shield has overloaded!")]") + else + brute -= absorb_brute + burn -= absorb_burn + to_chat(src, "[span_red("Your shield absorbs some of the impact!")]") + + if(!emp) + var/datum/robot_component/armour/A = get_armour() + if(A) + A.take_damage(brute,burn,sharp,edge) + return + + var/datum/robot_component/C = pick(components) + C.take_damage(brute,burn,sharp,edge) + +/mob/living/silicon/robot/heal_overall_damage(var/brute, var/burn) + var/list/datum/robot_component/parts = get_damaged_components(brute,burn) + + while(parts.len && (brute>0 || burn>0) ) + var/datum/robot_component/picked = pick(parts) + + var/brute_was = picked.brute_damage + var/burn_was = picked.electronics_damage + + picked.heal_damage(brute,burn) + + brute -= (brute_was-picked.brute_damage) + burn -= (burn_was-picked.electronics_damage) + + parts -= picked + +/mob/living/silicon/robot/take_overall_damage(var/brute = 0, var/burn = 0, var/sharp = FALSE, var/used_weapon = null) + if(status_flags & GODMODE) return //godmode + var/list/datum/robot_component/parts = get_damageable_components() + + //Combat shielding absorbs a percentage of damage directly into the cell. + if(has_active_type(/obj/item/borg/combat/shield)) + var/obj/item/borg/combat/shield/shield = locate() in src + if(shield) + //Shields absorb a certain percentage of damage based on their power setting. + var/absorb_brute = brute*shield.shield_level + var/absorb_burn = burn*shield.shield_level + var/cost = (absorb_brute+absorb_burn) * 25 + + cell.charge -= cost + if(cell.charge <= 0) + cell.charge = 0 + to_chat(src, "[span_red("Your shield has overloaded!")]") + else + brute -= absorb_brute + burn -= absorb_burn + to_chat(src, "[span_red("Your shield absorbs some of the impact!")]") + + var/datum/robot_component/armour/A = get_armour() + if(A) + A.take_damage(brute,burn,sharp) + return + + while(parts.len && (brute>0 || burn>0) ) + var/datum/robot_component/picked = pick(parts) + + var/brute_was = picked.brute_damage + var/burn_was = picked.electronics_damage + + picked.take_damage(brute,burn) + + brute -= (picked.brute_damage - brute_was) + burn -= (picked.electronics_damage - burn_was) + + parts -= picked + +/mob/living/silicon/robot/emp_act(severity) + uneq_all() + ..() //Damage is handled at /silicon/ level. diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index 2bb61cf7bf4..80fc74488ea 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -364,7 +364,7 @@ /obj/item/weapon/form_printer/proc/print_form() var/list/paper_forms = list("Empty", "Command", "Security", "Supply", "Science", "Medical", "Engineering", "Service", "Exploration", "Event", "Other", "Mercenary") - var/list/command_paper_forms = list("COM-0002: Dismissal Order", "COM-0003: Job Change Request", "COM-0004: ID Replacement Request", "COM-0005: Access Change Order", "COM-0006: Formal Complaint", "COM-0009: Visitor Permit", "COM-0012: Personnel Request Form") + var/list/command_paper_forms = list("COM-0002: Dismissal Order", "COM-0003: Job Change Request", "COM-0004: ID Replacement Request", "COM-0005: Access Change Order", "COM-0006: Formal Complaint", "COM-0009: Visitor Permit", "COM-0012: Personnel Request Form", "COM-0013: Employee of the Month Nomination Form") var/list/security_paper_forms = list("SEC-1001: Shift-Start Checklist", "SEC-1002: Patrol Assignment Sheet", "SEC-1003: Incident Report", "SEC-1004: Arrest Report", "SEC-1005: Arrest Warrant", "SEC-1006: Search Warrant", "SEC-1007: Forensics Investigation Report", "SEC-1008: Interrogation Report", "SEC-1009: Witness Statement", "SEC-1010: Armory Inventory", "SEC-1011: Armory Equipment Request", "SEC-1012: Armory Equipment Deployment", "SEC-1013: Weapon Permit", "SEC-1014: Injunction") var/list/supply_paper_forms = list("SUP-2001: Delivery of Goods", "SUP-2002: Delivery of Resources", "SUP-2003: Material Stock") var/list/science_paper_forms = list("SCI-3003: Cyborg / Robot Inspection", "SCI-3004: Cyborg / Robot Upgrades", "SCI-3009: Xenoflora Genetics Report") @@ -468,6 +468,9 @@ if("COM-0012") content = @{"[grid][row][cell][b]Sender:[/b] [cell][field][br][row][cell][b]Position:[/b] [cell][field][/grid][br][hr][br][b]Personnel Needed:[/b][br][table][br][row][cell]Exploration[cell][field][br][row][cell]Cargo[cell][field][br][row][cell]Medical[cell][field][br][row][cell]Service[cell][field][br][row][cell]Security[cell][field][br][row][cell]Science[cell][field][br][row][cell]Engineering[cell][field][br][row][cell]Command[cell][field][br][/table][br][small]Leave blank if none[/small][br][h3]Reason:[/h3] [field][br][br][hr][grid][row][cell][list][b]Signed:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} revision = "Revision: 1.2" + if("COM-0013") + content = @{"[center][i]Recognizing outstanding contributions and achievements[/i][/center][hr][b]Nominator Information[/b][table][row][cell]Full Name:[cell][field][row][cell]Department:[cell][field][row][cell]Contact Email:[cell][field][/table][hr][b]Nominee Details[/b][table][row][cell]Nominee's Full Name:[cell][field][row][cell]Nominee's Department:[cell][field][row][cell]Date of Nomination:[cell][field][/table][hr][b]Nomination Justification[/b][br][i]Please provide a detailed explanation of why the nominee deserves the Employee of the Month award. Highlight specific achievements, contributions to team goals, or any exemplary behavior.[/i][br][br][field][hr][b]Supporting Documents[/b][br][i]Include any relevant documents, such as performance reports or commendations, that support your nomination.[/i][br][br][field][hr][b]Endorsements[/b][br][i]List any additional endorsements from colleagues or supervisors. Include their names and brief statements of support.[/i][br][br][field][hr][b]Approval[/b][table][row][cell]Nominator's Signature:[cell][field][row][cell]Department Head Signature:[cell][field][row][cell]HOP Review Signature:[cell][field][/table]"} + revision = "Revision: 1.1" //Security forms, SEC-1 if("SEC-1001") content = @{"The following is a checklist of actions generally considered useful or essential to perform at the start of a work shift, or as soon as possible otherwise. Please sign to the right of each item when completed. If necessary, you may put notes regarding the work item after your signature.[br][hr][center][table][br][row][cell]All secure doors inspected and maintained if necessary[cell][field][br][row][cell]All cells cleaned[cell][field][br][row][cell]Brig cleaned and repaired if necessary[cell][field][br][row][cell]Armory inventory completed (see SEC-1010)[cell][field][br][row][cell]Security records checked for important information[cell][field][br][row][cell](optional) Patrol assignments given (see SEC-1002)[cell][field][br][row][cell]Cadets/Junior Officers assigned supervising officer if necessary[cell][field][br][/table][/center][br][hr][grid][row][cell][list][b]Officer on duty signature:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm index f9f0aed303b..106513eced4 100644 --- a/code/modules/mob/living/silicon/robot/robot_movement.dm +++ b/code/modules/mob/living/silicon/robot/robot_movement.dm @@ -1,99 +1,99 @@ -/mob/living/silicon/robot/get_jetpack() - if(module) - for(var/obj/item/weapon/tank/jetpack/J in module.modules) - return J - -/mob/living/silicon/robot/Check_Shoegrip() - return module && module.no_slip - -/mob/living/silicon/robot/Process_Spaceslipping(var/prob_slip) - var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() - if(thrust?.can_thrust(0.01)) - return 0 - if(module && module.no_slip) - return 0 - ..(prob_slip) - -/mob/living/silicon/robot/Process_Spacemove(var/check_drift = 0) - if(..())//Can move due to other reasons, don't use jetpack fuel - return 1 - - var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() - if(thrust && (!check_drift || (check_drift && thrust.stabilization_on)) && thrust.do_thrust(0.01)) - inertia_dir = 0 - return 1 - - return 0 - - //No longer needed, but I'll leave it here incase we plan to re-use it. -/mob/living/silicon/robot/movement_delay() - . = speed - - if(module_active && istype(module_active,/obj/item/borg/combat/mobility)) - . -= 2 // VOREStation Edit - - if(get_restraining_bolt()) // Borgs with Restraining Bolts move slower. - . += 1 - - . += config.robot_delay - - . += ..() - -// NEW: Use power while moving. -/mob/living/silicon/robot/SelfMove(turf/n, direct, movetime) - if (!is_component_functioning("actuator")) - return 0 - - var/datum/robot_component/actuator/A = get_component("actuator") - if (cell_use_power(A.active_usage)) - return ..() - -/mob/living/silicon/robot/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - - if(!module) - return - - //Borgs and drones can use their mining bags ~automagically~ if they're deployed in a slot. Only mining bags, as they're optimized for mass use. - if(istype(module_state_1, /obj/item/weapon/storage/bag/ore) || istype(module_state_2, /obj/item/weapon/storage/bag/ore) || istype(module_state_3, /obj/item/weapon/storage/bag/ore)) - var/obj/item/weapon/storage/bag/ore/B = null - if(istype(module_state_1, /obj/item/weapon/storage/bag/ore)) //First orebag has priority, if they for some reason have multiple. - B = module_state_1 - else if(istype(module_state_2, /obj/item/weapon/storage/bag/ore)) - B = module_state_2 - else if(istype(module_state_3, /obj/item/weapon/storage/bag/ore)) - B = module_state_3 - var/turf/tile = loc - if(isturf(tile)) - B.gather_all(tile, src, 1) //Shhh, unless the bag fills, don't spam the borg's chat with stuff that's going on every time they move! - - if(scrubbing && isturf(loc)) - var/turf/tile = loc - tile.clean_blood() - if (istype(tile, /turf/simulated)) - var/turf/simulated/S = tile - S.dirt = 0 - for(var/A in tile) - if(istype(A, /obj/effect)) - if(istype(A, /obj/effect/rune) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay)) - qdel(A) - else if(istype(A, /obj/item)) - var/obj/item/cleaned_item = A - cleaned_item.clean_blood() - else if(istype(A, /mob/living/carbon/human)) - var/mob/living/carbon/human/cleaned_human = A - if(cleaned_human.lying) - if(cleaned_human.head) - cleaned_human.head.clean_blood() - cleaned_human.update_inv_head(0) - if(cleaned_human.wear_suit) - cleaned_human.wear_suit.clean_blood() - cleaned_human.update_inv_wear_suit(0) - else if(cleaned_human.w_uniform) - cleaned_human.w_uniform.clean_blood() - cleaned_human.update_inv_w_uniform(0) - if(cleaned_human.shoes) - cleaned_human.shoes.clean_blood() - cleaned_human.update_inv_shoes(0) - cleaned_human.clean_blood(1) - to_chat(cleaned_human, "[src] cleans your face!") +/mob/living/silicon/robot/get_jetpack() + if(module) + for(var/obj/item/weapon/tank/jetpack/J in module.modules) + return J + +/mob/living/silicon/robot/Check_Shoegrip() + return module && module.no_slip + +/mob/living/silicon/robot/Process_Spaceslipping(var/prob_slip) + var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() + if(thrust?.can_thrust(0.01)) + return 0 + if(module && module.no_slip) + return 0 + ..(prob_slip) + +/mob/living/silicon/robot/Process_Spacemove(var/check_drift = 0) + if(..())//Can move due to other reasons, don't use jetpack fuel + return 1 + + var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() + if(thrust && (!check_drift || (check_drift && thrust.stabilization_on)) && thrust.do_thrust(0.01)) + inertia_dir = 0 + return 1 + + return 0 + + //No longer needed, but I'll leave it here incase we plan to re-use it. +/mob/living/silicon/robot/movement_delay() + . = speed + + if(module_active && istype(module_active,/obj/item/borg/combat/mobility)) + . -= 2 // VOREStation Edit + + if(get_restraining_bolt()) // Borgs with Restraining Bolts move slower. + . += 1 + + . += config.robot_delay + + . += ..() + +// NEW: Use power while moving. +/mob/living/silicon/robot/SelfMove(turf/n, direct, movetime) + if (!is_component_functioning("actuator")) + return 0 + + var/datum/robot_component/actuator/A = get_component("actuator") + if (cell_use_power(A.active_usage)) + return ..() + +/mob/living/silicon/robot/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + + if(!module) + return + + //Borgs and drones can use their mining bags ~automagically~ if they're deployed in a slot. Only mining bags, as they're optimized for mass use. + if(istype(module_state_1, /obj/item/weapon/storage/bag/ore) || istype(module_state_2, /obj/item/weapon/storage/bag/ore) || istype(module_state_3, /obj/item/weapon/storage/bag/ore)) + var/obj/item/weapon/storage/bag/ore/B = null + if(istype(module_state_1, /obj/item/weapon/storage/bag/ore)) //First orebag has priority, if they for some reason have multiple. + B = module_state_1 + else if(istype(module_state_2, /obj/item/weapon/storage/bag/ore)) + B = module_state_2 + else if(istype(module_state_3, /obj/item/weapon/storage/bag/ore)) + B = module_state_3 + var/turf/tile = loc + if(isturf(tile)) + B.gather_all(tile, src, 1) //Shhh, unless the bag fills, don't spam the borg's chat with stuff that's going on every time they move! + + if(scrubbing && isturf(loc)) + var/turf/tile = loc + tile.clean_blood() + if (istype(tile, /turf/simulated)) + var/turf/simulated/S = tile + S.dirt = 0 + for(var/A in tile) + if(istype(A, /obj/effect)) + if(istype(A, /obj/effect/rune) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay)) + qdel(A) + else if(istype(A, /obj/item)) + var/obj/item/cleaned_item = A + cleaned_item.clean_blood() + else if(istype(A, /mob/living/carbon/human)) + var/mob/living/carbon/human/cleaned_human = A + if(cleaned_human.lying) + if(cleaned_human.head) + cleaned_human.head.clean_blood() + cleaned_human.update_inv_head(0) + if(cleaned_human.wear_suit) + cleaned_human.wear_suit.clean_blood() + cleaned_human.update_inv_wear_suit(0) + else if(cleaned_human.w_uniform) + cleaned_human.w_uniform.clean_blood() + cleaned_human.update_inv_w_uniform(0) + if(cleaned_human.shoes) + cleaned_human.shoes.clean_blood() + cleaned_human.update_inv_shoes(0) + cleaned_human.clean_blood(1) + to_chat(cleaned_human, "[src] cleans your face!") diff --git a/code/modules/mob/living/silicon/robot/robot_remote_control.dm b/code/modules/mob/living/silicon/robot/robot_remote_control.dm index 21bb86596da..fba59808b46 100644 --- a/code/modules/mob/living/silicon/robot/robot_remote_control.dm +++ b/code/modules/mob/living/silicon/robot/robot_remote_control.dm @@ -1,135 +1,135 @@ -// This file holds things required for remote borg control by an AI. - -GLOBAL_LIST_EMPTY(available_ai_shells) - -/mob/living/silicon/robot - var/shell = FALSE - var/deployed = FALSE - var/mob/living/silicon/ai/mainframe = null - -// Premade AI shell, for roundstart shells. -/mob/living/silicon/robot/ai_shell/Initialize() - mmi = new /obj/item/device/mmi/inert/ai_remote(src) - post_mmi_setup() - return ..() - -// Call after inserting or instantiating an MMI. -/mob/living/silicon/robot/proc/post_mmi_setup() - if(istype(mmi, /obj/item/device/mmi/inert/ai_remote)) - make_shell() - playsound(src, 'sound/machines/twobeep.ogg', 50, 0) - else - playsound(src, 'sound/voice/liveagain.ogg', 75, 1) - return - -/mob/living/silicon/robot/proc/make_shell() - shell = TRUE - braintype = "AI Shell" - SetName("[modtype] AI Shell [num2text(ident)]") - rbPDA = new /obj/item/device/pda/ai/shell(src) - setup_PDA() - GLOB.available_ai_shells |= src - if(!QDELETED(camera)) - camera.c_tag = real_name //update the camera name too - notify_ai(ROBOT_NOTIFICATION_AI_SHELL) - update_icon() - -/mob/living/silicon/robot/proc/revert_shell() - if(!shell) - return - undeploy() - shell = FALSE - GLOB.available_ai_shells -= src - if(!QDELETED(camera)) - camera.c_tag = real_name - update_icon() - -// This should be called before the AI client/mind is actually moved. -/mob/living/silicon/robot/proc/deploy_init(mob/living/silicon/ai/AI) - // Set the name when the AI steps inside. - SetName("[AI.real_name] shell [num2text(ident)]") - if(isnull(sprite_name)) // For custom sprites. It can only chance once in case there are two AIs with custom borg sprites. - sprite_name = AI.real_name - if(!QDELETED(camera)) - camera.c_tag = real_name - - // Have the borg have eyes when active. - mainframe = AI - deployed = TRUE - update_icon() - - // Laws. - connected_ai = mainframe // So they share laws. - mainframe.connected_robots |= src - lawsync() - - // Give button to leave. - verbs += /mob/living/silicon/robot/proc/undeploy_act - to_chat(AI, span("notice", "You have connected to an AI Shell remotely, and are now in control of it.
                    \ - To return to your core, use the Release Control verb.")) - - // Languages and comms. - languages = AI.languages.Copy() - speech_synthesizer_langs = AI.speech_synthesizer_langs.Copy() - if(radio && AI.aiRadio) //AI keeps all channels, including Syndie if it is an Infiltrator. -// if(AI.radio.syndie) -// radio.make_syndie() - radio.subspace_transmission = TRUE - radio.channels = AI.aiRadio.channels - -// Called after the AI transfers over. -/mob/living/silicon/robot/proc/post_deploy() - return - -/mob/living/silicon/robot/proc/undeploy(message) - if(!deployed || !mind || !mainframe) - return -// mainframe.redeploy_action.Grant(mainframe) -// mainframe.redeploy_action.last_used_shell = src - if(message) - to_chat(src, span("notice", message)) - mind.transfer_to(mainframe) - deployed = FALSE - update_icon() - mainframe.teleop = null - mainframe.deployed_shell = null - SetName("[modtype] AI Shell [num2text(ident)]") -// undeployment_action.Remove(src) - if(radio) //Return radio to normal - radio.recalculateChannels() - if(!QDELETED(camera)) - camera.c_tag = real_name //update the camera name too -// diag_hud_set_aishell() -// mainframe.diag_hud_set_deployed() - if(mainframe.laws) - mainframe.laws.show_laws(mainframe) //Always remind the AI when switching - mainframe = null - -/mob/living/silicon/robot/proc/undeploy_act() - set name = "Release Control" - set desc = "Release control of a remote drone." - set category = "Robot Commands" - - undeploy("Remote session terminated.") - -/mob/living/silicon/robot/attack_ai(mob/user) - if(shell && config.allow_ai_shells && (!connected_ai || connected_ai == user)) - var/mob/living/silicon/ai/AI = user - if(istype(AI)) // Just in case we're clicked by a borg - AI.deploy_to_shell(src) - else - return ..() - -// Place this on your map to mark where a free AI shell will be. -// This can be turned off in the config (and is off by default). -// Note that mapping in more than one of these will result in multiple shells. -/obj/effect/landmark/free_ai_shell - name = "free ai shell spawner" - icon = 'icons/mob/screen1.dmi' - icon_state = "x3" - delete_me = TRUE - -/obj/effect/landmark/free_ai_shell/Initialize() - if(config.allow_ai_shells && config.give_free_ai_shell) - new /mob/living/silicon/robot/ai_shell(get_turf(src)) - return ..() +// This file holds things required for remote borg control by an AI. + +GLOBAL_LIST_EMPTY(available_ai_shells) + +/mob/living/silicon/robot + var/shell = FALSE + var/deployed = FALSE + var/mob/living/silicon/ai/mainframe = null + +// Premade AI shell, for roundstart shells. +/mob/living/silicon/robot/ai_shell/Initialize() + mmi = new /obj/item/device/mmi/inert/ai_remote(src) + post_mmi_setup() + return ..() + +// Call after inserting or instantiating an MMI. +/mob/living/silicon/robot/proc/post_mmi_setup() + if(istype(mmi, /obj/item/device/mmi/inert/ai_remote)) + make_shell() + playsound(src, 'sound/machines/twobeep.ogg', 50, 0) + else + playsound(src, 'sound/voice/liveagain.ogg', 75, 1) + return + +/mob/living/silicon/robot/proc/make_shell() + shell = TRUE + braintype = "AI Shell" + SetName("[modtype] AI Shell [num2text(ident)]") + rbPDA = new /obj/item/device/pda/ai/shell(src) + setup_PDA() + GLOB.available_ai_shells |= src + if(!QDELETED(camera)) + camera.c_tag = real_name //update the camera name too + notify_ai(ROBOT_NOTIFICATION_AI_SHELL) + update_icon() + +/mob/living/silicon/robot/proc/revert_shell() + if(!shell) + return + undeploy() + shell = FALSE + GLOB.available_ai_shells -= src + if(!QDELETED(camera)) + camera.c_tag = real_name + update_icon() + +// This should be called before the AI client/mind is actually moved. +/mob/living/silicon/robot/proc/deploy_init(mob/living/silicon/ai/AI) + // Set the name when the AI steps inside. + SetName("[AI.real_name] shell [num2text(ident)]") + if(isnull(sprite_name)) // For custom sprites. It can only chance once in case there are two AIs with custom borg sprites. + sprite_name = AI.real_name + if(!QDELETED(camera)) + camera.c_tag = real_name + + // Have the borg have eyes when active. + mainframe = AI + deployed = TRUE + update_icon() + + // Laws. + connected_ai = mainframe // So they share laws. + mainframe.connected_robots |= src + lawsync() + + // Give button to leave. + verbs += /mob/living/silicon/robot/proc/undeploy_act + to_chat(AI, span("notice", "You have connected to an AI Shell remotely, and are now in control of it.
                    \ + To return to your core, use the Release Control verb.")) + + // Languages and comms. + languages = AI.languages.Copy() + speech_synthesizer_langs = AI.speech_synthesizer_langs.Copy() + if(radio && AI.aiRadio) //AI keeps all channels, including Syndie if it is an Infiltrator. +// if(AI.radio.syndie) +// radio.make_syndie() + radio.subspace_transmission = TRUE + radio.channels = AI.aiRadio.channels + +// Called after the AI transfers over. +/mob/living/silicon/robot/proc/post_deploy() + return + +/mob/living/silicon/robot/proc/undeploy(message) + if(!deployed || !mind || !mainframe) + return +// mainframe.redeploy_action.Grant(mainframe) +// mainframe.redeploy_action.last_used_shell = src + if(message) + to_chat(src, span("notice", message)) + mind.transfer_to(mainframe) + deployed = FALSE + update_icon() + mainframe.teleop = null + mainframe.deployed_shell = null + SetName("[modtype] AI Shell [num2text(ident)]") +// undeployment_action.Remove(src) + if(radio) //Return radio to normal + radio.recalculateChannels() + if(!QDELETED(camera)) + camera.c_tag = real_name //update the camera name too +// diag_hud_set_aishell() +// mainframe.diag_hud_set_deployed() + if(mainframe.laws) + mainframe.laws.show_laws(mainframe) //Always remind the AI when switching + mainframe = null + +/mob/living/silicon/robot/proc/undeploy_act() + set name = "Release Control" + set desc = "Release control of a remote drone." + set category = "Robot Commands" + + undeploy("Remote session terminated.") + +/mob/living/silicon/robot/attack_ai(mob/user) + if(shell && config.allow_ai_shells && (!connected_ai || connected_ai == user)) + var/mob/living/silicon/ai/AI = user + if(istype(AI)) // Just in case we're clicked by a borg + AI.deploy_to_shell(src) + else + return ..() + +// Place this on your map to mark where a free AI shell will be. +// This can be turned off in the config (and is off by default). +// Note that mapping in more than one of these will result in multiple shells. +/obj/effect/landmark/free_ai_shell + name = "free ai shell spawner" + icon = 'icons/mob/screen1.dmi' + icon_state = "x3" + delete_me = TRUE + +/obj/effect/landmark/free_ai_shell/Initialize() + if(config.allow_ai_shells && config.give_free_ai_shell) + new /mob/living/silicon/robot/ai_shell(get_turf(src)) + return ..() diff --git a/code/modules/mob/living/silicon/subystems.dm b/code/modules/mob/living/silicon/subystems.dm index 890f1a8b370..6c46ecb2e88 100644 --- a/code/modules/mob/living/silicon/subystems.dm +++ b/code/modules/mob/living/silicon/subystems.dm @@ -1,110 +1,110 @@ -/mob/living/silicon - var/register_alarms = 1 - var/datum/tgui_module/alarm_monitor/all/robot/alarm_monitor - var/datum/tgui_module/atmos_control/robot/atmos_control - var/datum/tgui_module/crew_manifest/robot/crew_manifest - var/datum/tgui_module/crew_monitor/robot/crew_monitor - var/datum/tgui_module/law_manager/robot/law_manager - var/datum/tgui_module/power_monitor/robot/power_monitor - var/datum/tgui_module/rcon/robot/rcon - -/mob/living/silicon - var/list/silicon_subsystems = list( - /mob/living/silicon/proc/subsystem_alarm_monitor, - /mob/living/silicon/proc/subsystem_crew_manifest, - /mob/living/silicon/proc/subsystem_law_manager - ) - -/mob/living/silicon/ai - silicon_subsystems = list( - /mob/living/silicon/proc/subsystem_alarm_monitor, - /mob/living/silicon/proc/subsystem_atmos_control, - /mob/living/silicon/proc/subsystem_crew_manifest, - /mob/living/silicon/proc/subsystem_crew_monitor, - /mob/living/silicon/proc/subsystem_law_manager, - /mob/living/silicon/proc/subsystem_power_monitor, - /mob/living/silicon/proc/subsystem_rcon - ) - -/mob/living/silicon/robot/syndicate - register_alarms = 0 - silicon_subsystems = list(/mob/living/silicon/proc/subsystem_law_manager) - -/mob/living/silicon/proc/init_subsystems() - alarm_monitor = new(src) - atmos_control = new(src) - crew_manifest = new(src) - crew_monitor = new(src) - law_manager = new(src) - power_monitor = new(src) - rcon = new(src) - - if(!register_alarms) - return - - for(var/datum/alarm_handler/AH in SSalarm.all_handlers) - AH.register_alarm(src, /mob/living/silicon/proc/receive_alarm) - queued_alarms[AH] = list() // Makes sure alarms remain listed in consistent order - -/******************** -* Alarm Monitor * -********************/ -/mob/living/silicon/proc/subsystem_alarm_monitor() - set name = "Alarm Monitor" - set category = "Subystems" - - alarm_monitor.tgui_interact(usr) - -/******************** -* Atmos Control * -********************/ -/mob/living/silicon/proc/subsystem_atmos_control() - set category = "Subystems" - set name = "Atmospherics Control" - - atmos_control.tgui_interact(usr) - -/******************** -* Crew Manifest * -********************/ -/mob/living/silicon/proc/subsystem_crew_manifest() - set category = "Subystems" - set name = "Crew Manifest" - - crew_manifest.tgui_interact(usr) - -/******************** -* Crew Monitor * -********************/ -/mob/living/silicon/proc/subsystem_crew_monitor() - set category = "Subystems" - set name = "Crew Monitor" - - crew_monitor.tgui_interact(usr) - -/**************** -* Law Manager * -****************/ -/mob/living/silicon/proc/subsystem_law_manager() - set name = "Law Manager" - set category = "Subystems" - - law_manager.tgui_interact(usr) - -/******************** -* Power Monitor * -********************/ -/mob/living/silicon/proc/subsystem_power_monitor() - set category = "Subystems" - set name = "Power Monitor" - - power_monitor.tgui_interact(usr) - -/************ -* RCON * -************/ -/mob/living/silicon/proc/subsystem_rcon() - set category = "Subystems" - set name = "RCON" - - rcon.tgui_interact(usr) +/mob/living/silicon + var/register_alarms = 1 + var/datum/tgui_module/alarm_monitor/all/robot/alarm_monitor + var/datum/tgui_module/atmos_control/robot/atmos_control + var/datum/tgui_module/crew_manifest/robot/crew_manifest + var/datum/tgui_module/crew_monitor/robot/crew_monitor + var/datum/tgui_module/law_manager/robot/law_manager + var/datum/tgui_module/power_monitor/robot/power_monitor + var/datum/tgui_module/rcon/robot/rcon + +/mob/living/silicon + var/list/silicon_subsystems = list( + /mob/living/silicon/proc/subsystem_alarm_monitor, + /mob/living/silicon/proc/subsystem_crew_manifest, + /mob/living/silicon/proc/subsystem_law_manager + ) + +/mob/living/silicon/ai + silicon_subsystems = list( + /mob/living/silicon/proc/subsystem_alarm_monitor, + /mob/living/silicon/proc/subsystem_atmos_control, + /mob/living/silicon/proc/subsystem_crew_manifest, + /mob/living/silicon/proc/subsystem_crew_monitor, + /mob/living/silicon/proc/subsystem_law_manager, + /mob/living/silicon/proc/subsystem_power_monitor, + /mob/living/silicon/proc/subsystem_rcon + ) + +/mob/living/silicon/robot/syndicate + register_alarms = 0 + silicon_subsystems = list(/mob/living/silicon/proc/subsystem_law_manager) + +/mob/living/silicon/proc/init_subsystems() + alarm_monitor = new(src) + atmos_control = new(src) + crew_manifest = new(src) + crew_monitor = new(src) + law_manager = new(src) + power_monitor = new(src) + rcon = new(src) + + if(!register_alarms) + return + + for(var/datum/alarm_handler/AH in SSalarm.all_handlers) + AH.register_alarm(src, /mob/living/silicon/proc/receive_alarm) + queued_alarms[AH] = list() // Makes sure alarms remain listed in consistent order + +/******************** +* Alarm Monitor * +********************/ +/mob/living/silicon/proc/subsystem_alarm_monitor() + set name = "Alarm Monitor" + set category = "Subystems" + + alarm_monitor.tgui_interact(usr) + +/******************** +* Atmos Control * +********************/ +/mob/living/silicon/proc/subsystem_atmos_control() + set category = "Subystems" + set name = "Atmospherics Control" + + atmos_control.tgui_interact(usr) + +/******************** +* Crew Manifest * +********************/ +/mob/living/silicon/proc/subsystem_crew_manifest() + set category = "Subystems" + set name = "Crew Manifest" + + crew_manifest.tgui_interact(usr) + +/******************** +* Crew Monitor * +********************/ +/mob/living/silicon/proc/subsystem_crew_monitor() + set category = "Subystems" + set name = "Crew Monitor" + + crew_monitor.tgui_interact(usr) + +/**************** +* Law Manager * +****************/ +/mob/living/silicon/proc/subsystem_law_manager() + set name = "Law Manager" + set category = "Subystems" + + law_manager.tgui_interact(usr) + +/******************** +* Power Monitor * +********************/ +/mob/living/silicon/proc/subsystem_power_monitor() + set category = "Subystems" + set name = "Power Monitor" + + power_monitor.tgui_interact(usr) + +/************ +* RCON * +************/ +/mob/living/silicon/proc/subsystem_rcon() + set category = "Subystems" + set name = "RCON" + + rcon.tgui_interact(usr) diff --git a/code/modules/mob/living/simple_mob/donteatpets_vr.dm b/code/modules/mob/living/simple_mob/donteatpets_vr.dm index adf7d00976a..98d6b0283fe 100644 --- a/code/modules/mob/living/simple_mob/donteatpets_vr.dm +++ b/code/modules/mob/living/simple_mob/donteatpets_vr.dm @@ -1,60 +1,60 @@ -//I figured since it's basically always frowned upon to eat the station pets, it would probably be a good idea to just make that not possible normally. - -/mob/living/simple_mob/animal/passive/dog/corgi/Ian - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/dog/corgi/Lisa - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/dog/corgi/puppy - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/cat/runtime - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/cat/kitten - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/bird/parrot/poly - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/opossum/poppy - digestable = 0 - devourable = 0 - -/mob/living/carbon/human/monkey/punpun - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/snake/python/noodle - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/mouse/white/apple //She's a mouse living with a snake. Accidents happen. But don't gurg apple >:I - digestable = 0 - -/mob/living/simple_mob/animal/passive/fox/renault - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/crab/Coffee - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/sif/fluffy - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/slime/xenobio/rainbow/kendrick - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/chick - digestable = 0 +//I figured since it's basically always frowned upon to eat the station pets, it would probably be a good idea to just make that not possible normally. + +/mob/living/simple_mob/animal/passive/dog/corgi/Ian + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/dog/corgi/Lisa + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/dog/corgi/puppy + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/cat/runtime + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/cat/kitten + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/bird/parrot/poly + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/opossum/poppy + digestable = 0 + devourable = 0 + +/mob/living/carbon/human/monkey/punpun + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/snake/python/noodle + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/mouse/white/apple //She's a mouse living with a snake. Accidents happen. But don't gurg apple >:I + digestable = 0 + +/mob/living/simple_mob/animal/passive/fox/renault + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/crab/Coffee + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/sif/fluffy + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/slime/xenobio/rainbow/kendrick + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/chick + digestable = 0 devourable = 0 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm index 03c5d9160ea..50e54cf8aaa 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm @@ -1,274 +1,274 @@ -// Borers are probably still going to be buggy as fuck, this is just bringing their mob defines up to the new system. -// IMO they're a relic of several ages we're long past, their code and their design showing this plainly, but removing them would -// make certain people Unhappy so here we are. They need a complete redesign but thats beyond the scope of the rewrite. - -/mob/living/simple_mob/animal/borer - name = "cortical borer" - desc = "A small, quivering sluglike creature." - icon_state = "brainslug" - item_state = "brainslug" - icon_living = "brainslug" - icon_dead = "brainslug_dead" - - response_help = "pokes" - response_disarm = "prods" - response_harm = "stomps on" - attacktext = list("nipped") - friendly = list("prods") - - organ_names = /decl/mob_organ_names/borer - - status_flags = CANPUSH - pass_flags = PASSTABLE - movement_cooldown = 1.5 - - universal_understand = TRUE - can_be_antagged = TRUE - - holder_type = /obj/item/weapon/holder/borer - ai_holder_type = null // This is player-controlled, always. - - var/mob/living/carbon/human/host = null // The humanoid host for the brain worm. - var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth. - - var/roundstart = FALSE // If true, spawning won't try to pull a ghost. - var/antag = TRUE // If false, will avoid setting up objectives and events - - var/chemicals = 10 // A resource used for reproduction and powers. - var/max_chemicals = 250 // Max of said resource. - var/true_name = null // String used when speaking among other worms. - var/controlling = FALSE // Used in human death ceck. - var/docile = FALSE // Sugar can stop borers from acting. - - var/has_reproduced = FALSE - var/used_dominate // world.time when the dominate power was last used. - -/mob/living/simple_mob/animal/borer/roundstart - roundstart = TRUE - -/mob/living/simple_mob/animal/borer/non_antag - antag = FALSE - -/mob/living/simple_mob/animal/borer/Login() - ..() - if(antag && mind) - borers.add_antagonist(mind) - -/mob/living/simple_mob/animal/borer/Initialize() - add_language("Cortical Link") - - verbs += /mob/living/proc/ventcrawl - verbs += /mob/living/proc/hide - - true_name = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]" - - if(!roundstart && antag) - request_player() - - return ..() - -/mob/living/simple_mob/animal/borer/handle_special() - if(host && !stat && !host.stat) - // Handle docility. - if(host.reagents.has_reagent("sugar") && !docile) - var/message = "You feel the soporific flow of sugar in your host's blood, lulling you into docility." - var/target = controlling ? host : src - to_chat(target, span("warning", message)) - docile = TRUE - - else if(docile) - var/message = "You shake off your lethargy as the sugar leaves your host's blood." - var/target = controlling ? host : src - to_chat(target, span("notice", message)) - docile = FALSE - - // Chem regen. - if(chemicals < max_chemicals) - chemicals++ - - // Control stuff. - if(controlling) - if(docile) - to_chat(host, span("warning", "You are feeling far too docile to continue controlling your host...")) - host.release_control() - return - - if(prob(5)) - host.adjustBrainLoss(0.1) - - if(prob(host.brainloss/20)) - host.say("*[pick(list("blink","blink_r","choke","aflap","drool","twitch","twitch_v","gasp"))]") - -/mob/living/simple_mob/animal/borer/Stat() - ..() - if(client.statpanel == "Status") - statpanel("Status") - if(emergency_shuttle) - var/eta_status = emergency_shuttle.get_status_panel_eta() - if(eta_status) - stat(null, eta_status) - stat("Chemicals", chemicals) - -/mob/living/simple_mob/animal/borer/proc/detatch() - if(!host || !controlling) - return - - if(istype(host, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = host - var/obj/item/organ/external/head = H.get_organ(BP_HEAD) - if(head) - head.implants -= src - - controlling = FALSE - - host.remove_language("Cortical Link") - host.verbs -= /mob/living/carbon/proc/release_control - host.verbs -= /mob/living/carbon/proc/punish_host - host.verbs -= /mob/living/carbon/proc/spawn_larvae - - if(host_brain) - // these are here so bans and multikey warnings are not triggered on the wrong people when ckey is changed. - // computer_id and IP are not updated magically on their own in offline mobs -walter0o - - // This shit need to die in a phoron fire. - - // host -> self - var/h2s_id = host.computer_id - var/h2s_ip= host.lastKnownIP - host.computer_id = null - host.lastKnownIP = null - - src.ckey = host.ckey - - if(!src.computer_id) - src.computer_id = h2s_id - - if(!host_brain.lastKnownIP) - src.lastKnownIP = h2s_ip - - // brain -> host - var/b2h_id = host_brain.computer_id - var/b2h_ip= host_brain.lastKnownIP - host_brain.computer_id = null - host_brain.lastKnownIP = null - - host.ckey = host_brain.ckey - - if(!host.computer_id) - host.computer_id = b2h_id - - if(!host.lastKnownIP) - host.lastKnownIP = b2h_ip - - qdel(host_brain) - - -/mob/living/simple_mob/animal/borer/proc/leave_host() - if(!host) - return - - if(host.mind) - borers.remove_antagonist(host.mind) - - forceMove(get_turf(host)) - - reset_view(null) - machine = null - - if(istype(host, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = host - var/obj/item/organ/external/head = H.get_organ(BP_HEAD) - if(head) - head.implants -= src - - host.reset_view(null) - host.machine = null - host = null - -/mob/living/simple_mob/animal/borer/proc/request_player() - var/datum/ghost_query/Q = new /datum/ghost_query/borer() - var/list/winner = Q.query() // This will sleep the proc for awhile. - if(winner.len) - var/mob/observer/dead/D = winner[1] - transfer_personality(D) - -/mob/living/simple_mob/animal/borer/proc/transfer_personality(mob/candidate) - if(!candidate || !candidate.mind) - return - - src.mind = candidate.mind - candidate.mind.current = src - ckey = candidate.ckey - - if(mind) - mind.assigned_role = "Cortical Borer" - mind.special_role = "Cortical Borer" - - to_chat(src, span("notice", "You are a cortical borer! You are a brain slug that worms its way \ - into the head of its victim. Use stealth, persuasion and your powers of mind control to keep you, \ - your host and your eventual spawn safe and warm.")) - to_chat(src, "You can speak to your victim with say, to other borers with say :x, and use your Abilities tab to access powers.") - -/mob/living/simple_mob/animal/borer/cannot_use_vents() - return - -// This is awful but its literally say code. -/mob/living/simple_mob/animal/borer/say(var/message, var/datum/language/speaking = null, var/whispering = 0) - message = sanitize(message) - message = capitalize(message) - - if(!message) - return - - if(stat >= DEAD) - return say_dead(message) - else if(stat) - return - - if(client && client.prefs.muted & MUTE_IC) - to_chat(src, span("danger", "You cannot speak in IC (muted).")) - return - - if(copytext(message, 1, 2) == "*") - return emote(copytext(message, 2)) - - var/list/message_pieces = parse_languages(message) - for(var/datum/multilingual_say_piece/S in message_pieces) - if(S.speaking && S.speaking.flags & HIVEMIND) - S.speaking.broadcast(src, trim(copytext(message, 3)), src.true_name) - return - - if(!host) - if(chemicals >= 30) - to_chat(src, span("alien", "..You emit a psionic pulse with an encoded message..")) - var/list/nearby_mobs = list() - for(var/mob/living/LM in view(src, 1 + round(6 * (chemicals / max_chemicals)))) - if(LM == src) - continue - if(!LM.stat) - nearby_mobs += LM - var/mob/living/speaker - if(nearby_mobs.len) - speaker = tgui_input_list(usr, "Choose a target speaker:", "Target Choice", nearby_mobs) - if(speaker) - log_admin("[src.ckey]/([src]) tried to force [speaker] to say: [message]") - message_admins("[src.ckey]/([src]) tried to force [speaker] to say: [message]") - speaker.say("[message]") - return - to_chat(src, span("alien", "..But nothing heard it..")) - else - to_chat(src, span("warning", "You have no host to speak to.")) - return //No host, no audible speech. - - to_chat(src, "You drop words into [host]'s mind: \"[message]\"") - to_chat(host, "Your own thoughts speak: \"[message]\"") - - for(var/mob/M in player_list) - if(istype(M, /mob/new_player)) - continue - else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - to_chat(M, "[src.true_name] whispers to [host], \"[message]\"") - - -/decl/mob_organ_names/borer - hit_zones = list("head", "central segment", "tail segment") +// Borers are probably still going to be buggy as fuck, this is just bringing their mob defines up to the new system. +// IMO they're a relic of several ages we're long past, their code and their design showing this plainly, but removing them would +// make certain people Unhappy so here we are. They need a complete redesign but thats beyond the scope of the rewrite. + +/mob/living/simple_mob/animal/borer + name = "cortical borer" + desc = "A small, quivering sluglike creature." + icon_state = "brainslug" + item_state = "brainslug" + icon_living = "brainslug" + icon_dead = "brainslug_dead" + + response_help = "pokes" + response_disarm = "prods" + response_harm = "stomps on" + attacktext = list("nipped") + friendly = list("prods") + + organ_names = /decl/mob_organ_names/borer + + status_flags = CANPUSH + pass_flags = PASSTABLE + movement_cooldown = 1.5 + + universal_understand = TRUE + can_be_antagged = TRUE + + holder_type = /obj/item/weapon/holder/borer + ai_holder_type = null // This is player-controlled, always. + + var/mob/living/carbon/human/host = null // The humanoid host for the brain worm. + var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth. + + var/roundstart = FALSE // If true, spawning won't try to pull a ghost. + var/antag = TRUE // If false, will avoid setting up objectives and events + + var/chemicals = 10 // A resource used for reproduction and powers. + var/max_chemicals = 250 // Max of said resource. + var/true_name = null // String used when speaking among other worms. + var/controlling = FALSE // Used in human death ceck. + var/docile = FALSE // Sugar can stop borers from acting. + + var/has_reproduced = FALSE + var/used_dominate // world.time when the dominate power was last used. + +/mob/living/simple_mob/animal/borer/roundstart + roundstart = TRUE + +/mob/living/simple_mob/animal/borer/non_antag + antag = FALSE + +/mob/living/simple_mob/animal/borer/Login() + ..() + if(antag && mind) + borers.add_antagonist(mind) + +/mob/living/simple_mob/animal/borer/Initialize() + add_language("Cortical Link") + + verbs += /mob/living/proc/ventcrawl + verbs += /mob/living/proc/hide + + true_name = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]" + + if(!roundstart && antag) + request_player() + + return ..() + +/mob/living/simple_mob/animal/borer/handle_special() + if(host && !stat && !host.stat) + // Handle docility. + if(host.reagents.has_reagent("sugar") && !docile) + var/message = "You feel the soporific flow of sugar in your host's blood, lulling you into docility." + var/target = controlling ? host : src + to_chat(target, span("warning", message)) + docile = TRUE + + else if(docile) + var/message = "You shake off your lethargy as the sugar leaves your host's blood." + var/target = controlling ? host : src + to_chat(target, span("notice", message)) + docile = FALSE + + // Chem regen. + if(chemicals < max_chemicals) + chemicals++ + + // Control stuff. + if(controlling) + if(docile) + to_chat(host, span("warning", "You are feeling far too docile to continue controlling your host...")) + host.release_control() + return + + if(prob(5)) + host.adjustBrainLoss(0.1) + + if(prob(host.brainloss/20)) + host.say("*[pick(list("blink","blink_r","choke","aflap","drool","twitch","twitch_v","gasp"))]") + +/mob/living/simple_mob/animal/borer/Stat() + ..() + if(client.statpanel == "Status") + statpanel("Status") + if(emergency_shuttle) + var/eta_status = emergency_shuttle.get_status_panel_eta() + if(eta_status) + stat(null, eta_status) + stat("Chemicals", chemicals) + +/mob/living/simple_mob/animal/borer/proc/detatch() + if(!host || !controlling) + return + + if(istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = host + var/obj/item/organ/external/head = H.get_organ(BP_HEAD) + if(head) + head.implants -= src + + controlling = FALSE + + host.remove_language("Cortical Link") + host.verbs -= /mob/living/carbon/proc/release_control + host.verbs -= /mob/living/carbon/proc/punish_host + host.verbs -= /mob/living/carbon/proc/spawn_larvae + + if(host_brain) + // these are here so bans and multikey warnings are not triggered on the wrong people when ckey is changed. + // computer_id and IP are not updated magically on their own in offline mobs -walter0o + + // This shit need to die in a phoron fire. + + // host -> self + var/h2s_id = host.computer_id + var/h2s_ip= host.lastKnownIP + host.computer_id = null + host.lastKnownIP = null + + src.ckey = host.ckey + + if(!src.computer_id) + src.computer_id = h2s_id + + if(!host_brain.lastKnownIP) + src.lastKnownIP = h2s_ip + + // brain -> host + var/b2h_id = host_brain.computer_id + var/b2h_ip= host_brain.lastKnownIP + host_brain.computer_id = null + host_brain.lastKnownIP = null + + host.ckey = host_brain.ckey + + if(!host.computer_id) + host.computer_id = b2h_id + + if(!host.lastKnownIP) + host.lastKnownIP = b2h_ip + + qdel(host_brain) + + +/mob/living/simple_mob/animal/borer/proc/leave_host() + if(!host) + return + + if(host.mind) + borers.remove_antagonist(host.mind) + + forceMove(get_turf(host)) + + reset_view(null) + machine = null + + if(istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = host + var/obj/item/organ/external/head = H.get_organ(BP_HEAD) + if(head) + head.implants -= src + + host.reset_view(null) + host.machine = null + host = null + +/mob/living/simple_mob/animal/borer/proc/request_player() + var/datum/ghost_query/Q = new /datum/ghost_query/borer() + var/list/winner = Q.query() // This will sleep the proc for awhile. + if(winner.len) + var/mob/observer/dead/D = winner[1] + transfer_personality(D) + +/mob/living/simple_mob/animal/borer/proc/transfer_personality(mob/candidate) + if(!candidate || !candidate.mind) + return + + src.mind = candidate.mind + candidate.mind.current = src + ckey = candidate.ckey + + if(mind) + mind.assigned_role = "Cortical Borer" + mind.special_role = "Cortical Borer" + + to_chat(src, span("notice", "You are a cortical borer! You are a brain slug that worms its way \ + into the head of its victim. Use stealth, persuasion and your powers of mind control to keep you, \ + your host and your eventual spawn safe and warm.")) + to_chat(src, "You can speak to your victim with say, to other borers with say :x, and use your Abilities tab to access powers.") + +/mob/living/simple_mob/animal/borer/cannot_use_vents() + return + +// This is awful but its literally say code. +/mob/living/simple_mob/animal/borer/say(var/message, var/datum/language/speaking = null, var/whispering = 0) + message = sanitize(message) + message = capitalize(message) + + if(!message) + return + + if(stat >= DEAD) + return say_dead(message) + else if(stat) + return + + if(client && client.prefs.muted & MUTE_IC) + to_chat(src, span("danger", "You cannot speak in IC (muted).")) + return + + if(copytext(message, 1, 2) == "*") + return emote(copytext(message, 2)) + + var/list/message_pieces = parse_languages(message) + for(var/datum/multilingual_say_piece/S in message_pieces) + if(S.speaking && S.speaking.flags & HIVEMIND) + S.speaking.broadcast(src, trim(copytext(message, 3)), src.true_name) + return + + if(!host) + if(chemicals >= 30) + to_chat(src, span("alien", "..You emit a psionic pulse with an encoded message..")) + var/list/nearby_mobs = list() + for(var/mob/living/LM in view(src, 1 + round(6 * (chemicals / max_chemicals)))) + if(LM == src) + continue + if(!LM.stat) + nearby_mobs += LM + var/mob/living/speaker + if(nearby_mobs.len) + speaker = tgui_input_list(usr, "Choose a target speaker:", "Target Choice", nearby_mobs) + if(speaker) + log_admin("[src.ckey]/([src]) tried to force [speaker] to say: [message]") + message_admins("[src.ckey]/([src]) tried to force [speaker] to say: [message]") + speaker.say("[message]") + return + to_chat(src, span("alien", "..But nothing heard it..")) + else + to_chat(src, span("warning", "You have no host to speak to.")) + return //No host, no audible speech. + + to_chat(src, "You drop words into [host]'s mind: \"[message]\"") + to_chat(host, "Your own thoughts speak: \"[message]\"") + + for(var/mob/M in player_list) + if(istype(M, /mob/new_player)) + continue + else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) + to_chat(M, "[src.true_name] whispers to [host], \"[message]\"") + + +/decl/mob_organ_names/borer + hit_zones = list("head", "central segment", "tail segment") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm index c0bc4e310af..ab1d9cfbfec 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm @@ -1,64 +1,64 @@ -// Straight move from the old location, with the paths corrected. - -/mob/living/captive_brain - name = "host brain" - real_name = "host brain" - universal_understand = 1 - -/mob/living/captive_brain/say(var/message, var/datum/language/speaking = null, var/whispering = 0) - - if (src.client) - if(client.prefs.muted & MUTE_IC) - to_chat(src, span_red("You cannot speak in IC (muted).")) - return - - if(istype(src.loc, /mob/living/simple_mob/animal/borer)) - - message = sanitize(message) - if (!message) - return - log_say(message,src) - if (stat == 2) - return say_dead(message) - - var/mob/living/simple_mob/animal/borer/B = src.loc - to_chat(src, "You whisper silently, \"[message]\"") - to_chat(B.host, "The captive mind of [src] whispers, \"[message]\"") - - for (var/mob/M in player_list) - if (istype(M, /mob/new_player)) - continue - else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - to_chat(M, "The captive mind of [src] whispers, \"[message]\"") - -/mob/living/captive_brain/me_verb(message as text) - to_chat(src, "You cannot emote as a captive mind.") - return - -/mob/living/captive_brain/emote(var/message) - to_chat(src, "You cannot emote as a captive mind.") - return - -/mob/living/captive_brain/process_resist() - //Resisting control by an alien mind. - if(istype(src.loc, /mob/living/simple_mob/animal/borer)) - var/mob/living/simple_mob/animal/borer/B = src.loc - var/mob/living/captive_brain/H = src - - to_chat(H, "You begin doggedly resisting the parasite's control (this will take approximately sixty seconds).") - to_chat(B.host, "You feel the captive mind of [src] begin to resist your control.") - - spawn(rand(200,250)+B.host.brainloss) - if(!B || !B.controlling) return - - B.host.adjustBrainLoss(rand(0.1,0.5)) - to_chat(H, "With an immense exertion of will, you regain control of your body!") - to_chat(B.host, "You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you.") - B.detatch() - verbs -= /mob/living/carbon/proc/release_control - verbs -= /mob/living/carbon/proc/punish_host - verbs -= /mob/living/carbon/proc/spawn_larvae - - return - - ..() +// Straight move from the old location, with the paths corrected. + +/mob/living/captive_brain + name = "host brain" + real_name = "host brain" + universal_understand = 1 + +/mob/living/captive_brain/say(var/message, var/datum/language/speaking = null, var/whispering = 0) + + if (src.client) + if(client.prefs.muted & MUTE_IC) + to_chat(src, span_red("You cannot speak in IC (muted).")) + return + + if(istype(src.loc, /mob/living/simple_mob/animal/borer)) + + message = sanitize(message) + if (!message) + return + log_say(message,src) + if (stat == 2) + return say_dead(message) + + var/mob/living/simple_mob/animal/borer/B = src.loc + to_chat(src, "You whisper silently, \"[message]\"") + to_chat(B.host, "The captive mind of [src] whispers, \"[message]\"") + + for (var/mob/M in player_list) + if (istype(M, /mob/new_player)) + continue + else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) + to_chat(M, "The captive mind of [src] whispers, \"[message]\"") + +/mob/living/captive_brain/me_verb(message as text) + to_chat(src, "You cannot emote as a captive mind.") + return + +/mob/living/captive_brain/emote(var/message) + to_chat(src, "You cannot emote as a captive mind.") + return + +/mob/living/captive_brain/process_resist() + //Resisting control by an alien mind. + if(istype(src.loc, /mob/living/simple_mob/animal/borer)) + var/mob/living/simple_mob/animal/borer/B = src.loc + var/mob/living/captive_brain/H = src + + to_chat(H, "You begin doggedly resisting the parasite's control (this will take approximately sixty seconds).") + to_chat(B.host, "You feel the captive mind of [src] begin to resist your control.") + + spawn(rand(200,250)+B.host.brainloss) + if(!B || !B.controlling) return + + B.host.adjustBrainLoss(rand(0.1,0.5)) + to_chat(H, "With an immense exertion of will, you regain control of your body!") + to_chat(B.host, "You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you.") + B.detatch() + verbs -= /mob/living/carbon/proc/release_control + verbs -= /mob/living/carbon/proc/punish_host + verbs -= /mob/living/carbon/proc/spawn_larvae + + return + + ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm index f7b19810b76..62b0c2b2090 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm @@ -1,356 +1,356 @@ -/mob/living/simple_mob/animal/borer/verb/release_host() - set category = "Abilities" - set name = "Release Host" - set desc = "Slither out of your host." - - if(!host) - to_chat(src, "You are not inside a host body.") - return - - if(stat) - to_chat(src, "You cannot leave your host in your current state.") - - if(docile) - to_chat(src, span_blue("You are feeling far too docile to do that.")) - return - - if(!host || !src) return - - to_chat(src, "You begin disconnecting from [host]'s synapses and prodding at their internal ear canal.") - - if(!host.stat) - to_chat(host, "An odd, uncomfortable pressure begins to build inside your skull, behind your ear...") - - spawn(100) - - if(!host || !src) return - - if(src.stat) - to_chat(src, "You cannot release your host in your current state.") - return - - to_chat(src, "You wiggle out of [host]'s ear and plop to the ground.") - if(host.mind) - if(!host.stat) - to_chat(host, "Something slimy wiggles out of your ear and plops to the ground!") - to_chat(host, "As though waking from a dream, you shake off the insidious mind control of the brain worm. Your thoughts are your own again.") - - detatch() - leave_host() - -/mob/living/simple_mob/animal/borer/verb/infest() - set category = "Abilities" - set name = "Infest" - set desc = "Infest a suitable humanoid host." - - if(host) - to_chat(src, "You are already within a host.") - return - - if(stat) - to_chat(src, "You cannot infest a target in your current state.") - return - - var/list/choices = list() - for(var/mob/living/carbon/C in view(1,src)) - if(src.Adjacent(C)) - choices += C - - if(!choices.len) - to_chat(src, "There are no viable hosts within range...") - return - - var/mob/living/carbon/M = tgui_input_list(src, "Who do you wish to infest?", "Target Choice", choices) - - if(!M || !src) return - - if(!(src.Adjacent(M))) return - - if(M.has_brain_worms()) - to_chat(src, "You cannot infest someone who is already infested!") - return - - if(istype(M,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - - var/obj/item/organ/external/E = H.organs_by_name[BP_HEAD] - if(!E || E.is_stump()) - to_chat(src, "\The [H] does not have a head!") - - if(!H.should_have_organ("brain")) - to_chat(src, "\The [H] does not seem to have an ear canal to breach.") - return - - if(H.check_head_coverage()) - to_chat(src, "You cannot get through that host's protective gear.") - return - - to_chat(M, "Something slimy begins probing at the opening of your ear canal...") - to_chat(src, "You slither up [M] and begin probing at their ear canal...") - - if(!do_after(src,30)) - to_chat(src, "As [M] moves away, you are dislodged and fall to the ground.") - return - - if(!M || !src) return - - if(src.stat) - to_chat(src, "You cannot infest a target in your current state.") - return - - if(M in view(1, src)) - to_chat(src, "You wiggle into [M]'s ear.") - if(!M.stat) - to_chat(M, "Something disgusting and slimy wiggles into your ear!") - - src.host = M - src.forceMove(M) - - //Update their traitor status. - if(host.mind) - borers.add_antagonist_mind(host.mind, 1, borers.faction_role_text, borers.faction_welcome) - - if(istype(M,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/I = H.internal_organs_by_name["brain"] - if(!I) // No brain organ, so the borer moves in and replaces it permanently. - replace_brain() - else - // If they're in normally, implant removal can get them out. - var/obj/item/organ/external/head = H.get_organ(BP_HEAD) - head.implants += src - - return - else - to_chat(src, "They are no longer in range!") - return - -/* -/mob/living/simple_mob/animal/borer/verb/devour_brain() - set category = "Abilities" - set name = "Devour Brain" - set desc = "Take permanent control of a dead host." - - if(!host) - to_chat(src, "You are not inside a host body.") - return - - if(host.stat != 2) - to_chat(src, "Your host is still alive.") - return - - if(stat) - to_chat(src, "You cannot do that in your current state.") - - if(docile) - to_chat(src, "You are feeling far too docile to do that.") - return - - - to_chat(src, "It only takes a few moments to render the dead host brain down into a nutrient-rich slurry...") - replace_brain() -*/ - -// BRAIN WORM ZOMBIES AAAAH. -/mob/living/simple_mob/animal/borer/proc/replace_brain() - - var/mob/living/carbon/human/H = host - - if(!istype(host)) - to_chat(src, "This host does not have a suitable brain.") - return - - to_chat(src, "You settle into the empty brainpan and begin to expand, fusing inextricably with the dead flesh of [H].") - - H.add_language("Cortical Link") - - if(host.stat == 2) - H.verbs |= /mob/living/carbon/human/proc/jumpstart - - H.verbs |= /mob/living/carbon/human/proc/psychic_whisper - H.verbs |= /mob/living/carbon/human/proc/tackle - if(antag) - H.verbs |= /mob/living/carbon/proc/spawn_larvae - - if(H.client) - H.ghostize(0) - - if(src.mind) - src.mind.special_role = "Borer Husk" - src.mind.transfer_to(host) - - H.ChangeToHusk() - - var/obj/item/organ/internal/borer/B = new(H) - H.internal_organs_by_name["brain"] = B - H.internal_organs |= B - - var/obj/item/organ/external/affecting = H.get_organ(BP_HEAD) - affecting.implants -= src - - var/s2h_id = src.computer_id - var/s2h_ip= src.lastKnownIP - src.computer_id = null - src.lastKnownIP = null - - if(!H.computer_id) - H.computer_id = s2h_id - - if(!H.lastKnownIP) - H.lastKnownIP = s2h_ip - -/mob/living/simple_mob/animal/borer/verb/secrete_chemicals() - set category = "Abilities" - set name = "Secrete Chemicals" - set desc = "Push some chemicals into your host's bloodstream." - - if(!host) - to_chat(src, "You are not inside a host body.") - return - - if(stat) - to_chat(src, "You cannot secrete chemicals in your current state.") - - if(docile) - to_chat(src, span_blue("You are feeling far too docile to do that.")) - return - - if(chemicals < 50) - to_chat(src, "You don't have enough chemicals!") - - var/chem = tgui_input_list(usr, "Select a chemical to secrete.", "Chemicals", list("alkysine","bicaridine","hyperzine","tramadol")) - - if(!chem || chemicals < 50 || !host || controlling || !src || stat) //Sanity check. - return - - to_chat(src, span_red("You squirt a measure of [chem] from your reservoirs into [host]'s bloodstream.")) - host.reagents.add_reagent(chem, 10) - chemicals -= 50 - -/mob/living/simple_mob/animal/borer/verb/dominate_victim() - set category = "Abilities" - set name = "Paralyze Victim" - set desc = "Freeze the limbs of a potential host with supernatural fear." - - if(world.time - used_dominate < 150) - to_chat(src, "You cannot use that ability again so soon.") - return - - if(host) - to_chat(src, "You cannot do that from within a host body.") - return - - if(src.stat) - to_chat(src, "You cannot do that in your current state.") - return - - var/list/choices = list() - for(var/mob/living/carbon/C in view(3,src)) - if(C.stat != 2) - choices += C - - if(world.time - used_dominate < 150) - to_chat(src, "You cannot use that ability again so soon.") - return - - var/mob/living/carbon/M = tgui_input_list(src, "Who do you wish to dominate?", "Target Choice", choices) - - if(!M || !src) return - - if(M.has_brain_worms()) - to_chat(src, "You cannot infest someone who is already infested!") - return - - to_chat(src, span_red("You focus your psychic lance on [M] and freeze their limbs with a wave of terrible dread.")) - to_chat(M, span_red("You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing.")) - M.Weaken(10) - - used_dominate = world.time - -/mob/living/simple_mob/animal/borer/verb/bond_brain() - set category = "Abilities" - set name = "Assume Control" - set desc = "Fully connect to the brain of your host." - - if(!host) - to_chat(src, "You are not inside a host body.") - return - - if(src.stat) - to_chat(src, "You cannot do that in your current state.") - return - - if(docile) - to_chat(src, span_blue("You are feeling far too docile to do that.")) - return - - to_chat(src, "You begin delicately adjusting your connection to the host brain...") - - spawn(100+(host.brainloss*5)) - - if(!host || !src || controlling) - return - else - - to_chat(src, span_red("You plunge your probosci deep into the cortex of the host brain, interfacing directly with their nervous system.")) - to_chat(host, span_red("You feel a strange shifting sensation behind your eyes as an alien consciousness displaces yours.")) - host.add_language("Cortical Link") - - // host -> brain - var/h2b_id = host.computer_id - var/h2b_ip= host.lastKnownIP - host.computer_id = null - host.lastKnownIP = null - - qdel(host_brain) - host_brain = new(src) - - host_brain.ckey = host.ckey - - host_brain.name = host.name - - if(!host_brain.computer_id) - host_brain.computer_id = h2b_id - - if(!host_brain.lastKnownIP) - host_brain.lastKnownIP = h2b_ip - - // self -> host - var/s2h_id = src.computer_id - var/s2h_ip= src.lastKnownIP - src.computer_id = null - src.lastKnownIP = null - - host.ckey = src.ckey - - if(!host.computer_id) - host.computer_id = s2h_id - - if(!host.lastKnownIP) - host.lastKnownIP = s2h_ip - - controlling = 1 - - host.verbs += /mob/living/carbon/proc/release_control - host.verbs += /mob/living/carbon/proc/punish_host - if(antag) - host.verbs += /mob/living/carbon/proc/spawn_larvae - - return - -/mob/living/carbon/human/proc/jumpstart() - set category = "Abilities" - set name = "Revive Host" - set desc = "Send a jolt of electricity through your host, reviving them." - - if(stat != 2) - to_chat(usr, "Your host is already alive.") - return - - verbs -= /mob/living/carbon/human/proc/jumpstart - visible_message("With a hideous, rattling moan, [src] shudders back to life!") - - rejuvenate() - restore_blood() - fixblood() - update_canmove() +/mob/living/simple_mob/animal/borer/verb/release_host() + set category = "Abilities" + set name = "Release Host" + set desc = "Slither out of your host." + + if(!host) + to_chat(src, "You are not inside a host body.") + return + + if(stat) + to_chat(src, "You cannot leave your host in your current state.") + + if(docile) + to_chat(src, span_blue("You are feeling far too docile to do that.")) + return + + if(!host || !src) return + + to_chat(src, "You begin disconnecting from [host]'s synapses and prodding at their internal ear canal.") + + if(!host.stat) + to_chat(host, "An odd, uncomfortable pressure begins to build inside your skull, behind your ear...") + + spawn(100) + + if(!host || !src) return + + if(src.stat) + to_chat(src, "You cannot release your host in your current state.") + return + + to_chat(src, "You wiggle out of [host]'s ear and plop to the ground.") + if(host.mind) + if(!host.stat) + to_chat(host, "Something slimy wiggles out of your ear and plops to the ground!") + to_chat(host, "As though waking from a dream, you shake off the insidious mind control of the brain worm. Your thoughts are your own again.") + + detatch() + leave_host() + +/mob/living/simple_mob/animal/borer/verb/infest() + set category = "Abilities" + set name = "Infest" + set desc = "Infest a suitable humanoid host." + + if(host) + to_chat(src, "You are already within a host.") + return + + if(stat) + to_chat(src, "You cannot infest a target in your current state.") + return + + var/list/choices = list() + for(var/mob/living/carbon/C in view(1,src)) + if(src.Adjacent(C)) + choices += C + + if(!choices.len) + to_chat(src, "There are no viable hosts within range...") + return + + var/mob/living/carbon/M = tgui_input_list(src, "Who do you wish to infest?", "Target Choice", choices) + + if(!M || !src) return + + if(!(src.Adjacent(M))) return + + if(M.has_brain_worms()) + to_chat(src, "You cannot infest someone who is already infested!") + return + + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + + var/obj/item/organ/external/E = H.organs_by_name[BP_HEAD] + if(!E || E.is_stump()) + to_chat(src, "\The [H] does not have a head!") + + if(!H.should_have_organ("brain")) + to_chat(src, "\The [H] does not seem to have an ear canal to breach.") + return + + if(H.check_head_coverage()) + to_chat(src, "You cannot get through that host's protective gear.") + return + + to_chat(M, "Something slimy begins probing at the opening of your ear canal...") + to_chat(src, "You slither up [M] and begin probing at their ear canal...") + + if(!do_after(src,30)) + to_chat(src, "As [M] moves away, you are dislodged and fall to the ground.") + return + + if(!M || !src) return + + if(src.stat) + to_chat(src, "You cannot infest a target in your current state.") + return + + if(M in view(1, src)) + to_chat(src, "You wiggle into [M]'s ear.") + if(!M.stat) + to_chat(M, "Something disgusting and slimy wiggles into your ear!") + + src.host = M + src.forceMove(M) + + //Update their traitor status. + if(host.mind) + borers.add_antagonist_mind(host.mind, 1, borers.faction_role_text, borers.faction_welcome) + + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/I = H.internal_organs_by_name["brain"] + if(!I) // No brain organ, so the borer moves in and replaces it permanently. + replace_brain() + else + // If they're in normally, implant removal can get them out. + var/obj/item/organ/external/head = H.get_organ(BP_HEAD) + head.implants += src + + return + else + to_chat(src, "They are no longer in range!") + return + +/* +/mob/living/simple_mob/animal/borer/verb/devour_brain() + set category = "Abilities" + set name = "Devour Brain" + set desc = "Take permanent control of a dead host." + + if(!host) + to_chat(src, "You are not inside a host body.") + return + + if(host.stat != 2) + to_chat(src, "Your host is still alive.") + return + + if(stat) + to_chat(src, "You cannot do that in your current state.") + + if(docile) + to_chat(src, "You are feeling far too docile to do that.") + return + + + to_chat(src, "It only takes a few moments to render the dead host brain down into a nutrient-rich slurry...") + replace_brain() +*/ + +// BRAIN WORM ZOMBIES AAAAH. +/mob/living/simple_mob/animal/borer/proc/replace_brain() + + var/mob/living/carbon/human/H = host + + if(!istype(host)) + to_chat(src, "This host does not have a suitable brain.") + return + + to_chat(src, "You settle into the empty brainpan and begin to expand, fusing inextricably with the dead flesh of [H].") + + H.add_language("Cortical Link") + + if(host.stat == 2) + H.verbs |= /mob/living/carbon/human/proc/jumpstart + + H.verbs |= /mob/living/carbon/human/proc/psychic_whisper + H.verbs |= /mob/living/carbon/human/proc/tackle + if(antag) + H.verbs |= /mob/living/carbon/proc/spawn_larvae + + if(H.client) + H.ghostize(0) + + if(src.mind) + src.mind.special_role = "Borer Husk" + src.mind.transfer_to(host) + + H.ChangeToHusk() + + var/obj/item/organ/internal/borer/B = new(H) + H.internal_organs_by_name["brain"] = B + H.internal_organs |= B + + var/obj/item/organ/external/affecting = H.get_organ(BP_HEAD) + affecting.implants -= src + + var/s2h_id = src.computer_id + var/s2h_ip= src.lastKnownIP + src.computer_id = null + src.lastKnownIP = null + + if(!H.computer_id) + H.computer_id = s2h_id + + if(!H.lastKnownIP) + H.lastKnownIP = s2h_ip + +/mob/living/simple_mob/animal/borer/verb/secrete_chemicals() + set category = "Abilities" + set name = "Secrete Chemicals" + set desc = "Push some chemicals into your host's bloodstream." + + if(!host) + to_chat(src, "You are not inside a host body.") + return + + if(stat) + to_chat(src, "You cannot secrete chemicals in your current state.") + + if(docile) + to_chat(src, span_blue("You are feeling far too docile to do that.")) + return + + if(chemicals < 50) + to_chat(src, "You don't have enough chemicals!") + + var/chem = tgui_input_list(usr, "Select a chemical to secrete.", "Chemicals", list("alkysine","bicaridine","hyperzine","tramadol")) + + if(!chem || chemicals < 50 || !host || controlling || !src || stat) //Sanity check. + return + + to_chat(src, span_red("You squirt a measure of [chem] from your reservoirs into [host]'s bloodstream.")) + host.reagents.add_reagent(chem, 10) + chemicals -= 50 + +/mob/living/simple_mob/animal/borer/verb/dominate_victim() + set category = "Abilities" + set name = "Paralyze Victim" + set desc = "Freeze the limbs of a potential host with supernatural fear." + + if(world.time - used_dominate < 150) + to_chat(src, "You cannot use that ability again so soon.") + return + + if(host) + to_chat(src, "You cannot do that from within a host body.") + return + + if(src.stat) + to_chat(src, "You cannot do that in your current state.") + return + + var/list/choices = list() + for(var/mob/living/carbon/C in view(3,src)) + if(C.stat != 2) + choices += C + + if(world.time - used_dominate < 150) + to_chat(src, "You cannot use that ability again so soon.") + return + + var/mob/living/carbon/M = tgui_input_list(src, "Who do you wish to dominate?", "Target Choice", choices) + + if(!M || !src) return + + if(M.has_brain_worms()) + to_chat(src, "You cannot infest someone who is already infested!") + return + + to_chat(src, span_red("You focus your psychic lance on [M] and freeze their limbs with a wave of terrible dread.")) + to_chat(M, span_red("You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing.")) + M.Weaken(10) + + used_dominate = world.time + +/mob/living/simple_mob/animal/borer/verb/bond_brain() + set category = "Abilities" + set name = "Assume Control" + set desc = "Fully connect to the brain of your host." + + if(!host) + to_chat(src, "You are not inside a host body.") + return + + if(src.stat) + to_chat(src, "You cannot do that in your current state.") + return + + if(docile) + to_chat(src, span_blue("You are feeling far too docile to do that.")) + return + + to_chat(src, "You begin delicately adjusting your connection to the host brain...") + + spawn(100+(host.brainloss*5)) + + if(!host || !src || controlling) + return + else + + to_chat(src, span_red("You plunge your probosci deep into the cortex of the host brain, interfacing directly with their nervous system.")) + to_chat(host, span_red("You feel a strange shifting sensation behind your eyes as an alien consciousness displaces yours.")) + host.add_language("Cortical Link") + + // host -> brain + var/h2b_id = host.computer_id + var/h2b_ip= host.lastKnownIP + host.computer_id = null + host.lastKnownIP = null + + qdel(host_brain) + host_brain = new(src) + + host_brain.ckey = host.ckey + + host_brain.name = host.name + + if(!host_brain.computer_id) + host_brain.computer_id = h2b_id + + if(!host_brain.lastKnownIP) + host_brain.lastKnownIP = h2b_ip + + // self -> host + var/s2h_id = src.computer_id + var/s2h_ip= src.lastKnownIP + src.computer_id = null + src.lastKnownIP = null + + host.ckey = src.ckey + + if(!host.computer_id) + host.computer_id = s2h_id + + if(!host.lastKnownIP) + host.lastKnownIP = s2h_ip + + controlling = 1 + + host.verbs += /mob/living/carbon/proc/release_control + host.verbs += /mob/living/carbon/proc/punish_host + if(antag) + host.verbs += /mob/living/carbon/proc/spawn_larvae + + return + +/mob/living/carbon/human/proc/jumpstart() + set category = "Abilities" + set name = "Revive Host" + set desc = "Send a jolt of electricity through your host, reviving them." + + if(stat != 2) + to_chat(usr, "Your host is already alive.") + return + + verbs -= /mob/living/carbon/human/proc/jumpstart + visible_message("With a hideous, rattling moan, [src] shudders back to life!") + + rejuvenate() + restore_blood() + fixblood() + update_canmove() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm index 7ff84ff697b..8fba024af57 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm @@ -1,56 +1,56 @@ -//Look Sir, free crabs! -/mob/living/simple_mob/animal/passive/crab - name = "crab" - desc = "A hard-shelled crustacean. Seems quite content to lounge around all the time." - tt_desc = "E Cancer bellianus" - faction = "crabs" - - icon_state = "crab" - icon_living = "crab" - icon_dead = "crab_dead" - - mob_size = MOB_SMALL - - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "stomps" - friendly = "pinches" - - organ_names = /decl/mob_organ_names/crab - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/crabmeat - meat_amount = 3 - - say_list_type = /datum/say_list/crab - -//COFFEE! SQUEEEEEEEEE! -/mob/living/simple_mob/animal/passive/crab/Coffee - name = "Coffee" - real_name = "Coffee" - desc = "It's Coffee, the other pet!" - -// Sif! - -/datum/category_item/catalogue/fauna/sif_crab - name = "Sivian Fauna - Shelf Crab" - desc = "Classification: S Ocypode glacian\ -

                    \ - A small crustacean sometimes considered a pest to Sivian fisheries, \ - as the creatures often tend to ignore non-native fish species when feeding. This \ - results in an unfortunate advantage for invasive species. \ -
                    \ - Otherwise, these animals are enjoyed as a reliable source of high-grade meat." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/animal/passive/crab/sif - icon = 'icons/mob/fish.dmi' - tt_desc = "S Ocypode glacian" - - catalogue_data = list(/datum/category_item/catalogue/fauna/sif_crab) - -/mob/living/simple_mob/animal/passive/crab/sif/Initialize() - . = ..() - adjust_scale(rand(5,12) / 10) - -/decl/mob_organ_names/crab +//Look Sir, free crabs! +/mob/living/simple_mob/animal/passive/crab + name = "crab" + desc = "A hard-shelled crustacean. Seems quite content to lounge around all the time." + tt_desc = "E Cancer bellianus" + faction = "crabs" + + icon_state = "crab" + icon_living = "crab" + icon_dead = "crab_dead" + + mob_size = MOB_SMALL + + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stomps" + friendly = "pinches" + + organ_names = /decl/mob_organ_names/crab + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/crabmeat + meat_amount = 3 + + say_list_type = /datum/say_list/crab + +//COFFEE! SQUEEEEEEEEE! +/mob/living/simple_mob/animal/passive/crab/Coffee + name = "Coffee" + real_name = "Coffee" + desc = "It's Coffee, the other pet!" + +// Sif! + +/datum/category_item/catalogue/fauna/sif_crab + name = "Sivian Fauna - Shelf Crab" + desc = "Classification: S Ocypode glacian\ +

                    \ + A small crustacean sometimes considered a pest to Sivian fisheries, \ + as the creatures often tend to ignore non-native fish species when feeding. This \ + results in an unfortunate advantage for invasive species. \ +
                    \ + Otherwise, these animals are enjoyed as a reliable source of high-grade meat." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/passive/crab/sif + icon = 'icons/mob/fish.dmi' + tt_desc = "S Ocypode glacian" + + catalogue_data = list(/datum/category_item/catalogue/fauna/sif_crab) + +/mob/living/simple_mob/animal/passive/crab/sif/Initialize() + . = ..() + adjust_scale(rand(5,12) / 10) + +/decl/mob_organ_names/crab hit_zones = list("cephalothorax", "abdomen", "left walking legs", "right walking legs", "left swimming legs", "right swimming legs", "left pincer", "right pincer") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm index 4dd631d5d72..5b8a5f2177d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm @@ -1,328 +1,328 @@ -// Different types of fish! They are all subtypes of this tho -/datum/category_item/catalogue/fauna/invasive_fish - name = "Invasive Fauna - Fish" - desc = "This fish is considered an invasive species according \ - to Sivian wildlife regulations. Removal or relocation is advised." - value = CATALOGUER_REWARD_TRIVIAL - -/mob/living/simple_mob/animal/passive/fish - name = "fish" - desc = "Its a fishy. No touchy fishy." - icon = 'icons/mob/fish.dmi' - item_state = "fish" - - //catalogue_data = list(/datum/category_item/catalogue/fauna/invasive_fish) //TODO: write non-sif lore - - mob_size = MOB_SMALL - // So fish are actually underwater. - plane = TURF_PLANE - layer = UNDERWATER_LAYER - - organ_names = /decl/mob_organ_names/fish - - holder_type = /obj/item/weapon/holder/fish - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish - meat_amount = 3 - - // By default they can be in any water turf. Subtypes might restrict to deep/shallow etc - var/global/list/suitable_turf_types = list( - /turf/simulated/floor/beach/water, - /turf/simulated/floor/beach/coastline, - /turf/simulated/floor/holofloor/beach/water, - /turf/simulated/floor/holofloor/beach/coastline, - /turf/simulated/floor/water - ) - - var/randomize_location = TRUE - -/mob/living/simple_mob/animal/passive/fish/Initialize() - . = ..() - - if(!default_pixel_x && randomize_location) - default_pixel_x = rand(-12, 12) - - if(!default_pixel_y && randomize_location) - default_pixel_y = rand(-6, 10) - -// Makes the AI unable to willingly go on land. -/mob/living/simple_mob/animal/passive/fish/IMove(turf/newloc, safety = TRUE) - if(is_type_in_list(newloc, suitable_turf_types)) - return ..() // Procede as normal. - return MOVEMENT_FAILED // Don't leave the water! - -// Take damage if we are not in water -/mob/living/simple_mob/animal/passive/fish/handle_breathing() - if(istype(loc, /obj/item/glass_jar/fish)) - var/obj/item/glass_jar/fish/F = loc - if(F.filled) - return - - var/turf/T = get_turf(src) - if(T && !is_type_in_list(T, suitable_turf_types)) - if(prob(50)) - say(pick("Blub", "Glub", "Burble")) - adjustBruteLoss(unsuitable_atoms_damage) - -// Subtypes. -/mob/living/simple_mob/animal/passive/fish/bass - name = "bass" - tt_desc = "E Micropterus notius" - icon_state = "bass-swim" - icon_living = "bass-swim" - icon_dead = "bass-dead" - -/mob/living/simple_mob/animal/passive/fish/trout - name = "trout" - tt_desc = "E Salmo trutta" - icon_state = "trout-swim" - icon_living = "trout-swim" - icon_dead = "trout-dead" - -/mob/living/simple_mob/animal/passive/fish/salmon - name = "salmon" - tt_desc = "E Oncorhynchus nerka" - icon_state = "salmon-swim" - icon_living = "salmon-swim" - icon_dead = "salmon-dead" - -/mob/living/simple_mob/animal/passive/fish/perch - name = "perch" - tt_desc = "E Perca flavescens" - icon_state = "perch-swim" - icon_living = "perch-swim" - icon_dead = "perch-dead" - -/mob/living/simple_mob/animal/passive/fish/pike - name = "pike" - tt_desc = "E Esox aquitanicus" - icon_state = "pike-swim" - icon_living = "pike-swim" - icon_dead = "pike-dead" - -/mob/living/simple_mob/animal/passive/fish/koi - name = "koi" - tt_desc = "E Cyprinus rubrofuscus" - icon_state = "koi-swim" - icon_living = "koi-swim" - icon_dead = "koi-dead" - -/datum/category_item/catalogue/fauna/javelin - name = "Sivian Fauna - Javelin Shark" - desc = "Classification: S Cetusan minimalix\ -

                    \ - A small breed of fatty shark native to the waters near the Ullran Expanse.\ - The creatures are not known to attack humans or larger animals, possibly \ - due to their size. It is speculated that they are actually scavengers, \ - as they are most commonly found near the gulf floor. \ -
                    \ - The Javelin's reproductive cycle only recurs between three and four \ - Sivian years. \ -
                    \ - These creatures are considered a protected species, and thus require an \ - up-to-date license to be hunted." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/animal/passive/fish/javelin - name = "javelin" - tt_desc = "S Cetusan minimalix" - icon_state = "javelin-swim" - icon_living = "javelin-swim" - icon_dead = "javelin-dead" - - catalogue_data = list(/datum/category_item/catalogue/fauna/javelin) - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif - -/datum/category_item/catalogue/fauna/icebass - name = "Sivian Fauna - Glitter Bass" - desc = "Classification: X Micropterus notius crotux\ -

                    \ - Initially a genetically engineered hybrid of the common Earth bass and \ - the Sivian Rock-Fish. These were designed to deal with the invasive \ - fish species, however to their creators' dismay, they instead \ - began to form their own passive niche. \ -
                    \ - Due to the brilliant reflective scales earning them their name, the \ - animals pose a specific issue for Sivian animals relying on \ - bioluminesence to aid in their hunt. \ -
                    \ - Despite their beauty, they are considered an invasive species." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/animal/passive/fish/icebass - name = "glitter bass" - tt_desc = "X Micropterus notius crotux" - icon_state = "sifbass-swim" - icon_living = "sifbass-swim" - icon_dead = "sifbass-dead" - - catalogue_data = list(/datum/category_item/catalogue/fauna/icebass) - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif - - var/max_red = 150 - var/min_red = 50 - - var/max_blue = 255 - var/min_blue = 50 - - var/max_green = 150 - var/min_green = 50 - - var/dorsal_color = "#FFFFFF" - var/belly_color = "#FFFFFF" - - var/image/dorsal_image - var/image/belly_image - -/mob/living/simple_mob/animal/passive/fish/icebass/Initialize() - . = ..() - dorsal_color = rgb(rand(min_red,max_red), rand(min_green,max_green), rand(min_blue,max_blue)) - belly_color = rgb(rand(min_red,max_red), rand(min_green,max_green), rand(min_blue,max_blue)) - update_icon() - -/mob/living/simple_mob/animal/passive/fish/icebass/update_icon() - cut_overlays() - ..() - - if(!dorsal_image) - dorsal_image = image(icon, "[icon_state]_mask-body") - if(!belly_image) - belly_image = image(icon, "[icon_state]_mask-belly") - - dorsal_image.icon_state = "[icon_state]_mask-body" - belly_image.icon_state = "[icon_state]_mask-belly" - - dorsal_image.color = dorsal_color - belly_image.color = belly_color - - add_overlay(dorsal_image) - add_overlay(belly_image) - -/datum/category_item/catalogue/fauna/rockfish - name = "Sivian Fauna - Rock Puffer" - desc = "Classification: S Tetraodontidae scopulix\ -

                    \ - A species strangely resembling the puffer-fish of Earth. These \ - creatures do not use toxic spines to protect themselves, instead \ - utilizing an incredibly durable exoskeleton that is molded by the \ - expansion of its ventral fluid bladders. \ -
                    \ - Rock Puffers or 'Rock-fish' are often host to smaller creatures which \ - maneuver their way into the gap between the fish's body and shell. \ -
                    \ - The species is also capable of pulling its vibrantly colored head into \ - the safer confines of its shell, the action being utilized in their \ - attempts to find a mate." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/animal/passive/fish/rockfish - name = "rock-fish" - tt_desc = "S Tetraodontidae scopulix" - icon_state = "rockfish-swim" - icon_living = "rockfish-swim" - icon_dead = "rockfish-dead" - - catalogue_data = list(/datum/category_item/catalogue/fauna/rockfish) - - armor = list( - "melee" = 90, - "bullet" = 50, - "laser" = -15, - "energy" = 30, - "bomb" = 30, - "bio" = 100, - "rad" = 100) - - var/max_red = 255 - var/min_red = 50 - - var/max_blue = 255 - var/min_blue = 50 - - var/max_green = 255 - var/min_green = 50 - - var/head_color = "#FFFFFF" - - var/image/head_image - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif - -/mob/living/simple_mob/animal/passive/fish/rockfish/Initialize() - . = ..() - head_color = rgb(rand(min_red,max_red), rand(min_green,max_green), rand(min_blue,max_blue)) - update_icon() - -/mob/living/simple_mob/animal/passive/fish/rockfish/update_icon() - cut_overlays() - ..() - if(!head_image) - head_image = image(icon, "[icon_state]_mask") - - head_image.icon_state = "[icon_state]_mask" - - head_image.color = head_color - - add_overlay(head_image) - -/datum/category_item/catalogue/fauna/solarfish - name = "Sivian Fauna - Solar Fin" - desc = "Classification: S Exocoetidae solarin\ -

                    \ - An incredibly rare species of Sivian fish.\ - The solar-fin missile fish is a specialized omnivore capable of \ - catching insects or small birds venturing too close to the water's \ - surface. \ -
                    \ - The glimmering fins of the solar-fin are actually biofluorescent, \ - 'charged' by the creature basking at the surface of the water, most \ - commonly by the edge of an ice-shelf, as a rapid means of cover. \ -
                    \ - These creatures are considered a protected species, and thus require an \ - up-to-date license to be hunted." - value = CATALOGUER_REWARD_HARD - -/mob/living/simple_mob/animal/passive/fish/solarfish - name = "sun-fin" - tt_desc = "S Exocoetidae solarin" - icon_state = "solarfin-swim" - icon_living = "solarfin-swim" - icon_dead = "solarfin-dead" - - catalogue_data = list(/datum/category_item/catalogue/fauna/solarfish) - - has_eye_glow = TRUE - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif - -/datum/category_item/catalogue/fauna/murkin - name = "Sivian Fauna - Murkfish" - desc = "Classification: S Perca lutux\ -

                    \ - A small, Sivian fish most known for its bland-ness.\ -
                    \ - The species is incredibly close in appearance to the Earth \ - perch, aside from its incredibly tall dorsal fin. The animals use \ - the fin to assess the wind direction while near the surface. \ -
                    \ - The murkfish earns its name from the fact its dense meat tastes like mud \ - thanks to a specially formed protein, most likely an adaptation to \ - protect the species from predation." - value = CATALOGUER_REWARD_TRIVIAL - -/mob/living/simple_mob/animal/passive/fish/murkin - name = "murkin" - tt_desc = "S Perca lutux" - - icon_state = "murkin-swim" - icon_living = "murkin-swim" - icon_dead = "murkin-dead" - - catalogue_data = list(/datum/category_item/catalogue/fauna/murkin) - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/sif/murkfish - -/decl/mob_organ_names/fish +// Different types of fish! They are all subtypes of this tho +/datum/category_item/catalogue/fauna/invasive_fish + name = "Invasive Fauna - Fish" + desc = "This fish is considered an invasive species according \ + to Sivian wildlife regulations. Removal or relocation is advised." + value = CATALOGUER_REWARD_TRIVIAL + +/mob/living/simple_mob/animal/passive/fish + name = "fish" + desc = "Its a fishy. No touchy fishy." + icon = 'icons/mob/fish.dmi' + item_state = "fish" + + //catalogue_data = list(/datum/category_item/catalogue/fauna/invasive_fish) //TODO: write non-sif lore + + mob_size = MOB_SMALL + // So fish are actually underwater. + plane = TURF_PLANE + layer = UNDERWATER_LAYER + + organ_names = /decl/mob_organ_names/fish + + holder_type = /obj/item/weapon/holder/fish + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish + meat_amount = 3 + + // By default they can be in any water turf. Subtypes might restrict to deep/shallow etc + var/global/list/suitable_turf_types = list( + /turf/simulated/floor/beach/water, + /turf/simulated/floor/beach/coastline, + /turf/simulated/floor/holofloor/beach/water, + /turf/simulated/floor/holofloor/beach/coastline, + /turf/simulated/floor/water + ) + + var/randomize_location = TRUE + +/mob/living/simple_mob/animal/passive/fish/Initialize() + . = ..() + + if(!default_pixel_x && randomize_location) + default_pixel_x = rand(-12, 12) + + if(!default_pixel_y && randomize_location) + default_pixel_y = rand(-6, 10) + +// Makes the AI unable to willingly go on land. +/mob/living/simple_mob/animal/passive/fish/IMove(turf/newloc, safety = TRUE) + if(is_type_in_list(newloc, suitable_turf_types)) + return ..() // Procede as normal. + return MOVEMENT_FAILED // Don't leave the water! + +// Take damage if we are not in water +/mob/living/simple_mob/animal/passive/fish/handle_breathing() + if(istype(loc, /obj/item/glass_jar/fish)) + var/obj/item/glass_jar/fish/F = loc + if(F.filled) + return + + var/turf/T = get_turf(src) + if(T && !is_type_in_list(T, suitable_turf_types)) + if(prob(50)) + say(pick("Blub", "Glub", "Burble")) + adjustBruteLoss(unsuitable_atoms_damage) + +// Subtypes. +/mob/living/simple_mob/animal/passive/fish/bass + name = "bass" + tt_desc = "E Micropterus notius" + icon_state = "bass-swim" + icon_living = "bass-swim" + icon_dead = "bass-dead" + +/mob/living/simple_mob/animal/passive/fish/trout + name = "trout" + tt_desc = "E Salmo trutta" + icon_state = "trout-swim" + icon_living = "trout-swim" + icon_dead = "trout-dead" + +/mob/living/simple_mob/animal/passive/fish/salmon + name = "salmon" + tt_desc = "E Oncorhynchus nerka" + icon_state = "salmon-swim" + icon_living = "salmon-swim" + icon_dead = "salmon-dead" + +/mob/living/simple_mob/animal/passive/fish/perch + name = "perch" + tt_desc = "E Perca flavescens" + icon_state = "perch-swim" + icon_living = "perch-swim" + icon_dead = "perch-dead" + +/mob/living/simple_mob/animal/passive/fish/pike + name = "pike" + tt_desc = "E Esox aquitanicus" + icon_state = "pike-swim" + icon_living = "pike-swim" + icon_dead = "pike-dead" + +/mob/living/simple_mob/animal/passive/fish/koi + name = "koi" + tt_desc = "E Cyprinus rubrofuscus" + icon_state = "koi-swim" + icon_living = "koi-swim" + icon_dead = "koi-dead" + +/datum/category_item/catalogue/fauna/javelin + name = "Sivian Fauna - Javelin Shark" + desc = "Classification: S Cetusan minimalix\ +

                    \ + A small breed of fatty shark native to the waters near the Ullran Expanse.\ + The creatures are not known to attack humans or larger animals, possibly \ + due to their size. It is speculated that they are actually scavengers, \ + as they are most commonly found near the gulf floor. \ +
                    \ + The Javelin's reproductive cycle only recurs between three and four \ + Sivian years. \ +
                    \ + These creatures are considered a protected species, and thus require an \ + up-to-date license to be hunted." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/passive/fish/javelin + name = "javelin" + tt_desc = "S Cetusan minimalix" + icon_state = "javelin-swim" + icon_living = "javelin-swim" + icon_dead = "javelin-dead" + + catalogue_data = list(/datum/category_item/catalogue/fauna/javelin) + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif + +/datum/category_item/catalogue/fauna/icebass + name = "Sivian Fauna - Glitter Bass" + desc = "Classification: X Micropterus notius crotux\ +

                    \ + Initially a genetically engineered hybrid of the common Earth bass and \ + the Sivian Rock-Fish. These were designed to deal with the invasive \ + fish species, however to their creators' dismay, they instead \ + began to form their own passive niche. \ +
                    \ + Due to the brilliant reflective scales earning them their name, the \ + animals pose a specific issue for Sivian animals relying on \ + bioluminesence to aid in their hunt. \ +
                    \ + Despite their beauty, they are considered an invasive species." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/passive/fish/icebass + name = "glitter bass" + tt_desc = "X Micropterus notius crotux" + icon_state = "sifbass-swim" + icon_living = "sifbass-swim" + icon_dead = "sifbass-dead" + + catalogue_data = list(/datum/category_item/catalogue/fauna/icebass) + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif + + var/max_red = 150 + var/min_red = 50 + + var/max_blue = 255 + var/min_blue = 50 + + var/max_green = 150 + var/min_green = 50 + + var/dorsal_color = "#FFFFFF" + var/belly_color = "#FFFFFF" + + var/image/dorsal_image + var/image/belly_image + +/mob/living/simple_mob/animal/passive/fish/icebass/Initialize() + . = ..() + dorsal_color = rgb(rand(min_red,max_red), rand(min_green,max_green), rand(min_blue,max_blue)) + belly_color = rgb(rand(min_red,max_red), rand(min_green,max_green), rand(min_blue,max_blue)) + update_icon() + +/mob/living/simple_mob/animal/passive/fish/icebass/update_icon() + cut_overlays() + ..() + + if(!dorsal_image) + dorsal_image = image(icon, "[icon_state]_mask-body") + if(!belly_image) + belly_image = image(icon, "[icon_state]_mask-belly") + + dorsal_image.icon_state = "[icon_state]_mask-body" + belly_image.icon_state = "[icon_state]_mask-belly" + + dorsal_image.color = dorsal_color + belly_image.color = belly_color + + add_overlay(dorsal_image) + add_overlay(belly_image) + +/datum/category_item/catalogue/fauna/rockfish + name = "Sivian Fauna - Rock Puffer" + desc = "Classification: S Tetraodontidae scopulix\ +

                    \ + A species strangely resembling the puffer-fish of Earth. These \ + creatures do not use toxic spines to protect themselves, instead \ + utilizing an incredibly durable exoskeleton that is molded by the \ + expansion of its ventral fluid bladders. \ +
                    \ + Rock Puffers or 'Rock-fish' are often host to smaller creatures which \ + maneuver their way into the gap between the fish's body and shell. \ +
                    \ + The species is also capable of pulling its vibrantly colored head into \ + the safer confines of its shell, the action being utilized in their \ + attempts to find a mate." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/passive/fish/rockfish + name = "rock-fish" + tt_desc = "S Tetraodontidae scopulix" + icon_state = "rockfish-swim" + icon_living = "rockfish-swim" + icon_dead = "rockfish-dead" + + catalogue_data = list(/datum/category_item/catalogue/fauna/rockfish) + + armor = list( + "melee" = 90, + "bullet" = 50, + "laser" = -15, + "energy" = 30, + "bomb" = 30, + "bio" = 100, + "rad" = 100) + + var/max_red = 255 + var/min_red = 50 + + var/max_blue = 255 + var/min_blue = 50 + + var/max_green = 255 + var/min_green = 50 + + var/head_color = "#FFFFFF" + + var/image/head_image + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif + +/mob/living/simple_mob/animal/passive/fish/rockfish/Initialize() + . = ..() + head_color = rgb(rand(min_red,max_red), rand(min_green,max_green), rand(min_blue,max_blue)) + update_icon() + +/mob/living/simple_mob/animal/passive/fish/rockfish/update_icon() + cut_overlays() + ..() + if(!head_image) + head_image = image(icon, "[icon_state]_mask") + + head_image.icon_state = "[icon_state]_mask" + + head_image.color = head_color + + add_overlay(head_image) + +/datum/category_item/catalogue/fauna/solarfish + name = "Sivian Fauna - Solar Fin" + desc = "Classification: S Exocoetidae solarin\ +

                    \ + An incredibly rare species of Sivian fish.\ + The solar-fin missile fish is a specialized omnivore capable of \ + catching insects or small birds venturing too close to the water's \ + surface. \ +
                    \ + The glimmering fins of the solar-fin are actually biofluorescent, \ + 'charged' by the creature basking at the surface of the water, most \ + commonly by the edge of an ice-shelf, as a rapid means of cover. \ +
                    \ + These creatures are considered a protected species, and thus require an \ + up-to-date license to be hunted." + value = CATALOGUER_REWARD_HARD + +/mob/living/simple_mob/animal/passive/fish/solarfish + name = "sun-fin" + tt_desc = "S Exocoetidae solarin" + icon_state = "solarfin-swim" + icon_living = "solarfin-swim" + icon_dead = "solarfin-dead" + + catalogue_data = list(/datum/category_item/catalogue/fauna/solarfish) + + has_eye_glow = TRUE + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif + +/datum/category_item/catalogue/fauna/murkin + name = "Sivian Fauna - Murkfish" + desc = "Classification: S Perca lutux\ +

                    \ + A small, Sivian fish most known for its bland-ness.\ +
                    \ + The species is incredibly close in appearance to the Earth \ + perch, aside from its incredibly tall dorsal fin. The animals use \ + the fin to assess the wind direction while near the surface. \ +
                    \ + The murkfish earns its name from the fact its dense meat tastes like mud \ + thanks to a specially formed protein, most likely an adaptation to \ + protect the species from predation." + value = CATALOGUER_REWARD_TRIVIAL + +/mob/living/simple_mob/animal/passive/fish/murkin + name = "murkin" + tt_desc = "S Perca lutux" + + icon_state = "murkin-swim" + icon_living = "murkin-swim" + icon_dead = "murkin-dead" + + catalogue_data = list(/datum/category_item/catalogue/fauna/murkin) + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/sif/murkfish + +/decl/mob_organ_names/fish hit_zones = list("head", "body", "dorsal fin", "left pectoral fin", "right pectoral fin", "tail fin") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/jerboa_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/jerboa_vr.dm index 7f40761eb8e..bc9a0c91185 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/jerboa_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/jerboa_vr.dm @@ -1,15 +1,15 @@ -/mob/living/simple_mob/animal/passive/mouse/jerboa - name = "jerboa" - real_name = "jerboa" - desc = "It's a jerboa, a particularly fast desert mouse" - tt_desc = "E Dipodidae musculus" - icon = 'icons/mob/animal_vr.dmi' - icon_state = "mouse_brown" - item_state = "mouse_brown" - icon_living = "mouse_brown" - icon_dead = "mouse_brown_dead" - kitchen_tag = "rodent" - - movement_cooldown = -2 - - body_color = "brown" +/mob/living/simple_mob/animal/passive/mouse/jerboa + name = "jerboa" + real_name = "jerboa" + desc = "It's a jerboa, a particularly fast desert mouse" + tt_desc = "E Dipodidae musculus" + icon = 'icons/mob/animal_vr.dmi' + icon_state = "mouse_brown" + item_state = "mouse_brown" + icon_living = "mouse_brown" + icon_dead = "mouse_brown_dead" + kitchen_tag = "rodent" + + movement_cooldown = -2 + + body_color = "brown" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm index 905d7f2f197..ecb6765c3fd 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm @@ -1,64 +1,64 @@ -/mob/living/simple_mob/animal/passive/lizard - name = "lizard" - desc = "A cute, tiny lizard." - tt_desc = "E Anolis cuvieri" - - icon_state = "lizard_green" - icon_living = "lizard_green" - icon_dead = "lizard_green_dead" - - health = 5 - maxHealth = 5 - mob_size = MOB_MINISCULE - - response_help = "pets" - response_disarm = "shoos" - response_harm = "stomps on" - - attacktext = list("bitten") - melee_damage_lower = 1 - melee_damage_upper = 2 - - speak_emote = list("hisses") - - say_list_type = /datum/say_list/lizard - - meat_amount = 1 - - var/body_color // Green, red, orange, yellow or cyan. Keep blank for random (including rare redblue) - -/mob/living/simple_mob/animal/passive/lizard/Initialize() - .=..() - - if(!body_color) - if(rand(1,1000) == 1) - body_color = "redblue" - else - body_color = pick(list("green","red","orange","yellow","cyan")) - icon_state = "lizard_[body_color]" - item_state = "lizard_[body_color]" - icon_living = "lizard_[body_color]" - icon_dead = "lizard_[body_color]_dead" - if(body_color == "redblue") - desc = "A cute, tiny, red lizard with distinctive blueish markings on its tiny limbs. Seems rare!" - else - desc = "A cute, tiny, [body_color] lizard." - -/mob/living/simple_mob/animal/passive/lizard/large - desc = "A cute, big lizard." - maxHealth = 20 - health = 20 - - melee_damage_lower = 5 - melee_damage_upper = 15 - - attack_sharp = TRUE - -/mob/living/simple_mob/animal/passive/lizard/large/Initialize() - . = ..() - adjust_scale(rand(12, 20) / 10) -/mob/living/simple_mob/animal/passive/lizard/large/defensive - maxHealth = 30 - health = 30 - - ai_holder_type = /datum/ai_holder/simple_mob/retaliate/cooperative +/mob/living/simple_mob/animal/passive/lizard + name = "lizard" + desc = "A cute, tiny lizard." + tt_desc = "E Anolis cuvieri" + + icon_state = "lizard_green" + icon_living = "lizard_green" + icon_dead = "lizard_green_dead" + + health = 5 + maxHealth = 5 + mob_size = MOB_MINISCULE + + response_help = "pets" + response_disarm = "shoos" + response_harm = "stomps on" + + attacktext = list("bitten") + melee_damage_lower = 1 + melee_damage_upper = 2 + + speak_emote = list("hisses") + + say_list_type = /datum/say_list/lizard + + meat_amount = 1 + + var/body_color // Green, red, orange, yellow or cyan. Keep blank for random (including rare redblue) + +/mob/living/simple_mob/animal/passive/lizard/Initialize() + .=..() + + if(!body_color) + if(rand(1,1000) == 1) + body_color = "redblue" + else + body_color = pick(list("green","red","orange","yellow","cyan")) + icon_state = "lizard_[body_color]" + item_state = "lizard_[body_color]" + icon_living = "lizard_[body_color]" + icon_dead = "lizard_[body_color]_dead" + if(body_color == "redblue") + desc = "A cute, tiny, red lizard with distinctive blueish markings on its tiny limbs. Seems rare!" + else + desc = "A cute, tiny, [body_color] lizard." + +/mob/living/simple_mob/animal/passive/lizard/large + desc = "A cute, big lizard." + maxHealth = 20 + health = 20 + + melee_damage_lower = 5 + melee_damage_upper = 15 + + attack_sharp = TRUE + +/mob/living/simple_mob/animal/passive/lizard/large/Initialize() + . = ..() + adjust_scale(rand(12, 20) / 10) +/mob/living/simple_mob/animal/passive/lizard/large/defensive + maxHealth = 30 + health = 30 + + ai_holder_type = /datum/ai_holder/simple_mob/retaliate/cooperative diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/misc.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/misc.dm index 3dabbb6430a..4bfba0a4439 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/misc.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/misc.dm @@ -1,39 +1,39 @@ -/mob/living/simple_mob/animal/passive/yithian - name = "yithian" - desc = "A friendly creature vaguely resembling an oversized snail without a shell." - tt_desc = "J Escargot escargot" // a product of Jade, which is a planet that totally exists - - icon_state = "yithian" - icon_living = "yithian" - icon_dead = "yithian_dead" - icon = 'icons/jungle.dmi' - - organ_names = /decl/mob_organ_names/yithian - - // Same stats as lizards. - health = 5 - maxHealth = 5 - mob_size = MOB_MINISCULE - -/mob/living/simple_mob/animal/passive/tindalos - name = "tindalos" - desc = "It looks like a large, flightless grasshopper." - tt_desc = "J Locusta bruchus" - - icon_state = "tindalos" - icon_living = "tindalos" - icon_dead = "tindalos_dead" - icon = 'icons/jungle.dmi' - - organ_names = /decl/mob_organ_names/tindalos - - // Same stats as lizards. - health = 5 - maxHealth = 5 - mob_size = MOB_MINISCULE - -/decl/mob_organ_names/yithian - hit_zones = list("head", "abdomen", "left foreleg", "right foreleg", "left hind leg", "right hind leg") - -/decl/mob_organ_names/tindalos +/mob/living/simple_mob/animal/passive/yithian + name = "yithian" + desc = "A friendly creature vaguely resembling an oversized snail without a shell." + tt_desc = "J Escargot escargot" // a product of Jade, which is a planet that totally exists + + icon_state = "yithian" + icon_living = "yithian" + icon_dead = "yithian_dead" + icon = 'icons/jungle.dmi' + + organ_names = /decl/mob_organ_names/yithian + + // Same stats as lizards. + health = 5 + maxHealth = 5 + mob_size = MOB_MINISCULE + +/mob/living/simple_mob/animal/passive/tindalos + name = "tindalos" + desc = "It looks like a large, flightless grasshopper." + tt_desc = "J Locusta bruchus" + + icon_state = "tindalos" + icon_living = "tindalos" + icon_dead = "tindalos_dead" + icon = 'icons/jungle.dmi' + + organ_names = /decl/mob_organ_names/tindalos + + // Same stats as lizards. + health = 5 + maxHealth = 5 + mob_size = MOB_MINISCULE + +/decl/mob_organ_names/yithian + hit_zones = list("head", "abdomen", "left foreleg", "right foreleg", "left hind leg", "right hind leg") + +/decl/mob_organ_names/tindalos hit_zones = list("head", "thorax", "abdomen", "left foreleg", "right foreleg", "left hind leg", "right hind leg", "left middle leg", "right middle leg") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm index c488d1ed28f..6450259f9c1 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm @@ -1,5 +1,5 @@ -// Passive mobs can't attack things, and will run away instead. -// They can also be displaced by all mobs. -/mob/living/simple_mob/animal/passive - ai_holder_type = /datum/ai_holder/simple_mob/passive +// Passive mobs can't attack things, and will run away instead. +// They can also be displaced by all mobs. +/mob/living/simple_mob/animal/passive + ai_holder_type = /datum/ai_holder/simple_mob/passive mob_bump_flag = 0 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/penguin.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/penguin.dm index dd312988eca..5f0f0107634 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/penguin.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/penguin.dm @@ -1,35 +1,35 @@ -/mob/living/simple_mob/animal/passive/penguin - name = "penguin" - desc = "An ungainly, waddling, cute, and VERY well-dressed bird." - tt_desc = "Aptenodytes forsteri" - icon_state = "penguin" - icon_living = "penguin" - icon_dead = "penguin_dead" - - maxHealth = 20 - health = 20 - minbodytemp = 175 // Same as Sif mobs. - - response_help = "pets" - response_disarm = "pushes aside" - response_harm = "hits" - - organ_names = /decl/mob_organ_names/penguin - - meat_amount = 3 - meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/chicken - - harm_intent_damage = 5 - melee_damage_lower = 10 - melee_damage_upper = 15 - attacktext = list("pecked") - - has_langs = list(LANGUAGE_ANIMAL) - -/mob/living/simple_mob/animal/passive/penguin/tux - name = "Tux" - desc = "A penguin that has been known to associate with gnus." - speak_emote = list("interjects") - -/decl/mob_organ_names/penguin +/mob/living/simple_mob/animal/passive/penguin + name = "penguin" + desc = "An ungainly, waddling, cute, and VERY well-dressed bird." + tt_desc = "Aptenodytes forsteri" + icon_state = "penguin" + icon_living = "penguin" + icon_dead = "penguin_dead" + + maxHealth = 20 + health = 20 + minbodytemp = 175 // Same as Sif mobs. + + response_help = "pets" + response_disarm = "pushes aside" + response_harm = "hits" + + organ_names = /decl/mob_organ_names/penguin + + meat_amount = 3 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/chicken + + harm_intent_damage = 5 + melee_damage_lower = 10 + melee_damage_upper = 15 + attacktext = list("pecked") + + has_langs = list(LANGUAGE_ANIMAL) + +/mob/living/simple_mob/animal/passive/penguin/tux + name = "Tux" + desc = "A penguin that has been known to associate with gnus." + speak_emote = list("interjects") + +/decl/mob_organ_names/penguin hit_zones = list("chest", "left leg", "right leg", "left flipper", "right flipper", "head") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm index 4eff5abe4e9..ba41d4388e2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm @@ -1,93 +1,93 @@ -// Base bird type. - -/mob/living/simple_mob/animal/passive/bird - name = "bird" - desc = "A domesticated bird. Tweet tweet!" - player_msg = "You are able to fly." - - icon = 'icons/mob/birds.dmi' - icon_state = "parrot" - item_state = null - icon_rest = "parrot-held" - icon_dead = "parrot-dead" - - pass_flags = PASSTABLE - - health = 30 - maxHealth = 30 - melee_damage_lower = 3 - melee_damage_upper = 3 - - movement_cooldown = -1 - hovering = TRUE // Birds can fly. - softfall = TRUE - parachuting = TRUE - - meat_amount = 1 - meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/chicken - - attacktext = list("clawed", "pecked") - speak_emote = list("chirps", "caws") - has_langs = list(LANGUAGE_ANIMAL) - response_help = "pets" - response_disarm = "gently moves aside" - response_harm = "swats" - organ_names = /decl/mob_organ_names/bird - - say_list_type = /datum/say_list/bird - holder_type = /obj/item/weapon/holder/bird - -/datum/say_list/bird - speak = list("Chirp!","Caw!","Screech!","Squawk!") - emote_hear = list("chirps","caws") - emote_see = list("shakes their head", "ruffles their feathers") - -// Subtypes for birbs. -/mob/living/simple_mob/animal/passive/bird/black_bird - name = "common blackbird" - desc = "A species of bird, both the males and females are known to be territorial on their breeding grounds." - icon_state = "commonblackbird" - icon_dead = "commonblackbird-dead" - tt_desc = "E Turdus merula" - icon_scale_x = 0.5 - icon_scale_y = 0.5 - -/mob/living/simple_mob/animal/passive/bird/azure_tit - name = "azure tit" - desc = "A species of bird, colored blue and white." - icon_state = "azuretit" - icon_dead = "azuretit-dead" - tt_desc = "E Cyanistes cyanus" - icon_scale_x = 0.5 - icon_scale_y = 0.5 - -/mob/living/simple_mob/animal/passive/bird/european_robin - name = "european robin" - desc = "A species of bird, they have been studied for their sense of magnetoreception." - icon_state = "europeanrobin" - icon_dead = "europeanrobin-dead" - tt_desc = "E Erithacus rubecula" - icon_scale_x = 0.5 - icon_scale_y = 0.5 - -/mob/living/simple_mob/animal/passive/bird/goldcrest - name = "goldcrest" - desc = "A species of bird, they were once called 'king of the birds' in ancient human folklore, for their golden crest. \ - Today, their scientific name still elude towards this, with regulus, meaning petty king." - icon_state = "goldcrest" - icon_dead = "goldcrest-dead" - tt_desc = "E Regulus regulus" - icon_scale_x = 0.5 - icon_scale_y = 0.5 - -/mob/living/simple_mob/animal/passive/bird/ringneck_dove - name = "ringneck dove" - desc = "A species of bird. They are also known as the barbary dove, and have a distinct ring-like shape around the back of their neck." - icon_state = "ringneckdove" - icon_dead = "ringneckdove-dead" - tt_desc = "E Streptopelia risoria" // This is actually disputed IRL but since we can't tell the future it'll stay the same for 500+ years. - icon_scale_x = 0.5 - icon_scale_y = 0.5 - -/decl/mob_organ_names/bird - hit_zones = list("head", "chest", "left leg", "right leg", "left wing", "right wing") +// Base bird type. + +/mob/living/simple_mob/animal/passive/bird + name = "bird" + desc = "A domesticated bird. Tweet tweet!" + player_msg = "You are able to fly." + + icon = 'icons/mob/birds.dmi' + icon_state = "parrot" + item_state = null + icon_rest = "parrot-held" + icon_dead = "parrot-dead" + + pass_flags = PASSTABLE + + health = 30 + maxHealth = 30 + melee_damage_lower = 3 + melee_damage_upper = 3 + + movement_cooldown = -1 + hovering = TRUE // Birds can fly. + softfall = TRUE + parachuting = TRUE + + meat_amount = 1 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/chicken + + attacktext = list("clawed", "pecked") + speak_emote = list("chirps", "caws") + has_langs = list(LANGUAGE_ANIMAL) + response_help = "pets" + response_disarm = "gently moves aside" + response_harm = "swats" + organ_names = /decl/mob_organ_names/bird + + say_list_type = /datum/say_list/bird + holder_type = /obj/item/weapon/holder/bird + +/datum/say_list/bird + speak = list("Chirp!","Caw!","Screech!","Squawk!") + emote_hear = list("chirps","caws") + emote_see = list("shakes their head", "ruffles their feathers") + +// Subtypes for birbs. +/mob/living/simple_mob/animal/passive/bird/black_bird + name = "common blackbird" + desc = "A species of bird, both the males and females are known to be territorial on their breeding grounds." + icon_state = "commonblackbird" + icon_dead = "commonblackbird-dead" + tt_desc = "E Turdus merula" + icon_scale_x = 0.5 + icon_scale_y = 0.5 + +/mob/living/simple_mob/animal/passive/bird/azure_tit + name = "azure tit" + desc = "A species of bird, colored blue and white." + icon_state = "azuretit" + icon_dead = "azuretit-dead" + tt_desc = "E Cyanistes cyanus" + icon_scale_x = 0.5 + icon_scale_y = 0.5 + +/mob/living/simple_mob/animal/passive/bird/european_robin + name = "european robin" + desc = "A species of bird, they have been studied for their sense of magnetoreception." + icon_state = "europeanrobin" + icon_dead = "europeanrobin-dead" + tt_desc = "E Erithacus rubecula" + icon_scale_x = 0.5 + icon_scale_y = 0.5 + +/mob/living/simple_mob/animal/passive/bird/goldcrest + name = "goldcrest" + desc = "A species of bird, they were once called 'king of the birds' in ancient human folklore, for their golden crest. \ + Today, their scientific name still elude towards this, with regulus, meaning petty king." + icon_state = "goldcrest" + icon_dead = "goldcrest-dead" + tt_desc = "E Regulus regulus" + icon_scale_x = 0.5 + icon_scale_y = 0.5 + +/mob/living/simple_mob/animal/passive/bird/ringneck_dove + name = "ringneck dove" + desc = "A species of bird. They are also known as the barbary dove, and have a distinct ring-like shape around the back of their neck." + icon_state = "ringneckdove" + icon_dead = "ringneckdove-dead" + tt_desc = "E Streptopelia risoria" // This is actually disputed IRL but since we can't tell the future it'll stay the same for 500+ years. + icon_scale_x = 0.5 + icon_scale_y = 0.5 + +/decl/mob_organ_names/bird + hit_zones = list("head", "chest", "left leg", "right leg", "left wing", "right wing") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm index 51ae8f87384..4f791e8caa2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm @@ -1,285 +1,285 @@ -var/list/_cat_default_emotes = list( - /decl/emote/visible, - /decl/emote/visible/scratch, - /decl/emote/visible/drool, - /decl/emote/visible/nod, - /decl/emote/visible/sway, - /decl/emote/visible/sulk, - /decl/emote/visible/twitch, - /decl/emote/visible/twitch_v, - /decl/emote/visible/dance, - /decl/emote/visible/roll, - /decl/emote/visible/shake, - /decl/emote/visible/jump, - /decl/emote/visible/shiver, - /decl/emote/visible/collapse, - /decl/emote/visible/spin, - /decl/emote/visible/sidestep, - /decl/emote/audible, - /decl/emote/audible/hiss, - /decl/emote/audible/whimper, - /decl/emote/audible/gasp, - /decl/emote/audible/scretch, - /decl/emote/audible/choke, - /decl/emote/audible/moan, - /decl/emote/audible/gnarl, - /decl/emote/audible/purr, - /decl/emote/audible/purrlong -) - -/mob/living/simple_mob/animal/passive/cat - name = "cat" - desc = "A domesticated, feline pet. Has a tendency to adopt crewmembers." - tt_desc = "E Felis silvestris catus" - icon = 'icons/mob/pets.dmi' - icon_state = "cat2" - item_state = "cat2" - - movement_cooldown = -1 - - meat_amount = 1 - see_in_dark = 6 // Not sure if this actually works. - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "kicks" - - holder_type = /obj/item/weapon/holder/cat - mob_size = MOB_SMALL - - has_langs = list(LANGUAGE_ANIMAL) - - var/mob/living/friend = null // Our best pal, who we'll follow. Meow. - var/named = FALSE //have I been named yet? - var/friend_name = null //VOREStation Edit - Lock befriending to this character - -/mob/living/simple_mob/animal/passive/cat/Initialize() - icon_living = "[initial(icon_state)]" - icon_dead = "[initial(icon_state)]_dead" - icon_rest = "[initial(icon_state)]_rest" - update_icon() - return ..() - -/mob/living/simple_mob/animal/passive/cat/get_available_emotes() - return global._cat_default_emotes.Copy() - -/mob/living/simple_mob/animal/passive/cat/handle_special() - if(!stat && prob(2)) // spooky - var/mob/observer/dead/spook = locate() in range(src, 5) - if(spook) - var/turf/T = get_turf(spook) - var/list/visible = list() - for(var/obj/O in T.contents) - if(!O.invisibility && O.name) - visible += O - if(visible.len) - var/atom/A = pick(visible) - visible_emote("suddenly stops and stares at something unseen[istype(A) ? " near [A]":""].") - -// Instakills mice. -/mob/living/simple_mob/animal/passive/cat/apply_melee_effects(var/atom/A) - if(ismouse(A)) - var/mob/living/simple_mob/animal/passive/mouse/mouse = A - if(mouse.getMaxHealth() < 20) // In case a badmin makes giant mice or something. - mouse.splat() - visible_emote(pick("bites \the [mouse]!", "toys with \the [mouse].", "chomps on \the [mouse]!")) - else - ..() - -/mob/living/simple_mob/animal/passive/cat/IIsAlly(mob/living/L) - if(L == friend) // Always be pals with our special friend. - return TRUE - - . = ..() - - if(.) // We're pals, but they might be a dirty mouse... - if(ismouse(L)) - return FALSE // Cats and mice can never get along. - -/mob/living/simple_mob/animal/passive/cat/verb/become_friends() - set name = "Become Friends" - set category = "IC" - set src in view(1) - - var/mob/living/L = usr - if(!istype(L)) - return // Fuck off ghosts. - - if(friend) - if(friend == usr) - to_chat(L, span("notice", "\The [src] is already your friend! Meow!")) - return - else - to_chat(L, span("warning", "\The [src] ignores you.")) - return - - //VOREStation Edit Start - Adds friend_name var checks - if(!friend_name || L.real_name == friend_name) - friend = L - face_atom(L) - to_chat(L, span("notice", "\The [src] is now your friend! Meow.")) - visible_emote(pick("nuzzles [friend].", "brushes against [friend].", "rubs against [friend].", "purrs.")) - - if(has_AI()) - var/datum/ai_holder/AI = ai_holder - AI.set_follow(friend) - else - to_chat(L, span("notice", "[src] ignores you.")) - //VOREStation Edit End - - -//RUNTIME IS ALIVE! SQUEEEEEEEE~ -/mob/living/simple_mob/animal/passive/cat/runtime - name = "Runtime" - desc = "Her fur has the look and feel of velvet, and her tail quivers occasionally." - tt_desc = "E Felis silvestris medicalis" // a hypoallergenic breed produced by NT for... medical purposes? Sure. - gender = FEMALE - icon_state = "cat" - item_state = "cat" - named = TRUE - holder_type = /obj/item/weapon/holder/cat/runtime - makes_dirt = 0 //Vorestation Edit - -/mob/living/simple_mob/animal/passive/cat/kitten - name = "kitten" - desc = "D'aaawwww!" - icon_state = "kitten" - item_state = "kitten" - gender = NEUTER - holder_type = /obj/item/weapon/holder/cat/kitten //VOREStation Edit - -/mob/living/simple_mob/animal/passive/cat/kitten/Initialize() - if(gender == NEUTER) - gender = pick(MALE, FEMALE) - return ..() - -/mob/living/simple_mob/animal/passive/cat/black - icon_state = "cat3" - item_state = "cat3" - -/mob/living/simple_mob/animal/passive/cat/bones - name = "Bones" - desc = "That's Bones the cat. He's a laid back, black cat. Meow." - gender = MALE - icon_state = "cat3" - item_state = "cat3" - named = TRUE - holder_type = /obj/item/weapon/holder/cat/fluff/bones - -// SPARKLY -/mob/living/simple_mob/animal/passive/cat/bluespace - name = "bluespace cat" - desc = "Shiny cat, shiny cat, it's not your fault." - tt_desc = "E Felis silvestris argentum" - icon_state = "bscat" - icon_living = "bscat" - icon_rest = null - icon_dead = null - makes_dirt = 0 - holder_type = /obj/item/weapon/holder/cat/bluespace - -/mob/living/simple_mob/animal/passive/cat/bluespace/death() - animate(src, alpha = 0, color = "#0000FF", time = 0.5 SECOND) - spawn(0.5 SECOND) - qdel(src) - -/mob/living/simple_mob/animal/passive/cat/bread - name = "bread cat" - desc = "Brought lunch to work." - tt_desc = "E Felis silvestris breadinum" - icon_state = "breadcat" - icon_living = "breadcat" - icon_rest = "breadcat_rest" - icon_dead = "breadcat_dead" - //icon_sit = "breadcat_sit" - makes_dirt = 0 - holder_type = /obj/item/weapon/holder/cat/breadcat - -/mob/living/simple_mob/animal/passive/cat/original - name = "original cat" - desc = "Donut steal." - tt_desc = "E Felis silvestris originalis" - icon_state = "original" - icon_living = "original" - icon_rest = "original_rest" - icon_dead = "original_dead" - //icon_sit = "original_sit" - makes_dirt = 0 - holder_type = /obj/item/weapon/holder/cat/original - -/mob/living/simple_mob/animal/passive/cat/cak - name = "cak" - desc = "Optimal combination of things?" - tt_desc = "E Felis silvestris dessertus" - icon_state = "cak" - icon_living = "cak" - icon_rest = "cak_rest" - icon_dead = "cak_dead" - //icon_sit = "cak_sit" - makes_dirt = 0 - holder_type = /obj/item/weapon/holder/cat/cak - -/mob/living/simple_mob/animal/passive/cat/space - name = "space cat" - desc = "Did someone write a song about this cat?" - tt_desc = "E Felis silvestris stellaris" - icon_state = "spacecat" - icon_living = "spacecat" - icon_rest = "spacecat_rest" - icon_dead = "spacecat_dead" - //icon_sit = "spacecat_sit" - holder_type = /obj/item/weapon/holder/cat/spacecat - makes_dirt = 0 - - minbodytemp = 0 // Minimum "okay" temperature in kelvin - maxbodytemp = 900 // Maximum of above - heat_damage_per_tick = 3 // Amount of damage applied if animal's body temperature is higher than maxbodytemp - cold_damage_per_tick = 2 // Same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp - - min_oxy = 0 // Oxygen in moles, minimum, 0 is 'no minimum' - max_oxy = 0 // Oxygen in moles, maximum, 0 is 'no maximum' - min_tox = 0 // Phoron min - max_tox = 0 // Phoron max - min_co2 = 0 // CO2 min - max_co2 = 0 // CO2 max - min_n2 = 0 // N2 min - max_n2 = 0 // N2 max - unsuitable_atoms_damage = 2 // This damage is taken when atmos doesn't fit all the requirements above - -/datum/say_list/cat - speak = list("Meow!","Esp!","Purr!","HSSSSS") - emote_hear = list("meows","mews") - emote_see = list("shakes their head", "shivers") - say_maybe_target = list("Meow?","Mew?","Mao?") - say_got_target = list("MEOW!","HSSSS!","REEER!") - -/mob/living/simple_mob/animal/passive/cat/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/pen) || istype(W, /obj/item/device/flashlight/pen)) - if(named) - to_chat(user, "\The [name] already has a name!") - else - var/tmp_name = sanitizeSafe(tgui_input_text(user, "Give \the [name] a name", "Name", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(length(tmp_name) > 50) - to_chat(user, "The name can be at most 50 characters long.") - else - to_chat(user, "You name \the [name]. Meow!") - name = tmp_name - named = TRUE - else - ..() - -/obj/item/weapon/cat_box - name = "faintly purring box" - desc = "This box is purring faintly. You're pretty sure there's a cat inside it." - icon = 'icons/obj/storage.dmi' - icon_state = "box" - var/cattype = /mob/living/simple_mob/animal/passive/cat - -/obj/item/weapon/cat_box/attack_self(var/mob/user) - var/turf/catturf = get_turf(src) - to_chat(user, "You peek into \the [name]-- and a cat jumps out!") - new cattype(catturf) - new /obj/item/stack/material/cardboard(catturf) //if i fits i sits - qdel(src) - -/obj/item/weapon/cat_box/black - cattype = /mob/living/simple_mob/animal/passive/cat/black +var/list/_cat_default_emotes = list( + /decl/emote/visible, + /decl/emote/visible/scratch, + /decl/emote/visible/drool, + /decl/emote/visible/nod, + /decl/emote/visible/sway, + /decl/emote/visible/sulk, + /decl/emote/visible/twitch, + /decl/emote/visible/twitch_v, + /decl/emote/visible/dance, + /decl/emote/visible/roll, + /decl/emote/visible/shake, + /decl/emote/visible/jump, + /decl/emote/visible/shiver, + /decl/emote/visible/collapse, + /decl/emote/visible/spin, + /decl/emote/visible/sidestep, + /decl/emote/audible, + /decl/emote/audible/hiss, + /decl/emote/audible/whimper, + /decl/emote/audible/gasp, + /decl/emote/audible/scretch, + /decl/emote/audible/choke, + /decl/emote/audible/moan, + /decl/emote/audible/gnarl, + /decl/emote/audible/purr, + /decl/emote/audible/purrlong +) + +/mob/living/simple_mob/animal/passive/cat + name = "cat" + desc = "A domesticated, feline pet. Has a tendency to adopt crewmembers." + tt_desc = "E Felis silvestris catus" + icon = 'icons/mob/pets.dmi' + icon_state = "cat2" + item_state = "cat2" + + movement_cooldown = -1 + + meat_amount = 1 + see_in_dark = 6 // Not sure if this actually works. + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + + holder_type = /obj/item/weapon/holder/cat + mob_size = MOB_SMALL + + has_langs = list(LANGUAGE_ANIMAL) + + var/mob/living/friend = null // Our best pal, who we'll follow. Meow. + var/named = FALSE //have I been named yet? + var/friend_name = null //VOREStation Edit - Lock befriending to this character + +/mob/living/simple_mob/animal/passive/cat/Initialize() + icon_living = "[initial(icon_state)]" + icon_dead = "[initial(icon_state)]_dead" + icon_rest = "[initial(icon_state)]_rest" + update_icon() + return ..() + +/mob/living/simple_mob/animal/passive/cat/get_available_emotes() + return global._cat_default_emotes.Copy() + +/mob/living/simple_mob/animal/passive/cat/handle_special() + if(!stat && prob(2)) // spooky + var/mob/observer/dead/spook = locate() in range(src, 5) + if(spook) + var/turf/T = get_turf(spook) + var/list/visible = list() + for(var/obj/O in T.contents) + if(!O.invisibility && O.name) + visible += O + if(visible.len) + var/atom/A = pick(visible) + visible_emote("suddenly stops and stares at something unseen[istype(A) ? " near [A]":""].") + +// Instakills mice. +/mob/living/simple_mob/animal/passive/cat/apply_melee_effects(var/atom/A) + if(ismouse(A)) + var/mob/living/simple_mob/animal/passive/mouse/mouse = A + if(mouse.getMaxHealth() < 20) // In case a badmin makes giant mice or something. + mouse.splat() + visible_emote(pick("bites \the [mouse]!", "toys with \the [mouse].", "chomps on \the [mouse]!")) + else + ..() + +/mob/living/simple_mob/animal/passive/cat/IIsAlly(mob/living/L) + if(L == friend) // Always be pals with our special friend. + return TRUE + + . = ..() + + if(.) // We're pals, but they might be a dirty mouse... + if(ismouse(L)) + return FALSE // Cats and mice can never get along. + +/mob/living/simple_mob/animal/passive/cat/verb/become_friends() + set name = "Become Friends" + set category = "IC" + set src in view(1) + + var/mob/living/L = usr + if(!istype(L)) + return // Fuck off ghosts. + + if(friend) + if(friend == usr) + to_chat(L, span("notice", "\The [src] is already your friend! Meow!")) + return + else + to_chat(L, span("warning", "\The [src] ignores you.")) + return + + //VOREStation Edit Start - Adds friend_name var checks + if(!friend_name || L.real_name == friend_name) + friend = L + face_atom(L) + to_chat(L, span("notice", "\The [src] is now your friend! Meow.")) + visible_emote(pick("nuzzles [friend].", "brushes against [friend].", "rubs against [friend].", "purrs.")) + + if(has_AI()) + var/datum/ai_holder/AI = ai_holder + AI.set_follow(friend) + else + to_chat(L, span("notice", "[src] ignores you.")) + //VOREStation Edit End + + +//RUNTIME IS ALIVE! SQUEEEEEEEE~ +/mob/living/simple_mob/animal/passive/cat/runtime + name = "Runtime" + desc = "Her fur has the look and feel of velvet, and her tail quivers occasionally." + tt_desc = "E Felis silvestris medicalis" // a hypoallergenic breed produced by NT for... medical purposes? Sure. + gender = FEMALE + icon_state = "cat" + item_state = "cat" + named = TRUE + holder_type = /obj/item/weapon/holder/cat/runtime + makes_dirt = 0 //Vorestation Edit + +/mob/living/simple_mob/animal/passive/cat/kitten + name = "kitten" + desc = "D'aaawwww!" + icon_state = "kitten" + item_state = "kitten" + gender = NEUTER + holder_type = /obj/item/weapon/holder/cat/kitten //VOREStation Edit + +/mob/living/simple_mob/animal/passive/cat/kitten/Initialize() + if(gender == NEUTER) + gender = pick(MALE, FEMALE) + return ..() + +/mob/living/simple_mob/animal/passive/cat/black + icon_state = "cat3" + item_state = "cat3" + +/mob/living/simple_mob/animal/passive/cat/bones + name = "Bones" + desc = "That's Bones the cat. He's a laid back, black cat. Meow." + gender = MALE + icon_state = "cat3" + item_state = "cat3" + named = TRUE + holder_type = /obj/item/weapon/holder/cat/fluff/bones + +// SPARKLY +/mob/living/simple_mob/animal/passive/cat/bluespace + name = "bluespace cat" + desc = "Shiny cat, shiny cat, it's not your fault." + tt_desc = "E Felis silvestris argentum" + icon_state = "bscat" + icon_living = "bscat" + icon_rest = null + icon_dead = null + makes_dirt = 0 + holder_type = /obj/item/weapon/holder/cat/bluespace + +/mob/living/simple_mob/animal/passive/cat/bluespace/death() + animate(src, alpha = 0, color = "#0000FF", time = 0.5 SECOND) + spawn(0.5 SECOND) + qdel(src) + +/mob/living/simple_mob/animal/passive/cat/bread + name = "bread cat" + desc = "Brought lunch to work." + tt_desc = "E Felis silvestris breadinum" + icon_state = "breadcat" + icon_living = "breadcat" + icon_rest = "breadcat_rest" + icon_dead = "breadcat_dead" + //icon_sit = "breadcat_sit" + makes_dirt = 0 + holder_type = /obj/item/weapon/holder/cat/breadcat + +/mob/living/simple_mob/animal/passive/cat/original + name = "original cat" + desc = "Donut steal." + tt_desc = "E Felis silvestris originalis" + icon_state = "original" + icon_living = "original" + icon_rest = "original_rest" + icon_dead = "original_dead" + //icon_sit = "original_sit" + makes_dirt = 0 + holder_type = /obj/item/weapon/holder/cat/original + +/mob/living/simple_mob/animal/passive/cat/cak + name = "cak" + desc = "Optimal combination of things?" + tt_desc = "E Felis silvestris dessertus" + icon_state = "cak" + icon_living = "cak" + icon_rest = "cak_rest" + icon_dead = "cak_dead" + //icon_sit = "cak_sit" + makes_dirt = 0 + holder_type = /obj/item/weapon/holder/cat/cak + +/mob/living/simple_mob/animal/passive/cat/space + name = "space cat" + desc = "Did someone write a song about this cat?" + tt_desc = "E Felis silvestris stellaris" + icon_state = "spacecat" + icon_living = "spacecat" + icon_rest = "spacecat_rest" + icon_dead = "spacecat_dead" + //icon_sit = "spacecat_sit" + holder_type = /obj/item/weapon/holder/cat/spacecat + makes_dirt = 0 + + minbodytemp = 0 // Minimum "okay" temperature in kelvin + maxbodytemp = 900 // Maximum of above + heat_damage_per_tick = 3 // Amount of damage applied if animal's body temperature is higher than maxbodytemp + cold_damage_per_tick = 2 // Same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp + + min_oxy = 0 // Oxygen in moles, minimum, 0 is 'no minimum' + max_oxy = 0 // Oxygen in moles, maximum, 0 is 'no maximum' + min_tox = 0 // Phoron min + max_tox = 0 // Phoron max + min_co2 = 0 // CO2 min + max_co2 = 0 // CO2 max + min_n2 = 0 // N2 min + max_n2 = 0 // N2 max + unsuitable_atoms_damage = 2 // This damage is taken when atmos doesn't fit all the requirements above + +/datum/say_list/cat + speak = list("Meow!","Esp!","Purr!","HSSSSS") + emote_hear = list("meows","mews") + emote_see = list("shakes their head", "shivers") + say_maybe_target = list("Meow?","Mew?","Mao?") + say_got_target = list("MEOW!","HSSSS!","REEER!") + +/mob/living/simple_mob/animal/passive/cat/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/pen) || istype(W, /obj/item/device/flashlight/pen)) + if(named) + to_chat(user, "\The [name] already has a name!") + else + var/tmp_name = sanitizeSafe(tgui_input_text(user, "Give \the [name] a name", "Name", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(length(tmp_name) > 50) + to_chat(user, "The name can be at most 50 characters long.") + else + to_chat(user, "You name \the [name]. Meow!") + name = tmp_name + named = TRUE + else + ..() + +/obj/item/weapon/cat_box + name = "faintly purring box" + desc = "This box is purring faintly. You're pretty sure there's a cat inside it." + icon = 'icons/obj/storage.dmi' + icon_state = "box" + var/cattype = /mob/living/simple_mob/animal/passive/cat + +/obj/item/weapon/cat_box/attack_self(var/mob/user) + var/turf/catturf = get_turf(src) + to_chat(user, "You peek into \the [name]-- and a cat jumps out!") + new cattype(catturf) + new /obj/item/stack/material/cardboard(catturf) //if i fits i sits + qdel(src) + +/obj/item/weapon/cat_box/black + cattype = /mob/living/simple_mob/animal/passive/cat/black diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/fennec.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/fennec.dm index 27c6cc7f09d..9adf67d779a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/fennec.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/fennec.dm @@ -1,29 +1,29 @@ -/mob/living/simple_mob/animal/passive/fennec - name = "fennec" - desc = "A fox preferring arid climates, also known as a dingler, or a goob." - tt_desc = "Vulpes Zerda" - icon_state = "fennec" - item_state = "fennec" - - movement_cooldown = -1 - - see_in_dark = 6 - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "kicks" - - holder_type = /obj/item/weapon/holder/fennec - mob_size = MOB_SMALL - - has_langs = list(LANGUAGE_ANIMAL) - -/mob/living/simple_mob/animal/passive/fennec/faux - name = "faux" - desc = "Domesticated fennec. Seems to like screaming just as much though." - -/mob/living/simple_mob/animal/passive/fennec/Initialize() - icon_living = "[initial(icon_state)]" - icon_dead = "[initial(icon_state)]_dead" - icon_rest = "[initial(icon_state)]_rest" - update_icon() - return ..() +/mob/living/simple_mob/animal/passive/fennec + name = "fennec" + desc = "A fox preferring arid climates, also known as a dingler, or a goob." + tt_desc = "Vulpes Zerda" + icon_state = "fennec" + item_state = "fennec" + + movement_cooldown = -1 + + see_in_dark = 6 + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + + holder_type = /obj/item/weapon/holder/fennec + mob_size = MOB_SMALL + + has_langs = list(LANGUAGE_ANIMAL) + +/mob/living/simple_mob/animal/passive/fennec/faux + name = "faux" + desc = "Domesticated fennec. Seems to like screaming just as much though." + +/mob/living/simple_mob/animal/passive/fennec/Initialize() + icon_living = "[initial(icon_state)]" + icon_dead = "[initial(icon_state)]_dead" + icon_rest = "[initial(icon_state)]_rest" + update_icon() + return ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm index c03327b5bd9..e985111c0c9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm @@ -1,253 +1,253 @@ -// Parrots can talk, and may repeat things it hears. -/mob/living/simple_mob/animal/passive/bird/parrot - name = "parrot" - description_info = "You can give it a headset by clicking on it with a headset. \ - To remove it, click the bird while on grab intent." - has_langs = list(LANGUAGE_GALCOM, LANGUAGE_ANIMAL) - - ai_holder_type = /datum/ai_holder/simple_mob/passive/parrot - - // A headset, so that talking parrots can yell at the crew over comms. - // If set to a type, on initialize it will be instantiated into that type. - var/obj/item/device/radio/headset/my_headset = null - -// Say list -/datum/say_list/bird/poly - speak = list( - "Poly wanna cracker!", - "Check the singulo, you chucklefucks!", - "Wire the solars, you lazy bums!", - "WHO TOOK THE DAMN HARDSUITS?", - "OH GOD ITS FREE CALL THE SHUTTLE", - "Danger! Crystal hyperstructure instability!", - "CRYSTAL DELAMINATION IMMINENT.", - "Tweet tweet, I'm a Teshari.", - "Chitters.", - "Meteors have been detected on a collision course with the station!" - ) - -// Lets the AI use headsets. -// Player-controlled parrots will need to do it manually. -/mob/living/simple_mob/animal/passive/bird/parrot/ISay(message) - if(my_headset && prob(50)) - var/list/keys = list() - for(var/channel in my_headset.channels) - var/key = get_radio_key_from_channel(channel) - if(key) - keys += key - if(keys.len) - var/key_used = pick(keys) - return say("[key_used] [message]") - return say(message) - -// Ugly saycode so parrots can use their headsets. -/mob/living/simple_mob/animal/passive/bird/parrot/handle_message_mode(message_mode, message, verb, used_radios, whispering) - ..() - if(message_mode) - if(my_headset && istype(my_headset, /obj/item/device/radio)) - my_headset.talk_into(src, message, message_mode, verb, whispering) - used_radios += my_headset - -// Clicked on while holding an object. -/mob/living/simple_mob/animal/passive/bird/parrot/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/device/radio/headset)) - give_headset(I, user) - return - return ..() - -// Clicked on by empty hand. -/mob/living/simple_mob/animal/passive/bird/parrot/attack_hand(mob/living/L) - if(L.a_intent == I_GRAB && my_headset) - remove_headset(L) - else - ..() - - -/mob/living/simple_mob/animal/passive/bird/parrot/proc/give_headset(obj/item/device/radio/headset/new_headset, mob/living/user) - if(!istype(new_headset)) - to_chat(user, span("warning", "\The [new_headset] isn't a headset.")) - return - if(my_headset) - to_chat(user, span("warning", "\The [src] is already wearing \a [my_headset].")) - return - else - user.drop_item(new_headset) - my_headset = new_headset - new_headset.forceMove(src) - to_chat(user, span("warning", "You place \a [new_headset] on \the [src]. You monster.")) - to_chat(src, span("notice", "\The [user] gives you \a [new_headset]. You should put it to good use immediately.")) - return - -/mob/living/simple_mob/animal/passive/bird/parrot/proc/remove_headset(mob/living/user) - if(!my_headset) - to_chat(user, "\The [src] doesn't have a headset to remove, thankfully.") - else - ISay("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") - my_headset.forceMove(get_turf(src)) - user.put_in_hands(my_headset) - to_chat(user, span("notice", "You take away \the [src]'s [my_headset.name]. Finally.")) - to_chat(src, span("warning", "\The [user] takes your [my_headset.name] away! How cruel!")) - my_headset = null - -/mob/living/simple_mob/animal/passive/bird/parrot/examine(mob/user) - . = ..() - if(my_headset) - . += "It is wearing \a [my_headset]." - -/mob/living/simple_mob/animal/passive/bird/parrot/Initialize() - if(my_headset) - my_headset = new my_headset(src) - return ..() - -// Subtypes. - -// Best Bird -/mob/living/simple_mob/animal/passive/bird/parrot/poly - name = "Poly" - desc = "It's a parrot. An expert on quantum cracker theory." - icon_state = "poly" - icon_rest = "poly-held" - icon_dead = "poly-dead" - tt_desc = "E Ara macao" - attack_armor_pen = 20 //HE HAS THE B E A K - my_headset = /obj/item/device/radio/headset/headset_eng - say_list_type = /datum/say_list/bird/poly - -// Best Bird with best headset. -/mob/living/simple_mob/animal/passive/bird/parrot/poly/ultimate - my_headset = /obj/item/device/radio/headset/omni - -/mob/living/simple_mob/animal/passive/bird/parrot/kea - name = "kea" - desc = "A species of parrot. On Earth, they are unique among other parrots for residing in alpine climates. \ - They are known to be intelligent and curious, which has made some consider them a pest." - icon_state = "kea" - icon_rest = "kea-held" - icon_dead = "kea-dead" - tt_desc = "E Nestor notabilis" - -/mob/living/simple_mob/animal/passive/bird/parrot/eclectus - name = "eclectus" - desc = "A species of parrot, this species features extreme sexual dimorphism in their plumage's colors. \ - A male eclectus has emerald green plumage, where as a female eclectus has red and purple plumage." - icon_state = "eclectus" - icon_rest = "eclectus-held" - icon_dead = "eclectus-dead" - tt_desc = "E Eclectus roratus" - -/mob/living/simple_mob/animal/passive/bird/parrot/eclectus/Initialize() - gender = pick(MALE, FEMALE) - if(gender == FEMALE) - icon_state = "eclectusf" - icon_rest = "eclectusf-held" - icon_dead = "eclectusf-dead" - return ..() - -/mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot - name = "grey parrot" - desc = "A species of parrot. This one is predominantly grey, but has red tail feathers." - icon_state = "agrey" - icon_rest = "agrey-held" - icon_dead = "agrey-dead" - tt_desc = "E Psittacus erithacus" - -/mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique - name = "black-headed caique" - desc = "A species of parrot, these birds have a distinct black color on their heads, distinguishing them from their relative Caiques." - icon_state = "bcaique" - icon_rest = "bcaique-held" - icon_dead = "bcaique-dead" - tt_desc = "E Pionites melanocephalus" - -/mob/living/simple_mob/animal/passive/bird/parrot/white_caique - name = "white-bellied caique" - desc = "A species of parrot, they are also known as the Green-Thighed Parrot." - icon_state = "wcaique" - icon_rest = "wcaique-held" - icon_dead = "wcaique-dead" - tt_desc = "E Pionites leucogaster" - -/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar - name = "budgerigar" - desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \ - This one is has its natural colors of green and yellow." - icon_state = "gbudge" - icon_rest = "gbudge-held" - icon_dead = "gbudge-dead" - tt_desc = "E Melopsittacus undulatus" - -/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue - icon_state = "bbudge" - icon_rest = "bbudge-held" - icon_dead = "bbudge-dead" - desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \ - This one has a mutation which altered its color to be blue instead of green and yellow." - -/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen - icon_state = "bgbudge" - icon_rest = "bgbudge-held" - icon_dead = "bgbudge-dead" - desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \ - This one has a mutation which altered its color to be a mix of blue and green." - -/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel - name = "cockatiel" - desc = "A species of parrot. This one has a highly visible crest." - icon_state = "tiel" - icon_rest = "tiel-held" - icon_dead = "tiel-dead" - tt_desc = "E Nymphicus hollandicus" - -/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white - icon_state = "wtiel" - icon_rest = "wtiel-held" - icon_dead = "wtiel-dead" - -/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish - icon_state = "luttiel" - icon_rest = "luttiel-held" - icon_dead = "luttiel-dead" - -/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey - icon_state = "blutiel" // idk why this is blu. - icon_rest = "blutiel-held" - icon_dead = "blutiel-dead" - -// This actually might be the yellow-crested cockatoo but idk. -/mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo - name = "sulphur-crested cockatoo" - desc = "A species of parrot. This one has an expressive yellow crest. Their underwing and tail feathers are also yellow." - icon_state = "too" - icon_rest = "too-held" - icon_dead = "too-dead" - tt_desc = "E Cacatua galerita" - -// This was originally called 'hooded_too', which might not mean the unbrella cockatoo but idk. -/mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo - name = "white cockatoo" - desc = "A species of parrot. This one is also known as the Umbrella Cockatoo, due to the semicircular shape of its crest." - icon_state = "utoo" - icon_rest = "utoo-held" - icon_dead = "utoo-dead" - tt_desc = "E Cacatua alba" - -/mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo - name = "pink cockatoo" - desc = "A species of parrot. This one is also known as Major Mitchell's cockatoo, \ - in honor of a human surveyor and explorer who existed before humans fully explored their home planet." - icon_state = "mtoo" - icon_rest = "mtoo-held" - icon_dead = "mtoo-dead" - tt_desc = "E Lophochroa leadbeateri" - - -// AI -/datum/ai_holder/simple_mob/passive/parrot - speak_chance = 2 - base_wander_delay = 8 - -/datum/ai_holder/simple_mob/passive/parrot/on_hear_say(mob/living/speaker, message) - if(holder.stat || !holder.say_list || !message || speaker == holder) - return - var/datum/say_list/S = holder.say_list - S.speak |= message +// Parrots can talk, and may repeat things it hears. +/mob/living/simple_mob/animal/passive/bird/parrot + name = "parrot" + description_info = "You can give it a headset by clicking on it with a headset. \ + To remove it, click the bird while on grab intent." + has_langs = list(LANGUAGE_GALCOM, LANGUAGE_ANIMAL) + + ai_holder_type = /datum/ai_holder/simple_mob/passive/parrot + + // A headset, so that talking parrots can yell at the crew over comms. + // If set to a type, on initialize it will be instantiated into that type. + var/obj/item/device/radio/headset/my_headset = null + +// Say list +/datum/say_list/bird/poly + speak = list( + "Poly wanna cracker!", + "Check the singulo, you chucklefucks!", + "Wire the solars, you lazy bums!", + "WHO TOOK THE DAMN HARDSUITS?", + "OH GOD ITS FREE CALL THE SHUTTLE", + "Danger! Crystal hyperstructure instability!", + "CRYSTAL DELAMINATION IMMINENT.", + "Tweet tweet, I'm a Teshari.", + "Chitters.", + "Meteors have been detected on a collision course with the station!" + ) + +// Lets the AI use headsets. +// Player-controlled parrots will need to do it manually. +/mob/living/simple_mob/animal/passive/bird/parrot/ISay(message) + if(my_headset && prob(50)) + var/list/keys = list() + for(var/channel in my_headset.channels) + var/key = get_radio_key_from_channel(channel) + if(key) + keys += key + if(keys.len) + var/key_used = pick(keys) + return say("[key_used] [message]") + return say(message) + +// Ugly saycode so parrots can use their headsets. +/mob/living/simple_mob/animal/passive/bird/parrot/handle_message_mode(message_mode, message, verb, used_radios, whispering) + ..() + if(message_mode) + if(my_headset && istype(my_headset, /obj/item/device/radio)) + my_headset.talk_into(src, message, message_mode, verb, whispering) + used_radios += my_headset + +// Clicked on while holding an object. +/mob/living/simple_mob/animal/passive/bird/parrot/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/device/radio/headset)) + give_headset(I, user) + return + return ..() + +// Clicked on by empty hand. +/mob/living/simple_mob/animal/passive/bird/parrot/attack_hand(mob/living/L) + if(L.a_intent == I_GRAB && my_headset) + remove_headset(L) + else + ..() + + +/mob/living/simple_mob/animal/passive/bird/parrot/proc/give_headset(obj/item/device/radio/headset/new_headset, mob/living/user) + if(!istype(new_headset)) + to_chat(user, span("warning", "\The [new_headset] isn't a headset.")) + return + if(my_headset) + to_chat(user, span("warning", "\The [src] is already wearing \a [my_headset].")) + return + else + user.drop_item(new_headset) + my_headset = new_headset + new_headset.forceMove(src) + to_chat(user, span("warning", "You place \a [new_headset] on \the [src]. You monster.")) + to_chat(src, span("notice", "\The [user] gives you \a [new_headset]. You should put it to good use immediately.")) + return + +/mob/living/simple_mob/animal/passive/bird/parrot/proc/remove_headset(mob/living/user) + if(!my_headset) + to_chat(user, "\The [src] doesn't have a headset to remove, thankfully.") + else + ISay("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") + my_headset.forceMove(get_turf(src)) + user.put_in_hands(my_headset) + to_chat(user, span("notice", "You take away \the [src]'s [my_headset.name]. Finally.")) + to_chat(src, span("warning", "\The [user] takes your [my_headset.name] away! How cruel!")) + my_headset = null + +/mob/living/simple_mob/animal/passive/bird/parrot/examine(mob/user) + . = ..() + if(my_headset) + . += "It is wearing \a [my_headset]." + +/mob/living/simple_mob/animal/passive/bird/parrot/Initialize() + if(my_headset) + my_headset = new my_headset(src) + return ..() + +// Subtypes. + +// Best Bird +/mob/living/simple_mob/animal/passive/bird/parrot/poly + name = "Poly" + desc = "It's a parrot. An expert on quantum cracker theory." + icon_state = "poly" + icon_rest = "poly-held" + icon_dead = "poly-dead" + tt_desc = "E Ara macao" + attack_armor_pen = 20 //HE HAS THE B E A K + my_headset = /obj/item/device/radio/headset/headset_eng + say_list_type = /datum/say_list/bird/poly + +// Best Bird with best headset. +/mob/living/simple_mob/animal/passive/bird/parrot/poly/ultimate + my_headset = /obj/item/device/radio/headset/omni + +/mob/living/simple_mob/animal/passive/bird/parrot/kea + name = "kea" + desc = "A species of parrot. On Earth, they are unique among other parrots for residing in alpine climates. \ + They are known to be intelligent and curious, which has made some consider them a pest." + icon_state = "kea" + icon_rest = "kea-held" + icon_dead = "kea-dead" + tt_desc = "E Nestor notabilis" + +/mob/living/simple_mob/animal/passive/bird/parrot/eclectus + name = "eclectus" + desc = "A species of parrot, this species features extreme sexual dimorphism in their plumage's colors. \ + A male eclectus has emerald green plumage, where as a female eclectus has red and purple plumage." + icon_state = "eclectus" + icon_rest = "eclectus-held" + icon_dead = "eclectus-dead" + tt_desc = "E Eclectus roratus" + +/mob/living/simple_mob/animal/passive/bird/parrot/eclectus/Initialize() + gender = pick(MALE, FEMALE) + if(gender == FEMALE) + icon_state = "eclectusf" + icon_rest = "eclectusf-held" + icon_dead = "eclectusf-dead" + return ..() + +/mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot + name = "grey parrot" + desc = "A species of parrot. This one is predominantly grey, but has red tail feathers." + icon_state = "agrey" + icon_rest = "agrey-held" + icon_dead = "agrey-dead" + tt_desc = "E Psittacus erithacus" + +/mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique + name = "black-headed caique" + desc = "A species of parrot, these birds have a distinct black color on their heads, distinguishing them from their relative Caiques." + icon_state = "bcaique" + icon_rest = "bcaique-held" + icon_dead = "bcaique-dead" + tt_desc = "E Pionites melanocephalus" + +/mob/living/simple_mob/animal/passive/bird/parrot/white_caique + name = "white-bellied caique" + desc = "A species of parrot, they are also known as the Green-Thighed Parrot." + icon_state = "wcaique" + icon_rest = "wcaique-held" + icon_dead = "wcaique-dead" + tt_desc = "E Pionites leucogaster" + +/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar + name = "budgerigar" + desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \ + This one is has its natural colors of green and yellow." + icon_state = "gbudge" + icon_rest = "gbudge-held" + icon_dead = "gbudge-dead" + tt_desc = "E Melopsittacus undulatus" + +/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue + icon_state = "bbudge" + icon_rest = "bbudge-held" + icon_dead = "bbudge-dead" + desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \ + This one has a mutation which altered its color to be blue instead of green and yellow." + +/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen + icon_state = "bgbudge" + icon_rest = "bgbudge-held" + icon_dead = "bgbudge-dead" + desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \ + This one has a mutation which altered its color to be a mix of blue and green." + +/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel + name = "cockatiel" + desc = "A species of parrot. This one has a highly visible crest." + icon_state = "tiel" + icon_rest = "tiel-held" + icon_dead = "tiel-dead" + tt_desc = "E Nymphicus hollandicus" + +/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white + icon_state = "wtiel" + icon_rest = "wtiel-held" + icon_dead = "wtiel-dead" + +/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish + icon_state = "luttiel" + icon_rest = "luttiel-held" + icon_dead = "luttiel-dead" + +/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey + icon_state = "blutiel" // idk why this is blu. + icon_rest = "blutiel-held" + icon_dead = "blutiel-dead" + +// This actually might be the yellow-crested cockatoo but idk. +/mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo + name = "sulphur-crested cockatoo" + desc = "A species of parrot. This one has an expressive yellow crest. Their underwing and tail feathers are also yellow." + icon_state = "too" + icon_rest = "too-held" + icon_dead = "too-dead" + tt_desc = "E Cacatua galerita" + +// This was originally called 'hooded_too', which might not mean the unbrella cockatoo but idk. +/mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo + name = "white cockatoo" + desc = "A species of parrot. This one is also known as the Umbrella Cockatoo, due to the semicircular shape of its crest." + icon_state = "utoo" + icon_rest = "utoo-held" + icon_dead = "utoo-dead" + tt_desc = "E Cacatua alba" + +/mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo + name = "pink cockatoo" + desc = "A species of parrot. This one is also known as Major Mitchell's cockatoo, \ + in honor of a human surveyor and explorer who existed before humans fully explored their home planet." + icon_state = "mtoo" + icon_rest = "mtoo-held" + icon_dead = "mtoo-dead" + tt_desc = "E Lophochroa leadbeateri" + + +// AI +/datum/ai_holder/simple_mob/passive/parrot + speak_chance = 2 + base_wander_delay = 8 + +/datum/ai_holder/simple_mob/passive/parrot/on_hear_say(mob/living/speaker, message) + if(holder.stat || !holder.say_list || !message || speaker == holder) + return + var/datum/say_list/S = holder.say_list + S.speak |= message diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm index cb989bd9547..20ea1e2db06 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm @@ -1,68 +1,68 @@ -// Diyaabs are rather weak, but tend to exist in large numbers. -// They cooperate with other diyaabs, in order to swarm whoever decides to pick on the little fluffy critter. -// A cleaving weapon like an axe will make short work of the pack. - -/datum/category_item/catalogue/fauna/diyaab - name = "Sivian Fauna - Diyaab" - desc = "Classification: S Choeros hirtus\ -

                    \ - Small, social omnivores with dense seasonal wool fur valued by Sivian colonists for its cold resistance and softness. \ - The Diyaab lives in packs of anywhere from three to ten individuals, usually comprised of a family unit. Primarily herbivorous browsers, \ - supplementing their diet with organisms living in tree bark, \ - Diyaab packs have been observed to hunt prey several times their size during the less plentiful winter months. \ - Despite their unassuming appearance, the Diyaab possesses remarkably sharp anterior teeth." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/animal/sif/diyaab - name = "diyaab" - desc = "A small pack animal. Although omnivorous, it will hunt meat on occasion." - tt_desc = "S Choeros hirtus" //diyaab and shantak are technically reletives! - catalogue_data = list(/datum/category_item/catalogue/fauna/diyaab) - - faction = "diyaab" - - icon_state = "diyaab" - icon_living = "diyaab" - icon_dead = "diyaab_dead" - icon = 'icons/jungle.dmi' - - maxHealth = 25 - health = 25 - - meat_amount = 2 - meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat - - minbodytemp = 175 //yw edit, Makes mobs survive cryogaia temps - - movement_cooldown = -1 - - melee_damage_lower = 2 - melee_damage_upper = 6 - base_attack_cooldown = 1 SECOND - attack_sharp = TRUE //Bleeds, but it shouldn't rip off a limb? - attacktext = list("gouged") - - say_list_type = /datum/say_list/diyaab - ai_holder_type = /datum/ai_holder/simple_mob/retaliate/cooperative - - // What do you hit the mob with (on help) to get something from it? - harvest_tool = /obj/item/weapon/tool/wirecutters - // How long do we have to wait until it's harvestable again? - harvest_cooldown = 10 MINUTES - // How long does it take to harvest? - harvest_delay = 30 SECONDS - // What world.time was the last harvest? - harvest_recent = 0 - // How many times do we roll on the chance table? - harvest_per_hit = 1 - // Verb for harvesting. "sheared" "clipped" etc. - harvest_verb = "sheared" - // Associative list of paths and their chances. path = straws in the lot - harvest_results = list( - /obj/item/stack/material/cloth/diyaab = 10 - ) - -/datum/say_list/diyaab - speak = list("Awrr?", "Aowrl!", "Worrl.") - emote_see = list("sniffs the air cautiously","looks around") - emote_hear = list("snuffles") +// Diyaabs are rather weak, but tend to exist in large numbers. +// They cooperate with other diyaabs, in order to swarm whoever decides to pick on the little fluffy critter. +// A cleaving weapon like an axe will make short work of the pack. + +/datum/category_item/catalogue/fauna/diyaab + name = "Sivian Fauna - Diyaab" + desc = "Classification: S Choeros hirtus\ +

                    \ + Small, social omnivores with dense seasonal wool fur valued by Sivian colonists for its cold resistance and softness. \ + The Diyaab lives in packs of anywhere from three to ten individuals, usually comprised of a family unit. Primarily herbivorous browsers, \ + supplementing their diet with organisms living in tree bark, \ + Diyaab packs have been observed to hunt prey several times their size during the less plentiful winter months. \ + Despite their unassuming appearance, the Diyaab possesses remarkably sharp anterior teeth." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/sif/diyaab + name = "diyaab" + desc = "A small pack animal. Although omnivorous, it will hunt meat on occasion." + tt_desc = "S Choeros hirtus" //diyaab and shantak are technically reletives! + catalogue_data = list(/datum/category_item/catalogue/fauna/diyaab) + + faction = "diyaab" + + icon_state = "diyaab" + icon_living = "diyaab" + icon_dead = "diyaab_dead" + icon = 'icons/jungle.dmi' + + maxHealth = 25 + health = 25 + + meat_amount = 2 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + + minbodytemp = 175 //yw edit, Makes mobs survive cryogaia temps + + movement_cooldown = -1 + + melee_damage_lower = 2 + melee_damage_upper = 6 + base_attack_cooldown = 1 SECOND + attack_sharp = TRUE //Bleeds, but it shouldn't rip off a limb? + attacktext = list("gouged") + + say_list_type = /datum/say_list/diyaab + ai_holder_type = /datum/ai_holder/simple_mob/retaliate/cooperative + + // What do you hit the mob with (on help) to get something from it? + harvest_tool = /obj/item/weapon/tool/wirecutters + // How long do we have to wait until it's harvestable again? + harvest_cooldown = 10 MINUTES + // How long does it take to harvest? + harvest_delay = 30 SECONDS + // What world.time was the last harvest? + harvest_recent = 0 + // How many times do we roll on the chance table? + harvest_per_hit = 1 + // Verb for harvesting. "sheared" "clipped" etc. + harvest_verb = "sheared" + // Associative list of paths and their chances. path = straws in the lot + harvest_results = list( + /obj/item/stack/material/cloth/diyaab = 10 + ) + +/datum/say_list/diyaab + speak = list("Awrr?", "Aowrl!", "Worrl.") + emote_see = list("sniffs the air cautiously","looks around") + emote_hear = list("snuffles") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm index 10b7c224f2b..27ec5bf6fa6 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm @@ -1,87 +1,87 @@ -// Saviks are dangerous, angry creatures that hit hard, and will berserk if losing a fight. - -/datum/category_item/catalogue/fauna/savik - name = "Sivian Fauna - Savik" - desc = "Classification: S Pistris tellus\ -

                    \ - A predatory warm-blooded reptillian species covered in a layer of insulating down feathers. \ - The Savik's preferred method of hunting is to burrow under deep snow drifts, and lie in ambush for prey. \ - The Savik has been known to lie in wait for days at a time, generating heat by vibrating its shoulder plates \ - at a nigh inperceptable frequency while most of its body enters a state of sopor in order to conserve energy. \ -

                    \ - Once the Savik detects its prey, it will charge with incredible kinetic force with the two enormous, \ - angled bony plates on either side of the Savik's upper body acting as a natural snow plow, \ - allowing frightening ease of movement through deep snow. Due to the long periods between feeding, \ - the Savik will hunt its prey with absolute perseverence, as failure to catch a suitable meal is likely to \ - spell death for the animal due to the high energy expenditure of its initial strike. \ - The Savik has no known predators, and should be avoided at all costs." - value = CATALOGUER_REWARD_MEDIUM - -/mob/living/simple_mob/animal/sif/savik - name = "savik" - tt_desc = "S Pistris tellus" //landshark - catalogue_data = list(/datum/category_item/catalogue/fauna/savik) - faction = "savik" - - icon_state = "savik" - icon_living = "savik" - icon_dead = "savik_dead" - icon = 'icons/jungle.dmi' - - maxHealth = 125 - health = 125 - minbodytemp = 175 //yw edit, Makes mobs survive cryogaia temps - movement_cooldown = -1 - heat_resist = -0.50 //yw edit, Makes mobs survive cryogaia temps - cold_resist = 0.75 //yw edit, Makes mobs survive cryogaia temps - melee_damage_lower = 15 - melee_damage_upper = 35 - attack_armor_pen = 15 - attack_sharp = TRUE - attack_edge = TRUE - melee_attack_delay = 1 SECOND - attacktext = list("mauled") - - organ_names = /decl/mob_organ_names/savik - - player_msg = "You have the ability to berserk at will, which will grant strong physical bonuses for \ - a short period of time, however it will tire you and you will be much weaker for awhile after it expires." - - tame_items = list( - /obj/item/organ = 70, - /obj/item/weapon/reagent_containers/food/snacks/crabmeat = 30, - /obj/item/weapon/reagent_containers/food/snacks/meat = 20 - ) - - say_list_type = /datum/say_list/savik - ai_holder_type = /datum/ai_holder/simple_mob/savik - -/datum/say_list/savik - speak = list("Hruuugh!","Hrunnph") - emote_see = list("paws the ground","shakes its mane","stomps") - emote_hear = list("snuffles") - -/mob/living/simple_mob/animal/sif/savik/handle_special() - if((get_AI_stance() in list(STANCE_APPROACH, STANCE_FIGHT)) && !is_AI_busy() && isturf(loc)) - if(health <= (maxHealth * 0.5)) // At half health, and fighting someone currently. - berserk() - -/mob/living/simple_mob/animal/sif/savik/fail_tame(var/obj/O, var/mob/user) - ..() - - if(prob(30)) // They don't like people messing with them and their food. - berserk() - -/datum/ai_holder/simple_mob/savik - mauling = TRUE - -// So players can use it too. -/mob/living/simple_mob/animal/sif/savik/verb/berserk() - set name = "Berserk" - set desc = "Enrage and become vastly stronger for a period of time, however you will be weaker afterwards." - set category = "Abilities" - - add_modifier(/datum/modifier/berserk, 30 SECONDS) - -/decl/mob_organ_names/savik - hit_zones = list("head", "torso", "left foreleg", "right foreleg", "left hind leg", "right hind leg", "right bone plate", "left bone plate", "tail", "left claw", "right claw") +// Saviks are dangerous, angry creatures that hit hard, and will berserk if losing a fight. + +/datum/category_item/catalogue/fauna/savik + name = "Sivian Fauna - Savik" + desc = "Classification: S Pistris tellus\ +

                    \ + A predatory warm-blooded reptillian species covered in a layer of insulating down feathers. \ + The Savik's preferred method of hunting is to burrow under deep snow drifts, and lie in ambush for prey. \ + The Savik has been known to lie in wait for days at a time, generating heat by vibrating its shoulder plates \ + at a nigh inperceptable frequency while most of its body enters a state of sopor in order to conserve energy. \ +

                    \ + Once the Savik detects its prey, it will charge with incredible kinetic force with the two enormous, \ + angled bony plates on either side of the Savik's upper body acting as a natural snow plow, \ + allowing frightening ease of movement through deep snow. Due to the long periods between feeding, \ + the Savik will hunt its prey with absolute perseverence, as failure to catch a suitable meal is likely to \ + spell death for the animal due to the high energy expenditure of its initial strike. \ + The Savik has no known predators, and should be avoided at all costs." + value = CATALOGUER_REWARD_MEDIUM + +/mob/living/simple_mob/animal/sif/savik + name = "savik" + tt_desc = "S Pistris tellus" //landshark + catalogue_data = list(/datum/category_item/catalogue/fauna/savik) + faction = "savik" + + icon_state = "savik" + icon_living = "savik" + icon_dead = "savik_dead" + icon = 'icons/jungle.dmi' + + maxHealth = 125 + health = 125 + minbodytemp = 175 //yw edit, Makes mobs survive cryogaia temps + movement_cooldown = -1 + heat_resist = -0.50 //yw edit, Makes mobs survive cryogaia temps + cold_resist = 0.75 //yw edit, Makes mobs survive cryogaia temps + melee_damage_lower = 15 + melee_damage_upper = 35 + attack_armor_pen = 15 + attack_sharp = TRUE + attack_edge = TRUE + melee_attack_delay = 1 SECOND + attacktext = list("mauled") + + organ_names = /decl/mob_organ_names/savik + + player_msg = "You have the ability to berserk at will, which will grant strong physical bonuses for \ + a short period of time, however it will tire you and you will be much weaker for awhile after it expires." + + tame_items = list( + /obj/item/organ = 70, + /obj/item/weapon/reagent_containers/food/snacks/crabmeat = 30, + /obj/item/weapon/reagent_containers/food/snacks/meat = 20 + ) + + say_list_type = /datum/say_list/savik + ai_holder_type = /datum/ai_holder/simple_mob/savik + +/datum/say_list/savik + speak = list("Hruuugh!","Hrunnph") + emote_see = list("paws the ground","shakes its mane","stomps") + emote_hear = list("snuffles") + +/mob/living/simple_mob/animal/sif/savik/handle_special() + if((get_AI_stance() in list(STANCE_APPROACH, STANCE_FIGHT)) && !is_AI_busy() && isturf(loc)) + if(health <= (maxHealth * 0.5)) // At half health, and fighting someone currently. + berserk() + +/mob/living/simple_mob/animal/sif/savik/fail_tame(var/obj/O, var/mob/user) + ..() + + if(prob(30)) // They don't like people messing with them and their food. + berserk() + +/datum/ai_holder/simple_mob/savik + mauling = TRUE + +// So players can use it too. +/mob/living/simple_mob/animal/sif/savik/verb/berserk() + set name = "Berserk" + set desc = "Enrage and become vastly stronger for a period of time, however you will be weaker afterwards." + set category = "Abilities" + + add_modifier(/datum/modifier/berserk, 30 SECONDS) + +/decl/mob_organ_names/savik + hit_zones = list("head", "torso", "left foreleg", "right foreleg", "left hind leg", "right hind leg", "right bone plate", "left bone plate", "tail", "left claw", "right claw") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm index bdd0ee97748..f14c909104c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm @@ -1,114 +1,114 @@ -// Shantaks are essentially sif wolves. - -/datum/category_item/catalogue/fauna/shantak - name = "Sivian Fauna - Shantak" - desc = "Classification: S Choeros shantak\ -

                    \ - The Shantak is easily recognized by its iridescent, crystaline mane. \ - The creature's specially adapted hairs are hardened by a natural hard mineral coating, \ - thickest in the mane but present across the whole body. \ - As well as giving the Shantak a coat nigh-inpenetrable to all but the most specialized predator, \ - their hard, almost metallic coat gives them a slightly musical accent as they move. \ - The Shantak uses its powerful foreclaws for both den-building and foraging. \ -

                    \ - Observed to share several square-mile territories with a small number of other individuals, \ - the Shantak will rotate between several dens dug deep into the hard earth throughout the year, \ - while deftly avoiding others of its species outwith mating season. While other wildlife makes use of these dens, \ - the Shantak is fiercely territorial and will defend itself against any creature it perceives as a threat with reckless abandon. \ - Their diet consists primarily of fungi and insects found just below the permafrost." - value = CATALOGUER_REWARD_MEDIUM - -/mob/living/simple_mob/animal/sif/shantak - name = "shantak" - desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. \ - Don't be fooled by its beauty though." - tt_desc = "S Choeros shantak" - catalogue_data = list(/datum/category_item/catalogue/fauna/shantak) - - faction = "shantak" - - icon_state = "shantak" - icon_living = "shantak" - icon_dead = "shantak_dead" - icon = 'icons/jungle.dmi' - - maxHealth = 75 - armor_soak = list( - "melee" = 5, - "bullet" = 0, - "laser" = 0, - "energy" = 0, - "bomb" = 0, - "bio" = 0, - "rad" = 0 - ) - heat_resist = -0.50 //yw edit, Makes mobs survive cryogaia temps - cold_resist = 0.75 //yw edit, Makes mobs survive cryogaia temps - movement_cooldown = -1 - minbodytemp = 175 //yw edit, Makes mobs survive cryogaia temps - melee_damage_lower = 6 - melee_damage_upper = 14 - base_attack_cooldown = 1 SECOND - melee_attack_delay = 0.5 SECONDS - attack_armor_pen = 5 - attack_sharp = TRUE - attack_edge = TRUE - attacktext = list("gouged") - - organ_names = /decl/mob_organ_names/shantak - - say_list_type = /datum/say_list/shantak - -/datum/say_list/shantak - speak = list("Shuhn.","Shrunnph?","Shunpf.") - emote_see = list("scratches the ground", "shakes out its mane", "clinks gently as it moves") - - -// The pack leader. -// Will command other shantaks to follow it. -/mob/living/simple_mob/animal/sif/shantak/leader - name = "big shantak" - desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. \ - This one seems bigger than the others, and has a commanding presence." - icon_scale_x = 1.5 - icon_scale_y = 1.5 - maxHealth = 125 - player_msg = "You have the ability to command other shantaks to follow you." - -/mob/living/simple_mob/animal/sif/shantak/leader/verb/rally_pack() - set name = "Rally Pack" - set desc = "Commands your fellow packmembers to follow you, the leader." - set category = "Abilities" - - for(var/mob/living/simple_mob/animal/sif/shantak/S in hearers(7, src)) - if(istype(S, /mob/living/simple_mob/animal/sif/shantak/leader)) // Leaders won't follow other leaders. Also avoids trying to follow ourselves. - continue - if(!S.ai_holder) - continue - if(S.faction != src.faction) - continue - var/datum/ai_holder/AI = S.ai_holder - AI.set_follow(src) - -// Variant that automatically commands nearby allies to follow it when created. -// Suggested to spawn last so it can rally up all the shantaks easily before hunting for tasty explorers. -/mob/living/simple_mob/animal/sif/shantak/leader/autofollow/Initialize() - rally_pack() - return ..() - -// These ones only retaliate. Used for PoIs. -/mob/living/simple_mob/animal/sif/shantak/retaliate - ai_holder_type = /datum/ai_holder/simple_mob/retaliate - -/mob/living/simple_mob/animal/sif/shantak/leader/autofollow/retaliate - ai_holder_type = /datum/ai_holder/simple_mob/retaliate - -//Vorestation Addition -/mob/living/simple_mob/animal/sif/shantak/scruffy - name = "Scruffy" - ai_holder_type = /datum/ai_holder/simple_mob/passive - makes_dirt = 0 - faction = "neutral" - -/decl/mob_organ_names/shantak - hit_zones = list("head", "torso", "left foreleg", "right foreleg", "left hind leg", "right hind leg", "tail", "mane", "snout") +// Shantaks are essentially sif wolves. + +/datum/category_item/catalogue/fauna/shantak + name = "Sivian Fauna - Shantak" + desc = "Classification: S Choeros shantak\ +

                    \ + The Shantak is easily recognized by its iridescent, crystaline mane. \ + The creature's specially adapted hairs are hardened by a natural hard mineral coating, \ + thickest in the mane but present across the whole body. \ + As well as giving the Shantak a coat nigh-inpenetrable to all but the most specialized predator, \ + their hard, almost metallic coat gives them a slightly musical accent as they move. \ + The Shantak uses its powerful foreclaws for both den-building and foraging. \ +

                    \ + Observed to share several square-mile territories with a small number of other individuals, \ + the Shantak will rotate between several dens dug deep into the hard earth throughout the year, \ + while deftly avoiding others of its species outwith mating season. While other wildlife makes use of these dens, \ + the Shantak is fiercely territorial and will defend itself against any creature it perceives as a threat with reckless abandon. \ + Their diet consists primarily of fungi and insects found just below the permafrost." + value = CATALOGUER_REWARD_MEDIUM + +/mob/living/simple_mob/animal/sif/shantak + name = "shantak" + desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. \ + Don't be fooled by its beauty though." + tt_desc = "S Choeros shantak" + catalogue_data = list(/datum/category_item/catalogue/fauna/shantak) + + faction = "shantak" + + icon_state = "shantak" + icon_living = "shantak" + icon_dead = "shantak_dead" + icon = 'icons/jungle.dmi' + + maxHealth = 75 + armor_soak = list( + "melee" = 5, + "bullet" = 0, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + heat_resist = -0.50 //yw edit, Makes mobs survive cryogaia temps + cold_resist = 0.75 //yw edit, Makes mobs survive cryogaia temps + movement_cooldown = -1 + minbodytemp = 175 //yw edit, Makes mobs survive cryogaia temps + melee_damage_lower = 6 + melee_damage_upper = 14 + base_attack_cooldown = 1 SECOND + melee_attack_delay = 0.5 SECONDS + attack_armor_pen = 5 + attack_sharp = TRUE + attack_edge = TRUE + attacktext = list("gouged") + + organ_names = /decl/mob_organ_names/shantak + + say_list_type = /datum/say_list/shantak + +/datum/say_list/shantak + speak = list("Shuhn.","Shrunnph?","Shunpf.") + emote_see = list("scratches the ground", "shakes out its mane", "clinks gently as it moves") + + +// The pack leader. +// Will command other shantaks to follow it. +/mob/living/simple_mob/animal/sif/shantak/leader + name = "big shantak" + desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. \ + This one seems bigger than the others, and has a commanding presence." + icon_scale_x = 1.5 + icon_scale_y = 1.5 + maxHealth = 125 + player_msg = "You have the ability to command other shantaks to follow you." + +/mob/living/simple_mob/animal/sif/shantak/leader/verb/rally_pack() + set name = "Rally Pack" + set desc = "Commands your fellow packmembers to follow you, the leader." + set category = "Abilities" + + for(var/mob/living/simple_mob/animal/sif/shantak/S in hearers(7, src)) + if(istype(S, /mob/living/simple_mob/animal/sif/shantak/leader)) // Leaders won't follow other leaders. Also avoids trying to follow ourselves. + continue + if(!S.ai_holder) + continue + if(S.faction != src.faction) + continue + var/datum/ai_holder/AI = S.ai_holder + AI.set_follow(src) + +// Variant that automatically commands nearby allies to follow it when created. +// Suggested to spawn last so it can rally up all the shantaks easily before hunting for tasty explorers. +/mob/living/simple_mob/animal/sif/shantak/leader/autofollow/Initialize() + rally_pack() + return ..() + +// These ones only retaliate. Used for PoIs. +/mob/living/simple_mob/animal/sif/shantak/retaliate + ai_holder_type = /datum/ai_holder/simple_mob/retaliate + +/mob/living/simple_mob/animal/sif/shantak/leader/autofollow/retaliate + ai_holder_type = /datum/ai_holder/simple_mob/retaliate + +//Vorestation Addition +/mob/living/simple_mob/animal/sif/shantak/scruffy + name = "Scruffy" + ai_holder_type = /datum/ai_holder/simple_mob/passive + makes_dirt = 0 + faction = "neutral" + +/decl/mob_organ_names/shantak + hit_zones = list("head", "torso", "left foreleg", "right foreleg", "left hind leg", "right hind leg", "tail", "mane", "snout") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm index 89cffd64c9b..357eb366e39 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm @@ -1,252 +1,252 @@ -// Space carp show up as a random event to wreck hapless people in space or near windows. -// They generally fit the archetype of 'fast but fragile'. -// This is compensated by being in groups (usually). - -/datum/category_item/catalogue/fauna/carp - name = "Voidborne Fauna - Space Carp" - desc = "A strange descendant of some form of voidborne life, they are the most \ - common naturally void-faring lifeform found in human territory. They've been named \ - 'Space Carp' by various groups of spacers due to resembling the fish from Earth.\ -

                    \ - Their lifecycle begins as a fungus-like growth, sometimes found on the walls of spacecraft \ - and space stations, before growing into a form which allows for independent travel. Even \ - when fully grown, they can sometimes be found to stow away on the hulls of spaceborne objects, \ - which might explain how they became widespread across many star systems.\ -

                    \ - Carp have a special gas bladder inside of them, which they utilize as a means of movement in \ - space by stategically releasing the gas to propel themselves in a process that resembles \ - thrusters on a spacecraft. The gas contained inside the carp also allows them \ - to float when inside an atmosphere. The carp might also spray 'spores' using a similar method.\ -

                    \ - They are hypercarnivorous to the point of cannibalism, consuming their own dead in order to \ - sustain themselves during hard times, which are rather frequent due to their prey being \ - vastly technologically advanced. For human habitats that are well secured, carp are generally \ - an annoyance. For those unable to adequately protect themselves, however, they can be \ - rather dangerous, especially if a mass migration of carp arrives." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/animal/space/carp - name = "space carp" - desc = "A ferocious, fang-bearing creature that resembles a fish." - catalogue_data = list(/datum/category_item/catalogue/fauna/carp) - icon_state = "carp" - icon_living = "carp" - icon_dead = "carp_dead" - icon_gib = "carp_gib" - - faction = "carp" - maxHealth = 25 - health = 25 - movement_cooldown = -2 - hovering = TRUE - - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "hits the" - - melee_damage_lower = 7 // About 14 DPS. - melee_damage_upper = 7 - base_attack_cooldown = 10 // One attack a second. - attack_sharp = TRUE - attack_sound = 'sound/weapons/bite.ogg' - attacktext = list("bitten") - - organ_names = /decl/mob_organ_names/fish - - meat_amount = 5 - meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat - - ai_holder_type = /datum/ai_holder/simple_mob/melee - - var/knockdown_chance = 15 - -/mob/living/simple_mob/animal/space/carp/apply_melee_effects(var/atom/A) - if(isliving(A)) - var/mob/living/L = A - if(prob(knockdown_chance)) - L.Weaken(3) - L.visible_message(span("danger", "\The [src] knocks down \the [L]!")) - -// Subtypes. - -// Won't wander away. -/mob/living/simple_mob/animal/space/carp/event - ai_holder_type = /datum/ai_holder/simple_mob/event - - -/mob/living/simple_mob/animal/space/carp/large - name = "elder carp" - desc = "An older, more matured carp. Few survive to this age due to their aggressiveness." - icon = 'icons/mob/64x32.dmi' - icon_state = "shark" - icon_living = "shark" - icon_dead = "shark_dead" - - maxHealth = 50 - health = 50 - movement_cooldown = 1 // Slower than the younger carp. - mob_size = MOB_LARGE - - pixel_x = -16 - default_pixel_x = -16 - icon_expected_width = 64 - icon_expected_height = 32 - - meat_amount = 7 - - -/mob/living/simple_mob/animal/space/carp/large/huge - name = "great white carp" - desc = "A very rare breed of carp- and a very aggressive one." - icon = 'icons/mob/64x64.dmi' - icon_dead = "megacarp_dead" - icon_living = "megacarp" - icon_state = "megacarp" - - maxHealth = 230 - health = 230 - movement_cooldown = 3 - - melee_damage_lower = 15 // About 20 DPS. - melee_damage_upper = 25 - - pixel_y = -16 - default_pixel_y = -16 - icon_expected_width = 64 - icon_expected_height = 64 - - meat_amount = 15 - - knockdown_chance = 15 - -/mob/living/simple_mob/animal/space/carp/large/huge/vorny - name = "great white carp" - desc = "A very rare breed of carp- and a very hungry one." - icon = 'icons/mob/64x64.dmi' - icon_dead = "megacarp_dead" - icon_living = "megacarp" - icon_state = "megacarp" - - maxHealth = 230 - health = 230 - movement_cooldown = 3 - - melee_damage_lower = 1 // Minimal damage to make the knockdown work. - melee_damage_upper = 1 - - pixel_y = -16 - default_pixel_y = -16 - icon_expected_width = 64 - icon_expected_height = 64 - - meat_amount = 15 - - knockdown_chance = 50 - ai_holder_type = /datum/ai_holder/simple_mob/vore - -/mob/living/simple_mob/animal/space/carp/large/huge/vorny/init_vore() - ..() - var/obj/belly/B = vore_selected - B.name = "stomach" - B.desc = "You've been swallowed whole and alive by a massive white carp! The stomach around you is oppressively tight, squeezing and grinding wrinkled walls across your body, making it hard to make any movement at all. The chamber is flooded with fluids that completely overwhelm you." - B.mode_flags = DM_FLAG_THICKBELLY - B.belly_fullscreen = "yet_another_tumby" - B.digest_brute = 2 - B.digest_burn = 2 - B.digest_oxy = 1 - B.digestchance = 100 - B.absorbchance = 0 - B.escapechance = 3 - B.selective_preference = DM_DIGEST - B.escape_stun = 10 - -/mob/living/simple_mob/animal/space/carp/holographic - name = "holographic carp" - desc = "An obviously holographic, but still ferocious looking carp." - // Might be worth using a filter similar to AI holograms in the future. - icon = 'icons/mob/AI.dmi' - icon_state = "holo4" - icon_living = "holo4" - icon_dead = "holo4" - alpha = 127 - icon_gib = null - meat_amount = 0 - meat_type = null - - mob_class = MOB_CLASS_PHOTONIC // Xeno-taser won't work on this as its not a 'real' carp. - -/mob/living/simple_mob/animal/space/carp/holographic/Initialize() - set_light(2) // Hologram lighting. - return ..() - -// Presumably the holodeck emag code requires this. -// Pass TRUE to make safe. Pass FALSE to make unsafe. -/mob/living/simple_mob/animal/space/carp/holographic/proc/set_safety(safe) - if(!isnull(get_AI_stance())) // Will return null if lacking an AI holder or a player is controlling it w/o autopilot var. - ai_holder.hostile = !safe // Inverted so safe = TRUE means hostility = FALSE. - ai_holder.forget_everything() // Reset state so it'll stop chewing on its target. - -// Called on death. -/mob/living/simple_mob/animal/space/carp/holographic/proc/derez() - visible_message(span("notice", "\The [src] fades away!")) - qdel(src) - -/mob/living/simple_mob/animal/space/carp/holographic/gib() - derez() // Holograms can't gib. - -/mob/living/simple_mob/animal/space/carp/holographic/death() - ..() - derez() - -// a slow-moving carp with the appearance of a sea mine and behaviour of a sea mine -/mob/living/simple_mob/animal/space/carp/puffer - name = "puffercarp" - desc = "A bloated, inflated carp covered in spines." - catalogue_data = list(/datum/category_item/catalogue/fauna/carp, /datum/category_item/catalogue/fauna/carp/puffer) - icon_state = "puffercarp" - icon_living = "puffercarp" - icon_dead = "puffercarp_dead" - icon_gib = "generic_gib" - movement_cooldown = 15 - var/ready_to_blow = TRUE - -/datum/category_item/catalogue/fauna/carp/puffer - name = "Voidborne Fauna - Space Carp: puffer variant" - desc = "An unusual subspecies of space carp with a novel defensive \ - and reproductive strategy - once the puffercarp is ready to spread spores \ - it begins to produce a highly volatile compound within its gas bladders, \ - which in addition to providing them with a means of propulsion through space \ - as per most space carp species, affords the puffercarp with a somewhat unique trait \ - - namely, that they are able to ignite and detonate their gas bladders \ - at will, and will do so aggressively when threatened. The bladders also tend to ignite \ - when struck by thermal or electrical discharges, or even sympathetic detonation from \ - other explosives - including other nearby puffercarp. As a result, most voidborne \ - predators have a tendency to keep clear, but even if this deterrent doesn't work the resulting \ - explosion serves to scatter their spores over a massive area - this improved seeding \ - strategy compared to regular carp results in the propagation of the species despite the \ - fact that it means each adult carp can only reproduce exactly once. \ -

                    \ - Due to their premature mortality it is extremely rare to see a puffercarp grow to any notable \ - size, often appearing to be somewhat stunted in growth compared to other subspecies, \ - their gas bloating being the only thing that brings them close to \ - the normal scale of an adult carp. " - value = CATALOGUER_REWARD_HARD //if you can hang around close enough to this thing without setting it off, you deserve it - -/mob/living/simple_mob/animal/space/carp/puffer/proc/kaboom() - if(ready_to_blow) - ready_to_blow = FALSE - gib() - var/turf/T = get_turf(src) - explosion(T, -1, -1, 4, 4) - - -/mob/living/simple_mob/animal/space/carp/puffer/apply_melee_effects() //it gets close enough to attack? EXPLODE - kaboom() - -/mob/living/simple_mob/animal/space/carp/puffer/adjustFireLoss(var/amount,var/include_robo) //you make it hot? EXPLODE - if(amount>0) - kaboom() - ..() - -/mob/living/simple_mob/animal/space/carp/puffer/ex_act() //explode? YOU BETTER BELIEVE THAT'S AN EXPLODE +// Space carp show up as a random event to wreck hapless people in space or near windows. +// They generally fit the archetype of 'fast but fragile'. +// This is compensated by being in groups (usually). + +/datum/category_item/catalogue/fauna/carp + name = "Voidborne Fauna - Space Carp" + desc = "A strange descendant of some form of voidborne life, they are the most \ + common naturally void-faring lifeform found in human territory. They've been named \ + 'Space Carp' by various groups of spacers due to resembling the fish from Earth.\ +

                    \ + Their lifecycle begins as a fungus-like growth, sometimes found on the walls of spacecraft \ + and space stations, before growing into a form which allows for independent travel. Even \ + when fully grown, they can sometimes be found to stow away on the hulls of spaceborne objects, \ + which might explain how they became widespread across many star systems.\ +

                    \ + Carp have a special gas bladder inside of them, which they utilize as a means of movement in \ + space by stategically releasing the gas to propel themselves in a process that resembles \ + thrusters on a spacecraft. The gas contained inside the carp also allows them \ + to float when inside an atmosphere. The carp might also spray 'spores' using a similar method.\ +

                    \ + They are hypercarnivorous to the point of cannibalism, consuming their own dead in order to \ + sustain themselves during hard times, which are rather frequent due to their prey being \ + vastly technologically advanced. For human habitats that are well secured, carp are generally \ + an annoyance. For those unable to adequately protect themselves, however, they can be \ + rather dangerous, especially if a mass migration of carp arrives." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/space/carp + name = "space carp" + desc = "A ferocious, fang-bearing creature that resembles a fish." + catalogue_data = list(/datum/category_item/catalogue/fauna/carp) + icon_state = "carp" + icon_living = "carp" + icon_dead = "carp_dead" + icon_gib = "carp_gib" + + faction = "carp" + maxHealth = 25 + health = 25 + movement_cooldown = -2 + hovering = TRUE + + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "hits the" + + melee_damage_lower = 7 // About 14 DPS. + melee_damage_upper = 7 + base_attack_cooldown = 10 // One attack a second. + attack_sharp = TRUE + attack_sound = 'sound/weapons/bite.ogg' + attacktext = list("bitten") + + organ_names = /decl/mob_organ_names/fish + + meat_amount = 5 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat + + ai_holder_type = /datum/ai_holder/simple_mob/melee + + var/knockdown_chance = 15 + +/mob/living/simple_mob/animal/space/carp/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(prob(knockdown_chance)) + L.Weaken(3) + L.visible_message(span("danger", "\The [src] knocks down \the [L]!")) + +// Subtypes. + +// Won't wander away. +/mob/living/simple_mob/animal/space/carp/event + ai_holder_type = /datum/ai_holder/simple_mob/event + + +/mob/living/simple_mob/animal/space/carp/large + name = "elder carp" + desc = "An older, more matured carp. Few survive to this age due to their aggressiveness." + icon = 'icons/mob/64x32.dmi' + icon_state = "shark" + icon_living = "shark" + icon_dead = "shark_dead" + + maxHealth = 50 + health = 50 + movement_cooldown = 1 // Slower than the younger carp. + mob_size = MOB_LARGE + + pixel_x = -16 + default_pixel_x = -16 + icon_expected_width = 64 + icon_expected_height = 32 + + meat_amount = 7 + + +/mob/living/simple_mob/animal/space/carp/large/huge + name = "great white carp" + desc = "A very rare breed of carp- and a very aggressive one." + icon = 'icons/mob/64x64.dmi' + icon_dead = "megacarp_dead" + icon_living = "megacarp" + icon_state = "megacarp" + + maxHealth = 230 + health = 230 + movement_cooldown = 3 + + melee_damage_lower = 15 // About 20 DPS. + melee_damage_upper = 25 + + pixel_y = -16 + default_pixel_y = -16 + icon_expected_width = 64 + icon_expected_height = 64 + + meat_amount = 15 + + knockdown_chance = 15 + +/mob/living/simple_mob/animal/space/carp/large/huge/vorny + name = "great white carp" + desc = "A very rare breed of carp- and a very hungry one." + icon = 'icons/mob/64x64.dmi' + icon_dead = "megacarp_dead" + icon_living = "megacarp" + icon_state = "megacarp" + + maxHealth = 230 + health = 230 + movement_cooldown = 3 + + melee_damage_lower = 1 // Minimal damage to make the knockdown work. + melee_damage_upper = 1 + + pixel_y = -16 + default_pixel_y = -16 + icon_expected_width = 64 + icon_expected_height = 64 + + meat_amount = 15 + + knockdown_chance = 50 + ai_holder_type = /datum/ai_holder/simple_mob/vore + +/mob/living/simple_mob/animal/space/carp/large/huge/vorny/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "You've been swallowed whole and alive by a massive white carp! The stomach around you is oppressively tight, squeezing and grinding wrinkled walls across your body, making it hard to make any movement at all. The chamber is flooded with fluids that completely overwhelm you." + B.mode_flags = DM_FLAG_THICKBELLY + B.belly_fullscreen = "yet_another_tumby" + B.digest_brute = 2 + B.digest_burn = 2 + B.digest_oxy = 1 + B.digestchance = 100 + B.absorbchance = 0 + B.escapechance = 3 + B.selective_preference = DM_DIGEST + B.escape_stun = 10 + +/mob/living/simple_mob/animal/space/carp/holographic + name = "holographic carp" + desc = "An obviously holographic, but still ferocious looking carp." + // Might be worth using a filter similar to AI holograms in the future. + icon = 'icons/mob/AI.dmi' + icon_state = "holo4" + icon_living = "holo4" + icon_dead = "holo4" + alpha = 127 + icon_gib = null + meat_amount = 0 + meat_type = null + + mob_class = MOB_CLASS_PHOTONIC // Xeno-taser won't work on this as its not a 'real' carp. + +/mob/living/simple_mob/animal/space/carp/holographic/Initialize() + set_light(2) // Hologram lighting. + return ..() + +// Presumably the holodeck emag code requires this. +// Pass TRUE to make safe. Pass FALSE to make unsafe. +/mob/living/simple_mob/animal/space/carp/holographic/proc/set_safety(safe) + if(!isnull(get_AI_stance())) // Will return null if lacking an AI holder or a player is controlling it w/o autopilot var. + ai_holder.hostile = !safe // Inverted so safe = TRUE means hostility = FALSE. + ai_holder.forget_everything() // Reset state so it'll stop chewing on its target. + +// Called on death. +/mob/living/simple_mob/animal/space/carp/holographic/proc/derez() + visible_message(span("notice", "\The [src] fades away!")) + qdel(src) + +/mob/living/simple_mob/animal/space/carp/holographic/gib() + derez() // Holograms can't gib. + +/mob/living/simple_mob/animal/space/carp/holographic/death() + ..() + derez() + +// a slow-moving carp with the appearance of a sea mine and behaviour of a sea mine +/mob/living/simple_mob/animal/space/carp/puffer + name = "puffercarp" + desc = "A bloated, inflated carp covered in spines." + catalogue_data = list(/datum/category_item/catalogue/fauna/carp, /datum/category_item/catalogue/fauna/carp/puffer) + icon_state = "puffercarp" + icon_living = "puffercarp" + icon_dead = "puffercarp_dead" + icon_gib = "generic_gib" + movement_cooldown = 15 + var/ready_to_blow = TRUE + +/datum/category_item/catalogue/fauna/carp/puffer + name = "Voidborne Fauna - Space Carp: puffer variant" + desc = "An unusual subspecies of space carp with a novel defensive \ + and reproductive strategy - once the puffercarp is ready to spread spores \ + it begins to produce a highly volatile compound within its gas bladders, \ + which in addition to providing them with a means of propulsion through space \ + as per most space carp species, affords the puffercarp with a somewhat unique trait \ + - namely, that they are able to ignite and detonate their gas bladders \ + at will, and will do so aggressively when threatened. The bladders also tend to ignite \ + when struck by thermal or electrical discharges, or even sympathetic detonation from \ + other explosives - including other nearby puffercarp. As a result, most voidborne \ + predators have a tendency to keep clear, but even if this deterrent doesn't work the resulting \ + explosion serves to scatter their spores over a massive area - this improved seeding \ + strategy compared to regular carp results in the propagation of the species despite the \ + fact that it means each adult carp can only reproduce exactly once. \ +

                    \ + Due to their premature mortality it is extremely rare to see a puffercarp grow to any notable \ + size, often appearing to be somewhat stunted in growth compared to other subspecies, \ + their gas bloating being the only thing that brings them close to \ + the normal scale of an adult carp. " + value = CATALOGUER_REWARD_HARD //if you can hang around close enough to this thing without setting it off, you deserve it + +/mob/living/simple_mob/animal/space/carp/puffer/proc/kaboom() + if(ready_to_blow) + ready_to_blow = FALSE + gib() + var/turf/T = get_turf(src) + explosion(T, -1, -1, 4, 4) + + +/mob/living/simple_mob/animal/space/carp/puffer/apply_melee_effects() //it gets close enough to attack? EXPLODE + kaboom() + +/mob/living/simple_mob/animal/space/carp/puffer/adjustFireLoss(var/amount,var/include_robo) //you make it hot? EXPLODE + if(amount>0) + kaboom() + ..() + +/mob/living/simple_mob/animal/space/carp/puffer/ex_act() //explode? YOU BETTER BELIEVE THAT'S AN EXPLODE kaboom() \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/goose_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/goose_vr.dm index 203bd791092..830964f05f8 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/goose_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/goose_vr.dm @@ -1,7 +1,7 @@ -/datum/category_item/catalogue/fauna/geese - name = "Planetary Fauna - Geese" - desc = "A goose. HONK. Not much to catalogue, they're exactly the same as their earth counterparts." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/animal/space/goose/virgo3b +/datum/category_item/catalogue/fauna/geese + name = "Planetary Fauna - Geese" + desc = "A goose. HONK. Not much to catalogue, they're exactly the same as their earth counterparts." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/space/goose/virgo3b faction = "virgo3b" \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/space.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/space.dm index b7f77e64104..a96df2f7ac8 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/space.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/space.dm @@ -1,15 +1,15 @@ -// 'Space' mobs don't care about atmos (like carp) -/mob/living/simple_mob/animal/space - min_oxy = 0 - max_oxy = 0 - min_tox = 0 - max_tox = 0 - min_co2 = 0 - max_co2 = 0 - min_n2 = 0 - max_n2 = 0 - minbodytemp = 0 - -// They can also, you know, move around, in space -/mob/living/simple_mob/animal/space/Process_Spacemove(var/check_drift = 0) +// 'Space' mobs don't care about atmos (like carp) +/mob/living/simple_mob/animal/space + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + +// They can also, you know, move around, in space +/mob/living/simple_mob/animal/space/Process_Spacemove(var/check_drift = 0) return TRUE \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm b/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm index 31ffb6ef1db..c735a93b2a6 100644 --- a/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm +++ b/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm @@ -1,89 +1,89 @@ -// Blob simple_mobs generally get made from the blob random event. -// They're considered slimes for the purposes of attack bonuses from certain weapons. - -// Do not spawn, this is a base type. -/mob/living/simple_mob/blob - icon = 'icons/mob/blob.dmi' - pass_flags = PASSBLOB | PASSTABLE - faction = "blob" - - organ_names = /decl/mob_organ_names/blob - - heat_damage_per_tick = 0 - cold_damage_per_tick = 0 - min_oxy = 0 - max_oxy = 0 - min_tox = 0 - max_tox = 0 - min_co2 = 0 - max_co2 = 0 - min_n2 = 0 - max_n2 = 0 - minbodytemp = 0 - - taser_kill = FALSE - - var/mob/observer/blob/overmind = null - var/obj/structure/blob/factory/factory = null - var/datum/blob_type/blob_type = null // Used for the blob core items, as they have no overmind mob. - - mob_class = MOB_CLASS_SLIME - ai_holder_type = /datum/ai_holder/simple_mob/melee - -/mob/living/simple_mob/blob/speech_bubble_appearance() - return "slime" - -/mob/living/simple_mob/blob/update_icons() - if(overmind) - color = overmind.blob_type.complementary_color - else if(blob_type) - color = blob_type.complementary_color - else - color = null - ..() - -/mob/living/simple_mob/blob/Destroy() - if(overmind) - overmind.blob_mobs -= src - if(blob_type) - blob_type = null - return ..() - -/mob/living/simple_mob/blob/blob_act(obj/structure/blob/B) - if(!overmind && B.overmind) - overmind = B.overmind - faction = B.overmind.blob_type.faction - update_icon() - - if(faction != B.faction && B.overmind) - adjustBruteLoss(rand(B.overmind.blob_type.damage_lower, B.overmind.blob_type.damage_upper)) - - else if(stat != DEAD && health < maxHealth) - adjustBruteLoss(-maxHealth*0.0125) - adjustFireLoss(-maxHealth*0.0125) - -/mob/living/simple_mob/blob/CanPass(atom/movable/mover, turf/target) - if(istype(mover, /obj/structure/blob)) // Don't block blobs from expanding onto a tile occupied by a blob mob. - return TRUE - return ..() - -/mob/living/simple_mob/blob/Process_Spacemove() - for(var/obj/structure/blob/B in range(1, src)) - return TRUE - return ..() - -/mob/living/simple_mob/blob/IIsAlly(mob/living/L) - var/ally = ..(L) - if(!ally) - var/list/items = L.get_all_held_items() - for(var/obj/item/I in items) - if(istype(I, /obj/item/weapon/blobcore_chunk)) - var/obj/item/weapon/blobcore_chunk/BC = I - if(!overmind || (BC.blob_type && overmind.blob_type.type == BC.blob_type.type) || BC.blob_type.faction == faction) - ally = TRUE - break - - return ally - -/decl/mob_organ_names/blob +// Blob simple_mobs generally get made from the blob random event. +// They're considered slimes for the purposes of attack bonuses from certain weapons. + +// Do not spawn, this is a base type. +/mob/living/simple_mob/blob + icon = 'icons/mob/blob.dmi' + pass_flags = PASSBLOB | PASSTABLE + faction = "blob" + + organ_names = /decl/mob_organ_names/blob + + heat_damage_per_tick = 0 + cold_damage_per_tick = 0 + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + + taser_kill = FALSE + + var/mob/observer/blob/overmind = null + var/obj/structure/blob/factory/factory = null + var/datum/blob_type/blob_type = null // Used for the blob core items, as they have no overmind mob. + + mob_class = MOB_CLASS_SLIME + ai_holder_type = /datum/ai_holder/simple_mob/melee + +/mob/living/simple_mob/blob/speech_bubble_appearance() + return "slime" + +/mob/living/simple_mob/blob/update_icons() + if(overmind) + color = overmind.blob_type.complementary_color + else if(blob_type) + color = blob_type.complementary_color + else + color = null + ..() + +/mob/living/simple_mob/blob/Destroy() + if(overmind) + overmind.blob_mobs -= src + if(blob_type) + blob_type = null + return ..() + +/mob/living/simple_mob/blob/blob_act(obj/structure/blob/B) + if(!overmind && B.overmind) + overmind = B.overmind + faction = B.overmind.blob_type.faction + update_icon() + + if(faction != B.faction && B.overmind) + adjustBruteLoss(rand(B.overmind.blob_type.damage_lower, B.overmind.blob_type.damage_upper)) + + else if(stat != DEAD && health < maxHealth) + adjustBruteLoss(-maxHealth*0.0125) + adjustFireLoss(-maxHealth*0.0125) + +/mob/living/simple_mob/blob/CanPass(atom/movable/mover, turf/target) + if(istype(mover, /obj/structure/blob)) // Don't block blobs from expanding onto a tile occupied by a blob mob. + return TRUE + return ..() + +/mob/living/simple_mob/blob/Process_Spacemove() + for(var/obj/structure/blob/B in range(1, src)) + return TRUE + return ..() + +/mob/living/simple_mob/blob/IIsAlly(mob/living/L) + var/ally = ..(L) + if(!ally) + var/list/items = L.get_all_held_items() + for(var/obj/item/I in items) + if(istype(I, /obj/item/weapon/blobcore_chunk)) + var/obj/item/weapon/blobcore_chunk/BC = I + if(!overmind || (BC.blob_type && overmind.blob_type.type == BC.blob_type.type) || BC.blob_type.faction == faction) + ally = TRUE + break + + return ally + +/decl/mob_organ_names/blob hit_zones = list("mass") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm b/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm index d0ed44099bb..7ac90f935f9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm +++ b/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm @@ -1,158 +1,158 @@ -// Spores are made from blob factories. -// They are very weak and expendable, but can overwhelm when a lot of them are together. -// When attacking, spores will hit harder if near other friendly spores. -// Some blobs can infest dead non-robotic mobs, making them into Not Zombies. - -/mob/living/simple_mob/blob/spore - name = "blob spore" - desc = "A floating, fragile spore." - - icon_state = "blobpod" - icon_living = "blobpod" - glow_range = 3 - glow_intensity = 5 - layer = ABOVE_MOB_LAYER // Over the blob. - - health = 30 - maxHealth = 30 - melee_damage_lower = 2 - melee_damage_upper = 4 - movement_cooldown = -2 - hovering = TRUE - - attacktext = list("slammed into") - attack_sound = 'sound/effects/slime_squish.ogg' - say_list_type = /datum/say_list/spore - - organ_names = /decl/mob_organ_names/spore - - var/mob/living/carbon/human/infested = null // The human this thing is totally not making into a zombie. - var/can_infest = FALSE - var/is_infesting = FALSE - -/datum/say_list/spore - emote_see = list("sways", "inflates briefly") - -/datum/say_list/infested - emote_see = list("shambles around", "twitches", "stares") - - -/mob/living/simple_mob/blob/spore/infesting - name = "infesting blob spore" - can_infest = TRUE - -/mob/living/simple_mob/blob/spore/weak - name = "fragile blob spore" - health = 15 - maxHealth = 15 - melee_damage_lower = 1 - melee_damage_upper = 2 - -/mob/living/simple_mob/blob/spore/Initialize(mapload, var/obj/structure/blob/factory/my_factory) - if(istype(my_factory)) - factory = my_factory - factory.spores += src - return ..() - -/mob/living/simple_mob/blob/spore/Destroy() - if(factory) - factory.spores -= src - factory = null - if(infested) - infested.forceMove(get_turf(src)) - visible_message(span("warning", "\The [infested] falls to the ground as the blob spore bursts.")) - infested = null - return ..() - -/mob/living/simple_mob/blob/spore/death(gibbed, deathmessage = "bursts!") - if(overmind) - overmind.blob_type.on_spore_death(src) - ..(gibbed, deathmessage) - qdel(src) - -/mob/living/simple_mob/blob/spore/update_icons() - ..() // This will cut our overlays. - - if(overmind) - color = overmind.blob_type.complementary_color - glow_color = color - glow_toggle = TRUE - else if(blob_type) - color = blob_type.complementary_color - glow_color = color - glow_toggle = TRUE - else - color = null - glow_color = null - glow_toggle = FALSE - - if(is_infesting) - icon = infested.icon - copy_overlays(infested) - var/mutable_appearance/blob_head_overlay = mutable_appearance('icons/mob/blob.dmi', "blob_head") - if(overmind) - blob_head_overlay.color = overmind.blob_type.complementary_color - color = initial(color)//looks better. - add_overlay(blob_head_overlay, TRUE) - -/mob/living/simple_mob/blob/spore/handle_special() - ..() - if(can_infest && !is_infesting && isturf(loc)) - for(var/mob/living/carbon/human/H in view(src,1)) - if(H.stat != DEAD) // We want zombies. - continue - if(H.isSynthetic()) // Not philosophical zombies. - continue - infest(H) - break - - if(overmind) - overmind.blob_type.on_spore_lifetick(src) - - if(factory && z != factory.z) // This is to prevent spores getting lost in space and making the factory useless. - qdel(src) - -/mob/living/simple_mob/blob/spore/proc/infest(mob/living/carbon/human/H) - is_infesting = TRUE - if(H.wear_suit) - var/obj/item/clothing/suit/A = H.wear_suit - if(A.armor && A.armor["melee"]) - maxHealth += A.armor["melee"] //That zombie's got armor, I want armor! - - maxHealth += 40 - health = maxHealth - name = "Infested [H.real_name]" // Not using the Z word. - desc = "A parasitic organism attached to a deceased body, controlling it directly as if it were a puppet." - melee_damage_lower += 8 // 10 total. - melee_damage_upper += 11 // 15 total. - attacktext = list("clawed") - - H.forceMove(src) - infested = H - - say_list = new /datum/say_list/infested() - - update_icons() - visible_message(span("warning", "The corpse of [H.name] suddenly rises!")) - -/mob/living/simple_mob/blob/spore/GetIdCard() - if(infested) // If we've infested someone, use their ID. - return infested.GetIdCard() - -/mob/living/simple_mob/blob/spore/apply_bonus_melee_damage(A, damage_to_do) - var/helpers = 0 - for(var/mob/living/simple_mob/blob/spore/S in view(1, src)) - if(S == src) // Don't count ourselves. - continue - if(!IIsAlly(S)) // Only friendly spores make us stronger. - continue - // Friendly spores contribute 1/4th of their averaged attack power to our attack. - damage_to_do += ((S.melee_damage_lower + S.melee_damage_upper) / 2) / 4 - helpers++ - - if(helpers) - to_chat(src, span("notice", "Your attack is assisted by [helpers] other spore\s.")) - return damage_to_do - -/decl/mob_organ_names/spore - hit_zones = list("sporangium", "stolon", "sporangiophore") +// Spores are made from blob factories. +// They are very weak and expendable, but can overwhelm when a lot of them are together. +// When attacking, spores will hit harder if near other friendly spores. +// Some blobs can infest dead non-robotic mobs, making them into Not Zombies. + +/mob/living/simple_mob/blob/spore + name = "blob spore" + desc = "A floating, fragile spore." + + icon_state = "blobpod" + icon_living = "blobpod" + glow_range = 3 + glow_intensity = 5 + layer = ABOVE_MOB_LAYER // Over the blob. + + health = 30 + maxHealth = 30 + melee_damage_lower = 2 + melee_damage_upper = 4 + movement_cooldown = -2 + hovering = TRUE + + attacktext = list("slammed into") + attack_sound = 'sound/effects/slime_squish.ogg' + say_list_type = /datum/say_list/spore + + organ_names = /decl/mob_organ_names/spore + + var/mob/living/carbon/human/infested = null // The human this thing is totally not making into a zombie. + var/can_infest = FALSE + var/is_infesting = FALSE + +/datum/say_list/spore + emote_see = list("sways", "inflates briefly") + +/datum/say_list/infested + emote_see = list("shambles around", "twitches", "stares") + + +/mob/living/simple_mob/blob/spore/infesting + name = "infesting blob spore" + can_infest = TRUE + +/mob/living/simple_mob/blob/spore/weak + name = "fragile blob spore" + health = 15 + maxHealth = 15 + melee_damage_lower = 1 + melee_damage_upper = 2 + +/mob/living/simple_mob/blob/spore/Initialize(mapload, var/obj/structure/blob/factory/my_factory) + if(istype(my_factory)) + factory = my_factory + factory.spores += src + return ..() + +/mob/living/simple_mob/blob/spore/Destroy() + if(factory) + factory.spores -= src + factory = null + if(infested) + infested.forceMove(get_turf(src)) + visible_message(span("warning", "\The [infested] falls to the ground as the blob spore bursts.")) + infested = null + return ..() + +/mob/living/simple_mob/blob/spore/death(gibbed, deathmessage = "bursts!") + if(overmind) + overmind.blob_type.on_spore_death(src) + ..(gibbed, deathmessage) + qdel(src) + +/mob/living/simple_mob/blob/spore/update_icons() + ..() // This will cut our overlays. + + if(overmind) + color = overmind.blob_type.complementary_color + glow_color = color + glow_toggle = TRUE + else if(blob_type) + color = blob_type.complementary_color + glow_color = color + glow_toggle = TRUE + else + color = null + glow_color = null + glow_toggle = FALSE + + if(is_infesting) + icon = infested.icon + copy_overlays(infested) + var/mutable_appearance/blob_head_overlay = mutable_appearance('icons/mob/blob.dmi', "blob_head") + if(overmind) + blob_head_overlay.color = overmind.blob_type.complementary_color + color = initial(color)//looks better. + add_overlay(blob_head_overlay, TRUE) + +/mob/living/simple_mob/blob/spore/handle_special() + ..() + if(can_infest && !is_infesting && isturf(loc)) + for(var/mob/living/carbon/human/H in view(src,1)) + if(H.stat != DEAD) // We want zombies. + continue + if(H.isSynthetic()) // Not philosophical zombies. + continue + infest(H) + break + + if(overmind) + overmind.blob_type.on_spore_lifetick(src) + + if(factory && z != factory.z) // This is to prevent spores getting lost in space and making the factory useless. + qdel(src) + +/mob/living/simple_mob/blob/spore/proc/infest(mob/living/carbon/human/H) + is_infesting = TRUE + if(H.wear_suit) + var/obj/item/clothing/suit/A = H.wear_suit + if(A.armor && A.armor["melee"]) + maxHealth += A.armor["melee"] //That zombie's got armor, I want armor! + + maxHealth += 40 + health = maxHealth + name = "Infested [H.real_name]" // Not using the Z word. + desc = "A parasitic organism attached to a deceased body, controlling it directly as if it were a puppet." + melee_damage_lower += 8 // 10 total. + melee_damage_upper += 11 // 15 total. + attacktext = list("clawed") + + H.forceMove(src) + infested = H + + say_list = new /datum/say_list/infested() + + update_icons() + visible_message(span("warning", "The corpse of [H.name] suddenly rises!")) + +/mob/living/simple_mob/blob/spore/GetIdCard() + if(infested) // If we've infested someone, use their ID. + return infested.GetIdCard() + +/mob/living/simple_mob/blob/spore/apply_bonus_melee_damage(A, damage_to_do) + var/helpers = 0 + for(var/mob/living/simple_mob/blob/spore/S in view(1, src)) + if(S == src) // Don't count ourselves. + continue + if(!IIsAlly(S)) // Only friendly spores make us stronger. + continue + // Friendly spores contribute 1/4th of their averaged attack power to our attack. + damage_to_do += ((S.melee_damage_lower + S.melee_damage_upper) / 2) / 4 + helpers++ + + if(helpers) + to_chat(src, span("notice", "Your attack is assisted by [helpers] other spore\s.")) + return damage_to_do + +/decl/mob_organ_names/spore + hit_zones = list("sporangium", "stolon", "sporangiophore") diff --git a/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm b/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm index de3fc7980e1..cbc9293f9e5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm +++ b/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm @@ -1,116 +1,116 @@ -// Illusion type mobs pretend to be other things visually, and generally cannot be harmed as they're not 'real'. - -/mob/living/simple_mob/illusion - name = "illusion" - desc = "If you can read me, the game broke. Please report this to a coder." - - resistance = 1000 // Holograms are tough. - heat_resist = 1 - cold_resist = 1 - shock_resist = 1 - poison_resist = 1 - - movement_cooldown = -2 - mob_bump_flag = 0 // If the illusion can't be swapped it will be obvious. - - response_help = "pushes a hand through" - response_disarm = "tried to disarm" - response_harm = "tried to punch" - - mob_class = MOB_CLASS_ILLUSION - - ai_holder_type = /datum/ai_holder/simple_mob/inert/astar // Gets controlled manually by technomancers/admins, with AI pathfinding assistance. - - var/atom/movable/copying = null // The thing we're trying to look like. - var/realistic = FALSE // If true, things like bullets and weapons will hit it, to be a bit more convincing from a distance. - -/mob/living/simple_mob/illusion/update_icon() // We don't want the appearance changing AT ALL unless by copy_appearance(). - return - -/mob/living/simple_mob/illusion/proc/copy_appearance(atom/movable/thing_to_copy) - if(!thing_to_copy) - return FALSE - appearance = thing_to_copy.appearance - copying = thing_to_copy - density = thing_to_copy.density // So you can't bump into objects that aren't supposed to be dense. - catalogue_data = thing_to_copy.get_catalogue_data() - catalogue_delay = thing_to_copy.catalogue_delay - return TRUE - -// Because we can't perfectly duplicate some examine() output, we directly examine the AM it is copying. It's messy but -// this is to prevent easy checks from the opposing force. -/mob/living/simple_mob/illusion/examine(mob/user) - if(copying) - return copying.examine(user) - else - return list("???") - -/mob/living/simple_mob/illusion/bullet_act(obj/item/projectile/P) - if(!P) - return - - if(realistic) - return ..() - - return PROJECTILE_FORCE_MISS - -/mob/living/simple_mob/illusion/attack_hand(mob/living/carbon/human/M) - if(!realistic) - playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message(span("warning", "\The [M]'s hand goes through \the [src]!")) - return - else - switch(M.a_intent) - if(I_HELP) - var/datum/gender/T = gender_datums[src.get_visible_gender()] - M.visible_message( - span("notice", "\The [M] hugs [src] to make [T.him] feel better!"), \ - span("notice", "You hug [src] to make [T.him] feel better!") - ) // slightly redundant as at the moment most mobs still use the normal gender var, but it works and future-proofs it - playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - - if(I_DISARM) - playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message(span("danger", "\The [M] attempted to disarm [src]!")) - M.do_attack_animation(src) - - if(I_GRAB) - ..() - - if(I_HURT) - adjustBruteLoss(harm_intent_damage) - M.visible_message(span("danger", "\The [M] [response_harm] \the [src]")) - M.do_attack_animation(src) - -/mob/living/simple_mob/illusion/hit_with_weapon(obj/item/I, mob/living/user, effective_force, hit_zone) - if(realistic) - return ..() - - playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message(span("warning", "\The [user]'s [I] goes through \the [src]!")) - return FALSE - -/mob/living/simple_mob/illusion/ex_act() - return - -// Try to have the same tooltip, or else it becomes really obvious which one is fake. -/mob/living/simple_mob/illusion/get_nametag_name(mob/user) - if(copying) - return copying.get_nametag_name(user) - -/mob/living/simple_mob/illusion/get_nametag_desc(mob/user) - if(copying) - return copying.get_nametag_desc(user) - -// Cataloguer stuff. I don't think this will actually come up but better safe than sorry. -/mob/living/simple_mob/illusion/get_catalogue_data() - if(copying) - return copying.get_catalogue_data() - -/mob/living/simple_mob/illusion/can_catalogue() - if(copying) - return copying.can_catalogue() - -/mob/living/simple_mob/illusion/get_catalogue_delay() - if(copying) - return copying.get_catalogue_delay() +// Illusion type mobs pretend to be other things visually, and generally cannot be harmed as they're not 'real'. + +/mob/living/simple_mob/illusion + name = "illusion" + desc = "If you can read me, the game broke. Please report this to a coder." + + resistance = 1000 // Holograms are tough. + heat_resist = 1 + cold_resist = 1 + shock_resist = 1 + poison_resist = 1 + + movement_cooldown = -2 + mob_bump_flag = 0 // If the illusion can't be swapped it will be obvious. + + response_help = "pushes a hand through" + response_disarm = "tried to disarm" + response_harm = "tried to punch" + + mob_class = MOB_CLASS_ILLUSION + + ai_holder_type = /datum/ai_holder/simple_mob/inert/astar // Gets controlled manually by technomancers/admins, with AI pathfinding assistance. + + var/atom/movable/copying = null // The thing we're trying to look like. + var/realistic = FALSE // If true, things like bullets and weapons will hit it, to be a bit more convincing from a distance. + +/mob/living/simple_mob/illusion/update_icon() // We don't want the appearance changing AT ALL unless by copy_appearance(). + return + +/mob/living/simple_mob/illusion/proc/copy_appearance(atom/movable/thing_to_copy) + if(!thing_to_copy) + return FALSE + appearance = thing_to_copy.appearance + copying = thing_to_copy + density = thing_to_copy.density // So you can't bump into objects that aren't supposed to be dense. + catalogue_data = thing_to_copy.get_catalogue_data() + catalogue_delay = thing_to_copy.catalogue_delay + return TRUE + +// Because we can't perfectly duplicate some examine() output, we directly examine the AM it is copying. It's messy but +// this is to prevent easy checks from the opposing force. +/mob/living/simple_mob/illusion/examine(mob/user) + if(copying) + return copying.examine(user) + else + return list("???") + +/mob/living/simple_mob/illusion/bullet_act(obj/item/projectile/P) + if(!P) + return + + if(realistic) + return ..() + + return PROJECTILE_FORCE_MISS + +/mob/living/simple_mob/illusion/attack_hand(mob/living/carbon/human/M) + if(!realistic) + playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message(span("warning", "\The [M]'s hand goes through \the [src]!")) + return + else + switch(M.a_intent) + if(I_HELP) + var/datum/gender/T = gender_datums[src.get_visible_gender()] + M.visible_message( + span("notice", "\The [M] hugs [src] to make [T.him] feel better!"), \ + span("notice", "You hug [src] to make [T.him] feel better!") + ) // slightly redundant as at the moment most mobs still use the normal gender var, but it works and future-proofs it + playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + + if(I_DISARM) + playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message(span("danger", "\The [M] attempted to disarm [src]!")) + M.do_attack_animation(src) + + if(I_GRAB) + ..() + + if(I_HURT) + adjustBruteLoss(harm_intent_damage) + M.visible_message(span("danger", "\The [M] [response_harm] \the [src]")) + M.do_attack_animation(src) + +/mob/living/simple_mob/illusion/hit_with_weapon(obj/item/I, mob/living/user, effective_force, hit_zone) + if(realistic) + return ..() + + playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message(span("warning", "\The [user]'s [I] goes through \the [src]!")) + return FALSE + +/mob/living/simple_mob/illusion/ex_act() + return + +// Try to have the same tooltip, or else it becomes really obvious which one is fake. +/mob/living/simple_mob/illusion/get_nametag_name(mob/user) + if(copying) + return copying.get_nametag_name(user) + +/mob/living/simple_mob/illusion/get_nametag_desc(mob/user) + if(copying) + return copying.get_nametag_desc(user) + +// Cataloguer stuff. I don't think this will actually come up but better safe than sorry. +/mob/living/simple_mob/illusion/get_catalogue_data() + if(copying) + return copying.get_catalogue_data() + +/mob/living/simple_mob/illusion/can_catalogue() + if(copying) + return copying.can_catalogue() + +/mob/living/simple_mob/illusion/get_catalogue_delay() + if(copying) + return copying.get_catalogue_delay() diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm index e001972ae84..09c45811258 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm @@ -1,71 +1,71 @@ -/* - A corrupted maintenance drone, produced from what seems like a bad factory. - They also tend to dodge while in melee range. - Code "borrowed" from viscerator drones. <3 -*/ - -/datum/category_item/catalogue/technology/drone/corrupt_maint_drone - name = "Drone - Corrupted Maintenance Drone" - desc = "This drone appears to be a station maintenance drone, produced by some sort of corrupt fab, \ - which has caused it to become corrupt and attack anything nearby, except spiders and such, oddy. \ - If one is found, a swarm of others are not too far away.\ -

                    \ - The drone struggles to harm large targets, due to it's small size, yet it possesses a welder, which allows \ - it to **ERROR** inject it's targets, in addition to the small slashes from it's skittering claws. \ - The simplistic AI inside attempts to attack and then run, as it is aware that it is fairly weak, \ - using evasive tactics to avoid harm." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/mechanical/corrupt_maint_drone - name = "Corrupt Maintenance Drone" - desc = "A small, normal-looking drone. It looks like one you'd find on station, except... IT'S COMING AT YOU!" - catalogue_data = list(/datum/category_item/catalogue/technology/drone/corrupt_maint_drone) - - icon = 'icons/mob/robots_vr.dmi' - icon_state = "corrupt-repairbot" - icon_living = "corrupt-repairbot" - hovering = FALSE // Can trigger landmines. - - faction = "underdark" - maxHealth = 25 - health = 25 - movement_cooldown = -1 - movement_sound = 'sound/effects/servostep.ogg' - - pass_flags = PASSTABLE - mob_swap_flags = 0 - mob_push_flags = 0 - - melee_damage_lower = 6 // Approx 12 DPS. - melee_damage_upper = 6 - base_attack_cooldown = 2.5 // Four attacks per second. - attack_sharp = TRUE - attack_edge = 1 - attack_sound = 'sound/weapons/bladeslice.ogg' - attacktext = list("cut", "sliced") - - var/poison_type = "welder fuel" // The reagent that gets injected when it attacks. - var/poison_chance = 35 // Chance for injection to occur. - var/poison_per_bite = 5 // Amount added per injection. - - ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive - - -/mob/living/simple_mob/mechanical/corrupt_maint_drone/apply_melee_effects(var/atom/A) - if(isliving(A)) - var/mob/living/L = A - if(L.reagents) - var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) - if(L.can_inject(src, null, target_zone)) - inject_poison(L, target_zone) - -// Does actual poison injection, after all checks passed. -/mob/living/simple_mob/mechanical/corrupt_maint_drone/proc/inject_poison(mob/living/L, target_zone) - if(prob(poison_chance)) - to_chat(L, "Something burns in your veins.") - L.reagents.add_reagent(poison_type, poison_per_bite) - - -/mob/living/simple_mob/mechanical/corrupt_maint_drone/death() - ..(null,"is smashed into pieces!") - qdel(src) +/* + A corrupted maintenance drone, produced from what seems like a bad factory. + They also tend to dodge while in melee range. + Code "borrowed" from viscerator drones. <3 +*/ + +/datum/category_item/catalogue/technology/drone/corrupt_maint_drone + name = "Drone - Corrupted Maintenance Drone" + desc = "This drone appears to be a station maintenance drone, produced by some sort of corrupt fab, \ + which has caused it to become corrupt and attack anything nearby, except spiders and such, oddy. \ + If one is found, a swarm of others are not too far away.\ +

                    \ + The drone struggles to harm large targets, due to it's small size, yet it possesses a welder, which allows \ + it to **ERROR** inject it's targets, in addition to the small slashes from it's skittering claws. \ + The simplistic AI inside attempts to attack and then run, as it is aware that it is fairly weak, \ + using evasive tactics to avoid harm." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/mechanical/corrupt_maint_drone + name = "Corrupt Maintenance Drone" + desc = "A small, normal-looking drone. It looks like one you'd find on station, except... IT'S COMING AT YOU!" + catalogue_data = list(/datum/category_item/catalogue/technology/drone/corrupt_maint_drone) + + icon = 'icons/mob/robots_vr.dmi' + icon_state = "corrupt-repairbot" + icon_living = "corrupt-repairbot" + hovering = FALSE // Can trigger landmines. + + faction = "underdark" + maxHealth = 25 + health = 25 + movement_cooldown = -1 + movement_sound = 'sound/effects/servostep.ogg' + + pass_flags = PASSTABLE + mob_swap_flags = 0 + mob_push_flags = 0 + + melee_damage_lower = 6 // Approx 12 DPS. + melee_damage_upper = 6 + base_attack_cooldown = 2.5 // Four attacks per second. + attack_sharp = TRUE + attack_edge = 1 + attack_sound = 'sound/weapons/bladeslice.ogg' + attacktext = list("cut", "sliced") + + var/poison_type = "welder fuel" // The reagent that gets injected when it attacks. + var/poison_chance = 35 // Chance for injection to occur. + var/poison_per_bite = 5 // Amount added per injection. + + ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive + + +/mob/living/simple_mob/mechanical/corrupt_maint_drone/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(L.reagents) + var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + if(L.can_inject(src, null, target_zone)) + inject_poison(L, target_zone) + +// Does actual poison injection, after all checks passed. +/mob/living/simple_mob/mechanical/corrupt_maint_drone/proc/inject_poison(mob/living/L, target_zone) + if(prob(poison_chance)) + to_chat(L, "Something burns in your veins.") + L.reagents.add_reagent(poison_type, poison_per_bite) + + +/mob/living/simple_mob/mechanical/corrupt_maint_drone/death() + ..(null,"is smashed into pieces!") + qdel(src) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm index 5ea69697d65..fbfedf1cd79 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm @@ -1,70 +1,70 @@ - -/datum/category_item/catalogue/technology/drone/infectionbot - name = "Drone - Injection Drone" - desc = "A strange and aged drone, this model appears to be made for gathering of genetic samples,\ - sacrificing both power and durability for storage space and advanced scanners.\ - The drone has clear dents and scratches all over it's casing bulging inwards in multiple areas,\ - some even penetrated showing the advanced and headache inducing parts inside.\ -

                    \ - This model in particular has a collection of hostile and spinetingeling parts on the underside,\ - small advanced anti-gravity generators located between giant syringes made for injecting and extracting all manners of fluids\ - and samples of soil or other biological matter.\ - Most interesting is the syringe it uses after taking a sample of any creature, the dry, flakey substance\ - it injects is rotten and expired toxins, seemingly once intended to heal damage from samples taken.\ - The container housing the substance as well as the fabricator showing great blunt trauma as well as environmental damage,\ - neither part salvageable but still operational, outputting near 0% efficiency making it near impossible to refill the housing unit this century.\ -

                    \ - The drone's frame extremily light weight but robust, unbendable by hand, is barren of any markings or ID,\ - no traces of paint visible and any 'writing' visible is uncomprehendable, short term scan unable to translate." - value = CATALOGUER_REWARD_MEDIUM - -/mob/living/simple_mob/mechanical/infectionbot - name = "Strange robot" - desc = "You get the feeling you should run." - icon = 'icons/mob/vore.dmi' - icon_state = "vagrant" - icon_living = "vagrant" - icon_dead = "vagrant" - icon_gib = "vagrant" - - maxHealth = 65 - health = 40 - movement_cooldown = 1 - - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "hits the" - faction = "vagrant" - harm_intent_damage = 3 - melee_damage_lower = 6 - melee_damage_upper = 9 - light_color = "#8a0707" - attacktext = "drugged" - attack_sound = 'sound/weapons/bite.ogg' - - ai_holder_type = /datum/ai_holder/simple_mob/melee - say_list_type = /datum/say_list/disbot - - var/poison_chance = 100 - var/poison_per_bite = 10 - var/poison_type = "expired_medicine" - -/datum/say_list/disbot - speak = list("ATTEMPTING TO CONTACT A.R.K, ATTEMPT 1e26+3","DIRT SAMPLE COLLECTED, DIRT QUOTA 124871/155 CONFIRMED.") - emote_see = list("scans the dirt around it","beeps as it scans a rock nearby") - say_maybe_target = list("BIOLOGICAL TRACES FOUND, ATTEMTPTING TO LOCATE SOURCE.","TRACE SOURCES FOUND, POWERING SCANNERS.",) - say_got_target = list("LIFEFORM LOCATED, ATTEMPTING TO COLLECT SAMPLE","CREATURE SPOTTED, PHEROMONE GENERATORS DAMAGED, ATTEMPTING TO COLLECT GENETIC SAMPLE.") - -/mob/living/simple_mob/mechanical/infectionbot/apply_melee_effects(var/atom/A) - if(isliving(A)) - var/mob/living/L = A - if(L.reagents) - var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) - if(L.can_inject(src, null, target_zone)) - inject_poison(L, target_zone) - -// Does actual poison injection, after all checks passed. -/mob/living/simple_mob/mechanical/infectionbot/proc/inject_poison(mob/living/L, target_zone) - if(prob(poison_chance)) - to_chat(L, "You feel a tiny prick.") - L.reagents.add_reagent(poison_type, poison_per_bite) + +/datum/category_item/catalogue/technology/drone/infectionbot + name = "Drone - Injection Drone" + desc = "A strange and aged drone, this model appears to be made for gathering of genetic samples,\ + sacrificing both power and durability for storage space and advanced scanners.\ + The drone has clear dents and scratches all over it's casing bulging inwards in multiple areas,\ + some even penetrated showing the advanced and headache inducing parts inside.\ +

                    \ + This model in particular has a collection of hostile and spinetingeling parts on the underside,\ + small advanced anti-gravity generators located between giant syringes made for injecting and extracting all manners of fluids\ + and samples of soil or other biological matter.\ + Most interesting is the syringe it uses after taking a sample of any creature, the dry, flakey substance\ + it injects is rotten and expired toxins, seemingly once intended to heal damage from samples taken.\ + The container housing the substance as well as the fabricator showing great blunt trauma as well as environmental damage,\ + neither part salvageable but still operational, outputting near 0% efficiency making it near impossible to refill the housing unit this century.\ +

                    \ + The drone's frame extremily light weight but robust, unbendable by hand, is barren of any markings or ID,\ + no traces of paint visible and any 'writing' visible is uncomprehendable, short term scan unable to translate." + value = CATALOGUER_REWARD_MEDIUM + +/mob/living/simple_mob/mechanical/infectionbot + name = "Strange robot" + desc = "You get the feeling you should run." + icon = 'icons/mob/vore.dmi' + icon_state = "vagrant" + icon_living = "vagrant" + icon_dead = "vagrant" + icon_gib = "vagrant" + + maxHealth = 65 + health = 40 + movement_cooldown = 1 + + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "hits the" + faction = "vagrant" + harm_intent_damage = 3 + melee_damage_lower = 6 + melee_damage_upper = 9 + light_color = "#8a0707" + attacktext = "drugged" + attack_sound = 'sound/weapons/bite.ogg' + + ai_holder_type = /datum/ai_holder/simple_mob/melee + say_list_type = /datum/say_list/disbot + + var/poison_chance = 100 + var/poison_per_bite = 10 + var/poison_type = "expired_medicine" + +/datum/say_list/disbot + speak = list("ATTEMPTING TO CONTACT A.R.K, ATTEMPT 1e26+3","DIRT SAMPLE COLLECTED, DIRT QUOTA 124871/155 CONFIRMED.") + emote_see = list("scans the dirt around it","beeps as it scans a rock nearby") + say_maybe_target = list("BIOLOGICAL TRACES FOUND, ATTEMTPTING TO LOCATE SOURCE.","TRACE SOURCES FOUND, POWERING SCANNERS.",) + say_got_target = list("LIFEFORM LOCATED, ATTEMPTING TO COLLECT SAMPLE","CREATURE SPOTTED, PHEROMONE GENERATORS DAMAGED, ATTEMPTING TO COLLECT GENETIC SAMPLE.") + +/mob/living/simple_mob/mechanical/infectionbot/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(L.reagents) + var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + if(L.can_inject(src, null, target_zone)) + inject_poison(L, target_zone) + +// Does actual poison injection, after all checks passed. +/mob/living/simple_mob/mechanical/infectionbot/proc/inject_poison(mob/living/L, target_zone) + if(prob(poison_chance)) + to_chat(L, "You feel a tiny prick.") + L.reagents.add_reagent(poison_type, poison_per_bite) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm index 10d195f281d..a0ceb1b1af0 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm @@ -1,158 +1,158 @@ -// The GOLEM is a spell-flinging synthetic. - -/mob/living/simple_mob/mechanical/technomancer_golem - name = "unknown synthetic" - desc = "A rather unusual looking synthetic." - icon = 'icons/mob/mob.dmi' - icon_state = "golem" - health = 300 - maxHealth = 300 - - faction = "golem" - - response_help = "pets" - response_disarm = "pushes away" - response_harm = "punches" - harm_intent_damage = 3 - friendly = "hugs" - - organ_names = /decl/mob_organ_names/golem - - melee_damage_lower = 30 // It has a built in esword. - melee_damage_upper = 30 - attack_armor_pen = 20 - attack_sound = 'sound/weapons/blade1.ogg' - attacktext = list("slashed") - melee_attack_delay = 0.5 SECONDS // Even has custom attack animations. - ranged_attack_delay = 0.5 SECONDS - special_attack_delay = 1 SECOND - - special_attack_min_range = 0 - special_attack_max_range = 7 - - ai_holder_type = /datum/ai_holder/simple_mob/melee - - var/obj/item/weapon/technomancer_core/golem/core = null - var/obj/item/weapon/spell/active_spell = null // Shield and ranged spells - var/mob/living/master = null - var/casting = FALSE // Used to ensure the correct animation is played. Testing if a spell exists won't always work as some spells delete themselves upon use. - - var/list/known_spells = list( - "beam" = /obj/item/weapon/spell/projectile/beam, - "chain lightning" = /obj/item/weapon/spell/projectile/chain_lightning, - "force missile" = /obj/item/weapon/spell/projectile/force_missile, - "ionic bolt" = /obj/item/weapon/spell/projectile/ionic_bolt, - "lightning" = /obj/item/weapon/spell/projectile/lightning, - "blink" = /obj/item/weapon/spell/blink, - "dispel" = /obj/item/weapon/spell/dispel, - "oxygenate" = /obj/item/weapon/spell/oxygenate, - "mend life" = /obj/item/weapon/spell/modifier/mend_life, - "mend synthetic" = /obj/item/weapon/spell/modifier/mend_synthetic, - "mend organs" = /obj/item/weapon/spell/mend_organs, - "purify" = /obj/item/weapon/spell/modifier/purify, - "resurrect" = /obj/item/weapon/spell/resurrect, - "passwall" = /obj/item/weapon/spell/passwall, - "repel missiles" = /obj/item/weapon/spell/modifier/repel_missiles, - "corona" = /obj/item/weapon/spell/modifier/corona, - "haste" = /obj/item/weapon/spell/modifier/haste - ) - -/mob/living/simple_mob/mechanical/technomancer_golem/Initialize() - core = new(src) - return ..() - -/mob/living/simple_mob/mechanical/technomancer_golem/Destroy() - qdel(core) - return ..() - -/mob/living/simple_mob/mechanical/technomancer_golem/unref_spell() - active_spell = null - return ..() - -/mob/living/simple_mob/mechanical/technomancer_golem/death() - ..() - visible_message("\The [src] disintegrates!") - new /obj/effect/decal/cleanable/blood/gibs/robot(src.loc) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(3, 1, src) - s.start() - qdel(src) - -/mob/living/simple_mob/mechanical/technomancer_golem/place_spell_in_hand(var/path) - if(!path || !ispath(path)) - return FALSE - if(active_spell) - qdel(active_spell) - - active_spell = new path(src) - -/mob/living/simple_mob/mechanical/technomancer_golem/verb/test_giving_spells() - var/choice = tgui_input_list(usr, "What spell?", "Give spell", known_spells) - if(choice) - place_spell_in_hand(known_spells[choice]) - else - qdel(active_spell) - -/mob/living/simple_mob/mechanical/technomancer_golem/get_technomancer_core() - return core - -/mob/living/simple_mob/mechanical/technomancer_golem/can_special_attack(atom/A) - if(active_spell) // Don't bother checking everything else if no spell is ready. - return ..() - return FALSE - -/mob/living/simple_mob/mechanical/technomancer_golem/should_special_attack(atom/A) - return instability < 50 // Don't kill ourselves by casting everything. - - -/mob/living/simple_mob/mechanical/technomancer_golem/do_special_attack(atom/A) - var/proximity = Adjacent(A) - if(active_spell) - if(proximity && active_spell.cast_methods & CAST_MELEE) // Use melee method if available and close enough. - return active_spell.on_melee_cast(A, src) - else if(active_spell.cast_methods & CAST_RANGED) // Otherwise use ranged if possible. Will also work for point-blank range. - return active_spell.on_ranged_cast(A, src) - return ..() - -/mob/living/simple_mob/mechanical/technomancer_golem/melee_pre_animation(atom/A) - if(active_spell && active_spell.cast_methods & CAST_MELEE|CAST_RANGED) // If they're trying to melee-cast a spell, use the special animation instead. - special_pre_animation(A) - return - - flick("golem_pre_melee", src) // To force the animation to restart. - icon_living = "golem_pre_melee" // The animation will hold after this point until melee_post_animation() gets called. - icon_state = "golem_pre_melee" - setClickCooldown(2) - -/mob/living/simple_mob/mechanical/technomancer_golem/melee_post_animation(atom/A) - if(casting) // Some spells delete themselves when used, so we use a different variable set earlier instead. - special_post_animation(A) - return - - flick("golem_post_melee", src) - icon_living = "golem" - icon_state = "golem" - setClickCooldown(6) - -/mob/living/simple_mob/mechanical/technomancer_golem/ranged_pre_animation(atom/A) - flick("golem_pre_ranged", src) - icon_living = "golem_pre_ranged" - icon_state = "golem_pre_ranged" - setClickCooldown(5) - -/mob/living/simple_mob/mechanical/technomancer_golem/ranged_post_animation(atom/A) - flick("golem_post_ranged", src) - icon_living = "golem" - icon_state = "golem" - setClickCooldown(5) - -/mob/living/simple_mob/mechanical/technomancer_golem/special_pre_animation(atom/A) - casting = TRUE - ranged_pre_animation(A) // Both have the same animation. - -/mob/living/simple_mob/mechanical/technomancer_golem/special_post_animation(atom/A) - casting = FALSE - ranged_post_animation(A) - -/decl/mob_organ_names/golem - hit_zones = list("helmet", "cuirass", "left tasset", "right tasset", "left gauntlet", "right gauntlet", "weapon") +// The GOLEM is a spell-flinging synthetic. + +/mob/living/simple_mob/mechanical/technomancer_golem + name = "unknown synthetic" + desc = "A rather unusual looking synthetic." + icon = 'icons/mob/mob.dmi' + icon_state = "golem" + health = 300 + maxHealth = 300 + + faction = "golem" + + response_help = "pets" + response_disarm = "pushes away" + response_harm = "punches" + harm_intent_damage = 3 + friendly = "hugs" + + organ_names = /decl/mob_organ_names/golem + + melee_damage_lower = 30 // It has a built in esword. + melee_damage_upper = 30 + attack_armor_pen = 20 + attack_sound = 'sound/weapons/blade1.ogg' + attacktext = list("slashed") + melee_attack_delay = 0.5 SECONDS // Even has custom attack animations. + ranged_attack_delay = 0.5 SECONDS + special_attack_delay = 1 SECOND + + special_attack_min_range = 0 + special_attack_max_range = 7 + + ai_holder_type = /datum/ai_holder/simple_mob/melee + + var/obj/item/weapon/technomancer_core/golem/core = null + var/obj/item/weapon/spell/active_spell = null // Shield and ranged spells + var/mob/living/master = null + var/casting = FALSE // Used to ensure the correct animation is played. Testing if a spell exists won't always work as some spells delete themselves upon use. + + var/list/known_spells = list( + "beam" = /obj/item/weapon/spell/projectile/beam, + "chain lightning" = /obj/item/weapon/spell/projectile/chain_lightning, + "force missile" = /obj/item/weapon/spell/projectile/force_missile, + "ionic bolt" = /obj/item/weapon/spell/projectile/ionic_bolt, + "lightning" = /obj/item/weapon/spell/projectile/lightning, + "blink" = /obj/item/weapon/spell/blink, + "dispel" = /obj/item/weapon/spell/dispel, + "oxygenate" = /obj/item/weapon/spell/oxygenate, + "mend life" = /obj/item/weapon/spell/modifier/mend_life, + "mend synthetic" = /obj/item/weapon/spell/modifier/mend_synthetic, + "mend organs" = /obj/item/weapon/spell/mend_organs, + "purify" = /obj/item/weapon/spell/modifier/purify, + "resurrect" = /obj/item/weapon/spell/resurrect, + "passwall" = /obj/item/weapon/spell/passwall, + "repel missiles" = /obj/item/weapon/spell/modifier/repel_missiles, + "corona" = /obj/item/weapon/spell/modifier/corona, + "haste" = /obj/item/weapon/spell/modifier/haste + ) + +/mob/living/simple_mob/mechanical/technomancer_golem/Initialize() + core = new(src) + return ..() + +/mob/living/simple_mob/mechanical/technomancer_golem/Destroy() + qdel(core) + return ..() + +/mob/living/simple_mob/mechanical/technomancer_golem/unref_spell() + active_spell = null + return ..() + +/mob/living/simple_mob/mechanical/technomancer_golem/death() + ..() + visible_message("\The [src] disintegrates!") + new /obj/effect/decal/cleanable/blood/gibs/robot(src.loc) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + qdel(src) + +/mob/living/simple_mob/mechanical/technomancer_golem/place_spell_in_hand(var/path) + if(!path || !ispath(path)) + return FALSE + if(active_spell) + qdel(active_spell) + + active_spell = new path(src) + +/mob/living/simple_mob/mechanical/technomancer_golem/verb/test_giving_spells() + var/choice = tgui_input_list(usr, "What spell?", "Give spell", known_spells) + if(choice) + place_spell_in_hand(known_spells[choice]) + else + qdel(active_spell) + +/mob/living/simple_mob/mechanical/technomancer_golem/get_technomancer_core() + return core + +/mob/living/simple_mob/mechanical/technomancer_golem/can_special_attack(atom/A) + if(active_spell) // Don't bother checking everything else if no spell is ready. + return ..() + return FALSE + +/mob/living/simple_mob/mechanical/technomancer_golem/should_special_attack(atom/A) + return instability < 50 // Don't kill ourselves by casting everything. + + +/mob/living/simple_mob/mechanical/technomancer_golem/do_special_attack(atom/A) + var/proximity = Adjacent(A) + if(active_spell) + if(proximity && active_spell.cast_methods & CAST_MELEE) // Use melee method if available and close enough. + return active_spell.on_melee_cast(A, src) + else if(active_spell.cast_methods & CAST_RANGED) // Otherwise use ranged if possible. Will also work for point-blank range. + return active_spell.on_ranged_cast(A, src) + return ..() + +/mob/living/simple_mob/mechanical/technomancer_golem/melee_pre_animation(atom/A) + if(active_spell && active_spell.cast_methods & CAST_MELEE|CAST_RANGED) // If they're trying to melee-cast a spell, use the special animation instead. + special_pre_animation(A) + return + + flick("golem_pre_melee", src) // To force the animation to restart. + icon_living = "golem_pre_melee" // The animation will hold after this point until melee_post_animation() gets called. + icon_state = "golem_pre_melee" + setClickCooldown(2) + +/mob/living/simple_mob/mechanical/technomancer_golem/melee_post_animation(atom/A) + if(casting) // Some spells delete themselves when used, so we use a different variable set earlier instead. + special_post_animation(A) + return + + flick("golem_post_melee", src) + icon_living = "golem" + icon_state = "golem" + setClickCooldown(6) + +/mob/living/simple_mob/mechanical/technomancer_golem/ranged_pre_animation(atom/A) + flick("golem_pre_ranged", src) + icon_living = "golem_pre_ranged" + icon_state = "golem_pre_ranged" + setClickCooldown(5) + +/mob/living/simple_mob/mechanical/technomancer_golem/ranged_post_animation(atom/A) + flick("golem_post_ranged", src) + icon_living = "golem" + icon_state = "golem" + setClickCooldown(5) + +/mob/living/simple_mob/mechanical/technomancer_golem/special_pre_animation(atom/A) + casting = TRUE + ranged_pre_animation(A) // Both have the same animation. + +/mob/living/simple_mob/mechanical/technomancer_golem/special_post_animation(atom/A) + casting = FALSE + ranged_post_animation(A) + +/decl/mob_organ_names/golem + hit_zones = list("helmet", "cuirass", "left tasset", "right tasset", "left gauntlet", "right gauntlet", "weapon") diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem_vr.dm index aab1387dbc1..f0d6da0a868 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem_vr.dm @@ -1,10 +1,10 @@ -// Cataloguer data below - strange we can catalogue space golem wizards -/datum/category_item/catalogue/technology/drone/technomancer_golem - name = "Drone - Technomancer Golem" - desc = "Some sort of advanced, unnatural looking synthetic, built for combat.\ - It has a black-and-blue chassis, and wields some sort of... stun baton in it's hand.\ - The drone has pristine armor, black and shiny, with the blue synth-parts glowing visibly from inside.\ -

                    \ - The drone's frame is heavy and armored, unbendable by hand, is barren of any markings or ID,\ - no traces of paint visible and any 'writing' visible is uncomprehendable, short term scan unable to translate." - value = CATALOGUER_REWARD_MEDIUM +// Cataloguer data below - strange we can catalogue space golem wizards +/datum/category_item/catalogue/technology/drone/technomancer_golem + name = "Drone - Technomancer Golem" + desc = "Some sort of advanced, unnatural looking synthetic, built for combat.\ + It has a black-and-blue chassis, and wields some sort of... stun baton in it's hand.\ + The drone has pristine armor, black and shiny, with the blue synth-parts glowing visibly from inside.\ +

                    \ + The drone's frame is heavy and armored, unbendable by hand, is barren of any markings or ID,\ + no traces of paint visible and any 'writing' visible is uncomprehendable, short term scan unable to translate." + value = CATALOGUER_REWARD_MEDIUM diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot_vr.dm index 37a3eb81cf8..ab63ffc4aeb 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot_vr.dm @@ -1,7 +1,7 @@ -/datum/category_item/catalogue/technology/drone/hivebot // Hivebot Scanner Data - This is for Generic Hivebots - name = "Drone - Hivebot" - desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to lack a specific weapon, \ - but uses a regular bullet-type weapon, firing a single projectile with a delay. Once upon a time, these bots may \ - have been used to be some sort of... security, or defensive machinery, at a guess, but their original/true purpose is \ - unclear. Whatever the matter, they're hostile and will engage anything they see, shooting to kill." - value = CATALOGUER_REWARD_HARD +/datum/category_item/catalogue/technology/drone/hivebot // Hivebot Scanner Data - This is for Generic Hivebots + name = "Drone - Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to lack a specific weapon, \ + but uses a regular bullet-type weapon, firing a single projectile with a delay. Once upon a time, these bots may \ + have been used to be some sort of... security, or defensive machinery, at a guess, but their original/true purpose is \ + unclear. Whatever the matter, they're hostile and will engage anything they see, shooting to kill." + value = CATALOGUER_REWARD_HARD diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage_vr.dm index 31c9cdf10fa..4f57f313a30 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage_vr.dm @@ -1,27 +1,27 @@ -/datum/category_item/catalogue/technology/drone/hivebot/rapidfire // Hivebot Scanner Data - This is for Rapidfire Hivebots - name = "Drone - Rapidfire Hivebot" - desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ - rifle, built for high-rate fire. Other than that, it has similar yellowish color \ - to regular hivebots." - value = CATALOGUER_REWARD_HARD - -/datum/category_item/catalogue/technology/drone/hivebot/laser // Hivebot Scanner Data - This is for Laser Hivebots - name = "Drone - Laser Hivebot" - desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ - laser weapon, different from ion bolts, firing bright, vibrant blue bolts of energy. Other than that, it has similar yellowish color \ - to regular hivebots." - value = CATALOGUER_REWARD_HARD - -/datum/category_item/catalogue/technology/drone/hivebot/ion // Hivebot Scanner Data - This is for Ion Hivebots - name = "Drone - Ion Hivebot" - desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ - electromagnetic pulse generator, firing bright, vibrant blue bolts of ion energy. Other than that, it has similar yellowish color \ - to regular hivebots." - value = CATALOGUER_REWARD_HARD - -/datum/category_item/catalogue/technology/drone/hivebot/strong // Hivebot Scanner Data - This is for Laser Hivebots - name = "Drone - Strong Laser Hivebot" - desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ - ballistic weapon. The weapon seems to fire larger projectiles, and it has heavier armor. Other than that, it has similar yellowish color \ - to regular hivebots." - value = CATALOGUER_REWARD_HARD +/datum/category_item/catalogue/technology/drone/hivebot/rapidfire // Hivebot Scanner Data - This is for Rapidfire Hivebots + name = "Drone - Rapidfire Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + rifle, built for high-rate fire. Other than that, it has similar yellowish color \ + to regular hivebots." + value = CATALOGUER_REWARD_HARD + +/datum/category_item/catalogue/technology/drone/hivebot/laser // Hivebot Scanner Data - This is for Laser Hivebots + name = "Drone - Laser Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + laser weapon, different from ion bolts, firing bright, vibrant blue bolts of energy. Other than that, it has similar yellowish color \ + to regular hivebots." + value = CATALOGUER_REWARD_HARD + +/datum/category_item/catalogue/technology/drone/hivebot/ion // Hivebot Scanner Data - This is for Ion Hivebots + name = "Drone - Ion Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + electromagnetic pulse generator, firing bright, vibrant blue bolts of ion energy. Other than that, it has similar yellowish color \ + to regular hivebots." + value = CATALOGUER_REWARD_HARD + +/datum/category_item/catalogue/technology/drone/hivebot/strong // Hivebot Scanner Data - This is for Laser Hivebots + name = "Drone - Strong Laser Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + ballistic weapon. The weapon seems to fire larger projectiles, and it has heavier armor. Other than that, it has similar yellowish color \ + to regular hivebots." + value = CATALOGUER_REWARD_HARD diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support_vr.dm index 434ce659e85..72cbeac0e6d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support_vr.dm @@ -1,13 +1,13 @@ -/datum/category_item/catalogue/technology/drone/hivebot/commander // Hivebot Scanner Data - This is for Commander Hivebots - name = "Drone - Commander Hivebot" - desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ - ballistic weapon. It also appears to have hardened internal connections and network interlinks, as well as some sort of datalink \ - to the other hivebots. Other than that, it has similar yellowish color to regular hivebots." - value = CATALOGUER_REWARD_HARD - -/datum/category_item/catalogue/technology/drone/hivebot/logistics // Hivebot Scanner Data - This is for Logistics Hivebots - name = "Drone - Logistics Hivebot" - desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ - ballistic weapon. It also appears to have supply deploying bays, and internal fabs to repair and buff their allies' special capabilities. \ - Other than that, it has similar yellowish color to regular hivebots." - value = CATALOGUER_REWARD_HARD +/datum/category_item/catalogue/technology/drone/hivebot/commander // Hivebot Scanner Data - This is for Commander Hivebots + name = "Drone - Commander Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + ballistic weapon. It also appears to have hardened internal connections and network interlinks, as well as some sort of datalink \ + to the other hivebots. Other than that, it has similar yellowish color to regular hivebots." + value = CATALOGUER_REWARD_HARD + +/datum/category_item/catalogue/technology/drone/hivebot/logistics // Hivebot Scanner Data - This is for Logistics Hivebots + name = "Drone - Logistics Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + ballistic weapon. It also appears to have supply deploying bays, and internal fabs to repair and buff their allies' special capabilities. \ + Other than that, it has similar yellowish color to regular hivebots." + value = CATALOGUER_REWARD_HARD diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm index f45c1b185df..653abae3eb9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm @@ -1,349 +1,349 @@ -// Stronger than a regular Dark Gygax, this one has three special attacks, based on intents. -// First special attack launches three arcing rockets at the current target. -// Second special attack fires a projectile that creates a short-lived microsingularity that pulls in everything nearby. Magboots can protect from this. -// Third special attack creates a dangerous electric field that causes escalating electric damage, before emitting a tesla shock and blinding anyone looking at the mecha. -// The AI will choose one every ten seconds. - -/datum/category_item/catalogue/technology/adv_dark_gygax - name = "Exosuit - Advanced Dark Gygax" - desc = "This exosuit is an experimental prototype, descended from the Dark Gygax. It retains the \ - speed that is characteristic of the other models, yet outclasses all of them in durability, \ - to the point of having a comparable amount of protection to models that placed a higher emphesis \ - on armor, like the Durand and even the Marauder. It is also much larger in scale, and significantly \ - heavier than most other exosuits developed by humans, which often causes shockwaves to be felt \ - whenever it moves. This has been observed to have a demoralizing effect on hostile forces.\ -

                    \ - Weapons & Power System
                    \ - Attached to the exosuit's chassis are several newly invented mounted weapons, each unique in purpose and capability. \ - These weapons are integral to the chassis as opposed to the modular equipment that more traditional exosuits utilize. \ - It is unknown if that is due to simply being an early prototype, or if discarding the modular design is benefitial \ - to the design of the model.\ -

                    \ - All the weapons utilize energy, as opposed to consumable projectiles. This appears to have been a conscious decision to \ - allow for more staying power, by only being limited by availablity of electricity. \ - In order to supply the needed energy for combat, the ADG contains a miniturized fusion reactor, which is also \ - considered experimental due to its size. The reactor is powerful enough to power the actuators, electronics, \ - and the primary weapon. The supplementary weapons, however, cannot be continiously fired and instead draw from \ - a electrical buffer that is constantly replenished by the reactor.\ -

                    \ - Homing Energy Bolts
                    \ - The primary weapon is a projector that fires somewhat slow moving blue bolts of energy. The ADG is able to \ - passively redirect the trajectory of the blue bolts towards the initial target, essentially acting as a \ - homing projectile. The blue bolt itself is otherwise not very powerful compared to conventional photonic \ - weaponry or ballistic shells, however the power required to fire the main gun is significantly less \ - than the other available weapons, and so the ADG uses it as the main weapon.\ -

                    \ - Self-Supplying Missile Launcher
                    \ - The first supplementary weapon would appear to not be an energy weapon, as it is a missile launcher. \ - What is not obvious is that the missiles are fabricated inside the exosuit, with the physical \ - materials also being created from energy, similar to the newer models of Rapid Construction Devices. \ - Therefore, the ADG does not need to concern itself with running out of missiles. The missiles themselves \ - are optimized towards harming hard targets, such as other exosuits, but are also still dangerous to soft \ - targets like infantry.\ -

                    \ - Electric Defense
                    \ - The second supplementary weapon is not a conventional gun. Instead, the ADG weaponizes its electrical \ - systems by redirecting power output from its fusion reactor to its exterior shell, becoming a walking \ - tesla coil. This generates a strong electric field that harms anything unprotected nearby. \ - The electric field grows in power, until reaching a critical point, after which a blinding flash \ - of light and arcs of lightning fly out from the exosuit towards its surroundings.\ -

                    \ - Microsingularity Projector
                    \ - Finally, the third supplementary weapon utilizes gravitation as a weapon, by firing a blue energetic orb \ - that, upon hitting the ground, collapses and causes a 'microsingularity' to emerge briefly, pulling in \ - anything unsecured, such as personnel or weapons. The microsingularity lacks the means to gain any energy, meaning it \ - will dissipate in a few seconds, and so it is probably safe to use on a planetary body.\ -

                    \ - Flaws
                    \ - It would appear the ADG is poised to take the place of other exosuits like the Marauder, however several \ - massive flaws exist to make that unlikely. Firstly, this exosuit is almost an order of magnitude more \ - costly to produce than comparable alternatives, even accounting for being a prototype. \ - Secondly, a number of weapons integrated into the ADG are dangerous both to enemies and \ - allies, limiting the ability for a massed assault using ADGs. \ - Finally, the nature of several weapons used could invoke technological fear, or otherwise \ - be considered a war crime to utilize, primarily the electrical field and microsingularity \ - projector.\ -

                    \ - All of these flaws appear to doom the ADG to becoming another technological marvel that was \ - overly ambitious and unconstrained to the demands of reality. They will likely be really rare, \ - and terrifying." - value = CATALOGUER_REWARD_SUPERHARD - - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced - name = "advanced dark gygax" - desc = "An experimental exosuit that utilizes advanced materials to allow for greater protection while still being lightweight and fast. \ - It also is armed with an array of next-generation weaponry." - catalogue_data = list(/datum/category_item/catalogue/technology/adv_dark_gygax) - icon_state = "darkgygax_adv" - wreckage = /obj/structure/loot_pile/mecha/gygax/dark/adv - icon_scale_x = 1.5 - icon_scale_y = 1.5 - movement_shake_radius = 14 - - maxHealth = 450 - deflect_chance = 25 - has_repair_droid = TRUE - armor = list( - "melee" = 50, - "bullet" = 50, - "laser" = 50, - "energy" = 30, - "bomb" = 30, - "bio" = 100, - "rad" = 100 - ) - - special_attack_min_range = 1 - special_attack_max_range = 7 - special_attack_cooldown = 10 SECONDS - projectiletype = /obj/item/projectile/energy/homing_bolt // We're now a bullet hell game. - projectilesound = 'sound/weapons/wave.ogg' - ai_holder_type = /datum/ai_holder/simple_mob/intentional/adv_dark_gygax - var/obj/effect/overlay/energy_ball/energy_ball = null - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/Destroy() - if(energy_ball) - energy_ball.stop_orbit() - qdel(energy_ball) - return ..() - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/do_special_attack(atom/A) - . = TRUE // So we don't fire a bolt as well. - switch(a_intent) - if(I_DISARM) // Side gun - electric_defense(A) - if(I_HURT) // Rockets - launch_rockets(A) - if(I_GRAB) // Micro-singulo - launch_microsingularity(A) - -/obj/item/projectile/energy/homing_bolt - name = "homing bolt" - icon_state = "force_missile" - damage = 20 - damage_type = BURN - check_armour = "laser" - -/obj/item/projectile/energy/homing_bolt/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) - ..() - if(target) - set_homing_target(target) - -/obj/item/projectile/energy/homing_bolt/fire(angle, atom/direct_target) - ..() - set_pixel_speed(0.5) - -#define ELECTRIC_ZAP_POWER 15000 //VOREStation Edit - -// Charges a tesla shot, while emitting a dangerous electric field. The exosuit is immune to electric damage while this is ongoing. -// It also briefly blinds anyone looking directly at the mech without flash protection. -/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/electric_defense(atom/target) - set waitfor = FALSE - - // Temporary immunity to shock to avoid killing themselves with their own attack. - var/old_shock_resist = shock_resist - shock_resist = 1 - - // Make the energy ball. This is purely visual since the tesla ball is hyper-deadly. - energy_ball = new(loc) - energy_ball.adjust_scale(0.5) - energy_ball.orbit(src, 32, TRUE, 1 SECOND) - - visible_message(span("warning", "\The [src] creates \an [energy_ball] around itself!")) - - playsound(src, 'sound/effects/lightning_chargeup.ogg', 100, 1, extrarange = 30) - - // Shock nearby things that aren't ourselves. - for(var/i = 1 to 10) - energy_ball.adjust_scale(0.5 + (i/10)) - energy_ball.set_light(i/2, i/2, "#0000FF") - for(var/thing in range(3, src)) - // This is stupid because mechs are stupid and not mobs. - if(isliving(thing)) - var/mob/living/L = thing - - if(L == src) - continue - if(L.stat) - continue // Otherwise it can get pretty laggy if there's loads of corpses around. - L.inflict_shock_damage(i * 2) - if(L && L.has_AI()) // Some mobs delete themselves when dying. - L.ai_holder.react_to_attack(src) - - else if(istype(thing, /obj/mecha)) - var/obj/mecha/M = thing - M.take_damage(i * 2, "energy") // Mechs don't have a concept for siemens so energy armor check is the best alternative. - - sleep(1 SECOND) - - // Shoot a tesla bolt, and flashes people who are looking at the mecha without sufficent eye protection. - visible_message(span("warning", "\The [energy_ball] explodes in a flash of light, sending a shock everywhere!")) - playsound(src, 'sound/effects/lightningbolt.ogg', 100, 1, extrarange = 30) - tesla_zap(src.loc, 5, ELECTRIC_ZAP_POWER, FALSE) - for(var/mob/living/L in viewers(src)) - if(L == src) - continue - var/dir_towards_us = get_dir(L, src) - if(L.dir && L.dir & dir_towards_us) - to_chat(L, span("danger", "The flash of light blinds you briefly.")) - L.flash_eyes(intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = TRUE) - - // Get rid of our energy ball. - energy_ball.stop_orbit() - qdel(energy_ball) - - sleep(1 SECOND) - // Resist resistance to old value. - shock_resist = old_shock_resist // Not using initial() in case the value gets modified by an admin or something. - -#undef ELECTRIC_ZAP_POWER - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/launch_rockets(atom/target) - set waitfor = FALSE - - // Telegraph our next move. - Beam(target, icon_state = "sat_beam", time = 3.5 SECONDS, maxdistance = INFINITY) - visible_message(span("warning", "\The [src] deploys a missile rack!")) - playsound(src, 'sound/effects/turret/move1.wav', 50, 1) - sleep(0.5 SECONDS) - - for(var/i = 1 to 3) - if(target) // Might get deleted in the meantime. - var/turf/T = get_turf(target) - if(T) - visible_message(span("warning", "\The [src] fires a rocket into the air!")) - playsound(src, 'sound/weapons/rpg.ogg', 70, 1) - face_atom(T) - var/obj/item/projectile/arc/explosive_rocket/rocket = new(loc) - rocket.old_style_target(T, src) - rocket.fire() - sleep(1 SECOND) - - visible_message(span("warning", "\The [src] retracts the missile rack.")) - playsound(src, 'sound/effects/turret/move2.wav', 50, 1) - -// Arcing rocket projectile that produces a weak explosion when it lands. -// Shouldn't punch holes in the floor, but will still hurt. -/obj/item/projectile/arc/explosive_rocket - name = "rocket" - icon_state = "mortar" - -/obj/item/projectile/arc/explosive_rocket/on_impact(turf/T) - new /obj/effect/explosion(T) // Weak explosions don't produce this on their own, apparently. - explosion(T, 0, 0, 2, adminlog = FALSE) - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/launch_microsingularity(atom/target) - var/turf/T = get_turf(target) - visible_message(span("warning", "\The [src] fires an energetic sphere into the air!")) - playsound(src, 'sound/weapons/Laser.ogg', 50, 1) - face_atom(T) - var/obj/item/projectile/arc/microsingulo/sphere = new(loc) - sphere.old_style_target(T, src) - sphere.fire() - -/obj/item/projectile/arc/microsingulo - name = "micro singularity" - icon_state = "bluespace" - -/obj/item/projectile/arc/microsingulo/on_impact(turf/T) - new /obj/effect/temporary_effect/pulse/microsingulo(T) - - -/obj/effect/temporary_effect/pulse/microsingulo - name = "micro singularity" - desc = "It's sucking everything in!" - icon = 'icons/obj/objects.dmi' - icon_state = "bhole3" - light_range = 4 - light_power = 5 - light_color = "#2ECCFA" - pulses_remaining = 10 - pulse_delay = 0.5 SECONDS - var/pull_radius = 3 - var/pull_strength = STAGE_THREE - -/obj/effect/temporary_effect/pulse/microsingulo/on_pulse() - for(var/atom/A in range(pull_radius, src)) - A.singularity_pull(src, pull_strength) - - -// The Advanced Dark Gygax's AI. -// The mob has three special attacks, based on the current intent. -// This AI choose the appropiate intent for the situation, and tries to ensure it doesn't kill itself by firing missiles at its feet. -/datum/ai_holder/simple_mob/intentional/adv_dark_gygax - conserve_ammo = TRUE // Might help avoid 'I shoot the wall forever' cheese. - var/closest_desired_distance = 1 // Otherwise run up to them to be able to potentially shock or punch them. - - var/electric_defense_radius = 3 // How big to assume electric defense's area is. - var/microsingulo_radius = 3 // Same but for microsingulo pull. - var/rocket_explosive_radius = 2 // Explosion radius for the rockets. - - var/electric_defense_threshold = 2 // How many non-targeted people are needed in close proximity before electric defense is viable. - var/microsingulo_threshold = 2 // Similar to above, but uses an area around the target. - -// Used to control the mob's positioning based on which special attack it has done. -// Note that the intent will not change again until the next special attack is about to happen. -/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/on_engagement(atom/A) - // Make the AI backpeddle if using an AoE special attack. - var/list/risky_intents = list(I_GRAB, I_HURT) // Mini-singulo and missiles. - if(holder.a_intent in risky_intents) - var/closest_distance = 1 - switch(holder.a_intent) // Plus one just in case. - if(I_HURT) - closest_distance = rocket_explosive_radius + 1 - if(I_GRAB) - closest_distance = microsingulo_radius + 1 - - if(get_dist(holder, A) <= closest_distance) - holder.IMove(get_step_away(holder, A, closest_distance)) - - // Otherwise get up close and personal. - else if(get_dist(holder, A) > closest_desired_distance) - holder.IMove(get_step_towards(holder, A)) - -// Changes the mob's intent, which controls which special attack is used. -// I_DISARM causes Electric Defense, I_GRAB causes Micro-Singularity, and I_HURT causes Missile Barrage. -/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/pre_special_attack(atom/A) - if(isliving(A)) - var/mob/living/target = A - - // If we're surrounded, Electric Defense will quickly fix that. - var/tally = 0 - var/list/potential_targets = list_targets() // Returns list of mobs and certain objects like mechs and turrets. - for(var/atom/movable/AM in potential_targets) - if(get_dist(holder, AM) > electric_defense_radius) - continue - if(!can_attack(AM)) - continue - tally++ - - // Should we shock them? - if(tally >= electric_defense_threshold || get_dist(target, holder) <= electric_defense_radius) - holder.a_intent = I_DISARM - return - - // Otherwise they're a fair distance away and we're not getting mobbed up close. - // See if we should use missiles or microsingulo. - tally = 0 // Let's recycle the var. - for(var/atom/movable/AM in potential_targets) - if(get_dist(target, AM) > microsingulo_radius) // Deliberately tests distance between target and nearby targets and not the holder. - continue - if(!can_attack(AM)) - continue - if(AM.anchored) // Microsingulo doesn't do anything to anchored things. - tally-- - else - tally++ - - // Lots of people means minisingulo would be more useful. - if(tally >= microsingulo_threshold) - holder.a_intent = I_GRAB - else // Otherwise use rockets. - holder.a_intent = I_HURT - - else - if(get_dist(holder, A) >= rocket_explosive_radius + 1) - holder.a_intent = I_HURT // Fire rockets if it's an obj/turf. - else - holder.a_intent = I_DISARM // Electricity might not work but it's safe up close. +// Stronger than a regular Dark Gygax, this one has three special attacks, based on intents. +// First special attack launches three arcing rockets at the current target. +// Second special attack fires a projectile that creates a short-lived microsingularity that pulls in everything nearby. Magboots can protect from this. +// Third special attack creates a dangerous electric field that causes escalating electric damage, before emitting a tesla shock and blinding anyone looking at the mecha. +// The AI will choose one every ten seconds. + +/datum/category_item/catalogue/technology/adv_dark_gygax + name = "Exosuit - Advanced Dark Gygax" + desc = "This exosuit is an experimental prototype, descended from the Dark Gygax. It retains the \ + speed that is characteristic of the other models, yet outclasses all of them in durability, \ + to the point of having a comparable amount of protection to models that placed a higher emphesis \ + on armor, like the Durand and even the Marauder. It is also much larger in scale, and significantly \ + heavier than most other exosuits developed by humans, which often causes shockwaves to be felt \ + whenever it moves. This has been observed to have a demoralizing effect on hostile forces.\ +

                    \ + Weapons & Power System
                    \ + Attached to the exosuit's chassis are several newly invented mounted weapons, each unique in purpose and capability. \ + These weapons are integral to the chassis as opposed to the modular equipment that more traditional exosuits utilize. \ + It is unknown if that is due to simply being an early prototype, or if discarding the modular design is benefitial \ + to the design of the model.\ +

                    \ + All the weapons utilize energy, as opposed to consumable projectiles. This appears to have been a conscious decision to \ + allow for more staying power, by only being limited by availablity of electricity. \ + In order to supply the needed energy for combat, the ADG contains a miniturized fusion reactor, which is also \ + considered experimental due to its size. The reactor is powerful enough to power the actuators, electronics, \ + and the primary weapon. The supplementary weapons, however, cannot be continiously fired and instead draw from \ + a electrical buffer that is constantly replenished by the reactor.\ +

                    \ + Homing Energy Bolts
                    \ + The primary weapon is a projector that fires somewhat slow moving blue bolts of energy. The ADG is able to \ + passively redirect the trajectory of the blue bolts towards the initial target, essentially acting as a \ + homing projectile. The blue bolt itself is otherwise not very powerful compared to conventional photonic \ + weaponry or ballistic shells, however the power required to fire the main gun is significantly less \ + than the other available weapons, and so the ADG uses it as the main weapon.\ +

                    \ + Self-Supplying Missile Launcher
                    \ + The first supplementary weapon would appear to not be an energy weapon, as it is a missile launcher. \ + What is not obvious is that the missiles are fabricated inside the exosuit, with the physical \ + materials also being created from energy, similar to the newer models of Rapid Construction Devices. \ + Therefore, the ADG does not need to concern itself with running out of missiles. The missiles themselves \ + are optimized towards harming hard targets, such as other exosuits, but are also still dangerous to soft \ + targets like infantry.\ +

                    \ + Electric Defense
                    \ + The second supplementary weapon is not a conventional gun. Instead, the ADG weaponizes its electrical \ + systems by redirecting power output from its fusion reactor to its exterior shell, becoming a walking \ + tesla coil. This generates a strong electric field that harms anything unprotected nearby. \ + The electric field grows in power, until reaching a critical point, after which a blinding flash \ + of light and arcs of lightning fly out from the exosuit towards its surroundings.\ +

                    \ + Microsingularity Projector
                    \ + Finally, the third supplementary weapon utilizes gravitation as a weapon, by firing a blue energetic orb \ + that, upon hitting the ground, collapses and causes a 'microsingularity' to emerge briefly, pulling in \ + anything unsecured, such as personnel or weapons. The microsingularity lacks the means to gain any energy, meaning it \ + will dissipate in a few seconds, and so it is probably safe to use on a planetary body.\ +

                    \ + Flaws
                    \ + It would appear the ADG is poised to take the place of other exosuits like the Marauder, however several \ + massive flaws exist to make that unlikely. Firstly, this exosuit is almost an order of magnitude more \ + costly to produce than comparable alternatives, even accounting for being a prototype. \ + Secondly, a number of weapons integrated into the ADG are dangerous both to enemies and \ + allies, limiting the ability for a massed assault using ADGs. \ + Finally, the nature of several weapons used could invoke technological fear, or otherwise \ + be considered a war crime to utilize, primarily the electrical field and microsingularity \ + projector.\ +

                    \ + All of these flaws appear to doom the ADG to becoming another technological marvel that was \ + overly ambitious and unconstrained to the demands of reality. They will likely be really rare, \ + and terrifying." + value = CATALOGUER_REWARD_SUPERHARD + + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced + name = "advanced dark gygax" + desc = "An experimental exosuit that utilizes advanced materials to allow for greater protection while still being lightweight and fast. \ + It also is armed with an array of next-generation weaponry." + catalogue_data = list(/datum/category_item/catalogue/technology/adv_dark_gygax) + icon_state = "darkgygax_adv" + wreckage = /obj/structure/loot_pile/mecha/gygax/dark/adv + icon_scale_x = 1.5 + icon_scale_y = 1.5 + movement_shake_radius = 14 + + maxHealth = 450 + deflect_chance = 25 + has_repair_droid = TRUE + armor = list( + "melee" = 50, + "bullet" = 50, + "laser" = 50, + "energy" = 30, + "bomb" = 30, + "bio" = 100, + "rad" = 100 + ) + + special_attack_min_range = 1 + special_attack_max_range = 7 + special_attack_cooldown = 10 SECONDS + projectiletype = /obj/item/projectile/energy/homing_bolt // We're now a bullet hell game. + projectilesound = 'sound/weapons/wave.ogg' + ai_holder_type = /datum/ai_holder/simple_mob/intentional/adv_dark_gygax + var/obj/effect/overlay/energy_ball/energy_ball = null + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/Destroy() + if(energy_ball) + energy_ball.stop_orbit() + qdel(energy_ball) + return ..() + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/do_special_attack(atom/A) + . = TRUE // So we don't fire a bolt as well. + switch(a_intent) + if(I_DISARM) // Side gun + electric_defense(A) + if(I_HURT) // Rockets + launch_rockets(A) + if(I_GRAB) // Micro-singulo + launch_microsingularity(A) + +/obj/item/projectile/energy/homing_bolt + name = "homing bolt" + icon_state = "force_missile" + damage = 20 + damage_type = BURN + check_armour = "laser" + +/obj/item/projectile/energy/homing_bolt/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) + ..() + if(target) + set_homing_target(target) + +/obj/item/projectile/energy/homing_bolt/fire(angle, atom/direct_target) + ..() + set_pixel_speed(0.5) + +#define ELECTRIC_ZAP_POWER 15000 //VOREStation Edit + +// Charges a tesla shot, while emitting a dangerous electric field. The exosuit is immune to electric damage while this is ongoing. +// It also briefly blinds anyone looking directly at the mech without flash protection. +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/electric_defense(atom/target) + set waitfor = FALSE + + // Temporary immunity to shock to avoid killing themselves with their own attack. + var/old_shock_resist = shock_resist + shock_resist = 1 + + // Make the energy ball. This is purely visual since the tesla ball is hyper-deadly. + energy_ball = new(loc) + energy_ball.adjust_scale(0.5) + energy_ball.orbit(src, 32, TRUE, 1 SECOND) + + visible_message(span("warning", "\The [src] creates \an [energy_ball] around itself!")) + + playsound(src, 'sound/effects/lightning_chargeup.ogg', 100, 1, extrarange = 30) + + // Shock nearby things that aren't ourselves. + for(var/i = 1 to 10) + energy_ball.adjust_scale(0.5 + (i/10)) + energy_ball.set_light(i/2, i/2, "#0000FF") + for(var/thing in range(3, src)) + // This is stupid because mechs are stupid and not mobs. + if(isliving(thing)) + var/mob/living/L = thing + + if(L == src) + continue + if(L.stat) + continue // Otherwise it can get pretty laggy if there's loads of corpses around. + L.inflict_shock_damage(i * 2) + if(L && L.has_AI()) // Some mobs delete themselves when dying. + L.ai_holder.react_to_attack(src) + + else if(istype(thing, /obj/mecha)) + var/obj/mecha/M = thing + M.take_damage(i * 2, "energy") // Mechs don't have a concept for siemens so energy armor check is the best alternative. + + sleep(1 SECOND) + + // Shoot a tesla bolt, and flashes people who are looking at the mecha without sufficent eye protection. + visible_message(span("warning", "\The [energy_ball] explodes in a flash of light, sending a shock everywhere!")) + playsound(src, 'sound/effects/lightningbolt.ogg', 100, 1, extrarange = 30) + tesla_zap(src.loc, 5, ELECTRIC_ZAP_POWER, FALSE) + for(var/mob/living/L in viewers(src)) + if(L == src) + continue + var/dir_towards_us = get_dir(L, src) + if(L.dir && L.dir & dir_towards_us) + to_chat(L, span("danger", "The flash of light blinds you briefly.")) + L.flash_eyes(intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = TRUE) + + // Get rid of our energy ball. + energy_ball.stop_orbit() + qdel(energy_ball) + + sleep(1 SECOND) + // Resist resistance to old value. + shock_resist = old_shock_resist // Not using initial() in case the value gets modified by an admin or something. + +#undef ELECTRIC_ZAP_POWER + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/launch_rockets(atom/target) + set waitfor = FALSE + + // Telegraph our next move. + Beam(target, icon_state = "sat_beam", time = 3.5 SECONDS, maxdistance = INFINITY) + visible_message(span("warning", "\The [src] deploys a missile rack!")) + playsound(src, 'sound/effects/turret/move1.wav', 50, 1) + sleep(0.5 SECONDS) + + for(var/i = 1 to 3) + if(target) // Might get deleted in the meantime. + var/turf/T = get_turf(target) + if(T) + visible_message(span("warning", "\The [src] fires a rocket into the air!")) + playsound(src, 'sound/weapons/rpg.ogg', 70, 1) + face_atom(T) + var/obj/item/projectile/arc/explosive_rocket/rocket = new(loc) + rocket.old_style_target(T, src) + rocket.fire() + sleep(1 SECOND) + + visible_message(span("warning", "\The [src] retracts the missile rack.")) + playsound(src, 'sound/effects/turret/move2.wav', 50, 1) + +// Arcing rocket projectile that produces a weak explosion when it lands. +// Shouldn't punch holes in the floor, but will still hurt. +/obj/item/projectile/arc/explosive_rocket + name = "rocket" + icon_state = "mortar" + +/obj/item/projectile/arc/explosive_rocket/on_impact(turf/T) + new /obj/effect/explosion(T) // Weak explosions don't produce this on their own, apparently. + explosion(T, 0, 0, 2, adminlog = FALSE) + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/launch_microsingularity(atom/target) + var/turf/T = get_turf(target) + visible_message(span("warning", "\The [src] fires an energetic sphere into the air!")) + playsound(src, 'sound/weapons/Laser.ogg', 50, 1) + face_atom(T) + var/obj/item/projectile/arc/microsingulo/sphere = new(loc) + sphere.old_style_target(T, src) + sphere.fire() + +/obj/item/projectile/arc/microsingulo + name = "micro singularity" + icon_state = "bluespace" + +/obj/item/projectile/arc/microsingulo/on_impact(turf/T) + new /obj/effect/temporary_effect/pulse/microsingulo(T) + + +/obj/effect/temporary_effect/pulse/microsingulo + name = "micro singularity" + desc = "It's sucking everything in!" + icon = 'icons/obj/objects.dmi' + icon_state = "bhole3" + light_range = 4 + light_power = 5 + light_color = "#2ECCFA" + pulses_remaining = 10 + pulse_delay = 0.5 SECONDS + var/pull_radius = 3 + var/pull_strength = STAGE_THREE + +/obj/effect/temporary_effect/pulse/microsingulo/on_pulse() + for(var/atom/A in range(pull_radius, src)) + A.singularity_pull(src, pull_strength) + + +// The Advanced Dark Gygax's AI. +// The mob has three special attacks, based on the current intent. +// This AI choose the appropiate intent for the situation, and tries to ensure it doesn't kill itself by firing missiles at its feet. +/datum/ai_holder/simple_mob/intentional/adv_dark_gygax + conserve_ammo = TRUE // Might help avoid 'I shoot the wall forever' cheese. + var/closest_desired_distance = 1 // Otherwise run up to them to be able to potentially shock or punch them. + + var/electric_defense_radius = 3 // How big to assume electric defense's area is. + var/microsingulo_radius = 3 // Same but for microsingulo pull. + var/rocket_explosive_radius = 2 // Explosion radius for the rockets. + + var/electric_defense_threshold = 2 // How many non-targeted people are needed in close proximity before electric defense is viable. + var/microsingulo_threshold = 2 // Similar to above, but uses an area around the target. + +// Used to control the mob's positioning based on which special attack it has done. +// Note that the intent will not change again until the next special attack is about to happen. +/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/on_engagement(atom/A) + // Make the AI backpeddle if using an AoE special attack. + var/list/risky_intents = list(I_GRAB, I_HURT) // Mini-singulo and missiles. + if(holder.a_intent in risky_intents) + var/closest_distance = 1 + switch(holder.a_intent) // Plus one just in case. + if(I_HURT) + closest_distance = rocket_explosive_radius + 1 + if(I_GRAB) + closest_distance = microsingulo_radius + 1 + + if(get_dist(holder, A) <= closest_distance) + holder.IMove(get_step_away(holder, A, closest_distance)) + + // Otherwise get up close and personal. + else if(get_dist(holder, A) > closest_desired_distance) + holder.IMove(get_step_towards(holder, A)) + +// Changes the mob's intent, which controls which special attack is used. +// I_DISARM causes Electric Defense, I_GRAB causes Micro-Singularity, and I_HURT causes Missile Barrage. +/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/pre_special_attack(atom/A) + if(isliving(A)) + var/mob/living/target = A + + // If we're surrounded, Electric Defense will quickly fix that. + var/tally = 0 + var/list/potential_targets = list_targets() // Returns list of mobs and certain objects like mechs and turrets. + for(var/atom/movable/AM in potential_targets) + if(get_dist(holder, AM) > electric_defense_radius) + continue + if(!can_attack(AM)) + continue + tally++ + + // Should we shock them? + if(tally >= electric_defense_threshold || get_dist(target, holder) <= electric_defense_radius) + holder.a_intent = I_DISARM + return + + // Otherwise they're a fair distance away and we're not getting mobbed up close. + // See if we should use missiles or microsingulo. + tally = 0 // Let's recycle the var. + for(var/atom/movable/AM in potential_targets) + if(get_dist(target, AM) > microsingulo_radius) // Deliberately tests distance between target and nearby targets and not the holder. + continue + if(!can_attack(AM)) + continue + if(AM.anchored) // Microsingulo doesn't do anything to anchored things. + tally-- + else + tally++ + + // Lots of people means minisingulo would be more useful. + if(tally >= microsingulo_threshold) + holder.a_intent = I_GRAB + else // Otherwise use rockets. + holder.a_intent = I_HURT + + else + if(get_dist(holder, A) >= rocket_explosive_radius + 1) + holder.a_intent = I_HURT // Fire rockets if it's an obj/turf. + else + holder.a_intent = I_DISARM // Electricity might not work but it's safe up close. diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/combat_mecha.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/combat_mecha.dm index da1dce8d34c..ecefa62de54 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/combat_mecha.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/combat_mecha.dm @@ -1,38 +1,38 @@ -// Base type for the 'combat' mechas like gygax/durand/maulers/etc. -// They generally are walking tanks, and their melee attack knocks back and stuns, like the real deal. - -/mob/living/simple_mob/mechanical/mecha/combat - name = "combat mecha" - desc = "An even bigger stompy mech!!" - - movement_cooldown = 3 - melee_damage_lower = 30 - melee_damage_upper = 30 - melee_attack_delay = 1 SECOND - attacktext = list("punched", "slammed", "uppercutted", "pummeled") - - armor = list( - "melee" = 30, - "bullet" = 30, - "laser" = 15, - "energy" = 0, - "bomb" = 20, - "bio" = 100, - "rad" = 100 - ) - - var/weaken_amount = 2 // Be careful with this number. High values can equal a permastun. - -// Melee hits knock back by one tile (or more if already stunned to help prevent permastuns). -/mob/living/simple_mob/mechanical/mecha/combat/apply_melee_effects(atom/A) - if(isliving(A)) - var/mob/living/L = A - if(L.mob_size <= MOB_MEDIUM) - visible_message(span("danger", "\The [src] sends \the [L] flying with their mechanized fist!")) - playsound(src, "punch", 50, 1) - L.Weaken(weaken_amount) - var/throw_dir = get_dir(src, L) - var/throw_dist = L.incapacitated(INCAPACITATION_DISABLED) ? 4 : 1 - L.throw_at(get_edge_target_turf(L, throw_dir), throw_dist, 1, src) - else - to_chat(L, span("warning", "\The [src] punches you with incredible force, but you remain in place.")) +// Base type for the 'combat' mechas like gygax/durand/maulers/etc. +// They generally are walking tanks, and their melee attack knocks back and stuns, like the real deal. + +/mob/living/simple_mob/mechanical/mecha/combat + name = "combat mecha" + desc = "An even bigger stompy mech!!" + + movement_cooldown = 3 + melee_damage_lower = 30 + melee_damage_upper = 30 + melee_attack_delay = 1 SECOND + attacktext = list("punched", "slammed", "uppercutted", "pummeled") + + armor = list( + "melee" = 30, + "bullet" = 30, + "laser" = 15, + "energy" = 0, + "bomb" = 20, + "bio" = 100, + "rad" = 100 + ) + + var/weaken_amount = 2 // Be careful with this number. High values can equal a permastun. + +// Melee hits knock back by one tile (or more if already stunned to help prevent permastuns). +/mob/living/simple_mob/mechanical/mecha/combat/apply_melee_effects(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(L.mob_size <= MOB_MEDIUM) + visible_message(span("danger", "\The [src] sends \the [L] flying with their mechanized fist!")) + playsound(src, "punch", 50, 1) + L.Weaken(weaken_amount) + var/throw_dir = get_dir(src, L) + var/throw_dist = L.incapacitated(INCAPACITATION_DISABLED) ? 4 : 1 + L.throw_at(get_edge_target_turf(L, throw_dir), throw_dist, 1, src) + else + to_chat(L, span("warning", "\The [src] punches you with incredible force, but you remain in place.")) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm index f1cefbef803..97d659f7e87 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm @@ -1,78 +1,78 @@ -// Durands are slow, tanky, beefy, and hit really hard. -// They can also root themselves to become even tankier. -// The AI doesn't do this currently. - -/datum/category_item/catalogue/technology/durand - name = "Exosuit - Durand" - desc = "The Durand is an old combat exosuit, that was once the most durable exosuit ever developed by humans. \ - In modern times, this exosuit has been dethroned from that title, yet it remains one of the more well built and armored \ - exosuits, despite its age.\ -

                    \ - During the Hegemony War against the Unathi, there was a need for various new technologies to be developed \ - to counter the Unathi war machine. One of many solutions created was the Durand, which was made to be heavy and \ - well-armored, and be capable of powering the various weapons that could be mounted onto it. Presently, the \ - Durand now generally serves as corporate asset protection hardware, due to modern militaries moving on to newer, \ - more advanced war machines." - value = CATALOGUER_REWARD_HARD - -/mob/living/simple_mob/mechanical/mecha/combat/durand - name = "durand" - desc = "An aging combat exosuit utilized by many corporations. It was originally developed to fight in the Hegemony War." - catalogue_data = list(/datum/category_item/catalogue/technology/durand) - icon_state = "durand" - movement_cooldown = 3 - wreckage = /obj/structure/loot_pile/mecha/durand - - maxHealth = 400 - deflect_chance = 20 - armor = list( - "melee" = 50, - "bullet" = 35, - "laser" = 15, - "energy" = 10, - "bomb" = 20, - "bio" = 100, - "rad" = 100 - ) - melee_damage_lower = 40 - melee_damage_upper = 40 - base_attack_cooldown = 2 SECONDS - projectiletype = /obj/item/projectile/beam/heavylaser - projectile_dispersion = 10 - projectile_accuracy = -30 - - var/defense_mode = FALSE - var/defense_deflect = 35 - -/mob/living/simple_mob/mechanical/mecha/combat/durand/proc/set_defense_mode(new_mode) - defense_mode = new_mode - deflect_chance = defense_mode ? defense_deflect : initial(deflect_chance) - projectile_accuracy = defense_mode ? -10 : initial(projectile_accuracy) - to_chat(src, span("notice", "You [defense_mode ? "en" : "dis"]able defense mode.")) - -/mob/living/simple_mob/mechanical/mecha/combat/durand/SelfMove(turf/n, direct) - if(defense_mode) - to_chat(src, span("warning", "You are in defense mode, you cannot move.")) - return FALSE - return ..() - -// So players can toggle it too. -/mob/living/simple_mob/mechanical/mecha/combat/durand/verb/toggle_defense_mode() - set name = "Toggle Defense Mode" - set desc = "Toggles a special mode which makes you immobile and much more resilient." - set category = "Abilities" - - set_defense_mode(!defense_mode) - -// Variant that starts in defense mode, perhaps for PoIs. -/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive/Initialize() - set_defense_mode(TRUE) - return ..() - -/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive/mercenary - desc = "An aging combat exosuit utilized by many corporations. It was originally developed to fight in the Hegemony War.\ - This one has been retrofitted into a turret." - - projectiletype = /obj/item/projectile/beam/heavylaser/fakeemitter - - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged +// Durands are slow, tanky, beefy, and hit really hard. +// They can also root themselves to become even tankier. +// The AI doesn't do this currently. + +/datum/category_item/catalogue/technology/durand + name = "Exosuit - Durand" + desc = "The Durand is an old combat exosuit, that was once the most durable exosuit ever developed by humans. \ + In modern times, this exosuit has been dethroned from that title, yet it remains one of the more well built and armored \ + exosuits, despite its age.\ +

                    \ + During the Hegemony War against the Unathi, there was a need for various new technologies to be developed \ + to counter the Unathi war machine. One of many solutions created was the Durand, which was made to be heavy and \ + well-armored, and be capable of powering the various weapons that could be mounted onto it. Presently, the \ + Durand now generally serves as corporate asset protection hardware, due to modern militaries moving on to newer, \ + more advanced war machines." + value = CATALOGUER_REWARD_HARD + +/mob/living/simple_mob/mechanical/mecha/combat/durand + name = "durand" + desc = "An aging combat exosuit utilized by many corporations. It was originally developed to fight in the Hegemony War." + catalogue_data = list(/datum/category_item/catalogue/technology/durand) + icon_state = "durand" + movement_cooldown = 3 + wreckage = /obj/structure/loot_pile/mecha/durand + + maxHealth = 400 + deflect_chance = 20 + armor = list( + "melee" = 50, + "bullet" = 35, + "laser" = 15, + "energy" = 10, + "bomb" = 20, + "bio" = 100, + "rad" = 100 + ) + melee_damage_lower = 40 + melee_damage_upper = 40 + base_attack_cooldown = 2 SECONDS + projectiletype = /obj/item/projectile/beam/heavylaser + projectile_dispersion = 10 + projectile_accuracy = -30 + + var/defense_mode = FALSE + var/defense_deflect = 35 + +/mob/living/simple_mob/mechanical/mecha/combat/durand/proc/set_defense_mode(new_mode) + defense_mode = new_mode + deflect_chance = defense_mode ? defense_deflect : initial(deflect_chance) + projectile_accuracy = defense_mode ? -10 : initial(projectile_accuracy) + to_chat(src, span("notice", "You [defense_mode ? "en" : "dis"]able defense mode.")) + +/mob/living/simple_mob/mechanical/mecha/combat/durand/SelfMove(turf/n, direct) + if(defense_mode) + to_chat(src, span("warning", "You are in defense mode, you cannot move.")) + return FALSE + return ..() + +// So players can toggle it too. +/mob/living/simple_mob/mechanical/mecha/combat/durand/verb/toggle_defense_mode() + set name = "Toggle Defense Mode" + set desc = "Toggles a special mode which makes you immobile and much more resilient." + set category = "Abilities" + + set_defense_mode(!defense_mode) + +// Variant that starts in defense mode, perhaps for PoIs. +/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive/Initialize() + set_defense_mode(TRUE) + return ..() + +/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive/mercenary + desc = "An aging combat exosuit utilized by many corporations. It was originally developed to fight in the Hegemony War.\ + This one has been retrofitted into a turret." + + projectiletype = /obj/item/projectile/beam/heavylaser/fakeemitter + + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/gygax.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/gygax.dm index e06960b1958..bafbb58cd7d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/gygax.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/gygax.dm @@ -1,84 +1,84 @@ -// Gygaxes are tough but also fast. -// Their AI, unlike most, will advance towards their target instead of remaining in place. - -/datum/category_item/catalogue/technology/gygax - name = "Exosuit - Gygax" - desc = "The Gygax is a relatively modern exosuit, built to be lightweight and agile, while still being fairly durable. \ - These traits have made them rather popular among well funded private and corporate security forces, who desire \ - the ability to rapidly respond to conflict.\ -

                    \ - One special feature of this model is that the actuators that \ - drive the exosuit can have their safeties disabled in order to achieve a short-term burst of unparalleled speed, \ - at the expense of damaging the exosuit considerably." - value = CATALOGUER_REWARD_HARD - -/mob/living/simple_mob/mechanical/mecha/combat/gygax - name = "gygax" - desc = "A lightweight, security exosuit. Popular among private and corporate security." - catalogue_data = list(/datum/category_item/catalogue/technology/gygax) - icon_state = "gygax" - movement_cooldown = -1 - wreckage = /obj/structure/loot_pile/mecha/gygax - - maxHealth = 300 - armor = list( - "melee" = 25, - "bullet" = 20, - "laser" = 30, - "energy" = 15, - "bomb" = 0, - "bio" = 100, - "rad" = 100 - ) - - projectile_dispersion = 8 - projectiletype = /obj/item/projectile/beam/midlaser - - ai_holder_type = /datum/ai_holder/simple_mob/intentional/adv_dark_gygax - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/manned - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged // Carries a pistol. - - -// A stronger variant. - -/datum/category_item/catalogue/technology/dark_gygax - name = "Exosuit - Dark Gygax" - desc = "This exosuit is a variant of the regular Gygax. It is generally referred to as the Dark Gygax, \ - due to being constructed from different materials that give it a darker appearance. Beyond merely looking \ - cosmetically different, the Dark Gygax also has various upgrades compared to the Gygax. It is much more \ - resilient, yet retains the agility and speed of the Gygax.\ -

                    \ - These are relatively rare compared to the other security exosuits, as most security forces are content with \ - a regular Gygax. Instead, this exosuit is often used by high-end asset protection teams, and mercenaries." - value = CATALOGUER_REWARD_HARD - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark - name = "dark gygax" - desc = "A significantly upgraded Gygax security mech, often utilized by corporate asset protection teams and \ - PMCs." - catalogue_data = list(/datum/category_item/catalogue/technology/dark_gygax) - icon_state = "darkgygax" - wreckage = /obj/structure/loot_pile/mecha/gygax/dark - - maxHealth = 400 - deflect_chance = 25 - has_repair_droid = TRUE - armor = list( - "melee" = 40, - "bullet" = 40, - "laser" = 50, - "energy" = 35, - "bomb" = 20, - "bio" = 100, - "rad" = 100 - ) - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/medgax - name = "medgax" - desc = "An unorthodox fusion of the Gygax and Odysseus exosuits, this one is fast, sturdy, and carries a wide array of \ - potent chemicals and delivery mechanisms. The doctor is in!" - icon_state = "medgax" - wreckage = /obj/structure/loot_pile/mecha/gygax/medgax - - projectile_dispersion = 8 +// Gygaxes are tough but also fast. +// Their AI, unlike most, will advance towards their target instead of remaining in place. + +/datum/category_item/catalogue/technology/gygax + name = "Exosuit - Gygax" + desc = "The Gygax is a relatively modern exosuit, built to be lightweight and agile, while still being fairly durable. \ + These traits have made them rather popular among well funded private and corporate security forces, who desire \ + the ability to rapidly respond to conflict.\ +

                    \ + One special feature of this model is that the actuators that \ + drive the exosuit can have their safeties disabled in order to achieve a short-term burst of unparalleled speed, \ + at the expense of damaging the exosuit considerably." + value = CATALOGUER_REWARD_HARD + +/mob/living/simple_mob/mechanical/mecha/combat/gygax + name = "gygax" + desc = "A lightweight, security exosuit. Popular among private and corporate security." + catalogue_data = list(/datum/category_item/catalogue/technology/gygax) + icon_state = "gygax" + movement_cooldown = -1 + wreckage = /obj/structure/loot_pile/mecha/gygax + + maxHealth = 300 + armor = list( + "melee" = 25, + "bullet" = 20, + "laser" = 30, + "energy" = 15, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + projectile_dispersion = 8 + projectiletype = /obj/item/projectile/beam/midlaser + + ai_holder_type = /datum/ai_holder/simple_mob/intentional/adv_dark_gygax + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged // Carries a pistol. + + +// A stronger variant. + +/datum/category_item/catalogue/technology/dark_gygax + name = "Exosuit - Dark Gygax" + desc = "This exosuit is a variant of the regular Gygax. It is generally referred to as the Dark Gygax, \ + due to being constructed from different materials that give it a darker appearance. Beyond merely looking \ + cosmetically different, the Dark Gygax also has various upgrades compared to the Gygax. It is much more \ + resilient, yet retains the agility and speed of the Gygax.\ +

                    \ + These are relatively rare compared to the other security exosuits, as most security forces are content with \ + a regular Gygax. Instead, this exosuit is often used by high-end asset protection teams, and mercenaries." + value = CATALOGUER_REWARD_HARD + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark + name = "dark gygax" + desc = "A significantly upgraded Gygax security mech, often utilized by corporate asset protection teams and \ + PMCs." + catalogue_data = list(/datum/category_item/catalogue/technology/dark_gygax) + icon_state = "darkgygax" + wreckage = /obj/structure/loot_pile/mecha/gygax/dark + + maxHealth = 400 + deflect_chance = 25 + has_repair_droid = TRUE + armor = list( + "melee" = 40, + "bullet" = 40, + "laser" = 50, + "energy" = 35, + "bomb" = 20, + "bio" = 100, + "rad" = 100 + ) + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/medgax + name = "medgax" + desc = "An unorthodox fusion of the Gygax and Odysseus exosuits, this one is fast, sturdy, and carries a wide array of \ + potent chemicals and delivery mechanisms. The doctor is in!" + icon_state = "medgax" + wreckage = /obj/structure/loot_pile/mecha/gygax/medgax + + projectile_dispersion = 8 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/hoverpod.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/hoverpod.dm index c575922be52..b4aa0d1ed6e 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/hoverpod.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/hoverpod.dm @@ -1,48 +1,48 @@ -// Ranged, and capable of flight. - -/datum/category_item/catalogue/technology/hoverpod - name = "Voidcraft - Hoverpod" - desc = "This is a small space-capable craft that has a round design. Can hold up to one pilot, \ - and sometimes one or two passengers, with the right modifications made. \ - Hoverpods have existed for a very long time, and the design has remained more or less consistant over its life. \ - They carved out a niche in short ranged transportation of cargo or crew while in space, \ - as they were more efficient compared to using a shuttle, and required less infrastructure to use due to being compact enough \ - to use airlocks. As such, they acted as a sort of bridge between being EVA in a spacesuit, and being inside a 'real' spacecraft.\ -

                    \ - In recent times, the Hoverpod is seen as outdated by some, as newer solutions to that niche now exist, however it remains an ancient favorite." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/mechanical/mecha/hoverpod - name = "hover pod" - desc = "Stubby and round, this space-capable craft is an ancient favorite. It has a jury-rigged welder-laser." - catalogue_data = list(/datum/category_item/catalogue/technology/hoverpod) - icon_state = "engineering_pod" - movement_sound = 'sound/machines/hiss.ogg' - wreckage = /obj/structure/loot_pile/mecha/hoverpod - - maxHealth = 150 - hovering = TRUE // Can fly. - - projectile_dispersion = 10 - projectile_accuracy = -30 - projectiletype = /obj/item/projectile/beam - base_attack_cooldown = 2 SECONDS - - organ_names = /decl/mob_organ_names/hoverpod - - var/datum/effect/effect/system/ion_trail_follow/ion_trail - -/mob/living/simple_mob/mechanical/mecha/hoverpod/manned - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged - -/mob/living/simple_mob/mechanical/mecha/hoverpod/Initialize() - ion_trail = new /datum/effect/effect/system/ion_trail_follow() - ion_trail.set_up(src) - ion_trail.start() - return ..() - -/mob/living/simple_mob/mechanical/mecha/hoverpod/Process_Spacemove(var/check_drift = 0) - return TRUE - -/decl/mob_organ_names/hoverpod - hit_zones = list("central chassis", "control module", "hydraulics", "left manipulator", "right manipulator", "left landing strut", "right landing strut", "maneuvering thruster", "sensor suite", "radiator", "power supply") +// Ranged, and capable of flight. + +/datum/category_item/catalogue/technology/hoverpod + name = "Voidcraft - Hoverpod" + desc = "This is a small space-capable craft that has a round design. Can hold up to one pilot, \ + and sometimes one or two passengers, with the right modifications made. \ + Hoverpods have existed for a very long time, and the design has remained more or less consistant over its life. \ + They carved out a niche in short ranged transportation of cargo or crew while in space, \ + as they were more efficient compared to using a shuttle, and required less infrastructure to use due to being compact enough \ + to use airlocks. As such, they acted as a sort of bridge between being EVA in a spacesuit, and being inside a 'real' spacecraft.\ +

                    \ + In recent times, the Hoverpod is seen as outdated by some, as newer solutions to that niche now exist, however it remains an ancient favorite." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/mechanical/mecha/hoverpod + name = "hover pod" + desc = "Stubby and round, this space-capable craft is an ancient favorite. It has a jury-rigged welder-laser." + catalogue_data = list(/datum/category_item/catalogue/technology/hoverpod) + icon_state = "engineering_pod" + movement_sound = 'sound/machines/hiss.ogg' + wreckage = /obj/structure/loot_pile/mecha/hoverpod + + maxHealth = 150 + hovering = TRUE // Can fly. + + projectile_dispersion = 10 + projectile_accuracy = -30 + projectiletype = /obj/item/projectile/beam + base_attack_cooldown = 2 SECONDS + + organ_names = /decl/mob_organ_names/hoverpod + + var/datum/effect/effect/system/ion_trail_follow/ion_trail + +/mob/living/simple_mob/mechanical/mecha/hoverpod/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged + +/mob/living/simple_mob/mechanical/mecha/hoverpod/Initialize() + ion_trail = new /datum/effect/effect/system/ion_trail_follow() + ion_trail.set_up(src) + ion_trail.start() + return ..() + +/mob/living/simple_mob/mechanical/mecha/hoverpod/Process_Spacemove(var/check_drift = 0) + return TRUE + +/decl/mob_organ_names/hoverpod + hit_zones = list("central chassis", "control module", "hydraulics", "left manipulator", "right manipulator", "left landing strut", "right landing strut", "maneuvering thruster", "sensor suite", "radiator", "power supply") diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/marauder.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/marauder.dm index b6e2934c8c2..61a525f5078 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/marauder.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/marauder.dm @@ -1,67 +1,67 @@ -// Marauders are even tougher than Durands. - -/datum/category_item/catalogue/technology/marauder - name = "Exosuit - Marauder" - desc = "Marauders are the more modern descendants of the Durand model. Stronger, faster, and \ - more resilient than their predecessor, they have replaced the Durand's role entirely, and are generally seen in service \ - for various militaries across human space. As such, they are generally unavailable to civilians, including \ - corporations and most Trans-Stellars." - value = CATALOGUER_REWARD_HARD - -/mob/living/simple_mob/mechanical/mecha/combat/marauder - name = "marauder" - desc = "A heavy-duty, combat exosuit, developed after the Durand model. This is rarely found among civilian populations." - catalogue_data = list(/datum/category_item/catalogue/technology/marauder) - icon_state = "marauder" - movement_cooldown = 1.5 - wreckage = /obj/structure/loot_pile/mecha/marauder - - maxHealth = 500 - deflect_chance = 25 - sight = SEE_SELF | SEE_MOBS - armor = list( - "melee" = 50, - "bullet" = 55, - "laser" = 40, - "energy" = 30, - "bomb" = 30, - "bio" = 100, - "rad" = 100 - ) - melee_damage_lower = 45 - melee_damage_upper = 45 - base_attack_cooldown = 2 SECONDS - projectiletype = /obj/item/projectile/beam/heavylaser - - - -/datum/category_item/catalogue/technology/seraph - name = "Exosuit - Seraph" - desc = "The Seraph line of combat exosuit is essentially a Marauder with incremental improvements, making \ - it slightly better. Due to the relatively minor improvements over its predecessor, and the cost of \ - said improvements, Seraphs have not made the Marauder obsolute. Instead, they have generally filled the \ - role of housing important commanders, and as such they generally contain specialized communications \ - equipment to aid in receiving and relaying orders.\ -

                    \ - Due to this role, they are generally not expected to see combat frequently. Despite this, they often have \ - one or more weapons attached, to allow for retaliation in case it is attacked directly." - value = CATALOGUER_REWARD_HARD - -// Slightly stronger, used to allow comdoms to frontline without dying instantly, I guess. -/mob/living/simple_mob/mechanical/mecha/combat/marauder/seraph - name = "seraph" - desc = "A heavy-duty, combat/command exosuit. This one is specialized towards housing important commanders such as high-ranking \ - military personnel. It's stronger than the regular Marauder model, but not by much." - catalogue_data = list(/datum/category_item/catalogue/technology/seraph) - icon_state = "seraph" - wreckage = /obj/structure/loot_pile/mecha/marauder/seraph - health = 550 - melee_damage_lower = 55 // The real version hits this hard apparently. Ouch. - melee_damage_upper = 55 - - -/mob/living/simple_mob/mechanical/mecha/combat/marauder/mauler - name = "mauler" - desc = "A heavy duty, combat exosuit that is based off of the Marauder model." - icon_state = "mauler" - wreckage = /obj/structure/loot_pile/mecha/marauder/mauler +// Marauders are even tougher than Durands. + +/datum/category_item/catalogue/technology/marauder + name = "Exosuit - Marauder" + desc = "Marauders are the more modern descendants of the Durand model. Stronger, faster, and \ + more resilient than their predecessor, they have replaced the Durand's role entirely, and are generally seen in service \ + for various militaries across human space. As such, they are generally unavailable to civilians, including \ + corporations and most Trans-Stellars." + value = CATALOGUER_REWARD_HARD + +/mob/living/simple_mob/mechanical/mecha/combat/marauder + name = "marauder" + desc = "A heavy-duty, combat exosuit, developed after the Durand model. This is rarely found among civilian populations." + catalogue_data = list(/datum/category_item/catalogue/technology/marauder) + icon_state = "marauder" + movement_cooldown = 1.5 + wreckage = /obj/structure/loot_pile/mecha/marauder + + maxHealth = 500 + deflect_chance = 25 + sight = SEE_SELF | SEE_MOBS + armor = list( + "melee" = 50, + "bullet" = 55, + "laser" = 40, + "energy" = 30, + "bomb" = 30, + "bio" = 100, + "rad" = 100 + ) + melee_damage_lower = 45 + melee_damage_upper = 45 + base_attack_cooldown = 2 SECONDS + projectiletype = /obj/item/projectile/beam/heavylaser + + + +/datum/category_item/catalogue/technology/seraph + name = "Exosuit - Seraph" + desc = "The Seraph line of combat exosuit is essentially a Marauder with incremental improvements, making \ + it slightly better. Due to the relatively minor improvements over its predecessor, and the cost of \ + said improvements, Seraphs have not made the Marauder obsolute. Instead, they have generally filled the \ + role of housing important commanders, and as such they generally contain specialized communications \ + equipment to aid in receiving and relaying orders.\ +

                    \ + Due to this role, they are generally not expected to see combat frequently. Despite this, they often have \ + one or more weapons attached, to allow for retaliation in case it is attacked directly." + value = CATALOGUER_REWARD_HARD + +// Slightly stronger, used to allow comdoms to frontline without dying instantly, I guess. +/mob/living/simple_mob/mechanical/mecha/combat/marauder/seraph + name = "seraph" + desc = "A heavy-duty, combat/command exosuit. This one is specialized towards housing important commanders such as high-ranking \ + military personnel. It's stronger than the regular Marauder model, but not by much." + catalogue_data = list(/datum/category_item/catalogue/technology/seraph) + icon_state = "seraph" + wreckage = /obj/structure/loot_pile/mecha/marauder/seraph + health = 550 + melee_damage_lower = 55 // The real version hits this hard apparently. Ouch. + melee_damage_upper = 55 + + +/mob/living/simple_mob/mechanical/mecha/combat/marauder/mauler + name = "mauler" + desc = "A heavy duty, combat exosuit that is based off of the Marauder model." + icon_state = "mauler" + wreckage = /obj/structure/loot_pile/mecha/marauder/mauler diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm index dc4733b6f0f..d5725084331 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm @@ -1,146 +1,146 @@ -// Mecha simple_mobs are essentially fake mechs. Generally tough and scary to fight. -// By default, they're automatically piloted by some kind of drone AI. They can be set to be "piloted" instead with a var. -// Tries to be as similar to the real deal as possible. - -/mob/living/simple_mob/mechanical/mecha - name = "mecha" - desc = "A big stompy mech!" - icon = 'icons/mecha/mecha.dmi' - - faction = "syndicate" - movement_cooldown = 1.5 - movement_sound = "mechstep" // This gets fed into playsound(), which can also take strings as a 'group' of sound files. - turn_sound = 'sound/mecha/mechturn.ogg' - maxHealth = 300 - mob_size = MOB_LARGE - damage_threshold = 5 //Anything that's 5 or less damage will not do damage. - - organ_names = /decl/mob_organ_names/mecha - - armor = list( - "melee" = 20, - "bullet" = 10, - "laser" = 0, - "energy" = 0, - "bomb" = 0, - "bio" = 100, - "rad" = 100 - ) - - response_help = "taps on" - response_disarm = "knocks on" - response_harm = "uselessly hits" - harm_intent_damage = 0 - - ai_holder_type = /datum/ai_holder/simple_mob/melee - say_list_type = /datum/say_list/malf_drone - - var/datum/effect/effect/system/spark_spread/sparks - var/wreckage = /obj/effect/decal/mecha_wreckage/gygax/dark - var/pilot_type = null // Set to spawn a pilot when destroyed. Setting this also makes the mecha vulnerable to things that affect sentient minds. - var/deflect_chance = 10 // Chance to outright stop an attack, just like a normal exosuit. - var/has_repair_droid = FALSE // If true, heals 2 damage every tick and gets a repair droid overlay. - - -/mob/living/simple_mob/mechanical/mecha/Initialize() - sparks = new (src) - sparks.set_up(3, 1, src) - sparks.attach(src) - - if(!pilot_type) - name = "autonomous [initial(name)]" - desc = "[initial(desc)] It appears to be piloted by a drone intelligence." - else - say_list_type = /datum/say_list/merc - - if(has_repair_droid) - update_icon() - - return ..() - -/mob/living/simple_mob/mechanical/mecha/Destroy() - qdel_null(sparks) - return ..() - -/mob/living/simple_mob/mechanical/mecha/death() - ..(0,"explodes!") // Do everything else first. - - // Make the exploding more convincing with an actual explosion and some sparks. - sparks.start() - explosion(get_turf(src), 0, 0, 1, 3) - - // 'Eject' our pilot, if one exists. - if(pilot_type) - var/mob/living/L = new pilot_type(loc) - L.faction = src.faction - - new wreckage(loc) // Leave some wreckage. - - qdel(src) // Then delete us since we don't actually have a body. - -/mob/living/simple_mob/mechanical/mecha/handle_special() - if(has_repair_droid) - adjustBruteLoss(-2) - adjustFireLoss(-2) - adjustToxLoss(-2) - adjustOxyLoss(-2) - adjustCloneLoss(-2) - ..() - -/mob/living/simple_mob/mechanical/mecha/update_icon() - ..() // Cuts everything else, so do that first. - if(has_repair_droid) - add_overlay(image(icon = 'icons/mecha/mecha_equipment.dmi', icon_state = "repair_droid")) - -/mob/living/simple_mob/mechanical/mecha/bullet_act() - . = ..() - sparks.start() - -/mob/living/simple_mob/mechanical/mecha/speech_bubble_appearance() - return pilot_type ? "" : ..() - -// Piloted mechs are controlled by (presumably) something humanoid so they are vulnerable to certain things. -/mob/living/simple_mob/mechanical/mecha/is_sentient() - return pilot_type ? TRUE : FALSE - -/* -// Real mechs can't turn and run at the same time. This tries to simulate that. -// Commented out because the AI can't handle it sadly. -/mob/living/simple_mob/mechanical/mecha/SelfMove(turf/n, direct) - if(direct != dir) - set_dir(direct) - return FALSE // We didn't actually move, and returning FALSE means the mob can try to actually move almost immediately and not have to wait the full movement cooldown. - return ..() -*/ - -/mob/living/simple_mob/mechanical/mecha/bullet_act(obj/item/projectile/P) - if(prob(deflect_chance)) - visible_message(span("warning", "\The [P] is deflected by \the [src]'s armor!")) - deflect_sprite() - return 0 - return ..() - -/mob/living/simple_mob/mechanical/mecha/proc/deflect_sprite() - var/image/deflect_image = image('icons/effects/effects.dmi', "deflect_static") - add_overlay(deflect_image) - sleep(1 SECOND) - cut_overlay(deflect_image) - qdel(deflect_image) -// flick_overlay_view(deflect_image, src, duration = 1 SECOND, gc_after = TRUE) - -/mob/living/simple_mob/mechanical/mecha/attackby(obj/item/I, mob/user) - if(prob(deflect_chance)) - visible_message(span("warning", "\The [user]'s [I] bounces off \the [src]'s armor!")) - deflect_sprite() - user.setClickCooldown(user.get_attack_speed(I)) - return - ..() - -/mob/living/simple_mob/mechanical/mecha/ex_act(severity) - if(prob(deflect_chance)) - severity++ // This somewhat misleadingly makes it less severe. - deflect_sprite() - ..(severity) - -/decl/mob_organ_names/mecha - hit_zones = list("central chassis", "control module", "hydraulics", "left arm", "right arm", "left leg", "right leg", "sensor suite", "radiator", "power supply", "left equipment mount", "right equipment mount") +// Mecha simple_mobs are essentially fake mechs. Generally tough and scary to fight. +// By default, they're automatically piloted by some kind of drone AI. They can be set to be "piloted" instead with a var. +// Tries to be as similar to the real deal as possible. + +/mob/living/simple_mob/mechanical/mecha + name = "mecha" + desc = "A big stompy mech!" + icon = 'icons/mecha/mecha.dmi' + + faction = "syndicate" + movement_cooldown = 1.5 + movement_sound = "mechstep" // This gets fed into playsound(), which can also take strings as a 'group' of sound files. + turn_sound = 'sound/mecha/mechturn.ogg' + maxHealth = 300 + mob_size = MOB_LARGE + damage_threshold = 5 //Anything that's 5 or less damage will not do damage. + + organ_names = /decl/mob_organ_names/mecha + + armor = list( + "melee" = 20, + "bullet" = 10, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + response_help = "taps on" + response_disarm = "knocks on" + response_harm = "uselessly hits" + harm_intent_damage = 0 + + ai_holder_type = /datum/ai_holder/simple_mob/melee + say_list_type = /datum/say_list/malf_drone + + var/datum/effect/effect/system/spark_spread/sparks + var/wreckage = /obj/effect/decal/mecha_wreckage/gygax/dark + var/pilot_type = null // Set to spawn a pilot when destroyed. Setting this also makes the mecha vulnerable to things that affect sentient minds. + var/deflect_chance = 10 // Chance to outright stop an attack, just like a normal exosuit. + var/has_repair_droid = FALSE // If true, heals 2 damage every tick and gets a repair droid overlay. + + +/mob/living/simple_mob/mechanical/mecha/Initialize() + sparks = new (src) + sparks.set_up(3, 1, src) + sparks.attach(src) + + if(!pilot_type) + name = "autonomous [initial(name)]" + desc = "[initial(desc)] It appears to be piloted by a drone intelligence." + else + say_list_type = /datum/say_list/merc + + if(has_repair_droid) + update_icon() + + return ..() + +/mob/living/simple_mob/mechanical/mecha/Destroy() + qdel_null(sparks) + return ..() + +/mob/living/simple_mob/mechanical/mecha/death() + ..(0,"explodes!") // Do everything else first. + + // Make the exploding more convincing with an actual explosion and some sparks. + sparks.start() + explosion(get_turf(src), 0, 0, 1, 3) + + // 'Eject' our pilot, if one exists. + if(pilot_type) + var/mob/living/L = new pilot_type(loc) + L.faction = src.faction + + new wreckage(loc) // Leave some wreckage. + + qdel(src) // Then delete us since we don't actually have a body. + +/mob/living/simple_mob/mechanical/mecha/handle_special() + if(has_repair_droid) + adjustBruteLoss(-2) + adjustFireLoss(-2) + adjustToxLoss(-2) + adjustOxyLoss(-2) + adjustCloneLoss(-2) + ..() + +/mob/living/simple_mob/mechanical/mecha/update_icon() + ..() // Cuts everything else, so do that first. + if(has_repair_droid) + add_overlay(image(icon = 'icons/mecha/mecha_equipment.dmi', icon_state = "repair_droid")) + +/mob/living/simple_mob/mechanical/mecha/bullet_act() + . = ..() + sparks.start() + +/mob/living/simple_mob/mechanical/mecha/speech_bubble_appearance() + return pilot_type ? "" : ..() + +// Piloted mechs are controlled by (presumably) something humanoid so they are vulnerable to certain things. +/mob/living/simple_mob/mechanical/mecha/is_sentient() + return pilot_type ? TRUE : FALSE + +/* +// Real mechs can't turn and run at the same time. This tries to simulate that. +// Commented out because the AI can't handle it sadly. +/mob/living/simple_mob/mechanical/mecha/SelfMove(turf/n, direct) + if(direct != dir) + set_dir(direct) + return FALSE // We didn't actually move, and returning FALSE means the mob can try to actually move almost immediately and not have to wait the full movement cooldown. + return ..() +*/ + +/mob/living/simple_mob/mechanical/mecha/bullet_act(obj/item/projectile/P) + if(prob(deflect_chance)) + visible_message(span("warning", "\The [P] is deflected by \the [src]'s armor!")) + deflect_sprite() + return 0 + return ..() + +/mob/living/simple_mob/mechanical/mecha/proc/deflect_sprite() + var/image/deflect_image = image('icons/effects/effects.dmi', "deflect_static") + add_overlay(deflect_image) + sleep(1 SECOND) + cut_overlay(deflect_image) + qdel(deflect_image) +// flick_overlay_view(deflect_image, src, duration = 1 SECOND, gc_after = TRUE) + +/mob/living/simple_mob/mechanical/mecha/attackby(obj/item/I, mob/user) + if(prob(deflect_chance)) + visible_message(span("warning", "\The [user]'s [I] bounces off \the [src]'s armor!")) + deflect_sprite() + user.setClickCooldown(user.get_attack_speed(I)) + return + ..() + +/mob/living/simple_mob/mechanical/mecha/ex_act(severity) + if(prob(deflect_chance)) + severity++ // This somewhat misleadingly makes it less severe. + deflect_sprite() + ..(severity) + +/decl/mob_organ_names/mecha + hit_zones = list("central chassis", "control module", "hydraulics", "left arm", "right arm", "left leg", "right leg", "sensor suite", "radiator", "power supply", "left equipment mount", "right equipment mount") diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm index c97050e6f63..736bc6caf51 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm @@ -1,81 +1,81 @@ -// Shoots syringe-darts at enemies, which applies a stacking poison modifier that hurts over time. -// They also do this in melee. -// Fortunately they're quite fragile and don't fire that fast. - -/datum/category_item/catalogue/technology/odysseus - name = "Exosuit - Odysseus" - desc = "A Vey-Medical innovation, the Odysseus was designed to incorporate some of their \ - other inventions, such as the Sleeper, into a mobile frame. Due to its ability to safely \ - rescue injured people in potentially hostile environments such as vacuum, as well as its \ - agility compared to other civilian exosuits, the Odysseus dominates the market for \ - medical exosuits." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/mechanical/mecha/odysseus - name = "odysseus" - desc = "These exosuits are developed and produced by Vey-Med. This one has a syringe gun." - catalogue_data = list( - /datum/category_item/catalogue/technology/odysseus, - /datum/category_item/catalogue/information/organization/vey_med - ) - icon_state = "odysseus" - wreckage = /obj/structure/loot_pile/mecha/odysseus - - maxHealth = 120 - movement_cooldown = -1 - turn_sound = 'sound/mecha/mechmove01.ogg' - - melee_damage_lower = 5 - melee_damage_upper = 5 - base_attack_cooldown = 2 SECONDS - attacktext = list("injected") - projectiletype = /obj/item/projectile/fake_syringe/poison - projectilesound = 'sound/weapons/empty.ogg' // Just like the syringe gun. - - ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/no_moonwalk - -/mob/living/simple_mob/mechanical/mecha/odysseus/manned - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged // Carries a pistol. - - -// Resprite of the regular one, perhaps for merc PoIs. -/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus - icon_state = "murdysseus" - wreckage = /obj/structure/loot_pile/mecha/odysseus/murdysseus - -/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus/manned - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged - - -/mob/living/simple_mob/mechanical/mecha/odysseus/apply_melee_effects(atom/A) - if(isliving(A)) - var/mob/living/L = A - - var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) - if(L.can_inject(src, null, target_zone)) - to_chat(L, span("warning", "You feel a tiny prick.")) - if(L.get_poison_protection() < 1) - L.add_modifier(/datum/modifier/poisoned, 30 SECONDS) - L.inflict_poison_damage(5) - - -// Fake syringe that tests if target can be injected before applying damage/modifiers/etc. -/obj/item/projectile/fake_syringe - name = "syringe" - icon_state = "syringe" - damage = 5 // Getting hit with a launched syringe probably hurts, and makes it at least slightly relevant against synthetics. - var/piercing = FALSE // If true, ignores thick material. - -/obj/item/projectile/fake_syringe/on_hit(atom/target, blocked = 0, def_zone = null) - if(isliving(target)) - var/mob/living/L = target - if(!L.can_inject(null, null, def_zone, piercing)) - return FALSE - to_chat(L, span("warning", "You feel a tiny prick.")) - return ..() // This will add the modifier and return the correct value. - - -// Fake syringe, which inflicts a long lasting modifier that slowly kills them. -/obj/item/projectile/fake_syringe/poison - modifier_type_to_apply = /datum/modifier/poisoned - modifier_duration = 1 MINUTE // About 30 damage per stack over a minute. +// Shoots syringe-darts at enemies, which applies a stacking poison modifier that hurts over time. +// They also do this in melee. +// Fortunately they're quite fragile and don't fire that fast. + +/datum/category_item/catalogue/technology/odysseus + name = "Exosuit - Odysseus" + desc = "A Vey-Medical innovation, the Odysseus was designed to incorporate some of their \ + other inventions, such as the Sleeper, into a mobile frame. Due to its ability to safely \ + rescue injured people in potentially hostile environments such as vacuum, as well as its \ + agility compared to other civilian exosuits, the Odysseus dominates the market for \ + medical exosuits." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/mechanical/mecha/odysseus + name = "odysseus" + desc = "These exosuits are developed and produced by Vey-Med. This one has a syringe gun." + catalogue_data = list( + /datum/category_item/catalogue/technology/odysseus, + /datum/category_item/catalogue/information/organization/vey_med + ) + icon_state = "odysseus" + wreckage = /obj/structure/loot_pile/mecha/odysseus + + maxHealth = 120 + movement_cooldown = -1 + turn_sound = 'sound/mecha/mechmove01.ogg' + + melee_damage_lower = 5 + melee_damage_upper = 5 + base_attack_cooldown = 2 SECONDS + attacktext = list("injected") + projectiletype = /obj/item/projectile/fake_syringe/poison + projectilesound = 'sound/weapons/empty.ogg' // Just like the syringe gun. + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/no_moonwalk + +/mob/living/simple_mob/mechanical/mecha/odysseus/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged // Carries a pistol. + + +// Resprite of the regular one, perhaps for merc PoIs. +/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus + icon_state = "murdysseus" + wreckage = /obj/structure/loot_pile/mecha/odysseus/murdysseus + +/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged + + +/mob/living/simple_mob/mechanical/mecha/odysseus/apply_melee_effects(atom/A) + if(isliving(A)) + var/mob/living/L = A + + var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + if(L.can_inject(src, null, target_zone)) + to_chat(L, span("warning", "You feel a tiny prick.")) + if(L.get_poison_protection() < 1) + L.add_modifier(/datum/modifier/poisoned, 30 SECONDS) + L.inflict_poison_damage(5) + + +// Fake syringe that tests if target can be injected before applying damage/modifiers/etc. +/obj/item/projectile/fake_syringe + name = "syringe" + icon_state = "syringe" + damage = 5 // Getting hit with a launched syringe probably hurts, and makes it at least slightly relevant against synthetics. + var/piercing = FALSE // If true, ignores thick material. + +/obj/item/projectile/fake_syringe/on_hit(atom/target, blocked = 0, def_zone = null) + if(isliving(target)) + var/mob/living/L = target + if(!L.can_inject(null, null, def_zone, piercing)) + return FALSE + to_chat(L, span("warning", "You feel a tiny prick.")) + return ..() // This will add the modifier and return the correct value. + + +// Fake syringe, which inflicts a long lasting modifier that slowly kills them. +/obj/item/projectile/fake_syringe/poison + modifier_type_to_apply = /datum/modifier/poisoned + modifier_duration = 1 MINUTE // About 30 damage per stack over a minute. diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/phazon.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/phazon.dm index 562a686385c..2e516da556f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/phazon.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/phazon.dm @@ -1,21 +1,21 @@ -// Phazons are weird. - -/mob/living/simple_mob/mechanical/mecha/combat/phazon - name = "phazon" - desc = "An extremly enigmatic exosuit." - icon_state = "phazon" - movement_cooldown = 1.5 - wreckage = /obj/structure/loot_pile/mecha/phazon - - maxHealth = 200 - deflect_chance = 30 - armor = list( - "melee" = 30, - "bullet" = 30, - "laser" = 30, - "energy" = 30, - "bomb" = 30, - "bio" = 100, - "rad" = 100 - ) - projectiletype = /obj/item/projectile/energy/declone +// Phazons are weird. + +/mob/living/simple_mob/mechanical/mecha/combat/phazon + name = "phazon" + desc = "An extremly enigmatic exosuit." + icon_state = "phazon" + movement_cooldown = 1.5 + wreckage = /obj/structure/loot_pile/mecha/phazon + + maxHealth = 200 + deflect_chance = 30 + armor = list( + "melee" = 30, + "bullet" = 30, + "laser" = 30, + "energy" = 30, + "bomb" = 30, + "bio" = 100, + "rad" = 100 + ) + projectiletype = /obj/item/projectile/energy/declone diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm index 063802ca4b2..960e96a65e3 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm @@ -1,75 +1,75 @@ -// Beefy, but somewhat slow. -// Melee attack is to bore you with its big drill, which has a lot of armor penetration and strikes rapidly. - -/datum/category_item/catalogue/technology/ripley - name = "Exosuit - APLU" - desc = "The Autonomous Power Loader Unit, more commonly designated as the 'Ripley', \ - is an exosuit that is often described as 'the workhorse of the exosuit world', \ - due to being designed for industrial use. Featuring a rugged design, they are fairly \ - resilient to the stresses of operation. As such, they are often used for various roles, \ - such as mining, construction, heavy lifting, and cargo transportation." - value = CATALOGUER_REWARD_EASY - - -/mob/living/simple_mob/mechanical/mecha/ripley - name = "\improper APLU ripley" - desc = "Autonomous Power Loader Unit. The workhorse of the exosuit world. This one has big drill." - catalogue_data = list(/datum/category_item/catalogue/technology/ripley) - icon_state = "ripley" - wreckage = /obj/structure/loot_pile/mecha/ripley - - maxHealth = 200 - - melee_damage_lower = 10 - melee_damage_upper = 10 - base_attack_cooldown = 5 // About 20 DPS. - attack_armor_pen = 50 - attack_sharp = TRUE - attack_sound = 'sound/mecha/mechdrill.ogg' - attacktext = list("drilled", "bored", "pierced") - -/mob/living/simple_mob/mechanical/mecha/ripley/manned - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged // Carries a pistol. - -/mob/living/simple_mob/mechanical/mecha/ripley/red_flames - icon_state = "ripley_flames_red" - -/mob/living/simple_mob/mechanical/mecha/ripley/blue_flames - icon_state = "ripley_flames_blue" - - -// Immune to heat damage, resistant to lasers, and somewhat beefier. Still tries to melee you. -/mob/living/simple_mob/mechanical/mecha/ripley/firefighter - name = "\improper APLU firefighter" - desc = "A standard APLU chassis, refitted with additional thermal protection and cistern. This one has a big drill." - icon_state = "firefighter" - wreckage = /obj/structure/loot_pile/mecha/ripley/firefighter - - maxHealth = 250 - heat_resist = 1 - armor = list( - "melee" = 0, - "bullet" = 20, - "laser" = 50, - "energy" = 0, - "bomb" = 50, - "bio" = 100, - "rad" = 100 - ) - -/mob/living/simple_mob/mechanical/mecha/ripley/firefighter/manned - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged - -// Mostly a joke mob, like the real DEATH-RIPLEY. -/mob/living/simple_mob/mechanical/mecha/ripley/deathripley - name = "\improper DEATH-RIPLEY" - desc = "OH SHIT RUN!!! IT HAS A KILL CLAMP!" - icon_state = "deathripley" - wreckage = /obj/structure/loot_pile/mecha/deathripley - - melee_damage_lower = 0 - melee_damage_upper = 0 - friendly = list("utterly obliterates", "furiously destroys", "permanently removes", "unflichingly decimates", "brutally murders", "absolutely demolishes", "completely annihilates") - -/mob/living/simple_mob/mechanical/mecha/ripley/deathripley/manned - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged +// Beefy, but somewhat slow. +// Melee attack is to bore you with its big drill, which has a lot of armor penetration and strikes rapidly. + +/datum/category_item/catalogue/technology/ripley + name = "Exosuit - APLU" + desc = "The Autonomous Power Loader Unit, more commonly designated as the 'Ripley', \ + is an exosuit that is often described as 'the workhorse of the exosuit world', \ + due to being designed for industrial use. Featuring a rugged design, they are fairly \ + resilient to the stresses of operation. As such, they are often used for various roles, \ + such as mining, construction, heavy lifting, and cargo transportation." + value = CATALOGUER_REWARD_EASY + + +/mob/living/simple_mob/mechanical/mecha/ripley + name = "\improper APLU ripley" + desc = "Autonomous Power Loader Unit. The workhorse of the exosuit world. This one has big drill." + catalogue_data = list(/datum/category_item/catalogue/technology/ripley) + icon_state = "ripley" + wreckage = /obj/structure/loot_pile/mecha/ripley + + maxHealth = 200 + + melee_damage_lower = 10 + melee_damage_upper = 10 + base_attack_cooldown = 5 // About 20 DPS. + attack_armor_pen = 50 + attack_sharp = TRUE + attack_sound = 'sound/mecha/mechdrill.ogg' + attacktext = list("drilled", "bored", "pierced") + +/mob/living/simple_mob/mechanical/mecha/ripley/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged // Carries a pistol. + +/mob/living/simple_mob/mechanical/mecha/ripley/red_flames + icon_state = "ripley_flames_red" + +/mob/living/simple_mob/mechanical/mecha/ripley/blue_flames + icon_state = "ripley_flames_blue" + + +// Immune to heat damage, resistant to lasers, and somewhat beefier. Still tries to melee you. +/mob/living/simple_mob/mechanical/mecha/ripley/firefighter + name = "\improper APLU firefighter" + desc = "A standard APLU chassis, refitted with additional thermal protection and cistern. This one has a big drill." + icon_state = "firefighter" + wreckage = /obj/structure/loot_pile/mecha/ripley/firefighter + + maxHealth = 250 + heat_resist = 1 + armor = list( + "melee" = 0, + "bullet" = 20, + "laser" = 50, + "energy" = 0, + "bomb" = 50, + "bio" = 100, + "rad" = 100 + ) + +/mob/living/simple_mob/mechanical/mecha/ripley/firefighter/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged + +// Mostly a joke mob, like the real DEATH-RIPLEY. +/mob/living/simple_mob/mechanical/mecha/ripley/deathripley + name = "\improper DEATH-RIPLEY" + desc = "OH SHIT RUN!!! IT HAS A KILL CLAMP!" + icon_state = "deathripley" + wreckage = /obj/structure/loot_pile/mecha/deathripley + + melee_damage_lower = 0 + melee_damage_upper = 0 + friendly = list("utterly obliterates", "furiously destroys", "permanently removes", "unflichingly decimates", "brutally murders", "absolutely demolishes", "completely annihilates") + +/mob/living/simple_mob/mechanical/mecha/ripley/deathripley/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/wahlem_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/wahlem_vr.dm index 6a98f7f44b4..b731653cc66 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/wahlem_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/wahlem_vr.dm @@ -1,35 +1,35 @@ -// Ancient "soul"-infused mechanical shells. Used by Hlf'erath. - -/mob/living/simple_mob/mechanical/wahlem - name = "Ancient Mechanical Warrior" - desc = "This construct is made of a brass-like material. Though aged, its shine is still brilliant, as it hovers ominously over the battlefield. Red flames spew from its shell. It diligently holds its shield and blade, at the ready, for any threats that may threaten its existence." - icon = 'icons/tgstation/clockworkwarrior.dmi' - icon_state = "clockM" - health = 300 - maxHealth = 300 - - faction = "golem" - - response_help = "brushes over" - response_disarm = "repulses away" - response_harm = "slices" - harm_intent_damage = 3 - friendly = "tolerates" - - melee_damage_lower = 30 // It has an ancient magic sword. - melee_damage_upper = 30 - attack_sound = 'sound/weapons/bladeslice.ogg' - attacktext = list("slashed") - - ai_holder_type = /datum/ai_holder/simple_mob/melee - - // Cataloguer data below - strange we can catalogue space golem wizards -/datum/category_item/catalogue/technology/drone/wahlem - name = "Ancient Construct" - desc = "Some sort of ancient, mechanical warrior, built for combat.\ - It has a brass and red theme. It wields a brass, slightly broken shield in its right hand. It has a sharp, foot-long blade in its other hand..\ - The drone has pristine armor, golden and shiny, with red cracks in its armour glowing visibly from inside.\ -

                    \ - The drone's frame is heavy and armored, unbendable by hand, is barren of any markings or ID,\ - no traces of paint visible and any 'writing' visible is uncomprehendable, short term scan unable to translate." - value = CATALOGUER_REWARD_MEDIUM +// Ancient "soul"-infused mechanical shells. Used by Hlf'erath. + +/mob/living/simple_mob/mechanical/wahlem + name = "Ancient Mechanical Warrior" + desc = "This construct is made of a brass-like material. Though aged, its shine is still brilliant, as it hovers ominously over the battlefield. Red flames spew from its shell. It diligently holds its shield and blade, at the ready, for any threats that may threaten its existence." + icon = 'icons/tgstation/clockworkwarrior.dmi' + icon_state = "clockM" + health = 300 + maxHealth = 300 + + faction = "golem" + + response_help = "brushes over" + response_disarm = "repulses away" + response_harm = "slices" + harm_intent_damage = 3 + friendly = "tolerates" + + melee_damage_lower = 30 // It has an ancient magic sword. + melee_damage_upper = 30 + attack_sound = 'sound/weapons/bladeslice.ogg' + attacktext = list("slashed") + + ai_holder_type = /datum/ai_holder/simple_mob/melee + + // Cataloguer data below - strange we can catalogue space golem wizards +/datum/category_item/catalogue/technology/drone/wahlem + name = "Ancient Construct" + desc = "Some sort of ancient, mechanical warrior, built for combat.\ + It has a brass and red theme. It wields a brass, slightly broken shield in its right hand. It has a sharp, foot-long blade in its other hand..\ + The drone has pristine armor, golden and shiny, with red cracks in its armour glowing visibly from inside.\ +

                    \ + The drone's frame is heavy and armored, unbendable by hand, is barren of any markings or ID,\ + no traces of paint visible and any 'writing' visible is uncomprehendable, short term scan unable to translate." + value = CATALOGUER_REWARD_MEDIUM diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm index aa49ce806b1..21f5e787223 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm @@ -1,134 +1,134 @@ -/* - 'Monitor' wards are drones that yell at their creator if they see someone besides them that they are hostile to. - They can also force an invisible entity to uncloak if the invisible mob is hostile to the ward. - If AI controlled, they will also warn their faction if they see a hostile entity, acting as floating cameras. -*/ - -/datum/category_item/catalogue/technology/drone/ward - name = "Drone - Monitor Ward" - desc = "This is a small drone that appears to have been designed for a singular purpose, \ - with little autonomous capability, common among the 'ward' models. This specific ward's \ - purpose is simply to observe the environment around it, reacting when it detects entities \ - it judges to be unfriendly. It presumably relays information about what it sees back to \ - whoever owns the drone.\ -

                    \ - The sensors onboard the ward are much more advanced than what is typical for drones, \ - allowing it to detect entities that might otherwise go unnoticed by inferior \ - observers. If this ward sees such a thing, it fires a beam of energy at the hidden \ - entity, which exposes them." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/mechanical/ward/monitor - desc = "It's a little flying drone. This one seems to be watching you..." - catalogue_data = list(/datum/category_item/catalogue/technology/drone/ward) - icon_state = "ward" - glow_color = "#00FF00" - see_invisible = SEE_INVISIBLE_LEVEL_TWO - - has_eye_glow = TRUE - glow_range = 3 - glow_intensity = 3 - glow_toggle = TRUE - - player_msg = "You will automatically alert your owner (if one exists) of enemies you see nearby.
                    \ - You can also see invisible entities, and will automatically uncloak nearby invisible or hidden enemies." - - ai_holder_type = /datum/ai_holder/simple_mob/monitor - - var/list/seen_mobs = list() - var/view_range = 5 - -// For PoIs. -/mob/living/simple_mob/mechanical/ward/monitor/syndicate - faction = "syndicate" - -/mob/living/simple_mob/mechanical/ward/monitor/crew - icon_state = "ward-nt" - -/mob/living/simple_mob/mechanical/ward/monitor/crew/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(istype(O, /obj/item/weapon/card/id) && !owner) - owner = user - return - ..() - -/mob/living/simple_mob/mechanical/ward/monitor/crew/IIsAlly(mob/living/L) - . = ..() - if(!.) - if(isrobot(L)) // They ignore synths. - return TRUE - return L.assess_perp(src, FALSE, FALSE, TRUE, FALSE) <= 3 - -/mob/living/simple_mob/mechanical/ward/monitor/death() - if(owner) - to_chat(owner, span("warning", "Your [src.name] inside [get_area(src)] was destroyed!")) - ..() - -/mob/living/simple_mob/mechanical/ward/monitor/handle_special() - detect_mobs() - -/mob/living/simple_mob/mechanical/ward/monitor/update_icon() - if(seen_mobs.len) - icon_living = "[initial(icon_state)]_spotted" - glow_color = "#FF0000" - else - icon_living = "[initial(icon_state)]" - glow_color = "#00FF00" - handle_light() // Update the light immediately. - ..() - -/mob/living/simple_mob/mechanical/ward/monitor/proc/detect_mobs() - var/last_seen_mobs_len = seen_mobs.len - var/list/mobs_nearby = hearers(view_range, src) - var/list/newly_seen_mobs = list() - for(var/mob/living/L in mobs_nearby) - if(L == src) // Don't detect ourselves. - continue - - if(L.stat) // Dead mobs aren't concerning. - continue - - if(src.IIsAlly(L)) - continue - - // Decloak them . - if(L.is_cloaked()) - Beam(L, icon_state = "solar_beam", time = 5) - playsound(L, 'sound/effects/EMPulse.ogg', 75, 1) - L.break_cloak() - - to_chat(L, span("danger", "\The [src] disrupts your cloak!")) - if(owner) - to_chat(owner, span("notice", "Your [src.name] at [get_area(src)] uncloaked \the [L].")) - - // Warn the owner when it sees a new mob. - if(!(L in seen_mobs)) - seen_mobs += L - newly_seen_mobs += L - - if(newly_seen_mobs.len && owner) // Yell at our owner if someone new shows up. - to_chat(owner, span("notice", "Your [src.name] at [get_area(src)] detected [english_list(newly_seen_mobs)].")) - - // Now get rid of old mobs that left vision. - for(var/thing in seen_mobs) - if(!(thing in mobs_nearby)) - seen_mobs -= thing - - // Check if we need to update icon. - if(seen_mobs.len != last_seen_mobs_len) - update_icon() - - -// Can't attack but calls for help. Used by the monitor and spotter wards. -// Special attacks are not blocked since they might be used for things besides attacking, and can be conditional. -/datum/ai_holder/simple_mob/monitor - hostile = TRUE // Required to call for help. - cooperative = TRUE - stand_ground = TRUE // So it doesn't run up to the thing it sees. - wander = FALSE - can_flee = FALSE - -/datum/ai_holder/simple_mob/monitor/melee_attack(atom/A) - return FALSE - -/datum/ai_holder/simple_mob/monitor/ranged_attack(atom/A) - return FALSE +/* + 'Monitor' wards are drones that yell at their creator if they see someone besides them that they are hostile to. + They can also force an invisible entity to uncloak if the invisible mob is hostile to the ward. + If AI controlled, they will also warn their faction if they see a hostile entity, acting as floating cameras. +*/ + +/datum/category_item/catalogue/technology/drone/ward + name = "Drone - Monitor Ward" + desc = "This is a small drone that appears to have been designed for a singular purpose, \ + with little autonomous capability, common among the 'ward' models. This specific ward's \ + purpose is simply to observe the environment around it, reacting when it detects entities \ + it judges to be unfriendly. It presumably relays information about what it sees back to \ + whoever owns the drone.\ +

                    \ + The sensors onboard the ward are much more advanced than what is typical for drones, \ + allowing it to detect entities that might otherwise go unnoticed by inferior \ + observers. If this ward sees such a thing, it fires a beam of energy at the hidden \ + entity, which exposes them." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/mechanical/ward/monitor + desc = "It's a little flying drone. This one seems to be watching you..." + catalogue_data = list(/datum/category_item/catalogue/technology/drone/ward) + icon_state = "ward" + glow_color = "#00FF00" + see_invisible = SEE_INVISIBLE_LEVEL_TWO + + has_eye_glow = TRUE + glow_range = 3 + glow_intensity = 3 + glow_toggle = TRUE + + player_msg = "You will automatically alert your owner (if one exists) of enemies you see nearby.
                    \ + You can also see invisible entities, and will automatically uncloak nearby invisible or hidden enemies." + + ai_holder_type = /datum/ai_holder/simple_mob/monitor + + var/list/seen_mobs = list() + var/view_range = 5 + +// For PoIs. +/mob/living/simple_mob/mechanical/ward/monitor/syndicate + faction = "syndicate" + +/mob/living/simple_mob/mechanical/ward/monitor/crew + icon_state = "ward-nt" + +/mob/living/simple_mob/mechanical/ward/monitor/crew/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(istype(O, /obj/item/weapon/card/id) && !owner) + owner = user + return + ..() + +/mob/living/simple_mob/mechanical/ward/monitor/crew/IIsAlly(mob/living/L) + . = ..() + if(!.) + if(isrobot(L)) // They ignore synths. + return TRUE + return L.assess_perp(src, FALSE, FALSE, TRUE, FALSE) <= 3 + +/mob/living/simple_mob/mechanical/ward/monitor/death() + if(owner) + to_chat(owner, span("warning", "Your [src.name] inside [get_area(src)] was destroyed!")) + ..() + +/mob/living/simple_mob/mechanical/ward/monitor/handle_special() + detect_mobs() + +/mob/living/simple_mob/mechanical/ward/monitor/update_icon() + if(seen_mobs.len) + icon_living = "[initial(icon_state)]_spotted" + glow_color = "#FF0000" + else + icon_living = "[initial(icon_state)]" + glow_color = "#00FF00" + handle_light() // Update the light immediately. + ..() + +/mob/living/simple_mob/mechanical/ward/monitor/proc/detect_mobs() + var/last_seen_mobs_len = seen_mobs.len + var/list/mobs_nearby = hearers(view_range, src) + var/list/newly_seen_mobs = list() + for(var/mob/living/L in mobs_nearby) + if(L == src) // Don't detect ourselves. + continue + + if(L.stat) // Dead mobs aren't concerning. + continue + + if(src.IIsAlly(L)) + continue + + // Decloak them . + if(L.is_cloaked()) + Beam(L, icon_state = "solar_beam", time = 5) + playsound(L, 'sound/effects/EMPulse.ogg', 75, 1) + L.break_cloak() + + to_chat(L, span("danger", "\The [src] disrupts your cloak!")) + if(owner) + to_chat(owner, span("notice", "Your [src.name] at [get_area(src)] uncloaked \the [L].")) + + // Warn the owner when it sees a new mob. + if(!(L in seen_mobs)) + seen_mobs += L + newly_seen_mobs += L + + if(newly_seen_mobs.len && owner) // Yell at our owner if someone new shows up. + to_chat(owner, span("notice", "Your [src.name] at [get_area(src)] detected [english_list(newly_seen_mobs)].")) + + // Now get rid of old mobs that left vision. + for(var/thing in seen_mobs) + if(!(thing in mobs_nearby)) + seen_mobs -= thing + + // Check if we need to update icon. + if(seen_mobs.len != last_seen_mobs_len) + update_icon() + + +// Can't attack but calls for help. Used by the monitor and spotter wards. +// Special attacks are not blocked since they might be used for things besides attacking, and can be conditional. +/datum/ai_holder/simple_mob/monitor + hostile = TRUE // Required to call for help. + cooperative = TRUE + stand_ground = TRUE // So it doesn't run up to the thing it sees. + wander = FALSE + can_flee = FALSE + +/datum/ai_holder/simple_mob/monitor/melee_attack(atom/A) + return FALSE + +/datum/ai_holder/simple_mob/monitor/ranged_attack(atom/A) + return FALSE diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm index 9120bec7283..79aba5ce9d1 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm @@ -1,47 +1,47 @@ -/* - Wards are a specific type of mechanical simplemob that generally fill a support role for their faction or for a specific mob. - Generally they are helpless by themselves and are fragile, but can do very useful things if protected. This makes them a high priority target. -*/ - -/mob/living/simple_mob/mechanical/ward - name = "ward" - desc = "A small floating machine. This one seems rather useless..." - icon = 'icons/mob/critter.dmi' - icon_state = "ward" - icon_living = "ward" - hovering = TRUE // Won't trigger landmines. - response_help = "pets" - response_disarm = "swats away" - response_harm = "punches" - faction = "wards" // Needed as most human mobs are in neutral faction. The owner is generally except from any ward hostility regardless. - - organ_names = /decl/mob_organ_names/ward - - maxHealth = 15 - health = 15 - movement_cooldown = -1 - hovering = TRUE - - mob_bump_flag = 0 - - melee_damage_lower = 0 - melee_damage_upper = 0 - - ai_holder_type = null - var/mob/living/owner = null // The mob that made the ward, if any. Used to ensure the ward does not interfere with its creator. - -/mob/living/simple_mob/mechanical/ward/death() - ..(null,"is smashed into pieces!") - qdel(src) - -/mob/living/simple_mob/mechanical/ward/Destroy() - owner = null - return ..() - -/mob/living/simple_mob/mechanical/ward/IIsAlly(mob/living/L) - if(owner == L) - return TRUE - return ..() - -/decl/mob_organ_names/ward - hit_zones = list("chassis", "sensor array", "hover thruster") +/* + Wards are a specific type of mechanical simplemob that generally fill a support role for their faction or for a specific mob. + Generally they are helpless by themselves and are fragile, but can do very useful things if protected. This makes them a high priority target. +*/ + +/mob/living/simple_mob/mechanical/ward + name = "ward" + desc = "A small floating machine. This one seems rather useless..." + icon = 'icons/mob/critter.dmi' + icon_state = "ward" + icon_living = "ward" + hovering = TRUE // Won't trigger landmines. + response_help = "pets" + response_disarm = "swats away" + response_harm = "punches" + faction = "wards" // Needed as most human mobs are in neutral faction. The owner is generally except from any ward hostility regardless. + + organ_names = /decl/mob_organ_names/ward + + maxHealth = 15 + health = 15 + movement_cooldown = -1 + hovering = TRUE + + mob_bump_flag = 0 + + melee_damage_lower = 0 + melee_damage_upper = 0 + + ai_holder_type = null + var/mob/living/owner = null // The mob that made the ward, if any. Used to ensure the ward does not interfere with its creator. + +/mob/living/simple_mob/mechanical/ward/death() + ..(null,"is smashed into pieces!") + qdel(src) + +/mob/living/simple_mob/mechanical/ward/Destroy() + owner = null + return ..() + +/mob/living/simple_mob/mechanical/ward/IIsAlly(mob/living/L) + if(owner == L) + return TRUE + return ..() + +/decl/mob_organ_names/ward + hit_zones = list("chassis", "sensor array", "hover thruster") diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm index ea524c3c802..0cddbaa268c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm @@ -1,95 +1,95 @@ -// These slimes lack certain xenobio features but get more combat-oriented goodies. Generally these are more oriented towards Explorers than Xenobiologists. - -/mob/living/simple_mob/slime/feral - name = "feral slime" - desc = "The result of slimes escaping containment from some xenobiology lab. \ - Having the means to successfully escape their lab, as well as having to survive on a harsh, cold world has made these \ - creatures rival the ferocity of other apex predators in this region of Sif. It is considered to be a very invasive species." - description_info = "Note that processing this large slime will give six cores." - - cores = 6 // Xenobio will love getting their hands on these. - - icon_state = "slime adult" - icon_living = "slime adult" - icon_dead = "slime adult dead" - glow_range = 5 - glow_intensity = 4 - icon_scale_x = 2 // Twice as big as the xenobio variant. - icon_scale_y = 2 - pixel_y = -10 // Since the base sprite isn't centered properly, the pixel auto-adjustment needs some help. - default_pixel_y = -10 // To prevent resetting above var. - - maxHealth = 300 - movement_cooldown = -3 - melee_attack_delay = 0.5 SECONDS - - ai_holder_type = /datum/ai_holder/simple_mob/ranged/pointblank - - -// Slimebatoning/xenotasing it just makes it mad at you (which can be good if you're heavily armored and your friends aren't). -/mob/living/simple_mob/slime/feral/slimebatoned(mob/living/user, amount) - taunt(user, TRUE) - - -// *********** -// *Dark Blue* -// *********** - -// Dark Blue feral slimes can fire a strong icicle projectile every few seconds. The icicle hits hard and has some armor penetration. -// They also have a similar aura as their xenobio counterparts, which inflicts cold damage. It also chills non-resistant mobs. - -/mob/living/simple_mob/slime/feral/dark_blue - name = "dark blue feral slime" - color = "#2398FF" - glow_toggle = TRUE - slime_color = "dark blue" - coretype = /obj/item/slime_extract/dark_blue - cold_resist = 1 // Complete immunity. - minbodytemp = 0 - cold_damage_per_tick = 0 - - projectiletype = /obj/item/projectile/icicle - base_attack_cooldown = 2 SECONDS - ranged_attack_delay = 1 SECOND - - player_msg = "You can fire an icicle projectile every two seconds. It hits hard, and armor has a hard time resisting it.
                    \ - You are also immune to the cold, and you cause enemies around you to suffer periodic harm from the cold, if unprotected.
                    \ - Unprotected enemies are also Chilled, making them slower and less evasive, and disabling effects last longer." - -/obj/item/projectile/icicle - name = "icicle" - icon_state = "ice_2" - damage = 40 - damage_type = BRUTE - check_armour = "melee" - armor_penetration = 30 - speed = 2 - icon_scale_x = 2 // It hits like a truck. - icon_scale_y = 2 - sharp = TRUE - -/obj/item/projectile/icicle/on_impact(atom/A) - playsound(A, "shatter", 70, 1) - return ..() - -/obj/item/projectile/icicle/get_structure_damage() - return damage / 2 // They're really deadly against mobs, but less effective against solid things. - -/mob/living/simple_mob/slime/feral/dark_blue/handle_special() - if(stat != DEAD) - cold_aura() - ..() - -/mob/living/simple_mob/slime/feral/dark_blue/proc/cold_aura() - for(var/mob/living/L in view(3, src)) - if(L == src) - continue - chill(L) - -/mob/living/simple_mob/slime/feral/dark_blue/proc/chill(mob/living/L) - L.inflict_cold_damage(10) - if(L.get_cold_protection() < 1) - L.add_modifier(/datum/modifier/chilled, 5 SECONDS, src) - - if(L.has_AI()) // Other AIs should react to hostile auras. - L.ai_holder.react_to_attack(src) +// These slimes lack certain xenobio features but get more combat-oriented goodies. Generally these are more oriented towards Explorers than Xenobiologists. + +/mob/living/simple_mob/slime/feral + name = "feral slime" + desc = "The result of slimes escaping containment from some xenobiology lab. \ + Having the means to successfully escape their lab, as well as having to survive on a harsh, cold world has made these \ + creatures rival the ferocity of other apex predators in this region of Sif. It is considered to be a very invasive species." + description_info = "Note that processing this large slime will give six cores." + + cores = 6 // Xenobio will love getting their hands on these. + + icon_state = "slime adult" + icon_living = "slime adult" + icon_dead = "slime adult dead" + glow_range = 5 + glow_intensity = 4 + icon_scale_x = 2 // Twice as big as the xenobio variant. + icon_scale_y = 2 + pixel_y = -10 // Since the base sprite isn't centered properly, the pixel auto-adjustment needs some help. + default_pixel_y = -10 // To prevent resetting above var. + + maxHealth = 300 + movement_cooldown = -3 + melee_attack_delay = 0.5 SECONDS + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/pointblank + + +// Slimebatoning/xenotasing it just makes it mad at you (which can be good if you're heavily armored and your friends aren't). +/mob/living/simple_mob/slime/feral/slimebatoned(mob/living/user, amount) + taunt(user, TRUE) + + +// *********** +// *Dark Blue* +// *********** + +// Dark Blue feral slimes can fire a strong icicle projectile every few seconds. The icicle hits hard and has some armor penetration. +// They also have a similar aura as their xenobio counterparts, which inflicts cold damage. It also chills non-resistant mobs. + +/mob/living/simple_mob/slime/feral/dark_blue + name = "dark blue feral slime" + color = "#2398FF" + glow_toggle = TRUE + slime_color = "dark blue" + coretype = /obj/item/slime_extract/dark_blue + cold_resist = 1 // Complete immunity. + minbodytemp = 0 + cold_damage_per_tick = 0 + + projectiletype = /obj/item/projectile/icicle + base_attack_cooldown = 2 SECONDS + ranged_attack_delay = 1 SECOND + + player_msg = "You can fire an icicle projectile every two seconds. It hits hard, and armor has a hard time resisting it.
                    \ + You are also immune to the cold, and you cause enemies around you to suffer periodic harm from the cold, if unprotected.
                    \ + Unprotected enemies are also Chilled, making them slower and less evasive, and disabling effects last longer." + +/obj/item/projectile/icicle + name = "icicle" + icon_state = "ice_2" + damage = 40 + damage_type = BRUTE + check_armour = "melee" + armor_penetration = 30 + speed = 2 + icon_scale_x = 2 // It hits like a truck. + icon_scale_y = 2 + sharp = TRUE + +/obj/item/projectile/icicle/on_impact(atom/A) + playsound(A, "shatter", 70, 1) + return ..() + +/obj/item/projectile/icicle/get_structure_damage() + return damage / 2 // They're really deadly against mobs, but less effective against solid things. + +/mob/living/simple_mob/slime/feral/dark_blue/handle_special() + if(stat != DEAD) + cold_aura() + ..() + +/mob/living/simple_mob/slime/feral/dark_blue/proc/cold_aura() + for(var/mob/living/L in view(3, src)) + if(L == src) + continue + chill(L) + +/mob/living/simple_mob/slime/feral/dark_blue/proc/chill(mob/living/L) + L.inflict_cold_damage(10) + if(L.get_cold_protection() < 1) + L.add_modifier(/datum/modifier/chilled, 5 SECONDS, src) + + if(L.has_AI()) // Other AIs should react to hostile auras. + L.ai_holder.react_to_attack(src) diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/combat.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/combat.dm index d74f2b4fcbd..03565489cdc 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/combat.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/combat.dm @@ -1,83 +1,83 @@ -// Code for slimes attacking other things. - -// Slime attacks change based on intent. -/mob/living/simple_mob/slime/xenobio/apply_attack(mob/living/L, damage_to_do) - if(istype(L)) - switch(a_intent) - if(I_HELP) // This shouldn't happen but just in case. - return FALSE - - if(I_DISARM) - var/stun_power = between(0, power_charge + rand(0, 3), 10) - - if(ishuman(L)) - var/mob/living/carbon/human/H = L - stun_power *= max(H.species.siemens_coefficient, 0) - - if(prob(stun_power * 10)) // Try an electric shock. - power_charge = max(0, power_charge - 3) - L.visible_message( - span("danger", "\The [src] has shocked \the [L]!"), - span("danger", "\The [src] has shocked you!") - ) - playsound(src, 'sound/weapons/Egloves.ogg', 75, 1) - L.Weaken(4) - L.Stun(4) - do_attack_animation(L) - if(L.buckled) - L.buckled.unbuckle_mob() // To prevent an exploit where being buckled prevents slimes from jumping on you. - L.stuttering = max(L.stuttering, stun_power) - - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, L) - s.start() - - if(prob(stun_power * 10) && stun_power >= 8) - L.adjustFireLoss(power_charge * rand(1, 2)) - return FALSE - - else if(prob(20)) // Try to do a regular disarm attack. - L.visible_message( - span("danger", "\The [src] has pounced at \the [L]!"), - span("danger", "\The [src] has pounced at you!") - ) - playsound(src, 'sound/weapons/thudswoosh.ogg', 75, 1) - L.Weaken(2) - do_attack_animation(L) - if(L.buckled) - L.buckled.unbuckle_mob() // To prevent an exploit where being buckled prevents slimes from jumping on you. - return FALSE - - else // Failed to do anything this time. - L.visible_message( - span("warning", "\The [src] has tried to pounce at \the [L]!"), - span("warning", "\The [src] has tried to pounce at you!") - ) - playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1) - do_attack_animation(L) - return FALSE - - if(I_GRAB) - start_consuming(L) - return FALSE - - if(I_HURT) - return ..() // Regular stuff. - else - return ..() // Do the regular stuff if we're hitting a window/mech/etc. - -/mob/living/simple_mob/slime/xenobio/apply_melee_effects(mob/living/L) - if(istype(L) && a_intent == I_HURT) - // Pump them full of toxins, if able. - if(L.reagents && L.can_inject() && reagent_injected) - L.reagents.add_reagent(reagent_injected, injection_amount) - - // Feed off of their flesh, if able. - consume(L, 5) - - -/mob/living/simple_mob/slime/xenobio/AltClickOn(atom/movable/A) - if(isliving(A) && Adjacent(A)) - animal_nom(A) - else +// Code for slimes attacking other things. + +// Slime attacks change based on intent. +/mob/living/simple_mob/slime/xenobio/apply_attack(mob/living/L, damage_to_do) + if(istype(L)) + switch(a_intent) + if(I_HELP) // This shouldn't happen but just in case. + return FALSE + + if(I_DISARM) + var/stun_power = between(0, power_charge + rand(0, 3), 10) + + if(ishuman(L)) + var/mob/living/carbon/human/H = L + stun_power *= max(H.species.siemens_coefficient, 0) + + if(prob(stun_power * 10)) // Try an electric shock. + power_charge = max(0, power_charge - 3) + L.visible_message( + span("danger", "\The [src] has shocked \the [L]!"), + span("danger", "\The [src] has shocked you!") + ) + playsound(src, 'sound/weapons/Egloves.ogg', 75, 1) + L.Weaken(4) + L.Stun(4) + do_attack_animation(L) + if(L.buckled) + L.buckled.unbuckle_mob() // To prevent an exploit where being buckled prevents slimes from jumping on you. + L.stuttering = max(L.stuttering, stun_power) + + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, L) + s.start() + + if(prob(stun_power * 10) && stun_power >= 8) + L.adjustFireLoss(power_charge * rand(1, 2)) + return FALSE + + else if(prob(20)) // Try to do a regular disarm attack. + L.visible_message( + span("danger", "\The [src] has pounced at \the [L]!"), + span("danger", "\The [src] has pounced at you!") + ) + playsound(src, 'sound/weapons/thudswoosh.ogg', 75, 1) + L.Weaken(2) + do_attack_animation(L) + if(L.buckled) + L.buckled.unbuckle_mob() // To prevent an exploit where being buckled prevents slimes from jumping on you. + return FALSE + + else // Failed to do anything this time. + L.visible_message( + span("warning", "\The [src] has tried to pounce at \the [L]!"), + span("warning", "\The [src] has tried to pounce at you!") + ) + playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1) + do_attack_animation(L) + return FALSE + + if(I_GRAB) + start_consuming(L) + return FALSE + + if(I_HURT) + return ..() // Regular stuff. + else + return ..() // Do the regular stuff if we're hitting a window/mech/etc. + +/mob/living/simple_mob/slime/xenobio/apply_melee_effects(mob/living/L) + if(istype(L) && a_intent == I_HURT) + // Pump them full of toxins, if able. + if(L.reagents && L.can_inject() && reagent_injected) + L.reagents.add_reagent(reagent_injected, injection_amount) + + // Feed off of their flesh, if able. + consume(L, 5) + + +/mob/living/simple_mob/slime/xenobio/AltClickOn(atom/movable/A) + if(isliving(A) && Adjacent(A)) + animal_nom(A) + else ..() \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm index d6b5948c264..0764c01de68 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm @@ -1,172 +1,172 @@ -// Handles hunger, starvation, growth, and eatting humans. - -/mob/living/simple_mob/slime/xenobio/adjust_nutrition(input, var/heal = 1) - ..(input) - - if(input > 0) - // Gain around one level per 50 nutrition. - if(prob(input * 2)) - power_charge = min(power_charge++, 10) - if(power_charge == 10) - adjustToxLoss(-10) - - // Heal 1 point of damage per 5 nutrition coming in. - if(heal) - adjustBruteLoss(-input * 0.2) - adjustFireLoss(-input * 0.2) - adjustToxLoss(-input * 0.2) - adjustOxyLoss(-input * 0.2) - adjustCloneLoss(-input * 0.2) - -/mob/living/simple_mob/slime/xenobio/proc/get_grow_nutrition() // Above it we grow, below it we can eat - return is_adult ? 1000 : 800 - -/mob/living/simple_mob/slime/xenobio/proc/get_hunger_nutrition() // Below it we will always eat - return is_adult ? 600 : 500 - -/mob/living/simple_mob/slime/xenobio/proc/get_starve_nutrition() // Below it we will eat before everything else - return is_adult ? 300 : 200 - -// Called by Life(). -/mob/living/simple_mob/slime/xenobio/proc/handle_nutrition() - if(harmless) - return - - if(prob(15)) - adjust_nutrition(is_adult ? -2 : -1) // Adult slimes get hungry faster. - - if(nutrition <= get_starve_nutrition()) - handle_starvation() - - else if(nutrition >= get_grow_nutrition() && amount_grown < 10) - adjust_nutrition(-20) - amount_grown = between(0, amount_grown + 1, 10) - -// Called if above proc happens while below a nutrition threshold. -/mob/living/simple_mob/slime/xenobio/proc/handle_starvation() - if(nutrition < get_starve_nutrition() && !client) // if a slime is starving, it starts losing its friends - if(friends.len && prob(1)) - var/mob/nofriend = pick(friends) - if(nofriend) - friends -= nofriend - say("[nofriend]... food now...") - - if(nutrition <= 0) - adjustToxLoss(rand(1,3)) - if(client && prob(5)) - to_chat(src, span("danger", "You are starving!")) - - -/mob/living/simple_mob/slime/xenobio/proc/handle_consumption() - if(victim && !stat) - if(istype(victim) && consume(victim, 20)) - if(prob(25)) - to_chat(src, span("notice", "You continue absorbing \the [victim].")) - - else - var/list/feedback = list( - "This subject is incompatible", - "This subject does not have a life energy", - "This subject is empty", - "I am not satisfied", - "I can not feed from this subject", - "I do not feel nourished", - "This subject is not food" - ) - to_chat(src, span("warning", "[pick(feedback)]...")) - stop_consumption() - - if(victim) - victim.updatehealth() - - else - stop_consumption() - -/mob/living/simple_mob/slime/xenobio/proc/start_consuming(mob/living/L) - if(!can_consume(L)) - return - if(!Adjacent(L)) - return - - step_towards(src, L) // Get on top of them to feed. - if(loc != L.loc) - return - - if(L.buckle_mob(src, forced = TRUE)) - victim = L - update_icon() - set_AI_busy(TRUE) // Don't want the AI to interfere with eatting. - victim.visible_message( - span("danger", "\The [src] latches onto \the [victim]!"), - span("critical", "\The [src] latches onto you!") - ) - -/mob/living/simple_mob/slime/xenobio/proc/stop_consumption(mob/living/L) - if(!victim) - return - victim.unbuckle_mob() - victim.visible_message( - span("notice", "\The [src] slides off of [victim]!"), - span("notice", "\The [src] slides off of you!") - ) - victim = null - update_icon() - set_AI_busy(FALSE) // Resume normal operations. - -/mob/living/simple_mob/slime/xenobio/proc/can_consume(mob/living/L) - if(!L || !istype(L)) - to_chat(src, "This subject is incomparable...") - return FALSE - if(harmless) - to_chat(src, "I am pacified... I cannot eat...") - return FALSE - if(L.mob_class & MOB_CLASS_SLIME) - to_chat(src, "I cannot feed on other slimes...") - return FALSE - if(L.isSynthetic()) - to_chat(src, "This subject is not biological...") - return FALSE - if(L.getarmor(null, "bio") >= 75) - to_chat(src, "I cannot reach this subject's biological matter...") - return FALSE - if(!Adjacent(L)) - to_chat(src, "This subject is too far away...") - return FALSE - if(L.getCloneLoss() >= L.getMaxHealth() * 1.5) - to_chat(src, "This subject does not have an edible life energy...") - return FALSE - //VOREStation Addition start - if(istype(L, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = L - if(H.species.flags & NO_SCAN) - to_chat(src, "This subject's life energy is beyond my reach...") - return FALSE - //VOREStation Addition end - if(L.has_buckled_mobs()) - for(var/A in L.buckled_mobs) - if(istype(A, /mob/living/simple_mob/slime/xenobio)) - if(A != src) - to_chat(src, "\The [A] is already feeding on this subject...") - return FALSE - return TRUE - -// This does the actual damage, as well as give nutrition and heals. -// Assuming no bio armor, calling consume(10) will result in; -// 6 clone damage to victim -// 4 tox damage to victim. -// 25 nutrition for the slime. -// 2 points of damage healed on the slime (as a result of the nutrition). -// 50% of giving +1 charge to the slime (same as above). -/mob/living/simple_mob/slime/xenobio/proc/consume(mob/living/victim, amount) - if(can_consume(victim)) - var/armor_modifier = abs((victim.getarmor(null, "bio") / 100) - 1) - var/damage_done = amount * armor_modifier - if(damage_done > 0) - victim.adjustCloneLoss(damage_done * 0.6) - victim.adjustToxLoss(damage_done * 0.4) - adjust_nutrition(damage_done * 5) - Beam(victim, icon_state = "slime_consume", time = 8) - to_chat(src, span("notice", "You absorb some biomaterial from \the [victim].")) - to_chat(victim, span("danger", "\The [src] consumes some of your flesh!")) - return TRUE - return FALSE +// Handles hunger, starvation, growth, and eatting humans. + +/mob/living/simple_mob/slime/xenobio/adjust_nutrition(input, var/heal = 1) + ..(input) + + if(input > 0) + // Gain around one level per 50 nutrition. + if(prob(input * 2)) + power_charge = min(power_charge++, 10) + if(power_charge == 10) + adjustToxLoss(-10) + + // Heal 1 point of damage per 5 nutrition coming in. + if(heal) + adjustBruteLoss(-input * 0.2) + adjustFireLoss(-input * 0.2) + adjustToxLoss(-input * 0.2) + adjustOxyLoss(-input * 0.2) + adjustCloneLoss(-input * 0.2) + +/mob/living/simple_mob/slime/xenobio/proc/get_grow_nutrition() // Above it we grow, below it we can eat + return is_adult ? 1000 : 800 + +/mob/living/simple_mob/slime/xenobio/proc/get_hunger_nutrition() // Below it we will always eat + return is_adult ? 600 : 500 + +/mob/living/simple_mob/slime/xenobio/proc/get_starve_nutrition() // Below it we will eat before everything else + return is_adult ? 300 : 200 + +// Called by Life(). +/mob/living/simple_mob/slime/xenobio/proc/handle_nutrition() + if(harmless) + return + + if(prob(15)) + adjust_nutrition(is_adult ? -2 : -1) // Adult slimes get hungry faster. + + if(nutrition <= get_starve_nutrition()) + handle_starvation() + + else if(nutrition >= get_grow_nutrition() && amount_grown < 10) + adjust_nutrition(-20) + amount_grown = between(0, amount_grown + 1, 10) + +// Called if above proc happens while below a nutrition threshold. +/mob/living/simple_mob/slime/xenobio/proc/handle_starvation() + if(nutrition < get_starve_nutrition() && !client) // if a slime is starving, it starts losing its friends + if(friends.len && prob(1)) + var/mob/nofriend = pick(friends) + if(nofriend) + friends -= nofriend + say("[nofriend]... food now...") + + if(nutrition <= 0) + adjustToxLoss(rand(1,3)) + if(client && prob(5)) + to_chat(src, span("danger", "You are starving!")) + + +/mob/living/simple_mob/slime/xenobio/proc/handle_consumption() + if(victim && !stat) + if(istype(victim) && consume(victim, 20)) + if(prob(25)) + to_chat(src, span("notice", "You continue absorbing \the [victim].")) + + else + var/list/feedback = list( + "This subject is incompatible", + "This subject does not have a life energy", + "This subject is empty", + "I am not satisfied", + "I can not feed from this subject", + "I do not feel nourished", + "This subject is not food" + ) + to_chat(src, span("warning", "[pick(feedback)]...")) + stop_consumption() + + if(victim) + victim.updatehealth() + + else + stop_consumption() + +/mob/living/simple_mob/slime/xenobio/proc/start_consuming(mob/living/L) + if(!can_consume(L)) + return + if(!Adjacent(L)) + return + + step_towards(src, L) // Get on top of them to feed. + if(loc != L.loc) + return + + if(L.buckle_mob(src, forced = TRUE)) + victim = L + update_icon() + set_AI_busy(TRUE) // Don't want the AI to interfere with eatting. + victim.visible_message( + span("danger", "\The [src] latches onto \the [victim]!"), + span("critical", "\The [src] latches onto you!") + ) + +/mob/living/simple_mob/slime/xenobio/proc/stop_consumption(mob/living/L) + if(!victim) + return + victim.unbuckle_mob() + victim.visible_message( + span("notice", "\The [src] slides off of [victim]!"), + span("notice", "\The [src] slides off of you!") + ) + victim = null + update_icon() + set_AI_busy(FALSE) // Resume normal operations. + +/mob/living/simple_mob/slime/xenobio/proc/can_consume(mob/living/L) + if(!L || !istype(L)) + to_chat(src, "This subject is incomparable...") + return FALSE + if(harmless) + to_chat(src, "I am pacified... I cannot eat...") + return FALSE + if(L.mob_class & MOB_CLASS_SLIME) + to_chat(src, "I cannot feed on other slimes...") + return FALSE + if(L.isSynthetic()) + to_chat(src, "This subject is not biological...") + return FALSE + if(L.getarmor(null, "bio") >= 75) + to_chat(src, "I cannot reach this subject's biological matter...") + return FALSE + if(!Adjacent(L)) + to_chat(src, "This subject is too far away...") + return FALSE + if(L.getCloneLoss() >= L.getMaxHealth() * 1.5) + to_chat(src, "This subject does not have an edible life energy...") + return FALSE + //VOREStation Addition start + if(istype(L, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = L + if(H.species.flags & NO_SCAN) + to_chat(src, "This subject's life energy is beyond my reach...") + return FALSE + //VOREStation Addition end + if(L.has_buckled_mobs()) + for(var/A in L.buckled_mobs) + if(istype(A, /mob/living/simple_mob/slime/xenobio)) + if(A != src) + to_chat(src, "\The [A] is already feeding on this subject...") + return FALSE + return TRUE + +// This does the actual damage, as well as give nutrition and heals. +// Assuming no bio armor, calling consume(10) will result in; +// 6 clone damage to victim +// 4 tox damage to victim. +// 25 nutrition for the slime. +// 2 points of damage healed on the slime (as a result of the nutrition). +// 50% of giving +1 charge to the slime (same as above). +/mob/living/simple_mob/slime/xenobio/proc/consume(mob/living/victim, amount) + if(can_consume(victim)) + var/armor_modifier = abs((victim.getarmor(null, "bio") / 100) - 1) + var/damage_done = amount * armor_modifier + if(damage_done > 0) + victim.adjustCloneLoss(damage_done * 0.6) + victim.adjustToxLoss(damage_done * 0.4) + adjust_nutrition(damage_done * 5) + Beam(victim, icon_state = "slime_consume", time = 8) + to_chat(src, span("notice", "You absorb some biomaterial from \the [victim].")) + to_chat(victim, span("danger", "\The [src] consumes some of your flesh!")) + return TRUE + return FALSE diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/discipline.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/discipline.dm index 5360dab7b33..0ab838f329a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/discipline.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/discipline.dm @@ -1,23 +1,23 @@ -// Handles the subjugation of slimes by force. -// Mostly a way for things to talk to the AI indirectly. - -/mob/living/simple_mob/slime/xenobio/proc/adjust_discipline(amount, silent) - if(amount > 0) - to_chat(src, span("warning", "You've been disciplined!")) - if(has_AI()) - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - AI.adjust_discipline(amount, silent) - - -/mob/living/simple_mob/slime/xenobio/proc/is_justified_to_discipline() - if(victim) // Punish if eating someone that isn't a monkey. - if(ishuman(victim)) - var/mob/living/carbon/human/H = victim - if(istype(H.species, /datum/species/monkey)) - return FALSE - return TRUE - - else if(has_AI()) // Now for thoughtcrimes. - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - return AI.is_justified_to_discipline() // Will return true if targeting a non-monkey. - return FALSE +// Handles the subjugation of slimes by force. +// Mostly a way for things to talk to the AI indirectly. + +/mob/living/simple_mob/slime/xenobio/proc/adjust_discipline(amount, silent) + if(amount > 0) + to_chat(src, span("warning", "You've been disciplined!")) + if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + AI.adjust_discipline(amount, silent) + + +/mob/living/simple_mob/slime/xenobio/proc/is_justified_to_discipline() + if(victim) // Punish if eating someone that isn't a monkey. + if(ishuman(victim)) + var/mob/living/carbon/human/H = victim + if(istype(H.species, /datum/species/monkey)) + return FALSE + return TRUE + + else if(has_AI()) // Now for thoughtcrimes. + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + return AI.is_justified_to_discipline() // Will return true if targeting a non-monkey. + return FALSE diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm index a28fb93e98a..d3b569a113c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm @@ -1,788 +1,788 @@ -// Here are where all the other colors of slime live. -// They will generally fight each other if not Unified, meaning the xenobiologist has to seperate them. - -// Tier 1. - -/mob/living/simple_mob/slime/xenobio/purple - desc = "This slime is rather toxic to handle, as it is poisonous." - color = "#CC23FF" - slime_color = "purple" - coretype = /obj/item/slime_extract/purple - reagent_injected = "toxin" - - description_info = "This slime spreads a toxin when it attacks. A biosuit or other thick armor can protect from the toxic attack." - player_msg = "You inject a harmful toxin when attacking." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/dark_purple, - /mob/living/simple_mob/slime/xenobio/dark_blue, - /mob/living/simple_mob/slime/xenobio/green, - /mob/living/simple_mob/slime/xenobio - ) - -/mob/living/simple_mob/slime/xenobio/orange - desc = "This slime is known to be flammable and can ignite enemies." - color = "#FFA723" - slime_color = "orange" - coretype = /obj/item/slime_extract/orange - melee_damage_lower = 5 - melee_damage_upper = 5 - heat_resist = 1 - - description_info = "The slime is immune to burning attacks, and attacks from this slime will burn you, and can ignite you. \ - A firesuit can protect from the burning attacks of this slime." - player_msg = "You inflict burning attacks, which causes additional damage, makes the target more flammable, and has a chance to ignite them.
                    \ - You are also immune to burning attacks." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/dark_purple, - /mob/living/simple_mob/slime/xenobio/yellow, - /mob/living/simple_mob/slime/xenobio/red, - /mob/living/simple_mob/slime/xenobio - ) - -/mob/living/simple_mob/slime/xenobio/orange/apply_melee_effects(atom/A) - ..() - if(isliving(A)) - var/mob/living/L = A - L.inflict_heat_damage(is_adult ? 10 : 5) - to_chat(src, span("span", "You burn \the [L].")) - to_chat(L, span("danger", "You've been burned by \the [src]!")) - L.adjust_fire_stacks(1) - if(prob(12)) - L.IgniteMob() - -/mob/living/simple_mob/slime/xenobio/blue - desc = "This slime produces 'cryotoxin' and uses it against their foes. Very deadly to other slimes." - color = "#19FFFF" - slime_color = "blue" - coretype = /obj/item/slime_extract/blue - reagent_injected = "cryotoxin" - cold_resist = 0.50 // Not as strong as dark blue, which has immunity. - - description_info = "The slime is resistant to the cold, and attacks from this slime can inject cryotoxin into you. \ - A biosuit or other thick armor can protect from the injection." - player_msg = "You inject cryotoxin on attack, which causes them to get very cold, slowing them down and harming them over time.
                    \ - You are also resistant to cold attacks." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/dark_blue, - /mob/living/simple_mob/slime/xenobio/silver, - /mob/living/simple_mob/slime/xenobio/pink, - /mob/living/simple_mob/slime/xenobio - ) - - -/mob/living/simple_mob/slime/xenobio/metal - desc = "This slime is a lot more resilient than the others, due to having a metamorphic metallic and sloped surface." - color = "#5F5F5F" - slime_color = "metal" - shiny = TRUE - coretype = /obj/item/slime_extract/metal - - description_info = "This slime is a lot more durable and tough to damage than the others. It also seems to provoke others to attack it over others." - player_msg = "You are more resilient and armored than more slimes. Your attacks will also encourage less intelligent enemies to focus on you." - - maxHealth = 250 - maxHealth_adult = 350 - - // The sloped armor. - // It's resistant to most weapons (but a spraybottle still kills it rather fast). - armor = list( - "melee" = 25, - "bullet" = 25, - "laser" = 25, - "energy" = 50, - "bomb" = 80, - "bio" = 100, - "rad" = 100 - ) - - armor_soak = list( - "melee" = 5, - "bullet" = 5, - "laser" = 5, - "energy" = 0, - "bomb" = 0, - "bio" = 0, - "rad" = 0 - ) - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/silver, - /mob/living/simple_mob/slime/xenobio/yellow, - /mob/living/simple_mob/slime/xenobio/gold, - /mob/living/simple_mob/slime/xenobio - ) - -/mob/living/simple_mob/slime/xenobio/metal/apply_melee_effects(atom/A) - ..() - if(isliving(A)) - var/mob/living/L = A - L.taunt(src, TRUE) // We're the party tank now. - -// Tier 2 - -/mob/living/simple_mob/slime/xenobio/yellow - desc = "This slime is very conductive, and is known to use electricity as a means of defense moreso than usual for slimes." - color = "#FFF423" - slime_color = "yellow" - coretype = /obj/item/slime_extract/yellow - melee_damage_lower = 5 - melee_damage_upper = 5 - shock_resist = 1 - - projectiletype = /obj/item/projectile/beam/lightning/slime - projectilesound = 'sound/effects/lightningbolt.ogg' - glow_toggle = TRUE - - description_info = "In addition to being immune to electrical shocks, this slime will fire ranged lightning attacks at \ - enemies if they are at range, inflict shocks upon entities they attack, and generate electricity for their stun \ - attack faster than usual. Insulative or reflective armor can protect from these attacks." - player_msg = "You have a ranged electric attack. You also shock enemies you attack, and your electric stun attack charges passively.
                    \ - You are also immune to shocking attacks." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/bluespace, - /mob/living/simple_mob/slime/xenobio/bluespace, - /mob/living/simple_mob/slime/xenobio/metal, - /mob/living/simple_mob/slime/xenobio/orange - ) - -/mob/living/simple_mob/slime/xenobio/yellow/apply_melee_effects(atom/A) - ..() - if(isliving(A)) - var/mob/living/L = A - L.inflict_shock_damage(is_adult ? 10 : 5) - to_chat(src, span("span", "You shock \the [L].")) - to_chat(L, span("danger", "You've been shocked by \the [src]!")) - -/mob/living/simple_mob/slime/xenobio/yellow/handle_special() - if(stat == CONSCIOUS) - if(prob(25)) - power_charge = between(0, power_charge + 1, 10) - ..() - -/obj/item/projectile/beam/lightning/slime - power = 10 - fire_sound = 'sound/effects/lightningbolt.ogg' - - -/mob/living/simple_mob/slime/xenobio/dark_purple - desc = "This slime produces ever-coveted phoron. Risky to handle but very much worth it." - color = "#660088" - slime_color = "dark purple" - coretype = /obj/item/slime_extract/dark_purple - reagent_injected = "phoron" - - description_info = "This slime applies phoron to enemies it attacks. A biosuit or other thick armor can protect from the toxic attack. \ - If hit with a burning attack, it will erupt in flames." - player_msg = "You inject phoron into enemies you attack.
                    \ - You will erupt into flames if harmed by fire!" - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/purple, - /mob/living/simple_mob/slime/xenobio/orange, - /mob/living/simple_mob/slime/xenobio/ruby, - /mob/living/simple_mob/slime/xenobio/ruby - ) - -/mob/living/simple_mob/slime/xenobio/dark_purple/proc/ignite() - visible_message(span("critical", "\The [src] erupts in an inferno!")) - for(var/turf/simulated/target_turf in view(2, src)) - target_turf.assume_gas("phoron", 30, 1500+T0C) - spawn(0) - target_turf.hotspot_expose(1500+T0C, 400) - qdel(src) - -/mob/living/simple_mob/slime/xenobio/dark_purple/ex_act(severity) - log_and_message_admins("[src] ignited due to a chain reaction with an explosion.") - ignite() - -/mob/living/simple_mob/slime/xenobio/dark_purple/fire_act(datum/gas_mixture/air, temperature, volume) - log_and_message_admins("[src] ignited due to exposure to fire.") - ignite() - -/mob/living/simple_mob/slime/xenobio/dark_purple/bullet_act(var/obj/item/projectile/P, var/def_zone) - if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. - log_and_message_admins("[src] ignited due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") - ignite() - else - ..() - -/mob/living/simple_mob/slime/xenobio/dark_purple/attackby(var/obj/item/weapon/W, var/mob/user) - if(istype(W) && W.force && W.damtype == BURN) - log_and_message_admins("[src] ignited due to being hit with a burning weapon ([W]) by [key_name(user)].") - ignite() - else - ..() - - - -/mob/living/simple_mob/slime/xenobio/dark_blue - desc = "This slime makes other entities near it feel much colder, and is more resilient to the cold. It tends to kill other slimes rather quickly." - color = "#2398FF" - glow_toggle = TRUE - slime_color = "dark blue" - coretype = /obj/item/slime_extract/dark_blue - melee_damage_lower = 5 - melee_damage_upper = 5 - cold_resist = 1 - - description_info = "This slime is immune to the cold, however water will still kill it. Its presense, as well as its attacks, will \ - also cause you additional harm from the cold. A winter coat or other cold-resistant clothing can protect from this." - player_msg = "You are immune to the cold, inflict additional cold damage on attack, and cause nearby entities to suffer from coldness." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/purple, - /mob/living/simple_mob/slime/xenobio/blue, - /mob/living/simple_mob/slime/xenobio/cerulean, - /mob/living/simple_mob/slime/xenobio/cerulean - ) - - minbodytemp = 0 - cold_damage_per_tick = 0 - -/mob/living/simple_mob/slime/xenobio/dark_blue/handle_special() - if(stat != DEAD) - cold_aura() - ..() - -/mob/living/simple_mob/slime/xenobio/dark_blue/proc/cold_aura() - for(var/mob/living/L in view(2, src)) - if(L == src) - continue - chill(L) - - var/turf/T = get_turf(src) - var/datum/gas_mixture/env = T.return_air() - if(env) - env.add_thermal_energy(-10 * 1000) - -/mob/living/simple_mob/slime/xenobio/dark_blue/apply_melee_effects(atom/A) - ..() - if(isliving(A)) - var/mob/living/L = A - chill(L) - to_chat(src, span("span", "You chill \the [L].")) - to_chat(L, span("danger", "You've been chilled by \the [src]!")) - - -/mob/living/simple_mob/slime/xenobio/dark_blue/proc/chill(mob/living/L) - L.inflict_cold_damage(is_adult ? 10 : 5) - if(L.get_cold_protection() < 1 && L.has_AI()) // Harmful auras will make the AI react to its bearer. - L.ai_holder.react_to_attack(src) - - -/mob/living/simple_mob/slime/xenobio/silver - desc = "This slime is shiny, and can deflect lasers or other energy weapons directed at it." - color = "#AAAAAA" - slime_color = "silver" - coretype = /obj/item/slime_extract/silver - shiny = TRUE - - description_info = "Tasers, including the slime version, are ineffective against this slime. The slimebation still works." - player_msg = "You automatically reflect lasers, beams, and tasers that hit you." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/metal, - /mob/living/simple_mob/slime/xenobio/blue, - /mob/living/simple_mob/slime/xenobio/amber, - /mob/living/simple_mob/slime/xenobio/amber - ) - -/mob/living/simple_mob/slime/xenobio/silver/bullet_act(var/obj/item/projectile/P, var/def_zone) - if(istype(P,/obj/item/projectile/beam) || istype(P, /obj/item/projectile/energy)) - visible_message(span("danger", "\The [src] reflects \the [P]!")) - - // Find a turf near or on the original location to bounce to - var/new_x = P.starting.x + pick(0, 0, 0, -1, 1, -2, 2) - var/new_y = P.starting.y + pick(0, 0, 0, -1, 1, -2, 2) - var/turf/curloc = get_turf(src) - - // redirect the projectile - P.redirect(new_x, new_y, curloc, src) - P.reflected = TRUE - return PROJECTILE_CONTINUE // complete projectile permutation - else - ..() - - -// Tier 3 - -/mob/living/simple_mob/slime/xenobio/bluespace - desc = "Trapping this slime in a cell is generally futile, as it can teleport at will." - color = null - slime_color = "bluespace" - icon_state_override = "bluespace" - coretype = /obj/item/slime_extract/bluespace - - description_info = "This slime will teleport to attack something if it is within a range of seven tiles. The teleport has a cooldown of five seconds." - player_msg = "You can teleport at will to a specific tile by clicking on it at range. This has a five second cooldown." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/bluespace, - /mob/living/simple_mob/slime/xenobio/bluespace, - /mob/living/simple_mob/slime/xenobio/yellow, - /mob/living/simple_mob/slime/xenobio/yellow - ) - - special_attack_min_range = 3 - special_attack_max_range = 7 - special_attack_cooldown = 5 SECONDS - -/mob/living/simple_mob/slime/xenobio/bluespace/do_special_attack(atom/A) - // Teleport attack. - if(!A) - to_chat(src, span("warning", "There's nothing to teleport to.")) - return FALSE - - var/list/nearby_things = range(1, A) - var/list/valid_turfs = list() - - // All this work to just go to a non-dense tile. - for(var/turf/potential_turf in nearby_things) - var/valid_turf = TRUE - if(potential_turf.density) - continue - for(var/atom/movable/AM in potential_turf) - if(AM.density) - valid_turf = FALSE - if(valid_turf) - valid_turfs.Add(potential_turf) - - if(!(valid_turfs.len)) - to_chat(src, span("warning", "There wasn't an unoccupied spot to teleport to.")) - return FALSE - - var/turf/target_turf = pick(valid_turfs) - var/turf/T = get_turf(src) - - var/datum/effect/effect/system/spark_spread/s1 = new /datum/effect/effect/system/spark_spread - s1.set_up(5, 1, T) - var/datum/effect/effect/system/spark_spread/s2 = new /datum/effect/effect/system/spark_spread - s2.set_up(5, 1, target_turf) - - - T.visible_message(span("notice", "\The [src] vanishes!")) - s1.start() - - forceMove(target_turf) - playsound(target_turf, 'sound/effects/phasein.ogg', 50, 1) - to_chat(src, span("notice", "You teleport to \the [target_turf].")) - - target_turf.visible_message(span("warning", "\The [src] appears!")) - s2.start() - - if(Adjacent(A)) - attack_target(A) - - -/mob/living/simple_mob/slime/xenobio/ruby - desc = "This slime has great physical strength." - color = "#FF3333" - slime_color = "ruby" - shiny = TRUE - glow_toggle = TRUE - coretype = /obj/item/slime_extract/ruby - - description_info = "This slime is unnaturally stronger, allowing it to hit much harder, take less damage, and be stunned for less time. \ - Their glomp attacks also send the victim flying." - player_msg = "Your attacks knock back the target a fair distance.
                    \ - You also hit harder, take less damage, and stuns affect you for less time." - - melee_attack_delay = 1 SECOND - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/dark_purple, - /mob/living/simple_mob/slime/xenobio/dark_purple, - /mob/living/simple_mob/slime/xenobio/ruby, - /mob/living/simple_mob/slime/xenobio/ruby - ) - -/mob/living/simple_mob/slime/xenobio/ruby/Initialize() - add_modifier(/datum/modifier/slime_strength, null, src) // Slime is always swole. - return ..() - -/mob/living/simple_mob/slime/xenobio/ruby/apply_melee_effects(atom/A) - ..() - - if(isliving(A) && a_intent == I_HURT) - var/mob/living/L = A - if(L.mob_size <= MOB_MEDIUM) - visible_message(span("danger", "\The [src] sends \the [L] flying with the impact!")) - playsound(src, "punch", 50, 1) - L.Weaken(1) - var/throwdir = get_dir(src, L) - L.throw_at(get_edge_target_turf(L, throwdir), 3, 1, src) - else - to_chat(L, span("warning", "\The [src] hits you with incredible force, but you remain in place.")) - - -/mob/living/simple_mob/slime/xenobio/amber - desc = "This slime seems to be an expert in the culinary arts, as they create their own food to share with others. \ - They would probably be very important to other slimes, if the other colors didn't try to kill them." - color = "#FFBB00" - slime_color = "amber" - shiny = TRUE - glow_toggle = TRUE - coretype = /obj/item/slime_extract/amber - - description_info = "This slime feeds nearby entities passively while it is alive. This can cause uncontrollable \ - slime growth and reproduction if not kept in check. The amber slime cannot feed itself, but can be fed by other amber slimes." - player_msg = "You passively provide nutrition to nearby entities." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/silver, - /mob/living/simple_mob/slime/xenobio/silver, - /mob/living/simple_mob/slime/xenobio/amber, - /mob/living/simple_mob/slime/xenobio/amber - ) - -/mob/living/simple_mob/slime/xenobio/amber/handle_special() - if(stat != DEAD) - feed_aura() - ..() - -/mob/living/simple_mob/slime/xenobio/amber/proc/feed_aura() - for(var/mob/living/L in view(1, src)) - if(L.stat == DEAD || !IIsAlly(L)) - continue - if(L == src || istype(L, /mob/living/simple_mob/slime/xenobio/amber)) // Don't feed themselves, or it is impossible to stop infinite slimes without killing all of the ambers. - continue - if(istype(L, /mob/living/simple_mob/slime/xenobio)) - var/mob/living/simple_mob/slime/xenobio/X = L - X.adjust_nutrition(rand(15, 25), 0) - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.isSynthetic()) - continue - H.nutrition = between(0, H.nutrition + rand(15, 25), 800) - -/mob/living/simple_mob/slime/xenobio/cerulean - desc = "This slime is generally superior in a wide range of attributes, compared to the common slime. The jack of all trades, but master of none." - color = "#4F7EAA" - slime_color = "cerulean" - coretype = /obj/item/slime_extract/cerulean - - // Less than the specialized slimes, but higher than the rest. - maxHealth = 200 - maxHealth_adult = 250 - - melee_damage_lower = 10 - melee_damage_upper = 30 - - movement_cooldown = -1 // This actually isn't any faster due to AI limitations that hopefully the timer subsystem can fix in the future. - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/dark_blue, - /mob/living/simple_mob/slime/xenobio/dark_blue, - /mob/living/simple_mob/slime/xenobio/cerulean, - /mob/living/simple_mob/slime/xenobio/cerulean - ) - -// Tier 4 - -/mob/living/simple_mob/slime/xenobio/red - desc = "This slime is full of energy, and very aggressive. 'The red ones go faster.' seems to apply here." - color = "#FF3333" - slime_color = "red" - coretype = /obj/item/slime_extract/red - movement_cooldown = -1 // See above. - untamable = TRUE // Will enrage if disciplined. - - description_info = "This slime is faster than the others. Attempting to discipline this slime will always cause it to go rabid and berserk." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/red, - /mob/living/simple_mob/slime/xenobio/oil, - /mob/living/simple_mob/slime/xenobio/oil, - /mob/living/simple_mob/slime/xenobio/orange - ) - - ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime - - -/mob/living/simple_mob/slime/xenobio/green - desc = "This slime is radioactive." - color = "#14FF20" - slime_color = "green" - coretype = /obj/item/slime_extract/green - glow_toggle = TRUE - reagent_injected = "radium" - var/rads = 25 - - description_info = "This slime will irradiate anything nearby passively, and will inject radium on attack. \ - A radsuit or other thick and radiation-hardened armor can protect from this. It will only radiate while alive." - player_msg = "You passively irradiate your surroundings.
                    \ - You also inject radium on attack." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/purple, - /mob/living/simple_mob/slime/xenobio/green, - /mob/living/simple_mob/slime/xenobio/emerald, - /mob/living/simple_mob/slime/xenobio/emerald - ) - -/mob/living/simple_mob/slime/xenobio/green/handle_special() - if(stat != DEAD) - irradiate() - ..() - -/mob/living/simple_mob/slime/xenobio/green/proc/irradiate() - SSradiation.radiate(src, rads) - - - -/mob/living/simple_mob/slime/xenobio/pink - desc = "This slime has regenerative properties." - color = "#FF0080" - slime_color = "pink" - coretype = /obj/item/slime_extract/pink - glow_toggle = TRUE - - description_info = "This slime will passively heal nearby entities within two tiles, including itself. It will only do this while alive." - player_msg = "You passively heal yourself and nearby allies." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/blue, - /mob/living/simple_mob/slime/xenobio/light_pink, - /mob/living/simple_mob/slime/xenobio/light_pink, - /mob/living/simple_mob/slime/xenobio/pink - ) - -/mob/living/simple_mob/slime/xenobio/pink/handle_special() - if(stat != DEAD) - heal_aura() - ..() - -/mob/living/simple_mob/slime/xenobio/pink/proc/heal_aura() - for(var/mob/living/L in view(src, 2)) - if(L.stat == DEAD || !IIsAlly(L)) - continue - L.add_modifier(/datum/modifier/aura/slime_heal, null, src) - -/datum/modifier/aura/slime_heal - name = "slime mending" - desc = "You feel somewhat gooey." - mob_overlay_state = "pink_sparkles" - stacks = MODIFIER_STACK_FORBID - aura_max_distance = 2 - - on_created_text = "Twinkling spores of goo surround you. It makes you feel healthier." - on_expired_text = "The spores of goo have faded, although you feel much healthier than before." - -/datum/modifier/aura/slime_heal/tick() - if(holder.stat == DEAD) - expire() - - if(ishuman(holder)) // Robolimbs need this code sadly. - var/mob/living/carbon/human/H = holder - for(var/obj/item/organ/external/E in H.organs) - var/obj/item/organ/external/O = E - O.heal_damage(2, 2, 0, 1) - else - holder.adjustBruteLoss(-2) - holder.adjustFireLoss(-2) - - holder.adjustToxLoss(-2) - holder.adjustOxyLoss(-2) - holder.adjustCloneLoss(-1) - - -/mob/living/simple_mob/slime/xenobio/gold - desc = "This slime absorbs energy, and cannot be stunned by normal means." - color = "#EEAA00" - shiny = TRUE - slime_color = "gold" - coretype = /obj/item/slime_extract/gold - description_info = "This slime is immune to the slimebaton and taser, and will actually charge the slime, however it will still discipline the slime." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/metal, - /mob/living/simple_mob/slime/xenobio/gold, - /mob/living/simple_mob/slime/xenobio/sapphire, - /mob/living/simple_mob/slime/xenobio/sapphire - ) - -/mob/living/simple_mob/slime/xenobio/gold/slimebatoned(mob/living/user, amount) - adjust_discipline(round(amount/2)) - power_charge = between(0, power_charge + amount, 10) - -/mob/living/simple_mob/slime/xenobio/gold/get_description_interaction() // So it doesn't say to use a baton on them. - return list() - - -// Tier 5 - -/mob/living/simple_mob/slime/xenobio/oil - desc = "This slime is explosive and volatile. Smoking near it is probably a bad idea." - color = "#333333" - slime_color = "oil" - shiny = TRUE - coretype = /obj/item/slime_extract/oil - - description_info = "If this slime suffers damage from a fire or heat based source, or if it is caught inside \ - an explosion, it will explode. Oil slimes will also suicide-bomb themselves when fighting something that is not a monkey or slime." - player_msg = "You will explode if struck by a burning attack, or if you hit an enemy with a melee attack that is not a monkey or another slime." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/oil, - /mob/living/simple_mob/slime/xenobio/oil, - /mob/living/simple_mob/slime/xenobio/red, - /mob/living/simple_mob/slime/xenobio/red - ) - -/mob/living/simple_mob/slime/xenobio/oil/proc/explode() - if(stat != DEAD) - explosion(src.loc, 0, 2, 4) // A bit weaker since the suicide charger tended to gib the poor sod being targeted. - if(src) // Delete ourselves if the explosion didn't do it. - qdel(src) - -/mob/living/simple_mob/slime/xenobio/oil/apply_melee_effects(atom/A) - if(isliving(A)) - var/mob/living/L = A - if(ishuman(L)) - var/mob/living/carbon/human/H = A - if(istype(H.species, /datum/species/monkey)) - return ..()// Don't blow up when just eatting monkeys. - - else if(isslime(L)) - return ..() - - // Otherwise blow ourselves up. - say(pick("Sacrifice...!", "Sssss...", "Boom...!")) - set_AI_busy(TRUE) - sleep(2 SECONDS) - log_and_message_admins("[src] has suicide-bombed themselves while trying to kill \the [L].") - explode() - - return ..() - -/mob/living/simple_mob/slime/xenobio/oil/ex_act(severity) - log_and_message_admins("[src] exploded due to a chain reaction with another explosion.") - explode() - -/mob/living/simple_mob/slime/xenobio/oil/fire_act(datum/gas_mixture/air, temperature, volume) - log_and_message_admins("[src] exploded due to exposure to fire.") - explode() - -/mob/living/simple_mob/slime/xenobio/oil/bullet_act(obj/item/projectile/P, def_zone) - if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. - log_and_message_admins("[src] exploded due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") - explode() - else - ..() - -/mob/living/simple_mob/slime/xenobio/oil/attackby(obj/item/weapon/W, mob/living/user) - if(istype(W) && W.force && W.damtype == BURN) - log_and_message_admins("[src] exploded due to being hit with a burning weapon ([W]) by [key_name(user)].") - explode() - else - ..() - - -/mob/living/simple_mob/slime/xenobio/sapphire - desc = "This slime seems a bit brighter than the rest, both figuratively and literally." - color = "#2398FF" - slime_color = "sapphire" - shiny = TRUE - glow_toggle = TRUE - coretype = /obj/item/slime_extract/sapphire - ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/sapphire - - description_info = "This slime uses more robust tactics when fighting and won't hold back, so it is dangerous to be alone \ - with one if hostile, and especially dangerous if they outnumber you." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/sapphire, - /mob/living/simple_mob/slime/xenobio/sapphire, - /mob/living/simple_mob/slime/xenobio/gold, - /mob/living/simple_mob/slime/xenobio/gold - ) - - -/mob/living/simple_mob/slime/xenobio/emerald - desc = "This slime is faster than usual, even more so than the red slimes." - color = "#22FF22" - shiny = TRUE - glow_toggle = TRUE - slime_color = "emerald" - coretype = /obj/item/slime_extract/emerald - - description_info = "This slime will make everything around it, and itself, faster for a few seconds, if close by." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/green, - /mob/living/simple_mob/slime/xenobio/green, - /mob/living/simple_mob/slime/xenobio/emerald, - /mob/living/simple_mob/slime/xenobio/emerald - ) - -/mob/living/simple_mob/slime/xenobio/emerald/handle_special() - if(stat != DEAD) - zoom_aura() - ..() - -/mob/living/simple_mob/slime/xenobio/emerald/proc/zoom_aura() - for(var/mob/living/L in view(src, 2)) - if(L.stat == DEAD || !IIsAlly(L)) - continue - L.add_modifier(/datum/modifier/technomancer/haste, 5 SECONDS, src) - - -/mob/living/simple_mob/slime/xenobio/light_pink - desc = "This slime seems a lot more peaceful than the others." - color = "#FF8888" - slime_color = "light pink" - coretype = /obj/item/slime_extract/light_pink - - description_info = "This slime is effectively always disciplined initially." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/pink, - /mob/living/simple_mob/slime/xenobio/pink, - /mob/living/simple_mob/slime/xenobio/light_pink, - /mob/living/simple_mob/slime/xenobio/light_pink - ) - - ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/light_pink - -// Special -/mob/living/simple_mob/slime/xenobio/rainbow - desc = "This slime changes colors constantly." - color = null // Uses a special icon_state. - slime_color = "rainbow" - coretype = /obj/item/slime_extract/rainbow - icon_state_override = "rainbow" - unity = TRUE - - description_info = "This slime is considered to be the same color as all other slime colors at the same time for the purposes of \ - other slimes being friendly to them, and therefore will never be harmed by another slime. \ - Attacking this slime will provoke the wrath of all slimes within range." - player_msg = "You are considered to be the same color as every slime, \ - meaning that you are considered an ally to all slimes." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/rainbow, - /mob/living/simple_mob/slime/xenobio/rainbow, - /mob/living/simple_mob/slime/xenobio/rainbow, - /mob/living/simple_mob/slime/xenobio/rainbow - ) - -/mob/living/simple_mob/slime/xenobio/rainbow/Initialize() - unify() - return ..() - -// The RD's pet slime. -/mob/living/simple_mob/slime/xenobio/rainbow/kendrick - name = "Kendrick" - desc = "The Research Director's pet slime. It shifts colors constantly." - rainbow_core_candidate = FALSE - // Doing pacify() in initialize() won't actually pacify the AI due to the ai_holder not existing due to parent initialize() not being called yet. - // Instead lets just give them an ai_holder that does that for us. - ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/passive - -/mob/living/simple_mob/slime/xenobio/rainbow/kendrick/Initialize() - pacify() // So the physical mob also gets made harmless. - return ..() +// Here are where all the other colors of slime live. +// They will generally fight each other if not Unified, meaning the xenobiologist has to seperate them. + +// Tier 1. + +/mob/living/simple_mob/slime/xenobio/purple + desc = "This slime is rather toxic to handle, as it is poisonous." + color = "#CC23FF" + slime_color = "purple" + coretype = /obj/item/slime_extract/purple + reagent_injected = "toxin" + + description_info = "This slime spreads a toxin when it attacks. A biosuit or other thick armor can protect from the toxic attack." + player_msg = "You inject a harmful toxin when attacking." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio + ) + +/mob/living/simple_mob/slime/xenobio/orange + desc = "This slime is known to be flammable and can ignite enemies." + color = "#FFA723" + slime_color = "orange" + coretype = /obj/item/slime_extract/orange + melee_damage_lower = 5 + melee_damage_upper = 5 + heat_resist = 1 + + description_info = "The slime is immune to burning attacks, and attacks from this slime will burn you, and can ignite you. \ + A firesuit can protect from the burning attacks of this slime." + player_msg = "You inflict burning attacks, which causes additional damage, makes the target more flammable, and has a chance to ignite them.
                    \ + You are also immune to burning attacks." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/yellow, + /mob/living/simple_mob/slime/xenobio/red, + /mob/living/simple_mob/slime/xenobio + ) + +/mob/living/simple_mob/slime/xenobio/orange/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + L.inflict_heat_damage(is_adult ? 10 : 5) + to_chat(src, span("span", "You burn \the [L].")) + to_chat(L, span("danger", "You've been burned by \the [src]!")) + L.adjust_fire_stacks(1) + if(prob(12)) + L.IgniteMob() + +/mob/living/simple_mob/slime/xenobio/blue + desc = "This slime produces 'cryotoxin' and uses it against their foes. Very deadly to other slimes." + color = "#19FFFF" + slime_color = "blue" + coretype = /obj/item/slime_extract/blue + reagent_injected = "cryotoxin" + cold_resist = 0.50 // Not as strong as dark blue, which has immunity. + + description_info = "The slime is resistant to the cold, and attacks from this slime can inject cryotoxin into you. \ + A biosuit or other thick armor can protect from the injection." + player_msg = "You inject cryotoxin on attack, which causes them to get very cold, slowing them down and harming them over time.
                    \ + You are also resistant to cold attacks." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/pink, + /mob/living/simple_mob/slime/xenobio + ) + + +/mob/living/simple_mob/slime/xenobio/metal + desc = "This slime is a lot more resilient than the others, due to having a metamorphic metallic and sloped surface." + color = "#5F5F5F" + slime_color = "metal" + shiny = TRUE + coretype = /obj/item/slime_extract/metal + + description_info = "This slime is a lot more durable and tough to damage than the others. It also seems to provoke others to attack it over others." + player_msg = "You are more resilient and armored than more slimes. Your attacks will also encourage less intelligent enemies to focus on you." + + maxHealth = 250 + maxHealth_adult = 350 + + // The sloped armor. + // It's resistant to most weapons (but a spraybottle still kills it rather fast). + armor = list( + "melee" = 25, + "bullet" = 25, + "laser" = 25, + "energy" = 50, + "bomb" = 80, + "bio" = 100, + "rad" = 100 + ) + + armor_soak = list( + "melee" = 5, + "bullet" = 5, + "laser" = 5, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/yellow, + /mob/living/simple_mob/slime/xenobio/gold, + /mob/living/simple_mob/slime/xenobio + ) + +/mob/living/simple_mob/slime/xenobio/metal/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + L.taunt(src, TRUE) // We're the party tank now. + +// Tier 2 + +/mob/living/simple_mob/slime/xenobio/yellow + desc = "This slime is very conductive, and is known to use electricity as a means of defense moreso than usual for slimes." + color = "#FFF423" + slime_color = "yellow" + coretype = /obj/item/slime_extract/yellow + melee_damage_lower = 5 + melee_damage_upper = 5 + shock_resist = 1 + + projectiletype = /obj/item/projectile/beam/lightning/slime + projectilesound = 'sound/effects/lightningbolt.ogg' + glow_toggle = TRUE + + description_info = "In addition to being immune to electrical shocks, this slime will fire ranged lightning attacks at \ + enemies if they are at range, inflict shocks upon entities they attack, and generate electricity for their stun \ + attack faster than usual. Insulative or reflective armor can protect from these attacks." + player_msg = "You have a ranged electric attack. You also shock enemies you attack, and your electric stun attack charges passively.
                    \ + You are also immune to shocking attacks." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/metal, + /mob/living/simple_mob/slime/xenobio/orange + ) + +/mob/living/simple_mob/slime/xenobio/yellow/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + L.inflict_shock_damage(is_adult ? 10 : 5) + to_chat(src, span("span", "You shock \the [L].")) + to_chat(L, span("danger", "You've been shocked by \the [src]!")) + +/mob/living/simple_mob/slime/xenobio/yellow/handle_special() + if(stat == CONSCIOUS) + if(prob(25)) + power_charge = between(0, power_charge + 1, 10) + ..() + +/obj/item/projectile/beam/lightning/slime + power = 10 + fire_sound = 'sound/effects/lightningbolt.ogg' + + +/mob/living/simple_mob/slime/xenobio/dark_purple + desc = "This slime produces ever-coveted phoron. Risky to handle but very much worth it." + color = "#660088" + slime_color = "dark purple" + coretype = /obj/item/slime_extract/dark_purple + reagent_injected = "phoron" + + description_info = "This slime applies phoron to enemies it attacks. A biosuit or other thick armor can protect from the toxic attack. \ + If hit with a burning attack, it will erupt in flames." + player_msg = "You inject phoron into enemies you attack.
                    \ + You will erupt into flames if harmed by fire!" + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/purple, + /mob/living/simple_mob/slime/xenobio/orange, + /mob/living/simple_mob/slime/xenobio/ruby, + /mob/living/simple_mob/slime/xenobio/ruby + ) + +/mob/living/simple_mob/slime/xenobio/dark_purple/proc/ignite() + visible_message(span("critical", "\The [src] erupts in an inferno!")) + for(var/turf/simulated/target_turf in view(2, src)) + target_turf.assume_gas("phoron", 30, 1500+T0C) + spawn(0) + target_turf.hotspot_expose(1500+T0C, 400) + qdel(src) + +/mob/living/simple_mob/slime/xenobio/dark_purple/ex_act(severity) + log_and_message_admins("[src] ignited due to a chain reaction with an explosion.") + ignite() + +/mob/living/simple_mob/slime/xenobio/dark_purple/fire_act(datum/gas_mixture/air, temperature, volume) + log_and_message_admins("[src] ignited due to exposure to fire.") + ignite() + +/mob/living/simple_mob/slime/xenobio/dark_purple/bullet_act(var/obj/item/projectile/P, var/def_zone) + if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. + log_and_message_admins("[src] ignited due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") + ignite() + else + ..() + +/mob/living/simple_mob/slime/xenobio/dark_purple/attackby(var/obj/item/weapon/W, var/mob/user) + if(istype(W) && W.force && W.damtype == BURN) + log_and_message_admins("[src] ignited due to being hit with a burning weapon ([W]) by [key_name(user)].") + ignite() + else + ..() + + + +/mob/living/simple_mob/slime/xenobio/dark_blue + desc = "This slime makes other entities near it feel much colder, and is more resilient to the cold. It tends to kill other slimes rather quickly." + color = "#2398FF" + glow_toggle = TRUE + slime_color = "dark blue" + coretype = /obj/item/slime_extract/dark_blue + melee_damage_lower = 5 + melee_damage_upper = 5 + cold_resist = 1 + + description_info = "This slime is immune to the cold, however water will still kill it. Its presense, as well as its attacks, will \ + also cause you additional harm from the cold. A winter coat or other cold-resistant clothing can protect from this." + player_msg = "You are immune to the cold, inflict additional cold damage on attack, and cause nearby entities to suffer from coldness." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/purple, + /mob/living/simple_mob/slime/xenobio/blue, + /mob/living/simple_mob/slime/xenobio/cerulean, + /mob/living/simple_mob/slime/xenobio/cerulean + ) + + minbodytemp = 0 + cold_damage_per_tick = 0 + +/mob/living/simple_mob/slime/xenobio/dark_blue/handle_special() + if(stat != DEAD) + cold_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/dark_blue/proc/cold_aura() + for(var/mob/living/L in view(2, src)) + if(L == src) + continue + chill(L) + + var/turf/T = get_turf(src) + var/datum/gas_mixture/env = T.return_air() + if(env) + env.add_thermal_energy(-10 * 1000) + +/mob/living/simple_mob/slime/xenobio/dark_blue/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + chill(L) + to_chat(src, span("span", "You chill \the [L].")) + to_chat(L, span("danger", "You've been chilled by \the [src]!")) + + +/mob/living/simple_mob/slime/xenobio/dark_blue/proc/chill(mob/living/L) + L.inflict_cold_damage(is_adult ? 10 : 5) + if(L.get_cold_protection() < 1 && L.has_AI()) // Harmful auras will make the AI react to its bearer. + L.ai_holder.react_to_attack(src) + + +/mob/living/simple_mob/slime/xenobio/silver + desc = "This slime is shiny, and can deflect lasers or other energy weapons directed at it." + color = "#AAAAAA" + slime_color = "silver" + coretype = /obj/item/slime_extract/silver + shiny = TRUE + + description_info = "Tasers, including the slime version, are ineffective against this slime. The slimebation still works." + player_msg = "You automatically reflect lasers, beams, and tasers that hit you." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/metal, + /mob/living/simple_mob/slime/xenobio/blue, + /mob/living/simple_mob/slime/xenobio/amber, + /mob/living/simple_mob/slime/xenobio/amber + ) + +/mob/living/simple_mob/slime/xenobio/silver/bullet_act(var/obj/item/projectile/P, var/def_zone) + if(istype(P,/obj/item/projectile/beam) || istype(P, /obj/item/projectile/energy)) + visible_message(span("danger", "\The [src] reflects \the [P]!")) + + // Find a turf near or on the original location to bounce to + var/new_x = P.starting.x + pick(0, 0, 0, -1, 1, -2, 2) + var/new_y = P.starting.y + pick(0, 0, 0, -1, 1, -2, 2) + var/turf/curloc = get_turf(src) + + // redirect the projectile + P.redirect(new_x, new_y, curloc, src) + P.reflected = TRUE + return PROJECTILE_CONTINUE // complete projectile permutation + else + ..() + + +// Tier 3 + +/mob/living/simple_mob/slime/xenobio/bluespace + desc = "Trapping this slime in a cell is generally futile, as it can teleport at will." + color = null + slime_color = "bluespace" + icon_state_override = "bluespace" + coretype = /obj/item/slime_extract/bluespace + + description_info = "This slime will teleport to attack something if it is within a range of seven tiles. The teleport has a cooldown of five seconds." + player_msg = "You can teleport at will to a specific tile by clicking on it at range. This has a five second cooldown." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/yellow, + /mob/living/simple_mob/slime/xenobio/yellow + ) + + special_attack_min_range = 3 + special_attack_max_range = 7 + special_attack_cooldown = 5 SECONDS + +/mob/living/simple_mob/slime/xenobio/bluespace/do_special_attack(atom/A) + // Teleport attack. + if(!A) + to_chat(src, span("warning", "There's nothing to teleport to.")) + return FALSE + + var/list/nearby_things = range(1, A) + var/list/valid_turfs = list() + + // All this work to just go to a non-dense tile. + for(var/turf/potential_turf in nearby_things) + var/valid_turf = TRUE + if(potential_turf.density) + continue + for(var/atom/movable/AM in potential_turf) + if(AM.density) + valid_turf = FALSE + if(valid_turf) + valid_turfs.Add(potential_turf) + + if(!(valid_turfs.len)) + to_chat(src, span("warning", "There wasn't an unoccupied spot to teleport to.")) + return FALSE + + var/turf/target_turf = pick(valid_turfs) + var/turf/T = get_turf(src) + + var/datum/effect/effect/system/spark_spread/s1 = new /datum/effect/effect/system/spark_spread + s1.set_up(5, 1, T) + var/datum/effect/effect/system/spark_spread/s2 = new /datum/effect/effect/system/spark_spread + s2.set_up(5, 1, target_turf) + + + T.visible_message(span("notice", "\The [src] vanishes!")) + s1.start() + + forceMove(target_turf) + playsound(target_turf, 'sound/effects/phasein.ogg', 50, 1) + to_chat(src, span("notice", "You teleport to \the [target_turf].")) + + target_turf.visible_message(span("warning", "\The [src] appears!")) + s2.start() + + if(Adjacent(A)) + attack_target(A) + + +/mob/living/simple_mob/slime/xenobio/ruby + desc = "This slime has great physical strength." + color = "#FF3333" + slime_color = "ruby" + shiny = TRUE + glow_toggle = TRUE + coretype = /obj/item/slime_extract/ruby + + description_info = "This slime is unnaturally stronger, allowing it to hit much harder, take less damage, and be stunned for less time. \ + Their glomp attacks also send the victim flying." + player_msg = "Your attacks knock back the target a fair distance.
                    \ + You also hit harder, take less damage, and stuns affect you for less time." + + melee_attack_delay = 1 SECOND + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/ruby, + /mob/living/simple_mob/slime/xenobio/ruby + ) + +/mob/living/simple_mob/slime/xenobio/ruby/Initialize() + add_modifier(/datum/modifier/slime_strength, null, src) // Slime is always swole. + return ..() + +/mob/living/simple_mob/slime/xenobio/ruby/apply_melee_effects(atom/A) + ..() + + if(isliving(A) && a_intent == I_HURT) + var/mob/living/L = A + if(L.mob_size <= MOB_MEDIUM) + visible_message(span("danger", "\The [src] sends \the [L] flying with the impact!")) + playsound(src, "punch", 50, 1) + L.Weaken(1) + var/throwdir = get_dir(src, L) + L.throw_at(get_edge_target_turf(L, throwdir), 3, 1, src) + else + to_chat(L, span("warning", "\The [src] hits you with incredible force, but you remain in place.")) + + +/mob/living/simple_mob/slime/xenobio/amber + desc = "This slime seems to be an expert in the culinary arts, as they create their own food to share with others. \ + They would probably be very important to other slimes, if the other colors didn't try to kill them." + color = "#FFBB00" + slime_color = "amber" + shiny = TRUE + glow_toggle = TRUE + coretype = /obj/item/slime_extract/amber + + description_info = "This slime feeds nearby entities passively while it is alive. This can cause uncontrollable \ + slime growth and reproduction if not kept in check. The amber slime cannot feed itself, but can be fed by other amber slimes." + player_msg = "You passively provide nutrition to nearby entities." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/amber, + /mob/living/simple_mob/slime/xenobio/amber + ) + +/mob/living/simple_mob/slime/xenobio/amber/handle_special() + if(stat != DEAD) + feed_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/amber/proc/feed_aura() + for(var/mob/living/L in view(1, src)) + if(L.stat == DEAD || !IIsAlly(L)) + continue + if(L == src || istype(L, /mob/living/simple_mob/slime/xenobio/amber)) // Don't feed themselves, or it is impossible to stop infinite slimes without killing all of the ambers. + continue + if(istype(L, /mob/living/simple_mob/slime/xenobio)) + var/mob/living/simple_mob/slime/xenobio/X = L + X.adjust_nutrition(rand(15, 25), 0) + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.isSynthetic()) + continue + H.nutrition = between(0, H.nutrition + rand(15, 25), 800) + +/mob/living/simple_mob/slime/xenobio/cerulean + desc = "This slime is generally superior in a wide range of attributes, compared to the common slime. The jack of all trades, but master of none." + color = "#4F7EAA" + slime_color = "cerulean" + coretype = /obj/item/slime_extract/cerulean + + // Less than the specialized slimes, but higher than the rest. + maxHealth = 200 + maxHealth_adult = 250 + + melee_damage_lower = 10 + melee_damage_upper = 30 + + movement_cooldown = -1 // This actually isn't any faster due to AI limitations that hopefully the timer subsystem can fix in the future. + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/cerulean, + /mob/living/simple_mob/slime/xenobio/cerulean + ) + +// Tier 4 + +/mob/living/simple_mob/slime/xenobio/red + desc = "This slime is full of energy, and very aggressive. 'The red ones go faster.' seems to apply here." + color = "#FF3333" + slime_color = "red" + coretype = /obj/item/slime_extract/red + movement_cooldown = -1 // See above. + untamable = TRUE // Will enrage if disciplined. + + description_info = "This slime is faster than the others. Attempting to discipline this slime will always cause it to go rabid and berserk." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/red, + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/orange + ) + + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime + + +/mob/living/simple_mob/slime/xenobio/green + desc = "This slime is radioactive." + color = "#14FF20" + slime_color = "green" + coretype = /obj/item/slime_extract/green + glow_toggle = TRUE + reagent_injected = "radium" + var/rads = 25 + + description_info = "This slime will irradiate anything nearby passively, and will inject radium on attack. \ + A radsuit or other thick and radiation-hardened armor can protect from this. It will only radiate while alive." + player_msg = "You passively irradiate your surroundings.
                    \ + You also inject radium on attack." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/purple, + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio/emerald, + /mob/living/simple_mob/slime/xenobio/emerald + ) + +/mob/living/simple_mob/slime/xenobio/green/handle_special() + if(stat != DEAD) + irradiate() + ..() + +/mob/living/simple_mob/slime/xenobio/green/proc/irradiate() + SSradiation.radiate(src, rads) + + + +/mob/living/simple_mob/slime/xenobio/pink + desc = "This slime has regenerative properties." + color = "#FF0080" + slime_color = "pink" + coretype = /obj/item/slime_extract/pink + glow_toggle = TRUE + + description_info = "This slime will passively heal nearby entities within two tiles, including itself. It will only do this while alive." + player_msg = "You passively heal yourself and nearby allies." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/blue, + /mob/living/simple_mob/slime/xenobio/light_pink, + /mob/living/simple_mob/slime/xenobio/light_pink, + /mob/living/simple_mob/slime/xenobio/pink + ) + +/mob/living/simple_mob/slime/xenobio/pink/handle_special() + if(stat != DEAD) + heal_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/pink/proc/heal_aura() + for(var/mob/living/L in view(src, 2)) + if(L.stat == DEAD || !IIsAlly(L)) + continue + L.add_modifier(/datum/modifier/aura/slime_heal, null, src) + +/datum/modifier/aura/slime_heal + name = "slime mending" + desc = "You feel somewhat gooey." + mob_overlay_state = "pink_sparkles" + stacks = MODIFIER_STACK_FORBID + aura_max_distance = 2 + + on_created_text = "Twinkling spores of goo surround you. It makes you feel healthier." + on_expired_text = "The spores of goo have faded, although you feel much healthier than before." + +/datum/modifier/aura/slime_heal/tick() + if(holder.stat == DEAD) + expire() + + if(ishuman(holder)) // Robolimbs need this code sadly. + var/mob/living/carbon/human/H = holder + for(var/obj/item/organ/external/E in H.organs) + var/obj/item/organ/external/O = E + O.heal_damage(2, 2, 0, 1) + else + holder.adjustBruteLoss(-2) + holder.adjustFireLoss(-2) + + holder.adjustToxLoss(-2) + holder.adjustOxyLoss(-2) + holder.adjustCloneLoss(-1) + + +/mob/living/simple_mob/slime/xenobio/gold + desc = "This slime absorbs energy, and cannot be stunned by normal means." + color = "#EEAA00" + shiny = TRUE + slime_color = "gold" + coretype = /obj/item/slime_extract/gold + description_info = "This slime is immune to the slimebaton and taser, and will actually charge the slime, however it will still discipline the slime." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/metal, + /mob/living/simple_mob/slime/xenobio/gold, + /mob/living/simple_mob/slime/xenobio/sapphire, + /mob/living/simple_mob/slime/xenobio/sapphire + ) + +/mob/living/simple_mob/slime/xenobio/gold/slimebatoned(mob/living/user, amount) + adjust_discipline(round(amount/2)) + power_charge = between(0, power_charge + amount, 10) + +/mob/living/simple_mob/slime/xenobio/gold/get_description_interaction() // So it doesn't say to use a baton on them. + return list() + + +// Tier 5 + +/mob/living/simple_mob/slime/xenobio/oil + desc = "This slime is explosive and volatile. Smoking near it is probably a bad idea." + color = "#333333" + slime_color = "oil" + shiny = TRUE + coretype = /obj/item/slime_extract/oil + + description_info = "If this slime suffers damage from a fire or heat based source, or if it is caught inside \ + an explosion, it will explode. Oil slimes will also suicide-bomb themselves when fighting something that is not a monkey or slime." + player_msg = "You will explode if struck by a burning attack, or if you hit an enemy with a melee attack that is not a monkey or another slime." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/red, + /mob/living/simple_mob/slime/xenobio/red + ) + +/mob/living/simple_mob/slime/xenobio/oil/proc/explode() + if(stat != DEAD) + explosion(src.loc, 0, 2, 4) // A bit weaker since the suicide charger tended to gib the poor sod being targeted. + if(src) // Delete ourselves if the explosion didn't do it. + qdel(src) + +/mob/living/simple_mob/slime/xenobio/oil/apply_melee_effects(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(ishuman(L)) + var/mob/living/carbon/human/H = A + if(istype(H.species, /datum/species/monkey)) + return ..()// Don't blow up when just eatting monkeys. + + else if(isslime(L)) + return ..() + + // Otherwise blow ourselves up. + say(pick("Sacrifice...!", "Sssss...", "Boom...!")) + set_AI_busy(TRUE) + sleep(2 SECONDS) + log_and_message_admins("[src] has suicide-bombed themselves while trying to kill \the [L].") + explode() + + return ..() + +/mob/living/simple_mob/slime/xenobio/oil/ex_act(severity) + log_and_message_admins("[src] exploded due to a chain reaction with another explosion.") + explode() + +/mob/living/simple_mob/slime/xenobio/oil/fire_act(datum/gas_mixture/air, temperature, volume) + log_and_message_admins("[src] exploded due to exposure to fire.") + explode() + +/mob/living/simple_mob/slime/xenobio/oil/bullet_act(obj/item/projectile/P, def_zone) + if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. + log_and_message_admins("[src] exploded due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") + explode() + else + ..() + +/mob/living/simple_mob/slime/xenobio/oil/attackby(obj/item/weapon/W, mob/living/user) + if(istype(W) && W.force && W.damtype == BURN) + log_and_message_admins("[src] exploded due to being hit with a burning weapon ([W]) by [key_name(user)].") + explode() + else + ..() + + +/mob/living/simple_mob/slime/xenobio/sapphire + desc = "This slime seems a bit brighter than the rest, both figuratively and literally." + color = "#2398FF" + slime_color = "sapphire" + shiny = TRUE + glow_toggle = TRUE + coretype = /obj/item/slime_extract/sapphire + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/sapphire + + description_info = "This slime uses more robust tactics when fighting and won't hold back, so it is dangerous to be alone \ + with one if hostile, and especially dangerous if they outnumber you." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/sapphire, + /mob/living/simple_mob/slime/xenobio/sapphire, + /mob/living/simple_mob/slime/xenobio/gold, + /mob/living/simple_mob/slime/xenobio/gold + ) + + +/mob/living/simple_mob/slime/xenobio/emerald + desc = "This slime is faster than usual, even more so than the red slimes." + color = "#22FF22" + shiny = TRUE + glow_toggle = TRUE + slime_color = "emerald" + coretype = /obj/item/slime_extract/emerald + + description_info = "This slime will make everything around it, and itself, faster for a few seconds, if close by." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio/emerald, + /mob/living/simple_mob/slime/xenobio/emerald + ) + +/mob/living/simple_mob/slime/xenobio/emerald/handle_special() + if(stat != DEAD) + zoom_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/emerald/proc/zoom_aura() + for(var/mob/living/L in view(src, 2)) + if(L.stat == DEAD || !IIsAlly(L)) + continue + L.add_modifier(/datum/modifier/technomancer/haste, 5 SECONDS, src) + + +/mob/living/simple_mob/slime/xenobio/light_pink + desc = "This slime seems a lot more peaceful than the others." + color = "#FF8888" + slime_color = "light pink" + coretype = /obj/item/slime_extract/light_pink + + description_info = "This slime is effectively always disciplined initially." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/pink, + /mob/living/simple_mob/slime/xenobio/pink, + /mob/living/simple_mob/slime/xenobio/light_pink, + /mob/living/simple_mob/slime/xenobio/light_pink + ) + + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/light_pink + +// Special +/mob/living/simple_mob/slime/xenobio/rainbow + desc = "This slime changes colors constantly." + color = null // Uses a special icon_state. + slime_color = "rainbow" + coretype = /obj/item/slime_extract/rainbow + icon_state_override = "rainbow" + unity = TRUE + + description_info = "This slime is considered to be the same color as all other slime colors at the same time for the purposes of \ + other slimes being friendly to them, and therefore will never be harmed by another slime. \ + Attacking this slime will provoke the wrath of all slimes within range." + player_msg = "You are considered to be the same color as every slime, \ + meaning that you are considered an ally to all slimes." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/rainbow, + /mob/living/simple_mob/slime/xenobio/rainbow, + /mob/living/simple_mob/slime/xenobio/rainbow, + /mob/living/simple_mob/slime/xenobio/rainbow + ) + +/mob/living/simple_mob/slime/xenobio/rainbow/Initialize() + unify() + return ..() + +// The RD's pet slime. +/mob/living/simple_mob/slime/xenobio/rainbow/kendrick + name = "Kendrick" + desc = "The Research Director's pet slime. It shifts colors constantly." + rainbow_core_candidate = FALSE + // Doing pacify() in initialize() won't actually pacify the AI due to the ai_holder not existing due to parent initialize() not being called yet. + // Instead lets just give them an ai_holder that does that for us. + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/passive + +/mob/living/simple_mob/slime/xenobio/rainbow/kendrick/Initialize() + pacify() // So the physical mob also gets made harmless. + return ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm index 4730c3ae4b8..d4e96c23d5d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm @@ -1,312 +1,312 @@ -// These slimes have the mechanics xenobiologists care about, such as reproduction, mutating into new colors, and being able to submit through fear. - -/mob/living/simple_mob/slime/xenobio - desc = "The most basic of slimes. The grey slime has no remarkable qualities, however it remains one of the most useful colors for scientists." - layer = MOB_LAYER + 1 // Need them on top of other mobs or it looks weird when consuming something. - ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime // This should never be changed for xenobio slimes. - max_nutrition = 1000 - var/is_adult = FALSE // Slimes turn into adults when fed enough. Adult slimes are somewhat stronger, and can reproduce if fed enough. - var/maxHealth_adult = 200 - var/power_charge = 0 // Disarm attacks can shock someone if high/lucky enough. - var/mob/living/victim = null // the person the slime is currently feeding on - var/rainbow_core_candidate = TRUE // If false, rainbow cores cannot make this type randomly. - var/mutation_chance = 25 // Odds of spawning as a new color when reproducing. Can be modified by certain xenobio products. Carried across generations of slimes. - var/split_amount = 4 // Amount of children we will normally have. Half of that for dead adult slimes. Is NOT carried across generations. - var/untamable = FALSE //Makes slime untamable via discipline. - var/untamable_inheirit = FALSE //Makes slime inheirit its untamability. - var/list/slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/orange, - /mob/living/simple_mob/slime/xenobio/metal, - /mob/living/simple_mob/slime/xenobio/blue, - /mob/living/simple_mob/slime/xenobio/purple - ) - var/amount_grown = 0 // controls how long the slime has been overfed, if 10, grows or reproduces - var/number = 0 // This is used to make the slime semi-unique for indentification. - var/harmless = FALSE // Set to true when pacified. Makes the slime harmless, not get hungry, and not be able to grow/reproduce. - -/mob/living/simple_mob/slime/xenobio/Initialize(mapload, var/mob/living/simple_mob/slime/xenobio/my_predecessor) - ASSERT(ispath(ai_holder_type, /datum/ai_holder/simple_mob/xenobio_slime)) - number = rand(1, 1000) - update_name() - - . = ..() // This will make the AI and do the other mob constructor things. It will also return the default hint at the end. - - if(my_predecessor) - inherit_information(my_predecessor) - - -/mob/living/simple_mob/slime/xenobio/Destroy() - if(victim) - stop_consumption() // Unbuckle us from our victim. - return ..() - -// Called when a slime makes another slime by splitting. The predecessor slime will be deleted shortly afterwards. -/mob/living/simple_mob/slime/xenobio/proc/inherit_information(var/mob/living/simple_mob/slime/xenobio/predecessor) - if(!predecessor) - return - - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - var/datum/ai_holder/simple_mob/xenobio_slime/previous_AI = predecessor.ai_holder - ASSERT(istype(AI)) - ASSERT(istype(previous_AI)) - - // Now to transfer the information. - // Newly made slimes are bit more rebellious than their predecessors, but they also somewhat forget the atrocities the xenobiologist may have done. - AI.discipline = max(previous_AI.discipline - 1, 0) - AI.obedience = max(previous_AI.obedience - 1, 0) - AI.resentment = max(previous_AI.resentment - 1, 0) - AI.rabid = previous_AI.rabid - -/mob/living/simple_mob/slime/xenobio/update_icon() - icon_living = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"][victim ? " eating" : ""]" - icon_dead = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"] dead" - icon_rest = icon_dead - ..() // This will apply the correct icon_state and do the other overlay-related things. - - -/mob/living/simple_mob/slime/xenobio/handle_special() - if(stat != DEAD) - handle_nutrition() - - if(victim) - handle_consumption() - - handle_stuttering() // ?? - - ..() - -/mob/living/simple_mob/slime/xenobio/examine(mob/user) - . = ..() - if(hat) - . += "It is wearing \a [hat]." - - if(stat == DEAD) - . += "It appears to be dead." - else if(incapacitated(INCAPACITATION_DISABLED)) - . += "It appears to be incapacitated." - else if(harmless) - . += "It appears to have been pacified." - else - if(has_AI()) - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - if(AI.rabid) - . += "It seems very, very angry and upset." - else if(AI.obedience >= 5) - . += "It looks rather obedient." - else if(AI.discipline) - . += "It has been subjugated by force, at least for now." - -/mob/living/simple_mob/slime/xenobio/proc/make_adult() - if(is_adult) - return - - is_adult = TRUE - melee_damage_lower = round(melee_damage_lower * 2) // 20 - melee_damage_upper = round(melee_damage_upper * 2) // 30 - maxHealth = maxHealth_adult - max_nutrition = 1200 - amount_grown = 0 - update_icon() - update_name() - -/mob/living/simple_mob/slime/xenobio/proc/make_baby() - if(!is_adult) - return - - is_adult = FALSE - melee_damage_lower = round(melee_damage_lower / 2) // 20 - melee_damage_upper = round(melee_damage_upper / 2) // 30 - maxHealth = initial(maxHealth) - health = clamp(health, 0, maxHealth) - max_nutrition = initial(max_nutrition) - nutrition = 400 - amount_grown = 0 - update_icon() - update_name() - -/mob/living/simple_mob/slime/xenobio/proc/update_name() - if(harmless) // Docile slimes are generally named, so we shouldn't mess with it. - return - name = "[slime_color] [is_adult ? "adult" : "baby"] [initial(name)] ([number])" - real_name = name - -/mob/living/simple_mob/slime/xenobio/update_mood() - var/old_mood = mood - if(incapacitated(INCAPACITATION_DISABLED)) - mood = "sad" - else if(harmless) - mood = ":33" - else if(has_AI()) - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - if(AI.rabid) - mood = "angry" - else if(AI.target) - mood = "mischevous" - else if(AI.discipline) - mood = "pout" - else - mood = ":3" - else - mood = ":3" - - if(old_mood != mood) - update_icon() - -/mob/living/simple_mob/slime/xenobio/proc/enrage() - if(harmless) - return - if(has_AI()) - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - AI.enrage() - -/mob/living/simple_mob/slime/xenobio/proc/relax() - if(harmless) - return - if(has_AI()) - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - AI.relax() - -/mob/living/simple_mob/slime/xenobio/proc/pacify() - harmless = TRUE - if(has_AI()) - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - AI.pacify() - - faction = "neutral" - - // If for whatever reason the mob AI (or player) decides to try to attack something anyways. - melee_damage_upper = 0 - melee_damage_lower = 0 - - update_mood() - - -// These are verbs so that player slimes can evolve/split. -/mob/living/simple_mob/slime/xenobio/verb/evolve() - set category = "Slime" - set desc = "This will let you evolve from baby to adult slime." - - if(stat) - to_chat(src, span("warning", "I must be conscious to do this...")) - return - - if(harmless) - to_chat(src, span("warning", "I have been pacified. I cannot evolve...")) - return - - if(!is_adult) - if(amount_grown >= 10) - make_adult() - else - to_chat(src, span("warning", "I am not ready to evolve yet...")) - else - to_chat(src, span("warning", "I have already evolved...")) - - -/mob/living/simple_mob/slime/xenobio/verb/reproduce() - set category = "Slime" - set desc = "This will make you split into four new slimes." - - if(stat) - to_chat(src, span("warning", "I must be conscious to do this...")) - return - - if(harmless) - to_chat(src, span("warning", "I have been pacified. I cannot reproduce...")) - return - - if(is_adult) - if(amount_grown >= 10) - // Check if there's enough 'room' to split. - var/list/nearby_things = orange(1, src) - var/free_tiles = 0 - for(var/turf/T in nearby_things) - var/free = TRUE - if(T.density) // No walls. - continue - for(var/atom/movable/AM in T) - if(istype(AM, /mob/living/simple_mob/slime) || !(AM.CanPass(src, T))) - free = FALSE - break - for(var/atom/movable/AM in get_turf(src)) - if(!(AM.CanPass(src, T)) && !(AM == src)) - free = FALSE - break - - if(free) - free_tiles++ - - if(free_tiles < split_amount-1) // Three free tiles are needed, as four slimes are made and the 4th tile is from the center tile that the current slime occupies. - to_chat(src, span("warning", "It is too cramped here to reproduce...")) - return - - var/list/babies = list() - for(var/i = 1 to split_amount) - babies.Add(make_new_slime(no_step = i)) - - var/mob/living/simple_mob/slime/new_slime = pick(babies) - new_slime.universal_speak = universal_speak - if(src.mind) - src.mind.transfer_to(new_slime) - else - new_slime.key = src.key - qdel(src) - else - to_chat(src, span("warning", "I am not ready to reproduce yet...")) - else - to_chat(src, span("warning", "I have not evolved enough to reproduce yet...")) - -// Used when reproducing or dying. -/mob/living/simple_mob/slime/xenobio/proc/make_new_slime(var/desired_type, var/no_step) - var/t = src.type - if(desired_type) - t = desired_type - if(prob(mutation_chance / 10)) - t = /mob/living/simple_mob/slime/xenobio/rainbow - else if(prob(mutation_chance) && slime_mutation.len) - t = slime_mutation[rand(1, slime_mutation.len)] - var/mob/living/simple_mob/slime/xenobio/baby = new t(loc, src) - - // Handle 'inheriting' from parent slime. - baby.mutation_chance = mutation_chance - baby.power_charge = round(power_charge / 4) - - if(!istype(baby, /mob/living/simple_mob/slime/xenobio/rainbow)) - baby.unity = unity - if(untamable_inheirit) - baby.untamable = untamable - baby.untamable_inheirit = untamable_inheirit - baby.faction = faction - baby.friends = friends.Copy() - - if(no_step != 1) - step_away(baby, src) - return baby - -/mob/living/simple_mob/slime/xenobio/get_description_interaction() - var/list/results = list() - - if(!stat) - results += "[desc_panel_image("slimebaton")]to stun the slime, if it's being bad." - - results += ..() - - return results - -/mob/living/simple_mob/slime/xenobio/get_description_info() - var/list/lines = list() - var/intro_line = "Slimes are generally the test subjects of Xenobiology, with different colors having different properties. \ - They can be extremely dangerous if not handled properly." - lines.Add(intro_line) - lines.Add(null) // To pad the line breaks. - - var/list/rewards = list() - for(var/potential_color in slime_mutation) - var/mob/living/simple_mob/slime/S = potential_color - rewards.Add(initial(S.slime_color)) - var/reward_line = "This color of slime can mutate into [english_list(rewards)] colors, when it reproduces. It will do so when it has eatten enough." - lines.Add(reward_line) - lines.Add(null) - - lines.Add(description_info) - return lines.Join("\n") +// These slimes have the mechanics xenobiologists care about, such as reproduction, mutating into new colors, and being able to submit through fear. + +/mob/living/simple_mob/slime/xenobio + desc = "The most basic of slimes. The grey slime has no remarkable qualities, however it remains one of the most useful colors for scientists." + layer = MOB_LAYER + 1 // Need them on top of other mobs or it looks weird when consuming something. + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime // This should never be changed for xenobio slimes. + max_nutrition = 1000 + var/is_adult = FALSE // Slimes turn into adults when fed enough. Adult slimes are somewhat stronger, and can reproduce if fed enough. + var/maxHealth_adult = 200 + var/power_charge = 0 // Disarm attacks can shock someone if high/lucky enough. + var/mob/living/victim = null // the person the slime is currently feeding on + var/rainbow_core_candidate = TRUE // If false, rainbow cores cannot make this type randomly. + var/mutation_chance = 25 // Odds of spawning as a new color when reproducing. Can be modified by certain xenobio products. Carried across generations of slimes. + var/split_amount = 4 // Amount of children we will normally have. Half of that for dead adult slimes. Is NOT carried across generations. + var/untamable = FALSE //Makes slime untamable via discipline. + var/untamable_inheirit = FALSE //Makes slime inheirit its untamability. + var/list/slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/orange, + /mob/living/simple_mob/slime/xenobio/metal, + /mob/living/simple_mob/slime/xenobio/blue, + /mob/living/simple_mob/slime/xenobio/purple + ) + var/amount_grown = 0 // controls how long the slime has been overfed, if 10, grows or reproduces + var/number = 0 // This is used to make the slime semi-unique for indentification. + var/harmless = FALSE // Set to true when pacified. Makes the slime harmless, not get hungry, and not be able to grow/reproduce. + +/mob/living/simple_mob/slime/xenobio/Initialize(mapload, var/mob/living/simple_mob/slime/xenobio/my_predecessor) + ASSERT(ispath(ai_holder_type, /datum/ai_holder/simple_mob/xenobio_slime)) + number = rand(1, 1000) + update_name() + + . = ..() // This will make the AI and do the other mob constructor things. It will also return the default hint at the end. + + if(my_predecessor) + inherit_information(my_predecessor) + + +/mob/living/simple_mob/slime/xenobio/Destroy() + if(victim) + stop_consumption() // Unbuckle us from our victim. + return ..() + +// Called when a slime makes another slime by splitting. The predecessor slime will be deleted shortly afterwards. +/mob/living/simple_mob/slime/xenobio/proc/inherit_information(var/mob/living/simple_mob/slime/xenobio/predecessor) + if(!predecessor) + return + + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + var/datum/ai_holder/simple_mob/xenobio_slime/previous_AI = predecessor.ai_holder + ASSERT(istype(AI)) + ASSERT(istype(previous_AI)) + + // Now to transfer the information. + // Newly made slimes are bit more rebellious than their predecessors, but they also somewhat forget the atrocities the xenobiologist may have done. + AI.discipline = max(previous_AI.discipline - 1, 0) + AI.obedience = max(previous_AI.obedience - 1, 0) + AI.resentment = max(previous_AI.resentment - 1, 0) + AI.rabid = previous_AI.rabid + +/mob/living/simple_mob/slime/xenobio/update_icon() + icon_living = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"][victim ? " eating" : ""]" + icon_dead = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"] dead" + icon_rest = icon_dead + ..() // This will apply the correct icon_state and do the other overlay-related things. + + +/mob/living/simple_mob/slime/xenobio/handle_special() + if(stat != DEAD) + handle_nutrition() + + if(victim) + handle_consumption() + + handle_stuttering() // ?? + + ..() + +/mob/living/simple_mob/slime/xenobio/examine(mob/user) + . = ..() + if(hat) + . += "It is wearing \a [hat]." + + if(stat == DEAD) + . += "It appears to be dead." + else if(incapacitated(INCAPACITATION_DISABLED)) + . += "It appears to be incapacitated." + else if(harmless) + . += "It appears to have been pacified." + else + if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + if(AI.rabid) + . += "It seems very, very angry and upset." + else if(AI.obedience >= 5) + . += "It looks rather obedient." + else if(AI.discipline) + . += "It has been subjugated by force, at least for now." + +/mob/living/simple_mob/slime/xenobio/proc/make_adult() + if(is_adult) + return + + is_adult = TRUE + melee_damage_lower = round(melee_damage_lower * 2) // 20 + melee_damage_upper = round(melee_damage_upper * 2) // 30 + maxHealth = maxHealth_adult + max_nutrition = 1200 + amount_grown = 0 + update_icon() + update_name() + +/mob/living/simple_mob/slime/xenobio/proc/make_baby() + if(!is_adult) + return + + is_adult = FALSE + melee_damage_lower = round(melee_damage_lower / 2) // 20 + melee_damage_upper = round(melee_damage_upper / 2) // 30 + maxHealth = initial(maxHealth) + health = clamp(health, 0, maxHealth) + max_nutrition = initial(max_nutrition) + nutrition = 400 + amount_grown = 0 + update_icon() + update_name() + +/mob/living/simple_mob/slime/xenobio/proc/update_name() + if(harmless) // Docile slimes are generally named, so we shouldn't mess with it. + return + name = "[slime_color] [is_adult ? "adult" : "baby"] [initial(name)] ([number])" + real_name = name + +/mob/living/simple_mob/slime/xenobio/update_mood() + var/old_mood = mood + if(incapacitated(INCAPACITATION_DISABLED)) + mood = "sad" + else if(harmless) + mood = ":33" + else if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + if(AI.rabid) + mood = "angry" + else if(AI.target) + mood = "mischevous" + else if(AI.discipline) + mood = "pout" + else + mood = ":3" + else + mood = ":3" + + if(old_mood != mood) + update_icon() + +/mob/living/simple_mob/slime/xenobio/proc/enrage() + if(harmless) + return + if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + AI.enrage() + +/mob/living/simple_mob/slime/xenobio/proc/relax() + if(harmless) + return + if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + AI.relax() + +/mob/living/simple_mob/slime/xenobio/proc/pacify() + harmless = TRUE + if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + AI.pacify() + + faction = "neutral" + + // If for whatever reason the mob AI (or player) decides to try to attack something anyways. + melee_damage_upper = 0 + melee_damage_lower = 0 + + update_mood() + + +// These are verbs so that player slimes can evolve/split. +/mob/living/simple_mob/slime/xenobio/verb/evolve() + set category = "Slime" + set desc = "This will let you evolve from baby to adult slime." + + if(stat) + to_chat(src, span("warning", "I must be conscious to do this...")) + return + + if(harmless) + to_chat(src, span("warning", "I have been pacified. I cannot evolve...")) + return + + if(!is_adult) + if(amount_grown >= 10) + make_adult() + else + to_chat(src, span("warning", "I am not ready to evolve yet...")) + else + to_chat(src, span("warning", "I have already evolved...")) + + +/mob/living/simple_mob/slime/xenobio/verb/reproduce() + set category = "Slime" + set desc = "This will make you split into four new slimes." + + if(stat) + to_chat(src, span("warning", "I must be conscious to do this...")) + return + + if(harmless) + to_chat(src, span("warning", "I have been pacified. I cannot reproduce...")) + return + + if(is_adult) + if(amount_grown >= 10) + // Check if there's enough 'room' to split. + var/list/nearby_things = orange(1, src) + var/free_tiles = 0 + for(var/turf/T in nearby_things) + var/free = TRUE + if(T.density) // No walls. + continue + for(var/atom/movable/AM in T) + if(istype(AM, /mob/living/simple_mob/slime) || !(AM.CanPass(src, T))) + free = FALSE + break + for(var/atom/movable/AM in get_turf(src)) + if(!(AM.CanPass(src, T)) && !(AM == src)) + free = FALSE + break + + if(free) + free_tiles++ + + if(free_tiles < split_amount-1) // Three free tiles are needed, as four slimes are made and the 4th tile is from the center tile that the current slime occupies. + to_chat(src, span("warning", "It is too cramped here to reproduce...")) + return + + var/list/babies = list() + for(var/i = 1 to split_amount) + babies.Add(make_new_slime(no_step = i)) + + var/mob/living/simple_mob/slime/new_slime = pick(babies) + new_slime.universal_speak = universal_speak + if(src.mind) + src.mind.transfer_to(new_slime) + else + new_slime.key = src.key + qdel(src) + else + to_chat(src, span("warning", "I am not ready to reproduce yet...")) + else + to_chat(src, span("warning", "I have not evolved enough to reproduce yet...")) + +// Used when reproducing or dying. +/mob/living/simple_mob/slime/xenobio/proc/make_new_slime(var/desired_type, var/no_step) + var/t = src.type + if(desired_type) + t = desired_type + if(prob(mutation_chance / 10)) + t = /mob/living/simple_mob/slime/xenobio/rainbow + else if(prob(mutation_chance) && slime_mutation.len) + t = slime_mutation[rand(1, slime_mutation.len)] + var/mob/living/simple_mob/slime/xenobio/baby = new t(loc, src) + + // Handle 'inheriting' from parent slime. + baby.mutation_chance = mutation_chance + baby.power_charge = round(power_charge / 4) + + if(!istype(baby, /mob/living/simple_mob/slime/xenobio/rainbow)) + baby.unity = unity + if(untamable_inheirit) + baby.untamable = untamable + baby.untamable_inheirit = untamable_inheirit + baby.faction = faction + baby.friends = friends.Copy() + + if(no_step != 1) + step_away(baby, src) + return baby + +/mob/living/simple_mob/slime/xenobio/get_description_interaction() + var/list/results = list() + + if(!stat) + results += "[desc_panel_image("slimebaton")]to stun the slime, if it's being bad." + + results += ..() + + return results + +/mob/living/simple_mob/slime/xenobio/get_description_info() + var/list/lines = list() + var/intro_line = "Slimes are generally the test subjects of Xenobiology, with different colors having different properties. \ + They can be extremely dangerous if not handled properly." + lines.Add(intro_line) + lines.Add(null) // To pad the line breaks. + + var/list/rewards = list() + for(var/potential_color in slime_mutation) + var/mob/living/simple_mob/slime/S = potential_color + rewards.Add(initial(S.slime_color)) + var/reward_line = "This color of slime can mutate into [english_list(rewards)] colors, when it reproduces. It will do so when it has eatten enough." + lines.Add(reward_line) + lines.Add(null) + + lines.Add(description_info) + return lines.Join("\n") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/softdog.dm b/code/modules/mob/living/simple_mob/subtypes/vore/softdog.dm index b6504a84802..078da106367 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/softdog.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/softdog.dm @@ -1,315 +1,315 @@ -/datum/category_item/catalogue/fauna/woof - name = "Wildlife - Dog" - desc = "It's a relatively ordinary looking canine. \ - It has an ominous aura..." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/vore/woof - name = "dog" - desc = "It is a relatively ordinary looking canine mutt! It radiates mischief!" - tt_desc = "E Canis lupus softus" - - icon_state = "woof" - icon_living = "woof" - icon_dead = "woof_dead" - icon_rest = "woof_rest" - icon = 'icons/mob/vore.dmi' - - faction = "dog" - maxHealth = 600 - health = 600 - movement_cooldown = -1 - - response_help = "pets" - response_disarm = "rudely paps" - response_harm = "punches" - - harm_intent_damage = 5 - melee_damage_lower = 5 - melee_damage_upper = 1 - catalogue_data = list(/datum/category_item/catalogue/fauna/woof) - - var/knockdown_chance = 20 - - min_oxy = 0 - max_oxy = 0 - min_tox = 0 - max_tox = 0 - min_co2 = 0 - max_co2 = 0 - min_n2 = 0 - max_n2 = 0 - minbodytemp = 0 - maxbodytemp = 900 - - attacktext = list("nipped", "chomped", "bullied", "gnaws on") - attack_sound = 'sound/voice/bork.ogg' - friendly = list("snoofs", "nuzzles", "ruffs happily at", "smooshes on") - - ai_holder_type = /datum/ai_holder/simple_mob/woof - - mob_size = MOB_SMALL - - has_langs = list(LANGUAGE_ANIMAL, LANGUAGE_CANILUNZT, LANGUAGE_GALCOM) - say_list_type = /datum/say_list/softdog - swallowTime = 0.1 SECONDS - -/mob/living/simple_mob/vore/woof/New() - ..() - - verbs += /mob/living/proc/ventcrawl - verbs += /mob/living/proc/hide - -/datum/say_list/softdog - speak = list("Woof~", "Woof!", "Yip!", "Yap!", "Yip~", "Yap~", "Awoooooo~", "Awoo!", "AwooooooooooOOOOOOoOooOoooOoOOoooo!") - emote_hear = list("barks", "woofs", "yaps", "yips","pants", "snoofs") - emote_see = list("wags its tail", "stretches", "yawns", "swivels its ears") - say_maybe_target = list("Whuff?") - say_got_target = list("Grrrr YIP YAP!!!") - -/datum/ai_holder/simple_mob/woof - hostile = FALSE - cooperative = TRUE - retaliate = TRUE - speak_chance = 1 - wander = TRUE - -// Activate Noms! -/mob/living/simple_mob/vore/woof - vore_active = 1 - vore_capacity = 3 - vore_bump_chance = 5 - vore_bump_emote = "greedily homms at" - vore_digest_chance = 1 - vore_absorb_chance = 5 - vore_escape_chance = 10 - vore_pounce_chance = 5 - vore_ignores_undigestable = 0 - vore_default_mode = DM_HOLD - vore_icons = SA_ICON_LIVING - vore_stomach_name = "Stomach" - vore_stomach_flavor = "You have found yourself pumping on down, down, down into this extremely soft dog. The slick touches of pulsing walls roll over you in greedy fashion as you're swallowed away, the flesh forms to your figure as in an instant the world is replaced by the hot squeeze of canine gullet. And in another moment a heavy GLLRMMPTCH seals you away, the dog tossing its head eagerly, the way forward stretching to accommodate your shape as you are greedily guzzled down. The wrinkled, doughy walls pulse against you in time to the creature's steady heartbeat. The sounds of the outside world muffled into obscure tones as the wet, grumbling rolls of this soft creature's gut hold you, churning you tightly such that no part of you is spared from these gastric affections." - vore_default_contamination_flavor = "Wet" - vore_default_contamination_color = "grey" - vore_default_item_mode = IM_DIGEST - - -/mob/living/simple_mob/vore/woof/init_vore() - ..() - var/obj/belly/B = vore_selected - B.name = "stomach" - B.desc = "You have found yourself pumping on down, down, down into this extremely soft dog. The slick touches of pulsing walls roll over you in greedy fashion as you're swallowed away, the flesh forms to your figure as in an instant the world is replaced by the hot squeeze of canine gullet. And in another moment a heavy GLLRMMPTCH seals you away, the dog tossing its head eagerly, the way forward stretching to accommodate your shape as you are greedily guzzled down. The wrinkled, doughy walls pulse against you in time to the creature's steady heartbeat. The sounds of the outside world muffled into obscure tones as the wet, grumbling rolls of this soft creature's gut hold you, churning you tightly such that no part of you is spared from these gastric affections." - - B.emote_lists[DM_HOLD] = list( - "You can feel yourself shift and sway as the dog moves around. Your figure held tightly there had little room to move in that organic gloom, but every wandering step is another jostling quake that shakes through the canine frame and rocks you once again.", - "It is hard to hear much of anything over the grumbling fleshy sounds of the stomach walls pressing to you. The wet sound of flesh gliding over you too was ever-present as the walls encroach upon your personal space. And beyond that, the steady booming of the dog's heart throbs in your ears, a relaxing drone of excited thumping. Any sounds from the outside world are muffled such that they are hard to hear, as the canine walls hold on to you greedily.", - "You can hear the vague dragging, creaking sounds of the flesh holding you stretching and compressing with the dog's movements. Any time you press out, the walls seem to groan and flex in to smother you heavily for a few moments. ", - "When you shift your weight to try to find a more comfortable position you can feel your weight stretch the chamber around you a little more, and it responds by collapsing in on you more tightly! Forming to you with heavily insistence, grinding against your curves. Holding you firmly for a few moments, before slowly relaxing...", - "The heat of the dog's body soaks into your form and relaxes your muscles. It's easy to let yourself go limp, to be squeezed and carried by this soft predator. The sway of its body, the swing of its gait, all enough to lull anyone who likes such things into a deeply relaxed state, as you're rocked and supported, squeezed deep within the gut of this woof. Possessively held, kept.", - "Thick slime soaks your form as the dog's insides churn over you. There is no part of you that is not totally soaked in it before too long as the steady gastric motions massage you from head to toe. Any pushes or squirms only get the affected flesh to cling more tightly, and to press back. It's very hard to get any personal space!", - "Beyond the grumbling gurgles and the ever-present drumming of the dog's heart, you can actually hear, more faintly, the whooshing of the canine's breath. The slow draw in coming with a vague tightening of your surroundings, while the dog's soft, whooshing exhales make your surroundings more relaxed, easy to sink in against.") - - B.emote_lists[DM_ABSORB] = list( - "You can feel the weight of the dog shift as it moves around. Your figure held tightly there had absolutely no room to move in that organic gloom. Every moment those pumping walls seem to squeeze over you tighter, every wandering step the dog takes is another jostling quake that seems to sink you that much deeper into the dog's flesh, the dog's body steadily collapsing in to claim you.", - "It is hard to hear much of anything over the smothering press of stomach walls pressing to you, forming to your features. The tarry flesh you are slowly sinking into squelches here and there as it flows over your features. The sound of the dog's body absorbing you is oddly quiet. No bubbling or glooping. Just one body slowly blending into and becoming one with another. And beyond all that, the steady booming of the dog's heart throbs in your ears, moment by moment that sound seems to tug at you, coursing through you as much as the dog you are steadily becoming a part of. Any sounds from the outside world are muffled such that they are hard to hear, as you sink into the walls of this canine predator.", - "You can hear the vague dragging, creaking sounds of the flesh holding you stretching and compressing with the dog's movements. Any time you press out, the walls seem to simply flow over you, and allow whatever pushed out to sink in that much more. The swell on the dog's tummy shrinking that much faster... ", - "When you shift your weight to try to get some space. you can feel your weight simply sink into that flesh, the folds forming around you tightly, and the deeper you sink, the harder it gets to move. The pressure never seems to let up as the tide of flesh holding you slowly overcomes your form.", - "As the seemingly molten heat of this dog's flesh flows over you, it's easy to let yourself go limp, to just give in and become one with this creature that so obviously wanted you, and indeed, unless something happened to stop this, you soon would be... The dog tail swaying in a knowing arc as you are added to its figure. Squeezed, tucked away, kept.", - "The thick slimes that coat your form do nothing to keep the molten flesh of this dog's stomach from advancing across your figure and claiming you up. Soon there's not a single part of you that is not totally inundated in the deep press of a woof's gastric massage. Any pushes or squirms only get the affected flesh to cling more tightly, and to press back, flowing over your form, deeper, deeper.", - "Beyond the squelching, clinging tide of dog flesh working to make the two of you one, and the ever-present drumming of the dog's heart, you can actually hear, more faintly, the whooshing of the canine's breath. The slow draw in coming with a vague tightening of your surroundings, while the dog's soft, whooshing exhales make your surroundings more relaxed. And you realize suddenly that the dog's breathing seem to bring you relief even as you are totally smothered in the canine's insistent gastric affections!") - - B.emote_lists[DM_DIGEST] = list( - "As the dog goes about its business, you can feel the shift your weight sway on its tummy. The gurgling glorping sounds that come with the squeezing, kneading, massaging motions let you know that you're held tight, churned. Dog food.", - "It is hard to hear much of anything over the roaring gurgles of stomach walls churning over you. The wet sound of flesh grinding heavily over you too was ever-present as the walls encroach upon your personal space, lathering you in tingly, syrupy thick slimes. And beyond that, the steady booming of the dog's heart throbs in your ears, a drone of excited thumping. Any sounds from the outside world are muffled such that they are hard to hear, as the canine walls churn on to you greedily.", - "You can hear the vague dragging, creaking sounds of the flesh holding you stretching and compressing with the dog's movements. The walls seem to constantly flex and squeeze across you, pressing in against you, massaging thick slime into your figure, steadily trying to soften up your outer layers...", - "When you try to shift your weight to try to find a more comfortable position, you find that those heavy walls pumping over you make it hard to move at all. You can feel the weight of the dog pressing in all around you even without those muscles flexing and throbbing across your form. It forms to you with heavily insistence, grinding against your curves, churning that bubbling gloop into you. Holding you firm and heavy as that stomach does its work...", - "The heat of the dog's body soaks into your form and relaxes your muscles. It's easy to let yourself go limp, to just completely give in to this soft predator. The sway of its body, the swing of its gait, all enough to lull anyone who likes such things into a deeply relaxed state. Churned and slathered, massaged by doughy wrinkled walls deep within the gut of this woof. Possessively held within that needy chamber.", - "Thick slime soaks your form as the dog's insides churn over you. There is no part of you that is not totally soaked in it before too long as the steady gastric motions massage you from head to toe. Any pushes or squirms only get the affected flesh to cling more tightly, and to press back. It's very hard to get any personal space!", - "Beyond the grumbling gurgles and the ever-present drumming of the dog's heart, you can actually hear, more faintly, the whooshing of the canine's breath. The slow draw in coming with a vague tightening of your surroundings, while the dog's soft, whooshing exhales make your surroundings more relaxed, easy to sink in against. Occasionally though everything would go all tight and cramped! And somewhere up above you can hear the dog let out a dainty little belch...") - - B.digest_brute = 0.05 - B.digest_burn = 0.05 - B.mode_flags = 8 - B.belly_fullscreen = "a_tumby" - B.struggle_messages_inside = list( - "Your struggling only causes %pred's doughy gut to smother you against those wrinkled walls...", - "As you squirm, %pred's %belly flexxes over you heavily, forming you back into a small ball...", - "You push out at those heavy wrinkled walls with all your might and they collapse back in on you! Clinging and churning over you heavily for a few minutes!!!", - "As you struggle against the gut of this dog, you can feel a squeeze roll over you from the bottom to the top! The walls cling to you a little tighter then as the dog emits a soft little burp...", - "You try to squirm, but you can't even move as those heavy walls throb and pulse and churn around you.", - "You paddle against the fleshy walls of %pred's %belly, making a little space for yourself for a moment, before the wrinkled surface bounces back against you.", - "The slick walls are doughy, smushy under your fingers, and very difficult to grip! The flesh pulses under your grip in time with %pred's heartbeat.", - "Your hands slip and slide over the slick slimes of %pred's %belly as you struggle to escape! The walls pulse and squeeze around you greedily.", - "%pred lets out a happy little awoo, rocking their hips to jostle you as you squirm, while the weight of those walls closes in on you, squeezing you tightly!", - "%pred's %belly glorgles around you as you push and struggle within! The squashy walls are always reluctant to give ground, and the moment your struggles lax, they redouble their efforts in smothering all the fight out of you!") - B.struggle_messages_outside = list( - "A vague shape briefly swells on %pred's %belly as something moves inside...", - "Something shifts within %pred's %belly.", - "%pred urps as something shifts in their %belly.") - B.examine_messages = list( - "Their %belly is distended.", - "Vague shapes swell their %belly.", - "It looks like they have something solid in their %belly") - -/obj/item/projectile/awoo_missile - name = "awoo missile" - icon_state = "force_missile" - fire_sound = 'sound/voice/long_awoo.ogg' - damage = 1 - damage_type = BRUTE - check_armour = "melee" - - impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser - hitsound_wall = 'sound/voice/bork.ogg' - -/mob/living/simple_mob/vore/woof/cass - name = "Cass" - desc = "Well trained, comfy company. They have a pretty red bow tied into their fur. They look very soft." - - icon_state = "cass" - icon_living = "cass" - icon_dead = "cass_dead" - icon_rest = "cass_rest" - ic_revivable = 0 - - faction = "theatre" - gender = PLURAL - ai_holder_type = /datum/ai_holder/simple_mob/woof/cass - -/mob/living/simple_mob/vore/woof/cass - vore_digest_chance = 0 - vore_escape_chance = 25 - digestable = 0 - -/datum/ai_holder/simple_mob/woof/cass - retaliate = 0 - violent_breakthrough = 0 - -/datum/ai_holder/simple_mob/ranged/kiting/threatening/woof - hostile = 1 - retaliate = 1 - cooperative = TRUE - speak_chance = 1 - lose_target_timeout = 0 // Easily distracted - -/datum/ai_holder/simple_mob/woof/hostile - hostile = 1 - retaliate = 1 - -/mob/living/simple_mob/vore/woof/apply_melee_effects(var/atom/A) - if(isliving(A)) - var/mob/living/L = A - if(prob(knockdown_chance)) - L.Weaken(3) - L.visible_message(span("danger", "\The [src] pounces on \the [L]!")) - -/mob/living/simple_mob/vore/woof/hostile/melee - - movement_cooldown = -2 - - ai_holder_type = /datum/ai_holder/simple_mob/woof/hostile - -/mob/living/simple_mob/vore/woof/hostile/ranged - - ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof - - projectiletype = /obj/item/projectile/awoo_missile - projectilesound = 'sound/voice/long_awoo.ogg' - -/mob/living/simple_mob/vore/woof/hostile/horrible - - ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof - armor = list( - "melee" = 75, - "bullet" = 75, - "laser" = 75, - "energy" = 75, - "bomb" = 75, - "bio" = 75, - "rad" = 75) - - projectiletype = /obj/item/projectile/awoo_missile/heavy - projectilesound = 'sound/voice/long_awoo.ogg' - -/obj/item/projectile/awoo_missile/heavy - damage = 50 - -/obj/item/projectile/forcebolt/harmless/awoobolt - icon_state = "force_missile" - fire_sound = 'sound/voice/long_awoo.ogg' - damage = 0 - impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser - hitsound_wall = 'sound/voice/bork.ogg' - -/mob/living/simple_mob/vore/woof/hostile/terrible - - ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof - - projectiletype = /obj/item/projectile/forcebolt/harmless/awoobolt - projectilesound = 'sound/voice/long_awoo.ogg' - -/mob/living/simple_mob/vore/woof/cass/attack_hand(mob/living/carbon/human/M as mob) - if(stat != DEAD) - return ..() - if(M.a_intent == I_HELP) - M.visible_message("[M] pets [src].", runemessage = "pets [src]") - if(do_after(M, 30 SECONDS, exclusive = TASK_USER_EXCLUSIVE, target = src)) - faction = M.faction - revive() - sight = initial(sight) - see_in_dark = initial(see_in_dark) - see_invisible = initial(see_invisible) - update_icon() - visible_message("[src] stops playing dead.", runemessage = "[src] stops playing dead") - else - M.visible_message("The petting was interrupted!!!", runemessage = "The petting was interrupted") - return - -/mob/living/simple_mob/vore/woof/hostile/aweful - maxHealth = 100 - health = 100 - var/killswitch = FALSE - - -/mob/living/simple_mob/vore/woof/hostile/aweful/Initialize() - . = ..() - var/thismany = (rand(25,500)) / 100 - resize(thismany, animate = FALSE, uncapped = TRUE, ignore_prefs = TRUE) - -/mob/living/simple_mob/vore/woof/hostile/aweful/death() - . = ..() - if(killswitch) - visible_message("\The [src] evaporates into nothing...") - qdel(src) - return - var/thismany = rand(0,3) - var/list/possiblewoofs = list(/mob/living/simple_mob/vore/woof/hostile/aweful/melee, /mob/living/simple_mob/vore/woof/hostile/aweful/ranged) - if(thismany == 0) - visible_message("\The [src] evaporates into nothing...") - if(thismany >= 1) - var/thiswoof = pick(possiblewoofs) - new thiswoof(loc, src) - visible_message("Another [src] appears!") - if(thismany >= 2) - var/thiswoof = pick(possiblewoofs) - new thiswoof(loc, src) - visible_message("Another [src] appears!") - if(thismany >= 3) - var/thiswoof = pick(possiblewoofs) - new thiswoof(loc, src) - visible_message("Another [src] appears!") - qdel(src) - -/mob/living/simple_mob/vore/woof/hostile/aweful/melee - - movement_cooldown = -2 - - ai_holder_type = /datum/ai_holder/simple_mob/woof/hostile - -/mob/living/simple_mob/vore/woof/hostile/aweful/ranged - movement_cooldown = -2 - - ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof - - projectiletype = /obj/item/projectile/awoo_missile - projectilesound = 'sound/voice/long_awoo.ogg' +/datum/category_item/catalogue/fauna/woof + name = "Wildlife - Dog" + desc = "It's a relatively ordinary looking canine. \ + It has an ominous aura..." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/vore/woof + name = "dog" + desc = "It is a relatively ordinary looking canine mutt! It radiates mischief!" + tt_desc = "E Canis lupus softus" + + icon_state = "woof" + icon_living = "woof" + icon_dead = "woof_dead" + icon_rest = "woof_rest" + icon = 'icons/mob/vore.dmi' + + faction = "dog" + maxHealth = 600 + health = 600 + movement_cooldown = -1 + + response_help = "pets" + response_disarm = "rudely paps" + response_harm = "punches" + + harm_intent_damage = 5 + melee_damage_lower = 5 + melee_damage_upper = 1 + catalogue_data = list(/datum/category_item/catalogue/fauna/woof) + + var/knockdown_chance = 20 + + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + maxbodytemp = 900 + + attacktext = list("nipped", "chomped", "bullied", "gnaws on") + attack_sound = 'sound/voice/bork.ogg' + friendly = list("snoofs", "nuzzles", "ruffs happily at", "smooshes on") + + ai_holder_type = /datum/ai_holder/simple_mob/woof + + mob_size = MOB_SMALL + + has_langs = list(LANGUAGE_ANIMAL, LANGUAGE_CANILUNZT, LANGUAGE_GALCOM) + say_list_type = /datum/say_list/softdog + swallowTime = 0.1 SECONDS + +/mob/living/simple_mob/vore/woof/New() + ..() + + verbs += /mob/living/proc/ventcrawl + verbs += /mob/living/proc/hide + +/datum/say_list/softdog + speak = list("Woof~", "Woof!", "Yip!", "Yap!", "Yip~", "Yap~", "Awoooooo~", "Awoo!", "AwooooooooooOOOOOOoOooOoooOoOOoooo!") + emote_hear = list("barks", "woofs", "yaps", "yips","pants", "snoofs") + emote_see = list("wags its tail", "stretches", "yawns", "swivels its ears") + say_maybe_target = list("Whuff?") + say_got_target = list("Grrrr YIP YAP!!!") + +/datum/ai_holder/simple_mob/woof + hostile = FALSE + cooperative = TRUE + retaliate = TRUE + speak_chance = 1 + wander = TRUE + +// Activate Noms! +/mob/living/simple_mob/vore/woof + vore_active = 1 + vore_capacity = 3 + vore_bump_chance = 5 + vore_bump_emote = "greedily homms at" + vore_digest_chance = 1 + vore_absorb_chance = 5 + vore_escape_chance = 10 + vore_pounce_chance = 5 + vore_ignores_undigestable = 0 + vore_default_mode = DM_HOLD + vore_icons = SA_ICON_LIVING + vore_stomach_name = "Stomach" + vore_stomach_flavor = "You have found yourself pumping on down, down, down into this extremely soft dog. The slick touches of pulsing walls roll over you in greedy fashion as you're swallowed away, the flesh forms to your figure as in an instant the world is replaced by the hot squeeze of canine gullet. And in another moment a heavy GLLRMMPTCH seals you away, the dog tossing its head eagerly, the way forward stretching to accommodate your shape as you are greedily guzzled down. The wrinkled, doughy walls pulse against you in time to the creature's steady heartbeat. The sounds of the outside world muffled into obscure tones as the wet, grumbling rolls of this soft creature's gut hold you, churning you tightly such that no part of you is spared from these gastric affections." + vore_default_contamination_flavor = "Wet" + vore_default_contamination_color = "grey" + vore_default_item_mode = IM_DIGEST + + +/mob/living/simple_mob/vore/woof/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "You have found yourself pumping on down, down, down into this extremely soft dog. The slick touches of pulsing walls roll over you in greedy fashion as you're swallowed away, the flesh forms to your figure as in an instant the world is replaced by the hot squeeze of canine gullet. And in another moment a heavy GLLRMMPTCH seals you away, the dog tossing its head eagerly, the way forward stretching to accommodate your shape as you are greedily guzzled down. The wrinkled, doughy walls pulse against you in time to the creature's steady heartbeat. The sounds of the outside world muffled into obscure tones as the wet, grumbling rolls of this soft creature's gut hold you, churning you tightly such that no part of you is spared from these gastric affections." + + B.emote_lists[DM_HOLD] = list( + "You can feel yourself shift and sway as the dog moves around. Your figure held tightly there had little room to move in that organic gloom, but every wandering step is another jostling quake that shakes through the canine frame and rocks you once again.", + "It is hard to hear much of anything over the grumbling fleshy sounds of the stomach walls pressing to you. The wet sound of flesh gliding over you too was ever-present as the walls encroach upon your personal space. And beyond that, the steady booming of the dog's heart throbs in your ears, a relaxing drone of excited thumping. Any sounds from the outside world are muffled such that they are hard to hear, as the canine walls hold on to you greedily.", + "You can hear the vague dragging, creaking sounds of the flesh holding you stretching and compressing with the dog's movements. Any time you press out, the walls seem to groan and flex in to smother you heavily for a few moments. ", + "When you shift your weight to try to find a more comfortable position you can feel your weight stretch the chamber around you a little more, and it responds by collapsing in on you more tightly! Forming to you with heavily insistence, grinding against your curves. Holding you firmly for a few moments, before slowly relaxing...", + "The heat of the dog's body soaks into your form and relaxes your muscles. It's easy to let yourself go limp, to be squeezed and carried by this soft predator. The sway of its body, the swing of its gait, all enough to lull anyone who likes such things into a deeply relaxed state, as you're rocked and supported, squeezed deep within the gut of this woof. Possessively held, kept.", + "Thick slime soaks your form as the dog's insides churn over you. There is no part of you that is not totally soaked in it before too long as the steady gastric motions massage you from head to toe. Any pushes or squirms only get the affected flesh to cling more tightly, and to press back. It's very hard to get any personal space!", + "Beyond the grumbling gurgles and the ever-present drumming of the dog's heart, you can actually hear, more faintly, the whooshing of the canine's breath. The slow draw in coming with a vague tightening of your surroundings, while the dog's soft, whooshing exhales make your surroundings more relaxed, easy to sink in against.") + + B.emote_lists[DM_ABSORB] = list( + "You can feel the weight of the dog shift as it moves around. Your figure held tightly there had absolutely no room to move in that organic gloom. Every moment those pumping walls seem to squeeze over you tighter, every wandering step the dog takes is another jostling quake that seems to sink you that much deeper into the dog's flesh, the dog's body steadily collapsing in to claim you.", + "It is hard to hear much of anything over the smothering press of stomach walls pressing to you, forming to your features. The tarry flesh you are slowly sinking into squelches here and there as it flows over your features. The sound of the dog's body absorbing you is oddly quiet. No bubbling or glooping. Just one body slowly blending into and becoming one with another. And beyond all that, the steady booming of the dog's heart throbs in your ears, moment by moment that sound seems to tug at you, coursing through you as much as the dog you are steadily becoming a part of. Any sounds from the outside world are muffled such that they are hard to hear, as you sink into the walls of this canine predator.", + "You can hear the vague dragging, creaking sounds of the flesh holding you stretching and compressing with the dog's movements. Any time you press out, the walls seem to simply flow over you, and allow whatever pushed out to sink in that much more. The swell on the dog's tummy shrinking that much faster... ", + "When you shift your weight to try to get some space. you can feel your weight simply sink into that flesh, the folds forming around you tightly, and the deeper you sink, the harder it gets to move. The pressure never seems to let up as the tide of flesh holding you slowly overcomes your form.", + "As the seemingly molten heat of this dog's flesh flows over you, it's easy to let yourself go limp, to just give in and become one with this creature that so obviously wanted you, and indeed, unless something happened to stop this, you soon would be... The dog tail swaying in a knowing arc as you are added to its figure. Squeezed, tucked away, kept.", + "The thick slimes that coat your form do nothing to keep the molten flesh of this dog's stomach from advancing across your figure and claiming you up. Soon there's not a single part of you that is not totally inundated in the deep press of a woof's gastric massage. Any pushes or squirms only get the affected flesh to cling more tightly, and to press back, flowing over your form, deeper, deeper.", + "Beyond the squelching, clinging tide of dog flesh working to make the two of you one, and the ever-present drumming of the dog's heart, you can actually hear, more faintly, the whooshing of the canine's breath. The slow draw in coming with a vague tightening of your surroundings, while the dog's soft, whooshing exhales make your surroundings more relaxed. And you realize suddenly that the dog's breathing seem to bring you relief even as you are totally smothered in the canine's insistent gastric affections!") + + B.emote_lists[DM_DIGEST] = list( + "As the dog goes about its business, you can feel the shift your weight sway on its tummy. The gurgling glorping sounds that come with the squeezing, kneading, massaging motions let you know that you're held tight, churned. Dog food.", + "It is hard to hear much of anything over the roaring gurgles of stomach walls churning over you. The wet sound of flesh grinding heavily over you too was ever-present as the walls encroach upon your personal space, lathering you in tingly, syrupy thick slimes. And beyond that, the steady booming of the dog's heart throbs in your ears, a drone of excited thumping. Any sounds from the outside world are muffled such that they are hard to hear, as the canine walls churn on to you greedily.", + "You can hear the vague dragging, creaking sounds of the flesh holding you stretching and compressing with the dog's movements. The walls seem to constantly flex and squeeze across you, pressing in against you, massaging thick slime into your figure, steadily trying to soften up your outer layers...", + "When you try to shift your weight to try to find a more comfortable position, you find that those heavy walls pumping over you make it hard to move at all. You can feel the weight of the dog pressing in all around you even without those muscles flexing and throbbing across your form. It forms to you with heavily insistence, grinding against your curves, churning that bubbling gloop into you. Holding you firm and heavy as that stomach does its work...", + "The heat of the dog's body soaks into your form and relaxes your muscles. It's easy to let yourself go limp, to just completely give in to this soft predator. The sway of its body, the swing of its gait, all enough to lull anyone who likes such things into a deeply relaxed state. Churned and slathered, massaged by doughy wrinkled walls deep within the gut of this woof. Possessively held within that needy chamber.", + "Thick slime soaks your form as the dog's insides churn over you. There is no part of you that is not totally soaked in it before too long as the steady gastric motions massage you from head to toe. Any pushes or squirms only get the affected flesh to cling more tightly, and to press back. It's very hard to get any personal space!", + "Beyond the grumbling gurgles and the ever-present drumming of the dog's heart, you can actually hear, more faintly, the whooshing of the canine's breath. The slow draw in coming with a vague tightening of your surroundings, while the dog's soft, whooshing exhales make your surroundings more relaxed, easy to sink in against. Occasionally though everything would go all tight and cramped! And somewhere up above you can hear the dog let out a dainty little belch...") + + B.digest_brute = 0.05 + B.digest_burn = 0.05 + B.mode_flags = 8 + B.belly_fullscreen = "a_tumby" + B.struggle_messages_inside = list( + "Your struggling only causes %pred's doughy gut to smother you against those wrinkled walls...", + "As you squirm, %pred's %belly flexxes over you heavily, forming you back into a small ball...", + "You push out at those heavy wrinkled walls with all your might and they collapse back in on you! Clinging and churning over you heavily for a few minutes!!!", + "As you struggle against the gut of this dog, you can feel a squeeze roll over you from the bottom to the top! The walls cling to you a little tighter then as the dog emits a soft little burp...", + "You try to squirm, but you can't even move as those heavy walls throb and pulse and churn around you.", + "You paddle against the fleshy walls of %pred's %belly, making a little space for yourself for a moment, before the wrinkled surface bounces back against you.", + "The slick walls are doughy, smushy under your fingers, and very difficult to grip! The flesh pulses under your grip in time with %pred's heartbeat.", + "Your hands slip and slide over the slick slimes of %pred's %belly as you struggle to escape! The walls pulse and squeeze around you greedily.", + "%pred lets out a happy little awoo, rocking their hips to jostle you as you squirm, while the weight of those walls closes in on you, squeezing you tightly!", + "%pred's %belly glorgles around you as you push and struggle within! The squashy walls are always reluctant to give ground, and the moment your struggles lax, they redouble their efforts in smothering all the fight out of you!") + B.struggle_messages_outside = list( + "A vague shape briefly swells on %pred's %belly as something moves inside...", + "Something shifts within %pred's %belly.", + "%pred urps as something shifts in their %belly.") + B.examine_messages = list( + "Their %belly is distended.", + "Vague shapes swell their %belly.", + "It looks like they have something solid in their %belly") + +/obj/item/projectile/awoo_missile + name = "awoo missile" + icon_state = "force_missile" + fire_sound = 'sound/voice/long_awoo.ogg' + damage = 1 + damage_type = BRUTE + check_armour = "melee" + + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + hitsound_wall = 'sound/voice/bork.ogg' + +/mob/living/simple_mob/vore/woof/cass + name = "Cass" + desc = "Well trained, comfy company. They have a pretty red bow tied into their fur. They look very soft." + + icon_state = "cass" + icon_living = "cass" + icon_dead = "cass_dead" + icon_rest = "cass_rest" + ic_revivable = 0 + + faction = "theatre" + gender = PLURAL + ai_holder_type = /datum/ai_holder/simple_mob/woof/cass + +/mob/living/simple_mob/vore/woof/cass + vore_digest_chance = 0 + vore_escape_chance = 25 + digestable = 0 + +/datum/ai_holder/simple_mob/woof/cass + retaliate = 0 + violent_breakthrough = 0 + +/datum/ai_holder/simple_mob/ranged/kiting/threatening/woof + hostile = 1 + retaliate = 1 + cooperative = TRUE + speak_chance = 1 + lose_target_timeout = 0 // Easily distracted + +/datum/ai_holder/simple_mob/woof/hostile + hostile = 1 + retaliate = 1 + +/mob/living/simple_mob/vore/woof/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(prob(knockdown_chance)) + L.Weaken(3) + L.visible_message(span("danger", "\The [src] pounces on \the [L]!")) + +/mob/living/simple_mob/vore/woof/hostile/melee + + movement_cooldown = -2 + + ai_holder_type = /datum/ai_holder/simple_mob/woof/hostile + +/mob/living/simple_mob/vore/woof/hostile/ranged + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof + + projectiletype = /obj/item/projectile/awoo_missile + projectilesound = 'sound/voice/long_awoo.ogg' + +/mob/living/simple_mob/vore/woof/hostile/horrible + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof + armor = list( + "melee" = 75, + "bullet" = 75, + "laser" = 75, + "energy" = 75, + "bomb" = 75, + "bio" = 75, + "rad" = 75) + + projectiletype = /obj/item/projectile/awoo_missile/heavy + projectilesound = 'sound/voice/long_awoo.ogg' + +/obj/item/projectile/awoo_missile/heavy + damage = 50 + +/obj/item/projectile/forcebolt/harmless/awoobolt + icon_state = "force_missile" + fire_sound = 'sound/voice/long_awoo.ogg' + damage = 0 + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + hitsound_wall = 'sound/voice/bork.ogg' + +/mob/living/simple_mob/vore/woof/hostile/terrible + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof + + projectiletype = /obj/item/projectile/forcebolt/harmless/awoobolt + projectilesound = 'sound/voice/long_awoo.ogg' + +/mob/living/simple_mob/vore/woof/cass/attack_hand(mob/living/carbon/human/M as mob) + if(stat != DEAD) + return ..() + if(M.a_intent == I_HELP) + M.visible_message("[M] pets [src].", runemessage = "pets [src]") + if(do_after(M, 30 SECONDS, exclusive = TASK_USER_EXCLUSIVE, target = src)) + faction = M.faction + revive() + sight = initial(sight) + see_in_dark = initial(see_in_dark) + see_invisible = initial(see_invisible) + update_icon() + visible_message("[src] stops playing dead.", runemessage = "[src] stops playing dead") + else + M.visible_message("The petting was interrupted!!!", runemessage = "The petting was interrupted") + return + +/mob/living/simple_mob/vore/woof/hostile/aweful + maxHealth = 100 + health = 100 + var/killswitch = FALSE + + +/mob/living/simple_mob/vore/woof/hostile/aweful/Initialize() + . = ..() + var/thismany = (rand(25,500)) / 100 + resize(thismany, animate = FALSE, uncapped = TRUE, ignore_prefs = TRUE) + +/mob/living/simple_mob/vore/woof/hostile/aweful/death() + . = ..() + if(killswitch) + visible_message("\The [src] evaporates into nothing...") + qdel(src) + return + var/thismany = rand(0,3) + var/list/possiblewoofs = list(/mob/living/simple_mob/vore/woof/hostile/aweful/melee, /mob/living/simple_mob/vore/woof/hostile/aweful/ranged) + if(thismany == 0) + visible_message("\The [src] evaporates into nothing...") + if(thismany >= 1) + var/thiswoof = pick(possiblewoofs) + new thiswoof(loc, src) + visible_message("Another [src] appears!") + if(thismany >= 2) + var/thiswoof = pick(possiblewoofs) + new thiswoof(loc, src) + visible_message("Another [src] appears!") + if(thismany >= 3) + var/thiswoof = pick(possiblewoofs) + new thiswoof(loc, src) + visible_message("Another [src] appears!") + qdel(src) + +/mob/living/simple_mob/vore/woof/hostile/aweful/melee + + movement_cooldown = -2 + + ai_holder_type = /datum/ai_holder/simple_mob/woof/hostile + +/mob/living/simple_mob/vore/woof/hostile/aweful/ranged + movement_cooldown = -2 + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof + + projectiletype = /obj/item/projectile/awoo_missile + projectilesound = 'sound/voice/long_awoo.ogg' diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/weretiger.dm b/code/modules/mob/living/simple_mob/subtypes/vore/weretiger.dm index 39a6e3bd2c3..a98e5592e71 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/weretiger.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/weretiger.dm @@ -1,46 +1,46 @@ -/mob/living/simple_mob/vore/weretiger - name = "weretiger" - desc = "A big scary, albino were-tiger! At least they seem decently mannered..." - tt_desc = "Tigris Thropus Album" - - icon_state = "bigcat" - icon_living = "bigcat" - icon_dead = "bigcat_dead" - icon_rest = null - icon = 'icons/mob/bigcat.dmi' - - faction = "panther" - maxHealth = 150 - health = 150 - movement_cooldown = -1 - - response_help = "pats" - response_disarm = "gently pushes aside" - response_harm = "hits" - - harm_intent_damage = 15 - melee_damage_lower = 10 - melee_damage_upper = 20 - attacktext = list("mauled") - - say_list_type = /datum/say_list/weretiger - ai_holder_type = /datum/ai_holder/simple_mob/retaliate - - pixel_x = -16 - default_pixel_x = -16 - - has_hands = 1 - -// Nomnomn -/mob/living/simple_mob/vore/weretiger - vore_active = 1 - vore_bump_chance = 10 - vore_bump_emote = "sneaks up on" - vore_pounce_chance = 50 - vore_default_mode = DM_HOLD - vore_icons = SA_ICON_LIVING - -/datum/say_list/weretiger - speak = list("Gruff.","ROAR!","Growl.") - emote_hear = list("growls!","grunts.") - emote_see = list("pads around noisily.","scratches the floor thoroughly.") +/mob/living/simple_mob/vore/weretiger + name = "weretiger" + desc = "A big scary, albino were-tiger! At least they seem decently mannered..." + tt_desc = "Tigris Thropus Album" + + icon_state = "bigcat" + icon_living = "bigcat" + icon_dead = "bigcat_dead" + icon_rest = null + icon = 'icons/mob/bigcat.dmi' + + faction = "panther" + maxHealth = 150 + health = 150 + movement_cooldown = -1 + + response_help = "pats" + response_disarm = "gently pushes aside" + response_harm = "hits" + + harm_intent_damage = 15 + melee_damage_lower = 10 + melee_damage_upper = 20 + attacktext = list("mauled") + + say_list_type = /datum/say_list/weretiger + ai_holder_type = /datum/ai_holder/simple_mob/retaliate + + pixel_x = -16 + default_pixel_x = -16 + + has_hands = 1 + +// Nomnomn +/mob/living/simple_mob/vore/weretiger + vore_active = 1 + vore_bump_chance = 10 + vore_bump_emote = "sneaks up on" + vore_pounce_chance = 50 + vore_default_mode = DM_HOLD + vore_icons = SA_ICON_LIVING + +/datum/say_list/weretiger + speak = list("Gruff.","ROAR!","Growl.") + emote_hear = list("growls!","grunts.") + emote_see = list("pads around noisily.","scratches the floor thoroughly.") diff --git a/code/modules/mob/living/status_indicators.dm b/code/modules/mob/living/status_indicators.dm index 4f61b7ad34f..bbb3f032683 100644 --- a/code/modules/mob/living/status_indicators.dm +++ b/code/modules/mob/living/status_indicators.dm @@ -1,87 +1,87 @@ -#define STATUS_INDICATOR_Y_OFFSET 2 // Offset from the edge of the icon sprite, so 32 pixels plus whatever number is here. -#define STATUS_INDICATOR_ICON_X_SIZE 16 // Don't need to care about the Y size due to the origin being on the bottom side. -#define STATUS_INDICATOR_ICON_MARGIN 2 // The space between two status indicators. - -// 'Status indicators' are icons that display over a mob's head, that visually indicate that the mob is suffering -// from some kind of effect, such as being stunned, blinded, confused, asleep, etc. -// The icons are managed automatically by the mob itself, so that their positions will shift if another indicator is added, -// and it will try to always be above the mob sprite, even for larger sprites like xenos. - -/mob/living - var/list/status_indicators = null // Will become a list as needed. - -// Adds an icon_state, or image overlay, to the list of indicators to be managed automatically. -// Also initializes the list if one doesn't exist. -/mob/living/proc/add_status_indicator(image/thing) - if(get_status_indicator(thing)) // No duplicates, please. - return - - if(!istype(thing, /image)) - thing = image(icon = 'icons/mob/status_indicators.dmi', icon_state = thing) - - LAZYADD(status_indicators, thing) - handle_status_indicators() - -// Similar to above but removes it instead, and nulls the list if it becomes empty as a result. -/mob/living/proc/remove_status_indicator(image/thing) - thing = get_status_indicator(thing) - - cut_overlay(thing) - LAZYREMOVE(status_indicators, thing) - handle_status_indicators() - -/mob/living/proc/get_status_indicator(image/thing) - if(!istype(thing, /image)) - for(var/image/I in status_indicators) - if(I.icon_state == thing) - return I - return LAZYACCESS(status_indicators, LAZYFIND(status_indicators, thing)) - -// Refreshes the indicators over a mob's head. Should only be called when adding or removing a status indicator with the above procs, -// or when the mob changes size visually for some reason. -/mob/living/proc/handle_status_indicators() - // First, get rid of all the overlays. - for(var/thing in status_indicators) - cut_overlay(thing) - - if(!LAZYLEN(status_indicators)) - return - - if(stat == DEAD) - return - - // Now put them back on in the right spot. - var/our_sprite_x = icon_expected_width * get_icon_scale_x() - var/our_sprite_y = icon_expected_height * get_icon_scale_y() - - var/x_offset = our_sprite_x // Add your own offset here later if you want. - var/y_offset = our_sprite_y + STATUS_INDICATOR_Y_OFFSET - - // Calculates how 'long' the row of indicators and the margin between them should be. - // The goal is to have the center of that row be horizontally aligned with the sprite's center. - var/expected_status_indicator_length = (STATUS_INDICATOR_ICON_X_SIZE * status_indicators.len) + (STATUS_INDICATOR_ICON_MARGIN * max(status_indicators.len - 1, 0)) - var/current_x_position = (x_offset / 2) - (expected_status_indicator_length / 2) - - // In /mob/living's `update_transform()`, the sprite is horizontally shifted when scaled up, so that the center of the sprite doesn't move to the right. - // Because of that, this adjustment needs to happen with the future indicator row as well, or it will look bad. - current_x_position -= (icon_expected_width / 2) * (get_icon_scale_y() - 1) - - // Now the indicator row can actually be built. - for(var/image/I as anything in status_indicators) - - // This is a semi-HUD element, in a similar manner as medHUDs, in that they're 'above' everything else in the world, - // but don't pierce obfuscation layers such as blindness or darkness, unlike actual HUD elements like inventory slots. - I.plane = PLANE_STATUS - I.layer = HUD_LAYER - I.appearance_flags = PIXEL_SCALE|TILE_BOUND|NO_CLIENT_COLOR|RESET_COLOR|RESET_ALPHA|RESET_TRANSFORM|KEEP_APART - I.pixel_y = y_offset - I.pixel_x = current_x_position - add_overlay(I) - // Adding the margin space every time saves a conditional check on the last iteration, - // and it won't cause any issues since no more icons will be added, and the var is not used for anything else. - current_x_position += STATUS_INDICATOR_ICON_X_SIZE + STATUS_INDICATOR_ICON_MARGIN - - -#undef STATUS_INDICATOR_Y_OFFSET -#undef STATUS_INDICATOR_ICON_X_SIZE -#undef STATUS_INDICATOR_ICON_MARGIN +#define STATUS_INDICATOR_Y_OFFSET 2 // Offset from the edge of the icon sprite, so 32 pixels plus whatever number is here. +#define STATUS_INDICATOR_ICON_X_SIZE 16 // Don't need to care about the Y size due to the origin being on the bottom side. +#define STATUS_INDICATOR_ICON_MARGIN 2 // The space between two status indicators. + +// 'Status indicators' are icons that display over a mob's head, that visually indicate that the mob is suffering +// from some kind of effect, such as being stunned, blinded, confused, asleep, etc. +// The icons are managed automatically by the mob itself, so that their positions will shift if another indicator is added, +// and it will try to always be above the mob sprite, even for larger sprites like xenos. + +/mob/living + var/list/status_indicators = null // Will become a list as needed. + +// Adds an icon_state, or image overlay, to the list of indicators to be managed automatically. +// Also initializes the list if one doesn't exist. +/mob/living/proc/add_status_indicator(image/thing) + if(get_status_indicator(thing)) // No duplicates, please. + return + + if(!istype(thing, /image)) + thing = image(icon = 'icons/mob/status_indicators.dmi', icon_state = thing) + + LAZYADD(status_indicators, thing) + handle_status_indicators() + +// Similar to above but removes it instead, and nulls the list if it becomes empty as a result. +/mob/living/proc/remove_status_indicator(image/thing) + thing = get_status_indicator(thing) + + cut_overlay(thing) + LAZYREMOVE(status_indicators, thing) + handle_status_indicators() + +/mob/living/proc/get_status_indicator(image/thing) + if(!istype(thing, /image)) + for(var/image/I in status_indicators) + if(I.icon_state == thing) + return I + return LAZYACCESS(status_indicators, LAZYFIND(status_indicators, thing)) + +// Refreshes the indicators over a mob's head. Should only be called when adding or removing a status indicator with the above procs, +// or when the mob changes size visually for some reason. +/mob/living/proc/handle_status_indicators() + // First, get rid of all the overlays. + for(var/thing in status_indicators) + cut_overlay(thing) + + if(!LAZYLEN(status_indicators)) + return + + if(stat == DEAD) + return + + // Now put them back on in the right spot. + var/our_sprite_x = icon_expected_width * get_icon_scale_x() + var/our_sprite_y = icon_expected_height * get_icon_scale_y() + + var/x_offset = our_sprite_x // Add your own offset here later if you want. + var/y_offset = our_sprite_y + STATUS_INDICATOR_Y_OFFSET + + // Calculates how 'long' the row of indicators and the margin between them should be. + // The goal is to have the center of that row be horizontally aligned with the sprite's center. + var/expected_status_indicator_length = (STATUS_INDICATOR_ICON_X_SIZE * status_indicators.len) + (STATUS_INDICATOR_ICON_MARGIN * max(status_indicators.len - 1, 0)) + var/current_x_position = (x_offset / 2) - (expected_status_indicator_length / 2) + + // In /mob/living's `update_transform()`, the sprite is horizontally shifted when scaled up, so that the center of the sprite doesn't move to the right. + // Because of that, this adjustment needs to happen with the future indicator row as well, or it will look bad. + current_x_position -= (icon_expected_width / 2) * (get_icon_scale_y() - 1) + + // Now the indicator row can actually be built. + for(var/image/I as anything in status_indicators) + + // This is a semi-HUD element, in a similar manner as medHUDs, in that they're 'above' everything else in the world, + // but don't pierce obfuscation layers such as blindness or darkness, unlike actual HUD elements like inventory slots. + I.plane = PLANE_STATUS + I.layer = HUD_LAYER + I.appearance_flags = PIXEL_SCALE|TILE_BOUND|NO_CLIENT_COLOR|RESET_COLOR|RESET_ALPHA|RESET_TRANSFORM|KEEP_APART + I.pixel_y = y_offset + I.pixel_x = current_x_position + add_overlay(I) + // Adding the margin space every time saves a conditional check on the last iteration, + // and it won't cause any issues since no more icons will be added, and the var is not used for anything else. + current_x_position += STATUS_INDICATOR_ICON_X_SIZE + STATUS_INDICATOR_ICON_MARGIN + + +#undef STATUS_INDICATOR_Y_OFFSET +#undef STATUS_INDICATOR_ICON_X_SIZE +#undef STATUS_INDICATOR_ICON_MARGIN diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 099ddfde0f6..14a3c56e8c2 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -1,86 +1,86 @@ -//handles setting lastKnownIP and computer_id for use by the ban systems as well as checking for multikeying -/mob/proc/update_Login_details() - //Multikey checks and logging - lastKnownIP = client.address - computer_id = client.computer_id - log_access_in(client) - if(config.log_access) - for(var/mob/M in player_list) - if(M == src) continue - if( M.key && (M.key != key) ) - var/matches - if( (M.lastKnownIP == client.address) ) - matches += "IP ([client.address])" - if( (client.connection != "web") && (M.computer_id == client.computer_id) ) - if(matches) matches += " and " - matches += "ID ([client.computer_id])" - if(!config.disable_cid_warn_popup) - tgui_alert_async(usr, "You appear to have logged in with another key this round, which is not permitted. Please contact an administrator if you believe this message to be in error.") - if(matches) - if(M.client) - message_admins("[span_red("Notice: ")][span_blue("[key_name_admin(src)] has the same [matches] as [key_name_admin(M)].")]", 1) - log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)].") - else - message_admins("[span_red("Notice: ")][span_blue("[key_name_admin(src)] has the same [matches] as [key_name_admin(M)] (no longer logged in). ")]", 1) - log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)] (no longer logged in).") - -/mob/Login() - - player_list |= src - update_Login_details() - world.update_status() - - client.images = null //remove the images such as AIs being unable to see runes - client.screen = list() //remove hud items just in case - if(hud_used) qdel(hud_used) //remove the hud objects - hud_used = new /datum/hud(src) - - if(client.prefs && client.prefs.client_fps) - client.fps = client.prefs.client_fps - else - client.fps = 0 // Results in using the server FPS - - next_move = 1 - disconnect_time = null //VOREStation Addition: clear the disconnect time - sight |= SEE_SELF - ..() - SEND_SIGNAL(src, COMSIG_MOB_LOGIN) - - if(loc && !isturf(loc)) - client.eye = loc - client.perspective = EYE_PERSPECTIVE - else - client.eye = src - client.perspective = MOB_PERSPECTIVE - add_click_catcher() - update_client_color() - - if(!plane_holder) //Lazy - plane_holder = new(src) //Not a location, it takes it and saves it. - if(!vis_enabled) - vis_enabled = list() - client.screen += plane_holder.plane_masters - recalculate_vis() - - // AO support - var/ao_enabled = client.is_preference_enabled(/datum/client_preference/ambient_occlusion) - plane_holder.set_ao(VIS_OBJS, ao_enabled) - plane_holder.set_ao(VIS_MOBS, ao_enabled) - - // Status indicators - var/status_enabled = client.is_preference_enabled(/datum/client_preference/status_indicators) - plane_holder.set_vis(VIS_STATUS, status_enabled) - - //set macro to normal incase it was overriden (like cyborg currently does) - client.set_hotkeys_macro("macro", "hotkeymode") - - if(!client.tooltips) - client.tooltips = new(client) - - var/turf/T = get_turf(src) - if(isturf(T)) - update_client_z(T.z) - - if(cloaked && cloaked_selfimage) - client.images += cloaked_selfimage - SEND_SIGNAL(src, COMSIG_MOB_CLIENT_LOGIN, client) +//handles setting lastKnownIP and computer_id for use by the ban systems as well as checking for multikeying +/mob/proc/update_Login_details() + //Multikey checks and logging + lastKnownIP = client.address + computer_id = client.computer_id + log_access_in(client) + if(config.log_access) + for(var/mob/M in player_list) + if(M == src) continue + if( M.key && (M.key != key) ) + var/matches + if( (M.lastKnownIP == client.address) ) + matches += "IP ([client.address])" + if( (client.connection != "web") && (M.computer_id == client.computer_id) ) + if(matches) matches += " and " + matches += "ID ([client.computer_id])" + if(!config.disable_cid_warn_popup) + tgui_alert_async(usr, "You appear to have logged in with another key this round, which is not permitted. Please contact an administrator if you believe this message to be in error.") + if(matches) + if(M.client) + message_admins("[span_red("Notice: ")][span_blue("[key_name_admin(src)] has the same [matches] as [key_name_admin(M)].")]", 1) + log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)].") + else + message_admins("[span_red("Notice: ")][span_blue("[key_name_admin(src)] has the same [matches] as [key_name_admin(M)] (no longer logged in). ")]", 1) + log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)] (no longer logged in).") + +/mob/Login() + + player_list |= src + update_Login_details() + world.update_status() + + client.images = null //remove the images such as AIs being unable to see runes + client.screen = list() //remove hud items just in case + if(hud_used) qdel(hud_used) //remove the hud objects + hud_used = new /datum/hud(src) + + if(client.prefs && client.prefs.client_fps) + client.fps = client.prefs.client_fps + else + client.fps = 0 // Results in using the server FPS + + next_move = 1 + disconnect_time = null //VOREStation Addition: clear the disconnect time + sight |= SEE_SELF + ..() + SEND_SIGNAL(src, COMSIG_MOB_LOGIN) + + if(loc && !isturf(loc)) + client.eye = loc + client.perspective = EYE_PERSPECTIVE + else + client.eye = src + client.perspective = MOB_PERSPECTIVE + add_click_catcher() + update_client_color() + + if(!plane_holder) //Lazy + plane_holder = new(src) //Not a location, it takes it and saves it. + if(!vis_enabled) + vis_enabled = list() + client.screen += plane_holder.plane_masters + recalculate_vis() + + // AO support + var/ao_enabled = client.is_preference_enabled(/datum/client_preference/ambient_occlusion) + plane_holder.set_ao(VIS_OBJS, ao_enabled) + plane_holder.set_ao(VIS_MOBS, ao_enabled) + + // Status indicators + var/status_enabled = client.is_preference_enabled(/datum/client_preference/status_indicators) + plane_holder.set_vis(VIS_STATUS, status_enabled) + + //set macro to normal incase it was overriden (like cyborg currently does) + client.set_hotkeys_macro("macro", "hotkeymode") + + if(!client.tooltips) + client.tooltips = new(client) + + var/turf/T = get_turf(src) + if(isturf(T)) + update_client_z(T.z) + + if(cloaked && cloaked_selfimage) + client.images += cloaked_selfimage + SEND_SIGNAL(src, COMSIG_MOB_CLIENT_LOGIN, client) diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index d965956dfe4..44cd61f616a 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -1,17 +1,17 @@ -/mob/Logout() - SStgui.on_logout(src) // Cleanup any TGUIs the user has open - player_list -= src - disconnect_time = world.realtime //VOREStation Addition: logging when we disappear. - update_client_z(null) - log_access_out(src) - unset_machine() - if(admin_datums[src.ckey]) - if (ticker && ticker.current_state == GAME_STATE_PLAYING) //Only report this stuff if we are currently playing. - var/admins_number = GLOB.admins.len - - message_admins("Admin logout: [key_name(src)]") - if(admins_number == 0) //Apparently the admin logging out is no longer an admin at this point, so we have to check this towards 0 and not towards 1. Awell. - send2adminirc("[key_name(src)] logged out - no more admins online.") - ..() - +/mob/Logout() + SStgui.on_logout(src) // Cleanup any TGUIs the user has open + player_list -= src + disconnect_time = world.realtime //VOREStation Addition: logging when we disappear. + update_client_z(null) + log_access_out(src) + unset_machine() + if(admin_datums[src.ckey]) + if (ticker && ticker.current_state == GAME_STATE_PLAYING) //Only report this stuff if we are currently playing. + var/admins_number = GLOB.admins.len + + message_admins("Admin logout: [key_name(src)]") + if(admins_number == 0) //Apparently the admin logging out is no longer an admin at this point, so we have to check this towards 0 and not towards 1. Awell. + send2adminirc("[key_name(src)] logged out - no more admins online.") + ..() + return 1 \ No newline at end of file diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index 7292f750352..e0a628eb907 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -1,419 +1,419 @@ -#define UPGRADE_COOLDOWN 40 -#define UPGRADE_KILL_TIMER 100 - -///Process_Grab() -///Called by client/Move() -///Checks to see if you are grabbing or being grabbed by anything and if moving will affect your grab. -/client/proc/Process_Grab() - //if we are being grabbed - if(isliving(mob)) - var/mob/living/L = mob - if(!L.canmove && L.grabbed_by.len) - L.resist() //shortcut for resisting grabs - - //if we are grabbing someone - for(var/obj/item/weapon/grab/G in list(L.l_hand, L.r_hand)) - G.reset_kill_state() //no wandering across the station/asteroid while choking someone - -/obj/item/weapon/grab - name = "grab" - icon = 'icons/mob/screen1.dmi' - icon_state = "reinforce" - flags = 0 - var/obj/screen/grab/hud = null - var/mob/living/affecting = null - var/mob/living/carbon/human/assailant = null - var/state = GRAB_PASSIVE - - var/allow_upgrade = 1 - var/last_action = 0 - var/last_hit_zone = 0 - var/force_down //determines if the affecting mob will be pinned to the ground - var/dancing //determines if assailant and affecting keep looking at each other. Basically a wrestling position - - abstract = 1 - item_state = "nothing" - w_class = ITEMSIZE_HUGE - destroy_on_drop = TRUE //VOREStation Edit - - -/obj/item/weapon/grab/New(mob/user, mob/victim) - ..() - loc = user - assailant = user - affecting = victim - - if(affecting.anchored || !assailant.Adjacent(victim)) - qdel(src) - return - - affecting.grabbed_by += src - affecting.reveal("You are revealed as [assailant] grabs you.") - assailant.reveal("You reveal yourself as you grab [affecting].") - - hud = new /obj/screen/grab(src) - hud.icon_state = "reinforce" - icon_state = "grabbed" - hud.name = "reinforce grab" - hud.master = src - - //check if assailant is grabbed by victim as well - if(assailant.grabbed_by) - for (var/obj/item/weapon/grab/G in assailant.grabbed_by) - if(G.assailant == affecting && G.affecting == assailant) - G.dancing = 1 - G.adjust_position() - dancing = 1 - - //stop pulling the affected - if(assailant.pulling == affecting) - assailant.stop_pulling() - - adjust_position() - - -//Used by throw code to hand over the mob, instead of throwing the grab. The grab is then deleted by the throw code. -/obj/item/weapon/grab/proc/throw_held() - if(affecting) - if(affecting.buckled) - return null - if(state >= GRAB_AGGRESSIVE) - animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 4, 1) - return affecting - - return null - - -//This makes sure that the grab screen object is displayed in the correct hand. -/obj/item/weapon/grab/proc/synch() //why is this needed? - if(QDELETED(src)) - return - if(affecting) - if(assailant.r_hand == src) - hud.screen_loc = ui_rhand - else - hud.screen_loc = ui_lhand - -/obj/item/weapon/grab/process() - if(QDELETED(src)) // GC is trying to delete us, we'll kill our processing so we can cleanly GC - return PROCESS_KILL - - confirm() - if(!assailant) - qdel(src) // Same here, except we're trying to delete ourselves. - return PROCESS_KILL - - if(assailant.client) - assailant.client.screen -= hud - assailant.client.screen += hud - - if(state <= GRAB_AGGRESSIVE) - allow_upgrade = 1 - //disallow upgrading if we're grabbing more than one person - if((assailant.l_hand && assailant.l_hand != src && istype(assailant.l_hand, /obj/item/weapon/grab))) - var/obj/item/weapon/grab/G = assailant.l_hand - if(G.affecting != affecting) - allow_upgrade = 0 - if((assailant.r_hand && assailant.r_hand != src && istype(assailant.r_hand, /obj/item/weapon/grab))) - var/obj/item/weapon/grab/G = assailant.r_hand - if(G.affecting != affecting) - allow_upgrade = 0 - - //disallow upgrading past aggressive if we're being grabbed aggressively - for(var/obj/item/weapon/grab/G in affecting.grabbed_by) - if(G == src) continue - if(G.state >= GRAB_AGGRESSIVE) - allow_upgrade = 0 - - if(allow_upgrade) - if(state < GRAB_AGGRESSIVE) - hud.icon_state = "reinforce" - else - hud.icon_state = "reinforce1" - else - hud.icon_state = "!reinforce" - - if(state >= GRAB_AGGRESSIVE) - affecting.drop_l_hand() - affecting.drop_r_hand() - - if(iscarbon(affecting)) - handle_eye_mouth_covering(affecting, assailant, assailant.zone_sel.selecting) - - if(force_down) - if(affecting.loc != assailant.loc || size_difference(affecting, assailant) > 0) - force_down = 0 - else - affecting.Weaken(2) - - if(state >= GRAB_NECK) - affecting.Stun(3) - if(isliving(affecting)) - var/mob/living/L = affecting - L.adjustOxyLoss(1) - - if(state >= GRAB_KILL) - //affecting.apply_effect(STUTTER, 5) //would do this, but affecting isn't declared as mob/living for some stupid reason. - affecting.stuttering = max(affecting.stuttering, 5) //It will hamper your voice, being choked and all. - affecting.Weaken(5) //Should keep you down unless you get help. - affecting.losebreath = max(affecting.losebreath + 2, 3) - - adjust_position() - -/obj/item/weapon/grab/proc/handle_eye_mouth_covering(mob/living/carbon/target, mob/user, var/target_zone) - var/announce = (target_zone != last_hit_zone) //only display messages when switching between different target zones - last_hit_zone = target_zone - - switch(target_zone) - if(O_MOUTH) - if(announce) - user.visible_message("\The [user] covers [target]'s mouth!") - if(target.silent < 3) - target.silent = 3 - if(O_EYES) - if(announce) - assailant.visible_message("[assailant] covers [affecting]'s eyes!") - if(affecting.eye_blind < 3) - affecting.Blind(3) - /*YW Change start, Nope - //VOREStation Edit - if(BP_HEAD) - if(force_down) - if(user.a_intent == I_HELP) - if(announce) - assailant.visible_message("[assailant] sits on [target]'s face!") - //VOREStation Edit End - YW Change stop*/ - -/obj/item/weapon/grab/attack_self() - return s_click(hud) - - -//Updating pixelshift, position and direction -//Gets called on process, when the grab gets upgraded or the assailant moves -/obj/item/weapon/grab/proc/adjust_position() - if(!affecting) - qdel(src) - return - if(affecting.buckled) - animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 4, 1, LINEAR_EASING) - return - if(affecting.lying && state != GRAB_KILL) - animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 5, 1, LINEAR_EASING) - if(force_down) - affecting.set_dir(SOUTH) //face up - return - var/shift = 0 - var/adir = get_dir(assailant, affecting) - affecting.layer = MOB_LAYER - switch(state) - if(GRAB_PASSIVE) - shift = 8 - if(dancing) //look at partner - shift = 10 - assailant.set_dir(get_dir(assailant, affecting)) - if(GRAB_AGGRESSIVE) - shift = 12 - if(GRAB_NECK, GRAB_UPGRADING) - shift = -10 - adir = assailant.dir - affecting.set_dir(assailant.dir) - affecting.loc = assailant.loc - if(GRAB_KILL) - shift = 0 - adir = 1 - affecting.set_dir(SOUTH) //face up - affecting.loc = assailant.loc - - switch(adir) - if(NORTH) - animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y =-shift, 5, 1, LINEAR_EASING) - affecting.layer = BELOW_MOB_LAYER - if(SOUTH) - animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = shift, 5, 1, LINEAR_EASING) - if(WEST) - animate(affecting, pixel_x = shift, pixel_y = initial(affecting.pixel_y), 5, 1, LINEAR_EASING) - if(EAST) - animate(affecting, pixel_x =-shift, pixel_y = initial(affecting.pixel_y), 5, 1, LINEAR_EASING) - -/obj/item/weapon/grab/proc/s_click(obj/screen/S) - if(QDELETED(src)) - return - if(!affecting) - return - if(state == GRAB_UPGRADING) - return - if(world.time < (last_action + UPGRADE_COOLDOWN)) - return - if(!assailant.canmove || assailant.lying) - qdel(src) - return - - var/datum/gender/TU = gender_datums[assailant.get_visible_gender()] - - last_action = world.time - - if(state < GRAB_AGGRESSIVE) - if(!allow_upgrade) - return - if(!affecting.lying || size_difference(affecting, assailant) > 0) - assailant.visible_message("[assailant] has grabbed [affecting] aggressively (now hands)!") - else - assailant.visible_message("[assailant] pins [affecting] down to the ground (now hands)!") - apply_pinning(affecting, assailant) - - state = GRAB_AGGRESSIVE - icon_state = "grabbed1" - hud.icon_state = "reinforce1" - add_attack_logs(assailant, affecting, "Aggressively grabbed", FALSE) // Not important enough to notify admins, but still helpful. - else if(state < GRAB_NECK) - if(isslime(affecting)) - to_chat(assailant, "You squeeze [affecting], but nothing interesting happens.") - return - - assailant.visible_message("[assailant] has reinforced [TU.his] grip on [affecting] (now neck)!") - state = GRAB_NECK - icon_state = "grabbed+1" - assailant.set_dir(get_dir(assailant, affecting)) - add_attack_logs(assailant,affecting,"Neck grabbed") - hud.icon_state = "kill" - hud.name = "kill" - affecting.Stun(10) //10 ticks of ensured grab - else if(state < GRAB_UPGRADING) - assailant.visible_message("[assailant] starts to tighten [TU.his] grip on [affecting]'s neck!") - hud.icon_state = "kill1" - - state = GRAB_KILL - assailant.visible_message("[assailant] has tightened [TU.his] grip on [affecting]'s neck!") - add_attack_logs(assailant,affecting,"Strangled") - affecting.setClickCooldown(10) - affecting.AdjustLosebreath(1) - affecting.set_dir(WEST) - adjust_position() - -//This is used to make sure the victim hasn't managed to yackety sax away before using the grab. -/obj/item/weapon/grab/proc/confirm() - if(!assailant || !affecting) - qdel(src) - return 0 - - if(affecting) - if(!isturf(assailant.loc) || ( !isturf(affecting.loc) || assailant.loc != affecting.loc && get_dist(assailant, affecting) > 1) ) - qdel(src) - return 0 - - return 1 - -/obj/item/weapon/grab/attack(mob/M, mob/living/user) - if(QDELETED(src)) - return - if(!affecting) - return - if(world.time < (last_action + 20)) - return - - last_action = world.time - reset_kill_state() //using special grab moves will interrupt choking them - - //clicking on the victim while grabbing them - if(M == affecting) - if(ishuman(affecting)) - var/mob/living/carbon/human/H = affecting - var/hit_zone = assailant.zone_sel.selecting - flick(hud.icon_state, hud) - switch(assailant.a_intent) - if(I_HELP) - if(force_down) - to_chat(assailant, "You are no longer pinning [affecting] to the ground.") - force_down = 0 - return - if(state >= GRAB_AGGRESSIVE) - H.apply_pressure(assailant, hit_zone) - else - inspect_organ(affecting, assailant, hit_zone) - - if(I_GRAB) - jointlock(affecting, assailant, hit_zone) - - if(I_HURT) - if(hit_zone == O_EYES) - attack_eye(affecting, assailant) - else if(hit_zone == BP_HEAD) - headbutt(affecting, assailant) - else - dislocate(affecting, assailant, hit_zone) - - if(I_DISARM) - pin_down(affecting, assailant) - - //clicking on yourself while grabbing them - if(M == assailant && state >= GRAB_AGGRESSIVE) - devour(affecting, assailant) - -/obj/item/weapon/grab/dropped() - loc = null - if(!QDELETED(src)) - qdel(src) - -/obj/item/weapon/grab/proc/reset_kill_state() - if(state == GRAB_KILL) - var/datum/gender/T = gender_datums[assailant.get_visible_gender()] - assailant.visible_message("[assailant] lost [T.his] tight grip on [affecting]'s neck!") - hud.icon_state = "kill" - state = GRAB_NECK - -/obj/item/weapon/grab/proc/handle_resist() - var/grab_name - var/break_strength = 1 - var/list/break_chance_table = list(100) - switch(state) - //if(GRAB_PASSIVE) - - if(GRAB_AGGRESSIVE) - grab_name = "grip" - //Being knocked down makes it harder to break a grab, so it is easier to cuff someone who is down without forcing them into unconsciousness. - if(!affecting.incapacitated(INCAPACITATION_KNOCKDOWN)) - break_strength++ - break_chance_table = list(15, 60, 100) - - if(GRAB_NECK) - grab_name = "headlock" - //If the you move when grabbing someone then it's easier for them to break free. Same if the affected mob is immune to stun. - if(world.time - assailant.l_move_time < 30 || !affecting.stunned) - break_strength++ - break_chance_table = list(3, 18, 45, 100) - - - if(GRAB_KILL) - grab_name = "stranglehold" - break_chance_table = list(5, 20, 40, 80, 100) - - //It's easier to break out of a grab by a smaller mob - break_strength += max(size_difference(affecting, assailant), 0) - - var/break_chance = break_chance_table[CLAMP(break_strength, 1, break_chance_table.len)] - if(prob(break_chance)) - if(state == GRAB_KILL) - reset_kill_state() - return - else if(grab_name) - affecting.visible_message("[affecting] has broken free of [assailant]'s [grab_name]!") - qdel(src) - -//returns the number of size categories between affecting and assailant, rounded. Positive means A is larger than B -/obj/item/weapon/grab/proc/size_difference(mob/A, mob/B) - return mob_size_difference(A.mob_size, B.mob_size) - -/obj/item/weapon/grab/Destroy() - animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 4, 1, LINEAR_EASING) - affecting.reset_plane_and_layer() - if(affecting) - affecting.grabbed_by -= src - affecting = null - if(assailant) - if(assailant.client) - assailant.client.screen -= hud - assailant = null - qdel(hud) - hud = null - return ..() +#define UPGRADE_COOLDOWN 40 +#define UPGRADE_KILL_TIMER 100 + +///Process_Grab() +///Called by client/Move() +///Checks to see if you are grabbing or being grabbed by anything and if moving will affect your grab. +/client/proc/Process_Grab() + //if we are being grabbed + if(isliving(mob)) + var/mob/living/L = mob + if(!L.canmove && L.grabbed_by.len) + L.resist() //shortcut for resisting grabs + + //if we are grabbing someone + for(var/obj/item/weapon/grab/G in list(L.l_hand, L.r_hand)) + G.reset_kill_state() //no wandering across the station/asteroid while choking someone + +/obj/item/weapon/grab + name = "grab" + icon = 'icons/mob/screen1.dmi' + icon_state = "reinforce" + flags = 0 + var/obj/screen/grab/hud = null + var/mob/living/affecting = null + var/mob/living/carbon/human/assailant = null + var/state = GRAB_PASSIVE + + var/allow_upgrade = 1 + var/last_action = 0 + var/last_hit_zone = 0 + var/force_down //determines if the affecting mob will be pinned to the ground + var/dancing //determines if assailant and affecting keep looking at each other. Basically a wrestling position + + abstract = 1 + item_state = "nothing" + w_class = ITEMSIZE_HUGE + destroy_on_drop = TRUE //VOREStation Edit + + +/obj/item/weapon/grab/New(mob/user, mob/victim) + ..() + loc = user + assailant = user + affecting = victim + + if(affecting.anchored || !assailant.Adjacent(victim)) + qdel(src) + return + + affecting.grabbed_by += src + affecting.reveal("You are revealed as [assailant] grabs you.") + assailant.reveal("You reveal yourself as you grab [affecting].") + + hud = new /obj/screen/grab(src) + hud.icon_state = "reinforce" + icon_state = "grabbed" + hud.name = "reinforce grab" + hud.master = src + + //check if assailant is grabbed by victim as well + if(assailant.grabbed_by) + for (var/obj/item/weapon/grab/G in assailant.grabbed_by) + if(G.assailant == affecting && G.affecting == assailant) + G.dancing = 1 + G.adjust_position() + dancing = 1 + + //stop pulling the affected + if(assailant.pulling == affecting) + assailant.stop_pulling() + + adjust_position() + + +//Used by throw code to hand over the mob, instead of throwing the grab. The grab is then deleted by the throw code. +/obj/item/weapon/grab/proc/throw_held() + if(affecting) + if(affecting.buckled) + return null + if(state >= GRAB_AGGRESSIVE) + animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 4, 1) + return affecting + + return null + + +//This makes sure that the grab screen object is displayed in the correct hand. +/obj/item/weapon/grab/proc/synch() //why is this needed? + if(QDELETED(src)) + return + if(affecting) + if(assailant.r_hand == src) + hud.screen_loc = ui_rhand + else + hud.screen_loc = ui_lhand + +/obj/item/weapon/grab/process() + if(QDELETED(src)) // GC is trying to delete us, we'll kill our processing so we can cleanly GC + return PROCESS_KILL + + confirm() + if(!assailant) + qdel(src) // Same here, except we're trying to delete ourselves. + return PROCESS_KILL + + if(assailant.client) + assailant.client.screen -= hud + assailant.client.screen += hud + + if(state <= GRAB_AGGRESSIVE) + allow_upgrade = 1 + //disallow upgrading if we're grabbing more than one person + if((assailant.l_hand && assailant.l_hand != src && istype(assailant.l_hand, /obj/item/weapon/grab))) + var/obj/item/weapon/grab/G = assailant.l_hand + if(G.affecting != affecting) + allow_upgrade = 0 + if((assailant.r_hand && assailant.r_hand != src && istype(assailant.r_hand, /obj/item/weapon/grab))) + var/obj/item/weapon/grab/G = assailant.r_hand + if(G.affecting != affecting) + allow_upgrade = 0 + + //disallow upgrading past aggressive if we're being grabbed aggressively + for(var/obj/item/weapon/grab/G in affecting.grabbed_by) + if(G == src) continue + if(G.state >= GRAB_AGGRESSIVE) + allow_upgrade = 0 + + if(allow_upgrade) + if(state < GRAB_AGGRESSIVE) + hud.icon_state = "reinforce" + else + hud.icon_state = "reinforce1" + else + hud.icon_state = "!reinforce" + + if(state >= GRAB_AGGRESSIVE) + affecting.drop_l_hand() + affecting.drop_r_hand() + + if(iscarbon(affecting)) + handle_eye_mouth_covering(affecting, assailant, assailant.zone_sel.selecting) + + if(force_down) + if(affecting.loc != assailant.loc || size_difference(affecting, assailant) > 0) + force_down = 0 + else + affecting.Weaken(2) + + if(state >= GRAB_NECK) + affecting.Stun(3) + if(isliving(affecting)) + var/mob/living/L = affecting + L.adjustOxyLoss(1) + + if(state >= GRAB_KILL) + //affecting.apply_effect(STUTTER, 5) //would do this, but affecting isn't declared as mob/living for some stupid reason. + affecting.stuttering = max(affecting.stuttering, 5) //It will hamper your voice, being choked and all. + affecting.Weaken(5) //Should keep you down unless you get help. + affecting.losebreath = max(affecting.losebreath + 2, 3) + + adjust_position() + +/obj/item/weapon/grab/proc/handle_eye_mouth_covering(mob/living/carbon/target, mob/user, var/target_zone) + var/announce = (target_zone != last_hit_zone) //only display messages when switching between different target zones + last_hit_zone = target_zone + + switch(target_zone) + if(O_MOUTH) + if(announce) + user.visible_message("\The [user] covers [target]'s mouth!") + if(target.silent < 3) + target.silent = 3 + if(O_EYES) + if(announce) + assailant.visible_message("[assailant] covers [affecting]'s eyes!") + if(affecting.eye_blind < 3) + affecting.Blind(3) + /*YW Change start, Nope + //VOREStation Edit + if(BP_HEAD) + if(force_down) + if(user.a_intent == I_HELP) + if(announce) + assailant.visible_message("[assailant] sits on [target]'s face!") + //VOREStation Edit End + YW Change stop*/ + +/obj/item/weapon/grab/attack_self() + return s_click(hud) + + +//Updating pixelshift, position and direction +//Gets called on process, when the grab gets upgraded or the assailant moves +/obj/item/weapon/grab/proc/adjust_position() + if(!affecting) + qdel(src) + return + if(affecting.buckled) + animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 4, 1, LINEAR_EASING) + return + if(affecting.lying && state != GRAB_KILL) + animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 5, 1, LINEAR_EASING) + if(force_down) + affecting.set_dir(SOUTH) //face up + return + var/shift = 0 + var/adir = get_dir(assailant, affecting) + affecting.layer = MOB_LAYER + switch(state) + if(GRAB_PASSIVE) + shift = 8 + if(dancing) //look at partner + shift = 10 + assailant.set_dir(get_dir(assailant, affecting)) + if(GRAB_AGGRESSIVE) + shift = 12 + if(GRAB_NECK, GRAB_UPGRADING) + shift = -10 + adir = assailant.dir + affecting.set_dir(assailant.dir) + affecting.loc = assailant.loc + if(GRAB_KILL) + shift = 0 + adir = 1 + affecting.set_dir(SOUTH) //face up + affecting.loc = assailant.loc + + switch(adir) + if(NORTH) + animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y =-shift, 5, 1, LINEAR_EASING) + affecting.layer = BELOW_MOB_LAYER + if(SOUTH) + animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = shift, 5, 1, LINEAR_EASING) + if(WEST) + animate(affecting, pixel_x = shift, pixel_y = initial(affecting.pixel_y), 5, 1, LINEAR_EASING) + if(EAST) + animate(affecting, pixel_x =-shift, pixel_y = initial(affecting.pixel_y), 5, 1, LINEAR_EASING) + +/obj/item/weapon/grab/proc/s_click(obj/screen/S) + if(QDELETED(src)) + return + if(!affecting) + return + if(state == GRAB_UPGRADING) + return + if(world.time < (last_action + UPGRADE_COOLDOWN)) + return + if(!assailant.canmove || assailant.lying) + qdel(src) + return + + var/datum/gender/TU = gender_datums[assailant.get_visible_gender()] + + last_action = world.time + + if(state < GRAB_AGGRESSIVE) + if(!allow_upgrade) + return + if(!affecting.lying || size_difference(affecting, assailant) > 0) + assailant.visible_message("[assailant] has grabbed [affecting] aggressively (now hands)!") + else + assailant.visible_message("[assailant] pins [affecting] down to the ground (now hands)!") + apply_pinning(affecting, assailant) + + state = GRAB_AGGRESSIVE + icon_state = "grabbed1" + hud.icon_state = "reinforce1" + add_attack_logs(assailant, affecting, "Aggressively grabbed", FALSE) // Not important enough to notify admins, but still helpful. + else if(state < GRAB_NECK) + if(isslime(affecting)) + to_chat(assailant, "You squeeze [affecting], but nothing interesting happens.") + return + + assailant.visible_message("[assailant] has reinforced [TU.his] grip on [affecting] (now neck)!") + state = GRAB_NECK + icon_state = "grabbed+1" + assailant.set_dir(get_dir(assailant, affecting)) + add_attack_logs(assailant,affecting,"Neck grabbed") + hud.icon_state = "kill" + hud.name = "kill" + affecting.Stun(10) //10 ticks of ensured grab + else if(state < GRAB_UPGRADING) + assailant.visible_message("[assailant] starts to tighten [TU.his] grip on [affecting]'s neck!") + hud.icon_state = "kill1" + + state = GRAB_KILL + assailant.visible_message("[assailant] has tightened [TU.his] grip on [affecting]'s neck!") + add_attack_logs(assailant,affecting,"Strangled") + affecting.setClickCooldown(10) + affecting.AdjustLosebreath(1) + affecting.set_dir(WEST) + adjust_position() + +//This is used to make sure the victim hasn't managed to yackety sax away before using the grab. +/obj/item/weapon/grab/proc/confirm() + if(!assailant || !affecting) + qdel(src) + return 0 + + if(affecting) + if(!isturf(assailant.loc) || ( !isturf(affecting.loc) || assailant.loc != affecting.loc && get_dist(assailant, affecting) > 1) ) + qdel(src) + return 0 + + return 1 + +/obj/item/weapon/grab/attack(mob/M, mob/living/user) + if(QDELETED(src)) + return + if(!affecting) + return + if(world.time < (last_action + 20)) + return + + last_action = world.time + reset_kill_state() //using special grab moves will interrupt choking them + + //clicking on the victim while grabbing them + if(M == affecting) + if(ishuman(affecting)) + var/mob/living/carbon/human/H = affecting + var/hit_zone = assailant.zone_sel.selecting + flick(hud.icon_state, hud) + switch(assailant.a_intent) + if(I_HELP) + if(force_down) + to_chat(assailant, "You are no longer pinning [affecting] to the ground.") + force_down = 0 + return + if(state >= GRAB_AGGRESSIVE) + H.apply_pressure(assailant, hit_zone) + else + inspect_organ(affecting, assailant, hit_zone) + + if(I_GRAB) + jointlock(affecting, assailant, hit_zone) + + if(I_HURT) + if(hit_zone == O_EYES) + attack_eye(affecting, assailant) + else if(hit_zone == BP_HEAD) + headbutt(affecting, assailant) + else + dislocate(affecting, assailant, hit_zone) + + if(I_DISARM) + pin_down(affecting, assailant) + + //clicking on yourself while grabbing them + if(M == assailant && state >= GRAB_AGGRESSIVE) + devour(affecting, assailant) + +/obj/item/weapon/grab/dropped() + loc = null + if(!QDELETED(src)) + qdel(src) + +/obj/item/weapon/grab/proc/reset_kill_state() + if(state == GRAB_KILL) + var/datum/gender/T = gender_datums[assailant.get_visible_gender()] + assailant.visible_message("[assailant] lost [T.his] tight grip on [affecting]'s neck!") + hud.icon_state = "kill" + state = GRAB_NECK + +/obj/item/weapon/grab/proc/handle_resist() + var/grab_name + var/break_strength = 1 + var/list/break_chance_table = list(100) + switch(state) + //if(GRAB_PASSIVE) + + if(GRAB_AGGRESSIVE) + grab_name = "grip" + //Being knocked down makes it harder to break a grab, so it is easier to cuff someone who is down without forcing them into unconsciousness. + if(!affecting.incapacitated(INCAPACITATION_KNOCKDOWN)) + break_strength++ + break_chance_table = list(15, 60, 100) + + if(GRAB_NECK) + grab_name = "headlock" + //If the you move when grabbing someone then it's easier for them to break free. Same if the affected mob is immune to stun. + if(world.time - assailant.l_move_time < 30 || !affecting.stunned) + break_strength++ + break_chance_table = list(3, 18, 45, 100) + + + if(GRAB_KILL) + grab_name = "stranglehold" + break_chance_table = list(5, 20, 40, 80, 100) + + //It's easier to break out of a grab by a smaller mob + break_strength += max(size_difference(affecting, assailant), 0) + + var/break_chance = break_chance_table[CLAMP(break_strength, 1, break_chance_table.len)] + if(prob(break_chance)) + if(state == GRAB_KILL) + reset_kill_state() + return + else if(grab_name) + affecting.visible_message("[affecting] has broken free of [assailant]'s [grab_name]!") + qdel(src) + +//returns the number of size categories between affecting and assailant, rounded. Positive means A is larger than B +/obj/item/weapon/grab/proc/size_difference(mob/A, mob/B) + return mob_size_difference(A.mob_size, B.mob_size) + +/obj/item/weapon/grab/Destroy() + animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 4, 1, LINEAR_EASING) + affecting.reset_plane_and_layer() + if(affecting) + affecting.grabbed_by -= src + affecting = null + if(assailant) + if(assailant.client) + assailant.client.screen -= hud + assailant = null + qdel(hud) + hud = null + return ..() diff --git a/code/modules/mob/mob_grab_specials.dm b/code/modules/mob/mob_grab_specials.dm index 4ac26e4e138..32ef25f4326 100644 --- a/code/modules/mob/mob_grab_specials.dm +++ b/code/modules/mob/mob_grab_specials.dm @@ -1,173 +1,173 @@ -/obj/item/weapon/grab/proc/inspect_organ(mob/living/carbon/human/H, mob/user, var/target_zone) - - var/obj/item/organ/external/E = H.get_organ(target_zone) - - if(!E || E.is_stump()) - to_chat(user, "[H] is missing that bodypart.") - return - - user.visible_message("[user] starts inspecting [affecting]'s [E.name] carefully.") - if(!do_mob(user,H, 10)) - to_chat(user, "You must stand still to inspect [E] for wounds.") - else if(E.wounds.len) - to_chat(user, "You find [E.get_wounds_desc()]") - else - to_chat(user, "You find no visible wounds.") - - to_chat(user, "Checking bones now...") - if(!do_mob(user, H, 20)) - to_chat(user, "You must stand still to feel [E] for fractures.") - else if(E.status & ORGAN_BROKEN) - to_chat(user, "The [E.encased ? E.encased : "bone in the [E.name]"] moves slightly when you poke it!") - H.custom_pain("Your [E.name] hurts where it's poked.", 40) - else - to_chat(user, "The [E.encased ? E.encased : "bones in the [E.name]"] seem to be fine.") - - to_chat(user, "Checking skin now...") - if(!do_mob(user, H, 10)) - to_chat(user, "You must stand still to check [H]'s skin for abnormalities.") - else - var/bad = 0 - if(H.getToxLoss() >= 40) - to_chat(user, "[H] has an unhealthy skin discoloration.") - bad = 1 - if(H.getOxyLoss() >= 20) - to_chat(user, "[H]'s skin is unusaly pale.") - bad = 1 - if(E.status & ORGAN_DEAD) - to_chat(user, "[E] is decaying!") - bad = 1 - if(!bad) - to_chat(user, "[H]'s skin is normal.") - -/obj/item/weapon/grab/proc/jointlock(mob/living/carbon/human/target, mob/attacker, var/target_zone) - if(state < GRAB_AGGRESSIVE) - to_chat(attacker, "You require a better grab to do this.") - return - - var/obj/item/organ/external/organ = target.get_organ(check_zone(target_zone)) - if(!organ || organ.dislocated == -1) - return - - attacker.visible_message("[attacker] [pick("bent", "twisted")] [target]'s [organ.name] into a jointlock!") - - if(target.species.flags & NO_PAIN) - return - - var/armor = target.run_armor_check(target, "melee") - var/soaked = target.get_armor_soak(target, "melee") - if(armor + soaked < 60) - to_chat(target, "You feel extreme pain!") - - var/max_halloss = round(target.species.total_health * 0.8) //up to 80% of passing out - affecting.adjustHalLoss(CLAMP(max_halloss - affecting.halloss, 0, 30)) - -/obj/item/weapon/grab/proc/attack_eye(mob/living/carbon/human/target, mob/living/carbon/human/attacker) - if(!istype(attacker)) - return - - var/datum/unarmed_attack/attack = attacker.get_unarmed_attack(target, O_EYES) - - if(!attack) - return - if(state < GRAB_NECK) - to_chat(attacker, "You require a better grab to do this.") - return - for(var/obj/item/protection in list(target.head, target.wear_mask, target.glasses)) - if(protection && (protection.body_parts_covered & EYES)) - to_chat(attacker, "You're going to need to remove the eye covering first.") - return - if(!target.has_eyes()) - to_chat(attacker, "You cannot locate any eyes on [target]!") - return - - add_attack_logs(attacker,target,"Eye gouge using grab") - - attack.handle_eye_attack(attacker, target) - -/obj/item/weapon/grab/proc/headbutt(mob/living/carbon/human/target, mob/living/carbon/human/attacker) - if(!istype(attacker)) - return - if(target.lying) - return - var/datum/gender/T = gender_datums[attacker.get_visible_gender()] - attacker.visible_message("[attacker] thrusts [T.his] head into [target]'s skull!") - - var/damage = 20 - var/obj/item/clothing/hat = attacker.head - if(istype(hat)) - damage += hat.force * 3 - - var/armor = target.run_armor_check(BP_HEAD, "melee") - var/soaked = target.get_armor_soak(BP_HEAD, "melee") - target.apply_damage(damage, BRUTE, BP_HEAD, armor, soaked) - attacker.apply_damage(10, BRUTE, BP_HEAD, attacker.run_armor_check(BP_HEAD), attacker.get_armor_soak(BP_HEAD), "melee") - - if(!armor && target.headcheck(BP_HEAD) && prob(damage)) - target.apply_effect(20, PARALYZE) - target.visible_message("[target] [target.species.get_knockout_message(target)]") - - playsound(attacker, "swing_hit", 25, 1, -1) - add_attack_logs(attacker,target,"Headbutted using grab") - - attacker.drop_from_inventory(src) - src.loc = null - qdel(src) - return - -/obj/item/weapon/grab/proc/dislocate(mob/living/carbon/human/target, mob/living/attacker, var/target_zone) - if(state < GRAB_NECK) - to_chat(attacker, "You require a better grab to do this.") - return - if(target.grab_joint(attacker, target_zone)) - playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - return - -/obj/item/weapon/grab/proc/pin_down(mob/target, mob/attacker) - if(state < GRAB_AGGRESSIVE) - to_chat(attacker, "You require a better grab to do this.") - return - if(force_down) - to_chat(attacker, "You are already pinning [target] to the ground.") - return - if(size_difference(affecting, assailant) > 0) - to_chat(attacker, "You are too small to do that!") - return - - attacker.visible_message("[attacker] starts forcing [target] to the ground!") - if(do_after(attacker, 20) && target) - last_action = world.time - attacker.visible_message("[attacker] forces [target] to the ground!") - apply_pinning(target, attacker) - -/obj/item/weapon/grab/proc/apply_pinning(mob/target, mob/attacker) - force_down = 1 - target.Weaken(3) - target.lying = 1 - step_to(attacker, target) - attacker.set_dir(EAST) //face the victim - target.set_dir(SOUTH) //face up - -/obj/item/weapon/grab/proc/devour(mob/target, mob/user) - var/can_eat - if((FAT in user.mutations) && ismini(target)) - can_eat = 1 - else - var/mob/living/carbon/human/H = user - if(istype(H) && H.species.gluttonous) - if(H.species.gluttonous == 2) - can_eat = 2 - else if((H.mob_size > target.mob_size) && !ishuman(target) && ismini(target)) - can_eat = 1 - - if(can_eat) - var/mob/living/carbon/attacker = user - user.visible_message("[user] is attempting to devour [target]!") - if(can_eat == 2) - if(!do_mob(user, target)||!do_after(user, 30)) return - else - if(!do_mob(user, target)||!do_after(user, 70)) return - user.visible_message("[user] devours [target]!") - target.loc = user - attacker.stomach_contents.Add(target) - qdel(src) +/obj/item/weapon/grab/proc/inspect_organ(mob/living/carbon/human/H, mob/user, var/target_zone) + + var/obj/item/organ/external/E = H.get_organ(target_zone) + + if(!E || E.is_stump()) + to_chat(user, "[H] is missing that bodypart.") + return + + user.visible_message("[user] starts inspecting [affecting]'s [E.name] carefully.") + if(!do_mob(user,H, 10)) + to_chat(user, "You must stand still to inspect [E] for wounds.") + else if(E.wounds.len) + to_chat(user, "You find [E.get_wounds_desc()]") + else + to_chat(user, "You find no visible wounds.") + + to_chat(user, "Checking bones now...") + if(!do_mob(user, H, 20)) + to_chat(user, "You must stand still to feel [E] for fractures.") + else if(E.status & ORGAN_BROKEN) + to_chat(user, "The [E.encased ? E.encased : "bone in the [E.name]"] moves slightly when you poke it!") + H.custom_pain("Your [E.name] hurts where it's poked.", 40) + else + to_chat(user, "The [E.encased ? E.encased : "bones in the [E.name]"] seem to be fine.") + + to_chat(user, "Checking skin now...") + if(!do_mob(user, H, 10)) + to_chat(user, "You must stand still to check [H]'s skin for abnormalities.") + else + var/bad = 0 + if(H.getToxLoss() >= 40) + to_chat(user, "[H] has an unhealthy skin discoloration.") + bad = 1 + if(H.getOxyLoss() >= 20) + to_chat(user, "[H]'s skin is unusaly pale.") + bad = 1 + if(E.status & ORGAN_DEAD) + to_chat(user, "[E] is decaying!") + bad = 1 + if(!bad) + to_chat(user, "[H]'s skin is normal.") + +/obj/item/weapon/grab/proc/jointlock(mob/living/carbon/human/target, mob/attacker, var/target_zone) + if(state < GRAB_AGGRESSIVE) + to_chat(attacker, "You require a better grab to do this.") + return + + var/obj/item/organ/external/organ = target.get_organ(check_zone(target_zone)) + if(!organ || organ.dislocated == -1) + return + + attacker.visible_message("[attacker] [pick("bent", "twisted")] [target]'s [organ.name] into a jointlock!") + + if(target.species.flags & NO_PAIN) + return + + var/armor = target.run_armor_check(target, "melee") + var/soaked = target.get_armor_soak(target, "melee") + if(armor + soaked < 60) + to_chat(target, "You feel extreme pain!") + + var/max_halloss = round(target.species.total_health * 0.8) //up to 80% of passing out + affecting.adjustHalLoss(CLAMP(max_halloss - affecting.halloss, 0, 30)) + +/obj/item/weapon/grab/proc/attack_eye(mob/living/carbon/human/target, mob/living/carbon/human/attacker) + if(!istype(attacker)) + return + + var/datum/unarmed_attack/attack = attacker.get_unarmed_attack(target, O_EYES) + + if(!attack) + return + if(state < GRAB_NECK) + to_chat(attacker, "You require a better grab to do this.") + return + for(var/obj/item/protection in list(target.head, target.wear_mask, target.glasses)) + if(protection && (protection.body_parts_covered & EYES)) + to_chat(attacker, "You're going to need to remove the eye covering first.") + return + if(!target.has_eyes()) + to_chat(attacker, "You cannot locate any eyes on [target]!") + return + + add_attack_logs(attacker,target,"Eye gouge using grab") + + attack.handle_eye_attack(attacker, target) + +/obj/item/weapon/grab/proc/headbutt(mob/living/carbon/human/target, mob/living/carbon/human/attacker) + if(!istype(attacker)) + return + if(target.lying) + return + var/datum/gender/T = gender_datums[attacker.get_visible_gender()] + attacker.visible_message("[attacker] thrusts [T.his] head into [target]'s skull!") + + var/damage = 20 + var/obj/item/clothing/hat = attacker.head + if(istype(hat)) + damage += hat.force * 3 + + var/armor = target.run_armor_check(BP_HEAD, "melee") + var/soaked = target.get_armor_soak(BP_HEAD, "melee") + target.apply_damage(damage, BRUTE, BP_HEAD, armor, soaked) + attacker.apply_damage(10, BRUTE, BP_HEAD, attacker.run_armor_check(BP_HEAD), attacker.get_armor_soak(BP_HEAD), "melee") + + if(!armor && target.headcheck(BP_HEAD) && prob(damage)) + target.apply_effect(20, PARALYZE) + target.visible_message("[target] [target.species.get_knockout_message(target)]") + + playsound(attacker, "swing_hit", 25, 1, -1) + add_attack_logs(attacker,target,"Headbutted using grab") + + attacker.drop_from_inventory(src) + src.loc = null + qdel(src) + return + +/obj/item/weapon/grab/proc/dislocate(mob/living/carbon/human/target, mob/living/attacker, var/target_zone) + if(state < GRAB_NECK) + to_chat(attacker, "You require a better grab to do this.") + return + if(target.grab_joint(attacker, target_zone)) + playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + return + +/obj/item/weapon/grab/proc/pin_down(mob/target, mob/attacker) + if(state < GRAB_AGGRESSIVE) + to_chat(attacker, "You require a better grab to do this.") + return + if(force_down) + to_chat(attacker, "You are already pinning [target] to the ground.") + return + if(size_difference(affecting, assailant) > 0) + to_chat(attacker, "You are too small to do that!") + return + + attacker.visible_message("[attacker] starts forcing [target] to the ground!") + if(do_after(attacker, 20) && target) + last_action = world.time + attacker.visible_message("[attacker] forces [target] to the ground!") + apply_pinning(target, attacker) + +/obj/item/weapon/grab/proc/apply_pinning(mob/target, mob/attacker) + force_down = 1 + target.Weaken(3) + target.lying = 1 + step_to(attacker, target) + attacker.set_dir(EAST) //face the victim + target.set_dir(SOUTH) //face up + +/obj/item/weapon/grab/proc/devour(mob/target, mob/user) + var/can_eat + if((FAT in user.mutations) && ismini(target)) + can_eat = 1 + else + var/mob/living/carbon/human/H = user + if(istype(H) && H.species.gluttonous) + if(H.species.gluttonous == 2) + can_eat = 2 + else if((H.mob_size > target.mob_size) && !ishuman(target) && ismini(target)) + can_eat = 1 + + if(can_eat) + var/mob/living/carbon/attacker = user + user.visible_message("[user] is attempting to devour [target]!") + if(can_eat == 2) + if(!do_mob(user, target)||!do_after(user, 30)) return + else + if(!do_mob(user, target)||!do_after(user, 70)) return + user.visible_message("[user] devours [target]!") + target.loc = user + attacker.stomach_contents.Add(target) + qdel(src) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index b938f0098d0..797854a283d 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -1,694 +1,694 @@ -// fun if you want to typecast humans/monkeys/etc without writing long path-filled lines. -/proc/isxenomorph(A) - if(istype(A, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = A - return istype(H.species, /datum/species/xenos) - return 0 - -/proc/issmall(A) - if(A && istype(A, /mob/living)) - var/mob/living/L = A - return L.mob_size <= MOB_SMALL - return 0 - -//returns the number of size categories between two mob_sizes, rounded. Positive means A is larger than B -/proc/mob_size_difference(var/mob_size_A, var/mob_size_B) - return round(log(2, mob_size_A/mob_size_B), 1) - -/mob/proc/can_wield_item(obj/item/W) - if(W.w_class >= ITEMSIZE_LARGE && issmall(src)) - return FALSE //M is too small to wield this - return TRUE - -/proc/istiny(A) - if(A && istype(A, /mob/living)) - var/mob/living/L = A - return L.mob_size <= MOB_TINY - return 0 - - -/proc/ismini(A) - if(A && istype(A, /mob/living)) - var/mob/living/L = A - return L.mob_size <= MOB_MINISCULE - return 0 - -/mob/living/silicon/isSynthetic() - return 1 - -/mob/proc/isMonkey() - return 0 - -/mob/living/carbon/human/isMonkey() - return istype(species, /datum/species/monkey) - -/proc/isdeaf(A) - if(istype(A, /mob)) - var/mob/M = A - return (M.sdisabilities & DEAF) || M.ear_deaf - return 0 - -/mob/proc/get_ear_protection() - return 0 - -/mob/proc/break_cloak() - return - -/mob/proc/is_cloaked() - return FALSE - -/proc/hasorgans(A) // Fucking really?? - return ishuman(A) - -/proc/iscuffed(A) - if(istype(A, /mob/living/carbon)) - var/mob/living/carbon/C = A - if(C.handcuffed) - return 1 - return 0 - -/proc/hassensorlevel(A, var/level) - var/mob/living/carbon/human/H = A - if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = H.w_uniform - return U.sensor_mode >= level - return 0 - -/proc/getsensorlevel(A) - var/mob/living/carbon/human/H = A - if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = H.w_uniform - return U.sensor_mode - return SUIT_SENSOR_OFF - - -/proc/is_admin(var/mob/user) - return check_rights(R_ADMIN|R_EVENT, 0, user) != 0 - - -/proc/hsl2rgb(h, s, l) - return //TODO: Implement - -/* - Miss Chance -*/ - -/proc/check_zone(zone) - if(!zone) return BP_TORSO - switch(zone) - if(O_EYES) - zone = BP_HEAD - if(O_MOUTH) - zone = BP_HEAD - return zone - -// Returns zone with a certain probability. If the probability fails, or no zone is specified, then a random body part is chosen. -// Do not use this if someone is intentionally trying to hit a specific body part. -// Use get_zone_with_miss_chance() for that. -/proc/ran_zone(zone, probability) - if (zone) - zone = check_zone(zone) - if (prob(probability)) - return zone - - var/ran_zone = zone - while (ran_zone == zone) - ran_zone = pick ( - organ_rel_size[BP_HEAD]; BP_HEAD, - organ_rel_size[BP_TORSO]; BP_TORSO, - organ_rel_size[BP_GROIN]; BP_GROIN, - organ_rel_size[BP_L_ARM]; BP_L_ARM, - organ_rel_size[BP_R_ARM]; BP_R_ARM, - organ_rel_size[BP_L_LEG]; BP_L_LEG, - organ_rel_size[BP_R_LEG]; BP_R_LEG, - organ_rel_size[BP_L_HAND]; BP_L_HAND, - organ_rel_size[BP_R_HAND]; BP_R_HAND, - organ_rel_size[BP_L_FOOT]; BP_L_FOOT, - organ_rel_size[BP_R_FOOT]; BP_R_FOOT, - ) - - return ran_zone - -// Emulates targetting a specific body part, and miss chances -// May return null if missed -// miss_chance_mod may be negative. -/proc/get_zone_with_miss_chance(zone, var/mob/target, var/miss_chance_mod = 0, var/ranged_attack=0, var/force_hit = FALSE) - zone = check_zone(zone) - - if(!ranged_attack) - // you cannot miss if your target is prone or restrained - if(target.buckled || target.lying) - return zone - // if your target is being grabbed aggressively by someone you cannot miss either - for(var/obj/item/weapon/grab/G in target.grabbed_by) - if(G.state >= GRAB_AGGRESSIVE) - return zone - - if(force_hit) - return zone - - var/miss_chance = 10 - if (zone in base_miss_chance) - miss_chance = base_miss_chance[zone] - if (zone == "eyes" || zone == "mouth") - miss_chance = base_miss_chance["head"] - miss_chance = max(miss_chance + miss_chance_mod, 0) - if(prob(miss_chance)) - if(prob(70)) - return null - return pick(base_miss_chance) - return zone - - -/proc/stars(n, pr) - if (pr == null) - pr = 25 - if (pr < 0) - return null - else - if (pr >= 100) - return n - var/te = n - var/t = "" - n = length(n) - var/p = null - p = 1 - var/intag = 0 - while(p <= n) - var/char = copytext(te, p, p + 1) - if (char == "<") //let's try to not break tags - intag = !intag - if (intag || char == " " || prob(pr)) - t = text("[][]", t, char) - else - t = text("[]*", t) - if (char == ">") - intag = !intag - p++ - return t - -/proc/stars_all(list/message_pieces, pr) - // eugh, we have to clone the list to avoid collateral damage due to the nature of these messages - . = list() - for(var/datum/multilingual_say_piece/S in message_pieces) - . += new /datum/multilingual_say_piece(S.speaking, stars(S.message)) - -/proc/slur(phrase) - phrase = html_decode(phrase) - var/leng=length(phrase) - var/counter=length(phrase) - var/newphrase="" - var/newletter="" - while(counter>=1) - newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2) - if(rand(1,3)==3) - if(lowertext(newletter)=="o") newletter="u" - if(lowertext(newletter)=="s") newletter="ch" - if(lowertext(newletter)=="a") newletter="ah" - if(lowertext(newletter)=="c") newletter="k" - switch(rand(1,9)) - if(1,3,5,8) newletter="[lowertext(newletter)]" - //if(2,4,6,15) newletter="[uppertext(newletter)]" - if(2,4,6,9) newletter="[uppertext(newletter)]" - if(7) newletter+="'" - //if(9,10) newletter="[newletter]" - //if(11,12) newletter="[newletter]" - //if(13) newletter="[newletter]" - newphrase+="[newletter]";counter-=1 - return newphrase - -/proc/stutter(n) - var/te = html_decode(n) - var/t = ""//placed before the message. Not really sure what it's for. - n = length(n)//length of the entire word - var/p = null - p = 1//1 is the start of any word - while(p <= n)//while P, which starts at 1 is less or equal to N which is the length. - var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time. - if (prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"))) - if (prob(10)) - n_letter = text("[n_letter]-[n_letter]-[n_letter]-[n_letter]")//replaces the current letter with this instead. - else - if (prob(20)) - n_letter = text("[n_letter]-[n_letter]-[n_letter]") - else - if (prob(5)) - n_letter = null - else - n_letter = text("[n_letter]-[n_letter]") - t = text("[t][n_letter]")//since the above is ran through for each letter, the text just adds up back to the original word. - p++//for each letter p is increased to find where the next letter will be. - return sanitize(t) - - -/proc/Gibberish(t, p)//t is the inputted message, and any value higher than 70 for p will cause letters to be replaced instead of added - /* Turn text into complete gibberish! */ - var/returntext = "" - for(var/i = 1, i <= length(t), i++) - - var/letter = copytext(t, i, i+1) - if(prob(50)) - if(p >= 70) - letter = "" - - for(var/j = 1, j <= rand(0, 2), j++) - letter += pick("#","@","*","&","%","$","/", "<", ">", ";","*","*","*","*","*","*","*") - - returntext += letter - - return returntext - - -/proc/ninjaspeak(n) -/* -The difference with stutter is that this proc can stutter more than 1 letter -The issue here is that anything that does not have a space is treated as one word (in many instances). For instance, "LOOKING," is a word, including the comma. -It's fairly easy to fix if dealing with single letters but not so much with compounds of letters./N -*/ - var/te = html_decode(n) - var/t = "" - n = length(n) - var/p = 1 - while(p <= n) - var/n_letter - var/n_mod = rand(1,4) - if(p+n_mod>n+1) - n_letter = copytext(te, p, n+1) - else - n_letter = copytext(te, p, p+n_mod) - if (prob(50)) - if (prob(30)) - n_letter = text("[n_letter]-[n_letter]-[n_letter]") - else - n_letter = text("[n_letter]-[n_letter]") - else - n_letter = text("[n_letter]") - t = text("[t][n_letter]") - p=p+n_mod - return sanitize(t) - - -/proc/shake_camera(mob/M, duration, strength=1) - if(!M || !M.client || M.shakecamera || M.stat || isEye(M) || isAI(M)) - return - M.shakecamera = 1 - spawn(1) - if(!M.client) - return - - var/atom/oldeye=M.client.eye - var/aiEyeFlag = 0 - if(istype(oldeye, /mob/observer/eye/aiEye)) - aiEyeFlag = 1 - - var/x - for(x=0; x